gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51
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 +7 -7
- package/dist/claude-cli-check.js +64 -37
- package/dist/cli-policy.d.ts +13 -0
- package/dist/cli-policy.js +17 -0
- package/dist/cli.js +95 -55
- package/dist/headless-query.d.ts +22 -0
- package/dist/headless-query.js +24 -4
- package/dist/headless.d.ts +10 -0
- package/dist/headless.js +16 -1
- package/dist/loader.js +7 -10
- package/dist/onboarding.d.ts +10 -0
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +5 -2
- package/dist/resource-loader.d.ts +5 -2
- package/dist/resource-loader.js +28 -5
- package/dist/resources/.managed-resources-content-hash +1 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
- package/dist/resources/extensions/gsd/auto/loop.js +23 -0
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
- package/dist/resources/extensions/gsd/auto.js +14 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
- package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +12 -5
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +23 -23
- package/dist/resources/extensions/gsd/memory-store.js +66 -31
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
- package/dist/resources/extensions/gsd/model-router.js +114 -9
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
- package/dist/resources/extensions/gsd/preferences-models.js +91 -15
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
- package/dist/resources/extensions/gsd/preferences.js +5 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
- package/dist/resources/extensions/gsd/state.js +42 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
- package/dist/resources/extensions/gsd/worktree-command.js +26 -46
- package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
- package/dist/resources/extensions/mcp-client/index.js +6 -3
- package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
- package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/rtk-shared.d.ts +3 -0
- package/dist/rtk-shared.js +17 -0
- package/dist/rtk.d.ts +2 -5
- package/dist/rtk.js +3 -20
- package/dist/runtime-checks.d.ts +27 -0
- package/dist/runtime-checks.js +38 -0
- 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 +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +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 +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
- package/dist/web/standalone/package.json +2 -1
- package/dist/worktree-status-banner.d.ts +1 -0
- package/dist/worktree-status-banner.js +132 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
- package/packages/mcp-server/dist/alias-telemetry.js +30 -0
- package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +74 -46
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
- package/packages/mcp-server/src/alias-telemetry.ts +30 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
- package/packages/mcp-server/src/workflow-tools.ts +93 -58
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +13 -0
- 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/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
- package/packages/pi-ai/src/types.ts +13 -0
- package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +4 -0
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -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 +18 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
- package/packages/pi-coding-agent/src/core/messages.ts +4 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
- package/src/resources/extensions/gsd/auto/loop.ts +24 -2
- package/src/resources/extensions/gsd/auto/phases.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto/types.ts +1 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
- package/src/resources/extensions/gsd/auto.ts +14 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +13 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -2
- package/src/resources/extensions/gsd/guided-flow.ts +25 -25
- package/src/resources/extensions/gsd/memory-store.ts +81 -28
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
- package/src/resources/extensions/gsd/model-router.ts +172 -9
- package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
- package/src/resources/extensions/gsd/preferences-models.ts +101 -15
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
- package/src/resources/extensions/gsd/state.ts +42 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
- package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
- package/src/resources/extensions/gsd/worktree-command.ts +31 -44
- package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
- package/src/resources/extensions/mcp-client/index.ts +6 -3
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
- package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
|
@@ -236,6 +236,20 @@ test("valid values pass through correctly", () => {
|
|
|
236
236
|
assert.equal(p3.auto_supervisor?.model, "claude-opus-4-6");
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
test("min_request_interval_ms floors decimals and rejects timer overflow values", () => {
|
|
240
|
+
const valid = validatePreferences({ min_request_interval_ms: 1000.9 });
|
|
241
|
+
assert.equal(valid.errors.length, 0);
|
|
242
|
+
assert.equal(valid.preferences.min_request_interval_ms, 1000);
|
|
243
|
+
|
|
244
|
+
const max = validatePreferences({ min_request_interval_ms: 2_147_483_647 });
|
|
245
|
+
assert.equal(max.errors.length, 0);
|
|
246
|
+
assert.equal(max.preferences.min_request_interval_ms, 2_147_483_647);
|
|
247
|
+
|
|
248
|
+
const tooHigh = validatePreferences({ min_request_interval_ms: 2_147_483_648 });
|
|
249
|
+
assert.ok(tooHigh.errors.some(e => e.includes("min_request_interval_ms must be a non-negative number <= 2147483647")));
|
|
250
|
+
assert.equal(tooHigh.preferences.min_request_interval_ms, undefined);
|
|
251
|
+
});
|
|
252
|
+
|
|
239
253
|
test("mixed valid/invalid/unknown keys handled correctly", () => {
|
|
240
254
|
const { preferences, errors, warnings } = validatePreferences({
|
|
241
255
|
uat_dispatch: true, totally_made_up: "value", budget_ceiling: "garbage",
|
|
@@ -246,6 +260,71 @@ test("mixed valid/invalid/unknown keys handled correctly", () => {
|
|
|
246
260
|
assert.equal(preferences.budget_ceiling, undefined);
|
|
247
261
|
});
|
|
248
262
|
|
|
263
|
+
test("disabled_model_providers validates and normalizes string arrays", () => {
|
|
264
|
+
const { preferences, errors } = validatePreferences({
|
|
265
|
+
disabled_model_providers: ["google-gemini-cli", " google-gemini-cli ", "openai-codex", " "],
|
|
266
|
+
});
|
|
267
|
+
assert.equal(errors.length, 0);
|
|
268
|
+
assert.deepEqual(preferences.disabled_model_providers, ["google-gemini-cli", "openai-codex"]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("disabled_model_providers rejects non-array values", () => {
|
|
272
|
+
const { errors } = validatePreferences({ disabled_model_providers: "google-gemini-cli" as any });
|
|
273
|
+
assert.ok(errors.some((e) => e.includes("disabled_model_providers must be an array of strings")));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("loadEffectiveGSDPreferences preserves disabled_model_providers across merge layers", () => {
|
|
277
|
+
const originalCwd = process.cwd();
|
|
278
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
279
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-disabled-provider-project-"));
|
|
280
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-disabled-provider-home-"));
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
284
|
+
|
|
285
|
+
writeFileSync(
|
|
286
|
+
join(tempGsdHome, "PREFERENCES.md"),
|
|
287
|
+
[
|
|
288
|
+
"---",
|
|
289
|
+
"version: 1",
|
|
290
|
+
"disabled_model_providers:",
|
|
291
|
+
" - google-gemini-cli",
|
|
292
|
+
"---",
|
|
293
|
+
].join("\n"),
|
|
294
|
+
"utf-8",
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
writeFileSync(
|
|
298
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
299
|
+
[
|
|
300
|
+
"---",
|
|
301
|
+
"version: 1",
|
|
302
|
+
"disabled_model_providers:",
|
|
303
|
+
" - openai-codex",
|
|
304
|
+
" - google-gemini-cli",
|
|
305
|
+
"---",
|
|
306
|
+
].join("\n"),
|
|
307
|
+
"utf-8",
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
311
|
+
process.chdir(tempProject);
|
|
312
|
+
|
|
313
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
314
|
+
assert.notEqual(loaded, null);
|
|
315
|
+
assert.deepEqual(
|
|
316
|
+
loaded!.preferences.disabled_model_providers,
|
|
317
|
+
["google-gemini-cli", "openai-codex"],
|
|
318
|
+
);
|
|
319
|
+
} finally {
|
|
320
|
+
process.chdir(originalCwd);
|
|
321
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
322
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
323
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
324
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
249
328
|
// ── Wizard fields ────────────────────────────────────────────────────────────
|
|
250
329
|
|
|
251
330
|
test("budget fields validate correctly", () => {
|
|
@@ -683,6 +762,54 @@ test("loadEffectiveGSDPreferences exposes slice_parallel prefs to runtime caller
|
|
|
683
762
|
}
|
|
684
763
|
});
|
|
685
764
|
|
|
765
|
+
test("loadEffectiveGSDPreferences merges min_request_interval_ms with project overriding global (#2996)", () => {
|
|
766
|
+
const originalCwd = process.cwd();
|
|
767
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
768
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-rate-limit-project-"));
|
|
769
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-rate-limit-home-"));
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
773
|
+
|
|
774
|
+
writeFileSync(
|
|
775
|
+
join(tempGsdHome, "PREFERENCES.md"),
|
|
776
|
+
[
|
|
777
|
+
"---",
|
|
778
|
+
"version: 1",
|
|
779
|
+
"min_request_interval_ms: 250",
|
|
780
|
+
"budget_ceiling: 45",
|
|
781
|
+
"---",
|
|
782
|
+
].join("\n"),
|
|
783
|
+
"utf-8",
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
writeFileSync(
|
|
787
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
788
|
+
[
|
|
789
|
+
"---",
|
|
790
|
+
"version: 1",
|
|
791
|
+
"min_request_interval_ms: 100",
|
|
792
|
+
"---",
|
|
793
|
+
].join("\n"),
|
|
794
|
+
"utf-8",
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
798
|
+
process.chdir(tempProject);
|
|
799
|
+
|
|
800
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
801
|
+
assert.notEqual(loaded, null);
|
|
802
|
+
assert.equal(loaded!.preferences.min_request_interval_ms, 100);
|
|
803
|
+
assert.equal(loaded!.preferences.budget_ceiling, 45);
|
|
804
|
+
} finally {
|
|
805
|
+
process.chdir(originalCwd);
|
|
806
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
807
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
808
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
809
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
686
813
|
test("preferences paths use canonical uppercase filenames", () => {
|
|
687
814
|
const originalCwd = process.cwd();
|
|
688
815
|
const originalGsdHome = process.env.GSD_HOME;
|
|
@@ -93,4 +93,20 @@ describe('register-hooks session_before_compact (#3696)', () => {
|
|
|
93
93
|
'session_before_compact should not check isAutoPaused',
|
|
94
94
|
);
|
|
95
95
|
});
|
|
96
|
+
|
|
97
|
+
test('session_before_compact does not gate checkpointing to executing phase (#4258)', () => {
|
|
98
|
+
const compactIdx = registerHooksSrc.indexOf('session_before_compact');
|
|
99
|
+
assert.ok(compactIdx > -1, 'session_before_compact hook should exist');
|
|
100
|
+
|
|
101
|
+
const preCheckpointSection = registerHooksSrc.slice(
|
|
102
|
+
compactIdx,
|
|
103
|
+
registerHooksSrc.indexOf('const sliceDir', compactIdx),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const normalized = preCheckpointSection.replace(/\/\/.*$/gm, '');
|
|
107
|
+
assert.ok(
|
|
108
|
+
!/if\s*\(\s*state\.phase\s*!==\s*['"]executing['"]\s*\)\s*\{?\s*return\b/.test(normalized),
|
|
109
|
+
'session_before_compact should not early-return on non-executing phases',
|
|
110
|
+
);
|
|
111
|
+
});
|
|
96
112
|
});
|
|
@@ -103,6 +103,13 @@ test("classifyError detects Codex server_error from extracted message", () => {
|
|
|
103
103
|
assert.ok("retryAfterMs" in result && result.retryAfterMs === 30_000);
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
test("classifyError detects stream INTERNAL_ERROR received from peer as transient server", () => {
|
|
107
|
+
const result = classifyError("stream error: stream ID 75; INTERNAL_ERROR; received from peer");
|
|
108
|
+
assert.ok(isTransient(result));
|
|
109
|
+
assert.equal(result.kind, "server");
|
|
110
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 30_000);
|
|
111
|
+
});
|
|
112
|
+
|
|
106
113
|
test("classifyError detects overloaded error", () => {
|
|
107
114
|
const result = classifyError("overloaded_error: Overloaded");
|
|
108
115
|
assert.ok(isTransient(result));
|
|
@@ -23,19 +23,19 @@ describe("quick task turn_end cleanup (#2668)", () => {
|
|
|
23
23
|
"utf-8",
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
it("register-hooks.ts
|
|
26
|
+
it("register-hooks.ts loads cleanupQuickBranch from quick.ts", () => {
|
|
27
27
|
assert.ok(
|
|
28
28
|
hooksSource.includes("cleanupQuickBranch"),
|
|
29
29
|
"register-hooks.ts must reference cleanupQuickBranch",
|
|
30
30
|
);
|
|
31
31
|
|
|
32
|
-
// Verify it
|
|
33
|
-
const importMatch =
|
|
34
|
-
/import\s*\{[^}]*cleanupQuickBranch[^}]*\}\s*from\s*["'][^"']*quick
|
|
35
|
-
|
|
32
|
+
// Verify it is loaded from quick.ts (static or lazy), not just mentioned in a comment.
|
|
33
|
+
const importMatch =
|
|
34
|
+
hooksSource.match(/import\s*\{[^}]*cleanupQuickBranch[^}]*\}\s*from\s*["'][^"']*quick/) ||
|
|
35
|
+
hooksSource.match(/const\s+\{\s*cleanupQuickBranch\s*\}\s*=\s*await\s+import\(["'][^"']*quick\.js["']\)/);
|
|
36
36
|
assert.ok(
|
|
37
37
|
importMatch,
|
|
38
|
-
"cleanupQuickBranch must be
|
|
38
|
+
"cleanupQuickBranch must be loaded from quick module",
|
|
39
39
|
);
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
8
|
+
import { parseContinue } from "../files.ts";
|
|
9
|
+
import { closeDatabase } from "../gsd-db.ts";
|
|
10
|
+
import { deriveState, invalidateStateCache } from "../state.ts";
|
|
11
|
+
|
|
12
|
+
function createPlanningFixtureBase(): string {
|
|
13
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-compact-checkpoint-"));
|
|
14
|
+
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
15
|
+
const sliceDir = join(milestoneDir, "slices", "S01");
|
|
16
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
17
|
+
|
|
18
|
+
writeFileSync(
|
|
19
|
+
join(milestoneDir, "M001-ROADMAP.md"),
|
|
20
|
+
`# M001: Test Milestone
|
|
21
|
+
|
|
22
|
+
**Vision:** Validate compaction checkpointing.
|
|
23
|
+
|
|
24
|
+
## Slices
|
|
25
|
+
|
|
26
|
+
- [ ] **S01: Test Slice** \`risk:low\` \`depends:[]\`
|
|
27
|
+
> After this: Slice is done.
|
|
28
|
+
`,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join(sliceDir, "S01-PLAN.md"),
|
|
33
|
+
`# S01: Test Slice
|
|
34
|
+
|
|
35
|
+
**Goal:** Validate planning checkpoint.
|
|
36
|
+
**Demo:** Checkpoint exists after compaction.
|
|
37
|
+
|
|
38
|
+
## Tasks
|
|
39
|
+
`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return base;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test("register-hooks writes CONTINUE checkpoint during planning phase without active task (#4258)", async (t) => {
|
|
46
|
+
const base = createPlanningFixtureBase();
|
|
47
|
+
const originalCwd = process.cwd();
|
|
48
|
+
process.chdir(base);
|
|
49
|
+
invalidateStateCache();
|
|
50
|
+
closeDatabase();
|
|
51
|
+
|
|
52
|
+
t.after(() => {
|
|
53
|
+
invalidateStateCache();
|
|
54
|
+
closeDatabase();
|
|
55
|
+
process.chdir(originalCwd);
|
|
56
|
+
rmSync(base, { recursive: true, force: true });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const state = await deriveState(base);
|
|
60
|
+
assert.equal(state.phase, "planning", "fixture should derive planning phase");
|
|
61
|
+
assert.equal(state.activeMilestone?.id, "M001", "fixture should have active milestone");
|
|
62
|
+
assert.equal(state.activeSlice?.id, "S01", "fixture should have active slice");
|
|
63
|
+
assert.equal(state.activeTask, null, "fixture should have no active task");
|
|
64
|
+
|
|
65
|
+
const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
|
|
66
|
+
const pi = {
|
|
67
|
+
on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
|
|
68
|
+
const existing = handlers.get(event) ?? [];
|
|
69
|
+
existing.push(handler);
|
|
70
|
+
handlers.set(event, existing);
|
|
71
|
+
},
|
|
72
|
+
} as any;
|
|
73
|
+
|
|
74
|
+
registerHooks(pi, []);
|
|
75
|
+
|
|
76
|
+
const compactHandlers = handlers.get("session_before_compact");
|
|
77
|
+
assert.ok(compactHandlers && compactHandlers.length > 0, "session_before_compact handler should be registered");
|
|
78
|
+
|
|
79
|
+
for (const handler of compactHandlers ?? []) {
|
|
80
|
+
await handler({});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const continuePath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-CONTINUE.md");
|
|
84
|
+
assert.ok(existsSync(continuePath), "compaction should create slice CONTINUE checkpoint");
|
|
85
|
+
|
|
86
|
+
const parsed = parseContinue(readFileSync(continuePath, "utf-8"));
|
|
87
|
+
assert.equal(parsed.frontmatter.milestone, "M001");
|
|
88
|
+
assert.equal(parsed.frontmatter.slice, "S01");
|
|
89
|
+
assert.equal(parsed.frontmatter.task, "none", "planning checkpoint should use non-task placeholder");
|
|
90
|
+
assert.equal(parsed.frontmatter.status, "compacted");
|
|
91
|
+
assert.match(parsed.completedWork, /planning phase/i, "completed-work should capture non-executing phase context");
|
|
92
|
+
assert.match(parsed.nextAction, /slice S01/i, "next action should route resume to the active slice");
|
|
93
|
+
});
|
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Testing strategy:
|
|
9
9
|
* 1. Source-code regression guards: structural checks on register-hooks.ts.
|
|
10
|
-
* 2. Behavioral integration
|
|
11
|
-
*
|
|
12
|
-
* setFooter nor setWidget("gsd-health") is called.
|
|
10
|
+
* 2. Behavioral integration tests: fire the live session handlers with fake
|
|
11
|
+
* contexts and confirm footer/widget behavior from runtime effects.
|
|
13
12
|
*
|
|
14
13
|
* Relates to #4314.
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
import test from "node:test";
|
|
18
17
|
import assert from "node:assert/strict";
|
|
19
|
-
import { mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
18
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
20
19
|
import { join, dirname } from "node:path";
|
|
21
20
|
import { tmpdir } from "node:os";
|
|
22
21
|
import { fileURLToPath } from "node:url";
|
|
23
22
|
|
|
23
|
+
import { autoSession } from "../auto-runtime-state.ts";
|
|
24
24
|
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
25
25
|
|
|
26
26
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -68,27 +68,89 @@ test("session_start handler guards initHealthWidget with !isAutoActive()", () =>
|
|
|
68
68
|
);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
test("session_switch
|
|
72
|
-
const
|
|
73
|
-
|
|
71
|
+
test("session_switch toggles gsd-health from runtime auto state without touching the footer", async (t) => {
|
|
72
|
+
const dir = join(
|
|
73
|
+
tmpdir(),
|
|
74
|
+
`gsd-session-switch-widget-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
75
|
+
);
|
|
76
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
77
|
+
const tempGsdHome = join(dir, "home");
|
|
78
|
+
mkdirSync(tempGsdHome, { recursive: true });
|
|
74
79
|
|
|
75
|
-
const
|
|
76
|
-
|
|
80
|
+
const originalCwd = process.cwd();
|
|
81
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
82
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
83
|
+
process.chdir(dir);
|
|
84
|
+
autoSession.reset();
|
|
85
|
+
t.after(() => {
|
|
86
|
+
autoSession.reset();
|
|
87
|
+
process.chdir(originalCwd);
|
|
88
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
89
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
90
|
+
try { rmSync(dir, { recursive: true, force: true }); } catch { /* best-effort */ }
|
|
91
|
+
});
|
|
77
92
|
|
|
78
|
-
const
|
|
93
|
+
const handlers = new Map<string, (event: unknown, ctx: any) => Promise<void> | void>();
|
|
94
|
+
const pi = {
|
|
95
|
+
on(event: string, handler: (event: unknown, ctx: any) => Promise<void> | void) {
|
|
96
|
+
handlers.set(event, handler);
|
|
97
|
+
},
|
|
98
|
+
} as any;
|
|
79
99
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
registerHooks(pi, []);
|
|
101
|
+
|
|
102
|
+
const sessionSwitch = handlers.get("session_switch");
|
|
103
|
+
assert.ok(sessionSwitch, "session_switch handler must be registered");
|
|
104
|
+
|
|
105
|
+
let setFooterCallCount = 0;
|
|
106
|
+
const widgetCalls: Array<{ key: string; value: unknown }> = [];
|
|
107
|
+
const ctx = {
|
|
108
|
+
hasUI: true,
|
|
109
|
+
ui: {
|
|
110
|
+
notify: () => {},
|
|
111
|
+
setStatus: () => {},
|
|
112
|
+
setFooter: (_footer: unknown) => {
|
|
113
|
+
setFooterCallCount++;
|
|
114
|
+
},
|
|
115
|
+
setWorkingMessage: () => {},
|
|
116
|
+
onTerminalInput: () => () => {},
|
|
117
|
+
setWidget: (key: string, value: unknown) => {
|
|
118
|
+
widgetCalls.push({ key, value });
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
sessionManager: { getSessionId: () => null },
|
|
122
|
+
model: null,
|
|
123
|
+
modelRegistry: {
|
|
124
|
+
setDisabledModelProviders: () => {},
|
|
125
|
+
getProviderAuthMode: () => undefined,
|
|
126
|
+
isProviderRequestReady: () => false,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
autoSession.active = true;
|
|
131
|
+
await sessionSwitch!({ reason: "resume" }, ctx);
|
|
132
|
+
assert.deepEqual(
|
|
133
|
+
widgetCalls.filter((call) => call.key === "gsd-health").map((call) => call.value),
|
|
134
|
+
[undefined],
|
|
135
|
+
"session_switch should hide gsd-health when auto is active",
|
|
87
136
|
);
|
|
137
|
+
assert.equal(setFooterCallCount, 0, "session_switch must not call setFooter when auto is active");
|
|
138
|
+
|
|
139
|
+
widgetCalls.length = 0;
|
|
140
|
+
autoSession.active = false;
|
|
141
|
+
await sessionSwitch!({ reason: "resume" }, ctx);
|
|
142
|
+
const healthWidgetValues = widgetCalls
|
|
143
|
+
.filter((call) => call.key === "gsd-health")
|
|
144
|
+
.map((call) => call.value);
|
|
145
|
+
|
|
146
|
+
assert.ok(healthWidgetValues.length >= 2, "session_switch should initialize gsd-health when auto is inactive");
|
|
88
147
|
assert.ok(
|
|
89
|
-
|
|
90
|
-
"session_switch
|
|
148
|
+
healthWidgetValues.every((value) => value !== undefined),
|
|
149
|
+
"session_switch must not hide gsd-health when auto is inactive",
|
|
91
150
|
);
|
|
151
|
+
assert.ok(Array.isArray(healthWidgetValues[0]), "initHealthWidget should publish initial health lines");
|
|
152
|
+
assert.equal(typeof healthWidgetValues.at(-1), "function", "initHealthWidget should register the live widget factory");
|
|
153
|
+
assert.equal(setFooterCallCount, 0, "session_switch must not call setFooter when auto is inactive");
|
|
92
154
|
});
|
|
93
155
|
|
|
94
156
|
// ─── Behavioral test: neither setFooter nor health suppression when auto inactive ─
|
|
@@ -143,3 +205,90 @@ test("session_start does NOT call setFooter or suppress gsd-health when isAutoAc
|
|
|
143
205
|
assert.equal(setFooterCallCount, 0, "setFooter must NOT be called when isAutoActive() is false");
|
|
144
206
|
assert.equal(healthWidgetHideCount, 0, "gsd-health must NOT be hidden when isAutoActive() is false");
|
|
145
207
|
});
|
|
208
|
+
|
|
209
|
+
test("session_start and session_switch apply disabled model provider policy from current preferences", async (t) => {
|
|
210
|
+
const dir = join(
|
|
211
|
+
tmpdir(),
|
|
212
|
+
`gsd-disabled-provider-policy-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
213
|
+
);
|
|
214
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
215
|
+
const tempGsdHome = join(dir, "home");
|
|
216
|
+
mkdirSync(tempGsdHome, { recursive: true });
|
|
217
|
+
|
|
218
|
+
const originalCwd = process.cwd();
|
|
219
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
220
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
221
|
+
process.chdir(dir);
|
|
222
|
+
t.after(() => {
|
|
223
|
+
process.chdir(originalCwd);
|
|
224
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
225
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
226
|
+
try { rmSync(dir, { recursive: true, force: true }); } catch { /* best-effort */ }
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const writePrefs = (providers: string[]) => {
|
|
230
|
+
writeFileSync(
|
|
231
|
+
join(dir, ".gsd", "PREFERENCES.md"),
|
|
232
|
+
[
|
|
233
|
+
"---",
|
|
234
|
+
"version: 1",
|
|
235
|
+
"disabled_model_providers:",
|
|
236
|
+
...providers.map((provider) => ` - ${provider}`),
|
|
237
|
+
"---",
|
|
238
|
+
"",
|
|
239
|
+
].join("\n"),
|
|
240
|
+
"utf-8",
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const appliedPolicies: string[][] = [];
|
|
245
|
+
const handlers = new Map<string, (event: unknown, ctx: any) => Promise<void> | void>();
|
|
246
|
+
const pi = {
|
|
247
|
+
on(event: string, handler: (event: unknown, ctx: any) => Promise<void> | void) {
|
|
248
|
+
handlers.set(event, handler);
|
|
249
|
+
},
|
|
250
|
+
} as any;
|
|
251
|
+
const ctx = {
|
|
252
|
+
hasUI: true,
|
|
253
|
+
ui: {
|
|
254
|
+
notify: () => {},
|
|
255
|
+
setStatus: () => {},
|
|
256
|
+
setFooter: () => {},
|
|
257
|
+
setWorkingMessage: () => {},
|
|
258
|
+
onTerminalInput: () => () => {},
|
|
259
|
+
setWidget: () => {},
|
|
260
|
+
},
|
|
261
|
+
sessionManager: { getSessionId: () => null },
|
|
262
|
+
model: null,
|
|
263
|
+
modelRegistry: {
|
|
264
|
+
setDisabledModelProviders: (providers: string[]) => {
|
|
265
|
+
appliedPolicies.push([...providers]);
|
|
266
|
+
},
|
|
267
|
+
getProviderAuthMode: () => undefined,
|
|
268
|
+
isProviderRequestReady: () => false,
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
registerHooks(pi, []);
|
|
273
|
+
|
|
274
|
+
const sessionStart = handlers.get("session_start");
|
|
275
|
+
const sessionSwitch = handlers.get("session_switch");
|
|
276
|
+
assert.ok(sessionStart, "session_start handler must be registered");
|
|
277
|
+
assert.ok(sessionSwitch, "session_switch handler must be registered");
|
|
278
|
+
|
|
279
|
+
writePrefs(["google-gemini-cli", " google-gemini-cli ", "openai-codex"]);
|
|
280
|
+
await sessionStart!({}, ctx);
|
|
281
|
+
assert.deepEqual(
|
|
282
|
+
appliedPolicies.at(-1),
|
|
283
|
+
["google-gemini-cli", "openai-codex"],
|
|
284
|
+
"session_start should apply normalized disabled providers before the first agent turn",
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
writePrefs(["anthropic"]);
|
|
288
|
+
await sessionSwitch!({ reason: "resume" }, ctx);
|
|
289
|
+
assert.deepEqual(
|
|
290
|
+
appliedPolicies.at(-1),
|
|
291
|
+
["anthropic"],
|
|
292
|
+
"session_switch should re-read preferences for the switched project/session context",
|
|
293
|
+
);
|
|
294
|
+
});
|
|
@@ -11,7 +11,7 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync
|
|
|
11
11
|
import { join, dirname } from "node:path";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { restoreSliceState } from "../slice-parallel-orchestrator.ts";
|
|
14
|
+
import { restoreSliceState, SLICE_WORKER_AUTO_ARGS } from "../slice-parallel-orchestrator.ts";
|
|
15
15
|
|
|
16
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
const gsdDir = join(__dirname, "..");
|
|
@@ -100,6 +100,12 @@ describe("slice-parallel-orchestrator structural tests", () => {
|
|
|
100
100
|
);
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
it("slice workers use headless auto instead of print-mode slash commands", () => {
|
|
104
|
+
const args: readonly string[] = SLICE_WORKER_AUTO_ARGS;
|
|
105
|
+
assert.deepEqual([...args], ["headless", "--json", "auto"]);
|
|
106
|
+
assert.equal(args.includes("--print"), false);
|
|
107
|
+
});
|
|
108
|
+
|
|
103
109
|
it("maxWorkers default is 2", () => {
|
|
104
110
|
const source = readFileSync(join(gsdDir, "slice-parallel-orchestrator.ts"), "utf-8");
|
|
105
111
|
// Check that default max workers is 2 (in opts.maxWorkers ?? 2 or similar)
|
|
@@ -48,6 +48,28 @@ test("guided-flow complete branch offers a chooser for next milestone or status"
|
|
|
48
48
|
|
|
49
49
|
assert.match(branchChunk, /showNextAction\(/, "complete branch should present a chooser");
|
|
50
50
|
assert.match(branchChunk, /findMilestoneIds\(basePath\)/, "complete branch should compute the next milestone id");
|
|
51
|
-
assert.match(
|
|
51
|
+
assert.match(
|
|
52
|
+
branchChunk,
|
|
53
|
+
/nextMilestoneIdReserved\(milestoneIds,\s*uniqueMilestoneIds,\s*basePath\)/,
|
|
54
|
+
"complete branch should derive the next milestone id",
|
|
55
|
+
);
|
|
52
56
|
assert.match(branchChunk, /dispatchWorkflow\(pi, await prepareAndBuildDiscussPrompt\(/, "complete branch should dispatch the prepared discuss prompt");
|
|
53
57
|
});
|
|
58
|
+
|
|
59
|
+
test("guided-flow needs-discussion skip branch opens the project DB before reserving a new milestone", () => {
|
|
60
|
+
const guidedFlowSource = readFileSync(join(import.meta.dirname, "..", "guided-flow.ts"), "utf-8");
|
|
61
|
+
const laterDbOpenIdx = guidedFlowSource.indexOf("// Ensure DB is open before querying slices (#2560).");
|
|
62
|
+
assert.ok(laterDbOpenIdx > -1, "guided-flow.ts should contain the post-draft DB-open guard");
|
|
63
|
+
|
|
64
|
+
const branchPrefix = guidedFlowSource.slice(0, laterDbOpenIdx);
|
|
65
|
+
const skipBranchIdx = branchPrefix.lastIndexOf('choice === "skip_milestone"');
|
|
66
|
+
assert.ok(skipBranchIdx > -1, "needs-discussion skip branch should be present");
|
|
67
|
+
|
|
68
|
+
const branchChunk = branchPrefix.slice(skipBranchIdx);
|
|
69
|
+
const ensureIdx = branchChunk.indexOf("ensureDbOpen(basePath)");
|
|
70
|
+
const reserveIdx = branchChunk.indexOf("nextMilestoneIdReserved");
|
|
71
|
+
|
|
72
|
+
assert.ok(ensureIdx > -1, "skip branch should open the project DB");
|
|
73
|
+
assert.ok(reserveIdx > -1, "skip branch should reserve the next milestone ID");
|
|
74
|
+
assert.ok(ensureIdx < reserveIdx, "project DB must be opened before milestone ID reservation");
|
|
75
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// GSD bootstrap + system-context-message-routing.test — regression coverage
|
|
2
|
+
// for #5019. `memoryBlock` is FTS-queried against the user prompt and changes
|
|
3
|
+
// per call; embedding it in the cached system prefix invalidates Anthropic
|
|
4
|
+
// prompt-cache hits on every request. The fix routes memory through the
|
|
5
|
+
// existing context-message channel (volatile user-message suffix) and combines
|
|
6
|
+
// it with any active guided-execute or forensics injection.
|
|
7
|
+
|
|
8
|
+
import { describe, test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
|
|
11
|
+
import { buildContextMessage } from "../bootstrap/system-context.ts";
|
|
12
|
+
|
|
13
|
+
describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
14
|
+
test("returns null when nothing to inject", () => {
|
|
15
|
+
const result = buildContextMessage({
|
|
16
|
+
memoryBlock: "",
|
|
17
|
+
injection: null,
|
|
18
|
+
forensicsInjection: null,
|
|
19
|
+
});
|
|
20
|
+
assert.equal(result, null);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("whitespace-only memoryBlock counts as empty", () => {
|
|
24
|
+
const result = buildContextMessage({
|
|
25
|
+
memoryBlock: " \n\n ",
|
|
26
|
+
injection: null,
|
|
27
|
+
forensicsInjection: null,
|
|
28
|
+
});
|
|
29
|
+
assert.equal(result, null);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("memory-only path emits gsd-memory message with trimmed content", () => {
|
|
33
|
+
const result = buildContextMessage({
|
|
34
|
+
memoryBlock: "\n\n[MEMORY]\nrule one\nrule two\n\n",
|
|
35
|
+
injection: null,
|
|
36
|
+
forensicsInjection: null,
|
|
37
|
+
});
|
|
38
|
+
assert.ok(result, "expected a context message");
|
|
39
|
+
assert.equal(result.customType, "gsd-memory");
|
|
40
|
+
assert.equal(result.content, "[MEMORY]\nrule one\nrule two");
|
|
41
|
+
assert.equal(result.display, false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("guided-execute injection alone emits gsd-guided-context", () => {
|
|
45
|
+
const result = buildContextMessage({
|
|
46
|
+
memoryBlock: "",
|
|
47
|
+
injection: "[GUIDED]\nexecute T01",
|
|
48
|
+
forensicsInjection: null,
|
|
49
|
+
});
|
|
50
|
+
assert.ok(result);
|
|
51
|
+
assert.equal(result.customType, "gsd-guided-context");
|
|
52
|
+
assert.equal(result.content, "[GUIDED]\nexecute T01");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("forensics injection alone emits gsd-forensics", () => {
|
|
56
|
+
const result = buildContextMessage({
|
|
57
|
+
memoryBlock: "",
|
|
58
|
+
injection: null,
|
|
59
|
+
forensicsInjection: "[FORENSICS]\ninvestigation context",
|
|
60
|
+
});
|
|
61
|
+
assert.ok(result);
|
|
62
|
+
assert.equal(result.customType, "gsd-forensics");
|
|
63
|
+
assert.equal(result.content, "[FORENSICS]\ninvestigation context");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("memory + guided injection: memory prepended, customType is gsd-guided-context", () => {
|
|
67
|
+
const result = buildContextMessage({
|
|
68
|
+
memoryBlock: "[MEMORY]\nrule one",
|
|
69
|
+
injection: "[GUIDED]\nexecute T01",
|
|
70
|
+
forensicsInjection: null,
|
|
71
|
+
});
|
|
72
|
+
assert.ok(result);
|
|
73
|
+
assert.equal(result.customType, "gsd-guided-context");
|
|
74
|
+
assert.equal(result.content, "[MEMORY]\nrule one\n\n[GUIDED]\nexecute T01");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("memory + forensics: memory prepended, customType is gsd-forensics", () => {
|
|
78
|
+
const result = buildContextMessage({
|
|
79
|
+
memoryBlock: "[MEMORY]\nrule one",
|
|
80
|
+
injection: null,
|
|
81
|
+
forensicsInjection: "[FORENSICS]\ninvestigation context",
|
|
82
|
+
});
|
|
83
|
+
assert.ok(result);
|
|
84
|
+
assert.equal(result.customType, "gsd-forensics");
|
|
85
|
+
assert.equal(result.content, "[MEMORY]\nrule one\n\n[FORENSICS]\ninvestigation context");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("guided takes precedence over forensics when both are somehow present", () => {
|
|
89
|
+
// The caller in buildBeforeAgentStartResult already gates forensics on
|
|
90
|
+
// `!injection`, but the helper's documented priority is guided > forensics.
|
|
91
|
+
// Test the contract directly so a future refactor can't silently flip it.
|
|
92
|
+
const result = buildContextMessage({
|
|
93
|
+
memoryBlock: "",
|
|
94
|
+
injection: "[GUIDED]",
|
|
95
|
+
forensicsInjection: "[FORENSICS]",
|
|
96
|
+
});
|
|
97
|
+
assert.ok(result);
|
|
98
|
+
assert.equal(result.customType, "gsd-guided-context");
|
|
99
|
+
assert.equal(result.content, "[GUIDED]");
|
|
100
|
+
});
|
|
101
|
+
});
|