deepseek-harness/packages/api/session-controller/src/client/index.ts

125 lines
4.6 KiB
TypeScript

/** Client Session object layer, Agent scopes, and Remote lifecycle wiring. */
import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-agent/types'
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
import { createSessionControlStream } from './transport.ts'
import { ClientSessions } from './sessions/service.ts'
import type { ISessions } from './contract/sessions.ts'
import type { SessionRemotes } from './sessions/remotes.ts'
import type {} from '../remote-events.ts'
export {
createSessionControlStream,
SessionEventStream,
SESSION_SEARCH_RESULT_LIMIT,
SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS,
sessionStreamFailure,
} from './transport.ts'
export type {
ClientSessionPageRequest,
SessionControlStream,
SessionControlStreamOptions,
SessionEventStreamOptions,
SessionJournalChange,
SessionRemote,
} from './transport.ts'
export { createScope, scopeOf } from './scope.ts'
export type { AgentContext, AgentScopeHandle } from './scope.ts'
export { SessionCreateError, SessionForkError, workspaceTitleOf } from './sessions/service.ts'
export type { SessionBinding, SessionListState, SessionSummary } from './sessions/service.ts'
export type {
SessionListPhase,
SessionListSnapshot,
SessionSearchResultItem,
SubagentCatalogSnapshot,
} from './sessions/manager.ts'
export type { Session } from './sessions/session.ts'
export type {
ProjectionsBaseline,
ProjectionValueStore,
SessionProjectionMap,
UseProjection,
} from './sessions/projection-store.ts'
export type { ISession, ProjectionsFace, SessionFace } from './contract/session.ts'
export type { ISessions } from './contract/sessions.ts'
export { MutableSessionEventSource } from './contract/events.ts'
export type { SessionEventChange, SessionEventSource, SessionEventWindow } from './contract/events.ts'
export type {
OpenState,
PromptError,
QueuedMessage,
SessionSnapshot,
} from './contract/snapshot.ts'
export type { ClientFailure, ClientResult } from './contract/result.ts'
export { indexSubagentDescendants } from './sessions/subagent-lineage.ts'
export type { SubagentDescendantSummary } from './sessions/subagent-lineage.ts'
declare module '@deepseek-ai/cordis' {
interface Events {
/**
* A Host connection generation completed its readiness handshake.
* @mode emit
*/
'connection/reset'(): void
}
interface Context {
/** Client Session object layer and Agent scope owner. */
sessions: import('./contract/sessions.ts').ISessions
}
}
/** Required wire, Remote, and Context projection services. */
export const inject = [
'connection',
'typert',
'remote',
'remote.commands',
'remote.session',
]
/**
* Resolve the Client Session service from any Client Cordis context.
* @param ctx - Client root or Agent-scoped context.
* @returns the Client Session object layer.
*/
export function resolveClientSessions(ctx: Context): ISessions {
const sessions = ctx.get('sessions')
if (sessions === undefined) throw new Error('session-controller: Client sessions service unavailable')
return sessions
}
/**
* Install Client Session state and its reconnecting control stream.
* @param ctx - Client Cordis context.
*/
export function apply(ctx: Context): void {
const connection = ctx.get('connection') as ConnectionHandle
const remotes = ctx.remote as unknown as SessionRemotes
const sessions = new ClientSessions(ctx, connection.api, remotes)
ctx.remote.$on('api-session/added', (summary) => { sessions.handleSessionAdded(summary) })
ctx.remote.$on('api-session/removed', (sessionId) => { sessions.handleSessionRemoved(sessionId) })
ctx.remote.$on('api-session/status', (sessionId, running) => {
sessions.handleSessionStatus(sessionId, running)
})
ctx.remote.$on('api-session/activity', (sessionId, updatedAt) => {
sessions.handleSessionActivity(sessionId, updatedAt)
})
ctx.remote.$on('api-session/error', (sessionId, message) => {
sessions.handleSessionError(sessionId, message)
})
const control = createSessionControlStream(remotes, {
accept: (frame) => { sessions.handleControlFrame(frame) },
failed: (error) => { console.error('[session-controller] control stream failed:', error) },
})
control.start()
ctx.on('connection/reset', () => { sessions.handleConnected() })
if (connection.hostDescription.getSnapshot() !== undefined) sessions.handleConnected()
ctx.typert.contexts.registerClient('agent', {
identity: candidate => sessions.scopeOf(candidate),
resolve: sessionId => sessions.resolveAgentScope(sessionId),
})
ctx.effect(() => async () => { await control.dispose() }, 'session-controller.client.control')
}