gsd-pi 2.50.0-dev.d210a87 → 2.51.0-dev.7d435fe
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/headless-events.d.ts +18 -0
- package/dist/headless-events.js +36 -0
- package/dist/headless-types.d.ts +28 -0
- package/dist/headless-types.js +7 -0
- package/dist/headless.d.ts +8 -3
- package/dist/headless.js +47 -16
- package/dist/help-text.js +16 -5
- package/dist/onboarding.js +5 -4
- package/dist/remote-questions-config.js +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
- package/dist/resources/extensions/gsd/auto-start.js +2 -0
- package/dist/resources/extensions/gsd/auto-timers.js +24 -2
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
- package/dist/resources/extensions/gsd/auto.js +4 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +95 -69
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
- package/dist/resources/extensions/gsd/claude-import.js +60 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
- package/dist/resources/extensions/gsd/commands-config.js +10 -5
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/detection.js +6 -6
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/dist/resources/extensions/gsd/error-classifier.js +105 -0
- package/dist/resources/extensions/gsd/gitignore.js +7 -7
- package/dist/resources/extensions/gsd/gsd-db.js +298 -45
- package/dist/resources/extensions/gsd/init-wizard.js +2 -2
- package/dist/resources/extensions/gsd/key-manager.js +7 -16
- package/dist/resources/extensions/gsd/memory-store.js +28 -13
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
- package/dist/resources/extensions/gsd/preferences-models.js +1 -13
- package/dist/resources/extensions/gsd/preferences.js +13 -13
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
- package/dist/resources/extensions/gsd/rule-registry.js +1 -1
- package/dist/resources/extensions/gsd/service-tier.js +13 -2
- package/dist/resources/extensions/gsd/state.js +21 -2
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -10
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -17
- package/dist/resources/extensions/gsd/tools/complete-task.js +7 -18
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +26 -17
- package/dist/resources/extensions/gsd/tools/plan-slice.js +25 -14
- package/dist/resources/extensions/gsd/tools/plan-task.js +21 -11
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +47 -37
- package/dist/resources/extensions/gsd/tools/replan-slice.js +49 -38
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
- package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
- package/dist/resources/extensions/remote-questions/config.js +1 -1
- package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
- package/dist/resources/extensions/search-the-web/native-search.js +1 -1
- package/dist/resources/extensions/search-the-web/provider.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +21 -21
- 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/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +21 -21
- package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- 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 +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
- package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
- package/dist/wizard.js +4 -1
- package/package.json +2 -2
- package/packages/pi-ai/dist/models.d.ts +14 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +53 -10
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +102 -1
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +30 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/models.test.ts +114 -1
- package/packages/pi-ai/src/models.ts +70 -13
- package/packages/pi-ai/src/types.ts +31 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +3 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +5 -3
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +4 -0
- package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
- package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
- package/packages/pi-coding-agent/src/index.ts +3 -0
- package/packages/pi-coding-agent/src/main.ts +5 -3
- package/packages/pi-coding-agent/src/modes/index.ts +8 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
- package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
- package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-start.ts +2 -0
- package/src/resources/extensions/gsd/auto-timers.ts +25 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
- package/src/resources/extensions/gsd/auto.ts +5 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +115 -72
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
- package/src/resources/extensions/gsd/claude-import.ts +58 -9
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
- package/src/resources/extensions/gsd/commands-config.ts +11 -5
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/detection.ts +6 -6
- package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/src/resources/extensions/gsd/error-classifier.ts +139 -0
- package/src/resources/extensions/gsd/gitignore.ts +7 -7
- package/src/resources/extensions/gsd/gsd-db.ts +355 -63
- package/src/resources/extensions/gsd/init-wizard.ts +2 -2
- package/src/resources/extensions/gsd/key-manager.ts +7 -16
- package/src/resources/extensions/gsd/memory-store.ts +29 -18
- package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
- package/src/resources/extensions/gsd/preferences-models.ts +1 -13
- package/src/resources/extensions/gsd/preferences.ts +12 -13
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
- package/src/resources/extensions/gsd/rule-registry.ts +1 -1
- package/src/resources/extensions/gsd/service-tier.ts +14 -2
- package/src/resources/extensions/gsd/state.ts +22 -2
- package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
- package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -14
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -21
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -22
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +28 -18
- package/src/resources/extensions/gsd/tools/plan-slice.ts +28 -16
- package/src/resources/extensions/gsd/tools/plan-task.ts +24 -12
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +54 -42
- package/src/resources/extensions/gsd/tools/replan-slice.ts +53 -40
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/remote-questions/config.ts +1 -1
- package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
- package/src/resources/extensions/search-the-web/native-search.ts +1 -1
- package/src/resources/extensions/search-the-web/provider.ts +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
- package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
- /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
- /package/dist/web/standalone/.next/static/{yJIyd5cXPNpmXTv18ZlyC → RqOU-jOv9uZ1Q03P6L6nn}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{yJIyd5cXPNpmXTv18ZlyC → RqOU-jOv9uZ1Q03P6L6nn}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for #2676: idle watchdog must exempt user-interactive tools
|
|
3
|
+
* (ask_user_questions, secure_env_collect) from stall detection.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test, beforeEach } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import {
|
|
8
|
+
markToolStart,
|
|
9
|
+
markToolEnd,
|
|
10
|
+
hasInteractiveToolInFlight,
|
|
11
|
+
getInFlightToolCount,
|
|
12
|
+
getOldestInFlightToolStart,
|
|
13
|
+
getOldestInFlightToolAgeMs,
|
|
14
|
+
clearInFlightTools,
|
|
15
|
+
} from "../auto-tool-tracking.ts";
|
|
16
|
+
|
|
17
|
+
// These tests call the tracking module directly (bypassing the auto.ts
|
|
18
|
+
// wrapper which guards on s.active) so we always pass isActive=true.
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
clearInFlightTools();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("hasInteractiveToolInFlight", () => {
|
|
25
|
+
test("returns false when no tools are in-flight", () => {
|
|
26
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("returns false when only non-interactive tools are in-flight", () => {
|
|
30
|
+
markToolStart("call-1", true, "bash");
|
|
31
|
+
markToolStart("call-2", true, "read");
|
|
32
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns true when ask_user_questions is in-flight", () => {
|
|
36
|
+
markToolStart("call-1", true, "bash");
|
|
37
|
+
markToolStart("call-2", true, "ask_user_questions");
|
|
38
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("returns true when secure_env_collect is in-flight", () => {
|
|
42
|
+
markToolStart("call-1", true, "secure_env_collect");
|
|
43
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("returns false after interactive tool completes", () => {
|
|
47
|
+
markToolStart("call-1", true, "ask_user_questions");
|
|
48
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
49
|
+
markToolEnd("call-1");
|
|
50
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("returns true if one of multiple tools is interactive", () => {
|
|
54
|
+
markToolStart("call-1", true, "bash");
|
|
55
|
+
markToolStart("call-2", true, "edit");
|
|
56
|
+
markToolStart("call-3", true, "ask_user_questions");
|
|
57
|
+
markToolStart("call-4", true, "write");
|
|
58
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("toolName tracking in markToolStart", () => {
|
|
63
|
+
test("defaults toolName to 'unknown' when not provided", () => {
|
|
64
|
+
markToolStart("call-1", true);
|
|
65
|
+
// unknown tool should not be treated as interactive
|
|
66
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
67
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("no-ops when isActive is false", () => {
|
|
71
|
+
markToolStart("call-1", false, "ask_user_questions");
|
|
72
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
73
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("existing tracking behavior preserved with toolName", () => {
|
|
78
|
+
test("getInFlightToolCount tracks correctly", () => {
|
|
79
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
80
|
+
markToolStart("call-1", true, "bash");
|
|
81
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
82
|
+
markToolStart("call-2", true, "ask_user_questions");
|
|
83
|
+
assert.equal(getInFlightToolCount(), 2);
|
|
84
|
+
markToolEnd("call-1");
|
|
85
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
86
|
+
markToolEnd("call-2");
|
|
87
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("getOldestInFlightToolStart returns correct timestamp", () => {
|
|
91
|
+
assert.equal(getOldestInFlightToolStart(), undefined);
|
|
92
|
+
const before = Date.now();
|
|
93
|
+
markToolStart("call-1", true, "bash");
|
|
94
|
+
const after = Date.now();
|
|
95
|
+
const oldest = getOldestInFlightToolStart();
|
|
96
|
+
assert.ok(oldest !== undefined);
|
|
97
|
+
assert.ok(oldest! >= before && oldest! <= after);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("getOldestInFlightToolAgeMs returns 0 with no tools", () => {
|
|
101
|
+
assert.equal(getOldestInFlightToolAgeMs(), 0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("getOldestInFlightToolAgeMs returns positive value with tools", () => {
|
|
105
|
+
markToolStart("call-1", true, "read");
|
|
106
|
+
const age = getOldestInFlightToolAgeMs();
|
|
107
|
+
assert.ok(age >= 0, `age should be non-negative, got ${age}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("clearInFlightTools resets all state", () => {
|
|
111
|
+
markToolStart("call-1", true, "ask_user_questions");
|
|
112
|
+
markToolStart("call-2", true, "bash");
|
|
113
|
+
assert.equal(getInFlightToolCount(), 2);
|
|
114
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
115
|
+
clearInFlightTools();
|
|
116
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
117
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -189,7 +189,22 @@ test("getAllKeyStatuses detects empty keys as not configured", () => {
|
|
|
189
189
|
const statuses = getAllKeyStatuses(auth);
|
|
190
190
|
const groq = statuses.find((s) => s.provider.id === "groq");
|
|
191
191
|
assert.equal(groq?.configured, false);
|
|
192
|
-
|
|
192
|
+
// Empty-key entries are filtered out, so provider appears unconfigured
|
|
193
|
+
assert.equal(groq?.source, "none");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("getAllKeyStatuses finds valid keys even when empty-key entry exists at index 0", () => {
|
|
197
|
+
const auth = makeAuth({
|
|
198
|
+
groq: [
|
|
199
|
+
{ type: "api_key", key: "" },
|
|
200
|
+
{ type: "api_key", key: "gsk-real-key" },
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
const statuses = getAllKeyStatuses(auth);
|
|
204
|
+
const groq = statuses.find((s) => s.provider.id === "groq");
|
|
205
|
+
assert.equal(groq?.configured, true);
|
|
206
|
+
assert.equal(groq?.source, "auth.json");
|
|
207
|
+
assert.equal(groq?.credentialCount, 1); // only the valid key counts
|
|
193
208
|
});
|
|
194
209
|
|
|
195
210
|
test("getAllKeyStatuses detects env var keys", () => {
|
|
@@ -363,7 +363,7 @@ test('md-importer: schema v1→v2 migration', () => {
|
|
|
363
363
|
openDatabase(':memory:');
|
|
364
364
|
const adapter = _getAdapter();
|
|
365
365
|
const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
366
|
-
assert.deepStrictEqual(version?.v,
|
|
366
|
+
assert.deepStrictEqual(version?.v, 14, 'new DB should be at schema version 14');
|
|
367
367
|
|
|
368
368
|
// Artifacts table should exist
|
|
369
369
|
const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
|
|
@@ -323,9 +323,9 @@ test('memory-store: schema includes memories table', () => {
|
|
|
323
323
|
const viewCount = adapter.prepare('SELECT count(*) as cnt FROM active_memories').get();
|
|
324
324
|
assert.deepStrictEqual(viewCount?.['cnt'], 0, 'active_memories view should exist');
|
|
325
325
|
|
|
326
|
-
// Verify schema version is
|
|
326
|
+
// Verify schema version is 14 (after indexes + slice_dependencies)
|
|
327
327
|
const version = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
328
|
-
assert.deepStrictEqual(version?.['v'],
|
|
328
|
+
assert.deepStrictEqual(version?.['v'], 14, 'schema version should be 14');
|
|
329
329
|
|
|
330
330
|
closeDatabase();
|
|
331
331
|
});
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Uses the writeRunnerPreferences pattern from doctor-git.test.ts:
|
|
9
9
|
* PROJECT_PREFERENCES_PATH is a module-level constant frozen at import
|
|
10
10
|
* time, so process.chdir() won't redirect preference loading. We write
|
|
11
|
-
* prefs to the runner's cwd .gsd/
|
|
11
|
+
* prefs to the runner's cwd .gsd/PREFERENCES.md and clean up in finally.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
@@ -24,7 +24,7 @@ import assert from 'node:assert/strict';
|
|
|
24
24
|
|
|
25
25
|
// --- Preferences helpers (same pattern as doctor-git.test.ts K001) ---
|
|
26
26
|
|
|
27
|
-
const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "
|
|
27
|
+
const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "PREFERENCES.md");
|
|
28
28
|
|
|
29
29
|
function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void {
|
|
30
30
|
mkdirSync(join(process.cwd(), ".gsd"), { recursive: true });
|
|
@@ -72,12 +72,12 @@ try {
|
|
|
72
72
|
|
|
73
73
|
// Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none)
|
|
74
74
|
// Worktree isolation requires explicit opt-in — default is "none" so GSD
|
|
75
|
-
// works out of the box without
|
|
75
|
+
// works out of the box without PREFERENCES.md (#2480).
|
|
76
76
|
// Skip if global prefs exist — they override the default and this test
|
|
77
|
-
// cannot control ~/.gsd/
|
|
77
|
+
// cannot control ~/.gsd/PREFERENCES.md.
|
|
78
78
|
|
|
79
79
|
test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => {
|
|
80
|
-
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "
|
|
80
|
+
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
|
|
81
81
|
|| existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
|
|
82
82
|
if (!globalPrefsExist) {
|
|
83
83
|
try {
|
|
@@ -91,9 +91,9 @@ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', ()
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
// Test 5: getIsolationMode returns "none" when no
|
|
94
|
+
// Test 5: getIsolationMode returns "none" when no PREFERENCES.md exists (#2480)
|
|
95
95
|
test('getIsolationMode returns "none" with no prefs (default)', () => {
|
|
96
|
-
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "
|
|
96
|
+
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
|
|
97
97
|
|| existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
|
|
98
98
|
if (!globalPrefsExist) {
|
|
99
99
|
try {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2694: parkMilestone and unparkMilestone must
|
|
3
|
+
* update the DB milestone status alongside the filesystem marker.
|
|
4
|
+
*
|
|
5
|
+
* Without this, deriveStateFromDb skips unparked milestones because
|
|
6
|
+
* the DB still has status='parked', causing "All milestones complete".
|
|
7
|
+
*/
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
import { parkMilestone, unparkMilestone } from "../milestone-actions.ts";
|
|
15
|
+
import {
|
|
16
|
+
openDatabase,
|
|
17
|
+
closeDatabase,
|
|
18
|
+
insertMilestone,
|
|
19
|
+
getMilestone,
|
|
20
|
+
} from "../gsd-db.ts";
|
|
21
|
+
|
|
22
|
+
function createBase(): string {
|
|
23
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-park-db-"));
|
|
24
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
25
|
+
writeFileSync(
|
|
26
|
+
join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
27
|
+
"# M001\n\nContext.",
|
|
28
|
+
);
|
|
29
|
+
return base;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test("parkMilestone updates DB status to 'parked' (#2694)", () => {
|
|
33
|
+
const base = createBase();
|
|
34
|
+
try {
|
|
35
|
+
openDatabase(":memory:");
|
|
36
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
37
|
+
|
|
38
|
+
assert.equal(getMilestone("M001")!.status, "active", "starts active");
|
|
39
|
+
|
|
40
|
+
parkMilestone(base, "M001", "deprioritized");
|
|
41
|
+
|
|
42
|
+
assert.equal(getMilestone("M001")!.status, "parked", "DB status should be parked");
|
|
43
|
+
|
|
44
|
+
closeDatabase();
|
|
45
|
+
} finally {
|
|
46
|
+
closeDatabase();
|
|
47
|
+
rmSync(base, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("unparkMilestone updates DB status to 'active' (#2694)", () => {
|
|
52
|
+
const base = createBase();
|
|
53
|
+
try {
|
|
54
|
+
openDatabase(":memory:");
|
|
55
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
56
|
+
|
|
57
|
+
// Park first
|
|
58
|
+
parkMilestone(base, "M001", "deprioritized");
|
|
59
|
+
assert.equal(getMilestone("M001")!.status, "parked");
|
|
60
|
+
|
|
61
|
+
// Unpark
|
|
62
|
+
unparkMilestone(base, "M001");
|
|
63
|
+
assert.equal(getMilestone("M001")!.status, "active", "DB status should be active after unpark");
|
|
64
|
+
|
|
65
|
+
closeDatabase();
|
|
66
|
+
} finally {
|
|
67
|
+
closeDatabase();
|
|
68
|
+
rmSync(base, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("park/unpark are safe when DB is not available (#2694 guard)", () => {
|
|
73
|
+
const base = createBase();
|
|
74
|
+
try {
|
|
75
|
+
// No openDatabase — DB not available
|
|
76
|
+
// park/unpark should still work (filesystem-only, no throw)
|
|
77
|
+
const parked = parkMilestone(base, "M001", "test");
|
|
78
|
+
assert.ok(parked, "parkMilestone succeeds without DB");
|
|
79
|
+
|
|
80
|
+
const unparked = unparkMilestone(base, "M001");
|
|
81
|
+
assert.ok(unparked, "unparkMilestone succeeds without DB");
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(base, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for #2684: preferences.md must be included in both
|
|
3
|
+
* ROOT_STATE_FILES (sync) and copyPlanningArtifacts (initial seed).
|
|
4
|
+
*
|
|
5
|
+
* Without this, post_unit_hooks and all preference-driven config silently
|
|
6
|
+
* stop working inside auto-mode worktrees.
|
|
7
|
+
*/
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
test("#2684: preferences.md is NOT in ROOT_STATE_FILES (forward-only sync)", () => {
|
|
15
|
+
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
16
|
+
const src = readFileSync(srcPath, "utf-8");
|
|
17
|
+
|
|
18
|
+
const constIdx = src.indexOf("ROOT_STATE_FILES");
|
|
19
|
+
assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
|
|
20
|
+
|
|
21
|
+
const arrayStart = src.indexOf("[", constIdx);
|
|
22
|
+
const arrayEnd = src.indexOf("] as const", arrayStart);
|
|
23
|
+
const block = src.slice(arrayStart, arrayEnd);
|
|
24
|
+
|
|
25
|
+
// preferences.md must NOT be in ROOT_STATE_FILES — it is handled separately
|
|
26
|
+
// in syncGsdStateToWorktree() (forward-only, additive). Including it in
|
|
27
|
+
// ROOT_STATE_FILES would cause syncWorktreeStateBack() to overwrite the
|
|
28
|
+
// authoritative project root copy (#2684).
|
|
29
|
+
const entries = block.split("\n")
|
|
30
|
+
.map(l => l.trim())
|
|
31
|
+
.filter(l => l.startsWith('"') && l.includes(".md"));
|
|
32
|
+
const hasPrefs = entries.some(l => l.includes("preferences.md"));
|
|
33
|
+
assert.ok(
|
|
34
|
+
!hasPrefs,
|
|
35
|
+
"preferences.md must NOT be in ROOT_STATE_FILES (back-sync would overwrite root)",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#2684: copyPlanningArtifacts file list includes preferences.md", () => {
|
|
40
|
+
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
41
|
+
const src = readFileSync(srcPath, "utf-8");
|
|
42
|
+
|
|
43
|
+
// Find the copyPlanningArtifacts function body
|
|
44
|
+
const fnIdx = src.indexOf("function copyPlanningArtifacts");
|
|
45
|
+
assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
|
|
46
|
+
|
|
47
|
+
// Extract function body (up to the next top-level function)
|
|
48
|
+
const fnBody = src.slice(fnIdx, fnIdx + 1500);
|
|
49
|
+
|
|
50
|
+
assert.ok(
|
|
51
|
+
fnBody.includes('"preferences.md"'),
|
|
52
|
+
"preferences.md should be in copyPlanningArtifacts file list",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("#2684: syncGsdStateToWorktree copies preferences.md", async () => {
|
|
57
|
+
// Functional test: create a mock source and destination, call the sync
|
|
58
|
+
const srcBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-src-"));
|
|
59
|
+
const dstBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-dst-"));
|
|
60
|
+
const srcGsd = join(srcBase, ".gsd");
|
|
61
|
+
const dstGsd = join(dstBase, ".gsd");
|
|
62
|
+
mkdirSync(srcGsd, { recursive: true });
|
|
63
|
+
mkdirSync(dstGsd, { recursive: true });
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Write a preferences.md in source
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(srcGsd, "preferences.md"),
|
|
69
|
+
"---\nversion: 1\n---\n\npost_unit_hooks:\n - name: notify\n command: echo done\n",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Import and call syncGsdStateToWorktree
|
|
73
|
+
const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
|
|
74
|
+
syncGsdStateToWorktree(srcBase, dstBase);
|
|
75
|
+
|
|
76
|
+
// Verify preferences.md was copied
|
|
77
|
+
assert.ok(
|
|
78
|
+
existsSync(join(dstGsd, "preferences.md")),
|
|
79
|
+
"preferences.md should be copied to worktree",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const content = readFileSync(join(dstGsd, "preferences.md"), "utf-8");
|
|
83
|
+
assert.ok(
|
|
84
|
+
content.includes("post_unit_hooks"),
|
|
85
|
+
"copied preferences.md should contain the hooks config",
|
|
86
|
+
);
|
|
87
|
+
} finally {
|
|
88
|
+
rmSync(srcBase, { recursive: true, force: true });
|
|
89
|
+
rmSync(dstBase, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
@@ -45,7 +45,7 @@ test("getIsolationMode defaults to none when preferences have no isolation setti
|
|
|
45
45
|
// Validate the default via validatePreferences: when no isolation is set,
|
|
46
46
|
// preferences.git.isolation is undefined, and getIsolationMode returns "none".
|
|
47
47
|
// Default changed from "worktree" to "none" so GSD works out of the box
|
|
48
|
-
// without
|
|
48
|
+
// without PREFERENCES.md (#2480).
|
|
49
49
|
const { preferences } = validatePreferences({});
|
|
50
50
|
assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs");
|
|
51
51
|
const isolation = preferences.git?.isolation;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider error handling tests — consolidated from:
|
|
3
|
-
* - provider-error-classify.test.ts (
|
|
3
|
+
* - provider-error-classify.test.ts (classifyError)
|
|
4
4
|
* - network-error-fallback.test.ts (isTransientNetworkError, getNextFallbackModel)
|
|
5
5
|
* - agent-end-provider-error.test.ts (pauseAutoForProviderError)
|
|
6
6
|
*/
|
|
@@ -10,102 +10,111 @@ import assert from "node:assert/strict";
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { join, dirname } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { classifyError, isTransient, isTransientNetworkError } from "../error-classifier.ts";
|
|
14
|
+
import { pauseAutoForProviderError } from "../provider-error-pause.ts";
|
|
15
|
+
import { getNextFallbackModel } from "../preferences.ts";
|
|
15
16
|
|
|
16
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
|
|
18
|
-
// ──
|
|
19
|
+
// ── classifyError ────────────────────────────────────────────────────────────
|
|
19
20
|
|
|
20
|
-
test("
|
|
21
|
-
const result =
|
|
22
|
-
assert.ok(result
|
|
23
|
-
assert.
|
|
24
|
-
assert.ok(result.
|
|
21
|
+
test("classifyError detects rate limit from 429", () => {
|
|
22
|
+
const result = classifyError("HTTP 429 Too Many Requests");
|
|
23
|
+
assert.ok(isTransient(result));
|
|
24
|
+
assert.equal(result.kind, "rate-limit");
|
|
25
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs > 0);
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
test("
|
|
28
|
-
const result =
|
|
29
|
-
assert.ok(result
|
|
30
|
-
assert.
|
|
28
|
+
test("classifyError detects rate limit from message", () => {
|
|
29
|
+
const result = classifyError("rate limit exceeded");
|
|
30
|
+
assert.ok(isTransient(result));
|
|
31
|
+
assert.equal(result.kind, "rate-limit");
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
test("
|
|
34
|
-
const result =
|
|
35
|
-
assert.
|
|
36
|
-
assert.
|
|
34
|
+
test("classifyError extracts reset delay from message", () => {
|
|
35
|
+
const result = classifyError("rate limit exceeded, reset in 45s");
|
|
36
|
+
assert.equal(result.kind, "rate-limit");
|
|
37
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 45000);
|
|
37
38
|
});
|
|
38
39
|
|
|
39
|
-
test("
|
|
40
|
-
const result =
|
|
41
|
-
assert.
|
|
42
|
-
assert.
|
|
40
|
+
test("classifyError defaults to 60s for rate limit without reset", () => {
|
|
41
|
+
const result = classifyError("429 too many requests");
|
|
42
|
+
assert.equal(result.kind, "rate-limit");
|
|
43
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 60_000);
|
|
43
44
|
});
|
|
44
45
|
|
|
45
|
-
test("
|
|
46
|
+
test("classifyError treats stream_exhausted_without_result as transient connection failure", () => {
|
|
47
|
+
const result = classifyError("stream_exhausted_without_result");
|
|
48
|
+
assert.ok(isTransient(result));
|
|
49
|
+
assert.equal(result.kind, "connection");
|
|
50
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 15_000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("classifyError detects Anthropic internal server error", () => {
|
|
46
54
|
const msg = '{"type":"error","error":{"details":null,"type":"api_error","message":"Internal server error"}}';
|
|
47
|
-
const result =
|
|
48
|
-
assert.ok(result
|
|
49
|
-
assert.
|
|
50
|
-
assert.
|
|
55
|
+
const result = classifyError(msg);
|
|
56
|
+
assert.ok(isTransient(result));
|
|
57
|
+
assert.equal(result.kind, "server");
|
|
58
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 30_000);
|
|
51
59
|
});
|
|
52
60
|
|
|
53
|
-
test("
|
|
61
|
+
test("classifyError detects Codex server_error from extracted message", () => {
|
|
54
62
|
// After fix, mapCodexEvents extracts the nested error type and produces
|
|
55
63
|
// "Codex server_error: <message>" instead of raw JSON.
|
|
56
64
|
const msg = "Codex server_error: An error occurred while processing your request.";
|
|
57
|
-
const result =
|
|
58
|
-
assert.ok(result
|
|
59
|
-
assert.
|
|
60
|
-
assert.
|
|
65
|
+
const result = classifyError(msg);
|
|
66
|
+
assert.ok(isTransient(result));
|
|
67
|
+
assert.equal(result.kind, "server");
|
|
68
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 30_000);
|
|
61
69
|
});
|
|
62
70
|
|
|
63
|
-
test("
|
|
64
|
-
const result =
|
|
65
|
-
assert.ok(result
|
|
66
|
-
assert.
|
|
71
|
+
test("classifyError detects overloaded error", () => {
|
|
72
|
+
const result = classifyError("overloaded_error: Overloaded");
|
|
73
|
+
assert.ok(isTransient(result));
|
|
74
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 30_000);
|
|
67
75
|
});
|
|
68
76
|
|
|
69
|
-
test("
|
|
70
|
-
const result =
|
|
71
|
-
assert.ok(result
|
|
77
|
+
test("classifyError detects 503 service unavailable", () => {
|
|
78
|
+
const result = classifyError("HTTP 503 Service Unavailable");
|
|
79
|
+
assert.ok(isTransient(result));
|
|
72
80
|
});
|
|
73
81
|
|
|
74
|
-
test("
|
|
75
|
-
const result =
|
|
76
|
-
assert.ok(result
|
|
82
|
+
test("classifyError detects 502 bad gateway", () => {
|
|
83
|
+
const result = classifyError("HTTP 502 Bad Gateway");
|
|
84
|
+
assert.ok(isTransient(result));
|
|
77
85
|
});
|
|
78
86
|
|
|
79
|
-
test("
|
|
80
|
-
const result =
|
|
81
|
-
assert.ok(!result
|
|
82
|
-
assert.
|
|
87
|
+
test("classifyError detects auth error as permanent", () => {
|
|
88
|
+
const result = classifyError("unauthorized: invalid API key");
|
|
89
|
+
assert.ok(!isTransient(result));
|
|
90
|
+
assert.equal(result.kind, "permanent");
|
|
83
91
|
});
|
|
84
92
|
|
|
85
|
-
test("
|
|
86
|
-
const result =
|
|
87
|
-
assert.ok(!result
|
|
93
|
+
test("classifyError detects billing error as permanent", () => {
|
|
94
|
+
const result = classifyError("billing issue: payment required");
|
|
95
|
+
assert.ok(!isTransient(result));
|
|
88
96
|
});
|
|
89
97
|
|
|
90
|
-
test("
|
|
91
|
-
const result =
|
|
92
|
-
assert.ok(!result
|
|
98
|
+
test("classifyError detects quota exceeded as permanent", () => {
|
|
99
|
+
const result = classifyError("quota exceeded for this month");
|
|
100
|
+
assert.ok(!isTransient(result));
|
|
93
101
|
});
|
|
94
102
|
|
|
95
|
-
test("
|
|
96
|
-
const result =
|
|
97
|
-
assert.ok(!result
|
|
103
|
+
test("classifyError treats unknown error as not transient", () => {
|
|
104
|
+
const result = classifyError("something went wrong");
|
|
105
|
+
assert.ok(!isTransient(result));
|
|
106
|
+
assert.equal(result.kind, "unknown");
|
|
98
107
|
});
|
|
99
108
|
|
|
100
|
-
test("
|
|
101
|
-
const result =
|
|
102
|
-
assert.ok(!result
|
|
109
|
+
test("classifyError treats empty string as not transient", () => {
|
|
110
|
+
const result = classifyError("");
|
|
111
|
+
assert.ok(!isTransient(result));
|
|
103
112
|
});
|
|
104
113
|
|
|
105
|
-
test("
|
|
106
|
-
const result =
|
|
107
|
-
assert.
|
|
108
|
-
assert.ok(result
|
|
114
|
+
test("classifyError: rate limit takes precedence over auth keywords", () => {
|
|
115
|
+
const result = classifyError("429 unauthorized rate limit");
|
|
116
|
+
assert.equal(result.kind, "rate-limit");
|
|
117
|
+
assert.ok(isTransient(result));
|
|
109
118
|
});
|
|
110
119
|
|
|
111
120
|
// ── isTransientNetworkError ──────────────────────────────────────────────────
|
|
@@ -265,8 +274,8 @@ test("agent-end-recovery.ts tracks consecutive transient errors for escalating b
|
|
|
265
274
|
const src = readFileSync(join(__dirname, "..", "bootstrap", "agent-end-recovery.ts"), "utf-8");
|
|
266
275
|
|
|
267
276
|
assert.ok(
|
|
268
|
-
src.includes("
|
|
269
|
-
"agent-end-recovery.ts must track
|
|
277
|
+
src.includes("consecutiveTransientCount"),
|
|
278
|
+
"agent-end-recovery.ts must track consecutiveTransientCount for escalating backoff (#1166)",
|
|
270
279
|
);
|
|
271
280
|
assert.ok(
|
|
272
281
|
src.includes("MAX_TRANSIENT_AUTO_RESUMES"),
|
|
@@ -274,15 +283,13 @@ test("agent-end-recovery.ts tracks consecutive transient errors for escalating b
|
|
|
274
283
|
);
|
|
275
284
|
});
|
|
276
285
|
|
|
277
|
-
test("agent-end-recovery.ts resets
|
|
286
|
+
test("agent-end-recovery.ts resets retry state before resolveAgentEnd on success", () => {
|
|
278
287
|
const src = readFileSync(join(__dirname, "..", "bootstrap", "agent-end-recovery.ts"), "utf-8");
|
|
279
288
|
|
|
280
|
-
// After successful agent_end
|
|
281
|
-
// Use a regex across the success block so CRLF checkouts on Windows do not
|
|
282
|
-
// push the reset line outside a fixed substring window.
|
|
289
|
+
// After successful agent_end, resetRetryState must be called before resolveAgentEnd.
|
|
283
290
|
assert.ok(
|
|
284
|
-
/
|
|
285
|
-
"
|
|
291
|
+
/resetRetryState[\s\S]{0,250}resolveAgentEnd/.test(src),
|
|
292
|
+
"resetRetryState must be called before resolveAgentEnd on the success path (#1166)",
|
|
286
293
|
);
|
|
287
294
|
});
|
|
288
295
|
|
|
@@ -291,7 +298,7 @@ test("agent-end-recovery.ts applies escalating delay for repeated transient erro
|
|
|
291
298
|
|
|
292
299
|
// Must contain the exponential backoff formula (may span multiple lines)
|
|
293
300
|
assert.ok(
|
|
294
|
-
src.includes("2 ** Math.max(0,
|
|
301
|
+
src.includes("2 ** Math.max(0, retryState.consecutiveTransientCount"),
|
|
295
302
|
"agent-end-recovery.ts must escalate retryAfterMs exponentially for consecutive transient errors (#1166)",
|
|
296
303
|
);
|
|
297
304
|
});
|