@humain/terminal 0.0.2 → 0.0.3-beta.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 (680) hide show
  1. package/CHANGELOG.md +241 -24
  2. package/README.md +143 -119
  3. package/dist/cli/args.d.ts +4 -2
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +37 -39
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-check.d.ts +19 -0
  8. package/dist/cli/auth-check.d.ts.map +1 -0
  9. package/dist/cli/auth-check.js +50 -0
  10. package/dist/cli/auth-check.js.map +1 -0
  11. package/dist/cli/auth-command.d.ts +24 -0
  12. package/dist/cli/auth-command.d.ts.map +1 -0
  13. package/dist/cli/auth-command.js +103 -0
  14. package/dist/cli/auth-command.js.map +1 -0
  15. package/dist/cli/config-selector.d.ts.map +1 -1
  16. package/dist/cli/config-selector.js +2 -2
  17. package/dist/cli/config-selector.js.map +1 -1
  18. package/dist/cli/credential-print.d.ts +5 -15
  19. package/dist/cli/credential-print.d.ts.map +1 -1
  20. package/dist/cli/credential-print.js +42 -83
  21. package/dist/cli/credential-print.js.map +1 -1
  22. package/dist/cli/experimental/auth.d.ts +16 -0
  23. package/dist/cli/experimental/auth.d.ts.map +1 -0
  24. package/dist/cli/experimental/auth.js +13 -0
  25. package/dist/cli/experimental/auth.js.map +1 -0
  26. package/dist/cli/experimental/cli.d.ts +6 -0
  27. package/dist/cli/experimental/cli.d.ts.map +1 -0
  28. package/dist/cli/experimental/cli.js +5 -0
  29. package/dist/cli/experimental/cli.js.map +1 -0
  30. package/dist/cli/experimental/command-options.d.ts +17 -0
  31. package/dist/cli/experimental/command-options.d.ts.map +1 -0
  32. package/dist/cli/experimental/command-options.js +35 -0
  33. package/dist/cli/experimental/command-options.js.map +1 -0
  34. package/dist/cli/experimental/command.d.ts +63 -0
  35. package/dist/cli/experimental/command.d.ts.map +1 -0
  36. package/dist/cli/experimental/command.js +130 -0
  37. package/dist/cli/experimental/command.js.map +1 -0
  38. package/dist/cli/experimental/commands/client.d.ts +13 -0
  39. package/dist/cli/experimental/commands/client.d.ts.map +1 -0
  40. package/dist/cli/experimental/commands/client.js +25 -0
  41. package/dist/cli/experimental/commands/client.js.map +1 -0
  42. package/dist/cli/experimental/commands/pi.d.ts +15 -0
  43. package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
  44. package/dist/cli/experimental/commands/pi.js +28 -0
  45. package/dist/cli/experimental/commands/pi.js.map +1 -0
  46. package/dist/cli/experimental/commands/server.d.ts +13 -0
  47. package/dist/cli/experimental/commands/server.d.ts.map +1 -0
  48. package/dist/cli/experimental/commands/server.js +25 -0
  49. package/dist/cli/experimental/commands/server.js.map +1 -0
  50. package/dist/cli/experimental/transport-address.d.ts +10 -0
  51. package/dist/cli/experimental/transport-address.d.ts.map +1 -0
  52. package/dist/cli/experimental/transport-address.js +38 -0
  53. package/dist/cli/experimental/transport-address.js.map +1 -0
  54. package/dist/cli/list-models.d.ts +1 -1
  55. package/dist/cli/list-models.d.ts.map +1 -1
  56. package/dist/cli/list-models.js +2 -2
  57. package/dist/cli/list-models.js.map +1 -1
  58. package/dist/cli/startup-ui.d.ts +1 -1
  59. package/dist/cli/startup-ui.d.ts.map +1 -1
  60. package/dist/cli/startup-ui.js +2 -2
  61. package/dist/cli/startup-ui.js.map +1 -1
  62. package/dist/client/index.d.ts +3 -0
  63. package/dist/client/index.d.ts.map +1 -0
  64. package/dist/client/index.js +3 -0
  65. package/dist/client/index.js.map +1 -0
  66. package/dist/client/remote-session.d.ts +53 -0
  67. package/dist/client/remote-session.d.ts.map +1 -0
  68. package/dist/client/remote-session.js +340 -0
  69. package/dist/client/remote-session.js.map +1 -0
  70. package/dist/client/transcript.d.ts +12 -0
  71. package/dist/client/transcript.d.ts.map +1 -0
  72. package/dist/client/transcript.js +98 -0
  73. package/dist/client/transcript.js.map +1 -0
  74. package/dist/core/agent-session-services.d.ts +1 -0
  75. package/dist/core/agent-session-services.d.ts.map +1 -1
  76. package/dist/core/agent-session-services.js +1 -0
  77. package/dist/core/agent-session-services.js.map +1 -1
  78. package/dist/core/agent-session.d.ts +1 -10
  79. package/dist/core/agent-session.d.ts.map +1 -1
  80. package/dist/core/agent-session.js +72 -70
  81. package/dist/core/agent-session.js.map +1 -1
  82. package/dist/core/auth-storage.d.ts +25 -9
  83. package/dist/core/auth-storage.d.ts.map +1 -1
  84. package/dist/core/auth-storage.js +237 -39
  85. package/dist/core/auth-storage.js.map +1 -1
  86. package/dist/core/extensions/index.d.ts +1 -1
  87. package/dist/core/extensions/index.d.ts.map +1 -1
  88. package/dist/core/extensions/index.js.map +1 -1
  89. package/dist/core/extensions/loader.d.ts.map +1 -1
  90. package/dist/core/extensions/loader.js +60 -41
  91. package/dist/core/extensions/loader.js.map +1 -1
  92. package/dist/core/extensions/runner.d.ts +2 -1
  93. package/dist/core/extensions/runner.d.ts.map +1 -1
  94. package/dist/core/extensions/runner.js +3 -0
  95. package/dist/core/extensions/runner.js.map +1 -1
  96. package/dist/core/extensions/types.d.ts +27 -4
  97. package/dist/core/extensions/types.d.ts.map +1 -1
  98. package/dist/core/extensions/types.js.map +1 -1
  99. package/dist/core/http-dispatcher.d.ts.map +1 -1
  100. package/dist/core/http-dispatcher.js +5 -0
  101. package/dist/core/http-dispatcher.js.map +1 -1
  102. package/dist/core/keybindings.d.ts +45 -5
  103. package/dist/core/keybindings.d.ts.map +1 -1
  104. package/dist/core/keybindings.js.map +1 -1
  105. package/dist/core/model-config.d.ts +29 -5
  106. package/dist/core/model-config.d.ts.map +1 -1
  107. package/dist/core/model-config.js +4 -0
  108. package/dist/core/model-config.js.map +1 -1
  109. package/dist/core/model-registry.d.ts +5 -3
  110. package/dist/core/model-registry.d.ts.map +1 -1
  111. package/dist/core/model-registry.js +13 -10
  112. package/dist/core/model-registry.js.map +1 -1
  113. package/dist/core/model-resolver.d.ts +4 -3
  114. package/dist/core/model-resolver.d.ts.map +1 -1
  115. package/dist/core/model-resolver.js +42 -10
  116. package/dist/core/model-resolver.js.map +1 -1
  117. package/dist/core/model-runtime.d.ts +30 -13
  118. package/dist/core/model-runtime.d.ts.map +1 -1
  119. package/dist/core/model-runtime.js +233 -73
  120. package/dist/core/model-runtime.js.map +1 -1
  121. package/dist/core/models-store.d.ts +12 -7
  122. package/dist/core/models-store.d.ts.map +1 -1
  123. package/dist/core/models-store.js +83 -14
  124. package/dist/core/models-store.js.map +1 -1
  125. package/dist/core/package-manager.d.ts +6 -3
  126. package/dist/core/package-manager.d.ts.map +1 -1
  127. package/dist/core/package-manager.js +80 -61
  128. package/dist/core/package-manager.js.map +1 -1
  129. package/dist/core/pi-manifest.d.ts +8 -0
  130. package/dist/core/pi-manifest.d.ts.map +1 -0
  131. package/dist/core/pi-manifest.js +28 -0
  132. package/dist/core/pi-manifest.js.map +1 -0
  133. package/dist/core/provider-composer.d.ts +4 -1
  134. package/dist/core/provider-composer.d.ts.map +1 -1
  135. package/dist/core/provider-composer.js +36 -15
  136. package/dist/core/provider-composer.js.map +1 -1
  137. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  138. package/dist/core/remote-catalog-provider.js +73 -67
  139. package/dist/core/remote-catalog-provider.js.map +1 -1
  140. package/dist/core/resource-loader.d.ts.map +1 -1
  141. package/dist/core/resource-loader.js +2 -2
  142. package/dist/core/resource-loader.js.map +1 -1
  143. package/dist/core/runtime-credentials.d.ts +5 -5
  144. package/dist/core/runtime-credentials.d.ts.map +1 -1
  145. package/dist/core/runtime-credentials.js +11 -8
  146. package/dist/core/runtime-credentials.js.map +1 -1
  147. package/dist/core/sdk.d.ts +1 -1
  148. package/dist/core/sdk.d.ts.map +1 -1
  149. package/dist/core/sdk.js +2 -2
  150. package/dist/core/sdk.js.map +1 -1
  151. package/dist/core/session-manager.d.ts.map +1 -1
  152. package/dist/core/session-manager.js +3 -1
  153. package/dist/core/session-manager.js.map +1 -1
  154. package/dist/core/settings-manager.d.ts +12 -0
  155. package/dist/core/settings-manager.d.ts.map +1 -1
  156. package/dist/core/settings-manager.js +40 -16
  157. package/dist/core/settings-manager.js.map +1 -1
  158. package/dist/core/system-prompt.d.ts.map +1 -1
  159. package/dist/core/system-prompt.js +8 -0
  160. package/dist/core/system-prompt.js.map +1 -1
  161. package/dist/core/tools/bash.d.ts +5 -1
  162. package/dist/core/tools/bash.d.ts.map +1 -1
  163. package/dist/core/tools/bash.js +16 -15
  164. package/dist/core/tools/bash.js.map +1 -1
  165. package/dist/core/tools/edit.d.ts +4 -0
  166. package/dist/core/tools/edit.d.ts.map +1 -1
  167. package/dist/core/tools/edit.js +11 -7
  168. package/dist/core/tools/edit.js.map +1 -1
  169. package/dist/core/tools/find.d.ts +7 -0
  170. package/dist/core/tools/find.d.ts.map +1 -1
  171. package/dist/core/tools/find.js +16 -19
  172. package/dist/core/tools/find.js.map +1 -1
  173. package/dist/core/tools/grep.d.ts +4 -0
  174. package/dist/core/tools/grep.d.ts.map +1 -1
  175. package/dist/core/tools/grep.js +5 -1
  176. package/dist/core/tools/grep.js.map +1 -1
  177. package/dist/core/tools/ls.d.ts +4 -0
  178. package/dist/core/tools/ls.d.ts.map +1 -1
  179. package/dist/core/tools/ls.js +5 -1
  180. package/dist/core/tools/ls.js.map +1 -1
  181. package/dist/core/tools/read.d.ts +4 -0
  182. package/dist/core/tools/read.d.ts.map +1 -1
  183. package/dist/core/tools/read.js +7 -3
  184. package/dist/core/tools/read.js.map +1 -1
  185. package/dist/core/tools/write.d.ts +4 -0
  186. package/dist/core/tools/write.d.ts.map +1 -1
  187. package/dist/core/tools/write.js +6 -2
  188. package/dist/core/tools/write.js.map +1 -1
  189. package/dist/extensions/llama/index.d.ts.map +1 -1
  190. package/dist/extensions/llama/index.js +11 -2
  191. package/dist/extensions/llama/index.js.map +1 -1
  192. package/dist/extensions/llama/provider.d.ts.map +1 -1
  193. package/dist/extensions/llama/provider.js +21 -7
  194. package/dist/extensions/llama/provider.js.map +1 -1
  195. package/dist/humain/agent-execution-protocol.d.ts +218 -0
  196. package/dist/humain/agent-execution-protocol.d.ts.map +1 -0
  197. package/dist/humain/agent-execution-protocol.js +204 -0
  198. package/dist/humain/agent-execution-protocol.js.map +1 -0
  199. package/dist/humain/agent-execution-runtime.d.ts +35 -0
  200. package/dist/humain/agent-execution-runtime.d.ts.map +1 -0
  201. package/dist/humain/agent-execution-runtime.js +170 -0
  202. package/dist/humain/agent-execution-runtime.js.map +1 -0
  203. package/dist/humain/index.d.ts.map +1 -1
  204. package/dist/humain/index.js +8 -7
  205. package/dist/humain/index.js.map +1 -1
  206. package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +660 -0
  207. package/dist/humain/mcp-adapter/vendor/LICENSE +21 -0
  208. package/dist/humain/mcp-adapter/vendor/README.md +733 -0
  209. package/dist/humain/mcp-adapter/vendor/abort.ts +34 -0
  210. package/dist/humain/mcp-adapter/vendor/agent-dir.ts +60 -0
  211. package/dist/humain/mcp-adapter/vendor/agent-plugin-loader.ts +377 -0
  212. package/dist/humain/mcp-adapter/vendor/app-bridge.bundle.js +67 -0
  213. package/dist/humain/mcp-adapter/vendor/banner.png +0 -0
  214. package/dist/humain/mcp-adapter/vendor/cli.js +209 -0
  215. package/dist/humain/mcp-adapter/vendor/commands.ts +627 -0
  216. package/dist/humain/mcp-adapter/vendor/config.ts +1226 -0
  217. package/dist/humain/mcp-adapter/vendor/consent-manager.ts +64 -0
  218. package/dist/humain/mcp-adapter/vendor/direct-tools.ts +604 -0
  219. package/dist/humain/mcp-adapter/vendor/elicitation-handler.ts +348 -0
  220. package/dist/humain/mcp-adapter/vendor/error-signal.ts +21 -0
  221. package/dist/humain/mcp-adapter/vendor/errors.ts +223 -0
  222. package/dist/humain/mcp-adapter/vendor/glimpse-ui.ts +80 -0
  223. package/dist/humain/mcp-adapter/vendor/host-html-template.ts +461 -0
  224. package/dist/humain/mcp-adapter/vendor/index.bundle.d.mts +4 -0
  225. package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +15923 -0
  226. package/dist/humain/mcp-adapter/vendor/index.ts +898 -0
  227. package/dist/humain/mcp-adapter/vendor/init.ts +649 -0
  228. package/dist/humain/mcp-adapter/vendor/json-schema-validator.ts +69 -0
  229. package/dist/humain/mcp-adapter/vendor/lifecycle.ts +147 -0
  230. package/dist/humain/mcp-adapter/vendor/logger.ts +169 -0
  231. package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +993 -0
  232. package/dist/humain/mcp-adapter/vendor/mcp-auth.ts +1000 -0
  233. package/dist/humain/mcp-adapter/vendor/mcp-callback-server.ts +510 -0
  234. package/dist/humain/mcp-adapter/vendor/mcp-code.ts +354 -0
  235. package/dist/humain/mcp-adapter/vendor/mcp-keyring-helper.cjs +89 -0
  236. package/dist/humain/mcp-adapter/vendor/mcp-oauth-provider.ts +616 -0
  237. package/dist/humain/mcp-adapter/vendor/mcp-output-guard.ts +408 -0
  238. package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +1015 -0
  239. package/dist/humain/mcp-adapter/vendor/mcp-probe.ts +187 -0
  240. package/dist/humain/mcp-adapter/vendor/mcp-script-worker.mjs +130 -0
  241. package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +666 -0
  242. package/dist/humain/mcp-adapter/vendor/mcp-status.ts +109 -0
  243. package/dist/humain/mcp-adapter/vendor/mcp-trace.ts +306 -0
  244. package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +353 -0
  245. package/dist/humain/mcp-adapter/vendor/npx-resolver.ts +543 -0
  246. package/dist/humain/mcp-adapter/vendor/oauth-handler.ts +30 -0
  247. package/dist/humain/mcp-adapter/vendor/oauth.ts +48 -0
  248. package/dist/humain/mcp-adapter/vendor/onboarding-state.ts +76 -0
  249. package/dist/humain/mcp-adapter/vendor/package.json +178 -0
  250. package/dist/humain/mcp-adapter/vendor/panel-keys.ts +53 -0
  251. package/dist/humain/mcp-adapter/vendor/prompts.ts +353 -0
  252. package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +1331 -0
  253. package/dist/humain/mcp-adapter/vendor/resource-tools.ts +17 -0
  254. package/dist/humain/mcp-adapter/vendor/runtime-owner.ts +91 -0
  255. package/dist/humain/mcp-adapter/vendor/sampling-handler.ts +284 -0
  256. package/dist/humain/mcp-adapter/vendor/search-ranking.ts +206 -0
  257. package/dist/humain/mcp-adapter/vendor/server-manager.ts +1124 -0
  258. package/dist/humain/mcp-adapter/vendor/session-recovery.ts +151 -0
  259. package/dist/humain/mcp-adapter/vendor/skills/mcp-scripting/SKILL.md +38 -0
  260. package/dist/humain/mcp-adapter/vendor/state.ts +62 -0
  261. package/dist/humain/mcp-adapter/vendor/tool-approval.ts +182 -0
  262. package/dist/humain/mcp-adapter/vendor/tool-metadata.ts +276 -0
  263. package/dist/humain/mcp-adapter/vendor/tool-registrar.ts +253 -0
  264. package/dist/humain/mcp-adapter/vendor/tool-result-renderer.ts +423 -0
  265. package/dist/humain/mcp-adapter/vendor/ts-shape.ts +149 -0
  266. package/dist/humain/mcp-adapter/vendor/types.ts +859 -0
  267. package/dist/humain/mcp-adapter/vendor/ui-app-bridge-helpers.ts +40 -0
  268. package/dist/humain/mcp-adapter/vendor/ui-resource-handler.ts +250 -0
  269. package/dist/humain/mcp-adapter/vendor/ui-server.ts +866 -0
  270. package/dist/humain/mcp-adapter/vendor/ui-session.ts +570 -0
  271. package/dist/humain/mcp-adapter/vendor/ui-stream-types.ts +89 -0
  272. package/dist/humain/mcp-adapter/vendor/ui-tool-visibility.ts +25 -0
  273. package/dist/humain/mcp-adapter/vendor/unix-socket-transport.ts +85 -0
  274. package/dist/humain/mcp-adapter/vendor/utils.ts +310 -0
  275. package/dist/humain/mcp-adapter.d.ts +3 -0
  276. package/dist/humain/mcp-adapter.d.ts.map +1 -0
  277. package/dist/humain/mcp-adapter.js +5 -0
  278. package/dist/humain/mcp-adapter.js.map +1 -0
  279. package/dist/humain/pi-agent-execution-host.d.ts +34 -0
  280. package/dist/humain/pi-agent-execution-host.d.ts.map +1 -0
  281. package/dist/humain/pi-agent-execution-host.js +77 -0
  282. package/dist/humain/pi-agent-execution-host.js.map +1 -0
  283. package/dist/humain/web-access/host.d.ts +4 -0
  284. package/dist/humain/web-access/host.d.ts.map +1 -0
  285. package/dist/humain/web-access/host.js +3 -0
  286. package/dist/humain/web-access/host.js.map +1 -0
  287. package/dist/humain/web-access/index.d.ts +3 -0
  288. package/dist/humain/web-access/index.d.ts.map +1 -0
  289. package/dist/humain/web-access/index.js +5 -0
  290. package/dist/humain/web-access/index.js.map +1 -0
  291. package/dist/humain/web-access/vendor/activity.d.ts +34 -0
  292. package/dist/humain/web-access/vendor/activity.d.ts.map +1 -0
  293. package/dist/humain/web-access/vendor/activity.js +68 -0
  294. package/dist/humain/web-access/vendor/activity.js.map +1 -0
  295. package/dist/humain/web-access/vendor/anysearch.d.ts +8 -0
  296. package/dist/humain/web-access/vendor/anysearch.d.ts.map +1 -0
  297. package/dist/humain/web-access/vendor/anysearch.js +167 -0
  298. package/dist/humain/web-access/vendor/anysearch.js.map +1 -0
  299. package/dist/humain/web-access/vendor/bocha.d.ts +4 -0
  300. package/dist/humain/web-access/vendor/bocha.d.ts.map +1 -0
  301. package/dist/humain/web-access/vendor/bocha.js +219 -0
  302. package/dist/humain/web-access/vendor/bocha.js.map +1 -0
  303. package/dist/humain/web-access/vendor/brave.d.ts +4 -0
  304. package/dist/humain/web-access/vendor/brave.d.ts.map +1 -0
  305. package/dist/humain/web-access/vendor/brave.js +194 -0
  306. package/dist/humain/web-access/vendor/brave.js.map +1 -0
  307. package/dist/humain/web-access/vendor/brightdata-unlocker.d.ts +12 -0
  308. package/dist/humain/web-access/vendor/brightdata-unlocker.d.ts.map +1 -0
  309. package/dist/humain/web-access/vendor/brightdata-unlocker.js +230 -0
  310. package/dist/humain/web-access/vendor/brightdata-unlocker.js.map +1 -0
  311. package/dist/humain/web-access/vendor/brightdata.d.ts +8 -0
  312. package/dist/humain/web-access/vendor/brightdata.d.ts.map +1 -0
  313. package/dist/humain/web-access/vendor/brightdata.js +519 -0
  314. package/dist/humain/web-access/vendor/brightdata.js.map +1 -0
  315. package/dist/humain/web-access/vendor/chrome-cookies.d.ts +10 -0
  316. package/dist/humain/web-access/vendor/chrome-cookies.d.ts.map +1 -0
  317. package/dist/humain/web-access/vendor/chrome-cookies.js +438 -0
  318. package/dist/humain/web-access/vendor/chrome-cookies.js.map +1 -0
  319. package/dist/humain/web-access/vendor/content-find.d.ts +11 -0
  320. package/dist/humain/web-access/vendor/content-find.d.ts.map +1 -0
  321. package/dist/humain/web-access/vendor/content-find.js +117 -0
  322. package/dist/humain/web-access/vendor/content-find.js.map +1 -0
  323. package/dist/humain/web-access/vendor/credential-source.d.ts +28 -0
  324. package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -0
  325. package/dist/humain/web-access/vendor/credential-source.js +151 -0
  326. package/dist/humain/web-access/vendor/credential-source.js.map +1 -0
  327. package/dist/humain/web-access/vendor/curator-page.d.ts +29 -0
  328. package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -0
  329. package/dist/humain/web-access/vendor/curator-page.js +3554 -0
  330. package/dist/humain/web-access/vendor/curator-page.js.map +1 -0
  331. package/dist/humain/web-access/vendor/curator-server.d.ts +93 -0
  332. package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -0
  333. package/dist/humain/web-access/vendor/curator-server.js +688 -0
  334. package/dist/humain/web-access/vendor/curator-server.js.map +1 -0
  335. package/dist/humain/web-access/vendor/datalab-pdf-extract.d.ts +60 -0
  336. package/dist/humain/web-access/vendor/datalab-pdf-extract.d.ts.map +1 -0
  337. package/dist/humain/web-access/vendor/datalab-pdf-extract.js +416 -0
  338. package/dist/humain/web-access/vendor/datalab-pdf-extract.js.map +1 -0
  339. package/dist/humain/web-access/vendor/declared-web-links.d.ts +16 -0
  340. package/dist/humain/web-access/vendor/declared-web-links.d.ts.map +1 -0
  341. package/dist/humain/web-access/vendor/declared-web-links.js +168 -0
  342. package/dist/humain/web-access/vendor/declared-web-links.js.map +1 -0
  343. package/dist/humain/web-access/vendor/duckduckgo.d.ts +4 -0
  344. package/dist/humain/web-access/vendor/duckduckgo.d.ts.map +1 -0
  345. package/dist/humain/web-access/vendor/duckduckgo.js +123 -0
  346. package/dist/humain/web-access/vendor/duckduckgo.js.map +1 -0
  347. package/dist/humain/web-access/vendor/exa.d.ts +10 -0
  348. package/dist/humain/web-access/vendor/exa.d.ts.map +1 -0
  349. package/dist/humain/web-access/vendor/exa.js +426 -0
  350. package/dist/humain/web-access/vendor/exa.js.map +1 -0
  351. package/dist/humain/web-access/vendor/extract.d.ts +46 -0
  352. package/dist/humain/web-access/vendor/extract.d.ts.map +1 -0
  353. package/dist/humain/web-access/vendor/extract.js +1064 -0
  354. package/dist/humain/web-access/vendor/extract.js.map +1 -0
  355. package/dist/humain/web-access/vendor/feature-config.d.ts +3 -0
  356. package/dist/humain/web-access/vendor/feature-config.d.ts.map +1 -0
  357. package/dist/humain/web-access/vendor/feature-config.js +27 -0
  358. package/dist/humain/web-access/vendor/feature-config.js.map +1 -0
  359. package/dist/humain/web-access/vendor/fetch-params.d.ts +25 -0
  360. package/dist/humain/web-access/vendor/fetch-params.d.ts.map +1 -0
  361. package/dist/humain/web-access/vendor/fetch-params.js +57 -0
  362. package/dist/humain/web-access/vendor/fetch-params.js.map +1 -0
  363. package/dist/humain/web-access/vendor/firecrawl.d.ts +12 -0
  364. package/dist/humain/web-access/vendor/firecrawl.d.ts.map +1 -0
  365. package/dist/humain/web-access/vendor/firecrawl.js +239 -0
  366. package/dist/humain/web-access/vendor/firecrawl.js.map +1 -0
  367. package/dist/humain/web-access/vendor/gemini-api.d.ts +27 -0
  368. package/dist/humain/web-access/vendor/gemini-api.d.ts.map +1 -0
  369. package/dist/humain/web-access/vendor/gemini-api.js +230 -0
  370. package/dist/humain/web-access/vendor/gemini-api.js.map +1 -0
  371. package/dist/humain/web-access/vendor/gemini-pdf-extract.d.ts +9 -0
  372. package/dist/humain/web-access/vendor/gemini-pdf-extract.d.ts.map +1 -0
  373. package/dist/humain/web-access/vendor/gemini-pdf-extract.js +85 -0
  374. package/dist/humain/web-access/vendor/gemini-pdf-extract.js.map +1 -0
  375. package/dist/humain/web-access/vendor/gemini-search.d.ts +40 -0
  376. package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -0
  377. package/dist/humain/web-access/vendor/gemini-search.js +814 -0
  378. package/dist/humain/web-access/vendor/gemini-search.js.map +1 -0
  379. package/dist/humain/web-access/vendor/gemini-url-context.d.ts +4 -0
  380. package/dist/humain/web-access/vendor/gemini-url-context.d.ts.map +1 -0
  381. package/dist/humain/web-access/vendor/gemini-url-context.js +101 -0
  382. package/dist/humain/web-access/vendor/gemini-url-context.js.map +1 -0
  383. package/dist/humain/web-access/vendor/gemini-web-config.d.ts +4 -0
  384. package/dist/humain/web-access/vendor/gemini-web-config.d.ts.map +1 -0
  385. package/dist/humain/web-access/vendor/gemini-web-config.js +42 -0
  386. package/dist/humain/web-access/vendor/gemini-web-config.js.map +1 -0
  387. package/dist/humain/web-access/vendor/gemini-web.d.ts +18 -0
  388. package/dist/humain/web-access/vendor/gemini-web.d.ts.map +1 -0
  389. package/dist/humain/web-access/vendor/gemini-web.js +385 -0
  390. package/dist/humain/web-access/vendor/gemini-web.js.map +1 -0
  391. package/dist/humain/web-access/vendor/github-api.d.ts +7 -0
  392. package/dist/humain/web-access/vendor/github-api.d.ts.map +1 -0
  393. package/dist/humain/web-access/vendor/github-api.js +160 -0
  394. package/dist/humain/web-access/vendor/github-api.js.map +1 -0
  395. package/dist/humain/web-access/vendor/github-extract.d.ts +13 -0
  396. package/dist/humain/web-access/vendor/github-extract.d.ts.map +1 -0
  397. package/dist/humain/web-access/vendor/github-extract.js +635 -0
  398. package/dist/humain/web-access/vendor/github-extract.js.map +1 -0
  399. package/dist/humain/web-access/vendor/index.d.ts +4 -0
  400. package/dist/humain/web-access/vendor/index.d.ts.map +1 -0
  401. package/dist/humain/web-access/vendor/index.js +3002 -0
  402. package/dist/humain/web-access/vendor/index.js.map +1 -0
  403. package/dist/humain/web-access/vendor/jina-search.d.ts +8 -0
  404. package/dist/humain/web-access/vendor/jina-search.d.ts.map +1 -0
  405. package/dist/humain/web-access/vendor/jina-search.js +255 -0
  406. package/dist/humain/web-access/vendor/jina-search.js.map +1 -0
  407. package/dist/humain/web-access/vendor/kagi.d.ts +15 -0
  408. package/dist/humain/web-access/vendor/kagi.d.ts.map +1 -0
  409. package/dist/humain/web-access/vendor/kagi.js +262 -0
  410. package/dist/humain/web-access/vendor/kagi.js.map +1 -0
  411. package/dist/humain/web-access/vendor/ollama.d.ts +15 -0
  412. package/dist/humain/web-access/vendor/ollama.d.ts.map +1 -0
  413. package/dist/humain/web-access/vendor/ollama.js +216 -0
  414. package/dist/humain/web-access/vendor/ollama.js.map +1 -0
  415. package/dist/humain/web-access/vendor/openai-search.d.ts +15 -0
  416. package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -0
  417. package/dist/humain/web-access/vendor/openai-search.js +459 -0
  418. package/dist/humain/web-access/vendor/openai-search.js.map +1 -0
  419. package/dist/humain/web-access/vendor/page-query.d.ts +16 -0
  420. package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -0
  421. package/dist/humain/web-access/vendor/page-query.js +79 -0
  422. package/dist/humain/web-access/vendor/page-query.js.map +1 -0
  423. package/dist/humain/web-access/vendor/parallel.d.ts +12 -0
  424. package/dist/humain/web-access/vendor/parallel.d.ts.map +1 -0
  425. package/dist/humain/web-access/vendor/parallel.js +343 -0
  426. package/dist/humain/web-access/vendor/parallel.js.map +1 -0
  427. package/dist/humain/web-access/vendor/pdf-extract.d.ts +44 -0
  428. package/dist/humain/web-access/vendor/pdf-extract.d.ts.map +1 -0
  429. package/dist/humain/web-access/vendor/pdf-extract.js +298 -0
  430. package/dist/humain/web-access/vendor/pdf-extract.js.map +1 -0
  431. package/dist/humain/web-access/vendor/perplexity.d.ts +20 -0
  432. package/dist/humain/web-access/vendor/perplexity.d.ts.map +1 -0
  433. package/dist/humain/web-access/vendor/perplexity.js +159 -0
  434. package/dist/humain/web-access/vendor/perplexity.js.map +1 -0
  435. package/dist/humain/web-access/vendor/querit.d.ts +10 -0
  436. package/dist/humain/web-access/vendor/querit.d.ts.map +1 -0
  437. package/dist/humain/web-access/vendor/querit.js +335 -0
  438. package/dist/humain/web-access/vendor/querit.js.map +1 -0
  439. package/dist/humain/web-access/vendor/render-search-error.d.ts +57 -0
  440. package/dist/humain/web-access/vendor/render-search-error.d.ts.map +1 -0
  441. package/dist/humain/web-access/vendor/render-search-error.js +130 -0
  442. package/dist/humain/web-access/vendor/render-search-error.js.map +1 -0
  443. package/dist/humain/web-access/vendor/rsc-extract.d.ts +12 -0
  444. package/dist/humain/web-access/vendor/rsc-extract.d.ts.map +1 -0
  445. package/dist/humain/web-access/vendor/rsc-extract.js +329 -0
  446. package/dist/humain/web-access/vendor/rsc-extract.js.map +1 -0
  447. package/dist/humain/web-access/vendor/search1api.d.ts +10 -0
  448. package/dist/humain/web-access/vendor/search1api.d.ts.map +1 -0
  449. package/dist/humain/web-access/vendor/search1api.js +251 -0
  450. package/dist/humain/web-access/vendor/search1api.js.map +1 -0
  451. package/dist/humain/web-access/vendor/searchinfinity.d.ts +8 -0
  452. package/dist/humain/web-access/vendor/searchinfinity.d.ts.map +1 -0
  453. package/dist/humain/web-access/vendor/searchinfinity.js +229 -0
  454. package/dist/humain/web-access/vendor/searchinfinity.js.map +1 -0
  455. package/dist/humain/web-access/vendor/searxng.d.ts +4 -0
  456. package/dist/humain/web-access/vendor/searxng.d.ts.map +1 -0
  457. package/dist/humain/web-access/vendor/searxng.js +238 -0
  458. package/dist/humain/web-access/vendor/searxng.js.map +1 -0
  459. package/dist/humain/web-access/vendor/serpbase.d.ts +4 -0
  460. package/dist/humain/web-access/vendor/serpbase.d.ts.map +1 -0
  461. package/dist/humain/web-access/vendor/serpbase.js +211 -0
  462. package/dist/humain/web-access/vendor/serpbase.js.map +1 -0
  463. package/dist/humain/web-access/vendor/serpdive.d.ts +8 -0
  464. package/dist/humain/web-access/vendor/serpdive.d.ts.map +1 -0
  465. package/dist/humain/web-access/vendor/serpdive.js +267 -0
  466. package/dist/humain/web-access/vendor/serpdive.js.map +1 -0
  467. package/dist/humain/web-access/vendor/source-check.d.ts +99 -0
  468. package/dist/humain/web-access/vendor/source-check.d.ts.map +1 -0
  469. package/dist/humain/web-access/vendor/source-check.js +205 -0
  470. package/dist/humain/web-access/vendor/source-check.js.map +1 -0
  471. package/dist/humain/web-access/vendor/ssrf-protection.d.ts +50 -0
  472. package/dist/humain/web-access/vendor/ssrf-protection.d.ts.map +1 -0
  473. package/dist/humain/web-access/vendor/ssrf-protection.js +466 -0
  474. package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -0
  475. package/dist/humain/web-access/vendor/storage.d.ts +55 -0
  476. package/dist/humain/web-access/vendor/storage.d.ts.map +1 -0
  477. package/dist/humain/web-access/vendor/storage.js +485 -0
  478. package/dist/humain/web-access/vendor/storage.js.map +1 -0
  479. package/dist/humain/web-access/vendor/summary-model-scope.d.ts +25 -0
  480. package/dist/humain/web-access/vendor/summary-model-scope.d.ts.map +1 -0
  481. package/dist/humain/web-access/vendor/summary-model-scope.js +98 -0
  482. package/dist/humain/web-access/vendor/summary-model-scope.js.map +1 -0
  483. package/dist/humain/web-access/vendor/summary-review.d.ts +24 -0
  484. package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -0
  485. package/dist/humain/web-access/vendor/summary-review.js +341 -0
  486. package/dist/humain/web-access/vendor/summary-review.js.map +1 -0
  487. package/dist/humain/web-access/vendor/tavily.d.ts +8 -0
  488. package/dist/humain/web-access/vendor/tavily.d.ts.map +1 -0
  489. package/dist/humain/web-access/vendor/tavily.js +194 -0
  490. package/dist/humain/web-access/vendor/tavily.js.map +1 -0
  491. package/dist/humain/web-access/vendor/tinyfish.d.ts +11 -0
  492. package/dist/humain/web-access/vendor/tinyfish.d.ts.map +1 -0
  493. package/dist/humain/web-access/vendor/tinyfish.js +331 -0
  494. package/dist/humain/web-access/vendor/tinyfish.js.map +1 -0
  495. package/dist/humain/web-access/vendor/utils.d.ts +20 -0
  496. package/dist/humain/web-access/vendor/utils.d.ts.map +1 -0
  497. package/dist/humain/web-access/vendor/utils.js +91 -0
  498. package/dist/humain/web-access/vendor/utils.js.map +1 -0
  499. package/dist/humain/web-access/vendor/video-extract.d.ts +16 -0
  500. package/dist/humain/web-access/vendor/video-extract.d.ts.map +1 -0
  501. package/dist/humain/web-access/vendor/video-extract.js +335 -0
  502. package/dist/humain/web-access/vendor/video-extract.js.map +1 -0
  503. package/dist/humain/web-access/vendor/xai-search.d.ts +13 -0
  504. package/dist/humain/web-access/vendor/xai-search.d.ts.map +1 -0
  505. package/dist/humain/web-access/vendor/xai-search.js +331 -0
  506. package/dist/humain/web-access/vendor/xai-search.js.map +1 -0
  507. package/dist/humain/web-access/vendor/youtube-extract.d.ts +27 -0
  508. package/dist/humain/web-access/vendor/youtube-extract.d.ts.map +1 -0
  509. package/dist/humain/web-access/vendor/youtube-extract.js +282 -0
  510. package/dist/humain/web-access/vendor/youtube-extract.js.map +1 -0
  511. package/dist/index.d.ts +7 -4
  512. package/dist/index.d.ts.map +1 -1
  513. package/dist/index.js +5 -2
  514. package/dist/index.js.map +1 -1
  515. package/dist/main.d.ts +2 -2
  516. package/dist/main.d.ts.map +1 -1
  517. package/dist/main.js +169 -102
  518. package/dist/main.js.map +1 -1
  519. package/dist/modes/agent-exec-mode.d.ts +14 -0
  520. package/dist/modes/agent-exec-mode.d.ts.map +1 -0
  521. package/dist/modes/agent-exec-mode.js +92 -0
  522. package/dist/modes/agent-exec-mode.js.map +1 -0
  523. package/dist/modes/index.d.ts +4 -2
  524. package/dist/modes/index.d.ts.map +1 -1
  525. package/dist/modes/index.js +2 -1
  526. package/dist/modes/index.js.map +1 -1
  527. package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
  528. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  529. package/dist/modes/interactive/components/assistant-message.js +13 -4
  530. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  531. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  532. package/dist/modes/interactive/components/custom-editor.js +7 -0
  533. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  534. package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  535. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
  536. package/dist/modes/interactive/components/earendil-announcement.js +40 -0
  537. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
  538. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  539. package/dist/modes/interactive/components/footer.js +1 -1
  540. package/dist/modes/interactive/components/footer.js.map +1 -1
  541. package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
  542. package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
  543. package/dist/modes/interactive/components/markdown-transform.js +19 -0
  544. package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
  545. package/dist/modes/interactive/components/mermaid.d.ts +11 -0
  546. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  547. package/dist/modes/interactive/components/mermaid.js +75 -0
  548. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  549. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  550. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  551. package/dist/modes/interactive/components/model-selector.js +16 -4
  552. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  553. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  554. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  555. package/dist/modes/interactive/components/scoped-models-selector.js +26 -0
  556. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  557. package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
  558. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  559. package/dist/modes/interactive/components/settings-selector.js +30 -0
  560. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  561. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  562. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  563. package/dist/modes/interactive/components/user-message.js +9 -2
  564. package/dist/modes/interactive/components/user-message.js.map +1 -1
  565. package/dist/modes/interactive/interactive-mode.d.ts +45 -9
  566. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  567. package/dist/modes/interactive/interactive-mode.js +581 -211
  568. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  569. package/dist/modes/interactive/theme/dark.json +1 -0
  570. package/dist/modes/interactive/theme/light.json +1 -0
  571. package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
  572. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  573. package/dist/modes/interactive/theme/theme-controller.js +10 -1
  574. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  575. package/dist/modes/interactive/theme/theme-schema.json +5 -1
  576. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  577. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  578. package/dist/modes/interactive/theme/theme.js +24 -6
  579. package/dist/modes/interactive/theme/theme.js.map +1 -1
  580. package/dist/modes/json-event.d.ts +28 -0
  581. package/dist/modes/json-event.d.ts.map +1 -0
  582. package/dist/modes/json-event.js +12 -0
  583. package/dist/modes/json-event.js.map +1 -0
  584. package/dist/modes/print-mode.d.ts.map +1 -1
  585. package/dist/modes/print-mode.js +12 -2
  586. package/dist/modes/print-mode.js.map +1 -1
  587. package/dist/modes/rpc/rpc-client.d.ts +12 -5
  588. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  589. package/dist/modes/rpc/rpc-client.js +55 -1
  590. package/dist/modes/rpc/rpc-client.js.map +1 -1
  591. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  592. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  593. package/dist/modes/rpc/rpc-mode.js +13 -3
  594. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  595. package/dist/modes/rpc/rpc-types.d.ts +15 -0
  596. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  597. package/dist/modes/rpc/rpc-types.js.map +1 -1
  598. package/dist/package-manager-cli.d.ts.map +1 -1
  599. package/dist/package-manager-cli.js +11 -9
  600. package/dist/package-manager-cli.js.map +1 -1
  601. package/dist/rpc-entry.d.ts.map +1 -1
  602. package/dist/rpc-entry.js +3 -2
  603. package/dist/rpc-entry.js.map +1 -1
  604. package/dist/server/create-harness.d.ts +26 -0
  605. package/dist/server/create-harness.d.ts.map +1 -0
  606. package/dist/server/create-harness.js +104 -0
  607. package/dist/server/create-harness.js.map +1 -0
  608. package/dist/utils/abort.d.ts +5 -0
  609. package/dist/utils/abort.d.ts.map +1 -0
  610. package/dist/utils/abort.js +48 -0
  611. package/dist/utils/abort.js.map +1 -0
  612. package/dist/utils/clipboard.d.ts +1 -1
  613. package/dist/utils/clipboard.d.ts.map +1 -1
  614. package/dist/utils/clipboard.js +22 -2
  615. package/dist/utils/clipboard.js.map +1 -1
  616. package/dist/utils/management-http.d.ts +23 -0
  617. package/dist/utils/management-http.d.ts.map +1 -0
  618. package/dist/utils/management-http.js +50 -0
  619. package/dist/utils/management-http.js.map +1 -0
  620. package/dist/utils/paths.d.ts +3 -0
  621. package/dist/utils/paths.d.ts.map +1 -1
  622. package/dist/utils/paths.js +23 -1
  623. package/dist/utils/paths.js.map +1 -1
  624. package/dist/utils/tool-result-images.d.ts +19 -0
  625. package/dist/utils/tool-result-images.d.ts.map +1 -0
  626. package/dist/utils/tool-result-images.js +45 -0
  627. package/dist/utils/tool-result-images.js.map +1 -0
  628. package/dist/utils/tools-manager.d.ts.map +1 -1
  629. package/dist/utils/tools-manager.js +13 -25
  630. package/dist/utils/tools-manager.js.map +1 -1
  631. package/dist/utils/version-check.d.ts +4 -0
  632. package/dist/utils/version-check.d.ts.map +1 -1
  633. package/dist/utils/version-check.js +23 -5
  634. package/dist/utils/version-check.js.map +1 -1
  635. package/docs/compaction.md +2 -2
  636. package/docs/custom-provider.md +7 -5
  637. package/docs/extensions.md +30 -5
  638. package/docs/humain-terminal.md +21 -1
  639. package/docs/json.md +24 -19
  640. package/docs/keybindings.md +46 -14
  641. package/docs/models.md +32 -11
  642. package/docs/providers.md +9 -1
  643. package/docs/quickstart.md +39 -37
  644. package/docs/rpc.md +31 -11
  645. package/docs/sdk.md +21 -2
  646. package/docs/security.md +21 -21
  647. package/docs/session-format.md +14 -14
  648. package/docs/settings.md +4 -1
  649. package/docs/themes.md +5 -3
  650. package/docs/usage.md +7 -0
  651. package/examples/extensions/custom-compaction.ts +3 -18
  652. package/examples/extensions/custom-provider-anthropic/index.ts +5 -1
  653. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  654. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  655. package/examples/extensions/custom-provider-gitlab-duo/index.ts +5 -1
  656. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  657. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  658. package/examples/extensions/gondolin/package-lock.json +2 -2
  659. package/examples/extensions/gondolin/package.json +1 -1
  660. package/examples/extensions/handoff.ts +4 -13
  661. package/examples/extensions/qna.ts +5 -9
  662. package/examples/extensions/sandbox/package-lock.json +2 -2
  663. package/examples/extensions/sandbox/package.json +1 -1
  664. package/examples/extensions/summarize.ts +9 -20
  665. package/examples/extensions/with-deps/package-lock.json +2 -2
  666. package/examples/extensions/with-deps/package.json +1 -1
  667. package/examples/rpc-extension-ui.ts +11 -2
  668. package/examples/sdk/02-custom-model.ts +2 -3
  669. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  670. package/examples/sdk/12-full-control.ts +1 -1
  671. package/examples/sdk/README.md +1 -1
  672. package/npm-shrinkwrap.json +2771 -535
  673. package/package.json +40 -11
  674. package/src/humain/mcp-adapter/UPSTREAM.md +15 -0
  675. package/src/humain/mcp-adapter/upstream.json +7 -0
  676. package/src/humain/mcp-adapter/vendor/CHANGELOG.md +660 -0
  677. package/src/humain/mcp-adapter/vendor/LICENSE +21 -0
  678. package/src/humain/web-access/UPSTREAM.md +17 -0
  679. package/src/humain/web-access/upstream.json +23 -0
  680. package/src/humain/web-access/vendor/LICENSE +21 -0
