gsd-pi 2.82.0-dev.dfbc5f58f → 2.82.0-dev.e7a7f1ed5
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/README.md +1 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +73 -30
- package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +40 -13
- package/dist/resources/extensions/gsd/auto-start.js +3 -3
- package/dist/resources/extensions/gsd/auto-verification.js +17 -4
- package/dist/resources/extensions/gsd/auto-worktree.js +65 -9
- package/dist/resources/extensions/gsd/auto.js +7 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +27 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -2
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +7 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +16 -4
- package/dist/resources/extensions/gsd/db/milestone-leases.js +24 -0
- package/dist/resources/extensions/gsd/doctor-git-checks.js +46 -1
- package/dist/resources/extensions/gsd/git-service.js +6 -2
- package/dist/resources/extensions/gsd/gsd-db.js +20 -6
- package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -3
- package/dist/resources/extensions/gsd/guided-flow.js +95 -116
- package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
- package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
- package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
- package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
- package/dist/resources/extensions/gsd/status-guards.js +7 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.js +5 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js +41 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js.map +1 -0
- package/packages/pi-ai/src/providers/google-gemini-cli.test.ts +49 -0
- package/packages/pi-ai/src/providers/google-gemini-cli.ts +7 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js +103 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js.map +1 -0
- package/packages/pi-tui/dist/terminal.d.ts +2 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +12 -0
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/src/__tests__/terminal.test.ts +121 -0
- package/packages/pi-tui/src/terminal.ts +11 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +1 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +83 -37
- package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -16
- package/src/resources/extensions/gsd/auto-recovery.ts +45 -11
- package/src/resources/extensions/gsd/auto-start.ts +2 -3
- package/src/resources/extensions/gsd/auto-verification.ts +22 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +74 -9
- package/src/resources/extensions/gsd/auto.ts +8 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +36 -6
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +8 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +16 -2
- package/src/resources/extensions/gsd/db/milestone-leases.ts +26 -0
- package/src/resources/extensions/gsd/doctor-git-checks.ts +45 -1
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +6 -3
- package/src/resources/extensions/gsd/gsd-db.ts +18 -6
- package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +128 -133
- package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
- package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
- package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
- package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
- package/src/resources/extensions/gsd/status-guards.ts +8 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +76 -5
- package/src/resources/extensions/gsd/tests/auto-stop-notification.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
- package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +37 -1
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +13 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +29 -2
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +18 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +18 -1
- /package/dist/web/standalone/.next/static/{q0WYuDVbHeFFYbdd-fei2 → 4dSwdrs__8NwCZggxP9KF}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{q0WYuDVbHeFFYbdd-fei2 → 4dSwdrs__8NwCZggxP9KF}/_ssgManifest.js +0 -0
|
@@ -10,6 +10,7 @@ import { shouldIgnoreAgentEndForActiveUnit } from "../auto/unit-runner-events.js
|
|
|
10
10
|
import { resolveModelId } from "../auto-model-selection.js";
|
|
11
11
|
import { resolveProjectRoot } from "../worktree.js";
|
|
12
12
|
import { clearDiscussionFlowState } from "./write-gate.js";
|
|
13
|
+
import { clearGuidedUnitContext } from "../guided-unit-context.js";
|
|
13
14
|
import { resumeAutoAfterProviderDelay } from "./provider-error-resume.js";
|
|
14
15
|
import { classifyError, createRetryState, resetRetryState, isTransient, } from "../error-classifier.js";
|
|
15
16
|
import { blockModel, isModelBlocked } from "../blocked-models.js";
|
|
@@ -60,6 +61,11 @@ export function isUserInitiatedAbortMessage(message) {
|
|
|
60
61
|
return false;
|
|
61
62
|
return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
|
|
62
63
|
}
|
|
64
|
+
export function shouldDeferTransientErrorToCoreRetry(cls, rawErrorMsg) {
|
|
65
|
+
if (!isTransient(cls) || cls.kind === "rate-limit")
|
|
66
|
+
return false;
|
|
67
|
+
return !/retry failed after \d+ attempts:/i.test(rawErrorMsg);
|
|
68
|
+
}
|
|
63
69
|
function isBareClaudeCodeSessionSwitchAbortMarker(message) {
|
|
64
70
|
if (!message)
|
|
65
71
|
return false;
|
|
@@ -153,6 +159,13 @@ export function resolveAgentEndErrorDisplay(rawErrorMsg, content) {
|
|
|
153
159
|
}
|
|
154
160
|
return rawErrorMsg;
|
|
155
161
|
}
|
|
162
|
+
export function isTerminalDeletedWorktreeProviderError(message) {
|
|
163
|
+
if (!message)
|
|
164
|
+
return false;
|
|
165
|
+
if (!/\bdoes not exist\b/i.test(message))
|
|
166
|
+
return false;
|
|
167
|
+
return /[/\\]\.gsd[/\\](?:projects[/\\][^/\\]+[/\\])?worktrees[/\\][^/\\\s"']+/i.test(message);
|
|
168
|
+
}
|
|
156
169
|
async function pauseTransientWithBackoff(cls, pi, ctx, errorDetail, isRateLimit) {
|
|
157
170
|
retryState.consecutiveTransientCount += 1;
|
|
158
171
|
const baseRetryAfterMs = "retryAfterMs" in cls ? cls.retryAfterMs : 15_000;
|
|
@@ -190,8 +203,10 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
190
203
|
// falsely report files as missing — producing a spurious "ready signal
|
|
191
204
|
// rejected" loop even though the files are on disk.
|
|
192
205
|
clearPathCache();
|
|
206
|
+
const basePath = resolveAgentEndBasePath();
|
|
207
|
+
clearGuidedUnitContext(basePath);
|
|
193
208
|
try {
|
|
194
|
-
if (await checkDeepProjectSetupAfterTurn(event, ctx,
|
|
209
|
+
if (await checkDeepProjectSetupAfterTurn(event, ctx, basePath)) {
|
|
195
210
|
return;
|
|
196
211
|
}
|
|
197
212
|
}
|
|
@@ -199,22 +214,22 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
199
214
|
const message = err instanceof Error ? err.message : String(err);
|
|
200
215
|
logWarning("bootstrap", `checkDeepProjectSetupAfterTurn failed: ${message}`);
|
|
201
216
|
}
|
|
202
|
-
if (checkAutoStartAfterDiscuss()) {
|
|
203
|
-
clearDiscussionFlowState(
|
|
217
|
+
if (checkAutoStartAfterDiscuss(basePath)) {
|
|
218
|
+
clearDiscussionFlowState(basePath ?? process.cwd());
|
|
204
219
|
return;
|
|
205
220
|
}
|
|
206
221
|
// #4573 — When the LLM emits "Milestone X ready." but the required files
|
|
207
222
|
// are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
|
|
208
223
|
// that and nudge the LLM to complete the writes before the user hits the
|
|
209
224
|
// downstream "All milestones complete" warning loop.
|
|
210
|
-
if (maybeHandleReadyPhraseWithoutFiles(event))
|
|
225
|
+
if (maybeHandleReadyPhraseWithoutFiles(event, basePath))
|
|
211
226
|
return;
|
|
212
227
|
// #4573 — Empty-turn recovery: if the LLM announced intent in prose but
|
|
213
228
|
// emitted no tool calls, nudge it to execute. Fires only when auto-mode is
|
|
214
229
|
// active or a discussion autostart is pending (non-auto interactive discuss
|
|
215
230
|
// is user-driven). Runs before `isAutoActive` early return so pending
|
|
216
231
|
// discussions (where isAutoActive may be false) still get recovered.
|
|
217
|
-
if (maybeHandleEmptyIntentTurn(event, isAutoActive()))
|
|
232
|
+
if (maybeHandleEmptyIntentTurn(event, isAutoActive(), basePath))
|
|
218
233
|
return;
|
|
219
234
|
if (!isAutoActive())
|
|
220
235
|
return;
|
|
@@ -295,6 +310,12 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
295
310
|
// the assistant message text content for display purposes only.
|
|
296
311
|
// Classification still uses rawErrorMsg to avoid false positives from prose.
|
|
297
312
|
const displayMsg = resolveAgentEndErrorDisplay(rawErrorMsg, "content" in lastMsg ? lastMsg.content : undefined);
|
|
313
|
+
if (isAutoCompletionStopInProgress() &&
|
|
314
|
+
isTerminalDeletedWorktreeProviderError(`${rawErrorMsg}\n${displayMsg}`)) {
|
|
315
|
+
resetRetryState(retryState);
|
|
316
|
+
logWarning("bootstrap", `Ignoring stale deleted-worktree provider error during terminal completion reroot: ${displayMsg || rawErrorMsg}`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
298
319
|
const errorDetail = displayMsg ? `: ${displayMsg}` : "";
|
|
299
320
|
const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
|
|
300
321
|
// ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
|
|
@@ -378,7 +399,7 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
378
399
|
// Core retries transient failures in-session after this handler.
|
|
379
400
|
// Keep that behavior for non-rate-limit classes to avoid pause/retry races,
|
|
380
401
|
// but let rate-limit continue into model fallback logic below (#4373).
|
|
381
|
-
if (
|
|
402
|
+
if (shouldDeferTransientErrorToCoreRetry(cls, rawErrorMsg)) {
|
|
382
403
|
return;
|
|
383
404
|
}
|
|
384
405
|
// Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
|
|
@@ -24,6 +24,7 @@ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
|
24
24
|
import { extractSubagentAgentClasses } from "./subagent-input.js";
|
|
25
25
|
import { approvalGateIdForUnit, isExplicitApprovalResponse, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.js";
|
|
26
26
|
import { resolveSkillManifest } from "../skill-manifest.js";
|
|
27
|
+
import { getGuidedUnitContext } from "../guided-unit-context.js";
|
|
27
28
|
let approvalQuestionAbortInFlight = false;
|
|
28
29
|
async function loadWelcomeScreenModule() {
|
|
29
30
|
const candidates = [];
|
|
@@ -675,7 +676,8 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
675
676
|
// subagent dispatch. Closes the b23 bug class where a discuss-milestone
|
|
676
677
|
// turn used the host Edit tool to modify user source files.
|
|
677
678
|
const dash = getAutoRuntimeSnapshot();
|
|
678
|
-
const
|
|
679
|
+
const guidedUnit = getGuidedUnitContext(discussionBasePath);
|
|
680
|
+
const activeUnitType = dash.currentUnit?.type ?? guidedUnit?.unitType;
|
|
679
681
|
if (activeUnitType) {
|
|
680
682
|
const manifest = resolveManifest(activeUnitType);
|
|
681
683
|
if (manifest) {
|
|
@@ -694,7 +696,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
694
696
|
// Subagent inputs use { agent }, { tasks: [{ agent }] }, or { chain: [{ agent }] }.
|
|
695
697
|
agentClasses = extractSubagentAgentClasses(event.input);
|
|
696
698
|
}
|
|
697
|
-
const planningGuard = shouldBlockPlanningUnit(event.toolName, planningInput, dash.basePath || discussionBasePath, activeUnitType, manifest.tools, agentClasses);
|
|
699
|
+
const planningGuard = shouldBlockPlanningUnit(event.toolName, planningInput, dash.basePath || guidedUnit?.basePath || discussionBasePath, activeUnitType, manifest.tools, agentClasses);
|
|
698
700
|
if (planningGuard.block)
|
|
699
701
|
return planningGuard;
|
|
700
702
|
}
|
|
@@ -571,10 +571,15 @@ async function configureModels(ctx, prefs) {
|
|
|
571
571
|
];
|
|
572
572
|
const models = prefs.models ?? {};
|
|
573
573
|
const availableModels = ctx.modelRegistry.getAvailable();
|
|
574
|
-
|
|
574
|
+
const getAllWithDiscovered = ctx.modelRegistry.getAllWithDiscovered;
|
|
575
|
+
const availableProviders = new Set(availableModels.map((m) => m.provider));
|
|
576
|
+
const selectableModels = typeof getAllWithDiscovered === "function"
|
|
577
|
+
? getAllWithDiscovered().filter((m) => availableProviders.has(m.provider))
|
|
578
|
+
: availableModels;
|
|
579
|
+
if (selectableModels.length > 0) {
|
|
575
580
|
// Group models by provider, sorted alphabetically
|
|
576
581
|
const byProvider = new Map();
|
|
577
|
-
for (const m of
|
|
582
|
+
for (const m of selectableModels) {
|
|
578
583
|
let group = byProvider.get(m.provider);
|
|
579
584
|
if (!group) {
|
|
580
585
|
group = [];
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
import { emitJournalEvent, queryJournal, } from "./journal.js";
|
|
24
24
|
import { readFileSync, unlinkSync, existsSync } from "node:fs";
|
|
25
25
|
import { join } from "node:path";
|
|
26
|
-
import { findStaleWorkerForProject, getAllAutoWorkers, markWorkerCrashed, } from "./db/auto-workers.js";
|
|
26
|
+
import { findStaleWorkerForProject, getAllAutoWorkers, markWorkerCrashed, markWorkerStopping, } from "./db/auto-workers.js";
|
|
27
|
+
import { forceReleaseLeasesForWorker } from "./db/milestone-leases.js";
|
|
27
28
|
import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
|
|
28
29
|
import { getRuntimeKv, setRuntimeKv, deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
29
30
|
import { _getAdapter, isDbAvailable } from "./gsd-db.js";
|
|
@@ -182,10 +183,21 @@ export function clearLock(basePath) {
|
|
|
182
183
|
return;
|
|
183
184
|
try {
|
|
184
185
|
const projectRoot = normalizeRealPath(basePath);
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
186
|
+
const staleWorker = findStaleWorkerForProject(projectRoot);
|
|
187
|
+
if (staleWorker) {
|
|
188
|
+
markWorkerCrashed(staleWorker.worker_id);
|
|
189
|
+
forceReleaseLeasesForWorker(staleWorker.worker_id);
|
|
190
|
+
deleteRuntimeKv("worker", staleWorker.worker_id, SESSION_FILE_KV_KEY);
|
|
187
191
|
return;
|
|
188
|
-
|
|
192
|
+
}
|
|
193
|
+
const worker = findActiveWorkerForCurrentProcess(projectRoot);
|
|
194
|
+
if (worker)
|
|
195
|
+
deleteRuntimeKv("worker", worker.worker_id, SESSION_FILE_KV_KEY);
|
|
196
|
+
const stale = findStaleWorkerForProject(projectRoot);
|
|
197
|
+
if (stale) {
|
|
198
|
+
markWorkerStopping(stale.worker_id);
|
|
199
|
+
deleteRuntimeKv("worker", stale.worker_id, SESSION_FILE_KV_KEY);
|
|
200
|
+
}
|
|
189
201
|
}
|
|
190
202
|
catch {
|
|
191
203
|
// Best-effort.
|
|
@@ -193,6 +193,30 @@ export function releaseMilestoneLease(workerId, milestoneId, fencingToken) {
|
|
|
193
193
|
return changes === 1;
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Force-release all held leases for a worker.
|
|
198
|
+
*
|
|
199
|
+
* Used by crash recovery once PID liveness has confirmed the worker is dead.
|
|
200
|
+
* No fencing token is required because this path is cleanup-only for a
|
|
201
|
+
* non-running process.
|
|
202
|
+
*/
|
|
203
|
+
export function forceReleaseLeasesForWorker(workerId) {
|
|
204
|
+
if (!isDbAvailable())
|
|
205
|
+
return 0;
|
|
206
|
+
const db = _getAdapter();
|
|
207
|
+
let changes = 0;
|
|
208
|
+
transaction(() => {
|
|
209
|
+
const result = db.prepare(`UPDATE milestone_leases
|
|
210
|
+
SET status = 'released'
|
|
211
|
+
WHERE worker_id = :worker_id
|
|
212
|
+
AND status = 'held'`).run({ ":worker_id": workerId });
|
|
213
|
+
changes =
|
|
214
|
+
typeof result.changes === "number"
|
|
215
|
+
? result.changes
|
|
216
|
+
: 0;
|
|
217
|
+
});
|
|
218
|
+
return changes;
|
|
219
|
+
}
|
|
196
220
|
/**
|
|
197
221
|
* Read current lease row for diagnostics. Returns null if no row exists.
|
|
198
222
|
*/
|
|
@@ -7,7 +7,7 @@ import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
|
|
|
7
7
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
8
8
|
import { resolveMilestoneFile } from "./paths.js";
|
|
9
9
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
10
|
-
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
10
|
+
import { createWorktree, listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
11
11
|
import { abortAndReset } from "./git-self-heal.js";
|
|
12
12
|
import { RUNTIME_EXCLUSION_PATHS, resolveMilestoneIntegrationBranch, writeIntegrationBranch } from "./git-service.js";
|
|
13
13
|
import { nativeIsRepo, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeAddTracked, nativeCommit } from "./native-git-bridge.js";
|
|
@@ -50,6 +50,21 @@ function isSameOrNestedPath(candidate, container) {
|
|
|
50
50
|
return normalizedCandidate === normalizedContainer ||
|
|
51
51
|
normalizedCandidate.startsWith(`${normalizedContainer}/`);
|
|
52
52
|
}
|
|
53
|
+
function hasProjectContentOnDisk(dirPath) {
|
|
54
|
+
try {
|
|
55
|
+
for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
|
|
56
|
+
if (entry.name === ".git" || entry.name === ".gsd")
|
|
57
|
+
continue;
|
|
58
|
+
if (entry.name === ".DS_Store")
|
|
59
|
+
continue;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
53
68
|
function getSnapshotDiffCheckFailure(basePath) {
|
|
54
69
|
const failures = [];
|
|
55
70
|
for (const args of [["--cached"], []]) {
|
|
@@ -104,6 +119,36 @@ export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix,
|
|
|
104
119
|
const isComplete = milestoneEntry
|
|
105
120
|
? await isCompletedMilestoneTerminal(basePath, milestoneId)
|
|
106
121
|
: false;
|
|
122
|
+
if (!isComplete && !hasProjectContentOnDisk(wt.path) && hasProjectContentOnDisk(basePath)) {
|
|
123
|
+
issues.push({
|
|
124
|
+
severity: "error",
|
|
125
|
+
code: "worktree_empty_with_project_content",
|
|
126
|
+
scope: "milestone",
|
|
127
|
+
unitId: milestoneId,
|
|
128
|
+
message: `Worktree ${wt.path} has no project content, but project root ${basePath} does. Run doctor --fix to recreate the worktree.`,
|
|
129
|
+
fixable: true,
|
|
130
|
+
});
|
|
131
|
+
if (shouldFix("worktree_empty_with_project_content")) {
|
|
132
|
+
try {
|
|
133
|
+
nativeWorktreeRemove(basePath, wt.path, true);
|
|
134
|
+
const recreated = createWorktree(basePath, milestoneId, {
|
|
135
|
+
branch: wt.branch,
|
|
136
|
+
reuseExistingBranch: true,
|
|
137
|
+
});
|
|
138
|
+
const reset = spawnSync("git", ["reset", "--hard"], {
|
|
139
|
+
cwd: recreated.path,
|
|
140
|
+
encoding: "utf-8",
|
|
141
|
+
});
|
|
142
|
+
if (reset.status !== 0) {
|
|
143
|
+
throw new Error(reset.stderr || reset.error?.message || "git reset --hard failed");
|
|
144
|
+
}
|
|
145
|
+
fixesApplied.push(`recreated empty worktree ${wt.path}`);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
fixesApplied.push(`failed to recreate empty worktree ${wt.path}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
107
152
|
if (isComplete) {
|
|
108
153
|
issues.push({
|
|
109
154
|
severity: "warning",
|
|
@@ -237,6 +237,8 @@ export function readIntegrationBranch(basePath, milestoneId) {
|
|
|
237
237
|
return null;
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
+
/** Re-export for backward compatibility — canonical definitions in branch-patterns.ts */
|
|
241
|
+
export { QUICK_BRANCH_RE, WORKFLOW_BRANCH_RE } from "./branch-patterns.js";
|
|
240
242
|
/**
|
|
241
243
|
* Persist the integration branch for a milestone.
|
|
242
244
|
*
|
|
@@ -247,9 +249,11 @@ export function readIntegrationBranch(basePath, milestoneId) {
|
|
|
247
249
|
*
|
|
248
250
|
* The file is committed immediately so the metadata is persisted in git.
|
|
249
251
|
*/
|
|
250
|
-
/** Re-export for backward compatibility — canonical definitions in branch-patterns.ts */
|
|
251
|
-
export { QUICK_BRANCH_RE, WORKFLOW_BRANCH_RE } from "./branch-patterns.js";
|
|
252
252
|
export function writeIntegrationBranch(basePath, milestoneId, branch) {
|
|
253
|
+
// Never persist milestone branches as integration targets.
|
|
254
|
+
// They are ephemeral execution branches and can cause self-diff corruption.
|
|
255
|
+
if (branch.startsWith("milestone/"))
|
|
256
|
+
return;
|
|
253
257
|
// Don't record slice branches as the integration target
|
|
254
258
|
if (SLICE_BRANCH_RE.test(branch))
|
|
255
259
|
return;
|
|
@@ -54,18 +54,19 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
54
54
|
writeStderr: (message) => process.stderr.write(message),
|
|
55
55
|
});
|
|
56
56
|
export const SCHEMA_VERSION = 28;
|
|
57
|
-
function initSchema(db, fileBacked) {
|
|
57
|
+
function initSchema(db, fileBacked, dbPath) {
|
|
58
|
+
const conservativeFilePragmas = fileBacked && _isLikelyWslDrvFsPathForTest(dbPath);
|
|
58
59
|
if (fileBacked)
|
|
59
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
60
|
+
db.exec(conservativeFilePragmas ? "PRAGMA journal_mode=DELETE" : "PRAGMA journal_mode=WAL");
|
|
60
61
|
if (fileBacked)
|
|
61
62
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
62
63
|
if (fileBacked)
|
|
63
|
-
db.exec("PRAGMA synchronous = NORMAL");
|
|
64
|
+
db.exec(conservativeFilePragmas ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = NORMAL");
|
|
64
65
|
if (fileBacked)
|
|
65
66
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
66
67
|
if (fileBacked)
|
|
67
68
|
db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
|
|
68
|
-
if (fileBacked && process.platform !== "darwin")
|
|
69
|
+
if (fileBacked && !conservativeFilePragmas && process.platform !== "darwin")
|
|
69
70
|
db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
|
|
70
71
|
db.exec("PRAGMA temp_store = MEMORY");
|
|
71
72
|
db.exec("PRAGMA foreign_keys = ON");
|
|
@@ -99,6 +100,19 @@ function initSchema(db, fileBacked) {
|
|
|
99
100
|
}
|
|
100
101
|
migrateSchema(db);
|
|
101
102
|
}
|
|
103
|
+
export function _isLikelyWslDrvFsPathForTest(dbPath) {
|
|
104
|
+
if (!dbPath || process.platform !== "linux")
|
|
105
|
+
return false;
|
|
106
|
+
const drvFsPathPattern = /^\/mnt\/[a-z](?:\/|$)/i;
|
|
107
|
+
if (drvFsPathPattern.test(dbPath))
|
|
108
|
+
return true;
|
|
109
|
+
try {
|
|
110
|
+
return drvFsPathPattern.test(realpathSync(dbPath));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
102
116
|
/**
|
|
103
117
|
* Create the FTS5 virtual table for memories plus the triggers that keep it
|
|
104
118
|
* in sync with the base table. FTS5 may be unavailable on stripped-down
|
|
@@ -504,7 +518,7 @@ export function openDatabase(path) {
|
|
|
504
518
|
const adapter = createDbAdapter(rawDb);
|
|
505
519
|
const fileBacked = path !== ":memory:";
|
|
506
520
|
try {
|
|
507
|
-
initSchema(adapter, fileBacked);
|
|
521
|
+
initSchema(adapter, fileBacked, path);
|
|
508
522
|
}
|
|
509
523
|
catch (err) {
|
|
510
524
|
// Corrupt freelist: DDL fails with "malformed" but VACUUM can rebuild.
|
|
@@ -512,7 +526,7 @@ export function openDatabase(path) {
|
|
|
512
526
|
if (fileBacked && err instanceof Error && err.message?.includes("malformed")) {
|
|
513
527
|
try {
|
|
514
528
|
adapter.exec("VACUUM");
|
|
515
|
-
initSchema(adapter, fileBacked);
|
|
529
|
+
initSchema(adapter, fileBacked, path);
|
|
516
530
|
process.stderr.write("gsd-db: recovered corrupt database via VACUUM\n");
|
|
517
531
|
}
|
|
518
532
|
catch (retryErr) {
|
|
@@ -18,6 +18,7 @@ import { nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
|
|
|
18
18
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
19
19
|
import { saveQueueOrder } from "./queue-order.js";
|
|
20
20
|
import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
|
|
21
|
+
import { isFutureMilestoneStatus } from "./status-guards.js";
|
|
21
22
|
// ─── Queue Entry Point ──────────────────────────────────────────────────────
|
|
22
23
|
/**
|
|
23
24
|
* Queue future milestones via conversational intake.
|
|
@@ -48,7 +49,7 @@ export async function showQueue(ctx, pi, basePath) {
|
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
// ── Count pending milestones ────────────────────────────────────────
|
|
51
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
52
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status) || m.status === "active");
|
|
52
53
|
const completeCount = state.registry.filter(m => m.status === "complete").length;
|
|
53
54
|
const parkedCount = state.registry.filter(m => m.status === "parked").length;
|
|
54
55
|
// ── If multiple pending milestones, show queue management hub ──────
|
|
@@ -140,7 +141,7 @@ export async function showQueueAdd(ctx, pi, basePath, state) {
|
|
|
140
141
|
const activePart = state.activeMilestone
|
|
141
142
|
? `Currently executing: ${state.activeMilestone.id} — ${state.activeMilestone.title} (phase: ${state.phase}).`
|
|
142
143
|
: "No milestone currently active.";
|
|
143
|
-
const pendingCount = state.registry.filter(m => m.status
|
|
144
|
+
const pendingCount = state.registry.filter(m => isFutureMilestoneStatus(m.status)).length;
|
|
144
145
|
const completeCount = state.registry.filter(m => m.status === "complete").length;
|
|
145
146
|
const preamble = [
|
|
146
147
|
`Queuing new work onto an existing GSD project.`,
|
|
@@ -223,7 +224,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
|
|
|
223
224
|
}
|
|
224
225
|
// For active/pending/parked milestones, include the roadmap if it exists
|
|
225
226
|
// (shows what's planned but not yet built)
|
|
226
|
-
if (status === "active" || status
|
|
227
|
+
if (status === "active" || isFutureMilestoneStatus(status) || status === "parked") {
|
|
227
228
|
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
|
228
229
|
if (roadmapFile) {
|
|
229
230
|
const content = await loadFile(roadmapFile);
|