muonroi-cli 1.7.2 → 1.8.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 +21 -21
- package/README.md +122 -122
- 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 +32 -3
- 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 +12 -12
- 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 +56 -21
- 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/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 +55 -60
- 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 +2 -2
- package/dist/src/lsp/manager.test.js +2 -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/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/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/setup-guide-text.js +74 -74
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/ops/doctor.js +7 -7
- 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 +2 -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 +1 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -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 +364 -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 +21 -5
- package/dist/src/orchestrator/message-processor.js +219 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +8 -2
- package/dist/src/orchestrator/orchestrator.js +187 -34
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +145 -0
- package/dist/src/orchestrator/prompts.d.ts +8 -0
- package/dist/src/orchestrator/prompts.js +76 -60
- 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-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
- package/dist/src/orchestrator/subagent-compactor.js +83 -10
- package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
- 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 +151 -0
- package/dist/src/orchestrator/tool-engine.js +3079 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -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__/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 +99 -44
- 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.js +3 -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/done-gate.js +3 -3
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +20 -20
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- 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/mcp-vision-bridge.js +48 -48
- 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/reporter/index.js +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/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +19 -19
- 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__/migrations.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/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.js +43 -43
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +1 -0
- package/dist/src/storage/transcript.js +55 -0
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- 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 +56 -0
- 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 +5 -0
- package/dist/src/tools/registry.js +185 -11
- package/dist/src/types/index.d.ts +7 -1
- 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/council-inspect.js +4 -4
- 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/clipboard-image.js +23 -23
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- 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 +27 -31
- package/dist/src/utils/settings.js +100 -109
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ export interface TurnRunnerDepsBase {
|
|
|
35
35
|
runDelegation(request: TaskRequest, signal?: AbortSignal): Promise<ToolResult>;
|
|
36
36
|
readDelegation(id: string): Promise<ToolResult>;
|
|
37
37
|
listDelegations(): Promise<ToolResult>;
|
|
38
|
+
killDelegation(id: string): Promise<ToolResult>;
|
|
38
39
|
appendCompletedTurn(userMessage: ModelMessage, assistantMessages: ModelMessage[]): void;
|
|
39
40
|
discardAbortedTurn(userMessage: ModelMessage): void;
|
|
40
41
|
recordUsage(usage: {
|
|
@@ -16,11 +16,20 @@ describe("discovery-types", () => {
|
|
|
16
16
|
};
|
|
17
17
|
expect(ctx.language).toBe("typescript");
|
|
18
18
|
});
|
|
19
|
-
it("
|
|
20
|
-
const
|
|
21
|
-
|
|
19
|
+
it("ModelCard has all required fields", () => {
|
|
20
|
+
const card = {
|
|
21
|
+
question: "What auth method?",
|
|
22
|
+
options: [
|
|
23
|
+
{ label: "OAuth", kind: "choice", isCancel: false, isAdjust: false },
|
|
24
|
+
{ label: "API keys", kind: "choice" },
|
|
25
|
+
{ label: "Custom", kind: "freetext" },
|
|
26
|
+
],
|
|
27
|
+
defaultIndex: 0,
|
|
28
|
+
};
|
|
29
|
+
expect(card.question).toBe("What auth method?");
|
|
30
|
+
expect(card.options).toHaveLength(3);
|
|
22
31
|
});
|
|
23
|
-
it("DiscoveryResult has all required fields", () => {
|
|
32
|
+
it("DiscoveryResult has all required fields including interviewTranscript", () => {
|
|
24
33
|
const result = {
|
|
25
34
|
raw: "fix auth",
|
|
26
35
|
projectContext: {
|
|
@@ -47,8 +56,10 @@ describe("discovery-types", () => {
|
|
|
47
56
|
domain: "typescript",
|
|
48
57
|
outputStyle: "balanced",
|
|
49
58
|
discoveryMs: 100,
|
|
59
|
+
interviewTranscript: [],
|
|
50
60
|
};
|
|
51
61
|
expect(result.accepted).toBe(true);
|
|
62
|
+
expect(result.interviewTranscript).toEqual([]);
|
|
52
63
|
});
|
|
53
64
|
});
|
|
54
65
|
//# sourceMappingURL=discovery-types.test.js.map
|
|
@@ -7,15 +7,11 @@ import { clearDiscoveryCache } from "../discovery-cache.js";
|
|
|
7
7
|
afterEach(() => clearDiscoveryCache());
|
|
8
8
|
const mockHandler = {
|
|
9
9
|
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "Error disappears", kind: "choice" }),
|
|
10
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
11
10
|
};
|
|
12
11
|
describe("runDiscovery()", () => {
|
|
13
|
-
it("proceeds without interview when the model proposes no
|
|
14
|
-
// Phase 2: the model is the sole ask-decider. An empty proposer result means
|
|
15
|
-
// "no gray area" → no interview, no fabricated [Discovery] outcome.
|
|
12
|
+
it("proceeds without interview when the model proposes no cards", async () => {
|
|
16
13
|
const handler = {
|
|
17
14
|
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "x", kind: "choice" }),
|
|
18
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
19
15
|
};
|
|
20
16
|
const proposer = vi.fn().mockResolvedValue([]);
|
|
21
17
|
const result = await runDiscovery("fix TypeError in src/auth/login.ts:42", {
|
|
@@ -31,15 +27,11 @@ describe("runDiscovery()", () => {
|
|
|
31
27
|
expect(result.accepted).toBe(true);
|
|
32
28
|
expect(handler.askQuestion).not.toHaveBeenCalled();
|
|
33
29
|
});
|
|
34
|
-
it("does NOT interview
|
|
35
|
-
// Phase 2 fail-loud: an interactive turn missing a proposer logs and proceeds
|
|
36
|
-
// WITHOUT an interview — it must never fall back to keyword-generated gaps.
|
|
30
|
+
it("does NOT interview when no proposer is wired", async () => {
|
|
37
31
|
const handler = {
|
|
38
32
|
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "x", kind: "choice" }),
|
|
39
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
40
33
|
};
|
|
41
|
-
const result = await runDiscovery("fix auth",
|
|
42
|
-
{
|
|
34
|
+
const result = await runDiscovery("fix auth", {
|
|
43
35
|
taskType: "debug",
|
|
44
36
|
confidence: 0.6,
|
|
45
37
|
complexity: "low",
|
|
@@ -51,16 +43,61 @@ describe("runDiscovery()", () => {
|
|
|
51
43
|
expect(result.accepted).toBe(true);
|
|
52
44
|
expect(handler.askQuestion).not.toHaveBeenCalled();
|
|
53
45
|
});
|
|
54
|
-
it("
|
|
55
|
-
const askQuestion = vi.fn().mockResolvedValue({ questionId: "q1", text: "
|
|
56
|
-
const handler = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
it("interviews user when the model proposes cards", async () => {
|
|
47
|
+
const askQuestion = vi.fn().mockResolvedValue({ questionId: "q1", text: "Error disappears", kind: "choice" });
|
|
48
|
+
const handler = { askQuestion };
|
|
49
|
+
const modelCards = [
|
|
50
|
+
{
|
|
51
|
+
question: "What outcome do you expect?",
|
|
52
|
+
options: [
|
|
53
|
+
{ label: "Error disappears", kind: "choice" },
|
|
54
|
+
{ label: "Not sure", kind: "freetext" },
|
|
55
|
+
],
|
|
56
|
+
defaultIndex: 0,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const proposer = vi.fn().mockResolvedValue(modelCards);
|
|
60
|
+
const result = await runDiscovery("fix auth", {
|
|
61
|
+
taskType: "debug",
|
|
62
|
+
confidence: 0.6,
|
|
63
|
+
complexity: "low",
|
|
64
|
+
domain: "typescript",
|
|
65
|
+
outputStyle: null,
|
|
66
|
+
intentKind: "task",
|
|
67
|
+
}, process.cwd(), handler, null, proposer);
|
|
68
|
+
expect(result.interviewed).toBe(true);
|
|
69
|
+
expect(result.accepted).toBe(true);
|
|
70
|
+
expect(handler.askQuestion).toHaveBeenCalled();
|
|
71
|
+
expect(result.interviewTranscript).toHaveLength(1);
|
|
72
|
+
expect(result.interviewTranscript[0].question).toBe("What outcome do you expect?");
|
|
73
|
+
expect(result.interviewTranscript[0].answer).toBe("Error disappears");
|
|
74
|
+
});
|
|
75
|
+
it("records multiple cards in the interview transcript", async () => {
|
|
76
|
+
const askQuestion = vi
|
|
61
77
|
.fn()
|
|
62
|
-
.
|
|
63
|
-
|
|
78
|
+
.mockResolvedValueOnce({ questionId: "q1", text: "OAuth", kind: "choice" })
|
|
79
|
+
.mockResolvedValueOnce({ questionId: "q2", text: "src/auth/", kind: "choice" });
|
|
80
|
+
const handler = { askQuestion };
|
|
81
|
+
const modelCards = [
|
|
82
|
+
{
|
|
83
|
+
question: "Which auth method?",
|
|
84
|
+
options: [
|
|
85
|
+
{ label: "OAuth", kind: "choice" },
|
|
86
|
+
{ label: "API Keys", kind: "choice" },
|
|
87
|
+
],
|
|
88
|
+
defaultIndex: 0,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
question: "Which module?",
|
|
92
|
+
options: [
|
|
93
|
+
{ label: "src/auth/", kind: "choice" },
|
|
94
|
+
{ label: "src/user/", kind: "choice" },
|
|
95
|
+
],
|
|
96
|
+
defaultIndex: 0,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
const proposer = vi.fn().mockResolvedValue(modelCards);
|
|
100
|
+
const result = await runDiscovery("add authentication", {
|
|
64
101
|
taskType: "generate",
|
|
65
102
|
confidence: 0.6,
|
|
66
103
|
complexity: "low",
|
|
@@ -68,24 +105,45 @@ describe("runDiscovery()", () => {
|
|
|
68
105
|
outputStyle: null,
|
|
69
106
|
intentKind: "task",
|
|
70
107
|
}, process.cwd(), handler, null, proposer);
|
|
71
|
-
expect(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
expect(
|
|
75
|
-
expect(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
108
|
+
expect(result.interviewTranscript).toHaveLength(2);
|
|
109
|
+
expect(result.interviewTranscript[0].question).toBe("Which auth method?");
|
|
110
|
+
expect(result.interviewTranscript[0].answer).toBe("OAuth");
|
|
111
|
+
expect(result.interviewTranscript[1].question).toBe("Which module?");
|
|
112
|
+
expect(result.interviewTranscript[1].answer).toBe("src/auth/");
|
|
113
|
+
});
|
|
114
|
+
it("sets accepted=false when user picks a cancel option", async () => {
|
|
115
|
+
const askQuestion = vi
|
|
116
|
+
.fn()
|
|
117
|
+
.mockResolvedValueOnce({ questionId: "q1", text: "Cancel this request", kind: "choice" });
|
|
118
|
+
const handler = { askQuestion };
|
|
119
|
+
const modelCards = [
|
|
120
|
+
{
|
|
121
|
+
question: "What outcome?",
|
|
122
|
+
options: [
|
|
123
|
+
{ label: "Proceed", kind: "choice" },
|
|
124
|
+
{ label: "Cancel this request", kind: "choice", isCancel: true },
|
|
125
|
+
],
|
|
126
|
+
defaultIndex: 0,
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
const proposer = vi.fn().mockResolvedValue(modelCards);
|
|
130
|
+
const result = await runDiscovery("fix auth", {
|
|
131
|
+
taskType: "debug",
|
|
132
|
+
confidence: 0.6,
|
|
133
|
+
complexity: "low",
|
|
134
|
+
domain: "typescript",
|
|
135
|
+
outputStyle: null,
|
|
136
|
+
intentKind: "task",
|
|
137
|
+
}, process.cwd(), handler, null, proposer);
|
|
138
|
+
expect(result.accepted).toBe(false);
|
|
80
139
|
});
|
|
81
140
|
it("skips all discovery when the user explicitly says don't ask (EN + VI)", async () => {
|
|
82
141
|
const handler = {
|
|
83
142
|
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "x", kind: "choice" }),
|
|
84
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
85
143
|
};
|
|
86
144
|
const l1 = {
|
|
87
145
|
taskType: "analyze",
|
|
88
|
-
confidence: 0.6,
|
|
146
|
+
confidence: 0.6,
|
|
89
147
|
complexity: "low",
|
|
90
148
|
domain: null,
|
|
91
149
|
outputStyle: null,
|
|
@@ -99,24 +157,6 @@ describe("runDiscovery()", () => {
|
|
|
99
157
|
expect(viResult.accepted).toBe(true);
|
|
100
158
|
expect(handler.askQuestion).not.toHaveBeenCalled();
|
|
101
159
|
});
|
|
102
|
-
it("interviews user when the model proposes a question", async () => {
|
|
103
|
-
const handler = {
|
|
104
|
-
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "Error disappears", kind: "choice" }),
|
|
105
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
106
|
-
};
|
|
107
|
-
const proposer = vi.fn().mockResolvedValue(["What's the expected fix outcome? [MODEL RECS: Error disappears]"]);
|
|
108
|
-
const result = await runDiscovery("fix auth", {
|
|
109
|
-
taskType: "debug",
|
|
110
|
-
confidence: 0.6,
|
|
111
|
-
complexity: "low",
|
|
112
|
-
domain: "typescript",
|
|
113
|
-
outputStyle: null,
|
|
114
|
-
intentKind: "task",
|
|
115
|
-
}, process.cwd(), handler, null, proposer);
|
|
116
|
-
expect(result.interviewed).toBe(true);
|
|
117
|
-
expect(result.accepted).toBe(true);
|
|
118
|
-
expect(handler.askQuestion).toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
160
|
it("skips interview but still passes when handler is null (headless)", async () => {
|
|
121
161
|
const result = await runDiscovery("fix auth", {
|
|
122
162
|
taskType: "debug",
|
|
@@ -129,106 +169,10 @@ describe("runDiscovery()", () => {
|
|
|
129
169
|
expect(result.interviewed).toBe(false);
|
|
130
170
|
expect(result.accepted).toBe(true);
|
|
131
171
|
});
|
|
132
|
-
it("sets accepted=false when user cancels", async () => {
|
|
133
|
-
const handler = {
|
|
134
|
-
// First askQuestion = the model's interview question, second = acceptance card.
|
|
135
|
-
askQuestion: vi
|
|
136
|
-
.fn()
|
|
137
|
-
.mockResolvedValueOnce({ questionId: "q1", text: "done", kind: "choice" })
|
|
138
|
-
.mockResolvedValue({ questionId: "q-acc", text: "cancel", kind: "choice" }),
|
|
139
|
-
showAcceptance: vi.fn().mockResolvedValue("cancel"),
|
|
140
|
-
};
|
|
141
|
-
const proposer = vi.fn().mockResolvedValue(["What's the expected outcome? [MODEL RECS: Error disappears]"]);
|
|
142
|
-
const result = await runDiscovery("fix auth", {
|
|
143
|
-
taskType: "debug",
|
|
144
|
-
confidence: 0.6,
|
|
145
|
-
complexity: "low",
|
|
146
|
-
domain: "typescript",
|
|
147
|
-
outputStyle: null,
|
|
148
|
-
intentKind: "task",
|
|
149
|
-
}, process.cwd(), handler, null, proposer);
|
|
150
|
-
expect(result.accepted).toBe(false);
|
|
151
|
-
});
|
|
152
|
-
it("does not swallow the original request into a generic outcome for a general prompt (B2)", async () => {
|
|
153
|
-
// B2 — the old generic outcome askcard collapsed intent to "general: Task
|
|
154
|
-
// completed", discarding the user's prompt. With the model proposing no
|
|
155
|
-
// questions, the outcome must derive from the raw text (no fabrication).
|
|
156
|
-
const handler = {
|
|
157
|
-
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text: "Task completed", kind: "choice" }),
|
|
158
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
159
|
-
};
|
|
160
|
-
const proposer = vi.fn().mockResolvedValue([]);
|
|
161
|
-
const result = await runDiscovery("make the dashboard feel less cluttered", {
|
|
162
|
-
taskType: "general",
|
|
163
|
-
confidence: 0.6,
|
|
164
|
-
complexity: "low",
|
|
165
|
-
domain: null,
|
|
166
|
-
outputStyle: null,
|
|
167
|
-
intentKind: "task",
|
|
168
|
-
}, process.cwd(), handler, null, proposer);
|
|
169
|
-
expect(result.intentStatement).not.toBe("general: Task completed");
|
|
170
|
-
expect(result.outcome).not.toBe("Task completed");
|
|
171
|
-
// The original request must survive into the resolved outcome.
|
|
172
|
-
expect(result.outcome.toLowerCase()).toContain("dashboard");
|
|
173
|
-
});
|
|
174
172
|
it("skips discovery entirely for chitchat", async () => {
|
|
175
173
|
const result = await runDiscovery("hi", { taskType: null, confidence: 0.5, complexity: "low", domain: null, outputStyle: null, intentKind: "chitchat" }, process.cwd(), mockHandler);
|
|
176
174
|
expect(result.interviewed).toBe(false);
|
|
177
175
|
expect(result.accepted).toBe(true);
|
|
178
176
|
});
|
|
179
177
|
});
|
|
180
|
-
describe("runDiscovery() — outcome autofill override (path-leak vs legit slash)", () => {
|
|
181
|
-
const analyzeL1 = {
|
|
182
|
-
taskType: "analyze",
|
|
183
|
-
confidence: 0.6,
|
|
184
|
-
complexity: "low",
|
|
185
|
-
domain: null,
|
|
186
|
-
outputStyle: null,
|
|
187
|
-
intentKind: "task",
|
|
188
|
-
};
|
|
189
|
-
// A handler that always picks `text` for both the interview answer and the
|
|
190
|
-
// acceptance card (any non-"cancel"/"adjust" text accepts).
|
|
191
|
-
const pickAnswer = (text) => ({
|
|
192
|
-
askQuestion: vi.fn().mockResolvedValue({ questionId: "q1", text, kind: "choice" }),
|
|
193
|
-
showAcceptance: vi.fn().mockResolvedValue("accept"),
|
|
194
|
-
});
|
|
195
|
-
it("preserves a user outcome answer containing '/' (does not clobber with the autofilled default)", async () => {
|
|
196
|
-
// Regression: the override matched ANY '/' (bare `\/` regex alt +
|
|
197
|
-
// `.includes("/")`), silently replacing a legit answer like
|
|
198
|
-
// "support both REST/GraphQL endpoints" with the analyze default.
|
|
199
|
-
const userAnswer = "support both REST/GraphQL endpoints";
|
|
200
|
-
const proposer = vi
|
|
201
|
-
.fn()
|
|
202
|
-
.mockResolvedValue([
|
|
203
|
-
"Which API surface should the analysis target? [MODEL RECS: support both REST/GraphQL endpoints | REST only]",
|
|
204
|
-
]);
|
|
205
|
-
const result = await runDiscovery("review the API layer", analyzeL1, process.cwd(), pickAnswer(userAnswer), null, proposer);
|
|
206
|
-
expect(result.outcome).toBe(userAnswer);
|
|
207
|
-
expect(result.outcome).not.toBe("Detailed analysis with concrete improvement recommendations");
|
|
208
|
-
});
|
|
209
|
-
it("preserves another 'or'-style slash answer (validate input/output schemas)", async () => {
|
|
210
|
-
const userAnswer = "validate input/output schemas";
|
|
211
|
-
const proposer = vi.fn().mockResolvedValue(["What should the analysis verify?"]);
|
|
212
|
-
const result = await runDiscovery("review the API layer", analyzeL1, process.cwd(), pickAnswer(userAnswer), null, proposer);
|
|
213
|
-
expect(result.outcome).toBe(userAnswer);
|
|
214
|
-
});
|
|
215
|
-
it("still overwrites a genuinely path-leaked outcome with the autofilled default", async () => {
|
|
216
|
-
// Guard against over-correction: a real filesystem-path leak (scope-option
|
|
217
|
-
// shape "src/cli (cli)") must STILL be replaced by the inferred outcome.
|
|
218
|
-
const proposer = vi.fn().mockResolvedValue(["What scope? [MODEL RECS: src/cli (cli)]"]);
|
|
219
|
-
const result = await runDiscovery("review the API layer", analyzeL1, process.cwd(), pickAnswer("src/cli (cli)"), null, proposer);
|
|
220
|
-
expect(result.outcome).toBe("Detailed analysis with concrete improvement recommendations");
|
|
221
|
-
});
|
|
222
|
-
it("treats the 'provide my own details' meta-option as no-answer, not a literal outcome", async () => {
|
|
223
|
-
// The default meta-option ("I will provide my own details / constraints")
|
|
224
|
-
// is a 'no specific answer' sentinel — it must not survive verbatim as the
|
|
225
|
-
// outcome. With no inferred default available (generate), it falls back to
|
|
226
|
-
// the raw-derived intent rather than the sentinel string.
|
|
227
|
-
const sentinel = "I will provide my own details / constraints";
|
|
228
|
-
const proposer = vi.fn().mockResolvedValue(["What outcome do you expect?"]);
|
|
229
|
-
const result = await runDiscovery("build the user dashboard widget", { ...analyzeL1, taskType: "generate" }, process.cwd(), pickAnswer(sentinel), null, proposer);
|
|
230
|
-
expect(result.outcome).not.toBe(sentinel);
|
|
231
|
-
expect(result.outcome.toLowerCase()).toContain("dashboard");
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
178
|
//# sourceMappingURL=discovery.test.js.map
|
|
@@ -12,6 +12,8 @@ vi.mock("../../ee/bridge.js", () => ({
|
|
|
12
12
|
getEmbeddingRaw: vi.fn().mockResolvedValue(null),
|
|
13
13
|
routeTask: vi.fn().mockResolvedValue(null),
|
|
14
14
|
pilContext: vi.fn().mockResolvedValue(null),
|
|
15
|
+
getWhoAmIProfile: vi.fn(() => null),
|
|
16
|
+
outputStyleFromProfile: vi.fn(() => null),
|
|
15
17
|
}));
|
|
16
18
|
const FIXTURES = [
|
|
17
19
|
"tại sao test fail?",
|
|
@@ -20,6 +20,9 @@ vi.mock("../../ee/bridge.js", () => ({
|
|
|
20
20
|
vi.mock("../config.js", () => ({
|
|
21
21
|
isUnifiedPilEnabled: mockIsUnifiedPilEnabled,
|
|
22
22
|
isLlmFirstClassifyEnabled: mockIsLlmFirstClassifyEnabled,
|
|
23
|
+
// Pass-3 unified reads the client-side budget; whole-module mock replaces the
|
|
24
|
+
// real export, so it must be provided or the call site throws.
|
|
25
|
+
getUnifiedPilBudgetMs: vi.fn(() => 3500),
|
|
23
26
|
}));
|
|
24
27
|
import { layer1Intent } from "../layer1-intent.js";
|
|
25
28
|
function makeCtx(raw) {
|
|
@@ -7,6 +7,9 @@ vi.mock("../config.js", () => ({
|
|
|
7
7
|
// OFF here so these cascade tests exercise the regex passes (the model-first
|
|
8
8
|
// gate is covered in src/pil/layer1-intent.test.ts).
|
|
9
9
|
isLlmFirstClassifyEnabled: vi.fn(() => false),
|
|
10
|
+
// Pass-3 unified reads the client-side budget; whole-module mock replaces the
|
|
11
|
+
// real export, so it must be provided or the call site throws.
|
|
12
|
+
getUnifiedPilBudgetMs: vi.fn(() => 3500),
|
|
10
13
|
}));
|
|
11
14
|
vi.mock("../../ee/bridge.js", () => ({
|
|
12
15
|
classifyViaBrain: vi.fn().mockResolvedValue(null),
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
// Phase 2 (2026-06-16): detectClarityGaps + its keyword option-builders were
|
|
4
|
-
// removed (the model now generates every clarification). The surviving helpers
|
|
5
|
-
// — buildInterviewQuestion (render) and resolveGapsNonInteractive (headless
|
|
6
|
-
// default-answer resolution) — are exercised here with model-shaped gaps.
|
|
2
|
+
import { modelCardToQuestion, resolveGapsNonInteractive } from "../layer16-clarity.js";
|
|
7
3
|
const EMPTY_PROJECT = {
|
|
8
4
|
language: "typescript",
|
|
9
5
|
framework: null,
|
|
@@ -18,49 +14,56 @@ const EMPTY_PROJECT = {
|
|
|
18
14
|
scannedAt: Date.now(),
|
|
19
15
|
cwd: "/proj",
|
|
20
16
|
};
|
|
21
|
-
describe("
|
|
22
|
-
it("
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
describe("modelCardToQuestion()", () => {
|
|
18
|
+
it("maps ModelCard to CouncilQuestionData with pil-interview phase", () => {
|
|
19
|
+
const card = {
|
|
20
|
+
question: "What outcome?",
|
|
21
|
+
context: "This changes how we debug",
|
|
22
|
+
options: [
|
|
23
|
+
{ label: "Error disappears", kind: "choice" },
|
|
24
|
+
{ label: "Custom fix", kind: "freetext" },
|
|
25
|
+
],
|
|
28
26
|
defaultIndex: 0,
|
|
29
27
|
};
|
|
30
|
-
const q =
|
|
28
|
+
const q = modelCardToQuestion(card, "q-1");
|
|
31
29
|
expect(q.phase).toBe("pil-interview");
|
|
32
30
|
expect(q.questionId).toBe("q-1");
|
|
33
31
|
expect(q.options).toBeDefined();
|
|
34
|
-
expect(q.options
|
|
32
|
+
expect(q.options).toHaveLength(2);
|
|
33
|
+
expect(q.options[0].isCancel).toBeUndefined();
|
|
34
|
+
expect(q.options[0].isAdjust).toBeUndefined();
|
|
35
35
|
});
|
|
36
|
-
it("
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
it("preserves isCancel and isAdjust flags from model card options", () => {
|
|
37
|
+
const card = {
|
|
38
|
+
question: "Proceed?",
|
|
39
|
+
options: [
|
|
40
|
+
{ label: "Looks good, go ahead", kind: "choice" },
|
|
41
|
+
{ label: "Cancel this", kind: "choice", isCancel: true },
|
|
42
|
+
{ label: "Let me clarify", kind: "choice", isAdjust: true },
|
|
43
|
+
],
|
|
42
44
|
defaultIndex: 0,
|
|
43
45
|
};
|
|
44
|
-
const q =
|
|
45
|
-
expect(q.
|
|
46
|
+
const q = modelCardToQuestion(card, "q-2");
|
|
47
|
+
expect(q.options[1].isCancel).toBe(true);
|
|
48
|
+
expect(q.options[2].isAdjust).toBe(true);
|
|
46
49
|
});
|
|
47
50
|
});
|
|
48
51
|
describe("resolveGapsNonInteractive()", () => {
|
|
49
|
-
it("fills gaps with
|
|
50
|
-
const
|
|
52
|
+
it("fills gaps with default options from model cards", () => {
|
|
53
|
+
const cards = [
|
|
51
54
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
question: "What outcome?",
|
|
56
|
+
options: [
|
|
57
|
+
{ label: "Error resolved", kind: "choice" },
|
|
58
|
+
{ label: "Not sure", kind: "freetext" },
|
|
59
|
+
],
|
|
56
60
|
defaultIndex: 0,
|
|
57
61
|
},
|
|
58
62
|
];
|
|
59
|
-
const resolved = resolveGapsNonInteractive(
|
|
60
|
-
expect(resolved.outcome).
|
|
61
|
-
expect(resolved.scope.length).toBeGreaterThan(0);
|
|
63
|
+
const resolved = resolveGapsNonInteractive(cards, EMPTY_PROJECT, "fix auth");
|
|
64
|
+
expect(resolved.outcome).toContain("Error resolved");
|
|
62
65
|
});
|
|
63
|
-
it("falls back to
|
|
66
|
+
it("falls back to raw-derived outcome when model cards are empty", () => {
|
|
64
67
|
const resolved = resolveGapsNonInteractive([], EMPTY_PROJECT, "fix the login bug");
|
|
65
68
|
expect(resolved.outcome).toBeTruthy();
|
|
66
69
|
expect(resolved.scope.length).toBeGreaterThan(0);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { buildAcceptanceCard
|
|
2
|
+
import { buildAcceptanceCard } from "../layer18-acceptance.js";
|
|
3
3
|
describe("buildAcceptanceCard()", () => {
|
|
4
4
|
it("builds card with intent, outcome, scope, and warnings", () => {
|
|
5
5
|
const intent = { outcome: "error gone", scope: ["src/auth/jwt.ts"], constraints: [], gaps: [] };
|
|
@@ -15,81 +15,4 @@ describe("buildAcceptanceCard()", () => {
|
|
|
15
15
|
expect(card.warnings).toEqual(["oauth.ts also handles tokens"]);
|
|
16
16
|
});
|
|
17
17
|
});
|
|
18
|
-
describe("buildAcceptanceQuestion()", () => {
|
|
19
|
-
it("builds a CouncilQuestionData with pil-acceptance phase", () => {
|
|
20
|
-
const card = { intentStatement: "Fix auth", outcome: "done", scope: ["src/auth/"], warnings: [] };
|
|
21
|
-
const q = buildAcceptanceQuestion(card, "acc-1");
|
|
22
|
-
expect(q.phase).toBe("pil-acceptance");
|
|
23
|
-
expect(q.questionId).toBe("acc-1");
|
|
24
|
-
expect(q.options).toHaveLength(3);
|
|
25
|
-
expect(q.options[0].label).toBe("Accept");
|
|
26
|
-
expect(q.options[1].label).toBe("Adjust");
|
|
27
|
-
expect(q.options[2].label).toBe("Cancel");
|
|
28
|
-
expect(q.defaultIndex).toBe(0);
|
|
29
|
-
});
|
|
30
|
-
it("includes warnings in context when present", () => {
|
|
31
|
-
const card = { intentStatement: "Fix auth", outcome: "done", scope: ["src/auth/"], warnings: ["risk: oauth.ts"] };
|
|
32
|
-
const q = buildAcceptanceQuestion(card, "acc-2");
|
|
33
|
-
expect(q.context).toContain("risk: oauth.ts");
|
|
34
|
-
});
|
|
35
|
-
// Default flips to "Adjust" only for BLOCKING warnings (e.g. infeasibility).
|
|
36
|
-
it("defaults to Adjust (index 1) when warning is blocking", () => {
|
|
37
|
-
const card = {
|
|
38
|
-
intentStatement: "Fix auth",
|
|
39
|
-
outcome: "done",
|
|
40
|
-
scope: ["src/"],
|
|
41
|
-
warnings: ["infeasible: file does not exist"],
|
|
42
|
-
};
|
|
43
|
-
const q = buildAcceptanceQuestion(card, "acc-3");
|
|
44
|
-
expect(q.defaultIndex).toBe(1);
|
|
45
|
-
});
|
|
46
|
-
it("defaults to Accept (index 0) when no warnings", () => {
|
|
47
|
-
const card = { intentStatement: "Fix auth", outcome: "done", scope: ["src/"], warnings: [] };
|
|
48
|
-
const q = buildAcceptanceQuestion(card, "acc-4");
|
|
49
|
-
expect(q.defaultIndex).toBe(0);
|
|
50
|
-
});
|
|
51
|
-
// Fix #7 — informational mismatch warning should NOT flip default to Adjust.
|
|
52
|
-
// Evidence: session 1f29e238 ("Can you fix it?" follow-up) — the intent
|
|
53
|
-
// mismatch detector emitted "Detected debug/bug-fix signals..." which is
|
|
54
|
-
// informational; forcing Adjust looped PIL through another interview round.
|
|
55
|
-
it("defaults to Accept when only warning is intent-mismatch (informational)", () => {
|
|
56
|
-
const card = {
|
|
57
|
-
intentStatement: "debug: Complete: Can you fix it?",
|
|
58
|
-
outcome: "done",
|
|
59
|
-
scope: ["src/"],
|
|
60
|
-
warnings: ['Detected debug/bug-fix signals in your prompt ("fail") but intent reframed as "..."'],
|
|
61
|
-
};
|
|
62
|
-
const q = buildAcceptanceQuestion(card, "acc-5");
|
|
63
|
-
expect(q.defaultIndex).toBe(0);
|
|
64
|
-
});
|
|
65
|
-
it("defaults to Adjust when mix of informational + blocking warnings", () => {
|
|
66
|
-
const card = {
|
|
67
|
-
intentStatement: "Fix auth",
|
|
68
|
-
outcome: "done",
|
|
69
|
-
scope: ["src/"],
|
|
70
|
-
warnings: ["Detected debug signals — Verify before accepting", "infeasible: file does not exist"],
|
|
71
|
-
};
|
|
72
|
-
const q = buildAcceptanceQuestion(card, "acc-6");
|
|
73
|
-
expect(q.defaultIndex).toBe(1);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe("buildAcceptanceCard() intent-mismatch detection — PIL-L6", () => {
|
|
77
|
-
const baseFeas = { viable: true, warnings: [], adjustedScope: [] };
|
|
78
|
-
it("flags warning when raw has debug signals but intent is non-debug", () => {
|
|
79
|
-
const intent = { outcome: "Feature implemented", scope: ["project"], constraints: [], gaps: [] };
|
|
80
|
-
const card = buildAcceptanceCard("generate: Feature implemented and working", intent, baseFeas, "ci action fail dùng gh check và fix cho t");
|
|
81
|
-
expect(card.warnings.length).toBeGreaterThan(0);
|
|
82
|
-
expect(card.warnings[0]).toMatch(/debug.*signals|Verify before accepting/i);
|
|
83
|
-
});
|
|
84
|
-
it("no mismatch when intent statement matches debug signal", () => {
|
|
85
|
-
const intent = { outcome: "CI green", scope: ["project"], constraints: [], gaps: [] };
|
|
86
|
-
const card = buildAcceptanceCard("debug: CI pipeline green", intent, baseFeas, "fix the ci fail");
|
|
87
|
-
expect(card.warnings).toEqual([]);
|
|
88
|
-
});
|
|
89
|
-
it("no warning when raw has no debug signals", () => {
|
|
90
|
-
const intent = { outcome: "Feature implemented", scope: ["project"], constraints: [], gaps: [] };
|
|
91
|
-
const card = buildAcceptanceCard("generate: Feature implemented and working", intent, baseFeas, "add a new button to the dashboard");
|
|
92
|
-
expect(card.warnings).toEqual([]);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
18
|
//# sourceMappingURL=layer18-acceptance.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { layer2_5Ponytail } from "../layer2_5-ponytail.js";
|
|
3
|
+
describe("layer2_5Ponytail", () => {
|
|
4
|
+
const originalEnv = process.env.MUONROI_PONYTAIL_DISABLE;
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
if (originalEnv === undefined) {
|
|
7
|
+
delete process.env.MUONROI_PONYTAIL_DISABLE;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
process.env.MUONROI_PONYTAIL_DISABLE = originalEnv;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
it("should apply ponytail instruction when enabled", async () => {
|
|
14
|
+
process.env.MUONROI_PONYTAIL_DISABLE = "0";
|
|
15
|
+
const ctx = {
|
|
16
|
+
raw: "test",
|
|
17
|
+
enriched: "test",
|
|
18
|
+
taskType: "general",
|
|
19
|
+
confidence: 1,
|
|
20
|
+
domain: null,
|
|
21
|
+
outputStyle: "balanced",
|
|
22
|
+
tokenBudget: 1000,
|
|
23
|
+
metrics: null,
|
|
24
|
+
layers: [],
|
|
25
|
+
};
|
|
26
|
+
const result = await layer2_5Ponytail(ctx);
|
|
27
|
+
expect(result.layers.length).toBe(1);
|
|
28
|
+
expect(result.layers[0].name).toBe("ponytail-mode");
|
|
29
|
+
expect(result.layers[0].applied).toBe(true);
|
|
30
|
+
expect(result.enriched).toContain("LAZY SENIOR / PONYTAIL MODE ACTIVE");
|
|
31
|
+
});
|
|
32
|
+
it("should skip ponytail instruction when disabled via env var", async () => {
|
|
33
|
+
process.env.MUONROI_PONYTAIL_DISABLE = "1";
|
|
34
|
+
const ctx = {
|
|
35
|
+
raw: "test",
|
|
36
|
+
enriched: "test",
|
|
37
|
+
taskType: "general",
|
|
38
|
+
confidence: 1,
|
|
39
|
+
domain: null,
|
|
40
|
+
outputStyle: "balanced",
|
|
41
|
+
tokenBudget: 1000,
|
|
42
|
+
metrics: null,
|
|
43
|
+
layers: [],
|
|
44
|
+
};
|
|
45
|
+
const result = await layer2_5Ponytail(ctx);
|
|
46
|
+
expect(result.layers.length).toBe(1);
|
|
47
|
+
expect(result.layers[0].name).toBe("ponytail-mode");
|
|
48
|
+
expect(result.layers[0].applied).toBe(false);
|
|
49
|
+
expect(result.layers[0].delta).toBe("skipped:disabled-by-config");
|
|
50
|
+
expect(result.enriched).toBe("test"); // Not enriched with ponytail
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=layer2_5-ponytail.test.js.map
|