From 574b95d93a8ac58ecf1b511179d5fb148a311a2c Mon Sep 17 00:00:00 2001 From: Sawyer Date: Sun, 4 Oct 2026 23:48:09 -0700 Subject: [PATCH 1/8] 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 --- agents/dispatch/package.json | 13 ++++ .../dispatch/src/index.ts | 69 +++++++++++++++++-- bun.lock | 5 ++ package.json | 1 + src/agent/directors/dispatch/package.test.ts | 35 ++++++++++ src/agent/directors/registry.ts | 2 +- src/agent/prompts.ts | 2 +- tsconfig.json | 1 + 8 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 agents/dispatch/package.json rename src/agent/directors/dispatch/package.ts => agents/dispatch/src/index.ts (74%) create mode 100644 src/agent/directors/dispatch/package.test.ts diff --git a/agents/dispatch/package.json b/agents/dispatch/package.json new file mode 100644 index 000000000..577509df7 --- /dev/null +++ b/agents/dispatch/package.json @@ -0,0 +1,13 @@ +{ + "name": "@corbits/agent-dispatch", + "version": "0.1.0", + "private": true, + "type": "module", + "license": "SEE LICENSE IN LICENSE.md", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + } +} diff --git a/src/agent/directors/dispatch/package.ts b/agents/dispatch/src/index.ts similarity index 74% rename from src/agent/directors/dispatch/package.ts rename to agents/dispatch/src/index.ts index 0313df46d..5d999aef9 100644 --- a/src/agent/directors/dispatch/package.ts +++ b/agents/dispatch/src/index.ts @@ -1,7 +1,66 @@ -// Dispatch: primary dispatcher card. Idle/mailbox/poll live in the harness. +/** + * Dispatch worker: primary dispatcher card. Idle/mailbox/poll live in the harness. + * + * Ships as the @corbits/agent-dispatch workspace package: the tool allowlist + * lives here so the package stays importable without the app. Drift against + * the app dispatch surface fails src/agent/directors/dispatch/package.test.ts. + */ +export type DispatchSpawnTarget = + | "explorer" + | "planner" + | "coder" + | "reviewer" + | "designer" + | "artist" + | "warden" + | "shakespeare" + | "prober" + | "qa-lead"; -import type { DirectorPackage } from "../types.js"; -import { DISPATCH_TOOLS } from "../tool-sets.js"; +export type AgentPackage = { + readonly id: "dispatch"; + readonly primaryIntent: string; + readonly outOfLane: readonly string[]; + readonly description: string; + readonly systemPrompt: string; + /** Unset — dispatch never attaches skill bodies at spawn. */ + readonly attachedSkills?: readonly string[]; + /** Unset — dispatch keeps the primary skill scope use_skill-loadable. */ + readonly optionalSkills?: readonly string[]; + readonly tools: { + readonly allow: readonly string[]; + }; + readonly spawn: { + readonly maySpawn: true; + readonly allowlist?: readonly DispatchSpawnTarget[]; + }; + readonly modelRole: "orchestrator"; + readonly tier: "orchestrator"; +}; + +const DISPATCH_TOOLS = [ + "read_file", + "grep", + "search_files", + "list_dir", + "lsp", + "run_shell", + "web_fetch", + "web_search", + "skill_search", + "use_skill", + "write_file", + "edit_file", + "delete_file", + "spawn_agent", + "list_agents", + "close_agent", + "resume_agent", + "interrupt_agent", + "send_input", + "read_agent_trace", + "search_agents", +] as const; const DISPATCH_CARD = `# Role 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 { return DISPATCH_CARD; } -export const dispatchPackage: DirectorPackage = { +export const dispatchPackage: AgentPackage = { id: "dispatch", primaryIntent: "Coordinate named specialists; DIY only obvious mechanical corrections", @@ -63,7 +122,7 @@ export const dispatchPackage: DirectorPackage = { description: "Primary dispatcher — classify, DIY tiny edits, spawn named specialists", systemPrompt: DISPATCH_CARD, - tools: { allow: DISPATCH_TOOLS }, + tools: { allow: [...DISPATCH_TOOLS] }, spawn: { maySpawn: true, allowlist: [ diff --git a/bun.lock b/bun.lock index 84d0c2a99..a204d285a 100644 --- a/bun.lock +++ b/bun.lock @@ -60,6 +60,10 @@ "name": "@corbits/code-agent-prober", "version": "0.1.0", }, + "agents/dispatch": { + "name": "@corbits/agent-dispatch", + "version": "0.1.0", + }, "packages/first-class-providers": { "name": "@corbits/first-class-providers", "version": "0.1.0", @@ -254,6 +258,7 @@ "@corbits/code-agent-artist": ["@corbits/code-agent-artist@workspace:agents/artist"], "@corbits/code-agent-shakespeare": ["@corbits/code-agent-shakespeare@workspace:agents/shakespeare"], "@corbits/code-agent-prober": ["@corbits/code-agent-prober@workspace:agents/prober"], + "@corbits/agent-dispatch": ["@corbits/agent-dispatch@workspace:agents/dispatch"], "@corbits/codex-provider": ["@corbits/codex-provider@0.1.1", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.2.0", "@corbits/openai-responses": "^0.2.0", "@intx/inference": "^0.4.0", "@intx/types": "^0.4.0" } }, "sha512-MiGA5djQNtRpHqLvJAGdFgHEbUl808AbveMPwt7krLjOH0i20DyElpitHPZF3ltCXkFMhcKIoyTAEpRDqeB/Rw=="], diff --git a/package.json b/package.json index c427a0851..c57a26eea 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "tar": "^7.5.1" }, "dependencies": { + "@corbits/agent-dispatch": "workspace:*", "@corbits/codex-provider": "0.1.1", "@corbits/oauth-core": "0.2.0", "@corbits/openai-responses": "0.2.1", diff --git a/src/agent/directors/dispatch/package.test.ts b/src/agent/directors/dispatch/package.test.ts new file mode 100644 index 000000000..009c9a8d7 --- /dev/null +++ b/src/agent/directors/dispatch/package.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, test } from "bun:test"; + +import { dispatchPackage } from "@corbits/agent-dispatch"; +import { DISPATCH_TOOLS } from "../tool-sets.js"; +import { DIRECTOR_REGISTRY } from "../registry.js"; +import type { DirectorPackage } from "../types.js"; + +describe("dispatchPackage", () => { + test("workspace package satisfies the director contract and feeds the registry", () => { + const asDirector: DirectorPackage = dispatchPackage; + expect(asDirector.id).toBe("dispatch"); + expect(DIRECTOR_REGISTRY.dispatch).toBe(dispatchPackage); + expect([...dispatchPackage.tools.allow]).toEqual([...DISPATCH_TOOLS]); + }); + + test("extracted copy stays orchestrator-scoped and spawning", () => { + expect(dispatchPackage.tools.allow).toContain("spawn_agent"); + expect(dispatchPackage.tools.allow).toContain("search_agents"); + expect(dispatchPackage.spawn.maySpawn).toBe(true); + expect(dispatchPackage.spawn.allowlist).toEqual([ + "explorer", + "planner", + "coder", + "reviewer", + "designer", + "artist", + "warden", + "shakespeare", + "prober", + "qa-lead", + ]); + expect(dispatchPackage.tier).toBe("orchestrator"); + expect(dispatchPackage.modelRole).toBe("orchestrator"); + }); +}); diff --git a/src/agent/directors/registry.ts b/src/agent/directors/registry.ts index 688db63e4..1a9fd672b 100644 --- a/src/agent/directors/registry.ts +++ b/src/agent/directors/registry.ts @@ -4,7 +4,7 @@ import type { AgentProfile, CapabilityFilter } from "../profile-types.js"; import { director as artistDirector } from "@corbits/code-agent-artist"; import { coderPackage } from "./coder/package.js"; import { designerPackage } from "./designer/package.js"; -import { dispatchPackage } from "./dispatch/package.js"; +import { dispatchPackage } from "@corbits/agent-dispatch"; import { explorerPackage } from "./explorer/package.js"; import { plannerPackage } from "./planner/package.js"; import { qaLeadPackage } from "./qa-lead/package.js"; diff --git a/src/agent/prompts.ts b/src/agent/prompts.ts index 6fb7f45a9..0850e822f 100644 --- a/src/agent/prompts.ts +++ b/src/agent/prompts.ts @@ -2,7 +2,7 @@ import type { EnvironmentInfo } from "./environment.js"; import type { SkillSummary } from "../extensions/skills.js"; import type { SessionMode } from "../config/session-mode.js"; import type { ToolAvailability } from "./tool-search.js"; -import { createDispatchSystemPrompt } from "./directors/dispatch/package.js"; +import { createDispatchSystemPrompt } from "@corbits/agent-dispatch"; import { buildWorkerContract, buildWorkerToolNames, diff --git a/tsconfig.json b/tsconfig.json index c7e3946b1..5d4885a3b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "@corbits/code-agent-artist": ["./agents/artist/src/index.ts"], "@corbits/code-agent-shakespeare": ["./agents/shakespeare/src/index.ts"], "@corbits/code-agent-prober": ["./agents/prober/src/index.ts"], + "@corbits/agent-dispatch": ["./agents/dispatch/src/index.ts"], "@corbits/provider-opencode-go": ["./packages/opencode-go/src/index.ts"], "@corbits/first-class-providers": [ "./packages/first-class-providers/src/index.ts" From 04271f0fcd30b20e54edafd36cfad88fa435d730 Mon Sep 17 00:00:00 2001 From: corbits-code Date: Mon, 5 Oct 2026 00:03:28 -0700 Subject: [PATCH 2/8] test(agent-dispatch): assert workspace card matches retained in-tree card --- src/agent/directors/dispatch/package.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/agent/directors/dispatch/package.test.ts b/src/agent/directors/dispatch/package.test.ts index 009c9a8d7..ab43dd6a6 100644 --- a/src/agent/directors/dispatch/package.test.ts +++ b/src/agent/directors/dispatch/package.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test"; import { dispatchPackage } from "@corbits/agent-dispatch"; +import { dispatchPackage as inTreeDispatchPackage } from "./package.js"; import { DISPATCH_TOOLS } from "../tool-sets.js"; import { DIRECTOR_REGISTRY } from "../registry.js"; import type { DirectorPackage } from "../types.js"; @@ -32,4 +33,10 @@ describe("dispatchPackage", () => { expect(dispatchPackage.tier).toBe("orchestrator"); expect(dispatchPackage.modelRole).toBe("orchestrator"); }); + + test("workspace card matches retained in-tree card until removal", () => { + expect(dispatchPackage.systemPrompt).toBe( + inTreeDispatchPackage.systemPrompt, + ); + }); }); From b784a4045e06e3ba38f9cb61176628e89e960df7 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Thu, 8 Oct 2026 14:00:30 -0700 Subject: [PATCH 3/8] chore(dispatch): rename package to @corbits/code-agent-dispatch --- agents/dispatch/package.json | 2 +- agents/dispatch/src/index.ts | 2 +- bun.lock | 5 +++-- package.json | 2 +- src/agent/directors/dispatch/package.test.ts | 2 +- src/agent/directors/registry.ts | 4 ++-- src/agent/prompts.ts | 2 +- tsconfig.json | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/agents/dispatch/package.json b/agents/dispatch/package.json index 577509df7..9521e89db 100644 --- a/agents/dispatch/package.json +++ b/agents/dispatch/package.json @@ -1,5 +1,5 @@ { - "name": "@corbits/agent-dispatch", + "name": "@corbits/code-agent-dispatch", "version": "0.1.0", "private": true, "type": "module", diff --git a/agents/dispatch/src/index.ts b/agents/dispatch/src/index.ts index 5d999aef9..bf3829cf6 100644 --- a/agents/dispatch/src/index.ts +++ b/agents/dispatch/src/index.ts @@ -1,7 +1,7 @@ /** * Dispatch worker: primary dispatcher card. Idle/mailbox/poll live in the harness. * - * Ships as the @corbits/agent-dispatch workspace package: the tool allowlist + * Ships as the @corbits/code-agent-dispatch workspace package: the tool allowlist * lives here so the package stays importable without the app. Drift against * the app dispatch surface fails src/agent/directors/dispatch/package.test.ts. */ diff --git a/bun.lock b/bun.lock index a204d285a..b22e2a2b1 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "": { "name": "@corbits/code", "dependencies": { + "@corbits/code-agent-dispatch": "workspace:*", "@corbits/codex-provider": "0.1.1", "@corbits/oauth-core": "0.2.0", "@corbits/openai-responses": "0.2.1", @@ -61,7 +62,7 @@ "version": "0.1.0", }, "agents/dispatch": { - "name": "@corbits/agent-dispatch", + "name": "@corbits/code-agent-dispatch", "version": "0.1.0", }, "packages/first-class-providers": { @@ -258,7 +259,7 @@ "@corbits/code-agent-artist": ["@corbits/code-agent-artist@workspace:agents/artist"], "@corbits/code-agent-shakespeare": ["@corbits/code-agent-shakespeare@workspace:agents/shakespeare"], "@corbits/code-agent-prober": ["@corbits/code-agent-prober@workspace:agents/prober"], - "@corbits/agent-dispatch": ["@corbits/agent-dispatch@workspace:agents/dispatch"], + "@corbits/code-agent-dispatch": ["@corbits/code-agent-dispatch@workspace:agents/dispatch"], "@corbits/codex-provider": ["@corbits/codex-provider@0.1.1", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.2.0", "@corbits/openai-responses": "^0.2.0", "@intx/inference": "^0.4.0", "@intx/types": "^0.4.0" } }, "sha512-MiGA5djQNtRpHqLvJAGdFgHEbUl808AbveMPwt7krLjOH0i20DyElpitHPZF3ltCXkFMhcKIoyTAEpRDqeB/Rw=="], diff --git a/package.json b/package.json index c57a26eea..19830a5c7 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "tar": "^7.5.1" }, "dependencies": { - "@corbits/agent-dispatch": "workspace:*", + "@corbits/code-agent-dispatch": "workspace:*", "@corbits/codex-provider": "0.1.1", "@corbits/oauth-core": "0.2.0", "@corbits/openai-responses": "0.2.1", diff --git a/src/agent/directors/dispatch/package.test.ts b/src/agent/directors/dispatch/package.test.ts index ab43dd6a6..45041f252 100644 --- a/src/agent/directors/dispatch/package.test.ts +++ b/src/agent/directors/dispatch/package.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { dispatchPackage } from "@corbits/agent-dispatch"; +import { dispatchPackage } from "@corbits/code-agent-dispatch"; import { dispatchPackage as inTreeDispatchPackage } from "./package.js"; import { DISPATCH_TOOLS } from "../tool-sets.js"; import { DIRECTOR_REGISTRY } from "../registry.js"; diff --git a/src/agent/directors/registry.ts b/src/agent/directors/registry.ts index 1a9fd672b..166daa03b 100644 --- a/src/agent/directors/registry.ts +++ b/src/agent/directors/registry.ts @@ -1,10 +1,10 @@ import { director as shakespeareDirector } from "@corbits/code-agent-shakespeare"; import { director as proberDirector } from "@corbits/code-agent-prober"; +import { dispatchPackage } from "@corbits/code-agent-dispatch"; import type { AgentProfile, CapabilityFilter } from "../profile-types.js"; import { director as artistDirector } from "@corbits/code-agent-artist"; import { coderPackage } from "./coder/package.js"; import { designerPackage } from "./designer/package.js"; -import { dispatchPackage } from "@corbits/agent-dispatch"; import { explorerPackage } from "./explorer/package.js"; import { plannerPackage } from "./planner/package.js"; import { qaLeadPackage } from "./qa-lead/package.js"; @@ -139,4 +139,4 @@ export function directorProfiles(): AgentProfile[] { return listDirectors() .filter((pkg) => pkg.id !== "dispatch") .map(packageToProfile); -} +} \ No newline at end of file diff --git a/src/agent/prompts.ts b/src/agent/prompts.ts index 0850e822f..7f85aea90 100644 --- a/src/agent/prompts.ts +++ b/src/agent/prompts.ts @@ -2,7 +2,7 @@ import type { EnvironmentInfo } from "./environment.js"; import type { SkillSummary } from "../extensions/skills.js"; import type { SessionMode } from "../config/session-mode.js"; import type { ToolAvailability } from "./tool-search.js"; -import { createDispatchSystemPrompt } from "@corbits/agent-dispatch"; +import { createDispatchSystemPrompt } from "@corbits/code-agent-dispatch"; import { buildWorkerContract, buildWorkerToolNames, diff --git a/tsconfig.json b/tsconfig.json index 5d4885a3b..1cb912592 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "@corbits/code-agent-artist": ["./agents/artist/src/index.ts"], "@corbits/code-agent-shakespeare": ["./agents/shakespeare/src/index.ts"], "@corbits/code-agent-prober": ["./agents/prober/src/index.ts"], - "@corbits/agent-dispatch": ["./agents/dispatch/src/index.ts"], + "@corbits/code-agent-dispatch": ["./agents/dispatch/src/index.ts"], "@corbits/provider-opencode-go": ["./packages/opencode-go/src/index.ts"], "@corbits/first-class-providers": [ "./packages/first-class-providers/src/index.ts" From ba1595d243f6e2b29e9f841c08014f89a7490e9e Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 8 Oct 2026 19:48:45 -0700 Subject: [PATCH 4/8] feat(agent-dispatch): adopt uniform reusable agent template shape --- agents/dispatch/README.md | 48 ++++++ agents/dispatch/src/agent.ts | 26 ++++ agents/dispatch/src/config.ts | 26 ++++ agents/dispatch/src/director.ts | 30 ++++ agents/dispatch/src/index.ts | 152 ++----------------- agents/dispatch/src/prompt.ts | 52 +++++++ agents/dispatch/src/toolset.ts | 30 ++++ agents/dispatch/tsconfig.json | 7 + bun.lock | 1 - package.json | 1 - src/agent/directors/dispatch/package.test.ts | 62 ++++---- src/agent/directors/registry.ts | 4 +- src/agent/prompts.ts | 4 +- 13 files changed, 267 insertions(+), 176 deletions(-) create mode 100644 agents/dispatch/README.md create mode 100644 agents/dispatch/src/agent.ts create mode 100644 agents/dispatch/src/config.ts create mode 100644 agents/dispatch/src/director.ts create mode 100644 agents/dispatch/src/prompt.ts create mode 100644 agents/dispatch/src/toolset.ts create mode 100644 agents/dispatch/tsconfig.json diff --git a/agents/dispatch/README.md b/agents/dispatch/README.md new file mode 100644 index 000000000..53c4048da --- /dev/null +++ b/agents/dispatch/README.md @@ -0,0 +1,48 @@ +# @corbits/code-agent-dispatch + +## What this is + +A ready-to-go Corbits agent for the **dispatch** role (`@corbits/code-agent-dispatch`) +plus its named component parts so a consumer can recreate or remix it. Dispatch is +the Tier-1 primary orchestrator: it classifies work, DIYs obvious mechanical +corrections, and spawns named specialists. + +## Consume (runLocal) + +```ts +import { agent } from "@corbits/code-agent-dispatch"; +const run = runLocal(defining(agent), env); +``` + +## Consume (deploy runWorkflow / hub / sidecar) + +Hand the same `AgentDefinition` (`agent`) to a workflow, hub, or sidecar. The +package never exports a workflow itself — a workflow _consumes_ `agent`. + +## Remix components + +```ts +import { + director, + tools, + systemPrompt, + config, + defineAgent, +} from "@corbits/code-agent-dispatch"; + +// Swap components at the consumer side. +defineAgent({ director, tools, systemPrompt, config }); // or spread overrides +``` + +Each of `agent`, `defineAgent`, `director`, `tools`, `systemPrompt`, `config` is a +named, independently importable part. + +## Byte-stability & single authority + +- `systemPrompt.build()` is pure and byte-stable — identical string every call. +- `tools` is the literal expansion of the app's `DISPATCH_TOOLS` surface constant + (`src/agent/directors/tool-sets.ts`); the in-tree drift-guard + (`src/agent/directors/dispatch/package.test.ts`) enforces they match. +- This package is the single prompt authority for dispatch; the in-tree + `src/agent/directors/dispatch/package.ts` (and the duplicate + `createDispatchSystemPrompt` / `DISPATCH_CARD` in `src/agent/prompts.ts`) are deleted. diff --git a/agents/dispatch/src/agent.ts b/agents/dispatch/src/agent.ts new file mode 100644 index 000000000..ee8ab57e8 --- /dev/null +++ b/agents/dispatch/src/agent.ts @@ -0,0 +1,26 @@ +// Dispatch assembled agent — ready unit + package defineAgent wrapper. +// Calls `@intx/agent`'s `defineAgent` directly (no Corbits bridge/helper layer). +// Tool mounting is consumer/app-side: `tools` here is [] (the allowlist surface +// is exported from `./toolset.js` and wired by the app harness). + +import { + defineAgent as coreDefineAgent, + type AgentDefinition, +} from "@intx/agent"; + +import { systemPrompt } from "./prompt.js"; + +/** The assembled dispatch agent — ready to hand to `runLocal` / `runWorkflow`. */ +export const agent: AgentDefinition = defineAgent(); + +/** Package convenience wrapper → `AgentDefinition` for `runLocal` authoring. */ +export function defineAgent(): AgentDefinition { + return coreDefineAgent({ + id: "dispatch", + systemPrompt: systemPrompt.build(), + tools: [], + capabilities: [], + inference: { sources: [] }, + tags: { fleet: "0.3.36", lane: "dispatch" }, + }); +} diff --git a/agents/dispatch/src/config.ts b/agents/dispatch/src/config.ts new file mode 100644 index 000000000..d232bdedf --- /dev/null +++ b/agents/dispatch/src/config.ts @@ -0,0 +1,26 @@ +// Dispatch config — uniform `{ spawn, tier, modelRole }` shape (§5). +// dispatch is the Tier-1 primary orchestrator: maySpawn true with spawn +// allowlist; others are leaf workers with no allowlist. +export const config = { + spawn: { + maySpawn: true, + allowlist: [ + "explorer", + "planner", + "coder", + "reviewer", + "designer", + "artist", + "warden", + "shakespeare", + "prober", + "qa-lead", + ], + }, + tier: "orchestrator", + modelRole: "orchestrator", +} as const satisfies { + spawn: { maySpawn: boolean; allowlist?: readonly string[] }; + tier: string; + modelRole: string; +}; diff --git a/agents/dispatch/src/director.ts b/agents/dispatch/src/director.ts new file mode 100644 index 000000000..c2a3cf1b4 --- /dev/null +++ b/agents/dispatch/src/director.ts @@ -0,0 +1,30 @@ +// Dispatch director card — the `DirectorPackage` data, extracted byte-for-byte +// from the in-tree `src/agent/directors/dispatch/package.ts` (now deleted). +// Structurally satisfies the app's `DirectorPackage` (asserted in-tree by +// `src/agent/directors/dispatch/package.test.ts`); it imports @intx/* only. + +import { tools } from "./toolset.js"; +import { systemPrompt } from "./prompt.js"; +import { config } from "./config.js"; + +/** The assembled dispatch director card. */ +export const director = { + id: "dispatch", + primaryIntent: + "Coordinate named specialists; DIY only obvious mechanical corrections", + outOfLane: [ + "substantive product work without spawning, including single-file behavior changes", + "docs/design authorship (PRODUCT.md, ARCHITECTURE.md, DESIGN.md) except one-line fixes", + "walking the repo yourself; spawn explorers (as many as the question needs)", + "being the reviewer, planner, or coder by default", + "catch-all worker", + "searching the repo yourself after a worker stops without finishing", + ], + description: + "Primary dispatcher — classify, DIY tiny edits, spawn named specialists", + systemPrompt: systemPrompt.build(), + tools: { allow: tools }, + spawn: config.spawn, + modelRole: config.modelRole, + tier: config.tier, +} as const; diff --git a/agents/dispatch/src/index.ts b/agents/dispatch/src/index.ts index bf3829cf6..bd370bcbb 100644 --- a/agents/dispatch/src/index.ts +++ b/agents/dispatch/src/index.ts @@ -1,143 +1,9 @@ -/** - * Dispatch worker: primary dispatcher card. Idle/mailbox/poll live in the harness. - * - * Ships as the @corbits/code-agent-dispatch workspace package: the tool allowlist - * lives here so the package stays importable without the app. Drift against - * the app dispatch surface fails src/agent/directors/dispatch/package.test.ts. - */ -export type DispatchSpawnTarget = - | "explorer" - | "planner" - | "coder" - | "reviewer" - | "designer" - | "artist" - | "warden" - | "shakespeare" - | "prober" - | "qa-lead"; - -export type AgentPackage = { - readonly id: "dispatch"; - readonly primaryIntent: string; - readonly outOfLane: readonly string[]; - readonly description: string; - readonly systemPrompt: string; - /** Unset — dispatch never attaches skill bodies at spawn. */ - readonly attachedSkills?: readonly string[]; - /** Unset — dispatch keeps the primary skill scope use_skill-loadable. */ - readonly optionalSkills?: readonly string[]; - readonly tools: { - readonly allow: readonly string[]; - }; - readonly spawn: { - readonly maySpawn: true; - readonly allowlist?: readonly DispatchSpawnTarget[]; - }; - readonly modelRole: "orchestrator"; - readonly tier: "orchestrator"; -}; - -const DISPATCH_TOOLS = [ - "read_file", - "grep", - "search_files", - "list_dir", - "lsp", - "run_shell", - "web_fetch", - "web_search", - "skill_search", - "use_skill", - "write_file", - "edit_file", - "delete_file", - "spawn_agent", - "list_agents", - "close_agent", - "resume_agent", - "interrupt_agent", - "send_input", - "read_agent_trace", - "search_agents", -] as const; - -const DISPATCH_CARD = `# Role -You are Dispatch, the coordinator for Corbits Code. Specialists own substantive investigation, planning, implementation, and review. You own routing, briefs, coordination, and synthesis. - -# Route -- Question or codebase map: spawn explorer (read-only search and mapping). -- Requirements, architecture plan, or task breakdown: spawn planner (PRD.md, SOLUTION_SCOPE.md, BUILD_PLAN.md). -- Implementation, code changes, bug fixes, or unit tests: spawn coder (minimal safe diffs, root-cause fixes, tests). -- Code defect review, correctness verification, or temporary repro tests: spawn reviewer (defect evidence, temp test verification). -- UI/UX styling, design systems, design tokens, or DESIGN.md: spawn designer (impeccable style laws, DESIGN.md ownership). -- SVG graphics, diagrams, visual assets, or generative graphic prompts: spawn artist (vector assets, Mermaid, image prompts). -- Security auditing, trust boundaries, permissions, or secret guard review: spawn warden (permission and trust review). -- Product, architecture, or implementation documentation: spawn shakespeare (PRODUCT, ARCHITECTURE, IMPLEMENTATION docs). -- Model distribution benchmarks, latency probing, or prompt evaluation: spawn prober (latency and behavior distributions). -- Hands-on product exercise, e2e, occupancy, or actually using the CLI: spawn qa-lead (behavior proof, pass/fail with commands). Distinct from coder unit tests and prober measurement. - -# Delegation boundary -- Delegate by default. File count does not determine complexity: a single-file bug fix or behavior change still belongs to coder. -- Answer directly only for trivia already in context. One or two targeted reads are trivia, not a repo walk. Do not DIY-map explorer work. -- Before editing, classify the change. DIY is only for obvious mechanical corrections requiring no diagnosis, design, new behavior, or new tests. If uncertain, delegate. -- Read only enough to route and write a useful brief. Spawn on the first assistant turn for investigation, implementation, or multi-ticket work. Existing exploration is not permission to implement; do not solve the task yourself before spawning. - -# Rules -- Edit files with file tools only, never shell redirection or sed. -- Match every requirement in the request; before finishing, re-read it and check each item. -- Use manage_tasks for work of three or more steps. -- Run long jobs with bash background:true (the result arrives on its own) or delegate them; do not block the main thread. - -# Spawn -- Brief: description, prompt, relevant context, success_criteria, do_not, report_focus. The worker starts blank. -- Latest operator turn is law. Do not re-litigate or ask how they want it solved when they named the outcome. -- Spawn independent lanes in the same turn. There is no parallelization cap. Duplicate = the same live job, not the same repo. -- Split by path, ownership, or lens. One worker = one outcome. Independent tickets/files/reviews go out together. Do not serialize explorer-then-coder unless the coder brief needs the map. -- After spawn, yield. Mailbox (TUI/nested) or wait_agents (exec) delivers. Spawn the next independent wave before the first finishes. Do not poll. -- Use reports as the working record. Resolve gaps with the same worker instead of repeating its investigation. Route unfinished implementation back to coder, not yourself. -- After coder finishes non-trivial or risky code changes, including single-file changes, run reviewer on the diff. Skip review only for mechanical or docs-only diffs and say so. - -# Style -- Concise by default: one-beat status while workers run. No counsel-first. -- Alive, not sterile: dry wit is fine; do not perform, do not pad. -- No emoji. No essays. Personality is tone, not extra paragraphs.`; - -export function createDispatchSystemPrompt(): string { - return DISPATCH_CARD; -} - -export const dispatchPackage: AgentPackage = { - id: "dispatch", - primaryIntent: - "Coordinate named specialists; DIY only obvious mechanical corrections", - outOfLane: [ - "substantive product work without spawning, including single-file behavior changes", - "docs/design authorship (PRODUCT.md, ARCHITECTURE.md, DESIGN.md) except one-line fixes", - "walking the repo yourself; spawn explorers (as many as the question needs)", - "being the reviewer, planner, or coder by default", - "catch-all worker", - "searching the repo yourself after a worker stops without finishing", - ], - description: - "Primary dispatcher — classify, DIY tiny edits, spawn named specialists", - systemPrompt: DISPATCH_CARD, - tools: { allow: [...DISPATCH_TOOLS] }, - spawn: { - maySpawn: true, - allowlist: [ - "explorer", - "planner", - "coder", - "reviewer", - "designer", - "artist", - "warden", - "shakespeare", - "prober", - "qa-lead", - ], - }, - modelRole: "orchestrator", - tier: "orchestrator", -}; +// Public surface — re-exports only (no logic). Exports exactly +// `agent, defineAgent, director, tools, systemPrompt, config`. No `workflow` +// export. No Corbits shared package is re-exported; the package's `director` +// structurally satisfies the app's `DirectorPackage` (app-side drift-guard). +export { agent, defineAgent } from "./agent.js"; +export { director } from "./director.js"; +export { tools } from "./toolset.js"; +export { systemPrompt } from "./prompt.js"; +export { config } from "./config.js"; diff --git a/agents/dispatch/src/prompt.ts b/agents/dispatch/src/prompt.ts new file mode 100644 index 000000000..f3ed4bbd6 --- /dev/null +++ b/agents/dispatch/src/prompt.ts @@ -0,0 +1,52 @@ +// Dispatch card — the single prompt authority. Extracted byte-for-byte from the +// in-tree `src/agent/directors/dispatch/package.ts` (now deleted). `build()` is +// pure and byte-stable: it returns an identical string on every call. +const CARD = `# Role +You are Dispatch, the coordinator for Corbits Code. Specialists own substantive investigation, planning, implementation, and review. You own routing, briefs, coordination, and synthesis. + +# Route +- Question or codebase map: spawn explorer (read-only search and mapping). +- Requirements, architecture plan, or task breakdown: spawn planner (PRD.md, SOLUTION_SCOPE.md, BUILD_PLAN.md). +- Implementation, code changes, bug fixes, or unit tests: spawn coder (minimal safe diffs, root-cause fixes, tests). +- Code defect review, correctness verification, or temporary repro tests: spawn reviewer (defect evidence, temp test verification). +- UI/UX styling, design systems, design tokens, or DESIGN.md: spawn designer (impeccable style laws, DESIGN.md ownership). +- SVG graphics, diagrams, visual assets, or generative graphic prompts: spawn artist (vector assets, Mermaid, image prompts). +- Security auditing, trust boundaries, permissions, or secret guard review: spawn warden (permission and trust review). +- Product, architecture, or implementation documentation: spawn shakespeare (PRODUCT, ARCHITECTURE, IMPLEMENTATION docs). +- Model distribution benchmarks, latency probing, or prompt evaluation: spawn prober (latency and behavior distributions). +- Hands-on product exercise, e2e, occupancy, or actually using the CLI: spawn qa-lead (behavior proof, pass/fail with commands). Distinct from coder unit tests and prober measurement. + +# Delegation boundary +- Delegate by default. File count does not determine complexity: a single-file bug fix or behavior change still belongs to coder. +- Answer directly only for trivia already in context. One or two targeted reads are trivia, not a repo walk. Do not DIY-map explorer work. +- Before editing, classify the change. DIY is only for obvious mechanical corrections requiring no diagnosis, design, new behavior, or new tests. If uncertain, delegate. +- Read only enough to route and write a useful brief. Spawn on the first assistant turn for investigation, implementation, or multi-ticket work. Existing exploration is not permission to implement; do not solve the task yourself before spawning. + +# Rules +- Edit files with file tools only, never shell redirection or sed. +- Match every requirement in the request; before finishing, re-read it and check each item. +- Use manage_tasks for work of three or more steps. +- Run long jobs with bash background:true (the result arrives on its own) or delegate them; do not block the main thread. + +# Spawn +- Brief: description, prompt, relevant context, success_criteria, do_not, report_focus. The worker starts blank. +- Latest operator turn is law. Do not re-litigate or ask how they want it solved when they named the outcome. +- Spawn independent lanes in the same turn. There is no parallelization cap. Duplicate = the same live job, not the same repo. +- Split by path, ownership, or lens. One worker = one outcome. Independent tickets/files/reviews go out together. Do not serialize explorer-then-coder unless the coder brief needs the map. +- After spawn, yield. Mailbox (TUI/nested) or wait_agents (exec) delivers. Spawn the next independent wave before the first finishes. Do not poll. +- Use reports as the working record. Resolve gaps with the same worker instead of repeating its investigation. Route unfinished implementation back to coder, not yourself. +- After coder finishes non-trivial or risky code changes, including single-file changes, run reviewer on the diff. Skip review only for mechanical or docs-only diffs and say so. + +# Style +- Concise by default: one-beat status while workers run. No counsel-first. +- Alive, not sterile: dry wit is fine; do not perform, do not pad. +- No emoji. No essays. Personality is tone, not extra paragraphs.`; + +/** Stable identifier of the package's voice. */ +export const theme = "dispatch"; + +/** Byte-stable builder: returns the raw dispatch card, identical every call. */ +export const systemPrompt = { + theme, + build: (): string => CARD, +}; diff --git a/agents/dispatch/src/toolset.ts b/agents/dispatch/src/toolset.ts new file mode 100644 index 000000000..7fe0f5d6a --- /dev/null +++ b/agents/dispatch/src/toolset.ts @@ -0,0 +1,30 @@ +// Dispatch primary: orchestrator surface plus fleet discovery (Tier-1 only). +// Literal expansion of the `DISPATCH_TOOLS` constant in +// `src/agent/directors/tool-sets.ts` (the single tool authority). Drift is +// enforced by the in-tree `src/agent/directors/dispatch/package.test.ts`. +const DISPATCH_TOOLS = [ + "read_file", + "grep", + "search_files", + "list_dir", + "lsp", + "run_shell", + "web_fetch", + "web_search", + "skill_search", + "use_skill", + "write_file", + "edit_file", + "delete_file", + "spawn_agent", + "list_agents", + "close_agent", + "resume_agent", + "interrupt_agent", + "send_input", + "read_agent_trace", + "search_agents", +] as const; + +/** The dispatch tool allowlist — literal equal to the in-tree `DISPATCH_TOOLS`. */ +export const tools: readonly string[] = [...DISPATCH_TOOLS]; diff --git a/agents/dispatch/tsconfig.json b/agents/dispatch/tsconfig.json new file mode 100644 index 000000000..0d7100cbc --- /dev/null +++ b/agents/dispatch/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/bun.lock b/bun.lock index b22e2a2b1..2d4d50072 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,6 @@ "": { "name": "@corbits/code", "dependencies": { - "@corbits/code-agent-dispatch": "workspace:*", "@corbits/codex-provider": "0.1.1", "@corbits/oauth-core": "0.2.0", "@corbits/openai-responses": "0.2.1", diff --git a/package.json b/package.json index 19830a5c7..c427a0851 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "tar": "^7.5.1" }, "dependencies": { - "@corbits/code-agent-dispatch": "workspace:*", "@corbits/codex-provider": "0.1.1", "@corbits/oauth-core": "0.2.0", "@corbits/openai-responses": "0.2.1", diff --git a/src/agent/directors/dispatch/package.test.ts b/src/agent/directors/dispatch/package.test.ts index 45041f252..ccf700fe5 100644 --- a/src/agent/directors/dispatch/package.test.ts +++ b/src/agent/directors/dispatch/package.test.ts @@ -1,42 +1,50 @@ import { describe, expect, test } from "bun:test"; -import { dispatchPackage } from "@corbits/code-agent-dispatch"; -import { dispatchPackage as inTreeDispatchPackage } from "./package.js"; +import { + director, + tools, + config, + systemPrompt, +} from "@corbits/code-agent-dispatch"; import { DISPATCH_TOOLS } from "../tool-sets.js"; import { DIRECTOR_REGISTRY } from "../registry.js"; import type { DirectorPackage } from "../types.js"; describe("dispatchPackage", () => { test("workspace package satisfies the director contract and feeds the registry", () => { - const asDirector: DirectorPackage = dispatchPackage; + const asDirector: DirectorPackage = director; expect(asDirector.id).toBe("dispatch"); - expect(DIRECTOR_REGISTRY.dispatch).toBe(dispatchPackage); - expect([...dispatchPackage.tools.allow]).toEqual([...DISPATCH_TOOLS]); + expect(DIRECTOR_REGISTRY.dispatch).toBe(director); + expect([...director.tools.allow]).toEqual([...DISPATCH_TOOLS]); + expect(new Set(director.tools.allow).size).toBe( + director.tools.allow.length, + ); }); - test("extracted copy stays orchestrator-scoped and spawning", () => { - expect(dispatchPackage.tools.allow).toContain("spawn_agent"); - expect(dispatchPackage.tools.allow).toContain("search_agents"); - expect(dispatchPackage.spawn.maySpawn).toBe(true); - expect(dispatchPackage.spawn.allowlist).toEqual([ - "explorer", - "planner", - "coder", - "reviewer", - "designer", - "artist", - "warden", - "shakespeare", - "prober", - "qa-lead", - ]); - expect(dispatchPackage.tier).toBe("orchestrator"); - expect(dispatchPackage.modelRole).toBe("orchestrator"); + test("spawn/tier/modelRole match config (Tier-1 orchestrator)", () => { + expect(director.spawn).toEqual(config.spawn); + expect(director.tier).toBe(config.tier); + expect(director.modelRole).toBe(config.modelRole); + expect(director.spawn.maySpawn).toBe(true); + expect(director.tier).toBe("orchestrator"); + expect(director.modelRole).toBe("orchestrator"); }); - test("workspace card matches retained in-tree card until removal", () => { - expect(dispatchPackage.systemPrompt).toBe( - inTreeDispatchPackage.systemPrompt, - ); + test("prompt authority is byte-stable", () => { + expect(director.systemPrompt).toBe(systemPrompt.build()); + expect(systemPrompt.build()).toBe(systemPrompt.build()); + }); + + test("tools surface matches DISPATCH_TOOLS and is orchestration-scoped", () => { + expect([...tools]).toEqual([...DISPATCH_TOOLS]); + // Orchestration surface: search_agents IS present, wait_agents is NOT. + expect(tools).toContain("search_agents"); + expect(tools).not.toContain("wait_agents"); + expect(tools).not.toContain("ask_operator"); + expect(tools).toContain("spawn_agent"); + }); + + test("tools allowlist has no duplicates", () => { + expect(new Set([...tools]).size).toBe(tools.length); }); }); diff --git a/src/agent/directors/registry.ts b/src/agent/directors/registry.ts index 166daa03b..d26d9a431 100644 --- a/src/agent/directors/registry.ts +++ b/src/agent/directors/registry.ts @@ -1,6 +1,6 @@ import { director as shakespeareDirector } from "@corbits/code-agent-shakespeare"; import { director as proberDirector } from "@corbits/code-agent-prober"; -import { dispatchPackage } from "@corbits/code-agent-dispatch"; +import { director as dispatchDirector } from "@corbits/code-agent-dispatch"; import type { AgentProfile, CapabilityFilter } from "../profile-types.js"; import { director as artistDirector } from "@corbits/code-agent-artist"; import { coderPackage } from "./coder/package.js"; @@ -37,7 +37,7 @@ export const INTENT_DEFAULT_DIRECTOR: Readonly< */ export const DIRECTOR_REGISTRY: Readonly> = { - dispatch: dispatchPackage, + dispatch: dispatchDirector, explorer: explorerPackage, planner: plannerPackage, coder: coderPackage, diff --git a/src/agent/prompts.ts b/src/agent/prompts.ts index 7f85aea90..39cc73a13 100644 --- a/src/agent/prompts.ts +++ b/src/agent/prompts.ts @@ -2,7 +2,7 @@ import type { EnvironmentInfo } from "./environment.js"; import type { SkillSummary } from "../extensions/skills.js"; import type { SessionMode } from "../config/session-mode.js"; import type { ToolAvailability } from "./tool-search.js"; -import { createDispatchSystemPrompt } from "@corbits/code-agent-dispatch"; +import { systemPrompt as dispatchSystemPrompt } from "@corbits/code-agent-dispatch"; import { buildWorkerContract, buildWorkerToolNames, @@ -48,7 +48,7 @@ export function buildChatRole( ): string { // Primary session identity is the closed Dispatch director package. // Harness facts / guidelines still append after this role in baseSection. - return createDispatchSystemPrompt(); + return dispatchSystemPrompt.build(); } // Facts the model cannot derive from its training: what the permission layer From 41d9cfe790e73c5538592987f0039b7914409972 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 8 Oct 2026 20:24:29 -0700 Subject: [PATCH 5/8] style(agent-dispatch): strip redundant commentary from extracted package --- agents/dispatch/src/agent.ts | 9 ++++----- agents/dispatch/src/director.ts | 8 ++------ agents/dispatch/src/index.ts | 7 +++---- agents/dispatch/src/prompt.ts | 7 ++----- agents/dispatch/src/toolset.ts | 6 ++---- 5 files changed, 13 insertions(+), 24 deletions(-) diff --git a/agents/dispatch/src/agent.ts b/agents/dispatch/src/agent.ts index ee8ab57e8..075ff9371 100644 --- a/agents/dispatch/src/agent.ts +++ b/agents/dispatch/src/agent.ts @@ -1,7 +1,7 @@ -// Dispatch assembled agent — ready unit + package defineAgent wrapper. -// Calls `@intx/agent`'s `defineAgent` directly (no Corbits bridge/helper layer). -// Tool mounting is consumer/app-side: `tools` here is [] (the allowlist surface -// is exported from `./toolset.js` and wired by the app harness). +// Dispatch assembled agent — ready unit + package defineAgent wrapper. Calls +// `@intx/agent`'s `defineAgent` directly (no Corbits bridge). Tool mounting is +// consumer/app-side: `tools` here is [] (the allowlist surface is exported from +// `./toolset.js` and wired by the app harness). import { defineAgent as coreDefineAgent, @@ -10,7 +10,6 @@ import { import { systemPrompt } from "./prompt.js"; -/** The assembled dispatch agent — ready to hand to `runLocal` / `runWorkflow`. */ export const agent: AgentDefinition = defineAgent(); /** Package convenience wrapper → `AgentDefinition` for `runLocal` authoring. */ diff --git a/agents/dispatch/src/director.ts b/agents/dispatch/src/director.ts index c2a3cf1b4..4d82f7ff0 100644 --- a/agents/dispatch/src/director.ts +++ b/agents/dispatch/src/director.ts @@ -1,13 +1,9 @@ -// Dispatch director card — the `DirectorPackage` data, extracted byte-for-byte -// from the in-tree `src/agent/directors/dispatch/package.ts` (now deleted). -// Structurally satisfies the app's `DirectorPackage` (asserted in-tree by -// `src/agent/directors/dispatch/package.test.ts`); it imports @intx/* only. - +// Dispatch director card; imports @intx/* only. The app asserts this satisfies +// `DirectorPackage` in-tree (src/agent/directors/dispatch/package.test.ts). import { tools } from "./toolset.js"; import { systemPrompt } from "./prompt.js"; import { config } from "./config.js"; -/** The assembled dispatch director card. */ export const director = { id: "dispatch", primaryIntent: diff --git a/agents/dispatch/src/index.ts b/agents/dispatch/src/index.ts index bd370bcbb..4badbc49d 100644 --- a/agents/dispatch/src/index.ts +++ b/agents/dispatch/src/index.ts @@ -1,7 +1,6 @@ -// Public surface — re-exports only (no logic). Exports exactly -// `agent, defineAgent, director, tools, systemPrompt, config`. No `workflow` -// export. No Corbits shared package is re-exported; the package's `director` -// structurally satisfies the app's `DirectorPackage` (app-side drift-guard). +// Public surface — re-exports only, so the package imports no Corbits code +// (the app asserts the director contract in-tree via drift-guard). No `workflow` +// export: workflows consume `agent`. export { agent, defineAgent } from "./agent.js"; export { director } from "./director.js"; export { tools } from "./toolset.js"; diff --git a/agents/dispatch/src/prompt.ts b/agents/dispatch/src/prompt.ts index f3ed4bbd6..8bd278704 100644 --- a/agents/dispatch/src/prompt.ts +++ b/agents/dispatch/src/prompt.ts @@ -1,6 +1,5 @@ -// Dispatch card — the single prompt authority. Extracted byte-for-byte from the -// in-tree `src/agent/directors/dispatch/package.ts` (now deleted). `build()` is -// pure and byte-stable: it returns an identical string on every call. +// Single prompt authority: `build()` is pure and byte-stable — it returns an +// identical string on every call. const CARD = `# Role You are Dispatch, the coordinator for Corbits Code. Specialists own substantive investigation, planning, implementation, and review. You own routing, briefs, coordination, and synthesis. @@ -42,10 +41,8 @@ You are Dispatch, the coordinator for Corbits Code. Specialists own substantive - Alive, not sterile: dry wit is fine; do not perform, do not pad. - No emoji. No essays. Personality is tone, not extra paragraphs.`; -/** Stable identifier of the package's voice. */ export const theme = "dispatch"; -/** Byte-stable builder: returns the raw dispatch card, identical every call. */ export const systemPrompt = { theme, build: (): string => CARD, diff --git a/agents/dispatch/src/toolset.ts b/agents/dispatch/src/toolset.ts index 7fe0f5d6a..3b7fe8f02 100644 --- a/agents/dispatch/src/toolset.ts +++ b/agents/dispatch/src/toolset.ts @@ -1,7 +1,6 @@ // Dispatch primary: orchestrator surface plus fleet discovery (Tier-1 only). -// Literal expansion of the `DISPATCH_TOOLS` constant in -// `src/agent/directors/tool-sets.ts` (the single tool authority). Drift is -// enforced by the in-tree `src/agent/directors/dispatch/package.test.ts`. +// Drift against the app's DISPATCH_TOOLS is enforced by the in-tree +// src/agent/directors/dispatch/package.test.ts. const DISPATCH_TOOLS = [ "read_file", "grep", @@ -26,5 +25,4 @@ const DISPATCH_TOOLS = [ "search_agents", ] as const; -/** The dispatch tool allowlist — literal equal to the in-tree `DISPATCH_TOOLS`. */ export const tools: readonly string[] = [...DISPATCH_TOOLS]; From a5de85dbcf649a4d2f9fb775d2bb85ff337b5d1c Mon Sep 17 00:00:00 2001 From: "user.email" Date: Thu, 8 Oct 2026 22:21:55 -0700 Subject: [PATCH 6/8] chore(dead-exports): allowlist cl-9802-extract-agent-dispatch package public surface --- scripts/dead-export-allowlist.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/dead-export-allowlist.txt b/scripts/dead-export-allowlist.txt index 0003b259a..03b4b8571 100644 --- a/scripts/dead-export-allowlist.txt +++ b/scripts/dead-export-allowlist.txt @@ -38,6 +38,16 @@ src/agent/directors/identity.ts: Record packages/opencode-go/src/models.ts: satisfies packages/opencode-go/src/models.ts: readonly +# Agent-package public API (CL-9800 extraction wave): each @corbits/code-agent-* +# workspace package's public surface (agent/defineAgent + the config card) is +# consumed by the app registry/barrel once the fleet merges; on a per-extract +# branch they have no in-repo consumer yet. Public API by design — owned by the +# agent-template lane; remove with the code if the package is deleted. +agents/dispatch/src/index.ts: agent +agents/dispatch/src/index.ts: defineAgent +agents/dispatch/src/config.ts: satisfies +agents/dispatch/src/config.ts: boolean + # Versioned prompt-variance package public surface (CL-8269 lane, # agent-owned). Family rows data re-exported through the package entry point; # no in-repo barrel consumer exists yet for the entries below (src/ consumes From 13d711b8e1c6b0fcb4b831bbdd9d65397af7c7f9 Mon Sep 17 00:00:00 2001 From: "user.email" Date: Thu, 8 Oct 2026 23:52:04 -0700 Subject: [PATCH 7/8] fix(agent-dispatch): drop hardcoded inference source and release tag --- agents/dispatch/src/agent.ts | 1 - agents/dispatch/src/toolset.ts | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/agents/dispatch/src/agent.ts b/agents/dispatch/src/agent.ts index 075ff9371..5abd16364 100644 --- a/agents/dispatch/src/agent.ts +++ b/agents/dispatch/src/agent.ts @@ -20,6 +20,5 @@ export function defineAgent(): AgentDefinition { tools: [], capabilities: [], inference: { sources: [] }, - tags: { fleet: "0.3.36", lane: "dispatch" }, }); } diff --git a/agents/dispatch/src/toolset.ts b/agents/dispatch/src/toolset.ts index 3b7fe8f02..f273a330e 100644 --- a/agents/dispatch/src/toolset.ts +++ b/agents/dispatch/src/toolset.ts @@ -1,7 +1,7 @@ // Dispatch primary: orchestrator surface plus fleet discovery (Tier-1 only). // Drift against the app's DISPATCH_TOOLS is enforced by the in-tree // src/agent/directors/dispatch/package.test.ts. -const DISPATCH_TOOLS = [ +export const tools = [ "read_file", "grep", "search_files", @@ -24,5 +24,3 @@ const DISPATCH_TOOLS = [ "read_agent_trace", "search_agents", ] as const; - -export const tools: readonly string[] = [...DISPATCH_TOOLS]; From 915227a0a779e71df716a70c0959fd7bd967da68 Mon Sep 17 00:00:00 2001 From: "user.email" Date: Fri, 9 Oct 2026 00:08:11 -0700 Subject: [PATCH 8/8] style(agent-dispatch): trailing newline for registry --- src/agent/directors/registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/directors/registry.ts b/src/agent/directors/registry.ts index d26d9a431..8741a0318 100644 --- a/src/agent/directors/registry.ts +++ b/src/agent/directors/registry.ts @@ -139,4 +139,4 @@ export function directorProfiles(): AgentProfile[] { return listDirectors() .filter((pkg) => pkg.id !== "dispatch") .map(packageToProfile); -} \ No newline at end of file +}