@opengsd/gsd-pi 1.8.1 → 1.9.0
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 +10 -8
- package/dist/cli.js +5 -1
- package/dist/headless-events.d.ts +2 -1
- package/dist/headless-events.js +11 -3
- package/dist/headless-milestone-readiness.d.ts +29 -0
- package/dist/headless-milestone-readiness.js +161 -0
- package/dist/headless.js +25 -2
- package/dist/print-mode-exit.d.ts +12 -0
- package/dist/print-mode-exit.js +14 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +16 -47
- package/dist/resources/extensions/browser-tools/capture.js +2 -77
- package/dist/resources/extensions/browser-tools/screenshot-constraints.js +83 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -2
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +30 -2
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +74 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +35 -1
- package/dist/resources/extensions/gsd/auto.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +78 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +84 -26
- package/dist/resources/extensions/gsd/command-feedback.js +16 -12
- package/dist/resources/extensions/gsd/compat/compat-marker.js +19 -2
- package/dist/resources/extensions/gsd/db/command-queue.js +7 -2
- package/dist/resources/extensions/gsd/db/engine.js +51 -16
- package/dist/resources/extensions/gsd/db/writers/cascades.js +5 -1
- package/dist/resources/extensions/gsd/db/writers/status.js +13 -3
- package/dist/resources/extensions/gsd/db-base-schema.js +28 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +30 -0
- package/dist/resources/extensions/gsd/db-schema-metadata.js +4 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/dist/resources/extensions/gsd/flat-phase-migration.js +156 -21
- package/dist/resources/extensions/gsd/gsd-db.js +187 -45
- package/dist/resources/extensions/gsd/guided-flow.js +29 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +16 -2
- package/dist/resources/extensions/gsd/md-importer.js +102 -14
- package/dist/resources/extensions/gsd/migrate/command.js +19 -162
- package/dist/resources/extensions/gsd/migrate/execution.js +98 -0
- package/dist/resources/extensions/gsd/migrate/plan.js +49 -0
- package/dist/resources/extensions/gsd/migrate/planning-writer.js +12 -17
- package/dist/resources/extensions/gsd/migrate/presentation.js +64 -0
- package/dist/resources/extensions/gsd/parallel-eligibility.js +5 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +28 -43
- package/dist/resources/extensions/gsd/paths.js +4 -1
- package/dist/resources/extensions/gsd/phase-anchor.js +7 -2
- package/dist/resources/extensions/gsd/post-execution-checks.js +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +26 -1
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +30 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +27 -13
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +13 -3
- package/dist/resources/extensions/gsd/stop-notice.js +15 -1
- package/dist/resources/extensions/gsd/sync-lock.js +13 -7
- package/dist/resources/extensions/gsd/tools/complete-task.js +82 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +13 -15
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +18 -15
- package/dist/resources/extensions/gsd/tools/replan-task.js +128 -0
- package/dist/resources/extensions/gsd/tools/rework-brief.js +74 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +81 -0
- package/dist/resources/extensions/gsd/unit-registry.js +4 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +19 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +6 -0
- package/dist/resources/extensions/mac-tools/index.js +31 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- 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/static/css/3b1ceb7abfb9a230.css +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/package.json +0 -1
- package/dist/web-mode.js +31 -3
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +12 -10
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-config.d.ts +1 -1
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-config.js +5 -1
- package/packages/daemon/dist/cloud-config.js.map +1 -1
- package/packages/daemon/dist/cloud-config.test.js +51 -1
- package/packages/daemon/dist/cloud-config.test.js.map +1 -1
- package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -1
- package/packages/daemon/dist/local-tool-executor.js +2 -0
- package/packages/daemon/dist/local-tool-executor.js.map +1 -1
- package/packages/daemon/package.json +6 -6
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/dist/cli.d.ts +5 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cli.js +160 -0
- package/packages/gsd-cloud/dist/cli.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts +19 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.js +231 -0
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js +118 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js +37 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +35 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js +258 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js +88 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts +3 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.js +37 -0
- package/packages/gsd-cloud/dist/cloud-token.js.map +1 -0
- package/packages/gsd-cloud/dist/config.d.ts +17 -0
- package/packages/gsd-cloud/dist/config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/config.js +146 -0
- package/packages/gsd-cloud/dist/config.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts +24 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.js +128 -0
- package/packages/gsd-cloud/dist/device-flow.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.js +155 -0
- package/packages/gsd-cloud/dist/device-flow.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts +28 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.js +11 -0
- package/packages/gsd-cloud/dist/executors/executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts +34 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js +179 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js +44 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts +17 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.js +29 -0
- package/packages/gsd-cloud/dist/executors/index.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts +36 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js +176 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js +34 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js.map +1 -0
- package/packages/gsd-cloud/dist/index.d.ts +7 -0
- package/packages/gsd-cloud/dist/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/index.js +8 -0
- package/packages/gsd-cloud/dist/index.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts +16 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.js +29 -0
- package/packages/gsd-cloud/dist/inject-gateway.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js +41 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js.map +1 -0
- package/packages/gsd-cloud/dist/logger.d.ts +25 -0
- package/packages/gsd-cloud/dist/logger.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/logger.js +72 -0
- package/packages/gsd-cloud/dist/logger.js.map +1 -0
- package/packages/gsd-cloud/dist/types.d.ts +51 -0
- package/packages/gsd-cloud/dist/types.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/types.js +9 -0
- package/packages/gsd-cloud/dist/types.js.map +1 -0
- package/packages/gsd-cloud/package.json +3 -3
- package/packages/mcp-server/README.md +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +60 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +54 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +1 -0
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +19 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +16 -47
- package/src/resources/extensions/browser-tools/capture.ts +2 -86
- package/src/resources/extensions/browser-tools/screenshot-constraints.ts +94 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -0
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +30 -2
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +86 -19
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -1
- package/src/resources/extensions/gsd/auto.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +91 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -24
- package/src/resources/extensions/gsd/command-feedback.ts +19 -12
- package/src/resources/extensions/gsd/compat/compat-marker.ts +21 -4
- package/src/resources/extensions/gsd/db/command-queue.ts +7 -1
- package/src/resources/extensions/gsd/db/engine.ts +53 -17
- package/src/resources/extensions/gsd/db/writers/cascades.ts +5 -1
- package/src/resources/extensions/gsd/db/writers/status.ts +25 -8
- package/src/resources/extensions/gsd/db-base-schema.ts +30 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +32 -0
- package/src/resources/extensions/gsd/db-schema-metadata.ts +4 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
- package/src/resources/extensions/gsd/gsd-db.ts +255 -52
- package/src/resources/extensions/gsd/guided-flow.ts +39 -10
- package/src/resources/extensions/gsd/markdown-renderer.ts +23 -7
- package/src/resources/extensions/gsd/md-importer.ts +109 -11
- package/src/resources/extensions/gsd/migrate/command.ts +21 -227
- package/src/resources/extensions/gsd/migrate/execution.ts +136 -0
- package/src/resources/extensions/gsd/migrate/plan.ts +101 -0
- package/src/resources/extensions/gsd/migrate/planning-writer.ts +12 -20
- package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
- package/src/resources/extensions/gsd/parallel-eligibility.ts +5 -4
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +37 -40
- package/src/resources/extensions/gsd/paths.ts +4 -1
- package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
- package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +28 -1
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +31 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +30 -13
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +16 -3
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +2 -2
- package/src/resources/extensions/gsd/stop-notice.ts +18 -1
- package/src/resources/extensions/gsd/sync-lock.ts +14 -6
- package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +96 -1
- package/src/resources/extensions/gsd/tests/command-feedback.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +286 -0
- package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/external-edit-scoped-import.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/import-slice-milestone-completion-preserve.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/migrate-plan.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/migrate-presentation.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/migrate-safety-audit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/parallel-eligibility-file-overlap.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +32 -4
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +26 -23
- package/src/resources/extensions/gsd/tests/planning-writer.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/replan-task-handler.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/rework-brief.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-migration-gates.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +120 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +14 -16
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +18 -17
- package/src/resources/extensions/gsd/tools/replan-task.ts +160 -0
- package/src/resources/extensions/gsd/tools/rework-brief.ts +91 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +92 -0
- package/src/resources/extensions/gsd/types.ts +11 -0
- package/src/resources/extensions/gsd/unit-registry.ts +4 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +20 -3
- package/src/resources/extensions/gsd/worktree-state-projection.ts +12 -0
- package/src/resources/extensions/mac-tools/index.ts +52 -9
- package/src/resources/extensions/mac-tools/tests/mac-screenshot-payload.test.cjs +70 -0
- package/dist/web/standalone/.next/static/css/18c2fdf261bfc1b7.css +0 -1
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// sharp is an optional native dependency. Load it lazily so that screenshot
|
|
2
|
+
// emitters still work on platforms where sharp is unavailable (e.g. bunx on
|
|
3
|
+
// Raspberry Pi). constrainScreenshot falls back to returning the raw buffer
|
|
4
|
+
// when sharp is not installed.
|
|
5
|
+
let _sharp;
|
|
6
|
+
async function getSharp() {
|
|
7
|
+
if (_sharp !== undefined)
|
|
8
|
+
return _sharp;
|
|
9
|
+
try {
|
|
10
|
+
_sharp = (await import("sharp")).default;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
_sharp = null;
|
|
14
|
+
}
|
|
15
|
+
return _sharp;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Test-only seam: override the cached sharp module. Pass `null` to simulate
|
|
19
|
+
* an environment where the sharp native dep is unavailable; pass `undefined`
|
|
20
|
+
* to clear the cache and let the next getSharp() call re-import. See
|
|
21
|
+
* tests/capture-sharp-optional.test.cjs.
|
|
22
|
+
*/
|
|
23
|
+
export function __setSharpForTesting(value) {
|
|
24
|
+
_sharp = value;
|
|
25
|
+
}
|
|
26
|
+
// Anthropic vision: 1568px is the recommended optimal width. Height is capped
|
|
27
|
+
// generously at 8000px so tall full-page screenshots remain readable rather
|
|
28
|
+
// than being squished into a square constraint.
|
|
29
|
+
//
|
|
30
|
+
// Override via environment variables:
|
|
31
|
+
// SCREENSHOT_MAX_WIDTH=0 -> uncap width (use raw resolution)
|
|
32
|
+
// SCREENSHOT_MAX_HEIGHT=0 -> uncap height
|
|
33
|
+
export const MAX_SCREENSHOT_WIDTH = parseScreenshotDimension(process.env.SCREENSHOT_MAX_WIDTH, 1568);
|
|
34
|
+
export const MAX_SCREENSHOT_HEIGHT = parseScreenshotDimension(process.env.SCREENSHOT_MAX_HEIGHT, 8000);
|
|
35
|
+
/** Parse a dimension env var: positive int = that value, 0 = Infinity (uncapped), absent/invalid = default. */
|
|
36
|
+
function parseScreenshotDimension(value, fallback) {
|
|
37
|
+
if (value === undefined || value === "")
|
|
38
|
+
return fallback;
|
|
39
|
+
const n = parseInt(value, 10);
|
|
40
|
+
if (isNaN(n) || n < 0)
|
|
41
|
+
return fallback;
|
|
42
|
+
if (n === 0)
|
|
43
|
+
return Infinity;
|
|
44
|
+
return n;
|
|
45
|
+
}
|
|
46
|
+
export async function getScreenshotBufferDimensions(buffer) {
|
|
47
|
+
const sharp = await getSharp();
|
|
48
|
+
if (!sharp)
|
|
49
|
+
return null;
|
|
50
|
+
const meta = await sharp(buffer).metadata();
|
|
51
|
+
if (meta.width === undefined || meta.height === undefined)
|
|
52
|
+
return null;
|
|
53
|
+
return { width: meta.width, height: meta.height };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Constrain screenshot dimensions for the Anthropic vision API.
|
|
57
|
+
* Width is capped at 1568px (optimal) and height at 8000px, each
|
|
58
|
+
* independently, using `fit: "inside"` so aspect ratio is preserved.
|
|
59
|
+
* Small images are never upscaled.
|
|
60
|
+
*
|
|
61
|
+
* `page` parameter is retained for ToolDeps signature stability (D008)
|
|
62
|
+
* but is no longer used — all processing is server-side via sharp.
|
|
63
|
+
*/
|
|
64
|
+
export async function constrainScreenshot(_page, buffer, mimeType, quality) {
|
|
65
|
+
const sharp = await getSharp();
|
|
66
|
+
if (!sharp)
|
|
67
|
+
return buffer;
|
|
68
|
+
const dimensions = await getScreenshotBufferDimensions(buffer);
|
|
69
|
+
const width = dimensions?.width;
|
|
70
|
+
const height = dimensions?.height;
|
|
71
|
+
if (width === undefined || height === undefined)
|
|
72
|
+
return buffer;
|
|
73
|
+
if (width <= MAX_SCREENSHOT_WIDTH && height <= MAX_SCREENSHOT_HEIGHT)
|
|
74
|
+
return buffer;
|
|
75
|
+
const resizer = sharp(buffer).resize(MAX_SCREENSHOT_WIDTH, MAX_SCREENSHOT_HEIGHT, {
|
|
76
|
+
fit: "inside",
|
|
77
|
+
withoutEnlargement: true,
|
|
78
|
+
});
|
|
79
|
+
if (mimeType === "image/png") {
|
|
80
|
+
return Buffer.from(await resizer.png().toBuffer());
|
|
81
|
+
}
|
|
82
|
+
return Buffer.from(await resizer.jpeg({ quality }).toBuffer());
|
|
83
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { EventStream } from "@gsd/pi-ai";
|
|
12
12
|
import { execSync } from "node:child_process";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
13
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
15
|
import { homedir } from "node:os";
|
|
15
16
|
import { createRequire } from "node:module";
|
|
@@ -368,6 +369,7 @@ function inferMimeTypeFromDataUri(value) {
|
|
|
368
369
|
/** Collect all base64 image blocks from user messages in the context for inclusion in the SDK prompt. */
|
|
369
370
|
export function extractImageBlocksFromContext(context) {
|
|
370
371
|
const imageBlocks = [];
|
|
372
|
+
const seenImageKeys = new Set();
|
|
371
373
|
for (const msg of context.messages) {
|
|
372
374
|
if (msg.role !== "user" || !Array.isArray(msg.content))
|
|
373
375
|
continue;
|
|
@@ -382,12 +384,17 @@ export function extractImageBlocksFromContext(context) {
|
|
|
382
384
|
: inferMimeTypeFromDataUri(block.data);
|
|
383
385
|
if (!mimeType)
|
|
384
386
|
continue;
|
|
387
|
+
const data = stripDataUriPrefix(block.data);
|
|
388
|
+
const imageKey = `${mimeType}\0${createHash("sha1").update(data).digest("hex")}`;
|
|
389
|
+
if (seenImageKeys.has(imageKey))
|
|
390
|
+
continue;
|
|
391
|
+
seenImageKeys.add(imageKey);
|
|
385
392
|
imageBlocks.push({
|
|
386
393
|
type: "image",
|
|
387
394
|
source: {
|
|
388
395
|
type: "base64",
|
|
389
396
|
media_type: mimeType,
|
|
390
|
-
data
|
|
397
|
+
data,
|
|
391
398
|
},
|
|
392
399
|
});
|
|
393
400
|
}
|
|
@@ -1831,6 +1838,7 @@ function createSdkAttemptMessageState() {
|
|
|
1831
1838
|
return {
|
|
1832
1839
|
builder: null,
|
|
1833
1840
|
intermediateToolBlocks: [],
|
|
1841
|
+
intermediateTextBlocks: [],
|
|
1834
1842
|
toolResultsById: new Map(),
|
|
1835
1843
|
toolCompletionTargetsById: new Map(),
|
|
1836
1844
|
emittedExternalToolResultIds: new Set(),
|
|
@@ -1939,7 +1947,7 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
1939
1947
|
}
|
|
1940
1948
|
}
|
|
1941
1949
|
sdkAttemptLoop: for (let readinessAttempt = 0;; readinessAttempt++) {
|
|
1942
|
-
let { builder, intermediateToolBlocks, toolResultsById, toolCompletionTargetsById, emittedExternalToolResultIds, } = createSdkAttemptMessageState();
|
|
1950
|
+
let { builder, intermediateToolBlocks, intermediateTextBlocks, toolResultsById, toolCompletionTargetsById, emittedExternalToolResultIds, } = createSdkAttemptMessageState();
|
|
1943
1951
|
const controller = new AbortController();
|
|
1944
1952
|
const forwardAbort = () => controller.abort();
|
|
1945
1953
|
if (options?.signal) {
|
|
@@ -2061,9 +2069,15 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
2061
2069
|
for (const [contentIndex, block] of builder.message.content.entries()) {
|
|
2062
2070
|
if (block.type === "text" && block.text) {
|
|
2063
2071
|
lastTextContent = block.text;
|
|
2072
|
+
// Accumulate completed prose in order — a multi-question
|
|
2073
|
+
// turn commits [prose][elicitation] segments across several
|
|
2074
|
+
// synthetic-user boundaries, and overwriting a single
|
|
2075
|
+
// scalar would drop every explanation but the last.
|
|
2076
|
+
intermediateTextBlocks.push({ type: "text", text: block.text });
|
|
2064
2077
|
}
|
|
2065
2078
|
else if (block.type === "thinking" && block.thinking) {
|
|
2066
2079
|
lastThinkingContent = block.thinking;
|
|
2080
|
+
intermediateTextBlocks.push({ type: "thinking", thinking: block.thinking });
|
|
2067
2081
|
}
|
|
2068
2082
|
else if (block.type === "toolCall" || block.type === "serverToolUse") {
|
|
2069
2083
|
// Collect tool blocks for externalToolExecution rendering
|
|
@@ -2161,6 +2175,7 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
2161
2175
|
const result = msg;
|
|
2162
2176
|
const finalContent = buildFinalAssistantContent({
|
|
2163
2177
|
intermediateToolBlocks,
|
|
2178
|
+
intermediateTextBlocks,
|
|
2164
2179
|
pendingContent: builder?.message.content,
|
|
2165
2180
|
toolResultsById,
|
|
2166
2181
|
lastThinkingContent,
|
|
@@ -198,6 +198,15 @@ export function buildFinalAssistantContent(params) {
|
|
|
198
198
|
}
|
|
199
199
|
attachExternalResultsToToolBlocks(mergedToolBlocks, params.toolResultsById);
|
|
200
200
|
const finalContent = mergedToolBlocks.filter((block) => !shouldSuppressDuplicateToolUnavailableBlock(block, mergedToolBlocks));
|
|
201
|
+
// Emit prose/thinking captured at earlier turn-boundaries (each successive
|
|
202
|
+
// `ask_user_questions` elicitation completes a turn) before the final
|
|
203
|
+
// segment. Capturing these in an ordered accumulator — symmetric with
|
|
204
|
+
// `intermediateToolBlocks` — is what keeps intermediate explanations from
|
|
205
|
+
// being collapsed to a single overwritten scalar and silently dropped.
|
|
206
|
+
const intermediateTextBlocks = params.intermediateTextBlocks ?? [];
|
|
207
|
+
for (const block of intermediateTextBlocks) {
|
|
208
|
+
finalContent.push(block);
|
|
209
|
+
}
|
|
201
210
|
if (params.pendingContent && params.pendingContent.length > 0) {
|
|
202
211
|
for (const block of params.pendingContent) {
|
|
203
212
|
if (block.type === "text" || block.type === "thinking") {
|
|
@@ -206,11 +215,30 @@ export function buildFinalAssistantContent(params) {
|
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
else {
|
|
218
|
+
// No builder content survived to this turn boundary, so fall back to the
|
|
219
|
+
// last scalar thinking/text captured off the stream. When the turn ends
|
|
220
|
+
// on an elicitation these scalars just mirror the final intermediate
|
|
221
|
+
// block (they are overwritten at each synthetic-user boundary), so only
|
|
222
|
+
// emit them when they carry content not already accumulated — otherwise
|
|
223
|
+
// a non-streaming `assistant` message's prose/thinking is silently lost.
|
|
224
|
+
const lastBlockOfType = (type) => {
|
|
225
|
+
for (let i = intermediateTextBlocks.length - 1; i >= 0; i--) {
|
|
226
|
+
if (intermediateTextBlocks[i].type === type)
|
|
227
|
+
return intermediateTextBlocks[i];
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
};
|
|
209
231
|
if (params.lastThinkingContent) {
|
|
210
|
-
|
|
232
|
+
const lastThinking = lastBlockOfType("thinking");
|
|
233
|
+
if (lastThinking?.thinking !== params.lastThinkingContent) {
|
|
234
|
+
finalContent.push({ type: "thinking", thinking: params.lastThinkingContent });
|
|
235
|
+
}
|
|
211
236
|
}
|
|
212
237
|
if (params.lastTextContent) {
|
|
213
|
-
|
|
238
|
+
const lastText = lastBlockOfType("text");
|
|
239
|
+
if (lastText?.text !== params.lastTextContent) {
|
|
240
|
+
finalContent.push({ type: "text", text: params.lastTextContent });
|
|
241
|
+
}
|
|
214
242
|
}
|
|
215
243
|
}
|
|
216
244
|
if (finalContent.length === 0 && params.fallbackResultText) {
|
|
@@ -263,9 +263,9 @@ export function diagnoseExpectedArtifact(unitType, unitId, base) {
|
|
|
263
263
|
}
|
|
264
264
|
return `${relSliceFile(base, mid, sid, "RESEARCH")} (slice research)`;
|
|
265
265
|
case "plan-slice":
|
|
266
|
-
return `${relSliceFile(base, mid, sid, "PLAN")}
|
|
266
|
+
return `${relSliceFile(base, mid, sid, "PLAN")} with embedded task plans`;
|
|
267
267
|
case "refine-slice":
|
|
268
|
-
return `${relSliceFile(base, mid, sid, "PLAN")}
|
|
268
|
+
return `${relSliceFile(base, mid, sid, "PLAN")} with embedded refined task plans`;
|
|
269
269
|
case "execute-task": {
|
|
270
270
|
return `Task ${tid} marked [x] in ${relSliceFile(base, mid, sid, "PLAN")} + summary written`;
|
|
271
271
|
}
|
|
@@ -209,7 +209,8 @@ export function shouldRunDeepProjectSetup(state, prefs, basePath, options = {})
|
|
|
209
209
|
}
|
|
210
210
|
function resolveArtifactBasePath(basePath, mid, session) {
|
|
211
211
|
if (session?.basePath &&
|
|
212
|
-
session.currentMilestoneId
|
|
212
|
+
session.currentMilestoneId &&
|
|
213
|
+
milestoneIdsDispatchCompatible(session.currentMilestoneId, mid) &&
|
|
213
214
|
existsSync(session.basePath)) {
|
|
214
215
|
return session.basePath;
|
|
215
216
|
}
|
|
@@ -231,6 +232,24 @@ function normalizeMilestoneScope(value) {
|
|
|
231
232
|
return null;
|
|
232
233
|
return trimmed;
|
|
233
234
|
}
|
|
235
|
+
function dispatchMilestoneIdentity(value) {
|
|
236
|
+
const normalized = normalizeMilestoneScope(value);
|
|
237
|
+
const match = normalized?.match(/^(M\d{3})(?:-[a-z0-9]{6})?$/);
|
|
238
|
+
if (!match)
|
|
239
|
+
return null;
|
|
240
|
+
return { baseId: match[1], hasSuffix: normalized !== match[1] };
|
|
241
|
+
}
|
|
242
|
+
function isBareSuffixedMilestoneAlias(left, right) {
|
|
243
|
+
const leftId = dispatchMilestoneIdentity(left);
|
|
244
|
+
const rightId = dispatchMilestoneIdentity(right);
|
|
245
|
+
return Boolean(leftId &&
|
|
246
|
+
rightId &&
|
|
247
|
+
leftId.baseId === rightId.baseId &&
|
|
248
|
+
leftId.hasSuffix !== rightId.hasSuffix);
|
|
249
|
+
}
|
|
250
|
+
function milestoneIdsDispatchCompatible(left, right) {
|
|
251
|
+
return left === right || isBareSuffixedMilestoneAlias(left, right);
|
|
252
|
+
}
|
|
234
253
|
function resolveDispatchMilestoneScope(ctx) {
|
|
235
254
|
const sessionMilestone = normalizeMilestoneScope(ctx.session?.currentMilestoneId);
|
|
236
255
|
if (sessionMilestone)
|
|
@@ -243,6 +262,31 @@ function resolveDispatchMilestoneScope(ctx) {
|
|
|
243
262
|
return { id: baseWorktree, source: "basePath worktree" };
|
|
244
263
|
return null;
|
|
245
264
|
}
|
|
265
|
+
function resolveEffectiveDispatchMilestoneId(ctx, scopedMilestone) {
|
|
266
|
+
if (scopedMilestone && isBareSuffixedMilestoneAlias(ctx.mid, scopedMilestone.id)) {
|
|
267
|
+
return dispatchMilestoneIdentity(scopedMilestone.id)?.hasSuffix ? scopedMilestone.id : ctx.mid;
|
|
268
|
+
}
|
|
269
|
+
const activeMid = ctx.state.activeMilestone?.id;
|
|
270
|
+
if (activeMid && isBareSuffixedMilestoneAlias(ctx.mid, activeMid)) {
|
|
271
|
+
return dispatchMilestoneIdentity(activeMid)?.hasSuffix ? activeMid : ctx.mid;
|
|
272
|
+
}
|
|
273
|
+
return ctx.mid;
|
|
274
|
+
}
|
|
275
|
+
function withEffectiveDispatchMilestone(ctx, effectiveMid) {
|
|
276
|
+
if (effectiveMid === ctx.mid)
|
|
277
|
+
return ctx;
|
|
278
|
+
const activeMilestone = ctx.state.activeMilestone;
|
|
279
|
+
const state = activeMilestone && milestoneIdsDispatchCompatible(activeMilestone.id, effectiveMid)
|
|
280
|
+
? {
|
|
281
|
+
...ctx.state,
|
|
282
|
+
activeMilestone: {
|
|
283
|
+
...activeMilestone,
|
|
284
|
+
id: effectiveMid,
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
: ctx.state;
|
|
288
|
+
return { ...ctx, mid: effectiveMid, state };
|
|
289
|
+
}
|
|
246
290
|
function hasMilestonePassedDiscuss(basePath, mid) {
|
|
247
291
|
if (isDbAvailable()) {
|
|
248
292
|
try {
|
|
@@ -510,18 +554,25 @@ export const DISPATCH_RULES = [
|
|
|
510
554
|
// Fire BEFORE the execution-entry phase rules so we redispatch to
|
|
511
555
|
// `discuss-milestone` instead of hitting the plan-v2 gate.
|
|
512
556
|
name: "execution-entry phase (no context) → discuss-milestone",
|
|
513
|
-
match: async ({ state, mid, midTitle, basePath, prefs, structuredQuestionsAvailable }) => {
|
|
557
|
+
match: async ({ state, mid, midTitle, basePath, session, prefs, structuredQuestionsAvailable }) => {
|
|
514
558
|
if (!EXECUTION_ENTRY_PHASES.has(state.phase))
|
|
515
559
|
return null;
|
|
516
560
|
if (!MILESTONE_ID_RE.test(mid))
|
|
517
561
|
return null;
|
|
518
562
|
if (isRegistryMilestoneComplete(state, mid))
|
|
519
563
|
return null;
|
|
520
|
-
|
|
564
|
+
// Resolve discuss/context artifacts against the active session worktree,
|
|
565
|
+
// mirroring the executing rules below. For a suffixed-worktree milestone
|
|
566
|
+
// the CONTEXT and slice plans live under the worktree, not the project
|
|
567
|
+
// root; checking the raw project-root basePath misreads the milestone as
|
|
568
|
+
// "never discussed" and re-dispatches discuss-milestone after task
|
|
569
|
+
// closeout instead of continuing execution (#1317).
|
|
570
|
+
const artifactBasePath = resolveArtifactBasePath(basePath, mid, session);
|
|
571
|
+
if (hasMilestonePassedDiscuss(artifactBasePath, mid))
|
|
521
572
|
return null;
|
|
522
573
|
// Align with the plan-v2 gate's lookup semantics: whitespace-only counts
|
|
523
574
|
// as missing, and an auto worktree may fall back to GSD_PROJECT_ROOT.
|
|
524
|
-
if (hasFinalizedMilestoneContext(
|
|
575
|
+
if (hasFinalizedMilestoneContext(artifactBasePath, mid))
|
|
525
576
|
return null;
|
|
526
577
|
// H6 fix (#4973): non-deep auto-mode has no human to answer the
|
|
527
578
|
// depth-verification question, so pre-marking avoids a write-gate
|
|
@@ -854,12 +905,14 @@ export const DISPATCH_RULES = [
|
|
|
854
905
|
},
|
|
855
906
|
{
|
|
856
907
|
name: "pre-planning (no context) → discuss-milestone",
|
|
857
|
-
match: async ({ state, mid, midTitle, basePath, prefs, structuredQuestionsAvailable }) => {
|
|
908
|
+
match: async ({ state, mid, midTitle, basePath, prefs, session, structuredQuestionsAvailable }) => {
|
|
858
909
|
if (state.phase !== "pre-planning")
|
|
859
910
|
return null;
|
|
860
911
|
if (isRegistryMilestoneComplete(state, mid))
|
|
861
912
|
return null;
|
|
862
|
-
const
|
|
913
|
+
const contextBasePath = resolveWorktreeProjectRoot(basePath, session?.originalBasePath);
|
|
914
|
+
const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT") ??
|
|
915
|
+
(contextBasePath !== basePath ? resolveMilestoneFile(contextBasePath, mid, "CONTEXT") : null);
|
|
863
916
|
const hasContext = !!(contextFile && (await loadFile(contextFile)));
|
|
864
917
|
if (hasContext)
|
|
865
918
|
return null; // fall through to next rule
|
|
@@ -1562,31 +1615,33 @@ function applyLanguageDirectiveToDispatch(action, prefs) {
|
|
|
1562
1615
|
* resolveDispatch directly without registry initialization).
|
|
1563
1616
|
*/
|
|
1564
1617
|
export async function resolveDispatch(ctx) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1618
|
+
const scopedMilestone = resolveDispatchMilestoneScope(ctx);
|
|
1619
|
+
const effectiveMid = resolveEffectiveDispatchMilestoneId(ctx, scopedMilestone);
|
|
1620
|
+
const dispatchCtx = withEffectiveDispatchMilestone(ctx, effectiveMid);
|
|
1621
|
+
if (dispatchCtx.mid && isDbAvailable()) {
|
|
1622
|
+
const milestone = getMilestone(dispatchCtx.mid);
|
|
1567
1623
|
if (milestone && isClosedStatus(milestone.status)) {
|
|
1568
1624
|
return {
|
|
1569
1625
|
action: "stop",
|
|
1570
|
-
reason: `Milestone ${
|
|
1626
|
+
reason: `Milestone ${dispatchCtx.mid} is closed (status: ${milestone.status}); auto-mode will not reopen or recover it implicitly. ` +
|
|
1571
1627
|
"Use an explicit reopen command before planning or executing more work for this milestone.",
|
|
1572
1628
|
level: "warning",
|
|
1573
1629
|
};
|
|
1574
1630
|
}
|
|
1575
1631
|
}
|
|
1576
|
-
const activeMid =
|
|
1577
|
-
if (activeMid &&
|
|
1632
|
+
const activeMid = dispatchCtx.state.activeMilestone?.id;
|
|
1633
|
+
if (activeMid && !milestoneIdsDispatchCompatible(dispatchCtx.mid, activeMid)) {
|
|
1578
1634
|
return {
|
|
1579
1635
|
action: "stop",
|
|
1580
|
-
reason: `Dispatch milestone mismatch: context mid "${
|
|
1636
|
+
reason: `Dispatch milestone mismatch: context mid "${dispatchCtx.mid}" does not match active milestone "${activeMid}". ` +
|
|
1581
1637
|
"This usually means a project-level deep setup pseudo-id leaked into milestone dispatch; rerun /gsd auto after setup state is reconciled.",
|
|
1582
1638
|
level: "warning",
|
|
1583
1639
|
};
|
|
1584
1640
|
}
|
|
1585
|
-
|
|
1586
|
-
if (scopedMilestone && ctx.mid !== scopedMilestone.id) {
|
|
1641
|
+
if (scopedMilestone && !milestoneIdsDispatchCompatible(dispatchCtx.mid, scopedMilestone.id)) {
|
|
1587
1642
|
return {
|
|
1588
1643
|
action: "stop",
|
|
1589
|
-
reason: `Dispatch milestone mismatch: context mid "${
|
|
1644
|
+
reason: `Dispatch milestone mismatch: context mid "${dispatchCtx.mid}" does not match ${scopedMilestone.source} "${scopedMilestone.id}". ` +
|
|
1590
1645
|
"The active worktree/session and derived project state disagree; recover, park, or discard the stranded milestone before continuing.",
|
|
1591
1646
|
level: "warning",
|
|
1592
1647
|
};
|
|
@@ -1594,9 +1649,9 @@ export async function resolveDispatch(ctx) {
|
|
|
1594
1649
|
// Delegate to registry when available
|
|
1595
1650
|
try {
|
|
1596
1651
|
const registry = getRegistry();
|
|
1597
|
-
const action = annotateBackgroundable(await registry.evaluateDispatch(
|
|
1652
|
+
const action = annotateBackgroundable(await registry.evaluateDispatch(dispatchCtx));
|
|
1598
1653
|
if (action.action === "dispatch" &&
|
|
1599
|
-
|
|
1654
|
+
dispatchCtx.session?.exhaustedVerificationUnits?.has(`${action.unitType}:${action.unitId}`)) {
|
|
1600
1655
|
return {
|
|
1601
1656
|
action: "stop",
|
|
1602
1657
|
reason: `Unit ${action.unitId} exhausted verification retries this session.`,
|
|
@@ -1610,13 +1665,13 @@ export async function resolveDispatch(ctx) {
|
|
|
1610
1665
|
logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
|
|
1611
1666
|
}
|
|
1612
1667
|
for (const rule of DISPATCH_RULES) {
|
|
1613
|
-
const result = await rule.match(
|
|
1668
|
+
const result = await rule.match(dispatchCtx);
|
|
1614
1669
|
if (result) {
|
|
1615
1670
|
if (result.action !== "skip")
|
|
1616
1671
|
result.matchedRule = rule.name;
|
|
1617
1672
|
const action = annotateBackgroundable(result);
|
|
1618
1673
|
if (action.action === "dispatch" &&
|
|
1619
|
-
|
|
1674
|
+
dispatchCtx.session?.exhaustedVerificationUnits?.has(`${action.unitType}:${action.unitId}`)) {
|
|
1620
1675
|
return {
|
|
1621
1676
|
action: "stop",
|
|
1622
1677
|
reason: `Unit ${action.unitId} exhausted verification retries this session.`,
|
|
@@ -1411,6 +1411,10 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
1411
1411
|
const blockingMismatch = mismatches.find((mismatch) => mismatch.severity === "error");
|
|
1412
1412
|
if (blockingMismatch) {
|
|
1413
1413
|
ctx.ui.notify(`Safety: task ${sTid} claimed passing verification that failed in recorded execution`, "error");
|
|
1414
|
+
const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit);
|
|
1415
|
+
if (gitActionResult === "dispatched") {
|
|
1416
|
+
return "dispatched";
|
|
1417
|
+
}
|
|
1414
1418
|
await pauseAuto(ctx, pi);
|
|
1415
1419
|
return "dispatched";
|
|
1416
1420
|
}
|
|
@@ -18,7 +18,7 @@ import { parseRoadmap } from "./parsers-legacy.js";
|
|
|
18
18
|
import { join, relative, sep } from "node:path";
|
|
19
19
|
import { existsSync } from "node:fs";
|
|
20
20
|
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
21
|
-
import { getGateResults, getPendingGatesForTurn } from "./gsd-db.js";
|
|
21
|
+
import { getBlockingReworkFindingsForTask, getGateResults, getPendingGatesForTurn } from "./gsd-db.js";
|
|
22
22
|
import { assertGateCoverage, getGatesForTurn, } from "./gate-registry.js";
|
|
23
23
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
24
24
|
import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
|
|
@@ -76,6 +76,32 @@ function resolvePromptBudgets() {
|
|
|
76
76
|
function resolveSummaryBudgetChars() {
|
|
77
77
|
return resolvePromptBudgets().summaryBudgetChars;
|
|
78
78
|
}
|
|
79
|
+
function renderBlockingReworkFindingsBlock(mid, sid, tid) {
|
|
80
|
+
const findings = getBlockingReworkFindingsForTask(mid, sid, tid)
|
|
81
|
+
.filter((finding) => finding.status === "pending");
|
|
82
|
+
if (findings.length === 0)
|
|
83
|
+
return "";
|
|
84
|
+
const lines = [
|
|
85
|
+
"## Blocking Rework Findings (non-optional)",
|
|
86
|
+
"",
|
|
87
|
+
"This task has structured blocking rework findings. Address every finding before calling `gsd_task_complete`. Include `reworkResolution` entries with each `findingId`, `status: \"resolved\"`, and concrete evidence. If deferring a finding, use `status: \"deferred-with-override\"` with concrete evidence and `decisionRef`.",
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
for (const finding of findings) {
|
|
91
|
+
lines.push(`### ${finding.finding_id} (${finding.severity})`);
|
|
92
|
+
lines.push(`- Status: ${finding.status}`);
|
|
93
|
+
lines.push(`- Finding: ${finding.description}`);
|
|
94
|
+
lines.push(`- Required fix: ${finding.required_fix}`);
|
|
95
|
+
if (finding.verification_commands.length > 0) {
|
|
96
|
+
lines.push("- Required verification:");
|
|
97
|
+
for (const command of finding.verification_commands) {
|
|
98
|
+
lines.push(` - \`${command}\``);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
lines.push("");
|
|
102
|
+
}
|
|
103
|
+
return lines.join("\n").trim();
|
|
104
|
+
}
|
|
79
105
|
function formatProjectClassificationForPlanning(classification) {
|
|
80
106
|
const sampleFiles = classification.contentFiles.slice(0, 8);
|
|
81
107
|
const sample = sampleFiles.length > 0 ? sampleFiles.map((file) => `\`${file}\``).join(", ") : "(none)";
|
|
@@ -2319,6 +2345,14 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
|
2319
2345
|
catch (reopenErr) {
|
|
2320
2346
|
logWarning("prompt", `reopen reason injection failed: ${reopenErr.message}`);
|
|
2321
2347
|
}
|
|
2348
|
+
const reworkFindingsBlock = renderBlockingReworkFindingsBlock(mid, sid, tid);
|
|
2349
|
+
if (reworkFindingsBlock) {
|
|
2350
|
+
const block = reworkFindingsBlock + "\n\n---\n\n";
|
|
2351
|
+
phaseAnchorSection = phaseAnchorSection
|
|
2352
|
+
? `${block}${phaseAnchorSection}`
|
|
2353
|
+
: block;
|
|
2354
|
+
trackPromptContext(contextTelemetry, "rework-findings", "inline", reworkFindingsBlock);
|
|
2355
|
+
}
|
|
2322
2356
|
// ADR-011 Phase 2: inject any resolved-but-unapplied escalation override
|
|
2323
2357
|
// into this task's prompt. Claim is atomic via DB UPDATE WHERE IS NULL, so
|
|
2324
2358
|
// if a parallel build already injected it, we skip. Feature-gated by
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* next unit type, creates a fresh session, and injects a focused prompt
|
|
12
12
|
* telling the LLM which files to read and what to do.
|
|
13
13
|
*/
|
|
14
|
-
import { deriveState } from "./state.js";
|
|
14
|
+
import { deriveState, invalidateStateCache } from "./state.js";
|
|
15
15
|
import { buildRequirementsBacklogSummaryLines, countUnmappedActiveRequirements, getUnmappedActiveRequirements, } from "./requirements-backlog.js";
|
|
16
16
|
import { parseUnitId } from "./unit-id.js";
|
|
17
17
|
import { assessInterruptedSession, readPausedSessionMetadata, PAUSED_SESSION_KV_KEY, } from "./interrupted-session.js";
|
|
@@ -214,6 +214,9 @@ function captureMilestoneLockEnv(milestoneId) {
|
|
|
214
214
|
else {
|
|
215
215
|
delete process.env.GSD_MILESTONE_LOCK;
|
|
216
216
|
}
|
|
217
|
+
// The derive cache is keyed on basePath only; GSD_MILESTONE_LOCK changes
|
|
218
|
+
// what deriveState computes, so a lock flip must not serve a cached result.
|
|
219
|
+
invalidateStateCache();
|
|
217
220
|
}
|
|
218
221
|
function restoreMilestoneLockEnv() {
|
|
219
222
|
if (!s.milestoneLockEnvCaptured)
|
|
@@ -227,6 +230,7 @@ function restoreMilestoneLockEnv() {
|
|
|
227
230
|
s.previousMilestoneLockEnv = null;
|
|
228
231
|
s.hadMilestoneLockEnv = false;
|
|
229
232
|
s.milestoneLockEnvCaptured = false;
|
|
233
|
+
invalidateStateCache();
|
|
230
234
|
}
|
|
231
235
|
/**
|
|
232
236
|
* Rebuild s.scope from the current s.basePath / s.originalBasePath / s.currentMilestoneId.
|
|
@@ -731,12 +731,12 @@ export function registerDbTools(pi) {
|
|
|
731
731
|
const planTaskTool = {
|
|
732
732
|
name: "gsd_plan_task",
|
|
733
733
|
label: "Plan Task",
|
|
734
|
-
description: "Write task planning state to the GSD database, render
|
|
735
|
-
promptSnippet: "Plan a task via DB write +
|
|
734
|
+
description: "Write task planning state to the GSD database, render the slice PLAN from DB, and clear caches after a successful render.",
|
|
735
|
+
promptSnippet: "Plan a task via DB write + slice PLAN render + cache invalidation",
|
|
736
736
|
promptGuidelines: [
|
|
737
|
-
"Use gsd_plan_task for task planning instead of writing
|
|
737
|
+
"Use gsd_plan_task for task planning instead of writing task PLAN files directly.",
|
|
738
738
|
"Keep parameters flat and provide the full task planning payload.",
|
|
739
|
-
"The tool validates input, requires an existing parent slice, writes task planning data, renders the
|
|
739
|
+
"The tool validates input, requires an existing parent slice, writes task planning data, renders the slice PLAN file from DB, and clears both state and parse caches after success.",
|
|
740
740
|
"Use the canonical name gsd_plan_task; gsd_task_plan is only an alias.",
|
|
741
741
|
],
|
|
742
742
|
parameters: Type.Object({
|
|
@@ -812,6 +812,12 @@ export function registerDbTools(pi) {
|
|
|
812
812
|
verdict: Type.String({ description: "Pass/fail verdict (e.g. '✅ pass', '❌ fail')" }),
|
|
813
813
|
durationMs: Type.Number({ description: "Duration of the command in milliseconds" }),
|
|
814
814
|
}), { description: "Array of verification evidence entries (structured objects only)" })),
|
|
815
|
+
reworkResolution: Type.Optional(Type.Array(Type.Object({
|
|
816
|
+
findingId: Type.String({ description: "Rework finding ID being resolved (e.g. F1)" }),
|
|
817
|
+
status: StringEnum(["resolved", "deferred-with-override"], { description: "Resolution status for this finding" }),
|
|
818
|
+
evidence: Type.String({ description: "Concrete evidence that the required fix was completed" }),
|
|
819
|
+
decisionRef: Type.Optional(Type.String({ description: "Decision reference required for deferred-with-override findings" })),
|
|
820
|
+
}), { description: "Resolution evidence for structured rework findings linked to this task" })),
|
|
815
821
|
// Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
|
|
816
822
|
actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
|
|
817
823
|
triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'task verified after retry')" })),
|
|
@@ -1084,6 +1090,74 @@ export function registerDbTools(pi) {
|
|
|
1084
1090
|
execute: replanSliceExecute,
|
|
1085
1091
|
};
|
|
1086
1092
|
registerWorkflowTool(pi, replanSliceTool);
|
|
1093
|
+
// ─── gsd_replan_task ───────────────────────────────────────────────────
|
|
1094
|
+
const replanTaskExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
1095
|
+
const { executeReplanTask } = await loadWorkflowExecutors();
|
|
1096
|
+
return executeReplanTask(params, resolveWorkflowToolBasePath(_ctx, params));
|
|
1097
|
+
};
|
|
1098
|
+
const replanTaskTool = {
|
|
1099
|
+
name: "gsd_replan_task",
|
|
1100
|
+
label: "Replan Task",
|
|
1101
|
+
description: "Update one pending task's planning contract after rework without touching sibling tasks. " +
|
|
1102
|
+
"Completed tasks are structurally protected. Re-renders the task/slice PLAN projection and records replan history.",
|
|
1103
|
+
promptSnippet: "Replan one pending GSD task after rework",
|
|
1104
|
+
promptGuidelines: [
|
|
1105
|
+
"Use gsd_replan_task when a reopened pending task needs its title, description, files, verify, inputs, or expectedOutput updated for rework.",
|
|
1106
|
+
"The task must already exist and be pending; completed tasks are rejected.",
|
|
1107
|
+
"Set reworkBriefRef when a structured rework brief triggered the task replan.",
|
|
1108
|
+
],
|
|
1109
|
+
parameters: Type.Object({
|
|
1110
|
+
milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
1111
|
+
sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
|
|
1112
|
+
taskId: Type.String({ description: "Task ID (e.g. T01)" }),
|
|
1113
|
+
title: Type.String({ description: "Updated task title" }),
|
|
1114
|
+
description: Type.String({ description: "Updated task description incorporating rework scope" }),
|
|
1115
|
+
estimate: Type.String({ description: "Updated task estimate string" }),
|
|
1116
|
+
files: Type.Array(Type.String(), { description: "Updated files likely touched" }),
|
|
1117
|
+
verify: Type.String({ description: "Updated verification command or block" }),
|
|
1118
|
+
inputs: Type.Array(Type.String(), { description: "Updated input files or references" }),
|
|
1119
|
+
expectedOutput: Type.Array(Type.String(), { description: "Updated files this task creates or overwrites" }),
|
|
1120
|
+
reworkBriefRef: Type.Optional(Type.String({ description: "Rework brief ID/reference that triggered this task replan" })),
|
|
1121
|
+
actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail" })),
|
|
1122
|
+
triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered" })),
|
|
1123
|
+
}),
|
|
1124
|
+
execute: replanTaskExecute,
|
|
1125
|
+
};
|
|
1126
|
+
registerWorkflowTool(pi, replanTaskTool);
|
|
1127
|
+
// ─── gsd_rework_brief_save ─────────────────────────────────────────────
|
|
1128
|
+
const reworkBriefSaveExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
1129
|
+
const { executeReworkBriefSave } = await loadWorkflowExecutors();
|
|
1130
|
+
return executeReworkBriefSave(params, resolveWorkflowToolBasePath(_ctx, params));
|
|
1131
|
+
};
|
|
1132
|
+
const reworkBriefSaveTool = {
|
|
1133
|
+
name: "gsd_rework_brief_save",
|
|
1134
|
+
label: "Save Rework Brief",
|
|
1135
|
+
description: "Persist a structured rework brief for a task. Blocking findings are enforced by gsd_task_complete until resolved with evidence.",
|
|
1136
|
+
promptSnippet: "Persist structured task rework findings",
|
|
1137
|
+
promptGuidelines: [
|
|
1138
|
+
"Use gsd_rework_brief_save when post-unit checks produce task rework findings.",
|
|
1139
|
+
"Use severity 'blocking' for findings that must be resolved before gsd_task_complete succeeds.",
|
|
1140
|
+
"Each finding needs a stable findingId, requiredFix, and verificationCommands.",
|
|
1141
|
+
],
|
|
1142
|
+
parameters: Type.Object({
|
|
1143
|
+
briefId: Type.Optional(Type.String({ description: "Stable brief ID; omitted defaults to RB-<milestone>-<slice>-<task>" })),
|
|
1144
|
+
milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
1145
|
+
sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
|
|
1146
|
+
taskId: Type.String({ description: "Task ID (e.g. T01)" }),
|
|
1147
|
+
findings: Type.Array(Type.Object({
|
|
1148
|
+
findingId: Type.String({ description: "Stable finding ID (e.g. F1)" }),
|
|
1149
|
+
severity: StringEnum(["blocking", "advisory"], { description: "Whether this finding blocks task completion" }),
|
|
1150
|
+
description: Type.String({ description: "What was found" }),
|
|
1151
|
+
requiredFix: Type.String({ description: "What must change" }),
|
|
1152
|
+
verificationCommands: Type.Array(Type.String(), { description: "Commands that must pass after the fix" }),
|
|
1153
|
+
status: Type.Optional(StringEnum(["pending", "resolved", "deferred-with-override"], { description: "Finding status; defaults to pending" })),
|
|
1154
|
+
evidence: Type.Optional(Type.String({ description: "Resolution evidence when already resolved" })),
|
|
1155
|
+
decisionRef: Type.Optional(Type.String({ description: "Decision reference for deferred-with-override" })),
|
|
1156
|
+
}), { description: "Structured rework findings for this task" }),
|
|
1157
|
+
}),
|
|
1158
|
+
execute: reworkBriefSaveExecute,
|
|
1159
|
+
};
|
|
1160
|
+
registerWorkflowTool(pi, reworkBriefSaveTool);
|
|
1087
1161
|
// ─── gsd_reassess_roadmap (gsd_roadmap_reassess alias) ─────────────────
|
|
1088
1162
|
const reassessRoadmapExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
1089
1163
|
const { executeReassessRoadmap } = await loadWorkflowExecutors();
|