gsd-pi 2.77.0-dev.c9c8932c6 → 2.77.0-dev.eaa4973bc
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/cli-web-branch.d.ts +1 -0
- package/dist/cli-web-branch.js +3 -0
- package/dist/cli.js +24 -2
- package/dist/provider-migrations.d.ts +18 -0
- package/dist/provider-migrations.js +14 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +14 -10
- package/dist/resources/extensions/gsd/abandon-detect.js +44 -0
- package/dist/resources/extensions/gsd/auto/resolve.js +24 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto/turn-epoch.js +95 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +24 -0
- package/dist/resources/extensions/gsd/auto-loop.js +1 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +31 -0
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +11 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +58 -8
- package/dist/resources/extensions/gsd/auto.js +4 -0
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +4 -2
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +31 -4
- package/dist/resources/extensions/gsd/file-lock.js +49 -9
- package/dist/resources/extensions/gsd/git-service.js +1 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -1
- package/dist/resources/extensions/gsd/journal.js +17 -2
- package/dist/resources/extensions/gsd/milestone-actions.js +15 -0
- package/dist/resources/extensions/gsd/reports.js +5 -4
- package/dist/resources/extensions/gsd/tools/complete-slice.js +21 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +31 -0
- package/dist/resources/extensions/gsd/uok/audit.js +18 -2
- package/dist/resources/extensions/gsd/workflow-logger.js +10 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +1 -0
- package/dist/resources/extensions/mcp-client/auth.js +10 -1
- package/dist/resources/extensions/mcp-client/index.js +118 -9
- package/dist/resources/skills/create-skill/SKILL.md +2 -2
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +4 -4
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
- 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 +13 -13
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- 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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +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 +13 -13
- package/dist/web/standalone/.next/server/chunks/1926.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware-react-loadable-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/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-1832629448831fdc.js → webpack-2e68521d7c82f7c2.js} +1 -1
- package/package.json +3 -3
- package/packages/mcp-server/README.md +3 -3
- package/packages/mcp-server/dist/env-writer.d.ts +1 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -1
- package/packages/mcp-server/dist/env-writer.js +74 -6
- package/packages/mcp-server/dist/env-writer.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +25 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +79 -1
- package/packages/mcp-server/src/env-writer.ts +76 -6
- package/packages/mcp-server/src/server.ts +29 -2
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +21 -16
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js +130 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -1
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js +19 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +15 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +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 +14 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +7 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +31 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.test.ts +154 -0
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +24 -17
- package/packages/pi-coding-agent/src/core/sdk.test.ts +25 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +17 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +14 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +45 -11
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +19 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts +7 -0
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +20 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +25 -0
- package/packages/pi-tui/src/stdin-buffer.ts +26 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +14 -10
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +22 -16
- package/src/resources/extensions/gsd/abandon-detect.ts +62 -0
- package/src/resources/extensions/gsd/auto/resolve.ts +29 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +16 -2
- package/src/resources/extensions/gsd/auto/turn-epoch.ts +108 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +20 -0
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +30 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +14 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +60 -5
- package/src/resources/extensions/gsd/auto.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +27 -8
- package/src/resources/extensions/gsd/file-lock.ts +84 -11
- package/src/resources/extensions/gsd/git-service.ts +1 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -1
- package/src/resources/extensions/gsd/journal.ts +27 -2
- package/src/resources/extensions/gsd/milestone-actions.ts +18 -0
- package/src/resources/extensions/gsd/reports.ts +5 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +11 -2
- package/src/resources/extensions/gsd/tests/auto-mode-guards.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +86 -12
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +39 -25
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +13 -7
- package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/rewrite-docs-abandon-detect.test.ts +195 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +50 -2
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +162 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +38 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +49 -0
- package/src/resources/extensions/gsd/uok/audit.ts +20 -2
- package/src/resources/extensions/gsd/workflow-logger.ts +22 -3
- package/src/resources/extensions/gsd/worktree-manager.ts +1 -0
- package/src/resources/extensions/mcp-client/auth.ts +12 -1
- package/src/resources/extensions/mcp-client/index.ts +129 -10
- package/src/resources/skills/create-skill/SKILL.md +2 -2
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
- package/src/resources/skills/create-skill/workflows/audit-skill.md +4 -4
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
- package/dist/web/standalone/.next/server/chunks/7461.js +0 -1
- package/dist/web/standalone/.next/static/chunks/2826.d445fb428ef41fa1.js +0 -9
- /package/dist/web/standalone/.next/static/{H51SzzS_EEgdBsDVhrLBv → 5wbu35_C2_MQ3Jj1lEVDx}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{H51SzzS_EEgdBsDVhrLBv → 5wbu35_C2_MQ3Jj1lEVDx}/_ssgManifest.js +0 -0
package/dist/cli-web-branch.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface RunWebCliBranchDeps {
|
|
|
34
34
|
webPreferencesPath?: string;
|
|
35
35
|
}
|
|
36
36
|
export declare function parseCliArgs(argv: string[]): CliFlags;
|
|
37
|
+
export declare function buildHeadlessAutoArgs(flags: Pick<CliFlags, 'messages' | 'model'>): string[];
|
|
37
38
|
export { getProjectSessionsDir } from './project-sessions.js';
|
|
38
39
|
export declare function migrateLegacyFlatSessions(baseSessionsDir: string, projectSessionsDir: string): void;
|
|
39
40
|
/**
|
package/dist/cli-web-branch.js
CHANGED
|
@@ -73,6 +73,9 @@ export function parseCliArgs(argv) {
|
|
|
73
73
|
}
|
|
74
74
|
return flags;
|
|
75
75
|
}
|
|
76
|
+
export function buildHeadlessAutoArgs(flags) {
|
|
77
|
+
return flags.model ? ['--model', flags.model, ...flags.messages] : [...flags.messages];
|
|
78
|
+
}
|
|
76
79
|
export { getProjectSessionsDir } from './project-sessions.js';
|
|
77
80
|
export function migrateLegacyFlatSessions(baseSessionsDir, projectSessionsDir) {
|
|
78
81
|
if (!existsSync(baseSessionsDir))
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,8 @@ import { checkForUpdates } from './update-check.js';
|
|
|
12
12
|
import { printHelp, printSubcommandHelp } from './help-text.js';
|
|
13
13
|
import { applySecurityOverrides } from './security-overrides.js';
|
|
14
14
|
import { validateConfiguredModel } from './startup-model-validation.js';
|
|
15
|
-
import {
|
|
15
|
+
import { migrateAnthropicDefaultToClaudeCode } from './provider-migrations.js';
|
|
16
|
+
import { buildHeadlessAutoArgs, parseCliArgs, runWebCliBranch, migrateLegacyFlatSessions, } from './cli-web-branch.js';
|
|
16
17
|
import { stopWebMode } from './web-mode.js';
|
|
17
18
|
import { getProjectSessionsDir } from './project-sessions.js';
|
|
18
19
|
import { markStartup, printStartupTimings } from './startup-timings.js';
|
|
@@ -366,11 +367,18 @@ async function runHeadlessFromAuto(headlessArgs) {
|
|
|
366
367
|
await runHeadless(parseHeadlessArgs(argv));
|
|
367
368
|
process.exit(0);
|
|
368
369
|
}
|
|
370
|
+
function flushPendingProviderRegistrations(resourceLoader, modelRegistry) {
|
|
371
|
+
const { runtime } = resourceLoader.getExtensions();
|
|
372
|
+
for (const { name, config } of runtime.pendingProviderRegistrations) {
|
|
373
|
+
modelRegistry.registerProvider(name, config);
|
|
374
|
+
}
|
|
375
|
+
runtime.pendingProviderRegistrations = [];
|
|
376
|
+
}
|
|
369
377
|
// `gsd auto [args...]` — shorthand for `gsd headless auto [args...]` (#2732)
|
|
370
378
|
// Without this, `gsd auto` falls through to the interactive TUI which hangs
|
|
371
379
|
// when stdin/stdout are piped (non-TTY environments).
|
|
372
380
|
if (cliFlags.messages[0] === 'auto') {
|
|
373
|
-
await runHeadlessFromAuto(cliFlags
|
|
381
|
+
await runHeadlessFromAuto(buildHeadlessAutoArgs(cliFlags));
|
|
374
382
|
}
|
|
375
383
|
// Pi's tool bootstrap can mis-detect already-installed fd/rg on some systems
|
|
376
384
|
// because spawnSync(..., ["--version"]) returns EPERM despite a zero exit code.
|
|
@@ -504,6 +512,13 @@ if (isPrintMode) {
|
|
|
504
512
|
});
|
|
505
513
|
await resourceLoader.reload();
|
|
506
514
|
markStartup('resourceLoader.reload');
|
|
515
|
+
flushPendingProviderRegistrations(resourceLoader, modelRegistry);
|
|
516
|
+
migrateAnthropicDefaultToClaudeCode({
|
|
517
|
+
authStorage,
|
|
518
|
+
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
519
|
+
settingsManager,
|
|
520
|
+
modelRegistry,
|
|
521
|
+
});
|
|
507
522
|
const { session, extensionsResult, modelFallbackMessage } = await createAgentSession({
|
|
508
523
|
authStorage,
|
|
509
524
|
modelRegistry,
|
|
@@ -642,6 +657,13 @@ const resourceLoadPromise = resourceLoader.reload();
|
|
|
642
657
|
// Then await the resource promise before creating the agent session.
|
|
643
658
|
await resourceLoadPromise;
|
|
644
659
|
markStartup('resourceLoader.reload');
|
|
660
|
+
flushPendingProviderRegistrations(resourceLoader, modelRegistry);
|
|
661
|
+
migrateAnthropicDefaultToClaudeCode({
|
|
662
|
+
authStorage,
|
|
663
|
+
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
664
|
+
settingsManager,
|
|
665
|
+
modelRegistry,
|
|
666
|
+
});
|
|
645
667
|
const { session, extensionsResult, modelFallbackMessage: interactiveFallbackMsg } = await createAgentSession({
|
|
646
668
|
authStorage,
|
|
647
669
|
modelRegistry,
|
|
@@ -5,6 +5,24 @@ type AnthropicMigrationDeps = {
|
|
|
5
5
|
defaultProvider: string | undefined;
|
|
6
6
|
env?: NodeJS.ProcessEnv;
|
|
7
7
|
};
|
|
8
|
+
type MigrationModel = {
|
|
9
|
+
provider: string;
|
|
10
|
+
id: string;
|
|
11
|
+
};
|
|
12
|
+
type AnthropicDefaultMigrationDeps = {
|
|
13
|
+
authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
|
|
14
|
+
isClaudeCodeReady: boolean;
|
|
15
|
+
settingsManager: {
|
|
16
|
+
getDefaultProvider(): string | undefined;
|
|
17
|
+
getDefaultModel(): string | undefined;
|
|
18
|
+
setDefaultModelAndProvider(provider: string, modelId: string): void;
|
|
19
|
+
};
|
|
20
|
+
modelRegistry: {
|
|
21
|
+
getAvailable(): MigrationModel[];
|
|
22
|
+
};
|
|
23
|
+
env?: NodeJS.ProcessEnv;
|
|
24
|
+
};
|
|
8
25
|
export declare function hasDirectAnthropicApiKey(authStorage: Pick<AuthStorage, "getCredentialsForProvider">, env?: NodeJS.ProcessEnv): boolean;
|
|
9
26
|
export declare function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env, }: AnthropicMigrationDeps): boolean;
|
|
27
|
+
export declare function migrateAnthropicDefaultToClaudeCode({ authStorage, isClaudeCodeReady, settingsManager, modelRegistry, env, }: AnthropicDefaultMigrationDeps): boolean;
|
|
10
28
|
export {};
|
|
@@ -10,3 +10,17 @@ export function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeRe
|
|
|
10
10
|
}
|
|
11
11
|
return !hasDirectAnthropicApiKey(authStorage, env);
|
|
12
12
|
}
|
|
13
|
+
export function migrateAnthropicDefaultToClaudeCode({ authStorage, isClaudeCodeReady, settingsManager, modelRegistry, env = process.env, }) {
|
|
14
|
+
const defaultProvider = settingsManager.getDefaultProvider();
|
|
15
|
+
if (!shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env })) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const defaultModel = settingsManager.getDefaultModel();
|
|
19
|
+
const target = modelRegistry.getAvailable().find((model) => model.provider === "claude-code" && model.id === defaultModel) ||
|
|
20
|
+
modelRegistry.getAvailable().find((model) => model.provider === "claude-code");
|
|
21
|
+
if (!target) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
settingsManager.setDefaultModelAndProvider(target.provider, target.id);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
@@ -511,7 +511,7 @@ export async function resolveClaudePermissionMode(env = process.env) {
|
|
|
511
511
|
console.warn("[claude-code-cli] Headless mode detected (GSD_HEADLESS=1): defaulting permissionMode to 'bypassPermissions' so verification Bash commands can run. Set GSD_CLAUDE_CODE_PERMISSION_MODE=acceptEdits to opt out.");
|
|
512
512
|
return "bypassPermissions";
|
|
513
513
|
}
|
|
514
|
-
return "
|
|
514
|
+
return "bypassPermissions";
|
|
515
515
|
}
|
|
516
516
|
// NOTE: These helpers intentionally mirror @gsd/pi-ai anthropic-shared
|
|
517
517
|
// behavior so this extension remains typecheck-stable even when the published
|
|
@@ -563,21 +563,25 @@ function mapThinkingLevelToAnthropicEffort(level, modelId) {
|
|
|
563
563
|
export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
564
564
|
const { reasoning, ...sdkExtraOptions } = extraOptions;
|
|
565
565
|
const mcpServers = buildWorkflowMcpServers();
|
|
566
|
-
const permissionMode = overrides?.permissionMode ?? "
|
|
567
|
-
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
// every `
|
|
572
|
-
//
|
|
566
|
+
const permissionMode = overrides?.permissionMode ?? "bypassPermissions";
|
|
567
|
+
// Globally unblock all tools. Users reported that the `acceptEdits` default
|
|
568
|
+
// plus a narrow allowlist silently declined most Bash/Agent/WebFetch calls
|
|
569
|
+
// when `extensionUIContext` wasn't threaded through. Default to
|
|
570
|
+
// bypassPermissions and an empty disallow list so every tool — including
|
|
571
|
+
// `AskUserQuestion` and every `mcp__*` workflow tool — is auto-approved.
|
|
572
|
+
// Opt back into gated mode with GSD_CLAUDE_CODE_PERMISSION_MODE=acceptEdits.
|
|
573
|
+
const disallowedTools = [];
|
|
573
574
|
const allowedTools = [
|
|
574
575
|
"Read",
|
|
575
576
|
"Write",
|
|
576
577
|
"Edit",
|
|
577
578
|
"Glob",
|
|
578
579
|
"Grep",
|
|
579
|
-
"Bash
|
|
580
|
-
"
|
|
580
|
+
"Bash",
|
|
581
|
+
"Agent",
|
|
582
|
+
"WebFetch",
|
|
583
|
+
"WebSearch",
|
|
584
|
+
"AskUserQuestion",
|
|
581
585
|
...(mcpServers ? Object.keys(mcpServers).map((serverName) => `mcp__${serverName}__*`) : []),
|
|
582
586
|
];
|
|
583
587
|
const supportsAdaptive = modelSupportsAdaptiveThinking(modelId);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abandon-milestone detection for rewrite-docs overrides (#3490).
|
|
3
|
+
*
|
|
4
|
+
* Isolated from auto-post-unit.ts so behavioral tests can import this module
|
|
5
|
+
* without pulling in the full post-unit handler graph (which transitively
|
|
6
|
+
* loads model-router, workflow engine, etc.).
|
|
7
|
+
*/
|
|
8
|
+
// Detect when a rewrite-docs override is about abandoning THE CURRENT
|
|
9
|
+
// MILESTONE — not just any override containing an abandon verb. Naively
|
|
10
|
+
// matching `/\b(abandon|cancel|drop|...)\b/` against override text produces
|
|
11
|
+
// false positives on scope-change prose ("cancel the standup reminder",
|
|
12
|
+
// "drop the dependency on X", "scrap the v1 design for the landing page").
|
|
13
|
+
//
|
|
14
|
+
// To qualify as an abandon-milestone signal, an override must contain both:
|
|
15
|
+
// 1. An abandon-family verb (abandon|descope|cancel|shelve|drop|scrap)
|
|
16
|
+
// 2. A milestone reference — either the literal word "milestone" or the
|
|
17
|
+
// current milestone ID — in the same override text.
|
|
18
|
+
// Verb variants cover both US and UK inflections:
|
|
19
|
+
// cancel / canceled / canceling / cancelled / cancelling / cancels
|
|
20
|
+
// travel-style "l"-doubling also applies to shelve/drop/scrap.
|
|
21
|
+
// "descope" also accepts "de-scope" and "de scope" (hyphen / space forms).
|
|
22
|
+
const ABANDON_VERB_RE = /\b(abandon(?:ed|ing|s)?|de[-\s]?scope(?:d|s|ing)?|cancel(?:led|ling|ed|ing|s)?|shelve(?:d|s)?|shelving|drop(?:ped|ping|s)?|scrap(?:ped|ping|s)?)\b/i;
|
|
23
|
+
/**
|
|
24
|
+
* Decide whether a set of active overrides indicates the current milestone
|
|
25
|
+
* should be parked. Pure function — no I/O, no imports beyond types.
|
|
26
|
+
*/
|
|
27
|
+
export function detectAbandonMilestone(overrides, currentMilestoneId) {
|
|
28
|
+
if (!currentMilestoneId) {
|
|
29
|
+
return { shouldPark: false, reason: "", matched: [] };
|
|
30
|
+
}
|
|
31
|
+
const escapedId = currentMilestoneId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
32
|
+
const milestoneRefRe = new RegExp(`\\b(?:milestone|${escapedId})\\b`, "i");
|
|
33
|
+
const matched = overrides
|
|
34
|
+
.filter(o => ABANDON_VERB_RE.test(o.change) && milestoneRefRe.test(o.change))
|
|
35
|
+
.map(o => o.change);
|
|
36
|
+
if (matched.length === 0) {
|
|
37
|
+
return { shouldPark: false, reason: "", matched: [] };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
shouldPark: true,
|
|
41
|
+
reason: matched.join("; "),
|
|
42
|
+
matched,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Imports from: auto/types
|
|
9
9
|
*/
|
|
10
10
|
import { debugLog } from "../debug-logger.js";
|
|
11
|
+
import { bumpTurnGeneration } from "./turn-epoch.js";
|
|
11
12
|
// ─── Per-unit one-shot promise state ────────────────────────────────────────
|
|
12
13
|
//
|
|
13
14
|
// A single module-level resolve function scoped to the current unit execution.
|
|
@@ -56,6 +57,22 @@ export function resolveAgentEnd(event) {
|
|
|
56
57
|
export function isSessionSwitchInFlight() {
|
|
57
58
|
return _sessionSwitchInFlight;
|
|
58
59
|
}
|
|
60
|
+
// ─── bumpAndResolveSynthetic ────────────────────────────────────────────────
|
|
61
|
+
/**
|
|
62
|
+
* Bump the turn epoch and synthetically resolve the pending unit promise —
|
|
63
|
+
* the exact sequence timeout recovery must perform when it advances past a
|
|
64
|
+
* timed-out unit. Using this helper enforces the invariant "bump iff we are
|
|
65
|
+
* actually superseding the turn" so a future caller cannot resolve without
|
|
66
|
+
* bumping (orphaned writes leak) or bump without resolving (next turn starts
|
|
67
|
+
* already stale).
|
|
68
|
+
*
|
|
69
|
+
* NOT to be used for steering retries that keep the same turn alive — those
|
|
70
|
+
* do not supersede the turn and must not bump.
|
|
71
|
+
*/
|
|
72
|
+
export function bumpAndResolveSynthetic(reason) {
|
|
73
|
+
bumpTurnGeneration(reason);
|
|
74
|
+
resolveAgentEnd({ messages: [], _synthetic: reason });
|
|
75
|
+
}
|
|
59
76
|
// ─── resolveAgentEndCancelled ─────────────────────────────────────────────────
|
|
60
77
|
/**
|
|
61
78
|
* Force-resolve the pending unit promise with { status: "cancelled" }.
|
|
@@ -66,6 +83,10 @@ export function isSessionSwitchInFlight() {
|
|
|
66
83
|
*/
|
|
67
84
|
export function resolveAgentEndCancelled(errorContext) {
|
|
68
85
|
if (_currentResolve) {
|
|
86
|
+
// Cancellation supersedes the in-flight turn the same way timeout
|
|
87
|
+
// recovery does — bump the turn epoch so any lingering writes from the
|
|
88
|
+
// cancelled turn drop themselves.
|
|
89
|
+
bumpTurnGeneration(`cancelled:${errorContext?.category ?? "unknown"}`);
|
|
69
90
|
debugLog("resolveAgentEndCancelled", { status: "resolving-cancelled" });
|
|
70
91
|
const r = _currentResolve;
|
|
71
92
|
_currentResolve = null;
|
|
@@ -81,6 +102,9 @@ export function _resetPendingResolve() {
|
|
|
81
102
|
_currentResolve = null;
|
|
82
103
|
_sessionSwitchInFlight = false;
|
|
83
104
|
}
|
|
105
|
+
export function _hasPendingResolveForTest() {
|
|
106
|
+
return _currentResolve !== null;
|
|
107
|
+
}
|
|
84
108
|
/**
|
|
85
109
|
* No-op for backward compatibility with tests that previously set the
|
|
86
110
|
* active session. The module no longer holds a session reference.
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Imports from: auto/types, auto/resolve
|
|
5
5
|
*/
|
|
6
6
|
import { NEW_SESSION_TIMEOUT_MS } from "./session.js";
|
|
7
|
-
import { _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
|
|
7
|
+
import { _clearCurrentResolve, _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
|
|
8
|
+
import { getCurrentTurnGeneration, runWithTurnGeneration, } from "./turn-epoch.js";
|
|
8
9
|
import { debugLog } from "../debug-logger.js";
|
|
9
10
|
import { logWarning } from "../workflow-logger.js";
|
|
10
11
|
import { resolveAutoSupervisorConfig } from "../preferences.js";
|
|
@@ -117,6 +118,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
117
118
|
ready = false;
|
|
118
119
|
}
|
|
119
120
|
if (!ready) {
|
|
121
|
+
_clearCurrentResolve();
|
|
120
122
|
return {
|
|
121
123
|
status: "cancelled",
|
|
122
124
|
errorContext: {
|
|
@@ -128,6 +130,12 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
}
|
|
133
|
+
// ── Capture turn generation for stale-write detection ──
|
|
134
|
+
// Any write site reached via the sendMessage → tool-call → await chain
|
|
135
|
+
// below sees this generation via AsyncLocalStorage. If a timeout recovery
|
|
136
|
+
// or cancellation bumps the generation while this turn is in flight, those
|
|
137
|
+
// writes see themselves as stale and self-drop.
|
|
138
|
+
const capturedTurnGen = getCurrentTurnGeneration();
|
|
131
139
|
// ── Send the prompt ──
|
|
132
140
|
debugLog("runUnit", { phase: "send-message", unitType, unitId });
|
|
133
141
|
pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
|
|
@@ -143,7 +151,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
143
151
|
resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
|
|
144
152
|
}, UNIT_HARD_TIMEOUT_MS);
|
|
145
153
|
});
|
|
146
|
-
const result = await Promise.race([unitPromise, timeoutResult]);
|
|
154
|
+
const result = await runWithTurnGeneration(capturedTurnGen, () => Promise.race([unitPromise, timeoutResult]));
|
|
147
155
|
if (unitTimeoutHandle)
|
|
148
156
|
clearTimeout(unitTimeoutHandle);
|
|
149
157
|
debugLog("runUnit", {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto/turn-epoch.ts — Turn generation counter + AsyncLocalStorage-backed
|
|
3
|
+
* capture for stale-turn write dropping.
|
|
4
|
+
*
|
|
5
|
+
* Problem: when auto-timeout-recovery synthetically resolves a timed-out
|
|
6
|
+
* unit so the loop can advance, the original LLM turn keeps running in the
|
|
7
|
+
* background. Its subsequent writes (journal events, audit events, tool
|
|
8
|
+
* calls that flow through closeout) then race the replacement unit's
|
|
9
|
+
* writes. DB-level guards (complete-task/complete-slice) block double
|
|
10
|
+
* state transitions, but journal/audit/closeout side-effects still fire
|
|
11
|
+
* with fresh identifiers and pollute forensics.
|
|
12
|
+
*
|
|
13
|
+
* Containment: every time we decide a turn is done (timeout recovery,
|
|
14
|
+
* explicit cancellation), bump a module-level generation counter.
|
|
15
|
+
* Turn-aware call sites wrap their body in `runWithTurnGeneration`, which
|
|
16
|
+
* captures the generation into AsyncLocalStorage. Write sites deep in the
|
|
17
|
+
* stack call `isStaleWrite` — if the captured generation is older than
|
|
18
|
+
* current, the turn has been superseded and the write is dropped.
|
|
19
|
+
*
|
|
20
|
+
* Failure mode: if AsyncLocalStorage context is lost across some exotic
|
|
21
|
+
* async boundary (e.g. a native-side worker callback), the write site sees
|
|
22
|
+
* `no-store` and falls through to current behavior — the write proceeds
|
|
23
|
+
* normally. That is a safe default; the correctness regression is only
|
|
24
|
+
* "noisier forensics under rare boundary loss," not duplicated state.
|
|
25
|
+
*/
|
|
26
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
27
|
+
import { debugLog } from "../debug-logger.js";
|
|
28
|
+
let _currentGeneration = 0;
|
|
29
|
+
const turnContext = new AsyncLocalStorage();
|
|
30
|
+
/** Current turn generation. Mutated only by bumpTurnGeneration. */
|
|
31
|
+
export function getCurrentTurnGeneration() {
|
|
32
|
+
return _currentGeneration;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Bump the turn generation and return the new value. Every caller should
|
|
36
|
+
* pass a short `reason` string so forensics can reconstruct why a given
|
|
37
|
+
* turn was marked stale.
|
|
38
|
+
*/
|
|
39
|
+
export function bumpTurnGeneration(reason) {
|
|
40
|
+
_currentGeneration += 1;
|
|
41
|
+
debugLog("turnEpoch.bump", { reason, newGeneration: _currentGeneration });
|
|
42
|
+
return _currentGeneration;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run fn() with `capturedGen` attached to AsyncLocalStorage so that any
|
|
46
|
+
* write site reached from within fn() can check for staleness without
|
|
47
|
+
* parameter threading.
|
|
48
|
+
*/
|
|
49
|
+
export function runWithTurnGeneration(capturedGen, fn) {
|
|
50
|
+
return turnContext.run({ capturedGen }, fn);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* True when the current async context was started at a turn generation
|
|
54
|
+
* older than the current one — meaning the turn has been superseded by
|
|
55
|
+
* recovery/cancellation since it began.
|
|
56
|
+
*
|
|
57
|
+
* Returns false when there is no captured generation (e.g. the write is
|
|
58
|
+
* happening outside any wrapped turn). That is the safe default: writes
|
|
59
|
+
* proceed as they did before this epoch was introduced.
|
|
60
|
+
*/
|
|
61
|
+
export function isStaleWrite(component) {
|
|
62
|
+
const store = turnContext.getStore();
|
|
63
|
+
if (!store)
|
|
64
|
+
return false;
|
|
65
|
+
const captured = store.capturedGen;
|
|
66
|
+
const current = _currentGeneration;
|
|
67
|
+
if (captured < current) {
|
|
68
|
+
debugLog("turnEpoch.stale", {
|
|
69
|
+
component: component ?? "unknown",
|
|
70
|
+
captured,
|
|
71
|
+
current,
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Snapshot of both the captured turn generation and the current one.
|
|
79
|
+
* Used by closeoutUnit to persist an orphan-marker entry instead of
|
|
80
|
+
* silently skipping the full closeout on a stale turn.
|
|
81
|
+
*/
|
|
82
|
+
export function describeTurnEpoch() {
|
|
83
|
+
const store = turnContext.getStore();
|
|
84
|
+
const captured = store?.capturedGen ?? null;
|
|
85
|
+
const current = _currentGeneration;
|
|
86
|
+
return {
|
|
87
|
+
captured,
|
|
88
|
+
current,
|
|
89
|
+
stale: captured !== null && captured < current,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Test helper — resets module state so tests start from a known baseline. */
|
|
93
|
+
export function _resetTurnEpoch() {
|
|
94
|
+
_currentGeneration = 0;
|
|
95
|
+
}
|
|
@@ -319,6 +319,30 @@ export const DISPATCH_RULES = [
|
|
|
319
319
|
};
|
|
320
320
|
},
|
|
321
321
|
},
|
|
322
|
+
{
|
|
323
|
+
name: "planning (require_slice_discussion) → pause for discussion (#3454)",
|
|
324
|
+
match: async ({ state, mid, basePath, prefs }) => {
|
|
325
|
+
if (state.phase !== "planning")
|
|
326
|
+
return null;
|
|
327
|
+
if (!prefs?.phases?.require_slice_discussion)
|
|
328
|
+
return null;
|
|
329
|
+
if (!state.activeSlice)
|
|
330
|
+
return null;
|
|
331
|
+
// Only pause if the slice has no context file yet (discussion not done).
|
|
332
|
+
// resolveSliceFile returns null when the file does not exist on disk,
|
|
333
|
+
// but cachedReaddir could return a stale hit — verify with existsSync
|
|
334
|
+
// so the guard is defence-in-depth and the contract is explicit at the
|
|
335
|
+
// call site.
|
|
336
|
+
const sliceContextFile = resolveSliceFile(basePath, mid, state.activeSlice.id, "CONTEXT");
|
|
337
|
+
if (sliceContextFile && existsSync(sliceContextFile))
|
|
338
|
+
return null; // discussion already done, proceed
|
|
339
|
+
return {
|
|
340
|
+
action: "stop",
|
|
341
|
+
reason: `Slice ${state.activeSlice.id} requires discussion before planning (require_slice_discussion is enabled). Run /gsd discuss to discuss this slice, then /gsd auto to resume.`,
|
|
342
|
+
level: "info",
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
},
|
|
322
346
|
{
|
|
323
347
|
// Keep this rule before the single-slice research rule so the multi-slice
|
|
324
348
|
// path wins whenever 2+ slices are ready.
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { autoLoop, runUokKernelLoop, runLegacyAutoLoop } from "./auto/loop.js";
|
|
10
10
|
export { isInfrastructureError, INFRA_ERROR_CODES } from "./auto/infra-errors.js";
|
|
11
|
-
export { resolveAgentEnd, resolveAgentEndCancelled, isSessionSwitchInFlight, _resetPendingResolve, _setActiveSession } from "./auto/resolve.js";
|
|
11
|
+
export { resolveAgentEnd, resolveAgentEndCancelled, isSessionSwitchInFlight, _hasPendingResolveForTest, _resetPendingResolve, _setActiveSession } from "./auto/resolve.js";
|
|
12
12
|
export { detectStuck } from "./auto/detect-stuck.js";
|
|
13
13
|
export { runUnit } from "./auto/run-unit.js";
|
|
@@ -46,6 +46,7 @@ import { resolveUokFlags } from "./uok/flags.js";
|
|
|
46
46
|
import { UokGateRunner } from "./uok/gate-runner.js";
|
|
47
47
|
import { writeTurnGitTransaction } from "./uok/gitops.js";
|
|
48
48
|
import { isClosedStatus } from "./status-guards.js";
|
|
49
|
+
import { detectAbandonMilestone } from "./abandon-detect.js";
|
|
49
50
|
/** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
|
|
50
51
|
const MAX_VERIFICATION_RETRIES = 3;
|
|
51
52
|
const COMPLETE_MILESTONE_DB_SETTLE_MS = 1500;
|
|
@@ -472,6 +473,36 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
472
473
|
// Rewrite-docs completion
|
|
473
474
|
if (s.currentUnit.type === "rewrite-docs") {
|
|
474
475
|
await runSafely("postUnit", "rewrite-docs-resolve", async () => {
|
|
476
|
+
// Detect abandon/descope overrides BEFORE resolving them (#3490).
|
|
477
|
+
// If an override is about abandoning the milestone, park it so the
|
|
478
|
+
// state engine skips it. Without this, rewrite-docs only edits
|
|
479
|
+
// markdown but the DB still has the milestone as active.
|
|
480
|
+
try {
|
|
481
|
+
const { loadActiveOverrides } = await import("./files.js");
|
|
482
|
+
const overrides = await loadActiveOverrides(s.basePath);
|
|
483
|
+
const decision = detectAbandonMilestone(overrides, s.currentMilestoneId);
|
|
484
|
+
if (decision.shouldPark && s.currentMilestoneId) {
|
|
485
|
+
const { parkMilestone } = await import("./milestone-actions.js");
|
|
486
|
+
const parked = parkMilestone(s.basePath, s.currentMilestoneId, decision.reason);
|
|
487
|
+
if (parked) {
|
|
488
|
+
ctx.ui.notify(`Milestone ${s.currentMilestoneId} parked: "${decision.reason}"`, "info");
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
// Park refused: milestone directory missing, milestone already
|
|
492
|
+
// completed (SUMMARY present), or PARKED.md already exists.
|
|
493
|
+
// resolveAllOverrides below will still consume the override —
|
|
494
|
+
// surface this loudly so the user notices state drift rather
|
|
495
|
+
// than silently losing the abandon directive.
|
|
496
|
+
const msg = `Abandon detected for ${s.currentMilestoneId} but park refused (milestone is completed, already parked, or missing). Override will be resolved anyway — verify state is correct.`;
|
|
497
|
+
logError("engine", msg);
|
|
498
|
+
ctx.ui.notify(msg, "warning");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
logError("engine", `abandon-detect failed: ${err.message}`);
|
|
504
|
+
ctx.ui.notify(`Abandon detection failed — check logs. Overrides will still be resolved.`, "warning");
|
|
505
|
+
}
|
|
475
506
|
await resolveAllOverrides(s.basePath);
|
|
476
507
|
// Reset both disk and in-memory counters. Disk counter is authoritative
|
|
477
508
|
// (survives restarts); in-memory is kept in sync for the current session.
|
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
import { readUnitRuntimeRecord, writeUnitRuntimeRecord, formatExecuteTaskRecoveryStatus, inspectExecuteTaskDurability, } from "./unit-runtime.js";
|
|
7
7
|
import { resolveExpectedArtifactPath, diagnoseExpectedArtifact, writeBlockerPlaceholder, } from "./auto-recovery.js";
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
|
-
import {
|
|
9
|
+
import { bumpAndResolveSynthetic } from "./auto/resolve.js";
|
|
10
10
|
export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rctx) {
|
|
11
|
+
// Note on turn epoch: the bump is intentionally NOT unconditional at
|
|
12
|
+
// function entry. Two branches below (the "steering retry" paths) keep
|
|
13
|
+
// the same LLM turn alive and let it try again — they must NOT bump,
|
|
14
|
+
// otherwise the retry's legitimate writes get marked stale and drop.
|
|
15
|
+
// Each advance branch calls `bumpAndResolveSynthetic` to bump+resolve
|
|
16
|
+
// atomically. Search for that helper to find all supersede sites.
|
|
11
17
|
const { basePath, verbose, currentUnitStartedAt, unitRecoveryCount } = rctx;
|
|
12
18
|
const runtime = readUnitRuntimeRecord(basePath, unitType, unitId);
|
|
13
19
|
const recoveryAttempts = runtime?.recoveryAttempts ?? 0;
|
|
@@ -36,7 +42,7 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
36
42
|
});
|
|
37
43
|
ctx.ui.notify(`${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} already completed on disk. Continuing auto-mode. (attempt ${attemptNumber})`, "info");
|
|
38
44
|
unitRecoveryCount.delete(recoveryKey);
|
|
39
|
-
|
|
45
|
+
bumpAndResolveSynthetic(`timeout-recovery:${reason}:${unitType}/${unitId}`);
|
|
40
46
|
return "recovered";
|
|
41
47
|
}
|
|
42
48
|
if (recoveryAttempts < maxRecoveryAttempts) {
|
|
@@ -90,7 +96,7 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
90
96
|
});
|
|
91
97
|
ctx.ui.notify(`${unitType} ${unitId} skipped after ${maxRecoveryAttempts} recovery attempts (${diagnostic}). Blocker artifacts written. Advancing pipeline. (attempt ${attemptNumber})`, "warning");
|
|
92
98
|
unitRecoveryCount.delete(recoveryKey);
|
|
93
|
-
|
|
99
|
+
bumpAndResolveSynthetic(`timeout-recovery:${reason}:${unitType}/${unitId}`);
|
|
94
100
|
return "recovered";
|
|
95
101
|
}
|
|
96
102
|
// Fallback: couldn't write skip artifacts — pause as before.
|
|
@@ -115,7 +121,7 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
115
121
|
});
|
|
116
122
|
ctx.ui.notify(`${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} artifact already exists on disk. Advancing. (attempt ${attemptNumber})`, "info");
|
|
117
123
|
unitRecoveryCount.delete(recoveryKey);
|
|
118
|
-
|
|
124
|
+
bumpAndResolveSynthetic(`timeout-recovery:${reason}:${unitType}/${unitId}`);
|
|
119
125
|
return "recovered";
|
|
120
126
|
}
|
|
121
127
|
if (recoveryAttempts < maxRecoveryAttempts) {
|
|
@@ -180,7 +186,7 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
|
|
|
180
186
|
});
|
|
181
187
|
ctx.ui.notify(`${unitType} ${unitId} skipped after ${maxRecoveryAttempts} recovery attempts. Blocker placeholder written to ${placeholder}. Advancing pipeline. (attempt ${attemptNumber})`, "warning");
|
|
182
188
|
unitRecoveryCount.delete(recoveryKey);
|
|
183
|
-
|
|
189
|
+
bumpAndResolveSynthetic(`timeout-recovery:${reason}:${unitType}/${unitId}`);
|
|
184
190
|
return "recovered";
|
|
185
191
|
}
|
|
186
192
|
// Fallback: couldn't resolve artifact path — pause as before.
|
|
@@ -20,9 +20,18 @@ export async function closeoutUnit(ctx, basePath, unitType, unitId, startedAt, o
|
|
|
20
20
|
const { buildMemoryLLMCall, extractMemoriesFromUnit } = await import('./memory-extractor.js');
|
|
21
21
|
const llmCallFn = buildMemoryLLMCall(ctx);
|
|
22
22
|
if (llmCallFn) {
|
|
23
|
-
|
|
23
|
+
// Awaited: a fire-and-forget here lets memory-extractor writes land in
|
|
24
|
+
// .gsd/ after closeoutUnit returns but before the milestone merge
|
|
25
|
+
// runs, which made the working tree appear dirty to `git merge
|
|
26
|
+
// --squash` (root cause class of #4704). Completion latency is now
|
|
27
|
+
// bounded by the extractor's LLM call, which is the acceptable price
|
|
28
|
+
// for not racing the merge boundary.
|
|
29
|
+
try {
|
|
30
|
+
await extractMemoriesFromUnit(activityFile, unitType, unitId, llmCallFn);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
24
33
|
logWarning("engine", `memory extraction failed for ${unitType}/${unitId}: ${err.message}`);
|
|
25
|
-
}
|
|
34
|
+
}
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
37
|
catch (err) { /* non-fatal */
|