refactor(agent): require explicit send options

This commit is contained in:
_Kerman
2026-07-24 18:23:24 +08:00
parent b56628ced7
commit 879bc71864
21 changed files with 69 additions and 73 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
architecture.md: 164b019855b371172d40564af447d6359f5e4ff6
architecture.zh.md: 586dc8a52afb9487df3f801cfd07fcca4afa4cf9
architecture.md: c8f328560600d8a7e1f1ab659fab39c13c18dee3
architecture.zh.md: 61fba67a8585220e1ef59f02844271fee8a46401

View File

@@ -126,7 +126,7 @@ Turn and step execution events are turn-enclosed; an idle injected `user/message
### Agent Handles
`ctx.agents` owns live agents and returns `AgentHandle { agent, dispose() }`. Plugins use `send()`, `steer()`, `inject()`, `cancel()`, and `whenIdle()`. The caller fiber, factory provider, and consumer handle co-own teardown through one awaited disposer.
`ctx.agents` owns live agents and returns `AgentHandle { agent, dispose() }`. Plugins use `send(content, completeOptions)` when routing must be explicit, or the `followup()`, `steer()`, and `inject()` presets; `cancel()` and `whenIdle()` control lifecycle. The caller fiber, factory provider, and consumer handle co-own teardown through one awaited disposer.
### Agent Scope

View File

@@ -126,7 +126,7 @@ idle inject:
### Agent 句柄
`ctx.agents` 拥有活跃 agent并返回 `AgentHandle { agent, dispose() }`。插件使用 `send()``steer()``inject()``cancel()` `whenIdle()`。调用方 fiber、工厂提供方和消费方句柄通过同一个需等待完成的 disposer 共同拥有拆卸过程。
`ctx.agents` 拥有活跃 agent并返回 `AgentHandle { agent, dispose() }`。插件需要显式路由时使用 `send(content, completeOptions)`,否则使用 `followup()``steer()``inject()` 预设;`cancel()` `whenIdle()` 控制生命周期。调用方 fiber、工厂提供方和消费方句柄通过同一个需等待完成的 disposer 共同拥有拆卸过程。
### Agent 作用域

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
core.md: 36d65522201bddfd491a4f4f26b4f4b3303e4a77
core.zh.md: f065a67a2f279944ba77d25076e4b190d09f6cd0
core.md: 1d7a06405bbda523f37389e1b09c62549ece6750
core.zh.md: 2048a59451c3064eefdfe4225cc186c77921a9c0

View File

@@ -378,27 +378,22 @@ type SendTarget = 'next-turn' | 'next-step'
* (`next-turn`/wakeup), {@link Agent.steer} (`next-step`/wakeup), and
* {@link Agent.inject} (`next-step`/no-wakeup).
*
* An omitted source attests direct human input as `{ kind: 'user' }` and may
* authorize policy consumers, so non-human producers must label their content.
* The object is complete so routing and provenance are explicit; callers that
* want the ordinary user-message preset use {@link Agent.followup}.
*/
interface SendOptions {
/** Queue the item joins; defaults to `next-turn`. */
target?: SendTarget
/** Queue the item joins. */
target: SendTarget
/**
* Whether this item makes the model run: wake a parked driver (`next-turn`)
* or force a continuation step (`next-step` while running). Defaults to
* `true`. A `false` `next-turn` item queues without waking; a `false`
* or force a continuation step (`next-step` while running). A `false`
* `next-turn` item queues without waking; a `false`
* `next-step` item attaches durable context without forcing another step
* (the injection preset).
*/
wakeup?: boolean
source?: MessageSource
/**
* Model-facing contexts captured with this inbox item. A queued prompt exposes
* them through the default `agent/prompt-submit` allow decision, while steering
* records them directly at its next checkpoint.
*/
contexts?: HookContext[]
wakeup: boolean
/** Producer provenance; direct human input uses `{ kind: 'user' }`. */
source: MessageSource
}
```
@@ -406,7 +401,10 @@ The fixed-preset aliases own `target` and `wakeup`, so they accept only the rema
```ts type-equiv
/** Options accepted by the fixed-preset aliases, which own `target` and `wakeup`. */
type AliasSendOptions = Omit<SendOptions, 'target' | 'wakeup'>
interface AliasSendOptions {
/** Producer provenance; each alias supplies its documented default when omitted. */
source?: MessageSource
}
```
`send` returns the accepted message's opaque `AgentMessageId`, stable across that message's `agent/inbox/*` events:
@@ -479,8 +477,8 @@ abstract class Agent {
* The unified delivery primitive over the (`target` × `wakeup`) matrix.
* Detaches, validates, and freezes one lossless-JSON item, then routes it:
*
* - `next-turn` (default) queues an item that becomes the sole ordinary
* message of its own FIFO-ordered turn; `wakeup` (default `true`) wakes a
* - `next-turn` queues an item that becomes the sole ordinary message of its
* own FIFO-ordered turn; `wakeup:true` wakes a
* parked driver, while `wakeup:false` queues without waking.
* - `next-step` with `wakeup:true` submits steering into the active turn
* (idle falls back to a woken `next-turn`).
@@ -492,10 +490,10 @@ abstract class Agent {
* Attached contexts share the same snapshot and ownership boundary. Invalid
* input throws synchronously before any notification, enqueue, or append.
* @param content - the model-facing content blocks to deliver.
* @param options - target queue, wakeup decision, source, and contexts.
* @param options - target queue, wakeup decision, and source.
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
*/
abstract send(content: ContentBlock[], options?: SendOptions): AgentMessageId
abstract send(content: ContentBlock[], options: SendOptions): AgentMessageId
/**
* Clear queued and steering work — unless `keepInbox` — and abort the active

View File

@@ -380,27 +380,22 @@ type SendTarget = 'next-turn' | 'next-step'
* (`next-turn`/wakeup), {@link Agent.steer} (`next-step`/wakeup), and
* {@link Agent.inject} (`next-step`/no-wakeup).
*
* An omitted source attests direct human input as `{ kind: 'user' }` and may
* authorize policy consumers, so non-human producers must label their content.
* The object is complete so routing and provenance are explicit; callers that
* want the ordinary user-message preset use {@link Agent.followup}.
*/
interface SendOptions {
/** Queue the item joins; defaults to `next-turn`. */
target?: SendTarget
/** Queue the item joins. */
target: SendTarget
/**
* Whether this item makes the model run: wake a parked driver (`next-turn`)
* or force a continuation step (`next-step` while running). Defaults to
* `true`. A `false` `next-turn` item queues without waking; a `false`
* or force a continuation step (`next-step` while running). A `false`
* `next-turn` item queues without waking; a `false`
* `next-step` item attaches durable context without forcing another step
* (the injection preset).
*/
wakeup?: boolean
source?: MessageSource
/**
* Model-facing contexts captured with this inbox item. A queued prompt exposes
* them through the default `agent/prompt-submit` allow decision, while steering
* records them directly at its next checkpoint.
*/
contexts?: HookContext[]
wakeup: boolean
/** Producer provenance; direct human input uses `{ kind: 'user' }`. */
source: MessageSource
}
```
@@ -408,7 +403,10 @@ interface SendOptions {
```ts type-equiv
/** Options accepted by the fixed-preset aliases, which own `target` and `wakeup`. */
type AliasSendOptions = Omit<SendOptions, 'target' | 'wakeup'>
interface AliasSendOptions {
/** Producer provenance; each alias supplies its documented default when omitted. */
source?: MessageSource
}
```
`send` 返回被接受消息的不透明 `AgentMessageId`,并在该消息的 `agent/inbox/*` 事件中保持稳定:
@@ -481,8 +479,8 @@ abstract class Agent {
* The unified delivery primitive over the (`target` × `wakeup`) matrix.
* Detaches, validates, and freezes one lossless-JSON item, then routes it:
*
* - `next-turn` (default) queues an item that becomes the sole ordinary
* message of its own FIFO-ordered turn; `wakeup` (default `true`) wakes a
* - `next-turn` queues an item that becomes the sole ordinary message of its
* own FIFO-ordered turn; `wakeup:true` wakes a
* parked driver, while `wakeup:false` queues without waking.
* - `next-step` with `wakeup:true` submits steering into the active turn
* (idle falls back to a woken `next-turn`).
@@ -494,10 +492,10 @@ abstract class Agent {
* Attached contexts share the same snapshot and ownership boundary. Invalid
* input throws synchronously before any notification, enqueue, or append.
* @param content - the model-facing content blocks to deliver.
* @param options - target queue, wakeup decision, source, and contexts.
* @param options - target queue, wakeup decision, and source.
* @returns the accepted message's {@link AgentMessageId}, stable across its `agent/inbox/*` events.
*/
abstract send(content: ContentBlock[], options?: SendOptions): AgentMessageId
abstract send(content: ContentBlock[], options: SendOptions): AgentMessageId
/**
* Clear queued and steering work — unless `keepInbox` — and abort the active