/** * sessions domain contract. Method signatures are the source of truth: * unary methods take the RpcRequest

narrow form and the impl echoes rpcId; everything * else references RequestPayload<'session.*'> / ResponseValue<'session.*'>. */ import type { ContentBlock } from '@deepseek-ai/dsh-llm/types' import type { InboxItemId } from '@deepseek-ai/dsh-agent/brand' import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session/types' // The pure-type outlet: api/ is browser-importable, and the package root's // cordis Context merge (via dsh-agent) must not enter client aggregates. import type { SessionProjectionMap } from '@deepseek-ai/dsh-session-projection/types' import type { RpcId, RpcRequest, RpcResponse } from './rpc.ts' import type { ToolEventView } from './events.ts' import type { WorkspaceId } from './workspace.ts' declare module '@deepseek-ai/dsh-llm' { interface MessageSourceMap { /** * The prompt's rpcId is passed through MessageSource into the `user/message` event * (the client uses it to reconcile the optimistically * echoed provisional message with the event stream). kind stays `'user'` — the model face * carries no transport vocabulary; rpcId is an extra durable-JSON field passed back to the client with the event. */ 'user-rpc': { kind: 'user'; rpcId: RpcId } } } /** * One history page entry: the raw event plus the optional host-computed render * intent (same semantics as the mux frame's `view` slot — a pagination-time * derivation, never persisted). */ export interface HistoryEntry { event: SessionEvent view?: ToolEventView } /** * The projection baseline riding the history tail page: one synchronous cut * over every registered projection unit, read from the registry's watermark * cache. `asOfSeq` is the seq of the last committed event every value * reflects — the window tail event seq (`-1` for an empty log, mirroring * `session/subscribed.lastSeq`), directly comparable with * `session/projection` frame seqs under the client's higher-seq-wins rule. A * key absent from `values` means the capability is absent (its domain plugin * is unmounted). */ export interface SessionProjectionsBlock { /** Seq of the last event the values reflect; -1 for an empty log. */ asOfSeq: number /** Whole current value per registered projection key. */ values: Partial } /** Complete model target selected for one session. */ export interface ModelTarget { /** Registered provider route. */ provider: string /** Provider-owned model id. */ model: string /** Adapter-owned reasoning effort; absence preserves adapter/provider default behavior. */ reasoningEffort?: string } /** One adapter-owned reasoning effort displayed for an exact model route. */ export interface ModelReasoningEffort { /** Opaque value submitted back to the owning adapter. */ id: string /** Adapter-supplied display name. */ name: string /** Optional adapter-supplied description. */ description?: string } /** Selectable reasoning metadata for one exact model route. */ export interface ModelReasoning { /** Efforts in adapter-preferred display order. */ efforts: ModelReasoningEffort[] /** Adapter-configured default; absence preserves the provider default. */ defaultEffort?: string } /** One model displayed inside its provider group. */ export interface ModelCatalogModel { /** Provider-owned model id. */ id: string /** Provider-supplied display name. */ name: string /** Optional provider-supplied description. */ description?: string /** The current model was inserted because the advisory catalog omitted it. */ unlisted?: true /** Exact-route reasoning metadata when the adapter exposes it. */ reasoning?: ModelReasoning } /** One provider and the models it advertised successfully. */ export interface ModelProviderGroup { /** Provider route id used for requests. */ id: string /** Provider display name. */ name: string /** Models in provider-preferred order. */ models: ModelCatalogModel[] } /** A provider whose asynchronous catalog lookup failed. */ export interface ModelCatalogFailure { /** Provider route id. */ id: string /** Provider display name. */ name: string /** Lookup failure diagnostic. */ message: string } /** Detached model-directory snapshot for one session. */ export interface SessionModels { /** Target selected for the session's next assembled step. */ current: ModelTarget /** Successfully loaded provider groups. */ groups: ModelProviderGroup[] /** Provider-local failures; successful groups remain usable. */ failures: ModelCatalogFailure[] } /** A client-requested mutation of one still-pending queue item. */ export type QueueAction = | { kind: 'edit'; content: ContentBlock[] } | { kind: 'remove' } /** Session list entry (v1 builds no index: list does readdir+stat). */ export interface SessionSummary { sessionId: SessionId /** * Last activity. Attached: the last non-`session/end-seed` event, since a * pickup is not activity. Cold: the log's mtime, or `createdAt` for a backend * with no per-session file (README Known Limitations covers the skew). */ updatedAt: number /** Status of the attached agent; always false for cold (unattached) sessions. */ running: boolean /** * Derived conversation-not-started bit: true while no turn has run (no * prompt was accepted yet). Standalone plugin events — command lifecycle * records, plan/mode, titles, goals — do not open a turn and therefore do * not clear it. Clients hide blank sessions from lists and reuse them for * New Session on the same workspace. Always false for cold sessions — * lazy persistence keeps a never-appended session out of the store, and a * listed cold session's log holds its turns. */ blank: boolean /** fork/spawn lineage (session.header.parentSession passthrough); absent for root sessions. */ parentSessionId?: SessionId /** Session working directory (header.cwd passthrough); absent when unrecorded. */ cwd?: string /** * Projection baseline for this row, with zero log loads: attached sessions * read the registry's live watermark cut; cold sessions read the persisted * projection cache's stored rows — as stale as that session's last durable * checkpoint (`asOfSeq` says exactly how stale), never wrong, and directly * seedable into the client's per-session value store under its * higher-seq-wins rule (a list baseline can never overwrite a newer push * frame). Absent when no value is available (no registry, no cache row for * a cold session, or a fail-soft cache read miss); a listing client treats * absence as "no title yet", exactly like a blank session. */ projections?: SessionProjectionsBlock } /** Session-domain unary methods (the map keys session.* of RpcMethodMap). */ export interface SessionsApi { /** Lists persisted sessions (updatedAt descending). v1 returns everything; cursor is a reserved seat, unimplemented. */ list(request: RpcRequest<{ cursor?: string }>): Promise> /** * Creates a real session and its idle agent. At most one of `workspaceId` / * `cwd` is accepted; an omitted project uses the Host cwd. A caller may * preallocate `sessionId`: retries with the same id and cwd return the same * session, while a different cwd fails with `session-conflict`. Workspace * creation attaches the session after publication; an attach failure * returns `workspace-attach-failed` with the published session id. */ create(request: RpcRequest<{ workspaceId?: WorkspaceId; cwd?: string; sessionId?: SessionId }>): Promise> /** * Reads a window of history events; page boundaries align to append-origin message * boundaries: one page = all raw events owned by a whole number of such messages (including * their chunk / tool events), never cut mid-message. Model-only replacement copies consume no * `maxMessages`, so a compaction's provenance stays on the page of its replacement. The tail * page (beforeSeq absent) additionally carries the in-flight * partial — chunk events already emitted for the last unfinalized message. * Each entry pairs the raw SessionEvent with the host-computed view (tool events whose * presenter produced one, evaluated against the registry at pagination time); the client * rebuilds the surface from the events with the shared fold. * The tail page — and only the tail page — additionally carries `projections` * when the deployment mounts the session-projection registry: every moment * the client needs a fresh baseline already pulls the tail page, and * loadOlder (the only beforeSeq path) is the only path that never needs one. * A deployment without the registry serves histories without the block. */ history(request: RpcRequest<{ sessionId: SessionId; beforeSeq?: number; maxMessages?: number }>): Promise> /** Reads a fresh advisory model directory for this session. Provider lookups run independently. */ models(request: RpcRequest<{ sessionId: SessionId }>): Promise> /** * Selects the complete target for this session. Exact model metadata * validates an optional reasoning effort, while catalog membership remains * advisory. */ selectModel(request: RpcRequest<{ sessionId: SessionId provider: string model: string reasoningEffort?: string }>): Promise> /** * Renames a session: appends a `session/title` event with the `user` * source, which pins the title against automatic regeneration. The * normalized accepted title and the title event's seq return so the caller * can settle its projection cell without waiting for the push frame. A * title that normalizes to empty fails with `title-invalid`. */ rename(request: RpcRequest<{ sessionId: SessionId; title: string }>): Promise> /** * Sends a message. content is core's ContentBlock[] verbatim; mode maps 1:1 — queue→send, steer→steer. * A prompt whose content is exactly one text block starting with '/' is a slash command: the host * executes it through the command registry (mode-agnostic) and it is never sent to the model. A * successful command returns ok with the command slot (its success text, when the command produced * one — carried for future rendering; the state change is the feedback). A usage/state error is an * RPC error with code command-error; an unrecognized name is an RPC error with code unknown-command. */ prompt(request: RpcRequest<{ sessionId: SessionId; mode: 'queue' | 'steer'; content: ContentBlock[] }>): Promise> /** * Edits or removes one pending queued occurrence. */ updateQueue(request: RpcRequest<{ sessionId: SessionId; itemId: InboxItemId; action: QueueAction }>): Promise> /** Stops: clears both FIFOs + aborts the current step (1:1 with agent.cancel). */ cancel(request: RpcRequest<{ sessionId: SessionId }>): Promise> }