@astralyn/pi 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/bundle/chunks/anthropic-messages-NVP5OPIM.js +6 -0
  3. package/dist/bundle/chunks/{azure-openai-responses-CH74HGP4.js → azure-openai-responses-IBTOZ5MH.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-H35KWBT5.js → chunk-4XZYHGVN.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-3MMB5I7J.js → chunk-7HJ6CHUB.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-MAC4N3BW.js → chunk-7UBIBLPN.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-D3XW5X3Z.js → chunk-A5HU4TE6.js} +160 -229
  8. package/dist/bundle/chunks/{chunk-C2ARIVS3.js → chunk-B2UOWO4A.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-KAC2YPTW.js → chunk-J75WV3FL.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-TEAAYBTS.js → chunk-LHSPN2IO.js} +1 -1
  11. package/dist/bundle/chunks/chunk-Q74S7ILH.js +2 -0
  12. package/dist/bundle/chunks/{chunk-QLU3LESZ.js → chunk-QDDSFJ35.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-EJEOEBSJ.js → chunk-UQECUCUH.js} +1 -1
  14. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-XO45ZT3V.js → cloudflare-workers-ai-system-one-HGOVFZDC.js} +1 -1
  15. package/dist/bundle/chunks/codemode-worker.js +17 -9
  16. package/dist/bundle/chunks/github-copilot.js +1 -1
  17. package/dist/bundle/chunks/{google-generative-ai-VVO64ZRQ.js → google-generative-ai-M44TSMAN.js} +1 -1
  18. package/dist/bundle/chunks/{google-vertex-LKS5NUHA.js → google-vertex-3C4OWLFX.js} +1 -1
  19. package/dist/bundle/chunks/{mistral-conversations-NJUOG4ET.js → mistral-conversations-I5PL4VCZ.js} +1 -1
  20. package/dist/bundle/chunks/openai-codex-responses-O5EUDL54.js +10 -0
  21. package/dist/bundle/chunks/{openai-completions-7HTTU6FH.js → openai-completions-X3SSG5BQ.js} +1 -1
  22. package/dist/bundle/chunks/{openai-decisions-776H4VDZ.js → openai-decisions-TVCDIPZX.js} +1 -1
  23. package/dist/bundle/chunks/{openai-responses-7A6BAQ5X.js → openai-responses-NYPNKVDV.js} +1 -1
  24. package/dist/bundle/chunks/{typesafe-system-one-2DQLG4R2.js → typesafe-system-one-LWPJYTJP.js} +1 -1
  25. package/dist/bundle/chunks/{virtual-modules-2NHGQRS6.js → virtual-modules-OZTPKGU7.js} +1 -1
  26. package/dist/bundle/cli.js +1 -1
  27. package/dist/bundle/index.js +1 -1
  28. package/dist/bundle/rpc-entry.js +1 -1
  29. package/dist/core/agent-session.d.ts +14 -1
  30. package/dist/core/agent-session.d.ts.map +1 -1
  31. package/dist/core/agent-session.js +93 -58
  32. package/dist/core/agent-session.js.map +1 -1
  33. package/dist/core/auth-storage.d.ts.map +1 -1
  34. package/dist/core/auth-storage.js +20 -13
  35. package/dist/core/auth-storage.js.map +1 -1
  36. package/dist/core/bug-report.d.ts +9 -1
  37. package/dist/core/bug-report.d.ts.map +1 -1
  38. package/dist/core/compaction/utils.d.ts +6 -0
  39. package/dist/core/compaction/utils.d.ts.map +1 -1
  40. package/dist/core/compaction/utils.js +7 -8
  41. package/dist/core/compaction/utils.js.map +1 -1
  42. package/dist/core/extensions/index.d.ts +1 -1
  43. package/dist/core/extensions/index.d.ts.map +1 -1
  44. package/dist/core/extensions/index.js.map +1 -1
  45. package/dist/core/extensions/runner.d.ts +4 -2
  46. package/dist/core/extensions/runner.d.ts.map +1 -1
  47. package/dist/core/extensions/runner.js +2 -3
  48. package/dist/core/extensions/runner.js.map +1 -1
  49. package/dist/core/extensions/types.d.ts +31 -7
  50. package/dist/core/extensions/types.d.ts.map +1 -1
  51. package/dist/core/extensions/types.js.map +1 -1
  52. package/dist/core/key-id-schema.d.ts +6 -0
  53. package/dist/core/key-id-schema.d.ts.map +1 -0
  54. package/dist/core/key-id-schema.js +16 -0
  55. package/dist/core/key-id-schema.js.map +1 -0
  56. package/dist/core/keybinding-registry.d.ts +4 -3
  57. package/dist/core/keybinding-registry.d.ts.map +1 -1
  58. package/dist/core/keybinding-registry.js +4 -3
  59. package/dist/core/keybinding-registry.js.map +1 -1
  60. package/dist/core/keybindings-schema.d.ts +4 -0
  61. package/dist/core/keybindings-schema.d.ts.map +1 -0
  62. package/dist/core/keybindings-schema.js +16 -0
  63. package/dist/core/keybindings-schema.js.map +1 -0
  64. package/dist/core/keybindings.d.ts +258 -81
  65. package/dist/core/keybindings.d.ts.map +1 -1
  66. package/dist/core/keybindings.js +27 -24
  67. package/dist/core/keybindings.js.map +1 -1
  68. package/dist/core/model-config.d.ts +713 -180
  69. package/dist/core/model-config.d.ts.map +1 -1
  70. package/dist/core/model-config.js +18 -179
  71. package/dist/core/model-config.js.map +1 -1
  72. package/dist/core/model-runtime.d.ts.map +1 -1
  73. package/dist/core/model-runtime.js +11 -6
  74. package/dist/core/model-runtime.js.map +1 -1
  75. package/dist/core/package-manager.d.ts.map +1 -1
  76. package/dist/core/package-manager.js +3 -3
  77. package/dist/core/package-manager.js.map +1 -1
  78. package/dist/core/project-trust.d.ts.map +1 -1
  79. package/dist/core/project-trust.js +2 -1
  80. package/dist/core/project-trust.js.map +1 -1
  81. package/dist/core/remote-catalog-provider.d.ts +12 -1
  82. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  83. package/dist/core/remote-catalog-provider.js +82 -24
  84. package/dist/core/remote-catalog-provider.js.map +1 -1
  85. package/dist/core/resource-loader.d.ts.map +1 -1
  86. package/dist/core/resource-loader.js +7 -2
  87. package/dist/core/resource-loader.js.map +1 -1
  88. package/dist/core/settings-defaults.d.ts +65 -0
  89. package/dist/core/settings-defaults.d.ts.map +1 -0
  90. package/dist/core/settings-defaults.js +66 -0
  91. package/dist/core/settings-defaults.js.map +1 -0
  92. package/dist/core/settings-manager.d.ts +4 -149
  93. package/dist/core/settings-manager.d.ts.map +1 -1
  94. package/dist/core/settings-manager.js +48 -44
  95. package/dist/core/settings-manager.js.map +1 -1
  96. package/dist/core/settings-schema.d.ts +231 -0
  97. package/dist/core/settings-schema.d.ts.map +1 -0
  98. package/dist/core/settings-schema.js +294 -0
  99. package/dist/core/settings-schema.js.map +1 -0
  100. package/dist/core/tools/render-utils.d.ts +18 -0
  101. package/dist/core/tools/render-utils.d.ts.map +1 -1
  102. package/dist/core/tools/render-utils.js +46 -0
  103. package/dist/core/tools/render-utils.js.map +1 -1
  104. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  105. package/dist/core/tools/renderers/bash.js +21 -50
  106. package/dist/core/tools/renderers/bash.js.map +1 -1
  107. package/dist/extensions/btw/keybindings.d.ts +2 -2
  108. package/dist/extensions/btw/keybindings.d.ts.map +1 -1
  109. package/dist/extensions/btw/keybindings.js.map +1 -1
  110. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  111. package/dist/extensions/codemode/execute.js +64 -45
  112. package/dist/extensions/codemode/execute.js.map +1 -1
  113. package/dist/extensions/codemode/index.d.ts.map +1 -1
  114. package/dist/extensions/codemode/index.js +3 -7
  115. package/dist/extensions/codemode/index.js.map +1 -1
  116. package/dist/extensions/codemode/renderer.d.ts +2 -2
  117. package/dist/extensions/codemode/renderer.d.ts.map +1 -1
  118. package/dist/extensions/codemode/renderer.js +9 -3
  119. package/dist/extensions/codemode/renderer.js.map +1 -1
  120. package/dist/extensions/codemode/tool.d.ts +6 -12
  121. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  122. package/dist/extensions/codemode/tool.js +44 -32
  123. package/dist/extensions/codemode/tool.js.map +1 -1
  124. package/dist/extensions/list-keybindings.d.ts +2 -2
  125. package/dist/extensions/list-keybindings.d.ts.map +1 -1
  126. package/dist/extensions/list-keybindings.js.map +1 -1
  127. package/dist/extensions/mcp/cli.js +5 -8
  128. package/dist/extensions/mcp/cli.js.map +1 -1
  129. package/dist/extensions/mcp/index.d.ts.map +1 -1
  130. package/dist/extensions/mcp/index.js +25 -34
  131. package/dist/extensions/mcp/index.js.map +1 -1
  132. package/dist/extensions/mcp/resources.d.ts +0 -6
  133. package/dist/extensions/mcp/resources.d.ts.map +1 -1
  134. package/dist/extensions/mcp/resources.js +1 -1
  135. package/dist/extensions/mcp/resources.js.map +1 -1
  136. package/dist/extensions/mcp/runtime.d.ts +3 -6
  137. package/dist/extensions/mcp/runtime.d.ts.map +1 -1
  138. package/dist/extensions/mcp/runtime.js +4 -39
  139. package/dist/extensions/mcp/runtime.js.map +1 -1
  140. package/dist/extensions/mcp/tools.d.ts +2 -3
  141. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  142. package/dist/extensions/mcp/tools.js +62 -6
  143. package/dist/extensions/mcp/tools.js.map +1 -1
  144. package/dist/extensions/provider/keybindings.d.ts +2 -2
  145. package/dist/extensions/provider/keybindings.d.ts.map +1 -1
  146. package/dist/extensions/provider/keybindings.js.map +1 -1
  147. package/dist/extensions/search/code-search.d.ts +0 -2
  148. package/dist/extensions/search/code-search.d.ts.map +1 -1
  149. package/dist/extensions/search/code-search.js +2 -2
  150. package/dist/extensions/search/code-search.js.map +1 -1
  151. package/dist/extensions/search/command.js +2 -2
  152. package/dist/extensions/search/command.js.map +1 -1
  153. package/dist/extensions/search/index.d.ts.map +1 -1
  154. package/dist/extensions/search/index.js +14 -11
  155. package/dist/extensions/search/index.js.map +1 -1
  156. package/dist/extensions/search/workspace.d.ts +11 -10
  157. package/dist/extensions/search/workspace.d.ts.map +1 -1
  158. package/dist/extensions/search/workspace.js +259 -141
  159. package/dist/extensions/search/workspace.js.map +1 -1
  160. package/dist/extensions/tool-search/index.d.ts.map +1 -1
  161. package/dist/extensions/tool-search/index.js +1 -6
  162. package/dist/extensions/tool-search/index.js.map +1 -1
  163. package/dist/extensions/tool-search/tool.d.ts +0 -2
  164. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  165. package/dist/extensions/tool-search/tool.js +2 -4
  166. package/dist/extensions/tool-search/tool.js.map +1 -1
  167. package/dist/index.d.ts +1 -1
  168. package/dist/index.d.ts.map +1 -1
  169. package/dist/index.js.map +1 -1
  170. package/dist/main.js +3 -3
  171. package/dist/main.js.map +1 -1
  172. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  173. package/dist/modes/interactive/components/settings-selector.js +3 -2
  174. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  175. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  176. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/status-indicator.js +2 -2
  178. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  179. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  180. package/dist/modes/interactive/interactive-mode.js +11 -5
  181. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  182. package/dist/modes/interactive/theme/dark.json +1 -1
  183. package/dist/modes/interactive/theme/light.json +1 -1
  184. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  185. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  186. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  187. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  188. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  189. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  190. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  191. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  192. package/dist/modes/interactive/theme/theme.d.ts +13 -16
  193. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  194. package/dist/modes/interactive/theme/theme.js +23 -31
  195. package/dist/modes/interactive/theme/theme.js.map +1 -1
  196. package/dist/modes/print-mode.d.ts.map +1 -1
  197. package/dist/modes/print-mode.js +4 -0
  198. package/dist/modes/print-mode.js.map +1 -1
  199. package/dist/utils/clipboard.d.ts.map +1 -1
  200. package/dist/utils/clipboard.js +6 -1
  201. package/dist/utils/clipboard.js.map +1 -1
  202. package/docs/bundled/extensions/search.md +2 -1
  203. package/docs/codemode.md +1 -1
  204. package/docs/compaction.md +2 -2
  205. package/docs/environment-variables.md +2 -1
  206. package/docs/extensions.md +10 -4
  207. package/docs/keybindings.md +1 -0
  208. package/docs/mcp.md +1 -1
  209. package/docs/models.md +1 -0
  210. package/docs/providers.md +16 -0
  211. package/docs/settings.md +2 -2
  212. package/docs/themes.md +4 -2
  213. package/docs/tui.md +1 -1
  214. package/docs/usage.md +2 -0
  215. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  216. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  217. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  218. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  219. package/examples/extensions/gondolin/package-lock.json +2 -2
  220. package/examples/extensions/gondolin/package.json +1 -1
  221. package/examples/extensions/overlay-test.ts +2 -2
  222. package/examples/extensions/plan-mode/index.ts +6 -2
  223. package/examples/extensions/sandbox/package-lock.json +2 -2
  224. package/examples/extensions/sandbox/package.json +1 -1
  225. package/examples/extensions/with-deps/package-lock.json +2 -2
  226. package/examples/extensions/with-deps/package.json +1 -1
  227. package/npm-shrinkwrap.json +54 -102
  228. package/package.json +8 -20
  229. package/schemas/keybindings.schema.json +453 -0
  230. package/schemas/models.schema.json +1157 -0
  231. package/schemas/settings.schema.json +782 -0
  232. package/schemas/theme.schema.json +365 -0
  233. package/dist/bundle/chunks/anthropic-messages-3K3DWVY6.js +0 -6
  234. package/dist/bundle/chunks/openai-codex-responses-TZWBTTYM.js +0 -10
  235. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  236. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  237. package/dist/modes/interactive/theme/theme-json.js +0 -132
  238. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  239. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  240. package/examples/plugins/pi-example-plugin/README.md +0 -38
  241. package/examples/plugins/pi-example-plugin/package.json +0 -10
  242. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  243. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  244. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
