gsd-pi 2.66.1 → 2.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/claude-cli-check.d.ts +8 -0
- package/dist/claude-cli-check.js +36 -0
- package/dist/cli.js +40 -0
- package/dist/onboarding.js +19 -2
- package/dist/resources/extensions/ask-user-questions.js +79 -11
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
- package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
- package/dist/resources/extensions/gsd/auto/loop.js +13 -1
- package/dist/resources/extensions/gsd/auto/phases.js +22 -3
- package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto/session.js +1 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
- package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
- package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
- package/dist/resources/extensions/gsd/auto.js +13 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
- package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
- package/dist/resources/extensions/gsd/commands/handlers/core.js +3 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -4
- package/dist/resources/extensions/gsd/context-store.js +134 -2
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
- package/dist/resources/extensions/gsd/detection.js +6 -0
- package/dist/resources/extensions/gsd/files.js +19 -2
- package/dist/resources/extensions/gsd/guided-flow.js +12 -8
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
- package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
- package/dist/resources/extensions/gsd/preferences.js +6 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
- package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
- package/dist/resources/extensions/gsd/state.js +2 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
- package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
- package/dist/resources/extensions/remote-questions/manager.js +8 -0
- package/dist/resources/extensions/shared/interview-ui.js +10 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- 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 +2 -2
- 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/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 +12 -12
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.js +11 -1
- package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
- package/packages/pi-ai/src/utils/json-parse.ts +11 -1
- package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
- package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
- 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 +2 -1
- 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 +1 -0
- 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 +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
- package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/packages/pi-tui/dist/autocomplete.js +9 -7
- package/packages/pi-tui/dist/autocomplete.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +3 -1
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +14 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +6 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +8 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
- package/packages/pi-tui/src/autocomplete.ts +9 -7
- package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
- package/packages/pi-tui/src/components/editor.ts +14 -3
- package/packages/pi-tui/src/stdin-buffer.ts +7 -0
- package/packages/pi-tui/src/tui.ts +9 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +103 -11
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
- package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop.ts +14 -1
- package/src/resources/extensions/gsd/auto/phases.ts +27 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
- package/src/resources/extensions/gsd/auto/session.ts +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
- package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
- package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
- package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
- package/src/resources/extensions/gsd/auto.ts +12 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
- package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -6
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +11 -4
- package/src/resources/extensions/gsd/context-store.ts +167 -2
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
- package/src/resources/extensions/gsd/detection.ts +6 -0
- package/src/resources/extensions/gsd/files.ts +21 -2
- package/src/resources/extensions/gsd/guided-flow.ts +15 -8
- package/src/resources/extensions/gsd/index.ts +6 -0
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
- package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
- package/src/resources/extensions/gsd/preferences.ts +6 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
- package/src/resources/extensions/gsd/state.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
- package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
- package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
- package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
- package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
- package/src/resources/extensions/remote-questions/manager.ts +9 -0
- package/src/resources/extensions/shared/interview-ui.ts +13 -0
- /package/dist/web/standalone/.next/static/{y5P0reMrCMs-4-gswdawm → DFZllMYDbO0OwyS6FSvm5}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{y5P0reMrCMs-4-gswdawm → DFZllMYDbO0OwyS6FSvm5}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if the `claude` binary is installed (regardless of auth state).
|
|
3
|
+
*/
|
|
4
|
+
export declare function isClaudeBinaryInstalled(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Check if the `claude` CLI is installed AND authenticated.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isClaudeCliReady(): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// GSD2 — Claude CLI binary detection for onboarding
|
|
2
|
+
// Lightweight check used at onboarding time (before extensions load).
|
|
3
|
+
// The full readiness check with caching lives in the claude-code-cli extension.
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
/**
|
|
6
|
+
* Check if the `claude` binary is installed (regardless of auth state).
|
|
7
|
+
*/
|
|
8
|
+
export function isClaudeBinaryInstalled() {
|
|
9
|
+
try {
|
|
10
|
+
execFileSync('claude', ['--version'], { timeout: 5_000, stdio: 'pipe' });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if the `claude` CLI is installed AND authenticated.
|
|
19
|
+
*/
|
|
20
|
+
export function isClaudeCliReady() {
|
|
21
|
+
try {
|
|
22
|
+
execFileSync('claude', ['--version'], { timeout: 5_000, stdio: 'pipe' });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const output = execFileSync('claude', ['auth', 'status'], { timeout: 5_000, stdio: 'pipe' })
|
|
29
|
+
.toString()
|
|
30
|
+
.toLowerCase();
|
|
31
|
+
return !(/not logged in|no credentials|unauthenticated|not authenticated/i.test(output));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -395,8 +395,28 @@ if (isPrintMode) {
|
|
|
395
395
|
settingsManager,
|
|
396
396
|
sessionManager,
|
|
397
397
|
resourceLoader,
|
|
398
|
+
isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
|
|
398
399
|
});
|
|
399
400
|
markStartup('createAgentSession');
|
|
401
|
+
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
402
|
+
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
403
|
+
// the local claude CLI binary is TOS-compliant.
|
|
404
|
+
if (modelRegistry.isProviderRequestReady('claude-code') && settingsManager.getDefaultProvider() === 'anthropic') {
|
|
405
|
+
const currentModelId = settingsManager.getDefaultModel();
|
|
406
|
+
if (currentModelId) {
|
|
407
|
+
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
|
408
|
+
if (ccModel) {
|
|
409
|
+
try {
|
|
410
|
+
await session.setModel(ccModel);
|
|
411
|
+
// Only persist after successful session switch to avoid desync
|
|
412
|
+
settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
// claude-code provider not ready — leave both session and settings unchanged
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
400
420
|
// Validate configured model AFTER extensions have registered their models (#2626).
|
|
401
421
|
// Before this, extension-provided models (e.g. claude-code/*) were not yet in the
|
|
402
422
|
// registry, causing the user's valid choice to be silently overwritten.
|
|
@@ -550,8 +570,28 @@ const { session, extensionsResult, modelFallbackMessage: interactiveFallbackMsg
|
|
|
550
570
|
settingsManager,
|
|
551
571
|
sessionManager,
|
|
552
572
|
resourceLoader,
|
|
573
|
+
isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
|
|
553
574
|
});
|
|
554
575
|
markStartup('createAgentSession');
|
|
576
|
+
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
577
|
+
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
578
|
+
// the local claude CLI binary is TOS-compliant.
|
|
579
|
+
if (modelRegistry.isProviderRequestReady('claude-code') && settingsManager.getDefaultProvider() === 'anthropic') {
|
|
580
|
+
const currentModelId = settingsManager.getDefaultModel();
|
|
581
|
+
if (currentModelId) {
|
|
582
|
+
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
|
583
|
+
if (ccModel) {
|
|
584
|
+
try {
|
|
585
|
+
await session.setModel(ccModel);
|
|
586
|
+
// Only persist after successful session switch to avoid desync
|
|
587
|
+
settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
// claude-code provider not ready — leave both session and settings unchanged
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
555
595
|
// Validate configured model AFTER extensions have registered their models (#2626).
|
|
556
596
|
// Before this, extension-provided models (e.g. claude-code/*) were not yet in the
|
|
557
597
|
// registry, causing the user's valid choice to be silently overwritten.
|
package/dist/onboarding.js
CHANGED
|
@@ -14,6 +14,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
14
14
|
import { dirname, join } from 'node:path';
|
|
15
15
|
import { renderLogo } from './logo.js';
|
|
16
16
|
import { agentDir } from './app-paths.js';
|
|
17
|
+
import { isClaudeCliReady } from './claude-cli-check.js';
|
|
17
18
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
18
19
|
const TOOL_KEYS = [
|
|
19
20
|
{
|
|
@@ -39,6 +40,7 @@ const TOOL_KEYS = [
|
|
|
39
40
|
const LLM_PROVIDER_IDS = [
|
|
40
41
|
'anthropic',
|
|
41
42
|
'anthropic-vertex',
|
|
43
|
+
'claude-code',
|
|
42
44
|
'openai',
|
|
43
45
|
'github-copilot',
|
|
44
46
|
'openai-codex',
|
|
@@ -255,7 +257,12 @@ async function runLlmStep(p, pc, authStorage) {
|
|
|
255
257
|
if (existingAuth) {
|
|
256
258
|
authOptions.push({ value: 'keep', label: `Keep current (${existingAuth})`, hint: 'already configured' });
|
|
257
259
|
}
|
|
258
|
-
|
|
260
|
+
// Show Claude Code CLI option at the top when the CLI is installed and authenticated (#3772).
|
|
261
|
+
// This is the only TOS-compliant path for Anthropic subscription users.
|
|
262
|
+
if (isClaudeCliReady()) {
|
|
263
|
+
authOptions.push({ value: 'claude-cli', label: 'Use Claude Code CLI', hint: 'recommended — uses your existing Claude subscription' });
|
|
264
|
+
}
|
|
265
|
+
authOptions.push({ value: 'browser', label: 'Sign in with your browser', hint: 'GitHub Copilot, ChatGPT, Google, etc.' }, { value: 'api-key', label: 'Paste an API key', hint: 'from your provider dashboard' }, { value: 'skip', label: 'Skip for now', hint: 'use /login inside GSD later' });
|
|
259
266
|
const method = await p.select({
|
|
260
267
|
message: existingAuth ? `LLM provider: ${existingAuth} — change it?` : 'How do you want to sign in?',
|
|
261
268
|
options: authOptions,
|
|
@@ -264,12 +271,22 @@ async function runLlmStep(p, pc, authStorage) {
|
|
|
264
271
|
return false;
|
|
265
272
|
if (method === 'keep')
|
|
266
273
|
return true;
|
|
274
|
+
// ── Claude Code CLI path (#3772) ────────────────────────────────────────
|
|
275
|
+
if (method === 'claude-cli') {
|
|
276
|
+
p.log.success('Claude Code CLI detected — routing through local CLI (TOS-compliant)');
|
|
277
|
+
p.log.info('Your Claude subscription will be used for inference. No API key needed.');
|
|
278
|
+
// Store sentinel so hasAuth('claude-code') returns true on future boots
|
|
279
|
+
authStorage.set('claude-code', { type: 'api_key', key: 'cli' });
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
267
282
|
// ── Step 2: Which provider? ──────────────────────────────────────────────
|
|
268
283
|
if (method === 'browser') {
|
|
284
|
+
// Anthropic OAuth is removed from browser auth — it violates Anthropic TOS for
|
|
285
|
+
// third-party apps (#3772). Anthropic subscription users should use the Claude
|
|
286
|
+
// Code CLI path (shown above when CLI is installed) or paste an API key.
|
|
269
287
|
const provider = await p.select({
|
|
270
288
|
message: 'Choose provider',
|
|
271
289
|
options: [
|
|
272
|
-
{ value: 'anthropic', label: 'Anthropic (Claude)', hint: 'recommended' },
|
|
273
290
|
{ value: 'github-copilot', label: 'GitHub Copilot' },
|
|
274
291
|
{ value: 'openai-codex', label: 'ChatGPT Plus/Pro (Codex)' },
|
|
275
292
|
{ value: 'google-gemini-cli', label: 'Google Gemini CLI' },
|
|
@@ -58,6 +58,51 @@ export function questionSignature(questions) {
|
|
|
58
58
|
export function resetAskUserQuestionsCache() {
|
|
59
59
|
turnCache.clear();
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Race a remote channel dispatch against the local TUI. The first to produce
|
|
63
|
+
* a valid (non-error, non-timeout) result wins. The loser is cancelled via
|
|
64
|
+
* the shared AbortController.
|
|
65
|
+
*
|
|
66
|
+
* If the local TUI responds first, the remote poll is aborted (the message
|
|
67
|
+
* stays in Discord/Slack but polling stops). If remote responds first, the
|
|
68
|
+
* local TUI prompt is cancelled.
|
|
69
|
+
*
|
|
70
|
+
* Returns null only when both sides fail or are cancelled.
|
|
71
|
+
*/
|
|
72
|
+
async function raceRemoteAndLocal(startRemote, startLocal, controller, questions) {
|
|
73
|
+
// Wrap local TUI result into the same shape as remote results
|
|
74
|
+
const localPromise = startLocal().then((result) => {
|
|
75
|
+
if (!result || Object.keys(result.answers).length === 0)
|
|
76
|
+
return null;
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: "text", text: formatForLLM(result) }],
|
|
79
|
+
details: { questions, response: result, cancelled: false },
|
|
80
|
+
};
|
|
81
|
+
}).catch(() => null);
|
|
82
|
+
const remotePromise = startRemote().then((result) => {
|
|
83
|
+
if (!result)
|
|
84
|
+
return null;
|
|
85
|
+
const details = result.details;
|
|
86
|
+
// Treat timeouts and errors as non-wins — let the local TUI win instead
|
|
87
|
+
if (details?.timed_out || details?.error)
|
|
88
|
+
return null;
|
|
89
|
+
return result;
|
|
90
|
+
}).catch(() => null);
|
|
91
|
+
// Race: first non-null result wins
|
|
92
|
+
const winner = await Promise.race([
|
|
93
|
+
localPromise.then((r) => r ? { source: "local", result: r } : null),
|
|
94
|
+
remotePromise.then((r) => r ? { source: "remote", result: r } : null),
|
|
95
|
+
]);
|
|
96
|
+
if (winner) {
|
|
97
|
+
// Cancel the loser
|
|
98
|
+
controller.abort();
|
|
99
|
+
return winner.result;
|
|
100
|
+
}
|
|
101
|
+
// First to resolve was null — wait for the other
|
|
102
|
+
const [localResult, remoteResult] = await Promise.all([localPromise, remotePromise]);
|
|
103
|
+
controller.abort();
|
|
104
|
+
return localResult ?? remoteResult;
|
|
105
|
+
}
|
|
61
106
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
62
107
|
const OTHER_OPTION_LABEL = "None of the above";
|
|
63
108
|
function errorResult(message, questions = []) {
|
|
@@ -116,19 +161,42 @@ export default function AskUserQuestions(pi) {
|
|
|
116
161
|
return errorResult(`Error: ask_user_questions requires non-empty options for every question (question "${q.id}" has none)`, params.questions);
|
|
117
162
|
}
|
|
118
163
|
}
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
164
|
+
// ── Routing: race remote + local, remote-only, or local-only ────────
|
|
165
|
+
const { tryRemoteQuestions, isRemoteConfigured } = await import("./remote-questions/manager.js");
|
|
166
|
+
const hasRemote = isRemoteConfigured();
|
|
167
|
+
// Case 1: Both remote and local UI available — race them.
|
|
168
|
+
// The first response wins; the loser is cancelled via AbortController.
|
|
169
|
+
if (hasRemote && ctx.hasUI) {
|
|
170
|
+
const raceController = new AbortController();
|
|
171
|
+
// Merge the parent signal so external cancellation propagates.
|
|
172
|
+
const onParentAbort = () => raceController.abort();
|
|
173
|
+
signal?.addEventListener("abort", onParentAbort, { once: true });
|
|
174
|
+
const raceSignal = raceController.signal;
|
|
175
|
+
const raceResult = await raceRemoteAndLocal(() => tryRemoteQuestions(params.questions, raceSignal), () => showInterviewRound(params.questions, { signal: raceSignal }, ctx), raceController, params.questions);
|
|
176
|
+
signal?.removeEventListener("abort", onParentAbort);
|
|
177
|
+
if (raceResult) {
|
|
178
|
+
const details = raceResult.details;
|
|
179
|
+
if (details && !details.timed_out && !details.error && !details.cancelled) {
|
|
180
|
+
turnCache.set(sig, raceResult);
|
|
181
|
+
}
|
|
182
|
+
return { ...raceResult, details: raceResult.details };
|
|
183
|
+
}
|
|
184
|
+
// Both sides failed/cancelled — fall through to error
|
|
185
|
+
return errorResult("ask_user_questions: no response received from local UI or remote channel", params.questions);
|
|
186
|
+
}
|
|
187
|
+
// Case 2: Remote configured but no local UI (headless) — remote only.
|
|
188
|
+
if (hasRemote && !ctx.hasUI) {
|
|
189
|
+
const remoteResult = await tryRemoteQuestions(params.questions, signal);
|
|
190
|
+
if (remoteResult) {
|
|
191
|
+
const remoteDetails = remoteResult.details;
|
|
192
|
+
if (remoteDetails && !remoteDetails.timed_out && !remoteDetails.error) {
|
|
193
|
+
turnCache.set(sig, remoteResult);
|
|
194
|
+
}
|
|
195
|
+
return { ...remoteResult, details: remoteResult.details };
|
|
129
196
|
}
|
|
130
|
-
return
|
|
197
|
+
return errorResult("Error: remote channel configured but returned no result", params.questions);
|
|
131
198
|
}
|
|
199
|
+
// Case 3: No remote — local UI only.
|
|
132
200
|
if (!ctx.hasUI) {
|
|
133
201
|
return errorResult("Error: UI not available (non-interactive mode)", params.questions);
|
|
134
202
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Translates the Claude Agent SDK's `BetaRawMessageStreamEvent` sequence
|
|
5
5
|
* into GSD's `AssistantMessageEvent` deltas for incremental TUI rendering.
|
|
6
6
|
*/
|
|
7
|
-
import { repairToolJson } from "@gsd/pi-ai";
|
|
7
|
+
import { hasXmlParameterTags, repairToolJson } from "@gsd/pi-ai";
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// Content-block mapping helpers
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
@@ -207,14 +207,15 @@ export class PartialMessageBuilder {
|
|
|
207
207
|
}
|
|
208
208
|
if (block.type === "toolCall") {
|
|
209
209
|
const jsonStr = this.toolJsonAccum.get(streamIndex) ?? "{}";
|
|
210
|
+
const jsonForParse = hasXmlParameterTags(jsonStr) ? repairToolJson(jsonStr) : jsonStr;
|
|
210
211
|
try {
|
|
211
|
-
block.arguments = JSON.parse(
|
|
212
|
+
block.arguments = JSON.parse(jsonForParse);
|
|
212
213
|
}
|
|
213
214
|
catch {
|
|
214
215
|
// JSON.parse failed — attempt repair for YAML-style bullet
|
|
215
216
|
// lists that LLMs copy from template formatting (#2660).
|
|
216
217
|
try {
|
|
217
|
-
block.arguments = JSON.parse(repairToolJson(
|
|
218
|
+
block.arguments = JSON.parse(repairToolJson(jsonForParse));
|
|
218
219
|
}
|
|
219
220
|
catch {
|
|
220
221
|
// Repair also failed — stream was truncated or garbage.
|
|
@@ -1,26 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Readiness check for the Claude Code CLI provider.
|
|
3
3
|
*
|
|
4
|
-
* Verifies the `claude` binary is installed
|
|
5
|
-
*
|
|
4
|
+
* Verifies the `claude` binary is installed, responsive, AND authenticated.
|
|
5
|
+
* Results are cached for 30 seconds to avoid shelling out on every
|
|
6
6
|
* model-availability check.
|
|
7
|
+
*
|
|
8
|
+
* Auth verification follows the T3 Code pattern: run `claude auth status`
|
|
9
|
+
* and check the exit code + output for an authenticated session.
|
|
7
10
|
*/
|
|
8
|
-
import {
|
|
9
|
-
let
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
let cachedBinaryPresent = null;
|
|
13
|
+
let cachedAuthed = null;
|
|
10
14
|
let lastCheckMs = 0;
|
|
11
15
|
const CHECK_INTERVAL_MS = 30_000;
|
|
12
|
-
|
|
16
|
+
function refreshCache() {
|
|
13
17
|
const now = Date.now();
|
|
14
|
-
if (
|
|
15
|
-
return
|
|
18
|
+
if (cachedBinaryPresent !== null && now - lastCheckMs < CHECK_INTERVAL_MS) {
|
|
19
|
+
return;
|
|
16
20
|
}
|
|
21
|
+
// Set timestamp first to prevent re-entrant checks during the same window
|
|
22
|
+
lastCheckMs = now;
|
|
23
|
+
// Check binary presence
|
|
17
24
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
execFileSync("claude", ["--version"], { timeout: 5_000, stdio: "pipe" });
|
|
26
|
+
cachedBinaryPresent = true;
|
|
20
27
|
}
|
|
21
28
|
catch {
|
|
22
|
-
|
|
29
|
+
cachedBinaryPresent = false;
|
|
30
|
+
cachedAuthed = false;
|
|
31
|
+
return;
|
|
23
32
|
}
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
// Check auth status — exit code 0 with non-error output means authenticated
|
|
34
|
+
try {
|
|
35
|
+
const output = execFileSync("claude", ["auth", "status"], { timeout: 5_000, stdio: "pipe" })
|
|
36
|
+
.toString()
|
|
37
|
+
.toLowerCase();
|
|
38
|
+
// The CLI outputs "not logged in", "no credentials", or similar when unauthenticated
|
|
39
|
+
cachedAuthed = !(/not logged in|no credentials|unauthenticated|not authenticated/i.test(output));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Non-zero exit code means not authenticated
|
|
43
|
+
cachedAuthed = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether the `claude` binary is installed (regardless of auth state).
|
|
48
|
+
*/
|
|
49
|
+
export function isClaudeBinaryPresent() {
|
|
50
|
+
refreshCache();
|
|
51
|
+
return cachedBinaryPresent ?? false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether the `claude` CLI is authenticated with a valid session.
|
|
55
|
+
* Returns false if the binary is not installed.
|
|
56
|
+
*/
|
|
57
|
+
export function isClaudeCodeAuthed() {
|
|
58
|
+
refreshCache();
|
|
59
|
+
return (cachedBinaryPresent ?? false) && (cachedAuthed ?? false);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Full readiness check: binary installed AND authenticated.
|
|
63
|
+
* This is the gating function used by the provider registration.
|
|
64
|
+
*/
|
|
65
|
+
export function isClaudeCodeReady() {
|
|
66
|
+
refreshCache();
|
|
67
|
+
return (cachedBinaryPresent ?? false) && (cachedAuthed ?? false);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Force-clear the cached readiness state.
|
|
71
|
+
* Useful after the user completes auth setup so the next check is fresh.
|
|
72
|
+
*/
|
|
73
|
+
export function clearReadinessCache() {
|
|
74
|
+
cachedBinaryPresent = null;
|
|
75
|
+
cachedAuthed = null;
|
|
76
|
+
lastCheckMs = 0;
|
|
26
77
|
}
|
|
@@ -29,6 +29,15 @@ function createAssistantStream() {
|
|
|
29
29
|
// Claude binary resolution
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
31
31
|
let cachedClaudePath = null;
|
|
32
|
+
export function getClaudeLookupCommand(platform = process.platform) {
|
|
33
|
+
return platform === "win32" ? "where claude" : "which claude";
|
|
34
|
+
}
|
|
35
|
+
export function parseClaudeLookupOutput(output) {
|
|
36
|
+
return output
|
|
37
|
+
.toString()
|
|
38
|
+
.trim()
|
|
39
|
+
.split(/\r?\n/)[0] ?? "";
|
|
40
|
+
}
|
|
32
41
|
/**
|
|
33
42
|
* Resolve the path to the system-installed `claude` binary.
|
|
34
43
|
* The SDK defaults to a bundled cli.js which doesn't exist when
|
|
@@ -38,9 +47,7 @@ function getClaudePath() {
|
|
|
38
47
|
if (cachedClaudePath)
|
|
39
48
|
return cachedClaudePath;
|
|
40
49
|
try {
|
|
41
|
-
cachedClaudePath = execSync(
|
|
42
|
-
.toString()
|
|
43
|
-
.trim();
|
|
50
|
+
cachedClaudePath = parseClaudeLookupOutput(execSync(getClaudeLookupCommand(), { timeout: 5_000, stdio: "pipe" }));
|
|
44
51
|
}
|
|
45
52
|
catch {
|
|
46
53
|
cachedClaudePath = "claude"; // fall back to PATH resolution
|
|
@@ -150,7 +150,7 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
150
150
|
}
|
|
151
151
|
// Verification passed — mark step complete
|
|
152
152
|
debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
|
|
153
|
-
await engine.reconcile(engineState, {
|
|
153
|
+
const reconcileResult = await engine.reconcile(engineState, {
|
|
154
154
|
unitType: iterData.unitType,
|
|
155
155
|
unitId: iterData.unitId,
|
|
156
156
|
startedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
@@ -161,6 +161,18 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
161
161
|
recentErrorMessages.length = 0;
|
|
162
162
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
163
163
|
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
164
|
+
if (reconcileResult.outcome === "milestone-complete") {
|
|
165
|
+
await deps.stopAuto(ctx, pi, "Workflow complete");
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
if (reconcileResult.outcome === "pause") {
|
|
169
|
+
await deps.pauseAuto(ctx, pi);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (reconcileResult.outcome === "stop") {
|
|
173
|
+
await deps.stopAuto(ctx, pi, reconcileResult.reason ?? "Engine stopped");
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
164
176
|
continue;
|
|
165
177
|
}
|
|
166
178
|
if (!sidecarItem) {
|
|
@@ -338,7 +338,13 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
338
338
|
ctx.ui.notify(`Milestone ${mid} has no title in roadmap — using ID as fallback.`, "warning");
|
|
339
339
|
}
|
|
340
340
|
// Mid-merge safety check
|
|
341
|
-
|
|
341
|
+
const mergeReconcileResult = deps.reconcileMergeState(s.basePath, ctx);
|
|
342
|
+
if (mergeReconcileResult === "blocked") {
|
|
343
|
+
await deps.pauseAuto(ctx, pi);
|
|
344
|
+
debugLog("autoLoop", { phase: "exit", reason: "merge-reconciliation-blocked" });
|
|
345
|
+
return { action: "break", reason: "merge-reconciliation-blocked" };
|
|
346
|
+
}
|
|
347
|
+
if (mergeReconcileResult === "reconciled") {
|
|
342
348
|
deps.invalidateAllCaches();
|
|
343
349
|
state = await deps.deriveState(s.basePath);
|
|
344
350
|
mid = state.activeMilestone?.id;
|
|
@@ -947,8 +953,21 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
947
953
|
debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext.isTransient });
|
|
948
954
|
return { action: "break", reason: "provider-pause" };
|
|
949
955
|
}
|
|
950
|
-
|
|
951
|
-
|
|
956
|
+
// Session creation timeout (not a structural error): pause auto-mode
|
|
957
|
+
// and let the provider-error-resume timer handle recovery (#3767). This
|
|
958
|
+
// matches the provider-pause path — break out cleanly, don't hard-stop.
|
|
959
|
+
// Structural errors (TypeError, is not a function) are NOT transient
|
|
960
|
+
// and must hard-stop to avoid infinite retry loops.
|
|
961
|
+
if (unitResult.errorContext?.isTransient &&
|
|
962
|
+
unitResult.errorContext?.category === "timeout") {
|
|
963
|
+
ctx.ui.notify(`Session creation timed out for ${unitType} ${unitId}. Pausing auto-mode (recoverable).`, "warning");
|
|
964
|
+
debugLog("autoLoop", { phase: "session-timeout-pause", unitType, unitId });
|
|
965
|
+
await deps.pauseAuto(ctx, pi);
|
|
966
|
+
return { action: "break", reason: "session-timeout" };
|
|
967
|
+
}
|
|
968
|
+
// All other cancelled states (structural errors, non-transient failures): hard stop
|
|
969
|
+
ctx.ui.notify(`Session creation failed for ${unitType} ${unitId}: ${unitResult.errorContext?.message ?? "unknown"}. Stopping auto-mode.`, "warning");
|
|
970
|
+
await deps.stopAuto(ctx, pi, `Session creation failed: ${unitResult.errorContext?.message ?? "unknown"}`);
|
|
952
971
|
debugLog("autoLoop", { phase: "exit", reason: "session-failed" });
|
|
953
972
|
return { action: "break", reason: "session-failed" };
|
|
954
973
|
}
|
|
@@ -7,6 +7,10 @@ import { NEW_SESSION_TIMEOUT_MS } from "./session.js";
|
|
|
7
7
|
import { _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
|
|
8
8
|
import { debugLog } from "../debug-logger.js";
|
|
9
9
|
import { logWarning } from "../workflow-logger.js";
|
|
10
|
+
import { resolveAutoSupervisorConfig } from "../preferences.js";
|
|
11
|
+
// Tracks the latest session-switch attempt so a late timeout settlement from an
|
|
12
|
+
// older runUnit() call cannot clear the guard for a newer one.
|
|
13
|
+
let sessionSwitchGeneration = 0;
|
|
10
14
|
/**
|
|
11
15
|
* Execute a single unit: create a new session, send the prompt, and await
|
|
12
16
|
* the agent_end promise. Returns a UnitResult describing what happened.
|
|
@@ -21,10 +25,13 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
21
25
|
debugLog("runUnit", { phase: "session-create", unitType, unitId });
|
|
22
26
|
let sessionResult;
|
|
23
27
|
let sessionTimeoutHandle;
|
|
28
|
+
const mySessionSwitchGeneration = ++sessionSwitchGeneration;
|
|
24
29
|
_setSessionSwitchInFlight(true);
|
|
25
30
|
try {
|
|
26
31
|
const sessionPromise = s.cmdCtx.newSession().finally(() => {
|
|
27
|
-
|
|
32
|
+
if (sessionSwitchGeneration === mySessionSwitchGeneration) {
|
|
33
|
+
_setSessionSwitchInFlight(false);
|
|
34
|
+
}
|
|
28
35
|
});
|
|
29
36
|
const timeoutPromise = new Promise((resolve) => {
|
|
30
37
|
sessionTimeoutHandle = setTimeout(() => resolve({ cancelled: true }), NEW_SESSION_TIMEOUT_MS);
|
|
@@ -83,7 +90,8 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
|
|
|
83
90
|
// If supervision fails to resolve unitPromise within 30s, treat as cancelled.
|
|
84
91
|
// Without this, a crashed agent that never emits agent_end hangs the loop (#3161).
|
|
85
92
|
debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
|
|
86
|
-
const
|
|
93
|
+
const supervisor = resolveAutoSupervisorConfig();
|
|
94
|
+
const UNIT_HARD_TIMEOUT_MS = Math.max(30_000, ((supervisor.hard_timeout_minutes ?? 30) * 60 * 1000) + 30_000);
|
|
87
95
|
let unitTimeoutHandle;
|
|
88
96
|
const timeoutResult = new Promise((resolve) => {
|
|
89
97
|
unitTimeoutHandle = setTimeout(() => {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
export const MAX_UNIT_DISPATCHES = 3;
|
|
20
20
|
export const STUB_RECOVERY_THRESHOLD = 2;
|
|
21
21
|
export const MAX_LIFETIME_DISPATCHES = 6;
|
|
22
|
-
export const NEW_SESSION_TIMEOUT_MS =
|
|
22
|
+
export const NEW_SESSION_TIMEOUT_MS = 120_000;
|
|
23
23
|
// ─── AutoSession ─────────────────────────────────────────────────────────────
|
|
24
24
|
export class AutoSession {
|
|
25
25
|
// ── Lifecycle ────────────────────────────────────────────────────────────
|