Run the CEO agent autonomously with a real tool-use loop
The new app reached a real LLM but never ran an agent: the kickoff task_run sat queued forever and runTask was a single text completion with no tools. This adds the missing runtime: - provider-tool-turn: OpenAI Responses + Anthropic Messages tool-use turns - agent-tools + deliverable-writer: hire_agent, create_task, update_task_status, write_deliverable (workspace-scoped, traversal-safe) - agent-run-loop: real tool-use loop, capped iterations - task-queue-worker: claims queued runs in a transaction and executes them task-execution now drives the loop; a background worker auto-runs the bootstrap CEO run so it hires its team, delegates tasks, and writes the first deliverable into the bound workspace. Autonomy is gated by AGENTCOMPANY_NEW_AUTORUN (tests run with it off to stay hermetic).
This commit is contained in:
1 parent
1c7b81830e
commit
0575712e02
15 files changed
+1008
-74
No files matched your search
@@ -0,0 +1,63 @@
|
||||
import type { ProviderService } from "./provider-service";
|
||||
import { AGENT_TOOLS, dispatchAgentTool, type AgentToolContext, type AgentToolDeps } from "./agent-tools";
|
||||
import type { AgentTurnMessage } from "./provider-tool-turn";
|
||||
|
||||
const MAX_ITERATIONS = 12;
|
||||
|
||||
export interface AgentRunLoopInput {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
workspaceId: string | null;
|
||||
instructions: string;
|
||||
initialUserTurn: string;
|
||||
toolContext: AgentToolContext;
|
||||
}
|
||||
|
||||
export interface AgentRunLoopResult {
|
||||
finalText: string;
|
||||
iterations: number;
|
||||
toolCallCount: number;
|
||||
lastRequestId: string;
|
||||
}
|
||||
|
||||
export interface AgentRunLoopDeps {
|
||||
providers: Pick<ProviderService, "runToolTurn">;
|
||||
tools: AgentToolDeps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drives a real tool-use loop against the live provider: prompt the model with
|
||||
* the agent tools, execute every tool call it makes, feed results back, and
|
||||
* repeat until the model returns a turn with no tool calls (it is done) or the
|
||||
* iteration cap is hit (which throws rather than silently truncating).
|
||||
*/
|
||||
export async function runAgentLoop(deps: AgentRunLoopDeps, input: AgentRunLoopInput): Promise<AgentRunLoopResult> {
|
||||
const messages: AgentTurnMessage[] = [{ role: "user", text: input.initialUserTurn }];
|
||||
let toolCallCount = 0;
|
||||
let lastRequestId = "";
|
||||
|
||||
for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration += 1) {
|
||||
const turn = await deps.providers.runToolTurn({
|
||||
orgId: input.orgId,
|
||||
providerId: input.providerId,
|
||||
workspaceId: input.workspaceId,
|
||||
instructions: input.instructions,
|
||||
messages,
|
||||
tools: AGENT_TOOLS,
|
||||
});
|
||||
lastRequestId = turn.requestId;
|
||||
|
||||
if (turn.toolCalls.length === 0) {
|
||||
return { finalText: turn.text, iterations: iteration, toolCallCount, lastRequestId };
|
||||
}
|
||||
|
||||
messages.push({ role: "assistant", text: turn.text, toolCalls: turn.toolCalls });
|
||||
const results = turn.toolCalls.map((call) => {
|
||||
toolCallCount += 1;
|
||||
return { toolCallId: call.id, toolName: call.name, output: dispatchAgentTool(deps.tools, input.toolContext, call) };
|
||||
});
|
||||
messages.push({ role: "tool", results });
|
||||
}
|
||||
|
||||
throw new Error(`Agent run did not converge within ${MAX_ITERATIONS} iterations.`);
|
||||
}
|
||||
Reference in new issue
Block a user