@@ -1 +1 @@
1
- {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAKpE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjF;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACzG,UAAU,EAAE,OAAO,GACjB,MAAM,CAsBR;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAkB5G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEnD;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,MAAM,CAKR"}
1
+ {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAIpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjF;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACzG,UAAU,EAAE,OAAO,GACjB,MAAM,CAsBR;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAkB5G;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED,6GAA6G;AAC7G,UAAU,cAAc;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CAC1C;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,CAAC,cAAc,CAAC,GAAG,IAAI,CAM/E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAkBhH;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEnD;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,MAAM,CAKR"}
@@ -77,6 +77,52 @@ export function formatToolCallWithArgs(title, args, theme, expanded) {
77
77
  const preview = pairs.length > COLLAPSED_ARGS_CHARS ? `${pairs.slice(0, COLLAPSED_ARGS_CHARS - 3)}...` : pairs;
78
78
  return `${header} ${theme.fg("muted", preview)}`;
79
79
  }
80
+ /** Duration as `4.2s` below a minute, then `1m 30s` and `2h 3m 4s`. */
81
+ export function formatToolDuration(ms) {
82
+ const seconds = ms / 1000;
83
+ if (seconds < 60)
84
+ return `${seconds.toFixed(1)}s`;
85
+ const totalSeconds = Math.floor(seconds);
86
+ const minutes = Math.floor(totalSeconds / 60);
87
+ const remainder = totalSeconds % 60;
88
+ if (minutes < 60)
89
+ return `${minutes}m ${remainder}s`;
90
+ return `${Math.floor(minutes / 60)}h ${minutes % 60}m ${remainder}s`;
91
+ }
92
+ /** Start the row's clock once execution starts. Call from `renderCall`. */
93
+ export function startToolTimer(context) {
94
+ const state = context.state;
95
+ if (context.executionStarted && state.startedAt === undefined) {
96
+ state.startedAt = Date.now();
97
+ state.endedAt = undefined;
98
+ }
99
+ }
100
+ /**
101
+ * The row's timing line, from `renderResult`: `Elapsed 4.2s` while the result is partial, redrawn
102
+ * every second, and `Took 4.2s` once it is final. Undefined when the row has no start time and no
103
+ * recorded duration.
104
+ */
105
+ export function toolTimerText(context, isPartial) {
106
+ const state = context.state;
107
+ if (state.startedAt !== undefined && isPartial && !state.interval) {
108
+ state.interval = setInterval(() => context.invalidate(), 1000);
109
+ }
110
+ if (!isPartial || context.isError) {
111
+ state.endedAt ??= Date.now();
112
+ if (state.interval) {
113
+ clearInterval(state.interval);
114
+ state.interval = undefined;
115
+ }
116
+ }
117
+ // A final result's recorded duration wins: it is monotonic and survives reloads. The renderer's own clock is the
118
+ // fallback for live progress and for results stored without one.
119
+ if (!isPartial && context.durationMs !== undefined)
120
+ return `Took ${formatToolDuration(context.durationMs)}`;
121
+ if (state.startedAt === undefined)
122
+ return undefined;
123
+ const endTime = state.endedAt ?? Date.now();
124
+ return `${isPartial ? "Elapsed" : "Took"} ${formatToolDuration(endTime - state.startedAt)}`;
125
+ }
80
126
  export function invalidArgText(theme) {
81
127
  return theme.fg("error", "[invalid arg]");
82
128
  }
@@ -1 +1 @@
1
- {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,UAAU,WAAW,CAAC,IAAa;IACxC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAkB,EAAE,OAAe,EAAE,GAAW;IACxE,IAAI,CAAC,eAAe,EAAE,CAAC,UAAU;QAAE,OAAO,UAAU,CAAC;IACrD,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,SAAS,CAAC,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,aAAa,CAC5B,MAAyG,EACzG,UAAmB;IAEnB,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACnE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAErE,IAAI,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhH,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,eAAe,GAAG,WAAW;aACjC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACZ,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,eAAe,CAAC;YACjD,MAAM,IAAI,GACT,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAClG,OAAO,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,eAAe,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;IACrE,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAOD,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,IAAa,EAAE,KAAY,EAAE,QAAiB;IACnG,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACxD,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC;IAChC,MAAM,OAAO,GACZ,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC,CAAE,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAyB,CAAC;IAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC1C,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACnG,OAAO,KAAK,GAAG,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvF,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,KAAK,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1G,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/G,OAAO,GAAG,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAY;IAC1C,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,cAAc,CAC7B,OAAsB,EACtB,KAAY,EACZ,GAAW,EACX,OAAoC;IAEpC,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,EAAE,aAAa,CAAC;IAChD,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;AACrE,CAAC","sourcesContent":["import * as os from \"node:os\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { getCapabilities, getImageDimensions, hyperlink, imageFallback } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function linkPath(styledText: string, rawPath: string, cwd: string): string {\n\tif (!getCapabilities().hyperlinks) return styledText;\n\tconst absolutePath = resolvePath(rawPath, cwd);\n\treturn hyperlink(styledText, pathToFileURL(absolutePath).href);\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport function getTextOutput(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }> } | undefined,\n\tshowImages: boolean,\n): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nconst COLLAPSED_ARGS_CHARS = 100;\n\n/**\n * Generic tool call header: the title followed by the arguments. Collapsed, they are `key=value`\n * pairs on the title line, cut to {@link COLLAPSED_ARGS_CHARS}. Expanded, each is a `key: value`\n * line below the title, with strings shown raw and continuation lines indented.\n */\nexport function formatToolCallWithArgs(title: string, args: unknown, theme: Theme, expanded: boolean): string {\n\tconst header = theme.fg(\"toolTitle\", theme.bold(title));\n\tif (args == null) return header;\n\tconst entries =\n\t\ttypeof args === \"object\" && !Array.isArray(args)\n\t\t\t? Object.entries(args)\n\t\t\t: ([[\"args\", args]] as [string, unknown][]);\n\tif (entries.length === 0) return header;\n\tif (expanded) {\n\t\tconst lines = entries.map(([key, value]) => {\n\t\t\tconst text = typeof value === \"string\" ? value : (JSON.stringify(value, null, 2) ?? String(value));\n\t\t\treturn ` ${key}: ${replaceTabs(text).replace(/\\r/g, \"\").split(\"\\n\").join(\"\\n \")}`;\n\t\t});\n\t\treturn `${header}\\n${theme.fg(\"muted\", lines.join(\"\\n\"))}`;\n\t}\n\tconst pairs = entries.map(([key, value]) => `${key}=${JSON.stringify(value) ?? String(value)}`).join(\" \");\n\tconst preview = pairs.length > COLLAPSED_ARGS_CHARS ? `${pairs.slice(0, COLLAPSED_ARGS_CHARS - 3)}...` : pairs;\n\treturn `${header} ${theme.fg(\"muted\", preview)}`;\n}\n\nexport function invalidArgText(theme: Theme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\nexport function renderToolPath(\n\trawPath: string | null,\n\ttheme: Theme,\n\tcwd: string,\n\toptions?: { emptyFallback?: string },\n): string {\n\tif (rawPath === null) return invalidArgText(theme);\n\tconst value = rawPath || options?.emptyFallback;\n\tif (!value) return theme.fg(\"toolOutput\", \"...\");\n\treturn linkPath(theme.fg(\"accent\", shortenPath(value)), value, cwd);\n}\n"]}
1
+ {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as os from \"node:os\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { getCapabilities, getImageDimensions, hyperlink, imageFallback } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { ToolRenderContext } from \"../extensions/types.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function linkPath(styledText: string, rawPath: string, cwd: string): string {\n\tif (!getCapabilities().hyperlinks) return styledText;\n\tconst absolutePath = resolvePath(rawPath, cwd);\n\treturn hyperlink(styledText, pathToFileURL(absolutePath).href);\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport function getTextOutput(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }> } | undefined,\n\tshowImages: boolean,\n): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nconst COLLAPSED_ARGS_CHARS = 100;\n\n/**\n * Generic tool call header: the title followed by the arguments. Collapsed, they are `key=value`\n * pairs on the title line, cut to {@link COLLAPSED_ARGS_CHARS}. Expanded, each is a `key: value`\n * line below the title, with strings shown raw and continuation lines indented.\n */\nexport function formatToolCallWithArgs(title: string, args: unknown, theme: Theme, expanded: boolean): string {\n\tconst header = theme.fg(\"toolTitle\", theme.bold(title));\n\tif (args == null) return header;\n\tconst entries =\n\t\ttypeof args === \"object\" && !Array.isArray(args)\n\t\t\t? Object.entries(args)\n\t\t\t: ([[\"args\", args]] as [string, unknown][]);\n\tif (entries.length === 0) return header;\n\tif (expanded) {\n\t\tconst lines = entries.map(([key, value]) => {\n\t\t\tconst text = typeof value === \"string\" ? value : (JSON.stringify(value, null, 2) ?? String(value));\n\t\t\treturn ` ${key}: ${replaceTabs(text).replace(/\\r/g, \"\").split(\"\\n\").join(\"\\n \")}`;\n\t\t});\n\t\treturn `${header}\\n${theme.fg(\"muted\", lines.join(\"\\n\"))}`;\n\t}\n\tconst pairs = entries.map(([key, value]) => `${key}=${JSON.stringify(value) ?? String(value)}`).join(\" \");\n\tconst preview = pairs.length > COLLAPSED_ARGS_CHARS ? `${pairs.slice(0, COLLAPSED_ARGS_CHARS - 3)}...` : pairs;\n\treturn `${header} ${theme.fg(\"muted\", preview)}`;\n}\n\n/** Duration as `4.2s` below a minute, then `1m 30s` and `2h 3m 4s`. */\nexport function formatToolDuration(ms: number): string {\n\tconst seconds = ms / 1000;\n\tif (seconds < 60) return `${seconds.toFixed(1)}s`;\n\n\tconst totalSeconds = Math.floor(seconds);\n\tconst minutes = Math.floor(totalSeconds / 60);\n\tconst remainder = totalSeconds % 60;\n\tif (minutes < 60) return `${minutes}m ${remainder}s`;\n\n\treturn `${Math.floor(minutes / 60)}h ${minutes % 60}m ${remainder}s`;\n}\n\n/** Renderer state of {@link startToolTimer} and {@link toolTimerText}, kept in `ToolRenderContext.state`. */\ninterface ToolTimerState {\n\tstartedAt?: number;\n\tendedAt?: number;\n\tinterval?: ReturnType<typeof setInterval>;\n}\n\n/** Start the row's clock once execution starts. Call from `renderCall`. */\nexport function startToolTimer(context: ToolRenderContext<ToolTimerState>): void {\n\tconst state = context.state;\n\tif (context.executionStarted && state.startedAt === undefined) {\n\t\tstate.startedAt = Date.now();\n\t\tstate.endedAt = undefined;\n\t}\n}\n\n/**\n * The row's timing line, from `renderResult`: `Elapsed 4.2s` while the result is partial, redrawn\n * every second, and `Took 4.2s` once it is final. Undefined when the row has no start time and no\n * recorded duration.\n */\nexport function toolTimerText(context: ToolRenderContext<ToolTimerState>, isPartial: boolean): string | undefined {\n\tconst state = context.state;\n\tif (state.startedAt !== undefined && isPartial && !state.interval) {\n\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t}\n\tif (!isPartial || context.isError) {\n\t\tstate.endedAt ??= Date.now();\n\t\tif (state.interval) {\n\t\t\tclearInterval(state.interval);\n\t\t\tstate.interval = undefined;\n\t\t}\n\t}\n\t// A final result's recorded duration wins: it is monotonic and survives reloads. The renderer's own clock is the\n\t// fallback for live progress and for results stored without one.\n\tif (!isPartial && context.durationMs !== undefined) return `Took ${formatToolDuration(context.durationMs)}`;\n\tif (state.startedAt === undefined) return undefined;\n\tconst endTime = state.endedAt ?? Date.now();\n\treturn `${isPartial ? \"Elapsed\" : \"Took\"} ${formatToolDuration(endTime - state.startedAt)}`;\n}\n\nexport function invalidArgText(theme: Theme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\nexport function renderToolPath(\n\trawPath: string | null,\n\ttheme: Theme,\n\tcwd: string,\n\toptions?: { emptyFallback?: string },\n): string {\n\tif (rawPath === null) return invalidArgText(theme);\n\tconst value = rawPath || options?.emptyFallback;\n\tif (!value) return theme.fg(\"toolOutput\", \"...\");\n\treturn linkPath(theme.fg(\"accent\", shortenPath(value)), value, cwd);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/renderers/bash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAA8C,MAAM,2BAA2B,CAAC;AAE5G,OAAO,KAAK,EAAoC,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAI1F,eAAO,MAAM,uBAAuB,MAAM,CAAC;AA6N3C,4GAA4G;AAC5G,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ,CAAC,GAC9C,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC,CAqC/D"}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/renderers/bash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAA8C,MAAM,2BAA2B,CAAC;AAE5G,OAAO,KAAK,EAAoC,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAI1F,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAqM3C,4GAA4G;AAC5G,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ,CAAC,GAC9C,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC,CAmB/D"}
@@ -9,7 +9,7 @@ import { Container, Spacer, Text, truncateToWidth, visibleWidth } from "@earendi
9
9
  import { keyHint } from "../../../modes/interactive/components/keybinding-hints.js";
10
10
  import { VisualLinePreview } from "../../../modes/interactive/components/visual-truncate.js";
11
11
  import { highlightCode, theme } from "../../../modes/interactive/theme/theme.js";
12
- import { getTextOutput, invalidArgText, str } from "../render-utils.js";
12
+ import { getTextOutput, invalidArgText, startToolTimer, str, toolTimerText } from "../render-utils.js";
13
13
  import { DEFAULT_MAX_BYTES, formatSize } from "../truncate.js";
14
14
  const BASH_PREVIEW_LINES = 5;
15
15
  export const BASH_UPDATE_THROTTLE_MS = 100;
@@ -49,39 +49,28 @@ class BashCallRenderComponent {
49
49
  return [formatTruncatedShellCall(this.command, this.timeout, width, this.config)];
50
50
  }
51
51
  }
