fix: close three seams the message-machine refactor left open

agent-loop lifecycle: dispose drains machine.done to true quiescence.
cancel()'s own running-to-idle transition can legitimately re-enter
through an automation listener (goal-session's idle drive runs
synchronously to its first await) and replace done with a fresh
admission after the single capture; teardown now re-cancels and
re-awaits until the slot stabilizes, so the scope never unwinds under a
live run.

tools: a nested concludeTurn() stages on its own execution and promotes
to the enclosing composite only on the call's authoritative successful
verdict. A post-execute policy that converts the nested success into an
error no longer lets a recovering composite stop the turn on a failed
terminal operation (the Code Mode structured-output shape).

goal-session: the driver owns its round durability barrier again. The
loop's persistence is eager write-behind with no turn-end flush, so the
old post-turn agent/error signal for flush failures never fires; a
settled round now sets needsCheckpoint and re-enters drive, flushing
before the next reservation and disarming on failure instead of queueing
an autonomous round on state that was never persisted.
This commit is contained in:
_Kerman
2026-07-26 20:50:02 +08:00
parent 338da9f2e0
commit 2a51ef85fb
6 changed files with 161 additions and 3 deletions
+18 -2
View File
@@ -657,6 +657,12 @@ export class ToolRegistry extends Service {
private concludingExecutions = new WeakSet<ToolExecution>()
/** Enclosing transport tokens marked terminal by a successful nested call. */
private concludingParents = new Set<ToolExecutionToken>()
/**
* Nested conclusions staged until their call's final verdict: a post-execute
* policy may still convert the nested success into an error, and a failed
* terminal operation must not stop the turn through its composite.
*/
private pendingParentConclusions = new WeakMap<ToolRunContext, ToolExecutionToken>()
/** Original caller cancellation, kept outside the wrapper-mutable execution object. */
private cancellationStates = new WeakMap<ToolRunContext, ToolCancellationState>()
/** Definition-owned final content transform snapshotted before policy begins. */
@@ -979,7 +985,7 @@ export class ToolRegistry extends Service {
const definition = this.get(name, agent)
const finalizeContent = definition?.finalizeContent?.bind(definition)
const concludingExecutions = this.concludingExecutions
const concludingParents = this.concludingParents
const pendingParentConclusions = this.pendingParentConclusions
const base = {
token,
callId,
@@ -992,7 +998,9 @@ export class ToolRegistry extends Service {
},
concludeTurn(): void {
if (parent === undefined) concludingExecutions.add(this as unknown as ToolExecution)
else concludingParents.add(parent)
// Staged, not propagated: only this nested call's authoritative
// successful result promotes the marker onto its parent.
else pendingParentConclusions.set(this as unknown as ToolRunContext, parent)
},
}
try {
@@ -1206,6 +1214,14 @@ export class ToolRegistry extends Service {
} catch (error: unknown) {
finalResult = this.materializeFinalResult(toolErrorResult(error))
}
// Promote a staged nested conclusion only on the call's authoritative
// successful verdict — a policy-converted failure must not let the
// composite stop the turn on a failed terminal operation.
const stagedParent = this.pendingParentConclusions.get(exec)
if (stagedParent !== undefined) {
this.pendingParentConclusions.delete(exec)
if (!finalResult.isError) this.concludingParents.add(stagedParent)
}
this.notifyResult(exec, finalResult)
this.concludingParents.delete(exec.token)
return finalResult