@code-yeongyu/senpi-agent-core 2026.9.12 → 2026.9.13

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 (344) hide show
  1. package/README.md +4 -0
  2. package/dist/harness/agent-harness.d.ts +639 -384
  3. package/dist/harness/agent-harness.d.ts.map +1 -1
  4. package/dist/harness/agent-harness.js +5 -250
  5. package/dist/harness/agent-harness.js.map +1 -1
  6. package/dist/harness/compaction/branch-summarization.d.ts +12 -6
  7. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  8. package/dist/harness/compaction/branch-summarization.js +33 -27
  9. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  10. package/dist/harness/compaction/compaction.d.ts +30 -8
  11. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  12. package/dist/harness/compaction/compaction.js +53 -64
  13. package/dist/harness/compaction/compaction.js.map +1 -1
  14. package/dist/harness/config.d.ts +9 -0
  15. package/dist/harness/config.d.ts.map +1 -0
  16. package/dist/harness/config.js +35 -0
  17. package/dist/harness/config.js.map +1 -0
  18. package/dist/harness/context.d.ts +9 -0
  19. package/dist/harness/context.d.ts.map +1 -0
  20. package/dist/harness/context.js +13 -0
  21. package/dist/harness/context.js.map +1 -0
  22. package/dist/harness/env/nodejs.d.ts +25 -28
  23. package/dist/harness/env/nodejs.d.ts.map +1 -1
  24. package/dist/harness/env/nodejs.js +368 -125
  25. package/dist/harness/env/nodejs.js.map +1 -1
  26. package/dist/harness/events.d.ts +20 -38
  27. package/dist/harness/events.d.ts.map +1 -1
  28. package/dist/harness/events.js +233 -55
  29. package/dist/harness/events.js.map +1 -1
  30. package/dist/harness/execution/assistant.d.ts +42 -0
  31. package/dist/harness/execution/assistant.d.ts.map +1 -0
  32. package/dist/harness/execution/assistant.js +82 -0
  33. package/dist/harness/execution/assistant.js.map +1 -0
  34. package/dist/harness/execution/effect-gate.d.ts +22 -0
  35. package/dist/harness/execution/effect-gate.d.ts.map +1 -0
  36. package/dist/harness/execution/effect-gate.js +48 -0
  37. package/dist/harness/execution/effect-gate.js.map +1 -0
  38. package/dist/harness/execution/tools.d.ts +67 -0
  39. package/dist/harness/execution/tools.d.ts.map +1 -0
  40. package/dist/harness/execution/tools.js +121 -0
  41. package/dist/harness/execution/tools.js.map +1 -0
  42. package/dist/harness/hooks.d.ts +38 -0
  43. package/dist/harness/hooks.d.ts.map +1 -0
  44. package/dist/harness/hooks.js +404 -0
  45. package/dist/harness/hooks.js.map +1 -0
  46. package/dist/harness/messages.d.ts +2 -2
  47. package/dist/harness/messages.d.ts.map +1 -1
  48. package/dist/harness/messages.js.map +1 -1
  49. package/dist/harness/prompt-templates.d.ts +3 -2
  50. package/dist/harness/prompt-templates.d.ts.map +1 -1
  51. package/dist/harness/prompt-templates.js +17 -17
  52. package/dist/harness/prompt-templates.js.map +1 -1
  53. package/dist/harness/result.d.ts +93 -0
  54. package/dist/harness/result.d.ts.map +1 -1
  55. package/dist/harness/result.js +39 -0
  56. package/dist/harness/result.js.map +1 -1
  57. package/dist/harness/runtime/drive/boundary.d.ts +28 -0
  58. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
  59. package/dist/harness/runtime/drive/boundary.js +173 -0
  60. package/dist/harness/runtime/drive/boundary.js.map +1 -0
  61. package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
  62. package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
  63. package/dist/harness/runtime/drive/checkpoint.js +133 -0
  64. package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
  65. package/dist/harness/runtime/drive/deferred.d.ts +15 -0
  66. package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
  67. package/dist/harness/runtime/drive/deferred.js +179 -0
  68. package/dist/harness/runtime/drive/deferred.js.map +1 -0
  69. package/dist/harness/runtime/drive/generation.d.ts +8 -0
  70. package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
  71. package/dist/harness/runtime/drive/generation.js +205 -0
  72. package/dist/harness/runtime/drive/generation.js.map +1 -0
  73. package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
  74. package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
  75. package/dist/harness/runtime/drive/reconcile.js +140 -0
  76. package/dist/harness/runtime/drive/reconcile.js.map +1 -0
  77. package/dist/harness/runtime/drive/recovery.d.ts +8 -0
  78. package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
  79. package/dist/harness/runtime/drive/recovery.js +85 -0
  80. package/dist/harness/runtime/drive/recovery.js.map +1 -0
  81. package/dist/harness/runtime/drive/response.d.ts +23 -0
  82. package/dist/harness/runtime/drive/response.d.ts.map +1 -0
  83. package/dist/harness/runtime/drive/response.js +394 -0
  84. package/dist/harness/runtime/drive/response.js.map +1 -0
  85. package/dist/harness/runtime/drive/retry.d.ts +4 -0
  86. package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
  87. package/dist/harness/runtime/drive/retry.js +34 -0
  88. package/dist/harness/runtime/drive/retry.js.map +1 -0
  89. package/dist/harness/runtime/drive/structural.d.ts +32 -0
  90. package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
  91. package/dist/harness/runtime/drive/structural.js +929 -0
  92. package/dist/harness/runtime/drive/structural.js.map +1 -0
  93. package/dist/harness/runtime/drive/terminal.d.ts +7 -0
  94. package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
  95. package/dist/harness/runtime/drive/terminal.js +49 -0
  96. package/dist/harness/runtime/drive/terminal.js.map +1 -0
  97. package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
  98. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
  99. package/dist/harness/runtime/drive/tool-placement.js +244 -0
  100. package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
  101. package/dist/harness/runtime/drive/tools.d.ts +6 -0
  102. package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
  103. package/dist/harness/runtime/drive/tools.js +449 -0
  104. package/dist/harness/runtime/drive/tools.js.map +1 -0
  105. package/dist/harness/runtime/drive.d.ts +6 -0
  106. package/dist/harness/runtime/drive.d.ts.map +1 -0
  107. package/dist/harness/runtime/drive.js +93 -0
  108. package/dist/harness/runtime/drive.js.map +1 -0
  109. package/dist/harness/runtime/harness.d.ts +59 -0
  110. package/dist/harness/runtime/harness.d.ts.map +1 -0
  111. package/dist/harness/runtime/harness.js +302 -0
  112. package/dist/harness/runtime/harness.js.map +1 -0
  113. package/dist/harness/runtime/index.d.ts +2 -0
  114. package/dist/harness/runtime/index.d.ts.map +1 -0
  115. package/dist/harness/runtime/index.js +2 -0
  116. package/dist/harness/runtime/index.js.map +1 -0
  117. package/dist/harness/runtime/lane.d.ts +118 -0
  118. package/dist/harness/runtime/lane.d.ts.map +1 -0
  119. package/dist/harness/runtime/lane.js +1559 -0
  120. package/dist/harness/runtime/lane.js.map +1 -0
  121. package/dist/harness/runtime/progress.d.ts +15 -0
  122. package/dist/harness/runtime/progress.d.ts.map +1 -0
  123. package/dist/harness/runtime/progress.js +66 -0
  124. package/dist/harness/runtime/progress.js.map +1 -0
  125. package/dist/harness/runtime/reducer.d.ts +5 -0
  126. package/dist/harness/runtime/reducer.d.ts.map +1 -0
  127. package/dist/harness/runtime/reducer.js +240 -0
  128. package/dist/harness/runtime/reducer.js.map +1 -0
  129. package/dist/harness/runtime/restore.d.ts +24 -0
  130. package/dist/harness/runtime/restore.d.ts.map +1 -0
  131. package/dist/harness/runtime/restore.js +117 -0
  132. package/dist/harness/runtime/restore.js.map +1 -0
  133. package/dist/harness/runtime/transcript.d.ts +21 -0
  134. package/dist/harness/runtime/transcript.d.ts.map +1 -0
  135. package/dist/harness/runtime/transcript.js +72 -0
  136. package/dist/harness/runtime/transcript.js.map +1 -0
  137. package/dist/harness/runtime/types.d.ts +105 -0
  138. package/dist/harness/runtime/types.d.ts.map +1 -0
  139. package/dist/harness/runtime/types.js +50 -0
  140. package/dist/harness/runtime/types.js.map +1 -0
  141. package/dist/harness/session/commit.d.ts +53 -0
  142. package/dist/harness/session/commit.d.ts.map +1 -0
  143. package/dist/harness/session/commit.js +57 -0
  144. package/dist/harness/session/commit.js.map +1 -0
  145. package/dist/harness/session/context.d.ts +6 -19
  146. package/dist/harness/session/context.d.ts.map +1 -1
  147. package/dist/harness/session/context.js +31 -53
  148. package/dist/harness/session/context.js.map +1 -1
  149. package/dist/harness/session/fork-policy.d.ts +21 -0
  150. package/dist/harness/session/fork-policy.d.ts.map +1 -0
  151. package/dist/harness/session/fork-policy.js +55 -0
  152. package/dist/harness/session/fork-policy.js.map +1 -0
  153. package/dist/harness/session/fork.d.ts +16 -0
  154. package/dist/harness/session/fork.d.ts.map +1 -0
  155. package/dist/harness/session/fork.js +79 -0
  156. package/dist/harness/session/fork.js.map +1 -0
  157. package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
  158. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
  159. package/dist/harness/session/in-memory-storage-state.js +269 -0
  160. package/dist/harness/session/in-memory-storage-state.js.map +1 -0
  161. package/dist/harness/session/index.d.ts +9 -5
  162. package/dist/harness/session/index.d.ts.map +1 -1
  163. package/dist/harness/session/index.js +7 -4
  164. package/dist/harness/session/index.js.map +1 -1
  165. package/dist/harness/session/jsonl/codec.d.ts +19 -8
  166. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  167. package/dist/harness/session/jsonl/codec.js +34 -219
  168. package/dist/harness/session/jsonl/codec.js.map +1 -1
  169. package/dist/harness/session/jsonl/fork.d.ts +38 -0
  170. package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
  171. package/dist/harness/session/jsonl/fork.js +266 -0
  172. package/dist/harness/session/jsonl/fork.js.map +1 -0
  173. package/dist/harness/session/jsonl/index.d.ts +4 -0
  174. package/dist/harness/session/jsonl/index.d.ts.map +1 -0
  175. package/dist/harness/session/jsonl/index.js +4 -0
  176. package/dist/harness/session/jsonl/index.js.map +1 -0
  177. package/dist/harness/session/jsonl/io.d.ts +14 -0
  178. package/dist/harness/session/jsonl/io.d.ts.map +1 -0
  179. package/dist/harness/session/jsonl/io.js +86 -0
  180. package/dist/harness/session/jsonl/io.js.map +1 -0
  181. package/dist/harness/session/jsonl/legacy-v3.d.ts +44 -0
  182. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
  183. package/dist/harness/session/jsonl/legacy-v3.js +454 -0
  184. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
  185. package/dist/harness/session/jsonl/repo.d.ts +27 -24
  186. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  187. package/dist/harness/session/jsonl/repo.js +276 -151
  188. package/dist/harness/session/jsonl/repo.js.map +1 -1
  189. package/dist/harness/session/jsonl/storage.d.ts +39 -41
  190. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  191. package/dist/harness/session/jsonl/storage.js +175 -196
  192. package/dist/harness/session/jsonl/storage.js.map +1 -1
  193. package/dist/harness/session/jsonl/types.d.ts +23 -22
  194. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  195. package/dist/harness/session/jsonl/types.js +2 -1
  196. package/dist/harness/session/jsonl/types.js.map +1 -1
  197. package/dist/harness/session/memory.d.ts +44 -40
  198. package/dist/harness/session/memory.d.ts.map +1 -1
  199. package/dist/harness/session/memory.js +333 -115
  200. package/dist/harness/session/memory.js.map +1 -1
  201. package/dist/harness/session/mutation-line.d.ts +8 -0
  202. package/dist/harness/session/mutation-line.d.ts.map +1 -0
  203. package/dist/harness/session/mutation-line.js +22 -0
  204. package/dist/harness/session/mutation-line.js.map +1 -0
  205. package/dist/harness/session/session.d.ts +78 -51
  206. package/dist/harness/session/session.d.ts.map +1 -1
  207. package/dist/harness/session/session.js +327 -207
  208. package/dist/harness/session/session.js.map +1 -1
  209. package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
  210. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
  211. package/dist/harness/session/testing/benchmark/datasets.js +24 -0
  212. package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
  213. package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
  214. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
  215. package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
  216. package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
  217. package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
  218. package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
  219. package/dist/harness/session/testing/benchmark/storage.js +114 -0
  220. package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
  221. package/dist/harness/session/testing/conformance/session-repo.d.ts +23 -0
  222. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
  223. package/dist/harness/session/testing/conformance/session-repo.js +809 -0
  224. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
  225. package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
  226. package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
  227. package/dist/harness/session/testing/conformance/storage.js +600 -0
  228. package/dist/harness/session/testing/conformance/storage.js.map +1 -0
  229. package/dist/harness/session/testing/gating-storage.d.ts +26 -0
  230. package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
  231. package/dist/harness/session/testing/gating-storage.js +103 -0
  232. package/dist/harness/session/testing/gating-storage.js.map +1 -0
  233. package/dist/harness/session/testing/index.d.ts +9 -2
  234. package/dist/harness/session/testing/index.d.ts.map +1 -1
  235. package/dist/harness/session/testing/index.js +8 -1
  236. package/dist/harness/session/testing/index.js.map +1 -1
  237. package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
  238. package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
  239. package/dist/harness/session/testing/instrumented-storage.js +19 -0
  240. package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
  241. package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
  242. package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
  243. package/dist/harness/session/testing/storage-decorator.js +40 -0
  244. package/dist/harness/session/testing/storage-decorator.js.map +1 -0
  245. package/dist/harness/session/testing/types.d.ts +5 -7
  246. package/dist/harness/session/testing/types.d.ts.map +1 -1
  247. package/dist/harness/session/testing/types.js.map +1 -1
  248. package/dist/harness/session/types.d.ts +425 -265
  249. package/dist/harness/session/types.d.ts.map +1 -1
  250. package/dist/harness/session/types.js +7 -7
  251. package/dist/harness/session/types.js.map +1 -1
  252. package/dist/harness/session/values.d.ts +95 -0
  253. package/dist/harness/session/values.d.ts.map +1 -0
  254. package/dist/harness/session/values.js +81 -0
  255. package/dist/harness/session/values.js.map +1 -0
  256. package/dist/harness/skills.d.ts +3 -2
  257. package/dist/harness/skills.d.ts.map +1 -1
  258. package/dist/harness/skills.js +29 -26
  259. package/dist/harness/skills.js.map +1 -1
  260. package/dist/harness/telemetry.d.ts +64 -43
  261. package/dist/harness/telemetry.d.ts.map +1 -1
  262. package/dist/harness/telemetry.js +45 -26
  263. package/dist/harness/telemetry.js.map +1 -1
  264. package/dist/harness/tools/bash.d.ts +4 -4
  265. package/dist/harness/tools/bash.d.ts.map +1 -1
  266. package/dist/harness/tools/bash.js +66 -93
  267. package/dist/harness/tools/bash.js.map +1 -1
  268. package/dist/harness/tools/edit.d.ts.map +1 -1
  269. package/dist/harness/tools/edit.js +17 -13
  270. package/dist/harness/tools/edit.js.map +1 -1
  271. package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
  272. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  273. package/dist/harness/tools/file-mutation-queue.js +5 -5
  274. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  275. package/dist/harness/tools/path-utils.d.ts +3 -2
  276. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  277. package/dist/harness/tools/path-utils.js +5 -5
  278. package/dist/harness/tools/path-utils.js.map +1 -1
  279. package/dist/harness/tools/read.d.ts +2 -1
  280. package/dist/harness/tools/read.d.ts.map +1 -1
  281. package/dist/harness/tools/read.js +4 -6
  282. package/dist/harness/tools/read.js.map +1 -1
  283. package/dist/harness/tools/write.d.ts.map +1 -1
  284. package/dist/harness/tools/write.js +12 -8
  285. package/dist/harness/tools/write.js.map +1 -1
  286. package/dist/harness/types.d.ts +128 -45
  287. package/dist/harness/types.d.ts.map +1 -1
  288. package/dist/harness/types.js.map +1 -1
  289. package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
  290. package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
  291. package/dist/harness/utils/adaptive-publisher.js +79 -0
  292. package/dist/harness/utils/adaptive-publisher.js.map +1 -0
  293. package/dist/harness/utils/output-capture.d.ts +31 -0
  294. package/dist/harness/utils/output-capture.d.ts.map +1 -0
  295. package/dist/harness/utils/output-capture.js +217 -0
  296. package/dist/harness/utils/output-capture.js.map +1 -0
  297. package/dist/harness/utils/shell-output.d.ts +19 -5
  298. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  299. package/dist/harness/utils/shell-output.js +65 -192
  300. package/dist/harness/utils/shell-output.js.map +1 -1
  301. package/dist/harness/utils/truncate.d.ts +1 -0
  302. package/dist/harness/utils/truncate.d.ts.map +1 -1
  303. package/dist/harness/utils/truncate.js +1 -1
  304. package/dist/harness/utils/truncate.js.map +1 -1
  305. package/dist/harness/utils/usage.d.ts +4 -0
  306. package/dist/harness/utils/usage.d.ts.map +1 -0
  307. package/dist/harness/utils/usage.js +33 -0
  308. package/dist/harness/utils/usage.js.map +1 -0
  309. package/dist/index.d.ts +4 -1
  310. package/dist/index.d.ts.map +1 -1
  311. package/dist/index.js +3 -6
  312. package/dist/index.js.map +1 -1
  313. package/dist/search/index.d.ts +24 -16
  314. package/dist/search/index.d.ts.map +1 -1
  315. package/dist/search/index.js +1 -1
  316. package/dist/search/index.js.map +1 -1
  317. package/dist/types.d.ts +2 -0
  318. package/dist/types.d.ts.map +1 -1
  319. package/dist/types.js.map +1 -1
  320. package/package.json +27 -6
  321. package/dist/harness/reducer.d.ts +0 -100
  322. package/dist/harness/reducer.d.ts.map +0 -1
  323. package/dist/harness/reducer.js +0 -420
  324. package/dist/harness/reducer.js.map +0 -1
  325. package/dist/harness/session/jsonl/errors.d.ts +0 -9
  326. package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
  327. package/dist/harness/session/jsonl/errors.js +0 -18
  328. package/dist/harness/session/jsonl/errors.js.map +0 -1
  329. package/dist/harness/session/jsonl.d.ts +0 -3
  330. package/dist/harness/session/jsonl.d.ts.map +0 -1
  331. package/dist/harness/session/jsonl.js +0 -2
  332. package/dist/harness/session/jsonl.js.map +0 -1
  333. package/dist/harness/session/state.d.ts +0 -65
  334. package/dist/harness/session/state.d.ts.map +0 -1
  335. package/dist/harness/session/state.js +0 -320
  336. package/dist/harness/session/state.js.map +0 -1
  337. package/dist/harness/session/testing/conformance.d.ts +0 -4
  338. package/dist/harness/session/testing/conformance.d.ts.map +0 -1
  339. package/dist/harness/session/testing/conformance.js +0 -762
  340. package/dist/harness/session/testing/conformance.js.map +0 -1
  341. package/dist/search/scanning.d.ts +0 -29
  342. package/dist/search/scanning.d.ts.map +0 -1
  343. package/dist/search/scanning.js +0 -103
  344. package/dist/search/scanning.js.map +0 -1
