@code-yeongyu/senpi 2026.8.29 → 2026.8.30-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/core/agent-session.d.ts +45 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +329 -29
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/bash-executor.d.ts.map +1 -1
  7. package/dist/core/bash-executor.js +155 -37
  8. package/dist/core/bash-executor.js.map +1 -1
  9. package/dist/core/compaction/compaction-execution.d.ts +14 -0
  10. package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
  11. package/dist/core/compaction/compaction-execution.js +121 -0
  12. package/dist/core/compaction/compaction-execution.js.map +1 -0
  13. package/dist/core/compaction/compaction-settings.d.ts +17 -0
  14. package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
  15. package/dist/core/compaction/compaction-settings.js +17 -0
  16. package/dist/core/compaction/compaction-settings.js.map +1 -0
  17. package/dist/core/compaction/compaction.d.ts +13 -26
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +7 -136
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
  22. package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
  23. package/dist/core/compaction/ideal-compaction-settings.js +7 -0
  24. package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
  25. package/dist/core/compaction-settings-access.d.ts +19 -0
  26. package/dist/core/compaction-settings-access.d.ts.map +1 -0
  27. package/dist/core/compaction-settings-access.js +10 -0
  28. package/dist/core/compaction-settings-access.js.map +1 -0
  29. package/dist/core/compaction-settings-resolver.d.ts +22 -0
  30. package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
  31. package/dist/core/compaction-settings-resolver.js +46 -0
  32. package/dist/core/compaction-settings-resolver.js.map +1 -0
  33. package/dist/core/credential-accounts.d.ts.map +1 -1
  34. package/dist/core/credential-accounts.js +23 -9
  35. package/dist/core/credential-accounts.js.map +1 -1
  36. package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
  37. package/dist/core/credential-pool/env-slots.js +2 -0
  38. package/dist/core/credential-pool/env-slots.js.map +1 -1
  39. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +17 -0
  40. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
  41. package/dist/core/extensions/builtin/compaction/context-pipeline.js +28 -0
  42. package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
  43. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
  44. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +163 -48
  46. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  47. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
  48. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
  49. package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
  50. package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
  51. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
  52. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
  53. package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
  54. package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
  55. package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
  56. package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/idle.js +13 -2
  58. package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
  60. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/index.js +121 -147
  62. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  64. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/log.js +2 -0
  66. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  67. package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
  68. package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
  69. package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
  70. package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
  71. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
  72. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
  74. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
  75. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
  76. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  77. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  78. package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
  79. package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
  81. package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
  82. package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
  83. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  84. package/dist/core/extensions/builtin/compaction/policy.js +43 -26
  85. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  86. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  87. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  88. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  89. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
  90. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
  91. package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
  92. package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
  93. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
  94. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
  95. package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
  96. package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
  97. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
  98. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
  99. package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
  100. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
  101. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
  102. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  103. package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
  104. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  105. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
  106. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
  107. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
  108. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
  109. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
  110. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
  111. package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
  112. package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
  113. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  114. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  115. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  116. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  117. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  118. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  119. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  120. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  121. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  122. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  123. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  124. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  125. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  126. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  127. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  128. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  129. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  130. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  131. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  132. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  133. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  134. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  135. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  136. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  137. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  138. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  139. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  140. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  141. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  142. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  143. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  144. package/dist/core/model-resolver.js +2 -2
  145. package/dist/core/model-resolver.js.map +1 -1
  146. package/dist/core/session-manager.d.ts.map +1 -1
  147. package/dist/core/session-manager.js +10 -1
  148. package/dist/core/session-manager.js.map +1 -1
  149. package/dist/core/settings-manager.d.ts +15 -85
  150. package/dist/core/settings-manager.d.ts.map +1 -1
  151. package/dist/core/settings-manager.js +15 -17
  152. package/dist/core/settings-manager.js.map +1 -1
  153. package/dist/core/settings-public-types.d.ts +11 -0
  154. package/dist/core/settings-public-types.d.ts.map +1 -0
  155. package/dist/core/settings-public-types.js +2 -0
  156. package/dist/core/settings-public-types.js.map +1 -0
  157. package/dist/core/settings-shapes.d.ts +36 -0
  158. package/dist/core/settings-shapes.d.ts.map +1 -0
  159. package/dist/core/settings-shapes.js +2 -0
  160. package/dist/core/settings-shapes.js.map +1 -0
  161. package/dist/core/shared-host-policy.d.ts +6 -0
  162. package/dist/core/shared-host-policy.d.ts.map +1 -0
  163. package/dist/core/shared-host-policy.js +7 -0
  164. package/dist/core/shared-host-policy.js.map +1 -0
  165. package/dist/core/terminal-settings.d.ts +22 -0
  166. package/dist/core/terminal-settings.d.ts.map +1 -0
  167. package/dist/core/terminal-settings.js +2 -0
  168. package/dist/core/terminal-settings.js.map +1 -0
  169. package/dist/core/tools/bash.d.ts.map +1 -1
  170. package/dist/core/tools/bash.js +97 -15
  171. package/dist/core/tools/bash.js.map +1 -1
  172. package/dist/core/tools/index.d.ts +1 -0
  173. package/dist/core/tools/index.d.ts.map +1 -1
  174. package/dist/core/tools/index.js +8 -0
  175. package/dist/core/tools/index.js.map +1 -1
  176. package/dist/core/tools/output-accumulator.d.ts +1 -1
  177. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  178. package/dist/core/tools/output-accumulator.js +76 -34
  179. package/dist/core/tools/output-accumulator.js.map +1 -1
  180. package/dist/main.d.ts.map +1 -1
  181. package/dist/main.js +17 -5
  182. package/dist/main.js.map +1 -1
  183. package/dist/modes/interactive/components/footer.d.ts +7 -0
  184. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  185. package/dist/modes/interactive/components/footer.js +34 -8
  186. package/dist/modes/interactive/components/footer.js.map +1 -1
  187. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  188. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  189. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  190. package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
  191. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  192. package/dist/modes/interactive/interactive-host-runtime.js +336 -65
  193. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  194. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  195. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  196. package/dist/modes/interactive/interactive-mode.js +114 -2
  197. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  198. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  199. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  200. package/dist/modes/rpc/connection-handler.js +145 -49
  201. package/dist/modes/rpc/connection-handler.js.map +1 -1
  202. package/dist/modes/rpc/host-ensure.d.ts +19 -0
  203. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  204. package/dist/modes/rpc/host-ensure.js +79 -15
  205. package/dist/modes/rpc/host-ensure.js.map +1 -1
  206. package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
  207. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  208. package/dist/modes/rpc/host-lifecycle.js +96 -25
  209. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  210. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  211. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  212. package/dist/modes/rpc/host-watchdog.js +23 -12
  213. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  214. package/dist/modes/rpc/multi-session-host.js +6 -4
  215. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  216. package/dist/modes/rpc/rpc-client.d.ts +10 -4
  217. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  218. package/dist/modes/rpc/rpc-client.js +47 -10
  219. package/dist/modes/rpc/rpc-client.js.map +1 -1
  220. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  221. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  222. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  223. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  224. package/dist/modes/rpc/rpc-types.d.ts +86 -1
  225. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  226. package/dist/modes/rpc/rpc-types.js.map +1 -1
  227. package/dist/modes/rpc/session-binding.d.ts +1 -0
  228. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  229. package/dist/modes/rpc/session-binding.js +21 -1
  230. package/dist/modes/rpc/session-binding.js.map +1 -1
  231. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  232. package/dist/modes/rpc/session-command-router.js +1 -0
  233. package/dist/modes/rpc/session-command-router.js.map +1 -1
  234. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  235. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  236. package/dist/modes/rpc/session-event-writer.js +54 -7
  237. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  238. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  239. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  240. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  241. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  242. package/dist/modes/rpc/session-registry.d.ts +10 -2
  243. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  244. package/dist/modes/rpc/session-registry.js +58 -4
  245. package/dist/modes/rpc/session-registry.js.map +1 -1
  246. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  247. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  248. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  249. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  250. package/docs/rpc.md +18 -2
  251. package/docs/settings.md +34 -0
  252. package/docs/windows.md +2 -0
  253. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +50 -0
  254. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  255. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  256. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  257. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  258. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  259. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  260. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  261. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  262. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  266. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  268. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
  270. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  272. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  273. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  274. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  275. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  276. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  278. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  282. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  284. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  287. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  290. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  292. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  295. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  296. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  297. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  298. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  299. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  300. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  301. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  302. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  303. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  305. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  306. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  307. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  308. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  309. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  310. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  311. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAgLrB"}
