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
|
@@ -23,13 +23,32 @@ export interface RelevantModule {
|
|
|
23
23
|
relevance: string;
|
|
24
24
|
exists: boolean;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
/**
|
|
27
|
+
* A card designed entirely by the model — the model controls the question,
|
|
28
|
+
* context, options (labels, kinds, which is cancel/adjust), and default.
|
|
29
|
+
* The CLI only assigns questionId and renders via CouncilQuestionData.
|
|
30
|
+
*/
|
|
31
|
+
export interface ModelCard {
|
|
32
|
+
/** The question text shown to the user */
|
|
33
|
+
question: string;
|
|
34
|
+
/** Optional context/explanation shown below the question */
|
|
35
|
+
context?: string;
|
|
36
|
+
/** User-selectable options. Model controls labels, kinds, cancel/adjust markers */
|
|
37
|
+
options: ModelCardOption[];
|
|
38
|
+
/** Which option is pre-selected (0 = first). Defaults to 0. */
|
|
39
|
+
defaultIndex?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface ModelCardOption {
|
|
42
|
+
/** Button label or freetext field label */
|
|
43
|
+
label: string;
|
|
44
|
+
/** Optional tooltip / description */
|
|
45
|
+
description?: string;
|
|
46
|
+
/** 'choice' = clickable button, 'freetext' = free-text input field */
|
|
47
|
+
kind: "choice" | "freetext";
|
|
48
|
+
/** If true, picking this option cancels the entire interaction */
|
|
49
|
+
isCancel?: boolean;
|
|
50
|
+
/** If true, picking this indicates the user wants to clarify further (triggers re-interview) */
|
|
51
|
+
isAdjust?: boolean;
|
|
33
52
|
}
|
|
34
53
|
export interface ClarifiedIntent {
|
|
35
54
|
intentStatement?: string;
|
|
@@ -59,7 +78,7 @@ export type ModelClarificationProposer = (input: {
|
|
|
59
78
|
domain?: string | null;
|
|
60
79
|
};
|
|
61
80
|
additionalContext?: string;
|
|
62
|
-
}) => Promise<
|
|
81
|
+
}) => Promise<ModelCard[]>;
|
|
63
82
|
export interface FeasibilityResult {
|
|
64
83
|
viable: boolean;
|
|
65
84
|
warnings: string[];
|
|
@@ -81,6 +100,11 @@ export interface DiscoveryResult {
|
|
|
81
100
|
domain: string | null;
|
|
82
101
|
outputStyle: OutputStyle | null;
|
|
83
102
|
discoveryMs: number;
|
|
103
|
+
/** Raw Q&A pairs from the interview, visible to the model in enrichment */
|
|
104
|
+
interviewTranscript: Array<{
|
|
105
|
+
question: string;
|
|
106
|
+
answer: string;
|
|
107
|
+
}>;
|
|
84
108
|
}
|
|
85
109
|
export interface AcceptanceCardData {
|
|
86
110
|
intentStatement: string;
|
|
@@ -90,5 +114,4 @@ export interface AcceptanceCardData {
|
|
|
90
114
|
}
|
|
91
115
|
export interface DiscoveryInteractionHandler {
|
|
92
116
|
askQuestion(question: CouncilQuestionData): Promise<CouncilQuestionAnswer>;
|
|
93
|
-
showAcceptance(card: AcceptanceCardData): Promise<"accept" | "adjust" | "cancel">;
|
|
94
117
|
}
|
|
@@ -11,8 +11,8 @@ export interface L1Result {
|
|
|
11
11
|
export declare function runDiscovery(raw: string, l1: L1Result, cwd: string, handler: DiscoveryInteractionHandler | null, sessionId?: string | null, clarificationProposer?: ModelClarificationProposer | null, recentTurnsSummary?: string | null): Promise<DiscoveryResult>;
|
|
12
12
|
/**
|
|
13
13
|
* Create a ModelClarificationProposer backed by the actual task model.
|
|
14
|
-
* The model receives the user raw + CLI enrichment
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* The model receives the user raw + CLI enrichment and outputs the
|
|
15
|
+
* exact ModelCard[] it wants shown — full control over questions, options,
|
|
16
|
+
* option kinds, and cancel/adjust markers.
|
|
17
17
|
*/
|
|
18
18
|
export declare function createModelClarificationProposer(providerFactory: any, modelId: string): ModelClarificationProposer;
|
|
@@ -4,9 +4,8 @@ import { getMaxInterviewQuestions, isDiscoveryEnabled } from "./config.js";
|
|
|
4
4
|
import { getCachedProjectContext, setCachedProjectContext } from "./discovery-cache.js";
|
|
5
5
|
import { isMetaAnalysisPrompt } from "./layer6-output.js";
|
|
6
6
|
import { scanProjectContext } from "./layer15-context-scan.js";
|
|
7
|
-
import {
|
|
7
|
+
import { modelCardToQuestion, resolveGapsNonInteractive } from "./layer16-clarity.js";
|
|
8
8
|
import { checkFeasibility } from "./layer17-feasibility.js";
|
|
9
|
-
import { buildAcceptanceCard, buildAcceptanceQuestion } from "./layer18-acceptance.js";
|
|
10
9
|
import { markDiscoveryAccepted } from "./session-state.js";
|
|
11
10
|
function emptyProjectContext(cwd) {
|
|
12
11
|
return {
|
|
@@ -23,7 +22,7 @@ function emptyProjectContext(cwd) {
|
|
|
23
22
|
}
|
|
24
23
|
export async function runDiscovery(raw, l1, cwd, handler, sessionId = null, clarificationProposer = null, recentTurnsSummary = null) {
|
|
25
24
|
const start = Date.now();
|
|
26
|
-
const baseResult = () => ({
|
|
25
|
+
const baseResult = (transcript = []) => ({
|
|
27
26
|
raw,
|
|
28
27
|
projectContext: emptyProjectContext(cwd),
|
|
29
28
|
clarifiedIntent: { outcome: raw, scope: [], constraints: [], gaps: [] },
|
|
@@ -39,41 +38,28 @@ export async function runDiscovery(raw, l1, cwd, handler, sessionId = null, clar
|
|
|
39
38
|
domain: l1.domain,
|
|
40
39
|
outputStyle: l1.outputStyle,
|
|
41
40
|
discoveryMs: Date.now() - start,
|
|
41
|
+
interviewTranscript: transcript,
|
|
42
42
|
});
|
|
43
|
-
if (!isDiscoveryEnabled())
|
|
43
|
+
if (!isDiscoveryEnabled()) {
|
|
44
|
+
// process.stderr.write(`[discovery] isDiscoveryEnabled is FALSE\n`);
|
|
44
45
|
return baseResult();
|
|
45
|
-
|
|
46
|
+
}
|
|
47
|
+
if (l1.intentKind === "chitchat" || l1.taskType === null) {
|
|
48
|
+
// process.stderr.write(`[discovery] intent is chitchat or taskType is null: ${l1.intentKind}, ${l1.taskType}\n`);
|
|
46
49
|
return baseResult();
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (detectNoClarifySignal(raw))
|
|
50
|
+
}
|
|
51
|
+
if (detectNoClarifySignal(raw)) {
|
|
52
|
+
// process.stderr.write(`[discovery] detectNoClarifySignal is TRUE\n`);
|
|
51
53
|
return baseResult();
|
|
52
|
-
|
|
53
|
-
// The configured chat model — NOT a regex/keyword heuristic — is the sole
|
|
54
|
-
// decider of whether this turn has a genuine gray area, what to ask, which
|
|
55
|
-
// options to offer, why, and which option is recommended (user directive
|
|
56
|
-
// 2026-06-16: "các askcard bắt buộc xuất phát từ model muốn hỏi"). The CLI only
|
|
57
|
-
// injects the proposer prompt to open the path.
|
|
58
|
-
//
|
|
59
|
-
// There is deliberately NO regex fallback. The old path ran shouldAutoPass() +
|
|
60
|
-
// detectClarityGaps() keyword heuristics and fabricated questions/outcomes from
|
|
61
|
-
// them — the exact "phân loại task qua regex từ khoá ... bad bad bad UX, miss
|
|
62
|
-
// hàng tỷ case" behaviour this phase removes (it also fabricated a build outcome
|
|
63
|
-
// for a yes/no question — live repro f6f7881a5fae). If the model cannot propose
|
|
64
|
-
// questions (no proposer wired, or it throws), we log loudly and proceed WITHOUT
|
|
65
|
-
// an interview; we never invent a question from keywords ("không bao giờ hardcode
|
|
66
|
-
// fallback... có vấn đề = fail = ghi logs"). The agent can still clarify inline.
|
|
54
|
+
}
|
|
67
55
|
if (!clarificationProposer) {
|
|
68
|
-
// Interactive turns always wire a proposer (orchestrator/message-processor).
|
|
69
|
-
// A missing one there is a wiring bug — surface it, never paper over with regex.
|
|
70
56
|
if (handler) {
|
|
71
57
|
console.error("[Agent:discovery] interactive turn has no model clarification proposer — skipping interview (no regex fallback by design)");
|
|
72
58
|
}
|
|
59
|
+
// process.stderr.write(`[discovery] clarificationProposer is NULL\n`);
|
|
73
60
|
return baseResult();
|
|
74
61
|
}
|
|
75
|
-
// L1.5: Context Discovery (cacheable)
|
|
76
|
-
// it never asks for something it can inspect itself (language/framework/modules).
|
|
62
|
+
// L1.5: Context Discovery (cacheable)
|
|
77
63
|
let projectContext;
|
|
78
64
|
const cached = getCachedProjectContext(cwd);
|
|
79
65
|
if (cached) {
|
|
@@ -91,104 +77,105 @@ export async function runDiscovery(raw, l1, cwd, handler, sessionId = null, clar
|
|
|
91
77
|
projectContext = emptyProjectContext(cwd);
|
|
92
78
|
}
|
|
93
79
|
}
|
|
94
|
-
// L1.6: Ask the MODEL
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
let
|
|
80
|
+
// L1.6: Ask the MODEL to design interview cards.
|
|
81
|
+
// The model returns ModelCard[] — it controls the question, options,
|
|
82
|
+
// option kinds (choice/freetext), and which options cancel/adjust.
|
|
83
|
+
// An empty array means the model sees no gray area → proceed directly.
|
|
84
|
+
let cards;
|
|
99
85
|
try {
|
|
100
|
-
|
|
86
|
+
// process.stderr.write(`[discovery] calling proposeModelCards...\n`);
|
|
87
|
+
cards = await proposeModelCards(clarificationProposer, raw, l1, projectContext, recentTurnsSummary);
|
|
88
|
+
// process.stderr.write(`[discovery] proposeModelCards returned ${cards?.length} cards\n`);
|
|
101
89
|
}
|
|
102
90
|
catch (err) {
|
|
103
|
-
// No Silent Catch + fail-loud: log with context, then proceed WITHOUT an
|
|
104
|
-
// interview. We do NOT fall back to regex-generated questions.
|
|
105
|
-
console.error(`[Agent:discovery] model clarification proposer threw — proceeding without interview (no regex fallback): ${err?.message}`, { stack: err?.stack?.split("\n").slice(0, 3) });
|
|
106
91
|
return baseResult();
|
|
107
92
|
}
|
|
108
|
-
|
|
109
|
-
if (gaps.length === 0)
|
|
93
|
+
if (cards.length === 0)
|
|
110
94
|
return baseResult();
|
|
111
|
-
let clarifiedIntent;
|
|
112
95
|
let interviewed = false;
|
|
113
|
-
|
|
96
|
+
const interviewTranscript = [];
|
|
97
|
+
// Interactive interview: show model-designed cards and collect raw answers
|
|
98
|
+
if (handler) {
|
|
114
99
|
interviewed = true;
|
|
115
|
-
const
|
|
116
|
-
...g,
|
|
117
|
-
answer: null,
|
|
118
|
-
}));
|
|
119
|
-
const maxQ = Math.min(gaps.length, getMaxInterviewQuestions());
|
|
100
|
+
const maxQ = Math.min(cards.length, getMaxInterviewQuestions());
|
|
120
101
|
for (let i = 0; i < maxQ; i++) {
|
|
121
|
-
const
|
|
122
|
-
const question =
|
|
102
|
+
const card = cards[i];
|
|
103
|
+
const question = modelCardToQuestion(card, randomUUID());
|
|
123
104
|
const answer = await handler.askQuestion(question);
|
|
124
|
-
|
|
105
|
+
interviewTranscript.push({ question: card.question, answer: answer.text });
|
|
106
|
+
// If the user picked a cancel option → stop the whole discovery
|
|
107
|
+
const chosenOption = card.options.find((o) => o.label === answer.text);
|
|
108
|
+
if (chosenOption?.isCancel) {
|
|
109
|
+
const result = baseResult(interviewTranscript);
|
|
110
|
+
result.accepted = false;
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
125
113
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// merely contains a slash ("support REST/GraphQL", "input/output") — see looksLikePathLeak.
|
|
136
|
-
const autoOutcome = getAutofilledOutcome(l1.taskType, raw);
|
|
137
|
-
const currentOutcome = clarifiedIntent.outcome ?? "";
|
|
138
|
-
const isGenericComplete = /^Complete(?::| the task)/i.test(currentOutcome);
|
|
139
|
-
if (autoOutcome && (!currentOutcome || isGenericComplete || looksLikePathLeak(currentOutcome))) {
|
|
140
|
-
clarifiedIntent = { ...clarifiedIntent, outcome: autoOutcome };
|
|
141
|
-
}
|
|
142
|
-
// L1.7: Feasibility Check
|
|
143
|
-
let feasibility = await checkFeasibility(clarifiedIntent, projectContext).catch(() => ({
|
|
144
|
-
viable: true,
|
|
145
|
-
warnings: [],
|
|
146
|
-
adjustedScope: clarifiedIntent.scope,
|
|
147
|
-
}));
|
|
148
|
-
// L1.8: User Acceptance
|
|
149
|
-
const intentStatement = `${l1.taskType ?? "task"}: ${clarifiedIntent.outcome}`;
|
|
150
|
-
let accepted = true;
|
|
151
|
-
if (handler && interviewed) {
|
|
152
|
-
const card = buildAcceptanceCard(intentStatement, clarifiedIntent, feasibility, raw);
|
|
153
|
-
const question = buildAcceptanceQuestion(card, randomUUID());
|
|
154
|
-
const answer = await handler.askQuestion(question);
|
|
155
|
-
const decision = answer.text.toLowerCase();
|
|
156
|
-
if (decision === "cancel") {
|
|
157
|
-
accepted = false;
|
|
114
|
+
// After the first round, if any answer had isAdjust flag → re-interview
|
|
115
|
+
let needsReInterview = false;
|
|
116
|
+
for (let i = 0; i < Math.min(cards.length, getMaxInterviewQuestions()); i++) {
|
|
117
|
+
const card = cards[i];
|
|
118
|
+
const chosenOption = card.options.find((o) => o.label === interviewTranscript[i].answer);
|
|
119
|
+
if (chosenOption?.isAdjust) {
|
|
120
|
+
needsReInterview = true;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
158
123
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// given the same context (it owns the gray-area decision, exactly like the
|
|
162
|
-
// initial pass). On a proposer failure we keep the already-resolved intent
|
|
163
|
-
// rather than fabricate regex questions.
|
|
164
|
-
let reGaps = [];
|
|
124
|
+
if (needsReInterview) {
|
|
125
|
+
let reCards = [];
|
|
165
126
|
try {
|
|
166
|
-
|
|
127
|
+
reCards = await proposeModelCards(clarificationProposer, raw, l1, projectContext, recentTurnsSummary);
|
|
167
128
|
}
|
|
168
129
|
catch (err) {
|
|
169
|
-
console.error(`[Agent:discovery] re-interview proposer threw — keeping prior
|
|
130
|
+
console.error(`[Agent:discovery] re-interview proposer threw — keeping prior answers: ${err?.message}`);
|
|
170
131
|
}
|
|
171
|
-
if (
|
|
172
|
-
const
|
|
173
|
-
...g,
|
|
174
|
-
answer: null,
|
|
175
|
-
}));
|
|
176
|
-
const maxRe = Math.min(reGaps.length, getMaxInterviewQuestions());
|
|
132
|
+
if (reCards.length > 0) {
|
|
133
|
+
const maxRe = Math.min(reCards.length, getMaxInterviewQuestions());
|
|
177
134
|
for (let i = 0; i < maxRe; i++) {
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
135
|
+
const card = reCards[i];
|
|
136
|
+
const question = modelCardToQuestion(card, randomUUID());
|
|
137
|
+
const answer = await handler.askQuestion(question);
|
|
138
|
+
interviewTranscript.push({ question: card.question, answer: answer.text });
|
|
139
|
+
const chosenOption = card.options.find((o) => o.label === answer.text);
|
|
140
|
+
if (chosenOption?.isCancel) {
|
|
141
|
+
const result = baseResult(interviewTranscript);
|
|
142
|
+
result.accepted = false;
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
182
145
|
}
|
|
183
|
-
clarifiedIntent = buildClarifiedIntentFromAnswers(reAnswered, raw, projectContext);
|
|
184
|
-
feasibility = await checkFeasibility(clarifiedIntent, projectContext).catch(() => feasibility);
|
|
185
146
|
}
|
|
186
|
-
accepted = true;
|
|
187
147
|
}
|
|
188
148
|
}
|
|
189
|
-
|
|
190
|
-
|
|
149
|
+
else {
|
|
150
|
+
// Headless: resolve defaults
|
|
151
|
+
const _resolved = resolveGapsNonInteractive(cards, projectContext, raw);
|
|
152
|
+
interviewTranscript.push(...cards.map((c) => ({
|
|
153
|
+
question: c.question,
|
|
154
|
+
answer: c.options[c.defaultIndex ?? 0]?.label ?? "",
|
|
155
|
+
})));
|
|
191
156
|
}
|
|
157
|
+
// Build a clarified intent summary from the transcript
|
|
158
|
+
const resolvedOutcome = interviewTranscript
|
|
159
|
+
.filter((qa) => qa.answer.length > 0)
|
|
160
|
+
.map((qa) => qa.answer)
|
|
161
|
+
.join("; ") || raw;
|
|
162
|
+
const intentStatement = `${l1.taskType}: ${resolvedOutcome.slice(0, 120)}`;
|
|
163
|
+
const clarifiedIntent = {
|
|
164
|
+
outcome: resolvedOutcome,
|
|
165
|
+
scope: projectContext.relevantModules.length > 0 ? projectContext.relevantModules.map((m) => m.path) : ["project root"],
|
|
166
|
+
constraints: [],
|
|
167
|
+
interviewed,
|
|
168
|
+
accepted: true,
|
|
169
|
+
};
|
|
170
|
+
// L1.7: Feasibility Check
|
|
171
|
+
const feasibility = await checkFeasibility(clarifiedIntent, projectContext).catch(() => ({
|
|
172
|
+
viable: true,
|
|
173
|
+
warnings: [],
|
|
174
|
+
adjustedScope: clarifiedIntent.scope,
|
|
175
|
+
}));
|
|
176
|
+
// L1.8: Acceptance — use the model's own cards; no separate acceptance ceremony.
|
|
177
|
+
// Feasibility warnings are added to the result but don't trigger a separate card.
|
|
178
|
+
markDiscoveryAccepted(sessionId);
|
|
192
179
|
return {
|
|
193
180
|
raw,
|
|
194
181
|
projectContext,
|
|
@@ -201,82 +188,21 @@ export async function runDiscovery(raw, l1, cwd, handler, sessionId = null, clar
|
|
|
201
188
|
? (feasibility.adjustedScope ?? [])
|
|
202
189
|
: (clarifiedIntent.scope ?? ["project root"]),
|
|
203
190
|
feasibilityWarnings: feasibility.warnings ?? [],
|
|
204
|
-
accepted,
|
|
191
|
+
accepted: true,
|
|
205
192
|
taskType: l1.taskType,
|
|
206
193
|
confidence: l1.confidence,
|
|
207
194
|
domain: l1.domain,
|
|
208
195
|
outputStyle: l1.outputStyle,
|
|
209
196
|
discoveryMs: Date.now() - start,
|
|
197
|
+
interviewTranscript,
|
|
210
198
|
};
|
|
211
199
|
}
|
|
212
200
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* (
|
|
216
|
-
* path-ish signal (file extension, path keyword, or trailing "(name)").
|
|
217
|
-
*
|
|
218
|
-
* A bare "/" is NOT sufficient: "support REST/GraphQL", "validate input/output",
|
|
219
|
-
* and "details / constraints" use the slash as an "or" separator and must be
|
|
220
|
-
* preserved. The previous implementation matched any "/" and silently clobbered
|
|
221
|
-
* such legitimate answers with the generic taskType default.
|
|
222
|
-
*/
|
|
223
|
-
function looksLikePathLeak(outcome) {
|
|
224
|
-
// Anchored known leaked-scope phrases (preserves prior override behaviour).
|
|
225
|
-
if (/(?:\b(?:local path|in prompts|directory as|project root|absolute)\b)|local\/repo/i.test(outcome)) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
// word/word path segment (no spaces around the slash) — e.g. "src/foo.ts".
|
|
229
|
-
if (!/\b[\w.-]+\/[\w./-]+/.test(outcome))
|
|
230
|
-
return false;
|
|
231
|
-
const hasFileExtension = /\/[\w-]+\.[a-z0-9]{1,5}\b/i.test(outcome); // ".../foo.ts"
|
|
232
|
-
const hasPathKeyword = /\b(?:path|dir|directory|folder|repo|root|module|src|lib|dist|tests?)\b/i.test(outcome);
|
|
233
|
-
const hasScopeOptionSuffix = /\([^)]+\)\s*$/.test(outcome); // "src/cli (cli)" scope-option shape
|
|
234
|
-
return hasFileExtension || hasPathKeyword || hasScopeOptionSuffix;
|
|
235
|
-
}
|
|
236
|
-
function buildClarifiedIntentFromAnswers(answeredGaps, raw, projectContext) {
|
|
237
|
-
const outcomeGap = answeredGaps.find((g) => g.dimension === "outcome");
|
|
238
|
-
const scopeGap = answeredGaps.find((g) => g.dimension === "scope");
|
|
239
|
-
const constraintGap = answeredGaps.find((g) => g.dimension === "constraint");
|
|
240
|
-
// The "provide my own details" meta-option is a no-answer sentinel; treat it
|
|
241
|
-
// as missing so the raw-derived generic (and downstream inferred outcome) is
|
|
242
|
-
// used instead of the sentinel string surviving verbatim as the outcome.
|
|
243
|
-
const outcomeAnswer = isProvideOwnDetailsSentinel(outcomeGap?.answer) ? null : (outcomeGap?.answer ?? null);
|
|
244
|
-
const outcome = outcomeAnswer ?? `Complete: ${raw.slice(0, 80)}`;
|
|
245
|
-
const scope = (() => {
|
|
246
|
-
if (scopeGap?.answer)
|
|
247
|
-
return [scopeGap.answer.replace(/\s*\(.*\)\s*$/, "").trim()];
|
|
248
|
-
return projectContext.relevantModules.map((m) => m.path);
|
|
249
|
-
})();
|
|
250
|
-
const constraints = constraintGap?.answer ? [constraintGap.answer] : [];
|
|
251
|
-
return {
|
|
252
|
-
intentStatement: outcome,
|
|
253
|
-
outcome,
|
|
254
|
-
scope: scope.length > 0 ? scope : ["project root"],
|
|
255
|
-
feasibilityWarnings: [],
|
|
256
|
-
interviewed: false,
|
|
257
|
-
accepted: false,
|
|
258
|
-
constraints,
|
|
259
|
-
gaps: answeredGaps.map((g) => ({
|
|
260
|
-
dimension: g.dimension,
|
|
261
|
-
description: "",
|
|
262
|
-
suggestedQuestion: "",
|
|
263
|
-
options: g.options,
|
|
264
|
-
defaultIndex: g.defaultIndex,
|
|
265
|
-
answer: g.answer,
|
|
266
|
-
})),
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Build the enrichment context the model sees, call the proposer, and map each
|
|
271
|
-
* returned line into a ClarityGap. This is the SINGLE place discovery turns a
|
|
272
|
-
* model response into askcard gaps — the model owns the question, the options
|
|
273
|
-
* (recommends), the recommended default (first option), and the "why" (threaded
|
|
274
|
-
* into the gap description → askcard context). No regex/keyword gap synthesis.
|
|
275
|
-
*
|
|
276
|
-
* Throws on proposer error so the caller can decide how to degrade (initial pass
|
|
277
|
-
* proceeds without interview; the "adjust" pass keeps the prior intent).
|
|
201
|
+
* Call the model proposer and parse its ModelCard[] response.
|
|
202
|
+
* The model is the sole designer of all cards — questions, options,
|
|
203
|
+
* option kinds (choice/freetext), cancel/adjust markers, and defaults.
|
|
278
204
|
*/
|
|
279
|
-
async function
|
|
205
|
+
async function proposeModelCards(proposer, raw, l1, projectContext, recentTurnsSummary) {
|
|
280
206
|
const additionalContext = [
|
|
281
207
|
projectContext.language ? `Language: ${projectContext.language}` : "",
|
|
282
208
|
projectContext.framework ? `Framework: ${projectContext.framework}` : "",
|
|
@@ -292,54 +218,22 @@ async function proposeModelGaps(proposer, raw, l1, projectContext, recentTurnsSu
|
|
|
292
218
|
]
|
|
293
219
|
.filter(Boolean)
|
|
294
220
|
.join("\n");
|
|
295
|
-
const
|
|
221
|
+
const modelCards = await proposer({
|
|
296
222
|
raw,
|
|
297
223
|
l1: { taskType: l1.taskType, confidence: l1.confidence },
|
|
298
224
|
additionalContext: additionalContext || undefined,
|
|
299
225
|
});
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Parse one proposer line ("question [MODEL RECS: a | b] [WHY: reason]") into a
|
|
304
|
-
* ClarityGap. The recommends become the askcard options (first = recommended
|
|
305
|
-
* default); the WHY clause becomes the askcard context so the user sees the
|
|
306
|
-
* model's own reason for asking.
|
|
307
|
-
*/
|
|
308
|
-
function parseModelQuestionToGap(line, idx) {
|
|
309
|
-
let recs = [PROVIDE_OWN_DETAILS_OPTION_EN];
|
|
310
|
-
const recMatch = line.match(/\[MODEL RECS:?\s*(.+?)\]/i) || line.match(/RECS:\s*(.+)$/i);
|
|
311
|
-
if (recMatch) {
|
|
312
|
-
const parsed = recMatch[1]
|
|
313
|
-
.split(/\s*\|\s*/)
|
|
314
|
-
.map((r) => r.trim())
|
|
315
|
-
.filter(Boolean)
|
|
316
|
-
.slice(0, 3);
|
|
317
|
-
if (parsed.length > 0)
|
|
318
|
-
recs = parsed;
|
|
319
|
-
}
|
|
320
|
-
const whyMatch = line.match(/\[WHY:\s*(.+?)\]/i);
|
|
321
|
-
const why = whyMatch ? whyMatch[1].trim() : "";
|
|
322
|
-
const question = line
|
|
323
|
-
.replace(/\[WHY:.*?\]/i, "")
|
|
324
|
-
.replace(/\[MODEL RECS:?.*?\]/i, "")
|
|
325
|
-
.replace(/RECS:.*$/, "")
|
|
326
|
-
.trim();
|
|
327
|
-
return {
|
|
328
|
-
dimension: "outcome",
|
|
329
|
-
description: why || `Model-generated clarification #${idx + 1}`,
|
|
330
|
-
suggestedQuestion: question || "What else needs clarification?",
|
|
331
|
-
options: [...recs, "Other (type free answer)"],
|
|
332
|
-
defaultIndex: 0,
|
|
333
|
-
};
|
|
226
|
+
return modelCards.slice(0, 3);
|
|
334
227
|
}
|
|
335
228
|
/**
|
|
336
229
|
* Create a ModelClarificationProposer backed by the actual task model.
|
|
337
|
-
* The model receives the user raw + CLI enrichment
|
|
338
|
-
*
|
|
339
|
-
*
|
|
230
|
+
* The model receives the user raw + CLI enrichment and outputs the
|
|
231
|
+
* exact ModelCard[] it wants shown — full control over questions, options,
|
|
232
|
+
* option kinds, and cancel/adjust markers.
|
|
340
233
|
*/
|
|
341
234
|
export function createModelClarificationProposer(providerFactory, modelId) {
|
|
342
235
|
return async (input) => {
|
|
236
|
+
// process.stderr.write(`[discovery] createModelClarificationProposer CALLED!\n`);
|
|
343
237
|
try {
|
|
344
238
|
const { resolveModelRuntime } = await import("../providers/runtime.js");
|
|
345
239
|
const { generateText } = await import("ai");
|
|
@@ -350,31 +244,47 @@ export function createModelClarificationProposer(providerFactory, modelId) {
|
|
|
350
244
|
const special = isMetaAnalysisPrompt(input.raw)
|
|
351
245
|
? `\nIf the request is a self-evaluation, meta-analysis or review of the CLI by the agent running inside it, do NOT ask about repo path, current directory, absolute path, local repo location or "which directory". Scope is always the full project root. Focus questions and recommends on which CLI internals (PIL, discovery, tools, compaction, EE, model BE, loop guard) to evaluate or specific improvements to assess after fixes. Use the enrichment context.`
|
|
352
246
|
: "";
|
|
353
|
-
// Environment/self header — the main system prompt has buildEnvironmentBlock,
|
|
354
|
-
// but THIS discovery question-generator is a separate LLM call that lacked it,
|
|
355
|
-
// so it assumed Python and asked the user to paste the directory tree despite
|
|
356
|
-
// running inside the repo (live grok session). Escape hatch:
|
|
357
|
-
// MUONROI_DISCOVERY_SKIP_ENV_CONTEXT=1.
|
|
358
247
|
const osLabel = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
|
|
359
248
|
const envHeader = process.env.MUONROI_DISCOVERY_SKIP_ENV_CONTEXT === "1"
|
|
360
249
|
? ""
|
|
361
250
|
: `Runtime: ${osLabel} (${process.platform}); \`bash\` is POSIX. The project's language/framework is in the context below — do NOT assume Python or a POSIX-only layout. Do NOT ask the user to paste the directory tree, file list, or project structure: you run INSIDE the repository and can inspect it with your own tools. Ask only about genuine intent / scope ambiguities.\n`;
|
|
362
|
-
const prompt = `You are the AI agent executing inside muonroi-cli.
|
|
363
|
-
${envHeader}User request: "${input.raw}"
|
|
364
|
-
Task type from CLI: ${input.l1.taskType}
|
|
365
|
-
${contextStr}
|
|
366
|
-
|
|
367
|
-
You
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
251
|
+
const prompt = `You are the AI agent executing inside muonroi-cli.
|
|
252
|
+
${envHeader}User request: "${input.raw}"
|
|
253
|
+
Task type from CLI: ${input.l1.taskType}
|
|
254
|
+
${contextStr}
|
|
255
|
+
|
|
256
|
+
You design question cards shown to the user *before* you start working.
|
|
257
|
+
Each card is a structured question with selectable options.
|
|
258
|
+
|
|
259
|
+
Rules:
|
|
260
|
+
1. Ask ONLY what is genuinely blocking. Most well-scoped requests need 0 cards.
|
|
261
|
+
2. If everything you need is inferable from the request + context above, OR the request is a plain question you can simply answer, return [] (empty array).
|
|
262
|
+
3. If this is a follow-up or continuation of recent conversation history, assume context is already established and return [] unless there is a critical new ambiguity.
|
|
263
|
+
4. Consider the provided language/framework/modules/EE patterns — never ask what the context already answers.${special}
|
|
264
|
+
5. For each card, design the options however you want:
|
|
265
|
+
- Use kind="choice" for clickable buttons (recommendations, accept/adjust/cancel, etc.)
|
|
266
|
+
- Use kind="freetext" when the user should type their own answer
|
|
267
|
+
- Mark an option with isCancel:true when picking it should cancel the entire request
|
|
268
|
+
- Mark an option with isAdjust:true when picking it means the user wants to clarify further
|
|
269
|
+
- Set defaultIndex to the option most users would pick (0 = first)
|
|
270
|
+
6. Return ONLY valid JSON array, nothing else.
|
|
271
|
+
7. Max 3 cards.
|
|
272
|
+
|
|
273
|
+
JSON format:
|
|
274
|
+
[{
|
|
275
|
+
"question": "string — the question text",
|
|
276
|
+
"context": "string — optional explanation shown below the question",
|
|
277
|
+
"options": [
|
|
278
|
+
{"label": "string", "description": "string", "kind": "choice", "isCancel": false, "isAdjust": false},
|
|
279
|
+
{"label": "string", "kind": "freetext"}
|
|
280
|
+
],
|
|
281
|
+
"defaultIndex": 0
|
|
282
|
+
}]`;
|
|
374
283
|
const result = await generateText({
|
|
375
284
|
model: runtime.model,
|
|
376
285
|
prompt,
|
|
377
|
-
maxOutputTokens:
|
|
286
|
+
maxOutputTokens: 600,
|
|
287
|
+
abortSignal: AbortSignal.timeout(15000),
|
|
378
288
|
});
|
|
379
289
|
let items;
|
|
380
290
|
try {
|
|
@@ -386,29 +296,13 @@ Max 3 items.`;
|
|
|
386
296
|
items = Array.isArray(parsed) ? parsed : [];
|
|
387
297
|
}
|
|
388
298
|
catch (parseErr) {
|
|
389
|
-
// A malformed (non-JSON) model response must NOT be coerced into a junk
|
|
390
|
-
// askcard — log and return no questions (proceed without interview).
|
|
391
|
-
console.error(`[Agent:discovery] clarification proposer returned non-JSON — no questions this turn: ${parseErr?.message}`, { sample: result.text.slice(0, 160) });
|
|
392
299
|
return [];
|
|
393
300
|
}
|
|
394
301
|
return items
|
|
395
302
|
.filter((it) => it && typeof it.question === "string" && it.question.trim())
|
|
396
|
-
.slice(0, 3)
|
|
397
|
-
.map((it) => {
|
|
398
|
-
const recs = (it.recommends || []).slice(0, 2).join(" | ");
|
|
399
|
-
const recTag = recs ? ` [MODEL RECS: ${recs}]` : "";
|
|
400
|
-
const why = (it.reason || "").trim();
|
|
401
|
-
const whyTag = why ? ` [WHY: ${why}]` : "";
|
|
402
|
-
return `${it.question.trim()}${recTag}${whyTag}`;
|
|
403
|
-
});
|
|
303
|
+
.slice(0, 3);
|
|
404
304
|
}
|
|
405
305
|
catch (err) {
|
|
406
|
-
// No Silent Catch + fail-loud: the model call failed. Log with context and
|
|
407
|
-
// return no questions — discovery proceeds WITHOUT an interview rather than
|
|
408
|
-
// fabricating a regex-derived one ("có vấn đề = fail = ghi logs").
|
|
409
|
-
console.error(`[Agent:discovery] clarification proposer failed (${modelId}): ${err?.message}`, {
|
|
410
|
-
stack: err?.stack?.split("\n").slice(0, 3),
|
|
411
|
-
});
|
|
412
306
|
return [];
|
|
413
307
|
}
|
|
414
308
|
};
|
package/dist/src/pil/index.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export { isDiscoveryEnabled } from "./config.js";
|
|
|
8
8
|
export type { AcceptanceCardData, DiscoveryInteractionHandler, DiscoveryResult, ProjectContext, } from "./discovery-types.js";
|
|
9
9
|
export { applyPilSuffix, getResponseToolSet } from "./layer6-output.js";
|
|
10
10
|
export { type PipelineOptions, runPipeline } from "./pipeline.js";
|
|
11
|
-
export { getResponseTaskType, isResponseTool, shouldHaltOnResponseTool, stepEmittedResponseTool, } from "./response-tools.js";
|
|
11
|
+
export { getResponseTaskType, isResponseTool, normalizeStructuredResponseTaskType, shouldHaltOnResponseTool, stepEmittedResponseTool, } from "./response-tools.js";
|
|
12
12
|
export { getPilLastResult, setPilLastResult } from "./store.js";
|
|
13
13
|
export type { LayerResult, OutputStyle, PipelineContext, PipelineMetrics, TaskType } from "./types.js";
|
package/dist/src/pil/index.js
CHANGED
|
@@ -7,6 +7,6 @@ export { DEFAULT_TOKEN_BUDGET, truncateToBudget } from "./budget.js";
|
|
|
7
7
|
export { isDiscoveryEnabled } from "./config.js";
|
|
8
8
|
export { applyPilSuffix, getResponseToolSet } from "./layer6-output.js";
|
|
9
9
|
export { runPipeline } from "./pipeline.js";
|
|
10
|
-
export { getResponseTaskType, isResponseTool, shouldHaltOnResponseTool, stepEmittedResponseTool, } from "./response-tools.js";
|
|
10
|
+
export { getResponseTaskType, isResponseTool, normalizeStructuredResponseTaskType, shouldHaltOnResponseTool, stepEmittedResponseTool, } from "./response-tools.js";
|
|
11
11
|
export { getPilLastResult, setPilLastResult } from "./store.js";
|
|
12
12
|
//# sourceMappingURL=index.js.map
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Fail-open: any error returns ctx unchanged with applied=false.
|
|
11
11
|
*/
|
|
12
12
|
import type { LlmClassifyFn } from "./llm-classify.js";
|
|
13
|
-
import type { PipelineContext } from "./types.js";
|
|
13
|
+
import type { OutputStyle, PipelineContext } from "./types.js";
|
|
14
14
|
export interface ComplexityInput {
|
|
15
15
|
rawText: string;
|
|
16
16
|
taskType: string | null;
|
|
@@ -75,6 +75,14 @@ export declare function isStatusCheckQuestion(raw: string): boolean;
|
|
|
75
75
|
export interface Layer1Options {
|
|
76
76
|
/** Pass 4 LLM fallback closure — fires when brain returned null or confidence < 0.7. */
|
|
77
77
|
llmFallback?: LlmClassifyFn;
|
|
78
|
+
/**
|
|
79
|
+
* WhoAmI v4.0 output-style baseline, derived once in the pipeline from the
|
|
80
|
+
* device-local profile (via ../ee/bridge.js getWhoAmIProfile + outputStyleFromProfile).
|
|
81
|
+
* Applied as the standing default when no per-turn style signal resolves — replaces
|
|
82
|
+
* the generic "balanced" classifier-default. null/undefined ⇒ behaviour unchanged.
|
|
83
|
+
* Passed in (not read here) to keep layer1 off the EE/profile import path.
|
|
84
|
+
*/
|
|
85
|
+
profileStyleBaseline?: OutputStyle | null;
|
|
78
86
|
}
|
|
79
87
|
/**
|
|
80
88
|
* True when the prompt is an explicit request to RUN a command / use a shell
|