Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions agents/dispatch/src/toolset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const tools = [
"resume_agent",
"interrupt_agent",
"send_input",
"approve_worker_call",
"read_agent_trace",
"search_agents",
] as const;
9 changes: 9 additions & 0 deletions src/agent/directors/tool-sets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ describe("DISPATCH_TOOLS / ORCHESTRATOR_TOOLS", () => {
);
});

test("approve_worker_call sits beside send_input on both dispatch surfaces", () => {
for (const surface of [DISPATCH_TOOLS, ORCHESTRATOR_TOOLS] as const) {
const names = surface as readonly string[];
const send = names.indexOf("send_input");
expect(send).toBeGreaterThanOrEqual(0);
expect(names[send + 1]).toBe("approve_worker_call");
}
});

test("skill_search + use_skill mount on every worker surface, never ask_operator", () => {
expect([...SKILL_TOOLS]).toEqual(["skill_search", "use_skill"]);
for (const surface of [
Expand Down
1 change: 1 addition & 0 deletions src/agent/directors/tool-sets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const ORCHESTRATOR_TOOLS = [
"resume_agent",
"interrupt_agent",
"send_input",
"approve_worker_call",
"read_agent_trace",
] as const;

Expand Down
6 changes: 5 additions & 1 deletion src/agent/prompt-sizes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,11 @@ describe("director prompt size budget", () => {
expect(names, `${directorId} [${family}]`).not.toContain(phantom);
}
if (DIRECTOR_REGISTRY[directorId].spawn.maySpawn) {
for (const verb of ["spawn_agent", "send_input"]) {
for (const verb of [
"spawn_agent",
"send_input",
"approve_worker_call",
]) {
expect(
names.filter((n) => n === verb).length,
`${directorId} [${family}] mounts ${verb} once`,
Expand Down
1 change: 1 addition & 0 deletions src/agent/prompt-sizes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export function canonicalToolNamesForDirector(
"resume_agent",
"interrupt_agent",
"send_input",
"approve_worker_call",
);
}
const dupe = names.find((name, index) => names.indexOf(name) !== index);
Expand Down
2 changes: 2 additions & 0 deletions src/agent/tool-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const CORE_TOOL_NAMES: readonly string[] = [
"resume_agent",
"interrupt_agent",
"send_input",
"approve_worker_call",
];

const ORCHESTRATOR_ONLY_TOOL_NAMES: readonly string[] = [
Expand All @@ -60,6 +61,7 @@ const ORCHESTRATOR_ONLY_TOOL_NAMES: readonly string[] = [
"resume_agent",
"interrupt_agent",
"send_input",
"approve_worker_call",
];

// Session-start facts gating a core tool's advertisement, fixed for the
Expand Down
62 changes: 62 additions & 0 deletions src/exec/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,68 @@ describe("exec advertised tools vs TUI", () => {
});
expect(isAdvertised("ask_operator")).toBe(true);
});

test("parent prefix and dispatch call gate list approve_worker_call after send_input", () => {
const stub = (name: string) => ({
name,
description: name,
inputSchema: { type: "object" as const, properties: {} },
});
const expectBesideSendInput = (names: readonly string[]) => {
const send = names.indexOf("send_input");
expect(send).toBeGreaterThanOrEqual(0);
expect(names[send + 1]).toBe("approve_worker_call");
};
const registry = [
stub("read_file"),
stub("approve_worker_call"),
stub("send_input"),
];
for (const toolAvailability of [
{
languageServerAvailable: false,
operatorAvailable: true,
waitAgentsMounted: false,
},
{
languageServerAvailable: false,
operatorAvailable: false,
waitAgentsMounted: true,
},
] as const) {
expectBesideSendInput(
advertisedToolNamesForSessionMode(sessionMode, toolAvailability),
);
const tui = createAdvertisedToolset({
sessionMode,
toolAvailability,
getProvider: () => ({ providerName: "test", model: "test" }),
});
expectBesideSendInput(
tui.computeAdvertised(registry).map((def) => def.name),
);
expect(
createExecToolCallGate(tui.isAdvertised)("approve_worker_call"),
).toBe(true);
}
const overlay = resolveExecDirectorOverlay("dispatch");
const exec = createAdvertisedToolset({
sessionMode,
toolAvailability: {
languageServerAvailable: false,
operatorAvailable: false,
waitAgentsMounted: true,
},
getProvider: () => ({ providerName: "test", model: "test" }),
builtInPrefix: overlay.advertisedAllow,
});
expectBesideSendInput(
exec.computeAdvertised(registry).map((def) => def.name),
);
expect(
createExecToolCallGate(exec.isAdvertised)("approve_worker_call"),
).toBe(true);
});
});

describe("exec tool call gate and promoter", () => {
Expand Down
Loading