@earendil-works/pi-agent-core 0.84.4 → 0.85.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 (343) hide show
  1. package/README.md +4 -0
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +9 -0
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/harness/agent-harness.d.ts +639 -384
  6. package/dist/harness/agent-harness.d.ts.map +1 -1
  7. package/dist/harness/agent-harness.js +5 -251
  8. package/dist/harness/agent-harness.js.map +1 -1
  9. package/dist/harness/compaction/branch-summarization.d.ts +12 -6
  10. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  11. package/dist/harness/compaction/branch-summarization.js +33 -26
  12. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  13. package/dist/harness/compaction/compaction.d.ts +30 -8
  14. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  15. package/dist/harness/compaction/compaction.js +53 -63
  16. package/dist/harness/compaction/compaction.js.map +1 -1
  17. package/dist/harness/config.d.ts +9 -0
  18. package/dist/harness/config.d.ts.map +1 -0
  19. package/dist/harness/config.js +27 -0
  20. package/dist/harness/config.js.map +1 -0
  21. package/dist/harness/context.d.ts +9 -0
  22. package/dist/harness/context.d.ts.map +1 -0
  23. package/dist/harness/context.js +13 -0
  24. package/dist/harness/context.js.map +1 -0
  25. package/dist/harness/env/nodejs.d.ts +24 -28
  26. package/dist/harness/env/nodejs.d.ts.map +1 -1
  27. package/dist/harness/env/nodejs.js +239 -86
  28. package/dist/harness/env/nodejs.js.map +1 -1
  29. package/dist/harness/events.d.ts +20 -38
  30. package/dist/harness/events.d.ts.map +1 -1
  31. package/dist/harness/events.js +240 -55
  32. package/dist/harness/events.js.map +1 -1
  33. package/dist/harness/execution/assistant.d.ts +42 -0
  34. package/dist/harness/execution/assistant.d.ts.map +1 -0
  35. package/dist/harness/execution/assistant.js +82 -0
  36. package/dist/harness/execution/assistant.js.map +1 -0
  37. package/dist/harness/execution/effect-gate.d.ts +22 -0
  38. package/dist/harness/execution/effect-gate.d.ts.map +1 -0
  39. package/dist/harness/execution/effect-gate.js +49 -0
  40. package/dist/harness/execution/effect-gate.js.map +1 -0
  41. package/dist/harness/execution/tools.d.ts +67 -0
  42. package/dist/harness/execution/tools.d.ts.map +1 -0
  43. package/dist/harness/execution/tools.js +121 -0
  44. package/dist/harness/execution/tools.js.map +1 -0
  45. package/dist/harness/hooks.d.ts +38 -0
  46. package/dist/harness/hooks.d.ts.map +1 -0
  47. package/dist/harness/hooks.js +406 -0
  48. package/dist/harness/hooks.js.map +1 -0
  49. package/dist/harness/messages.d.ts +2 -2
  50. package/dist/harness/messages.d.ts.map +1 -1
  51. package/dist/harness/messages.js.map +1 -1
  52. package/dist/harness/prompt-templates.d.ts +3 -2
  53. package/dist/harness/prompt-templates.d.ts.map +1 -1
  54. package/dist/harness/prompt-templates.js +17 -17
  55. package/dist/harness/prompt-templates.js.map +1 -1
  56. package/dist/harness/result.d.ts +93 -0
  57. package/dist/harness/result.d.ts.map +1 -1
  58. package/dist/harness/result.js +40 -0
  59. package/dist/harness/result.js.map +1 -1
  60. package/dist/harness/runtime/drive/boundary.d.ts +28 -0
  61. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
  62. package/dist/harness/runtime/drive/boundary.js +170 -0
  63. package/dist/harness/runtime/drive/boundary.js.map +1 -0
  64. package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
  65. package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
  66. package/dist/harness/runtime/drive/checkpoint.js +133 -0
  67. package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
  68. package/dist/harness/runtime/drive/deferred.d.ts +15 -0
  69. package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
  70. package/dist/harness/runtime/drive/deferred.js +179 -0
  71. package/dist/harness/runtime/drive/deferred.js.map +1 -0
  72. package/dist/harness/runtime/drive/generation.d.ts +8 -0
  73. package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
  74. package/dist/harness/runtime/drive/generation.js +205 -0
  75. package/dist/harness/runtime/drive/generation.js.map +1 -0
  76. package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
  77. package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
  78. package/dist/harness/runtime/drive/reconcile.js +140 -0
  79. package/dist/harness/runtime/drive/reconcile.js.map +1 -0
  80. package/dist/harness/runtime/drive/recovery.d.ts +8 -0
  81. package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
  82. package/dist/harness/runtime/drive/recovery.js +85 -0
  83. package/dist/harness/runtime/drive/recovery.js.map +1 -0
  84. package/dist/harness/runtime/drive/response.d.ts +23 -0
  85. package/dist/harness/runtime/drive/response.d.ts.map +1 -0
  86. package/dist/harness/runtime/drive/response.js +394 -0
  87. package/dist/harness/runtime/drive/response.js.map +1 -0
  88. package/dist/harness/runtime/drive/retry.d.ts +4 -0
  89. package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
  90. package/dist/harness/runtime/drive/retry.js +37 -0
  91. package/dist/harness/runtime/drive/retry.js.map +1 -0
  92. package/dist/harness/runtime/drive/structural.d.ts +32 -0
  93. package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
  94. package/dist/harness/runtime/drive/structural.js +929 -0
  95. package/dist/harness/runtime/drive/structural.js.map +1 -0
  96. package/dist/harness/runtime/drive/terminal.d.ts +7 -0
  97. package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
  98. package/dist/harness/runtime/drive/terminal.js +49 -0
  99. package/dist/harness/runtime/drive/terminal.js.map +1 -0
  100. package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
  101. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
  102. package/dist/harness/runtime/drive/tool-placement.js +244 -0
  103. package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
  104. package/dist/harness/runtime/drive/tools.d.ts +6 -0
  105. package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
  106. package/dist/harness/runtime/drive/tools.js +449 -0
  107. package/dist/harness/runtime/drive/tools.js.map +1 -0
  108. package/dist/harness/runtime/drive.d.ts +6 -0
  109. package/dist/harness/runtime/drive.d.ts.map +1 -0
  110. package/dist/harness/runtime/drive.js +93 -0
  111. package/dist/harness/runtime/drive.js.map +1 -0
  112. package/dist/harness/runtime/harness.d.ts +59 -0
  113. package/dist/harness/runtime/harness.d.ts.map +1 -0
  114. package/dist/harness/runtime/harness.js +311 -0
  115. package/dist/harness/runtime/harness.js.map +1 -0
  116. package/dist/harness/runtime/index.d.ts +2 -0
  117. package/dist/harness/runtime/index.d.ts.map +1 -0
  118. package/dist/harness/runtime/index.js +2 -0
  119. package/dist/harness/runtime/index.js.map +1 -0
  120. package/dist/harness/runtime/lane.d.ts +101 -0
  121. package/dist/harness/runtime/lane.d.ts.map +1 -0
  122. package/dist/harness/runtime/lane.js +1575 -0
  123. package/dist/harness/runtime/lane.js.map +1 -0
  124. package/dist/harness/runtime/progress.d.ts +15 -0
  125. package/dist/harness/runtime/progress.d.ts.map +1 -0
  126. package/dist/harness/runtime/progress.js +66 -0
  127. package/dist/harness/runtime/progress.js.map +1 -0
  128. package/dist/harness/runtime/reducer.d.ts +5 -0
  129. package/dist/harness/runtime/reducer.d.ts.map +1 -0
  130. package/dist/harness/runtime/reducer.js +240 -0
  131. package/dist/harness/runtime/reducer.js.map +1 -0
  132. package/dist/harness/runtime/restore.d.ts +24 -0
  133. package/dist/harness/runtime/restore.d.ts.map +1 -0
  134. package/dist/harness/runtime/restore.js +117 -0
  135. package/dist/harness/runtime/restore.js.map +1 -0
  136. package/dist/harness/runtime/transcript.d.ts +21 -0
  137. package/dist/harness/runtime/transcript.d.ts.map +1 -0
  138. package/dist/harness/runtime/transcript.js +72 -0
  139. package/dist/harness/runtime/transcript.js.map +1 -0
  140. package/dist/harness/runtime/types.d.ts +105 -0
  141. package/dist/harness/runtime/types.d.ts.map +1 -0
  142. package/dist/harness/runtime/types.js +61 -0
  143. package/dist/harness/runtime/types.js.map +1 -0
  144. package/dist/harness/session/commit.d.ts +53 -0
  145. package/dist/harness/session/commit.d.ts.map +1 -0
  146. package/dist/harness/session/commit.js +57 -0
  147. package/dist/harness/session/commit.js.map +1 -0
  148. package/dist/harness/session/context.d.ts +6 -18
  149. package/dist/harness/session/context.d.ts.map +1 -1
  150. package/dist/harness/session/context.js +31 -49
  151. package/dist/harness/session/context.js.map +1 -1
  152. package/dist/harness/session/fork-policy.d.ts +13 -0
  153. package/dist/harness/session/fork-policy.d.ts.map +1 -0
  154. package/dist/harness/session/fork-policy.js +22 -0
  155. package/dist/harness/session/fork-policy.js.map +1 -0
  156. package/dist/harness/session/fork.d.ts +18 -0
  157. package/dist/harness/session/fork.d.ts.map +1 -0
  158. package/dist/harness/session/fork.js +127 -0
  159. package/dist/harness/session/fork.js.map +1 -0
  160. package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
  161. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
  162. package/dist/harness/session/in-memory-storage-state.js +216 -0
  163. package/dist/harness/session/in-memory-storage-state.js.map +1 -0
  164. package/dist/harness/session/index.d.ts +9 -5
  165. package/dist/harness/session/index.d.ts.map +1 -1
  166. package/dist/harness/session/index.js +7 -4
  167. package/dist/harness/session/index.js.map +1 -1
  168. package/dist/harness/session/jsonl/codec.d.ts +19 -8
  169. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  170. package/dist/harness/session/jsonl/codec.js +34 -218
  171. package/dist/harness/session/jsonl/codec.js.map +1 -1
  172. package/dist/harness/session/jsonl/index.d.ts +4 -0
  173. package/dist/harness/session/jsonl/index.d.ts.map +1 -0
  174. package/dist/harness/session/jsonl/index.js +4 -0
  175. package/dist/harness/session/jsonl/index.js.map +1 -0
  176. package/dist/harness/session/jsonl/legacy-v3.d.ts +18 -0
  177. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
  178. package/dist/harness/session/jsonl/legacy-v3.js +383 -0
  179. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
  180. package/dist/harness/session/jsonl/repo.d.ts +26 -19
  181. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  182. package/dist/harness/session/jsonl/repo.js +266 -156
  183. package/dist/harness/session/jsonl/repo.js.map +1 -1
  184. package/dist/harness/session/jsonl/storage.d.ts +39 -41
  185. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  186. package/dist/harness/session/jsonl/storage.js +238 -181
  187. package/dist/harness/session/jsonl/storage.js.map +1 -1
  188. package/dist/harness/session/jsonl/types.d.ts +23 -22
  189. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  190. package/dist/harness/session/jsonl/types.js +2 -1
  191. package/dist/harness/session/jsonl/types.js.map +1 -1
  192. package/dist/harness/session/memory.d.ts +46 -40
  193. package/dist/harness/session/memory.d.ts.map +1 -1
  194. package/dist/harness/session/memory.js +346 -113
  195. package/dist/harness/session/memory.js.map +1 -1
  196. package/dist/harness/session/mutation-line.d.ts +8 -0
  197. package/dist/harness/session/mutation-line.d.ts.map +1 -0
  198. package/dist/harness/session/mutation-line.js +21 -0
  199. package/dist/harness/session/mutation-line.js.map +1 -0
  200. package/dist/harness/session/session.d.ts +78 -46
  201. package/dist/harness/session/session.d.ts.map +1 -1
  202. package/dist/harness/session/session.js +343 -209
  203. package/dist/harness/session/session.js.map +1 -1
  204. package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
  205. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
  206. package/dist/harness/session/testing/benchmark/datasets.js +24 -0
  207. package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
  208. package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
  209. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
  210. package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
  211. package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
  212. package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
  213. package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
  214. package/dist/harness/session/testing/benchmark/storage.js +114 -0
  215. package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
  216. package/dist/harness/session/testing/conformance/session-repo.d.ts +21 -0
  217. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
  218. package/dist/harness/session/testing/conformance/session-repo.js +590 -0
  219. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
  220. package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
  221. package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
  222. package/dist/harness/session/testing/conformance/storage.js +600 -0
  223. package/dist/harness/session/testing/conformance/storage.js.map +1 -0
  224. package/dist/harness/session/testing/gating-storage.d.ts +26 -0
  225. package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
  226. package/dist/harness/session/testing/gating-storage.js +100 -0
  227. package/dist/harness/session/testing/gating-storage.js.map +1 -0
  228. package/dist/harness/session/testing/index.d.ts +9 -2
  229. package/dist/harness/session/testing/index.d.ts.map +1 -1
  230. package/dist/harness/session/testing/index.js +8 -1
  231. package/dist/harness/session/testing/index.js.map +1 -1
  232. package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
  233. package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
  234. package/dist/harness/session/testing/instrumented-storage.js +16 -0
  235. package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
  236. package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
  237. package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
  238. package/dist/harness/session/testing/storage-decorator.js +41 -0
  239. package/dist/harness/session/testing/storage-decorator.js.map +1 -0
  240. package/dist/harness/session/testing/types.d.ts +5 -7
  241. package/dist/harness/session/testing/types.d.ts.map +1 -1
  242. package/dist/harness/session/testing/types.js.map +1 -1
  243. package/dist/harness/session/types.d.ts +424 -259
  244. package/dist/harness/session/types.d.ts.map +1 -1
  245. package/dist/harness/session/types.js +7 -8
  246. package/dist/harness/session/types.js.map +1 -1
  247. package/dist/harness/session/values.d.ts +95 -0
  248. package/dist/harness/session/values.d.ts.map +1 -0
  249. package/dist/harness/session/values.js +81 -0
  250. package/dist/harness/session/values.js.map +1 -0
  251. package/dist/harness/skills.d.ts +3 -2
  252. package/dist/harness/skills.d.ts.map +1 -1
  253. package/dist/harness/skills.js +29 -26
  254. package/dist/harness/skills.js.map +1 -1
  255. package/dist/harness/telemetry.d.ts +64 -43
  256. package/dist/harness/telemetry.d.ts.map +1 -1
  257. package/dist/harness/telemetry.js +45 -26
  258. package/dist/harness/telemetry.js.map +1 -1
  259. package/dist/harness/tools/bash.d.ts +4 -4
  260. package/dist/harness/tools/bash.d.ts.map +1 -1
  261. package/dist/harness/tools/bash.js +66 -93
  262. package/dist/harness/tools/bash.js.map +1 -1
  263. package/dist/harness/tools/edit.d.ts.map +1 -1
  264. package/dist/harness/tools/edit.js +10 -10
  265. package/dist/harness/tools/edit.js.map +1 -1
  266. package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
  267. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  268. package/dist/harness/tools/file-mutation-queue.js +5 -5
  269. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  270. package/dist/harness/tools/path-utils.d.ts +3 -2
  271. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  272. package/dist/harness/tools/path-utils.js +5 -5
  273. package/dist/harness/tools/path-utils.js.map +1 -1
  274. package/dist/harness/tools/read.d.ts +2 -1
  275. package/dist/harness/tools/read.d.ts.map +1 -1
  276. package/dist/harness/tools/read.js +4 -6
  277. package/dist/harness/tools/read.js.map +1 -1
  278. package/dist/harness/tools/write.d.ts.map +1 -1
  279. package/dist/harness/tools/write.js +7 -7
  280. package/dist/harness/tools/write.js.map +1 -1
  281. package/dist/harness/types.d.ts +108 -45
  282. package/dist/harness/types.d.ts.map +1 -1
  283. package/dist/harness/types.js.map +1 -1
  284. package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
  285. package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
  286. package/dist/harness/utils/adaptive-publisher.js +79 -0
  287. package/dist/harness/utils/adaptive-publisher.js.map +1 -0
  288. package/dist/harness/utils/output-capture.d.ts +31 -0
  289. package/dist/harness/utils/output-capture.d.ts.map +1 -0
  290. package/dist/harness/utils/output-capture.js +217 -0
  291. package/dist/harness/utils/output-capture.js.map +1 -0
  292. package/dist/harness/utils/shell-output.d.ts +12 -5
  293. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  294. package/dist/harness/utils/shell-output.js +64 -174
  295. package/dist/harness/utils/shell-output.js.map +1 -1
  296. package/dist/harness/utils/truncate.d.ts +1 -0
  297. package/dist/harness/utils/truncate.d.ts.map +1 -1
  298. package/dist/harness/utils/truncate.js +1 -1
  299. package/dist/harness/utils/truncate.js.map +1 -1
  300. package/dist/harness/utils/usage.d.ts +4 -0
  301. package/dist/harness/utils/usage.d.ts.map +1 -0
  302. package/dist/harness/utils/usage.js +33 -0
  303. package/dist/harness/utils/usage.js.map +1 -0
  304. package/dist/index.d.ts +4 -1
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +3 -6
  307. package/dist/index.js.map +1 -1
  308. package/dist/proxy.d.ts +2 -0
  309. package/dist/proxy.d.ts.map +1 -1
  310. package/dist/proxy.js +40 -10
  311. package/dist/proxy.js.map +1 -1
  312. package/dist/search/index.d.ts +24 -16
  313. package/dist/search/index.d.ts.map +1 -1
  314. package/dist/search/index.js +1 -1
  315. package/dist/search/index.js.map +1 -1
  316. package/dist/types.d.ts +2 -0
  317. package/dist/types.d.ts.map +1 -1
  318. package/dist/types.js.map +1 -1
  319. package/package.json +25 -4
  320. package/dist/harness/reducer.d.ts +0 -100
  321. package/dist/harness/reducer.d.ts.map +0 -1
  322. package/dist/harness/reducer.js +0 -415
  323. package/dist/harness/reducer.js.map +0 -1
  324. package/dist/harness/session/jsonl/errors.d.ts +0 -9
  325. package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
  326. package/dist/harness/session/jsonl/errors.js +0 -19
  327. package/dist/harness/session/jsonl/errors.js.map +0 -1
  328. package/dist/harness/session/jsonl.d.ts +0 -3
  329. package/dist/harness/session/jsonl.d.ts.map +0 -1
  330. package/dist/harness/session/jsonl.js +0 -2
  331. package/dist/harness/session/jsonl.js.map +0 -1
  332. package/dist/harness/session/state.d.ts +0 -65
  333. package/dist/harness/session/state.d.ts.map +0 -1
  334. package/dist/harness/session/state.js +0 -319
  335. package/dist/harness/session/state.js.map +0 -1
  336. package/dist/harness/session/testing/conformance.d.ts +0 -4
  337. package/dist/harness/session/testing/conformance.d.ts.map +0 -1
  338. package/dist/harness/session/testing/conformance.js +0 -762
  339. package/dist/harness/session/testing/conformance.js.map +0 -1
  340. package/dist/search/scanning.d.ts +0 -29
  341. package/dist/search/scanning.d.ts.map +0 -1
  342. package/dist/search/scanning.js +0 -103
  343. package/dist/search/scanning.js.map +0 -1
