@earendil-works/pi-durable 1.3.0 → 1.5.0

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 (257) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +287 -82
  3. package/dist/codemode/codemode.d.ts +4 -4
  4. package/dist/codemode/codemode.d.ts.map +1 -1
  5. package/dist/codemode/codemode.js +21 -16
  6. package/dist/codemode/codemode.js.map +1 -1
  7. package/dist/codemode/models.d.ts.map +1 -1
  8. package/dist/codemode/models.js +6 -4
  9. package/dist/codemode/models.js.map +1 -1
  10. package/dist/documents.d.ts +1 -1
  11. package/dist/documents.d.ts.map +1 -1
  12. package/dist/documents.js.map +1 -1
  13. package/dist/env/index.d.ts +12 -10
  14. package/dist/env/index.d.ts.map +1 -1
  15. package/dist/env/index.js.map +1 -1
  16. package/dist/env/node-watch.d.ts +3 -3
  17. package/dist/env/node-watch.d.ts.map +1 -1
  18. package/dist/env/node-watch.js +18 -16
  19. package/dist/env/node-watch.js.map +1 -1
  20. package/dist/env/node.d.ts +2 -2
  21. package/dist/env/node.d.ts.map +1 -1
  22. package/dist/env/node.js +12 -4
  23. package/dist/env/node.js.map +1 -1
  24. package/dist/errors.d.ts +18 -2
  25. package/dist/errors.d.ts.map +1 -1
  26. package/dist/errors.js +26 -3
  27. package/dist/errors.js.map +1 -1
  28. package/dist/harness/agent.d.ts +13 -2
  29. package/dist/harness/agent.d.ts.map +1 -1
  30. package/dist/harness/agent.js +48 -6
  31. package/dist/harness/agent.js.map +1 -1
  32. package/dist/harness/compaction.d.ts +3 -1
  33. package/dist/harness/compaction.d.ts.map +1 -1
  34. package/dist/harness/compaction.js +38 -6
  35. package/dist/harness/compaction.js.map +1 -1
  36. package/dist/harness/context.d.ts +23 -7
  37. package/dist/harness/context.d.ts.map +1 -1
  38. package/dist/harness/context.js +71 -25
  39. package/dist/harness/context.js.map +1 -1
  40. package/dist/harness/define.d.ts +7 -3
  41. package/dist/harness/define.d.ts.map +1 -1
  42. package/dist/harness/define.js +4 -0
  43. package/dist/harness/define.js.map +1 -1
  44. package/dist/harness/generation.d.ts +38 -5
  45. package/dist/harness/generation.d.ts.map +1 -1
  46. package/dist/harness/generation.js +497 -108
  47. package/dist/harness/generation.js.map +1 -1
  48. package/dist/harness/harness.d.ts +7 -2
  49. package/dist/harness/harness.d.ts.map +1 -1
  50. package/dist/harness/harness.js +147 -57
  51. package/dist/harness/harness.js.map +1 -1
  52. package/dist/harness/inbox.d.ts +7 -3
  53. package/dist/harness/inbox.d.ts.map +1 -1
  54. package/dist/harness/inbox.js +7 -4
  55. package/dist/harness/inbox.js.map +1 -1
  56. package/dist/harness/json.d.ts +20 -4
  57. package/dist/harness/json.d.ts.map +1 -1
  58. package/dist/harness/json.js +23 -5
  59. package/dist/harness/json.js.map +1 -1
  60. package/dist/harness/live.d.ts +122 -16
  61. package/dist/harness/live.d.ts.map +1 -1
  62. package/dist/harness/live.js +93 -12
  63. package/dist/harness/live.js.map +1 -1
  64. package/dist/harness/partial-changes.d.ts +54 -0
  65. package/dist/harness/partial-changes.d.ts.map +1 -0
  66. package/dist/harness/partial-changes.js +162 -0
  67. package/dist/harness/partial-changes.js.map +1 -0
  68. package/dist/harness/prompt.d.ts +1 -1
  69. package/dist/harness/prompt.d.ts.map +1 -1
  70. package/dist/harness/prompt.js +1 -1
  71. package/dist/harness/prompt.js.map +1 -1
  72. package/dist/harness/registry-reader.d.ts +11 -0
  73. package/dist/harness/registry-reader.d.ts.map +1 -0
  74. package/dist/harness/registry-reader.js +25 -0
  75. package/dist/harness/registry-reader.js.map +1 -0
  76. package/dist/harness/scheduler.d.ts +86 -14
  77. package/dist/harness/scheduler.d.ts.map +1 -1
  78. package/dist/harness/scheduler.js +1459 -369
  79. package/dist/harness/scheduler.js.map +1 -1
  80. package/dist/harness/submissions.d.ts +23 -11
  81. package/dist/harness/submissions.d.ts.map +1 -1
  82. package/dist/harness/submissions.js +104 -36
  83. package/dist/harness/submissions.js.map +1 -1
  84. package/dist/harness/task-graph.d.ts +28 -8
  85. package/dist/harness/task-graph.d.ts.map +1 -1
  86. package/dist/harness/task-graph.js +85 -36
  87. package/dist/harness/task-graph.js.map +1 -1
  88. package/dist/harness/telemetry.d.ts +307 -0
  89. package/dist/harness/telemetry.d.ts.map +1 -0
  90. package/dist/harness/telemetry.js +136 -0
  91. package/dist/harness/telemetry.js.map +1 -0
  92. package/dist/harness/tool.d.ts +2 -0
  93. package/dist/harness/tool.d.ts.map +1 -1
  94. package/dist/harness/tool.js +223 -61
  95. package/dist/harness/tool.js.map +1 -1
  96. package/dist/harness/transcript.d.ts +72 -0
  97. package/dist/harness/transcript.d.ts.map +1 -0
  98. package/dist/harness/transcript.js +264 -0
  99. package/dist/harness/transcript.js.map +1 -0
  100. package/dist/harness/types.d.ts +302 -48
  101. package/dist/harness/types.d.ts.map +1 -1
  102. package/dist/harness/types.js.map +1 -1
  103. package/dist/harness/util.d.ts +7 -1
  104. package/dist/harness/util.d.ts.map +1 -1
  105. package/dist/harness/util.js +13 -0
  106. package/dist/harness/util.js.map +1 -1
  107. package/dist/harness/view.d.ts +43 -22
  108. package/dist/harness/view.d.ts.map +1 -1
  109. package/dist/harness/view.js +130 -71
  110. package/dist/harness/view.js.map +1 -1
  111. package/dist/index.d.ts +13 -9
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +7 -5
  114. package/dist/index.js.map +1 -1
  115. package/dist/search.d.ts +19 -3
  116. package/dist/search.d.ts.map +1 -1
  117. package/dist/search.js +107 -44
  118. package/dist/search.js.map +1 -1
  119. package/dist/session/cached-storage.d.ts +71 -0
  120. package/dist/session/cached-storage.d.ts.map +1 -0
  121. package/dist/session/cached-storage.js +269 -0
  122. package/dist/session/cached-storage.js.map +1 -0
  123. package/dist/session/conversation-tails.d.ts +33 -0
  124. package/dist/session/conversation-tails.d.ts.map +1 -0
  125. package/dist/session/conversation-tails.js +101 -0
  126. package/dist/session/conversation-tails.js.map +1 -0
  127. package/dist/session/keys.d.ts +68 -0
  128. package/dist/session/keys.d.ts.map +1 -0
  129. package/dist/session/keys.js +161 -0
  130. package/dist/session/keys.js.map +1 -0
  131. package/dist/session/session.d.ts +181 -29
  132. package/dist/session/session.d.ts.map +1 -1
  133. package/dist/session/session.js +1073 -183
  134. package/dist/session/session.js.map +1 -1
  135. package/dist/session/transaction.d.ts +86 -4
  136. package/dist/session/transaction.d.ts.map +1 -1
  137. package/dist/session/transaction.js +250 -38
  138. package/dist/session/transaction.js.map +1 -1
  139. package/dist/session/writer.d.ts +74 -0
  140. package/dist/session/writer.d.ts.map +1 -0
  141. package/dist/session/writer.js +294 -0
  142. package/dist/session/writer.js.map +1 -0
  143. package/dist/storage/indexeddb.d.ts +68 -0
  144. package/dist/storage/indexeddb.d.ts.map +1 -0
  145. package/dist/storage/indexeddb.js +881 -0
  146. package/dist/storage/indexeddb.js.map +1 -0
  147. package/dist/storage/jsonl/storage.d.ts +30 -2
  148. package/dist/storage/jsonl/storage.d.ts.map +1 -1
  149. package/dist/storage/jsonl/storage.js +88 -24
  150. package/dist/storage/jsonl/storage.js.map +1 -1
  151. package/dist/storage/memory.d.ts +7 -2
  152. package/dist/storage/memory.d.ts.map +1 -1
  153. package/dist/storage/memory.js +29 -3
  154. package/dist/storage/memory.js.map +1 -1
  155. package/dist/storage/sqlite/cloudflare.d.ts +2 -51
  156. package/dist/storage/sqlite/cloudflare.d.ts.map +1 -1
  157. package/dist/storage/sqlite/cloudflare.js +2 -133
  158. package/dist/storage/sqlite/cloudflare.js.map +1 -1
  159. package/dist/storage/sqlite/database.d.ts +1 -36
  160. package/dist/storage/sqlite/database.d.ts.map +1 -1
  161. package/dist/storage/sqlite/database.js.map +1 -1
  162. package/dist/storage/sqlite/index.d.ts +1 -1
  163. package/dist/storage/sqlite/index.d.ts.map +1 -1
  164. package/dist/storage/sqlite/index.js.map +1 -1
  165. package/dist/storage/sqlite/migrations.d.ts +2 -5
  166. package/dist/storage/sqlite/migrations.d.ts.map +1 -1
  167. package/dist/storage/sqlite/migrations.js +28 -28
  168. package/dist/storage/sqlite/migrations.js.map +1 -1
  169. package/dist/storage/sqlite/node.d.ts +2 -37
  170. package/dist/storage/sqlite/node.d.ts.map +1 -1
  171. package/dist/storage/sqlite/node.js +3 -174
  172. package/dist/storage/sqlite/node.js.map +1 -1
  173. package/dist/storage/sqlite/schema.d.ts +1 -3
  174. package/dist/storage/sqlite/schema.d.ts.map +1 -1
  175. package/dist/storage/sqlite/schema.js +2 -9
  176. package/dist/storage/sqlite/schema.js.map +1 -1
  177. package/dist/storage/sqlite/storage.d.ts +15 -3
  178. package/dist/storage/sqlite/storage.d.ts.map +1 -1
  179. package/dist/storage/sqlite/storage.js +110 -36
  180. package/dist/storage/sqlite/storage.js.map +1 -1
  181. package/dist/storage/store/commit.d.ts +33 -0
  182. package/dist/storage/store/commit.d.ts.map +1 -0
  183. package/dist/storage/store/commit.js +286 -0
  184. package/dist/storage/store/commit.js.map +1 -0
  185. package/dist/storage/store/documents.d.ts +43 -0
  186. package/dist/storage/store/documents.d.ts.map +1 -0
  187. package/dist/storage/store/documents.js +102 -0
  188. package/dist/storage/store/documents.js.map +1 -0
  189. package/dist/storage/store/index.d.ts +2 -0
  190. package/dist/storage/store/index.d.ts.map +1 -0
  191. package/dist/storage/store/index.js +2 -0
  192. package/dist/storage/store/index.js.map +1 -0
  193. package/dist/storage/store/queries.d.ts +49 -0
  194. package/dist/storage/store/queries.d.ts.map +1 -0
  195. package/dist/storage/store/queries.js +93 -0
  196. package/dist/storage/store/queries.js.map +1 -0
  197. package/dist/storage/store/storage.d.ts +59 -0
  198. package/dist/storage/store/storage.d.ts.map +1 -0
  199. package/dist/storage/store/storage.js +438 -0
  200. package/dist/storage/store/storage.js.map +1 -0
  201. package/dist/storage/store/tables.d.ts +54 -0
  202. package/dist/storage/store/tables.d.ts.map +1 -0
  203. package/dist/storage/store/tables.js +108 -0
  204. package/dist/storage/store/tables.js.map +1 -0
  205. package/dist/testing/commit-writes.d.ts +5 -0
  206. package/dist/testing/commit-writes.d.ts.map +1 -0
  207. package/dist/testing/commit-writes.js +12 -0
  208. package/dist/testing/commit-writes.js.map +1 -0
  209. package/dist/testing/env-conformance.js +7 -7
  210. package/dist/testing/env-conformance.js.map +1 -1
  211. package/dist/testing/index.d.ts +1 -0
  212. package/dist/testing/index.d.ts.map +1 -1
  213. package/dist/testing/index.js +1 -0
  214. package/dist/testing/index.js.map +1 -1
  215. package/dist/testing/storage-benchmark.d.ts.map +1 -1
  216. package/dist/testing/storage-benchmark.js +18 -17
  217. package/dist/testing/storage-benchmark.js.map +1 -1
  218. package/dist/testing/storage-conformance.d.ts.map +1 -1
  219. package/dist/testing/storage-conformance.js +369 -67
  220. package/dist/testing/storage-conformance.js.map +1 -1
  221. package/dist/timers.d.ts +21 -0
  222. package/dist/timers.d.ts.map +1 -0
  223. package/dist/timers.js +72 -0
  224. package/dist/timers.js.map +1 -0
  225. package/dist/tool-search/tool-search.d.ts +6 -12
  226. package/dist/tool-search/tool-search.d.ts.map +1 -1
  227. package/dist/tool-search/tool-search.js +7 -7
  228. package/dist/tool-search/tool-search.js.map +1 -1
  229. package/dist/tools/bash.d.ts +12 -12
  230. package/dist/tools/bash.d.ts.map +1 -1
  231. package/dist/tools/bash.js +16 -15
  232. package/dist/tools/bash.js.map +1 -1
  233. package/dist/tools/edit.d.ts +7 -7
  234. package/dist/tools/edit.d.ts.map +1 -1
  235. package/dist/tools/edit.js +9 -9
  236. package/dist/tools/edit.js.map +1 -1
  237. package/dist/tools/index.d.ts +32 -32
  238. package/dist/tools/read.d.ts +6 -6
  239. package/dist/tools/read.d.ts.map +1 -1
  240. package/dist/tools/read.js +19 -8
  241. package/dist/tools/read.js.map +1 -1
  242. package/dist/tools/write.d.ts +4 -4
  243. package/dist/tools/write.d.ts.map +1 -1
  244. package/dist/tools/write.js +5 -5
  245. package/dist/tools/write.js.map +1 -1
  246. package/dist/types.d.ts +331 -64
  247. package/dist/types.d.ts.map +1 -1
  248. package/dist/types.js.map +1 -1
  249. package/package.json +27 -10
  250. package/dist/harness/events.d.ts +0 -186
  251. package/dist/harness/events.d.ts.map +0 -1
  252. package/dist/harness/events.js +0 -373
  253. package/dist/harness/events.js.map +0 -1
  254. package/dist/session/observation.d.ts +0 -42
  255. package/dist/session/observation.d.ts.map +0 -1
  256. package/dist/session/observation.js +0 -281
  257. package/dist/session/observation.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/harness/live.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAyGjD,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAY;IAC3C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACnB,kHAAkH;IAClH,+GAA+G;IAC/G,iHAAiH;IACjH,mHAAmH;IACnH,uEAAuE;IACvE,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CACzB,KAAK,CAAC,UAAU,KAAK,SAAS;QAC9B,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;QAC9D,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;CACrE,CAAC,CAAC;AAEH,sDAAsD;AACtD,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AACvE,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,EAAM,EAAE,IAAsB,EAAE,MAAc,EAAE,UAAgC;IACtG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACvB,OAAO,IAAI,CAAC,KAAK,CAAC;IAClB,OAAO,IAAI,CAAC,WAAW,CAAC;AACzB,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CAAC,IAAsB,EAAE,MAAwB;IACnF,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,IAAsB,EAAE,MAAc;IACtE,OAAO,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CAAC,IAAsB,EAAE,MAAc;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC;IAClC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO;IACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACvE,IAAI,KAAK,IAAI,CAAC;QAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,WAAW,CAAC;AACpD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,oBAAoB,CAAC,IAAsB,EAAE,MAAc,EAAE,KAAa;IACzF,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,IAAI,CAAC,iBAAiB,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3G,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAsB,EAAE,MAAc;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAChE,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC;IAChC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAE,CAAC;QAClC,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO,SAAS,CAAC;QAClD,IAAI,SAAS,CAAC,MAAM,GAAG,MAAM;YAAE,GAAG,GAAG,MAAM,GAAG,CAAC,CAAC;;YAC3C,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAsC;IAChE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACrB,aAAa,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,iBAAiB,CAAC,IAAsB,EAAE,MAAc;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC;IAChC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO;IACjC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxF,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACzD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAE,CAAC,YAAY,CAAC;YAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,WAAW,CAAC;AAClD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,IAAyB;IACtD,OAAO,IAAI,CAAC,MAAM,CAAC;IACnB,OAAO,IAAI,CAAC,YAAY,CAAC;IACzB,OAAO,IAAI,CAAC,YAAY,CAAC;IACzB,OAAO,IAAI,CAAC,OAAO,CAAC;IACpB,OAAO,IAAI,CAAC,WAAW,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAe,EACf,MAAmD,EACnD,OAAyB;IAEzB,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,IAAI,KAAK,SAAS;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACzC,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO;IACR,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACpF,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACrF,OAAO;IACR,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO;IAC7C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1D,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,EAAE;QAAE,OAAO;IAC3C,MAAM,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IACtD,MAAM,CACL,EAAE,EACF,IAAI,EACJ,MAAM,CAAC,EAAE,EACT,OAAO,CAAC,MAAM,KAAK,SAAS;QAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE;QAC5E,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CACnD,CAAC;AACH,CAAC","sourcesContent":["import type { Draft, JsonRepresentation, JsonValue } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Usage } from \"@earendil-works/pi-ai\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { Transaction } from \"../session/transaction.ts\";\nimport type { EntryId, JsonObject, SubmissionId, SubmissionSettlement, TaskId, TaskRecord, Tx } from \"../types.ts\";\nimport { convertPartial } from \"./generation.ts\";\nimport type { SchedulerOutcome } from \"./scheduler.ts\";\nimport type { CompactionReason, CompactionResult, ToolDiagnostic } from \"./types.ts\";\n\n/** What a running tool call publishes, shared by model-issued and nested calls. */\ntype SlotProgress = {\n\tcallId: string;\n\tname: string;\n\tstatus: \"pending\" | \"running\" | \"done\";\n\t/** Retained running output and what the bounds dropped. */\n\toutput?: string;\n\tdroppedBytes?: number;\n\tdroppedLines?: number;\n\t/** Last `details()` value. */\n\tdetails?: JsonValue;\n\t/** Diagnostics recorded through `api.diagnostic()`. */\n\tdiagnostics?: ToolDiagnostic[];\n};\n\n/** Presentation of one tool call of the current round. */\nexport type ToolSlot = SlotProgress & {\n\t/**\n\t * Absent for a call not started yet (sequential round) and for a call its request did not offer, which starts `done`\n\t * with the `entry` generation wrote.\n\t */\n\ttaskId?: TaskId;\n\t/** Result entry once done; absent when the tool task faulted or was orphaned. */\n\tentry?: EntryId;\n};\n\n/**\n * Presentation of one nested call: a call a running tool made through `executeTool()`. `callId` is\n * `<parentCallId>/<key>`, the call's ID in tool events and unique because keys contain no `/`. The Harness relates\n * slots by task ID: `taskId` is the call's tool task, `parentTaskId` the calling one, model-issued or nested.\n */\nexport type NestedToolSlot = SlotProgress & {\n\tparentCallId: string;\n\tparentTaskId: TaskId;\n\ttaskId: TaskId;\n\t/**\n\t * What the call was made with, replaced by the arguments it runs with, after repair, `beforeTool`, and coercion,\n\t * once it starts executing. A model-issued call's arguments are in its assistant entry instead.\n\t */\n\targuments: JsonObject;\n\t/** Once done: how the call ended, for a status line. The result itself is in the caller's `NestedCallDoc`. */\n\tsummary?: NestedToolSummary;\n};\n\n/** How a nested call ended: an error flag, its execution time, its spend, and up to 500 characters of error text. */\nexport type NestedToolSummary = {\n\tisError: boolean;\n\tdurationMs?: number;\n\tusage?: JsonRepresentation<Usage>;\n\terror?: string;\n};\n\n/** Presentation of one live compaction task (spec §8.7). */\nexport type CompactionStatus = {\n\ttaskId: TaskId<CompactionResult>;\n\treason: CompactionReason;\n\t/** Whether a generation waits for it: a compaction the generation owns. */\n\tblocking: boolean;\n\tattempt: number;\n\t/** Durable backoff before the next summarization attempt. */\n\tretry?: { at: number; error: string };\n};\n\n/** The newest compaction that failed since a compaction last produced a summary (spec §8.7). */\nexport type CompactionFailure = {\n\ttaskId: TaskId<CompactionResult>;\n\treason: CompactionReason;\n\t/** The failed or faulted outcome's error message, or why the scheduler orphaned the task. */\n\terror: string;\n};\n\n/** Built-in live conversation state: run control and presentation of the current generation and tool round. */\nexport type LiveState = {\n\t/** Run control: the task that settles the run's inputs, and those inputs; present exactly while busy. */\n\trun?: { taskId: TaskId; inputs: SubmissionId[] };\n\t/** Presentation of the current generation attempt. */\n\tgeneration?: {\n\t\tattempt: number;\n\t\t/** Committed throttled partial of the in-flight response. */\n\t\tmessage?: JsonRepresentation<AssistantMessage>;\n\t\t/** Durable backoff before the next attempt. */\n\t\tretry?: { at: number; error: string };\n\t\t/** Provider-side deferred response being polled. */\n\t\tdeferred?: { pollAt: number };\n\t};\n\t/** The current tool round in call order, from the tool-calling answer until the generation's `tools` phase ends it. */\n\ttools?: ToolSlot[];\n\t/**\n\t * Nested calls of running tool calls, in creation order, so a parent precedes its children and task IDs ascend. A\n\t * call's nested slots are removed when the call settles.\n\t */\n\tnestedTools?: NestedToolSlot[];\n\t/** Live compaction tasks in task ID order; absent when none. */\n\tcompactions?: CompactionStatus[];\n\t/**\n\t * The newest compaction failure, so a client can show it after the task ended. A compaction that produces a summary\n\t * removes it; one that is aborted or finds nothing to compact leaves it.\n\t */\n\tcompactionFailure?: CompactionFailure;\n};\n\nexport const LiveDoc = defineDoc<LiveState>({\n\tkind: \"pi.live\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"latest\",\n\tfork: \"initial\",\n\tinitial: () => ({}),\n\t// REMINDER: a complete base whenever nothing runs (spec §8.2): no generation and no running tool slot. That holds\n\t// while idle, in the commit handing a generation over to its tool round, and between tools, so the delta chain\n\t// spans at most one generation or the overlapping execution of one round's tools. A slot holds output only while\n\t// running, so every base is small; a nested call's result lives in its caller's task documents, never here. Do not\n\t// add a delta-count bound; the tool output benchmark checks this rule.\n\tcheckpointWhen: (value) =>\n\t\tvalue.generation === undefined &&\n\t\t!(value.tools ?? []).some((slot) => slot.status === \"running\") &&\n\t\t!(value.nestedTools ?? []).some((slot) => slot.status === \"running\"),\n});\n\n/** Built-in task kinds that can own `pi.live.run`. */\nconst RUN_TASK_KINDS: ReadonlySet<string> = new Set([\"pi.generation\"]);\nconst TOOL_TASK_KIND = \"pi.tool\";\nconst COMPACTION_TASK_KIND = \"pi.compaction\";\n\n/**\n * End the run owned by `taskId`: settle each of its inputs and remove `run`. Always removes `generation`, `tools`, and\n * `nestedTools`, whose presentation belongs to the ending run.\n */\nexport function endRun(tx: Tx, live: Draft<LiveState>, taskId: TaskId, settlement: SubmissionSettlement): void {\n\tif (live.run?.taskId === taskId) {\n\t\tfor (const id of live.run.inputs) tx.settleSubmission(id, settlement);\n\t\tdelete live.run;\n\t}\n\tdelete live.generation;\n\tdelete live.tools;\n\tdelete live.nestedTools;\n}\n\n/** Add the status of a compaction task created in this commit; statuses stay in task ID order. */\nexport function addCompactionStatus(live: Draft<LiveState>, status: CompactionStatus): void {\n\tlive.compactions ??= [];\n\tlive.compactions.push(status);\n}\n\n/** The status of compaction task `taskId`, if listed. */\nexport function compactionStatus(live: Draft<LiveState>, taskId: TaskId): Draft<CompactionStatus> | undefined {\n\treturn live.compactions?.find((status) => status.taskId === taskId);\n}\n\n/** Remove the status of compaction task `taskId`, and the list once empty. */\nexport function removeCompactionStatus(live: Draft<LiveState>, taskId: TaskId): void {\n\tconst statuses = live.compactions;\n\tif (statuses === undefined) return;\n\tconst index = statuses.findIndex((status) => status.taskId === taskId);\n\tif (index >= 0) statuses.splice(index, 1);\n\tif (statuses.length === 0) delete live.compactions;\n}\n\n/** Remove the status of compaction task `taskId` and record that it failed with `error`. */\nexport function failCompactionStatus(live: Draft<LiveState>, taskId: TaskId, error: string): void {\n\tconst status = compactionStatus(live, taskId);\n\tif (status !== undefined) live.compactionFailure = { taskId: status.taskId, reason: status.reason, error };\n\tremoveCompactionStatus(live, taskId);\n}\n\n/**\n * The slot of tool task `taskId`, model-issued or nested, if `pi.live` still lists it. Nested slots are found by binary\n * search: a tool may make thousands of nested calls, each of which looks up its slot in several commits, and every\n * element a search reads costs a node in the draft's change tracker. Their task IDs ascend: each slot is appended in\n * the commit that creates its task, task IDs ascend in commit order, and removal keeps the order.\n */\nexport function toolSlot(live: Draft<LiveState>, taskId: TaskId): Draft<ToolSlot | NestedToolSlot> | undefined {\n\tconst slot = live.tools?.find((each) => each.taskId === taskId);\n\tif (slot !== undefined || live.nestedTools === undefined) return slot;\n\tconst nested = live.nestedTools;\n\tlet low = 0;\n\tlet high = nested.length - 1;\n\twhile (low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst candidate = nested[middle]!;\n\t\tif (candidate.taskId === taskId) return candidate;\n\t\tif (candidate.taskId < taskId) low = middle + 1;\n\t\telse high = middle - 1;\n\t}\n\treturn undefined;\n}\n\n/**\n * Mark a slot done and clear its running output; the caller sets a model-issued call's result `entry`, or a nested\n * call's `summary`. Neither is set when the task faulted or was orphaned.\n */\nexport function finishSlot(slot: Draft<ToolSlot | NestedToolSlot>): void {\n\tslot.status = \"done\";\n\tclearProgress(slot);\n}\n\n/** Remove the nested slots below tool task `taskId`, transitively; the list holds parents before their children. */\nexport function removeNestedSlots(live: Draft<LiveState>, taskId: TaskId): void {\n\tconst nested = live.nestedTools;\n\tif (nested === undefined) return;\n\tconst removed = new Set<TaskId>([taskId]);\n\tfor (const slot of nested) if (removed.has(slot.parentTaskId)) removed.add(slot.taskId);\n\tfor (let index = nested.length - 1; index >= 0; index--) {\n\t\tif (removed.has(nested[index]!.parentTaskId)) nested.splice(index, 1);\n\t}\n\tif (nested.length === 0) delete live.nestedTools;\n}\n\n/** Remove what a tool published while running; its result entry or a rerun replaces it. */\nexport function clearProgress(slot: Draft<SlotProgress>): void {\n\tdelete slot.output;\n\tdelete slot.droppedBytes;\n\tdelete slot.droppedLines;\n\tdelete slot.details;\n\tdelete slot.diagnostics;\n}\n\n/**\n * Harness cleanup for a terminal outcome the scheduler writes itself (`faulted` or `orphaned`). A run task ends its\n * run; a tool task's slot is marked done without an entry, and context derivation synthesizes the missing result; a\n * compaction task's status is removed and its failure recorded.\n * Ignores other kinds so it never creates `pi.live` elsewhere. The scheduler calls this without knowing task kinds;\n * the Harness passes it in (spec §5.4).\n * REMINDER: a committed generation partial becomes an aborted assistant entry here, exactly as in the generation abort\n * handler, so the transcript keeps what the model produced and `pi.usage` counts its spend. The scheduler's commit has\n * no task scope, so that entry has no `byTaskId`. Faults come from task bugs\n * or malformed provider data (a non-JSON value in a response), or a commit the Storage rejected without effect; an\n * uncertain storage failure poisons the Session instead and writes no outcome.\n */\nexport async function settleSchedulerOutcome(\n\ttx: Transaction,\n\trecord: TaskRecord<JsonValue, JsonValue, JsonValue>,\n\toutcome: SchedulerOutcome,\n): Promise<void> {\n\tif (record.kind === TOOL_TASK_KIND) {\n\t\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\t\tconst slot = toolSlot(live, record.id);\n\t\tif (slot !== undefined) finishSlot(slot);\n\t\tremoveNestedSlots(live, record.id);\n\t\treturn;\n\t}\n\tif (record.kind === COMPACTION_TASK_KIND) {\n\t\tconst error = outcome.status === \"faulted\" ? outcome.error.message : outcome.reason;\n\t\tfailCompactionStatus(await tx.doc(LiveDoc, record.conversationId), record.id, error);\n\t\treturn;\n\t}\n\tif (!RUN_TASK_KINDS.has(record.kind)) return;\n\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\tif (live.run?.taskId !== record.id) return;\n\tawait convertPartial(tx, live, record.conversationId);\n\tendRun(\n\t\ttx,\n\t\tlive,\n\t\trecord.id,\n\t\toutcome.status === \"faulted\"\n\t\t\t? { status: \"unanswered\", reason: \"faulted\", detail: outcome.error.message }\n\t\t\t: { status: \"unanswered\", reason: outcome.reason },\n\t);\n}\n"]}
