|
|
@@ -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[] },
|