refactor agent pre-step inbox lifecycle

This commit is contained in:
_Kerman
2026-07-31 19:21:16 +08:00
parent c2ff9ddec8
commit fcc2b5e282
267 changed files with 2052 additions and 1546 deletions

View File

@@ -23,7 +23,7 @@ function stubAgent(ctx: Context, rawId: string): Agent {
id,
options: {},
session,
inbox: new Inbox(session),
inbox: new Inbox(session, { inserted: () => {}, discarded: () => {} }),
status: 'idle' as const,
ctx: scopeFiber.ctx,
send: () => {},

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/tasks/tool-tasks/README.md
README.md: 276ca3f8284b366bee54e585297c3a033a65c8d1
README.zh.md: 823c25c3e0c7c208ffef9c5be5b2e4ef7e96bad8
README.md: 6e8e889c2330d6991cb384674b011e4d2e988268
README.zh.md: 9e54e2de69f36d5d5b70094cab12e5503eaeb5fd

View File

@@ -18,7 +18,7 @@ When a producer supplies `outputLimitBytes`, `task_output`, terminal `task_kill`
## Completion notices
An unreported completion injects `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.` into the exact owner's session. When bounded, the stable id prefix and collection command outrank variable label/detail so the notice remains actionable at PTY's supported 64-byte minimum. Injection is durable context for the next request, not a wake-up. A kill or terminal read/wait marks delivery reported and suppresses the redundant notice. An owner-disposal race needs no special handling: the loop has no terminal state, so a notice injected during teardown appends as idle context — persisted for resume while the session is still attached, dropped with the detached log afterwards.
An unreported completion injects `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.` into the exact owner's next-step inbox. When bounded, the stable id prefix and collection command outrank variable label/detail so the notice remains actionable at PTY's supported 64-byte minimum. Injection is durable pending context for a later pre-step claim, not a wake-up; cancellation or owner disposal may discard it before claim. A kill or terminal read/wait marks delivery reported and suppresses the redundant notice.
## Config

View File

@@ -18,7 +18,7 @@
## 完成通知
一项尚未报告的完成会向精确 owner 的会话注入 `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.`。应用上限时,即使采用 PTY 支持的 64 字节下限,稳定 id 前缀和收集命令的优先级也高于可变 label/detail,因此通知仍可操作。注入是下一次请求使用的持久上下文,并非唤醒。kill 或针对已终止任务的 read/wait 会把交付标为已报告,并抑制重复通知。owner 释放竞态无需特殊处理:循环没有终结状态,teardown 期间注入的通知作为空闲上下文追加——会话仍挂接时随之持久化以供恢复,之后随脱离挂接的日志一并丢弃。
一项尚未报告的完成会把 `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.` 注入到精确 owner 的 next-step inbox。应用上限时,即使采用 PTY 支持的 64 字节下限,稳定 id 前缀和收集命令的优先级也高于可变 label/detail,因此通知仍可操作。注入是等待后续 pre-step 领取的持久上下文,并非唤醒;取消或 owner 释放可能在领取前丢弃它。kill 或针对已终止任务的 read/wait 会把交付标为已报告,并抑制重复通知。
## 配置

View File

@@ -218,11 +218,9 @@ export function apply(ctx: Context, config: Config): void {
})
// Use the exact lifecycle owner; reusable ids could resolve to a replacement.
// Delivery into a tearing-down owner is well-defined: the loop treats
// disposal like any cancel, so the notice appends as durable idle context
// (still attached and persisted during owner cleanup, presented on resume);
// after detach it lands in an unreferenced in-memory log and is dropped
// with it.
// Delivery targets the exact lifecycle owner. The notice waits in its
// next-step inbox until another step claims it; disposal before that
// boundary discards it with the owner.
ctx.tasks.onTaskDone((snapshot, owner) => {
if (snapshot.reported || owner === undefined) return
owner.inject(createUserMessage({