fix inbox lifecycle downstream contracts

This commit is contained in:
_Kerman
2026-07-31 22:00:39 +08:00
parent 8e88b17c9f
commit afedf18ccf
219 files changed
+5660 -4556

No files matched your search

+2 -2
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 docs/core-data-structures/core.md
core.md: d5fb4b1efb4a74d7a01ee09e4086863e981e2122
core.zh.md: 2cb15e20a87160e0ff0bc375752282969d77124b
core.md: 6bb01c91446fbb51e3c472bba3ed3a51b56704ff
core.zh.md: c52bf426020c591aab66d66658b46dfd8dc9b8af
+1 -1
View File
@@ -485,7 +485,7 @@ Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types
type InboxTarget = 'next-turn' | 'next-step'
```
Every pending occurrence is its `UserMessage`; `MessageId` is the sole identity. `Inbox.append`, `prepend`, `update`, `remove`, and `splice` record normalized durable `agent/inbox/spliced` mutations and reject duplicate pending ids. Ordinary removals are cancellations. `claim(target)` atomically removes the proposed step batch through pure deletion splices; the loop separately emits per-message claimed notifications. Whole-queue consumers such as UI projections reconstruct `nextTurn` and `nextStep` from the durable splices, while consumers following one message use the exact `agent/inbox/inserted`, `claimed`, and `discarded` notifications.
Every pending occurrence is its `UserMessage`; `MessageId` is the sole identity. `Inbox.append`, `prepend`, `update`, `remove`, `clear`, and `splice` record normalized durable `agent/inbox/spliced` mutations and reject duplicate pending ids. Ordinary removals and `clear()` are cancellations. `claim(target)` atomically removes the proposed step batch through pure deletion splices; the loop separately emits per-message claimed notifications. Whole-queue consumers such as UI projections reconstruct `nextTurn` and `nextStep` from the durable splices, while consumers following one message use the exact `agent/inbox/inserted`, `claimed`, and `discarded` notifications.
```ts type-equiv
/** Options for {@link Agent.cancel}. */
+1 -1
View File
@@ -493,7 +493,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
type InboxTarget = 'next-turn' | 'next-step'
```
每个待处理入队项就是其 `UserMessage``MessageId` 是唯一标识。`Inbox.append`、`prepend`、`update`、`remove` 与 `splice` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。普通删除表示取消。`claim(target)` 通过纯删除 splice 原子移除拟进入步骤的批次;循环另行逐条发出 claimed 通知。UI 投影等整体队列消费方通过持久 splice 重建 `nextTurn` 与 `nextStep`,而跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。
每个待处理入队项就是其 `UserMessage``MessageId` 是唯一标识。`Inbox.append`、`prepend`、`update`、`remove`、`clear` 与 `splice` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。普通删除和 `clear()` 都表示取消。`claim(target)` 通过纯删除 splice 原子移除拟进入步骤的批次;循环另行逐条发出 claimed 通知。UI 投影等整体队列消费方通过持久 splice 重建 `nextTurn` 与 `nextStep`,而跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。
```ts type-equiv
/** Options for {@link Agent.cancel}. */
+3 -3
View File
@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
goal.md: 704a93320cc38d1b9400edc2d9ad2342bc11dccd
goal.zh.md: b2e083843a70823bf6a6b43e046b1f38f4e11e22
# pnpm run verify-translation-pairing --write docs/core-data-structures/goal.md
goal.md: bb8b4525f7e12a904b1ba77cdefa4e4e1650ffe8
goal.zh.md: c61bfcf7683b3aa835ee7cca58aba861af847d17
+5 -5
View File
@@ -71,10 +71,10 @@ interface GoalView extends GoalSnapshot {
## Durable changes
Every mutation is a round-zero goal-sourced `user/message` whose metadata is either a complete snapshot or a clear tombstone. The version, metadata, goal source, and verbatim rendered content form one replay invariant.
Every mutation is a round-zero goal-sourced message whose metadata is either a complete snapshot or a clear tombstone. It commits when `agent.inject()` records that message in the `inserted` payload of a durable `agent/inbox/spliced` event. The strict fold and persisted projection derive mutations only from these insertions, so deleting the queued context does not roll back goal state. A later `user/message` with the same id verifies the source, metadata, and verbatim rendered content against the insertion without applying the mutation again.
```ts type-equiv
/** Full-snapshot goal mutation retained in a model-visible context event. */
/** Full-snapshot goal mutation committed by an injected inbox message. */
interface GoalSnapshotChangeMeta {
readonly kind: 'goal/change'
readonly version: 1
@@ -97,7 +97,7 @@ interface GoalClearChangeMeta {
}
```
Goal state changes use round `0`. A continuation consumer attributes each admitted user-message turn with a positive, sequential round number and the current revision; replay rejects gaps, stale revisions, stopped phases, and cap overflow.
Goal state changes use round `0`. A continuation consumer attributes each admitted user-message turn with a positive, sequential round number and the current revision; only these admitted `user/message` events advance `roundsStarted`. Replay rejects gaps, stale revisions, stopped phases, and cap overflow.
```ts type-equiv
/** Message attribution for durable goal state and continuation rounds. */
@@ -133,7 +133,7 @@ interface EditGoalRequest {
```
```ts type-equiv
/** Live notification after one goal mutation has been accepted for logging. */
/** Live notification after one goal mutation commits through inbox insertion. */
interface GoalChanged {
readonly operation: GoalOperation
readonly ref: GoalRef
@@ -144,4 +144,4 @@ interface GoalChanged {
## Service behavior
[`GoalService`](../../packages/goal/goal/src/index.ts) resolves creation defaults, folds strict replay, enforces exact-live-agent identity and compare-and-set mutations, overlays deferred injections, and emits contained `goal/changed` notifications. The package [README](../../packages/goal/goal/README.md) owns the callable and model-visible contract.
[`GoalService`](../../packages/goal/goal/src/index.ts) resolves creation defaults, folds strict replay from durable inbox insertions, enforces exact-live-agent identity and compare-and-set mutations, reconciles later admission by message id, and emits contained `goal/changed` notifications. The package [README](../../packages/goal/goal/README.md) owns the callable and model-visible contract.
+5 -5
View File
@@ -71,10 +71,10 @@ interface GoalView extends GoalSnapshot {
## 持久变更
每次变更都是 Round 编号为 0、来源为目标的 `user/message`,其元数据要么是完整快照,要么是清除墓碑。版本、元数据、目标来源和逐字渲染内容共同构成一项回放不变量
每次变更都是 Round 编号为 0、来源为目标的消息,其元数据要么是完整快照,要么是清除墓碑。当 `agent.inject()` 将该消息记录到持久 `agent/inbox/spliced` 事件的 `inserted` 载荷时,变更即已提交。严格折叠与持久投影只从这些插入项派生变更,因此删除队列中的上下文不会回滚目标状态。随后具有相同 id 的 `user/message` 会对照插入项验证来源、元数据和逐字渲染内容,而不会再次应用变更
```ts type-equiv
/** Full-snapshot goal mutation retained in a model-visible context event. */
/** Full-snapshot goal mutation committed by an injected inbox message. */
interface GoalSnapshotChangeMeta {
readonly kind: 'goal/change'
readonly version: 1
@@ -97,7 +97,7 @@ interface GoalClearChangeMeta {
}
```
目标状态变更使用 Round `0`。续跑消费方会为每个获准的用户消息轮次标注正数且连续的 Round 编号和当前修订号;回放会拒绝编号缺口、陈旧修订号、已停止阶段和超出上限。
目标状态变更使用 Round `0`。续跑消费方会为每个获准的用户消息轮次标注正数且连续的 Round 编号和当前修订号;只有这些获准的 `user/message` 事件会推进 `roundsStarted`。回放会拒绝编号缺口、陈旧修订号、已停止阶段和超出上限。
```ts type-equiv
/** Message attribution for durable goal state and continuation rounds. */
@@ -133,7 +133,7 @@ interface EditGoalRequest {
```
```ts type-equiv
/** Live notification after one goal mutation has been accepted for logging. */
/** Live notification after one goal mutation commits through inbox insertion. */
interface GoalChanged {
readonly operation: GoalOperation
readonly ref: GoalRef
@@ -144,4 +144,4 @@ interface GoalChanged {
## 服务行为
[`GoalService`](../../packages/goal/goal/src/index.ts) 解析创建默认值、执行严格回放折叠、校验确切的活跃 agent 身份、以比较并设置方式执行变更、叠加延迟注入,并发出 `goal/changed` 通知;监听器故障会被隔离。包 [README](../../packages/goal/goal/README.md) 负责记录可调用契约和面向模型的契约。
[`GoalService`](../../packages/goal/goal/src/index.ts) 解析创建默认值、从持久 inbox 插入项执行严格回放折叠、校验确切的活跃 agent 身份、以比较并设置方式执行变更、按消息 id 对账后续准入,并发出 `goal/changed` 通知;监听器故障会被隔离。包 [README](../../packages/goal/goal/README.md) 负责记录可调用契约和面向模型的契约。
+2 -2
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 docs/core-data-structures/session.md
session.md: 05c993755cc50fe9f1c6fe8836fc6a6fb4196ed2
session.zh.md: ac7e59c8563a0319609557c7346919b6e1d78ad2
session.md: 829a52c5ec4017b47f8fa6c7d45f20b0ff9630d4
session.zh.md: 6a70c1548fdd3b2b13bbcd1b102fadecdb34bf11
+6 -4
View File
@@ -26,9 +26,9 @@ interface UserMessage extends Message {
*/
interface SessionEventMap {
/**
* Opens turn `turn`. Every turn begins when the loop admits queued input;
* the following identified `user/message` event or batch records the
* admitted input.
* Opens turn `turn`. Every turn begins after the loop claims queued input
* and accepts the pre-step result; the following identified `user/message`
* event or batch records the messages entering the step.
*/
'turn/start': { turn: number }
/**
@@ -46,7 +46,7 @@ interface SessionEventMap {
* A user-role message on the model-visible surface: a direct human prompt
* (the queued message claimed for this turn), a synthetic `agent.inject()`
* context (file-change notices, subdir AGENTS.md, skill content, cron
* notifications, …), or an admitted goal continuation round. All three
* notifications, …), or an entered goal continuation round. All three
* project their `content` verbatim; `source` tells them apart.
*/
'user/message': UserMessage
@@ -494,6 +494,8 @@ interface TurnEndReasonMap {
completed: { kind: 'completed' }
/** A cancellation request interrupted the live turn. */
aborted: { kind: 'aborted'; reason: AgentCancelCause }
blocked: { kind: 'blocked' }
/**
* The turn failed.
*/
+6 -4
View File
@@ -26,9 +26,9 @@ interface UserMessage extends Message {
*/
interface SessionEventMap {
/**
* Opens turn `turn`. Every turn begins when the loop admits queued input;
* the following identified `user/message` event or batch records the
* admitted input.
* Opens turn `turn`. Every turn begins after the loop claims queued input
* and accepts the pre-step result; the following identified `user/message`
* event or batch records the messages entering the step.
*/
'turn/start': { turn: number }
/**
@@ -46,7 +46,7 @@ interface SessionEventMap {
* A user-role message on the model-visible surface: a direct human prompt
* (the queued message claimed for this turn), a synthetic `agent.inject()`
* context (file-change notices, subdir AGENTS.md, skill content, cron
* notifications, …), or an admitted goal continuation round. All three
* notifications, …), or an entered goal continuation round. All three
* project their `content` verbatim; `source` tells them apart.
*/
'user/message': UserMessage
@@ -498,6 +498,8 @@ interface TurnEndReasonMap {
completed: { kind: 'completed' }
/** A cancellation request interrupted the live turn. */
aborted: { kind: 'aborted'; reason: AgentCancelCause }
blocked: { kind: 'blocked' }
/**
* The turn failed.
*/