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.
- package/dist/agents/background-agent.d.ts.map +1 -1
- package/dist/agents/background-agent.js +2 -1
- package/dist/agents/background-agent.js.map +1 -1
- package/dist/agents/file-scope-policy.d.ts +116 -0
- package/dist/agents/file-scope-policy.d.ts.map +1 -0
- package/dist/agents/file-scope-policy.js +190 -0
- package/dist/agents/file-scope-policy.js.map +1 -0
- package/dist/agents/yaml-manifest.d.ts +125 -0
- package/dist/agents/yaml-manifest.d.ts.map +1 -0
- package/dist/agents/yaml-manifest.js +901 -0
- package/dist/agents/yaml-manifest.js.map +1 -0
- package/dist/auth/login.d.ts +3 -3
- package/dist/auth/login.d.ts.map +1 -1
- package/dist/auth/login.js +80 -23
- package/dist/auth/login.js.map +1 -1
- package/dist/autopilot/completion-oracle.d.ts.map +1 -1
- package/dist/autopilot/completion-oracle.js +3 -1
- package/dist/autopilot/completion-oracle.js.map +1 -1
- package/dist/autopilot/run-manifest.d.ts +90 -0
- package/dist/autopilot/run-manifest.d.ts.map +1 -0
- package/dist/autopilot/run-manifest.js +261 -0
- package/dist/autopilot/run-manifest.js.map +1 -0
- package/dist/browser/adaptive-selectors.d.ts +170 -0
- package/dist/browser/adaptive-selectors.d.ts.map +1 -0
- package/dist/browser/adaptive-selectors.js +317 -0
- package/dist/browser/adaptive-selectors.js.map +1 -0
- package/dist/browser/humanize-input.d.ts +76 -0
- package/dist/browser/humanize-input.d.ts.map +1 -0
- package/dist/browser/humanize-input.js +386 -0
- package/dist/browser/humanize-input.js.map +1 -0
- package/dist/channels/teams.d.ts +41 -19
- package/dist/channels/teams.d.ts.map +1 -1
- package/dist/channels/teams.js +121 -24
- package/dist/channels/teams.js.map +1 -1
- package/dist/channels/terminal-mention.d.ts.map +1 -1
- package/dist/channels/terminal-mention.js +4 -1
- package/dist/channels/terminal-mention.js.map +1 -1
- package/dist/cli/commands/blast-radius.d.ts +47 -0
- package/dist/cli/commands/blast-radius.d.ts.map +1 -0
- package/dist/cli/commands/blast-radius.js +123 -0
- package/dist/cli/commands/blast-radius.js.map +1 -0
- package/dist/cli/commands/evolve.d.ts +92 -0
- package/dist/cli/commands/evolve.d.ts.map +1 -0
- package/dist/cli/commands/evolve.js +336 -0
- package/dist/cli/commands/evolve.js.map +1 -0
- package/dist/cli/commands/learning.d.ts +54 -0
- package/dist/cli/commands/learning.d.ts.map +1 -0
- package/dist/cli/commands/learning.js +380 -0
- package/dist/cli/commands/learning.js.map +1 -0
- package/dist/cli/commands/persona.d.ts +42 -0
- package/dist/cli/commands/persona.d.ts.map +1 -0
- package/dist/cli/commands/persona.js +198 -0
- package/dist/cli/commands/persona.js.map +1 -0
- package/dist/cli/commands/stuck.d.ts.map +1 -1
- package/dist/cli/commands/stuck.js +5 -2
- package/dist/cli/commands/stuck.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +5 -2
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/orphan-wires/agent-manifest-cmd.d.ts +17 -0
- package/dist/cli/orphan-wires/agent-manifest-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/agent-manifest-cmd.js +99 -0
- package/dist/cli/orphan-wires/agent-manifest-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/arena-cmd.d.ts +26 -0
- package/dist/cli/orphan-wires/arena-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/arena-cmd.js +167 -0
- package/dist/cli/orphan-wires/arena-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/artifact-cmd.d.ts +17 -0
- package/dist/cli/orphan-wires/artifact-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/artifact-cmd.js +175 -0
- package/dist/cli/orphan-wires/artifact-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/demo-cmd.d.ts +25 -0
- package/dist/cli/orphan-wires/demo-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/demo-cmd.js +164 -0
- package/dist/cli/orphan-wires/demo-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/extras-cmd.d.ts +20 -0
- package/dist/cli/orphan-wires/extras-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/extras-cmd.js +289 -0
- package/dist/cli/orphan-wires/extras-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts +15 -0
- package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/harness-introspect-cmd.js +36 -0
- package/dist/cli/orphan-wires/harness-introspect-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/index.d.ts.map +1 -1
- package/dist/cli/orphan-wires/index.js +30 -0
- package/dist/cli/orphan-wires/index.js.map +1 -1
- package/dist/cli/orphan-wires/integrations-cmd.d.ts +38 -0
- package/dist/cli/orphan-wires/integrations-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/integrations-cmd.js +345 -0
- package/dist/cli/orphan-wires/integrations-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/mcp-scaffold-cmd.d.ts +18 -0
- package/dist/cli/orphan-wires/mcp-scaffold-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/mcp-scaffold-cmd.js +127 -0
- package/dist/cli/orphan-wires/mcp-scaffold-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/memory-consolidate-cmd.d.ts +21 -0
- package/dist/cli/orphan-wires/memory-consolidate-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/memory-consolidate-cmd.js +86 -0
- package/dist/cli/orphan-wires/memory-consolidate-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/patch-cmd.d.ts +29 -0
- package/dist/cli/orphan-wires/patch-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/patch-cmd.js +150 -0
- package/dist/cli/orphan-wires/patch-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts +28 -0
- package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/redteam-scan-cmd.js +169 -0
- package/dist/cli/orphan-wires/redteam-scan-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/scrape-adapt-cmd.d.ts +21 -0
- package/dist/cli/orphan-wires/scrape-adapt-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/scrape-adapt-cmd.js +70 -0
- package/dist/cli/orphan-wires/scrape-adapt-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/shell-tier-cmd.d.ts +20 -0
- package/dist/cli/orphan-wires/shell-tier-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/shell-tier-cmd.js +74 -0
- package/dist/cli/orphan-wires/shell-tier-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/sub-recipes-cmd.d.ts +18 -0
- package/dist/cli/orphan-wires/sub-recipes-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/sub-recipes-cmd.js +87 -0
- package/dist/cli/orphan-wires/sub-recipes-cmd.js.map +1 -0
- package/dist/cli/orphan-wires/webapp-test-cmd.d.ts +20 -0
- package/dist/cli/orphan-wires/webapp-test-cmd.d.ts.map +1 -0
- package/dist/cli/orphan-wires/webapp-test-cmd.js +115 -0
- package/dist/cli/orphan-wires/webapp-test-cmd.js.map +1 -0
- package/dist/cli/thin-client.d.ts +17 -10
- package/dist/cli/thin-client.d.ts.map +1 -1
- package/dist/cli/thin-client.js +108 -26
- package/dist/cli/thin-client.js.map +1 -1
- package/dist/computer-use/action-history-compactor.d.ts +92 -0
- package/dist/computer-use/action-history-compactor.d.ts.map +1 -0
- package/dist/computer-use/action-history-compactor.js +204 -0
- package/dist/computer-use/action-history-compactor.js.map +1 -0
- package/dist/computer-use/action-repertoire.d.ts +8 -0
- package/dist/computer-use/action-repertoire.d.ts.map +1 -1
- package/dist/computer-use/action-repertoire.js +19 -0
- package/dist/computer-use/action-repertoire.js.map +1 -1
- package/dist/computer-use/computer-agent.d.ts +232 -2
- package/dist/computer-use/computer-agent.d.ts.map +1 -1
- package/dist/computer-use/computer-agent.js +558 -19
- package/dist/computer-use/computer-agent.js.map +1 -1
- package/dist/computer-use/coordinate-scaling.d.ts +104 -0
- package/dist/computer-use/coordinate-scaling.d.ts.map +1 -0
- package/dist/computer-use/coordinate-scaling.js +175 -0
- package/dist/computer-use/coordinate-scaling.js.map +1 -0
- package/dist/computer-use/driver-contract.d.ts +158 -0
- package/dist/computer-use/driver-contract.d.ts.map +1 -0
- package/dist/computer-use/driver-contract.js +471 -0
- package/dist/computer-use/driver-contract.js.map +1 -0
- package/dist/computer-use/perception-engine.d.ts +39 -0
- package/dist/computer-use/perception-engine.d.ts.map +1 -1
- package/dist/computer-use/perception-engine.js +27 -27
- package/dist/computer-use/perception-engine.js.map +1 -1
- package/dist/computer-use/platform-bindings.d.ts.map +1 -1
- package/dist/computer-use/platform-bindings.js +216 -2
- package/dist/computer-use/platform-bindings.js.map +1 -1
- package/dist/computer-use/safe-execute.d.ts +86 -0
- package/dist/computer-use/safe-execute.d.ts.map +1 -0
- package/dist/computer-use/safe-execute.js +152 -0
- package/dist/computer-use/safe-execute.js.map +1 -0
- package/dist/context/branch-summarization.d.ts +97 -0
- package/dist/context/branch-summarization.d.ts.map +1 -0
- package/dist/context/branch-summarization.js +170 -0
- package/dist/context/branch-summarization.js.map +1 -0
- package/dist/context/compaction.d.ts +16 -1
- package/dist/context/compaction.d.ts.map +1 -1
- package/dist/context/compaction.js +50 -2
- package/dist/context/compaction.js.map +1 -1
- package/dist/context/inspector.d.ts +2 -2
- package/dist/context/inspector.d.ts.map +1 -1
- package/dist/context/inspector.js +4 -3
- package/dist/context/inspector.js.map +1 -1
- package/dist/core/agent-bridge.d.ts.map +1 -1
- package/dist/core/agent-bridge.js +200 -69
- package/dist/core/agent-bridge.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +7 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/mode-model-preference.d.ts +95 -0
- package/dist/core/mode-model-preference.d.ts.map +1 -0
- package/dist/core/mode-model-preference.js +170 -0
- package/dist/core/mode-model-preference.js.map +1 -0
- package/dist/core/prompt-override.d.ts.map +1 -1
- package/dist/core/prompt-override.js +1 -0
- package/dist/core/prompt-override.js.map +1 -1
- package/dist/core/runtime-intelligence.d.ts +11 -0
- package/dist/core/runtime-intelligence.d.ts.map +1 -1
- package/dist/core/runtime-intelligence.js +18 -3
- package/dist/core/runtime-intelligence.js.map +1 -1
- package/dist/core/runtime-tool-dispatch.js +1 -1
- package/dist/core/runtime-tool-dispatch.js.map +1 -1
- package/dist/core/runtime.d.ts +186 -13
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +717 -228
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/session-rewind.d.ts +67 -0
- package/dist/core/session-rewind.d.ts.map +1 -0
- package/dist/core/session-rewind.js +120 -0
- package/dist/core/session-rewind.js.map +1 -0
- package/dist/core/types.d.ts +19 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/daemon/file-watcher.d.ts +84 -0
- package/dist/daemon/file-watcher.d.ts.map +1 -0
- package/dist/daemon/file-watcher.js +193 -0
- package/dist/daemon/file-watcher.js.map +1 -0
- package/dist/daemon/jsonl-mode.d.ts +11 -0
- package/dist/daemon/jsonl-mode.d.ts.map +1 -0
- package/dist/daemon/jsonl-mode.js +77 -0
- package/dist/daemon/jsonl-mode.js.map +1 -0
- package/dist/daemon/kairos-ipc.d.ts +4 -1
- package/dist/daemon/kairos-ipc.d.ts.map +1 -1
- package/dist/daemon/kairos-ipc.js +38 -22
- package/dist/daemon/kairos-ipc.js.map +1 -1
- package/dist/daemon/kairos-rpc.d.ts +5 -1
- package/dist/daemon/kairos-rpc.d.ts.map +1 -1
- package/dist/daemon/kairos-rpc.js +1262 -114
- package/dist/daemon/kairos-rpc.js.map +1 -1
- package/dist/daemon/kairos.d.ts +35 -4
- package/dist/daemon/kairos.d.ts.map +1 -1
- package/dist/daemon/kairos.js +204 -24
- package/dist/daemon/kairos.js.map +1 -1
- package/dist/daemon/rpc-handlers/intelligence-rpc.d.ts.map +1 -1
- package/dist/daemon/rpc-handlers/intelligence-rpc.js +15 -3
- package/dist/daemon/rpc-handlers/intelligence-rpc.js.map +1 -1
- package/dist/daemon/transport/jsonl.d.ts +7 -0
- package/dist/daemon/transport/jsonl.d.ts.map +1 -0
- package/dist/daemon/transport/jsonl.js +38 -0
- package/dist/daemon/transport/jsonl.js.map +1 -0
- package/dist/daemon/transport/socket-path.d.ts +28 -0
- package/dist/daemon/transport/socket-path.d.ts.map +1 -0
- package/dist/daemon/transport/socket-path.js +36 -0
- package/dist/daemon/transport/socket-path.js.map +1 -0
- package/dist/design/theme-presets.d.ts +77 -0
- package/dist/design/theme-presets.d.ts.map +1 -0
- package/dist/design/theme-presets.js +274 -0
- package/dist/design/theme-presets.js.map +1 -0
- package/dist/desktop/companion-server.d.ts +67 -4
- package/dist/desktop/companion-server.d.ts.map +1 -1
- package/dist/desktop/companion-server.js +617 -70
- package/dist/desktop/companion-server.js.map +1 -1
- package/dist/desktop/web-artifacts-builder.d.ts +108 -0
- package/dist/desktop/web-artifacts-builder.d.ts.map +1 -0
- package/dist/desktop/web-artifacts-builder.js +184 -0
- package/dist/desktop/web-artifacts-builder.js.map +1 -0
- package/dist/hooks/built-in.d.ts.map +1 -1
- package/dist/hooks/built-in.js +15 -10
- package/dist/hooks/built-in.js.map +1 -1
- package/dist/index.js +1301 -167
- package/dist/index.js.map +1 -1
- package/dist/integrations/integration-manifest.d.ts +140 -0
- package/dist/integrations/integration-manifest.d.ts.map +1 -0
- package/dist/integrations/integration-manifest.js +268 -0
- package/dist/integrations/integration-manifest.js.map +1 -0
- package/dist/intelligence/apply-patch-dsl.d.ts +112 -0
- package/dist/intelligence/apply-patch-dsl.d.ts.map +1 -0
- package/dist/intelligence/apply-patch-dsl.js +264 -0
- package/dist/intelligence/apply-patch-dsl.js.map +1 -0
- package/dist/intelligence/apply-patch-executor.d.ts +147 -0
- package/dist/intelligence/apply-patch-executor.d.ts.map +1 -0
- package/dist/intelligence/apply-patch-executor.js +418 -0
- package/dist/intelligence/apply-patch-executor.js.map +1 -0
- package/dist/intelligence/blast-radius.d.ts +67 -0
- package/dist/intelligence/blast-radius.d.ts.map +1 -0
- package/dist/intelligence/blast-radius.js +536 -0
- package/dist/intelligence/blast-radius.js.map +1 -0
- package/dist/intelligence/code-graph.d.ts +58 -7
- package/dist/intelligence/code-graph.d.ts.map +1 -1
- package/dist/intelligence/code-graph.js +347 -16
- package/dist/intelligence/code-graph.js.map +1 -1
- package/dist/intelligence/codebase-health.d.ts.map +1 -1
- package/dist/intelligence/codebase-health.js +4 -3
- package/dist/intelligence/codebase-health.js.map +1 -1
- package/dist/intelligence/codemaps.d.ts +30 -1
- package/dist/intelligence/codemaps.d.ts.map +1 -1
- package/dist/intelligence/codemaps.js +99 -16
- package/dist/intelligence/codemaps.js.map +1 -1
- package/dist/intelligence/consecutive-error-counter.d.ts +89 -0
- package/dist/intelligence/consecutive-error-counter.d.ts.map +1 -0
- package/dist/intelligence/consecutive-error-counter.js +151 -0
- package/dist/intelligence/consecutive-error-counter.js.map +1 -0
- package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.d.ts +87 -0
- package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.d.ts.map +1 -0
- package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.js +882 -0
- package/dist/intelligence/eval-frameworks/redteam-plugin-catalog.js.map +1 -0
- package/dist/intelligence/extractors/mineru.d.ts +108 -0
- package/dist/intelligence/extractors/mineru.d.ts.map +1 -0
- package/dist/intelligence/extractors/mineru.js +352 -0
- package/dist/intelligence/extractors/mineru.js.map +1 -0
- package/dist/intelligence/harness-introspect.d.ts +124 -0
- package/dist/intelligence/harness-introspect.d.ts.map +1 -0
- package/dist/intelligence/harness-introspect.js +349 -0
- package/dist/intelligence/harness-introspect.js.map +1 -0
- package/dist/intelligence/multi-patch-voter.d.ts +59 -1
- package/dist/intelligence/multi-patch-voter.d.ts.map +1 -1
- package/dist/intelligence/multi-patch-voter.js +191 -27
- package/dist/intelligence/multi-patch-voter.js.map +1 -1
- package/dist/intelligence/multimodal-extract.d.ts +44 -0
- package/dist/intelligence/multimodal-extract.d.ts.map +1 -1
- package/dist/intelligence/multimodal-extract.js +40 -1
- package/dist/intelligence/multimodal-extract.js.map +1 -1
- package/dist/intelligence/research-loops/budgeted-research.d.ts +7 -0
- package/dist/intelligence/research-loops/budgeted-research.d.ts.map +1 -1
- package/dist/intelligence/research-loops/budgeted-research.js +18 -0
- package/dist/intelligence/research-loops/budgeted-research.js.map +1 -1
- package/dist/intelligence/research-loops/constraint-guided-explorer.d.ts +124 -0
- package/dist/intelligence/research-loops/constraint-guided-explorer.d.ts.map +1 -0
- package/dist/intelligence/research-loops/constraint-guided-explorer.js +177 -0
- package/dist/intelligence/research-loops/constraint-guided-explorer.js.map +1 -0
- package/dist/intelligence/research-loops/results-collector.d.ts +62 -0
- package/dist/intelligence/research-loops/results-collector.d.ts.map +1 -0
- package/dist/intelligence/research-loops/results-collector.js +128 -0
- package/dist/intelligence/research-loops/results-collector.js.map +1 -0
- package/dist/intelligence/research-loops/results-log.d.ts +94 -0
- package/dist/intelligence/research-loops/results-log.d.ts.map +1 -0
- package/dist/intelligence/research-loops/results-log.js +178 -0
- package/dist/intelligence/research-loops/results-log.js.map +1 -0
- package/dist/intelligence/research-loops/strategies/base-strategy.d.ts +111 -0
- package/dist/intelligence/research-loops/strategies/base-strategy.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategies/base-strategy.js +72 -0
- package/dist/intelligence/research-loops/strategies/base-strategy.js.map +1 -0
- package/dist/intelligence/research-loops/strategies/dual-confidence.d.ts +57 -0
- package/dist/intelligence/research-loops/strategies/dual-confidence.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategies/dual-confidence.js +172 -0
- package/dist/intelligence/research-loops/strategies/dual-confidence.js.map +1 -0
- package/dist/intelligence/research-loops/strategies/langgraph-agent.d.ts +68 -0
- package/dist/intelligence/research-loops/strategies/langgraph-agent.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategies/langgraph-agent.js +221 -0
- package/dist/intelligence/research-loops/strategies/langgraph-agent.js.map +1 -0
- package/dist/intelligence/research-loops/strategies/parallel-constrained.d.ts +42 -0
- package/dist/intelligence/research-loops/strategies/parallel-constrained.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategies/parallel-constrained.js +132 -0
- package/dist/intelligence/research-loops/strategies/parallel-constrained.js.map +1 -0
- package/dist/intelligence/research-loops/strategies/recursive-decomposition.d.ts +55 -0
- package/dist/intelligence/research-loops/strategies/recursive-decomposition.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategies/recursive-decomposition.js +187 -0
- package/dist/intelligence/research-loops/strategies/recursive-decomposition.js.map +1 -0
- package/dist/intelligence/research-loops/strategy-registry.d.ts +43 -0
- package/dist/intelligence/research-loops/strategy-registry.d.ts.map +1 -0
- package/dist/intelligence/research-loops/strategy-registry.js +62 -0
- package/dist/intelligence/research-loops/strategy-registry.js.map +1 -0
- package/dist/intelligence/research-strategies.d.ts +133 -0
- package/dist/intelligence/research-strategies.d.ts.map +1 -0
- package/dist/intelligence/research-strategies.js +204 -0
- package/dist/intelligence/research-strategies.js.map +1 -0
- package/dist/lib.d.ts +49 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +133 -0
- package/dist/lib.js.map +1 -1
- package/dist/loop/tool-description-reset.d.ts +113 -0
- package/dist/loop/tool-description-reset.d.ts.map +1 -0
- package/dist/loop/tool-description-reset.js +136 -0
- package/dist/loop/tool-description-reset.js.map +1 -0
- package/dist/lsp/server-registry.d.ts.map +1 -1
- package/dist/lsp/server-registry.js +2 -1
- package/dist/lsp/server-registry.js.map +1 -1
- package/dist/lsp/symbol-operations.d.ts.map +1 -1
- package/dist/lsp/symbol-operations.js +2 -1
- package/dist/lsp/symbol-operations.js.map +1 -1
- package/dist/marketplace/acp-agent-registry.d.ts.map +1 -1
- package/dist/marketplace/acp-agent-registry.js +2 -1
- package/dist/marketplace/acp-agent-registry.js.map +1 -1
- package/dist/marketplace/marketplace-source-git.d.ts +135 -0
- package/dist/marketplace/marketplace-source-git.d.ts.map +1 -0
- package/dist/marketplace/marketplace-source-git.js +211 -0
- package/dist/marketplace/marketplace-source-git.js.map +1 -0
- package/dist/marketplace/registry.d.ts +87 -0
- package/dist/marketplace/registry.d.ts.map +1 -1
- package/dist/marketplace/registry.js +113 -2
- package/dist/marketplace/registry.js.map +1 -1
- package/dist/mcp/chrome-devtools.d.ts +62 -2
- package/dist/mcp/chrome-devtools.d.ts.map +1 -1
- package/dist/mcp/chrome-devtools.js +183 -1
- package/dist/mcp/chrome-devtools.js.map +1 -1
- package/dist/mcp/health-probe.d.ts +1 -0
- package/dist/mcp/health-probe.d.ts.map +1 -1
- package/dist/mcp/health-probe.js +32 -1
- package/dist/mcp/health-probe.js.map +1 -1
- package/dist/mcp/mcp-scaffolder.d.ts +166 -0
- package/dist/mcp/mcp-scaffolder.d.ts.map +1 -0
- package/dist/mcp/mcp-scaffolder.js +526 -0
- package/dist/mcp/mcp-scaffolder.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +55 -0
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/memory-mcp.d.ts +163 -0
- package/dist/mcp/memory-mcp.d.ts.map +1 -0
- package/dist/mcp/memory-mcp.js +368 -0
- package/dist/mcp/memory-mcp.js.map +1 -0
- package/dist/mcp/tool-loader.d.ts.map +1 -1
- package/dist/mcp/tool-loader.js +13 -0
- package/dist/mcp/tool-loader.js.map +1 -1
- package/dist/meet/meeting-runtime.d.ts.map +1 -1
- package/dist/meet/meeting-runtime.js +3 -1
- package/dist/meet/meeting-runtime.js.map +1 -1
- package/dist/memory/incremental-indexer.d.ts.map +1 -1
- package/dist/memory/incremental-indexer.js +5 -1
- package/dist/memory/incremental-indexer.js.map +1 -1
- package/dist/memory/qmd-integration.d.ts.map +1 -1
- package/dist/memory/qmd-integration.js +47 -15
- package/dist/memory/qmd-integration.js.map +1 -1
- package/dist/memory/two-phase-consolidator.d.ts +118 -0
- package/dist/memory/two-phase-consolidator.d.ts.map +1 -0
- package/dist/memory/two-phase-consolidator.js +265 -0
- package/dist/memory/two-phase-consolidator.js.map +1 -0
- package/dist/middleware/doom-loop.d.ts +21 -0
- package/dist/middleware/doom-loop.d.ts.map +1 -1
- package/dist/middleware/doom-loop.js +49 -6
- package/dist/middleware/doom-loop.js.map +1 -1
- package/dist/middleware/loop-detection.d.ts +176 -6
- package/dist/middleware/loop-detection.d.ts.map +1 -1
- package/dist/middleware/loop-detection.js +341 -6
- package/dist/middleware/loop-detection.js.map +1 -1
- package/dist/middleware/pipeline.d.ts +9 -1
- package/dist/middleware/pipeline.d.ts.map +1 -1
- package/dist/middleware/pipeline.js +116 -1
- package/dist/middleware/pipeline.js.map +1 -1
- package/dist/middleware/tool-flow-gate.d.ts +20 -0
- package/dist/middleware/tool-flow-gate.d.ts.map +1 -1
- package/dist/middleware/tool-flow-gate.js +93 -0
- package/dist/middleware/tool-flow-gate.js.map +1 -1
- package/dist/mobile/ios-app.d.ts +18 -1
- package/dist/mobile/ios-app.d.ts.map +1 -1
- package/dist/mobile/ios-app.js +108 -8
- package/dist/mobile/ios-app.js.map +1 -1
- package/dist/orchestration/adversarial-cut.d.ts +85 -0
- package/dist/orchestration/adversarial-cut.d.ts.map +1 -0
- package/dist/orchestration/adversarial-cut.js +222 -0
- package/dist/orchestration/adversarial-cut.js.map +1 -0
- package/dist/orchestration/agent-kanban.d.ts +147 -0
- package/dist/orchestration/agent-kanban.d.ts.map +1 -0
- package/dist/orchestration/agent-kanban.js +414 -0
- package/dist/orchestration/agent-kanban.js.map +1 -0
- package/dist/orchestration/autonomous.d.ts +111 -0
- package/dist/orchestration/autonomous.d.ts.map +1 -1
- package/dist/orchestration/autonomous.js +183 -6
- package/dist/orchestration/autonomous.js.map +1 -1
- package/dist/orchestration/elo-tournament.d.ts +90 -0
- package/dist/orchestration/elo-tournament.d.ts.map +1 -0
- package/dist/orchestration/elo-tournament.js +203 -0
- package/dist/orchestration/elo-tournament.js.map +1 -0
- package/dist/orchestration/multi-model-arena.d.ts +138 -0
- package/dist/orchestration/multi-model-arena.d.ts.map +1 -0
- package/dist/orchestration/multi-model-arena.js +298 -0
- package/dist/orchestration/multi-model-arena.js.map +1 -0
- package/dist/orchestration/review-pipeline.d.ts +78 -0
- package/dist/orchestration/review-pipeline.d.ts.map +1 -0
- package/dist/orchestration/review-pipeline.js +202 -0
- package/dist/orchestration/review-pipeline.js.map +1 -0
- package/dist/orchestration/unified-state-thread.d.ts +186 -0
- package/dist/orchestration/unified-state-thread.d.ts.map +1 -0
- package/dist/orchestration/unified-state-thread.js +327 -0
- package/dist/orchestration/unified-state-thread.js.map +1 -0
- package/dist/plugins/manager.d.ts.map +1 -1
- package/dist/plugins/manager.js +2 -1
- package/dist/plugins/manager.js.map +1 -1
- package/dist/prompt/engine.d.ts +7 -0
- package/dist/prompt/engine.d.ts.map +1 -1
- package/dist/prompt/engine.js.map +1 -1
- package/dist/prompt/modules/caveman.d.ts +51 -0
- package/dist/prompt/modules/caveman.d.ts.map +1 -0
- package/dist/prompt/modules/caveman.js +97 -0
- package/dist/prompt/modules/caveman.js.map +1 -0
- package/dist/prompt/modules/index.d.ts +1 -0
- package/dist/prompt/modules/index.d.ts.map +1 -1
- package/dist/prompt/modules/index.js +3 -0
- package/dist/prompt/modules/index.js.map +1 -1
- package/dist/providers/account-pool.d.ts.map +1 -1
- package/dist/providers/account-pool.js +1 -0
- package/dist/providers/account-pool.js.map +1 -1
- package/dist/providers/anthropic-adapter.d.ts.map +1 -1
- package/dist/providers/anthropic-adapter.js +33 -0
- package/dist/providers/anthropic-adapter.js.map +1 -1
- package/dist/providers/cloud-offload/config-loader.d.ts +20 -0
- package/dist/providers/cloud-offload/config-loader.d.ts.map +1 -0
- package/dist/providers/cloud-offload/config-loader.js +148 -0
- package/dist/providers/cloud-offload/config-loader.js.map +1 -0
- package/dist/providers/codex-adapter.d.ts.map +1 -1
- package/dist/providers/codex-adapter.js +136 -4
- package/dist/providers/codex-adapter.js.map +1 -1
- package/dist/providers/discovery.d.ts.map +1 -1
- package/dist/providers/discovery.js +20 -0
- package/dist/providers/discovery.js.map +1 -1
- package/dist/providers/effort-cascade.d.ts +113 -0
- package/dist/providers/effort-cascade.d.ts.map +1 -0
- package/dist/providers/effort-cascade.js +215 -0
- package/dist/providers/effort-cascade.js.map +1 -0
- package/dist/providers/fallback-chain.d.ts.map +1 -1
- package/dist/providers/fallback-chain.js +1 -0
- package/dist/providers/fallback-chain.js.map +1 -1
- package/dist/providers/health-check.d.ts.map +1 -1
- package/dist/providers/health-check.js +8 -0
- package/dist/providers/health-check.js.map +1 -1
- package/dist/providers/model-defaults.d.ts +1 -1
- package/dist/providers/model-defaults.d.ts.map +1 -1
- package/dist/providers/model-defaults.js +13 -1
- package/dist/providers/model-defaults.js.map +1 -1
- package/dist/providers/model-discovery.d.ts.map +1 -1
- package/dist/providers/model-discovery.js +1 -0
- package/dist/providers/model-discovery.js.map +1 -1
- package/dist/providers/ollama-adapter.d.ts.map +1 -1
- package/dist/providers/ollama-adapter.js +21 -1
- package/dist/providers/ollama-adapter.js.map +1 -1
- package/dist/providers/openai-compat-adapter.d.ts.map +1 -1
- package/dist/providers/openai-compat-adapter.js +25 -0
- package/dist/providers/openai-compat-adapter.js.map +1 -1
- package/dist/providers/preset-library.d.ts.map +1 -1
- package/dist/providers/preset-library.js +9 -0
- package/dist/providers/preset-library.js.map +1 -1
- package/dist/providers/provider-service.d.ts.map +1 -1
- package/dist/providers/provider-service.js +40 -1
- package/dist/providers/provider-service.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +7 -0
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/sticky-rotation.d.ts +100 -0
- package/dist/providers/sticky-rotation.d.ts.map +1 -0
- package/dist/providers/sticky-rotation.js +134 -0
- package/dist/providers/sticky-rotation.js.map +1 -0
- package/dist/providers/tool-parsers/parsers.d.ts.map +1 -1
- package/dist/providers/tool-parsers/parsers.js +17 -4
- package/dist/providers/tool-parsers/parsers.js.map +1 -1
- package/dist/providers/types.d.ts +25 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/recipes/final-output.d.ts +74 -0
- package/dist/recipes/final-output.d.ts.map +1 -0
- package/dist/recipes/final-output.js +232 -0
- package/dist/recipes/final-output.js.map +1 -0
- package/dist/recipes/recipe-loader.d.ts.map +1 -1
- package/dist/recipes/recipe-loader.js +29 -0
- package/dist/recipes/recipe-loader.js.map +1 -1
- package/dist/recipes/recipe-runtime.d.ts.map +1 -1
- package/dist/recipes/recipe-runtime.js +31 -0
- package/dist/recipes/recipe-runtime.js.map +1 -1
- package/dist/recipes/recipe-types.d.ts +28 -0
- package/dist/recipes/recipe-types.d.ts.map +1 -1
- package/dist/recipes/sop-crystallizer.d.ts +90 -0
- package/dist/recipes/sop-crystallizer.d.ts.map +1 -0
- package/dist/recipes/sop-crystallizer.js +238 -0
- package/dist/recipes/sop-crystallizer.js.map +1 -0
- package/dist/recipes/sub-recipe-orchestrator.d.ts +126 -0
- package/dist/recipes/sub-recipe-orchestrator.d.ts.map +1 -0
- package/dist/recipes/sub-recipe-orchestrator.js +225 -0
- package/dist/recipes/sub-recipe-orchestrator.js.map +1 -0
- package/dist/sandbox/extended-backends.d.ts.map +1 -1
- package/dist/sandbox/extended-backends.js +7 -2
- package/dist/sandbox/extended-backends.js.map +1 -1
- package/dist/sandbox/seatbelt-policy.d.ts +132 -0
- package/dist/sandbox/seatbelt-policy.d.ts.map +1 -0
- package/dist/sandbox/seatbelt-policy.js +562 -0
- package/dist/sandbox/seatbelt-policy.js.map +1 -0
- package/dist/sandbox/terminal-backends.d.ts.map +1 -1
- package/dist/sandbox/terminal-backends.js +3 -1
- package/dist/sandbox/terminal-backends.js.map +1 -1
- package/dist/security/auto-mode-ruleset.d.ts +142 -0
- package/dist/security/auto-mode-ruleset.d.ts.map +1 -0
- package/dist/security/auto-mode-ruleset.js +264 -0
- package/dist/security/auto-mode-ruleset.js.map +1 -0
- package/dist/security/bash-arity-policy.d.ts +70 -0
- package/dist/security/bash-arity-policy.d.ts.map +1 -0
- package/dist/security/bash-arity-policy.js +189 -0
- package/dist/security/bash-arity-policy.js.map +1 -0
- package/dist/security/command-sanitizer.d.ts.map +1 -1
- package/dist/security/command-sanitizer.js +24 -0
- package/dist/security/command-sanitizer.js.map +1 -1
- package/dist/security/credential-broker.d.ts +136 -0
- package/dist/security/credential-broker.d.ts.map +1 -0
- package/dist/security/credential-broker.js +284 -0
- package/dist/security/credential-broker.js.map +1 -0
- package/dist/security/invisible-text-sanitizer.d.ts +114 -0
- package/dist/security/invisible-text-sanitizer.d.ts.map +1 -0
- package/dist/security/invisible-text-sanitizer.js +221 -0
- package/dist/security/invisible-text-sanitizer.js.map +1 -0
- package/dist/security/osv-check.d.ts +174 -0
- package/dist/security/osv-check.d.ts.map +1 -0
- package/dist/security/osv-check.js +409 -0
- package/dist/security/osv-check.js.map +1 -0
- package/dist/security/process-hardening.d.ts +114 -0
- package/dist/security/process-hardening.d.ts.map +1 -0
- package/dist/security/process-hardening.js +132 -0
- package/dist/security/process-hardening.js.map +1 -0
- package/dist/security/shell-escalation-tiers.d.ts +73 -0
- package/dist/security/shell-escalation-tiers.d.ts.map +1 -0
- package/dist/security/shell-escalation-tiers.js +439 -0
- package/dist/security/shell-escalation-tiers.js.map +1 -0
- package/dist/session/fork.d.ts +100 -0
- package/dist/session/fork.d.ts.map +1 -0
- package/dist/session/fork.js +223 -0
- package/dist/session/fork.js.map +1 -0
- package/dist/skills/activation-telemetry.d.ts +154 -0
- package/dist/skills/activation-telemetry.d.ts.map +1 -0
- package/dist/skills/activation-telemetry.js +274 -0
- package/dist/skills/activation-telemetry.js.map +1 -0
- package/dist/skills/agentskills-registry.d.ts +12 -1
- package/dist/skills/agentskills-registry.d.ts.map +1 -1
- package/dist/skills/agentskills-registry.js +11 -0
- package/dist/skills/agentskills-registry.js.map +1 -1
- package/dist/skills/description-optimizer.d.ts +101 -0
- package/dist/skills/description-optimizer.d.ts.map +1 -0
- package/dist/skills/description-optimizer.js +304 -0
- package/dist/skills/description-optimizer.js.map +1 -0
- package/dist/skills/history-deduper.d.ts +109 -0
- package/dist/skills/history-deduper.d.ts.map +1 -0
- package/dist/skills/history-deduper.js +222 -0
- package/dist/skills/history-deduper.js.map +1 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +2 -1
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/skill-source-adapter.d.ts +16 -0
- package/dist/skills/skill-source-adapter.d.ts.map +1 -1
- package/dist/skills/skill-source-adapter.js +0 -0
- package/dist/skills/skill-source-adapter.js.map +1 -1
- package/dist/skills/skill-standard.d.ts +29 -4
- package/dist/skills/skill-standard.d.ts.map +1 -1
- package/dist/skills/skill-standard.js +139 -66
- package/dist/skills/skill-standard.js.map +1 -1
- package/dist/skills/sop-completion-gate.d.ts +137 -0
- package/dist/skills/sop-completion-gate.d.ts.map +1 -0
- package/dist/skills/sop-completion-gate.js +159 -0
- package/dist/skills/sop-completion-gate.js.map +1 -0
- package/dist/skills/sop-template.d.ts +85 -0
- package/dist/skills/sop-template.d.ts.map +1 -0
- package/dist/skills/sop-template.js +106 -0
- package/dist/skills/sop-template.js.map +1 -0
- package/dist/skills/wotann-skills-registry.d.ts +1 -1
- package/dist/skills/wotann-skills-registry.d.ts.map +1 -1
- package/dist/skills/wotann-skills-registry.js +503 -0
- package/dist/skills/wotann-skills-registry.js.map +1 -1
- package/dist/snippets/prompt-corpus.d.ts +10 -0
- package/dist/snippets/prompt-corpus.d.ts.map +1 -0
- package/dist/snippets/prompt-corpus.js +158 -0
- package/dist/snippets/prompt-corpus.js.map +1 -0
- package/dist/snippets/snippet-store.d.ts +27 -0
- package/dist/snippets/snippet-store.d.ts.map +1 -1
- package/dist/snippets/snippet-store.js +109 -6
- package/dist/snippets/snippet-store.js.map +1 -1
- package/dist/storage/sqlite-node-backend.d.ts +47 -36
- package/dist/storage/sqlite-node-backend.d.ts.map +1 -1
- package/dist/storage/sqlite-node-backend.js +250 -94
- package/dist/storage/sqlite-node-backend.js.map +1 -1
- package/dist/testing/webapp-server-lifecycle.d.ts +74 -0
- package/dist/testing/webapp-server-lifecycle.d.ts.map +1 -0
- package/dist/testing/webapp-server-lifecycle.js +272 -0
- package/dist/testing/webapp-server-lifecycle.js.map +1 -0
- package/dist/tools/markitdown-bridge.d.ts +19 -0
- package/dist/tools/markitdown-bridge.d.ts.map +1 -1
- package/dist/tools/markitdown-bridge.js +167 -2
- package/dist/tools/markitdown-bridge.js.map +1 -1
- package/dist/tools/pdf-processor.d.ts.map +1 -1
- package/dist/tools/pdf-processor.js +7 -3
- package/dist/tools/pdf-processor.js.map +1 -1
- package/dist/tools/pptx-processor.d.ts +51 -0
- package/dist/tools/pptx-processor.d.ts.map +1 -0
- package/dist/tools/pptx-processor.js +334 -0
- package/dist/tools/pptx-processor.js.map +1 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +1661 -149
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/agent-tools.d.ts +60 -0
- package/dist/ui/agent-tools.d.ts.map +1 -0
- package/dist/ui/agent-tools.js +468 -0
- package/dist/ui/agent-tools.js.map +1 -0
- package/dist/ui/alt-buffer-mode.d.ts +68 -0
- package/dist/ui/alt-buffer-mode.d.ts.map +1 -0
- package/dist/ui/alt-buffer-mode.js +114 -0
- package/dist/ui/alt-buffer-mode.js.map +1 -0
- package/dist/ui/alt-buffer.d.ts +57 -10
- package/dist/ui/alt-buffer.d.ts.map +1 -1
- package/dist/ui/alt-buffer.js +54 -12
- package/dist/ui/alt-buffer.js.map +1 -1
- package/dist/ui/components/AgentStatusPanel.d.ts.map +1 -1
- package/dist/ui/components/AgentStatusPanel.js +14 -6
- package/dist/ui/components/AgentStatusPanel.js.map +1 -1
- package/dist/ui/components/AuditLogPanel.js +3 -3
- package/dist/ui/components/AuditLogPanel.js.map +1 -1
- package/dist/ui/components/AutomationsPanel.js +3 -3
- package/dist/ui/components/AutomationsPanel.js.map +1 -1
- package/dist/ui/components/ChatView.d.ts +9 -1
- package/dist/ui/components/ChatView.d.ts.map +1 -1
- package/dist/ui/components/ChatView.js +503 -14
- package/dist/ui/components/ChatView.js.map +1 -1
- package/dist/ui/components/CommandPaletteCommands.d.ts +2 -0
- package/dist/ui/components/CommandPaletteCommands.d.ts.map +1 -1
- package/dist/ui/components/CommandPaletteCommands.js +18 -2
- package/dist/ui/components/CommandPaletteCommands.js.map +1 -1
- package/dist/ui/components/ContextHUD.d.ts.map +1 -1
- package/dist/ui/components/ContextHUD.js +3 -3
- package/dist/ui/components/ContextHUD.js.map +1 -1
- package/dist/ui/components/DispatchInbox.js +3 -3
- package/dist/ui/components/DispatchInbox.js.map +1 -1
- package/dist/ui/components/GdprPanel.d.ts.map +1 -1
- package/dist/ui/components/GdprPanel.js +2 -3
- package/dist/ui/components/GdprPanel.js.map +1 -1
- package/dist/ui/components/HistoryPicker.js +3 -3
- package/dist/ui/components/HistoryPicker.js.map +1 -1
- package/dist/ui/components/ModelPicker.d.ts +9 -6
- package/dist/ui/components/ModelPicker.d.ts.map +1 -1
- package/dist/ui/components/ModelPicker.js +12 -9
- package/dist/ui/components/ModelPicker.js.map +1 -1
- package/dist/ui/components/OptionPicker.js +3 -3
- package/dist/ui/components/OptionPicker.js.map +1 -1
- package/dist/ui/components/PermissionPrompt.js +3 -3
- package/dist/ui/components/PermissionPrompt.js.map +1 -1
- package/dist/ui/components/PromptInput.d.ts.map +1 -1
- package/dist/ui/components/PromptInput.js +65 -10
- package/dist/ui/components/PromptInput.js.map +1 -1
- package/dist/ui/components/ProviderSetupOverlay.d.ts +19 -0
- package/dist/ui/components/ProviderSetupOverlay.d.ts.map +1 -0
- package/dist/ui/components/ProviderSetupOverlay.js +124 -0
- package/dist/ui/components/ProviderSetupOverlay.js.map +1 -0
- package/dist/ui/components/StartupScreen.d.ts.map +1 -1
- package/dist/ui/components/StartupScreen.js +10 -1
- package/dist/ui/components/StartupScreen.js.map +1 -1
- package/dist/ui/components/StatusBar.d.ts.map +1 -1
- package/dist/ui/components/StatusBar.js +3 -3
- package/dist/ui/components/StatusBar.js.map +1 -1
- package/dist/ui/components/TrustPanel.js +3 -3
- package/dist/ui/components/TrustPanel.js.map +1 -1
- package/dist/ui/components/UnifiedStatusBar.d.ts.map +1 -1
- package/dist/ui/components/UnifiedStatusBar.js +76 -5
- package/dist/ui/components/UnifiedStatusBar.js.map +1 -1
- package/dist/ui/computer-action-parser.d.ts +6 -0
- package/dist/ui/computer-action-parser.d.ts.map +1 -0
- package/dist/ui/computer-action-parser.js +119 -0
- package/dist/ui/computer-action-parser.js.map +1 -0
- package/dist/ui/helpers.d.ts +1 -1
- package/dist/ui/helpers.d.ts.map +1 -1
- package/dist/ui/helpers.js +1 -1
- package/dist/ui/helpers.js.map +1 -1
- package/dist/ui/keybindings.d.ts +12 -2
- package/dist/ui/keybindings.d.ts.map +1 -1
- package/dist/ui/keybindings.js +36 -4
- package/dist/ui/keybindings.js.map +1 -1
- package/dist/ui/markdown-stream.d.ts +99 -0
- package/dist/ui/markdown-stream.d.ts.map +1 -0
- package/dist/ui/markdown-stream.js +314 -0
- package/dist/ui/markdown-stream.js.map +1 -0
- package/dist/ui/terminal-keyboard-protocol.d.ts +23 -0
- package/dist/ui/terminal-keyboard-protocol.d.ts.map +1 -0
- package/dist/ui/terminal-keyboard-protocol.js +72 -0
- package/dist/ui/terminal-keyboard-protocol.js.map +1 -0
- package/dist/ui/theme/context.d.ts +39 -0
- package/dist/ui/theme/context.d.ts.map +1 -0
- package/dist/ui/theme/context.js +42 -0
- package/dist/ui/theme/context.js.map +1 -0
- package/dist/utils/platform.d.ts +115 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +146 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/verification/auto-verify-policy.d.ts +107 -0
- package/dist/verification/auto-verify-policy.d.ts.map +1 -0
- package/dist/verification/auto-verify-policy.js +309 -0
- package/dist/verification/auto-verify-policy.js.map +1 -0
- package/package.json +4 -1
- package/skills/anthropic-finance/audit-spreadsheet.md +165 -0
- package/skills/anthropic-finance/clean-data-xls.md +59 -0
- package/skills/anthropic-finance/competitive-analysis.md +288 -0
- package/skills/anthropic-finance/compliance-rules-engine.md +56 -0
- package/skills/anthropic-finance/dd-checklist.md +126 -0
- package/skills/anthropic-finance/independent-recompute.md +47 -0
- package/skills/anthropic-finance/reconcile-root-cause.md +48 -0
- package/skills/anthropic-finance/roll-forward.md +42 -0
- package/skills/anthropic-finance/untrusted-doc-parse.md +57 -0
- package/skills/anthropic-finance/variance-commentary.md +43 -0
- package/skills/doubt-driven-development.md +95 -0
- package/skills/mattpocock/diagnose.md +126 -0
- package/skills/mattpocock/grill-with-docs.md +97 -0
- package/skills/mattpocock/improve-codebase-architecture.md +80 -0
- package/skills/mattpocock/prototype.md +39 -0
- package/skills/mattpocock/review.md +87 -0
- package/skills/mattpocock/tdd.md +118 -0
- package/skills/mattpocock/zoom-out.md +17 -0
- package/skills/scientific/citation-management/SKILL.md +1113 -0
- package/skills/scientific/citation-management/assets/bibtex_template.bib +264 -0
- package/skills/scientific/citation-management/assets/citation_checklist.md +386 -0
- package/skills/scientific/citation-management/references/bibtex_formatting.md +908 -0
- package/skills/scientific/citation-management/references/citation_validation.md +794 -0
- package/skills/scientific/citation-management/references/google_scholar_search.md +725 -0
- package/skills/scientific/citation-management/references/metadata_extraction.md +870 -0
- package/skills/scientific/citation-management/references/pubmed_search.md +839 -0
- package/skills/scientific/citation-management/scripts/doi_to_bibtex.py +204 -0
- package/skills/scientific/citation-management/scripts/extract_metadata.py +569 -0
- package/skills/scientific/citation-management/scripts/format_bibtex.py +349 -0
- package/skills/scientific/citation-management/scripts/generate_schematic.py +139 -0
- package/skills/scientific/citation-management/scripts/generate_schematic_ai.py +817 -0
- package/skills/scientific/citation-management/scripts/search_google_scholar.py +282 -0
- package/skills/scientific/citation-management/scripts/search_pubmed.py +398 -0
- package/skills/scientific/citation-management/scripts/validate_citations.py +497 -0
- package/skills/scientific/database-lookup/SKILL.md +480 -0
- package/skills/scientific/database-lookup/references/addgene.md +38 -0
- package/skills/scientific/database-lookup/references/alphafold.md +40 -0
- package/skills/scientific/database-lookup/references/alphavantage.md +261 -0
- package/skills/scientific/database-lookup/references/bea.md +409 -0
- package/skills/scientific/database-lookup/references/bindingdb.md +85 -0
- package/skills/scientific/database-lookup/references/biogrid.md +110 -0
- package/skills/scientific/database-lookup/references/bls.md +235 -0
- package/skills/scientific/database-lookup/references/brenda.md +71 -0
- package/skills/scientific/database-lookup/references/cbioportal.md +206 -0
- package/skills/scientific/database-lookup/references/census.md +251 -0
- package/skills/scientific/database-lookup/references/chebi.md +103 -0
- package/skills/scientific/database-lookup/references/chembl.md +80 -0
- package/skills/scientific/database-lookup/references/clinicaltrials.md +77 -0
- package/skills/scientific/database-lookup/references/clinpgx.md +64 -0
- package/skills/scientific/database-lookup/references/clinvar.md +91 -0
- package/skills/scientific/database-lookup/references/cod.md +121 -0
- package/skills/scientific/database-lookup/references/cosmic.md +59 -0
- package/skills/scientific/database-lookup/references/dailymed.md +65 -0
- package/skills/scientific/database-lookup/references/datacommons.md +237 -0
- package/skills/scientific/database-lookup/references/dbsnp.md +143 -0
- package/skills/scientific/database-lookup/references/disgenet.md +52 -0
- package/skills/scientific/database-lookup/references/drugbank.md +54 -0
- package/skills/scientific/database-lookup/references/ecb.md +191 -0
- package/skills/scientific/database-lookup/references/emdb.md +37 -0
- package/skills/scientific/database-lookup/references/ena.md +372 -0
- package/skills/scientific/database-lookup/references/encode.md +47 -0
- package/skills/scientific/database-lookup/references/ensembl.md +539 -0
- package/skills/scientific/database-lookup/references/epa.md +232 -0
- package/skills/scientific/database-lookup/references/eurostat.md +237 -0
- package/skills/scientific/database-lookup/references/fda.md +64 -0
- package/skills/scientific/database-lookup/references/federal-reserve.md +216 -0
- package/skills/scientific/database-lookup/references/fred.md +297 -0
- package/skills/scientific/database-lookup/references/gene-ontology.md +147 -0
- package/skills/scientific/database-lookup/references/geo.md +130 -0
- package/skills/scientific/database-lookup/references/gnomad.md +93 -0
- package/skills/scientific/database-lookup/references/gtex.md +136 -0
- package/skills/scientific/database-lookup/references/gwas-catalog.md +46 -0
- package/skills/scientific/database-lookup/references/hca.md +35 -0
- package/skills/scientific/database-lookup/references/hpo.md +48 -0
- package/skills/scientific/database-lookup/references/human-protein-atlas.md +57 -0
- package/skills/scientific/database-lookup/references/interpro.md +120 -0
- package/skills/scientific/database-lookup/references/jaspar.md +50 -0
- package/skills/scientific/database-lookup/references/kegg.md +78 -0
- package/skills/scientific/database-lookup/references/lincs-l1000.md +68 -0
- package/skills/scientific/database-lookup/references/materials-project.md +123 -0
- package/skills/scientific/database-lookup/references/metabolomics-workbench.md +98 -0
- package/skills/scientific/database-lookup/references/monarch.md +46 -0
- package/skills/scientific/database-lookup/references/mousemine.md +40 -0
- package/skills/scientific/database-lookup/references/nasa-exoplanet-archive.md +112 -0
- package/skills/scientific/database-lookup/references/nasa.md +121 -0
- package/skills/scientific/database-lookup/references/ncbi-gene.md +64 -0
- package/skills/scientific/database-lookup/references/ncbi-protein.md +104 -0
- package/skills/scientific/database-lookup/references/ncbi-taxonomy.md +121 -0
- package/skills/scientific/database-lookup/references/nist.md +105 -0
- package/skills/scientific/database-lookup/references/noaa.md +199 -0
- package/skills/scientific/database-lookup/references/omim.md +114 -0
- package/skills/scientific/database-lookup/references/opentargets.md +459 -0
- package/skills/scientific/database-lookup/references/openweathermap.md +255 -0
- package/skills/scientific/database-lookup/references/pdb.md +121 -0
- package/skills/scientific/database-lookup/references/pride.md +74 -0
- package/skills/scientific/database-lookup/references/pubchem.md +145 -0
- package/skills/scientific/database-lookup/references/quickgo.md +45 -0
- package/skills/scientific/database-lookup/references/reactome.md +140 -0
- package/skills/scientific/database-lookup/references/rummageo.md +32 -0
- package/skills/scientific/database-lookup/references/sdss.md +130 -0
- package/skills/scientific/database-lookup/references/sec-edgar.md +315 -0
- package/skills/scientific/database-lookup/references/simbad.md +131 -0
- package/skills/scientific/database-lookup/references/sra.md +149 -0
- package/skills/scientific/database-lookup/references/string.md +283 -0
- package/skills/scientific/database-lookup/references/tcga-gdc.md +58 -0
- package/skills/scientific/database-lookup/references/treasury.md +215 -0
- package/skills/scientific/database-lookup/references/ucsc-genome.md +135 -0
- package/skills/scientific/database-lookup/references/uniprot.md +283 -0
- package/skills/scientific/database-lookup/references/usgs.md +260 -0
- package/skills/scientific/database-lookup/references/uspto.md +130 -0
- package/skills/scientific/database-lookup/references/who.md +283 -0
- package/skills/scientific/database-lookup/references/worldbank.md +239 -0
- package/skills/scientific/database-lookup/references/zinc.md +202 -0
- package/skills/scientific/hypothesis-generation/SKILL.md +297 -0
- package/skills/scientific/hypothesis-generation/assets/FORMATTING_GUIDE.md +672 -0
- package/skills/scientific/hypothesis-generation/assets/hypothesis_generation.sty +307 -0
- package/skills/scientific/hypothesis-generation/assets/hypothesis_report_template.tex +572 -0
- package/skills/scientific/hypothesis-generation/references/experimental_design_patterns.md +329 -0
- package/skills/scientific/hypothesis-generation/references/hypothesis_quality_criteria.md +198 -0
- package/skills/scientific/hypothesis-generation/references/literature_search_strategies.md +622 -0
- package/skills/scientific/hypothesis-generation/scripts/generate_schematic.py +139 -0
- package/skills/scientific/hypothesis-generation/scripts/generate_schematic_ai.py +817 -0
- package/skills/scientific/literature-review/SKILL.md +699 -0
- package/skills/scientific/literature-review/assets/review_template.md +412 -0
- package/skills/scientific/literature-review/references/citation_styles.md +166 -0
- package/skills/scientific/literature-review/references/database_strategies.md +455 -0
- package/skills/scientific/literature-review/scripts/generate_pdf.py +176 -0
- package/skills/scientific/literature-review/scripts/generate_schematic.py +139 -0
- package/skills/scientific/literature-review/scripts/generate_schematic_ai.py +817 -0
- package/skills/scientific/literature-review/scripts/search_databases.py +303 -0
- package/skills/scientific/literature-review/scripts/verify_citations.py +222 -0
- package/skills/scientific/markdown-mermaid-writing/SKILL.md +327 -0
- package/skills/scientific/markdown-mermaid-writing/assets/examples/example-research-report.md +221 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/architecture.md +108 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/block.md +177 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/c4.md +136 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/class.md +246 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/complex_examples.md +384 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/er.md +222 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/flowchart.md +177 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/gantt.md +138 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/git_graph.md +74 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/kanban.md +107 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/mindmap.md +74 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/packet.md +55 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/pie.md +52 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/quadrant.md +66 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/radar.md +59 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/requirement.md +88 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/sankey.md +71 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/sequence.md +174 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/state.md +150 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/timeline.md +96 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/treemap.md +66 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/user_journey.md +108 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/xy_chart.md +53 -0
- package/skills/scientific/markdown-mermaid-writing/references/diagrams/zenuml.md +71 -0
- package/skills/scientific/markdown-mermaid-writing/references/markdown_style_guide.md +733 -0
- package/skills/scientific/markdown-mermaid-writing/references/mermaid_style_guide.md +458 -0
- package/skills/scientific/markdown-mermaid-writing/templates/decision_record.md +211 -0
- package/skills/scientific/markdown-mermaid-writing/templates/how_to_guide.md +275 -0
- package/skills/scientific/markdown-mermaid-writing/templates/issue.md +303 -0
- package/skills/scientific/markdown-mermaid-writing/templates/kanban.md +223 -0
- package/skills/scientific/markdown-mermaid-writing/templates/presentation.md +312 -0
- package/skills/scientific/markdown-mermaid-writing/templates/project_documentation.md +412 -0
- package/skills/scientific/markdown-mermaid-writing/templates/pull_request.md +319 -0
- package/skills/scientific/markdown-mermaid-writing/templates/research_paper.md +304 -0
- package/skills/scientific/markdown-mermaid-writing/templates/status_report.md +185 -0
- package/skills/scientific/paper-lookup/SKILL.md +193 -0
- package/skills/scientific/paper-lookup/references/arxiv.md +161 -0
- package/skills/scientific/paper-lookup/references/biorxiv.md +118 -0
- package/skills/scientific/paper-lookup/references/core.md +150 -0
- package/skills/scientific/paper-lookup/references/crossref.md +181 -0
- package/skills/scientific/paper-lookup/references/medrxiv.md +104 -0
- package/skills/scientific/paper-lookup/references/openalex.md +174 -0
- package/skills/scientific/paper-lookup/references/pmc.md +152 -0
- package/skills/scientific/paper-lookup/references/pubmed.md +124 -0
- package/skills/scientific/paper-lookup/references/semantic-scholar.md +203 -0
- package/skills/scientific/paper-lookup/references/unpaywall.md +127 -0
- package/skills/scientific/peer-review/SKILL.md +569 -0
- package/skills/scientific/peer-review/references/common_issues.md +552 -0
- package/skills/scientific/peer-review/references/reporting_standards.md +290 -0
- package/skills/scientific/peer-review/scripts/generate_schematic.py +139 -0
- package/skills/scientific/peer-review/scripts/generate_schematic_ai.py +817 -0
- package/skills/scientific/scholar-evaluation/SKILL.md +298 -0
- package/skills/scientific/scholar-evaluation/references/evaluation_framework.md +663 -0
- package/skills/scientific/scholar-evaluation/scripts/calculate_scores.py +378 -0
- package/skills/scientific/scholar-evaluation/scripts/generate_schematic.py +139 -0
- package/skills/scientific/scholar-evaluation/scripts/generate_schematic_ai.py +817 -0
- package/skills/scientific/scientific-brainstorming/SKILL.md +189 -0
- package/skills/scientific/scientific-brainstorming/references/brainstorming_methods.md +326 -0
- package/skills/scientific/scientific-critical-thinking/SKILL.md +570 -0
- package/skills/scientific/scientific-critical-thinking/references/common_biases.md +364 -0
- package/skills/scientific/scientific-critical-thinking/references/evidence_hierarchy.md +484 -0
- package/skills/scientific/scientific-critical-thinking/references/experimental_design.md +496 -0
- package/skills/scientific/scientific-critical-thinking/references/logical_fallacies.md +478 -0
- package/skills/scientific/scientific-critical-thinking/references/scientific_method.md +169 -0
- package/skills/scientific/scientific-critical-thinking/references/statistical_pitfalls.md +506 -0
- package/skills/scientific/scientific-critical-thinking/scripts/generate_schematic.py +139 -0
- package/skills/scientific/scientific-critical-thinking/scripts/generate_schematic_ai.py +817 -0
- package/skills/wotann-imports/incremental-implementation.md +241 -0
- package/skills/wotann-imports/security-threat-model.md +81 -0
- package/skills/wotann-imports/triage.md +103 -0
- package/dist/build/deploy-targets/coolify.d.ts +0 -148
- package/dist/build/deploy-targets/coolify.d.ts.map +0 -1
- package/dist/build/deploy-targets/coolify.js +0 -339
- package/dist/build/deploy-targets/coolify.js.map +0 -1
- package/dist/build/deploy-targets/dokploy.d.ts +0 -139
- package/dist/build/deploy-targets/dokploy.d.ts.map +0 -1
- package/dist/build/deploy-targets/dokploy.js +0 -339
- package/dist/build/deploy-targets/dokploy.js.map +0 -1
- package/dist/claude/hardening/error-handler.d.ts +0 -52
- package/dist/claude/hardening/error-handler.d.ts.map +0 -1
- package/dist/claude/hardening/error-handler.js +0 -158
- package/dist/claude/hardening/error-handler.js.map +0 -1
- package/dist/cli/onboarding.d.ts +0 -34
- package/dist/cli/onboarding.d.ts.map +0 -1
- package/dist/cli/onboarding.js +0 -150
- package/dist/cli/onboarding.js.map +0 -1
- package/dist/core/agent-profiles.d.ts +0 -65
- package/dist/core/agent-profiles.d.ts.map +0 -1
- package/dist/core/agent-profiles.js +0 -137
- package/dist/core/agent-profiles.js.map +0 -1
- package/dist/desktop/supabase-relay.d.ts +0 -86
- package/dist/desktop/supabase-relay.d.ts.map +0 -1
- package/dist/desktop/supabase-relay.js +0 -335
- package/dist/desktop/supabase-relay.js.map +0 -1
- package/dist/intelligence/kg-builder.d.ts +0 -181
- package/dist/intelligence/kg-builder.d.ts.map +0 -1
- package/dist/intelligence/kg-builder.js +0 -807
- package/dist/intelligence/kg-builder.js.map +0 -1
- package/dist/orchestration/jean-orchestrator.d.ts +0 -79
- package/dist/orchestration/jean-orchestrator.d.ts.map +0 -1
- package/dist/orchestration/jean-orchestrator.js +0 -253
- package/dist/orchestration/jean-orchestrator.js.map +0 -1
- package/dist/orchestration/jean-registries/command-registry.d.ts +0 -85
- package/dist/orchestration/jean-registries/command-registry.d.ts.map +0 -1
- package/dist/orchestration/jean-registries/command-registry.js +0 -120
- package/dist/orchestration/jean-registries/command-registry.js.map +0 -1
- package/dist/orchestration/jean-registries/event-registry.d.ts +0 -80
- package/dist/orchestration/jean-registries/event-registry.d.ts.map +0 -1
- package/dist/orchestration/jean-registries/event-registry.js +0 -147
- package/dist/orchestration/jean-registries/event-registry.js.map +0 -1
- package/dist/orchestration/jean-registries/process-registry.d.ts +0 -71
- package/dist/orchestration/jean-registries/process-registry.d.ts.map +0 -1
- package/dist/orchestration/jean-registries/process-registry.js +0 -104
- package/dist/orchestration/jean-registries/process-registry.js.map +0 -1
- package/dist/orchestration/jean-registries/result-registry.d.ts +0 -71
- package/dist/orchestration/jean-registries/result-registry.d.ts.map +0 -1
- package/dist/orchestration/jean-registries/result-registry.js +0 -97
- package/dist/orchestration/jean-registries/result-registry.js.map +0 -1
- package/dist/providers/bedrock-signer.d.ts +0 -23
- package/dist/providers/bedrock-signer.d.ts.map +0 -1
- package/dist/providers/bedrock-signer.js +0 -439
- package/dist/providers/bedrock-signer.js.map +0 -1
- package/dist/providers/harness-profiles.d.ts +0 -70
- package/dist/providers/harness-profiles.d.ts.map +0 -1
- package/dist/providers/harness-profiles.js +0 -210
- package/dist/providers/harness-profiles.js.map +0 -1
- package/dist/providers/vertex-oauth.d.ts +0 -21
- package/dist/providers/vertex-oauth.d.ts.map +0 -1
- package/dist/providers/vertex-oauth.js +0 -393
- package/dist/providers/vertex-oauth.js.map +0 -1
- package/dist/sandbox/backends/cloud-auth.d.ts +0 -50
- package/dist/sandbox/backends/cloud-auth.d.ts.map +0 -1
- package/dist/sandbox/backends/cloud-auth.js +0 -93
- package/dist/sandbox/backends/cloud-auth.js.map +0 -1
- package/dist/security/anti-distillation.d.ts +0 -46
- package/dist/security/anti-distillation.d.ts.map +0 -1
- package/dist/security/anti-distillation.js +0 -358
- package/dist/security/anti-distillation.js.map +0 -1
- package/dist/security/multi-encoding-decoder.d.ts +0 -47
- package/dist/security/multi-encoding-decoder.d.ts.map +0 -1
- package/dist/security/multi-encoding-decoder.js +0 -336
- package/dist/security/multi-encoding-decoder.js.map +0 -1
- package/dist/ui/accessibility.d.ts +0 -157
- package/dist/ui/accessibility.d.ts.map +0 -1
- package/dist/ui/accessibility.js +0 -232
- package/dist/ui/accessibility.js.map +0 -1
- package/dist/ui/animations.d.ts +0 -102
- package/dist/ui/animations.d.ts.map +0 -1
- package/dist/ui/animations.js +0 -277
- package/dist/ui/animations.js.map +0 -1
- package/dist/ui/components/Sparkline.d.ts +0 -81
- package/dist/ui/components/Sparkline.d.ts.map +0 -1
- package/dist/ui/components/Sparkline.js +0 -102
- package/dist/ui/components/Sparkline.js.map +0 -1
- package/dist/ui/input/mouse.d.ts +0 -139
- package/dist/ui/input/mouse.d.ts.map +0 -1
- package/dist/ui/input/mouse.js +0 -239
- package/dist/ui/input/mouse.js.map +0 -1
- package/dist/ui/sound.d.ts +0 -85
- package/dist/ui/sound.d.ts.map +0 -1
- package/dist/ui/sound.js +0 -126
- 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:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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" ||
|
|
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+
|
|
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 `
|
|
406
|
-
//
|
|
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+
|
|
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
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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
|
-
"│ /
|
|
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]
|
|
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+
|
|
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+
|
|
900
|
-
"│ Ctrl+R Search history Ctrl+
|
|
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
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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+
|
|
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
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
//
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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+
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(`
|
|
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 = ["
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
"
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
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
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
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
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
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
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
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:
|
|
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
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
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
|