/** * routes/session-runtime.ts * * Per-session permission mode (get/set) and context-usage exposure on the * operator wire, the session-scoped RPCs that were missing, so a remote * surface (webui) can read/write a session's permission mode and read its * context-window pressure instead of only touching the daemon-wide * `permissions.mode` config the way the in-process TUI reads per-session * state. * * SCOPE OF TRUTH: the daemon can only get/set the permission mode of, and read * the context usage of, the LIVE LOCAL runtime it actually hosts. A request * for any other session id is an honest 404 (SESSION_NOT_LOCAL) rather than a * fabricated answer, mirroring the honest-refusal pattern the fleet archive * verbs use (routes/fleet.ts). * * MODE-CHANGE EVENT: `sessions.permissionMode.set` mutates `permissions.mode` * through the ordinary config surface, which the already-wired * `bindPermissionModeChangeEvent` binding (permissions/mode-change-emitter.ts, * attached in runtime/services.ts) turns into a runtime.permissions * PERMISSION_MODE_CHANGED event, so surfaces stay in sync without this verb * emitting its own event. * * HONESTY (context usage): the token figure is the estimator's * (estimatedContextTokens), NOT a measured provider prompt-token count; the * field name and the `estimated: true` flag keep that explicit. The percentage * and remaining tokens derive from that estimate via the one shared * runtime/context-usage.ts helper the in-process read model also uses. */ import type { GatewayMethodCatalog } from '../method-catalog.js'; import type { GatewayMethodHandler } from '../method-catalog-shared.js'; import type { PermissionMode } from '../../config/schema-types.js'; /** * The operator-facing permission-mode vocabulary. `custom` is read-only * (surfaced by get when a session is in a custom rule set) and is never a * settable value. */ export type OperatorPermissionMode = 'plan' | 'normal' | 'accept-edits' | 'auto' | 'custom'; export type SettableOperatorPermissionMode = Exclude; /** Map the internal config permission mode onto the operator vocabulary. */ export declare function toOperatorPermissionMode(mode: PermissionMode): OperatorPermissionMode; /** Map a settable operator mode back onto the internal config permission mode. */ export declare function toConfigPermissionMode(mode: string): PermissionMode; /** The measured/estimated context usage of a single session's live runtime. */ export interface SessionContextUsage { readonly estimatedContextTokens: number; readonly contextWindow: number; readonly contextUsagePct: number; readonly contextRemainingTokens: number; } /** * The live-turn control surface an interactive runtime host (an Orchestrator) * binds so remote surfaces can cancel one in-flight tool call and manage the * pending mid-turn message queue. Matches the Orchestrator's own public * methods structurally, so binding is `holder.bind(orchestrator)`. */ export interface SessionLiveTurnControls { cancelToolCall(callId: string): boolean; listQueuedMessages(): ReadonlyArray<{ readonly id: string; readonly queuedAt: number; readonly text: string; }>; editQueuedMessage(id: string, text: string): boolean; deleteQueuedMessage(id: string): boolean; } /** * Settable holder an interactive consumer binds its live Orchestrator-backed * controls into (the contextAccountingHolder pattern): the verbs read whatever * is currently bound; unbinding (or a different instance being bound) is safe. */ export declare class SessionLiveTurnControlsHolder { private controls; /** * Per-session bindings, for a host that runs SEVERAL loops at once. * * The single slot above is the interactive host's: one process, one * conversation, and `sessions.toolCalls.cancel` against "the local runtime" * means that one. A daemon hosting sessions runs many at the same time, and * "the local runtime" is no longer a single thing, so each hosted loop binds * under its own id and the verbs resolve by id first, falling back to the * single slot for the interactive case that has always worked that way. */ private readonly bySession; bind(controls: SessionLiveTurnControls): void; /** Unbind only when the caller is still the bound instance (idempotent). */ unbind(controls: SessionLiveTurnControls): void; get(): SessionLiveTurnControls | null; /** Bind one session's live-turn controls by id. Replaces any previous binding for that id. */ bindSession(sessionId: string, controls: SessionLiveTurnControls): void; /** Unbind only when the caller is still the bound instance for that id (idempotent). */ unbindSession(sessionId: string, controls: SessionLiveTurnControls): void; /** The controls bound for this session id, or null. */ getSession(sessionId: string): SessionLiveTurnControls | null; /** Whether any loop is bound under this id. */ hasSession(sessionId: string): boolean; } /** * The narrow control surface the session-runtime verbs need over the live * local runtime. `isLocalSession` decides whether a requested session id is * the runtime this daemon hosts (the only one whose mode/usage it can answer * for truthfully). */ export interface SessionRuntimeControls { isLocalSession(sessionId: string): boolean; getPermissionMode(): PermissionMode; setPermissionMode(mode: PermissionMode): void; getContextUsage(): SessionContextUsage; /** * The live-turn controls for this session id. * * Takes the id because a daemon hosting sessions runs several loops at once: * a per-session binding answers for a hosted session, and the single * interactive binding answers for the local runtime. Null when neither is * bound, which the verbs report as an honest refusal. */ getLiveTurnControls(sessionId: string): SessionLiveTurnControls | null; } export declare function createSessionPermissionModeGetHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionPermissionModeSetHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionToolCallCancelHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionQueuedMessagesListHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionQueuedMessageEditHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionQueuedMessageDeleteHandler(controls: SessionRuntimeControls): GatewayMethodHandler; export declare function createSessionContextUsageGetHandler(controls: SessionRuntimeControls): GatewayMethodHandler; /** * The `permissions.mode` config surface the session-runtime controls read and * write. The daemon's real ConfigManager (whose generic get/set resolve * `permissions.mode` to {@link PermissionMode}) satisfies this narrower shape. */ export interface PermissionModeConfig { get(key: 'permissions.mode'): PermissionMode; set(key: 'permissions.mode', value: PermissionMode): void; } /** The slice of the runtime state the session-runtime controls read. */ export interface SessionRuntimeStateReader { getState(): { readonly session: { readonly id: string; }; readonly conversation: { readonly estimatedContextTokens: number; }; readonly model: { readonly tokenLimits: { readonly contextWindow: number; }; }; }; } /** * Build the concrete controls over the daemon's config + runtime store. The * local runtime is addressable by its own store session id, or by the stable * `'runtime'` alias the mode-change binding stamps on its wire event * (runtime/services.ts) so a surface can subscribe before it knows the id. */ export declare function createSessionRuntimeControls(deps: { readonly config: PermissionModeConfig; readonly store: SessionRuntimeStateReader; /** Live-turn controls holder an interactive consumer binds; absent = no live-turn verbs. */ readonly liveTurnHolder?: SessionLiveTurnControlsHolder | undefined; }): SessionRuntimeControls; /** * Attach the session-runtime handlers to the descriptors already registered * (without a handler) from ../method-catalog-control-core.ts's static builtin * array. Call once, at RuntimeServices construction time. A missing descriptor * is a silent no-op, the same rationale as routes/fleet.ts. */ export declare function registerSessionRuntimeGatewayMethods(catalog: GatewayMethodCatalog, controls: SessionRuntimeControls): void; //# sourceMappingURL=session-runtime.d.ts.map