fix(runtime): stop gating pi tool calls; Co-Founder approvals live at API layer
The Co-Founder beforeToolCall hook (introduced in 77f81a7) gated every
non-readonly pi tool call as a `dangerous_command` approval. Because
agents call `POST /api/companies/:id/messages`, `/agents` (hire), and
every other company mutation via bash→node/curl, the first bash call in
every Co-Founder run got parked on a 30-minute approval timeout. Users
observed it as "agents never talk to each other" and "every bash command
waits for approval"; both symptoms are the same bug.
The business-level approval flow (`hire_agent`, `document_review`,
`plan_review`, etc. in execution-policy.ts) already gates the events
users actually care about, at the HTTP route layer where the event
originates (routes-agents.ts:368 for hiring, document routes for
deliverables). Stacking a pi-level hook on top was duplicate gating with
the wrong taxonomy.
Removed:
- src/main/agent-runtime/approval-hook.ts (entire file, 137 LOC)
- `beforeToolCall` conditional in worker-runner.ts and the
corresponding RPC method in worker-types.ts and runtime-bridge.ts
- `approvalHook` field + `resolveApproval` callback + shutdown
`cancelAll` in runtime-bridge.ts / service-graph-pi-runtime.ts /
wire-api-server.ts
Unchanged:
- `dangerous_command` approval type itself (still used by automation
workflows and routes-misc.ts)
- CEO mode (already never attached the hook)
- All business-level approval types and their wiring
This commit is contained in:
1 parent
4cfb926971
commit
02ba887610
7 files changed
+8
-200
No files matched your search
@@ -109,13 +109,6 @@ export class WorkerRunner {
|
||||
const cwd = job.workspacePath ?? process.cwd();
|
||||
const tools = createCodingTools(cwd) as never;
|
||||
|
||||
// Co-Founder mode gates tool calls through the HumanApprovalHook in
|
||||
// main. CEO mode skips the hook entirely (zero IPC overhead per tool
|
||||
// call). Two distinct Agent-construction paths instead of a runtime
|
||||
// `if mode === ...` inside a shared hook body.
|
||||
const gateToolCalls = job.executionMode === "ai_cofounder";
|
||||
const TOOL_APPROVAL_TIMEOUT_MS = 30 * 60 * 1000;
|
||||
|
||||
const agent = new Agent({
|
||||
initialState: {
|
||||
systemPrompt: job.systemPrompt ?? "",
|
||||
@@ -129,29 +122,6 @@ export class WorkerRunner {
|
||||
apiKey: prepared.apiKey,
|
||||
headers: { ...(options?.headers ?? {}), ...(extraHeaders ?? {}) },
|
||||
}),
|
||||
...(gateToolCalls
|
||||
? {
|
||||
beforeToolCall: async (context) => {
|
||||
const verdict = await rpc.invoke(
|
||||
job.runId,
|
||||
"beforeToolCall",
|
||||
{
|
||||
agentId: job.agentId,
|
||||
companyId: job.companyId,
|
||||
taskId: job.taskId,
|
||||
toolCallId: context.toolCall.id,
|
||||
toolName: context.toolCall.name,
|
||||
args: context.args,
|
||||
},
|
||||
{ timeoutMs: TOOL_APPROVAL_TIMEOUT_MS },
|
||||
);
|
||||
if (verdict.ok) {
|
||||
return verdict.result as { block?: boolean; reason?: string } | undefined;
|
||||
}
|
||||
return { block: true, reason: verdict.error };
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
const unsubscribe = agent.subscribe(async (event) => {
|
||||
|
||||
@@ -49,7 +49,7 @@ export const workerMessageSchema = z.union([
|
||||
type: z.literal("rpc-request"),
|
||||
rpcId: z.string(),
|
||||
runId: z.string().uuid(),
|
||||
method: z.enum(["persistMessage", "prepareRun", "beforeToolCall"]),
|
||||
method: z.enum(["persistMessage", "prepareRun"]),
|
||||
payload: z.unknown(),
|
||||
}),
|
||||
]);
|
||||
|
||||
Reference in new issue
Block a user