wotann 0.5.0 → 0.5.39

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 (1045) hide show
  1. package/dist/agents/background-agent.d.ts.map +1 -1
  2. package/dist/agents/background-agent.js +2 -1
  3. package/dist/agents/background-agent.js.map +1 -1
  4. package/dist/agents/file-scope-policy.d.ts +116 -0
  5. package/dist/agents/file-scope-policy.d.ts.map +1 -0
  6. package/dist/agents/file-scope-policy.js +190 -0
  7. package/dist/agents/file-scope-policy.js.map +1 -0
  8. package/dist/agents/yaml-manifest.d.ts +125 -0
  9. package/dist/agents/yaml-manifest.d.ts.map +1 -0
  10. package/dist/agents/yaml-manifest.js +901 -0
  11. package/dist/agents/yaml-manifest.js.map +1 -0
  12. package/dist/auth/login.d.ts +3 -3
  13. package/dist/auth/login.d.ts.map +1 -1
  14. package/dist/auth/login.js +80 -23
  15. package/dist/auth/login.js.map +1 -1
  16. package/dist/autopilot/completion-oracle.d.ts.map +1 -1
  17. package/dist/autopilot/completion-oracle.js +3 -1
  18. package/dist/autopilot/completion-oracle.js.map +1 -1
  19. package/dist/autopilot/run-manifest.d.ts +90 -0
  20. package/dist/autopilot/run-manifest.d.ts.map +1 -0
  21. package/dist/autopilot/run-manifest.js +261 -0
  22. package/dist/autopilot/run-manifest.js.map +1 -0
  23. package/dist/browser/adaptive-selectors.d.ts +170 -0
  24. package/dist/browser/adaptive-selectors.d.ts.map +1 -0
  25. package/dist/browser/adaptive-selectors.js +317 -0
  26. package/dist/browser/adaptive-selectors.js.map +1 -0
  27. package/dist/browser/humanize-input.d.ts +76 -0
  28. package/dist/browser/humanize-input.d.ts.map +1 -0
  29. package/dist/browser/humanize-input.js +386 -0
  30. package/dist/browser/humanize-input.js.map +1 -0
  31. package/dist/channels/teams.d.ts +41 -19
  32. package/dist/channels/teams.d.ts.map +1 -1
  33. package/dist/channels/teams.js +121 -24
  34. package/dist/channels/teams.js.map +1 -1
  35. package/dist/channels/terminal-mention.d.ts.map +1 -1
  36. package/dist/channels/terminal-mention.js +4 -1
  37. package/dist/channels/terminal-mention.js.map +1 -1
  38. package/dist/cli/commands/blast-radius.d.ts +47 -0
  39. package/dist/cli/commands/blast-radius.d.ts.map +1 -0
  40. package/dist/cli/commands/blast-radius.js +123 -0
  41. package/dist/cli/commands/blast-radius.js.map +1 -0
  42. package/dist/cli/commands/evolve.d.ts +92 -0
  43. package/dist/cli/commands/evolve.d.ts.map +1 -0
  44. package/dist/cli/commands/evolve.js +336 -0
  45. package/dist/cli/commands/evolve.js.map +1 -0
  46. package/dist/cli/commands/learning.d.ts +54 -0
  47. package/dist/cli/commands/learning.d.ts.map +1 -0
  48. package/dist/cli/commands/learning.js +380 -0
  49. package/dist/cli/commands/learning.js.map +1 -0
  50. package/dist/cli/commands/persona.d.ts +42 -0
  51. package/dist/cli/commands/persona.d.ts.map +1 -0
  52. package/dist/cli/commands/persona.js +198 -0
  53. package/dist/cli/commands/persona.js.map +1 -0
  54. package/dist/cli/commands/stuck.d.ts.map +1 -1
  55. package/dist/cli/commands/stuck.js +5 -2
  56. package/dist/cli/commands/stuck.js.map +1 -1
  57. package/dist/cli/commands.d.ts.map +1 -1
  58. package/dist/cli/commands.js +5 -2
  59. package/dist/cli/commands.js.map +1 -1
  60. package/dist/cli/orphan-wires/agent-manifest-cmd.d.ts +17 -0
  61. package/dist/cli/orphan-wires/agent-manifest-cmd.d.ts.map +1 -0
  62. package/dist/cli/orphan-wires/agent-manifest-cmd.js +99 -0
  63. package/dist/cli/orphan-wires/agent-manifest-cmd.js.map +1 -0
  64. package/dist/cli/orphan-wires/arena-cmd.d.ts +26 -0
  65. package/dist/cli/orphan-wires/arena-cmd.d.ts.map +1 -0
  66. package/dist/cli/orphan-wires/arena-cmd.js +167 -0
  67. package/dist/cli/orphan-wires/arena-cmd.js.map +1 -0
  68. package/dist/cli/orphan-wires/artifact-cmd.d.ts +17 -0
  69. package/dist/cli/orphan-wires/artifact-cmd.d.ts.map +1 -0
  70. package/dist/cli/orphan-wires/artifact-cmd.js +175 -0
  71. package/dist/cli/orphan-wires/artifact-cmd.js.map +1 -0
  72. package/dist/cli/orphan-wires/demo-cmd.d.ts +25 -0
  73. package/dist/cli/orphan-wires/demo-cmd.d.ts.map +1 -0
  74. package/dist/cli/orphan-wires/demo-cmd.js +164 -0
  75. package/dist/cli/orphan-wires/demo-cmd.js.map +1 -0
  76. package/dist/cli/orphan-wires/extras-cmd.d.ts +20 -0
  77. package/dist/cli/orphan-wires/extras-cmd.d.ts.map +1 -0
  78. package/dist/cli/orphan-wires/extras-cmd.js +289 -0
  79. package/dist/cli/orphan-wires/extras-cmd.js.map +1 -0
  80. package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts +15 -0
  81. package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts.map +1 -0
  82. package/dist/cli/orphan-wires/harness-introspect-cmd.js +36 -0
  83. package/dist/cli/orphan-wires/harness-introspect-cmd.js.map +1 -0
  84. package/dist/cli/orphan-wires/index.d.ts.map +1 -1
  85. package/dist/cli/orphan-wires/index.js +30 -0
  86. package/dist/cli/orphan-wires/index.js.map +1 -1
  87. package/dist/cli/orphan-wires/integrations-cmd.d.ts +38 -0
  88. package/dist/cli/orphan-wires/integrations-cmd.d.ts.map +1 -0
  89. package/dist/cli/orphan-wires/integrations-cmd.js +345 -0
  90. package/dist/cli/orphan-wires/integrations-cmd.js.map +1 -0
  91. package/dist/cli/orphan-wires/mcp-scaffold-cmd.d.ts +18 -0
  92. package/dist/cli/orphan-wires/mcp-scaffold-cmd.d.ts.map +1 -0
  93. package/dist/cli/orphan-wires/mcp-scaffold-cmd.js +127 -0
  94. package/dist/cli/orphan-wires/mcp-scaffold-cmd.js.map +1 -0
  95. package/dist/cli/orphan-wires/memory-consolidate-cmd.d.ts +21 -0
  96. package/dist/cli/orphan-wires/memory-consolidate-cmd.d.ts.map +1 -0
  97. package/dist/cli/orphan-wires/memory-consolidate-cmd.js +86 -0
  98. package/dist/cli/orphan-wires/memory-consolidate-cmd.js.map +1 -0
  99. package/dist/cli/orphan-wires/patch-cmd.d.ts +29 -0
  100. package/dist/cli/orphan-wires/patch-cmd.d.ts.map +1 -0
  101. package/dist/cli/orphan-wires/patch-cmd.js +150 -0
  102. package/dist/cli/orphan-wires/patch-cmd.js.map +1 -0
  103. package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts +28 -0
  104. package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts.map +1 -0
  105. package/dist/cli/orphan-wires/redteam-scan-cmd.js +169 -0
  106. package/dist/cli/orphan-wires/redteam-scan-cmd.js.map +1 -0
  107. package/dist/cli/orphan-wires/scrape-adapt-cmd.d.ts +21 -0
  108. package/dist/cli/orphan-wires/scrape-adapt-cmd.d.ts.map +1 -0
  109. package/dist/cli/orphan-wires/scrape-adapt-cmd.js +70 -0
  110. package/dist/cli/orphan-wires/scrape-adapt-cmd.js.map +1 -0
  111. package/dist/cli/orphan-wires/shell-tier-cmd.d.ts +20 -0
  112. package/dist/cli/orphan-wires/shell-tier-cmd.d.ts.map +1 -0
  113. package/dist/cli/orphan-wires/shell-tier-cmd.js +74 -0
  114. package/dist/cli/orphan-wires/shell-tier-cmd.js.map +1 -0
  115. package/dist/cli/orphan-wires/sub-recipes-cmd.d.ts +18 -0
  116. package/dist/cli/orphan-wires/sub-recipes-cmd.d.ts.map +1 -0
  117. package/dist/cli/orphan-wires/sub-recipes-cmd.js +87 -0
  118. package/dist/cli/orphan-wires/sub-recipes-cmd.js.map +1 -0
  119. package/dist/cli/orphan-wires/webapp-test-cmd.d.ts +20 -0
  120. package/dist/cli/orphan-wires/webapp-test-cmd.d.ts.map +1 -0
  121. package/dist/cli/orphan-wires/webapp-test-cmd.js +115 -0
  122. package/dist/cli/orphan-wires/webapp-test-cmd.js.map +1 -0
  123. package/dist/cli/thin-client.d.ts +17 -10
  124. package/dist/cli/thin-client.d.ts.map +1 -1
  125. package/dist/cli/thin-client.js +108 -26
  126. package/dist/cli/thin-client.js.map +1 -1
  127. package/dist/computer-use/action-history-compactor.d.ts +92 -0
  128. package/dist/computer-use/action-history-compactor.d.ts.map +1 -0
  129. package/dist/computer-use/action-history-compactor.js +204 -0
  130. package/dist/computer-use/action-history-compactor.js.map +1 -0
  131. package/dist/computer-use/action-repertoire.d.ts +8 -0
  132. package/dist/computer-use/action-repertoire.d.ts.map +1 -1
  133. package/dist/computer-use/action-repertoire.js +19 -0
  134. package/dist/computer-use/action-repertoire.js.map +1 -1
  135. package/dist/computer-use/computer-agent.d.ts +232 -2
  136. package/dist/computer-use/computer-agent.d.ts.map +1 -1
  137. package/dist/computer-use/computer-agent.js +558 -19
  138. package/dist/computer-use/computer-agent.js.map +1 -1
  139. package/dist/computer-use/coordinate-scaling.d.ts +104 -0
  140. package/dist/computer-use/coordinate-scaling.d.ts.map +1 -0
  141. package/dist/computer-use/coordinate-scaling.js +175 -0
  142. package/dist/computer-use/coordinate-scaling.js.map +1 -0
  143. package/dist/computer-use/driver-contract.d.ts +158 -0
  144. package/dist/computer-use/driver-contract.d.ts.map +1 -0
  145. package/dist/computer-use/driver-contract.js +471 -0
  146. package/dist/computer-use/driver-contract.js.map +1 -0
  147. package/dist/computer-use/perception-engine.d.ts +39 -0
  148. package/dist/computer-use/perception-engine.d.ts.map +1 -1
  149. package/dist/computer-use/perception-engine.js +27 -27
  150. package/dist/computer-use/perception-engine.js.map +1 -1
  151. package/dist/computer-use/platform-bindings.d.ts.map +1 -1
  152. package/dist/computer-use/platform-bindings.js +216 -2
  153. package/dist/computer-use/platform-bindings.js.map +1 -1
  154. package/dist/computer-use/safe-execute.d.ts +86 -0
  155. package/dist/computer-use/safe-execute.d.ts.map +1 -0
  156. package/dist/computer-use/safe-execute.js +152 -0
  157. package/dist/computer-use/safe-execute.js.map +1 -0
  158. package/dist/context/branch-summarization.d.ts +97 -0
  159. package/dist/context/branch-summarization.d.ts.map +1 -0
  160. package/dist/context/branch-summarization.js +170 -0
  161. package/dist/context/branch-summarization.js.map +1 -0
  162. package/dist/context/compaction.d.ts +16 -1
  163. package/dist/context/compaction.d.ts.map +1 -1
  164. package/dist/context/compaction.js +50 -2
  165. package/dist/context/compaction.js.map +1 -1
  166. package/dist/context/inspector.d.ts +2 -2
  167. package/dist/context/inspector.d.ts.map +1 -1
  168. package/dist/context/inspector.js +4 -3
  169. package/dist/context/inspector.js.map +1 -1
  170. package/dist/core/agent-bridge.d.ts.map +1 -1
  171. package/dist/core/agent-bridge.js +200 -69
  172. package/dist/core/agent-bridge.js.map +1 -1
  173. package/dist/core/config.d.ts.map +1 -1
  174. package/dist/core/config.js +7 -0
  175. package/dist/core/config.js.map +1 -1
  176. package/dist/core/mode-model-preference.d.ts +95 -0
  177. package/dist/core/mode-model-preference.d.ts.map +1 -0
  178. package/dist/core/mode-model-preference.js +170 -0
  179. package/dist/core/mode-model-preference.js.map +1 -0
  180. package/dist/core/prompt-override.d.ts.map +1 -1
  181. package/dist/core/prompt-override.js +1 -0
  182. package/dist/core/prompt-override.js.map +1 -1
  183. package/dist/core/runtime-intelligence.d.ts +11 -0
  184. package/dist/core/runtime-intelligence.d.ts.map +1 -1
  185. package/dist/core/runtime-intelligence.js +18 -3
  186. package/dist/core/runtime-intelligence.js.map +1 -1
  187. package/dist/core/runtime-tool-dispatch.js +1 -1
  188. package/dist/core/runtime-tool-dispatch.js.map +1 -1
  189. package/dist/core/runtime.d.ts +186 -13
  190. package/dist/core/runtime.d.ts.map +1 -1
  191. package/dist/core/runtime.js +717 -228
  192. package/dist/core/runtime.js.map +1 -1
  193. package/dist/core/session-rewind.d.ts +67 -0
  194. package/dist/core/session-rewind.d.ts.map +1 -0
  195. package/dist/core/session-rewind.js +120 -0
  196. package/dist/core/session-rewind.js.map +1 -0
  197. package/dist/core/types.d.ts +19 -0
  198. package/dist/core/types.d.ts.map +1 -1
  199. package/dist/daemon/file-watcher.d.ts +84 -0
  200. package/dist/daemon/file-watcher.d.ts.map +1 -0
  201. package/dist/daemon/file-watcher.js +193 -0
  202. package/dist/daemon/file-watcher.js.map +1 -0
  203. package/dist/daemon/jsonl-mode.d.ts +11 -0
  204. package/dist/daemon/jsonl-mode.d.ts.map +1 -0
  205. package/dist/daemon/jsonl-mode.js +77 -0
  206. package/dist/daemon/jsonl-mode.js.map +1 -0
  207. package/dist/daemon/kairos-ipc.d.ts +4 -1
  208. package/dist/daemon/kairos-ipc.d.ts.map +1 -1
  209. package/dist/daemon/kairos-ipc.js +38 -22
  210. package/dist/daemon/kairos-ipc.js.map +1 -1
  211. package/dist/daemon/kairos-rpc.d.ts +5 -1
  212. package/dist/daemon/kairos-rpc.d.ts.map +1 -1
  213. package/dist/daemon/kairos-rpc.js +1262 -114
  214. package/dist/daemon/kairos-rpc.js.map +1 -1
  215. package/dist/daemon/kairos.d.ts +35 -4
  216. package/dist/daemon/kairos.d.ts.map +1 -1
  217. package/dist/daemon/kairos.js +204 -24
  218. package/dist/daemon/kairos.js.map +1 -1
  219. package/dist/daemon/rpc-handlers/intelligence-rpc.d.ts.map +1 -1
  220. package/dist/daemon/rpc-handlers/intelligence-rpc.js +15 -3
  221. package/dist/daemon/rpc-handlers/intelligence-rpc.js.map +1 -1
  222. package/dist/daemon/transport/jsonl.d.ts +7 -0
  223. package/dist/daemon/transport/jsonl.d.ts.map +1 -0
  224. package/dist/daemon/transport/jsonl.js +38 -0
  225. package/dist/daemon/transport/jsonl.js.map +1 -0
  226. package/dist/daemon/transport/socket-path.d.ts +28 -0
  227. package/dist/daemon/transport/socket-path.d.ts.map +1 -0
  228. package/dist/daemon/transport/socket-path.js +36 -0
  229. package/dist/daemon/transport/socket-path.js.map +1 -0
  230. package/dist/design/theme-presets.d.ts +77 -0
  231. package/dist/design/theme-presets.d.ts.map +1 -0
  232. package/dist/design/theme-presets.js +274 -0
  233. package/dist/design/theme-presets.js.map +1 -0
  234. package/dist/desktop/companion-server.d.ts +67 -4
  235. package/dist/desktop/companion-server.d.ts.map +1 -1
  236. package/dist/desktop/companion-server.js +617 -70
  237. package/dist/desktop/companion-server.js.map +1 -1
  238. package/dist/desktop/web-artifacts-builder.d.ts +108 -0
  239. package/dist/desktop/web-artifacts-builder.d.ts.map +1 -0
  240. package/dist/desktop/web-artifacts-builder.js +184 -0
  241. package/dist/desktop/web-artifacts-builder.js.map +1 -0
  242. package/dist/hooks/built-in.d.ts.map +1 -1
  243. package/dist/hooks/built-in.js +15 -10
  244. package/dist/hooks/built-in.js.map +1 -1
  245. package/dist/index.js +1301 -167
  246. package/dist/index.js.map +1 -1
  247. package/dist/integrations/integration-manifest.d.ts +140 -0
  248. package/dist/integrations/integration-manifest.d.ts.map +1 -0
  249. package/dist/integrations/integration-manifest.js +268 -0
  250. package/dist/integrations/integration-manifest.js.map +1 -0
  251. package/dist/intelligence/apply-patch-dsl.d.ts +112 -0
  252. package/dist/intelligence/apply-patch-dsl.d.ts.map +1 -0
  253. package/dist/intelligence/apply-patch-dsl.js +264 -0
  254. package/dist/intelligence/apply-patch-dsl.js.map +1 -0
  255. package/dist/intelligence/apply-patch-executor.d.ts +147 -0
  256. package/dist/intelligence/apply-patch-executor.d.ts.map +1 -0
  257. package/dist/intelligence/apply-patch-executor.js +418 -0
  258. package/dist/intelligence/apply-patch-executor.js.map +1 -0
  259. package/dist/intelligence/blast-radius.d.ts +67 -0
  260. package/dist/intelligence/blast-radius.d.ts.map +1 -0
  261. package/dist/intelligence/blast-radius.js +536 -0
  262. package/dist/intelligence/blast-radius.js.map +1 -0
  263. package/dist/intelligence/code-graph.d.ts +58 -7
  264. package/dist/intelligence/code-graph.d.ts.map +1 -1
  265. package/dist/intelligence/code-graph.js +347 -16
  266. package/dist/intelligence/code-graph.js.map +1 -1
  267. package/dist/intelligence/codebase-health.d.ts.map +1 -1
  268. package/dist/intelligence/codebase-health.js +4 -3
  269. package/dist/intelligence/codebase-health.js.map +1 -1
  270. package/dist/intelligence/codemaps.d.ts +30 -1
  271. package/dist/intelligence/codemaps.d.ts.map +1 -1
  272. package/dist/intelligence/codemaps.js +99 -16
  273. package/dist/intelligence/codemaps.js.map +1 -1
  274. package/dist/intelligence/consecutive-error-counter.d.ts +89 -0
  275. package/dist/intelligence/consecutive-error-counter.d.ts.map +1 -0
  276. package/dist/intelligence/consecutive-error-counter.js +151 -0
  277. package/dist/intelligence/consecutive-error-counter.js.map +1 -0
  278. package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.d.ts +87 -0
  279. package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.d.ts.map +1 -0
  280. package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.js +882 -0
  281. package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.js.map +1 -0
  282. package/dist/intelligence/extractors/mineru.d.ts +108 -0
  283. package/dist/intelligence/extractors/mineru.d.ts.map +1 -0
  284. package/dist/intelligence/extractors/mineru.js +352 -0
  285. package/dist/intelligence/extractors/mineru.js.map +1 -0
  286. package/dist/intelligence/harness-introspect.d.ts +124 -0
  287. package/dist/intelligence/harness-introspect.d.ts.map +1 -0
  288. package/dist/intelligence/harness-introspect.js +349 -0
  289. package/dist/intelligence/harness-introspect.js.map +1 -0
  290. package/dist/intelligence/multi-patch-voter.d.ts +59 -1
  291. package/dist/intelligence/multi-patch-voter.d.ts.map +1 -1
  292. package/dist/intelligence/multi-patch-voter.js +191 -27
  293. package/dist/intelligence/multi-patch-voter.js.map +1 -1
  294. package/dist/intelligence/multimodal-extract.d.ts +44 -0
  295. package/dist/intelligence/multimodal-extract.d.ts.map +1 -1
  296. package/dist/intelligence/multimodal-extract.js +40 -1
  297. package/dist/intelligence/multimodal-extract.js.map +1 -1
  298. package/dist/intelligence/research-loops/budgeted-research.d.ts +7 -0
  299. package/dist/intelligence/research-loops/budgeted-research.d.ts.map +1 -1
  300. package/dist/intelligence/research-loops/budgeted-research.js +18 -0
  301. package/dist/intelligence/research-loops/budgeted-research.js.map +1 -1
  302. package/dist/intelligence/research-loops/constraint-guided-explorer.d.ts +124 -0
  303. package/dist/intelligence/research-loops/constraint-guided-explorer.d.ts.map +1 -0
  304. package/dist/intelligence/research-loops/constraint-guided-explorer.js +177 -0
  305. package/dist/intelligence/research-loops/constraint-guided-explorer.js.map +1 -0
  306. package/dist/intelligence/research-loops/results-collector.d.ts +62 -0
  307. package/dist/intelligence/research-loops/results-collector.d.ts.map +1 -0
  308. package/dist/intelligence/research-loops/results-collector.js +128 -0
  309. package/dist/intelligence/research-loops/results-collector.js.map +1 -0
  310. package/dist/intelligence/research-loops/results-log.d.ts +94 -0
  311. package/dist/intelligence/research-loops/results-log.d.ts.map +1 -0
  312. package/dist/intelligence/research-loops/results-log.js +178 -0
  313. package/dist/intelligence/research-loops/results-log.js.map +1 -0
  314. package/dist/intelligence/research-loops/strategies/base-strategy.d.ts +111 -0
  315. package/dist/intelligence/research-loops/strategies/base-strategy.d.ts.map +1 -0
  316. package/dist/intelligence/research-loops/strategies/base-strategy.js +72 -0
  317. package/dist/intelligence/research-loops/strategies/base-strategy.js.map +1 -0
  318. package/dist/intelligence/research-loops/strategies/dual-confidence.d.ts +57 -0
  319. package/dist/intelligence/research-loops/strategies/dual-confidence.d.ts.map +1 -0
  320. package/dist/intelligence/research-loops/strategies/dual-confidence.js +172 -0
  321. package/dist/intelligence/research-loops/strategies/dual-confidence.js.map +1 -0
  322. package/dist/intelligence/research-loops/strategies/langgraph-agent.d.ts +68 -0
  323. package/dist/intelligence/research-loops/strategies/langgraph-agent.d.ts.map +1 -0
  324. package/dist/intelligence/research-loops/strategies/langgraph-agent.js +221 -0
  325. package/dist/intelligence/research-loops/strategies/langgraph-agent.js.map +1 -0
  326. package/dist/intelligence/research-loops/strategies/parallel-constrained.d.ts +42 -0
  327. package/dist/intelligence/research-loops/strategies/parallel-constrained.d.ts.map +1 -0
  328. package/dist/intelligence/research-loops/strategies/parallel-constrained.js +132 -0
  329. package/dist/intelligence/research-loops/strategies/parallel-constrained.js.map +1 -0
  330. package/dist/intelligence/research-loops/strategies/recursive-decomposition.d.ts +55 -0
  331. package/dist/intelligence/research-loops/strategies/recursive-decomposition.d.ts.map +1 -0
  332. package/dist/intelligence/research-loops/strategies/recursive-decomposition.js +187 -0
  333. package/dist/intelligence/research-loops/strategies/recursive-decomposition.js.map +1 -0
  334. package/dist/intelligence/research-loops/strategy-registry.d.ts +43 -0
  335. package/dist/intelligence/research-loops/strategy-registry.d.ts.map +1 -0
  336. package/dist/intelligence/research-loops/strategy-registry.js +62 -0
  337. package/dist/intelligence/research-loops/strategy-registry.js.map +1 -0
  338. package/dist/intelligence/research-strategies.d.ts +133 -0
  339. package/dist/intelligence/research-strategies.d.ts.map +1 -0
  340. package/dist/intelligence/research-strategies.js +204 -0
  341. package/dist/intelligence/research-strategies.js.map +1 -0
  342. package/dist/lib.d.ts +49 -0
  343. package/dist/lib.d.ts.map +1 -1
  344. package/dist/lib.js +133 -0
  345. package/dist/lib.js.map +1 -1
  346. package/dist/loop/tool-description-reset.d.ts +113 -0
  347. package/dist/loop/tool-description-reset.d.ts.map +1 -0
  348. package/dist/loop/tool-description-reset.js +136 -0
  349. package/dist/loop/tool-description-reset.js.map +1 -0
  350. package/dist/lsp/server-registry.d.ts.map +1 -1
  351. package/dist/lsp/server-registry.js +2 -1
  352. package/dist/lsp/server-registry.js.map +1 -1
  353. package/dist/lsp/symbol-operations.d.ts.map +1 -1
  354. package/dist/lsp/symbol-operations.js +2 -1
  355. package/dist/lsp/symbol-operations.js.map +1 -1
  356. package/dist/marketplace/acp-agent-registry.d.ts.map +1 -1
  357. package/dist/marketplace/acp-agent-registry.js +2 -1
  358. package/dist/marketplace/acp-agent-registry.js.map +1 -1
  359. package/dist/marketplace/marketplace-source-git.d.ts +135 -0
  360. package/dist/marketplace/marketplace-source-git.d.ts.map +1 -0
  361. package/dist/marketplace/marketplace-source-git.js +211 -0
  362. package/dist/marketplace/marketplace-source-git.js.map +1 -0
  363. package/dist/marketplace/registry.d.ts +87 -0
  364. package/dist/marketplace/registry.d.ts.map +1 -1
  365. package/dist/marketplace/registry.js +113 -2
  366. package/dist/marketplace/registry.js.map +1 -1
  367. package/dist/mcp/chrome-devtools.d.ts +62 -2
  368. package/dist/mcp/chrome-devtools.d.ts.map +1 -1
  369. package/dist/mcp/chrome-devtools.js +183 -1
  370. package/dist/mcp/chrome-devtools.js.map +1 -1
  371. package/dist/mcp/health-probe.d.ts +1 -0
  372. package/dist/mcp/health-probe.d.ts.map +1 -1
  373. package/dist/mcp/health-probe.js +32 -1
  374. package/dist/mcp/health-probe.js.map +1 -1
  375. package/dist/mcp/mcp-scaffolder.d.ts +166 -0
  376. package/dist/mcp/mcp-scaffolder.d.ts.map +1 -0
  377. package/dist/mcp/mcp-scaffolder.js +526 -0
  378. package/dist/mcp/mcp-scaffolder.js.map +1 -0
  379. package/dist/mcp/mcp-server.d.ts.map +1 -1
  380. package/dist/mcp/mcp-server.js +55 -0
  381. package/dist/mcp/mcp-server.js.map +1 -1
  382. package/dist/mcp/memory-mcp.d.ts +163 -0
  383. package/dist/mcp/memory-mcp.d.ts.map +1 -0
  384. package/dist/mcp/memory-mcp.js +368 -0
  385. package/dist/mcp/memory-mcp.js.map +1 -0
  386. package/dist/mcp/tool-loader.d.ts.map +1 -1
  387. package/dist/mcp/tool-loader.js +13 -0
  388. package/dist/mcp/tool-loader.js.map +1 -1
  389. package/dist/meet/meeting-runtime.d.ts.map +1 -1
  390. package/dist/meet/meeting-runtime.js +3 -1
  391. package/dist/meet/meeting-runtime.js.map +1 -1
  392. package/dist/memory/incremental-indexer.d.ts.map +1 -1
  393. package/dist/memory/incremental-indexer.js +5 -1
  394. package/dist/memory/incremental-indexer.js.map +1 -1
  395. package/dist/memory/qmd-integration.d.ts.map +1 -1
  396. package/dist/memory/qmd-integration.js +47 -15
  397. package/dist/memory/qmd-integration.js.map +1 -1
  398. package/dist/memory/two-phase-consolidator.d.ts +118 -0
  399. package/dist/memory/two-phase-consolidator.d.ts.map +1 -0
  400. package/dist/memory/two-phase-consolidator.js +265 -0
  401. package/dist/memory/two-phase-consolidator.js.map +1 -0
  402. package/dist/middleware/doom-loop.d.ts +21 -0
  403. package/dist/middleware/doom-loop.d.ts.map +1 -1
  404. package/dist/middleware/doom-loop.js +49 -6
  405. package/dist/middleware/doom-loop.js.map +1 -1
  406. package/dist/middleware/loop-detection.d.ts +176 -6
  407. package/dist/middleware/loop-detection.d.ts.map +1 -1
  408. package/dist/middleware/loop-detection.js +341 -6
  409. package/dist/middleware/loop-detection.js.map +1 -1
  410. package/dist/middleware/pipeline.d.ts +9 -1
  411. package/dist/middleware/pipeline.d.ts.map +1 -1
  412. package/dist/middleware/pipeline.js +116 -1
  413. package/dist/middleware/pipeline.js.map +1 -1
  414. package/dist/middleware/tool-flow-gate.d.ts +20 -0
  415. package/dist/middleware/tool-flow-gate.d.ts.map +1 -1
  416. package/dist/middleware/tool-flow-gate.js +93 -0
  417. package/dist/middleware/tool-flow-gate.js.map +1 -1
  418. package/dist/mobile/ios-app.d.ts +18 -1
  419. package/dist/mobile/ios-app.d.ts.map +1 -1
  420. package/dist/mobile/ios-app.js +108 -8
  421. package/dist/mobile/ios-app.js.map +1 -1
  422. package/dist/orchestration/adversarial-cut.d.ts +85 -0
  423. package/dist/orchestration/adversarial-cut.d.ts.map +1 -0
  424. package/dist/orchestration/adversarial-cut.js +222 -0
  425. package/dist/orchestration/adversarial-cut.js.map +1 -0
  426. package/dist/orchestration/agent-kanban.d.ts +147 -0
  427. package/dist/orchestration/agent-kanban.d.ts.map +1 -0
  428. package/dist/orchestration/agent-kanban.js +414 -0
  429. package/dist/orchestration/agent-kanban.js.map +1 -0
  430. package/dist/orchestration/autonomous.d.ts +111 -0
  431. package/dist/orchestration/autonomous.d.ts.map +1 -1
  432. package/dist/orchestration/autonomous.js +183 -6
  433. package/dist/orchestration/autonomous.js.map +1 -1
  434. package/dist/orchestration/elo-tournament.d.ts +90 -0
  435. package/dist/orchestration/elo-tournament.d.ts.map +1 -0
  436. package/dist/orchestration/elo-tournament.js +203 -0
  437. package/dist/orchestration/elo-tournament.js.map +1 -0
  438. package/dist/orchestration/multi-model-arena.d.ts +138 -0
  439. package/dist/orchestration/multi-model-arena.d.ts.map +1 -0
  440. package/dist/orchestration/multi-model-arena.js +298 -0
  441. package/dist/orchestration/multi-model-arena.js.map +1 -0
  442. package/dist/orchestration/review-pipeline.d.ts +78 -0
  443. package/dist/orchestration/review-pipeline.d.ts.map +1 -0
  444. package/dist/orchestration/review-pipeline.js +202 -0
  445. package/dist/orchestration/review-pipeline.js.map +1 -0
  446. package/dist/orchestration/unified-state-thread.d.ts +186 -0
  447. package/dist/orchestration/unified-state-thread.d.ts.map +1 -0
  448. package/dist/orchestration/unified-state-thread.js +327 -0
  449. package/dist/orchestration/unified-state-thread.js.map +1 -0
  450. package/dist/plugins/manager.d.ts.map +1 -1
  451. package/dist/plugins/manager.js +2 -1
  452. package/dist/plugins/manager.js.map +1 -1
  453. package/dist/prompt/engine.d.ts +7 -0
  454. package/dist/prompt/engine.d.ts.map +1 -1
  455. package/dist/prompt/engine.js.map +1 -1
  456. package/dist/prompt/modules/caveman.d.ts +51 -0
  457. package/dist/prompt/modules/caveman.d.ts.map +1 -0
  458. package/dist/prompt/modules/caveman.js +97 -0
  459. package/dist/prompt/modules/caveman.js.map +1 -0
  460. package/dist/prompt/modules/index.d.ts +1 -0
  461. package/dist/prompt/modules/index.d.ts.map +1 -1
  462. package/dist/prompt/modules/index.js +3 -0
  463. package/dist/prompt/modules/index.js.map +1 -1
  464. package/dist/providers/account-pool.d.ts.map +1 -1
  465. package/dist/providers/account-pool.js +1 -0
  466. package/dist/providers/account-pool.js.map +1 -1
  467. package/dist/providers/anthropic-adapter.d.ts.map +1 -1
  468. package/dist/providers/anthropic-adapter.js +33 -0
  469. package/dist/providers/anthropic-adapter.js.map +1 -1
  470. package/dist/providers/cloud-offload/config-loader.d.ts +20 -0
  471. package/dist/providers/cloud-offload/config-loader.d.ts.map +1 -0
  472. package/dist/providers/cloud-offload/config-loader.js +148 -0
  473. package/dist/providers/cloud-offload/config-loader.js.map +1 -0
  474. package/dist/providers/codex-adapter.d.ts.map +1 -1
  475. package/dist/providers/codex-adapter.js +136 -4
  476. package/dist/providers/codex-adapter.js.map +1 -1
  477. package/dist/providers/discovery.d.ts.map +1 -1
  478. package/dist/providers/discovery.js +20 -0
  479. package/dist/providers/discovery.js.map +1 -1
  480. package/dist/providers/effort-cascade.d.ts +113 -0
  481. package/dist/providers/effort-cascade.d.ts.map +1 -0
  482. package/dist/providers/effort-cascade.js +215 -0
  483. package/dist/providers/effort-cascade.js.map +1 -0
  484. package/dist/providers/fallback-chain.d.ts.map +1 -1
  485. package/dist/providers/fallback-chain.js +1 -0
  486. package/dist/providers/fallback-chain.js.map +1 -1
  487. package/dist/providers/health-check.d.ts.map +1 -1
  488. package/dist/providers/health-check.js +8 -0
  489. package/dist/providers/health-check.js.map +1 -1
  490. package/dist/providers/model-defaults.d.ts +1 -1
  491. package/dist/providers/model-defaults.d.ts.map +1 -1
  492. package/dist/providers/model-defaults.js +13 -1
  493. package/dist/providers/model-defaults.js.map +1 -1
  494. package/dist/providers/model-discovery.d.ts.map +1 -1
  495. package/dist/providers/model-discovery.js +1 -0
  496. package/dist/providers/model-discovery.js.map +1 -1
  497. package/dist/providers/ollama-adapter.d.ts.map +1 -1
  498. package/dist/providers/ollama-adapter.js +21 -1
  499. package/dist/providers/ollama-adapter.js.map +1 -1
  500. package/dist/providers/openai-compat-adapter.d.ts.map +1 -1
  501. package/dist/providers/openai-compat-adapter.js +25 -0
  502. package/dist/providers/openai-compat-adapter.js.map +1 -1
  503. package/dist/providers/preset-library.d.ts.map +1 -1
  504. package/dist/providers/preset-library.js +9 -0
  505. package/dist/providers/preset-library.js.map +1 -1
  506. package/dist/providers/provider-service.d.ts.map +1 -1
  507. package/dist/providers/provider-service.js +40 -1
  508. package/dist/providers/provider-service.js.map +1 -1
  509. package/dist/providers/registry.d.ts.map +1 -1
  510. package/dist/providers/registry.js +7 -0
  511. package/dist/providers/registry.js.map +1 -1
  512. package/dist/providers/sticky-rotation.d.ts +100 -0
  513. package/dist/providers/sticky-rotation.d.ts.map +1 -0
  514. package/dist/providers/sticky-rotation.js +134 -0
  515. package/dist/providers/sticky-rotation.js.map +1 -0
  516. package/dist/providers/tool-parsers/parsers.d.ts.map +1 -1
  517. package/dist/providers/tool-parsers/parsers.js +17 -4
  518. package/dist/providers/tool-parsers/parsers.js.map +1 -1
  519. package/dist/providers/types.d.ts +25 -0
  520. package/dist/providers/types.d.ts.map +1 -1
  521. package/dist/recipes/final-output.d.ts +74 -0
  522. package/dist/recipes/final-output.d.ts.map +1 -0
  523. package/dist/recipes/final-output.js +232 -0
  524. package/dist/recipes/final-output.js.map +1 -0
  525. package/dist/recipes/recipe-loader.d.ts.map +1 -1
  526. package/dist/recipes/recipe-loader.js +29 -0
  527. package/dist/recipes/recipe-loader.js.map +1 -1
  528. package/dist/recipes/recipe-runtime.d.ts.map +1 -1
  529. package/dist/recipes/recipe-runtime.js +31 -0
  530. package/dist/recipes/recipe-runtime.js.map +1 -1
  531. package/dist/recipes/recipe-types.d.ts +28 -0
  532. package/dist/recipes/recipe-types.d.ts.map +1 -1
  533. package/dist/recipes/sop-crystallizer.d.ts +90 -0
  534. package/dist/recipes/sop-crystallizer.d.ts.map +1 -0
  535. package/dist/recipes/sop-crystallizer.js +238 -0
  536. package/dist/recipes/sop-crystallizer.js.map +1 -0
  537. package/dist/recipes/sub-recipe-orchestrator.d.ts +126 -0
  538. package/dist/recipes/sub-recipe-orchestrator.d.ts.map +1 -0
  539. package/dist/recipes/sub-recipe-orchestrator.js +225 -0
  540. package/dist/recipes/sub-recipe-orchestrator.js.map +1 -0
  541. package/dist/sandbox/extended-backends.d.ts.map +1 -1
  542. package/dist/sandbox/extended-backends.js +7 -2
  543. package/dist/sandbox/extended-backends.js.map +1 -1
  544. package/dist/sandbox/seatbelt-policy.d.ts +132 -0
  545. package/dist/sandbox/seatbelt-policy.d.ts.map +1 -0
  546. package/dist/sandbox/seatbelt-policy.js +562 -0
  547. package/dist/sandbox/seatbelt-policy.js.map +1 -0
  548. package/dist/sandbox/terminal-backends.d.ts.map +1 -1
  549. package/dist/sandbox/terminal-backends.js +3 -1
  550. package/dist/sandbox/terminal-backends.js.map +1 -1
  551. package/dist/security/auto-mode-ruleset.d.ts +142 -0
  552. package/dist/security/auto-mode-ruleset.d.ts.map +1 -0
  553. package/dist/security/auto-mode-ruleset.js +264 -0
  554. package/dist/security/auto-mode-ruleset.js.map +1 -0
  555. package/dist/security/bash-arity-policy.d.ts +70 -0
  556. package/dist/security/bash-arity-policy.d.ts.map +1 -0
  557. package/dist/security/bash-arity-policy.js +189 -0
  558. package/dist/security/bash-arity-policy.js.map +1 -0
  559. package/dist/security/command-sanitizer.d.ts.map +1 -1
  560. package/dist/security/command-sanitizer.js +24 -0
  561. package/dist/security/command-sanitizer.js.map +1 -1
  562. package/dist/security/credential-broker.d.ts +136 -0
  563. package/dist/security/credential-broker.d.ts.map +1 -0
  564. package/dist/security/credential-broker.js +284 -0
  565. package/dist/security/credential-broker.js.map +1 -0
  566. package/dist/security/invisible-text-sanitizer.d.ts +114 -0
  567. package/dist/security/invisible-text-sanitizer.d.ts.map +1 -0
  568. package/dist/security/invisible-text-sanitizer.js +221 -0
  569. package/dist/security/invisible-text-sanitizer.js.map +1 -0
  570. package/dist/security/osv-check.d.ts +174 -0
  571. package/dist/security/osv-check.d.ts.map +1 -0
  572. package/dist/security/osv-check.js +409 -0
  573. package/dist/security/osv-check.js.map +1 -0
  574. package/dist/security/process-hardening.d.ts +114 -0
  575. package/dist/security/process-hardening.d.ts.map +1 -0
  576. package/dist/security/process-hardening.js +132 -0
  577. package/dist/security/process-hardening.js.map +1 -0
  578. package/dist/security/shell-escalation-tiers.d.ts +73 -0
  579. package/dist/security/shell-escalation-tiers.d.ts.map +1 -0
  580. package/dist/security/shell-escalation-tiers.js +439 -0
  581. package/dist/security/shell-escalation-tiers.js.map +1 -0
  582. package/dist/session/fork.d.ts +100 -0
  583. package/dist/session/fork.d.ts.map +1 -0
  584. package/dist/session/fork.js +223 -0
  585. package/dist/session/fork.js.map +1 -0
  586. package/dist/skills/activation-telemetry.d.ts +154 -0
  587. package/dist/skills/activation-telemetry.d.ts.map +1 -0
  588. package/dist/skills/activation-telemetry.js +274 -0
  589. package/dist/skills/activation-telemetry.js.map +1 -0
  590. package/dist/skills/agentskills-registry.d.ts +12 -1
  591. package/dist/skills/agentskills-registry.d.ts.map +1 -1
  592. package/dist/skills/agentskills-registry.js +11 -0
  593. package/dist/skills/agentskills-registry.js.map +1 -1
  594. package/dist/skills/description-optimizer.d.ts +101 -0
  595. package/dist/skills/description-optimizer.d.ts.map +1 -0
  596. package/dist/skills/description-optimizer.js +304 -0
  597. package/dist/skills/description-optimizer.js.map +1 -0
  598. package/dist/skills/history-deduper.d.ts +109 -0
  599. package/dist/skills/history-deduper.d.ts.map +1 -0
  600. package/dist/skills/history-deduper.js +222 -0
  601. package/dist/skills/history-deduper.js.map +1 -0
  602. package/dist/skills/loader.d.ts.map +1 -1
  603. package/dist/skills/loader.js +2 -1
  604. package/dist/skills/loader.js.map +1 -1
  605. package/dist/skills/skill-source-adapter.d.ts +16 -0
  606. package/dist/skills/skill-source-adapter.d.ts.map +1 -1
  607. package/dist/skills/skill-source-adapter.js +0 -0
  608. package/dist/skills/skill-source-adapter.js.map +1 -1
  609. package/dist/skills/skill-standard.d.ts +29 -4
  610. package/dist/skills/skill-standard.d.ts.map +1 -1
  611. package/dist/skills/skill-standard.js +139 -66
  612. package/dist/skills/skill-standard.js.map +1 -1
  613. package/dist/skills/sop-completion-gate.d.ts +137 -0
  614. package/dist/skills/sop-completion-gate.d.ts.map +1 -0
  615. package/dist/skills/sop-completion-gate.js +159 -0
  616. package/dist/skills/sop-completion-gate.js.map +1 -0
  617. package/dist/skills/sop-template.d.ts +85 -0
  618. package/dist/skills/sop-template.d.ts.map +1 -0
  619. package/dist/skills/sop-template.js +106 -0
  620. package/dist/skills/sop-template.js.map +1 -0
  621. package/dist/skills/wotann-skills-registry.d.ts +1 -1
  622. package/dist/skills/wotann-skills-registry.d.ts.map +1 -1
  623. package/dist/skills/wotann-skills-registry.js +503 -0
  624. package/dist/skills/wotann-skills-registry.js.map +1 -1
  625. package/dist/snippets/prompt-corpus.d.ts +10 -0
  626. package/dist/snippets/prompt-corpus.d.ts.map +1 -0
  627. package/dist/snippets/prompt-corpus.js +158 -0
  628. package/dist/snippets/prompt-corpus.js.map +1 -0
  629. package/dist/snippets/snippet-store.d.ts +27 -0
  630. package/dist/snippets/snippet-store.d.ts.map +1 -1
  631. package/dist/snippets/snippet-store.js +109 -6
  632. package/dist/snippets/snippet-store.js.map +1 -1
  633. package/dist/storage/sqlite-node-backend.d.ts +47 -36
  634. package/dist/storage/sqlite-node-backend.d.ts.map +1 -1
  635. package/dist/storage/sqlite-node-backend.js +250 -94
  636. package/dist/storage/sqlite-node-backend.js.map +1 -1
  637. package/dist/testing/webapp-server-lifecycle.d.ts +74 -0
  638. package/dist/testing/webapp-server-lifecycle.d.ts.map +1 -0
  639. package/dist/testing/webapp-server-lifecycle.js +272 -0
  640. package/dist/testing/webapp-server-lifecycle.js.map +1 -0
  641. package/dist/tools/markitdown-bridge.d.ts +19 -0
  642. package/dist/tools/markitdown-bridge.d.ts.map +1 -1
  643. package/dist/tools/markitdown-bridge.js +167 -2
  644. package/dist/tools/markitdown-bridge.js.map +1 -1
  645. package/dist/tools/pdf-processor.d.ts.map +1 -1
  646. package/dist/tools/pdf-processor.js +7 -3
  647. package/dist/tools/pdf-processor.js.map +1 -1
  648. package/dist/tools/pptx-processor.d.ts +51 -0
  649. package/dist/tools/pptx-processor.d.ts.map +1 -0
  650. package/dist/tools/pptx-processor.js +334 -0
  651. package/dist/tools/pptx-processor.js.map +1 -0
  652. package/dist/ui/App.d.ts.map +1 -1
  653. package/dist/ui/App.js +1661 -149
  654. package/dist/ui/App.js.map +1 -1
  655. package/dist/ui/agent-tools.d.ts +60 -0
  656. package/dist/ui/agent-tools.d.ts.map +1 -0
  657. package/dist/ui/agent-tools.js +468 -0
  658. package/dist/ui/agent-tools.js.map +1 -0
  659. package/dist/ui/alt-buffer-mode.d.ts +68 -0
  660. package/dist/ui/alt-buffer-mode.d.ts.map +1 -0
  661. package/dist/ui/alt-buffer-mode.js +114 -0
  662. package/dist/ui/alt-buffer-mode.js.map +1 -0
  663. package/dist/ui/alt-buffer.d.ts +57 -10
  664. package/dist/ui/alt-buffer.d.ts.map +1 -1
  665. package/dist/ui/alt-buffer.js +54 -12
  666. package/dist/ui/alt-buffer.js.map +1 -1
  667. package/dist/ui/components/AgentStatusPanel.d.ts.map +1 -1
  668. package/dist/ui/components/AgentStatusPanel.js +14 -6
  669. package/dist/ui/components/AgentStatusPanel.js.map +1 -1
  670. package/dist/ui/components/AuditLogPanel.js +3 -3
  671. package/dist/ui/components/AuditLogPanel.js.map +1 -1
  672. package/dist/ui/components/AutomationsPanel.js +3 -3
  673. package/dist/ui/components/AutomationsPanel.js.map +1 -1
  674. package/dist/ui/components/ChatView.d.ts +9 -1
  675. package/dist/ui/components/ChatView.d.ts.map +1 -1
  676. package/dist/ui/components/ChatView.js +503 -14
  677. package/dist/ui/components/ChatView.js.map +1 -1
  678. package/dist/ui/components/CommandPaletteCommands.d.ts +2 -0
  679. package/dist/ui/components/CommandPaletteCommands.d.ts.map +1 -1
  680. package/dist/ui/components/CommandPaletteCommands.js +18 -2
  681. package/dist/ui/components/CommandPaletteCommands.js.map +1 -1
  682. package/dist/ui/components/ContextHUD.d.ts.map +1 -1
  683. package/dist/ui/components/ContextHUD.js +3 -3
  684. package/dist/ui/components/ContextHUD.js.map +1 -1
  685. package/dist/ui/components/DispatchInbox.js +3 -3
  686. package/dist/ui/components/DispatchInbox.js.map +1 -1
  687. package/dist/ui/components/GdprPanel.d.ts.map +1 -1
  688. package/dist/ui/components/GdprPanel.js +2 -3
  689. package/dist/ui/components/GdprPanel.js.map +1 -1
  690. package/dist/ui/components/HistoryPicker.js +3 -3
  691. package/dist/ui/components/HistoryPicker.js.map +1 -1
  692. package/dist/ui/components/ModelPicker.d.ts +9 -6
  693. package/dist/ui/components/ModelPicker.d.ts.map +1 -1
  694. package/dist/ui/components/ModelPicker.js +12 -9
  695. package/dist/ui/components/ModelPicker.js.map +1 -1
  696. package/dist/ui/components/OptionPicker.js +3 -3
  697. package/dist/ui/components/OptionPicker.js.map +1 -1
  698. package/dist/ui/components/PermissionPrompt.js +3 -3
  699. package/dist/ui/components/PermissionPrompt.js.map +1 -1
  700. package/dist/ui/components/PromptInput.d.ts.map +1 -1
  701. package/dist/ui/components/PromptInput.js +65 -10
  702. package/dist/ui/components/PromptInput.js.map +1 -1
  703. package/dist/ui/components/ProviderSetupOverlay.d.ts +19 -0
  704. package/dist/ui/components/ProviderSetupOverlay.d.ts.map +1 -0
  705. package/dist/ui/components/ProviderSetupOverlay.js +124 -0
  706. package/dist/ui/components/ProviderSetupOverlay.js.map +1 -0
  707. package/dist/ui/components/StartupScreen.d.ts.map +1 -1
  708. package/dist/ui/components/StartupScreen.js +10 -1
  709. package/dist/ui/components/StartupScreen.js.map +1 -1
  710. package/dist/ui/components/StatusBar.d.ts.map +1 -1
  711. package/dist/ui/components/StatusBar.js +3 -3
  712. package/dist/ui/components/StatusBar.js.map +1 -1
  713. package/dist/ui/components/TrustPanel.js +3 -3
  714. package/dist/ui/components/TrustPanel.js.map +1 -1
  715. package/dist/ui/components/UnifiedStatusBar.d.ts.map +1 -1
  716. package/dist/ui/components/UnifiedStatusBar.js +76 -5
  717. package/dist/ui/components/UnifiedStatusBar.js.map +1 -1
  718. package/dist/ui/computer-action-parser.d.ts +6 -0
  719. package/dist/ui/computer-action-parser.d.ts.map +1 -0
  720. package/dist/ui/computer-action-parser.js +119 -0
  721. package/dist/ui/computer-action-parser.js.map +1 -0
  722. package/dist/ui/helpers.d.ts +1 -1
  723. package/dist/ui/helpers.d.ts.map +1 -1
  724. package/dist/ui/helpers.js +1 -1
  725. package/dist/ui/helpers.js.map +1 -1
  726. package/dist/ui/keybindings.d.ts +12 -2
  727. package/dist/ui/keybindings.d.ts.map +1 -1
  728. package/dist/ui/keybindings.js +36 -4
  729. package/dist/ui/keybindings.js.map +1 -1
  730. package/dist/ui/markdown-stream.d.ts +99 -0
  731. package/dist/ui/markdown-stream.d.ts.map +1 -0
  732. package/dist/ui/markdown-stream.js +314 -0
  733. package/dist/ui/markdown-stream.js.map +1 -0
  734. package/dist/ui/terminal-keyboard-protocol.d.ts +23 -0
  735. package/dist/ui/terminal-keyboard-protocol.d.ts.map +1 -0
  736. package/dist/ui/terminal-keyboard-protocol.js +72 -0
  737. package/dist/ui/terminal-keyboard-protocol.js.map +1 -0
  738. package/dist/ui/theme/context.d.ts +39 -0
  739. package/dist/ui/theme/context.d.ts.map +1 -0
  740. package/dist/ui/theme/context.js +42 -0
  741. package/dist/ui/theme/context.js.map +1 -0
  742. package/dist/utils/platform.d.ts +115 -0
  743. package/dist/utils/platform.d.ts.map +1 -0
  744. package/dist/utils/platform.js +146 -0
  745. package/dist/utils/platform.js.map +1 -0
  746. package/dist/verification/auto-verify-policy.d.ts +107 -0
  747. package/dist/verification/auto-verify-policy.d.ts.map +1 -0
  748. package/dist/verification/auto-verify-policy.js +309 -0
  749. package/dist/verification/auto-verify-policy.js.map +1 -0
  750. package/package.json +4 -1
  751. package/skills/anthropic-finance/audit-spreadsheet.md +165 -0
  752. package/skills/anthropic-finance/clean-data-xls.md +59 -0
  753. package/skills/anthropic-finance/competitive-analysis.md +288 -0
  754. package/skills/anthropic-finance/compliance-rules-engine.md +56 -0
  755. package/skills/anthropic-finance/dd-checklist.md +126 -0
  756. package/skills/anthropic-finance/independent-recompute.md +47 -0
  757. package/skills/anthropic-finance/reconcile-root-cause.md +48 -0
  758. package/skills/anthropic-finance/roll-forward.md +42 -0
  759. package/skills/anthropic-finance/untrusted-doc-parse.md +57 -0
  760. package/skills/anthropic-finance/variance-commentary.md +43 -0
  761. package/skills/doubt-driven-development.md +95 -0
  762. package/skills/mattpocock/diagnose.md +126 -0
  763. package/skills/mattpocock/grill-with-docs.md +97 -0
  764. package/skills/mattpocock/improve-codebase-architecture.md +80 -0
  765. package/skills/mattpocock/prototype.md +39 -0
  766. package/skills/mattpocock/review.md +87 -0
  767. package/skills/mattpocock/tdd.md +118 -0
  768. package/skills/mattpocock/zoom-out.md +17 -0
  769. package/skills/scientific/citation-management/SKILL.md +1113 -0
  770. package/skills/scientific/citation-management/assets/bibtex_template.bib +264 -0
  771. package/skills/scientific/citation-management/assets/citation_checklist.md +386 -0
  772. package/skills/scientific/citation-management/references/bibtex_formatting.md +908 -0
  773. package/skills/scientific/citation-management/references/citation_validation.md +794 -0
  774. package/skills/scientific/citation-management/references/google_scholar_search.md +725 -0
  775. package/skills/scientific/citation-management/references/metadata_extraction.md +870 -0
  776. package/skills/scientific/citation-management/references/pubmed_search.md +839 -0
  777. package/skills/scientific/citation-management/scripts/doi_to_bibtex.py +204 -0
  778. package/skills/scientific/citation-management/scripts/extract_metadata.py +569 -0
  779. package/skills/scientific/citation-management/scripts/format_bibtex.py +349 -0
  780. package/skills/scientific/citation-management/scripts/generate_schematic.py +139 -0
  781. package/skills/scientific/citation-management/scripts/generate_schematic_ai.py +817 -0
  782. package/skills/scientific/citation-management/scripts/search_google_scholar.py +282 -0
  783. package/skills/scientific/citation-management/scripts/search_pubmed.py +398 -0
  784. package/skills/scientific/citation-management/scripts/validate_citations.py +497 -0
  785. package/skills/scientific/database-lookup/SKILL.md +480 -0
  786. package/skills/scientific/database-lookup/references/addgene.md +38 -0
  787. package/skills/scientific/database-lookup/references/alphafold.md +40 -0
  788. package/skills/scientific/database-lookup/references/alphavantage.md +261 -0
  789. package/skills/scientific/database-lookup/references/bea.md +409 -0
  790. package/skills/scientific/database-lookup/references/bindingdb.md +85 -0
  791. package/skills/scientific/database-lookup/references/biogrid.md +110 -0
  792. package/skills/scientific/database-lookup/references/bls.md +235 -0
  793. package/skills/scientific/database-lookup/references/brenda.md +71 -0
  794. package/skills/scientific/database-lookup/references/cbioportal.md +206 -0
  795. package/skills/scientific/database-lookup/references/census.md +251 -0
  796. package/skills/scientific/database-lookup/references/chebi.md +103 -0
  797. package/skills/scientific/database-lookup/references/chembl.md +80 -0
  798. package/skills/scientific/database-lookup/references/clinicaltrials.md +77 -0
  799. package/skills/scientific/database-lookup/references/clinpgx.md +64 -0
  800. package/skills/scientific/database-lookup/references/clinvar.md +91 -0
  801. package/skills/scientific/database-lookup/references/cod.md +121 -0
  802. package/skills/scientific/database-lookup/references/cosmic.md +59 -0
  803. package/skills/scientific/database-lookup/references/dailymed.md +65 -0
  804. package/skills/scientific/database-lookup/references/datacommons.md +237 -0
  805. package/skills/scientific/database-lookup/references/dbsnp.md +143 -0
  806. package/skills/scientific/database-lookup/references/disgenet.md +52 -0
  807. package/skills/scientific/database-lookup/references/drugbank.md +54 -0
  808. package/skills/scientific/database-lookup/references/ecb.md +191 -0
  809. package/skills/scientific/database-lookup/references/emdb.md +37 -0
  810. package/skills/scientific/database-lookup/references/ena.md +372 -0
  811. package/skills/scientific/database-lookup/references/encode.md +47 -0
  812. package/skills/scientific/database-lookup/references/ensembl.md +539 -0
  813. package/skills/scientific/database-lookup/references/epa.md +232 -0
  814. package/skills/scientific/database-lookup/references/eurostat.md +237 -0
  815. package/skills/scientific/database-lookup/references/fda.md +64 -0
  816. package/skills/scientific/database-lookup/references/federal-reserve.md +216 -0
  817. package/skills/scientific/database-lookup/references/fred.md +297 -0
  818. package/skills/scientific/database-lookup/references/gene-ontology.md +147 -0
  819. package/skills/scientific/database-lookup/references/geo.md +130 -0
  820. package/skills/scientific/database-lookup/references/gnomad.md +93 -0
  821. package/skills/scientific/database-lookup/references/gtex.md +136 -0
  822. package/skills/scientific/database-lookup/references/gwas-catalog.md +46 -0
  823. package/skills/scientific/database-lookup/references/hca.md +35 -0
  824. package/skills/scientific/database-lookup/references/hpo.md +48 -0
  825. package/skills/scientific/database-lookup/references/human-protein-atlas.md +57 -0
  826. package/skills/scientific/database-lookup/references/interpro.md +120 -0
  827. package/skills/scientific/database-lookup/references/jaspar.md +50 -0
  828. package/skills/scientific/database-lookup/references/kegg.md +78 -0
  829. package/skills/scientific/database-lookup/references/lincs-l1000.md +68 -0
  830. package/skills/scientific/database-lookup/references/materials-project.md +123 -0
  831. package/skills/scientific/database-lookup/references/metabolomics-workbench.md +98 -0
  832. package/skills/scientific/database-lookup/references/monarch.md +46 -0
  833. package/skills/scientific/database-lookup/references/mousemine.md +40 -0
  834. package/skills/scientific/database-lookup/references/nasa-exoplanet-archive.md +112 -0
  835. package/skills/scientific/database-lookup/references/nasa.md +121 -0
  836. package/skills/scientific/database-lookup/references/ncbi-gene.md +64 -0
  837. package/skills/scientific/database-lookup/references/ncbi-protein.md +104 -0
  838. package/skills/scientific/database-lookup/references/ncbi-taxonomy.md +121 -0
  839. package/skills/scientific/database-lookup/references/nist.md +105 -0
  840. package/skills/scientific/database-lookup/references/noaa.md +199 -0
  841. package/skills/scientific/database-lookup/references/omim.md +114 -0
  842. package/skills/scientific/database-lookup/references/opentargets.md +459 -0
  843. package/skills/scientific/database-lookup/references/openweathermap.md +255 -0
  844. package/skills/scientific/database-lookup/references/pdb.md +121 -0
  845. package/skills/scientific/database-lookup/references/pride.md +74 -0
  846. package/skills/scientific/database-lookup/references/pubchem.md +145 -0
  847. package/skills/scientific/database-lookup/references/quickgo.md +45 -0
  848. package/skills/scientific/database-lookup/references/reactome.md +140 -0
  849. package/skills/scientific/database-lookup/references/rummageo.md +32 -0
  850. package/skills/scientific/database-lookup/references/sdss.md +130 -0
  851. package/skills/scientific/database-lookup/references/sec-edgar.md +315 -0
  852. package/skills/scientific/database-lookup/references/simbad.md +131 -0
  853. package/skills/scientific/database-lookup/references/sra.md +149 -0
  854. package/skills/scientific/database-lookup/references/string.md +283 -0
  855. package/skills/scientific/database-lookup/references/tcga-gdc.md +58 -0
  856. package/skills/scientific/database-lookup/references/treasury.md +215 -0
  857. package/skills/scientific/database-lookup/references/ucsc-genome.md +135 -0
  858. package/skills/scientific/database-lookup/references/uniprot.md +283 -0
  859. package/skills/scientific/database-lookup/references/usgs.md +260 -0
  860. package/skills/scientific/database-lookup/references/uspto.md +130 -0
  861. package/skills/scientific/database-lookup/references/who.md +283 -0
  862. package/skills/scientific/database-lookup/references/worldbank.md +239 -0
  863. package/skills/scientific/database-lookup/references/zinc.md +202 -0
  864. package/skills/scientific/hypothesis-generation/SKILL.md +297 -0
  865. package/skills/scientific/hypothesis-generation/assets/FORMATTING_GUIDE.md +672 -0
  866. package/skills/scientific/hypothesis-generation/assets/hypothesis_generation.sty +307 -0
  867. package/skills/scientific/hypothesis-generation/assets/hypothesis_report_template.tex +572 -0
  868. package/skills/scientific/hypothesis-generation/references/experimental_design_patterns.md +329 -0
  869. package/skills/scientific/hypothesis-generation/references/hypothesis_quality_criteria.md +198 -0
  870. package/skills/scientific/hypothesis-generation/references/literature_search_strategies.md +622 -0
  871. package/skills/scientific/hypothesis-generation/scripts/generate_schematic.py +139 -0
  872. package/skills/scientific/hypothesis-generation/scripts/generate_schematic_ai.py +817 -0
  873. package/skills/scientific/literature-review/SKILL.md +699 -0
  874. package/skills/scientific/literature-review/assets/review_template.md +412 -0
  875. package/skills/scientific/literature-review/references/citation_styles.md +166 -0
  876. package/skills/scientific/literature-review/references/database_strategies.md +455 -0
  877. package/skills/scientific/literature-review/scripts/generate_pdf.py +176 -0
  878. package/skills/scientific/literature-review/scripts/generate_schematic.py +139 -0
  879. package/skills/scientific/literature-review/scripts/generate_schematic_ai.py +817 -0
  880. package/skills/scientific/literature-review/scripts/search_databases.py +303 -0
  881. package/skills/scientific/literature-review/scripts/verify_citations.py +222 -0
  882. package/skills/scientific/markdown-mermaid-writing/SKILL.md +327 -0
  883. package/skills/scientific/markdown-mermaid-writing/assets/examples/example-research-report.md +221 -0
  884. package/skills/scientific/markdown-mermaid-writing/references/diagrams/architecture.md +108 -0
  885. package/skills/scientific/markdown-mermaid-writing/references/diagrams/block.md +177 -0
  886. package/skills/scientific/markdown-mermaid-writing/references/diagrams/c4.md +136 -0
  887. package/skills/scientific/markdown-mermaid-writing/references/diagrams/class.md +246 -0
  888. package/skills/scientific/markdown-mermaid-writing/references/diagrams/complex_examples.md +384 -0
  889. package/skills/scientific/markdown-mermaid-writing/references/diagrams/er.md +222 -0
  890. package/skills/scientific/markdown-mermaid-writing/references/diagrams/flowchart.md +177 -0
  891. package/skills/scientific/markdown-mermaid-writing/references/diagrams/gantt.md +138 -0
  892. package/skills/scientific/markdown-mermaid-writing/references/diagrams/git_graph.md +74 -0
  893. package/skills/scientific/markdown-mermaid-writing/references/diagrams/kanban.md +107 -0
  894. package/skills/scientific/markdown-mermaid-writing/references/diagrams/mindmap.md +74 -0
  895. package/skills/scientific/markdown-mermaid-writing/references/diagrams/packet.md +55 -0
  896. package/skills/scientific/markdown-mermaid-writing/references/diagrams/pie.md +52 -0
  897. package/skills/scientific/markdown-mermaid-writing/references/diagrams/quadrant.md +66 -0
  898. package/skills/scientific/markdown-mermaid-writing/references/diagrams/radar.md +59 -0
  899. package/skills/scientific/markdown-mermaid-writing/references/diagrams/requirement.md +88 -0
  900. package/skills/scientific/markdown-mermaid-writing/references/diagrams/sankey.md +71 -0
  901. package/skills/scientific/markdown-mermaid-writing/references/diagrams/sequence.md +174 -0
  902. package/skills/scientific/markdown-mermaid-writing/references/diagrams/state.md +150 -0
  903. package/skills/scientific/markdown-mermaid-writing/references/diagrams/timeline.md +96 -0
  904. package/skills/scientific/markdown-mermaid-writing/references/diagrams/treemap.md +66 -0
  905. package/skills/scientific/markdown-mermaid-writing/references/diagrams/user_journey.md +108 -0
  906. package/skills/scientific/markdown-mermaid-writing/references/diagrams/xy_chart.md +53 -0
  907. package/skills/scientific/markdown-mermaid-writing/references/diagrams/zenuml.md +71 -0
  908. package/skills/scientific/markdown-mermaid-writing/references/markdown_style_guide.md +733 -0
  909. package/skills/scientific/markdown-mermaid-writing/references/mermaid_style_guide.md +458 -0
  910. package/skills/scientific/markdown-mermaid-writing/templates/decision_record.md +211 -0
  911. package/skills/scientific/markdown-mermaid-writing/templates/how_to_guide.md +275 -0
  912. package/skills/scientific/markdown-mermaid-writing/templates/issue.md +303 -0
  913. package/skills/scientific/markdown-mermaid-writing/templates/kanban.md +223 -0
  914. package/skills/scientific/markdown-mermaid-writing/templates/presentation.md +312 -0
  915. package/skills/scientific/markdown-mermaid-writing/templates/project_documentation.md +412 -0
  916. package/skills/scientific/markdown-mermaid-writing/templates/pull_request.md +319 -0
  917. package/skills/scientific/markdown-mermaid-writing/templates/research_paper.md +304 -0
  918. package/skills/scientific/markdown-mermaid-writing/templates/status_report.md +185 -0
  919. package/skills/scientific/paper-lookup/SKILL.md +193 -0
  920. package/skills/scientific/paper-lookup/references/arxiv.md +161 -0
  921. package/skills/scientific/paper-lookup/references/biorxiv.md +118 -0
  922. package/skills/scientific/paper-lookup/references/core.md +150 -0
  923. package/skills/scientific/paper-lookup/references/crossref.md +181 -0
  924. package/skills/scientific/paper-lookup/references/medrxiv.md +104 -0
  925. package/skills/scientific/paper-lookup/references/openalex.md +174 -0
  926. package/skills/scientific/paper-lookup/references/pmc.md +152 -0
  927. package/skills/scientific/paper-lookup/references/pubmed.md +124 -0
  928. package/skills/scientific/paper-lookup/references/semantic-scholar.md +203 -0
  929. package/skills/scientific/paper-lookup/references/unpaywall.md +127 -0
  930. package/skills/scientific/peer-review/SKILL.md +569 -0
  931. package/skills/scientific/peer-review/references/common_issues.md +552 -0
  932. package/skills/scientific/peer-review/references/reporting_standards.md +290 -0
  933. package/skills/scientific/peer-review/scripts/generate_schematic.py +139 -0
  934. package/skills/scientific/peer-review/scripts/generate_schematic_ai.py +817 -0
  935. package/skills/scientific/scholar-evaluation/SKILL.md +298 -0
  936. package/skills/scientific/scholar-evaluation/references/evaluation_framework.md +663 -0
  937. package/skills/scientific/scholar-evaluation/scripts/calculate_scores.py +378 -0
  938. package/skills/scientific/scholar-evaluation/scripts/generate_schematic.py +139 -0
  939. package/skills/scientific/scholar-evaluation/scripts/generate_schematic_ai.py +817 -0
  940. package/skills/scientific/scientific-brainstorming/SKILL.md +189 -0
  941. package/skills/scientific/scientific-brainstorming/references/brainstorming_methods.md +326 -0
  942. package/skills/scientific/scientific-critical-thinking/SKILL.md +570 -0
  943. package/skills/scientific/scientific-critical-thinking/references/common_biases.md +364 -0
  944. package/skills/scientific/scientific-critical-thinking/references/evidence_hierarchy.md +484 -0
  945. package/skills/scientific/scientific-critical-thinking/references/experimental_design.md +496 -0
  946. package/skills/scientific/scientific-critical-thinking/references/logical_fallacies.md +478 -0
  947. package/skills/scientific/scientific-critical-thinking/references/scientific_method.md +169 -0
  948. package/skills/scientific/scientific-critical-thinking/references/statistical_pitfalls.md +506 -0
  949. package/skills/scientific/scientific-critical-thinking/scripts/generate_schematic.py +139 -0
  950. package/skills/scientific/scientific-critical-thinking/scripts/generate_schematic_ai.py +817 -0
  951. package/skills/wotann-imports/incremental-implementation.md +241 -0
  952. package/skills/wotann-imports/security-threat-model.md +81 -0
  953. package/skills/wotann-imports/triage.md +103 -0
  954. package/dist/build/deploy-targets/coolify.d.ts +0 -148
  955. package/dist/build/deploy-targets/coolify.d.ts.map +0 -1
  956. package/dist/build/deploy-targets/coolify.js +0 -339
  957. package/dist/build/deploy-targets/coolify.js.map +0 -1
  958. package/dist/build/deploy-targets/dokploy.d.ts +0 -139
  959. package/dist/build/deploy-targets/dokploy.d.ts.map +0 -1
  960. package/dist/build/deploy-targets/dokploy.js +0 -339
  961. package/dist/build/deploy-targets/dokploy.js.map +0 -1
  962. package/dist/claude/hardening/error-handler.d.ts +0 -52
  963. package/dist/claude/hardening/error-handler.d.ts.map +0 -1
  964. package/dist/claude/hardening/error-handler.js +0 -158
  965. package/dist/claude/hardening/error-handler.js.map +0 -1
  966. package/dist/cli/onboarding.d.ts +0 -34
  967. package/dist/cli/onboarding.d.ts.map +0 -1
  968. package/dist/cli/onboarding.js +0 -150
  969. package/dist/cli/onboarding.js.map +0 -1
  970. package/dist/core/agent-profiles.d.ts +0 -65
  971. package/dist/core/agent-profiles.d.ts.map +0 -1
  972. package/dist/core/agent-profiles.js +0 -137
  973. package/dist/core/agent-profiles.js.map +0 -1
  974. package/dist/desktop/supabase-relay.d.ts +0 -86
  975. package/dist/desktop/supabase-relay.d.ts.map +0 -1
  976. package/dist/desktop/supabase-relay.js +0 -335
  977. package/dist/desktop/supabase-relay.js.map +0 -1
  978. package/dist/intelligence/kg-builder.d.ts +0 -181
  979. package/dist/intelligence/kg-builder.d.ts.map +0 -1
  980. package/dist/intelligence/kg-builder.js +0 -807
  981. package/dist/intelligence/kg-builder.js.map +0 -1
  982. package/dist/orchestration/jean-orchestrator.d.ts +0 -79
  983. package/dist/orchestration/jean-orchestrator.d.ts.map +0 -1
  984. package/dist/orchestration/jean-orchestrator.js +0 -253
  985. package/dist/orchestration/jean-orchestrator.js.map +0 -1
  986. package/dist/orchestration/jean-registries/command-registry.d.ts +0 -85
  987. package/dist/orchestration/jean-registries/command-registry.d.ts.map +0 -1
  988. package/dist/orchestration/jean-registries/command-registry.js +0 -120
  989. package/dist/orchestration/jean-registries/command-registry.js.map +0 -1
  990. package/dist/orchestration/jean-registries/event-registry.d.ts +0 -80
  991. package/dist/orchestration/jean-registries/event-registry.d.ts.map +0 -1
  992. package/dist/orchestration/jean-registries/event-registry.js +0 -147
  993. package/dist/orchestration/jean-registries/event-registry.js.map +0 -1
  994. package/dist/orchestration/jean-registries/process-registry.d.ts +0 -71
  995. package/dist/orchestration/jean-registries/process-registry.d.ts.map +0 -1
  996. package/dist/orchestration/jean-registries/process-registry.js +0 -104
  997. package/dist/orchestration/jean-registries/process-registry.js.map +0 -1
  998. package/dist/orchestration/jean-registries/result-registry.d.ts +0 -71
  999. package/dist/orchestration/jean-registries/result-registry.d.ts.map +0 -1
  1000. package/dist/orchestration/jean-registries/result-registry.js +0 -97
  1001. package/dist/orchestration/jean-registries/result-registry.js.map +0 -1
  1002. package/dist/providers/bedrock-signer.d.ts +0 -23
  1003. package/dist/providers/bedrock-signer.d.ts.map +0 -1
  1004. package/dist/providers/bedrock-signer.js +0 -439
  1005. package/dist/providers/bedrock-signer.js.map +0 -1
  1006. package/dist/providers/harness-profiles.d.ts +0 -70
  1007. package/dist/providers/harness-profiles.d.ts.map +0 -1
  1008. package/dist/providers/harness-profiles.js +0 -210
  1009. package/dist/providers/harness-profiles.js.map +0 -1
  1010. package/dist/providers/vertex-oauth.d.ts +0 -21
  1011. package/dist/providers/vertex-oauth.d.ts.map +0 -1
  1012. package/dist/providers/vertex-oauth.js +0 -393
  1013. package/dist/providers/vertex-oauth.js.map +0 -1
  1014. package/dist/sandbox/backends/cloud-auth.d.ts +0 -50
  1015. package/dist/sandbox/backends/cloud-auth.d.ts.map +0 -1
  1016. package/dist/sandbox/backends/cloud-auth.js +0 -93
  1017. package/dist/sandbox/backends/cloud-auth.js.map +0 -1
  1018. package/dist/security/anti-distillation.d.ts +0 -46
  1019. package/dist/security/anti-distillation.d.ts.map +0 -1
  1020. package/dist/security/anti-distillation.js +0 -358
  1021. package/dist/security/anti-distillation.js.map +0 -1
  1022. package/dist/security/multi-encoding-decoder.d.ts +0 -47
  1023. package/dist/security/multi-encoding-decoder.d.ts.map +0 -1
  1024. package/dist/security/multi-encoding-decoder.js +0 -336
  1025. package/dist/security/multi-encoding-decoder.js.map +0 -1
  1026. package/dist/ui/accessibility.d.ts +0 -157
  1027. package/dist/ui/accessibility.d.ts.map +0 -1
  1028. package/dist/ui/accessibility.js +0 -232
  1029. package/dist/ui/accessibility.js.map +0 -1
  1030. package/dist/ui/animations.d.ts +0 -102
  1031. package/dist/ui/animations.d.ts.map +0 -1
  1032. package/dist/ui/animations.js +0 -277
  1033. package/dist/ui/animations.js.map +0 -1
  1034. package/dist/ui/components/Sparkline.d.ts +0 -81
  1035. package/dist/ui/components/Sparkline.d.ts.map +0 -1
  1036. package/dist/ui/components/Sparkline.js +0 -102
  1037. package/dist/ui/components/Sparkline.js.map +0 -1
  1038. package/dist/ui/input/mouse.d.ts +0 -139
  1039. package/dist/ui/input/mouse.d.ts.map +0 -1
  1040. package/dist/ui/input/mouse.js +0 -239
  1041. package/dist/ui/input/mouse.js.map +0 -1
  1042. package/dist/ui/sound.d.ts +0 -85
  1043. package/dist/ui/sound.d.ts.map +0 -1
  1044. package/dist/ui/sound.js +0 -126
  1045. package/dist/ui/sound.js.map +0 -1
