@opengsd/gsd-pi 1.2.0-dev.955e4da0 → 1.2.0-dev.fb12b103
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/cli-style.d.ts +17 -0
- package/dist/cli-style.js +28 -0
- package/dist/cli.js +1 -1
- package/dist/headless-events.d.ts +4 -2
- package/dist/headless-events.js +7 -29
- package/dist/models-resolver.d.ts +3 -13
- package/dist/models-resolver.js +3 -22
- package/dist/resource-loader.js +2 -14
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
- package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
- package/dist/resources/extensions/async-jobs/index.js +65 -0
- package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
- package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
- package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
- package/dist/resources/extensions/bg-shell/overlay.js +9 -6
- package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
- package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
- package/dist/resources/extensions/bg-shell/utilities.js +3 -0
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
- package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
- package/dist/resources/extensions/browser-tools/index.js +69 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +30 -4
- package/dist/resources/extensions/gsd/auto/orchestrator.js +7 -5
- package/dist/resources/extensions/gsd/auto-dispatch.js +12 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
- package/dist/resources/extensions/gsd/auto-post-unit.js +11 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +15 -10
- package/dist/resources/extensions/gsd/auto-start.js +15 -10
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -90
- package/dist/resources/extensions/gsd/auto.js +4 -13
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +122 -20
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +6 -2
- package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
- package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
- package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
- package/dist/resources/extensions/gsd/captures.js +4 -6
- package/dist/resources/extensions/gsd/constants.js +0 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
- package/dist/resources/extensions/gsd/doctor-environment.js +2 -6
- package/dist/resources/extensions/gsd/doctor-format.js +9 -6
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +13 -15
- package/dist/resources/extensions/gsd/error-classifier.js +9 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
- package/dist/resources/extensions/gsd/guidance.js +98 -0
- package/dist/resources/extensions/gsd/guided-flow.js +17 -2
- package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
- package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
- package/dist/resources/extensions/gsd/migrate/safety.js +4 -1
- package/dist/resources/extensions/gsd/notification-store.js +11 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +6 -4
- package/dist/resources/extensions/gsd/paths.js +27 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
- package/dist/resources/extensions/gsd/preferences-models.js +14 -48
- package/dist/resources/extensions/gsd/prompts/complete-slice.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/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +5 -2
- package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
- package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
- package/dist/resources/extensions/gsd/publication.js +87 -0
- package/dist/resources/extensions/gsd/recovery-classification.js +37 -94
- package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
- package/dist/resources/extensions/gsd/state.js +1 -20
- package/dist/resources/extensions/gsd/stop-notice.js +57 -0
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +9 -7
- package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
- package/dist/resources/extensions/gsd/uat-policy.js +2 -1
- package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
- package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
- package/dist/resources/extensions/gsd/unit-registry.js +337 -0
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
- package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
- package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
- package/dist/resources/extensions/gsd/worktree-git-recovery.js +15 -9
- package/dist/resources/extensions/gsd/worktree-root.js +11 -0
- package/dist/resources/extensions/gsd/worktree-session-state.js +4 -5
- package/dist/resources/extensions/search-the-web/native-search.js +5 -3
- package/dist/resources/extensions/shared/browser-contract.js +59 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
- package/dist/resources/shared/package.json +3 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
- package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -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 +13 -13
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- 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/middleware-react-loadable-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/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/container.js +18 -26
- package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +14 -47
- package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
- package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
- package/dist/web/standalone/node_modules/postcss/lib/input.js +29 -54
- package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +37 -47
- package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +9 -26
- package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +55 -57
- package/dist/web/standalone/node_modules/postcss/lib/node.js +31 -99
- package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/parser.js +9 -10
- package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
- package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +11 -30
- package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
- package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
- package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
- package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +28 -69
- package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +2 -6
- package/dist/web/standalone/node_modules/postcss/package.json +48 -48
- package/dist/web/standalone/package.json +1 -1
- package/dist/worktree-cli.js +3 -6
- package/dist/worktree-status-banner.js +7 -15
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.d.ts +1 -0
- package/packages/contracts/dist/rpc.d.ts.map +1 -1
- package/packages/contracts/dist/rpc.js.map +1 -1
- package/packages/contracts/dist/workflow.d.ts +4 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js.map +1 -1
- 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/tool-execution.d.ts +5 -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 +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.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 +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-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 +11 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/cli.js +6 -3
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +17 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
- package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +3 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +94 -382
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +149 -422
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
- package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +9 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- 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/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
- package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
- package/src/resources/extensions/async-jobs/index.ts +79 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
- package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
- package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
- package/src/resources/extensions/bg-shell/overlay.ts +9 -5
- package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
- package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
- package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
- package/src/resources/extensions/bg-shell/utilities.ts +3 -0
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
- package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
- package/src/resources/extensions/browser-tools/index.ts +71 -13
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +34 -4
- package/src/resources/extensions/gsd/auto/orchestrator.ts +7 -5
- package/src/resources/extensions/gsd/auto-dispatch.ts +12 -0
- package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +13 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -26
- package/src/resources/extensions/gsd/auto-start.ts +15 -10
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
- package/src/resources/extensions/gsd/auto-worktree.ts +30 -93
- package/src/resources/extensions/gsd/auto.ts +8 -15
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +151 -15
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +6 -2
- package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
- package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
- package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
- package/src/resources/extensions/gsd/captures.ts +4 -6
- package/src/resources/extensions/gsd/constants.ts +0 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
- package/src/resources/extensions/gsd/doctor-environment.ts +2 -7
- package/src/resources/extensions/gsd/doctor-format.ts +12 -7
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +13 -15
- package/src/resources/extensions/gsd/error-classifier.ts +11 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/guidance.ts +139 -0
- package/src/resources/extensions/gsd/guided-flow.ts +16 -2
- package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
- package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
- package/src/resources/extensions/gsd/migrate/safety.ts +4 -1
- package/src/resources/extensions/gsd/notification-store.ts +26 -3
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -4
- package/src/resources/extensions/gsd/paths.ts +33 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
- package/src/resources/extensions/gsd/preferences-models.ts +12 -47
- package/src/resources/extensions/gsd/prompts/complete-slice.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/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +5 -2
- package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
- package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
- package/src/resources/extensions/gsd/publication.ts +122 -0
- package/src/resources/extensions/gsd/recovery-classification.ts +42 -96
- package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
- package/src/resources/extensions/gsd/state.ts +4 -21
- package/src/resources/extensions/gsd/stop-notice.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +16 -19
- package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
- package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
- package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +53 -11
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +73 -58
- package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
- package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +24 -29
- package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +8 -7
- package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
- package/src/resources/extensions/gsd/uat-policy.ts +2 -1
- package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
- package/src/resources/extensions/gsd/unit-registry.ts +412 -0
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
- package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
- package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
- package/src/resources/extensions/gsd/worktree-git-recovery.ts +15 -9
- package/src/resources/extensions/gsd/worktree-root.ts +12 -0
- package/src/resources/extensions/gsd/worktree-session-state.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +5 -3
- package/src/resources/extensions/shared/browser-contract.ts +66 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
- package/src/resources/shared/package.json +3 -0
- package/src/resources/skills/create-skill/references/executable-code.md +1 -1
- package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
- /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → mU4QIDlpVHDdjDpeEKh5W}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → mU4QIDlpVHDdjDpeEKh5W}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot confirmation token for destructive bash commands.
|
|
3
|
+
*
|
|
4
|
+
* The destructive-command guard hard-blocks classified commands (force push,
|
|
5
|
+
* rm -rf, SQL drop, etc.) in all modes. The block instructs the model to
|
|
6
|
+
* confirm via ask_user_questions and re-issue the command. This module is the
|
|
7
|
+
* missing escape hatch: it records the user's confirmation and lets the exact
|
|
8
|
+
* confirmed command through exactly once.
|
|
9
|
+
*
|
|
10
|
+
* Design constraints:
|
|
11
|
+
* - In-memory only, never persisted. A confirmation token written to disk
|
|
12
|
+
* could silently auto-approve a destructive command in a later session —
|
|
13
|
+
* confirmation must be re-obtained every process lifetime.
|
|
14
|
+
* - One-shot. Consuming a token clears it, so a second destructive command
|
|
15
|
+
* (even an identical one) re-blocks and re-prompts.
|
|
16
|
+
* - Command-bound. The token only matches the exact (normalized) command
|
|
17
|
+
* string the user confirmed. A reworded command re-blocks, which is safe.
|
|
18
|
+
* - Per basePath, so concurrent workspaces in one process never share tokens.
|
|
19
|
+
*
|
|
20
|
+
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { resolve } from "node:path";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Question-id substring that marks an ask_user_questions call as a
|
|
27
|
+
* destructive-command confirmation. The tool_result handler promotes the
|
|
28
|
+
* pending command to a confirmed token when an affirmative answer arrives for
|
|
29
|
+
* a question whose id contains this marker.
|
|
30
|
+
*/
|
|
31
|
+
export const DESTRUCTIVE_CONFIRM_GATE_MARKER = "destructive_confirm";
|
|
32
|
+
|
|
33
|
+
interface DestructiveConfirmationState {
|
|
34
|
+
/** Command awaiting confirmation, captured when the guard blocked it. */
|
|
35
|
+
pendingCommand: string | null;
|
|
36
|
+
/** Confirmed command cleared on first matching consume. */
|
|
37
|
+
confirmedCommand: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const statesByBasePath = new Map<string, DestructiveConfirmationState>();
|
|
41
|
+
|
|
42
|
+
function stateKey(basePath: string): string {
|
|
43
|
+
return resolve(basePath);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getState(basePath: string): DestructiveConfirmationState {
|
|
47
|
+
const key = stateKey(basePath);
|
|
48
|
+
let state = statesByBasePath.get(key);
|
|
49
|
+
if (!state) {
|
|
50
|
+
state = { pendingCommand: null, confirmedCommand: null };
|
|
51
|
+
statesByBasePath.set(key, state);
|
|
52
|
+
}
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Normalize a command for stable matching across block → confirm → retry.
|
|
58
|
+
* Trims surrounding whitespace and collapses internal runs of whitespace so
|
|
59
|
+
* cosmetic reformatting of the same command still matches the token.
|
|
60
|
+
*/
|
|
61
|
+
export function normalizeDestructiveCommand(command: string): string {
|
|
62
|
+
return command.replace(/\s+/g, " ").trim();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether an ask_user_questions question id is a destructive-confirm gate.
|
|
67
|
+
*/
|
|
68
|
+
export function isDestructiveConfirmGateId(questionId: unknown): boolean {
|
|
69
|
+
return typeof questionId === "string" && questionId.includes(DESTRUCTIVE_CONFIRM_GATE_MARKER);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Record that a destructive command was blocked and is awaiting confirmation.
|
|
74
|
+
* Called by the guard at block time. Overwrites any prior pending command —
|
|
75
|
+
* only the most recently blocked command can be confirmed.
|
|
76
|
+
*/
|
|
77
|
+
export function requestDestructiveConfirmation(
|
|
78
|
+
command: string,
|
|
79
|
+
basePath: string = process.cwd(),
|
|
80
|
+
): void {
|
|
81
|
+
const state = getState(basePath);
|
|
82
|
+
state.pendingCommand = normalizeDestructiveCommand(command);
|
|
83
|
+
// A fresh request invalidates any stale confirmed token for a different
|
|
84
|
+
// command so confirmation cannot leak across distinct destructive actions.
|
|
85
|
+
state.confirmedCommand = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Promote the pending command to a confirmed, one-shot token. Called by the
|
|
90
|
+
* tool_result handler when the user gives an affirmative answer to a
|
|
91
|
+
* destructive-confirm gate. Returns the confirmed command, or null if there
|
|
92
|
+
* was nothing pending (e.g. confirmation arrived without a preceding block).
|
|
93
|
+
*/
|
|
94
|
+
export function confirmDestructiveCommand(
|
|
95
|
+
basePath: string = process.cwd(),
|
|
96
|
+
): string | null {
|
|
97
|
+
const state = getState(basePath);
|
|
98
|
+
if (!state.pendingCommand) return null;
|
|
99
|
+
state.confirmedCommand = state.pendingCommand;
|
|
100
|
+
state.pendingCommand = null;
|
|
101
|
+
return state.confirmedCommand;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check whether the given command has been confirmed, consuming the token if
|
|
106
|
+
* so. Returns true exactly once per confirmation; subsequent calls (or a
|
|
107
|
+
* non-matching command) return false. Called by the guard before blocking.
|
|
108
|
+
*/
|
|
109
|
+
export function consumeDestructiveConfirmation(
|
|
110
|
+
command: string,
|
|
111
|
+
basePath: string = process.cwd(),
|
|
112
|
+
): boolean {
|
|
113
|
+
const state = getState(basePath);
|
|
114
|
+
if (!state.confirmedCommand) return false;
|
|
115
|
+
if (state.confirmedCommand !== normalizeDestructiveCommand(command)) return false;
|
|
116
|
+
state.confirmedCommand = null;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Inspect the pending command without consuming it (diagnostics/tests).
|
|
122
|
+
*/
|
|
123
|
+
export function peekPendingDestructiveCommand(
|
|
124
|
+
basePath: string = process.cwd(),
|
|
125
|
+
): string | null {
|
|
126
|
+
return getState(basePath).pendingCommand;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Clear all destructive-confirmation state for a basePath (tests / flow reset).
|
|
131
|
+
*/
|
|
132
|
+
export function resetDestructiveConfirmation(basePath: string = process.cwd()): void {
|
|
133
|
+
statesByBasePath.delete(stateKey(basePath));
|
|
134
|
+
}
|
|
@@ -71,27 +71,10 @@ import {
|
|
|
71
71
|
readinessNeedsDiscussion,
|
|
72
72
|
} from './milestone-readiness.js';
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`1. Review the validation details: \`/gsd status\``,
|
|
79
|
-
`2. If you fixed the missing evidence or issue, re-run milestone validation: \`/gsd validate-milestone\``,
|
|
80
|
-
`3. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
81
|
-
`4. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
82
|
-
`After validation or override passes, run \`/gsd auto\` to complete and merge the milestone.`,
|
|
83
|
-
].join("\n");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function formatNeedsRemediationBlocker(milestoneId: string): string {
|
|
87
|
-
return [
|
|
88
|
-
`Milestone ${milestoneId} is blocked because milestone validation returned needs-remediation, but all slices are complete.`,
|
|
89
|
-
`Fix options:`,
|
|
90
|
-
`1. Run \`/gsd dispatch reassess\` to add remediation slices, then run \`/gsd auto\``,
|
|
91
|
-
`2. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
92
|
-
`3. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
93
|
-
].join("\n");
|
|
94
|
-
}
|
|
74
|
+
import {
|
|
75
|
+
needsAttentionBlockerGuidance as formatNeedsAttentionBlocker,
|
|
76
|
+
needsRemediationBlockerGuidance as formatNeedsRemediationBlocker,
|
|
77
|
+
} from './guidance.js';
|
|
95
78
|
|
|
96
79
|
/**
|
|
97
80
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Stop Notice module — single owner of the auto/step-mode
|
|
3
|
+
// stop/pause notice vocabulary. Both sides of the wire live here: the
|
|
4
|
+
// formatters that produce the canonical prefixes (used by stopAuto/pauseAuto)
|
|
5
|
+
// and the classifiers that recognize them (used by the headless host to pick
|
|
6
|
+
// exit codes). Wording changes in this file keep emitter and detector in
|
|
7
|
+
// lockstep; round-trip tests enforce it.
|
|
8
|
+
|
|
9
|
+
export type StopNoticeKind = "stopped" | "blocked";
|
|
10
|
+
|
|
11
|
+
/** A reason string of the form "Blocked: …" marks a blocked stop. */
|
|
12
|
+
export function isBlockedStopReason(reason?: string | null): boolean {
|
|
13
|
+
return /^Blocked:\s*/i.test(reason ?? "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Strip the "Blocked: " marker for display. */
|
|
17
|
+
export function stopNoticeDisplayReason(reason?: string | null): string {
|
|
18
|
+
return (reason ?? "").replace(/^Blocked:\s*/i, "").trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function stopNoticeKind(reason?: string | null): StopNoticeKind {
|
|
22
|
+
return isBlockedStopReason(reason) ? "blocked" : "stopped";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Canonical stop-notice prefix: "Auto-mode blocked — reason" / "Auto-mode stopped". */
|
|
26
|
+
export function formatStopNoticePrefix(reason?: string | null): string {
|
|
27
|
+
const displayReason = stopNoticeDisplayReason(reason);
|
|
28
|
+
const prefix = stopNoticeKind(reason) === "blocked" ? "Auto-mode blocked" : "Auto-mode stopped";
|
|
29
|
+
return displayReason ? `${prefix} — ${displayReason}` : prefix;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ─── Classification (headless host side) ────────────────────────────────
|
|
33
|
+
// The canonical lowercase prefixes the headless event loop recognizes in
|
|
34
|
+
// notify messages. Emitters above and ad-hoc emitters elsewhere must start
|
|
35
|
+
// their terminal notices with one of these.
|
|
36
|
+
|
|
37
|
+
export const PAUSED_NOTICE_PREFIXES = ["auto-mode paused", "step-mode paused"] as const;
|
|
38
|
+
|
|
39
|
+
export const TERMINAL_NOTICE_PREFIXES = [
|
|
40
|
+
"auto-mode stopped",
|
|
41
|
+
"step-mode stopped",
|
|
42
|
+
"auto-mode complete",
|
|
43
|
+
"no active milestone",
|
|
44
|
+
"auto-mode idle",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
/** Manual-resolution notices emitted before auto-mode can formally pause/stop. */
|
|
48
|
+
export function isManualResolutionNotice(message: string): boolean {
|
|
49
|
+
return (
|
|
50
|
+
message.includes("resolve manually and re-run /gsd auto") ||
|
|
51
|
+
message.includes("resolve conflicts manually and run /gsd auto to resume") ||
|
|
52
|
+
message.includes("resolve and run /gsd auto to resume")
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isPauseNotice(message: string): boolean {
|
|
57
|
+
return PAUSED_NOTICE_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isTerminalNotice(message: string): boolean {
|
|
61
|
+
return TERMINAL_NOTICE_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Pauses that do not require operator intervention in headless mode. */
|
|
65
|
+
export function isNonBlockingPauseNotice(message: string): boolean {
|
|
66
|
+
return message.includes("idempotent advance: unit already active");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function isBlockedNoticeMessage(message: string): boolean {
|
|
70
|
+
return (
|
|
71
|
+
message.includes("blocked:") ||
|
|
72
|
+
(isPauseNotice(message) && !isNonBlockingPauseNotice(message)) ||
|
|
73
|
+
isManualResolutionNotice(message)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -857,3 +857,25 @@ test("resolveModelId: claude-code wins when session is claude-code regardless of
|
|
|
857
857
|
assert.ok(result, "should resolve a model");
|
|
858
858
|
assert.equal(result.provider, "claude-code", "claude-code must win when it is the session provider");
|
|
859
859
|
});
|
|
860
|
+
|
|
861
|
+
test("resolveModelId: openai-codex wins over openai for bare GPT IDs when both are available", () => {
|
|
862
|
+
const availableModels = [
|
|
863
|
+
{ id: "gpt-5.5", provider: "openai" },
|
|
864
|
+
{ id: "gpt-5.5", provider: "openai-codex" },
|
|
865
|
+
];
|
|
866
|
+
|
|
867
|
+
const result = resolveModelId("gpt-5.5", availableModels, undefined);
|
|
868
|
+
assert.ok(result, "should resolve a model");
|
|
869
|
+
assert.equal(result.provider, "openai-codex", "ChatGPT OAuth must win over platform API for bare IDs");
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
test("resolveModelId: github-copilot wins over openai when both offer the same GPT model", () => {
|
|
873
|
+
const availableModels = [
|
|
874
|
+
{ id: "gpt-5.5", provider: "openai" },
|
|
875
|
+
{ id: "gpt-5.5", provider: "github-copilot" },
|
|
876
|
+
];
|
|
877
|
+
|
|
878
|
+
const result = resolveModelId("gpt-5.5", availableModels, undefined);
|
|
879
|
+
assert.ok(result);
|
|
880
|
+
assert.equal(result.provider, "github-copilot");
|
|
881
|
+
});
|
|
@@ -518,13 +518,12 @@ test("advance() is idempotent for the same active unit", async (t) => {
|
|
|
518
518
|
if (first.kind === "advanced") {
|
|
519
519
|
assert.deepEqual(first.unit, { unitType: "execute-task", unitId: "M001/S01/T01" });
|
|
520
520
|
}
|
|
521
|
-
assert.equal(second.kind, "
|
|
522
|
-
if (second.kind !== "
|
|
521
|
+
assert.equal(second.kind, "skipped");
|
|
522
|
+
if (second.kind !== "skipped") return;
|
|
523
523
|
assert.equal(second.reason, "idempotent advance: unit already active");
|
|
524
|
-
assert.equal(second.action, "pause");
|
|
525
524
|
});
|
|
526
525
|
|
|
527
|
-
test("idempotency
|
|
526
|
+
test("idempotency skip fires with its own reason before saturation", async (t) => {
|
|
528
527
|
const f = makeFixture();
|
|
529
528
|
t.after(() => f.cleanup());
|
|
530
529
|
|
|
@@ -532,10 +531,9 @@ test("idempotency block fires with its own reason before saturation", async (t)
|
|
|
532
531
|
const second = await f.orchestrator.advance();
|
|
533
532
|
|
|
534
533
|
assert.equal(first.kind, "advanced");
|
|
535
|
-
assert.equal(second.kind, "
|
|
536
|
-
if (second.kind !== "
|
|
534
|
+
assert.equal(second.kind, "skipped");
|
|
535
|
+
if (second.kind !== "skipped") return;
|
|
537
536
|
assert.equal(second.reason, "idempotent advance: unit already active");
|
|
538
|
-
assert.equal(second.action, "pause");
|
|
539
537
|
});
|
|
540
538
|
|
|
541
539
|
test("completeActiveUnit clears in-flight idempotency and stops stale same-unit advance", async (t) => {
|
|
@@ -671,12 +669,12 @@ test("resume() clears idempotent lock and allows re-advance", async (t) => {
|
|
|
671
669
|
t.after(() => f.cleanup());
|
|
672
670
|
|
|
673
671
|
const first = await f.orchestrator.advance();
|
|
674
|
-
const
|
|
672
|
+
const idempotent = await f.orchestrator.advance();
|
|
675
673
|
const resumed = await f.orchestrator.resume();
|
|
676
674
|
const next = await f.orchestrator.advance();
|
|
677
675
|
|
|
678
676
|
assert.equal(first.kind, "advanced");
|
|
679
|
-
assert.equal(
|
|
677
|
+
assert.equal(idempotent.kind, "skipped");
|
|
680
678
|
assert.equal(resumed.kind, "resumed");
|
|
681
679
|
assert.equal(next.kind, "advanced");
|
|
682
680
|
});
|
|
@@ -686,11 +684,11 @@ test("start() clears prior idempotent lock", async (t) => {
|
|
|
686
684
|
t.after(() => f.cleanup());
|
|
687
685
|
|
|
688
686
|
await f.orchestrator.advance();
|
|
689
|
-
const
|
|
687
|
+
const idempotent = await f.orchestrator.advance();
|
|
690
688
|
const restarted = await f.orchestrator.start(SESSION_CONTEXT);
|
|
691
689
|
const next = await f.orchestrator.advance();
|
|
692
690
|
|
|
693
|
-
assert.equal(
|
|
691
|
+
assert.equal(idempotent.kind, "skipped");
|
|
694
692
|
assert.equal(restarted.kind, "started");
|
|
695
693
|
assert.equal(next.kind, "advanced");
|
|
696
694
|
});
|
|
@@ -700,24 +698,24 @@ test("stop() clears idempotent unit lock so advance can run again", async (t) =>
|
|
|
700
698
|
t.after(() => f.cleanup());
|
|
701
699
|
|
|
702
700
|
const first = await f.orchestrator.advance();
|
|
703
|
-
const
|
|
701
|
+
const idempotent = await f.orchestrator.advance();
|
|
704
702
|
const stopped = await f.orchestrator.stop("reset");
|
|
705
703
|
const second = await f.orchestrator.advance();
|
|
706
704
|
|
|
707
705
|
assert.equal(first.kind, "advanced");
|
|
708
|
-
assert.equal(
|
|
706
|
+
assert.equal(idempotent.kind, "skipped");
|
|
709
707
|
assert.equal(stopped.kind, "stopped");
|
|
710
708
|
assert.equal(second.kind, "advanced");
|
|
711
709
|
});
|
|
712
710
|
|
|
713
|
-
test("
|
|
711
|
+
test("idempotent path journals advance-skipped and records a health snapshot", async (t) => {
|
|
714
712
|
const f = makeFixture();
|
|
715
713
|
t.after(() => f.cleanup());
|
|
716
714
|
|
|
717
715
|
await f.orchestrator.advance();
|
|
718
716
|
await f.orchestrator.advance();
|
|
719
717
|
|
|
720
|
-
assert.ok(f.journalNames().includes("advance-
|
|
718
|
+
assert.ok(f.journalNames().includes("advance-skipped"));
|
|
721
719
|
});
|
|
722
720
|
|
|
723
721
|
// ─── Stuck-loop ring buffer (issue #5787) ──────────────────────────────────
|
|
@@ -761,13 +759,12 @@ test("stuck-loop: ring saturated with same unit blocks with action 'stop' and st
|
|
|
761
759
|
// First call advances.
|
|
762
760
|
assert.equal(results[0].kind, "advanced");
|
|
763
761
|
|
|
764
|
-
// Intermediate calls are
|
|
762
|
+
// Intermediate calls are skipped by idempotency (not stuck-loop yet).
|
|
765
763
|
for (let i = 1; i < STUCK_WINDOW_SIZE - 1; i++) {
|
|
766
764
|
const r = results[i];
|
|
767
|
-
assert.equal(r.kind, "
|
|
768
|
-
if (r.kind !== "
|
|
765
|
+
assert.equal(r.kind, "skipped", `round ${i} should be skipped`);
|
|
766
|
+
if (r.kind !== "skipped") return;
|
|
769
767
|
assert.equal(r.reason, "idempotent advance: unit already active");
|
|
770
|
-
assert.equal(r.action, "pause");
|
|
771
768
|
}
|
|
772
769
|
|
|
773
770
|
// The final call (ring now holds STUCK_WINDOW_SIZE copies) returns stuck-loop.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getUatBrowserToolSupportError,
|
|
5
|
+
hasUatBrowserToolSurface,
|
|
6
|
+
type UatType,
|
|
7
|
+
} from "../uat-policy.ts";
|
|
8
|
+
|
|
9
|
+
export const BROWSER_AUTOMATION_CONTRACT_TOOLS = {
|
|
10
|
+
piProvider: ["read", "browser_navigate"],
|
|
11
|
+
externalMcpClient: ["read", "mcp__gsd-browser__browser_navigate"],
|
|
12
|
+
externalMcpWildcard: ["read", "mcp__gsd-browser__*"],
|
|
13
|
+
otherBrowserMcp: ["read", "mcp__browser-uat__*"],
|
|
14
|
+
workflowOnly: ["read", "mcp__gsd-workflow__*"],
|
|
15
|
+
withoutBrowser: ["read", "gsd_uat_exec"],
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export function assertBrowserAutomationContractAvailable(tools: readonly string[]): void {
|
|
19
|
+
assert.equal(hasUatBrowserToolSurface(tools), true, `${tools.join(", ")} should satisfy the Browser Automation Contract`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function assertBrowserAutomationContractMissing(tools: readonly string[] | undefined): void {
|
|
23
|
+
assert.equal(hasUatBrowserToolSurface(tools), false, `${tools?.join(", ") ?? "undefined"} should not satisfy the Browser Automation Contract`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function assertBrowserBackedUatCanDispatch(options: {
|
|
27
|
+
uatType: UatType;
|
|
28
|
+
activeTools: readonly string[] | undefined;
|
|
29
|
+
registeredTools?: readonly string[];
|
|
30
|
+
}): void {
|
|
31
|
+
assert.equal(
|
|
32
|
+
getUatBrowserToolSupportError({
|
|
33
|
+
...options,
|
|
34
|
+
milestoneId: "M001",
|
|
35
|
+
sliceId: "S01",
|
|
36
|
+
}),
|
|
37
|
+
null,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BROWSER_CONTRACT_TOOL_NAMES,
|
|
6
|
+
BROWSER_EVIDENCE_SIGNAL_TOOL_NAMES,
|
|
7
|
+
hasBrowserContractPrefix,
|
|
8
|
+
isBrowserContractToolName,
|
|
9
|
+
} from "../../shared/browser-contract.ts";
|
|
10
|
+
import { isUatBrowserToolName } from "../uat-policy.ts";
|
|
11
|
+
import { BROWSER_REQUIREMENT_RE, BROWSER_RUNTIME_RE } from "../browser-evidence.ts";
|
|
12
|
+
|
|
13
|
+
// Note: RUN_UAT_BROWSER_TOOL_NAMES and MANAGED_GSD_BROWSER_TOOL_NAMES are
|
|
14
|
+
// reference-equal aliases of BROWSER_CONTRACT_TOOL_NAMES, and the managed
|
|
15
|
+
// adapter's spec table is Record-keyed by BrowserContractToolName — both
|
|
16
|
+
// derivations are pinned by the type system, not by runtime assertions here.
|
|
17
|
+
describe("Browser Automation Contract parity", () => {
|
|
18
|
+
it("every contract name satisfies the UAT browser-tool predicate, bare and MCP-prefixed", () => {
|
|
19
|
+
for (const name of BROWSER_CONTRACT_TOOL_NAMES) {
|
|
20
|
+
assert.equal(isUatBrowserToolName(name), true, name);
|
|
21
|
+
assert.equal(isUatBrowserToolName(`mcp__gsd-browser__${name}`), true, `mcp__gsd-browser__${name}`);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("contract names are canonical browser_* names with no duplicates", () => {
|
|
26
|
+
assert.equal(new Set(BROWSER_CONTRACT_TOOL_NAMES).size, BROWSER_CONTRACT_TOOL_NAMES.length);
|
|
27
|
+
for (const name of BROWSER_CONTRACT_TOOL_NAMES) {
|
|
28
|
+
assert.equal(hasBrowserContractPrefix(name), true, name);
|
|
29
|
+
assert.equal(isBrowserContractToolName(name), true, name);
|
|
30
|
+
}
|
|
31
|
+
assert.equal(isBrowserContractToolName("browser_not_a_real_tool"), false);
|
|
32
|
+
assert.equal(hasBrowserContractPrefix("gsd_uat_exec"), false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("evidence-signal names stay a subset of the contract and drive the detection regexes", () => {
|
|
36
|
+
for (const name of BROWSER_EVIDENCE_SIGNAL_TOOL_NAMES) {
|
|
37
|
+
assert.equal(isBrowserContractToolName(name), true, name);
|
|
38
|
+
// Identifier-shaped names keep the regex splice in browser-evidence.ts escape-free.
|
|
39
|
+
assert.match(name, /^browser_[a-z_]+$/);
|
|
40
|
+
assert.match(`Verified via ${name} call`, BROWSER_REQUIREMENT_RE);
|
|
41
|
+
assert.match(`Verified via ${name} call`, BROWSER_RUNTIME_RE);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
prepareBrowserDaemonForUat,
|
|
6
|
+
shouldWarmBrowserDaemonForUat,
|
|
7
|
+
} from "../browser-daemon-auto-prep.ts";
|
|
8
|
+
import { commitBrowserEngineResolution } from "../../browser-tools/engine/selection.ts";
|
|
9
|
+
import { resolveGsdBrowserCliAvailability } from "../../shared/gsd-browser-cli.ts";
|
|
10
|
+
|
|
11
|
+
const GSD_BROWSER_ENGINE = { GSD_BROWSER_ENGINE: "gsd-browser" } as const;
|
|
12
|
+
|
|
13
|
+
test("shouldWarmBrowserDaemonForUat skips artifact-driven UAT", () => {
|
|
14
|
+
assert.equal(
|
|
15
|
+
shouldWarmBrowserDaemonForUat({
|
|
16
|
+
uatType: "artifact-driven",
|
|
17
|
+
sessionProvider: "claude-code",
|
|
18
|
+
projectRoot: "/tmp/project",
|
|
19
|
+
}),
|
|
20
|
+
false,
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("shouldWarmBrowserDaemonForUat enables Claude Code browser UAT when gsd-browser is available", (t) => {
|
|
25
|
+
const availability = resolveGsdBrowserCliAvailability();
|
|
26
|
+
if (!availability.available) {
|
|
27
|
+
t.skip("bundled gsd-browser CLI unavailable");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
assert.equal(
|
|
31
|
+
shouldWarmBrowserDaemonForUat({
|
|
32
|
+
uatType: "browser-executable",
|
|
33
|
+
sessionProvider: "claude-code",
|
|
34
|
+
sessionAuthMode: "externalCli",
|
|
35
|
+
projectRoot: "/tmp/project",
|
|
36
|
+
env: GSD_BROWSER_ENGINE,
|
|
37
|
+
}),
|
|
38
|
+
true,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("shouldWarmBrowserDaemonForUat enables warm-up for Claude Code oauth/apiKey when engine is gsd-browser", (t) => {
|
|
43
|
+
const availability = resolveGsdBrowserCliAvailability();
|
|
44
|
+
if (!availability.available) {
|
|
45
|
+
t.skip("bundled gsd-browser CLI unavailable");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const sessionAuthMode of ["oauth", "apiKey"] as const) {
|
|
49
|
+
assert.equal(
|
|
50
|
+
shouldWarmBrowserDaemonForUat({
|
|
51
|
+
uatType: "browser-executable",
|
|
52
|
+
sessionProvider: "claude-code",
|
|
53
|
+
sessionAuthMode,
|
|
54
|
+
sessionBaseUrl: "https://api.anthropic.com",
|
|
55
|
+
projectRoot: "/tmp/project",
|
|
56
|
+
env: GSD_BROWSER_ENGINE,
|
|
57
|
+
}),
|
|
58
|
+
true,
|
|
59
|
+
`expected warm-up for sessionAuthMode=${sessionAuthMode}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("shouldWarmBrowserDaemonForUat skips legacy Playwright engine for Claude Code", () => {
|
|
65
|
+
assert.equal(
|
|
66
|
+
shouldWarmBrowserDaemonForUat({
|
|
67
|
+
uatType: "browser-executable",
|
|
68
|
+
sessionProvider: "claude-code",
|
|
69
|
+
sessionAuthMode: "oauth",
|
|
70
|
+
projectRoot: "/tmp/project",
|
|
71
|
+
env: { GSD_BROWSER_ENGINE: "legacy" },
|
|
72
|
+
}),
|
|
73
|
+
false,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("shouldWarmBrowserDaemonForUat uses session-committed ambient engine for non-Claude providers", () => {
|
|
78
|
+
const projectRoot = "/tmp/ambient-engine-project";
|
|
79
|
+
commitBrowserEngineResolution(projectRoot, {
|
|
80
|
+
engine: "legacy",
|
|
81
|
+
source: "probe",
|
|
82
|
+
reason: "gsd-browser daemon connect failed (test); using legacy Playwright",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
assert.equal(
|
|
86
|
+
shouldWarmBrowserDaemonForUat({
|
|
87
|
+
uatType: "browser-executable",
|
|
88
|
+
sessionProvider: "openai",
|
|
89
|
+
projectRoot,
|
|
90
|
+
}),
|
|
91
|
+
false,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("shouldWarmBrowserDaemonForUat skips when browser MCP is disabled", () => {
|
|
96
|
+
assert.equal(
|
|
97
|
+
shouldWarmBrowserDaemonForUat({
|
|
98
|
+
uatType: "browser-executable",
|
|
99
|
+
sessionProvider: "claude-code",
|
|
100
|
+
projectRoot: "/tmp/project",
|
|
101
|
+
env: { GSD_BROWSER_MCP_ENABLED: "0" },
|
|
102
|
+
}),
|
|
103
|
+
false,
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("shouldWarmBrowserDaemonForUat skips when warm-up is disabled", () => {
|
|
108
|
+
assert.equal(
|
|
109
|
+
shouldWarmBrowserDaemonForUat({
|
|
110
|
+
uatType: "browser-executable",
|
|
111
|
+
sessionProvider: "claude-code",
|
|
112
|
+
projectRoot: "/tmp/project",
|
|
113
|
+
env: { GSD_BROWSER_WARMUP: "0" },
|
|
114
|
+
}),
|
|
115
|
+
false,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("prepareBrowserDaemonForUat returns null when warm-up is not required", () => {
|
|
120
|
+
assert.equal(
|
|
121
|
+
prepareBrowserDaemonForUat({
|
|
122
|
+
uatType: "artifact-driven",
|
|
123
|
+
sessionProvider: "claude-code",
|
|
124
|
+
sessionAuthMode: "externalCli",
|
|
125
|
+
projectRoot: "/tmp/example-project",
|
|
126
|
+
}),
|
|
127
|
+
null,
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("prepareBrowserDaemonForUat returns actionable error when daemon start fails", () => {
|
|
132
|
+
const error = prepareBrowserDaemonForUat({
|
|
133
|
+
uatType: "browser-executable",
|
|
134
|
+
sessionProvider: "claude-code",
|
|
135
|
+
sessionAuthMode: "externalCli",
|
|
136
|
+
projectRoot: "/tmp/example-project",
|
|
137
|
+
env: {
|
|
138
|
+
...GSD_BROWSER_ENGINE,
|
|
139
|
+
GSD_BROWSER_MCP_COMMAND: "/definitely/missing/gsd-browser",
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
assert.match(error ?? "", /gsd-browser daemon failed to start/i);
|
|
144
|
+
});
|