@tea-agent/loop-agent 0.39.3 → 0.40.0-beta.2

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 (223) hide show
  1. package/CHANGELOG.md +135 -92
  2. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  3. package/dist/adapters/context-transfer/pi-session.js +61 -0
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/cli/command-definitions.js +12 -0
  6. package/dist/cli/program.js +23 -1
  7. package/dist/commands/dag-artifact.js +284 -0
  8. package/dist/commands/dag-context.js +184 -0
  9. package/dist/commands/dag-rerun.js +206 -1
  10. package/dist/executors/dag-pi-executor.js +108 -19
  11. package/dist/executors/pi-executor.js +4 -2
  12. package/dist/executors/pi-sdk-executor.js +49 -1
  13. package/dist/executors/pi-writer-tool-policy.js +57 -1
  14. package/dist/executors/shell-executor.js +42 -2
  15. package/dist/executors/workspace-write-snapshot.js +68 -0
  16. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  17. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  18. package/dist/infrastructure/console/context-export-store.js +160 -0
  19. package/dist/infrastructure/console/dir-lock.js +132 -0
  20. package/dist/infrastructure/harness/artifact-store.js +10 -1
  21. package/dist/infrastructure/harness/atomic-write.js +35 -2
  22. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  23. package/dist/shared/context-transfer.js +418 -0
  24. package/dist/shared/operator/capabilities.js +14 -0
  25. package/dist/shared/operator/safe-run-summary.js +1 -0
  26. package/dist/shared/path-safety.js +93 -0
  27. package/dist/shared/preview.js +28 -4
  28. package/dist/task/config-types.js +12 -1
  29. package/dist/task/source-prepare/fragment-inventory.js +1 -14
  30. package/dist/task/task-demand-routing.js +3 -0
  31. package/dist/worker/console/chat/chat-event-store.js +77 -7
  32. package/dist/worker/console/chat/context-panel.js +7 -0
  33. package/dist/worker/console/chat/deferred-turn.js +12 -0
  34. package/dist/worker/console/chat/pi-runtime.js +331 -18
  35. package/dist/worker/console/chat/provider-error.js +98 -0
  36. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  37. package/dist/worker/console/chat/routes.js +389 -56
  38. package/dist/worker/console/chat/session-stats.js +179 -0
  39. package/dist/worker/console/chat/session-store.js +94 -64
  40. package/dist/worker/console/chat/todos.js +115 -0
  41. package/dist/worker/console/chat/tool-preview.js +42 -0
  42. package/dist/worker/console/chat/turn-process.js +20 -55
  43. package/dist/worker/console/chat/user-questions.js +266 -0
  44. package/dist/worker/console/chat/workspace-landing.js +79 -0
  45. package/dist/worker/console/console-update-runtime.js +1 -1
  46. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  47. package/dist/worker/console/dag-confirmation.js +1 -1
  48. package/dist/worker/console/dag-execution-receipt.js +1 -1
  49. package/dist/worker/console/doctor.js +1 -1
  50. package/dist/worker/console/draft-store.js +1 -1
  51. package/dist/worker/console/human-gate-token.js +1 -1
  52. package/dist/worker/console/index.js +3 -3
  53. package/dist/worker/console/interview/assessment.js +1 -1
  54. package/dist/worker/console/interview/session.js +1 -1
  55. package/dist/worker/console/native-directory-picker.js +13 -0
  56. package/dist/worker/console/operation-runner.js +45 -4
  57. package/dist/worker/console/operation-sse.js +1 -1
  58. package/dist/worker/console/operation-wait.js +1 -1
  59. package/dist/worker/console/operator-actions.js +104 -3
  60. package/dist/worker/console/operator-surface-health.js +1 -1
  61. package/dist/worker/console/operator-user-error.js +128 -0
  62. package/dist/worker/console/routes.js +927 -8
  63. package/dist/worker/console/security.js +39 -4
  64. package/dist/worker/console/server.js +532 -187
  65. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CTx1TrNa.js → abnfDiagram-N423BO3Z-CpWvwARc.js} +1 -1
  66. package/dist/worker/console/static/assets/{arc-B4i9cVxX.js → arc-DctXgO-g.js} +1 -1
  67. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BAXE8_Rl.js → architectureDiagram-T3A2C74G-D4mIkoQl.js} +1 -1
  68. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-C5Xh59at.js → blockDiagram-VBNYF7ZC-ClG0--KO.js} +1 -1
  69. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B_asOzAu.js → c4Diagram-5PPSVZJV-CwdLk78T.js} +1 -1
  70. package/dist/worker/console/static/assets/channel-C9VQZ-ru.js +1 -0
  71. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-wRO_iRNc.js → chunk-2GRJ4B5K-DKczcu-O.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-ByOaSh_G.js → chunk-2Q5K7J3B-BV6F-vBI.js} +1 -1
  73. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLzudan.js → chunk-5RXB4S5H-DtojVnvA.js} +1 -1
  74. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-JCNGkXYT.js → chunk-5VM5RSS4-BjDQRGnO.js} +1 -1
  75. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BhrPXXMZ.js → chunk-6Q2QTUOP-I0fV6ngU.js} +1 -1
  76. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-TJ5JuEBT.js → chunk-GF5L2VYU-BM8C-mmC.js} +1 -1
  77. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-VVthn_mD.js → chunk-JWPE2WC7-BEK-a15e.js} +1 -1
  78. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D60-Mny4.js → chunk-KBJHAD2P-CQsg_fSR.js} +1 -1
  79. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-zXv7aVye.js → chunk-RYQCIY6F-B-BcaUm3.js} +1 -1
  80. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-By9VQoi1.js → chunk-XXDRQBXY-D1Bu08fg.js} +1 -1
  81. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BsCkw5MF.js +1 -0
  82. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BsCkw5MF.js +1 -0
  83. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C4zQLe-M.js → cose-bilkent-JH36ORCC-q8nwtxBR.js} +1 -1
  84. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-wh5N2ReN.js → cynefin-VYW2F7L2-CruhpURw.js} +1 -1
  85. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Dlu5wIqs.js → cynefinDiagram-MW4NZA55-CJne6iYF.js} +1 -1
  86. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CrDgthc6.js → dagre-VZM6K2ZE-CbeLvBU5.js} +1 -1
  87. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-zN1clg27.js → diagram-7IWD3JNH-DmCxH1Pe.js} +1 -1
  88. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BN-o7J7-.js → diagram-B4RE2ZJO-DJ5OYhtC.js} +1 -1
  89. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C8SuPt0d.js → diagram-LBJQPF4R-B-UN-hln.js} +1 -1
  90. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BoizuHbZ.js → diagram-Q27KOJAE-CcEro8Ey.js} +1 -1
  91. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BzUzXixG.js → diagram-UB23O5K3-BBBzDden.js} +1 -1
  92. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js → ebnfDiagram-BXEA7PRR-xKEyH-oo.js} +1 -1
  93. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-GALUzRbs.js → erDiagram-JOGREHBK-C7xl63VC.js} +1 -1
  94. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CZJN65Sb.js → flowDiagram-UKHOOZJN-nthQQ9nR.js} +1 -1
  95. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-JLB9OESl.js → ganttDiagram-PKOTCBZU-CWN5mE33.js} +1 -1
  96. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CcvCr4GD.js → gitGraphDiagram-DS77QQ5N-Wn86tQ6N.js} +1 -1
  97. package/dist/worker/console/static/assets/index-D-0NVyMk.css +1 -0
  98. package/dist/worker/console/static/assets/index-gwyOxQiS.js +407 -0
  99. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-HG5z7aVg.js → infoDiagram-6WML65LV-_WQymGtW.js} +1 -1
  100. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BHEu9chR.js → ishikawaDiagram-WSZJBQD7-COBeivBm.js} +1 -1
  101. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Cp_Up8-a.js → journeyDiagram-NVQOT4AX-CwGOJl3Q.js} +1 -1
  102. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DvVeY-em.js → kanban-definition-27J2QSJJ-Ci0CJ5Vi.js} +1 -1
  103. package/dist/worker/console/static/assets/{linear-aGmcjgTn.js → linear-CPjMq6vN.js} +1 -1
  104. package/dist/worker/console/static/assets/{mermaid.core-Biyo-tqA.js → mermaid.core-Z0wGoe0b.js} +5 -5
  105. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DyxY2pIb.js → mindmap-definition-FAOFIHXS-dISked0d.js} +1 -1
  106. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkdGuc50.js → pegDiagram-VL7TDLO6-BJxgof7Y.js} +1 -1
  107. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-NNQI9xVR.js → pieDiagram-7S7Q4E2Y-CIblU8eW.js} +1 -1
  108. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-PAJkuz_G.js → quadrantDiagram-CIZ2JOQS-vwYiqh8E.js} +1 -1
  109. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-ZwrQod0O.js → railroadDiagram-AXF67PYL-_iOsVeFm.js} +1 -1
  110. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D3ZfrzM2.js → requirementDiagram-LRYGKXZP-Dk0MLxO_.js} +1 -1
  111. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DDGFmWk5.js → sankeyDiagram-W5VNT64P-Dx7HtLVW.js} +1 -1
  112. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DO6mmXTA.js → sequenceDiagram-SI44F4Z6-DRuWjlpL.js} +1 -1
  113. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DMbCQiJt.js → sizeCapture-X5ZJPWSS-Bhy9DQAA.js} +1 -1
  114. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Di_YUEfC.js → stateDiagram-OKZ733FA-DAx9twnU.js} +1 -1
  115. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dmdf8cGy.js +1 -0
  116. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Bn2PphNJ.js → swimlanes-SLNWSIFB-okMlvVrM.js} +2 -2
  117. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-W1IcAFff.js +8 -0
  118. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DPh8pRsp.js → timeline-definition-Z64GVDOM-DXqCKe37.js} +1 -1
  119. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-GMfl26pW.js → vennDiagram-T6HMQDX7-CeIUkwBh.js} +1 -1
  120. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DTlmVrmF.js → wardleyDiagram-T6FBY63Y-BP76BKTN.js} +1 -1
  121. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-YGBjKAS5.js → xychartDiagram-ELKLHX3M-BpVI0OpF.js} +1 -1
  122. package/dist/worker/console/static/index.html +7 -2
  123. package/dist/worker/console/static-src/app/console-types.js +29 -4
  124. package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
  125. package/dist/worker/console/static-src/app/useOperatorActions.js +36 -6
  126. package/dist/worker/console/static-src/app/useRecoveryConsole.js +10 -11
  127. package/dist/worker/console/static-src/app/useRunProgress.js +2 -1
  128. package/dist/worker/console/static-src/app/useTaskWizard.js +2 -2
  129. package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
  130. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
  131. package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
  132. package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
  133. package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
  134. package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
  135. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  136. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
  137. package/dist/worker/console/static-src/operator-chat/turn-submission.js +3 -2
  138. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
  139. package/dist/worker/console/static-src/operator-chat/useChatStream.js +159 -44
  140. package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
  141. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
  142. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  143. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
  144. package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
  145. package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
  146. package/dist/worker/console/workspace-context.js +260 -0
  147. package/dist/worker/console/workspace-registry.js +214 -0
  148. package/dist/worker/continuation/worker-continuation.js +278 -0
  149. package/dist/worker/observe/health.js +1 -1
  150. package/dist/worker/observe/node-transparency.js +572 -0
  151. package/dist/worker/observe/routes.js +51 -1
  152. package/dist/worker/observe/server.js +4 -0
  153. package/dist/worker/observe/static/api.js +94 -10
  154. package/dist/worker/observe/static/app.js +18 -70
  155. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  156. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  157. package/dist/worker/observe/static/index.html +1 -1
  158. package/dist/worker/observe/static/inspect-workspace.js +307 -0
  159. package/dist/worker/observe/static/operator-chrome.css +145 -20
  160. package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
  161. package/dist/worker/observe/static/operator-chrome.js +330 -100
  162. package/dist/worker/observe/static/router.d.ts +46 -0
  163. package/dist/worker/observe/static/router.js +25 -1
  164. package/dist/worker/observe/static/state.d.ts +50 -0
  165. package/dist/worker/observe/static/state.js +175 -1
  166. package/dist/worker/observe/static/styles.css +1039 -128
  167. package/dist/worker/observe/static/views/dag-inspector.js +1218 -60
  168. package/dist/worker/observe/static/views/dag.js +2 -1
  169. package/dist/worker/observe/static/views/dashboard.js +6 -4
  170. package/dist/worker/observe/static/views/night.js +2 -2
  171. package/dist/worker/observe/static/views/pool.js +1 -0
  172. package/dist/worker/observe/static/views/run.js +3 -1
  173. package/dist/worker/observe/static/views/session-timeline.js +75 -10
  174. package/dist/worker/observe/static/views/task.js +1 -1
  175. package/dist/worker/outcomes/adapters.js +19 -0
  176. package/dist/worker/outcomes/types.js +1 -0
  177. package/dist/worker/pool/attempt-lease.js +97 -66
  178. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  179. package/dist/worker/pool/reconcile.js +46 -1
  180. package/dist/worker/pool/run-store.js +2 -0
  181. package/dist/worker/pool/state-projection.js +2 -0
  182. package/dist/worker/task-spec/workflow-routing.js +8 -3
  183. package/dist/workflows/dag/artifact-bindings.js +149 -0
  184. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  185. package/dist/workflows/dag/backend-test-pytest-collection.js +11 -10
  186. package/dist/workflows/dag/backend-test-writer-completeness.js +7 -12
  187. package/dist/workflows/dag/context-receipt.js +305 -0
  188. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  189. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  190. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  191. package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
  192. package/dist/workflows/dag/frontend-implementation-contract.js +1 -89
  193. package/dist/workflows/dag/init-hybrid.js +259 -47
  194. package/dist/workflows/dag/interrupt-request.js +3 -3
  195. package/dist/workflows/dag/lifecycle.js +3 -3
  196. package/dist/workflows/dag/node-execution.js +112 -27
  197. package/dist/workflows/dag/path-safety.js +1 -0
  198. package/dist/workflows/dag/prompt.js +29 -4
  199. package/dist/workflows/dag/rerun-plan.js +272 -0
  200. package/dist/workflows/dag/rerun-run.js +390 -12
  201. package/dist/workflows/dag/skill-snapshot.js +1 -0
  202. package/dist/workflows/dag/types.js +203 -8
  203. package/docs/README.md +1 -0
  204. package/docs/architecture/runtime-boundaries.md +6 -6
  205. package/docs/architecture/worker-and-feature.md +1 -1
  206. package/docs/operations/local-development-environment.md +21 -0
  207. package/docs/templates/agent-dag.schema.json +50 -0
  208. package/docs/templates/backend-test-dag.json +265 -237
  209. package/docs/templates/product-line/task.yaml +1 -1
  210. package/harness.json +3 -3
  211. package/package.json +5 -1
  212. package/skills/loop-agent/references/command-reference.md +6 -0
  213. package/dist/worker/console/static/assets/channel-Dj_4Moei.js +0 -1
  214. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +0 -1
  215. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +0 -1
  216. package/dist/worker/console/static/assets/index-CW88Fjbw.js +0 -406
  217. package/dist/worker/console/static/assets/index-DP0ZJl39.css +0 -1
  218. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +0 -1
  219. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +0 -8
  220. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  221. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  222. /package/dist/{worker → infrastructure}/console/operation-store.js +0 -0
  223. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -19,8 +19,8 @@