@@ -6,10 +6,11 @@ import * as crypto from "node:crypto";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
9
+ import * as TuiLayouts from "@earendil-works/pi-tui";
10
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
10
11
  import chalk from "chalk";
11
12
  import { spawn, spawnSync } from "child_process";
12
- import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, ENV_OFFLINE, getAgentDir, getAuthPath, getChangelogUrl, getDebugLogPath, getDocsPath, getInstallTelemetryUrl, getShareViewerUrl, VERSION, } from "../../config.js";
13
+ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
13
14
  import { parseSkillBlock } from "../../core/agent-session.js";
14
15
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
15
16
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
@@ -17,7 +18,8 @@ import { FooterDataProvider } from "../../core/footer-data-provider.js";
17
18
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
18
19
  import { KeybindingsManager } from "../../core/keybindings.js";
19
20
  import { createCompactionSummaryMessage } from "../../core/messages.js";
20
- import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope, resolveModelScopeWithDiagnostics, } from "../../core/model-resolver.js";
21
+ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
22
+ import { CredentialSynchronizationError } from "../../core/model-runtime.js";
21
23
  import { DefaultPackageManager } from "../../core/package-manager.js";
22
24
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
23
25
  import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
@@ -29,6 +31,8 @@ import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelo
29
31
  import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