@@ -1,15 +1,14 @@
1
1
  import { convertToLlm, createBranchSummaryMessage, createCompactionSummaryMessage } from "../messages.js";
2
- import { SessionError } from "../session/index.js";
3
2
  import { BranchSummaryError, err, ok } from "../types.js";
4
- import { completeSimpleWithRetries, estimateTokens, SUMMARIZATION_SYSTEM_PROMPT } from "./compaction.js";
3
+ import { completeSimpleWithRetries, createSummaryRequestOptions, estimateTokens, SUMMARIZATION_SYSTEM_PROMPT, } from "./compaction.js";
5
4
  import { computeFileLists, contentTextForSummary, createFileOps, extractFileOpsFromMessage, formatFileOperations, serializeConversation, } from "./utils.js";
6
5
  /** Collect entries that should be summarized before navigating to a different session tree entry. */
7
- export async function collectEntriesForBranchSummary(session, oldLeafId, targetId) {
8
- if (!oldLeafId) {
6
+ export async function collectEntriesForBranchSummary(branch, session, oldTipId, targetId, context) {
7
+ if (!oldTipId) {
9
8
  return { entries: [], commonAncestorId: null };
10
9
  }
11
- const oldPath = new Set((await session.findEntriesOnBranch({ start: oldLeafId })).map((entry) => entry.id));
12
- const targetPath = await session.findEntriesOnBranch({ start: targetId });
10
+ const oldPath = new Set((await branch.findEntries({ start: oldTipId }, context)).map((entry) => entry.id));
11
+ const targetPath = await branch.findEntries({ start: targetId }, context);
13
12
  let commonAncestorId = null;
14
13
  for (const entry of targetPath) {
15
14
  if (oldPath.has(entry.id)) {
@@ -18,11 +17,11 @@ export async function collectEntriesForBranchSummary(session, oldLeafId, targetI
18
17
  }
19
18
  }
20
19
  const entries = [];
21
- let current = oldLeafId;
20
+ let current = oldTipId;
22
21
  while (current && current !== commonAncestorId) {
23
- const entry = await session.getEntry(current);
22
+ const entry = await session.getEntry(current, context);
24
23
  if (!entry)
25
- throw new SessionError("invalid_entry", `Entry ${current} not found`);
24
+ throw new Error(`Corrupt session: entry ${current} not found`);
26
25
  entries.push(entry);
27
26
  current = entry.parentId;
28
27
  }
@@ -39,10 +38,6 @@ function getMessageFromEntry(entry) {
39
38
  return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
40
39
  case "compaction":
41
40
  return createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);
42
- case "thinking_level_change":
43
- case "configuration_update":
44
- case "model_change":
45
- case "active_tools_change":
46
41
  case "custom":
47
42
  return undefined;
48
43
  }
@@ -53,16 +48,22 @@ export function prepareBranchEntries(entries, tokenBudget = 0) {
53
48
  const fileOps = createFileOps();
54
49
  let totalTokens = 0;
55
50
  for (const entry of entries) {
56
- if (entry.type === "branch_summary" && entry.details) {
57
- const details = entry.details;
58
- if (Array.isArray(details.readFiles)) {
59
- for (const f of details.readFiles)
60
- fileOps.read.add(f);
51
+ if (entry.type !== "branch_summary" ||
52
+ typeof entry.details !== "object" ||
53
+ entry.details === null ||
54
+ Array.isArray(entry.details)) {
55
+ continue;
56
+ }
57
+ if (Array.isArray(entry.details.readFiles)) {
58
+ for (const path of entry.details.readFiles) {
59
+ if (typeof path === "string")
60
+ fileOps.read.add(path);
61
61
  }
62
- if (Array.isArray(details.modifiedFiles)) {
63
- for (const f of details.modifiedFiles) {
64
- fileOps.edited.add(f);
65
- }
62
+ }
63
+ if (Array.isArray(entry.details.modifiedFiles)) {
64
+ for (const path of entry.details.modifiedFiles) {
65
+ if (typeof path === "string")
66
+ fileOps.edited.add(path);
66
67
  }
67
68
  }
68
69
  }
@@ -120,11 +121,16 @@ Use this EXACT format:
120
121
 
121
122
  Keep each section concise. Preserve exact file paths, function names, and error messages.`;
122
123
  /** Generate a summary for abandoned branch entries. */
123
- export async function generateBranchSummary(entries, options) {
124
- const { models, model, signal, customInstructions, replaceInstructions, reserveTokens = 16384, retry, callbacks, } = options;
124
+ export function generateBranchSummary(entries, options, context) {
125
+ const { models, model, customInstructions, replaceInstructions, reserveTokens = 16384, retry, callbacks } = options;
125
126
  const contextWindow = model.contextWindow || 128000;
126
- const tokenBudget = contextWindow - reserveTokens;
127
- const { messages, fileOps } = prepareBranchEntries(entries, tokenBudget);
127
+ const preparation = prepareBranchEntries(entries, contextWindow - reserveTokens);
128
+ return generateBranchSummaryWithRequest(preparation, { customInstructions, replaceInstructions }, (aiContext, requestOptions, requestContext) => completeSimpleWithRetries(models, model, aiContext, requestOptions, retry, callbacks, requestContext), context);
129
+ }
130
+ /** Generate a prepared branch summary through a caller-owned one-request boundary. */
131
+ export async function generateBranchSummaryWithRequest(preparation, options, request, context) {
132
+ const { customInstructions, replaceInstructions } = options;
133
+ const { messages, fileOps } = preparation;
128
134
  if (messages.length === 0) {
129
135
  return ok({ summary: "No content to summarize", readFiles: [], modifiedFiles: [] });
130
136
  }
@@ -148,7 +154,7 @@ export async function generateBranchSummary(entries, options) {
148
154
  timestamp: Date.now(),
149
155
  },
150
156
  ];
151
- const response = await completeSimpleWithRetries(models, model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, { signal, maxTokens: 2048 }, retry, callbacks);
157
+ const response = await request({ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, createSummaryRequestOptions({ maxTokens: 2048 }, context), context);
152
158
  if (response.stopReason === "aborted") {
153
159
  return err(new BranchSummaryError("aborted", response.errorMessage || "Branch summary aborted"));
154
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"branch-summarization.js","sourceRoot":"","sources":["../../../src/harness/compaction/branch-summarization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, Models, RetryCallbacks, RetryPolicy, Usage } from \"@earendil-works/pi-ai\";\n\nimport type { AgentMessage } from \"../../types.ts\";\nimport { convertToLlm, createBranchSummaryMessage, createCompactionSummaryMessage } from \"../messages.ts\";\nimport { type Entry, type Session, SessionError } from \"../session/index.ts\";\nimport { BranchSummaryError, err, ok, type Result } from \"../types.ts\";\nimport { completeSimpleWithRetries, estimateTokens, SUMMARIZATION_SYSTEM_PROMPT } from \"./compaction.ts\";\nimport {\n\tcomputeFileLists,\n\tcontentTextForSummary,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tserializeConversation,\n} from \"./utils.ts\";\n\n/** Generated branch summary data ready to be persisted as a branch-summary entry. */\nexport interface BranchSummaryResult {\n\tsummary: string;\n\tusage?: Usage;\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\n/** File-operation details stored on generated branch summary entries. */\nexport interface BranchSummaryDetails {\n\t/** Files read while exploring the summarized branch. */\n\treadFiles: string[];\n\t/** Files modified while exploring the summarized branch. */\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.ts\";\n\n/** Prepared branch content for summarization. */\nexport interface BranchPreparation {\n\t/** Messages selected for the branch summary. */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from the branch. */\n\tfileOps: FileOperations;\n\t/** Estimated token count for selected messages. */\n\ttotalTokens: number;\n}\n\n/** Entries selected for branch summarization. */\nexport interface CollectEntriesResult {\n\t/** Entries to summarize in chronological order. */\n\tentries: Entry[];\n\t/** Deepest common ancestor between the previous leaf and target entry. */\n\tcommonAncestorId: string | null;\n}\n\n/** Options for generating a branch summary. */\nexport interface GenerateBranchSummaryOptions {\n\t/** Provider collection the summarization request goes through; owns auth resolution. */\n\tmodels: Models;\n\t/** Model used for summarization. */\n\tmodel: Model<Api>;\n\t/** Abort signal for the summarization request. */\n\tsignal: AbortSignal;\n\t/** Optional instructions appended to or replacing the default prompt. */\n\tcustomInstructions?: string;\n\t/** Replace the default prompt with custom instructions instead of appending them. */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved for prompt and model output. Defaults to 16384. */\n\treserveTokens?: number;\n\t/** Optional retry policy for transient summarization errors. */\n\tretry?: RetryPolicy;\n\t/** Optional callbacks for retry reporting. */\n\tcallbacks?: RetryCallbacks;\n}\n\n/** Collect entries that should be summarized before navigating to a different session tree entry. */\nexport async function collectEntriesForBranchSummary(\n\tsession: Session,\n\toldLeafId: string | null,\n\ttargetId: string,\n): Promise<CollectEntriesResult> {\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\tconst oldPath = new Set((await session.findEntriesOnBranch({ start: oldLeafId })).map((entry) => entry.id));\n\tconst targetPath = await session.findEntriesOnBranch({ start: targetId });\n\tlet commonAncestorId: string | null = null;\n\tfor (const entry of targetPath) {\n\t\tif (oldPath.has(entry.id)) {\n\t\t\tcommonAncestorId = entry.id;\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst entries: Entry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = await session.getEntry(current);\n\t\tif (!entry) throw new SessionError(\"invalid_entry\", `Entry ${current} not found`);\n\t\tentries.push(entry);\n\t\tcurrent = entry.parentId;\n\t}\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\nfunction getMessageFromEntry(entry: Entry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message;\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\t\tcase \"thinking_level_change\":\n\t\tcase \"configuration_update\":\n\t\tcase \"model_change\":\n\t\tcase \"active_tools_change\":\n\t\tcase \"custom\":\n\t\t\treturn undefined;\n\t}\n}\n\n/** Prepare branch entries for summarization within an optional token budget. */\nexport function prepareBranchEntries(entries: Entry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/** Generate a summary for abandoned branch entries. */\nexport async function generateBranchSummary(\n\tentries: Entry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<Result<BranchSummaryResult, BranchSummaryError>> {\n\tconst {\n\t\tmodels,\n\t\tmodel,\n\t\tsignal,\n\t\tcustomInstructions,\n\t\treplaceInstructions,\n\t\treserveTokens = 16384,\n\t\tretry,\n\t\tcallbacks,\n\t} = options;\n\tconst contextWindow = model.contextWindow || 128000;\n\tconst tokenBudget = contextWindow - reserveTokens;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries, tokenBudget);\n\n\tif (messages.length === 0) {\n\t\treturn ok({ summary: \"No content to summarize\", readFiles: [], modifiedFiles: [] });\n\t}\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tlet instructions: string;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${instructions}`;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\tconst response = await completeSimpleWithRetries(\n\t\tmodels,\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\t{ signal, maxTokens: 2048 },\n\t\tretry,\n\t\tcallbacks,\n\t);\n\tif (response.stopReason === \"aborted\") {\n\t\treturn err(new BranchSummaryError(\"aborted\", response.errorMessage || \"Branch summary aborted\"));\n\t}\n\tif (response.stopReason === \"error\") {\n\t\treturn err(\n\t\t\tnew BranchSummaryError(\n\t\t\t\t\"summarization_failed\",\n\t\t\t\t`Branch summary failed: ${response.errorMessage || \"Unknown error\"}`,\n\t\t\t),\n\t\t);\n\t}\n\n\tlet summary = contentTextForSummary(response.content);\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn ok({\n\t\tsummary: summary || \"No summary generated\",\n\t\tusage: response.usage,\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t});\n}\n"]}
1
+ {"version":3,"file":"branch-summarization.js","sourceRoot":"","sources":["../../../src/harness/compaction/branch-summarization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, Models, RetryCallbacks, RetryPolicy, Usage } from \"@earendil-works/pi-ai\";\n\nimport type { AgentMessage } from \"../../types.ts\";\nimport type { Context } from \"../context.ts\";\nimport { convertToLlm, createBranchSummaryMessage, createCompactionSummaryMessage } from \"../messages.ts\";\nimport type { Branch, Entry, Session } from \"../session/index.ts\";\nimport { BranchSummaryError, err, ok, type Result } from \"../types.ts\";\nimport {\n\tcompleteSimpleWithRetries,\n\tcreateSummaryRequestOptions,\n\testimateTokens,\n\tSUMMARIZATION_SYSTEM_PROMPT,\n\ttype SummaryRequest,\n} from \"./compaction.ts\";\nimport {\n\tcomputeFileLists,\n\tcontentTextForSummary,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tserializeConversation,\n} from \"./utils.ts\";\n\n/** Generated branch summary data ready to be persisted as a branch-summary entry. */\nexport interface BranchSummaryResult {\n\tsummary: string;\n\tusage?: Usage;\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\n/** File-operation details stored on generated branch summary entries. */\nexport interface BranchSummaryDetails {\n\t/** Files read while exploring the summarized branch. */\n\treadFiles: string[];\n\t/** Files modified while exploring the summarized branch. */\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.ts\";\n\n/** Prepared branch content for summarization. */\nexport interface BranchPreparation {\n\t/** Messages selected for the branch summary. */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from the branch. */\n\tfileOps: FileOperations;\n\t/** Estimated token count for selected messages. */\n\ttotalTokens: number;\n}\n\n/** Entries selected for branch summarization. */\nexport interface CollectEntriesResult {\n\t/** Entries to summarize in chronological order. */\n\tentries: Entry[];\n\t/** Deepest common ancestor between the previous tip and target entry. */\n\tcommonAncestorId: string | null;\n}\n\n/** Options for generating a branch summary. */\nexport interface GenerateBranchSummaryOptions {\n\t/** Provider collection the summarization request goes through; owns auth resolution. */\n\tmodels: Models;\n\t/** Model used for summarization. */\n\tmodel: Model<Api>;\n\t/** Optional instructions appended to or replacing the default prompt. */\n\tcustomInstructions?: string;\n\t/** Replace the default prompt with custom instructions instead of appending them. */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved for prompt and model output. Defaults to 16384. */\n\treserveTokens?: number;\n\t/** Optional retry policy for transient summarization errors. */\n\tretry?: RetryPolicy;\n\t/** Optional callbacks for retry reporting. */\n\tcallbacks?: RetryCallbacks;\n}\n\n/** Collect entries that should be summarized before navigating to a different session tree entry. */\nexport async function collectEntriesForBranchSummary(\n\tbranch: Pick<Branch, \"findEntries\">,\n\tsession: Pick<Session, \"getEntry\">,\n\toldTipId: string | null,\n\ttargetId: string,\n\tcontext: Context,\n): Promise<CollectEntriesResult> {\n\tif (!oldTipId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\tconst oldPath = new Set((await branch.findEntries({ start: oldTipId }, context)).map((entry) => entry.id));\n\tconst targetPath = await branch.findEntries({ start: targetId }, context);\n\tlet commonAncestorId: string | null = null;\n\tfor (const entry of targetPath) {\n\t\tif (oldPath.has(entry.id)) {\n\t\t\tcommonAncestorId = entry.id;\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst entries: Entry[] = [];\n\tlet current: string | null = oldTipId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = await session.getEntry(current, context);\n\t\tif (!entry) throw new Error(`Corrupt session: entry ${current} not found`);\n\t\tentries.push(entry);\n\t\tcurrent = entry.parentId;\n\t}\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\nfunction getMessageFromEntry(entry: Entry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message;\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\t\tcase \"custom\":\n\t\t\treturn undefined;\n\t}\n}\n\n/** Prepare branch entries for summarization within an optional token budget. */\nexport function prepareBranchEntries(entries: Entry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\tfor (const entry of entries) {\n\t\tif (\n\t\t\tentry.type !== \"branch_summary\" ||\n\t\t\ttypeof entry.details !== \"object\" ||\n\t\t\tentry.details === null ||\n\t\t\tArray.isArray(entry.details)\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(entry.details.readFiles)) {\n\t\t\tfor (const path of entry.details.readFiles) {\n\t\t\t\tif (typeof path === \"string\") fileOps.read.add(path);\n\t\t\t}\n\t\t}\n\t\tif (Array.isArray(entry.details.modifiedFiles)) {\n\t\t\tfor (const path of entry.details.modifiedFiles) {\n\t\t\t\tif (typeof path === \"string\") fileOps.edited.add(path);\n\t\t\t}\n\t\t}\n\t}\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/** Generate a summary for abandoned branch entries. */\nexport function generateBranchSummary(\n\tentries: Entry[],\n\toptions: GenerateBranchSummaryOptions,\n\tcontext: Context,\n): Promise<Result<BranchSummaryResult, BranchSummaryError>> {\n\tconst { models, model, customInstructions, replaceInstructions, reserveTokens = 16384, retry, callbacks } = options;\n\tconst contextWindow = model.contextWindow || 128000;\n\tconst preparation = prepareBranchEntries(entries, contextWindow - reserveTokens);\n\treturn generateBranchSummaryWithRequest(\n\t\tpreparation,\n\t\t{ customInstructions, replaceInstructions },\n\t\t(aiContext, requestOptions, requestContext) =>\n\t\t\tcompleteSimpleWithRetries(models, model, aiContext, requestOptions, retry, callbacks, requestContext),\n\t\tcontext,\n\t);\n}\n\nexport interface PreparedBranchSummaryOptions {\n\tcustomInstructions?: string;\n\treplaceInstructions?: boolean;\n}\n\n/** Generate a prepared branch summary through a caller-owned one-request boundary. */\nexport async function generateBranchSummaryWithRequest(\n\tpreparation: BranchPreparation,\n\toptions: PreparedBranchSummaryOptions,\n\trequest: SummaryRequest,\n\tcontext: Context,\n): Promise<Result<BranchSummaryResult, BranchSummaryError>> {\n\tconst { customInstructions, replaceInstructions } = options;\n\tconst { messages, fileOps } = preparation;\n\tif (messages.length === 0) {\n\t\treturn ok({ summary: \"No content to summarize\", readFiles: [], modifiedFiles: [] });\n\t}\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tlet instructions: string;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${instructions}`;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\tconst response = await request(\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\tcreateSummaryRequestOptions({ maxTokens: 2048 }, context),\n\t\tcontext,\n\t);\n\tif (response.stopReason === \"aborted\") {\n\t\treturn err(new BranchSummaryError(\"aborted\", response.errorMessage || \"Branch summary aborted\"));\n\t}\n\tif (response.stopReason === \"error\") {\n\t\treturn err(\n\t\t\tnew BranchSummaryError(\n\t\t\t\t\"summarization_failed\",\n\t\t\t\t`Branch summary failed: ${response.errorMessage || \"Unknown error\"}`,\n\t\t\t),\n\t\t);\n\t}\n\n\tlet summary = contentTextForSummary(response.content);\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn ok({\n\t\tsummary: summary || \"No summary generated\",\n\t\tusage: response.usage,\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t});\n}\n"]}
@@ -1,17 +1,18 @@
1
- import { type Api, type AssistantMessage, type Context, type Model, type Models, type RetryCallbacks, type RetryPolicy, type SimpleStreamOptions, type Usage } from "@earendil-works/pi-ai";
1
+ import { type Context as AiContext, type Api, type AssistantMessage, type Model, type Models, type RetryCallbacks, type RetryPolicy, type SimpleStreamOptions, type Usage } from "@earendil-works/pi-ai";
2
2
  import type { AgentMessage, ThinkingLevel } from "../../types.ts";
3
- import type { Entry } from "../session/types.ts";
3
+ import { type Context } from "../context.ts";
4
+ import type { Entry, JsonValue } from "../session/types.ts";
4
5
  import { CompactionError, type Result } from "../types.ts";
5
6
  import { type FileOperations } from "./utils.ts";
6
7
  /** File-operation details stored on generated compaction entries. */
7
- export interface CompactionDetails {
8
+ export interface CompactionDetails extends Record<string, JsonValue> {
8
9
  /** Files read in the compacted history. */
9
10
  readFiles: string[];
10
11
  /** Files modified in the compacted history. */
11
12
  modifiedFiles: string[];
12
13
  }
13
14
  /** Generated compaction data ready to be persisted as a compaction entry. */
14
- export interface CompactResult<T = unknown> {
15
+ export interface CompactResult<T = JsonValue> {
15
16
  /** Summary text that replaces compacted history in future context. */
16
17
  summary: string;
17
18
  /** Estimated context tokens before compaction. */
@@ -23,7 +24,9 @@ export interface CompactResult<T = unknown> {
23
24
  /** Optional implementation-specific details stored with the compaction entry. */
24
25
  details?: T;
25
26
  }
26
- export declare function completeSimpleWithRetries(models: Models, model: Model<Api>, context: Context, options: SimpleStreamOptions, retry?: RetryPolicy, callbacks?: RetryCallbacks): Promise<AssistantMessage>;
27
+ export type SummaryRequest = (aiContext: AiContext, options: SimpleStreamOptions, context: Context) => Promise<AssistantMessage>;
28
+ export declare function createSummaryRequestOptions(options: SimpleStreamOptions, context: Context): SimpleStreamOptions;
29
+ export declare function completeSimpleWithRetries(models: Models, model: Model<Api>, aiContext: AiContext, options: SimpleStreamOptions, retry: RetryPolicy | undefined, callbacks: RetryCallbacks | undefined, context: Context): Promise<AssistantMessage>;
27
30
  /** Compaction thresholds and retention settings. */
28
31
  export interface CompactionSettings {
29
32
  /** Enable automatic compaction decisions. */
@@ -71,9 +74,21 @@ export interface CutPointResult {
71
74
  export declare function findCutPoint(entries: Entry[], startIndex: number, endIndex: number, keepRecentTokens: number): CutPointResult;
72
75
  export declare const SUMMARIZATION_SYSTEM_PROMPT = "You are a context summarization assistant. Your task is to read a conversation between a user and an AI assistant, then produce a structured summary following the exact format specified.\n\nDo NOT continue the conversation. Do NOT respond to any questions in the conversation. ONLY output the structured summary.";
73
76
  /** Generate or update a conversation summary for compaction. */
74
- export declare function generateSummary(currentMessages: AgentMessage[], models: Models, model: Model<Api>, reserveTokens: number, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, retry?: RetryPolicy, callbacks?: RetryCallbacks): Promise<Result<string, CompactionError>>;
77
+ export declare function generateSummary(currentMessages: AgentMessage[], models: Models, model: Model<Api>, reserveTokens: number, customInstructions: string | undefined, previousSummary: string | undefined, thinkingLevel: ThinkingLevel | undefined, retry: RetryPolicy | undefined, callbacks: RetryCallbacks | undefined, context: Context): Promise<Result<string, CompactionError>>;
75
78
  /** Generate or update a conversation summary and return its provider usage. */
76
- export declare function generateSummaryWithUsage(currentMessages: AgentMessage[], models: Models, model: Model<Api>, reserveTokens: number, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, retry?: RetryPolicy, callbacks?: RetryCallbacks): Promise<Result<{
79
+ export declare function generateSummaryWithUsage(currentMessages: AgentMessage[], models: Models, model: Model<Api>, reserveTokens: number, customInstructions: string | undefined, previousSummary: string | undefined, thinkingLevel: ThinkingLevel | undefined, retry: RetryPolicy | undefined, callbacks: RetryCallbacks | undefined, context: Context): Promise<Result<{
80
+ text: string;
81
+ usage: Usage;
82
+ }, CompactionError>>;
83
+ export interface SummaryGenerationOptions {
84
+ model: Model<Api>;
85
+ reserveTokens: number;
86
+ customInstructions?: string;
87
+ previousSummary?: string;
88
+ thinkingLevel?: ThinkingLevel;
89
+ }
90
+ /** Generate one summary through a caller-owned one-request boundary. */
91
+ export declare function generateSummaryWithRequest(currentMessages: AgentMessage[], options: SummaryGenerationOptions, request: SummaryRequest, context: Context): Promise<Result<{
77
92
  text: string;
78
93
  usage: Usage;
79
94
  }, CompactionError>>;
@@ -100,5 +115,12 @@ export interface CompactionPreparation {
100
115
  export declare function prepareCompaction(pathEntries: Entry[], settings: CompactionSettings): Result<CompactionPreparation | undefined, CompactionError>;
101
116
  export { serializeConversation } from "./utils.ts";
102
117
  /** Generate compaction summary data from prepared session history. */
103
- export declare function compact(preparation: CompactionPreparation, models: Models, model: Model<Api>, customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, retry?: RetryPolicy, callbacks?: RetryCallbacks): Promise<Result<CompactResult, CompactionError>>;
118
+ export declare function compact(preparation: CompactionPreparation, models: Models, model: Model<Api>, customInstructions: string | undefined, thinkingLevel: ThinkingLevel | undefined, retry: RetryPolicy | undefined, callbacks: RetryCallbacks | undefined, context: Context): Promise<Result<CompactResult, CompactionError>>;
119
+ export interface CompactGenerationOptions {
120
+ model: Model<Api>;
121
+ customInstructions?: string;
122
+ thinkingLevel?: ThinkingLevel;
123
+ }
124
+ /** Generate compaction data through a caller-owned boundary for each provider request. */
125
+ export declare function compactWithRequest(preparation: CompactionPreparation, options: CompactGenerationOptions, request: SummaryRequest, context: Context): Promise<Result<CompactResult, CompactionError>>;
104
126
  //# sourceMappingURL=compaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../../src/harness/compaction/compaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../../src/harness/compaction/compaction.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import { dropFailedAssistantTurns, retryAssistantCall, uuidv7, } from "@earendil-works/pi-ai";
2
+ import { getTelemetryContext } from "../context.js";
2
3
  import { convertToLlm, createBranchSummaryMessage, createCompactionSummaryMessage } from "../messages.js";
3
- import { buildSessionContext } from "../session/context.js";
4
+ import { buildContextEntries, sessionEntryToContextMessages } from "../session/context.js";
4
5
  import { CompactionError, err, ok } from "../types.js";
6
+ import { addUsage } from "../utils/usage.js";
5
7
  import { computeFileLists, contentTextForSummary, createFileOps, extractFileOpsFromMessage, formatFileOperations, serializeConversation, } from "./utils.js";
6
8
  function safeJsonStringify(value) {
7
9
  try {
@@ -15,15 +17,20 @@ function extractFileOperations(messages, entries, prevCompactionIndex) {
15
17
  const fileOps = createFileOps();
16
18
  if (prevCompactionIndex >= 0) {
17
19
  const prevCompaction = entries[prevCompactionIndex];
18
- if (prevCompaction.details) {
19
- const details = prevCompaction.details;
20
- if (Array.isArray(details.readFiles)) {
21
- for (const f of details.readFiles)
22
- fileOps.read.add(f);
20
+ if (typeof prevCompaction.details === "object" &&
21
+ prevCompaction.details !== null &&
22
+ !Array.isArray(prevCompaction.details)) {
23
+ if (Array.isArray(prevCompaction.details.readFiles)) {
24
+ for (const path of prevCompaction.details.readFiles) {
25
+ if (typeof path === "string")
26
+ fileOps.read.add(path);
27
+ }
23
28
  }
24
- if (Array.isArray(details.modifiedFiles)) {
25
- for (const f of details.modifiedFiles)
26
- fileOps.edited.add(f);
29
+ if (Array.isArray(prevCompaction.details.modifiedFiles)) {
30
+ for (const path of prevCompaction.details.modifiedFiles) {
31
+ if (typeof path === "string")
32
+ fileOps.edited.add(path);
33
+ }
27
34
  }
28
35
  }
29
36
  }
@@ -50,36 +57,19 @@ function getMessageFromEntryForCompaction(entry) {
50
57
  }
51
58
  return getMessageFromEntry(entry);
52
59
  }
53
- export async function completeSimpleWithRetries(models, model, context, options, retry, callbacks) {
54
- // Summaries are standalone requests, so isolate routing and avoid cache writes that cannot be reused.
55
- const requestOptions = {
60
+ export function createSummaryRequestOptions(options, context) {
61
+ return {
56
62
  ...options,
63
+ signal: context.abortSignal,
64
+ telemetryContext: getTelemetryContext(context),
57
65
  cacheRetention: "none",
58
- sessionId: uuidv7(),
66
+ sessionId: options.sessionId ?? uuidv7(),
59
67
  };
60
- return retryAssistantCall(() => models.completeSimple(model, context, requestOptions), retry, requestOptions.signal, callbacks);
61
68
  }
62
- function combineUsage(first, second) {
63
- return {
64
- input: first.input + second.input,
65
- output: first.output + second.output,
66
- cacheRead: first.cacheRead + second.cacheRead,
67
- cacheWrite: first.cacheWrite + second.cacheWrite,
68
- ...(first.cacheWrite1h !== undefined || second.cacheWrite1h !== undefined
69
- ? { cacheWrite1h: (first.cacheWrite1h ?? 0) + (second.cacheWrite1h ?? 0) }
70
- : {}),
71
- ...(first.reasoning !== undefined || second.reasoning !== undefined
72
- ? { reasoning: (first.reasoning ?? 0) + (second.reasoning ?? 0) }
73
- : {}),
74
- totalTokens: first.totalTokens + second.totalTokens,
75
- cost: {
76
- input: first.cost.input + second.cost.input,
77
- output: first.cost.output + second.cost.output,
78
- cacheRead: first.cost.cacheRead + second.cost.cacheRead,
79
- cacheWrite: first.cost.cacheWrite + second.cost.cacheWrite,
80
- total: first.cost.total + second.cost.total,
81
- },
82
- };
69
+ export async function completeSimpleWithRetries(models, model, aiContext, options, retry, callbacks, context) {
70
+ // Summaries are standalone requests, so isolate routing and avoid cache writes that cannot be reused.
71
+ const requestOptions = createSummaryRequestOptions(options, context);
72
+ return retryAssistantCall(() => models.completeSimple(model, aiContext, requestOptions), retry, requestOptions.signal, callbacks);
83
73
  }
84
74
  /** Default compaction settings used by the harness. */
85
75
  export const DEFAULT_COMPACTION_SETTINGS = {
@@ -243,10 +233,6 @@ function findValidCutPoints(entries, startIndex, endIndex) {
243
233
  }
244
234
  break;
245
235
  }
246
- case "thinking_level_change":
247
- case "configuration_update":
248
- case "model_change":
249
- case "active_tools_change":
250
236
  case "compaction":
251
237
  case "branch_summary":
252
238
  case "custom":
@@ -395,12 +381,17 @@ Use this EXACT format:
395
381
 
396
382
  Keep each section concise. Preserve exact file paths, function names, and error messages.`;
397
383
  /** Generate or update a conversation summary for compaction. */
398
- export async function generateSummary(currentMessages, models, model, reserveTokens, signal, customInstructions, previousSummary, thinkingLevel, retry, callbacks) {
399
- const result = await generateSummaryWithUsage(currentMessages, models, model, reserveTokens, signal, customInstructions, previousSummary, thinkingLevel, retry, callbacks);
384
+ export async function generateSummary(currentMessages, models, model, reserveTokens, customInstructions, previousSummary, thinkingLevel, retry, callbacks, context) {
385
+ const result = await generateSummaryWithUsage(currentMessages, models, model, reserveTokens, customInstructions, previousSummary, thinkingLevel, retry, callbacks, context);
400
386
  return result.ok ? ok(result.value.text) : err(result.error);
401
387
  }
402
388
  /** Generate or update a conversation summary and return its provider usage. */
403
- export async function generateSummaryWithUsage(currentMessages, models, model, reserveTokens, signal, customInstructions, previousSummary, thinkingLevel, retry, callbacks) {
389
+ export function generateSummaryWithUsage(currentMessages, models, model, reserveTokens, customInstructions, previousSummary, thinkingLevel, retry, callbacks, context) {
390
+ return generateSummaryWithRequest(currentMessages, { model, reserveTokens, customInstructions, previousSummary, thinkingLevel }, (aiContext, options, requestContext) => completeSimpleWithRetries(models, model, aiContext, options, retry, callbacks, requestContext), context);
391
+ }
392
+ /** Generate one summary through a caller-owned one-request boundary. */
393
+ export async function generateSummaryWithRequest(currentMessages, options, request, context) {
394
+ const { model, reserveTokens, customInstructions, previousSummary, thinkingLevel } = options;
404
395
  const maxTokens = Math.min(Math.floor(0.8 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY);
405
396
  let basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;
406
397
  if (customInstructions) {
@@ -421,9 +412,9 @@ export async function generateSummaryWithUsage(currentMessages, models, model, r
421
412
  },
422
413
  ];
423
414
  const completionOptions = model.reasoning && thinkingLevel && thinkingLevel !== "off"
424
- ? { maxTokens, signal, reasoning: thinkingLevel }
425
- : { maxTokens, signal };
426
- const response = await completeSimpleWithRetries(models, model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, completionOptions, retry, callbacks);
415
+ ? { maxTokens, reasoning: thinkingLevel }
416
+ : { maxTokens };
417
+ const response = await request({ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, createSummaryRequestOptions(completionOptions, context), context);
427
418
  if (response.stopReason === "aborted") {
428
419
  return err(new CompactionError("aborted", response.errorMessage || "Summarization aborted"));
429
420
  }
@@ -461,7 +452,7 @@ export function prepareCompaction(pathEntries, settings) {
461
452
  compactableEntries = [...virtualRetainedEntries, ...pathEntries.slice(prevCompactionIndex + 1)];
462
453
  }
463
454
  const boundaryEnd = compactableEntries.length;
464
- const tokensBefore = estimateContextTokens(buildSessionContext(pathEntries).messages).tokens;
455
+ const tokensBefore = estimateContextTokens(buildContextEntries(pathEntries).flatMap(sessionEntryToContextMessages)).tokens;
465
456
  const cutPoint = findCutPoint(compactableEntries, 0, boundaryEnd, settings.keepRecentTokens);
466
457
  const historyEnd = cutPoint.isSplitTurn ? cutPoint.turnStartIndex : cutPoint.firstKeptEntryIndex;
467
458
  const messagesToSummarize = [];
@@ -517,7 +508,12 @@ Summarize the prefix to provide context for the retained suffix:
517
508
  Be concise. Focus on what's needed to understand the kept suffix.`;
518
509
  export { serializeConversation } from "./utils.js";
519
510
  /** Generate compaction summary data from prepared session history. */
520
- export async function compact(preparation, models, model, customInstructions, signal, thinkingLevel, retry, callbacks) {
511
+ export function compact(preparation, models, model, customInstructions, thinkingLevel, retry, callbacks, context) {
512
+ return compactWithRequest(preparation, { model, customInstructions, thinkingLevel }, (aiContext, options, requestContext) => completeSimpleWithRetries(models, model, aiContext, options, retry, callbacks, requestContext), context);
513
+ }
514
+ /** Generate compaction data through a caller-owned boundary for each provider request. */
515
+ export async function compactWithRequest(preparation, options, request, context) {
516
+ const { model, customInstructions, thinkingLevel } = options;
521
517
  const { messagesToSummarize, turnPrefixMessages, retainedTail, isSplitTurn, tokensBefore, previousSummary, fileOps, settings, } = preparation;
522
518
  let summary;
523
519
  let summaryUsage;
@@ -525,22 +521,20 @@ export async function compact(preparation, models, model, customInstructions, si
525
521
  let historyText = "No prior history.";
526
522
  let historyUsage;
527
523
  if (messagesToSummarize.length > 0) {
528
- const historyResult = await generateSummaryWithUsage(messagesToSummarize, models, model, settings.reserveTokens, signal, customInstructions, previousSummary, thinkingLevel, retry, callbacks);
524
+ const historyResult = await generateSummaryWithRequest(messagesToSummarize, { model, reserveTokens: settings.reserveTokens, customInstructions, previousSummary, thinkingLevel }, request, context);
529
525
  if (!historyResult.ok)
530
526
  return err(historyResult.error);
531
527
  historyText = historyResult.value.text;
532
528
  historyUsage = historyResult.value.usage;
533
529
  }
534
- const turnPrefixResult = await generateTurnPrefixSummary(turnPrefixMessages, models, model, settings.reserveTokens, signal, thinkingLevel, retry, callbacks);
530
+ const turnPrefixResult = await generateTurnPrefixSummary(turnPrefixMessages, model, settings.reserveTokens, thinkingLevel, request, context);
535
531
  if (!turnPrefixResult.ok)
536
532
  return err(turnPrefixResult.error);
537
533
  summary = `${historyText}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult.value.text}`;
538
- summaryUsage = historyUsage
539
- ? combineUsage(historyUsage, turnPrefixResult.value.usage)
540
- : turnPrefixResult.value.usage;
534
+ summaryUsage = historyUsage ? addUsage(historyUsage, turnPrefixResult.value.usage) : turnPrefixResult.value.usage;
541
535
  }
542
536
  else {
543
- const summaryResult = await generateSummaryWithUsage(messagesToSummarize, models, model, settings.reserveTokens, signal, customInstructions, previousSummary, thinkingLevel, retry, callbacks);
537
+ const summaryResult = await generateSummaryWithRequest(messagesToSummarize, { model, reserveTokens: settings.reserveTokens, customInstructions, previousSummary, thinkingLevel }, request, context);
544
538
  if (!summaryResult.ok)
545
539
  return err(summaryResult.error);
546
540
  summary = summaryResult.value.text;
@@ -548,15 +542,10 @@ export async function compact(preparation, models, model, customInstructions, si
548
542
  }
549
543
  const { readFiles, modifiedFiles } = computeFileLists(fileOps);
550
544
  summary += formatFileOperations(readFiles, modifiedFiles);
551
- return ok({
552
- summary,
553
- tokensBefore,
554
- usage: summaryUsage,
555
- retainedTail,
556
- details: { readFiles, modifiedFiles },
557
- });
545
+ const details = { readFiles, modifiedFiles };
546
+ return ok({ summary, tokensBefore, usage: summaryUsage, retainedTail, details });
558
547
  }
559
- async function generateTurnPrefixSummary(messages, models, model, reserveTokens, signal, thinkingLevel, retry, callbacks) {
548
+ async function generateTurnPrefixSummary(messages, model, reserveTokens, thinkingLevel, request, context) {
560
549
  const maxTokens = Math.min(Math.floor(0.5 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY);
561
550
  const llmMessages = convertToLlm(messages);
562
551
  const conversationText = serializeConversation(llmMessages);
@@ -569,9 +558,9 @@ async function generateTurnPrefixSummary(messages, models, model, reserveTokens,
569
558
  },
570
559
  ];
571
560
  const completionOptions = model.reasoning && thinkingLevel && thinkingLevel !== "off"
572
- ? { maxTokens, signal, reasoning: thinkingLevel }
573
- : { maxTokens, signal };
574
- const response = await completeSimpleWithRetries(models, model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, completionOptions, retry, callbacks);
561
+ ? { maxTokens, reasoning: thinkingLevel }
562
+ : { maxTokens };
563
+ const response = await request({ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, createSummaryRequestOptions(completionOptions, context), context);
575
564
  if (response.stopReason === "aborted") {
576
565
  return err(new CompactionError("aborted", response.errorMessage || "Turn prefix summarization aborted"));
577
566
  }