@bubblebrain-ai/bubble 0.0.51 → 0.0.53

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 (481) hide show
  1. package/README.md +4 -12
  2. package/dist/agent/routing-catalog.js +3 -2
  3. package/dist/agent/subagent/router.js +5 -0
  4. package/dist/agent/subagent/runtime.d.ts +1 -0
  5. package/dist/agent/subagent/runtime.js +13 -1
  6. package/dist/agent/subagent-control.d.ts +4 -0
  7. package/dist/agent/subagent-control.js +1 -0
  8. package/dist/agent/subagent-store.js +2 -0
  9. package/dist/agent/workflow/control.d.ts +2 -0
  10. package/dist/agent/workflow/runs.d.ts +1 -0
  11. package/dist/agent/workflow/runs.js +3 -0
  12. package/dist/agent.d.ts +11 -1
  13. package/dist/agent.js +158 -57
  14. package/dist/bin.js +2 -44
  15. package/dist/config.d.ts +14 -1
  16. package/dist/config.js +45 -4
  17. package/dist/context/budget.d.ts +7 -1
  18. package/dist/context/budget.js +25 -2
  19. package/dist/context/compact-llm.d.ts +1 -1
  20. package/dist/context/compact-llm.js +5 -2
  21. package/dist/context/overflow.js +2 -0
  22. package/dist/context/projector.d.ts +8 -0
  23. package/dist/context/projector.js +55 -6
  24. package/dist/context/tool-output-truncate.d.ts +17 -1
  25. package/dist/context/tool-output-truncate.js +171 -49
  26. package/dist/feishu/agent-host/run-driver.js +3 -0
  27. package/dist/feishu/serve.js +2 -0
  28. package/dist/goal/command.d.ts +1 -1
  29. package/dist/goal/command.js +7 -2
  30. package/dist/main.d.ts +1 -1
  31. package/dist/main.js +38 -8
  32. package/dist/memory/db.js +0 -7
  33. package/dist/model-catalog.js +25 -6
  34. package/dist/model-config.js +3 -2
  35. package/dist/model-pricing.d.ts +24 -5
  36. package/dist/model-pricing.js +0 -0
  37. package/dist/model-selection.js +13 -4
  38. package/dist/network/chatgpt-transport.js +0 -34
  39. package/dist/network/errors.d.ts +19 -0
  40. package/dist/network/errors.js +24 -0
  41. package/dist/network/provider-transport.d.ts +0 -4
  42. package/dist/network/provider-transport.js +3 -35
  43. package/dist/network/retry.d.ts +6 -1
  44. package/dist/network/retry.js +9 -0
  45. package/dist/provider-model-policy.d.ts +16 -0
  46. package/dist/provider-model-policy.js +33 -0
  47. package/dist/provider-registry.d.ts +1 -1
  48. package/dist/provider-registry.js +80 -11
  49. package/dist/provider-transform.js +32 -4
  50. package/dist/provider.d.ts +6 -2
  51. package/dist/provider.js +100 -11
  52. package/dist/question/controller.js +5 -4
  53. package/dist/question/normalize.d.ts +6 -0
  54. package/dist/question/normalize.js +11 -0
  55. package/dist/rewind.d.ts +20 -0
  56. package/dist/rewind.js +29 -0
  57. package/dist/sdk/index.d.ts +1 -1
  58. package/dist/sdk/index.js +11 -3
  59. package/dist/session.d.ts +7 -0
  60. package/dist/session.js +20 -0
  61. package/dist/skills/discovery.js +18 -0
  62. package/dist/skills/registry.d.ts +12 -2
  63. package/dist/skills/registry.js +40 -5
  64. package/dist/skills/types.d.ts +2 -0
  65. package/dist/slash-commands/commands.js +120 -81
  66. package/dist/slash-commands/registry.d.ts +2 -1
  67. package/dist/slash-commands/registry.js +3 -0
  68. package/dist/slash-commands/types.d.ts +22 -4
  69. package/dist/stats/usage.d.ts +3 -1
  70. package/dist/stats/usage.js +22 -13
  71. package/dist/tasks/manager.d.ts +4 -2
  72. package/dist/tasks/manager.js +34 -9
  73. package/dist/tasks/promotion.d.ts +2 -2
  74. package/dist/tasks/promotion.js +2 -2
  75. package/dist/tools/agent-lifecycle.js +4 -0
  76. package/dist/tools/bash.d.ts +3 -1
  77. package/dist/tools/bash.js +13 -2
  78. package/dist/tools/grep.d.ts +5 -1
  79. package/dist/tools/grep.js +318 -69
  80. package/dist/tools/index.d.ts +3 -1
  81. package/dist/tools/index.js +1 -0
  82. package/dist/tools/question.js +5 -4
  83. package/dist/tools/read.js +40 -21
  84. package/dist/tui/animation-clock.d.ts +14 -0
  85. package/dist/tui/animation-clock.js +34 -0
  86. package/dist/tui/app.d.ts +176 -0
  87. package/dist/tui/app.js +1647 -0
  88. package/dist/tui/components/approval-dialog.d.ts +43 -0
  89. package/dist/tui/components/approval-dialog.js +247 -0
  90. package/dist/tui/components/bottom-sheet.d.ts +9 -0
  91. package/dist/tui/components/bottom-sheet.js +32 -0
  92. package/dist/tui/components/context-info.d.ts +54 -0
  93. package/dist/tui/components/context-info.js +388 -0
  94. package/dist/tui/components/feedback-dialog.d.ts +56 -0
  95. package/dist/tui/components/feedback-dialog.js +262 -0
  96. package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
  97. package/dist/tui/components/feishu-setup-dialog.js +332 -0
  98. package/dist/tui/components/image-preview.d.ts +32 -0
  99. package/dist/tui/components/image-preview.js +130 -0
  100. package/dist/tui/components/plan-dialog.d.ts +34 -0
  101. package/dist/tui/components/plan-dialog.js +196 -0
  102. package/dist/tui/components/provider-key-input.d.ts +18 -0
  103. package/dist/tui/components/provider-key-input.js +49 -0
  104. package/dist/tui/components/question-dialog.d.ts +25 -0
  105. package/dist/tui/components/question-dialog.js +263 -0
  106. package/dist/tui/components/responsive-transcript.d.ts +31 -0
  107. package/dist/tui/components/responsive-transcript.js +77 -0
  108. package/dist/tui/components/rewind-picker.d.ts +62 -0
  109. package/dist/tui/components/rewind-picker.js +317 -0
  110. package/dist/tui/components/session-picker.d.ts +49 -0
  111. package/dist/tui/components/session-picker.js +303 -0
  112. package/dist/tui/components/skills-panel.d.ts +59 -0
  113. package/dist/tui/components/skills-panel.js +568 -0
  114. package/dist/tui/components/stats-panel.d.ts +40 -0
  115. package/dist/tui/components/stats-panel.js +301 -0
  116. package/dist/tui/components/streaming-message.d.ts +69 -0
  117. package/dist/tui/components/streaming-message.js +393 -0
  118. package/dist/tui/components/subagent-inspector.d.ts +28 -0
  119. package/dist/tui/components/subagent-inspector.js +127 -0
  120. package/dist/tui/components/task-inspector.d.ts +26 -0
  121. package/dist/tui/components/task-inspector.js +70 -0
  122. package/dist/tui/components/tasks-pane.d.ts +88 -0
  123. package/dist/tui/components/tasks-pane.js +416 -0
  124. package/dist/tui/components/transcript.d.ts +96 -0
  125. package/dist/tui/components/transcript.js +771 -0
  126. package/dist/tui/components/welcome.d.ts +20 -0
  127. package/dist/tui/components/welcome.js +133 -0
  128. package/dist/tui/components/workflow-inspector.d.ts +30 -0
  129. package/dist/tui/components/workflow-inspector.js +136 -0
  130. package/dist/tui/composer-autocomplete.d.ts +61 -0
  131. package/dist/tui/composer-autocomplete.js +383 -0
  132. package/dist/tui/composer-style.d.ts +7 -0
  133. package/dist/tui/composer-style.js +29 -0
  134. package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
  135. package/dist/tui/controller/agent-event-reducer.js +238 -0
  136. package/dist/tui/controller/composer-controller.d.ts +95 -0
  137. package/dist/tui/controller/composer-controller.js +502 -0
  138. package/dist/tui/controller/controller.d.ts +194 -0
  139. package/dist/tui/controller/controller.js +1035 -0
  140. package/dist/tui/controller/effects.d.ts +71 -0
  141. package/dist/tui/controller/effects.js +1 -0
  142. package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
  143. package/dist/tui/controller/goal-runtime-controller.js +202 -0
  144. package/dist/tui/controller/input-queue-machine.d.ts +71 -0
  145. package/dist/tui/controller/input-queue-machine.js +121 -0
  146. package/dist/tui/controller/intents.d.ts +56 -0
  147. package/dist/tui/controller/intents.js +6 -0
  148. package/dist/tui/controller/overlay-controller.d.ts +64 -0
  149. package/dist/tui/controller/overlay-controller.js +129 -0
  150. package/dist/tui/controller/ports.d.ts +57 -0
  151. package/dist/tui/controller/ports.js +9 -0
  152. package/dist/tui/controller/session-transition.d.ts +54 -0
  153. package/dist/tui/controller/session-transition.js +68 -0
  154. package/dist/tui/controller/state.d.ts +14 -0
  155. package/dist/tui/controller/state.js +36 -0
  156. package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
  157. package/dist/tui/controller/task-runtime-controller.js +158 -0
  158. package/dist/tui/footer.d.ts +37 -0
  159. package/dist/tui/footer.js +80 -0
  160. package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
  161. package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
  162. package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
  163. package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
  164. package/dist/tui/fullscreen.d.ts +48 -0
  165. package/dist/tui/fullscreen.js +255 -0
  166. package/dist/tui/image-display.d.ts +4 -0
  167. package/dist/tui/image-display.js +30 -12
  168. package/dist/tui/markdown-style.d.ts +5 -0
  169. package/dist/tui/markdown-style.js +23 -0
  170. package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
  171. package/dist/tui/model/composer-types.d.ts +17 -0
  172. package/dist/tui/model/composer-types.js +1 -0
  173. package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
  174. package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
  175. package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
  176. package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
  177. package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
  178. package/dist/tui/model/image-attachment.d.ts +15 -0
  179. package/dist/tui/model/image-attachment.js +1 -0
  180. package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
  181. package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
  182. package/dist/tui/model/input-history.d.ts +1 -0
  183. package/dist/tui/model/input-history.js +1 -0
  184. package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
  185. package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
  186. package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
  187. package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
  188. package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
  189. package/dist/tui/model/theme-style.d.ts +10 -0
  190. package/dist/tui/model/theme-style.js +79 -0
  191. package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
  192. package/dist/{tui-ink → tui/model}/theme.js +23 -38
  193. package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
  194. package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
  195. package/dist/tui/model/trace-interaction.d.ts +63 -0
  196. package/dist/tui/model/trace-interaction.js +87 -0
  197. package/dist/tui/model-picker-data.d.ts +1 -2
  198. package/dist/tui/model-picker-data.js +8 -1
  199. package/dist/tui/model-switch.js +2 -0
  200. package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
  201. package/dist/tui/run-session-picker.js +64 -0
  202. package/dist/tui/run.d.ts +72 -0
  203. package/dist/tui/run.js +139 -0
  204. package/dist/tui/terminal/multiplexer.d.ts +5 -0
  205. package/dist/tui/terminal/multiplexer.js +14 -0
  206. package/dist/tui/testing/fake-agent.d.ts +24 -0
  207. package/dist/tui/testing/fake-agent.js +22 -0
  208. package/dist/tui/testing/spy-host.d.ts +21 -0
  209. package/dist/tui/testing/spy-host.js +55 -0
  210. package/dist/tui/trace-groups.js +27 -6
  211. package/dist/types.d.ts +7 -0
  212. package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
  213. package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
  214. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
  215. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
  216. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
  217. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
  218. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
  219. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
  220. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
  221. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
  222. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
  223. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
  224. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
  225. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
  226. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
  227. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
  228. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
  229. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
  230. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
  231. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
  232. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
  233. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
  234. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
  235. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
  236. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
  237. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
  238. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
  239. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
  240. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
  241. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
  242. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
  243. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
  244. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
  245. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
  246. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
  247. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
  248. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
  249. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
  250. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
  251. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
  252. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
  253. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
  254. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
  255. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
  256. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
  257. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
  258. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
  259. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
  260. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
  261. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
  262. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
  263. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
  264. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
  265. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
  266. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
  267. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
  268. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
  269. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
  270. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
  271. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
  272. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
  273. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
  274. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
  275. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
  276. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
  277. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
  278. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
  279. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
  280. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
  281. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
  282. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
  283. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
  284. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
  285. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
  286. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
  287. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
  288. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
  289. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
  290. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
  291. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
  292. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
  293. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
  294. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
  295. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
  296. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
  297. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
  298. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
  299. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
  300. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
  301. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
  302. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
  303. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
  304. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
  305. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
  306. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
  307. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
  308. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
  309. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
  310. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
  311. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
  312. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
  313. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
  314. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
  315. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
  316. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
  317. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
  318. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
  319. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
  320. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
  321. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
  322. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
  323. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
  324. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
  325. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
  326. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
  327. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
  328. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
  329. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
  330. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
  331. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
  332. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
  333. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
  334. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
  335. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
  336. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
  337. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
  338. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
  339. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
  340. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
  341. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
  342. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
  343. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
  344. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
  345. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
  346. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
  347. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
  348. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
  349. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
  350. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
  351. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
  352. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
  353. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
  354. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
  355. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
  356. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
  357. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
  358. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
  359. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
  360. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
  361. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
  362. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
  363. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
  364. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
  365. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
  366. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
  367. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
  368. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
  369. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
  370. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
  371. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
  372. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
  373. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
  374. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
  375. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
  376. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
  377. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
  378. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
  379. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
  380. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  381. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  382. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
  383. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
  384. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
  385. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  386. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  387. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
  388. package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
  389. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  390. package/node_modules/get-east-asian-width/index.js +30 -0
  391. package/node_modules/get-east-asian-width/license +9 -0
  392. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  393. package/node_modules/get-east-asian-width/lookup.js +138 -0
  394. package/node_modules/get-east-asian-width/package.json +71 -0
  395. package/node_modules/get-east-asian-width/readme.md +65 -0
  396. package/node_modules/get-east-asian-width/utilities.js +24 -0
  397. package/package.json +23 -10
  398. package/packages/pi-tui/node_modules/marked/LICENSE +44 -0
  399. package/packages/pi-tui/node_modules/marked/README.md +115 -0
  400. package/packages/pi-tui/node_modules/marked/bin/main.js +284 -0
  401. package/packages/pi-tui/node_modules/marked/bin/marked.js +16 -0
  402. package/packages/pi-tui/node_modules/marked/lib/marked.d.ts +760 -0
  403. package/packages/pi-tui/node_modules/marked/lib/marked.esm.js +77 -0
  404. package/packages/pi-tui/node_modules/marked/lib/marked.esm.js.map +7 -0
  405. package/packages/pi-tui/node_modules/marked/lib/marked.umd.js +79 -0
  406. package/packages/pi-tui/node_modules/marked/lib/marked.umd.js.map +7 -0
  407. package/packages/pi-tui/node_modules/marked/man/marked.1 +113 -0
  408. package/packages/pi-tui/node_modules/marked/man/marked.1.md +93 -0
  409. package/packages/pi-tui/node_modules/marked/package.json +103 -0
  410. package/dist/tui-ink/app.d.ts +0 -76
  411. package/dist/tui-ink/app.js +0 -2377
  412. package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
  413. package/dist/tui-ink/approval/approval-dialog.js +0 -184
  414. package/dist/tui-ink/approval/diff-view.d.ts +0 -7
  415. package/dist/tui-ink/approval/diff-view.js +0 -44
  416. package/dist/tui-ink/approval/select.d.ts +0 -35
  417. package/dist/tui-ink/approval/select.js +0 -98
  418. package/dist/tui-ink/code-highlight.d.ts +0 -8
  419. package/dist/tui-ink/code-highlight.js +0 -121
  420. package/dist/tui-ink/command-palette.d.ts +0 -32
  421. package/dist/tui-ink/command-palette.js +0 -158
  422. package/dist/tui-ink/compaction-progress.d.ts +0 -19
  423. package/dist/tui-ink/compaction-progress.js +0 -74
  424. package/dist/tui-ink/detect-theme.d.ts +0 -1
  425. package/dist/tui-ink/detect-theme.js +0 -1
  426. package/dist/tui-ink/feedback-dialog.d.ts +0 -19
  427. package/dist/tui-ink/feedback-dialog.js +0 -133
  428. package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
  429. package/dist/tui-ink/feishu-setup-picker.js +0 -271
  430. package/dist/tui-ink/footer.d.ts +0 -26
  431. package/dist/tui-ink/footer.js +0 -51
  432. package/dist/tui-ink/input-box.d.ts +0 -189
  433. package/dist/tui-ink/input-box.js +0 -1279
  434. package/dist/tui-ink/input-history.d.ts +0 -1
  435. package/dist/tui-ink/input-history.js +0 -1
  436. package/dist/tui-ink/markdown.d.ts +0 -103
  437. package/dist/tui-ink/markdown.js +0 -747
  438. package/dist/tui-ink/message-list.d.ts +0 -49
  439. package/dist/tui-ink/message-list.js +0 -841
  440. package/dist/tui-ink/model-picker.d.ts +0 -143
  441. package/dist/tui-ink/model-picker.js +0 -966
  442. package/dist/tui-ink/plan-confirm.d.ts +0 -7
  443. package/dist/tui-ink/plan-confirm.js +0 -115
  444. package/dist/tui-ink/question-dialog.d.ts +0 -8
  445. package/dist/tui-ink/question-dialog.js +0 -135
  446. package/dist/tui-ink/rewind-picker.d.ts +0 -8
  447. package/dist/tui-ink/rewind-picker.js +0 -102
  448. package/dist/tui-ink/run-session-picker.js +0 -22
  449. package/dist/tui-ink/run.d.ts +0 -62
  450. package/dist/tui-ink/run.js +0 -103
  451. package/dist/tui-ink/session-picker.d.ts +0 -10
  452. package/dist/tui-ink/session-picker.js +0 -113
  453. package/dist/tui-ink/stats-panel.d.ts +0 -11
  454. package/dist/tui-ink/stats-panel.js +0 -65
  455. package/dist/tui-ink/subagent-inspector.d.ts +0 -21
  456. package/dist/tui-ink/subagent-inspector.js +0 -205
  457. package/dist/tui-ink/use-terminal-size.d.ts +0 -4
  458. package/dist/tui-ink/use-terminal-size.js +0 -48
  459. package/dist/tui-ink/waiting-indicator.d.ts +0 -12
  460. package/dist/tui-ink/waiting-indicator.js +0 -129
  461. package/dist/tui-ink/welcome.d.ts +0 -25
  462. package/dist/tui-ink/welcome.js +0 -88
  463. package/dist/tui-ink/wordmark.d.ts +0 -8
  464. package/dist/tui-ink/wordmark.js +0 -103
  465. /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
  466. /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
  467. /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
  468. /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
  469. /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
  470. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
  471. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
  472. /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
  473. /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
  474. /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
  475. /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
  476. /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
  477. /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
  478. /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
  479. /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
  480. /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
  481. /package/dist/{tui-ink → tui/model}/width.js +0 -0
