muonroi-cli 1.8.4 → 1.8.5
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 +17 -5
- package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
- package/dist/packages/agent-harness-core/src/driver.js +46 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
- package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
- package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
- package/dist/packages/agent-harness-core/src/protocol.js +15 -0
- package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
- package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
- package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
- package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
- package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
- package/dist/packages/agent-harness-opentui/src/install.js +10 -0
- package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
- package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
- package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
- package/dist/src/agent-harness/mock-model.d.ts +28 -0
- package/dist/src/agent-harness/mock-model.js +63 -1
- package/dist/src/agent-harness/test-spawn.js +31 -0
- package/dist/src/cli/config/screen-providers.js +1 -1
- package/dist/src/cli/cost-forensics.d.ts +10 -0
- package/dist/src/cli/cost-forensics.js +18 -3
- package/dist/src/cli/keys-bundle.d.ts +1 -1
- package/dist/src/cli/keys-bundle.js +1 -1
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +19 -81
- package/dist/src/council/clarifier.d.ts +28 -2
- package/dist/src/council/clarifier.js +81 -15
- package/dist/src/council/context.js +49 -15
- package/dist/src/council/debate-checkpoint.d.ts +129 -0
- package/dist/src/council/debate-checkpoint.js +176 -0
- package/dist/src/council/debate-planner.js +51 -3
- package/dist/src/council/debate-summary.d.ts +25 -0
- package/dist/src/council/debate-summary.js +85 -0
- package/dist/src/council/debate.d.ts +169 -2
- package/dist/src/council/debate.js +1210 -134
- package/dist/src/council/index.d.ts +85 -1
- package/dist/src/council/index.js +634 -196
- package/dist/src/council/leader.d.ts +26 -0
- package/dist/src/council/leader.js +150 -9
- package/dist/src/council/llm.d.ts +32 -0
- package/dist/src/council/llm.js +231 -38
- package/dist/src/council/panel-select.d.ts +30 -0
- package/dist/src/council/panel-select.js +72 -0
- package/dist/src/council/planner.js +23 -0
- package/dist/src/council/preflight.d.ts +7 -0
- package/dist/src/council/preflight.js +14 -2
- package/dist/src/council/prompts.d.ts +30 -3
- package/dist/src/council/prompts.js +234 -64
- package/dist/src/council/stance-recall.d.ts +42 -0
- package/dist/src/council/stance-recall.js +57 -0
- package/dist/src/council/strip-think.d.ts +17 -0
- package/dist/src/council/strip-think.js +33 -0
- package/dist/src/council/types.d.ts +128 -0
- package/dist/src/ee/artifact-cache.d.ts +16 -0
- package/dist/src/ee/artifact-cache.js +32 -0
- package/dist/src/ee/auth.d.ts +1 -0
- package/dist/src/ee/auth.js +15 -2
- package/dist/src/ee/bridge.d.ts +10 -0
- package/dist/src/ee/bridge.js +58 -0
- package/dist/src/ee/client.js +81 -18
- package/dist/src/ee/export-transcripts.d.ts +1 -0
- package/dist/src/ee/export-transcripts.js +8 -10
- package/dist/src/ee/extract-session.js +29 -0
- package/dist/src/ee/extract-style.d.ts +58 -0
- package/dist/src/ee/extract-style.js +270 -0
- package/dist/src/ee/recall-ledger.d.ts +9 -0
- package/dist/src/ee/recall-ledger.js +3 -0
- package/dist/src/ee/scope.d.ts +1 -0
- package/dist/src/ee/scope.js +26 -1
- package/dist/src/ee/search.d.ts +7 -0
- package/dist/src/ee/search.js +24 -0
- package/dist/src/ee/transcript-emit.js +2 -0
- package/dist/src/ee/types.d.ts +22 -0
- package/dist/src/ee/who-am-i-brain.d.ts +35 -0
- package/dist/src/ee/who-am-i-brain.js +220 -0
- package/dist/src/ee/who-am-i.d.ts +10 -3
- package/dist/src/ee/who-am-i.js +12 -0
- package/dist/src/ee/workflow-event.d.ts +48 -0
- package/dist/src/ee/workflow-event.js +81 -0
- package/dist/src/flow/compaction/compress.d.ts +3 -3
- package/dist/src/flow/compaction/compress.js +45 -8
- package/dist/src/flow/compaction/extract.d.ts +4 -7
- package/dist/src/flow/compaction/extract.js +50 -10
- package/dist/src/flow/compaction/index.d.ts +13 -1
- package/dist/src/flow/compaction/index.js +70 -3
- package/dist/src/flow/compaction/input-guard.d.ts +24 -0
- package/dist/src/flow/compaction/input-guard.js +43 -0
- package/dist/src/flow/fold-planning.d.ts +36 -0
- package/dist/src/flow/fold-planning.js +83 -0
- package/dist/src/flow/hierarchy.d.ts +146 -0
- package/dist/src/flow/hierarchy.js +427 -0
- package/dist/src/flow/index.d.ts +1 -0
- package/dist/src/flow/index.js +2 -0
- package/dist/src/flow/run-artifacts.d.ts +102 -0
- package/dist/src/flow/run-artifacts.js +208 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/assessment-schema.d.ts +44 -0
- package/dist/src/gsd/assessment-schema.js +134 -0
- package/dist/src/gsd/capability-registry.d.ts +45 -0
- package/dist/src/gsd/capability-registry.js +337 -0
- package/dist/src/gsd/complexity-assessor.d.ts +39 -0
- package/dist/src/gsd/complexity-assessor.js +152 -0
- package/dist/src/gsd/config-bridge.d.ts +7 -0
- package/dist/src/gsd/config-bridge.js +114 -0
- package/dist/src/gsd/config-loader.d.ts +27 -0
- package/dist/src/gsd/config-loader.js +50 -0
- package/dist/src/gsd/council-context.d.ts +44 -0
- package/dist/src/gsd/council-context.js +114 -0
- package/dist/src/gsd/ee-closure.d.ts +28 -0
- package/dist/src/gsd/ee-closure.js +49 -0
- package/dist/src/gsd/flags.d.ts +55 -0
- package/dist/src/gsd/flags.js +83 -0
- package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
- package/dist/src/gsd/gsd-dispatch.js +131 -0
- package/dist/src/gsd/gsd-runtime.d.ts +22 -0
- package/dist/src/gsd/gsd-runtime.js +37 -0
- package/dist/src/gsd/host-adapter.d.ts +11 -0
- package/dist/src/gsd/host-adapter.js +29 -0
- package/dist/src/gsd/index.d.ts +24 -1
- package/dist/src/gsd/index.js +27 -0
- package/dist/src/gsd/loop-host-contract.d.ts +21 -0
- package/dist/src/gsd/loop-host-contract.js +39 -0
- package/dist/src/gsd/loop-host.d.ts +69 -0
- package/dist/src/gsd/loop-host.js +245 -0
- package/dist/src/gsd/loop-resolver.d.ts +36 -0
- package/dist/src/gsd/loop-resolver.js +79 -0
- package/dist/src/gsd/model-tier.d.ts +13 -0
- package/dist/src/gsd/model-tier.js +45 -0
- package/dist/src/gsd/mutation-gate.d.ts +16 -0
- package/dist/src/gsd/mutation-gate.js +41 -0
- package/dist/src/gsd/native-roadmap.d.ts +89 -0
- package/dist/src/gsd/native-roadmap.js +343 -0
- package/dist/src/gsd/native-state.d.ts +47 -0
- package/dist/src/gsd/native-state.js +220 -0
- package/dist/src/gsd/paths.d.ts +23 -0
- package/dist/src/gsd/paths.js +66 -0
- package/dist/src/gsd/phase-dag.d.ts +12 -0
- package/dist/src/gsd/phase-dag.js +94 -0
- package/dist/src/gsd/phase-sync.d.ts +42 -0
- package/dist/src/gsd/phase-sync.js +321 -0
- package/dist/src/gsd/pil-gate-context.d.ts +13 -0
- package/dist/src/gsd/pil-gate-context.js +64 -0
- package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
- package/dist/src/gsd/pil-gate-critic.js +74 -0
- package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
- package/dist/src/gsd/plan-council-prompts.js +79 -0
- package/dist/src/gsd/plan-council.d.ts +44 -0
- package/dist/src/gsd/plan-council.js +251 -0
- package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
- package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
- package/dist/src/gsd/product-workspace.d.ts +13 -0
- package/dist/src/gsd/product-workspace.js +124 -0
- package/dist/src/gsd/ship-bridge.d.ts +25 -0
- package/dist/src/gsd/ship-bridge.js +65 -0
- package/dist/src/gsd/state-document.d.ts +40 -0
- package/dist/src/gsd/state-document.js +163 -0
- package/dist/src/gsd/verdict-schema.d.ts +39 -0
- package/dist/src/gsd/verdict-schema.js +144 -0
- package/dist/src/gsd/verify-context.d.ts +22 -0
- package/dist/src/gsd/verify-context.js +27 -0
- package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
- package/dist/src/gsd/verify-council-prompts.js +85 -0
- package/dist/src/gsd/verify-council.d.ts +25 -0
- package/dist/src/gsd/verify-council.js +119 -0
- package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
- package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
- package/dist/src/gsd/workflow-engine.d.ts +60 -0
- package/dist/src/gsd/workflow-engine.js +207 -0
- package/dist/src/gsd/workflow-tools.d.ts +13 -0
- package/dist/src/gsd/workflow-tools.js +277 -0
- package/dist/src/hooks/index.js +1 -1
- package/dist/src/index.js +44 -11
- package/dist/src/maintain/pr-builder.js +23 -13
- package/dist/src/mcp/auto-setup.js +57 -32
- package/dist/src/mcp/client-pool.js +1 -1
- package/dist/src/mcp/ee-tools.js +1 -0
- package/dist/src/mcp/research-onboarding.js +8 -7
- package/dist/src/mcp/runtime.js +34 -2
- package/dist/src/models/catalog-client.d.ts +87 -0
- package/dist/src/models/catalog-client.js +105 -38
- package/dist/src/models/catalog.json +528 -265
- package/dist/src/models/registry.d.ts +22 -7
- package/dist/src/models/registry.js +73 -10
- package/dist/src/ops/doctor.js +1 -1
- package/dist/src/orchestrator/auto-commit.js +1 -1
- package/dist/src/orchestrator/batch-turn-runner.js +2 -2
- package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
- package/dist/src/orchestrator/cache-prefix.js +83 -0
- package/dist/src/orchestrator/compact-request.d.ts +32 -0
- package/dist/src/orchestrator/compact-request.js +41 -0
- package/dist/src/orchestrator/compaction.d.ts +10 -0
- package/dist/src/orchestrator/compaction.js +27 -7
- package/dist/src/orchestrator/council-manager.d.ts +12 -3
- package/dist/src/orchestrator/council-manager.js +65 -24
- package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
- package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
- package/dist/src/orchestrator/error-utils.d.ts +29 -0
- package/dist/src/orchestrator/error-utils.js +132 -24
- package/dist/src/orchestrator/grounding-check.js +39 -1
- package/dist/src/orchestrator/message-processor.js +242 -33
- package/dist/src/orchestrator/orchestrator.d.ts +39 -3
- package/dist/src/orchestrator/orchestrator.js +651 -102
- package/dist/src/orchestrator/preprocessor.js +1 -1
- package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
- package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
- package/dist/src/orchestrator/prompts.js +17 -17
- package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
- package/dist/src/orchestrator/reactive-delegation.js +59 -0
- package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
- package/dist/src/orchestrator/retry-classifier.js +46 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
- package/dist/src/orchestrator/safety-intercept.js +55 -0
- package/dist/src/orchestrator/scope-reminder.js +1 -1
- package/dist/src/orchestrator/session-experience.d.ts +2 -1
- package/dist/src/orchestrator/session-experience.js +2 -1
- package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
- package/dist/src/orchestrator/should-run-gate.js +18 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
- package/dist/src/orchestrator/stall-watchdog.js +47 -13
- package/dist/src/orchestrator/stream-runner.js +62 -29
- package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-cap.js +16 -1
- package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
- package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
- package/dist/src/orchestrator/subagent-compactor.js +126 -15
- package/dist/src/orchestrator/tool-engine.d.ts +22 -0
- package/dist/src/orchestrator/tool-engine.js +620 -56
- package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
- package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
- package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
- package/dist/src/orchestrator/turn-watchdog.js +55 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +1 -1
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +5 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/discovery-types.d.ts +1 -0
- package/dist/src/pil/discovery.js +16 -11
- package/dist/src/pil/layer1-intent.d.ts +18 -6
- package/dist/src/pil/layer1-intent.js +66 -757
- package/dist/src/pil/layer15-context-scan.js +15 -1
- package/dist/src/pil/layer3-ee-injection.js +23 -8
- package/dist/src/pil/layer4-gsd.js +69 -16
- package/dist/src/pil/layer5-context.js +7 -3
- package/dist/src/pil/layer6-output.d.ts +23 -0
- package/dist/src/pil/layer6-output.js +5 -1
- package/dist/src/pil/llm-classify.d.ts +33 -2
- package/dist/src/pil/llm-classify.js +123 -131
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +1 -0
- package/dist/src/pil/pipeline.js +34 -2
- package/dist/src/pil/response-tools.js +5 -3
- package/dist/src/pil/schema.d.ts +1 -0
- package/dist/src/pil/schema.js +2 -0
- package/dist/src/pil/types.d.ts +18 -0
- package/dist/src/playbook/directives.d.ts +4 -0
- package/dist/src/playbook/directives.js +17 -5
- package/dist/src/product-loop/backlog-builder.d.ts +14 -1
- package/dist/src/product-loop/backlog-builder.js +30 -6
- package/dist/src/product-loop/discovery-context-format.js +3 -1
- package/dist/src/product-loop/discovery-ecosystem.js +4 -1
- package/dist/src/product-loop/discovery-interview.js +32 -3
- package/dist/src/product-loop/discovery-schema.js +5 -1
- package/dist/src/product-loop/ideal-trace.d.ts +7 -0
- package/dist/src/product-loop/ideal-trace.js +64 -0
- package/dist/src/product-loop/index.d.ts +13 -1
- package/dist/src/product-loop/index.js +333 -52
- package/dist/src/product-loop/loop-driver.d.ts +7 -0
- package/dist/src/product-loop/loop-driver.js +310 -99
- package/dist/src/product-loop/phase-plan.d.ts +5 -0
- package/dist/src/product-loop/phase-plan.js +39 -2
- package/dist/src/product-loop/phase-runner.js +9 -1
- package/dist/src/product-loop/sprint-runner.d.ts +111 -0
- package/dist/src/product-loop/sprint-runner.js +559 -16
- package/dist/src/product-loop/types.d.ts +36 -5
- package/dist/src/providers/adapter.d.ts +1 -1
- package/dist/src/providers/adapter.js +3 -4
- package/dist/src/providers/auth/browser-flow.d.ts +1 -1
- package/dist/src/providers/auth/browser-flow.js +1 -1
- package/dist/src/providers/auth/openai-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +0 -34
- package/dist/src/providers/auth/token-store.js +4 -1
- package/dist/src/providers/auth/types.d.ts +1 -1
- package/dist/src/providers/auth/types.js +1 -1
- package/dist/src/providers/capabilities.d.ts +24 -5
- package/dist/src/providers/capabilities.js +42 -24
- package/dist/src/providers/endpoints.d.ts +2 -2
- package/dist/src/providers/endpoints.js +11 -10
- package/dist/src/providers/keychain.d.ts +1 -1
- package/dist/src/providers/keychain.js +7 -9
- package/dist/src/providers/mcp-vision-bridge.js +56 -146
- package/dist/src/providers/openai-compatible.js +8 -1
- package/dist/src/providers/pricing.d.ts +2 -2
- package/dist/src/providers/pricing.js +3 -13
- package/dist/src/providers/runtime.d.ts +27 -2
- package/dist/src/providers/runtime.js +78 -15
- package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
- package/dist/src/providers/strategies/base.strategy.js +24 -1
- package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
- package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
- package/dist/src/providers/strategies/registry.js +4 -4
- package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
- package/dist/src/providers/strategies/thinking-mode.js +280 -1
- package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
- package/dist/src/providers/strategies/zai.strategy.js +44 -0
- package/dist/src/providers/types.d.ts +5 -6
- package/dist/src/providers/types.js +2 -2
- package/dist/src/providers/vision-backend.d.ts +47 -0
- package/dist/src/providers/vision-backend.js +258 -0
- package/dist/src/providers/vision-proxy.d.ts +22 -9
- package/dist/src/providers/vision-proxy.js +63 -132
- package/dist/src/providers/wire-debug.js +95 -0
- package/dist/src/router/decide.d.ts +13 -0
- package/dist/src/router/decide.js +138 -36
- package/dist/src/router/peak-hour.d.ts +38 -0
- package/dist/src/router/peak-hour.js +107 -0
- package/dist/src/router/step-router.js +3 -2
- package/dist/src/router/warm.js +4 -5
- package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
- package/dist/src/scaffold/continuation-prompt.js +26 -0
- package/dist/src/scaffold/point-to-existing.d.ts +21 -0
- package/dist/src/scaffold/point-to-existing.js +25 -0
- package/dist/src/self-qa/agentic-loop.js +3 -3
- package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
- package/dist/src/{ui/state → state}/active-run.js +21 -0
- package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
- package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
- package/dist/src/state/turn-trace.d.ts +43 -0
- package/dist/src/state/turn-trace.js +32 -0
- package/dist/src/storage/db.js +2 -1
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.d.ts +1 -1
- package/dist/src/storage/migrations.js +71 -1
- package/dist/src/storage/sessions.d.ts +28 -10
- package/dist/src/storage/sessions.js +78 -21
- package/dist/src/storage/transcript-view.js +1 -1
- package/dist/src/storage/transcript.d.ts +51 -0
- package/dist/src/storage/transcript.js +284 -13
- package/dist/src/tools/file.d.ts +15 -0
- package/dist/src/tools/file.js +32 -0
- package/dist/src/tools/native-tools.js +5 -0
- package/dist/src/tools/registry.d.ts +3 -0
- package/dist/src/tools/registry.js +460 -22
- package/dist/src/tools/research.d.ts +29 -0
- package/dist/src/tools/research.js +233 -0
- package/dist/src/types/index.d.ts +118 -3
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/cards/product-status-card.js +1 -1
- package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
- package/dist/src/ui/components/bubble-body-guard.js +50 -0
- package/dist/src/ui/components/context-rail.d.ts +26 -0
- package/dist/src/ui/components/context-rail.js +33 -0
- package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
- package/dist/src/ui/components/council-conclusion-card.js +420 -0
- package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
- package/dist/src/ui/components/council-debate-pill.js +34 -0
- package/dist/src/ui/components/council-info-card.js +2 -2
- package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
- package/dist/src/ui/components/council-leader-bubble.js +21 -11
- package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
- package/dist/src/ui/components/council-message-bubble.js +16 -15
- package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
- package/dist/src/ui/components/council-phase-timeline.js +49 -15
- package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
- package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
- package/dist/src/ui/components/council-question-card.js +12 -12
- package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
- package/dist/src/ui/components/council-rail-rounds.js +57 -0
- package/dist/src/ui/components/council-round-group.d.ts +38 -0
- package/dist/src/ui/components/council-round-group.js +88 -0
- package/dist/src/ui/components/council-status-list.d.ts +3 -1
- package/dist/src/ui/components/council-status-list.js +36 -24
- package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
- package/dist/src/ui/components/council-synthesis-banner.js +20 -5
- package/dist/src/ui/components/halt-recovery-card.js +9 -5
- package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
- package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
- package/dist/src/ui/components/prompt-box.js +18 -16
- package/dist/src/ui/components/session-tree-card.d.ts +14 -0
- package/dist/src/ui/components/session-tree-card.js +46 -0
- package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
- package/dist/src/ui/components/slash-inline-menu.js +26 -5
- package/dist/src/ui/components/task-list-panel.d.ts +14 -1
- package/dist/src/ui/components/task-list-panel.js +22 -2
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
- package/dist/src/ui/mcp-modal.js +2 -4
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/connect-modal.js +4 -3
- package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
- package/dist/src/ui/modals/session-picker-modal.js +3 -5
- package/dist/src/ui/picker-providers.d.ts +1 -1
- package/dist/src/ui/picker-providers.js +1 -1
- package/dist/src/ui/primitives/index.d.ts +1 -0
- package/dist/src/ui/primitives/index.js +2 -0
- package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
- package/dist/src/ui/primitives/semantic-primitives.js +81 -0
- package/dist/src/ui/slash/compact.js +5 -7
- package/dist/src/ui/slash/cost.js +1 -1
- package/dist/src/ui/slash/council.js +19 -1
- package/dist/src/ui/slash/debug.d.ts +3 -31
- package/dist/src/ui/slash/debug.js +9 -20
- package/dist/src/ui/slash/ideal.d.ts +6 -2
- package/dist/src/ui/slash/ideal.js +97 -7
- package/dist/src/ui/slash/menu-items.d.ts +7 -0
- package/dist/src/ui/slash/menu-items.js +12 -18
- package/dist/src/ui/slash/registry.d.ts +2 -0
- package/dist/src/ui/slash/registry.js +4 -0
- package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
- package/dist/src/ui/status-bar/cache-hit.js +9 -0
- package/dist/src/ui/status-bar/index.d.ts +1 -1
- package/dist/src/ui/status-bar/index.js +7 -3
- package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
- package/dist/src/ui/status-bar/usd-meter.js +6 -4
- package/dist/src/ui/theme.d.ts +1 -0
- package/dist/src/ui/theme.js +2 -0
- package/dist/src/ui/types.d.ts +7 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/format.d.ts +14 -0
- package/dist/src/ui/utils/format.js +23 -3
- package/dist/src/usage/downgrade.js +2 -2
- package/dist/src/usage/product-ledger.js +2 -2
- package/dist/src/utils/install-manager.js +2 -1
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/permission-mode.js +5 -3
- package/dist/src/utils/redactor.js +1 -1
- package/dist/src/utils/settings.d.ts +153 -5
- package/dist/src/utils/settings.js +233 -29
- package/dist/src/utils/visible-retry.d.ts +11 -0
- package/dist/src/utils/visible-retry.js +10 -1
- package/dist/src/verify/entrypoint.d.ts +1 -1
- package/dist/src/verify/entrypoint.js +1 -1
- package/dist/src/verify/recipes.d.ts +13 -0
- package/dist/src/verify/recipes.js +15 -0
- package/package.json +135 -132
- package/dist/src/providers/auth/gcloud.d.ts +0 -28
- package/dist/src/providers/auth/gcloud.js +0 -102
- package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
- package/dist/src/providers/auth/gemini-oauth.js +0 -472
- package/dist/src/providers/gemini.d.ts +0 -11
- package/dist/src/providers/gemini.js +0 -45
- package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
- package/dist/src/providers/siliconflow-sse-repair.js +0 -177
- package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
- package/dist/src/providers/strategies/google.strategy.js +0 -174
- package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
- package/dist/src/ui/containers/chat-feed.d.ts +0 -40
- package/dist/src/ui/containers/chat-feed.js +0 -66
package/dist/src/council/llm.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import { generateText, stepCountIs } from "ai";
|
|
2
|
+
import { generateText, stepCountIs, streamText } from "ai";
|
|
3
3
|
import { getDefaultEEClient } from "../ee/intercept.js";
|
|
4
4
|
import { emitMatches } from "../ee/render.js";
|
|
5
|
+
import { getMcpKey } from "../mcp/mcp-keychain.js";
|
|
5
6
|
import { buildMcpToolSet } from "../mcp/runtime.js";
|
|
6
|
-
import {
|
|
7
|
+
import { getModelInfo } from "../models/registry.js";
|
|
8
|
+
import { getProviderCapabilities, resolveTemperature } from "../providers/capabilities.js";
|
|
7
9
|
import { loadKeyForProvider, ProviderKeyMissingError } from "../providers/keychain.js";
|
|
8
10
|
import { createProviderFactoryAsync, detectProviderForModel, resolveModelRuntime } from "../providers/runtime.js";
|
|
11
|
+
import { statusBarStore } from "../state/status-bar-store.js";
|
|
12
|
+
import { recordUsageEvent } from "../storage/index.js";
|
|
9
13
|
import { createBuiltinTools as createTools } from "../tools/registry.js";
|
|
10
14
|
import { appendCostLog } from "../usage/cost-log.js";
|
|
11
15
|
import { projectCostUSD } from "../usage/estimator.js";
|
|
12
16
|
import { withDeadlineRace, withTimeoutSignal } from "../utils/llm-deadline.js";
|
|
17
|
+
import { logger } from "../utils/logger.js";
|
|
13
18
|
import { loadMcpServers } from "../utils/settings.js";
|
|
14
19
|
import { withVisibleRetry } from "../utils/visible-retry.js";
|
|
15
20
|
import { buildResearchSystemPrompt } from "./prompts.js";
|
|
21
|
+
import { stripThinkBlocks } from "./strip-think.js";
|
|
16
22
|
/**
|
|
17
23
|
* Resolve a provider factory for a council sub-call, OAuth-aware.
|
|
18
24
|
*
|
|
@@ -186,6 +192,62 @@ function logCouncilCost(args) {
|
|
|
186
192
|
}).catch(() => undefined);
|
|
187
193
|
return usage;
|
|
188
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Record a council LLM call into the `usage_events` table with source="council".
|
|
197
|
+
*
|
|
198
|
+
* This is the SINGLE source of truth for council usage accounting: it fires from
|
|
199
|
+
* inside every council generate/debate/research call so ALL council entry points
|
|
200
|
+
* (the /council slash path, auto-council, and every /ideal phase — clarifier,
|
|
201
|
+
* research, generate, sprint) land in usage_events. Before this existed, only
|
|
202
|
+
* the loop-driver's `runDebate` call site externally wrapped usage recording, so
|
|
203
|
+
* `runCouncilV2` (/council + auto-council) and the non-debate /ideal phases never
|
|
204
|
+
* recorded — their token cost was invisible to session totals, the StatusBar,
|
|
205
|
+
* `usage forensics`, and the cost caps (root cause of session f24c28b6dcb3:
|
|
206
|
+
* council ran but produced 0 source="council" usage_events).
|
|
207
|
+
*
|
|
208
|
+
* Best-effort: a null sessionId (no chat session FK) or a DB failure must never
|
|
209
|
+
* break the council run, but per the No-Silent-Catch rule we log the failure.
|
|
210
|
+
*/
|
|
211
|
+
export function recordCouncilUsage(sessionId, modelId, usage) {
|
|
212
|
+
if (!sessionId)
|
|
213
|
+
return;
|
|
214
|
+
try {
|
|
215
|
+
recordUsageEvent(sessionId, "council", modelId, {
|
|
216
|
+
inputTokens: usage.inputTokens,
|
|
217
|
+
outputTokens: usage.outputTokens,
|
|
218
|
+
cacheReadTokens: usage.cachedInputTokens,
|
|
219
|
+
});
|
|
220
|
+
// Mirror council billing into the live StatusBar so cache-hit% / tokens /
|
|
221
|
+
// cost reflect the WHOLE session (council calls have ~0 cache; excluding
|
|
222
|
+
// them overstated the message-only cache ratio — session f24c28b6dcb3).
|
|
223
|
+
// ONLY the five cumulative billing counters — NEVER ctx_tokens/ctx_pct/
|
|
224
|
+
// model/provider, which describe the current MAIN-conversation call and
|
|
225
|
+
// would be clobbered by a small council sub-call.
|
|
226
|
+
const totalInput = usage.inputTokens;
|
|
227
|
+
const output = usage.outputTokens;
|
|
228
|
+
const cacheRead = usage.cachedInputTokens;
|
|
229
|
+
const info = getModelInfo(modelId);
|
|
230
|
+
const priceIn = info?.inputPrice ?? 0;
|
|
231
|
+
const priceCached = info?.cachedInputPrice ?? priceIn * 0.1;
|
|
232
|
+
const priceOut = info?.outputPrice ?? 0;
|
|
233
|
+
const nonCachedInput = Math.max(0, totalInput - cacheRead);
|
|
234
|
+
const turnCostMicros = nonCachedInput * priceIn + cacheRead * priceCached + output * priceOut;
|
|
235
|
+
const prev = statusBarStore.getState();
|
|
236
|
+
statusBarStore.setState({
|
|
237
|
+
in_tokens: prev.in_tokens + totalInput,
|
|
238
|
+
out_tokens: prev.out_tokens + output,
|
|
239
|
+
cache_read_tokens: (prev.cache_read_tokens ?? 0) + cacheRead,
|
|
240
|
+
session_usd: prev.session_usd + turnCostMicros / 1_000_000,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
logger.error("storage", "recordUsageEvent(council) failed — token cost not accounted", {
|
|
245
|
+
sessionId,
|
|
246
|
+
modelId,
|
|
247
|
+
message: err instanceof Error ? err.message : String(err),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
189
251
|
function getMockLlm() {
|
|
190
252
|
// biome-ignore lint/suspicious/noExplicitAny: intentionally untyped global access
|
|
191
253
|
return globalThis.__muonroiMockLlm ?? null;
|
|
@@ -209,6 +271,87 @@ const COUNCIL_LLM_TIMEOUT_MS = (() => {
|
|
|
209
271
|
// withTimeoutSignal + withDeadlineRace moved to ../utils/llm-deadline.js so all
|
|
210
272
|
// pre-flight LLM call sites (council, debate-planner, scope-ceiling) share one
|
|
211
273
|
// implementation. Imported at the top of this file.
|
|
274
|
+
/**
|
|
275
|
+
* Run a single-shot LLM call over the STREAMING transport and collect the full
|
|
276
|
+
* result, returning a `generateText`-shaped object ({ text, usage, finishReason,
|
|
277
|
+
* reasoningText }).
|
|
278
|
+
*
|
|
279
|
+
* Why stream a one-shot generate? The OpenAI codex/oauth endpoint
|
|
280
|
+
* (chatgpt.com/backend-api/codex/responses, used by gpt-5.*-codex subscription
|
|
281
|
+
* auth) HARD-REJECTS non-streaming requests with 400 `{"detail":"Stream must be
|
|
282
|
+
* set to true"}`. `generateText` issues a non-stream POST, so every council
|
|
283
|
+
* sub-task that ran through it (leader round evaluation, clarify, spec
|
|
284
|
+
* synthesis, running summary) failed on a codex session — surfacing to the user
|
|
285
|
+
* as the opaque "Decision: evaluation unavailable" round card (diagnosed from
|
|
286
|
+
* session 8191ecaee149: gpt-5.4-mini → codex/responses → "Stream must be set to
|
|
287
|
+
* true"). The panel debate never hit this because it streams. Streaming is the
|
|
288
|
+
* universal transport (every provider + the whole TUI already use it), so
|
|
289
|
+
* collecting a streamed result fixes codex without special-casing it.
|
|
290
|
+
*
|
|
291
|
+
* A provider `error` part is re-thrown so the caller's retry / cross-provider
|
|
292
|
+
* fallback treats it as a failure exactly as a thrown `generateText` did.
|
|
293
|
+
*
|
|
294
|
+
* Also used by `debate()` (the panel pair turns) with the tiny verification
|
|
295
|
+
* toolset: pass `tools`/`stopWhen`/`prepareStep` and the collected `toolCalls`
|
|
296
|
+
* (toolName + input + matched result) come back in the same shape the old
|
|
297
|
+
* `generateText` result exposed. A debater on the codex/oauth endpoint hit the
|
|
298
|
+
* exact same non-stream 400 as the eval path — streaming fixes it uniformly.
|
|
299
|
+
*/
|
|
300
|
+
async function collectStreamText(args) {
|
|
301
|
+
const hasTools = !!args.tools && Object.keys(args.tools).length > 0;
|
|
302
|
+
const result = streamText({
|
|
303
|
+
model: args.model,
|
|
304
|
+
system: args.system,
|
|
305
|
+
prompt: args.prompt,
|
|
306
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
307
|
+
maxRetries: 0,
|
|
308
|
+
...(args.temperature === undefined ? {} : { temperature: args.temperature }),
|
|
309
|
+
...(args.providerOptions ? { providerOptions: args.providerOptions } : {}),
|
|
310
|
+
...(hasTools ? { tools: args.tools, stopWhen: args.stopWhen, prepareStep: args.prepareStep } : {}),
|
|
311
|
+
abortSignal: args.abortSignal,
|
|
312
|
+
});
|
|
313
|
+
let text = "";
|
|
314
|
+
let reasoningText = "";
|
|
315
|
+
let usage;
|
|
316
|
+
let finishReason;
|
|
317
|
+
const toolCalls = [];
|
|
318
|
+
const byId = new Map();
|
|
319
|
+
for await (const part of result.fullStream) {
|
|
320
|
+
switch (part.type) {
|
|
321
|
+
case "text-delta":
|
|
322
|
+
text += part.text ?? "";
|
|
323
|
+
break;
|
|
324
|
+
case "reasoning-delta":
|
|
325
|
+
reasoningText += part.text ?? "";
|
|
326
|
+
break;
|
|
327
|
+
case "tool-call": {
|
|
328
|
+
const p = part;
|
|
329
|
+
const tc = { toolName: p.toolName, input: p.input };
|
|
330
|
+
byId.set(p.toolCallId, tc);
|
|
331
|
+
toolCalls.push(tc);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
case "tool-result": {
|
|
335
|
+
const p = part;
|
|
336
|
+
const tc = byId.get(p.toolCallId);
|
|
337
|
+
if (tc)
|
|
338
|
+
tc.result = p.output;
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "finish":
|
|
342
|
+
usage = part.totalUsage ?? part.usage;
|
|
343
|
+
finishReason = part.finishReason;
|
|
344
|
+
break;
|
|
345
|
+
case "error": {
|
|
346
|
+
const raw = part.error;
|
|
347
|
+
throw raw instanceof Error ? raw : new Error(String(raw));
|
|
348
|
+
}
|
|
349
|
+
default:
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return { text, usage, finishReason, reasoningText: reasoningText || undefined, toolCalls };
|
|
354
|
+
}
|
|
212
355
|
export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
213
356
|
return {
|
|
214
357
|
async generate(modelId, system, prompt, maxTokens = 4096, onUsage, signal) {
|
|
@@ -216,7 +359,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
216
359
|
if (mock) {
|
|
217
360
|
stats.calls++;
|
|
218
361
|
const result = await mock.complete({ prompt });
|
|
219
|
-
return result.text;
|
|
362
|
+
return stripThinkBlocks(result.text);
|
|
220
363
|
}
|
|
221
364
|
const providerId = detectProviderForModel(modelId);
|
|
222
365
|
const factory = await resolveCouncilFactory(providerId);
|
|
@@ -228,18 +371,23 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
228
371
|
// eval) was a no-op — the call ran to completion or hit the 5-min timeout.
|
|
229
372
|
const { signal: timedSignal, cleanup: cleanupTimeout } = withTimeoutSignal(signal, COUNCIL_LLM_TIMEOUT_MS);
|
|
230
373
|
try {
|
|
231
|
-
const result = await withDeadlineRace(() => withVisibleRetry(() =>
|
|
374
|
+
const result = await withDeadlineRace(() => withVisibleRetry(() =>
|
|
375
|
+
// Stream + collect (NOT generateText). The codex/oauth endpoint
|
|
376
|
+
// 400s on non-stream requests ("Stream must be set to true"),
|
|
377
|
+
// which nulled every council eval/clarify/synthesis on a codex
|
|
378
|
+
// session → the opaque "evaluation unavailable" card. See
|
|
379
|
+
// collectStreamText's doc for the full diagnosis.
|
|
380
|
+
collectStreamText({
|
|
232
381
|
model: runtime.model,
|
|
233
382
|
system,
|
|
234
383
|
prompt,
|
|
235
384
|
maxOutputTokens: maxTokens,
|
|
236
|
-
temperature:
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
...(runtime.providerOptions ? { providerOptions: runtime.providerOptions } : {}),
|
|
385
|
+
// Never hardcode temperature: some upstreams (Moonshot/Kimi via
|
|
386
|
+
// opencode-go) reject any value but their pinned one, which
|
|
387
|
+
// failed every clarify/spec call on a Kimi session. resolveTemperature
|
|
388
|
+
// omits the field or clamps to the model's fixed value.
|
|
389
|
+
temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
|
|
390
|
+
providerOptions: runtime.providerOptions,
|
|
243
391
|
abortSignal: timedSignal,
|
|
244
392
|
}), { label: "council.generate" }), COUNCIL_LLM_TIMEOUT_MS + 5_000, "council.generate", signal);
|
|
245
393
|
cleanupTimeout();
|
|
@@ -255,6 +403,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
255
403
|
durationMs: durMs,
|
|
256
404
|
});
|
|
257
405
|
onUsage?.(callUsage);
|
|
406
|
+
recordCouncilUsage(sessionId, modelId, callUsage);
|
|
258
407
|
writeDebugRecord({
|
|
259
408
|
ts: new Date().toISOString(),
|
|
260
409
|
kind: "generate",
|
|
@@ -273,7 +422,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
273
422
|
finishReason: result.finishReason,
|
|
274
423
|
usage: result.usage,
|
|
275
424
|
});
|
|
276
|
-
return result.text;
|
|
425
|
+
return stripThinkBlocks(result.text);
|
|
277
426
|
}
|
|
278
427
|
catch (err) {
|
|
279
428
|
cleanupTimeout();
|
|
@@ -300,7 +449,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
300
449
|
if (mock) {
|
|
301
450
|
stats.calls++;
|
|
302
451
|
const result = await mock.complete({ prompt });
|
|
303
|
-
return { text: result.text, toolCalls: [] };
|
|
452
|
+
return { text: stripThinkBlocks(result.text), toolCalls: [] };
|
|
304
453
|
}
|
|
305
454
|
const providerId = detectProviderForModel(modelId);
|
|
306
455
|
const factory = await resolveCouncilFactory(providerId);
|
|
@@ -351,10 +500,26 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
351
500
|
const t0 = Date.now();
|
|
352
501
|
const { signal: timedSignal, cleanup: cleanupTimeout } = withTimeoutSignal(signal, COUNCIL_LLM_TIMEOUT_MS);
|
|
353
502
|
try {
|
|
354
|
-
const result = await withDeadlineRace(() => withVisibleRetry(() =>
|
|
503
|
+
const result = await withDeadlineRace(() => withVisibleRetry(() =>
|
|
504
|
+
// Stream + collect (NOT generateText). A debater on the codex/oauth
|
|
505
|
+
// endpoint hits the same non-stream 400 ("Stream must be set to
|
|
506
|
+
// true") that nulled the eval path; streaming is uniform across
|
|
507
|
+
// providers. Tools (when the tier + circuit breaker allow) ride
|
|
508
|
+
// through with stepCountIs(2) + the same sanitizeHistory prepareStep.
|
|
509
|
+
collectStreamText({
|
|
355
510
|
model: runtime.model,
|
|
356
511
|
system,
|
|
357
512
|
prompt,
|
|
513
|
+
// Reasoning models (deepseek-v4-*, anthropic thinking) consume part
|
|
514
|
+
// of this budget on reasoning_tokens before producing user-visible
|
|
515
|
+
// text. E2E showed 2048 caused finishReason=length on 3KB debate
|
|
516
|
+
// prompts. 6144 leaves ~4000 tokens for text after typical reasoning
|
|
517
|
+
// overhead and avoids cuts mid-thought.
|
|
518
|
+
maxOutputTokens: 6144,
|
|
519
|
+
// See generate(): capability-aware temperature (omit / clamp).
|
|
520
|
+
temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
|
|
521
|
+
providerOptions: runtime.providerOptions,
|
|
522
|
+
abortSignal: timedSignal,
|
|
358
523
|
...(verificationTools && Object.keys(verificationTools).length > 0
|
|
359
524
|
? {
|
|
360
525
|
tools: verificationTools,
|
|
@@ -367,20 +532,6 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
367
532
|
},
|
|
368
533
|
}
|
|
369
534
|
: {}),
|
|
370
|
-
// Reasoning models (deepseek-v4-*, anthropic thinking) consume part
|
|
371
|
-
// of this budget on reasoning_tokens before producing user-visible
|
|
372
|
-
// text. E2E showed 2048 caused finishReason=length on 3KB debate
|
|
373
|
-
// prompts. 6144 leaves ~4000 tokens for text after typical reasoning
|
|
374
|
-
// overhead and avoids cuts mid-thought.
|
|
375
|
-
maxOutputTokens: 6144,
|
|
376
|
-
temperature: 0.7,
|
|
377
|
-
// Visible retry (src/utils/visible-retry.ts) replaces SDK's silent
|
|
378
|
-
// exponential backoff (2,4,8,16,32s). When SiliconFlow rate-limits
|
|
379
|
-
// with 429, user now sees "[retry] rate-limited (429) — waiting Xs
|
|
380
|
-
// before attempt N/6" instead of a 62s blank window that looks hung.
|
|
381
|
-
maxRetries: 0,
|
|
382
|
-
...(runtime.providerOptions ? { providerOptions: runtime.providerOptions } : {}),
|
|
383
|
-
abortSignal: timedSignal,
|
|
384
535
|
}), { label: "council.debate" }), COUNCIL_LLM_TIMEOUT_MS + 5_000, "council.debate", signal);
|
|
385
536
|
cleanupTimeout();
|
|
386
537
|
stats.calls++;
|
|
@@ -402,6 +553,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
402
553
|
stepCount: toolCalls.length,
|
|
403
554
|
});
|
|
404
555
|
onUsage?.(debateUsage);
|
|
556
|
+
recordCouncilUsage(sessionId, modelId, debateUsage);
|
|
405
557
|
writeDebugRecord({
|
|
406
558
|
ts: new Date().toISOString(),
|
|
407
559
|
kind: "debate",
|
|
@@ -423,7 +575,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
423
575
|
usage: result.usage,
|
|
424
576
|
});
|
|
425
577
|
return {
|
|
426
|
-
text: result.text,
|
|
578
|
+
text: stripThinkBlocks(result.text),
|
|
427
579
|
toolCalls: toolCalls,
|
|
428
580
|
};
|
|
429
581
|
}
|
|
@@ -456,7 +608,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
456
608
|
if (mock) {
|
|
457
609
|
stats.calls++;
|
|
458
610
|
const result = await mock.complete({ prompt: topic });
|
|
459
|
-
return result.text;
|
|
611
|
+
return stripThinkBlocks(result.text);
|
|
460
612
|
}
|
|
461
613
|
const providerId = detectProviderForModel(modelId);
|
|
462
614
|
const factory = await resolveCouncilFactory(providerId);
|
|
@@ -476,12 +628,21 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
476
628
|
const hasUrl = /https?:\/\/\S+/.test(topic);
|
|
477
629
|
const internetFirst = options?.internetFirst === true;
|
|
478
630
|
const systemPrompt = buildResearchSystemPrompt(hasUrl, internetFirst);
|
|
479
|
-
// Warn early if internet-first mode is requested but no
|
|
480
|
-
|
|
631
|
+
// Warn early if internet-first mode is requested but no *working* web
|
|
632
|
+
// research capability exists. The builtin `web_search`/`fetch_url` tools
|
|
633
|
+
// are ALWAYS registered, so the old tool-NAME check was always true — even
|
|
634
|
+
// when `web_search` will just return `ERROR no_tavily_key`, so the warning
|
|
635
|
+
// never fired and internet-first research failed silently. Gate on real
|
|
636
|
+
// capability: a Tavily key, or an MCP search/browser tool actually loaded
|
|
637
|
+
// (builtins alone don't count — open-ended search needs the key).
|
|
638
|
+
const hasTavilyKey = ((await getMcpKey("tavily")) || process.env.TAVILY_API_KEY || "").trim().length >= 10;
|
|
639
|
+
const mcpSearchTool = Object.keys(mcpBundle?.tools ?? {}).some((n) => /tavily|web[_-]?search|web[_-]?fetch|playwright|chrome|context7|firecrawl|exa|browser/i.test(n));
|
|
640
|
+
const internetToolAvailable = hasTavilyKey || mcpSearchTool;
|
|
481
641
|
const internetGapWarning = internetFirst && !internetToolAvailable
|
|
482
|
-
? `\n\n## Research Gap\n- Internet-first mode requested but no
|
|
483
|
-
`(
|
|
484
|
-
|
|
642
|
+
? `\n\n## Research Gap\n- Internet-first mode requested but no working web-search capability ` +
|
|
643
|
+
`(a Tavily API key or an MCP search/browser tool) is available. ` +
|
|
644
|
+
`\`fetch_url\` still works for explicit URLs, but open-ended search is unavailable — ` +
|
|
645
|
+
`findings will be limited to what the model already knows.`
|
|
485
646
|
: "";
|
|
486
647
|
const userPrompt = conversationContext
|
|
487
648
|
? `## Context\n${conversationContext}\n\n---\n\n## Research Topic\n${topic}\n\nInvestigate and report findings.`
|
|
@@ -508,7 +669,11 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
508
669
|
return stripped === messages ? {} : { messages: stripped };
|
|
509
670
|
},
|
|
510
671
|
maxOutputTokens: 4096,
|
|
511
|
-
|
|
672
|
+
// See generate(): capability-aware temperature (omit / clamp).
|
|
673
|
+
...(() => {
|
|
674
|
+
const t = resolveTemperature(providerId, runtime.modelInfo, 0.3);
|
|
675
|
+
return t === undefined ? {} : { temperature: t };
|
|
676
|
+
})(),
|
|
512
677
|
// Visible retry (src/utils/visible-retry.ts) replaces SDK's silent
|
|
513
678
|
// exponential backoff (2,4,8,16,32s). When SiliconFlow rate-limits
|
|
514
679
|
// with 429, user now sees "[retry] rate-limited (429) — waiting Xs
|
|
@@ -530,6 +695,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
530
695
|
stepCount: (result.toolCalls ?? []).length,
|
|
531
696
|
});
|
|
532
697
|
onUsage?.(researchUsage);
|
|
698
|
+
recordCouncilUsage(sessionId, modelId, researchUsage);
|
|
533
699
|
writeDebugRecord({
|
|
534
700
|
ts: new Date().toISOString(),
|
|
535
701
|
kind: "research",
|
|
@@ -558,7 +724,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
558
724
|
// CQ-04: When URL present, verify at least one browser tool was invoked
|
|
559
725
|
if (hasUrl) {
|
|
560
726
|
// Use result.toolCalls (flat array across all steps) — more reliably typed than steps[].toolCalls
|
|
561
|
-
const browserUsed = researchToolCalls.some((tc) => tc.toolName.includes("playwright") || tc.toolName.includes("chrome"));
|
|
727
|
+
const browserUsed = researchToolCalls.some((tc) => tc.toolName.includes("playwright") || tc.toolName.includes("chrome") || tc.toolName === "fetch_url");
|
|
562
728
|
if (!browserUsed) {
|
|
563
729
|
stats.calls++;
|
|
564
730
|
return (result.text +
|
|
@@ -568,7 +734,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
568
734
|
}
|
|
569
735
|
}
|
|
570
736
|
stats.calls++;
|
|
571
|
-
return result.text + internetGapWarning;
|
|
737
|
+
return stripThinkBlocks(result.text) + internetGapWarning;
|
|
572
738
|
}
|
|
573
739
|
catch (err) {
|
|
574
740
|
cleanupTimeout();
|
|
@@ -691,6 +857,33 @@ export async function* tracedGenerate(llm, args) {
|
|
|
691
857
|
};
|
|
692
858
|
return resultText;
|
|
693
859
|
}
|
|
860
|
+
/**
|
|
861
|
+
* Run {@link tracedGenerate} across a list of models, returning the first
|
|
862
|
+
* non-empty completion. A model that throws (e.g. a flaky proxy returning
|
|
863
|
+
* "Upstream request failed") or yields only whitespace advances to the next
|
|
864
|
+
* candidate; fallback attempts are labelled so the timeline shows the retry.
|
|
865
|
+
* Returns null when every candidate fails — the caller decides the degraded
|
|
866
|
+
* fallback (e.g. a single-criterion spec). Dedupes the model list in order.
|
|
867
|
+
*/
|
|
868
|
+
export async function* tracedGenerateWithFallback(llm, args) {
|
|
869
|
+
const seen = new Set();
|
|
870
|
+
const models = args.models.filter((m) => m && !seen.has(m) && (seen.add(m), true));
|
|
871
|
+
for (let i = 0; i < models.length; i++) {
|
|
872
|
+
try {
|
|
873
|
+
const raw = yield* tracedGenerate(llm, {
|
|
874
|
+
...args,
|
|
875
|
+
modelId: models[i],
|
|
876
|
+
label: i > 0 ? `${args.label} (fallback: ${models[i]})` : args.label,
|
|
877
|
+
});
|
|
878
|
+
if (raw?.trim())
|
|
879
|
+
return raw;
|
|
880
|
+
}
|
|
881
|
+
catch {
|
|
882
|
+
/* try the next candidate */
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
694
887
|
/**
|
|
695
888
|
* Generic version of {@link tracedGenerate} for arbitrary async work
|
|
696
889
|
* (e.g. `llm.research`, `Promise.all` over multiple model calls).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/council/panel-select.ts
|
|
3
|
+
*
|
|
4
|
+
* U3 — task-aware debate panel selection. Instead of a fixed capability roster
|
|
5
|
+
* (resolveParticipants picks by tier, blind to the prompt), the leader reads the
|
|
6
|
+
* task and CHOOSES which reachable models should debate it. Fail-open: any parse
|
|
7
|
+
* / provider failure returns null and the caller keeps the default roster. The
|
|
8
|
+
* internal implement/verify/research routing slots are assigned here purely for
|
|
9
|
+
* downstream compatibility — they are never surfaced to the user (the UI shows
|
|
10
|
+
* the task-adaptive persona / model, per U1.1).
|
|
11
|
+
*/
|
|
12
|
+
import type { StreamChunk } from "../types/index.js";
|
|
13
|
+
import type { ModelRole } from "../utils/settings.js";
|
|
14
|
+
import type { CouncilCandidate } from "./leader.js";
|
|
15
|
+
import type { CouncilLLM } from "./types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Ask the leader to select a task-appropriate debate panel from `pool`.
|
|
18
|
+
* Returns a roster (models mapped onto internal routing roles) or null when the
|
|
19
|
+
* leader is unavailable / returns an unusable selection — the caller then keeps
|
|
20
|
+
* its default roster.
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectTaskAwarePanel(opts: {
|
|
23
|
+
topic: string;
|
|
24
|
+
pool: CouncilCandidate[];
|
|
25
|
+
leaderModelId: string;
|
|
26
|
+
llm: CouncilLLM;
|
|
27
|
+
}): AsyncGenerator<StreamChunk, Array<{
|
|
28
|
+
role: ModelRole;
|
|
29
|
+
model: string;
|
|
30
|
+
}> | null, unknown>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/council/panel-select.ts
|
|
3
|
+
*
|
|
4
|
+
* U3 — task-aware debate panel selection. Instead of a fixed capability roster
|
|
5
|
+
* (resolveParticipants picks by tier, blind to the prompt), the leader reads the
|
|
6
|
+
* task and CHOOSES which reachable models should debate it. Fail-open: any parse
|
|
7
|
+
* / provider failure returns null and the caller keeps the default roster. The
|
|
8
|
+
* internal implement/verify/research routing slots are assigned here purely for
|
|
9
|
+
* downstream compatibility — they are never surfaced to the user (the UI shows
|
|
10
|
+
* the task-adaptive persona / model, per U1.1).
|
|
11
|
+
*/
|
|
12
|
+
import { tracedGenerate } from "./llm.js";
|
|
13
|
+
const ALL_ROLES = ["implement", "verify", "research"];
|
|
14
|
+
const SELECT_SYSTEM = "You are the council leader assembling a debate panel for a specific task. From the AVAILABLE MODELS, " +
|
|
15
|
+
"pick the 2-4 that will produce the most rigorous, diverse debate for THIS task — prefer provider and " +
|
|
16
|
+
"tier diversity, and match model strengths to what the task actually needs (e.g. a decision/analysis task " +
|
|
17
|
+
"wants strong reasoners; a build task wants a coding-capable model plus a critical reviewer). " +
|
|
18
|
+
'Return ONLY JSON: {"members":[{"model":"<exact id from the list>","why":"<short reason>"}]}. ' +
|
|
19
|
+
"Use exact model ids from the list; never invent an id.";
|
|
20
|
+
/**
|
|
21
|
+
* Ask the leader to select a task-appropriate debate panel from `pool`.
|
|
22
|
+
* Returns a roster (models mapped onto internal routing roles) or null when the
|
|
23
|
+
* leader is unavailable / returns an unusable selection — the caller then keeps
|
|
24
|
+
* its default roster.
|
|
25
|
+
*/
|
|
26
|
+
export async function* selectTaskAwarePanel(opts) {
|
|
27
|
+
const { topic, pool, leaderModelId, llm } = opts;
|
|
28
|
+
if (pool.length < 2)
|
|
29
|
+
return null;
|
|
30
|
+
const poolList = pool
|
|
31
|
+
.map((c) => `- ${c.model}${c.tier ? ` [${c.tier}]` : ""}${c.provider ? ` (${c.provider})` : ""}: ${c.description.slice(0, 120)}`)
|
|
32
|
+
.join("\n");
|
|
33
|
+
const prompt = `## Task\n${topic}\n\n## Available models\n${poolList}\n\nSelect the 2-4 models best suited to debate this task.`;
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = yield* tracedGenerate(llm, {
|
|
37
|
+
phase: "panel_select",
|
|
38
|
+
label: "Leader selecting debate panel",
|
|
39
|
+
modelId: leaderModelId,
|
|
40
|
+
system: SELECT_SYSTEM,
|
|
41
|
+
prompt,
|
|
42
|
+
maxTokens: 512,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
49
|
+
if (!match)
|
|
50
|
+
return null;
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(match[0]);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const valid = new Set(pool.map((c) => c.model));
|
|
59
|
+
const picked = [];
|
|
60
|
+
for (const m of parsed.members ?? []) {
|
|
61
|
+
const id = typeof m?.model === "string" ? m.model.trim() : "";
|
|
62
|
+
if (id && valid.has(id) && !picked.includes(id))
|
|
63
|
+
picked.push(id);
|
|
64
|
+
if (picked.length >= 4)
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (picked.length < 2)
|
|
68
|
+
return null;
|
|
69
|
+
// Roles are internal cost-tier routing slots (hidden from UI); assign cyclically.
|
|
70
|
+
return picked.map((model, i) => ({ role: ALL_ROLES[i % ALL_ROLES.length], model }));
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=panel-select.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getCouncilLanguage } from "../utils/settings.js";
|
|
1
2
|
import { tracedGenerate } from "./llm.js";
|
|
2
3
|
import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
|
|
3
4
|
import { buildSynthesisPrompt } from "./prompts.js";
|
|
@@ -33,6 +34,9 @@ planEmphasis) {
|
|
|
33
34
|
outputStyle: outputStyle ?? undefined,
|
|
34
35
|
refineContext,
|
|
35
36
|
planEmphasis,
|
|
37
|
+
// Feature B — synthesis output language follows the resolved council
|
|
38
|
+
// debate language (auto → detect from the brief; pinned → that locale).
|
|
39
|
+
language: getCouncilLanguage(),
|
|
36
40
|
};
|
|
37
41
|
const first = buildSynthesisPrompt(baseArgs);
|
|
38
42
|
synthesisText = yield* tracedGenerate(llm, {
|
|
@@ -233,10 +237,29 @@ function parseOutcome(synthesisText, debatePlan) {
|
|
|
233
237
|
sections[s.key] = parsed[s.key];
|
|
234
238
|
}
|
|
235
239
|
}
|
|
240
|
+
// Model-first post-debate options. Keep only entries whose action is in
|
|
241
|
+
// the wired vocabulary; drop malformed items so a hallucinated action id
|
|
242
|
+
// can't reach the handler switch. Empty → index.ts uses its fallback set.
|
|
243
|
+
const VALID_ACTIONS = new Set(["ask_followup", "generate_plan", "implement", "save_exit", "continue_session"]);
|
|
244
|
+
const nextActions = Array.isArray(parsed.nextActions)
|
|
245
|
+
? parsed.nextActions
|
|
246
|
+
.filter((a) => !!a &&
|
|
247
|
+
typeof a === "object" &&
|
|
248
|
+
typeof a.action === "string" &&
|
|
249
|
+
VALID_ACTIONS.has(a.action) &&
|
|
250
|
+
typeof a.label === "string" &&
|
|
251
|
+
a.label.trim().length > 0)
|
|
252
|
+
.map((a) => ({
|
|
253
|
+
action: a.action,
|
|
254
|
+
label: a.label.trim(),
|
|
255
|
+
reason: typeof a.reason === "string" ? a.reason.trim() : undefined,
|
|
256
|
+
}))
|
|
257
|
+
: undefined;
|
|
236
258
|
return {
|
|
237
259
|
type,
|
|
238
260
|
summary,
|
|
239
261
|
sections: Object.keys(sections).length > 0 ? sections : undefined,
|
|
262
|
+
nextActions: nextActions && nextActions.length > 0 ? nextActions : undefined,
|
|
240
263
|
// Legacy fields — synthesizer may still emit them when shape calls for them.
|
|
241
264
|
agreed: Array.isArray(parsed.agreed) ? parsed.agreed : undefined,
|
|
242
265
|
tradeoffs: Array.isArray(parsed.tradeoffs) ? parsed.tradeoffs : undefined,
|
|
@@ -3,6 +3,13 @@ import type { ClarifiedSpec, PreflightResponder } from "./types.js";
|
|
|
3
3
|
export interface RunPreflightOptions {
|
|
4
4
|
repoEmpty?: boolean;
|
|
5
5
|
researchOverridable?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* ROI: when the clarifier judged the spec ready (high confidence, no gaps),
|
|
8
|
+
* the approve card is a rubber-stamp. Show the Discussion Brief for
|
|
9
|
+
* transparency but auto-approve without blocking on a gate. Default false —
|
|
10
|
+
* an unready/low-confidence spec still surfaces the approve card.
|
|
11
|
+
*/
|
|
12
|
+
autoApprove?: boolean;
|
|
6
13
|
}
|
|
7
14
|
export declare function runPreflight(spec: ClarifiedSpec, participants: Array<{
|
|
8
15
|
role: string;
|
|
@@ -31,13 +31,25 @@ export async function* runPreflight(spec, participants, researchNeeded, respondT
|
|
|
31
31
|
},
|
|
32
32
|
{ heading: "Scope", body: spec.scope || "Not specified" },
|
|
33
33
|
{
|
|
34
|
-
heading: "
|
|
35
|
-
body: participants.map((p) => `- ${p.
|
|
34
|
+
heading: "Panel",
|
|
35
|
+
body: participants.map((p) => `- ${p.model}`).join("\n"),
|
|
36
36
|
},
|
|
37
37
|
{ heading: "Research phase", body: `${researchMode}${researchNote}` },
|
|
38
38
|
],
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
|
+
// ROI: skip the approve gate when the spec is already judged ready — show the
|
|
42
|
+
// brief above for transparency but don't block on a rubber-stamp.
|
|
43
|
+
if (options?.autoApprove === true) {
|
|
44
|
+
yield phaseDone({
|
|
45
|
+
phaseId: "phase:preflight",
|
|
46
|
+
kind: "preflight",
|
|
47
|
+
label: "Pre-flight review",
|
|
48
|
+
startedAt,
|
|
49
|
+
detail: "auto-approved (spec ready)",
|
|
50
|
+
});
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
41
53
|
yield {
|
|
42
54
|
type: "council_preflight",
|
|
43
55
|
content: "Review the discussion brief above. Approve to start debate, or reject to revise.",
|