1
+ {"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAStD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAuSrB"}
@@ -7,11 +7,13 @@
7
7
  */
8
8
  import { randomBytes } from "node:crypto";
9
9
  import { createWriteStream } from "node:fs";
10
+ import { rm } from "node:fs/promises";
10
11
  import { tmpdir } from "node:os";
11
12
  import { join } from "node:path";
12
13
  import { stripAnsi } from "../utils/ansi.js";
13
14
  import { sanitizeBinaryOutput } from "../utils/shell.js";
14
15
  import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
16
+ const NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS = 5_000;
15
17
  // ============================================================================
16
18
  // Implementation
17
19
  // ============================================================================
@@ -21,13 +23,37 @@ import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
21
23
  */
22
24
  export async function executeBashWithOperations(command, cwd, operations, options) {
23
25
  const outputChunks = [];
26
+ const callbackAbortController = new AbortController();
27
+ const executionSignal = options?.signal
28
+ ? AbortSignal.any([options.signal, callbackAbortController.signal])
29
+ : callbackAbortController.signal;
30
+ let callbackError;
31
+ let hasCallbackError = false;
32
+ let callbacksAbandoned = false;
33
+ const callbackPromises = new Set();
34
+ const waitForCallbacks = async (allowAbandonment) => {
35
+ if (callbackPromises.size === 0)
36
+ return true;
37
+ const settled = Promise.allSettled([...callbackPromises]).then(() => true);
38
+ let timeout;
39
+ const bounded = new Promise((resolve) => {
40
+ timeout = setTimeout(resolve, allowAbandonment ? 100 : NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS, false);
41
+ timeout.unref?.();
42
+ });
43
+ try {
44
+ return await Promise.race([settled, bounded]);
45
+ }
46
+ finally {
47
+ if (timeout)
48
+ clearTimeout(timeout);
49
+ }
50
+ };
24
51
  let outputChunkStart = 0;
25
52
  let outputBytes = 0;
26
53
  const maxOutputBytes = DEFAULT_MAX_BYTES * 2;
27
54
  let tempFilePath;
28
55
  let tempFileStream;
29
56
  let tempFileError;
30
- let tempFileErrorListener;
31
57
  let totalBytes = 0;
32
58
  const compactOutputChunks = () => {
33
59
  if (outputChunkStart < 64) {
@@ -44,10 +70,9 @@ export async function executeBashWithOperations(command, cwd, operations, option
44
70
  const id = randomBytes(8).toString("hex");
45
71
  tempFilePath = join(tmpdir(), `pi-bash-${id}.log`);
46
72
  tempFileStream = createWriteStream(tempFilePath);
47
- tempFileErrorListener = (error) => {
73
+ tempFileStream.on("error", (error) => {
48
74
  tempFileError ??= error;
49
- };
50
- tempFileStream.on("error", tempFileErrorListener);
75
+ });
51
76
  for (let i = outputChunkStart; i < outputChunks.length; i++) {
52
77
  const chunk = outputChunks[i];
53
78
  if (chunk !== undefined) {
@@ -62,42 +87,84 @@ export async function executeBashWithOperations(command, cwd, operations, option
62
87
  return;
63
88
  }
64
89
  if (tempFileError) {
65
- if (tempFileErrorListener)
66
- stream.off("error", tempFileErrorListener);
67
- stream.destroy();
90
+ await new Promise((resolve) => {
91
+ if (stream.closed) {
92
+ resolve();
93
+ return;
94
+ }
95
+ stream.once("close", resolve);
96
+ stream.destroy();
97
+ });
68
98
  throw tempFileError;
69
99
  }
70
100
  await new Promise((resolve, reject) => {
71
- let settled = false;
72
- const cleanup = () => {
101
+ let finished = false;
102
+ let closed = false;
103
+ let streamError;
104
+ const settle = () => {
105
+ if (!closed) {
106
+ return;
107
+ }
108
+ stream.off("finish", onFinish);
73
109
  stream.off("error", onError);
74
- if (tempFileErrorListener)
75
- stream.off("error", tempFileErrorListener);
76
110
  stream.off("close", onClose);
77
- };
78
- const settle = (error) => {
79
- if (settled)
80
- return;
81
- settled = true;
82
- cleanup();
83
- if (error)
84
- reject(error);
85
- else
111
+ if (streamError) {
112
+ reject(streamError);
113
+ }
114
+ else if (finished) {
86
115
  resolve();
116
+ }
117
+ else {
118
+ reject(new Error("Bash spill stream closed before finish"));
119
+ }
87
120
  };
88
121
  const onError = (error) => {
89
- tempFileError ??= error;
122
+ streamError ??= error;
123
+ };
124
+ const onFinish = () => {
125
+ finished = true;
90
126
  };
91
127
  const onClose = () => {
92
- settle(tempFileError);
128
+ closed = true;
129
+ if (!finished && !stream.destroyed) {
130
+ stream.destroy();
131
+ }
132
+ settle();
93
133
  };
94
- stream.on("error", onError);
134
+ stream.once("error", onError);
135
+ stream.once("finish", onFinish);
95
136
  stream.once("close", onClose);
96
137
  stream.end();
97
138
  });
98
139
  };
140
+ const removeTempFile = async () => {
141
+ if (!tempFilePath) {
142
+ return;
143
+ }
144
+ const path = tempFilePath;
145
+ await rm(path, { force: true });
146
+ tempFilePath = undefined;
147
+ };
148
+ const closeTempFileAndCleanup = async (primaryError) => {
149
+ let finalError = primaryError;
150
+ try {
151
+ await closeTempFileStream();
152
+ }
153
+ catch (closeError) {
154
+ finalError = new AggregateError([primaryError, closeError], "Bash output cleanup failed");
155
+ }
156
+ try {
157
+ await removeTempFile();
158
+ }
159
+ catch (unlinkError) {
160
+ finalError = new AggregateError([finalError, unlinkError], "Bash output cleanup failed");
161
+ }
162
+ throw finalError;
163
+ };
99
164
  const decoder = new TextDecoder();
100
165
  const onData = (data) => {
166
+ if (callbackAbortController.signal.aborted)
167
+ return;
101
168
  totalBytes += data.length;
102
169
  // Sanitize: strip ANSI, replace binary garbage, normalize newlines
103
170
  const text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\r/g, "");
@@ -121,7 +188,33 @@ export async function executeBashWithOperations(command, cwd, operations, option
121
188
  compactOutputChunks();
122
189
  // Stream to callback
123
190
  if (options?.onChunk) {
124
- options.onChunk(text);
191
+ try {
192
+ const callbackResult = options.onChunk(text);
193
+ if (callbackResult && typeof callbackResult.then === "function") {
194
+ const callbackPromise = Promise.resolve(callbackResult).then(() => undefined, (error) => {
195
+ if (!hasCallbackError) {
196
+ callbackError = error;
197
+ hasCallbackError = true;
198
+ }
199
+ if (callbacksAbandoned) {
200
+ process.emitWarning(`Bash output callback rejected after the command timeout: ${error instanceof Error ? error.message : String(error)}`, { code: "BASH_CALLBACK_ERROR" });
201
+ }
202
+ callbackAbortController.abort();
203
+ throw error;
204
+ });
205
+ callbackPromises.add(callbackPromise);
206
+ void callbackPromise.catch(() => { }).finally(() => callbackPromises.delete(callbackPromise));
207
+ return callbackPromise;
208
+ }
209
+ }
210
+ catch (error) {
211
+ if (!hasCallbackError) {
212
+ callbackError = error;
213
+ hasCallbackError = true;
214
+ }
215
+ callbackAbortController.abort();
216
+ throw error;
217
+ }
125
218
  }
126
219
  };
127
220
  const finishDecoder = () => {
@@ -141,40 +234,65 @@ export async function executeBashWithOperations(command, cwd, operations, option
141
234
  return { fullOutput, truncationResult };
142
235
  }
143
236
  catch (error) {
144
- try {
145
- await closeTempFileStream();
146
- }
147
- catch (closeError) {
148
- throw new AggregateError([error, closeError], "Bash output finalization and spill cleanup failed");
149
- }
150
- throw error;
237
+ return await closeTempFileAndCleanup(error);
151
238
  }
152
239
  };
153
240
  let result;
154
241
  try {
155
242
  result = await operations.exec(command, cwd, {
156
243
  onData,
157
- signal: options?.signal,
244
+ signal: executionSignal,
158
245
  });
159
246
  }
160
247
  catch (err) {
248
+ await waitForCallbacks(options?.signal?.aborted === true);
161
249
  // Check if it was an abort
250
+ if (hasCallbackError) {
251
+ return await closeTempFileAndCleanup(callbackError);
252
+ }
162
253
  if (options?.signal?.aborted) {
163
254
  const { fullOutput, truncationResult } = await prepareFinalOutput();
164
- await closeTempFileStream();
255
+ try {
256
+ await closeTempFileStream();
257
+ }
258
+ catch (error) {
259
+ return await closeTempFileAndCleanup(error);
260
+ }
261
+ try {
262
+ await removeTempFile();
263
+ }
264
+ catch (cleanupError) {
265
+ throw new AggregateError([cleanupError], "Bash output cleanup failed");
266
+ }
165
267
  return {
166
268
  output: truncationResult.truncated ? truncationResult.content : fullOutput,
167
269
  exitCode: undefined,
168
270
  cancelled: true,
169
271
  truncated: truncationResult.truncated,
170
- fullOutputPath: tempFilePath,
171
272
  };
172
273
  }
173
- await closeTempFileStream();
174
- throw err;
274
+ return await closeTempFileAndCleanup(err);
275
+ }
276
+ // Normal completion has a generous bound for observer callbacks: unlike the
277
+ // cancellation path, it must not hang forever on an observer that never
278
+ // settles, while still allowing legitimately slow callbacks to finish. A
279
+ // callback that exceeds the public bound is an execution failure, not silent
280
+ // abandonment, so its spill is cleaned before the caller is released.
281
+ const callbacksSettled = await waitForCallbacks(false);
282
+ if (!callbacksSettled) {
283
+ callbacksAbandoned = true;
284
+ return await closeTempFileAndCleanup(new Error(`Bash output callback did not settle within ${NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS}ms`));
285
+ }
286
+ if (hasCallbackError) {
287
+ return await closeTempFileAndCleanup(callbackError);
175
288
  }
176
289
  const { fullOutput, truncationResult } = await prepareFinalOutput();
177
- await closeTempFileStream();
290
+ try {
291
+ await closeTempFileStream();
292
+ }
293
+ catch (error) {
294
+ return await closeTempFileAndCleanup(error);
295
+ }
178
296
  const cancelled = options?.signal?.aborted ?? false;
179
297
  return {
180
298
  output: truncationResult.truncated ? truncationResult.content : fullOutput,
@@ -1 +1 @@
1
- {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputChunkStart = 0;\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet tempFileError: Error | undefined;\n\tlet tempFileErrorListener: ((error: Error) => void) | undefined;\n\tlet totalBytes = 0;\n\n\tconst compactOutputChunks = () => {\n\t\tif (outputChunkStart < 64) {\n\t\t\treturn;\n\t\t}\n\t\toutputChunks.splice(0, outputChunkStart);\n\t\toutputChunkStart = 0;\n\t};\n\n\tconst outputText = () => outputChunks.slice(outputChunkStart).join(\"\");\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\ttempFileErrorListener = (error) => {\n\t\t\ttempFileError ??= error;\n\t\t};\n\t\ttempFileStream.on(\"error\", tempFileErrorListener);\n\t\tfor (let i = outputChunkStart; i < outputChunks.length; i++) {\n\t\t\tconst chunk = outputChunks[i];\n\t\t\tif (chunk !== undefined) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst closeTempFileStream = async () => {\n\t\tconst stream = tempFileStream;\n\t\ttempFileStream = undefined;\n\t\tif (!stream) {\n\t\t\treturn;\n\t\t}\n\t\tif (tempFileError) {\n\t\t\tif (tempFileErrorListener) stream.off(\"error\", tempFileErrorListener);\n\t\t\tstream.destroy();\n\t\t\tthrow tempFileError;\n\t\t}\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst cleanup = () => {\n\t\t\t\tstream.off(\"error\", onError);\n\t\t\t\tif (tempFileErrorListener) stream.off(\"error\", tempFileErrorListener);\n\t\t\t\tstream.off(\"close\", onClose);\n\t\t\t};\n\t\t\tconst settle = (error?: Error) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tif (error) reject(error);\n\t\t\t\telse resolve();\n\t\t\t};\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\ttempFileError ??= error;\n\t\t\t};\n\t\t\tconst onClose = () => {\n\t\t\t\tsettle(tempFileError);\n\t\t\t};\n\t\t\tstream.on(\"error\", onError);\n\t\t\tstream.once(\"close\", onClose);\n\t\t\tstream.end();\n\t\t});\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunkStart < outputChunks.length - 1) {\n\t\t\tconst removed = outputChunks[outputChunkStart];\n\t\t\tif (removed === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toutputBytes -= removed.length;\n\t\t\toutputChunkStart++;\n\t\t}\n\t\tcompactOutputChunks();\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\tconst finishDecoder = () => {\n\t\tconst finalText = sanitizeBinaryOutput(stripAnsi(decoder.decode())).replace(/\\r/g, \"\");\n\t\tif (finalText.length > 0) {\n\t\t\tonData(Buffer.from(finalText, \"utf-8\"));\n\t\t}\n\t};\n\n\tconst prepareFinalOutput = async () => {\n\t\ttry {\n\t\t\tfinishDecoder();\n\t\t\tconst fullOutput = outputText();\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\treturn { fullOutput, truncationResult };\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait closeTempFileStream();\n\t\t\t} catch (closeError) {\n\t\t\t\tthrow new AggregateError([error, closeError], \"Bash output finalization and spill cleanup failed\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tlet result: Awaited<ReturnType<BashOperations[\"exec\"]>>;\n\ttry {\n\t\tresult = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst { fullOutput, truncationResult } = await prepareFinalOutput();\n\t\t\tawait closeTempFileStream();\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tawait closeTempFileStream();\n\n\t\tthrow err;\n\t}\n\n\tconst { fullOutput, truncationResult } = await prepareFinalOutput();\n\tawait closeTempFileStream();\n\tconst cancelled = options?.signal?.aborted ?? false;\n\n\treturn {\n\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\tcancelled,\n\t\ttruncated: truncationResult.truncated,\n\t\tfullOutputPath: tempFilePath,\n\t};\n}\n"]}
1
+ {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\nconst NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS = 5_000;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tconst callbackAbortController = new AbortController();\n\tconst executionSignal = options?.signal\n\t\t? AbortSignal.any([options.signal, callbackAbortController.signal])\n\t\t: callbackAbortController.signal;\n\tlet callbackError: unknown;\n\tlet hasCallbackError = false;\n\tlet callbacksAbandoned = false;\n\tconst callbackPromises = new Set<Promise<void>>();\n\tconst waitForCallbacks = async (allowAbandonment: boolean): Promise<boolean> => {\n\t\tif (callbackPromises.size === 0) return true;\n\t\tconst settled = Promise.allSettled([...callbackPromises]).then(() => true);\n\t\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\t\tconst bounded = new Promise<false>((resolve) => {\n\t\t\ttimeout = setTimeout(resolve, allowAbandonment ? 100 : NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS, false);\n\t\t\ttimeout.unref?.();\n\t\t});\n\t\ttry {\n\t\t\treturn await Promise.race([settled, bounded]);\n\t\t} finally {\n\t\t\tif (timeout) clearTimeout(timeout);\n\t\t}\n\t};\n\tlet outputChunkStart = 0;\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet tempFileError: Error | undefined;\n\tlet totalBytes = 0;\n\n\tconst compactOutputChunks = () => {\n\t\tif (outputChunkStart < 64) {\n\t\t\treturn;\n\t\t}\n\t\toutputChunks.splice(0, outputChunkStart);\n\t\toutputChunkStart = 0;\n\t};\n\n\tconst outputText = () => outputChunks.slice(outputChunkStart).join(\"\");\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\ttempFileStream.on(\"error\", (error) => {\n\t\t\ttempFileError ??= error;\n\t\t});\n\t\tfor (let i = outputChunkStart; i < outputChunks.length; i++) {\n\t\t\tconst chunk = outputChunks[i];\n\t\t\tif (chunk !== undefined) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst closeTempFileStream = async () => {\n\t\tconst stream = tempFileStream;\n\t\ttempFileStream = undefined;\n\t\tif (!stream) {\n\t\t\treturn;\n\t\t}\n\t\tif (tempFileError) {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tif (stream.closed) {\n\t\t\t\t\tresolve();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstream.once(\"close\", resolve);\n\t\t\t\tstream.destroy();\n\t\t\t});\n\t\t\tthrow tempFileError;\n\t\t}\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tlet finished = false;\n\t\t\tlet closed = false;\n\t\t\tlet streamError: Error | undefined;\n\t\t\tconst settle = () => {\n\t\t\t\tif (!closed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstream.off(\"finish\", onFinish);\n\t\t\t\tstream.off(\"error\", onError);\n\t\t\t\tstream.off(\"close\", onClose);\n\t\t\t\tif (streamError) {\n\t\t\t\t\treject(streamError);\n\t\t\t\t} else if (finished) {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\treject(new Error(\"Bash spill stream closed before finish\"));\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\tstreamError ??= error;\n\t\t\t};\n\t\t\tconst onFinish = () => {\n\t\t\t\tfinished = true;\n\t\t\t};\n\t\t\tconst onClose = () => {\n\t\t\t\tclosed = true;\n\t\t\t\tif (!finished && !stream.destroyed) {\n\t\t\t\t\tstream.destroy();\n\t\t\t\t}\n\t\t\t\tsettle();\n\t\t\t};\n\t\t\tstream.once(\"error\", onError);\n\t\t\tstream.once(\"finish\", onFinish);\n\t\t\tstream.once(\"close\", onClose);\n\t\t\tstream.end();\n\t\t});\n\t};\n\n\tconst removeTempFile = async (): Promise<void> => {\n\t\tif (!tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst path = tempFilePath;\n\t\tawait rm(path, { force: true });\n\t\ttempFilePath = undefined;\n\t};\n\n\tconst closeTempFileAndCleanup = async (primaryError: unknown): Promise<never> => {\n\t\tlet finalError = primaryError;\n\t\ttry {\n\t\t\tawait closeTempFileStream();\n\t\t} catch (closeError) {\n\t\t\tfinalError = new AggregateError([primaryError, closeError], \"Bash output cleanup failed\");\n\t\t}\n\t\ttry {\n\t\t\tawait removeTempFile();\n\t\t} catch (unlinkError) {\n\t\t\tfinalError = new AggregateError([finalError, unlinkError], \"Bash output cleanup failed\");\n\t\t}\n\t\tthrow finalError;\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\tif (callbackAbortController.signal.aborted) return;\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunkStart < outputChunks.length - 1) {\n\t\t\tconst removed = outputChunks[outputChunkStart];\n\t\t\tif (removed === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toutputBytes -= removed.length;\n\t\t\toutputChunkStart++;\n\t\t}\n\t\tcompactOutputChunks();\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\ttry {\n\t\t\t\tconst callbackResult = (options.onChunk as (chunk: string) => unknown)(text);\n\t\t\t\tif (callbackResult && typeof (callbackResult as { then?: unknown }).then === \"function\") {\n\t\t\t\t\tconst callbackPromise = Promise.resolve(callbackResult).then(\n\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t\t(error) => {\n\t\t\t\t\t\t\tif (!hasCallbackError) {\n\t\t\t\t\t\t\t\tcallbackError = error;\n\t\t\t\t\t\t\t\thasCallbackError = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (callbacksAbandoned) {\n\t\t\t\t\t\t\t\tprocess.emitWarning(\n\t\t\t\t\t\t\t\t\t`Bash output callback rejected after the command timeout: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\t\t\t\t{ code: \"BASH_CALLBACK_ERROR\" },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcallbackAbortController.abort();\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tcallbackPromises.add(callbackPromise);\n\t\t\t\t\tvoid callbackPromise.catch(() => {}).finally(() => callbackPromises.delete(callbackPromise));\n\t\t\t\t\treturn callbackPromise;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (!hasCallbackError) {\n\t\t\t\t\tcallbackError = error;\n\t\t\t\t\thasCallbackError = true;\n\t\t\t\t}\n\t\t\t\tcallbackAbortController.abort();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst finishDecoder = () => {\n\t\tconst finalText = sanitizeBinaryOutput(stripAnsi(decoder.decode())).replace(/\\r/g, \"\");\n\t\tif (finalText.length > 0) {\n\t\t\tonData(Buffer.from(finalText, \"utf-8\"));\n\t\t}\n\t};\n\n\tconst prepareFinalOutput = async () => {\n\t\ttry {\n\t\t\tfinishDecoder();\n\t\t\tconst fullOutput = outputText();\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\treturn { fullOutput, truncationResult };\n\t\t} catch (error) {\n\t\t\treturn await closeTempFileAndCleanup(error);\n\t\t}\n\t};\n\n\tlet result: Awaited<ReturnType<BashOperations[\"exec\"]>>;\n\ttry {\n\t\tresult = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: executionSignal,\n\t\t});\n\t} catch (err) {\n\t\tawait waitForCallbacks(options?.signal?.aborted === true);\n\t\t// Check if it was an abort\n\t\tif (hasCallbackError) {\n\t\t\treturn await closeTempFileAndCleanup(callbackError);\n\t\t}\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst { fullOutput, truncationResult } = await prepareFinalOutput();\n\t\t\ttry {\n\t\t\t\tawait closeTempFileStream();\n\t\t\t} catch (error) {\n\t\t\t\treturn await closeTempFileAndCleanup(error);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait removeTempFile();\n\t\t\t} catch (cleanupError) {\n\t\t\t\tthrow new AggregateError([cleanupError], \"Bash output cleanup failed\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t};\n\t\t}\n\n\t\treturn await closeTempFileAndCleanup(err);\n\t}\n\n\t// Normal completion has a generous bound for observer callbacks: unlike the\n\t// cancellation path, it must not hang forever on an observer that never\n\t// settles, while still allowing legitimately slow callbacks to finish. A\n\t// callback that exceeds the public bound is an execution failure, not silent\n\t// abandonment, so its spill is cleaned before the caller is released.\n\tconst callbacksSettled = await waitForCallbacks(false);\n\tif (!callbacksSettled) {\n\t\tcallbacksAbandoned = true;\n\t\treturn await closeTempFileAndCleanup(\n\t\t\tnew Error(`Bash output callback did not settle within ${NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS}ms`),\n\t\t);\n\t}\n\tif (hasCallbackError) {\n\t\treturn await closeTempFileAndCleanup(callbackError);\n\t}\n\tconst { fullOutput, truncationResult } = await prepareFinalOutput();\n\ttry {\n\t\tawait closeTempFileStream();\n\t} catch (error) {\n\t\treturn await closeTempFileAndCleanup(error);\n\t}\n\tconst cancelled = options?.signal?.aborted ?? false;\n\n\treturn {\n\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\tcancelled,\n\t\ttruncated: truncationResult.truncated,\n\t\tfullOutputPath: tempFilePath,\n\t};\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { AgentMessage, StreamFn, ThinkingLevel } from "@earendil-works/pi-agent-core";
2
+ import type { RetryCallbacks, RetryPolicy } from "@earendil-works/pi-ai";
3
+ import type { Model } from "@earendil-works/pi-ai/compat";
4
+ import { type CacheFriendlySummaryOptions, type CompactionPreparation, type CompactionResult } from "./compaction.ts";
5
+ /**
6
+ * Generate summaries for compaction using prepared data.
7
+ * Returns CompactionResult - SessionManager adds uuid/parentUuid when saving.
8
+ *
9
+ * @param preparation - Pre-calculated preparation from prepareCompaction()
10
+ * @param customInstructions - Optional custom focus for the summary
11
+ * @param cacheFriendly - Active provider contexts and request settings for cache-friendly summarization
12
+ */
13
+ export declare function compact(preparation: CompactionPreparation, model: Model<any>, apiKey: string | undefined, headers?: Record<string, string>, customInstructions?: string, signal?: AbortSignal, extraBody?: Record<string, unknown>, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>, transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>, retry?: RetryPolicy, callbacks?: RetryCallbacks, sessionId?: string, cacheFriendly?: CacheFriendlySummaryOptions): Promise<CompactionResult>;
14
+ //# sourceMappingURL=compaction-execution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-execution.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/compaction-execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC3F,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAEN,KAAK,2BAA2B,EAEhC,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAQrB,MAAM,iBAAiB,CAAC;AAmCzB;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAC5B,WAAW,EAAE,qBAAqB,EAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,kBAAkB,CAAC,EAAE,MAAM,EAC3B,MAAM,CAAC,EAAE,WAAW,EACpB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,QAAQ,CAAC,EAAE,QAAQ,EACnB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,EAC9F,KAAK,CAAC,EAAE,WAAW,EACnB,SAAS,CAAC,EAAE,cAAc,EAC1B,SAAS,CAAC,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,2BAA2B,GACzC,OAAO,CAAC,gBAAgB,CAAC,CA+G3B"}
@@ -0,0 +1,121 @@
1
+ import { convertToLlm } from "../messages.js";
2
+ import { buildSummarizationContext, cacheFriendlyContextFits, combineUsage, completeSummarization, createSummarizationOptions, generateSummaryWithUsage, getSummarizationFailure, } from "./compaction.js";
3
+ import { computeFileLists, contentTextForSummary, formatFileOperations, serializeConversation } from "./utils.js";
4
+ const TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.
5
+
6
+ Summarize the prefix to provide context for the retained suffix:
7
+
8
+ ## Original Request
9
+ [What did the user ask for in this turn?]
10
+
11
+ ## Early Progress
12
+ - [Key decisions and work done in the prefix]
13
+
14
+ ## Context for Suffix
15
+ - [Information needed to understand the retained recent work]
16
+
17
+ Be concise. Focus on what's needed to understand the kept suffix.`;
18
+ const SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT = `The final turn in the source conversation was too large to keep in full. Its SUFFIX (recent work) is retained.
19
+
20
+ The source conversation may also contain complete earlier turns for background. Summarize only the final, incomplete turn. It begins with the last user-role request before this instruction. Do not summarize earlier turns except for details needed to understand this final turn's prefix.
21
+
22
+ Summarize the prefix to provide context for the retained suffix:
23
+
24
+ ## Original Request
25
+ [What did the user ask for in this turn?]
26
+
27
+ ## Early Progress
28
+ - [Key decisions and work done in the prefix]
29
+
30
+ ## Context for Suffix
31
+ - [Information needed to understand the retained recent work]
32
+
33
+ Be concise. Focus on what's needed to understand the kept suffix.`;
34
+ /**
35
+ * Generate summaries for compaction using prepared data.
36
+ * Returns CompactionResult - SessionManager adds uuid/parentUuid when saving.
37
+ *
38
+ * @param preparation - Pre-calculated preparation from prepareCompaction()
39
+ * @param customInstructions - Optional custom focus for the summary
40
+ * @param cacheFriendly - Active provider contexts and request settings for cache-friendly summarization
41
+ */
42
+ export async function compact(preparation, model, apiKey, headers, customInstructions, signal, extraBody, thinkingLevel, streamFn, env, transformContext, retry, callbacks, sessionId, cacheFriendly) {
43
+ const { firstKeptEntryId, messagesToSummarize, turnPrefixMessages, isSplitTurn, tokensBefore, previousSummary, fileOps, settings, } = preparation;
44
+ // Generate summaries and merge into one
45
+ let summary;
46
+ let summaryUsage;
47
+ if (isSplitTurn && turnPrefixMessages.length > 0) {
48
+ let historyText = "No prior history.";
49
+ let historyUsage;
50
+ if (messagesToSummarize.length > 0) {
51
+ const historyResult = await generateSummaryWithUsage(messagesToSummarize, model, settings.reserveTokens, apiKey, headers, signal, customInstructions, previousSummary, extraBody, thinkingLevel, streamFn, env, transformContext, retry, callbacks, sessionId, {
52
+ sourceContext: cacheFriendly?.sourceContext,
53
+ requestOptions: cacheFriendly?.requestOptions,
54
+ });
55
+ historyText = historyResult.text;
56
+ historyUsage = historyResult.usage;
57
+ }
58
+ const turnPrefixResult = await generateTurnPrefixSummary(turnPrefixMessages, model, settings.reserveTokens, apiKey, headers, env, signal, extraBody, thinkingLevel, streamFn, transformContext, retry, callbacks, sessionId, {
59
+ sourceContext: cacheFriendly?.turnPrefixSourceContext,
60
+ requestOptions: cacheFriendly?.requestOptions,
61
+ });
62
+ // Merge into single summary
63
+ summary = `${historyText}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult.text}`;
64
+ summaryUsage = historyUsage ? combineUsage(historyUsage, turnPrefixResult.usage) : turnPrefixResult.usage;
65
+ }
66
+ else {
67
+ // Just generate history summary
68
+ const result = await generateSummaryWithUsage(messagesToSummarize, model, settings.reserveTokens, apiKey, headers, signal, customInstructions, previousSummary, extraBody, thinkingLevel, streamFn, env, transformContext, retry, callbacks, sessionId, {
69
+ sourceContext: cacheFriendly?.sourceContext,
70
+ requestOptions: cacheFriendly?.requestOptions,
71
+ });
72
+ summary = result.text;
73
+ summaryUsage = result.usage;
74
+ }
75
+ // Compute file lists and append to summary
76
+ const { readFiles, modifiedFiles } = computeFileLists(fileOps);
77
+ summary += formatFileOperations(readFiles, modifiedFiles);
78
+ if (!firstKeptEntryId) {
79
+ throw new Error("First kept entry has no UUID - session may need migration");
80
+ }
81
+ return {
82
+ summary,
83
+ firstKeptEntryId,
84
+ tokensBefore,
85
+ usage: summaryUsage,
86
+ details: { readFiles, modifiedFiles },
87
+ };
88
+ }
89
+ /**
90
+ * Generate a summary for a turn prefix (when splitting a turn).
91
+ */
92
+ async function generateTurnPrefixSummary(messages, model, reserveTokens, apiKey, headers, env, signal, extraBody, thinkingLevel, streamFn, transformContext, retry, callbacks, sessionId, cacheFriendly) {
93
+ const maxTokens = Math.min(Math.floor(0.5 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY);
94
+ let sourceContext = cacheFriendly?.sourceContext;
95
+ if (sourceContext &&
96
+ !cacheFriendlyContextFits(model, buildSummarizationContext(SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT, sourceContext), maxTokens)) {
97
+ sourceContext = undefined;
98
+ }
99
+ let promptText;
100
+ if (sourceContext) {
101
+ promptText = SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT;
102
+ }
103
+ else {
104
+ const providerMessages = transformContext ? await transformContext(messages, signal) : messages;
105
+ const llmMessages = convertToLlm(providerMessages);
106
+ const conversationText = serializeConversation(llmMessages);
107
+ promptText = `<conversation>\n${conversationText}\n</conversation>\n\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;
108
+ }
109
+ const response = await completeSummarization(model, buildSummarizationContext(promptText, sourceContext), createSummarizationOptions(model, maxTokens, apiKey, headers, env, signal, thinkingLevel, extraBody, sessionId, sourceContext ? cacheFriendly?.requestOptions : undefined, sourceContext ? "short" : undefined), streamFn, retry, callbacks);
110
+ const failure = getSummarizationFailure(response, "Turn prefix summarization");
111
+ if (failure)
112
+ throw new Error(failure);
113
+ if (response.content.some((block) => block.type === "toolCall")) {
114
+ throw new Error("Turn prefix summarization attempted to call a tool");
115
+ }
116
+ return {
117
+ text: contentTextForSummary(response.content),
118
+ usage: response.usage,
119
+ };
120
+ }
121
+ //# sourceMappingURL=compaction-execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-execution.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction-execution.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACN,yBAAyB,EAKzB,wBAAwB,EACxB,YAAY,EACZ,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,GAEvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAElH,MAAM,gCAAgC,GAAG;;;;;;;;;;;;;kEAayB,CAAC;AAEnE,MAAM,+CAA+C,GAAG;;;;;;;;;;;;;;;kEAeU,CAAC;AAEnE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC5B,WAAkC,EAClC,KAAiB,EACjB,MAA0B,EAC1B,OAAgC,EAChC,kBAA2B,EAC3B,MAAoB,EACpB,SAAmC,EACnC,aAA6B,EAC7B,QAAmB,EACnB,GAA4B,EAC5B,gBAA8F,EAC9F,KAAmB,EACnB,SAA0B,EAC1B,SAAkB,EAClB,aAA2C;IAE3C,MAAM,EACL,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,OAAO,EACP,QAAQ,GACR,GAAG,WAAW,CAAC;IAEhB,wCAAwC;IACxC,IAAI,OAAe,CAAC;IACpB,IAAI,YAAmB,CAAC;IAExB,IAAI,WAAW,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,IAAI,WAAW,GAAG,mBAAmB,CAAC;QACtC,IAAI,YAA+B,CAAC;QACpC,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,aAAa,GAAG,MAAM,wBAAwB,CACnD,mBAAmB,EACnB,KAAK,EACL,QAAQ,CAAC,aAAa,EACtB,MAAM,EACN,OAAO,EACP,MAAM,EACN,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,QAAQ,EACR,GAAG,EACH,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,SAAS,EACT;gBACC,aAAa,EAAE,aAAa,EAAE,aAAa;gBAC3C,cAAc,EAAE,aAAa,EAAE,cAAc;aAC7C,CACD,CAAC;YACF,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC;YACjC,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC;QACpC,CAAC;QACD,MAAM,gBAAgB,GAAG,MAAM,yBAAyB,CACvD,kBAAkB,EAClB,KAAK,EACL,QAAQ,CAAC,aAAa,EACtB,MAAM,EACN,OAAO,EACP,GAAG,EACH,MAAM,EACN,SAAS,EACT,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,SAAS,EACT;YACC,aAAa,EAAE,aAAa,EAAE,uBAAuB;YACrD,cAAc,EAAE,aAAa,EAAE,cAAc;SAC7C,CACD,CAAC;QACF,4BAA4B;QAC5B,OAAO,GAAG,GAAG,WAAW,gDAAgD,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAChG,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC;IAC3G,CAAC;SAAM,CAAC;QACP,gCAAgC;QAChC,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAC5C,mBAAmB,EACnB,KAAK,EACL,QAAQ,CAAC,aAAa,EACtB,MAAM,EACN,OAAO,EACP,MAAM,EACN,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,QAAQ,EACR,GAAG,EACH,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,SAAS,EACT;YACC,aAAa,EAAE,aAAa,EAAE,aAAa;YAC3C,cAAc,EAAE,aAAa,EAAE,cAAc;SAC7C,CACD,CAAC;QACF,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;QACtB,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,2CAA2C;IAC3C,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/D,OAAO,IAAI,oBAAoB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAE1D,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO;QACN,OAAO;QACP,gBAAgB;QAChB,YAAY;QACZ,KAAK,EAAE,YAAY;QACnB,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,EAAuB;KAC1D,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACvC,QAAwB,EACxB,KAAiB,EACjB,aAAqB,EACrB,MAA0B,EAC1B,OAAgC,EAChC,GAA4B,EAC5B,MAAoB,EACpB,SAAmC,EACnC,aAA6B,EAC7B,QAAgC,EAChC,gBAA8F,EAC9F,KAAmB,EACnB,SAA0B,EAC1B,SAAkB,EAClB,aAAqF;IAErF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACzB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,aAAa,CAAC,EAC/B,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAChE,CAAC;IACF,IAAI,aAAa,GAAG,aAAa,EAAE,aAAa,CAAC;IACjD,IACC,aAAa;QACb,CAAC,wBAAwB,CACxB,KAAK,EACL,yBAAyB,CAAC,+CAA+C,EAAE,aAAa,CAAC,EACzF,SAAS,CACT,EACA,CAAC;QACF,aAAa,GAAG,SAAS,CAAC;IAC3B,CAAC;IACD,IAAI,UAAkB,CAAC;IACvB,IAAI,aAAa,EAAE,CAAC;QACnB,UAAU,GAAG,+CAA+C,CAAC;IAC9D,CAAC;SAAM,CAAC;QACP,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChG,MAAM,WAAW,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;QACnD,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC5D,UAAU,GAAG,mBAAmB,gBAAgB,wBAAwB,gCAAgC,EAAE,CAAC;IAC5G,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAC3C,KAAK,EACL,yBAAyB,CAAC,UAAU,EAAE,aAAa,CAAC,EACpD,0BAA0B,CACzB,KAAK,EACL,SAAS,EACT,MAAM,EACN,OAAO,EACP,GAAG,EACH,MAAM,EACN,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,EACzD,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACnC,EACD,QAAQ,EACR,KAAK,EACL,SAAS,CACT,CAAC;IAEF,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;IAC/E,IAAI,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO;QACN,IAAI,EAAE,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC;QAC7C,KAAK,EAAE,QAAQ,CAAC,KAAK;KACrB,CAAC;AACH,CAAC","sourcesContent":["import type { AgentMessage, StreamFn, ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { RetryCallbacks, RetryPolicy } from \"@earendil-works/pi-ai\";\nimport type { Model, Usage } from \"@earendil-works/pi-ai/compat\";\nimport { convertToLlm } from \"../messages.ts\";\nimport {\n\tbuildSummarizationContext,\n\ttype CacheFriendlySummaryOptions,\n\ttype CompactionDetails,\n\ttype CompactionPreparation,\n\ttype CompactionResult,\n\tcacheFriendlyContextFits,\n\tcombineUsage,\n\tcompleteSummarization,\n\tcreateSummarizationOptions,\n\tgenerateSummaryWithUsage,\n\tgetSummarizationFailure,\n\ttype SummarizationStreamFn,\n} from \"./compaction.ts\";\nimport { computeFileLists, contentTextForSummary, formatFileOperations, serializeConversation } from \"./utils.ts\";\n\nconst TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.\n\nSummarize the prefix to provide context for the retained suffix:\n\n## Original Request\n[What did the user ask for in this turn?]\n\n## Early Progress\n- [Key decisions and work done in the prefix]\n\n## Context for Suffix\n- [Information needed to understand the retained recent work]\n\nBe concise. Focus on what's needed to understand the kept suffix.`;\n\nconst SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT = `The final turn in the source conversation was too large to keep in full. Its SUFFIX (recent work) is retained.\n\nThe source conversation may also contain complete earlier turns for background. Summarize only the final, incomplete turn. It begins with the last user-role request before this instruction. Do not summarize earlier turns except for details needed to understand this final turn's prefix.\n\nSummarize the prefix to provide context for the retained suffix:\n\n## Original Request\n[What did the user ask for in this turn?]\n\n## Early Progress\n- [Key decisions and work done in the prefix]\n\n## Context for Suffix\n- [Information needed to understand the retained recent work]\n\nBe concise. Focus on what's needed to understand the kept suffix.`;\n\n/**\n * Generate summaries for compaction using prepared data.\n * Returns CompactionResult - SessionManager adds uuid/parentUuid when saving.\n *\n * @param preparation - Pre-calculated preparation from prepareCompaction()\n * @param customInstructions - Optional custom focus for the summary\n * @param cacheFriendly - Active provider contexts and request settings for cache-friendly summarization\n */\nexport async function compact(\n\tpreparation: CompactionPreparation,\n\tmodel: Model<any>,\n\tapiKey: string | undefined,\n\theaders?: Record<string, string>,\n\tcustomInstructions?: string,\n\tsignal?: AbortSignal,\n\textraBody?: Record<string, unknown>,\n\tthinkingLevel?: ThinkingLevel,\n\tstreamFn?: StreamFn,\n\tenv?: Record<string, string>,\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>,\n\tretry?: RetryPolicy,\n\tcallbacks?: RetryCallbacks,\n\tsessionId?: string,\n\tcacheFriendly?: CacheFriendlySummaryOptions,\n): Promise<CompactionResult> {\n\tconst {\n\t\tfirstKeptEntryId,\n\t\tmessagesToSummarize,\n\t\tturnPrefixMessages,\n\t\tisSplitTurn,\n\t\ttokensBefore,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t} = preparation;\n\n\t// Generate summaries and merge into one\n\tlet summary: string;\n\tlet summaryUsage: Usage;\n\n\tif (isSplitTurn && turnPrefixMessages.length > 0) {\n\t\tlet historyText = \"No prior history.\";\n\t\tlet historyUsage: Usage | undefined;\n\t\tif (messagesToSummarize.length > 0) {\n\t\t\tconst historyResult = await generateSummaryWithUsage(\n\t\t\t\tmessagesToSummarize,\n\t\t\t\tmodel,\n\t\t\t\tsettings.reserveTokens,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tsignal,\n\t\t\t\tcustomInstructions,\n\t\t\t\tpreviousSummary,\n\t\t\t\textraBody,\n\t\t\t\tthinkingLevel,\n\t\t\t\tstreamFn,\n\t\t\t\tenv,\n\t\t\t\ttransformContext,\n\t\t\t\tretry,\n\t\t\t\tcallbacks,\n\t\t\t\tsessionId,\n\t\t\t\t{\n\t\t\t\t\tsourceContext: cacheFriendly?.sourceContext,\n\t\t\t\t\trequestOptions: cacheFriendly?.requestOptions,\n\t\t\t\t},\n\t\t\t);\n\t\t\thistoryText = historyResult.text;\n\t\t\thistoryUsage = historyResult.usage;\n\t\t}\n\t\tconst turnPrefixResult = await generateTurnPrefixSummary(\n\t\t\tturnPrefixMessages,\n\t\t\tmodel,\n\t\t\tsettings.reserveTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tenv,\n\t\t\tsignal,\n\t\t\textraBody,\n\t\t\tthinkingLevel,\n\t\t\tstreamFn,\n\t\t\ttransformContext,\n\t\t\tretry,\n\t\t\tcallbacks,\n\t\t\tsessionId,\n\t\t\t{\n\t\t\t\tsourceContext: cacheFriendly?.turnPrefixSourceContext,\n\t\t\t\trequestOptions: cacheFriendly?.requestOptions,\n\t\t\t},\n\t\t);\n\t\t// Merge into single summary\n\t\tsummary = `${historyText}\\n\\n---\\n\\n**Turn Context (split turn):**\\n\\n${turnPrefixResult.text}`;\n\t\tsummaryUsage = historyUsage ? combineUsage(historyUsage, turnPrefixResult.usage) : turnPrefixResult.usage;\n\t} else {\n\t\t// Just generate history summary\n\t\tconst result = await generateSummaryWithUsage(\n\t\t\tmessagesToSummarize,\n\t\t\tmodel,\n\t\t\tsettings.reserveTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\tcustomInstructions,\n\t\t\tpreviousSummary,\n\t\t\textraBody,\n\t\t\tthinkingLevel,\n\t\t\tstreamFn,\n\t\t\tenv,\n\t\t\ttransformContext,\n\t\t\tretry,\n\t\t\tcallbacks,\n\t\t\tsessionId,\n\t\t\t{\n\t\t\t\tsourceContext: cacheFriendly?.sourceContext,\n\t\t\t\trequestOptions: cacheFriendly?.requestOptions,\n\t\t\t},\n\t\t);\n\t\tsummary = result.text;\n\t\tsummaryUsage = result.usage;\n\t}\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\tif (!firstKeptEntryId) {\n\t\tthrow new Error(\"First kept entry has no UUID - session may need migration\");\n\t}\n\n\treturn {\n\t\tsummary,\n\t\tfirstKeptEntryId,\n\t\ttokensBefore,\n\t\tusage: summaryUsage,\n\t\tdetails: { readFiles, modifiedFiles } as CompactionDetails,\n\t};\n}\n\n/**\n * Generate a summary for a turn prefix (when splitting a turn).\n */\nasync function generateTurnPrefixSummary(\n\tmessages: AgentMessage[],\n\tmodel: Model<any>,\n\treserveTokens: number,\n\tapiKey: string | undefined,\n\theaders?: Record<string, string>,\n\tenv?: Record<string, string>,\n\tsignal?: AbortSignal,\n\textraBody?: Record<string, unknown>,\n\tthinkingLevel?: ThinkingLevel,\n\tstreamFn?: SummarizationStreamFn,\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>,\n\tretry?: RetryPolicy,\n\tcallbacks?: RetryCallbacks,\n\tsessionId?: string,\n\tcacheFriendly?: Pick<CacheFriendlySummaryOptions, \"sourceContext\" | \"requestOptions\">,\n): Promise<{ text: string; usage: Usage }> {\n\tconst maxTokens = Math.min(\n\t\tMath.floor(0.5 * reserveTokens),\n\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t);\n\tlet sourceContext = cacheFriendly?.sourceContext;\n\tif (\n\t\tsourceContext &&\n\t\t!cacheFriendlyContextFits(\n\t\t\tmodel,\n\t\t\tbuildSummarizationContext(SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT, sourceContext),\n\t\t\tmaxTokens,\n\t\t)\n\t) {\n\t\tsourceContext = undefined;\n\t}\n\tlet promptText: string;\n\tif (sourceContext) {\n\t\tpromptText = SOURCE_CONTEXT_TURN_PREFIX_SUMMARIZATION_PROMPT;\n\t} else {\n\t\tconst providerMessages = transformContext ? await transformContext(messages, signal) : messages;\n\t\tconst llmMessages = convertToLlm(providerMessages);\n\t\tconst conversationText = serializeConversation(llmMessages);\n\t\tpromptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;\n\t}\n\n\tconst response = await completeSummarization(\n\t\tmodel,\n\t\tbuildSummarizationContext(promptText, sourceContext),\n\t\tcreateSummarizationOptions(\n\t\t\tmodel,\n\t\t\tmaxTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tenv,\n\t\t\tsignal,\n\t\t\tthinkingLevel,\n\t\t\textraBody,\n\t\t\tsessionId,\n\t\t\tsourceContext ? cacheFriendly?.requestOptions : undefined,\n\t\t\tsourceContext ? \"short\" : undefined,\n\t\t),\n\t\tstreamFn,\n\t\tretry,\n\t\tcallbacks,\n\t);\n\n\tconst failure = getSummarizationFailure(response, \"Turn prefix summarization\");\n\tif (failure) throw new Error(failure);\n\tif (response.content.some((block) => block.type === \"toolCall\")) {\n\t\tthrow new Error(\"Turn prefix summarization attempted to call a tool\");\n\t}\n\n\treturn {\n\t\ttext: contentTextForSummary(response.content),\n\t\tusage: response.usage,\n\t};\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import { type IdealCompactionSettings } from "./ideal-compaction-settings.ts";
2
+ export interface CompactionSettings extends IdealCompactionSettings {
3
+ enabled: boolean;
4
+ reserveTokens: number;
5
+ keepRecentTokens: number;
6
+ speculativeEnabled?: boolean;
7
+ speculativeFraction?: number;
8
+ speculativeCooldownMs?: number;
9
+ restorationEnabled?: boolean;
10
+ restorationMaxItems?: number;
11
+ restorationMaxTokensPerItem?: number;
12
+ restorationMaxTotalTokens?: number;
13
+ restorationContextRatio?: number;
14
+ idleCompactionEnabled?: boolean;
15
+ }
16
+ export declare const DEFAULT_COMPACTION_SETTINGS: CompactionSettings;
17
+ //# sourceMappingURL=compaction-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-settings.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/compaction-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEjH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,eAAO,MAAM,2BAA2B,EAAE,kBAczC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { DEFAULT_IDEAL_COMPACTION_SETTINGS } from "./ideal-compaction-settings.js";
2
+ export const DEFAULT_COMPACTION_SETTINGS = {
3
+ enabled: true,
4
+ reserveTokens: 16384,
5
+ keepRecentTokens: 20000,
6
+ speculativeEnabled: true,
7
+ speculativeFraction: 0.75,
8
+ speculativeCooldownMs: 30000,
9
+ restorationEnabled: true,
10
+ restorationMaxItems: 10,
11
+ restorationMaxTokensPerItem: 5000,
12
+ restorationMaxTotalTokens: 50_000,
13
+ restorationContextRatio: 0.15,
14
+ idleCompactionEnabled: true,
15
+ ...DEFAULT_IDEAL_COMPACTION_SETTINGS,
16
+ };
17
+ //# sourceMappingURL=compaction-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-settings.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAgC,MAAM,gCAAgC,CAAC;AAiBjH,MAAM,CAAC,MAAM,2BAA2B,GAAuB;IAC9D,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,KAAK;IACvB,kBAAkB,EAAE,IAAI;IACxB,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,KAAK;IAC5B,kBAAkB,EAAE,IAAI;IACxB,mBAAmB,EAAE,EAAE;IACvB,2BAA2B,EAAE,IAAI;IACjC,yBAAyB,EAAE,MAAM;IACjC,uBAAuB,EAAE,IAAI;IAC7B,qBAAqB,EAAE,IAAI;IAC3B,GAAG,iCAAiC;CACpC,CAAC","sourcesContent":["import { DEFAULT_IDEAL_COMPACTION_SETTINGS, type IdealCompactionSettings } from \"./ideal-compaction-settings.ts\";\n\nexport interface CompactionSettings extends IdealCompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n\tspeculativeEnabled?: boolean;\n\tspeculativeFraction?: number;\n\tspeculativeCooldownMs?: number;\n\trestorationEnabled?: boolean;\n\trestorationMaxItems?: number;\n\trestorationMaxTokensPerItem?: number;\n\trestorationMaxTotalTokens?: number;\n\trestorationContextRatio?: number;\n\tidleCompactionEnabled?: boolean;\n}\n\nexport const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n\tspeculativeEnabled: true,\n\tspeculativeFraction: 0.75,\n\tspeculativeCooldownMs: 30000,\n\trestorationEnabled: true,\n\trestorationMaxItems: 10,\n\trestorationMaxTokensPerItem: 5000,\n\trestorationMaxTotalTokens: 50_000,\n\trestorationContextRatio: 0.15,\n\tidleCompactionEnabled: true,\n\t...DEFAULT_IDEAL_COMPACTION_SETTINGS,\n};\n"]}