@opengsd/gsd-pi 1.3.0-dev.eed73bea → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/cli.js +17 -4
- package/dist/headless-answers.js +1 -1
- package/dist/headless-ui.d.ts +1 -1
- package/dist/headless-ui.js +1 -1
- package/dist/headless.js +1 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/models.js +9 -0
- package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
- package/dist/resources/extensions/gsd/auto/session.js +11 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
- package/dist/resources/extensions/gsd/auto-budget.js +5 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +17 -6
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +8 -24
- package/dist/resources/extensions/gsd/auto-recovery.js +9 -517
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
- package/dist/resources/extensions/gsd/auto-verification.js +5 -2
- package/dist/resources/extensions/gsd/auto.js +19 -10
- package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +39 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +5 -4
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -18
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +48 -36
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +15 -4
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +1 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands-usage.js +1 -1
- package/dist/resources/extensions/gsd/db/engine.js +4 -1
- package/dist/resources/extensions/gsd/db-memory-fts-schema.js +34 -0
- package/dist/resources/extensions/gsd/db-workspace.js +55 -3
- package/dist/resources/extensions/gsd/delegation-policy.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +192 -4
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +8 -2
- package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
- package/dist/resources/extensions/gsd/git-service.js +57 -0
- package/dist/resources/extensions/gsd/gitignore.js +4 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +20 -13
- package/dist/resources/extensions/gsd/json-persistence.js +20 -0
- package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
- package/dist/resources/extensions/gsd/layout-policy.js +3 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +2 -7
- package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
- package/dist/resources/extensions/gsd/metrics.js +4 -4
- package/dist/resources/extensions/gsd/migrate-external.js +11 -2
- package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
- package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
- package/dist/resources/extensions/gsd/root-write-leak-guard.js +7 -1
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +8 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +16 -1
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
- package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
- package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
- package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
- package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +27 -33
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
- package/dist/resources/extensions/gsd/state.js +12 -611
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -34
- package/dist/resources/extensions/gsd/tools/complete-task.js +8 -41
- package/dist/resources/extensions/gsd/tools/plan-slice.js +59 -44
- package/dist/resources/extensions/gsd/tools/plan-task.js +28 -5
- package/dist/resources/extensions/gsd/undo.js +43 -6
- package/dist/resources/extensions/gsd/unit-registry.js +11 -3
- package/dist/resources/extensions/gsd/verification-gate.js +7 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +126 -13
- package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
- package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
- 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 +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
- 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/middleware.js +3 -3
- 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/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{layout-4ae2d68984392bbf.js → layout-b35cbfff38aaf4cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-90d1d8d5e5d2dc6b.js → main-app-590a74400e35f685.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +1 -0
- package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +3 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts +2 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +2 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
- package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
- package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
- 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-latency.d.ts +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
- 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 +26 -841
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +23 -5
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.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 +18 -0
- 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/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +3 -2
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +46 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +38 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +3 -2
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme-schema.d.ts +75 -75
- package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/theme/theme-schema.js +1 -1
- package/pkg/dist/theme/theme-schema.js.map +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +11 -7
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/models.ts +9 -0
- package/src/resources/extensions/claude-code-cli/tests/index.test.ts +14 -0
- package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/session.ts +14 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
- package/src/resources/extensions/gsd/auto-budget.ts +12 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -7
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -23
- package/src/resources/extensions/gsd/auto-recovery.ts +13 -518
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
- package/src/resources/extensions/gsd/auto-verification.ts +5 -1
- package/src/resources/extensions/gsd/auto.ts +23 -8
- package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +44 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +5 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +43 -18
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +49 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +50 -37
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +16 -4
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +1 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands-usage.ts +1 -1
- package/src/resources/extensions/gsd/db/engine.ts +8 -1
- package/src/resources/extensions/gsd/db-memory-fts-schema.ts +45 -0
- package/src/resources/extensions/gsd/db-workspace.ts +61 -3
- package/src/resources/extensions/gsd/delegation-policy.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +233 -5
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
- package/src/resources/extensions/gsd/doctor-types.ts +3 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +7 -2
- package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
- package/src/resources/extensions/gsd/git-service.ts +64 -0
- package/src/resources/extensions/gsd/gitignore.ts +4 -1
- package/src/resources/extensions/gsd/guided-flow-queue.ts +20 -13
- package/src/resources/extensions/gsd/json-persistence.ts +20 -0
- package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
- package/src/resources/extensions/gsd/layout-policy.ts +2 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +2 -10
- package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
- package/src/resources/extensions/gsd/metrics.ts +4 -4
- package/src/resources/extensions/gsd/migrate-external.ts +8 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
- package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
- package/src/resources/extensions/gsd/root-write-leak-guard.ts +13 -1
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +9 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +16 -1
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
- package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
- package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
- package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
- package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +36 -30
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
- package/src/resources/extensions/gsd/state.ts +32 -732
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +14 -5
- package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +182 -4
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +58 -3
- package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +143 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +239 -2
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +11 -2
- package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +103 -4
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +44 -1
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +20 -1
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +15 -3
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +96 -15
- package/src/resources/extensions/gsd/tests/workflow-mcp-preflight.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -30
- package/src/resources/extensions/gsd/tools/complete-task.ts +7 -39
- package/src/resources/extensions/gsd/tools/plan-slice.ts +60 -47
- package/src/resources/extensions/gsd/tools/plan-task.ts +30 -5
- package/src/resources/extensions/gsd/undo.ts +47 -6
- package/src/resources/extensions/gsd/unit-registry.ts +11 -3
- package/src/resources/extensions/gsd/verification-gate.ts +8 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +141 -7
- package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.3976108148518f7d.js +0 -10
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-6644fc6ee8ca1247.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +0 -1
- package/dist/web/standalone/.next/static/chunks/webpack-7c1d97e39be2da11.js +0 -1
- /package/dist/web/standalone/.next/static/{SzEuqWX37DR9MEpEuQjP1 → zQLcXm0Za8lexoXtgDpMG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{SzEuqWX37DR9MEpEuQjP1 → zQLcXm0Za8lexoXtgDpMG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { autoSession } from "../auto-runtime-state.ts";
|
|
5
|
+
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
6
|
+
import { resetToolCallLoopGuard } from "../bootstrap/tool-call-loop-guard.ts";
|
|
7
|
+
|
|
8
|
+
type Handler = (event: any, ctx?: any) => Promise<any> | any;
|
|
9
|
+
|
|
10
|
+
function makeHookHarness(): {
|
|
11
|
+
emitToolCall: (toolName: string, input: Record<string, unknown>) => Promise<any>;
|
|
12
|
+
} {
|
|
13
|
+
const handlers = new Map<string, Handler[]>();
|
|
14
|
+
const pi = {
|
|
15
|
+
on(event: string, handler: Handler) {
|
|
16
|
+
const existing = handlers.get(event) ?? [];
|
|
17
|
+
existing.push(handler);
|
|
18
|
+
handlers.set(event, existing);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const ctx = {
|
|
22
|
+
cwd: process.cwd(),
|
|
23
|
+
ui: { notify: () => undefined },
|
|
24
|
+
};
|
|
25
|
+
let callId = 0;
|
|
26
|
+
|
|
27
|
+
registerHooks(pi as any, []);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
async emitToolCall(toolName: string, input: Record<string, unknown>): Promise<any> {
|
|
31
|
+
callId += 1;
|
|
32
|
+
const loopGuardHandler = handlers.get("tool_call")?.[0];
|
|
33
|
+
assert.ok(loopGuardHandler, "loop-guard tool_call handler should be registered");
|
|
34
|
+
return loopGuardHandler({ toolCallId: `loop-${callId}`, toolName, input }, ctx);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test("register-hooks keeps loop-guard block reason interactive outside auto-mode", async (t) => {
|
|
40
|
+
autoSession.reset();
|
|
41
|
+
resetToolCallLoopGuard();
|
|
42
|
+
t.after(() => {
|
|
43
|
+
autoSession.reset();
|
|
44
|
+
resetToolCallLoopGuard();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const { emitToolCall } = makeHookHarness();
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < 4; i += 1) {
|
|
50
|
+
await emitToolCall("web_search", { query: "same query" });
|
|
51
|
+
}
|
|
52
|
+
const block = await emitToolCall("web_search", { query: "same query" });
|
|
53
|
+
|
|
54
|
+
assert.equal(block?.block, true);
|
|
55
|
+
assert.match(block.reason, /respond to the user in text/);
|
|
56
|
+
assert.doesNotMatch(block.reason, /auto-mode recovery\/replan path/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("register-hooks rewrites loop-guard block reason for auto-mode identical-args blocks", async (t) => {
|
|
60
|
+
autoSession.reset();
|
|
61
|
+
resetToolCallLoopGuard();
|
|
62
|
+
autoSession.active = true;
|
|
63
|
+
t.after(() => {
|
|
64
|
+
autoSession.reset();
|
|
65
|
+
resetToolCallLoopGuard();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const { emitToolCall } = makeHookHarness();
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < 4; i += 1) {
|
|
71
|
+
await emitToolCall("web_search", { query: "same query" });
|
|
72
|
+
}
|
|
73
|
+
const block = await emitToolCall("web_search", { query: "same query" });
|
|
74
|
+
|
|
75
|
+
assert.equal(block?.block, true);
|
|
76
|
+
assert.match(block.reason, /Tool loop detected \(identical args\): web_search/);
|
|
77
|
+
assert.match(block.reason, /Do not re-issue this blocked tool/);
|
|
78
|
+
assert.match(block.reason, /auto-mode recovery\/replan path/);
|
|
79
|
+
assert.doesNotMatch(block.reason, /respond to the user in text/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("register-hooks rewrites loop-guard block reason for auto-mode per-tool blocks", async (t) => {
|
|
83
|
+
autoSession.reset();
|
|
84
|
+
resetToolCallLoopGuard();
|
|
85
|
+
autoSession.active = true;
|
|
86
|
+
t.after(() => {
|
|
87
|
+
autoSession.reset();
|
|
88
|
+
resetToolCallLoopGuard();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const { emitToolCall } = makeHookHarness();
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < 6; i += 1) {
|
|
94
|
+
await emitToolCall("gsd_complete_milestone", { milestone: `M${i}` });
|
|
95
|
+
}
|
|
96
|
+
const block = await emitToolCall("gsd_complete_milestone", { milestone: "M7" });
|
|
97
|
+
|
|
98
|
+
assert.equal(block?.block, true);
|
|
99
|
+
assert.match(block.reason, /Tool loop detected \(repeated tool\): gsd_complete_milestone/);
|
|
100
|
+
assert.match(block.reason, /Do not re-issue this blocked tool/);
|
|
101
|
+
assert.match(block.reason, /auto-mode recovery\/replan path/);
|
|
102
|
+
assert.doesNotMatch(block.reason, /respond to the user in text/);
|
|
103
|
+
});
|
|
@@ -7,7 +7,26 @@ import { closeSync, ftruncateSync, openSync, statSync, writeFileSync } from "nod
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
9
|
import { captureRootDirtySnapshot } from "../root-write-leak-guard.ts";
|
|
10
|
-
import { cleanup, git, makeTempRepo } from "./test-utils.ts";
|
|
10
|
+
import { cleanup, createFile, git, makeTempRepo } from "./test-utils.ts";
|
|
11
|
+
|
|
12
|
+
test("captureRootDirtySnapshot ignores unignored GSD root runtime directories", () => {
|
|
13
|
+
const base = makeTempRepo("gsd-root-dirty-runtime-");
|
|
14
|
+
try {
|
|
15
|
+
createFile(base, ".gsd/STATE.md", "state\n");
|
|
16
|
+
createFile(base, ".gsd-backups/snap-001", "backup\n");
|
|
17
|
+
createFile(base, ".gsd-worktrees/M001/file.txt", "worktree\n");
|
|
18
|
+
createFile(base, "src/foo.ts", "export {};\n");
|
|
19
|
+
|
|
20
|
+
const snapshot = captureRootDirtySnapshot(base);
|
|
21
|
+
|
|
22
|
+
assert.equal(snapshot.has(".gsd/STATE.md"), false);
|
|
23
|
+
assert.equal(snapshot.has(".gsd-backups/snap-001"), false);
|
|
24
|
+
assert.equal(snapshot.has(".gsd-worktrees/M001/file.txt"), false);
|
|
25
|
+
assert.equal(snapshot.has("src/foo.ts"), true);
|
|
26
|
+
} finally {
|
|
27
|
+
cleanup(base);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
11
30
|
|
|
12
31
|
test("captureRootDirtySnapshot does not read dirty files larger than Node's Buffer limit", () => {
|
|
13
32
|
const base = makeTempRepo("gsd-root-dirty-large-");
|
|
@@ -177,10 +177,22 @@ test("auto Unit tool scope allows status/read helpers named by closeout prompts"
|
|
|
177
177
|
}
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
test("auto Unit tool scope
|
|
181
|
-
for (const
|
|
180
|
+
test("auto Unit tool scope allows reactive-execute status and diagnostic tools", () => {
|
|
181
|
+
for (const toolName of ["gsd_milestone_status", "gsd_exec"]) {
|
|
182
|
+
const result = shouldBlockAutoUnitToolCall("reactive-execute", toolName);
|
|
183
|
+
assert.equal(result.block, false, `reactive-execute should be able to call ${toolName}`);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("auto Unit tool scope allows incremental task planning during plan-slice", () => {
|
|
188
|
+
const result = shouldBlockAutoUnitToolCall("plan-slice", "gsd_plan_task");
|
|
189
|
+
assert.equal(result.block, false, "plan-slice should be able to call gsd_plan_task incrementally");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("auto Unit tool scope still blocks per-task planner outside plan-slice", () => {
|
|
193
|
+
for (const unitType of ["refine-slice", "replan-slice"]) {
|
|
182
194
|
const result = shouldBlockAutoUnitToolCall(unitType, "gsd_plan_task");
|
|
183
|
-
assert.equal(result.block, true, `${unitType} should not call
|
|
195
|
+
assert.equal(result.block, true, `${unitType} should not call gsd_plan_task`);
|
|
184
196
|
}
|
|
185
197
|
});
|
|
186
198
|
|
|
@@ -48,6 +48,16 @@ function makeRecordingPi() {
|
|
|
48
48
|
} as any;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function makeRecordingCtx() {
|
|
52
|
+
const notifications: Array<{ message: string; level: string }> = [];
|
|
53
|
+
return {
|
|
54
|
+
notifications,
|
|
55
|
+
ui: {
|
|
56
|
+
notify: (message: string, level: string) => { notifications.push({ message, level }); },
|
|
57
|
+
},
|
|
58
|
+
} as any;
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
// ═══ #1855: empty RecoveryContext (basePath undefined) crashes ════════════════
|
|
52
62
|
|
|
53
63
|
{
|
|
@@ -111,6 +121,52 @@ function makeRecordingPi() {
|
|
|
111
121
|
}
|
|
112
122
|
}
|
|
113
123
|
|
|
124
|
+
// ═══ plan-slice timeout recovery verifies stale PLAN before advancing ═══════
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
console.log("\n=== plan-slice timeout recovery rejects stale placeholder PLAN ===");
|
|
128
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-timeout-stale-plan-"));
|
|
129
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
130
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
134
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
135
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
|
|
136
|
+
writeFileSync(
|
|
137
|
+
join(sliceDir, "S01-PLAN.md"),
|
|
138
|
+
"# S01: Slice\n\n## Tasks\n\nPlanning was interrupted before task rows were persisted.\n",
|
|
139
|
+
"utf-8",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const ctx = makeRecordingCtx();
|
|
143
|
+
const pi = makeRecordingPi();
|
|
144
|
+
const result = await recoverTimedOutUnit(ctx, pi, "plan-slice", "M001/S01", "idle", {
|
|
145
|
+
basePath: base,
|
|
146
|
+
verbose: false,
|
|
147
|
+
currentUnitStartedAt: Date.now(),
|
|
148
|
+
unitRecoveryCount: new Map(),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
assert.equal(result, "recovered", "invalid existing plan should enter steering recovery");
|
|
152
|
+
assert.equal(pi.messages.length, 1, "invalid existing plan should trigger steering instead of advancing");
|
|
153
|
+
assert.ok(
|
|
154
|
+
ctx.notifications.some((entry: { message: string }) => entry.message.includes("steering plan-slice M001/S01")),
|
|
155
|
+
"recovery notification should describe steering, not artifact advance",
|
|
156
|
+
);
|
|
157
|
+
assert.ok(
|
|
158
|
+
!ctx.notifications.some((entry: { message: string }) => entry.message.includes("artifact already exists on disk. Advancing.")),
|
|
159
|
+
"stale placeholder plan should not use the advance-on-existence path",
|
|
160
|
+
);
|
|
161
|
+
const runtime = JSON.parse(readFileSync(join(base, ".gsd", "runtime", "units", "plan-slice-M001-S01.json"), "utf-8"));
|
|
162
|
+
assert.equal(runtime.phase, "recovered", "stale placeholder plan must not be finalized");
|
|
163
|
+
assert.equal(runtime.recoveryAttempts, 1, "steering recovery attempt should be recorded");
|
|
164
|
+
} finally {
|
|
165
|
+
closeDatabase();
|
|
166
|
+
rmSync(base, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
114
170
|
// ═══ #1855: valid RecoveryContext does not crash ═════════════════════════════
|
|
115
171
|
|
|
116
172
|
{
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
type ReconciliationDeps,
|
|
43
43
|
} from "../state-reconciliation.ts";
|
|
44
44
|
import { classifyFailure } from "../recovery-classification.ts";
|
|
45
|
+
import { handlerPhaseIndex, RECONCILIATION_REPAIR_PHASES } from "../state-reconciliation/registry.ts";
|
|
45
46
|
import { staleRenderHandler } from "../state-reconciliation/drift/stale-render.ts";
|
|
46
47
|
import type { GSDState } from "../types.ts";
|
|
47
48
|
|
|
@@ -160,6 +161,29 @@ test("ADR-017 (#5700): repair failure throws ReconciliationFailedError with shap
|
|
|
160
161
|
);
|
|
161
162
|
});
|
|
162
163
|
|
|
164
|
+
test("ADR-017 (#5700): custom registry handlers outside built-in phases are repaired", async () => {
|
|
165
|
+
const drift = { kind: "custom-drift", id: "D001" } as unknown as DriftRecord;
|
|
166
|
+
let repaired = false;
|
|
167
|
+
const handler = {
|
|
168
|
+
kind: "custom-drift",
|
|
169
|
+
detect: () => (repaired ? [] : [drift]),
|
|
170
|
+
repair: () => {
|
|
171
|
+
repaired = true;
|
|
172
|
+
},
|
|
173
|
+
} as unknown as DriftHandler;
|
|
174
|
+
|
|
175
|
+
const result = await reconcileBeforeDispatch("/project", {
|
|
176
|
+
invalidateStateCache: () => {},
|
|
177
|
+
deriveState: async () => makeState(),
|
|
178
|
+
registry: [handler],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
assert.equal(result.ok, true);
|
|
182
|
+
assert.equal(repaired, true);
|
|
183
|
+
assert.equal(result.repaired.length, 1);
|
|
184
|
+
assert.equal(result.repaired[0]?.kind, "custom-drift");
|
|
185
|
+
});
|
|
186
|
+
|
|
163
187
|
test("ADR-017 (#5700): a detector failure degrades to a blocker without aborting other handlers", async () => {
|
|
164
188
|
// A single detector throwing (e.g. a transient file read error) must NOT
|
|
165
189
|
// abort the whole cycle and hide every later handler's drift. It is collected
|
|
@@ -772,6 +796,51 @@ test("#1003: stale-render plan repair switches back from an open wrong DB", asyn
|
|
|
772
796
|
assert.equal(getSliceTasks("M001", "S01").length, 1, "repair should leave the project DB active");
|
|
773
797
|
});
|
|
774
798
|
|
|
799
|
+
test("#1034: validation-blocked milestone summary drift returns blocker instead of exhausting repair passes", async () => {
|
|
800
|
+
const drift: Extract<DriftRecord, { kind: "stale-render" }> = {
|
|
801
|
+
kind: "stale-render",
|
|
802
|
+
renderPath: "/repo/.gsd/milestones/M001/M001-SUMMARY.md",
|
|
803
|
+
reason: "M001 is complete with summary in DB but SUMMARY.md missing on disk",
|
|
804
|
+
};
|
|
805
|
+
let repairCalled = false;
|
|
806
|
+
const handler: DriftHandler<Extract<DriftRecord, { kind: "stale-render" }>> = {
|
|
807
|
+
kind: "stale-render",
|
|
808
|
+
detect: () => [drift],
|
|
809
|
+
blocker: staleRenderHandler.blocker!,
|
|
810
|
+
repair: () => {
|
|
811
|
+
repairCalled = true;
|
|
812
|
+
},
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const validationBlocker = [
|
|
816
|
+
"Milestone M001 is blocked because milestone validation returned needs-attention.",
|
|
817
|
+
"Fix options:",
|
|
818
|
+
"1. Review the validation details: `/gsd status`",
|
|
819
|
+
].join("\n");
|
|
820
|
+
|
|
821
|
+
const result = await reconcileBeforeDispatch("/repo", {
|
|
822
|
+
invalidateStateCache: () => {},
|
|
823
|
+
deriveState: async () =>
|
|
824
|
+
makeState({
|
|
825
|
+
phase: "blocked",
|
|
826
|
+
blockers: [validationBlocker],
|
|
827
|
+
nextAction: "Resolve M001 validation attention before proceeding.",
|
|
828
|
+
}),
|
|
829
|
+
registry: [handler],
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
assert.equal(result.ok, true);
|
|
833
|
+
assert.equal(repairCalled, false, "validation-blocked milestone summary drift should not attempt repair");
|
|
834
|
+
assert.ok(
|
|
835
|
+
result.blockers.some((blocker) => blocker.includes("milestone validation returned needs-attention")),
|
|
836
|
+
"validation blocker should be returned to the caller",
|
|
837
|
+
);
|
|
838
|
+
assert.ok(
|
|
839
|
+
result.blockers.some((blocker) => blocker.includes("Stale milestone summary render")),
|
|
840
|
+
"stale-render blocker should explain why repair did not run",
|
|
841
|
+
);
|
|
842
|
+
});
|
|
843
|
+
|
|
775
844
|
test("ADR-017 (#5702): stale-render detector reason strings match repair contract", (t) => {
|
|
776
845
|
t.skip("TODO(flat-phase): stale-render detection temporarily disabled during layout transition"); return;
|
|
777
846
|
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-render-reasons-"));
|
|
@@ -1887,3 +1956,49 @@ test("ADR-017 (#5700): cascading drift triggers second pass within cap", async (
|
|
|
1887
1956
|
assert.equal(result.repaired.length, 2, "both passes' repairs collected");
|
|
1888
1957
|
assert.equal(repaired.length, 2);
|
|
1889
1958
|
});
|
|
1959
|
+
|
|
1960
|
+
test("deriveState is pure: stale sketch healed only via reconcileBeforeDispatch", async (t) => {
|
|
1961
|
+
const base = makeFixtureBase();
|
|
1962
|
+
t.after(() => cleanup(base));
|
|
1963
|
+
|
|
1964
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1965
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
1966
|
+
insertSlice({
|
|
1967
|
+
id: "S02",
|
|
1968
|
+
milestoneId: "M001",
|
|
1969
|
+
title: "Feature",
|
|
1970
|
+
status: "pending",
|
|
1971
|
+
risk: "medium",
|
|
1972
|
+
depends: [],
|
|
1973
|
+
demo: "S02 demo.",
|
|
1974
|
+
sequence: 1,
|
|
1975
|
+
isSketch: true,
|
|
1976
|
+
sketchScope: "limited",
|
|
1977
|
+
});
|
|
1978
|
+
writeFileSync(
|
|
1979
|
+
join(base, ".gsd", "phases", "01-test", "01-02-PLAN.md"),
|
|
1980
|
+
"# S02 Plan\n",
|
|
1981
|
+
);
|
|
1982
|
+
|
|
1983
|
+
const { deriveState } = await import("../state.ts");
|
|
1984
|
+
invalidateStateCache();
|
|
1985
|
+
|
|
1986
|
+
const beforeReconcile = await deriveState(base);
|
|
1987
|
+
assert.equal(beforeReconcile.phase, "refining", "derive alone must not heal stale sketch flag");
|
|
1988
|
+
assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "DB flag unchanged before reconcile");
|
|
1989
|
+
|
|
1990
|
+
const result = await reconcileBeforeDispatch(base);
|
|
1991
|
+
assert.equal(result.ok, true);
|
|
1992
|
+
assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "reconcile clears sketch flag");
|
|
1993
|
+
|
|
1994
|
+
invalidateStateCache();
|
|
1995
|
+
const afterReconcile = await deriveState(base);
|
|
1996
|
+
assert.notEqual(afterReconcile.phase, "refining", "derive after reconcile advances past sketch gate");
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
test("reconciliation repair phases: external edits precede re-project handlers", () => {
|
|
2000
|
+
assert.equal(RECONCILIATION_REPAIR_PHASES.length, 3);
|
|
2001
|
+
assert.ok(handlerPhaseIndex("external-markdown-edit") < handlerPhaseIndex("stale-render"));
|
|
2002
|
+
assert.ok(handlerPhaseIndex("external-planning-edit") < handlerPhaseIndex("roadmap-divergence"));
|
|
2003
|
+
assert.ok(handlerPhaseIndex("stale-sketch-flag") < handlerPhaseIndex("stale-render"));
|
|
2004
|
+
});
|
|
@@ -7,6 +7,7 @@ import { test } from 'node:test';
|
|
|
7
7
|
import assert from 'node:assert/strict';
|
|
8
8
|
import {
|
|
9
9
|
checkToolCallLoop,
|
|
10
|
+
recordToolCallLoopMutation,
|
|
10
11
|
resetToolCallLoopGuard,
|
|
11
12
|
disableToolCallLoopGuard,
|
|
12
13
|
getToolCallLoopCount,
|
|
@@ -223,6 +224,62 @@ console.log('\n── Loop guard: repeatable tools get the higher cap (#783) ─
|
|
|
223
224
|
assert.ok(blocked.reason!.includes('cap 15'), 'reason should mention the repeatable cap');
|
|
224
225
|
}
|
|
225
226
|
|
|
227
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
228
|
+
// Mutation progress decays per-tool counts (#1092)
|
|
229
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
230
|
+
|
|
231
|
+
console.log('\n── Loop guard: mutation progress decays per-tool counts (#1092) ──');
|
|
232
|
+
|
|
233
|
+
{
|
|
234
|
+
resetToolCallLoopGuard();
|
|
235
|
+
|
|
236
|
+
// bash is called more than the repeatable cap, but each count is separated
|
|
237
|
+
// by a successful file mutation. Guard 2 should treat that as progress and
|
|
238
|
+
// avoid blocking the progressing unit.
|
|
239
|
+
for (let i = 1; i <= 20; i++) {
|
|
240
|
+
const result = checkToolCallLoop('bash', { command: `echo ${i}` });
|
|
241
|
+
assert.ok(result.block === false, `bash call ${i} after mutation progress should be allowed`);
|
|
242
|
+
assert.deepStrictEqual(getToolCallCountForTool('bash'), 1, `bash count should decay before call ${i}`);
|
|
243
|
+
|
|
244
|
+
const mutationTool = i % 2 === 0 ? 'write' : 'edit';
|
|
245
|
+
const mutation = checkToolCallLoop(mutationTool, {
|
|
246
|
+
path: `file-${i}.ts`,
|
|
247
|
+
content: `content ${i}`,
|
|
248
|
+
});
|
|
249
|
+
assert.ok(mutation.block === false, `${mutationTool} mutation ${i} should be allowed`);
|
|
250
|
+
recordToolCallLoopMutation(mutationTool);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
255
|
+
// Distinct read-only navigation calls are context gathering, not repeated-tool loops.
|
|
256
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
257
|
+
|
|
258
|
+
console.log('\n── Loop guard: distinct read-only navigation calls are not capped by per-tool count ──');
|
|
259
|
+
|
|
260
|
+
{
|
|
261
|
+
const exemptTools = ['find', 'glob', 'grep', 'ls', 'read', 'search_and_read'];
|
|
262
|
+
|
|
263
|
+
for (const toolName of exemptTools) {
|
|
264
|
+
resetToolCallLoopGuard();
|
|
265
|
+
|
|
266
|
+
for (let i = 1; i <= 20; i++) {
|
|
267
|
+
const result = checkToolCallLoop(toolName, { path: `file-${i}.ts` });
|
|
268
|
+
assert.ok(result.block === false, `distinct ${toolName} call ${i} should be allowed`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
resetToolCallLoopGuard();
|
|
272
|
+
for (let i = 1; i <= 4; i++) {
|
|
273
|
+
const result = checkToolCallLoop(toolName, { path: 'same-file.ts' });
|
|
274
|
+
assert.ok(result.block === false, `identical ${toolName} call ${i} should be allowed`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const blocked = checkToolCallLoop(toolName, { path: 'same-file.ts' });
|
|
278
|
+
assert.ok(blocked.block === true, `5th identical ${toolName} call should still be blocked`);
|
|
279
|
+
assert.ok(blocked.reason?.includes('identical args'), `${toolName} loops should be caught by Guard 1`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
226
283
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
227
284
|
// Per-tool counts are independent per tool and reset together
|
|
228
285
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -245,3 +302,86 @@ console.log('\n── Loop guard: per-tool counts are independent and reset toge
|
|
|
245
302
|
}
|
|
246
303
|
|
|
247
304
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
305
|
+
// Non-exempt repeatable tools from core-session-tools.ts get the higher cap.
|
|
306
|
+
// Read-only navigation tools are exempted earlier; mutating/execution repeatable
|
|
307
|
+
// tools (e.g. gsd_exec) must still trip the per-tool cap.
|
|
308
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
309
|
+
|
|
310
|
+
console.log('\n── Loop guard: non-exempt repeatable tools get high cap ──');
|
|
311
|
+
|
|
312
|
+
{
|
|
313
|
+
for (const toolName of ['bg_shell', 'gsd_exec', 'write']) {
|
|
314
|
+
resetToolCallLoopGuard();
|
|
315
|
+
|
|
316
|
+
// Should allow up to 15 varied calls without blocking.
|
|
317
|
+
for (let i = 1; i <= 15; i++) {
|
|
318
|
+
const result = checkToolCallLoop(toolName, { arg: `v${i}` });
|
|
319
|
+
assert.ok(result.block === false, `${toolName} call ${i} (varied args) should be allowed`);
|
|
320
|
+
}
|
|
321
|
+
// 16th call with varied args must be blocked by the per-tool repeatable cap.
|
|
322
|
+
const blocked = checkToolCallLoop(toolName, { arg: 'v16' });
|
|
323
|
+
assert.ok(blocked.block === true, `${toolName}: 16th call must be blocked by repeatable cap`);
|
|
324
|
+
assert.ok(blocked.reason?.includes('cap 15'), `${toolName}: reason must mention cap 15`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
329
|
+
// Non-repeatable tools excluded from INHERENTLY_REPEATABLE_TOOL_SET use the
|
|
330
|
+
// default cap (6), not the repeatable cap (15).
|
|
331
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
332
|
+
|
|
333
|
+
console.log('\n── Loop guard: ToolSearch (excluded from repeatable set) hits default cap ──');
|
|
334
|
+
|
|
335
|
+
{
|
|
336
|
+
// ToolSearch is in MINIMAL_AUTO_BASE but explicitly excluded from repeatable.
|
|
337
|
+
// It should hit the default cap of 6, not 15.
|
|
338
|
+
resetToolCallLoopGuard();
|
|
339
|
+
for (let i = 1; i <= 6; i++) {
|
|
340
|
+
const result = checkToolCallLoop('ToolSearch', { query: `q${i}` });
|
|
341
|
+
assert.ok(result.block === false, `ToolSearch call ${i} should be allowed`);
|
|
342
|
+
}
|
|
343
|
+
const blocked = checkToolCallLoop('ToolSearch', { query: 'q7' });
|
|
344
|
+
assert.ok(blocked.block === true, 'ToolSearch: 7th varied call must be blocked by default cap');
|
|
345
|
+
assert.ok(!blocked.reason?.includes('cap 15'), 'ToolSearch must NOT use the repeatable cap');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
349
|
+
// Block reasons instruct the model to stop tooling this turn
|
|
350
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
351
|
+
|
|
352
|
+
console.log('\n── Loop guard: block reasons tell model to respond in text ──');
|
|
353
|
+
|
|
354
|
+
{
|
|
355
|
+
resetToolCallLoopGuard();
|
|
356
|
+
|
|
357
|
+
for (let i = 0; i < 4; i++) {
|
|
358
|
+
checkToolCallLoop('web_search', { query: 'same query' });
|
|
359
|
+
}
|
|
360
|
+
const identicalBlocked = checkToolCallLoop('web_search', { query: 'same query' });
|
|
361
|
+
assert.ok(identicalBlocked.block === true, 'identical-args guard should block');
|
|
362
|
+
assert.ok(
|
|
363
|
+
identicalBlocked.reason!.includes('respond to the user in text'),
|
|
364
|
+
'identical-args reason should tell the model to stop tooling',
|
|
365
|
+
);
|
|
366
|
+
assert.ok(
|
|
367
|
+
!identicalBlocked.reason!.includes('Try a different approach'),
|
|
368
|
+
'identical-args reason should not suggest retrying another tool',
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
resetToolCallLoopGuard();
|
|
372
|
+
for (let i = 0; i < 6; i++) {
|
|
373
|
+
checkToolCallLoop('gsd_complete_milestone', { milestone: `M${i}` });
|
|
374
|
+
}
|
|
375
|
+
const perToolBlocked = checkToolCallLoop('gsd_complete_milestone', { milestone: 'M7' });
|
|
376
|
+
assert.ok(perToolBlocked.block === true, 'per-tool guard should block');
|
|
377
|
+
assert.ok(
|
|
378
|
+
perToolBlocked.reason!.includes('respond to the user in text'),
|
|
379
|
+
'per-tool reason should tell the model to stop tooling',
|
|
380
|
+
);
|
|
381
|
+
assert.ok(
|
|
382
|
+
perToolBlocked.reason!.includes('Do not retry this tool'),
|
|
383
|
+
'per-tool reason should forbid retrying the blocked tool',
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -385,14 +385,17 @@ test("gsd_plan_slice — enrichment fields are optional", () => {
|
|
|
385
385
|
|
|
386
386
|
const required = new Set(getRequiredProps(tool));
|
|
387
387
|
|
|
388
|
-
// Core fields
|
|
389
|
-
|
|
388
|
+
// Core fields. `tasks` is intentionally NOT here: incremental planning
|
|
389
|
+
// (#1027) lets gsd_plan_slice persist slice metadata only, then add tasks
|
|
390
|
+
// one at a time via gsd_plan_task, so tasks must be optional.
|
|
391
|
+
const coreRequired = ["milestoneId", "sliceId", "goal"];
|
|
390
392
|
for (const field of coreRequired) {
|
|
391
393
|
assert.ok(required.has(field), `core field "${field}" must be required`);
|
|
392
394
|
}
|
|
393
395
|
|
|
394
|
-
// Enrichment fields
|
|
396
|
+
// Enrichment fields (plus tasks, optional for incremental planning).
|
|
395
397
|
const enrichmentFields = [
|
|
398
|
+
"tasks",
|
|
396
399
|
"successCriteria",
|
|
397
400
|
"proofLevel",
|
|
398
401
|
"integrationClosure",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
handleUndo,
|
|
11
11
|
handleUndoTask,
|
|
12
12
|
handleResetSlice,
|
|
13
|
+
parseActivityLogFilename,
|
|
13
14
|
uncheckTaskInPlan,
|
|
14
15
|
} from "../undo.ts";
|
|
15
16
|
import {
|
|
@@ -67,6 +68,37 @@ test("handleUndo without --force only warns and leaves completed units intact",
|
|
|
67
68
|
}
|
|
68
69
|
});
|
|
69
70
|
|
|
71
|
+
test("handleUndo execute-task with --force reopens the task row and re-renders plan", async () => {
|
|
72
|
+
const base = makeTempDir("gsd-undo-execute-task");
|
|
73
|
+
try {
|
|
74
|
+
setupTaskFixture(base);
|
|
75
|
+
mkdirSync(join(base, ".gsd", "activity"), { recursive: true });
|
|
76
|
+
writeFileSync(
|
|
77
|
+
join(base, ".gsd", "activity", "001-execute-task-M001-S01-T01.jsonl"),
|
|
78
|
+
"",
|
|
79
|
+
"utf-8",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const { notifications, ctx } = makeCtx();
|
|
83
|
+
await handleUndo("--force", ctx, {} as any, base);
|
|
84
|
+
|
|
85
|
+
const task = getTask("M001", "S01", "T01");
|
|
86
|
+
assert.equal(task?.status, "pending");
|
|
87
|
+
|
|
88
|
+
const planContent = readFileSync(
|
|
89
|
+
join(base, ".gsd", "phases", "01-test", "01-01-PLAN.md"),
|
|
90
|
+
"utf-8",
|
|
91
|
+
);
|
|
92
|
+
assert.match(planContent, /\[ \] \*\*T01\*\*:/);
|
|
93
|
+
|
|
94
|
+
assert.equal(notifications[0]?.level, "success");
|
|
95
|
+
assert.match(notifications[0]?.message ?? "", /Unchecked task in PLAN/);
|
|
96
|
+
} finally {
|
|
97
|
+
closeDatabase();
|
|
98
|
+
rmSync(base, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
70
102
|
test("uncheckTaskInPlan flips a checked task back to unchecked", () => {
|
|
71
103
|
const base = makeTempDir("gsd-undo-plan");
|
|
72
104
|
try {
|
|
@@ -154,6 +186,48 @@ test("extractCommitShas ignores malformed commit tokens", () => {
|
|
|
154
186
|
assert.deepEqual(extractCommitShas(content), ["1234567"]);
|
|
155
187
|
});
|
|
156
188
|
|
|
189
|
+
test("parseActivityLogFilename splits hyphenated unit types from their IDs", () => {
|
|
190
|
+
// Task-level: unit type and ID both contain hyphens.
|
|
191
|
+
assert.deepEqual(parseActivityLogFilename("001-execute-task-M001-S01-T01.jsonl"), {
|
|
192
|
+
unitType: "execute-task",
|
|
193
|
+
unitId: "M001-S01-T01",
|
|
194
|
+
});
|
|
195
|
+
// Variant must win over its shorter prefix ("execute-task").
|
|
196
|
+
assert.deepEqual(parseActivityLogFilename("002-execute-task-simple-M001-S02-T03.jsonl"), {
|
|
197
|
+
unitType: "execute-task-simple",
|
|
198
|
+
unitId: "M001-S02-T03",
|
|
199
|
+
});
|
|
200
|
+
// Milestone- and slice-shaped IDs.
|
|
201
|
+
assert.deepEqual(parseActivityLogFilename("003-research-milestone-M001.jsonl"), {
|
|
202
|
+
unitType: "research-milestone",
|
|
203
|
+
unitId: "M001",
|
|
204
|
+
});
|
|
205
|
+
assert.deepEqual(parseActivityLogFilename("004-plan-slice-M001-S01.jsonl"), {
|
|
206
|
+
unitType: "plan-slice",
|
|
207
|
+
unitId: "M001-S01",
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("parseActivityLogFilename accepts non-milestone-shaped unit IDs", () => {
|
|
212
|
+
// Regression (#1057): project-level units use IDs that do not start with
|
|
213
|
+
// "M<digit>". A milestone-shaped regex made /gsd undo bail out with
|
|
214
|
+
// "could not parse latest activity log" when one of these was most recent.
|
|
215
|
+
assert.deepEqual(parseActivityLogFilename("005-discuss-project-PROJECT.jsonl"), {
|
|
216
|
+
unitType: "discuss-project",
|
|
217
|
+
unitId: "PROJECT",
|
|
218
|
+
});
|
|
219
|
+
assert.deepEqual(parseActivityLogFilename("006-workflow-preferences-WORKFLOW-PREFS.jsonl"), {
|
|
220
|
+
unitType: "workflow-preferences",
|
|
221
|
+
unitId: "WORKFLOW-PREFS",
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("parseActivityLogFilename returns null for unrecognized names", () => {
|
|
226
|
+
assert.equal(parseActivityLogFilename("007-not-a-real-unit-M001.jsonl"), null);
|
|
227
|
+
assert.equal(parseActivityLogFilename("no-sequence-prefix.jsonl"), null);
|
|
228
|
+
assert.equal(parseActivityLogFilename("001-execute-task-M001-S01-T01.txt"), null);
|
|
229
|
+
});
|
|
230
|
+
|
|
157
231
|
// ─── handleUndoTask tests ────────────────────────────────────────────────────
|
|
158
232
|
|
|
159
233
|
function makeCtx(): { notifications: Array<{ message: string; level: string }>; ctx: any } {
|