Session V4 reserves developer-role messages for incremental model-visible session changes. The role, tool addition/removal blocks, and deferred-loading marker need a durable representation before independent provider and UI integrations can emit or consume them. Known developer events must match an open turn and step; unsupported providers and UI consumers fail explicitly. Request headers already capture full tool definitions. Persisting another ToolSchema in each addition would duplicate Session information and create two definitions that could drift. Additions therefore store toolName, and the containing developer event uses headerSeq to select one earlier known request/header for all its additions. A same-name replacement keeps its historical definition identity through restart, fork and surface changes. Removals require no definition copy, and ordinary developer messages omit headerSeq. Generic sourceEventSeqs remains independent derivation metadata. The rejected inline tool field is optional never with @persistenceReserved. The schema extractor retains that uninhabited slot, so permitting a future value requires a format bump instead of being mistaken for compatible optional metadata. Unrelated optional fields remain additive and readable. Native and Session admission reject missing, forward, non-header, unknown, ambiguous and incomplete referenced definitions, and reject the retired inline tool field. Validate complete schema fields only for referenced definitions, preserving unrelated header behavior and extra JSON metadata. Unknown ignorable records remain opaque. Empty developer nodes retain their surface position without producing a message or shadowing the system head. No automatic emitter, provider activation, UI implementation, or speculative public resolver is added. Keep the core-owned developer source binding beside the introduced role, including the request-only type rejection and attribution round trips. Update consumer guards, SDK/snapshot handling, bilingual owner documentation, and the same unreleased V4 acknowledgement. Keep earlier tool/source migration work and its documentation in the parent. Preserve the existing recorded Cordis inspection generation from the original developer change. Integration keeps the final source layer as the sole parent and preserves master's Messages input filtering, empty-user omission, empty tool results, and system-update placement. Developer/deferred guards still run before conversion. Native V4 admission, header references, reservation declarations, and generic tooling match the validated marker candidate. Parent-owned fixture and generator fixes remain in the parent. Earlier feature validation passed full build, full lint, 41 doc-sync gates, and 16 hygiene gates. The unchanged native implementation retains that evidence. The focused Session/V4/request-input run passes 884 tests across 38 files with 100% statement, branch, function and line coverage for the V3-to-V4 package and Session surface. Consumer tests pass 482 with one existing skip; tool, message and optional-field tests pass 146. Cordis inspection and compaction replays pass in both source and built modes (two cases per mode). Ten invalid referenced-definition/unknown-header controls failed before their scoped guards and pass after them. Reservation validation: the actual generated developer root records tool as optional primitive never. Widening it requires a bump, while traceId remains additive and preserved; all 85 focused reader/classifier tests pass. Final-parent validation: 205 tests across eight provider, type, catalog, Session and native-format files pass. The preserved parent fixture verifies Messages input-history replay; runtime/declaration and parent-owned-file comparisons show no loss during the final rebase. Co-authored-by: Tianyi Cui <[email protected]>
description, kind
| description | kind |
|---|---|
| Host and Client session control: create, resume, prompt, follow history, and project live session state. | package-reference |
Session Controller
English | 中文
Summary
@deepseek-ai/dsh-api-session-controller owns the Host ctx.sessionController service and the generated Client session, skills, and fileReferences Remote namespaces. It serves Session lifecycle and history, the Host-generation model catalog, workspace-path opening, user-invocable skill discovery, and Agent-scoped file references. Use it through API Gateway when a Client needs operations addressed by a Session.
Table of Contents
- Use this package
- Client references
- Session media references
- Configuration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
History pages and follow opening snapshots carry one { type: 'event', event: SessionWireEvent } record per durable Session event. The Client retains each accepted record as one durable SessionEventLikeEntry; Assistant token boundaries remain inside the compact stream on assistant/message or assistant/attempt. Tool arguments, result content, failures, and tool/result.data.meta pass through unchanged; the controller does not resolve a Tool definition, run a presenter, or attach UI data.
The Client journal validates exact V3 event envelopes before publishing follow snapshots, live entries, or history pages. It reuses the browser-safe Session validators for required surface markers, exact replacement endpoints, earlier unique source seqs, embedded Assistant provider metadata, request-header omissions, and tool-error consistency. Invalid records fail without field stripping or normalization; range membership and source existence remain durable-log checks on the Host.
Each endpoint states its activation policy. List reads only stored headers and projection-cache rows: it never calls per-session stat or opens a cold Session body. A current-format cache identity may supply every list hint; a lifecycle-matching predecessor cache may supply only its version-compatible title as a stale display fact, never as an authoritative fold seed. Search, attachment, history pages, log following, skill discovery, and workspace-path opening can inspect persistence without activating an Agent; canOpenWorkspacePath() reports native-opening availability without addressing a Session. Cancellation requires live state; queue mutation, model, rename, prompt, and file-reference operations may resolve or resume an ordinary Session. Prompt rejects content with neither non-whitespace text nor an attachment before resolving the Agent or appending Session events; queue edits accept only non-empty text content. Prompt admission consumes opaque receipts from the injected fileUploads Host service and resolves every same-Agent receipt before sending the complete ordered content list through ctx.attachments. Prompt retries whose requestId is already queued or logged return the original acceptance without inserting another message. Create and fork are the only operations that create a new Agent directly. The service applies one preset-aware resume policy and subagent ownership fence to its own methods and to the Typert Agent and Session lookups used by other Remote namespaces. Queue mutation has one narrow exception: a live child whose current projected identity is continuable and comes from its own non-seed suffix accepts the ordinary Edit, Remove, and QueueDock Steer actions across both inbox destinations. One-shot, missing, unknown, corrupt, seed-only, or cold children remain rejected without resume. The skill catalog uses a live Agent when present or the recorded preset's standing scope when cold, so listing never starts an Agent. The authenticated delivery routes use workspaceDesktop() for the serving Host name and file-manager behavior. openWorkspacePath({ path, action: "reveal" }) delegates file-manager navigation to the native adapter; omitting action opens the file-type association, including HTML and SVG. Both operations require the composed filesystem to map the requested Host path to the same canonical process path; unmapped remote paths are refused before a native command runs. session.projections reads one complete baseline through a live-preferred Session observation without activating an Agent. It returns null for a missing Session and serves any registered projection keys. The Client exposes shared values and explicit-read state through projectionsBySession; domains select their own keys. Session-list summaries carry agentAvailable, updated through existing summary and status events independently of durable projections. Initial reads and live projection frames use the same sequence ordering.
Client list rows and resident Sessions use the current sessionListMetadata projection to reject stale blank-session hints; recency is the later of the summary timestamp and the projected last user prompt. When SessionManager creates an instance before its list row arrives, it reconciles blankness with metadata already retained for that Session. An opened conversation is therefore not reused by New Session even when an older list response still marks it blank.
Explicit-id session.create adopts a live Session or resumes a persisted Session while retaining its writer lock. Writer contention returns session/writer-held; callers may try another blank without suppressing unrelated failures. session.list includes persisted blanks using cached metadata, without opening cold log bodies.
Client list refreshes retain unchanged row objects and reuse the items array when order and values match. Each row's retainedBy contains positive local reference-source counts; Host metadata refreshes cannot overwrite them. Cache membership checks use a per-refresh ID set, so reconciliation grows linearly with the current list and retained cache sizes. Host summary updates replace running and Agent availability; local create/fork responses only fill missing metadata on existing rows. Removed ordinary Sessions retain projection stores only when their catalogs contain children.
Projection reads retain loading and failure state independently of their values. Reconnect cancels reads from the previous connection and reloads previously requested projections; live membership updates arrive through the control stream. Parent availability comes from Host summaries and remains unknown until a summary or a successful list baseline establishes it. Address lookup resolves projected children without selecting them or creating scopes.
The Client adapter exposes SessionEventStream, a Gateway RemoteJournalStream bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous replace, prepend, append, and settle-assistant changes, and repairs reconnect or sequence gaps through a tail page. Backwards paging has two verbs: loadOlder() pulls one 50-message page, and loadThrough(seq) — the turn-jump loader — loops 200-message pages until the window covers the target seq, lowering a shared target on repeated calls, stopping on a page that makes no progress, and reporting busy through the same loadingOlder snapshot bit. The Web adapter explicitly opts into cursorless Assistant frames: each opening carries the active attempt's startedAfterSeq, nextIndex, and compact stream, and every stream member becomes a Client-only assistant/live-chunk entry ordered between durable cursors. The Host captures a follower-local arrival ordinal with that baseline and suppresses buffered frames at or before the cut; a replacement Agent may restart frame revision at one. A durable assistant/message or assistant/attempt arriving after an active opening stays staged only when its seq follows startedAfterSeq and its Turn and Step match; the matching end type, seq, and index publishes one named settlement delta that retires the attempt's transient rows and adds the durable entry while earlier same-step retries remain visible. Revision, dense-index, or settlement gaps for a known attempt reopen follow, while a controller that missed the start ignores unknown-attempt frames and publishes their durable settlement normally. An abandoned end publishes a settlement delta without a durable entry so its transient rows retire immediately. A durable gap-repair page has no Assistant baseline, so its held notification reopens follow once for a paired page and baseline. Every history record covers exactly its event seq. A business, persistence, or unresolved continuity failure terminates the stream, while only physical carrier loss selects automatic resumption. SessionControlStream is a Gateway RemoteSnapshotStream; every generation opens with a complete process-local baseline, so reconnect replaces jobs and projection state instead of treating transient values as durable events. At each ready Host generation, a synchronous Client subscription clears retained projection values and watermarks before refreshing queries and restarting the control stream, including Sessions absent from the control baseline. The first control stream waits for generation readiness, so its opening values cannot precede invalidation. Outstanding list responses from the previous generation cannot republish those values. Within a generation, a delayed control baseline cannot overwrite or clear newer list, history, or live values. The durable inbox projection carries both pending lists through the same cold-read and reconnect path as other projections. Client Agent contexts provide the identity used by the independent fileUpload service; Session objects expose lifecycle, prompt, queue, and history operations rather than file transfer.
The Session object also carries local submission echoes: session.beginSubmission inserts one into SessionSnapshot.pendingSubmissions synchronously, before the caller serializes and prompts, so a conversation UI can show the message on the submit click's own frame. The echo stores ordered image previews and durable file references. Session derives its transcript, queued, or steering placement from the current running state and requested delivery mode, then retains that placement while serialization is in flight. The prompt's requestId is the correlation identity: the Host echoes it as the durable user source's rpcId, including pending messages in the inbox projection. An echo retires one animation frame after its durable event or queue occurrence is observed, immediately when its identified prompt fails or is abandoned, and as failed on disposal. Each retirement fires onRetire exactly once; an observed retirement includes the ordered durable attachment references so the composer can release successful cards while preserving failed drafts. Echoes are Client memory only; reload and reconnect rebuild the conversation from durable events alone.
The user-invocable skills/list metadata includes the winning provider’s optional instruction-file path. The composer can preview that file without loading every skill body or activating a cold Agent.
Fork copies the exact inclusive event prefix selected by atSeq, including a cut inside an open turn. The child records its inherited marker before synthetic fork results and closing events. Omitting atSeq selects the latest completed turn and its standalone tail, stopping before the next turn or queued input; a nonexistent event is rejected. The chat action selects a completed turn.
A resume blocked by an existing write handle returns session/writer-held with the Session id; other resume failures retain gateway/internal.
loadThrough(seq) retains older pages privately until its shared target is covered or loading ends, then publishes the successful pages as one ordered prepend. Live events remain visible while history loads. A later page failure retains the successful prefix; replacing the history window discards buffered pages from the replaced window. Ordinary loadOlder() still publishes its single page directly.
Queue edits replace pending content with non-empty text only.
Client references
sessions.retain(target, { source, signal? }) immediately acquires one exact Client generation and starts its shared initial history opening. The target is a known Session id or a durable direct-parent subagent address; the Host validates an explicit address when history opens. The returned reference supports idempotent release() and Symbol.dispose; its ready Promise follows the shared Session.open() result and resolves to the exact binding when that attempt settles, including when a Remote failure is represented by openState: 'error'. It rejects when Session.open() rejects, its waiter is cancelled, or the reference is released early. Cancelling one waiter does not cancel another owner's opening. sessions.using(target, options, operation) waits for that settlement, holds its reference until the callback settles, and propagates rejected readiness and callback failures.
References keep local Session data, scoped Contexts, and history streams alive, not Host Agents. Final release withdraws the generation before teardown; later acquisition can create a new generation with the same id. binding(id) and scope(id) only borrow an existing generation. retainInfo(id) observes stable read-only source counts independently of catalog membership and performs no history I/O. Consumer source keys are declaration-merge extensible; navigation and completion acknowledgement belong to UI consumers, not this Controller. See Client Session references for ownership and teardown rules.
Session media references
SessionMediaReferences mounts GET|HEAD /api/file?path=<absolute path> on the authenticated connection.fetch channel when connection, fs, and attachments are composed. It reads ordinary files through ctx.fs, including temporary paths outside registered workspaces and files in remote providers. Neither directory containment nor MIME categories restrict access; mime-types supplies the response type, with application/octet-stream for unknown extensions. GET reuses readBytes for preflight and ongoing byte limits; HEAD reads metadata only. All files use ctx.attachments.imageLimits.maxImageBytes (normally 20 MiB); exceeding this limit returns 413. Responses contain the complete file, ignore Range, and carry private, no-store, nosniff, and a sandbox CSP so directly opened HTML/SVG cannot execute with the API origin. The Client rewrite lives in ui-chat (AssistantMarkdown); audio/video responses are available, while Markdown audio/video player nodes remain separate work.
Configuration
| Field | Default | Meaning |
|---|---|---|
nativeOpen |
platform-detected | Whether Session workspace paths can be handed to a native desktop opener |
The generated configuration catalog is the exhaustive source for accepted fields and their JSDoc.
Model Experience
None, as invoked Agent commands own any model-visible effect.
KV Cache effect
No direct effect; model requests remain owned by the Agent and LLM packages.
Known Limitations and Deferred Work
- The image byte cap does not validate decoded dimensions or pixel count.
- Control baselines represent process-local state and therefore cannot reconstruct jobs after a Host restart.
- A failed follow resumption remains visible to the caller instead of retrying indefinitely.
- The raw browser upload is one streaming HTTP request without resumable offsets; a retry sends the file again from byte zero.
- File-reference completion uses the shared Agent lookup and can resume a cold Session; the
skills/listcatalog is the non-activating alternative for skill metadata.
Dev Note
Working context for maintainers — click to expand
None.
Runtime invariant: No companion is published. Every page and frame is checked against the addressed durable Session.