@@ -0,0 +1,929 @@
1
+ import { isRetryableAssistantError, } from "@earendil-works/pi-ai";
2
+ import { generateBranchSummaryWithRequest } from "../../compaction/branch-summarization.js";
3
+ import { compactWithRequest, prepareCompaction, shouldCompact } from "../../compaction/compaction.js";
4
+ import { getTelemetryContext, withAbortSignal } from "../../context.js";
5
+ import { AbortRequested } from "../../execution/effect-gate.js";
6
+ import { applyStreamOptionsPatch } from "../../hooks.js";
7
+ import { insertEntry, insertUsage } from "../../session/commit.js";
8
+ import { SessionInvariantError } from "../../session/session.js";
9
+ import { operationScopeOf, } from "../../session/types.js";
10
+ import { branchTip, entryLabel, operationPreparation, setValue } from "../../session/values.js";
11
+ import { committedEntryEvents, readBoundedEntries } from "../transcript.js";
12
+ import { assistantReadyAtBoundary, boundaryPlacementEvents, finishRunBoundary, normalizedRetryPolicy, planBoundaryInbox, } from "./boundary.js";
13
+ import { retryDelay, retryNotBefore, waitUntil } from "./retry.js";
14
+ import { operationCleanupWrites, operationResultRecord } from "./terminal.js";
15
+ class StructuralCancelled extends Error {
16
+ constructor() {
17
+ super("Structural generation was cancelled");
18
+ this.name = "StructuralCancelled";
19
+ }
20
+ }
21
+ function durableFileOperations(fileOps) {
22
+ return {
23
+ read: [...fileOps.read],
24
+ written: [...fileOps.written],
25
+ edited: [...fileOps.edited],
26
+ };
27
+ }
28
+ export function durableCompactionPreparation(preparation) {
29
+ return {
30
+ kind: "compaction",
31
+ messagesToSummarize: preparation.messagesToSummarize,
32
+ turnPrefixMessages: preparation.turnPrefixMessages,
33
+ retainedTail: preparation.retainedTail,
34
+ isSplitTurn: preparation.isSplitTurn,
35
+ tokensBefore: preparation.tokensBefore,
36
+ ...(preparation.previousSummary === undefined ? {} : { previousSummary: preparation.previousSummary }),
37
+ fileOps: durableFileOperations(preparation.fileOps),
38
+ settings: preparation.settings,
39
+ };
40
+ }
41
+ export function durableBranchPreparation(preparation) {
42
+ return {
43
+ kind: "branch_summary",
44
+ messages: preparation.messages,
45
+ fileOps: durableFileOperations(preparation.fileOps),
46
+ totalTokens: preparation.totalTokens,
47
+ };
48
+ }
49
+ function fileOperations(fileOps) {
50
+ return {
51
+ read: new Set(fileOps.read),
52
+ written: new Set(fileOps.written),
53
+ edited: new Set(fileOps.edited),
54
+ };
55
+ }
56
+ function compactionPreparation(preparation) {
57
+ return {
58
+ messagesToSummarize: preparation.messagesToSummarize,
59
+ turnPrefixMessages: preparation.turnPrefixMessages,
60
+ retainedTail: preparation.retainedTail,
61
+ isSplitTurn: preparation.isSplitTurn,
62
+ tokensBefore: preparation.tokensBefore,
63
+ ...(preparation.previousSummary === undefined ? {} : { previousSummary: preparation.previousSummary }),
64
+ fileOps: fileOperations(preparation.fileOps),
65
+ settings: preparation.settings,
66
+ };
67
+ }
68
+ function branchPreparation(preparation) {
69
+ return {
70
+ messages: preparation.messages,
71
+ fileOps: fileOperations(preparation.fileOps),
72
+ totalTokens: preparation.totalTokens,
73
+ };
74
+ }
75
+ function summaryKind(task) {
76
+ return task.boundary.kind === "commit_navigation" ? "branch_summary" : "compaction";
77
+ }
78
+ function compactionReason(task) {
79
+ if (task.reason !== undefined)
80
+ return task.reason;
81
+ if (task.boundary.kind === "finish")
82
+ return "manual";
83
+ throw new SessionInvariantError(`In-run compaction task ${task.taskId} is missing its reason`);
84
+ }
85
+ function navigationBoundary(task) {
86
+ if (task.boundary.kind !== "commit_navigation") {
87
+ throw new SessionInvariantError(`Summary task ${task.taskId} is not a navigation`);
88
+ }
89
+ return task.boundary;
90
+ }
91
+ async function readStructuralPreparation(lane, drive, deciding) {
92
+ return lane.continueOperation(deciding, async (_state, current, _meta, reader) => {
93
+ const expected = summaryKind(current.task);
94
+ const stored = await reader.getValue(operationPreparation(drive.operationId, current.task.taskId), drive.context);
95
+ if (stored?.value.kind !== expected) {
96
+ throw new SessionInvariantError(`Structural task ${current.task.taskId} is missing its ${expected} preparation`);
97
+ }
98
+ if (current.task.boundary.kind === "commit_navigation") {
99
+ const targetId = current.task.boundary.targetId;
100
+ if (!(await reader.getEntries([targetId], drive.context)).has(targetId)) {
101
+ throw new SessionInvariantError(`Navigation target ${targetId} is missing`);
102
+ }
103
+ }
104
+ return {
105
+ kind: "return",
106
+ result: stored.value.kind === "compaction"
107
+ ? compactionPreparation(stored.value)
108
+ : branchPreparation(stored.value),
109
+ };
110
+ }, drive.context);
111
+ }
112
+ function summaryContext(lane, resultEntryId, configuration) {
113
+ return {
114
+ resultEntryId,
115
+ configuration,
116
+ streamOptions: { ...lane.readConfig().streamOptions, deferred: false },
117
+ retryPolicy: normalizedRetryPolicy(lane),
118
+ };
119
+ }
120
+ function usageEvent(row, writeIndex, commit, lane) {
121
+ return {
122
+ type: "usage",
123
+ lane,
124
+ row: { ...row, seq: commit.seqs[writeIndex] },
125
+ totals: commit.stats.usage,
126
+ };
127
+ }
128
+ function operationError(code, message, details) {
129
+ return { code, message, ...(details === undefined ? {} : { details }) };
130
+ }
131
+ async function publishStructuralOutcome(lane, drive, capability, outcome) {
132
+ const hookUsageId = (outcome.kind === "compaction" || outcome.kind === "branch_summary") &&
133
+ outcome.fromHook &&
134
+ outcome.result.usage !== undefined
135
+ ? lane.session.idGenerator.next()
136
+ : undefined;
137
+ const published = await lane.continueOperation(capability, async (state, current, meta, reader) => {
138
+ const expected = summaryKind(current.task);
139
+ let terminalCompactionEndedAt;
140
+ if ((outcome.kind === "compaction" || outcome.kind === "branch_summary") && outcome.kind !== expected) {
141
+ throw new SessionInvariantError(`Structural ${outcome.kind} result does not match ${expected} task ${current.task.taskId}`);
142
+ }
143
+ const writes = [];
144
+ const baseEvents = [];
145
+ let terminalTipId = state.tipId;
146
+ if (hookUsageId !== undefined && (outcome.kind === "compaction" || outcome.kind === "branch_summary")) {
147
+ const usage = outcome.result.usage;
148
+ if (usage === undefined)
149
+ throw new SessionInvariantError("Hook usage id exists without structural usage");
150
+ const row = { id: hookUsageId, usage, adjustment: false };
151
+ const writeIndex = writes.length;
152
+ writes.push(insertUsage(row));
153
+ baseEvents.push((commit) => [usageEvent(row, writeIndex, commit, lane.name)]);
154
+ }
155
+ if (outcome.kind === "compaction") {
156
+ const entry = {
157
+ id: outcome.resultEntryId,
158
+ parentId: state.tipId,
159
+ type: "compaction",
160
+ summary: outcome.result.summary,
161
+ retainedTail: outcome.result.retainedTail,
162
+ tokensBefore: outcome.result.tokensBefore,
163
+ ...(outcome.result.details === undefined ? {} : { details: outcome.result.details }),
164
+ ...(outcome.result.usage === undefined ? {} : { usage: outcome.result.usage }),
165
+ fromHook: outcome.fromHook,
166
+ };
167
+ const entryWriteIndex = writes.length;
168
+ writes.push(insertEntry(entry), setValue(branchTip(lane.name), outcome.resultEntryId));
169
+ terminalTipId = outcome.resultEntryId;
170
+ baseEvents.push((commit) => committedEntryEvents([entry], commit, lane.name, drive.operationId, entryWriteIndex));
171
+ }
172
+ else if (outcome.kind === "branch_summary") {
173
+ const boundary = navigationBoundary(current.task);
174
+ const entry = {
175
+ id: outcome.resultEntryId,
176
+ parentId: boundary.targetId,
177
+ type: "branch_summary",
178
+ fromId: meta.sourceTipId,
179
+ summary: outcome.result.summary,
180
+ details: { readFiles: outcome.result.readFiles, modifiedFiles: outcome.result.modifiedFiles },
181
+ ...(outcome.result.usage === undefined ? {} : { usage: outcome.result.usage }),
182
+ fromHook: outcome.fromHook,
183
+ };
184
+ writes.push(setValue(branchTip(lane.name), boundary.targetId));
185
+ const entryWriteIndex = writes.length;
186
+ writes.push(insertEntry(entry), setValue(branchTip(lane.name), outcome.resultEntryId));
187
+ if (boundary.label !== undefined)
188
+ writes.push(setValue(entryLabel(boundary.targetId), boundary.label));
189
+ terminalTipId = outcome.resultEntryId;
190
+ baseEvents.push((commit) => committedEntryEvents([entry], commit, lane.name, drive.operationId, entryWriteIndex));
191
+ }
192
+ const attempt = current.at === "summary.ready"
193
+ ? current.nextAttempt
194
+ : current.at === "summary.effect_pending"
195
+ ? current.attempt
196
+ : undefined;
197
+ if (attempt !== undefined && attempt > 1) {
198
+ baseEvents.push(() => [
199
+ {
200
+ type: "retry_end",
201
+ lane: lane.name,
202
+ runId: drive.operationId,
203
+ step: current.task.taskId,
204
+ attempt,
205
+ success: outcome.kind === "compaction" || outcome.kind === "branch_summary",
206
+ ...(outcome.kind === "failed" ? { finalError: outcome.error.message } : {}),
207
+ },
208
+ ]);
209
+ }
210
+ if (outcome.kind === "compaction") {
211
+ baseEvents.push((commit) => [
212
+ {
213
+ type: "compaction_end",
214
+ lane: lane.name,
215
+ runId: drive.operationId,
216
+ reason: compactionReason(current.task),
217
+ status: "completed",
218
+ entryId: outcome.resultEntryId,
219
+ endedAt: terminalCompactionEndedAt ?? commit.timestamp,
220
+ },
221
+ ]);
222
+ }
223
+ const events = (commit) => baseEvents.flatMap((materialize) => materialize(commit));
224
+ switch (current.task.boundary.kind) {
225
+ case "resume_checkpoint": {
226
+ if (outcome.kind === "branch_summary") {
227
+ throw new SessionInvariantError("Run compaction boundary received a branch summary");
228
+ }
229
+ if (outcome.kind === "compaction" ||
230
+ (outcome.kind === "declined" && current.task.reason === "threshold")) {
231
+ if (terminalTipId === null)
232
+ throw new SessionInvariantError("Run compaction has no Branch tip");
233
+ const continuation = current.task.boundary.resumeAfter.continuation;
234
+ const placement = await planBoundaryInbox(lane, drive, state, current, reader, terminalTipId, outcome.kind === "declined" && continuation.kind === "may_finish");
235
+ if (outcome.kind === "declined" &&
236
+ placement.triggerEntryId === undefined &&
237
+ continuation.kind === "may_finish") {
238
+ return {
239
+ kind: "return",
240
+ result: {
241
+ kind: "finish_pending",
242
+ entryIds: placement.entries.map((entry) => entry.id),
243
+ },
244
+ };
245
+ }
246
+ const placementWriteIndex = writes.length;
247
+ writes.push(...placement.writes);
248
+ let operationState;
249
+ if (placement.triggerEntryId !== undefined || continuation.kind === "need_assistant") {
250
+ const overflowRecoveryUsed = placement.triggerEntryId === undefined && continuation.kind === "need_assistant"
251
+ ? continuation.overflowRecoveryUsed
252
+ : false;
253
+ operationState = assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId ?? current.task.boundary.resumeAfter.triggerEntryId, overflowRecoveryUsed);
254
+ }
255
+ else {
256
+ operationState = {
257
+ ...operationScopeOf(current),
258
+ at: "checkpoint",
259
+ ...current.task.boundary.resumeAfter,
260
+ };
261
+ }
262
+ return {
263
+ kind: "commit",
264
+ writes,
265
+ operationState,
266
+ lane: { tipId: placement.tipId, inbox: placement.inbox },
267
+ materialize: () => ({ kind: "continue" }),
268
+ events: (commit) => [
269
+ ...(outcome.kind === "compaction"
270
+ ? events(commit)
271
+ : [
272
+ {
273
+ type: "compaction_end",
274
+ lane: lane.name,
275
+ runId: drive.operationId,
276
+ reason: "threshold",
277
+ status: "declined",
278
+ endedAt: commit.timestamp,
279
+ },
280
+ ]),
281
+ ...boundaryPlacementEvents(placement, commit, placementWriteIndex, lane.name, drive.operationId),
282
+ ],
283
+ };
284
+ }
285
+ if (state.tipId === null)
286
+ throw new SessionInvariantError("Failed run has no Branch tip");
287
+ const error = outcome.kind === "declined"
288
+ ? operationError("compaction_declined", "Overflow compaction was declined")
289
+ : outcome.error;
290
+ const cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);
291
+ const record = operationResultRecord(meta, "failed", state.tipId, error);
292
+ const compactionEnd = outcome.kind === "declined"
293
+ ? {
294
+ type: "compaction_end",
295
+ lane: lane.name,
296
+ runId: drive.operationId,
297
+ reason: compactionReason(current.task),
298
+ status: "declined",
299
+ endedAt: record.endedAt,
300
+ }
301
+ : {
302
+ type: "compaction_end",
303
+ lane: lane.name,
304
+ runId: drive.operationId,
305
+ reason: compactionReason(current.task),
306
+ status: "failed",
307
+ error: outcome.error,
308
+ endedAt: record.endedAt,
309
+ };
310
+ return {
311
+ kind: "finish",
312
+ writes: [...writes, ...cleanup],
313
+ record,
314
+ materialize: () => ({ kind: "settled", outcome: record }),
315
+ events: (commit) => [
316
+ ...events(commit),
317
+ compactionEnd,
318
+ {
319
+ type: "run_end",
320
+ lane: lane.name,
321
+ runId: drive.operationId,
322
+ status: "failed",
323
+ error,
324
+ fromTipId: meta.sourceTipId,
325
+ tipId: state.tipId,
326
+ endedAt: record.endedAt,
327
+ },
328
+ ],
329
+ };
330
+ }
331
+ case "finish": {
332
+ if (outcome.kind === "branch_summary") {
333
+ throw new SessionInvariantError("Compaction finish boundary received a branch summary");
334
+ }
335
+ if (outcome.kind !== "compaction" && state.tipId === null) {
336
+ throw new SessionInvariantError("Standalone compaction has no Branch tip");
337
+ }
338
+ const error = outcome.kind === "failed" ? outcome.error : undefined;
339
+ const status = outcome.kind === "declined" ? "declined" : outcome.kind === "failed" ? "failed" : "completed";
340
+ const cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);
341
+ const record = operationResultRecord(meta, status, terminalTipId, error);
342
+ terminalCompactionEndedAt = record.endedAt;
343
+ const compactionEnd = outcome.kind === "compaction"
344
+ ? undefined
345
+ : outcome.kind === "declined"
346
+ ? {
347
+ type: "compaction_end",
348
+ lane: lane.name,
349
+ runId: drive.operationId,
350
+ reason: "manual",
351
+ status: "declined",
352
+ endedAt: record.endedAt,
353
+ }
354
+ : {
355
+ type: "compaction_end",
356
+ lane: lane.name,
357
+ runId: drive.operationId,
358
+ reason: "manual",
359
+ status: "failed",
360
+ error: outcome.error,
361
+ endedAt: record.endedAt,
362
+ };
363
+ return {
364
+ kind: "finish",
365
+ writes: [...writes, ...cleanup],
366
+ record,
367
+ ...(outcome.kind === "compaction" ? { lane: { tipId: terminalTipId } } : {}),
368
+ materialize: () => ({ kind: "settled", outcome: record }),
369
+ events: (commit) => [...events(commit), ...(compactionEnd === undefined ? [] : [compactionEnd])],
370
+ };
371
+ }
372
+ case "commit_navigation": {
373
+ if (outcome.kind === "compaction") {
374
+ throw new SessionInvariantError("Navigation boundary received a compaction result");
375
+ }
376
+ const error = outcome.kind === "failed" ? outcome.error : undefined;
377
+ const status = outcome.kind === "declined" ? "declined" : outcome.kind === "failed" ? "failed" : "completed";
378
+ const cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);
379
+ const record = operationResultRecord(meta, status, terminalTipId, error);
380
+ const navigationEnd = outcome.kind === "branch_summary"
381
+ ? {
382
+ type: "navigation_end",
383
+ lane: lane.name,
384
+ runId: drive.operationId,
385
+ status: "completed",
386
+ fromTipId: meta.sourceTipId,
387
+ tipId: terminalTipId,
388
+ endedAt: record.endedAt,
389
+ }
390
+ : outcome.kind === "declined"
391
+ ? {
392
+ type: "navigation_end",
393
+ lane: lane.name,
394
+ runId: drive.operationId,
395
+ status: "declined",
396
+ fromTipId: meta.sourceTipId,
397
+ tipId: terminalTipId,
398
+ endedAt: record.endedAt,
399
+ }
400
+ : {
401
+ type: "navigation_end",
402
+ lane: lane.name,
403
+ runId: drive.operationId,
404
+ status: "failed",
405
+ error: outcome.error,
406
+ fromTipId: meta.sourceTipId,
407
+ tipId: terminalTipId,
408
+ endedAt: record.endedAt,
409
+ };
410
+ return {
411
+ kind: "finish",
412
+ writes: [...writes, ...cleanup],
413
+ record,
414
+ ...(outcome.kind === "branch_summary" ? { lane: { tipId: terminalTipId } } : {}),
415
+ materialize: () => ({ kind: "settled", outcome: record }),
416
+ events: (commit) => [...events(commit), navigationEnd],
417
+ };
418
+ }
419
+ }
420
+ }, drive.context);
421
+ if (published.kind === "cancel_requested")
422
+ return { kind: "continue" };
423
+ if (published.value.kind !== "finish_pending")
424
+ return published.value;
425
+ const boundary = capability.task.boundary;
426
+ if (boundary.kind !== "resume_checkpoint" || boundary.resumeAfter.continuation.kind !== "may_finish") {
427
+ throw new SessionInvariantError("Structural finish mediation requires a resumable finish boundary");
428
+ }
429
+ return finishRunBoundary(lane, drive, capability, boundary.resumeAfter.continuation, published.value.entryIds, [
430
+ {
431
+ type: "compaction_end",
432
+ lane: lane.name,
433
+ runId: drive.operationId,
434
+ reason: "threshold",
435
+ status: "declined",
436
+ endedAt: Date.now(),
437
+ },
438
+ ]);
439
+ }
440
+ async function publishStructuralReady(lane, drive, deciding) {
441
+ const resultEntryId = lane.session.idGenerator.next();
442
+ const published = await lane.continueOperation(deciding, (state, current) => {
443
+ const operationState = {
444
+ ...operationScopeOf(current),
445
+ at: "summary.ready",
446
+ task: current.task,
447
+ summaryContext: summaryContext(lane, resultEntryId, state.configuration),
448
+ nextAttempt: 1,
449
+ };
450
+ return {
451
+ kind: "commit",
452
+ writes: [],
453
+ operationState,
454
+ materialize: () => ({ kind: "continue" }),
455
+ };
456
+ }, drive.context);
457
+ return published.kind === "cancel_requested" ? { kind: "continue" } : published.value;
458
+ }
459
+ /** Consume one durable structural preparation and decision hook. */
460
+ export async function runStructuralDecision(lane, drive, deciding) {
461
+ const preparation = await readStructuralPreparation(lane, drive, deciding);
462
+ if (preparation.kind === "cancel_requested")
463
+ return { kind: "continue" };
464
+ if (deciding.task.boundary.kind === "commit_navigation") {
465
+ if (!("messages" in preparation.value)) {
466
+ throw new SessionInvariantError("Navigation task has invalid durable preparation");
467
+ }
468
+ const hook = await lane.hooks.runWithGate("before_navigation", {
469
+ lane: lane.name,
470
+ runId: drive.operationId,
471
+ targetId: deciding.task.boundary.targetId,
472
+ preparation: preparation.value,
473
+ ...(deciding.task.customInstructions === undefined
474
+ ? {}
475
+ : { customInstructions: deciding.task.customInstructions }),
476
+ }, drive.gate, drive.context);
477
+ if (hook?.decline === true)
478
+ return publishStructuralOutcome(lane, drive, deciding, { kind: "declined" });
479
+ if (hook?.summary !== undefined) {
480
+ return publishStructuralOutcome(lane, drive, deciding, {
481
+ kind: "branch_summary",
482
+ resultEntryId: lane.session.idGenerator.next(),
483
+ result: hook.summary,
484
+ fromHook: true,
485
+ });
486
+ }
487
+ return publishStructuralReady(lane, drive, deciding);
488
+ }
489
+ if (!("messagesToSummarize" in preparation.value)) {
490
+ throw new SessionInvariantError("Compaction task has invalid durable preparation");
491
+ }
492
+ const hook = await lane.hooks.runWithGate("before_compaction", {
493
+ lane: lane.name,
494
+ runId: drive.operationId,
495
+ reason: compactionReason(deciding.task),
496
+ preparation: preparation.value,
497
+ ...(deciding.task.customInstructions === undefined
498
+ ? {}
499
+ : { customInstructions: deciding.task.customInstructions }),
500
+ }, drive.gate, drive.context);
501
+ if (hook?.decline === true)
502
+ return publishStructuralOutcome(lane, drive, deciding, { kind: "declined" });
503
+ if (hook?.compaction !== undefined) {
504
+ return publishStructuralOutcome(lane, drive, deciding, {
505
+ kind: "compaction",
506
+ resultEntryId: lane.session.idGenerator.next(),
507
+ result: hook.compaction,
508
+ fromHook: true,
509
+ });
510
+ }
511
+ return publishStructuralReady(lane, drive, deciding);
512
+ }
513
+ function effectPendingFromReady(ready) {
514
+ return {
515
+ ...operationScopeOf(ready),
516
+ at: "summary.effect_pending",
517
+ task: ready.task,
518
+ summaryContext: ready.summaryContext,
519
+ attempt: ready.nextAttempt,
520
+ usageIds: [],
521
+ };
522
+ }
523
+ function retryWaitFromEffect(effect, errorMessage) {
524
+ return {
525
+ ...operationScopeOf(effect),
526
+ at: "summary.retry_wait",
527
+ task: effect.task,
528
+ summaryContext: effect.summaryContext,
529
+ nextAttempt: effect.attempt + 1,
530
+ notBefore: retryNotBefore(effect.summaryContext.retryPolicy.baseDelayMs, effect.attempt),
531
+ errorMessage,
532
+ };
533
+ }
534
+ function readyFromRetryWait(retry) {
535
+ return {
536
+ ...operationScopeOf(retry),
537
+ at: "summary.ready",
538
+ task: retry.task,
539
+ summaryContext: retry.summaryContext,
540
+ nextAttempt: retry.nextAttempt,
541
+ };
542
+ }
543
+ async function publishAttemptIntent(lane, drive, ready) {
544
+ return lane.continueOperation(ready, (_state, current) => {
545
+ const effectPending = effectPendingFromReady(current);
546
+ return {
547
+ kind: "commit",
548
+ writes: [],
549
+ operationState: effectPending,
550
+ materialize: () => effectPending,
551
+ };
552
+ }, drive.context);
553
+ }
554
+ async function publishNestedRequestIntent(lane, drive, effect, index, usageId) {
555
+ return lane.continueOperation(effect, (_state, current) => {
556
+ const next = { ...current, request: { index, usageId } };
557
+ return {
558
+ kind: "commit",
559
+ writes: [],
560
+ operationState: next,
561
+ materialize: () => next,
562
+ };
563
+ }, drive.context);
564
+ }
565
+ async function publishNestedRequestOutcome(lane, drive, effect, usageId, response) {
566
+ await lane.settleOperation(effect, (_state, current) => {
567
+ const next = { ...current, usageIds: [...current.usageIds, usageId] };
568
+ delete next.request;
569
+ const row = { id: usageId, usage: response.usage, adjustment: false };
570
+ return {
571
+ kind: "commit",
572
+ writes: [insertUsage(row)],
573
+ operationState: next,
574
+ materialize: () => undefined,
575
+ events: (commit) => [usageEvent(row, 0, commit, lane.name)],
576
+ };
577
+ }, drive.context);
578
+ }
579
+ function requestStreamOptions(options, streamOptions, context, onPayload) {
580
+ return {
581
+ ...options,
582
+ transport: streamOptions.transport,
583
+ timeoutMs: streamOptions.timeoutMs,
584
+ maxRetries: streamOptions.maxRetries,
585
+ maxRetryDelayMs: streamOptions.maxRetryDelayMs,
586
+ headers: streamOptions.headers,
587
+ metadata: streamOptions.metadata,
588
+ cacheRetention: "none",
589
+ deferred: false,
590
+ signal: context.abortSignal,
591
+ telemetryContext: getTelemetryContext(context),
592
+ onPayload,
593
+ };
594
+ }
595
+ async function performStructuralAttempt(lane, drive, effect, model, preparation) {
596
+ let requestIndex = 0;
597
+ let lastResponse;
598
+ const request = async (aiContext, options, requestContext) => {
599
+ const baseOptions = { ...effect.summaryContext.streamOptions, deferred: false };
600
+ const beforeRequest = await lane.hooks
601
+ .runWithGate("before_request", {
602
+ lane: lane.name,
603
+ runId: drive.operationId,
604
+ model,
605
+ step: summaryKind(effect.task),
606
+ attempt: effect.attempt,
607
+ streamOptions: baseOptions,
608
+ }, drive.gate, requestContext)
609
+ .catch(async (error) => {
610
+ if (!(error instanceof AbortRequested))
611
+ throw error;
612
+ await error.cancellation;
613
+ throw new StructuralCancelled();
614
+ });
615
+ const streamOptions = {
616
+ ...(beforeRequest?.streamOptions === undefined
617
+ ? baseOptions
618
+ : applyStreamOptionsPatch(baseOptions, beforeRequest.streamOptions)),
619
+ deferred: false,
620
+ };
621
+ const usageId = lane.session.idGenerator.next();
622
+ const intent = await publishNestedRequestIntent(lane, drive, effect, requestIndex, usageId);
623
+ requestIndex += 1;
624
+ if (intent.kind === "cancel_requested")
625
+ throw new StructuralCancelled();
626
+ const admittedContext = withAbortSignal(drive.gate.signal, requestContext);
627
+ let response;
628
+ try {
629
+ response = await drive.gate.admit(() => lane.models.completeSimple(model, aiContext, requestStreamOptions(options, streamOptions, admittedContext, async (payload, requestModel) => {
630
+ const hook = await lane.hooks.runWithGate("before_payload", { lane: lane.name, runId: drive.operationId, model: requestModel, payload }, drive.gate, admittedContext);
631
+ return hook?.payload;
632
+ })));
633
+ }
634
+ catch (error) {
635
+ if (!(error instanceof AbortRequested))
636
+ throw error;
637
+ await error.cancellation;
638
+ throw new StructuralCancelled();
639
+ }
640
+ lastResponse = response;
641
+ await publishNestedRequestOutcome(lane, drive, intent.value, usageId, response);
642
+ return response;
643
+ };
644
+ try {
645
+ if (summaryKind(effect.task) === "compaction") {
646
+ if (!("messagesToSummarize" in preparation)) {
647
+ throw new SessionInvariantError("Compaction summary has invalid durable preparation");
648
+ }
649
+ const result = await compactWithRequest(preparation, {
650
+ model,
651
+ customInstructions: effect.task.customInstructions,
652
+ thinkingLevel: effect.summaryContext.configuration.thinkingLevel,
653
+ }, request, drive.context);
654
+ if (!result.ok) {
655
+ return {
656
+ kind: "error",
657
+ error: operationError(result.error.code, result.error.message),
658
+ retryable: lastResponse !== undefined && isRetryableAssistantError(lastResponse),
659
+ };
660
+ }
661
+ return {
662
+ kind: "compaction",
663
+ result: result.value,
664
+ retryable: lastResponse !== undefined && isRetryableAssistantError(lastResponse),
665
+ };
666
+ }
667
+ if (!("messages" in preparation)) {
668
+ throw new SessionInvariantError("Branch summary has invalid durable preparation");
669
+ }
670
+ const result = await generateBranchSummaryWithRequest(preparation, { customInstructions: effect.task.customInstructions }, request, drive.context);
671
+ if (!result.ok) {
672
+ return {
673
+ kind: "error",
674
+ error: operationError(result.error.code, result.error.message),
675
+ retryable: lastResponse !== undefined && isRetryableAssistantError(lastResponse),
676
+ };
677
+ }
678
+ return {
679
+ kind: "branch_summary",
680
+ result: result.value,
681
+ retryable: lastResponse !== undefined && isRetryableAssistantError(lastResponse),
682
+ };
683
+ }
684
+ catch (error) {
685
+ if (error instanceof StructuralCancelled)
686
+ return { kind: "cancel_requested" };
687
+ throw error;
688
+ }
689
+ }
690
+ async function readAttemptPreparation(lane, drive, ready) {
691
+ return lane.continueOperation(ready, async (_state, current, _meta, reader) => {
692
+ const expected = summaryKind(current.task);
693
+ const stored = await reader.getValue(operationPreparation(drive.operationId, current.task.taskId), drive.context);
694
+ if (stored === undefined || stored.value.kind !== expected) {
695
+ throw new SessionInvariantError(`Structural task ${current.task.taskId} has invalid durable preparation`);
696
+ }
697
+ return {
698
+ kind: "return",
699
+ result: stored.value.kind === "compaction"
700
+ ? compactionPreparation(stored.value)
701
+ : branchPreparation(stored.value),
702
+ };
703
+ }, drive.context);
704
+ }
705
+ async function publishAttemptResult(lane, drive, effect, result) {
706
+ if (result.kind === "cancel_requested")
707
+ return { kind: "continue" };
708
+ if (result.kind === "compaction") {
709
+ return publishStructuralOutcome(lane, drive, effect, {
710
+ kind: "compaction",
711
+ resultEntryId: effect.summaryContext.resultEntryId,
712
+ result: result.result,
713
+ fromHook: false,
714
+ });
715
+ }
716
+ if (result.kind === "branch_summary") {
717
+ return publishStructuralOutcome(lane, drive, effect, {
718
+ kind: "branch_summary",
719
+ resultEntryId: effect.summaryContext.resultEntryId,
720
+ result: result.result,
721
+ fromHook: false,
722
+ });
723
+ }
724
+ if (result.error.code === "aborted" && lane.state.operation.state.control.status === "running") {
725
+ throw new SessionInvariantError("Structural provider response is aborted while durable control is running");
726
+ }
727
+ if (result.retryable && effect.attempt < effect.summaryContext.retryPolicy.maxAttempts) {
728
+ const retryWait = retryWaitFromEffect(effect, result.error.message);
729
+ const published = await lane.continueOperation(effect, () => ({
730
+ kind: "commit",
731
+ writes: [],
732
+ operationState: retryWait,
733
+ materialize: () => ({ kind: "continue" }),
734
+ events: () => [
735
+ {
736
+ type: "retry_scheduled",
737
+ lane: lane.name,
738
+ runId: drive.operationId,
739
+ step: effect.task.taskId,
740
+ attempt: retryWait.nextAttempt,
741
+ maxAttempts: effect.summaryContext.retryPolicy.maxAttempts,
742
+ delayMs: retryDelay(effect.summaryContext.retryPolicy.baseDelayMs, effect.attempt),
743
+ notBefore: retryWait.notBefore,
744
+ errorMessage: result.error.message,
745
+ },
746
+ ],
747
+ }), drive.context);
748
+ return published.kind === "cancel_requested" ? { kind: "continue" } : published.value;
749
+ }
750
+ return publishStructuralOutcome(lane, drive, effect, { kind: "failed", error: result.error });
751
+ }
752
+ /** Execute one ready structural generation attempt. */
753
+ export async function runStructuralGeneration(lane, drive, ready) {
754
+ const preparation = await readAttemptPreparation(lane, drive, ready);
755
+ if (preparation.kind === "cancel_requested")
756
+ return { kind: "continue" };
757
+ const identity = ready.summaryContext.configuration.model;
758
+ const model = lane.models.getModel(identity.provider, identity.modelId);
759
+ if (model === undefined) {
760
+ return publishStructuralOutcome(lane, drive, ready, {
761
+ kind: "failed",
762
+ error: operationError("model_unavailable", "The configured model is unavailable in this process", identity),
763
+ });
764
+ }
765
+ const intent = await publishAttemptIntent(lane, drive, ready);
766
+ if (intent.kind === "cancel_requested")
767
+ return { kind: "continue" };
768
+ const result = await performStructuralAttempt(lane, drive, intent.value, model, preparation.value);
769
+ return publishAttemptResult(lane, drive, intent.value, result);
770
+ }
771
+ /** Consume one structural retry wait without starting a provider effect. */
772
+ export async function runStructuralRetryWait(lane, drive, retry) {
773
+ if (Date.now() < retry.notBefore) {
774
+ if (!drive.waitForRetry) {
775
+ return {
776
+ kind: "waiting",
777
+ outcome: {
778
+ kind: "waiting",
779
+ operationId: drive.operationId,
780
+ reason: "retry",
781
+ notBefore: retry.notBefore,
782
+ },
783
+ };
784
+ }
785
+ await drive.gate.admit(() => waitUntil(retry.notBefore, drive.gate.signal));
786
+ }
787
+ const published = await lane.continueOperation(retry, (_state, current) => {
788
+ const ready = readyFromRetryWait(current);
789
+ return {
790
+ kind: "commit",
791
+ writes: [],
792
+ operationState: ready,
793
+ materialize: () => ({ kind: "continue" }),
794
+ events: () => [
795
+ {
796
+ type: "retry_start",
797
+ lane: lane.name,
798
+ runId: drive.operationId,
799
+ step: current.task.taskId,
800
+ attempt: current.nextAttempt,
801
+ },
802
+ ],
803
+ };
804
+ }, drive.context);
805
+ return published.kind === "cancel_requested" ? { kind: "continue" } : published.value;
806
+ }
807
+ /** Convert an orphaned structural attempt into a fresh numbered attempt or terminal failure. */
808
+ export async function recoverStructuralGeneration(lane, drive, effect) {
809
+ const error = operationError("structural_interrupted", "Structural summary attempt was interrupted and its external outcome is unknown");
810
+ if (effect.attempt >= effect.summaryContext.retryPolicy.maxAttempts) {
811
+ return publishStructuralOutcome(lane, drive, effect, { kind: "failed", error });
812
+ }
813
+ const retryWait = retryWaitFromEffect(effect, error.message);
814
+ const published = await lane.continueOperation(effect, () => ({
815
+ kind: "commit",
816
+ writes: [],
817
+ operationState: retryWait,
818
+ materialize: () => ({ kind: "continue" }),
819
+ events: () => [
820
+ {
821
+ type: "retry_scheduled",
822
+ lane: lane.name,
823
+ runId: drive.operationId,
824
+ step: effect.task.taskId,
825
+ attempt: retryWait.nextAttempt,
826
+ maxAttempts: effect.summaryContext.retryPolicy.maxAttempts,
827
+ delayMs: retryDelay(effect.summaryContext.retryPolicy.baseDelayMs, effect.attempt),
828
+ notBefore: retryWait.notBefore,
829
+ errorMessage: error.message,
830
+ recovery: true,
831
+ },
832
+ ],
833
+ }), drive.context);
834
+ return published.kind === "cancel_requested" ? { kind: "continue" } : published.value;
835
+ }
836
+ /** Prepare threshold compaction only when no newer compaction already guards this trigger. */
837
+ export async function prepareCompactionThreshold(lane, drive, checkpoint) {
838
+ const settings = checkpoint.settings.compaction;
839
+ const identity = lane.state.configuration.model;
840
+ const model = lane.models.getModel(identity.provider, identity.modelId);
841
+ if (!settings.enabled || model === undefined)
842
+ return { kind: "result", value: undefined };
843
+ const path = await readBoundedEntries(lane, drive, checkpoint);
844
+ if (path.kind === "cancel_requested")
845
+ return path;
846
+ const triggerIndex = path.value.findIndex((entry) => entry.id === checkpoint.triggerEntryId);
847
+ let newestCompactionIndex = -1;
848
+ for (let index = path.value.length - 1; index >= 0; index--) {
849
+ if (path.value[index].type === "compaction") {
850
+ newestCompactionIndex = index;
851
+ break;
852
+ }
853
+ }
854
+ if (newestCompactionIndex >= triggerIndex && newestCompactionIndex !== -1) {
855
+ return { kind: "result", value: undefined };
856
+ }
857
+ if (triggerIndex === -1) {
858
+ throw new SessionInvariantError(`Checkpoint trigger ${checkpoint.triggerEntryId} is missing from its Branch`);
859
+ }
860
+ const prepared = prepareCompaction(path.value, settings);
861
+ if (!prepared.ok)
862
+ throw prepared.error;
863
+ if (prepared.value === undefined || !shouldCompact(prepared.value.tokensBefore, model.contextWindow, settings)) {
864
+ return { kind: "result", value: undefined };
865
+ }
866
+ return {
867
+ kind: "result",
868
+ value: { taskId: lane.session.idGenerator.next(), preparation: durableCompactionPreparation(prepared.value) },
869
+ };
870
+ }
871
+ /** Prepare one overflow compaction before the response settlement transaction. */
872
+ export async function prepareOverflowCompaction(lane, drive, generation) {
873
+ if (generation.generationContext.overflowRecoveryUsed)
874
+ return undefined;
875
+ const path = await readBoundedEntries(lane, drive, generation);
876
+ if (path.kind === "cancel_requested")
877
+ return undefined;
878
+ const prepared = prepareCompaction(path.value, generation.settings.compaction);
879
+ if (!prepared.ok)
880
+ throw prepared.error;
881
+ if (prepared.value === undefined)
882
+ return undefined;
883
+ return {
884
+ taskId: lane.session.idGenerator.next(),
885
+ preparation: durableCompactionPreparation(prepared.value),
886
+ };
887
+ }
888
+ /** Atomically move an unsummarized navigation and finish its operation. */
889
+ export function commitNavigation(lane, drive, navigation) {
890
+ return lane
891
+ .continueOperation(navigation, async (_state, current, meta, reader) => {
892
+ if (current.targetId !== null &&
893
+ !(await reader.getEntries([current.targetId], drive.context)).has(current.targetId)) {
894
+ throw new SessionInvariantError(`Navigation target ${current.targetId} is missing`);
895
+ }
896
+ if (current.targetId === meta.sourceTipId) {
897
+ throw new SessionInvariantError("Navigation target must differ from its source tip");
898
+ }
899
+ if (current.targetId === null && current.label !== undefined) {
900
+ throw new SessionInvariantError("Root navigation cannot set a label");
901
+ }
902
+ const writes = [setValue(branchTip(lane.name), current.targetId)];
903
+ if (current.label !== undefined && current.targetId !== null) {
904
+ writes.push(setValue(entryLabel(current.targetId), current.label));
905
+ }
906
+ const cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);
907
+ const record = operationResultRecord(meta, "completed", current.targetId);
908
+ return {
909
+ kind: "finish",
910
+ writes: [...writes, ...cleanup],
911
+ record,
912
+ lane: { tipId: current.targetId },
913
+ materialize: () => ({ kind: "settled", outcome: record }),
914
+ events: () => [
915
+ {
916
+ type: "navigation_end",
917
+ lane: lane.name,
918
+ runId: drive.operationId,
919
+ status: "completed",
920
+ fromTipId: meta.sourceTipId,
921
+ tipId: current.targetId,
922
+ endedAt: record.endedAt,
923
+ },
924
+ ],
925
+ };
926
+ }, drive.context)
927
+ .then((result) => (result.kind === "cancel_requested" ? { kind: "continue" } : result.value));
928
+ }
929
+ //# sourceMappingURL=structural.js.map