/** * Run protocol + atomic run services seam. * * The protocol declares the wire contract for skill runs: the lifecycle states * (admitted / leased / running / terminal), run_id, attempt_id, and lease_generation. * The zod schemas validate both admission and terminal payloads against * contractVersion 1 — the same version the shipped server emits on /api/v1/runs. * * The run services half is the atomic engine: admission, claim/lease, and transition. * The current implementation wraps the store's own atomic transitions (Postgres * `FOR UPDATE SKIP LOCKED`, SQLite `BEGIN IMMEDIATE` plus a conditional claim) — no * business logic is duplicated here. */ import { z } from "zod/v3"; import { REMOTE_SKILL_RUN_CONTRACT_VERSION, normalizeRemoteSkillRunContract, type RemoteSkillRunContract } from "../lib/remote-run-contract.js"; import type { ApiPrincipal, CreateRunInput, ServerRunRecord, ServerRunStatus, SkillsProductStore } from "../server/types.js"; import type { OfflineGate } from "./offline.js"; import type { RunEventEmitter } from "./events.js"; import type { SpendService } from "./spend.js"; import type { RunQuota } from "./governance.js"; /** Wire version shared by every run payload this SDK produces or consumes. */ export declare const RUN_PROTOCOL_VERSION: 1; /** Lifecycle states of the run protocol (admitted → leased → running → terminal). */ export declare const RUN_PROTOCOL_STATES: readonly ["admitted", "leased", "running", "terminal"]; export type RunProtocolState = (typeof RUN_PROTOCOL_STATES)[number]; /** Stable run identifier (`run_id`). */ export type RunId = string; /** Attempt identifier (`attempt_id`). */ export type AttemptId = string; /** Fencing token for a claim (`lease_generation`). */ export type LeaseGeneration = number; /** Admission: a run is accepted into the queue. */ export declare const runAdmissionSchema: z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; skill: z.ZodString; status: z.ZodLiteral<"admitted">; createdAt: z.ZodString; }, "strip", z.ZodTypeAny, { status: "admitted"; createdAt: string; skill: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }, { status: "admitted"; createdAt: string; skill: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }>; export type RunAdmission = z.infer; /** Lease: a worker has claimed the run. */ export declare const runLeaseSchema: z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; workerId: z.ZodString; status: z.ZodLiteral<"leased">; }, "strip", z.ZodTypeAny, { status: "leased"; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; workerId: string; }, { status: "leased"; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; workerId: string; }>; export type RunLease = z.infer; /** Terminal: the run reached a final state. */ export declare const runTerminalSchema: z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; skill: z.ZodString; status: z.ZodEnum<["succeeded", "failed", "cancelled", "expired"]>; completedAt: z.ZodString; }, "strip", z.ZodTypeAny, { status: "cancelled" | "failed" | "expired" | "succeeded"; skill: string; completedAt: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }, { status: "cancelled" | "failed" | "expired" | "succeeded"; skill: string; completedAt: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }>; export type RunTerminal = z.infer; /** Any single protocol message, discriminated by `status`. */ export declare const runProtocolSchema: z.ZodDiscriminatedUnion<"status", [z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; skill: z.ZodString; status: z.ZodLiteral<"admitted">; createdAt: z.ZodString; }, "strip", z.ZodTypeAny, { status: "admitted"; createdAt: string; skill: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }, { status: "admitted"; createdAt: string; skill: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }>, z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; workerId: z.ZodString; status: z.ZodLiteral<"leased">; }, "strip", z.ZodTypeAny, { status: "leased"; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; workerId: string; }, { status: "leased"; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; workerId: string; }>, z.ZodObject<{ contractVersion: z.ZodLiteral<1>; runId: z.ZodString; attemptId: z.ZodString; leaseGeneration: z.ZodNumber; skill: z.ZodString; status: z.ZodEnum<["succeeded", "failed", "cancelled", "expired"]>; completedAt: z.ZodString; }, "strip", z.ZodTypeAny, { status: "cancelled" | "failed" | "expired" | "succeeded"; skill: string; completedAt: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }, { status: "cancelled" | "failed" | "expired" | "succeeded"; skill: string; completedAt: string; contractVersion: 1; runId: string; leaseGeneration: number; attemptId: string; }>]>; export type RunProtocolMessage = z.infer; /** Map the store's status vocabulary onto the protocol lifecycle. */ export declare function protocolStateOf(status: ServerRunStatus): RunProtocolState; /** The current engine is single-attempt: the attempt id is the run id. */ export declare function attemptIdOf(run: Pick): AttemptId; /** The current engine keeps the generation the claim actually stamped. */ export declare function leaseGenerationOf(run: Pick): LeaseGeneration; /** One atomic status transition on a run. */ export type RunTransition = Partial>; /** Atomic run services: admission, lease/claim, transition, and read. */ export interface RunService { admit(input: CreateRunInput): Promise; leaseNext(workerId: string): Promise; transition(runId: string, patch: RunTransition): Promise; get(principal: ApiPrincipal, runId: string): Promise; } export interface RunServiceOptions { store: SkillsProductStore; /** * Optional governance wiring. When present, admit() runs the full admission * chain before a run enters the queue: the offline gate first (fail closed), * then the spend ceilings (RUN_BUDGET_EXHAUSTED on refusal), then the run is * created, reserved against, and announced. Absent, admit() is exactly what * it always was - the embedder opts in to the controls. */ governance?: RunServiceGovernance; } export interface RunServiceGovernance { offline?: OfflineGate; spend?: SpendService; events?: RunEventEmitter; /** Resource envelope this run requests, checked against the org ceilings. */ quota?: RunQuota; /** Estimated integer cents (0..2147483647), reserved before dispatch. */ estimatedCents?: number; } /** Current implementation: the store's own atomic transitions, plus the optional admission chain. */ export declare function createRunService({ store, governance }: RunServiceOptions): RunService; /** * Settle a terminal run: reconcile its credit reservation against the actual * cost and emit the terminal lifecycle event. The reservation is released * (actual 0) or charged (actual > 0); unused reservations never linger. */ export declare function settleRun(store: SkillsProductStore, options: { spend?: SpendService; events?: RunEventEmitter; }, run: ServerRunRecord, actualCents?: number): Promise; export { REMOTE_SKILL_RUN_CONTRACT_VERSION, normalizeRemoteSkillRunContract }; export type { RemoteSkillRunContract };