package/dist/agent.js CHANGED
@@ -5,13 +5,13 @@
5
5
  import { compactCurrentTurnToolGroups, compactMessages } from "./context/compact.js";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { compactMessagesWithLLM } from "./context/compact-llm.js";
8
- import { getContextBudget } from "./context/budget.js";
8
+ import { estimateToolDefinitionsTokens, getContextBudget, getMaxInputTokens, } from "./context/budget.js";
9
9
  import { buildContextUsageSnapshot } from "./context/usage.js";
10
10
  import { isContextOverflowError } from "./context/overflow.js";
11
- import { computeRetryDelayMs, isProviderStreamInterruption, MAX_STREAM_INTERRUPTION_RETRIES, sleepBeforeRetry, } from "./network/retry.js";
11
+ import { computeRetryDelayMs, isProviderStreamInterruption, providerStreamRetryLimit, sleepBeforeRetry, } from "./network/retry.js";
12
12
  import { projectMessages } from "./context/projector.js";
13
13
  import { aggressivePruneMessages, markStableCurrentToolResultsForCache, markToolResultCacheStable } from "./context/prune.js";
14
- import { truncateToolOutputForModel } from "./context/tool-output-truncate.js";
14
+ import { normalizeToolResultForModel, truncateToolOutputForModel } from "./context/tool-output-truncate.js";
15
15
  import { buildDeferredToolsReminder, buildToolFreezeReminder, isPermissionModeReminder, reminderForMode } from "./prompt/reminders.js";