52
- const shellTiming = Symbol("shellTiming");
53
- /** Either slot can initialize timing, since extensions may replace the other slot. */
54
- function getShellTiming(context) {
55
- if (!context.executionStarted)
56
- return undefined;
52
+ const shellTimerHooked = Symbol("shellTimerHooked");
53
+ /**
54
+ * Start the row's clock and hook disposal once. Either slot can initialize timing, since
55
+ * extensions may replace the other slot; upstream's timer state outlives a replaced slot, but
56
+ * its refresh interval must still end when the row is disposed mid-run.
57
+ */
58
+ function ensureShellTimer(context) {
59
+ startToolTimer(context);
57
60
  const state = context.state;
58
- if (state[shellTiming])
59
- return state[shellTiming];
60
- const timing = { startedAt: Date.now(), disposed: false };
61
- state[shellTiming] = timing;
61
+ if (state[shellTimerHooked])
62
+ return;
63
+ state[shellTimerHooked] = true;
62
64
  const previousDispose = state.dispose;
63
65
  state.dispose = () => {
64
66
  try {
65
67
  previousDispose?.call(state);
66
68
  }
67
69
  finally {
68
- clearInterval(timing.interval);
69
- timing.interval = undefined;
70
- timing.disposed = true;
70
+ clearInterval(state.interval);
71
+ state.interval = undefined;
71
72
  }
72
73
  };
73
- return timing;
74
- }
75
- function formatDuration(ms) {
76
- const seconds = ms / 1000;
77
- if (seconds < 60)
78
- return `${seconds.toFixed(1)}s`;
79
- const totalSeconds = Math.floor(seconds);
80
- const minutes = Math.floor(totalSeconds / 60);
81
- const remainder = totalSeconds % 60;
82
- if (minutes < 60)
83
- return `${minutes}m ${remainder}s`;
84
- return `${Math.floor(minutes / 60)}h ${minutes % 60}m ${remainder}s`;
85
74
  }
86
75
  function formatShellPrompt(prompt) {
87
76
  return theme.fg("toolTitle", theme.bold(`${prompt} `));
@@ -121,7 +110,7 @@ function formatTruncatedShellCall(command, timeout, width, config) {
121
110
  : theme.fg("muted", " …");
122
111
  return fitCollapsedShellCall(styleShellCommand(firstNonEmptyLine, config), timeout, width, suffix, config);
123
112
  }
124
- function rebuildBashResultRenderComponent(component, result, options, showImages, startedAt, endedAt, durationMs, submittedAsBackground) {
113
+ function rebuildBashResultRenderComponent(component, result, options, showImages, timerText, submittedAsBackground) {
125
114
  component.clear();
126
115
  const background = result.details?.background;
127
116
  let output = background?.kind === "background"
@@ -169,42 +158,24 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
169
158
  }
170
159
  component.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
171
160
  }
172
- // A final result's recorded duration wins: it is monotonic and survives reloads. The renderer's own clock is the
173
- // fallback for live progress and for results stored without one.
174
- if (!options.isPartial && durationMs !== undefined) {
175
- component.addChild(new Text(`\n${theme.fg("muted", `Took ${formatDuration(durationMs)}`)}`, 0, 0));
176
- }
177
- else if (startedAt !== undefined) {
178
- const label = options.isPartial ? "Elapsed" : "Took";
179
- const endTime = endedAt ?? Date.now();
180
- component.addChild(new Text(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));
181
- }
161
+ if (timerText)
162
+ component.addChild(new Text(`\n${theme.fg("muted", timerText)}`, 0, 0));
182
163
  }
183
164
  /** Shell renderers are shared by bash and powershell, which differ in the prompt and highlight language. */
184
165
  export function createShellRenderers(config) {
185
166
  return {
186
167
  renderCall(args, _theme, context) {
187
- getShellTiming(context);
168
+ ensureShellTimer(context);
188
169
  const component = context.lastComponent ?? new BashCallRenderComponent(config);
189
170
  component.update(args, context.expanded);
190
171
  return component;
191
172
  },
192
173
  renderResult(result, options, _theme, context) {
193
- const timing = getShellTiming(context);
194
- if (timing) {
195
- if (!options.isPartial || context.isError) {
196
- timing.endedAt ??= Date.now();
197
- clearInterval(timing.interval);
198
- timing.interval = undefined;
199
- }
200
- else if (!timing.disposed && timing.interval === undefined) {
201
- timing.interval = setInterval(() => context.invalidate(), 1000);
202
- timing.interval.unref?.();
203
- }
204
- }
174
+ ensureShellTimer(context);
175
+ const timerText = toolTimerText(context, options.isPartial);
205
176
  const component = context.lastComponent ?? new Container();
206
177
  const submitted = context.args?.background === true;
207
- rebuildBashResultRenderComponent(component, result, options, context.showImages, timing?.startedAt, timing?.endedAt, context.durationMs, submitted);
178
+ rebuildBashResultRenderComponent(component, result, options, context.showImages, timerText, submitted);
208
179
  component.invalidate();
209
180
  return component;
210
181
  },
@@ -1 +1 @@
1
- {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../../src/core/tools/renderers/bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Presentation for the shell tools.\n *\n * Renderers live apart from the implementation so a process that only displays tool output does not\n * load the execution path or its typebox parameter schema. `bash.ts` spreads these into the shell\n * tool definition, so the tool's public shape is unchanged.\n */\n\nimport { type Component, Container, Spacer, Text, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { keyHint } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport { VisualLinePreview } from \"../../../modes/interactive/components/visual-truncate.ts\";\nimport { highlightCode, theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderContext, ToolRenderResultOptions } from \"../../extensions/types.ts\";\nimport { getTextOutput, invalidArgText, str } from \"../render-utils.ts\";\nimport type { BashRenderState, BashToolDetails, ShellToolConfig } from \"../shell-tool.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize } from \"../truncate.ts\";\n\nconst BASH_PREVIEW_LINES = 5;\nexport const BASH_UPDATE_THROTTLE_MS = 100;\nclass BashCallRenderComponent implements Component {\n\tprivate command: string | null = \"\";\n\tprivate timeout: number | undefined;\n\tprivate expanded = false;\n\tprivate cachedWidth: number | undefined;\n\tprivate cachedLines: string[] | undefined;\n\tprivate readonly config: Pick<ShellToolConfig, \"name\" | \"prompt\">;\n\n\tconstructor(config: Pick<ShellToolConfig, \"name\" | \"prompt\">) {\n\t\tthis.config = config;\n\t}\n\n\tupdate(args: { command?: string; timeout?: number } | undefined, expanded: boolean): void {\n\t\tconst command = str(args?.command);\n\t\tconst timeout = args?.timeout as number | undefined;\n\t\tif (this.command === command && this.timeout === timeout && this.expanded === expanded) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.command = command;\n\t\tthis.timeout = timeout;\n\t\tthis.expanded = expanded;\n\t\tthis.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.cachedLines === undefined || this.cachedWidth !== width) {\n\t\t\tthis.cachedLines = this.renderLines(Math.max(1, width));\n\t\t\tthis.cachedWidth = width;\n\t\t}\n\t\treturn this.cachedLines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tprivate renderLines(width: number): string[] {\n\t\tconst fullCall = formatFullShellCall(this.command, this.timeout, this.config);\n\t\tconst fullLines = new Text(fullCall, 0, 0).render(width);\n\t\tif (this.expanded || fullLines.length <= 1) return fullLines;\n\t\treturn [formatTruncatedShellCall(this.command, this.timeout, width, this.config)];\n\t}\n}\nconst shellTiming = Symbol(\"shellTiming\");\ninterface ShellTiming {\n\tstartedAt: number;\n\tendedAt?: number;\n\tinterval?: ReturnType<typeof setInterval>;\n\tdisposed: boolean;\n}\ninterface ShellRenderState extends BashRenderState {\n\t[shellTiming]?: ShellTiming;\n}\n\n/** Either slot can initialize timing, since extensions may replace the other slot. */\nfunction getShellTiming(context: ToolRenderContext<ShellRenderState>): ShellTiming | undefined {\n\tif (!context.executionStarted) return undefined;\n\tconst state = context.state;\n\tif (state[shellTiming]) return state[shellTiming];\n\tconst timing: ShellTiming = { startedAt: Date.now(), disposed: false };\n\tstate[shellTiming] = timing;\n\tconst previousDispose = state.dispose;\n\tstate.dispose = () => {\n\t\ttry {\n\t\t\tpreviousDispose?.call(state);\n\t\t} finally {\n\t\t\tclearInterval(timing.interval);\n\t\t\ttiming.interval = undefined;\n\t\t\ttiming.disposed = true;\n\t\t}\n\t};\n\treturn timing;\n}\n\nfunction formatDuration(ms: number): string {\n\tconst seconds = ms / 1000;\n\tif (seconds < 60) return `${seconds.toFixed(1)}s`;\n\n\tconst totalSeconds = Math.floor(seconds);\n\tconst minutes = Math.floor(totalSeconds / 60);\n\tconst remainder = totalSeconds % 60;\n\tif (minutes < 60) return `${minutes}m ${remainder}s`;\n\n\treturn `${Math.floor(minutes / 60)}h ${minutes % 60}m ${remainder}s`;\n}\nfunction formatShellPrompt(prompt: string): string {\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} `));\n}\nfunction formatShellTimeout(timeout: number | undefined): string {\n\treturn timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n}\nfunction styleShellCommand(command: string, config: Pick<ShellToolConfig, \"name\" | \"prompt\">): string {\n\tconst language = config.name === \"powershell\" ? \"powershell\" : \"bash\";\n\treturn highlightCode(command, language).join(\"\\n\");\n}\nfunction formatFullShellCall(\n\tcommand: string | null,\n\ttimeout: number | undefined,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tconst commandDisplay =\n\t\tcommand === null\n\t\t\t? invalidArgText(theme)\n\t\t\t: command\n\t\t\t\t? styleShellCommand(command, config)\n\t\t\t\t: theme.fg(\"toolOutput\", \"...\");\n\treturn formatShellPrompt(config.prompt) + commandDisplay + formatShellTimeout(timeout);\n}\nfunction fitCollapsedShellCall(\n\tbody: string,\n\ttimeout: number | undefined,\n\twidth: number,\n\tsuffix: string,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tconst prompt = formatShellPrompt(config.prompt);\n\tconst timeoutSuffix = formatShellTimeout(timeout);\n\tconst bodyWidth = Math.max(0, width - visibleWidth(prompt) - visibleWidth(suffix) - visibleWidth(timeoutSuffix));\n\tconst fittedBody = truncateToWidth(body, bodyWidth, \"\");\n\treturn truncateToWidth(prompt + fittedBody + suffix + timeoutSuffix, width, \"…\");\n}\nfunction formatTruncatedShellCall(\n\tcommand: string | null,\n\ttimeout: number | undefined,\n\twidth: number,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tif (command === null) return truncateToWidth(formatFullShellCall(command, timeout, config), width, \"…\");\n\tif (!command) return formatFullShellCall(command, timeout, config);\n\tconst physicalLines = command.split(/\\r?\\n/).filter((line) => line.trim());\n\tconst firstNonEmptyLine = physicalLines[0]?.trim() ?? command.trim();\n\tconst hiddenLines = Math.max(0, physicalLines.length - 1);\n\tconst suffix =\n\t\thiddenLines > 0\n\t\t\t? theme.fg(\"muted\", ` (+${hiddenLines} line${hiddenLines === 1 ? \"\" : \"s\"})`)\n\t\t\t: theme.fg(\"muted\", \" …\");\n\treturn fitCollapsedShellCall(styleShellCommand(firstNonEmptyLine, config), timeout, width, suffix, config);\n}\nfunction rebuildBashResultRenderComponent(\n\tcomponent: Container,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n\tdurationMs: number | undefined,\n\tsubmittedAsBackground: boolean,\n): void {\n\tcomponent.clear();\n\n\tconst background = result.details?.background;\n\tlet output =\n\t\tbackground?.kind === \"background\"\n\t\t\t? `${submittedAsBackground ? \"Running in the background\" : \"Moved to background\"} · ${background.taskId}`\n\t\t\t: getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(\n\t\t\t\tnew VisualLinePreview({\n\t\t\t\t\ttext: styledOutput,\n\t\t\t\t\tmaxVisualLines: BASH_PREVIEW_LINES,\n\t\t\t\t\tkeep: \"end\",\n\t\t\t\t\tformatHint: (hidden) =>\n\t\t\t\t\t\ttheme.fg(\"muted\", `... (${hidden} earlier lines,`) +\n\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\t// A final result's recorded duration wins: it is monotonic and survives reloads. The renderer's own clock is the\n\t// fallback for live progress and for results stored without one.\n\tif (!options.isPartial && durationMs !== undefined) {\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `Took ${formatDuration(durationMs)}`)}`, 0, 0));\n\t} else if (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\n/** Shell renderers are shared by bash and powershell, which differ in the prompt and highlight language. */\nexport function createShellRenderers(\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): Pick<ToolDefinition<any, any>, \"renderCall\" | \"renderResult\"> {\n\treturn {\n\t\trenderCall(args, _theme, context) {\n\t\t\tgetShellTiming(context);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashCallRenderComponent | undefined) ?? new BashCallRenderComponent(config);\n\t\t\tcomponent.update(args as { command?: string; timeout?: number } | undefined, context.expanded);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst timing = getShellTiming(context);\n\t\t\tif (timing) {\n\t\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\t\ttiming.endedAt ??= Date.now();\n\t\t\t\t\tclearInterval(timing.interval);\n\t\t\t\t\ttiming.interval = undefined;\n\t\t\t\t} else if (!timing.disposed && timing.interval === undefined) {\n\t\t\t\t\ttiming.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t\t\ttiming.interval.unref?.();\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tconst submitted = (context.args as { background?: boolean } | undefined)?.background === true;\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\ttiming?.startedAt,\n\t\t\t\ttiming?.endedAt,\n\t\t\t\tcontext.durationMs,\n\t\t\t\tsubmitted,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../../src/core/tools/renderers/bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Presentation for the shell tools.\n *\n * Renderers live apart from the implementation so a process that only displays tool output does not\n * load the execution path or its typebox parameter schema. `bash.ts` spreads these into the shell\n * tool definition, so the tool's public shape is unchanged.\n */\n\nimport { type Component, Container, Spacer, Text, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { keyHint } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport { VisualLinePreview } from \"../../../modes/interactive/components/visual-truncate.ts\";\nimport { highlightCode, theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderContext, ToolRenderResultOptions } from \"../../extensions/types.ts\";\nimport { getTextOutput, invalidArgText, startToolTimer, str, toolTimerText } from \"../render-utils.ts\";\nimport type { BashRenderState, BashToolDetails, ShellToolConfig } from \"../shell-tool.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize } from \"../truncate.ts\";\n\nconst BASH_PREVIEW_LINES = 5;\nexport const BASH_UPDATE_THROTTLE_MS = 100;\nclass BashCallRenderComponent implements Component {\n\tprivate command: string | null = \"\";\n\tprivate timeout: number | undefined;\n\tprivate expanded = false;\n\tprivate cachedWidth: number | undefined;\n\tprivate cachedLines: string[] | undefined;\n\tprivate readonly config: Pick<ShellToolConfig, \"name\" | \"prompt\">;\n\n\tconstructor(config: Pick<ShellToolConfig, \"name\" | \"prompt\">) {\n\t\tthis.config = config;\n\t}\n\n\tupdate(args: { command?: string; timeout?: number } | undefined, expanded: boolean): void {\n\t\tconst command = str(args?.command);\n\t\tconst timeout = args?.timeout as number | undefined;\n\t\tif (this.command === command && this.timeout === timeout && this.expanded === expanded) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.command = command;\n\t\tthis.timeout = timeout;\n\t\tthis.expanded = expanded;\n\t\tthis.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.cachedLines === undefined || this.cachedWidth !== width) {\n\t\t\tthis.cachedLines = this.renderLines(Math.max(1, width));\n\t\t\tthis.cachedWidth = width;\n\t\t}\n\t\treturn this.cachedLines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tprivate renderLines(width: number): string[] {\n\t\tconst fullCall = formatFullShellCall(this.command, this.timeout, this.config);\n\t\tconst fullLines = new Text(fullCall, 0, 0).render(width);\n\t\tif (this.expanded || fullLines.length <= 1) return fullLines;\n\t\treturn [formatTruncatedShellCall(this.command, this.timeout, width, this.config)];\n\t}\n}\n\nconst shellTimerHooked = Symbol(\"shellTimerHooked\");\ntype ShellTimerState = BashRenderState & {\n\tinterval?: ReturnType<typeof setInterval>;\n\t[shellTimerHooked]?: true;\n};\n\n/**\n * Start the row's clock and hook disposal once. Either slot can initialize timing, since\n * extensions may replace the other slot; upstream's timer state outlives a replaced slot, but\n * its refresh interval must still end when the row is disposed mid-run.\n */\nfunction ensureShellTimer(context: ToolRenderContext<ShellTimerState>): void {\n\tstartToolTimer(context);\n\tconst state = context.state;\n\tif (state[shellTimerHooked]) return;\n\tstate[shellTimerHooked] = true;\n\tconst previousDispose = state.dispose;\n\tstate.dispose = () => {\n\t\ttry {\n\t\t\tpreviousDispose?.call(state);\n\t\t} finally {\n\t\t\tclearInterval(state.interval);\n\t\t\tstate.interval = undefined;\n\t\t}\n\t};\n}\n\nfunction formatShellPrompt(prompt: string): string {\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} `));\n}\nfunction formatShellTimeout(timeout: number | undefined): string {\n\treturn timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n}\nfunction styleShellCommand(command: string, config: Pick<ShellToolConfig, \"name\" | \"prompt\">): string {\n\tconst language = config.name === \"powershell\" ? \"powershell\" : \"bash\";\n\treturn highlightCode(command, language).join(\"\\n\");\n}\nfunction formatFullShellCall(\n\tcommand: string | null,\n\ttimeout: number | undefined,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tconst commandDisplay =\n\t\tcommand === null\n\t\t\t? invalidArgText(theme)\n\t\t\t: command\n\t\t\t\t? styleShellCommand(command, config)\n\t\t\t\t: theme.fg(\"toolOutput\", \"...\");\n\treturn formatShellPrompt(config.prompt) + commandDisplay + formatShellTimeout(timeout);\n}\nfunction fitCollapsedShellCall(\n\tbody: string,\n\ttimeout: number | undefined,\n\twidth: number,\n\tsuffix: string,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tconst prompt = formatShellPrompt(config.prompt);\n\tconst timeoutSuffix = formatShellTimeout(timeout);\n\tconst bodyWidth = Math.max(0, width - visibleWidth(prompt) - visibleWidth(suffix) - visibleWidth(timeoutSuffix));\n\tconst fittedBody = truncateToWidth(body, bodyWidth, \"\");\n\treturn truncateToWidth(prompt + fittedBody + suffix + timeoutSuffix, width, \"…\");\n}\nfunction formatTruncatedShellCall(\n\tcommand: string | null,\n\ttimeout: number | undefined,\n\twidth: number,\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): string {\n\tif (command === null) return truncateToWidth(formatFullShellCall(command, timeout, config), width, \"…\");\n\tif (!command) return formatFullShellCall(command, timeout, config);\n\tconst physicalLines = command.split(/\\r?\\n/).filter((line) => line.trim());\n\tconst firstNonEmptyLine = physicalLines[0]?.trim() ?? command.trim();\n\tconst hiddenLines = Math.max(0, physicalLines.length - 1);\n\tconst suffix =\n\t\thiddenLines > 0\n\t\t\t? theme.fg(\"muted\", ` (+${hiddenLines} line${hiddenLines === 1 ? \"\" : \"s\"})`)\n\t\t\t: theme.fg(\"muted\", \" …\");\n\treturn fitCollapsedShellCall(styleShellCommand(firstNonEmptyLine, config), timeout, width, suffix, config);\n}\nfunction rebuildBashResultRenderComponent(\n\tcomponent: Container,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\ttimerText: string | undefined,\n\tsubmittedAsBackground: boolean,\n): void {\n\tcomponent.clear();\n\n\tconst background = result.details?.background;\n\tlet output =\n\t\tbackground?.kind === \"background\"\n\t\t\t? `${submittedAsBackground ? \"Running in the background\" : \"Moved to background\"} · ${background.taskId}`\n\t\t\t: getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(\n\t\t\t\tnew VisualLinePreview({\n\t\t\t\t\ttext: styledOutput,\n\t\t\t\t\tmaxVisualLines: BASH_PREVIEW_LINES,\n\t\t\t\t\tkeep: \"end\",\n\t\t\t\t\tformatHint: (hidden) =>\n\t\t\t\t\t\ttheme.fg(\"muted\", `... (${hidden} earlier lines,`) +\n\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (timerText) component.addChild(new Text(`\\n${theme.fg(\"muted\", timerText)}`, 0, 0));\n}\n\n/** Shell renderers are shared by bash and powershell, which differ in the prompt and highlight language. */\nexport function createShellRenderers(\n\tconfig: Pick<ShellToolConfig, \"name\" | \"prompt\">,\n): Pick<ToolDefinition<any, any>, \"renderCall\" | \"renderResult\"> {\n\treturn {\n\t\trenderCall(args, _theme, context) {\n\t\t\tensureShellTimer(context);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashCallRenderComponent | undefined) ?? new BashCallRenderComponent(config);\n\t\t\tcomponent.update(args as { command?: string; timeout?: number } | undefined, context.expanded);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tensureShellTimer(context);\n\t\t\tconst timerText = toolTimerText(context, options.isPartial);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tconst submitted = (context.args as { background?: boolean } | undefined)?.background === true;\n\t\t\trebuildBashResultRenderComponent(component, result as any, options, context.showImages, timerText, submitted);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n"]}
@@ -1,5 +1,5 @@
1
- declare module "../../core/keybindings.ts" {
2
- interface AppKeybindings {
1
+ declare module "@earendil-works/pi-tui" {
2
+ interface Keybindings {
3
3
  "app.btw.close": true;
4
4
  "app.btw.cancel": true;
5
5
  "app.btw.scrollUp": true;
@@ -1 +1 @@
1
- {"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../../../src/extensions/btw/keybindings.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,2BAA2B,CAAC;IAC1C,UAAU,cAAc;QACvB,eAAe,EAAE,IAAI,CAAC;QACtB,gBAAgB,EAAE,IAAI,CAAC;QACvB,kBAAkB,EAAE,IAAI,CAAC;QACzB,oBAAoB,EAAE,IAAI,CAAC;KAC3B;CACD"}
1
+ {"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../../../src/extensions/btw/keybindings.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,wBAAwB,CAAC;IACvC,UAAU,WAAW;QACpB,eAAe,EAAE,IAAI,CAAC;QACtB,gBAAgB,EAAE,IAAI,CAAC;QACvB,kBAAkB,EAAE,IAAI,CAAC;QACzB,oBAAoB,EAAE,IAAI,CAAC;KAC3B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"keybindings.js","sourceRoot":"","sources":["../../../src/extensions/btw/keybindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAWxE,mBAAmB,CAAC;IACnB,eAAe,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IACrF,gBAAgB,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACnG,kBAAkB,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE;IAChG,oBAAoB,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,0CAA0C,EAAE;CACtG,CAAC,CAAC","sourcesContent":["import { registerKeybindings } from \"../../core/keybinding-registry.ts\";\n\ndeclare module \"../../core/keybindings.ts\" {\n\tinterface AppKeybindings {\n\t\t\"app.btw.close\": true;\n\t\t\"app.btw.cancel\": true;\n\t\t\"app.btw.scrollUp\": true;\n\t\t\"app.btw.scrollDown\": true;\n\t}\n}\n\nregisterKeybindings({\n\t\"app.btw.close\": { defaultKeys: \"escape\", description: \"Close the BTW conversation\" },\n\t\"app.btw.cancel\": { defaultKeys: \"ctrl+c\", description: \"Stop the BTW answer, or close when idle\" },\n\t\"app.btw.scrollUp\": { defaultKeys: \"up\", description: \"Scroll BTW up when the editor is empty\" },\n\t\"app.btw.scrollDown\": { defaultKeys: \"down\", description: \"Scroll BTW down when the editor is empty\" },\n});\n"]}
1
+ {"version":3,"file":"keybindings.js","sourceRoot":"","sources":["../../../src/extensions/btw/keybindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAWxE,mBAAmB,CAAC;IACnB,eAAe,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IACrF,gBAAgB,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACnG,kBAAkB,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE;IAChG,oBAAoB,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,0CAA0C,EAAE;CACtG,CAAC,CAAC","sourcesContent":["import { registerKeybindings } from \"../../core/keybinding-registry.ts\";\n\ndeclare module \"@earendil-works/pi-tui\" {\n\tinterface Keybindings {\n\t\t\"app.btw.close\": true;\n\t\t\"app.btw.cancel\": true;\n\t\t\"app.btw.scrollUp\": true;\n\t\t\"app.btw.scrollDown\": true;\n\t}\n}\n\nregisterKeybindings({\n\t\"app.btw.close\": { defaultKeys: \"escape\", description: \"Close the BTW conversation\" },\n\t\"app.btw.cancel\": { defaultKeys: \"ctrl+c\", description: \"Stop the BTW answer, or close when idle\" },\n\t\"app.btw.scrollUp\": { defaultKeys: \"up\", description: \"Scroll BTW up when the editor is empty\" },\n\t\"app.btw.scrollDown\": { defaultKeys: \"down\", description: \"Scroll BTW down when the editor is empty\" },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/extensions/codemode/execute.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAmC,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAsBtG,OAAO,KAAK,EAAE,oBAAoB,EAAiB,MAAM,gCAAgC,CAAC;AAC1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAKlE,OAAO,EAMN,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAGxB,MAAM,WAAW,CAAC;AA2LnB,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU1F;AAyKD;;;GAGG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAC9E,GAAG,EAAE,oBAAoB,GAAG,SAAS,EACrC,OAAO,GAAE,mBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC,CAkH/C"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/extensions/codemode/execute.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAmC,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAsBtG,OAAO,KAAK,EAAE,oBAAoB,EAAiB,MAAM,gCAAgC,CAAC;AAC1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAKlE,OAAO,EAMN,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAGxB,MAAM,WAAW,CAAC;AAgMnB,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU1F;AAyLD;;;GAGG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAC9E,GAAG,EAAE,oBAAoB,GAAG,SAAS,EACrC,OAAO,GAAE,mBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC,CAoH/C"}
@@ -61,9 +61,6 @@ function toModelInfo(model) {
61
61
  function withArticle(word) {
62
62
  return `${/^[aeiou]/.test(word) ? "an" : "a"} ${word}`;
63
63
  }
64
- function isRecord(value) {
65
- return typeof value === "object" && value !== null && !Array.isArray(value);
66
- }
67
64
  /** A script value in an error message: `undefined`, `a string`, `an array`, or its keys (`{ prompt }`). */
68
65
  function describeValue(value) {
69
66
  if (value === undefined || value === null)
@@ -82,52 +79,58 @@ const CLASSIFIER_CONTEXT_SHAPE = '{ state: { ... }, images?: [{ type: "image", d
82
79
  /** Check a script's classifier context, so mistakes fail with the expected shape instead of a provider error. */
83
80
  function checkClassifierContext(context) {
84
81
  const fail = (problem) => new Error(`models.classify() ${problem}. Expected context: ${CLASSIFIER_CONTEXT_SHAPE}. See "Classify" in ${CODEMODE_DOCS_PATH}.`);
85
- if (!isRecord(context))
82
+ if (typeof context !== "object" || context === null || Array.isArray(context)) {
86
83
  throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
87
- if (!isRecord(context.state))
88
- throw fail(`context.state must be an object, got ${describeValue(context.state)}`);
89
- const { images } = context;
84
+ }
85
+ const { state, images, questions } = context;
86
+ if (typeof state !== "object" || state === null || Array.isArray(state)) {
87
+ throw fail(`context.state must be an object, got ${describeValue(state)}`);
88
+ }
90
89
  if (images !== undefined) {
91
90
  if (!Array.isArray(images))
92
91
  throw fail(`context.images must be an array, got ${describeValue(images)}`);
93
92
  images.forEach((image, index) => {
94
- if (!isRecord(image) ||
95
- image.type !== "image" ||
96
- typeof image.data !== "string" ||
97
- typeof image.mimeType !== "string") {
93
+ if (image?.type !== "image" || typeof image.data !== "string" || typeof image.mimeType !== "string") {
98
94
  throw fail(`context.images[${index}] must be an image block, got ${describeValue(image)}`);
99
95
  }
100
96
  });
101
97
  }
102
- const { questions } = context;
103
- if (!isRecord(questions) || Object.keys(questions).length === 0) {
98
+ if (typeof questions !== "object" ||
99
+ questions === null ||
100
+ Array.isArray(questions) ||
101
+ Object.keys(questions).length === 0) {
104
102
  throw fail(`context.questions must map question IDs to questions, got ${describeValue(questions)}`);
105
103
  }
106
104
  const isStrings = (values) => values.length > 0 && values.every((value) => typeof value === "string");
107
105
  for (const [id, question] of Object.entries(questions)) {
108
106
  const at = `context.questions.${id}`;
109
- if (!isRecord(question))
107
+ if (typeof question !== "object" || question === null || Array.isArray(question)) {
110
108
  throw fail(`${at} must be a question object, got ${describeValue(question)}`);
111
- if (typeof question.instructions !== "string")
109
+ }
110
+ const { type, instructions, criteria } = question;
111
+ if (typeof instructions !== "string")
112
112
  throw fail(`${at}.instructions must be a string`);
113
- const { criteria } = question;
114
- if (question.type === "choice") {
115
- if (!isRecord(criteria) || !isStrings(Object.values(criteria))) {
113
+ if (type === "choice") {
114
+ if (typeof criteria !== "object" ||
115
+ criteria === null ||
116
+ Array.isArray(criteria) ||
117
+ !isStrings(Object.values(criteria))) {
116
118
  throw fail(`${at} is a "choice" question, so criteria must map each label to its meaning`);
117
119
  }
118
120
  }
119
- else if (question.type === "score") {
121
+ else if (type === "score") {
120
122
  if (!Array.isArray(criteria) || !isStrings(criteria)) {
121
123
  throw fail(`${at} is a "score" question, so criteria must list the levels as strings, lowest first`);
122
124
  }
123
125
  }
124
- else if (question.type === "bool") {
125
- if (!isRecord(criteria) || typeof criteria.true !== "string" || typeof criteria.false !== "string") {
126
+ else if (type === "bool") {
127
+ const labels = criteria;
128
+ if (typeof labels?.true !== "string" || typeof labels.false !== "string") {
126
129
  throw fail(`${at} is a "bool" question, so criteria must be { true: string, false: string }`);
127
130
  }
128
131
  }
129
132
  else {
130
- throw fail(`${at}.type must be "choice", "score", or "bool", got ${JSON.stringify(question.type)}`);
133
+ throw fail(`${at}.type must be "choice", "score", or "bool", got ${JSON.stringify(type)}`);
131
134
  }
132
135
  }
133
136
  return context;
@@ -135,21 +138,18 @@ function checkClassifierContext(context) {
135
138
  /** Check a script's image context, so mistakes such as `{ prompt }` fail with the expected shape. */
136
139
  function checkImagesContext(context) {
137
140
  const fail = (problem) => new Error(`models.generateImages() ${problem}. Expected context: { input: [{ type: "text", text: <prompt> }, ...optional { type: "image", data: <base64>, mimeType } references] }. See "Generate images" in ${CODEMODE_DOCS_PATH}.`);
138
- if (!isRecord(context))
141
+ if (typeof context !== "object" || context === null || Array.isArray(context)) {
139
142
  throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
143
+ }
140
144
  const { input } = context;
141
145
  if (!Array.isArray(input) || input.length === 0) {
142
146
  throw fail(`context.input must be a non-empty array of blocks, got ${describeValue(input)}`);
143
147
  }
144
148
  input.forEach((block, index) => {
145
- if (isRecord(block) && block.type === "text" && typeof block.text === "string")
149
+ if (block?.type === "text" && typeof block.text === "string")
146
150
  return;
147
- if (isRecord(block) &&
148
- block.type === "image" &&
149
- typeof block.data === "string" &&
150
- typeof block.mimeType === "string") {
151
+ if (block?.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string")
151
152
  return;
152
- }
153
153
  throw fail(`context.input[${index}] must be a text or image block, got ${describeValue(block)}`);
154
154
  });
155
155
  return context;
@@ -232,19 +232,36 @@ function formatOutput(output) {
232
232
  }
233
233
  return items;
234
234
  }
235
- /** Join adjacent text items into one, each part starting on its own line. */
235
+ /**
236
+ * Join adjacent text items into one, each part starting on its own line. The separator check looks
237
+ * at the last non-empty part, not the joined text: reading the end of a growing concatenation makes
238
+ * V8 copy it on every item, which is quadratic (#10678).
239
+ */
236
240
  function joinAdjacentText(items) {
237
241
  const joined = [];
242
+ let parts = [];
243
+ let lastPart = "";
244
+ const flush = () => {
245
+ if (parts.length > 0)
246
+ joined.push({ type: "text", text: parts.join("") });
247
+ parts = [];
248
+ lastPart = "";
249
+ };
238
250
  for (const item of items) {
239
- const last = joined.at(-1);
240
- if (item.type === "text" && last?.type === "text") {
241
- const separator = last.text === "" || last.text.endsWith("\n") ? "" : "\n";
242
- joined[joined.length - 1] = { type: "text", text: `${last.text}${separator}${item.text}` };
243
- }
244
- else {
251
+ if (item.type !== "text") {
252
+ flush();
245
253
  joined.push(item);
254
+ continue;
255
+ }
256
+ if (lastPart !== "" && !lastPart.endsWith("\n")) {
257
+ parts.push("\n");
258
+ lastPart = "\n";
246
259
  }
260
+ parts.push(item.text);
261
+ if (item.text !== "")
262
+ lastPart = item.text;
247
263
  }
264
+ flush();
248
265
  return joined;
249
266
  }
250
267
  function formatCallSummary(calls) {
@@ -376,6 +393,8 @@ export async function executeCodemode(toolCallId, input, signal, onUpdate, ctx,
376
393
  };
377
394
  const snapshot = () => ({ calls: calls.map((call) => ({ ...call })) });
378
395
  const publish = () => onUpdate?.({ content: [], details: snapshot() });
396
+ // Show the script as running before its first nested call, so the row's timer starts.
397
+ publish();
379
398
  const callable = ctx ? getCodemodeCallableTools(ctx.tools) : [];
380
399
  // ALL_TOOLS entries carry the declaration.
381
400
  const guidelines = options.getToolGuidelines?.();
@@ -472,14 +491,12 @@ export async function executeCodemode(toolCallId, input, signal, onUpdate, ctx,
472
491
  };
473
492
  }
474
493
  /**
475
- * Whether `query` names the namespace: its name, its script identifier (`mcp__dev-radius` is
476
- * `mcp__dev_radius`), or the part after its last `__` in either form (`dev-radius`, `dev_radius`).
494
+ * Whether `query` names the namespace: its name or one of its aliases, as written or as the same
495
+ * script identifier (`dev-radius` and `dev_radius` are both `dev_radius`).
477
496
  */
478
497
  function isNamespaceName(namespace, query) {
479
- const id = toCodemodeIdentifier(namespace);
480
498
  const queryId = toCodemodeIdentifier(query);
481
- const suffix = (name) => (name.includes("__") ? name.slice(name.lastIndexOf("__") + 2) : undefined);
482
- return namespace === query || id === queryId || suffix(namespace) === query || suffix(id) === queryId;
499
+ return [namespace.name, ...(namespace.aliases ?? [])].some((name) => name === query || toCodemodeIdentifier(name) === queryId);
483
500
  }
484
501
  /**
485
502
  * `searchTools()`, `describeTool()`, and `describeNamespace()`: ranked search and lookup over the
@@ -506,7 +523,7 @@ function createDiscoveryGlobals(tools, samples, options) {
506
523
  }
507
524
  const documents = tools.flatMap((tool) => {
508
525
  const toolNamespace = options.getToolNamespace?.(tool.name);
509
- if (namespace && (!toolNamespace || !isNamespaceName(toolNamespace.name, namespace)))
526
+ if (namespace && (!toolNamespace || !isNamespaceName(toolNamespace, namespace)))
510
527
  return [];
511
528
  return [createToolSearchDocument(tool, toolNamespace)];
512
529
  });
@@ -535,7 +552,7 @@ function createDiscoveryGlobals(tools, samples, options) {
535
552
  const names = [];
536
553
  for (const tool of tools) {
537
554
  const toolNamespace = options.getToolNamespace?.(tool.name);
538
- if (!toolNamespace || !isNamespaceName(toolNamespace.name, name))
555
+ if (!toolNamespace || !isNamespaceName(toolNamespace, name))
539
556
  continue;
540
557
  namespace ??= toolNamespace;
541
558
  names.push(toCodemodeIdentifier(tool.name));
@@ -566,14 +583,16 @@ function createModelGlobals(models, toolCallId, calls, publish, addUsage, addGen
566
583
  */
567
584
  const runModelCall = async (name, type, [model, context], checkContext, run) => {
568
585
  const listHint = `List the ${type} models you can use with models.getAvailableOfType("${type}").`;
569
- if (!isRecord(model) || typeof model.provider !== "string" || typeof model.id !== "string") {
586
+ const given = model;
587
+ if (typeof given?.provider !== "string" || typeof given.id !== "string") {
570
588
  // undefined arrives as null: spread arguments cross the sandbox as a JSON array.
571
589
  const undefinedHint = model === undefined || model === null
572
590
  ? " models.getModelOfType() returns undefined for an unknown provider or id."
573
591
  : "";
574
592
  throw new Error(`${name}() expects ${withArticle(type)} model as its first argument, got ${describeValue(model)}.${undefinedHint} ${listHint}`);
575
593
  }
576
- const { provider, id } = model;
594
+ const provider = given.provider;
595
+ const id = given.id;
577
596
  const ref = `${provider}/${id}`;
578
597
  const resolved = models.getModelOfType(type, provider, id);
579
598
  if (!resolved) {