Skip to content

Commit b377879

Browse files
committed
feat(agent-dispatch): extract dispatch director as a versioned package
Extract @corbits/agent-dispatch following the explorer/coder/reviewer/planner precedent (PRs #1339/#1340/#1341/#1343): byte-identical prompt, order-identical tools, registry + prompts import the workspace dep, in-tree package.ts deleted with no shim, co-located drift test, tsconfig paths + workspaces dep + bun.lock. Fixes CL-9802
1 parent 17909e4 commit b377879

8 files changed

Lines changed: 121 additions & 7 deletions

File tree

‎agents/dispatch/package.json‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "@corbits/agent-dispatch",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"license": "SEE LICENSE IN LICENSE.md",
7+
"exports": {
8+
".": {
9+
"types": "./src/index.ts",
10+
"default": "./src/index.ts"
11+
}
12+
}
13+
}
Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,66 @@
1-
// Dispatch: primary dispatcher card. Idle/mailbox/poll live in the harness.
1+
/**
2+
* Dispatch worker: primary dispatcher card. Idle/mailbox/poll live in the harness.
3+
*
4+
* Ships as the @corbits/agent-dispatch workspace package: the tool allowlist
5+
* lives here so the package stays importable without the app. Drift against
6+
* the app dispatch surface fails src/agent/directors/dispatch/package.test.ts.
7+
*/
8+
export type DispatchSpawnTarget =
9+
| "explorer"
10+
| "planner"
11+
| "coder"
12+
| "reviewer"
13+
| "designer"
14+
| "artist"
15+
| "warden"
16+
| "shakespeare"
17+
| "prober"
18+
| "qa-lead";
219

3-
import type { DirectorPackage } from "../types.js";
4-
import { DISPATCH_TOOLS } from "../tool-sets.js";
20+
export type AgentPackage = {
21+
readonly id: "dispatch";
22+
readonly primaryIntent: string;
23+
readonly outOfLane: readonly string[];
24+
readonly description: string;
25+
readonly systemPrompt: string;
26+
/** Unset — dispatch never attaches skill bodies at spawn. */
27+
readonly attachedSkills?: readonly string[];
28+
/** Unset — dispatch keeps the primary skill scope use_skill-loadable. */
29+
readonly optionalSkills?: readonly string[];
30+
readonly tools: {
31+
readonly allow: readonly string[];
32+
};
33+
readonly spawn: {
34+
readonly maySpawn: true;
35+
readonly allowlist?: readonly DispatchSpawnTarget[];
36+
};
37+
readonly modelRole: "orchestrator";
38+
readonly tier: "orchestrator";
39+
};
40+
41+
const DISPATCH_TOOLS = [
42+
"read_file",
43+
"grep",
44+
"search_files",
45+
"list_dir",
46+
"lsp",
47+
"run_shell",
48+
"web_fetch",
49+
"web_search",
50+
"skill_search",
51+
"use_skill",
52+
"write_file",
53+
"edit_file",
54+
"delete_file",
55+
"spawn_agent",
56+
"list_agents",
57+
"close_agent",
58+
"resume_agent",
59+
"interrupt_agent",
60+
"send_input",
61+
"read_agent_trace",
62+
"search_agents",
63+
] as const;
564