16
16
  import { HookBus } from "./orchestrator/hooks.js";
17
17
  import { normalizeHookInput, truncateHookText, } from "./hooks/index.js";
@@ -33,11 +33,17 @@ import { debugReasoningStream, summarizeDebugText } from "./reasoning-debug.js";
33
33
  import { buildToolPromptOptions } from "./tools/prompt-metadata.js";
34
34
  import { stopAutoServersForSession } from "./tools/server-manager.js";
35
35
  import { summarizeAgentEventForTrace, summarizeTraceError, summarizeTraceMessage, summarizeTraceToolResult, summarizeTraceValue, traceEvent, } from "./debug-trace.js";
36
- const MAX_CONSECUTIVE_OVERFLOW_RECOVERIES = 3;
36
+ const MAX_CONSECUTIVE_OVERFLOW_RECOVERIES = 2;
37
37
  const MAX_EMPTY_ASSISTANT_RECOVERIES = 1;
38
38
  const EMPTY_ASSISTANT_RECOVERY_REMINDER = "The previous model response contained no user-visible assistant content and no tool calls. " +
39
39
  "Respond now with a concise, user-visible answer, or call an available tool if more work is required. " +
40
40
  "Do not put the final answer only in hidden reasoning.";
41
+ class LocalContextPreflightError extends Error {
42
+ constructor(estimatedTokens, maxInputTokens) {
43
+ super(`Local request preflight: estimated prompt length ${estimatedTokens} tokens exceeds maximum prompt length ${maxInputTokens}.`);
44
+ this.name = "LocalContextPreflightError";
45
+ }
46
+ }
41
47
  export { AgentAbortError, SubagentAbortError } from "./agent/abort-errors.js";
