@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless-recover.js +56 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
- package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
- package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/dist/resources/extensions/browser-tools/index.js +68 -24
- package/dist/resources/extensions/browser-tools/state.js +12 -0
- package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
- package/dist/resources/extensions/browser-tools/utils.js +3 -3
- package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +87 -12
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
- package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-timers.js +24 -10
- package/dist/resources/extensions/gsd/auto.js +40 -15
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
- package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
- package/dist/resources/extensions/gsd/config-overlay.js +1 -0
- package/dist/resources/extensions/gsd/context-masker.js +129 -5
- package/dist/resources/extensions/gsd/db-writer.js +35 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/dist/resources/extensions/gsd/gsd-db.js +480 -172
- package/dist/resources/extensions/gsd/guided-flow.js +4 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
- package/dist/resources/extensions/gsd/md-importer.js +38 -3
- package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
- package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
- package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
- package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
- package/dist/resources/extensions/gsd/preferences-models.js +111 -43
- package/dist/resources/extensions/gsd/preferences-types.js +13 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
- package/dist/resources/extensions/gsd/preferences.js +4 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
- package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
- package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
- package/dist/resources/extensions/gsd/source-observations.js +306 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
- package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
- package/dist/resources/extensions/gsd/state.js +7 -3
- package/dist/resources/extensions/gsd/tool-contract.js +15 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
- package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
- package/dist/resources/extensions/gsd/uat-policy.js +130 -0
- package/dist/resources/extensions/gsd/uat-run.js +414 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
- package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
- package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
- package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
- package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
- package/dist/resources/extensions/subagent/agents.js +1 -0
- package/dist/resources/extensions/subagent/index.js +27 -12
- package/dist/resources/extensions/subagent/launch.js +7 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +4 -4
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/dist/native.js +22 -0
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +30 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +174 -29
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +178 -54
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/utils.d.ts +11 -0
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +119 -6
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/themes.js +1 -1
- package/pkg/dist/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install/handoff.js +16 -3
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
- package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
- package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/src/resources/extensions/browser-tools/index.ts +75 -27
- package/src/resources/extensions/browser-tools/state.ts +13 -0
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
- package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
- package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
- package/src/resources/extensions/browser-tools/utils.ts +3 -3
- package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +89 -15
- package/src/resources/extensions/gsd/auto/session.ts +24 -1
- package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
- package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
- package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
- package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-timers.ts +25 -9
- package/src/resources/extensions/gsd/auto.ts +41 -14
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
- package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
- package/src/resources/extensions/gsd/config-overlay.ts +1 -0
- package/src/resources/extensions/gsd/context-masker.ts +152 -5
- package/src/resources/extensions/gsd/db-writer.ts +38 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/src/resources/extensions/gsd/gsd-db.ts +564 -186
- package/src/resources/extensions/gsd/guided-flow.ts +4 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
- package/src/resources/extensions/gsd/md-importer.ts +49 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
- package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
- package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
- package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
- package/src/resources/extensions/gsd/preferences-models.ts +113 -43
- package/src/resources/extensions/gsd/preferences-types.ts +47 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
- package/src/resources/extensions/gsd/preferences.ts +5 -0
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
- package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
- package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
- package/src/resources/extensions/gsd/source-observations.ts +402 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
- package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
- package/src/resources/extensions/gsd/state.ts +7 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
- package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
- package/src/resources/extensions/gsd/tool-contract.ts +29 -1
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
- package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/uat-policy.ts +191 -0
- package/src/resources/extensions/gsd/uat-run.ts +550 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
- package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
- package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
- package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
- package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
- package/src/resources/extensions/subagent/agents.ts +4 -0
- package/src/resources/extensions/subagent/index.ts +28 -3
- package/src/resources/extensions/subagent/launch.ts +8 -0
- package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
|
@@ -61,6 +61,18 @@ function isRepairableStaleRenderReason(reason: string): boolean {
|
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function canonicalizeMilestoneId(dirSegment: string): string {
|
|
65
|
+
if (getMilestone(dirSegment)) return dirSegment;
|
|
66
|
+
const suffixId = dirSegment.match(/^(M\d+-[a-z0-9]{6})(?:$|-)/)?.[1];
|
|
67
|
+
if (suffixId && getMilestone(suffixId)) return suffixId;
|
|
68
|
+
|
|
69
|
+
// Descriptor layout: e.g. M001-DESCRIPTOR → M001
|
|
70
|
+
const baseId = dirSegment.match(/^(M\d+)(?:$|-)/i)?.[1];
|
|
71
|
+
if (baseId && getMilestone(baseId)) return baseId;
|
|
72
|
+
|
|
73
|
+
return suffixId ?? baseId ?? dirSegment;
|
|
74
|
+
}
|
|
75
|
+
|
|
64
76
|
function resolveRoadmapMilestoneIdFromPath(normPath: string): string {
|
|
65
77
|
const milestoneMatch = normPath.match(/milestones\/([^/]+)\//);
|
|
66
78
|
if (!milestoneMatch) {
|
|
@@ -106,7 +118,19 @@ async function repairStaleRenderFromBasePath(
|
|
|
106
118
|
`stale-render drift: plan path missing milestone/slice segments: ${record.renderPath}`,
|
|
107
119
|
);
|
|
108
120
|
}
|
|
109
|
-
|
|
121
|
+
const milestoneId = canonicalizeMilestoneId(pathMatch[1]);
|
|
122
|
+
const wrote = await renderPlanCheckboxes(
|
|
123
|
+
basePath,
|
|
124
|
+
milestoneId,
|
|
125
|
+
pathMatch[2],
|
|
126
|
+
record.renderPath,
|
|
127
|
+
);
|
|
128
|
+
if (!wrote) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`stale-render drift: plan re-render wrote nothing for ${milestoneId}/${pathMatch[2]} ` +
|
|
131
|
+
`(${record.renderPath}); slice has no tasks or its path is unresolvable`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
110
134
|
return;
|
|
111
135
|
}
|
|
112
136
|
|
|
@@ -120,7 +144,15 @@ async function repairStaleRenderFromBasePath(
|
|
|
120
144
|
`stale-render drift: task summary path/reason malformed: ${record.renderPath} reason=${reason}`,
|
|
121
145
|
);
|
|
122
146
|
}
|
|
123
|
-
|
|
147
|
+
const milestoneId = canonicalizeMilestoneId(pathMatch[1]);
|
|
148
|
+
const wrote = await renderTaskSummary(basePath, milestoneId, pathMatch[2], taskMatch[1]);
|
|
149
|
+
if (!wrote) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`stale-render drift: task summary re-render wrote nothing for ` +
|
|
152
|
+
`${milestoneId}/${pathMatch[2]}/${taskMatch[1]} (${record.renderPath}); ` +
|
|
153
|
+
`task has no summary in DB or its slice path is unresolvable`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
124
156
|
return;
|
|
125
157
|
}
|
|
126
158
|
|
|
@@ -131,7 +163,7 @@ async function repairStaleRenderFromBasePath(
|
|
|
131
163
|
`stale-render drift: slice summary path missing milestone/slice segments: ${record.renderPath}`,
|
|
132
164
|
);
|
|
133
165
|
}
|
|
134
|
-
const milestoneId = pathMatch[1];
|
|
166
|
+
const milestoneId = canonicalizeMilestoneId(pathMatch[1]);
|
|
135
167
|
const sliceId = pathMatch[2];
|
|
136
168
|
const slice = getSlice(milestoneId, sliceId);
|
|
137
169
|
const uatPath = join(dirname(record.renderPath), buildSliceFileName(sliceId, "UAT"));
|
|
@@ -139,7 +171,14 @@ async function repairStaleRenderFromBasePath(
|
|
|
139
171
|
if (slice?.full_uat_md && !existsSync(uatPath)) {
|
|
140
172
|
setSliceSummaryMd(milestoneId, sliceId, slice.full_summary_md ?? "", "");
|
|
141
173
|
}
|
|
142
|
-
await renderSliceSummary(basePath, milestoneId, sliceId);
|
|
174
|
+
const wrote = await renderSliceSummary(basePath, milestoneId, sliceId);
|
|
175
|
+
if (!wrote) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`stale-render drift: slice summary re-render wrote nothing for ` +
|
|
178
|
+
`${milestoneId}/${sliceId} (${record.renderPath}); slice has no summary/UAT ` +
|
|
179
|
+
`in DB or its path is unresolvable`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
143
182
|
return;
|
|
144
183
|
}
|
|
145
184
|
|
|
@@ -152,7 +191,7 @@ async function repairStaleRenderFromBasePath(
|
|
|
152
191
|
}
|
|
153
192
|
// When UAT.md is removed from disk, mirror that intent by clearing stale
|
|
154
193
|
// persisted UAT content instead of rehydrating it back onto disk.
|
|
155
|
-
const milestoneId = pathMatch[1];
|
|
194
|
+
const milestoneId = canonicalizeMilestoneId(pathMatch[1]);
|
|
156
195
|
const sliceId = pathMatch[2];
|
|
157
196
|
const slice = getSlice(milestoneId, sliceId);
|
|
158
197
|
if (!slice) {
|
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
readSessionLockData,
|
|
11
11
|
removeStaleSessionLock,
|
|
12
12
|
} from "../../session-lock.js";
|
|
13
|
+
import { clearStaleWorkerLock } from "../../crash-recovery.js";
|
|
14
|
+
import { findStaleWorkerForProject } from "../../db/auto-workers.js";
|
|
15
|
+
import { isDbAvailable } from "../../gsd-db.js";
|
|
16
|
+
import { normalizeRealPath } from "../../paths.js";
|
|
17
|
+
import { logWarning } from "../../workflow-logger.js";
|
|
13
18
|
import type { GSDState } from "../../types.js";
|
|
14
19
|
import type { DriftContext, DriftHandler, DriftRecord } from "../types.js";
|
|
15
20
|
|
|
@@ -20,23 +25,46 @@ export function detectStaleWorkerDrift(
|
|
|
20
25
|
ctx: DriftContext,
|
|
21
26
|
): StaleWorkerDrift[] {
|
|
22
27
|
const data = readSessionLockData(ctx.basePath);
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
if (data && typeof data.pid === "number") {
|
|
29
|
+
return isSessionLockProcessAlive(data)
|
|
30
|
+
? []
|
|
31
|
+
: [{ kind: "stale-worker", lockPath: effectiveLockFile(), pid: data.pid }];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The lock file is missing or unparseable. It is not the only source of
|
|
35
|
+
// truth: a crashed worker can leave a workers row 'active' with held leases
|
|
36
|
+
// and in-flight dispatches even when its lock file is gone. Fall back to the
|
|
37
|
+
// DB worker registry so that state is still detected and repaired.
|
|
38
|
+
if (isDbAvailable()) {
|
|
39
|
+
try {
|
|
40
|
+
const stale = findStaleWorkerForProject(normalizeRealPath(ctx.basePath));
|
|
41
|
+
if (stale && typeof stale.pid === "number") {
|
|
42
|
+
return [{ kind: "stale-worker", lockPath: effectiveLockFile(), pid: stale.pid }];
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
// Best-effort: detection must never throw and abort the reconcile cycle.
|
|
46
|
+
logWarning(
|
|
47
|
+
"reconcile",
|
|
48
|
+
`stale-worker DB fallback detection failed: ${(err as Error).message}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return [];
|
|
34
54
|
}
|
|
35
55
|
|
|
36
56
|
export function repairStaleWorker(_record: StaleWorkerDrift, ctx: DriftContext): void {
|
|
37
57
|
// removeStaleSessionLock is idempotent: it re-reads lock state and is a
|
|
38
58
|
// no-op when the lock is held by an alive process. Safe under cap=2 retry.
|
|
39
59
|
removeStaleSessionLock(ctx.basePath);
|
|
60
|
+
|
|
61
|
+
// Removing the lock file alone leaves the DB-side worker state dangling: the
|
|
62
|
+
// dead worker's milestone_leases stay 'held' and its unit_dispatches stay
|
|
63
|
+
// 'running'/'claimed', blocking new claims until the lease TTL expires.
|
|
64
|
+
// clearStaleWorkerLock cancels those dispatches, releases the leases, and
|
|
65
|
+
// marks the worker stopping — the same cleanup the startup crash-recovery
|
|
66
|
+
// path performs. It is DB-gated, idempotent, and best-effort.
|
|
67
|
+
clearStaleWorkerLock(ctx.basePath);
|
|
40
68
|
}
|
|
41
69
|
|
|
42
70
|
export const staleWorkerHandler: DriftHandler<StaleWorkerDrift> = {
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
invalidateStateCache as defaultInvalidate,
|
|
8
8
|
} from "../state.js";
|
|
9
9
|
import { clearParseCache as defaultClearParseCache } from "../files.js";
|
|
10
|
+
import { clearPathCache } from "../paths.js";
|
|
11
|
+
import { logWarning } from "../workflow-logger.js";
|
|
10
12
|
import type { GSDState } from "../types.js";
|
|
11
13
|
|
|
12
14
|
import {
|
|
@@ -68,18 +70,24 @@ export async function reconcileBeforeDispatch(
|
|
|
68
70
|
const stateSnapshot = await deps.deriveState(basePath, deps.deriveStateOptions);
|
|
69
71
|
const ctx: DriftContext = { basePath, state: stateSnapshot };
|
|
70
72
|
|
|
71
|
-
const
|
|
73
|
+
const detection = await detectAllDrift(stateSnapshot, ctx, registry);
|
|
74
|
+
const drift = detection.records;
|
|
72
75
|
if (drift.length === 0) {
|
|
73
76
|
return {
|
|
74
77
|
ok: true,
|
|
75
78
|
stateSnapshot,
|
|
76
79
|
repaired,
|
|
77
|
-
blockers:
|
|
80
|
+
blockers: [
|
|
81
|
+
...new Set([
|
|
82
|
+
...(stateSnapshot.blockers ?? []),
|
|
83
|
+
...detection.detectBlockers,
|
|
84
|
+
]),
|
|
85
|
+
],
|
|
78
86
|
};
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
const failures: ReconciliationFailureDetail[] = [];
|
|
82
|
-
const blockers: string[] = [];
|
|
90
|
+
const blockers: string[] = [...detection.detectBlockers];
|
|
83
91
|
let repairedThisPass = false;
|
|
84
92
|
for (const record of drift) {
|
|
85
93
|
const handler = registry.find((h) => h.kind === record.kind);
|
|
@@ -107,7 +115,11 @@ export async function reconcileBeforeDispatch(
|
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
if (repairedThisPass) {
|
|
118
|
+
// A repair may have mutated on-disk structure (e.g. quarantined a slice
|
|
119
|
+
// dir). Clear both the parse cache and the path/dir cache centrally so
|
|
120
|
+
// later passes and any subsequent repair see fresh filesystem state.
|
|
110
121
|
clearParseCache();
|
|
122
|
+
clearPathCache();
|
|
111
123
|
}
|
|
112
124
|
if (blockers.length > 0) {
|
|
113
125
|
let blockerState = stateSnapshot;
|
|
@@ -132,10 +144,11 @@ export async function reconcileBeforeDispatch(
|
|
|
132
144
|
deps.invalidateStateCache();
|
|
133
145
|
const finalState = await deps.deriveState(basePath, deps.deriveStateOptions);
|
|
134
146
|
const finalCtx: DriftContext = { basePath, state: finalState };
|
|
135
|
-
const
|
|
147
|
+
const finalDetection = await detectAllDrift(finalState, finalCtx, registry);
|
|
148
|
+
const persistent = finalDetection.records;
|
|
136
149
|
|
|
137
150
|
if (persistent.length > 0) {
|
|
138
|
-
const blockers: string[] = [];
|
|
151
|
+
const blockers: string[] = [...finalDetection.detectBlockers];
|
|
139
152
|
const unblockedPersistent: DriftRecord[] = [];
|
|
140
153
|
for (const record of persistent) {
|
|
141
154
|
const handler = registry.find((h) => h.kind === record.kind);
|
|
@@ -161,28 +174,45 @@ export async function reconcileBeforeDispatch(
|
|
|
161
174
|
ok: true,
|
|
162
175
|
stateSnapshot: finalState,
|
|
163
176
|
repaired,
|
|
164
|
-
blockers:
|
|
177
|
+
blockers: [
|
|
178
|
+
...new Set([
|
|
179
|
+
...(finalState.blockers ?? []),
|
|
180
|
+
...finalDetection.detectBlockers,
|
|
181
|
+
]),
|
|
182
|
+
],
|
|
165
183
|
};
|
|
166
184
|
}
|
|
167
185
|
|
|
186
|
+
interface DetectionOutcome {
|
|
187
|
+
records: DriftRecord[];
|
|
188
|
+
/** One blocker string per handler whose detect() threw. */
|
|
189
|
+
detectBlockers: string[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Run every detector. A single detector throwing (e.g. a transient file read
|
|
194
|
+
* error) must NOT abort the whole cycle and hide every later handler's drift —
|
|
195
|
+
* it is collected as a blocker so dispatch is still gated, while the remaining
|
|
196
|
+
* detectors run and their drift gets repaired (graceful degradation, ADR-017).
|
|
197
|
+
*/
|
|
168
198
|
async function detectAllDrift(
|
|
169
199
|
state: GSDState,
|
|
170
200
|
ctx: DriftContext,
|
|
171
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
202
|
registry: ReadonlyArray<DriftHandler<any>>,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
203
|
+
): Promise<DetectionOutcome> {
|
|
204
|
+
const records: DriftRecord[] = [];
|
|
205
|
+
const detectBlockers: string[] = [];
|
|
176
206
|
for (const handler of registry) {
|
|
177
207
|
try {
|
|
178
208
|
const detected = await handler.detect(state, ctx);
|
|
179
|
-
|
|
209
|
+
records.push(...detected);
|
|
180
210
|
} catch (cause) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
211
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
212
|
+
const blocker = `Drift detection failed for "${handler.kind}": ${message}`;
|
|
213
|
+
logWarning("reconcile", blocker);
|
|
214
|
+
detectBlockers.push(blocker);
|
|
185
215
|
}
|
|
186
216
|
}
|
|
187
|
-
return
|
|
217
|
+
return { records, detectBlockers };
|
|
188
218
|
}
|
|
@@ -21,10 +21,10 @@ export interface SpawnGateDeps extends Partial<ReconciliationDeps> {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Run reconciliation before spawning workers. Returns ok=true when the run
|
|
25
|
-
* completed without throwing
|
|
26
|
-
*
|
|
27
|
-
* ReconciliationFailedError, returns ok=false with the error message so the
|
|
24
|
+
* Run reconciliation before spawning workers. Returns ok=true only when the run
|
|
25
|
+
* completed without throwing AND surfaced no blockers; any blocker fails the
|
|
26
|
+
* gate (ok=false, reason carries the first blocker) so callers must not spawn.
|
|
27
|
+
* On ReconciliationFailedError, returns ok=false with the error message so the
|
|
28
28
|
* caller can surface it to the user without re-throwing.
|
|
29
29
|
*
|
|
30
30
|
* Other unexpected errors propagate; they are not part of the drift
|
|
@@ -483,7 +483,7 @@ async function buildRegistryAndFindActive(
|
|
|
483
483
|
let activeMilestoneSlices: SliceRow[] = [];
|
|
484
484
|
let activeMilestoneFound = false;
|
|
485
485
|
let activeMilestoneHasDraft = false;
|
|
486
|
-
let firstDeferredQueuedShell: { id: string; title: string; deps: string[] } | null = null;
|
|
486
|
+
let firstDeferredQueuedShell: { id: string; title: string; deps: string[]; hasDraftContext: boolean } | null = null;
|
|
487
487
|
|
|
488
488
|
for (const m of milestones) {
|
|
489
489
|
if (parkedMilestoneIds.has(m.id)) {
|
|
@@ -505,6 +505,8 @@ async function buildRegistryAndFindActive(
|
|
|
505
505
|
const allSlicesDone = slices.length > 0 && slices.every(s => isStatusDone(s.status));
|
|
506
506
|
|
|
507
507
|
const title = stripMilestonePrefix(m.title) || m.id;
|
|
508
|
+
const hasContext = !!resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
509
|
+
const hasDraftContext = !hasContext && !!resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
508
510
|
|
|
509
511
|
if (!activeMilestoneFound) {
|
|
510
512
|
const deps = m.depends_on;
|
|
@@ -515,9 +517,9 @@ async function buildRegistryAndFindActive(
|
|
|
515
517
|
continue;
|
|
516
518
|
}
|
|
517
519
|
|
|
518
|
-
if (m.status === 'queued' && slices.length === 0) {
|
|
520
|
+
if (m.status === 'queued' && slices.length === 0 && !hasContext) {
|
|
519
521
|
if (!firstDeferredQueuedShell) {
|
|
520
|
-
firstDeferredQueuedShell = { id: m.id, title, deps };
|
|
522
|
+
firstDeferredQueuedShell = { id: m.id, title, deps, hasDraftContext };
|
|
521
523
|
}
|
|
522
524
|
registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
|
|
523
525
|
continue;
|
|
@@ -531,7 +533,7 @@ async function buildRegistryAndFindActive(
|
|
|
531
533
|
continue;
|
|
532
534
|
}
|
|
533
535
|
|
|
534
|
-
if (m.status === 'needs-discussion') activeMilestoneHasDraft = true;
|
|
536
|
+
if ((m.status === 'needs-discussion' && !hasContext) || hasDraftContext) activeMilestoneHasDraft = true;
|
|
535
537
|
|
|
536
538
|
activeMilestone = { id: m.id, title };
|
|
537
539
|
activeMilestoneSlices = slices;
|
|
@@ -548,6 +550,7 @@ async function buildRegistryAndFindActive(
|
|
|
548
550
|
activeMilestone = { id: shell.id, title: shell.title };
|
|
549
551
|
activeMilestoneSlices = [];
|
|
550
552
|
activeMilestoneFound = true;
|
|
553
|
+
if (shell.hasDraftContext) activeMilestoneHasDraft = true;
|
|
551
554
|
const entry = registry.find(e => e.id === shell.id);
|
|
552
555
|
if (entry) entry.status = 'active';
|
|
553
556
|
}
|
|
@@ -36,6 +36,7 @@ import { registerAutoWorker } from "../db/auto-workers.js";
|
|
|
36
36
|
import { claimMilestoneLease } from "../db/milestone-leases.js";
|
|
37
37
|
import { recordDispatchClaim, markCanceled } from "../db/unit-dispatches.js";
|
|
38
38
|
import { setRuntimeKv, getRuntimeKv } from "../db/runtime-kv.js";
|
|
39
|
+
import { SourceObservationStore } from "../source-observations.js";
|
|
39
40
|
|
|
40
41
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
41
42
|
|
|
@@ -1102,6 +1103,7 @@ function makeLoopSession(overrides?: Partial<Record<string, unknown>>) {
|
|
|
1102
1103
|
currentMilestoneId: "M001",
|
|
1103
1104
|
currentUnit: null,
|
|
1104
1105
|
currentUnitRouting: null,
|
|
1106
|
+
sourceObservations: new SourceObservationStore(),
|
|
1105
1107
|
completedUnits: [],
|
|
1106
1108
|
resourceVersionOnStart: null,
|
|
1107
1109
|
lastPromptCharCount: undefined,
|
|
@@ -1118,6 +1120,7 @@ function makeLoopSession(overrides?: Partial<Record<string, unknown>>) {
|
|
|
1118
1120
|
unitLifetimeDispatches: new Map<string, number>(),
|
|
1119
1121
|
unitRecoveryCount: new Map<string, number>(),
|
|
1120
1122
|
verificationRetryCount: new Map<string, number>(),
|
|
1123
|
+
zeroToolRetryCount: new Map<string, number>(),
|
|
1121
1124
|
gitService: null,
|
|
1122
1125
|
lastRequestTimestamp: 0,
|
|
1123
1126
|
autoStartTime: Date.now(),
|
|
@@ -1125,6 +1128,19 @@ function makeLoopSession(overrides?: Partial<Record<string, unknown>>) {
|
|
|
1125
1128
|
newSession: () => Promise.resolve({ cancelled: false }),
|
|
1126
1129
|
getContextUsage: () => ({ percent: 10, tokens: 1000, limit: 10000 }),
|
|
1127
1130
|
},
|
|
1131
|
+
setCurrentUnit(this: any, unit: any) {
|
|
1132
|
+
this.currentUnit = unit;
|
|
1133
|
+
this.sourceObservations.beginUnit({
|
|
1134
|
+
unitType: unit.type,
|
|
1135
|
+
unitId: unit.id,
|
|
1136
|
+
startedAt: unit.startedAt,
|
|
1137
|
+
basePath: unit.workspaceRoot ?? this.basePath,
|
|
1138
|
+
});
|
|
1139
|
+
},
|
|
1140
|
+
clearCurrentUnit(this: any) {
|
|
1141
|
+
this.currentUnit = null;
|
|
1142
|
+
this.sourceObservations.clear();
|
|
1143
|
+
},
|
|
1128
1144
|
clearTimers: () => {},
|
|
1129
1145
|
...overrides,
|
|
1130
1146
|
} as any;
|
|
@@ -4693,6 +4709,104 @@ test("runUnitPhase retries 0-tool units with ordinary network-related assistant
|
|
|
4693
4709
|
assert.equal(deps.callLog.includes("pauseAuto"), false);
|
|
4694
4710
|
});
|
|
4695
4711
|
|
|
4712
|
+
test("runUnitPhase pauses auto-mode when zero-tool-call retry is exhausted", async (t) => {
|
|
4713
|
+
_resetPendingResolve();
|
|
4714
|
+
|
|
4715
|
+
const basePath = mkdtempSync(join(tmpdir(), "gsd-zero-tool-exhausted-"));
|
|
4716
|
+
t.after(() => {
|
|
4717
|
+
rmSync(basePath, { recursive: true, force: true });
|
|
4718
|
+
});
|
|
4719
|
+
|
|
4720
|
+
const ctx = {
|
|
4721
|
+
...makeMockCtx(),
|
|
4722
|
+
ui: {
|
|
4723
|
+
notify: () => {},
|
|
4724
|
+
setStatus: () => {},
|
|
4725
|
+
setWorkingMessage: () => {},
|
|
4726
|
+
},
|
|
4727
|
+
sessionManager: {
|
|
4728
|
+
getEntries: () => [],
|
|
4729
|
+
},
|
|
4730
|
+
modelRegistry: {
|
|
4731
|
+
getProviderAuthMode: () => undefined,
|
|
4732
|
+
isProviderRequestReady: () => true,
|
|
4733
|
+
},
|
|
4734
|
+
} as any;
|
|
4735
|
+
const pi = {
|
|
4736
|
+
...makeMockPi(),
|
|
4737
|
+
sendMessage: () => {
|
|
4738
|
+
queueMicrotask(() => resolveAgentEnd(makeEvent([
|
|
4739
|
+
{
|
|
4740
|
+
role: "assistant",
|
|
4741
|
+
content: [
|
|
4742
|
+
{ type: "text", text: "Error: I'll investigate the network error handling next." },
|
|
4743
|
+
],
|
|
4744
|
+
},
|
|
4745
|
+
])));
|
|
4746
|
+
},
|
|
4747
|
+
} as any;
|
|
4748
|
+
const s = makeLoopSession({
|
|
4749
|
+
basePath,
|
|
4750
|
+
canonicalProjectRoot: basePath,
|
|
4751
|
+
originalBasePath: basePath,
|
|
4752
|
+
});
|
|
4753
|
+
// Pre-seed counter at MAX_ZERO_TOOL_RETRIES so the next zero-tool turn exhausts the cap
|
|
4754
|
+
s.zeroToolRetryCount.set("execute-task/M001/S01/T01", 1);
|
|
4755
|
+
|
|
4756
|
+
const mockLedger = {
|
|
4757
|
+
version: 1,
|
|
4758
|
+
projectStartedAt: Date.now(),
|
|
4759
|
+
units: [] as any[],
|
|
4760
|
+
};
|
|
4761
|
+
const deps = makeMockDeps({
|
|
4762
|
+
closeoutUnit: async () => {
|
|
4763
|
+
mockLedger.units.push({
|
|
4764
|
+
type: "execute-task",
|
|
4765
|
+
id: "M001/S01/T01",
|
|
4766
|
+
startedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
4767
|
+
toolCalls: 0,
|
|
4768
|
+
assistantMessages: 1,
|
|
4769
|
+
tokens: { input: 100, output: 20, total: 120, cacheRead: 0, cacheWrite: 0 },
|
|
4770
|
+
cost: 0.01,
|
|
4771
|
+
});
|
|
4772
|
+
},
|
|
4773
|
+
getLedger: () => mockLedger,
|
|
4774
|
+
});
|
|
4775
|
+
let seq = 0;
|
|
4776
|
+
|
|
4777
|
+
const result = await runUnitPhase(
|
|
4778
|
+
{ ctx, pi, s, deps, prefs: undefined, iteration: 1, flowId: "flow-zero-tool-exhausted", nextSeq: () => ++seq },
|
|
4779
|
+
{
|
|
4780
|
+
unitType: "execute-task",
|
|
4781
|
+
unitId: "M001/S01/T01",
|
|
4782
|
+
prompt: "do work",
|
|
4783
|
+
finalPrompt: "do work",
|
|
4784
|
+
pauseAfterUatDispatch: false,
|
|
4785
|
+
state: {
|
|
4786
|
+
phase: "executing",
|
|
4787
|
+
activeMilestone: { id: "M001", title: "Milestone" },
|
|
4788
|
+
activeSlice: { id: "S01", title: "Slice" },
|
|
4789
|
+
activeTask: { id: "T01", title: "Task" },
|
|
4790
|
+
registry: [{ id: "M001", title: "Milestone", status: "active" }],
|
|
4791
|
+
recentDecisions: [],
|
|
4792
|
+
blockers: [],
|
|
4793
|
+
nextAction: "",
|
|
4794
|
+
progress: { milestones: { done: 0, total: 1 } },
|
|
4795
|
+
requirements: { active: 0, validated: 0, deferred: 0, outOfScope: 0, blocked: 0, total: 0 },
|
|
4796
|
+
} as any,
|
|
4797
|
+
mid: "M001",
|
|
4798
|
+
midTitle: "Milestone",
|
|
4799
|
+
isRetry: false,
|
|
4800
|
+
previousTier: undefined,
|
|
4801
|
+
},
|
|
4802
|
+
{ recentUnits: [{ key: "execute-task/M001/S01/T01" }], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 },
|
|
4803
|
+
);
|
|
4804
|
+
|
|
4805
|
+
assert.equal(result.action, "break");
|
|
4806
|
+
assert.equal((result as any).reason, "zero-tool-calls-exhausted");
|
|
4807
|
+
assert.equal(deps.callLog.includes("pauseAuto"), true);
|
|
4808
|
+
});
|
|
4809
|
+
|
|
4696
4810
|
test("autoLoop pauses user-driven deep question instead of flagging 0 tool calls", async () => {
|
|
4697
4811
|
_resetPendingResolve();
|
|
4698
4812
|
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
selectAndApplyModel,
|
|
34
34
|
ModelPolicyDispatchBlockedError,
|
|
35
35
|
clearToolBaseline,
|
|
36
|
+
getToolBaselineSnapshot,
|
|
36
37
|
} from "../auto-model-selection.js";
|
|
37
38
|
import { applyModelPolicyFilter } from "../uok/model-policy.js";
|
|
38
39
|
import {
|
|
@@ -139,7 +140,7 @@ function makeCtx(
|
|
|
139
140
|
test("vacuous-truth (a): unit type with empty workflow-required tools → dispatch succeeds", async () => {
|
|
140
141
|
const env = makeTempProject();
|
|
141
142
|
try {
|
|
142
|
-
// `
|
|
143
|
+
// `rewrite-docs` has no required workflow tools
|
|
143
144
|
// → returns []. Exercises the empty-requiredTools branch in
|
|
144
145
|
// applyModelPolicyFilter (existing test used
|
|
145
146
|
// gate-evaluate which has non-empty required tools and never hit this path).
|
|
@@ -161,7 +162,7 @@ test("vacuous-truth (a): unit type with empty workflow-required tools → dispat
|
|
|
161
162
|
const result = await selectAndApplyModel(
|
|
162
163
|
makeCtx(availableModels),
|
|
163
164
|
pi as any,
|
|
164
|
-
"
|
|
165
|
+
"rewrite-docs",
|
|
165
166
|
"x1",
|
|
166
167
|
env.dir,
|
|
167
168
|
undefined,
|
|
@@ -308,8 +309,8 @@ test("genuinely-impossible (a): pi-native required tool incompatible with candid
|
|
|
308
309
|
test("genuinely-impossible (b): cross-provider routing disabled + provider mismatch → typed error", async () => {
|
|
309
310
|
const env = makeTempProject();
|
|
310
311
|
try {
|
|
311
|
-
// Use plan-slice
|
|
312
|
-
//
|
|
312
|
+
// Use plan-slice but pretend no candidate model can carry its required
|
|
313
|
+
// workflow tools. The simplest way: provide a model whose
|
|
313
314
|
// api is a fictitious "no-tools" string — `filterToolsForProvider` returns
|
|
314
315
|
// every tool as filtered for an unknown api with toolCalling=false, OR we
|
|
315
316
|
// can pick a real api that also denies the tool. We use an api that
|
|
@@ -711,3 +712,64 @@ test("cross-mode (#4965): auto → guided → auto preserves the original auto-e
|
|
|
711
712
|
env.cleanup();
|
|
712
713
|
}
|
|
713
714
|
});
|
|
715
|
+
|
|
716
|
+
// ─── 8. Baseline union: MCP tools connected after baseline capture (#477) ─────
|
|
717
|
+
//
|
|
718
|
+
// `getToolBaselineSnapshot` must return the UNION of the frozen WeakMap baseline
|
|
719
|
+
// and the current live tool set. This ensures:
|
|
720
|
+
// (a) Provider-narrowed tools (in baseline, dropped from live) are still seen
|
|
721
|
+
// by transport preflight — the bug-5 fix from #477.
|
|
722
|
+
// (b) Tools connected after the baseline was captured (e.g. MCP server attached
|
|
723
|
+
// mid-session) are also visible — so a paused run that resumes after MCP
|
|
724
|
+
// reconnects clears the transport warning on the first iteration instead of
|
|
725
|
+
// repeating it indefinitely.
|
|
726
|
+
|
|
727
|
+
test("baseline union (#477): getToolBaselineSnapshot includes live tools not present in frozen baseline", async () => {
|
|
728
|
+
const env = makeTempProject();
|
|
729
|
+
try {
|
|
730
|
+
const availableModels = [
|
|
731
|
+
{ id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" },
|
|
732
|
+
];
|
|
733
|
+
|
|
734
|
+
const initialTools = ["bash", "read", "write"];
|
|
735
|
+
const pi = makeRecordingPi(initialTools);
|
|
736
|
+
clearToolBaseline(pi as unknown as object);
|
|
737
|
+
|
|
738
|
+
// Capture baseline with only native tools (no MCP connected yet).
|
|
739
|
+
await selectAndApplyModel(
|
|
740
|
+
makeCtx(availableModels),
|
|
741
|
+
pi as any,
|
|
742
|
+
"execute-task",
|
|
743
|
+
"u1",
|
|
744
|
+
env.dir,
|
|
745
|
+
undefined,
|
|
746
|
+
false,
|
|
747
|
+
{ provider: "anthropic", id: "claude-sonnet-4-6" },
|
|
748
|
+
undefined,
|
|
749
|
+
/* isAutoMode */ true,
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
// Simulate: provider narrows tools (Groq cap, hook override, etc.).
|
|
753
|
+
// The baseline in the WeakMap still has the full initial set.
|
|
754
|
+
pi.setActiveTools(["bash"]);
|
|
755
|
+
|
|
756
|
+
// Simulate: user connects MCP mid-session (after the baseline was captured).
|
|
757
|
+
const liveTools = pi.getActiveTools().concat(["mcp__gsd-workflow__gsd_uat_exec"]);
|
|
758
|
+
pi.setActiveTools(liveTools);
|
|
759
|
+
|
|
760
|
+
const snapshot = getToolBaselineSnapshot(pi as any);
|
|
761
|
+
|
|
762
|
+
// All baseline tools must be present (even the provider-narrowed ones).
|
|
763
|
+
for (const t of initialTools) {
|
|
764
|
+
assert.ok(snapshot.includes(t), `snapshot must include baseline tool: ${t}`);
|
|
765
|
+
}
|
|
766
|
+
// Newly connected MCP tool must also be present.
|
|
767
|
+
assert.ok(
|
|
768
|
+
snapshot.includes("mcp__gsd-workflow__gsd_uat_exec"),
|
|
769
|
+
"snapshot must include MCP tool connected after baseline capture",
|
|
770
|
+
);
|
|
771
|
+
} finally {
|
|
772
|
+
env.restoreEnv();
|
|
773
|
+
env.cleanup();
|
|
774
|
+
}
|
|
775
|
+
});
|