fix(agent): restore public send method

This commit is contained in:
_Kerman
2026-07-31 10:01:35 +08:00
parent c891cb6f6f
commit 060c0f78f0
28 changed files with 76 additions and 32 deletions

View File

@@ -81,7 +81,7 @@ export class ReactLoopAgent implements Agent {
}
}
private send(message: UserMessage, target: InboxTarget, wakeup: boolean): void {
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void {
// Waking input cannot join an aborted admission or turn, so it starts the next turn.
const wakingAfterAbort = wakeup && this.phase.kind !== 'idle' && this.phase.abort.signal.aborted
const resolvedTarget = wakingAfterAbort ? 'next-turn' : target
@@ -180,7 +180,7 @@ export class ReactLoopAgent implements Agent {
if (admission.kind !== 'admitted') return false
signal.throwIfAborted()
} catch (error: unknown) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cancel may abort while admission awaits
// oxlint-disable-next-line typescript/no-unnecessary-condition -- cancel may abort while admission awaits
if (signal.aborted) return this.inbox.hasPending
throw error
}
@@ -216,11 +216,11 @@ export class ReactLoopAgent implements Agent {
if (admission.kind === 'empty' && turnEnds) break
}
} catch (error: unknown) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cancel may abort during any awaited turn operation
// oxlint-disable-next-line typescript/no-unnecessary-condition -- cancel may abort during any awaited turn operation
if (signal.aborted) turnEnds = { kind: 'aborted', reason: signal.reason as AgentCancelCause }
else turnEnds = { kind: 'error', error: errorChain(error) }
} finally {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- the turn is always ended in this block
// oxlint-disable-next-line typescript/no-non-null-assertion -- the turn is always ended in this block
this.session.append('turn/end', { turn, reason: turnEnds! })
}
return this.inbox.hasPending
@@ -317,7 +317,7 @@ export class ReactLoopAgent implements Agent {
: undefined
const maxTokens = this.options.maxTokens
const seedConfig = this.requestHeaderLogged
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- the instance logged the frozen header it now folds
// oxlint-disable-next-line typescript/no-non-null-assertion -- the instance logged the frozen header it now folds
? persistedConfig!
: deepFreeze({
...route,

View File

@@ -109,6 +109,15 @@ export interface Agent {
*/
whenIdle(): Promise<void>
/**
* Route identified input to an inbox boundary and optionally wake the driver.
* Waking input submitted after active cancellation is queued for the next turn.
* @param message - identified content and its producer provenance.
* @param target - the preferred next-turn or next-step inbox boundary.
* @param wakeup - whether delivery may wake the driver.
*/
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void
/**
* Queue an ordinary follow-up turn and wake the driver. The item becomes the
* sole ordinary message of its own turn.

View File

@@ -24,6 +24,7 @@ function stubAgent(rawId: string, overrides: Partial<Agent> = {}): Agent {
inbox: new Inbox(session),
status: 'idle',
ctx: new Context(),
send: () => {},
followup: () => {},
steer: () => {},
inject: () => {},