gsd-pi 2.63.0-dev.026d309 → 2.63.0-dev.786f0ff
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 +9 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +124 -10
- 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-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +19 -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/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences.js +7 -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/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 +20 -20
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +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/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +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/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +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/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -20
- package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-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/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/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 +11 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +141 -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-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +22 -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/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences.ts +9 -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/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/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/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/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/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_ssgManifest.js +0 -0
|
@@ -13,13 +13,20 @@ import { runUnit } from "./run-unit.js";
|
|
|
13
13
|
import { debugLog } from "../debug-logger.js";
|
|
14
14
|
import { PROJECT_FILES } from "../detection.js";
|
|
15
15
|
import { MergeConflictError } from "../git-service.js";
|
|
16
|
-
import { join, basename } from "node:path";
|
|
17
|
-
import { existsSync, cpSync } from "node:fs";
|
|
16
|
+
import { join, basename, dirname, parse as parsePath } from "node:path";
|
|
17
|
+
import { existsSync, cpSync, readdirSync } from "node:fs";
|
|
18
18
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
19
19
|
import { gsdRoot } from "../paths.js";
|
|
20
20
|
import { atomicWriteSync } from "../atomic-write.js";
|
|
21
21
|
import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.js";
|
|
22
22
|
import { writeUnitRuntimeRecord } from "../unit-runtime.js";
|
|
23
|
+
import { withTimeout, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
|
|
24
|
+
import { getEligibleSlices } from "../slice-parallel-eligibility.js";
|
|
25
|
+
import { startSliceParallel } from "../slice-parallel-orchestrator.js";
|
|
26
|
+
import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
|
|
27
|
+
import { resetEvidence } from "../safety/evidence-collector.js";
|
|
28
|
+
import { createCheckpoint, cleanupCheckpoint, rollbackToCheckpoint } from "../safety/git-checkpoint.js";
|
|
29
|
+
import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
|
|
23
30
|
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
|
24
31
|
/**
|
|
25
32
|
* Resolve the base path for milestone reports.
|
|
@@ -142,6 +149,50 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
142
149
|
mid,
|
|
143
150
|
statePhase: state.phase,
|
|
144
151
|
});
|
|
152
|
+
// ── Slice-level parallelism gate (#2340) ─────────────────────────────
|
|
153
|
+
// When slice_parallel is enabled, check if multiple slices are eligible
|
|
154
|
+
// for parallel execution. If so, dispatch them in parallel and stop the
|
|
155
|
+
// sequential loop. Workers are spawned via slice-parallel-orchestrator.ts.
|
|
156
|
+
if (prefs?.slice_parallel?.enabled &&
|
|
157
|
+
mid &&
|
|
158
|
+
!process.env.GSD_PARALLEL_WORKER &&
|
|
159
|
+
isDbAvailable()) {
|
|
160
|
+
try {
|
|
161
|
+
const dbSlices = getMilestoneSlices(mid);
|
|
162
|
+
if (dbSlices.length > 0) {
|
|
163
|
+
const doneIds = new Set(dbSlices.filter(sl => sl.status === "complete" || sl.status === "done").map(sl => sl.id));
|
|
164
|
+
const sliceInputs = dbSlices.map(sl => ({
|
|
165
|
+
id: sl.id,
|
|
166
|
+
done: doneIds.has(sl.id),
|
|
167
|
+
depends: sl.depends ?? [],
|
|
168
|
+
}));
|
|
169
|
+
const eligible = getEligibleSlices(sliceInputs, doneIds);
|
|
170
|
+
if (eligible.length > 1) {
|
|
171
|
+
debugLog("autoLoop", {
|
|
172
|
+
phase: "slice-parallel-dispatch",
|
|
173
|
+
iteration: ic.iteration,
|
|
174
|
+
mid,
|
|
175
|
+
eligibleSlices: eligible.map(e => e.id),
|
|
176
|
+
});
|
|
177
|
+
ctx.ui.notify(`Slice-parallel: dispatching ${eligible.length} eligible slices for ${mid}.`, "info");
|
|
178
|
+
const result = await startSliceParallel(s.basePath, mid, eligible, { maxWorkers: prefs.slice_parallel.max_workers ?? 2 });
|
|
179
|
+
if (result.started.length > 0) {
|
|
180
|
+
ctx.ui.notify(`Slice-parallel: started ${result.started.length} worker(s): ${result.started.join(", ")}.`, "info");
|
|
181
|
+
await deps.stopAuto(ctx, pi, `Slice-parallel dispatched for ${mid}`);
|
|
182
|
+
return { action: "break", reason: "slice-parallel-dispatched" };
|
|
183
|
+
}
|
|
184
|
+
// Fall through to sequential if no workers started
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
debugLog("autoLoop", {
|
|
190
|
+
phase: "slice-parallel-check-error",
|
|
191
|
+
error: err instanceof Error ? err.message : String(err),
|
|
192
|
+
});
|
|
193
|
+
// Non-fatal — fall through to sequential dispatch
|
|
194
|
+
}
|
|
195
|
+
}
|
|
145
196
|
// ── Milestone transition ────────────────────────────────────────────
|
|
146
197
|
if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
|
|
147
198
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
|
|
@@ -669,11 +720,40 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
669
720
|
}
|
|
670
721
|
const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
|
|
671
722
|
const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
|
|
672
|
-
|
|
723
|
+
// Xcode bundles have project-specific names (*.xcodeproj, *.xcworkspace)
|
|
724
|
+
// that cannot be matched by exact filename — scan the directory by suffix.
|
|
725
|
+
let hasXcodeBundle = false;
|
|
726
|
+
try {
|
|
727
|
+
const entries = deps.existsSync(s.basePath) ? readdirSync(s.basePath) : [];
|
|
728
|
+
hasXcodeBundle = entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
|
|
729
|
+
}
|
|
730
|
+
catch (err) {
|
|
731
|
+
debugLog("runUnitPhase", { phase: "xcode-bundle-scan-failed", basePath: s.basePath, error: String(err) });
|
|
732
|
+
}
|
|
733
|
+
// Monorepo support (#2347): if no project files in the worktree directory,
|
|
734
|
+
// walk parent directories up to the filesystem root. In monorepos,
|
|
735
|
+
// package.json / Cargo.toml etc. live in a parent directory.
|
|
736
|
+
let hasProjectFileInParent = false;
|
|
737
|
+
if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle) {
|
|
738
|
+
let checkDir = dirname(s.basePath);
|
|
739
|
+
const { root } = parsePath(checkDir);
|
|
740
|
+
while (checkDir !== root) {
|
|
741
|
+
// Stop at git repository boundary — ancestors above the repo root
|
|
742
|
+
// (e.g. ~ or /usr/local) may contain unrelated project files.
|
|
743
|
+
if (deps.existsSync(join(checkDir, ".git")))
|
|
744
|
+
break;
|
|
745
|
+
if (PROJECT_FILES.some((f) => deps.existsSync(join(checkDir, f)))) {
|
|
746
|
+
hasProjectFileInParent = true;
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
checkDir = dirname(checkDir);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
|
|
673
753
|
// Greenfield projects won't have project files yet — the first task creates them.
|
|
674
754
|
// Log a warning but allow execution to proceed. The .git check above is sufficient
|
|
675
755
|
// to ensure we're in a valid working directory.
|
|
676
|
-
debugLog("runUnitPhase", { phase: "worktree-health-warn-greenfield", basePath: s.basePath, hasProjectFile, hasSrcDir });
|
|
756
|
+
debugLog("runUnitPhase", { phase: "worktree-health-warn-greenfield", basePath: s.basePath, hasProjectFile, hasSrcDir, hasXcodeBundle });
|
|
677
757
|
ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
|
|
678
758
|
}
|
|
679
759
|
}
|
|
@@ -683,6 +763,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
683
763
|
s.currentUnit.id === unitId);
|
|
684
764
|
const previousTier = s.currentUnitRouting?.tier;
|
|
685
765
|
s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
|
|
766
|
+
s.lastToolInvocationError = null; // #2883: clear stale error from previous unit
|
|
686
767
|
const unitStartSeq = ic.nextSeq();
|
|
687
768
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
|
|
688
769
|
deps.captureAvailableSkills();
|
|
@@ -695,18 +776,22 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
695
776
|
lastProgressKind: "dispatch",
|
|
696
777
|
recoveryAttempts: 0, // Reset so re-dispatched units get full recovery budget (#2322)
|
|
697
778
|
});
|
|
698
|
-
//
|
|
699
|
-
const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier });
|
|
700
|
-
s.currentUnitRouting =
|
|
701
|
-
modelResult.routing;
|
|
702
|
-
s.currentUnitModel =
|
|
703
|
-
modelResult.appliedModel;
|
|
704
|
-
// Status bar + progress widget
|
|
779
|
+
// Status bar (widget + preconditions deferred until after model selection — see #2899)
|
|
705
780
|
ctx.ui.setStatus("gsd-auto", "auto");
|
|
706
781
|
if (mid)
|
|
707
782
|
deps.updateSliceProgressCache(s.basePath, mid, state.activeSlice?.id);
|
|
708
|
-
|
|
709
|
-
|
|
783
|
+
// ── Safety harness: reset evidence + create checkpoint ──
|
|
784
|
+
const safetyConfig = resolveSafetyHarnessConfig(prefs?.safety_harness);
|
|
785
|
+
if (safetyConfig.enabled && safetyConfig.evidence_collection) {
|
|
786
|
+
resetEvidence();
|
|
787
|
+
}
|
|
788
|
+
// Only checkpoint code-executing units (not lifecycle/planning units)
|
|
789
|
+
if (safetyConfig.enabled && safetyConfig.checkpoints && unitType === "execute-task") {
|
|
790
|
+
s.checkpointSha = createCheckpoint(s.basePath, unitId);
|
|
791
|
+
if (s.checkpointSha) {
|
|
792
|
+
debugLog("runUnitPhase", { phase: "checkpoint-created", unitId, sha: s.checkpointSha.slice(0, 8) });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
710
795
|
// Prompt injection
|
|
711
796
|
let finalPrompt = prompt;
|
|
712
797
|
if (s.pendingVerificationRetry) {
|
|
@@ -764,6 +849,12 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
764
849
|
const msg = reorderErr instanceof Error ? reorderErr.message : String(reorderErr);
|
|
765
850
|
logWarning("engine", "Prompt reorder failed", { error: msg });
|
|
766
851
|
}
|
|
852
|
+
// Select and apply model (with tier escalation on retry — normal units only)
|
|
853
|
+
const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier });
|
|
854
|
+
s.currentUnitRouting =
|
|
855
|
+
modelResult.routing;
|
|
856
|
+
s.currentUnitModel =
|
|
857
|
+
modelResult.appliedModel;
|
|
767
858
|
// Apply sidecar/pre-dispatch hook model override (takes priority over standard model selection)
|
|
768
859
|
const hookModelOverride = sidecarItem?.model ?? iterData.hookModelOverride;
|
|
769
860
|
if (hookModelOverride) {
|
|
@@ -784,6 +875,15 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
784
875
|
`Ensure the model is defined in models.json and has auth configured.`, "warning");
|
|
785
876
|
}
|
|
786
877
|
}
|
|
878
|
+
// Store the final dispatched model ID so the dashboard can read it (#2899).
|
|
879
|
+
// This accounts for hook model overrides applied after selectAndApplyModel.
|
|
880
|
+
s.currentDispatchedModelId = s.currentUnitModel
|
|
881
|
+
? `${s.currentUnitModel.provider ?? ""}/${s.currentUnitModel.id ?? ""}`
|
|
882
|
+
: null;
|
|
883
|
+
// Progress widget + preconditions — deferred to after model selection so the
|
|
884
|
+
// widget's first render tick shows the correct model (#2899).
|
|
885
|
+
deps.updateProgressWidget(ctx, unitType, unitId, state);
|
|
886
|
+
deps.ensurePreconditions(unitType, unitId, s.basePath, state);
|
|
787
887
|
// Start unit supervision
|
|
788
888
|
deps.clearUnitTimeout();
|
|
789
889
|
deps.startUnitSupervision({
|
|
@@ -860,11 +960,13 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
860
960
|
if (s.currentUnit) {
|
|
861
961
|
await deps.closeoutUnit(ctx, s.basePath, unitType, unitId, s.currentUnit.startedAt, deps.buildSnapshotOpts(unitType, unitId));
|
|
862
962
|
}
|
|
863
|
-
// ── Zero tool-call guard (#1833)
|
|
864
|
-
//
|
|
865
|
-
//
|
|
866
|
-
// the
|
|
867
|
-
|
|
963
|
+
// ── Zero tool-call guard (#1833, #2653) ──────────────────────────
|
|
964
|
+
// Any unit that completes with 0 tool calls made no real progress —
|
|
965
|
+
// likely context exhaustion where all tool calls errored out. Treat
|
|
966
|
+
// as failed so the unit is retried in a fresh context instead of
|
|
967
|
+
// silently passing through to artifact verification (which loops
|
|
968
|
+
// forever when the unit never produced its artifact).
|
|
969
|
+
{
|
|
868
970
|
const currentLedger = deps.getLedger();
|
|
869
971
|
if (currentLedger?.units) {
|
|
870
972
|
const lastUnit = [...currentLedger.units].reverse().find((u) => u.type === unitType && u.id === unitId && u.startedAt === s.currentUnit?.startedAt);
|
|
@@ -873,11 +975,11 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
873
975
|
phase: "zero-tool-calls",
|
|
874
976
|
unitType,
|
|
875
977
|
unitId,
|
|
876
|
-
warning: "
|
|
978
|
+
warning: "Unit completed with 0 tool calls — likely context exhaustion, marking as failed",
|
|
877
979
|
});
|
|
878
|
-
ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls —
|
|
980
|
+
ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, will retry`, "warning");
|
|
879
981
|
// Fall through to next iteration where dispatch will re-derive
|
|
880
|
-
// and re-dispatch this
|
|
982
|
+
// and re-dispatch this unit.
|
|
881
983
|
return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
|
|
882
984
|
}
|
|
883
985
|
}
|
|
@@ -912,6 +1014,25 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
912
1014
|
}
|
|
913
1015
|
}
|
|
914
1016
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified, ...(unitResult.errorContext ? { errorContext: unitResult.errorContext } : {}) }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
|
|
1017
|
+
// ── Safety harness: checkpoint cleanup or rollback ──
|
|
1018
|
+
if (s.checkpointSha) {
|
|
1019
|
+
if (unitResult.status === "error" && safetyConfig.auto_rollback) {
|
|
1020
|
+
const rolled = rollbackToCheckpoint(s.basePath, unitId, s.checkpointSha);
|
|
1021
|
+
if (rolled) {
|
|
1022
|
+
ctx.ui.notify(`Rolled back to pre-unit checkpoint for ${unitId}`, "info");
|
|
1023
|
+
debugLog("runUnitPhase", { phase: "checkpoint-rollback", unitId });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
else if (unitResult.status === "error") {
|
|
1027
|
+
ctx.ui.notify(`Unit ${unitId} failed. Pre-unit checkpoint available at ${s.checkpointSha.slice(0, 8)}`, "warning");
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
// Success — clean up checkpoint ref
|
|
1031
|
+
cleanupCheckpoint(s.basePath, unitId);
|
|
1032
|
+
debugLog("runUnitPhase", { phase: "checkpoint-cleaned", unitId });
|
|
1033
|
+
}
|
|
1034
|
+
s.checkpointSha = null;
|
|
1035
|
+
}
|
|
915
1036
|
return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
|
|
916
1037
|
}
|
|
917
1038
|
// ─── runFinalize ──────────────────────────────────────────────────────────────
|
|
@@ -993,7 +1114,21 @@ export async function runFinalize(ic, iterData, sidecarItem) {
|
|
|
993
1114
|
}
|
|
994
1115
|
}
|
|
995
1116
|
// Post-verification processing (DB dual-write, hooks, triage, quick-tasks)
|
|
996
|
-
|
|
1117
|
+
// Timeout guard: if postUnitPostVerification hangs (e.g., module import
|
|
1118
|
+
// deadlock, SQLite transaction hang), force-continue after timeout so the
|
|
1119
|
+
// auto-loop is not permanently frozen (#2344).
|
|
1120
|
+
const postResultGuard = await withTimeout(deps.postUnitPostVerification(postUnitCtx), FINALIZE_POST_TIMEOUT_MS, "postUnitPostVerification");
|
|
1121
|
+
if (postResultGuard.timedOut) {
|
|
1122
|
+
debugLog("autoLoop", {
|
|
1123
|
+
phase: "post-verification-timeout",
|
|
1124
|
+
iteration: ic.iteration,
|
|
1125
|
+
unitType: iterData.unitType,
|
|
1126
|
+
unitId: iterData.unitId,
|
|
1127
|
+
});
|
|
1128
|
+
ctx.ui.notify(`postUnitPostVerification timed out after ${FINALIZE_POST_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} — continuing to next iteration`, "warning");
|
|
1129
|
+
return { action: "next", data: undefined };
|
|
1130
|
+
}
|
|
1131
|
+
const postResult = postResultGuard.value;
|
|
997
1132
|
if (postResult === "stopped") {
|
|
998
1133
|
debugLog("autoLoop", {
|
|
999
1134
|
phase: "exit",
|
|
@@ -50,6 +50,8 @@ export class AutoSession {
|
|
|
50
50
|
// ── Model state ──────────────────────────────────────────────────────────
|
|
51
51
|
autoModeStartModel = null;
|
|
52
52
|
currentUnitModel = null;
|
|
53
|
+
/** Fully-qualified model ID (provider/id) set after selectAndApplyModel + hook overrides (#2899). */
|
|
54
|
+
currentDispatchedModelId = null;
|
|
53
55
|
originalModelId = null;
|
|
54
56
|
originalModelProvider = null;
|
|
55
57
|
lastBudgetAlertLevel = 0;
|
|
@@ -62,6 +64,10 @@ export class AutoSession {
|
|
|
62
64
|
lastStateRebuildAt = 0;
|
|
63
65
|
// ── Sidecar queue ─────────────────────────────────────────────────────
|
|
64
66
|
sidecarQueue = [];
|
|
67
|
+
// ── Tool invocation errors (#2883) ──────────────────────────────────
|
|
68
|
+
/** Set when a GSD tool execution ends with isError due to malformed/truncated
|
|
69
|
+
* JSON arguments. Checked by postUnitPreVerification to break retry loops. */
|
|
70
|
+
lastToolInvocationError = null;
|
|
65
71
|
// ── Isolation degradation ────────────────────────────────────────────
|
|
66
72
|
/** Set to true when worktree creation fails; prevents merge of nonexistent branch. */
|
|
67
73
|
isolationDegraded = false;
|
|
@@ -76,6 +82,9 @@ export class AutoSession {
|
|
|
76
82
|
lastPromptCharCount;
|
|
77
83
|
lastBaselineCharCount;
|
|
78
84
|
pendingQuickTasks = [];
|
|
85
|
+
// ── Safety harness ───────────────────────────────────────────────────────
|
|
86
|
+
/** SHA of the pre-unit git checkpoint ref. Cleared on success or rollback. */
|
|
87
|
+
checkpointSha = null;
|
|
79
88
|
// ── Signal handler ───────────────────────────────────────────────────────
|
|
80
89
|
sigtermHandler = null;
|
|
81
90
|
// ── Loop promise state ──────────────────────────────────────────────────
|
|
@@ -132,6 +141,7 @@ export class AutoSession {
|
|
|
132
141
|
// Model
|
|
133
142
|
this.autoModeStartModel = null;
|
|
134
143
|
this.currentUnitModel = null;
|
|
144
|
+
this.currentDispatchedModelId = null;
|
|
135
145
|
this.originalModelId = null;
|
|
136
146
|
this.originalModelProvider = null;
|
|
137
147
|
this.lastBudgetAlertLevel = 0;
|
|
@@ -149,8 +159,10 @@ export class AutoSession {
|
|
|
149
159
|
this.pendingQuickTasks = [];
|
|
150
160
|
this.sidecarQueue = [];
|
|
151
161
|
this.rewriteAttemptCount = 0;
|
|
162
|
+
this.lastToolInvocationError = null;
|
|
152
163
|
this.isolationDegraded = false;
|
|
153
164
|
this.milestoneMergedInPhases = false;
|
|
165
|
+
this.checkpointSha = null;
|
|
154
166
|
// Signal handler
|
|
155
167
|
this.sigtermHandler = null;
|
|
156
168
|
// Loop promise state lives in auto-loop.ts module scope
|
|
@@ -516,9 +516,15 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
516
516
|
const cxWindow = cxUsage?.contextWindow ?? cmdCtx?.model?.contextWindow ?? 0;
|
|
517
517
|
const cxPctVal = cxUsage?.percent ?? 0;
|
|
518
518
|
const cxPct = cxUsage?.percent !== null ? cxPctVal.toFixed(1) : "?";
|
|
519
|
-
// Model display —
|
|
520
|
-
|
|
521
|
-
const
|
|
519
|
+
// Model display — prefer dispatched model ID (set after selectAndApplyModel
|
|
520
|
+
// + hook overrides) over cmdCtx?.model which can be stale (#2899).
|
|
521
|
+
const dispatchedModelId = accessors.getCurrentDispatchedModelId();
|
|
522
|
+
const modelId = dispatchedModelId
|
|
523
|
+
? dispatchedModelId.split("/").slice(1).join("/") || dispatchedModelId
|
|
524
|
+
: (cmdCtx?.model?.id ?? "");
|
|
525
|
+
const modelProvider = dispatchedModelId
|
|
526
|
+
? dispatchedModelId.split("/")[0] || ""
|
|
527
|
+
: (cmdCtx?.model?.provider ?? "");
|
|
522
528
|
const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
|
|
523
529
|
const modelDisplay = (modelProvider && modelId
|
|
524
530
|
? `${modelProvider}/${modelId}`
|
|
@@ -15,6 +15,9 @@ export function resolvePreferredModelConfig(unitType, autoModeStartModel) {
|
|
|
15
15
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
16
16
|
if (!routingConfig.enabled || !routingConfig.tier_models)
|
|
17
17
|
return undefined;
|
|
18
|
+
// Don't synthesize a routing config for flat-rate providers (#3453).
|
|
19
|
+
if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
|
|
20
|
+
return undefined;
|
|
18
21
|
const ceilingModel = routingConfig.tier_models.heavy
|
|
19
22
|
?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
|
|
20
23
|
if (!ceilingModel)
|
|
@@ -41,6 +44,25 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
|
|
|
41
44
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
42
45
|
let effectiveModelConfig = modelConfig;
|
|
43
46
|
let routingTierLabel = "";
|
|
47
|
+
// Disable routing for flat-rate providers like GitHub Copilot (#3453).
|
|
48
|
+
// All models cost the same per request, so downgrading to a cheaper
|
|
49
|
+
// model provides no cost benefit — it only degrades quality.
|
|
50
|
+
// Fail-closed: if primary model can't be resolved, fall back to
|
|
51
|
+
// provider-level signals rather than allowing unwanted downgrades.
|
|
52
|
+
if (routingConfig.enabled) {
|
|
53
|
+
const primaryModel = resolveModelId(modelConfig.primary, availableModels, ctx.model?.provider);
|
|
54
|
+
if (primaryModel) {
|
|
55
|
+
if (isFlatRateProvider(primaryModel.provider)) {
|
|
56
|
+
routingConfig.enabled = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if ((autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
|
|
60
|
+
|| (ctx.model?.provider && isFlatRateProvider(ctx.model.provider))) {
|
|
61
|
+
// Primary model unresolvable but provider signals indicate flat-rate —
|
|
62
|
+
// disable routing to prevent quality degradation.
|
|
63
|
+
routingConfig.enabled = false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
44
66
|
if (routingConfig.enabled) {
|
|
45
67
|
let budgetPct;
|
|
46
68
|
if (routingConfig.budget_pressure !== false) {
|
|
@@ -244,3 +266,13 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
|
|
|
244
266
|
// Fall back to first non-extension candidate, or any candidate
|
|
245
267
|
return candidates.find(m => !EXTENSION_PROVIDERS.has(m.provider)) ?? candidates[0];
|
|
246
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Flat-rate providers charge the same per request regardless of model.
|
|
271
|
+
* Dynamic routing provides no cost benefit — it only degrades quality (#3453).
|
|
272
|
+
* Uses case-insensitive matching with alias support to prevent fail-open on
|
|
273
|
+
* provider naming variations (e.g. "copilot" vs "github-copilot").
|
|
274
|
+
*/
|
|
275
|
+
const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot"]);
|
|
276
|
+
export function isFlatRateProvider(provider) {
|
|
277
|
+
return FLAT_RATE_PROVIDERS.has(provider.toLowerCase());
|
|
278
|
+
}
|
|
@@ -19,7 +19,7 @@ import { invalidateAllCaches } from "./cache.js";
|
|
|
19
19
|
import { parseUnitId } from "./unit-id.js";
|
|
20
20
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
21
21
|
import { autoCommitCurrentBranch, } from "./worktree.js";
|
|
22
|
-
import { verifyExpectedArtifact, resolveExpectedArtifactPath, diagnoseExpectedArtifact, } from "./auto-recovery.js";
|
|
22
|
+
import { verifyExpectedArtifact, resolveExpectedArtifactPath, writeBlockerPlaceholder, diagnoseExpectedArtifact, } from "./auto-recovery.js";
|
|
23
23
|
import { regenerateIfMissing } from "./workflow-projections.js";
|
|
24
24
|
import { syncStateToProjectRoot } from "./auto-worktree.js";
|
|
25
25
|
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
|
|
@@ -29,6 +29,15 @@ import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookSta
|
|
|
29
29
|
import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
|
|
30
30
|
import { debugLog } from "./debug-logger.js";
|
|
31
31
|
import { runSafely } from "./auto-utils.js";
|
|
32
|
+
import { getEvidence } from "./safety/evidence-collector.js";
|
|
33
|
+
import { validateFileChanges } from "./safety/file-change-validator.js";
|
|
34
|
+
// crossReferenceEvidence available for future use when verification_evidence is stored in DB
|
|
35
|
+
// import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
|
|
36
|
+
import { validateContent } from "./safety/content-validator.js";
|
|
37
|
+
import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
|
|
38
|
+
import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
|
|
39
|
+
/** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
|
|
40
|
+
const MAX_VERIFICATION_RETRIES = 3;
|
|
32
41
|
/** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
|
|
33
42
|
* drain via runUnit. Logs the enqueue event and notifies the UI. */
|
|
34
43
|
function enqueueSidecar(s, ctx, entry, debugExtra, notification) {
|
|
@@ -339,6 +348,78 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
339
348
|
catch (e) {
|
|
340
349
|
debugLog("postUnit", { phase: "rogue-detection", error: String(e) });
|
|
341
350
|
}
|
|
351
|
+
// ── Safety harness: post-unit validation ──
|
|
352
|
+
try {
|
|
353
|
+
const { loadEffectiveGSDPreferences } = await import("./preferences.js");
|
|
354
|
+
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
355
|
+
const safetyConfig = resolveSafetyHarnessConfig(prefs?.safety_harness);
|
|
356
|
+
if (safetyConfig.enabled) {
|
|
357
|
+
const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
|
|
358
|
+
// File change validation (execute-task only, after auto-commit)
|
|
359
|
+
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
|
|
360
|
+
try {
|
|
361
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
362
|
+
if (taskRow) {
|
|
363
|
+
const expectedOutput = taskRow.expected_output ?? [];
|
|
364
|
+
const plannedFiles = taskRow.files ?? [];
|
|
365
|
+
const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles);
|
|
366
|
+
if (audit && audit.violations.length > 0) {
|
|
367
|
+
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
368
|
+
for (const v of warnings) {
|
|
369
|
+
logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
|
|
370
|
+
}
|
|
371
|
+
if (warnings.length > 0) {
|
|
372
|
+
ctx.ui.notify(`Safety: ${warnings.length} unexpected file change(s) outside task plan`, "warning");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (e) {
|
|
378
|
+
debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Evidence cross-reference (execute-task only)
|
|
382
|
+
// Verification evidence is passed via the complete-task tool call and
|
|
383
|
+
// stored in the SUMMARY.md on disk — not available as structured data
|
|
384
|
+
// in the DB. The evidence collector tracks actual bash tool calls, so
|
|
385
|
+
// we can still detect units that claimed success but ran no commands.
|
|
386
|
+
if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
|
|
387
|
+
try {
|
|
388
|
+
const actual = getEvidence();
|
|
389
|
+
const bashCalls = actual.filter(e => e.kind === "bash");
|
|
390
|
+
// If the task is marked complete but zero bash commands were run,
|
|
391
|
+
// it's suspicious — the LLM may have fabricated results.
|
|
392
|
+
if (sMid && sSid && sTid && isDbAvailable()) {
|
|
393
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
394
|
+
if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
|
|
395
|
+
logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
|
|
396
|
+
ctx.ui.notify(`Safety: task ${sTid} has verification commands but no bash calls were recorded`, "warning");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch (e) {
|
|
401
|
+
debugLog("postUnit", { phase: "safety-evidence-xref", error: String(e) });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Content validation (plan-slice, plan-milestone)
|
|
405
|
+
if (safetyConfig.content_validation) {
|
|
406
|
+
try {
|
|
407
|
+
const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
|
|
408
|
+
const contentViolations = validateContent(s.currentUnit.type, artifactPath);
|
|
409
|
+
for (const v of contentViolations) {
|
|
410
|
+
logWarning("safety", `content: ${v.reason}`);
|
|
411
|
+
ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch (e) {
|
|
415
|
+
debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
catch (e) {
|
|
421
|
+
debugLog("postUnit", { phase: "safety-harness", error: String(e) });
|
|
422
|
+
}
|
|
342
423
|
// Artifact verification
|
|
343
424
|
let triggerArtifactVerified = false;
|
|
344
425
|
if (!s.currentUnit.type.startsWith("hook/")) {
|
|
@@ -374,6 +455,8 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
374
455
|
// When artifact verification fails for a unit type that has a known expected
|
|
375
456
|
// artifact, return "retry" so the caller re-dispatches with failure context
|
|
376
457
|
// instead of blindly re-dispatching the same unit (#1571).
|
|
458
|
+
// After MAX_VERIFICATION_RETRIES, escalate to writeBlockerPlaceholder so the
|
|
459
|
+
// pipeline can advance instead of looping forever (#2653).
|
|
377
460
|
//
|
|
378
461
|
// HOWEVER, if the DB is unavailable (db_unavailable), the artifact was never
|
|
379
462
|
// written because the completion tool failed at the infra level. Retrying
|
|
@@ -387,20 +470,51 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
387
470
|
ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — DB unavailable, skipping retry.${dbSkipDiag ? ` Expected: ${dbSkipDiag}` : ""}`, "error");
|
|
388
471
|
}
|
|
389
472
|
else if (!triggerArtifactVerified) {
|
|
473
|
+
// #2883: If the artifact is missing because the tool invocation itself
|
|
474
|
+
// failed (malformed/truncated JSON arguments), retrying will produce the
|
|
475
|
+
// same failure. Pause auto-mode instead of entering a stuck retry loop.
|
|
476
|
+
if (s.lastToolInvocationError) {
|
|
477
|
+
const errMsg = `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
|
|
478
|
+
debugLog("postUnit", { phase: "tool-invocation-error-pause", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
|
|
479
|
+
ctx.ui.notify(errMsg, "error");
|
|
480
|
+
s.lastToolInvocationError = null;
|
|
481
|
+
await pauseAuto(ctx, pi);
|
|
482
|
+
return "dispatched";
|
|
483
|
+
}
|
|
390
484
|
const hasExpectedArtifact = resolveExpectedArtifactPath(s.currentUnit.type, s.currentUnit.id, s.basePath) !== null;
|
|
391
485
|
if (hasExpectedArtifact) {
|
|
392
486
|
const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
|
|
393
487
|
const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
|
|
394
488
|
s.verificationRetryCount.set(retryKey, attempt);
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
489
|
+
if (attempt > MAX_VERIFICATION_RETRIES) {
|
|
490
|
+
// Retries exhausted — write a blocker placeholder so the pipeline
|
|
491
|
+
// can advance past this stuck unit (#2653).
|
|
492
|
+
debugLog("postUnit", {
|
|
493
|
+
phase: "artifact-verify-escalate",
|
|
494
|
+
unitType: s.currentUnit.type,
|
|
495
|
+
unitId: s.currentUnit.id,
|
|
496
|
+
attempt,
|
|
497
|
+
maxRetries: MAX_VERIFICATION_RETRIES,
|
|
498
|
+
});
|
|
499
|
+
const reason = `Artifact verification failed after ${MAX_VERIFICATION_RETRIES} retries for ${s.currentUnit.type} "${s.currentUnit.id}".`;
|
|
500
|
+
writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
|
|
501
|
+
ctx.ui.notify(`${s.currentUnit.type} ${s.currentUnit.id} — verification retries exhausted (${MAX_VERIFICATION_RETRIES}), wrote blocker placeholder to advance pipeline`, "warning");
|
|
502
|
+
// Reset retry count and fall through to "continue" so the loop
|
|
503
|
+
// re-derives state with the placeholder in place.
|
|
504
|
+
s.verificationRetryCount.delete(retryKey);
|
|
505
|
+
s.pendingVerificationRetry = null;
|
|
506
|
+
// Do NOT return "retry" — fall through to "continue" below.
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
s.pendingVerificationRetry = {
|
|
510
|
+
unitId: s.currentUnit.id,
|
|
511
|
+
failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
|
|
512
|
+
attempt,
|
|
513
|
+
};
|
|
514
|
+
debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
|
|
515
|
+
ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt})`, "warning");
|
|
516
|
+
return "retry";
|
|
517
|
+
}
|
|
404
518
|
}
|
|
405
519
|
}
|
|
406
520
|
}
|
|
@@ -880,11 +880,16 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
880
880
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
881
881
|
const milestoneResearchPath = resolveMilestoneFile(base, mid, "RESEARCH");
|
|
882
882
|
const milestoneResearchRel = relMilestoneFile(base, mid, "RESEARCH");
|
|
883
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
884
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
883
885
|
const inlined = [];
|
|
884
886
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
885
887
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
886
888
|
if (contextInline)
|
|
887
889
|
inlined.push(contextInline);
|
|
890
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
891
|
+
if (sliceCtxInline)
|
|
892
|
+
inlined.push(sliceCtxInline);
|
|
888
893
|
const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
|
|
889
894
|
if (researchInline)
|
|
890
895
|
inlined.push(researchInline);
|
|
@@ -931,12 +936,17 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
|
|
|
931
936
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
932
937
|
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
|
|
933
938
|
const researchRel = relSliceFile(base, mid, sid, "RESEARCH");
|
|
939
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
940
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
934
941
|
const inlined = [];
|
|
935
942
|
// Inject phase handoff anchor from research phase (if available)
|
|
936
943
|
const researchSliceAnchor = readPhaseAnchor(base, mid, "research-slice");
|
|
937
944
|
if (researchSliceAnchor)
|
|
938
945
|
inlined.push(formatAnchorForPrompt(researchSliceAnchor));
|
|
939
946
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
947
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
948
|
+
if (sliceCtxInline)
|
|
949
|
+
inlined.push(sliceCtxInline);
|
|
940
950
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
941
951
|
if (researchInline)
|
|
942
952
|
inlined.push(researchInline);
|
|
@@ -1097,8 +1107,13 @@ export async function buildCompleteSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
1097
1107
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1098
1108
|
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
|
|
1099
1109
|
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
|
|
1110
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
1111
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1100
1112
|
const inlined = [];
|
|
1101
1113
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1114
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1115
|
+
if (sliceCtxInline)
|
|
1116
|
+
inlined.push(sliceCtxInline);
|
|
1102
1117
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
|
|
1103
1118
|
if (inlineLevel !== "minimal") {
|
|
1104
1119
|
const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
|
|
@@ -1351,8 +1366,13 @@ export async function buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base) {
|
|
|
1351
1366
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1352
1367
|
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
|
|
1353
1368
|
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
|
|
1369
|
+
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
1370
|
+
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1354
1371
|
const inlined = [];
|
|
1355
1372
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1373
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1374
|
+
if (sliceCtxInline)
|
|
1375
|
+
inlined.push(sliceCtxInline);
|
|
1356
1376
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Current Slice Plan"));
|
|
1357
1377
|
// Find the blocker task summary — the completed task with blocker_discovered: true
|
|
1358
1378
|
let blockerTaskId = "";
|
|
@@ -1454,8 +1474,13 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
1454
1474
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1455
1475
|
const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
1456
1476
|
const summaryRel = relSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
1477
|
+
const sliceContextPath = resolveSliceFile(base, mid, completedSliceId, "CONTEXT");
|
|
1478
|
+
const sliceContextRel = relSliceFile(base, mid, completedSliceId, "CONTEXT");
|
|
1457
1479
|
const inlined = [];
|
|
1458
1480
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
|
|
1481
|
+
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1482
|
+
if (sliceCtxInline)
|
|
1483
|
+
inlined.push(sliceCtxInline);
|
|
1459
1484
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
|
|
1460
1485
|
if (inlineLevel !== "minimal") {
|
|
1461
1486
|
const projectInline = await inlineProjectFromDb(base);
|