42
48
  // Model-facing interruption boundary. Persisted into the transcript so the
43
49
  // next turn sees an explicit stop instead of a dangling request — but it must
@@ -364,6 +370,11 @@ export class Agent {
364
370
  memoryPrompt: this.memoryPrompt,
365
371
  };
366
372
  }
373
+ /** Refresh the live Skill catalog after /skills reloads or changes enablement. */
374
+ setSkillSummaries(skills) {
375
+ this.skillSummaries = skills.slice();
376
+ this.notifyContextChanged();
377
+ }
367
378
  /** Current routing menu (design §4); undefined when no accessor is wired. */
368
379
  buildModelRoutingPromptSection() {
369
380
  return this.router.promptSection();
@@ -690,7 +701,11 @@ export class Agent {
690
701
  return;
691
702
  }
692
703
  this.injectHookModelContext(promptHook.result);
693
- this.appendMessage({ role: "user", content: userInput });
704
+ this.appendMessage({
705
+ role: "user",
706
+ content: userInput,
707
+ ...(options.userMessageUi ? { ui: { ...options.userMessageUi } } : {}),
708
+ });
694
709
  }
695
710
  await hookBus.runBeforeTurn({
696
711
  agent: this,
@@ -804,22 +819,32 @@ export class Agent {
804
819
  description: t.description,
805
820
  parameters: t.parameters,
806
821
  }));
822
+ const toolDefinitionTokens = estimateToolDefinitionsTokens(toolDefinitions, this.providerId);
807
823
  // LLM-driven compaction runs ahead of projector's algorithmic passes. If
808
824
  // it succeeds, this.messages is replaced with [preserved system+meta] +
809
825
  // [LLM summary] + [last user msg], and the projector becomes a no-op for
810
826
  // budget. If it fails (network error, etc.), the projector's existing
811
827
  // algorithmic fallback still kicks in.
812
- await this.maybeCompactWithLLM();
828
+ await this.maybeCompactWithLLM(toolDefinitionTokens);
813
829
  const bufferedStreamingToolCallIds = new Set();
