refactor(agent): scope queue actions to edit and remove

This commit is contained in:
kingwl
2026-07-30 01:58:51 +08:00
parent e56c0699a9
commit 7ce330f0e4
49 changed files with 181 additions and 294 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 packages/core/agent/README.md
README.md: e71d3454d22fa829af9617083c5e68586970c70a
README.zh.md: 2dac18e50ab6b4acfc899013313e5bb0069ab36c
README.md: 6bd5279ace93b6d2569833be6f102c854105c2eb
README.zh.md: cdbb0c0b70124e0037a0f7a7a03ddedf1e3b78d3

View File

@@ -61,7 +61,7 @@ Turn and step boundaries and the model token stream are durable `session/event`
The handle every plugin programs against:
- `agent.send(message, options)` — the one delivery primitive over the (`target` × `wakeup`) matrix. `message` is an already identified, frozen `UserMessage`; callers normally create it with `createUserMessage()` before routing begins. `SendOptions` owns only the `target` and `wakeup` policy. Each accepted FIFO occurrence receives its own `InboxItemId`, even when callers reuse a `MessageId`; `agent/inbox/enqueue`/`update` and the terminal `dequeue` or `discard` carry that complete `InboxItem`. `target: 'next-turn'` queues one independent FIFO item that, if admitted, becomes the sole ordinary prompt in its turn. `target: 'next-step'` with `wakeup: true` submits steering, while `target: 'next-step'` with `wakeup: false` injects durable context without running the model. The [one-send-one-turn Agent Note](../../../.agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md) owns the turn rationale.
- `agent.updateInbox(itemId, action)` — synchronously edits, removes, or promotes one still-pending occurrence. Edit keeps its `MessageId`, `InboxItemId`, source, placement, and FIFO position while replacing frozen content. Remove emits the occurrence's terminal discard. Promote moves it to the front of its current FIFO and makes an ordinary queued item waking. A claimed item has crossed the ownership boundary and returns `not-found`.
- `agent.updateInbox(itemId, action)` — synchronously edits or removes one still-pending queued occurrence. Edit keeps its `MessageId`, `InboxItemId`, source, and FIFO position while replacing frozen content; remove emits the occurrence's terminal discard. Steering and claimed occurrences return `not-found`.
- `agent.followup(input)` — the `next-turn`/wakeup preset of `send()`: queue an ordinary follow-up turn and wake the driver.
- `agent.steer(input)` — the `next-step`/wakeup preset: during prompt admission or an open turn, stage steering for the next safe boundary without dispatching `agent/prompt-submit`; outside that acceptance window, delegate to a woken follow-up. Admission failure leaves staged steering for retry or a later admitted prompt, while cancellation or disposal may discard it.
- `agent.inject(input)` — the `next-step`/no-wakeup preset: append model-facing context without running the model; the next request sees a verbatim user-role message whose provenance is carried by the required `input.source`. During prompt admission or an open turn, injection waits in the outbox for the next safe boundary. Outside that acceptance window, it appends immediately without opening a turn; a context-only admission batch takes this fallback if admission closes without a turn, while context staged beside steering remains pending with it. Persistence reacts to `session/event` independently. Injection emits no `agent/inbox/*` event.

View File

