muonroi-cli 1.7.2 → 1.8.1
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/dist/packages/agent-harness-core/src/event-filter.js +1 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
- package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
- package/dist/packages/agent-harness-core/src/selector.js +2 -2
- package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
- package/dist/packages/agent-harness-opentui/src/install.js +1 -1
- package/dist/src/__test-stubs__/vitest-setup.js +4 -0
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
- package/dist/src/agent-harness/mock-model.d.ts +1 -1
- package/dist/src/agent-harness/mock-model.js +143 -6
- package/dist/src/agent-harness/test-spawn.js +3 -1
- package/dist/src/chat/chat-keychain.js +9 -1
- package/dist/src/cli/cost-forensics.js +7 -4
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +107 -28
- package/dist/src/cli/usage-report.js +2 -2
- package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
- package/dist/src/council/__tests__/abort-threading.test.js +193 -0
- package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
- package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
- package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
- package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
- package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
- package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
- package/dist/src/council/__tests__/research-tools.test.js +4 -0
- package/dist/src/council/__tests__/round-tools.test.js +6 -0
- package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
- package/dist/src/council/clarifier-question-cap.test.js +73 -0
- package/dist/src/council/clarifier.d.ts +29 -1
- package/dist/src/council/clarifier.js +64 -1
- package/dist/src/council/debate-planner.d.ts +2 -1
- package/dist/src/council/debate-planner.js +5 -3
- package/dist/src/council/debate.d.ts +9 -0
- package/dist/src/council/debate.js +88 -8
- package/dist/src/council/index.d.ts +46 -1
- package/dist/src/council/index.js +125 -19
- package/dist/src/council/llm.js +41 -13
- package/dist/src/council/prompts.js +36 -1
- package/dist/src/council/types.d.ts +14 -1
- package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
- package/dist/src/ee/bridge.d.ts +3 -1
- package/dist/src/ee/bridge.js +9 -3
- package/dist/src/ee/bridge.test.js +1 -2
- package/dist/src/ee/client.js +15 -2
- package/dist/src/ee/recall-ledger.d.ts +20 -0
- package/dist/src/ee/recall-ledger.js +40 -1
- package/dist/src/ee/search.d.ts +25 -0
- package/dist/src/ee/search.js +73 -0
- package/dist/src/ee/types.d.ts +15 -1
- package/dist/src/ee/who-am-i.d.ts +41 -0
- package/dist/src/ee/who-am-i.js +143 -0
- package/dist/src/ee/who-am-i.test.d.ts +1 -0
- package/dist/src/ee/who-am-i.test.js +101 -0
- package/dist/src/flow/compaction/index.d.ts +1 -0
- package/dist/src/flow/compaction/index.js +4 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/council-answers.js +1 -0
- package/dist/src/headless/output.d.ts +30 -1
- package/dist/src/headless/output.js +180 -0
- package/dist/src/headless/output.test.js +61 -1
- package/dist/src/hooks/index.js +48 -2
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +59 -62
- package/dist/src/lsp/client-normalize.test.d.ts +12 -0
- package/dist/src/lsp/client-normalize.test.js +34 -0
- package/dist/src/lsp/client.d.ts +1 -0
- package/dist/src/lsp/client.js +10 -2
- package/dist/src/lsp/manager.d.ts +1 -1
- package/dist/src/lsp/manager.js +13 -5
- package/dist/src/lsp/manager.test.js +41 -2
- package/dist/src/lsp/runtime.d.ts +1 -1
- package/dist/src/lsp/runtime.js +2 -2
- package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
- package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
- package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
- package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
- package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
- package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
- package/dist/src/mcp/auto-setup.js +0 -8
- package/dist/src/mcp/client-pool.d.ts +1 -1
- package/dist/src/mcp/client-pool.js +25 -9
- package/dist/src/mcp/ee-tools.d.ts +10 -0
- package/dist/src/mcp/ee-tools.js +34 -0
- package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/self-verify-runner.js +1 -1
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +3 -0
- package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +2 -0
- package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
- package/dist/src/orchestrator/agent.test.js +5 -2
- package/dist/src/orchestrator/auto-commit.d.ts +110 -0
- package/dist/src/orchestrator/auto-commit.js +366 -0
- package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
- package/dist/src/orchestrator/batch-turn-runner.js +2 -1
- package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
- package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
- package/dist/src/orchestrator/compaction.d.ts +22 -0
- package/dist/src/orchestrator/compaction.js +42 -0
- package/dist/src/orchestrator/delegations.d.ts +1 -0
- package/dist/src/orchestrator/delegations.js +41 -0
- package/dist/src/orchestrator/error-utils.js +27 -2
- package/dist/src/orchestrator/message-processor.d.ts +23 -5
- package/dist/src/orchestrator/message-processor.js +226 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +11 -2
- package/dist/src/orchestrator/orchestrator.js +530 -43
- package/dist/src/orchestrator/pending-calls.js +2 -1
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +146 -0
- package/dist/src/orchestrator/prompts.d.ts +16 -0
- package/dist/src/orchestrator/prompts.js +130 -67
- package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
- package/dist/src/orchestrator/safety-askcard.js +74 -0
- package/dist/src/orchestrator/sandbox.test.js +8 -5
- package/dist/src/orchestrator/scope-ceiling.js +1 -1
- package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
- package/dist/src/orchestrator/stall-watchdog.js +23 -0
- package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
- package/dist/src/orchestrator/stream-runner.js +52 -30
- package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
- package/dist/src/orchestrator/sub-agent-cap.js +12 -4
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
- package/dist/src/orchestrator/subagent-compactor.js +148 -24
- package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
- package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
- package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
- package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
- package/dist/src/orchestrator/tool-args-repair.js +1 -1
- package/dist/src/orchestrator/tool-engine.d.ts +153 -0
- package/dist/src/orchestrator/tool-engine.js +3138 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -0
- package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
- package/dist/src/pil/__tests__/discovery.test.js +89 -145
- package/dist/src/pil/__tests__/dual-run.test.js +2 -0
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
- package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
- package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
- package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
- package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
- package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
- package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
- package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
- package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
- package/dist/src/pil/__tests__/pipeline.test.js +11 -8
- package/dist/src/pil/__tests__/response-tools.test.js +52 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +5 -3
- package/dist/src/pil/agent-operating-contract.test.js +2 -2
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +1 -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/config.d.ts +20 -0
- package/dist/src/pil/config.js +35 -0
- package/dist/src/pil/discovery-types.d.ts +32 -9
- package/dist/src/pil/discovery.d.ts +3 -3
- package/dist/src/pil/discovery.js +138 -244
- package/dist/src/pil/index.d.ts +1 -1
- package/dist/src/pil/index.js +1 -1
- package/dist/src/pil/layer1-intent.d.ts +9 -1
- package/dist/src/pil/layer1-intent.js +62 -7
- package/dist/src/pil/layer1-intent.test.js +112 -2
- package/dist/src/pil/layer15-context-scan.js +1 -1
- package/dist/src/pil/layer16-clarity.d.ts +21 -25
- package/dist/src/pil/layer16-clarity.js +52 -88
- package/dist/src/pil/layer18-acceptance.d.ts +13 -3
- package/dist/src/pil/layer18-acceptance.js +1 -75
- package/dist/src/pil/layer2-personality.js +7 -8
- package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
- package/dist/src/pil/layer2_5-ponytail.js +41 -0
- package/dist/src/pil/layer3-ee-injection.js +180 -14
- package/dist/src/pil/layer4-gsd.js +22 -8
- package/dist/src/pil/layer5-context.d.ts +1 -0
- package/dist/src/pil/layer5-context.js +26 -11
- package/dist/src/pil/layer6-output.js +23 -5
- package/dist/src/pil/llm-classify.d.ts +11 -0
- package/dist/src/pil/llm-classify.js +196 -3
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +4 -3
- package/dist/src/pil/pipeline.js +15 -1
- package/dist/src/pil/response-tools.d.ts +16 -0
- package/dist/src/pil/response-tools.js +41 -0
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +24 -3
- package/dist/src/pil/task-tier-map.js +7 -7
- package/dist/src/pil/types.d.ts +9 -3
- package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
- package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
- package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
- package/dist/src/product-loop/discovery-interview.d.ts +20 -0
- package/dist/src/product-loop/discovery-interview.js +121 -7
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +2 -2
- package/dist/src/product-loop/sprint-self-verify.js +2 -2
- package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
- package/dist/src/providers/auth/gcloud.d.ts +28 -0
- package/dist/src/providers/auth/gcloud.js +102 -0
- package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
- package/dist/src/providers/auth/gemini-oauth.js +292 -105
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
- package/dist/src/providers/auth/grok-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +28 -6
- package/dist/src/providers/gemini.d.ts +1 -1
- package/dist/src/providers/gemini.js +10 -3
- package/dist/src/providers/keychain.d.ts +2 -2
- package/dist/src/providers/keychain.js +20 -2
- package/dist/src/providers/openai.js +5 -1
- package/dist/src/providers/runtime.d.ts +1 -1
- package/dist/src/providers/runtime.js +11 -2
- package/dist/src/providers/siliconflow-sse-repair.js +1 -1
- package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
- package/dist/src/providers/strategies/google.strategy.js +159 -9
- package/dist/src/providers/strategies/thinking-mode.js +19 -6
- package/dist/src/providers/types.d.ts +1 -1
- package/dist/src/router/decide.test.js +40 -8
- package/dist/src/router/step-router.d.ts +20 -0
- package/dist/src/router/step-router.js +73 -0
- package/dist/src/self-qa/agentic-loop.js +30 -21
- package/dist/src/self-qa/orchestrator.js +5 -5
- package/dist/src/self-qa/scenario-planner.js +1 -1
- package/dist/src/self-qa/spec-emitter.js +4 -4
- package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
- package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
- package/dist/src/storage/__tests__/sweep-stale-pending.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/migrations.js +13 -1
- package/dist/src/storage/transcript-response-entry.test.js +18 -5
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +3 -0
- package/dist/src/storage/transcript.js +254 -73
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +22 -5
- package/dist/src/tools/__tests__/native-tools.test.js +6 -0
- package/dist/src/tools/bash.d.ts +36 -23
- package/dist/src/tools/bash.js +59 -301
- package/dist/src/tools/bash.test.js +2 -257
- package/dist/src/tools/file-tracker.d.ts +5 -0
- package/dist/src/tools/file-tracker.js +14 -0
- package/dist/src/tools/file.js +2 -0
- package/dist/src/tools/git-safety.d.ts +38 -1
- package/dist/src/tools/git-safety.js +94 -4
- package/dist/src/tools/git-safety.test.js +45 -1
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +66 -1
- package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
- package/dist/src/tools/registry-git-safety.test.js +17 -6
- package/dist/src/tools/registry.d.ts +6 -0
- package/dist/src/tools/registry.js +215 -13
- package/dist/src/types/index.d.ts +8 -2
- package/dist/src/ui/app.d.ts +1 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.js +37 -3
- package/dist/src/ui/components/structured-response-view.js +7 -0
- package/dist/src/ui/components/task-list-panel.js +3 -12
- package/dist/src/ui/containers/chat-feed.d.ts +40 -0
- package/dist/src/ui/containers/chat-feed.js +66 -0
- package/dist/src/ui/containers/modals-layer.d.ts +87 -0
- package/dist/src/ui/containers/modals-layer.js +18 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
- package/dist/src/ui/slash/expand.js +14 -1
- package/dist/src/ui/slash/export.js +16 -1
- package/dist/src/ui/slash/menu-items.js +1 -0
- package/dist/src/ui/slash/ponytail.d.ts +2 -0
- package/dist/src/ui/slash/ponytail.js +19 -0
- package/dist/src/ui/status-bar/store.js +0 -1
- package/dist/src/ui/status-bar/store.test.js +2 -2
- package/dist/src/ui/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.js +1 -1
- package/dist/src/ui/utils/text.d.ts +11 -0
- package/dist/src/ui/utils/text.js +21 -1
- package/dist/src/ui/utils/text.test.js +25 -1
- package/dist/src/ui/utils/tools.js +3 -1
- package/dist/src/usage/cost-log.js +2 -2
- package/dist/src/usage/estimator.d.ts +14 -0
- package/dist/src/usage/estimator.js +22 -0
- package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
- package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
- package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/src/utils/__tests__/logger.test.js +115 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
- package/dist/src/utils/install-manager.d.ts +1 -0
- package/dist/src/utils/install-manager.js +76 -7
- package/dist/src/utils/install-manager.test.js +43 -1
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/logger.d.ts +28 -0
- package/dist/src/utils/logger.js +171 -0
- package/dist/src/utils/permission-mode.d.ts +33 -0
- package/dist/src/utils/permission-mode.js +164 -3
- package/dist/src/utils/permission-mode.test.js +92 -1
- package/dist/src/utils/settings.d.ts +36 -32
- package/dist/src/utils/settings.js +151 -123
- package/dist/src/utils/update-checker.test.js +5 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { iterateInterview } from "../discovery-interview.js";
|
|
7
7
|
import { initDiscoveryState, readDiscoveryState } from "../discovery-persistence.js";
|
|
8
8
|
async function mktmp() {
|
|
@@ -66,13 +66,25 @@ function makeRecommender(answers) {
|
|
|
66
66
|
describe("discovery-interview", () => {
|
|
67
67
|
let flowDir;
|
|
68
68
|
const runId = "iv-run";
|
|
69
|
+
let prevAutofill;
|
|
69
70
|
beforeEach(async () => {
|
|
71
|
+
// These tests exercise the per-question CARD flow (skip budget, FE-policy
|
|
72
|
+
// retry, leader/council dispatch). Disable G2-b auto-fill so the cards
|
|
73
|
+
// actually surface; the auto-fill path has its own test below.
|
|
74
|
+
prevAutofill = process.env.MUONROI_DISCOVERY_AUTOFILL;
|
|
75
|
+
process.env.MUONROI_DISCOVERY_AUTOFILL = "0";
|
|
70
76
|
flowDir = await mktmp();
|
|
71
77
|
await initDiscoveryState(flowDir, runId, {
|
|
72
78
|
classification: "greenfield",
|
|
73
79
|
prefillSource: { fromDetection: [], fromPrompt: [] },
|
|
74
80
|
});
|
|
75
81
|
});
|
|
82
|
+
afterEach(() => {
|
|
83
|
+
if (prevAutofill === undefined)
|
|
84
|
+
delete process.env.MUONROI_DISCOVERY_AUTOFILL;
|
|
85
|
+
else
|
|
86
|
+
process.env.MUONROI_DISCOVERY_AUTOFILL = prevAutofill;
|
|
87
|
+
});
|
|
76
88
|
it("iterates all 10 questions with leader/council dispatch", async () => {
|
|
77
89
|
const answers = {
|
|
78
90
|
productType: "saas",
|
|
@@ -288,5 +300,95 @@ describe("discovery-interview", () => {
|
|
|
288
300
|
const state = await readDiscoveryState(flowDir, runId);
|
|
289
301
|
expect((state?.answers.frontendApproach).library).toBe("shadcn");
|
|
290
302
|
});
|
|
303
|
+
// G2-b: a minimal/well-specified prompt auto-accepts the recommender primary
|
|
304
|
+
// for required questions — NO per-question cards — and surfaces ONE summary
|
|
305
|
+
// gate listing the assumptions (verified live: /ideal previously showed
|
|
306
|
+
// productType/targetPlatform/audience as 3 separate cards).
|
|
307
|
+
it("auto-fills required questions and surfaces ONE summary gate (minimal prompt)", async () => {
|
|
308
|
+
delete process.env.MUONROI_DISCOVERY_AUTOFILL; // auto-fill ON (default)
|
|
309
|
+
const answers = {
|
|
310
|
+
productType: "saas",
|
|
311
|
+
targetPlatform: ["cli"],
|
|
312
|
+
audience: { persona: "devs", scale: "1k-100k", geography: "SEA" },
|
|
313
|
+
backendArchitecture: "monolith",
|
|
314
|
+
backendStack: { language: "TS", framework: "Nest" },
|
|
315
|
+
dbStrategy: { mode: "greenfield", engine: "PG" },
|
|
316
|
+
};
|
|
317
|
+
const rec = makeRecommender(answers);
|
|
318
|
+
const promptedIds = [];
|
|
319
|
+
let gateAssumptions;
|
|
320
|
+
const userPrompt = async (args) => {
|
|
321
|
+
promptedIds.push(args.questionId);
|
|
322
|
+
if (args.questionId === "__user_gate__") {
|
|
323
|
+
gateAssumptions = args.assumptions;
|
|
324
|
+
return { action: "proceed" };
|
|
325
|
+
}
|
|
326
|
+
return { action: "accept" };
|
|
327
|
+
};
|
|
328
|
+
await iterateInterview({
|
|
329
|
+
flowDir,
|
|
330
|
+
runId,
|
|
331
|
+
idea: "build a small cli todo app",
|
|
332
|
+
capUsd: 50,
|
|
333
|
+
detection: FAKE_DETECTION,
|
|
334
|
+
userPrompt,
|
|
335
|
+
recommender: rec,
|
|
336
|
+
});
|
|
337
|
+
// No per-question card was shown — only the single summary gate.
|
|
338
|
+
expect(promptedIds.filter((id) => id !== "__user_gate__")).toEqual([]);
|
|
339
|
+
expect(promptedIds).toContain("__user_gate__");
|
|
340
|
+
// The gate carried the assumed required answers for one-shot review.
|
|
341
|
+
expect(gateAssumptions?.map((a) => a.id)).toEqual(expect.arrayContaining(["productType", "targetPlatform", "audience", "backendStack", "dbStrategy"]));
|
|
342
|
+
const state = await readDiscoveryState(flowDir, runId);
|
|
343
|
+
expect(state?.answers.productType).toBe("saas");
|
|
344
|
+
expect(state?.userGatePassed).toBe(true);
|
|
345
|
+
});
|
|
346
|
+
// G1 follow-up: from the summary gate the user can revise ONE auto-filled
|
|
347
|
+
// assumption in place (instead of abort+reprompt or AUTOFILL=0). The interview
|
|
348
|
+
// re-asks just that field, persists the new value, and re-shows the gate.
|
|
349
|
+
it("edit-field at the gate re-asks one assumption, saves the new value, then re-shows the gate", async () => {
|
|
350
|
+
delete process.env.MUONROI_DISCOVERY_AUTOFILL; // auto-fill ON
|
|
351
|
+
const answers = {
|
|
352
|
+
productType: "saas",
|
|
353
|
+
targetPlatform: ["cli"],
|
|
354
|
+
audience: { persona: "devs", scale: "1k-100k", geography: "SEA" },
|
|
355
|
+
backendArchitecture: "monolith",
|
|
356
|
+
backendStack: { language: "TS", framework: "Nest" },
|
|
357
|
+
dbStrategy: { mode: "greenfield", engine: "PG" },
|
|
358
|
+
};
|
|
359
|
+
const rec = makeRecommender(answers);
|
|
360
|
+
const gatesSeen = [];
|
|
361
|
+
let productTypeEditAsks = 0;
|
|
362
|
+
const userPrompt = async (args) => {
|
|
363
|
+
if (args.questionId === "__user_gate__") {
|
|
364
|
+
gatesSeen.push(args.assumptions);
|
|
365
|
+
// First gate → edit productType; after the edit, second gate → proceed.
|
|
366
|
+
return gatesSeen.length === 1 ? { action: "edit-field", fieldId: "productType" } : { action: "proceed" };
|
|
367
|
+
}
|
|
368
|
+
if (args.questionId === "productType" && !args.message) {
|
|
369
|
+
productTypeEditAsks += 1;
|
|
370
|
+
return { action: "override", value: "internal-tool", reason: "revised at gate" };
|
|
371
|
+
}
|
|
372
|
+
return { action: "accept" };
|
|
373
|
+
};
|
|
374
|
+
await iterateInterview({
|
|
375
|
+
flowDir,
|
|
376
|
+
runId,
|
|
377
|
+
idea: "build a small cli todo app",
|
|
378
|
+
capUsd: 50,
|
|
379
|
+
detection: FAKE_DETECTION,
|
|
380
|
+
userPrompt,
|
|
381
|
+
recommender: rec,
|
|
382
|
+
});
|
|
383
|
+
// Gate shown twice (edit, then proceed); productType re-asked exactly once.
|
|
384
|
+
expect(gatesSeen.length).toBe(2);
|
|
385
|
+
expect(productTypeEditAsks).toBe(1);
|
|
386
|
+
// The edited value is persisted (NOT the auto-filled "saas").
|
|
387
|
+
const state = await readDiscoveryState(flowDir, runId);
|
|
388
|
+
expect(state?.answers.productType).toBe("internal-tool");
|
|
389
|
+
expect(state?.userGatePassed).toBe(true);
|
|
390
|
+
// The re-shown gate reflects the edit in its assumption summary.
|
|
391
|
+
expect(gatesSeen[1]?.find((a) => a.id === "productType")?.value).toBe("internal-tool");
|
|
392
|
+
});
|
|
291
393
|
});
|
|
292
394
|
//# sourceMappingURL=discovery-interview.test.js.map
|
|
@@ -106,4 +106,38 @@ describe("buildGatherUserPrompt — selectable alternatives", () => {
|
|
|
106
106
|
expect(await prompt2({ questionId: "x", recommendation: makeRec() })).toEqual({ action: "skip" });
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
|
+
describe("buildGatherUserPrompt — user-gate per-field edit (G1 follow-up)", () => {
|
|
110
|
+
const assumptions = [
|
|
111
|
+
{ id: "productType", value: "internal-tool" },
|
|
112
|
+
{ id: "targetPlatform", value: ["cli"] },
|
|
113
|
+
];
|
|
114
|
+
it("offers proceed + one edit:<id> per assumption + ask-more + abort", async () => {
|
|
115
|
+
let captured;
|
|
116
|
+
const tuiAsk = vi.fn(async (_label, options) => {
|
|
117
|
+
captured = options;
|
|
118
|
+
return "proceed";
|
|
119
|
+
});
|
|
120
|
+
await buildGatherUserPrompt(tuiAsk)({ questionId: "__user_gate__", assumptions });
|
|
121
|
+
expect(captured).toEqual(["proceed", "edit: productType", "edit: targetPlatform", "ask-more", "abort"]);
|
|
122
|
+
});
|
|
123
|
+
it('selecting "edit: targetPlatform" returns {action:"edit-field", fieldId}', async () => {
|
|
124
|
+
const tuiAsk = vi.fn(async () => "edit: targetPlatform");
|
|
125
|
+
const result = await buildGatherUserPrompt(tuiAsk)({ questionId: "__user_gate__", assumptions });
|
|
126
|
+
expect(result).toEqual({ action: "edit-field", fieldId: "targetPlatform" });
|
|
127
|
+
});
|
|
128
|
+
it("no assumptions → no edit options (proceed/ask-more/abort only)", async () => {
|
|
129
|
+
let captured;
|
|
130
|
+
const tuiAsk = vi.fn(async (_label, options) => {
|
|
131
|
+
captured = options;
|
|
132
|
+
return "ask-more";
|
|
133
|
+
});
|
|
134
|
+
await buildGatherUserPrompt(tuiAsk)({ questionId: "__user_gate__" });
|
|
135
|
+
expect(captured).toEqual(["proceed", "ask-more", "abort"]);
|
|
136
|
+
});
|
|
137
|
+
it("proceed / abort / ask-more still map correctly with assumptions present", async () => {
|
|
138
|
+
expect(await buildGatherUserPrompt(vi.fn(async () => "proceed"))({ questionId: "__user_gate__", assumptions })).toEqual({ action: "proceed" });
|
|
139
|
+
expect(await buildGatherUserPrompt(vi.fn(async () => "abort"))({ questionId: "__user_gate__", assumptions })).toEqual({ action: "abort" });
|
|
140
|
+
expect(await buildGatherUserPrompt(vi.fn(async () => "ask-more"))({ questionId: "__user_gate__", assumptions })).toEqual({ action: "ask-more" });
|
|
141
|
+
});
|
|
142
|
+
});
|
|
109
143
|
//# sourceMappingURL=gather-selectable-alts.test.js.map
|
|
@@ -8,22 +8,22 @@
|
|
|
8
8
|
import { describe, expect, it } from "vitest";
|
|
9
9
|
import { runSprintSelfVerify } from "../sprint-self-verify.js";
|
|
10
10
|
describe("runSprintSelfVerify", () => {
|
|
11
|
-
const ORIG_CI = process.env
|
|
12
|
-
const ORIG_NODE_ENV = process.env
|
|
11
|
+
const ORIG_CI = process.env.CI;
|
|
12
|
+
const ORIG_NODE_ENV = process.env.NODE_ENV;
|
|
13
13
|
function clearEnv() {
|
|
14
|
-
delete process.env
|
|
15
|
-
delete process.env
|
|
16
|
-
delete process.env
|
|
14
|
+
delete process.env.MUONROI_SPRINT_SELF_VERIFY;
|
|
15
|
+
delete process.env.CI;
|
|
16
|
+
delete process.env.NODE_ENV;
|
|
17
17
|
}
|
|
18
18
|
function restoreEnv() {
|
|
19
19
|
if (ORIG_CI === undefined)
|
|
20
|
-
delete process.env
|
|
20
|
+
delete process.env.CI;
|
|
21
21
|
else
|
|
22
|
-
process.env
|
|
22
|
+
process.env.CI = ORIG_CI;
|
|
23
23
|
if (ORIG_NODE_ENV === undefined)
|
|
24
|
-
delete process.env
|
|
24
|
+
delete process.env.NODE_ENV;
|
|
25
25
|
else
|
|
26
|
-
process.env
|
|
26
|
+
process.env.NODE_ENV = ORIG_NODE_ENV;
|
|
27
27
|
}
|
|
28
28
|
it("default ON when no env flag set — fires past the gate", async () => {
|
|
29
29
|
clearEnv();
|
|
@@ -34,7 +34,7 @@ describe("runSprintSelfVerify", () => {
|
|
|
34
34
|
});
|
|
35
35
|
it("returns skipReason='disabled' when MUONROI_SPRINT_SELF_VERIFY=0", async () => {
|
|
36
36
|
clearEnv();
|
|
37
|
-
process.env
|
|
37
|
+
process.env.MUONROI_SPRINT_SELF_VERIFY = "0";
|
|
38
38
|
const r = await runSprintSelfVerify({ repoRoot: process.cwd() });
|
|
39
39
|
expect(r.ran).toBe(false);
|
|
40
40
|
expect(r.skipReason).toBe("disabled");
|
|
@@ -42,7 +42,7 @@ describe("runSprintSelfVerify", () => {
|
|
|
42
42
|
});
|
|
43
43
|
it("returns skipReason='disabled' when CI=true", async () => {
|
|
44
44
|
clearEnv();
|
|
45
|
-
process.env
|
|
45
|
+
process.env.CI = "true";
|
|
46
46
|
const r = await runSprintSelfVerify({ repoRoot: process.cwd() });
|
|
47
47
|
expect(r.ran).toBe(false);
|
|
48
48
|
expect(r.skipReason).toBe("disabled");
|
|
@@ -60,7 +60,7 @@ describe("runSprintSelfVerify", () => {
|
|
|
60
60
|
});
|
|
61
61
|
it("forceEnable overrides CI=true", async () => {
|
|
62
62
|
clearEnv();
|
|
63
|
-
process.env
|
|
63
|
+
process.env.CI = "true";
|
|
64
64
|
const r = await runSprintSelfVerify({
|
|
65
65
|
repoRoot: process.cwd(),
|
|
66
66
|
baseRef: "HEAD",
|
|
@@ -14,6 +14,16 @@ export type UserPromptResult = {
|
|
|
14
14
|
action: "proceed";
|
|
15
15
|
} | {
|
|
16
16
|
action: "ask-more";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* G1 follow-up: from the `__user_gate__` summary card the user picked one
|
|
20
|
+
* auto-filled assumption to revise. The interview re-asks that single field
|
|
21
|
+
* (reusing the per-question card) and re-shows the gate — instead of forcing
|
|
22
|
+
* an abort-and-reprompt or MUONROI_DISCOVERY_AUTOFILL=0 to change one value.
|
|
23
|
+
*/
|
|
24
|
+
| {
|
|
25
|
+
action: "edit-field";
|
|
26
|
+
fieldId: string;
|
|
17
27
|
} | {
|
|
18
28
|
action: "abort";
|
|
19
29
|
};
|
|
@@ -22,6 +32,16 @@ export interface UserPromptArgs {
|
|
|
22
32
|
recommendation?: RecommendOutput;
|
|
23
33
|
prefilled?: any;
|
|
24
34
|
message?: string;
|
|
35
|
+
/**
|
|
36
|
+
* G2-b: when the interview auto-filled required questions from the
|
|
37
|
+
* recommender (minimal/well-specified prompt), the `__user_gate__` card
|
|
38
|
+
* carries the assumed answers so it can render ONE summary ("I assumed X/Y/Z
|
|
39
|
+
* — proceed or adjust?") instead of N sequential per-question cards.
|
|
40
|
+
*/
|
|
41
|
+
assumptions?: Array<{
|
|
42
|
+
id: string;
|
|
43
|
+
value: any;
|
|
44
|
+
}>;
|
|
25
45
|
}
|
|
26
46
|
export type UserPromptFn = (args: UserPromptArgs) => Promise<UserPromptResult>;
|
|
27
47
|
export interface RecommenderLike {
|
|
@@ -21,6 +21,21 @@ export async function iterateInterview(opts) {
|
|
|
21
21
|
// not web) are deferred unless the user explicitly re-runs with more context.
|
|
22
22
|
const specificity = computePromptSpecificity(opts.idea);
|
|
23
23
|
const skipOptionalForMinimal = specificity === "minimal";
|
|
24
|
+
// G2-b: for a minimal OR well-specified ("detailed") prompt the recommender's
|
|
25
|
+
// primary is high-confidence — minimal picks smallest-scope defaults, detailed
|
|
26
|
+
// respects the stated context. In both cases surfacing a card for EVERY
|
|
27
|
+
// required question (productType/targetPlatform/audience/…) is over-asking the
|
|
28
|
+
// user already complained about: they accept by reflex. Instead auto-accept
|
|
29
|
+
// the recommender primary for required questions and surface ONE summary card
|
|
30
|
+
// (the user gate) listing the assumptions so the user can proceed or adjust.
|
|
31
|
+
// "moderate" prompts keep the per-question cards (genuinely ambiguous). Escape
|
|
32
|
+
// hatch: MUONROI_DISCOVERY_AUTOFILL=0 restores per-question cards everywhere.
|
|
33
|
+
const autoFillRequired = (specificity === "minimal" || specificity === "detailed") && process.env.MUONROI_DISCOVERY_AUTOFILL !== "0";
|
|
34
|
+
const assumed = [];
|
|
35
|
+
// G1 follow-up: keep the recommendation behind each auto-filled assumption so
|
|
36
|
+
// the user-gate "edit: <field>" path can re-render the SAME per-question card
|
|
37
|
+
// (Recommended + alternatives) without a second recommender LLM call.
|
|
38
|
+
const assumedRec = new Map();
|
|
24
39
|
// Build the repo brief ONCE per interview run for existing projects. The
|
|
25
40
|
// brief replaces the Muonroi vendor preamble inside leader prompts so
|
|
26
41
|
// rationales must cite real files, deps, and scripts. Skipped for greenfield —
|
|
@@ -74,12 +89,33 @@ export async function iterateInterview(opts) {
|
|
|
74
89
|
else {
|
|
75
90
|
recommendation = await opts.recommender.leaderRecommend(recInput);
|
|
76
91
|
}
|
|
92
|
+
// G2-b: auto-accept the recommender primary for required questions on a
|
|
93
|
+
// minimal/well-specified prompt — no per-question card. Validated (+ FE
|
|
94
|
+
// policy) so a malformed recommendation falls back to the normal card flow.
|
|
95
|
+
// The assumed answers are surfaced together on the single user-gate card.
|
|
96
|
+
let autoAccepted = false;
|
|
97
|
+
if (autoFillRequired && effectivelyRequired && recommendation.primary?.value != null) {
|
|
98
|
+
const v = recommendation.primary.value;
|
|
99
|
+
const validation = validateAnswer(question.id, v);
|
|
100
|
+
const feLib = question.id === "frontendApproach" ? v?.library : undefined;
|
|
101
|
+
const fePolicyOk = !feLib || isFePolicyAccepted(feLib);
|
|
102
|
+
if (validation.ok && fePolicyOk) {
|
|
103
|
+
await recordRecommendation(flowDir, runId, question.id, toEntry(recommendation), recommendation.costUsd);
|
|
104
|
+
await saveDiscoveryAnswer(flowDir, runId, question.id, v);
|
|
105
|
+
assumed.push({ id: question.id, value: v });
|
|
106
|
+
assumedRec.set(question.id, recommendation);
|
|
107
|
+
autoAccepted = true;
|
|
108
|
+
// Fall through to the post-answer user-gate check below (skip the card).
|
|
109
|
+
}
|
|
110
|
+
}
|
|
77
111
|
// Per-question skip-attempt counter for required questions.
|
|
78
112
|
// Resets when the user provides a real answer (accept/override).
|
|
79
113
|
let skipAttempts = 0;
|
|
80
114
|
const MAX_SKIP_ATTEMPTS = 3;
|
|
81
115
|
const _debugInterview = process.env.MUONROI_DEBUG_LEADER === "1";
|
|
82
|
-
|
|
116
|
+
// When auto-accepted above, the card loop body is skipped entirely (the
|
|
117
|
+
// for-condition is false on entry) and we go straight to the gate check.
|
|
118
|
+
for (; !autoAccepted;) {
|
|
83
119
|
const _iterStart = Date.now();
|
|
84
120
|
if (_debugInterview) {
|
|
85
121
|
process.stderr.write(`[interview-timing] iter-start: ${JSON.stringify({ questionId: question.id, skipAttempts })}\n`);
|
|
@@ -176,14 +212,32 @@ export async function iterateInterview(opts) {
|
|
|
176
212
|
if (refreshed &&
|
|
177
213
|
allRequiredAnswered(refreshed.questionsAnswered, refreshedPlatforms) &&
|
|
178
214
|
!refreshed.userGatePassed) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
215
|
+
// Gate loop: "proceed" exits the whole interview, "abort" throws,
|
|
216
|
+
// "edit-field" re-asks one assumed answer then RE-shows the gate (so the
|
|
217
|
+
// user can revise several before proceeding), "ask-more" falls through to
|
|
218
|
+
// keep iterating optional questions.
|
|
219
|
+
let proceeded = false;
|
|
220
|
+
for (;;) {
|
|
221
|
+
const gate = await opts.userPrompt({
|
|
222
|
+
questionId: "__user_gate__",
|
|
223
|
+
assumptions: assumed.length > 0 ? assumed : undefined,
|
|
224
|
+
});
|
|
225
|
+
if (gate.action === "proceed") {
|
|
226
|
+
await markUserGatePassed(flowDir, runId);
|
|
227
|
+
proceeded = true;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
if (gate.action === "abort")
|
|
231
|
+
throw new Error("discovery aborted at user gate");
|
|
232
|
+
if (gate.action === "edit-field") {
|
|
233
|
+
await reAskAssumedField(opts, flowDir, runId, gate.fieldId, assumed, assumedRec.get(gate.fieldId));
|
|
234
|
+
continue; // re-render the gate with the updated assumption
|
|
235
|
+
}
|
|
236
|
+
// ask-more: stop gating, continue iterating optional questions
|
|
182
237
|
break;
|
|
183
238
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
// ask-more: continue iterating optional questions
|
|
239
|
+
if (proceeded)
|
|
240
|
+
break;
|
|
187
241
|
}
|
|
188
242
|
}
|
|
189
243
|
const finalState = await readDiscoveryState(flowDir, runId);
|
|
@@ -197,6 +251,66 @@ export async function iterateInterview(opts) {
|
|
|
197
251
|
await markDone(flowDir, runId);
|
|
198
252
|
return ctx;
|
|
199
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* G1 follow-up: re-ask ONE auto-filled assumption from the user gate. Renders
|
|
256
|
+
* the same per-question card (Recommended + alternatives) as the normal flow by
|
|
257
|
+
* reusing the stored recommendation, validates (+ FE policy), persists the new
|
|
258
|
+
* answer, and updates the in-memory `assumed` list so the re-rendered gate shows
|
|
259
|
+
* the revised value. "skip" cancels the edit (keeps the current assumption);
|
|
260
|
+
* "abort" aborts the whole interview, consistent with the per-question card.
|
|
261
|
+
*/
|
|
262
|
+
async function reAskAssumedField(opts, flowDir, runId, fieldId, assumed, recommendation) {
|
|
263
|
+
const question = DISCOVERY_QUESTIONS.find((q) => q.id === fieldId);
|
|
264
|
+
if (!question)
|
|
265
|
+
return; // unknown field id — ignore defensively, keep current value
|
|
266
|
+
for (;;) {
|
|
267
|
+
const ans = await opts.userPrompt({ questionId: fieldId, recommendation });
|
|
268
|
+
if (ans.action === "abort")
|
|
269
|
+
throw new Error("discovery aborted by user");
|
|
270
|
+
let chosenValue;
|
|
271
|
+
if (ans.action === "accept") {
|
|
272
|
+
if (recommendation?.primary?.value == null)
|
|
273
|
+
continue; // nothing to accept — re-prompt
|
|
274
|
+
chosenValue = recommendation.primary.value;
|
|
275
|
+
}
|
|
276
|
+
else if (ans.action === "override") {
|
|
277
|
+
chosenValue = ans.value;
|
|
278
|
+
}
|
|
279
|
+
else if (ans.action === "skip") {
|
|
280
|
+
return; // user backed out of editing this field — keep the existing assumption
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
continue; // more-options / edit-field / proceed / ask-more are no-ops here
|
|
284
|
+
}
|
|
285
|
+
const validation = validateAnswer(fieldId, chosenValue);
|
|
286
|
+
if (!validation.ok) {
|
|
287
|
+
if (fieldId !== "frontendApproach") {
|
|
288
|
+
await opts.userPrompt({ questionId: fieldId, message: validation.reason ?? "invalid answer" });
|
|
289
|
+
}
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (fieldId === "frontendApproach") {
|
|
293
|
+
const lib = chosenValue?.library;
|
|
294
|
+
if (lib && !isFePolicyAccepted(lib)) {
|
|
295
|
+
await opts.userPrompt({
|
|
296
|
+
questionId: fieldId,
|
|
297
|
+
message: "FE policy: library must be shadcn, radix, headlessui, or none",
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (ans.action === "override" && recommendation) {
|
|
303
|
+
await appendUserOverride(flowDir, runId, fieldId, recommendation.primary.value, chosenValue, ans.reason);
|
|
304
|
+
}
|
|
305
|
+
await saveDiscoveryAnswer(flowDir, runId, fieldId, chosenValue);
|
|
306
|
+
const entry = assumed.find((a) => a.id === fieldId);
|
|
307
|
+
if (entry)
|
|
308
|
+
entry.value = chosenValue;
|
|
309
|
+
else
|
|
310
|
+
assumed.push({ id: fieldId, value: chosenValue });
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
200
314
|
function allRequiredAnswered(answered, platforms) {
|
|
201
315
|
const baseRequired = REQUIRED_QUESTION_IDS.every((id) => answered.includes(id));
|
|
202
316
|
if (!baseRequired)
|
|
@@ -93,15 +93,29 @@ function buildLiveTuiAsk(emit, respondToQuestion) {
|
|
|
93
93
|
export function buildGatherUserPrompt(tuiAsk) {
|
|
94
94
|
return async (args) => {
|
|
95
95
|
if (args.questionId === "__user_gate__") {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
// G2-b: when required answers were auto-filled from the recommender
|
|
97
|
+
// (minimal/well-specified prompt), this single card IS the summary — list
|
|
98
|
+
// the assumptions so the user reviews them in one place instead of N
|
|
99
|
+
// sequential per-question cards. "ask-more" lets them revisit/expand.
|
|
100
|
+
const hasAssumptions = !!args.assumptions && args.assumptions.length > 0;
|
|
101
|
+
const label = hasAssumptions
|
|
102
|
+
? `Assumed from your prompt:\n${args
|
|
103
|
+
.assumptions.map((a) => ` • ${a.id} = ${JSON.stringify(a.value)}`)
|
|
104
|
+
.join("\n")}\n\nProceed, edit one of the assumptions, or ask more to adjust/expand?`
|
|
105
|
+
: "All required questions answered. Proceed to research or ask more?";
|
|
106
|
+
// G1 follow-up: one "edit: <field>" option per auto-filled assumption so a
|
|
107
|
+
// single wrong default can be revised in place — no abort+reprompt or
|
|
108
|
+
// MUONROI_DISCOVERY_AUTOFILL=0. The prefix must match the parse below.
|
|
109
|
+
const EDIT_PREFIX = "edit: ";
|
|
110
|
+
const editOpts = hasAssumptions ? args.assumptions.map((a) => `${EDIT_PREFIX}${a.id}`) : [];
|
|
111
|
+
const choice = await tuiAsk(label, ["proceed", ...editOpts, "ask-more", "abort"]);
|
|
101
112
|
if (choice === "proceed")
|
|
102
113
|
return { action: "proceed" };
|
|
103
114
|
if (choice === "abort")
|
|
104
115
|
return { action: "abort" };
|
|
116
|
+
if (choice.startsWith(EDIT_PREFIX)) {
|
|
117
|
+
return { action: "edit-field", fieldId: choice.slice(EDIT_PREFIX.length) };
|
|
118
|
+
}
|
|
105
119
|
return { action: "ask-more" };
|
|
106
120
|
}
|
|
107
121
|
if (args.message) {
|
|
@@ -39,10 +39,10 @@ function wrapLLMForUsageTracking(llm, ctx) {
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
return {
|
|
42
|
-
generate: (modelId, system, prompt, maxTokens, onUsage) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
|
|
42
|
+
generate: (modelId, system, prompt, maxTokens, onUsage, signal) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
|
|
43
43
|
recorder(modelId)(u);
|
|
44
44
|
onUsage?.(u);
|
|
45
|
-
}),
|
|
45
|
+
}, signal),
|
|
46
46
|
debate: (modelId, system, prompt, signal, persistTrace, options, onUsage) => llm.debate(modelId, system, prompt, signal, persistTrace, options, (u) => {
|
|
47
47
|
recorder(modelId)(u);
|
|
48
48
|
onUsage?.(u);
|
|
@@ -31,10 +31,10 @@ const WATCH_DIRS = ["src/ui/", "src/self-qa/", "src/agent-harness/", "packages/a
|
|
|
31
31
|
function isEnabled(forceEnable) {
|
|
32
32
|
if (forceEnable === true)
|
|
33
33
|
return true;
|
|
34
|
-
const v = process.env
|
|
34
|
+
const v = process.env.MUONROI_SPRINT_SELF_VERIFY;
|
|
35
35
|
if (v === "0" || (v && v.toLowerCase() === "false"))
|
|
36
36
|
return false;
|
|
37
|
-
if (process.env
|
|
37
|
+
if (process.env.CI === "true" || process.env.NODE_ENV === "ci")
|
|
38
38
|
return false;
|
|
39
39
|
// Default: ON. v === "1"/"true" still works; absence of env var also enables.
|
|
40
40
|
return true;
|
|
@@ -179,6 +179,18 @@ describe("transformThinkingModeBody — backfill / disable (code 20015 fix)", ()
|
|
|
179
179
|
expect(asst[0].reasoning_content).toBe("real thought"); // untouched
|
|
180
180
|
expect(asst[1].reasoning_content).toBe(""); // backfilled
|
|
181
181
|
});
|
|
182
|
+
it("A (default): backfills content: '' on a reasoning-only turn that lacks content and tool_calls", () => {
|
|
183
|
+
const body = {
|
|
184
|
+
messages: [
|
|
185
|
+
{ role: "user", content: "go" },
|
|
186
|
+
{ role: "assistant", content: null, reasoning_content: "only thought" },
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
const out = transformThinkingModeBody(body);
|
|
190
|
+
const asst = out.messages.find((m) => m.role === "assistant");
|
|
191
|
+
expect(asst.reasoning_content).toBe("only thought");
|
|
192
|
+
expect(asst.content).toBe("");
|
|
193
|
+
});
|
|
182
194
|
it("A (default): does not touch non-assistant messages", () => {
|
|
183
195
|
const body = {
|
|
184
196
|
messages: [
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for
|
|
3
|
-
*
|
|
4
|
-
* Login flow (callback server mocked), refresh (expiry pre-emption + mutex),
|
|
5
|
-
* revoke, authHeaders, loadGeminiTokensWithRefresh.
|
|
6
|
-
* No live network calls — all HTTP via mockFetch, no real browser opens.
|
|
2
|
+
* Tests for Agy Google OAuth (replaces legacy gemini public client)
|
|
7
3
|
*/
|
|
8
4
|
export {};
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for
|
|
3
|
-
*
|
|
4
|
-
* Login flow (callback server mocked), refresh (expiry pre-emption + mutex),
|
|
5
|
-
* revoke, authHeaders, loadGeminiTokensWithRefresh.
|
|
6
|
-
* No live network calls — all HTTP via mockFetch, no real browser opens.
|
|
2
|
+
* Tests for Agy Google OAuth (replaces legacy gemini public client)
|
|
7
3
|
*/
|
|
8
4
|
import { describe, expect, it, vi } from "vitest";
|
|
9
5
|
import { GeminiOAuthProvider, loadGeminiTokensWithRefresh } from "../gemini-oauth.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/providers/auth/gcloud.ts
|
|
3
|
+
*
|
|
4
|
+
* Reads and refreshes tokens from Google Cloud SDK credentials
|
|
5
|
+
* (~/.config/gcloud/application_default_credentials.json).
|
|
6
|
+
*
|
|
7
|
+
* The gcloud ADC token uses GCP's official OAuth client with cloud-platform
|
|
8
|
+
* scopes, which IS accepted by generativelanguage.googleapis.com (unlike the
|
|
9
|
+
* Agy OAuth token whose scope is restricted to cloudcode-pa).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* 1. User runs `gcloud auth application-default login` once.
|
|
13
|
+
* 2. This file reads the resulting credentials file, extracts the token,
|
|
14
|
+
* and returns it as an OAuthTokens-compatible object.
|
|
15
|
+
* 3. The token auto-refreshes when expired (using the refresh_token inside
|
|
16
|
+
* the credentials file).
|
|
17
|
+
*/
|
|
18
|
+
import type { OAuthTokens } from "./types.js";
|
|
19
|
+
/**
|
|
20
|
+
* Try to load a token from gcloud ADC.
|
|
21
|
+
* Returns null if gcloud is not installed or no credentials exist.
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadGcloudToken(): Promise<OAuthTokens | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Obtain auth headers from a gcloud-derived OAuthTokens.
|
|
26
|
+
* Compatible with ProviderOAuth.authHeaders() signature.
|
|
27
|
+
*/
|
|
28
|
+
export declare function gcloudAuthHeaders(tokens: OAuthTokens): Record<string, string>;
|