package/dist/ui/App.js CHANGED
@@ -14,12 +14,15 @@ import { Box, Text, useApp, useInput } from "ink";
14
14
  import { StartupScreen } from "./components/StartupScreen.js";
15
15
  import { ChatView } from "./components/ChatView.js";
16
16
  import { PromptInput } from "./components/PromptInput.js";
17
+ import { ThemeProvider } from "./theme/context.js";
18
+ import { AGENT_TOOL_DEFINITIONS, executeAgentTool } from "./agent-tools.js";
17
19
  import { UnifiedStatusBar } from "./components/UnifiedStatusBar.js";
18
20
  import { DiffViewer } from "./components/DiffViewer.js";
19
21
  import { AgentStatusPanel } from "./components/AgentStatusPanel.js";
20
22
  import { HistoryPicker } from "./components/HistoryPicker.js";
21
23
  import { CommandPalette } from "./components/CommandPalette.js";
22
24
  import { ModelPicker } from "./components/ModelPicker.js";
25
+ import { ProviderSetupOverlay } from "./components/ProviderSetupOverlay.js";
23
26
  import { OptionPicker } from "./components/OptionPicker.js";
24
27
  import { CommandRegistry } from "./command-registry.js";
25
28
  // V9 Wave 2-M (R-09) — TUI palette command set. `registerR09Commands`
