| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /** Session Remote owner: cold reads, explicit Agent commands, and live control state. */
- import { Context } from '@deepseek-ai/cordis'
- import z from '@deepseek-ai/schemastery'
- import { errorChain } from '@deepseek-ai/dsh-llm'
- import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session'
- import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
- import {
- ApiSessionAgentController,
- inspectApiSession,
- type ApiSessionAgentResult,
- } from './agent.ts'
- import { SessionCommandController } from './commands.ts'
- import { SessionControlController } from './control.ts'
- import { SessionHistoryController } from './history.ts'
- import { ApiSessionList, DEFAULT_COLD_BLANK_PROBE_MAX_BYTES } from './list.ts'
- import type {
- SessionAttachmentRequest,
- SessionAttachmentValue,
- SessionCancelRequest,
- SessionCancelValue,
- SessionControlFrame,
- SessionCreateRequest,
- SessionCreateValue,
- SessionFollowFrame,
- SessionFollowRequest,
- SessionForkRequest,
- SessionForkValue,
- SessionListRequest,
- SessionListValue,
- SessionModels,
- SessionModelsRequest,
- SessionPage,
- SessionPageRequest,
- SessionPromptRequest,
- SessionPromptValue,
- SessionRenameRequest,
- SessionRenameValue,
- SessionSearchRequest,
- SessionSearchValue,
- SessionSelectModelRequest,
- SessionSelectModelValue,
- SessionUpdateQueueRequest,
- SessionUpdateQueueValue,
- } from './types.ts'
- export type * from './types.ts'
- export { ApiSessionNotFound } from './agent.ts'
- declare module '@deepseek-ai/cordis' {
- interface Context {
- /** Host Session business API and Remote namespace owner. */
- sessionController: SessionController
- }
- }
- /** Session Controller deployment policy. */
- export interface Config {
- /** Maximum cold Session artifact size read to determine blankness. */
- readonly coldBlankProbeMaxBytes?: number
- }
- /** Host service backing the generated `ctx.remote.session` namespace. */
- export class SessionController extends TypertRemoteService {
- static inject = [
- 'agentDefaultModel',
- 'agents',
- 'attachments',
- 'llm',
- 'sessions',
- 'sessionQuery',
- 'typert',
- 'workspaceRegistry',
- ]
- static Config: z<Config> = z.object({
- coldBlankProbeMaxBytes: z.natural().default(DEFAULT_COLD_BLANK_PROBE_MAX_BYTES),
- })
- private readonly agents: ApiSessionAgentController
- private readonly commands: SessionCommandController
- private readonly controlState: SessionControlController
- private readonly history: SessionHistoryController
- private readonly listState: ApiSessionList
- /**
- * @param ctx - Host context containing the Session capability assembly.
- * @param config - cold-list read policy.
- */
- constructor(ctx: Context, config: Config) {
- super(ctx, 'sessionController', { namespace: 'session' })
- this.agents = new ApiSessionAgentController(ctx)
- this.commands = new SessionCommandController(ctx, this.agents, process.cwd())
- this.controlState = new SessionControlController(ctx)
- this.history = new SessionHistoryController(ctx)
- this.listState = new ApiSessionList(
- ctx,
- config.coldBlankProbeMaxBytes ?? DEFAULT_COLD_BLANK_PROBE_MAX_BYTES,
- )
- ctx.on('session/created', (session) => {
- ctx.emit('api-session/added', this.listState.summaryFor(session))
- })
- ctx.on('session/disposed', (session) => {
- ctx.emit('api-session/removed', session.id)
- })
- ctx.on('agent/status', ({ agent, status }) => {
- ctx.emit('api-session/status', agent.id, status === 'running')
- })
- ctx.on('agent/error', ({ agent, error }) => {
- ctx.emit('api-session/error', agent.id, errorChain(error))
- })
- ctx.on('session/event', (session, event) => {
- if (event.type !== 'user/message' || event.data.source.kind !== 'user') return
- ctx.emit('api-session/activity', session.id, event.time)
- })
- }
- /**
- * Resolve or resume one ordinary Session for another Host API domain.
- * @param sessionId - Session identity whose Agent owns the operation.
- * @returns the live Agent or the stable Session-domain failure.
- */
- resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult> {
- return this.agents.resolveAgent(sessionId)
- }
- /**
- * Inspect one attached or persisted Session without activating its Agent.
- * @param sessionId - durable Session identity.
- * @param signal - optional caller cancellation for persistence reads.
- * @returns the current attached state or persisted header and event prefix.
- */
- inspect(
- sessionId: SessionId,
- signal?: AbortSignal,
- ): Promise<{ meta: SessionHeader; events: SessionEvent[] }> {
- const attached = this.ctx.sessions.get(sessionId)
- if (attached !== undefined) {
- return Promise.resolve({ meta: attached.header, events: [...attached.events] })
- }
- return inspectApiSession(this.ctx, sessionId, signal)
- }
- /**
- * Read all visible Session rows without resuming an Agent.
- * @param _request - reserved empty list request.
- * @param signal - cancellation for persistence reads.
- * @returns visible Session summaries ordered by activity.
- */
- @Remote('list')
- async list(_request: SessionListRequest, signal: AbortSignal): Promise<SessionListValue> {
- return { items: await this.listState.list(signal) }
- }
- /**
- * Search visible Session content without resuming an Agent.
- * @param request - literal message-content query.
- * @param signal - cancellation for list and search reads.
- * @returns authorized bounded Session search results.
- */
- @Remote('search')
- search(request: SessionSearchRequest, signal: AbortSignal): Promise<SessionSearchValue> {
- return this.listState.search(request.query, signal)
- }
- /**
- * Create or idempotently adopt one ordinary Session.
- * @param request - requested identity, location, and Agent preset.
- * @returns the Session identity and resolved preset when configured.
- */
- @Remote('create')
- create(request: SessionCreateRequest): Promise<SessionCreateValue> {
- return this.commands.create(request)
- }
- /**
- * Read model choices after explicitly resuming the addressed Session.
- * @param request - Session whose model state is requested.
- * @returns the current selection and available model groups.
- */
- @Remote('models')
- models(request: SessionModelsRequest): Promise<SessionModels> {
- return this.commands.models(request)
- }
- /**
- * Select one Session-local model after explicitly resuming the Session.
- * @param request - Session identity and requested model selection.
- * @returns the normalized selection installed for the Session.
- */
- @Remote('selectModel')
- selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue> {
- return this.commands.selectModel(request)
- }
- /**
- * Rename one Session after explicitly resuming it.
- * @param request - Session identity and proposed title.
- * @returns the accepted title and durable event sequence.
- */
- @Remote('rename')
- rename(request: SessionRenameRequest): Promise<SessionRenameValue> {
- return this.commands.rename(request)
- }
- /**
- * Fork one cold-readable completed-turn prefix into a new Session.
- * @param request - source Session and optional event anchor.
- * @returns the new Session identity.
- */
- @Remote('fork')
- fork(request: SessionForkRequest): Promise<SessionForkValue> {
- return this.commands.fork(request)
- }
- /**
- * Admit one prompt after explicitly resuming its Session.
- * @param request - Session identity, prompt content, source metadata, and delivery mode.
- * @param signal - caller cancellation before prompt admission begins.
- * @returns acknowledgement that the Agent accepted the prompt.
- */
- @Remote('prompt')
- prompt(request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue> {
- signal.throwIfAborted()
- return this.commands.prompt(request)
- }
- /**
- * Read one image proven reachable from the addressed Session log.
- * @param request - Session and attachment identities used for authorization.
- * @returns the durable attachment reference and base64-encoded bytes.
- */
- @Remote('attachment')
- attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue> {
- return this.commands.attachment(request)
- }
- /**
- * Mutate one still-pending queue occurrence on a live Agent.
- * @param request - Session, queue item, and requested mutation.
- * @returns acknowledgement that the queue mutation was applied.
- */
- @Remote('updateQueue')
- updateQueue(request: SessionUpdateQueueRequest): SessionUpdateQueueValue {
- return this.commands.updateQueue(request)
- }
- /**
- * Cancel one active Agent turn without dropping its pending inbox.
- * @param request - Session whose active Agent turn is cancelled.
- * @returns acknowledgement that cancellation was requested.
- */
- @Remote('cancel')
- cancel(request: SessionCancelRequest): SessionCancelValue {
- return this.commands.cancel(request)
- }
- /**
- * Read one cold-safe, message-aligned Session history page.
- * @param request - durable address, backward cursor, and page budget.
- * @param signal - cancellation for persistence reads.
- * @returns one chronological page and optional latest projections.
- */
- @Remote('page')
- page(request: SessionPageRequest, signal: AbortSignal): Promise<SessionPage> {
- return this.history.page(request, signal)
- }
- /**
- * Follow one Session log from its opening or resume cursor.
- * @param request - durable address and last committed sequence already held by the caller.
- * @param signal - cancellation owned by the Remote stream carrier.
- * @returns an opened cursor followed by gap-free event frames.
- */
- @Remote({ mode: 'stream' })
- follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame> {
- return this.history.follow(request, signal)
- }
- /**
- * Stream a complete live-control baseline followed by replacement frames.
- * @param signal - cancellation owned by the Remote stream carrier.
- * @returns one complete baseline followed by live replacement frames.
- */
- @Remote({ mode: 'stream' })
- control(signal: AbortSignal): AsyncIterable<SessionControlFrame> {
- return this.controlState.control(signal)
- }
- }
- export { buildModelCatalog } from './catalog.ts'
- export default SessionController
|