Inject hire_agent and file_approval into CEO agent (C4 part 3/5)

Wire the two internal tools from parts 1-2 end-to-end:

- `invokeInternalTool` RPC method added to RuntimeBridge; worker tool
  `execute` calls route here, handler dispatches by tool name via the
  new `agent-runtime/internal-tools/router.ts`.
- `InternalToolCallbacks` (`onApprovalCreated` / `onApprovalResolved`
  / `onDomainChanged`) are late-bound on the bridge via
  `setInternalToolCallbacks`; `wire-api-server` now cross-wires the
  same callbacks used by the HTTP approval route so thread integration
  / automation fire identically across both entry points.
- `RunJob.internalTools` (new field) carries the per-run tool list
  main decides at dispatch time via `orchestration/internal-tool-
  selector.ts`. Selection rule: `hire_agent` → any agent with
  `canRequestHire` authority; `file_approval` → CEOs only (scoped to
  the `approve_ceo_strategy` use case).
- Worker `createPiAgent` call site now assembles `codingTools +
  internalTools` before handing to pi. Exhaustiveness guard on the
  switch catches new tool names at compile time.

No prompt-level wiring yet — the `initial-user-turn` and
`team-building.en.md` updates ship in parts 4 and 5 so this commit
only adds the tool surface without changing the CEO's observable
behaviour (the existing HTTP path is still the one the prompt tells
the CEO to call).

tests: pnpm typecheck + pnpm test:unit (529) + pnpm test:eval (8/8) green
This commit is contained in:
ZacharyZhang-NY committed 2026-04-20 14:25:44 -04:00
1 parent d65f156732
commit b839ce4f23
9 files changed
+265 -6

No files matched your search

+23 -3
View File
@@ -90,6 +90,28 @@ export function wireApiServer(deps: WireApiServerDeps): AgentApiServer {
automation.handleApprovalResolved(approvalId, companyId, decision);
deps.threadIntegration.onApprovalResolved(approvalId, companyId, decision);
};
const onApprovalCreated = (
approvalId: string,
companyId: string,
taskId: string | null,
approvalType: string,
summary: string,
) => {
deps.threadIntegration.onApprovalCreated(approvalId, companyId, taskId, approvalType, summary);
};
// Cross-wire the same callbacks into the pi runtime bridge so the
// internal-tool path (C4) triggers identical thread/automation
// notifications as the HTTP route.
const piRuntime = deps.serviceGraph.getPiRuntime();
if (!piRuntime) {
throw new Error("Pi runtime is not initialized; cannot wire internal-tool callbacks.");
}
piRuntime.runtimeBridge.setInternalToolCallbacks({
onApprovalCreated,
onApprovalResolved,
onDomainChanged: deps.publishDomainChanged,
});
const allowedOrigins = [deps.isDev && deps.devServerUrl ? new URL(deps.devServerUrl).origin : null, "app://-"]
.filter((origin): origin is string => Boolean(origin));
@@ -102,9 +124,7 @@ export function wireApiServer(deps: WireApiServerDeps): AgentApiServer {
onCommentPosted: (taskId, companyId, authorAgentId) => {
dispatchAutomation("comment_posted", { companyId, taskId, agentId: authorAgentId ?? undefined });
},
onApprovalCreated: (approvalId, companyId, taskId, approvalType, summary) => {
deps.threadIntegration.onApprovalCreated(approvalId, companyId, taskId, approvalType, summary);
},
onApprovalCreated,
onApprovalResolved,
createDocument: (input) =>
createDocumentWithLifecycle(