30
32
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
31
33
  import { parseGitUrl } from "../../utils/git.js";
34
+ import { openBrowser } from "../../utils/open-browser.js";
35
+ import { getCwdRelativePath } from "../../utils/paths.js";
32
36
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
33
37
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
34
38
  import { ensureTool } from "../../utils/tools-manager.js";
@@ -44,12 +48,14 @@ import { CustomEntryComponent } from "./components/custom-entry.js";
44
48
  import { CustomMessageComponent } from "./components/custom-message.js";
45
49
  import { DaxnutsComponent } from "./components/daxnuts.js";
46
50
  import { DynamicBorder } from "./components/dynamic-border.js";
51
+ import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
47
52
  import { ExtensionEditorComponent } from "./components/extension-editor.js";
48
53
  import { ExtensionInputComponent } from "./components/extension-input.js";
49
54
  import { ExtensionSelectorComponent } from "./components/extension-selector.js";
50
55
  import { FooterComponent, formatTokens } from "./components/footer.js";
51
56
  import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
52
57
  import { LoginDialogComponent } from "./components/login-dialog.js";
58
+ import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
53
59
  import { ModelSelectorComponent } from "./components/model-selector.js";
54
60
  import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
55
61
  import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
@@ -159,11 +165,58 @@ function formatLoginProviderCompletionDescription(provider) {
159
165
  const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
160
166
  return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
161
167
  }