@@ -40,17 +43,22 @@ import { AuditLogPanel } from "./components/AuditLogPanel.js";
40
43
  import { AutomationsPanel } from "./components/AutomationsPanel.js";
41
44
  import { BlockBuffer } from "./terminal-blocks/block.js";
42
45
  import { Osc133Parser } from "./terminal-blocks/osc-133-parser.js";
46
+ import { MarkdownStreamState } from "./markdown-stream.js";
43
47
  import { makeMessageQueue } from "../core/message-queue.js";
44
48
  import { SkillRegistry } from "../skills/loader.js";
45
49
  import { execFileSync } from "node:child_process";
46
50
  import { join } from "node:path";
47
- import { existsSync } from "node:fs";
51
+ import { existsSync, readFileSync } from "node:fs";
52
+ import { whichBinary, nodeToolBinary } from "../utils/platform.js";
48
53
  import { KeybindingManager } from "./keybindings.js";
54
+ import { detectKeyboardProtocolSupport, enableKeyboardProtocol, } from "./terminal-keyboard-protocol.js";
49
55
  import { ThemeManager, cycleNorseTheme } from "./themes.js";
50
56
  import { TUIVoiceController } from "./voice-controller.js";
51
57
  import { buildPrimaryAgentStatuses, cycleModel, cyclePanel, cycleThinkingEffort, readWorkspaceDiff, resolveFileAttachments, } from "./helpers.js";
