muonroi-cli 1.8.4 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +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-filter.js +11 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
- package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
- 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 +38 -0
- package/dist/src/agent-harness/mock-model.js +69 -3
- package/dist/src/agent-harness/test-spawn.js +31 -0
- package/dist/src/chat/chat-keychain.d.ts +7 -12
- package/dist/src/chat/chat-keychain.js +19 -86
- 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 +10 -47
- package/dist/src/cli/keys.js +31 -399
- package/dist/src/council/clarifier.d.ts +31 -3
- package/dist/src/council/clarifier.js +220 -32
- 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 +54 -5
- 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 +1265 -135
- package/dist/src/council/index.d.ts +108 -1
- package/dist/src/council/index.js +670 -197
- package/dist/src/council/leader.d.ts +26 -0
- package/dist/src/council/leader.js +150 -9
- package/dist/src/council/llm.d.ts +94 -0
- package/dist/src/council/llm.js +348 -55
- package/dist/src/council/panel-select.d.ts +30 -0
- package/dist/src/council/panel-select.js +82 -0
- package/dist/src/council/planner.js +40 -0
- package/dist/src/council/preflight.d.ts +17 -0
- package/dist/src/council/preflight.js +50 -2
- package/dist/src/council/prompts.d.ts +39 -4
- package/dist/src/council/prompts.js +256 -69
- 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 +138 -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 +20 -0
- package/dist/src/ee/auth.js +54 -2
- package/dist/src/ee/bridge.d.ts +10 -0
- package/dist/src/ee/bridge.js +58 -0
- package/dist/src/ee/client.js +109 -21
- package/dist/src/ee/ee-onboarding.js +6 -26
- 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 +58 -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 +14 -1
- package/dist/src/flow/compaction/index.js +96 -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/compaction/progress.d.ts +35 -0
- package/dist/src/flow/compaction/progress.js +35 -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 +66 -0
- package/dist/src/gsd/flags.js +102 -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 +283 -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/headless/council-answers.js +4 -0
- package/dist/src/hooks/index.js +1 -1
- package/dist/src/index.js +172 -270
- package/dist/src/lsp/builtins.js +3 -1
- package/dist/src/lsp/manager.d.ts +5 -1
- package/dist/src/lsp/manager.js +249 -3
- package/dist/src/lsp/npm-cache.d.ts +11 -1
- package/dist/src/lsp/npm-cache.js +17 -1
- package/dist/src/lsp/runtime.d.ts +6 -1
- package/dist/src/lsp/runtime.js +17 -1
- package/dist/src/lsp/types.d.ts +83 -1
- package/dist/src/lsp/types.js +10 -0
- 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 +44 -16
- package/dist/src/mcp/lsp-tools.d.ts +5 -1
- package/dist/src/mcp/lsp-tools.js +93 -2
- package/dist/src/mcp/mcp-keychain.d.ts +3 -5
- package/dist/src/mcp/mcp-keychain.js +9 -49
- 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 +22 -2
- package/dist/src/mcp/tools-server.d.ts +10 -0
- package/dist/src/mcp/tools-server.js +10 -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/ask-user.d.ts +61 -0
- package/dist/src/orchestrator/ask-user.js +65 -0
- 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 +12 -3
- package/dist/src/orchestrator/compaction.js +35 -15
- package/dist/src/orchestrator/council-manager.d.ts +12 -3
- package/dist/src/orchestrator/council-manager.js +74 -32
- package/dist/src/orchestrator/council-request.d.ts +49 -0
- package/dist/src/orchestrator/council-request.js +62 -0
- 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/interactive-pause.d.ts +26 -0
- package/dist/src/orchestrator/interactive-pause.js +36 -0
- package/dist/src/orchestrator/message-processor.d.ts +4 -0
- package/dist/src/orchestrator/message-processor.js +268 -41
- package/dist/src/orchestrator/orchestrator.d.ts +64 -3
- package/dist/src/orchestrator/orchestrator.js +823 -120
- package/dist/src/orchestrator/preprocessor.js +3 -3
- 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-askcard.d.ts +1 -1
- package/dist/src/orchestrator/safety-askcard.js +5 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
- package/dist/src/orchestrator/safety-intercept.js +62 -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 +31 -3
- package/dist/src/orchestrator/stall-watchdog.js +65 -10
- package/dist/src/orchestrator/stream-runner.d.ts +13 -3
- package/dist/src/orchestrator/stream-runner.js +115 -49
- 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 +41 -0
- package/dist/src/orchestrator/tool-engine.js +846 -66
- 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 +44 -0
- package/dist/src/orchestrator/turn-watchdog.js +84 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +6 -4
- 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.d.ts +1 -1
- package/dist/src/pil/discovery.js +18 -13
- 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/layer1_5-complexity-size.d.ts +7 -0
- package/dist/src/pil/layer1_5-complexity-size.js +31 -5
- 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 +111 -5
- package/dist/src/pil/llm-classify.js +421 -189
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +8 -0
- package/dist/src/pil/pipeline.js +36 -2
- package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
- package/dist/src/pil/repo-grounding-probe.js +136 -0
- package/dist/src/pil/repo-structure-hints.d.ts +7 -0
- package/dist/src/pil/repo-structure-hints.js +45 -0
- 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/artifact-io.js +4 -0
- 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/criteria-seed.d.ts +51 -0
- package/dist/src/product-loop/criteria-seed.js +200 -0
- 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.d.ts +9 -0
- package/dist/src/product-loop/discovery-interview.js +60 -12
- package/dist/src/product-loop/discovery-recommender.js +2 -1
- package/dist/src/product-loop/discovery-schema.js +19 -2
- package/dist/src/product-loop/discovery-triage.d.ts +23 -0
- package/dist/src/product-loop/discovery-triage.js +109 -0
- package/dist/src/product-loop/gather.js +150 -2
- 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 +340 -52
- package/dist/src/product-loop/loop-driver.d.ts +7 -0
- package/dist/src/product-loop/loop-driver.js +330 -106
- package/dist/src/product-loop/phase-plan.d.ts +21 -0
- package/dist/src/product-loop/phase-plan.js +81 -6
- package/dist/src/product-loop/phase-rituals.d.ts +3 -0
- package/dist/src/product-loop/phase-rituals.js +8 -3
- package/dist/src/product-loop/phase-runner.js +39 -12
- package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
- package/dist/src/product-loop/plan-adherence-review.js +144 -0
- package/dist/src/product-loop/sprint-runner.d.ts +173 -0
- package/dist/src/product-loop/sprint-runner.js +863 -19
- package/dist/src/product-loop/types.d.ts +61 -5
- package/dist/src/providers/adapter.d.ts +1 -1
- package/dist/src/providers/adapter.js +3 -4
- package/dist/src/providers/anthropic.d.ts +9 -8
- package/dist/src/providers/anthropic.js +13 -47
- 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/grok-oauth.d.ts +1 -0
- package/dist/src/providers/auth/grok-oauth.js +30 -5
- package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
- package/dist/src/providers/auth/openai-oauth.js +15 -1
- package/dist/src/providers/auth/registry.js +0 -34
- package/dist/src/providers/auth/token-store.d.ts +9 -9
- package/dist/src/providers/auth/token-store.js +8 -67
- package/dist/src/providers/auth/types.d.ts +9 -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/env-store.d.ts +17 -0
- package/dist/src/providers/env-store.js +228 -0
- package/dist/src/providers/keychain.d.ts +22 -18
- package/dist/src/providers/keychain.js +127 -140
- 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 +43 -3
- package/dist/src/providers/runtime.js +88 -14
- 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 +288 -1
- package/dist/src/providers/strategies/xai.strategy.js +27 -0
- 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/warm.d.ts +65 -0
- package/dist/src/providers/warm.js +145 -0
- 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 +6 -5
- 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 +340 -15
- package/dist/src/tools/file.d.ts +15 -0
- package/dist/src/tools/file.js +32 -0
- package/dist/src/tools/git-safety.d.ts +19 -0
- package/dist/src/tools/git-safety.js +168 -0
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +81 -1
- package/dist/src/tools/registry.d.ts +20 -0
- package/dist/src/tools/registry.js +576 -23
- package/dist/src/tools/research.d.ts +29 -0
- package/dist/src/tools/research.js +233 -0
- package/dist/src/types/index.d.ts +147 -4
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/cards/product-status-card.js +1 -1
- package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
- package/dist/src/ui/components/agent-rail-activities.js +47 -0
- 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/compact-progress-card.d.ts +24 -0
- package/dist/src/ui/components/compact-progress-card.js +42 -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 +66 -17
- 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 +13 -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/message-view.d.ts +15 -0
- package/dist/src/ui/components/message-view.js +50 -1
- 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/components/tool-group.d.ts +15 -3
- package/dist/src/ui/components/tool-group.js +69 -11
- package/dist/src/ui/containers/modals-layer.d.ts +4 -2
- package/dist/src/ui/containers/modals-layer.js +2 -2
- package/dist/src/ui/council-harness-event.d.ts +57 -0
- package/dist/src/ui/council-harness-event.js +46 -0
- package/dist/src/ui/heartbeat-debug.d.ts +29 -0
- package/dist/src/ui/heartbeat-debug.js +45 -0
- 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/model-picker-modal.d.ts +8 -18
- package/dist/src/ui/modals/model-picker-modal.js +8 -10
- 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/ee.js +81 -0
- 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 +23 -20
- 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/agent-activities.d.ts +39 -0
- package/dist/src/ui/utils/agent-activities.js +96 -0
- package/dist/src/ui/utils/format.d.ts +14 -0
- package/dist/src/ui/utils/format.js +23 -3
- package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
- package/dist/src/ui/utils/group-tool-entries.js +111 -0
- package/dist/src/ui/utils/tool-summary.d.ts +21 -0
- package/dist/src/ui/utils/tool-summary.js +91 -0
- package/dist/src/usage/downgrade.js +2 -2
- package/dist/src/usage/product-ledger.js +2 -2
- package/dist/src/utils/event-loop-monitor.d.ts +85 -0
- package/dist/src/utils/event-loop-monitor.js +107 -0
- package/dist/src/utils/install-manager.js +2 -1
- package/dist/src/utils/llm-deadline.d.ts +14 -0
- package/dist/src/utils/llm-deadline.js +19 -0
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/loop-profiler.d.ts +102 -0
- package/dist/src/utils/loop-profiler.js +202 -0
- package/dist/src/utils/permission-mode.js +5 -3
- package/dist/src/utils/redactor.js +1 -1
- package/dist/src/utils/settings.d.ts +180 -5
- package/dist/src/utils/settings.js +271 -31
- package/dist/src/utils/side-question.d.ts +1 -2
- package/dist/src/utils/side-question.js +2 -2
- 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 +52 -17
- package/dist/src/verify/orchestrator.d.ts +1 -1
- package/dist/src/verify/orchestrator.js +20 -3
- package/dist/src/verify/recipes.d.ts +13 -0
- package/dist/src/verify/recipes.js +15 -0
- package/package.json +134 -132
- package/dist/src/cli/bw-vault.d.ts +0 -55
- package/dist/src/cli/bw-vault.js +0 -133
- package/dist/src/mcp/ee-tools.d.ts +0 -46
- package/dist/src/mcp/ee-tools.js +0 -193
- 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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { CouncilLLM } from "../council/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Extract acceptance-criteria strings from a council synthesis blob. The synthesis
|
|
4
|
+
* is `<json>---READABLE---<prose>` (see council/planner.parseOutcome); the JSON
|
|
5
|
+
* block carries `acceptance_criteria: string[]`. Falls back to a markdown
|
|
6
|
+
* "Acceptance Criteria" bullet section when JSON is absent/malformed so a
|
|
7
|
+
* readable-only plan still seeds something.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractAcceptanceCriteria(planSynthesis: string): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Stable, single-line id for a criterion (used as the gray-areas.md section
|
|
12
|
+
* heading). Deterministic so re-seeding the same criterion is idempotent.
|
|
13
|
+
*/
|
|
14
|
+
export declare function criterionIdFromText(text: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Merge the plan's acceptance criteria into gray-areas.md as unmet Criterion rows.
|
|
17
|
+
* Idempotent and non-clobbering: criteria already present (any status) are left
|
|
18
|
+
* untouched so a re-plan/retry never resets progress.
|
|
19
|
+
* Returns the count of NEW criteria seeded.
|
|
20
|
+
*/
|
|
21
|
+
export declare function seedCriteriaFromPlan(flowDir: string, runId: string, criteriaTexts: string[], sprintN: number): Promise<number>;
|
|
22
|
+
/**
|
|
23
|
+
* Non-blocking plan-quality check. The per-sprint plan is auto-approved (there is
|
|
24
|
+
* no plan-check gate by design, to avoid stranding the loop), so a weak plan reaches
|
|
25
|
+
* the implementer silently. This surfaces the two failure modes that let Sprint 1
|
|
26
|
+
* diverge undetected: (1) no acceptance_criteria → done-gate can't score/gate,
|
|
27
|
+
* (2) no file_edits → the plan is prose, not an executable target list. Callers
|
|
28
|
+
* emit these as warnings and may inject a corrective note; they never halt.
|
|
29
|
+
*/
|
|
30
|
+
export declare function planQualityIssues(planSynthesis: string, seededCriteriaCount: number): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Judge the still-unmet criteria against the sprint's verify output + a diff
|
|
33
|
+
* summary, using a single bounded LLM call, then persist met/partial statuses
|
|
34
|
+
* with evidence. This is what lets the done-gate score reflect what was actually
|
|
35
|
+
* implemented (and blocks ship when the impl diverged from the plan).
|
|
36
|
+
*
|
|
37
|
+
* Fail-open: any parse/LLM error leaves criteria unmet (conservative — a sprint
|
|
38
|
+
* cannot pass on a judging failure). Returns the number of criteria upgraded.
|
|
39
|
+
*/
|
|
40
|
+
export declare function judgeCriteriaAgainstVerify(args: {
|
|
41
|
+
flowDir: string;
|
|
42
|
+
runId: string;
|
|
43
|
+
llm: CouncilLLM;
|
|
44
|
+
modelId: string;
|
|
45
|
+
verifyVerdict: string;
|
|
46
|
+
verifyOutput: string;
|
|
47
|
+
diffSummary: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
judged: number;
|
|
50
|
+
total: number;
|
|
51
|
+
}>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { readCriteria, updateCriteria } from "./artifact-io.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slice fidelity fix (2026-07-11): the per-sprint council synthesis (planSynthesis)
|
|
4
|
+
* already carries a rich `acceptance_criteria` array, but nothing extracted it into
|
|
5
|
+
* the criteria store (gray-areas.md). readCriteria therefore returned [], the
|
|
6
|
+
* done-gate's calculateScore returned 0, and every sprint scored 0.00 — the
|
|
7
|
+
* implementation could diverge from the plan (wrong LSP op, stub tools) with no
|
|
8
|
+
* gate to catch it. This module closes that gap: seed the plan's acceptance
|
|
9
|
+
* criteria as real Criterion rows, then judge them against the verify output so the
|
|
10
|
+
* score reflects what was actually built and a failing criterion forces a retry.
|
|
11
|
+
*/
|
|
12
|
+
const MAX_CRITERIA_PER_SPRINT = 24;
|
|
13
|
+
const ID_MAX_LEN = 70;
|
|
14
|
+
/**
|
|
15
|
+
* Extract acceptance-criteria strings from a council synthesis blob. The synthesis
|
|
16
|
+
* is `<json>---READABLE---<prose>` (see council/planner.parseOutcome); the JSON
|
|
17
|
+
* block carries `acceptance_criteria: string[]`. Falls back to a markdown
|
|
18
|
+
* "Acceptance Criteria" bullet section when JSON is absent/malformed so a
|
|
19
|
+
* readable-only plan still seeds something.
|
|
20
|
+
*/
|
|
21
|
+
export function extractAcceptanceCriteria(planSynthesis) {
|
|
22
|
+
if (!planSynthesis || !planSynthesis.trim())
|
|
23
|
+
return [];
|
|
24
|
+
// 1) Prefer the JSON block before the ---READABLE--- separator.
|
|
25
|
+
const jsonPart = planSynthesis.includes("---READABLE---") ? planSynthesis.split("---READABLE---")[0] : planSynthesis;
|
|
26
|
+
const jsonMatch = jsonPart.match(/\{[\s\S]*\}/);
|
|
27
|
+
if (jsonMatch) {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
30
|
+
const raw = parsed.acceptance_criteria ?? parsed.acceptanceCriteria;
|
|
31
|
+
if (Array.isArray(raw)) {
|
|
32
|
+
const items = raw
|
|
33
|
+
.map((c) => (typeof c === "string" ? c : typeof c === "object" && c ? JSON.stringify(c) : ""))
|
|
34
|
+
.map((s) => s.trim())
|
|
35
|
+
.filter((s) => s.length > 0);
|
|
36
|
+
if (items.length > 0)
|
|
37
|
+
return dedupe(items).slice(0, MAX_CRITERIA_PER_SPRINT);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
/* fall through to markdown */
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// 2) Markdown fallback: bullets under an "Acceptance Criteria" heading.
|
|
45
|
+
const lines = planSynthesis.split("\n");
|
|
46
|
+
const out = [];
|
|
47
|
+
let inSection = false;
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
if (/^#{1,6}\s|^\*\*/.test(trimmed) && /acceptance\s+criteria/i.test(trimmed)) {
|
|
51
|
+
inSection = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (inSection) {
|
|
55
|
+
if (/^#{1,6}\s/.test(trimmed) || /^\*\*[A-Z]/.test(trimmed))
|
|
56
|
+
break; // next heading
|
|
57
|
+
const bullet = trimmed.match(/^[-*]\s+(.*)$/) ?? trimmed.match(/^\d+\.\s+(.*)$/);
|
|
58
|
+
if (bullet && bullet[1].trim())
|
|
59
|
+
out.push(bullet[1].trim());
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return dedupe(out).slice(0, MAX_CRITERIA_PER_SPRINT);
|
|
63
|
+
}
|
|
64
|
+
function dedupe(items) {
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const it of items) {
|
|
68
|
+
const key = it.toLowerCase().replace(/\s+/g, " ");
|
|
69
|
+
if (!seen.has(key)) {
|
|
70
|
+
seen.add(key);
|
|
71
|
+
out.push(it);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Stable, single-line id for a criterion (used as the gray-areas.md section
|
|
78
|
+
* heading). Deterministic so re-seeding the same criterion is idempotent.
|
|
79
|
+
*/
|
|
80
|
+
export function criterionIdFromText(text) {
|
|
81
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
82
|
+
if (oneLine.length <= ID_MAX_LEN)
|
|
83
|
+
return oneLine;
|
|
84
|
+
// Truncate but append a short hash so two long criteria that share a prefix
|
|
85
|
+
// don't collapse to the same heading.
|
|
86
|
+
let hash = 0;
|
|
87
|
+
for (let i = 0; i < oneLine.length; i++)
|
|
88
|
+
hash = (hash * 31 + oneLine.charCodeAt(i)) | 0;
|
|
89
|
+
const suffix = (hash >>> 0).toString(36).slice(0, 6);
|
|
90
|
+
return `${oneLine.slice(0, ID_MAX_LEN - 8).trim()}… #${suffix}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Merge the plan's acceptance criteria into gray-areas.md as unmet Criterion rows.
|
|
94
|
+
* Idempotent and non-clobbering: criteria already present (any status) are left
|
|
95
|
+
* untouched so a re-plan/retry never resets progress.
|
|
96
|
+
* Returns the count of NEW criteria seeded.
|
|
97
|
+
*/
|
|
98
|
+
export async function seedCriteriaFromPlan(flowDir, runId, criteriaTexts, sprintN) {
|
|
99
|
+
if (criteriaTexts.length === 0)
|
|
100
|
+
return 0;
|
|
101
|
+
const existing = await readCriteria(flowDir, runId);
|
|
102
|
+
const existingIds = new Set(existing.map((c) => c.id.trim()));
|
|
103
|
+
const fresh = [];
|
|
104
|
+
for (const text of criteriaTexts) {
|
|
105
|
+
const id = criterionIdFromText(text);
|
|
106
|
+
if (existingIds.has(id.trim()))
|
|
107
|
+
continue;
|
|
108
|
+
existingIds.add(id.trim());
|
|
109
|
+
fresh.push({ id, status: "unmet", sprint: sprintN });
|
|
110
|
+
}
|
|
111
|
+
if (fresh.length === 0)
|
|
112
|
+
return 0;
|
|
113
|
+
await updateCriteria(flowDir, runId, fresh);
|
|
114
|
+
return fresh.length;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Non-blocking plan-quality check. The per-sprint plan is auto-approved (there is
|
|
118
|
+
* no plan-check gate by design, to avoid stranding the loop), so a weak plan reaches
|
|
119
|
+
* the implementer silently. This surfaces the two failure modes that let Sprint 1
|
|
120
|
+
* diverge undetected: (1) no acceptance_criteria → done-gate can't score/gate,
|
|
121
|
+
* (2) no file_edits → the plan is prose, not an executable target list. Callers
|
|
122
|
+
* emit these as warnings and may inject a corrective note; they never halt.
|
|
123
|
+
*/
|
|
124
|
+
export function planQualityIssues(planSynthesis, seededCriteriaCount) {
|
|
125
|
+
const issues = [];
|
|
126
|
+
if (seededCriteriaCount === 0) {
|
|
127
|
+
issues.push("plan carries no acceptance_criteria — the done-gate cannot score this sprint against the plan");
|
|
128
|
+
}
|
|
129
|
+
if (!/"?file_edits"?\s*:/.test(planSynthesis) && !/##\s*file edits/i.test(planSynthesis)) {
|
|
130
|
+
issues.push("plan lists no file_edits — implementation has no concrete target files to follow");
|
|
131
|
+
}
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Judge the still-unmet criteria against the sprint's verify output + a diff
|
|
136
|
+
* summary, using a single bounded LLM call, then persist met/partial statuses
|
|
137
|
+
* with evidence. This is what lets the done-gate score reflect what was actually
|
|
138
|
+
* implemented (and blocks ship when the impl diverged from the plan).
|
|
139
|
+
*
|
|
140
|
+
* Fail-open: any parse/LLM error leaves criteria unmet (conservative — a sprint
|
|
141
|
+
* cannot pass on a judging failure). Returns the number of criteria upgraded.
|
|
142
|
+
*/
|
|
143
|
+
export async function judgeCriteriaAgainstVerify(args) {
|
|
144
|
+
const criteria = await readCriteria(args.flowDir, args.runId);
|
|
145
|
+
const unmet = criteria.filter((c) => c.status !== "met");
|
|
146
|
+
if (unmet.length === 0)
|
|
147
|
+
return { judged: 0, total: criteria.length };
|
|
148
|
+
// Evidence must be verifiable (done-gate condition #2 rejects criteria marked
|
|
149
|
+
// met/partial without a valid evidence string), so only allow upgrades when
|
|
150
|
+
// verify did not hard-fail. On FAIL/ERROR, leave everything unmet.
|
|
151
|
+
if (args.verifyVerdict !== "PASS") {
|
|
152
|
+
return { judged: 0, total: criteria.length };
|
|
153
|
+
}
|
|
154
|
+
const list = unmet.map((c, i) => `${i + 1}. [${c.id}] ${c.id}`).join("\n");
|
|
155
|
+
const prompt = `You are grading whether each acceptance criterion is satisfied by a sprint's ` +
|
|
156
|
+
`actual work. Be strict: mark "met" ONLY when the diff + verify output show it ` +
|
|
157
|
+
`is truly satisfied; "partial" if started but incomplete; "unmet" otherwise.\n\n` +
|
|
158
|
+
`Verify verdict: ${args.verifyVerdict}\n` +
|
|
159
|
+
`Verify output (truncated):\n${args.verifyOutput.slice(0, 4000)}\n\n` +
|
|
160
|
+
`Changed files / diff summary (truncated):\n${args.diffSummary.slice(0, 4000)}\n\n` +
|
|
161
|
+
`Criteria:\n${list}\n\n` +
|
|
162
|
+
`Return ONLY a JSON array: [{"n": <number>, "status": "met"|"partial"|"unmet", ` +
|
|
163
|
+
`"evidence": "<one concrete sentence citing a file/test/output; required for met/partial>"}]`;
|
|
164
|
+
let raw;
|
|
165
|
+
try {
|
|
166
|
+
raw = await args.llm.generate(args.modelId, "You are a strict acceptance-criteria grader.", prompt);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return { judged: 0, total: criteria.length };
|
|
170
|
+
}
|
|
171
|
+
const jsonMatch = raw.match(/\[[\s\S]*\]/);
|
|
172
|
+
if (!jsonMatch)
|
|
173
|
+
return { judged: 0, total: criteria.length };
|
|
174
|
+
let verdicts;
|
|
175
|
+
try {
|
|
176
|
+
verdicts = JSON.parse(jsonMatch[0]);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return { judged: 0, total: criteria.length };
|
|
180
|
+
}
|
|
181
|
+
const updates = [];
|
|
182
|
+
for (const v of verdicts) {
|
|
183
|
+
if (typeof v.n !== "number" || v.n < 1 || v.n > unmet.length)
|
|
184
|
+
continue;
|
|
185
|
+
const target = unmet[v.n - 1];
|
|
186
|
+
const status = v.status === "met" || v.status === "partial" ? v.status : "unmet";
|
|
187
|
+
const evidence = typeof v.evidence === "string" ? v.evidence.trim() : "";
|
|
188
|
+
// Don't upgrade without evidence — the done-gate would reject it anyway.
|
|
189
|
+
if ((status === "met" || status === "partial") && evidence.length < 8)
|
|
190
|
+
continue;
|
|
191
|
+
if (status === "unmet")
|
|
192
|
+
continue;
|
|
193
|
+
updates.push({ id: target.id, status, evidence, sprint: target.sprint });
|
|
194
|
+
}
|
|
195
|
+
if (updates.length > 0) {
|
|
196
|
+
await updateCriteria(args.flowDir, args.runId, updates);
|
|
197
|
+
}
|
|
198
|
+
return { judged: updates.length, total: criteria.length };
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=criteria-seed.js.map
|
|
@@ -34,7 +34,9 @@ export function formatProjectContextForPrompt(ctx) {
|
|
|
34
34
|
lines.push(`Backend stack: ${lang} / ${fw}${runtime}`);
|
|
35
35
|
}
|
|
36
36
|
if (c.dbStrategy) {
|
|
37
|
-
lines.push(
|
|
37
|
+
lines.push(c.dbStrategy.mode === "none"
|
|
38
|
+
? "Database: none (stateless — no persistent storage)"
|
|
39
|
+
: `Database: ${c.dbStrategy.mode ?? "(unspecified)"} ${c.dbStrategy.engine ?? ""}`.trimEnd());
|
|
38
40
|
}
|
|
39
41
|
if (c.frontendApproach) {
|
|
40
42
|
const harness = c.frontendApproach.agentHarness
|
|
@@ -164,7 +164,10 @@ export function buildEcosystemPreamble() {
|
|
|
164
164
|
"- Angular with @muonroi/agent-harness-angular directive.",
|
|
165
165
|
'When recommending `frontendApproach`, ALWAYS fill the optional `agentHarness` slot — pick "react" for React/Next, "angular" for Angular, "opentui" for terminal UI, "core" for non-DOM integration. Only use "none" when the project has no UI at all.',
|
|
166
166
|
"",
|
|
167
|
-
"
|
|
167
|
+
"Strongest-default, still extensible:",
|
|
168
|
+
"- Keep the Muonroi/.NET path as the PRIMARY recommendation — it is the ecosystem's strongest, best-supported option.",
|
|
169
|
+
"- BUT for stack choices (architecture/stack/platform) ALWAYS include at least one mainstream NON-ecosystem alternative in `alternatives` (pick what genuinely fits the task — e.g. Python for data/ML/scripting, Go or Rust for a fast static-binary CLI, Node/TypeScript for JS-native tooling), with a one-line rationale. A user outside the Muonroi ecosystem must always see a real, viable path — never a .NET-only menu.",
|
|
170
|
+
"- Promote the non-ecosystem option to PRIMARY only when the prompt names another stack, or the task is a poor fit for .NET (e.g. a shell-glue one-liner, a data-science notebook, an npm-ecosystem plugin). Otherwise it stays a first-class alternative, not the default.",
|
|
168
171
|
].join("\n");
|
|
169
172
|
}
|
|
170
173
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RecommendInput, type RecommendOutput } from "./discovery-recommender.js";
|
|
2
|
+
import type { InterviewTriage } from "./discovery-triage.js";
|
|
2
3
|
import type { ExistingProjectSignals, ProjectContext } from "./types.js";
|
|
3
4
|
export type UserPromptResult = {
|
|
4
5
|
action: "accept";
|
|
@@ -62,5 +63,13 @@ export interface IterateOpts {
|
|
|
62
63
|
* sandboxed temp dir to exercise existing-project paths deterministically.
|
|
63
64
|
*/
|
|
64
65
|
cwd?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Model-decided interview triage (see discovery-triage.ts). When present it is
|
|
68
|
+
* the PRIMARY signal for how deep to interview — replacing the legacy keyword
|
|
69
|
+
* `computePromptSpecificity` heuristic (kept only as the graceful-degrade
|
|
70
|
+
* fallback inside `triageInterview`). Absent → the old specificity behaviour is
|
|
71
|
+
* used verbatim, so existing callers/tests are unaffected.
|
|
72
|
+
*/
|
|
73
|
+
triage?: InterviewTriage;
|
|
65
74
|
}
|
|
66
75
|
export declare function iterateInterview(opts: IterateOpts): Promise<ProjectContext>;
|
|
@@ -3,6 +3,13 @@ import { appendUserOverride, buildProjectContextFromState, markDone, markUserGat
|
|
|
3
3
|
import { computePromptSpecificity, toEntry, } from "./discovery-recommender.js";
|
|
4
4
|
import { DISCOVERY_QUESTIONS, isFePolicyAccepted, isRequiredForPlatform, REQUIRED_QUESTION_IDS, validateAnswer, } from "./discovery-schema.js";
|
|
5
5
|
import { buildRepoBrief } from "./repo-brief.js";
|
|
6
|
+
/**
|
|
7
|
+
* Under existing-repo collapse, keep ONLY these questions as interactive
|
|
8
|
+
* per-field cards — they are the fields whose answers actually shape the
|
|
9
|
+
* technical change. Everything else in DISCOVERY_QUESTIONS is auto-filled and
|
|
10
|
+
* surfaced together on the compact __user_gate__ confirm card.
|
|
11
|
+
*/
|
|
12
|
+
const KEEP_CARD_FOR_EXISTING = new Set(["backendArchitecture"]);
|
|
6
13
|
export async function iterateInterview(opts) {
|
|
7
14
|
const { flowDir, runId, detection } = opts;
|
|
8
15
|
const _itvDbg = process.env.MUONROI_DEBUG_LEADER === "1";
|
|
@@ -20,17 +27,47 @@ export async function iterateInterview(opts) {
|
|
|
20
27
|
// but optional ones (baStatus, designStatus, deployment, frontendApproach when
|
|
21
28
|
// not web) are deferred unless the user explicitly re-runs with more context.
|
|
22
29
|
const specificity = computePromptSpecificity(opts.idea);
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
30
|
+
// Interview-depth tier. PRIMARY signal is the model-decided triage
|
|
31
|
+
// (opts.triage); the legacy keyword specificity is used ONLY when no triage was
|
|
32
|
+
// supplied (older callers / tests), mapped: minimal→trivial, else→standard.
|
|
33
|
+
// "detailed" no longer force-collapses on the heuristic alone — a precise-but-
|
|
34
|
+
// complex idea should be triaged as complex by the model, not auto-filled blind.
|
|
35
|
+
const tier = opts.triage?.complexity ?? (specificity === "minimal" ? "trivial" : "standard");
|
|
36
|
+
// Existing-codebase work (refactor / migration / feature-add on a repo that
|
|
37
|
+
// already has source) should NOT re-run the full greenfield product-scoping
|
|
38
|
+
// questionnaire. productType/audience/targetPlatform/frontend/design/deployment
|
|
39
|
+
// are derivable from the codebase and inert for the technical change — asking
|
|
40
|
+
// them per-field is the over-ask users complained about. For any non-greenfield
|
|
41
|
+
// classification (existing OR ambiguous polyglot) we collapse to the ONE
|
|
42
|
+
// decision-relevant card (backendArchitecture) plus the compact "assumed from
|
|
43
|
+
// codebase" confirm gate (the same __user_gate__ path used for minimal/detailed
|
|
44
|
+
// prompts). This mirrors the repoBrief-build condition below. Escape hatch:
|
|
45
|
+
// MUONROI_DISCOVERY_EXISTING_COLLAPSE=0 restores the full per-field interview.
|
|
46
|
+
const collapseForExisting = detection.classification !== "greenfield" && process.env.MUONROI_DISCOVERY_EXISTING_COLLAPSE !== "0";
|
|
47
|
+
const skipOptionalForMinimal = tier === "trivial" || collapseForExisting;
|
|
48
|
+
// Which REQUIRED questions stay as interactive per-question cards. Everything
|
|
49
|
+
// else auto-fills the recommender primary and is surfaced together on the ONE
|
|
50
|
+
// summary confirm card (__user_gate__). This is the crux of the "sharper, less
|
|
51
|
+
// hardcoded" interview:
|
|
52
|
+
// - existing-repo collapse → only the decision-relevant field(s)
|
|
53
|
+
// - trivial → NONE (e.g. a hello-world script: don't interrogate audience
|
|
54
|
+
// scale / backend architecture / db strategy — just confirm the defaults)
|
|
55
|
+
// - complex → ONLY the questions the model flagged as genuinely shaping this
|
|
56
|
+
// build (triage.relevant); auto-fill the rest
|
|
57
|
+
// - standard (or model unavailable) → ALL required stay cards (unchanged UX)
|
|
58
|
+
const interactiveRequired = collapseForExisting
|
|
59
|
+
? new Set(KEEP_CARD_FOR_EXISTING)
|
|
60
|
+
: tier === "trivial"
|
|
61
|
+
? new Set()
|
|
62
|
+
: tier === "complex" && opts.triage
|
|
63
|
+
? new Set(opts.triage.relevant)
|
|
64
|
+
: new Set(REQUIRED_QUESTION_IDS);
|
|
65
|
+
// Auto-accept the recommender primary for required questions NOT kept interactive
|
|
66
|
+
// above, and surface ONE summary card listing the assumptions so the user can
|
|
67
|
+
// proceed or adjust. Enabled whenever ANY required question is being auto-filled
|
|
68
|
+
// (i.e. the interactive set is a strict subset of the required set). Escape hatch:
|
|
69
|
+
// MUONROI_DISCOVERY_AUTOFILL=0 restores per-question cards everywhere.
|
|
70
|
+
const autoFillRequired = process.env.MUONROI_DISCOVERY_AUTOFILL !== "0" && REQUIRED_QUESTION_IDS.some((id) => !interactiveRequired.has(id));
|
|
34
71
|
const assumed = [];
|
|
35
72
|
// G1 follow-up: keep the recommendation behind each auto-filled assumption so
|
|
36
73
|
// the user-gate "edit: <field>" path can re-render the SAME per-question card
|
|
@@ -94,7 +131,18 @@ export async function iterateInterview(opts) {
|
|
|
94
131
|
// policy) so a malformed recommendation falls back to the normal card flow.
|
|
95
132
|
// The assumed answers are surfaced together on the single user-gate card.
|
|
96
133
|
let autoAccepted = false;
|
|
97
|
-
|
|
134
|
+
// Keep the interview-relevant fields interactive (existing-repo →
|
|
135
|
+
// backendArchitecture; complex → the model-flagged `triage.relevant`), and
|
|
136
|
+
// NEVER silently auto-accept a weakly-grounded recommendation (synthFailed =
|
|
137
|
+
// the rationale failed the repo-brief citation check twice) — fall through to a
|
|
138
|
+
// per-question card so the user can catch a hallucinated value instead of it
|
|
139
|
+
// being assumed.
|
|
140
|
+
const keepInteractive = interactiveRequired.has(question.id);
|
|
141
|
+
if (autoFillRequired &&
|
|
142
|
+
effectivelyRequired &&
|
|
143
|
+
recommendation.primary?.value != null &&
|
|
144
|
+
!recommendation.synthFailed &&
|
|
145
|
+
!keepInteractive) {
|
|
98
146
|
const v = recommendation.primary.value;
|
|
99
147
|
const validation = validateAnswer(question.id, v);
|
|
100
148
|
const feLib = question.id === "frontendApproach" ? v?.library : undefined;
|
|
@@ -79,7 +79,8 @@ const LEADER_SYSTEM = "You are a product context recommender. Output ONE JSON ob
|
|
|
79
79
|
"## Scope-sizing discipline\n" +
|
|
80
80
|
"You will be told the user's original prompt and its specificity bucket (minimal/moderate/detailed).\n" +
|
|
81
81
|
'- When specificity is "minimal" (e.g. user typed "build a todo app" or "tạo wiki"), pick the SMALLEST-SCOPE primary that still works: ' +
|
|
82
|
-
'productType="
|
|
82
|
+
'productType="consumer-app" for a small app, or "cli-tool"/"script"/"library" for a stand-alone tool/snippet ' +
|
|
83
|
+
'(prefer these over the catch-all "other" — a hello-world script is a "script", not "other"), audience scale="1-100" (NOT "100-1k" or above), ' +
|
|
83
84
|
"single-user / no auth / web-only / simplest stack. Put richer multi-tenant/team-scale alternatives in `alternatives`, NOT primary. " +
|
|
84
85
|
"Rationale: short prompts mean the user has NOT asked for enterprise complexity. Inflating scope here cascades into wasted debate and over-built code.\n" +
|
|
85
86
|
'- When specificity is "moderate", pick pragmatic defaults grounded in any stated context; surface ONE richer alternative.\n' +
|
|
@@ -18,7 +18,7 @@ export const DISCOVERY_QUESTIONS = [
|
|
|
18
18
|
id: "dbStrategy",
|
|
19
19
|
required: true,
|
|
20
20
|
recommendMode: "council",
|
|
21
|
-
prompt: "Database strategy: greenfield, existing schema, or migration?",
|
|
21
|
+
prompt: "Database strategy: none (stateless — no persistent storage), greenfield, existing schema, or migration? Choose none for a CLI/tool/filter that keeps no data.",
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
id: "frontendApproach",
|
|
@@ -48,7 +48,20 @@ export function isRequiredForPlatform(questionId, platforms) {
|
|
|
48
48
|
}
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
|
-
const PRODUCT_TYPES = new Set([
|
|
51
|
+
const PRODUCT_TYPES = new Set([
|
|
52
|
+
"saas",
|
|
53
|
+
"internal-tool",
|
|
54
|
+
"consumer-app",
|
|
55
|
+
"b2b-platform",
|
|
56
|
+
"marketplace",
|
|
57
|
+
// Small-scope builds that previously had no home in the enum and so were
|
|
58
|
+
// forced to "other" — the exact "recommend other" bad UX users hit for a
|
|
59
|
+
// hello-world script. A trivial CLI/script/library now maps cleanly.
|
|
60
|
+
"cli-tool",
|
|
61
|
+
"script",
|
|
62
|
+
"library",
|
|
63
|
+
"other",
|
|
64
|
+
]);
|
|
52
65
|
const SCALES = new Set(["1-100", "100-1k", "1k-100k", "100k-1M", "1M+"]);
|
|
53
66
|
/**
|
|
54
67
|
* Schema hint for the leader prompt — surfaces the enum/shape that
|
|
@@ -74,6 +87,10 @@ export function getSchemaHintForLeader(questionId) {
|
|
|
74
87
|
return `value MUST be an object {"persona": string, "scale": one of ${Array.from(SCALES)
|
|
75
88
|
.map((s) => JSON.stringify(s))
|
|
76
89
|
.join("|")}, "geography": string}`;
|
|
90
|
+
case "dbStrategy":
|
|
91
|
+
// F7 — give the recommender an explicit "no persistence" option so it
|
|
92
|
+
// stops mis-ranking "greenfield" for stateless products (CLI/tool/filter).
|
|
93
|
+
return `value MUST be an object {"mode": one of "none"|"greenfield"|"existing-schema"|"migrate-from", "engine": string, "notes"?: string}. Pick "mode":"none" with "engine":"" when the product keeps NO persistent data (e.g. a stateless CLI, filter, or one-shot tool) — do NOT default to "greenfield" in that case.`;
|
|
77
94
|
case "frontendApproach":
|
|
78
95
|
return `value MUST be an object {"library": one of ${Array.from(ACCEPTED_FE_LIBRARIES)
|
|
79
96
|
.map((l) => JSON.stringify(l))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LeaderLike } from "./discovery-prompt-parser.js";
|
|
2
|
+
import { type DiscoveryQuestion } from "./discovery-schema.js";
|
|
3
|
+
export type ComplexityTier = "trivial" | "standard" | "complex";
|
|
4
|
+
export interface InterviewTriage {
|
|
5
|
+
complexity: ComplexityTier;
|
|
6
|
+
/**
|
|
7
|
+
* Required question ids (subset of REQUIRED_QUESTION_IDS) whose answers
|
|
8
|
+
* genuinely shape THIS build and must stay interactive cards. Only meaningful
|
|
9
|
+
* for `complexity === "complex"`; empty for trivial (auto-fill all) and ignored
|
|
10
|
+
* for standard (keep all cards).
|
|
11
|
+
*/
|
|
12
|
+
relevant: string[];
|
|
13
|
+
rationale: string;
|
|
14
|
+
source: "model" | "fallback";
|
|
15
|
+
}
|
|
16
|
+
/** Graceful-degrade triage derived from the legacy specificity heuristic. */
|
|
17
|
+
export declare function fallbackTriage(idea: string): InterviewTriage;
|
|
18
|
+
/**
|
|
19
|
+
* Triage the interview depth for `idea` via one leader call. Never throws —
|
|
20
|
+
* returns a fallback triage on any LLM/parse failure. Skips the call entirely
|
|
21
|
+
* (and returns the fallback) when the idea is empty.
|
|
22
|
+
*/
|
|
23
|
+
export declare function triageInterview(idea: string, leader: LeaderLike, requiredQuestions: DiscoveryQuestion[]): Promise<InterviewTriage>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/product-loop/discovery-triage.ts
|
|
2
|
+
//
|
|
3
|
+
// Model-decided interview triage.
|
|
4
|
+
//
|
|
5
|
+
// The discover/gather interview used to size itself off `computePromptSpecificity`
|
|
6
|
+
// — a hardcoded word-count + keyword-list heuristic. That misclassifies precise-
|
|
7
|
+
// but-trivial ideas: "build a Python script hello.py that prints … and a pytest
|
|
8
|
+
// test" is ~18 words with no keyword hit → "moderate" → the user is dragged
|
|
9
|
+
// through 6 generic cards (productType/platform/audience/architecture/stack/db)
|
|
10
|
+
// and the productType recommender falls back to "other". That is the "hời hợt /
|
|
11
|
+
// hardcode" UX users complained about, and it violates the project's
|
|
12
|
+
// agent-first-no-regex principle (interview depth must be model-decided).
|
|
13
|
+
//
|
|
14
|
+
// This module asks the leader model to triage the idea into a complexity tier and,
|
|
15
|
+
// for genuinely complex ideas, name which of the fixed required questions actually
|
|
16
|
+
// shape the build. The interview then:
|
|
17
|
+
// - trivial → auto-fill every required answer → ONE summary confirm card
|
|
18
|
+
// - complex → keep only the model-relevant questions as cards, auto-fill the rest
|
|
19
|
+
// - standard → unchanged per-question flow
|
|
20
|
+
//
|
|
21
|
+
// Fail-safe: any parse/LLM failure returns a `fallback` triage derived from the
|
|
22
|
+
// old specificity heuristic, so the interview degrades to today's behaviour rather
|
|
23
|
+
// than breaking. Pure decision + one bounded LLM call → unit-testable.
|
|
24
|
+
import { computePromptSpecificity } from "./discovery-recommender.js";
|
|
25
|
+
import { REQUIRED_QUESTION_IDS } from "./discovery-schema.js";
|
|
26
|
+
const TRIAGE_SYSTEM = "You triage a software build request to decide how much of an interview it warrants. " +
|
|
27
|
+
"Output ONE JSON object, no prose, no code fences: " +
|
|
28
|
+
'{"complexity":"trivial"|"standard"|"complex","relevant":[<question-id>...],"rationale":"<short>"}.\n\n' +
|
|
29
|
+
"Tiers:\n" +
|
|
30
|
+
'- "trivial": a self-contained script, snippet, one-off tool, or exercise with ONE obvious stack and NO ' +
|
|
31
|
+
"persistence / scale / architecture / integration decisions. Examples: a hello-world script + a test, " +
|
|
32
|
+
"a regex to validate an email, a bash one-liner, a single pure function + unit test. The user should just " +
|
|
33
|
+
"confirm sensible defaults — asking about audience scale, backend architecture, or database strategy is noise.\n" +
|
|
34
|
+
'- "standard": a real but conventional app with a few genuine but well-trodden choices. Examples: a todo web ' +
|
|
35
|
+
"app, a CRUD REST API, a small CLI with a couple of subcommands.\n" +
|
|
36
|
+
'- "complex": multiple non-obvious architecture / scale / integration / security decisions. Examples: a ' +
|
|
37
|
+
"multi-tenant SaaS, an OAuth / SSO service, an event-driven pipeline, anything naming enterprise scale.\n\n" +
|
|
38
|
+
'For "complex" ONLY, set `relevant` to the question ids (from the provided list) whose answers genuinely ' +
|
|
39
|
+
"shape this build and MUST be asked; auto-filled defaults are fine for the rest. For trivial/standard, " +
|
|
40
|
+
"set `relevant` to []. Bias toward FEWER questions: every card the user accepts by reflex becomes locked-in spec.";
|
|
41
|
+
function buildTriagePrompt(idea, requiredQuestions) {
|
|
42
|
+
const qList = requiredQuestions.map((q) => `- ${q.id}: ${q.prompt}`).join("\n");
|
|
43
|
+
return [
|
|
44
|
+
`Build request: ${JSON.stringify(idea)}`,
|
|
45
|
+
"",
|
|
46
|
+
"Required question ids you may cite in `relevant`:",
|
|
47
|
+
qList,
|
|
48
|
+
].join("\n");
|
|
49
|
+
}
|
|
50
|
+
function stripFences(s) {
|
|
51
|
+
return s
|
|
52
|
+
.trim()
|
|
53
|
+
.replace(/^```(?:json)?\s*/, "")
|
|
54
|
+
.replace(/\s*```$/, "");
|
|
55
|
+
}
|
|
56
|
+
const TIERS = new Set(["trivial", "standard", "complex"]);
|
|
57
|
+
/** Graceful-degrade triage derived from the legacy specificity heuristic. */
|
|
58
|
+
export function fallbackTriage(idea) {
|
|
59
|
+
const specificity = computePromptSpecificity(idea);
|
|
60
|
+
// minimal prompt → treat as trivial (collapse to one confirm card, matching the
|
|
61
|
+
// old minimal-autofill path); everything else stays "standard" so the current
|
|
62
|
+
// per-question behaviour is preserved when the model is unavailable.
|
|
63
|
+
const complexity = specificity === "minimal" ? "trivial" : "standard";
|
|
64
|
+
return { complexity, relevant: [], rationale: `fallback from specificity=${specificity}`, source: "fallback" };
|
|
65
|
+
}
|
|
66
|
+
function parseTriage(raw) {
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(stripFences(raw));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const obj = parsed;
|
|
75
|
+
if (typeof obj?.complexity !== "string" || !TIERS.has(obj.complexity))
|
|
76
|
+
return null;
|
|
77
|
+
const requiredSet = new Set(REQUIRED_QUESTION_IDS);
|
|
78
|
+
const relevant = Array.isArray(obj.relevant)
|
|
79
|
+
? obj.relevant.filter((r) => typeof r === "string" && requiredSet.has(r))
|
|
80
|
+
: [];
|
|
81
|
+
const rationale = typeof obj.rationale === "string" ? obj.rationale : "";
|
|
82
|
+
return { complexity: obj.complexity, relevant, rationale };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Triage the interview depth for `idea` via one leader call. Never throws —
|
|
86
|
+
* returns a fallback triage on any LLM/parse failure. Skips the call entirely
|
|
87
|
+
* (and returns the fallback) when the idea is empty.
|
|
88
|
+
*/
|
|
89
|
+
export async function triageInterview(idea, leader, requiredQuestions) {
|
|
90
|
+
if (!idea || idea.trim().length === 0)
|
|
91
|
+
return fallbackTriage(idea);
|
|
92
|
+
try {
|
|
93
|
+
const res = await leader.generate({
|
|
94
|
+
system: TRIAGE_SYSTEM,
|
|
95
|
+
prompt: buildTriagePrompt(idea, requiredQuestions),
|
|
96
|
+
maxTokens: 1024,
|
|
97
|
+
});
|
|
98
|
+
const parsed = parseTriage(res?.content ?? "");
|
|
99
|
+
if (!parsed)
|
|
100
|
+
return fallbackTriage(idea);
|
|
101
|
+
// `relevant` is only meaningful for complex; normalize the others to [].
|
|
102
|
+
const relevant = parsed.complexity === "complex" ? parsed.relevant : [];
|
|
103
|
+
return { complexity: parsed.complexity, relevant, rationale: parsed.rationale, source: "model" };
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return fallbackTriage(idea);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=discovery-triage.js.map
|