// The agent module's use cases: start a run, read one, list them, cancel one, and the approval // queue. Every write is the run row and the outbox event in one transaction; the worker's // subscriber enqueues the run job, and the engine (@conusai-kit/agent) does the durable execution. import { AGENT_ENTITIES, type AgentRunRecord, type AgentStepRecord, admitPlan, agentRuns, answerOf, clampBudget, dataAgentPersistence, dataRunStore, isTerminalRunStatus, } from "@conusai-kit/agent" import { resumeAfter } from "@conusai-kit/agent/agui" import { notFound, validationFailed } from "@conusai-kit/core" import { type ApprovalView, RUN_JOB, type RunView, runResumeRequested } from "../contract/index.js" import type { AgentDeps, AgentScope } from "./ports.js" const DEFAULT_BUDGET = { maxCostMicrocents: 100_000_000, maxSteps: 10, maxDurationSeconds: 600 } const PAGE = 50 export const runViewOf = async (deps: AgentDeps, scope: AgentScope, run: { id: string }): Promise => { const store = dataRunStore(deps.data, scope) const record = await store.get(run.id) if (!record) throw notFound("no such run") const steps = await store.listSteps(run.id) const stepViews = record.plan.map((planned) => { const logged = steps.find((candidate) => candidate.key === planned.key) return { key: planned.key, label: planned.label, tool: planned.tool, args: planned.args, needsApproval: planned.needsApproval === true, status: logged?.status ?? ("pending" as const), attempts: logged?.attempts ?? 0, error: logged?.error ?? null, } }) const answer = await answerOf(dataAgentPersistence(deps.data, scope), record) return { runId: record.id, goal: record.goal, status: record.status, failureCode: record.failureCode, failureDetail: record.failureDetail, plan: stepViews, usageCostMicrocents: record.usageCostMicrocents, usageSteps: record.usageSteps, percent: record.plan.length === 0 ? 0 : Math.min(record.usageSteps, record.plan.length) / record.plan.length, cancelRequested: record.cancelRequestedAt !== null, answer, answerProvenance: answer === null ? null : record.answerProvenance, actorId: record.actorId, createdAt: record.createdAt, startedAt: record.startedAt, finishedAt: record.finishedAt, } } /** * Starts a run: the plan is admitted against the hard ceilings before anything is written, the run * row and the resume event land in one transaction, and the subscriber's enqueue (singleton on * the run id) is what actually starts the work. Idempotency is the caller's idempotency key at * the transport; a repeated start with the same key answers the first run. */ export async function startRun( deps: AgentDeps, scope: AgentScope, input: { goal: string plan: { key: string label: string tool: string args: Record needsApproval?: boolean | undefined }[] budget?: { maxCostMicrocents?: number | undefined maxSteps?: number | undefined maxDurationSeconds?: number | undefined } }, ): Promise { const budget = clampBudget({ maxCostMicrocents: input.budget?.maxCostMicrocents ?? DEFAULT_BUDGET.maxCostMicrocents, maxSteps: input.budget?.maxSteps ?? DEFAULT_BUDGET.maxSteps, maxDurationSeconds: input.budget?.maxDurationSeconds ?? DEFAULT_BUDGET.maxDurationSeconds, }) // The plan is data here; the engine's steps come from these rows in the worker. admitPlan( input.plan.map((planned) => ({ key: planned.key, label: planned.label, invoke: () => Promise.resolve({ ok: true as const }), })), budget, ) const run = await deps.data.unitOfWork.run(scope, async (tx) => { const row = await tx.of(agentRuns).insert({ goal: input.goal, plan: input.plan.map((planned) => ({ key: planned.key, label: planned.label, tool: planned.tool, args: planned.args, ...(planned.needsApproval === true ? { needsApproval: true } : {}), })), status: "queued", failureCode: null, failureDetail: null, actorId: scope.actor.userId, budgetCostMicrocents: budget.maxCostMicrocents, budgetSteps: budget.maxSteps, budgetSeconds: budget.maxDurationSeconds, usageCostMicrocents: 0, usageSteps: 0, cancelRequestedAt: null, startedAt: null, finishedAt: null, }) await tx.publish(runResumeRequested, { orgId: scope.orgId, workspaceId: scope.workspaceId, projectId: scope.projectId, runId: row.id, }) return row }) return runViewOf(deps, scope, run) } /** * Opens a run's event stream: the run must be one this scope can read (another organization's or * project's run is not found, exactly as `agent.run.status` answers), and the resume point must * belong to it. Answers the offset to replay after and a reader of the durable record. The reader * reads the run row before its step log: both only move forward, so a step log newer than its run * row projects a prefix of the next projection, never a terminal event ahead of its steps. */ export async function openRunEvents( deps: AgentDeps, scope: AgentScope, input: { runId: string; after?: string | undefined }, ): Promise<{ after: number load: () => Promise<{ run: AgentRunRecord; steps: readonly AgentStepRecord[]; answer: string | null } | null> }> { const store = dataRunStore(deps.data, scope) const persistence = dataAgentPersistence(deps.data, scope) if (!(await store.get(input.runId))) throw notFound("no such run") let after: number try { after = resumeAfter(input.runId, input.after) } catch { throw validationFailed([{ path: "after", message: "the offset does not belong to this run" }], "invalid offset") } return { after, load: async () => { const run = await store.get(input.runId) return run ? { run, steps: await store.listSteps(input.runId), answer: await answerOf(persistence, run) } : null }, } } export async function getRun(deps: AgentDeps, scope: AgentScope, runId: string): Promise { return runViewOf(deps, scope, { id: runId }) } export async function listRuns( deps: AgentDeps, scope: AgentScope, input: { cursor?: string | undefined; limit?: number | undefined }, ): Promise<{ runs: RunView[]; cursor?: string; hasMore: boolean }> { const store = dataRunStore(deps.data, scope, { pageSize: PAGE }) const page = await store.list({ ...(input.cursor === undefined ? {} : { cursor: input.cursor }), ...(input.limit === undefined ? {} : { limit: input.limit }), }) return { runs: await Promise.all(page.runs.map((run) => runViewOf(deps, scope, run))), ...("cursor" in page ? { cursor: page.cursor } : {}), hasMore: page.hasMore, } } /** The most ids one batch-get reads (contract C-05). */ export const MAX_BATCH = 100 /** * The live collection's read-back: the runs with these ids, as the list shows them. A run this caller * cannot see is not in the answer: the repository's tenant wall leaves it out. */ export async function getManyRuns(deps: AgentDeps, scope: AgentScope, runIds: readonly string[]): Promise { const page = await deps.data .forScope(scope) .of(agentRuns) .list({ where: [{ field: "id", op: "in", values: [...new Set(runIds)] }], limit: MAX_BATCH }) return Promise.all(page.items.map((run) => runViewOf(deps, scope, run))) } /** Cancels a run: the persisted request is honored between steps and inside the in-flight call. */ export async function cancelRun( deps: AgentDeps, scope: AgentScope, runId: string, ): Promise<{ runId: string; cancelled: boolean }> { const store = dataRunStore(deps.data, scope) const record = await store.get(runId) if (!record) throw notFound("no such run") if (record.cancelRequestedAt !== null) return { runId, cancelled: true } await store.requestCancel(runId, new Date().toISOString()) // A parked run only notices the request when it is resumed; enqueue the resume so the cancel // lands even while the run waits in awaiting_approval. await deps.data.unitOfWork.run(scope, async (tx) => { await tx.publish(runResumeRequested, { orgId: scope.orgId, workspaceId: scope.workspaceId, projectId: scope.projectId, runId, }) }) return { runId, cancelled: true } } /** * The approval queue: every step that parked an approval, with the EXACT stored proposed * arguments. Nothing here is a model's narration; the arguments are what the tool was called * with, copied before they were proposed. */ export async function listApprovals(deps: AgentDeps, scope: AgentScope): Promise<{ approvals: ApprovalView[] }> { const approvals: ApprovalView[] = [] let cursor: string | undefined for (;;) { const page = await deps.data .forScope(scope) .of(agentRuns) .list({ order: [{ field: "createdAt", direction: "desc" }], limit: PAGE, ...(cursor === undefined ? {} : { cursor }), }) for (const run of page.items) { if (isTerminalRunStatus(run.status)) continue const steps = await dataRunStore(deps.data, scope).listSteps(run.id) for (const step of steps) { // Only what still waits for a person: a decided approval or a step that moved on is history. if (step.approval === null || step.approval.decision !== null || step.status !== "pending") continue approvals.push({ runId: run.id, stepKey: step.key, goal: run.goal, tool: step.approval.tool, arguments: step.approval.arguments, reviewUrl: step.approval.reviewUrl, createdAt: run.createdAt, }) } } if (!page.hasMore) break cursor = page.cursor } return { approvals } } /** * Records a person's decision on the step and resumes the run. An approved `needsApproval` step * resumes its TanStack AI interrupt and runs once; an approved platform proposal re-enters the * parked step, whose tools adapter re-calls with the step's idempotency key so the platform executes * the approved, persisted proposal exactly once. Rejection cancels the run: nothing is executed. A * step with nothing parked, or one already decided, is refused. */ export async function reviewApproval( deps: AgentDeps, scope: AgentScope, input: { runId: string; stepKey: string; decision: "approve" | "reject" }, ): Promise<{ runId: string; status: RunView["status"] }> { const store = dataRunStore(deps.data, scope) const record = await store.get(input.runId) if (!record) throw notFound("no such run") await store.decideApproval(input.runId, input.stepKey, input.decision === "approve" ? "approved" : "rejected") if (input.decision === "reject") { await store.requestCancel(input.runId, new Date().toISOString()) } await deps.data.unitOfWork.run(scope, async (tx) => { await tx.publish(runResumeRequested, { orgId: scope.orgId, workspaceId: scope.workspaceId, projectId: scope.projectId, runId: input.runId, }) }) return { runId: input.runId, status: (await store.get(input.runId))?.status ?? record.status } } export { AGENT_ENTITIES, RUN_JOB }