refactor: remove per-followup result attribution

This commit is contained in:
_Kerman
2026-07-30 16:48:28 +08:00
parent f6db60b52c
commit a6baddaaac
72 changed files with 586 additions and 1059 deletions
+10 -29
View File
@@ -10,7 +10,7 @@ import { resolve } from 'node:path'
import type { Agent, AgentHandle } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { carrierKeyOf, type Scoped } from '@deepseek-ai/dsh-scope'
import { SessionId, type TurnEndReason } from '@deepseek-ai/dsh-session'
import { SessionId } from '@deepseek-ai/dsh-session'
import type SubagentService from '@deepseek-ai/dsh-subagent'
import type { SubagentRunEndInfo } from '@deepseek-ai/dsh-subagent'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
@@ -19,7 +19,6 @@ import type {
InitializeResult,
JsonRpcTransportPeer,
SessionEventNotification,
SessionFinishedNotification,
SessionPromptParams,
SessionPromptResult,
SubagentFinishedNotification,
@@ -28,7 +27,6 @@ import type {
interface SessionRecord {
handle: AgentHandle
activePrompt: boolean
}
/** Recover the delegating parent from the service-owned scoped carrier. */
@@ -74,6 +72,9 @@ export class HarnessSdkServer {
const payload: SessionEventNotification = { sessionId: String(session.id), event }
this.transport.notify('session.event', payload)
}))
this.disposers.push(ctx.on('agent/status', (agent, status) => {
this.transport.notify('session.status', { sessionId: String(agent.session.id), status })
}))
this.disposers.push(ctx.on('session/created', (session) => {
const parentSession = session.header.parentSession
if (parentSession === undefined) return
@@ -124,36 +125,21 @@ export class HarnessSdkServer {
}
/**
* Run one prompt to settlement; overlap on the same session fails.
* Queue one identified prompt without assigning later activity to it.
* @param params - target session and user content.
* @returns acceptance after the turn settled.
* @returns the durable message identity.
*/
async prompt(params: SessionPromptParams): Promise<SessionPromptResult> {
const rec = await this.getOrCreateSession(params.sessionId)
if (rec.activePrompt) throw new Error(`session already has an active prompt: ${params.sessionId}`)
// An agent-loop-only reload disposes the loop's agents while this record
// survives; a retained agent accepts followup() silently, so validate the
// record against the live registry before delivery (as the ACP bridge does).
if (this.ctx.agents.get(rec.handle.agent.id) !== rec.handle.agent) {
throw new Error(`session agent was disposed outside the server: ${params.sessionId}`)
}
rec.activePrompt = true
try {
const message = createUserMessage({ content: params.contentBlocks, source: { kind: 'user' } })
rec.handle.agent.followup(message)
await rec.handle.agent.whenIdle()
const lastEnd = rec.handle.agent.session.events.findLast(event => event.type === 'turn/end')
const reason = lastEnd?.data.reason
const payload: SessionFinishedNotification = {
sessionId: params.sessionId,
status: this.finishedStatus(reason),
reason,
}
this.transport.notify('session.finished', payload)
return { accepted: true }
} finally {
rec.activePrompt = false
}
const message = createUserMessage({ content: params.contentBlocks, source: { kind: 'user' } })
rec.handle.agent.followup(message)
return { messageId: message.id }
}
/**
@@ -239,16 +225,11 @@ export class HarnessSdkServer {
...this.maxTokens === undefined ? {} : { maxTokens: this.maxTokens },
},
})
const rec: SessionRecord = { handle, activePrompt: false }
const rec: SessionRecord = { handle }
this.sessions.set(sessionId, rec)
return rec
}
private finishedStatus(reason: TurnEndReason | undefined): 'ok' | 'error' {
if (!reason) return 'error'
return successStatus(reason.kind, this.options)
}
private hasAdapterFor(provider: string): boolean {
return this.ctx.get('llm')?.listProviders().some(entry => entry.id === provider) ?? false
}