import type { AiMessage, ContinuationValidator, ValidateContinuationOptions } from './types.js'; export type { ContinuationValidator, ValidateContinuationOptions } from './types.js'; /** * Continuation validation — defends the auto-persist / continuation path * against a client that resubmits a forged conversation. * * `runWithPersistence` (and the explicit `forUser`/`continue` form) trusts * the caller's incoming history. A continuation request after a client-tool * or approval round-trip carries the prior messages back from the browser, * which means a malicious caller can: * * 1. **Rewrite history / continue someone else's thread (IDOR)** — send * messages that don't match what the server persisted for this thread. * 2. **Forge a tool result** — append a `tool` message answering a tool * call that the server never issued (smuggling attacker-chosen data in * as if a tool produced it). * 3. **Forge an approval** — claim `approvedToolCallIds` for a tool call * that isn't actually pending approval. * * {@link validateContinuation} runs all three checks against the trusted * `persisted` history and returns a verdict. {@link assertValidContinuation} * throws {@link ContinuationValidationError} on failure, and * {@link defaultContinuationValidator} adapts it to the * {@link ContinuationValidator} hook shape consumed by `AgentPromptOptions`. */ export type ContinuationRejectionCode = 'not-a-prefix' | 'forged-tool-result' | 'forged-approval'; export interface ContinuationValidationResult { /** `true` when the incoming continuation is a legitimate extension of `persisted`. */ ok: boolean; /** Machine-readable reason, present only when `ok` is `false`. */ code?: ContinuationRejectionCode; /** Human-readable explanation, present only when `ok` is `false`. */ reason?: string; /** * Index of the offending message — into `incoming` for `not-a-prefix` and * `forged-tool-result`. Absent for `forged-approval` (the offending id is * named in `reason` instead). */ index?: number; } /** * Validate that an `incoming` continuation is a legitimate extension of the * server-persisted `persisted` history. Pure and synchronous — safe to call * from any runtime. Returns a verdict; never throws. * * Checks, in order: * * - **Prefix equality.** Every message the two share by position must match * (role, content, `toolCallId`, and any assistant `toolCalls`). Comparison * is order-insensitive for nested objects, so a tool-call `arguments` map * reordered across a serialization boundary still matches. A genuine * mismatch means the caller rewrote history or is replaying a different * thread (IDOR) → `not-a-prefix`. * - **Tool-result forgery.** Every `tool` message in `incoming` must answer * a tool call actually requested by some assistant message (in either * `persisted` or `incoming`). A `tool` message with no matching request is * smuggled data → `forged-tool-result`. * - **Approval forgery.** Every id in `opts.approvedToolCallIds` / * `opts.rejectedToolCallIds` must reference a real requested tool call → * `forged-approval`. */ export declare function validateContinuation(persisted: readonly AiMessage[], incoming: readonly AiMessage[], opts?: ValidateContinuationOptions): ContinuationValidationResult; /** Thrown by {@link assertValidContinuation} when validation fails. */ export declare class ContinuationValidationError extends Error { readonly code: ContinuationRejectionCode; readonly index: number | undefined; constructor(result: ContinuationValidationResult); } /** * {@link validateContinuation} that throws {@link ContinuationValidationError} * instead of returning a verdict. Use directly, or via * {@link defaultContinuationValidator} as a `validate` hook. */ export declare function assertValidContinuation(persisted: readonly AiMessage[], incoming: readonly AiMessage[], opts?: ValidateContinuationOptions): void; /** * A ready-made {@link ContinuationValidator} backed by * {@link assertValidContinuation}. Drop into `AgentPromptOptions.validate` * for the default prefix + tool-result-forgery + approval-forgery gate: * * ```ts * agent.continue(id).prompt(input, { validate: defaultContinuationValidator() }) * ``` */ export declare function defaultContinuationValidator(): ContinuationValidator; //# sourceMappingURL=continuation-validation.d.ts.map