fix(agent-loop): address review — quiet-item parking, meta, discard balance
Resolve six review findings on the unified-send change: - quiet (wakeup:false) queued items no longer un-park the driver; the inbox distinguishes hasWakingQueued (drives the loop, idle/quiescence) from hasQueued (anything to dequeue), so a lone quiet item parks at idle and rides the next waking send. whenIdle/cancel settle off the waking signal, so cancelling a parked quiet item no longer hangs whenIdle. - SendOptions.meta on queued/steering sends now reaches the durable user/message and steering/message (was dropped except on injection). - a terminal agent/turn-stop that drops pending steering emits agent/inbox/discard so the enqueue-dequeue-or-discard ledger balances. - the loop-authored continuation reason is snapshotted and frozen like a public send. - gen-cordis-api collects exported classes (body-stripped) so the now- abstract-class Agent and its transitive shapes reappear in the API catalog. Adds regression tests for each and re-records the affected snapshot.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import type { ContentBlock, MessageSource } from '@deepseek-ai/dsh-llm'
|
||||
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
||||
import type { AgentMessage, AgentMessageId, HookContext } from '@deepseek-ai/dsh-agent'
|
||||
|
||||
/** One message waiting in an agent's inbox; `id` is the value `send` returned. */
|
||||
@@ -17,6 +18,8 @@ export interface InboxMessage {
|
||||
contexts: HookContext[]
|
||||
/** Whether the item is marked to wake the driver or force a continuation. */
|
||||
wakeup: boolean
|
||||
/** Opaque durable JSON state retained on the durable message but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,11 +42,22 @@ export class Inbox {
|
||||
private steeringMessages: InboxMessage[] = []
|
||||
private wakeup: (() => void) | undefined
|
||||
|
||||
/** True while queued messages are pending — read by the idle wait's fast path and the loop's turn-start checks. */
|
||||
/** True while any queued message is pending — read by cancellation's discard snapshot and the turn-start dequeue guard. */
|
||||
get hasQueued(): boolean {
|
||||
return this.queuedMessages.length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* True while a queued message wants to wake the driver — the "should the loop
|
||||
* run" signal read by the idle wait's fast path, the loop's idle-publish
|
||||
* check, and `whenIdle`. A `wakeup:false` (quiet) item alone leaves this
|
||||
* false, so the driver stays parked until a waking send (or a waking item
|
||||
* ahead of it in FIFO order) drives the loop; the quiet item then rides along.
|
||||
*/
|
||||
get hasWakingQueued(): boolean {
|
||||
return this.queuedMessages.some(message => message.wakeup)
|
||||
}
|
||||
|
||||
/** True while steering messages are pending — read by cancellation and the loop's stop-override check. */
|
||||
get hasSteering(): boolean {
|
||||
return this.steeringMessages.length > 0
|
||||
@@ -116,7 +130,7 @@ export class Inbox {
|
||||
* loop can exit).
|
||||
*/
|
||||
waitForQueued(cancel: Promise<void>): Promise<void> {
|
||||
if (this.hasQueued) return Promise.resolve()
|
||||
if (this.hasWakingQueued) return Promise.resolve()
|
||||
const { promise, resolve } = Promise.withResolvers<void>()
|
||||
this.wakeup = resolve
|
||||
void cancel.then(resolve)
|
||||
|
||||
Reference in New Issue
Block a user