gsd-pi 2.38.0-dev.add4f78 → 2.38.0-dev.d533afb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resource-loader.js +34 -1
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-loop.js +292 -263
- package/dist/resources/extensions/gsd/auto-post-unit.js +28 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +23 -43
- package/dist/resources/extensions/gsd/auto-start.js +7 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/auto.js +143 -80
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +2 -1
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-providers.js +27 -11
- package/dist/resources/extensions/gsd/doctor.js +20 -1
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/files.js +4 -0
- package/dist/resources/extensions/gsd/git-service.js +15 -12
- package/dist/resources/extensions/gsd/guided-flow.js +82 -32
- package/dist/resources/extensions/gsd/index.js +22 -19
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +58 -10
- package/dist/resources/extensions/gsd/preferences.js +4 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/dist/resources/extensions/gsd/prompts/run-uat.md +27 -10
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +19 -3
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-loop.ts +382 -360
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +25 -45
- package/src/resources/extensions/gsd/auto-start.ts +11 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/auto.ts +139 -86
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +2 -2
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-providers.ts +26 -9
- package/src/resources/extensions/gsd/doctor.ts +22 -1
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/files.ts +3 -1
- package/src/resources/extensions/gsd/git-service.ts +20 -10
- package/src/resources/extensions/gsd/guided-flow.ts +110 -38
- package/src/resources/extensions/gsd/index.ts +21 -16
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +4 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +50 -10
- package/src/resources/extensions/gsd/preferences.ts +3 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/src/resources/extensions/gsd/prompts/run-uat.md +27 -10
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +20 -3
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
- package/src/resources/extensions/gsd/types.ts +0 -1
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -72,11 +72,21 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
72
72
|
const summaryContent = await loadFile(summaryPath);
|
|
73
73
|
if (summaryContent) {
|
|
74
74
|
const summary = parseSummary(summaryContent);
|
|
75
|
+
// Look up GitHub issue number for commit linking
|
|
76
|
+
let ghIssueNumber;
|
|
77
|
+
try {
|
|
78
|
+
const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
|
|
79
|
+
ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// GitHub sync not available — skip
|
|
83
|
+
}
|
|
75
84
|
taskContext = {
|
|
76
85
|
taskId: `${sid}/${tid}`,
|
|
77
86
|
taskTitle: summary.title?.replace(/^T\d+:\s*/, "") || tid,
|
|
78
87
|
oneLiner: summary.oneLiner || undefined,
|
|
79
88
|
keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
|
|
89
|
+
issueNumber: ghIssueNumber,
|
|
80
90
|
};
|
|
81
91
|
}
|
|
82
92
|
}
|
|
@@ -94,6 +104,14 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
94
104
|
catch (e) {
|
|
95
105
|
debugLog("postUnit", { phase: "auto-commit", error: String(e) });
|
|
96
106
|
}
|
|
107
|
+
// GitHub sync (non-blocking, opt-in)
|
|
108
|
+
try {
|
|
109
|
+
const { runGitHubSync } = await import("../github-sync/sync.js");
|
|
110
|
+
await runGitHubSync(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
debugLog("postUnit", { phase: "github-sync", error: String(e) });
|
|
114
|
+
}
|
|
97
115
|
// Doctor: fix mechanical bookkeeping (skipped for lightweight sidecars)
|
|
98
116
|
if (!opts?.skipDoctor)
|
|
99
117
|
try {
|
|
@@ -105,12 +123,18 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
105
123
|
if (report.fixesApplied.length > 0) {
|
|
106
124
|
ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info");
|
|
107
125
|
}
|
|
108
|
-
// Proactive health tracking
|
|
109
|
-
|
|
126
|
+
// Proactive health tracking — filter to current milestone to avoid
|
|
127
|
+
// cross-milestone stale errors inflating the escalation counter
|
|
128
|
+
const currentMilestoneId = s.currentUnit.id.split("/")[0];
|
|
129
|
+
const milestoneIssues = currentMilestoneId
|
|
130
|
+
? report.issues.filter(i => i.unitId === currentMilestoneId ||
|
|
131
|
+
i.unitId.startsWith(`${currentMilestoneId}/`))
|
|
132
|
+
: report.issues;
|
|
133
|
+
const summary = summarizeDoctorIssues(milestoneIssues);
|
|
110
134
|
recordHealthSnapshot(summary.errors, summary.warnings, report.fixesApplied.length);
|
|
111
135
|
// Check if we should escalate to LLM-assisted heal
|
|
112
136
|
if (summary.errors > 0) {
|
|
113
|
-
const unresolvedErrors =
|
|
137
|
+
const unresolvedErrors = milestoneIssues
|
|
114
138
|
.filter(i => i.severity === "error" && !i.fixable)
|
|
115
139
|
.map(i => ({ code: i.code, message: i.message, unitId: i.unitId }));
|
|
116
140
|
const escalation = checkHealEscalation(summary.errors, unresolvedErrors);
|
|
@@ -123,6 +147,7 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
123
147
|
const reportText = formatDoctorReport(report, { scope: doctorScope, includeWarnings: true });
|
|
124
148
|
const structuredIssues = formatDoctorIssuesForPrompt(actionable);
|
|
125
149
|
dispatchDoctorHeal(pi, doctorScope, reportText, structuredIssues);
|
|
150
|
+
return "dispatched";
|
|
126
151
|
}
|
|
127
152
|
catch (e) {
|
|
128
153
|
debugLog("postUnit", { phase: "doctor-heal-dispatch", error: String(e) });
|
|
@@ -11,11 +11,15 @@ import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksD
|
|
|
11
11
|
import { resolveSkillDiscoveryMode, resolveInlineLevel, loadEffectiveGSDPreferences } from "./preferences.js";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
|
-
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
15
|
-
import { compressToTarget } from "./prompt-compressor.js";
|
|
16
|
-
import { distillSummaries } from "./summary-distiller.js";
|
|
14
|
+
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
17
15
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
18
|
-
|
|
16
|
+
// ─── Preamble Cap ─────────────────────────────────────────────────────────────
|
|
17
|
+
const MAX_PREAMBLE_CHARS = 30_000;
|
|
18
|
+
function capPreamble(preamble) {
|
|
19
|
+
if (preamble.length <= MAX_PREAMBLE_CHARS)
|
|
20
|
+
return preamble;
|
|
21
|
+
return truncateAtSectionBoundary(preamble, MAX_PREAMBLE_CHARS).content;
|
|
22
|
+
}
|
|
19
23
|
// ─── Executor Constraints ─────────────────────────────────────────────────────
|
|
20
24
|
/**
|
|
21
25
|
* Format executor context constraints for injection into the plan-slice prompt.
|
|
@@ -126,14 +130,9 @@ export async function inlineFileSmart(absPath, relPath, label, query, threshold
|
|
|
126
130
|
if (content.length <= threshold || !query) {
|
|
127
131
|
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
128
132
|
}
|
|
129
|
-
//
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
if (result.savingsPercent < 20) {
|
|
133
|
-
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
134
|
-
}
|
|
135
|
-
const formatted = formatChunks(result, relPath);
|
|
136
|
-
return `### ${label} (${result.omittedChunks} sections omitted for relevance)\nSource: \`${relPath}\`\n\n${formatted}`;
|
|
133
|
+
// For large files, truncate at section boundary
|
|
134
|
+
const truncated = truncateAtSectionBoundary(content, threshold).content;
|
|
135
|
+
return `### ${label}\nSource: \`${relPath}\`\n\n${truncated}`;
|
|
137
136
|
}
|
|
138
137
|
/**
|
|
139
138
|
* Load and inline dependency slice summaries (full content, not just paths).
|
|
@@ -165,21 +164,6 @@ export async function inlineDependencySummaries(mid, sid, base, budgetChars) {
|
|
|
165
164
|
}
|
|
166
165
|
const result = sections.join("\n\n");
|
|
167
166
|
if (budgetChars !== undefined && result.length > budgetChars) {
|
|
168
|
-
// For 3+ summaries, try distillation first (preserves more information)
|
|
169
|
-
if (sections.length >= 3) {
|
|
170
|
-
const rawSummaries = sections.map(s => {
|
|
171
|
-
// Extract content after the header line
|
|
172
|
-
const lines = s.split("\n");
|
|
173
|
-
const contentStart = lines.findIndex(l => l.startsWith("Source:"));
|
|
174
|
-
return contentStart >= 0 ? lines.slice(contentStart + 1).join("\n").trim() : s;
|
|
175
|
-
});
|
|
176
|
-
const distilled = distillSummaries(rawSummaries, budgetChars);
|
|
177
|
-
if (distilled.content.length <= budgetChars) {
|
|
178
|
-
return distilled.content;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// Fall back to section-boundary truncation
|
|
182
|
-
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
183
167
|
return truncateAtSectionBoundary(result, budgetChars).content;
|
|
184
168
|
}
|
|
185
169
|
return result;
|
|
@@ -546,7 +530,7 @@ export async function buildResearchMilestonePrompt(mid, midTitle, base) {
|
|
|
546
530
|
if (knowledgeInlineRM)
|
|
547
531
|
inlined.push(knowledgeInlineRM);
|
|
548
532
|
inlined.push(inlineTemplate("research", "Research"));
|
|
549
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
533
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
550
534
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
551
535
|
return loadPrompt("research-milestone", {
|
|
552
536
|
workingDirectory: base,
|
|
@@ -599,7 +583,7 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
|
|
|
599
583
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
600
584
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
601
585
|
}
|
|
602
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
586
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
603
587
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
604
588
|
const researchOutputPath = join(base, relMilestoneFile(base, mid, "RESEARCH"));
|
|
605
589
|
const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
|
|
@@ -647,7 +631,7 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
647
631
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
648
632
|
if (overridesInline)
|
|
649
633
|
inlined.unshift(overridesInline);
|
|
650
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
634
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
651
635
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
652
636
|
return loadPrompt("research-slice", {
|
|
653
637
|
workingDirectory: base,
|
|
@@ -693,7 +677,7 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
|
|
|
693
677
|
const planOverridesInline = formatOverridesSection(planActiveOverrides);
|
|
694
678
|
if (planOverridesInline)
|
|
695
679
|
inlined.unshift(planOverridesInline);
|
|
696
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
680
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
697
681
|
// Build executor context constraints from the budget engine
|
|
698
682
|
const executorContextConstraints = formatExecutorConstraints();
|
|
699
683
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
@@ -777,15 +761,11 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
|
777
761
|
const contextWindow = resolveExecutorContextWindow(undefined, prefs?.preferences);
|
|
778
762
|
const budgets = computeBudgets(contextWindow);
|
|
779
763
|
const verificationBudget = `~${Math.round(budgets.verificationBudgetChars / 1000)}K chars`;
|
|
780
|
-
//
|
|
781
|
-
// Only compress when compression_strategy is "compress" (budget/balanced profiles).
|
|
764
|
+
// Truncate carry-forward section when it exceeds 40% of inline context budget.
|
|
782
765
|
const carryForwardBudget = Math.floor(budgets.inlineContextBudgetChars * 0.4);
|
|
783
766
|
let finalCarryForward = carryForwardSection;
|
|
784
767
|
if (carryForwardSection.length > carryForwardBudget) {
|
|
785
|
-
|
|
786
|
-
if (resolveCompressionStrategy() === "compress") {
|
|
787
|
-
finalCarryForward = compressToTarget(carryForwardSection, carryForwardBudget).content;
|
|
788
|
-
}
|
|
768
|
+
finalCarryForward = truncateAtSectionBoundary(carryForwardSection, carryForwardBudget).content;
|
|
789
769
|
}
|
|
790
770
|
return loadPrompt("execute-task", {
|
|
791
771
|
overridesSection,
|
|
@@ -843,7 +823,7 @@ export async function buildCompleteSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
843
823
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
844
824
|
if (completeOverridesInline)
|
|
845
825
|
inlined.unshift(completeOverridesInline);
|
|
846
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
826
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
847
827
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
848
828
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
849
829
|
const sliceUatPath = join(base, `${sliceRel}/${sid}-UAT.md`);
|
|
@@ -899,7 +879,7 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
|
|
|
899
879
|
if (contextInline)
|
|
900
880
|
inlined.push(contextInline);
|
|
901
881
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
902
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
882
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
903
883
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
904
884
|
return loadPrompt("complete-milestone", {
|
|
905
885
|
workingDirectory: base,
|
|
@@ -966,7 +946,7 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
|
966
946
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
967
947
|
if (contextInline)
|
|
968
948
|
inlined.push(contextInline);
|
|
969
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
949
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
970
950
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
971
951
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
972
952
|
return loadPrompt("validate-milestone", {
|
|
@@ -1014,7 +994,7 @@ export async function buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base) {
|
|
|
1014
994
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
1015
995
|
if (replanOverridesInline)
|
|
1016
996
|
inlined.unshift(replanOverridesInline);
|
|
1017
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
997
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1018
998
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
1019
999
|
// Build capture context for replan prompt (captures that triggered this replan)
|
|
1020
1000
|
let captureContext = "(none)";
|
|
@@ -1054,7 +1034,7 @@ export async function buildRunUatPrompt(mid, sliceId, uatPath, uatContent, base)
|
|
|
1054
1034
|
const projectInline = await inlineProjectFromDb(base);
|
|
1055
1035
|
if (projectInline)
|
|
1056
1036
|
inlined.push(projectInline);
|
|
1057
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1037
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1058
1038
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
1059
1039
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
1060
1040
|
return loadPrompt("run-uat", {
|
|
@@ -1090,7 +1070,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
1090
1070
|
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
1091
1071
|
if (knowledgeInlineRA)
|
|
1092
1072
|
inlined.push(knowledgeInlineRA);
|
|
1093
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1073
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1094
1074
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
1095
1075
|
// Build deferred captures context for reassess prompt
|
|
1096
1076
|
let deferredCaptures = "(none)";
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { deriveState } from "./state.js";
|
|
12
12
|
import { loadFile, getManifestStatus } from "./files.js";
|
|
13
13
|
import { loadEffectiveGSDPreferences, resolveSkillDiscoveryMode, getIsolationMode, } from "./preferences.js";
|
|
14
|
-
import { ensureGsdSymlink } from "./repo-identity.js";
|
|
14
|
+
import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
|
|
15
15
|
import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
|
|
16
16
|
import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
|
|
17
17
|
import { gsdRoot, resolveMilestoneFile } from "./paths.js";
|
|
@@ -63,6 +63,12 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
63
63
|
return false;
|
|
64
64
|
}
|
|
65
65
|
try {
|
|
66
|
+
// Validate GSD_PROJECT_ID early so the user gets immediate feedback
|
|
67
|
+
const customProjectId = process.env.GSD_PROJECT_ID;
|
|
68
|
+
if (customProjectId && !validateProjectId(customProjectId)) {
|
|
69
|
+
ctx.ui.notify(`GSD_PROJECT_ID must contain only alphanumeric characters, hyphens, and underscores. Got: "${customProjectId}"`, "error");
|
|
70
|
+
return releaseLockAndReturn();
|
|
71
|
+
}
|
|
66
72
|
// Ensure git repo exists
|
|
67
73
|
if (!nativeIsRepo(base)) {
|
|
68
74
|
const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
|
|
@@ -15,10 +15,10 @@ import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
|
15
15
|
import { gsdRoot } from "./paths.js";
|
|
16
16
|
import { createWorktree, removeWorktree, worktreePath, } from "./worktree-manager.js";
|
|
17
17
|
import { detectWorktreeName, nudgeGitBranchCache, } from "./worktree.js";
|
|
18
|
-
import { MergeConflictError, readIntegrationBranch } from "./git-service.js";
|
|
18
|
+
import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
|
|
19
19
|
import { parseRoadmap } from "./files.js";
|
|
20
20
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
21
|
-
import { nativeGetCurrentBranch, nativeWorkingTreeStatus,
|
|
21
|
+
import { nativeGetCurrentBranch, nativeWorkingTreeStatus, nativeAddAllWithExclusions, nativeCommit, nativeCheckoutBranch, nativeMergeSquash, nativeConflictFiles, nativeCheckoutTheirs, nativeAddPaths, nativeRmForce, nativeBranchDelete, nativeBranchExists, } from "./native-git-bridge.js";
|
|
22
22
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
23
23
|
/** Original project root before chdir into auto-worktree. */
|
|
24
24
|
let originalBase = null;
|
|
@@ -656,7 +656,7 @@ function autoCommitDirtyState(cwd) {
|
|
|
656
656
|
const status = nativeWorkingTreeStatus(cwd);
|
|
657
657
|
if (!status)
|
|
658
658
|
return false;
|
|
659
|
-
|
|
659
|
+
nativeAddAllWithExclusions(cwd, RUNTIME_EXCLUSION_PATHS);
|
|
660
660
|
const result = nativeCommit(cwd, "chore: auto-commit before milestone merge");
|
|
661
661
|
return result !== null;
|
|
662
662
|
}
|
|
@@ -297,100 +297,163 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
297
297
|
return;
|
|
298
298
|
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
299
299
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
clearLock(lockBase());
|
|
303
|
-
if (lockBase())
|
|
304
|
-
releaseSessionLock(lockBase());
|
|
305
|
-
clearSkillSnapshot();
|
|
306
|
-
resetSkillTelemetry();
|
|
307
|
-
// Remove SIGTERM handler registered at auto-mode start
|
|
308
|
-
deregisterSigtermHandler();
|
|
309
|
-
// ── Auto-worktree: exit worktree and reset s.basePath on stop ──
|
|
310
|
-
if (s.currentMilestoneId) {
|
|
311
|
-
const notifyCtx = ctx
|
|
312
|
-
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
313
|
-
: { notify: () => { } };
|
|
314
|
-
buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
315
|
-
preserveBranch: true,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
// ── DB cleanup: close the SQLite connection ──
|
|
319
|
-
if (isDbAvailable()) {
|
|
300
|
+
try {
|
|
301
|
+
// ── Step 1: Timers and locks ──
|
|
320
302
|
try {
|
|
321
|
-
|
|
322
|
-
|
|
303
|
+
clearUnitTimeout();
|
|
304
|
+
if (lockBase())
|
|
305
|
+
clearLock(lockBase());
|
|
306
|
+
if (lockBase())
|
|
307
|
+
releaseSessionLock(lockBase());
|
|
323
308
|
}
|
|
324
309
|
catch (e) {
|
|
325
|
-
debugLog("
|
|
326
|
-
error: e instanceof Error ? e.message : String(e),
|
|
327
|
-
});
|
|
310
|
+
debugLog("stop-cleanup-locks", { error: e instanceof Error ? e.message : String(e) });
|
|
328
311
|
}
|
|
329
|
-
|
|
330
|
-
if (s.originalBasePath) {
|
|
331
|
-
s.basePath = s.originalBasePath;
|
|
312
|
+
// ── Step 2: Skill state ──
|
|
332
313
|
try {
|
|
333
|
-
|
|
314
|
+
clearSkillSnapshot();
|
|
315
|
+
resetSkillTelemetry();
|
|
334
316
|
}
|
|
335
|
-
catch {
|
|
336
|
-
|
|
317
|
+
catch (e) {
|
|
318
|
+
debugLog("stop-cleanup-skills", { error: e instanceof Error ? e.message : String(e) });
|
|
337
319
|
}
|
|
338
|
-
|
|
339
|
-
const ledger = getLedger();
|
|
340
|
-
if (ledger && ledger.units.length > 0) {
|
|
341
|
-
const totals = getProjectTotals(ledger.units);
|
|
342
|
-
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`, "info");
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
|
|
346
|
-
}
|
|
347
|
-
if (s.basePath) {
|
|
320
|
+
// ── Step 3: SIGTERM handler ──
|
|
348
321
|
try {
|
|
349
|
-
|
|
322
|
+
deregisterSigtermHandler();
|
|
350
323
|
}
|
|
351
324
|
catch (e) {
|
|
352
|
-
debugLog("stop-
|
|
353
|
-
error: e instanceof Error ? e.message : String(e),
|
|
354
|
-
});
|
|
325
|
+
debugLog("stop-cleanup-sigterm", { error: e instanceof Error ? e.message : String(e) });
|
|
355
326
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
327
|
+
// ── Step 4: Auto-worktree exit ──
|
|
328
|
+
try {
|
|
329
|
+
if (s.currentMilestoneId) {
|
|
330
|
+
const notifyCtx = ctx
|
|
331
|
+
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
332
|
+
: { notify: () => { } };
|
|
333
|
+
buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
334
|
+
preserveBranch: true,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
|
|
340
|
+
}
|
|
341
|
+
// ── Step 5: DB cleanup ──
|
|
342
|
+
if (isDbAvailable()) {
|
|
343
|
+
try {
|
|
344
|
+
const { closeDatabase } = await import("./gsd-db.js");
|
|
345
|
+
closeDatabase();
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
debugLog("db-close-failed", {
|
|
349
|
+
error: e instanceof Error ? e.message : String(e),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// ── Step 6: Restore basePath and chdir ──
|
|
354
|
+
try {
|
|
355
|
+
if (s.originalBasePath) {
|
|
356
|
+
s.basePath = s.originalBasePath;
|
|
357
|
+
try {
|
|
358
|
+
process.chdir(s.basePath);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
/* best-effort */
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
debugLog("stop-cleanup-basepath", { error: e instanceof Error ? e.message : String(e) });
|
|
367
|
+
}
|
|
368
|
+
// ── Step 7: Ledger notification ──
|
|
369
|
+
try {
|
|
370
|
+
const ledger = getLedger();
|
|
371
|
+
if (ledger && ledger.units.length > 0) {
|
|
372
|
+
const totals = getProjectTotals(ledger.units);
|
|
373
|
+
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`, "info");
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
debugLog("stop-cleanup-ledger", { error: e instanceof Error ? e.message : String(e) });
|
|
381
|
+
}
|
|
382
|
+
// ── Step 8: Rebuild state ──
|
|
383
|
+
if (s.basePath) {
|
|
384
|
+
try {
|
|
385
|
+
await rebuildState(s.basePath);
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
debugLog("stop-rebuild-state-failed", {
|
|
389
|
+
error: e instanceof Error ? e.message : String(e),
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ── Step 9: Cmux sidebar / event log ──
|
|
394
|
+
try {
|
|
395
|
+
clearCmuxSidebar(loadedPreferences);
|
|
396
|
+
logCmuxEvent(loadedPreferences, `Auto-mode stopped${reasonSuffix || ""}.`, reason?.startsWith("Blocked:") ? "warning" : "info");
|
|
397
|
+
}
|
|
398
|
+
catch (e) {
|
|
399
|
+
debugLog("stop-cleanup-cmux", { error: e instanceof Error ? e.message : String(e) });
|
|
400
|
+
}
|
|
401
|
+
// ── Step 10: Debug summary ──
|
|
402
|
+
try {
|
|
403
|
+
if (isDebugEnabled()) {
|
|
404
|
+
const logPath = writeDebugSummary();
|
|
405
|
+
if (logPath) {
|
|
406
|
+
ctx?.ui.notify(`Debug log written → ${logPath}`, "info");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (e) {
|
|
411
|
+
debugLog("stop-cleanup-debug", { error: e instanceof Error ? e.message : String(e) });
|
|
412
|
+
}
|
|
413
|
+
// ── Step 11: Reset metrics, routing, hooks ──
|
|
414
|
+
try {
|
|
415
|
+
resetMetrics();
|
|
416
|
+
resetRoutingHistory();
|
|
417
|
+
resetHookState();
|
|
418
|
+
if (s.basePath)
|
|
419
|
+
clearPersistedHookState(s.basePath);
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
debugLog("stop-cleanup-metrics", { error: e instanceof Error ? e.message : String(e) });
|
|
423
|
+
}
|
|
424
|
+
// ── Step 12: Remove paused-session metadata (#1383) ──
|
|
425
|
+
try {
|
|
426
|
+
const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
|
|
427
|
+
if (existsSync(pausedPath))
|
|
428
|
+
unlinkSync(pausedPath);
|
|
429
|
+
}
|
|
430
|
+
catch { /* non-fatal */ }
|
|
431
|
+
// ── Step 13: Restore original model (before reset clears IDs) ──
|
|
432
|
+
try {
|
|
433
|
+
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
434
|
+
const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
|
|
435
|
+
if (original)
|
|
436
|
+
await pi.setModel(original);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
|
|
363
441
|
}
|
|
364
442
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
379
|
-
const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
|
|
380
|
-
if (original)
|
|
381
|
-
await pi.setModel(original);
|
|
443
|
+
finally {
|
|
444
|
+
// ── Critical invariants: these MUST execute regardless of errors ──
|
|
445
|
+
// External cleanup (not covered by session reset)
|
|
446
|
+
clearInFlightTools();
|
|
447
|
+
clearSliceProgressCache();
|
|
448
|
+
clearActivityLogState();
|
|
449
|
+
resetProactiveHealing();
|
|
450
|
+
// UI cleanup
|
|
451
|
+
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
452
|
+
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
453
|
+
ctx?.ui.setFooter(undefined);
|
|
454
|
+
// Reset all session state in one call
|
|
455
|
+
s.reset();
|
|
382
456
|
}
|
|
383
|
-
// External cleanup (not covered by session reset)
|
|
384
|
-
clearInFlightTools();
|
|
385
|
-
clearSliceProgressCache();
|
|
386
|
-
clearActivityLogState();
|
|
387
|
-
resetProactiveHealing();
|
|
388
|
-
// UI cleanup
|
|
389
|
-
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
390
|
-
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
391
|
-
ctx?.ui.setFooter(undefined);
|
|
392
|
-
// Reset all session state in one call
|
|
393
|
-
s.reset();
|
|
394
457
|
}
|
|
395
458
|
/**
|
|
396
459
|
* Pause auto-mode without destroying state. Context is preserved.
|
|
@@ -631,7 +631,7 @@ export function serializePreferencesToFrontmatter(prefs) {
|
|
|
631
631
|
"dynamic_routing", "token_profile", "phases", "parallel",
|
|
632
632
|
"auto_visualize", "auto_report",
|
|
633
633
|
"verification_commands", "verification_auto_fix", "verification_max_retries",
|
|
634
|
-
"search_provider", "
|
|
634
|
+
"search_provider", "context_selection",
|
|
635
635
|
];
|
|
636
636
|
const seen = new Set();
|
|
637
637
|
for (const key of orderedKeys) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* One command, one wizard. Routes to smart entry or status.
|
|
5
5
|
*/
|
|
6
|
+
import { importExtensionModule } from "@gsd/pi-coding-agent";
|
|
6
7
|
import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
7
8
|
import { homedir } from "node:os";
|
|
8
9
|
import { join } from "node:path";
|
|
@@ -529,7 +530,7 @@ export async function handleGSDCommand(args, ctx, pi) {
|
|
|
529
530
|
return;
|
|
530
531
|
}
|
|
531
532
|
if (trimmed === "widget" || trimmed.startsWith("widget ")) {
|
|
532
|
-
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await import
|
|
533
|
+
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await importExtensionModule(import.meta.url, "./auto-dashboard.js");
|
|
533
534
|
const arg = trimmed.replace(/^widget\s*/, "").trim();
|
|
534
535
|
if (arg === "full" || arg === "small" || arg === "min" || arg === "off") {
|
|
535
536
|
setWidgetMode(arg);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* @see D001 (module location), D002 (200K fallback), D003 (section-boundary truncation)
|
|
9
9
|
*/
|
|
10
10
|
import { getCharsPerToken } from "./token-counter.js";
|
|
11
|
-
import { compressToTarget } from "./prompt-compressor.js";
|
|
12
11
|
// ─── Budget ratio constants ──────────────────────────────────────────────────
|
|
13
12
|
// Percentages of total context window allocated to each budget category.
|
|
14
13
|
// These are applied after tokens→chars conversion.
|
|
@@ -132,20 +131,13 @@ export function resolveExecutorContextWindow(registry, preferences, sessionConte
|
|
|
132
131
|
return DEFAULT_CONTEXT_WINDOW;
|
|
133
132
|
}
|
|
134
133
|
/**
|
|
135
|
-
*
|
|
136
|
-
* Returns the content within budget with maximum information preservation.
|
|
134
|
+
* Reduce content to fit within budget using section-boundary truncation.
|
|
137
135
|
*/
|
|
138
136
|
export function reduceToFit(content, budgetChars) {
|
|
139
137
|
if (!content || content.length <= budgetChars) {
|
|
140
138
|
return { content, droppedSections: 0 };
|
|
141
139
|
}
|
|
142
|
-
|
|
143
|
-
const compressed = compressToTarget(content, budgetChars);
|
|
144
|
-
if (compressed.compressedChars <= budgetChars) {
|
|
145
|
-
return { content: compressed.content, droppedSections: 0 };
|
|
146
|
-
}
|
|
147
|
-
// Step 2: Truncate the compressed content at section boundaries
|
|
148
|
-
return truncateAtSectionBoundary(compressed.content, budgetChars);
|
|
140
|
+
return truncateAtSectionBoundary(content, budgetChars);
|
|
149
141
|
}
|
|
150
142
|
// ─── Internal helpers ────────────────────────────────────────────────────────
|
|
151
143
|
/**
|
|
@@ -194,8 +194,6 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
194
194
|
|
|
195
195
|
- `search_provider`: `"brave"`, `"tavily"`, `"ollama"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.
|
|
196
196
|
|
|
197
|
-
- `compression_strategy`: `"truncate"` or `"compress"` — controls how context that exceeds the budget is reduced. `"truncate"` (default) drops sections from the end. `"compress"` applies heuristic compression before truncating, preserving more content at the cost of some fidelity. Default: `"truncate"`.
|
|
198
|
-
|
|
199
197
|
- `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.
|
|
200
198
|
|
|
201
199
|
- `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
|