/** * The turns a person still owes an answer — findable and resumable from ANY * process, not just the one that parked them. * * `TurnResult.resume()` (turn.ts) is the same move for a caller still holding * the result. That closure dies with the process, and the ask does not: a * server restarts, the turn is on a queue worker and the yes arrives at a web * route, or the person answers tomorrow. So this face addresses a turn by ID. * * NOTHING NEW IS PERSISTED FOR IT. The guard's own approval row already carries * the exact `ToolCall` it parked and the ctx it was parked in * (`#parkApproval`, packages/vendo/src/guard/guard.ts) — including, now, the turn * that asked. That row IS the durable interrupted turn: `pending()` is the * list, and re-dispatching the stored call through the same guard-bound * registry is the resume. The guard's one-shot receipt on an approved call * (`#approvedReplay`) is what makes it exactly once, however many times a * flaky client asks. * * FAIL-CLOSED, in both directions. A turn nobody can see reads as absent, a * turn whose asks were already answered refuses rather than re-running them, * and an ask nobody answered in a week can no longer be answered at all. */ import { type Decisions, type Interruption, type ResumeOptions, type ThreadId, type TurnId } from "../core/index.js"; import { type AgentDeps, type TurnResult } from "./turn.js"; /** * How long a parked turn waits for its person: seven days. * * The guard's own default is an hour, sized for a BYO agent loop with no * conversation to come back to. A turn's ask is answered by a HUMAN — who is * asleep, or off for the weekend — and an hour turns "approve this refund" * into work that silently has to be redone. Seven days is long enough for a * person and short enough that a week-old write is never approved into a world * that has moved on. A host's own `guard: { approvals: { parkedCallTtlMs } }` * still wins, and `createVendo` keeps the hour. */ export declare const PARKED_TURN_TTL_MS: number; /** One turn waiting on a person, as another process finds it. The same * `turnId` the interrupted turn returned, the thread it is on, and the asks * themselves — everything {@link Turns.resume} needs, and nothing a caller * would have to join two reads to get. */ export interface InterruptedTurn { turnId: TurnId; threadId: ThreadId; interruptions: Interruption[]; } /** The durable half of the turn contract. */ export interface Turns { /** Every turn of this user's that is waiting on them. `status` is named * rather than assumed: a listing that quietly meant one thing is the kind * that grows a second meaning later. */ list(options: { status: "interrupted"; }): Promise; /** * Answer a parked turn's interruptions and carry it on. The same * `TurnResult` any turn answers with — including `interrupted` again, if the * turn went on to ask for something else. * * The RESULT rather than a live `Turn`, because this call has to read the * store first (the thread and the parked calls are facts only the store has * once the process that asked is gone) and a `Turn` cannot survive that: a * `Turn` is itself a thenable, so `await turns.resume(…)` would unwrap the * handle to its result whatever this promise carried. The turn is watched * live where it is STARTED (`chat`/`run`); it is answered here. * * The answer is prose. An output schema belongs to the code that called * `run({ output })` and was never persisted, so a turn resumed from another * process reports in words — resume through the result you are holding * (`TurnResult.resume`) to keep the shape. */ resume(turnId: string, decisions: Decisions, options?: ResumeOptions): Promise; } /** What one user can do with their own interrupted turns. Bound to a subject * because every read under it is: the guard scopes a pending feed to its * owner, so a turn another user parked is not visible here at all. */ export declare function createTurns(deps: AgentDeps, subject: string): Turns;