Files
OmniRoute/tests/unit/responses-chat-namespace-qualified-name.test.ts

150 lines
5.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const { openaiResponsesToOpenAIRequest } = await import(
"../../open-sse/translator/request/openai-responses.ts"
);
interface ChatTool {
function: { name: string; description?: string; parameters?: unknown };
}
interface ChatRequest {
messages: unknown[];
tools: ChatTool[];
}
// Helper: drive the Responses->Chat translator with one namespace tool and
// return the flattened Chat function names in order.
function flattenNamespace(nsName: string, subNames: string[]): string[] {
const result = openaiResponsesToOpenAIRequest(
"any-model",
{
input: [
{
type: "additional_tools",
tools: [
{
type: "namespace",
name: nsName,
tools: subNames.map((name) => ({
name,
description: "sub-tool",
parameters: { type: "object", properties: {} },
})),
},
],
},
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
],
},
false,
{ provider: "any-provider" }
) as ChatRequest;
return result.tools.map((t) => t.function?.name).filter(Boolean) as string[];
}
test("namespace sub-tool with a bare leaf name is flattened to nsName__leaf", () => {
// Real Codex client shape: container mcp__1mcp, sub-tool name is the bare leaf "tool_list".
const names = flattenNamespace("mcp__1mcp", ["tool_list", "tool_schema"]);
assert.deepEqual(names, ["mcp__1mcp__tool_list", "mcp__1mcp__tool_schema"]);
});
test("non-mcp__-prefixed container (multi_agent_v1) is still qualified", () => {
// Codex adjudicator dispatches by splitting on "__" and routing to the trailing
// leaf; the prefix is not gated on "mcp__". Empirically, multi_agent_v1__close_agent
// is accepted and routed to close_agent (failing only on missing args, not the name).
const names = flattenNamespace("multi_agent_v1", ["close_agent", "spawn_agent"]);
assert.deepEqual(names, ["multi_agent_v1__close_agent", "multi_agent_v1__spawn_agent"]);
});
test("codex_app and mcp__context_mode namespaces all get the qualified form", () => {
const ctx = flattenNamespace("mcp__context_mode", ["ctx_search", "ctx_execute"]);
assert.deepEqual(ctx, ["mcp__context_mode__ctx_search", "mcp__context_mode__ctx_execute"]);
const app = flattenNamespace("codex_app", ["read_thread_terminal"]);
assert.deepEqual(app, ["codex_app__read_thread_terminal"]);
});
test("container name ending with __ (mcp__<server>__ convention) collapses to nsName + leaf", () => {
// The mcp__atlassian__ trailing-"__" container-name convention (documented in
// open-sse/executors/codex/tools.ts comments) must NOT produce mcp__atlassian____leaf
// (four underscores). It collapses to mcp__atlassian__leaf (still three trailing chars
// before the leaf, matching the convention).
const names = flattenNamespace("mcp__atlassian__", ["read", "write"]);
assert.deepEqual(names, ["mcp__atlassian__read", "mcp__atlassian__write"]);
});
test("sub-tool name already containing __ (self-prefixed leaf) is preserved verbatim", () => {
// A sub-tool whose name already carries its own prefix (e.g. a fixture that names a
// sub-tool "mcp__server__read" directly) is NOT double-prefixed into nsName + "__" + leaf.
// Real Codex clients never send this shape (they use bare leaf names), but legacy
// fixtures / unusual test inputs can; the guard keeps them from producing a pathological
// double-prefixed wire name.
const names = flattenNamespace("server", ["mcp__server__read"]);
assert.deepEqual(names, ["mcp__server__read"]);
});
test("empty container name falls back to the bare leaf (no prefix appended)", () => {
const names = flattenNamespace("", ["bare_tool"]);
assert.deepEqual(names, ["bare_tool"]);
});
test("namespace flatten still lets adjacent top-level function tools pass through", () => {
const result = openaiResponsesToOpenAIRequest(
"any-model",
{
input: [
{
type: "additional_tools",
tools: [
{ type: "function", name: "lookup", parameters: { type: "object", properties: {} } },
{
type: "namespace",
name: "mcp__1mcp",
tools: [{ name: "tool_list", parameters: { type: "object", properties: {} } }],
},
],
},
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
],
},
false,
{ provider: "any-provider" }
) as ChatRequest;
assert.deepEqual(
result.tools.map((t) => t.function?.name),
["lookup", "mcp__1mcp__tool_list"]
);
});
// #8295 — the regression this file was originally meant to guard against: two
// namespaces sharing a leaf name must not collapse into duplicate Chat tool names.
test("#8295: cross-namespace leaf collisions produce distinct qualified wire names", () => {
const result = openaiResponsesToOpenAIRequest(
"any-model",
{
input: [
{
type: "additional_tools",
tools: [
{
type: "namespace",
name: "mcp__codex_apps__atlassian_rovo",
tools: [{ name: "_search", parameters: { type: "object", properties: {} } }],
},
{
type: "namespace",
name: "mcp__codex_apps__linear",
tools: [{ name: "_search", parameters: { type: "object", properties: {} } }],
},
],
},
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
],
},
false,
{ provider: "any-provider" }
) as ChatRequest;
const names = result.tools.map((t) => t.function?.name);
assert.equal(new Set(names).size, names.length, "Chat tool names must be unique");
});