gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.73f2fd5
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 +30 -12
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +36 -36
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +34 -16
- package/dist/resources/extensions/gsd/doctor.js +8 -0
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +12 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/state.js +19 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- 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 +2 -2
- 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/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 +17 -17
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/phases.ts +45 -48
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
- package/src/resources/extensions/gsd/auto.ts +37 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/db-writer.ts +39 -17
- package/src/resources/extensions/gsd/doctor.ts +7 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +16 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/state.ts +19 -1
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminated-transient.test.ts — Regression test for #2309.
|
|
3
|
+
*
|
|
4
|
+
* classifyProviderError should treat 'terminated' errors (process killed,
|
|
5
|
+
* connection reset) as transient with auto-resume, not permanent.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { classifyProviderError } from "../provider-error-pause.ts";
|
|
11
|
+
|
|
12
|
+
test("#2309: 'terminated' errors should be classified as transient", () => {
|
|
13
|
+
const result = classifyProviderError("terminated");
|
|
14
|
+
assert.equal(result.isTransient, true, "'terminated' should be transient");
|
|
15
|
+
assert.equal(result.isRateLimit, false, "'terminated' is not a rate limit");
|
|
16
|
+
assert.ok(result.suggestedDelayMs > 0, "'terminated' should have a retry delay");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("#2309: 'connection reset' errors should be classified as transient", () => {
|
|
20
|
+
const result = classifyProviderError("connection reset by peer");
|
|
21
|
+
assert.equal(result.isTransient, true, "'connection reset' should be transient");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("#2309: 'other side closed' errors should be classified as transient", () => {
|
|
25
|
+
const result = classifyProviderError("other side closed the connection");
|
|
26
|
+
assert.equal(result.isTransient, true, "'other side closed' should be transient");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("#2309: 'fetch failed' errors should be classified as transient", () => {
|
|
30
|
+
const result = classifyProviderError("fetch failed: network error");
|
|
31
|
+
assert.equal(result.isTransient, true, "'fetch failed' should be transient");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("#2309: 'connection refused' errors should be classified as transient", () => {
|
|
35
|
+
const result = classifyProviderError("ECONNREFUSED: connection refused");
|
|
36
|
+
assert.equal(result.isTransient, true, "'connection refused' should be transient");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#2309: permanent errors are still permanent", () => {
|
|
40
|
+
const authResult = classifyProviderError("unauthorized: invalid API key");
|
|
41
|
+
assert.equal(authResult.isTransient, false, "auth errors should stay permanent");
|
|
42
|
+
assert.equal(authResult.suggestedDelayMs, 0, "permanent errors have no delay");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("#2309: rate limits are still transient", () => {
|
|
46
|
+
const rlResult = classifyProviderError("rate limit exceeded (429)");
|
|
47
|
+
assert.equal(rlResult.isTransient, true, "rate limits are still transient");
|
|
48
|
+
assert.equal(rlResult.isRateLimit, true, "rate limits are flagged as rate limits");
|
|
49
|
+
});
|
|
@@ -18,9 +18,9 @@ import {
|
|
|
18
18
|
formatDecisionsForPrompt,
|
|
19
19
|
formatRequirementsForPrompt,
|
|
20
20
|
} from '../context-store.ts';
|
|
21
|
-
import {
|
|
21
|
+
import { test } from 'node:test';
|
|
22
|
+
import assert from 'node:assert/strict';
|
|
22
23
|
|
|
23
|
-
const { assertEq, assertTrue, assertMatch, assertNoMatch, report } = createTestContext();
|
|
24
24
|
|
|
25
25
|
// ─── Fixture Generators ────────────────────────────────────────────────────
|
|
26
26
|
|
|
@@ -154,8 +154,8 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
|
|
|
154
154
|
openDatabase(':memory:');
|
|
155
155
|
const result = migrateFromMarkdown(base);
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
assert.ok(result.decisions === DECISIONS_COUNT, `imported ${result.decisions} decisions, expected ${DECISIONS_COUNT}`);
|
|
158
|
+
assert.ok(result.requirements === REQUIREMENTS_COUNT, `imported ${result.requirements} requirements, expected ${REQUIREMENTS_COUNT}`);
|
|
159
159
|
|
|
160
160
|
// ── DB-scoped content for plan-slice (M001 decisions + S01 requirements) ──
|
|
161
161
|
const scopedDecisions = queryDecisions({ milestoneId: 'M001' });
|
|
@@ -174,31 +174,31 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
|
|
|
174
174
|
const savingsPercent = ((fullTotal - dbTotal) / fullTotal) * 100;
|
|
175
175
|
console.log(` Plan-slice savings: ${savingsPercent.toFixed(1)}% (DB: ${dbTotal} chars, full: ${fullTotal} chars)`);
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
assert.ok(dbTotal > 0, 'DB-scoped content is non-empty');
|
|
178
|
+
assert.ok(dbDecisionsContent.length > 0, 'DB-scoped decisions content is non-empty');
|
|
179
|
+
assert.ok(dbRequirementsContent.length > 0, 'DB-scoped requirements content is non-empty');
|
|
180
|
+
assert.ok(savingsPercent >= 30, `plan-slice savings ≥30% (actual: ${savingsPercent.toFixed(1)}%)`);
|
|
181
|
+
assert.ok(dbTotal < fullTotal * 0.70, `DB total (${dbTotal}) < 70% of full total (${fullTotal})`);
|
|
182
182
|
|
|
183
183
|
// ── Verify correct scoping: decisions ──
|
|
184
184
|
// M001 decisions: those with when_context containing 'M001' — indices 1,4,7,10,13,16,19,22
|
|
185
185
|
// (24 decisions round-robin across M001/M002/M003 → 8 for M001)
|
|
186
|
-
|
|
186
|
+
assert.ok(scopedDecisions.length === 8, `M001 decisions: expected 8, got ${scopedDecisions.length}`);
|
|
187
187
|
for (const d of scopedDecisions) {
|
|
188
|
-
|
|
188
|
+
assert.ok(d.when_context.includes('M001'), `decision ${d.id} should have M001 in when_context, got "${d.when_context}"`);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// Verify NO decisions from other milestones leak in
|
|
192
192
|
for (const d of scopedDecisions) {
|
|
193
|
-
|
|
193
|
+
assert.doesNotMatch(d.when_context, /M002|M003/, `decision ${d.id} should not contain M002 or M003`);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// ── Verify correct scoping: requirements ──
|
|
197
197
|
// S01 requirements: those assigned to S01 as primary_owner
|
|
198
198
|
// S01 appears in positions 1,6,11,16,21 (5 assignments cycling, 21 reqs → indices 0,5,10,15,20)
|
|
199
|
-
|
|
199
|
+
assert.ok(scopedRequirements.length > 0, 'S01 requirements non-empty');
|
|
200
200
|
for (const r of scopedRequirements) {
|
|
201
|
-
|
|
201
|
+
assert.ok(
|
|
202
202
|
r.primary_owner.includes('S01') || r.supporting_slices.includes('S01'),
|
|
203
203
|
`requirement ${r.id} should be owned by or support S01`,
|
|
204
204
|
);
|
|
@@ -206,13 +206,13 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
|
|
|
206
206
|
|
|
207
207
|
// Verify specific expected IDs are present
|
|
208
208
|
const scopedDecisionIds = scopedDecisions.map(d => d.id);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
assert.ok(scopedDecisionIds.includes('D001'), 'M001 scoped decisions includes D001');
|
|
210
|
+
assert.ok(scopedDecisionIds.includes('D004'), 'M001 scoped decisions includes D004');
|
|
211
|
+
assert.ok(!scopedDecisionIds.includes('D002'), 'M001 scoped decisions excludes D002 (M002)');
|
|
212
|
+
assert.ok(!scopedDecisionIds.includes('D003'), 'M001 scoped decisions excludes D003 (M003)');
|
|
213
213
|
|
|
214
214
|
const scopedReqIds = scopedRequirements.map(r => r.id);
|
|
215
|
-
|
|
215
|
+
assert.ok(scopedReqIds.includes('R001'), 'S01 scoped requirements includes R001');
|
|
216
216
|
|
|
217
217
|
closeDatabase();
|
|
218
218
|
rmSync(base, { recursive: true, force: true });
|
|
@@ -246,9 +246,9 @@ console.log('\n=== token-savings: research-milestone prompt shows meaningful sav
|
|
|
246
246
|
const decisionsSavings = ((fullDecisionsContent.length - dbDecisionsContent.length) / fullDecisionsContent.length) * 100;
|
|
247
247
|
console.log(` Decisions savings (M001): ${decisionsSavings.toFixed(1)}% (DB: ${dbDecisionsContent.length}, full: ${fullDecisionsContent.length})`);
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
assert.ok(decisionsSavings > 0, `decisions savings > 0% (actual: ${decisionsSavings.toFixed(1)}%)`);
|
|
250
|
+
assert.ok(scopedDecisions.length === 8, `M001 decisions: 8 of 24 total`);
|
|
251
|
+
assert.ok(allRequirements.length === REQUIREMENTS_COUNT, `all requirements returned: ${allRequirements.length}`);
|
|
252
252
|
|
|
253
253
|
// Requirements: DB-formatted vs raw markdown — formatted output may differ in size
|
|
254
254
|
// but decisions savings alone should make the composite meaningful
|
|
@@ -259,8 +259,8 @@ console.log('\n=== token-savings: research-milestone prompt shows meaningful sav
|
|
|
259
259
|
|
|
260
260
|
// With 8/24 decisions = 66% reduction in decisions, even if requirements are equal,
|
|
261
261
|
// the composite should show meaningful savings
|
|
262
|
-
|
|
263
|
-
|
|
262
|
+
assert.ok(compositeSavings > 10, `research-milestone shows >10% composite savings (actual: ${compositeSavings.toFixed(1)}%)`);
|
|
263
|
+
assert.ok(decisionsSavings >= 30, `decisions-only savings ≥30% for M001 scope (actual: ${decisionsSavings.toFixed(1)}%)`);
|
|
264
264
|
|
|
265
265
|
closeDatabase();
|
|
266
266
|
rmSync(base, { recursive: true, force: true });
|
|
@@ -283,17 +283,17 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
|
|
|
283
283
|
|
|
284
284
|
// ── M002-scoped decisions should not contain M001/M003 items ──
|
|
285
285
|
const m002Decisions = queryDecisions({ milestoneId: 'M002' });
|
|
286
|
-
|
|
286
|
+
assert.ok(m002Decisions.length === 8, `M002 decisions: expected 8, got ${m002Decisions.length}`);
|
|
287
287
|
for (const d of m002Decisions) {
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
assert.ok(d.when_context.includes('M002'), `M002 decision ${d.id} has M002 in when_context`);
|
|
289
|
+
assert.doesNotMatch(d.when_context, /M001|M003/, `M002 decision ${d.id} should not contain M001/M003`);
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// ── S04-scoped requirements should only include S04-related items ──
|
|
293
293
|
const s04Requirements = queryRequirements({ sliceId: 'S04' });
|
|
294
|
-
|
|
294
|
+
assert.ok(s04Requirements.length > 0, 'S04 requirements non-empty');
|
|
295
295
|
for (const r of s04Requirements) {
|
|
296
|
-
|
|
296
|
+
assert.ok(
|
|
297
297
|
r.primary_owner.includes('S04') || r.supporting_slices.includes('S04'),
|
|
298
298
|
`S04 requirement ${r.id} should be owned by or support S04`,
|
|
299
299
|
);
|
|
@@ -301,13 +301,13 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
|
|
|
301
301
|
|
|
302
302
|
// ── Verify formatted output is well-formed and non-empty ──
|
|
303
303
|
const formattedDecisions = formatDecisionsForPrompt(m002Decisions);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
assert.ok(formattedDecisions.length > 0, 'formatted M002 decisions is non-empty');
|
|
305
|
+
assert.match(formattedDecisions, /\| D/, 'formatted decisions contains decision rows');
|
|
306
|
+
assert.match(formattedDecisions, /\| # \|/, 'formatted decisions has table header');
|
|
307
307
|
|
|
308
308
|
const formattedReqs = formatRequirementsForPrompt(s04Requirements);
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
assert.ok(formattedReqs.length > 0, 'formatted S04 requirements is non-empty');
|
|
310
|
+
assert.match(formattedReqs, /### R\d+/, 'formatted requirements has requirement headings');
|
|
311
311
|
|
|
312
312
|
// ── Verify all milestones have decisions and counts add up ──
|
|
313
313
|
const m001Count = queryDecisions({ milestoneId: 'M001' }).length;
|
|
@@ -315,11 +315,11 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
|
|
|
315
315
|
const m003Count = queryDecisions({ milestoneId: 'M003' }).length;
|
|
316
316
|
const allCount = queryDecisions().length;
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
318
|
+
assert.ok(m001Count === 8, `M001: 8 decisions (got ${m001Count})`);
|
|
319
|
+
assert.ok(m002Count === 8, `M002: 8 decisions (got ${m002Count})`);
|
|
320
|
+
assert.ok(m003Count === 8, `M003: 8 decisions (got ${m003Count})`);
|
|
321
|
+
assert.ok(allCount === DECISIONS_COUNT, `all: ${DECISIONS_COUNT} decisions (got ${allCount})`);
|
|
322
|
+
assert.ok(m001Count + m002Count + m003Count === allCount, 'milestone decision counts sum to total');
|
|
323
323
|
|
|
324
324
|
// ── Verify all slices have requirements ──
|
|
325
325
|
const s01Reqs = queryRequirements({ sliceId: 'S01' });
|
|
@@ -328,11 +328,11 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
|
|
|
328
328
|
const s04Reqs = queryRequirements({ sliceId: 'S04' });
|
|
329
329
|
const s05Reqs = queryRequirements({ sliceId: 'S05' });
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
assert.ok(s01Reqs.length > 0, 'S01 has requirements');
|
|
332
|
+
assert.ok(s02Reqs.length > 0, 'S02 has requirements');
|
|
333
|
+
assert.ok(s03Reqs.length > 0, 'S03 has requirements');
|
|
334
|
+
assert.ok(s04Reqs.length > 0, 'S04 has requirements');
|
|
335
|
+
assert.ok(s05Reqs.length > 0, 'S05 has requirements');
|
|
336
336
|
|
|
337
337
|
closeDatabase();
|
|
338
338
|
rmSync(base, { recursive: true, force: true });
|
|
@@ -345,22 +345,20 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
|
|
|
345
345
|
console.log('\n=== token-savings: fixture data realism ===');
|
|
346
346
|
{
|
|
347
347
|
// Verify fixture generators produce sufficient volume
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
348
|
+
assert.ok(DECISIONS_COUNT >= 20, `decisions count ≥ 20 (actual: ${DECISIONS_COUNT})`);
|
|
349
|
+
assert.ok(REQUIREMENTS_COUNT >= 20, `requirements count ≥ 20 (actual: ${REQUIREMENTS_COUNT})`);
|
|
350
|
+
assert.ok(MILESTONES.length >= 3, `milestones ≥ 3 (actual: ${MILESTONES.length})`);
|
|
351
|
+
assert.ok(SLICE_ASSIGNMENTS.length >= 5, `slice assignments ≥ 5 (actual: ${SLICE_ASSIGNMENTS.length})`);
|
|
352
352
|
|
|
353
353
|
// Verify markdown content is substantial
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
assert.ok(decisionsMarkdown.length > 1000, `decisions markdown > 1000 chars (actual: ${decisionsMarkdown.length})`);
|
|
355
|
+
assert.ok(requirementsMarkdown.length > 1000, `requirements markdown > 1000 chars (actual: ${requirementsMarkdown.length})`);
|
|
356
356
|
|
|
357
357
|
// Verify content structure
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
358
|
+
assert.match(decisionsMarkdown, /\| D001 \|/, 'decisions markdown has D001');
|
|
359
|
+
assert.match(decisionsMarkdown, /\| D024 \|/, 'decisions markdown has D024');
|
|
360
|
+
assert.match(requirementsMarkdown, /### R001/, 'requirements markdown has R001');
|
|
361
|
+
assert.match(requirementsMarkdown, /### R021/, 'requirements markdown has R021');
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// ─── Report ────────────────────────────────────────────────────────────────
|
|
365
|
-
|
|
366
|
-
report();
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// Verifies that identical consecutive tool calls are detected and blocked
|
|
4
4
|
// after exceeding the threshold, and that the guard resets properly.
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { test } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
7
8
|
import {
|
|
8
9
|
checkToolCallLoop,
|
|
9
10
|
resetToolCallLoopGuard,
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
getToolCallLoopCount,
|
|
12
13
|
} from '../bootstrap/tool-call-loop-guard.ts';
|
|
13
14
|
|
|
14
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
15
15
|
|
|
16
16
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
17
|
// Allows first N calls, blocks after threshold
|
|
@@ -25,15 +25,15 @@ console.log('\n── Loop guard: blocks after threshold ──');
|
|
|
25
25
|
// First 4 identical calls should be allowed (threshold is 4)
|
|
26
26
|
for (let i = 1; i <= 4; i++) {
|
|
27
27
|
const result = checkToolCallLoop('web_search', { query: 'same query' });
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
assert.ok(result.block === false, `Call ${i} should be allowed`);
|
|
29
|
+
assert.deepStrictEqual(result.count, i, `Count should be ${i} after call ${i}`);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// 5th identical call should be blocked
|
|
33
33
|
const blocked = checkToolCallLoop('web_search', { query: 'same query' });
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
assert.ok(blocked.block === true, '5th identical call should be blocked');
|
|
35
|
+
assert.ok(blocked.reason!.includes('web_search'), 'Reason should mention tool name');
|
|
36
|
+
assert.ok(blocked.reason!.includes('5'), 'Reason should mention count');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -48,17 +48,17 @@ console.log('\n── Loop guard: different calls reset streak ──');
|
|
|
48
48
|
checkToolCallLoop('web_search', { query: 'query A' });
|
|
49
49
|
checkToolCallLoop('web_search', { query: 'query A' });
|
|
50
50
|
checkToolCallLoop('web_search', { query: 'query A' });
|
|
51
|
-
|
|
51
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 3, 'Count should be 3 after 3 identical calls');
|
|
52
52
|
|
|
53
53
|
// A different call resets the streak
|
|
54
54
|
const different = checkToolCallLoop('bash', { command: 'ls' });
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
assert.ok(different.block === false, 'Different tool call should be allowed');
|
|
56
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Count should reset to 1 after different call');
|
|
57
57
|
|
|
58
58
|
// Same tool but different args also resets
|
|
59
59
|
checkToolCallLoop('web_search', { query: 'query A' });
|
|
60
60
|
checkToolCallLoop('web_search', { query: 'query B' }); // different args
|
|
61
|
-
|
|
61
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Different args should reset count');
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -72,15 +72,15 @@ console.log('\n── Loop guard: reset clears state ──');
|
|
|
72
72
|
checkToolCallLoop('web_search', { query: 'q' });
|
|
73
73
|
checkToolCallLoop('web_search', { query: 'q' });
|
|
74
74
|
checkToolCallLoop('web_search', { query: 'q' });
|
|
75
|
-
|
|
75
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 3, 'Count should be 3 before reset');
|
|
76
76
|
|
|
77
77
|
resetToolCallLoopGuard();
|
|
78
|
-
|
|
78
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 0, 'Count should be 0 after reset');
|
|
79
79
|
|
|
80
80
|
// After reset, the same call starts fresh
|
|
81
81
|
const result = checkToolCallLoop('web_search', { query: 'q' });
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
assert.ok(result.block === false, 'Call after reset should be allowed');
|
|
83
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Count should be 1 after first call post-reset');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -94,13 +94,13 @@ console.log('\n── Loop guard: disable allows everything ──');
|
|
|
94
94
|
|
|
95
95
|
for (let i = 0; i < 10; i++) {
|
|
96
96
|
const result = checkToolCallLoop('web_search', { query: 'same' });
|
|
97
|
-
|
|
97
|
+
assert.ok(result.block === false, `Call ${i + 1} should be allowed when disabled`);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// Re-enable via reset
|
|
101
101
|
resetToolCallLoopGuard();
|
|
102
102
|
checkToolCallLoop('web_search', { query: 'q' });
|
|
103
|
-
|
|
103
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Guard should be active again after reset');
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -114,8 +114,8 @@ console.log('\n── Loop guard: arg order is normalized ──');
|
|
|
114
114
|
|
|
115
115
|
checkToolCallLoop('web_search', { query: 'test', limit: 5 });
|
|
116
116
|
const result = checkToolCallLoop('web_search', { limit: 5, query: 'test' }); // same args, different order
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
assert.ok(result.block === false, 'Same args in different order should count as consecutive');
|
|
118
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 2, 'Should detect as same call regardless of key order');
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -132,8 +132,8 @@ console.log('\n── Loop guard: nested args are not stripped ──');
|
|
|
132
132
|
const result = checkToolCallLoop('ask_user_questions', {
|
|
133
133
|
questions: [{ id: `q${i}`, question: `Question ${i}?` }],
|
|
134
134
|
});
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
assert.ok(result.block === false, `Nested call ${i} with unique content should be allowed`);
|
|
136
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 1, `Each unique nested call should reset count to 1`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// Truly identical nested calls should still be detected
|
|
@@ -146,7 +146,7 @@ console.log('\n── Loop guard: nested args are not stripped ──');
|
|
|
146
146
|
const blocked = checkToolCallLoop('ask_user_questions', {
|
|
147
147
|
questions: [{ id: 'same', question: 'Same?' }],
|
|
148
148
|
});
|
|
149
|
-
|
|
149
|
+
assert.ok(blocked.block === true, 'Identical nested calls should still be blocked');
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -160,9 +160,7 @@ console.log('\n── Loop guard: nested key order is normalized ──');
|
|
|
160
160
|
|
|
161
161
|
checkToolCallLoop('tool', { outer: { b: 2, a: 1 } });
|
|
162
162
|
const result = checkToolCallLoop('tool', { outer: { a: 1, b: 2 } });
|
|
163
|
-
|
|
163
|
+
assert.deepStrictEqual(getToolCallLoopCount(), 2, 'Same nested args in different key order should match');
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
167
|
-
|
|
168
|
-
report();
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// AND under a backward-compatible alias name.
|
|
5
5
|
// The alias must share the exact same execute function reference as the canonical tool.
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { test } from 'node:test';
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
8
9
|
import { registerDbTools } from '../bootstrap/db-tools.ts';
|
|
9
10
|
|
|
10
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
11
11
|
|
|
12
12
|
// ─── Mock PI ──────────────────────────────────────────────────────────────────
|
|
13
13
|
|
|
@@ -34,6 +34,7 @@ const RENAME_MAP: Array<{ canonical: string; alias: string }> = [
|
|
|
34
34
|
{ canonical: "gsd_replan_slice", alias: "gsd_slice_replan" },
|
|
35
35
|
{ canonical: "gsd_reassess_roadmap", alias: "gsd_roadmap_reassess" },
|
|
36
36
|
{ canonical: "gsd_complete_milestone", alias: "gsd_milestone_complete" },
|
|
37
|
+
{ canonical: "gsd_validate_milestone", alias: "gsd_milestone_validate" },
|
|
37
38
|
];
|
|
38
39
|
|
|
39
40
|
// ─── Registration count ──────────────────────────────────────────────────────
|
|
@@ -43,7 +44,7 @@ console.log('\n── Tool naming: registration count ──');
|
|
|
43
44
|
const pi = makeMockPi();
|
|
44
45
|
registerDbTools(pi);
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
assert.deepStrictEqual(pi.tools.length, 26, 'Should register exactly 26 tools (13 canonical + 13 aliases)');
|
|
47
48
|
|
|
48
49
|
// ─── Both names exist for each pair ──────────────────────────────────────────
|
|
49
50
|
|
|
@@ -53,8 +54,8 @@ for (const { canonical, alias } of RENAME_MAP) {
|
|
|
53
54
|
const canonicalTool = pi.tools.find((t: any) => t.name === canonical);
|
|
54
55
|
const aliasTool = pi.tools.find((t: any) => t.name === alias);
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
assert.ok(canonicalTool !== undefined, `Canonical tool "${canonical}" should be registered`);
|
|
58
|
+
assert.ok(aliasTool !== undefined, `Alias tool "${alias}" should be registered`);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
// ─── Execute function identity ───────────────────────────────────────────────
|
|
@@ -66,7 +67,7 @@ for (const { canonical, alias } of RENAME_MAP) {
|
|
|
66
67
|
const aliasTool = pi.tools.find((t: any) => t.name === alias);
|
|
67
68
|
|
|
68
69
|
if (canonicalTool && aliasTool) {
|
|
69
|
-
|
|
70
|
+
assert.ok(
|
|
70
71
|
canonicalTool.execute === aliasTool.execute,
|
|
71
72
|
`"${canonical}" and "${alias}" should share the same execute function reference`,
|
|
72
73
|
);
|
|
@@ -81,7 +82,7 @@ for (const { canonical, alias } of RENAME_MAP) {
|
|
|
81
82
|
const aliasTool = pi.tools.find((t: any) => t.name === alias);
|
|
82
83
|
|
|
83
84
|
if (aliasTool) {
|
|
84
|
-
|
|
85
|
+
assert.ok(
|
|
85
86
|
aliasTool.description.includes(`alias for ${canonical}`),
|
|
86
87
|
`Alias "${alias}" description should include "alias for ${canonical}"`,
|
|
87
88
|
);
|
|
@@ -97,7 +98,7 @@ for (const { canonical } of RENAME_MAP) {
|
|
|
97
98
|
|
|
98
99
|
if (canonicalTool) {
|
|
99
100
|
const guidelinesText = canonicalTool.promptGuidelines.join(' ');
|
|
100
|
-
|
|
101
|
+
assert.ok(
|
|
101
102
|
guidelinesText.includes(canonical),
|
|
102
103
|
`Canonical tool "${canonical}" promptGuidelines should reference its own name`,
|
|
103
104
|
);
|
|
@@ -113,7 +114,7 @@ for (const { canonical, alias } of RENAME_MAP) {
|
|
|
113
114
|
|
|
114
115
|
if (aliasTool) {
|
|
115
116
|
const guidelinesText = aliasTool.promptGuidelines.join(' ');
|
|
116
|
-
|
|
117
|
+
assert.ok(
|
|
117
118
|
guidelinesText.includes(`Alias for ${canonical}`),
|
|
118
119
|
`Alias "${alias}" promptGuidelines should say "Alias for ${canonical}"`,
|
|
119
120
|
);
|
|
@@ -121,5 +122,3 @@ for (const { canonical, alias } of RENAME_MAP) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
-
|
|
125
|
-
report();
|