gsd-pi 2.33.1-dev.ee47f1b → 2.34.0-dev.bbb5216
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/bundled-resource-path.d.ts +8 -0
- package/dist/bundled-resource-path.js +14 -0
- package/dist/headless-query.js +6 -6
- package/dist/resources/extensions/gsd/auto/session.js +27 -32
- package/dist/resources/extensions/gsd/auto-dashboard.js +29 -109
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +6 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +52 -81
- package/dist/resources/extensions/gsd/auto-loop.js +956 -0
- package/dist/resources/extensions/gsd/auto-observability.js +4 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +75 -185
- package/dist/resources/extensions/gsd/auto-prompts.js +133 -101
- package/dist/resources/extensions/gsd/auto-recovery.js +59 -97
- package/dist/resources/extensions/gsd/auto-start.js +330 -309
- package/dist/resources/extensions/gsd/auto-supervisor.js +5 -11
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +7 -7
- package/dist/resources/extensions/gsd/auto-timers.js +3 -4
- package/dist/resources/extensions/gsd/auto-verification.js +35 -73
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +167 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +291 -126
- package/dist/resources/extensions/gsd/auto.js +283 -1013
- package/dist/resources/extensions/gsd/captures.js +10 -4
- package/dist/resources/extensions/gsd/dispatch-guard.js +7 -8
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/dist/resources/extensions/gsd/doctor-checks.js +3 -4
- package/dist/resources/extensions/gsd/git-service.js +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +296 -151
- package/dist/resources/extensions/gsd/index.js +92 -228
- package/dist/resources/extensions/gsd/post-unit-hooks.js +13 -13
- package/dist/resources/extensions/gsd/progress-score.js +61 -156
- package/dist/resources/extensions/gsd/quick.js +98 -122
- package/dist/resources/extensions/gsd/session-lock.js +13 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/undo.js +43 -48
- package/dist/resources/extensions/gsd/unit-runtime.js +16 -15
- package/dist/resources/extensions/gsd/verification-evidence.js +0 -1
- package/dist/resources/extensions/gsd/verification-gate.js +6 -35
- package/dist/resources/extensions/gsd/worktree-command.js +30 -24
- package/dist/resources/extensions/gsd/worktree-manager.js +2 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +344 -0
- package/dist/resources/extensions/gsd/worktree.js +7 -44
- package/dist/tool-bootstrap.js +59 -11
- package/dist/worktree-cli.js +7 -7
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +3630 -5483
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +735 -2588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/src/models.generated.ts +1039 -2892
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/session.ts +47 -30
- package/src/resources/extensions/gsd/auto-dashboard.ts +28 -131
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +6 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +135 -91
- package/src/resources/extensions/gsd/auto-loop.ts +1665 -0
- package/src/resources/extensions/gsd/auto-observability.ts +4 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +85 -228
- package/src/resources/extensions/gsd/auto-prompts.ts +138 -109
- package/src/resources/extensions/gsd/auto-recovery.ts +124 -118
- package/src/resources/extensions/gsd/auto-start.ts +440 -354
- package/src/resources/extensions/gsd/auto-supervisor.ts +5 -12
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +8 -8
- package/src/resources/extensions/gsd/auto-timers.ts +3 -4
- package/src/resources/extensions/gsd/auto-verification.ts +76 -90
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +204 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +389 -141
- package/src/resources/extensions/gsd/auto.ts +515 -1199
- package/src/resources/extensions/gsd/captures.ts +10 -4
- package/src/resources/extensions/gsd/dispatch-guard.ts +13 -9
- package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/src/resources/extensions/gsd/doctor-checks.ts +3 -4
- package/src/resources/extensions/gsd/git-service.ts +8 -1
- package/src/resources/extensions/gsd/gitignore.ts +4 -2
- package/src/resources/extensions/gsd/gsd-db.ts +375 -180
- package/src/resources/extensions/gsd/index.ts +104 -263
- package/src/resources/extensions/gsd/post-unit-hooks.ts +13 -13
- package/src/resources/extensions/gsd/progress-score.ts +65 -200
- package/src/resources/extensions/gsd/quick.ts +121 -125
- package/src/resources/extensions/gsd/session-lock.ts +11 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +32 -59
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +75 -27
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1458 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +8 -162
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -108
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -55
- package/src/resources/extensions/gsd/tests/headless-query.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +8 -11
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/undo.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +24 -26
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +7 -201
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +705 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +57 -106
- package/src/resources/extensions/gsd/tests/worktree.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +43 -132
- package/src/resources/extensions/gsd/types.ts +90 -81
- package/src/resources/extensions/gsd/undo.ts +42 -46
- package/src/resources/extensions/gsd/unit-runtime.ts +14 -18
- package/src/resources/extensions/gsd/verification-evidence.ts +1 -3
- package/src/resources/extensions/gsd/verification-gate.ts +6 -39
- package/src/resources/extensions/gsd/worktree-command.ts +36 -24
- package/src/resources/extensions/gsd/worktree-manager.ts +2 -3
- package/src/resources/extensions/gsd/worktree-resolver.ts +485 -0
- package/src/resources/extensions/gsd/worktree.ts +7 -44
- package/dist/resources/extensions/gsd/auto-constants.js +0 -5
- package/dist/resources/extensions/gsd/auto-idempotency.js +0 -106
- package/dist/resources/extensions/gsd/auto-stuck-detection.js +0 -165
- package/dist/resources/extensions/gsd/mechanical-completion.js +0 -351
- package/src/resources/extensions/gsd/auto-constants.ts +0 -6
- package/src/resources/extensions/gsd/auto-idempotency.ts +0 -151
- package/src/resources/extensions/gsd/auto-stuck-detection.ts +0 -221
- package/src/resources/extensions/gsd/mechanical-completion.ts +0 -430
- package/src/resources/extensions/gsd/tests/auto-dispatch-loop.test.ts +0 -691
- package/src/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +0 -127
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +0 -123
- package/src/resources/extensions/gsd/tests/dispatch-stall-guard.test.ts +0 -126
- package/src/resources/extensions/gsd/tests/loop-regression.test.ts +0 -874
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +0 -356
- package/src/resources/extensions/gsd/tests/progress-score.test.ts +0 -206
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +0 -434
|
@@ -17,65 +17,35 @@
|
|
|
17
17
|
* agent_end — auto-mode advancement
|
|
18
18
|
* session_before_compact — save continue.md OR block during auto
|
|
19
19
|
*/
|
|
20
|
-
import { createBashTool,
|
|
20
|
+
import { createBashTool, createWriteTool, createReadTool, createEditTool, isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
21
21
|
import { Type } from "@sinclair/typebox";
|
|
22
22
|
import { debugTime } from "./debug-logger.js";
|
|
23
|
-
import {
|
|
23
|
+
import { registerGSDCommand } from "./commands.js";
|
|
24
24
|
import { loadToolApiKeys } from "./commands-config.js";
|
|
25
25
|
import { registerExitCommand } from "./exit-command.js";
|
|
26
|
-
import {
|
|
26
|
+
import { registerWorktreeCommand, getWorktreeOriginalCwd, getActiveWorktreeName } from "./worktree-command.js";
|
|
27
|
+
import { getActiveAutoWorktreeContext } from "./auto-worktree.js";
|
|
27
28
|
import { saveFile, formatContinue, loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection } from "./files.js";
|
|
28
29
|
import { loadPrompt } from "./prompt-loader.js";
|
|
30
|
+
import { deriveState } from "./state.js";
|
|
31
|
+
import { isAutoActive, isAutoPaused, pauseAuto, getAutoDashboardData, getAutoModeStartModel, markToolStart, markToolEnd } from "./auto.js";
|
|
32
|
+
import { isSessionSwitchInFlight, resolveAgentEnd } from "./auto-loop.js";
|
|
29
33
|
import { saveActivityLog } from "./activity-log.js";
|
|
34
|
+
import { checkAutoStartAfterDiscuss, getDiscussionMilestoneId, findMilestoneIds, nextMilestoneId } from "./guided-flow.js";
|
|
35
|
+
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
36
|
+
import { loadEffectiveGSDPreferences, renderPreferencesForSystemPrompt, resolveAllSkillReferences, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, } from "./preferences.js";
|
|
30
37
|
import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "./skill-discovery.js";
|
|
31
|
-
import { resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile, buildSliceFileName, buildMilestoneFileName,
|
|
38
|
+
import { resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile, buildSliceFileName, buildMilestoneFileName, resolveMilestonePath, resolveGsdRootFile, } from "./paths.js";
|
|
32
39
|
import { Key } from "@gsd/pi-tui";
|
|
33
40
|
import { join } from "node:path";
|
|
34
41
|
import { existsSync, readFileSync } from "node:fs";
|
|
35
42
|
import { homedir } from "node:os";
|
|
36
43
|
import { shortcutDesc } from "../shared/mod.js";
|
|
37
44
|
import { Text } from "@gsd/pi-tui";
|
|
45
|
+
import { pauseAutoForProviderError, classifyProviderError } from "./provider-error-pause.js";
|
|
38
46
|
import { toPosixPath } from "../shared/mod.js";
|
|
47
|
+
import { isParallelActive, shutdownParallel } from "./parallel-orchestrator.js";
|
|
39
48
|
import { DEFAULT_BASH_TIMEOUT_SECS } from "./constants.js";
|
|
40
|
-
import { getErrorMessage } from "./error-utils.js";
|
|
41
|
-
function memoizeImport(loader) {
|
|
42
|
-
let promise = null;
|
|
43
|
-
return () => {
|
|
44
|
-
if (!promise) {
|
|
45
|
-
promise = loader();
|
|
46
|
-
}
|
|
47
|
-
return promise;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
const loadAutoModule = memoizeImport(() => importExtensionModule(import.meta.url, "./auto.js"));
|
|
51
|
-
const loadStateModule = memoizeImport(() => importExtensionModule(import.meta.url, "./state.js"));
|
|
52
|
-
const loadGuidedFlowModule = memoizeImport(() => importExtensionModule(import.meta.url, "./guided-flow.js"));
|
|
53
|
-
const loadPreferencesModule = memoizeImport(() => importExtensionModule(import.meta.url, "./preferences.js"));
|
|
54
|
-
const loadDashboardOverlayModule = memoizeImport(() => importExtensionModule(import.meta.url, "./dashboard-overlay.js"));
|
|
55
|
-
const loadWorktreeCommandModule = memoizeImport(() => importExtensionModule(import.meta.url, "./worktree-command.js"));
|
|
56
|
-
const loadAutoWorktreeModule = memoizeImport(() => importExtensionModule(import.meta.url, "./auto-worktree.js"));
|
|
57
|
-
const loadProviderErrorPauseModule = memoizeImport(() => importExtensionModule(import.meta.url, "./provider-error-pause.js"));
|
|
58
|
-
const loadParallelOrchestratorModule = memoizeImport(() => importExtensionModule(import.meta.url, "./parallel-orchestrator.js"));
|
|
59
|
-
/**
|
|
60
|
-
* Ensure the GSD database is available, auto-initializing if needed.
|
|
61
|
-
* Returns true if the DB is ready, false if initialization failed.
|
|
62
|
-
*/
|
|
63
|
-
async function ensureDbAvailable() {
|
|
64
|
-
try {
|
|
65
|
-
const db = await importExtensionModule(import.meta.url, "./gsd-db.js");
|
|
66
|
-
if (db.isDbAvailable())
|
|
67
|
-
return true;
|
|
68
|
-
// Auto-initialize: open (and create if needed) the DB at the standard path
|
|
69
|
-
const gsdDir = gsdRoot(process.cwd());
|
|
70
|
-
if (!existsSync(gsdDir))
|
|
71
|
-
return false; // No GSD project — can't create DB
|
|
72
|
-
const dbPath = join(gsdDir, "gsd.db");
|
|
73
|
-
return db.openDatabase(dbPath);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
49
|
// ── Agent Instructions ────────────────────────────────────────────────────
|
|
80
50
|
// Lightweight "always follow" files injected into every GSD agent session.
|
|
81
51
|
// Global: ~/.gsd/agent-instructions.md Project: .gsd/agent-instructions.md
|
|
@@ -105,9 +75,7 @@ function loadAgentInstructions() {
|
|
|
105
75
|
return parts.join("\n\n");
|
|
106
76
|
}
|
|
107
77
|
// ── Depth verification state ──────────────────────────────────────────────
|
|
108
|
-
|
|
109
|
-
// Single-milestone flows set '*' (wildcard). Multi-milestone flows set per-ID.
|
|
110
|
-
const depthVerifiedMilestones = new Set();
|
|
78
|
+
let depthVerificationDone = false;
|
|
111
79
|
// ── Queue phase tracking ──────────────────────────────────────────────────
|
|
112
80
|
// When true, the LLM is in a queue flow writing CONTEXT.md files.
|
|
113
81
|
// The write-gate applies during queue flows just like discussion flows.
|
|
@@ -116,25 +84,10 @@ let activeQueuePhase = false;
|
|
|
116
84
|
// Tracks per-model retry attempts for transient network errors.
|
|
117
85
|
// Cleared when a model switch occurs or retries are exhausted.
|
|
118
86
|
const networkRetryCounters = new Map();
|
|
119
|
-
|
|
120
|
-
// Tracks consecutive transient auto-resume attempts. Each attempt doubles
|
|
121
|
-
// the delay. After MAX_TRANSIENT_AUTO_RESUMES consecutive failures, auto-mode
|
|
122
|
-
// pauses indefinitely to avoid infinite rapid-fire retries (#1166).
|
|
123
|
-
const MAX_TRANSIENT_AUTO_RESUMES = 5;
|
|
87
|
+
const MAX_TRANSIENT_AUTO_RESUMES = 3;
|
|
124
88
|
let consecutiveTransientErrors = 0;
|
|
125
89
|
export function isDepthVerified() {
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
/** Check whether a specific milestone has passed depth verification. */
|
|
129
|
-
export function isDepthVerifiedFor(milestoneId) {
|
|
130
|
-
// Wildcard means "all milestones verified" (single-milestone flow)
|
|
131
|
-
if (depthVerifiedMilestones.has("*"))
|
|
132
|
-
return true;
|
|
133
|
-
return depthVerifiedMilestones.has(milestoneId);
|
|
134
|
-
}
|
|
135
|
-
/** Mark a specific milestone as depth-verified. */
|
|
136
|
-
export function markDepthVerified(milestoneId) {
|
|
137
|
-
depthVerifiedMilestones.add(milestoneId);
|
|
90
|
+
return depthVerificationDone;
|
|
138
91
|
}
|
|
139
92
|
/** Check whether a queue phase is active. */
|
|
140
93
|
export function isQueuePhaseActive() {
|
|
@@ -156,25 +109,11 @@ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthV
|
|
|
156
109
|
return { block: false };
|
|
157
110
|
if (!MILESTONE_CONTEXT_RE.test(inputPath))
|
|
158
111
|
return { block: false };
|
|
159
|
-
|
|
160
|
-
if (inDiscussion && depthVerified)
|
|
112
|
+
if (depthVerified)
|
|
161
113
|
return { block: false };
|
|
162
|
-
// For queue flows: extract milestone ID from the path and check per-milestone verification
|
|
163
|
-
if (inQueue) {
|
|
164
|
-
const pathMatch = inputPath.match(/\/(M\d+(?:-[a-z0-9]{6})?)-CONTEXT\.md$/);
|
|
165
|
-
const targetMid = pathMatch?.[1];
|
|
166
|
-
if (targetMid && depthVerifiedMilestones.has(targetMid))
|
|
167
|
-
return { block: false };
|
|
168
|
-
// Wildcard passes all
|
|
169
|
-
if (depthVerifiedMilestones.has("*"))
|
|
170
|
-
return { block: false };
|
|
171
|
-
}
|
|
172
114
|
return {
|
|
173
115
|
block: true,
|
|
174
|
-
reason: `Blocked: Cannot write milestone CONTEXT.md without depth verification.
|
|
175
|
-
`Use ask_user_questions with a question id containing "depth_verification" first. ` +
|
|
176
|
-
`For multi-milestone flows, include the milestone ID in the question id (e.g., "depth_verification_M001"). ` +
|
|
177
|
-
`This ensures each milestone's context has been critically examined before being written.`,
|
|
116
|
+
reason: `Blocked: Cannot write to milestone CONTEXT.md during discussion phase without depth verification. Call ask_user_questions with question id "depth_verification" first to confirm discussion depth before writing context.`,
|
|
178
117
|
};
|
|
179
118
|
}
|
|
180
119
|
// ── ASCII logo ────────────────────────────────────────────────────────────
|
|
@@ -187,8 +126,8 @@ const GSD_LOGO_LINES = [
|
|
|
187
126
|
" ╚═════╝ ╚══════╝╚═════╝ ",
|
|
188
127
|
];
|
|
189
128
|
export default function (pi) {
|
|
190
|
-
|
|
191
|
-
|
|
129
|
+
registerGSDCommand(pi);
|
|
130
|
+
registerWorktreeCommand(pi);
|
|
192
131
|
registerExitCommand(pi);
|
|
193
132
|
// ── EPIPE guard — prevent crash when stdout/stderr pipe closes unexpectedly ──
|
|
194
133
|
// Node.js throws a fatal `Error: write EPIPE` when the parent process closes
|
|
@@ -197,22 +136,11 @@ export default function (pi) {
|
|
|
197
136
|
// chance to persist state and pause instead of crashing (see issue #739).
|
|
198
137
|
if (!process.listeners("uncaughtException").some(l => l.name === "_gsdEpipeGuard")) {
|
|
199
138
|
const _gsdEpipeGuard = (err) => {
|
|
200
|
-
|
|
201
|
-
if (code === "EPIPE") {
|
|
139
|
+
if (err.code === "EPIPE") {
|
|
202
140
|
// Pipe closed — nothing we can write; just exit cleanly
|
|
203
141
|
process.exit(0);
|
|
204
142
|
}
|
|
205
|
-
//
|
|
206
|
-
// sleep, heavy event loop stall, or filesystem precision mismatch on Node.js
|
|
207
|
-
// v25+). The onCompromised callback already set _lockCompromised = true, but
|
|
208
|
-
// due to a subtle interaction between the synchronous fs adapter and the
|
|
209
|
-
// setTimeout boundary, the error can still propagate here as an uncaught
|
|
210
|
-
// exception. Exit cleanly so the process.once("exit") handler removes the
|
|
211
|
-
// lock directory — allowing the next session to acquire cleanly (#1322).
|
|
212
|
-
if (code === "ECOMPROMISED") {
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
// Re-throw anything that isn't EPIPE or ECOMPROMISED so real crashes still surface
|
|
143
|
+
// Re-throw anything that isn't EPIPE so real crashes still surface
|
|
216
144
|
throw err;
|
|
217
145
|
};
|
|
218
146
|
process.on("uncaughtException", _gsdEpipeGuard);
|
|
@@ -301,8 +229,14 @@ export default function (pi) {
|
|
|
301
229
|
when_context: Type.Optional(Type.String({ description: "When/context for the decision (e.g. milestone ID)" })),
|
|
302
230
|
}),
|
|
303
231
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
304
|
-
//
|
|
305
|
-
|
|
232
|
+
// Check DB availability
|
|
233
|
+
let dbAvailable = false;
|
|
234
|
+
try {
|
|
235
|
+
const db = await import("./gsd-db.js");
|
|
236
|
+
dbAvailable = db.isDbAvailable();
|
|
237
|
+
}
|
|
238
|
+
catch { /* dynamic import failed */ }
|
|
239
|
+
if (!dbAvailable) {
|
|
306
240
|
return {
|
|
307
241
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot save decision." }],
|
|
308
242
|
isError: true,
|
|
@@ -310,7 +244,7 @@ export default function (pi) {
|
|
|
310
244
|
};
|
|
311
245
|
}
|
|
312
246
|
try {
|
|
313
|
-
const { saveDecisionToDb } = await
|
|
247
|
+
const { saveDecisionToDb } = await import("./db-writer.js");
|
|
314
248
|
const { id } = await saveDecisionToDb({
|
|
315
249
|
scope: params.scope,
|
|
316
250
|
decision: params.decision,
|
|
@@ -325,7 +259,7 @@ export default function (pi) {
|
|
|
325
259
|
};
|
|
326
260
|
}
|
|
327
261
|
catch (err) {
|
|
328
|
-
const msg =
|
|
262
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
329
263
|
process.stderr.write(`gsd-db: gsd_save_decision tool failed: ${msg}\n`);
|
|
330
264
|
return {
|
|
331
265
|
content: [{ type: "text", text: `Error saving decision: ${msg}` }],
|
|
@@ -357,8 +291,13 @@ export default function (pi) {
|
|
|
357
291
|
supporting_slices: Type.Optional(Type.String({ description: "Supporting slices" })),
|
|
358
292
|
}),
|
|
359
293
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
360
|
-
|
|
361
|
-
|
|
294
|
+
let dbAvailable = false;
|
|
295
|
+
try {
|
|
296
|
+
const db = await import("./gsd-db.js");
|
|
297
|
+
dbAvailable = db.isDbAvailable();
|
|
298
|
+
}
|
|
299
|
+
catch { /* dynamic import failed */ }
|
|
300
|
+
if (!dbAvailable) {
|
|
362
301
|
return {
|
|
363
302
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot update requirement." }],
|
|
364
303
|
isError: true,
|
|
@@ -367,7 +306,7 @@ export default function (pi) {
|
|
|
367
306
|
}
|
|
368
307
|
try {
|
|
369
308
|
// Verify requirement exists
|
|
370
|
-
const db = await
|
|
309
|
+
const db = await import("./gsd-db.js");
|
|
371
310
|
const existing = db.getRequirementById(params.id);
|
|
372
311
|
if (!existing) {
|
|
373
312
|
return {
|
|
@@ -376,7 +315,7 @@ export default function (pi) {
|
|
|
376
315
|
details: { operation: "update_requirement", id: params.id, error: "not_found" },
|
|
377
316
|
};
|
|
378
317
|
}
|
|
379
|
-
const { updateRequirementInDb } = await
|
|
318
|
+
const { updateRequirementInDb } = await import("./db-writer.js");
|
|
380
319
|
const updates = {};
|
|
381
320
|
if (params.status !== undefined)
|
|
382
321
|
updates.status = params.status;
|
|
@@ -397,7 +336,7 @@ export default function (pi) {
|
|
|
397
336
|
};
|
|
398
337
|
}
|
|
399
338
|
catch (err) {
|
|
400
|
-
const msg =
|
|
339
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
401
340
|
process.stderr.write(`gsd-db: gsd_update_requirement tool failed: ${msg}\n`);
|
|
402
341
|
return {
|
|
403
342
|
content: [{ type: "text", text: `Error updating requirement: ${msg}` }],
|
|
@@ -427,8 +366,13 @@ export default function (pi) {
|
|
|
427
366
|
content: Type.String({ description: "The full markdown content of the artifact" }),
|
|
428
367
|
}),
|
|
429
368
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
430
|
-
|
|
431
|
-
|
|
369
|
+
let dbAvailable = false;
|
|
370
|
+
try {
|
|
371
|
+
const db = await import("./gsd-db.js");
|
|
372
|
+
dbAvailable = db.isDbAvailable();
|
|
373
|
+
}
|
|
374
|
+
catch { /* dynamic import failed */ }
|
|
375
|
+
if (!dbAvailable) {
|
|
432
376
|
return {
|
|
433
377
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
|
|
434
378
|
isError: true,
|
|
@@ -456,7 +400,7 @@ export default function (pi) {
|
|
|
456
400
|
else {
|
|
457
401
|
relativePath = `milestones/${params.milestone_id}/${params.milestone_id}-${params.artifact_type}.md`;
|
|
458
402
|
}
|
|
459
|
-
const { saveArtifactToDb } = await
|
|
403
|
+
const { saveArtifactToDb } = await import("./db-writer.js");
|
|
460
404
|
await saveArtifactToDb({
|
|
461
405
|
path: relativePath,
|
|
462
406
|
artifact_type: params.artifact_type,
|
|
@@ -471,7 +415,7 @@ export default function (pi) {
|
|
|
471
415
|
};
|
|
472
416
|
}
|
|
473
417
|
catch (err) {
|
|
474
|
-
const msg =
|
|
418
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
475
419
|
process.stderr.write(`gsd-db: gsd_save_summary tool failed: ${msg}\n`);
|
|
476
420
|
return {
|
|
477
421
|
content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
|
|
@@ -505,10 +449,6 @@ export default function (pi) {
|
|
|
505
449
|
parameters: Type.Object({}),
|
|
506
450
|
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
507
451
|
try {
|
|
508
|
-
const [{ findMilestoneIds, nextMilestoneId }, { loadEffectiveGSDPreferences }] = await Promise.all([
|
|
509
|
-
loadGuidedFlowModule(),
|
|
510
|
-
loadPreferencesModule(),
|
|
511
|
-
]);
|
|
512
452
|
const basePath = process.cwd();
|
|
513
453
|
const existingIds = findMilestoneIds(basePath);
|
|
514
454
|
const uniqueEnabled = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
@@ -522,7 +462,7 @@ export default function (pi) {
|
|
|
522
462
|
};
|
|
523
463
|
}
|
|
524
464
|
catch (err) {
|
|
525
|
-
const msg =
|
|
465
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
526
466
|
return {
|
|
527
467
|
content: [{ type: "text", text: `Error generating milestone ID: ${msg}` }],
|
|
528
468
|
isError: true,
|
|
@@ -533,9 +473,8 @@ export default function (pi) {
|
|
|
533
473
|
});
|
|
534
474
|
// ── session_start: render branded GSD header + load tool keys + remote status ──
|
|
535
475
|
pi.on("session_start", async (_event, ctx) => {
|
|
536
|
-
// Clear
|
|
537
|
-
|
|
538
|
-
activeQueuePhase = false;
|
|
476
|
+
// Clear per-session state that must not leak across sessions (e.g. RPC mode)
|
|
477
|
+
depthVerificationDone = false;
|
|
539
478
|
// Theme access throws in RPC mode (no TUI) — header is decorative, skip it
|
|
540
479
|
try {
|
|
541
480
|
const theme = ctx.ui.theme;
|
|
@@ -550,17 +489,11 @@ export default function (pi) {
|
|
|
550
489
|
}
|
|
551
490
|
// Load tool API keys from auth.json into environment
|
|
552
491
|
loadToolApiKeys();
|
|
553
|
-
// Always-on health widget — ambient system health signal below the editor
|
|
554
|
-
try {
|
|
555
|
-
const { initHealthWidget } = await importExtensionModule(import.meta.url, "./health-widget.js");
|
|
556
|
-
initHealthWidget(ctx);
|
|
557
|
-
}
|
|
558
|
-
catch { /* non-fatal — widget is best-effort */ }
|
|
559
492
|
// Notify remote questions status if configured
|
|
560
493
|
try {
|
|
561
494
|
const [{ getRemoteConfigStatus }, { getLatestPromptSummary }] = await Promise.all([
|
|
562
|
-
|
|
563
|
-
|
|
495
|
+
import("../remote-questions/config.js"),
|
|
496
|
+
import("../remote-questions/status.js"),
|
|
564
497
|
]);
|
|
565
498
|
const status = getRemoteConfigStatus();
|
|
566
499
|
const latest = getLatestPromptSummary();
|
|
@@ -578,12 +511,11 @@ export default function (pi) {
|
|
|
578
511
|
description: shortcutDesc("Open GSD dashboard", "/gsd status"),
|
|
579
512
|
handler: async (ctx) => {
|
|
580
513
|
// Only show if .gsd/ exists
|
|
581
|
-
if (!existsSync(
|
|
514
|
+
if (!existsSync(join(process.cwd(), ".gsd"))) {
|
|
582
515
|
ctx.ui.notify("No .gsd/ directory found. Run /gsd to start.", "info");
|
|
583
516
|
return;
|
|
584
517
|
}
|
|
585
|
-
|
|
586
|
-
const result = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
518
|
+
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
587
519
|
return new GSDDashboardOverlay(tui, theme, () => done());
|
|
588
520
|
}, {
|
|
589
521
|
overlay: true,
|
|
@@ -594,20 +526,14 @@ export default function (pi) {
|
|
|
594
526
|
anchor: "center",
|
|
595
527
|
},
|
|
596
528
|
});
|
|
597
|
-
// Fallback for RPC mode where ctx.ui.custom() returns undefined.
|
|
598
|
-
if (result === undefined) {
|
|
599
|
-
const { fireStatusViaCommand } = await importExtensionModule(import.meta.url, "./commands.js");
|
|
600
|
-
await fireStatusViaCommand(ctx);
|
|
601
|
-
}
|
|
602
529
|
},
|
|
603
530
|
});
|
|
604
531
|
// ── before_agent_start: inject GSD contract into true system prompt ─────
|
|
605
532
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
606
|
-
if (!existsSync(
|
|
533
|
+
if (!existsSync(join(process.cwd(), ".gsd")))
|
|
607
534
|
return;
|
|
608
535
|
const stopContextTimer = debugTime("context-inject");
|
|
609
536
|
const systemContent = loadPrompt("system");
|
|
610
|
-
const { loadEffectiveGSDPreferences, resolveAllSkillReferences, renderPreferencesForSystemPrompt } = await loadPreferencesModule();
|
|
611
537
|
const loadedPreferences = loadEffectiveGSDPreferences();
|
|
612
538
|
let preferenceBlock = "";
|
|
613
539
|
if (loadedPreferences) {
|
|
@@ -636,7 +562,7 @@ export default function (pi) {
|
|
|
636
562
|
// Inject auto-learned project memories
|
|
637
563
|
let memoryBlock = "";
|
|
638
564
|
try {
|
|
639
|
-
const { getActiveMemoriesRanked, formatMemoriesForPrompt } = await
|
|
565
|
+
const { getActiveMemoriesRanked, formatMemoriesForPrompt } = await import("./memory-store.js");
|
|
640
566
|
const memories = getActiveMemoriesRanked(30);
|
|
641
567
|
if (memories.length > 0) {
|
|
642
568
|
const formatted = formatMemoriesForPrompt(memories, 2000);
|
|
@@ -663,10 +589,6 @@ export default function (pi) {
|
|
|
663
589
|
const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
|
|
664
590
|
// Worktree context — override the static CWD in the system prompt
|
|
665
591
|
let worktreeBlock = "";
|
|
666
|
-
const [{ getActiveWorktreeName, getWorktreeOriginalCwd }, { getActiveAutoWorktreeContext }] = await Promise.all([
|
|
667
|
-
loadWorktreeCommandModule(),
|
|
668
|
-
loadAutoWorktreeModule(),
|
|
669
|
-
]);
|
|
670
592
|
const worktreeName = getActiveWorktreeName();
|
|
671
593
|
const worktreeMainCwd = getWorktreeOriginalCwd();
|
|
672
594
|
const autoWorktree = getActiveAutoWorktreeContext();
|
|
@@ -728,27 +650,21 @@ export default function (pi) {
|
|
|
728
650
|
});
|
|
729
651
|
// ── agent_end: auto-mode advancement or auto-start after discuss ───────────
|
|
730
652
|
pi.on("agent_end", async (event, ctx) => {
|
|
731
|
-
const [{ isAutoActive, pauseAuto, getAutoDashboardData, getAutoModeStartModel, handleAgentEnd, }, { checkAutoStartAfterDiscuss }, { isTransientNetworkError, resolveModelWithFallbacksForUnit, getNextFallbackModel, }, { classifyProviderError, pauseAutoForProviderError },] = await Promise.all([
|
|
732
|
-
loadAutoModule(),
|
|
733
|
-
loadGuidedFlowModule(),
|
|
734
|
-
loadPreferencesModule(),
|
|
735
|
-
loadProviderErrorPauseModule(),
|
|
736
|
-
]);
|
|
737
|
-
// Clean up quick-task branch if one just completed (#1269)
|
|
738
|
-
try {
|
|
739
|
-
const { cleanupQuickBranch } = await importExtensionModule(import.meta.url, "./quick.js");
|
|
740
|
-
cleanupQuickBranch();
|
|
741
|
-
}
|
|
742
|
-
catch { /* non-fatal */ }
|
|
743
653
|
// If discuss phase just finished, start auto-mode
|
|
744
654
|
if (checkAutoStartAfterDiscuss()) {
|
|
745
|
-
|
|
655
|
+
depthVerificationDone = false;
|
|
746
656
|
activeQueuePhase = false;
|
|
747
657
|
return;
|
|
748
658
|
}
|
|
749
659
|
// If auto-mode is already running, advance to next unit
|
|
750
660
|
if (!isAutoActive())
|
|
751
661
|
return;
|
|
662
|
+
// Fresh-session auto-mode intentionally aborts the previous session during
|
|
663
|
+
// cmdCtx.newSession(). Ignore that agent_end so we neither pause nor
|
|
664
|
+
// resolve the new unit with an event from the old session.
|
|
665
|
+
if (isSessionSwitchInFlight()) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
752
668
|
// If the agent was aborted (user pressed Escape) or hit a provider
|
|
753
669
|
// error (fetch failure, rate limit, etc.), pause auto-mode instead of
|
|
754
670
|
// advancing. This preserves the conversation so the user can inspect
|
|
@@ -853,43 +769,39 @@ export default function (pi) {
|
|
|
853
769
|
const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number")
|
|
854
770
|
? lastMsg.retryAfterMs
|
|
855
771
|
: undefined;
|
|
856
|
-
let retryAfterMs = explicitRetryAfterMs ?? classification.suggestedDelayMs;
|
|
857
|
-
// ── Escalating backoff for repeated transient errors ──────────────
|
|
858
|
-
// Each consecutive transient auto-resume doubles the delay. After
|
|
859
|
-
// MAX_TRANSIENT_AUTO_RESUMES consecutive failures, treat as permanent
|
|
860
|
-
// to avoid infinite rapid-fire retries (#1166).
|
|
861
|
-
let effectiveTransient = classification.isTransient;
|
|
862
772
|
if (classification.isTransient) {
|
|
863
|
-
consecutiveTransientErrors
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
773
|
+
consecutiveTransientErrors += 1;
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
consecutiveTransientErrors = 0;
|
|
777
|
+
}
|
|
778
|
+
const baseRetryAfterMs = explicitRetryAfterMs ?? classification.suggestedDelayMs;
|
|
779
|
+
const retryAfterMs = classification.isTransient ? baseRetryAfterMs * 2 ** Math.max(0, consecutiveTransientErrors - 1) : baseRetryAfterMs;
|
|
780
|
+
const allowAutoResume = classification.isTransient
|
|
781
|
+
&& consecutiveTransientErrors <= MAX_TRANSIENT_AUTO_RESUMES;
|
|
782
|
+
if (classification.isTransient && !allowAutoResume) {
|
|
783
|
+
ctx.ui.notify(`Transient provider errors persisted after ${MAX_TRANSIENT_AUTO_RESUMES} auto-resume attempts. Pausing for manual review.`, "warning");
|
|
873
784
|
}
|
|
874
785
|
await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi), {
|
|
875
786
|
isRateLimit: classification.isRateLimit,
|
|
876
|
-
isTransient:
|
|
787
|
+
isTransient: allowAutoResume,
|
|
877
788
|
retryAfterMs,
|
|
878
|
-
resume:
|
|
879
|
-
|
|
880
|
-
|
|
789
|
+
resume: allowAutoResume
|
|
790
|
+
? () => {
|
|
791
|
+
pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution \u2014 provider error recovery delay elapsed.", display: false }, { triggerTurn: true });
|
|
792
|
+
}
|
|
793
|
+
: undefined,
|
|
881
794
|
});
|
|
882
795
|
return;
|
|
883
796
|
}
|
|
884
797
|
try {
|
|
798
|
+
consecutiveTransientErrors = 0;
|
|
885
799
|
networkRetryCounters.clear(); // Clear network retry state on successful unit completion
|
|
886
|
-
|
|
887
|
-
await handleAgentEnd(ctx, pi);
|
|
800
|
+
resolveAgentEnd(event);
|
|
888
801
|
}
|
|
889
802
|
catch (err) {
|
|
890
|
-
// Safety net: if
|
|
891
|
-
|
|
892
|
-
const message = getErrorMessage(err);
|
|
803
|
+
// Safety net: if resolveAgentEnd throws, ensure auto-mode stops gracefully (#381).
|
|
804
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
893
805
|
ctx.ui.notify(`Auto-mode error in agent_end handler: ${message}. Stopping auto-mode.`, "error");
|
|
894
806
|
try {
|
|
895
807
|
await pauseAuto(ctx, pi);
|
|
@@ -901,10 +813,6 @@ export default function (pi) {
|
|
|
901
813
|
});
|
|
902
814
|
// ── session_before_compact ────────────────────────────────────────────────
|
|
903
815
|
pi.on("session_before_compact", async (_event, _ctx) => {
|
|
904
|
-
const [{ isAutoActive, isAutoPaused }, { deriveState }] = await Promise.all([
|
|
905
|
-
loadAutoModule(),
|
|
906
|
-
loadStateModule(),
|
|
907
|
-
]);
|
|
908
816
|
// Block compaction during auto-mode — each unit is a fresh session
|
|
909
817
|
// Also block during paused state — context is valuable for the user
|
|
910
818
|
if (isAutoActive() || isAutoPaused()) {
|
|
@@ -948,29 +856,12 @@ export default function (pi) {
|
|
|
948
856
|
});
|
|
949
857
|
// ── session_shutdown: save activity log on Ctrl+C / SIGTERM ─────────────
|
|
950
858
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
951
|
-
const [{ isParallelActive, shutdownParallel }, { isAutoActive, isAutoPaused, getAutoDashboardData }] = await Promise.all([
|
|
952
|
-
loadParallelOrchestratorModule(),
|
|
953
|
-
loadAutoModule(),
|
|
954
|
-
]);
|
|
955
859
|
if (isParallelActive()) {
|
|
956
860
|
try {
|
|
957
861
|
await shutdownParallel(process.cwd());
|
|
958
862
|
}
|
|
959
863
|
catch { /* best-effort */ }
|
|
960
864
|
}
|
|
961
|
-
// Auto-commit dirty work in CLI-spawned worktrees so nothing is lost.
|
|
962
|
-
// The CLI sets GSD_CLI_WORKTREE when launched with -w.
|
|
963
|
-
const cliWorktree = process.env.GSD_CLI_WORKTREE;
|
|
964
|
-
if (cliWorktree) {
|
|
965
|
-
try {
|
|
966
|
-
const { autoCommitCurrentBranch } = await importExtensionModule(import.meta.url, "./worktree.js");
|
|
967
|
-
const msg = autoCommitCurrentBranch(process.cwd(), "session-end", cliWorktree);
|
|
968
|
-
if (msg) {
|
|
969
|
-
ctx.ui.notify(`Auto-committed worktree ${cliWorktree} before exit.`, "info");
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
catch { /* best-effort */ }
|
|
973
|
-
}
|
|
974
865
|
if (!isAutoActive() && !isAutoPaused())
|
|
975
866
|
return;
|
|
976
867
|
// Save the current session — the lock file stays on disk
|
|
@@ -980,56 +871,32 @@ export default function (pi) {
|
|
|
980
871
|
saveActivityLog(ctx, dash.basePath, dash.currentUnit.type, dash.currentUnit.id);
|
|
981
872
|
}
|
|
982
873
|
});
|
|
983
|
-
// ── tool_call: block CONTEXT.md writes without depth verification ──
|
|
984
|
-
// Active during both discussion flows (pendingAutoStart set) and
|
|
985
|
-
// queue flows (activeQueuePhase set). For multi-milestone queue flows,
|
|
986
|
-
// each milestone must pass its own depth verification before its
|
|
987
|
-
// CONTEXT.md can be written.
|
|
874
|
+
// ── tool_call: block CONTEXT.md writes during discussion without depth verification ──
|
|
988
875
|
pi.on("tool_call", async (event) => {
|
|
989
876
|
if (!isToolCallEventType("write", event))
|
|
990
877
|
return;
|
|
991
|
-
const { getDiscussionMilestoneId } = await loadGuidedFlowModule();
|
|
992
878
|
const result = shouldBlockContextWrite(event.toolName, event.input.path, getDiscussionMilestoneId(), isDepthVerified(), activeQueuePhase);
|
|
993
879
|
if (result.block)
|
|
994
880
|
return result;
|
|
995
881
|
});
|
|
996
882
|
// ── tool_result: persist discussion exchanges & detect depth gate ──────
|
|
997
|
-
// Handles both discussion flows and queue flows. For queue flows,
|
|
998
|
-
// depth verification question IDs may include milestone IDs
|
|
999
|
-
// (e.g., "depth_verification_M001") for per-milestone gating.
|
|
1000
883
|
pi.on("tool_result", async (event) => {
|
|
1001
884
|
if (event.toolName !== "ask_user_questions")
|
|
1002
885
|
return;
|
|
1003
|
-
const { getDiscussionMilestoneId } = await loadGuidedFlowModule();
|
|
1004
886
|
const milestoneId = getDiscussionMilestoneId();
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const inQueue = activeQueuePhase;
|
|
887
|
+
if (!milestoneId)
|
|
888
|
+
return;
|
|
1008
889
|
const details = event.details;
|
|
1009
890
|
if (details?.cancelled || !details?.response)
|
|
1010
891
|
return;
|
|
1011
892
|
// ── Depth gate detection ──────────────────────────────────────────
|
|
1012
|
-
// Supports two patterns:
|
|
1013
|
-
// 1. "depth_verification" — wildcard, marks all milestones verified
|
|
1014
|
-
// 2. "depth_verification_M001" — per-milestone verification
|
|
1015
893
|
const questions = event.input?.questions ?? [];
|
|
1016
894
|
for (const q of questions) {
|
|
1017
895
|
if (typeof q.id === "string" && q.id.includes("depth_verification")) {
|
|
1018
|
-
|
|
1019
|
-
const midMatch = q.id.match(/depth_verification[_-](M\d+(?:-[a-z0-9]{6})?)/i);
|
|
1020
|
-
if (midMatch) {
|
|
1021
|
-
depthVerifiedMilestones.add(midMatch[1]);
|
|
1022
|
-
}
|
|
1023
|
-
else {
|
|
1024
|
-
// Wildcard — all milestones verified (backward compat for single-milestone)
|
|
1025
|
-
depthVerifiedMilestones.add("*");
|
|
1026
|
-
}
|
|
896
|
+
depthVerificationDone = true;
|
|
1027
897
|
break;
|
|
1028
898
|
}
|
|
1029
899
|
}
|
|
1030
|
-
// Discussion persistence only applies when in a discussion flow with a known milestone
|
|
1031
|
-
if (!milestoneId)
|
|
1032
|
-
return;
|
|
1033
900
|
// ── Persist exchange to DISCUSSION.md ──────────────────────────────
|
|
1034
901
|
const basePath = process.cwd();
|
|
1035
902
|
const milestoneDir = resolveMilestonePath(basePath, milestoneId);
|
|
@@ -1069,13 +936,11 @@ export default function (pi) {
|
|
|
1069
936
|
});
|
|
1070
937
|
// ── tool_execution_start/end: track in-flight tools for idle detection ──
|
|
1071
938
|
pi.on("tool_execution_start", async (event) => {
|
|
1072
|
-
const { isAutoActive, markToolStart } = await loadAutoModule();
|
|
1073
939
|
if (!isAutoActive())
|
|
1074
940
|
return;
|
|
1075
941
|
markToolStart(event.toolCallId);
|
|
1076
942
|
});
|
|
1077
943
|
pi.on("tool_execution_end", async (event) => {
|
|
1078
|
-
const { markToolEnd } = await loadAutoModule();
|
|
1079
944
|
markToolEnd(event.toolCallId);
|
|
1080
945
|
});
|
|
1081
946
|
}
|
|
@@ -1088,7 +953,6 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
|
|
|
1088
953
|
const resumeMatch = prompt.match(/Resume interrupted work\.[\s\S]*?slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
|
|
1089
954
|
if (resumeMatch) {
|
|
1090
955
|
const [, sliceId, milestoneId] = resumeMatch;
|
|
1091
|
-
const { deriveState } = await loadStateModule();
|
|
1092
956
|
const state = await deriveState(basePath);
|
|
1093
957
|
if (state.activeMilestone?.id === milestoneId &&
|
|
1094
958
|
state.activeSlice?.id === sliceId &&
|