@opengsd/gsd-pi 1.5.0 → 1.6.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/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
- package/dist/resources/extensions/gsd/activity-log.js +1 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +20 -13
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
- package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
- package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
- package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
- package/dist/resources/extensions/gsd/auto-start.js +57 -33
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
- package/dist/resources/extensions/gsd/auto-timers.js +9 -2
- package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
- package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
- package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
- package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
- package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
- package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
- package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
- package/dist/resources/extensions/gsd/auto.js +106 -34
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
- package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
- package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
- package/dist/resources/extensions/gsd/config-overlay.js +7 -2
- package/dist/resources/extensions/gsd/db/engine.js +33 -0
- package/dist/resources/extensions/gsd/db/queries.js +9 -9
- package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
- package/dist/resources/extensions/gsd/db-adapter.js +30 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
- package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +45 -0
- package/dist/resources/extensions/gsd/gsd-db.js +23 -11
- package/dist/resources/extensions/gsd/guided-flow.js +22 -17
- package/dist/resources/extensions/gsd/journal.js +1 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +34 -79
- package/dist/resources/extensions/gsd/md-importer.js +89 -23
- package/dist/resources/extensions/gsd/metrics.js +2 -2
- package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
- package/dist/resources/extensions/gsd/paths.js +87 -52
- package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
- package/dist/resources/extensions/gsd/preferences-models.js +66 -15
- package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +148 -10
- package/dist/resources/extensions/gsd/preferences.js +20 -0
- package/dist/resources/extensions/gsd/prompts/system.md +6 -6
- package/dist/resources/extensions/gsd/quick.js +12 -3
- package/dist/resources/extensions/gsd/rule-registry.js +106 -10
- package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
- package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
- package/dist/resources/extensions/gsd/tool-contract.js +14 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
- package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
- package/dist/resources/extensions/gsd/unit-registry.js +12 -2
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
- package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
- package/dist/resources/extensions/gsd/uok/audit.js +3 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
- package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
- package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
- package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
- package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
- package/dist/resources/extensions/gsd/workspace.js +5 -14
- package/dist/resources/extensions/gsd/worktree-command.js +1 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
- package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
- package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
- package/dist/resources/extensions/shared/interview-ui.js +62 -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 +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/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/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 +9 -9
- package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/worktree-cli-create.d.ts +10 -0
- package/dist/worktree-cli-create.js +11 -0
- package/dist/worktree-cli-format.d.ts +12 -0
- package/dist/worktree-cli-format.js +38 -0
- package/dist/worktree-cli-plan.d.ts +16 -0
- package/dist/worktree-cli-plan.js +19 -0
- package/dist/worktree-cli-session.d.ts +13 -0
- package/dist/worktree-cli-session.js +18 -0
- package/dist/worktree-cli-status.d.ts +35 -0
- package/dist/worktree-cli-status.js +54 -0
- package/dist/worktree-cli.d.ts +1 -16
- package/dist/worktree-cli.js +28 -122
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +25 -36
- package/integrations/hermes/pyproject.toml +1 -1
- package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
- package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator-agent.js +113 -0
- package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
- package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
- package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator-tools.js +137 -0
- package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
- package/packages/daemon/dist/orchestrator.d.ts +4 -56
- package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
- package/packages/daemon/dist/orchestrator.js +13 -303
- package/packages/daemon/dist/orchestrator.js.map +1 -1
- package/packages/daemon/dist/orchestrator.test.js +11 -1
- package/packages/daemon/dist/orchestrator.test.js.map +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
- package/packages/gsd-agent-core/package.json +5 -5
- 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 +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -9
- 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/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
- package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
- package/packages/pi-ai/dist/tool-result-content.js +62 -0
- package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
- package/packages/pi-ai/dist/types.d.ts +8 -20
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +4 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.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/core/export-html/template.js +53 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
- package/src/resources/extensions/gsd/activity-log.ts +1 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
- package/src/resources/extensions/gsd/auto/orchestrator.ts +21 -14
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
- package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
- package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
- package/src/resources/extensions/gsd/auto-start.ts +62 -31
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
- package/src/resources/extensions/gsd/auto-timers.ts +9 -2
- package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
- package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
- package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
- package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
- package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
- package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
- package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
- package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
- package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
- package/src/resources/extensions/gsd/auto.ts +114 -47
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
- package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
- package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
- package/src/resources/extensions/gsd/config-overlay.ts +7 -1
- package/src/resources/extensions/gsd/db/engine.ts +36 -0
- package/src/resources/extensions/gsd/db/queries.ts +9 -9
- package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
- package/src/resources/extensions/gsd/db-adapter.ts +29 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
- package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +53 -0
- package/src/resources/extensions/gsd/gsd-db.ts +33 -11
- package/src/resources/extensions/gsd/guided-flow.ts +26 -18
- package/src/resources/extensions/gsd/journal.ts +1 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +52 -91
- package/src/resources/extensions/gsd/md-importer.ts +93 -22
- package/src/resources/extensions/gsd/metrics.ts +2 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/paths.ts +108 -51
- package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
- package/src/resources/extensions/gsd/preferences-models.ts +74 -20
- package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
- package/src/resources/extensions/gsd/preferences-types.ts +54 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +146 -9
- package/src/resources/extensions/gsd/preferences.ts +20 -0
- package/src/resources/extensions/gsd/prompts/system.md +6 -6
- package/src/resources/extensions/gsd/quick.ts +11 -3
- package/src/resources/extensions/gsd/rule-registry.ts +111 -9
- package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +46 -1
- package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
- package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
- package/src/resources/extensions/gsd/tool-contract.ts +29 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
- package/src/resources/extensions/gsd/types.ts +23 -4
- package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
- package/src/resources/extensions/gsd/unit-registry.ts +16 -2
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
- package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
- package/src/resources/extensions/gsd/uok/audit.ts +2 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
- package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
- package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
- package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
- package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
- package/src/resources/extensions/gsd/workspace.ts +5 -21
- package/src/resources/extensions/gsd/worktree-command.ts +1 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
- package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
- package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
- package/src/resources/extensions/shared/interview-ui.ts +58 -11
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
- /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_ssgManifest.js +0 -0
|
@@ -116,7 +116,8 @@ import {
|
|
|
116
116
|
} from "./auto-tool-tracking.js";
|
|
117
117
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
118
118
|
import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
|
|
119
|
-
import { selectAndApplyModel, resolveModelId, clearToolBaseline } from "./auto-model-selection.js";
|
|
119
|
+
import { selectAndApplyModel, resolveModelId, clearToolBaseline, isModelUnavailable, applyThinkingLevelForModel } from "./auto-model-selection.js";
|
|
120
|
+
import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
|
|
120
121
|
import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
|
|
121
122
|
import {
|
|
122
123
|
checkPostUnitHooks,
|
|
@@ -127,6 +128,7 @@ import {
|
|
|
127
128
|
runPreDispatchHooks,
|
|
128
129
|
persistHookState,
|
|
129
130
|
restoreHookState,
|
|
131
|
+
reconcileRestoredHookDispatch,
|
|
130
132
|
clearPersistedHookState,
|
|
131
133
|
} from "./post-unit-hooks.js";
|
|
132
134
|
import { runGSDDoctor, rebuildState } from "./doctor.js";
|
|
@@ -177,24 +179,18 @@ import {
|
|
|
177
179
|
import { GitServiceImpl } from "./git-service.js";
|
|
178
180
|
import { nativeCheckoutBranch } from "./native-git-bridge.js";
|
|
179
181
|
import { getPriorSliceCompletionBlocker } from "./dispatch-guard.js";
|
|
180
|
-
import {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
autoWorktreeBranch,
|
|
190
|
-
syncWorktreeStateBack,
|
|
191
|
-
readResourceVersion,
|
|
192
|
-
checkResourcesStale,
|
|
193
|
-
escapeStaleWorktree,
|
|
194
|
-
} from "./auto-worktree.js";
|
|
182
|
+
import { autoWorktreeBranch, enterBranchModeForMilestone } from "./auto-worktree-branch-lifecycle.js";
|
|
183
|
+
import { createAutoWorktree } from "./auto-worktree-creation.js";
|
|
184
|
+
import { enterAutoWorktree, isInAutoWorktree } from "./auto-worktree-entry.js";
|
|
185
|
+
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
186
|
+
import { checkResourcesStale, readResourceVersion } from "./auto-worktree-resource-version.js";
|
|
187
|
+
import { escapeStaleWorktree } from "./auto-worktree-runtime-cleanup.js";
|
|
188
|
+
import { getAutoWorktreeOriginalBase } from "./auto-worktree-session-registry.js";
|
|
189
|
+
import { syncWorktreeStateBack } from "./auto-worktree-sync.js";
|
|
190
|
+
import { teardownAutoWorktree } from "./auto-worktree-teardown.js";
|
|
195
191
|
import { pruneQueueOrder } from "./queue-order.js";
|
|
196
192
|
import { startCommandPolling as _startCommandPolling, isRemoteConfigured } from "../remote-questions/manager.js";
|
|
197
|
-
import {
|
|
193
|
+
import { createDefaultMilestoneMergeTransaction } from "./milestone-merge-transaction.js";
|
|
198
194
|
|
|
199
195
|
import { debugLog, isDebugEnabled, writeDebugSummary } from "./debug-logger.js";
|
|
200
196
|
import {
|
|
@@ -621,11 +617,27 @@ export function startAutoDetached(
|
|
|
621
617
|
milestoneLock?: string | null;
|
|
622
618
|
},
|
|
623
619
|
): void {
|
|
624
|
-
void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch((err) => {
|
|
620
|
+
void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch(async (err) => {
|
|
625
621
|
const message = getErrorMessage(err);
|
|
626
622
|
ctx.ui.notify(`Auto-start failed: ${message}`, "error");
|
|
627
623
|
logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
|
|
628
624
|
debugLog("auto-start-failed", { error: message });
|
|
625
|
+
// Backstop cleanup (#1235): if startAuto threw after auto-mode was activated
|
|
626
|
+
// (e.g. an exception during bootstrap, before the loop's try/finally could
|
|
627
|
+
// run cleanupAfterLoopExit), s.active and the on-disk auto lock leak, so the
|
|
628
|
+
// re-entry guard silently no-ops every later /gsd auto until restart. Clear
|
|
629
|
+
// the leaked activation here so a failed start is always recoverable.
|
|
630
|
+
if (s.active) {
|
|
631
|
+
try {
|
|
632
|
+
await cleanupAfterLoopExit(ctx);
|
|
633
|
+
} catch (cleanupErr) {
|
|
634
|
+
logWarning(
|
|
635
|
+
"engine",
|
|
636
|
+
`auto start cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`,
|
|
637
|
+
{ file: "auto.ts" },
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
629
641
|
});
|
|
630
642
|
}
|
|
631
643
|
|
|
@@ -2326,7 +2338,7 @@ export function buildWorktreeLifecycleDeps(): WorktreeLifecycleDeps {
|
|
|
2326
2338
|
return new GitServiceImpl(basePath, gitConfig);
|
|
2327
2339
|
},
|
|
2328
2340
|
worktreeProjection: new WorktreeStateProjection(),
|
|
2329
|
-
mergeMilestone:
|
|
2341
|
+
mergeMilestone: createDefaultMilestoneMergeTransaction(),
|
|
2330
2342
|
};
|
|
2331
2343
|
}
|
|
2332
2344
|
|
|
@@ -2833,6 +2845,10 @@ export async function startAuto(
|
|
|
2833
2845
|
"info",
|
|
2834
2846
|
);
|
|
2835
2847
|
restoreHookState(s.basePath);
|
|
2848
|
+
// A restored activeHook has no live dispatch (the sidecar queue is not
|
|
2849
|
+
// persisted); re-enqueue it so the hook runs instead of blocking the next
|
|
2850
|
+
// unrelated unit's close-out (#1246).
|
|
2851
|
+
reconcileRestoredHookDispatch(s.basePath, s.sidecarQueue);
|
|
2836
2852
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
2837
2853
|
// bundled extension updates before resume-time verification/state logic runs.
|
|
2838
2854
|
// GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
|
|
@@ -2901,15 +2917,18 @@ export async function startAuto(
|
|
|
2901
2917
|
debugLog("resume-orchestration-resume", { error: err instanceof Error ? err.message : String(err) });
|
|
2902
2918
|
}
|
|
2903
2919
|
startAutoCommandPolling(s.basePath);
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2920
|
+
try {
|
|
2921
|
+
await runAutoLoopWithUok({
|
|
2922
|
+
ctx,
|
|
2923
|
+
pi,
|
|
2924
|
+
s,
|
|
2925
|
+
deps: loopDeps,
|
|
2926
|
+
runKernelLoop: runUokKernelLoop,
|
|
2927
|
+
runLegacyLoop: runLegacyAutoLoop,
|
|
2928
|
+
});
|
|
2929
|
+
} finally {
|
|
2930
|
+
await cleanupAfterLoopExit(ctx);
|
|
2931
|
+
}
|
|
2913
2932
|
return;
|
|
2914
2933
|
}
|
|
2915
2934
|
|
|
@@ -2937,7 +2956,15 @@ export async function startAuto(
|
|
|
2937
2956
|
bootstrapDeps,
|
|
2938
2957
|
freshStartAssessment,
|
|
2939
2958
|
);
|
|
2940
|
-
if (!ready)
|
|
2959
|
+
if (!ready) {
|
|
2960
|
+
// bootstrapAutoSession sets s.active = true (auto-start.ts) before several
|
|
2961
|
+
// post-activation bail-outs return false without a loop ever running (e.g.
|
|
2962
|
+
// the SQLite-unavailable gate). Without this, s.active and the auto lock
|
|
2963
|
+
// leak and every later /gsd auto silently no-ops via the re-entry guard
|
|
2964
|
+
// (#1235). Run the canonical cleanup so a failed bootstrap stays recoverable.
|
|
2965
|
+
if (s.active) await cleanupAfterLoopExit(ctx);
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2941
2968
|
|
|
2942
2969
|
// Build scope after bootstrap has populated s.basePath / s.originalBasePath /
|
|
2943
2970
|
// s.currentMilestoneId (including worktree setup inside bootstrapAutoSession).
|
|
@@ -2963,15 +2990,18 @@ export async function startAuto(
|
|
|
2963
2990
|
startAutoCommandPolling(s.basePath);
|
|
2964
2991
|
|
|
2965
2992
|
// Dispatch the first unit
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2993
|
+
try {
|
|
2994
|
+
await runAutoLoopWithUok({
|
|
2995
|
+
ctx,
|
|
2996
|
+
pi,
|
|
2997
|
+
s,
|
|
2998
|
+
deps: loopDeps,
|
|
2999
|
+
runKernelLoop: runUokKernelLoop,
|
|
3000
|
+
runLegacyLoop: runLegacyAutoLoop,
|
|
3001
|
+
});
|
|
3002
|
+
} finally {
|
|
3003
|
+
await cleanupAfterLoopExit(ctx);
|
|
3004
|
+
}
|
|
2975
3005
|
}
|
|
2976
3006
|
|
|
2977
3007
|
// describeNextUnit is imported from auto-dashboard.ts and re-exported
|
|
@@ -3129,19 +3159,56 @@ export async function dispatchHookUnit(
|
|
|
3129
3159
|
workspaceRoot: s.basePath,
|
|
3130
3160
|
});
|
|
3131
3161
|
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3162
|
+
// Resolve the hook's model honoring the configured `fallbacks[]` chain
|
|
3163
|
+
// (#1229). The manual trigger path bypasses selectAndApplyModel, so resolve
|
|
3164
|
+
// the primary→fallbacks order here and apply the first candidate available in
|
|
3165
|
+
// the registry — a blocked or unconfigured primary then transparently drops
|
|
3166
|
+
// to a fallback instead of silently reverting to the session model.
|
|
3167
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
3168
|
+
const availableModelIds = availableModels.map(
|
|
3169
|
+
(m: { provider: string; id: string }) => `${m.provider}/${m.id}`,
|
|
3170
|
+
);
|
|
3171
|
+
const hookModelConfig = resolveModelWithFallbacksForUnit(
|
|
3172
|
+
hookUnitType,
|
|
3173
|
+
targetBasePath,
|
|
3174
|
+
availableModelIds,
|
|
3175
|
+
);
|
|
3176
|
+
const modelCandidates = hookModelConfig
|
|
3177
|
+
? [hookModelConfig.primary, ...hookModelConfig.fallbacks]
|
|
3178
|
+
: hookModel
|
|
3179
|
+
? [hookModel]
|
|
3180
|
+
: [];
|
|
3181
|
+
if (modelCandidates.length > 0) {
|
|
3182
|
+
let applied = false;
|
|
3183
|
+
for (const candidate of modelCandidates) {
|
|
3184
|
+
const match = resolveModelId(candidate, availableModels, ctx.model?.provider);
|
|
3185
|
+
if (!match) continue;
|
|
3186
|
+
// Skip models the runtime has marked blocked or temporarily unavailable
|
|
3187
|
+
// (e.g. a primary that just tripped a provider limit) so the configured
|
|
3188
|
+
// fallbacks[] chain actually engages — parity with auto-mode's
|
|
3189
|
+
// selectAndApplyModel, which consults the same blocked-models store (#1229).
|
|
3190
|
+
if (isModelUnavailable(targetBasePath, match.provider, match.id)) continue;
|
|
3136
3191
|
try {
|
|
3137
|
-
await pi.setModel(match)
|
|
3192
|
+
if (await pi.setModel(match)) {
|
|
3193
|
+
// The manual trigger path bypasses selectAndApplyModel, so apply the
|
|
3194
|
+
// hook's per-field `thinking` (from `post_unit_hooks[].model`'s object
|
|
3195
|
+
// form) here against the just-set model rather than leaving the hook at
|
|
3196
|
+
// the session level (#1269). Absent → session level, unchanged.
|
|
3197
|
+
if (hookModelConfig?.thinking) {
|
|
3198
|
+
applyThinkingLevelForModel(pi, hookModelConfig.thinking, match, ctx);
|
|
3199
|
+
}
|
|
3200
|
+
applied = true;
|
|
3201
|
+
break;
|
|
3202
|
+
}
|
|
3138
3203
|
} catch (err) {
|
|
3139
|
-
/* non-fatal */
|
|
3140
|
-
logWarning("dispatch", `hook model set failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
3204
|
+
/* non-fatal — try the next fallback */
|
|
3205
|
+
logWarning("dispatch", `hook model set failed for ${candidate}: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
3141
3206
|
}
|
|
3142
|
-
}
|
|
3207
|
+
}
|
|
3208
|
+
if (!applied) {
|
|
3143
3209
|
ctx.ui.notify(
|
|
3144
|
-
`Hook model
|
|
3210
|
+
`Hook model${modelCandidates.length > 1 ? "s" : ""} "${modelCandidates.join(", ")}" not available. ` +
|
|
3211
|
+
`Falling back to current session model. ` +
|
|
3145
3212
|
`Ensure the model is defined in models.json and has auth configured.`,
|
|
3146
3213
|
"warning",
|
|
3147
3214
|
);
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type WorkflowDatabaseOpenResult,
|
|
15
15
|
type WorkflowDatabaseStatus,
|
|
16
16
|
} from "../db-workspace.js";
|
|
17
|
-
import { getAutoWorktreePath } from "../auto-worktree.js";
|
|
17
|
+
import { getAutoWorktreePath } from "../auto-worktree-path-resolution.js";
|
|
18
18
|
import { resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
19
19
|
import { worktreesDirs } from "../worktree-placement.js";
|
|
20
20
|
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "../auto-tool-tracking.js";
|
|
41
41
|
import { applyProviderPayloadPolicy } from "../provider-payload-policy.js";
|
|
42
42
|
|
|
43
|
-
import { checkToolCallLoop, recordToolCallLoopMutation, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
43
|
+
import { checkToolCallLoop, configureToolCallLoopGuard, recordToolCallLoopMutation, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
44
44
|
import { MINIMAL_AUTO_BASE_TOOL_NAMES } from "./core-session-tools.js";
|
|
45
45
|
import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
|
|
46
46
|
import { saveActivityLog } from "../activity-log.js";
|
|
@@ -594,6 +594,22 @@ async function applyCompactionThresholdOverride(ctx: ExtensionContext): Promise<
|
|
|
594
594
|
}
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
+
/**
|
|
598
|
+
* Apply user-tunable tool-call loop guard thresholds (#1198) from GSD
|
|
599
|
+
* preferences plus GSD_TOOL_LOOP_* env overrides. Runs at session boundaries
|
|
600
|
+
* so both interactive sessions and `/gsd auto` pick up the configuration.
|
|
601
|
+
* Non-fatal: falls back to built-in defaults when preferences cannot be loaded.
|
|
602
|
+
*/
|
|
603
|
+
async function applyToolCallLoopGuardConfig(basePath: string): Promise<void> {
|
|
604
|
+
try {
|
|
605
|
+
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
606
|
+
const prefs = loadEffectiveGSDPreferences(basePath);
|
|
607
|
+
configureToolCallLoopGuard(prefs?.preferences.tool_call_loop_guard);
|
|
608
|
+
} catch {
|
|
609
|
+
configureToolCallLoopGuard(null);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
597
613
|
function clearDeferredApprovalGate(basePath?: string): void {
|
|
598
614
|
if (!basePath) {
|
|
599
615
|
deferredApprovalGates.clear();
|
|
@@ -996,6 +1012,7 @@ export function registerHooks(
|
|
|
996
1012
|
}
|
|
997
1013
|
resetWriteGateState(basePath);
|
|
998
1014
|
resetToolCallLoopGuard();
|
|
1015
|
+
await applyToolCallLoopGuardConfig(basePath);
|
|
999
1016
|
approvalQuestionAbortInFlight = false;
|
|
1000
1017
|
clearDeferredApprovalGate();
|
|
1001
1018
|
await resetAskUserQuestionsTurnCache();
|
|
@@ -1087,6 +1104,7 @@ export function registerHooks(
|
|
|
1087
1104
|
initSessionNotifications(ctx);
|
|
1088
1105
|
resetWriteGateState(basePath);
|
|
1089
1106
|
resetToolCallLoopGuard();
|
|
1107
|
+
await applyToolCallLoopGuardConfig(basePath);
|
|
1090
1108
|
clearDeferredApprovalGate();
|
|
1091
1109
|
await resetAskUserQuestionsTurnCache();
|
|
1092
1110
|
clearDiscussionFlowState(basePath);
|
|
@@ -1707,7 +1725,7 @@ export function registerHooks(
|
|
|
1707
1725
|
}
|
|
1708
1726
|
}
|
|
1709
1727
|
if (!event.isError) {
|
|
1710
|
-
recordToolCallLoopMutation(toolName);
|
|
1728
|
+
recordToolCallLoopMutation(toolName, event.details);
|
|
1711
1729
|
refreshSourceObservationAfterMutation(toolName, event.input, ctx);
|
|
1712
1730
|
clearSourceObservationsAfterShell(toolName);
|
|
1713
1731
|
}
|
|
@@ -14,7 +14,7 @@ import { resolveModelWithFallbacksForUnit } from "../preferences-models.js";
|
|
|
14
14
|
import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
|
|
15
15
|
import { extractIntroAndRules } from "../knowledge-parser.js";
|
|
16
16
|
import { ensureCodebaseMapFresh, readCodebaseMap } from "../codebase-generator.js";
|
|
17
|
-
import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
|
|
17
|
+
import { getActiveAutoWorktreeContext } from "../auto-worktree-session-registry.js";
|
|
18
18
|
import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-session-state.js";
|
|
19
19
|
import { deriveState } from "../state.js";
|
|
20
20
|
import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
|
|
@@ -14,13 +14,21 @@
|
|
|
14
14
|
* to stop tooling for the rest of that turn and answer in text.
|
|
15
15
|
*
|
|
16
16
|
* The per-tool-name check (#783 Brief C) tracks call counts within a
|
|
17
|
-
* turn regardless of args, decaying after successful state-
|
|
17
|
+
* turn regardless of args, decaying after successful state-progressing tools.
|
|
18
18
|
* This catches improvisation
|
|
19
19
|
* loops where the model attempts the same missing workflow tool through
|
|
20
20
|
* varied surfaces (bash → `node -e` → CLI), each with a different
|
|
21
21
|
* signature, so the identical-args streak never trips, while allowing
|
|
22
|
-
* tool-heavy turns that are making
|
|
23
|
-
*
|
|
22
|
+
* tool-heavy turns that are making progress. Progress means a successful file
|
|
23
|
+
* mutation (edit/write) or a successful shell/exec call (bg_shell,
|
|
24
|
+
* gsd_uat_exec, …) — so a local model running a legitimate iterative
|
|
25
|
+
* debugging loop (restart server, npm install, curl an endpoint) is not
|
|
26
|
+
* blocked as a false loop (#1206). Whichever guard trips first blocks.
|
|
27
|
+
*
|
|
28
|
+
* Thresholds, exempt tools, and enable flags are user-tunable (#1198) via the
|
|
29
|
+
* `tool_call_loop_guard` key in `.gsd/PREFERENCES.md` and `GSD_TOOL_LOOP_*`
|
|
30
|
+
* environment variables, applied through {@link configureToolCallLoopGuard}.
|
|
31
|
+
* Defaults preserve the original hardcoded behavior.
|
|
24
32
|
*/
|
|
25
33
|
|
|
26
34
|
import { createHash } from "node:crypto";
|
|
@@ -28,26 +36,189 @@ import { INHERENTLY_REPEATABLE_TOOL_SET } from "./core-session-tools.js";
|
|
|
28
36
|
import { hasBrowserContractPrefix } from "../../shared/browser-contract.js";
|
|
29
37
|
import { canonicalToolName } from "../engine-hook-contract.js";
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
/** Built-in defaults. Preserved when preferences/env do not override them. */
|
|
40
|
+
const DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
|
|
41
|
+
const DEFAULT_PER_TOOL_DEFAULT_CAP = 6;
|
|
42
|
+
const DEFAULT_PER_TOOL_REPEATABLE_CAP = 15;
|
|
43
|
+
const DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS = ["find", "glob", "grep", "ls", "read", "search_and_read"] as const;
|
|
32
44
|
|
|
33
45
|
/** Interactive/user-facing tools where even 1 duplicate is confusing. */
|
|
34
46
|
const STRICT_LOOP_TOOLS = new Set(["ask_user_questions"]);
|
|
35
47
|
const MAX_CONSECUTIVE_STRICT = 1;
|
|
36
48
|
|
|
49
|
+
const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
|
|
50
|
+
|
|
37
51
|
/**
|
|
38
|
-
*
|
|
52
|
+
* Successful shell/exec calls are state progression too (#1206), not just file
|
|
53
|
+
* mutations. A local model debugging a UAT scenario legitimately restarts
|
|
54
|
+
* servers, runs `npm install`, and curls endpoints via bg_shell / gsd_uat_exec
|
|
55
|
+
* with varied args — productive work that never touches a file, so Guard 2's
|
|
56
|
+
* per-tool cap would otherwise misfire as a false loop. Decaying on these
|
|
57
|
+
* successful calls keeps the guard from aborting a progressing turn.
|
|
39
58
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
59
|
+
* Only successful calls reach {@link recordToolCallLoopMutation}: the
|
|
60
|
+
* tool_result hook skips `isError` results, and the bash tool throws on
|
|
61
|
+
* non-zero exit, so a genuinely stuck improvisation loop of failing commands
|
|
62
|
+
* (#783) never decays and still trips the cap. `async_bash` is excluded
|
|
63
|
+
* because it only registers a background job; command success is reported
|
|
64
|
+
* later via `await_job`. `bg_shell` may return `isError: false` for failed
|
|
65
|
+
* `run`/`start` outcomes, so the hook passes `details` for validation.
|
|
46
66
|
*/
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
67
|
+
const STATE_PROGRESSING_EXEC_TOOL_SET = new Set([
|
|
68
|
+
"bash",
|
|
69
|
+
"bg_shell",
|
|
70
|
+
"shell",
|
|
71
|
+
"powershell",
|
|
72
|
+
"gsd_exec",
|
|
73
|
+
"gsd_uat_exec",
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
function isSuccessfulBgShellLoopProgress(details: unknown): boolean {
|
|
77
|
+
if (!details || typeof details !== "object") return false;
|
|
78
|
+
const record = details as Record<string, unknown>;
|
|
79
|
+
switch (record.action) {
|
|
80
|
+
case "run":
|
|
81
|
+
return record.exitCode === 0 && record.timedOut !== true;
|
|
82
|
+
case "start":
|
|
83
|
+
case "restart": {
|
|
84
|
+
const process = record.process;
|
|
85
|
+
if (!process || typeof process !== "object") return false;
|
|
86
|
+
return (process as Record<string, unknown>).alive === true;
|
|
87
|
+
}
|
|
88
|
+
case "wait_for_ready":
|
|
89
|
+
return record.ready === true;
|
|
90
|
+
case "send_and_wait":
|
|
91
|
+
return record.matched === true;
|
|
92
|
+
default:
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* User-tunable configuration shape for the loop guard (#1198).
|
|
99
|
+
*
|
|
100
|
+
* Mirrors the `tool_call_loop_guard` key in `.gsd/PREFERENCES.md`. All fields
|
|
101
|
+
* are optional; anything omitted falls back to the built-in defaults so that
|
|
102
|
+
* existing installs keep their current behavior.
|
|
103
|
+
*/
|
|
104
|
+
export interface ToolCallLoopGuardConfig {
|
|
105
|
+
enabled?: boolean;
|
|
106
|
+
identical_args?: {
|
|
107
|
+
enabled?: boolean;
|
|
108
|
+
max_consecutive_calls?: number;
|
|
109
|
+
};
|
|
110
|
+
repeated_tool?: {
|
|
111
|
+
enabled?: boolean;
|
|
112
|
+
default_cap?: number;
|
|
113
|
+
repeatable_cap?: number;
|
|
114
|
+
exempt_tools?: string[];
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Active, resolved configuration. Set by {@link configureToolCallLoopGuard}
|
|
120
|
+
* from preferences + environment overrides, and deliberately NOT touched by
|
|
121
|
+
* {@link resetToolCallLoopGuard} so a session's tuning survives turn
|
|
122
|
+
* boundaries.
|
|
123
|
+
*/
|
|
124
|
+
interface ResolvedGuardConfig {
|
|
125
|
+
/** Master switch for both guards. */
|
|
126
|
+
guardEnabled: boolean;
|
|
127
|
+
identicalEnabled: boolean;
|
|
128
|
+
maxConsecutiveIdentical: number;
|
|
129
|
+
repeatedEnabled: boolean;
|
|
130
|
+
perToolDefaultCap: number;
|
|
131
|
+
perToolRepeatableCap: number;
|
|
132
|
+
/** Tool names exempt from Guard 2 (per-tool-name cap). */
|
|
133
|
+
perToolExempt: Set<string>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function defaultGuardConfig(): ResolvedGuardConfig {
|
|
137
|
+
return {
|
|
138
|
+
guardEnabled: true,
|
|
139
|
+
identicalEnabled: true,
|
|
140
|
+
maxConsecutiveIdentical: DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS,
|
|
141
|
+
repeatedEnabled: true,
|
|
142
|
+
perToolDefaultCap: DEFAULT_PER_TOOL_DEFAULT_CAP,
|
|
143
|
+
perToolRepeatableCap: DEFAULT_PER_TOOL_REPEATABLE_CAP,
|
|
144
|
+
perToolExempt: new Set<string>(DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let config: ResolvedGuardConfig = defaultGuardConfig();
|
|
149
|
+
|
|
150
|
+
/** Parse a positive-integer env var, returning undefined when unset/invalid. */
|
|
151
|
+
function envPositiveInt(name: string): number | undefined {
|
|
152
|
+
const raw = process.env[name];
|
|
153
|
+
if (raw === undefined || raw.trim() === "") return undefined;
|
|
154
|
+
const n = Number(raw);
|
|
155
|
+
return Number.isFinite(n) && n >= 1 ? Math.floor(n) : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Parse a boolean env var (`true`/`1` vs `false`/`0`), undefined when unset. */
|
|
159
|
+
function envBool(name: string): boolean | undefined {
|
|
160
|
+
const raw = process.env[name];
|
|
161
|
+
if (raw === undefined || raw.trim() === "") return undefined;
|
|
162
|
+
const v = raw.trim().toLowerCase();
|
|
163
|
+
if (v === "true" || v === "1" || v === "yes" || v === "on") return true;
|
|
164
|
+
if (v === "false" || v === "0" || v === "no" || v === "off") return false;
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Parse a comma-separated tool-name list env var. */
|
|
169
|
+
function envToolList(name: string): string[] {
|
|
170
|
+
const raw = process.env[name];
|
|
171
|
+
if (!raw) return [];
|
|
172
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve preferences + environment overrides into the active configuration.
|
|
177
|
+
* Environment variables win over preferences; preferences win over built-in
|
|
178
|
+
* defaults. User-supplied exempt tools are additive to the built-in exempt
|
|
179
|
+
* set so defaults are always preserved.
|
|
180
|
+
*
|
|
181
|
+
* Applied to both interactive sessions and `/gsd auto` (called from the
|
|
182
|
+
* session_start / session_switch hooks).
|
|
183
|
+
*/
|
|
184
|
+
export function configureToolCallLoopGuard(prefs?: ToolCallLoopGuardConfig | null): void {
|
|
185
|
+
const next = defaultGuardConfig();
|
|
186
|
+
|
|
187
|
+
if (prefs) {
|
|
188
|
+
if (typeof prefs.enabled === "boolean") next.guardEnabled = prefs.enabled;
|
|
189
|
+
if (prefs.identical_args) {
|
|
190
|
+
if (typeof prefs.identical_args.enabled === "boolean") next.identicalEnabled = prefs.identical_args.enabled;
|
|
191
|
+
if (typeof prefs.identical_args.max_consecutive_calls === "number" && prefs.identical_args.max_consecutive_calls >= 1) {
|
|
192
|
+
next.maxConsecutiveIdentical = Math.floor(prefs.identical_args.max_consecutive_calls);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (prefs.repeated_tool) {
|
|
196
|
+
if (typeof prefs.repeated_tool.enabled === "boolean") next.repeatedEnabled = prefs.repeated_tool.enabled;
|
|
197
|
+
if (typeof prefs.repeated_tool.default_cap === "number" && prefs.repeated_tool.default_cap >= 1) {
|
|
198
|
+
next.perToolDefaultCap = Math.floor(prefs.repeated_tool.default_cap);
|
|
199
|
+
}
|
|
200
|
+
if (typeof prefs.repeated_tool.repeatable_cap === "number" && prefs.repeated_tool.repeatable_cap >= 1) {
|
|
201
|
+
next.perToolRepeatableCap = Math.floor(prefs.repeated_tool.repeatable_cap);
|
|
202
|
+
}
|
|
203
|
+
for (const tool of prefs.repeated_tool.exempt_tools ?? []) {
|
|
204
|
+
if (typeof tool === "string" && tool.trim()) next.perToolExempt.add(tool.trim());
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Environment overrides (win over preferences).
|
|
210
|
+
const envEnabled = envBool("GSD_TOOL_LOOP_GUARD_ENABLED");
|
|
211
|
+
if (envEnabled !== undefined) next.guardEnabled = envEnabled;
|
|
212
|
+
const envIdenticalMax = envPositiveInt("GSD_TOOL_LOOP_IDENTICAL_MAX");
|
|
213
|
+
if (envIdenticalMax !== undefined) next.maxConsecutiveIdentical = envIdenticalMax;
|
|
214
|
+
const envDefaultCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_DEFAULT_CAP");
|
|
215
|
+
if (envDefaultCap !== undefined) next.perToolDefaultCap = envDefaultCap;
|
|
216
|
+
const envRepeatableCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_REPEATABLE_CAP");
|
|
217
|
+
if (envRepeatableCap !== undefined) next.perToolRepeatableCap = envRepeatableCap;
|
|
218
|
+
for (const tool of envToolList("GSD_TOOL_LOOP_EXEMPT_TOOLS")) next.perToolExempt.add(tool);
|
|
219
|
+
|
|
220
|
+
config = next;
|
|
221
|
+
}
|
|
51
222
|
|
|
52
223
|
let consecutiveCount = 0;
|
|
53
224
|
let lastSignature = "";
|
|
@@ -82,17 +253,20 @@ function hashToolCall(toolName: string, args: Record<string, unknown>): string {
|
|
|
82
253
|
* Returns `{ block: true, reason }` when the loop threshold is exceeded.
|
|
83
254
|
*
|
|
84
255
|
* Two independent guards run; whichever trips first blocks:
|
|
85
|
-
* 1. Identical-signature streak (
|
|
256
|
+
* 1. Identical-signature streak (config.maxConsecutiveIdentical, strict for
|
|
86
257
|
* ask_user_questions).
|
|
87
|
-
* 2. Per-tool-name cap (
|
|
258
|
+
* 2. Per-tool-name cap (config.perToolDefaultCap / config.perToolRepeatableCap),
|
|
88
259
|
* independent of args, reset after file-mutation progress — catches
|
|
89
260
|
* improvisation loops (#783).
|
|
261
|
+
*
|
|
262
|
+
* Both guards, their thresholds, and the per-tool exempt set are user-tunable
|
|
263
|
+
* via {@link configureToolCallLoopGuard} (#1198).
|
|
90
264
|
*/
|
|
91
265
|
export function checkToolCallLoop(
|
|
92
266
|
toolName: string,
|
|
93
267
|
args: Record<string, unknown>,
|
|
94
268
|
): { block: boolean; reason?: string; count?: number } {
|
|
95
|
-
if (!enabled) return { block: false, count: 0 };
|
|
269
|
+
if (!enabled || !config.guardEnabled) return { block: false, count: 0 };
|
|
96
270
|
|
|
97
271
|
const sig = hashToolCall(toolName, args);
|
|
98
272
|
|
|
@@ -107,14 +281,16 @@ export function checkToolCallLoop(
|
|
|
107
281
|
// ── Guard 1: identical-signature streak ──
|
|
108
282
|
const threshold = STRICT_LOOP_TOOLS.has(toolName)
|
|
109
283
|
? MAX_CONSECUTIVE_STRICT
|
|
110
|
-
:
|
|
284
|
+
: config.maxConsecutiveIdentical;
|
|
111
285
|
|
|
112
|
-
if (consecutiveCount > threshold) {
|
|
286
|
+
if (config.identicalEnabled && consecutiveCount > threshold) {
|
|
113
287
|
return {
|
|
114
288
|
block: true,
|
|
115
289
|
reason:
|
|
116
290
|
`Tool loop detected (identical args): ${toolName} called ${consecutiveCount} times ` +
|
|
117
|
-
`with identical arguments. Blocking to prevent infinite loop. ` +
|
|
291
|
+
`with identical arguments (max ${threshold}). Blocking to prevent infinite loop. ` +
|
|
292
|
+
`Raise tool_call_loop_guard.identical_args.max_consecutive_calls in .gsd/PREFERENCES.md ` +
|
|
293
|
+
`(or GSD_TOOL_LOOP_IDENTICAL_MAX) if this is expected. ` +
|
|
118
294
|
`Do not retry this tool or call other tools this turn — stop and respond to the user in text.`,
|
|
119
295
|
count: consecutiveCount,
|
|
120
296
|
};
|
|
@@ -139,15 +315,16 @@ export function checkToolCallLoop(
|
|
|
139
315
|
// Guard 1's identical-signature streak still catches a genuinely stuck
|
|
140
316
|
// browser loop.
|
|
141
317
|
if (
|
|
142
|
-
|
|
318
|
+
!config.repeatedEnabled ||
|
|
319
|
+
config.perToolExempt.has(toolName) ||
|
|
143
320
|
hasBrowserContractPrefix(canonicalToolName(toolName))
|
|
144
321
|
) {
|
|
145
322
|
return { block: false, count: consecutiveCount };
|
|
146
323
|
}
|
|
147
324
|
|
|
148
325
|
const perToolCap = INHERENTLY_REPEATABLE_TOOL_SET.has(toolName)
|
|
149
|
-
?
|
|
150
|
-
:
|
|
326
|
+
? config.perToolRepeatableCap
|
|
327
|
+
: config.perToolDefaultCap;
|
|
151
328
|
|
|
152
329
|
if (perToolCount > perToolCap) {
|
|
153
330
|
return {
|
|
@@ -156,6 +333,9 @@ export function checkToolCallLoop(
|
|
|
156
333
|
`Tool loop detected (repeated tool): ${toolName} called ${perToolCount} times ` +
|
|
157
334
|
`this turn (cap ${perToolCap}). Blocking to prevent infinite loop. ` +
|
|
158
335
|
`The tool may be unavailable or failing repeatedly. ` +
|
|
336
|
+
`Raise tool_call_loop_guard.repeated_tool.default_cap/repeatable_cap or add "${toolName}" ` +
|
|
337
|
+
`to tool_call_loop_guard.repeated_tool.exempt_tools in .gsd/PREFERENCES.md ` +
|
|
338
|
+
`(or GSD_TOOL_LOOP_REPEATED_* / GSD_TOOL_LOOP_EXEMPT_TOOLS) if this is expected. ` +
|
|
159
339
|
`Do not retry this tool or pivot to other tools this turn — stop and respond to the user in text.`,
|
|
160
340
|
count: perToolCount,
|
|
161
341
|
};
|
|
@@ -164,10 +344,21 @@ export function checkToolCallLoop(
|
|
|
164
344
|
return { block: false, count: consecutiveCount };
|
|
165
345
|
}
|
|
166
346
|
|
|
167
|
-
/**
|
|
168
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Record successful state progress so Guard 2 can decay on the next count.
|
|
349
|
+
* File mutations (edit/write/…) and successful shell/exec calls (bash,
|
|
350
|
+
* bg_shell, gsd_uat_exec, …) both count as progress (#1092, #1206). The caller
|
|
351
|
+
* only invokes this for non-error results, so failing improvisation loops
|
|
352
|
+
* (#783) never decay and still trip the per-tool cap.
|
|
353
|
+
*/
|
|
354
|
+
export function recordToolCallLoopMutation(toolName: string, details?: unknown): void {
|
|
169
355
|
if (!enabled) return;
|
|
170
|
-
if (
|
|
356
|
+
if (STATE_MUTATING_TOOL_SET.has(toolName)) {
|
|
357
|
+
mutationEpoch++;
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (!STATE_PROGRESSING_EXEC_TOOL_SET.has(toolName)) return;
|
|
361
|
+
if (toolName === "bg_shell" && details !== undefined && !isSuccessfulBgShellLoopProgress(details)) return;
|
|
171
362
|
mutationEpoch++;
|
|
172
363
|
}
|
|
173
364
|
|
|
@@ -675,10 +675,16 @@ export function ensureCodebaseMapFresh(
|
|
|
675
675
|
|
|
676
676
|
const metadata = parseCodebaseMapMetadata(existing);
|
|
677
677
|
const existingDescriptions = parseCodebaseMap(existing);
|
|
678
|
+
// Compare against the truncated count: when the repository has more tracked
|
|
679
|
+
// files than maxFiles, the map only lists the first maxFiles entries, so a
|
|
680
|
+
// stored fileCount above the cap (e.g. from an older map that recorded the
|
|
681
|
+
// full count) still matches the current truncated listing. Comparing raw
|
|
682
|
+
// values would flag "file-count-changed" on every turn, rewriting the
|
|
683
|
+
// timestamp and invalidating the provider KV cache prefix.
|
|
678
684
|
const staleReason =
|
|
679
685
|
!metadata ? "missing-metadata"
|
|
680
686
|
: metadata.fingerprint !== fingerprint ? "files-changed"
|
|
681
|
-
: metadata.fileCount !== listed.files.length ? "file-count-changed"
|
|
687
|
+
: Math.min(metadata.fileCount, resolved.maxFiles) !== listed.files.length ? "file-count-changed"
|
|
682
688
|
: metadata.truncated !== listed.truncated ? "truncation-changed"
|
|
683
689
|
: undefined;
|
|
684
690
|
|