import { execFileSync } from "node:child_process"; import type { AgentId } from "../external.ts"; import { log } from "../utils/cli.ts"; import type { ResolvedInstructions } from "../utils/instructions.ts"; import type { ResolvedPayload } from "../utils/payload.ts"; import type { TodoTracker } from "../utils/todoTracking.ts"; // maximum number of stderr lines to keep in the rolling buffer during agent execution export const MAX_STDERR_LINES = 20; // ── post-run commit enforcement ───────────────────────────────────────────────── export const MAX_COMMIT_RETRIES = 3; export function getGitStatus(): string { try { return execFileSync("git", ["status", "--porcelain"], { encoding: "utf-8", timeout: 10_000, }).trim(); } catch { return ""; } } export function buildCommitPrompt(_agentId: AgentId, status: string): string { return [ `UNCOMMITTED CHANGES — the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`, "", "```", status, "```", ].join("\n"); } /** * token/cost usage data from a single agent run */ export interface AgentUsage { agent: string; inputTokens: number; outputTokens: number; cacheReadTokens?: number | undefined; cacheWriteTokens?: number | undefined; costUsd?: number | undefined; } export interface AgentToolUseEvent { toolName: string; input: unknown; } /** * Result returned by agent execution */ export interface AgentResult { success: boolean; output?: string | undefined; error?: string | undefined; metadata?: Record; usage?: AgentUsage | undefined; } /** * Minimal context passed to agent.run() */ export interface AgentRunContext { payload: ResolvedPayload; resolvedModel?: string | undefined; mcpServerUrl: string; tmpdir: string; instructions: ResolvedInstructions; todoTracker?: TodoTracker | undefined; /** * called synchronously when the agent subprocess is killed for inner * activity timeout. lets main.ts tear down shared resources (MCP HTTP * server) so lingering SSE reconnects don't keep the outer timer alive. */ onActivityTimeout?: (() => void) | undefined; onToolUse?: ((event: AgentToolUseEvent) => void) | undefined; } export interface Agent { name: AgentId; install: (token?: string) => Promise; run: (ctx: AgentRunContext) => Promise; } export const agent = (input: Agent): Agent => { return { ...input, run: async (ctx: AgentRunContext): Promise => { log.debug(`» payload: ${JSON.stringify(ctx.payload, null, 2)}`); return input.run(ctx); }, }; };