@opengsd/gsd-pi 1.2.0-dev.b1abb545 → 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 +5 -2
- 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/models.js +9 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +38 -6
- package/dist/resources/extensions/gsd/auto/orchestrator.js +40 -9
- package/dist/resources/extensions/gsd/auto/phases.js +6 -1
- 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 +19 -8
- package/dist/resources/extensions/gsd/auto-prompts.js +15 -10
- package/dist/resources/extensions/gsd/auto-start.js +21 -21
- 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-repair.js +10 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +35 -352
- package/dist/resources/extensions/gsd/auto.js +8 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +32 -12
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +151 -20
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +30 -4
- 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 +5 -15
- package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
- package/dist/resources/extensions/gsd/constants.js +0 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
- package/dist/resources/extensions/gsd/db/engine.js +755 -0
- package/dist/resources/extensions/gsd/db/queries.js +372 -0
- package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
- package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
- package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
- package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
- package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
- package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +5 -11
- package/dist/resources/extensions/gsd/doctor-format.js +9 -6
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +21 -16
- 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/git-service.js +1 -0
- package/dist/resources/extensions/gsd/gitignore.js +3 -0
- package/dist/resources/extensions/gsd/gsd-db.js +171 -2048
- package/dist/resources/extensions/gsd/guidance.js +98 -0
- package/dist/resources/extensions/gsd/guided-flow.js +51 -5
- 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 +20 -9
- package/dist/resources/extensions/gsd/migration-auto-check.js +24 -3
- package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
- package/dist/resources/extensions/gsd/model-router.js +3 -0
- package/dist/resources/extensions/gsd/notification-store.js +11 -4
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +11 -7
- package/dist/resources/extensions/gsd/paths.js +37 -24
- 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/preferences.js +14 -0
- 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 +41 -87
- package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
- package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
- package/dist/resources/extensions/gsd/state.js +1 -20
- package/dist/resources/extensions/gsd/status-guards.js +56 -8
- 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/complete-slice.js +24 -43
- package/dist/resources/extensions/gsd/tools/exec-tool.js +10 -8
- package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
- package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
- package/dist/resources/extensions/gsd/uat-policy.js +2 -1
- package/dist/resources/extensions/gsd/undo.js +8 -7
- 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 +293 -0
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
- package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
- package/dist/resources/extensions/gsd/worktree-root.js +28 -6
- package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
- package/dist/resources/extensions/gsd/worktree-session-state.js +12 -11
- 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/gsd-browser/SKILL.md +1 -1
- 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 +10 -10
- 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/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +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/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
- 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 -11
- 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 +8 -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 +46 -21
- 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/image-models.generated.d.ts +2 -2
- package/packages/pi-ai/dist/image-models.generated.js +6 -6
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +478 -484
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +500 -533
- 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/capability-patches.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.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 +5 -2
- 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/models.ts +9 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +40 -4
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +46 -10
- package/src/resources/extensions/gsd/auto/phases.ts +10 -1
- 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 +25 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -26
- package/src/resources/extensions/gsd/auto-start.ts +21 -22
- 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-repair.ts +13 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
- package/src/resources/extensions/gsd/auto.ts +20 -24
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +33 -12
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +180 -15
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
- 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 +5 -16
- package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
- package/src/resources/extensions/gsd/constants.ts +0 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
- package/src/resources/extensions/gsd/db/engine.ts +809 -0
- package/src/resources/extensions/gsd/db/queries.ts +453 -0
- package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
- package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
- package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
- package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
- package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
- package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
- package/src/resources/extensions/gsd/doctor-format.ts +12 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
- 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/git-service.ts +1 -0
- package/src/resources/extensions/gsd/gitignore.ts +3 -0
- package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
- package/src/resources/extensions/gsd/guidance.ts +139 -0
- package/src/resources/extensions/gsd/guided-flow.ts +50 -5
- 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 +18 -7
- package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
- package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
- package/src/resources/extensions/gsd/model-router.ts +3 -0
- package/src/resources/extensions/gsd/notification-store.ts +26 -3
- package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
- package/src/resources/extensions/gsd/paths.ts +42 -22
- 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/preferences.ts +18 -0
- 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 +47 -88
- package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
- package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
- package/src/resources/extensions/gsd/state.ts +4 -21
- package/src/resources/extensions/gsd/status-guards.ts +59 -8
- package/src/resources/extensions/gsd/stop-notice.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -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/auto-paused-ui-cleanup.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
- 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 +44 -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/evidence-xref-gsd-exec.test.ts +157 -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/file-change-validator.test.ts +33 -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 +58 -15
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +74 -59
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
- 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/migration-auto-check.test.ts +85 -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/recovery-classification-illegal-transition.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
- package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -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/tests/worktree-lifecycle.test.ts +41 -4
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
- package/src/resources/extensions/gsd/tools/exec-tool.ts +9 -8
- package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
- package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
- package/src/resources/extensions/gsd/uat-policy.ts +2 -1
- package/src/resources/extensions/gsd/undo.ts +9 -8
- 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 +314 -0
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
- package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
- package/src/resources/extensions/gsd/worktree-root.ts +29 -6
- package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
- package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
- 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/gsd-browser/SKILL.md +1 -1
- package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
- /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_ssgManifest.js +0 -0
|
@@ -73,4 +73,48 @@ describe("clear stale pending auto-start (#3667)", () => {
|
|
|
73
73
|
"pending auto-start gate must clear stale map entries for completed discussions",
|
|
74
74
|
);
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
test("guided-flow recovers a finished-but-unconsumed discussion instead of dead-ending", () => {
|
|
78
|
+
// CONTEXT exists + no live turn means the discussion completed but the
|
|
79
|
+
// agent_end handoff never consumed the entry (e.g. an external-engine
|
|
80
|
+
// post-hoc gate re-arm wiped the depth verification after the save).
|
|
81
|
+
// Without recovery, every /gsd prints "Discussion already in progress"
|
|
82
|
+
// forever: the stale heuristic requires CONTEXT to be absent and
|
|
83
|
+
// discussPlanComplete requires a ROADMAP that planning never produced.
|
|
84
|
+
const source = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
|
|
85
|
+
assert.ok(
|
|
86
|
+
source.includes("milestoneHasContext && !isAgentTurnInFlight(ctx)"),
|
|
87
|
+
"pending-entry guard must have a recovery branch for CONTEXT-present, no-turn-in-flight entries",
|
|
88
|
+
);
|
|
89
|
+
assert.ok(
|
|
90
|
+
source.includes("extractDepthVerificationMilestoneId(pendingGateId) === entry.milestoneId"),
|
|
91
|
+
"recovery must only clear a pending gate belonging to the entry's own milestone",
|
|
92
|
+
);
|
|
93
|
+
assert.ok(
|
|
94
|
+
source.includes("if (checkAutoStartAfterDiscuss(basePath)) return;"),
|
|
95
|
+
"recovery must re-run the discuss→auto handoff after clearing the stale gate",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("guided-flow does not treat a live discuss turn as a stale pending entry", () => {
|
|
100
|
+
const source = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
|
|
101
|
+
assert.ok(
|
|
102
|
+
source.includes("!isAgentTurnInFlight(ctx)"),
|
|
103
|
+
"stale-entry deletion must be gated on no agent turn being in flight — a dispatched " +
|
|
104
|
+
"discuss turn can think for over 30s before writing its first artifact, and deleting " +
|
|
105
|
+
"its entry re-dispatches the workflow (duplicate interview + duplicate completion message)",
|
|
106
|
+
);
|
|
107
|
+
assert.ok(
|
|
108
|
+
source.includes('const milestoneHasDraft = !!resolveMilestoneFile(basePath, entry.milestoneId, "CONTEXT-DRAFT");'),
|
|
109
|
+
"stale-entry check must treat an existing CONTEXT-DRAFT as proof of an in-progress interview",
|
|
110
|
+
);
|
|
111
|
+
assert.ok(
|
|
112
|
+
source.includes("!milestoneHasDraft"),
|
|
113
|
+
"stale-entry deletion must require the CONTEXT-DRAFT to be absent",
|
|
114
|
+
);
|
|
115
|
+
assert.ok(
|
|
116
|
+
source.includes("ctx.hasPendingMessages"),
|
|
117
|
+
"in-flight detection must also cover dispatched-but-not-yet-started queued messages",
|
|
118
|
+
);
|
|
119
|
+
});
|
|
76
120
|
});
|
|
@@ -472,18 +472,19 @@ test("auto-dispatch needs-attention pause message references /gsd verdict", asyn
|
|
|
472
472
|
}
|
|
473
473
|
});
|
|
474
474
|
|
|
475
|
-
test("
|
|
475
|
+
test("guidance.ts needs-remediation blocker messages reference /gsd verdict", async () => {
|
|
476
476
|
// We don't need to invoke deriveState — just assert the substring is in the
|
|
477
|
-
// source. The blocker strings are constructed
|
|
478
|
-
// verbatim, so a static check is sufficient and avoids
|
|
479
|
-
|
|
480
|
-
|
|
477
|
+
// source. The blocker strings are constructed in the guidance catalog and
|
|
478
|
+
// shipped to the user verbatim, so a static check is sufficient and avoids
|
|
479
|
+
// fragile DB setup.
|
|
480
|
+
const guidanceSource = readFileSync(
|
|
481
|
+
new URL("../guidance.ts", import.meta.url).pathname,
|
|
481
482
|
"utf-8",
|
|
482
483
|
);
|
|
483
|
-
const occurrences =
|
|
484
|
+
const occurrences = guidanceSource.match(/`\/gsd verdict /g) ?? [];
|
|
484
485
|
assert.ok(
|
|
485
486
|
occurrences.length >= 2,
|
|
486
|
-
`expected at least 2 references to /gsd verdict in
|
|
487
|
+
`expected at least 2 references to /gsd verdict in guidance.ts blockers, found ${occurrences.length}`,
|
|
487
488
|
);
|
|
488
489
|
});
|
|
489
490
|
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the destructive-command confirmation escape hatch.
|
|
3
|
+
*
|
|
4
|
+
* Regression target: the destructive-guard hard block had no confirmation
|
|
5
|
+
* path. Re-issuing a force push after confirming via ask_user_questions hit
|
|
6
|
+
* the identical HARD BLOCK every time — an unwinnable loop. These tests pin
|
|
7
|
+
* the block → confirm → allow-once flow and its safety invariants.
|
|
8
|
+
*
|
|
9
|
+
* Two layers of coverage:
|
|
10
|
+
* 1. Unit — the confirmation-token module in isolation (block/confirm/consume
|
|
11
|
+
* and every safety invariant).
|
|
12
|
+
* 2. Integration — the real registerHooks() bash tool_call guard +
|
|
13
|
+
* ask_user_questions tool_result handler driven end-to-end, proving the
|
|
14
|
+
* loop is escapable through the actual wiring, not just the helper module.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import test from "node:test";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
|
|
20
|
+
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
21
|
+
import { classifyCommand } from "../safety/destructive-guard.ts";
|
|
22
|
+
import {
|
|
23
|
+
DESTRUCTIVE_CONFIRM_GATE_MARKER,
|
|
24
|
+
confirmDestructiveCommand,
|
|
25
|
+
consumeDestructiveConfirmation,
|
|
26
|
+
isDestructiveConfirmGateId,
|
|
27
|
+
normalizeDestructiveCommand,
|
|
28
|
+
peekPendingDestructiveCommand,
|
|
29
|
+
requestDestructiveConfirmation,
|
|
30
|
+
resetDestructiveConfirmation,
|
|
31
|
+
} from "../safety/destructive-confirmation.ts";
|
|
32
|
+
|
|
33
|
+
const BASE = "/tmp/gsd-destructive-confirm-test";
|
|
34
|
+
const FORCE_PUSH = "git push --force-with-lease origin feature/PO-566-chart-supply-chain";
|
|
35
|
+
|
|
36
|
+
function blocked(command: string, basePath: string): boolean {
|
|
37
|
+
// Mirror the guard's decision: a classified-destructive command is blocked
|
|
38
|
+
// unless a matching confirmation token is consumed in this same check.
|
|
39
|
+
if (!classifyCommand(command).destructive) return false;
|
|
40
|
+
return !consumeDestructiveConfirmation(command, basePath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test("repro: destructive command loops forever without confirmation", () => {
|
|
44
|
+
resetDestructiveConfirmation(BASE);
|
|
45
|
+
// Every attempt blocks — this is the bug the escape hatch fixes.
|
|
46
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "first attempt blocks");
|
|
47
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "retry still blocks");
|
|
48
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "and again — unwinnable loop");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("block then confirm then retry allows the exact command once", () => {
|
|
52
|
+
resetDestructiveConfirmation(BASE);
|
|
53
|
+
|
|
54
|
+
// 1. Guard blocks and records the pending command.
|
|
55
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "initial attempt blocks");
|
|
56
|
+
requestDestructiveConfirmation(FORCE_PUSH, BASE);
|
|
57
|
+
assert.equal(peekPendingDestructiveCommand(BASE), normalizeDestructiveCommand(FORCE_PUSH));
|
|
58
|
+
|
|
59
|
+
// 2. User confirms via ask_user_questions affirmative answer.
|
|
60
|
+
const confirmed = confirmDestructiveCommand(BASE);
|
|
61
|
+
assert.equal(confirmed, normalizeDestructiveCommand(FORCE_PUSH));
|
|
62
|
+
|
|
63
|
+
// 3. Retry in the same turn now passes.
|
|
64
|
+
assert.equal(blocked(FORCE_PUSH, BASE), false, "confirmed command passes once");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("confirmation is one-shot — a second destructive command re-blocks", () => {
|
|
68
|
+
resetDestructiveConfirmation(BASE);
|
|
69
|
+
|
|
70
|
+
requestDestructiveConfirmation(FORCE_PUSH, BASE);
|
|
71
|
+
confirmDestructiveCommand(BASE);
|
|
72
|
+
assert.equal(blocked(FORCE_PUSH, BASE), false, "first run consumes the token");
|
|
73
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "identical second run re-blocks");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("confirmation is command-bound — a different command is not approved", () => {
|
|
77
|
+
resetDestructiveConfirmation(BASE);
|
|
78
|
+
|
|
79
|
+
requestDestructiveConfirmation(FORCE_PUSH, BASE);
|
|
80
|
+
confirmDestructiveCommand(BASE);
|
|
81
|
+
|
|
82
|
+
// A different destructive command must not ride the force-push confirmation.
|
|
83
|
+
assert.equal(blocked("rm -rf node_modules", BASE), true, "unrelated destructive cmd re-blocks");
|
|
84
|
+
// The original token is still intact for its exact command.
|
|
85
|
+
assert.equal(blocked(FORCE_PUSH, BASE), false, "original confirmed command still passes once");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("cosmetic whitespace reformatting still matches the confirmed token", () => {
|
|
89
|
+
resetDestructiveConfirmation(BASE);
|
|
90
|
+
|
|
91
|
+
requestDestructiveConfirmation("git push --force origin main", BASE);
|
|
92
|
+
confirmDestructiveCommand(BASE);
|
|
93
|
+
assert.equal(blocked("git push --force origin main", BASE), false, "whitespace-normalized match passes");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("requesting a new confirmation invalidates a stale confirmed token", () => {
|
|
97
|
+
resetDestructiveConfirmation(BASE);
|
|
98
|
+
|
|
99
|
+
requestDestructiveConfirmation(FORCE_PUSH, BASE);
|
|
100
|
+
confirmDestructiveCommand(BASE);
|
|
101
|
+
// New block for a different command before the first was consumed.
|
|
102
|
+
requestDestructiveConfirmation("git reset --hard HEAD~3", BASE);
|
|
103
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "old confirmation no longer valid after new request");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("confirm with nothing pending returns null and approves nothing", () => {
|
|
107
|
+
resetDestructiveConfirmation(BASE);
|
|
108
|
+
assert.equal(confirmDestructiveCommand(BASE), null, "no pending command -> null");
|
|
109
|
+
assert.equal(blocked(FORCE_PUSH, BASE), true, "no spurious approval");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("tokens are isolated per basePath", () => {
|
|
113
|
+
const a = "/tmp/gsd-confirm-ws-a";
|
|
114
|
+
const b = "/tmp/gsd-confirm-ws-b";
|
|
115
|
+
resetDestructiveConfirmation(a);
|
|
116
|
+
resetDestructiveConfirmation(b);
|
|
117
|
+
|
|
118
|
+
requestDestructiveConfirmation(FORCE_PUSH, a);
|
|
119
|
+
confirmDestructiveCommand(a);
|
|
120
|
+
|
|
121
|
+
assert.equal(blocked(FORCE_PUSH, b), true, "workspace B is unaffected by workspace A's confirmation");
|
|
122
|
+
assert.equal(blocked(FORCE_PUSH, a), false, "workspace A still passes once");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("gate id marker detection", () => {
|
|
126
|
+
assert.equal(isDestructiveConfirmGateId(`${DESTRUCTIVE_CONFIRM_GATE_MARKER}_push`), true);
|
|
127
|
+
assert.equal(isDestructiveConfirmGateId("depth_verification_M001"), false);
|
|
128
|
+
assert.equal(isDestructiveConfirmGateId(undefined), false);
|
|
129
|
+
assert.equal(isDestructiveConfirmGateId(123), false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("non-destructive commands are never gated regardless of token state", () => {
|
|
133
|
+
resetDestructiveConfirmation(BASE);
|
|
134
|
+
assert.equal(blocked("git status", BASE), false);
|
|
135
|
+
assert.equal(blocked("ls -la", BASE), false);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ─── Integration: real registerHooks() wiring, driven end-to-end ────────────
|
|
139
|
+
// The unit tests above exercise the token module directly. These drive the
|
|
140
|
+
// actual registered bash tool_call guard and ask_user_questions tool_result
|
|
141
|
+
// handler, proving the loop the user hit is escapable through the real hooks —
|
|
142
|
+
// not just the helper. Mirrors the harness in
|
|
143
|
+
// register-hooks-depth-verification.test.ts.
|
|
144
|
+
|
|
145
|
+
type Handler = (event: any, ctx?: any) => Promise<any> | any;
|
|
146
|
+
|
|
147
|
+
function makeHookHarness(): {
|
|
148
|
+
handlers: Map<string, Handler[]>;
|
|
149
|
+
pi: any;
|
|
150
|
+
} {
|
|
151
|
+
const handlers = new Map<string, Handler[]>();
|
|
152
|
+
const pi = {
|
|
153
|
+
on(event: string, handler: Handler) {
|
|
154
|
+
const existing = handlers.get(event) ?? [];
|
|
155
|
+
existing.push(handler);
|
|
156
|
+
handlers.set(event, existing);
|
|
157
|
+
},
|
|
158
|
+
} as any;
|
|
159
|
+
return { handlers, pi };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Run every bash tool_call guard and return the first block result, if any. */
|
|
163
|
+
async function runBashGuard(
|
|
164
|
+
handlers: Map<string, Handler[]>,
|
|
165
|
+
command: string,
|
|
166
|
+
ctx: any,
|
|
167
|
+
): Promise<{ block?: boolean; reason?: string } | undefined> {
|
|
168
|
+
let block: { block?: boolean; reason?: string } | undefined;
|
|
169
|
+
for (const handler of handlers.get("tool_call") ?? []) {
|
|
170
|
+
const result = await handler(
|
|
171
|
+
{ toolCallId: "bash-1", toolName: "bash", input: { command } },
|
|
172
|
+
ctx,
|
|
173
|
+
);
|
|
174
|
+
if (result?.block) block = result;
|
|
175
|
+
}
|
|
176
|
+
return block;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Deliver an affirmative ask_user_questions answer for a destructive gate. */
|
|
180
|
+
async function answerConfirmGate(
|
|
181
|
+
handlers: Map<string, Handler[]>,
|
|
182
|
+
questionId: string,
|
|
183
|
+
ctx: any,
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
const questions = [
|
|
186
|
+
{
|
|
187
|
+
id: questionId,
|
|
188
|
+
question: "Run this destructive command?",
|
|
189
|
+
options: [{ label: "Yes, run it (Recommended)" }, { label: "No, cancel" }],
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
for (const handler of handlers.get("tool_result") ?? []) {
|
|
193
|
+
await handler(
|
|
194
|
+
{
|
|
195
|
+
toolName: "ask_user_questions",
|
|
196
|
+
input: { questions },
|
|
197
|
+
details: {
|
|
198
|
+
response: { answers: { [questionId]: { selected: "Yes, run it (Recommended)" } } },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
ctx,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
test("integration: real hooks block a force push, then allow it once after confirmation", async (t) => {
|
|
207
|
+
const dir = "/tmp/gsd-destructive-confirm-int-allow";
|
|
208
|
+
const ctx = { cwd: dir, ui: { notify: () => undefined } } as any;
|
|
209
|
+
resetDestructiveConfirmation(dir);
|
|
210
|
+
t.after(() => resetDestructiveConfirmation(dir));
|
|
211
|
+
|
|
212
|
+
const { handlers, pi } = makeHookHarness();
|
|
213
|
+
registerHooks(pi, []);
|
|
214
|
+
|
|
215
|
+
// 1. First attempt is hard-blocked by the real guard, with instructions that
|
|
216
|
+
// name the destructive_confirm gate.
|
|
217
|
+
const firstBlock = await runBashGuard(handlers, FORCE_PUSH, ctx);
|
|
218
|
+
assert.equal(firstBlock?.block, true, "real guard blocks the first attempt");
|
|
219
|
+
assert.match(firstBlock?.reason ?? "", /HARD BLOCK: destructive Bash command/);
|
|
220
|
+
assert.match(firstBlock?.reason ?? "", /force push/);
|
|
221
|
+
assert.match(firstBlock?.reason ?? "", /destructive_confirm/);
|
|
222
|
+
|
|
223
|
+
// 2. User answers an affirmative destructive_confirm gate.
|
|
224
|
+
await answerConfirmGate(handlers, "destructive_confirm_force_push", ctx);
|
|
225
|
+
|
|
226
|
+
// 3. Re-issuing the exact command in the same turn now runs once (no block).
|
|
227
|
+
const secondAttempt = await runBashGuard(handlers, FORCE_PUSH, ctx);
|
|
228
|
+
assert.equal(secondAttempt, undefined, "confirmed command passes the real guard once");
|
|
229
|
+
|
|
230
|
+
// 4. One-shot: an immediate identical third attempt re-blocks.
|
|
231
|
+
const thirdAttempt = await runBashGuard(handlers, FORCE_PUSH, ctx);
|
|
232
|
+
assert.equal(thirdAttempt?.block, true, "second run of the same command re-blocks (one-shot)");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("integration: declining the confirm gate leaves the command blocked", async (t) => {
|
|
236
|
+
const dir = "/tmp/gsd-destructive-confirm-int-decline";
|
|
237
|
+
const ctx = { cwd: dir, ui: { notify: () => undefined } } as any;
|
|
238
|
+
resetDestructiveConfirmation(dir);
|
|
239
|
+
t.after(() => resetDestructiveConfirmation(dir));
|
|
240
|
+
|
|
241
|
+
const { handlers, pi } = makeHookHarness();
|
|
242
|
+
registerHooks(pi, []);
|
|
243
|
+
|
|
244
|
+
assert.equal((await runBashGuard(handlers, FORCE_PUSH, ctx))?.block, true, "first attempt blocks");
|
|
245
|
+
|
|
246
|
+
// Decline: the non-affirmative (non-first) option must NOT promote a token.
|
|
247
|
+
const questions = [
|
|
248
|
+
{
|
|
249
|
+
id: "destructive_confirm_force_push",
|
|
250
|
+
question: "Run this destructive command?",
|
|
251
|
+
options: [{ label: "Yes, run it (Recommended)" }, { label: "No, cancel" }],
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
for (const handler of handlers.get("tool_result") ?? []) {
|
|
255
|
+
await handler(
|
|
256
|
+
{
|
|
257
|
+
toolName: "ask_user_questions",
|
|
258
|
+
input: { questions },
|
|
259
|
+
details: {
|
|
260
|
+
response: { answers: { "destructive_confirm_force_push": { selected: "No, cancel" } } },
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
ctx,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
assert.equal(
|
|
268
|
+
(await runBashGuard(handlers, FORCE_PUSH, ctx))?.block,
|
|
269
|
+
true,
|
|
270
|
+
"declined command stays blocked",
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("integration: a confirm token does not leak to a different destructive command", async (t) => {
|
|
275
|
+
const dir = "/tmp/gsd-destructive-confirm-int-bound";
|
|
276
|
+
const ctx = { cwd: dir, ui: { notify: () => undefined } } as any;
|
|
277
|
+
resetDestructiveConfirmation(dir);
|
|
278
|
+
t.after(() => resetDestructiveConfirmation(dir));
|
|
279
|
+
|
|
280
|
+
const { handlers, pi } = makeHookHarness();
|
|
281
|
+
registerHooks(pi, []);
|
|
282
|
+
|
|
283
|
+
await runBashGuard(handlers, FORCE_PUSH, ctx);
|
|
284
|
+
await answerConfirmGate(handlers, "destructive_confirm_force_push", ctx);
|
|
285
|
+
|
|
286
|
+
// A different destructive command must not ride the force-push confirmation.
|
|
287
|
+
// In the real guard, blocking this command also records it as pending, which
|
|
288
|
+
// invalidates the stale force-push token (stale-token safety invariant) — so
|
|
289
|
+
// the original confirmation is consumed/cleared, not left dangling.
|
|
290
|
+
assert.equal(
|
|
291
|
+
(await runBashGuard(handlers, "rm -rf build", ctx))?.block,
|
|
292
|
+
true,
|
|
293
|
+
"unrelated destructive command is still blocked",
|
|
294
|
+
);
|
|
295
|
+
// Because the unrelated block invalidated the stale token, the original
|
|
296
|
+
// force-push now re-blocks too: a confirmation never survives an intervening
|
|
297
|
+
// destructive command. This is stricter (safer) than per-command isolation.
|
|
298
|
+
assert.equal(
|
|
299
|
+
(await runBashGuard(handlers, FORCE_PUSH, ctx))?.block,
|
|
300
|
+
true,
|
|
301
|
+
"force-push token was invalidated by the intervening destructive block",
|
|
302
|
+
);
|
|
303
|
+
});
|
|
@@ -9,6 +9,7 @@ import { join } from "node:path";
|
|
|
9
9
|
|
|
10
10
|
import { DISPATCH_RULES, type DispatchContext } from "../auto-dispatch.ts";
|
|
11
11
|
import type { GSDState } from "../types.ts";
|
|
12
|
+
import { BROWSER_AUTOMATION_CONTRACT_TOOLS } from "./browser-automation-contract-fixture.ts";
|
|
12
13
|
|
|
13
14
|
type DispatchRuleEntry = (typeof DISPATCH_RULES)[number];
|
|
14
15
|
|
|
@@ -80,7 +81,7 @@ test("run-uat browser preflight uses registered tools when the active surface is
|
|
|
80
81
|
assert.match(blocked?.action === "stop" ? blocked.reason : "", /run-uat tool surface has none/);
|
|
81
82
|
|
|
82
83
|
const dispatched = await runUatRule().match(makeContext(basePath, {
|
|
83
|
-
registeredTools: [
|
|
84
|
+
registeredTools: [...BROWSER_AUTOMATION_CONTRACT_TOOLS.piProvider],
|
|
84
85
|
}));
|
|
85
86
|
assert.equal(dispatched?.action, "dispatch");
|
|
86
87
|
assert.equal(dispatched?.action === "dispatch" ? dispatched.unitType : undefined, "run-uat");
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { createBashTool } from "@gsd/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
import { registerDynamicTools } from "../bootstrap/dynamic-tools.ts";
|
|
7
|
+
import { createAsyncBashTool } from "../../async-jobs/async-bash-tool.ts";
|
|
8
|
+
|
|
9
|
+
// These tests assert on the runtime tool objects the agent actually receives —
|
|
10
|
+
// the registered GSD bash tool, the core bash tool, and the async_bash tool —
|
|
11
|
+
// not on source text. The behavioral guarantee (an explicit timeout still
|
|
12
|
+
// fires after the silent 120s cap was removed) is exercised by spawning a real
|
|
13
|
+
// process at the bottom of the file.
|
|
14
|
+
|
|
15
|
+
/** Capture the GSD-registered `bash` tool object from registerDynamicTools. */
|
|
16
|
+
function registerAndCaptureBash(): any {
|
|
17
|
+
let captured: any = undefined;
|
|
18
|
+
const pi = {
|
|
19
|
+
registerTool: (tool: any) => {
|
|
20
|
+
if (!captured && tool && tool.name === "bash") captured = tool;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
registerDynamicTools(pi as any);
|
|
24
|
+
assert.ok(captured, "registerDynamicTools must register a tool named 'bash'");
|
|
25
|
+
return captured;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 1. Cap removal: the GSD bash tool no longer injects a default timeout. The
|
|
29
|
+
// schema's timeout stays optional (no `default`), so an omitted timeout
|
|
30
|
+
// means "no timeout" rather than a silent 120s cap.
|
|
31
|
+
test("GSD bash tool exposes an optional timeout with no injected default", () => {
|
|
32
|
+
const bash = registerAndCaptureBash();
|
|
33
|
+
const timeout = bash.parameters?.properties?.timeout;
|
|
34
|
+
assert.ok(timeout, "bash tool must expose a `timeout` parameter");
|
|
35
|
+
assert.equal(
|
|
36
|
+
"default" in timeout,
|
|
37
|
+
false,
|
|
38
|
+
"the timeout schema must not carry a default — an omitted timeout means uncapped",
|
|
39
|
+
);
|
|
40
|
+
assert.equal(
|
|
41
|
+
bash.parameters?.required?.includes?.("timeout") ?? false,
|
|
42
|
+
false,
|
|
43
|
+
"timeout must remain optional",
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 2. Watchdog verbiage is GSD-scoped: the core bash tool (reused by non-GSD
|
|
48
|
+
// embeddings with no watchdog) must NOT claim a watchdog, while the
|
|
49
|
+
// GSD-registered tool injects it into both description and timeout schema.
|
|
50
|
+
test("core bash tool does not advertise the auto-mode watchdog", () => {
|
|
51
|
+
const core = createBashTool(process.cwd(), { spawnHook: (c: any) => c });
|
|
52
|
+
assert.equal(
|
|
53
|
+
/stalled-tool watchdog/.test(core.description ?? ""),
|
|
54
|
+
false,
|
|
55
|
+
"core bash must not advertise a watchdog — non-GSD embeddings have none",
|
|
56
|
+
);
|
|
57
|
+
assert.equal(
|
|
58
|
+
/stalled-tool watchdog/.test(
|
|
59
|
+
(core.parameters as any)?.properties?.timeout?.description ?? "",
|
|
60
|
+
),
|
|
61
|
+
false,
|
|
62
|
+
"core bash timeout schema must not advertise a watchdog",
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("GSD-registered bash tool advertises the stalled-tool watchdog", () => {
|
|
67
|
+
const bash = registerAndCaptureBash();
|
|
68
|
+
assert.equal(
|
|
69
|
+
/stalled-tool watchdog/.test(bash.description ?? ""),
|
|
70
|
+
true,
|
|
71
|
+
"the GSD bash description must name the stalled-tool watchdog",
|
|
72
|
+
);
|
|
73
|
+
assert.equal(
|
|
74
|
+
/stalled-tool watchdog/.test(
|
|
75
|
+
bash.parameters?.properties?.timeout?.description ?? "",
|
|
76
|
+
),
|
|
77
|
+
true,
|
|
78
|
+
"the GSD bash timeout schema must name the stalled-tool watchdog",
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// 3. async_bash guidance frames itself as a non-blocking/background choice and
|
|
83
|
+
// no longer implies sync bash is time-capped.
|
|
84
|
+
test("async_bash guidance is non-blocking and drops the sync-cap implication", () => {
|
|
85
|
+
const asyncTool: any = createAsyncBashTool(
|
|
86
|
+
() => ({ register: () => "job-0" }) as any,
|
|
87
|
+
() => process.cwd(),
|
|
88
|
+
);
|
|
89
|
+
const guidance = (asyncTool.promptGuidelines ?? []).join("\n");
|
|
90
|
+
assert.equal(
|
|
91
|
+
/more than a few seconds/.test(guidance),
|
|
92
|
+
false,
|
|
93
|
+
"async_bash guidance must not imply sync bash is time-capped",
|
|
94
|
+
);
|
|
95
|
+
assert.equal(
|
|
96
|
+
/non-blocking/.test(guidance),
|
|
97
|
+
true,
|
|
98
|
+
"async_bash guidance must describe itself as non-blocking",
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 4. Behavioral passthrough: an explicitly-set timeout is still honored after
|
|
103
|
+
// the cap removal. A 1s timeout on a 5s sleep must fire well under 5s.
|
|
104
|
+
test("GSD bash still forwards an explicit timeout (cap removal did not break passthrough)", async () => {
|
|
105
|
+
const bash = registerAndCaptureBash();
|
|
106
|
+
|
|
107
|
+
const start = Date.now();
|
|
108
|
+
let resultText = "";
|
|
109
|
+
try {
|
|
110
|
+
const result: any = await bash.execute(
|
|
111
|
+
"t1",
|
|
112
|
+
{ command: "sleep 5", timeout: 1 },
|
|
113
|
+
undefined,
|
|
114
|
+
undefined,
|
|
115
|
+
{ cwd: process.cwd() },
|
|
116
|
+
);
|
|
117
|
+
resultText = JSON.stringify(result ?? "");
|
|
118
|
+
} catch (err) {
|
|
119
|
+
resultText = String(err instanceof Error ? err.message : err);
|
|
120
|
+
}
|
|
121
|
+
const elapsed = Date.now() - start;
|
|
122
|
+
|
|
123
|
+
assert.equal(
|
|
124
|
+
/timed out|timeout|exited with code|force-killed/i.test(resultText),
|
|
125
|
+
true,
|
|
126
|
+
`explicit 1s timeout should fire before the 5s sleep completes; got: ${resultText}`,
|
|
127
|
+
);
|
|
128
|
+
assert.ok(
|
|
129
|
+
elapsed < 4500,
|
|
130
|
+
`explicit timeout should fire quickly, not run to completion (elapsed ${elapsed}ms)`,
|
|
131
|
+
);
|
|
132
|
+
});
|