/** Origin class assigned to every causal callback/queue entry before escape. */ export type DeliveryOrigin = Readonly<{ kind: "turn-continuation"; lineageIdHash: string; attemptEpoch: number; continuationId: string; }> | Readonly<{ kind: "owned-completion"; lineageIdHash: string; attemptEpoch: number; registration: TurnRegistrationKey; }> | Readonly<{ kind: "ordinary"; source: string; }>; /** Exact causal registration key bound before a job/subagent handle escapes. */ export interface TurnRegistrationKey { /** Endpoint (top-level session) identity that owns this job: the registry is * process-global and job ids/generations restart per AsyncJobManager, so * two concurrent sessions both mint bg_1/job:1 — without the endpoint in * the key the second registration would overwrite the first and an owned * abort of the first turn could find an empty causal set (review thread * P1). Absent (legacy/test fixtures) = a default scope. */ endpointId?: string; endpointGeneration: number; lineageIdHash: string; promptAttemptEpoch: number; jobId: string; jobGeneration: string; } /** Per-completion delivery key: registration tuple plus entry identity. */ export type TurnDeliveryKey = TurnRegistrationKey & { entryId: string; progressSeq?: number; }; /** Private origin envelope carried through the plain AgentMessage boundary. */ export interface OwnedCompletionEnvelope { lineageIdHash: string; promptAttemptEpoch: number; /** Exact registered five-tuple so the final gate can validate source authority. */ registration: TurnRegistrationKey; } export type TurnContinuationFenceState = "open" | "closing" | "closed" | "retained" | "released"; export type OwnedCompletionPolicy = "enabled" | "disabled"; /** * Continuation fence lifecycle: `open -> closing -> closed` happens * synchronously before the first await that interrupts the root turn. Closing * records exact continuation tombstones and invalidates ONLY continuation * tokens; it never invalidates an owned-completion token, cancels a manager * job, or creates a turn delivery receipt. `retained` keeps tombstones for * restart/later-owned binding; `released` requires exact tokens gone, teardown * with no live continuation, or bounded durable retention. Host response * success/replay/retry never releases it. */ export interface TurnContinuationFence { state: TurnContinuationFenceState; lineageIdHash: string; abortedAttemptEpoch: number; terminalScopeId: string; blockedContinuationIds: ReadonlySet; predecessorTombstones: ReadonlySet; ownedCompletionPolicy: OwnedCompletionPolicy; } /** * The one gate consulted immediately before turn-origin continuation calls and * owned-completion admission. * * `authorizeContinuation` denies any post-close same-turn continuation and * allows only a call already linearized as a predecessor before close. * `authorizeOwnedCompletion` does NOT consult the closed continuation state as * a suppression flag; it validates exact source metadata and, when allowed, * AgentSession allocates a FRESH attempt/lineage for the new turn. */ export interface TurnContinuationGate { close(reason: "terminal-turn"): void; authorizeContinuation(origin: DeliveryOrigin): "deny" | "allow-predecessor"; authorizeOwnedCompletion(origin: DeliveryOrigin): "allow-new-turn" | "deny"; } export type OwnedDeliverySettlementPath = "enqueue-acknowledged-return" | "acknowledgeDeliveries-queue-purge" | "delivery-loop-acknowledged-skip" | "deliverDelivery-acknowledged-return" | "terminal-wait-acknowledge-suppression-purge" | "filtered-drain-post-selection-suppression"; /** Owned-scope-only settlement observer (never installed for turn scope). */ export type OwnedDeliverySettlementObserver = (event: { key: TurnDeliveryKey; path: OwnedDeliverySettlementPath; action: "owned_settled" | "owned_absent"; }) => void; /** Safe, bounded reasons surfaced on `terminal_uncertain` responses. */ export declare const TERMINAL_UNCERTAIN_REASONS: readonly ["persistence_unavailable", "publication_failed", "delivery_failed", "owned_unsettled", "worker_unsettled", "unknown_origin", "registration_authority_unavailable"]; export type TerminalUncertainReason = (typeof TERMINAL_UNCERTAIN_REASONS)[number]; export interface TerminalScopeDispositions { selection: "turn" | "owned"; turnDisposition: "pending" | "stopped" | "uncertain"; ownedWorkDisposition: "not_requested" | "left_running" | "stopped" | "uncertain"; automaticDeliveryDisposition: "enabled" | "none"; resumeOnOwnedCompletion: boolean; } export interface ActiveTerminalScope { scopeId: string; lineageIdHash: string; abortedAttemptEpoch: number; gate: TurnContinuationGate; fence: TurnContinuationFence; } /** Register one active terminal scope (scopeId -> seam). Bounded; evicts oldest. */ export declare function registerTerminalScope(scope: ActiveTerminalScope): boolean; /** Look up the active terminal scope for an aborted attempt (exact lineage+epoch). */ export declare function lookupTerminalScope(lineageIdHash: string, attemptEpoch: number): ActiveTerminalScope | undefined; export declare function unregisterTerminalScope(scopeId: string): void; /** Record an exact owned registration before its handle escapes (bounded). */ export declare function registerOwnedRegistration(key: TurnRegistrationKey, options?: { isJobTerminal?: (candidate: TurnRegistrationKey) => boolean | undefined; }): void; /** Whether an attempt's owned registration set is KNOWN incomplete (an * evicted in-flight lineage binding or a registry-saturation skip) — a * scope:"owned" abort of that exact attempt must fail closed to uncertainty * (review thread P2). */ export declare function isOwnedAttemptRegistrationIncomplete(lineageIdHash: string, attemptEpoch: number): boolean; /** Exact (jobId, jobGeneration) lookup for completion-origin classification. */ export declare function lookupOwnedRegistration(jobId: string, jobGeneration: string, endpointId?: string): TurnRegistrationKey | undefined; export declare function unregisterOwnedRegistration(key: TurnRegistrationKey): void; /** * Enumerate every exact owned registration belonging to one aborted turn * (matching lineage + attempt epoch). Used by `scope:"owned"` cleanup to * capture the exact causal job set; foreign/unclassified work is never * returned and is never swept. */ /** Retire EVERY owned registration owned by a disposing endpoint (live, * retained-evidence, and backlogged tuples): after the endpoint's manager is * unregistered the tuples can no longer reach a delivery settlement boundary, * and future managers deliberately cannot classify foreign-endpoint tuples as * terminal for eviction — repeatedly disposing distinct sessions with pending * jobs would accumulate tuples until the 8192-entry registry saturates and * all later owned aborts fail closed (review thread P2). */ export declare function retireOwnedRegistrationsForEndpoint(endpointId: string): void; export declare function findOwnedRegistrationsForTurn(lineageIdHash: string, attemptEpoch: number): TurnRegistrationKey[]; export interface OwnedCompletionClassification { lineageIdHash: string; promptAttemptEpoch: number; registration: TurnRegistrationKey; terminalScopeId: string; } /** * Classify a manager completion/progress delivery against the terminal-abort * registries. Returns an exact owned-completion classification ONLY when the * job carries an exact registered five-tuple AND a terminal scope exists for * that turn. Missing or mismatched metadata fails closed (undefined) and the * delivery is then ordinary. Classification is source/lineage-based, never * timing-based; a closed terminal record does NOT suppress an exact * left-running owned completion (corrected turn semantics). */ export declare function classifyOwnedCompletion(jobId: string, jobGeneration: string | undefined): OwnedCompletionClassification | undefined; /** * Whether an owned-completion envelope is authorized by its owning terminal * scope as a fresh-turn resume. Used at batch build (sdk/session.ts) and the * final injection boundary (agent-session.ts): a denied envelope — owned scope * (policy disabled), forged/unregistered tuple, or vanished scope — must be * dropped/partitioned out so stopped work can never call followUp/prompt. */ /** * Classify an owned-completion envelope into three states. A registration * without a terminal scope (no abort yet) is ORDINARY — normal delivery; * only a scope with the owned policy disabled DROPS, and a turn-scope * enabled policy is FRESH (new-turn resume). This lets the batch keep * ownership on the entry and reclassify at flush/abort time (review * thread P1: a completion finished before the abort must not become an * unpurgeable ordinary entry that can still resume the agent). */ /** * Structural validation for envelopes carried through the public AgentMessage * `details` boundary: `ExtensionAPI.sendMessage` allows arbitrary details, so * a malformed `ownedCompletions` entry (object, null item, missing tuple * fields) must never crash the delivery path — invalid entries are skipped, * never classified (review thread P2). */ export declare function isOwnedCompletionEnvelope(value: unknown): value is OwnedCompletionEnvelope; export declare function classifyOwnedEnvelope(envelope: OwnedCompletionEnvelope): "ordinary" | "fresh" | "drop"; /** Whether an envelope must be kept in the batch (not an owned-scope drop). */ export declare function isOwnedCompletionEnvelopeAllowed(envelope: OwnedCompletionEnvelope): boolean; /** Structural subset of AsyncJobManager used by owned-stop settlement (avoids an import cycle). */ export interface OwnedStopManager { cancel(jobId: string): boolean; getJob(jobId: string): { generation?: string; status?: string; } | undefined; getJobPromise?(jobId: string, generation: string): Promise | undefined; acknowledgeDeliveries(jobIds: string[]): number; } /** * Settle exact owned work for `scope:"owned"`: generation-verified cancel, a * fixed grace, a second quiescence proof, then a delivery purge. Returns * "stopped" only when every captured job is terminal after the grace; a reused * job id with a new generation, a missing/evicted record, or still-running/ * paused work fails closed to "unsettled" (AC 16/36 — foreign work is never * swept and unprovable quiescence never claims stopped). */ export declare function settleOwnedWork(manager: OwnedStopManager, exactJobs: TurnRegistrationKey[], graceMs: number): Promise<"stopped" | "unsettled">; export interface LineageBinding { lineageIdHash: string; promptAttemptEpoch: number; endpointGeneration: number; /** Endpoint (top-level session) identity that minted this binding. */ endpointId?: string; } /** * Bind immutable lineage/attempt metadata to an attempt-scoped tool call * identity (toolCallId). The binding is set once at prompt admission and must * never be mutated from a session-current fallback; missing/mismatched * context fails closed (resolve returns undefined). */ export declare function bindToolLineage(toolCallId: string, binding: LineageBinding): void; export declare function resolveToolLineage(toolCallId: string | undefined, endpointId?: string): LineageBinding | undefined; /** Close an evicted tool's registration window after its execution settles. */ export declare function settleToolLineageRegistrationWindow(toolCallId: string, endpointId?: string): void; export declare function unbindToolLineage(toolCallId: string, endpointId?: string): void; /** * Mint an unforgeable opaque lineage id for one prompt turn. The hash binds * session id, attempt epoch, and a per-session secret; it never contains * prompt body and cannot be re-derived from public session data. It is * created before model/tool execution and must never be mutated from a * session-current fallback. */ export declare function mintTurnLineageIdHash(sessionId: string, promptAttemptEpoch: number, sessionSecret: string): string; /** * Register an exact owned registration when the tool call carries immutable * lineage metadata. The generation is read synchronously from the manager's * job record; a missing generation fails closed (no ownership claim). A * registry failure never breaks ordinary registration. */ export declare function registerOwnedIfLineaged(manager: { getJob?(id: string): { generation?: string; status?: string; } | undefined; }, toolCallId: string | undefined, jobId: string, endpointId?: string): void; /** Retire the exact (endpoint, jobId, jobGeneration) owned registration when * its completion delivery is dead-lettered by the manager (delivery-queue * overflow or retry exhaustion): no message is injected and no later * consumption boundary will settle it, so the terminal tuple would otherwise * occupy the global registries until saturation (review thread P2). The * delivery is enqueued only when the job is terminal, so no live job's * authority is dropped. */ export declare function retireOwnedRegistrationForDeadLetter(endpointId: string | undefined, jobId: string, jobGeneration: string): void; /** Monotonic fresh-attempt epoch for `resumeFromOwnedCompletion` allocation. */ export declare function nextPromptAttemptEpoch(): number; /** Mint a fresh terminal scope id (opaque, never persisted raw). */ export declare function newTerminalScopeId(): string; export interface TurnContinuationSeam { fence: TurnContinuationFence; gate: TurnContinuationGate; } /** * Create a continuation fence + gate for one terminal scope. The fence starts * `open` and is closed synchronously via `gate.close()` before the root turn is * interrupted. Continuation authorization is source-based (lineageIdHash + * attemptEpoch + continuationId); timing alone never authorizes. */ export declare function createTurnContinuationSeam(options: { lineageIdHash: string; abortedAttemptEpoch: number; terminalScopeId: string; ownedCompletionPolicy?: OwnedCompletionPolicy; blockedContinuationIds?: readonly string[]; }): TurnContinuationSeam; export interface RegisteredTerminalScope { scopeId: string; lineageIdHash: string; promptAttemptEpoch: number; seam: TurnContinuationSeam; } /** * Create, register, and synchronously close a terminal scope for one aborted * turn. The fence closes before the first await that interrupts the root turn; * owned-completion policy is enabled for `scope:"turn"` (left-running owned * delivery intentionally resumes the agent as a fresh turn) and disabled for * `scope:"owned"`. Registered scopes are process-local and bounded; the exact * (lineageIdHash, attemptEpoch) key makes later owned-completion classification * source-exact and fail-closed. */ export declare function registerTerminalTurnScope(options: { lineageIdHash: string; promptAttemptEpoch: number; terminalScopeId?: string; ownedCompletionPolicy?: OwnedCompletionPolicy; blockedContinuationIds?: readonly string[]; }): RegisteredTerminalScope | undefined; /** * TEST-ONLY: clear the module-global terminal-abort registries so tests get * isolated lineage/binding/scope state. Never call from production code — * the registries are intentionally process-lifetime in the runtime. */ export declare function resetTerminalAbortRegistriesForTests(): void; /** * Structural subset of a durable terminal-scope row needed for bounding * retention (review thread P2). Only rows with a COMPLETED disposition are * evictable; pending markers are never touched. */ export interface DurableScopeRetentionRow { idempotencyKeyHash?: string; idempotencyInputHash?: string; turnDisposition: "pending" | "no_effect" | (string & {}); acceptedAt?: number; ownedWorkDisposition?: "not_requested" | "left_running" | "stopped" | "uncertain"; responseState?: "pending" | "sent" | "delivered" | "failed"; responsePayloadHash?: string; replayPayloadHash?: string; terminalPublished?: boolean; } /** * Evict the OLDEST COMPLETED terminal-scope rows beyond `cap`, mirroring the * bounded in-memory idempotency cache so a long-lived session cannot grow * the durable reconciliation document indefinitely. Completed dispositions * (stopped/uncertain/no_effect) are evicted oldest-first; pending markers and * TRANSITIONAL no_effect_reserved reservations (an in-flight abort that may * still transition to active) are never evicted — evicting a reserved row * would leave a tombstone that replays uncertainty over an unfinalized * reservation (review thread P2). Returns a new array. */ export declare function boundCompletedTerminalScopeRows(rows: T[], cap: number): T[]; /** * Compact key tombstones for completed rows evicted by the retention cap: the * key+input hashes are retained durably so a same-key retry after dispatch * cache expiry/restart still replays instead of aborting an unrelated later * prompt (review thread P2). */ /** Compact key tombstone with enough disposition metadata to reconstruct the * original replay result (the retention cap can evict stopped/uncertain rows, * not only no-effect reservations — review thread P2). */ export interface EvictedTerminalKey { keyHash: string; inputHash: string; turnDisposition: "stopped" | "uncertain" | "no_effect" | "no_effect_marker_failure"; ownedWorkDisposition: "not_requested" | "left_running" | "stopped" | "uncertain"; responseState?: "pending" | "sent" | "delivered" | "failed"; responsePayloadHash?: string; replayPayloadHash?: string; terminalPublished?: boolean; } /** Bound the retained evicted-key tombstone collection FIFO: when a * long-lived session's unique terminal-abort keys evict completed rows until * the tombstone cap is reached, the OLDEST tombstones expire instead of the * next finalization throwing after the destructive stop already happened — * the client would otherwise receive an error while its durable row stays * pending, and subsequent aborts repeat the failure and accumulate * non-evictable pending rows (review thread P2). A dropped tombstone only * loses replay authority for keys older than the bound; the idempotency * guarantee degrades to the in-memory cache horizon instead of disabling * future aborts. */ export declare function boundEvictedTerminalKeys(keys: T[], cap: number): T[]; export declare function collectEvictedTerminalKeys(before: T[], after: T[]): EvictedTerminalKey[]; /** Durable terminal-scope reservation cap. Idle/already-terminal aborts write * durable no-effect reservations, so a client sending idle aborts with unique * keys must not grow the reconciliation document indefinitely: only the OLDEST * COMPLETED rows beyond this cap are evicted (review thread P2). */ export declare const MAX_DURABLE_TERMINAL_RESERVATIONS = 256; /** Retained evicted-key tombstone cap; see {@link boundEvictedTerminalKeys}. */ export declare const MAX_RETAINED_TERMINAL_KEY_TOMBSTONES = 4096; /** * Apply the durable terminal-scope retention bound to a pending * `transactTerminalState` mutation: evict the oldest COMPLETED scope rows past * {@link MAX_DURABLE_TERMINAL_RESERVATIONS}, retain a compact key tombstone for * every evicted row ATOMICALLY with the scope write, and FIFO-expire tombstones * past {@link MAX_RETAINED_TERMINAL_KEY_TOMBSTONES} instead of throwing after a * destructive stop already happened (review thread P2). * * Every durable terminal-state write — admission markers, no-effect * reservations, reservation finalization, and pending-marker transitions — must * go through this single bound in BOTH session runtimes (the notifications-hosted * bus runtime and the SDK-only host runtime); a hand-rolled copy is how the two * paths drift apart. */ export declare function boundTerminalRetentionState(priorKeys: readonly Key[], nextScopes: Row[], maxScopes?: number): { scopes: Row[]; keys: Array; };