// The agent module's use cases on the in-memory adapter: starting lists and statuses, cancel's // resume event, and the approval queue — which must surface the EXACT stored proposed arguments. import { AGENT_ENTITIES, dataAgentPersistence, dataRunStore, threadIdOf } from "@conusai-kit/agent" import type { TenantScope } from "@conusai-kit/core" import { createMemoryDataSource } from "@conusai-kit/data" import { describe, expect, it } from "vitest" import { cancelRun, getManyRuns, getRun, listApprovals, listRuns, reviewApproval, startRun } from "../core/runs.js" const USER = "01900000-0000-7000-0000-000000000009" const scope: TenantScope = { level: "project", actor: { userId: USER }, orgId: "01900000-0000-7000-0000-000000000001", workspaceId: "01900000-0000-7000-0000-000000000002", projectId: "01900000-0000-7000-0000-000000000003", } const makeDeps = () => ({ data: createMemoryDataSource({ entities: [...AGENT_ENTITIES] }) }) const PLAN = [ { key: "read", label: "Read the document", tool: "documents.read", args: { fileId: "f-1" } }, { key: "propose", label: "Propose lines", tool: "documents.extract", args: { fileId: "f-1", schema: "line" } }, ] describe("the agent module's run use cases", () => { const deps = makeDeps() it("starts a run queued, with the plan visible, and lists it", async () => { const run = await startRun(deps, scope, { goal: "Review the contract", plan: PLAN }) expect(run.status).toBe("queued") expect(run.goal).toBe("Review the contract") expect(run.plan).toEqual([ { key: "read", label: "Read the document", tool: "documents.read", args: { fileId: "f-1" }, needsApproval: false, status: "pending", attempts: 0, error: null, }, { key: "propose", label: "Propose lines", tool: "documents.extract", args: { fileId: "f-1", schema: "line" }, needsApproval: false, status: "pending", attempts: 0, error: null, }, ]) expect(run.percent).toBe(0) expect(run.answer).toBeNull() const listed = await listRuns(deps, scope, {}) expect(listed.runs).toHaveLength(1) expect((await getRun(deps, scope, run.runId)).runId).toBe(run.runId) // The outbox event that enqueues the run job in the worker. const pending = await deps.data.outbox.pending(10) expect(pending.some((record) => record.name === "agent.runResumeRequested")).toBe(true) }) it("reads runs back by id for the live list, and answers nothing for another project's or no run", async () => { const own = makeDeps() const run = await startRun(own, scope, { goal: "Read back", plan: PLAN }) const listed = (await listRuns(own, scope, {})).runs expect(await getManyRuns(own, scope, [run.runId, run.runId])).toEqual(listed) const elsewhere: TenantScope = { ...scope, projectId: "01900000-0000-7000-0000-000000000004" } expect(await getManyRuns(own, elsewhere, [run.runId])).toEqual([]) expect(await getManyRuns(own, scope, ["01900000-0000-7000-0000-0000000000ff"])).toEqual([]) }) it("answers a completed run's answer with the provenance it is stored with (AI Act Art. 50(2))", async () => { const fresh = makeDeps() const run = await startRun(fresh, scope, { goal: "Review the contract", plan: PLAN }) expect(run.answerProvenance).toBeNull() const provenance = { ai_generated: true as const, mode: "machine-generated" as const, digital_source_type: "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", generator: "conusai.ai-gateway", provider: "openai", model: "gpt-4o-mini-2024-07-18", generated_at: "2026-11-02T10:30:00Z", } const store = dataRunStore(fresh.data, scope) const record = await store.get(run.runId) await store.transition(run.runId, record?.version ?? 0, { status: "completed", finishedAt: "2026-11-02T10:30:01.000Z", answerProvenance: provenance, }) await dataAgentPersistence(fresh.data, scope).stores.messages.saveThread(threadIdOf(run.runId), [ { role: "user", content: "Review the contract" }, { role: "assistant", content: "The contract was reviewed." }, ]) const completed = await getRun(fresh, scope, run.runId) expect(completed.answer).toBe("The contract was reviewed.") expect(completed.answerProvenance).toEqual(provenance) }) it("refuses an over-long or repeated plan before anything is written", async () => { const fresh = makeDeps() const tooLong = await startRun(fresh, scope, { goal: "x", plan: Array.from({ length: 26 }, (_, at) => ({ key: `s${at}`, label: `Step ${at}`, tool: "t", args: {} })), }).catch((error: unknown) => error) expect((tooLong as { status?: number }).status).toBe(422) const repeated = await startRun(fresh, scope, { goal: "x", plan: [ { key: "same", label: "One", tool: "t", args: {} }, { key: "same", label: "Two", tool: "t", args: {} }, ], }).catch((error: unknown) => error) expect((repeated as { status?: number }).status).toBe(422) expect((await listRuns(fresh, scope, {})).runs).toHaveLength(0) }) it("cancel persists the request and re-enqueues the resume; a parked approval surfaces its exact arguments", async () => { const fresh = makeDeps() const run = await startRun(fresh, scope, { goal: "Approve-aware run", plan: PLAN }) // Simulate the engine parking an approval by writing the step row as the engine would. const { dataRunStore } = await import("@conusai-kit/agent") const store = dataRunStore(fresh.data, scope) await store.parkStepApproval( run.runId, { key: "read", index: 0, label: "Read the document" }, { actionId: "act-7", tool: "documents.read", arguments: { fileId: "f-1", dryRun: false }, reviewUrl: "https://console/review?action=act-7", interrupt: null, }, new Date().toISOString(), ) await fresh.data .forScope(scope) // biome-ignore lint/suspicious/noExplicitAny: test adjusts the run's status as the engine would. .of(AGENT_ENTITIES[0] as any) .update(run.runId, { status: "awaiting_approval" }, 1) const queue = await listApprovals(fresh, scope) expect(queue.approvals).toHaveLength(1) expect(queue.approvals[0]).toMatchObject({ runId: run.runId, stepKey: "read", tool: "documents.read", arguments: { fileId: "f-1", dryRun: false }, reviewUrl: "https://console/review?action=act-7", }) const cancelled = await cancelRun(fresh, scope, run.runId) expect(cancelled.cancelled).toBe(true) expect((await getRun(fresh, scope, run.runId)).cancelRequested).toBe(true) // The cancel re-published the resume event: a parked run notices the request only when resumed. const pending = await fresh.data.outbox.pending(10) expect(pending.filter((record) => record.name === "agent.runResumeRequested")).toHaveLength(2) const review = await reviewApproval(fresh, scope, { runId: run.runId, stepKey: "read", decision: "approve" }) expect(["queued", "running", "awaiting_approval", "cancelled"]).toContain(review.status) const bad = await reviewApproval(fresh, scope, { runId: run.runId, stepKey: "propose", decision: "approve" }).catch( (error: unknown) => error, ) expect((bad as { status?: number }).status).toBe(422) // The decision is recorded on the step: the queue no longer offers it, and it is not decided twice. expect((await listApprovals(fresh, scope)).approvals).toHaveLength(0) const again = await reviewApproval(fresh, scope, { runId: run.runId, stepKey: "read", decision: "reject" }).catch( (error: unknown) => error, ) expect((again as { status?: number }).status).toBe(409) expect((await store.listSteps(run.runId))[0]?.approval?.decision).toBe("approved") }) }) it("does not offer or accept a pending approval after its run failed", async () => { const fresh = makeDeps() const run = await startRun(fresh, scope, { goal: "Synthetic approval", plan: PLAN }) const store = dataRunStore(fresh.data, scope) await store.parkStepApproval( run.runId, { key: "read", index: 0, label: "Read" }, { actionId: null, tool: "records.write", arguments: {}, reviewUrl: null, interrupt: null, }, new Date().toISOString(), ) const record = await store.get(run.runId) if (!record) throw new Error("expected a run") await store.transition(run.runId, record.version, { status: "failed", failureCode: "budget_exhausted" }) expect((await listApprovals(fresh, scope)).approvals).toEqual([]) await expect( reviewApproval(fresh, scope, { runId: run.runId, stepKey: "read", decision: "approve" }), ).rejects.toMatchObject({ status: 409 }) })