@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
|
@@ -8,7 +8,7 @@ import { existsSync, readdirSync } from "node:fs";
|
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
9
|
import { resolveGsdPathContract } from "./paths.js";
|
|
10
10
|
import { worktreePathFor, worktreesDirs } from "./worktree-placement.js";
|
|
11
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
11
|
+
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
12
12
|
import { buildWorktreeLifecycleDeps } from "./auto.js";
|
|
13
13
|
import { mergeMilestoneStandalone, } from "./worktree-lifecycle.js";
|
|
14
14
|
import { MergeConflictError } from "./git-service.js";
|
|
@@ -12,7 +12,9 @@ import { join, dirname } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { gsdRoot } from "./paths.js";
|
|
14
14
|
import { createWorktree, worktreePath } from "./worktree-manager.js";
|
|
15
|
-
import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe
|
|
15
|
+
import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe } from "./auto-worktree-branch-lifecycle.js";
|
|
16
|
+
import { syncGsdStateToWorktreeByScope } from "./auto-worktree-sync.js";
|
|
17
|
+
import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
|
|
16
18
|
import { nativeBranchExists } from "./native-git-bridge.js";
|
|
17
19
|
import { readIntegrationBranch } from "./git-service.js";
|
|
18
20
|
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* via prefix matching, so existing projects work without migration.
|
|
11
11
|
*/
|
|
12
12
|
import { readdirSync, existsSync, realpathSync, statSync, Dirent } from "node:fs";
|
|
13
|
-
import { join, dirname, normalize, resolve } from "node:path";
|
|
13
|
+
import { join, dirname, normalize, relative, resolve } from "node:path";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
16
16
|
import { nativeScanGsdTree } from "./native-parser-bridge.js";
|
|
@@ -331,21 +331,26 @@ export function resolveGsdPathContract(workRoot, originalProjectRoot) {
|
|
|
331
331
|
const resolvedWorkRoot = resolve(workRoot || process.cwd());
|
|
332
332
|
const isWorktree = isGsdWorktreePath(resolvedWorkRoot);
|
|
333
333
|
if (isWorktree && !originalProjectRoot?.trim()) {
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
334
|
+
const rawProjectGsd = resolveExternalStateProjectGsdFromWorktreePath(resolvedWorkRoot);
|
|
335
|
+
if (rawProjectGsd) {
|
|
336
|
+
// Canonicalize the `.gsd` root so the DB path matches what every other
|
|
337
|
+
// accessor (gsdRoot/gsdProjectionRoot) returns — otherwise an unresolved
|
|
338
|
+
// symlink (e.g. `/mnt/c/.../.gsd` → native ext4 on WSL) selects the wrong
|
|
339
|
+
// journal mode and yields a fragile, move-prone handle. See issue #1239.
|
|
340
|
+
const projectGsd = normalizeRealPath(rawProjectGsd);
|
|
336
341
|
return {
|
|
337
342
|
projectRoot: dirname(dirname(projectGsd)),
|
|
338
343
|
workRoot: resolvedWorkRoot,
|
|
339
344
|
projectGsd,
|
|
340
|
-
worktreeGsd: join(resolvedWorkRoot, ".gsd"),
|
|
345
|
+
worktreeGsd: normalizeRealPath(join(resolvedWorkRoot, ".gsd")),
|
|
341
346
|
projectDb: join(projectGsd, "gsd.db"),
|
|
342
347
|
isWorktree,
|
|
343
348
|
};
|
|
344
349
|
}
|
|
345
350
|
}
|
|
346
351
|
const projectRoot = resolve(resolveWorktreeProjectRoot(resolvedWorkRoot, originalProjectRoot));
|
|
347
|
-
const projectGsd = join(projectRoot, ".gsd");
|
|
348
|
-
const worktreeGsd = isWorktree ? join(resolvedWorkRoot, ".gsd") : null;
|
|
352
|
+
const projectGsd = normalizeRealPath(join(projectRoot, ".gsd"));
|
|
353
|
+
const worktreeGsd = isWorktree ? normalizeRealPath(join(resolvedWorkRoot, ".gsd")) : null;
|
|
349
354
|
return {
|
|
350
355
|
projectRoot,
|
|
351
356
|
workRoot: resolvedWorkRoot,
|
|
@@ -669,7 +674,10 @@ function pickPreferredPhaseDir(phasesDir, matches) {
|
|
|
669
674
|
}
|
|
670
675
|
return best;
|
|
671
676
|
}
|
|
672
|
-
function
|
|
677
|
+
function slugLooksLikeTeamSuffixProjection(slugPart) {
|
|
678
|
+
return /^[a-z0-9]{6}(-|$)/.test(slugPart);
|
|
679
|
+
}
|
|
680
|
+
function phaseDirMatchesMilestoneId(dirName, milestoneId, phaseNum, allowTeamSuffixSlugs = false) {
|
|
673
681
|
const numMatch = dirName.match(/^(\d+)-(.*)$/);
|
|
674
682
|
if (!numMatch || parseInt(numMatch[1], 10) !== phaseNum)
|
|
675
683
|
return false;
|
|
@@ -678,8 +686,9 @@ function phaseDirMatchesMilestoneId(dirName, milestoneId, phaseNum) {
|
|
|
678
686
|
if (suffix) {
|
|
679
687
|
return slugPart === suffix || slugPart.startsWith(`${suffix}-`);
|
|
680
688
|
}
|
|
681
|
-
|
|
682
|
-
|
|
689
|
+
if (slugLooksLikeTeamSuffixProjection(slugPart) && !allowTeamSuffixSlugs)
|
|
690
|
+
return false;
|
|
691
|
+
return true;
|
|
683
692
|
}
|
|
684
693
|
function resolvePhaseDir(basePath, milestoneId) {
|
|
685
694
|
// Try flat-phase layout first: phases/NN-slug/ (always scan phases/, even when
|
|
@@ -700,6 +709,16 @@ function resolvePhaseDir(basePath, milestoneId) {
|
|
|
700
709
|
matches.push(entry.name);
|
|
701
710
|
}
|
|
702
711
|
}
|
|
712
|
+
// Bare milestone ids may only have a suffixed team-mode projection on disk.
|
|
713
|
+
if (matches.length === 0 && !milestoneIdUniqueSuffix(milestoneId)) {
|
|
714
|
+
for (const entry of readdirSync(phasesDir, { withFileTypes: true })) {
|
|
715
|
+
if (!entry.isDirectory())
|
|
716
|
+
continue;
|
|
717
|
+
if (phaseDirMatchesMilestoneId(entry.name, milestoneId, phaseNum, true)) {
|
|
718
|
+
matches.push(entry.name);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
703
722
|
}
|
|
704
723
|
catch {
|
|
705
724
|
// unreadable — fall through
|
|
@@ -865,11 +884,22 @@ export function resolveTasksDir(basePath, milestoneId, sliceId) {
|
|
|
865
884
|
* Resolve a specific task file.
|
|
866
885
|
*/
|
|
867
886
|
export function resolveTaskFile(basePath, milestoneId, sliceId, taskId, suffix) {
|
|
868
|
-
const
|
|
869
|
-
if (!
|
|
887
|
+
const phaseDir = resolveMilestonePath(basePath, milestoneId);
|
|
888
|
+
if (!phaseDir)
|
|
870
889
|
return null;
|
|
871
|
-
const
|
|
872
|
-
|
|
890
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
891
|
+
const isLegacy = phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\");
|
|
892
|
+
if (suffix !== "PLAN" && !isLegacy) {
|
|
893
|
+
const flatPath = join(phaseDir, buildTaskFileName(taskId, suffix));
|
|
894
|
+
return existsSync(flatPath) ? flatPath : null;
|
|
895
|
+
}
|
|
896
|
+
const tDir = resolveTasksDir(basePath, milestoneId, sliceId);
|
|
897
|
+
if (tDir) {
|
|
898
|
+
const file = resolveFile(tDir, taskId, suffix);
|
|
899
|
+
if (file)
|
|
900
|
+
return join(tDir, file);
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
873
903
|
}
|
|
874
904
|
// ─── Relative Path Builders (for prompts — .gsd/milestones/...) ────────────
|
|
875
905
|
/**
|
|
@@ -901,29 +931,30 @@ export function relMilestonePath(basePath, milestoneId, title) {
|
|
|
901
931
|
return `.gsd/${LAYOUT_SEGMENTS.level1}/${canonicalPhaseDirName(milestoneId, title)}`;
|
|
902
932
|
}
|
|
903
933
|
/**
|
|
904
|
-
* Build
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
export function relMilestoneFile(basePath, milestoneId, suffix) {
|
|
910
|
-
const mRel = relMilestonePath(basePath, milestoneId);
|
|
911
|
-
// resolveMilestoneFile checks NN-SUFFIX.md (flat-phase) and MID-SUFFIX.md (legacy).
|
|
912
|
-
const absFile = resolveMilestoneFile(basePath, milestoneId, suffix);
|
|
913
|
-
if (absFile) {
|
|
914
|
-
const fileName = absFile.split(/[/\\]/).pop();
|
|
915
|
-
return `${mRel}/${fileName}`;
|
|
916
|
-
}
|
|
917
|
-
// File doesn't exist yet — use layout-aware fallback filename.
|
|
934
|
+
* Build the canonical absolute write target for a milestone file.
|
|
935
|
+
* Existing compatibility filenames are intentionally ignored; readers that need
|
|
936
|
+
* to preserve an existing file should call resolveMilestoneFile first.
|
|
937
|
+
*/
|
|
938
|
+
export function targetMilestoneFile(basePath, milestoneId, suffix, title) {
|
|
918
939
|
const mDir = resolveMilestonePath(basePath, milestoneId);
|
|
919
940
|
const legacyBase = legacyMilestonesDir(basePath);
|
|
920
941
|
const isLegacy = mDir
|
|
921
942
|
? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
|
|
922
|
-
:
|
|
943
|
+
: isLegacyMilestonesLayout(basePath);
|
|
944
|
+
const dir = mDir ?? join(isLegacy ? legacyBase : milestonesDir(basePath), isLegacy ? milestoneId : canonicalPhaseDirName(milestoneId, title));
|
|
923
945
|
const fileName = isLegacy
|
|
924
946
|
? `${milestoneId}-${suffix}.md`
|
|
925
947
|
: `${String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0")}-${suffix}.md`;
|
|
926
|
-
return
|
|
948
|
+
return join(dir, fileName);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Build relative .gsd/ path to a milestone file.
|
|
952
|
+
* Preserves an existing compatibility filename when present; otherwise falls
|
|
953
|
+
* back to the canonical write target.
|
|
954
|
+
*/
|
|
955
|
+
export function relMilestoneFile(basePath, milestoneId, suffix, title) {
|
|
956
|
+
const rel = relative(gsdProjectionRoot(basePath), resolveMilestoneFile(basePath, milestoneId, suffix) ?? targetMilestoneFile(basePath, milestoneId, suffix, title)).replace(/\\/g, "/");
|
|
957
|
+
return `.gsd/${rel}`;
|
|
927
958
|
}
|
|
928
959
|
/**
|
|
929
960
|
* Build relative .gsd/ path to a slice directory.
|
|
@@ -951,32 +982,36 @@ export function relSlicePath(basePath, milestoneId, sliceId, milestoneTitle) {
|
|
|
951
982
|
return relMilestonePath(basePath, milestoneId, milestoneTitle);
|
|
952
983
|
}
|
|
953
984
|
/**
|
|
954
|
-
* Build
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
* @param milestoneTitle - Optional milestone title forwarded to relSlicePath /
|
|
958
|
-
* relMilestonePath for title-aware flat-phase fallback dir naming. Only used
|
|
959
|
-
* when the phase directory does not yet exist on disk.
|
|
985
|
+
* Build the canonical absolute write target for a slice file.
|
|
986
|
+
* Existing compatibility filenames are intentionally ignored; readers that need
|
|
987
|
+
* to preserve an existing file should call resolveSliceFile first.
|
|
960
988
|
*/
|
|
961
|
-
export function
|
|
962
|
-
const sRel = relSlicePath(basePath, milestoneId, sliceId, milestoneTitle);
|
|
963
|
-
const absPath = resolveSliceFile(basePath, milestoneId, sliceId, suffix);
|
|
964
|
-
if (absPath) {
|
|
965
|
-
const fileName = absPath.split(/[/\\]/).pop();
|
|
966
|
-
return `${sRel}/${fileName}`;
|
|
967
|
-
}
|
|
968
|
-
// Fallback when file doesn't exist yet — use layout-aware filename.
|
|
989
|
+
export function targetSliceFile(basePath, milestoneId, sliceId, suffix, milestoneTitle) {
|
|
969
990
|
const mDir = resolveMilestonePath(basePath, milestoneId);
|
|
970
991
|
const legacyBase = legacyMilestonesDir(basePath);
|
|
971
992
|
const isLegacy = mDir
|
|
972
993
|
? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
|
|
973
|
-
:
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
994
|
+
: isLegacyMilestonesLayout(basePath);
|
|
995
|
+
const milestoneDir = mDir
|
|
996
|
+
?? dirname(targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestoneTitle));
|
|
997
|
+
const slicesDir = join(milestoneDir, "slices");
|
|
998
|
+
const dir = isLegacy
|
|
999
|
+
? join(slicesDir, resolveDir(slicesDir, sliceId) ?? sliceId)
|
|
1000
|
+
: milestoneDir;
|
|
1001
|
+
const fileName = isLegacy
|
|
1002
|
+
? `${sliceId}-${suffix}.md`
|
|
1003
|
+
: planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), suffix);
|
|
1004
|
+
return join(dir, fileName);
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Build relative .gsd/ path to a slice file.
|
|
1008
|
+
* Preserves an existing compatibility filename when present; otherwise falls
|
|
1009
|
+
* back to the canonical write target.
|
|
1010
|
+
*/
|
|
1011
|
+
export function relSliceFile(basePath, milestoneId, sliceId, suffix, milestoneTitle) {
|
|
1012
|
+
const rel = relative(gsdProjectionRoot(basePath), resolveSliceFile(basePath, milestoneId, sliceId, suffix)
|
|
1013
|
+
?? targetSliceFile(basePath, milestoneId, sliceId, suffix, milestoneTitle)).replace(/\\/g, "/");
|
|
1014
|
+
return `.gsd/${rel}`;
|
|
980
1015
|
}
|
|
981
1016
|
/**
|
|
982
1017
|
* Build relative .gsd/ path to a task file.
|
|
@@ -985,7 +1020,7 @@ export function relSliceFile(basePath, milestoneId, sliceId, suffix, milestoneTi
|
|
|
985
1020
|
* Flat-phase: PLAN → slice plan path (tasks as checkboxes); other suffixes
|
|
986
1021
|
* (e.g. SUMMARY) → phase dir / TID-SUFFIX.md
|
|
987
1022
|
*/
|
|
988
|
-
export function relTaskFile(basePath, milestoneId, sliceId, taskId, suffix) {
|
|
1023
|
+
export function relTaskFile(basePath, milestoneId, sliceId, taskId, suffix, milestoneTitle) {
|
|
989
1024
|
const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
|
|
990
1025
|
const phaseDir = resolveMilestonePath(basePath, milestoneId);
|
|
991
1026
|
// Legacy: slice path is a slices/SID/ subdir inside the milestone dir
|
|
@@ -997,6 +1032,6 @@ export function relTaskFile(basePath, milestoneId, sliceId, taskId, suffix) {
|
|
|
997
1032
|
if (suffix === "PLAN") {
|
|
998
1033
|
return relSliceFile(basePath, milestoneId, sliceId, "PLAN");
|
|
999
1034
|
}
|
|
1000
|
-
const relS = relSlicePath(basePath, milestoneId, sliceId);
|
|
1035
|
+
const relS = relSlicePath(basePath, milestoneId, sliceId, milestoneTitle);
|
|
1001
1036
|
return `${relS}/${buildTaskFileName(taskId, suffix)}`;
|
|
1002
1037
|
}
|
|
@@ -42,6 +42,33 @@ export function persistHookState(basePath) {
|
|
|
42
42
|
export function restoreHookState(basePath) {
|
|
43
43
|
getOrCreateRegistry().restoreState(basePath);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Reconcile a restored `activeHook` against the session's sidecar queue.
|
|
47
|
+
*
|
|
48
|
+
* The registry persists `activeHook` to disk but the pending hook *dispatch*
|
|
49
|
+
* lives only on the non-persisted `s.sidecarQueue`. After a pause/resume or
|
|
50
|
+
* crash-recovery, `restoreHookState` re-hydrates `activeHook` while the dispatch
|
|
51
|
+
* is gone, so the registry believes a hook is in-flight but nothing will ever
|
|
52
|
+
* complete it — and the next unrelated unit's close-out is falsely charged
|
|
53
|
+
* against the stale hook and blocked. Re-enqueue the missing dispatch so the
|
|
54
|
+
* hook actually runs, restoring the invariant that a persisted `activeHook`
|
|
55
|
+
* always has a live dispatch (#1246).
|
|
56
|
+
*/
|
|
57
|
+
export function reconcileRestoredHookDispatch(basePath, sidecarQueue) {
|
|
58
|
+
const dispatch = getOrCreateRegistry().getPendingHookDispatch(basePath);
|
|
59
|
+
if (!dispatch)
|
|
60
|
+
return;
|
|
61
|
+
const alreadyQueued = sidecarQueue.some(item => item.kind === "hook" && item.unitType === dispatch.unitType);
|
|
62
|
+
if (alreadyQueued)
|
|
63
|
+
return;
|
|
64
|
+
sidecarQueue.push({
|
|
65
|
+
kind: "hook",
|
|
66
|
+
unitType: dispatch.unitType,
|
|
67
|
+
unitId: dispatch.unitId,
|
|
68
|
+
prompt: dispatch.prompt,
|
|
69
|
+
model: dispatch.model,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
45
72
|
export function clearPersistedHookState(basePath) {
|
|
46
73
|
getOrCreateRegistry().clearPersistedState(basePath);
|
|
47
74
|
}
|
|
@@ -66,6 +66,39 @@ function resolveWinningPhase(models, chain) {
|
|
|
66
66
|
}
|
|
67
67
|
return undefined;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Normalize a single model field into a {@link ResolvedModelConfig}.
|
|
71
|
+
*
|
|
72
|
+
* Accepts either the legacy bare-string form or the extended object form
|
|
73
|
+
* (`{ model, provider?, fallbacks? }`). This is the shared normalization used
|
|
74
|
+
* for phase buckets so sibling single-model fields — `post_unit_hooks[].model`,
|
|
75
|
+
* `auto_supervisor.model`, and `reactive_execution.subagent_model` — honor
|
|
76
|
+
* `fallbacks[]` identically rather than accepting a bare string only (#1229).
|
|
77
|
+
*
|
|
78
|
+
* Returns undefined for an unset, blank, or model-less field.
|
|
79
|
+
*/
|
|
80
|
+
export function normalizeModelFieldConfig(field) {
|
|
81
|
+
if (!field)
|
|
82
|
+
return undefined;
|
|
83
|
+
if (typeof field === "string") {
|
|
84
|
+
return field.trim() ? { primary: field, fallbacks: [] } : undefined;
|
|
85
|
+
}
|
|
86
|
+
if (!field.model)
|
|
87
|
+
return undefined;
|
|
88
|
+
// When provider is explicitly set, prepend it to the model ID so the
|
|
89
|
+
// resolution code in auto.ts can do an explicit provider match.
|
|
90
|
+
const primary = field.provider && !field.model.includes("/")
|
|
91
|
+
? `${field.provider}/${field.model}`
|
|
92
|
+
: field.model;
|
|
93
|
+
// Carry the per-field `thinking` sub-field through so the dispatch site can
|
|
94
|
+
// prefer it over the session/floor level, mirroring phase-inline thinking
|
|
95
|
+
// (#1269). `undefined` is preserved as absent so behavior is unchanged.
|
|
96
|
+
return {
|
|
97
|
+
primary,
|
|
98
|
+
fallbacks: field.fallbacks ?? [],
|
|
99
|
+
...(field.thinking ? { thinking: field.thinking } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
69
102
|
/**
|
|
70
103
|
* Resolve model and fallbacks for a given auto-mode unit type.
|
|
71
104
|
* Returns the primary model and ordered fallbacks, or undefined if not configured.
|
|
@@ -73,6 +106,15 @@ function resolveWinningPhase(models, chain) {
|
|
|
73
106
|
* Supports both legacy string format and extended object format:
|
|
74
107
|
* - Legacy: `planning: claude-opus-4-6`
|
|
75
108
|
* - Extended: `planning: { model: claude-opus-4-6, fallbacks: [glm-5, minimax-m2.5] }`
|
|
109
|
+
*
|
|
110
|
+
* Hook unit types (`hook/<name>`) resolve against the matching
|
|
111
|
+
* `post_unit_hooks[]` entry's `model` field so hook sessions honor the same
|
|
112
|
+
* `fallbacks[]` chain as phase buckets — the recovery path can then switch to
|
|
113
|
+
* a fallback provider instead of hard-failing (and potentially pausing) the
|
|
114
|
+
* run on a transient provider trip (#1229).
|
|
115
|
+
*
|
|
116
|
+
* The synthetic `supervisor` unit type resolves against `auto_supervisor.model`
|
|
117
|
+
* for supervisor interventions (wrap-up warnings, timeout recovery).
|
|
76
118
|
*/
|
|
77
119
|
export function resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId, skipProfileDefaults = false) {
|
|
78
120
|
const loadOpts = {
|
|
@@ -81,26 +123,25 @@ export function resolveModelWithFallbacksForUnit(unitType, basePath, availableMo
|
|
|
81
123
|
...(skipProfileDefaults ? { skipProfileDefaults: true } : {}),
|
|
82
124
|
};
|
|
83
125
|
const prefs = loadEffectiveGSDPreferences(basePath, loadOpts);
|
|
126
|
+
// Supervisor interventions resolve against `auto_supervisor.model` (#1229).
|
|
127
|
+
if (unitType === "supervisor") {
|
|
128
|
+
return normalizeModelFieldConfig(prefs?.preferences?.auto_supervisor?.model);
|
|
129
|
+
}
|
|
130
|
+
// Hook sessions resolve against their own `post_unit_hooks[].model` field,
|
|
131
|
+
// which now shares the phase-bucket object form (#1229).
|
|
132
|
+
if (unitType.startsWith("hook/")) {
|
|
133
|
+
const hookName = unitType.slice("hook/".length);
|
|
134
|
+
const hooks = prefs?.preferences?.post_unit_hooks;
|
|
135
|
+
const hook = Array.isArray(hooks) ? hooks.find((h) => h.name === hookName) : undefined;
|
|
136
|
+
return normalizeModelFieldConfig(hook?.model);
|
|
137
|
+
}
|
|
84
138
|
const chain = phaseChainForUnit(unitType);
|
|
85
139
|
if (!chain)
|
|
86
140
|
return undefined;
|
|
87
141
|
const winner = resolveWinningPhase(prefs?.preferences?.models, chain);
|
|
88
142
|
if (!winner)
|
|
89
143
|
return undefined;
|
|
90
|
-
|
|
91
|
-
// Normalize: string -> { model, fallbacks: [] }
|
|
92
|
-
if (typeof phaseConfig === "string") {
|
|
93
|
-
return { primary: phaseConfig, fallbacks: [] };
|
|
94
|
-
}
|
|
95
|
-
// When provider is explicitly set, prepend it to the model ID so the
|
|
96
|
-
// resolution code in auto.ts can do an explicit provider match.
|
|
97
|
-
const primary = phaseConfig.provider && !phaseConfig.model.includes("/")
|
|
98
|
-
? `${phaseConfig.provider}/${phaseConfig.model}`
|
|
99
|
-
: phaseConfig.model;
|
|
100
|
-
return {
|
|
101
|
-
primary,
|
|
102
|
-
fallbacks: phaseConfig.fallbacks ?? [],
|
|
103
|
-
};
|
|
144
|
+
return normalizeModelFieldConfig(winner.config);
|
|
104
145
|
}
|
|
105
146
|
/**
|
|
106
147
|
* Resolve the explicitly configured reasoning effort for a unit type (ADR-026).
|
|
@@ -125,6 +166,13 @@ export function resolveModelWithFallbacksForUnit(unitType, basePath, availableMo
|
|
|
125
166
|
* applied here.
|
|
126
167
|
*/
|
|
127
168
|
export function resolveThinkingLevelForUnit(unitType, basePath, availableModelIds) {
|
|
169
|
+
// Single-field configs — `auto_supervisor.model` and `post_unit_hooks[].model`
|
|
170
|
+
// — carry their own inline `thinking` on the object model form and have no
|
|
171
|
+
// phase-bucket chain, so the walk below never covers them. Resolve their
|
|
172
|
+
// per-field level directly (#1269).
|
|
173
|
+
if (unitType === "supervisor" || unitType.startsWith("hook/")) {
|
|
174
|
+
return resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds)?.thinking;
|
|
175
|
+
}
|
|
128
176
|
const loadOpts = availableModelIds !== undefined ? { availableModelIds } : undefined;
|
|
129
177
|
const prefs = loadEffectiveGSDPreferences(basePath, loadOpts)?.preferences;
|
|
130
178
|
if (!prefs)
|
|
@@ -383,12 +431,15 @@ export function resolveDynamicRoutingConfig() {
|
|
|
383
431
|
export function resolveAutoSupervisorConfig() {
|
|
384
432
|
const prefs = loadEffectiveGSDPreferences();
|
|
385
433
|
const configured = prefs?.preferences.auto_supervisor ?? {};
|
|
434
|
+
const modelConfig = normalizeModelFieldConfig(configured.model);
|
|
386
435
|
return {
|
|
387
436
|
soft_timeout_minutes: configured.soft_timeout_minutes ?? 20,
|
|
388
437
|
idle_timeout_minutes: configured.idle_timeout_minutes ?? 10,
|
|
389
438
|
hard_timeout_minutes: configured.hard_timeout_minutes ?? 30,
|
|
390
439
|
stalled_tool_timeout_minutes: configured.stalled_tool_timeout_minutes ?? 5,
|
|
391
|
-
...(
|
|
440
|
+
...(modelConfig
|
|
441
|
+
? { model: modelConfig.primary, modelFallbacks: modelConfig.fallbacks }
|
|
442
|
+
: {}),
|
|
392
443
|
};
|
|
393
444
|
}
|
|
394
445
|
// ─── Token Profile Resolution ─────────────────────────────────────────────
|
|
@@ -4,35 +4,29 @@
|
|
|
4
4
|
* Contains all logic for resolving skill references from preferences
|
|
5
5
|
* to absolute filesystem paths, plus skill discovery and staleness config.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
7
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { isAbsolute, join } from "node:path";
|
|
10
|
+
import { getSkillDirectories } from "@gsd/pi-coding-agent";
|
|
10
11
|
import { gsdHome } from "./gsd-home.js";
|
|
11
|
-
import { statSync } from "node:fs";
|
|
12
12
|
import { validatePreferences } from "./preferences-validation.js";
|
|
13
13
|
/**
|
|
14
14
|
* Known skill directories for **preference reference resolution** (bare skill names).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* 4. ~/.claude/skills/ and .claude/skills/ (Claude Code compatibility)
|
|
16
|
+
* Sourced from the shared taxonomy (`@gsd/pi-coding-agent` `getSkillDirectories`)
|
|
17
|
+
* so preference resolution, mid-session discovery, and the PackageManager
|
|
18
|
+
* catalog all agree on which directories exist. The `method` is derived from
|
|
19
|
+
* each entry's scope.
|
|
21
20
|
*
|
|
22
21
|
* Note: the ResourceLoader catalog uses PackageManager precedence instead —
|
|
23
22
|
* project `.gsd/skills` and `.agents/skills` can override bundled GSD skills
|
|
24
23
|
* on name collision. See docs/dev/what-is-pi/09-the-customization-stack.md.
|
|
25
24
|
*/
|
|
26
25
|
export function getSkillSearchDirs(cwd) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Claude Code official skill directories
|
|
32
|
-
{ dir: join(homedir(), ".claude", "skills"), method: "user-skill" },
|
|
33
|
-
{ dir: join(cwd, ".claude", "skills"), method: "project-skill" },
|
|
34
|
-
];
|
|
35
|
-
return dirs;
|
|
26
|
+
return getSkillDirectories({ cwd, gsdHome: gsdHome() }).map((entry) => ({
|
|
27
|
+
dir: entry.path,
|
|
28
|
+
method: entry.scope === "project" ? "project-skill" : "user-skill",
|
|
29
|
+
}));
|
|
36
30
|
}
|
|
37
31
|
/**
|
|
38
32
|
* Resolve a single skill reference to an absolute path.
|
|
@@ -25,6 +25,57 @@ const VALID_UOK_TURN_ACTIONS = new Set([
|
|
|
25
25
|
"snapshot",
|
|
26
26
|
"status-only",
|
|
27
27
|
]);
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize a single model field that accepts either a bare model ID or the
|
|
30
|
+
* extended `{ model, provider?, fallbacks? }` object form used by phase buckets
|
|
31
|
+
* (#1229). Returns the sanitized value, or undefined when unset/invalid (in
|
|
32
|
+
* which case an error describing `label` is pushed onto `errors`).
|
|
33
|
+
*/
|
|
34
|
+
function sanitizeModelField(raw, label, errors, warnings) {
|
|
35
|
+
if (raw === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof raw === "string") {
|
|
38
|
+
const trimmed = raw.trim();
|
|
39
|
+
if (!trimmed) {
|
|
40
|
+
errors.push(`${label} must not be an empty string`);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return trimmed;
|
|
44
|
+
}
|
|
45
|
+
if (raw && typeof raw === "object") {
|
|
46
|
+
const obj = raw;
|
|
47
|
+
const model = typeof obj.model === "string" ? obj.model.trim() : "";
|
|
48
|
+
if (!model) {
|
|
49
|
+
errors.push(`${label} object form requires a non-empty "model"`);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const sanitized = { model };
|
|
53
|
+
if (typeof obj.provider === "string" && obj.provider.trim()) {
|
|
54
|
+
sanitized.provider = obj.provider.trim();
|
|
55
|
+
}
|
|
56
|
+
if (obj.fallbacks !== undefined) {
|
|
57
|
+
const fallbacks = normalizeStringArray(obj.fallbacks);
|
|
58
|
+
if (fallbacks.length > 0)
|
|
59
|
+
sanitized.fallbacks = fallbacks;
|
|
60
|
+
}
|
|
61
|
+
// Preserve the object form's `thinking` sub-field so the dispatch site can
|
|
62
|
+
// pin the hook/supervisor/subagent reasoning level (#1269). An illegal level
|
|
63
|
+
// is warned-and-stripped, mirroring inline per-phase thinking, so a typo can't
|
|
64
|
+
// reach the resolver and be treated as explicit configuration.
|
|
65
|
+
if (obj.thinking !== undefined) {
|
|
66
|
+
if (VALID_THINKING_LEVELS.has(obj.thinking)) {
|
|
67
|
+
sanitized.thinking = obj.thinking;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
warnings?.push(`${label} thinking "${String(obj.thinking)}" is not a valid thinking level ` +
|
|
71
|
+
`(off, minimal, low, medium, high, xhigh) — ignored`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return sanitized;
|
|
75
|
+
}
|
|
76
|
+
errors.push(`${label} must be a string or an object with a "model" field`);
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
28
79
|
const VALID_POST_UNIT_HOOK_CRITICALITIES = new Set(["advisory", "blocking"]);
|
|
29
80
|
const VALID_POST_UNIT_HOOK_ON_BLOCK_ACTIONS = new Set([
|
|
30
81
|
"retry-unit",
|
|
@@ -452,7 +503,15 @@ export function validatePreferences(preferences) {
|
|
|
452
503
|
// ─── Auto Supervisor ────────────────────────────────────────────────
|
|
453
504
|
if (preferences.auto_supervisor !== undefined) {
|
|
454
505
|
if (preferences.auto_supervisor && typeof preferences.auto_supervisor === "object") {
|
|
455
|
-
|
|
506
|
+
// Validate `model` through the shared sanitizer so the
|
|
507
|
+
// `{ model, provider?, fallbacks? }` object form is rejected on the same
|
|
508
|
+
// terms as its post_unit_hooks / reactive_execution siblings (#1229).
|
|
509
|
+
// Other supervisor fields pass through unchanged.
|
|
510
|
+
const supervisor = { ...preferences.auto_supervisor };
|
|
511
|
+
if (supervisor.model !== undefined) {
|
|
512
|
+
supervisor.model = sanitizeModelField(supervisor.model, "auto_supervisor.model", errors, warnings);
|
|
513
|
+
}
|
|
514
|
+
validated.auto_supervisor = supervisor;
|
|
456
515
|
}
|
|
457
516
|
else {
|
|
458
517
|
errors.push("auto_supervisor must be an object");
|
|
@@ -548,8 +607,9 @@ export function validatePreferences(preferences) {
|
|
|
548
607
|
const mc = typeof hook.max_cycles === "number" ? hook.max_cycles : Number(hook.max_cycles);
|
|
549
608
|
validHook.max_cycles = Number.isFinite(mc) ? Math.max(1, Math.min(10, Math.round(mc))) : 1;
|
|
550
609
|
}
|
|
551
|
-
|
|
552
|
-
|
|
610
|
+
const hookModel = sanitizeModelField(hook.model, `post_unit_hooks "${name}" model`, errors, warnings);
|
|
611
|
+
if (hookModel !== undefined) {
|
|
612
|
+
validHook.model = hookModel;
|
|
553
613
|
}
|
|
554
614
|
if (typeof hook.artifact === "string" && hook.artifact.trim()) {
|
|
555
615
|
validHook.artifact = hook.artifact.trim();
|
|
@@ -801,6 +861,88 @@ export function validatePreferences(preferences) {
|
|
|
801
861
|
errors.push("context_management must be an object");
|
|
802
862
|
}
|
|
803
863
|
}
|
|
864
|
+
// ─── Tool-call loop guard (#1198) ────────────────────────────────────────
|
|
865
|
+
if (preferences.tool_call_loop_guard !== undefined) {
|
|
866
|
+
if (typeof preferences.tool_call_loop_guard === "object" && preferences.tool_call_loop_guard !== null) {
|
|
867
|
+
const g = preferences.tool_call_loop_guard;
|
|
868
|
+
const validGuard = {};
|
|
869
|
+
if (g.enabled !== undefined) {
|
|
870
|
+
if (typeof g.enabled === "boolean")
|
|
871
|
+
validGuard.enabled = g.enabled;
|
|
872
|
+
else
|
|
873
|
+
errors.push("tool_call_loop_guard.enabled must be a boolean");
|
|
874
|
+
}
|
|
875
|
+
if (g.identical_args !== undefined) {
|
|
876
|
+
if (typeof g.identical_args === "object" && g.identical_args !== null) {
|
|
877
|
+
const ia = g.identical_args;
|
|
878
|
+
const validIa = {};
|
|
879
|
+
if (ia.enabled !== undefined) {
|
|
880
|
+
if (typeof ia.enabled === "boolean")
|
|
881
|
+
validIa.enabled = ia.enabled;
|
|
882
|
+
else
|
|
883
|
+
errors.push("tool_call_loop_guard.identical_args.enabled must be a boolean");
|
|
884
|
+
}
|
|
885
|
+
if (ia.max_consecutive_calls !== undefined) {
|
|
886
|
+
const n = ia.max_consecutive_calls;
|
|
887
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1)
|
|
888
|
+
validIa.max_consecutive_calls = n;
|
|
889
|
+
else
|
|
890
|
+
errors.push("tool_call_loop_guard.identical_args.max_consecutive_calls must be an integer >= 1");
|
|
891
|
+
}
|
|
892
|
+
if (Object.keys(validIa).length > 0)
|
|
893
|
+
validGuard.identical_args = validIa;
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
errors.push("tool_call_loop_guard.identical_args must be an object");
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (g.repeated_tool !== undefined) {
|
|
900
|
+
if (typeof g.repeated_tool === "object" && g.repeated_tool !== null) {
|
|
901
|
+
const rt = g.repeated_tool;
|
|
902
|
+
const validRt = {};
|
|
903
|
+
if (rt.enabled !== undefined) {
|
|
904
|
+
if (typeof rt.enabled === "boolean")
|
|
905
|
+
validRt.enabled = rt.enabled;
|
|
906
|
+
else
|
|
907
|
+
errors.push("tool_call_loop_guard.repeated_tool.enabled must be a boolean");
|
|
908
|
+
}
|
|
909
|
+
if (rt.default_cap !== undefined) {
|
|
910
|
+
const n = rt.default_cap;
|
|
911
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1)
|
|
912
|
+
validRt.default_cap = n;
|
|
913
|
+
else
|
|
914
|
+
errors.push("tool_call_loop_guard.repeated_tool.default_cap must be an integer >= 1");
|
|
915
|
+
}
|
|
916
|
+
if (rt.repeatable_cap !== undefined) {
|
|
917
|
+
const n = rt.repeatable_cap;
|
|
918
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 1)
|
|
919
|
+
validRt.repeatable_cap = n;
|
|
920
|
+
else
|
|
921
|
+
errors.push("tool_call_loop_guard.repeated_tool.repeatable_cap must be an integer >= 1");
|
|
922
|
+
}
|
|
923
|
+
if (rt.exempt_tools !== undefined) {
|
|
924
|
+
if (Array.isArray(rt.exempt_tools) && rt.exempt_tools.every((t) => typeof t === "string")) {
|
|
925
|
+
validRt.exempt_tools = rt.exempt_tools;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
errors.push("tool_call_loop_guard.repeated_tool.exempt_tools must be an array of strings");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (Object.keys(validRt).length > 0)
|
|
932
|
+
validGuard.repeated_tool = validRt;
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
errors.push("tool_call_loop_guard.repeated_tool must be an object");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (Object.keys(validGuard).length > 0) {
|
|
939
|
+
validated.tool_call_loop_guard = validGuard;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
errors.push("tool_call_loop_guard must be an object");
|
|
944
|
+
}
|
|
945
|
+
}
|
|
804
946
|
// ─── Context Mode (gsd_exec sandbox) ────────────────────────────────────
|
|
805
947
|
if (preferences.context_mode !== undefined) {
|
|
806
948
|
if (typeof preferences.context_mode === "object" && preferences.context_mode !== null) {
|
|
@@ -968,13 +1110,9 @@ export function validatePreferences(preferences) {
|
|
|
968
1110
|
errors.push('reactive_execution.isolation_mode must be "same-tree"');
|
|
969
1111
|
}
|
|
970
1112
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
}
|
|
975
|
-
else {
|
|
976
|
-
errors.push("reactive_execution.subagent_model must be a non-empty string");
|
|
977
|
-
}
|
|
1113
|
+
const subagentModel = sanitizeModelField(re.subagent_model, "reactive_execution.subagent_model", errors, warnings);
|
|
1114
|
+
if (subagentModel !== undefined) {
|
|
1115
|
+
validRe.subagent_model = subagentModel;
|
|
978
1116
|
}
|
|
979
1117
|
const knownReKeys = new Set(["enabled", "max_parallel", "isolation_mode", "subagent_model"]);
|
|
980
1118
|
for (const key of Object.keys(re)) {
|