muonroi-cli 1.8.3 → 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 +21 -21
- package/README.md +133 -122
- 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 +30 -15
- 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 +254 -84
- 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/oauth-callback.js +2 -2
- package/dist/src/mcp/research-onboarding.js +8 -7
- package/dist/src/mcp/runtime.js +34 -2
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +77 -76
- 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 +8 -8
- 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 +159 -159
- 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 +26 -0
- package/dist/src/orchestrator/tool-engine.js +669 -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/layer2_5-ponytail.js +8 -8
- 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/done-gate.js +3 -3
- 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 +327 -116
- 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/progress-snapshot.js +4 -4
- 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 +82 -172
- 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/reporter/index.js +1 -1
- 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/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
- package/dist/src/scaffold/continuation-prompt.js +86 -60
- package/dist/src/scaffold/init-new.js +453 -453
- 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 +22 -22
- 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/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +196 -126
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.d.ts +28 -10
- package/dist/src/storage/sessions.js +112 -55
- package/dist/src/storage/transcript-view.js +1 -1
- package/dist/src/storage/transcript.d.ts +51 -0
- package/dist/src/storage/transcript.js +383 -112
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- 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-inspect.js +4 -4
- 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/clipboard-image.js +23 -23
- package/dist/src/utils/install-manager.js +14 -11
- 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/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- 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
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { runDebate } from "../council/debate.js";
|
|
3
|
+
import { deleteDebateInputs, readDebateCheckpoint, readDebateInputs, writeDebateInputs, } from "../council/debate-checkpoint.js";
|
|
4
|
+
import { resolveDebateSummary } from "../council/debate-summary.js";
|
|
3
5
|
import { resolveLeaderModelDetailed, resolveParticipants } from "../council/leader.js";
|
|
4
6
|
import { phaseStart } from "../council/phase-events.js";
|
|
5
7
|
import { runPreflight } from "../council/preflight.js";
|
|
8
|
+
import { makeStanceRecall } from "../council/stance-recall.js";
|
|
6
9
|
import { fetchBBContext, inferBBFromPrompt, renderBBContextBlock } from "../ee/bb-retrieval.js";
|
|
10
|
+
import { getDefaultEEClient } from "../ee/intercept.js";
|
|
11
|
+
import { fireAndForgetWorkflowEvent } from "../ee/workflow-event.js";
|
|
7
12
|
import { readArtifact, writeArtifact } from "../flow/artifact-io.js";
|
|
8
|
-
import {
|
|
13
|
+
import { ensureRunScoped } from "../flow/hierarchy.js";
|
|
14
|
+
import { renderResumeDigest, writeContextDoc, writeResearchDoc } from "../flow/run-artifacts.js";
|
|
15
|
+
import { logInteraction } from "../storage/index.js";
|
|
9
16
|
import { isCouncilMultiProviderPreferred } from "../utils/settings.js";
|
|
10
17
|
import { extractAssumptionsFromDebate, mergeAssumptions, renderLedgerSummary } from "./assumption-ledger.js";
|
|
11
18
|
import { buildPriorContext } from "./cross-run-memory.js";
|
|
@@ -17,42 +24,13 @@ import { recordPhaseEnd, recordPhaseStart } from "./phase-budget.js";
|
|
|
17
24
|
import { additionalPrefills, auditAsContextBlock, auditRepo } from "./repo-audit.js";
|
|
18
25
|
import { SEED_DIMENSIONS } from "./seed-questions.js";
|
|
19
26
|
import { deriveTasksFromSpec, writeTasks } from "./typed-artifacts.js";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function wrapLLMForUsageTracking(llm, ctx) {
|
|
28
|
-
const sid = ctx.sessionId ?? ctx.runId;
|
|
29
|
-
const recorder = (modelId) => (usage) => {
|
|
30
|
-
try {
|
|
31
|
-
recordUsageEvent(sid, "council", modelId, {
|
|
32
|
-
inputTokens: usage.inputTokens,
|
|
33
|
-
outputTokens: usage.outputTokens,
|
|
34
|
-
cacheReadTokens: usage.cachedInputTokens,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
/* best-effort — never block the debate */
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
return {
|
|
42
|
-
generate: (modelId, system, prompt, maxTokens, onUsage, signal) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
|
|
43
|
-
recorder(modelId)(u);
|
|
44
|
-
onUsage?.(u);
|
|
45
|
-
}, signal),
|
|
46
|
-
debate: (modelId, system, prompt, signal, persistTrace, options, onUsage) => llm.debate(modelId, system, prompt, signal, persistTrace, options, (u) => {
|
|
47
|
-
recorder(modelId)(u);
|
|
48
|
-
onUsage?.(u);
|
|
49
|
-
}),
|
|
50
|
-
research: (modelId, topic, conversationContext, signal, persistTrace, options, onUsage) => llm.research(modelId, topic, conversationContext, signal, persistTrace, options, (u) => {
|
|
51
|
-
recorder(modelId)(u);
|
|
52
|
-
onUsage?.(u);
|
|
53
|
-
}),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
27
|
+
// Council usage_events recording (source="council") now happens at the single
|
|
28
|
+
// source of truth inside createCouncilLLM (src/council/llm.ts → recordCouncilUsage),
|
|
29
|
+
// so it covers EVERY council entry point — the /council slash path, auto-council,
|
|
30
|
+
// and all /ideal phases (clarifier, research, generate, sprint) — not just this
|
|
31
|
+
// driver's runDebate call site. The former loop-driver-local wrapper only wrapped
|
|
32
|
+
// the debate llm and double-counted nothing else; recording at the source removes
|
|
33
|
+
// both the leak and the risk of a double count here.
|
|
56
34
|
/**
|
|
57
35
|
* Best-effort interaction_logs writer for the loop-driver. Swallows failures
|
|
58
36
|
* so a broken DB never blocks the FSM. `ctx.sessionId` falls back to runId
|
|
@@ -71,6 +49,21 @@ function logLoopEvent(ctx, subtype, data) {
|
|
|
71
49
|
/* non-critical — audit trail only */
|
|
72
50
|
}
|
|
73
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Clamp a title for the milestone/phase index (F12): truncate at a word
|
|
54
|
+
* boundary when possible and strip trailing punctuation/whitespace so a raw
|
|
55
|
+
* slice can't leave a dangling "(" or a half word. Falls back to a hard slice
|
|
56
|
+
* for a single long token.
|
|
57
|
+
*/
|
|
58
|
+
export function clampTitle(raw, max) {
|
|
59
|
+
const s = (raw ?? "").trim();
|
|
60
|
+
if (s.length <= max)
|
|
61
|
+
return s.replace(/[\s([{<"'.,;:–—-]+$/u, "").trim() || s;
|
|
62
|
+
const hard = s.slice(0, max);
|
|
63
|
+
const lastSpace = hard.lastIndexOf(" ");
|
|
64
|
+
const base = lastSpace >= Math.floor(max * 0.6) ? hard.slice(0, lastSpace) : hard;
|
|
65
|
+
return base.replace(/[\s([{<"'.,;:–—-]+$/u, "").trim() || hard.trim();
|
|
66
|
+
}
|
|
74
67
|
function buildWorkspaceDiscoveryCard(d, a, priorRunCount) {
|
|
75
68
|
const sections = [];
|
|
76
69
|
if (!d.hasProject) {
|
|
@@ -157,6 +150,12 @@ export async function* runLoopDriver(ctx) {
|
|
|
157
150
|
let state = "idle";
|
|
158
151
|
let clarifiedSpec;
|
|
159
152
|
let debateState;
|
|
153
|
+
// Resolved once the research debate completes (F9): runningSummary, or a
|
|
154
|
+
// faithful fallback synthesized from participant positions when the debate
|
|
155
|
+
// returned after openings without a running summary. Reused across the
|
|
156
|
+
// research artifacts AND the scoping synthesis prompt so neither loses the
|
|
157
|
+
// debate. Empty until the research phase runs.
|
|
158
|
+
let resolvedDebateSummary = "";
|
|
160
159
|
let discovery;
|
|
161
160
|
let audit;
|
|
162
161
|
let productSpec;
|
|
@@ -168,6 +167,28 @@ export async function* runLoopDriver(ctx) {
|
|
|
168
167
|
const leaderResolution = await resolveLeaderModelDetailed(ctx.sessionModelId);
|
|
169
168
|
const leaderModelId = leaderResolution.modelId;
|
|
170
169
|
const councilParticipants = await resolveParticipants(ctx.sessionModelId, isCouncilMultiProviderPreferred());
|
|
170
|
+
// C-v2 cross-session resume — a persisted debate checkpoint means the council
|
|
171
|
+
// debate was interrupted in a PRIOR session (the process died mid-round; the
|
|
172
|
+
// in-process C-v1 retry only covers same-session throws). Skip the already-
|
|
173
|
+
// completed + persisted discovery + interview and jump straight to the
|
|
174
|
+
// research/debate stage with the restored gather outputs; the `research` case
|
|
175
|
+
// reads the checkpoint and resumes the debate from its last completed round.
|
|
176
|
+
const priorCheckpoint = await readDebateCheckpoint(runDir);
|
|
177
|
+
if (priorCheckpoint) {
|
|
178
|
+
const inputs = await readDebateInputs(runDir);
|
|
179
|
+
if (inputs) {
|
|
180
|
+
clarifiedSpec = inputs.clarifiedSpec;
|
|
181
|
+
conversationContext = inputs.conversationContext;
|
|
182
|
+
state = "research";
|
|
183
|
+
yield {
|
|
184
|
+
type: "content",
|
|
185
|
+
content: `\n> Resuming an interrupted council debate (round ${priorCheckpoint.roundCount + 1}) — skipping discovery + interview.\n`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.error(`[loop-driver] debate checkpoint present but debate-inputs.json missing (run ${ctx.runId}); running the council FSM fresh.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
171
192
|
while (true) {
|
|
172
193
|
switch (state) {
|
|
173
194
|
case "idle": {
|
|
@@ -218,6 +239,21 @@ export async function* runLoopDriver(ctx) {
|
|
|
218
239
|
if (discoveryCard) {
|
|
219
240
|
yield { type: "council_info_card", councilInfoCard: discoveryCard };
|
|
220
241
|
}
|
|
242
|
+
// Part A — persist prior-run context as a first-class context.md. The
|
|
243
|
+
// digest is intentionally NOT re-injected into the live system prompt
|
|
244
|
+
// (PIL Layer 3 already does semantic injection), but writing it to disk
|
|
245
|
+
// makes it a reviewable, resumable surface — the run's inherited memory.
|
|
246
|
+
try {
|
|
247
|
+
const ctxParts = [`Prior runs on this workspace: ${prior.runs.length}`];
|
|
248
|
+
if (prior.digest?.trim())
|
|
249
|
+
ctxParts.push("", prior.digest.trim());
|
|
250
|
+
if (audit.hasProject)
|
|
251
|
+
ctxParts.push("", "## Repo audit", conversationContext);
|
|
252
|
+
await writeContextDoc(ctx.flowDir, ctx.runId, ctxParts.join("\n"));
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
/* non-critical — context.md is a review surface, never blocks the FSM */
|
|
256
|
+
}
|
|
221
257
|
// Persist discovery evidence + repo audit so resume can replay.
|
|
222
258
|
const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
|
|
223
259
|
if (discovery.evidence.length > 0) {
|
|
@@ -266,7 +302,12 @@ export async function* runLoopDriver(ctx) {
|
|
|
266
302
|
});
|
|
267
303
|
// Write Resume Digest to state.md
|
|
268
304
|
const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
|
|
269
|
-
stateMap.sections.set("Resume Digest",
|
|
305
|
+
stateMap.sections.set("Resume Digest", renderResumeDigest({
|
|
306
|
+
stage: "gather",
|
|
307
|
+
lastCompleted: "discover",
|
|
308
|
+
nextAction: "Answer clarification questions to define product dimensions",
|
|
309
|
+
updatedAt: new Date().toISOString(),
|
|
310
|
+
}));
|
|
270
311
|
await writeArtifact(runDir, "state.md", stateMap);
|
|
271
312
|
// runGatherPhase is async (not a generator), but the discovery
|
|
272
313
|
// interview emits askcard chunks via the io.emit callback. We collect
|
|
@@ -390,6 +431,9 @@ export async function* runLoopDriver(ctx) {
|
|
|
390
431
|
if (!clarifiedSpec) {
|
|
391
432
|
return { runId: ctx.runId, stage: "error", success: false, reason: "missing_spec_for_research" };
|
|
392
433
|
}
|
|
434
|
+
// Part E — web-research confidence for this run, filled when the
|
|
435
|
+
// Researcher stance is (re)assigned to a web-capable model below.
|
|
436
|
+
let researchWebConfidence;
|
|
393
437
|
const researchPhaseStartMs = Date.now();
|
|
394
438
|
yield phaseStart({
|
|
395
439
|
phaseId: "loop:research",
|
|
@@ -402,7 +446,12 @@ export async function* runLoopDriver(ctx) {
|
|
|
402
446
|
phase: "research",
|
|
403
447
|
});
|
|
404
448
|
const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
|
|
405
|
-
stateMap.sections.set("Resume Digest",
|
|
449
|
+
stateMap.sections.set("Resume Digest", renderResumeDigest({
|
|
450
|
+
stage: "research",
|
|
451
|
+
lastCompleted: "gather",
|
|
452
|
+
nextAction: "Run / resume the multi-expert council debate",
|
|
453
|
+
updatedAt: new Date().toISOString(),
|
|
454
|
+
}));
|
|
406
455
|
await writeArtifact(runDir, "state.md", stateMap);
|
|
407
456
|
// inside research phase, before building councilTopic:
|
|
408
457
|
const projectCtx = await readProjectContext(ctx.flowDir, ctx.runId);
|
|
@@ -456,6 +505,42 @@ export async function* runLoopDriver(ctx) {
|
|
|
456
505
|
stance: s,
|
|
457
506
|
};
|
|
458
507
|
});
|
|
508
|
+
// Part E — the Researcher stance MUST prefer a model with NATIVE online
|
|
509
|
+
// web research (its own web_search/browsing), so the research phase gets
|
|
510
|
+
// real online facts rather than codebase-only reasoning. If a web-capable
|
|
511
|
+
// model is reachable this session, route the Researcher to it; otherwise
|
|
512
|
+
// record degraded confidence into research.md (the fallback add-in path
|
|
513
|
+
// — Tavily/MCP — is untrusted per the owner's Part E principle).
|
|
514
|
+
const { modelHasNativeWebResearch, getWebResearchModel } = await import("../models/registry.js");
|
|
515
|
+
const reachableIds = new Set([...councilParticipants.map((p) => p.model), leaderModelId].filter(Boolean));
|
|
516
|
+
const researcherIdx = participants.findIndex((p) => p.stance?.name === "Researcher");
|
|
517
|
+
if (researcherIdx >= 0) {
|
|
518
|
+
const current = participants[researcherIdx].model;
|
|
519
|
+
if (!modelHasNativeWebResearch(current)) {
|
|
520
|
+
// ONLY reroute to a web-native model that is actually REACHABLE this
|
|
521
|
+
// session. A model whose provider has no factory wired (e.g. grok/xai
|
|
522
|
+
// when only opencode-go is authed) would fail council participant
|
|
523
|
+
// creation ("no factory for model's provider") and wedge the debate —
|
|
524
|
+
// so if no reachable web-native model exists, degrade gracefully and
|
|
525
|
+
// KEEP the current reachable model rather than swap in a dead id.
|
|
526
|
+
const webModel = getWebResearchModel(reachableIds);
|
|
527
|
+
if (webModel) {
|
|
528
|
+
participants[researcherIdx].model = webModel.id;
|
|
529
|
+
researchWebConfidence = { confidence: "native", model: webModel.id };
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
researchWebConfidence = { confidence: "degraded" };
|
|
533
|
+
logLoopEvent(ctx, "research_web_degraded", {
|
|
534
|
+
phase: "research",
|
|
535
|
+
reason: "no reachable native_web_research model",
|
|
536
|
+
researcherModel: current,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
researchWebConfidence = { confidence: "native", model: current };
|
|
542
|
+
}
|
|
543
|
+
}
|
|
459
544
|
// CB-1 — BB-aware context injection. Runs before council debate fires so
|
|
460
545
|
// the research stances have access to relevant BB recipes and rules.
|
|
461
546
|
//
|
|
@@ -503,67 +588,124 @@ export async function* runLoopDriver(ctx) {
|
|
|
503
588
|
/* graceful degrade — never block the research phase */
|
|
504
589
|
}
|
|
505
590
|
}
|
|
506
|
-
|
|
507
|
-
|
|
591
|
+
// C-v2 — persist the minimal gather outputs so a fresh session can
|
|
592
|
+
// re-enter the debate (loop-driver resume entry above) without re-running
|
|
593
|
+
// the interactive discovery + interview. Written once here at debate start,
|
|
594
|
+
// captures the final conversationContext (after project + BB context).
|
|
595
|
+
// Deleted after scoping writes the spec. Non-fatal on failure.
|
|
596
|
+
await writeDebateInputs(runDir, {
|
|
597
|
+
version: 1,
|
|
598
|
+
problemStatement: clarifiedSpec.problemStatement,
|
|
599
|
+
clarifiedSpec,
|
|
508
600
|
conversationContext,
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
//
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
601
|
+
savedAt: new Date().toISOString(),
|
|
602
|
+
});
|
|
603
|
+
// C (mid-debate checkpoint) — runDebate snapshots its per-round state to
|
|
604
|
+
// `<runDir>/debate-checkpoint.json` after each completed round and deletes
|
|
605
|
+
// it on normal completion. If a prior attempt (this process, or a crashed
|
|
606
|
+
// earlier run of the SAME runId) left a checkpoint, seed it so the debate
|
|
607
|
+
// resumes from the last completed round instead of re-running rounds 1..N.
|
|
608
|
+
let resumeCheckpoint = (await readDebateCheckpoint(runDir)) ?? undefined;
|
|
609
|
+
// In-process resilience: a mid-debate throw (provider 5xx after the
|
|
610
|
+
// fallback budget, transient network) re-runs runDebate ONCE (default)
|
|
611
|
+
// from the freshly-written checkpoint, so a round-5 break loses one round,
|
|
612
|
+
// not five. Only retried when a checkpoint exists (≥1 round completed) —
|
|
613
|
+
// a pre-round-1 failure has nothing to resume and rethrows immediately.
|
|
614
|
+
const maxDebateRetries = (() => {
|
|
615
|
+
const raw = process.env.MUONROI_DEBATE_RESUME_RETRIES;
|
|
616
|
+
const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
617
|
+
return Number.isFinite(n) && n >= 0 ? n : 1;
|
|
618
|
+
})();
|
|
619
|
+
for (let attempt = 0;; attempt++) {
|
|
620
|
+
const debateGen = runDebate(clarifiedSpec, {
|
|
621
|
+
topic: ctx.idea,
|
|
622
|
+
conversationContext,
|
|
623
|
+
leaderModelId,
|
|
624
|
+
participants,
|
|
625
|
+
runId: ctx.sessionId ?? ctx.runId,
|
|
626
|
+
checkpointDir: runDir,
|
|
627
|
+
resumeCheckpoint,
|
|
628
|
+
// Item 3 — per-stance recall: each participant opens grounded in the
|
|
629
|
+
// stance-weighted slice of the brain its lens cares about. Bounded +
|
|
630
|
+
// failure-tolerant; unavailable EE leaves openings unchanged.
|
|
631
|
+
stanceRecall: makeStanceRecall(getDefaultEEClient(), {
|
|
632
|
+
cwd: ctx.flowDir,
|
|
633
|
+
sourceSession: ctx.sessionId ?? ctx.runId,
|
|
634
|
+
}),
|
|
635
|
+
}, ctx.llm);
|
|
636
|
+
// Suppress raw debate content so the user is not confused by inter-role
|
|
637
|
+
// monologue ("Researcher → Architect ... Question back to you?") which
|
|
638
|
+
// is NOT addressed to them. We still pass through phase/status events
|
|
639
|
+
// so the UI keeps a live progress indicator. After the debate completes
|
|
640
|
+
// we emit a single condensed summary.
|
|
641
|
+
try {
|
|
642
|
+
while (true) {
|
|
643
|
+
const { value, done } = await debateGen.next();
|
|
644
|
+
if (done) {
|
|
645
|
+
debateState = value;
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
const chunk = value;
|
|
649
|
+
if (chunk.type === "content")
|
|
650
|
+
continue;
|
|
651
|
+
// Persist debate speaker turns to interaction_logs so forensics
|
|
652
|
+
// can replay the debate text without relying on TUI scrollback
|
|
653
|
+
// (which currently holds the only copy — messages/usage_events
|
|
654
|
+
// tables stay empty for the debate path).
|
|
655
|
+
if (chunk.type === "council_message" && chunk.councilMessage) {
|
|
656
|
+
const cm = chunk.councilMessage;
|
|
657
|
+
logLoopEvent(ctx, "council_message", {
|
|
658
|
+
phase: "research",
|
|
659
|
+
kind: cm.kind,
|
|
660
|
+
speakerRole: cm.speaker.role,
|
|
661
|
+
speakerModel: cm.speaker.model,
|
|
662
|
+
partnerRole: cm.partner?.role ?? null,
|
|
663
|
+
round: cm.round ?? null,
|
|
664
|
+
attempts: cm.attempts ?? 1,
|
|
665
|
+
failureReason: cm.failureReason ?? null,
|
|
666
|
+
toolCalls: cm.toolCalls?.map((tc) => tc.name) ?? [],
|
|
667
|
+
// Cap at 4000 chars so a single row stays well under SQLite
|
|
668
|
+
// text limits even for the most verbose speaker turn.
|
|
669
|
+
textExcerpt: cm.text.slice(0, 4000),
|
|
670
|
+
textLength: cm.text.length,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
yield chunk;
|
|
523
674
|
}
|
|
524
|
-
|
|
525
|
-
|
|
675
|
+
break; // debate completed — exit the retry loop.
|
|
676
|
+
}
|
|
677
|
+
catch (err) {
|
|
678
|
+
// The debate iterator hit an exception (e.g. provider 5xx after the
|
|
679
|
+
// retry budget). Persist an audit row so we have a forensics trail —
|
|
680
|
+
// without it the FSM unwinds silently and the session looks like
|
|
681
|
+
// "research = 0 word" in the DB.
|
|
682
|
+
const freshCp = (await readDebateCheckpoint(runDir)) ?? undefined;
|
|
683
|
+
logLoopEvent(ctx, "council_error", {
|
|
684
|
+
phase: "research",
|
|
685
|
+
stage: "debate",
|
|
686
|
+
error: err instanceof Error ? err.message : String(err),
|
|
687
|
+
roundCount: debateState?.roundCount ?? freshCp?.roundCount ?? 0,
|
|
688
|
+
participantCount: participants.length,
|
|
689
|
+
elapsedMs: Date.now() - researchPhaseStartMs,
|
|
690
|
+
});
|
|
691
|
+
// C — resume-from-checkpoint retry: only when a completed round was
|
|
692
|
+
// checkpointed and retries remain. Otherwise rethrow unchanged.
|
|
693
|
+
if (attempt < maxDebateRetries && freshCp && freshCp.roundCount >= 1) {
|
|
694
|
+
resumeCheckpoint = freshCp;
|
|
695
|
+
yield {
|
|
696
|
+
type: "content",
|
|
697
|
+
content: `\n> Debate interrupted (${err instanceof Error ? err.message : String(err)}); resuming from round ${freshCp.roundCount + 1}…\n`,
|
|
698
|
+
};
|
|
526
699
|
continue;
|
|
527
|
-
// Persist debate speaker turns to interaction_logs so forensics
|
|
528
|
-
// can replay the debate text without relying on TUI scrollback
|
|
529
|
-
// (which currently holds the only copy — messages/usage_events
|
|
530
|
-
// tables stay empty for the debate path).
|
|
531
|
-
if (chunk.type === "council_message" && chunk.councilMessage) {
|
|
532
|
-
const cm = chunk.councilMessage;
|
|
533
|
-
logLoopEvent(ctx, "council_message", {
|
|
534
|
-
phase: "research",
|
|
535
|
-
kind: cm.kind,
|
|
536
|
-
speakerRole: cm.speaker.role,
|
|
537
|
-
speakerModel: cm.speaker.model,
|
|
538
|
-
partnerRole: cm.partner?.role ?? null,
|
|
539
|
-
round: cm.round ?? null,
|
|
540
|
-
attempts: cm.attempts ?? 1,
|
|
541
|
-
failureReason: cm.failureReason ?? null,
|
|
542
|
-
toolCalls: cm.toolCalls?.map((tc) => tc.name) ?? [],
|
|
543
|
-
// Cap at 4000 chars so a single row stays well under SQLite
|
|
544
|
-
// text limits even for the most verbose speaker turn.
|
|
545
|
-
textExcerpt: cm.text.slice(0, 4000),
|
|
546
|
-
textLength: cm.text.length,
|
|
547
|
-
});
|
|
548
700
|
}
|
|
549
|
-
|
|
701
|
+
throw err;
|
|
550
702
|
}
|
|
551
703
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
logLoopEvent(ctx, "council_error", {
|
|
558
|
-
phase: "research",
|
|
559
|
-
stage: "debate",
|
|
560
|
-
error: err instanceof Error ? err.message : String(err),
|
|
561
|
-
roundCount: debateState?.roundCount ?? 0,
|
|
562
|
-
participantCount: participants.length,
|
|
563
|
-
elapsedMs: Date.now() - researchPhaseStartMs,
|
|
564
|
-
});
|
|
565
|
-
throw err;
|
|
566
|
-
}
|
|
704
|
+
// F9 — resolve the debate summary once (runningSummary or a fallback
|
|
705
|
+
// synthesized from participant positions) for reuse across the research
|
|
706
|
+
// artifacts + scoping synthesis.
|
|
707
|
+
if (debateState)
|
|
708
|
+
resolvedDebateSummary = resolveDebateSummary(debateState);
|
|
567
709
|
// Forensics row mirrors the council/index.ts council_summary record
|
|
568
710
|
// (which only fires for sprint planning via runCouncil). The /ideal
|
|
569
711
|
// initial debate goes through runDebate here and was previously
|
|
@@ -592,18 +734,47 @@ export async function* runLoopDriver(ctx) {
|
|
|
592
734
|
durationMs: Date.now() - researchPhaseStartMs,
|
|
593
735
|
});
|
|
594
736
|
}
|
|
595
|
-
const summaryText =
|
|
737
|
+
const summaryText = resolvedDebateSummary || "(debate produced no summary — using empty research findings)";
|
|
596
738
|
yield {
|
|
597
739
|
type: "council_info_card",
|
|
598
740
|
councilInfoCard: buildResearchSummaryCard(summaryText, debateState?.researchFindings),
|
|
599
741
|
};
|
|
600
|
-
// Append research summary to delegations.md
|
|
742
|
+
// Append research summary to delegations.md (kept for back-compat: the
|
|
743
|
+
// EE transcript extractor + legacy readers still read these sections).
|
|
744
|
+
// A debate that returns after openings (leader routes straight to the
|
|
745
|
+
// preflight gate) leaves runningSummary empty — synthesize a faithful
|
|
746
|
+
// fallback from the participants' positions so the canonical research
|
|
747
|
+
// artifacts never silently drop the whole debate.
|
|
601
748
|
const delegationsMap = (await readArtifact(runDir, "delegations.md")) ?? { preamble: "", sections: new Map() };
|
|
602
|
-
delegationsMap.sections.set("Research Summary",
|
|
749
|
+
delegationsMap.sections.set("Research Summary", resolvedDebateSummary);
|
|
603
750
|
if (debateState.researchFindings) {
|
|
604
751
|
delegationsMap.sections.set("Research Findings", debateState.researchFindings);
|
|
605
752
|
}
|
|
606
753
|
await writeArtifact(runDir, "delegations.md", delegationsMap);
|
|
754
|
+
// Part A — promote the debate output to a first-class research.md. This
|
|
755
|
+
// is the canonical, reviewable surface (`/ideal review`) and the home
|
|
756
|
+
// for the EE recall seed once per-turn grounding lands (deferred).
|
|
757
|
+
try {
|
|
758
|
+
await writeResearchDoc(ctx.flowDir, ctx.runId, {
|
|
759
|
+
summary: resolvedDebateSummary,
|
|
760
|
+
findings: debateState.researchFindings ?? undefined,
|
|
761
|
+
webResearch: researchWebConfidence,
|
|
762
|
+
});
|
|
763
|
+
// Part C — persist the debate as a workflow_debate experience so a
|
|
764
|
+
// future run on a similar topic can seed its stances with what this
|
|
765
|
+
// council concluded (gate-on-outcome: fired after the debate produced
|
|
766
|
+
// a summary, not per-turn — Kill #4/#5).
|
|
767
|
+
fireAndForgetWorkflowEvent({
|
|
768
|
+
kind: "council-debate",
|
|
769
|
+
phaseRef: `runs/${ctx.runId}#research`,
|
|
770
|
+
sessionId: ctx.sessionId ?? ctx.runId,
|
|
771
|
+
text: (debateState.runningSummary ?? "").slice(0, 2000) || `debate on: ${ctx.idea.slice(0, 200)}`,
|
|
772
|
+
payload: { topic: ctx.idea.slice(0, 200), roundCount: debateState.roundCount ?? 0 },
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
catch {
|
|
776
|
+
/* non-critical — research.md is a review surface, never blocks the FSM */
|
|
777
|
+
}
|
|
607
778
|
// P6 - extract assumptions from the research debate so foundational
|
|
608
779
|
// claims (perf budgets, SDK contracts, etc.) become trackable across
|
|
609
780
|
// sprints rather than buried in stance prose. Silent skip on
|
|
@@ -667,7 +838,12 @@ export async function* runLoopDriver(ctx) {
|
|
|
667
838
|
phase: "scoping",
|
|
668
839
|
});
|
|
669
840
|
const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
|
|
670
|
-
stateMap.sections.set("Resume Digest",
|
|
841
|
+
stateMap.sections.set("Resume Digest", renderResumeDigest({
|
|
842
|
+
stage: "scoping",
|
|
843
|
+
lastCompleted: "research",
|
|
844
|
+
nextAction: "Synthesize the ProductSpec and confirm the roadmap at the preflight gate",
|
|
845
|
+
updatedAt: new Date().toISOString(),
|
|
846
|
+
}));
|
|
671
847
|
await writeArtifact(runDir, "state.md", stateMap);
|
|
672
848
|
// Visible progress indicator — without this the TUI sits silent for
|
|
673
849
|
// 30-60s while ctx.llm.generate runs the synthesis call. Users read
|
|
@@ -684,24 +860,24 @@ export async function* runLoopDriver(ctx) {
|
|
|
684
860
|
},
|
|
685
861
|
};
|
|
686
862
|
// Synthesize ProductSpec
|
|
687
|
-
const synthesisPrompt = `Synthesize a ProductSpec JSON based on the following:
|
|
688
|
-
Idea: ${ctx.idea}
|
|
689
|
-
Clarified Spec: ${JSON.stringify(clarifiedSpec)}
|
|
690
|
-
Debate Summary: ${debateState.runningSummary}
|
|
691
|
-
Research Findings: ${debateState.researchFindings ?? "N/A"}
|
|
692
|
-
|
|
693
|
-
Output ONLY a JSON object matching this interface:
|
|
694
|
-
interface ProductSpec {
|
|
695
|
-
idea: string;
|
|
696
|
-
persona: string;
|
|
697
|
-
mvp: string[];
|
|
698
|
-
phase2: string[];
|
|
699
|
-
architecture: string;
|
|
700
|
-
ioContract: string;
|
|
701
|
-
folderStructure: string;
|
|
702
|
-
sprintEstimate: number;
|
|
703
|
-
costEstimate: number;
|
|
704
|
-
}
|
|
863
|
+
const synthesisPrompt = `Synthesize a ProductSpec JSON based on the following:
|
|
864
|
+
Idea: ${ctx.idea}
|
|
865
|
+
Clarified Spec: ${JSON.stringify(clarifiedSpec)}
|
|
866
|
+
Debate Summary: ${resolvedDebateSummary || debateState.runningSummary}
|
|
867
|
+
Research Findings: ${debateState.researchFindings ?? "N/A"}
|
|
868
|
+
|
|
869
|
+
Output ONLY a JSON object matching this interface:
|
|
870
|
+
interface ProductSpec {
|
|
871
|
+
idea: string;
|
|
872
|
+
persona: string;
|
|
873
|
+
mvp: string[];
|
|
874
|
+
phase2: string[];
|
|
875
|
+
architecture: string;
|
|
876
|
+
ioContract: string;
|
|
877
|
+
folderStructure: string;
|
|
878
|
+
sprintEstimate: number;
|
|
879
|
+
costEstimate: number;
|
|
880
|
+
}
|
|
705
881
|
`;
|
|
706
882
|
// The scoping phase's only LLM call. Wrapped so a provider hang/
|
|
707
883
|
// timeout leaves a council_error audit row instead of swallowing the
|
|
@@ -758,6 +934,41 @@ interface ProductSpec {
|
|
|
758
934
|
const roadmapMap = (await readArtifact(runDir, "roadmap.md")) ?? { preamble: "", sections: new Map() };
|
|
759
935
|
roadmapMap.sections.set("Product Specification", JSON.stringify(productSpec, null, 2));
|
|
760
936
|
await writeArtifact(runDir, "roadmap.md", roadmapMap);
|
|
937
|
+
// Part C — the synthesized spec IS the council's decision. Persist it as
|
|
938
|
+
// a workflow_decision experience so future runs don't re-litigate a
|
|
939
|
+
// settled architectural/scoping choice.
|
|
940
|
+
fireAndForgetWorkflowEvent({
|
|
941
|
+
kind: "decision",
|
|
942
|
+
phaseRef: `runs/${ctx.runId}#scoping`,
|
|
943
|
+
sessionId: ctx.sessionId ?? ctx.runId,
|
|
944
|
+
text: `Scoped "${ctx.idea.slice(0, 120)}": ${(productSpec?.architecture ?? "").slice(0, 400)}`,
|
|
945
|
+
payload: {
|
|
946
|
+
persona: productSpec?.persona ?? null,
|
|
947
|
+
mvp: productSpec?.mvp ?? [],
|
|
948
|
+
sprintEstimate: productSpec?.sprintEstimate ?? null,
|
|
949
|
+
},
|
|
950
|
+
});
|
|
951
|
+
// C-v2 — debate + scoping are done and the spec is persisted, so the
|
|
952
|
+
// cross-session resume inputs are obsolete (the checkpoint was already
|
|
953
|
+
// deleted by runDebate on completion). Clean them up so a later
|
|
954
|
+
// `/ideal resume` of this run does not re-enter the debate FSM.
|
|
955
|
+
await deleteDebateInputs(runDir);
|
|
956
|
+
// Hierarchy index — place this run under a milestone (the product idea)
|
|
957
|
+
// and a phase (this scoped iteration). Idempotent across resume; purely
|
|
958
|
+
// an index over runs/, never rewrites run artifacts or ROADMAP phases.
|
|
959
|
+
try {
|
|
960
|
+
const mvpHead = Array.isArray(productSpec?.mvp) && productSpec.mvp.length > 0 ? productSpec.mvp[0] : "";
|
|
961
|
+
await ensureRunScoped(ctx.flowDir, {
|
|
962
|
+
runId: ctx.runId,
|
|
963
|
+
milestoneTitle: clampTitle(ctx.idea, 60),
|
|
964
|
+
milestoneGoal: (productSpec?.architecture ?? "").slice(0, 200),
|
|
965
|
+
phaseTitle: clampTitle(mvpHead || ctx.idea, 50),
|
|
966
|
+
phaseGoal: (productSpec?.persona ?? "").slice(0, 200),
|
|
967
|
+
}, new Date().toISOString());
|
|
968
|
+
}
|
|
969
|
+
catch {
|
|
970
|
+
/* non-critical — hierarchy is an index; failure must not block scoping */
|
|
971
|
+
}
|
|
761
972
|
// P8 - derive tasks.json from the spec (canonical machine-readable
|
|
762
973
|
// surface for downstream /execute consumption). MVP items get
|
|
763
974
|
// sprint=1, phase2 gets sprint=2. Re-deriving the same spec is
|
|
@@ -3,6 +3,11 @@ import type { Migrator } from "./discovery-migrations.js";
|
|
|
3
3
|
import type { LeaderLike } from "./discovery-prompt-parser.js";
|
|
4
4
|
import type { PhasePlanArtifact, ProductRunManifest } from "./types.js";
|
|
5
5
|
export declare const PHASE_PLANNER_SYSTEM: string;
|
|
6
|
+
/**
|
|
7
|
+
* Normalise `id` + `dependsOn` on every phase to the canonical string form.
|
|
8
|
+
* Mutates and returns the same plan object. Safe on already-normalised plans.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizePhasePlan(plan: PhasePlanArtifact): PhasePlanArtifact;
|
|
6
11
|
export declare function parsePhasePlanJson(raw: string): PhasePlanArtifact;
|
|
7
12
|
export declare function validatePhasePlan(plan: PhasePlanArtifact, spec: ClarifiedSpec): void;
|
|
8
13
|
export declare function fallbackSinglePhase(spec: ClarifiedSpec, manifest: ProductRunManifest): PhasePlanArtifact;
|
|
@@ -6,12 +6,47 @@ import { withRateLimitBackoff } from "./discovery-recommender.js";
|
|
|
6
6
|
export const PHASE_PLANNER_SYSTEM = "You decompose a product idea into 3–5 sequential phases. Output strict JSON only. " +
|
|
7
7
|
"Each phase covers a subset of successCriteria verbatim from the input spec. " +
|
|
8
8
|
"Union of all phases.successCriteria MUST equal the input successCriteria array (no drift, no omission).";
|
|
9
|
+
/**
|
|
10
|
+
* Coerce a phase id / dependsOn element to the canonical `phase-N` string form.
|
|
11
|
+
*
|
|
12
|
+
* The phase-planner prompt does not pin `id` to a string, so the LLM frequently
|
|
13
|
+
* emits bare numbers — e.g. `"id": 1, "dependsOn": [1]` instead of
|
|
14
|
+
* `"id": "phase-1", "dependsOn": ["phase-1"]`. Downstream consumers assume the
|
|
15
|
+
* declared `Phase.id: string` / `dependsOn: string[]` contract; the roadmap
|
|
16
|
+
* renderer (`buildRoadmapFromPhasePlan`) calls `dep.match(...)` and crashed with
|
|
17
|
+
* `dep.match is not a function` on a numeric id, aborting the whole /ideal run
|
|
18
|
+
* right after the sprint plan committed — before any implementation ran.
|
|
19
|
+
* Normalising here (the single parse choke-point) restores the contract for
|
|
20
|
+
* every consumer.
|
|
21
|
+
*/
|
|
22
|
+
function normalizePhaseRef(ref) {
|
|
23
|
+
if (typeof ref === "number" && Number.isFinite(ref))
|
|
24
|
+
return `phase-${ref}`;
|
|
25
|
+
const s = String(ref).trim();
|
|
26
|
+
const bareNumber = s.match(/^(\d+)$/);
|
|
27
|
+
if (bareNumber)
|
|
28
|
+
return `phase-${bareNumber[1]}`;
|
|
29
|
+
return s;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Normalise `id` + `dependsOn` on every phase to the canonical string form.
|
|
33
|
+
* Mutates and returns the same plan object. Safe on already-normalised plans.
|
|
34
|
+
*/
|
|
35
|
+
export function normalizePhasePlan(plan) {
|
|
36
|
+
if (Array.isArray(plan?.phases)) {
|
|
37
|
+
for (const phase of plan.phases) {
|
|
38
|
+
phase.id = normalizePhaseRef(phase.id);
|
|
39
|
+
phase.dependsOn = Array.isArray(phase.dependsOn) ? phase.dependsOn.map(normalizePhaseRef) : [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return plan;
|
|
43
|
+
}
|
|
9
44
|
export function parsePhasePlanJson(raw) {
|
|
10
45
|
const stripped = raw
|
|
11
46
|
.replace(/^```(?:json)?\s*/, "")
|
|
12
47
|
.replace(/\s*```$/, "")
|
|
13
48
|
.trim();
|
|
14
|
-
return JSON.parse(stripped);
|
|
49
|
+
return normalizePhasePlan(JSON.parse(stripped));
|
|
15
50
|
}
|
|
16
51
|
export function validatePhasePlan(plan, spec) {
|
|
17
52
|
if (plan.phases.length < 1 || plan.phases.length > 6) {
|
|
@@ -109,7 +144,9 @@ export async function readPhasePlan(flowDir, runId) {
|
|
|
109
144
|
if (!raw)
|
|
110
145
|
return null;
|
|
111
146
|
try {
|
|
112
|
-
|
|
147
|
+
// Normalise on read too: runs persisted before the parse-time normalisation
|
|
148
|
+
// landed (or hand-edited phases.md) can still carry numeric ids.
|
|
149
|
+
return normalizePhasePlan(JSON.parse(raw));
|
|
113
150
|
}
|
|
114
151
|
catch {
|
|
115
152
|
return null;
|