19
19
  import { createHash } from "node:crypto";
20
20
  import { existsSync, readFileSync, rmSync } from "node:fs";
21
21
  import path from "node:path";
22
- import { ensureSecureDir, writeSecureJsonSync, } from "../app-data.js";
23
- import { isTerminalOperationState, } from "../operation-store.js";
22
+ import { ensureSecureDir, writeSecureJsonSync, } from "../../../infrastructure/console/app-data.js";
23
+ import { isTerminalOperationState, } from "../../../infrastructure/console/operation-store.js";
24
24
  import { scrubSecrets } from "./explore-tools.js";
25
25
  const OPERATION_PREVIEW_MAX_CHARS = 1200;
26
26
  function boundedRedactedText(value, maxChars = OPERATION_PREVIEW_MAX_CHARS) {
@@ -231,6 +231,10 @@ export function projectCompactSnapshot(value) {
231
231
  Number.isFinite(record.tokensBefore)
232
232
  ? Math.max(0, Math.floor(record.tokensBefore))
233
233
  : undefined;
234
+ const estimatedTokensAfter = typeof record.estimatedTokensAfter === "number" &&
235
+ Number.isFinite(record.estimatedTokensAfter)
236
+ ? Math.max(0, Math.floor(record.estimatedTokensAfter))
237
+ : undefined;
234
238
  const details = record.details === undefined
235
239
  ? undefined
236
240
  : boundedRedactedJson(record.details).preview;
@@ -239,9 +243,16 @@ export function projectCompactSnapshot(value) {
239
243
  summary,
240
244
  ...(firstKeptEntryId ? { firstKeptEntryId } : {}),
241
245
  ...(tokensBefore !== undefined ? { tokensBefore } : {}),
246
+ ...(estimatedTokensAfter !== undefined ? { estimatedTokensAfter } : {}),
242
247
  ...(details ? { details } : {}),
243
248
  previewHash: createHash("sha256")
244
- .update(JSON.stringify({ summary, firstKeptEntryId, tokensBefore, details }))
249
+ .update(JSON.stringify({
250
+ summary,
251
+ firstKeptEntryId,
252
+ tokensBefore,
253
+ estimatedTokensAfter,
254
+ details,
255
+ }))
245
256
  .digest("hex"),
246
257
  };
247
258
  }
@@ -433,6 +444,20 @@ function safeSessionId(sessionId) {
433
444
  * Link durable Chat refs to the existing canonical operation store/event ring.
434
445
  * This owns no operation state and schedules no work; it only projects facts.
435
446
  */
447
+ /** R8 AC-R8-001: operationIds whose operation-ref has been overshadowed by
448
+ * a later operation-unlinked tombstone in the same ring. A tombstoned
449
+ * association must never be restored by recoverSession / replay / fork. */
450
+ function tombstonedOperationIds(events) {
451
+ const tombstones = new Set();
452
+ for (const event of events) {
453
+ if (event.kind !== "operation-unlinked")
454
+ continue;
455
+ const operationId = event.data.operationId;
456
+ if (typeof operationId === "string" && operationId)
457
+ tombstones.add(operationId);
458
+ }
459
+ return tombstones;
460
+ }
436
461
  export function createChatOperationLinker(options) {
437
462
  const followers = new Map();
438
463
  const keyFor = (sessionId, operationId) => `${sessionId}\u0000${operationId}`;
@@ -535,14 +560,37 @@ export function createChatOperationLinker(options) {
535
560
  }
536
561
  return {
537
562
  link,
563
+ async unlink(input) {
564
+ // Idempotent removal (AC-R7-001): drop the live follower and the
565
+ // persisted operation-ref event so the association is truly gone.
566
+ const key = keyFor(input.sessionId, input.operationId);
567
+ followers.get(key)?.();
568
+ followers.delete(key);
569
+ const ring = options.chatEvents.snapshot(input.sessionId);
570
+ const refs = ring.filter((event) => event.kind === "operation-ref" &&
571
+ event.data.operationId ===
572
+ input.operationId);
573
+ for (const event of refs) {
574
+ options.chatEvents.append(input.sessionId, event.turnId, {
575
+ kind: "operation-unlinked",
576
+ data: { operationId: input.operationId, unlinkedAt: new Date().toISOString() },
577
+ });
578
+ }
579
+ return refs.length > 0;
580
+ },
538
581
  async recoverSession(sessionId) {
539
- const refs = options.chatEvents
540
- .snapshot(sessionId)
541
- .filter((event) => event.kind === "operation-ref");
582
+ const snapshot = options.chatEvents.snapshot(sessionId);
583
+ // R8 AC-R8-001: a tombstoned (unlinked) operationId must NOT be
584
+ // re-linked on recovery — the persisted operation-unlinked event
585
+ // overshadows any earlier operation-ref in the same ring.
586
+ const tombstones = tombstonedOperationIds(snapshot);
587
+ const refs = snapshot.filter((event) => event.kind === "operation-ref");
542
588
  for (const event of refs) {
543
589
  const data = event.data;
544
590
  if (!data.operationId || !data.toolCallId)
545
591
  continue;
592
+ if (tombstones.has(data.operationId))
593
+ continue;
546
594
  await link({
547
595
  sessionId,
548
596
  turnId: event.turnId,
@@ -566,6 +614,19 @@ export function createChatOperationLinker(options) {
566
614
  },
567
615
  };
568
616
  }
617
+ /** R8 AC-R8-001 (fork): same tombstone scan as the linker — kept local to
618
+ * the store so forkContextRefs needs no linker dependency. */
619
+ function forkTombstonedOperationIds(events) {
620
+ const tombstones = new Set();
621
+ for (const event of events) {
622
+ if (event.kind !== "operation-unlinked")
623
+ continue;
624
+ const operationId = event.data.operationId;
625
+ if (typeof operationId === "string" && operationId)
626
+ tombstones.add(operationId);
627
+ }
628
+ return tombstones;
629
+ }
569
630
  export function createChatEventStore(options) {
570
631
  const maxEvents = options?.maxEventsPerSession ?? DEFAULT_MAX_EVENTS;
571
632
  const maxBytes = options?.maxBytesPerSession ?? DEFAULT_MAX_BYTES;
@@ -735,13 +796,22 @@ export function createChatEventStore(options) {
735
796
  "assessment",
736
797
  "taskKind-confirmed",
737
798
  "human-gate-card",
799
+ "compaction_end",
738
800
  "compact",
739
801
  "artifact-ref",
740
802
  ]);
741
803
  let copiedCount = 0;
742
- for (const event of this.snapshot(input.sourceSessionId)) {
804
+ const source = this.snapshot(input.sourceSessionId);
805
+ // R8 AC-R8-001: fork excludes tombstoned associations — an unlinked
806
+ // operationId must not leak its operation-ref into a forked session.
807
+ const tombstones = forkTombstonedOperationIds(source);
808
+ for (const event of source) {
743
809
  if (!allowed.has(event.kind))
744
810
  continue;
811
+ if ((event.kind === "operation-ref" ||
812
+ event.kind === "operation-status") &&
813
+ tombstones.has(event.data.operationId ?? ""))
814
+ continue;
745
815
  const data = structuredClone(event.data);
746
816
  if (event.kind === "human-gate-card") {
747
817
  delete data.humanGateToken;
@@ -38,6 +38,13 @@ export function projectTaskContext(input) {
38
38
  const previous = gates.get(card.cardId);
39
39
  gates.set(card.cardId, safeGate({ ...card, ...(previous?.dagSpine && !card.dagSpine ? { dagSpine: previous.dagSpine } : {}) }));
40
40
  }
41
+ else if (event.kind === "operation-unlinked") {
42
+ // R8 AC-001/AC-R8-001: tombstone — an operation-unlinked event appended
43
+ // after its operation-ref removes the card from the projection for good.
44
+ const operationId = typeof data.operationId === "string" ? data.operationId : "";
45
+ if (operationId)
46
+ view.operations = view.operations.filter((operation) => operation.operationId !== operationId);
47
+ }
41
48
  else if (event.kind === "operation-ref" || event.kind === "operation-status") {
42
49
  const operation = operationFromEventPayload(data);
43
50
  if (operation && (!activeTaskId || !operation.taskId || operation.taskId === activeTaskId))
@@ -81,11 +81,23 @@ export function projectDeferredTurnSummary(deferredTurn) {
81
81
  blockedByTurnId: deferredTurn.blockedByTurnId,
82
82
  state: deferredTurn.state,
83
83
  updatedAt: deferredTurn.updatedAt,
84
+ text: deferredTurn.text,
84
85
  textPreview: deferredTurn.text.replace(/\s+/g, " ").trim().slice(0, 120),
85
86
  imageCount: deferredTurn.images.length,
87
+ images: structuredClone(deferredTurn.images),
86
88
  ...(deferredTurn.acceptedTurnId
87
89
  ? { acceptedTurnId: deferredTurn.acceptedTurnId }
88
90
  : {}),
89
91
  ...(deferredTurn.lastError ? { lastError: deferredTurn.lastError } : {}),
90
92
  };
91
93
  }
94
+ export function parseDeferredTurns(value) {
95
+ if (!Array.isArray(value))
96
+ return [];
97
+ return value
98
+ .map((item) => parseDeferredTurn(item))
99
+ .filter((item) => item !== undefined);
100
+ }
101
+ export function projectDeferredTurnSummaries(deferredTurns) {
102
+ return deferredTurns.map((item) => projectDeferredTurnSummary(item));
103
+ }