1import { assert, describe, it } from "@effect/vitest";
2import { Tool } from "effect/ai";
3
4import {
5 CreateThreadsTool,
6 DelegateTaskTool,
7 OrchestratorToolkit,
8 ScheduleTaskTool,
9 ThreadUpdateTool,
10} from "./tools.ts";
11
12describe("orchestrator MCP tool guidance", () => {
13 it("directs subagent requests to delegation instead of ordinary threads", () => {
14 assert.include(DelegateTaskTool.description ?? "", "child agent/subagent");
15 assert.include(DelegateTaskTool.description ?? "", "cross-provider");
16 assert.include(CreateThreadsTool.description ?? "", "not delegation");
17 assert.include(CreateThreadsTool.description ?? "", "call delegate_task");
18 assert.include(DelegateTaskTool.description ?? "", "waitTimedOut");
19 assert.include(DelegateTaskTool.description ?? "", "does not cancel the child");
20 assert.include(DelegateTaskTool.description ?? "", "keep that taskId");
21 assert.include(DelegateTaskTool.description ?? "", "call delegate_task again");
22 assert.include(DelegateTaskTool.description ?? "", "childThreadId is backing storage");
23 assert.include(
24 OrchestratorToolkit.tools.t3_thread_send.description ?? "",
25 "a requested follow-up creates a fresh task",
26 );
27 assert.include(
28 OrchestratorToolkit.tools.task_cancel.description ?? "",
29 "This includes later child-thread runs, even after the task is terminal",
30 );
31 });
32
33 it("documents wait timeout as a parent budget, not a child failure", () => {
34 const schema = Tool.getJsonSchema(DelegateTaskTool) as {
35 readonly properties?: Readonly<
36 Record<
37 string,
38 {
39 readonly description?: unknown;
40 readonly anyOf?: ReadonlyArray<{ readonly description?: unknown }>;
41 }
42 >
43 >;
44 };
45 const mode = schema.properties?.mode;
46 const timeoutMs = schema.properties?.timeoutMs;
47 const modeText = [mode?.description, ...(mode?.anyOf ?? []).map((entry) => entry.description)]
48 .filter((value) => typeof value === "string")
49 .join(" ");
50 const timeoutText = [
51 timeoutMs?.description,
52 ...(timeoutMs?.anyOf ?? []).map((entry) => entry.description),
53 ]
54 .filter((value) => typeof value === "string")
55 .join(" ");
56 assert.include(modeText, "Defaults to async");
57 assert.include(timeoutText, "does not cancel the child");
58 });
59
60 it("publishes an actionable schedule schema and compatibility string branch", () => {
61 const schema = Tool.getJsonSchema(ScheduleTaskTool) as {
62 readonly type?: unknown;
63 readonly properties?: Readonly<
64 Record<string, { readonly description?: unknown; readonly anyOf?: ReadonlyArray<unknown> }>
65 >;
66 };
67
68 assert.equal(schema.type, "object");
69 assert.isString(schema.properties?.schedule?.description);
70 assert.isAtLeast(schema.properties?.schedule?.anyOf?.length ?? 0, 2);
71 assert.include(ScheduleTaskTool.description ?? "", "STRUCTURED OBJECT");
72 assert.include(ScheduleTaskTool.description ?? "", "nextRunAt");
73 });
74
75 it("publishes thread metadata actions from an object-root schema", () => {
76 const schema = Tool.getJsonSchema(ThreadUpdateTool) as {
77 readonly type?: unknown;
78 readonly properties?: Readonly<Record<string, unknown>>;
79 };
80
81 assert.equal(schema.type, "object");
82 assert.hasAllKeys(schema.properties ?? {}, [
83 "threadId",
84 "action",
85 "title",
86 "pullRequest",
87 "clientRequestId",
88 ]);
89 assert.include(ThreadUpdateTool.description ?? "", "Workspace and branch changes");
90 });
91});