muonroi-cli 1.4.1 → 1.6.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/predicate.d.ts +1 -1
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +48 -1
- package/dist/src/agent-harness/mock-model.d.ts +11 -0
- package/dist/src/agent-harness/mock-model.js +21 -0
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/council/__tests__/clarification-prompt.test.js +51 -0
- package/dist/src/council/__tests__/clarifier-ready-gate.test.js +32 -0
- package/dist/src/council/__tests__/decisions-lock.test.js +17 -1
- package/dist/src/council/__tests__/oauth-reachable.test.d.ts +1 -0
- package/dist/src/council/__tests__/oauth-reachable.test.js +31 -0
- package/dist/src/council/__tests__/parse-outcome-fallback.test.js +11 -0
- package/dist/src/council/clarifier.js +9 -1
- package/dist/src/council/debate.js +5 -1
- package/dist/src/council/decisions-lock.js +3 -3
- package/dist/src/council/index.js +12 -5
- package/dist/src/council/leader.d.ts +0 -17
- package/dist/src/council/leader.js +22 -15
- package/dist/src/council/planner.js +1 -1
- package/dist/src/council/prompts.js +63 -57
- package/dist/src/council/types.d.ts +7 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.d.ts +1 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.js +32 -0
- package/dist/src/ee/artifact-cache.d.ts +56 -0
- package/dist/src/ee/artifact-cache.js +155 -0
- package/dist/src/ee/artifact-cache.test.d.ts +1 -0
- package/dist/src/ee/artifact-cache.test.js +69 -0
- package/dist/src/ee/auth.d.ts +9 -0
- package/dist/src/ee/auth.js +19 -0
- package/dist/src/ee/ee-onboarding.d.ts +5 -0
- package/dist/src/ee/ee-onboarding.js +76 -0
- package/dist/src/ee/search.js +7 -5
- package/dist/src/ee/search.test.d.ts +1 -0
- package/dist/src/ee/search.test.js +23 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/output.js +6 -4
- package/dist/src/headless/output.test.js +4 -3
- package/dist/src/index.js +20 -1
- package/dist/src/mcp/__tests__/auto-setup.test.js +74 -0
- package/dist/src/mcp/__tests__/client-pool.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/client-pool.spec.js +98 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.js +67 -0
- package/dist/src/mcp/__tests__/smart-filter.test.js +56 -0
- package/dist/src/mcp/auto-setup.js +56 -2
- package/dist/src/mcp/client-pool.d.ts +46 -0
- package/dist/src/mcp/client-pool.js +212 -0
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/runtime.d.ts +28 -0
- package/dist/src/mcp/runtime.js +117 -51
- package/dist/src/mcp/self-verify-runner.d.ts +14 -0
- package/dist/src/mcp/self-verify-runner.js +38 -0
- package/dist/src/mcp/setup-guide-text.d.ts +9 -0
- package/dist/src/mcp/setup-guide-text.js +84 -0
- package/dist/src/mcp/smart-filter.js +49 -0
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/mcp/tools-server.d.ts +7 -0
- package/dist/src/mcp/tools-server.js +19 -22
- package/dist/src/models/catalog.json +349 -349
- package/dist/src/ops/__tests__/doctor-ee-health.test.js +21 -0
- package/dist/src/ops/doctor.d.ts +3 -2
- package/dist/src/ops/doctor.js +47 -11
- package/dist/src/ops/doctor.test.js +4 -3
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.js +39 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.js +65 -0
- package/dist/src/orchestrator/batch-turn-runner.js +7 -11
- package/dist/src/orchestrator/compaction.d.ts +2 -0
- package/dist/src/orchestrator/compaction.js +14 -1
- package/dist/src/orchestrator/compaction.test.js +25 -1
- package/dist/src/orchestrator/message-processor.js +72 -32
- package/dist/src/orchestrator/orchestrator.js +26 -0
- package/dist/src/orchestrator/prompts.d.ts +51 -0
- package/dist/src/orchestrator/prompts.js +257 -134
- package/dist/src/orchestrator/scope-ceiling.js +6 -1
- package/dist/src/orchestrator/scope-reminder.d.ts +12 -0
- package/dist/src/orchestrator/scope-reminder.js +16 -0
- package/dist/src/orchestrator/scope-reminder.test.js +22 -1
- package/dist/src/orchestrator/stream-runner.js +23 -15
- package/dist/src/orchestrator/subagent-compactor.d.ts +14 -5
- package/dist/src/orchestrator/subagent-compactor.js +30 -8
- package/dist/src/orchestrator/subagent-compactor.spec.js +18 -0
- package/dist/src/orchestrator/text-tool-call-detector.test.js +13 -13
- package/dist/src/pil/__tests__/clarity-gate.test.js +24 -215
- package/dist/src/pil/__tests__/config.test.js +1 -17
- package/dist/src/pil/__tests__/discovery.test.js +144 -11
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +7 -2
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +32 -116
- package/dist/src/pil/__tests__/layer4-gsd.test.js +37 -0
- package/dist/src/pil/__tests__/layer6-output.test.js +158 -18
- package/dist/src/pil/__tests__/llm-classify.test.js +49 -2
- package/dist/src/pil/__tests__/surface-compaction-artifacts.test.d.ts +1 -0
- package/dist/src/pil/__tests__/surface-compaction-artifacts.test.js +112 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +2 -0
- package/dist/src/pil/agent-operating-contract.test.js +7 -2
- package/dist/src/pil/cheap-model-playbook.js +35 -35
- package/dist/src/pil/cheap-model-workbooks.js +16 -13
- package/dist/src/pil/clarity-gate.d.ts +21 -19
- package/dist/src/pil/clarity-gate.js +26 -153
- package/dist/src/pil/config.d.ts +9 -1
- package/dist/src/pil/config.js +15 -4
- package/dist/src/pil/discovery.js +211 -136
- package/dist/src/pil/layer1-intent.d.ts +12 -0
- package/dist/src/pil/layer1-intent.js +283 -38
- package/dist/src/pil/layer1-intent.test.js +210 -4
- package/dist/src/pil/layer16-clarity.d.ts +25 -11
- package/dist/src/pil/layer16-clarity.js +19 -306
- package/dist/src/pil/layer3-ee-injection.d.ts +19 -0
- package/dist/src/pil/layer3-ee-injection.js +96 -4
- package/dist/src/pil/layer4-gsd.js +18 -6
- package/dist/src/pil/layer6-output.d.ts +2 -0
- package/dist/src/pil/layer6-output.js +151 -25
- package/dist/src/pil/llm-classify.d.ts +26 -0
- package/dist/src/pil/llm-classify.js +34 -5
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +82 -76
- package/dist/src/pil/pipeline.js +15 -9
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +12 -1
- package/dist/src/pil/task-tier-map.js +4 -0
- package/dist/src/pil/types.d.ts +11 -1
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/loop-driver.js +18 -18
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/providers/auth/gemini-oauth.js +6 -15
- package/dist/src/providers/auth/grok-oauth.js +6 -15
- package/dist/src/providers/auth/openai-oauth.js +6 -15
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/reporter/index.js +1 -1
- 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 +24 -19
- package/dist/src/self-qa/spec-emitter.js +26 -23
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/sessions.js +42 -42
- package/dist/src/storage/transcript.js +91 -84
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/__tests__/native-tools.test.d.ts +1 -0
- package/dist/src/tools/__tests__/native-tools.test.js +53 -0
- package/dist/src/tools/git-safety.d.ts +61 -0
- package/dist/src/tools/git-safety.js +141 -0
- package/dist/src/tools/git-safety.test.d.ts +1 -0
- package/dist/src/tools/git-safety.test.js +111 -0
- package/dist/src/tools/native-tools.d.ts +31 -0
- package/dist/src/tools/native-tools.js +273 -0
- package/dist/src/tools/registry-ee-query.test.js +18 -1
- package/dist/src/tools/registry-git-safety.test.d.ts +7 -0
- package/dist/src/tools/registry-git-safety.test.js +92 -0
- package/dist/src/tools/registry.js +52 -6
- package/dist/src/ui/__tests__/markdown-render.test.d.ts +1 -0
- package/dist/src/ui/__tests__/markdown-render.test.js +48 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/message-view.js +4 -1
- package/dist/src/ui/components/structured-response-view.js +7 -3
- package/dist/src/ui/components/tool-group.js +7 -1
- package/dist/src/ui/markdown-render.d.ts +41 -0
- package/dist/src/ui/markdown-render.js +223 -0
- package/dist/src/ui/markdown.d.ts +10 -0
- package/dist/src/ui/markdown.js +12 -35
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/export.js +4 -4
- package/dist/src/ui/utils/text.d.ts +8 -0
- package/dist/src/ui/utils/text.js +16 -0
- package/dist/src/ui/utils/text.test.d.ts +1 -0
- package/dist/src/ui/utils/text.test.js +23 -0
- package/dist/src/usage/ledger.js +48 -15
- package/dist/src/utils/__tests__/footprint-gitignore.test.d.ts +1 -0
- package/dist/src/utils/__tests__/footprint-gitignore.test.js +50 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/open-url.d.ts +56 -0
- package/dist/src/utils/open-url.js +58 -0
- package/dist/src/utils/open-url.test.d.ts +1 -0
- package/dist/src/utils/open-url.test.js +86 -0
- package/dist/src/utils/settings.d.ts +12 -0
- package/dist/src/utils/settings.js +48 -0
- 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/dist/src/verify/environment.js +2 -1
- package/package.json +1 -1
- package/dist/src/pil/layer16-clarity.test.js +0 -31
- /package/dist/src/{pil/layer16-clarity.test.d.ts → council/__tests__/clarification-prompt.test.d.ts} +0 -0
|
@@ -1,19 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/pil/layer16-clarity.ts
|
|
3
|
+
*
|
|
4
|
+
* Phase 2 (2026-06-16): `detectClarityGaps` and its keyword option-builders
|
|
5
|
+
* (`buildOutcomeOptions` / `buildScopeOptions` / `pickBest*` / recency ranking)
|
|
6
|
+
* were removed. The configured chat model now decides every clarification —
|
|
7
|
+
* its questions, options, recommended default, and reason — in
|
|
8
|
+
* `proposeModelGaps` (`discovery.ts`). There is no regex gap synthesis.
|
|
9
|
+
*
|
|
10
|
+
* What remains here is gap RENDERING + RESOLUTION (consumed by the model path):
|
|
11
|
+
* - the "provide my own details" no-answer sentinel,
|
|
12
|
+
* - `buildInterviewQuestion` (ClarityGap → askcard),
|
|
13
|
+
* - `resolveGapsNonInteractive` (default-answer resolution when headless),
|
|
14
|
+
* - `getAutofilledOutcome` / `getDefaultOutcome` (outcome-label polish).
|
|
15
|
+
*/
|
|
1
16
|
import type { CouncilQuestionData } from "../types/index.js";
|
|
2
17
|
import type { ClarifiedIntent, ClarityGap, ProjectContext } from "./discovery-types.js";
|
|
3
18
|
import type { TaskType } from "./types.js";
|
|
4
|
-
export declare function detectClarityGaps(raw: string, taskType: TaskType | null, confidence: number, projectContext: ProjectContext): ClarityGap[];
|
|
5
19
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* was demoted to last. Only recommend a specific bounded context when the prompt
|
|
13
|
-
* literally names it (same word-overlap test buildScopeOptions uses); otherwise
|
|
14
|
-
* recommend "Entire project".
|
|
20
|
+
* The default "no specific answer" meta-option offered for a model-generated
|
|
21
|
+
* clarification when the model supplies no concrete recommendations. Selecting
|
|
22
|
+
* it means "use your judgment / I have nothing specific to add" — it is a
|
|
23
|
+
* sentinel, NOT a real outcome, so it must never surface verbatim as the
|
|
24
|
+
* resolved outcome. Centralised here so discovery.ts (which presents the
|
|
25
|
+
* option) and the outcome-resolution paths agree on the exact strings.
|
|
15
26
|
*/
|
|
16
|
-
export declare
|
|
27
|
+
export declare const PROVIDE_OWN_DETAILS_OPTION_EN = "I will provide my own details / constraints";
|
|
28
|
+
export declare const PROVIDE_OWN_DETAILS_OPTION_VI = "T\u00F4i s\u1EBD tr\u1EA3 l\u1EDDi t\u1EF1 do / cung c\u1EA5p chi ti\u1EBFt c\u1EA7n thi\u1EBFt";
|
|
29
|
+
/** True when an answer is the "I'll provide my own details" meta-option (any locale). */
|
|
30
|
+
export declare function isProvideOwnDetailsSentinel(answer: string | null | undefined): boolean;
|
|
17
31
|
export declare function buildInterviewQuestion(gap: ClarityGap, questionId: string): CouncilQuestionData;
|
|
18
32
|
export declare function resolveGapsNonInteractive(gaps: ClarityGap[], projectContext: ProjectContext, raw: string): ClarifiedIntent;
|
|
19
33
|
export declare function getAutofilledOutcome(taskType: TaskType | null, raw?: string): string | null;
|
|
@@ -1,309 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export function detectClarityGaps(raw, taskType, confidence, projectContext) {
|
|
3
|
-
const gaps = [];
|
|
4
|
-
// PIL-L6 fix — debug joins the autofill set. For "fix ci fail" the outcome
|
|
5
|
-
// is trivially "error resolved / pipeline green" and forcing an askcard
|
|
6
|
-
// there produces noise (the user already said "goal: ci green").
|
|
7
|
-
const AUTOFILL_OUTCOME_TYPES = new Set(["analyze", "plan", "documentation", "debug"]);
|
|
8
|
-
if (!canInferOutcome(taskType, raw)) {
|
|
9
|
-
if (taskType && AUTOFILL_OUTCOME_TYPES.has(taskType)) {
|
|
10
|
-
// These task types have predictable outcomes — auto-fill without asking
|
|
11
|
-
}
|
|
12
|
-
else if (!taskType || taskType === "general") {
|
|
13
|
-
// B2 intent-swallow fix — a `general` (or unclassified) prompt has no
|
|
14
|
-
// task-specific outcome options, so `buildOutcomeOptions` falls back to
|
|
15
|
-
// the tautological ["Task completed", "Issue resolved"]. Asking that
|
|
16
|
-
// askcard adds zero signal, and its default answer overwrites the intent
|
|
17
|
-
// → "general: Task completed", discarding the user's original request.
|
|
18
|
-
// Skip it; the outcome defaults to the raw prompt downstream
|
|
19
|
-
// (buildClarifiedIntentFromAnswers / getDefaultOutcome), preserving intent.
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
const outcomeOptions = buildOutcomeOptions(taskType, projectContext);
|
|
23
|
-
gaps.push({
|
|
24
|
-
dimension: "outcome",
|
|
25
|
-
description: "Cannot infer the expected outcome from the prompt",
|
|
26
|
-
suggestedQuestion: `What's the expected outcome? ${taskType === "debug" ? "(e.g., error gone, test passes, behavior fixed)" : "(e.g., feature works, file updated, test passes)"}`,
|
|
27
|
-
options: outcomeOptions,
|
|
28
|
-
defaultIndex: pickBestOutcomeIndex(taskType, outcomeOptions, raw),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// PIL-L6 fix — operational scope (CI / build / deploy / lint) is enough
|
|
33
|
-
// even without a file path. The task's target is the pipeline itself.
|
|
34
|
-
//
|
|
35
|
-
// B2-symmetric scope guard — the scope detector assumes EVERY prompt is a
|
|
36
|
-
// codebase task: any prompt lacking a file/module/operational reference gets
|
|
37
|
-
// asked "Which part of the codebase should this target?". For a general or
|
|
38
|
-
// unclassified prompt that has no codebase dimension at all (e.g. a pure
|
|
39
|
-
// chat / generation request like "Reply with one word: PONG", live session
|
|
40
|
-
// 8a87aa060c6a) this question is nonsensical, and because it is the only gap
|
|
41
|
-
// it also drags in a downstream acceptance card. Skip it for general/null —
|
|
42
|
-
// the same population the B2 outcome guard above protects. Scope then falls
|
|
43
|
-
// back to project-root in resolveGapsNonInteractive. Classified code tasks
|
|
44
|
-
// (debug/generate/refactor/…) still get the scope-narrowing askcard.
|
|
45
|
-
// Image-scope guard — an image-analysis task (e.g. "analyze diagram.png",
|
|
46
|
-
// "take a screenshot and describe it") is scoped to the IMAGE, not the
|
|
47
|
-
// codebase, so the "Which part of the codebase?" askcard is nonsensical for
|
|
48
|
-
// it. Symmetric to hasOperationalScope (pipeline-scoped). hasImageScope is
|
|
49
|
-
// deliberately narrow so it never swallows a real codebase task.
|
|
50
|
-
const scopeAppliesToCodebase = !!taskType && taskType !== "general";
|
|
51
|
-
if (scopeAppliesToCodebase &&
|
|
52
|
-
countFileReferences(raw) === 0 &&
|
|
53
|
-
!hasExplicitScope(raw) &&
|
|
54
|
-
!hasOperationalScope(raw) &&
|
|
55
|
-
!hasImageScope(raw) &&
|
|
56
|
-
!hasExternalInfoScope(raw) &&
|
|
57
|
-
// Whole-repo / eval prompts ("đánh giá repo", "review the entire codebase")
|
|
58
|
-
// are already scoped to everything — asking "which part?" (and recommending
|
|
59
|
-
// a narrow subdir as default) is nonsensical. See hasWholeRepoScope.
|
|
60
|
-
!hasWholeRepoScope(raw) &&
|
|
61
|
-
// Self-contained computation ("Compute f([3,1,2]) …") supplies its operand
|
|
62
|
-
// data inline — there is no codebase to scope. See hasSelfContainedComputationScope.
|
|
63
|
-
!hasSelfContainedComputationScope(raw)) {
|
|
64
|
-
const scopeOptions = buildScopeOptions(raw, projectContext);
|
|
65
|
-
gaps.push({
|
|
66
|
-
dimension: "scope",
|
|
67
|
-
description: "No specific file or module referenced",
|
|
68
|
-
suggestedQuestion: "Which part of the codebase should this target?",
|
|
69
|
-
options: scopeOptions,
|
|
70
|
-
defaultIndex: pickBestScopeIndex(raw, scopeOptions),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
const hasConstraint = /\b(\d+\s*ms|\d+\s*%|faster|slower|before|deadline|limit|max|min)\b/i.test(raw);
|
|
74
|
-
const isPerformanceTask = /\b(optimi[zs]e|performance|speed|fast|slow|latency|throughput)\b/i.test(raw);
|
|
75
|
-
if (isPerformanceTask && !hasConstraint) {
|
|
76
|
-
gaps.push({
|
|
77
|
-
dimension: "constraint",
|
|
78
|
-
description: "Performance target not specified",
|
|
79
|
-
suggestedQuestion: "Any specific performance target? (e.g., <200ms response, 50% faster)",
|
|
80
|
-
options: ["General improvement", "Specific latency target", "Reduce bundle size"],
|
|
81
|
-
defaultIndex: 0,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return gaps;
|
|
85
|
-
}
|
|
1
|
+
import { hasOperationalScope } from "./clarity-gate.js";
|
|
86
2
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* mentioned tests. This picks a more relevant option based on prompt
|
|
94
|
-
* keywords, with a fallback to 0 when nothing matches.
|
|
95
|
-
*
|
|
96
|
-
* Keep this list short — overengineering breaks predictability. We only
|
|
97
|
-
* encode the keyword→index pairs we've actually seen mismatch in the
|
|
98
|
-
* 5-baseline + sanity sessions.
|
|
3
|
+
* The default "no specific answer" meta-option offered for a model-generated
|
|
4
|
+
* clarification when the model supplies no concrete recommendations. Selecting
|
|
5
|
+
* it means "use your judgment / I have nothing specific to add" — it is a
|
|
6
|
+
* sentinel, NOT a real outcome, so it must never surface verbatim as the
|
|
7
|
+
* resolved outcome. Centralised here so discovery.ts (which presents the
|
|
8
|
+
* option) and the outcome-resolution paths agree on the exact strings.
|
|
99
9
|
*/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// "improve coverage", "add tests", "viết test" → "Tests added"
|
|
109
|
-
if (has(/\b(coverage|unit test|viết test|viet test|spec|jest|vitest|pytest)\b/) || has(/\btest(?:s|ing)?\b/)) {
|
|
110
|
-
const idx = find("test");
|
|
111
|
-
if (idx >= 0)
|
|
112
|
-
return idx;
|
|
113
|
-
}
|
|
114
|
-
// "scaffold", "boilerplate", "tạo file mới" → "File created with boilerplate"
|
|
115
|
-
if (has(/\b(scaffold|boilerplate|template|skeleton)\b/) || has(/\btạo file\b|\btao file\b/)) {
|
|
116
|
-
const idx = find("file created");
|
|
117
|
-
if (idx >= 0)
|
|
118
|
-
return idx;
|
|
119
|
-
}
|
|
120
|
-
return 0; // "Feature implemented and working"
|
|
121
|
-
}
|
|
122
|
-
case "refactor": {
|
|
123
|
-
// "performance", "speed", "faster" → "Better performance"
|
|
124
|
-
if (has(/\b(performance|speed|fast(er)?|slow|latency|throughput|optimi[zs]e)\b/)) {
|
|
125
|
-
const idx = find("performance");
|
|
126
|
-
if (idx >= 0)
|
|
127
|
-
return idx;
|
|
128
|
-
}
|
|
129
|
-
// "test", "testable" → "Easier to test"
|
|
130
|
-
if (has(/\b(testable|easier to test|unit test)\b/)) {
|
|
131
|
-
const idx = find("test");
|
|
132
|
-
if (idx >= 0)
|
|
133
|
-
return idx;
|
|
134
|
-
}
|
|
135
|
-
return 0; // "Code cleaner, same behavior"
|
|
136
|
-
}
|
|
137
|
-
case "debug": {
|
|
138
|
-
// "test fail", "test pass" → "Test passes"
|
|
139
|
-
if (has(/\btest(?:s|ing)? (?:fail|pass)/) || has(/\bspec fail/)) {
|
|
140
|
-
const idx = find("test passes");
|
|
141
|
-
if (idx >= 0)
|
|
142
|
-
return idx;
|
|
143
|
-
}
|
|
144
|
-
return 0; // "Error disappears"
|
|
145
|
-
}
|
|
146
|
-
case "documentation": {
|
|
147
|
-
if (has(/\b(readme)\b/)) {
|
|
148
|
-
const idx = find("readme");
|
|
149
|
-
if (idx >= 0)
|
|
150
|
-
return idx;
|
|
151
|
-
}
|
|
152
|
-
if (has(/\b(api docs|api documentation|openapi|swagger)\b/)) {
|
|
153
|
-
const idx = find("api docs");
|
|
154
|
-
if (idx >= 0)
|
|
155
|
-
return idx;
|
|
156
|
-
}
|
|
157
|
-
return 0;
|
|
158
|
-
}
|
|
159
|
-
case "plan": {
|
|
160
|
-
if (has(/\b(trade-?offs?|alternative|compare)\b/)) {
|
|
161
|
-
const idx = find("trade");
|
|
162
|
-
if (idx >= 0)
|
|
163
|
-
return idx;
|
|
164
|
-
}
|
|
165
|
-
if (has(/\b(step.?by.?step|phase|roadmap)\b/)) {
|
|
166
|
-
const idx = find("step-by-step");
|
|
167
|
-
if (idx >= 0)
|
|
168
|
-
return idx;
|
|
169
|
-
}
|
|
170
|
-
return 0;
|
|
171
|
-
}
|
|
172
|
-
case "analyze": {
|
|
173
|
-
if (has(/\b(root cause|why|tại sao|tai sao|crash|stack trace)\b/)) {
|
|
174
|
-
const idx = find("root cause");
|
|
175
|
-
if (idx >= 0)
|
|
176
|
-
return idx;
|
|
177
|
-
}
|
|
178
|
-
if (has(/\b(recommend|suggest|đề xuất|de xuat)\b/)) {
|
|
179
|
-
const idx = find("recommendations");
|
|
180
|
-
if (idx >= 0)
|
|
181
|
-
return idx;
|
|
182
|
-
}
|
|
183
|
-
return 0;
|
|
184
|
-
}
|
|
185
|
-
default:
|
|
186
|
-
return 0;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function buildOutcomeOptions(taskType, ctx) {
|
|
190
|
-
switch (taskType) {
|
|
191
|
-
case "debug":
|
|
192
|
-
return ["Error disappears", "Test passes", "Feature works correctly"];
|
|
193
|
-
case "refactor":
|
|
194
|
-
return ["Code cleaner, same behavior", "Better performance", "Easier to test"];
|
|
195
|
-
case "generate":
|
|
196
|
-
return ["Feature implemented and working", "File created with boilerplate", "Tests added"];
|
|
197
|
-
case "documentation":
|
|
198
|
-
return ["Docs updated", "README reflects current state", "API docs generated"];
|
|
199
|
-
case "plan":
|
|
200
|
-
return ["Architecture decided", "Step-by-step plan", "Trade-offs documented"];
|
|
201
|
-
case "analyze":
|
|
202
|
-
return ["Root cause identified", "Report generated", "Recommendations listed"];
|
|
203
|
-
default:
|
|
204
|
-
return ["Task completed", "Issue resolved"];
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Pick the "Recommended" default index for the scope askcard.
|
|
209
|
-
*
|
|
210
|
-
* Bug fixed (live obs 2026-06-04, deepseek session): the scope gap hardcoded
|
|
211
|
-
* defaultIndex 0, but buildScopeOptions lists recency-ranked (NOT prompt-matched)
|
|
212
|
-
* bounded contexts first when nothing matched — so the card recommended an
|
|
213
|
-
* arbitrary subdir (e.g. "src/cli") for a repo-wide prompt while "Entire project"
|
|
214
|
-
* was demoted to last. Only recommend a specific bounded context when the prompt
|
|
215
|
-
* literally names it (same word-overlap test buildScopeOptions uses); otherwise
|
|
216
|
-
* recommend "Entire project".
|
|
217
|
-
*/
|
|
218
|
-
export function pickBestScopeIndex(raw, options) {
|
|
219
|
-
const entireIdx = options.findIndex((o) => /entire project/i.test(o));
|
|
220
|
-
const fallback = entireIdx >= 0 ? entireIdx : Math.max(0, options.length - 1);
|
|
221
|
-
const words = raw
|
|
222
|
-
.toLowerCase()
|
|
223
|
-
.split(/\s+/)
|
|
224
|
-
.filter((w) => w.length > 2);
|
|
225
|
-
for (let i = 0; i < options.length; i++) {
|
|
226
|
-
const opt = options[i] ?? "";
|
|
227
|
-
if (/entire project/i.test(opt))
|
|
228
|
-
continue;
|
|
229
|
-
const nameMatch = opt.match(/\(([^)]+)\)\s*$/);
|
|
230
|
-
const name = (nameMatch?.[1] ?? opt).toLowerCase();
|
|
231
|
-
if (words.some((w) => name.includes(w) || w.includes(name)))
|
|
232
|
-
return i;
|
|
233
|
-
}
|
|
234
|
-
return fallback;
|
|
235
|
-
}
|
|
236
|
-
function buildScopeOptions(raw, ctx) {
|
|
237
|
-
const words = raw
|
|
238
|
-
.toLowerCase()
|
|
239
|
-
.split(/\s+/)
|
|
240
|
-
.filter((w) => w.length > 2);
|
|
241
|
-
const matching = ctx.boundedContexts.filter((bc) => {
|
|
242
|
-
const name = bc.name.toLowerCase();
|
|
243
|
-
return words.some((w) => name.includes(w) || w.includes(name));
|
|
244
|
-
});
|
|
245
|
-
const options = matching.map((bc) => `${bc.path} (${bc.name})`);
|
|
246
|
-
if (options.length === 0 && ctx.boundedContexts.length > 0) {
|
|
247
|
-
// Phase 5 F4 — when no keyword matches a module name, the previous
|
|
248
|
-
// fallback returned the first 3 alphabetically (which on muonroi-cli
|
|
249
|
-
// surfaced `agent-harness`, `billing`, `chat` — three OLD scaffolding
|
|
250
|
-
// folders that almost never match a fresh prompt). Rank by recency
|
|
251
|
-
// signal instead: most recently modified module dir comes first.
|
|
252
|
-
const ranked = rankModulesByRecency(ctx.boundedContexts, ctx.cwd);
|
|
253
|
-
options.push(...ranked.slice(0, 3).map((bc) => `${bc.path} (${bc.name})`));
|
|
254
|
-
}
|
|
255
|
-
options.push("Entire project");
|
|
256
|
-
return options.slice(0, 4);
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* F4 — rank bounded contexts by recency-of-modification of any tracked file
|
|
260
|
-
* inside. Falls back to alphabetical order when stat() throws for the dir.
|
|
261
|
-
* The 4-level depth cap + 50-entry-per-level cap keeps the walk under 200ms
|
|
262
|
-
* even on huge monorepos.
|
|
263
|
-
*/
|
|
264
|
-
function rankModulesByRecency(contexts, cwd) {
|
|
265
|
-
const fs = require("node:fs");
|
|
266
|
-
const path = require("node:path");
|
|
267
|
-
const scored = contexts.map((bc) => {
|
|
268
|
-
const dirPath = path.join(cwd, bc.path);
|
|
269
|
-
let maxMtime = 0;
|
|
270
|
-
try {
|
|
271
|
-
// walk up to 4 levels deep, cap entries per level
|
|
272
|
-
const walk = (dir, depth) => {
|
|
273
|
-
if (depth > 4)
|
|
274
|
-
return;
|
|
275
|
-
let entries = [];
|
|
276
|
-
try {
|
|
277
|
-
entries = fs.readdirSync(dir).slice(0, 50);
|
|
278
|
-
}
|
|
279
|
-
catch {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
for (const e of entries) {
|
|
283
|
-
if (e.startsWith(".") || e === "node_modules" || e === "dist")
|
|
284
|
-
continue;
|
|
285
|
-
try {
|
|
286
|
-
const full = path.join(dir, e);
|
|
287
|
-
const st = fs.statSync(full);
|
|
288
|
-
if (st.mtimeMs > maxMtime)
|
|
289
|
-
maxMtime = st.mtimeMs;
|
|
290
|
-
if (st.isDirectory())
|
|
291
|
-
walk(full, depth + 1);
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
/* skip unreadable entries */
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
walk(dirPath, 0);
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
/* fall back to mtime=0 — keeps the entry at the bottom of the ranked list */
|
|
302
|
-
}
|
|
303
|
-
return { bc, mtime: maxMtime };
|
|
304
|
-
});
|
|
305
|
-
scored.sort((a, b) => b.mtime - a.mtime);
|
|
306
|
-
return scored.map((s) => s.bc);
|
|
10
|
+
export const PROVIDE_OWN_DETAILS_OPTION_EN = "I will provide my own details / constraints";
|
|
11
|
+
export const PROVIDE_OWN_DETAILS_OPTION_VI = "Tôi sẽ trả lời tự do / cung cấp chi tiết cần thiết";
|
|
12
|
+
/** True when an answer is the "I'll provide my own details" meta-option (any locale). */
|
|
13
|
+
export function isProvideOwnDetailsSentinel(answer) {
|
|
14
|
+
if (!answer)
|
|
15
|
+
return false;
|
|
16
|
+
const norm = answer.trim().toLowerCase();
|
|
17
|
+
return norm === PROVIDE_OWN_DETAILS_OPTION_EN.toLowerCase() || norm === PROVIDE_OWN_DETAILS_OPTION_VI.toLowerCase();
|
|
307
18
|
}
|
|
308
19
|
export function buildInterviewQuestion(gap, questionId) {
|
|
309
20
|
const options = gap.options.map((label) => ({
|
|
@@ -335,7 +46,9 @@ export function resolveGapsNonInteractive(gaps, projectContext, raw) {
|
|
|
335
46
|
const defaultAnswer = gap.options[gap.defaultIndex] ?? gap.options[0] ?? "";
|
|
336
47
|
switch (gap.dimension) {
|
|
337
48
|
case "outcome":
|
|
338
|
-
|
|
49
|
+
// The "provide my own details" meta-option is a no-answer sentinel —
|
|
50
|
+
// leave outcome empty so the inferred/default outcome is used downstream.
|
|
51
|
+
outcome = isProvideOwnDetailsSentinel(defaultAnswer) ? "" : defaultAnswer;
|
|
339
52
|
break;
|
|
340
53
|
case "scope": {
|
|
341
54
|
const relevant = projectContext.relevantModules.map((m) => m.path);
|
|
@@ -377,7 +90,7 @@ export function getAutofilledOutcome(taskType, raw) {
|
|
|
377
90
|
// Prevents generic "Local path...", "In prompts/ directory...", "Complete the task..." in [Discovery]
|
|
378
91
|
return "Native self-assessment of the CLI with specific, actionable code fixes proposed and verified";
|
|
379
92
|
}
|
|
380
|
-
//
|
|
93
|
+
// Operational debug tasks (CI/build/deploy) have a stronger default outcome.
|
|
381
94
|
if (taskType === "debug" && hasOperationalScope(raw)) {
|
|
382
95
|
return "Pipeline green, all checks passing";
|
|
383
96
|
}
|
|
@@ -16,3 +16,22 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { PipelineContext } from "./types.js";
|
|
18
18
|
export declare function layer3EeInjection(ctx: PipelineContext): Promise<PipelineContext>;
|
|
19
|
+
/**
|
|
20
|
+
* Issue #4 — meta-turn TARGETED complement to Layer 3's checkpoint arm.
|
|
21
|
+
*
|
|
22
|
+
* Since issue #2, Layer 3 now runs on the meta-analysis path too, so its
|
|
23
|
+
* checkpoint arm already surfaces recent checkpoints/artifacts for the agent.
|
|
24
|
+
* That arm uses a FIXED recency query, though — it isn't biased toward the
|
|
25
|
+
* current meta question. This arm fills that gap: it searches by `ctx.raw` so a
|
|
26
|
+
* self-evaluating agent sees the elided tool-artifacts RELEVANT to what it's
|
|
27
|
+
* analyzing, rendered via the same `formatTaskCheckpoints` so the `[artifact]
|
|
28
|
+
* … id=X` refs appear automatically instead of waiting on a manual `ee_query`.
|
|
29
|
+
*
|
|
30
|
+
* Defers to Layer 3: if a checkpoint block was already injected this turn (any
|
|
31
|
+
* `ee-checkpoint-injected` marker present) it skips entirely — no duplicate
|
|
32
|
+
* block and no second EE round-trip. Gated on `sessionId` (no session ⇒ no prior
|
|
33
|
+
* compaction to rehydrate). Strictly additive and fail-open: any error /
|
|
34
|
+
* no-session / no-match / already-surfaced returns ctx with the original
|
|
35
|
+
* `enriched` plus an `ee-meta-artifacts` layer marker for forensics.
|
|
36
|
+
*/
|
|
37
|
+
export declare function surfaceCompactionArtifacts(ctx: PipelineContext): Promise<PipelineContext>;
|
|
@@ -119,7 +119,7 @@ async function queryEeBridge(raw) {
|
|
|
119
119
|
const [principleRaw, behavioralRaw, checkpointRaw] = await Promise.all([
|
|
120
120
|
searchByText(raw, ["experience-principles"], 3, signal),
|
|
121
121
|
searchByText(raw, ["experience-behavioral"], 4, signal),
|
|
122
|
-
searchByText(
|
|
122
|
+
searchByText('Context checkpoint summary OR "compaction checkpoint" recent Progress DONE elided OR tool-artifact OR "tool result id="', ["experience-behavioral"], 3, signal).catch(() => []),
|
|
123
123
|
]);
|
|
124
124
|
const principlePoints = principleRaw.filter((p) => (p.score ?? 0) >= PIL_PRINCIPLES_FLOOR);
|
|
125
125
|
const behavioralPoints = behavioralRaw.filter((p) => (p.score ?? 0) >= PIL_SCORE_FLOOR);
|
|
@@ -161,14 +161,16 @@ function formatExperienceHints(points) {
|
|
|
161
161
|
function formatTaskCheckpoints(points) {
|
|
162
162
|
if (points.length === 0)
|
|
163
163
|
return "";
|
|
164
|
-
const lines = points
|
|
164
|
+
const lines = points
|
|
165
|
+
.map((p) => {
|
|
165
166
|
const t = extractPointText(p);
|
|
166
167
|
// Idea 4: surface tool-artifact refs so agent sees "elided high-value, query for full"
|
|
167
168
|
if (/tool-artifact|tool result id=|elided.*id=/.test(t.toLowerCase())) {
|
|
168
169
|
return `- [artifact] ${t.slice(0, 160)} [id:${p.id}]`;
|
|
169
170
|
}
|
|
170
171
|
return `- ${t.slice(0, 180)} [id:${p.id}]`;
|
|
171
|
-
})
|
|
172
|
+
})
|
|
173
|
+
.filter((l) => l !== "- ");
|
|
172
174
|
if (lines.length === 0)
|
|
173
175
|
return "";
|
|
174
176
|
return `[task checkpoints — prior compactions: use to answer "task finished?", "compacted yet?". Artifacts: use ee.query tool with "tool-artifact id=XXX" for full elided tool output.] \n${lines.join("\n")}`;
|
|
@@ -282,7 +284,7 @@ export async function layer3EeInjection(ctx) {
|
|
|
282
284
|
const text = extractPointText(p);
|
|
283
285
|
return text.length === 0 || !checkpointMarkerShas.has(payloadSha16(text));
|
|
284
286
|
})
|
|
285
|
-
:
|
|
287
|
+
: result.checkpointPoints || [];
|
|
286
288
|
const allPoints = [...deduplicatedPrinciples, ...deduplicatedBehavioral, ...deduplicatedCheckpoints];
|
|
287
289
|
// STALE-01: Register injected point IDs for prompt-stale reconciliation.
|
|
288
290
|
updateLastSurfacedState(allPoints.map((p) => String(p.id)));
|
|
@@ -359,4 +361,94 @@ export async function layer3EeInjection(ctx) {
|
|
|
359
361
|
],
|
|
360
362
|
};
|
|
361
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Records whose text actually reads like a compaction checkpoint or an elided
|
|
366
|
+
* tool-artifact. Used to keep generic behavioral hits from being mislabelled as
|
|
367
|
+
* `[artifact]`/checkpoint lines when we search by the meta question (ctx.raw)
|
|
368
|
+
* rather than the fixed checkpoint-arm query.
|
|
369
|
+
*/
|
|
370
|
+
const CHECKPOINT_LIKE_RE = /context checkpoint summary|compaction checkpoint|tool-artifact|tool result id=|elided|progress[^a-z]*done|✔/i;
|
|
371
|
+
/**
|
|
372
|
+
* Issue #4 — meta-turn TARGETED complement to Layer 3's checkpoint arm.
|
|
373
|
+
*
|
|
374
|
+
* Since issue #2, Layer 3 now runs on the meta-analysis path too, so its
|
|
375
|
+
* checkpoint arm already surfaces recent checkpoints/artifacts for the agent.
|
|
376
|
+
* That arm uses a FIXED recency query, though — it isn't biased toward the
|
|
377
|
+
* current meta question. This arm fills that gap: it searches by `ctx.raw` so a
|
|
378
|
+
* self-evaluating agent sees the elided tool-artifacts RELEVANT to what it's
|
|
379
|
+
* analyzing, rendered via the same `formatTaskCheckpoints` so the `[artifact]
|
|
380
|
+
* … id=X` refs appear automatically instead of waiting on a manual `ee_query`.
|
|
381
|
+
*
|
|
382
|
+
* Defers to Layer 3: if a checkpoint block was already injected this turn (any
|
|
383
|
+
* `ee-checkpoint-injected` marker present) it skips entirely — no duplicate
|
|
384
|
+
* block and no second EE round-trip. Gated on `sessionId` (no session ⇒ no prior
|
|
385
|
+
* compaction to rehydrate). Strictly additive and fail-open: any error /
|
|
386
|
+
* no-session / no-match / already-surfaced returns ctx with the original
|
|
387
|
+
* `enriched` plus an `ee-meta-artifacts` layer marker for forensics.
|
|
388
|
+
*/
|
|
389
|
+
export async function surfaceCompactionArtifacts(ctx) {
|
|
390
|
+
const markLayer = (applied, delta) => ({
|
|
391
|
+
...ctx,
|
|
392
|
+
layers: [...ctx.layers, { name: "ee-meta-artifacts", applied, delta }],
|
|
393
|
+
});
|
|
394
|
+
if (!ctx.sessionId)
|
|
395
|
+
return markLayer(false, "no-session");
|
|
396
|
+
// Defer to Layer 3: a checkpoint/artifact block is already present this turn,
|
|
397
|
+
// so don't duplicate it or pay a second EE round-trip. This arm only fills the
|
|
398
|
+
// gap when Layer 3's fixed-query checkpoint arm surfaced nothing.
|
|
399
|
+
if (extractCheckpointMarkerShas(ctx.enriched).size > 0)
|
|
400
|
+
return markLayer(false, "already-surfaced");
|
|
401
|
+
let points = [];
|
|
402
|
+
try {
|
|
403
|
+
const signal = AbortSignal.timeout(PIL_SEARCH_TIMEOUT_MS);
|
|
404
|
+
// Bias toward records relevant to THIS meta question (ctx.raw) while pulling
|
|
405
|
+
// in checkpoint/artifact vocabulary so the single cheap arm lands on the
|
|
406
|
+
// compaction records rather than generic behavioral patterns.
|
|
407
|
+
const query = `${ctx.raw}\nContext checkpoint summary tool-artifact "tool result id=" elided Progress DONE`;
|
|
408
|
+
const raw = await searchByText(query, ["experience-behavioral"], 5, signal);
|
|
409
|
+
points = raw
|
|
410
|
+
.filter((p) => (p.score ?? 0) >= PIL_SCORE_FLOOR * 0.7)
|
|
411
|
+
.filter((p) => CHECKPOINT_LIKE_RE.test(extractPointText(p)));
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
logEeFailure("pil.meta.surfaceCompactionArtifacts", classifyEeError(err), err, { budgetMs: PIL_SEARCH_TIMEOUT_MS });
|
|
415
|
+
return markLayer(false, `error=${String(err)}`);
|
|
416
|
+
}
|
|
417
|
+
if (points.length === 0)
|
|
418
|
+
return markLayer(false, "no-artifacts");
|
|
419
|
+
const cpText = formatTaskCheckpoints(points);
|
|
420
|
+
if (!cpText)
|
|
421
|
+
return markLayer(false, "no-artifacts");
|
|
422
|
+
// Append the marker AFTER truncation so it always survives into `enriched`
|
|
423
|
+
// — that marker is what makes the defer-check above fire on any later pass.
|
|
424
|
+
const blockSha = payloadSha16(cpText);
|
|
425
|
+
const body = truncateToBudget(cpText, Math.floor(ctx.tokenBudget * 0.12));
|
|
426
|
+
const block = `${body}\n<!-- ee-checkpoint-injected:${blockSha} -->`;
|
|
427
|
+
try {
|
|
428
|
+
if (ctx.sessionId) {
|
|
429
|
+
logInteraction(ctx.sessionId, "ee_injection", {
|
|
430
|
+
eventSubtype: "injected",
|
|
431
|
+
data: {
|
|
432
|
+
phase: "pil_meta_artifacts",
|
|
433
|
+
role: "knowledge_retriever",
|
|
434
|
+
checkpointCount: points.length,
|
|
435
|
+
pointIds: points.map((p) => String(p.id)),
|
|
436
|
+
injectedChars: block.length,
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
catch (err) {
|
|
442
|
+
// No silent catch: surfacing succeeded; only the audit write failed.
|
|
443
|
+
console.error(`[pil.meta.surfaceCompactionArtifacts] interaction log failed: ${err?.message}`);
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
...ctx,
|
|
447
|
+
enriched: `${ctx.enriched}\n${block}`,
|
|
448
|
+
layers: [
|
|
449
|
+
...ctx.layers,
|
|
450
|
+
{ name: "ee-meta-artifacts", applied: true, delta: `artifacts=${points.length} chars=${block.length}` },
|
|
451
|
+
],
|
|
452
|
+
};
|
|
453
|
+
}
|
|
362
454
|
//# sourceMappingURL=layer3-ee-injection.js.map
|
|
@@ -23,7 +23,7 @@ import { detectGrayAreas } from "../gsd/gray-areas.js";
|
|
|
23
23
|
import { detectGsdPhase } from "../gsd/types.js";
|
|
24
24
|
import { classifyEeError, logEeFailure } from "../utils/ee-logger.js";
|
|
25
25
|
import { truncateToBudget } from "./budget.js";
|
|
26
|
-
import { isMetaAnalysisPrompt } from "./layer6-output.js";
|
|
26
|
+
import { isImplementationIntent, isMetaAnalysisPrompt, isQuestionLike } from "./layer6-output.js";
|
|
27
27
|
function mapRouteToPhase(route) {
|
|
28
28
|
switch (route) {
|
|
29
29
|
case "qc-flow":
|
|
@@ -79,11 +79,23 @@ export async function layer4Gsd(ctx) {
|
|
|
79
79
|
}
|
|
80
80
|
const complexity = scoreComplexity(ctx.raw);
|
|
81
81
|
const grayAreas = complexity.tier === "heavy" ? detectGrayAreas(ctx.raw).questions : [];
|
|
82
|
-
// Informational
|
|
83
|
-
//
|
|
84
|
-
// human-facing reply as a "2-3 line plan" + process narration
|
|
85
|
-
// 829a83888dd2). Route them to the human-facing question directive
|
|
86
|
-
|
|
82
|
+
// Informational prompts (a question / explanation / self-eval) ask for an
|
|
83
|
+
// ANSWER, not a code change. The implement/verify directive otherwise leaks
|
|
84
|
+
// into the human-facing reply as a "2-3 line plan" + process narration
|
|
85
|
+
// (session 829a83888dd2). Route them to the human-facing question directive.
|
|
86
|
+
//
|
|
87
|
+
// Phase 2b: when the model classified the deliverable, CONSUME it — an
|
|
88
|
+
// "answer" deliverable IS informational. Only when the model didn't emit one
|
|
89
|
+
// (deliverableKind null → legacy cascade, or the model omitted the word) do
|
|
90
|
+
// we fall back to the legacy regex predicates:
|
|
91
|
+
// 1. isMetaAnalysisPrompt — self/CLI evaluation, prior-turn reflection.
|
|
92
|
+
// 2. taskType "general" classified as a real task by L1.
|
|
93
|
+
// 3. question-shaped prompt that is NOT an implementation request.
|
|
94
|
+
const informational = ctx.deliverableKind
|
|
95
|
+
? ctx.deliverableKind === "answer"
|
|
96
|
+
: isMetaAnalysisPrompt(ctx.raw) ||
|
|
97
|
+
(ctx.taskType === "general" && ctx.intentKind === "task") ||
|
|
98
|
+
(isQuestionLike(ctx.raw) && !isImplementationIntent(ctx.raw));
|
|
87
99
|
const directive = buildDirective({ complexity, phase, grayAreas, informational });
|
|
88
100
|
const budgetChars = Math.floor(ctx.tokenBudget * DIRECTIVE_BUDGET_FRACTION);
|
|
89
101
|
const trimmed = truncateToBudget(directive.text, budgetChars);
|
|
@@ -18,5 +18,7 @@ import type { PipelineContext } from "./types.js";
|
|
|
18
18
|
export declare function isMetaAnalysisPrompt(raw: string): boolean;
|
|
19
19
|
export declare function applyPilSuffix(systemPrompt: string, ctx: PipelineContext, responseToolsActive?: boolean): string;
|
|
20
20
|
export declare function isImplementationIntent(raw: string): boolean;
|
|
21
|
+
export declare function isQuestionLike(raw: string): boolean;
|
|
22
|
+
export declare function prefersStructuredReport(raw: string): boolean;
|
|
21
23
|
export declare function getResponseToolSet(ctx: PipelineContext, providerId?: ProviderId): ToolSet;
|
|
22
24
|
export declare function layer6Output(ctx: PipelineContext): Promise<PipelineContext>;
|