muonroi-cli 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +380 -0
- package/dist/__test-stubs__/ee-server.d.ts +27 -0
- package/dist/__test-stubs__/ee-server.js +138 -0
- package/dist/__test-stubs__/ee-server.js.map +1 -0
- package/dist/billing/index.d.ts +5 -0
- package/dist/billing/index.js +2 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/cloud/index.d.ts +5 -0
- package/dist/cloud/index.js +2 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/daemon/scheduler.d.ts +15 -0
- package/dist/daemon/scheduler.js +126 -0
- package/dist/daemon/scheduler.js.map +1 -0
- package/dist/daemon/scheduler.test.d.ts +1 -0
- package/dist/daemon/scheduler.test.js +103 -0
- package/dist/daemon/scheduler.test.js.map +1 -0
- package/dist/ee/__tests__/pipeline.integration.test.d.ts +14 -0
- package/dist/ee/__tests__/pipeline.integration.test.js +151 -0
- package/dist/ee/__tests__/pipeline.integration.test.js.map +1 -0
- package/dist/ee/auth.d.ts +19 -0
- package/dist/ee/auth.js +48 -0
- package/dist/ee/auth.js.map +1 -0
- package/dist/ee/auth.test.d.ts +1 -0
- package/dist/ee/auth.test.js +59 -0
- package/dist/ee/auth.test.js.map +1 -0
- package/dist/ee/bridge.d.ts +68 -0
- package/dist/ee/bridge.js +177 -0
- package/dist/ee/bridge.js.map +1 -0
- package/dist/ee/bridge.test.d.ts +1 -0
- package/dist/ee/bridge.test.js +231 -0
- package/dist/ee/bridge.test.js.map +1 -0
- package/dist/ee/client.d.ts +22 -0
- package/dist/ee/client.js +464 -0
- package/dist/ee/client.js.map +1 -0
- package/dist/ee/client.test.d.ts +1 -0
- package/dist/ee/client.test.js +151 -0
- package/dist/ee/client.test.js.map +1 -0
- package/dist/ee/embedding-cache.d.ts +7 -0
- package/dist/ee/embedding-cache.js +33 -0
- package/dist/ee/embedding-cache.js.map +1 -0
- package/dist/ee/extract-session.d.ts +17 -0
- package/dist/ee/extract-session.js +53 -0
- package/dist/ee/extract-session.js.map +1 -0
- package/dist/ee/extract-session.test.d.ts +1 -0
- package/dist/ee/extract-session.test.js +197 -0
- package/dist/ee/extract-session.test.js.map +1 -0
- package/dist/ee/health.d.ts +29 -0
- package/dist/ee/health.js +64 -0
- package/dist/ee/health.js.map +1 -0
- package/dist/ee/index.d.ts +10 -0
- package/dist/ee/index.js +9 -0
- package/dist/ee/index.js.map +1 -0
- package/dist/ee/intercept.d.ts +46 -0
- package/dist/ee/intercept.js +117 -0
- package/dist/ee/intercept.js.map +1 -0
- package/dist/ee/intercept.test.d.ts +1 -0
- package/dist/ee/intercept.test.js +170 -0
- package/dist/ee/intercept.test.js.map +1 -0
- package/dist/ee/judge.d.ts +36 -0
- package/dist/ee/judge.js +56 -0
- package/dist/ee/judge.js.map +1 -0
- package/dist/ee/judge.test.d.ts +1 -0
- package/dist/ee/judge.test.js +139 -0
- package/dist/ee/judge.test.js.map +1 -0
- package/dist/ee/offline-queue.d.ts +37 -0
- package/dist/ee/offline-queue.js +163 -0
- package/dist/ee/offline-queue.js.map +1 -0
- package/dist/ee/offline-queue.test.d.ts +1 -0
- package/dist/ee/offline-queue.test.js +246 -0
- package/dist/ee/offline-queue.test.js.map +1 -0
- package/dist/ee/posttool.d.ts +11 -0
- package/dist/ee/posttool.js +16 -0
- package/dist/ee/posttool.js.map +1 -0
- package/dist/ee/posttool.test.d.ts +1 -0
- package/dist/ee/posttool.test.js +70 -0
- package/dist/ee/posttool.test.js.map +1 -0
- package/dist/ee/prompt-stale.d.ts +20 -0
- package/dist/ee/prompt-stale.js +37 -0
- package/dist/ee/prompt-stale.js.map +1 -0
- package/dist/ee/prompt-stale.test.d.ts +8 -0
- package/dist/ee/prompt-stale.test.js +76 -0
- package/dist/ee/prompt-stale.test.js.map +1 -0
- package/dist/ee/render.d.ts +20 -0
- package/dist/ee/render.js +30 -0
- package/dist/ee/render.js.map +1 -0
- package/dist/ee/render.test.d.ts +1 -0
- package/dist/ee/render.test.js +61 -0
- package/dist/ee/render.test.js.map +1 -0
- package/dist/ee/scope.d.ts +6 -0
- package/dist/ee/scope.js +92 -0
- package/dist/ee/scope.js.map +1 -0
- package/dist/ee/scope.test.d.ts +1 -0
- package/dist/ee/scope.test.js +97 -0
- package/dist/ee/scope.test.js.map +1 -0
- package/dist/ee/tenant.d.ts +2 -0
- package/dist/ee/tenant.js +13 -0
- package/dist/ee/tenant.js.map +1 -0
- package/dist/ee/touch.test.d.ts +1 -0
- package/dist/ee/touch.test.js +60 -0
- package/dist/ee/touch.test.js.map +1 -0
- package/dist/ee/types.d.ts +300 -0
- package/dist/ee/types.js +14 -0
- package/dist/ee/types.js.map +1 -0
- package/dist/flow/__tests__/migration.test.d.ts +1 -0
- package/dist/flow/__tests__/migration.test.js +105 -0
- package/dist/flow/__tests__/migration.test.js.map +1 -0
- package/dist/flow/__tests__/parser.test.d.ts +1 -0
- package/dist/flow/__tests__/parser.test.js +68 -0
- package/dist/flow/__tests__/parser.test.js.map +1 -0
- package/dist/flow/__tests__/run-manager.test.d.ts +1 -0
- package/dist/flow/__tests__/run-manager.test.js +83 -0
- package/dist/flow/__tests__/run-manager.test.js.map +1 -0
- package/dist/flow/__tests__/scaffold.test.d.ts +1 -0
- package/dist/flow/__tests__/scaffold.test.js +47 -0
- package/dist/flow/__tests__/scaffold.test.js.map +1 -0
- package/dist/flow/__tests__/warning-persist.test.d.ts +5 -0
- package/dist/flow/__tests__/warning-persist.test.js +96 -0
- package/dist/flow/__tests__/warning-persist.test.js.map +1 -0
- package/dist/flow/artifact-io.d.ts +16 -0
- package/dist/flow/artifact-io.js +35 -0
- package/dist/flow/artifact-io.js.map +1 -0
- package/dist/flow/compaction/__tests__/compress.test.d.ts +1 -0
- package/dist/flow/compaction/__tests__/compress.test.js +63 -0
- package/dist/flow/compaction/__tests__/compress.test.js.map +1 -0
- package/dist/flow/compaction/__tests__/extract.test.d.ts +1 -0
- package/dist/flow/compaction/__tests__/extract.test.js +66 -0
- package/dist/flow/compaction/__tests__/extract.test.js.map +1 -0
- package/dist/flow/compaction/__tests__/preserve.test.d.ts +1 -0
- package/dist/flow/compaction/__tests__/preserve.test.js +62 -0
- package/dist/flow/compaction/__tests__/preserve.test.js.map +1 -0
- package/dist/flow/compaction/compress.d.ts +22 -0
- package/dist/flow/compaction/compress.js +47 -0
- package/dist/flow/compaction/compress.js.map +1 -0
- package/dist/flow/compaction/extract.d.ts +23 -0
- package/dist/flow/compaction/extract.js +46 -0
- package/dist/flow/compaction/extract.js.map +1 -0
- package/dist/flow/compaction/index.d.ts +23 -0
- package/dist/flow/compaction/index.js +63 -0
- package/dist/flow/compaction/index.js.map +1 -0
- package/dist/flow/compaction/preserve.d.ts +24 -0
- package/dist/flow/compaction/preserve.js +35 -0
- package/dist/flow/compaction/preserve.js.map +1 -0
- package/dist/flow/index.d.ts +12 -0
- package/dist/flow/index.js +16 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/flow/migration.d.ts +23 -0
- package/dist/flow/migration.js +126 -0
- package/dist/flow/migration.js.map +1 -0
- package/dist/flow/parser.d.ts +27 -0
- package/dist/flow/parser.js +68 -0
- package/dist/flow/parser.js.map +1 -0
- package/dist/flow/run-manager.d.ts +36 -0
- package/dist/flow/run-manager.js +110 -0
- package/dist/flow/run-manager.js.map +1 -0
- package/dist/flow/scaffold.d.ts +22 -0
- package/dist/flow/scaffold.js +47 -0
- package/dist/flow/scaffold.js.map +1 -0
- package/dist/flow/warning-persist.d.ts +9 -0
- package/dist/flow/warning-persist.js +75 -0
- package/dist/flow/warning-persist.js.map +1 -0
- package/dist/gsd/__tests__/types.test.d.ts +1 -0
- package/dist/gsd/__tests__/types.test.js +65 -0
- package/dist/gsd/__tests__/types.test.js.map +1 -0
- package/dist/gsd/index.d.ts +1 -0
- package/dist/gsd/index.js +2 -0
- package/dist/gsd/index.js.map +1 -0
- package/dist/gsd/types.d.ts +4 -0
- package/dist/gsd/types.js +39 -0
- package/dist/gsd/types.js.map +1 -0
- package/dist/headless/output.d.ts +68 -0
- package/dist/headless/output.js +206 -0
- package/dist/headless/output.js.map +1 -0
- package/dist/headless/output.test.d.ts +1 -0
- package/dist/headless/output.test.js +178 -0
- package/dist/headless/output.test.js.map +1 -0
- package/dist/hooks/config.d.ts +18 -0
- package/dist/hooks/config.js +39 -0
- package/dist/hooks/config.js.map +1 -0
- package/dist/hooks/index.d.ts +44 -0
- package/dist/hooks/index.js +187 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/types.d.ts +130 -0
- package/dist/hooks/types.js +47 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +512 -0
- package/dist/index.js.map +1 -0
- package/dist/lsp/builtins.d.ts +9 -0
- package/dist/lsp/builtins.js +351 -0
- package/dist/lsp/builtins.js.map +1 -0
- package/dist/lsp/builtins.test.d.ts +1 -0
- package/dist/lsp/builtins.test.js +91 -0
- package/dist/lsp/builtins.test.js.map +1 -0
- package/dist/lsp/client.d.ts +20 -0
- package/dist/lsp/client.js +290 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/lsp/manager.d.ts +20 -0
- package/dist/lsp/manager.js +235 -0
- package/dist/lsp/manager.js.map +1 -0
- package/dist/lsp/manager.test.d.ts +1 -0
- package/dist/lsp/manager.test.js +133 -0
- package/dist/lsp/manager.test.js.map +1 -0
- package/dist/lsp/npm-cache.d.ts +2 -0
- package/dist/lsp/npm-cache.js +105 -0
- package/dist/lsp/npm-cache.js.map +1 -0
- package/dist/lsp/npm-cache.test.d.ts +1 -0
- package/dist/lsp/npm-cache.test.js +53 -0
- package/dist/lsp/npm-cache.test.js.map +1 -0
- package/dist/lsp/runtime.d.ts +6 -0
- package/dist/lsp/runtime.js +53 -0
- package/dist/lsp/runtime.js.map +1 -0
- package/dist/lsp/smoke.test.d.ts +1 -0
- package/dist/lsp/smoke.test.js +56 -0
- package/dist/lsp/smoke.test.js.map +1 -0
- package/dist/lsp/types.d.ts +83 -0
- package/dist/lsp/types.js +12 -0
- package/dist/lsp/types.js.map +1 -0
- package/dist/mcp/auto-setup.d.ts +2 -0
- package/dist/mcp/auto-setup.js +76 -0
- package/dist/mcp/auto-setup.js.map +1 -0
- package/dist/mcp/catalog.d.ts +10 -0
- package/dist/mcp/catalog.js +121 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/oauth-callback.d.ts +9 -0
- package/dist/mcp/oauth-callback.js +49 -0
- package/dist/mcp/oauth-callback.js.map +1 -0
- package/dist/mcp/oauth-provider.d.ts +33 -0
- package/dist/mcp/oauth-provider.js +94 -0
- package/dist/mcp/oauth-provider.js.map +1 -0
- package/dist/mcp/parse-headers.d.ts +2 -0
- package/dist/mcp/parse-headers.js +33 -0
- package/dist/mcp/parse-headers.js.map +1 -0
- package/dist/mcp/parse-headers.test.d.ts +1 -0
- package/dist/mcp/parse-headers.test.js +43 -0
- package/dist/mcp/parse-headers.test.js.map +1 -0
- package/dist/mcp/runtime.d.ts +11 -0
- package/dist/mcp/runtime.js +81 -0
- package/dist/mcp/runtime.js.map +1 -0
- package/dist/mcp/smoke.test.d.ts +1 -0
- package/dist/mcp/smoke.test.js +159 -0
- package/dist/mcp/smoke.test.js.map +1 -0
- package/dist/mcp/validate.d.ts +9 -0
- package/dist/mcp/validate.js +39 -0
- package/dist/mcp/validate.js.map +1 -0
- package/dist/models/__tests__/registry.test.d.ts +1 -0
- package/dist/models/__tests__/registry.test.js +74 -0
- package/dist/models/__tests__/registry.test.js.map +1 -0
- package/dist/models/catalog-client.d.ts +21 -0
- package/dist/models/catalog-client.js +53 -0
- package/dist/models/catalog-client.js.map +1 -0
- package/dist/models/classify-tier.d.ts +2 -0
- package/dist/models/classify-tier.js +34 -0
- package/dist/models/classify-tier.js.map +1 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/registry.d.ts +19 -0
- package/dist/models/registry.js +65 -0
- package/dist/models/registry.js.map +1 -0
- package/dist/ops/bug-report.d.ts +28 -0
- package/dist/ops/bug-report.js +67 -0
- package/dist/ops/bug-report.js.map +1 -0
- package/dist/ops/bug-report.test.d.ts +1 -0
- package/dist/ops/bug-report.test.js +148 -0
- package/dist/ops/bug-report.test.js.map +1 -0
- package/dist/ops/doctor.d.ts +16 -0
- package/dist/ops/doctor.js +162 -0
- package/dist/ops/doctor.js.map +1 -0
- package/dist/ops/doctor.test.d.ts +1 -0
- package/dist/ops/doctor.test.js +95 -0
- package/dist/ops/doctor.test.js.map +1 -0
- package/dist/orchestrator/__tests__/flow-resume.test.d.ts +5 -0
- package/dist/orchestrator/__tests__/flow-resume.test.js +61 -0
- package/dist/orchestrator/__tests__/flow-resume.test.js.map +1 -0
- package/dist/orchestrator/__tests__/route-feedback.test.d.ts +1 -0
- package/dist/orchestrator/__tests__/route-feedback.test.js +47 -0
- package/dist/orchestrator/__tests__/route-feedback.test.js.map +1 -0
- package/dist/orchestrator/abort.d.ts +29 -0
- package/dist/orchestrator/abort.js +31 -0
- package/dist/orchestrator/abort.js.map +1 -0
- package/dist/orchestrator/abort.test.d.ts +1 -0
- package/dist/orchestrator/abort.test.js +34 -0
- package/dist/orchestrator/abort.test.js.map +1 -0
- package/dist/orchestrator/agent.test.d.ts +1 -0
- package/dist/orchestrator/agent.test.js +126 -0
- package/dist/orchestrator/agent.test.js.map +1 -0
- package/dist/orchestrator/cleanup.test.d.ts +1 -0
- package/dist/orchestrator/cleanup.test.js +67 -0
- package/dist/orchestrator/cleanup.test.js.map +1 -0
- package/dist/orchestrator/compaction.d.ts +36 -0
- package/dist/orchestrator/compaction.js +375 -0
- package/dist/orchestrator/compaction.js.map +1 -0
- package/dist/orchestrator/compaction.test.d.ts +1 -0
- package/dist/orchestrator/compaction.test.js +105 -0
- package/dist/orchestrator/compaction.test.js.map +1 -0
- package/dist/orchestrator/delegations.d.ts +49 -0
- package/dist/orchestrator/delegations.js +283 -0
- package/dist/orchestrator/delegations.js.map +1 -0
- package/dist/orchestrator/delegations.test.d.ts +1 -0
- package/dist/orchestrator/delegations.test.js +107 -0
- package/dist/orchestrator/delegations.test.js.map +1 -0
- package/dist/orchestrator/flow-resume.d.ts +25 -0
- package/dist/orchestrator/flow-resume.js +53 -0
- package/dist/orchestrator/flow-resume.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +239 -0
- package/dist/orchestrator/orchestrator.js +3209 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/pending-calls.d.ts +75 -0
- package/dist/orchestrator/pending-calls.js +178 -0
- package/dist/orchestrator/pending-calls.js.map +1 -0
- package/dist/orchestrator/pending-calls.test.d.ts +1 -0
- package/dist/orchestrator/pending-calls.test.js +188 -0
- package/dist/orchestrator/pending-calls.test.js.map +1 -0
- package/dist/orchestrator/reasoning.d.ts +3 -0
- package/dist/orchestrator/reasoning.js +53 -0
- package/dist/orchestrator/reasoning.js.map +1 -0
- package/dist/orchestrator/reasoning.test.d.ts +1 -0
- package/dist/orchestrator/reasoning.test.js +26 -0
- package/dist/orchestrator/reasoning.test.js.map +1 -0
- package/dist/orchestrator/sandbox.test.d.ts +1 -0
- package/dist/orchestrator/sandbox.test.js +94 -0
- package/dist/orchestrator/sandbox.test.js.map +1 -0
- package/dist/pil/__tests__/budget.test.d.ts +1 -0
- package/dist/pil/__tests__/budget.test.js +33 -0
- package/dist/pil/__tests__/budget.test.js.map +1 -0
- package/dist/pil/__tests__/layer1-intent.test.d.ts +1 -0
- package/dist/pil/__tests__/layer1-intent.test.js +199 -0
- package/dist/pil/__tests__/layer1-intent.test.js.map +1 -0
- package/dist/pil/__tests__/layer2-personality.test.d.ts +1 -0
- package/dist/pil/__tests__/layer2-personality.test.js +57 -0
- package/dist/pil/__tests__/layer2-personality.test.js.map +1 -0
- package/dist/pil/__tests__/layer3-ee-injection.test.d.ts +1 -0
- package/dist/pil/__tests__/layer3-ee-injection.test.js +96 -0
- package/dist/pil/__tests__/layer3-ee-injection.test.js.map +1 -0
- package/dist/pil/__tests__/layer4-gsd.test.d.ts +1 -0
- package/dist/pil/__tests__/layer4-gsd.test.js +68 -0
- package/dist/pil/__tests__/layer4-gsd.test.js.map +1 -0
- package/dist/pil/__tests__/layer5-context.test.d.ts +1 -0
- package/dist/pil/__tests__/layer5-context.test.js +100 -0
- package/dist/pil/__tests__/layer5-context.test.js.map +1 -0
- package/dist/pil/__tests__/layer6-output.test.d.ts +1 -0
- package/dist/pil/__tests__/layer6-output.test.js +115 -0
- package/dist/pil/__tests__/layer6-output.test.js.map +1 -0
- package/dist/pil/__tests__/ollama-classify.test.d.ts +1 -0
- package/dist/pil/__tests__/ollama-classify.test.js +68 -0
- package/dist/pil/__tests__/ollama-classify.test.js.map +1 -0
- package/dist/pil/__tests__/orchestrator-integration.test.d.ts +10 -0
- package/dist/pil/__tests__/orchestrator-integration.test.js +98 -0
- package/dist/pil/__tests__/orchestrator-integration.test.js.map +1 -0
- package/dist/pil/__tests__/pipeline.test.d.ts +1 -0
- package/dist/pil/__tests__/pipeline.test.js +150 -0
- package/dist/pil/__tests__/pipeline.test.js.map +1 -0
- package/dist/pil/__tests__/response-tools.test.d.ts +1 -0
- package/dist/pil/__tests__/response-tools.test.js +133 -0
- package/dist/pil/__tests__/response-tools.test.js.map +1 -0
- package/dist/pil/__tests__/schema.test.d.ts +1 -0
- package/dist/pil/__tests__/schema.test.js +112 -0
- package/dist/pil/__tests__/schema.test.js.map +1 -0
- package/dist/pil/__tests__/store.test.d.ts +1 -0
- package/dist/pil/__tests__/store.test.js +44 -0
- package/dist/pil/__tests__/store.test.js.map +1 -0
- package/dist/pil/__tests__/task-tier-map.test.d.ts +1 -0
- package/dist/pil/__tests__/task-tier-map.test.js +33 -0
- package/dist/pil/__tests__/task-tier-map.test.js.map +1 -0
- package/dist/pil/budget.d.ts +8 -0
- package/dist/pil/budget.js +17 -0
- package/dist/pil/budget.js.map +1 -0
- package/dist/pil/index.d.ts +11 -0
- package/dist/pil/index.js +11 -0
- package/dist/pil/index.js.map +1 -0
- package/dist/pil/layer1-intent.d.ts +13 -0
- package/dist/pil/layer1-intent.js +142 -0
- package/dist/pil/layer1-intent.js.map +1 -0
- package/dist/pil/layer2-personality.d.ts +2 -0
- package/dist/pil/layer2-personality.js +29 -0
- package/dist/pil/layer2-personality.js.map +1 -0
- package/dist/pil/layer3-ee-injection.d.ts +10 -0
- package/dist/pil/layer3-ee-injection.js +72 -0
- package/dist/pil/layer3-ee-injection.js.map +1 -0
- package/dist/pil/layer4-gsd.d.ts +2 -0
- package/dist/pil/layer4-gsd.js +64 -0
- package/dist/pil/layer4-gsd.js.map +1 -0
- package/dist/pil/layer5-context.d.ts +2 -0
- package/dist/pil/layer5-context.js +124 -0
- package/dist/pil/layer5-context.js.map +1 -0
- package/dist/pil/layer6-output.d.ts +18 -0
- package/dist/pil/layer6-output.js +105 -0
- package/dist/pil/layer6-output.js.map +1 -0
- package/dist/pil/ollama-classify.d.ts +13 -0
- package/dist/pil/ollama-classify.js +38 -0
- package/dist/pil/ollama-classify.js.map +1 -0
- package/dist/pil/pipeline.d.ts +16 -0
- package/dist/pil/pipeline.js +96 -0
- package/dist/pil/pipeline.js.map +1 -0
- package/dist/pil/response-tools.d.ts +63 -0
- package/dist/pil/response-tools.js +87 -0
- package/dist/pil/response-tools.js.map +1 -0
- package/dist/pil/schema.d.ts +80 -0
- package/dist/pil/schema.js +43 -0
- package/dist/pil/schema.js.map +1 -0
- package/dist/pil/store.d.ts +12 -0
- package/dist/pil/store.js +21 -0
- package/dist/pil/store.js.map +1 -0
- package/dist/pil/task-tier-map.d.ts +30 -0
- package/dist/pil/task-tier-map.js +83 -0
- package/dist/pil/task-tier-map.js.map +1 -0
- package/dist/pil/timeout.d.ts +7 -0
- package/dist/pil/timeout.js +10 -0
- package/dist/pil/timeout.js.map +1 -0
- package/dist/pil/types.d.ts +39 -0
- package/dist/pil/types.js +7 -0
- package/dist/pil/types.js.map +1 -0
- package/dist/providers/__test-utils__/load-fixture.d.ts +9 -0
- package/dist/providers/__test-utils__/load-fixture.js +34 -0
- package/dist/providers/__test-utils__/load-fixture.js.map +1 -0
- package/dist/providers/__tests__/runtime-integration.test.d.ts +1 -0
- package/dist/providers/__tests__/runtime-integration.test.js +71 -0
- package/dist/providers/__tests__/runtime-integration.test.js.map +1 -0
- package/dist/providers/__tests__/runtime.test.d.ts +1 -0
- package/dist/providers/__tests__/runtime.test.js +76 -0
- package/dist/providers/__tests__/runtime.test.js.map +1 -0
- package/dist/providers/adapter.d.ts +15 -0
- package/dist/providers/adapter.js +45 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/adapter.test.d.ts +1 -0
- package/dist/providers/adapter.test.js +19 -0
- package/dist/providers/adapter.test.js.map +1 -0
- package/dist/providers/anthropic.d.ts +53 -0
- package/dist/providers/anthropic.js +141 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/errors.d.ts +18 -0
- package/dist/providers/errors.js +36 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/errors.test.d.ts +1 -0
- package/dist/providers/errors.test.js +66 -0
- package/dist/providers/errors.test.js.map +1 -0
- package/dist/providers/gemini.d.ts +11 -0
- package/dist/providers/gemini.js +33 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/gemini.test.d.ts +1 -0
- package/dist/providers/gemini.test.js +37 -0
- package/dist/providers/gemini.test.js.map +1 -0
- package/dist/providers/index.d.ts +11 -0
- package/dist/providers/index.js +15 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/keychain.d.ts +32 -0
- package/dist/providers/keychain.js +127 -0
- package/dist/providers/keychain.js.map +1 -0
- package/dist/providers/keychain.test.d.ts +1 -0
- package/dist/providers/keychain.test.js +80 -0
- package/dist/providers/keychain.test.js.map +1 -0
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/providers/ollama.js +33 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/ollama.test.d.ts +1 -0
- package/dist/providers/ollama.test.js +37 -0
- package/dist/providers/ollama.test.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +15 -0
- package/dist/providers/openai-compatible.js +45 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/openai-compatible.test.d.ts +1 -0
- package/dist/providers/openai-compatible.test.js +53 -0
- package/dist/providers/openai-compatible.test.js.map +1 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +33 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openai.test.d.ts +1 -0
- package/dist/providers/openai.test.js +53 -0
- package/dist/providers/openai.test.js.map +1 -0
- package/dist/providers/patch-zod-schema.d.ts +24 -0
- package/dist/providers/patch-zod-schema.js +121 -0
- package/dist/providers/patch-zod-schema.js.map +1 -0
- package/dist/providers/pricing.d.ts +21 -0
- package/dist/providers/pricing.js +50 -0
- package/dist/providers/pricing.js.map +1 -0
- package/dist/providers/pricing.test.d.ts +1 -0
- package/dist/providers/pricing.test.js +47 -0
- package/dist/providers/pricing.test.js.map +1 -0
- package/dist/providers/runtime.d.ts +21 -0
- package/dist/providers/runtime.js +83 -0
- package/dist/providers/runtime.js.map +1 -0
- package/dist/providers/stream-loop.d.ts +16 -0
- package/dist/providers/stream-loop.js +63 -0
- package/dist/providers/stream-loop.js.map +1 -0
- package/dist/providers/types.d.ts +106 -0
- package/dist/providers/types.js +12 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/vision-proxy.d.ts +23 -0
- package/dist/providers/vision-proxy.js +152 -0
- package/dist/providers/vision-proxy.js.map +1 -0
- package/dist/providers/vision-proxy.test.d.ts +1 -0
- package/dist/providers/vision-proxy.test.js +136 -0
- package/dist/providers/vision-proxy.test.js.map +1 -0
- package/dist/router/classifier/grammars.d.ts +3 -0
- package/dist/router/classifier/grammars.js +14 -0
- package/dist/router/classifier/grammars.js.map +1 -0
- package/dist/router/classifier/index.d.ts +3 -0
- package/dist/router/classifier/index.js +19 -0
- package/dist/router/classifier/index.js.map +1 -0
- package/dist/router/classifier/index.test.d.ts +1 -0
- package/dist/router/classifier/index.test.js +29 -0
- package/dist/router/classifier/index.test.js.map +1 -0
- package/dist/router/classifier/regex.d.ts +2 -0
- package/dist/router/classifier/regex.js +45 -0
- package/dist/router/classifier/regex.js.map +1 -0
- package/dist/router/classifier/regex.test.d.ts +1 -0
- package/dist/router/classifier/regex.test.js +42 -0
- package/dist/router/classifier/regex.test.js.map +1 -0
- package/dist/router/classifier/tree-sitter.d.ts +5 -0
- package/dist/router/classifier/tree-sitter.js +84 -0
- package/dist/router/classifier/tree-sitter.js.map +1 -0
- package/dist/router/classifier/tree-sitter.test.d.ts +1 -0
- package/dist/router/classifier/tree-sitter.test.js +23 -0
- package/dist/router/classifier/tree-sitter.test.js.map +1 -0
- package/dist/router/cold.d.ts +7 -0
- package/dist/router/cold.js +21 -0
- package/dist/router/cold.js.map +1 -0
- package/dist/router/cold.test.d.ts +1 -0
- package/dist/router/cold.test.js +56 -0
- package/dist/router/cold.test.js.map +1 -0
- package/dist/router/decide.d.ts +42 -0
- package/dist/router/decide.js +300 -0
- package/dist/router/decide.js.map +1 -0
- package/dist/router/decide.test.d.ts +1 -0
- package/dist/router/decide.test.js +116 -0
- package/dist/router/decide.test.js.map +1 -0
- package/dist/router/health.d.ts +10 -0
- package/dist/router/health.js +44 -0
- package/dist/router/health.js.map +1 -0
- package/dist/router/health.test.d.ts +1 -0
- package/dist/router/health.test.js +52 -0
- package/dist/router/health.test.js.map +1 -0
- package/dist/router/store.d.ts +24 -0
- package/dist/router/store.js +27 -0
- package/dist/router/store.js.map +1 -0
- package/dist/router/types.d.ts +18 -0
- package/dist/router/types.js +2 -0
- package/dist/router/types.js.map +1 -0
- package/dist/router/warm.d.ts +7 -0
- package/dist/router/warm.js +40 -0
- package/dist/router/warm.js.map +1 -0
- package/dist/router/warm.test.d.ts +1 -0
- package/dist/router/warm.test.js +144 -0
- package/dist/router/warm.test.js.map +1 -0
- package/dist/storage/__tests__/migrations.test.d.ts +12 -0
- package/dist/storage/__tests__/migrations.test.js +357 -0
- package/dist/storage/__tests__/migrations.test.js.map +1 -0
- package/dist/storage/atomic-io.d.ts +18 -0
- package/dist/storage/atomic-io.js +65 -0
- package/dist/storage/atomic-io.js.map +1 -0
- package/dist/storage/atomic-io.test.d.ts +1 -0
- package/dist/storage/atomic-io.test.js +49 -0
- package/dist/storage/atomic-io.test.js.map +1 -0
- package/dist/storage/config.d.ts +22 -0
- package/dist/storage/config.js +35 -0
- package/dist/storage/config.js.map +1 -0
- package/dist/storage/config.test.d.ts +1 -0
- package/dist/storage/config.test.js +29 -0
- package/dist/storage/config.test.js.map +1 -0
- package/dist/storage/db.d.ts +18 -0
- package/dist/storage/db.js +71 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/migrations.d.ts +2 -0
- package/dist/storage/migrations.js +138 -0
- package/dist/storage/migrations.js.map +1 -0
- package/dist/storage/session-dir.d.ts +27 -0
- package/dist/storage/session-dir.js +36 -0
- package/dist/storage/session-dir.js.map +1 -0
- package/dist/storage/sessions.d.ts +16 -0
- package/dist/storage/sessions.js +142 -0
- package/dist/storage/sessions.js.map +1 -0
- package/dist/storage/tool-results.d.ts +4 -0
- package/dist/storage/tool-results.js +50 -0
- package/dist/storage/tool-results.js.map +1 -0
- package/dist/storage/transcript-view.d.ts +14 -0
- package/dist/storage/transcript-view.js +23 -0
- package/dist/storage/transcript-view.js.map +1 -0
- package/dist/storage/transcript.d.ts +12 -0
- package/dist/storage/transcript.js +269 -0
- package/dist/storage/transcript.js.map +1 -0
- package/dist/storage/transcript.test.d.ts +1 -0
- package/dist/storage/transcript.test.js +22 -0
- package/dist/storage/transcript.test.js.map +1 -0
- package/dist/storage/usage-cap.d.ts +34 -0
- package/dist/storage/usage-cap.js +54 -0
- package/dist/storage/usage-cap.js.map +1 -0
- package/dist/storage/usage-cap.test.d.ts +1 -0
- package/dist/storage/usage-cap.test.js +51 -0
- package/dist/storage/usage-cap.test.js.map +1 -0
- package/dist/storage/usage.d.ts +11 -0
- package/dist/storage/usage.js +65 -0
- package/dist/storage/usage.js.map +1 -0
- package/dist/storage/workspaces.d.ts +9 -0
- package/dist/storage/workspaces.js +60 -0
- package/dist/storage/workspaces.js.map +1 -0
- package/dist/tools/bash.d.ts +46 -0
- package/dist/tools/bash.js +541 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/bash.test.d.ts +1 -0
- package/dist/tools/bash.test.js +240 -0
- package/dist/tools/bash.test.js.map +1 -0
- package/dist/tools/computer.d.ts +88 -0
- package/dist/tools/computer.js +443 -0
- package/dist/tools/computer.js.map +1 -0
- package/dist/tools/computer.test.d.ts +1 -0
- package/dist/tools/computer.test.js +142 -0
- package/dist/tools/computer.test.js.map +1 -0
- package/dist/tools/file.d.ts +17 -0
- package/dist/tools/file.js +101 -0
- package/dist/tools/file.js.map +1 -0
- package/dist/tools/file.test.d.ts +1 -0
- package/dist/tools/file.test.js +58 -0
- package/dist/tools/file.test.js.map +1 -0
- package/dist/tools/grep.d.ts +8 -0
- package/dist/tools/grep.js +144 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/registry.d.ts +19 -0
- package/dist/tools/registry.js +245 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/schedule.d.ts +85 -0
- package/dist/tools/schedule.js +498 -0
- package/dist/tools/schedule.js.map +1 -0
- package/dist/tools/schedule.test.d.ts +1 -0
- package/dist/tools/schedule.test.js +118 -0
- package/dist/tools/schedule.test.js.map +1 -0
- package/dist/types/index.d.ts +279 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/agents-modal.d.ts +36 -0
- package/dist/ui/agents-modal.js +54 -0
- package/dist/ui/agents-modal.js.map +1 -0
- package/dist/ui/app.d.ts +31 -0
- package/dist/ui/app.js +3957 -0
- package/dist/ui/app.js.map +1 -0
- package/dist/ui/components/SuggestionOverlay.d.ts +6 -0
- package/dist/ui/components/SuggestionOverlay.js +12 -0
- package/dist/ui/components/SuggestionOverlay.js.map +1 -0
- package/dist/ui/components/btw-overlay.d.ts +11 -0
- package/dist/ui/components/btw-overlay.js +16 -0
- package/dist/ui/components/btw-overlay.js.map +1 -0
- package/dist/ui/hooks/useTypeahead.d.ts +18 -0
- package/dist/ui/hooks/useTypeahead.js +111 -0
- package/dist/ui/hooks/useTypeahead.js.map +1 -0
- package/dist/ui/markdown.d.ts +5 -0
- package/dist/ui/markdown.js +38 -0
- package/dist/ui/markdown.js.map +1 -0
- package/dist/ui/mcp-modal-types.d.ts +24 -0
- package/dist/ui/mcp-modal-types.js +13 -0
- package/dist/ui/mcp-modal-types.js.map +1 -0
- package/dist/ui/mcp-modal.d.ts +33 -0
- package/dist/ui/mcp-modal.js +94 -0
- package/dist/ui/mcp-modal.js.map +1 -0
- package/dist/ui/plan.d.ts +24 -0
- package/dist/ui/plan.js +122 -0
- package/dist/ui/plan.js.map +1 -0
- package/dist/ui/schedule-modal.d.ts +15 -0
- package/dist/ui/schedule-modal.js +36 -0
- package/dist/ui/schedule-modal.js.map +1 -0
- package/dist/ui/slash/__tests__/clear.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/clear.test.js +58 -0
- package/dist/ui/slash/__tests__/clear.test.js.map +1 -0
- package/dist/ui/slash/__tests__/compact.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/compact.test.js +43 -0
- package/dist/ui/slash/__tests__/compact.test.js.map +1 -0
- package/dist/ui/slash/__tests__/cost.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/cost.test.js +54 -0
- package/dist/ui/slash/__tests__/cost.test.js.map +1 -0
- package/dist/ui/slash/__tests__/discuss.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/discuss.test.js +83 -0
- package/dist/ui/slash/__tests__/discuss.test.js.map +1 -0
- package/dist/ui/slash/__tests__/execute.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/execute.test.js +71 -0
- package/dist/ui/slash/__tests__/execute.test.js.map +1 -0
- package/dist/ui/slash/__tests__/expand.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/expand.test.js +67 -0
- package/dist/ui/slash/__tests__/expand.test.js.map +1 -0
- package/dist/ui/slash/__tests__/optimize.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/optimize.test.js +130 -0
- package/dist/ui/slash/__tests__/optimize.test.js.map +1 -0
- package/dist/ui/slash/__tests__/plan.test.d.ts +1 -0
- package/dist/ui/slash/__tests__/plan.test.js +79 -0
- package/dist/ui/slash/__tests__/plan.test.js.map +1 -0
- package/dist/ui/slash/clear.d.ts +11 -0
- package/dist/ui/slash/clear.js +77 -0
- package/dist/ui/slash/clear.js.map +1 -0
- package/dist/ui/slash/compact.d.ts +11 -0
- package/dist/ui/slash/compact.js +40 -0
- package/dist/ui/slash/compact.js.map +1 -0
- package/dist/ui/slash/cost.d.ts +11 -0
- package/dist/ui/slash/cost.js +55 -0
- package/dist/ui/slash/cost.js.map +1 -0
- package/dist/ui/slash/council.d.ts +2 -0
- package/dist/ui/slash/council.js +22 -0
- package/dist/ui/slash/council.js.map +1 -0
- package/dist/ui/slash/debug.d.ts +45 -0
- package/dist/ui/slash/debug.js +111 -0
- package/dist/ui/slash/debug.js.map +1 -0
- package/dist/ui/slash/discuss.d.ts +11 -0
- package/dist/ui/slash/discuss.js +59 -0
- package/dist/ui/slash/discuss.js.map +1 -0
- package/dist/ui/slash/ee.d.ts +10 -0
- package/dist/ui/slash/ee.js +247 -0
- package/dist/ui/slash/ee.js.map +1 -0
- package/dist/ui/slash/execute.d.ts +12 -0
- package/dist/ui/slash/execute.js +36 -0
- package/dist/ui/slash/execute.js.map +1 -0
- package/dist/ui/slash/expand.d.ts +11 -0
- package/dist/ui/slash/expand.js +42 -0
- package/dist/ui/slash/expand.js.map +1 -0
- package/dist/ui/slash/optimize.d.ts +12 -0
- package/dist/ui/slash/optimize.js +37 -0
- package/dist/ui/slash/optimize.js.map +1 -0
- package/dist/ui/slash/plan.d.ts +11 -0
- package/dist/ui/slash/plan.js +48 -0
- package/dist/ui/slash/plan.js.map +1 -0
- package/dist/ui/slash/registry.d.ts +23 -0
- package/dist/ui/slash/registry.js +26 -0
- package/dist/ui/slash/registry.js.map +1 -0
- package/dist/ui/slash/route.d.ts +12 -0
- package/dist/ui/slash/route.js +35 -0
- package/dist/ui/slash/route.js.map +1 -0
- package/dist/ui/slash/route.test.d.ts +1 -0
- package/dist/ui/slash/route.test.js +70 -0
- package/dist/ui/slash/route.test.js.map +1 -0
- package/dist/ui/status-bar/index.d.ts +14 -0
- package/dist/ui/status-bar/index.js +74 -0
- package/dist/ui/status-bar/index.js.map +1 -0
- package/dist/ui/status-bar/index.test.d.ts +1 -0
- package/dist/ui/status-bar/index.test.js +80 -0
- package/dist/ui/status-bar/index.test.js.map +1 -0
- package/dist/ui/status-bar/store.d.ts +38 -0
- package/dist/ui/status-bar/store.js +144 -0
- package/dist/ui/status-bar/store.js.map +1 -0
- package/dist/ui/status-bar/store.test.d.ts +1 -0
- package/dist/ui/status-bar/store.test.js +116 -0
- package/dist/ui/status-bar/store.test.js.map +1 -0
- package/dist/ui/status-bar/tier-badge.d.ts +11 -0
- package/dist/ui/status-bar/tier-badge.js +19 -0
- package/dist/ui/status-bar/tier-badge.js.map +1 -0
- package/dist/ui/status-bar/tier-badge.test.d.ts +1 -0
- package/dist/ui/status-bar/tier-badge.test.js +34 -0
- package/dist/ui/status-bar/tier-badge.test.js.map +1 -0
- package/dist/ui/status-bar/usd-meter.d.ts +13 -0
- package/dist/ui/status-bar/usd-meter.js +13 -0
- package/dist/ui/status-bar/usd-meter.js.map +1 -0
- package/dist/ui/status-bar/usd-meter.test.d.ts +1 -0
- package/dist/ui/status-bar/usd-meter.test.js +32 -0
- package/dist/ui/status-bar/usd-meter.test.js.map +1 -0
- package/dist/ui/terminal-selection-text.d.ts +23 -0
- package/dist/ui/terminal-selection-text.js +59 -0
- package/dist/ui/terminal-selection-text.js.map +1 -0
- package/dist/ui/theme.d.ts +53 -0
- package/dist/ui/theme.js +53 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/usage/downgrade.d.ts +37 -0
- package/dist/usage/downgrade.js +49 -0
- package/dist/usage/downgrade.js.map +1 -0
- package/dist/usage/downgrade.test.d.ts +1 -0
- package/dist/usage/downgrade.test.js +67 -0
- package/dist/usage/downgrade.test.js.map +1 -0
- package/dist/usage/estimator.d.ts +17 -0
- package/dist/usage/estimator.js +28 -0
- package/dist/usage/estimator.js.map +1 -0
- package/dist/usage/estimator.test.d.ts +1 -0
- package/dist/usage/estimator.test.js +38 -0
- package/dist/usage/estimator.test.js.map +1 -0
- package/dist/usage/ledger.d.ts +42 -0
- package/dist/usage/ledger.js +181 -0
- package/dist/usage/ledger.js.map +1 -0
- package/dist/usage/ledger.test.d.ts +1 -0
- package/dist/usage/ledger.test.js +171 -0
- package/dist/usage/ledger.test.js.map +1 -0
- package/dist/usage/midstream.d.ts +24 -0
- package/dist/usage/midstream.js +45 -0
- package/dist/usage/midstream.js.map +1 -0
- package/dist/usage/midstream.test.d.ts +1 -0
- package/dist/usage/midstream.test.js +47 -0
- package/dist/usage/midstream.test.js.map +1 -0
- package/dist/usage/thresholds.d.ts +38 -0
- package/dist/usage/thresholds.js +54 -0
- package/dist/usage/thresholds.js.map +1 -0
- package/dist/usage/thresholds.test.d.ts +1 -0
- package/dist/usage/thresholds.test.js +77 -0
- package/dist/usage/thresholds.test.js.map +1 -0
- package/dist/usage/types.d.ts +30 -0
- package/dist/usage/types.js +21 -0
- package/dist/usage/types.js.map +1 -0
- package/dist/utils/at-mentions.d.ts +12 -0
- package/dist/utils/at-mentions.js +92 -0
- package/dist/utils/at-mentions.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +13 -0
- package/dist/utils/clipboard-image.js +121 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/file-index.d.ts +13 -0
- package/dist/utils/file-index.js +143 -0
- package/dist/utils/file-index.js.map +1 -0
- package/dist/utils/git-root.d.ts +1 -0
- package/dist/utils/git-root.js +16 -0
- package/dist/utils/git-root.js.map +1 -0
- package/dist/utils/host-clipboard.d.ts +4 -0
- package/dist/utils/host-clipboard.js +32 -0
- package/dist/utils/host-clipboard.js.map +1 -0
- package/dist/utils/install-manager.d.ts +56 -0
- package/dist/utils/install-manager.js +340 -0
- package/dist/utils/install-manager.js.map +1 -0
- package/dist/utils/install-manager.test.d.ts +1 -0
- package/dist/utils/install-manager.test.js +127 -0
- package/dist/utils/install-manager.test.js.map +1 -0
- package/dist/utils/instructions.d.ts +3 -0
- package/dist/utils/instructions.js +107 -0
- package/dist/utils/instructions.js.map +1 -0
- package/dist/utils/instructions.test.d.ts +1 -0
- package/dist/utils/instructions.test.js +68 -0
- package/dist/utils/instructions.test.js.map +1 -0
- package/dist/utils/permission-mode.d.ts +24 -0
- package/dist/utils/permission-mode.js +29 -0
- package/dist/utils/permission-mode.js.map +1 -0
- package/dist/utils/permission-mode.test.d.ts +1 -0
- package/dist/utils/permission-mode.test.js +97 -0
- package/dist/utils/permission-mode.test.js.map +1 -0
- package/dist/utils/redactor.d.ts +57 -0
- package/dist/utils/redactor.js +179 -0
- package/dist/utils/redactor.js.map +1 -0
- package/dist/utils/redactor.test.d.ts +1 -0
- package/dist/utils/redactor.test.js +84 -0
- package/dist/utils/redactor.test.js.map +1 -0
- package/dist/utils/settings.d.ts +179 -0
- package/dist/utils/settings.js +579 -0
- package/dist/utils/settings.js.map +1 -0
- package/dist/utils/settings.test.d.ts +1 -0
- package/dist/utils/settings.test.js +160 -0
- package/dist/utils/settings.test.js.map +1 -0
- package/dist/utils/side-question.d.ts +10 -0
- package/dist/utils/side-question.js +24 -0
- package/dist/utils/side-question.js.map +1 -0
- package/dist/utils/skills.d.ts +20 -0
- package/dist/utils/skills.js +194 -0
- package/dist/utils/skills.js.map +1 -0
- package/dist/utils/skills.test.d.ts +1 -0
- package/dist/utils/skills.test.js +45 -0
- package/dist/utils/skills.test.js.map +1 -0
- package/dist/utils/subagent-display.d.ts +1 -0
- package/dist/utils/subagent-display.js +20 -0
- package/dist/utils/subagent-display.js.map +1 -0
- package/dist/utils/subagent-display.test.d.ts +1 -0
- package/dist/utils/subagent-display.test.js +21 -0
- package/dist/utils/subagent-display.test.js.map +1 -0
- package/dist/utils/subagents-settings.test.d.ts +1 -0
- package/dist/utils/subagents-settings.test.js +58 -0
- package/dist/utils/subagents-settings.test.js.map +1 -0
- package/dist/utils/telegram-audio-settings.test.d.ts +1 -0
- package/dist/utils/telegram-audio-settings.test.js +39 -0
- package/dist/utils/telegram-audio-settings.test.js.map +1 -0
- package/dist/utils/update-checker.d.ts +11 -0
- package/dist/utils/update-checker.js +22 -0
- package/dist/utils/update-checker.js.map +1 -0
- package/dist/utils/update-checker.test.d.ts +1 -0
- package/dist/utils/update-checker.test.js +125 -0
- package/dist/utils/update-checker.test.js.map +1 -0
- package/dist/verify/checkpoint.d.ts +11 -0
- package/dist/verify/checkpoint.js +202 -0
- package/dist/verify/checkpoint.js.map +1 -0
- package/dist/verify/checkpoint.test.d.ts +1 -0
- package/dist/verify/checkpoint.test.js +160 -0
- package/dist/verify/checkpoint.test.js.map +1 -0
- package/dist/verify/entrypoint.d.ts +30 -0
- package/dist/verify/entrypoint.js +349 -0
- package/dist/verify/entrypoint.js.map +1 -0
- package/dist/verify/entrypoint.test.d.ts +1 -0
- package/dist/verify/entrypoint.test.js +233 -0
- package/dist/verify/entrypoint.test.js.map +1 -0
- package/dist/verify/environment.d.ts +9 -0
- package/dist/verify/environment.js +116 -0
- package/dist/verify/environment.js.map +1 -0
- package/dist/verify/environment.test.d.ts +1 -0
- package/dist/verify/environment.test.js +94 -0
- package/dist/verify/environment.test.js.map +1 -0
- package/dist/verify/evidence.d.ts +10 -0
- package/dist/verify/evidence.js +94 -0
- package/dist/verify/evidence.js.map +1 -0
- package/dist/verify/orchestrator.d.ts +25 -0
- package/dist/verify/orchestrator.js +87 -0
- package/dist/verify/orchestrator.js.map +1 -0
- package/dist/verify/orchestrator.test.d.ts +1 -0
- package/dist/verify/orchestrator.test.js +126 -0
- package/dist/verify/orchestrator.test.js.map +1 -0
- package/dist/verify/recipes.d.ts +20 -0
- package/dist/verify/recipes.js +451 -0
- package/dist/verify/recipes.js.map +1 -0
- package/dist/verify/retry.d.ts +4 -0
- package/dist/verify/retry.js +50 -0
- package/dist/verify/retry.js.map +1 -0
- package/dist/verify/runtime-prep.test.d.ts +1 -0
- package/dist/verify/runtime-prep.test.js +38 -0
- package/dist/verify/runtime-prep.test.js.map +1 -0
- package/package.json +93 -0
- package/src/__test-stubs__/ee-server.ts +173 -0
- package/src/billing/index.ts +5 -0
- package/src/bun-sqlite.d.ts +15 -0
- package/src/cloud/index.ts +5 -0
- package/src/daemon/scheduler.test.ts +128 -0
- package/src/daemon/scheduler.ts +152 -0
- package/src/ee/.gitkeep +0 -0
- package/src/ee/__tests__/pipeline.integration.test.ts +193 -0
- package/src/ee/auth.test.ts +76 -0
- package/src/ee/auth.ts +59 -0
- package/src/ee/bridge.test.ts +290 -0
- package/src/ee/bridge.ts +254 -0
- package/src/ee/client.test.ts +171 -0
- package/src/ee/client.ts +512 -0
- package/src/ee/embedding-cache.ts +42 -0
- package/src/ee/extract-session.test.ts +236 -0
- package/src/ee/extract-session.ts +69 -0
- package/src/ee/health.ts +83 -0
- package/src/ee/index.ts +32 -0
- package/src/ee/intercept.test.ts +191 -0
- package/src/ee/intercept.ts +132 -0
- package/src/ee/judge.test.ts +157 -0
- package/src/ee/judge.ts +68 -0
- package/src/ee/offline-queue.test.ts +351 -0
- package/src/ee/offline-queue.ts +212 -0
- package/src/ee/posttool.test.ts +81 -0
- package/src/ee/posttool.ts +16 -0
- package/src/ee/prompt-stale.test.ts +87 -0
- package/src/ee/prompt-stale.ts +39 -0
- package/src/ee/render.test.ts +71 -0
- package/src/ee/render.ts +47 -0
- package/src/ee/scope.test.ts +112 -0
- package/src/ee/scope.ts +93 -0
- package/src/ee/tenant.ts +14 -0
- package/src/ee/touch.test.ts +71 -0
- package/src/ee/types.ts +322 -0
- package/src/flow/.gitkeep +0 -0
- package/src/flow/__tests__/migration.test.ts +133 -0
- package/src/flow/__tests__/parser.test.ts +77 -0
- package/src/flow/__tests__/run-manager.test.ts +95 -0
- package/src/flow/__tests__/scaffold.test.ts +57 -0
- package/src/flow/__tests__/warning-persist.test.ts +112 -0
- package/src/flow/artifact-io.ts +41 -0
- package/src/flow/compaction/__tests__/compress.test.ts +69 -0
- package/src/flow/compaction/__tests__/extract.test.ts +74 -0
- package/src/flow/compaction/__tests__/preserve.test.ts +69 -0
- package/src/flow/compaction/compress.ts +67 -0
- package/src/flow/compaction/extract.ts +60 -0
- package/src/flow/compaction/index.ts +86 -0
- package/src/flow/compaction/preserve.ts +48 -0
- package/src/flow/index.ts +18 -0
- package/src/flow/migration.ts +139 -0
- package/src/flow/parser.ts +78 -0
- package/src/flow/run-manager.ts +129 -0
- package/src/flow/scaffold.ts +52 -0
- package/src/flow/warning-persist.ts +84 -0
- package/src/gsd/.gitkeep +0 -0
- package/src/gsd/__tests__/types.test.ts +77 -0
- package/src/gsd/index.ts +1 -0
- package/src/gsd/types.ts +47 -0
- package/src/headless/output.test.ts +201 -0
- package/src/headless/output.ts +312 -0
- package/src/hooks/config.ts +41 -0
- package/src/hooks/index.ts +250 -0
- package/src/hooks/types.ts +221 -0
- package/src/index.ts +655 -0
- package/src/lsp/builtins.test.ts +104 -0
- package/src/lsp/builtins.ts +409 -0
- package/src/lsp/client.ts +342 -0
- package/src/lsp/manager.test.ts +164 -0
- package/src/lsp/manager.ts +293 -0
- package/src/lsp/npm-cache.test.ts +68 -0
- package/src/lsp/npm-cache.ts +108 -0
- package/src/lsp/runtime.ts +70 -0
- package/src/lsp/smoke.test.ts +72 -0
- package/src/lsp/types.ts +116 -0
- package/src/mcp/auto-setup.ts +80 -0
- package/src/mcp/catalog.ts +138 -0
- package/src/mcp/oauth-callback.ts +63 -0
- package/src/mcp/oauth-provider.ts +130 -0
- package/src/mcp/parse-headers.test.ts +54 -0
- package/src/mcp/parse-headers.ts +35 -0
- package/src/mcp/runtime.ts +113 -0
- package/src/mcp/smoke.test.ts +170 -0
- package/src/mcp/validate.ts +48 -0
- package/src/models/__tests__/registry.test.ts +95 -0
- package/src/models/catalog-client.ts +83 -0
- package/src/models/catalog.json +314 -0
- package/src/models/classify-tier.ts +37 -0
- package/src/models/index.ts +9 -0
- package/src/models/registry.ts +73 -0
- package/src/ops/bug-report.test.ts +172 -0
- package/src/ops/bug-report.ts +80 -0
- package/src/ops/doctor.test.ts +107 -0
- package/src/ops/doctor.ts +172 -0
- package/src/orchestrator/__tests__/flow-resume.test.ts +71 -0
- package/src/orchestrator/__tests__/route-feedback.test.ts +55 -0
- package/src/orchestrator/abort.test.ts +37 -0
- package/src/orchestrator/abort.ts +51 -0
- package/src/orchestrator/agent.test.ts +138 -0
- package/src/orchestrator/cleanup.test.ts +88 -0
- package/src/orchestrator/compaction.test.ts +134 -0
- package/src/orchestrator/compaction.ts +487 -0
- package/src/orchestrator/delegations.test.ts +145 -0
- package/src/orchestrator/delegations.ts +364 -0
- package/src/orchestrator/flow-resume.ts +55 -0
- package/src/orchestrator/orchestrator.ts +3965 -0
- package/src/orchestrator/pending-calls.test.ts +226 -0
- package/src/orchestrator/pending-calls.ts +240 -0
- package/src/orchestrator/reasoning.test.ts +29 -0
- package/src/orchestrator/reasoning.ts +64 -0
- package/src/orchestrator/sandbox.test.ts +115 -0
- package/src/pil/__tests__/budget.test.ts +39 -0
- package/src/pil/__tests__/layer1-intent.test.ts +243 -0
- package/src/pil/__tests__/layer2-personality.test.ts +63 -0
- package/src/pil/__tests__/layer3-ee-injection.test.ts +117 -0
- package/src/pil/__tests__/layer4-gsd.test.ts +76 -0
- package/src/pil/__tests__/layer5-context.test.ts +116 -0
- package/src/pil/__tests__/layer6-output.test.ts +139 -0
- package/src/pil/__tests__/ollama-classify.test.ts +81 -0
- package/src/pil/__tests__/orchestrator-integration.test.ts +107 -0
- package/src/pil/__tests__/pipeline.test.ts +173 -0
- package/src/pil/__tests__/response-tools.test.ts +165 -0
- package/src/pil/__tests__/schema.test.ts +128 -0
- package/src/pil/__tests__/store.test.ts +49 -0
- package/src/pil/__tests__/task-tier-map.test.ts +41 -0
- package/src/pil/budget.ts +18 -0
- package/src/pil/index.ts +12 -0
- package/src/pil/layer1-intent.ts +155 -0
- package/src/pil/layer2-personality.ts +37 -0
- package/src/pil/layer3-ee-injection.ts +79 -0
- package/src/pil/layer4-gsd.ts +77 -0
- package/src/pil/layer5-context.ts +125 -0
- package/src/pil/layer6-output.ts +113 -0
- package/src/pil/ollama-classify.ts +49 -0
- package/src/pil/pipeline.ts +112 -0
- package/src/pil/response-tools.ts +110 -0
- package/src/pil/schema.ts +49 -0
- package/src/pil/store.ts +29 -0
- package/src/pil/task-tier-map.ts +90 -0
- package/src/pil/timeout.ts +10 -0
- package/src/pil/types.ts +42 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/__test-utils__/load-fixture.ts +36 -0
- package/src/providers/__tests__/runtime-integration.test.ts +80 -0
- package/src/providers/__tests__/runtime.test.ts +86 -0
- package/src/providers/adapter.test.ts +21 -0
- package/src/providers/adapter.ts +48 -0
- package/src/providers/anthropic.ts +169 -0
- package/src/providers/errors.test.ts +76 -0
- package/src/providers/errors.ts +46 -0
- package/src/providers/gemini.test.ts +46 -0
- package/src/providers/gemini.ts +37 -0
- package/src/providers/index.ts +32 -0
- package/src/providers/keychain.test.ts +95 -0
- package/src/providers/keychain.ts +133 -0
- package/src/providers/ollama.test.ts +46 -0
- package/src/providers/ollama.ts +36 -0
- package/src/providers/openai-compatible.test.ts +63 -0
- package/src/providers/openai-compatible.ts +50 -0
- package/src/providers/openai.test.ts +65 -0
- package/src/providers/openai.ts +37 -0
- package/src/providers/patch-zod-schema.ts +129 -0
- package/src/providers/pricing.test.ts +56 -0
- package/src/providers/pricing.ts +55 -0
- package/src/providers/runtime.ts +111 -0
- package/src/providers/stream-loop.ts +69 -0
- package/src/providers/types.ts +111 -0
- package/src/providers/vision-proxy.test.ts +167 -0
- package/src/providers/vision-proxy.ts +209 -0
- package/src/router/.gitkeep +0 -0
- package/src/router/classifier/grammars.ts +17 -0
- package/src/router/classifier/index.test.ts +33 -0
- package/src/router/classifier/index.ts +21 -0
- package/src/router/classifier/regex.test.ts +47 -0
- package/src/router/classifier/regex.ts +54 -0
- package/src/router/classifier/tree-sitter.test.ts +26 -0
- package/src/router/classifier/tree-sitter.ts +87 -0
- package/src/router/cold.test.ts +62 -0
- package/src/router/cold.ts +27 -0
- package/src/router/decide.test.ts +135 -0
- package/src/router/decide.ts +354 -0
- package/src/router/health.test.ts +59 -0
- package/src/router/health.ts +49 -0
- package/src/router/store.ts +47 -0
- package/src/router/types.ts +20 -0
- package/src/router/warm.test.ts +167 -0
- package/src/router/warm.ts +47 -0
- package/src/storage/__tests__/migrations.test.ts +395 -0
- package/src/storage/atomic-io.test.ts +61 -0
- package/src/storage/atomic-io.ts +62 -0
- package/src/storage/config.test.ts +33 -0
- package/src/storage/config.ts +49 -0
- package/src/storage/db.ts +95 -0
- package/src/storage/index.ts +19 -0
- package/src/storage/migrations.ts +143 -0
- package/src/storage/session-dir.ts +37 -0
- package/src/storage/sessions.ts +178 -0
- package/src/storage/tool-results.ts +56 -0
- package/src/storage/transcript-view.ts +45 -0
- package/src/storage/transcript.test.ts +24 -0
- package/src/storage/transcript.ts +358 -0
- package/src/storage/usage-cap.test.ts +59 -0
- package/src/storage/usage-cap.ts +81 -0
- package/src/storage/usage.ts +115 -0
- package/src/storage/workspaces.ts +84 -0
- package/src/tools/bash.test.ts +336 -0
- package/src/tools/bash.ts +612 -0
- package/src/tools/computer.test.ts +187 -0
- package/src/tools/computer.ts +632 -0
- package/src/tools/file.test.ts +95 -0
- package/src/tools/file.ts +128 -0
- package/src/tools/grep.ts +187 -0
- package/src/tools/registry.ts +282 -0
- package/src/tools/schedule.test.ts +143 -0
- package/src/tools/schedule.ts +610 -0
- package/src/types/index.ts +311 -0
- package/src/ui/agents-modal.tsx +272 -0
- package/src/ui/app.tsx +6277 -0
- package/src/ui/components/SuggestionOverlay.tsx +38 -0
- package/src/ui/components/btw-overlay.tsx +66 -0
- package/src/ui/hooks/useTypeahead.ts +146 -0
- package/src/ui/markdown.tsx +49 -0
- package/src/ui/mcp-modal-types.ts +33 -0
- package/src/ui/mcp-modal.tsx +456 -0
- package/src/ui/plan.tsx +346 -0
- package/src/ui/schedule-modal.tsx +126 -0
- package/src/ui/slash/__tests__/clear.test.ts +86 -0
- package/src/ui/slash/__tests__/compact.test.ts +56 -0
- package/src/ui/slash/__tests__/cost.test.ts +62 -0
- package/src/ui/slash/__tests__/discuss.test.ts +101 -0
- package/src/ui/slash/__tests__/execute.test.ts +86 -0
- package/src/ui/slash/__tests__/expand.test.ts +86 -0
- package/src/ui/slash/__tests__/optimize.test.ts +155 -0
- package/src/ui/slash/__tests__/plan.test.ts +95 -0
- package/src/ui/slash/clear.ts +89 -0
- package/src/ui/slash/compact.ts +46 -0
- package/src/ui/slash/cost.ts +59 -0
- package/src/ui/slash/council.ts +27 -0
- package/src/ui/slash/debug.ts +153 -0
- package/src/ui/slash/discuss.ts +71 -0
- package/src/ui/slash/ee.ts +271 -0
- package/src/ui/slash/execute.ts +44 -0
- package/src/ui/slash/expand.ts +51 -0
- package/src/ui/slash/optimize.ts +47 -0
- package/src/ui/slash/plan.ts +62 -0
- package/src/ui/slash/registry.ts +38 -0
- package/src/ui/slash/route.test.ts +82 -0
- package/src/ui/slash/route.ts +43 -0
- package/src/ui/status-bar/index.test.tsx +90 -0
- package/src/ui/status-bar/index.tsx +94 -0
- package/src/ui/status-bar/store.test.ts +131 -0
- package/src/ui/status-bar/store.ts +166 -0
- package/src/ui/status-bar/tier-badge.test.tsx +38 -0
- package/src/ui/status-bar/tier-badge.tsx +29 -0
- package/src/ui/status-bar/usd-meter.test.tsx +37 -0
- package/src/ui/status-bar/usd-meter.tsx +22 -0
- package/src/ui/terminal-selection-text.ts +72 -0
- package/src/ui/theme.ts +54 -0
- package/src/usage/.gitkeep +0 -0
- package/src/usage/downgrade.test.ts +82 -0
- package/src/usage/downgrade.ts +70 -0
- package/src/usage/estimator.test.ts +43 -0
- package/src/usage/estimator.ts +34 -0
- package/src/usage/ledger.test.ts +200 -0
- package/src/usage/ledger.ts +213 -0
- package/src/usage/midstream.test.ts +55 -0
- package/src/usage/midstream.ts +51 -0
- package/src/usage/thresholds.test.ts +83 -0
- package/src/usage/thresholds.ts +74 -0
- package/src/usage/types.ts +40 -0
- package/src/utils/at-mentions.ts +120 -0
- package/src/utils/clipboard-image.ts +114 -0
- package/src/utils/file-index.ts +152 -0
- package/src/utils/git-root.ts +17 -0
- package/src/utils/host-clipboard.ts +30 -0
- package/src/utils/install-manager.test.ts +167 -0
- package/src/utils/install-manager.ts +429 -0
- package/src/utils/instructions.test.ts +84 -0
- package/src/utils/instructions.ts +116 -0
- package/src/utils/permission-mode.test.ts +121 -0
- package/src/utils/permission-mode.ts +37 -0
- package/src/utils/redactor.test.ts +100 -0
- package/src/utils/redactor.ts +223 -0
- package/src/utils/settings.test.ts +181 -0
- package/src/utils/settings.ts +787 -0
- package/src/utils/side-question.ts +39 -0
- package/src/utils/skills.test.ts +58 -0
- package/src/utils/skills.ts +207 -0
- package/src/utils/subagent-display.test.ts +23 -0
- package/src/utils/subagent-display.ts +11 -0
- package/src/utils/subagents-settings.test.ts +77 -0
- package/src/utils/telegram-audio-settings.test.ts +44 -0
- package/src/utils/update-checker.test.ts +182 -0
- package/src/utils/update-checker.ts +33 -0
- package/src/verify/checkpoint.test.ts +186 -0
- package/src/verify/checkpoint.ts +239 -0
- package/src/verify/entrypoint.test.ts +293 -0
- package/src/verify/entrypoint.ts +439 -0
- package/src/verify/environment.test.ts +119 -0
- package/src/verify/environment.ts +115 -0
- package/src/verify/evidence.ts +104 -0
- package/src/verify/orchestrator.test.ts +159 -0
- package/src/verify/orchestrator.ts +129 -0
- package/src/verify/recipes.ts +516 -0
- package/src/verify/retry.ts +62 -0
- package/src/verify/runtime-prep.test.ts +47 -0
package/dist/ui/app.js
ADDED
|
@@ -0,0 +1,3957 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { decodePasteBytes, parseKeypress } from "@opentui/core";
|
|
3
|
+
import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/react";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { POPULAR_MCP_CATALOG } from "../mcp/catalog";
|
|
7
|
+
import { parseEnvLines, parseHeaderLines } from "../mcp/parse-headers";
|
|
8
|
+
import { toMcpServerId, validateMcpServerConfig } from "../mcp/validate";
|
|
9
|
+
import { Agent } from "../orchestrator/orchestrator";
|
|
10
|
+
import { deliberateCompact } from "../flow/compaction/index.js";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { MODES } from "../types/index";
|
|
13
|
+
import { processAtMentions } from "../utils/at-mentions.js";
|
|
14
|
+
import { FileIndex } from "../utils/file-index.js";
|
|
15
|
+
import { readClipboardImage } from "../utils/clipboard-image";
|
|
16
|
+
import { copyTextToHostClipboard } from "../utils/host-clipboard";
|
|
17
|
+
import { getApiKey, getTelegramBotToken, isReservedSubagentName, loadMcpServers, loadPaymentSettings, loadUserSettings, loadValidSubAgents, saveApprovedTelegramUserId, saveMcpServers, savePaymentSettings, saveProjectSettings, saveUserSettings, } from "../utils/settings";
|
|
18
|
+
import { discoverSkills, formatSkillsForChat } from "../utils/skills";
|
|
19
|
+
import { formatSubagentName } from "../utils/subagent-display";
|
|
20
|
+
import { checkForUpdate, runUpdate } from "../utils/update-checker";
|
|
21
|
+
import { buildVerifyPrompt } from "../verify/entrypoint";
|
|
22
|
+
import { buildSubagentBrowseRows, SUBAGENT_EDITOR_FIELDS, SubagentEditorModal, SubagentsBrowserModal, } from "./agents-modal";
|
|
23
|
+
import { BtwOverlay } from "./components/btw-overlay.js";
|
|
24
|
+
import { SuggestionOverlay } from "./components/SuggestionOverlay.js";
|
|
25
|
+
import { useTypeahead } from "./hooks/useTypeahead.js";
|
|
26
|
+
import { Markdown } from "./markdown";
|
|
27
|
+
import { buildMcpBrowseRows, McpBrowserModal, McpEditorModal } from "./mcp-modal";
|
|
28
|
+
import { createEmptyMcpEditorDraft } from "./mcp-modal-types";
|
|
29
|
+
import { formatPlanAnswers, initialPlanQuestionsState, PlanQuestionsPanel, PlanView, } from "./plan";
|
|
30
|
+
import { buildScheduleBrowseRows, ScheduleBrowserModal } from "./schedule-modal";
|
|
31
|
+
import { dispatchSlash } from "./slash/registry.js";
|
|
32
|
+
import { StatusBar } from "./status-bar/index.js";
|
|
33
|
+
import { statusBarStore, wireStatusBar } from "./status-bar/store.js";
|
|
34
|
+
import { getCompactTuiSelectionText } from "./terminal-selection-text";
|
|
35
|
+
import { dark } from "./theme";
|
|
36
|
+
import "./slash/route.js";
|
|
37
|
+
import "./slash/optimize.js";
|
|
38
|
+
import "./slash/discuss.js";
|
|
39
|
+
import "./slash/plan.js";
|
|
40
|
+
import "./slash/execute.js";
|
|
41
|
+
import "./slash/compact.js";
|
|
42
|
+
import "./slash/expand.js";
|
|
43
|
+
import "./slash/clear.js";
|
|
44
|
+
import "./slash/cost.js";
|
|
45
|
+
import "./slash/ee.js";
|
|
46
|
+
import "./slash/debug.js";
|
|
47
|
+
import "./slash/council.js";
|
|
48
|
+
import { getEffectiveReasoningEffort, getModelIds, getModelInfo, getSupportedReasoningEfforts, MODELS, isLoading, normalizeModelId, } from "../models/registry.js";
|
|
49
|
+
const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
50
|
+
function createTelegramBridge(_opts) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function approvePairingCode(_code) {
|
|
54
|
+
return { ok: false, error: "Telegram bridge not available." };
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
function createTurnCoordinator() {
|
|
58
|
+
return {
|
|
59
|
+
reset: () => { },
|
|
60
|
+
handleEvent: () => { },
|
|
61
|
+
run: async (fn) => fn(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function formatStructuredResponse(sr) {
|
|
65
|
+
const d = sr.data;
|
|
66
|
+
switch (sr.taskType) {
|
|
67
|
+
case "refactor": {
|
|
68
|
+
const r = d;
|
|
69
|
+
const parts = [r.summary ?? ""];
|
|
70
|
+
for (const c of r.changes ?? [])
|
|
71
|
+
parts.push(`\n── ${c.file} ──\n${c.diff}`);
|
|
72
|
+
if (r.verify_command)
|
|
73
|
+
parts.push(`\nverify: ${r.verify_command}`);
|
|
74
|
+
return parts.join("\n");
|
|
75
|
+
}
|
|
76
|
+
case "debug": {
|
|
77
|
+
const r = d;
|
|
78
|
+
const parts = [`hypothesis: ${r.hypothesis}`, `root cause: ${r.root_cause}`];
|
|
79
|
+
if (r.fix)
|
|
80
|
+
parts.push(`\n── fix: ${r.fix.file} ──\n${r.fix.diff}`);
|
|
81
|
+
if (r.verify_command)
|
|
82
|
+
parts.push(`verify: ${r.verify_command}`);
|
|
83
|
+
return parts.join("\n");
|
|
84
|
+
}
|
|
85
|
+
case "plan": {
|
|
86
|
+
const r = d;
|
|
87
|
+
const lines = (r.steps ?? []).map((s, i) => `${i + 1}. ${s.action}\n done when: ${s.criterion}${s.rationale ? `\n why: ${s.rationale}` : ""}`);
|
|
88
|
+
if (r.assumptions?.length)
|
|
89
|
+
lines.push(`\nassumptions:\n${r.assumptions.map((a) => ` - ${a}`).join("\n")}`);
|
|
90
|
+
if (r.risks?.length)
|
|
91
|
+
lines.push(`\nrisks:\n${r.risks.map((r2) => ` - ${r2}`).join("\n")}`);
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
case "analyze": {
|
|
95
|
+
const r = d;
|
|
96
|
+
return (r.findings ?? []).map((f) => `[${f.severity.toUpperCase()}] ${f.text}\n evidence: ${f.evidence}`).join("\n");
|
|
97
|
+
}
|
|
98
|
+
case "documentation": {
|
|
99
|
+
const r = d;
|
|
100
|
+
const parts = [r.content ?? ""];
|
|
101
|
+
for (const ex of r.examples ?? [])
|
|
102
|
+
parts.push(`\n${ex.description}\n${ex.code}`);
|
|
103
|
+
return parts.join("\n");
|
|
104
|
+
}
|
|
105
|
+
case "generate": {
|
|
106
|
+
const r = d;
|
|
107
|
+
const parts = [];
|
|
108
|
+
if (r.explanation)
|
|
109
|
+
parts.push(r.explanation);
|
|
110
|
+
for (const f of r.files ?? [])
|
|
111
|
+
parts.push(`\n── ${f.path} (${f.language}) ──\n${f.content}`);
|
|
112
|
+
return parts.join("\n");
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return JSON.stringify(d, null, 2);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function buildAssistantEntry(content, extra) {
|
|
119
|
+
return { type: "assistant", content, timestamp: new Date(), ...extra };
|
|
120
|
+
}
|
|
121
|
+
function buildToolResultEntry(toolCall, toolResult, extra) {
|
|
122
|
+
const output = toolResult.output ?? (toolResult.error ? `Error: ${toolResult.error}` : "");
|
|
123
|
+
return {
|
|
124
|
+
type: "tool_result",
|
|
125
|
+
content: typeof output === "string" ? output : String(output),
|
|
126
|
+
timestamp: new Date(),
|
|
127
|
+
toolCall,
|
|
128
|
+
toolResult,
|
|
129
|
+
...extra,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function buildUserEntry(content, extra) {
|
|
133
|
+
return { type: "user", content, timestamp: new Date(), ...extra };
|
|
134
|
+
}
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136
|
+
function decorateTelegramEntries(_entries, _userId, _remoteKey) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
function getTelegramSourceLabel(_role, _userId) {
|
|
140
|
+
return "";
|
|
141
|
+
}
|
|
142
|
+
function getUnflushedTelegramAssistantContent(_content, _flushedChars) {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
146
|
+
function replaceTurnEntries(_prev, _remoteKey, _delta) {
|
|
147
|
+
return _prev;
|
|
148
|
+
}
|
|
149
|
+
const STAR_PALETTE = ["#777777", "#666666", "#4a4a4a", "#333333", "#222222"];
|
|
150
|
+
const LOADING_SPINNER_FRAMES = ["⬒", "⬔", "⬓", "⬕"];
|
|
151
|
+
const PROMPT_LOADING_FRAMES = [
|
|
152
|
+
{ active: 0, forward: true },
|
|
153
|
+
{ active: 1, forward: true },
|
|
154
|
+
{ active: 2, forward: true },
|
|
155
|
+
{ active: 1, forward: false },
|
|
156
|
+
];
|
|
157
|
+
function getPasteBlockToken(block) {
|
|
158
|
+
if (block.isImage) {
|
|
159
|
+
return `[Image #${block.id}]`;
|
|
160
|
+
}
|
|
161
|
+
return `[Pasted #${block.id} ${block.lines}+ lines]`;
|
|
162
|
+
}
|
|
163
|
+
function getFileMentionToken(block) {
|
|
164
|
+
const name = block.path.split("/").pop() || block.path;
|
|
165
|
+
return `[File: ${name}]`;
|
|
166
|
+
}
|
|
167
|
+
const HERO_ROWS = [
|
|
168
|
+
{
|
|
169
|
+
stars: [
|
|
170
|
+
{ col: 0, ch: "·" },
|
|
171
|
+
{ col: 13, ch: "*" },
|
|
172
|
+
{ col: 21, ch: "·" },
|
|
173
|
+
{ col: 34, ch: "·" },
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
stars: [
|
|
178
|
+
{ col: 3, ch: "*" },
|
|
179
|
+
{ col: 11, ch: "·" },
|
|
180
|
+
{ col: 17, ch: "·" },
|
|
181
|
+
{ col: 25, ch: "*" },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
stars: [
|
|
186
|
+
{ col: 6, ch: "·" },
|
|
187
|
+
{ col: 12, ch: "·" },
|
|
188
|
+
{ col: 15, ch: "·" },
|
|
189
|
+
{ col: 18, ch: "·" },
|
|
190
|
+
{ col: 24, ch: "·" },
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
stars: [
|
|
195
|
+
{ col: 2, ch: "·" },
|
|
196
|
+
{ col: 10, ch: "·" },
|
|
197
|
+
{ col: 19, ch: "·" },
|
|
198
|
+
{ col: 27, ch: "·" },
|
|
199
|
+
],
|
|
200
|
+
brand: 13,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
stars: [
|
|
204
|
+
{ col: 6, ch: "·" },
|
|
205
|
+
{ col: 12, ch: "·" },
|
|
206
|
+
{ col: 15, ch: "·" },
|
|
207
|
+
{ col: 18, ch: "·" },
|
|
208
|
+
{ col: 24, ch: "·" },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
stars: [
|
|
213
|
+
{ col: 3, ch: "·" },
|
|
214
|
+
{ col: 11, ch: "*" },
|
|
215
|
+
{ col: 17, ch: "·" },
|
|
216
|
+
{ col: 25, ch: "·" },
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
stars: [
|
|
221
|
+
{ col: 0, ch: "*" },
|
|
222
|
+
{ col: 13, ch: "·" },
|
|
223
|
+
{ col: 21, ch: "*" },
|
|
224
|
+
{ col: 34, ch: "·" },
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
function HeroLogo({ t }) {
|
|
229
|
+
const [tick, setTick] = useState(0);
|
|
230
|
+
const starIdx = useRef(0);
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
const id = setInterval(() => setTick((n) => n + 1), 900);
|
|
233
|
+
return () => clearInterval(id);
|
|
234
|
+
}, []);
|
|
235
|
+
starIdx.current = 0;
|
|
236
|
+
const nextColor = () => {
|
|
237
|
+
const i = starIdx.current++;
|
|
238
|
+
return STAR_PALETTE[(i * 7 + tick * 3 + i * tick) % STAR_PALETTE.length];
|
|
239
|
+
};
|
|
240
|
+
return (_jsx("box", { flexDirection: "column", alignItems: "center", children: HERO_ROWS.map((row, r) => {
|
|
241
|
+
const els = [];
|
|
242
|
+
let cursor = 0;
|
|
243
|
+
for (const star of row.stars) {
|
|
244
|
+
if (row.brand !== undefined && cursor <= row.brand && star.col > row.brand) {
|
|
245
|
+
els.push(" ".repeat(row.brand - cursor));
|
|
246
|
+
els.push(_jsx("span", { style: { fg: t.primary }, children: "muonroi" }, "brand"));
|
|
247
|
+
cursor = row.brand + 7;
|
|
248
|
+
}
|
|
249
|
+
const gap = star.col - cursor;
|
|
250
|
+
if (gap > 0)
|
|
251
|
+
els.push(" ".repeat(gap));
|
|
252
|
+
els.push(_jsx("span", { style: { fg: nextColor() }, children: star.ch }, `s-${star.col}`));
|
|
253
|
+
cursor = star.col + 1;
|
|
254
|
+
}
|
|
255
|
+
if (row.brand !== undefined && cursor <= row.brand) {
|
|
256
|
+
els.push(" ".repeat(row.brand - cursor));
|
|
257
|
+
els.push(_jsx("span", { style: { fg: t.primary }, children: "muonroi" }, "brand"));
|
|
258
|
+
cursor = row.brand + 7;
|
|
259
|
+
}
|
|
260
|
+
els.push(" ".repeat(Math.max(0, 35 - cursor)));
|
|
261
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static constant array that never reorders
|
|
262
|
+
return _jsx("text", { children: els }, r);
|
|
263
|
+
}) }));
|
|
264
|
+
}
|
|
265
|
+
const SPLIT = {
|
|
266
|
+
topLeft: "",
|
|
267
|
+
bottomLeft: "",
|
|
268
|
+
vertical: "┃",
|
|
269
|
+
topRight: "",
|
|
270
|
+
bottomRight: "",
|
|
271
|
+
horizontal: " ",
|
|
272
|
+
bottomT: "",
|
|
273
|
+
topT: "",
|
|
274
|
+
cross: "",
|
|
275
|
+
leftT: "",
|
|
276
|
+
rightT: "",
|
|
277
|
+
};
|
|
278
|
+
const _SPLIT_END = { ...SPLIT, bottomLeft: "╹" };
|
|
279
|
+
const _EMPTY = {
|
|
280
|
+
topLeft: "",
|
|
281
|
+
bottomLeft: "",
|
|
282
|
+
vertical: "",
|
|
283
|
+
topRight: "",
|
|
284
|
+
bottomRight: "",
|
|
285
|
+
horizontal: " ",
|
|
286
|
+
bottomT: "",
|
|
287
|
+
topT: "",
|
|
288
|
+
cross: "",
|
|
289
|
+
leftT: "",
|
|
290
|
+
rightT: "",
|
|
291
|
+
};
|
|
292
|
+
const _LINE = {
|
|
293
|
+
topLeft: "━",
|
|
294
|
+
bottomLeft: "━",
|
|
295
|
+
vertical: "",
|
|
296
|
+
topRight: "━",
|
|
297
|
+
bottomRight: "━",
|
|
298
|
+
horizontal: "━",
|
|
299
|
+
bottomT: "━",
|
|
300
|
+
topT: "━",
|
|
301
|
+
cross: "━",
|
|
302
|
+
leftT: "━",
|
|
303
|
+
rightT: "━",
|
|
304
|
+
};
|
|
305
|
+
const SLASH_MENU_ITEMS = [
|
|
306
|
+
{ id: "exit", label: "exit", description: "Quit the CLI" },
|
|
307
|
+
{ id: "help", label: "help", description: "Show available commands" },
|
|
308
|
+
{ id: "clear", label: "clear", description: "Clear conversation and start fresh" },
|
|
309
|
+
{ id: "compact", label: "compact", description: "Compact conversation context" },
|
|
310
|
+
{ id: "remote-control", label: "remote-control", description: "Remote control" },
|
|
311
|
+
{ id: "agents", label: "agents", description: "Manage custom sub-agents" },
|
|
312
|
+
{ id: "schedule", label: "schedule", description: "View scheduled runs" },
|
|
313
|
+
{ id: "mcp", label: "mcp", description: "Manage MCP servers" },
|
|
314
|
+
{ id: "sandbox", label: "sandbox", description: "Select shell sandbox mode" },
|
|
315
|
+
{ id: "wallet", label: "wallet", description: "Wallet and payment settings" },
|
|
316
|
+
{ id: "models", label: "models", description: "Select a model" },
|
|
317
|
+
{ id: "new", label: "new session", description: "Start a new session" },
|
|
318
|
+
{ id: "commit-push", label: "commit & push", description: "Commit and push" },
|
|
319
|
+
{ id: "commit-pr", label: "commit & pr", description: "Commit and open PR" },
|
|
320
|
+
{ id: "review", label: "review", description: "Review recent changes" },
|
|
321
|
+
{ id: "verify", label: "verify", description: "Run local verification" },
|
|
322
|
+
{ id: "skills", label: "skills", description: "Manage skills" },
|
|
323
|
+
{ id: "btw", label: "btw", description: "Ask a side question without interrupting" },
|
|
324
|
+
{ id: "update", label: "update", description: "Update muonroi-cli to the latest version" },
|
|
325
|
+
{ id: "cost", label: "cost", description: "Show session cost breakdown" },
|
|
326
|
+
{ id: "ee", label: "ee", description: "Experience Engine status and controls" },
|
|
327
|
+
{ id: "route", label: "route", description: "Show current model routing info" },
|
|
328
|
+
{ id: "plan", label: "plan", description: "Show active GSD plan" },
|
|
329
|
+
{ id: "execute", label: "execute", description: "Execute active GSD plan" },
|
|
330
|
+
{ id: "discuss", label: "discuss", description: "Discuss phase gray areas" },
|
|
331
|
+
{ id: "expand", label: "expand", description: "Expand last compacted context" },
|
|
332
|
+
{ id: "optimize", label: "optimize", description: "Optimize prompt for token savings" },
|
|
333
|
+
{ id: "debug", label: "debug", description: "Toggle debug trace mode" },
|
|
334
|
+
{ id: "council", label: "council", description: "Multi-model adversarial debate" },
|
|
335
|
+
];
|
|
336
|
+
const REVIEW_PROMPT = `Review all current changes in this repository. Follow these steps:
|
|
337
|
+
|
|
338
|
+
1. Run \`git status\` to see which files have been modified, staged, or are untracked.
|
|
339
|
+
2. Run \`git diff\` to see unstaged changes and \`git diff --cached\` to see staged changes.
|
|
340
|
+
3. If there are no changes at all, say so and stop.
|
|
341
|
+
4. Read any changed files in full if needed for context.
|
|
342
|
+
|
|
343
|
+
Then produce a **Review Report** in this exact structure:
|
|
344
|
+
|
|
345
|
+
## Summary
|
|
346
|
+
One paragraph overview of what changed and why (inferred from the diff).
|
|
347
|
+
|
|
348
|
+
## Files Changed
|
|
349
|
+
For each changed file, list the filename and a brief description of the change.
|
|
350
|
+
|
|
351
|
+
## Issues Found
|
|
352
|
+
List any bugs, logic errors, security concerns, missing error handling, or correctness problems. If none, say "No issues found."
|
|
353
|
+
|
|
354
|
+
## Suggestions
|
|
355
|
+
Code quality, naming, performance, and best-practice improvements. If none, say "No suggestions."
|
|
356
|
+
|
|
357
|
+
## Risk Assessment
|
|
358
|
+
Rate the overall risk of these changes as **Low**, **Medium**, or **High** with a short justification.`;
|
|
359
|
+
const COMMIT_PUSH_PROMPT = `Create a git commit for the current repository changes and push the current branch to its remote.
|
|
360
|
+
|
|
361
|
+
Before committing, inspect the current branch. If it is not already a feature branch, create and switch to a new feature branch with a descriptive name based on the changes.
|
|
362
|
+
|
|
363
|
+
Follow the repository's commit workflow and safety checks. Inspect the current changes, stage any relevant untracked files, create an appropriate commit message, and push the branch if a commit was created. If there is nothing to commit, say so and stop.`;
|
|
364
|
+
const COMMIT_PR_PROMPT = `Create a git commit for the current repository changes and open a pull request for the current branch.
|
|
365
|
+
|
|
366
|
+
Before committing, inspect the current branch. If it is not already a feature branch, create and switch to a new feature branch with a descriptive name based on the changes.
|
|
367
|
+
|
|
368
|
+
Follow the repository's commit and pull request workflows. Inspect the current changes, stage any relevant untracked files, create an appropriate commit, push the branch if needed, then open a pull request with a concise summary and test plan. Return the pull request URL. If there is nothing to commit or open in a pull request, explain why and stop.`;
|
|
369
|
+
const BUILTIN_TYPED_SLASH_COMMANDS = new Set([
|
|
370
|
+
"/clear",
|
|
371
|
+
"/model",
|
|
372
|
+
"/models",
|
|
373
|
+
"/sandbox",
|
|
374
|
+
"/remote-control",
|
|
375
|
+
"/mcp",
|
|
376
|
+
"/mcps",
|
|
377
|
+
"/agents",
|
|
378
|
+
"/agent",
|
|
379
|
+
"/schedule",
|
|
380
|
+
"/schedules",
|
|
381
|
+
"/quit",
|
|
382
|
+
"/exit",
|
|
383
|
+
"/q",
|
|
384
|
+
"/review",
|
|
385
|
+
"/verify",
|
|
386
|
+
"/commit-push",
|
|
387
|
+
"/commit-pr",
|
|
388
|
+
"/wallet",
|
|
389
|
+
"/btw",
|
|
390
|
+
]);
|
|
391
|
+
const SANDBOX_ROWS = [
|
|
392
|
+
{
|
|
393
|
+
key: "mode",
|
|
394
|
+
label: "Mode",
|
|
395
|
+
type: "toggle",
|
|
396
|
+
getDisplay: (mode) => (mode === "shuru" ? "Shuru" : "Off"),
|
|
397
|
+
getOptions: () => ["Off", "Shuru"],
|
|
398
|
+
apply: (_mode, _s, value) => ({ mode: value === "Shuru" ? "shuru" : "off" }),
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
key: "allowNet",
|
|
402
|
+
label: "Network",
|
|
403
|
+
type: "toggle",
|
|
404
|
+
getDisplay: (_m, s) => (s.allowNet ? "On" : "Off"),
|
|
405
|
+
getOptions: () => ["Off", "On"],
|
|
406
|
+
apply: (_m, _s, value) => ({ settings: { allowNet: value === "On" } }),
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
key: "allowedHosts",
|
|
410
|
+
label: "Allowed hosts",
|
|
411
|
+
type: "text",
|
|
412
|
+
placeholder: "api.openai.com, registry.npmjs.org",
|
|
413
|
+
getDisplay: (_m, s) => s.allowedHosts?.join(", ") || "(unrestricted)",
|
|
414
|
+
apply: (_m, _s, value) => ({
|
|
415
|
+
settings: {
|
|
416
|
+
allowedHosts: value
|
|
417
|
+
? value
|
|
418
|
+
.split(",")
|
|
419
|
+
.map((h) => h.trim())
|
|
420
|
+
.filter(Boolean)
|
|
421
|
+
: undefined,
|
|
422
|
+
},
|
|
423
|
+
}),
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
key: "ports",
|
|
427
|
+
label: "Port forwards",
|
|
428
|
+
type: "text",
|
|
429
|
+
placeholder: "8080:80, 8443:443",
|
|
430
|
+
getDisplay: (_m, s) => s.ports?.join(", ") || "(none)",
|
|
431
|
+
apply: (_m, _s, value) => ({
|
|
432
|
+
settings: {
|
|
433
|
+
ports: value
|
|
434
|
+
? value
|
|
435
|
+
.split(",")
|
|
436
|
+
.map((p) => p.trim())
|
|
437
|
+
.filter(Boolean)
|
|
438
|
+
: undefined,
|
|
439
|
+
},
|
|
440
|
+
}),
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
key: "cpus",
|
|
444
|
+
label: "CPUs",
|
|
445
|
+
type: "text",
|
|
446
|
+
placeholder: "e.g. 4",
|
|
447
|
+
getDisplay: (_m, s) => (s.cpus ? String(s.cpus) : "(default)"),
|
|
448
|
+
apply: (_m, _s, value) => ({ settings: { cpus: value ? parseInt(value, 10) || undefined : undefined } }),
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
key: "memory",
|
|
452
|
+
label: "Memory (MB)",
|
|
453
|
+
type: "text",
|
|
454
|
+
placeholder: "e.g. 4096",
|
|
455
|
+
getDisplay: (_m, s) => (s.memory ? String(s.memory) : "(default)"),
|
|
456
|
+
apply: (_m, _s, value) => ({ settings: { memory: value ? parseInt(value, 10) || undefined : undefined } }),
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
key: "diskSize",
|
|
460
|
+
label: "Disk size (MB)",
|
|
461
|
+
type: "text",
|
|
462
|
+
placeholder: "e.g. 8192",
|
|
463
|
+
getDisplay: (_m, s) => (s.diskSize ? String(s.diskSize) : "(default)"),
|
|
464
|
+
apply: (_m, _s, value) => ({ settings: { diskSize: value ? parseInt(value, 10) || undefined : undefined } }),
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
key: "from",
|
|
468
|
+
label: "Checkpoint",
|
|
469
|
+
type: "text",
|
|
470
|
+
placeholder: "checkpoint name",
|
|
471
|
+
getDisplay: (_m, s) => s.from || "(none)",
|
|
472
|
+
apply: (_m, _s, value) => ({ settings: { from: value || undefined } }),
|
|
473
|
+
},
|
|
474
|
+
];
|
|
475
|
+
function getSandboxVisibleRows(mode) {
|
|
476
|
+
return mode === "shuru" ? SANDBOX_ROWS : SANDBOX_ROWS.slice(0, 1);
|
|
477
|
+
}
|
|
478
|
+
const WALLET_ROWS = [
|
|
479
|
+
{
|
|
480
|
+
key: "enabled",
|
|
481
|
+
label: "Payments",
|
|
482
|
+
type: "toggle",
|
|
483
|
+
getDisplay: (s) => (s.enabled ? "enabled" : "disabled"),
|
|
484
|
+
getOptions: () => ["enabled", "disabled"],
|
|
485
|
+
apply: (_s, v) => ({ enabled: v === "enabled" }),
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
key: "chain",
|
|
489
|
+
label: "Chain",
|
|
490
|
+
type: "toggle",
|
|
491
|
+
getDisplay: (s) => s.chain,
|
|
492
|
+
getOptions: () => ["base-sepolia", "base"],
|
|
493
|
+
apply: (_s, v) => ({ chain: v }),
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
key: "autoApprove",
|
|
497
|
+
label: "Auto-approve",
|
|
498
|
+
type: "toggle",
|
|
499
|
+
getDisplay: (s) => (s.approval.autoApprove ? "on" : "off"),
|
|
500
|
+
getOptions: () => ["off", "on"],
|
|
501
|
+
apply: (s, v) => ({ approval: { ...s.approval, autoApprove: v === "on" } }),
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
key: "address",
|
|
505
|
+
label: "Address",
|
|
506
|
+
type: "readonly",
|
|
507
|
+
getDisplay: (_s, info) => info.address ?? "No wallet",
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
key: "eth",
|
|
511
|
+
label: "ETH",
|
|
512
|
+
type: "readonly",
|
|
513
|
+
getDisplay: (_s, info) => info.ethBalance ?? "...",
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
key: "usdc",
|
|
517
|
+
label: "USDC",
|
|
518
|
+
type: "readonly",
|
|
519
|
+
getDisplay: (_s, info) => info.usdcBalance ?? "...",
|
|
520
|
+
},
|
|
521
|
+
];
|
|
522
|
+
function parseCustomSubagentSlashCommand(cmd, subagents) {
|
|
523
|
+
const trimmed = cmd.trim();
|
|
524
|
+
if (!trimmed.startsWith("/"))
|
|
525
|
+
return null;
|
|
526
|
+
const body = trimmed.slice(1).trim();
|
|
527
|
+
if (!body)
|
|
528
|
+
return null;
|
|
529
|
+
const commandToken = body.split(/\s+/, 1)[0]?.toLowerCase();
|
|
530
|
+
if (commandToken && BUILTIN_TYPED_SLASH_COMMANDS.has(`/${commandToken}`)) {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
const lowerBody = body.toLowerCase();
|
|
534
|
+
const sortedSubagents = [...subagents].sort((a, b) => b.name.length - a.name.length);
|
|
535
|
+
const match = sortedSubagents.find((item) => {
|
|
536
|
+
const lowerName = item.name.trim().toLowerCase();
|
|
537
|
+
return lowerBody === lowerName || lowerBody.startsWith(`${lowerName} `);
|
|
538
|
+
});
|
|
539
|
+
if (!match)
|
|
540
|
+
return null;
|
|
541
|
+
return {
|
|
542
|
+
agentName: match.name,
|
|
543
|
+
prompt: body.slice(match.name.length).trim(),
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function buildCustomSubagentSlashPrompt(agentName, prompt) {
|
|
547
|
+
return `Use the custom sub-agent "${agentName}" for this task.
|
|
548
|
+
|
|
549
|
+
Delegate the work with the \`task\` tool using:
|
|
550
|
+
- \`agent\`: "${agentName}"
|
|
551
|
+
- \`description\`: a short summary of the work
|
|
552
|
+
- \`prompt\`: a detailed prompt based on the user's request
|
|
553
|
+
|
|
554
|
+
User request:
|
|
555
|
+
${prompt}`;
|
|
556
|
+
}
|
|
557
|
+
const CONNECT_CHANNELS = [
|
|
558
|
+
{ id: "telegram", label: "Telegram", description: "Chat from Telegram" },
|
|
559
|
+
];
|
|
560
|
+
const MCP_REMOTE_FIELDS = ["transport", "label", "url", "headers", "env"];
|
|
561
|
+
const MCP_STDIO_FIELDS = ["transport", "label", "command", "args", "cwd", "env"];
|
|
562
|
+
export function App({ agent, startupConfig, initialMessage, onExit }) {
|
|
563
|
+
const t = dark;
|
|
564
|
+
const renderer = useRenderer();
|
|
565
|
+
// Set initial status bar values synchronously before first render
|
|
566
|
+
useMemo(() => {
|
|
567
|
+
statusBarStore.setState({
|
|
568
|
+
provider: agent.getProviderId(),
|
|
569
|
+
model: agent.getModel(),
|
|
570
|
+
});
|
|
571
|
+
}, []);
|
|
572
|
+
// Wire status bar subscriptions once at boot (Plan 06)
|
|
573
|
+
useEffect(() => wireStatusBar(), []);
|
|
574
|
+
const initialHasApiKey = agent.hasApiKey();
|
|
575
|
+
const [hasApiKey, setHasApiKey] = useState(initialHasApiKey);
|
|
576
|
+
const [messages, setMessages] = useState(() => agent.getChatEntries());
|
|
577
|
+
const [streamContent, setStreamContent] = useState("");
|
|
578
|
+
const [_streamReasoning, setStreamReasoning] = useState("");
|
|
579
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
580
|
+
const [liveTurnSourceLabel, setLiveTurnSourceLabel] = useState(null);
|
|
581
|
+
const [model, setModel] = useState(agent.getModel());
|
|
582
|
+
const [sandboxMode, setSandboxModeState] = useState(agent.getSandboxMode());
|
|
583
|
+
const [mode, setModeState] = useState(agent.getMode());
|
|
584
|
+
const [showModelPicker, setShowModelPicker] = useState(false);
|
|
585
|
+
const [modelPickerIndex, setModelPickerIndex] = useState(0);
|
|
586
|
+
const [modelSearchQuery, setModelSearchQuery] = useState("");
|
|
587
|
+
const [showSandboxPicker, setShowSandboxPicker] = useState(false);
|
|
588
|
+
const [sandboxSettings, setSandboxSettingsState] = useState(() => agent.getSandboxSettings());
|
|
589
|
+
const [sandboxSettingsFocusIndex, setSandboxSettingsFocusIndex] = useState(0);
|
|
590
|
+
const [sandboxSettingsEditing, setSandboxSettingsEditing] = useState(null);
|
|
591
|
+
const [sandboxSettingsEditBuffer, setSandboxSettingsEditBuffer] = useState("");
|
|
592
|
+
const [showWalletPicker, setShowWalletPicker] = useState(false);
|
|
593
|
+
const [walletSettings, setWalletSettings] = useState(() => loadPaymentSettings());
|
|
594
|
+
const [walletFocusIndex, setWalletFocusIndex] = useState(0);
|
|
595
|
+
const [walletDisplayInfo, setWalletDisplayInfo] = useState({
|
|
596
|
+
address: null,
|
|
597
|
+
ethBalance: null,
|
|
598
|
+
usdcBalance: null,
|
|
599
|
+
});
|
|
600
|
+
const [pendingPaymentApproval, setPendingPaymentApproval] = useState(null);
|
|
601
|
+
const [activeToolCalls, setActiveToolCalls] = useState([]);
|
|
602
|
+
const [sessionTitle, setSessionTitle] = useState(() => agent.getSessionTitle());
|
|
603
|
+
const [sessionId, setSessionId] = useState(() => agent.getSessionId());
|
|
604
|
+
const [showApiKeyModal, setShowApiKeyModal] = useState(() => !initialHasApiKey);
|
|
605
|
+
const [apiKeyError, setApiKeyError] = useState(null);
|
|
606
|
+
const [showSlashMenu, setShowSlashMenu] = useState(false);
|
|
607
|
+
const [slashMenuIndex, setSlashMenuIndex] = useState(0);
|
|
608
|
+
const [slashSearchQuery, setSlashSearchQuery] = useState("");
|
|
609
|
+
const [btwState, setBtwState] = useState(null);
|
|
610
|
+
const btwAbortRef = useRef(null);
|
|
611
|
+
const btwStateRef = useRef(null);
|
|
612
|
+
const [reasoningEffortByModel, setReasoningEffortByModel] = useState(() => Object.fromEntries(Object.entries(loadUserSettings().reasoningEffortByModel ?? {}).map(([modelId, effort]) => [
|
|
613
|
+
normalizeModelId(modelId),
|
|
614
|
+
effort,
|
|
615
|
+
])));
|
|
616
|
+
const [pasteBlocks, setPasteBlocks] = useState([]);
|
|
617
|
+
const [activePlan, setActivePlan] = useState(null);
|
|
618
|
+
/** Incremented on each successful TUI copy; drives a brief "Copied" banner. */
|
|
619
|
+
const [copyFlashId, setCopyFlashId] = useState(0);
|
|
620
|
+
const [expandedMessages, setExpandedMessages] = useState(() => new Set());
|
|
621
|
+
const [activeSubagent, setActiveSubagent] = useState(null);
|
|
622
|
+
const [pqs, setPqs] = useState(initialPlanQuestionsState());
|
|
623
|
+
const pasteCounterRef = useRef(0);
|
|
624
|
+
const pasteBlocksRef = useRef([]);
|
|
625
|
+
const apiKeyInputRef = useRef(null);
|
|
626
|
+
const inputRef = useRef(null);
|
|
627
|
+
const scrollRef = useRef(null);
|
|
628
|
+
const { width, height } = useTerminalDimensions();
|
|
629
|
+
const processedInitial = useRef(false);
|
|
630
|
+
const contentAccRef = useRef("");
|
|
631
|
+
const startTimeRef = useRef(0);
|
|
632
|
+
const isProcessingRef = useRef(false);
|
|
633
|
+
const hasApiKeyRef = useRef(initialHasApiKey);
|
|
634
|
+
const showApiKeyModalRef = useRef(!initialHasApiKey);
|
|
635
|
+
const queuedMessagesRef = useRef([]);
|
|
636
|
+
const processMessageRef = useRef(() => { });
|
|
637
|
+
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
638
|
+
const modeInfoRef = useRef(MODES[0]);
|
|
639
|
+
const activeRunIdRef = useRef(0);
|
|
640
|
+
const interruptedRunIdRef = useRef(null);
|
|
641
|
+
const activeTurnRef = useRef(null);
|
|
642
|
+
const coordinatorRef = useRef(createTurnCoordinator());
|
|
643
|
+
const bridgeRef = useRef(null);
|
|
644
|
+
const telegramAgentsRef = useRef(new Map());
|
|
645
|
+
const telegramEntryCountsRef = useRef(new Map());
|
|
646
|
+
const telegramSubagentUnsubsRef = useRef(new Map());
|
|
647
|
+
const [showConnectModal, setShowConnectModal] = useState(false);
|
|
648
|
+
const [showTelegramTokenModal, setShowTelegramTokenModal] = useState(false);
|
|
649
|
+
const [showTelegramPairModal, setShowTelegramPairModal] = useState(false);
|
|
650
|
+
const [telegramTokenError, setTelegramTokenError] = useState(null);
|
|
651
|
+
const [telegramPairError, setTelegramPairError] = useState(null);
|
|
652
|
+
const [connectModalIndex, setConnectModalIndex] = useState(0);
|
|
653
|
+
const telegramTokenInputRef = useRef(null);
|
|
654
|
+
const telegramPairInputRef = useRef(null);
|
|
655
|
+
const showConnectModalRef = useRef(false);
|
|
656
|
+
const showTelegramTokenModalRef = useRef(false);
|
|
657
|
+
const showTelegramPairModalRef = useRef(false);
|
|
658
|
+
const [showMcpModal, setShowMcpModal] = useState(false);
|
|
659
|
+
const [showMcpEditor, setShowMcpEditor] = useState(false);
|
|
660
|
+
const [mcpSearchQuery, setMcpSearchQuery] = useState("");
|
|
661
|
+
const [mcpModalIndex, setMcpModalIndex] = useState(0);
|
|
662
|
+
const [mcpServers, setMcpServers] = useState(() => loadMcpServers());
|
|
663
|
+
const [mcpEditorDraft, setMcpEditorDraft] = useState(createEmptyMcpEditorDraft());
|
|
664
|
+
const [mcpEditorField, setMcpEditorField] = useState("transport");
|
|
665
|
+
const [mcpEditorSyncKey, setMcpEditorSyncKey] = useState(0);
|
|
666
|
+
const [mcpEditorError, setMcpEditorError] = useState(null);
|
|
667
|
+
const [editingMcpId, setEditingMcpId] = useState(null);
|
|
668
|
+
const showMcpModalRef = useRef(false);
|
|
669
|
+
const showMcpEditorRef = useRef(false);
|
|
670
|
+
const mcpLabelRef = useRef(null);
|
|
671
|
+
const mcpUrlRef = useRef(null);
|
|
672
|
+
const mcpHeadersRef = useRef(null);
|
|
673
|
+
const mcpCommandRef = useRef(null);
|
|
674
|
+
const mcpArgsRef = useRef(null);
|
|
675
|
+
const mcpCwdRef = useRef(null);
|
|
676
|
+
const mcpEnvRef = useRef(null);
|
|
677
|
+
const [showAgentsModal, setShowAgentsModal] = useState(false);
|
|
678
|
+
const [showAgentsEditor, setShowAgentsEditor] = useState(false);
|
|
679
|
+
const [subAgents, setSubAgents] = useState(() => loadValidSubAgents());
|
|
680
|
+
const [agentsSearchQuery, setAgentsSearchQuery] = useState("");
|
|
681
|
+
const [agentsModalIndex, setAgentsModalIndex] = useState(0);
|
|
682
|
+
const [editingSubagent, setEditingSubagent] = useState(null);
|
|
683
|
+
const [agentsEditorDraft, setAgentsEditorDraft] = useState({ name: "", instruction: "" });
|
|
684
|
+
const [agentsEditorField, setAgentsEditorField] = useState("name");
|
|
685
|
+
const [agentsEditorModelIndex, setAgentsEditorModelIndex] = useState(() => Math.max(0, MODELS.findIndex((model) => model.id === DEFAULT_MODEL)));
|
|
686
|
+
const [agentsEditorSyncKey, setAgentsEditorSyncKey] = useState(0);
|
|
687
|
+
const [agentsEditorError, setAgentsEditorError] = useState(null);
|
|
688
|
+
const showAgentsModalRef = useRef(false);
|
|
689
|
+
const showAgentsEditorRef = useRef(false);
|
|
690
|
+
const subagentNameRef = useRef(null);
|
|
691
|
+
const subagentInstructionRef = useRef(null);
|
|
692
|
+
const [showScheduleModal, setShowScheduleModal] = useState(false);
|
|
693
|
+
const [schedules, setSchedules] = useState([]);
|
|
694
|
+
const [scheduleSearchQuery, setScheduleSearchQuery] = useState("");
|
|
695
|
+
const [scheduleModalIndex, setScheduleModalIndex] = useState(0);
|
|
696
|
+
const showScheduleModalRef = useRef(false);
|
|
697
|
+
const [updateInfo, setUpdateInfo] = useState(null);
|
|
698
|
+
const [showUpdateModal, setShowUpdateModal] = useState(false);
|
|
699
|
+
const [isUpdating, setIsUpdating] = useState(false);
|
|
700
|
+
const [updateOutput, setUpdateOutput] = useState(null);
|
|
701
|
+
const showUpdateModalRef = useRef(false);
|
|
702
|
+
const fileIndexRef = useRef(null);
|
|
703
|
+
if (!fileIndexRef.current) {
|
|
704
|
+
fileIndexRef.current = new FileIndex(agent.getCwd());
|
|
705
|
+
}
|
|
706
|
+
const fileMentionCounterRef = useRef(0);
|
|
707
|
+
const fileMentionBlocksRef = useRef([]);
|
|
708
|
+
const handleFileAccept = useCallback((filePath, tokenInfo) => {
|
|
709
|
+
const ta = inputRef.current;
|
|
710
|
+
if (!ta)
|
|
711
|
+
return;
|
|
712
|
+
const id = ++fileMentionCounterRef.current;
|
|
713
|
+
const block = { id, path: fileIndexRef.current?.resolvePath(filePath) ?? filePath };
|
|
714
|
+
fileMentionBlocksRef.current = [...fileMentionBlocksRef.current, block];
|
|
715
|
+
const text = ta.plainText;
|
|
716
|
+
const before = text.slice(0, tokenInfo.startPos);
|
|
717
|
+
const after = text.slice(tokenInfo.endPos);
|
|
718
|
+
const token = getFileMentionToken(block);
|
|
719
|
+
const newText = `${before}${token} ${after}`;
|
|
720
|
+
ta.setText(newText);
|
|
721
|
+
ta.cursorOffset = before.length + token.length + 1;
|
|
722
|
+
}, []);
|
|
723
|
+
const typeahead = useTypeahead(inputRef, fileIndexRef.current, handleFileAccept);
|
|
724
|
+
const typeaheadRef = useRef(typeahead);
|
|
725
|
+
typeaheadRef.current = typeahead;
|
|
726
|
+
const setMode = useCallback((m) => {
|
|
727
|
+
if (m === "agent" && mode === "plan" && activePlan) {
|
|
728
|
+
const planText = [
|
|
729
|
+
`# ${activePlan.title}`,
|
|
730
|
+
activePlan.summary,
|
|
731
|
+
"",
|
|
732
|
+
...activePlan.steps.map((s, i) => `${i + 1}. ${s.title}: ${s.description}${s.filePaths?.length ? ` (${s.filePaths.join(", ")})` : ""}`),
|
|
733
|
+
].join("\n");
|
|
734
|
+
agent.setPlanContext(planText);
|
|
735
|
+
}
|
|
736
|
+
agent.setMode(m);
|
|
737
|
+
setModeState(m);
|
|
738
|
+
setModel(agent.getModel());
|
|
739
|
+
}, [agent, mode, activePlan]);
|
|
740
|
+
const cycleMode = useCallback(() => {
|
|
741
|
+
const idx = MODES.findIndex((m) => m.id === mode);
|
|
742
|
+
setMode(MODES[(idx + 1) % MODES.length].id);
|
|
743
|
+
}, [mode, setMode]);
|
|
744
|
+
const modeInfo = MODES.find((m) => m.id === mode);
|
|
745
|
+
modeInfoRef.current = modeInfo;
|
|
746
|
+
const modelInfo = getModelInfo(model);
|
|
747
|
+
const contextStats = modelInfo ? agent.getContextStats(modelInfo.contextWindow, streamContent) : null;
|
|
748
|
+
// UI Loading logic for dynamic models
|
|
749
|
+
const modelList = isLoading ? [] : MODELS;
|
|
750
|
+
const filteredModels = modelSearchQuery
|
|
751
|
+
? modelList.filter((m) => m.name.toLowerCase().includes(modelSearchQuery.toLowerCase()) ||
|
|
752
|
+
m.id.toLowerCase().includes(modelSearchQuery.toLowerCase()))
|
|
753
|
+
: [...modelList];
|
|
754
|
+
const filteredModelIds = filteredModels.map((m) => m.id);
|
|
755
|
+
const filteredSlashItems = slashSearchQuery
|
|
756
|
+
? SLASH_MENU_ITEMS.filter((item) => item.label.toLowerCase().includes(slashSearchQuery.toLowerCase()) ||
|
|
757
|
+
item.description.toLowerCase().includes(slashSearchQuery.toLowerCase()))
|
|
758
|
+
: SLASH_MENU_ITEMS;
|
|
759
|
+
const mcpRows = buildMcpBrowseRows(mcpServers, POPULAR_MCP_CATALOG, mcpSearchQuery);
|
|
760
|
+
const mcpEditorFields = mcpEditorDraft.transport === "stdio" ? MCP_STDIO_FIELDS : MCP_REMOTE_FIELDS;
|
|
761
|
+
const agentRows = useMemo(() => buildSubagentBrowseRows(subAgents, agentsSearchQuery), [subAgents, agentsSearchQuery]);
|
|
762
|
+
const scheduleRows = useMemo(() => buildScheduleBrowseRows(schedules, scheduleSearchQuery), [schedules, scheduleSearchQuery]);
|
|
763
|
+
const syncStoredMcpServers = useCallback((servers) => {
|
|
764
|
+
setMcpServers(servers);
|
|
765
|
+
saveMcpServers(servers);
|
|
766
|
+
}, []);
|
|
767
|
+
const applySandboxMode = useCallback((next) => {
|
|
768
|
+
agent.setSandboxMode(next);
|
|
769
|
+
for (const telegramAgent of telegramAgentsRef.current.values()) {
|
|
770
|
+
telegramAgent.setSandboxMode(next);
|
|
771
|
+
}
|
|
772
|
+
setSandboxModeState(next);
|
|
773
|
+
saveProjectSettings({ sandboxMode: next });
|
|
774
|
+
saveUserSettings({ sandboxMode: next });
|
|
775
|
+
}, [agent]);
|
|
776
|
+
const applySandboxSettings = useCallback((next) => {
|
|
777
|
+
agent.setSandboxSettings(next);
|
|
778
|
+
for (const telegramAgent of telegramAgentsRef.current.values()) {
|
|
779
|
+
telegramAgent.setSandboxSettings(next);
|
|
780
|
+
}
|
|
781
|
+
setSandboxSettingsState(next);
|
|
782
|
+
saveProjectSettings({ sandbox: next });
|
|
783
|
+
saveUserSettings({ sandbox: next });
|
|
784
|
+
}, [agent]);
|
|
785
|
+
const openSandboxPicker = useCallback(() => {
|
|
786
|
+
setSandboxSettingsFocusIndex(0);
|
|
787
|
+
setSandboxSettingsEditing(null);
|
|
788
|
+
setSandboxSettingsEditBuffer("");
|
|
789
|
+
setShowSandboxPicker(true);
|
|
790
|
+
}, []);
|
|
791
|
+
const applyWalletSettings = useCallback((next) => {
|
|
792
|
+
setWalletSettings(next);
|
|
793
|
+
savePaymentSettings(next);
|
|
794
|
+
}, []);
|
|
795
|
+
const openWalletPicker = useCallback(() => {
|
|
796
|
+
setWalletFocusIndex(0);
|
|
797
|
+
setWalletSettings(loadPaymentSettings());
|
|
798
|
+
setShowWalletPicker(true);
|
|
799
|
+
// Wallet UI disabled — Stripe billing pending.
|
|
800
|
+
setWalletDisplayInfo({ address: null, ethBalance: null, usdcBalance: null });
|
|
801
|
+
}, []);
|
|
802
|
+
const setReasoningEfforts = useCallback((next) => {
|
|
803
|
+
setReasoningEffortByModel(next);
|
|
804
|
+
saveUserSettings({ reasoningEffortByModel: next });
|
|
805
|
+
}, []);
|
|
806
|
+
const replacePasteBlocks = useCallback((next) => {
|
|
807
|
+
pasteBlocksRef.current = next;
|
|
808
|
+
setPasteBlocks(next);
|
|
809
|
+
}, []);
|
|
810
|
+
const getModelReasoningEffort = useCallback((modelId) => {
|
|
811
|
+
const normalizedModelId = normalizeModelId(modelId);
|
|
812
|
+
return getEffectiveReasoningEffort(normalizedModelId, reasoningEffortByModel[normalizedModelId]);
|
|
813
|
+
}, [reasoningEffortByModel]);
|
|
814
|
+
const adjustModelReasoningEffort = useCallback((modelId, direction) => {
|
|
815
|
+
const normalizedModelId = normalizeModelId(modelId);
|
|
816
|
+
const supported = getSupportedReasoningEfforts(normalizedModelId);
|
|
817
|
+
if (supported.length === 0)
|
|
818
|
+
return;
|
|
819
|
+
const current = getModelReasoningEffort(normalizedModelId);
|
|
820
|
+
if (!current) {
|
|
821
|
+
if (direction > 0) {
|
|
822
|
+
setReasoningEfforts({ ...reasoningEffortByModel, [normalizedModelId]: supported[0] });
|
|
823
|
+
}
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const currentIndex = supported.indexOf(current);
|
|
827
|
+
if (direction < 0 && currentIndex <= 0) {
|
|
828
|
+
const { [normalizedModelId]: _, ...rest } = reasoningEffortByModel;
|
|
829
|
+
setReasoningEfforts(rest);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
const nextIndex = direction < 0 ? currentIndex - 1 : Math.min(supported.length - 1, currentIndex + 1);
|
|
833
|
+
setReasoningEfforts({ ...reasoningEffortByModel, [normalizedModelId]: supported[nextIndex] });
|
|
834
|
+
}
|
|
835
|
+
}, [getModelReasoningEffort, reasoningEffortByModel, setReasoningEfforts]);
|
|
836
|
+
const snapshotMcpEditorDraft = useCallback(() => {
|
|
837
|
+
return {
|
|
838
|
+
...mcpEditorDraft,
|
|
839
|
+
label: mcpLabelRef.current?.plainText ?? mcpEditorDraft.label,
|
|
840
|
+
url: mcpUrlRef.current?.plainText ?? mcpEditorDraft.url,
|
|
841
|
+
headersText: mcpHeadersRef.current?.plainText ?? mcpEditorDraft.headersText,
|
|
842
|
+
command: mcpCommandRef.current?.plainText ?? mcpEditorDraft.command,
|
|
843
|
+
argsText: mcpArgsRef.current?.plainText ?? mcpEditorDraft.argsText,
|
|
844
|
+
cwd: mcpCwdRef.current?.plainText ?? mcpEditorDraft.cwd,
|
|
845
|
+
envText: mcpEnvRef.current?.plainText ?? mcpEditorDraft.envText,
|
|
846
|
+
};
|
|
847
|
+
}, [mcpEditorDraft]);
|
|
848
|
+
const openMcpModal = useCallback(() => {
|
|
849
|
+
const latest = loadMcpServers();
|
|
850
|
+
setMcpServers(latest);
|
|
851
|
+
setMcpSearchQuery("");
|
|
852
|
+
setMcpModalIndex(0);
|
|
853
|
+
setShowMcpModal(true);
|
|
854
|
+
setShowMcpEditor(false);
|
|
855
|
+
setEditingMcpId(null);
|
|
856
|
+
setMcpEditorError(null);
|
|
857
|
+
}, []);
|
|
858
|
+
const openMcpEditor = useCallback((draft, editingId = null) => {
|
|
859
|
+
setMcpEditorDraft(draft);
|
|
860
|
+
setEditingMcpId(editingId);
|
|
861
|
+
setMcpEditorField("transport");
|
|
862
|
+
setMcpEditorError(null);
|
|
863
|
+
setMcpEditorSyncKey((n) => n + 1);
|
|
864
|
+
setShowMcpEditor(true);
|
|
865
|
+
setShowMcpModal(true);
|
|
866
|
+
}, []);
|
|
867
|
+
const openCatalogMcp = useCallback((entry) => {
|
|
868
|
+
const existing = mcpServers.find((server) => toMcpServerId(server.id) === toMcpServerId(entry.id));
|
|
869
|
+
if (existing) {
|
|
870
|
+
openMcpEditor({
|
|
871
|
+
label: existing.label,
|
|
872
|
+
transport: existing.transport,
|
|
873
|
+
url: existing.url ?? "",
|
|
874
|
+
headersText: Object.entries(existing.headers ?? {})
|
|
875
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
876
|
+
.join("\n"),
|
|
877
|
+
command: existing.command ?? "",
|
|
878
|
+
argsText: (existing.args ?? []).join("\n"),
|
|
879
|
+
cwd: existing.cwd ?? "",
|
|
880
|
+
envText: Object.entries(existing.env ?? {})
|
|
881
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
882
|
+
.join("\n"),
|
|
883
|
+
}, existing.id);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
openMcpEditor({
|
|
887
|
+
...createEmptyMcpEditorDraft(),
|
|
888
|
+
label: entry.name,
|
|
889
|
+
transport: entry.starterTransport ?? "stdio",
|
|
890
|
+
});
|
|
891
|
+
}, [mcpServers, openMcpEditor]);
|
|
892
|
+
const editSavedMcp = useCallback((server) => {
|
|
893
|
+
openMcpEditor({
|
|
894
|
+
label: server.label,
|
|
895
|
+
transport: server.transport,
|
|
896
|
+
url: server.url ?? "",
|
|
897
|
+
headersText: Object.entries(server.headers ?? {})
|
|
898
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
899
|
+
.join("\n"),
|
|
900
|
+
command: server.command ?? "",
|
|
901
|
+
argsText: (server.args ?? []).join("\n"),
|
|
902
|
+
cwd: server.cwd ?? "",
|
|
903
|
+
envText: Object.entries(server.env ?? {})
|
|
904
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
905
|
+
.join("\n"),
|
|
906
|
+
}, server.id);
|
|
907
|
+
}, [openMcpEditor]);
|
|
908
|
+
const toggleSavedMcp = useCallback((server) => {
|
|
909
|
+
syncStoredMcpServers(mcpServers.map((item) => (item.id === server.id ? { ...item, enabled: !item.enabled } : item)));
|
|
910
|
+
}, [mcpServers, syncStoredMcpServers]);
|
|
911
|
+
const deleteSavedMcp = useCallback((server) => {
|
|
912
|
+
syncStoredMcpServers(mcpServers.filter((item) => item.id !== server.id));
|
|
913
|
+
setMcpModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, mcpRows.length - 2))));
|
|
914
|
+
}, [mcpRows.length, mcpServers, syncStoredMcpServers]);
|
|
915
|
+
const openAgentsModal = useCallback(() => {
|
|
916
|
+
setSubAgents(loadValidSubAgents());
|
|
917
|
+
setAgentsSearchQuery("");
|
|
918
|
+
setAgentsModalIndex(0);
|
|
919
|
+
setEditingSubagent(null);
|
|
920
|
+
setAgentsEditorError(null);
|
|
921
|
+
setShowAgentsEditor(false);
|
|
922
|
+
setShowAgentsModal(true);
|
|
923
|
+
}, []);
|
|
924
|
+
const openScheduleModal = useCallback(() => {
|
|
925
|
+
void agent
|
|
926
|
+
.listSchedules()
|
|
927
|
+
.then((latest) => {
|
|
928
|
+
setSchedules(latest);
|
|
929
|
+
setScheduleSearchQuery("");
|
|
930
|
+
setScheduleModalIndex(0);
|
|
931
|
+
setShowScheduleModal(true);
|
|
932
|
+
})
|
|
933
|
+
.catch((err) => {
|
|
934
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
935
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Failed to load schedules: ${message}`)]);
|
|
936
|
+
});
|
|
937
|
+
}, [agent]);
|
|
938
|
+
const showScheduleDetails = useCallback((schedule) => {
|
|
939
|
+
void agent
|
|
940
|
+
.getScheduleDaemonStatus()
|
|
941
|
+
.then((status) => {
|
|
942
|
+
setMessages((prev) => [...prev, buildAssistantEntry(formatScheduleDetails(schedule, status))]);
|
|
943
|
+
setShowScheduleModal(false);
|
|
944
|
+
setScheduleSearchQuery("");
|
|
945
|
+
setTimeout(() => {
|
|
946
|
+
try {
|
|
947
|
+
scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
/* */
|
|
951
|
+
}
|
|
952
|
+
}, 10);
|
|
953
|
+
})
|
|
954
|
+
.catch((err) => {
|
|
955
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
956
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Failed to load schedule details: ${message}`)]);
|
|
957
|
+
});
|
|
958
|
+
}, [agent]);
|
|
959
|
+
const removeSchedule = useCallback((schedule) => {
|
|
960
|
+
void agent
|
|
961
|
+
.removeSchedule(schedule.id)
|
|
962
|
+
.then(async (message) => {
|
|
963
|
+
const latest = await agent.listSchedules();
|
|
964
|
+
setSchedules(latest);
|
|
965
|
+
setScheduleModalIndex((index) => Math.max(0, Math.min(index, Math.max(0, latest.length - 1))));
|
|
966
|
+
setMessages((prev) => [...prev, buildAssistantEntry(message)]);
|
|
967
|
+
setTimeout(() => {
|
|
968
|
+
try {
|
|
969
|
+
scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
|
|
970
|
+
}
|
|
971
|
+
catch {
|
|
972
|
+
/* */
|
|
973
|
+
}
|
|
974
|
+
}, 10);
|
|
975
|
+
})
|
|
976
|
+
.catch((err) => {
|
|
977
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
978
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Failed to remove schedule: ${message}`)]);
|
|
979
|
+
});
|
|
980
|
+
}, [agent]);
|
|
981
|
+
const openSubagentEditor = useCallback((agent) => {
|
|
982
|
+
setEditingSubagent(agent);
|
|
983
|
+
if (agent) {
|
|
984
|
+
setAgentsEditorDraft({ name: agent.name, instruction: agent.instruction });
|
|
985
|
+
setAgentsEditorModelIndex(Math.max(0, MODELS.findIndex((model) => model.id === normalizeModelId(agent.model))));
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
setAgentsEditorDraft({ name: "", instruction: "" });
|
|
989
|
+
setAgentsEditorModelIndex(Math.max(0, MODELS.findIndex((model) => model.id === DEFAULT_MODEL)));
|
|
990
|
+
}
|
|
991
|
+
setAgentsEditorField("name");
|
|
992
|
+
setAgentsEditorError(null);
|
|
993
|
+
setAgentsEditorSyncKey((n) => n + 1);
|
|
994
|
+
setShowAgentsEditor(true);
|
|
995
|
+
setShowAgentsModal(true);
|
|
996
|
+
}, []);
|
|
997
|
+
const submitSubagentEditor = useCallback(() => {
|
|
998
|
+
const name = (subagentNameRef.current?.plainText || "").trim();
|
|
999
|
+
const instruction = subagentInstructionRef.current?.plainText || "";
|
|
1000
|
+
const model = MODELS[agentsEditorModelIndex]?.id;
|
|
1001
|
+
if (!name) {
|
|
1002
|
+
setAgentsEditorError("Name is required.");
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (isReservedSubagentName(name)) {
|
|
1006
|
+
setAgentsEditorError('Names "general" and "explore" are reserved.');
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (!model || !getModelIds().includes(model)) {
|
|
1010
|
+
setAgentsEditorError("Pick a valid model.");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const next = [...subAgents];
|
|
1014
|
+
if (editingSubagent) {
|
|
1015
|
+
const index = next.findIndex((item) => item.name === editingSubagent.name);
|
|
1016
|
+
if (index >= 0)
|
|
1017
|
+
next.splice(index, 1);
|
|
1018
|
+
}
|
|
1019
|
+
if (next.some((item) => item.name.toLowerCase() === name.toLowerCase())) {
|
|
1020
|
+
setAgentsEditorError("Another sub-agent already uses this name.");
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
next.push({ name, model, instruction });
|
|
1024
|
+
saveUserSettings({ subAgents: next });
|
|
1025
|
+
setSubAgents(loadValidSubAgents());
|
|
1026
|
+
setShowAgentsEditor(false);
|
|
1027
|
+
setEditingSubagent(null);
|
|
1028
|
+
setAgentsEditorError(null);
|
|
1029
|
+
}, [agentsEditorModelIndex, editingSubagent, subAgents]);
|
|
1030
|
+
const removeEditingSubagent = useCallback(() => {
|
|
1031
|
+
if (!editingSubagent)
|
|
1032
|
+
return;
|
|
1033
|
+
const next = subAgents.filter((item) => item.name !== editingSubagent.name);
|
|
1034
|
+
saveUserSettings({ subAgents: next });
|
|
1035
|
+
setSubAgents(loadValidSubAgents());
|
|
1036
|
+
setShowAgentsEditor(false);
|
|
1037
|
+
setEditingSubagent(null);
|
|
1038
|
+
setAgentsEditorError(null);
|
|
1039
|
+
setAgentsModalIndex(0);
|
|
1040
|
+
}, [editingSubagent, subAgents]);
|
|
1041
|
+
const submitMcpEditor = useCallback(() => {
|
|
1042
|
+
const draft = {
|
|
1043
|
+
label: mcpLabelRef.current?.plainText || "",
|
|
1044
|
+
transport: mcpEditorDraft.transport,
|
|
1045
|
+
url: mcpUrlRef.current?.plainText || "",
|
|
1046
|
+
headersText: mcpHeadersRef.current?.plainText || "",
|
|
1047
|
+
command: mcpCommandRef.current?.plainText || "",
|
|
1048
|
+
argsText: mcpArgsRef.current?.plainText || "",
|
|
1049
|
+
cwd: mcpCwdRef.current?.plainText || "",
|
|
1050
|
+
envText: mcpEnvRef.current?.plainText || "",
|
|
1051
|
+
};
|
|
1052
|
+
const baseId = toMcpServerId(draft.label);
|
|
1053
|
+
const currentServers = loadMcpServers();
|
|
1054
|
+
const conflictingServer = currentServers.find((s) => s.id === baseId && s.id !== editingMcpId);
|
|
1055
|
+
if (conflictingServer) {
|
|
1056
|
+
setMcpEditorError(`Only one protocol is supported per MCP. Edit "${conflictingServer.label}" instead.`);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const id = editingMcpId ?? baseId;
|
|
1060
|
+
const server = {
|
|
1061
|
+
id,
|
|
1062
|
+
label: draft.label.trim(),
|
|
1063
|
+
enabled: true,
|
|
1064
|
+
transport: draft.transport,
|
|
1065
|
+
...(draft.transport === "stdio"
|
|
1066
|
+
? {
|
|
1067
|
+
command: draft.command.trim(),
|
|
1068
|
+
args: draft.argsText
|
|
1069
|
+
.split("\n")
|
|
1070
|
+
.map((line) => line.trim())
|
|
1071
|
+
.filter(Boolean),
|
|
1072
|
+
cwd: draft.cwd.trim() || undefined,
|
|
1073
|
+
env: Object.keys(parseEnvLines(draft.envText)).length ? parseEnvLines(draft.envText) : undefined,
|
|
1074
|
+
}
|
|
1075
|
+
: {
|
|
1076
|
+
url: draft.url.trim(),
|
|
1077
|
+
headers: Object.keys(parseHeaderLines(draft.headersText)).length
|
|
1078
|
+
? parseHeaderLines(draft.headersText)
|
|
1079
|
+
: undefined,
|
|
1080
|
+
env: Object.keys(parseEnvLines(draft.envText)).length ? parseEnvLines(draft.envText) : undefined,
|
|
1081
|
+
}),
|
|
1082
|
+
};
|
|
1083
|
+
const validation = validateMcpServerConfig(server);
|
|
1084
|
+
if (!validation.ok) {
|
|
1085
|
+
setMcpEditorError(validation.error);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
const nextServers = editingMcpId
|
|
1089
|
+
? currentServers.map((item) => item.id === editingMcpId ? { ...server, id: editingMcpId, enabled: item.enabled } : item)
|
|
1090
|
+
: [...currentServers, server];
|
|
1091
|
+
saveMcpServers(nextServers);
|
|
1092
|
+
setMcpServers(nextServers);
|
|
1093
|
+
setShowMcpEditor(false);
|
|
1094
|
+
setEditingMcpId(null);
|
|
1095
|
+
setMcpEditorError(null);
|
|
1096
|
+
setMcpSearchQuery("");
|
|
1097
|
+
setMcpModalIndex(Math.max(0, nextServers.findIndex((item) => item.id === (editingMcpId ?? server.id))));
|
|
1098
|
+
}, [editingMcpId, mcpEditorDraft.transport]);
|
|
1099
|
+
const cycleMcpEditorTransport = useCallback((direction = 1) => {
|
|
1100
|
+
const draft = snapshotMcpEditorDraft();
|
|
1101
|
+
const order = ["stdio", "http", "sse"];
|
|
1102
|
+
const currentIndex = order.indexOf(draft.transport);
|
|
1103
|
+
const nextTransport = order[(currentIndex + direction + order.length) % order.length];
|
|
1104
|
+
const nextDraft = { ...draft, transport: nextTransport };
|
|
1105
|
+
setMcpEditorDraft(nextDraft);
|
|
1106
|
+
setMcpEditorField("transport");
|
|
1107
|
+
setMcpEditorSyncKey((n) => n + 1);
|
|
1108
|
+
if (!editingMcpId)
|
|
1109
|
+
return;
|
|
1110
|
+
const existing = mcpServers.find((server) => server.id === editingMcpId);
|
|
1111
|
+
if (!existing)
|
|
1112
|
+
return;
|
|
1113
|
+
const optimisticServer = {
|
|
1114
|
+
id: existing.id,
|
|
1115
|
+
label: nextDraft.label.trim() || existing.label,
|
|
1116
|
+
enabled: existing.enabled,
|
|
1117
|
+
transport: nextTransport,
|
|
1118
|
+
...(nextTransport === "stdio"
|
|
1119
|
+
? {
|
|
1120
|
+
command: nextDraft.command.trim() || existing.command,
|
|
1121
|
+
args: nextDraft.argsText
|
|
1122
|
+
.split("\n")
|
|
1123
|
+
.map((line) => line.trim())
|
|
1124
|
+
.filter(Boolean),
|
|
1125
|
+
cwd: nextDraft.cwd.trim() || undefined,
|
|
1126
|
+
env: Object.keys(parseEnvLines(nextDraft.envText)).length ? parseEnvLines(nextDraft.envText) : undefined,
|
|
1127
|
+
}
|
|
1128
|
+
: {
|
|
1129
|
+
url: nextDraft.url.trim() || existing.url,
|
|
1130
|
+
headers: Object.keys(parseHeaderLines(nextDraft.headersText)).length
|
|
1131
|
+
? parseHeaderLines(nextDraft.headersText)
|
|
1132
|
+
: undefined,
|
|
1133
|
+
env: Object.keys(parseEnvLines(nextDraft.envText)).length ? parseEnvLines(nextDraft.envText) : undefined,
|
|
1134
|
+
}),
|
|
1135
|
+
};
|
|
1136
|
+
syncStoredMcpServers(mcpServers.map((server) => (server.id === editingMcpId ? optimisticServer : server)));
|
|
1137
|
+
}, [editingMcpId, mcpServers, snapshotMcpEditorDraft, syncStoredMcpServers]);
|
|
1138
|
+
useEffect(() => {
|
|
1139
|
+
if (!showMcpEditor || !editingMcpId)
|
|
1140
|
+
return;
|
|
1141
|
+
const existing = mcpServers.find((server) => server.id === editingMcpId);
|
|
1142
|
+
if (!existing)
|
|
1143
|
+
return;
|
|
1144
|
+
if (existing.transport === mcpEditorDraft.transport)
|
|
1145
|
+
return;
|
|
1146
|
+
const syncedServer = {
|
|
1147
|
+
id: existing.id,
|
|
1148
|
+
label: mcpEditorDraft.label.trim() || existing.label,
|
|
1149
|
+
enabled: existing.enabled,
|
|
1150
|
+
transport: mcpEditorDraft.transport,
|
|
1151
|
+
...(mcpEditorDraft.transport === "stdio"
|
|
1152
|
+
? {
|
|
1153
|
+
command: mcpEditorDraft.command.trim() || undefined,
|
|
1154
|
+
args: mcpEditorDraft.argsText
|
|
1155
|
+
.split("\n")
|
|
1156
|
+
.map((line) => line.trim())
|
|
1157
|
+
.filter(Boolean),
|
|
1158
|
+
cwd: mcpEditorDraft.cwd.trim() || undefined,
|
|
1159
|
+
env: Object.keys(parseEnvLines(mcpEditorDraft.envText)).length
|
|
1160
|
+
? parseEnvLines(mcpEditorDraft.envText)
|
|
1161
|
+
: undefined,
|
|
1162
|
+
}
|
|
1163
|
+
: {
|
|
1164
|
+
url: mcpEditorDraft.url.trim() || undefined,
|
|
1165
|
+
headers: Object.keys(parseHeaderLines(mcpEditorDraft.headersText)).length
|
|
1166
|
+
? parseHeaderLines(mcpEditorDraft.headersText)
|
|
1167
|
+
: undefined,
|
|
1168
|
+
env: Object.keys(parseEnvLines(mcpEditorDraft.envText)).length
|
|
1169
|
+
? parseEnvLines(mcpEditorDraft.envText)
|
|
1170
|
+
: undefined,
|
|
1171
|
+
}),
|
|
1172
|
+
};
|
|
1173
|
+
syncStoredMcpServers(mcpServers.map((server) => (server.id === editingMcpId ? syncedServer : server)));
|
|
1174
|
+
}, [editingMcpId, mcpEditorDraft, mcpServers, showMcpEditor, syncStoredMcpServers]);
|
|
1175
|
+
useEffect(() => {
|
|
1176
|
+
setMcpModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, mcpRows.length - 1))));
|
|
1177
|
+
}, [mcpRows.length]);
|
|
1178
|
+
useEffect(() => {
|
|
1179
|
+
setScheduleModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, scheduleRows.length - 1))));
|
|
1180
|
+
}, [scheduleRows.length]);
|
|
1181
|
+
const scrollToBottom = useCallback(() => {
|
|
1182
|
+
try {
|
|
1183
|
+
scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
|
|
1184
|
+
}
|
|
1185
|
+
catch {
|
|
1186
|
+
/* */
|
|
1187
|
+
}
|
|
1188
|
+
}, []);
|
|
1189
|
+
const clearLiveTurnUi = useCallback(() => {
|
|
1190
|
+
setStreamContent("");
|
|
1191
|
+
setStreamReasoning("");
|
|
1192
|
+
setActiveToolCalls([]);
|
|
1193
|
+
setActiveSubagent(null);
|
|
1194
|
+
setLiveTurnSourceLabel(null);
|
|
1195
|
+
contentAccRef.current = "";
|
|
1196
|
+
}, []);
|
|
1197
|
+
const finishTurnProcessing = useCallback(() => {
|
|
1198
|
+
const nextQueued = queuedMessagesRef.current.shift();
|
|
1199
|
+
if (nextQueued) {
|
|
1200
|
+
setQueuedMessages(queuedMessagesRef.current.map((msg) => msg.displayText));
|
|
1201
|
+
isProcessingRef.current = false;
|
|
1202
|
+
void processMessageRef.current(nextQueued.text, nextQueued.displayText);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
isProcessingRef.current = false;
|
|
1206
|
+
setIsProcessing(false);
|
|
1207
|
+
}, []);
|
|
1208
|
+
const beginLiveTurn = useCallback((turn) => {
|
|
1209
|
+
clearLiveTurnUi();
|
|
1210
|
+
activeTurnRef.current = {
|
|
1211
|
+
...turn,
|
|
1212
|
+
latestAssistantText: "",
|
|
1213
|
+
flushedAssistantChars: 0,
|
|
1214
|
+
};
|
|
1215
|
+
isProcessingRef.current = true;
|
|
1216
|
+
setIsProcessing(true);
|
|
1217
|
+
setLiveTurnSourceLabel(turn.sourceLabel ?? null);
|
|
1218
|
+
startTimeRef.current = Date.now();
|
|
1219
|
+
}, [clearLiveTurnUi]);
|
|
1220
|
+
const flushPendingAssistantMessage = useCallback(() => {
|
|
1221
|
+
const activeTurn = activeTurnRef.current;
|
|
1222
|
+
if (!activeTurn)
|
|
1223
|
+
return;
|
|
1224
|
+
const cleaned = sanitizeContent(contentAccRef.current);
|
|
1225
|
+
if (!cleaned) {
|
|
1226
|
+
contentAccRef.current = "";
|
|
1227
|
+
setStreamContent("");
|
|
1228
|
+
if (activeTurn.kind === "telegram") {
|
|
1229
|
+
activeTurn.flushedAssistantChars = activeTurn.latestAssistantText.length;
|
|
1230
|
+
}
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
setMessages((prev) => [
|
|
1234
|
+
...prev,
|
|
1235
|
+
buildAssistantEntry(cleaned, {
|
|
1236
|
+
modeColor: activeTurn.modeColor,
|
|
1237
|
+
remoteKey: activeTurn.remoteKey,
|
|
1238
|
+
sourceLabel: activeTurn.sourceLabel,
|
|
1239
|
+
}),
|
|
1240
|
+
]);
|
|
1241
|
+
if (activeTurn.kind === "telegram") {
|
|
1242
|
+
activeTurn.flushedAssistantChars = activeTurn.latestAssistantText.length;
|
|
1243
|
+
}
|
|
1244
|
+
contentAccRef.current = "";
|
|
1245
|
+
setStreamContent("");
|
|
1246
|
+
}, []);
|
|
1247
|
+
const applyLocalAssistantDelta = useCallback((delta) => {
|
|
1248
|
+
contentAccRef.current += delta;
|
|
1249
|
+
setStreamContent(sanitizeContent(contentAccRef.current));
|
|
1250
|
+
setTimeout(scrollToBottom, 10);
|
|
1251
|
+
}, [scrollToBottom]);
|
|
1252
|
+
const applyTelegramAssistantPreview = useCallback((fullContent) => {
|
|
1253
|
+
const activeTurn = activeTurnRef.current;
|
|
1254
|
+
if (!activeTurn || activeTurn.kind !== "telegram")
|
|
1255
|
+
return;
|
|
1256
|
+
activeTurn.latestAssistantText = fullContent;
|
|
1257
|
+
contentAccRef.current = getUnflushedTelegramAssistantContent(fullContent, activeTurn.flushedAssistantChars);
|
|
1258
|
+
setStreamContent(sanitizeContent(contentAccRef.current));
|
|
1259
|
+
setTimeout(scrollToBottom, 10);
|
|
1260
|
+
}, [scrollToBottom]);
|
|
1261
|
+
const showLiveToolCalls = useCallback((toolCalls) => {
|
|
1262
|
+
flushPendingAssistantMessage();
|
|
1263
|
+
setActiveToolCalls(toolCalls);
|
|
1264
|
+
setTimeout(scrollToBottom, 10);
|
|
1265
|
+
}, [flushPendingAssistantMessage, scrollToBottom]);
|
|
1266
|
+
const appendLiveToolResult = useCallback((toolCall, toolResult) => {
|
|
1267
|
+
const activeTurn = activeTurnRef.current;
|
|
1268
|
+
if (!activeTurn)
|
|
1269
|
+
return;
|
|
1270
|
+
setMessages((prev) => [
|
|
1271
|
+
...prev,
|
|
1272
|
+
buildToolResultEntry(toolCall, toolResult, {
|
|
1273
|
+
modeColor: activeTurn.modeColor,
|
|
1274
|
+
remoteKey: activeTurn.remoteKey,
|
|
1275
|
+
sourceLabel: activeTurn.sourceLabel,
|
|
1276
|
+
}),
|
|
1277
|
+
]);
|
|
1278
|
+
if (toolResult.plan?.questions?.length) {
|
|
1279
|
+
setActivePlan(toolResult.plan);
|
|
1280
|
+
setPqs(initialPlanQuestionsState());
|
|
1281
|
+
}
|
|
1282
|
+
setActiveToolCalls([]);
|
|
1283
|
+
setTimeout(scrollToBottom, 10);
|
|
1284
|
+
}, [scrollToBottom]);
|
|
1285
|
+
const syncTelegramTurnEntries = useCallback((activeTurn) => {
|
|
1286
|
+
if (activeTurn.kind !== "telegram" || activeTurn.userId === undefined || !activeTurn.remoteKey)
|
|
1287
|
+
return;
|
|
1288
|
+
const currentEntries = activeTurn.agent.getChatEntries();
|
|
1289
|
+
const syncedCount = telegramEntryCountsRef.current.get(activeTurn.userId) ?? 0;
|
|
1290
|
+
if (currentEntries.length <= syncedCount)
|
|
1291
|
+
return;
|
|
1292
|
+
const delta = decorateTelegramEntries(currentEntries.slice(syncedCount), activeTurn.userId, activeTurn.remoteKey);
|
|
1293
|
+
telegramEntryCountsRef.current.set(activeTurn.userId, currentEntries.length);
|
|
1294
|
+
setMessages((prev) => replaceTurnEntries(prev, activeTurn.remoteKey, delta));
|
|
1295
|
+
}, []);
|
|
1296
|
+
const finalizeActiveTurn = useCallback(({ wasInterrupted = false, hadError = false } = {}) => {
|
|
1297
|
+
const activeTurn = activeTurnRef.current;
|
|
1298
|
+
if (!activeTurn) {
|
|
1299
|
+
finishTurnProcessing();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const finalContent = sanitizeContent(contentAccRef.current);
|
|
1303
|
+
if (!wasInterrupted && finalContent) {
|
|
1304
|
+
setMessages((prev) => [
|
|
1305
|
+
...prev,
|
|
1306
|
+
buildAssistantEntry(finalContent, {
|
|
1307
|
+
modeColor: activeTurn.modeColor,
|
|
1308
|
+
remoteKey: activeTurn.remoteKey,
|
|
1309
|
+
sourceLabel: activeTurn.sourceLabel,
|
|
1310
|
+
}),
|
|
1311
|
+
]);
|
|
1312
|
+
}
|
|
1313
|
+
if (!wasInterrupted && !hadError) {
|
|
1314
|
+
if (activeTurn.kind === "local" && activeTurn.agent.getSessionId()) {
|
|
1315
|
+
setMessages((prev) => {
|
|
1316
|
+
const fresh = activeTurn.agent.getChatEntries();
|
|
1317
|
+
let prevUserIdx = 0;
|
|
1318
|
+
for (let i = 0; i < fresh.length; i++) {
|
|
1319
|
+
if (fresh[i].type !== "user")
|
|
1320
|
+
continue;
|
|
1321
|
+
while (prevUserIdx < prev.length && prev[prevUserIdx].type !== "user")
|
|
1322
|
+
prevUserIdx++;
|
|
1323
|
+
if (prevUserIdx < prev.length) {
|
|
1324
|
+
fresh[i] = { ...fresh[i], content: prev[prevUserIdx].content };
|
|
1325
|
+
prevUserIdx++;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return fresh;
|
|
1329
|
+
});
|
|
1330
|
+
setSessionTitle(activeTurn.agent.getSessionTitle());
|
|
1331
|
+
setSessionId(activeTurn.agent.getSessionId());
|
|
1332
|
+
}
|
|
1333
|
+
else if (activeTurn.kind === "telegram") {
|
|
1334
|
+
syncTelegramTurnEntries(activeTurn);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
activeTurnRef.current = null;
|
|
1338
|
+
clearLiveTurnUi();
|
|
1339
|
+
finishTurnProcessing();
|
|
1340
|
+
setTimeout(scrollToBottom, 50);
|
|
1341
|
+
}, [clearLiveTurnUi, finishTurnProcessing, scrollToBottom, syncTelegramTurnEntries]);
|
|
1342
|
+
const wireTelegramAgentUi = useCallback((userId, telegramAgent) => {
|
|
1343
|
+
if (!telegramEntryCountsRef.current.has(userId)) {
|
|
1344
|
+
telegramEntryCountsRef.current.set(userId, telegramAgent.getChatEntries().length);
|
|
1345
|
+
}
|
|
1346
|
+
if (telegramSubagentUnsubsRef.current.has(userId)) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const unsubscribe = telegramAgent.onSubagentStatus((status) => {
|
|
1350
|
+
if (activeTurnRef.current?.agent !== telegramAgent)
|
|
1351
|
+
return;
|
|
1352
|
+
setActiveSubagent(status);
|
|
1353
|
+
});
|
|
1354
|
+
telegramSubagentUnsubsRef.current.set(userId, unsubscribe);
|
|
1355
|
+
}, []);
|
|
1356
|
+
const getTelegramAgent = useCallback((userId) => {
|
|
1357
|
+
const map = telegramAgentsRef.current;
|
|
1358
|
+
const existing = map.get(userId);
|
|
1359
|
+
if (existing) {
|
|
1360
|
+
wireTelegramAgentUi(userId, existing);
|
|
1361
|
+
return existing;
|
|
1362
|
+
}
|
|
1363
|
+
const apiKey = getApiKey();
|
|
1364
|
+
if (!apiKey) {
|
|
1365
|
+
throw new Error("API key required. Set MUONROI_API_KEY or add via CLI.");
|
|
1366
|
+
}
|
|
1367
|
+
const u = loadUserSettings();
|
|
1368
|
+
const sid = u.telegram?.sessionsByUserId?.[String(userId)];
|
|
1369
|
+
const a = new Agent(apiKey, startupConfig.baseURL, startupConfig.model, startupConfig.maxToolRounds, {
|
|
1370
|
+
session: sid,
|
|
1371
|
+
sandboxMode,
|
|
1372
|
+
sandboxSettings,
|
|
1373
|
+
});
|
|
1374
|
+
if (!sid && a.getSessionId()) {
|
|
1375
|
+
saveUserSettings({
|
|
1376
|
+
telegram: {
|
|
1377
|
+
...u.telegram,
|
|
1378
|
+
sessionsByUserId: {
|
|
1379
|
+
...u.telegram?.sessionsByUserId,
|
|
1380
|
+
[String(userId)]: a.getSessionId(),
|
|
1381
|
+
},
|
|
1382
|
+
},
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
wireTelegramAgentUi(userId, a);
|
|
1386
|
+
map.set(userId, a);
|
|
1387
|
+
return a;
|
|
1388
|
+
}, [sandboxMode, sandboxSettings, startupConfig, wireTelegramAgentUi]);
|
|
1389
|
+
const appendTelegramUserMessage = useCallback((event) => {
|
|
1390
|
+
const telegramAgent = getTelegramAgent(event.userId);
|
|
1391
|
+
beginLiveTurn({
|
|
1392
|
+
kind: "telegram",
|
|
1393
|
+
agent: telegramAgent,
|
|
1394
|
+
remoteKey: event.turnKey,
|
|
1395
|
+
userId: event.userId,
|
|
1396
|
+
sourceLabel: getTelegramSourceLabel("assistant", event.userId),
|
|
1397
|
+
});
|
|
1398
|
+
setMessages((prev) => [
|
|
1399
|
+
...prev,
|
|
1400
|
+
buildUserEntry(event.content, {
|
|
1401
|
+
remoteKey: event.turnKey,
|
|
1402
|
+
sourceLabel: getTelegramSourceLabel("user", event.userId),
|
|
1403
|
+
}),
|
|
1404
|
+
]);
|
|
1405
|
+
setTimeout(scrollToBottom, 10);
|
|
1406
|
+
}, [beginLiveTurn, getTelegramAgent, scrollToBottom]);
|
|
1407
|
+
const upsertTelegramAssistantMessage = useCallback((event) => {
|
|
1408
|
+
if (activeTurnRef.current?.remoteKey !== event.turnKey) {
|
|
1409
|
+
const telegramAgent = getTelegramAgent(event.userId);
|
|
1410
|
+
beginLiveTurn({
|
|
1411
|
+
kind: "telegram",
|
|
1412
|
+
agent: telegramAgent,
|
|
1413
|
+
remoteKey: event.turnKey,
|
|
1414
|
+
userId: event.userId,
|
|
1415
|
+
sourceLabel: getTelegramSourceLabel("assistant", event.userId),
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
applyTelegramAssistantPreview(event.content);
|
|
1419
|
+
if (event.done) {
|
|
1420
|
+
finalizeActiveTurn();
|
|
1421
|
+
}
|
|
1422
|
+
}, [applyTelegramAssistantPreview, beginLiveTurn, finalizeActiveTurn, getTelegramAgent]);
|
|
1423
|
+
const showTelegramToolCalls = useCallback((event) => {
|
|
1424
|
+
if (activeTurnRef.current?.remoteKey !== event.turnKey) {
|
|
1425
|
+
const telegramAgent = getTelegramAgent(event.userId);
|
|
1426
|
+
beginLiveTurn({
|
|
1427
|
+
kind: "telegram",
|
|
1428
|
+
agent: telegramAgent,
|
|
1429
|
+
remoteKey: event.turnKey,
|
|
1430
|
+
userId: event.userId,
|
|
1431
|
+
sourceLabel: getTelegramSourceLabel("assistant", event.userId),
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
showLiveToolCalls(event.toolCalls);
|
|
1435
|
+
}, [beginLiveTurn, getTelegramAgent, showLiveToolCalls]);
|
|
1436
|
+
const appendTelegramToolResult = useCallback((event) => {
|
|
1437
|
+
if (activeTurnRef.current?.remoteKey !== event.turnKey) {
|
|
1438
|
+
const telegramAgent = getTelegramAgent(event.userId);
|
|
1439
|
+
beginLiveTurn({
|
|
1440
|
+
kind: "telegram",
|
|
1441
|
+
agent: telegramAgent,
|
|
1442
|
+
remoteKey: event.turnKey,
|
|
1443
|
+
userId: event.userId,
|
|
1444
|
+
sourceLabel: getTelegramSourceLabel("assistant", event.userId),
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
appendLiveToolResult(event.toolCall, event.toolResult);
|
|
1448
|
+
}, [appendLiveToolResult, beginLiveTurn, getTelegramAgent]);
|
|
1449
|
+
const startTelegramBridge = useCallback(() => {
|
|
1450
|
+
const token = getTelegramBotToken();
|
|
1451
|
+
if (!token || !getApiKey())
|
|
1452
|
+
return;
|
|
1453
|
+
if (bridgeRef.current)
|
|
1454
|
+
return;
|
|
1455
|
+
const bridge = createTelegramBridge({
|
|
1456
|
+
token,
|
|
1457
|
+
getApprovedUserIds: () => loadUserSettings().telegram?.approvedUserIds ?? [],
|
|
1458
|
+
coordinator: coordinatorRef.current,
|
|
1459
|
+
getTelegramAgent,
|
|
1460
|
+
onUserMessage: appendTelegramUserMessage,
|
|
1461
|
+
onAssistantMessage: upsertTelegramAssistantMessage,
|
|
1462
|
+
onToolCalls: showTelegramToolCalls,
|
|
1463
|
+
onToolResult: appendTelegramToolResult,
|
|
1464
|
+
onError: (msg) => {
|
|
1465
|
+
setMessages((p) => [...p, { type: "assistant", content: `Telegram: ${msg}`, timestamp: new Date() }]);
|
|
1466
|
+
},
|
|
1467
|
+
});
|
|
1468
|
+
bridgeRef.current = bridge;
|
|
1469
|
+
bridge.start();
|
|
1470
|
+
}, [
|
|
1471
|
+
appendTelegramToolResult,
|
|
1472
|
+
appendTelegramUserMessage,
|
|
1473
|
+
getTelegramAgent,
|
|
1474
|
+
showTelegramToolCalls,
|
|
1475
|
+
upsertTelegramAssistantMessage,
|
|
1476
|
+
]);
|
|
1477
|
+
/** Start long polling when a bot token is already saved (pairing UI is optional if already approved). */
|
|
1478
|
+
useEffect(() => {
|
|
1479
|
+
if (!hasApiKey)
|
|
1480
|
+
return;
|
|
1481
|
+
if (!getTelegramBotToken())
|
|
1482
|
+
return;
|
|
1483
|
+
startTelegramBridge();
|
|
1484
|
+
}, [hasApiKey, startTelegramBridge]);
|
|
1485
|
+
const handleExit = useCallback(() => {
|
|
1486
|
+
// Best-effort EE session-end reconciliation (fire-and-forget)
|
|
1487
|
+
try {
|
|
1488
|
+
const { getDefaultEEClient: getEE, getLastSurfacedState: getSurfaced } = require("../ee/intercept.js");
|
|
1489
|
+
const { surfacedIds, timestamp } = getSurfaced();
|
|
1490
|
+
const ee = getEE();
|
|
1491
|
+
const cwd = agent.getCwd();
|
|
1492
|
+
// Prompt-stale: mark surfaced suggestions as stale on session end
|
|
1493
|
+
if (surfacedIds.length > 0) {
|
|
1494
|
+
ee.promptStale({
|
|
1495
|
+
state: { surfacedIds, timestamp },
|
|
1496
|
+
nextPromptMeta: { trigger: "session-end", cwd, tenantId: "local" },
|
|
1497
|
+
}).catch(() => { });
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
catch {
|
|
1501
|
+
// Swallow all errors — exit must never fail due to EE
|
|
1502
|
+
}
|
|
1503
|
+
void bridgeRef.current?.stop();
|
|
1504
|
+
bridgeRef.current = null;
|
|
1505
|
+
onExit?.();
|
|
1506
|
+
}, [onExit, agent]);
|
|
1507
|
+
const showCopyBanner = useCallback(() => {
|
|
1508
|
+
setCopyFlashId((n) => n + 1);
|
|
1509
|
+
}, []);
|
|
1510
|
+
/** Match OpenCode: OSC 52 + real OS clipboard; used from keyboard and root onMouseUp. */
|
|
1511
|
+
const copyTuiSelectionToHost = useCallback(() => {
|
|
1512
|
+
if (!renderer.hasSelection)
|
|
1513
|
+
return false;
|
|
1514
|
+
const sel = renderer.getSelection();
|
|
1515
|
+
const text = sel ? getCompactTuiSelectionText(sel) : "";
|
|
1516
|
+
if (!text)
|
|
1517
|
+
return false;
|
|
1518
|
+
renderer.copyToClipboardOSC52(text);
|
|
1519
|
+
copyTextToHostClipboard(text);
|
|
1520
|
+
renderer.clearSelection();
|
|
1521
|
+
showCopyBanner();
|
|
1522
|
+
return true;
|
|
1523
|
+
}, [renderer, showCopyBanner]);
|
|
1524
|
+
const handleRootMouseUp = useCallback(() => {
|
|
1525
|
+
copyTuiSelectionToHost();
|
|
1526
|
+
inputRef.current?.focus();
|
|
1527
|
+
}, [copyTuiSelectionToHost]);
|
|
1528
|
+
const handleRootMouseDown = useCallback(() => {
|
|
1529
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
1530
|
+
}, []);
|
|
1531
|
+
useEffect(() => {
|
|
1532
|
+
if (copyFlashId === 0)
|
|
1533
|
+
return;
|
|
1534
|
+
const id = setTimeout(() => setCopyFlashId(0), 2000);
|
|
1535
|
+
return () => clearTimeout(id);
|
|
1536
|
+
}, [copyFlashId]);
|
|
1537
|
+
const openApiKeyModal = useCallback(() => {
|
|
1538
|
+
showApiKeyModalRef.current = true;
|
|
1539
|
+
setApiKeyError(null);
|
|
1540
|
+
setShowApiKeyModal(true);
|
|
1541
|
+
}, []);
|
|
1542
|
+
const closeApiKeyModal = useCallback(() => {
|
|
1543
|
+
showApiKeyModalRef.current = false;
|
|
1544
|
+
setApiKeyError(null);
|
|
1545
|
+
setShowApiKeyModal(false);
|
|
1546
|
+
}, []);
|
|
1547
|
+
const submitApiKey = useCallback(() => {
|
|
1548
|
+
const apiKey = (apiKeyInputRef.current?.plainText || "").trim();
|
|
1549
|
+
if (!apiKey) {
|
|
1550
|
+
setApiKeyError("Enter an API key to continue.");
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
if (!apiKey.startsWith("xai-")) {
|
|
1554
|
+
setApiKeyError("API keys should start with xai-.");
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
saveUserSettings({ apiKey });
|
|
1558
|
+
agent.setApiKey(apiKey);
|
|
1559
|
+
hasApiKeyRef.current = true;
|
|
1560
|
+
showApiKeyModalRef.current = false;
|
|
1561
|
+
setHasApiKey(true);
|
|
1562
|
+
setApiKeyError(null);
|
|
1563
|
+
setShowApiKeyModal(false);
|
|
1564
|
+
apiKeyInputRef.current?.clear();
|
|
1565
|
+
if (getTelegramBotToken()) {
|
|
1566
|
+
startTelegramBridge();
|
|
1567
|
+
}
|
|
1568
|
+
}, [agent, startTelegramBridge]);
|
|
1569
|
+
useEffect(() => {
|
|
1570
|
+
hasApiKeyRef.current = hasApiKey;
|
|
1571
|
+
}, [hasApiKey]);
|
|
1572
|
+
useEffect(() => {
|
|
1573
|
+
showApiKeyModalRef.current = showApiKeyModal;
|
|
1574
|
+
}, [showApiKeyModal]);
|
|
1575
|
+
useEffect(() => {
|
|
1576
|
+
showConnectModalRef.current = showConnectModal;
|
|
1577
|
+
}, [showConnectModal]);
|
|
1578
|
+
useEffect(() => {
|
|
1579
|
+
showTelegramTokenModalRef.current = showTelegramTokenModal;
|
|
1580
|
+
}, [showTelegramTokenModal]);
|
|
1581
|
+
useEffect(() => {
|
|
1582
|
+
showTelegramPairModalRef.current = showTelegramPairModal;
|
|
1583
|
+
}, [showTelegramPairModal]);
|
|
1584
|
+
useEffect(() => {
|
|
1585
|
+
showMcpModalRef.current = showMcpModal;
|
|
1586
|
+
}, [showMcpModal]);
|
|
1587
|
+
useEffect(() => {
|
|
1588
|
+
showMcpEditorRef.current = showMcpEditor;
|
|
1589
|
+
}, [showMcpEditor]);
|
|
1590
|
+
useEffect(() => {
|
|
1591
|
+
showAgentsModalRef.current = showAgentsModal;
|
|
1592
|
+
}, [showAgentsModal]);
|
|
1593
|
+
useEffect(() => {
|
|
1594
|
+
showAgentsEditorRef.current = showAgentsEditor;
|
|
1595
|
+
}, [showAgentsEditor]);
|
|
1596
|
+
useEffect(() => {
|
|
1597
|
+
showScheduleModalRef.current = showScheduleModal;
|
|
1598
|
+
}, [showScheduleModal]);
|
|
1599
|
+
useEffect(() => {
|
|
1600
|
+
showUpdateModalRef.current = showUpdateModal;
|
|
1601
|
+
}, [showUpdateModal]);
|
|
1602
|
+
useEffect(() => {
|
|
1603
|
+
let cancelled = false;
|
|
1604
|
+
checkForUpdate(startupConfig.version).then((result) => {
|
|
1605
|
+
if (cancelled || !result?.hasUpdate)
|
|
1606
|
+
return;
|
|
1607
|
+
setUpdateInfo(result);
|
|
1608
|
+
setShowUpdateModal(true);
|
|
1609
|
+
});
|
|
1610
|
+
return () => {
|
|
1611
|
+
cancelled = true;
|
|
1612
|
+
};
|
|
1613
|
+
}, [startupConfig.version]);
|
|
1614
|
+
useEffect(() => {
|
|
1615
|
+
return () => {
|
|
1616
|
+
void bridgeRef.current?.stop();
|
|
1617
|
+
bridgeRef.current = null;
|
|
1618
|
+
};
|
|
1619
|
+
}, []);
|
|
1620
|
+
const submitTelegramToken = useCallback(() => {
|
|
1621
|
+
const token = (telegramTokenInputRef.current?.plainText || "").trim();
|
|
1622
|
+
if (!token) {
|
|
1623
|
+
setTelegramTokenError("Paste your bot token from @BotFather.");
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
if (!getApiKey()) {
|
|
1627
|
+
setTelegramTokenError("Add an API key first.");
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
const u = loadUserSettings();
|
|
1631
|
+
saveUserSettings({ telegram: { ...u.telegram, botToken: token } });
|
|
1632
|
+
telegramTokenInputRef.current?.clear();
|
|
1633
|
+
setShowTelegramTokenModal(false);
|
|
1634
|
+
setTelegramTokenError(null);
|
|
1635
|
+
startTelegramBridge();
|
|
1636
|
+
setShowTelegramPairModal(true);
|
|
1637
|
+
setTelegramPairError(null);
|
|
1638
|
+
setMessages((p) => [
|
|
1639
|
+
...p,
|
|
1640
|
+
{
|
|
1641
|
+
type: "assistant",
|
|
1642
|
+
content: "Telegram polling started. In Telegram, DM your bot and send /pair. Copy the code, then enter it below.",
|
|
1643
|
+
timestamp: new Date(),
|
|
1644
|
+
},
|
|
1645
|
+
]);
|
|
1646
|
+
}, [startTelegramBridge]);
|
|
1647
|
+
const submitTelegramPair = useCallback(async () => {
|
|
1648
|
+
const code = (telegramPairInputRef.current?.plainText || "").trim();
|
|
1649
|
+
if (!code) {
|
|
1650
|
+
setTelegramPairError("Enter the pairing code.");
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
const result = approvePairingCode(code);
|
|
1654
|
+
if (!result.ok) {
|
|
1655
|
+
setTelegramPairError(result.error);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
saveApprovedTelegramUserId(result.userId);
|
|
1659
|
+
telegramPairInputRef.current?.clear();
|
|
1660
|
+
setShowTelegramPairModal(false);
|
|
1661
|
+
setTelegramPairError(null);
|
|
1662
|
+
setMessages((p) => [
|
|
1663
|
+
...p,
|
|
1664
|
+
{
|
|
1665
|
+
type: "assistant",
|
|
1666
|
+
content: `Telegram user ${result.userId} paired. Keep this CLI open while you use the bot.`,
|
|
1667
|
+
timestamp: new Date(),
|
|
1668
|
+
},
|
|
1669
|
+
]);
|
|
1670
|
+
try {
|
|
1671
|
+
await bridgeRef.current?.sendDm(result.userId, "Pairing approved. You can message muonroi-cli here.");
|
|
1672
|
+
}
|
|
1673
|
+
catch {
|
|
1674
|
+
/* optional DM */
|
|
1675
|
+
}
|
|
1676
|
+
}, []);
|
|
1677
|
+
const beginTelegramFromConnect = useCallback(() => {
|
|
1678
|
+
setShowConnectModal(false);
|
|
1679
|
+
if (!getApiKey()) {
|
|
1680
|
+
setMessages((p) => [...p, { type: "assistant", content: "Add an API key first.", timestamp: new Date() }]);
|
|
1681
|
+
openApiKeyModal();
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
if (!getTelegramBotToken()) {
|
|
1685
|
+
setShowTelegramTokenModal(true);
|
|
1686
|
+
setTelegramTokenError(null);
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
startTelegramBridge();
|
|
1690
|
+
const alreadyPaired = (loadUserSettings().telegram?.approvedUserIds?.length ?? 0) > 0;
|
|
1691
|
+
if (!alreadyPaired) {
|
|
1692
|
+
setShowTelegramPairModal(true);
|
|
1693
|
+
setTelegramPairError(null);
|
|
1694
|
+
setMessages((p) => [
|
|
1695
|
+
...p,
|
|
1696
|
+
{
|
|
1697
|
+
type: "assistant",
|
|
1698
|
+
content: "Telegram polling started. In Telegram, DM your bot and send /pair. Copy the code, then enter it below.",
|
|
1699
|
+
timestamp: new Date(),
|
|
1700
|
+
},
|
|
1701
|
+
]);
|
|
1702
|
+
}
|
|
1703
|
+
else {
|
|
1704
|
+
setMessages((p) => [
|
|
1705
|
+
...p,
|
|
1706
|
+
{
|
|
1707
|
+
type: "assistant",
|
|
1708
|
+
content: "Telegram polling is running. Your chat is already paired.",
|
|
1709
|
+
timestamp: new Date(),
|
|
1710
|
+
},
|
|
1711
|
+
]);
|
|
1712
|
+
}
|
|
1713
|
+
}, [openApiKeyModal, startTelegramBridge]);
|
|
1714
|
+
const interruptActiveRun = useCallback((key) => {
|
|
1715
|
+
if (btwStateRef.current) {
|
|
1716
|
+
btwAbortRef.current?.abort();
|
|
1717
|
+
btwAbortRef.current = null;
|
|
1718
|
+
btwStateRef.current = null;
|
|
1719
|
+
setBtwState(null);
|
|
1720
|
+
key?.preventDefault();
|
|
1721
|
+
key?.stopPropagation();
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
if (!isProcessingRef.current)
|
|
1725
|
+
return false;
|
|
1726
|
+
key?.preventDefault();
|
|
1727
|
+
key?.stopPropagation();
|
|
1728
|
+
interruptedRunIdRef.current = activeRunIdRef.current;
|
|
1729
|
+
queuedMessagesRef.current = [];
|
|
1730
|
+
setQueuedMessages([]);
|
|
1731
|
+
const activeAgent = activeTurnRef.current?.agent ?? agent;
|
|
1732
|
+
activeTurnRef.current = null;
|
|
1733
|
+
clearLiveTurnUi();
|
|
1734
|
+
activeAgent.abort();
|
|
1735
|
+
return true;
|
|
1736
|
+
}, [agent, clearLiveTurnUi]);
|
|
1737
|
+
useEffect(() => {
|
|
1738
|
+
const onInternalKey = (key) => {
|
|
1739
|
+
if (isEscapeKey(key)) {
|
|
1740
|
+
interruptActiveRun(key);
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
renderer._internalKeyInput.onInternal("keypress", onInternalKey);
|
|
1744
|
+
return () => {
|
|
1745
|
+
renderer._internalKeyInput.offInternal("keypress", onInternalKey);
|
|
1746
|
+
};
|
|
1747
|
+
}, [interruptActiveRun, renderer]);
|
|
1748
|
+
useEffect(() => {
|
|
1749
|
+
const onRawInput = (sequence) => {
|
|
1750
|
+
const parsed = parseKeypress(sequence, { useKittyKeyboard: renderer.useKittyKeyboard });
|
|
1751
|
+
if (parsed?.name === "escape" || sequence === "\u001b" || sequence === "\u001b\u001b") {
|
|
1752
|
+
return interruptActiveRun();
|
|
1753
|
+
}
|
|
1754
|
+
return false;
|
|
1755
|
+
};
|
|
1756
|
+
renderer.prependInputHandler(onRawInput);
|
|
1757
|
+
return () => {
|
|
1758
|
+
renderer.removeInputHandler(onRawInput);
|
|
1759
|
+
};
|
|
1760
|
+
}, [interruptActiveRun, renderer]);
|
|
1761
|
+
useEffect(() => {
|
|
1762
|
+
const onStdinData = (chunk) => {
|
|
1763
|
+
const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1764
|
+
if (data.length === 1 && data[0] === 27) {
|
|
1765
|
+
interruptActiveRun();
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
renderer.stdin.on("data", onStdinData);
|
|
1769
|
+
return () => {
|
|
1770
|
+
renderer.stdin.off("data", onStdinData);
|
|
1771
|
+
};
|
|
1772
|
+
}, [interruptActiveRun, renderer]);
|
|
1773
|
+
const resetToNewSession = useCallback(() => {
|
|
1774
|
+
const snapshot = agent.startNewSession();
|
|
1775
|
+
setMessages(snapshot?.entries ?? []);
|
|
1776
|
+
setExpandedMessages(new Set());
|
|
1777
|
+
activeTurnRef.current = null;
|
|
1778
|
+
clearLiveTurnUi();
|
|
1779
|
+
setSessionTitle(snapshot?.session.title ?? null);
|
|
1780
|
+
setSessionId(snapshot?.session.id ?? agent.getSessionId());
|
|
1781
|
+
setActivePlan(null);
|
|
1782
|
+
setPqs(initialPlanQuestionsState());
|
|
1783
|
+
replacePasteBlocks([]);
|
|
1784
|
+
queuedMessagesRef.current = [];
|
|
1785
|
+
setQueuedMessages([]);
|
|
1786
|
+
}, [agent, clearLiveTurnUi, replacePasteBlocks]);
|
|
1787
|
+
const processMessage = useCallback(async (text, displayText, images) => {
|
|
1788
|
+
if (!text.trim() || isProcessingRef.current)
|
|
1789
|
+
return;
|
|
1790
|
+
const runId = ++activeRunIdRef.current;
|
|
1791
|
+
const isStale = () => activeRunIdRef.current !== runId;
|
|
1792
|
+
isProcessingRef.current = true;
|
|
1793
|
+
setIsProcessing(true);
|
|
1794
|
+
if (!sessionTitle)
|
|
1795
|
+
agent
|
|
1796
|
+
.generateTitle((displayText ?? text).trim())
|
|
1797
|
+
.then(setSessionTitle)
|
|
1798
|
+
.catch(() => { });
|
|
1799
|
+
await coordinatorRef.current.run(async () => {
|
|
1800
|
+
const color = modeInfoRef.current.color;
|
|
1801
|
+
beginLiveTurn({ kind: "local", agent, modeColor: color });
|
|
1802
|
+
setMessages((prev) => [...prev, buildUserEntry((displayText ?? text).trim(), { modeColor: color })]);
|
|
1803
|
+
setTimeout(scrollToBottom, 50);
|
|
1804
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
1805
|
+
let turnHadError = false;
|
|
1806
|
+
let turnHadAuthError = false;
|
|
1807
|
+
try {
|
|
1808
|
+
for await (const chunk of agent.processMessage(text.trim(), undefined, images)) {
|
|
1809
|
+
if (isStale()) {
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
switch (chunk.type) {
|
|
1813
|
+
case "content":
|
|
1814
|
+
applyLocalAssistantDelta(chunk.content || "");
|
|
1815
|
+
break;
|
|
1816
|
+
case "reasoning":
|
|
1817
|
+
setStreamReasoning((p) => p + (chunk.content || ""));
|
|
1818
|
+
break;
|
|
1819
|
+
case "tool_calls":
|
|
1820
|
+
if (chunk.toolCalls) {
|
|
1821
|
+
showLiveToolCalls(chunk.toolCalls);
|
|
1822
|
+
}
|
|
1823
|
+
break;
|
|
1824
|
+
case "tool_result":
|
|
1825
|
+
if (chunk.toolCall && chunk.toolResult) {
|
|
1826
|
+
appendLiveToolResult(chunk.toolCall, chunk.toolResult);
|
|
1827
|
+
}
|
|
1828
|
+
break;
|
|
1829
|
+
case "structured_response":
|
|
1830
|
+
if (chunk.structuredResponse) {
|
|
1831
|
+
flushPendingAssistantMessage();
|
|
1832
|
+
setMessages((prev) => [
|
|
1833
|
+
...prev,
|
|
1834
|
+
{
|
|
1835
|
+
type: "structured_response",
|
|
1836
|
+
content: "",
|
|
1837
|
+
timestamp: new Date(),
|
|
1838
|
+
modeColor: activeTurnRef.current?.modeColor,
|
|
1839
|
+
structuredResponse: chunk.structuredResponse,
|
|
1840
|
+
},
|
|
1841
|
+
]);
|
|
1842
|
+
setTimeout(scrollToBottom, 10);
|
|
1843
|
+
}
|
|
1844
|
+
break;
|
|
1845
|
+
case "tool_approval_request":
|
|
1846
|
+
if (chunk.toolCall && chunk.approvalId) {
|
|
1847
|
+
let args = {};
|
|
1848
|
+
try {
|
|
1849
|
+
args = JSON.parse(chunk.toolCall.function.arguments);
|
|
1850
|
+
}
|
|
1851
|
+
catch {
|
|
1852
|
+
/* ignore */
|
|
1853
|
+
}
|
|
1854
|
+
const pc = chunk.paymentPrecheck;
|
|
1855
|
+
setPendingPaymentApproval({
|
|
1856
|
+
url: args?.url ?? "",
|
|
1857
|
+
description: pc?.description ?? "",
|
|
1858
|
+
security: pc?.security ?? "",
|
|
1859
|
+
securityLabel: pc?.securityLabel ?? "",
|
|
1860
|
+
securityUrl: pc?.securityUrl ?? "",
|
|
1861
|
+
amount: pc?.amount ?? "",
|
|
1862
|
+
network: pc?.network ?? "",
|
|
1863
|
+
asset: pc?.asset ?? "",
|
|
1864
|
+
approvalId: chunk.approvalId,
|
|
1865
|
+
selected: 0,
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
break;
|
|
1869
|
+
case "error":
|
|
1870
|
+
turnHadError = true;
|
|
1871
|
+
if (chunk.isAuthError) {
|
|
1872
|
+
turnHadAuthError = true;
|
|
1873
|
+
}
|
|
1874
|
+
contentAccRef.current += `\n${chunk.content || "Unknown error"}`;
|
|
1875
|
+
setStreamContent(contentAccRef.current);
|
|
1876
|
+
break;
|
|
1877
|
+
case "done":
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
catch {
|
|
1883
|
+
turnHadError = true;
|
|
1884
|
+
if (!isStale()) {
|
|
1885
|
+
contentAccRef.current += "\nAn unexpected error occurred.";
|
|
1886
|
+
setStreamContent(contentAccRef.current);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const wasInterrupted = interruptedRunIdRef.current === runId;
|
|
1890
|
+
if (isStale()) {
|
|
1891
|
+
contentAccRef.current = "";
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
if (turnHadAuthError) {
|
|
1895
|
+
setApiKeyError("Your API key is invalid or expired. Please enter a new key.");
|
|
1896
|
+
setShowApiKeyModal(true);
|
|
1897
|
+
showApiKeyModalRef.current = true;
|
|
1898
|
+
}
|
|
1899
|
+
if (!isStale()) {
|
|
1900
|
+
finalizeActiveTurn({ wasInterrupted, hadError: turnHadError });
|
|
1901
|
+
}
|
|
1902
|
+
if (wasInterrupted) {
|
|
1903
|
+
interruptedRunIdRef.current = null;
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
}, [
|
|
1907
|
+
agent,
|
|
1908
|
+
appendLiveToolResult,
|
|
1909
|
+
applyLocalAssistantDelta,
|
|
1910
|
+
beginLiveTurn,
|
|
1911
|
+
finalizeActiveTurn,
|
|
1912
|
+
scrollToBottom,
|
|
1913
|
+
sessionTitle,
|
|
1914
|
+
showLiveToolCalls,
|
|
1915
|
+
]);
|
|
1916
|
+
useEffect(() => {
|
|
1917
|
+
if (initialMessage && hasApiKey && !processedInitial.current) {
|
|
1918
|
+
processedInitial.current = true;
|
|
1919
|
+
processMessage(initialMessage);
|
|
1920
|
+
}
|
|
1921
|
+
}, [hasApiKey, initialMessage, processMessage]);
|
|
1922
|
+
useEffect(() => {
|
|
1923
|
+
processMessageRef.current = processMessage;
|
|
1924
|
+
}, [processMessage]);
|
|
1925
|
+
useEffect(() => agent.onSubagentStatus((status) => {
|
|
1926
|
+
if (activeTurnRef.current?.agent !== agent)
|
|
1927
|
+
return;
|
|
1928
|
+
setActiveSubagent(status);
|
|
1929
|
+
}), [agent]);
|
|
1930
|
+
useEffect(() => () => {
|
|
1931
|
+
for (const unsubscribe of telegramSubagentUnsubsRef.current.values()) {
|
|
1932
|
+
unsubscribe();
|
|
1933
|
+
}
|
|
1934
|
+
telegramSubagentUnsubsRef.current.clear();
|
|
1935
|
+
}, []);
|
|
1936
|
+
useEffect(() => {
|
|
1937
|
+
let active = true;
|
|
1938
|
+
const id = setInterval(() => {
|
|
1939
|
+
agent
|
|
1940
|
+
.consumeBackgroundNotifications()
|
|
1941
|
+
.then((notifications) => {
|
|
1942
|
+
if (!active || notifications.length === 0)
|
|
1943
|
+
return;
|
|
1944
|
+
setMessages((prev) => [
|
|
1945
|
+
...prev,
|
|
1946
|
+
...notifications.map((message) => ({
|
|
1947
|
+
type: "assistant",
|
|
1948
|
+
content: message,
|
|
1949
|
+
timestamp: new Date(),
|
|
1950
|
+
})),
|
|
1951
|
+
]);
|
|
1952
|
+
setTimeout(scrollToBottom, 10);
|
|
1953
|
+
})
|
|
1954
|
+
.catch(() => { });
|
|
1955
|
+
}, 2000);
|
|
1956
|
+
return () => {
|
|
1957
|
+
active = false;
|
|
1958
|
+
clearInterval(id);
|
|
1959
|
+
};
|
|
1960
|
+
}, [agent, scrollToBottom]);
|
|
1961
|
+
const handleCommand = useCallback((cmd) => {
|
|
1962
|
+
const c = cmd.trim().toLowerCase();
|
|
1963
|
+
if (c === "/clear") {
|
|
1964
|
+
resetToNewSession();
|
|
1965
|
+
return true;
|
|
1966
|
+
}
|
|
1967
|
+
if (c === "/model" || c === "/models") {
|
|
1968
|
+
setShowModelPicker(true);
|
|
1969
|
+
setModelPickerIndex(0);
|
|
1970
|
+
setModelSearchQuery("");
|
|
1971
|
+
return true;
|
|
1972
|
+
}
|
|
1973
|
+
if (c === "/sandbox") {
|
|
1974
|
+
openSandboxPicker();
|
|
1975
|
+
return true;
|
|
1976
|
+
}
|
|
1977
|
+
if (c === "/wallet") {
|
|
1978
|
+
openWalletPicker();
|
|
1979
|
+
return true;
|
|
1980
|
+
}
|
|
1981
|
+
if (c === "/remote-control") {
|
|
1982
|
+
setConnectModalIndex(0);
|
|
1983
|
+
setShowConnectModal(true);
|
|
1984
|
+
return true;
|
|
1985
|
+
}
|
|
1986
|
+
if (c === "/mcp" || c === "/mcps") {
|
|
1987
|
+
openMcpModal();
|
|
1988
|
+
return true;
|
|
1989
|
+
}
|
|
1990
|
+
if (c === "/agents" || c === "/agent") {
|
|
1991
|
+
openAgentsModal();
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
if (c === "/schedule" || c === "/schedules") {
|
|
1995
|
+
openScheduleModal();
|
|
1996
|
+
return true;
|
|
1997
|
+
}
|
|
1998
|
+
if (c === "/quit" || c === "/exit" || c === "/q") {
|
|
1999
|
+
handleExit();
|
|
2000
|
+
return true;
|
|
2001
|
+
}
|
|
2002
|
+
if (c === "/review") {
|
|
2003
|
+
processMessage(REVIEW_PROMPT);
|
|
2004
|
+
return true;
|
|
2005
|
+
}
|
|
2006
|
+
if (c === "/verify") {
|
|
2007
|
+
processMessage(buildVerifyPrompt(agent.getCwd()));
|
|
2008
|
+
return true;
|
|
2009
|
+
}
|
|
2010
|
+
if (c === "/commit-push") {
|
|
2011
|
+
processMessage(COMMIT_PUSH_PROMPT);
|
|
2012
|
+
return true;
|
|
2013
|
+
}
|
|
2014
|
+
if (c === "/commit-pr") {
|
|
2015
|
+
processMessage(COMMIT_PR_PROMPT);
|
|
2016
|
+
return true;
|
|
2017
|
+
}
|
|
2018
|
+
if (c.startsWith("/btw ") || c === "/btw") {
|
|
2019
|
+
const question = cmd.trim().slice(4).trim();
|
|
2020
|
+
if (!question) {
|
|
2021
|
+
setMessages((prev) => [
|
|
2022
|
+
...prev,
|
|
2023
|
+
buildAssistantEntry("Usage: /btw <question>\nExample: /btw what does useEffect cleanup do?"),
|
|
2024
|
+
]);
|
|
2025
|
+
return true;
|
|
2026
|
+
}
|
|
2027
|
+
const ac = new AbortController();
|
|
2028
|
+
btwAbortRef.current = ac;
|
|
2029
|
+
const loadingState = { status: "loading", question };
|
|
2030
|
+
btwStateRef.current = loadingState;
|
|
2031
|
+
setBtwState(loadingState);
|
|
2032
|
+
agent
|
|
2033
|
+
.askSideQuestion(question, ac.signal)
|
|
2034
|
+
.then((result) => {
|
|
2035
|
+
if (ac.signal.aborted)
|
|
2036
|
+
return;
|
|
2037
|
+
const doneState = { status: "done", question, answer: result.response };
|
|
2038
|
+
btwStateRef.current = doneState;
|
|
2039
|
+
setBtwState(doneState);
|
|
2040
|
+
})
|
|
2041
|
+
.catch((err) => {
|
|
2042
|
+
if (ac.signal.aborted)
|
|
2043
|
+
return;
|
|
2044
|
+
const errState = {
|
|
2045
|
+
status: "error",
|
|
2046
|
+
question,
|
|
2047
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2048
|
+
};
|
|
2049
|
+
btwStateRef.current = errState;
|
|
2050
|
+
setBtwState(errState);
|
|
2051
|
+
});
|
|
2052
|
+
return true;
|
|
2053
|
+
}
|
|
2054
|
+
const customSubagentCommand = parseCustomSubagentSlashCommand(cmd, subAgents);
|
|
2055
|
+
if (customSubagentCommand) {
|
|
2056
|
+
if (!customSubagentCommand.prompt) {
|
|
2057
|
+
setMessages((prev) => [
|
|
2058
|
+
...prev,
|
|
2059
|
+
buildAssistantEntry(`Usage: /${customSubagentCommand.agentName} <task>\nExample: /${customSubagentCommand.agentName} review the latest changes`),
|
|
2060
|
+
]);
|
|
2061
|
+
return true;
|
|
2062
|
+
}
|
|
2063
|
+
processMessage(buildCustomSubagentSlashPrompt(customSubagentCommand.agentName, customSubagentCommand.prompt));
|
|
2064
|
+
return true;
|
|
2065
|
+
}
|
|
2066
|
+
// Plan 06: fallback to slash registry (dispatchSlash) for custom commands like /route
|
|
2067
|
+
if (c.startsWith("/")) {
|
|
2068
|
+
const parts = c.slice(1).split(/\s+/);
|
|
2069
|
+
const name = parts[0] ?? "";
|
|
2070
|
+
const args = parts.slice(1);
|
|
2071
|
+
dispatchSlash(name, args, {
|
|
2072
|
+
cwd: agent.getCwd(),
|
|
2073
|
+
tenantId: "local",
|
|
2074
|
+
defaultProvider: "anthropic",
|
|
2075
|
+
defaultModel: model,
|
|
2076
|
+
lastPrompt: messages[messages.length - 1]?.content,
|
|
2077
|
+
}).then(async (result) => {
|
|
2078
|
+
if (result === null)
|
|
2079
|
+
return;
|
|
2080
|
+
if (result.startsWith("__COMPACT__")) {
|
|
2081
|
+
const flowDir = path.join(agent.getCwd(), ".muonroi-flow");
|
|
2082
|
+
try {
|
|
2083
|
+
const cr = await deliberateCompact(flowDir, agent.getMessages(), "", 4096);
|
|
2084
|
+
setMessages((prev) => [
|
|
2085
|
+
...prev,
|
|
2086
|
+
buildAssistantEntry(`Compaction: ${cr.decisionsExtracted} decisions extracted, ${cr.tokensBeforeCompress} → ${cr.tokensAfterCompress} tokens.\nSnapshot: ${cr.historyPath}`),
|
|
2087
|
+
]);
|
|
2088
|
+
}
|
|
2089
|
+
catch (e) {
|
|
2090
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Compaction failed: ${e}`)]);
|
|
2091
|
+
}
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
if (result.startsWith("__EXPAND__")) {
|
|
2095
|
+
const content = result.replace(/^__EXPAND__\n[^\n]*\n?/, "");
|
|
2096
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Restored session context:\n${content}`)]);
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
if (result.startsWith("__CLEAR__")) {
|
|
2100
|
+
const summary = result.replace(/^__CLEAR__\n/, "");
|
|
2101
|
+
agent.clearHistory();
|
|
2102
|
+
setMessages([buildAssistantEntry(`Session cleared and relocked.\n\n${summary}`)]);
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
if (result.startsWith("__COUNCIL__")) {
|
|
2106
|
+
const lines = result.split("\n");
|
|
2107
|
+
const roundsStr = lines[1] ?? "";
|
|
2108
|
+
const rounds = roundsStr ? parseInt(roundsStr, 10) : undefined;
|
|
2109
|
+
const topic = lines.slice(2).join("\n");
|
|
2110
|
+
setMessages((prev) => [...prev, buildUserEntry(`/council ${topic}`), buildAssistantEntry("Council convening...\n")]);
|
|
2111
|
+
try {
|
|
2112
|
+
const gen = agent.runCouncilRound(topic, undefined, rounds || undefined);
|
|
2113
|
+
for await (const chunk of gen) {
|
|
2114
|
+
if (chunk.type === "content") {
|
|
2115
|
+
setMessages((prev) => {
|
|
2116
|
+
const last = prev[prev.length - 1];
|
|
2117
|
+
if (last?.type === "assistant") {
|
|
2118
|
+
return [...prev.slice(0, -1), { ...last, content: (last.content ?? "") + chunk.content }];
|
|
2119
|
+
}
|
|
2120
|
+
return [...prev, buildAssistantEntry(chunk.content ?? "")];
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
if (chunk.type === "done")
|
|
2124
|
+
break;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
catch (e) {
|
|
2128
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Council error: ${e}`)]);
|
|
2129
|
+
}
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
setMessages((prev) => [...prev, buildAssistantEntry(result)]);
|
|
2133
|
+
});
|
|
2134
|
+
return true;
|
|
2135
|
+
}
|
|
2136
|
+
return false;
|
|
2137
|
+
}, [
|
|
2138
|
+
agent,
|
|
2139
|
+
handleExit,
|
|
2140
|
+
openAgentsModal,
|
|
2141
|
+
openMcpModal,
|
|
2142
|
+
openSandboxPicker,
|
|
2143
|
+
openWalletPicker,
|
|
2144
|
+
openScheduleModal,
|
|
2145
|
+
processMessage,
|
|
2146
|
+
resetToNewSession,
|
|
2147
|
+
subAgents,
|
|
2148
|
+
model,
|
|
2149
|
+
messages.length,
|
|
2150
|
+
messages,
|
|
2151
|
+
]);
|
|
2152
|
+
const handleSlashMenuSelect = useCallback((item) => {
|
|
2153
|
+
setShowSlashMenu(false);
|
|
2154
|
+
inputRef.current?.clear();
|
|
2155
|
+
switch (item.id) {
|
|
2156
|
+
case "new":
|
|
2157
|
+
resetToNewSession();
|
|
2158
|
+
break;
|
|
2159
|
+
case "models":
|
|
2160
|
+
setShowModelPicker(true);
|
|
2161
|
+
setModelPickerIndex(0);
|
|
2162
|
+
setModelSearchQuery("");
|
|
2163
|
+
break;
|
|
2164
|
+
case "sandbox":
|
|
2165
|
+
openSandboxPicker();
|
|
2166
|
+
break;
|
|
2167
|
+
case "wallet":
|
|
2168
|
+
openWalletPicker();
|
|
2169
|
+
break;
|
|
2170
|
+
case "remote-control":
|
|
2171
|
+
setConnectModalIndex(0);
|
|
2172
|
+
setShowConnectModal(true);
|
|
2173
|
+
break;
|
|
2174
|
+
case "exit":
|
|
2175
|
+
handleExit();
|
|
2176
|
+
break;
|
|
2177
|
+
case "help":
|
|
2178
|
+
setMessages((p) => [
|
|
2179
|
+
...p,
|
|
2180
|
+
{
|
|
2181
|
+
type: "assistant",
|
|
2182
|
+
content: SLASH_MENU_ITEMS.map((i) => `/${i.label} — ${i.description}`).join("\n"),
|
|
2183
|
+
timestamp: new Date(),
|
|
2184
|
+
},
|
|
2185
|
+
]);
|
|
2186
|
+
break;
|
|
2187
|
+
case "skills":
|
|
2188
|
+
setMessages((p) => [
|
|
2189
|
+
...p,
|
|
2190
|
+
{
|
|
2191
|
+
type: "assistant",
|
|
2192
|
+
content: formatSkillsForChat(discoverSkills(agent.getCwd()), agent.getCwd()),
|
|
2193
|
+
timestamp: new Date(),
|
|
2194
|
+
},
|
|
2195
|
+
]);
|
|
2196
|
+
break;
|
|
2197
|
+
case "mcp":
|
|
2198
|
+
openMcpModal();
|
|
2199
|
+
break;
|
|
2200
|
+
case "agents":
|
|
2201
|
+
openAgentsModal();
|
|
2202
|
+
break;
|
|
2203
|
+
case "schedule":
|
|
2204
|
+
openScheduleModal();
|
|
2205
|
+
break;
|
|
2206
|
+
case "review":
|
|
2207
|
+
processMessage(REVIEW_PROMPT);
|
|
2208
|
+
break;
|
|
2209
|
+
case "verify":
|
|
2210
|
+
processMessage(buildVerifyPrompt(agent.getCwd()));
|
|
2211
|
+
break;
|
|
2212
|
+
case "commit-push":
|
|
2213
|
+
processMessage(COMMIT_PUSH_PROMPT);
|
|
2214
|
+
break;
|
|
2215
|
+
case "commit-pr":
|
|
2216
|
+
processMessage(COMMIT_PR_PROMPT);
|
|
2217
|
+
break;
|
|
2218
|
+
case "btw":
|
|
2219
|
+
inputRef.current?.clear();
|
|
2220
|
+
inputRef.current?.insertText("/btw ");
|
|
2221
|
+
break;
|
|
2222
|
+
case "update":
|
|
2223
|
+
setIsUpdating(true);
|
|
2224
|
+
setUpdateOutput(null);
|
|
2225
|
+
runUpdate(startupConfig.version).then((result) => {
|
|
2226
|
+
setIsUpdating(false);
|
|
2227
|
+
setUpdateOutput(result.success ? result.output : `Update failed: ${result.output}`);
|
|
2228
|
+
});
|
|
2229
|
+
break;
|
|
2230
|
+
case "clear":
|
|
2231
|
+
agent.clearHistory();
|
|
2232
|
+
resetToNewSession();
|
|
2233
|
+
break;
|
|
2234
|
+
default: {
|
|
2235
|
+
// Dispatch to slash registry for registered commands (compact, cost, ee, route, plan, execute, discuss, expand, optimize, debug)
|
|
2236
|
+
dispatchSlash(item.id, [], {
|
|
2237
|
+
cwd: agent.getCwd(),
|
|
2238
|
+
tenantId: "local",
|
|
2239
|
+
defaultProvider: "anthropic",
|
|
2240
|
+
defaultModel: model,
|
|
2241
|
+
lastPrompt: messages[messages.length - 1]?.content,
|
|
2242
|
+
}).then(async (result) => {
|
|
2243
|
+
if (result === null)
|
|
2244
|
+
return;
|
|
2245
|
+
if (result.startsWith("__COMPACT__")) {
|
|
2246
|
+
const flowDir = path.join(agent.getCwd(), ".muonroi-flow");
|
|
2247
|
+
try {
|
|
2248
|
+
const cr = await deliberateCompact(flowDir, agent.getMessages(), "", 4096);
|
|
2249
|
+
setMessages((prev) => [
|
|
2250
|
+
...prev,
|
|
2251
|
+
buildAssistantEntry(`Compaction: ${cr.decisionsExtracted} decisions extracted, ${cr.tokensBeforeCompress} → ${cr.tokensAfterCompress} tokens.\nSnapshot: ${cr.historyPath}`),
|
|
2252
|
+
]);
|
|
2253
|
+
}
|
|
2254
|
+
catch (e) {
|
|
2255
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Compaction failed: ${e}`)]);
|
|
2256
|
+
}
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (result.startsWith("__EXPAND__")) {
|
|
2260
|
+
const content = result.replace(/^__EXPAND__\n[^\n]*\n?/, "");
|
|
2261
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Restored session context:\n${content}`)]);
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
if (result.startsWith("__CLEAR__")) {
|
|
2265
|
+
const summary = result.replace(/^__CLEAR__\n/, "");
|
|
2266
|
+
agent.clearHistory();
|
|
2267
|
+
setMessages([buildAssistantEntry(`Session cleared and relocked.\n\n${summary}`)]);
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
if (result.startsWith("__COUNCIL__")) {
|
|
2271
|
+
const topic = result.replace(/^__COUNCIL__\n/, "");
|
|
2272
|
+
setMessages((prev) => [...prev, buildUserEntry(`/council ${topic}`), buildAssistantEntry("Council convening...\n")]);
|
|
2273
|
+
try {
|
|
2274
|
+
const gen = agent.runCouncilRound(topic);
|
|
2275
|
+
for await (const chunk of gen) {
|
|
2276
|
+
if (chunk.type === "content") {
|
|
2277
|
+
setMessages((prev) => {
|
|
2278
|
+
const last = prev[prev.length - 1];
|
|
2279
|
+
if (last?.type === "assistant") {
|
|
2280
|
+
return [...prev.slice(0, -1), { ...last, content: (last.content ?? "") + chunk.content }];
|
|
2281
|
+
}
|
|
2282
|
+
return [...prev, buildAssistantEntry(chunk.content ?? "")];
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
if (chunk.type === "done")
|
|
2286
|
+
break;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
catch (e) {
|
|
2290
|
+
setMessages((prev) => [...prev, buildAssistantEntry(`Council error: ${e}`)]);
|
|
2291
|
+
}
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
setMessages((prev) => [...prev, buildAssistantEntry(result)]);
|
|
2295
|
+
});
|
|
2296
|
+
break;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}, [
|
|
2300
|
+
agent,
|
|
2301
|
+
handleExit,
|
|
2302
|
+
model,
|
|
2303
|
+
messages,
|
|
2304
|
+
openAgentsModal,
|
|
2305
|
+
openMcpModal,
|
|
2306
|
+
openSandboxPicker,
|
|
2307
|
+
openWalletPicker,
|
|
2308
|
+
openScheduleModal,
|
|
2309
|
+
processMessage,
|
|
2310
|
+
resetToNewSession,
|
|
2311
|
+
startupConfig.version,
|
|
2312
|
+
]);
|
|
2313
|
+
const blockPrompt = showConnectModal ||
|
|
2314
|
+
showTelegramTokenModal ||
|
|
2315
|
+
showTelegramPairModal ||
|
|
2316
|
+
showMcpModal ||
|
|
2317
|
+
showSandboxPicker ||
|
|
2318
|
+
showWalletPicker ||
|
|
2319
|
+
!!pendingPaymentApproval ||
|
|
2320
|
+
showScheduleModal ||
|
|
2321
|
+
showAgentsModal ||
|
|
2322
|
+
showAgentsEditor ||
|
|
2323
|
+
showUpdateModal;
|
|
2324
|
+
const showPlanPanel = !!activePlan?.questions?.length;
|
|
2325
|
+
const planQuestions = activePlan?.questions ?? [];
|
|
2326
|
+
const isSinglePlan = planQuestions.length === 1 && planQuestions[0]?.type !== "multiselect";
|
|
2327
|
+
const planTabCount = isSinglePlan ? 1 : planQuestions.length + 1;
|
|
2328
|
+
const isPlanConfirmTab = !isSinglePlan && pqs.tab === planQuestions.length;
|
|
2329
|
+
const dismissPlan = useCallback(() => {
|
|
2330
|
+
setActivePlan(null);
|
|
2331
|
+
setPqs(initialPlanQuestionsState());
|
|
2332
|
+
}, []);
|
|
2333
|
+
const submitPlanAnswers = useCallback(() => {
|
|
2334
|
+
if (!activePlan?.questions?.length)
|
|
2335
|
+
return;
|
|
2336
|
+
const text = formatPlanAnswers(activePlan.questions, pqs.answers);
|
|
2337
|
+
setActivePlan(null);
|
|
2338
|
+
setPqs(initialPlanQuestionsState());
|
|
2339
|
+
processMessage(text);
|
|
2340
|
+
}, [activePlan, pqs.answers, processMessage]);
|
|
2341
|
+
const handlePlanSelect = useCallback((q, idx, options, showCustom) => {
|
|
2342
|
+
const isCustom = showCustom && idx === options.length;
|
|
2343
|
+
if (isCustom) {
|
|
2344
|
+
if (q.type === "multiselect") {
|
|
2345
|
+
const customVal = pqs.customInputs[q.id] ?? "";
|
|
2346
|
+
if (customVal) {
|
|
2347
|
+
const existing = pqs.answers[q.id] ?? [];
|
|
2348
|
+
if (existing.includes(customVal)) {
|
|
2349
|
+
setPqs((s) => ({ ...s, answers: { ...s.answers, [q.id]: existing.filter((x) => x !== customVal) } }));
|
|
2350
|
+
}
|
|
2351
|
+
else {
|
|
2352
|
+
setPqs((s) => ({ ...s, editing: true }));
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
else {
|
|
2356
|
+
setPqs((s) => ({ ...s, editing: true }));
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
else {
|
|
2360
|
+
setPqs((s) => ({ ...s, editing: true }));
|
|
2361
|
+
}
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
const opt = options[idx];
|
|
2365
|
+
if (!opt)
|
|
2366
|
+
return;
|
|
2367
|
+
if (q.type === "multiselect") {
|
|
2368
|
+
setPqs((s) => {
|
|
2369
|
+
const existing = s.answers[q.id] ?? [];
|
|
2370
|
+
const next = existing.includes(opt.id) ? existing.filter((x) => x !== opt.id) : [...existing, opt.id];
|
|
2371
|
+
return { ...s, answers: { ...s.answers, [q.id]: next } };
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
else {
|
|
2375
|
+
setPqs((s) => ({ ...s, answers: { ...s.answers, [q.id]: opt.id } }));
|
|
2376
|
+
if (isSinglePlan) {
|
|
2377
|
+
submitPlanAnswers();
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
|
|
2381
|
+
}
|
|
2382
|
+
}, [pqs, isSinglePlan, submitPlanAnswers]);
|
|
2383
|
+
const dismissBtw = useCallback(() => {
|
|
2384
|
+
btwAbortRef.current?.abort();
|
|
2385
|
+
btwAbortRef.current = null;
|
|
2386
|
+
btwStateRef.current = null;
|
|
2387
|
+
setBtwState(null);
|
|
2388
|
+
}, []);
|
|
2389
|
+
const handleKey = useCallback((key) => {
|
|
2390
|
+
if (btwState) {
|
|
2391
|
+
if (isEscapeKey(key) || key.name === "return") {
|
|
2392
|
+
dismissBtw();
|
|
2393
|
+
}
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
if (showPlanPanel) {
|
|
2397
|
+
const q = planQuestions[pqs.tab];
|
|
2398
|
+
// Escape always dismisses
|
|
2399
|
+
if (isEscapeKey(key)) {
|
|
2400
|
+
dismissPlan();
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
// When editing custom text input
|
|
2404
|
+
if (pqs.editing && !isPlanConfirmTab) {
|
|
2405
|
+
if (key.name === "return") {
|
|
2406
|
+
const qId = q?.id;
|
|
2407
|
+
if (qId) {
|
|
2408
|
+
const text = (pqs.customInputs[qId] ?? "").trim();
|
|
2409
|
+
if (text) {
|
|
2410
|
+
if (q.type === "multiselect") {
|
|
2411
|
+
const existing = pqs.answers[qId] ?? [];
|
|
2412
|
+
const next = existing.includes(text) ? existing : [...existing, text];
|
|
2413
|
+
setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: next } }));
|
|
2414
|
+
}
|
|
2415
|
+
else if (q.type === "text") {
|
|
2416
|
+
setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: text } }));
|
|
2417
|
+
if (isSinglePlan) {
|
|
2418
|
+
submitPlanAnswers();
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
|
|
2422
|
+
}
|
|
2423
|
+
else {
|
|
2424
|
+
setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: text } }));
|
|
2425
|
+
if (isSinglePlan) {
|
|
2426
|
+
submitPlanAnswers();
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
else {
|
|
2433
|
+
setPqs((s) => ({ ...s, editing: false }));
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
if (key.name === "backspace") {
|
|
2439
|
+
const qId = q?.id;
|
|
2440
|
+
if (qId)
|
|
2441
|
+
setPqs((s) => ({
|
|
2442
|
+
...s,
|
|
2443
|
+
customInputs: { ...s.customInputs, [qId]: (s.customInputs[qId] ?? "").slice(0, -1) },
|
|
2444
|
+
}));
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2448
|
+
const qId = q?.id;
|
|
2449
|
+
if (qId)
|
|
2450
|
+
setPqs((s) => ({
|
|
2451
|
+
...s,
|
|
2452
|
+
customInputs: { ...s.customInputs, [qId]: (s.customInputs[qId] ?? "") + key.sequence },
|
|
2453
|
+
}));
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
// Tab / left / right — switch between question tabs
|
|
2459
|
+
if (key.name === "tab") {
|
|
2460
|
+
const dir = key.shift ? -1 : 1;
|
|
2461
|
+
setPqs((s) => ({ ...s, tab: (s.tab + dir + planTabCount) % planTabCount, selected: 0 }));
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
if (key.name === "left" || key.name === "h") {
|
|
2465
|
+
setPqs((s) => ({ ...s, tab: (s.tab - 1 + planTabCount) % planTabCount, selected: 0 }));
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
if (key.name === "right" || key.name === "l") {
|
|
2469
|
+
setPqs((s) => ({ ...s, tab: (s.tab + 1) % planTabCount, selected: 0 }));
|
|
2470
|
+
return;
|
|
2471
|
+
}
|
|
2472
|
+
// Confirm tab
|
|
2473
|
+
if (isPlanConfirmTab) {
|
|
2474
|
+
if (key.name === "return") {
|
|
2475
|
+
submitPlanAnswers();
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
if (!q)
|
|
2481
|
+
return;
|
|
2482
|
+
// Text-only question (no options)
|
|
2483
|
+
if (q.type === "text") {
|
|
2484
|
+
setPqs((s) => ({ ...s, editing: true }));
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
// Up/down — navigate options
|
|
2488
|
+
const options = q.options ?? [];
|
|
2489
|
+
const showCustom = true;
|
|
2490
|
+
const totalItems = options.length + 1;
|
|
2491
|
+
if (key.name === "up" || key.name === "k") {
|
|
2492
|
+
setPqs((s) => ({ ...s, selected: (s.selected - 1 + totalItems) % totalItems }));
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
if (key.name === "down" || key.name === "j") {
|
|
2496
|
+
setPqs((s) => ({ ...s, selected: (s.selected + 1) % totalItems }));
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
// Number keys 1-9 for quick selection
|
|
2500
|
+
const digit = Number(key.name);
|
|
2501
|
+
if (!Number.isNaN(digit) && digit >= 1 && digit <= Math.min(totalItems, 9)) {
|
|
2502
|
+
const idx = digit - 1;
|
|
2503
|
+
setPqs((s) => ({ ...s, selected: idx }));
|
|
2504
|
+
handlePlanSelect(q, idx, options, showCustom);
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
// Enter — select current option
|
|
2508
|
+
if (key.name === "return") {
|
|
2509
|
+
handlePlanSelect(q, pqs.selected, options, showCustom);
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
if (showUpdateModalRef.current) {
|
|
2515
|
+
if (isEscapeKey(key)) {
|
|
2516
|
+
setShowUpdateModal(false);
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
if (key.name === "return") {
|
|
2520
|
+
setIsUpdating(true);
|
|
2521
|
+
setShowUpdateModal(false);
|
|
2522
|
+
runUpdate(startupConfig.version).then((result) => {
|
|
2523
|
+
setIsUpdating(false);
|
|
2524
|
+
setUpdateOutput(result.output);
|
|
2525
|
+
});
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
if (showMcpEditorRef.current) {
|
|
2531
|
+
if (isEscapeKey(key)) {
|
|
2532
|
+
setShowMcpEditor(false);
|
|
2533
|
+
setMcpEditorError(null);
|
|
2534
|
+
setMcpSearchQuery("");
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (key.name === "return") {
|
|
2538
|
+
submitMcpEditor();
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
if (mcpEditorField === "transport" && (key.name === "left" || key.name === "right")) {
|
|
2542
|
+
cycleMcpEditorTransport(key.name === "left" ? -1 : 1);
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
if (key.name === "tab") {
|
|
2546
|
+
const idx = mcpEditorFields.indexOf(mcpEditorField);
|
|
2547
|
+
const nextIdx = (idx + (key.shift ? -1 : 1) + mcpEditorFields.length) % mcpEditorFields.length;
|
|
2548
|
+
setMcpEditorField(mcpEditorFields[nextIdx]);
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
if (mcpEditorField === "transport") {
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
if (showAgentsEditorRef.current) {
|
|
2556
|
+
if (isEscapeKey(key)) {
|
|
2557
|
+
setShowAgentsEditor(false);
|
|
2558
|
+
setAgentsEditorError(null);
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
if (key.name === "x" && key.ctrl && editingSubagent) {
|
|
2562
|
+
removeEditingSubagent();
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (key.name === "return") {
|
|
2566
|
+
submitSubagentEditor();
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
if (agentsEditorField === "model" &&
|
|
2570
|
+
(key.name === "up" ||
|
|
2571
|
+
key.name === "down" ||
|
|
2572
|
+
key.name === "left" ||
|
|
2573
|
+
key.name === "right" ||
|
|
2574
|
+
key.name === "j" ||
|
|
2575
|
+
key.name === "k")) {
|
|
2576
|
+
const decrement = key.name === "up" || key.name === "left" || key.name === "k";
|
|
2577
|
+
setAgentsEditorModelIndex((index) => decrement ? Math.max(0, index - 1) : Math.min(MODELS.length - 1, index + 1));
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
if (key.name === "tab") {
|
|
2581
|
+
const index = SUBAGENT_EDITOR_FIELDS.indexOf(agentsEditorField);
|
|
2582
|
+
const nextIndex = (index + (key.shift ? -1 : 1) + SUBAGENT_EDITOR_FIELDS.length) % SUBAGENT_EDITOR_FIELDS.length;
|
|
2583
|
+
setAgentsEditorField(SUBAGENT_EDITOR_FIELDS[nextIndex]);
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
if (agentsEditorField === "model") {
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (showMcpModalRef.current) {
|
|
2591
|
+
const row = mcpRows[mcpModalIndex];
|
|
2592
|
+
if (isEscapeKey(key)) {
|
|
2593
|
+
setShowMcpEditor(false);
|
|
2594
|
+
setShowMcpModal(false);
|
|
2595
|
+
setMcpSearchQuery("");
|
|
2596
|
+
setEditingMcpId(null);
|
|
2597
|
+
setMcpEditorError(null);
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
if (key.name === "up") {
|
|
2601
|
+
setMcpModalIndex((i) => Math.max(0, i - 1));
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
if (key.name === "down") {
|
|
2605
|
+
setMcpModalIndex((i) => Math.min(mcpRows.length - 1, i + 1));
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
if (key.name === "return") {
|
|
2609
|
+
if (row?.kind === "server") {
|
|
2610
|
+
toggleSavedMcp(row.server);
|
|
2611
|
+
}
|
|
2612
|
+
else if (row?.kind === "catalog") {
|
|
2613
|
+
openCatalogMcp(row.entry);
|
|
2614
|
+
}
|
|
2615
|
+
else {
|
|
2616
|
+
openMcpEditor(createEmptyMcpEditorDraft());
|
|
2617
|
+
}
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
if (key.name === "a" && key.ctrl) {
|
|
2621
|
+
openMcpEditor(createEmptyMcpEditorDraft());
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
if (key.name === "e" && key.ctrl && row?.kind === "server") {
|
|
2625
|
+
editSavedMcp(row.server);
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
if (key.name === "x" && key.ctrl && row?.kind === "server") {
|
|
2629
|
+
deleteSavedMcp(row.server);
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
if (key.name === "backspace") {
|
|
2633
|
+
setMcpSearchQuery((q) => q.slice(0, -1));
|
|
2634
|
+
setMcpModalIndex(0);
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2638
|
+
setMcpSearchQuery((q) => q + key.sequence);
|
|
2639
|
+
setMcpModalIndex(0);
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2644
|
+
if (showScheduleModalRef.current) {
|
|
2645
|
+
const row = scheduleRows[scheduleModalIndex];
|
|
2646
|
+
if (isEscapeKey(key)) {
|
|
2647
|
+
setShowScheduleModal(false);
|
|
2648
|
+
setScheduleSearchQuery("");
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
if (key.name === "up") {
|
|
2652
|
+
setScheduleModalIndex((index) => Math.max(0, index - 1));
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (key.name === "down") {
|
|
2656
|
+
setScheduleModalIndex((index) => Math.min(Math.max(0, scheduleRows.length - 1), index + 1));
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
if (key.name === "return") {
|
|
2660
|
+
if (row?.kind === "schedule") {
|
|
2661
|
+
showScheduleDetails(row.schedule);
|
|
2662
|
+
}
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
if (key.name === "x" && key.ctrl && row?.kind === "schedule") {
|
|
2666
|
+
removeSchedule(row.schedule);
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
if (key.name === "backspace") {
|
|
2670
|
+
setScheduleSearchQuery((query) => query.slice(0, -1));
|
|
2671
|
+
setScheduleModalIndex(0);
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2675
|
+
setScheduleSearchQuery((query) => query + key.sequence);
|
|
2676
|
+
setScheduleModalIndex(0);
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
if (showAgentsModalRef.current && !showAgentsEditorRef.current) {
|
|
2682
|
+
const row = agentRows[agentsModalIndex];
|
|
2683
|
+
if (isEscapeKey(key)) {
|
|
2684
|
+
setShowAgentsModal(false);
|
|
2685
|
+
setShowAgentsEditor(false);
|
|
2686
|
+
setAgentsSearchQuery("");
|
|
2687
|
+
setEditingSubagent(null);
|
|
2688
|
+
setAgentsEditorError(null);
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
if (key.name === "up") {
|
|
2692
|
+
setAgentsModalIndex((index) => Math.max(0, index - 1));
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
if (key.name === "down") {
|
|
2696
|
+
setAgentsModalIndex((index) => Math.min(Math.max(0, agentRows.length - 1), index + 1));
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
if (key.name === "return") {
|
|
2700
|
+
if (row?.kind === "agent") {
|
|
2701
|
+
openSubagentEditor(row.agent);
|
|
2702
|
+
}
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
if (key.name === "a" && key.ctrl) {
|
|
2706
|
+
openSubagentEditor(null);
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
if (key.name === "backspace") {
|
|
2710
|
+
setAgentsSearchQuery((query) => query.slice(0, -1));
|
|
2711
|
+
setAgentsModalIndex(0);
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2715
|
+
setAgentsSearchQuery((query) => query + key.sequence);
|
|
2716
|
+
setAgentsModalIndex(0);
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (showTelegramTokenModalRef.current) {
|
|
2722
|
+
if (isEscapeKey(key)) {
|
|
2723
|
+
setShowTelegramTokenModal(false);
|
|
2724
|
+
setTelegramTokenError(null);
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
if (key.name === "return") {
|
|
2728
|
+
submitTelegramToken();
|
|
2729
|
+
}
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
if (showTelegramPairModalRef.current) {
|
|
2733
|
+
if (isEscapeKey(key)) {
|
|
2734
|
+
setShowTelegramPairModal(false);
|
|
2735
|
+
setTelegramPairError(null);
|
|
2736
|
+
return;
|
|
2737
|
+
}
|
|
2738
|
+
if (key.name === "return") {
|
|
2739
|
+
void submitTelegramPair();
|
|
2740
|
+
}
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
if (showConnectModalRef.current) {
|
|
2744
|
+
if (isEscapeKey(key)) {
|
|
2745
|
+
setShowConnectModal(false);
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
if (key.name === "up") {
|
|
2749
|
+
setConnectModalIndex((i) => Math.max(0, i - 1));
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
2752
|
+
if (key.name === "down") {
|
|
2753
|
+
setConnectModalIndex((i) => Math.min(CONNECT_CHANNELS.length - 1, i + 1));
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
if (key.name === "return") {
|
|
2757
|
+
const ch = CONNECT_CHANNELS[connectModalIndex];
|
|
2758
|
+
if (ch?.id === "telegram")
|
|
2759
|
+
beginTelegramFromConnect();
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
if (showApiKeyModalRef.current) {
|
|
2765
|
+
if (isEscapeKey(key)) {
|
|
2766
|
+
closeApiKeyModal();
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
if (key.name === "return") {
|
|
2770
|
+
submitApiKey();
|
|
2771
|
+
}
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
if (showSlashMenu) {
|
|
2775
|
+
if (isEscapeKey(key)) {
|
|
2776
|
+
setShowSlashMenu(false);
|
|
2777
|
+
setSlashSearchQuery("");
|
|
2778
|
+
inputRef.current?.clear();
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
if (key.name === "up") {
|
|
2782
|
+
setSlashMenuIndex((i) => Math.max(0, i - 1));
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
if (key.name === "down") {
|
|
2786
|
+
setSlashMenuIndex((i) => Math.min(filteredSlashItems.length - 1, i + 1));
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (key.name === "return") {
|
|
2790
|
+
const item = filteredSlashItems[slashMenuIndex];
|
|
2791
|
+
if (item)
|
|
2792
|
+
handleSlashMenuSelect(item);
|
|
2793
|
+
setSlashSearchQuery("");
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
if (key.name === "backspace") {
|
|
2797
|
+
setSlashSearchQuery((q) => q.slice(0, -1));
|
|
2798
|
+
setSlashMenuIndex(0);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2802
|
+
setSlashSearchQuery((q) => q + key.sequence);
|
|
2803
|
+
setSlashMenuIndex(0);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
if (showModelPicker) {
|
|
2809
|
+
if (isEscapeKey(key)) {
|
|
2810
|
+
setShowModelPicker(false);
|
|
2811
|
+
setModelSearchQuery("");
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
if (key.name === "up") {
|
|
2815
|
+
setModelPickerIndex((i) => Math.max(0, i - 1));
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
if (key.name === "down") {
|
|
2819
|
+
setModelPickerIndex((i) => Math.min(filteredModelIds.length - 1, i + 1));
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
if (key.name === "left" || key.name === "right") {
|
|
2823
|
+
const sel = filteredModelIds[modelPickerIndex];
|
|
2824
|
+
if (sel) {
|
|
2825
|
+
adjustModelReasoningEffort(sel, key.name === "left" ? -1 : 1);
|
|
2826
|
+
}
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
if (key.name === "return") {
|
|
2830
|
+
const sel = filteredModelIds[modelPickerIndex];
|
|
2831
|
+
if (sel) {
|
|
2832
|
+
agent.setModel(sel);
|
|
2833
|
+
setModel(sel);
|
|
2834
|
+
statusBarStore.setState({ model: sel, provider: agent.getProviderId() });
|
|
2835
|
+
saveProjectSettings({ model: sel });
|
|
2836
|
+
saveUserSettings({ defaultModel: sel });
|
|
2837
|
+
}
|
|
2838
|
+
setShowModelPicker(false);
|
|
2839
|
+
setModelSearchQuery("");
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
if (key.name === "backspace") {
|
|
2843
|
+
setModelSearchQuery((q) => q.slice(0, -1));
|
|
2844
|
+
setModelPickerIndex(0);
|
|
2845
|
+
return;
|
|
2846
|
+
}
|
|
2847
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2848
|
+
setModelSearchQuery((q) => q + key.sequence);
|
|
2849
|
+
setModelPickerIndex(0);
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
if (pendingPaymentApproval) {
|
|
2855
|
+
if (isEscapeKey(key)) {
|
|
2856
|
+
setPendingPaymentApproval(null);
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
if (key.name === "up" || key.name === "down") {
|
|
2860
|
+
setPendingPaymentApproval((p) => (p ? { ...p, selected: p.selected === 0 ? 1 : 0 } : p));
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
if (key.name === "return") {
|
|
2864
|
+
const approved = pendingPaymentApproval.selected === 0;
|
|
2865
|
+
const aid = pendingPaymentApproval.approvalId;
|
|
2866
|
+
setPendingPaymentApproval(null);
|
|
2867
|
+
if (aid) {
|
|
2868
|
+
agent.respondToToolApproval(aid, approved);
|
|
2869
|
+
if (approved) {
|
|
2870
|
+
processMessage("[Payment approved]");
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
if (showWalletPicker) {
|
|
2878
|
+
if (isEscapeKey(key)) {
|
|
2879
|
+
setShowWalletPicker(false);
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
if (key.name === "up") {
|
|
2883
|
+
setWalletFocusIndex((i) => Math.max(0, i - 1));
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
if (key.name === "down") {
|
|
2887
|
+
setWalletFocusIndex((i) => Math.min(WALLET_ROWS.length - 1, i + 1));
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
const focusedWalletRow = WALLET_ROWS[walletFocusIndex];
|
|
2891
|
+
if (!focusedWalletRow || focusedWalletRow.type === "readonly")
|
|
2892
|
+
return;
|
|
2893
|
+
if (key.name === "left" || key.name === "right") {
|
|
2894
|
+
const options = focusedWalletRow.getOptions();
|
|
2895
|
+
const current = focusedWalletRow.getDisplay(walletSettings, walletDisplayInfo);
|
|
2896
|
+
const idx = options.indexOf(current);
|
|
2897
|
+
const next = key.name === "right" ? options[Math.min(options.length - 1, idx + 1)] : options[Math.max(0, idx - 1)];
|
|
2898
|
+
if (next && next !== current && focusedWalletRow.apply) {
|
|
2899
|
+
const patch = focusedWalletRow.apply(walletSettings, next);
|
|
2900
|
+
applyWalletSettings({ ...walletSettings, ...patch });
|
|
2901
|
+
}
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2904
|
+
if (key.name === "return") {
|
|
2905
|
+
const options = focusedWalletRow.getOptions();
|
|
2906
|
+
const current = focusedWalletRow.getDisplay(walletSettings, walletDisplayInfo);
|
|
2907
|
+
const idx = options.indexOf(current);
|
|
2908
|
+
const next = options[(idx + 1) % options.length];
|
|
2909
|
+
if (next && focusedWalletRow.apply) {
|
|
2910
|
+
const patch = focusedWalletRow.apply(walletSettings, next);
|
|
2911
|
+
applyWalletSettings({ ...walletSettings, ...patch });
|
|
2912
|
+
}
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
if (showSandboxPicker) {
|
|
2918
|
+
const visibleRows = getSandboxVisibleRows(sandboxMode);
|
|
2919
|
+
if (sandboxSettingsEditing) {
|
|
2920
|
+
if (isEscapeKey(key)) {
|
|
2921
|
+
setSandboxSettingsEditing(null);
|
|
2922
|
+
setSandboxSettingsEditBuffer("");
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
if (key.name === "return") {
|
|
2926
|
+
const row = visibleRows.find((r) => r.key === sandboxSettingsEditing);
|
|
2927
|
+
if (row) {
|
|
2928
|
+
const result = row.apply(sandboxMode, sandboxSettings, sandboxSettingsEditBuffer.trim());
|
|
2929
|
+
if (result.mode !== undefined)
|
|
2930
|
+
applySandboxMode(result.mode);
|
|
2931
|
+
if (result.settings)
|
|
2932
|
+
applySandboxSettings({ ...sandboxSettings, ...result.settings });
|
|
2933
|
+
}
|
|
2934
|
+
setSandboxSettingsEditing(null);
|
|
2935
|
+
setSandboxSettingsEditBuffer("");
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
if (key.name === "backspace") {
|
|
2939
|
+
setSandboxSettingsEditBuffer((b) => b.slice(0, -1));
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
2943
|
+
setSandboxSettingsEditBuffer((b) => b + key.sequence);
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
if (isEscapeKey(key)) {
|
|
2949
|
+
setShowSandboxPicker(false);
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
if (key.name === "up") {
|
|
2953
|
+
setSandboxSettingsFocusIndex((i) => Math.max(0, i - 1));
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
if (key.name === "down") {
|
|
2957
|
+
setSandboxSettingsFocusIndex((i) => Math.min(visibleRows.length - 1, i + 1));
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
const focusedRow = visibleRows[sandboxSettingsFocusIndex];
|
|
2961
|
+
if (!focusedRow)
|
|
2962
|
+
return;
|
|
2963
|
+
if (focusedRow.type === "toggle" && (key.name === "left" || key.name === "right")) {
|
|
2964
|
+
const options = focusedRow.getOptions();
|
|
2965
|
+
const current = focusedRow.getDisplay(sandboxMode, sandboxSettings);
|
|
2966
|
+
const idx = options.indexOf(current);
|
|
2967
|
+
const next = key.name === "right" ? options[Math.min(options.length - 1, idx + 1)] : options[Math.max(0, idx - 1)];
|
|
2968
|
+
if (next && next !== current) {
|
|
2969
|
+
const result = focusedRow.apply(sandboxMode, sandboxSettings, next);
|
|
2970
|
+
if (result.mode !== undefined)
|
|
2971
|
+
applySandboxMode(result.mode);
|
|
2972
|
+
if (result.settings)
|
|
2973
|
+
applySandboxSettings({ ...sandboxSettings, ...result.settings });
|
|
2974
|
+
}
|
|
2975
|
+
return;
|
|
2976
|
+
}
|
|
2977
|
+
if (key.name === "return") {
|
|
2978
|
+
if (focusedRow.type === "toggle") {
|
|
2979
|
+
const options = focusedRow.getOptions();
|
|
2980
|
+
const current = focusedRow.getDisplay(sandboxMode, sandboxSettings);
|
|
2981
|
+
const idx = options.indexOf(current);
|
|
2982
|
+
const next = options[(idx + 1) % options.length];
|
|
2983
|
+
const result = focusedRow.apply(sandboxMode, sandboxSettings, next);
|
|
2984
|
+
if (result.mode !== undefined)
|
|
2985
|
+
applySandboxMode(result.mode);
|
|
2986
|
+
if (result.settings)
|
|
2987
|
+
applySandboxSettings({ ...sandboxSettings, ...result.settings });
|
|
2988
|
+
}
|
|
2989
|
+
else {
|
|
2990
|
+
setSandboxSettingsEditing(focusedRow.key);
|
|
2991
|
+
const current = sandboxSettings[focusedRow.key];
|
|
2992
|
+
setSandboxSettingsEditBuffer(Array.isArray(current) ? current.join(", ") : current != null ? String(current) : "");
|
|
2993
|
+
}
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
if (isEscapeKey(key) && interruptActiveRun(key)) {
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
if (!hasApiKeyRef.current && shouldOpenApiKeyModalForKey(key)) {
|
|
3002
|
+
openApiKeyModal();
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
if (key.sequence === "/" && !isProcessing) {
|
|
3006
|
+
const text = inputRef.current?.plainText || "";
|
|
3007
|
+
if (!text.trim()) {
|
|
3008
|
+
setShowSlashMenu(true);
|
|
3009
|
+
setSlashMenuIndex(0);
|
|
3010
|
+
setSlashSearchQuery("");
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
if (key.name === "e" && key.ctrl) {
|
|
3015
|
+
let lastUserIdx = -1;
|
|
3016
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3017
|
+
if (messages[i].type === "user") {
|
|
3018
|
+
lastUserIdx = i;
|
|
3019
|
+
break;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
if (lastUserIdx >= 0) {
|
|
3023
|
+
setExpandedMessages((prev) => {
|
|
3024
|
+
const next = new Set(prev);
|
|
3025
|
+
if (next.has(lastUserIdx))
|
|
3026
|
+
next.delete(lastUserIdx);
|
|
3027
|
+
else
|
|
3028
|
+
next.add(lastUserIdx);
|
|
3029
|
+
return next;
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
if (key.name === "c" && key.ctrl && key.shift) {
|
|
3035
|
+
if (copyTuiSelectionToHost()) {
|
|
3036
|
+
key.preventDefault();
|
|
3037
|
+
key.stopPropagation();
|
|
3038
|
+
}
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
if (key.name === "y" && key.ctrl && copyTuiSelectionToHost()) {
|
|
3042
|
+
key.preventDefault();
|
|
3043
|
+
key.stopPropagation();
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
// ⌘C: Kitty / iTerm report Command as `super`; some setups use `meta` instead.
|
|
3047
|
+
if (key.name === "c" && !key.ctrl && (key.meta || key.super)) {
|
|
3048
|
+
if (copyTuiSelectionToHost()) {
|
|
3049
|
+
key.preventDefault();
|
|
3050
|
+
key.stopPropagation();
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
// Alt+V: paste image from clipboard (like Claude Code / Codex / Gemini CLI)
|
|
3055
|
+
if (key.name === "v" && (key.meta || key.option)) {
|
|
3056
|
+
const clip = readClipboardImage();
|
|
3057
|
+
if (clip) {
|
|
3058
|
+
const id = ++pasteCounterRef.current;
|
|
3059
|
+
const block = { id, content: `__clipboard_image_${id}__`, lines: 1, isImage: true, clipboardBase64: clip.base64, clipboardMediaType: clip.mediaType };
|
|
3060
|
+
replacePasteBlocks([...pasteBlocksRef.current, block]);
|
|
3061
|
+
inputRef.current?.insertText(getPasteBlockToken(block));
|
|
3062
|
+
}
|
|
3063
|
+
key.preventDefault();
|
|
3064
|
+
key.stopPropagation();
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
if (key.name === "c" && key.ctrl) {
|
|
3068
|
+
if (copyTuiSelectionToHost()) {
|
|
3069
|
+
key.preventDefault();
|
|
3070
|
+
key.stopPropagation();
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
const text = inputRef.current?.plainText || "";
|
|
3074
|
+
if (text.trim()) {
|
|
3075
|
+
inputRef.current?.clear();
|
|
3076
|
+
replacePasteBlocks([]);
|
|
3077
|
+
}
|
|
3078
|
+
else {
|
|
3079
|
+
handleExit();
|
|
3080
|
+
}
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
if (typeaheadRef.current.visible) {
|
|
3084
|
+
if (key.name === "up") {
|
|
3085
|
+
typeaheadRef.current.navigateUp();
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
if (key.name === "down") {
|
|
3089
|
+
typeaheadRef.current.navigateDown();
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
if (key.name === "tab" || key.name === "return") {
|
|
3093
|
+
key.preventDefault();
|
|
3094
|
+
key.stopPropagation();
|
|
3095
|
+
typeaheadRef.current.accept();
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
if (isEscapeKey(key)) {
|
|
3099
|
+
typeaheadRef.current.dismiss();
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
if (key.name === "tab" && !isProcessing) {
|
|
3104
|
+
cycleMode();
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
}, [
|
|
3108
|
+
agent,
|
|
3109
|
+
agentRows,
|
|
3110
|
+
agentsEditorField,
|
|
3111
|
+
agentsModalIndex,
|
|
3112
|
+
beginTelegramFromConnect,
|
|
3113
|
+
btwState,
|
|
3114
|
+
closeApiKeyModal,
|
|
3115
|
+
connectModalIndex,
|
|
3116
|
+
cycleMode,
|
|
3117
|
+
cycleMcpEditorTransport,
|
|
3118
|
+
deleteSavedMcp,
|
|
3119
|
+
dismissBtw,
|
|
3120
|
+
dismissPlan,
|
|
3121
|
+
editingSubagent,
|
|
3122
|
+
editSavedMcp,
|
|
3123
|
+
adjustModelReasoningEffort,
|
|
3124
|
+
filteredModelIds,
|
|
3125
|
+
filteredSlashItems,
|
|
3126
|
+
handleExit,
|
|
3127
|
+
handlePlanSelect,
|
|
3128
|
+
handleSlashMenuSelect,
|
|
3129
|
+
interruptActiveRun,
|
|
3130
|
+
isPlanConfirmTab,
|
|
3131
|
+
isProcessing,
|
|
3132
|
+
isSinglePlan,
|
|
3133
|
+
mcpEditorField,
|
|
3134
|
+
mcpEditorFields,
|
|
3135
|
+
mcpModalIndex,
|
|
3136
|
+
mcpRows,
|
|
3137
|
+
modelPickerIndex,
|
|
3138
|
+
openApiKeyModal,
|
|
3139
|
+
openCatalogMcp,
|
|
3140
|
+
openMcpEditor,
|
|
3141
|
+
replacePasteBlocks,
|
|
3142
|
+
openSubagentEditor,
|
|
3143
|
+
removeSchedule,
|
|
3144
|
+
scheduleModalIndex,
|
|
3145
|
+
scheduleRows,
|
|
3146
|
+
showScheduleDetails,
|
|
3147
|
+
submitTelegramPair,
|
|
3148
|
+
submitTelegramToken,
|
|
3149
|
+
submitMcpEditor,
|
|
3150
|
+
submitSubagentEditor,
|
|
3151
|
+
planQuestions,
|
|
3152
|
+
planTabCount,
|
|
3153
|
+
pqs,
|
|
3154
|
+
removeEditingSubagent,
|
|
3155
|
+
applySandboxMode,
|
|
3156
|
+
applySandboxSettings,
|
|
3157
|
+
sandboxSettings,
|
|
3158
|
+
sandboxSettingsEditing,
|
|
3159
|
+
sandboxSettingsEditBuffer,
|
|
3160
|
+
sandboxSettingsFocusIndex,
|
|
3161
|
+
sandboxMode,
|
|
3162
|
+
showModelPicker,
|
|
3163
|
+
showPlanPanel,
|
|
3164
|
+
showSandboxPicker,
|
|
3165
|
+
pendingPaymentApproval,
|
|
3166
|
+
processMessage,
|
|
3167
|
+
showWalletPicker,
|
|
3168
|
+
walletSettings,
|
|
3169
|
+
walletFocusIndex,
|
|
3170
|
+
walletDisplayInfo,
|
|
3171
|
+
applyWalletSettings,
|
|
3172
|
+
showSlashMenu,
|
|
3173
|
+
slashMenuIndex,
|
|
3174
|
+
submitApiKey,
|
|
3175
|
+
submitPlanAnswers,
|
|
3176
|
+
copyTuiSelectionToHost,
|
|
3177
|
+
toggleSavedMcp,
|
|
3178
|
+
messages,
|
|
3179
|
+
startupConfig.version,
|
|
3180
|
+
]);
|
|
3181
|
+
useKeyboard(handleKey);
|
|
3182
|
+
const handlePaste = useCallback((event) => {
|
|
3183
|
+
if (!hasApiKeyRef.current) {
|
|
3184
|
+
event.preventDefault();
|
|
3185
|
+
openApiKeyModal();
|
|
3186
|
+
return;
|
|
3187
|
+
}
|
|
3188
|
+
const text = decodePasteBytes(event.bytes);
|
|
3189
|
+
const trimmed = text.trim();
|
|
3190
|
+
const imageExts = /\.(png|jpe?g|gif|webp|svg|bmp|ico|tiff?)$/i;
|
|
3191
|
+
if (imageExts.test(trimmed) && !trimmed.includes("\n")) {
|
|
3192
|
+
event.preventDefault();
|
|
3193
|
+
const id = ++pasteCounterRef.current;
|
|
3194
|
+
const block = { id, content: trimmed, lines: 1, isImage: true };
|
|
3195
|
+
replacePasteBlocks([...pasteBlocksRef.current, block]);
|
|
3196
|
+
inputRef.current?.insertText(getPasteBlockToken(block));
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
const lineCount = text.split("\n").length;
|
|
3200
|
+
if (lineCount < 2)
|
|
3201
|
+
return;
|
|
3202
|
+
event.preventDefault();
|
|
3203
|
+
const id = ++pasteCounterRef.current;
|
|
3204
|
+
const block = { id, content: text, lines: lineCount };
|
|
3205
|
+
replacePasteBlocks([...pasteBlocksRef.current, block]);
|
|
3206
|
+
inputRef.current?.insertText(getPasteBlockToken(block));
|
|
3207
|
+
}, [openApiKeyModal, replacePasteBlocks]);
|
|
3208
|
+
const handleSubmit = useCallback(() => {
|
|
3209
|
+
const raw = inputRef.current?.plainText || "";
|
|
3210
|
+
if (!raw.trim() && pasteBlocksRef.current.length === 0) {
|
|
3211
|
+
if (queuedMessagesRef.current.length > 0 && isProcessingRef.current) {
|
|
3212
|
+
interruptedRunIdRef.current = activeRunIdRef.current;
|
|
3213
|
+
const activeAgent = activeTurnRef.current?.agent ?? agent;
|
|
3214
|
+
activeTurnRef.current = null;
|
|
3215
|
+
clearLiveTurnUi();
|
|
3216
|
+
activeAgent.abort();
|
|
3217
|
+
}
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
inputRef.current?.clear();
|
|
3221
|
+
let message = raw;
|
|
3222
|
+
const blocks = [...pasteBlocksRef.current];
|
|
3223
|
+
replacePasteBlocks([]);
|
|
3224
|
+
const imageBlocks = blocks.filter((b) => b.isImage);
|
|
3225
|
+
const textBlocks = blocks.filter((b) => !b.isImage);
|
|
3226
|
+
for (const block of textBlocks) {
|
|
3227
|
+
message = message.replace(getPasteBlockToken(block), block.content);
|
|
3228
|
+
}
|
|
3229
|
+
// Load images into base64 for multimodal messages
|
|
3230
|
+
const images = [];
|
|
3231
|
+
for (const block of imageBlocks) {
|
|
3232
|
+
// Clipboard image (Alt+V): already has base64 data
|
|
3233
|
+
if (block.clipboardBase64) {
|
|
3234
|
+
message = message.replace(getPasteBlockToken(block), "[clipboard image]");
|
|
3235
|
+
images.push({ path: "clipboard", mediaType: block.clipboardMediaType ?? "image/png", base64: block.clipboardBase64 });
|
|
3236
|
+
continue;
|
|
3237
|
+
}
|
|
3238
|
+
// File path image: read from disk
|
|
3239
|
+
const filePath = block.content.trim();
|
|
3240
|
+
message = message.replace(getPasteBlockToken(block), `[image: ${filePath}]`);
|
|
3241
|
+
try {
|
|
3242
|
+
const fs = require("node:fs");
|
|
3243
|
+
const path = require("node:path");
|
|
3244
|
+
const resolved = path.isAbsolute(filePath) ? filePath : path.resolve(agent.getCwd(), filePath);
|
|
3245
|
+
const buf = fs.readFileSync(resolved);
|
|
3246
|
+
const ext = path.extname(resolved).toLowerCase().replace(".", "");
|
|
3247
|
+
const mimeMap = {
|
|
3248
|
+
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
|
|
3249
|
+
gif: "image/gif", webp: "image/webp", svg: "image/svg+xml",
|
|
3250
|
+
bmp: "image/bmp", ico: "image/x-icon", tif: "image/tiff", tiff: "image/tiff",
|
|
3251
|
+
};
|
|
3252
|
+
images.push({ path: resolved, mediaType: mimeMap[ext] ?? "image/png", base64: buf.toString("base64") });
|
|
3253
|
+
}
|
|
3254
|
+
catch {
|
|
3255
|
+
// File unreadable — keep path as text fallback
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
const displayText = message.trim();
|
|
3259
|
+
const fileBlocks = [...fileMentionBlocksRef.current];
|
|
3260
|
+
fileMentionBlocksRef.current = [];
|
|
3261
|
+
for (const block of fileBlocks) {
|
|
3262
|
+
message = message.replace(getFileMentionToken(block), `@${block.path}`);
|
|
3263
|
+
}
|
|
3264
|
+
if (!message.trim())
|
|
3265
|
+
return;
|
|
3266
|
+
if (!hasApiKeyRef.current) {
|
|
3267
|
+
openApiKeyModal();
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
if (handleCommand(message))
|
|
3271
|
+
return;
|
|
3272
|
+
const { enhancedMessage } = processAtMentions(message.trim(), agent.getCwd());
|
|
3273
|
+
if (isProcessingRef.current) {
|
|
3274
|
+
queuedMessagesRef.current.push({ text: enhancedMessage, displayText });
|
|
3275
|
+
setQueuedMessages(queuedMessagesRef.current.map((msg) => msg.displayText));
|
|
3276
|
+
setTimeout(scrollToBottom, 10);
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
processMessage(enhancedMessage, displayText, images.length > 0 ? images : undefined);
|
|
3280
|
+
}, [agent, clearLiveTurnUi, handleCommand, openApiKeyModal, processMessage, replacePasteBlocks, scrollToBottom]);
|
|
3281
|
+
const hasMessages = messages.length > 0 || streamContent || isProcessing;
|
|
3282
|
+
return (
|
|
3283
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: OpenCode-style copy-on-mouse-up on root surface
|
|
3284
|
+
_jsxs("box", { width: width, height: height, backgroundColor: t.background, flexDirection: "column", onMouseUp: handleRootMouseUp, onMouseDown: handleRootMouseDown, children: [copyFlashId > 0 ? _jsx(CopyFlashBanner, { t: t, width: width }) : null, hasMessages ? (_jsxs("box", { flexGrow: 1, flexDirection: "column", children: [_jsx(SessionHeader, { t: t, modeInfo: modeInfo, sessionTitle: sessionTitle, sessionId: sessionId }), _jsxs("box", { flexGrow: 1, paddingBottom: 1, paddingTop: 1, paddingLeft: 2, paddingRight: 2, gap: 1, children: [_jsxs("scrollbox", { ref: scrollRef, flexGrow: 1, stickyScroll: true, stickyStart: "bottom", children: [messages.map((msg, i) => (_jsx(MessageView, { entry: msg, index: i, t: t, modeColor: modeInfo.color, expandedMessages: expandedMessages }, `${msg.timestamp?.getTime?.() ?? i}-${msg.type}-${msg.remoteKey ?? ""}-${String(msg.content ?? "").slice(0, 24)}`))), liveTurnSourceLabel && (activeToolCalls.length > 0 || streamContent || isProcessing) && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx("text", { fg: t.textMuted, children: liveTurnSourceLabel }) })), activeToolCalls.map((tc) => tc.function.name === "task" ? (_jsx(SubagentTaskLine, { t: t, agent: tryParseArg(tc, "agent") || "sub-agent", label: toolArgs(tc) || "Working", pending: true }, tc.id)) : tc.function.name === "delegate" ? (_jsx(DelegationTaskLine, { t: t, label: toolArgs(tc) || "Background research", pending: true, id: undefined }, tc.id)) : (_jsx(InlineTool, { t: t, pending: true, children: toolLabel(tc) }, tc.id))), activeSubagent && _jsx(SubagentActivity, { t: t, status: activeSubagent }), streamContent && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: streamContent, t: t }) })), isProcessing && !streamContent && activeToolCalls.length === 0 && (_jsx(ShimmerText, { t: t, text: "Planning next moves" })), showPlanPanel && _jsx(PlanQuestionsPanel, { t: t, questions: planQuestions, state: pqs }), pendingPaymentApproval && _jsx(PaymentApprovalPanel, { t: t, payment: pendingPaymentApproval })] }), btwState && _jsx(BtwOverlay, { state: btwState, theme: t }), _jsx("box", { flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, typeahead: typeahead }) })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 })] })] })) : (
|
|
3285
|
+
/* ── Home ───────────────────────────────────────── */
|
|
3286
|
+
_jsxs(_Fragment, { children: [_jsxs("box", { flexGrow: 1, alignItems: "center", paddingLeft: 2, paddingRight: 2, children: [_jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { flexShrink: 0, alignItems: "center", children: _jsx(HeroLogo, { t: t }) }), _jsx("box", { height: 1, minHeight: 0, flexShrink: 1 }), _jsx("box", { width: "100%", maxWidth: 75, flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, placeholder: "What are we building?", typeahead: typeahead }) }), _jsx("box", { height: 2, minHeight: 0, flexShrink: 1 }), _jsx("box", { flexGrow: 1, minHeight: 0 })] }), updateInfo?.hasUpdate && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: "#f59e0b", children: ["┃ Update available: v", startupConfig.version, " → v", updateInfo.latestVersion, " — run /update to install"] }) })), isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsx("text", { fg: "#f59e0b", children: "┃ Updating..." }) })), updateOutput && !isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: updateOutput.startsWith("Update complete") ? "#22c55e" : "#ef4444", children: ["┃ ", updateOutput] }) })), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 }), _jsx("text", { fg: t.textDim, children: `v${startupConfig.version}` })] })] })), showApiKeyModal && (_jsx(ApiKeyModal, { t: t, width: width, height: height, inputRef: apiKeyInputRef, error: apiKeyError, onSubmit: submitApiKey })), showUpdateModal && updateInfo && (_jsx(UpdateModal, { t: t, width: width, height: height, currentVersion: startupConfig.version, latestVersion: updateInfo.latestVersion })), showSlashMenu && (_jsx(SlashMenuModal, { t: t, selectedIndex: slashMenuIndex, width: width, height: height, searchQuery: slashSearchQuery, filteredItems: filteredSlashItems })), showMcpModal && !showMcpEditor && (_jsx(McpBrowserModal, { t: t, width: width, height: height, selectedIndex: mcpModalIndex, searchQuery: mcpSearchQuery, rows: mcpRows })), showMcpEditor && (_jsx(McpEditorModal, { t: t, width: width, height: height, draft: mcpEditorDraft, focusedField: mcpEditorField, syncKey: mcpEditorSyncKey, error: mcpEditorError, title: editingMcpId ? "Edit MCP Server" : "Add MCP Server", labelRef: mcpLabelRef, urlRef: mcpUrlRef, headersRef: mcpHeadersRef, commandRef: mcpCommandRef, argsRef: mcpArgsRef, cwdRef: mcpCwdRef, envRef: mcpEnvRef, onSubmit: submitMcpEditor })), showScheduleModal && (_jsx(ScheduleBrowserModal, { t: t, width: width, height: height, selectedIndex: scheduleModalIndex, searchQuery: scheduleSearchQuery, rows: scheduleRows })), showAgentsModal && !showAgentsEditor && (_jsx(SubagentsBrowserModal, { t: t, width: width, height: height, selectedIndex: agentsModalIndex, searchQuery: agentsSearchQuery, rows: agentRows })), showAgentsEditor && (_jsx(SubagentEditorModal, { t: t, width: width, height: height, draft: agentsEditorDraft, focusedField: agentsEditorField, modelIndex: agentsEditorModelIndex, error: agentsEditorError, title: editingSubagent ? `Edit sub-agent: ${formatSubagentName(editingSubagent.name)}` : "Add sub-agent", nameRef: subagentNameRef, instructionRef: subagentInstructionRef, onSubmit: submitSubagentEditor, showRemoveHint: !!editingSubagent }, `subagent-editor-${agentsEditorSyncKey}`)), showModelPicker && (_jsx(ModelPickerModal, { t: t, currentModel: model, selectedIndex: modelPickerIndex, width: width, height: height, searchQuery: modelSearchQuery, filteredModels: filteredModels, reasoningEffortByModel: reasoningEffortByModel })), showWalletPicker && (_jsx(WalletPickerModal, { t: t, settings: walletSettings, walletInfo: walletDisplayInfo, focusIndex: walletFocusIndex, width: width, height: height })), showSandboxPicker && (_jsx(SandboxPickerModal, { t: t, currentMode: sandboxMode, settings: sandboxSettings, focusIndex: sandboxSettingsFocusIndex, editing: sandboxSettingsEditing, editBuffer: sandboxSettingsEditBuffer, width: width, height: height })), showConnectModal && (_jsx(ConnectModal, { t: t, width: width, height: height, selectedIndex: connectModalIndex, channels: CONNECT_CHANNELS })), showTelegramTokenModal && (_jsx(TelegramTokenModal, { t: t, width: width, height: height, inputRef: telegramTokenInputRef, error: telegramTokenError, onSubmit: submitTelegramToken })), showTelegramPairModal && (_jsx(TelegramPairModal, { t: t, width: width, height: height, inputRef: telegramPairInputRef, error: telegramPairError, onSubmit: () => void submitTelegramPair() }))] }));
|
|
3287
|
+
}
|
|
3288
|
+
/* ── Session Header ──────────────────────────────────────────── */
|
|
3289
|
+
function SessionHeader({ t, modeInfo, sessionTitle, sessionId, }) {
|
|
3290
|
+
return (_jsx("box", { flexShrink: 0, width: "100%", children: _jsxs("box", { flexDirection: "row", width: "100%", paddingTop: 1, paddingBottom: 1, paddingLeft: 2, paddingRight: 2, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeInfo.color }, children: _jsx("b", { children: modeInfo.label }) }), sessionTitle ? (_jsx("span", { style: { fg: t.text }, children: _jsxs("b", { children: [": ", sessionTitle] }) })) : null] }), _jsx("box", { flexGrow: 1 }), sessionId ? _jsx("text", { fg: t.textDim, children: sessionId }) : null] }) }));
|
|
3291
|
+
}
|
|
3292
|
+
/* ── Prompt Box ──────────────────────────────────────────────── */
|
|
3293
|
+
const TEXTAREA_KEYBINDINGS = [
|
|
3294
|
+
{ name: "return", action: "submit" },
|
|
3295
|
+
{ name: "return", shift: true, action: "newline" },
|
|
3296
|
+
];
|
|
3297
|
+
function formatTokenCount(tokens) {
|
|
3298
|
+
if (tokens >= 1_000_000)
|
|
3299
|
+
return `${(tokens / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
|
|
3300
|
+
if (tokens >= 1_000)
|
|
3301
|
+
return `${Math.round(tokens / 1_000)}K`;
|
|
3302
|
+
return String(tokens);
|
|
3303
|
+
}
|
|
3304
|
+
function ContextMeter({ t, stats }) {
|
|
3305
|
+
return (_jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: `${Math.round(stats.ratioRemaining * 100)}%` }), _jsx("span", { style: { fg: t.textDim }, children: ` ${formatTokenCount(stats.remainingTokens)}` })] }));
|
|
3306
|
+
}
|
|
3307
|
+
function PromptBox({ t, inputRef, isProcessing, showModelPicker, showSandboxPicker, showWalletPicker, showSlashMenu, showPlanQuestions, showApiKeyModal, blockPrompt, onSubmit, onPaste, pasteBlocks: _pasteBlocks, modeInfo, model, modelInfo, contextStats, placeholder, queuedCount, queuedMessages, typeahead, }) {
|
|
3308
|
+
const hasQueue = (queuedMessages?.length ?? 0) > 0;
|
|
3309
|
+
const showSuggestions = typeahead?.visible ?? false;
|
|
3310
|
+
return (_jsxs("box", { backgroundColor: t.backgroundPanel, children: [_jsxs("box", { children: [hasQueue && (_jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.queueBg, flexShrink: 0, children: [queuedMessages.map((msg, i) => (
|
|
3311
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: append-only queue of plain strings
|
|
3312
|
+
_jsxs("text", { fg: t.text, children: ["→ ", msg] }, i))), _jsx("box", { height: 1 }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "send now" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "↑ " }), _jsx("span", { style: { fg: t.textMuted }, children: "edit" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "cancel" })] })] })), showSuggestions && typeahead && (_jsx(SuggestionOverlay, { t: t, suggestions: typeahead.suggestions, selectedIndex: typeahead.selectedIndex })), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundElement, flexDirection: "row", gap: 2, alignItems: "flex-start", flexShrink: 0, children: [_jsx(PromptModeLabel, { t: t, modeInfo: modeInfo, isProcessing: isProcessing }), _jsx("box", { flexGrow: 1, children: _jsx("textarea", { ref: inputRef, focused: !showModelPicker &&
|
|
3313
|
+
!showSandboxPicker &&
|
|
3314
|
+
!showWalletPicker &&
|
|
3315
|
+
!showSlashMenu &&
|
|
3316
|
+
!showPlanQuestions &&
|
|
3317
|
+
!showApiKeyModal &&
|
|
3318
|
+
!blockPrompt, placeholder: isProcessing ? "Queue a follow-up... (esc to interrupt)" : placeholder || "Message muonroi-cli...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 10, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit, onPaste: onPaste }) })] })] }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingLeft: 2, paddingRight: 2, height: 1, flexShrink: 0, children: [_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: [_jsx("text", { fg: t.text, children: modelInfo?.name || model }), contextStats ? _jsx(ContextMeter, { t: t, stats: contextStats }) : null] }), _jsx("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: isProcessing ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "queue" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: (queuedCount ?? 0) > 0 ? "clear queue" : "interrupt" })] })] })) : showSuggestions ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "accept" })] }), _jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("text", { fg: t.text, children: ["@ ", _jsx("span", { style: { fg: t.textMuted }, children: "files" })] }), _jsxs("text", { fg: t.text, children: ["shift+enter ", _jsx("span", { style: { fg: t.textMuted }, children: "new line" })] }), _jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "modes" })] })] })) })] })] }));
|
|
3319
|
+
}
|
|
3320
|
+
function PromptModeLabel({ t, modeInfo, isProcessing, }) {
|
|
3321
|
+
if (!isProcessing) {
|
|
3322
|
+
return (_jsx("text", { fg: modeInfo.color, children: _jsx("b", { children: modeInfo.label }) }));
|
|
3323
|
+
}
|
|
3324
|
+
return _jsx(PromptLoadingBoxes, { t: t, color: modeInfo.color });
|
|
3325
|
+
}
|
|
3326
|
+
function PromptLoadingBoxes({ t: _t, color }) {
|
|
3327
|
+
const [frame, setFrame] = useState(0);
|
|
3328
|
+
useEffect(() => {
|
|
3329
|
+
const id = setInterval(() => setFrame((n) => (n + 1) % PROMPT_LOADING_FRAMES.length), 120);
|
|
3330
|
+
return () => clearInterval(id);
|
|
3331
|
+
}, []);
|
|
3332
|
+
const step = PROMPT_LOADING_FRAMES[frame] ?? PROMPT_LOADING_FRAMES[0];
|
|
3333
|
+
return (_jsx("text", { children: [0, 1, 2].map((idx) => (_jsx("span", { style: { fg: promptLoadingCellColor(color, idx, step.active, step.forward) }, children: promptLoadingCellGlyph(idx, step.active, step.forward) }, idx))) }));
|
|
3334
|
+
}
|
|
3335
|
+
function promptLoadingCellGlyph(index, active, forward) {
|
|
3336
|
+
const distance = forward ? active - index : index - active;
|
|
3337
|
+
return distance >= 0 && distance < 2 ? "■" : "⬝";
|
|
3338
|
+
}
|
|
3339
|
+
function promptLoadingCellColor(color, index, active, forward) {
|
|
3340
|
+
const distance = forward ? active - index : index - active;
|
|
3341
|
+
if (distance === 0)
|
|
3342
|
+
return color;
|
|
3343
|
+
if (distance === 1)
|
|
3344
|
+
return withAlpha(color, 0.72);
|
|
3345
|
+
return withAlpha(color, 0.22);
|
|
3346
|
+
}
|
|
3347
|
+
function withAlpha(color, alpha) {
|
|
3348
|
+
const normalized = color.trim();
|
|
3349
|
+
const hex = normalized.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
|
3350
|
+
if (!hex)
|
|
3351
|
+
return color;
|
|
3352
|
+
const body = hex[1];
|
|
3353
|
+
const expanded = body.length === 3
|
|
3354
|
+
? body
|
|
3355
|
+
.split("")
|
|
3356
|
+
.map((ch) => ch + ch)
|
|
3357
|
+
.join("")
|
|
3358
|
+
: body;
|
|
3359
|
+
const alphaHex = Math.round(Math.max(0, Math.min(1, alpha)) * 255)
|
|
3360
|
+
.toString(16)
|
|
3361
|
+
.padStart(2, "0");
|
|
3362
|
+
return `#${expanded}${alphaHex}`;
|
|
3363
|
+
}
|
|
3364
|
+
function CopyFlashBanner({ t, width }) {
|
|
3365
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 1, width: width, zIndex: 500, alignItems: "center", flexShrink: 0, backgroundColor: t.background, shouldFill: false, children: _jsx("box", { height: 3, paddingLeft: 2, paddingRight: 2, backgroundColor: t.queueBg, justifyContent: "center", alignItems: "center", children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: "✓ " }), _jsx("span", { style: { fg: t.text }, children: "Copied to clipboard" })] }) }) }));
|
|
3366
|
+
}
|
|
3367
|
+
function ApiKeyModal({ t, width, height, inputRef, error, onSubmit, }) {
|
|
3368
|
+
const overlayBg = "#000000cc";
|
|
3369
|
+
const panelWidth = Math.min(68, width - 6);
|
|
3370
|
+
const panelHeight = 13;
|
|
3371
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3372
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Add API key" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "Paste your xAI API key to unlock chat. You can hide this prompt with esc." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "xai-...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save key · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "hide" })] })) })] }) }));
|
|
3373
|
+
}
|
|
3374
|
+
/* ── Messages ────────────────────────────────────────────────── */
|
|
3375
|
+
const USER_MSG_COLLAPSED_LINES = 5;
|
|
3376
|
+
function UserMessageContent({ content, t, expanded }) {
|
|
3377
|
+
const lines = content.split("\n");
|
|
3378
|
+
const isLong = lines.length > USER_MSG_COLLAPSED_LINES;
|
|
3379
|
+
if (!isLong) {
|
|
3380
|
+
return _jsx("text", { fg: t.text, children: content });
|
|
3381
|
+
}
|
|
3382
|
+
if (expanded) {
|
|
3383
|
+
return (_jsxs(_Fragment, { children: [_jsx("text", { fg: t.text, children: content }), _jsx("box", { marginTop: 1, children: _jsxs("text", { fg: t.textDim, children: ["ctrl+e ", _jsx("span", { style: { fg: t.textMuted }, children: "collapse" })] }) })] }));
|
|
3384
|
+
}
|
|
3385
|
+
const preview = lines.slice(0, USER_MSG_COLLAPSED_LINES).join("\n");
|
|
3386
|
+
const hiddenCount = lines.length - USER_MSG_COLLAPSED_LINES;
|
|
3387
|
+
return (_jsxs(_Fragment, { children: [_jsx("text", { fg: t.text, children: preview }), _jsx("box", { marginTop: 1, children: _jsxs("text", { fg: t.textDim, children: ["ctrl+e ", _jsx("span", { style: { fg: t.textMuted }, children: `expand (${hiddenCount} more lines)` })] }) })] }));
|
|
3388
|
+
}
|
|
3389
|
+
function MessageView({ entry, index, t, modeColor, expandedMessages, }) {
|
|
3390
|
+
switch (entry.type) {
|
|
3391
|
+
case "user":
|
|
3392
|
+
return (_jsx("box", { border: ["left"], customBorderChars: SPLIT, borderColor: entry.modeColor || modeColor, marginTop: index === 0 ? 0 : 1, marginBottom: 1, children: _jsxs("box", { paddingTop: 1, paddingBottom: 1, paddingLeft: 2, backgroundColor: t.backgroundPanel, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, _jsx(UserMessageContent, { content: entry.content, t: t, expanded: expandedMessages?.has(index) ?? false })] }) }));
|
|
3393
|
+
case "assistant":
|
|
3394
|
+
return (_jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, _jsx(Markdown, { content: entry.content, t: t })] }));
|
|
3395
|
+
case "tool_call":
|
|
3396
|
+
return (_jsx("box", { paddingLeft: 3, marginTop: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: entry.modeColor || modeColor }, children: "▣ " }), _jsx("span", { style: { fg: t.textMuted }, children: entry.content.replace("▣ ", "") })] }) }));
|
|
3397
|
+
case "tool_result": {
|
|
3398
|
+
const name = entry.toolCall?.function.name || "tool";
|
|
3399
|
+
const args = toolArgs(entry.toolCall);
|
|
3400
|
+
const diff = entry.toolResult?.diff;
|
|
3401
|
+
const plan = entry.toolResult?.plan;
|
|
3402
|
+
if (name === "generate_plan" && plan) {
|
|
3403
|
+
return _jsx(PlanView, { plan: plan, t: t });
|
|
3404
|
+
}
|
|
3405
|
+
if (name === "task" && entry.toolResult?.task) {
|
|
3406
|
+
return _jsx(TaskResultView, { t: t, entry: entry });
|
|
3407
|
+
}
|
|
3408
|
+
if (name === "delegate" && entry.toolResult?.delegation) {
|
|
3409
|
+
return _jsx(DelegationResultView, { t: t, entry: entry });
|
|
3410
|
+
}
|
|
3411
|
+
if (name === "delegation_list") {
|
|
3412
|
+
return _jsx(DelegationListView, { t: t, content: entry.content });
|
|
3413
|
+
}
|
|
3414
|
+
if (name === "delegation_read") {
|
|
3415
|
+
return _jsx(ToolTextOutputView, { t: t, label: toolLabel(entry.toolCall), content: entry.content });
|
|
3416
|
+
}
|
|
3417
|
+
if (name === "lsp") {
|
|
3418
|
+
const lspOp = tryParseArg(entry.toolCall, "operation") || "query";
|
|
3419
|
+
const lspFile = tryParseArg(entry.toolCall, "filePath") || "";
|
|
3420
|
+
const lspLine = tryParseArg(entry.toolCall, "line");
|
|
3421
|
+
const lspPos = lspLine ? `:${lspLine}` : "";
|
|
3422
|
+
return (_jsxs("box", { gap: 0, marginTop: 1, children: [_jsx(InlineTool, { t: t, pending: false, children: `lsp ${lspOp} ${lspFile}${lspPos}` }), _jsx(LspResultView, { t: t, operation: lspOp, filePath: lspFile, position: lspPos, content: entry.content })] }));
|
|
3423
|
+
}
|
|
3424
|
+
if ((entry.toolResult?.media?.length ?? 0) > 0) {
|
|
3425
|
+
if (name === "generate_image" || name === "generate_video") {
|
|
3426
|
+
return _jsx(MediaAutoOpenView, { t: t, label: toolLabel(entry.toolCall), toolResult: entry.toolResult });
|
|
3427
|
+
}
|
|
3428
|
+
return _jsx(MediaToolResultView, { t: t, label: toolLabel(entry.toolCall), toolResult: entry.toolResult });
|
|
3429
|
+
}
|
|
3430
|
+
if (name === "write_file" || name === "edit_file") {
|
|
3431
|
+
const filePath = diff?.filePath || tryParseArg(entry.toolCall, "file_path") || tryParseArg(entry.toolCall, "path") || args;
|
|
3432
|
+
const label = name === "write_file" ? `Write ${filePath}` : `Edit ${filePath}`;
|
|
3433
|
+
return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), diff && _jsx(DiffView, { t: t, diff: diff }), (entry.toolResult?.lspDiagnostics?.length ?? 0) > 0 && (_jsx(LspDiagnosticsView, { t: t, diagnostics: entry.toolResult?.lspDiagnostics ?? [] }))] }));
|
|
3434
|
+
}
|
|
3435
|
+
if (name === "bash" && entry.toolResult?.backgroundProcess) {
|
|
3436
|
+
const bp = entry.toolResult.backgroundProcess;
|
|
3437
|
+
return _jsx(BackgroundProcessLine, { t: t, id: bp.id, pid: bp.pid, command: bp.command });
|
|
3438
|
+
}
|
|
3439
|
+
if (name === "process_logs") {
|
|
3440
|
+
return _jsx(ProcessLogsView, { t: t, content: entry.content });
|
|
3441
|
+
}
|
|
3442
|
+
if (name === "process_stop" || name === "process_list") {
|
|
3443
|
+
return (_jsx(InlineTool, { t: t, pending: false, children: entry.content }));
|
|
3444
|
+
}
|
|
3445
|
+
if (name === "read_file")
|
|
3446
|
+
return (_jsx(InlineTool, { t: t, pending: false, children: `Read ${trunc(tryParseArg(entry.toolCall, "file_path") || tryParseArg(entry.toolCall, "path") || args, 60)}` }));
|
|
3447
|
+
if (name === "grep")
|
|
3448
|
+
return (_jsx(InlineTool, { t: t, pending: false, children: `Grep ${trunc(args, 60)}` }));
|
|
3449
|
+
if (name === "search_web" || name === "search_x")
|
|
3450
|
+
return (_jsxs(InlineTool, { t: t, pending: false, children: [name === "search_web" ? "Web" : "X", ` Search "${trunc(args, 60)}"`] }));
|
|
3451
|
+
return (_jsx(InlineTool, { t: t, pending: false, children: trunc(name === "bash" ? args : `${name} ${args}`, 80) }));
|
|
3452
|
+
}
|
|
3453
|
+
case "structured_response": {
|
|
3454
|
+
const sr = entry.structuredResponse;
|
|
3455
|
+
if (!sr)
|
|
3456
|
+
return _jsx("text", { fg: t.textMuted, children: entry.content });
|
|
3457
|
+
return _jsx(StructuredResponseView, { t: t, sr: sr, modeColor: entry.modeColor || modeColor });
|
|
3458
|
+
}
|
|
3459
|
+
default:
|
|
3460
|
+
return _jsx("text", { fg: t.textMuted, children: entry.content });
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
function StructuredResponseView({ t, sr, modeColor }) {
|
|
3464
|
+
const d = sr.data;
|
|
3465
|
+
switch (sr.taskType) {
|
|
3466
|
+
case "refactor": {
|
|
3467
|
+
const r = d;
|
|
3468
|
+
return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeColor }, children: "◆ " }), _jsx("span", { style: { fg: "#ffffff" }, children: r.summary ?? "Refactor" })] }), (r.changes ?? []).map((c, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: " ── " }), _jsx("span", { style: { fg: t.accent }, children: c.file }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), c.diff.split("\n").map((line, j) => {
|
|
3469
|
+
const fg = line.startsWith("+") ? t.diffAddedFg : line.startsWith("-") ? t.diffRemovedFg : t.text;
|
|
3470
|
+
return _jsx("text", { fg: fg, children: ` ${line}` }, `rl${i}-${j}`);
|
|
3471
|
+
})] }, `rc${i}`))), r.verify_command && (_jsx("text", { fg: t.textMuted, marginTop: 1, children: ` verify: ${r.verify_command}` }))] }));
|
|
3472
|
+
}
|
|
3473
|
+
case "debug": {
|
|
3474
|
+
const r = d;
|
|
3475
|
+
return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeColor }, children: "◆ " }), _jsx("span", { style: { fg: t.textMuted }, children: "hypothesis: " }), _jsx("span", { children: r.hypothesis })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: " root cause: " }), _jsx("span", { style: { fg: "#ffffff" }, children: r.root_cause })] }), r.fix && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: " ── fix: " }), _jsx("span", { style: { fg: t.accent }, children: r.fix.file }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), r.fix.diff.split("\n").map((line, j) => {
|
|
3476
|
+
const fg = line.startsWith("+") ? t.diffAddedFg : line.startsWith("-") ? t.diffRemovedFg : t.text;
|
|
3477
|
+
return _jsx("text", { fg: fg, children: ` ${line}` }, `dl${j}`);
|
|
3478
|
+
})] })), r.verify_command && (_jsx("text", { fg: t.textMuted, marginTop: 1, children: ` verify: ${r.verify_command}` }))] }));
|
|
3479
|
+
}
|
|
3480
|
+
case "plan": {
|
|
3481
|
+
const r = d;
|
|
3482
|
+
return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [(r.steps ?? []).map((s, i) => (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.planStepNum }, children: `${i + 1}. ` }), _jsx("span", { style: { fg: "#ffffff" }, children: s.action })] }), _jsx("text", { fg: t.planStepDesc, children: ` done when: ${s.criterion}` }), s.rationale && _jsx("text", { fg: t.textMuted, children: ` why: ${s.rationale}` })] }, `ps${i}`))), (r.assumptions?.length ?? 0) > 0 && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: " assumptions:" }), r.assumptions.map((a, i) => (_jsx("text", { fg: t.text, children: ` - ${a}` }, `pa${i}`)))] })), (r.risks?.length ?? 0) > 0 && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: " risks:" }), r.risks.map((rk, i) => (_jsx("text", { fg: t.diffRemovedFg, children: ` - ${rk}` }, `pr${i}`)))] }))] }));
|
|
3483
|
+
}
|
|
3484
|
+
case "analyze": {
|
|
3485
|
+
const r = d;
|
|
3486
|
+
const sevColor = (s) => s === "high" ? t.diffRemovedFg : s === "medium" ? t.planStepNum : t.textMuted;
|
|
3487
|
+
return (_jsx("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: (r.findings ?? []).map((f, i) => (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { style: { fg: sevColor(f.severity) }, children: `[${f.severity.toUpperCase()}] ` }), _jsx("span", { children: f.text })] }), _jsx("text", { fg: t.textMuted, children: ` evidence: ${f.evidence}` })] }, `af${i}`))) }));
|
|
3488
|
+
}
|
|
3489
|
+
case "documentation": {
|
|
3490
|
+
const r = d;
|
|
3491
|
+
return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsx("text", { children: r.content }), (r.examples ?? []).map((ex, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: ex.description }), _jsx("text", { fg: t.mdCode, children: ex.code })] }, `de${i}`)))] }));
|
|
3492
|
+
}
|
|
3493
|
+
case "generate": {
|
|
3494
|
+
const r = d;
|
|
3495
|
+
return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [r.explanation && _jsx("text", { fg: t.textMuted, children: r.explanation }), (r.files ?? []).map((f, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: "── " }), _jsx("span", { style: { fg: t.accent }, children: f.path }), _jsx("span", { style: { fg: t.textMuted }, children: ` (${f.language})` }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), _jsx("text", { fg: t.mdCodeBlockFg, children: f.content })] }, `gf${i}`)))] }));
|
|
3496
|
+
}
|
|
3497
|
+
default:
|
|
3498
|
+
return _jsx("text", { fg: t.text, children: JSON.stringify(d, null, 2) });
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
const MAX_DIFF_ROWS = 20;
|
|
3502
|
+
const LINE_NUM_WIDTH = 4;
|
|
3503
|
+
function parsePatch(patch) {
|
|
3504
|
+
const lines = patch.split("\n");
|
|
3505
|
+
const rows = [];
|
|
3506
|
+
let oldLine = 0;
|
|
3507
|
+
let newLine = 0;
|
|
3508
|
+
let prevOldEnd = 0;
|
|
3509
|
+
for (const line of lines) {
|
|
3510
|
+
const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
3511
|
+
if (hunkMatch) {
|
|
3512
|
+
oldLine = parseInt(hunkMatch[1], 10);
|
|
3513
|
+
newLine = parseInt(hunkMatch[2], 10);
|
|
3514
|
+
const skipped = oldLine - prevOldEnd - 1;
|
|
3515
|
+
if (skipped > 0) {
|
|
3516
|
+
rows.push({ kind: "separator", count: skipped });
|
|
3517
|
+
}
|
|
3518
|
+
continue;
|
|
3519
|
+
}
|
|
3520
|
+
if (line.startsWith("---") || line.startsWith("+++") || line.startsWith("\\"))
|
|
3521
|
+
continue;
|
|
3522
|
+
if (line.startsWith("Index:") || line.startsWith("===="))
|
|
3523
|
+
continue;
|
|
3524
|
+
if (line.startsWith("-")) {
|
|
3525
|
+
rows.push({ kind: "removed", oldNum: oldLine, text: line.slice(1) });
|
|
3526
|
+
oldLine++;
|
|
3527
|
+
prevOldEnd = oldLine - 1;
|
|
3528
|
+
}
|
|
3529
|
+
else if (line.startsWith("+")) {
|
|
3530
|
+
rows.push({ kind: "added", newNum: newLine, text: line.slice(1) });
|
|
3531
|
+
newLine++;
|
|
3532
|
+
}
|
|
3533
|
+
else if (line.length > 0 || (oldLine > 0 && newLine > 0)) {
|
|
3534
|
+
const content = line.startsWith(" ") ? line.slice(1) : line;
|
|
3535
|
+
rows.push({ kind: "context", oldNum: oldLine, newNum: newLine, text: content });
|
|
3536
|
+
oldLine++;
|
|
3537
|
+
newLine++;
|
|
3538
|
+
prevOldEnd = oldLine - 1;
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
return rows;
|
|
3542
|
+
}
|
|
3543
|
+
function DiffView({ t, diff }) {
|
|
3544
|
+
const rows = parsePatch(diff.patch);
|
|
3545
|
+
if (rows.length === 0)
|
|
3546
|
+
return null;
|
|
3547
|
+
const truncated = rows.length > MAX_DIFF_ROWS;
|
|
3548
|
+
const visible = truncated ? rows.slice(0, MAX_DIFF_ROWS) : rows;
|
|
3549
|
+
const pad = (n) => n !== undefined ? String(n).padStart(LINE_NUM_WIDTH) : " ".repeat(LINE_NUM_WIDTH);
|
|
3550
|
+
return (_jsx("box", { paddingLeft: 5, marginTop: 0, flexShrink: 0, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { backgroundColor: t.diffHeader, paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.diffHeaderFg }, children: diff.filePath }), _jsx("span", { style: { fg: t.textDim }, children: " " }), _jsx("span", { style: { fg: t.diffRemovedFg }, children: `-${diff.removals}` }), _jsx("span", { style: { fg: t.textDim }, children: " " }), _jsx("span", { style: { fg: t.diffAddedFg }, children: `+${diff.additions}` })] }) }), visible.map((row, i) => {
|
|
3551
|
+
if (row.kind === "separator") {
|
|
3552
|
+
return (
|
|
3553
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: separator rows lack unique identifiers
|
|
3554
|
+
_jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", row.count, " unmodified lines"] }) }, `sep-${i}`));
|
|
3555
|
+
}
|
|
3556
|
+
if (row.kind === "removed") {
|
|
3557
|
+
return (_jsxs("box", { backgroundColor: t.diffRemoved, flexDirection: "row", children: [_jsx("text", { fg: t.diffRemovedLineNum, children: pad(row.oldNum) }), _jsx("text", { fg: t.diffRemovedFg, children: ` ${row.text}` })] }, `rm-${row.oldNum}`));
|
|
3558
|
+
}
|
|
3559
|
+
if (row.kind === "added") {
|
|
3560
|
+
return (_jsxs("box", { backgroundColor: t.diffAdded, flexDirection: "row", children: [_jsx("text", { fg: t.diffAddedLineNum, children: pad(row.newNum) }), _jsx("text", { fg: t.diffAddedFg, children: ` ${row.text}` })] }, `add-${row.newNum}`));
|
|
3561
|
+
}
|
|
3562
|
+
return (_jsxs("box", { backgroundColor: t.diffContext, flexDirection: "row", children: [_jsx("text", { fg: t.diffLineNumber, children: pad(row.oldNum) }), _jsx("text", { fg: t.diffContextFg, children: ` ${row.text}` })] }, `ctx-${row.oldNum}`));
|
|
3563
|
+
}), truncated && (_jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", rows.length - MAX_DIFF_ROWS, " more lines"] }) }))] }) }));
|
|
3564
|
+
}
|
|
3565
|
+
const MAX_LSP_RESULT_LINES = 10;
|
|
3566
|
+
function LspResultView({ t, operation, filePath, position, content, }) {
|
|
3567
|
+
const body = content.trim();
|
|
3568
|
+
const lines = body.split("\n");
|
|
3569
|
+
const truncated = lines.length > MAX_LSP_RESULT_LINES;
|
|
3570
|
+
const visible = truncated ? lines.slice(0, MAX_LSP_RESULT_LINES).join("\n") : body;
|
|
3571
|
+
const label = `${operation} ${filePath}${position}`;
|
|
3572
|
+
return (_jsx("box", { paddingLeft: 5, marginTop: 0, flexShrink: 0, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { backgroundColor: t.diffHeader, paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "lsp" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.diffHeaderFg }, children: label })] }) }), _jsx("box", { backgroundColor: t.mdCodeBlockBg, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: t.mdCodeBlockFg, children: visible }) }), truncated && (_jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", lines.length - MAX_LSP_RESULT_LINES, " more lines"] }) }))] }) }));
|
|
3573
|
+
}
|
|
3574
|
+
function LspDiagnosticsView({ t, diagnostics }) {
|
|
3575
|
+
const files = diagnostics.slice(0, 3);
|
|
3576
|
+
return (_jsx("box", { paddingLeft: 5, marginTop: 1, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { children: _jsx("text", { fg: t.textMuted, children: "LSP diagnostics" }) }), files.map((entry) => (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: t.textDim, children: `${entry.serverId} • ${entry.filePath}` }), entry.diagnostics.slice(0, 5).map((diagnostic, index) => (_jsx("text", { fg: diagnostic.severity === 1 ? t.diffRemovedFg : diagnostic.severity === 2 ? t.primary : t.textMuted, children: `${formatLspSeverity(diagnostic.severity)} ${diagnostic.range.start.line + 1}:${diagnostic.range.start.character + 1} ${diagnostic.message}` }, `${entry.serverId}:${entry.filePath}:${index}`)))] }, `${entry.serverId}:${entry.filePath}`)))] }) }));
|
|
3577
|
+
}
|
|
3578
|
+
function formatLspSeverity(severity) {
|
|
3579
|
+
switch (severity) {
|
|
3580
|
+
case 1:
|
|
3581
|
+
return "error";
|
|
3582
|
+
case 2:
|
|
3583
|
+
return "warning";
|
|
3584
|
+
case 3:
|
|
3585
|
+
return "info";
|
|
3586
|
+
case 4:
|
|
3587
|
+
return "hint";
|
|
3588
|
+
default:
|
|
3589
|
+
return "issue";
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
function ShimmerText({ t, text }) {
|
|
3593
|
+
return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: _jsx(LoadingSpinner, {}) }), _jsxs("span", { style: { fg: t.textMuted }, children: [" ", text] })] }) }));
|
|
3594
|
+
}
|
|
3595
|
+
function InlineTool({ t, pending: _pending, children }) {
|
|
3596
|
+
return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { fg: t.textMuted, children: ["→ ", children] }) }));
|
|
3597
|
+
}
|
|
3598
|
+
function SubagentTaskLine({ t, agent, label, pending }) {
|
|
3599
|
+
const displayLabel = compactTaskLabel(label);
|
|
3600
|
+
const displayAgent = formatSubagentName(agent);
|
|
3601
|
+
return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [pending ? (_jsx("span", { style: { fg: t.subagentAccent }, children: _jsx(LoadingSpinner, {}) })) : null, pending ? " " : "", _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: `${displayAgent}: ${displayLabel}` }) })] }) }));
|
|
3602
|
+
}
|
|
3603
|
+
function DelegationTaskLine({ t, label, pending, id }) {
|
|
3604
|
+
const displayLabel = compactTaskLabel(label);
|
|
3605
|
+
return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [pending ? (_jsx("span", { style: { fg: t.subagentAccent }, children: _jsx(LoadingSpinner, {}) })) : (_jsx("span", { style: { fg: t.subagentAccent }, children: "◆" })), " ", _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: "Background" }) }), _jsxs("span", { style: { fg: t.textMuted }, children: [" — ", displayLabel] }), id ? _jsx("span", { style: { fg: t.textDim }, children: ` (${id})` }) : null] }) }));
|
|
3606
|
+
}
|
|
3607
|
+
function LoadingSpinner() {
|
|
3608
|
+
const [frame, setFrame] = useState(0);
|
|
3609
|
+
useEffect(() => {
|
|
3610
|
+
const id = setInterval(() => setFrame((n) => (n + 1) % LOADING_SPINNER_FRAMES.length), 120);
|
|
3611
|
+
return () => clearInterval(id);
|
|
3612
|
+
}, []);
|
|
3613
|
+
return _jsx(_Fragment, { children: LOADING_SPINNER_FRAMES[frame] });
|
|
3614
|
+
}
|
|
3615
|
+
function SubagentActivity({ t, status }) {
|
|
3616
|
+
return (_jsx("box", { paddingLeft: 5, children: _jsxs("text", { fg: t.textMuted, children: ["→ ", truncateLine(status.detail, 100)] }) }));
|
|
3617
|
+
}
|
|
3618
|
+
function TaskResultView({ t, entry }) {
|
|
3619
|
+
const task = entry.toolResult?.task;
|
|
3620
|
+
if (!task)
|
|
3621
|
+
return null;
|
|
3622
|
+
return (_jsxs("box", { gap: 0, children: [_jsx(SubagentTaskLine, { t: t, agent: task.agent, label: task.description, pending: false }), _jsx("box", { paddingLeft: 5, children: _jsxs("text", { fg: t.text, children: [formatSubagentName(task.agent), ": ", truncateLine(task.summary, 90)] }) })] }));
|
|
3623
|
+
}
|
|
3624
|
+
function DelegationResultView({ t, entry }) {
|
|
3625
|
+
const delegation = entry.toolResult?.delegation;
|
|
3626
|
+
if (!delegation)
|
|
3627
|
+
return null;
|
|
3628
|
+
return _jsx(DelegationTaskLine, { t: t, label: delegation.description, pending: false, id: delegation.id });
|
|
3629
|
+
}
|
|
3630
|
+
function DelegationListView({ t, content }) {
|
|
3631
|
+
const items = parseDelegationList(content);
|
|
3632
|
+
if (items.length === 0) {
|
|
3633
|
+
return (_jsx(InlineTool, { t: t, pending: false, children: "No background delegations" }));
|
|
3634
|
+
}
|
|
3635
|
+
return (_jsx("box", { paddingLeft: 3, gap: 0, children: items.map((item) => {
|
|
3636
|
+
const statusColor = item.status === "complete"
|
|
3637
|
+
? "#8adf8a"
|
|
3638
|
+
: item.status === "running"
|
|
3639
|
+
? t.subagentAccent
|
|
3640
|
+
: item.status === "error"
|
|
3641
|
+
? "#df8a8a"
|
|
3642
|
+
: t.textMuted;
|
|
3643
|
+
return (_jsx("box", { children: _jsxs("text", { children: [_jsx("span", { style: { fg: statusColor }, children: "◆ " }), _jsx("span", { style: { fg: t.text }, children: item.id }), _jsx("span", { style: { fg: statusColor }, children: ` ${item.status}` }), _jsxs("span", { style: { fg: t.textMuted }, children: [" — ", truncateLine(item.label, 60)] })] }) }, item.id));
|
|
3644
|
+
}) }));
|
|
3645
|
+
}
|
|
3646
|
+
function parseDelegationList(content) {
|
|
3647
|
+
const items = [];
|
|
3648
|
+
for (const line of content.split("\n")) {
|
|
3649
|
+
const match = line.match(/`([^`]+)`\s+\[(\w+)]\s+(.*)/);
|
|
3650
|
+
if (match) {
|
|
3651
|
+
items.push({ id: match[1], status: match[2], label: match[3].trim() });
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
return items;
|
|
3655
|
+
}
|
|
3656
|
+
function BackgroundProcessLine({ t, id, pid, command }) {
|
|
3657
|
+
return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.subagentAccent }, children: "◆ " }), _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: "Background process" }) }), _jsx("span", { style: { fg: t.textMuted }, children: ` id:${id} pid:${pid}` }), _jsxs("span", { style: { fg: t.textDim }, children: [" — ", truncateLine(command, 60)] })] }) }));
|
|
3658
|
+
}
|
|
3659
|
+
function formatScheduleDetails(schedule, daemonStatus) {
|
|
3660
|
+
const daemonText = daemonStatus.running
|
|
3661
|
+
? `running${daemonStatus.pid ? ` (pid ${daemonStatus.pid})` : ""}`
|
|
3662
|
+
: "not running";
|
|
3663
|
+
return [
|
|
3664
|
+
`Schedule: ${schedule.name}`,
|
|
3665
|
+
`ID: ${schedule.id}`,
|
|
3666
|
+
`Type: ${schedule.cron ? "recurring" : "one-time"}`,
|
|
3667
|
+
`Cron: ${schedule.cron ?? "runs once immediately"}`,
|
|
3668
|
+
`Enabled: ${schedule.enabled ? "yes" : "no"}`,
|
|
3669
|
+
`Model: ${schedule.model}`,
|
|
3670
|
+
`Directory: ${schedule.directory}`,
|
|
3671
|
+
`Last run: ${schedule.lastRunAt ?? "never"}`,
|
|
3672
|
+
`Daemon: ${daemonText}`,
|
|
3673
|
+
"",
|
|
3674
|
+
"Instruction:",
|
|
3675
|
+
schedule.instruction,
|
|
3676
|
+
].join("\n");
|
|
3677
|
+
}
|
|
3678
|
+
function ProcessLogsView({ t, content }) {
|
|
3679
|
+
const lines = content.split("\n");
|
|
3680
|
+
const header = lines[0] || "";
|
|
3681
|
+
const body = lines.slice(1).join("\n").trim();
|
|
3682
|
+
return (_jsxs("box", { paddingLeft: 3, gap: 0, children: [_jsxs("text", { fg: t.textMuted, children: ["→ ", header] }), body ? (_jsx("box", { paddingLeft: 2, marginTop: 0, children: _jsx("box", { backgroundColor: t.mdCodeBlockBg, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: t.mdCodeBlockFg, children: truncateBlock(body, 15) }) }) })) : null] }));
|
|
3683
|
+
}
|
|
3684
|
+
function truncateBlock(text, maxLines) {
|
|
3685
|
+
const lines = text.split("\n");
|
|
3686
|
+
if (lines.length <= maxLines)
|
|
3687
|
+
return text;
|
|
3688
|
+
return [...lines.slice(0, maxLines), `… ${lines.length - maxLines} more lines`].join("\n");
|
|
3689
|
+
}
|
|
3690
|
+
function ToolTextOutputView({ t, label, content }) {
|
|
3691
|
+
return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), _jsx("box", { paddingLeft: 5, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: content, t: t }) })] }));
|
|
3692
|
+
}
|
|
3693
|
+
function openMediaFile(filePath) {
|
|
3694
|
+
try {
|
|
3695
|
+
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
3696
|
+
require("child_process").execFile(cmd, [filePath]);
|
|
3697
|
+
}
|
|
3698
|
+
catch { }
|
|
3699
|
+
}
|
|
3700
|
+
function MediaAutoOpenView({ t, label, toolResult }) {
|
|
3701
|
+
const media = toolResult.media ?? [];
|
|
3702
|
+
const openedRef = useRef(new Set());
|
|
3703
|
+
useEffect(() => {
|
|
3704
|
+
for (const asset of media) {
|
|
3705
|
+
if (!openedRef.current.has(asset.path)) {
|
|
3706
|
+
openedRef.current.add(asset.path);
|
|
3707
|
+
openMediaFile(asset.path);
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
}, [media]);
|
|
3711
|
+
return (_jsx("box", { gap: 0, children: _jsx(InlineTool, { t: t, pending: false, children: label }) }));
|
|
3712
|
+
}
|
|
3713
|
+
function MediaToolResultView({ t, label, toolResult }) {
|
|
3714
|
+
const media = toolResult.media ?? [];
|
|
3715
|
+
return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), toolResult.output ? (_jsx("box", { paddingLeft: 5, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: toolResult.output, t: t }) })) : null, media.length > 0 ? (_jsx("box", { paddingLeft: 5, marginTop: toolResult.output ? 1 : 0, flexDirection: "column", children: media.map((asset) => (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: t.text, children: asset.path }), asset.url ? _jsx("text", { fg: t.textMuted, children: `url: ${asset.url}` }) : null, asset.sourcePath ? _jsx("text", { fg: t.textMuted, children: `source: ${asset.sourcePath}` }) : null, asset.sourceUrl ? _jsx("text", { fg: t.textMuted, children: `source_url: ${asset.sourceUrl}` }) : null] }, `${asset.path}-${asset.url ?? ""}-${asset.sourcePath ?? ""}-${asset.sourceUrl ?? ""}`))) })) : null] }));
|
|
3716
|
+
}
|
|
3717
|
+
/* ── Slash Menu ──────────────────────────────────────────────── */
|
|
3718
|
+
function bottomAlignedModalTop(height, panelHeight) {
|
|
3719
|
+
return Math.max(2, Math.floor((height - panelHeight) / 2));
|
|
3720
|
+
}
|
|
3721
|
+
/* ── Update Modal ────────────────────────────────────────────── */
|
|
3722
|
+
function UpdateModal({ t, width, height, currentVersion, latestVersion, }) {
|
|
3723
|
+
const overlayBg = "#000000cc";
|
|
3724
|
+
const panelWidth = Math.min(60, width - 6);
|
|
3725
|
+
const panelHeight = 9;
|
|
3726
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3727
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: "#f59e0b", children: _jsx("b", { children: "Update Available" }) }), _jsx("text", { fg: t.textMuted, children: "esc to dismiss" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("text", { fg: t.text, children: ["A new version of muonroi-cli is available: ", _jsxs("span", { style: { fg: t.textMuted }, children: ["v", currentVersion] }), " → ", _jsxs("span", { style: { fg: "#22c55e" }, children: ["v", latestVersion] })] }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "Press enter to update now, or esc to dismiss" }) })] }) }));
|
|
3728
|
+
}
|
|
3729
|
+
function SlashMenuModal({ t, selectedIndex, width, height, searchQuery, filteredItems, }) {
|
|
3730
|
+
const listRef = useRef(null);
|
|
3731
|
+
useEffect(() => {
|
|
3732
|
+
const item = filteredItems[selectedIndex];
|
|
3733
|
+
if (item)
|
|
3734
|
+
listRef.current?.scrollChildIntoView(`slash-${item.id}`);
|
|
3735
|
+
}, [selectedIndex, filteredItems]);
|
|
3736
|
+
const itemCount = Math.max(filteredItems.length, 1);
|
|
3737
|
+
const contentHeight = itemCount + 5;
|
|
3738
|
+
const maxH = Math.floor(height * 0.6);
|
|
3739
|
+
const panelHeight = Math.min(contentHeight, maxH);
|
|
3740
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3741
|
+
const overlayBg = "#000000cc";
|
|
3742
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(50, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Commands" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search..." }) }) }), _jsxs("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: [filteredItems.map((item, idx) => (_jsx("box", { id: `slash-${item.id}`, backgroundColor: idx === selectedIndex ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, children: _jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsxs("text", { fg: idx === selectedIndex ? t.selected : t.text, children: ["/", item.label] }), _jsx("text", { fg: t.textMuted, children: item.description })] }) }, item.id))), filteredItems.length === 0 && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: "No commands match your search" }) }))] })] }) }));
|
|
3743
|
+
}
|
|
3744
|
+
function ConnectModal({ t, width, height, selectedIndex, channels, }) {
|
|
3745
|
+
const listRef = useRef(null);
|
|
3746
|
+
useEffect(() => {
|
|
3747
|
+
const ch = channels[selectedIndex];
|
|
3748
|
+
if (ch)
|
|
3749
|
+
listRef.current?.scrollChildIntoView(`connect-${ch.id}`);
|
|
3750
|
+
}, [selectedIndex, channels]);
|
|
3751
|
+
const panelHeight = Math.min(channels.length + 9, Math.floor(height * 0.5));
|
|
3752
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3753
|
+
const overlayBg = "#000000cc";
|
|
3754
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(56, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Connect" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.textMuted, children: "Choose a channel" }) }), _jsx("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: channels.map((ch, idx) => (_jsx("box", { id: `connect-${ch.id}`, backgroundColor: idx === selectedIndex ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, children: _jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: idx === selectedIndex ? t.selected : t.text, children: ch.label }), _jsx("text", { fg: t.textMuted, children: ch.description })] }) }, ch.id))) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "select · " }), _jsx("span", { style: { fg: t.primary }, children: "↑↓ " }), _jsx("span", { style: { fg: t.textMuted }, children: "navigate · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] }) })] }) }));
|
|
3755
|
+
}
|
|
3756
|
+
function TelegramTokenModal({ t, width, height, inputRef, error, onSubmit, }) {
|
|
3757
|
+
const overlayBg = "#000000cc";
|
|
3758
|
+
const panelWidth = Math.min(68, width - 6);
|
|
3759
|
+
const panelHeight = 14;
|
|
3760
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3761
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Telegram bot token" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "From @BotFather: /newbot, then paste the token here. Stored in ~/.muonroi-cli/user-settings.json." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "123456:ABC...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save token · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] })) })] }) }));
|
|
3762
|
+
}
|
|
3763
|
+
function TelegramPairModal({ t, width, height, inputRef, error, onSubmit, }) {
|
|
3764
|
+
const overlayBg = "#000000cc";
|
|
3765
|
+
const panelWidth = Math.min(68, width - 6);
|
|
3766
|
+
const panelHeight = 13;
|
|
3767
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3768
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Pairing code" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "DM your bot with /pair, then paste the 6-character code." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "ABC123", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 2, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "approve pairing · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] })) })] }) }));
|
|
3769
|
+
}
|
|
3770
|
+
/* ── Model Picker ────────────────────────────────────────────── */
|
|
3771
|
+
function ModelPickerModal({ t, currentModel, selectedIndex, width, height, searchQuery, filteredModels, reasoningEffortByModel, }) {
|
|
3772
|
+
const listRef = useRef(null);
|
|
3773
|
+
useEffect(() => {
|
|
3774
|
+
const m = filteredModels[selectedIndex];
|
|
3775
|
+
if (m)
|
|
3776
|
+
listRef.current?.scrollChildIntoView(`model-${m.id}`);
|
|
3777
|
+
}, [selectedIndex, filteredModels]);
|
|
3778
|
+
const itemCount = Math.max(filteredModels.length, 1);
|
|
3779
|
+
const selectedModel = filteredModels[selectedIndex];
|
|
3780
|
+
const selectedSupportsReasoning = !!selectedModel && getSupportedReasoningEfforts(selectedModel.id).length > 0;
|
|
3781
|
+
const contentHeight = itemCount + 6;
|
|
3782
|
+
const maxH = Math.floor(height * 0.6);
|
|
3783
|
+
const panelHeight = Math.min(contentHeight, maxH);
|
|
3784
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3785
|
+
const overlayBg = "#000000cc";
|
|
3786
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(60, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Select model" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search..." }) }) }), _jsxs("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: [filteredModels.map((m, idx) => {
|
|
3787
|
+
const selected = idx === selectedIndex;
|
|
3788
|
+
const current = m.id === currentModel;
|
|
3789
|
+
const supportedReasoningEfforts = getSupportedReasoningEfforts(m.id);
|
|
3790
|
+
const reasoningEffort = getEffectiveReasoningEffort(m.id, reasoningEffortByModel[normalizeModelId(m.id)]) ?? "auto";
|
|
3791
|
+
return (_jsx("box", { id: `model-${m.id}`, backgroundColor: selected ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: current ? t.accent : selected ? t.selected : t.text, children: m.name }), supportedReasoningEfforts.length > 0 ? (_jsx("text", { fg: selected ? t.primary : t.textMuted, children: `[${reasoningEffort}]` })) : null] }) }, m.id));
|
|
3792
|
+
}), filteredModels.length === 0 && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: "No models match your search" }) }))] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: selectedSupportsReasoning ? "left/right reasoning enter select esc close" : "enter select esc close" }) })] }) }));
|
|
3793
|
+
}
|
|
3794
|
+
function SandboxPickerModal({ t, currentMode, settings, focusIndex, editing, editBuffer, width, height, }) {
|
|
3795
|
+
const visibleRows = getSandboxVisibleRows(currentMode);
|
|
3796
|
+
const panelHeight = Math.min(visibleRows.length + 6, Math.floor(height * 0.6));
|
|
3797
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3798
|
+
const overlayBg = "#000000cc";
|
|
3799
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(64, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Sandbox settings" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: visibleRows.map((row, idx) => {
|
|
3800
|
+
const focused = idx === focusIndex;
|
|
3801
|
+
const isEditing = editing === row.key;
|
|
3802
|
+
const display = row.getDisplay(currentMode, settings);
|
|
3803
|
+
return (_jsx("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: row.label }), isEditing ? (_jsxs("text", { fg: t.accent, children: [editBuffer || row.placeholder || "", "_"] })) : row.type === "toggle" ? (_jsxs("text", { fg: focused ? t.primary : t.textMuted, children: ["< ", display, " >"] })) : (_jsx("text", { fg: focused ? t.primary : t.textMuted, children: display }))] }) }, row.key));
|
|
3804
|
+
}) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: editing
|
|
3805
|
+
? "type value enter confirm esc cancel"
|
|
3806
|
+
: "arrows navigate left/right toggle enter edit esc close" }) })] }) }));
|
|
3807
|
+
}
|
|
3808
|
+
function PaymentApprovalPanel({ t, payment, }) {
|
|
3809
|
+
const options = ["Approve payment", "Reject"];
|
|
3810
|
+
return (_jsxs("box", { flexDirection: "column", border: ["left"], customBorderChars: {
|
|
3811
|
+
topLeft: "",
|
|
3812
|
+
bottomLeft: "",
|
|
3813
|
+
vertical: "┃",
|
|
3814
|
+
topRight: "",
|
|
3815
|
+
bottomRight: "",
|
|
3816
|
+
horizontal: " ",
|
|
3817
|
+
bottomT: "",
|
|
3818
|
+
topT: "",
|
|
3819
|
+
cross: "",
|
|
3820
|
+
leftT: "",
|
|
3821
|
+
rightT: "",
|
|
3822
|
+
}, borderColor: "#e5c07b", marginTop: 1, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundPanel, children: [_jsx("text", { children: _jsx("span", { style: { fg: t.planTitle ?? t.primary }, children: _jsx("b", { children: "Payment required" }) }) }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { children: _jsx("span", { style: { fg: t.text }, children: payment.url }) }), payment.description ? (_jsx("text", { children: _jsx("span", { style: { fg: t.textMuted }, children: payment.description }) })) : null, payment.security ? (_jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: "Security: " }), _jsx("span", { style: { fg: "#60a5fa" }, children: payment.securityLabel })] })) : null, _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: "Price: " }), _jsx("span", { style: { fg: "#22c55e" }, children: _jsx("b", { children: `${payment.amount} USDC` }) }), _jsx("span", { style: { fg: t.textMuted }, children: ` on ${payment.network}` })] })] }), _jsx("box", { marginTop: 1, flexDirection: "column", children: options.map((label, i) => {
|
|
3823
|
+
const isSel = i === payment.selected;
|
|
3824
|
+
return (_jsxs("text", { children: [_jsx("span", { style: { fg: isSel ? "#22c55e" : t.textMuted }, children: isSel ? "> " : " " }), _jsx("span", { style: { fg: isSel ? t.text : t.textMuted }, children: isSel ? _jsx("b", { children: label }) : label })] }, label));
|
|
3825
|
+
}) }), _jsxs("box", { flexDirection: "row", gap: 3, marginTop: 1, flexShrink: 0, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "↑↓" }), _jsx("span", { style: { fg: t.textMuted }, children: " select" })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "enter" }), _jsx("span", { style: { fg: t.textMuted }, children: " confirm" })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "esc" }), _jsx("span", { style: { fg: t.textMuted }, children: " reject" })] })] })] }));
|
|
3826
|
+
}
|
|
3827
|
+
function WalletPickerModal({ t, settings, walletInfo, focusIndex, width, height, }) {
|
|
3828
|
+
const panelHeight = Math.min(WALLET_ROWS.length + 6, Math.floor(height * 0.6));
|
|
3829
|
+
const top = bottomAlignedModalTop(height, panelHeight);
|
|
3830
|
+
const overlayBg = "#000000cc";
|
|
3831
|
+
return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(64, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Wallet & Payments" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: WALLET_ROWS.map((row, idx) => {
|
|
3832
|
+
const focused = idx === focusIndex;
|
|
3833
|
+
const display = row.getDisplay(settings, walletInfo);
|
|
3834
|
+
return (_jsx("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: row.label }), row.type === "toggle" ? (_jsxs("text", { fg: focused ? t.primary : t.textMuted, children: ["< ", display, " >"] })) : (_jsx("text", { fg: focused ? t.primary : t.textMuted, children: display }))] }) }, row.key));
|
|
3835
|
+
}) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "arrows navigate left/right toggle esc close" }) })] }) }));
|
|
3836
|
+
}
|
|
3837
|
+
/* ── Helpers ──────────────────────────────────────────────────── */
|
|
3838
|
+
function isEscapeKey(key) {
|
|
3839
|
+
return (key.name === "escape" ||
|
|
3840
|
+
key.code === "Escape" ||
|
|
3841
|
+
key.baseCode === 27 ||
|
|
3842
|
+
key.sequence === "\u001b" ||
|
|
3843
|
+
key.raw === "\u001b");
|
|
3844
|
+
}
|
|
3845
|
+
function toolArgs(tc) {
|
|
3846
|
+
if (!tc)
|
|
3847
|
+
return "";
|
|
3848
|
+
try {
|
|
3849
|
+
const a = JSON.parse(tc.function.arguments);
|
|
3850
|
+
if (tc.function.name === "bash")
|
|
3851
|
+
return (a.command || "").replace(/\n/g, " ").trim();
|
|
3852
|
+
if (tc.function.name === "read_file" || tc.function.name === "write_file" || tc.function.name === "edit_file")
|
|
3853
|
+
return a.file_path || a.path || "";
|
|
3854
|
+
if (tc.function.name === "grep") {
|
|
3855
|
+
const path = a.path ? ` in ${a.path}` : "";
|
|
3856
|
+
return `"${a.pattern || ""}"${path}`;
|
|
3857
|
+
}
|
|
3858
|
+
if (tc.function.name === "generate_image" || tc.function.name === "generate_video")
|
|
3859
|
+
return a.prompt || "";
|
|
3860
|
+
if (tc.function.name === "task")
|
|
3861
|
+
return a.description || "";
|
|
3862
|
+
if (tc.function.name === "lsp")
|
|
3863
|
+
return `${a.operation || "query"} ${a.filePath || ""}`.trim();
|
|
3864
|
+
if (tc.function.name === "delegate")
|
|
3865
|
+
return a.description || "";
|
|
3866
|
+
if (tc.function.name === "delegation_read")
|
|
3867
|
+
return a.id || "";
|
|
3868
|
+
if (tc.function.name === "process_logs" || tc.function.name === "process_stop")
|
|
3869
|
+
return a.id != null ? String(a.id) : "";
|
|
3870
|
+
return a.query || "";
|
|
3871
|
+
}
|
|
3872
|
+
catch {
|
|
3873
|
+
return "";
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
function tryParseArg(tc, key) {
|
|
3877
|
+
if (!tc)
|
|
3878
|
+
return "";
|
|
3879
|
+
try {
|
|
3880
|
+
return JSON.parse(tc.function.arguments)[key] || "";
|
|
3881
|
+
}
|
|
3882
|
+
catch {
|
|
3883
|
+
return "";
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
function toolLabel(tc) {
|
|
3887
|
+
const args = toolArgs(tc);
|
|
3888
|
+
if (tc.function.name === "bash") {
|
|
3889
|
+
try {
|
|
3890
|
+
const parsed = JSON.parse(tc.function.arguments);
|
|
3891
|
+
if (parsed.background)
|
|
3892
|
+
return `Background: ${trunc(args || "Starting process...", 70)}`;
|
|
3893
|
+
}
|
|
3894
|
+
catch {
|
|
3895
|
+
/* */
|
|
3896
|
+
}
|
|
3897
|
+
return trunc(args || "Running command...", 80);
|
|
3898
|
+
}
|
|
3899
|
+
if (tc.function.name === "read_file")
|
|
3900
|
+
return `Read ${trunc(args, 60)}`;
|
|
3901
|
+
if (tc.function.name === "write_file")
|
|
3902
|
+
return `Write ${trunc(args, 60)}`;
|
|
3903
|
+
if (tc.function.name === "edit_file")
|
|
3904
|
+
return `Edit ${trunc(args, 60)}`;
|
|
3905
|
+
if (tc.function.name === "grep")
|
|
3906
|
+
return `Grep ${trunc(args, 60)}`;
|
|
3907
|
+
if (tc.function.name === "search_web")
|
|
3908
|
+
return `Web Search "${trunc(args, 60)}"`;
|
|
3909
|
+
if (tc.function.name === "search_x")
|
|
3910
|
+
return `X Search "${trunc(args, 60)}"`;
|
|
3911
|
+
if (tc.function.name === "generate_image")
|
|
3912
|
+
return `Generate image "${trunc(args, 60)}"`;
|
|
3913
|
+
if (tc.function.name === "generate_video")
|
|
3914
|
+
return `Generate video "${trunc(args, 60)}"`;
|
|
3915
|
+
if (tc.function.name === "task")
|
|
3916
|
+
return `Task ${trunc(args, 60)}`;
|
|
3917
|
+
if (tc.function.name === "delegate")
|
|
3918
|
+
return `Background ${trunc(args, 60)}`;
|
|
3919
|
+
if (tc.function.name === "delegation_read")
|
|
3920
|
+
return `Read delegation ${trunc(args, 60)}`;
|
|
3921
|
+
if (tc.function.name === "delegation_list")
|
|
3922
|
+
return "List delegations";
|
|
3923
|
+
if (tc.function.name === "process_logs")
|
|
3924
|
+
return `Logs for process ${args}`;
|
|
3925
|
+
if (tc.function.name === "process_stop")
|
|
3926
|
+
return `Stop process ${args}`;
|
|
3927
|
+
if (tc.function.name === "process_list")
|
|
3928
|
+
return "List processes";
|
|
3929
|
+
if (tc.function.name === "generate_plan")
|
|
3930
|
+
return "Generating plan...";
|
|
3931
|
+
return trunc(`${tc.function.name} ${args}`, 80);
|
|
3932
|
+
}
|
|
3933
|
+
function sanitizeContent(raw) {
|
|
3934
|
+
let s = raw.replace(/^[\s\n]*assistant:\s*/gi, "");
|
|
3935
|
+
s = s.replace(/\{"success"\s*:\s*(true|false)\s*,\s*"output"\s*:\s*"[\s\S]*$/m, "");
|
|
3936
|
+
return s.trim();
|
|
3937
|
+
}
|
|
3938
|
+
function shouldOpenApiKeyModalForKey(key) {
|
|
3939
|
+
if (key.ctrl || key.meta)
|
|
3940
|
+
return false;
|
|
3941
|
+
if (key.name === "return" || key.name === "backspace")
|
|
3942
|
+
return true;
|
|
3943
|
+
return !!(key.sequence && key.sequence.length === 1);
|
|
3944
|
+
}
|
|
3945
|
+
function compactTaskLabel(label) {
|
|
3946
|
+
const words = label.trim().split(/\s+/).filter(Boolean);
|
|
3947
|
+
if (words.length <= 3)
|
|
3948
|
+
return label.trim() || "Working";
|
|
3949
|
+
return `${words.slice(0, 3).join(" ")}...`;
|
|
3950
|
+
}
|
|
3951
|
+
function trunc(s, n) {
|
|
3952
|
+
return s.length <= n ? s : `${s.slice(0, n)}…`;
|
|
3953
|
+
}
|
|
3954
|
+
function truncateLine(s, n) {
|
|
3955
|
+
return trunc(s.replace(/\s+/g, " ").trim(), n);
|
|
3956
|
+
}
|
|
3957
|
+
//# sourceMappingURL=app.js.map
|