665
const DISPATCH_CARD = `# Role
766
You are Dispatch, the coordinator for Corbits Code. Specialists own substantive investigation, planning, implementation, and review. You own routing, briefs, coordination, and synthesis.
@@ -48,7 +107,7 @@ export function createDispatchSystemPrompt(): string {
48107
return DISPATCH_CARD;
49108
}
50109

51-
export const dispatchPackage: DirectorPackage = {
110+
export const dispatchPackage: AgentPackage = {
52111
id: "dispatch",
53112
primaryIntent:
54113
"Coordinate named specialists; DIY only obvious mechanical corrections",
@@ -63,7 +122,7 @@ export const dispatchPackage: DirectorPackage = {
63122
description:
64123
"Primary dispatcher — classify, DIY tiny edits, spawn named specialists",
65124
systemPrompt: DISPATCH_CARD,
66-
tools: { allow: DISPATCH_TOOLS },
125+
tools: { allow: [...DISPATCH_TOOLS] },
67126
spawn: {
68127
maySpawn: true,
69128
allowlist: [

‎bun.lock‎

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"tar": "^7.5.1"
8787
},
8888
"dependencies": {
89+
"@corbits/agent-dispatch": "workspace:*",
8990
"@corbits/codex-provider": "0.1.1",
9091
"@corbits/oauth-core": "0.2.0",
9192
"@corbits/openai-responses": "0.2.1",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, test } from "bun:test";
2+
3+
import { dispatchPackage } from "@corbits/agent-dispatch";
4+
import { DISPATCH_TOOLS } from "../tool-sets.js";
5+
import { DIRECTOR_REGISTRY } from "../registry.js";
6+
import type { DirectorPackage } from "../types.js";
7+
8+
describe("dispatchPackage", () => {
9+
test("workspace package satisfies the director contract and feeds the registry", () => {
10+
const asDirector: DirectorPackage = dispatchPackage;
11+
expect(asDirector.id).toBe("dispatch");
12+
expect(DIRECTOR_REGISTRY.dispatch).toBe(dispatchPackage);
13+
expect([...dispatchPackage.tools.allow]).toEqual([...DISPATCH_TOOLS]);
14+
});
15+
16+
test("extracted copy stays orchestrator-scoped and spawning", () => {
17+
expect(dispatchPackage.tools.allow).toContain("spawn_agent");
18+
expect(dispatchPackage.tools.allow).toContain("search_agents");
19+
expect(dispatchPackage.spawn.maySpawn).toBe(true);
20+
expect(dispatchPackage.spawn.allowlist).toEqual([
21+
"explorer",
22+
"planner",
23+
"coder",
24+
"reviewer",
25+
"designer",
26+
"artist",
27+
"warden",
28+
"shakespeare",
29+
"prober",
30+
"qa-lead",
31+
]);
32+
expect(dispatchPackage.tier).toBe("orchestrator");
33+
expect(dispatchPackage.modelRole).toBe("orchestrator");
34+
});
35+
});

‎src/agent/directors/registry.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { AgentProfile, CapabilityFilter } from "../profile-types.js";
44
import { director as artistDirector } from "@corbits/code-agent-artist";
55
import { coderPackage } from "./coder/package.js";
66
import { designerPackage } from "./designer/package.js";
7-
import { dispatchPackage } from "./dispatch/package.js";
7+
import { dispatchPackage } from "@corbits/agent-dispatch";
88
import { explorerPackage } from "./explorer/package.js";
99
import { plannerPackage } from "./planner/package.js";
1010
import { qaLeadPackage } from "./qa-lead/package.js";

‎src/agent/prompts.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { EnvironmentInfo } from "./environment.js";
22
import type { SkillSummary } from "../extensions/skills.js";
33
import type { SessionMode } from "../config/session-mode.js";
44
import type { ToolAvailability } from "./tool-search.js";
5-
import { createDispatchSystemPrompt } from "./directors/dispatch/package.js";
5+
import { createDispatchSystemPrompt } from "@corbits/agent-dispatch";
66
import {
77
buildWorkerContract,
88
buildWorkerToolNames,

‎tsconfig.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@corbits/code-agent-artist": ["./agents/artist/src/index.ts"],
99
"@corbits/code-agent-shakespeare": ["./agents/shakespeare/src/index.ts"],
1010
"@corbits/code-agent-prober": ["./agents/prober/src/index.ts"],
11+
"@corbits/agent-dispatch": ["./agents/dispatch/src/index.ts"],
1112
"@corbits/provider-opencode-go": ["./packages/opencode-go/src/index.ts"],
1213
"@corbits/first-class-providers": [
1314
"./packages/first-class-providers/src/index.ts"

0 commit comments

Comments
 (0)