@@ -61,7 +61,7 @@ Agent *创建* 由实现 `AgentFactory` 的插件(`dsh-agent-loop`)提供,
每个插件面向的 handle:
- `agent.send(message, options)`:覆盖(`target` × `wakeup`)矩阵的唯一投递原语。`message` 是已有标识且已冻结的 `UserMessage`;调用方通常会在开始路由前使用 `createUserMessage()` 创建它。`SendOptions` 只持有 `target` 与 `wakeup` 策略。每次获准进入 FIFO 的项都会获得独立的 `InboxItemId`,即使调用方复用了同一个 `MessageId`;`agent/inbox/enqueue`/`update` 及终态 `dequeue` 或 `discard` 都会携带这一完整 `InboxItem`。`target: 'next-turn'` 排队一条独立 FIFO 项,获准后成为其轮次中唯一的普通提示词。`target: 'next-step'` 且 `wakeup: true` 提交 steering(中途引导),而 `target: 'next-step'` 且 `wakeup: false` 注入持久上下文,不运行模型。轮次原理由 [one-send-one-turn Agent Note](../../../.agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md)拥有。
- `agent.updateInbox(itemId, action)`:同步编辑、移除或前移一个仍处于待处理状态的项。编辑会替换已冻结的内容,同时保留其 `MessageId`、`InboxItemId`、来源、放置方式与 FIFO 位置。移除会发出该项的终态 discard。前移会把它移至当前 FIFO 的队首,并使普通 queued 项能够唤醒驱动器。已被认领的项已经跨越所有权边界,因此返回 `not-found`。
- `agent.updateInbox(itemId, action)`:同步编辑或移除一个仍处于待处理状态的 queued 入队项。编辑会替换已冻结的内容,同时保留其 `MessageId`、`InboxItemId`、来源与 FIFO 位置;移除会发出该项的终态 discard。steering 项和已被认领的项会返回 `not-found`。
- `agent.followup(input)`:`send()` 的 `next-turn`/wakeup 预设:排队一个普通后续轮次并唤醒驱动器。
- `agent.steer(input)`:`next-step`/wakeup 预设:提示词接纳期间或轮次打开时,为下一个安全边界暂存 steering,且不分发 `agent/prompt-submit`;该接收窗口之外则委托给会唤醒的后续轮次。接纳失败会保留暂存的 steering,以供重试或之后获准的提示词使用,而取消或 dispose 可能丢弃它。
- `agent.inject(input)`:`next-step`/不唤醒预设:追加面向模型的上下文而不运行模型;下一次请求会看到一条逐字的 user role 消息,其来源由必填的 `input.source` 携带。提示词接纳期间或轮次打开时,注入会在 outbox 中等待下一个安全边界。该接收窗口之外,它会立即追加而不开启轮次;如果接纳结束却未开启轮次,仅含上下文的接纳批次会采用这一回退,而与 steering 一同暂存的上下文则会随其继续待处理。持久化独立地响应 `session/event`。注入不发出 `agent/inbox/*` 事件。

View File

@@ -50,11 +50,10 @@ export interface InboxItem {
readonly placement: InboxPlacement
}
/** A user-requested mutation of one still-pending inbox item. */
/** A user-requested mutation of one still-pending queued occurrence. */
export type InboxAction =
| { readonly kind: 'edit'; readonly content: ContentBlock[] }
| { readonly kind: 'remove' }
| { readonly kind: 'promote' }
/** Result of applying an inbox action at the synchronous ownership boundary. */
export type InboxActionResult = 'applied' | 'not-found'
@@ -180,13 +179,11 @@ export interface Agent {
send(message: UserMessage, options: SendOptions): void
/**
* Mutate one still-pending inbox occurrence synchronously. Editing preserves
* Mutate one still-pending queued occurrence synchronously. Editing preserves
* the message identity and queue position; removal publishes its terminal
* discard; promotion moves it to the front of its current FIFO and makes a
* queued item waking. A driver-claimed item is no longer pending and returns
* `not-found`.
* @param id - independently addressable inbox occurrence.
* @param action - edit, remove, or promote operation.
* discard. Steering occurrences and driver-claimed items return `not-found`.
* @param id - independently addressable queued occurrence.
* @param action - edit or remove operation.
* @returns whether the pending occurrence was found and updated.
*/
updateInbox(id: InboxItemId, action: InboxAction): InboxActionResult
@@ -280,21 +277,14 @@ declare module 'cordis' {
*/
'agent/inbox/enqueue'(this: Scoped<Agent>, agent: Agent, item: InboxItem): void
/**
* A still-pending inbox item changed content or position. The item id and
* placement remain stable; edit carries the replacement message, while
* promote makes this occurrence first in its current FIFO.
* A still-pending queued item changed content. The item id, placement, and
* position remain stable while the event carries the replacement message.
* @param agent - the owning agent.
* @param item - the complete post-update occurrence.
* @param action - the applied non-terminal operation.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @mode emit
*/
'agent/inbox/update'(
this: Scoped<Agent>,
agent: Agent,
item: InboxItem,
action: 'edit' | 'promote',
): void
'agent/inbox/update'(this: Scoped<Agent>, agent: Agent, item: InboxItem): void
/**
* The driver claimed one item out of the inbox: a queued item at a turn
* boundary, or steering drained between steps. Fires after the item leaves