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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pickCouncilTaskModel } from "./leader.js";
|
|
2
|
-
import { tracedGenerate } from "./llm.js";
|
|
2
|
+
import { tracedGenerate, tracedGenerateWithFallback } from "./llm.js";
|
|
3
3
|
import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
|
|
4
4
|
import { buildClarificationPrompt, buildReadinessJudgePrompt, buildSpecSynthesisPrompt } from "./prompts.js";
|
|
5
5
|
/** P5: Hard cap on clarification rounds regardless of judge verdict. */
|
|
@@ -138,6 +138,45 @@ export function buildClarifyOptions(suggestions, recommended) {
|
|
|
138
138
|
}
|
|
139
139
|
return { options, defaultIndex };
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Build council options from the model's richer `{label, description,
|
|
143
|
+
* recommended}` objects, preserving each choice's explanation as `description`
|
|
144
|
+
* and pointing `defaultIndex` at the option the model flagged `recommended`
|
|
145
|
+
* (falling back to the first when none is flagged). The same "Type something" /
|
|
146
|
+
* "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
|
|
147
|
+
* Pure + exported for unit testing.
|
|
148
|
+
*/
|
|
149
|
+
export function buildClarifyOptionsRich(specs) {
|
|
150
|
+
const cleaned = (specs ?? []).filter((s) => !!s && typeof s.label === "string" && s.label.trim().length > 0);
|
|
151
|
+
const choices = cleaned.map((s) => ({
|
|
152
|
+
label: s.label.trim(),
|
|
153
|
+
value: s.label.trim(),
|
|
154
|
+
kind: "choice",
|
|
155
|
+
...(typeof s.description === "string" && s.description.trim().length > 0
|
|
156
|
+
? { description: s.description.trim() }
|
|
157
|
+
: {}),
|
|
158
|
+
}));
|
|
159
|
+
const options = [
|
|
160
|
+
...choices,
|
|
161
|
+
{
|
|
162
|
+
label: "Type something",
|
|
163
|
+
description: "Nhập câu trả lời tự do",
|
|
164
|
+
value: "",
|
|
165
|
+
kind: "freetext",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
label: "Chat about this",
|
|
169
|
+
description: "Thảo luận thêm trước khi trả lời",
|
|
170
|
+
value: "",
|
|
171
|
+
kind: "chat",
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
let defaultIndex;
|
|
175
|
+
const recIdx = cleaned.findIndex((s) => s.recommended === true);
|
|
176
|
+
if (recIdx !== -1)
|
|
177
|
+
defaultIndex = recIdx;
|
|
178
|
+
return { options, defaultIndex };
|
|
179
|
+
}
|
|
141
180
|
export async function* runClarification(topic, leaderModelId, conversationContext, respondToQuestion, llm, signal, seedQuestions, maxRounds,
|
|
142
181
|
/**
|
|
143
182
|
* Optional pre-filled answers keyed by seed-question id. When the loop's
|
|
@@ -152,7 +191,13 @@ prefillAnswers,
|
|
|
152
191
|
* a cheaper tier model on the same provider as the leader. Falls back to
|
|
153
192
|
* the leader model if no cheaper model is cataloged. Default false.
|
|
154
193
|
*/
|
|
155
|
-
costAware = false
|
|
194
|
+
costAware = false,
|
|
195
|
+
/**
|
|
196
|
+
* Healthy panel models to fall back to when the leader's cost-tier spec-synth
|
|
197
|
+
* model is on a failing proxy — keeps the spec's ≥3 observable criteria (and
|
|
198
|
+
* thus a meaningful leader eval) instead of degrading to one generic criterion.
|
|
199
|
+
*/
|
|
200
|
+
fallbackModels = []) {
|
|
156
201
|
// P5: use MAX_CLARIFY_ROUNDS (12) as the hard cap; respect explicit override
|
|
157
202
|
// from callers that pass maxRounds (e.g. tests that want old 3-round behavior).
|
|
158
203
|
const max = typeof maxRounds === "number" && maxRounds > 0 ? maxRounds : MAX_CLARIFY_ROUNDS;
|
|
@@ -284,7 +329,16 @@ costAware = false) {
|
|
|
284
329
|
continue;
|
|
285
330
|
}
|
|
286
331
|
const questionId = crypto.randomUUID();
|
|
287
|
-
|
|
332
|
+
// Prefer the model's richer {label, description, recommended} options so
|
|
333
|
+
// every choice shows its own explanation + the recommended default is
|
|
334
|
+
// pre-selected. Fall back to the legacy string `suggestions` shape (PIL
|
|
335
|
+
// gray-area seeds and any model still emitting the old format).
|
|
336
|
+
const { options, defaultIndex } = q.options && q.options.length > 0
|
|
337
|
+
? buildClarifyOptionsRich(q.options)
|
|
338
|
+
: buildClarifyOptions(q.suggestions, q.recommended);
|
|
339
|
+
// Keep the deprecated `suggestions` mirror populated for consumers that
|
|
340
|
+
// still read it (audit/replay), deriving labels from rich options.
|
|
341
|
+
const suggestionLabels = q.suggestions ?? (q.options ? q.options.map((o) => o.label).filter((l) => typeof l === "string") : undefined);
|
|
288
342
|
yield {
|
|
289
343
|
type: "council_question",
|
|
290
344
|
content: `**${q.question}**\n${q.why ? `> ${q.why}` : ""}`,
|
|
@@ -293,7 +347,7 @@ costAware = false) {
|
|
|
293
347
|
phase: "clarify",
|
|
294
348
|
question: q.question,
|
|
295
349
|
context: q.why,
|
|
296
|
-
suggestions:
|
|
350
|
+
suggestions: suggestionLabels,
|
|
297
351
|
options,
|
|
298
352
|
isRequired: q.isRequired,
|
|
299
353
|
defaultIndex,
|
|
@@ -329,7 +383,7 @@ costAware = false) {
|
|
|
329
383
|
startedAt: phaseStartedAt,
|
|
330
384
|
detail: allQA.length > 0 ? `${allQA.length} Q&A` : "no clarification needed",
|
|
331
385
|
});
|
|
332
|
-
const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware);
|
|
386
|
+
const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware, fallbackModels);
|
|
333
387
|
// P5: attach ready-gate metadata to the returned spec
|
|
334
388
|
spec.confidenceScore = gateConfidence;
|
|
335
389
|
spec.remainingGaps = gateGaps;
|
|
@@ -355,13 +409,18 @@ costAware = false) {
|
|
|
355
409
|
};
|
|
356
410
|
return spec;
|
|
357
411
|
}
|
|
358
|
-
export function buildSpecFromTopic(topic,
|
|
412
|
+
export function buildSpecFromTopic(topic, conversationContext) {
|
|
359
413
|
return {
|
|
360
414
|
problemStatement: topic,
|
|
361
415
|
constraints: [],
|
|
362
416
|
successCriteria: [`Address the topic: ${topic.slice(0, 100)}`],
|
|
363
417
|
scope: "Determined by conversation context",
|
|
364
418
|
rawQA: [],
|
|
419
|
+
// Carry the session/task context onto the spec so every debate stage (not
|
|
420
|
+
// just the opening) can stay anchored to the parent task. Previously this
|
|
421
|
+
// argument was dropped (prefixed `_`), which is why auto-council debates saw
|
|
422
|
+
// only the isolated sub-task and drifted off-topic.
|
|
423
|
+
parentContext: conversationContext?.trim() || undefined,
|
|
365
424
|
};
|
|
366
425
|
}
|
|
367
426
|
/**
|
|
@@ -374,7 +433,7 @@ export function buildSpecFromTopic(topic, _conversationContext) {
|
|
|
374
433
|
* We LLM-extract criteria/constraints/scope from the topic when QA is empty.
|
|
375
434
|
* Falls back to the original buildSpecFromTopic if the LLM call fails.
|
|
376
435
|
*/
|
|
377
|
-
async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware) {
|
|
436
|
+
async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels = []) {
|
|
378
437
|
const system = `You are extracting an implicit specification from a short topic statement. ` +
|
|
379
438
|
`The user did not answer clarification questions, but the topic itself often ` +
|
|
380
439
|
`implies scope, criteria, and constraints. Tease them out.\n\n` +
|
|
@@ -396,16 +455,21 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
|
|
|
396
455
|
`- Write all fields in the user's language (detected from the topic).`;
|
|
397
456
|
const prompt = `## Topic\n${topic}\n\n${conversationContext ? `## Context\n${conversationContext}\n` : ""}`;
|
|
398
457
|
try {
|
|
458
|
+
// Model-fallback: the cost-tier synth model can sit on a flaky proxy
|
|
459
|
+
// (Console Go glm/kimi → "Upstream request failed"). Falling back to
|
|
460
|
+
// buildSpecFromTopic yields a single generic criterion, which makes the
|
|
461
|
+
// leader eval read a vague "1/1 criteria met". Retry on healthy panel models
|
|
462
|
+
// first so the spec keeps its ≥3 observable criteria.
|
|
399
463
|
const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
|
|
400
|
-
const raw = yield*
|
|
464
|
+
const raw = yield* tracedGenerateWithFallback(llm, {
|
|
401
465
|
phase: "synthesis",
|
|
402
466
|
label: "Inferring spec from topic (no clarification answers)",
|
|
403
|
-
|
|
467
|
+
models: [synthModel, ...fallbackModels],
|
|
404
468
|
system,
|
|
405
469
|
prompt,
|
|
406
470
|
maxTokens: 1024,
|
|
407
471
|
});
|
|
408
|
-
const match = raw
|
|
472
|
+
const match = raw?.match(/\{[\s\S]*\}/);
|
|
409
473
|
if (match) {
|
|
410
474
|
const parsed = JSON.parse(match[0]);
|
|
411
475
|
const criteria = Array.isArray(parsed.successCriteria) && parsed.successCriteria.length >= 1
|
|
@@ -429,12 +493,12 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
|
|
|
429
493
|
}
|
|
430
494
|
return buildSpecFromTopic(topic, conversationContext);
|
|
431
495
|
}
|
|
432
|
-
async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false) {
|
|
496
|
+
async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false, fallbackModels = []) {
|
|
433
497
|
if (qa.length === 0) {
|
|
434
498
|
// Clarifier asked 0 questions OR user skipped all of them. Use the LLM to
|
|
435
499
|
// pull implicit criteria/constraints/scope from the topic alone instead of
|
|
436
500
|
// returning the single-criterion fallback that makes leader-eval trivial.
|
|
437
|
-
return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware);
|
|
501
|
+
return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels);
|
|
438
502
|
}
|
|
439
503
|
const { system, prompt } = buildSpecSynthesisPrompt(topic, conversationContext, qa);
|
|
440
504
|
const resolved = {};
|
|
@@ -444,16 +508,18 @@ async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, ll
|
|
|
444
508
|
}
|
|
445
509
|
}
|
|
446
510
|
try {
|
|
511
|
+
// Model-fallback (see inferSpecFromTopicOnly): keep the ≥3-criteria spec even
|
|
512
|
+
// when the leader's cost-tier synth model is on a failing proxy.
|
|
447
513
|
const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
|
|
448
|
-
const raw = yield*
|
|
514
|
+
const raw = yield* tracedGenerateWithFallback(llm, {
|
|
449
515
|
phase: "synthesis",
|
|
450
516
|
label: "Synthesizing clarified spec",
|
|
451
|
-
|
|
517
|
+
models: [synthModel, ...fallbackModels],
|
|
452
518
|
system,
|
|
453
519
|
prompt,
|
|
454
520
|
maxTokens: 2048,
|
|
455
521
|
});
|
|
456
|
-
const match = raw
|
|
522
|
+
const match = raw?.match(/\{[\s\S]*\}/);
|
|
457
523
|
if (match) {
|
|
458
524
|
const parsed = JSON.parse(match[0]);
|
|
459
525
|
return {
|
|
@@ -98,7 +98,7 @@ function getCompactionText(msg) {
|
|
|
98
98
|
return null;
|
|
99
99
|
return msg.content;
|
|
100
100
|
}
|
|
101
|
-
function
|
|
101
|
+
function extractText(content) {
|
|
102
102
|
if (typeof content === "string")
|
|
103
103
|
return content;
|
|
104
104
|
if (Array.isArray(content)) {
|
|
@@ -109,40 +109,74 @@ function extractUserText(content) {
|
|
|
109
109
|
}
|
|
110
110
|
return "";
|
|
111
111
|
}
|
|
112
|
+
/** Filter assistant messages to exclude tool-call-only turns (tool_use + tool_result
|
|
113
|
+
* pairs that carry no analytical text). An assistant turn with ≥80 chars of text
|
|
114
|
+
* content is considered "analytical" (contains decisions, analysis, code, or plans). */
|
|
115
|
+
function isAnalyticalAssistant(content) {
|
|
116
|
+
const text = extractText(content);
|
|
117
|
+
return text.trim().length >= 80;
|
|
118
|
+
}
|
|
119
|
+
/** Maximum total characters for the built context string. Raised from 12K to 32K
|
|
120
|
+
* (~8K tokens) so council participants get meaningful session history rather than
|
|
121
|
+
* only 5 user-message snippets. */
|
|
122
|
+
const MAX_CONTEXT_CHARS = 32_000;
|
|
112
123
|
export function buildCouncilContext(messages) {
|
|
113
124
|
const parts = [];
|
|
125
|
+
// 1. Compaction summary (first message if it's a system compaction)
|
|
114
126
|
if (messages.length > 0 && isCompactionSummary(messages[0])) {
|
|
115
127
|
const summary = getCompactionText(messages[0]);
|
|
116
128
|
if (summary) {
|
|
117
129
|
parts.push(`## Session Context (from compaction summary)\n${summary}`);
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
// 2. Recent conversation turns — scan BOTH user + analytical assistant messages
|
|
133
|
+
// from the tail, capturing at most 8 meaningful entries.
|
|
134
|
+
// Previously only captured 5 user messages — lost ALL assistant analysis,
|
|
135
|
+
// code changes, and decisions made during the session.
|
|
136
|
+
const conversationTurns = [];
|
|
137
|
+
for (let i = messages.length - 1; i >= 0 && conversationTurns.length < 8; i--) {
|
|
122
138
|
const msg = messages[i];
|
|
123
139
|
if (msg.role === "user") {
|
|
124
|
-
const text =
|
|
140
|
+
const text = extractText(msg.content);
|
|
141
|
+
if (text.trim()) {
|
|
142
|
+
conversationTurns.unshift(`[User] ${text.slice(0, 3000).trim()}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (msg.role === "assistant" && isAnalyticalAssistant(msg.content)) {
|
|
146
|
+
const text = extractText(msg.content);
|
|
125
147
|
if (text.trim()) {
|
|
126
|
-
|
|
148
|
+
// Cap assistant text to 1500 chars — enough to convey analysis/decisions
|
|
149
|
+
// without blowing the context budget on long code outputs.
|
|
150
|
+
conversationTurns.unshift(`[Assistant] ${text.slice(0, 1500).trim()}`);
|
|
127
151
|
}
|
|
128
152
|
}
|
|
129
153
|
}
|
|
130
|
-
if (
|
|
131
|
-
parts.push(`## Recent
|
|
154
|
+
if (conversationTurns.length > 0) {
|
|
155
|
+
parts.push(`## Recent Conversation\n${conversationTurns.join("\n\n")}`);
|
|
132
156
|
}
|
|
133
|
-
|
|
157
|
+
// 3. Key decisions from system messages (task outcomes, decisions, plans)
|
|
158
|
+
const decisionMsgs = [];
|
|
134
159
|
for (const msg of messages) {
|
|
135
|
-
if (msg.role
|
|
136
|
-
|
|
160
|
+
if (msg.role !== "system" || typeof msg.content !== "string")
|
|
161
|
+
continue;
|
|
162
|
+
const c = msg.content;
|
|
163
|
+
if (c.includes("[Council Memory]") ||
|
|
164
|
+
c.includes("[Council Decision]") ||
|
|
165
|
+
c.includes("[Council Action Items]") ||
|
|
166
|
+
c.includes("[Council Plan Update]") ||
|
|
167
|
+
c.includes("[Decision]") ||
|
|
168
|
+
c.includes("[Task Completed]") ||
|
|
169
|
+
c.includes("[NEEDS HUMAN REVIEW]")) {
|
|
170
|
+
decisionMsgs.push(c);
|
|
137
171
|
}
|
|
138
172
|
}
|
|
139
|
-
if (
|
|
140
|
-
const digests =
|
|
141
|
-
parts.push(`##
|
|
173
|
+
if (decisionMsgs.length > 0) {
|
|
174
|
+
const digests = decisionMsgs.slice(-4).map(formatCouncilMemoryDigest);
|
|
175
|
+
parts.push(`## Key Decisions\n${digests.join("\n\n")}`);
|
|
142
176
|
}
|
|
143
177
|
const combined = parts.join("\n\n---\n\n");
|
|
144
|
-
if (combined.length >
|
|
145
|
-
return `${combined.slice(0,
|
|
178
|
+
if (combined.length > MAX_CONTEXT_CHARS) {
|
|
179
|
+
return `${combined.slice(0, MAX_CONTEXT_CHARS)}\n\n[... context truncated to fit token budget]`;
|
|
146
180
|
}
|
|
147
181
|
return combined;
|
|
148
182
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/council/debate-checkpoint.ts
|
|
3
|
+
*
|
|
4
|
+
* C (mid-debate checkpoint) — persist the CB-1 council debate's per-round state
|
|
5
|
+
* so a break mid-debate (provider 5xx on round 5, host crash, cap breach) can
|
|
6
|
+
* resume from the last completed round instead of re-running the whole debate.
|
|
7
|
+
*
|
|
8
|
+
* The debate accumulates all state in RAM (`exchangeLogs`, `active`,
|
|
9
|
+
* `runningSummary`, `archive`, convergence trackers). Without a checkpoint a
|
|
10
|
+
* break loses every round — a 5-round multi-provider debate must restart from
|
|
11
|
+
* round 1. This module owns the on-disk snapshot: `runDebate` writes it after
|
|
12
|
+
* each completed round and deletes it on normal completion; the resume path
|
|
13
|
+
* reads it and seeds `runDebate` so it skips research + openings + completed
|
|
14
|
+
* rounds.
|
|
15
|
+
*
|
|
16
|
+
* File: `<checkpointDir>/debate-checkpoint.json` (checkpointDir = the run dir,
|
|
17
|
+
* `.muonroi-flow/runs/<runId>`). Written atomically (tmp + rename) so a crash
|
|
18
|
+
* mid-write can never leave a half-JSON file.
|
|
19
|
+
*/
|
|
20
|
+
import type { ClarifiedSpec, CouncilParticipant, DebateArchiveEntry } from "./types.js";
|
|
21
|
+
/** Bump when the checkpoint shape changes incompatibly so stale files are ignored. */
|
|
22
|
+
export declare const DEBATE_CHECKPOINT_VERSION: 1;
|
|
23
|
+
export declare const DEBATE_CHECKPOINT_FILE = "debate-checkpoint.json";
|
|
24
|
+
/**
|
|
25
|
+
* Serializable snapshot of an in-flight debate, written after each completed
|
|
26
|
+
* discussion round. `exchangeLogs` (a `Map`) is stored as entry tuples since
|
|
27
|
+
* JSON has no Map; everything else in the payload is already JSON-native.
|
|
28
|
+
*/
|
|
29
|
+
export interface DebateCheckpoint {
|
|
30
|
+
version: typeof DEBATE_CHECKPOINT_VERSION;
|
|
31
|
+
/**
|
|
32
|
+
* Identity guard — the checkpoint is only restored when it matches the debate
|
|
33
|
+
* about to run. `problemStatement` pins the spec; `participantModels` pins the
|
|
34
|
+
* panel. A mismatch (spec changed, panel re-resolved differently) => ignore
|
|
35
|
+
* the stale checkpoint and run the debate fresh rather than splice wrong state.
|
|
36
|
+
*/
|
|
37
|
+
problemStatement: string;
|
|
38
|
+
participantModels: string[];
|
|
39
|
+
/** Rounds fully completed (transcript + summary persisted). Resume starts at roundCount+1. */
|
|
40
|
+
roundCount: number;
|
|
41
|
+
/** Current round budget after any leader/user extensions. */
|
|
42
|
+
maxRounds: number;
|
|
43
|
+
/** Accumulated per-pair transcript (Map<pairKey, turns[]> serialized as entries). */
|
|
44
|
+
exchangeLogs: Array<[string, string[]]>;
|
|
45
|
+
/** Latest inter-round summary. */
|
|
46
|
+
runningSummary: string;
|
|
47
|
+
/** Research findings (so the research phase is not re-run on resume). */
|
|
48
|
+
researchFindings?: string;
|
|
49
|
+
/** Surviving participants with their latest position + stance (so openings are not re-run). */
|
|
50
|
+
active: CouncilParticipant[];
|
|
51
|
+
/** Citation archive accumulated so far. */
|
|
52
|
+
archive: DebateArchiveEntry[];
|
|
53
|
+
/** Convergence continuity: last round's per-criterion met flags. */
|
|
54
|
+
lastCriteriaMet: boolean[];
|
|
55
|
+
/** B4 auto-remedy high-water mark of pinned criteria met. */
|
|
56
|
+
bestCriteriaMetCount: number;
|
|
57
|
+
/** B4 consecutive evaluated rounds with no new met criterion. */
|
|
58
|
+
roundsSinceProgress: number;
|
|
59
|
+
/** Topic carried into the next round from the prior leader nextRoundFocus. */
|
|
60
|
+
nextTopic?: string;
|
|
61
|
+
/** Wall-clock the checkpoint was written (ISO). Diagnostics only. */
|
|
62
|
+
savedAt: string;
|
|
63
|
+
}
|
|
64
|
+
/** Input to {@link buildDebateCheckpoint} — the live round-loop locals. */
|
|
65
|
+
export interface DebateCheckpointInput {
|
|
66
|
+
problemStatement: string;
|
|
67
|
+
roundCount: number;
|
|
68
|
+
maxRounds: number;
|
|
69
|
+
exchangeLogs: Map<string, string[]>;
|
|
70
|
+
runningSummary: string;
|
|
71
|
+
researchFindings?: string;
|
|
72
|
+
active: CouncilParticipant[];
|
|
73
|
+
archive: DebateArchiveEntry[];
|
|
74
|
+
lastCriteriaMet: boolean[];
|
|
75
|
+
bestCriteriaMetCount: number;
|
|
76
|
+
roundsSinceProgress: number;
|
|
77
|
+
nextTopic?: string;
|
|
78
|
+
/** ISO timestamp; the caller supplies it (Date.now is unavailable in some sandboxes). */
|
|
79
|
+
savedAt: string;
|
|
80
|
+
}
|
|
81
|
+
/** Build the serializable checkpoint payload from live debate state. */
|
|
82
|
+
export declare function buildDebateCheckpoint(input: DebateCheckpointInput): DebateCheckpoint;
|
|
83
|
+
/** Rehydrate `exchangeLogs` from the serialized entry tuples. */
|
|
84
|
+
export declare function restoreExchangeLogs(cp: DebateCheckpoint): Map<string, string[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Whether `cp` describes the debate that is about to run. Guards against
|
|
87
|
+
* splicing a stale checkpoint (different question, re-resolved panel) into a
|
|
88
|
+
* fresh debate. Participant order is not significant; the model SET must match.
|
|
89
|
+
*/
|
|
90
|
+
export declare function checkpointMatches(cp: DebateCheckpoint, problemStatement: string, participantModels: string[]): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Atomically write the checkpoint to `<dir>/debate-checkpoint.json`.
|
|
93
|
+
* Non-fatal: a checkpoint write failure must never break the live debate — it
|
|
94
|
+
* only forfeits resumability for that round. Logs with context (No-Silent-Catch).
|
|
95
|
+
*/
|
|
96
|
+
export declare function writeDebateCheckpoint(dir: string, cp: DebateCheckpoint): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Read + parse the checkpoint. Returns null when absent or unparseable (a
|
|
99
|
+
* corrupt checkpoint must not crash resume — the debate just runs fresh).
|
|
100
|
+
*/
|
|
101
|
+
export declare function readDebateCheckpoint(dir: string): Promise<DebateCheckpoint | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Delete the checkpoint (debate completed normally, or the run was aborted).
|
|
104
|
+
* Non-fatal — a leftover checkpoint is only re-validated (and discarded on
|
|
105
|
+
* mismatch) on the next resume, never silently mis-applied.
|
|
106
|
+
*/
|
|
107
|
+
export declare function deleteDebateCheckpoint(dir: string): Promise<void>;
|
|
108
|
+
export declare const DEBATE_INPUTS_FILE = "debate-inputs.json";
|
|
109
|
+
/**
|
|
110
|
+
* C-v2 — the minimal gather-stage outputs needed to re-enter the council debate
|
|
111
|
+
* on a fresh session (cross-process resume) WITHOUT re-running the interactive
|
|
112
|
+
* discovery + interview. Persisted once at debate start; the loop-driver resume
|
|
113
|
+
* entry restores `clarifiedSpec` + `conversationContext` and jumps straight to
|
|
114
|
+
* the research/debate stage (participants + leader are re-resolved from the
|
|
115
|
+
* session model deterministically, then matched against the checkpoint panel).
|
|
116
|
+
*/
|
|
117
|
+
export interface DebateInputs {
|
|
118
|
+
version: typeof DEBATE_CHECKPOINT_VERSION;
|
|
119
|
+
problemStatement: string;
|
|
120
|
+
clarifiedSpec: ClarifiedSpec;
|
|
121
|
+
conversationContext: string;
|
|
122
|
+
savedAt: string;
|
|
123
|
+
}
|
|
124
|
+
/** Atomically persist the debate inputs. Non-fatal on failure (forfeits cross-session resume only). */
|
|
125
|
+
export declare function writeDebateInputs(dir: string, inputs: DebateInputs): Promise<void>;
|
|
126
|
+
/** Read the debate inputs. Returns null when absent/unparseable/version-mismatched. */
|
|
127
|
+
export declare function readDebateInputs(dir: string): Promise<DebateInputs | null>;
|
|
128
|
+
/** Delete the debate inputs (debate + scoping finished, or run aborted). Non-fatal. */
|
|
129
|
+
export declare function deleteDebateInputs(dir: string): Promise<void>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/council/debate-checkpoint.ts
|
|
3
|
+
*
|
|
4
|
+
* C (mid-debate checkpoint) — persist the CB-1 council debate's per-round state
|
|
5
|
+
* so a break mid-debate (provider 5xx on round 5, host crash, cap breach) can
|
|
6
|
+
* resume from the last completed round instead of re-running the whole debate.
|
|
7
|
+
*
|
|
8
|
+
* The debate accumulates all state in RAM (`exchangeLogs`, `active`,
|
|
9
|
+
* `runningSummary`, `archive`, convergence trackers). Without a checkpoint a
|
|
10
|
+
* break loses every round — a 5-round multi-provider debate must restart from
|
|
11
|
+
* round 1. This module owns the on-disk snapshot: `runDebate` writes it after
|
|
12
|
+
* each completed round and deletes it on normal completion; the resume path
|
|
13
|
+
* reads it and seeds `runDebate` so it skips research + openings + completed
|
|
14
|
+
* rounds.
|
|
15
|
+
*
|
|
16
|
+
* File: `<checkpointDir>/debate-checkpoint.json` (checkpointDir = the run dir,
|
|
17
|
+
* `.muonroi-flow/runs/<runId>`). Written atomically (tmp + rename) so a crash
|
|
18
|
+
* mid-write can never leave a half-JSON file.
|
|
19
|
+
*/
|
|
20
|
+
import { promises as fs } from "node:fs";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
/** Bump when the checkpoint shape changes incompatibly so stale files are ignored. */
|
|
23
|
+
export const DEBATE_CHECKPOINT_VERSION = 1;
|
|
24
|
+
export const DEBATE_CHECKPOINT_FILE = "debate-checkpoint.json";
|
|
25
|
+
/** Build the serializable checkpoint payload from live debate state. */
|
|
26
|
+
export function buildDebateCheckpoint(input) {
|
|
27
|
+
return {
|
|
28
|
+
version: DEBATE_CHECKPOINT_VERSION,
|
|
29
|
+
problemStatement: input.problemStatement,
|
|
30
|
+
participantModels: input.active.map((p) => p.model),
|
|
31
|
+
roundCount: input.roundCount,
|
|
32
|
+
maxRounds: input.maxRounds,
|
|
33
|
+
exchangeLogs: [...input.exchangeLogs.entries()].map(([k, v]) => [k, [...v]]),
|
|
34
|
+
runningSummary: input.runningSummary,
|
|
35
|
+
researchFindings: input.researchFindings,
|
|
36
|
+
active: input.active,
|
|
37
|
+
archive: input.archive,
|
|
38
|
+
lastCriteriaMet: [...input.lastCriteriaMet],
|
|
39
|
+
bestCriteriaMetCount: input.bestCriteriaMetCount,
|
|
40
|
+
roundsSinceProgress: input.roundsSinceProgress,
|
|
41
|
+
nextTopic: input.nextTopic,
|
|
42
|
+
savedAt: input.savedAt,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Rehydrate `exchangeLogs` from the serialized entry tuples. */
|
|
46
|
+
export function restoreExchangeLogs(cp) {
|
|
47
|
+
return new Map(cp.exchangeLogs.map(([k, v]) => [k, [...v]]));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether `cp` describes the debate that is about to run. Guards against
|
|
51
|
+
* splicing a stale checkpoint (different question, re-resolved panel) into a
|
|
52
|
+
* fresh debate. Participant order is not significant; the model SET must match.
|
|
53
|
+
*/
|
|
54
|
+
export function checkpointMatches(cp, problemStatement, participantModels) {
|
|
55
|
+
if (cp.version !== DEBATE_CHECKPOINT_VERSION)
|
|
56
|
+
return false;
|
|
57
|
+
if (cp.roundCount < 1)
|
|
58
|
+
return false; // nothing useful to resume from
|
|
59
|
+
if (cp.problemStatement !== problemStatement)
|
|
60
|
+
return false;
|
|
61
|
+
const a = [...cp.participantModels].sort();
|
|
62
|
+
const b = [...participantModels].sort();
|
|
63
|
+
if (a.length !== b.length)
|
|
64
|
+
return false;
|
|
65
|
+
return a.every((m, i) => m === b[i]);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Atomically write the checkpoint to `<dir>/debate-checkpoint.json`.
|
|
69
|
+
* Non-fatal: a checkpoint write failure must never break the live debate — it
|
|
70
|
+
* only forfeits resumability for that round. Logs with context (No-Silent-Catch).
|
|
71
|
+
*/
|
|
72
|
+
export async function writeDebateCheckpoint(dir, cp) {
|
|
73
|
+
const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
|
|
74
|
+
const tmpPath = `${finalPath}.${process.pid}.tmp`;
|
|
75
|
+
try {
|
|
76
|
+
await fs.mkdir(dir, { recursive: true });
|
|
77
|
+
await fs.writeFile(tmpPath, JSON.stringify(cp), "utf8");
|
|
78
|
+
await fs.rename(tmpPath, finalPath);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
console.error(`[debate-checkpoint] write failed (dir=${dir}, round=${cp.roundCount}): ${err?.message}`);
|
|
82
|
+
// Best-effort cleanup of the temp file; ignore if it never got created.
|
|
83
|
+
await fs.rm(tmpPath, { force: true }).catch(() => { });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Read + parse the checkpoint. Returns null when absent or unparseable (a
|
|
88
|
+
* corrupt checkpoint must not crash resume — the debate just runs fresh).
|
|
89
|
+
*/
|
|
90
|
+
export async function readDebateCheckpoint(dir) {
|
|
91
|
+
const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
raw = await fs.readFile(finalPath, "utf8");
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err?.code !== "ENOENT") {
|
|
98
|
+
console.error(`[debate-checkpoint] read failed (dir=${dir}): ${err?.message}`);
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(raw);
|
|
104
|
+
if (parsed?.version !== DEBATE_CHECKPOINT_VERSION)
|
|
105
|
+
return null;
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error(`[debate-checkpoint] parse failed (dir=${dir}): ${err?.message}`);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Delete the checkpoint (debate completed normally, or the run was aborted).
|
|
115
|
+
* Non-fatal — a leftover checkpoint is only re-validated (and discarded on
|
|
116
|
+
* mismatch) on the next resume, never silently mis-applied.
|
|
117
|
+
*/
|
|
118
|
+
export async function deleteDebateCheckpoint(dir) {
|
|
119
|
+
const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
|
|
120
|
+
try {
|
|
121
|
+
await fs.rm(finalPath, { force: true });
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error(`[debate-checkpoint] delete failed (dir=${dir}): ${err?.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ── Debate inputs (C-v2 cross-session resume) ────────────────────────────────
|
|
128
|
+
export const DEBATE_INPUTS_FILE = "debate-inputs.json";
|
|
129
|
+
/** Atomically persist the debate inputs. Non-fatal on failure (forfeits cross-session resume only). */
|
|
130
|
+
export async function writeDebateInputs(dir, inputs) {
|
|
131
|
+
const finalPath = path.join(dir, DEBATE_INPUTS_FILE);
|
|
132
|
+
const tmpPath = `${finalPath}.${process.pid}.tmp`;
|
|
133
|
+
try {
|
|
134
|
+
await fs.mkdir(dir, { recursive: true });
|
|
135
|
+
await fs.writeFile(tmpPath, JSON.stringify(inputs), "utf8");
|
|
136
|
+
await fs.rename(tmpPath, finalPath);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error(`[debate-checkpoint] inputs write failed (dir=${dir}): ${err?.message}`);
|
|
140
|
+
await fs.rm(tmpPath, { force: true }).catch(() => { });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Read the debate inputs. Returns null when absent/unparseable/version-mismatched. */
|
|
144
|
+
export async function readDebateInputs(dir) {
|
|
145
|
+
const finalPath = path.join(dir, DEBATE_INPUTS_FILE);
|
|
146
|
+
let raw;
|
|
147
|
+
try {
|
|
148
|
+
raw = await fs.readFile(finalPath, "utf8");
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (err?.code !== "ENOENT") {
|
|
152
|
+
console.error(`[debate-checkpoint] inputs read failed (dir=${dir}): ${err?.message}`);
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(raw);
|
|
158
|
+
if (parsed?.version !== DEBATE_CHECKPOINT_VERSION)
|
|
159
|
+
return null;
|
|
160
|
+
return parsed;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
console.error(`[debate-checkpoint] inputs parse failed (dir=${dir}): ${err?.message}`);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** Delete the debate inputs (debate + scoping finished, or run aborted). Non-fatal. */
|
|
168
|
+
export async function deleteDebateInputs(dir) {
|
|
169
|
+
try {
|
|
170
|
+
await fs.rm(path.join(dir, DEBATE_INPUTS_FILE), { force: true });
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.error(`[debate-checkpoint] inputs delete failed (dir=${dir}): ${err?.message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=debate-checkpoint.js.map
|