52
58
  import { parseReferences, resolveReferences, expandPromptWithReferences, } from "./context-references.js";
53
59
  import { parseDeepLink, executeDeepLink } from "../core/deep-link.js";
60
+ import { parseComputerActionCommand } from "./computer-action-parser.js";
61
+ import { detectAvailableTools, executeDesktopAction, listDesktopActions, } from "../computer-use/platform-bindings.js";
54
62
  // ── Types ──────────────────────────────────────────────────
55
63
  const VALID_MODES = [
56
64
  "default",
@@ -66,6 +74,71 @@ const VALID_MODES = [
66
74
  "review",
67
75
  "exploit",
68
76
  ];
77
+ function chooseProviderCredentialMethod(providerId, token, spec) {
78
+ if (!spec)
79
+ throw new Error(`Unknown provider: ${providerId}`);
80
+ const supported = spec.supportedMethods;
81
+ const normalizedToken = token.trim();
82
+ if (supported.includes("apiKey")) {
83
+ if (providerId === "copilot" && supported.includes("oauth"))
84
+ return "oauth";
85
+ if (supported.includes("subscription") &&
86
+ (normalizedToken.startsWith("sk-ant-oat") ||
87
+ (providerId === "codex" && !normalizedToken.startsWith("sk-")))) {
88
+ return "subscription";
89
+ }
90
+ return "apiKey";
91
+ }
92
+ if (supported.includes("oauth"))
93
+ return "oauth";
94
+ if (supported.includes("subscription"))
95
+ return "subscription";
96
+ if (supported.includes("cli"))
97
+ return "cli";
98
+ if (supported.includes("local"))
99
+ return "local";
100
+ throw new Error(`${spec.name} has no supported credential flow`);
101
+ }
102
+ function providerServiceMethodToStatusMethod(method) {
103
+ switch (method) {
104
+ case "apiKey":
105
+ return "api-key";
106
+ case "oauth":
107
+ return "oauth-token";
108
+ case "subscription":
109
+ return "subscription-direct";
110
+ case "local":
111
+ return "local";
112
+ case "cli":
113
+ return "local";
114
+ }
115
+ }
116
+ function providerServiceBilling(state, previous) {
117
+ const method = state.credential?.method;
118
+ if (method === "subscription")
119
+ return "subscription";
120
+ if (method === "local" || method === "cli" || state.tier === "local" || state.tier === "free") {
121
+ return "free";
122
+ }
123
+ if (state.credential)
124
+ return "api-key";
125
+ return previous.billing;
126
+ }
127
+ function mergeProviderState(previous, state) {
128
+ return {
129
+ ...previous,
130
+ label: state.name,
131
+ available: state.configured,
132
+ authMethod: state.credential
133
+ ? providerServiceMethodToStatusMethod(state.credential.method)
134
+ : state.tier === "local" || state.tier === "free"
135
+ ? "local"
136
+ : previous.authMethod,
137
+ billing: providerServiceBilling(state, previous),
138
+ models: state.models.map((model) => model.id),
139
+ error: state.lastError,
140
+ };
141
+ }
69
142
  // ── V9 T14.1 — Flicker-free TUI mode ───────────────────────
70
143
  //
71
144
  // Ink's default reconciliation is React's full virtual-DOM diff per render.
@@ -119,12 +192,14 @@ const NO_FLICKER_MODE = (() => {
119
192
  * matters more than skip-paint cost.
120
193
  */
121
194
  const MainScene = memo(function MainScene(props) {
122
- const { messages, isStreaming, streamingContent, currentModel, activePanel, diffPanel, agentStatuses, thinkingEffort, history, currentThemeBorder, currentThemePrimary, currentThemeInfo, } = props;
123
- return (_jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { flexGrow: 1, flexBasis: 0, children: _jsx(ChatView, { messages: messages, isStreaming: isStreaming, streamingContent: streamingContent, currentModel: currentModel }) }), _jsxs(Box, { width: 44, marginLeft: 1, borderStyle: "round", borderColor: currentThemeBorder, flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [_jsx(Text, { bold: true, color: currentThemePrimary, children: activePanel === "diff"
124
- ? "Diff"
125
- : activePanel === "agents"
126
- ? "Agents"
127
- : "◆ Tasks" }), _jsx(Text, { color: currentThemeInfo, children: "tab to cycle" })] }), activePanel === "diff" && diffPanel && (_jsx(DiffViewer, { filePath: diffPanel.filePath, hunks: diffPanel.hunks, compact: true, maxLines: 40 })), activePanel === "diff" && !diffPanel && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { color: currentThemePrimary, bold: true, children: "Quick keys" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+M" }), _jsx(Text, { dimColor: true, children: "switch model" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+P" }), _jsx(Text, { dimColor: true, children: "command palette" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+R" }), _jsx(Text, { dimColor: true, children: "history search" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+T" }), _jsx(Text, { dimColor: true, children: "thinking depth" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Tab" }), _jsx(Text, { dimColor: true, children: "cycle panel" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "/" }), _jsx(Text, { dimColor: true, children: "slash menu" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Esc Esc" }), _jsx(Text, { dimColor: true, children: "edit prev" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Edits land here as they happen." }) })] })), activePanel === "agents" && _jsx(AgentStatusPanel, { agents: agentStatuses }), activePanel === "tasks" && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Thinking" }), _jsx(Text, { dimColor: true, children: thinkingEffort })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Panel" }), _jsx(Text, { dimColor: true, children: activePanel })] }), _jsx(Text, { bold: true, dimColor: true, children: "Recent prompts" }), history.length === 0 && _jsx(Text, { dimColor: true, children: "(none yet \u2014 type to begin)" }), history.slice(0, 5).map((entry, index) => (_jsxs(Box, { gap: 1, children: [_jsxs(Text, { color: currentThemePrimary, children: [index + 1, "."] }), _jsxs(Text, { dimColor: true, children: [entry.slice(0, 36), entry.length > 36 ? "..." : ""] })] }, `task-${index}`)))] }))] })] }));
195
+ const { messages, isStreaming, streamingContent, streamingThinking, currentModel, activePanel, diffPanel, agentStatuses, thinkingEffort, history, computerTools, computerActions, currentThemeBorder, currentThemePrimary, currentThemeInfo, } = props;
196
+ return (_jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { flexGrow: 1, flexBasis: 0, children: _jsx(ChatView, { messages: messages, isStreaming: isStreaming, streamingContent: streamingContent, streamingThinking: streamingThinking, currentModel: currentModel }) }), _jsxs(Box, { width: 36, marginLeft: 1, borderStyle: "single", borderColor: currentThemeBorder, flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemePrimary, bold: true, children: "\u258D" }), _jsx(Text, { bold: true, color: currentThemePrimary, children: activePanel === "diff"
197
+ ? "Diff"
198
+ : activePanel === "agents"
199
+ ? "Agents"
200
+ : activePanel === "computer"
201
+ ? "Computer"
202
+ : "Tasks" })] }), _jsx(Text, { color: currentThemeInfo, dimColor: true, children: "\u21B5 tab" })] }), activePanel === "diff" && diffPanel && (_jsx(DiffViewer, { filePath: diffPanel.filePath, hunks: diffPanel.hunks, compact: true, maxLines: 40 })), activePanel === "diff" && !diffPanel && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { color: currentThemePrimary, bold: true, children: "Quick keys" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+O" }), _jsx(Text, { dimColor: true, children: "switch model" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+P" }), _jsx(Text, { dimColor: true, children: "command palette" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+R" }), _jsx(Text, { dimColor: true, children: "history search" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Ctrl+T" }), _jsx(Text, { dimColor: true, children: "thinking depth" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Tab" }), _jsx(Text, { dimColor: true, children: "cycle panel" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "/" }), _jsx(Text, { dimColor: true, children: "slash menu" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Esc Esc" }), _jsx(Text, { dimColor: true, children: "edit prev" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Edits land here as they happen." }) })] })), activePanel === "agents" && _jsx(AgentStatusPanel, { agents: agentStatuses }), activePanel === "tasks" && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Thinking" }), _jsx(Text, { dimColor: true, children: thinkingEffort })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, bold: true, children: "Panel" }), _jsx(Text, { dimColor: true, children: activePanel })] }), _jsx(Text, { bold: true, dimColor: true, children: "Recent prompts" }), history.length === 0 && _jsx(Text, { dimColor: true, children: "(none yet \u2014 type to begin)" }), history.slice(0, 5).map((entry, index) => (_jsxs(Box, { gap: 1, children: [_jsxs(Text, { color: currentThemePrimary, children: [index + 1, "."] }), _jsxs(Text, { dimColor: true, children: [entry.slice(0, 36), entry.length > 36 ? "..." : ""] })] }, `task-${index}`)))] })), activePanel === "computer" && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { color: currentThemePrimary, bold: true, children: "Desktop control" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: computerTools.length > 0 ? "green" : "yellow", bold: true, children: computerTools.length > 0 ? "Ready" : "Setup" }), _jsx(Text, { dimColor: true, children: computerTools.length > 0 ? computerTools.join(", ") : "install cliclick/xdotool" })] }), _jsx(Text, { bold: true, dimColor: true, children: "Actions" }), computerActions.slice(0, 10).map((action) => (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: currentThemeInfo, children: "\u2022" }), _jsx(Text, { children: action })] }, action))), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: currentThemeInfo, children: "/computer click 420 320" }), _jsx(Text, { color: currentThemeInfo, children: "/computer type hello from WOTANN" }), _jsx(Text, { color: currentThemeInfo, children: "/computer key command+c" }), _jsx(Text, { dimColor: true, children: "iOS Desktop Control uses the same shared route table." })] })] }))] })] }));
128
203
  },
129
204
  // Custom equality: skip re-render if every prop reference is unchanged
130
205
  // AND messages.length matches. We use length + last-message identity as
@@ -137,6 +212,8 @@ const MainScene = memo(function MainScene(props) {
137
212
  return false;
138
213
  if (prev.streamingContent !== next.streamingContent)
139
214
  return false;
215
+ if (prev.streamingThinking !== next.streamingThinking)
216
+ return false;
140
217
  if (prev.currentModel !== next.currentModel)
141
218
  return false;
142
219
  if (prev.activePanel !== next.activePanel)
@@ -149,6 +226,10 @@ const MainScene = memo(function MainScene(props) {
149
226
  return false;
150
227
  if (prev.history !== next.history)
151
228
  return false;
229
+ if (prev.computerTools !== next.computerTools)
230
+ return false;
231
+ if (prev.computerActions !== next.computerActions)
232
+ return false;
152
233
  if (prev.currentThemeBorder !== next.currentThemeBorder)
153
234
  return false;
154
235
  if (prev.currentThemePrimary !== next.currentThemePrimary)
@@ -176,6 +257,14 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
176
257
  const [messages, setMessages] = useState(() => [...initialMessages]);
177
258
  const [isStreaming, setIsStreaming] = useState(false);
178
259
  const [streamingContent, setStreamingContent] = useState("");
260
+ const [streamingThinking, setStreamingThinking] = useState("");
261
+ // Synchronous guard against React state-update races: setIsStreaming
262
+ // is async, so a second Enter pressed within a render window sees the
263
+ // old false value and takes the regular path again — pushing a duplicate
264
+ // user message AND opening a parallel runtime.query. The ref flips
265
+ // synchronously inside handleSubmit so subsequent calls in the same
266
+ // tick see the in-flight state and route to the queue path.
267
+ const streamingGuardRef = useRef(false);
179
268
  const [promptValue, setPromptValue] = useState("");
180
269
  const [history, setHistory] = useState(() => [...initialMessages]
181
270
  .filter((msg) => msg.role === "user")
@@ -183,13 +272,28 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
183
272
  .reverse());
184
273
  const [currentMode, setCurrentMode] = useState(runtime?.getCurrentMode() ?? "default");
185
274
  const [currentModel, setCurrentModel] = useState(initialModel);
275
+ // Mirror of runtime.session.provider so the picker, status bar, and
276
+ // routing all agree on which provider is active. Initialized to the
277
+ // bootstrap-detected provider; updated in lock-step with currentModel
278
+ // whenever the user picks a new provider/model pair.
279
+ const [currentProvider, setCurrentProvider] = useState(initialProvider);
280
+ const [liveProviders, setLiveProviders] = useState(providers);
281
+ useEffect(() => {
282
+ setLiveProviders(providers);
283
+ }, [providers]);
186
284
  const [thinkingEffort, setThinkingEffort] = useState(runtime?.getThinkingEffort() ?? "medium");
187
285
  const themeManagerRef = useRef(new ThemeManager("default", uiStatePath));
188
286
  const [themeName, setThemeName] = useState(themeManagerRef.current.getCurrent().name);
189
287
  const [activePanel, setActivePanel] = useState(() => {
190
288
  const persistedPanel = themeManagerRef.current.readPersistedState().panel;
191
- return persistedPanel === "agents" || persistedPanel === "tasks" ? persistedPanel : "diff";
289
+ return persistedPanel === "agents" ||
290
+ persistedPanel === "tasks" ||
291
+ persistedPanel === "computer"
292
+ ? persistedPanel
293
+ : "diff";
192
294
  });
295
+ const [computerTools, setComputerTools] = useState(() => detectAvailableTools());
296
+ const [computerActions] = useState(() => listDesktopActions());
193
297
  const [diffEntries, setDiffEntries] = useState(() => readWorkspaceDiff(workingDir));
194
298
  const [agentStatuses, setAgentStatuses] = useState(() => buildPrimaryAgentStatuses({
195
299
  model: initialModel,
@@ -207,7 +311,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
207
311
  });
208
312
  const abortRef = useRef(null);
209
313
  const skillRegistryRef = useRef(null);
210
- const keybindingManagerRef = useRef(new KeybindingManager());
314
+ const keybindingManagerRef = useRef(new KeybindingManager(undefined, detectKeyboardProtocolSupport()));
211
315
  const voiceControllerRef = useRef(new TUIVoiceController());
212
316
  const [voiceBusy, setVoiceBusy] = useState(false);
213
317
  // Mid-stream message queue (per-session, never module-global per QB#7).
@@ -219,10 +323,11 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
219
323
  const [queueDepth, setQueueDepth] = useState(0);
220
324
  const [showHistoryPicker, setShowHistoryPicker] = useState(false);
221
325
  const [showCommandPalette, setShowCommandPalette] = useState(false);
222
- // Ctrl+M opens an interactive provider/model picker (replaces the
326
+ // Ctrl+O opens an interactive provider/model picker (replaces the
223
327
  // prior round-robin cycleModel pattern that was unusable past 2-3
224
328
  // providers). Modeled after OpenClaw's modal-overlay picker.
225
329
  const [showModelPicker, setShowModelPicker] = useState(false);
330
+ const [showProviderSetup, setShowProviderSetup] = useState(false);
226
331
  // Generic option picker — driven by `optionPicker` (null when closed).
227
332
  // Used by `/mode`, `/theme`, `/thinking` to give users an interactive
228
333
  // selector instead of a static list with "type the next arg" hint.
@@ -265,6 +370,18 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
265
370
  if (messages.length > 0 && showStartup)
266
371
  setShowStartup(false);
267
372
  }, [messages.length, showStartup]);
373
+ // Opt into the kitty keyboard protocol on supported terminals so
374
+ // Ctrl+M / Ctrl+I / Ctrl+H / Ctrl+J become distinguishable from
375
+ // Enter/Tab/Backspace/LF. Cleanup on unmount restores the parent's
376
+ // keyboard mode. No-op on unsupported terminals — Ctrl+O / Ctrl+X
377
+ // remain the portable bindings.
378
+ useEffect(() => {
379
+ const mode = detectKeyboardProtocolSupport();
380
+ const restore = enableKeyboardProtocol(mode);
381
+ return () => {
382
+ restore();
383
+ };
384
+ }, []);
268
385
  // Wire the TUI-owned MessageQueue into the runtime so the runtime's
269
386
  // pre-model drain reads from the same queue the submit handler
270
387
  // enqueues to. Inspired by langchain-ai/open-swe
@@ -331,6 +448,38 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
331
448
  turnCount,
332
449
  }));
333
450
  }, [currentModel, currentMode, isStreaming, turnCount]);
451
+ // Startup auth-expiry sweep — runs once on mount. For each provider
452
+ // that has a known expiry timestamp (codex OAuth, anthropic
453
+ // subscription tokens), warn the user if it's in the past or expiring
454
+ // within an hour. Catches stale credentials BEFORE the user spends
455
+ // 30s on a doomed query and the watchdog has to clean up.
456
+ useEffect(() => {
457
+ void (async () => {
458
+ try {
459
+ const { detectExistingCodexCredential } = await import("../providers/codex-detector.js");
460
+ const codex = detectExistingCodexCredential();
461
+ if (codex.found && codex.expiresAt) {
462
+ const expiresInMs = codex.expiresAt * 1000 - Date.now();
463
+ if (expiresInMs < 0) {
464
+ appendSystemMessage(`⚠ Codex token expired ${Math.abs(Math.round(expiresInMs / 60000))}m ago. ` +
465
+ `Run \`codex login\` in another shell, then \`wotann login codex\` to refresh.`);
466
+ }
467
+ else if (expiresInMs < 60 * 60 * 1000) {
468
+ appendSystemMessage(`⚠ Codex token expires in ${Math.round(expiresInMs / 60000)}m. ` +
469
+ `Refresh now via \`codex login\` to avoid mid-session auth failures.`);
470
+ }
471
+ }
472
+ }
473
+ catch {
474
+ // Detection is best-effort; never block startup on it.
475
+ }
476
+ })();
477
+ // Mount-only — no deps. Re-running on every render would spam the
478
+ // chat with the same warning. The `react-hooks/exhaustive-deps`
479
+ // rule isn't loaded in eslint.config.js, so a disable-comment for
480
+ // it is itself a lint error ("rule not found") — see CI failure
481
+ // 25271249471. Closures here are mount-stable by design.
482
+ }, []);
334
483
  // Sync stats from runtime after each query