168
+ /** Composition root for selecting the interactive terminal renderer. */
169
+ export function createInteractiveTui(options) {
170
+ const terminal = options.terminal ?? new ProcessTerminal();
171
+ if (options.tuiMode === "fullscreen") {
172
+ return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
173
+ openUrl: openBrowser,
174
+ onRightClickPaste: options.onRightClickPaste,
175
+ });
176
+ }
177
+ return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
178
+ }
179
+ /** Stable reference for components while InteractiveMode replaces the active renderer. */
180
+ export function createInteractiveTuiReference(getTui) {
181
+ return new Proxy({}, {
182
+ get: (_target, property) => {
183
+ const tui = getTui();
184
+ const value = Reflect.get(tui, property, tui);
185
+ if (typeof value !== "function")
186
+ return value;
187
+ let methodTui = tui;
188
+ let method = value;
189
+ return (...args) => {
190
+ const currentTui = getTui();
191
+ if (currentTui !== methodTui) {
192
+ const currentMethod = Reflect.get(currentTui, property, currentTui);
193
+ if (typeof currentMethod !== "function") {
194
+ throw new TypeError(`TUI property ${String(property)} is not callable`);
195
+ }
196
+ methodTui = currentTui;
197
+ method = currentMethod;
198
+ }
199
+ return Reflect.apply(method, methodTui, args);
200
+ };
201
+ },
202
+ set: (_target, property, value) => {
203
+ const tui = getTui();
204
+ return Reflect.set(tui, property, value, tui);
205
+ },
206
+ has: (_target, property) => Reflect.has(getTui(), property),
207
+ getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
208
+ });
209
+ }
162
210
  export class InteractiveMode {
163
211
  runtimeHost;
212
+ renderer;
164
213
  ui;
214
+ mainScreenRenderState;
165
215
  loadedResourcesContainer;
166
216
  chatContainer;
217
+ documentContainer;
218
+ transcriptScrollView;
219
+ fullscreenLayoutRoot;
167
220
  pendingMessagesContainer;
168
221
  statusContainer;
169
222
  defaultEditor;
@@ -173,7 +226,10 @@ export class InteractiveMode {
173
226
  autocompleteProviderWrappers = [];
174
227
  fdPath;
175
228
  editorContainer;
229
+ activeSelectorToken;
230
+ activeSelectorDispose;
176
231
  footer;
232
+ footerContainer;
177
233
  footerDataProvider;
178
234
  // Stored so the same manager can be injected into custom editors, selectors, and extension UI.
179
235
  keybindings;
@@ -207,6 +263,10 @@ export class InteractiveMode {
207
263
  // Thinking block visibility state
208
264
  hideThinkingBlock = false;
209
265
  outputPad = 1;
266
+ mermaidMarkdownTransformer = createMermaidMarkdownTransformer({
267
+ getMode: () => this.settingsManager.getMermaidRenderingMode(),
268
+ theme,
269
+ });
210
270
  // Skill commands: command name -> skill file path
211
271
  skillCommands = new Map();
212
272
  // Agent subscription unsubscribe function
@@ -230,7 +290,7 @@ export class InteractiveMode {
230
290
  extensionSelector = undefined;
231
291
  extensionInput = undefined;
232
292
  extensionEditor = undefined;
233
- extensionTerminalInputUnsubscribers = new Set();
293
+ extensionTerminalInputSubscriptions = new Set();
234
294
  // Extension widgets (components rendered above/below the editor)
235
295
  extensionWidgetsAbove = new Map();
236
296
  extensionWidgetsBelow = new Map();
@@ -245,6 +305,9 @@ export class InteractiveMode {
245
305
  // Custom header from extension (undefined = use built-in header)
246
306
  customHeader = undefined;
247
307
  options;
308
+ onRightClickPaste = () => {
309
+ void this.handleRightClickPaste();
310
+ };
248
311
  autoTrustOnReloadCwd;
249
312
  themeController;
250
313
  // Convenience accessors
@@ -262,7 +325,8 @@ export class InteractiveMode {
262
325
  }
263
326
  constructor(runtimeHost, options = {}) {
264
327
  this.runtimeHost = runtimeHost;
265
- this.options = options;
328
+ const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
329
+ this.options = { ...options, tuiMode };
266
330
  this.autoTrustOnReloadCwd = options.autoTrustOnReloadCwd;
267
331
  this.runtimeHost.setBeforeSessionInvalidate(() => {
268
332
  this.resetExtensionUI();
@@ -271,11 +335,21 @@ export class InteractiveMode {
271
335
  await this.rebindCurrentSession({ renderBeforeBind: true });
272
336
  });
273
337
  this.version = VERSION;
274
- this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor(), getAgentDir());
338
+ this.renderer = createInteractiveTui({
339
+ tuiMode,
340
+ showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
341
+ logDirectory: getAgentDir(),
342
+ onRightClickPaste: this.onRightClickPaste,
343
+ });
344
+ this.ui = createInteractiveTuiReference(() => this.renderer);
275
345
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
276
346
  this.headerContainer = new Container();
277
347
  this.loadedResourcesContainer = new Container();
278
348
  this.chatContainer = new Container();
349
+ this.documentContainer = new Container();
350
+ this.documentContainer.addChild(this.headerContainer);
351
+ this.documentContainer.addChild(this.loadedResourcesContainer);
352
+ this.documentContainer.addChild(this.chatContainer);
279
353
  this.pendingMessagesContainer = new Container();
280
354
  this.statusContainer = new Container();
281
355
  this.widgetContainerAbove = new Container();
@@ -294,6 +368,8 @@ export class InteractiveMode {
294
368
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
295
369
  this.footer = new FooterComponent(this.session, this.footerDataProvider);
296
370
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
371
+ this.footerContainer = new Container();
372
+ this.footerContainer.addChild(this.footer);
297
373
  // Load hide thinking block setting
298
374
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
299
375
  this.outputPad = this.settingsManager.getOutputPad();
@@ -349,11 +425,10 @@ export class InteractiveMode {
349
425
  }));
350
426
  const modelCommand = slashCommands.find((command) => command.name === "model");
351
427
  if (modelCommand) {
352
- modelCommand.getArgumentCompletions = async (prefix) => {
353
- // Get available models (scoped or from registry)
428
+ modelCommand.getArgumentCompletions = (prefix) => {
354
429
  const models = this.session.scopedModels.length > 0
355
430
  ? this.session.scopedModels.map((s) => s.model)
356
- : await this.session.modelRuntime.getAvailable();
431
+ : this.session.modelRuntime.getAvailableSnapshot();
357
432
  if (models.length === 0)
358
433
  return null;
359
434
  // Create items with provider/id format
@@ -454,6 +529,73 @@ export class InteractiveMode {
454
529
  }
455
530
  this.chatContainer.addChild(new DynamicBorder());
456
531
  }
532
+ mountInteractiveTui(tui, components) {
533
+ for (const component of components)
534
+ tui.addChild(component);
535
+ if (TuiLayouts.isViewportTUI(tui)) {
536
+ if (!this.fullscreenLayoutRoot)
537
+ throw new Error("Fullscreen layout is not initialized");
538
+ tui.setLayoutRoot(this.fullscreenLayoutRoot);
539
+ }
540
+ }
541
+ stopInteractiveTui() {
542
+ if (this.renderer.mode === "fullscreen") {
543
+ while (this.renderer.hasOverlayEntries)
544
+ this.renderer.hideOverlay();
545
+ this.switchTuiMode("regular", false, false);
546
+ this.renderer.renderNow();
547
+ }
548
+ this.ui.stop();
549
+ }
550
+ switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
551
+ const previousUi = this.renderer;
552
+ if (mode === previousUi.mode)
553
+ return true;
554
+ if (previousUi.hasOverlayEntries)
555
+ return false;
556
+ const components = [...previousUi.children];
557
+ const focus = previousUi.getFocusedComponent();
558
+ const terminal = previousUi.terminal;
559
+ const showHardwareCursor = previousUi.getShowHardwareCursor();
560
+ const clearOnShrink = previousUi.getClearOnShrink();
561
+ const onDebug = previousUi.onDebug;
562
+ if (previousUi instanceof TuiMainScreen) {
563
+ this.mainScreenRenderState = previousUi.captureRenderState();
564
+ }
565
+ previousUi.stop({ preserveScreen: true });
566
+ previousUi.setFocus(null);
567
+ previousUi.clear();
568
+ if (TuiLayouts.isViewportTUI(previousUi))
569
+ previousUi.setLayoutRoot(undefined);
570
+ const nextUi = createInteractiveTui({
571
+ tuiMode: mode,
572
+ showHardwareCursor,
573
+ logDirectory: getAgentDir(),
574
+ terminal,
575
+ onRightClickPaste: this.onRightClickPaste,
576
+ });
577
+ nextUi.setClearOnShrink(clearOnShrink);
578
+ nextUi.onDebug = onDebug;
579
+ if (nextUi instanceof TuiMainScreen && this.mainScreenRenderState) {
580
+ nextUi.restoreRenderState(this.mainScreenRenderState);
581
+ }
582
+ this.renderer = nextUi;
583
+ this.options.tuiMode = mode;
584
+ this.mountInteractiveTui(nextUi, components);
585
+ nextUi.invalidate();
586
+ nextUi.setFocus(focus);
587
+ if (!startRenderer)
588
+ return true;
589
+ nextUi.start();
590
+ this.themeController.rebindTui();
591
+ this.rebindExtensionTerminalInputListeners();
592
+ if (restoreProgress &&
593
+ this.settingsManager.getShowTerminalProgress() &&
594
+ (this.session.isStreaming || this.session.isCompacting)) {
595
+ terminal.setProgress(true);
596
+ }
597
+ return true;
598
+ }
457
599
  async init() {
458
600
  if (this.isInitialized)
459
601
  return;
@@ -477,18 +619,36 @@ export class InteractiveMode {
477
619
  : "";
478
620
  console.log(theme.fg("dim", `Model scope: ${modelList}${cycleHint}`));
479
621
  }
480
- // Add header container as first child. Populate it after applying theme settings.
481
- // Keep loaded resources before chat so restored session messages never precede them.
482
- this.ui.addChild(this.headerContainer);
483
- this.ui.addChild(this.loadedResourcesContainer);
484
- this.ui.addChild(this.chatContainer);
485
- this.ui.addChild(this.pendingMessagesContainer);
486
- this.ui.addChild(this.statusContainer);
622
+ // Keep one component tree and remount it when changing renderers.
487
623
  this.renderWidgets(); // Initialize with default spacer
488
- this.ui.addChild(this.widgetContainerAbove);
489
- this.ui.addChild(this.editorContainer);
490
- this.ui.addChild(this.widgetContainerBelow);
491
- this.ui.addChild(this.footer);
624
+ this.transcriptScrollView = new TuiLayouts.ScrollView(this.documentContainer, {
625
+ follow: "end",
626
+ primary: true,
627
+ overscroll: "chain",
628
+ scrollbar: this.settingsManager.getFullscreenScrollbar(),
629
+ scrollbarStyle: (text) => theme.bg("scrollbarThumb", text),
630
+ });
631
+ const dock = new TuiLayouts.VStack([
632
+ { component: this.pendingMessagesContainer, shrink: 1, minSize: 0 },
633
+ { component: this.statusContainer, shrink: 1, minSize: 0 },
634
+ { component: this.widgetContainerAbove, shrink: 1, minSize: 0 },
635
+ { component: this.editorContainer, shrink: 1, minSize: 3 },
636
+ { component: this.widgetContainerBelow, shrink: 1, minSize: 0 },
637
+ { component: this.footerContainer, shrink: 1, minSize: 1 },
638
+ ]);
639
+ this.fullscreenLayoutRoot = new TuiLayouts.VStack([
640
+ { component: this.transcriptScrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },
641
+ { component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
642
+ ]);
643
+ this.mountInteractiveTui(this.renderer, [
644
+ this.documentContainer,
645
+ this.pendingMessagesContainer,
646
+ this.statusContainer,
647
+ this.widgetContainerAbove,
648
+ this.editorContainer,
649
+ this.widgetContainerBelow,
650
+ this.footerContainer,
651
+ ]);
492
652
  this.ui.setFocus(this.editor);
493
653
  this.setupKeyHandlers();
494
654
  this.setupEditorSubmitHandler();
@@ -498,7 +658,7 @@ export class InteractiveMode {
498
658
  await this.themeController.applyFromSettings();
499
659
  // Add header with keybindings from config (unless silenced)
500
660
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
501
- const logo = theme.fg("accent", APP_TITLE) + theme.fg("dim", ` v${this.version}`);
661
+ const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
502
662
  // Build startup instructions using keybinding hint helpers
503
663
  const hint = (keybinding, description) => keyHint(keybinding, description);
504
664
  const expandedInstructions = [
@@ -530,7 +690,7 @@ export class InteractiveMode {
530
690
  hint("app.tools.expand", "more"),
531
691
  ].join(theme.fg("muted", " · "));
532
692
  const compactOnboarding = theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
533
- const onboarding = theme.fg("dim", `${APP_TITLE} can explain its own features, model profiles, and package resources.`);
693
+ const onboarding = theme.fg("dim", `Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.`);
534
694
  this.builtInHeader = new ExpandableText(() => `${logo}\n${compactInstructions}\n${compactOnboarding}\n\n${onboarding}`, () => `${logo}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
535
695
  // Setup UI layout
536
696
  this.headerContainer.addChild(new Spacer(1));
@@ -580,10 +740,13 @@ export class InteractiveMode {
580
740
  async run() {
581
741
  await this.init();
582
742
  if (!process.env.PI_OFFLINE) {
743
+ const controller = new AbortController();
744
+ const timeout = setTimeout(() => controller.abort(), 15_000);
583
745
  void this.session.modelRuntime
584
- .refresh()
746
+ .refresh({ signal: controller.signal })
585
747
  .then(() => this.updateAvailableProviderCount())
586
- .catch(() => { });
748
+ .catch(() => { })
749
+ .finally(() => clearTimeout(timeout));
587
750
  }
588
751
  // Start version check asynchronously
589
752
  checkForNewPiVersion(this.version).then((newRelease) => {
@@ -620,7 +783,7 @@ export class InteractiveMode {
620
783
  if (modelsJsonError) {
621
784
  this.showError(`models.json error: ${modelsJsonError}`);
622
785
  }
623
- if (modelFallbackMessage && !this.isNoModelsAvailableMessage(modelFallbackMessage)) {
786
+ if (modelFallbackMessage) {
624
787
  this.showWarning(modelFallbackMessage);
625
788
  }
626
789
  void this.maybeWarnAboutAnthropicSubscriptionAuth();
@@ -658,7 +821,7 @@ export class InteractiveMode {
658
821
  }
659
822
  }
660
823
  async checkForPackageUpdates() {
661
- if (process.env[ENV_OFFLINE]) {
824
+ if (process.env.PI_OFFLINE) {
662
825
  return [];
663
826
  }
664
827
  try {
@@ -711,7 +874,7 @@ export class InteractiveMode {
711
874
  return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
712
875
  }
713
876
  if (extendedKeysFormat === "xterm") {
714
- return `tmux extended-keys-format is xterm. ${APP_TITLE} works best with csi-u. Add \`set -g extended-keys-format csi-u\` to ~/.tmux.conf and restart tmux.`;
877
+ return "tmux extended-keys-format is xterm. Pi works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
715
878
  }
716
879
  return undefined;
717
880
  }
@@ -742,13 +905,13 @@ export class InteractiveMode {
742
905
  return undefined;
743
906
  }
744
907
  reportInstallTelemetry(version) {
745
- if (process.env[ENV_OFFLINE]) {
908
+ if (process.env.PI_OFFLINE) {
746
909
  return;
747
910
  }
748
911
  if (!isInstallTelemetryEnabled(this.settingsManager)) {
749
912
  return;
750
913
  }
751
- void fetch(getInstallTelemetryUrl(version), {
914
+ void fetch(`https://pi.dev/api/report-install?version=${encodeURIComponent(version)}`, {
752
915
  headers: {
753
916
  "User-Agent": getPiUserAgent(version),
754
917
  },
@@ -763,20 +926,6 @@ export class InteractiveMode {
763
926
  codeBlockIndent: this.settingsManager.getCodeBlockIndent(),
764
927
  };
765
928
  }
766
- isNoModelsAvailableMessage(message) {
767
- return message?.startsWith("No models available.") ?? false;
768
- }
769
- getNoModelsStartupNotice() {
770
- if (!this.isNoModelsAvailableMessage(this.options.modelFallbackMessage)) {
771
- return undefined;
772
- }
773
- return [
774
- theme.bold(theme.fg("warning", "Model setup required")),
775
- `${theme.fg("muted", "No models are available yet. Run ")}${theme.bold("/login")}${theme.fg("muted", " to add a provider.")}`,
776
- `${theme.fg("dim", "Then use ")}${theme.bold("/model")}${theme.fg("dim", " if you want to switch models.")}`,
777
- `${theme.fg("dim", "Docs: ")}${theme.fg("accent", "docs/providers.md")}${theme.fg("dim", ", ")}${theme.fg("accent", "docs/models.md")}`,
778
- ].join("\n");
779
- }
780
929
  // =========================================================================
781
930
  // Extension System
782
931
  // =========================================================================
@@ -794,6 +943,15 @@ export class InteractiveMode {
794
943
  result = result.replace(/\/index\.ts$/, "").replace(/\/index\.js$/, "");
795
944
  return result;
796
945
  }
946
+ formatContextPath(p) {
947
+ const cwd = path.resolve(this.sessionManager.getCwd());
948
+ const absolutePath = path.isAbsolute(p) ? path.resolve(p) : path.resolve(cwd, p);
949
+ const relativePath = getCwdRelativePath(absolutePath, cwd);
950
+ if (relativePath !== undefined) {
951
+ return relativePath;
952
+ }
953
+ return this.formatDisplayPath(absolutePath);
954
+ }
797
955
  getStartupExpansionState() {
798
956
  return this.options.verbose || this.toolOutputExpanded;
799
957
  }
@@ -830,43 +988,92 @@ export class InteractiveMode {
830
988
  }
831
989
  return this.formatDisplayPath(fullPath);
832
990
  }
833
- stripNpmPackageVersion(packageSpec) {
834
- if (packageSpec.startsWith("@")) {
835
- const versionStart = packageSpec.indexOf("@", packageSpec.indexOf("/") + 1);
836
- return versionStart === -1 ? packageSpec : packageSpec.slice(0, versionStart);
837
- }
838
- const versionStart = packageSpec.indexOf("@");
839
- return versionStart === -1 ? packageSpec : packageSpec.slice(0, versionStart);
840
- }
841
- stripLegacyPackagePrefix(packageName) {
842
- const scopedMatch = /^(@[^/]+)\/(.+)$/.exec(packageName);
843
- if (scopedMatch) {
844
- return `${scopedMatch[1]}/${this.stripLegacyPackagePrefix(scopedMatch[2])}`;
991
+ getCompactPathLabel(resourcePath, sourceInfo) {
992
+ const shortPath = this.getShortPath(resourcePath, sourceInfo);
993
+ const normalizedPath = shortPath.replace(/\\/g, "/");
994
+ const segments = normalizedPath.split("/").filter((segment) => segment.length > 0 && segment !== "~");
995
+ if (segments.length > 0) {
996
+ return segments[segments.length - 1];
845
997
  }
846
- const sanitized = packageName.replace(/^pi[-_]/, "");
847
- return sanitized || packageName;
998
+ return shortPath;
848
999
  }
849
- getPackageSourceName(source) {
1000
+ getCompactPackageSourceLabel(sourceInfo) {
1001
+ const source = sourceInfo?.source ?? "";
850
1002
  if (source.startsWith("npm:")) {
851
- return this.stripLegacyPackagePrefix(this.stripNpmPackageVersion(source.slice("npm:".length)));
1003
+ return source.slice("npm:".length) || source;
852
1004
  }
853
1005
  const gitSource = parseGitUrl(source);
854
- if (gitSource?.path) {
855
- return gitSource.path
856
- .split("/")
857
- .map((segment, index, segments) => index === segments.length - 1 ? this.stripLegacyPackagePrefix(segment) : segment)
858
- .join("/");
1006
+ if (gitSource) {
1007
+ return gitSource.path || source;
859
1008
  }
860
1009
  return source;
861
1010
  }
862
- getDisplaySourceLabel(source) {
863
- if (source.startsWith("npm:")) {
864
- return `npm:${this.getPackageSourceName(source)}`;
865
- }
866
- if (source.startsWith("git:")) {
867
- return `git:${this.getPackageSourceName(source)}`;
1011
+ getCompactExtensionLabel(resourcePath, sourceInfo) {
1012
+ if (!this.isPackageSource(sourceInfo)) {
1013
+ return this.getCompactPathLabel(resourcePath, sourceInfo);
1014
+ }
1015
+ const sourceLabel = this.getCompactPackageSourceLabel(sourceInfo);
1016
+ if (!sourceLabel) {
1017
+ return this.getCompactPathLabel(resourcePath, sourceInfo);
1018
+ }
1019
+ const shortPath = this.getShortPath(resourcePath, sourceInfo).replace(/\\/g, "/");
1020
+ const packagePath = shortPath.startsWith("extensions/") ? shortPath.slice("extensions/".length) : shortPath;
1021
+ const parsedPath = path.posix.parse(packagePath);
1022
+ if (parsedPath.name === "index") {
1023
+ return !parsedPath.dir || parsedPath.dir === "." ? sourceLabel : `${sourceLabel}:${parsedPath.dir}`;
1024
+ }
1025
+ return `${sourceLabel}:${packagePath}`;
1026
+ }
1027
+ getCompactDisplayPathSegments(resourcePath) {
1028
+ return this.formatDisplayPath(resourcePath)
1029
+ .replace(/\\/g, "/")
1030
+ .split("/")
1031
+ .filter((segment) => segment.length > 0 && segment !== "~");
1032
+ }
1033
+ getCompactNonPackageExtensionLabel(resourcePath, index, allPaths) {
1034
+ const segments = allPaths[index]?.segments;
1035
+ if (!segments || segments.length === 0) {
1036
+ return this.getCompactPathLabel(resourcePath);
1037
+ }
1038
+ for (let segmentCount = 1; segmentCount <= segments.length; segmentCount += 1) {
1039
+ const candidate = segments.slice(-segmentCount).join("/");
1040
+ const isUnique = allPaths.every((item, itemIndex) => {
1041
+ if (itemIndex === index) {
1042
+ return true;
1043
+ }
1044
+ return item.segments.slice(-segmentCount).join("/") !== candidate;
1045
+ });
1046
+ if (isUnique) {
1047
+ return candidate;
1048
+ }
868
1049
  }
869
- return source;
1050
+ return segments.join("/");
1051
+ }
1052
+ getCompactExtensionLabels(extensions) {
1053
+ const nonPackageExtensions = extensions
1054
+ .map((extension) => {
1055
+ const segments = this.getCompactDisplayPathSegments(extension.path);
1056
+ const lastSegment = segments[segments.length - 1];
1057
+ if (segments.length > 1 && (lastSegment === "index.ts" || lastSegment === "index.js")) {
1058
+ segments.pop();
1059
+ }
1060
+ return {
1061
+ path: extension.path,
1062
+ sourceInfo: extension.sourceInfo,
1063
+ segments,
1064
+ };
1065
+ })
1066
+ .filter((extension) => !this.isPackageSource(extension.sourceInfo));
1067
+ return extensions.map((extension) => {
1068
+ if (this.isPackageSource(extension.sourceInfo)) {
1069
+ return this.getCompactExtensionLabel(extension.path, extension.sourceInfo);
1070
+ }
1071
+ const nonPackageIndex = nonPackageExtensions.findIndex((item) => item.path === extension.path);
1072
+ if (nonPackageIndex === -1) {
1073
+ return this.getCompactPathLabel(extension.path, extension.sourceInfo);
1074
+ }
1075
+ return this.getCompactNonPackageExtensionLabel(extension.path, nonPackageIndex, nonPackageExtensions);
1076
+ });
870
1077
  }
871
1078
  getDisplaySourceInfo(sourceInfo) {
872
1079
  const source = sourceInfo?.source ?? "local";
@@ -887,7 +1094,7 @@ export class InteractiveMode {
887
1094
  return { label: "path", scopeLabel: scope === "temporary" ? "temp" : undefined, color: "muted" };
888
1095
  }
889
1096
  const scopeLabel = scope === "user" ? "user" : scope === "project" ? "project" : scope === "temporary" ? "temp" : undefined;
890
- return { label: this.getDisplaySourceLabel(source), scopeLabel, color: "accent" };
1097
+ return { label: source, scopeLabel, color: "accent" };
891
1098
  }
892
1099
  getScopeGroup(sourceInfo) {
893
1100
  const source = sourceInfo?.source ?? "local";
@@ -935,7 +1142,7 @@ export class InteractiveMode {
935
1142
  }
936
1143
  const sortedPackages = Array.from(group.packages.entries()).sort(([a], [b]) => a.localeCompare(b));
937
1144
  for (const [source, items] of sortedPackages) {
938
- lines.push(` ${theme.fg("mdLink", this.getDisplaySourceLabel(source))}`);
1145
+ lines.push(` ${theme.fg("mdLink", source)}`);
939
1146
  const sortedPackagePaths = [...items].sort((a, b) => a.path.localeCompare(b.path));
940
1147
  for (const item of sortedPackagePaths) {
941
1148
  lines.push(theme.fg("dim", ` ${options.formatPackagePath(item, source)}`));
@@ -1011,24 +1218,22 @@ export class InteractiveMode {
1011
1218
  this.loadedResourcesContainer.clear();
1012
1219
  const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
1013
1220
  const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
1014
- const noModelsStartupNotice = this.getNoModelsStartupNotice();
1015
- if (!showListing && !showDiagnostics && !noModelsStartupNotice) {
1221
+ if (!showListing && !showDiagnostics) {
1016
1222
  return;
1017
1223
  }
1018
1224
  const sectionHeader = (name, color = "mdHeading") => theme.fg(color, `[${name}]`);
1019
- const formatCountSummary = (count, singular, plural = `${singular}s`) => theme.fg("dim", ` ${count} ${count === 1 ? singular : plural}`);
1225
+ const formatCompactList = (items, options) => {
1226
+ const labels = items.map((item) => item.trim()).filter((item) => item.length > 0);
1227
+ if (options?.sort !== false) {
1228
+ labels.sort((a, b) => a.localeCompare(b));
1229
+ }
1230
+ return theme.fg("dim", ` ${labels.join(", ")}`);
1231
+ };
1020
1232
  const addLoadedSection = (name, collapsedBody, expandedBody = collapsedBody, color = "mdHeading") => {
1021
1233
  const section = new ExpandableText(() => `${sectionHeader(name, color)}\n${collapsedBody}`, () => `${sectionHeader(name, color)}\n${expandedBody}`, this.getStartupExpansionState(), 0, 0);
1022
1234
  this.loadedResourcesContainer.addChild(section);
1023
1235
  this.loadedResourcesContainer.addChild(new Spacer(1));
1024
1236
  };
1025
- if (noModelsStartupNotice) {
1026
- this.loadedResourcesContainer.addChild(new Spacer(1));
1027
- this.loadedResourcesContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
1028
- this.loadedResourcesContainer.addChild(new Text(noModelsStartupNotice, 1, 0));
1029
- this.loadedResourcesContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
1030
- this.loadedResourcesContainer.addChild(new Spacer(1));
1031
- }
1032
1237
  const skillsResult = this.session.resourceLoader.getSkills();
1033
1238
  const promptsResult = this.session.resourceLoader.getPrompts();
1034
1239
  const themesResult = this.session.resourceLoader.getThemes();
@@ -1073,7 +1278,7 @@ export class InteractiveMode {
1073
1278
  const contextList = contextFiles
1074
1279
  .map((f) => theme.fg("dim", ` ${this.formatDisplayPath(f.path)}`))
1075
1280
  .join("\n");
1076
- const contextCompactList = formatCountSummary(contextFiles.length, "context file");
1281
+ const contextCompactList = formatCompactList(contextFiles.map((contextFile) => this.formatContextPath(contextFile.path)), { sort: false });
1077
1282
  addLoadedSection("Context", contextCompactList, contextList);
1078
1283
  }
1079
1284
  const skills = skillsResult.skills;
@@ -1083,7 +1288,7 @@ export class InteractiveMode {
1083
1288
  formatPath: (item) => this.formatDisplayPath(item.path),
1084
1289
  formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
1085
1290
  });
1086
- const skillCompactList = formatCountSummary(skills.length, "skill");
1291
+ const skillCompactList = formatCompactList(skills.map((skill) => skill.name));
1087
1292
  addLoadedSection("Skills", skillCompactList, skillList);
1088
1293
  }
1089
1294
  const templates = this.session.promptTemplates;
@@ -1100,7 +1305,7 @@ export class InteractiveMode {
1100
1305
  return template ? `/${template.name}` : this.formatDisplayPath(item.path);
1101
1306
  },
1102
1307
  });
1103
- const promptCompactList = formatCountSummary(templates.length, "prompt");
1308
+ const promptCompactList = formatCompactList(templates.map((template) => `/${template.name}`));
1104
1309
  addLoadedSection("Prompts", promptCompactList, templateList);
1105
1310
  }
1106
1311
  if (extensions.length > 0) {
@@ -1109,7 +1314,7 @@ export class InteractiveMode {
1109
1314
  formatPath: (item) => this.formatExtensionDisplayPath(item.path),
1110
1315
  formatPackagePath: (item) => this.formatExtensionDisplayPath(this.getShortPath(item.path, item.sourceInfo)),
1111
1316
  });
1112
- const extensionCompactList = formatCountSummary(extensions.length, "extension");
1317
+ const extensionCompactList = formatCompactList(this.getCompactExtensionLabels(extensions));
1113
1318
  addLoadedSection("Extensions", extensionCompactList, extList, "mdHeading");
1114
1319
  }
1115
1320
  // Show loaded themes (excluding built-in)
@@ -1124,7 +1329,7 @@ export class InteractiveMode {
1124
1329
  formatPath: (item) => this.formatDisplayPath(item.path),
1125
1330
  formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
1126
1331
  });
1127
- const themeCompactList = formatCountSummary(customThemes.length, "theme");
1332
+ const themeCompactList = formatCompactList(customThemes.map((loadedTheme) => loadedTheme.name ?? this.getCompactPathLabel(loadedTheme.sourcePath, loadedTheme.sourceInfo)));
1128
1333
  addLoadedSection("Themes", themeCompactList, themeList);
1129
1334
  }
1130
1335
  }
@@ -1244,8 +1449,12 @@ export class InteractiveMode {
1244
1449
  this.showLoadedResources({ force: false, showDiagnosticsWhenQuiet: true });
1245
1450
  this.showStartupNoticesIfNeeded();
1246
1451
  }
1452
+ applyFullscreenScrollbarSetting() {
1453
+ this.transcriptScrollView?.setScrollbar(this.settingsManager.getFullscreenScrollbar());
1454
+ }
1247
1455
  applyRuntimeSettings() {
1248
1456
  configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
1457
+ this.applyFullscreenScrollbarSetting();
1249
1458
  this.footer.setSession(this.session);
1250
1459
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1251
1460
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
@@ -1309,6 +1518,9 @@ export class InteractiveMode {
1309
1518
  getRegisteredToolDefinition(toolName) {
1310
1519
  return this.session.getToolDefinition(toolName);
1311
1520
  }
1521
+ getMarkdownTransformers() {
1522
+ return [this.mermaidMarkdownTransformer, ...this.session.extensionRunner.getMarkdownTransformers()];
1523
+ }
1312
1524
  /**
1313
1525
  * Set up keyboard shortcuts registered by extensions.
1314
1526
  */
@@ -1388,7 +1600,7 @@ export class InteractiveMode {
1388
1600
  this.activeStatusIndicator?.dispose();
1389
1601
  this.activeStatusIndicator = undefined;
1390
1602
  this.statusContainer.clear();
1391
- if (hadActiveStatusIndicator && this.ui.getClearOnShrink()) {
1603
+ if (hadActiveStatusIndicator && this.options.tuiMode === "regular" && this.ui.getClearOnShrink()) {
1392
1604
  this.statusContainer.addChild(this.idleStatus);
1393
1605
  }
1394
1606
  }
@@ -1536,22 +1748,16 @@ export class InteractiveMode {
1536
1748
  if (this.customFooter?.dispose) {
1537
1749
  this.customFooter.dispose();
1538
1750
  }
1539
- // Remove current footer from UI
1540
- if (this.customFooter) {
1541
- this.ui.removeChild(this.customFooter);
1542
- }
1543
- else {
1544
- this.ui.removeChild(this.footer);
1545
- }
1751
+ this.footerContainer.clear();
1546
1752
  if (factory) {
1547
1753
  // Create and add custom footer, passing the data provider
1548
1754
  this.customFooter = factory(this.ui, theme, this.footerDataProvider);
1549
- this.ui.addChild(this.customFooter);
1755
+ this.footerContainer.addChild(this.customFooter);
1550
1756
  }
1551
1757
  else {
1552
1758
  // Restore built-in footer
1553
1759
  this.customFooter = undefined;
1554
- this.ui.addChild(this.footer);
1760
+ this.footerContainer.addChild(this.footer);
1555
1761
  }
1556
1762
  this.ui.requestRender();
1557
1763
  }
@@ -1597,18 +1803,23 @@ export class InteractiveMode {
1597
1803
  this.ui.requestRender();
1598
1804
  }
1599
1805
  addExtensionTerminalInputListener(handler) {
1600
- const unsubscribe = this.ui.addInputListener(handler);
1601
- this.extensionTerminalInputUnsubscribers.add(unsubscribe);
1806
+ const subscription = { handler, unsubscribe: this.ui.addInputListener(handler) };
1807
+ this.extensionTerminalInputSubscriptions.add(subscription);
1602
1808
  return () => {
1603
- unsubscribe();
1604
- this.extensionTerminalInputUnsubscribers.delete(unsubscribe);
1809
+ subscription.unsubscribe();
1810
+ this.extensionTerminalInputSubscriptions.delete(subscription);
1605
1811
  };
1606
1812
  }
1607
- clearExtensionTerminalInputListeners() {
1608
- for (const unsubscribe of this.extensionTerminalInputUnsubscribers) {
1609
- unsubscribe();
1813
+ rebindExtensionTerminalInputListeners() {
1814
+ for (const subscription of this.extensionTerminalInputSubscriptions) {
1815
+ subscription.unsubscribe();
1816
+ subscription.unsubscribe = this.ui.addInputListener(subscription.handler);
1610
1817
  }
1611
- this.extensionTerminalInputUnsubscribers.clear();
1818
+ }
1819
+ clearExtensionTerminalInputListeners() {
1820
+ for (const subscription of this.extensionTerminalInputSubscriptions)
1821
+ subscription.unsubscribe();
1822
+ this.extensionTerminalInputSubscriptions.clear();
1612
1823
  }
1613
1824
  /**
1614
1825
  * Create the ExtensionUIContext for extensions.
@@ -1703,6 +1914,7 @@ export class InteractiveMode {
1703
1914
  this.hideExtensionSelector();
1704
1915
  resolve(undefined);
1705
1916
  }, { tui: this.ui, timeout: opts?.timeout, onToggleToolsExpanded: () => this.toggleToolOutputExpansion() });
1917
+ this.disposeActiveSelector();
1706
1918
  this.editorContainer.clear();
1707
1919
  this.editorContainer.addChild(this.extensionSelector);
1708
1920
  this.ui.setFocus(this.extensionSelector);
@@ -1754,6 +1966,7 @@ export class InteractiveMode {
1754
1966
  this.hideExtensionInput();
1755
1967
  resolve(undefined);
1756
1968
  }, { tui: this.ui, timeout: opts?.timeout });
1969
+ this.disposeActiveSelector();
1757
1970
  this.editorContainer.clear();
1758
1971
  this.editorContainer.addChild(this.extensionInput);
1759
1972
  this.ui.setFocus(this.extensionInput);
@@ -1783,6 +1996,7 @@ export class InteractiveMode {
1783
1996
  this.hideExtensionEditor();
1784
1997
  resolve(undefined);
1785
1998
  }, undefined, this.settingsManager.getExternalEditorCommand());
1999
+ this.disposeActiveSelector();
1786
2000
  this.editorContainer.clear();
1787
2001
  this.editorContainer.addChild(this.extensionEditor);
1788
2002
  this.ui.setFocus(this.extensionEditor);
@@ -1807,6 +2021,7 @@ export class InteractiveMode {
1807
2021
  this.editorComponentFactory = factory;
1808
2022
  // Save text from current editor before switching
1809
2023
  const currentText = this.editor.getText();
2024
+ this.disposeActiveSelector();
1810
2025
  this.editorContainer.clear();
1811
2026
  if (factory) {
1812
2027
  // Create the custom editor with tui, theme, and keybindings
@@ -1823,6 +2038,9 @@ export class InteractiveMode {
1823
2038
  if (newEditor.setPaddingX !== undefined) {
1824
2039
  newEditor.setPaddingX(this.defaultEditor.getPaddingX());
1825
2040
  }
2041
+ if (newEditor.setAutocompleteMaxVisible !== undefined) {
2042
+ newEditor.setAutocompleteMaxVisible(this.defaultEditor.getAutocompleteMaxVisible());
2043
+ }
1826
2044
  // Set autocomplete if supported
1827
2045
  if (newEditor.setAutocompleteProvider && this.autocompleteProvider) {
1828
2046
  newEditor.setAutocompleteProvider(this.autocompleteProvider);
@@ -1927,6 +2145,7 @@ export class InteractiveMode {
1927
2145
  options?.onHandle?.(handle);
1928
2146
  }
1929
2147
  else {
2148
+ this.disposeActiveSelector();
1930
2149
  this.editorContainer.clear();
1931
2150
  this.editorContainer.addChild(component);
1932
2151
  this.ui.setFocus(component);
@@ -2013,7 +2232,7 @@ export class InteractiveMode {
2013
2232
  this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2014
2233
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2015
2234
  this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
2016
- this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
2235
+ this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true }));
2017
2236
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2018
2237
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
2019
2238
  this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
@@ -2033,13 +2252,29 @@ export class InteractiveMode {
2033
2252
  void this.handleClipboardPaste();
2034
2253
  };
2035
2254
  }
2255
+ async handleRightClickPaste() {
2256
+ const target = this.renderer.getFocusedComponent();
2257
+ const handleInput = target?.handleInput;
2258
+ if (!target || !handleInput)
2259
+ return;
2260
+ try {
2261
+ const text = await readClipboardText();
2262
+ if (!text || this.renderer.getFocusedComponent() !== target)
2263
+ return;
2264
+ handleInput.call(target, `\x1b[200~${text}\x1b[201~`);
2265
+ this.ui.requestRender();
2266
+ }
2267
+ catch {
2268
+ // Silently ignore clipboard errors (may not have permission, etc.)
2269
+ }
2270
+ }
2036
2271
  async handleClipboardPaste() {
2037
2272
  try {
2038
2273
  const image = await readClipboardImage();
2039
2274
  if (image) {
2040
2275
  const tmpDir = os.tmpdir();
2041
2276
  const ext = extensionForImageMimeType(image.mimeType) ?? "png";
2042
- const fileName = `humain-terminal-clipboard-${crypto.randomUUID()}.${ext}`;
2277
+ const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
2043
2278
  const filePath = path.join(tmpDir, fileName);
2044
2279
  fs.writeFileSync(filePath, Buffer.from(image.bytes));
2045
2280
  this.editor.insertTextAtCursor?.(filePath);
@@ -2175,6 +2410,11 @@ export class InteractiveMode {
2175
2410
  this.editor.setText("");
2176
2411
  return;
2177
2412
  }
2413
+ if (text === "/dementedelves") {
2414
+ this.handleDementedDelves();
2415
+ this.editor.setText("");
2416
+ return;
2417
+ }
2178
2418
  if (text === "/resume") {
2179
2419
  this.showSessionSelector();
2180
2420
  this.editor.setText("");
@@ -2296,17 +2536,17 @@ export class InteractiveMode {
2296
2536
  this.ui.requestRender();
2297
2537
  }
2298
2538
  else if (event.message.role === "assistant") {
2299
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
2539
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
2300
2540
  this.streamingMessage = event.message;
2301
2541
  this.chatContainer.addChild(this.streamingComponent);
2302
- this.streamingComponent.updateContent(this.streamingMessage);
2542
+ this.streamingComponent.updateContent(this.streamingMessage, true);
2303
2543
  this.ui.requestRender();
2304
2544
  }
2305
2545
  break;
2306
2546
  case "message_update":
2307
2547
  if (this.streamingComponent && event.message.role === "assistant") {
2308
2548
  this.streamingMessage = event.message;
2309
- this.streamingComponent.updateContent(this.streamingMessage);
2549
+ this.streamingComponent.updateContent(this.streamingMessage, true);
2310
2550
  for (const content of this.streamingMessage.content) {
2311
2551
  if (content.type === "toolCall") {
2312
2552
  if (!this.pendingTools.has(content.id)) {
@@ -2343,7 +2583,7 @@ export class InteractiveMode {
2343
2583
  : "Operation aborted";
2344
2584
  this.streamingMessage.errorMessage = errorMessage;
2345
2585
  }
2346
- this.streamingComponent.updateContent(this.streamingMessage);
2586
+ this.streamingComponent.updateContent(this.streamingMessage, false);
2347
2587
  if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
2348
2588
  if (!errorMessage) {
2349
2589
  errorMessage = this.streamingMessage.errorMessage || "Error";
@@ -2617,12 +2857,12 @@ export class InteractiveMode {
2617
2857
  // Render user message separately if present
2618
2858
  if (skillBlock.userMessage) {
2619
2859
  this.chatContainer.addChild(new Spacer(1));
2620
- const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad);
2860
+ const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
2621
2861
  this.chatContainer.addChild(userComponent);
2622
2862
  }
2623
2863
  }
2624
2864
  else {
2625
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad);
2865
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
2626
2866
  this.chatContainer.addChild(userComponent);
2627
2867
  }
2628
2868
  if (options?.populateHistory) {
@@ -2632,7 +2872,7 @@ export class InteractiveMode {
2632
2872
  break;
2633
2873
  }
2634
2874
  case "assistant": {
2635
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
2875
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
2636
2876
  this.chatContainer.addChild(assistantComponent);
2637
2877
  break;
2638
2878
  }
@@ -2784,7 +3024,7 @@ export class InteractiveMode {
2784
3024
  if (this.chatContainer.children.length > 0) {
2785
3025
  this.chatContainer.addChild(new Spacer(1));
2786
3026
  }
2787
- this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart ${APP_NAME}.`), 1, 0));
3027
+ this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart pi.`), 1, 0));
2788
3028
  }
2789
3029
  async getUserInput() {
2790
3030
  const queuedInput = this.pendingUserInputs.shift();
@@ -2897,7 +3137,7 @@ export class InteractiveMode {
2897
3137
  this.ui.stop();
2898
3138
  }
2899
3139
  catch { }
2900
- console.error(`${APP_NAME} exiting due to uncaughtException:`);
3140
+ console.error("pi exiting due to uncaughtException:");
2901
3141
  console.error(error);
2902
3142
  process.exit(1);
2903
3143
  }
@@ -2939,7 +3179,7 @@ export class InteractiveMode {
2939
3179
  this.signalCleanupHandlers.push(() => process.stderr.off("error", terminalErrorHandler));
2940
3180
  // Restore the terminal before the process dies on any uncaught throw.
2941
3181
  // Without this, an unhandled exception from extension code (or anywhere
2942
- // in the terminal agent) leaves the terminal in raw mode with no cursor.
3182
+ // in pi) leaves the terminal in raw mode with no cursor.
2943
3183
  const uncaughtExceptionHandler = (error) => this.uncaughtCrash(error);
2944
3184
  process.prependListener("uncaughtException", uncaughtExceptionHandler);
2945
3185
  this.signalCleanupHandlers.push(() => process.off("uncaughtException", uncaughtExceptionHandler));
@@ -3066,6 +3306,8 @@ export class InteractiveMode {
3066
3306
  this.setToolsExpanded(!this.toolOutputExpanded);
3067
3307
  }
3068
3308
  setToolsExpanded(expanded) {
3309
+ if (expanded === this.toolOutputExpanded)
3310
+ return;
3069
3311
  this.toolOutputExpanded = expanded;
3070
3312
  const activeHeader = this.customHeader ?? this.builtInHeader;
3071
3313
  if (isExpandable(activeHeader)) {
@@ -3121,7 +3363,7 @@ export class InteractiveMode {
3121
3363
  }
3122
3364
  showError(errorMessage) {
3123
3365
  this.chatContainer.addChild(new Spacer(1));
3124
- this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
3366
+ this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), this.outputPad, 0));
3125
3367
  this.ui.requestRender();
3126
3368
  }
3127
3369
  showWarning(warningMessage) {
@@ -3132,7 +3374,7 @@ export class InteractiveMode {
3132
3374
  showNewVersionNotification(release) {
3133
3375
  const action = theme.fg("accent", `${APP_NAME} update`);
3134
3376
  const updateInstruction = theme.fg("muted", `New version ${release.version} is available. Run `) + action;
3135
- const changelogUrl = getChangelogUrl();
3377
+ const changelogUrl = "https://pi.dev/changelog";
3136
3378
  const changelogLink = getCapabilities().hyperlinks
3137
3379
  ? hyperlink(theme.fg("accent", changelogUrl), changelogUrl)
3138
3380
  : theme.fg("accent", changelogUrl);
@@ -3331,25 +3573,43 @@ export class InteractiveMode {
3331
3573
  // =========================================================================
3332
3574
  // Selectors
3333
3575
  // =========================================================================
3576
+ disposeActiveSelector() {
3577
+ const dispose = this.activeSelectorDispose;
3578
+ this.activeSelectorToken = undefined;
3579
+ this.activeSelectorDispose = undefined;
3580
+ dispose?.();
3581
+ }
3334
3582
  /**
3335
3583
  * Shows a selector component in place of the editor.
3336
3584
  * @param create Factory that receives a `done` callback and returns the component and focus target
3337
3585
  */
3338
3586
  showSelector(create) {
3587
+ const token = {};
3588
+ let dispose;
3339
3589
  const done = () => {
3590
+ dispose?.();
3591
+ if (this.activeSelectorToken !== token)
3592
+ return;
3593
+ this.activeSelectorToken = undefined;
3594
+ this.activeSelectorDispose = undefined;
3340
3595
  this.editorContainer.clear();
3341
3596
  this.editorContainer.addChild(this.editor);
3342
3597
  this.ui.setFocus(this.editor);
3343
3598
  };
3344
- const { component, focus } = create(done);
3599
+ const created = create(done);
3600
+ dispose = created.dispose;
3601
+ this.disposeActiveSelector();
3602
+ this.activeSelectorToken = token;
3603
+ this.activeSelectorDispose = dispose;
3345
3604
  this.editorContainer.clear();
3346
- this.editorContainer.addChild(component);
3347
- this.ui.setFocus(focus);
3605
+ this.editorContainer.addChild(created.component);
3606
+ this.ui.setFocus(created.focus);
3348
3607
  this.ui.requestRender();
3349
3608
  }
3350
3609
  showSettingsSelector() {
3351
3610
  this.showSelector((done) => {
3352
- const selector = new SettingsSelectorComponent({
3611
+ let selector;
3612
+ selector = new SettingsSelectorComponent({
3353
3613
  autoCompact: this.session.autoCompactionEnabled,
3354
3614
  showImages: this.settingsManager.getShowImages(),
3355
3615
  imageWidthCells: this.settingsManager.getImageWidthCells(),
@@ -3366,6 +3626,7 @@ export class InteractiveMode {
3366
3626
  terminalTheme: this.themeController.getTerminalTheme(),
3367
3627
  availableThemes: getAvailableThemes(),
3368
3628
  hideThinkingBlock: this.hideThinkingBlock,
3629
+ mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
3369
3630
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
3370
3631
  enableInstallTelemetry: this.settingsManager.getEnableInstallTelemetry(),
3371
3632
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
@@ -3379,6 +3640,8 @@ export class InteractiveMode {
3379
3640
  quietStartup: this.settingsManager.getQuietStartup(),
3380
3641
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3381
3642
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3643
+ tuiMode: this.ui.mode,
3644
+ fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3382
3645
  warnings: this.settingsManager.getWarnings(),
3383
3646
  }, {
3384
3647
  onAutoCompactChange: (enabled) => {
@@ -3447,6 +3710,11 @@ export class InteractiveMode {
3447
3710
  this.chatContainer.clear();
3448
3711
  this.rebuildChatFromMessages();
3449
3712
  },
3713
+ onMermaidRenderingModeChange: (mode) => {
3714
+ this.settingsManager.setMermaidRenderingMode(mode);
3715
+ this.chatContainer.invalidate();
3716
+ this.ui.requestRender();
3717
+ },
3450
3718
  onShowCacheMissNoticesChange: (shown) => {
3451
3719
  this.settingsManager.setShowCacheMissNotices(shown);
3452
3720
  this.rebuildChatFromMessages();
@@ -3516,6 +3784,21 @@ export class InteractiveMode {
3516
3784
  onShowTerminalProgressChange: (enabled) => {
3517
3785
  this.settingsManager.setShowTerminalProgress(enabled);
3518
3786
  },
3787
+ onTuiModeChange: (mode) => {
3788
+ if (!this.switchTuiMode(mode)) {
3789
+ selector?.getSettingsList().updateValue("tui-mode", this.ui.mode);
3790
+ this.showStatus("Close active overlays before changing TUI mode");
3791
+ return;
3792
+ }
3793
+ this.settingsManager.setTuiMode(mode);
3794
+ if (!this.activeStatusIndicator)
3795
+ this.statusContainer.clear();
3796
+ this.showStatus(`TUI mode: ${mode}`);
3797
+ },
3798
+ onFullscreenScrollbarChange: (mode) => {
3799
+ this.settingsManager.setFullscreenScrollbar(mode);
3800
+ this.applyFullscreenScrollbarSetting();
3801
+ },
3519
3802
  onWarningsChange: (warnings) => {
3520
3803
  this.settingsManager.setWarnings(warnings);
3521
3804
  },
@@ -3550,20 +3833,37 @@ export class InteractiveMode {
3550
3833
  this.showModelSelector(searchTerm);
3551
3834
  }
3552
3835
  async findExactModelMatch(searchTerm) {
3553
- const models = await this.getModelCandidates();
3554
- return findExactModelReferenceMatch(searchTerm, models);
3555
- }
3556
- async getModelCandidates() {
3557
- if (this.session.scopedModels.length > 0) {
3558
- return this.session.scopedModels.map((scoped) => scoped.model);
3559
- }
3836
+ const cachedModels = this.session.scopedModels.length > 0
3837
+ ? this.session.scopedModels.map((scoped) => scoped.model)
3838
+ : [...this.session.modelRuntime.getAvailableSnapshot()];
3839
+ const cachedMatch = findExactModelReferenceMatch(searchTerm, cachedModels);
3840
+ if (cachedMatch || this.session.scopedModels.length > 0)
3841
+ return cachedMatch;
3842
+ this.showStatus("Refreshing model catalogs…");
3843
+ const controller = new AbortController();
3844
+ let timedOut = false;
3845
+ const timeout = setTimeout(() => {
3846
+ timedOut = true;
3847
+ controller.abort();
3848
+ }, 15_000);
3560
3849
  try {
3561
- await this.session.modelRuntime.refresh();
3562
- return [...(await this.session.modelRuntime.getAvailable())];
3850
+ const result = await this.session.modelRuntime.refresh({ signal: controller.signal });
3851
+ if (result.aborted && timedOut) {
3852
+ this.showWarning("Model refresh timed out; searching cached models.");
3853
+ }
3854
+ else if (result.errors.size > 0) {
3855
+ this.showWarning(`Could not refresh ${[...result.errors.keys()].join(", ")}; searching cached models.`);
3856
+ }
3563
3857
  }
3564
- catch {
3565
- return [];
3858
+ catch (error) {
3859
+ this.showWarning(timedOut
3860
+ ? "Model refresh timed out; searching cached models."
3861
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`);
3566
3862
  }
3863
+ finally {
3864
+ clearTimeout(timeout);
3865
+ }
3866
+ return findExactModelReferenceMatch(searchTerm, [...this.session.modelRuntime.getAvailableSnapshot()]);
3567
3867
  }
3568
3868
  /** Update the footer's available provider count from the current snapshot without refreshing catalogs. */
3569
3869
  updateAvailableProviderCount() {
@@ -3635,7 +3935,7 @@ export class InteractiveMode {
3635
3935
  onSelect: (selection) => {
3636
3936
  trustStore.setMany(selection.updates);
3637
3937
  done();
3638
- this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
3938
+ this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
3639
3939
  },
3640
3940
  onCancel: () => {
3641
3941
  done();
@@ -3665,73 +3965,67 @@ export class InteractiveMode {
3665
3965
  done();
3666
3966
  this.ui.requestRender();
3667
3967
  }, initialSearchInput);
3668
- return { component: selector, focus: selector };
3968
+ return { component: selector, focus: selector, dispose: () => selector.dispose() };
3669
3969
  });
3670
3970
  }
3671
- async showModelsSelector() {
3672
- // Get all available models
3673
- await this.session.modelRuntime.refresh();
3674
- const allModels = [...(await this.session.modelRuntime.getAvailable())];
3675
- const allModelIds = new Set(allModels.map((model) => `${model.provider}/${model.id}`));
3971
+ showModelsSelector() {
3972
+ let availableModels = [...this.session.modelRuntime.getAvailableSnapshot()];
3973
+ let availableModelIds = new Set(availableModels.map((model) => `${model.provider}/${model.id}`));
3676
3974
  const configuredPatterns = this.settingsManager.getEnabledModels();
3677
3975
  const sessionScopedModels = this.session.scopedModels;
3678
- if (allModels.length === 0 && !configuredPatterns?.length && sessionScopedModels.length === 0) {
3679
- this.showStatus("No models available");
3680
- return;
3681
- }
3682
- const configuredScope = configuredPatterns?.length
3683
- ? await resolveModelScopeWithDiagnostics(configuredPatterns, this.session.modelRuntime)
3684
- : undefined;
3685
- // Check if session has scoped models (from previous session-only changes or CLI --models)
3686
- const hasSessionScope = sessionScopedModels.length > 0;
3687
- // Build enabled model IDs from session state or settings
3688
- let currentEnabledIds = null;
3689
- if (hasSessionScope) {
3690
- // Use current session's scoped models
3691
- currentEnabledIds = sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3692
- }
3693
- else if (configuredScope) {
3694
- currentEnabledIds = configuredScope.scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3695
- }
3696
- for (const diagnostic of configuredScope?.diagnostics ?? []) {
3697
- if (diagnostic.code !== "no-match")
3698
- continue;
3699
- currentEnabledIds ??= [];
3700
- if (!currentEnabledIds.includes(diagnostic.pattern))
3701
- currentEnabledIds.push(diagnostic.pattern);
3702
- }
3703
- // Helper to update session's scoped models (session-only, no persist)
3704
- const updateSessionModels = async (enabledIds) => {
3976
+ const configuredEnabledIds = (models) => {
3977
+ if (!configuredPatterns?.length)
3978
+ return null;
3979
+ const resolved = resolveModelScopeFromModels(configuredPatterns, models);
3980
+ const ids = resolved.scopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`);
3981
+ for (const diagnostic of resolved.diagnostics) {
3982
+ if (diagnostic.code === "no-match" && !ids.includes(diagnostic.pattern))
3983
+ ids.push(diagnostic.pattern);
3984
+ }
3985
+ return ids;
3986
+ };
3987
+ let currentEnabledIds = sessionScopedModels.length > 0
3988
+ ? sessionScopedModels.map((scoped) => `${scoped.model.provider}/${scoped.model.id}`)
3989
+ : configuredEnabledIds(availableModels);
3990
+ let selectionChanged = false;
3991
+ const updateSessionModels = (enabledIds) => {
3705
3992
  currentEnabledIds = enabledIds === null ? null : [...enabledIds];
3706
- const hasEnabledAvailableModel = enabledIds?.some((id) => allModelIds.has(id)) ?? false;
3707
- const allAvailableModelsEnabled = enabledIds !== null && [...allModelIds].every((id) => enabledIds.includes(id));
3993
+ const hasEnabledAvailableModel = enabledIds?.some((id) => availableModelIds.has(id)) ?? false;
3994
+ const allAvailableModelsEnabled = enabledIds !== null && [...availableModelIds].every((id) => enabledIds.includes(id));
3708
3995
  if (enabledIds && hasEnabledAvailableModel && !allAvailableModelsEnabled) {
3709
- const newScopedModels = await resolveModelScope(enabledIds, this.session.modelRuntime);
3710
- this.session.setScopedModels(newScopedModels.map((sm) => ({
3711
- model: sm.model,
3712
- thinkingLevel: sm.thinkingLevel,
3996
+ const newScopedModels = resolveModelScopeFromModels(enabledIds, availableModels).scopedModels;
3997
+ this.session.setScopedModels(newScopedModels.map((scoped) => ({
3998
+ model: scoped.model,
3999
+ thinkingLevel: scoped.thinkingLevel,
3713
4000
  })));
3714
4001
  }
3715
4002
  else {
3716
- // All enabled or none enabled = no filter
3717
4003
  this.session.setScopedModels([]);
3718
4004
  }
3719
- await this.updateAvailableProviderCount();
4005
+ this.updateAvailableProviderCount();
3720
4006
  this.ui.requestRender();
3721
4007
  };
3722
4008
  this.showSelector((done) => {
4009
+ let disposed = false;
4010
+ let timedOut = false;
4011
+ const controller = new AbortController();
4012
+ const timeout = setTimeout(() => {
4013
+ timedOut = true;
4014
+ controller.abort();
4015
+ }, 15_000);
3723
4016
  const selector = new ScopedModelsSelectorComponent({
3724
- allModels,
4017
+ allModels: availableModels,
3725
4018
  enabledModelIds: currentEnabledIds,
4019
+ refreshStatus: "Refreshing model catalogs…",
3726
4020
  }, {
3727
- onChange: async (enabledIds) => {
3728
- await updateSessionModels(enabledIds);
4021
+ onChange: (enabledIds) => {
4022
+ selectionChanged = true;
4023
+ updateSessionModels(enabledIds);
3729
4024
  },
3730
4025
  onPersist: (enabledIds) => {
3731
- // Persist to settings
3732
4026
  const allEnabled = enabledIds !== null &&
3733
- enabledIds.length === allModels.length &&
3734
- enabledIds.every((id) => allModelIds.has(id));
4027
+ enabledIds.length === availableModels.length &&
4028
+ enabledIds.every((id) => availableModelIds.has(id));
3735
4029
  const newPatterns = enabledIds === null || allEnabled ? undefined : enabledIds;
3736
4030
  this.settingsManager.setEnabledModels(newPatterns ? [...newPatterns] : undefined);
3737
4031
  this.showStatus("Model selection saved to settings");
@@ -3741,7 +4035,51 @@ export class InteractiveMode {
3741
4035
  this.ui.requestRender();
3742
4036
  },
3743
4037
  });
3744
- return { component: selector, focus: selector };
4038
+ void this.session.modelRuntime
4039
+ .refresh({ signal: controller.signal })
4040
+ .then((result) => {
4041
+ if (disposed)
4042
+ return;
4043
+ availableModels = [...this.session.modelRuntime.getAvailableSnapshot()];
4044
+ availableModelIds = new Set(availableModels.map((model) => `${model.provider}/${model.id}`));
4045
+ if (!selectionChanged && sessionScopedModels.length === 0) {
4046
+ currentEnabledIds = configuredEnabledIds(availableModels);
4047
+ selector.updateModels(availableModels, currentEnabledIds);
4048
+ }
4049
+ else {
4050
+ selector.updateModels(availableModels);
4051
+ }
4052
+ if (currentEnabledIds !== null)
4053
+ updateSessionModels(currentEnabledIds);
4054
+ if (result.aborted && timedOut) {
4055
+ selector.setRefreshStatus("Model refresh timed out; showing cached models.", "warning");
4056
+ }
4057
+ else if (result.errors.size > 0) {
4058
+ selector.setRefreshStatus(`Could not refresh ${[...result.errors.keys()].join(", ")}; showing cached models.`, "warning");
4059
+ }
4060
+ else {
4061
+ selector.setRefreshStatus("Model catalogs refreshed.", "success");
4062
+ }
4063
+ this.ui.requestRender();
4064
+ })
4065
+ .catch((error) => {
4066
+ if (disposed)
4067
+ return;
4068
+ selector.setRefreshStatus(timedOut
4069
+ ? "Model refresh timed out; showing cached models."
4070
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`, "warning");
4071
+ this.ui.requestRender();
4072
+ })
4073
+ .finally(() => clearTimeout(timeout));
4074
+ return {
4075
+ component: selector,
4076
+ focus: selector,
4077
+ dispose: () => {
4078
+ disposed = true;
4079
+ clearTimeout(timeout);
4080
+ controller.abort();
4081
+ },
4082
+ };
3745
4083
  });
3746
4084
  }
3747
4085
  showUserMessageSelector() {
@@ -4003,7 +4341,7 @@ export class InteractiveMode {
4003
4341
  return options.sort((a, b) => a.name.localeCompare(b.name));
4004
4342
  }
4005
4343
  async getLogoutProviderOptions() {
4006
- return (await this.session.modelRuntime.listCredentials())
4344
+ return (await this.session.modelRuntime.listCredentials({ signal: AbortSignal.timeout(15_000) }))
4007
4345
  .map(({ providerId, type }) => ({
4008
4346
  id: providerId,
4009
4347
  name: this.session.modelRuntime.getProvider(providerId)?.name ?? providerId,
@@ -4021,7 +4359,6 @@ export class InteractiveMode {
4021
4359
  provider.name.toLowerCase() === normalizedProviderRef);
4022
4360
  }
4023
4361
  async handleLoginCommand(providerRef) {
4024
- await this.session.modelRuntime.getAvailable();
4025
4362
  if (!providerRef) {
4026
4363
  this.showLoginAuthTypeSelector();
4027
4364
  return;
@@ -4135,7 +4472,14 @@ export class InteractiveMode {
4135
4472
  this.showLoginAuthTypeSelector();
4136
4473
  return;
4137
4474
  }
4138
- const providerOptions = await this.getLogoutProviderOptions();
4475
+ let providerOptions;
4476
+ try {
4477
+ providerOptions = await this.getLogoutProviderOptions();
4478
+ }
4479
+ catch (error) {
4480
+ this.showError(`Could not read stored credentials: ${error instanceof Error ? error.message : String(error)}`);
4481
+ return;
4482
+ }
4139
4483
  if (providerOptions.length === 0) {
4140
4484
  this.showStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");
4141
4485
  return;
@@ -4148,7 +4492,9 @@ export class InteractiveMode {
4148
4492
  return;
4149
4493
  }
4150
4494
  try {
4151
- await this.session.modelRuntime.logout(providerOption.id);
4495
+ await this.session.modelRuntime.logout(providerOption.id, {
4496
+ signal: AbortSignal.timeout(15_000),
4497
+ });
4152
4498
  await this.updateAvailableProviderCount();
4153
4499
  const message = providerOption.authType === "oauth"
4154
4500
  ? `Logged out of ${providerOption.name}`
@@ -4156,7 +4502,10 @@ export class InteractiveMode {
4156
4502
  this.showStatus(message);
4157
4503
  }
4158
4504
  catch (error) {
4159
- this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
4505
+ const message = error instanceof Error ? error.message : String(error);
4506
+ this.showError(error instanceof CredentialSynchronizationError
4507
+ ? `Credentials removed for ${providerOption.name}, but local model state could not be synchronized: ${message}`
4508
+ : `Logout failed: ${message}`);
4160
4509
  }
4161
4510
  }, () => {
4162
4511
  done();
@@ -4166,12 +4515,11 @@ export class InteractiveMode {
4166
4515
  });
4167
4516
  }
4168
4517
  async completeProviderAuthentication(providerId, providerName, authType, previousModel) {
4169
- await this.session.modelRuntime.getAvailable();
4170
4518
  const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
4171
4519
  let selectedModel;
4172
4520
  let selectionError;
4173
4521
  if (isUnknownModel(previousModel)) {
4174
- const availableModels = await this.session.modelRuntime.getAvailable();
4522
+ const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4175
4523
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4176
4524
  if (!hasDefaultModelProvider(providerId)) {
4177
4525
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
@@ -4214,6 +4562,25 @@ export class InteractiveMode {
4214
4562
  void this.maybeWarnAboutAnthropicSubscriptionAuth();
4215
4563
  }
4216
4564
  }
4565
+ const controller = new AbortController();
4566
+ const timeout = setTimeout(() => controller.abort(), 15_000);
4567
+ void this.session.modelRuntime
4568
+ .refresh({ providers: [providerId], signal: controller.signal })
4569
+ .then((result) => {
4570
+ if (result.aborted) {
4571
+ this.showWarning(`${actionLabel}, but its model catalog refresh timed out; using cached models.`);
4572
+ }
4573
+ else if (result.errors.size > 0) {
4574
+ this.showWarning(`${actionLabel}, but its model catalog could not be refreshed; using cached models.`);
4575
+ }
4576
+ this.updateAvailableProviderCount();
4577
+ this.footer.invalidate();
4578
+ this.ui.requestRender();
4579
+ })
4580
+ .catch((error) => {
4581
+ this.showWarning(`${actionLabel}, but its model catalog could not be refreshed: ${error instanceof Error ? error.message : String(error)}`);
4582
+ })
4583
+ .finally(() => clearTimeout(timeout));
4217
4584
  }
4218
4585
  showAmbientAuthDialog(providerOption) {
4219
4586
  const restoreEditor = () => {
@@ -4259,7 +4626,10 @@ export class InteractiveMode {
4259
4626
  catch (error) {
4260
4627
  restoreEditor();
4261
4628
  const errorMsg = error instanceof Error ? error.message : String(error);
4262
- if (errorMsg !== "Login cancelled") {
4629
+ if (error instanceof CredentialSynchronizationError) {
4630
+ this.showError(`Saved API key for ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
4631
+ }
4632
+ else if (errorMsg !== "Login cancelled") {
4263
4633
  this.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);
4264
4634
  }
4265
4635
  }
@@ -4362,7 +4732,10 @@ export class InteractiveMode {
4362
4732
  catch (error) {
4363
4733
  restoreEditor();
4364
4734
  const errorMsg = error instanceof Error ? error.message : String(error);
4365
- if (errorMsg !== "Login cancelled") {
4735
+ if (error instanceof CredentialSynchronizationError) {
4736
+ this.showError(`Logged in to ${providerName}, but local model state could not be synchronized: ${errorMsg}`);
4737
+ }
4738
+ else if (errorMsg !== "Login cancelled") {
4366
4739
  this.showError(`Failed to login to ${providerName}: ${errorMsg}`);
4367
4740
  }
4368
4741
  }
@@ -4413,7 +4786,6 @@ export class InteractiveMode {
4413
4786
  try {
4414
4787
  await this.session.reload({ beforeSessionStart: restoreChatBeforeSessionStart });
4415
4788
  restoreChatBeforeSessionStart();
4416
- configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
4417
4789
  this.keybindings.reload();
4418
4790
  const activeHeader = this.customHeader ?? this.builtInHeader;
4419
4791
  if (isExpandable(activeHeader)) {
@@ -4421,20 +4793,7 @@ export class InteractiveMode {
4421
4793
  }
4422
4794
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
4423
4795
  await this.themeController.applyFromSettings();
4424
- const editorPaddingX = this.settingsManager.getEditorPaddingX();
4425
- const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
4426
- this.defaultEditor.setPaddingX(editorPaddingX);
4427
- this.defaultEditor.setAutocompleteMaxVisible(autocompleteMaxVisible);
4428
- if (this.editor !== this.defaultEditor) {
4429
- this.editor.setPaddingX?.(editorPaddingX);
4430
- this.editor.setAutocompleteMaxVisible?.(autocompleteMaxVisible);
4431
- }
4432
- this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
4433
- const clearOnShrink = this.settingsManager.getClearOnShrink();
4434
- this.ui.setClearOnShrink(clearOnShrink);
4435
- if (!clearOnShrink && !this.activeStatusIndicator) {
4436
- this.statusContainer.clear();
4437
- }
4796
+ this.applyRuntimeSettings();
4438
4797
  this.setupAutocompleteProvider();
4439
4798
  const runner = this.session.extensionRunner;
4440
4799
  this.setupExtensionShortcuts(runner);
@@ -4630,7 +4989,7 @@ export class InteractiveMode {
4630
4989
  }
4631
4990
  }
4632
4991
  }
4633
- async handleCopyCommand() {
4992
+ async handleCopyCommand(options = {}) {
4634
4993
  const text = this.session.getLastAssistantText();
4635
4994
  if (!text) {
4636
4995
  this.showError("No agent messages to copy yet.");
@@ -4638,7 +4997,12 @@ export class InteractiveMode {
4638
4997
  }
4639
4998
  try {
4640
4999
  await copyToClipboard(text);
4641
- this.showStatus("Copied last agent message to clipboard");
5000
+ if (options.flashConfirmation && this.ui instanceof TuiAltScreen) {
5001
+ this.ui.flash("Copied!");
5002
+ }
5003
+ else {
5004
+ this.showStatus("Copied last agent message to clipboard");
5005
+ }
4642
5006
  }
4643
5007
  catch (error) {
4644
5008
  this.showError(error instanceof Error ? error.message : String(error));
@@ -4911,6 +5275,11 @@ export class InteractiveMode {
4911
5275
  this.chatContainer.addChild(new ArminComponent(this.ui));
4912
5276
  this.ui.requestRender();
4913
5277
  }
5278
+ handleDementedDelves() {
5279
+ this.chatContainer.addChild(new Spacer(1));
5280
+ this.chatContainer.addChild(new EarendilAnnouncementComponent());
5281
+ this.ui.requestRender();
5282
+ }
4914
5283
  handleDaxnuts() {
4915
5284
  this.chatContainer.addChild(new Spacer(1));
4916
5285
  this.chatContainer.addChild(new DaxnutsComponent(this.ui));
@@ -4996,6 +5365,7 @@ export class InteractiveMode {
4996
5365
  }
4997
5366
  }
4998
5367
  stop() {
5368
+ this.disposeActiveSelector();
4999
5369
  if (this.settingsManager.getShowTerminalProgress()) {
5000
5370
  this.ui.terminal.setProgress(false);
5001
5371
  }
@@ -5008,7 +5378,7 @@ export class InteractiveMode {
5008
5378
  this.unsubscribe();
5009
5379
  }
5010
5380
  if (this.isInitialized) {
5011
- this.ui.stop();
5381
+ this.stopInteractiveTui();
5012
5382
  this.isInitialized = false;
5013
5383
  }
5014
5384
  this.unregisterSignalHandlers();