1
+ {"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/harness/live.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Draft, JsonRepresentation, JsonValue } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Usage } from \"@earendil-works/pi-ai\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { Transaction } from \"../session/transaction.ts\";\nimport type {\n\tCommitOptions,\n\tEntryId,\n\tJsonObject,\n\tSubmissionId,\n\tSubmissionSettlement,\n\tTaskId,\n\tTaskRecord,\n\tTx,\n} from \"../types.ts\";\nimport { convertPartial } from \"./generation.ts\";\nimport type { SchedulerOutcome } from \"./scheduler.ts\";\nimport type { CompactionReason, CompactionResult, ToolDiagnostic } from \"./types.ts\";\n\n/** What a running tool call publishes, shared by model-issued and nested calls. */\ntype SlotProgress = {\n\tcallId: string;\n\tname: string;\n\tstatus: \"pending\" | \"running\" | \"done\";\n\t/** Retained running output and what the bounds dropped. */\n\toutput?: string;\n\tdroppedBytes?: number;\n\tdroppedLines?: number;\n\t/** Last `details()` value. */\n\tdetails?: JsonValue;\n\t/** Diagnostics recorded through `api.diagnostic()`. */\n\tdiagnostics?: ToolDiagnostic[];\n};\n\n/** Presentation of one tool call of the current round. */\nexport type ToolSlot = SlotProgress & {\n\t/** Absent for a call not started yet (sequential round). */\n\ttaskId?: TaskId;\n\t/** A call to a tool its request did not offer: its task settles `tool_unavailable` without running anything. */\n\tunoffered?: true;\n\t/** Result entry once done; absent when the tool task faulted or was orphaned. */\n\tentry?: EntryId;\n};\n\n/**\n * Presentation of one nested call: a call a running tool made through `executeTool()`. `callId` is\n * `<parentCallId>/<key>`, the call's ID, unique because keys contain no `/`. The Harness relates\n * slots by task ID: `taskId` is the call's tool task, `parentTaskId` the calling one, model-issued or nested.\n */\nexport type NestedToolSlot = SlotProgress & {\n\tparentCallId: string;\n\tparentTaskId: TaskId;\n\ttaskId: TaskId;\n\t/**\n\t * What the call was made with, replaced by the arguments it runs with, after repair, `beforeTool`, and coercion,\n\t * once it starts executing. A model-issued call's arguments are in its assistant entry instead.\n\t */\n\targuments: JsonObject;\n\t/** Once done: how the call ended, for a status line. The result itself is in the caller's `NestedCallDoc`. */\n\tsummary?: NestedToolSummary;\n};\n\n/** How a nested call ended: an error flag, its execution time, its spend, and up to 500 characters of error text. */\nexport type NestedToolSummary = {\n\tisError: boolean;\n\tdurationMs?: number;\n\tusage?: JsonRepresentation<Usage>;\n\terror?: string;\n};\n\n/** Presentation of one live compaction task (spec §8.7). */\nexport type CompactionStatus = {\n\ttaskId: TaskId<CompactionResult>;\n\t/** What started it: `compact()`, a threshold, or a context overflow. */\n\ttrigger: CompactionReason;\n\t/** Whether a generation waits for it: a compaction the generation owns. */\n\tblocking: boolean;\n\tattempt: number;\n\t/** Durable backoff before the next summarization attempt. */\n\tretry?: { at: number; error: string };\n};\n\n/** The newest compaction that failed since a compaction last produced a summary (spec §8.7). */\nexport type CompactionFailure = {\n\ttaskId: TaskId<CompactionResult>;\n\t/** What started the compaction that failed. */\n\ttrigger: CompactionReason;\n\t/** The failed or faulted outcome's error message, or why the scheduler orphaned the task. */\n\terror: string;\n};\n\n/**\n * Why a run ended without an answer:\n * - `model_error`: the model's response failed, after any retries, or the compaction a request needed failed;\n * - `no_model`: no model is configured, or the configured one is not available;\n * - `aborted`: the run was aborted (`Conversation.abort()`, `abortTask()`); `detail` says when its abort handler was\n * cut off `settings.restartLimit` times and did not finish;\n * - `reset`: a queued reset started a new context at the post-tools boundary, before an answer (a tool's handoff\n * continues the run);\n * - `faulted`: the run's task threw where it cannot recover, or storage rejected its commit;\n * - `orphaned`: the run's task was aborted while no registered definition could take it;\n * - `interrupted`: the host stopped while the model was answering, more often than `settings.retry` retries;\n * - `restart_limit`: the run's generation was cut off `settings.restartLimit` times in a row; a request or poll it was\n * cut off during counts as a failed attempt first, so this ends one whose charge itself keeps failing;\n * - `nothing_to_answer`: the context did not end in a user message or tool results (empty, or ending in an answer), so\n * no request was sent, as for a continue after an answer;\n * - `turn_limit`: a tool round reached the conversation's `maxTurns`; a continue resumes on its results.\n */\nexport type RunFailureReason =\n\t| \"model_error\"\n\t| \"no_model\"\n\t| \"aborted\"\n\t| \"reset\"\n\t| \"faulted\"\n\t| \"orphaned\"\n\t| \"interrupted\"\n\t| \"restart_limit\"\n\t| \"nothing_to_answer\"\n\t| \"turn_limit\";\n\n/** The newest run that ended without an answer (spec §8.2). */\nexport type RunFailure = {\n\t/** The task that ended the run: the run's task at its end. */\n\ttaskId: TaskId;\n\t/** The run's inputs, settled `unanswered`. */\n\tinputs: SubmissionId[];\n\treason: RunFailureReason;\n\t/**\n\t * `model_error`: the error text; `no_model`: which model is missing; `faulted`: the error message; `orphaned`: why\n\t * the task could not run (`missing_task`, `task_too_old`, `migration_failed`); `interrupted` and `restart_limit`: what\n\t * was cut off, and how often; `turn_limit`: the tool rounds the run took, `\"1 turn\"` or `\"N turns\"`.\n\t */\n\tdetail?: string;\n\t/** The failed run's `run.first`, so a continue's `afterRun` hooks see its history too. */\n\tfirst?: EntryId;\n};\n\n/** Run control (spec §8.2). */\nexport type LiveRun = {\n\t/** The task that settles the run's inputs: its generation, handed from one to the next. */\n\ttaskId: TaskId;\n\t/** The run's placed inputs, settled when it ends; `[]` once it answered and runs only its `afterRun` tasks. */\n\tinputs: SubmissionId[];\n\t/** Tool rounds so far, which `AgentState.maxTurns` bounds; an `onYield` continuation starts the count over. */\n\tturns: number;\n\t/**\n\t * The run's first entry: the first one the boundary that started it placed, a continue's the failed run's it\n\t * continues; otherwise its first tool-calling answer. A run started before it was recorded may lack it; `afterRun`\n\t * hooks then get the answer as the first entry.\n\t */\n\tfirst?: EntryId;\n\t/**\n\t * Present once the run answered and its generation waits for the tasks `afterRun` hooks asked for, listed once\n\t * created: queued items stay queued until they are terminal, and the run then ends without a failure.\n\t */\n\tafterRun?: { tasks?: TaskId[] };\n};\n\n/** Built-in live conversation state: run control and presentation of the current generation and tool round. */\nexport type LiveState = {\n\t/** Run control; present exactly while busy, also while a run that answered runs its `afterRun` tasks. */\n\trun?: LiveRun;\n\t/** Presentation of the current generation attempt. */\n\tgeneration?: {\n\t\tattempt: number;\n\t\t/** Committed throttled partial of the in-flight response. */\n\t\tmessage?: JsonRepresentation<AssistantMessage>;\n\t\t/** Durable backoff before the next attempt. */\n\t\tretry?: { at: number; error: string };\n\t\t/** Provider-side deferred response being polled. */\n\t\tdeferred?: { pollAt: number };\n\t};\n\t/** The current tool round in call order, from the tool-calling answer until the generation's `tools` phase ends it. */\n\ttools?: ToolSlot[];\n\t/**\n\t * Nested calls of running tool calls, in creation order, so a parent precedes its children and task IDs ascend. A\n\t * call's nested slots are removed when the call settles.\n\t */\n\tnestedTools?: NestedToolSlot[];\n\t/** Live compaction tasks in task ID order; absent when none. */\n\tcompactions?: CompactionStatus[];\n\t/**\n\t * The newest compaction failure, so a client can show it after the task ended. A compaction that produces a summary\n\t * removes it; one that is aborted or finds nothing to compact leaves it.\n\t */\n\tcompactionFailure?: CompactionFailure;\n\t/**\n\t * Why the newest run ended without an answer, so a client can show it after the run ended. Set in the commit that\n\t * ends the run; removed when a later commit starts a run. A run started in the same commit, by\n\t * `afterFailedRun: \"continue\"` or after a reset, keeps it.\n\t */\n\trunFailure?: RunFailure;\n};\n\nexport const LiveDoc = defineDoc<LiveState>({\n\tkind: \"pi.live\",\n\tversion: 3,\n\tscope: \"conversation\",\n\thistory: \"latest\",\n\tfork: \"initial\",\n\tinitial: () => ({}),\n\t// Version 1 named a compaction's trigger `reason`, in `compactions` and `compactionFailure`. Versions 1 and 2 (as\n\t// builds before `run.turns` stored it) may lack `run.turns`: a run in flight across the upgrade counts its turns\n\t// from 0. Version 2 already names the trigger `trigger`, so renaming leaves it alone.\n\tmigrate: (value) => {\n\t\tconst live = value as { run?: JsonObject; compactions?: JsonObject[]; compactionFailure?: JsonObject };\n\t\tconst { run, compactions, compactionFailure } = live;\n\t\treturn {\n\t\t\t...value,\n\t\t\t...(run === undefined ? {} : { run: { ...run, turns: typeof run.turns === \"number\" ? run.turns : 0 } }),\n\t\t\t...(compactions === undefined ? {} : { compactions: compactions.map(renameReason) }),\n\t\t\t...(compactionFailure === undefined ? {} : { compactionFailure: renameReason(compactionFailure) }),\n\t\t} as LiveState;\n\t},\n\t// REMINDER: a complete base whenever nothing runs (spec §8.2): no generation and no running tool slot. That holds\n\t// while idle, in the commit handing a generation over to its tool round, and between tools, so the delta chain\n\t// spans at most one generation or the overlapping execution of one round's tools. A slot holds output only while\n\t// running, so every base is small; a nested call's result lives in its caller's task documents, never here. Do not\n\t// add a delta-count bound; the tool output benchmark checks this rule.\n\tcheckpointWhen: (value) =>\n\t\tvalue.generation === undefined &&\n\t\t!(value.tools ?? []).some((slot) => slot.status === \"running\") &&\n\t\t!(value.nestedTools ?? []).some((slot) => slot.status === \"running\"),\n});\n\n/** A copy of a version 1 compaction status or failure with `reason` renamed `trigger`, its keys in their order. */\nfunction renameReason(value: JsonObject): JsonObject {\n\treturn Object.fromEntries(Object.entries(value).map(([key, field]) => [key === \"reason\" ? \"trigger\" : key, field]));\n}\n\n/**\n * How the progress throttles commit a partial answer and running tool output into `pi.live`: deferred, so storage gets\n * them together, at the next commit that reads or writes `pi.live` or after `settings.progress.persistWithinMs`. Every\n * commit that consumes or clears streamed fields reads `pi.live`, so it stores them with its own changes (spec §8.2).\n */\nexport const PROGRESS_COMMIT: CommitOptions = { persistence: { kind: \"deferred\" } };\n\n/** Built-in task kinds that can own `pi.live.run`. */\nconst RUN_TASK_KINDS: ReadonlySet<string> = new Set([\"pi.generation\"]);\nconst TOOL_TASK_KIND = \"pi.tool\";\nconst COMPACTION_TASK_KIND = \"pi.compaction\";\n\n/**\n * Built-in task kinds that end themselves once cut off `settings.restartLimit` times (`TaskRecord.restarts`); the\n * scheduler faults a task of any other kind instead (spec §5.4).\n */\nexport const LIMITS_RESTARTS: ReadonlySet<string> = new Set([...RUN_TASK_KINDS, TOOL_TASK_KIND, COMPACTION_TASK_KIND]);\n\n/** Whether `task` was cut off `settings.restartLimit` times in a row, so its phase ends it instead of running. */\nexport function cutOffTooOften(\n\ttask: { readonly restarts?: number },\n\truntime: { readonly settings: { readonly restartLimit: number } },\n): boolean {\n\t// The settings are read only for a task that was cut off at all, so the normal path resolves nothing more.\n\treturn task.restarts !== undefined && task.restarts >= runtime.settings.restartLimit;\n}\n\n/**\n * Whether a task at the limit may still do the cleanup it does when it ends, such as converting a partial: only at the\n * limit. Past it, that cleanup may be what cut the task off, so the task ends without it.\n */\nexport function salvageable(\n\ttask: { readonly restarts?: number },\n\truntime: { readonly settings: { readonly restartLimit: number } },\n): boolean {\n\treturn (task.restarts ?? 0) <= runtime.settings.restartLimit;\n}\n\n/** How often a task was cut off, for a message: \"once\", or \"3 times in a row\". */\nexport function cutOffCount(task: { readonly restarts?: number }): string {\n\tconst restarts = task.restarts ?? 0;\n\treturn restarts === 1 ? \"once\" : `${restarts} times in a row`;\n}\n\n/** How a run ends: with an answer, or without one, settling its inputs `unanswered` and recording why. */\nexport type RunEnd =\n\t| { readonly status: \"done\"; readonly answer: EntryId }\n\t| {\n\t\t\treadonly status: \"unanswered\";\n\t\t\treadonly reason: RunFailureReason;\n\t\t\treadonly detail?: string;\n\t\t\t/** The inputs' settlement; default `unanswered` with `reason` and `detail`. */\n\t\t\treadonly settlement?: SubmissionSettlement;\n\t };\n\n/**\n * End the run owned by `taskId`: settle each of its inputs, remove `run`, and, without an answer, record\n * `runFailure`. A run that answered already, and runs only its `afterRun` tasks, records no failure, however it ends.\n * Always removes `generation`, `tools`, and `nestedTools`, whose presentation belongs to the ending run.\n */\nexport function endRun(tx: Tx, live: Draft<LiveState>, taskId: TaskId, end: RunEnd): void {\n\tif (live.run?.taskId === taskId) {\n\t\tconst inputs = [...live.run.inputs];\n\t\tconst { first } = live.run;\n\t\tif (end.status === \"done\") {\n\t\t\tfor (const id of inputs) tx.settleSubmission(id, { status: \"done\", answer: end.answer });\n\t\t} else if (live.run.afterRun === undefined) {\n\t\t\tconst { reason, detail } = end;\n\t\t\tconst settlement = end.settlement ?? {\n\t\t\t\tstatus: \"unanswered\",\n\t\t\t\treason,\n\t\t\t\t...(detail === undefined ? {} : { detail }),\n\t\t\t};\n\t\t\tfor (const id of inputs) tx.settleSubmission(id, settlement);\n\t\t\tlive.runFailure = {\n\t\t\t\ttaskId,\n\t\t\t\tinputs,\n\t\t\t\treason,\n\t\t\t\t...(detail === undefined ? {} : { detail }),\n\t\t\t\t...(first === undefined ? {} : { first }),\n\t\t\t};\n\t\t}\n\t\tdelete live.run;\n\t}\n\tdelete live.generation;\n\tdelete live.tools;\n\tdelete live.nestedTools;\n}\n\n/** Add the status of a compaction task created in this commit; statuses stay in task ID order. */\nexport function addCompactionStatus(live: Draft<LiveState>, status: CompactionStatus): void {\n\tlive.compactions ??= [];\n\tlive.compactions.push(status);\n}\n\n/** The status of compaction task `taskId`, if listed. */\nexport function compactionStatus(live: Draft<LiveState>, taskId: TaskId): Draft<CompactionStatus> | undefined {\n\treturn live.compactions?.find((status) => status.taskId === taskId);\n}\n\n/** Remove the status of compaction task `taskId`, and the list once empty. */\nexport function removeCompactionStatus(live: Draft<LiveState>, taskId: TaskId): void {\n\tconst statuses = live.compactions;\n\tif (statuses === undefined) return;\n\tconst index = statuses.findIndex((status) => status.taskId === taskId);\n\tif (index >= 0) statuses.splice(index, 1);\n\tif (statuses.length === 0) delete live.compactions;\n}\n\n/** Remove the status of compaction task `taskId` and record that it failed with `error`. */\nexport function failCompactionStatus(live: Draft<LiveState>, taskId: TaskId, error: string): void {\n\tconst status = compactionStatus(live, taskId);\n\tif (status !== undefined) live.compactionFailure = { taskId: status.taskId, trigger: status.trigger, error };\n\tremoveCompactionStatus(live, taskId);\n}\n\n/**\n * The slot of tool task `taskId`, model-issued or nested, if `pi.live` still lists it. Nested slots are found by binary\n * search: a tool may make thousands of nested calls, each of which looks up its slot in several commits, and every\n * element a search reads costs a node in the draft's change tracker. Their task IDs ascend: each slot is appended in\n * the commit that creates its task, task IDs ascend in commit order, and removal keeps the order.\n */\nexport function toolSlot(live: Draft<LiveState>, taskId: TaskId): Draft<ToolSlot | NestedToolSlot> | undefined {\n\tconst slot = live.tools?.find((each) => each.taskId === taskId);\n\tif (slot !== undefined || live.nestedTools === undefined) return slot;\n\tconst nested = live.nestedTools;\n\tlet low = 0;\n\tlet high = nested.length - 1;\n\twhile (low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst candidate = nested[middle]!;\n\t\tif (candidate.taskId === taskId) return candidate;\n\t\tif (candidate.taskId < taskId) low = middle + 1;\n\t\telse high = middle - 1;\n\t}\n\treturn undefined;\n}\n\n/**\n * Mark a slot done and clear its running output; the caller sets a model-issued call's result `entry`, or a nested\n * call's `summary`. Neither is set when the task faulted or was orphaned.\n */\nexport function finishSlot(slot: Draft<ToolSlot | NestedToolSlot>): void {\n\tslot.status = \"done\";\n\tclearProgress(slot);\n}\n\n/** Remove the nested slots below tool task `taskId`, transitively; the list holds parents before their children. */\nexport function removeNestedSlots(live: Draft<LiveState>, taskId: TaskId): void {\n\tconst nested = live.nestedTools;\n\tif (nested === undefined) return;\n\tconst removed = new Set<TaskId>([taskId]);\n\tfor (const slot of nested) if (removed.has(slot.parentTaskId)) removed.add(slot.taskId);\n\tfor (let index = nested.length - 1; index >= 0; index--) {\n\t\tif (removed.has(nested[index]!.parentTaskId)) nested.splice(index, 1);\n\t}\n\tif (nested.length === 0) delete live.nestedTools;\n}\n\n/** Remove what a tool published while running; its result entry or a rerun replaces it. */\nexport function clearProgress(slot: Draft<SlotProgress>): void {\n\tdelete slot.output;\n\tdelete slot.droppedBytes;\n\tdelete slot.droppedLines;\n\tdelete slot.details;\n\tdelete slot.diagnostics;\n}\n\n/**\n * Harness cleanup for a terminal outcome the scheduler writes itself (`faulted` or `orphaned`). A run task ends its\n * run; a tool task's slot is marked done without an entry, and context derivation synthesizes the missing result; a\n * compaction task's status is removed and its failure recorded.\n * Ignores other kinds so it never creates `pi.live` elsewhere. The scheduler calls this without knowing task kinds;\n * the Harness passes it in (spec §5.4).\n * REMINDER: a committed generation partial becomes an aborted assistant entry here, exactly as in the generation abort\n * handler, so the transcript keeps what the model produced and `pi.usage` counts its spend. The scheduler's commit has\n * no task scope, so that entry has no `byTaskId`. Faults come from task bugs\n * or malformed provider data (a non-JSON value in a response), or a commit the Storage rejected without effect; an\n * uncertain storage failure poisons the Session instead and writes no outcome.\n */\nexport async function settleSchedulerOutcome(\n\ttx: Transaction,\n\trecord: TaskRecord<JsonValue, JsonValue, JsonValue>,\n\toutcome: SchedulerOutcome,\n): Promise<void> {\n\tif (record.kind === TOOL_TASK_KIND) {\n\t\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\t\tconst slot = toolSlot(live, record.id);\n\t\tif (slot !== undefined) finishSlot(slot);\n\t\tremoveNestedSlots(live, record.id);\n\t\treturn;\n\t}\n\t// Only an abort handler is faulted for the limit here (the built-in phases end themselves): the task was aborted,\n\t// and keeps that meaning. Its run ends `aborted`, with the limit as detail, and an aborted compaction is no failure.\n\tconst detail = outcome.status === \"faulted\" ? outcome.error.detail : undefined;\n\tconst cutOff =\n\t\ttypeof detail === \"object\" && detail !== null && \"reason\" in detail && detail.reason === \"restart_limit\";\n\tif (record.kind === COMPACTION_TASK_KIND) {\n\t\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\t\tif (cutOff) removeCompactionStatus(live, record.id);\n\t\telse failCompactionStatus(live, record.id, outcome.status === \"faulted\" ? outcome.error.message : outcome.reason);\n\t\treturn;\n\t}\n\tif (!RUN_TASK_KINDS.has(record.kind)) return;\n\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\tif (live.run?.taskId !== record.id) return;\n\t// Cut off, the abort handler may have died converting the partial: the run ends without it.\n\tif (!cutOff) await convertPartial(tx, live, record.conversationId);\n\tendRun(\n\t\ttx,\n\t\tlive,\n\t\trecord.id,\n\t\toutcome.status === \"faulted\"\n\t\t\t? { status: \"unanswered\", reason: cutOff ? \"aborted\" : \"faulted\", detail: outcome.error.message }\n\t\t\t: {\n\t\t\t\t\tstatus: \"unanswered\",\n\t\t\t\t\treason: \"orphaned\",\n\t\t\t\t\tdetail: outcome.reason,\n\t\t\t\t\tsettlement: { status: \"unanswered\", reason: outcome.reason },\n\t\t\t\t},\n\t);\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import type { JsonRepresentation } from "@earendil-works/chord";
2
+ import { type Draft } from "@earendil-works/chord/delta";
3
+ import type { AssistantMessage, AssistantMessageEvent, ToolCall } from "@earendil-works/pi-ai";
4
+ import { type JsonChange } from "@earendil-works/pi-ai/utils/streaming-json";
5
+ import { type AssignPlan } from "./json.ts";
6
+ import type { LiveState } from "./live.ts";
7
+ /** A partial message as `pi.live.generation.message` holds it. */
8
+ export type PartialMessage = JsonRepresentation<AssistantMessage>;
9
+ /** Committed arguments of a streamed tool call. */
10
+ type Arguments = JsonRepresentation<ToolCall>["arguments"];
11
+ /** What one partial commit writes besides the partial message itself; from `PartialChanges.take()`. */
12
+ export type PartialCommit = {
13
+ /** The plan `assignJson()` writes the message with: text appends, and the arguments it leaves to `changes`. */
14
+ readonly plan: AssignPlan;
15
+ /** Each tracked tool call by content index: its argument changes since the last commit, in order, and its snapshot. */
16
+ readonly toolCalls: ReadonlyMap<number, {
17
+ readonly changes: readonly JsonChange[];
18
+ readonly snapshot: Arguments;
19
+ }>;
20
+ };
21
+ /**
22
+ * Collects, from a response's events, what changed in its partial message since the last commit, so the commit appends
23
+ * new text instead of reassigning growing strings. Reassigning makes Chord compare the old and new string, which costs
24
+ * O(length) per commit.
25
+ *
26
+ * - Text and thinking: the `text_delta` and `thinking_delta` events. The commit appends them where the committed string
27
+ * plus the deltas has the new string's length, and compares otherwise. A block's `*_end` event drops them, because
28
+ * providers may replace the text when a block ends.
29
+ * - Tool-call arguments: a `StreamingJsonParser` per call, fed with the raw argument text of each `toolcall_delta`,
30
+ * reports what each piece changed. The commit applies those changes to the committed arguments in order and leaves
31
+ * the arguments out of the message assignment, so the committed arguments are exactly the parsed text so far.
32
+ * `toolcall_end` ends the tracking; the call's last commit assigns its final arguments.
33
+ */
34
+ export declare class PartialChanges {
35
+ #private;
36
+ /** Record what `event` changed. */
37
+ observe(event: AssistantMessageEvent): void;
38
+ /**
39
+ * Take what changed since the last call, for a commit of `message`, a copy of the partial. Clears the arguments of
40
+ * tracked tool calls in `message`: the commit writes them from the changes. Call `restore()` if the commit fails.
41
+ */
42
+ take(message: PartialMessage): PartialCommit;
43
+ /**
44
+ * Write `message` with what `commit` took into `generation.message`: the message leaf by leaf with the plan, then the
45
+ * argument changes. A tracked call whose block is new here gets its arguments from its changes, which then start at
46
+ * its first piece; when the committed message lost a call whose changes were already committed (another writer reset
47
+ * it), the call falls back to its snapshot from now on.
48
+ */
49
+ commit(generation: Draft<NonNullable<LiveState["generation"]>>, message: PartialMessage, commit: PartialCommit): void;
50
+ /** Put back what a failed commit took, so the next commit applies it first. */
51
+ restore(commit: PartialCommit): void;
52
+ }
53
+ export {};
54
+ //# sourceMappingURL=partial-changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-changes.d.ts","sourceRoot":"","sources":["../../src/harness/partial-changes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAa,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAc,KAAK,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,KAAK,UAAU,EAAuB,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,WAAW,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAI3C,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;AAElE,mDAAmD;AACnD,KAAK,SAAS,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC;AAE3D,uGAAuG;AACvG,MAAM,MAAM,aAAa,GAAG;IAC3B,+GAA+G;IAC/G,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,uHAAuH;IACvH,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CACnH,CAAC;AAKF;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;;IAI1B,mCAAmC;IACnC,OAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAsC1C;IAED;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAoB3C;IAED;;;;;OAKG;IACH,MAAM,CACL,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,EACvD,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,GACnB,IAAI,CAuBN;IAED,+EAA+E;IAC/E,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAKnC;CACD"}
@@ -0,0 +1,162 @@
1
+ import { appendText } from "@earendil-works/chord/delta";
2
+ import { StreamingJsonParser } from "@earendil-works/pi-ai/utils/streaming-json";
3
+ import { assignJson } from "./json.js";
4
+ /**
5
+ * Collects, from a response's events, what changed in its partial message since the last commit, so the commit appends
6
+ * new text instead of reassigning growing strings. Reassigning makes Chord compare the old and new string, which costs
7
+ * O(length) per commit.
8
+ *
9
+ * - Text and thinking: the `text_delta` and `thinking_delta` events. The commit appends them where the committed string
10
+ * plus the deltas has the new string's length, and compares otherwise. A block's `*_end` event drops them, because
11
+ * providers may replace the text when a block ends.
12
+ * - Tool-call arguments: a `StreamingJsonParser` per call, fed with the raw argument text of each `toolcall_delta`,
13
+ * reports what each piece changed. The commit applies those changes to the committed arguments in order and leaves
14
+ * the arguments out of the message assignment, so the committed arguments are exactly the parsed text so far.
15
+ * `toolcall_end` ends the tracking; the call's last commit assigns its final arguments.
16
+ */
17
+ export class PartialChanges {
18
+ #text = new Map();
19
+ #toolCalls = new Map();
20
+ /** Record what `event` changed. */
21
+ observe(event) {
22
+ switch (event.type) {
23
+ case "text_delta":
24
+ case "thinking_delta": {
25
+ const field = event.type === "text_delta" ? "text" : "thinking";
26
+ const pending = this.#text.get(event.contentIndex);
27
+ if (pending?.field === field)
28
+ pending.text += event.delta;
29
+ else
30
+ this.#text.set(event.contentIndex, { field, text: event.delta });
31
+ return;
32
+ }
33
+ case "toolcall_start":
34
+ this.#toolCalls.set(event.contentIndex, {
35
+ parser: new StreamingJsonParser(),
36
+ changes: [],
37
+ committed: false,
38
+ });
39
+ return;
40
+ case "toolcall_delta": {
41
+ const call = this.#toolCalls.get(event.contentIndex);
42
+ if (call === undefined)
43
+ return;
44
+ for (const change of call.parser.append(event.delta)) {
45
+ // Arguments are an object: any other root shows as no arguments, so the snapshot is committed instead.
46
+ if (change.path.length === 0 && !isRecord(change.kind === "set" ? value(change) : null)) {
47
+ this.#toolCalls.delete(event.contentIndex);
48
+ return;
49
+ }
50
+ call.changes.push(change);
51
+ }
52
+ return;
53
+ }
54
+ case "text_end":
55
+ case "thinking_end":
56
+ this.#text.delete(event.contentIndex);
57
+ return;
58
+ case "toolcall_end":
59
+ this.#toolCalls.delete(event.contentIndex);
60
+ return;
61
+ }
62
+ }
63
+ /**
64
+ * Take what changed since the last call, for a commit of `message`, a copy of the partial. Clears the arguments of
65
+ * tracked tool calls in `message`: the commit writes them from the changes. Call `restore()` if the commit fails.
66
+ */
67
+ take(message) {
68
+ const blocks = new Map();
69
+ for (const [index, { field, text }] of this.#text) {
70
+ blocks.set(index, { kind: "fields", fields: new Map([[field, { kind: "append", text }]]) });
71
+ }
72
+ this.#text = new Map();
73
+ const toolCalls = new Map();
74
+ for (const [index, call] of this.#toolCalls) {
75
+ const block = message.content[index];
76
+ // A tracked call is the tool call its `toolcall_start` named; the narrowing only tells TypeScript.
77
+ if (block?.type !== "toolCall")
78
+ continue;
79
+ toolCalls.set(index, { changes: call.changes, snapshot: block.arguments });
80
+ block.arguments = {};
81
+ blocks.set(index, { kind: "fields", fields: new Map([["arguments", { kind: "keep" }]]) });
82
+ call.changes = [];
83
+ }
84
+ return {
85
+ plan: { kind: "fields", fields: new Map([["content", { kind: "fields", fields: blocks }]]) },
86
+ toolCalls,
87
+ };
88
+ }
89
+ /**
90
+ * Write `message` with what `commit` took into `generation.message`: the message leaf by leaf with the plan, then the
91
+ * argument changes. A tracked call whose block is new here gets its arguments from its changes, which then start at
92
+ * its first piece; when the committed message lost a call whose changes were already committed (another writer reset
93
+ * it), the call falls back to its snapshot from now on.
94
+ */
95
+ commit(generation, message, commit) {
96
+ const committedBlocks = generation.message?.content.length ?? 0;
97
+ const apply = [];
98
+ for (const [index, { changes, snapshot }] of commit.toolCalls) {
99
+ const call = this.#toolCalls.get(index);
100
+ if (call === undefined)
101
+ continue;
102
+ const block = message.content[index];
103
+ if (index >= committedBlocks && call.committed && block?.type === "toolCall") {
104
+ // The earlier changes are not in the committed message: commit the snapshot and stop tracking the call.
105
+ this.#toolCalls.delete(index);
106
+ block.arguments = snapshot;
107
+ continue;
108
+ }
109
+ call.committed = true;
110
+ apply.push([index, changes]);
111
+ }
112
+ assignJson(generation, "message", message, commit.plan);
113
+ const content = generation.message?.content ?? [];
114
+ for (const [index, changes] of apply) {
115
+ const block = content[index];
116
+ if (block?.type !== "toolCall")
117
+ continue;
118
+ for (const change of changes)
119
+ applyChange(block, change);
120
+ }
121
+ }
122
+ /** Put back what a failed commit took, so the next commit applies it first. */
123
+ restore(commit) {
124
+ for (const [index, { changes }] of commit.toolCalls) {
125
+ const call = this.#toolCalls.get(index);
126
+ if (call !== undefined)
127
+ call.changes = [...changes, ...call.changes];
128
+ }
129
+ }
130
+ }
131
+ /** Apply one argument change to the committed arguments of `block`. */
132
+ function applyChange(block, change) {
133
+ const key = change.path.at(-1);
134
+ if (key === undefined) {
135
+ // Only an object root is tracked (`observe()` drops any other), and only `set` starts it.
136
+ const root = change.kind === "set" ? value(change) : undefined;
137
+ if (isRecord(root))
138
+ block.arguments = root;
139
+ return;
140
+ }
141
+ // The path follows the parsed argument text, not a type: each segment names a container `observe()` saw set.
142
+ let parent = block.arguments;
143
+ for (const segment of change.path.slice(0, -1)) {
144
+ parent = parent[segment];
145
+ }
146
+ const slots = parent;
147
+ if (change.kind === "set")
148
+ slots[key] = value(change);
149
+ // appendText() takes object members; a string in an array is reassigned, which compares it whole.
150
+ else if (Array.isArray(parent))
151
+ slots[key] = `${slots[key]}${change.text}`;
152
+ else
153
+ appendText(parent, String(key), change.text);
154
+ }
155
+ /** The value a `set` change reports: JSON data, which pi-ai types with read-only arrays. */
156
+ function value(change) {
157
+ return change.value;
158
+ }
159
+ function isRecord(value) {
160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
161
+ }
162
+ //# sourceMappingURL=partial-changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-changes.js","sourceRoot":"","sources":["../../src/harness/partial-changes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAc,MAAM,6BAA6B,CAAC;AAErE,OAAO,EAAmB,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAmB,UAAU,EAAE,MAAM,WAAW,CAAC;AAsBxD;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,cAAc;IAC1B,KAAK,GAAG,IAAI,GAAG,EAAiE,CAAC;IACxE,UAAU,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEvD,mCAAmC;IACnC,OAAO,CAAC,KAA4B;QACnC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,YAAY,CAAC;YAClB,KAAK,gBAAgB,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;gBAChE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACnD,IAAI,OAAO,EAAE,KAAK,KAAK,KAAK;oBAAE,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC;;oBACrD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;gBACtE,OAAO;YACR,CAAC;YACD,KAAK,gBAAgB;gBACpB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE;oBACvC,MAAM,EAAE,IAAI,mBAAmB,EAAE;oBACjC,OAAO,EAAE,EAAE;oBACX,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAC;gBACH,OAAO;YACR,KAAK,gBAAgB,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACrD,IAAI,IAAI,KAAK,SAAS;oBAAE,OAAO;gBAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBACtD,uGAAuG;oBACvG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;wBACzF,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;wBAC3C,OAAO;oBACR,CAAC;oBACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO;YACR,CAAC;YACD,KAAK,UAAU,CAAC;YAChB,KAAK,cAAc;gBAClB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACtC,OAAO;YACR,KAAK,cAAc;gBAClB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBAC3C,OAAO;QACT,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,OAAuB;QAC3B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;QACtD,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqF,CAAC;QAC/G,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACrC,mGAAmG;YACnG,IAAI,KAAK,EAAE,IAAI,KAAK,UAAU;gBAAE,SAAS;YACzC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;YAC3E,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1F,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QACnB,CAAC;QACD,OAAO;YACN,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;YAC5F,SAAS;SACT,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CACL,UAAuD,EACvD,OAAuB,EACvB,MAAqB;QAErB,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAChE,MAAM,KAAK,GAAsC,EAAE,CAAC;QACpD,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACrC,IAAI,KAAK,IAAI,eAAe,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC9E,wGAAwG;gBACxG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC9B,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC;gBAC3B,SAAS;YACV,CAAC;YACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,UAAU,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;QAClD,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,KAAK,EAAE,IAAI,KAAK,UAAU;gBAAE,SAAS;YACzC,KAAK,MAAM,MAAM,IAAI,OAAO;gBAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;IACF,CAAC;IAED,+EAA+E;IAC/E,OAAO,CAAC,MAAqB;QAC5B,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,OAAO,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,CAAC;IACF,CAAC;CACD;AAED,uEAAuE;AACvE,SAAS,WAAW,CAAC,KAA0C,EAAE,MAAkB;IAClF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACvB,0FAA0F;QAC1F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,IAAI,QAAQ,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QAC3C,OAAO;IACR,CAAC;IACD,6GAA6G;IAC7G,IAAI,MAAM,GAAG,KAAK,CAAC,SAAiC,CAAC;IACrD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,GAAI,MAAwD,CAAC,OAAO,CAAE,CAAC;IAC9E,CAAC;IACD,MAAM,KAAK,GAAG,MAA4C,CAAC;IAC3D,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACtD,kGAAkG;SAC7F,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAW,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;;QAChF,UAAU,CAAC,MAAuC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC;AAED,4FAA4F;AAC5F,SAAS,KAAK,CAAC,MAA4C;IAC1D,OAAO,MAAM,CAAC,KAAkB,CAAC;AAClC,CAAC;AAED,SAAS,QAAQ,CAAC,KAA4B;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["// What a streamed answer changed in its partial message between two partial commits, so a commit costs the new text.\nimport type { JsonRepresentation, JsonValue } from \"@earendil-works/chord\";\nimport { appendText, type Draft } from \"@earendil-works/chord/delta\";\nimport type { AssistantMessage, AssistantMessageEvent, ToolCall } from \"@earendil-works/pi-ai\";\nimport { type JsonChange, StreamingJsonParser } from \"@earendil-works/pi-ai/utils/streaming-json\";\nimport { type AssignPlan, assignJson } from \"./json.ts\";\nimport type { LiveState } from \"./live.ts\";\n\ntype JsonContainer = Record<string, JsonValue> | JsonValue[];\n\n/** A partial message as `pi.live.generation.message` holds it. */\nexport type PartialMessage = JsonRepresentation<AssistantMessage>;\n\n/** Committed arguments of a streamed tool call. */\ntype Arguments = JsonRepresentation<ToolCall>[\"arguments\"];\n\n/** What one partial commit writes besides the partial message itself; from `PartialChanges.take()`. */\nexport type PartialCommit = {\n\t/** The plan `assignJson()` writes the message with: text appends, and the arguments it leaves to `changes`. */\n\treadonly plan: AssignPlan;\n\t/** Each tracked tool call by content index: its argument changes since the last commit, in order, and its snapshot. */\n\treadonly toolCalls: ReadonlyMap<number, { readonly changes: readonly JsonChange[]; readonly snapshot: Arguments }>;\n};\n\n/** What a tracked tool call changed since the last commit, and whether any commit took its changes yet. */\ntype ToolCallState = { readonly parser: StreamingJsonParser; changes: JsonChange[]; committed: boolean };\n\n/**\n * Collects, from a response's events, what changed in its partial message since the last commit, so the commit appends\n * new text instead of reassigning growing strings. Reassigning makes Chord compare the old and new string, which costs\n * O(length) per commit.\n *\n * - Text and thinking: the `text_delta` and `thinking_delta` events. The commit appends them where the committed string\n * plus the deltas has the new string's length, and compares otherwise. A block's `*_end` event drops them, because\n * providers may replace the text when a block ends.\n * - Tool-call arguments: a `StreamingJsonParser` per call, fed with the raw argument text of each `toolcall_delta`,\n * reports what each piece changed. The commit applies those changes to the committed arguments in order and leaves\n * the arguments out of the message assignment, so the committed arguments are exactly the parsed text so far.\n * `toolcall_end` ends the tracking; the call's last commit assigns its final arguments.\n */\nexport class PartialChanges {\n\t#text = new Map<number, { readonly field: \"text\" | \"thinking\"; text: string }>();\n\treadonly #toolCalls = new Map<number, ToolCallState>();\n\n\t/** Record what `event` changed. */\n\tobserve(event: AssistantMessageEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"text_delta\":\n\t\t\tcase \"thinking_delta\": {\n\t\t\t\tconst field = event.type === \"text_delta\" ? \"text\" : \"thinking\";\n\t\t\t\tconst pending = this.#text.get(event.contentIndex);\n\t\t\t\tif (pending?.field === field) pending.text += event.delta;\n\t\t\t\telse this.#text.set(event.contentIndex, { field, text: event.delta });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"toolcall_start\":\n\t\t\t\tthis.#toolCalls.set(event.contentIndex, {\n\t\t\t\t\tparser: new StreamingJsonParser(),\n\t\t\t\t\tchanges: [],\n\t\t\t\t\tcommitted: false,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase \"toolcall_delta\": {\n\t\t\t\tconst call = this.#toolCalls.get(event.contentIndex);\n\t\t\t\tif (call === undefined) return;\n\t\t\t\tfor (const change of call.parser.append(event.delta)) {\n\t\t\t\t\t// Arguments are an object: any other root shows as no arguments, so the snapshot is committed instead.\n\t\t\t\t\tif (change.path.length === 0 && !isRecord(change.kind === \"set\" ? value(change) : null)) {\n\t\t\t\t\t\tthis.#toolCalls.delete(event.contentIndex);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcall.changes.push(change);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"text_end\":\n\t\t\tcase \"thinking_end\":\n\t\t\t\tthis.#text.delete(event.contentIndex);\n\t\t\t\treturn;\n\t\t\tcase \"toolcall_end\":\n\t\t\t\tthis.#toolCalls.delete(event.contentIndex);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * Take what changed since the last call, for a commit of `message`, a copy of the partial. Clears the arguments of\n\t * tracked tool calls in `message`: the commit writes them from the changes. Call `restore()` if the commit fails.\n\t */\n\ttake(message: PartialMessage): PartialCommit {\n\t\tconst blocks = new Map<string | number, AssignPlan>();\n\t\tfor (const [index, { field, text }] of this.#text) {\n\t\t\tblocks.set(index, { kind: \"fields\", fields: new Map([[field, { kind: \"append\", text }]]) });\n\t\t}\n\t\tthis.#text = new Map();\n\t\tconst toolCalls = new Map<number, { readonly changes: readonly JsonChange[]; readonly snapshot: Arguments }>();\n\t\tfor (const [index, call] of this.#toolCalls) {\n\t\t\tconst block = message.content[index];\n\t\t\t// A tracked call is the tool call its `toolcall_start` named; the narrowing only tells TypeScript.\n\t\t\tif (block?.type !== \"toolCall\") continue;\n\t\t\ttoolCalls.set(index, { changes: call.changes, snapshot: block.arguments });\n\t\t\tblock.arguments = {};\n\t\t\tblocks.set(index, { kind: \"fields\", fields: new Map([[\"arguments\", { kind: \"keep\" }]]) });\n\t\t\tcall.changes = [];\n\t\t}\n\t\treturn {\n\t\t\tplan: { kind: \"fields\", fields: new Map([[\"content\", { kind: \"fields\", fields: blocks }]]) },\n\t\t\ttoolCalls,\n\t\t};\n\t}\n\n\t/**\n\t * Write `message` with what `commit` took into `generation.message`: the message leaf by leaf with the plan, then the\n\t * argument changes. A tracked call whose block is new here gets its arguments from its changes, which then start at\n\t * its first piece; when the committed message lost a call whose changes were already committed (another writer reset\n\t * it), the call falls back to its snapshot from now on.\n\t */\n\tcommit(\n\t\tgeneration: Draft<NonNullable<LiveState[\"generation\"]>>,\n\t\tmessage: PartialMessage,\n\t\tcommit: PartialCommit,\n\t): void {\n\t\tconst committedBlocks = generation.message?.content.length ?? 0;\n\t\tconst apply: [number, readonly JsonChange[]][] = [];\n\t\tfor (const [index, { changes, snapshot }] of commit.toolCalls) {\n\t\t\tconst call = this.#toolCalls.get(index);\n\t\t\tif (call === undefined) continue;\n\t\t\tconst block = message.content[index];\n\t\t\tif (index >= committedBlocks && call.committed && block?.type === \"toolCall\") {\n\t\t\t\t// The earlier changes are not in the committed message: commit the snapshot and stop tracking the call.\n\t\t\t\tthis.#toolCalls.delete(index);\n\t\t\t\tblock.arguments = snapshot;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcall.committed = true;\n\t\t\tapply.push([index, changes]);\n\t\t}\n\t\tassignJson(generation, \"message\", message, commit.plan);\n\t\tconst content = generation.message?.content ?? [];\n\t\tfor (const [index, changes] of apply) {\n\t\t\tconst block = content[index];\n\t\t\tif (block?.type !== \"toolCall\") continue;\n\t\t\tfor (const change of changes) applyChange(block, change);\n\t\t}\n\t}\n\n\t/** Put back what a failed commit took, so the next commit applies it first. */\n\trestore(commit: PartialCommit): void {\n\t\tfor (const [index, { changes }] of commit.toolCalls) {\n\t\t\tconst call = this.#toolCalls.get(index);\n\t\t\tif (call !== undefined) call.changes = [...changes, ...call.changes];\n\t\t}\n\t}\n}\n\n/** Apply one argument change to the committed arguments of `block`. */\nfunction applyChange(block: Draft<JsonRepresentation<ToolCall>>, change: JsonChange): void {\n\tconst key = change.path.at(-1);\n\tif (key === undefined) {\n\t\t// Only an object root is tracked (`observe()` drops any other), and only `set` starts it.\n\t\tconst root = change.kind === \"set\" ? value(change) : undefined;\n\t\tif (isRecord(root)) block.arguments = root;\n\t\treturn;\n\t}\n\t// The path follows the parsed argument text, not a type: each segment names a container `observe()` saw set.\n\tlet parent = block.arguments as Draft<JsonContainer>;\n\tfor (const segment of change.path.slice(0, -1)) {\n\t\tparent = (parent as Record<string | number, Draft<JsonContainer>>)[segment]!;\n\t}\n\tconst slots = parent as Record<string | number, JsonValue>;\n\tif (change.kind === \"set\") slots[key] = value(change);\n\t// appendText() takes object members; a string in an array is reassigned, which compares it whole.\n\telse if (Array.isArray(parent)) slots[key] = `${slots[key] as string}${change.text}`;\n\telse appendText(parent as Draft<Record<string, string>>, String(key), change.text);\n}\n\n/** The value a `set` change reports: JSON data, which pi-ai types with read-only arrays. */\nfunction value(change: Extract<JsonChange, { kind: \"set\" }>): JsonValue {\n\treturn change.value as JsonValue;\n}\n\nfunction isRecord(value: JsonValue | undefined): value is Record<string, JsonValue> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
@@ -8,7 +8,7 @@ export declare function replaySections(messages: readonly Message[]): Map<string
8
8
  * Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\n...\n</key>`. A
9
9
  * section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.
10
10
  */
11
- export declare function renderSections<Tool extends ToolRegistration>(sections: readonly PromptSection<Tool>[], input: PromptInput<Tool>, shown: ReadonlyMap<string, string>, report: (error: unknown) => void, context: Context): Promise<Map<string, string>>;
11
+ export declare function renderSections<Tool extends ToolRegistration>(sections: readonly PromptSection<Tool>[], input: PromptInput<Tool>, shown: ReadonlyMap<string, string>, report: (error: unknown, section: PromptSection<Tool>) => void, context: Context): Promise<Map<string, string>>;
12
12
  type SystemDraft = TypedEntryDraft<never>;
13
13
  /**
14
14
  * Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiB,IAAI,EAAiB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5F,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUhF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,SAAS,gBAAgB,EACjE,QAAQ,EAAE,SAAS,aAAa,CAAC,IAAI,CAAC,EAAE,EACxC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAChC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiB9B;AAED,KAAK,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;AAI1C;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,SAAS,EAAE,MAAM,GACf,WAAW,EAAE,CAsBf"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiB,IAAI,EAAiB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5F,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUhF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,SAAS,gBAAgB,EACjE,QAAQ,EAAE,SAAS,aAAa,CAAC,IAAI,CAAC,EAAE,EACxC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,IAAI,EAC9D,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiB9B;AAED,KAAK,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;AAI1C;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,SAAS,EAAE,MAAM,GACf,WAAW,EAAE,CAsBf"}
@@ -29,7 +29,7 @@ export async function renderSections(sections, input, shown, report, context) {
29
29
  catch (error) {
30
30
  if (context.abortSignal?.aborted)
31
31
  throw error;
32
- report(error);
32
+ report(error, section);
33
33
  const kept = shown.get(section.key);
34
34
  if (kept !== undefined)
35
35
  desired.set(section.key, kept);
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { Message, SystemMessage, Tool, ToolReference } from \"@earendil-works/pi-ai\";\nimport { declarationsEqual, getCurrentTools, toToolDeclaration } from \"@earendil-works/pi-ai/utils/transcript\";\nimport { SystemEntry } from \"../entries.ts\";\nimport type { ContextEdit, TypedEntryDraft } from \"../types.ts\";\nimport type { ContextView, PromptInput, PromptSection, ToolRegistration } from \"./types.ts\";\n\n/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */\nexport function replaySections(messages: readonly Message[]): Map<string, string> {\n\tconst shown = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"system\" || message.sections === undefined) continue;\n\t\tfor (const [key, value] of Object.entries(message.sections)) {\n\t\t\tif (value === null) shown.delete(key);\n\t\t\telse shown.set(key, value);\n\t\t}\n\t}\n\treturn shown;\n}\n\n/**\n * Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\\n...\\n</key>`. A\n * section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.\n */\nexport async function renderSections<Tool extends ToolRegistration>(\n\tsections: readonly PromptSection<Tool>[],\n\tinput: PromptInput<Tool>,\n\tshown: ReadonlyMap<string, string>,\n\treport: (error: unknown) => void,\n\tcontext: Context,\n): Promise<Map<string, string>> {\n\tconst desired = new Map<string, string>();\n\tfor (const section of sections) {\n\t\tlet text: string | undefined;\n\t\ttry {\n\t\t\ttext = await section.render(input, context);\n\t\t} catch (error) {\n\t\t\tif (context.abortSignal?.aborted) throw error;\n\t\t\treport(error);\n\t\t\tconst kept = shown.get(section.key);\n\t\t\tif (kept !== undefined) desired.set(section.key, kept);\n\t\t\tcontinue;\n\t\t}\n\t\tif (text === undefined) continue;\n\t\tdesired.set(section.key, section.tag === false ? text : `<${section.key}>\\n${text}\\n</${section.key}>`);\n\t}\n\treturn desired;\n}\n\ntype SystemDraft = TypedEntryDraft<never>;\n\ntype ToolChanges = { readonly toolsRemoved: ToolReference[]; readonly toolsAdded: Tool[] };\n\n/**\n * Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values\n * and order.\n *\n * - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier\n * `pi.system` entry, written even when it restates the replayed values.\n * - Otherwise, when a minimal section patch would leave a different order: remove every shown section, then re-add\n * every desired section in order.\n * - Otherwise the minimal patch of changed values and `null` removals, or nothing.\n *\n * Tool changes ride on the last planned entry, or on one entry of their own.\n */\nexport function planSystemEntries(\n\tview: ContextView,\n\tdesired: ReadonlyMap<string, string>,\n\ttools: readonly Tool[],\n\ttimestamp: number,\n): SystemDraft[] {\n\tconst head = view.head;\n\tif (head !== undefined && !view.entries.some((entry) => SystemEntry.is(entry) && entry.id > head.id)) {\n\t\tconst edits: ContextEdit[] = view.entries\n\t\t\t.filter((entry) => SystemEntry.is(entry))\n\t\t\t.map((entry) => ({ target: entry.id, action: \"omit\" }));\n\t\tconst baseline = systemEntry(\n\t\t\tObject.fromEntries(desired),\n\t\t\t{ toolsRemoved: [], toolsAdded: tools.map(toToolDeclaration) },\n\t\t\ttimestamp,\n\t\t);\n\t\treturn [edits.length === 0 ? baseline : { ...baseline, edits }];\n\t}\n\tconst sections = planSections(replaySections(view.messages), desired);\n\tconst changes = planTools(getCurrentTools(view.messages), tools);\n\tif (changes.toolsRemoved.length === 0 && changes.toolsAdded.length === 0) {\n\t\treturn sections.map((patch) => systemEntry(patch, undefined, timestamp));\n\t}\n\tif (sections.length === 0) return [systemEntry(undefined, changes, timestamp)];\n\treturn sections.map((patch, index) =>\n\t\tsystemEntry(patch, index === sections.length - 1 ? changes : undefined, timestamp),\n\t);\n}\n\n/**\n * Tool changes from `offered` to `desired`. A changed declaration is removed and re-added. Replay keeps retained tools\n * in place and appends additions; when that would not yield the desired order, every offered tool is removed and\n * every desired tool re-added in order.\n */\nfunction planTools(offered: readonly Tool[], desired: readonly Tool[]): ToolChanges {\n\tconst wanted = new Map(desired.map((tool) => [tool.name, tool]));\n\tconst kept = offered.filter((tool) => {\n\t\tconst next = wanted.get(tool.name);\n\t\treturn next !== undefined && declarationsEqual(tool, next);\n\t});\n\tconst keptNames = new Set(kept.map((tool) => tool.name));\n\tconst added = desired.filter((tool) => !keptNames.has(tool.name));\n\tconst replayed = [...kept, ...added];\n\tif (replayed.some((tool, index) => tool.name !== desired[index]!.name)) {\n\t\treturn {\n\t\t\ttoolsRemoved: offered.map((tool) => ({ name: tool.name })),\n\t\t\ttoolsAdded: desired.map(toToolDeclaration),\n\t\t};\n\t}\n\treturn {\n\t\ttoolsRemoved: offered.filter((tool) => !keptNames.has(tool.name)).map((tool) => ({ name: tool.name })),\n\t\ttoolsAdded: added.map(toToolDeclaration),\n\t};\n}\n\n/** Section patches: none, the minimal patch, or a remove-all/re-add-all pair when the order would differ. */\nfunction planSections(\n\tshown: ReadonlyMap<string, string>,\n\tdesired: ReadonlyMap<string, string>,\n): Record<string, string | null>[] {\n\tconst patchedOrder = [\n\t\t...[...shown.keys()].filter((key) => desired.has(key)),\n\t\t...[...desired.keys()].filter((key) => !shown.has(key)),\n\t];\n\tconst desiredOrder = [...desired.keys()];\n\tif (patchedOrder.some((key, index) => key !== desiredOrder[index])) {\n\t\treturn [Object.fromEntries([...shown.keys()].map((key) => [key, null])), Object.fromEntries(desired)];\n\t}\n\n\tconst patch: Record<string, string | null> = {};\n\tfor (const [key, value] of shown) {\n\t\tconst next = desired.get(key);\n\t\tif (next !== value) patch[key] = next ?? null;\n\t}\n\tfor (const [key, value] of desired) if (!shown.has(key)) patch[key] = value;\n\treturn Object.keys(patch).length === 0 ? [] : [patch];\n}\n\nfunction systemEntry(\n\tsections: Record<string, string | null> | undefined,\n\ttools: ToolChanges | undefined,\n\ttimestamp: number,\n): SystemDraft {\n\tconst message: SystemMessage = {\n\t\trole: \"system\",\n\t\tcontent: \"\",\n\t\t...(sections === undefined ? {} : { sections }),\n\t\t...(tools === undefined || tools.toolsRemoved.length === 0 ? {} : { toolsRemoved: tools.toolsRemoved }),\n\t\t...(tools === undefined || tools.toolsAdded.length === 0 ? {} : { toolsAdded: tools.toolsAdded }),\n\t\ttimestamp,\n\t};\n\treturn { model: [message] };\n}\n"]}
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { Message, SystemMessage, Tool, ToolReference } from \"@earendil-works/pi-ai\";\nimport { declarationsEqual, getCurrentTools, toToolDeclaration } from \"@earendil-works/pi-ai/utils/transcript\";\nimport { SystemEntry } from \"../entries.ts\";\nimport type { ContextEdit, TypedEntryDraft } from \"../types.ts\";\nimport type { ContextView, PromptInput, PromptSection, ToolRegistration } from \"./types.ts\";\n\n/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */\nexport function replaySections(messages: readonly Message[]): Map<string, string> {\n\tconst shown = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"system\" || message.sections === undefined) continue;\n\t\tfor (const [key, value] of Object.entries(message.sections)) {\n\t\t\tif (value === null) shown.delete(key);\n\t\t\telse shown.set(key, value);\n\t\t}\n\t}\n\treturn shown;\n}\n\n/**\n * Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\\n...\\n</key>`. A\n * section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.\n */\nexport async function renderSections<Tool extends ToolRegistration>(\n\tsections: readonly PromptSection<Tool>[],\n\tinput: PromptInput<Tool>,\n\tshown: ReadonlyMap<string, string>,\n\treport: (error: unknown, section: PromptSection<Tool>) => void,\n\tcontext: Context,\n): Promise<Map<string, string>> {\n\tconst desired = new Map<string, string>();\n\tfor (const section of sections) {\n\t\tlet text: string | undefined;\n\t\ttry {\n\t\t\ttext = await section.render(input, context);\n\t\t} catch (error) {\n\t\t\tif (context.abortSignal?.aborted) throw error;\n\t\t\treport(error, section);\n\t\t\tconst kept = shown.get(section.key);\n\t\t\tif (kept !== undefined) desired.set(section.key, kept);\n\t\t\tcontinue;\n\t\t}\n\t\tif (text === undefined) continue;\n\t\tdesired.set(section.key, section.tag === false ? text : `<${section.key}>\\n${text}\\n</${section.key}>`);\n\t}\n\treturn desired;\n}\n\ntype SystemDraft = TypedEntryDraft<never>;\n\ntype ToolChanges = { readonly toolsRemoved: ToolReference[]; readonly toolsAdded: Tool[] };\n\n/**\n * Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values\n * and order.\n *\n * - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier\n * `pi.system` entry, written even when it restates the replayed values.\n * - Otherwise, when a minimal section patch would leave a different order: remove every shown section, then re-add\n * every desired section in order.\n * - Otherwise the minimal patch of changed values and `null` removals, or nothing.\n *\n * Tool changes ride on the last planned entry, or on one entry of their own.\n */\nexport function planSystemEntries(\n\tview: ContextView,\n\tdesired: ReadonlyMap<string, string>,\n\ttools: readonly Tool[],\n\ttimestamp: number,\n): SystemDraft[] {\n\tconst head = view.head;\n\tif (head !== undefined && !view.entries.some((entry) => SystemEntry.is(entry) && entry.id > head.id)) {\n\t\tconst edits: ContextEdit[] = view.entries\n\t\t\t.filter((entry) => SystemEntry.is(entry))\n\t\t\t.map((entry) => ({ target: entry.id, action: \"omit\" }));\n\t\tconst baseline = systemEntry(\n\t\t\tObject.fromEntries(desired),\n\t\t\t{ toolsRemoved: [], toolsAdded: tools.map(toToolDeclaration) },\n\t\t\ttimestamp,\n\t\t);\n\t\treturn [edits.length === 0 ? baseline : { ...baseline, edits }];\n\t}\n\tconst sections = planSections(replaySections(view.messages), desired);\n\tconst changes = planTools(getCurrentTools(view.messages), tools);\n\tif (changes.toolsRemoved.length === 0 && changes.toolsAdded.length === 0) {\n\t\treturn sections.map((patch) => systemEntry(patch, undefined, timestamp));\n\t}\n\tif (sections.length === 0) return [systemEntry(undefined, changes, timestamp)];\n\treturn sections.map((patch, index) =>\n\t\tsystemEntry(patch, index === sections.length - 1 ? changes : undefined, timestamp),\n\t);\n}\n\n/**\n * Tool changes from `offered` to `desired`. A changed declaration is removed and re-added. Replay keeps retained tools\n * in place and appends additions; when that would not yield the desired order, every offered tool is removed and\n * every desired tool re-added in order.\n */\nfunction planTools(offered: readonly Tool[], desired: readonly Tool[]): ToolChanges {\n\tconst wanted = new Map(desired.map((tool) => [tool.name, tool]));\n\tconst kept = offered.filter((tool) => {\n\t\tconst next = wanted.get(tool.name);\n\t\treturn next !== undefined && declarationsEqual(tool, next);\n\t});\n\tconst keptNames = new Set(kept.map((tool) => tool.name));\n\tconst added = desired.filter((tool) => !keptNames.has(tool.name));\n\tconst replayed = [...kept, ...added];\n\tif (replayed.some((tool, index) => tool.name !== desired[index]!.name)) {\n\t\treturn {\n\t\t\ttoolsRemoved: offered.map((tool) => ({ name: tool.name })),\n\t\t\ttoolsAdded: desired.map(toToolDeclaration),\n\t\t};\n\t}\n\treturn {\n\t\ttoolsRemoved: offered.filter((tool) => !keptNames.has(tool.name)).map((tool) => ({ name: tool.name })),\n\t\ttoolsAdded: added.map(toToolDeclaration),\n\t};\n}\n\n/** Section patches: none, the minimal patch, or a remove-all/re-add-all pair when the order would differ. */\nfunction planSections(\n\tshown: ReadonlyMap<string, string>,\n\tdesired: ReadonlyMap<string, string>,\n): Record<string, string | null>[] {\n\tconst patchedOrder = [\n\t\t...[...shown.keys()].filter((key) => desired.has(key)),\n\t\t...[...desired.keys()].filter((key) => !shown.has(key)),\n\t];\n\tconst desiredOrder = [...desired.keys()];\n\tif (patchedOrder.some((key, index) => key !== desiredOrder[index])) {\n\t\treturn [Object.fromEntries([...shown.keys()].map((key) => [key, null])), Object.fromEntries(desired)];\n\t}\n\n\tconst patch: Record<string, string | null> = {};\n\tfor (const [key, value] of shown) {\n\t\tconst next = desired.get(key);\n\t\tif (next !== value) patch[key] = next ?? null;\n\t}\n\tfor (const [key, value] of desired) if (!shown.has(key)) patch[key] = value;\n\treturn Object.keys(patch).length === 0 ? [] : [patch];\n}\n\nfunction systemEntry(\n\tsections: Record<string, string | null> | undefined,\n\ttools: ToolChanges | undefined,\n\ttimestamp: number,\n): SystemDraft {\n\tconst message: SystemMessage = {\n\t\trole: \"system\",\n\t\tcontent: \"\",\n\t\t...(sections === undefined ? {} : { sections }),\n\t\t...(tools === undefined || tools.toolsRemoved.length === 0 ? {} : { toolsRemoved: tools.toolsRemoved }),\n\t\t...(tools === undefined || tools.toolsAdded.length === 0 ? {} : { toolsAdded: tools.toolsAdded }),\n\t\ttimestamp,\n\t};\n\treturn { model: [message] };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { Context } from "@earendil-works/chord/context";
2
+ import type { RegistryValue } from "@earendil-works/chord/next";
3
+ import type { RegistryReader, RegistrySnapshot, ToolRegistration } from "./types.ts";
4
+ /**
5
+ * A `RegistryReader` over a Chord registry value whose composition is a registry snapshot: `snapshot()` reads the
6
+ * current composition (the last good one while `compose` fails), `subscribe` observes its compositions, its listener
7
+ * called synchronously inside the Chord batch that composed (so it only wakes the scheduler; a Chord call there
8
+ * throws `invalid/reentrant`). `context` is the observation's: aborting it detaches every listener.
9
+ */
10
+ export declare function registryReader<Tool extends ToolRegistration = ToolRegistration>(value: RegistryValue<RegistrySnapshot<Tool>>, context: Context): RegistryReader<Tool>;
11
+ //# sourceMappingURL=registry-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-reader.d.ts","sourceRoot":"","sources":["../../src/harness/registry-reader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EAC9E,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAC5C,OAAO,EAAE,OAAO,GACd,cAAc,CAAC,IAAI,CAAC,CAiBtB"}
@@ -0,0 +1,25 @@
1
+ // A Harness's registry over a Chord registry (Chord v2 slice 8 §5): the composed value of p3's extensions registry,
2
+ // read as Durable's `RegistryReader`.
3
+ /**
4
+ * A `RegistryReader` over a Chord registry value whose composition is a registry snapshot: `snapshot()` reads the
5
+ * current composition (the last good one while `compose` fails), `subscribe` observes its compositions, its listener
6
+ * called synchronously inside the Chord batch that composed (so it only wakes the scheduler; a Chord call there
7
+ * throws `invalid/reentrant`). `context` is the observation's: aborting it detaches every listener.
8
+ */
9
+ export function registryReader(value, context) {
10
+ return {
11
+ snapshot: () => value.value,
12
+ subscribe: (listener) => {
13
+ const observation = value.observe({
14
+ advance: (call) => {
15
+ // A failed composition keeps the last snapshot: nothing to reconsider.
16
+ if (call.kind === "value")
17
+ listener();
18
+ },
19
+ end: () => { },
20
+ }, context);
21
+ return () => observation.detach();
22
+ },
23
+ };
24
+ }
25
+ //# sourceMappingURL=registry-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-reader.js","sourceRoot":"","sources":["../../src/harness/registry-reader.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,sCAAsC;AAMtC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC7B,KAA4C,EAC5C,OAAgB;IAEhB,OAAO;QACN,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK;QAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE;YACvB,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAChC;gBACC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;oBACjB,uEAAuE;oBACvE,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;wBAAE,QAAQ,EAAE,CAAC;gBACvC,CAAC;gBACD,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC;aACb,EACD,OAAO,CACP,CAAC;YACF,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QACnC,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["// A Harness's registry over a Chord registry (Chord v2 slice 8 §5): the composed value of p3's extensions registry,\n// read as Durable's `RegistryReader`.\n\nimport type { Context } from \"@earendil-works/chord/context\";\nimport type { RegistryValue } from \"@earendil-works/chord/next\";\nimport type { RegistryReader, RegistrySnapshot, ToolRegistration } from \"./types.ts\";\n\n/**\n * A `RegistryReader` over a Chord registry value whose composition is a registry snapshot: `snapshot()` reads the\n * current composition (the last good one while `compose` fails), `subscribe` observes its compositions, its listener\n * called synchronously inside the Chord batch that composed (so it only wakes the scheduler; a Chord call there\n * throws `invalid/reentrant`). `context` is the observation's: aborting it detaches every listener.\n */\nexport function registryReader<Tool extends ToolRegistration = ToolRegistration>(\n\tvalue: RegistryValue<RegistrySnapshot<Tool>>,\n\tcontext: Context,\n): RegistryReader<Tool> {\n\treturn {\n\t\tsnapshot: () => value.value,\n\t\tsubscribe: (listener) => {\n\t\t\tconst observation = value.observe(\n\t\t\t\t{\n\t\t\t\t\tadvance: (call) => {\n\t\t\t\t\t\t// A failed composition keeps the last snapshot: nothing to reconsider.\n\t\t\t\t\t\tif (call.kind === \"value\") listener();\n\t\t\t\t\t},\n\t\t\t\t\tend: () => {},\n\t\t\t\t},\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\treturn () => observation.detach();\n\t\t},\n\t};\n}\n"]}