830
+ let currentRequestEstimate;
814
831
  try {
815
832
  markStableCurrentToolResultsForCache(this.messages);
816
833
  const projectedMessages = projectMessages(this.messages, {
817
834
  mode: "budgeted",
818
835
  providerId: this.providerId,
819
836
  modelId: this.apiModel,
820
- usageAnchorTokens: this.lastInputTokens ?? undefined,
821
- anchorMessageCount: this.lastAnchorMessageCount ?? undefined,
837
+ // Projection changes message identity/count (meta rendering, pruning,
838
+ // chain repair), so a resident-array anchor index is not safe here.
839
+ // A fresh estimate also lets us account for current tool schemas.
840
+ additionalInputTokens: toolDefinitionTokens,
822
841
  });
842
+ const requestBudget = getContextBudget(this.providerId, this.apiModel, projectedMessages, { additionalInputTokens: toolDefinitionTokens });
843
+ currentRequestEstimate = requestBudget.estimatedTokens;
844
+ const maxInputTokens = getMaxInputTokens(requestBudget.contextWindow);
845
+ if (maxInputTokens !== undefined && currentRequestEstimate > maxInputTokens) {
846
+ throw new LocalContextPreflightError(currentRequestEstimate, maxInputTokens);
847
+ }
823
848
  const providerStartedAt = Date.now();
824
849
  let streamTextChars = 0;
825
850
  let streamReasoningChars = 0;
@@ -1014,9 +1039,13 @@ export class Agent {
1014
1039
  if (assistantAppended) {
1015
1040
  throw error;
1016
1041
  }
1017
- if (isProviderStreamInterruption(error)
1042
+ const streamInterruption = isProviderStreamInterruption(error) ? error : undefined;
1043
+ const streamRetryLimit = streamInterruption
1044
+ ? providerStreamRetryLimit(streamInterruption)
1045
+ : 0;
1046
+ if (streamInterruption
1018
1047
  && !isAbortLikeError(error, abortSignal)
1019
- && consecutiveStreamInterruptionRetries < MAX_STREAM_INTERRUPTION_RETRIES) {
1048
+ && consecutiveStreamInterruptionRetries < streamRetryLimit) {
1020
1049
  // The provider stream died after partial content. The half-built
1021
1050
  // assistantMsg was never appended to this.messages, and the next
1022
1051
  // turn_start resets the streaming display, so re-issuing the whole
@@ -1025,10 +1054,12 @@ export class Agent {
1025
1054
  yield emit({
1026
1055
  type: "provider_retry",
1027
1056
  attempt: consecutiveStreamInterruptionRetries,
1028
- maxAttempts: MAX_STREAM_INTERRUPTION_RETRIES,
1029
- reason: "Provider stream interrupted mid-response.",
1057
+ maxAttempts: streamRetryLimit,
1058
+ reason: streamInterruption.message,
1030
1059
  });
1031
- await sleepBeforeRetry(computeRetryDelayMs(consecutiveStreamInterruptionRetries), abortSignal).catch(() => undefined);
1060
+ await sleepBeforeRetry(computeRetryDelayMs(consecutiveStreamInterruptionRetries, {
1061
+ retryAfterMs: streamInterruption.retryAfterMs,
1062
+ }), abortSignal).catch(() => undefined);
1032
1063
  continue;
1033
1064
  }
1034
1065
  if (!isContextOverflowError(error)) {
@@ -1045,12 +1076,44 @@ export class Agent {
1045
1076
  if (consecutiveOverflowRecoveries >= MAX_CONSECUTIVE_OVERFLOW_RECOVERIES) {
1046
1077
  throw error;
1047
1078
  }
1048
- const droppedMessages = await this.recoverFromOverflow(consecutiveOverflowRecoveries);
1079
+ const messagesBeforeRecovery = this.messages;
1080
+ const inputTokensBeforeRecovery = this.lastInputTokens;
1081
+ const anchorBeforeRecovery = this.lastAnchorMessageCount;
1082
+ const failedRequestEstimate = currentRequestEstimate ?? getContextBudget(this.providerId, this.apiModel, projectMessages(this.messages, {
1083
+ mode: "budgeted",
1084
+ providerId: this.providerId,
1085
+ modelId: this.apiModel,
1086
+ additionalInputTokens: toolDefinitionTokens,
1087
+ }), { additionalInputTokens: toolDefinitionTokens }).estimatedTokens;
1088
+ const droppedMessages = await this.recoverFromOverflow(consecutiveOverflowRecoveries, error instanceof LocalContextPreflightError ? 0.999_999 : 0.9);
1089
+ const recoveredRequestEstimate = getContextBudget(this.providerId, this.apiModel, projectMessages(this.messages, {
1090
+ mode: "budgeted",
1091
+ providerId: this.providerId,
1092
+ modelId: this.apiModel,
1093
+ additionalInputTokens: toolDefinitionTokens,
1094
+ }), { additionalInputTokens: toolDefinitionTokens }).estimatedTokens;
1095
+ // Never issue a retry that is the same size or larger. Apart from
1096
+ // avoiding a useless retry loop, this proves that already-completed
1097
+ // tool calls stay historical data rather than being executed again.
1098
+ const madeProgress = recoveredRequestEstimate < failedRequestEstimate;
1099
+ const meaningfulProviderProgress = error instanceof LocalContextPreflightError
1100
+ || recoveredRequestEstimate <= Math.floor(failedRequestEstimate * 0.9);
1101
+ if (!madeProgress || !meaningfulProviderProgress) {
1102
+ this.messages = messagesBeforeRecovery;
1103
+ this.lastInputTokens = inputTokensBeforeRecovery;
1104
+ this.lastAnchorMessageCount = anchorBeforeRecovery;
1105
+ throw error;
1106
+ }
1049
1107
  consecutiveOverflowRecoveries += 1;
1050
1108
  this.compactionStats.overflow += 1;
1051
1109
  this.compactionStats.fired += 1;
1052
1110
  this.compactionStats.droppedMessages += droppedMessages;
1053
- traceEvent("compaction_fired", { path: "overflow", droppedMessages });
1111
+ traceEvent("compaction_fired", {
1112
+ path: "overflow",
1113
+ droppedMessages,
1114
+ beforeEstimatedTokens: failedRequestEstimate,
1115
+ afterEstimatedTokens: recoveredRequestEstimate,
1116
+ });
1054
1117
  yield emit({ type: "context_recovered", droppedMessages, reason: "overflow" });
1055
1118
  continue;
1056
1119
  }
@@ -1175,7 +1238,7 @@ export class Agent {
1175
1238
  yield emit({ type: "tool_call_end", id: tc.id, name: tc.name, arguments: tc.arguments });
1176
1239
  }
1177
1240
  if (isHiddenToolResult(blockedResult)) {
1178
- let result = blockedResult;
1241
+ let result = normalizeToolResultForModel(blockedResult, this.providerId, this.apiModel);
1179
1242
  await hookBus.runAfterToolCall({
1180
1243
  agent: this,
1181
1244
  cwd,
@@ -1189,6 +1252,7 @@ export class Agent {
1189
1252
  result = next;
1190
1253
  },
1191
1254
  });
1255
+ result = normalizeToolResultForModel(result, this.providerId, this.apiModel);
1192
1256
  const postToolHook = await this.runExternalHook({
1193
1257
  eventName: result.isError ? "PostToolUseFailure" : "PostToolUse",
1194
1258
  cwd,
@@ -1292,6 +1356,9 @@ export class Agent {
1292
1356
  result = resolved ?? { content: `Error: Tool "${tc.name}" returned no result`, isError: true };
1293
1357
  }
1294
1358
  }
1359
+ // Hooks receive a bounded canonical result. This prevents a tool
1360
+ // from handing an unbounded multi-megabyte value to PostToolUse.
1361
+ result = normalizeToolResultForModel(result, this.providerId, this.apiModel);
1295
1362
  await hookBus.runAfterToolCall({
1296
1363
  agent: this,
1297
1364
  cwd,
@@ -1305,6 +1372,9 @@ export class Agent {
1305
1372
  result = next;
1306
1373
  },
1307
1374
  });
1375
+ // A hook is allowed to replace the result, so enforce the invariant
1376
+ // again before persistence, callbacks, UI events and provider reuse.
1377
+ result = normalizeToolResultForModel(result, this.providerId, this.apiModel);
1308
1378
  const postToolHook = await this.runExternalHook({
1309
1379
  eventName: result.isError ? "PostToolUseFailure" : "PostToolUse",
1310
1380
  cwd,
@@ -1339,7 +1409,10 @@ export class Agent {
1339
1409
  truncated: truncatedOutput.truncated,
1340
1410
  originalTokens: truncatedOutput.originalTokens,
1341
1411
  finalTokens: truncatedOutput.finalTokens,
1412
+ originalBytes: truncatedOutput.originalBytes,
1413
+ finalBytes: truncatedOutput.finalBytes,
1342
1414
  limit: truncatedOutput.limit,
1415
+ hardByteLimit: truncatedOutput.hardByteLimit,
1343
1416
  },
1344
1417
  }, traceContext);
1345
1418
  const toolMessage = {
@@ -1458,71 +1531,89 @@ export class Agent {
1458
1531
  }, traceContext);
1459
1532
  }
1460
1533
  }
1461
- async recoverFromOverflow(attempt) {
1534
+ async recoverFromOverflow(attempt, maximumSizeRatio = 0.999_999) {
1462
1535
  const before = this.messages.length;
1463
- const beforeTokens = this.messages.reduce((sum, m) => sum + JSON.stringify(m).length, 0);
1536
+ const originalMessages = this.messages;
1537
+ const beforeBytes = serializedMessageBytes(originalMessages);
1538
+ const commitIfSmaller = (candidate, summary) => {
1539
+ if (!candidate
1540
+ || serializedMessageBytes(candidate) > Math.floor(beforeBytes * maximumSizeRatio))
1541
+ return false;
1542
+ this.messages = candidate;
1543
+ this.lastInputTokens = null;
1544
+ this.lastAnchorMessageCount = null;
1545
+ this.fileStateTracker?.invalidateReadHistory();
1546
+ this.persistCompactionSummary(summary);
1547
+ return true;
1548
+ };
1464
1549
  if (attempt === 0) {
1465
- this.messages = aggressivePruneMessages(this.messages);
1466
- const afterTokens = this.messages.reduce((sum, m) => sum + JSON.stringify(m).length, 0);
1467
- if (afterTokens < beforeTokens) {
1468
- this.lastInputTokens = null;
1469
- this.lastAnchorMessageCount = null;
1470
- this.fileStateTracker?.invalidateReadHistory();
1550
+ if (commitIfSmaller(aggressivePruneMessages(originalMessages))) {
1471
1551
  return before - this.messages.length;
1472
1552
  }
1473
1553
  }
1474
- const keepRecentTurns = attempt >= 2 ? 1 : 2;
1475
- const llmResult = await compactMessagesWithLLM(this.messages, {
1554
+ // Unlike ordinary pruning, overflow recovery is allowed to thin the most
1555
+ // recent completed tool batch. Keeping it protected is exactly what made
1556
+ // a one-turn 6MB grep result impossible to recover from.
1557
+ const emergencyToolLimit = attempt === 0 ? 16 * 1024 : 4 * 1024;
1558
+ const thinnedLatestTools = originalMessages.map((message) => {
1559
+ if (message.role !== "tool")
1560
+ return message;
1561
+ const bounded = truncateToolOutputForModel(message.content, this.providerId, this.apiModel, { hardByteLimit: emergencyToolLimit });
1562
+ return bounded.truncated ? { ...message, content: bounded.content } : message;
1563
+ });
1564
+ if (commitIfSmaller(thinnedLatestTools)) {
1565
+ return before - this.messages.length;
1566
+ }
1567
+ const keepRecentTurns = attempt >= 1 ? 1 : 2;
1568
+ const llmResult = await compactMessagesWithLLM(originalMessages, {
1476
1569
  provider: this.provider,
1477
1570
  model: this.apiModel,
1478
1571
  thinkingLevel: this.thinkingLevel,
1479
1572
  keepRecentTurns,
1480
1573
  });
1481
- if (llmResult.compacted && llmResult.messages) {
1482
- this.messages = llmResult.messages;
1483
- this.lastInputTokens = null;
1484
- this.lastAnchorMessageCount = null;
1485
- this.fileStateTracker?.invalidateReadHistory();
1486
- this.persistCompactionSummary(llmResult.summary);
1574
+ if (llmResult.compacted && commitIfSmaller(llmResult.messages, llmResult.summary)) {
1487
1575
  return before - this.messages.length;
1488
1576
  }
1489
1577
  // Single-turn capable LLM compactor. compactMessagesWithLLM above no-ops
1490
1578
  // when there's only one user turn (the "single huge prompt with many tool
1491
1579
  // calls" case), so try the turn-internal compactor before giving up.
1492
1580
  const { compactWithLLM } = await import("./context/llm-compactor.js");
1493
- const singleTurnResult = await compactWithLLM(this.messages, {
1581
+ const singleTurnResult = await compactWithLLM(originalMessages, {
1494
1582
  provider: this.provider,
1495
1583
  modelId: this.apiModel,
1496
1584
  });
1497
- if (singleTurnResult.compacted && singleTurnResult.messages) {
1498
- this.messages = singleTurnResult.messages;
1499
- this.lastInputTokens = null;
1500
- this.lastAnchorMessageCount = null;
1501
- this.fileStateTracker?.invalidateReadHistory();
1502
- this.persistCompactionSummary(singleTurnResult.summary);
1585
+ if (singleTurnResult.compacted && commitIfSmaller(singleTurnResult.messages, singleTurnResult.summary)) {
1503
1586
  return before - this.messages.length;
1504
1587
  }
1505
- const fallback = compactMessages(this.messages, { keepRecentTurns });
1506
- if (fallback.compacted && fallback.messages) {
1507
- this.messages = fallback.messages;
1508
- this.lastInputTokens = null;
1509
- this.lastAnchorMessageCount = null;
1510
- this.fileStateTracker?.invalidateReadHistory();
1511
- this.persistCompactionSummary(fallback.summary);
1588
+ const fallback = compactMessages(originalMessages, { keepRecentTurns });
1589
+ if (fallback.compacted && commitIfSmaller(fallback.messages, fallback.summary)) {
1590
+ return before - this.messages.length;
1591
+ }
1592
+ const subturn = compactCurrentTurnToolGroups(originalMessages, {
1593
+ keepRecentGroups: attempt === 0 ? 1 : 0,
1594
+ });
1595
+ if (subturn.compacted && commitIfSmaller(subturn.messages, subturn.summary)) {
1512
1596
  return before - this.messages.length;
1513
1597
  }
1514
1598
  // Codex-style last-resort: drop the single oldest non-protected message
1515
1599
  // and let the retry loop try again. Cheap, but eventually narrows even an
1516
1600
  // intractable single-turn overflow.
1517
- const oldestIdx = this.messages.findIndex((m) => m.role !== "system" && m.role !== "meta");
1518
- if (oldestIdx >= 0 && oldestIdx < this.messages.length - 1) {
1519
- this.messages = [
1520
- ...this.messages.slice(0, oldestIdx),
1521
- ...this.messages.slice(oldestIdx + 1),
1601
+ const realUserIndexes = originalMessages
1602
+ .map((message, index) => message.role === "user" ? index : -1)
1603
+ .filter((index) => index >= 0);
1604
+ const oldestIdx = realUserIndexes.length > 1
1605
+ ? realUserIndexes[0]
1606
+ : -1;
1607
+ const removeUntil = realUserIndexes.length > 1
1608
+ ? realUserIndexes[1]
1609
+ : -1;
1610
+ if (oldestIdx >= 0 && removeUntil > oldestIdx && removeUntil < originalMessages.length) {
1611
+ const candidate = [
1612
+ ...originalMessages.slice(0, oldestIdx),
1613
+ ...originalMessages.slice(removeUntil),
1522
1614
  ];
1523
- this.lastInputTokens = null;
1524
- this.lastAnchorMessageCount = null;
1525
- this.fileStateTracker?.invalidateReadHistory();
1615
+ if (!commitIfSmaller(candidate))
1616
+ return 0;
1526
1617
  return before - this.messages.length;
1527
1618
  }
1528
1619
  return 0;
@@ -1530,7 +1621,7 @@ export class Agent {
1530
1621
  compactResidentHistory() {
1531
1622
  this.maybeCompactResidentHistory();
1532
1623
  }
1533
- async maybeCompactWithLLM() {
1624
+ async maybeCompactWithLLM(additionalInputTokens = 0) {
1534
1625
  if (!this.providerId || !this.apiModel)
1535
1626
  return;
1536
1627
  if (this.messages.length === 0)
@@ -1541,6 +1632,7 @@ export class Agent {
1541
1632
  const budget = getContextBudget(this.providerId, this.apiModel, this.messages, {
1542
1633
  usageAnchorTokens: this.lastInputTokens ?? undefined,
1543
1634
  tailMessages: tail,
1635
+ additionalInputTokens,
1544
1636
  });
1545
1637
  if (!budget.shouldCompact)
1546
1638
  return;
@@ -1576,7 +1668,7 @@ export class Agent {
1576
1668
  * (the caller falls back to heuristic compaction in that case). Throws only
1577
1669
  * if the provider stream itself errors.
1578
1670
  */
1579
- async summarizeForCompaction(oldMessages, onDelta, abortSignal) {
1671
+ async summarizeForCompaction(oldMessages, onDelta, abortSignal, userContext) {
1580
1672
  if (oldMessages.length === 0)
1581
1673
  return "";
1582
1674
  const { buildCompactionPromptMessages } = await import("./context/compact-llm.js");
@@ -1589,7 +1681,7 @@ export class Agent {
1589
1681
  const promptInput = oldMessages.map((message) => isCompactionSummaryMessage(message) && typeof message.content === "string"
1590
1682
  ? { ...message, content: stripFileBlocks(messageText(message)) }
1591
1683
  : message);
1592
- const promptMessages = buildCompactionPromptMessages(promptInput);
1684
+ const promptMessages = buildCompactionPromptMessages(promptInput, userContext);
1593
1685
  const stream = this.provider.streamChat(promptMessages, {
1594
1686
  model: this.apiModel,
1595
1687
  temperature: 0.2,
@@ -1641,6 +1733,12 @@ export class Agent {
1641
1733
  listSubAgents() {
1642
1734
  return this.subagents.listSubAgents();
1643
1735
  }
1736
+ /** Read-only child transcript for the TUI inspector. */
1737
+ getSubAgentMessages(agentId) {
1738
+ const record = this.subagentStore.get(agentId);
1739
+ const messages = record?.agent?.messages ?? record?.messages ?? [];
1740
+ return messages.map((message) => ({ ...message }));
1741
+ }
1644
1742
  /** Marks a child's full summary as delivered to parent context (design §3.3). */
1645
1743
  markSubagentDelivered(agentId) {
1646
1744
  this.subagents.markSubagentDelivered(agentId);
@@ -1982,3 +2080,6 @@ export class Agent {
1982
2080
  }
1983
2081
  }
1984
2082
  }
2083
+ function serializedMessageBytes(messages) {
2084
+ return Buffer.byteLength(JSON.stringify(messages), "utf8");
2085
+ }
package/dist/bin.js CHANGED
@@ -1,45 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { spawn, spawnSync } from "node:child_process";
3
- import { fileURLToPath } from "node:url";
4
- const mainPath = fileURLToPath(new URL("./main.js", import.meta.url));
5
- if (process.versions.bun) {
6
- await import("./main.js");
7
- }
8
- else {
9
- const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
10
- if (bunCheck.error) {
11
- console.error([
12
- "Bubble requires Bun to run.",
13
- "",
14
- "Install Bun, then run bubble again:",
15
- " curl -fsSL https://bun.sh/install | bash",
16
- "",
17
- "After installation, restart your terminal if the bun command is not found.",
18
- ].join("\n"));
19
- process.exit(1);
20
- }
21
- const child = spawn("bun", [mainPath, ...process.argv.slice(2)], {
22
- stdio: "inherit",
23
- env: process.env,
24
- });
25
- child.on("error", (error) => {
26
- console.error(`Failed to start Bubble with Bun: ${error.message}`);
27
- process.exit(1);
28
- });
29
- for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
30
- process.on(signal, () => {
31
- if (!child.killed) {
32
- child.kill(signal);
33
- }
34
- });
35
- }
36
- const result = await new Promise((resolve) => {
37
- child.on("exit", (code, signal) => resolve({ code, signal }));
38
- });
39
- if (result.signal) {
40
- process.kill(process.pid, result.signal);
41
- }
42
- else {
43
- process.exit(result.code ?? 1);
44
- }
45
- }
2
+ await import("./main.js");
3
+ export {};
package/dist/config.d.ts CHANGED
@@ -17,6 +17,10 @@ export interface UserConfigData {
17
17
  defaultModel?: string;
18
18
  defaultThinkingLevel?: ThinkingLevel;
19
19
  skillPaths?: string[];
20
+ /** Skill names disabled from invocation/search until re-enabled in /skills. */
21
+ skills?: {
22
+ disabled?: string[];
23
+ };
20
24
  /**
21
25
  * Three shapes are accepted on disk so we can evolve without breaking
22
26
  * existing configs:
@@ -35,9 +39,14 @@ export interface UserConfigData {
35
39
  /** Subagent model-routing knobs: autoTier (default true), allowCrossProvider (default true). */
36
40
  agentRouting?: Partial<AgentRoutingConfig>;
37
41
  subagents?: SubagentsUserConfig;
38
- /** Background tasks (design §2.3b): autoResume defaults ON; false keeps notices + reminders only. */
42
+ /** Background tasks: completion wakes and Grok-style foreground demotion. */
39
43
  tasks?: {
44
+ /** Auto-resume the Agent when an owned task finishes. Defaults ON. */
40
45
  autoResume?: boolean;
46
+ /** Move long foreground Execute calls into Tasks automatically. Defaults ON. */
47
+ autoBackground?: boolean;
48
+ /** Foreground wait budget before automatic demotion. Defaults to 15 seconds. */
49
+ foregroundWaitMs?: number;
41
50
  };
42
51
  }
43
52
  export interface SubagentsUserConfig {
@@ -56,6 +65,8 @@ export declare class UserConfig {
56
65
  getRecentModels(): string[];
57
66
  /** Auto-resume on background-task completion (design §2.3b). Default ON. */
58
67
  getTasksAutoResume(): boolean;
68
+ /** Grok-style foreground wait budget. `false` disables automatic demotion. */
69
+ getTasksAutoBackgroundAfterMs(): number | false;
59
70
  pushRecentModel(model: string): void;
60
71
  getApiKey(): string | undefined;
61
72
  setApiKey(key: string): void;
@@ -65,6 +76,8 @@ export declare class UserConfig {
65
76
  setDefaultProvider(id: string): void;
66
77
  getSkillPaths(): string[];
67
78
  setSkillPaths(paths: string[]): void;
79
+ getDisabledSkills(): string[];
80
+ setDisabledSkills(names: string[]): void;
68
81
  getTheme(): ThemeConfig;
69
82
  getThemeMode(): ThemeMode;
70
83
  getThemeOverrides(): Record<string, string>;
package/dist/config.js CHANGED
@@ -8,7 +8,10 @@ import { dirname, join } from "node:path";
8
8
  import { getBubbleHome } from "./bubble-home.js";
9
9
  import { sanitizeAgentCategories } from "./agent/categories.js";
10
10
  import { sanitizeAgentRouting } from "./agent/routing-catalog.js";
11
- const HIDDEN_PROVIDER_IDS = new Set(["openrouter", "openai-codex"]);
11
+ import { isProviderModelAllowed } from "./provider-model-policy.js";
12
+ // openai-codex is an internal transport profile surfaced through the visible
13
+ // OpenAI OAuth provider. OpenRouter is a regular user-configurable provider.
14
+ const HIDDEN_PROVIDER_IDS = new Set(["openai-codex"]);
12
15
  function getConfigPath() {
13
16
  return join(getBubbleHome(), "config.json");
14
17
  }
@@ -20,10 +23,16 @@ function modelProviderId(model) {
20
23
  return undefined;
21
24
  return model.split(":", 1)[0];
22
25
  }
26
+ function isAllowedConfiguredModel(model) {
27
+ if (!model.includes(":"))
28
+ return true;
29
+ const [providerId, ...modelParts] = model.split(":");
30
+ return isProviderModelAllowed(providerId, modelParts.join(":"));
31
+ }
23
32
  function sanitizeRecentModels(models) {
24
33
  if (!models)
25
34
  return undefined;
26
- return models.filter((model) => !isHiddenProviderId(modelProviderId(model)));
35
+ return models.filter((model) => !isHiddenProviderId(modelProviderId(model)) && isAllowedConfiguredModel(model));
27
36
  }
28
37
  function sanitizeProviders(providers) {
29
38
  if (!providers)
@@ -33,7 +42,9 @@ function sanitizeProviders(providers) {
33
42
  function sanitizeDefaultModel(model) {
34
43
  if (!model)
35
44
  return undefined;
36
- return isHiddenProviderId(modelProviderId(model)) ? undefined : model;
45
+ return isHiddenProviderId(modelProviderId(model)) || !isAllowedConfiguredModel(model)
46
+ ? undefined
47
+ : model;
37
48
  }
38
49
  function sanitizeDefaultProvider(providerId) {
39
50
  return isHiddenProviderId(providerId) ? undefined : providerId;
@@ -143,8 +154,17 @@ export class UserConfig {
143
154
  getTasksAutoResume() {
144
155
  return this.data.tasks?.autoResume !== false;
145
156
  }
157
+ /** Grok-style foreground wait budget. `false` disables automatic demotion. */
158
+ getTasksAutoBackgroundAfterMs() {
159
+ if (this.data.tasks?.autoBackground === false)
160
+ return false;
161
+ const configured = this.data.tasks?.foregroundWaitMs;
162
+ if (typeof configured !== "number" || !Number.isFinite(configured))
163
+ return 15_000;
164
+ return Math.max(1_000, Math.min(300_000, Math.floor(configured)));
165
+ }
146
166
  pushRecentModel(model) {
147
- if (isHiddenProviderId(modelProviderId(model))) {
167
+ if (isHiddenProviderId(modelProviderId(model)) || !isAllowedConfiguredModel(model)) {
148
168
  return;
149
169
  }
150
170
  const recent = this.data.recentModels ?? [];
@@ -182,6 +202,27 @@ export class UserConfig {
182
202
  this.data.skillPaths = paths.slice();
183
203
  this.save();
184
204
  }
205
+ getDisabledSkills() {
206
+ const disabled = this.data.skills?.disabled;
207
+ if (!Array.isArray(disabled))
208
+ return [];
209
+ return [...new Set(disabled.filter((name) => (typeof name === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name))))].sort((a, b) => a.localeCompare(b));
210
+ }
211
+ setDisabledSkills(names) {
212
+ const disabled = [...new Set(names.filter((name) => (/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name))))].sort((a, b) => a.localeCompare(b));
213
+ if (disabled.length === 0) {
214
+ const next = { ...(this.data.skills ?? {}) };
215
+ delete next.disabled;
216
+ if (Object.keys(next).length === 0)
217
+ delete this.data.skills;
218
+ else
219
+ this.data.skills = next;
220
+ }
221
+ else {
222
+ this.data.skills = { ...(this.data.skills ?? {}), disabled };
223
+ }
224
+ this.save();
225
+ }
185
226
  getTheme() {
186
227
  const theme = sanitizeTheme(this.data.theme);
187
228
  return theme ?? { mode: "auto" };
@@ -1,4 +1,4 @@
1
- import type { Message } from "../types.js";
1
+ import type { Message, ToolDefinition } from "../types.js";
2
2
  export declare const OUTPUT_RESERVE_TOKENS = 20000;
3
3
  export declare const AUTOCOMPACT_BUFFER_TOKENS = 13000;
4
4
  export declare const PRUNE_BUFFER_TOKENS = 50000;
@@ -15,8 +15,14 @@ export interface ContextBudgetOptions {
15
15
  usageAnchorTokens?: number;
16
16
  /** Messages appended after the anchor (their tokens are estimated and added). */
17
17
  tailMessages?: Message[];
18
+ /** Tokens outside the message list, primarily serialized tool definitions. */
19
+ additionalInputTokens?: number;
18
20
  }
19
21
  export declare function estimateMessageTokens(message: Message, providerId?: string): number;
20
22
  export declare function estimateContextTokens(messages: Message[], providerId?: string): number;
21
23
  export declare function getContextBudget(providerId: string, modelId: string, messages: Message[], options?: ContextBudgetOptions): ContextBudget;
24
+ /** Estimate the request-space occupied by tool schemas sent on every turn. */
25
+ export declare function estimateToolDefinitionsTokens(tools: ToolDefinition[], providerId?: string): number;
26
+ /** Maximum estimated input size that still leaves useful room for an answer. */
27
+ export declare function getMaxInputTokens(contextWindow: number | undefined): number | undefined;
22
28
  export declare function estimateTextTokens(text: string, providerId?: string): number;
@@ -65,16 +65,39 @@ export function getContextBudget(providerId, modelId, messages, options = {}) {
65
65
  };
66
66
  }
67
67
  function computeEstimatedTokens(providerId, messages, options) {
68
+ const additionalInputTokens = Math.max(0, options.additionalInputTokens ?? 0);
68
69
  if (options.usageAnchorTokens !== undefined && options.tailMessages) {
69
70
  // Anchor is authoritative (server-reported input tokens from the last
70
71
  // response). Tail goes through our estimator and may undercount on dense /
71
72
  // tool-output content, so we inflate it by a small margin before adding.
72
73
  const tailEstimate = estimateContextTokens(options.tailMessages, providerId);
73
- return options.usageAnchorTokens + Math.ceil(tailEstimate * TAIL_SAFETY_MARGIN);
74
+ return options.usageAnchorTokens
75
+ + Math.ceil(tailEstimate * TAIL_SAFETY_MARGIN)
76
+ + additionalInputTokens;
74
77
  }
75
78
  // First turn (or anchor lost): there's no server-reported baseline at all,
76
79
  // so apply a larger safety margin to the pure estimate.
77
- return Math.ceil(estimateContextTokens(messages, providerId) * FIRST_TURN_SAFETY_MARGIN);
80
+ return Math.ceil((estimateContextTokens(messages, providerId) + additionalInputTokens)
81
+ * FIRST_TURN_SAFETY_MARGIN);
82
+ }
83
+ /** Estimate the request-space occupied by tool schemas sent on every turn. */
84
+ export function estimateToolDefinitionsTokens(tools, providerId) {
85
+ if (tools.length === 0)
86
+ return 0;
87
+ // Providers wrap this array in their own function/tool envelope. The fixed
88
+ // per-entry allowance covers names, discriminator keys and separators not
89
+ // represented by the plain JSON payload.
90
+ return estimateTextTokens(JSON.stringify(tools), providerId) + tools.length * 16 + 16;
91
+ }
92
+ /** Maximum estimated input size that still leaves useful room for an answer. */
93
+ export function getMaxInputTokens(contextWindow) {
94
+ if (!contextWindow)
95
+ return undefined;
96
+ return contextWindow >= MIN_WINDOW_FOR_RESERVE
97
+ ? Math.max(1, contextWindow
98
+ - OUTPUT_RESERVE_TOKENS
99
+ - Math.max(AUTOCOMPACT_BUFFER_TOKENS, Math.floor(contextWindow * 0.05)))
100
+ : Math.max(1, Math.floor(contextWindow * 0.75));
78
101
  }
79
102
  function shouldTriggerPrune(estimatedTokens, contextWindow) {
80
103
  if (!contextWindow) {
@@ -21,5 +21,5 @@ export declare function compactMessagesWithLLM(messages: Message[], options: LLM
21
21
  * `oldMessages`. Shared by the non-streaming overflow path (`generateSummary`)
22
22
  * and the streaming manual `/compact` path (`Agent.summarizeForCompaction`).
23
23
  */
24
- export declare function buildCompactionPromptMessages(oldMessages: Message[]): ProviderMessage[];
24
+ export declare function buildCompactionPromptMessages(oldMessages: Message[], userContext?: string): ProviderMessage[];
25
25
  export declare function serializeTranscript(messages: Message[]): string;