Files

127 lines
5.3 KiB
TypeScript

// 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 } 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, 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" },
status: "pending",
attempts: 0,
error: null,
},
{
key: "propose",
label: "Propose lines",
tool: "documents.extract",
args: { fileId: "f-1", schema: "line" },
status: "pending",
attempts: 0,
error: null,
},
])
expect(run.percent).toBe(0)
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("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.beginStep(run.runId, { key: "read", index: 0, label: "Read the document" }, new Date().toISOString())
await store.parkStepApproval(
run.runId,
"read",
{
actionId: "act-7",
tool: "documents.read",
arguments: { fileId: "f-1", dryRun: false },
reviewUrl: "https://console/review?action=act-7",
},
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)
})
})