gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6
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/dist/headless.js +25 -4
- package/dist/resource-loader.d.ts +40 -0
- package/dist/resource-loader.js +32 -13
- package/dist/resources/extensions/browser-tools/capture.js +9 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -18
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
- package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
- package/dist/resources/extensions/gsd/auto-start.js +75 -24
- package/dist/resources/extensions/gsd/auto.js +34 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
- package/dist/resources/extensions/gsd/component-loader.js +447 -0
- package/dist/resources/extensions/gsd/component-types.js +69 -0
- package/dist/resources/extensions/gsd/context-store.js +23 -7
- package/dist/resources/extensions/gsd/detection.js +49 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/forensics.js +106 -0
- package/dist/resources/extensions/gsd/gsd-db.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +2 -4
- package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
- package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
- package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
- package/dist/resources/extensions/gsd/model-router.js +6 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
- package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
- package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
- package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
- package/dist/resources/extensions/mcp-client/index.js +3 -1
- package/dist/resources/extensions/ollama/index.js +5 -1
- package/dist/resources/extensions/remote-questions/manager.js +11 -5
- 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 +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 +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/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/chunks/6897.js +1 -1
- 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/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/package.json +1 -3
- package/packages/mcp-server/src/mcp-server.test.ts +25 -3
- package/packages/mcp-server/src/readers/graph.test.ts +87 -15
- package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
- package/packages/native/package.json +1 -1
- package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
- package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
- package/packages/native/src/__tests__/ps.test.mjs +14 -8
- package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
- package/packages/native/src/__tests__/truncate.test.mjs +17 -2
- package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
- package/packages/pi-agent-core/src/agent.test.ts +96 -102
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
- package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
- package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/generated/openai.js +17 -0
- package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.js +43 -70
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +29 -11
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +44 -0
- package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
- package/packages/pi-ai/src/models/generated/openai.ts +17 -0
- package/packages/pi-ai/src/models.generated.test.ts +46 -73
- package/packages/pi-ai/src/models.test.ts +39 -11
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
- package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
- package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -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 +16 -2
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
- 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 +30 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
- package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
- package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
- package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
- package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
- package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.js +6 -5
- package/packages/pi-tui/dist/components/image.test.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
- package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
- package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
- package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
- package/packages/pi-tui/src/components/image.test.ts +10 -5
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/dist/rpc-client.test.js +101 -51
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
- package/packages/rpc-client/src/rpc-client.test.ts +109 -52
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/scripts/install.js +15 -1
- package/src/resources/extensions/browser-tools/capture.ts +12 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
- package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
- package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
- package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -17
- package/src/resources/extensions/gsd/auto/session.ts +7 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
- package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
- package/src/resources/extensions/gsd/auto-start.ts +97 -4
- package/src/resources/extensions/gsd/auto.ts +37 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
- package/src/resources/extensions/gsd/component-loader.ts +598 -0
- package/src/resources/extensions/gsd/component-types.ts +362 -0
- package/src/resources/extensions/gsd/context-store.ts +25 -8
- package/src/resources/extensions/gsd/detection.ts +58 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/forensics.ts +118 -1
- package/src/resources/extensions/gsd/git-service.ts +16 -0
- package/src/resources/extensions/gsd/gsd-db.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +2 -4
- package/src/resources/extensions/gsd/journal.ts +11 -1
- package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
- package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
- package/src/resources/extensions/gsd/model-router.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
- package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
- package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
- package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
- package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
- package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
- package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
- package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
- package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
- package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
- package/src/resources/extensions/mcp-client/index.ts +3 -1
- package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
- package/src/resources/extensions/ollama/index.ts +5 -1
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
- package/src/resources/extensions/remote-questions/manager.ts +36 -4
- package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
- package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
- package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
- package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
- /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
|
@@ -20,6 +20,9 @@ import type { AutoSession } from "./auto/session.js";
|
|
|
20
20
|
import { debugLog } from "./debug-logger.js";
|
|
21
21
|
import { MergeConflictError } from "./git-service.js";
|
|
22
22
|
import { emitJournalEvent } from "./journal.js";
|
|
23
|
+
import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js";
|
|
24
|
+
import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain } from "./slice-cadence.js";
|
|
25
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
23
26
|
|
|
24
27
|
// ─── Dependency Interface ──────────────────────────────────────────────────
|
|
25
28
|
|
|
@@ -290,6 +293,19 @@ export class WorktreeResolver {
|
|
|
290
293
|
eventType: "worktree-enter",
|
|
291
294
|
data: { milestoneId, wtPath, created: !existingPath },
|
|
292
295
|
});
|
|
296
|
+
// #4764 — record creation/enter as a lifecycle event so the telemetry
|
|
297
|
+
// aggregator can pair it with the eventual worktree-merged event.
|
|
298
|
+
try {
|
|
299
|
+
emitWorktreeCreated(this.s.originalBasePath || this.s.basePath, milestoneId, {
|
|
300
|
+
reason: existingPath ? "enter-milestone" : "create-milestone",
|
|
301
|
+
});
|
|
302
|
+
} catch (telemetryErr) {
|
|
303
|
+
debugLog("WorktreeResolver", {
|
|
304
|
+
action: "enterMilestone",
|
|
305
|
+
phase: "telemetry-emit",
|
|
306
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
293
309
|
ctx.notify(`Entered worktree for ${milestoneId} at ${wtPath}`, "info");
|
|
294
310
|
} catch (err) {
|
|
295
311
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -398,6 +414,10 @@ export class WorktreeResolver {
|
|
|
398
414
|
mergeAndExit(milestoneId: string, ctx: NotifyCtx): void {
|
|
399
415
|
this.validateMilestoneId(milestoneId);
|
|
400
416
|
|
|
417
|
+
// #4764 — telemetry: record start timestamp so we can emit merge duration.
|
|
418
|
+
const mergeStartedAt = new Date().toISOString();
|
|
419
|
+
const mergeStartMs = Date.now();
|
|
420
|
+
|
|
401
421
|
// If worktree creation failed earlier, skip merge — work is on current branch (#2483)
|
|
402
422
|
if (this.s.isolationDegraded) {
|
|
403
423
|
debugLog("WorktreeResolver", {
|
|
@@ -444,17 +464,79 @@ export class WorktreeResolver {
|
|
|
444
464
|
return;
|
|
445
465
|
}
|
|
446
466
|
|
|
467
|
+
let actuallyMerged = false;
|
|
447
468
|
if (
|
|
448
469
|
mode === "worktree" || inWorktree
|
|
449
470
|
) {
|
|
450
|
-
this._mergeWorktreeMode(milestoneId, ctx);
|
|
471
|
+
actuallyMerged = this._mergeWorktreeMode(milestoneId, ctx);
|
|
451
472
|
} else if (mode === "branch") {
|
|
452
|
-
this._mergeBranchMode(milestoneId, ctx);
|
|
473
|
+
actuallyMerged = this._mergeBranchMode(milestoneId, ctx);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// The remainder of this function emits telemetry and runs re-squash.
|
|
477
|
+
// Both are gated on actuallyMerged — if the _merge* helper took a
|
|
478
|
+
// no-merge path (missing originalBase, no roadmap, wrong branch) the
|
|
479
|
+
// milestone branch was intentionally left unmerged and we must not
|
|
480
|
+
// emit a worktree-merged event or collapse commits on main.
|
|
481
|
+
if (!actuallyMerged) {
|
|
482
|
+
// Always clear the start-SHA tracker to avoid leaking across sessions.
|
|
483
|
+
this.s.milestoneStartShas.delete(milestoneId);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// #4765 — when collapse_cadence=slice AND milestone_resquash=true, the
|
|
488
|
+
// N per-slice commits on main should be collapsed into one milestone
|
|
489
|
+
// commit. Done AFTER the primary merge-and-teardown so the branch and
|
|
490
|
+
// worktree are already cleaned up; we operate on main directly.
|
|
491
|
+
try {
|
|
492
|
+
const startSha = this.s.milestoneStartShas.get(milestoneId);
|
|
493
|
+
if (startSha) {
|
|
494
|
+
const prefs = loadEffectiveGSDPreferences(this.s.originalBasePath || this.s.basePath)?.preferences;
|
|
495
|
+
if (getCollapseCadence(prefs) === "slice" && getMilestoneResquash(prefs)) {
|
|
496
|
+
const result = resquashMilestoneOnMain(
|
|
497
|
+
this.s.originalBasePath || this.s.basePath,
|
|
498
|
+
milestoneId,
|
|
499
|
+
startSha,
|
|
500
|
+
);
|
|
501
|
+
if (result.resquashed) {
|
|
502
|
+
ctx.notify(
|
|
503
|
+
`slice-cadence: re-squashed slice commits for ${milestoneId} into a single milestone commit.`,
|
|
504
|
+
"info",
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
this.s.milestoneStartShas.delete(milestoneId);
|
|
509
|
+
}
|
|
510
|
+
} catch (err) {
|
|
511
|
+
debugLog("WorktreeResolver", {
|
|
512
|
+
action: "mergeAndExit",
|
|
513
|
+
milestoneId,
|
|
514
|
+
phase: "resquash",
|
|
515
|
+
error: err instanceof Error ? err.message : String(err),
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// #4764 — record merge completion. Only reaches here when an actual
|
|
520
|
+
// merge ran; failure paths throw out of _merge* before this point and
|
|
521
|
+
// no-merge paths returned above.
|
|
522
|
+
try {
|
|
523
|
+
emitWorktreeMerged(this.s.originalBasePath || this.s.basePath, milestoneId, {
|
|
524
|
+
reason: "milestone-complete",
|
|
525
|
+
startedAt: mergeStartedAt,
|
|
526
|
+
durationMs: Date.now() - mergeStartMs,
|
|
527
|
+
});
|
|
528
|
+
} catch (telemetryErr) {
|
|
529
|
+
debugLog("WorktreeResolver", {
|
|
530
|
+
action: "mergeAndExit",
|
|
531
|
+
phase: "telemetry-emit",
|
|
532
|
+
error: telemetryErr instanceof Error ? telemetryErr.message : String(telemetryErr),
|
|
533
|
+
});
|
|
453
534
|
}
|
|
454
535
|
}
|
|
455
536
|
|
|
456
|
-
/** Worktree-mode merge: read roadmap, merge, teardown, reset paths.
|
|
457
|
-
|
|
537
|
+
/** Worktree-mode merge: read roadmap, merge, teardown, reset paths.
|
|
538
|
+
* Returns true when a squash-merge actually ran (false on skip paths). */
|
|
539
|
+
private _mergeWorktreeMode(milestoneId: string, ctx: NotifyCtx): boolean {
|
|
458
540
|
const originalBase = this.s.originalBasePath;
|
|
459
541
|
if (!originalBase) {
|
|
460
542
|
debugLog("WorktreeResolver", {
|
|
@@ -464,9 +546,10 @@ export class WorktreeResolver {
|
|
|
464
546
|
skipped: true,
|
|
465
547
|
reason: "missing-original-base",
|
|
466
548
|
});
|
|
467
|
-
return;
|
|
549
|
+
return false;
|
|
468
550
|
}
|
|
469
551
|
|
|
552
|
+
let merged = false;
|
|
470
553
|
try {
|
|
471
554
|
const { synced } = this.deps.syncWorktreeStateBack(
|
|
472
555
|
originalBase,
|
|
@@ -515,6 +598,7 @@ export class WorktreeResolver {
|
|
|
515
598
|
milestoneId,
|
|
516
599
|
roadmapContent,
|
|
517
600
|
);
|
|
601
|
+
merged = true;
|
|
518
602
|
|
|
519
603
|
// #2945 Bug 3: mergeMilestoneToMain performs best-effort worktree
|
|
520
604
|
// cleanup internally (step 12), but it can silently fail on Windows
|
|
@@ -618,10 +702,12 @@ export class WorktreeResolver {
|
|
|
618
702
|
result: "done",
|
|
619
703
|
basePath: this.s.basePath,
|
|
620
704
|
});
|
|
705
|
+
return merged;
|
|
621
706
|
}
|
|
622
707
|
|
|
623
|
-
/** Branch-mode merge: check current branch, merge if on milestone branch.
|
|
624
|
-
|
|
708
|
+
/** Branch-mode merge: check current branch, merge if on milestone branch.
|
|
709
|
+
* Returns true when a merge actually ran (false on skip paths). */
|
|
710
|
+
private _mergeBranchMode(milestoneId: string, ctx: NotifyCtx): boolean {
|
|
625
711
|
try {
|
|
626
712
|
const currentBranch = this.deps.getCurrentBranch(this.s.basePath);
|
|
627
713
|
const milestoneBranch = this.deps.autoWorktreeBranch(milestoneId);
|
|
@@ -636,7 +722,7 @@ export class WorktreeResolver {
|
|
|
636
722
|
currentBranch,
|
|
637
723
|
milestoneBranch,
|
|
638
724
|
});
|
|
639
|
-
return;
|
|
725
|
+
return false;
|
|
640
726
|
}
|
|
641
727
|
|
|
642
728
|
const roadmapPath = this.deps.resolveMilestoneFile(
|
|
@@ -652,7 +738,7 @@ export class WorktreeResolver {
|
|
|
652
738
|
skipped: true,
|
|
653
739
|
reason: "no-roadmap",
|
|
654
740
|
});
|
|
655
|
-
return;
|
|
741
|
+
return false;
|
|
656
742
|
}
|
|
657
743
|
|
|
658
744
|
const roadmapContent = this.deps.readFileSync(roadmapPath, "utf-8");
|
|
@@ -683,6 +769,7 @@ export class WorktreeResolver {
|
|
|
683
769
|
mode: "branch",
|
|
684
770
|
result: "success",
|
|
685
771
|
});
|
|
772
|
+
return true;
|
|
686
773
|
} catch (err) {
|
|
687
774
|
const msg = err instanceof Error ? err.message : String(err);
|
|
688
775
|
debugLog("WorktreeResolver", {
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree telemetry — #4764
|
|
3
|
+
*
|
|
4
|
+
* Thin emit helpers + aggregator on top of the existing journal. Separate
|
|
5
|
+
* module so callers import a tiny surface and don't have to assemble
|
|
6
|
+
* JournalEntry records by hand. Kernighan: the underlying emit path
|
|
7
|
+
* (emitJournalEvent) is already battle-tested; this module is just
|
|
8
|
+
* structured call sites + a summarizer.
|
|
9
|
+
*
|
|
10
|
+
* Emitted event types (see journal.ts):
|
|
11
|
+
* - worktree-created worktree entered/created for a milestone
|
|
12
|
+
* - worktree-merged worktree merge back to main completed
|
|
13
|
+
* - worktree-orphaned audit detected an orphaned branch/worktree
|
|
14
|
+
* - auto-exit auto-mode exited (pause/stop/blocked/error)
|
|
15
|
+
* - worktree-sync syncStateToProjectRoot snapshot
|
|
16
|
+
* - canonical-root-redirect resolveCanonicalMilestoneRoot redirected
|
|
17
|
+
*
|
|
18
|
+
* These events are purely observational. They never block, never throw,
|
|
19
|
+
* and never carry code content — only IDs, counts, durations, and reasons.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
23
|
+
import { emitJournalEvent, queryJournal } from "./journal.js";
|
|
24
|
+
import type { JournalEntry } from "./journal.js";
|
|
25
|
+
|
|
26
|
+
function now(): string {
|
|
27
|
+
return new Date().toISOString();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function baseEntry(eventType: JournalEntry["eventType"], data: Record<string, unknown>): JournalEntry {
|
|
31
|
+
return {
|
|
32
|
+
ts: now(),
|
|
33
|
+
flowId: (typeof data.flowId === "string" ? data.flowId : undefined) ?? randomUUID(),
|
|
34
|
+
seq: typeof data.seq === "number" ? data.seq : 0,
|
|
35
|
+
eventType,
|
|
36
|
+
data,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Reason literal unions ───────────────────────────────────────────────
|
|
41
|
+
// Closed sets so typos at call sites are rejected at compile time and can't
|
|
42
|
+
// silently fragment the telemetry buckets produced by summarizeWorktreeTelemetry.
|
|
43
|
+
|
|
44
|
+
export type WorktreeCreatedReason = "create-milestone" | "enter-milestone";
|
|
45
|
+
export type AutoExitReason =
|
|
46
|
+
| "pause"
|
|
47
|
+
| "stop"
|
|
48
|
+
| "blocked"
|
|
49
|
+
| "merge-conflict"
|
|
50
|
+
| "merge-failed"
|
|
51
|
+
| "slice-merge-conflict"
|
|
52
|
+
| "all-complete"
|
|
53
|
+
| "no-active-milestone"
|
|
54
|
+
| "other";
|
|
55
|
+
|
|
56
|
+
// ─── Emitters ────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export function emitWorktreeCreated(
|
|
59
|
+
projectRoot: string,
|
|
60
|
+
milestoneId: string,
|
|
61
|
+
meta: { flowId?: string; reason?: WorktreeCreatedReason } = {},
|
|
62
|
+
): void {
|
|
63
|
+
emitJournalEvent(projectRoot, baseEntry("worktree-created", {
|
|
64
|
+
milestoneId,
|
|
65
|
+
startedAt: now(),
|
|
66
|
+
flowId: meta.flowId,
|
|
67
|
+
reason: meta.reason ?? "enter-milestone",
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function emitWorktreeMerged(
|
|
72
|
+
projectRoot: string,
|
|
73
|
+
milestoneId: string,
|
|
74
|
+
meta: {
|
|
75
|
+
flowId?: string;
|
|
76
|
+
reason?: "milestone-complete" | "all-complete" | "stop-fallback" | "transition" | "other";
|
|
77
|
+
startedAt?: string;
|
|
78
|
+
durationMs?: number;
|
|
79
|
+
sliceCount?: number;
|
|
80
|
+
taskCount?: number;
|
|
81
|
+
conflict?: boolean;
|
|
82
|
+
conflictedFiles?: number;
|
|
83
|
+
} = {},
|
|
84
|
+
): void {
|
|
85
|
+
emitJournalEvent(projectRoot, baseEntry("worktree-merged", {
|
|
86
|
+
milestoneId,
|
|
87
|
+
endedAt: now(),
|
|
88
|
+
flowId: meta.flowId,
|
|
89
|
+
reason: meta.reason ?? "other",
|
|
90
|
+
startedAt: meta.startedAt,
|
|
91
|
+
durationMs: meta.durationMs,
|
|
92
|
+
sliceCount: meta.sliceCount,
|
|
93
|
+
taskCount: meta.taskCount,
|
|
94
|
+
conflict: meta.conflict ?? false,
|
|
95
|
+
conflictedFiles: meta.conflictedFiles ?? 0,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function emitWorktreeOrphaned(
|
|
100
|
+
projectRoot: string,
|
|
101
|
+
milestoneId: string,
|
|
102
|
+
meta: {
|
|
103
|
+
flowId?: string;
|
|
104
|
+
reason: "in-progress-unmerged" | "complete-unmerged" | "stale-branch";
|
|
105
|
+
commitsAhead?: number;
|
|
106
|
+
worktreeDirExists?: boolean;
|
|
107
|
+
},
|
|
108
|
+
): void {
|
|
109
|
+
emitJournalEvent(projectRoot, baseEntry("worktree-orphaned", {
|
|
110
|
+
milestoneId,
|
|
111
|
+
flowId: meta.flowId,
|
|
112
|
+
reason: meta.reason,
|
|
113
|
+
commitsAhead: meta.commitsAhead,
|
|
114
|
+
worktreeDirExists: meta.worktreeDirExists ?? false,
|
|
115
|
+
detectedAt: now(),
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function emitAutoExit(
|
|
120
|
+
projectRoot: string,
|
|
121
|
+
meta: {
|
|
122
|
+
flowId?: string;
|
|
123
|
+
/** Must come from the closed AutoExitReason set. Callers with free-form
|
|
124
|
+
* reasons (e.g. stopAuto's `reason?: string` parameter) should map to
|
|
125
|
+
* the closed set before emitting. */
|
|
126
|
+
reason: AutoExitReason;
|
|
127
|
+
milestoneId?: string;
|
|
128
|
+
milestoneMerged: boolean;
|
|
129
|
+
},
|
|
130
|
+
): void {
|
|
131
|
+
emitJournalEvent(projectRoot, baseEntry("auto-exit", {
|
|
132
|
+
reason: meta.reason,
|
|
133
|
+
flowId: meta.flowId,
|
|
134
|
+
milestoneId: meta.milestoneId,
|
|
135
|
+
milestoneMerged: meta.milestoneMerged,
|
|
136
|
+
exitedAt: now(),
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function emitWorktreeSync(
|
|
141
|
+
projectRoot: string,
|
|
142
|
+
milestoneId: string,
|
|
143
|
+
meta: {
|
|
144
|
+
flowId?: string;
|
|
145
|
+
filesCopied?: number;
|
|
146
|
+
bytesCopied?: number;
|
|
147
|
+
commitsAhead?: number;
|
|
148
|
+
worktreeAgeMs?: number;
|
|
149
|
+
},
|
|
150
|
+
): void {
|
|
151
|
+
emitJournalEvent(projectRoot, baseEntry("worktree-sync", {
|
|
152
|
+
milestoneId,
|
|
153
|
+
flowId: meta.flowId,
|
|
154
|
+
filesCopied: meta.filesCopied,
|
|
155
|
+
bytesCopied: meta.bytesCopied,
|
|
156
|
+
commitsAhead: meta.commitsAhead,
|
|
157
|
+
worktreeAgeMs: meta.worktreeAgeMs,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function emitCanonicalRootRedirect(
|
|
162
|
+
projectRoot: string,
|
|
163
|
+
milestoneId: string,
|
|
164
|
+
redirectedTo: string,
|
|
165
|
+
meta: { flowId?: string } = {},
|
|
166
|
+
): void {
|
|
167
|
+
emitJournalEvent(projectRoot, baseEntry("canonical-root-redirect", {
|
|
168
|
+
milestoneId,
|
|
169
|
+
redirectedTo,
|
|
170
|
+
flowId: meta.flowId,
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// #4765 — slice-cadence collapse events
|
|
175
|
+
|
|
176
|
+
export function emitSliceMerged(
|
|
177
|
+
projectRoot: string,
|
|
178
|
+
milestoneId: string,
|
|
179
|
+
sliceId: string,
|
|
180
|
+
meta: { durationMs?: number; conflict?: boolean; commitSha?: string; flowId?: string } = {},
|
|
181
|
+
): void {
|
|
182
|
+
emitJournalEvent(projectRoot, baseEntry("slice-merged", {
|
|
183
|
+
milestoneId,
|
|
184
|
+
sliceId,
|
|
185
|
+
mergedAt: now(),
|
|
186
|
+
durationMs: meta.durationMs,
|
|
187
|
+
conflict: meta.conflict ?? false,
|
|
188
|
+
commitSha: meta.commitSha,
|
|
189
|
+
flowId: meta.flowId,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function emitMilestoneResquash(
|
|
194
|
+
projectRoot: string,
|
|
195
|
+
milestoneId: string,
|
|
196
|
+
meta: { sliceCount: number; startSha?: string; endSha?: string; flowId?: string } = { sliceCount: 0 },
|
|
197
|
+
): void {
|
|
198
|
+
emitJournalEvent(projectRoot, baseEntry("milestone-resquash", {
|
|
199
|
+
milestoneId,
|
|
200
|
+
sliceCount: meta.sliceCount,
|
|
201
|
+
startSha: meta.startSha,
|
|
202
|
+
endSha: meta.endSha,
|
|
203
|
+
resquashedAt: now(),
|
|
204
|
+
flowId: meta.flowId,
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ─── Aggregator ──────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
export interface WorktreeTelemetrySummary {
|
|
211
|
+
/** Count of worktrees created within the window */
|
|
212
|
+
worktreesCreated: number;
|
|
213
|
+
/** Count of worktrees merged within the window */
|
|
214
|
+
worktreesMerged: number;
|
|
215
|
+
/** Count of orphan detections within the window */
|
|
216
|
+
orphansDetected: number;
|
|
217
|
+
/** Breakdown by orphan reason */
|
|
218
|
+
orphansByReason: Record<string, number>;
|
|
219
|
+
/** Merge durations in milliseconds, sorted ascending */
|
|
220
|
+
mergeDurationsMs: number[];
|
|
221
|
+
/** Number of merges that hit a conflict */
|
|
222
|
+
mergeConflicts: number;
|
|
223
|
+
/** Auto-exit reasons and their counts */
|
|
224
|
+
exitsByReason: Record<string, number>;
|
|
225
|
+
/** Auto-exits where the milestone was NOT merged before exit — the #4761 producer metric */
|
|
226
|
+
exitsWithUnmergedWork: number;
|
|
227
|
+
/** Count of canonical-root-redirects (how often #4761 validation would have read stale state) */
|
|
228
|
+
canonicalRedirects: number;
|
|
229
|
+
/** #4765 — count of successful slice-level merges (slice-cadence feature) */
|
|
230
|
+
slicesMerged: number;
|
|
231
|
+
/** #4765 — count of slice-level merge conflicts */
|
|
232
|
+
sliceMergeConflicts: number;
|
|
233
|
+
/** #4765 — count of milestone-level re-squash operations */
|
|
234
|
+
milestoneResquashes: number;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Summarize worktree telemetry across the journal. Optional time window
|
|
239
|
+
* via filters.after / filters.before (ISO-8601).
|
|
240
|
+
*/
|
|
241
|
+
export function summarizeWorktreeTelemetry(
|
|
242
|
+
projectRoot: string,
|
|
243
|
+
filters?: { after?: string; before?: string },
|
|
244
|
+
): WorktreeTelemetrySummary {
|
|
245
|
+
const entries = queryJournal(projectRoot, filters);
|
|
246
|
+
|
|
247
|
+
const summary: WorktreeTelemetrySummary = {
|
|
248
|
+
worktreesCreated: 0,
|
|
249
|
+
worktreesMerged: 0,
|
|
250
|
+
orphansDetected: 0,
|
|
251
|
+
orphansByReason: {},
|
|
252
|
+
mergeDurationsMs: [],
|
|
253
|
+
mergeConflicts: 0,
|
|
254
|
+
exitsByReason: {},
|
|
255
|
+
exitsWithUnmergedWork: 0,
|
|
256
|
+
canonicalRedirects: 0,
|
|
257
|
+
slicesMerged: 0,
|
|
258
|
+
sliceMergeConflicts: 0,
|
|
259
|
+
milestoneResquashes: 0,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
for (const e of entries) {
|
|
263
|
+
const d = e.data ?? {};
|
|
264
|
+
switch (e.eventType) {
|
|
265
|
+
case "worktree-created":
|
|
266
|
+
summary.worktreesCreated++;
|
|
267
|
+
break;
|
|
268
|
+
case "worktree-merged":
|
|
269
|
+
summary.worktreesMerged++;
|
|
270
|
+
if (typeof d.durationMs === "number") summary.mergeDurationsMs.push(d.durationMs);
|
|
271
|
+
if (d.conflict === true) summary.mergeConflicts++;
|
|
272
|
+
break;
|
|
273
|
+
case "worktree-orphaned": {
|
|
274
|
+
summary.orphansDetected++;
|
|
275
|
+
const reason = typeof d.reason === "string" ? d.reason : "unknown";
|
|
276
|
+
summary.orphansByReason[reason] = (summary.orphansByReason[reason] ?? 0) + 1;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
case "auto-exit": {
|
|
280
|
+
const reason = typeof d.reason === "string" ? d.reason : "unknown";
|
|
281
|
+
summary.exitsByReason[reason] = (summary.exitsByReason[reason] ?? 0) + 1;
|
|
282
|
+
if (d.milestoneMerged === false) summary.exitsWithUnmergedWork++;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case "canonical-root-redirect":
|
|
286
|
+
summary.canonicalRedirects++;
|
|
287
|
+
break;
|
|
288
|
+
case "slice-merged":
|
|
289
|
+
summary.slicesMerged++;
|
|
290
|
+
if (d.conflict === true) summary.sliceMergeConflicts++;
|
|
291
|
+
break;
|
|
292
|
+
case "milestone-resquash":
|
|
293
|
+
summary.milestoneResquashes++;
|
|
294
|
+
break;
|
|
295
|
+
default:
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
summary.mergeDurationsMs.sort((a, b) => a - b);
|
|
301
|
+
return summary;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Return the p{quantile} of a sorted array using the nearest-rank method.
|
|
306
|
+
* Quantile in [0,1].
|
|
307
|
+
*
|
|
308
|
+
* Prior implementation used Math.floor(q*n), which overstates exact-rank
|
|
309
|
+
* quantiles by one sample (e.g. p95 of 20 values returned the max instead
|
|
310
|
+
* of the 19th value). The nearest-rank index is ceil(q*n) - 1, clamped to
|
|
311
|
+
* [0, n-1].
|
|
312
|
+
*/
|
|
313
|
+
export function percentile(sortedValues: number[], q: number): number | null {
|
|
314
|
+
if (sortedValues.length === 0) return null;
|
|
315
|
+
if (q <= 0) return sortedValues[0];
|
|
316
|
+
if (q >= 1) return sortedValues[sortedValues.length - 1];
|
|
317
|
+
const idx = Math.min(
|
|
318
|
+
sortedValues.length - 1,
|
|
319
|
+
Math.max(0, Math.ceil(q * sortedValues.length) - 1),
|
|
320
|
+
);
|
|
321
|
+
return sortedValues[idx];
|
|
322
|
+
}
|
|
@@ -203,7 +203,9 @@ async function assertTrustedStdioServer(
|
|
|
203
203
|
return trustKey;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
// Exported for tests (see tests/server-name-spaces.test.ts).
|
|
207
|
+
// Production call sites treat this as module-private.
|
|
208
|
+
export function getServerConfig(name: string): McpServerConfig | undefined {
|
|
207
209
|
const trimmed = name.trim();
|
|
208
210
|
return readConfigs().find((s) =>
|
|
209
211
|
s.name === trimmed ||
|
|
@@ -1,55 +1,89 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Regression test for #3029 — mcp_discover fails for server names with spaces.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1. Exact match
|
|
4
|
+
* getServerConfig must handle:
|
|
5
|
+
* 1. Exact match
|
|
6
6
|
* 2. Names with leading/trailing whitespace (trimming)
|
|
7
7
|
* 3. Case-insensitive matching (e.g. "Langgraph code" vs "langgraph Code")
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* getOrConnect must use the canonical (config.name) as the cache key so that
|
|
10
|
+
* subsequent lookups with variant casing/whitespace hit the same connection.
|
|
11
|
+
*
|
|
12
|
+
* These are behaviour tests against the real exported getServerConfig — no
|
|
13
|
+
* source grep.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
|
-
import test from "node:test";
|
|
16
|
+
import test, { before, after } from "node:test";
|
|
13
17
|
import assert from "node:assert/strict";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
18
|
+
import { writeFileSync, unlinkSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { mkdtempSync } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
|
|
23
|
+
import { getServerConfig } from "../index.js";
|
|
24
|
+
|
|
25
|
+
// readConfigs() anchors to process.cwd() — run each test in a sandbox dir
|
|
26
|
+
// with a purpose-built .mcp.json so the extension reads our fixture, not
|
|
27
|
+
// whatever .mcp.json happens to live in the current working directory.
|
|
28
|
+
let sandboxDir: string;
|
|
29
|
+
let originalCwd: string;
|
|
30
|
+
|
|
31
|
+
before(() => {
|
|
32
|
+
originalCwd = process.cwd();
|
|
33
|
+
sandboxDir = mkdtempSync(join(tmpdir(), "mcp-name-spaces-"));
|
|
34
|
+
const mcpConfig = {
|
|
35
|
+
mcpServers: {
|
|
36
|
+
"Langgraph Code": {
|
|
37
|
+
command: "echo",
|
|
38
|
+
args: ["test"],
|
|
39
|
+
},
|
|
40
|
+
"other-server": {
|
|
41
|
+
url: "https://example.com",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
writeFileSync(join(sandboxDir, ".mcp.json"), JSON.stringify(mcpConfig), "utf-8");
|
|
46
|
+
process.chdir(sandboxDir);
|
|
47
|
+
});
|
|
17
48
|
|
|
18
|
-
|
|
19
|
-
|
|
49
|
+
after(() => {
|
|
50
|
+
process.chdir(originalCwd);
|
|
51
|
+
try {
|
|
52
|
+
rmSync(sandboxDir, { recursive: true, force: true });
|
|
53
|
+
} catch {
|
|
54
|
+
// Best-effort cleanup
|
|
55
|
+
}
|
|
56
|
+
});
|
|
20
57
|
|
|
21
|
-
|
|
58
|
+
test("#3029: getServerConfig finds exact match", () => {
|
|
59
|
+
const cfg = getServerConfig("Langgraph Code");
|
|
60
|
+
assert.ok(cfg, "exact name must resolve");
|
|
61
|
+
assert.equal(cfg?.name, "Langgraph Code");
|
|
62
|
+
});
|
|
22
63
|
|
|
23
64
|
test("#3029: getServerConfig trims whitespace from input name", () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
65
|
+
const cfg = getServerConfig(" Langgraph Code ");
|
|
66
|
+
assert.ok(cfg, "whitespace-padded name must resolve to the same server");
|
|
67
|
+
assert.equal(cfg?.name, "Langgraph Code");
|
|
28
68
|
});
|
|
29
69
|
|
|
30
70
|
test("#3029: getServerConfig performs case-insensitive matching", () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
const cfg = getServerConfig("langgraph code");
|
|
72
|
+
assert.ok(cfg, "lower-cased name must resolve");
|
|
73
|
+
assert.equal(cfg?.name, "Langgraph Code");
|
|
74
|
+
|
|
75
|
+
const mixed = getServerConfig("LANGGRAPH CODE");
|
|
76
|
+
assert.ok(mixed, "upper-cased name must resolve");
|
|
77
|
+
assert.equal(mixed?.name, "Langgraph Code");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("#3029: getServerConfig combines trim + case-insensitive", () => {
|
|
81
|
+
const cfg = getServerConfig(" LANGGRAPH code ");
|
|
82
|
+
assert.ok(cfg, "padded + mixed-case must resolve");
|
|
83
|
+
assert.equal(cfg?.name, "Langgraph Code");
|
|
35
84
|
});
|
|
36
85
|
|
|
37
|
-
test("#3029:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// user's casing differs.
|
|
41
|
-
const getOrConnectMatch = source.match(
|
|
42
|
-
/async function getOrConnect\(name: string[\s\S]*?const existing = connections\.get\(/,
|
|
43
|
-
);
|
|
44
|
-
assert.ok(
|
|
45
|
-
getOrConnectMatch,
|
|
46
|
-
"getOrConnect function should exist",
|
|
47
|
-
);
|
|
48
|
-
// After the fix, getOrConnect should normalize the name via getServerConfig
|
|
49
|
-
// or use config.name as the canonical cache key.
|
|
50
|
-
assert.ok(
|
|
51
|
-
source.includes("connections.get(config.name") ||
|
|
52
|
-
source.includes("connections.set(config.name"),
|
|
53
|
-
"getOrConnect should use config.name (canonical) as the connections cache key",
|
|
54
|
-
);
|
|
86
|
+
test("#3029: getServerConfig returns undefined for unknown name", () => {
|
|
87
|
+
const cfg = getServerConfig("does-not-exist");
|
|
88
|
+
assert.equal(cfg, undefined);
|
|
55
89
|
});
|
|
@@ -45,8 +45,12 @@ let providerRegistered = false;
|
|
|
45
45
|
/**
|
|
46
46
|
* Probe Ollama and register discovered models.
|
|
47
47
|
* Safe to call multiple times — re-discovers and re-registers.
|
|
48
|
+
*
|
|
49
|
+
* Exported for tests (see ollama-auth-mode.test.ts, ollama-status-indicator.test.ts)
|
|
50
|
+
* so a fake HTTP endpoint can drive the registration/unregistration paths.
|
|
51
|
+
* Production callers always go through the session_start handler below.
|
|
48
52
|
*/
|
|
49
|
-
async function probeAndRegister(pi: ExtensionAPI): Promise<boolean> {
|
|
53
|
+
export async function probeAndRegister(pi: ExtensionAPI): Promise<boolean> {
|
|
50
54
|
const running = await client.isRunning();
|
|
51
55
|
if (!running) {
|
|
52
56
|
if (providerRegistered) {
|