Files
deepseek-harness/packages/api/session-controller/src/commands.ts
T
Tianyi Cui 165cc31eb8 perf(llm,host): read embedded Assistant streams per compact record
Session format v2 embeds each attempt's compact stream in
assistant/message and assistant/attempt, but Host and client consumers
still expanded it into per-member TimedStreamChunk arrays and did
per-member work; expandAssistantStream materializes the full array before
find/toReversed/break can answer. Session Stats (the projection phase of
every Session open), the token meter's usage and provider-assembly folds,
the subagent output fold, and the Session Controller image lookup still
paid O(members) allocation and time per settlement.

The Chat and Trajectory definitions were already settled from
message.content on master; the remaining per-member folds stay.

dsh-llm now exports record-level readers (first token, visible content,
visible text, last raw chunk of a type, raw chunks of a type, joined
text, run-aware assembly, per-run first-token/first-visible times) that
scan the compact records once with early exit. Session Stats reads
assistantStreamFirstTokenTime, the token meter reads
lastAssistantStreamChunk(stream, 'usage') and assembles through
assembleAssistantStream, the subagent output fold appends
joinAssistantStreamText, and the Session Controller scans
assistantStreamChunks(stream, 'block-end').

expandAssistantStream is deliberately not memoized: retaining expansions
costs roughly ten times the compact stream for the Session's lifetime.
It remains the validating path at durable boundaries.

Synthetic 200-turn v0 migration benchmark, median of five: first-open
projection 28.0 ms -> 5.4 ms, first-open total 76.9 -> 50.0 ms, peak RSS
137.2 -> 94.9 MB; reopen projection 17.8 -> 5.6 ms; all phase budgets and
the 128 MB heap constraint keep passing.
2026-09-06 12:41:25 +08:00

618 lines
24 KiB
TypeScript

