This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
// The procedure contracts of the agent module: schemas and names, no handler and no port.
|
||||
//
|
||||
// A run is a bounded, person-requested plan of tool calls that the worker executes durably: the
|
||||
// progress a page renders is the run row and its step log, and the only writes a tool's result can
|
||||
// cause are proposals a person decides. The approval queue renders the EXACT stored proposed
|
||||
// arguments — never a model's narration of them.
|
||||
|
||||
import { procedure } from "@conusai-kit/core"
|
||||
import { z } from "zod"
|
||||
import { APPROVAL_REVIEWED, RUN_CANCELLED_ACTION } from "./constants.js"
|
||||
|
||||
const inProject = { org: z.string(), workspace: z.string(), project: z.string() }
|
||||
|
||||
/** One step of the plan a person admits: one explicitly published tool, exact arguments. */
|
||||
export const planStepSchema = z
|
||||
.object({
|
||||
key: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(128)
|
||||
.regex(/^[a-z][a-z0-9-]*$/, "lower-case letters, digits and dashes"),
|
||||
label: z.string().min(1).max(200),
|
||||
tool: z.string().min(1).max(200),
|
||||
args: z.record(z.string(), z.unknown()),
|
||||
})
|
||||
.strict()
|
||||
|
||||
/** What a run may spend; the engine clamps it by the hard safety ceilings. */
|
||||
export const runBudgetSchema = z
|
||||
.object({
|
||||
maxCostMicrocents: z.number().int().positive().optional(),
|
||||
maxSteps: z.number().int().positive().optional(),
|
||||
maxDurationSeconds: z.number().int().positive().optional(),
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const runViewSchema = z.object({
|
||||
runId: z.string(),
|
||||
goal: z.string(),
|
||||
status: z.enum(["queued", "running", "awaiting_approval", "completed", "failed", "cancelled", "uncertain"]),
|
||||
failureCode: z
|
||||
.enum(["tool_error", "budget_exhausted", "plan_refused", "approval_unavailable", "internal"])
|
||||
.nullable(),
|
||||
failureDetail: z.string().nullable(),
|
||||
/** The plan as admitted: what the run will do, visible before it does it. */
|
||||
plan: z.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
label: z.string(),
|
||||
tool: z.string(),
|
||||
args: z.record(z.string(), z.unknown()),
|
||||
/** The step's state from the step log; steps not begun have no row yet. */
|
||||
status: z.enum(["pending", "committed", "failed", "cancelled", "uncertain"]),
|
||||
attempts: z.number().int(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
),
|
||||
usageCostMicrocents: z.number().int(),
|
||||
usageSteps: z.number().int(),
|
||||
/** Committed steps over the plan: the progress bar. */
|
||||
percent: z.number(),
|
||||
cancelRequested: z.boolean(),
|
||||
actorId: z.string(),
|
||||
createdAt: z.string(),
|
||||
startedAt: z.string().nullable(),
|
||||
finishedAt: z.string().nullable(),
|
||||
})
|
||||
export type RunView = z.output<typeof runViewSchema>
|
||||
|
||||
/** One parked approval: the exact stored proposed arguments, copied before they were proposed. */
|
||||
export const approvalViewSchema = z.object({
|
||||
runId: z.string(),
|
||||
stepKey: z.string(),
|
||||
goal: z.string(),
|
||||
tool: z.string(),
|
||||
/** The exact arguments the tool was called with; a reviewer reads THESE, never a narration. */
|
||||
arguments: z.record(z.string(), z.unknown()),
|
||||
/** Where the persisted proposal is reviewed in the console, when the platform names one. */
|
||||
reviewUrl: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
export type ApprovalView = z.output<typeof approvalViewSchema>
|
||||
|
||||
export const runStart = procedure("agent.run.start")
|
||||
.kind("write")
|
||||
.scope("project")
|
||||
.permission("agent.run.start")
|
||||
.input(
|
||||
z.object({
|
||||
...inProject,
|
||||
goal: z.string().min(1).max(2_000),
|
||||
plan: z.array(planStepSchema).min(1).max(25),
|
||||
budget: runBudgetSchema.optional(),
|
||||
}),
|
||||
)
|
||||
.output(runViewSchema)
|
||||
.contract()
|
||||
|
||||
export const runStatus = procedure("agent.run.status")
|
||||
.kind("read")
|
||||
.scope("project")
|
||||
.permission("agent.run.read")
|
||||
.tool()
|
||||
.input(z.object({ ...inProject, runId: z.string() }))
|
||||
.output(runViewSchema)
|
||||
.contract()
|
||||
|
||||
export const runCancel = procedure("agent.run.cancel")
|
||||
.kind("write")
|
||||
.scope("project")
|
||||
.permission("agent.run.cancel")
|
||||
.input(z.object({ ...inProject, runId: z.string() }))
|
||||
.output(z.object({ runId: z.string(), cancelled: z.boolean() }))
|
||||
.audit(RUN_CANCELLED_ACTION)
|
||||
.contract()
|
||||
|
||||
export const runList = procedure("agent.run.list")
|
||||
.kind("read")
|
||||
.scope("project")
|
||||
.permission("agent.run.read")
|
||||
.input(z.object({ ...inProject, cursor: z.string().optional(), limit: z.number().int().min(1).max(100).optional() }))
|
||||
.output(z.object({ runs: z.array(runViewSchema), cursor: z.string().optional(), hasMore: z.boolean() }))
|
||||
.contract()
|
||||
|
||||
export const approvalList = procedure("agent.approval.list")
|
||||
.kind("read")
|
||||
.scope("project")
|
||||
.permission("agent.approval.review")
|
||||
.input(z.object({ ...inProject }))
|
||||
.output(z.object({ approvals: z.array(approvalViewSchema) }))
|
||||
.contract()
|
||||
|
||||
export const approvalReview = procedure("agent.approval.review")
|
||||
.kind("write")
|
||||
.scope("project")
|
||||
.permission("agent.approval.review")
|
||||
.input(
|
||||
z.object({
|
||||
...inProject,
|
||||
runId: z.string(),
|
||||
stepKey: z.string(),
|
||||
decision: z.enum(["approve", "reject"]),
|
||||
reason: z.string().max(500).optional(),
|
||||
}),
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
runId: z.string(),
|
||||
status: z.enum(["queued", "running", "awaiting_approval", "completed", "failed", "cancelled", "uncertain"]),
|
||||
}),
|
||||
)
|
||||
.audit(APPROVAL_REVIEWED)
|
||||
.contract()
|
||||
|
||||
export const AGENT_PROCEDURES = [runStart, runStatus, runCancel, runList, approvalList, approvalReview]
|
||||
Reference in New Issue
Block a user