Skip to content

fix(core): fork、候选通道和 Persona 注入的附件按 pushEvent 的规则落库 - #377

Open
Phantivia wants to merge 3 commits into
mainfrom
claude/kaypathy-may-2026-whereabouts-qj46m4
Open

Phantivia wants to merge 3 commits into
mainfrom
claude/kaypathy-may-2026-whereabouts-qj46m4

Conversation

@Phantivia

Copy link
Copy Markdown
Member

中文

附件(blobs)有三条路径没有按 pushEvent 和主循环工具回执的规则处理。这条规则是:字节落库,换成 log: 句柄;正文后面接一行 [blob 句柄 mime 名字] fallbackText;记录带上 context.blobs。这个 PR 让三条路径都按这条规则走,三个提交各改一处:

  1. fork 的工具回执:runForkLoop 只取 ToolOutcome.text。附件不落库,正文里没有那一行,记录上也没有引用。fork 里调用的工具(比如后台思考调用的 World 工具)返回的图片会丢掉,fallbackText 也一起丢了。现在改用 Core 的 internBlobs,和主循环一样。
  2. 候选通道:pushCandidate 的原始事件,以及投递时生成的投影事件,都原样写进事件库。World 交不了新字节;带已有句柄时正文里也没有那一行,模型不收该 MIME 时就什么都看不到。现在两处都会落库并接上那一行。CandidateSourceEvent.blobs 放宽为 BlobInput[],投影事件可以直接引用原始事件的 BlobRef。bilibili 测试的假宿主按新类型去掉了 blobs 字段。
  3. Persona 注入:injectInternal / injectExternal 增加可选参数 blobs。injectDeferred 的 render 放宽为 DeferredEventSpec['render'],可以返回 { text, blobs },和 World 的 pushDeferred 走同一条投递路径。没有把第一个参数改成 string | { text, blobs },因为那样实现 CoreApi 的假件都得跟着改;在末尾追加可选参数,现有的调用方和实现方都不受影响。

怎么验的:三个新用例(tests/core/fork.test.ts、tests/core/core.test.ts、tests/core/loop.test.ts)在去掉对应的 src/core 改动后会失败,加上改动后通过。三个提交各自单独跑过 pnpm test 和 pnpm run typecheck。没验的:没有用真实 bot 跑过,也没有让真模型看过这些路径上的图片。

English

Attachments (blobs) did not follow the pushEvent and main-loop tool receipt rule on three paths. The rule is: store the bytes under a log: handle, append [blob <handle> <mime> <name>] <fallbackText> to the text, and carry the refs in context.blobs. This PR applies the rule on all three paths, one commit each:

  1. Fork tool receipts: runForkLoop kept only ToolOutcome.text. Attachments were not stored, the text had no line, and the record had no refs. An image returned by a tool called inside a fork, such as a World tool called by background cognition, was lost along with its fallbackText. It now uses Core's internBlobs, as the main loop does.
  2. Candidate channel: pushCandidate source events, and the projections written at delivery, went into the event store as given. A World could not pass new bytes. With an existing handle the text had no line, so a model that does not take the MIME saw nothing. Both are now interned with the line appended. CandidateSourceEvent.blobs widens to BlobInput[], and a projection can reference a source event's BlobRef directly. The bilibili test's fake host drops the blobs field under the new type.
  3. Persona injection: injectInternal / injectExternal take an optional blobs argument. injectDeferred's render widens to DeferredEventSpec['render'], so it may return { text, blobs } and takes the same delivery path as a World's pushDeferred. The first parameter was not changed to string | { text, blobs }, because every fake implementing CoreApi would have had to change; a trailing optional argument leaves existing callers and implementers unaffected.

Verified: the three new tests (tests/core/fork.test.ts, tests/core/core.test.ts, tests/core/loop.test.ts) fail with the matching src/core change removed and pass with it. Each of the three commits passes pnpm test and pnpm run typecheck on its own. Not verified: no real bot run, and no real model has seen images on these paths.

Checks

  • pnpm test (287 files: 286 passed, 1 skipped; 5194 passed, 7 skipped)
  • pnpm run typecheck
  • pnpm typecheck:web (no browser code changed / 没改浏览器代码)
  • A bug fix carries a test that fails before the fix / 修 bug 附带修复前会失败的测试

Generated by Claude Code

- 根因:runForkLoop 只取 ToolOutcome.text,附件不落库,正文没有 [blob …] 行,
  回执记录也没有 context.blobs。fork 里调用的工具(后台思考调 World 工具)带的
  图片连同 fallbackText 一起丢失。
- 现在用 Core 的 internBlobs,与主循环同一套规则:新字节进 data/media 换 log: 句柄,
  句柄行接在正文后,回执记录带引用。
- tests/core/fork.test.ts 新增的用例在修复前失败。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178FYNAxBFGS64T8DSfgLqt
- 根因:pushCandidate 的原始事件和投递时的投影事件都原样写进事件库,不经过
  internBlobs 与 withBlobLines。World 交不了新字节,带已有句柄时正文也没有
  [blob …] 行,模型不收该 MIME 时连 fallbackText 都看不到。
- 两处都改用与 pushEvent 相同的处理;CandidateSourceEvent 的 blobs 放宽为
  BlobInput[],投影事件可以直接引用原始事件的 BlobRef。
- tests/core/core.test.ts 新增的用例走真实 Core 与日志附件库,修复前失败;
  bilibili 测试的假宿主按新类型去掉 blobs 字段。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178FYNAxBFGS64T8DSfgLqt
- injectInternal / injectExternal 增加可选的 blobs 参数,落库规则与 World 事件相同:
  新字节换 log: 句柄,句柄行接在正文后,事件带 BlobRef 并随投递挂到记录上。
- injectDeferred 的 render 放宽为 DeferredEventSpec['render'],可返回
  { text, blobs },与 World 的 pushDeferred 同一条投递路径。
- 没有把首参改成 string | { text, blobs }:那样实现 CoreApi 的假件都要改,
  追加可选参数对现有调用方和实现方都兼容。
- tests/core/loop.test.ts 新增的用例覆盖内部与外部注入,修复前失败。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178FYNAxBFGS64T8DSfgLqt

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants