@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +8 -11
  3. package/docs/prefix-cache-architecture.md +14 -0
  4. package/package.json +22 -24
  5. package/packages/ask-user/README.md +55 -107
  6. package/packages/ask-user/ask-ui.ts +306 -732
  7. package/packages/ask-user/clipboard.ts +50 -0
  8. package/packages/ask-user/config.ts +74 -78
  9. package/packages/ask-user/package.json +2 -2
  10. package/packages/ask-user/questions.ts +151 -0
  11. package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
  12. package/packages/ask-user/tools.ts +198 -432
  13. package/packages/autocomplete/package.json +1 -1
  14. package/packages/autocomplete/src/constants.ts +38 -43
  15. package/packages/autocomplete/src/sorting.ts +8 -0
  16. package/packages/background-tasks/package.json +2 -2
  17. package/packages/background-tasks/src/config.ts +0 -73
  18. package/packages/background-tasks/src/index.ts +20 -16
  19. package/packages/background-tasks/src/registry.ts +1 -171
  20. package/packages/background-tasks/src/types.ts +0 -32
  21. package/packages/btw/README.md +13 -56
  22. package/packages/btw/extensions/btw.ts +403 -1796
  23. package/packages/btw/package.json +5 -2
  24. package/packages/btw/skills/btw/SKILL.md +7 -143
  25. package/packages/compactor/README.md +56 -287
  26. package/packages/compactor/package.json +5 -9
  27. package/packages/compactor/skills/compactor/SKILL.md +13 -20
  28. package/packages/compactor/src/card.ts +90 -0
  29. package/packages/compactor/src/commands/index.ts +73 -130
  30. package/packages/compactor/src/compaction/compact-args.ts +5 -3
  31. package/packages/compactor/src/compaction/filter-noise.ts +1 -1
  32. package/packages/compactor/src/compaction/hooks.ts +487 -311
  33. package/packages/compactor/src/compaction/source.ts +178 -0
  34. package/packages/compactor/src/compaction/summarize.ts +586 -52
  35. package/packages/compactor/src/config/manager.ts +130 -127
  36. package/packages/compactor/src/config/schema.ts +32 -48
  37. package/packages/compactor/src/index.ts +40 -431
  38. package/packages/compactor/src/info-screen.ts +44 -190
  39. package/packages/compactor/src/session/recall-blocks.ts +1 -1
  40. package/packages/compactor/src/stats.ts +76 -0
  41. package/packages/compactor/src/tools/context-budget.ts +17 -34
  42. package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
  43. package/packages/compactor/src/tools/register.ts +29 -269
  44. package/packages/compactor/src/types.ts +47 -186
  45. package/packages/core/README.md +25 -0
  46. package/packages/core/child-agent.ts +1 -0
  47. package/packages/core/command-echo.ts +63 -0
  48. package/packages/core/compaction-context.ts +41 -0
  49. package/packages/core/constants.ts +8 -38
  50. package/packages/core/events.ts +0 -53
  51. package/packages/core/fusion-status.ts +4 -0
  52. package/packages/core/global.d.ts +10 -0
  53. package/packages/core/harness-messages.ts +392 -0
  54. package/packages/core/index.ts +14 -0
  55. package/packages/core/kanboard-status.ts +49 -0
  56. package/packages/core/long-horizon-owner-status.ts +46 -0
  57. package/packages/core/model-cache.ts +81 -7
  58. package/packages/core/package.json +6 -3
  59. package/packages/core/src/attach/detect.ts +163 -0
  60. package/packages/core/src/child-agent/child-args.ts +31 -0
  61. package/packages/core/src/child-agent/delivery.ts +41 -0
  62. package/packages/core/src/child-agent/index.ts +5 -0
  63. package/packages/core/src/child-agent/readers.ts +203 -0
  64. package/packages/core/src/child-agent/runtime.ts +584 -0
  65. package/packages/core/src/evidence.ts +78 -0
  66. package/packages/core/src/hints/browser.ts +278 -0
  67. package/packages/core/src/hints/crab-data.ts +19 -0
  68. package/packages/core/src/hints/index.ts +865 -0
  69. package/packages/core/src/hints/lines.ts +607 -0
  70. package/packages/core/src/hints/store.ts +120 -0
  71. package/packages/core/src/jev/chatter.ts +24 -0
  72. package/packages/core/src/jev/settings.ts +177 -14
  73. package/packages/core/src/package-colors.ts +2 -0
  74. package/packages/core/src/settings/catalog.ts +39 -15
  75. package/packages/core/src/settings/engine.ts +48 -2
  76. package/packages/core/src/settings/hub.ts +83 -20
  77. package/packages/core/src/settings/open.ts +49 -0
  78. package/packages/core/src/settings/schema.ts +27 -7
  79. package/packages/core/src/tui/hub-kit.ts +18 -5
  80. package/packages/core/src/tui/kit.ts +198 -0
  81. package/packages/core/src/tui/progress.ts +73 -0
  82. package/packages/core/src/turn/arbiter.ts +290 -0
  83. package/packages/footer/README.md +4 -6
  84. package/packages/footer/package.json +3 -3
  85. package/packages/footer/src/glance-editor.ts +19 -11
  86. package/packages/footer/src/help.ts +1 -1
  87. package/packages/footer/src/index.ts +36 -3
  88. package/packages/footer/src/presets.ts +3 -4
  89. package/packages/footer/src/rendering/icons.ts +0 -20
  90. package/packages/footer/src/segments/compactor.ts +52 -142
  91. package/packages/footer/src/segments/status-ext.ts +0 -1
  92. package/packages/fusion/README.md +14 -4
  93. package/packages/fusion/package.json +4 -3
  94. package/packages/fusion/src/child-args.ts +1 -0
  95. package/packages/fusion/src/index.ts +161 -8
  96. package/packages/fusion/src/pi-settings.ts +57 -0
  97. package/packages/fusion/src/picker.ts +186 -51
  98. package/packages/fusion/src/preset.ts +56 -2
  99. package/packages/fusion/src/prompts.ts +1 -1
  100. package/packages/fusion/src/sidekick-runtime.ts +1 -420
  101. package/packages/fusion/src/tools.ts +107 -128
  102. package/packages/fusion/src/transcript.ts +27 -85
  103. package/packages/info-screen/package.json +2 -2
  104. package/packages/input-shortcuts/README.md +18 -30
  105. package/packages/input-shortcuts/package.json +2 -2
  106. package/packages/input-shortcuts/src/burst.ts +88 -0
  107. package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
  108. package/packages/input-shortcuts/src/cleared-input.ts +97 -0
  109. package/packages/input-shortcuts/src/index.ts +99 -193
  110. package/packages/input-shortcuts/src/last-response.ts +40 -0
  111. package/packages/input-shortcuts/src/registers.ts +8 -23
  112. package/packages/input-shortcuts/src/types.ts +4 -16
  113. package/packages/input-shortcuts/src/undo-redo.ts +59 -47
  114. package/packages/kanboard/README.md +51 -47
  115. package/packages/kanboard/index.ts +293 -55
  116. package/packages/kanboard/package.json +8 -8
  117. package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
  118. package/packages/kanboard/src/badges.ts +93 -0
  119. package/packages/kanboard/src/bin.ts +4 -2
  120. package/packages/kanboard/src/commands.ts +218 -84
  121. package/packages/kanboard/src/debug.ts +20 -0
  122. package/packages/kanboard/src/guard.ts +267 -109
  123. package/packages/kanboard/src/monitor.ts +330 -0
  124. package/packages/kanboard/src/notice-buffer.ts +74 -0
  125. package/packages/kanboard/src/progress.ts +27 -0
  126. package/packages/kanboard/src/reminders.ts +234 -0
  127. package/packages/kanboard/src/settings.ts +69 -15
  128. package/packages/kanboard/src/shapes.ts +5 -0
  129. package/packages/long-horizon/index.ts +190 -13
  130. package/packages/long-horizon/package.json +3 -3
  131. package/packages/long-horizon/src/commands.ts +103 -7
  132. package/packages/long-horizon/src/compaction-brief.ts +35 -0
  133. package/packages/long-horizon/src/engine/continuation.ts +84 -15
  134. package/packages/long-horizon/src/engine/goal-state.ts +64 -13
  135. package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
  136. package/packages/long-horizon/src/engine/ralph.ts +3 -2
  137. package/packages/long-horizon/src/engine/runaway.ts +68 -11
  138. package/packages/long-horizon/src/engine/verifier.ts +19 -0
  139. package/packages/long-horizon/src/gate.ts +37 -12
  140. package/packages/long-horizon/src/judge/resolve.ts +1 -0
  141. package/packages/long-horizon/src/owner.ts +35 -2
  142. package/packages/long-horizon/src/progress.ts +87 -0
  143. package/packages/long-horizon/src/prompts/goal.ts +1 -1
  144. package/packages/long-horizon/src/runtime.ts +172 -18
  145. package/packages/long-horizon/src/settings.ts +54 -39
  146. package/packages/long-horizon/src/tools/goal.ts +6 -0
  147. package/packages/long-horizon/src/tools/graph.ts +1 -1
  148. package/packages/long-horizon/src/tools/swarm.ts +3 -3
  149. package/packages/mcp/package.json +2 -2
  150. package/packages/memory/commands.ts +7 -2
  151. package/packages/memory/index.ts +210 -32
  152. package/packages/memory/package.json +3 -3
  153. package/packages/memory/save-session.ts +264 -0
  154. package/packages/memory/settings.ts +17 -0
  155. package/packages/memory/tools.ts +253 -203
  156. package/packages/notify/package.json +2 -2
  157. package/packages/skill-registry/README.md +53 -16
  158. package/packages/skill-registry/index.ts +230 -0
  159. package/packages/skill-registry/package.json +19 -8
  160. package/packages/skill-registry/src/editor.ts +206 -0
  161. package/packages/skill-registry/src/judge.ts +289 -0
  162. package/packages/skill-registry/src/registry.ts +87 -0
  163. package/packages/skill-registry/src/settings.ts +259 -0
  164. package/packages/skill-registry/src/vault.ts +80 -0
  165. package/packages/subagents/README.md +39 -119
  166. package/packages/subagents/package.json +6 -13
  167. package/packages/subagents/skills/subagents/SKILL.md +18 -42
  168. package/packages/subagents/src/agents.ts +213 -0
  169. package/packages/subagents/src/cards.ts +182 -0
  170. package/packages/subagents/src/index.ts +609 -1084
  171. package/packages/subagents/src/manager.ts +429 -0
  172. package/packages/subagents/src/profiles.ts +130 -0
  173. package/packages/subagents/src/transcript.ts +154 -0
  174. package/packages/subagents/src/ui.ts +389 -0
  175. package/packages/unipi/bundled.js +39756 -45569
  176. package/packages/unipi/index.ts +55 -10
  177. package/packages/updater/package.json +2 -2
  178. package/packages/utility/README.md +76 -69
  179. package/packages/utility/package.json +2 -2
  180. package/packages/utility/skills/image/SKILL.md +37 -0
  181. package/packages/utility/skills/utility/SKILL.md +17 -60
  182. package/packages/utility/src/answer/extract.ts +128 -0
  183. package/packages/utility/src/answer/index.ts +213 -0
  184. package/packages/utility/src/answer/reply.ts +133 -0
  185. package/packages/utility/src/answer/web.ts +167 -0
  186. package/packages/utility/src/attach/index.ts +205 -0
  187. package/packages/utility/src/commands.ts +38 -142
  188. package/packages/utility/src/diagnostics/engine.ts +50 -5
  189. package/packages/utility/src/image/index.ts +33 -0
  190. package/packages/utility/src/image/models.ts +221 -0
  191. package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
  192. package/packages/utility/src/image/settings.ts +178 -0
  193. package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
  194. package/packages/utility/src/image/tools.ts +168 -0
  195. package/packages/utility/src/image/transports.ts +186 -0
  196. package/packages/utility/src/index.ts +68 -389
  197. package/packages/utility/src/lifecycle/cleanup.ts +116 -311
  198. package/packages/utility/src/rename/gate.ts +102 -0
  199. package/packages/utility/src/rename/index.ts +191 -0
  200. package/packages/utility/src/rename/session.ts +170 -0
  201. package/packages/utility/src/render/delegated.ts +285 -0
  202. package/packages/utility/src/render/harness.ts +683 -0
  203. package/packages/utility/src/render/parse.ts +156 -0
  204. package/packages/utility/src/render/pi-settings.ts +38 -0
  205. package/packages/utility/src/render/reply-bg.ts +307 -0
  206. package/packages/utility/src/render/simple.ts +724 -0
  207. package/packages/utility/src/render/spacing.ts +235 -0
  208. package/packages/utility/src/render/styled.ts +251 -0
  209. package/packages/utility/src/render/tools.ts +353 -0
  210. package/packages/utility/src/settings.ts +166 -233
  211. package/packages/utility/src/types.ts +1 -222
  212. package/packages/watchdog/index.ts +17 -6
  213. package/packages/watchdog/package.json +3 -3
  214. package/packages/watchdog/src/config.ts +3 -2
  215. package/packages/web-api/package.json +2 -2
  216. package/packages/workflow/README.md +3 -0
  217. package/packages/workflow/index.ts +0 -2
  218. package/packages/workflow/package.json +2 -2
  219. package/packages/workflow/src/permission/decide.ts +6 -4
  220. package/packages/workflow/src/permission/index.ts +17 -7
  221. package/packages/workflow/src/permission/settings.ts +39 -4
  222. package/packages/workflow/src/plan/index.ts +19 -4
  223. package/packages/background-tasks/extensions/delegate-child.ts +0 -1
  224. package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
  225. package/packages/background-tasks/src/context-token-budget.ts +0 -903
  226. package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
  227. package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
  228. package/packages/background-tasks/src/delegate/budget.ts +0 -415
  229. package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
  230. package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
  231. package/packages/background-tasks/src/delegate/launch.ts +0 -484
  232. package/packages/background-tasks/src/delegate/result-package.ts +0 -459
  233. package/packages/background-tasks/src/delegate/runner.ts +0 -430
  234. package/packages/background-tasks/src/delegate/seed.ts +0 -423
  235. package/packages/background-tasks/src/delegate/types.ts +0 -323
  236. package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
  237. package/packages/background-tasks/src/delegate-extension.ts +0 -654
  238. package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
  239. package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
  240. package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
  241. package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
  242. package/packages/compactor/src/compaction/build-sections.ts +0 -76
  243. package/packages/compactor/src/compaction/extract/goals.ts +0 -36
  244. package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
  245. package/packages/compactor/src/compaction/format.ts +0 -53
  246. package/packages/compactor/src/compaction/merge.ts +0 -143
  247. package/packages/compactor/src/config/presets.ts +0 -160
  248. package/packages/compactor/src/executor/executor.ts +0 -233
  249. package/packages/compactor/src/executor/runtime.ts +0 -115
  250. package/packages/compactor/src/security/evaluator.ts +0 -89
  251. package/packages/compactor/src/security/policy.ts +0 -96
  252. package/packages/compactor/src/security/scanner.ts +0 -65
  253. package/packages/compactor/src/session/auto-inject.ts +0 -60
  254. package/packages/compactor/src/session/db.ts +0 -325
  255. package/packages/compactor/src/session/extract.ts +0 -144
  256. package/packages/compactor/src/session/resume-inject.ts +0 -64
  257. package/packages/compactor/src/session/snapshot.ts +0 -327
  258. package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
  259. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
  260. package/packages/compactor/src/tools/ctx-execute.ts +0 -23
  261. package/packages/compactor/src/tools/ctx-stats.ts +0 -96
  262. package/packages/image/README.md +0 -99
  263. package/packages/image/package.json +0 -61
  264. package/packages/image/skills/image/SKILL.md +0 -76
  265. package/packages/image/src/generate.ts +0 -256
  266. package/packages/image/src/index.ts +0 -135
  267. package/packages/image/src/models.ts +0 -519
  268. package/packages/image/src/openai-images-api.ts +0 -282
  269. package/packages/image/src/register-providers.ts +0 -209
  270. package/packages/image/src/settings.ts +0 -173
  271. package/packages/image/src/tools.ts +0 -326
  272. package/packages/input-shortcuts/src/clipboard.ts +0 -119
  273. package/packages/kanboard/src/runner.ts +0 -817
  274. package/packages/skill-registry/skills/auto/SKILL.md +0 -465
  275. package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
  276. package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
  277. package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
  278. package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
  279. package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
  280. package/packages/skill-registry/skills/debug/SKILL.md +0 -228
  281. package/packages/skill-registry/skills/document/SKILL.md +0 -120
  282. package/packages/skill-registry/skills/fix/SKILL.md +0 -212
  283. package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
  284. package/packages/skill-registry/skills/plan/SKILL.md +0 -231
  285. package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
  286. package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
  287. package/packages/skill-registry/skills/research/SKILL.md +0 -251
  288. package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
  289. package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
  290. package/packages/skill-registry/skills/work/SKILL.md +0 -218
  291. package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
  292. package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
  293. package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
  294. package/packages/subagents/agents/delegate.md +0 -14
  295. package/packages/subagents/agents/oracle.md +0 -78
  296. package/packages/subagents/agents/researcher.md +0 -52
  297. package/packages/subagents/agents/reviewer.md +0 -80
  298. package/packages/subagents/agents/scout.md +0 -50
  299. package/packages/subagents/agents/worker.md +0 -59
  300. package/packages/subagents/prompts/council.md +0 -48
  301. package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
  302. package/packages/subagents/prompts/parallel-cleanup.md +0 -59
  303. package/packages/subagents/prompts/parallel-research.md +0 -50
  304. package/packages/subagents/prompts/parallel-review.md +0 -54
  305. package/packages/subagents/prompts/review-loop.md +0 -43
  306. package/packages/subagents/skills/council-mode/SKILL.md +0 -230
  307. package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
  308. package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
  309. package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
  310. package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
  311. package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
  312. package/packages/subagents/src/acceptance.ts +0 -331
  313. package/packages/subagents/src/agent-manager.ts +0 -405
  314. package/packages/subagents/src/agent-memory.ts +0 -300
  315. package/packages/subagents/src/agent-overrides.ts +0 -183
  316. package/packages/subagents/src/agent-runner.ts +0 -271
  317. package/packages/subagents/src/async-runner.ts +0 -422
  318. package/packages/subagents/src/authority-policy.ts +0 -47
  319. package/packages/subagents/src/budgets.ts +0 -285
  320. package/packages/subagents/src/child-safety.ts +0 -140
  321. package/packages/subagents/src/config.ts +0 -249
  322. package/packages/subagents/src/conversation-viewer.ts +0 -281
  323. package/packages/subagents/src/core-compat.ts +0 -94
  324. package/packages/subagents/src/custom-agents.ts +0 -343
  325. package/packages/subagents/src/file-system-retry.ts +0 -50
  326. package/packages/subagents/src/fleet-data.ts +0 -105
  327. package/packages/subagents/src/fleet-view.ts +0 -322
  328. package/packages/subagents/src/foreground-detach.ts +0 -59
  329. package/packages/subagents/src/fork-context.ts +0 -285
  330. package/packages/subagents/src/global.d.ts +0 -7
  331. package/packages/subagents/src/guide.ts +0 -129
  332. package/packages/subagents/src/mission-state.ts +0 -133
  333. package/packages/subagents/src/mission-store.ts +0 -436
  334. package/packages/subagents/src/output-limits.ts +0 -142
  335. package/packages/subagents/src/parity-types.ts +0 -315
  336. package/packages/subagents/src/pi-args.ts +0 -167
  337. package/packages/subagents/src/result-files.ts +0 -232
  338. package/packages/subagents/src/result-watcher.ts +0 -221
  339. package/packages/subagents/src/retained-children.ts +0 -176
  340. package/packages/subagents/src/run-fanout-budget.ts +0 -356
  341. package/packages/subagents/src/scheduled-runs.ts +0 -349
  342. package/packages/subagents/src/schemas.ts +0 -263
  343. package/packages/subagents/src/slash-commands.ts +0 -80
  344. package/packages/subagents/src/supervisor-channel.ts +0 -249
  345. package/packages/subagents/src/tool-handler.ts +0 -1144
  346. package/packages/subagents/src/types.ts +0 -238
  347. package/packages/subagents/src/widget.ts +0 -457
  348. package/packages/subagents/src/workflow-script.ts +0 -737
  349. package/packages/subagents/src/workflow-worker.ts +0 -384
  350. package/packages/subagents/src/worktree.ts +0 -614
  351. package/packages/utility/src/analytics/collector.ts +0 -293
  352. package/packages/utility/src/info-screen.ts +0 -80
  353. package/packages/utility/src/lifecycle/process.ts +0 -162
  354. package/packages/utility/src/prefix-cache.ts +0 -263
  355. package/packages/utility/src/skill-discovery.ts +0 -542
  356. package/packages/utility/src/tools/env.ts +0 -113
  357. package/packages/utility/src/tui/name-badge-state.ts +0 -315
  358. package/packages/utility/src/tui/name-badge.ts +0 -109
  359. /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
@@ -1,551 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import type { Message } from '@earendil-works/pi-ai';
3
- import { canonicalJson } from './json-utils.js';
4
-
5
- /**
6
- * Frozen `visible-conversation-ledger-v2` transform.
7
- *
8
- * This is the single shared implementation of the package's conversation
9
- * projection: visible user/assistant text verbatim, assistant thinking and all
10
- * tool traffic replaced with deterministic hash-accounted omission receipts,
11
- * images marker-only, unknown block types a loud error.
12
- *
13
- * The transform has **no behavioural knobs**. Every consumer receives exactly
14
- * the same disposition for exactly the same input. Consumer identity — schema
15
- * versions, policy ids, request authority, branch-filter provenance — lives
16
- * entirely outside this module, so a consumer can never claim behaviour that did
17
- * not occur. A semantic change requires a new transform id and a new
18
- * implementation, never a flag added here.
19
- *
20
- * Fusion's persisted artifact bytes are frozen against this implementation and
21
- * are proven bit-identical by `tests/unit/fusion-golden-bytes.test.ts`.
22
- */
23
- export const VISIBLE_CONVERSATION_TRANSFORM_ID = 'visible-conversation-ledger-v2';
24
-
25
- export const CONVERSATION_IMAGE_OMISSION_PREFIX =
26
- '[Image omitted from fusion text transcript: ';
27
-
28
- export const OMITTED_EVENT_KINDS = [
29
- 'assistant_thinking',
30
- 'tool_call',
31
- 'tool_result_text',
32
- 'tool_result_image',
33
- ] as const;
34
- export type OmittedEventKind = (typeof OMITTED_EVENT_KINDS)[number];
35
-
36
- export interface OmittedEventRecord {
37
- index: number;
38
- source_ordinal: number;
39
- block_ordinal: number;
40
- kind: OmittedEventKind;
41
- payload_bytes: number;
42
- payload_sha256: string;
43
- tool_name?: string;
44
- tool_call_id?: string;
45
- mime_type?: string;
46
- }
47
-
48
- export interface OmittedActivityProjectionMapEntry {
49
- canonical_entry_index: number;
50
- entry_kind: 'omitted_activity';
51
- ledger_index_first: number;
52
- ledger_index_last: number;
53
- }
54
-
55
- export interface LedgerOnlyImageProjectionMapEntry {
56
- entry_kind: 'ledger_only_tool_result_image';
57
- ledger_index_first: number;
58
- ledger_index_last: number;
59
- }
60
-
61
- export type ContextProjectionMapEntry =
62
- | OmittedActivityProjectionMapEntry
63
- | LedgerOnlyImageProjectionMapEntry;
64
-
65
- export interface ProjectionTextEntry {
66
- kind: 'text';
67
- source_ordinal: number;
68
- block_ordinal: number;
69
- role: 'user' | 'assistant';
70
- text: string;
71
- }
72
-
73
- /**
74
- * Per-kind counts for one omitted run. Zero-valued kinds are omitted from the
75
- * serialized receipt by a fixed policy rule so receipt size does not scale with
76
- * the number of tracked kinds; absent means exactly zero.
77
- */
78
- export interface OmittedRunCounts {
79
- assistant_thinking?: number;
80
- tool_calls?: number;
81
- tool_result_texts?: number;
82
- }
83
-
84
- export interface ProjectionOmissionEntry {
85
- kind: 'omitted_activity';
86
- at: readonly [number, number];
87
- bytes: number;
88
- counts: OmittedRunCounts;
89
- }
90
-
91
- export type ProjectionEntry = ProjectionTextEntry | ProjectionOmissionEntry;
92
-
93
- export interface ToolCallNameCount {
94
- name: string;
95
- calls: number;
96
- }
97
-
98
- export interface ProjectionAccounting {
99
- message_count: number;
100
- included_text_entry_count: number;
101
- included_user_text_bytes: number;
102
- included_assistant_text_bytes: number;
103
- included_image_marker_count: number;
104
- empty_text_block_count: number;
105
- omitted_run_count: number;
106
- omitted_event_count: number;
107
- omitted_thinking_bytes: number;
108
- omitted_tool_call_count: number;
109
- omitted_tool_call_argument_bytes: number;
110
- omitted_tool_result_text_count: number;
111
- omitted_tool_result_text_bytes: number;
112
- omitted_tool_result_image_count: number;
113
- omitted_tool_result_image_bytes: number;
114
- tool_call_names: readonly ToolCallNameCount[];
115
- ledger_entry_count: number;
116
- ledger_root_sha256: string;
117
- omission_receipt_utf8_bytes: number;
118
- }
119
-
120
- /**
121
- * Ledger body produced by the transform, carrying no consumer envelope.
122
- *
123
- * The ledger root commits only to the ledger rows, never to schema version,
124
- * policy id, or transform id, so this body can be sealed by different consumers
125
- * without changing the root. That property is pinned by a dedicated test.
126
- */
127
- export interface LedgerBodyV2 {
128
- entries: readonly OmittedEventRecord[];
129
- projection_map: readonly ContextProjectionMapEntry[];
130
- root_sha256: string;
131
- }
132
-
133
- export interface ProjectedConversationV2 {
134
- entries: readonly ProjectionEntry[];
135
- accounting: ProjectionAccounting;
136
- ledger: LedgerBodyV2;
137
- }
138
-
139
- /** Raised when the transform meets a conversation block it refuses to silently drop. */
140
- export class UnsupportedConversationBlockError extends Error {
141
- readonly label: string;
142
-
143
- constructor(label: string) {
144
- super(
145
- `fusion context projection encountered an unsupported conversation block: ${label}`,
146
- );
147
- this.name = 'UnsupportedConversationBlockError';
148
- this.label = label;
149
- }
150
- }
151
-
152
- function utf8Bytes(value: string): number {
153
- return Buffer.byteLength(value, 'utf8');
154
- }
155
-
156
- function sha256Hex(bytes: Buffer): string {
157
- return createHash('sha256').update(bytes).digest('hex');
158
- }
159
-
160
- function uint64be(value: number): Buffer {
161
- const out = Buffer.alloc(8);
162
- out.writeBigUInt64BE(BigInt(value));
163
- return out;
164
- }
165
-
166
- /** Length-prefixed framing so concatenated fields cannot collide across boundaries. */
167
- function lengthPrefixed(bytes: Buffer): Buffer {
168
- return Buffer.concat([uint64be(bytes.length), bytes]);
169
- }
170
-
171
- function ledgerLeafHash(index: number, record: OmittedEventRecord): Buffer {
172
- return createHash('sha256')
173
- .update(Buffer.from('pi-fusion-ledger-leaf-v1\0', 'utf8'))
174
- .update(uint64be(index))
175
- .update(lengthPrefixed(Buffer.from(canonicalJson(record), 'utf8')))
176
- .digest();
177
- }
178
-
179
- function ledgerRootHash(leaves: readonly Buffer[]): string {
180
- const hash = createHash('sha256')
181
- .update(Buffer.from('pi-fusion-ledger-root-v1\0', 'utf8'))
182
- .update(uint64be(leaves.length));
183
- for (const leaf of leaves) hash.update(leaf);
184
- return hash.digest('hex');
185
- }
186
-
187
- function unsupportedBlock(label: string): UnsupportedConversationBlockError {
188
- return new UnsupportedConversationBlockError(label);
189
- }
190
-
191
- /**
192
- * Fixed policy rule: a zero-valued kind is absent from the receipt rather than
193
- * serialized as `0`. This keeps receipts compact without losing information —
194
- * absent is defined by the policy version to mean exactly zero — and it is
195
- * applied unconditionally, never adaptively because an input happens to be large.
196
- */
197
- function compactCounts(counts: Required<OmittedRunCounts>): OmittedRunCounts {
198
- const out: OmittedRunCounts = {};
199
- if (counts.assistant_thinking > 0) out.assistant_thinking = counts.assistant_thinking;
200
- if (counts.tool_calls > 0) out.tool_calls = counts.tool_calls;
201
- if (counts.tool_result_texts > 0) out.tool_result_texts = counts.tool_result_texts;
202
- return out;
203
- }
204
-
205
- function imageMarker(mimeType: string): string {
206
- return `${CONVERSATION_IMAGE_OMISSION_PREFIX}${mimeType}]`;
207
- }
208
-
209
- /**
210
- * Accumulates omitted-event ledger rows and turns maximal contiguous non-image
211
- * omission runs into compact, source-ordered receipts inside the projection.
212
- */
213
- class ProjectionBuilder {
214
- private readonly entries: ProjectionEntry[] = [];
215
- private readonly ledger: OmittedEventRecord[] = [];
216
- private readonly leaves: Buffer[] = [];
217
- private readonly projectionMap: ContextProjectionMapEntry[] = [];
218
- private readonly toolCallNames = new Map<string, number>();
219
- private pendingCounts: Required<OmittedRunCounts> | undefined;
220
- private pendingBytes = 0;
221
- private pendingLedgerFirst = 0;
222
- private pendingLedgerLast = 0;
223
- private pendingSourceFirst = 0;
224
- private pendingSourceLast = 0;
225
-
226
- includedUserTextBytes = 0;
227
- includedAssistantTextBytes = 0;
228
- includedImageMarkers = 0;
229
- emptyTextBlocks = 0;
230
-
231
- addText(entry: ProjectionTextEntry): void {
232
- this.flush();
233
- this.entries.push(entry);
234
- if (entry.role === 'user') this.includedUserTextBytes += utf8Bytes(entry.text);
235
- else this.includedAssistantTextBytes += utf8Bytes(entry.text);
236
- }
237
-
238
- addOmission(
239
- sourceOrdinal: number,
240
- blockOrdinal: number,
241
- kind: OmittedEventKind,
242
- payload: Buffer,
243
- extra: { toolName?: string; toolCallId?: string; mimeType?: string } = {},
244
- ): void {
245
- const index = this.ledger.length;
246
- const record: OmittedEventRecord = {
247
- index,
248
- source_ordinal: sourceOrdinal,
249
- block_ordinal: blockOrdinal,
250
- kind,
251
- payload_bytes: payload.length,
252
- payload_sha256: sha256Hex(payload),
253
- };
254
- if (extra.toolName !== undefined) record.tool_name = extra.toolName;
255
- if (extra.toolCallId !== undefined) record.tool_call_id = extra.toolCallId;
256
- if (extra.mimeType !== undefined) record.mime_type = extra.mimeType;
257
- this.ledger.push(record);
258
- this.leaves.push(ledgerLeafHash(index, record));
259
-
260
- if (extra.toolName !== undefined && kind === 'tool_call') {
261
- this.toolCallNames.set(extra.toolName, (this.toolCallNames.get(extra.toolName) ?? 0) + 1);
262
- }
263
-
264
- if (kind === 'tool_result_image') {
265
- this.flush();
266
- this.addLedgerOnlyImageMap(index);
267
- return;
268
- }
269
-
270
- if (this.pendingCounts === undefined) {
271
- this.pendingCounts = {
272
- assistant_thinking: 0,
273
- tool_calls: 0,
274
- tool_result_texts: 0,
275
- };
276
- this.pendingBytes = 0;
277
- this.pendingLedgerFirst = index;
278
- this.pendingSourceFirst = sourceOrdinal;
279
- }
280
- this.pendingLedgerLast = index;
281
- this.pendingSourceLast = sourceOrdinal;
282
- this.pendingBytes += payload.length;
283
- if (kind === 'assistant_thinking') this.pendingCounts.assistant_thinking += 1;
284
- else if (kind === 'tool_call') this.pendingCounts.tool_calls += 1;
285
- else this.pendingCounts.tool_result_texts += 1;
286
- }
287
-
288
- private addLedgerOnlyImageMap(index: number): void {
289
- const previous = this.projectionMap[this.projectionMap.length - 1];
290
- if (
291
- previous !== undefined &&
292
- previous.entry_kind === 'ledger_only_tool_result_image' &&
293
- previous.ledger_index_last + 1 === index
294
- ) {
295
- previous.ledger_index_last = index;
296
- return;
297
- }
298
- this.projectionMap.push({
299
- entry_kind: 'ledger_only_tool_result_image',
300
- ledger_index_first: index,
301
- ledger_index_last: index,
302
- });
303
- }
304
-
305
- private flush(): void {
306
- const counts = this.pendingCounts;
307
- if (counts === undefined) return;
308
- const canonicalEntryIndex = this.entries.length;
309
- const entry: ProjectionOmissionEntry = {
310
- at: [this.pendingSourceFirst, this.pendingSourceLast],
311
- bytes: this.pendingBytes,
312
- counts: compactCounts(counts),
313
- kind: 'omitted_activity',
314
- };
315
- this.entries.push(entry);
316
- this.projectionMap.push({
317
- canonical_entry_index: canonicalEntryIndex,
318
- entry_kind: 'omitted_activity',
319
- ledger_index_first: this.pendingLedgerFirst,
320
- ledger_index_last: this.pendingLedgerLast,
321
- });
322
- this.pendingCounts = undefined;
323
- this.pendingBytes = 0;
324
- }
325
-
326
- finish(messageCount: number): ProjectedConversationV2 {
327
- this.flush();
328
- const rootSha256 = ledgerRootHash(this.leaves);
329
- let omittedRunCount = 0;
330
- let includedTextEntries = 0;
331
- let receiptBytes = 0;
332
- for (const entry of this.entries) {
333
- if (entry.kind === 'text') includedTextEntries += 1;
334
- else {
335
- omittedRunCount += 1;
336
- receiptBytes += utf8Bytes(canonicalJson(entry));
337
- }
338
- }
339
- let thinkingBytes = 0;
340
- let toolCallCount = 0;
341
- let toolArgumentBytes = 0;
342
- let toolResultTextCount = 0;
343
- let toolResultTextBytes = 0;
344
- let toolResultImageCount = 0;
345
- let toolResultImageBytes = 0;
346
- for (const row of this.ledger) {
347
- if (row.kind === 'assistant_thinking') thinkingBytes += row.payload_bytes;
348
- else if (row.kind === 'tool_call') {
349
- toolCallCount += 1;
350
- toolArgumentBytes += row.payload_bytes;
351
- } else if (row.kind === 'tool_result_text') {
352
- toolResultTextCount += 1;
353
- toolResultTextBytes += row.payload_bytes;
354
- } else {
355
- toolResultImageCount += 1;
356
- toolResultImageBytes += row.payload_bytes;
357
- }
358
- }
359
- const toolCallNames: ToolCallNameCount[] = [...this.toolCallNames.entries()]
360
- .map(([name, calls]) => ({ name, calls }))
361
- .sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
362
- const accounting: ProjectionAccounting = {
363
- message_count: messageCount,
364
- included_text_entry_count: includedTextEntries,
365
- included_user_text_bytes: this.includedUserTextBytes,
366
- included_assistant_text_bytes: this.includedAssistantTextBytes,
367
- included_image_marker_count: this.includedImageMarkers,
368
- empty_text_block_count: this.emptyTextBlocks,
369
- omitted_run_count: omittedRunCount,
370
- omitted_event_count: this.ledger.length,
371
- omitted_thinking_bytes: thinkingBytes,
372
- omitted_tool_call_count: toolCallCount,
373
- omitted_tool_call_argument_bytes: toolArgumentBytes,
374
- omitted_tool_result_text_count: toolResultTextCount,
375
- omitted_tool_result_text_bytes: toolResultTextBytes,
376
- omitted_tool_result_image_count: toolResultImageCount,
377
- omitted_tool_result_image_bytes: toolResultImageBytes,
378
- tool_call_names: toolCallNames,
379
- ledger_entry_count: this.ledger.length,
380
- ledger_root_sha256: rootSha256,
381
- omission_receipt_utf8_bytes: receiptBytes,
382
- };
383
- return {
384
- entries: this.entries,
385
- accounting,
386
- ledger: {
387
- entries: this.ledger,
388
- projection_map: this.projectionMap,
389
- root_sha256: rootSha256,
390
- },
391
- };
392
- }
393
- }
394
-
395
- /**
396
- * Projects one user-role message. User text is retained verbatim; images stay
397
- * marker-only so the child never receives raw bytes.
398
- */
399
- function projectUserMessage(
400
- builder: ProjectionBuilder,
401
- content: Message['content'],
402
- sourceOrdinal: number,
403
- ): void {
404
- if (typeof content === 'string') {
405
- if (content.length === 0) {
406
- builder.emptyTextBlocks += 1;
407
- return;
408
- }
409
- builder.addText({
410
- kind: 'text',
411
- source_ordinal: sourceOrdinal,
412
- block_ordinal: 0,
413
- role: 'user',
414
- text: content,
415
- });
416
- return;
417
- }
418
- for (const [blockOrdinal, block] of content.entries()) {
419
- if (block.type === 'text') {
420
- if (block.text.length === 0) {
421
- builder.emptyTextBlocks += 1;
422
- continue;
423
- }
424
- builder.addText({
425
- kind: 'text',
426
- source_ordinal: sourceOrdinal,
427
- block_ordinal: blockOrdinal,
428
- role: 'user',
429
- text: block.text,
430
- });
431
- continue;
432
- }
433
- if (block.type === 'image') {
434
- builder.includedImageMarkers += 1;
435
- builder.addText({
436
- kind: 'text',
437
- source_ordinal: sourceOrdinal,
438
- block_ordinal: blockOrdinal,
439
- role: 'user',
440
- text: imageMarker(block.mimeType),
441
- });
442
- continue;
443
- }
444
- throw unsupportedBlock(`user block ${String(Reflect.get(block, 'type'))}`);
445
- }
446
- }
447
-
448
- function projectAssistantMessage(
449
- builder: ProjectionBuilder,
450
- content: Extract<Message, { role: 'assistant' }>['content'],
451
- sourceOrdinal: number,
452
- ): void {
453
- for (const [blockOrdinal, block] of content.entries()) {
454
- if (block.type === 'text') {
455
- if (block.text.length === 0) {
456
- builder.emptyTextBlocks += 1;
457
- continue;
458
- }
459
- builder.addText({
460
- kind: 'text',
461
- source_ordinal: sourceOrdinal,
462
- block_ordinal: blockOrdinal,
463
- role: 'assistant',
464
- text: block.text,
465
- });
466
- continue;
467
- }
468
- if (block.type === 'thinking') {
469
- builder.addOmission(
470
- sourceOrdinal,
471
- blockOrdinal,
472
- 'assistant_thinking',
473
- Buffer.from(block.thinking, 'utf8'),
474
- );
475
- continue;
476
- }
477
- if (block.type === 'toolCall') {
478
- builder.addOmission(
479
- sourceOrdinal,
480
- blockOrdinal,
481
- 'tool_call',
482
- Buffer.from(canonicalJson(block.arguments), 'utf8'),
483
- { toolName: block.name, toolCallId: block.id },
484
- );
485
- continue;
486
- }
487
- throw unsupportedBlock(`assistant block ${String(Reflect.get(block, 'type'))}`);
488
- }
489
- }
490
-
491
- function projectToolResultMessage(
492
- builder: ProjectionBuilder,
493
- message: Extract<Message, { role: 'toolResult' }>,
494
- sourceOrdinal: number,
495
- ): void {
496
- const content = message.content;
497
- if (typeof content === 'string') {
498
- builder.addOmission(sourceOrdinal, 0, 'tool_result_text', Buffer.from(content, 'utf8'), {
499
- toolName: message.toolName,
500
- toolCallId: message.toolCallId,
501
- });
502
- return;
503
- }
504
- for (const [blockOrdinal, block] of content.entries()) {
505
- if (block.type === 'text') {
506
- builder.addOmission(
507
- sourceOrdinal,
508
- blockOrdinal,
509
- 'tool_result_text',
510
- Buffer.from(block.text, 'utf8'),
511
- { toolName: message.toolName, toolCallId: message.toolCallId },
512
- );
513
- continue;
514
- }
515
- if (block.type === 'image') {
516
- builder.addOmission(
517
- sourceOrdinal,
518
- blockOrdinal,
519
- 'tool_result_image',
520
- Buffer.from(block.data, 'utf8'),
521
- {
522
- toolName: message.toolName,
523
- toolCallId: message.toolCallId,
524
- mimeType: block.mimeType,
525
- },
526
- );
527
- continue;
528
- }
529
- throw unsupportedBlock(`tool result block ${String(Reflect.get(block, 'type'))}`);
530
- }
531
- }
532
-
533
- /**
534
- * Deterministic conversation projection. Every retained source block receives
535
- * exactly one disposition: included verbatim, or represented as an omission
536
- * ledger row. Unknown block types fail loudly instead of disappearing.
537
- */
538
- export function projectVisibleConversationV2(
539
- messages: readonly Message[],
540
- ): ProjectedConversationV2 {
541
- const builder = new ProjectionBuilder();
542
- for (const [sourceOrdinal, message] of messages.entries()) {
543
- if (message.role === 'user') projectUserMessage(builder, message.content, sourceOrdinal);
544
- else if (message.role === 'assistant')
545
- projectAssistantMessage(builder, message.content, sourceOrdinal);
546
- else if (message.role === 'toolResult')
547
- projectToolResultMessage(builder, message, sourceOrdinal);
548
- else throw unsupportedBlock(`message role ${String(Reflect.get(message, 'role'))}`);
549
- }
550
- return builder.finish(messages.length);
551
- }