mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-13 04:03:30 +00:00
fix(agent-loop): admit prompts under the prepared route
Resolve agent/request and prepareCall inside the accepted open step before committing system and user input. Preserve prompt-assembly model selection, bind capability and dispatch to the same prepared adapter, then log the envelope and derive the request synchronously. Preparation cancellation leaves a balanced empty step with no admitted input. Normalize incapable-route retained prompt versions with logged per-node empty replacements and a current head, including unchanged rendering and resumed history. Dormant empty tails do not override the effective prompt. No new event or delete operation. Tests: 389 agent-loop cases pass with exact agent.ts/runtime-context.ts coverage at 100% for all metrics; tsc -b packages/core/agent-loop/tsconfig.json passes. Seven actual-loop MockAdapter cases exercise pi-ai conversion, source-event reconstruction, both route transitions, resume, cancellation barriers, and bound selection. Stale-capability negative control fails all three transition regressions. Update EN/ZH request visibility, lifecycle generator, and owning rationale. Broad catalogs, recorded SDK/GUI and aggregate gates remain parent-owned.
This commit is contained in:
@@ -33,7 +33,7 @@ export interface SystemPromptCommit {
|
||||
|
||||
/** The request-series facts one prompt decision is made under. */
|
||||
export interface SystemPromptDecisionInput {
|
||||
/** Whether the route that served the last request reads a later `system` message as the effective prompt. */
|
||||
/** Whether the prepared route for this attempt reads a later `system` message as the effective prompt. */
|
||||
inHistory: boolean
|
||||
/**
|
||||
* Whether this step's request starts a new model-message series: a pre-step
|
||||
@@ -54,7 +54,8 @@ function eventsNewestFirst(session: Session): readonly SessionEvent[] {
|
||||
* replaces the latest surviving system node in place, except on an
|
||||
* `in-history` route while the request series continues, where it appends a
|
||||
* new `system/message` after the cached history; a series start folds the
|
||||
* prompt back into node 0 when no later system node survives.
|
||||
* prompt back into node 0 when no later system node survives. Incapable routes
|
||||
* normalize nonempty prompts to the first node and empty all later active nodes.
|
||||
*/
|
||||
export class SystemPromptProjection {
|
||||
constructor(private readonly session: Session) {}
|
||||
@@ -74,25 +75,35 @@ export class SystemPromptProjection {
|
||||
* Create an uncommitted system node when absent, even for an empty prompt, or changed.
|
||||
* @param rendered - the fully rendered system prompt; `''` when none is active.
|
||||
* @param input - the route capability and series facts for this step.
|
||||
* @returns the message and its surface intent, or `undefined` when no update is needed.
|
||||
* @returns ordered per-node updates; an empty list means no update is needed.
|
||||
*/
|
||||
project(rendered: string, input: SystemPromptDecisionInput): SystemPromptCommit | undefined {
|
||||
project(rendered: string, input: SystemPromptDecisionInput): SystemPromptCommit[] {
|
||||
const nodes = this.systemNodes()
|
||||
const latest = nodes.at(-1)
|
||||
if (latest === undefined) {
|
||||
return { message: createSystemMessage(rendered, SOURCE), intent: { surfaceOp: 'append' } }
|
||||
const head = nodes[0]
|
||||
if (head === undefined) {
|
||||
return [{ message: createSystemMessage(rendered, SOURCE), intent: { surfaceOp: 'append' } }]
|
||||
}
|
||||
if (latest.text === rendered) return
|
||||
const latest = nodes.findLast(node => node.text.length > 0) ?? head
|
||||
if (!input.inHistory && rendered.length > 0) {
|
||||
const updates = nodes.slice(1).filter(node => node.text.length > 0)
|
||||
.map(node => this.replace(node.seq, ''))
|
||||
if (head.text !== rendered) updates.push(this.replace(head.seq, rendered))
|
||||
return updates
|
||||
}
|
||||
if (latest.text === rendered) return []
|
||||
const message = createSystemMessage(rendered, SOURCE)
|
||||
// An empty node projects to no message, so clearing the prompt must rewrite
|
||||
// the surviving node; a series start with one system node re-baselines it.
|
||||
const append = input.inHistory
|
||||
&& rendered.length > 0
|
||||
&& (!input.startsSeries || nodes.length > 1)
|
||||
if (append) return { message, intent: { surfaceOp: 'append' } }
|
||||
const { seq } = latest
|
||||
if (append) return [{ message, intent: { surfaceOp: 'append' } }]
|
||||
return [this.replace(latest.seq, rendered)]
|
||||
}
|
||||
|
||||
private replace(seq: SessionSeq, text: string): SystemPromptCommit {
|
||||
return {
|
||||
message,
|
||||
message: createSystemMessage(text, SOURCE),
|
||||
intent: { surfaceOp: { op: 'replace', start: seq, end: seq }, sourceEventSeqs: [seq] },
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user