/** Session commands whose activation policy is explicit at each Remote method. */
import { randomUUID } from 'node:crypto'
import type { Context } from '@deepseek-ai/cordis'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { Agent, ModelSelection as AgentModelSelection } from '@deepseek-ai/dsh-agent'
import { AttachmentError } from '@deepseek-ai/dsh-attachment'
import type {
AttachmentAdmissionPart, FileAttachmentRef, ImageAttachmentRef,
} from '@deepseek-ai/dsh-attachment'
import type { FileUploadReceiptId } from '@deepseek-ai/dsh-client-file-upload/types'
import type {} from '@deepseek-ai/dsh-client-file-upload'
import {
ReasoningEffortId, assistantStreamChunks, createUserMessage, freezeMessage,
} from '@deepseek-ai/dsh-llm'
import type { MessageSource } from '@deepseek-ai/dsh-llm'
import { SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session'
import type { SessionEvent, SessionHeader, SessionId, UserMessage } from '@deepseek-ai/dsh-session'
import { SessionQueryError, type SessionObservation } from '@deepseek-ai/dsh-session-query'
import { SessionTitleInvalidError } from '@deepseek-ai/dsh-session-title'
import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time'
import { RemoteError, remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
import type { Workspace } from '@deepseek-ai/dsh-workspace'
import {
ApiSessionAgentController,
ApiSessionCwdConflict,
ApiSessionNotFound,
ApiSessionPresetConflict,
ApiSessionSubagentOwnership,
apiSessionSubagentOwnershipError,
hasApiSessionSubagentOwner,
inspectApiSession,
} from './agent.ts'
import type {
SessionAttachmentRequest,
SessionAttachmentValue,
SessionCancelRequest,
SessionCancelValue,
SessionCreateRequest,
SessionCreateValue,
SessionForkRequest,
SessionForkValue,
SessionPromptRequest,
SessionPromptValue,
SessionRenameRequest,
SessionRenameValue,
SessionSelectModelRequest,
SessionSelectModelValue,
SessionUpdateQueueRequest,
SessionUpdateQueueValue,
SessionRequestId,
} from './types.ts'
interface SessionReadState {
readonly id: SessionId
readonly header: SessionHeader
readonly events: readonly SessionEvent[]
}
/** Implements Session business commands delegated by the Session Controller Remote service. */
export class SessionCommandController {
/**
* @param ctx - Host context carrying Agent, model, attachment, title, and Workspace services.
* @param agents - sole owner of create, resume, and Session-local model selection.
* @param defaultCwd - project directory used when create names neither a Workspace nor a cwd.
*/
constructor(
private readonly ctx: Context,
private readonly agents: ApiSessionAgentController,
private readonly defaultCwd: string,
) {}
/**
* Create or idempotently adopt one ordinary Session.
* @param request - requested identity, location, and Agent preset.
* @returns the Session identity and resolved preset when configured.
*/
async create(request: SessionCreateRequest): Promise<SessionCreateValue> {
if (request.workspaceId !== undefined && request.cwd !== undefined) {
throw new RemoteError('gateway/bad-request', 'session.create accepts workspaceId or cwd, not both', {})
}
const sessionId = request.sessionId ?? brandString<SessionId>(`session-${randomUUID()}`)
let workspace: Workspace | undefined
if (request.workspaceId !== undefined) {
workspace = this.ctx.workspaceRegistry.get(request.workspaceId)
if (workspace === undefined) {
throw new RemoteError('workspace/not-found', `workspace "${request.workspaceId}" not found`, {
workspaceId: request.workspaceId,
})
}
}
const cwd = workspace?.path ?? request.cwd ?? this.defaultCwd
let adopted: Agent
try {
adopted = await this.agents.ensureSession(
sessionId,
cwd,
request.sessionId !== undefined,
request.agentPreset,
)
} catch (error) {
this.rejectCreation(sessionId, error)
}
if (workspace !== undefined) {
try {
await workspace.attachSession(sessionId)
} catch (error) {
throw new RemoteError(
'session/workspace-attach-failed',
`session "${sessionId}" was created but could not attach to workspace "${workspace.id}": ${String(error)}`,
{ sessionId, workspaceId: workspace.id },
)
}
}
const agentPreset = this.agents.presetForSession(adopted.session)
return { sessionId, ...(agentPreset === undefined ? {} : { agentPreset }) }
}
/**
* Validate and install one Session-local model selection.
* @param request - Session identity and requested model selection.
* @returns the normalized selection installed for the Session.
*/
async selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue> {
const agent = await this.resolveAgent(request.sessionId)
return this.agents.serializeImageAdmission(agent, async () => {
try {
const resolved = await this.ctx.llm.resolveCallConfig({
provider: request.provider,
model: request.model,
...(request.reasoningEffort === undefined
? {}
: { reasoningEffort: ReasoningEffortId(request.reasoningEffort) }),
})
const selected: AgentModelSelection = {
provider: resolved.provider,
model: resolved.model,
...(resolved.reasoningEffort === undefined
? {}
: { reasoningEffort: resolved.reasoningEffort }),
}
this.agents.selectForNextRequest(agent, selected)
try {
await this.ctx.agentDefaultModel.saveSelection(selected)
} catch (error) {
this.ctx.logger.warn(
`session-controller: model selection changed for the Session but the default was not saved: ${String(error)}`,
)
}
return { selected: { ...selected } }
} catch (error) {
if (remoteErrorOf(error) !== undefined) throw error
throw new RemoteError(
'session/model-unavailable',
error instanceof Error ? error.message : String(error),
{ provider: request.provider, model: request.model },
)
}
})
}
/**
* Normalize and append a user-owned Session title.
* @param request - Session identity and proposed title.
* @returns the accepted title and durable event sequence.
*/
async rename(request: SessionRenameRequest): Promise<SessionRenameValue> {
const agent = await this.resolveAgent(request.sessionId)
const titles = this.ctx.get('sessionTitle')
if (titles === undefined) {
throw new RemoteError('gateway/internal', 'renaming is unavailable: this deployment mounts no session-title service', {})
}
try {
const accepted = titles.rename(agent.session, request.title)
return { title: accepted.title, seq: accepted.eventSeq }
} catch (error) {
if (error instanceof SessionTitleInvalidError) {
throw new RemoteError('session/title-invalid', error.message, { sessionId: request.sessionId })
}
throw new RemoteError(
'gateway/internal',
`failed to rename session "${request.sessionId}": ${String(error)}`,
{},
)
}
}
/**
* Create a new ordinary Session from one completed-turn prefix.
* @param request - source Session and optional event anchor.
* @returns the new Session identity.
*/
async fork(request: SessionForkRequest): Promise<SessionForkValue> {
let atSeq: ReturnType<typeof SessionSeq> | undefined
try {
atSeq = request.atSeq === undefined ? undefined : SessionSeq(request.atSeq)
} catch {
throw new RemoteError('gateway/bad-request', 'atSeq must be a non-negative safe integer', {})
}
let observed: SessionObservation
try {
observed = await this.ctx.sessionQuery.observeSession(request.sessionId)
} catch (error) {
if (error instanceof SessionQueryError
&& error.code === 'SESSION_QUERY_SESSION_NOT_FOUND') {
throw new RemoteError('session/not-found', `session "${request.sessionId}" not found`, {
sessionId: request.sessionId,
})
}
throw new RemoteError(
'gateway/internal',
`fork source unavailable for session "${request.sessionId}": ${String(error)}`,
{},
)
}
using source = observed
const lastSeq = source.events.at(-1)?.seq ?? -1
const anchoredBoundary = atSeq === undefined
? undefined
: source.events.find(event => event.type === 'turn/end' && event.seq >= atSeq)
const boundary = anchoredBoundary
?? (atSeq === undefined || atSeq > lastSeq
? source.events.findLast(event => event.type === 'turn/end')
: undefined)
if (boundary === undefined) {
throw new RemoteError(
'session/fork-unavailable',
atSeq !== undefined && atSeq <= lastSeq
? `session "${request.sessionId}" has not completed the turn containing event ${String(atSeq)}`
: `session "${request.sessionId}" has no completed turn to fork from`,
{ sessionId: request.sessionId },
)
}
let cut = SessionLogOffset(boundary.seq + 1)
while (cut < source.events.length && source.events[cut]?.type !== 'turn/start') {
cut = SessionLogOffset(cut + 1)
}
let workspace: Workspace | undefined
try {
workspace = await this.forkWorkspace(source.header)
} catch (error) {
throw new RemoteError(
'gateway/internal',
`failed to resolve fork workspace for session "${request.sessionId}": ${String(error)}`,
{},
)
}
const childId = brandString<SessionId>(`session-${randomUUID()}`)
const composition = await this.agents.composeAgent(this.agents.presetForObservation(source))
try {
const { provider, model } = this.ctx.agentDefaultModel.currentSelection()
await this.ctx.agents.create({
sessionId: childId,
seed: source.events.slice(0, cut),
inheritedEventCount: cut,
meta: {
...(source.header.cwd === undefined ? {} : { cwd: source.header.cwd }),
parentSession: source.header.id,
isSeeded: true,
...(composition.agentPreset === undefined
? {}
: { agentPreset: composition.agentPreset }),
},
agentOptions: { provider, model },
setup: composition.setup,
})
} catch (error) {
throw new RemoteError(
'gateway/internal',
`failed to fork session "${request.sessionId}": ${String(error)}`,
{},
)
}
if (workspace !== undefined) {
try {
await workspace.attachSession(childId)
} catch (error) {
throw new RemoteError(
'session/workspace-attach-failed',
`session "${childId}" was forked but could not attach to workspace "${workspace.id}": ${String(error)}`,
{ sessionId: childId, workspaceId: workspace.id },
)
}
}
return { sessionId: childId }
}
/**
* Admit one browser prompt after explicit Agent resume and image validation.
* @param request - Session identity, prompt content, source metadata, and delivery mode.
* @returns acknowledgement that the Agent accepted the prompt.
*/
async prompt(request: SessionPromptRequest): Promise<SessionPromptValue> {
const clientTimeZone = request.clientTimeZone === undefined
? undefined
: canonicalClientTimeZone(request.clientTimeZone)
if (request.clientTimeZone !== undefined && clientTimeZone === undefined) {
throw new RemoteError(
'session/invalid-time-zone',
'clientTimeZone must be UTC or a valid IANA Area/Location name',
{ value: request.clientTimeZone },
)
}
const agent = await this.resolveAgent(request.sessionId)
if (hasPromptRequest(agent, request.requestId)) return { accepted: true }
const selection = this.agents.selectionFor(agent).current
if (!routeServed(this.ctx, selection.provider)) {
throw new RemoteError(
'session/model-unavailable',
`no adapter serves provider "${selection.provider}"; select a model for this session`,
{ provider: selection.provider, model: selection.model },
)
}
const source: MessageSource = {
kind: 'user',
rpcId: request.requestId,
...(clientTimeZone === undefined ? {} : { clientTimeZone }),
}
const hasImage = request.content.some(part => part.type === 'image')
const admit = async (): Promise<SessionPromptValue> => {
try {
if (hasImage) {
const current = this.agents.selectionFor(agent).current
const model = await this.ctx.llm.resolveModelInfo(current.provider, current.model)
if (model.inputModalities !== undefined && !model.inputModalities.includes('image')) {
throw new RemoteError(
'session/attachment-invalid',
`Model "${current.model}" does not support image input.`,
{ reason: 'MODEL_DOES_NOT_SUPPORT_IMAGES' },
)
}
}
const admission = resolvePromptFileReceipts(
request.content,
receiptId => this.ctx.fileUploads.resolve(agent, receiptId),
)
const content = await this.ctx.attachments.admitPromptContent(admission.content)
const message: UserMessage = createUserMessage({ content, source })
if (this.ctx.agents.get(agent.id) !== agent) {
throw new RemoteError(
'session/not-found',
`session "${agent.id}" was disposed during prompt admission`,
{ sessionId: agent.id },
)
}
using binding = this.ctx.fileUploads.bindPrompt(agent, admission.receiptIds, request.requestId)
if (request.mode === 'steer') agent.steer(message)
else agent.followup(message)
binding.commit()
} catch (error) {
if (remoteErrorOf(error) !== undefined) throw error
if (error instanceof AttachmentError) {
throw new RemoteError('session/attachment-invalid', error.message, { reason: error.code })
}
throw new RemoteError('session/agent-busy', 'prompt rejected', { reason: String(error) })
}
return { accepted: true }
}
return hasImage ? this.agents.serializeImageAdmission(agent, admit) : admit()
}
/**
* Read one durable image after proving the Session log references it.
* @param request - Session and attachment identities used for authorization.
* @returns the durable attachment reference and base64-encoded bytes.
*/
async attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue> {
let source: SessionReadState
try {
source = await this.readSessionState(request.sessionId)
} catch (error) {
if (error instanceof ApiSessionNotFound) {
throw new RemoteError('session/not-found', error.message, { sessionId: request.sessionId })
}
throw new RemoteError(
'gateway/internal',
`attachment authorization unavailable for session "${request.sessionId}": ${String(error)}`,
{},
)
}
const ref = referencedImage(source.events, String(request.attachmentId))
if (ref === undefined) {
throw new RemoteError(
'session/attachment-invalid',
'Image is not referenced by this session.',
{ reason: 'ATTACHMENT_NOT_REFERENCED' },
)
}
try {
const stored = await this.ctx.attachments.readImage(ref)
return {
attachment: stored.ref,
data: Buffer.from(stored.data).toString('base64'),
}
} catch (error) {
if (error instanceof AttachmentError) {
throw new RemoteError('session/attachment-invalid', error.message, { reason: error.code })
}
throw new RemoteError('gateway/internal', 'Unable to read image attachment.', {})
}
}
/**
* Mutate one still-pending queue occurrence without resuming a cold Agent.
* @param request - Session, queue item, and requested mutation.
* @returns acknowledgement that the queue mutation was applied.
*/
updateQueue(request: SessionUpdateQueueRequest): SessionUpdateQueueValue {
if (request.action.kind === 'edit'
&& request.action.content.some(block => block.type !== 'text')) {
throw new RemoteError(
'session/attachment-invalid',
'queue edits accept text content only',
{ reason: 'QUEUE_EDIT_NON_TEXT' },
)
}
const agent = this.ctx.agents.get(request.sessionId)
if (agent !== undefined && hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
throw apiSessionSubagentOwnershipError(request.sessionId)
}
if (agent === undefined) {
throw new RemoteError('session/queue-item-not-found', 'queued item is no longer pending', { itemId: request.itemId })
}
const nextTurn = agent.inbox.nextTurn.find(message => message.id === request.itemId)
const nextStep = agent.inbox.nextStep.find(message => message.id === request.itemId)
const located = nextTurn === undefined
? nextStep === undefined ? undefined : { target: 'next-step' as const, message: nextStep }
: { target: 'next-turn' as const, message: nextTurn }
if (located === undefined) {
throw new RemoteError('session/queue-item-not-found', 'queued item is no longer pending', { itemId: request.itemId })
}
const { target, message } = located
if (request.action.kind === 'steer' && (target !== 'next-turn' || agent.status !== 'running')) {
throw new RemoteError('session/steer-unavailable', 'current turn no longer accepts steering', { itemId: request.itemId })
}
if (request.action.kind === 'edit') {
agent.inbox.replace(request.itemId, freezeMessage<UserMessage>({
...message,
content: [...request.action.content],
}))
} else {
agent.inbox.remove(request.itemId)
if (request.action.kind === 'remove') {
const source = message.source
if (source.kind === 'user' && 'rpcId' in source) {
this.ctx.fileUploads.retirePrompt(agent, source.rpcId)
}
}
if (request.action.kind === 'steer') agent.steer(message)
}
return { accepted: true }
}
/**
* Cancel one live ordinary Agent while retaining pending inbox work.
* @param request - Session whose active Agent turn is cancelled.
* @returns acknowledgement that cancellation was requested.
*/
cancel(request: SessionCancelRequest): SessionCancelValue {
const agent = this.ctx.agents.get(request.sessionId)
if (agent === undefined) {
throw new RemoteError(
'session/not-found',
`session "${request.sessionId}" not found (not attached)`,
{ sessionId: request.sessionId },
)
}
if (hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
throw apiSessionSubagentOwnershipError(request.sessionId)
}
agent.cancel({ kind: 'user' }, { keepInbox: true })
return { accepted: true }
}
private async resolveAgent(sessionId: SessionId): Promise<Agent> {
const found = await this.agents.resolveAgent(sessionId)
if ('error' in found) throw found.error
return found.agent
}
private rejectCreation(sessionId: SessionId, error: unknown): never {
if (remoteErrorOf(error) !== undefined) throw error
if (error instanceof ApiSessionPresetConflict) {
throw new RemoteError('agent-preset/conflict', error.message, {
sessionId: error.sessionId,
requestedPreset: error.requestedPreset,
...(error.existingPreset === undefined ? {} : { existingPreset: error.existingPreset }),
})
}
if (error instanceof ApiSessionCwdConflict) {
throw new RemoteError('session/conflict', error.message, {
sessionId: error.sessionId,
requestedCwd: error.requestedCwd,
...(error.existingCwd === undefined ? {} : { existingCwd: error.existingCwd }),
})
}
if (error instanceof ApiSessionSubagentOwnership) {
throw apiSessionSubagentOwnershipError(error.sessionId)
}
throw new RemoteError('gateway/internal', `failed to create session "${sessionId}": ${String(error)}`, {})
}
private async readSessionState(sessionId: SessionId): Promise<SessionReadState> {
const attached = this.ctx.sessions.get(sessionId)
if (attached !== undefined) {
return { id: attached.id, header: attached.header, events: attached.snapshotEvents() }
}
const inspected = await inspectApiSession(this.ctx, sessionId)
return { id: inspected.meta.id, header: inspected.meta, events: inspected.events }
}
private async forkWorkspace(source: SessionHeader): Promise<Workspace | undefined> {
const workspaces = this.ctx.workspaceRegistry.list()
const direct = workspaces.find(workspace => workspace.sessionIds.includes(source.id))
if (direct !== undefined || source.origin !== 'subagent') return direct
const lineage = await this.ctx.sessionQuery.traceSession(source.id)
for (const ancestor of lineage.ancestors) {
const workspace = workspaces.find(candidate => candidate.sessionIds.includes(ancestor.header.id))
if (workspace !== undefined) return workspace
}
return undefined
}
}
function resolvePromptFileReceipts(
content: SessionPromptRequest['content'],
stagedFile: (receiptId: FileUploadReceiptId) => FileAttachmentRef | undefined,
): { readonly content: AttachmentAdmissionPart[]; readonly receiptIds: readonly FileUploadReceiptId[] } {
const receiptIds = new Set<FileUploadReceiptId>()
const resolved = content.map((part): AttachmentAdmissionPart => {
if (part.type !== 'file') return part
const attachment = stagedFile(part.receiptId)
if (attachment === undefined) {
throw new RemoteError(
'session/attachment-invalid',
'File was not uploaded for this session.',
{ reason: 'FILE_NOT_STAGED' },
)
}
receiptIds.add(part.receiptId)
return { type: 'file', attachment }
})
return { content: resolved, receiptIds: [...receiptIds] }
}
function hasPromptRequest(agent: Agent, requestId: SessionRequestId): boolean {
const matches = (message: UserMessage): boolean => {
const source = message.source
return source.kind === 'user' && 'rpcId' in source && source.rpcId === requestId
}
if (agent.inbox.nextTurn.some(matches) || agent.inbox.nextStep.some(matches)) return true
return agent.session.snapshotEvents().some((event) => {
if (event.type !== 'user/message') return false
const source = event.data.source
return source.kind === 'user' && 'rpcId' in source && source.rpcId === requestId
})
}
function imageBlockIn(
content: unknown,
match: (ref: ImageAttachmentRef) => boolean,
): ImageAttachmentRef | undefined {
if (!Array.isArray(content)) return undefined
for (const value of content) {
if (typeof value !== 'object' || value === null || Array.isArray(value)) continue
const block = value as { readonly type?: unknown; readonly attachment?: unknown; readonly content?: unknown }
if (block.type === 'image' && typeof block.attachment === 'object' && block.attachment !== null) {
const ref = block.attachment as ImageAttachmentRef
if (match(ref)) return ref
}
if (block.type === 'tool-result') {
const nested = imageBlockIn(block.content, match)
if (nested !== undefined) return nested
}
}
return undefined
}
function imageInEvent(
event: SessionEvent,
match: (ref: ImageAttachmentRef) => boolean,
): ImageAttachmentRef | undefined {
const data = event.data as {
readonly content?: unknown
readonly message?: { readonly content?: unknown }
readonly inserted?: readonly { readonly content?: unknown }[]
}
const direct = imageBlockIn(data.content, match)
if (direct !== undefined) return direct
const message = imageBlockIn(data.message?.content, match)
if (message !== undefined) return message
for (const inserted of data.inserted ?? []) {
const found = imageBlockIn(inserted.content, match)
if (found !== undefined) return found
}
if (event.type === 'assistant/message' || event.type === 'assistant/attempt') {
for (const chunk of assistantStreamChunks(event.data.stream, 'block-end')) {
const found = imageBlockIn([chunk.block], match)
if (found !== undefined) return found
}
}
return undefined
}
function referencedImage(
events: readonly SessionEvent[],
attachmentId: string,
): ImageAttachmentRef | undefined {
for (const event of events) {
const found = imageInEvent(event, ref => String(ref.attachmentId) === attachmentId)
if (found !== undefined) return found
}
return undefined
}
function routeServed(ctx: Context, provider: string): boolean {
return ctx.llm.listProviders().some(entry => entry.id === provider)
}