/** * session-runtime.ts, the per-session half of a hosted session: the loop. * * The workspace floor (workspace-floor.ts) supplies everything a turn needs * that is shared, the model stack, the agent graph, hooks, plugins, the file * cache and project index, the permission manager with the product's trust * gate on its ask seam. This file builds what a turn needs that is NOT shared: * * - a `ConversationManager`, this session's history, restorable from disk; * - a `ToolRegistry` populated by the SAME `registerAllTools` a terminal * calls, rooted at THIS session's workspace, with this session's id * resolving for the task tool; * - a `ContextAccountingHolder` bound to this session's orchestrator, so the * context_accounting tool reports this conversation rather than another; * - the `Orchestrator` itself. * * The permission path is deliberately NOT rebuilt here. `floor.services * .permissionManager` is the manager the product composed, with the ask seam * the product chose (for the daemon: the workspace trust gate in front of the * approval broker, so a hosted run's ask becomes an approval record any * attached surface can answer). Building a second manager here would give * hosted runs a different gate from the one the product's own composition * documents, which is the exact defect the trust-gated seam was added to fix. * * The approval-DERIVED handlers (sandbox escalation, exec terminal prompts, the * localhost-fetch one-tap) are rebuilt from that same `requestApproval` seam, * because the floor exposes the seam rather than the handlers. Same seam, same * gate, so a hosted run asks exactly like a terminal run. * * ── The exec posture, and why it is not the terminal's ────────────────────── * * A hosted turn's exec tool is composed by the same `registerAllTools` a * terminal calls, from the same `sandbox.*` config and the same `exec-sandbox` * gate, so the boundary a hosted command runs inside is the boundary a local * command runs inside, network, PID and filesystem namespacing, the * `sandbox.egressAllowlist` escape hatch, and the self-labelling note on the * result. That much is sameness. * * The difference is what happens when there is NO boundary. A terminal falls * back to the host and says so, because a person asked for the command and is * reading the answer. A hosted CONVERSATIONAL turn has nobody in that chair, so * it refuses instead ({@link HostedSessionExecPosture} `conversational`): a * turn that cannot be contained does not run uncontained. That is the defect * this closes, a hosted conversational turn reached the whole host, forked a * second agent onto a live home, and typed into the owner's terminal, and every * one of those was a command the composition never said it minded. * * `workstream` is the other posture, and it is granted PER SPAWN by the product * composing that spawn, a real work chain the owner authorized, which may * legitimately need the machine itself. It is never reachable from a * conversational turn, from the wire, or from a tool argument, because nothing * on those paths can set it. */ import { ConversationManager } from '../core/conversation.js'; import { Orchestrator } from '../core/orchestrator.js'; import { ToolRegistry } from '../tools/registry.js'; import type { SessionLiveTurnControls } from '../control-plane/routes/session-runtime.js'; import type { ModelDefinition } from '../providers/registry.js'; import type { HostedWorkspaceFloor } from './workspace-floor.js'; import { type HostedSessionExecPosture } from './exec-posture.js'; /** What a hosted session's loop is composed with. */ export interface HostedSessionRuntimeOptions { readonly sessionId: string; readonly workspaceRoot: string; readonly floor: HostedWorkspaceFloor; /** * The base system prompt for this session's turns. The orchestrator appends * the runtime-awareness block itself, so this is the product's own operator * policy and nothing more. */ readonly systemPrompt: string; /** * This session's model, already resolved against the live registry (see * model-route.ts). Omitted ⇒ the session follows the shared registry's * current selection, exactly as a terminal does. */ readonly model?: ModelDefinition | undefined; /** * The surface this session was created FOR, `agent`, `tui`, `webui`. * * A hosted session is composed inside the host, and it used to take the host's * identity with it. That is wrong in a way that is invisible until it costs a * session: an agent conversation hosted by a `tui`-rooted floor resolved every * client-owned setting against the TUI's store, so enabling the wake word * changed the TUI and reported success to a user who was talking to the agent. * * Omitted ⇒ the host's own surface, which is right only when the host created * the session for itself. */ readonly originSurface?: string | undefined; /** * What this run's exec tool may do, when the caller decides per session * rather than per floor. Omitted ⇒ the floor's own decider, and * `conversational` when the floor states nothing either (exec-posture.ts). */ readonly execPosture?: HostedSessionExecPosture | undefined; } /** * A composed hosted session. Everything a caller needs to drive one turn, read * its history, and take it apart again. */ export interface HostedSessionRuntime { readonly sessionId: string; readonly conversation: ConversationManager; readonly toolRegistry: ToolRegistry; readonly orchestrator: Orchestrator; /** The per-call cancel / queued-message surface the session verbs act on. */ readonly liveTurnControls: SessionLiveTurnControls; /** True while a turn is in flight. */ isRunning(): boolean; /** * Submit a user message. Resolves when the turn this call started has ended; * a message submitted while a turn is running is QUEUED by the orchestrator * and this resolves immediately, which is the same contract a terminal has. */ submit(text: string): Promise; /** Interrupt the in-flight turn. Returns whether one was running. */ cancel(): boolean; dispose(): void; } /** Compose one hosted session's loop over an already-acquired workspace floor. */ export declare function createHostedSessionRuntime(options: HostedSessionRuntimeOptions): HostedSessionRuntime; /** A stable id for a new hosted session. */ export declare function newHostedSessionId(): string; //# sourceMappingURL=session-runtime.d.ts.map