fix(session-controller): derive queues from projections

This commit is contained in:
_Kerman
2026-08-31 13:35:52 +08:00
parent e5f2fbd9a2
commit 8b0ea3e461
11 changed files with 66 additions and 40 deletions
+20 -26
View File
@@ -1,10 +1,10 @@
/** Live Session queue, jobs, and projection state with reconnect baselines. */
import type { Context } from '@deepseek-ai/cordis'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { Agent, InboxState } from '@deepseek-ai/dsh-agent'
import type { JobSnapshot } from '@deepseek-ai/dsh-jobs'
import type {
JsonValue, Session, SessionEvent, SessionEventMap, SessionId, UserMessage,
JsonValue, Session, SessionId,
} from '@deepseek-ai/dsh-session'
import type {
SessionControlBaseline,
@@ -21,7 +21,6 @@ export class SessionControlController {
/** @param ctx - Host context carrying live Agent, projection, and jobs services. */
constructor(private readonly ctx: Context) {
ctx.on('session/event', (session, event) => { this.onSessionEvent(session, event) })
ctx.inject(['sessionProjections'], (projectionCtx) => {
projectionCtx.sessionProjections.onChanged((session, key, value, seq) => {
this.broadcast({
@@ -31,6 +30,14 @@ export class SessionControlController {
value: value as JsonValue,
seq,
})
if (key !== 'inbox') return
const agent = this.ctx.agents.get(session.id)
if (agent?.session !== session) return
this.broadcast({
type: 'queue',
sessionId: session.id,
items: queueItemsFromInbox(value as InboxState),
})
})
})
ctx.inject(['jobs'], (jobsCtx) => {
@@ -98,17 +105,6 @@ export class SessionControlController {
return blocks
}
private onSessionEvent(session: Session, event: SessionEvent): void {
if (event.type !== 'agent/inbox/spliced') return
const agent = this.ctx.agents.get(session.id)
if (agent?.session !== session) return
this.broadcast({
type: 'queue',
sessionId: session.id,
items: queueItems(agent, event.data),
})
}
private onJobsChanged(owner: Agent | undefined): void {
if (owner !== undefined) {
this.broadcast({ type: 'jobs', sessionId: owner.id, jobs: this.jobsFor(owner) })
@@ -174,23 +170,21 @@ class ControlQueue {
}
}
function queueItems(
agent: Agent,
splice?: SessionEventMap['agent/inbox/spliced'],
): SessionQueuedItem[] {
const project = (target: 'next-turn' | 'next-step'): readonly UserMessage[] => {
const messages = target === 'next-turn' ? agent.inbox.nextTurn : agent.inbox.nextStep
return splice?.target === target
? messages.toSpliced(splice.start, splice.removedCount ?? 0, ...splice.inserted)
: messages
}
function queueItems(agent: Agent): SessionQueuedItem[] {
return queueItemsFromInbox({
'next-turn': agent.inbox.nextTurn,
'next-step': agent.inbox.nextStep,
})
}
function queueItemsFromInbox(inbox: InboxState): SessionQueuedItem[] {
return [
...project('next-turn').map(message => ({
...inbox['next-turn'].map(message => ({
id: message.id,
placement: 'queued' as const,
message: { id: message.id, content: message.content as unknown as JsonValue[] },
})),
...project('next-step').map(message => ({
...inbox['next-step'].map(message => ({
id: message.id,
placement: message.source.kind === 'user' ? 'steering' as const : 'context' as const,
message: { id: message.id, content: message.content as unknown as JsonValue[] },