335
484
  const syncStatsFromRuntime = useCallback(() => {
336
485
  if (!runtime)
@@ -402,8 +551,8 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
402
551
  // pressing the bound key still cycles linearly for users who
403
552
  // rely on muscle memory — but the default is now a proper
404
553
  // searchable list so the 19-provider stack is navigable.
405
- // The cycle path remains accessible via the `Ctrl+Shift+M`
406
- // chord and the `/model next` command.
554
+ // The cycle path remains accessible via the `/model next`
555
+ // command.
407
556
  setShowModelPicker((prev) => !prev);
408
557
  break;
409
558
  }
@@ -559,7 +708,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
559
708
  // across renders while still reflecting live app state.
560
709
  const paletteStateRef = useRef({
561
710
  currentModel,
562
- providers,
711
+ providers: liveProviders,
563
712
  themeName,
564
713
  history,
565
714
  runtime,
@@ -567,7 +716,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
567
716
  });
568
717
  paletteStateRef.current = {
569
718
  currentModel,
570
- providers,
719
+ providers: liveProviders,
571
720
  themeName,
572
721
  history,
573
722
  runtime,
@@ -629,7 +778,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
629
778
  {
630
779
  id: "palette.toggle-context-inspector",
631
780
  label: "Toggle Context Inspector",
632
- description: "Show context source panel (Ctrl+I)",
781
+ description: "Show context source panel (Ctrl+X)",
633
782
  keywords: ["context", "inspect", "sources"],
634
783
  handler: () => {
635
784
  setShowContextPanel((prev) => !prev);
@@ -698,6 +847,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
698
847
  onVoiceCapture: () => void handleVoiceCapture(),
699
848
  onCycleMode: cycleMode,
700
849
  onOpenMagic: openMagic,
850
+ onOpenProviderSetup: () => setShowProviderSetup(true),
701
851
  });
702
852
  // Wave 4 — admin overlay commands (trust, gdpr, audit, automations).
703
853
  // These are registered alongside R-09 so the same Cmd+P fuzzy search
@@ -816,13 +966,237 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
816
966
  runtime.close();
817
967
  exit();
818
968
  return true;
819
- case "/clear":
820
- setMessages([]);
821
- setShowStartup(true);
822
- setTurnCount(0);
823
- setPromptValue("");
824
- setStats({ cost: 0, contextPercent: 0, reads: 0, edits: 0, bashCalls: 0 });
969
+ case "/clear": {
970
+ // Confirmation picker — wiping the conversation is destructive
971
+ // (no undo path) and a stray /clear costs the user the entire
972
+ // turn history. Skip the picker when the user types
973
+ // "/clear yes" or the message list is already empty.
974
+ if (arg === "yes" || arg === "force" || messages.length === 0) {
975
+ setMessages([]);
976
+ setShowStartup(true);
977
+ setTurnCount(0);
978
+ setPromptValue("");
979
+ setStats({ cost: 0, contextPercent: 0, reads: 0, edits: 0, bashCalls: 0 });
980
+ return true;
981
+ }
982
+ setOptionPicker({
983
+ title: `Clear ${messages.length} message${messages.length === 1 ? "" : "s"}?`,
984
+ options: [
985
+ {
986
+ value: "cancel",
987
+ label: "Cancel",
988
+ description: "Keep the conversation",
989
+ },
990
+ {
991
+ value: "confirm",
992
+ label: "Clear conversation",
993
+ description: "Delete all turns. Cost + edit stats also reset.",
994
+ },
995
+ ],
996
+ currentValue: "cancel",
997
+ onSelect: (value) => {
998
+ setOptionPicker(null);
999
+ if (value === "confirm") {
1000
+ setMessages([]);
1001
+ setShowStartup(true);
1002
+ setTurnCount(0);
1003
+ setPromptValue("");
1004
+ setStats({
1005
+ cost: 0,
1006
+ contextPercent: 0,
1007
+ reads: 0,
1008
+ edits: 0,
1009
+ bashCalls: 0,
1010
+ });
1011
+ appendSystemMessage("Conversation cleared.");
1012
+ }
1013
+ },
1014
+ });
1015
+ return true;
1016
+ }
1017
+ case "/save": {
1018
+ // Save the current conversation to ~/.wotann/conversations/<name>.md
1019
+ // for later /recall. Uses the user-supplied name (slug-cleaned) or
1020
+ // the active session id when no name given.
1021
+ (async () => {
1022
+ try {
1023
+ const slug = arg
1024
+ ? arg
1025
+ .toLowerCase()
1026
+ .replace(/[^a-z0-9]+/g, "-")
1027
+ .replace(/^-+|-+$/g, "")
1028
+ .slice(0, 60)
1029
+ : `session-${Date.now()}`;
1030
+ const fs = await import("node:fs/promises");
1031
+ const fsSync = await import("node:fs");
1032
+ const { resolveWotannHomeSubdir } = await import("../utils/wotann-home.js");
1033
+ const dir = resolveWotannHomeSubdir("conversations");
1034
+ if (!fsSync.existsSync(dir)) {
1035
+ fsSync.mkdirSync(dir, { recursive: true, mode: 0o700 });
1036
+ }
1037
+ const path = `${dir}/${slug}.md`;
1038
+ const lines = [
1039
+ `# WOTANN conversation: ${slug}`,
1040
+ ``,
1041
+ `Saved: ${new Date().toISOString()}`,
1042
+ `Provider: ${currentProvider} · Model: ${currentModel} · Mode: ${currentMode}`,
1043
+ ``,
1044
+ ...messages.map((m) => {
1045
+ const role = m.role === "user" ? "You" : m.role === "assistant" ? "WOTANN" : "System";
1046
+ return `## ${role}\n\n${m.content}\n`;
1047
+ }),
1048
+ ];
1049
+ await fs.writeFile(path, lines.join("\n"), "utf-8");
1050
+ sysMsg(`Saved ${messages.length} message${messages.length === 1 ? "" : "s"} to ${path}`);
1051
+ }
1052
+ catch (err) {
1053
+ sysMsg(`/save failed: ${err instanceof Error ? err.message : String(err)}`);
1054
+ }
1055
+ })();
1056
+ return true;
1057
+ }
1058
+ case "/recall": {
1059
+ // List or fuzzy-search saved conversations from
1060
+ // ~/.wotann/conversations/. With no arg, opens an inline picker
1061
+ // of every saved file. With an arg, fuzzy-matches names and
1062
+ // opens that file (read-only — replays into the chat as
1063
+ // historical context, doesn't resume the live session).
1064
+ (async () => {
1065
+ try {
1066
+ const fs = await import("node:fs/promises");
1067
+ const fsSync = await import("node:fs");
1068
+ const { resolveWotannHomeSubdir } = await import("../utils/wotann-home.js");
1069
+ const dir = resolveWotannHomeSubdir("conversations");
1070
+ if (!fsSync.existsSync(dir)) {
1071
+ sysMsg("No saved conversations yet. /save [name] to create one.");
1072
+ return;
1073
+ }
1074
+ const files = (await fs.readdir(dir))
1075
+ .filter((f) => f.endsWith(".md"))
1076
+ .sort()
1077
+ .reverse();
1078
+ if (files.length === 0) {
1079
+ sysMsg("No saved conversations. /save [name] to create one.");
1080
+ return;
1081
+ }
1082
+ if (arg) {
1083
+ const match = files.find((f) => f === `${arg}.md` || f.includes(arg)) ?? null;
1084
+ if (!match) {
1085
+ sysMsg(`No conversation matching "${arg}". Available: ${files.slice(0, 5).join(", ")}`);
1086
+ return;
1087
+ }
1088
+ const body = await fs.readFile(`${dir}/${match}`, "utf-8");
1089
+ sysMsg(`── ${match} ──\n${body.slice(0, 4000)}${body.length > 4000 ? "\n[truncated]" : ""}`);
1090
+ return;
1091
+ }
1092
+ setOptionPicker({
1093
+ title: `Recall conversation (${files.length} saved)`,
1094
+ options: files.slice(0, 20).map((f) => ({
1095
+ value: f,
1096
+ label: f.replace(/\.md$/, ""),
1097
+ description: `${dir}/${f}`,
1098
+ })),
1099
+ onSelect: (value) => {
1100
+ setOptionPicker(null);
1101
+ void (async () => {
1102
+ try {
1103
+ const body = await (await import("node:fs/promises")).readFile(`${dir}/${value}`, "utf-8");
1104
+ appendSystemMessage(`── ${value} ──\n${body.slice(0, 4000)}${body.length > 4000 ? "\n[truncated]" : ""}`);
1105
+ }
1106
+ catch (err) {
1107
+ appendSystemMessage(`Failed to read ${value}: ${err instanceof Error ? err.message : String(err)}`);
1108
+ }
1109
+ })();
1110
+ },
1111
+ });
1112
+ }
1113
+ catch (err) {
1114
+ sysMsg(`/recall failed: ${err instanceof Error ? err.message : String(err)}`);
1115
+ }
1116
+ })();
1117
+ return true;
1118
+ }
1119
+ case "/share": {
1120
+ // Copy the conversation as markdown to the clipboard. macOS
1121
+ // pbcopy is the only platform we can rely on without adding a
1122
+ // dep — Linux users can paste from the system message preview.
1123
+ if (messages.length === 0) {
1124
+ sysMsg("No conversation to share.");
1125
+ return true;
1126
+ }
1127
+ const md = [
1128
+ `# WOTANN conversation`,
1129
+ ``,
1130
+ `${new Date().toISOString()} · ${currentProvider}/${currentModel} · mode: ${currentMode}`,
1131
+ ``,
1132
+ ...messages.map((m) => {
1133
+ const role = m.role === "user" ? "You" : m.role === "assistant" ? "WOTANN" : "System";
1134
+ return `## ${role}\n\n${m.content}\n`;
1135
+ }),
1136
+ ].join("\n");
1137
+ try {
1138
+ execFileSync("pbcopy", [], { input: md, timeout: 2000 });
1139
+ sysMsg(`Copied ${messages.length} message${messages.length === 1 ? "" : "s"} (${md.length} chars) to clipboard.`);
1140
+ }
1141
+ catch {
1142
+ // pbcopy unavailable — paste a preview into the chat so the
1143
+ // user can copy from the terminal's scrollback.
1144
+ sysMsg(`pbcopy unavailable. Markdown preview:\n${md.slice(0, 2000)}${md.length > 2000 ? "\n[truncated — /save to write to disk]" : ""}`);
1145
+ }
825
1146
  return true;
1147
+ }
1148
+ case "/agents": {
1149
+ // List or pick from registered orchestration agents. The
1150
+ // registry is populated at runtime startup with planning /
1151
+ // implementation / utility / specialist tiers; /agents
1152
+ // surfaces them without forcing the user to remember the
1153
+ // catalog. Selecting a row prints a short profile (tier,
1154
+ // model, description) — actual dispatch goes through
1155
+ // /dispatch since it needs a task arg.
1156
+ if (!runtime) {
1157
+ sysMsg("Agents require WotannRuntime.");
1158
+ return true;
1159
+ }
1160
+ const registry = runtime.getAgentRegistry();
1161
+ const all = registry.getAll();
1162
+ if (all.length === 0) {
1163
+ sysMsg("No agents registered. Add YAML in .wotann/agents/ to populate.");
1164
+ return true;
1165
+ }
1166
+ if (arg === "list") {
1167
+ sysMsg([
1168
+ `${all.length} registered agents:`,
1169
+ ...all.map((a) => ` ${a.id.padEnd(28)} ${a.model.padEnd(10)} ${a.name}`),
1170
+ ].join("\n"));
1171
+ return true;
1172
+ }
1173
+ setOptionPicker({
1174
+ title: `Agent registry (${all.length} agents)`,
1175
+ options: all.slice(0, 30).map((a) => ({
1176
+ value: a.id,
1177
+ label: a.id,
1178
+ description: `${a.model} · ${a.name}`,
1179
+ })),
1180
+ onSelect: (value) => {
1181
+ setOptionPicker(null);
1182
+ const def = registry.get(value);
1183
+ if (!def) {
1184
+ appendSystemMessage(`Agent "${value}" not found.`);
1185
+ return;
1186
+ }
1187
+ appendSystemMessage([
1188
+ `╭─ Agent: ${def.id} ─`,
1189
+ `│ Name: ${def.name}`,
1190
+ `│ Model: ${def.model}`,
1191
+ `│ Allowed tools: ${def.allowedTools.length}`,
1192
+ `│ Denied tools: ${def.deniedTools.length}`,
1193
+ `│ Max turns: ${def.maxTurns}`,
1194
+ `╰─ Use /dispatch ${def.id} <task> to invoke ─`,
1195
+ ].join("\n"));
1196
+ },
1197
+ });
1198
+ return true;
1199
+ }
826
1200
  case "/help":
827
1201
  sysMsg([
828
1202
  "╭─ WOTANN Commands ──────────────────────────────────────────╮",
@@ -830,22 +1204,30 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
830
1204
  "│ ── Session ── │",
831
1205
  "│ /help Show this help │",
832
1206
  "│ /clear Clear conversation + reset │",
1207
+ "│ /save [name] Save conversation to .wotann/ │",
1208
+ "│ /recall [name] Pick a saved conversation to view │",
1209
+ "│ /share Copy current conversation to clipboard│",
1210
+ "│ /agents Browse registered agents (picker) │",
833
1211
  "│ /exit Exit WOTANN │",
834
1212
  "│ /history Show recent prompts │",
835
1213
  "│ /compact Compact context window │",
836
1214
  "│ │",
837
1215
  "│ ── Configuration ── │",
838
1216
  "│ /config [key=val] View/edit configuration │",
1217
+ "│ /login <prov> [key] Configure provider auth in daemon │",
839
1218
  "│ /providers List providers + auth status │",
840
- "│ /model [name] Show or switch model │",
1219
+ "│ /provider [name] Switch provider (keeps model) │",
1220
+ "│ /model [name] Open picker / switch model │",
1221
+ "│ /remote [set|clear] Configure iOS off-WiFi endpoint │",
841
1222
  "│ /mode [name] Show or switch behavioral mode │",
842
1223
  "│ /thinking [level] Set reasoning effort (low/med/high) │",
843
1224
  "│ /theme [name] Show or switch persisted theme │",
844
- "│ /permission [mode] Switch approval mode │",
1225
+ "│ /permission [mode] Approval mode (autonomous available) │",
1226
+ "│ /verifier [on|off] Toggle secondary-AI judge │",
845
1227
  "│ │",
846
1228
  "│ ── Intelligence ── │",
847
1229
  "│ /context Show context window budget │",
848
- "│ /inspect Context source inspector (Ctrl+I) │",
1230
+ "│ /inspect Context source inspector (Ctrl+X) │",
849
1231
  "│ /skills [query] List/search skills │",
850
1232
  "│ /memory [query] Search memory (FTS5 + semantic) │",
851
1233
  "│ /learnings Show cross-session patterns │",
@@ -896,21 +1278,204 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
896
1278
  "│ /context-panel Toggle context source panel │",
897
1279
  "│ │",
898
1280
  "│ ── Shortcuts ── │",
899
- "│ Ctrl+C Abort streaming Ctrl+M Cycle model │",
900
- "│ Ctrl+R Search history Ctrl+I Context panel │",
1281
+ "│ Ctrl+C Abort streaming Ctrl+O Cycle model │",
1282
+ "│ Ctrl+R Search history Ctrl+X Context panel │",
901
1283
  "│ Ctrl+T Thinking depth Ctrl+/ Memory search │",
902
1284
  "│ Tab Cycle right panel Esc Close overlay │",
903
1285
  "╰──────────────────────────────────────────────────────────╯",
904
1286
  ].join("\n"));
905
1287
  return true;
1288
+ case "/remote": {
1289
+ const parts = arg.trim().split(/\s+/).filter(Boolean);
1290
+ const action = parts[0] ?? "status";
1291
+ const targetUrl = action === "set" || action === "configure"
1292
+ ? parts.slice(1).join(" ")
1293
+ : action !== "status" && action !== "clear"
1294
+ ? arg.trim()
1295
+ : "";
1296
+ if ((action === "set" || action === "configure") && !targetUrl) {
1297
+ appendSystemMessage("Usage: /remote set wss://your-host.example");
1298
+ return true;
1299
+ }
1300
+ sysMsg("Checking remote access through KAIROS daemon...");
1301
+ void (async () => {
1302
+ try {
1303
+ const { KairosIPCClient } = await import("../daemon/kairos-ipc.js");
1304
+ const client = new KairosIPCClient(undefined, { requestTimeoutMs: 5_000 });
1305
+ try {
1306
+ const ok = await client.connect();
1307
+ if (!ok) {
1308
+ appendSystemMessage([
1309
+ "KAIROS daemon IPC is not reachable.",
1310
+ "Run `wotann daemon start`, then retry `/remote`.",
1311
+ ].join("\n"));
1312
+ return;
1313
+ }
1314
+ const remote = (await (action === "clear"
1315
+ ? client.call("remote.access.clear")
1316
+ : targetUrl
1317
+ ? client.call("remote.access.configure", { url: targetUrl })
1318
+ : client.call("remote.access.status")));
1319
+ const endpointLines = (remote.endpoints ?? []).map((endpoint) => {
1320
+ const scheme = endpoint.scheme ?? "ws";
1321
+ const port = endpoint.port ?? (scheme === "wss" ? 443 : 3849);
1322
+ return ` ${endpoint.label ?? "Remote"}: ${scheme}://${endpoint.host ?? "unknown"}:${port}`;
1323
+ });
1324
+ const configured = remote.setupOptions?.find((option) => option.id === "configured-url");
1325
+ appendSystemMessage([
1326
+ "Remote Access",
1327
+ "",
1328
+ ` Status: ${remote.status ?? "unknown"}`,
1329
+ ` Mode: ${remote.mode ?? "unknown"}`,
1330
+ remote.message ? ` ${remote.message}` : "",
1331
+ ...endpointLines,
1332
+ endpointLines.length === 0 && configured?.command
1333
+ ? ` Configure: ${configured.command}`
1334
+ : "",
1335
+ endpointLines.length === 0 ? " TUI: /remote set wss://your-host.example" : "",
1336
+ ]
1337
+ .filter(Boolean)
1338
+ .join("\n"));
1339
+ }
1340
+ finally {
1341
+ client.disconnect();
1342
+ }
1343
+ }
1344
+ catch (err) {
1345
+ appendSystemMessage(`Remote access command failed: ${err.message ?? "unknown error"}`);
1346
+ }
1347
+ })();
1348
+ return true;
1349
+ }
1350
+ case "/reauth": {
1351
+ const [providerIdRaw = "", ...tokenParts] = arg.trim().split(/\s+/);
1352
+ const providerId = providerIdRaw.toLowerCase();
1353
+ const token = tokenParts.join(" ").trim();
1354
+ if (!providerId) {
1355
+ setShowProviderSetup(true);
1356
+ return true;
1357
+ }
1358
+ void (async () => {
1359
+ try {
1360
+ const { getProviderService } = await import("../providers/provider-service.js");
1361
+ const service = getProviderService();
1362
+ const snapshot = await service.getSnapshot({ force: false });
1363
+ const known = snapshot.providers.find((provider) => provider.id === providerId);
1364
+ const spec = service.getSpec(providerId);
1365
+ if (!known) {
1366
+ appendSystemMessage(`Unknown provider "${providerId}". Run /providers to inspect the daemon catalog.`);
1367
+ return;
1368
+ }
1369
+ if (providerId === "ollama" && !token) {
1370
+ appendSystemMessage("Ollama uses the local desktop daemon and does not need an API key. Start Ollama on the Mac, then run /providers or Ctrl+O.");
1371
+ return;
1372
+ }
1373
+ if (!token) {
1374
+ appendSystemMessage([
1375
+ `Paste a credential to save ${known.name}: /login ${providerId} <token>`,
1376
+ providerId === "copilot"
1377
+ ? "Use a GitHub token with Copilot access; WOTANN stores it as the Copilot OAuth credential."
1378
+ : "",
1379
+ ]
1380
+ .filter(Boolean)
1381
+ .join("\n"));
1382
+ return;
1383
+ }
1384
+ const method = chooseProviderCredentialMethod(providerId, token, spec);
1385
+ const state = await service.saveCredential(providerId, {
1386
+ method,
1387
+ token,
1388
+ label: `${known.name} from TUI`,
1389
+ });
1390
+ const refreshed = await service.getSnapshot({ force: true });
1391
+ setLiveProviders((prev) => prev.map((provider) => {
1392
+ const fresh = refreshed.providers.find((candidate) => candidate.id === provider.provider);
1393
+ return fresh ? mergeProviderState(provider, fresh) : provider;
1394
+ }));
1395
+ const model = state?.defaultModel ?? state?.models[0]?.id ?? currentModel;
1396
+ if (state?.configured && model) {
1397
+ setCurrentProvider(providerId);
1398
+ setCurrentModel(model);
1399
+ runtime?.setActiveProvider(providerId, model);
1400
+ }
1401
+ appendSystemMessage(`${known.name} ${method} credential saved. ${model ? `Active model: ${model}` : "Refresh providers to discover models."}`);
1402
+ }
1403
+ catch (err) {
1404
+ appendSystemMessage(`/login failed: ${err instanceof Error ? err.message : String(err)}`);
1405
+ }
1406
+ })();
1407
+ return true;
1408
+ }
1409
+ case "/provider":
906
1410
  case "/providers": {
907
- const lines = providers.map((p) => {
908
- const icon = p.available ? "●" : "○";
909
- const models = p.available && p.models.length > 0 ? ` ${p.models.slice(0, 3).join(", ")}` : "";
910
- return ` ${icon} ${p.provider}${p.available ? "" : " (not configured)"}${models}`;
1411
+ // Unified provider command. Pre-2026-05-03 there were TWO
1412
+ // commands (/provider for switch, /providers for status);
1413
+ // duplicate surface area. Now one command does both:
1414
+ // /provider inline picker
1415
+ // /provider <name> → switch immediately
1416
+ // /provider status → list all providers + auth state
1417
+ // /providers → alias of /provider status (legacy)
1418
+ const availableProviders = providers.filter((p) => p.available);
1419
+ const showStatus = cmd === "/providers" || arg === "status" || arg === "ls" || arg === "list";
1420
+ if (showStatus) {
1421
+ const lines = providers.map((p) => {
1422
+ const icon = p.available ? "●" : "○";
1423
+ const active = p.provider === currentProvider ? " ← active" : "";
1424
+ const models = p.available && p.models.length > 0 ? ` ${p.models.slice(0, 3).join(", ")}` : "";
1425
+ return ` ${icon} ${p.provider}${p.available ? "" : " (not configured)"}${active}${models}`;
1426
+ });
1427
+ const activeCount = availableProviders.length;
1428
+ sysMsg(`Providers: ${activeCount}/${providers.length} active\n\n${lines.join("\n")}\n\n` +
1429
+ `Switch: \`/provider <name>\` or just \`/provider\` for picker.`);
1430
+ return true;
1431
+ }
1432
+ if (availableProviders.length === 0) {
1433
+ sysMsg("No providers configured. Run `wotann onboard` to set one up.");
1434
+ return true;
1435
+ }
1436
+ if (arg) {
1437
+ const target = availableProviders.find((p) => p.provider === arg);
1438
+ if (!target) {
1439
+ sysMsg(`Provider "${arg}" not authed. Available: ${availableProviders
1440
+ .map((p) => p.provider)
1441
+ .join(", ")}`);
1442
+ return true;
1443
+ }
1444
+ const nextModel = target.models.includes(currentModel)
1445
+ ? currentModel
1446
+ : (target.models[0] ?? currentModel);
1447
+ setCurrentProvider(target.provider);
1448
+ setCurrentModel(nextModel);
1449
+ runtime?.setActiveProvider(target.provider, nextModel);
1450
+ sysMsg(`Provider switched to: ${target.provider} (model: ${nextModel})`);
1451
+ return true;
1452
+ }
1453
+ setOptionPicker({
1454
+ title: "Switch provider",
1455
+ options: availableProviders.map((p) => ({
1456
+ value: p.provider,
1457
+ label: p.provider,
1458
+ description: p.billing === "subscription"
1459
+ ? `subscription · ${p.models.length} models`
1460
+ : p.billing === "free"
1461
+ ? `free · ${p.models.length} models`
1462
+ : `API key · ${p.models.length} models`,
1463
+ })),
1464
+ currentValue: currentProvider,
1465
+ onSelect: (value) => {
1466
+ setOptionPicker(null);
1467
+ const target = availableProviders.find((p) => p.provider === value);
1468
+ if (!target)
1469
+ return;
1470
+ const nextModel = target.models.includes(currentModel)
1471
+ ? currentModel
1472
+ : (target.models[0] ?? currentModel);
1473
+ setCurrentProvider(target.provider);
1474
+ setCurrentModel(nextModel);
1475
+ runtime?.setActiveProvider(target.provider, nextModel);
1476
+ appendSystemMessage(`Provider switched to: ${target.provider} (model: ${nextModel})`);
1477
+ },
911
1478
  });
912
- const active = providers.filter((p) => p.available).length;
913
- sysMsg(`Providers: ${active}/${providers.length} active\n\n${lines.join("\n")}`);
914
1479
  return true;
915
1480
  }
916
1481
  case "/skills": {
@@ -1062,19 +1627,20 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1062
1627
  }
1063
1628
  }
1064
1629
  lines.push("");
1065
- lines.push("Open the picker with Ctrl+M to switch.");
1630
+ lines.push("Open the picker with Ctrl+O to switch.");
1066
1631
  sysMsg(lines.join("\n"));
1067
1632
  return true;
1068
1633
  }
1069
1634
  if (!arg) {
1070
- // Condensed system message the picker is the primary path
1071
- // for switching models, so the slash command's job here is
1072
- // to point at Ctrl+M and surface the current model in one
1073
- // tight line. Long enumerations of every provider's model
1074
- // list belong in /model status, not the bare command output.
1075
- sysMsg(providerModels.size === 0
1076
- ? `Current: ${currentModel} (no providers configured — run \`wotann login <provider>\`)`
1077
- : `Current: ${currentModel}. Press Ctrl+M to switch · /model <name> · /model status`);
1635
+ // Open the searchable model picker inline. Skips the static
1636
+ // hint text the user complained about typing /model now
1637
+ // does the same thing as Ctrl+O, no extra keystroke.
1638
+ if (providerModels.size === 0) {
1639
+ sysMsg(`Current: ${currentModel} (no providers configured run \`wotann login <provider>\`)`);
1640
+ }
1641
+ else {
1642
+ setShowModelPicker(true);
1643
+ }
1078
1644
  }
1079
1645
  else {
1080
1646
  // Validate model is available on some provider
@@ -1596,7 +2162,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1596
2162
  }, async () => {
1597
2163
  const tsc = await new Promise((resolve) => {
1598
2164
  try {
1599
- execFileSync("npx", ["tsc", "--noEmit"], {
2165
+ execFileSync(nodeToolBinary("npx"), ["tsc", "--noEmit"], {
1600
2166
  cwd: runtime.getWorkingDir(),
1601
2167
  timeout: 60000,
1602
2168
  });
@@ -1609,7 +2175,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1609
2175
  });
1610
2176
  const tests = await new Promise((resolve) => {
1611
2177
  try {
1612
- execFileSync("npx", ["vitest", "run", "--reporter=dot"], {
2178
+ execFileSync(nodeToolBinary("npx"), ["vitest", "run", "--reporter=dot"], {
1613
2179
  cwd: runtime.getWorkingDir(),
1614
2180
  timeout: 120000,
1615
2181
  });
@@ -1786,7 +2352,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1786
2352
  .map((s) => ` ${s.source.padEnd(28)} ${String(s.tokens).padStart(7)} tokens ${s.percent.toFixed(1)}%`);
1787
2353
  const utilPct = snapshot.utilizationPercent;
1788
2354
  sysMsg([
1789
- "╭─ Context Source Inspector (Ctrl+I) ─────────────────────╮",
2355
+ "╭─ Context Source Inspector (Ctrl+X) ─────────────────────╮",
1790
2356
  `│ Total: ${snapshot.totalTokens.toLocaleString().padStart(9)} / ${snapshot.maxTokens.toLocaleString()} tokens`,
1791
2357
  `│ Usage: ${utilPct.toFixed(1)}% ${utilPct > 75 ? "⚠ HIGH" : "✓ OK"}`,
1792
2358
  "├──────────────────────────────────────────────────────────┤",
@@ -1806,36 +2372,100 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1806
2372
  void (async () => {
1807
2373
  const { MCPRegistry } = await import("../marketplace/registry.js");
1808
2374
  const registry = new MCPRegistry({ projectDir: workingDir });
2375
+ const loaded = registry.loadFromDisk();
1809
2376
  registry.registerBuiltins();
1810
- registry.importFromClaudeCode();
2377
+ const persistRegistry = () => {
2378
+ try {
2379
+ return { path: registry.persistToDisk() };
2380
+ }
2381
+ catch (err) {
2382
+ return {
2383
+ error: err instanceof Error ? err.message : String(err),
2384
+ };
2385
+ }
2386
+ };
1811
2387
  switch (mcpAction) {
1812
2388
  case "list": {
1813
2389
  const servers = registry.getAllServers();
1814
2390
  if (servers.length === 0) {
1815
- appendSystemMessage("No MCP servers registered. Import with: /mcp import");
2391
+ appendSystemMessage("No MCP servers registered. Import with: /mcp import claude|cursor|windsurf|codex|vscode|all");
1816
2392
  }
1817
2393
  else {
1818
- const lines = servers.map((s) => `│ ${s.enabled ? "✓" : "✗"} ${s.name.padEnd(14)} ${s.command.padEnd(20)} ${s.transport}`);
2394
+ const lines = servers.map((s) => `│ ${s.enabled ? "✓" : "✗"} ${s.name.padEnd(14)} ${s.command.padEnd(20)} ${s.transport.padEnd(6)} ${s.autoStart === false ? "manual" : "auto"}`);
1819
2395
  appendSystemMessage([
1820
2396
  "╭─ MCP Server Registry ────────────────────────────────────╮",
1821
- `│ ${servers.length} server(s) registered │`,
2397
+ `│ ${servers.length} server(s) registered · ${loaded} persisted loaded`,
1822
2398
  "├──────────────────────────────────────────────────────────┤",
1823
2399
  ...lines,
1824
2400
  "├──────────────────────────────────────────────────────────┤",
1825
- "│ Commands: /mcp list | /mcp install <name> │",
2401
+ "│ Commands: /mcp list | /mcp inspect <name> │",
2402
+ "│ /mcp import <source> | /mcp install <name> │",
1826
2403
  "│ /mcp remove <name> | /mcp audit <name> │",
1827
2404
  "╰──────────────────────────────────────────────────────────╯",
1828
2405
  ].join("\n"));
1829
2406
  }
1830
2407
  break;
1831
2408
  }
2409
+ case "import": {
2410
+ const source = (sub[1] ?? "all").toLowerCase();
2411
+ const importers = {
2412
+ claude: () => registry.importFromClaudeCode(),
2413
+ "claude-code": () => registry.importFromClaudeCode(),
2414
+ cursor: () => registry.importFromTool("cursor"),
2415
+ windsurf: () => registry.importFromTool("windsurf"),
2416
+ codex: () => registry.importFromTool("codex"),
2417
+ vscode: () => registry.importFromTool("vscode"),
2418
+ };
2419
+ const sources = source === "all"
2420
+ ? ["claude", "cursor", "windsurf", "codex", "vscode"]
2421
+ : [source];
2422
+ const invalid = sources.find((s) => !(s in importers));
2423
+ if (invalid) {
2424
+ appendSystemMessage("Usage: /mcp import claude|cursor|windsurf|codex|vscode|all");
2425
+ break;
2426
+ }
2427
+ const counts = sources.map((s) => ({ source: s, count: importers[s]() }));
2428
+ const total = counts.reduce((sum, item) => sum + item.count, 0);
2429
+ const persisted = persistRegistry();
2430
+ if ("error" in persisted) {
2431
+ appendSystemMessage(`MCP import failed while writing config: ${persisted.error}`);
2432
+ break;
2433
+ }
2434
+ appendSystemMessage([
2435
+ "╭─ MCP Import ─────────────────────────────────────────────╮",
2436
+ ...counts.map((item) => `│ ${item.source.padEnd(10)} ${String(item.count).padStart(3)} server${item.count === 1 ? " " : "s"}`),
2437
+ "├──────────────────────────────────────────────────────────┤",
2438
+ `│ Imported ${total} server${total === 1 ? "" : "s"} · wrote ${persisted.path}`,
2439
+ "╰──────────────────────────────────────────────────────────╯",
2440
+ ].join("\n"));
2441
+ break;
2442
+ }
1832
2443
  case "install": {
1833
2444
  const serverName = sub[1];
1834
2445
  if (!serverName) {
1835
2446
  appendSystemMessage("Usage: /mcp install <name>");
1836
2447
  break;
1837
2448
  }
1838
- appendSystemMessage(`Installing MCP server: ${serverName}\nUse /mcp list to see registered servers.`);
2449
+ const existing = registry.getServer(serverName);
2450
+ if (!existing) {
2451
+ appendSystemMessage(`MCP server "${serverName}" is not known yet. Run /mcp import all, then /mcp list to choose a server.`);
2452
+ break;
2453
+ }
2454
+ if (!existing.command) {
2455
+ appendSystemMessage(`MCP server "${serverName}" has no command configured, so it cannot be installed.`);
2456
+ break;
2457
+ }
2458
+ registry.register({
2459
+ ...existing,
2460
+ enabled: true,
2461
+ autoStart: existing.autoStart ?? true,
2462
+ });
2463
+ const persisted = persistRegistry();
2464
+ if ("error" in persisted) {
2465
+ appendSystemMessage(`MCP server "${serverName}" was enabled in-memory, but config persistence failed: ${persisted.error}`);
2466
+ break;
2467
+ }
2468
+ appendSystemMessage(`Enabled MCP server "${serverName}" and persisted it to ${persisted.path}. Use /mcp inspect ${serverName} to verify health.`);
1839
2469
  break;
1840
2470
  }
1841
2471
  case "remove": {
@@ -1850,7 +2480,12 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1850
2480
  }
1851
2481
  else {
1852
2482
  registry.unregister(serverName);
1853
- appendSystemMessage(`Removed MCP server: ${serverName}`);
2483
+ const persisted = persistRegistry();
2484
+ if ("error" in persisted) {
2485
+ appendSystemMessage(`MCP server "${serverName}" was removed in-memory, but config persistence failed: ${persisted.error}`);
2486
+ break;
2487
+ }
2488
+ appendSystemMessage(`Removed MCP server "${serverName}" and persisted ${persisted.path}.`);
1854
2489
  }
1855
2490
  break;
1856
2491
  }
@@ -1876,8 +2511,65 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1876
2511
  }
1877
2512
  break;
1878
2513
  }
2514
+ case "inspect":
2515
+ case "status": {
2516
+ const serverName = sub[1];
2517
+ if (!serverName) {
2518
+ const servers = registry.getAllServers();
2519
+ appendSystemMessage([
2520
+ "╭─ MCP Inspector ──────────────────────────────────────────╮",
2521
+ "│ Pick a server to inspect: │",
2522
+ ...servers.map((s) => `│ ${s.enabled ? "✓" : "○"} ${s.name.padEnd(18)} ${s.transport.padEnd(6)} ${s.command.slice(0, 22).padEnd(22)}│`),
2523
+ "├──────────────────────────────────────────────────────────┤",
2524
+ "│ Usage: /mcp inspect <name> │",
2525
+ "╰──────────────────────────────────────────────────────────╯",
2526
+ ].join("\n"));
2527
+ break;
2528
+ }
2529
+ const server = registry.getServer(serverName);
2530
+ if (!server) {
2531
+ appendSystemMessage(`MCP server "${serverName}" not found.`);
2532
+ break;
2533
+ }
2534
+ const envNames = Object.keys(server.env ?? {});
2535
+ const envLine = envNames.length === 0
2536
+ ? "(none)"
2537
+ : envNames.map((name) => `${name}=••••`).join(", ");
2538
+ let commandHealth = "not checked";
2539
+ if (server.transport === "stdio" &&
2540
+ server.command &&
2541
+ !server.command.includes("/")) {
2542
+ try {
2543
+ execFileSync(whichBinary(), [server.command], { stdio: "ignore" });
2544
+ commandHealth = "binary found";
2545
+ }
2546
+ catch {
2547
+ commandHealth = "binary missing";
2548
+ }
2549
+ }
2550
+ else if (server.transport === "http") {
2551
+ commandHealth = "http endpoint configured";
2552
+ }
2553
+ else if (server.command) {
2554
+ commandHealth = existsSync(server.command) ? "path exists" : "path missing";
2555
+ }
2556
+ appendSystemMessage([
2557
+ "╭─ MCP Inspector ──────────────────────────────────────────╮",
2558
+ `│ Name: ${server.name}`,
2559
+ `│ Status: ${server.enabled ? "enabled" : "disabled"} · ${server.autoStart === false ? "manual start" : "auto start"}`,
2560
+ `│ Health: ${commandHealth}`,
2561
+ `│ Transport: ${server.transport}`,
2562
+ `│ Command: ${server.command}`,
2563
+ `│ Args: ${server.args.join(" ") || "(none)"}`,
2564
+ `│ Env: ${envLine}`,
2565
+ "├──────────────────────────────────────────────────────────┤",
2566
+ "│ Actions: /mcp audit <name> | /mcp remove <name> │",
2567
+ "╰──────────────────────────────────────────────────────────╯",
2568
+ ].join("\n"));
2569
+ break;
2570
+ }
1879
2571
  default:
1880
- appendSystemMessage("Usage: /mcp [list|install|remove|audit] [name]");
2572
+ appendSystemMessage("Usage: /mcp [list|import|inspect|install|remove|audit] [name/source]");
1881
2573
  }
1882
2574
  })();
1883
2575
  return true;
@@ -1915,25 +2607,101 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1915
2607
  ? `Loaded personas (${personas.length}):\n${personas.map((p) => ` • ${p}`).join("\n")}`
1916
2608
  : "No personas loaded. Create YAML files in .wotann/identity/personas/ or run /persona init.");
1917
2609
  }
2610
+ else if (personaSub === "switch" || personaSub.startsWith("switch ")) {
2611
+ const manager = runtime.getPersonaManager();
2612
+ const personas = manager.list();
2613
+ const argName = personaSub.startsWith("switch ")
2614
+ ? personaSub.slice("switch ".length).trim()
2615
+ : "";
2616
+ if (personas.length === 0) {
2617
+ sysMsg("No personas to switch to. Create YAML files in .wotann/identity/personas/ first.");
2618
+ return true;
2619
+ }
2620
+ if (argName) {
2621
+ if (!personas.includes(argName)) {
2622
+ sysMsg(`Persona "${argName}" not found. Available: ${personas.join(", ")}`);
2623
+ return true;
2624
+ }
2625
+ const setActive = manager
2626
+ .setActive;
2627
+ if (typeof setActive === "function") {
2628
+ setActive.call(manager, argName);
2629
+ sysMsg(`Persona switched to: ${argName}`);
2630
+ }
2631
+ else {
2632
+ sysMsg(`Persona switching not supported by this build.`);
2633
+ }
2634
+ return true;
2635
+ }
2636
+ // Inline picker — same pattern as /mode, /theme, /thinking,
2637
+ // /permission. Lists every loaded persona; selecting applies.
2638
+ setOptionPicker({
2639
+ title: "Switch persona",
2640
+ options: personas.map((p) => ({
2641
+ value: p,
2642
+ description: `.wotann/identity/personas/${p}`,
2643
+ })),
2644
+ onSelect: (value) => {
2645
+ setOptionPicker(null);
2646
+ const setActive = manager
2647
+ .setActive;
2648
+ if (typeof setActive === "function") {
2649
+ setActive.call(manager, value);
2650
+ appendSystemMessage(`Persona switched to: ${value}`);
2651
+ }
2652
+ else {
2653
+ appendSystemMessage(`Persona switching not supported by this build.`);
2654
+ }
2655
+ },
2656
+ });
2657
+ }
1918
2658
  else {
1919
- sysMsg(`Persona command: ${personaSub}`);
2659
+ sysMsg(`Unknown persona sub-command: ${personaSub}\nUsage: /persona [list|init|switch [name]]`);
1920
2660
  }
1921
2661
  return true;
1922
2662
  }
1923
2663
  case "/permission": {
1924
2664
  const permMode = rest.trim();
1925
- const validModes = ["auto-approve", "ask-always", "smart"];
1926
- if (!permMode || !validModes.includes(permMode)) {
1927
- const currentPerm = runtime?.getPermissionMode() ?? "smart";
1928
- sysMsg([
1929
- "╭─ Permission Mode ─────────────────────────────────────────╮",
1930
- `│ Current: ${currentPerm.padEnd(45)}│`,
1931
- "├──────────────────────────────────────────────────────────┤",
1932
- "│ auto-approve Skip all approval prompts │",
1933
- "│ ask-always Ask before every tool call │",
1934
- "│ smart Ask for destructive ops only (default) │",
1935
- "╰──────────────────────────────────────────────────────────╯",
1936
- ].join("\n"));
2665
+ const validModes = ["ask-always", "smart", "auto-approve", "autonomous"];
2666
+ const currentPerm = runtime?.getPermissionMode() ?? "smart";
2667
+ if (!permMode) {
2668
+ // Inline picker — same pattern as /mode, /theme, /thinking.
2669
+ // Selecting a row applies the mode immediately + appends a
2670
+ // confirmation system message; Esc cancels with no change.
2671
+ // Ordered safest → most autonomous, top to bottom.
2672
+ setOptionPicker({
2673
+ title: "Permission mode",
2674
+ options: [
2675
+ {
2676
+ value: "ask-always",
2677
+ description: "Ask before every tool call (max safety)",
2678
+ },
2679
+ {
2680
+ value: "smart",
2681
+ description: "Ask for destructive ops only (default)",
2682
+ },
2683
+ {
2684
+ value: "auto-approve",
2685
+ description: "Skip approval prompts; still escalate on errors",
2686
+ },
2687
+ {
2688
+ value: "autonomous",
2689
+ description: "Full autonomy — skip prompts, auto-retry, escalate only on hard blocks",
2690
+ },
2691
+ ],
2692
+ currentValue: currentPerm,
2693
+ onSelect: (value) => {
2694
+ setOptionPicker(null);
2695
+ if (validModes.includes(value)) {
2696
+ runtime?.setPermissionMode(value);
2697
+ appendSystemMessage(`Permission mode set to: ${value}`);
2698
+ }
2699
+ },
2700
+ });
2701
+ return true;
2702
+ }
2703
+ if (!validModes.includes(permMode)) {
2704
+ sysMsg(`Unknown permission mode: ${permMode}\nValid: ${validModes.join(", ")}`);
1937
2705
  return true;
1938
2706
  }
1939
2707
  if (runtime) {
@@ -1942,6 +2710,65 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
1942
2710
  sysMsg(`Permission mode set to: ${permMode}`);
1943
2711
  return true;
1944
2712
  }
2713
+ case "/verifier": {
2714
+ // Toggle the secondary AI judge that audits primary responses
2715
+ // before they land in the user's chat. Doubles per-turn provider
2716
+ // spend but catches hallucinations / wrong-answers / silent
2717
+ // failures the primary missed. Off by default.
2718
+ if (!runtime) {
2719
+ sysMsg("Verifier requires WotannRuntime.");
2720
+ return true;
2721
+ }
2722
+ const sub = rest.trim();
2723
+ const current = runtime.isVerifierEnabled();
2724
+ if (!sub) {
2725
+ setOptionPicker({
2726
+ title: `Secondary AI verifier (currently ${current ? "ON" : "OFF"})`,
2727
+ options: [
2728
+ {
2729
+ value: "off",
2730
+ label: "Off",
2731
+ description: "Single-model responses (default; lowest cost)",
2732
+ },
2733
+ {
2734
+ value: "on",
2735
+ label: "On",
2736
+ description: "Run a second model to judge primary responses — catches hallucinations, doubles cost",
2737
+ },
2738
+ ],
2739
+ currentValue: current ? "on" : "off",
2740
+ onSelect: (value) => {
2741
+ setOptionPicker(null);
2742
+ runtime.setVerifierEnabled(value === "on");
2743
+ appendSystemMessage(`Verifier ${value === "on" ? "enabled" : "disabled"}.`);
2744
+ },
2745
+ });
2746
+ return true;
2747
+ }
2748
+ if (sub === "on" || sub === "enable" || sub === "true") {
2749
+ runtime.setVerifierEnabled(true);
2750
+ sysMsg("Verifier enabled.");
2751
+ }
2752
+ else if (sub === "off" || sub === "disable" || sub === "false") {
2753
+ runtime.setVerifierEnabled(false);
2754
+ sysMsg("Verifier disabled.");
2755
+ }
2756
+ else if (sub === "status") {
2757
+ const history = runtime.getVerifierHistory();
2758
+ sysMsg([
2759
+ `Verifier: ${current ? "ENABLED" : "DISABLED"}`,
2760
+ `Last ${Math.min(history.length, 5)} verdicts:`,
2761
+ ...history.slice(-5).map((v) => {
2762
+ const decision = v.decision ?? "?";
2763
+ return ` ${decision}`;
2764
+ }),
2765
+ ].join("\n"));
2766
+ }
2767
+ else {
2768
+ sysMsg(`Usage: /verifier [on|off|status]`);
2769
+ }
2770
+ return true;
2771
+ }
1945
2772
  case "/healing": {
1946
2773
  if (!runtime) {
1947
2774
  sysMsg("Self-healing requires WotannRuntime.");
@@ -2350,24 +3177,157 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
2350
3177
  }
2351
3178
  return true;
2352
3179
  }
3180
+ case "/link":
3181
+ case "/pair":
2353
3182
  case "/deeplink": {
2354
- const link = parseDeepLink(arg);
2355
- if (!link) {
2356
- sysMsg("Invalid deep link. Format: wotann://action?params");
3183
+ // Primary use: pair the iOS app to this WOTANN session.
3184
+ // Mirrors the `wotann link` CLI command (src/index.ts:296)
3185
+ // which calls into KAIROS daemon → companion.pairing returns
3186
+ // QR data URL + PIN + host/port + expiry. Surfacing this in
3187
+ // the TUI lets the user pair without leaving the running
3188
+ // session.
3189
+ //
3190
+ // Secondary use: when called with a `wotann://...` URL,
3191
+ // execute the deep-link (mode/theme/channel-pair branches).
3192
+ // Backwards-compat with the prior /deeplink semantics so
3193
+ // muscle-memory and external triggers keep working.
3194
+ const trimmed = arg.trim();
3195
+ if (trimmed.startsWith("wotann://")) {
3196
+ const link = parseDeepLink(trimmed);
3197
+ if (!link) {
3198
+ sysMsg(`Invalid deep link "${trimmed}". Expected: wotann://<action>?<params>.`);
3199
+ return true;
3200
+ }
3201
+ const ctx = {
3202
+ workingDir,
3203
+ setMode: (mode) => {
3204
+ if (runtime)
3205
+ runtime.setMode(mode);
3206
+ setCurrentMode(mode);
3207
+ },
3208
+ setTheme: (theme) => themeManagerRef.current.setTheme(theme),
3209
+ verifyPairingCode: (code) => runtime?.getDispatchPlane()?.verifyPairingCode(code) ?? false,
3210
+ };
3211
+ const result = executeDeepLink(link, ctx);
3212
+ sysMsg(result.success ? `✓ ${result.message}` : `✗ ${result.message}`);
2357
3213
  return true;
2358
3214
  }
2359
- const ctx = {
2360
- workingDir,
2361
- setMode: (mode) => {
2362
- if (runtime)
2363
- runtime.setMode(mode);
2364
- setCurrentMode(mode);
2365
- },
2366
- setTheme: (theme) => themeManagerRef.current.setTheme(theme),
2367
- verifyPairingCode: (code) => runtime?.getDispatchPlane()?.verifyPairingCode(code) ?? false,
2368
- };
2369
- const result = executeDeepLink(link, ctx);
2370
- sysMsg(result.success ? `✓ ${result.message}` : `✗ ${result.message}`);
3215
+ sysMsg("Generating pairing details from KAIROS daemon...");
3216
+ void (async () => {
3217
+ try {
3218
+ const { KairosIPCClient } = await import("../daemon/kairos-ipc.js");
3219
+ const client = new KairosIPCClient(undefined, { requestTimeoutMs: 5_000 });
3220
+ const ok = await client.connect();
3221
+ if (!ok) {
3222
+ const { resolveWotannHomeSubdir } = await import("../utils/wotann-home.js");
3223
+ const pidPath = resolveWotannHomeSubdir("daemon.pid");
3224
+ const pidHint = existsSync(pidPath)
3225
+ ? `Daemon PID file exists (${readFileSync(pidPath, "utf-8").trim()}), but the IPC socket is not reachable.`
3226
+ : "No daemon PID file was found.";
3227
+ appendSystemMessage([
3228
+ "KAIROS daemon IPC is not reachable.",
3229
+ pidHint,
3230
+ "",
3231
+ " 1. Open the WOTANN desktop app (auto-starts the daemon)",
3232
+ " 2. Or run: wotann daemon start",
3233
+ " 3. Then re-run /link",
3234
+ ].join("\n"));
3235
+ return;
3236
+ }
3237
+ try {
3238
+ const pairing = (await client.call("companion.pairing"));
3239
+ const expiresMs = Date.parse(pairing.expiresAt);
3240
+ const expiresIn = Number.isFinite(expiresMs)
3241
+ ? `${Math.max(0, Math.round((expiresMs - Date.now()) / 1000))}s`
3242
+ : pairing.expiresAt;
3243
+ const remoteLines = (pairing.remoteEndpoints ?? []).map((endpoint) => {
3244
+ const scheme = endpoint.scheme ?? "ws";
3245
+ const port = endpoint.port ?? (scheme === "wss" ? 443 : 3849);
3246
+ return ` Remote: ${endpoint.label ?? "Remote"} ${scheme}://${endpoint.host ?? "unknown"}:${port}`;
3247
+ });
3248
+ const remoteSetupLines = remoteLines.length > 0
3249
+ ? remoteLines
3250
+ : [
3251
+ ` Remote: ${pairing.remoteAccess?.status ?? "missing-endpoint"}`,
3252
+ ` ${pairing.remoteAccess?.message ?? "Same network only."}`,
3253
+ ...(pairing.remoteAccess?.setupOptions
3254
+ ?.filter((option) => option.id === "tailscale" || option.id === "configured-url")
3255
+ .slice(0, 2)
3256
+ .map((option) => ` ${option.label ?? option.id}: ${option.command ?? option.detail ?? ""}`) ?? []),
3257
+ ];
3258
+ appendSystemMessage([
3259
+ "WOTANN ↔ iOS pairing",
3260
+ "",
3261
+ ` PIN: ${pairing.pin}`,
3262
+ ` Host: ${pairing.host ?? "unknown"}`,
3263
+ ` Port: ${pairing.port ?? 3849}`,
3264
+ ...remoteSetupLines,
3265
+ ` Expires: in ${expiresIn}`,
3266
+ "",
3267
+ ` Deep-link URL (paste / scan):`,
3268
+ ` ${pairing.qrData}`,
3269
+ "",
3270
+ " In the WOTANN iOS app: Settings → Pair Device → scan QR or enter PIN.",
3271
+ " Run `wotann link` from a separate shell for an inline ASCII QR.",
3272
+ ].join("\n"));
3273
+ }
3274
+ finally {
3275
+ client.disconnect();
3276
+ }
3277
+ }
3278
+ catch (err) {
3279
+ appendSystemMessage(`Pairing failed: ${err.message ?? "unknown error"}`);
3280
+ }
3281
+ })();
3282
+ return true;
3283
+ }
3284
+ case "/computer": {
3285
+ const refreshedTools = detectAvailableTools();
3286
+ setComputerTools(refreshedTools);
3287
+ setActivePanel("computer");
3288
+ const actionInput = arg.trim();
3289
+ if (actionInput && actionInput !== "status" && actionInput !== "help") {
3290
+ const parsed = parseComputerActionCommand(actionInput);
3291
+ if (!parsed) {
3292
+ sysMsg([
3293
+ "Unknown desktop action.",
3294
+ "Try: /computer click 420 320",
3295
+ " /computer type hello from WOTANN",
3296
+ " /computer key command+c",
3297
+ " /computer scroll down 4",
3298
+ " /computer screenshot ~/Desktop/wotann-screen.png",
3299
+ ].join("\n"));
3300
+ return true;
3301
+ }
3302
+ const result = executeDesktopAction(parsed);
3303
+ if (!result) {
3304
+ sysMsg([
3305
+ `Desktop action not available: ${parsed.action}`,
3306
+ `Available: ${computerActions.join(", ")}`,
3307
+ ].join("\n"));
3308
+ return true;
3309
+ }
3310
+ sysMsg([
3311
+ `Desktop action: ${parsed.action}`,
3312
+ `Status: ${result.success ? "ok" : "failed"}`,
3313
+ result.output ? `Output: ${result.output}` : "",
3314
+ ]
3315
+ .filter(Boolean)
3316
+ .join("\n"));
3317
+ return true;
3318
+ }
3319
+ sysMsg([
3320
+ "Computer-use panel opened.",
3321
+ `Tools: ${refreshedTools.length > 0 ? refreshedTools.join(", ") : "none detected"}`,
3322
+ `Actions: ${computerActions.join(", ")}`,
3323
+ "",
3324
+ "Run actions directly:",
3325
+ " /computer click 420 320",
3326
+ " /computer type hello from WOTANN",
3327
+ " /computer key command+c",
3328
+ " /computer scroll down 4",
3329
+ " /computer screenshot ~/Desktop/wotann-screen.png",
3330
+ ].join("\n"));
2371
3331
  return true;
2372
3332
  }
2373
3333
  case "/roe": {
@@ -2944,8 +3904,167 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
2944
3904
  })();
2945
3905
  return true;
2946
3906
  }
3907
+ // ── CLI parity: commands that mirror `wotann <verb>` ──────
3908
+ // Each handler wraps the same module the CLI command does so
3909
+ // power users don't have to drop to a shell mid-session.
3910
+ case "/stuck": {
3911
+ // Mirrors `wotann stuck` — diagnose why a model is stuck
3912
+ // (recent traces, hook denials, file freezes, doom-loop
3913
+ // fingerprints). Runs the same module the CLI invokes.
3914
+ sysMsg("Running stuck diagnostic...");
3915
+ void (async () => {
3916
+ try {
3917
+ const { runStuck, formatStuckReport } = await import("../cli/commands/stuck.js");
3918
+ const report = await runStuck();
3919
+ appendSystemMessage(formatStuckReport(report));
3920
+ }
3921
+ catch (err) {
3922
+ appendSystemMessage(`Stuck diagnostic failed: ${err instanceof Error ? err.message : "unknown"}`);
3923
+ }
3924
+ })();
3925
+ return true;
3926
+ }
3927
+ case "/git": {
3928
+ // Mirrors `wotann git <verb>` — pass-through to git in the
3929
+ // working directory. Args are split on whitespace and passed
3930
+ // to execFile directly (no shell), so command-injection
3931
+ // is impossible.
3932
+ const verb = rest.trim();
3933
+ if (!verb) {
3934
+ sysMsg("Usage: /git <verb> [args]\nExamples: /git status, /git log -5, /git diff HEAD");
3935
+ return true;
3936
+ }
3937
+ sysMsg(`Running: git ${verb}`);
3938
+ void (async () => {
3939
+ try {
3940
+ // Direct execFile (NOT shell) — args split on whitespace,
3941
+ // passed as array → no shell interpretation, no injection
3942
+ // surface.
3943
+ const { execFile } = await import("node:child_process");
3944
+ const { promisify } = await import("node:util");
3945
+ const exec = promisify(execFile);
3946
+ const args = verb.split(/\s+/).filter(Boolean);
3947
+ const result = await exec("git", args, {
3948
+ cwd: workingDir,
3949
+ timeout: 30_000,
3950
+ maxBuffer: 8 * 1024 * 1024,
3951
+ });
3952
+ const out = (result.stdout || "").toString().trim();
3953
+ appendSystemMessage(out.slice(0, 4000) || "(no output)");
3954
+ }
3955
+ catch (err) {
3956
+ const e = err;
3957
+ const out = (e.stdout?.toString() || "").trim();
3958
+ const errOut = (e.stderr?.toString() || "").trim();
3959
+ appendSystemMessage(`git ${verb} → exit ${e.code ?? "?"}\n${(errOut || out).slice(0, 2000)}`);
3960
+ }
3961
+ })();
3962
+ return true;
3963
+ }
3964
+ case "/worktree": {
3965
+ // Mirrors `wotann worktree <action> [taskId]`. Heads-up:
3966
+ // creating a worktree changes filesystem state outside the
3967
+ // current cwd, so we surface the CLI command rather than
3968
+ // exec it silently.
3969
+ const sub = (parts[1] ?? "").toLowerCase();
3970
+ if (!sub || !["create", "list", "abandon", "accept"].includes(sub)) {
3971
+ sysMsg("Usage: /worktree <create|list|abandon|accept> [taskId]\n" +
3972
+ "Run from a separate shell — worktree create alters filesystem state outside the TUI's cwd.");
3973
+ return true;
3974
+ }
3975
+ sysMsg(`Run from a separate shell: \`wotann worktree ${sub}${parts[2] ? ` ${parts[2]}` : ""}\``);
3976
+ return true;
3977
+ }
3978
+ case "/architect": {
3979
+ // `wotann architect <prompt>` runs an architecture-mode
3980
+ // session. In the TUI we set the mode pill + forward the
3981
+ // prompt so the rest of the streaming UX stays consistent.
3982
+ const prompt = rest.trim();
3983
+ if (!prompt) {
3984
+ sysMsg("Usage: /architect <prompt>\nSwitches to plan mode + asks the model to design an architecture.");
3985
+ return true;
3986
+ }
3987
+ if (runtime)
3988
+ runtime.setMode("plan");
3989
+ setCurrentMode("plan");
3990
+ sysMsg("Architect mode enabled — switched to plan mode for this turn.");
3991
+ void handleSubmitRef.current(`Design the architecture for: ${prompt}`);
3992
+ return true;
3993
+ }
3994
+ case "/init": {
3995
+ // `wotann init` is interactive — running it inside Ink would
3996
+ // corrupt the screen. Surface guidance instead.
3997
+ sysMsg([
3998
+ "/init runs the interactive setup wizard, which conflicts with the TUI.",
3999
+ "",
4000
+ "Open a separate terminal and run:",
4001
+ " wotann init # interactive (default)",
4002
+ " wotann init --free # free-tier setup (Ollama + free APIs)",
4003
+ " wotann init --reset # reset workspace to defaults",
4004
+ "",
4005
+ "After init completes, re-launch `wotann` to see new providers.",
4006
+ ].join("\n"));
4007
+ return true;
4008
+ }
4009
+ case "/sizes":
4010
+ case "/size": {
4011
+ // Diagnostic: show current system prompt + tool count + skill
4012
+ // count so the user can see WHY a turn might be slow. Codex
4013
+ // gpt-5.5 prefill at ~5K tokens/sec → 50KB request = ~3s,
4014
+ // 200KB = ~12s. The watchdog firing at 30s with no content
4015
+ // usually means we're sending way more than expected.
4016
+ if (!runtime) {
4017
+ sysMsg("Runtime not initialised — sizes unavailable.");
4018
+ return true;
4019
+ }
4020
+ const sysPrompt = runtime.getSystemPrompt?.() ?? "";
4021
+ const sysKB = (sysPrompt.length / 1024).toFixed(1);
4022
+ const skillCount = getSkillRegistry().getSummaries().length;
4023
+ sysMsg([
4024
+ `System prompt: ${sysKB}KB (~${Math.round(sysPrompt.length / 4)} tokens)`,
4025
+ `Skills loaded: ${skillCount}`,
4026
+ ``,
4027
+ `On a slow first turn, also try:`,
4028
+ ` WOTANN_DEBUG_REQUEST=1 wotann 2>~/wotann-diag.log`,
4029
+ `then check ~/wotann-diag.log for actual codex request size + TTFT.`,
4030
+ ].join("\n"));
4031
+ return true;
4032
+ }
2947
4033
  case "/login": {
2948
- sysMsg("Run `wotann login [provider]` from your shell. Supported: anthropic, openai, codex, copilot, gemini, ollama, deepseek, etc.");
4034
+ // Provider-aware re-auth instructions. The TUI can't spawn an
4035
+ // interactive OAuth/PKCE flow without breaking Ink's screen,
4036
+ // so we direct the user to run the login in another terminal
4037
+ // — but the runtime auto-picks-up the new credentials on the
4038
+ // very next request (the codex adapter re-reads
4039
+ // ~/.codex/auth.json per query; anthropic/copilot/gemini
4040
+ // similarly re-read their token stores). No restart needed.
4041
+ const target = (arg.trim() || currentProvider).toLowerCase();
4042
+ const instructions = {
4043
+ codex: "1. Open a new terminal\n" +
4044
+ "2. Run: `codex login` (or `wotann login codex` for guided flow)\n" +
4045
+ "3. Complete the browser flow\n" +
4046
+ "4. Come back here — your next message will use the refreshed token automatically.",
4047
+ anthropic: "1. Open a new terminal\n" +
4048
+ "2. Run: `wotann login anthropic`\n" +
4049
+ "3. Complete the browser flow\n" +
4050
+ "4. Come back here — your next message will use the refreshed credentials.",
4051
+ copilot: "1. Open a new terminal\n" +
4052
+ "2. Run: `wotann login copilot`\n" +
4053
+ "3. Enter the device code at github.com\n" +
4054
+ "4. Come back here — your next message uses the new token.",
4055
+ gemini: "1. Open a new terminal\n" +
4056
+ "2. Run: `wotann login gemini`\n" +
4057
+ "3. Paste your AI Studio API key when prompted\n" +
4058
+ "4. Come back here — your next message uses the new key.",
4059
+ ollama: "Ollama is local — no login required. Just run `ollama serve` and `ollama pull <model>`.",
4060
+ openai: "1. Open a new terminal\n" +
4061
+ "2. Run: `wotann login openai`\n" +
4062
+ "3. Paste your OpenAI API key (sk-...)\n" +
4063
+ "4. Come back here — your next message uses the new key.",
4064
+ };
4065
+ const text = instructions[target] ??
4066
+ `Run \`wotann login ${target}\` from your shell. Supported: anthropic, openai, codex, copilot, gemini, ollama. The runtime picks up new credentials on the next request — no restart needed.`;
4067
+ sysMsg(`Re-auth ${target}:\n${text}`);
2949
4068
  return true;
2950
4069
  }
2951
4070
  default:
@@ -2987,7 +4106,12 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
2987
4106
  // Slash commands and deep links remain immediate even mid-stream:
2988
4107
  // they're local TUI affordances, not model input, so queuing them
2989
4108
  // would be surprising.
2990
- if (isStreaming && !input.startsWith("/") && !input.trimStart().startsWith("wotann://")) {
4109
+ // Use BOTH the React state and the synchronous ref so a second
4110
+ // Enter pressed within the same render tick still routes to the
4111
+ // queue (state alone has a 1-frame delay).
4112
+ if ((isStreaming || streamingGuardRef.current) &&
4113
+ !input.startsWith("/") &&
4114
+ !input.trimStart().startsWith("wotann://")) {
2991
4115
  const trimmed = input.trim();
2992
4116
  if (trimmed.length === 0)
2993
4117
  return;
@@ -3045,8 +4169,14 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3045
4169
  ]);
3046
4170
  }
3047
4171
  setHistory((prev) => [input, ...prev.slice(0, 49)]);
4172
+ // Synchronous flip — the ref is the FIRST guard subsequent submits
4173
+ // see, even before React's re-render. Without this, two Enter presses
4174
+ // within a render window both take the regular path and emit
4175
+ // duplicate user messages + parallel runtime.query calls.
4176
+ streamingGuardRef.current = true;
3048
4177
  setIsStreaming(true);
3049
4178
  setStreamingContent("");
4179
+ setStreamingThinking("");
3050
4180
  setPromptValue("");
3051
4181
  setTurnCount((t) => t + 1);
3052
4182
  // Queued messages are drained inside `runtime.query()` BEFORE the
@@ -3057,41 +4187,345 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3057
4187
  setQueueDepth(0);
3058
4188
  if (runtime) {
3059
4189
  // ═══════════════════════════════════════════════════════════
3060
- // FULL HARNESS INTELLIGENCE PATH (via WotannRuntime)
4190
+ // FULL HARNESS INTELLIGENCE PATH (via WotannRuntime) + AGENT LOOP
3061
4191
  // This goes through: WASM bypass → Hooks → 16 Middleware layers
3062
4192
  // → DoomLoop → Amplifier → Reasoning Sandwich → TTSR → Provider
3063
4193
  // → After-hooks → Memory capture → Cost tracking
4194
+ //
4195
+ // Pre-2026-05-03 the TUI was a CHAT interface only — when the
4196
+ // model emitted a tool_use chunk, App.tsx showed a breadcrumb
4197
+ // and never executed the tool. The model would say things like
4198
+ // "I attempted to start the daemon" with no real result.
4199
+ //
4200
+ // The agent loop below closes that gap:
4201
+ // 1. Pass AGENT_TOOL_DEFINITIONS (Bash/Read/Write/Edit/Grep/Glob)
4202
+ // so the model has real tools available, not just
4203
+ // computer_use + web_fetch + plan_*.
4204
+ // 2. Capture every tool_use into pendingToolCalls during the
4205
+ // streaming phase.
4206
+ // 3. After streaming, execute each tool client-side and
4207
+ // append a `tool` message to the agent's context.
4208
+ // 4. Re-call runtime.query with the grown context. Loop until
4209
+ // no tool_use chunks arrive (or MAX_AGENT_ITERATIONS).
4210
+ // 5. The FINAL iteration runs the post-stream UI cleanup
4211
+ // (assistant message, watchdog tips, fallback hints).
3064
4212
  // ═══════════════════════════════════════════════════════════
4213
+ const MAX_AGENT_ITERATIONS = 8;
4214
+ let agentMessages = [...messages, userMsg];
4215
+ let agentIteration = 0;
4216
+ let shouldContinueAgentLoop = true;
4217
+ // Shared state across iterations (final stats / accumulators)
3065
4218
  let fullContent = "";
4219
+ let fullThinking = "";
3066
4220
  let tokensUsed = 0;
3067
4221
  const errors = [];
3068
4222
  let responseModel = currentModel;
3069
- let responseProvider = initialProvider;
4223
+ let responseProvider = currentProvider;
4224
+ const queryStartedAt = Date.now();
4225
+ let lastChunkAt = Date.now();
4226
+ // Tracks whether the *provider* has produced visible content
4227
+ // (text or thinking). The runtime middleware can yield its own
4228
+ // metadata chunks (TTSR retry banners, context-pressure hints,
4229
+ // tool_use breadcrumbs) which we need to NOT count toward "the
4230
+ // provider responded". Otherwise the 30s no-content timeout
4231
+ // never fires because the runtime "spoke" first. The user's
4232
+ // 54s-no-output screenshot was hitting exactly this gap.
4233
+ let receivedAnyContent = false;
3070
4234
  const abort = new AbortController();
3071
4235
  abortRef.current = abort;
4236
+ // Two watchdogs, both provider-agnostic:
4237
+ //
4238
+ // 1. Initial-chunk timeout (30s) — if the provider hasn't
4239
+ // emitted a single chunk by then, that's almost always a
4240
+ // hard failure: stale auth, network drop, mis-routed
4241
+ // request, provider returned 200 with empty SSE. Bail
4242
+ // fast with a recovery hint so the user isn't stuck
4243
+ // staring at "Thinking..."
4244
+ //
4245
+ // 2. Stale-chunk timeout (90s) — once chunks ARE flowing,
4246
+ // but the next one is taking too long, the SSE connection
4247
+ // has likely stalled mid-stream. Abort so we don't hang
4248
+ // forever.
4249
+ //
4250
+ // Both push to a watchdog-error array (separate from the
4251
+ // adapter `errors` list) so we can surface them via the
4252
+ // post-abort branch — the prior implementation gated error
4253
+ // display on `!abort.signal.aborted` and silently swallowed
4254
+ // every watchdog message.
4255
+ const watchdogErrors = [];
4256
+ const watchdog = setInterval(() => {
4257
+ const elapsed = Date.now() - lastChunkAt;
4258
+ const totalElapsed = Date.now() - queryStartedAt;
4259
+ if (!receivedAnyContent && totalElapsed > 30_000) {
4260
+ watchdogErrors.push(`${currentProvider}/${currentModel} produced no content in 30s. ` +
4261
+ `Likely a stale auth token, network issue, or provider stall. ` +
4262
+ `Try \`/provider\` to switch providers, or \`wotann login ${currentProvider}\` to re-authenticate.`);
4263
+ abort.abort();
4264
+ clearInterval(watchdog);
4265
+ }
4266
+ else if (receivedAnyContent && elapsed > 90_000) {
4267
+ watchdogErrors.push(`${currentProvider} stream stalled for 90s mid-response. ` +
4268
+ `Aborting. Try \`/provider\` to switch.`);
4269
+ abort.abort();
4270
+ clearInterval(watchdog);
4271
+ }
4272
+ }, 5_000);
4273
+ let pendingToolCalls = [];
3072
4274
  try {
3073
- for await (const chunk of runtime.query({
3074
- prompt: runtimePrompt,
3075
- context: messages,
3076
- model: currentModel,
3077
- provider: initialProvider,
3078
- })) {
3079
- if (abort.signal.aborted)
4275
+ while (shouldContinueAgentLoop && agentIteration < MAX_AGENT_ITERATIONS) {
4276
+ agentIteration++;
4277
+ // Reset per-iteration accumulators. Streaming UI starts
4278
+ // fresh for each agent loop cycle so the user sees each
4279
+ // round's output rather than a stale concatenation.
4280
+ fullContent = "";
4281
+ fullThinking = "";
4282
+ pendingToolCalls = [];
4283
+ setStreamingContent("");
4284
+ setStreamingThinking("");
4285
+ // Fence-aware streamed-render buffer (per-stream).
4286
+ //
4287
+ // Why: LLM tokens arrive in tiny deltas. When the model is
4288
+ // mid-fence — e.g. it has emitted ```ts\nconst x but the
4289
+ // closing ``` is still in flight — naive
4290
+ // setStreamingContent(fullContent) flashes a partial fence
4291
+ // through ChatView's `(```[\\s\\S]*?```)` segmenter as
4292
+ // unstyled inline text, then redraws as a styled code block
4293
+ // once the closer lands. The flicker reads like a UI bug.
4294
+ //
4295
+ // MarkdownStreamState holds tokens back at fence-safe
4296
+ // boundaries (closed fences / blank lines outside fences),
4297
+ // releasing only `readyToFlush` chunks the renderer can paint
4298
+ // without mid-fence flicker. The raw `fullContent`
4299
+ // accumulator is preserved for downstream consumers —
4300
+ // message commit, codex round-trip, voice TTS — that need
4301
+ // the unbuffered text.
4302
+ const markdownStream = new MarkdownStreamState();
4303
+ let safeFlushedContent = "";
4304
+ for await (const chunk of runtime.query({
4305
+ // Subsequent iterations use a tiny anchor prompt so the
4306
+ // model knows tool results are fresh. The full user
4307
+ // request is already in `agentMessages` context.
4308
+ prompt: agentIteration === 1 ? runtimePrompt : "(tool results above — continue)",
4309
+ context: agentMessages,
4310
+ model: currentModel,
4311
+ // Pass currentProvider so a Ctrl+O picker switch is honoured
4312
+ // immediately. The runtime's options.provider takes precedence
4313
+ // over session.provider, so without this update the routing
4314
+ // layer would still hit the bootstrap provider's adapter even
4315
+ // after setActiveProvider() updated the session.
4316
+ provider: currentProvider,
4317
+ allowProviderFallback: false,
4318
+ // Real agent tools — see src/ui/agent-tools.ts. Without
4319
+ // these the model can only chat; with them it can read
4320
+ // files, run bash, edit code, search the repo, etc.
4321
+ tools: AGENT_TOOL_DEFINITIONS,
4322
+ })) {
4323
+ if (abort.signal.aborted)
4324
+ break;
4325
+ lastChunkAt = Date.now();
4326
+ if (chunk.type === "text") {
4327
+ fullContent += chunk.content;
4328
+ // Route the delta through the fence-aware boundary
4329
+ // detector. `readyToFlush` is the slice that crossed a
4330
+ // safe boundary on THIS push — it can be appended to
4331
+ // the rendered surface without risk of mid-fence flash.
4332
+ // `pending` (anything still mid-fence or mid-line) stays
4333
+ // inside markdownStream and surfaces on the next safe
4334
+ // push or the post-stream flush().
4335
+ const released = markdownStream.push(chunk.content);
4336
+ if (released.readyToFlush.length > 0) {
4337
+ safeFlushedContent = safeFlushedContent + released.readyToFlush;
4338
+ setStreamingContent(safeFlushedContent);
4339
+ }
4340
+ // Only flip the receivedAnyContent gate on REAL content —
4341
+ // whitespace-only middleware preambles (TTSR retry markers,
4342
+ // empty newlines) shouldn't suppress the 30s no-content
4343
+ // watchdog. The user's repeated "75s with nothing visible"
4344
+ // hits were the runtime emitting empty/whitespace text
4345
+ // chunks, flipping the gate true, and silencing the
4346
+ // diagnostic. Gated on the raw delta (not on what the
4347
+ // fence buffer chose to flush) so an in-fence stream
4348
+ // still counts as live activity.
4349
+ if (chunk.content.trim().length > 0) {
4350
+ receivedAnyContent = true;
4351
+ }
4352
+ }
4353
+ else if (chunk.type === "thinking") {
4354
+ // Reasoning-tier providers (codex/gpt-5.5, anthropic
4355
+ // extended thinking, etc.) stream reasoning deltas BEFORE
4356
+ // any text. Render them so the user sees progress instead
4357
+ // of a blank "Thinking..." spinner; without this, the UI
4358
+ // looks identical to a hang for the entire 5–15s reasoning
4359
+ // window the model spends thinking through "who are you?".
4360
+ fullThinking += chunk.content;
4361
+ setStreamingThinking(fullThinking);
4362
+ if (chunk.content.trim().length > 0) {
4363
+ receivedAnyContent = true;
4364
+ }
4365
+ }
4366
+ else if (chunk.type === "error") {
4367
+ // NEVER mix errors into content — show as separate system message
4368
+ errors.push(chunk.content);
4369
+ }
4370
+ else if (chunk.type === "tool_use") {
4371
+ // Tool calls land here — capture the request so we can
4372
+ // execute it AFTER the stream finishes (the model may
4373
+ // chain multiple tool_use chunks per turn). Show the
4374
+ // breadcrumb immediately so the user sees activity.
4375
+ const argSummary = typeof chunk.toolInput === "object"
4376
+ ? JSON.stringify(chunk.toolInput).slice(0, 80)
4377
+ : String(chunk.content).slice(0, 80);
4378
+ setMessages((prev) => [
4379
+ ...prev,
4380
+ {
4381
+ role: "system",
4382
+ content: `🔧 ${chunk.toolName ?? "tool"}(${argSummary})`,
4383
+ },
4384
+ ]);
4385
+ if (chunk.toolName && chunk.toolCallId) {
4386
+ pendingToolCalls.push({
4387
+ toolName: chunk.toolName,
4388
+ toolCallId: chunk.toolCallId,
4389
+ toolInput: (chunk.toolInput ?? {}),
4390
+ });
4391
+ }
4392
+ }
4393
+ if (chunk.tokensUsed)
4394
+ tokensUsed = chunk.tokensUsed;
4395
+ if (chunk.model)
4396
+ responseModel = chunk.model;
4397
+ if (chunk.provider)
4398
+ responseProvider = chunk.provider;
4399
+ }
4400
+ // Stream ended — drain whatever the fence buffer still holds
4401
+ // so the closing ``` of the final code block (or any
4402
+ // trailing prose held back by an open boundary) reaches the
4403
+ // renderer. flush() returns "" when only whitespace was
4404
+ // buffered, which keeps a clean tail.
4405
+ const finalChunk = markdownStream.flush();
4406
+ if (finalChunk.length > 0) {
4407
+ safeFlushedContent = safeFlushedContent + finalChunk;
4408
+ setStreamingContent(safeFlushedContent);
4409
+ }
4410
+ // ── Agent-loop continuation gate ──
4411
+ // After the iteration's stream ends, decide whether to run
4412
+ // another runtime.query turn:
4413
+ // - If pendingToolCalls is empty → done; fall through to
4414
+ // post-stream cleanup outside the while.
4415
+ // - If non-empty AND we haven't hit MAX_AGENT_ITERATIONS →
4416
+ // execute tools, append assistant + tool messages to
4417
+ // `agentMessages`, and continue the loop.
4418
+ if (pendingToolCalls.length === 0 || abort.signal.aborted) {
4419
+ shouldContinueAgentLoop = false;
3080
4420
  break;
3081
- if (chunk.type === "text") {
3082
- fullContent += chunk.content;
3083
- setStreamingContent(fullContent);
3084
4421
  }
3085
- else if (chunk.type === "error") {
3086
- // NEVER mix errors into content show as separate system message
3087
- errors.push(chunk.content);
4422
+ // Stash this turn's assistant text + tool_use calls into the
4423
+ // conversation so the next runtime.query sees the full
4424
+ // history. Each codex tool_call also needs to round-trip as
4425
+ // a function_call item with its call_id, which the codex
4426
+ // adapter handles when it sees `role: "assistant"` +
4427
+ // `toolCallId` on the AgentMessage.
4428
+ if (fullContent.trim()) {
4429
+ agentMessages = [
4430
+ ...agentMessages,
4431
+ {
4432
+ role: "assistant",
4433
+ content: fullContent,
4434
+ model: responseModel,
4435
+ provider: responseProvider,
4436
+ },
4437
+ ];
3088
4438
  }
3089
- if (chunk.tokensUsed)
3090
- tokensUsed = chunk.tokensUsed;
3091
- if (chunk.model)
3092
- responseModel = chunk.model;
3093
- if (chunk.provider)
3094
- responseProvider = chunk.provider;
4439
+ // Codex Responses API requires every tool result to be
4440
+ // preceded by the assistant function_call item that created
4441
+ // that call_id. The UI used to append only the tool-role
4442
+ // result below; Codex then rejected the next request with
4443
+ // "No tool call found for function call output". Preserve
4444
+ // each requested call as an assistant message with
4445
+ // toolCallId/toolName so codex-adapter serializes it back
4446
+ // as a matching `function_call` item before the
4447
+ // `function_call_output`.
4448
+ agentMessages = [
4449
+ ...agentMessages,
4450
+ ...pendingToolCalls.map((call) => ({
4451
+ role: "assistant",
4452
+ content: JSON.stringify(call.toolInput ?? {}),
4453
+ toolCallId: call.toolCallId,
4454
+ toolName: call.toolName,
4455
+ model: responseModel,
4456
+ provider: responseProvider,
4457
+ })),
4458
+ ];
4459
+ // Execute tools sequentially. (Parallel execution is doable
4460
+ // but most tool sequences depend on prior results; serial
4461
+ // is the safer default.)
4462
+ for (const call of pendingToolCalls) {
4463
+ const result = await executeAgentTool(call.toolName, call.toolInput, {
4464
+ workingDir,
4465
+ permissionMode: runtime?.getPermissionMode?.(),
4466
+ });
4467
+ // Surface a compact result preview as a system message
4468
+ // so the user sees what the tool did. Long results get
4469
+ // truncated in the UI; the FULL result still goes into
4470
+ // the model's context.
4471
+ const preview = result.length > 240 ? result.slice(0, 240) + "…" : result;
4472
+ setMessages((prev) => [
4473
+ ...prev,
4474
+ {
4475
+ role: "system",
4476
+ content: `↳ ${call.toolName} → ${preview}`,
4477
+ },
4478
+ ]);
4479
+ // Push as a tool-role message so the codex adapter
4480
+ // serialises it back as a `function_call_output` item
4481
+ // (see codex-adapter.ts:218 — tool messages with
4482
+ // toolCallId become function_call_output).
4483
+ agentMessages = [
4484
+ ...agentMessages,
4485
+ {
4486
+ role: "tool",
4487
+ content: result,
4488
+ toolCallId: call.toolCallId,
4489
+ toolName: call.toolName,
4490
+ },
4491
+ ];
4492
+ }
4493
+ // Per-iteration accumulators get cleared at the top of the
4494
+ // next loop turn. No need to clear here.
4495
+ }
4496
+ clearInterval(watchdog);
4497
+ // Watchdog errors ALWAYS surface — even when the abort fires
4498
+ // *because* of the watchdog. The prior gate
4499
+ // (`!abort.signal.aborted`) silently swallowed every
4500
+ // watchdog message, leaving the user staring at a closed
4501
+ // spinner with no diagnostic.
4502
+ for (const err of watchdogErrors) {
4503
+ setMessages((prev) => [...prev, { role: "system", content: `⚠ ${err}` }]);
4504
+ }
4505
+ // Auto-suggest fast recovery options when the active provider
4506
+ // produced zero output. Two-prong: (1) drop reasoning effort
4507
+ // — most "stalled" cases on codex/gpt-5.5 are actually 60-90s
4508
+ // of `xhigh` reasoning that we never see deltas for, and
4509
+ // dropping to `low`/`minimal` returns instantly; (2) switch
4510
+ // provider as the fallback if the user is on a different
4511
+ // provider that still works.
4512
+ if (!receivedAnyContent && watchdogErrors.length > 0) {
4513
+ const tips = [];
4514
+ // Always offer the effort drop first — it's the most common
4515
+ // root cause and the cheapest to try (one slash command).
4516
+ tips.push(`Try \`/thinking low\` (or \`/thinking medium\`) — most ${currentProvider} stalls are over-eager reasoning, not auth or network.`);
4517
+ const fallback = providers.find((p) => p.available && p.provider !== currentProvider);
4518
+ if (fallback) {
4519
+ tips.push(`Or \`/provider ${fallback.provider}\` to switch providers; your last message stays in scrollback so you can re-send.`);
4520
+ }
4521
+ tips.push(`Or \`wotann login ${currentProvider}\` to re-authenticate (rare; usually the issue above).`);
4522
+ setMessages((prev) => [
4523
+ ...prev,
4524
+ {
4525
+ role: "system",
4526
+ content: tips.join("\n"),
4527
+ },
4528
+ ]);
3095
4529
  }
3096
4530
  if (!abort.signal.aborted) {
3097
4531
  // Show errors as system messages (not mixed into assistant response)
@@ -3115,12 +4549,29 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3115
4549
  ]);
3116
4550
  void voiceControllerRef.current.speakAssistantReply(fullContent);
3117
4551
  }
4552
+ else if (fullThinking.trim() && errors.length === 0) {
4553
+ // Reasoning-only response — provider thought but never
4554
+ // emitted a text answer (codex sometimes does this for
4555
+ // overlong reasoning chains). Surface the thinking so
4556
+ // the user has something rather than a silent close.
4557
+ setMessages((prev) => [
4558
+ ...prev,
4559
+ {
4560
+ role: "assistant",
4561
+ content: `[reasoning, no answer]\n${fullThinking.slice(0, 2000)}`,
4562
+ model: responseModel,
4563
+ provider: responseProvider,
4564
+ tokensUsed,
4565
+ },
4566
+ ]);
4567
+ }
3118
4568
  else if (errors.length === 0) {
3119
4569
  setMessages((prev) => [
3120
4570
  ...prev,
3121
4571
  {
3122
4572
  role: "system",
3123
- content: "No response received from provider.",
4573
+ content: `Empty response from ${responseProvider}/${responseModel}. ` +
4574
+ `Try /provider to switch or re-send.`,
3124
4575
  },
3125
4576
  ]);
3126
4577
  }
@@ -3128,6 +4579,7 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3128
4579
  }
3129
4580
  }
3130
4581
  catch (error) {
4582
+ clearInterval(watchdog);
3131
4583
  if (!abort.signal.aborted) {
3132
4584
  const msg = error instanceof Error ? error.message : "Unknown error";
3133
4585
  setMessages((prev) => [...prev, { role: "system", content: `Error: ${msg}` }]);
@@ -3154,10 +4606,12 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3154
4606
  }
3155
4607
  setIsStreaming(false);
3156
4608
  setStreamingContent("");
4609
+ setStreamingThinking("");
4610
+ streamingGuardRef.current = false;
3157
4611
  abortRef.current = null;
3158
4612
  }, [
3159
4613
  currentModel,
3160
- initialProvider,
4614
+ currentProvider,
3161
4615
  runtime,
3162
4616
  messages,
3163
4617
  handleSlashCommand,
@@ -3187,7 +4641,14 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3187
4641
  });
3188
4642
  return sources;
3189
4643
  }, [runtime, messages.length]);
3190
- const activeProvider = providers.find((p) => p.available);
4644
+ // Prefer the user's explicit pick (currentProvider) over a "first
4645
+ // available" fallback. If currentProvider isn't (yet) in the providers
4646
+ // array (e.g. bootstrap is still detecting it), fall back to the
4647
+ // initialProvider record; finally fall back to first-available so the
4648
+ // status bar always has something to show.
4649
+ const activeProvider = liveProviders.find((p) => p.provider === currentProvider) ??
4650
+ liveProviders.find((p) => p.provider === initialProvider) ??
4651
+ liveProviders.find((p) => p.available);
3191
4652
  const runtimeStatus = runtime?.getStatus();
3192
4653
  const currentTheme = themeManagerRef.current.getCurrent();
3193
4654
  // contextUsagePercent removed — UnifiedStatusBar derives the percent
@@ -3195,54 +4656,105 @@ export function WotannApp({ version, providers, initialModel = "gemma4:e4b", ini
3195
4656
  // (legacy from when the StatusBar at the bottom of the layout took
3196
4657
  // a precomputed contextPercent prop) is no longer needed.
3197
4658
  const diffPanel = diffEntries[0];
3198
- return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [showStartup && (_jsx(StartupScreen, { version: version, providers: providers, palette: currentTheme.colors })), needsOnboarding && (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, borderStyle: "round", borderColor: currentTheme.colors.warning, marginBottom: 1, children: [_jsx(Text, { bold: true, color: currentTheme.colors.warning, children: "\u26A0 No providers configured" }), _jsx(Text, { color: currentTheme.colors.muted, children: "Run `wotann init` to set up a provider, or run `wotann login <provider>` to add one. Free options: Ollama (local), Google Gemini (free tier), Groq (free tier)." })] })), _jsx(UnifiedStatusBar, { model: currentModel, provider: activeProvider?.provider ?? initialProvider, mode: currentMode, usedTokens: runtimeStatus?.totalTokens ?? 0, maxTokens: runtime?.getMaxContextTokens() ?? 200_000, costUsd: runtimeStatus?.totalCost ?? stats.cost, reads: stats.reads, edits: stats.edits, bashCalls: stats.bashCalls, turnCount: turnCount, skillCount: runtimeStatus?.skillCount, isStreaming: isStreaming, roeSessionActive: runtime?.getActiveROESessionId() !== undefined }), _jsx(MainScene, { messages: messages, isStreaming: isStreaming, streamingContent: streamingContent, currentModel: currentModel, activePanel: activePanel, diffPanel: diffPanel ? { filePath: diffPanel.filePath, hunks: diffPanel.hunks } : undefined, agentStatuses: agentStatuses, thinkingEffort: thinkingEffort, history: history, currentThemeBorder: currentTheme.colors.border,
3199
- // `accent` is the canonical brand-tint slot; `primary` is its
3200
- // deprecated alias (see src/ui/themes.ts ThemeColors). Use
3201
- // accent so theme-cycle keeps working when palettes drop the
3202
- // legacy property entirely.
3203
- currentThemePrimary: currentTheme.colors.accent, currentThemeInfo: currentTheme.colors.info }), showHistoryPicker && (_jsx(HistoryPicker, { history: history, onSelect: handleHistorySelect, onCancel: handleHistoryCancel })), showCommandPalette && (_jsx(CommandPalette, { registry: commandRegistryRef.current, onClose: handleCommandPaletteClose, onError: handleCommandPaletteError, palette: currentTheme.colors })), optionPicker && (_jsx(OptionPicker, { title: optionPicker.title, options: optionPicker.options, currentValue: optionPicker.currentValue, onSelect: optionPicker.onSelect, onCancel: () => setOptionPicker(null) })), showModelPicker && (_jsx(ModelPicker, { providers: providers, currentProvider: activeProvider?.provider ?? initialProvider, currentModel: currentModel, onSelect: (provider, model) => {
3204
- setShowModelPicker(false);
3205
- setCurrentModel(model);
3206
- // Note: provider is captured for completeness (the routing
3207
- // layer infers provider from model id, but a future
3208
- // setActiveProvider hook can use it directly without
3209
- // touching this call site).
3210
- setMessages((prev) => [
3211
- ...prev,
3212
- {
3213
- role: "system",
3214
- content: `Switched to ${provider}/${model}.`,
3215
- },
3216
- ]);
3217
- }, onCancel: () => setShowModelPicker(false), onRefresh: () => {
3218
- // Force-refresh every provider's model catalog. Lazy-load
3219
- // the discoverer so the import isn't paid on first render.
3220
- void import("../providers/model-discovery.js").then(({ discoverModelsForProvider }) => {
3221
- for (const auth of providers) {
3222
- if (!auth.available)
3223
- continue;
3224
- void discoverModelsForProvider({
3225
- provider: auth.provider,
3226
- fallback: auth.models,
3227
- forceRefresh: true,
3228
- });
3229
- }
4659
+ return (_jsx(ThemeProvider, { palette: currentTheme.colors, children: _jsxs(Box, { flexDirection: "column", height: "100%", children: [showStartup && (_jsx(StartupScreen, { version: version, providers: providers, palette: currentTheme.colors })), needsOnboarding && (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, borderStyle: "round", borderColor: currentTheme.colors.warning, marginBottom: 1, children: [_jsx(Text, { bold: true, color: currentTheme.colors.warning, children: "\u26A0 No providers configured" }), _jsx(Text, { color: currentTheme.colors.muted, children: "Run `wotann init` to set up a provider, or run `wotann login <provider>` to add one. Free options: Ollama (local), Google Gemini (free tier), Groq (free tier)." })] })), _jsx(UnifiedStatusBar, { model: currentModel, provider: activeProvider?.provider ?? initialProvider, mode: currentMode, usedTokens: runtimeStatus?.totalTokens ?? 0, maxTokens: runtime?.getMaxContextTokens() ?? 200_000, costUsd: runtimeStatus?.totalCost ?? stats.cost, reads: stats.reads, edits: stats.edits, bashCalls: stats.bashCalls, turnCount: turnCount, skillCount: runtimeStatus?.skillCount, isStreaming: isStreaming, roeSessionActive: runtime?.getActiveROESessionId() !== undefined }), _jsx(MainScene, { messages: messages, isStreaming: isStreaming, streamingContent: streamingContent, streamingThinking: streamingThinking, currentModel: currentModel, activePanel: activePanel, diffPanel: diffPanel ? { filePath: diffPanel.filePath, hunks: diffPanel.hunks } : undefined, agentStatuses: agentStatuses, thinkingEffort: thinkingEffort, history: history, computerTools: computerTools, computerActions: computerActions, currentThemeBorder: currentTheme.colors.border,
4660
+ // `accent` is the canonical brand-tint slot; `primary` is its
4661
+ // deprecated alias (see src/ui/themes.ts ThemeColors). Use
4662
+ // accent so theme-cycle keeps working when palettes drop the
4663
+ // legacy property entirely.
4664
+ currentThemePrimary: currentTheme.colors.accent, currentThemeInfo: currentTheme.colors.info }), showHistoryPicker && (_jsx(HistoryPicker, { history: history, onSelect: handleHistorySelect, onCancel: handleHistoryCancel })), showCommandPalette && (_jsx(CommandPalette, { registry: commandRegistryRef.current, onClose: handleCommandPaletteClose, onError: handleCommandPaletteError, palette: currentTheme.colors })), optionPicker && (_jsx(OptionPicker, { title: optionPicker.title, options: optionPicker.options, currentValue: optionPicker.currentValue, onSelect: optionPicker.onSelect, onCancel: () => setOptionPicker(null) })), showModelPicker && (_jsx(ModelPicker, { providers: liveProviders, currentProvider: activeProvider?.provider ?? initialProvider, currentModel: currentModel, onSelect: (provider, model) => {
4665
+ setShowModelPicker(false);
4666
+ // Provider-agnostic atomic switch: the runtime's
4667
+ // setActiveProvider updates session.provider AND session.model
4668
+ // together AND refits context-intelligence + system-prompt
4669
+ // resending. Without this, picking e.g. "codex/gpt-5.5" only
4670
+ // updates currentModel the runtime keeps routing through
4671
+ // the previously-active provider's adapter, producing
4672
+ // "unknown model" errors on the next turn.
4673
+ setCurrentProvider(provider);
4674
+ setCurrentModel(model);
4675
+ runtime?.setActiveProvider(provider, model);
3230
4676
  setMessages((prev) => [
3231
4677
  ...prev,
3232
4678
  {
3233
4679
  role: "system",
3234
- content: "Model catalogs refreshing in the background. The picker " +
3235
- "will reflect new entries on the next open (cache TTL: 5 min).",
4680
+ content: `Switched to ${provider}/${model}.`,
3236
4681
  },
3237
4682
  ]);
3238
- });
3239
- } })), showTrustPanel && (_jsx(TrustPanel, { workingDir: workingDir, onClose: () => setShowTrustPanel(false) })), showGdprPanel && _jsx(GdprPanel, { onClose: () => setShowGdprPanel(false) }), showAuditPanel && _jsx(AuditLogPanel, { onClose: () => setShowAuditPanel(false) }), showAutomationsPanel && _jsx(AutomationsPanel, { onClose: () => setShowAutomationsPanel(false) }), showContextPanel && runtime && (_jsx(ContextSourcePanel, { sources: contextSources, totalTokens: runtimeStatus?.totalTokens ?? 0, maxTokens: runtime.getMaxContextTokens() })), showTerminalBlocks && _jsx(TerminalBlocksView, { blocks: terminalBlocks }), showMessageActions &&
3240
- messages.length > 0 &&
3241
- (() => {
3242
- const lastIdx = messages.length - 1;
3243
- const lastMsg = messages[lastIdx];
3244
- const role = lastMsg.role === "user" || lastMsg.role === "assistant" ? lastMsg.role : "assistant";
3245
- return (_jsx(MessageActions, { messageId: String(lastIdx), content: lastMsg.content, role: role, onAction: handleMessageAction }));
3246
- })(), isIncognito && (_jsxs(Box, { paddingX: 1, gap: 1, children: [_jsx(Text, { color: currentTheme.colors.warning, bold: true, children: "\u25D0 INCOGNITO" }), _jsx(Text, { color: currentTheme.colors.muted, children: "\u00B7 memory capture paused \u00B7 all messages local" })] })), queueDepth > 0 && (_jsxs(Box, { paddingX: 1, gap: 1, children: [_jsxs(Text, { color: currentTheme.colors.info, bold: true, children: ["\uD83D\uDCE8 ", queueDepth, " message", queueDepth === 1 ? "" : "s", " queued"] }), _jsx(Text, { color: currentTheme.colors.muted, children: "\u00B7 will prepend on next turn" })] })), _jsx(PromptInput, { onSubmit: handleSubmit, onChange: setPromptValue, onAbort: handleAbort, disabled: isStreaming, isStreaming: isStreaming, history: history, mode: currentMode, value: promptValue })] }));
4683
+ }, onCancel: () => setShowModelPicker(false), onRefresh: () => {
4684
+ if (!runtime) {
4685
+ setMessages((prev) => [
4686
+ ...prev,
4687
+ {
4688
+ role: "system",
4689
+ content: "Model refresh requires the local runtime to be active.",
4690
+ },
4691
+ ]);
4692
+ return;
4693
+ }
4694
+ void runtime.refreshProviderModelCatalogs().then((catalog) => {
4695
+ setLiveProviders((prev) => prev.map((provider) => {
4696
+ const models = catalog[provider.provider];
4697
+ return models && models.length > 0 ? { ...provider, models } : provider;
4698
+ }));
4699
+ setMessages((prev) => [
4700
+ ...prev,
4701
+ {
4702
+ role: "system",
4703
+ content: Object.keys(catalog).length > 0
4704
+ ? "Model catalogs refreshed from the active daemon adapters."
4705
+ : "No live model catalog updates were available.",
4706
+ },
4707
+ ]);
4708
+ });
4709
+ } })), showProviderSetup && (_jsx(ProviderSetupOverlay, { providers: liveProviders, onCancel: () => setShowProviderSetup(false), onOpenModelPicker: () => {
4710
+ setShowProviderSetup(false);
4711
+ setShowModelPicker(true);
4712
+ }, onSave: async (providerId, token) => {
4713
+ const { getProviderService } = await import("../providers/provider-service.js");
4714
+ const service = getProviderService();
4715
+ const snapshot = await service.getSnapshot({ force: false });
4716
+ const known = snapshot.providers.find((provider) => provider.id === providerId);
4717
+ const spec = service.getSpec(providerId);
4718
+ if (!known)
4719
+ throw new Error(`Unknown provider "${providerId}"`);
4720
+ if (providerId === "ollama" && token.length === 0) {
4721
+ const catalog = await runtime?.refreshProviderModelCatalogs();
4722
+ if (catalog) {
4723
+ setLiveProviders((prev) => prev.map((provider) => {
4724
+ const models = catalog[provider.provider];
4725
+ return models && models.length > 0
4726
+ ? { ...provider, available: true, models }
4727
+ : provider;
4728
+ }));
4729
+ }
4730
+ appendSystemMessage("Ollama checked. Start Ollama locally if no models appear.");
4731
+ return;
4732
+ }
4733
+ const method = chooseProviderCredentialMethod(providerId, token, spec);
4734
+ const state = await service.saveCredential(providerId, {
4735
+ method,
4736
+ token,
4737
+ label: `${known.name} from TUI Provider Forge`,
4738
+ });
4739
+ const refreshed = await service.getSnapshot({ force: true });
4740
+ setLiveProviders((prev) => prev.map((provider) => {
4741
+ const fresh = refreshed.providers.find((candidate) => candidate.id === provider.provider);
4742
+ return fresh ? mergeProviderState(provider, fresh) : provider;
4743
+ }));
4744
+ const model = state?.defaultModel ?? state?.models[0]?.id ?? currentModel;
4745
+ if (state?.configured && model) {
4746
+ setCurrentProvider(providerId);
4747
+ setCurrentModel(model);
4748
+ runtime?.setActiveProvider(providerId, model);
4749
+ }
4750
+ appendSystemMessage(`${known.name} ${method} credential saved. ${model ? `Active model: ${model}` : "Refresh providers to discover models."}`);
4751
+ } })), showTrustPanel && (_jsx(TrustPanel, { workingDir: workingDir, onClose: () => setShowTrustPanel(false) })), showGdprPanel && _jsx(GdprPanel, { onClose: () => setShowGdprPanel(false) }), showAuditPanel && _jsx(AuditLogPanel, { onClose: () => setShowAuditPanel(false) }), showAutomationsPanel && (_jsx(AutomationsPanel, { onClose: () => setShowAutomationsPanel(false) })), showContextPanel && runtime && (_jsx(ContextSourcePanel, { sources: contextSources, totalTokens: runtimeStatus?.totalTokens ?? 0, maxTokens: runtime.getMaxContextTokens() })), showTerminalBlocks && _jsx(TerminalBlocksView, { blocks: terminalBlocks }), showMessageActions &&
4752
+ messages.length > 0 &&
4753
+ (() => {
4754
+ const lastIdx = messages.length - 1;
4755
+ const lastMsg = messages[lastIdx];
4756
+ const role = lastMsg.role === "user" || lastMsg.role === "assistant" ? lastMsg.role : "assistant";
4757
+ return (_jsx(MessageActions, { messageId: String(lastIdx), content: lastMsg.content, role: role, onAction: handleMessageAction }));
4758
+ })(), isIncognito && (_jsxs(Box, { paddingX: 1, gap: 1, children: [_jsx(Text, { color: currentTheme.colors.warning, bold: true, children: "\u25D0 INCOGNITO" }), _jsx(Text, { color: currentTheme.colors.muted, children: "\u00B7 memory capture paused \u00B7 all messages local" })] })), queueDepth > 0 && (_jsxs(Box, { paddingX: 1, gap: 1, children: [_jsxs(Text, { color: currentTheme.colors.info, bold: true, children: ["\uD83D\uDCE8 ", queueDepth, " message", queueDepth === 1 ? "" : "s", " queued"] }), _jsx(Text, { color: currentTheme.colors.muted, children: "\u00B7 will prepend on next turn" })] })), _jsx(PromptInput, { onSubmit: handleSubmit, onChange: setPromptValue, onAbort: handleAbort, disabled: isStreaming, isStreaming: isStreaming, history: history, mode: currentMode, value: promptValue })] }) }));
3247
4759
  }
3248
4760
  //# sourceMappingURL=App.js.map