gsd-pi 2.51.0 → 2.52.0-dev.585e355
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/async-jobs/job-manager.js +4 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
- package/dist/resources/extensions/gsd/auto/phases.js +6 -0
- 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 +8 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +105 -70
- 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 +4 -4
- package/dist/resources/extensions/gsd/detection.js +6 -6
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/dist/resources/extensions/gsd/error-classifier.js +105 -0
- package/dist/resources/extensions/gsd/git-service.js +4 -3
- 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/markdown-renderer.js +5 -4
- 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-types.js +1 -1
- 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 +33 -19
- package/dist/resources/extensions/gsd/status-guards.js +12 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
- package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
- package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
- package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
- package/dist/resources/extensions/gsd/validation.js +21 -0
- 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/resources/extensions/shared/rtk.js +5 -3
- package/dist/rtk.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- 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.js +2 -2
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- 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 +4 -4
- 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.js +1 -1
- 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.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.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.js +2 -2
- 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.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.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.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.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.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 +2 -2
- 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.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.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.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.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.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.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.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.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.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.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.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.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.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 +2 -2
- 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.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.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.js +2 -2
- 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.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.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.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.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.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.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.js +2 -2
- 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.js +2 -2
- 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.js +2 -2
- 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.js +4 -4
- 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.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.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.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.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 +5 -5
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +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 +15 -15
- package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- 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/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -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/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/wizard.js +4 -1
- package/package.json +2 -2
- package/packages/mcp-server/README.md +202 -0
- package/packages/mcp-server/package.json +36 -0
- package/packages/mcp-server/src/cli.ts +68 -0
- package/packages/mcp-server/src/index.ts +14 -0
- package/packages/mcp-server/src/mcp-server.test.ts +628 -0
- package/packages/mcp-server/src/server.ts +278 -0
- package/packages/mcp-server/src/session-manager.ts +328 -0
- package/packages/mcp-server/src/types.ts +107 -0
- package/packages/mcp-server/tsconfig.json +24 -0
- 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/packages/rpc-client/package.json +20 -0
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +36 -8
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
- package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
- 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/phases.ts +6 -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 +10 -4
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -73
- 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 +4 -4
- package/src/resources/extensions/gsd/detection.ts +6 -6
- package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/src/resources/extensions/gsd/error-classifier.ts +139 -0
- package/src/resources/extensions/gsd/git-service.ts +4 -3
- 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/markdown-renderer.ts +5 -4
- 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-types.ts +1 -1
- 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 +34 -20
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
- 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 +37 -4
- 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 +2 -2
- 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/status-guards.test.ts +30 -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/validation.test.ts +72 -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 +7 -17
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
- package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
- package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
- package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
- package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
- package/src/resources/extensions/gsd/validation.ts +23 -0
- 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/src/resources/extensions/shared/rtk.ts +12 -3
- package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- 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/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -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
|
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2675: completing-milestone dispatch rule must
|
|
3
|
+
* block completion when VALIDATION verdict is "needs-remediation".
|
|
4
|
+
*
|
|
5
|
+
* Without this guard, needs-remediation + allSlicesDone causes a loop:
|
|
6
|
+
* complete-milestone dispatched → agent refuses (correct) → no SUMMARY
|
|
7
|
+
* → re-dispatch → repeat until stuck detection fires.
|
|
8
|
+
*/
|
|
9
|
+
import { test } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
|
|
15
|
+
import { DISPATCH_RULES } from "../auto-dispatch.ts";
|
|
16
|
+
|
|
17
|
+
/** Find the completing-milestone dispatch rule */
|
|
18
|
+
const completingRule = DISPATCH_RULES.find(r => r.name === "completing-milestone → complete-milestone");
|
|
19
|
+
|
|
20
|
+
test("completing-milestone dispatch rule exists", () => {
|
|
21
|
+
assert.ok(completingRule, "rule should exist in DISPATCH_RULES");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("completing-milestone blocks when VALIDATION verdict is needs-remediation (#2675)", async () => {
|
|
25
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-remediation-"));
|
|
26
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Write a VALIDATION file with needs-remediation verdict
|
|
30
|
+
writeFileSync(
|
|
31
|
+
join(base, ".gsd", "milestones", "M001", "M001-VALIDATION.md"),
|
|
32
|
+
[
|
|
33
|
+
"---",
|
|
34
|
+
"verdict: needs-remediation",
|
|
35
|
+
"remediation_round: 0",
|
|
36
|
+
"---",
|
|
37
|
+
"",
|
|
38
|
+
"# Validation Report",
|
|
39
|
+
"",
|
|
40
|
+
"3 success criteria failed. Remediation required.",
|
|
41
|
+
].join("\n"),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const ctx = {
|
|
45
|
+
mid: "M001",
|
|
46
|
+
midTitle: "Test Milestone",
|
|
47
|
+
basePath: base,
|
|
48
|
+
state: { phase: "completing-milestone" } as any,
|
|
49
|
+
prefs: {} as any,
|
|
50
|
+
session: undefined,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const result = await completingRule!.match(ctx);
|
|
54
|
+
|
|
55
|
+
assert.ok(result !== null, "rule should match");
|
|
56
|
+
assert.equal(result!.action, "stop", "should return stop action");
|
|
57
|
+
if (result!.action === "stop") {
|
|
58
|
+
assert.equal(result!.level, "warning", "should be warning level (pausable)");
|
|
59
|
+
assert.ok(
|
|
60
|
+
result!.reason.includes("needs-remediation"),
|
|
61
|
+
"reason should mention needs-remediation",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
} finally {
|
|
65
|
+
rmSync(base, { recursive: true, force: true });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("completing-milestone proceeds normally when VALIDATION verdict is pass (#2675 guard)", async () => {
|
|
70
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-remediation-"));
|
|
71
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Write a VALIDATION file with pass verdict
|
|
75
|
+
writeFileSync(
|
|
76
|
+
join(base, ".gsd", "milestones", "M001", "M001-VALIDATION.md"),
|
|
77
|
+
[
|
|
78
|
+
"---",
|
|
79
|
+
"verdict: pass",
|
|
80
|
+
"---",
|
|
81
|
+
"",
|
|
82
|
+
"# Validation Report",
|
|
83
|
+
"",
|
|
84
|
+
"All criteria met.",
|
|
85
|
+
].join("\n"),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const ctx = {
|
|
89
|
+
mid: "M001",
|
|
90
|
+
midTitle: "Test Milestone",
|
|
91
|
+
basePath: base,
|
|
92
|
+
state: { phase: "completing-milestone" } as any,
|
|
93
|
+
prefs: {} as any,
|
|
94
|
+
session: undefined,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const result = await completingRule!.match(ctx);
|
|
98
|
+
|
|
99
|
+
// Should NOT return a stop — should either dispatch or return stop for
|
|
100
|
+
// a different reason (e.g. missing SUMMARY files, no implementation)
|
|
101
|
+
if (result && result.action === "stop") {
|
|
102
|
+
assert.ok(
|
|
103
|
+
!result.reason.includes("needs-remediation"),
|
|
104
|
+
"pass verdict should NOT trigger the remediation guard",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} finally {
|
|
108
|
+
rmSync(base, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
@@ -724,3 +724,32 @@ test("resolveRemoteConfig returns null when preferences are absent (no env side-
|
|
|
724
724
|
if (savedTelegram !== undefined) process.env.TELEGRAM_BOT_TOKEN = savedTelegram;
|
|
725
725
|
}
|
|
726
726
|
});
|
|
727
|
+
|
|
728
|
+
test("config source-level: hydration skips api_key entries with empty keys", () => {
|
|
729
|
+
const configSrc = readFileSync(
|
|
730
|
+
join(__dirname, "..", "..", "remote-questions", "config.ts"),
|
|
731
|
+
"utf-8",
|
|
732
|
+
);
|
|
733
|
+
// The find() call in hydrateRemoteTokensFromAuth must filter for non-empty keys,
|
|
734
|
+
// not just match on type === "api_key". This prevents stale empty-key entries
|
|
735
|
+
// (left by removeProviderToken) from shadowing valid tokens.
|
|
736
|
+
assert.ok(
|
|
737
|
+
configSrc.includes('c.type === "api_key" && !!c.key'),
|
|
738
|
+
"hydrateRemoteTokensFromAuth find() should require a non-empty key",
|
|
739
|
+
);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
test("config source-level: removeProviderToken uses auth.remove not auth.set with empty key", () => {
|
|
743
|
+
const commandSrc = readFileSync(
|
|
744
|
+
join(__dirname, "..", "..", "remote-questions", "remote-command.ts"),
|
|
745
|
+
"utf-8",
|
|
746
|
+
);
|
|
747
|
+
// removeProviderToken should call auth.remove(provider), not auth.set(provider, { key: "" }).
|
|
748
|
+
// Setting an empty key pollutes the credentials array and shadows valid tokens.
|
|
749
|
+
const fnStart = commandSrc.indexOf("function removeProviderToken");
|
|
750
|
+
assert.ok(fnStart !== -1, "removeProviderToken should exist");
|
|
751
|
+
const fnEnd = commandSrc.indexOf("\n}", fnStart);
|
|
752
|
+
const fnBody = commandSrc.slice(fnStart, fnEnd);
|
|
753
|
+
assert.ok(fnBody.includes("auth.remove("), "removeProviderToken should call auth.remove()");
|
|
754
|
+
assert.ok(!fnBody.includes('key: ""'), "removeProviderToken should not set an empty key");
|
|
755
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// GSD — status-guards unit tests
|
|
2
|
+
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
|
|
6
|
+
import { isClosedStatus } from '../status-guards.ts';
|
|
7
|
+
|
|
8
|
+
test('isClosedStatus: "complete" returns true', () => {
|
|
9
|
+
assert.equal(isClosedStatus('complete'), true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('isClosedStatus: "done" returns true', () => {
|
|
13
|
+
assert.equal(isClosedStatus('done'), true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('isClosedStatus: "pending" returns false', () => {
|
|
17
|
+
assert.equal(isClosedStatus('pending'), false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('isClosedStatus: "in_progress" returns false', () => {
|
|
21
|
+
assert.equal(isClosedStatus('in_progress'), false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('isClosedStatus: "active" returns false', () => {
|
|
25
|
+
assert.equal(isClosedStatus('active'), false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('isClosedStatus: "" (empty string) returns false', () => {
|
|
29
|
+
assert.equal(isClosedStatus(''), false);
|
|
30
|
+
});
|
|
@@ -1,73 +1,84 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* terminated-transient.test.ts — Regression test for #2309.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* classifyError should treat 'terminated' errors (process killed,
|
|
5
5
|
* connection reset) as transient with auto-resume, not permanent.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import test from "node:test";
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
|
-
import {
|
|
10
|
+
import { classifyError, isTransient } from "../error-classifier.ts";
|
|
11
11
|
|
|
12
12
|
test("#2309: 'terminated' errors should be classified as transient", () => {
|
|
13
|
-
const result =
|
|
14
|
-
assert.equal(result
|
|
15
|
-
assert.equal(result.
|
|
16
|
-
assert.ok(result.
|
|
13
|
+
const result = classifyError("terminated");
|
|
14
|
+
assert.equal(isTransient(result), true, "'terminated' should be transient");
|
|
15
|
+
assert.equal(result.kind, "connection", "'terminated' matches connection");
|
|
16
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs > 0, "'terminated' should have a retry delay");
|
|
17
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 15_000, "'terminated' should use 15s backoff");
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
test("#2309: 'connection reset' errors should be classified as transient", () => {
|
|
20
|
-
const result =
|
|
21
|
-
assert.equal(result
|
|
20
|
+
test("#2309: 'connection reset by peer' errors should be classified as transient (network)", () => {
|
|
21
|
+
const result = classifyError("connection reset by peer");
|
|
22
|
+
assert.equal(isTransient(result), true, "'connection reset by peer' should be transient");
|
|
23
|
+
assert.equal(result.kind, "network", "'connection reset by peer' matches NETWORK_RE (connection.*reset) before CONNECTION_RE");
|
|
24
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 3_000, "network errors use 3s backoff");
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
test("#2309: 'other side closed' errors should be classified as transient", () => {
|
|
25
|
-
const result =
|
|
26
|
-
assert.equal(result
|
|
28
|
+
const result = classifyError("other side closed the connection");
|
|
29
|
+
assert.equal(isTransient(result), true, "'other side closed' should be transient");
|
|
30
|
+
assert.equal(result.kind, "connection", "'other side closed' matches CONNECTION_RE");
|
|
27
31
|
});
|
|
28
32
|
|
|
29
33
|
test("#2309: 'fetch failed' errors should be classified as transient", () => {
|
|
30
|
-
const result =
|
|
31
|
-
assert.equal(result
|
|
34
|
+
const result = classifyError("fetch failed: network error");
|
|
35
|
+
assert.equal(isTransient(result), true, "'fetch failed' should be transient");
|
|
36
|
+
assert.equal(result.kind, "network", "'fetch failed' matches NETWORK_RE");
|
|
37
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 3_000, "network errors use 3s backoff");
|
|
32
38
|
});
|
|
33
39
|
|
|
34
40
|
test("#2309: 'connection refused' errors should be classified as transient", () => {
|
|
35
|
-
const result =
|
|
36
|
-
assert.equal(result
|
|
41
|
+
const result = classifyError("ECONNREFUSED: connection refused");
|
|
42
|
+
assert.equal(isTransient(result), true, "'connection refused' should be transient");
|
|
43
|
+
assert.equal(result.kind, "network", "'ECONNREFUSED' matches NETWORK_RE (same-model retry)");
|
|
37
44
|
});
|
|
38
45
|
|
|
39
46
|
test("#2309: permanent errors are still permanent", () => {
|
|
40
|
-
const authResult =
|
|
41
|
-
assert.equal(authResult
|
|
42
|
-
assert.equal(authResult.
|
|
47
|
+
const authResult = classifyError("unauthorized: invalid API key");
|
|
48
|
+
assert.equal(isTransient(authResult), false, "auth errors should stay permanent");
|
|
49
|
+
assert.equal(authResult.kind, "permanent", "auth errors are permanent");
|
|
50
|
+
assert.equal("retryAfterMs" in authResult, false, "permanent errors have no retryAfterMs");
|
|
43
51
|
});
|
|
44
52
|
|
|
45
53
|
test("#2309: rate limits are still transient", () => {
|
|
46
|
-
const rlResult =
|
|
47
|
-
assert.equal(rlResult
|
|
48
|
-
assert.equal(rlResult.
|
|
54
|
+
const rlResult = classifyError("rate limit exceeded (429)");
|
|
55
|
+
assert.equal(isTransient(rlResult), true, "rate limits are still transient");
|
|
56
|
+
assert.equal(rlResult.kind, "rate-limit", "rate limits are flagged as rate-limit kind");
|
|
49
57
|
});
|
|
50
58
|
|
|
51
59
|
// --- #2572: stream-truncation JSON parse errors should be transient ---
|
|
52
60
|
|
|
53
61
|
test("#2572: 'Expected double-quoted property name' (truncated stream) is transient", () => {
|
|
54
|
-
const result =
|
|
55
|
-
assert.equal(result
|
|
56
|
-
assert.equal(result.
|
|
57
|
-
assert.equal(result.
|
|
62
|
+
const result = classifyError("Expected double-quoted property name in JSON at position 23 (line 1 column 24)");
|
|
63
|
+
assert.equal(isTransient(result), true, "truncated-stream JSON parse error should be transient");
|
|
64
|
+
assert.equal(result.kind, "stream", "JSON parse errors are stream kind");
|
|
65
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 15_000, "should use 15s backoff");
|
|
58
66
|
});
|
|
59
67
|
|
|
60
68
|
test("#2572: 'Unexpected end of JSON input' (truncated stream) is transient", () => {
|
|
61
|
-
const result =
|
|
62
|
-
assert.equal(result
|
|
69
|
+
const result = classifyError("Unexpected end of JSON input");
|
|
70
|
+
assert.equal(isTransient(result), true, "'Unexpected end of JSON input' should be transient");
|
|
71
|
+
assert.equal(result.kind, "stream", "JSON parse errors are stream kind");
|
|
63
72
|
});
|
|
64
73
|
|
|
65
74
|
test("#2572: 'Unexpected token' in JSON (truncated stream) is transient", () => {
|
|
66
|
-
const result =
|
|
67
|
-
assert.equal(result
|
|
75
|
+
const result = classifyError("Unexpected token < in JSON at position 0");
|
|
76
|
+
assert.equal(isTransient(result), true, "'Unexpected token in JSON' should be transient");
|
|
77
|
+
assert.equal(result.kind, "stream", "JSON parse errors are stream kind");
|
|
68
78
|
});
|
|
69
79
|
|
|
70
80
|
test("#2572: 'SyntaxError' with JSON context (truncated stream) is transient", () => {
|
|
71
|
-
const result =
|
|
72
|
-
assert.equal(result
|
|
81
|
+
const result = classifyError("SyntaxError: JSON.parse: unexpected character at line 1 column 1");
|
|
82
|
+
assert.equal(isTransient(result), true, "'SyntaxError...JSON' should be transient");
|
|
83
|
+
assert.equal(result.kind, "stream", "JSON parse errors are stream kind");
|
|
73
84
|
});
|
|
@@ -63,13 +63,13 @@ test("show_token_cost defaults to undefined (disabled) when not set", () => {
|
|
|
63
63
|
assert.equal(preferences.show_token_cost, undefined);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
test("empty
|
|
66
|
+
test("empty PREFERENCES.md does not enable show_token_cost", () => {
|
|
67
67
|
const prefs = parsePreferencesMarkdown("---\nversion: 1\n---\n");
|
|
68
68
|
assert.ok(prefs);
|
|
69
69
|
assert.equal(prefs.show_token_cost, undefined);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
test("
|
|
72
|
+
test("PREFERENCES.md with show_token_cost: true enables the preference", () => {
|
|
73
73
|
const prefs = parsePreferencesMarkdown("---\nshow_token_cost: true\n---\n");
|
|
74
74
|
assert.ok(prefs);
|
|
75
75
|
assert.equal(prefs.show_token_cost, true);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2667: deriveStateFromDb must NOT treat an empty
|
|
3
|
+
* slice array as "all slices done" due to JavaScript's vacuous-truth
|
|
4
|
+
* behavior of Array.prototype.every on an empty array.
|
|
5
|
+
*
|
|
6
|
+
* [].every(predicate) === true in JavaScript. Without a length > 0 guard,
|
|
7
|
+
* this causes a premature phase transition to validating-milestone when
|
|
8
|
+
* the DB returns 0 slices (e.g. after a worktree DB wipe).
|
|
9
|
+
*/
|
|
10
|
+
import { test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
|
|
16
|
+
import { deriveStateFromDb, invalidateStateCache } from "../state.ts";
|
|
17
|
+
import {
|
|
18
|
+
openDatabase,
|
|
19
|
+
closeDatabase,
|
|
20
|
+
insertMilestone,
|
|
21
|
+
insertSlice,
|
|
22
|
+
} from "../gsd-db.ts";
|
|
23
|
+
|
|
24
|
+
test("deriveStateFromDb does NOT skip to validating when slice array is empty (#2667)", async () => {
|
|
25
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-vacuous-truth-"));
|
|
26
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Set up a milestone with a roadmap that references slices,
|
|
30
|
+
// but the DB has NO slice rows (simulating a worktree DB wipe)
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
33
|
+
[
|
|
34
|
+
"# M001: Test Milestone",
|
|
35
|
+
"",
|
|
36
|
+
"## Slices",
|
|
37
|
+
"",
|
|
38
|
+
"### S01 — First Slice",
|
|
39
|
+
"Do something.",
|
|
40
|
+
"",
|
|
41
|
+
"### S02 — Second Slice",
|
|
42
|
+
"Do another thing.",
|
|
43
|
+
].join("\n"),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
openDatabase(":memory:");
|
|
47
|
+
// Milestone exists but NO slices inserted — simulates DB wipe
|
|
48
|
+
insertMilestone({ id: "M001", title: "Test Milestone", status: "active" });
|
|
49
|
+
|
|
50
|
+
invalidateStateCache();
|
|
51
|
+
const state = await deriveStateFromDb(base);
|
|
52
|
+
|
|
53
|
+
// The phase must NOT be "validating-milestone" or "completing-milestone"
|
|
54
|
+
// because no slices have been executed — the empty array should not
|
|
55
|
+
// trigger the "all slices done" code path.
|
|
56
|
+
assert.notEqual(
|
|
57
|
+
state.phase,
|
|
58
|
+
"validating-milestone",
|
|
59
|
+
"empty slice array must not trigger validating-milestone (vacuous truth)",
|
|
60
|
+
);
|
|
61
|
+
assert.notEqual(
|
|
62
|
+
state.phase,
|
|
63
|
+
"completing-milestone",
|
|
64
|
+
"empty slice array must not trigger completing-milestone (vacuous truth)",
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
closeDatabase();
|
|
68
|
+
} finally {
|
|
69
|
+
closeDatabase();
|
|
70
|
+
rmSync(base, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("deriveStateFromDb correctly reaches validating when all slices are done (#2667 guard)", async () => {
|
|
75
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-vacuous-truth-"));
|
|
76
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
writeFileSync(
|
|
80
|
+
join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
81
|
+
[
|
|
82
|
+
"# M001: Test Milestone",
|
|
83
|
+
"",
|
|
84
|
+
"## Slices",
|
|
85
|
+
"",
|
|
86
|
+
"### S01 — First Slice",
|
|
87
|
+
"Do something.",
|
|
88
|
+
].join("\n"),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Write a slice summary so the filesystem recognizes it as complete
|
|
92
|
+
writeFileSync(
|
|
93
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md"),
|
|
94
|
+
"# S01 Summary\n\nDone.",
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
openDatabase(":memory:");
|
|
98
|
+
insertMilestone({ id: "M001", title: "Test Milestone", status: "active" });
|
|
99
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "First Slice", status: "complete", risk: "low", depends: [] });
|
|
100
|
+
|
|
101
|
+
invalidateStateCache();
|
|
102
|
+
const state = await deriveStateFromDb(base);
|
|
103
|
+
|
|
104
|
+
// With one slice that IS complete, phase should advance
|
|
105
|
+
assert.ok(
|
|
106
|
+
state.phase === "validating-milestone" || state.phase === "completing-milestone",
|
|
107
|
+
`expected validating or completing phase, got "${state.phase}"`,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
closeDatabase();
|
|
111
|
+
} finally {
|
|
112
|
+
closeDatabase();
|
|
113
|
+
rmSync(base, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
});
|