Repository navigation
子代理结果投递即落盘,修复异常中断丢失结果 - #152
Merged
Merged
Conversation
- drainRun 先 appendEvent 写父转录再 mailbox.offer:盘为事实源,队列只负责本回合唤醒 - SpawnOptions.parentChain 改为必填,并按链首位推出父会话自己的转录位置(parentTranscriptOf) - 删除 recordSubagentResult / recordSubagentMessage,父回合 drain 只做 context.absorb,避免双写 - subagent_result / subagent_message 增加 uuid,absorb 按 uuid 判重,防跨回合重复注入 - send_message 走同一契约:先写收件人转录再投递,且只追加到已存在的转录
祖先不再凭链上身份免校验:没有转录就收不下可持久化的消息,一律 INVALID_INPUT。 用例改为真实建盘后再投递,同时锁住祖先路径推导(子代理平级挂顶层会话的 subagents/)。
parentTranscriptOf 注释与文档原文都写成「嵌套父」,易被读成目录逐层嵌套; 实际 owner 取链末位,子代理一律与父平级挂在顶层会话的 subagents/ 下。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更说明
子代理终态结果此前只进内存 Mailbox,由父回合在自己的 drain 点才写盘。这留下两个丢失窗口:
wait_agent就收尾)→ 无人 drain,结果留在内存队列里;MailboxLayer是内存 Map,一起清空。本 PR 把「投递」的语义从「入内存队列」改成「先写父转录、再入队」,使结果在投递点即持久化:异常中断后重建上下文能读到,不中断时也不影响内存中已有上下文。
主要改动
subagent/registry.ts:drainRun组装带uuid的subagent_result事件,先session.appendEvent(parentTranscriptPath)再mailbox.offer(...)。落盘失败仍投递(Effect.ignore),退化为旧行为。subagent/registry.ts:新增parentTranscriptOf()。parentSessionId本身不携带位置信息(同一 id 可能是顶层sessions/<id>.jsonl,也可能是sessions/<root>/subagents/<id>.jsonl),且全仓没有 id→路径反查,所以路径必须在spawn时定好跟着 run 走。SpawnOptions.parentChain改为必填,链首位即父,其余是父的祖先。agent/agent.ts:drain 不再经 session 落盘,直接context.absorb(sessionRef, items),保证「落盘一次、吸收一次」严格 1:1。session/session.ts/port.ts:删除recordSubagentResult/recordSubagentMessage,它们只在父回合 drain 点写盘,正是要拆掉的路径;已无调用者。context/context.ts:absorb按事件uuid判重。反例是真实存在的:结果在 turn 5 落盘、父回合未 drain 就结束,turn 6 重建 buffer 时已从盘上读到它,step 1 再 drain 到陈旧条目就会重复注入。tools/domains/subagent/message.ts:send_message走同一契约(来信是同一个洞)。收件人路径按祖先/直接子代两种情况解析,且只追加到已存在的转录,避免为已删除的祖先建幽灵文件。docs/subagent.md:更新结果回注一节,写明顺序不可颠倒的理由与跨回合去重契约。预期收益
测试
pnpm typecheck、pnpm lint、全量 1026 个用例通过。新增/调整:registry.test.ts:断言投递点就写盘、盘上那条与队列里那条是同一个对象、嵌套父的路径落在root-1/subagents/parent-1.jsonl。memory-buffer.test.ts:跨回合 drain 到已读入的事件不重复注入。lifecycle-tools.test.ts:send_message给不在线的子会话投递后,事件已在收件人转录末行。subagent-lifecycle-meta.test.ts中recordSubagentMessage的用例(锁的是被删函数返回字面量,属元测试)。已知遗留(未在本次处理)
subagent_result仍无turnId,因此不会被 rollback 隐藏、不进压缩区间。落盘更早会让这个既有语义问题更容易被撞上,但改变它属于回滚语义变更。appendLine不是 fsync。窗口从「整段」缩到「一行」,掉电仍可能丢最后一帧。