/** * One user's conversation — the THREAD's lifecycle, and the posture its turns * run at. * * The turn body itself is `spine.ts`, shared with `turn.ts`. This file owns the * two things that are a session's alone: the thread is opened ONCE and many * turns stream over it, and every one of them is `interactive: true` — a person * is on the other end, so a turn that asks for permission waits for the tap. * Approval checkpointing, byte-for-byte re-dispatch and state persistence are * the runtime's and the guard's — INHERITED, not rebuilt. */ import { type ApprovalRequest, type FilesAdapter, type Harness, type Json, type Skill, type Principal, type SeatModels, type ThreadId, type ToolRegistry } from "../core/index.js"; import type { VendoGuard } from "../guard/index.js"; import { type HarnessRuntimeDeps } from "../harnesses/index.js"; import { type VendoStore } from "../store/index.js"; import type { LanguageModel, UIMessage } from "ai"; import type { MemoryAdapter } from "./memory.js"; import type { SystemPromptHook } from "./prompt.js"; export interface SessionOptions { /** Server-trust identity facts, model-visible (`[User]`). */ user?: Record; /** Guard/tools context: functions run at check-time, data survives parking. */ context?: Record; /** Present-user auth forwarding — the request's own headers. */ headers?: Record | Headers; /** Reopen the conversation with this id instead of starting a new one. A * session is a request-lifetime object; the thread is what outlives it, so * this id is what a Node backend hands back to reach the same conversation * on the next request. Not this subject's thread (or not a thread at all) is * `not-found` — never a silent new conversation. */ threadId?: string; } /** `respond()`'s options: a session's, plus the per-turn cancellation a * one-shot call has nowhere else to put. */ export interface RespondOptions extends SessionOptions { signal?: AbortSignal; } /** * Mint a new conversation, or reopen the one this subject already owns. * * `get` is scoped to the principal's own subject, so it IS the ownership check: * a foreign thread reads back as absent and gets the same answer as one that * never existed. There is deliberately NO `put` on the reopen leg — put * replaces the transcript with the `messages` it is handed, so an empty array * would delete every turn the caller came back to read. */ export declare function openThread(store: VendoStore, principal: Principal, threadId: ThreadId, reopen: boolean): Promise; export interface ApprovalEvent { request: ApprovalRequest; approve(): Promise; deny(): Promise; } /** @deprecated The object a session hands back is request-lifetime, and the * THREAD is what outlives it — so hold the durable noun instead: * `agent.forUser(subject)` for the turns, `user.threads` for the * conversations. Reached only through `agent.session()`, which still works; * `respond()` is unchanged and is not deprecated. */ export interface AgentSession { /** The conversation this session is on. Hand it back as * `session(subject, { threadId })` to reopen the same conversation later. */ readonly threadId: string; /** One turn; an AI-SDK UI-message stream `Response` (approval parts included). */ stream(message: string | UIMessage, options?: { context?: Record; signal?: AbortSignal; }): Promise; on(event: "approval", handler: (req: ApprovalEvent) => void): () => void; } export interface SessionDeps { name: string; harness: Harness; store: VendoStore; files: FilesAdapter; guard: VendoGuard; /** Guard-bound already — the one choke point. */ tools: ToolRegistry; skills: readonly Skill[]; instructions?: string; /** The seats a harness that does NOT bring its own brain reads (`vendo()`). */ models?: SeatModels; /** The host's last word on the turn's prompt — see `AgentConfig.system`. */ system?: SystemPromptHook; /** Per-user memory; its `recall` is what fills `[Memory]` each turn. */ memory?: MemoryAdapter; /** Publish the turn in flight to the agent's own MCP door (`door.ts`). A * harness that thinks outside this process mints a credential pointing at * "the turn now live on thread T"; without this the pointer resolves to * nothing and every tool call it makes is a 401. */ liveTurn?: HarnessRuntimeDeps["liveTurn"]; /** A loopback door still binding its port. Awaited here, once, so no turn * can ever read the door's URL mid-bind. */ doorReady?: Promise; } export declare const toHeaderRecord: (headers: Record | Headers | undefined) => Record | undefined; export declare const asUserMessage: (message: string | UIMessage) => UIMessage; export declare function createSession(deps: SessionDeps, subject: string, options?: SessionOptions): Promise;