gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0
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 +46 -134
- package/dist/cli.js +48 -6
- package/dist/headless-query.js +11 -1
- package/dist/help-text.js +4 -1
- package/dist/onboarding.js +15 -8
- package/dist/resource-loader.js +18 -3
- package/dist/resources/extensions/cmux/index.js +21 -12
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
- package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -0
- package/dist/resources/extensions/gsd/auto/phases.js +157 -22
- package/dist/resources/extensions/gsd/auto/session.js +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
- package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
- package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
- package/dist/resources/extensions/gsd/auto-start.js +10 -21
- package/dist/resources/extensions/gsd/auto-timers.js +2 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +24 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
- package/dist/resources/extensions/gsd/constants.js +42 -0
- package/dist/resources/extensions/gsd/db-writer.js +72 -4
- package/dist/resources/extensions/gsd/forensics.js +20 -4
- package/dist/resources/extensions/gsd/gsd-db.js +64 -17
- package/dist/resources/extensions/gsd/guided-flow.js +19 -0
- package/dist/resources/extensions/gsd/metrics.js +27 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +6 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +11 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +4 -7
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
- package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
- package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
- package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
- package/dist/resources/extensions/gsd/state.js +74 -14
- package/dist/resources/extensions/gsd/status-guards.js +11 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
- package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
- package/dist/resources/extensions/mcp-client/auth.js +101 -0
- package/dist/resources/extensions/mcp-client/index.js +10 -1
- package/dist/resources/extensions/ollama/index.js +28 -22
- package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
- package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
- package/dist/resources/extensions/ollama/ollama-client.js +23 -32
- package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
- package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
- package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
- package/dist/update-cmd.js +4 -2
- 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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/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.js.nft.json +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.js.nft.json +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.js.nft.json +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.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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +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.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.js.nft.json +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.js.nft.json +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 +4 -4
- 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 +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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/6897.js +12 -0
- 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.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/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-0c485498795110d6.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/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/welcome-screen.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +50 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
- package/packages/pi-agent-core/src/agent-loop.ts +53 -0
- package/packages/pi-ai/dist/types.d.ts +16 -1
- 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/types.ts +18 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
- package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
- package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +18 -12
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
- package/src/resources/extensions/gsd/auto/loop.ts +5 -0
- package/src/resources/extensions/gsd/auto/phases.ts +194 -33
- package/src/resources/extensions/gsd/auto/session.ts +14 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
- package/src/resources/extensions/gsd/auto-start.ts +11 -20
- package/src/resources/extensions/gsd/auto-timers.ts +2 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +26 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
- package/src/resources/extensions/gsd/constants.ts +44 -0
- package/src/resources/extensions/gsd/db-writer.ts +78 -4
- package/src/resources/extensions/gsd/forensics.ts +21 -5
- package/src/resources/extensions/gsd/gsd-db.ts +64 -17
- package/src/resources/extensions/gsd/guided-flow.ts +22 -0
- package/src/resources/extensions/gsd/metrics.ts +28 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +44 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +13 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +4 -7
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
- package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
- package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
- package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
- package/src/resources/extensions/gsd/state.ts +67 -12
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
- package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
- package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
- package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
- package/src/resources/extensions/gsd/types.ts +44 -22
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
- package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
- package/src/resources/extensions/mcp-client/auth.ts +149 -0
- package/src/resources/extensions/mcp-client/index.ts +16 -1
- package/src/resources/extensions/ollama/index.ts +26 -25
- package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
- package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
- package/src/resources/extensions/ollama/ollama-client.ts +30 -30
- package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
- package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
- package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
- package/src/resources/extensions/ollama/types.ts +23 -0
- package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.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/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
|
@@ -18,6 +18,7 @@ import { loadFile, parseSummary, resolveAllOverrides } from "./files.js";
|
|
|
18
18
|
import { loadPrompt } from "./prompt-loader.js";
|
|
19
19
|
import {
|
|
20
20
|
resolveSliceFile,
|
|
21
|
+
resolveSlicePath,
|
|
21
22
|
resolveTaskFile,
|
|
22
23
|
resolveMilestoneFile,
|
|
23
24
|
resolveTasksDir,
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
import {
|
|
34
35
|
verifyExpectedArtifact,
|
|
35
36
|
resolveExpectedArtifactPath,
|
|
37
|
+
writeBlockerPlaceholder,
|
|
36
38
|
diagnoseExpectedArtifact,
|
|
37
39
|
} from "./auto-recovery.js";
|
|
38
40
|
import { regenerateIfMissing } from "./workflow-projections.js";
|
|
@@ -51,6 +53,20 @@ import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures
|
|
|
51
53
|
import { debugLog } from "./debug-logger.js";
|
|
52
54
|
import { runSafely } from "./auto-utils.js";
|
|
53
55
|
import type { AutoSession, SidecarItem } from "./auto/session.js";
|
|
56
|
+
import { getEvidence } from "./safety/evidence-collector.js";
|
|
57
|
+
import { validateFileChanges } from "./safety/file-change-validator.js";
|
|
58
|
+
// crossReferenceEvidence available for future use when verification_evidence is stored in DB
|
|
59
|
+
// import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
|
|
60
|
+
import { validateContent } from "./safety/content-validator.js";
|
|
61
|
+
import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
|
|
62
|
+
import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
|
|
63
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
64
|
+
import { getSliceTasks } from "./gsd-db.js";
|
|
65
|
+
import { runPreExecutionChecks, type PreExecutionResult } from "./pre-execution-checks.js";
|
|
66
|
+
import { writePreExecutionEvidence } from "./verification-evidence.js";
|
|
67
|
+
|
|
68
|
+
/** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
|
|
69
|
+
const MAX_VERIFICATION_RETRIES = 3;
|
|
54
70
|
|
|
55
71
|
|
|
56
72
|
/** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
|
|
@@ -433,6 +449,87 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
433
449
|
debugLog("postUnit", { phase: "rogue-detection", error: String(e) });
|
|
434
450
|
}
|
|
435
451
|
|
|
452
|
+
// ── Safety harness: post-unit validation ──
|
|
453
|
+
try {
|
|
454
|
+
const { loadEffectiveGSDPreferences } = await import("./preferences.js");
|
|
455
|
+
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
456
|
+
const safetyConfig = resolveSafetyHarnessConfig(
|
|
457
|
+
prefs?.safety_harness as Record<string, unknown> | undefined,
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
if (safetyConfig.enabled) {
|
|
461
|
+
const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
|
|
462
|
+
|
|
463
|
+
// File change validation (execute-task only, after auto-commit)
|
|
464
|
+
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
|
|
465
|
+
try {
|
|
466
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
467
|
+
if (taskRow) {
|
|
468
|
+
const expectedOutput = taskRow.expected_output ?? [];
|
|
469
|
+
const plannedFiles = taskRow.files ?? [];
|
|
470
|
+
const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles);
|
|
471
|
+
if (audit && audit.violations.length > 0) {
|
|
472
|
+
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
473
|
+
for (const v of warnings) {
|
|
474
|
+
logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
|
|
475
|
+
}
|
|
476
|
+
if (warnings.length > 0) {
|
|
477
|
+
ctx.ui.notify(
|
|
478
|
+
`Safety: ${warnings.length} unexpected file change(s) outside task plan`,
|
|
479
|
+
"warning",
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} catch (e) {
|
|
485
|
+
debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Evidence cross-reference (execute-task only)
|
|
490
|
+
// Verification evidence is passed via the complete-task tool call and
|
|
491
|
+
// stored in the SUMMARY.md on disk — not available as structured data
|
|
492
|
+
// in the DB. The evidence collector tracks actual bash tool calls, so
|
|
493
|
+
// we can still detect units that claimed success but ran no commands.
|
|
494
|
+
if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
|
|
495
|
+
try {
|
|
496
|
+
const actual = getEvidence();
|
|
497
|
+
const bashCalls = actual.filter(e => e.kind === "bash");
|
|
498
|
+
// If the task is marked complete but zero bash commands were run,
|
|
499
|
+
// it's suspicious — the LLM may have fabricated results.
|
|
500
|
+
if (sMid && sSid && sTid && isDbAvailable()) {
|
|
501
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
502
|
+
if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
|
|
503
|
+
logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
|
|
504
|
+
ctx.ui.notify(
|
|
505
|
+
`Safety: task ${sTid} has verification commands but no bash calls were recorded`,
|
|
506
|
+
"warning",
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} catch (e) {
|
|
511
|
+
debugLog("postUnit", { phase: "safety-evidence-xref", error: String(e) });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Content validation (plan-slice, plan-milestone)
|
|
516
|
+
if (safetyConfig.content_validation) {
|
|
517
|
+
try {
|
|
518
|
+
const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
|
|
519
|
+
const contentViolations = validateContent(s.currentUnit.type, artifactPath);
|
|
520
|
+
for (const v of contentViolations) {
|
|
521
|
+
logWarning("safety", `content: ${v.reason}`);
|
|
522
|
+
ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
|
|
523
|
+
}
|
|
524
|
+
} catch (e) {
|
|
525
|
+
debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} catch (e) {
|
|
530
|
+
debugLog("postUnit", { phase: "safety-harness", error: String(e) });
|
|
531
|
+
}
|
|
532
|
+
|
|
436
533
|
// Artifact verification
|
|
437
534
|
let triggerArtifactVerified = false;
|
|
438
535
|
if (!s.currentUnit.type.startsWith("hook/")) {
|
|
@@ -468,6 +565,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
468
565
|
// When artifact verification fails for a unit type that has a known expected
|
|
469
566
|
// artifact, return "retry" so the caller re-dispatches with failure context
|
|
470
567
|
// instead of blindly re-dispatching the same unit (#1571).
|
|
568
|
+
// After MAX_VERIFICATION_RETRIES, escalate to writeBlockerPlaceholder so the
|
|
569
|
+
// pipeline can advance instead of looping forever (#2653).
|
|
471
570
|
//
|
|
472
571
|
// HOWEVER, if the DB is unavailable (db_unavailable), the artifact was never
|
|
473
572
|
// written because the completion tool failed at the infra level. Retrying
|
|
@@ -483,23 +582,58 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
483
582
|
"error",
|
|
484
583
|
);
|
|
485
584
|
} else if (!triggerArtifactVerified) {
|
|
585
|
+
// #2883: If the artifact is missing because the tool invocation itself
|
|
586
|
+
// failed (malformed/truncated JSON arguments), retrying will produce the
|
|
587
|
+
// same failure. Pause auto-mode instead of entering a stuck retry loop.
|
|
588
|
+
if (s.lastToolInvocationError) {
|
|
589
|
+
const errMsg = `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
|
|
590
|
+
debugLog("postUnit", { phase: "tool-invocation-error-pause", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
|
|
591
|
+
ctx.ui.notify(errMsg, "error");
|
|
592
|
+
s.lastToolInvocationError = null;
|
|
593
|
+
await pauseAuto(ctx, pi);
|
|
594
|
+
return "dispatched";
|
|
595
|
+
}
|
|
596
|
+
|
|
486
597
|
const hasExpectedArtifact = resolveExpectedArtifactPath(s.currentUnit.type, s.currentUnit.id, s.basePath) !== null;
|
|
487
598
|
if (hasExpectedArtifact) {
|
|
488
599
|
const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
|
|
489
600
|
const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
|
|
490
601
|
s.verificationRetryCount.set(retryKey, attempt);
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
602
|
+
|
|
603
|
+
if (attempt > MAX_VERIFICATION_RETRIES) {
|
|
604
|
+
// Retries exhausted — write a blocker placeholder so the pipeline
|
|
605
|
+
// can advance past this stuck unit (#2653).
|
|
606
|
+
debugLog("postUnit", {
|
|
607
|
+
phase: "artifact-verify-escalate",
|
|
608
|
+
unitType: s.currentUnit.type,
|
|
609
|
+
unitId: s.currentUnit.id,
|
|
610
|
+
attempt,
|
|
611
|
+
maxRetries: MAX_VERIFICATION_RETRIES,
|
|
612
|
+
});
|
|
613
|
+
const reason = `Artifact verification failed after ${MAX_VERIFICATION_RETRIES} retries for ${s.currentUnit.type} "${s.currentUnit.id}".`;
|
|
614
|
+
writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
|
|
615
|
+
ctx.ui.notify(
|
|
616
|
+
`${s.currentUnit.type} ${s.currentUnit.id} — verification retries exhausted (${MAX_VERIFICATION_RETRIES}), wrote blocker placeholder to advance pipeline`,
|
|
617
|
+
"warning",
|
|
618
|
+
);
|
|
619
|
+
// Reset retry count and fall through to "continue" so the loop
|
|
620
|
+
// re-derives state with the placeholder in place.
|
|
621
|
+
s.verificationRetryCount.delete(retryKey);
|
|
622
|
+
s.pendingVerificationRetry = null;
|
|
623
|
+
// Do NOT return "retry" — fall through to "continue" below.
|
|
624
|
+
} else {
|
|
625
|
+
s.pendingVerificationRetry = {
|
|
626
|
+
unitId: s.currentUnit.id,
|
|
627
|
+
failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
|
|
628
|
+
attempt,
|
|
629
|
+
};
|
|
630
|
+
debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
|
|
631
|
+
ctx.ui.notify(
|
|
632
|
+
`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt})`,
|
|
633
|
+
"warning",
|
|
634
|
+
);
|
|
635
|
+
return "retry";
|
|
636
|
+
}
|
|
503
637
|
}
|
|
504
638
|
}
|
|
505
639
|
} else {
|
|
@@ -643,6 +777,123 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
643
777
|
}
|
|
644
778
|
}
|
|
645
779
|
|
|
780
|
+
// ── Pre-execution checks (after plan-slice completes) ──
|
|
781
|
+
if (
|
|
782
|
+
s.currentUnit &&
|
|
783
|
+
s.currentUnit.type === "plan-slice"
|
|
784
|
+
) {
|
|
785
|
+
let preExecPauseNeeded = false;
|
|
786
|
+
await runSafely("postUnitPostVerification", "pre-execution-checks", async () => {
|
|
787
|
+
try {
|
|
788
|
+
// Check preferences — respect enhanced_verification and enhanced_verification_pre
|
|
789
|
+
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
790
|
+
const enhancedEnabled = prefs?.enhanced_verification !== false; // default true
|
|
791
|
+
const preEnabled = prefs?.enhanced_verification_pre !== false; // default true
|
|
792
|
+
|
|
793
|
+
if (!enhancedEnabled || !preEnabled) {
|
|
794
|
+
debugLog("postUnitPostVerification", {
|
|
795
|
+
phase: "pre-execution-checks",
|
|
796
|
+
skipped: true,
|
|
797
|
+
reason: "disabled by preferences",
|
|
798
|
+
});
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Parse the unit ID to get milestone/slice IDs
|
|
803
|
+
const { milestone: mid, slice: sid } = parseUnitId(s.currentUnit!.id);
|
|
804
|
+
if (!mid || !sid) {
|
|
805
|
+
debugLog("postUnitPostVerification", {
|
|
806
|
+
phase: "pre-execution-checks",
|
|
807
|
+
skipped: true,
|
|
808
|
+
reason: "could not parse milestone/slice from unit ID",
|
|
809
|
+
});
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Get tasks for this slice from DB
|
|
814
|
+
const tasks = getSliceTasks(mid, sid);
|
|
815
|
+
if (tasks.length === 0) {
|
|
816
|
+
debugLog("postUnitPostVerification", {
|
|
817
|
+
phase: "pre-execution-checks",
|
|
818
|
+
skipped: true,
|
|
819
|
+
reason: "no tasks found for slice",
|
|
820
|
+
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Run pre-execution checks
|
|
825
|
+
const result: PreExecutionResult = await runPreExecutionChecks(tasks, s.basePath);
|
|
826
|
+
|
|
827
|
+
// Log summary to stderr in existing verification output format
|
|
828
|
+
const emoji = result.status === "pass" ? "✅" : result.status === "warn" ? "⚠️" : "❌";
|
|
829
|
+
process.stderr.write(
|
|
830
|
+
`gsd-pre-exec: ${emoji} Pre-execution checks ${result.status} for ${mid}/${sid} (${result.durationMs}ms)\n`,
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
// Log individual check results
|
|
834
|
+
for (const check of result.checks) {
|
|
835
|
+
const checkEmoji = check.passed ? "✓" : check.blocking ? "✗" : "⚠";
|
|
836
|
+
process.stderr.write(
|
|
837
|
+
`gsd-pre-exec: ${checkEmoji} [${check.category}] ${check.target}: ${check.message}\n`,
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Write evidence JSON to slice artifacts directory
|
|
842
|
+
const slicePath = resolveSlicePath(s.basePath, mid, sid);
|
|
843
|
+
if (slicePath) {
|
|
844
|
+
writePreExecutionEvidence(result, slicePath, mid, sid);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Notify UI
|
|
848
|
+
if (result.status === "fail") {
|
|
849
|
+
const blockingCount = result.checks.filter(c => !c.passed && c.blocking).length;
|
|
850
|
+
ctx.ui.notify(
|
|
851
|
+
`Pre-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found`,
|
|
852
|
+
"error",
|
|
853
|
+
);
|
|
854
|
+
preExecPauseNeeded = true;
|
|
855
|
+
} else if (result.status === "warn") {
|
|
856
|
+
ctx.ui.notify(
|
|
857
|
+
`Pre-execution checks passed with warnings`,
|
|
858
|
+
"warning",
|
|
859
|
+
);
|
|
860
|
+
// Strict mode: treat warnings as blocking
|
|
861
|
+
if (prefs?.enhanced_verification_strict === true) {
|
|
862
|
+
preExecPauseNeeded = true;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
debugLog("postUnitPostVerification", {
|
|
867
|
+
phase: "pre-execution-checks",
|
|
868
|
+
status: result.status,
|
|
869
|
+
checkCount: result.checks.length,
|
|
870
|
+
durationMs: result.durationMs,
|
|
871
|
+
});
|
|
872
|
+
} catch (preExecError) {
|
|
873
|
+
// Fail-closed: if runPreExecutionChecks throws, pause auto-mode instead of silently continuing
|
|
874
|
+
const errorMessage = preExecError instanceof Error ? preExecError.message : String(preExecError);
|
|
875
|
+
debugLog("postUnitPostVerification", {
|
|
876
|
+
phase: "pre-execution-checks",
|
|
877
|
+
error: errorMessage,
|
|
878
|
+
failClosed: true,
|
|
879
|
+
});
|
|
880
|
+
logError("engine", `gsd-pre-exec: Pre-execution checks threw an error: ${errorMessage}`);
|
|
881
|
+
ctx.ui.notify(
|
|
882
|
+
`Pre-execution checks error: ${errorMessage} — pausing for human review`,
|
|
883
|
+
"error",
|
|
884
|
+
);
|
|
885
|
+
preExecPauseNeeded = true;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
// Check for blocking failures after runSafely completes
|
|
890
|
+
if (preExecPauseNeeded) {
|
|
891
|
+
debugLog("postUnitPostVerification", { phase: "pre-execution-checks", pausing: true, reason: "blocking failures detected" });
|
|
892
|
+
await pauseAuto(ctx, pi);
|
|
893
|
+
return "stopped";
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
646
897
|
// ── Triage check ──
|
|
647
898
|
if (
|
|
648
899
|
!s.stepMode &&
|
|
@@ -994,10 +994,15 @@ export async function buildResearchSlicePrompt(
|
|
|
994
994
|
const milestoneResearchPath = resolveMilestoneFile(base, mid, "RESEARCH");
|
|
995
995
|
const milestoneResearchRel = relMilestoneFile(base, mid, "RESEARCH");
|
|
996
996
|
|
|
997
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
998
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
999
|
+
|
|
997
1000
|
const inlined: string[] = [];
|
|
998
1001
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
999
1002
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1000
1003
|
if (contextInline) inlined.push(contextInline);
|
|
1004
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1005
|
+
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1001
1006
|
const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
|
|
1002
1007
|
if (researchInline) inlined.push(researchInline);
|
|
1003
1008
|
const decisionsInline = await inlineDecisionsFromDb(base, mid);
|
|
@@ -1045,6 +1050,8 @@ export async function buildPlanSlicePrompt(
|
|
|
1045
1050
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1046
1051
|
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
|
|
1047
1052
|
const researchRel = relSliceFile(base, mid, sid, "RESEARCH");
|
|
1053
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
1054
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1048
1055
|
|
|
1049
1056
|
const inlined: string[] = [];
|
|
1050
1057
|
|
|
@@ -1053,6 +1060,8 @@ export async function buildPlanSlicePrompt(
|
|
|
1053
1060
|
if (researchSliceAnchor) inlined.push(formatAnchorForPrompt(researchSliceAnchor));
|
|
1054
1061
|
|
|
1055
1062
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1063
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1064
|
+
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1056
1065
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
1057
1066
|
if (researchInline) inlined.push(researchInline);
|
|
1058
1067
|
if (inlineLevel !== "minimal") {
|
|
@@ -1253,9 +1262,13 @@ export async function buildCompleteSlicePrompt(
|
|
|
1253
1262
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1254
1263
|
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
|
|
1255
1264
|
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
|
|
1265
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
1266
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1256
1267
|
|
|
1257
1268
|
const inlined: string[] = [];
|
|
1258
1269
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1270
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1271
|
+
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1259
1272
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
|
|
1260
1273
|
if (inlineLevel !== "minimal") {
|
|
1261
1274
|
const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
|
|
@@ -1510,9 +1523,13 @@ export async function buildReplanSlicePrompt(
|
|
|
1510
1523
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1511
1524
|
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
|
|
1512
1525
|
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
|
|
1526
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
1527
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1513
1528
|
|
|
1514
1529
|
const inlined: string[] = [];
|
|
1515
1530
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1531
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1532
|
+
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1516
1533
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Current Slice Plan"));
|
|
1517
1534
|
|
|
1518
1535
|
// Find the blocker task summary — the completed task with blocker_discovered: true
|
|
@@ -1627,9 +1644,13 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1627
1644
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1628
1645
|
const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
1629
1646
|
const summaryRel = relSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
1647
|
+
const sliceContextPath = resolveSliceFile(base, mid, completedSliceId, "CONTEXT");
|
|
1648
|
+
const sliceContextRel = relSliceFile(base, mid, completedSliceId, "CONTEXT");
|
|
1630
1649
|
|
|
1631
1650
|
const inlined: string[] = [];
|
|
1632
1651
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
|
|
1652
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1653
|
+
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1633
1654
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
|
|
1634
1655
|
if (inlineLevel !== "minimal") {
|
|
1635
1656
|
const projectInline = await inlineProjectFromDb(base);
|
|
@@ -12,7 +12,7 @@ import { parseUnitId } from "./unit-id.js";
|
|
|
12
12
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
13
13
|
import { clearParseCache } from "./files.js";
|
|
14
14
|
import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
|
|
15
|
-
import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus } from "./gsd-db.js";
|
|
15
|
+
import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
|
|
16
16
|
import { isValidationTerminal } from "./state.js";
|
|
17
17
|
import { getErrorMessage } from "./error-utils.js";
|
|
18
18
|
import { logWarning, logError } from "./workflow-logger.js";
|
|
@@ -424,15 +424,16 @@ export function writeBlockerPlaceholder(
|
|
|
424
424
|
].join("\n");
|
|
425
425
|
writeFileSync(absPath, content, "utf-8");
|
|
426
426
|
|
|
427
|
-
// Mark the task as complete in the DB so verifyExpectedArtifact passes.
|
|
427
|
+
// Mark the task/slice as complete in the DB so verifyExpectedArtifact passes.
|
|
428
428
|
// Without this, the DB status stays "pending" and the dispatch loop
|
|
429
|
-
// re-derives the same
|
|
430
|
-
if (
|
|
429
|
+
// re-derives the same unit indefinitely (#2531, #2653).
|
|
430
|
+
if (isDbAvailable()) {
|
|
431
431
|
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
432
|
-
if (mid && sid && tid) {
|
|
433
|
-
try { updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString()); } catch (
|
|
434
|
-
|
|
435
|
-
|
|
432
|
+
if (unitType === "execute-task" && mid && sid && tid) {
|
|
433
|
+
try { updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
|
|
434
|
+
}
|
|
435
|
+
if (unitType === "complete-slice" && mid && sid) {
|
|
436
|
+
try { updateSliceStatus(mid, sid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
|
|
436
437
|
}
|
|
437
438
|
}
|
|
438
439
|
|
|
@@ -58,7 +58,7 @@ import { initRoutingHistory } from "./routing-history.js";
|
|
|
58
58
|
import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
|
|
59
59
|
import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
|
|
60
60
|
import { snapshotSkills } from "./skill-discovery.js";
|
|
61
|
-
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
61
|
+
import { isDbAvailable, getMilestone, openDatabase } from "./gsd-db.js";
|
|
62
62
|
import { hideFooter } from "./auto-dashboard.js";
|
|
63
63
|
import {
|
|
64
64
|
debugLog,
|
|
@@ -99,33 +99,24 @@ export interface BootstrapDeps {
|
|
|
99
99
|
* concurrent session detected). Returns true when ready to dispatch.
|
|
100
100
|
*/
|
|
101
101
|
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
async function openProjectDbIfPresent(basePath: string): Promise<void> {
|
|
102
|
+
/** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
|
|
103
|
+
* Prevents the recursive dialog loop described in #1348 where
|
|
104
|
+
* bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
|
|
105
|
+
* cycles indefinitely when the discuss workflow doesn't produce a milestone. */
|
|
106
|
+
let _consecutiveCompleteBootstraps = 0;
|
|
107
|
+
const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
|
|
108
|
+
|
|
109
|
+
export async function openProjectDbIfPresent(basePath: string): Promise<void> {
|
|
110
110
|
const gsdDbPath = resolveProjectRootDbPath(basePath);
|
|
111
|
-
if (!existsSync(gsdDbPath)) return;
|
|
112
|
-
if (isDbAvailable()) return;
|
|
111
|
+
if (!existsSync(gsdDbPath) || isDbAvailable()) return;
|
|
113
112
|
|
|
114
113
|
try {
|
|
115
|
-
const { openDatabase } = await import("./gsd-db.js");
|
|
116
114
|
openDatabase(gsdDbPath);
|
|
117
115
|
} catch (err) {
|
|
118
|
-
|
|
119
|
-
logWarning("engine", `DB open failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
116
|
+
logWarning("engine", `gsd-db: failed to open existing database: ${err instanceof Error ? err.message : String(err)}`);
|
|
120
117
|
}
|
|
121
118
|
}
|
|
122
119
|
|
|
123
|
-
/** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
|
|
124
|
-
* Prevents the recursive dialog loop described in #1348 where
|
|
125
|
-
* bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
|
|
126
|
-
* cycles indefinitely when the discuss workflow doesn't produce a milestone. */
|
|
127
|
-
let _consecutiveCompleteBootstraps = 0;
|
|
128
|
-
const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
|
|
129
120
|
export async function bootstrapAutoSession(
|
|
130
121
|
s: AutoSession,
|
|
131
122
|
ctx: ExtensionCommandContext,
|
|
@@ -106,8 +106,9 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
const estimateMinutes = taskEstimate ? parseEstimateMinutes(taskEstimate) : null;
|
|
109
|
+
const MAX_TIMEOUT_SCALE = 6; // Cap at 6x (60min task). Prevents 2h+ tasks from creating 120min+ timeout windows.
|
|
109
110
|
const timeoutScale = estimateMinutes && estimateMinutes > 0
|
|
110
|
-
? Math.max(1, estimateMinutes / 10)
|
|
111
|
+
? Math.min(MAX_TIMEOUT_SCALE, Math.max(1, estimateMinutes / 10))
|
|
111
112
|
: 1;
|
|
112
113
|
|
|
113
114
|
const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
|
|
@@ -83,3 +83,22 @@ export function hasInteractiveToolInFlight(): boolean {
|
|
|
83
83
|
export function clearInFlightTools(): void {
|
|
84
84
|
inFlightTools.clear();
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
// ─── Tool invocation error classification (#2883) ────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Patterns that indicate a tool invocation failed due to malformed or truncated
|
|
91
|
+
* JSON arguments — as opposed to a normal business-logic error from the tool
|
|
92
|
+
* handler. When these errors occur, retrying the same unit will produce the same
|
|
93
|
+
* failure, so the retry loop must be broken.
|
|
94
|
+
*/
|
|
95
|
+
const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns true if the error message indicates a tool invocation failure due to
|
|
99
|
+
* malformed/truncated arguments (as opposed to a normal tool execution error).
|
|
100
|
+
*/
|
|
101
|
+
export function isToolInvocationError(errorMsg: string): boolean {
|
|
102
|
+
if (!errorMsg) return false;
|
|
103
|
+
return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
|
|
104
|
+
}
|