gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714
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 +1 -1
- package/dist/claude-cli-check.js +5 -1
- package/dist/headless.js +49 -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/claude-code-cli/readiness.js +5 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
- package/dist/resources/extensions/gsd/auto/loop.js +43 -0
- package/dist/resources/extensions/gsd/auto/phases.js +15 -21
- package/dist/resources/extensions/gsd/auto/session.js +0 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
- package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
- package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
- package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
- package/dist/resources/extensions/gsd/auto-start.js +34 -24
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
- package/dist/resources/extensions/gsd/auto.js +31 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
- 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/detection.js +49 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/gate-registry.js +2 -2
- package/dist/resources/extensions/gsd/git-constants.js +28 -1
- package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
- package/dist/resources/extensions/gsd/git-service.js +126 -2
- package/dist/resources/extensions/gsd/gsd-db.js +6 -3
- package/dist/resources/extensions/gsd/guided-flow.js +17 -5
- 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/native-git-bridge.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
- package/dist/resources/extensions/gsd/service-tier.js +5 -2
- package/dist/resources/extensions/gsd/session-lock.js +19 -10
- package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
- package/dist/resources/extensions/gsd/state.js +44 -33
- package/dist/resources/extensions/gsd/sync-lock.js +98 -42
- package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
- 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 +5 -5
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +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 +5 -5
- package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
- 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 +2 -3
- package/packages/daemon/src/logger.ts +4 -3
- package/packages/mcp-server/dist/server.d.ts +24 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +88 -87
- package/packages/mcp-server/dist/server.js.map +1 -1
- 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/secure-env-collect.test.ts +232 -237
- package/packages/mcp-server/src/server.ts +131 -105
- package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- 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/capability-patches.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/capability-patches.js +9 -2
- package/packages/pi-ai/dist/models/capability-patches.js.map +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 +36 -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/capability-patches.ts +10 -2
- 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 +48 -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/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/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
- 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 +125 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.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/dynamic-border.test.ts +26 -20
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
- 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/editor.d.ts +14 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +19 -0
- package/packages/pi-tui/dist/components/editor.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/dist/editor-component.d.ts +2 -0
- package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
- package/packages/pi-tui/dist/editor-component.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/editor.ts +22 -0
- package/packages/pi-tui/src/components/image.test.ts +10 -5
- package/packages/pi-tui/src/editor-component.ts +3 -0
- 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/readiness.ts +5 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
- 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/loop.ts +47 -0
- package/src/resources/extensions/gsd/auto/phases.ts +16 -20
- package/src/resources/extensions/gsd/auto/session.ts +0 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
- package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
- package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
- package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
- package/src/resources/extensions/gsd/auto-start.ts +54 -6
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
- package/src/resources/extensions/gsd/auto.ts +43 -22
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
- 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/detection.ts +58 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/gate-registry.ts +2 -2
- package/src/resources/extensions/gsd/git-constants.ts +30 -1
- package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
- package/src/resources/extensions/gsd/git-service.ts +133 -2
- package/src/resources/extensions/gsd/gsd-db.ts +6 -3
- package/src/resources/extensions/gsd/guided-flow.ts +20 -5
- 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/native-git-bridge.ts +34 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
- package/src/resources/extensions/gsd/service-tier.ts +5 -2
- package/src/resources/extensions/gsd/session-lock.ts +20 -10
- package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
- package/src/resources/extensions/gsd/state.ts +49 -44
- package/src/resources/extensions/gsd/sync-lock.ts +97 -39
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
- package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
- 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/crash-recovery.test.ts +50 -1
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
- package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
- 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 -57
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
- 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 +144 -7
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
- 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 -62
- 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 -49
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
- 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/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/session-lock-regression.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
- 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-parallel-orchestrator.test.ts +164 -1
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
- package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/tool-naming.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 +258 -0
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -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 -81
- 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/write-gate-planning-unit.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
- package/src/resources/extensions/gsd/types.ts +3 -3
- package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
- package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
- 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 -144
- 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 -75
- package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
- package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
- 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 -130
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// GSD2 + skill-manifest — per-unit-type skill allowlist resolver (RFC #4779)
|
|
2
|
+
//
|
|
3
|
+
// Each auto-mode unit type can declare which skills are relevant to it. This
|
|
4
|
+
// trims the set of skills considered for activation in the per-unit prompt,
|
|
5
|
+
// reducing prompt token bloat and sharpening model focus.
|
|
6
|
+
//
|
|
7
|
+
// Contract:
|
|
8
|
+
// - Unknown unit types fall through to "all skills" (current behavior).
|
|
9
|
+
// - A manifest entry referencing a skill that is not installed is a silent
|
|
10
|
+
// no-op at filter time — the filter passes through installed skills only.
|
|
11
|
+
// - The allowlist is an inclusion list: only skills whose normalized name
|
|
12
|
+
// appears in the allowlist are retained. Order is not preserved.
|
|
13
|
+
//
|
|
14
|
+
// Phase 1 scope: seed manifests for a small number of unit types as proof.
|
|
15
|
+
// Additional unit types can be added incrementally; each addition is a pure
|
|
16
|
+
// data change with no wiring cost.
|
|
17
|
+
import { logWarning } from "./workflow-logger.js";
|
|
18
|
+
/** Normalize a skill reference the same way callers do (lowercase, trim). */
|
|
19
|
+
function normalize(name) {
|
|
20
|
+
return name.trim().toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Allowlist per unit type. Keys match unit type identifiers used by auto-mode
|
|
24
|
+
* dispatch. Values are normalized skill names.
|
|
25
|
+
*
|
|
26
|
+
* Wildcard semantics: a unit type absent from this map resolves to `null`
|
|
27
|
+
* (wildcard) — meaning "all installed skills are eligible". Prefer absence
|
|
28
|
+
* over an exhaustive list when uncertain.
|
|
29
|
+
*/
|
|
30
|
+
const UNIT_TYPE_SKILL_MANIFEST = {
|
|
31
|
+
// Milestone-level planning / meta flows — predictable skill sets.
|
|
32
|
+
"research-milestone": [
|
|
33
|
+
"write-docs",
|
|
34
|
+
"write-milestone-brief",
|
|
35
|
+
"decompose-into-slices",
|
|
36
|
+
"grill-me",
|
|
37
|
+
"design-an-interface",
|
|
38
|
+
"api-design",
|
|
39
|
+
"observability",
|
|
40
|
+
],
|
|
41
|
+
"plan-milestone": [
|
|
42
|
+
"write-milestone-brief",
|
|
43
|
+
"decompose-into-slices",
|
|
44
|
+
"design-an-interface",
|
|
45
|
+
"grill-me",
|
|
46
|
+
"write-docs",
|
|
47
|
+
"api-design",
|
|
48
|
+
"tdd",
|
|
49
|
+
"verify-before-complete",
|
|
50
|
+
],
|
|
51
|
+
"complete-milestone": [
|
|
52
|
+
"verify-before-complete",
|
|
53
|
+
"write-docs",
|
|
54
|
+
"handoff",
|
|
55
|
+
"forensics",
|
|
56
|
+
"observability",
|
|
57
|
+
"security-review",
|
|
58
|
+
],
|
|
59
|
+
"validate-milestone": [
|
|
60
|
+
"verify-before-complete",
|
|
61
|
+
"review",
|
|
62
|
+
"test",
|
|
63
|
+
"lint",
|
|
64
|
+
"security-review",
|
|
65
|
+
"accessibility",
|
|
66
|
+
"forensics",
|
|
67
|
+
"observability",
|
|
68
|
+
],
|
|
69
|
+
"reassess-roadmap": [
|
|
70
|
+
"decompose-into-slices",
|
|
71
|
+
"grill-me",
|
|
72
|
+
"write-milestone-brief",
|
|
73
|
+
"write-docs",
|
|
74
|
+
"forensics",
|
|
75
|
+
],
|
|
76
|
+
// Slice-level research / planning.
|
|
77
|
+
"research-slice": [
|
|
78
|
+
"write-docs",
|
|
79
|
+
"decompose-into-slices",
|
|
80
|
+
"design-an-interface",
|
|
81
|
+
"grill-me",
|
|
82
|
+
"api-design",
|
|
83
|
+
"observability",
|
|
84
|
+
],
|
|
85
|
+
"plan-slice": [
|
|
86
|
+
"decompose-into-slices",
|
|
87
|
+
"design-an-interface",
|
|
88
|
+
"grill-me",
|
|
89
|
+
"write-docs",
|
|
90
|
+
"api-design",
|
|
91
|
+
"tdd",
|
|
92
|
+
"verify-before-complete",
|
|
93
|
+
],
|
|
94
|
+
"refine-slice": [
|
|
95
|
+
"decompose-into-slices",
|
|
96
|
+
"design-an-interface",
|
|
97
|
+
"grill-me",
|
|
98
|
+
"write-docs",
|
|
99
|
+
"api-design",
|
|
100
|
+
"tdd",
|
|
101
|
+
"verify-before-complete",
|
|
102
|
+
],
|
|
103
|
+
"replan-slice": [
|
|
104
|
+
"decompose-into-slices",
|
|
105
|
+
"grill-me",
|
|
106
|
+
"design-an-interface",
|
|
107
|
+
"write-docs",
|
|
108
|
+
"api-design",
|
|
109
|
+
],
|
|
110
|
+
"run-uat": [
|
|
111
|
+
"verify-before-complete",
|
|
112
|
+
"test",
|
|
113
|
+
"review",
|
|
114
|
+
"accessibility",
|
|
115
|
+
],
|
|
116
|
+
// `execute-task` intentionally omitted — implementation hot path covers a
|
|
117
|
+
// wide surface of technologies; wildcard fallback preserves today's
|
|
118
|
+
// behavior until per-task skill hints can be derived from task-plan
|
|
119
|
+
// frontmatter. See RFC #4779.
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the skill allowlist for a unit type.
|
|
123
|
+
*
|
|
124
|
+
* @returns Array of normalized skill names when an entry exists, or `null`
|
|
125
|
+
* when the unit type is unknown (wildcard — caller should not filter).
|
|
126
|
+
*/
|
|
127
|
+
export function resolveSkillManifest(unitType) {
|
|
128
|
+
if (!unitType)
|
|
129
|
+
return null;
|
|
130
|
+
const entry = UNIT_TYPE_SKILL_MANIFEST[unitType];
|
|
131
|
+
if (!entry)
|
|
132
|
+
return null;
|
|
133
|
+
return entry.map(normalize);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Filter a skill list by the manifest for `unitType`. Pass-through when the
|
|
137
|
+
* manifest is wildcard (unknown unit type) or `unitType` is undefined.
|
|
138
|
+
*/
|
|
139
|
+
export function filterSkillsByManifest(skills, unitType) {
|
|
140
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
141
|
+
if (allowlist === null)
|
|
142
|
+
return skills;
|
|
143
|
+
const allowed = new Set(allowlist);
|
|
144
|
+
return skills.filter(skill => allowed.has(normalize(skill.name)));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Dev-mode guard: warn once per process if a manifest entry references a name
|
|
148
|
+
* that is not currently installed. Silent in production.
|
|
149
|
+
*/
|
|
150
|
+
const warnedMissing = new Set();
|
|
151
|
+
export function warnIfManifestHasMissingSkills(unitType, installedNames) {
|
|
152
|
+
// Strict mode is intentionally opt-in via exactly "1"; values like "0" or
|
|
153
|
+
// "false" must preserve the normal silent manifest behavior.
|
|
154
|
+
if (process.env.GSD_SKILL_MANIFEST_STRICT !== "1")
|
|
155
|
+
return;
|
|
156
|
+
const allowlist = resolveSkillManifest(unitType);
|
|
157
|
+
if (!allowlist)
|
|
158
|
+
return;
|
|
159
|
+
for (const name of allowlist) {
|
|
160
|
+
const key = `${unitType}:${name}`;
|
|
161
|
+
if (warnedMissing.has(key))
|
|
162
|
+
continue;
|
|
163
|
+
if (!installedNames.has(name)) {
|
|
164
|
+
warnedMissing.add(key);
|
|
165
|
+
logWarning("prompt", `skill-manifest: references uninstalled skill '${name}' for unit '${unitType}'`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* - Lock env: GSD_SLICE_LOCK (in addition to GSD_MILESTONE_LOCK)
|
|
13
13
|
* - Conflict check: file overlap between slice plans (slice-parallel-conflict.ts)
|
|
14
14
|
*/
|
|
15
|
-
import { spawn } from "node:child_process";
|
|
16
|
-
import {
|
|
15
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
import { appendFileSync, existsSync, writeFileSync, readFileSync, mkdirSync, renameSync, unlinkSync, } from "node:fs";
|
|
17
18
|
import { join, dirname } from "node:path";
|
|
18
19
|
import { fileURLToPath } from "node:url";
|
|
19
20
|
import { gsdRoot } from "./paths.js";
|
|
@@ -24,12 +25,243 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
24
25
|
import { selectConflictFreeBatch } from "./uok/execution-graph.js";
|
|
25
26
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
26
27
|
let sliceState = null;
|
|
28
|
+
// ─── Persisted State (crash recovery) ──────────────────────────────────────
|
|
29
|
+
//
|
|
30
|
+
// Mirrors parallel-orchestrator.ts. Without persistence, a coordinator crash
|
|
31
|
+
// leaves orphaned worktrees on disk with no way to detect or clean them up
|
|
32
|
+
// on next session start. (Issue #4980 HIGH-8)
|
|
33
|
+
const SLICE_ORCHESTRATOR_STATE_FILE = "slice-orchestrator.json";
|
|
34
|
+
const TMP_SUFFIX = ".tmp";
|
|
35
|
+
function sliceStateFilePath(basePath) {
|
|
36
|
+
return join(gsdRoot(basePath), SLICE_ORCHESTRATOR_STATE_FILE);
|
|
37
|
+
}
|
|
38
|
+
function isPidAlive(pid) {
|
|
39
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
40
|
+
return false;
|
|
41
|
+
try {
|
|
42
|
+
process.kill(pid, 0);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err.code === "EPERM")
|
|
47
|
+
return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function readLinuxProcessStartFingerprint(pid) {
|
|
52
|
+
try {
|
|
53
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf-8");
|
|
54
|
+
const afterCommand = stat.slice(stat.lastIndexOf(")") + 2).trim();
|
|
55
|
+
const fields = afterCommand.split(/\s+/);
|
|
56
|
+
const startTimeTicks = fields[19];
|
|
57
|
+
return startTimeTicks ? `linux-stat:${startTimeTicks}` : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function readPsProcessStartFingerprint(pid) {
|
|
64
|
+
try {
|
|
65
|
+
const raw = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
66
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
67
|
+
encoding: "utf-8",
|
|
68
|
+
}).trim().replace(/\s+/g, " ");
|
|
69
|
+
return raw ? `ps-lstart:${raw}` : null;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function readProcessStartFingerprint(pid) {
|
|
76
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
77
|
+
return null;
|
|
78
|
+
return readLinuxProcessStartFingerprint(pid) ?? readPsProcessStartFingerprint(pid);
|
|
79
|
+
}
|
|
80
|
+
function linuxProcessEnvContains(pid, key, value) {
|
|
81
|
+
if (process.platform !== "linux")
|
|
82
|
+
return null;
|
|
83
|
+
try {
|
|
84
|
+
const env = readFileSync(`/proc/${pid}/environ`, "utf-8");
|
|
85
|
+
return env.split("\0").includes(`${key}=${value}`);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function createWorkerToken(milestoneId, sliceId) {
|
|
92
|
+
return `slice:${milestoneId}:${sliceId}:${Date.now()}:${randomUUID()}`;
|
|
93
|
+
}
|
|
94
|
+
function isRecoveredSliceWorkerAlive(worker) {
|
|
95
|
+
if (!isPidAlive(worker.pid))
|
|
96
|
+
return false;
|
|
97
|
+
if (!worker.processStartFingerprint)
|
|
98
|
+
return false;
|
|
99
|
+
const currentFingerprint = readProcessStartFingerprint(worker.pid);
|
|
100
|
+
if (!currentFingerprint || currentFingerprint !== worker.processStartFingerprint) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
if (worker.workerToken) {
|
|
104
|
+
const envMatches = linuxProcessEnvContains(worker.pid, "GSD_SLICE_WORKER_TOKEN", worker.workerToken);
|
|
105
|
+
if (envMatches === false)
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Persist current slice orchestrator state. Atomic write (tmp + rename) to
|
|
112
|
+
* prevent partial reads if the coordinator dies mid-write.
|
|
113
|
+
*/
|
|
114
|
+
function persistSliceState() {
|
|
115
|
+
if (!sliceState)
|
|
116
|
+
return;
|
|
117
|
+
try {
|
|
118
|
+
const dir = gsdRoot(sliceState.basePath);
|
|
119
|
+
if (!existsSync(dir))
|
|
120
|
+
mkdirSync(dir, { recursive: true });
|
|
121
|
+
const persisted = {
|
|
122
|
+
active: sliceState.active,
|
|
123
|
+
workers: [...sliceState.workers.values()].map((w) => ({
|
|
124
|
+
milestoneId: w.milestoneId,
|
|
125
|
+
sliceId: w.sliceId,
|
|
126
|
+
pid: w.pid,
|
|
127
|
+
workerToken: w.workerToken,
|
|
128
|
+
processStartFingerprint: w.processStartFingerprint,
|
|
129
|
+
worktreePath: w.worktreePath,
|
|
130
|
+
startedAt: w.startedAt,
|
|
131
|
+
state: w.state,
|
|
132
|
+
completedUnits: w.completedUnits,
|
|
133
|
+
cost: w.cost,
|
|
134
|
+
})),
|
|
135
|
+
totalCost: sliceState.totalCost,
|
|
136
|
+
budgetCeiling: sliceState.budgetCeiling,
|
|
137
|
+
maxWorkers: sliceState.maxWorkers,
|
|
138
|
+
startedAt: sliceState.startedAt,
|
|
139
|
+
basePath: sliceState.basePath,
|
|
140
|
+
};
|
|
141
|
+
const dest = sliceStateFilePath(sliceState.basePath);
|
|
142
|
+
const tmp = dest + TMP_SUFFIX;
|
|
143
|
+
writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
|
|
144
|
+
renameSync(tmp, dest);
|
|
145
|
+
lastPersistTs = Date.now();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* non-fatal: persistence is best-effort */
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Throttled wrapper around `persistSliceState`. Skips if the last successful
|
|
153
|
+
* persist was less than `PERSIST_THROTTLE_MS` ago; otherwise persists
|
|
154
|
+
* immediately. Use this on hot paths (e.g. `message_end` events) where we
|
|
155
|
+
* receive many events per second per worker. Terminal events (worker exit,
|
|
156
|
+
* crash, stop) should call `persistSliceState()` directly to guarantee the
|
|
157
|
+
* final state hits disk regardless of timing.
|
|
158
|
+
*/
|
|
159
|
+
const PERSIST_THROTTLE_MS = 1000;
|
|
160
|
+
let lastPersistTs = 0;
|
|
161
|
+
function persistSliceStateThrottled() {
|
|
162
|
+
if (Date.now() - lastPersistTs < PERSIST_THROTTLE_MS)
|
|
163
|
+
return;
|
|
164
|
+
persistSliceState();
|
|
165
|
+
}
|
|
166
|
+
function removeSliceStateFile(basePath) {
|
|
167
|
+
try {
|
|
168
|
+
const p = sliceStateFilePath(basePath);
|
|
169
|
+
if (existsSync(p))
|
|
170
|
+
unlinkSync(p);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
/* non-fatal */
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Restore slice orchestrator state from disk. Filters dead-PID workers and
|
|
178
|
+
* removes their orphaned worktrees so a clean restart is possible.
|
|
179
|
+
*
|
|
180
|
+
* Returns null if no state file exists or no workers survive.
|
|
181
|
+
*/
|
|
182
|
+
export function restoreSliceState(basePath) {
|
|
183
|
+
try {
|
|
184
|
+
const p = sliceStateFilePath(basePath);
|
|
185
|
+
if (!existsSync(p))
|
|
186
|
+
return null;
|
|
187
|
+
const persisted = JSON.parse(readFileSync(p, "utf-8"));
|
|
188
|
+
const survivors = [];
|
|
189
|
+
const dead = [];
|
|
190
|
+
for (const w of persisted.workers) {
|
|
191
|
+
if (w.state === "running" && isRecoveredSliceWorkerAlive(w)) {
|
|
192
|
+
survivors.push(w);
|
|
193
|
+
}
|
|
194
|
+
else if (w.state === "running") {
|
|
195
|
+
dead.push(w);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
survivors.push(w);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Best-effort cleanup of orphaned worktrees from dead workers.
|
|
202
|
+
for (const w of dead) {
|
|
203
|
+
const wtName = `${w.milestoneId}-${w.sliceId}`;
|
|
204
|
+
try {
|
|
205
|
+
removeWorktree(persisted.basePath, wtName, { deleteBranch: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
/* worktree may already be gone */
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
persisted.workers = survivors;
|
|
212
|
+
if (survivors.length === 0) {
|
|
213
|
+
removeSliceStateFile(basePath);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return persisted;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
27
222
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
28
223
|
/**
|
|
29
224
|
* Check whether slice-level parallel is currently active.
|
|
225
|
+
*
|
|
226
|
+
* If in-memory state is unset but a persisted state file exists with at
|
|
227
|
+
* least one live-PID worker, treat as active and rehydrate so a coordinator
|
|
228
|
+
* crash followed by a fresh process is detectable. (Issue #4980 HIGH-8)
|
|
30
229
|
*/
|
|
31
|
-
export function isSliceParallelActive() {
|
|
32
|
-
|
|
230
|
+
export function isSliceParallelActive(basePath) {
|
|
231
|
+
if (sliceState?.active === true)
|
|
232
|
+
return true;
|
|
233
|
+
if (!basePath)
|
|
234
|
+
return false;
|
|
235
|
+
const restored = restoreSliceState(basePath);
|
|
236
|
+
if (!restored || restored.workers.length === 0)
|
|
237
|
+
return false;
|
|
238
|
+
// Rehydrate in-memory state from disk; processes are detached so we have
|
|
239
|
+
// no ChildProcess handles, only PIDs.
|
|
240
|
+
sliceState = {
|
|
241
|
+
active: restored.active,
|
|
242
|
+
workers: new Map(),
|
|
243
|
+
totalCost: restored.totalCost,
|
|
244
|
+
budgetCeiling: restored.budgetCeiling,
|
|
245
|
+
maxWorkers: restored.maxWorkers,
|
|
246
|
+
startedAt: restored.startedAt,
|
|
247
|
+
basePath: restored.basePath,
|
|
248
|
+
};
|
|
249
|
+
for (const w of restored.workers) {
|
|
250
|
+
sliceState.workers.set(w.sliceId, {
|
|
251
|
+
milestoneId: w.milestoneId,
|
|
252
|
+
sliceId: w.sliceId,
|
|
253
|
+
pid: w.pid,
|
|
254
|
+
process: null,
|
|
255
|
+
worktreePath: w.worktreePath,
|
|
256
|
+
workerToken: w.workerToken ?? "",
|
|
257
|
+
processStartFingerprint: w.processStartFingerprint ?? null,
|
|
258
|
+
startedAt: w.startedAt,
|
|
259
|
+
state: w.state,
|
|
260
|
+
completedUnits: w.completedUnits,
|
|
261
|
+
cost: w.cost,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
33
265
|
}
|
|
34
266
|
/**
|
|
35
267
|
* Get current slice orchestrator state (read-only snapshot).
|
|
@@ -80,6 +312,8 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
|
|
|
80
312
|
milestoneId,
|
|
81
313
|
sliceId: slice.id,
|
|
82
314
|
pid: 0,
|
|
315
|
+
workerToken: createWorkerToken(milestoneId, slice.id),
|
|
316
|
+
processStartFingerprint: null,
|
|
83
317
|
process: null,
|
|
84
318
|
worktreePath: wtPath,
|
|
85
319
|
startedAt: Date.now(),
|
|
@@ -95,13 +329,18 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
|
|
|
95
329
|
}
|
|
96
330
|
else {
|
|
97
331
|
errors.push({ sid: slice.id, error: "Failed to spawn worker process" });
|
|
98
|
-
|
|
332
|
+
sliceState.workers.delete(slice.id);
|
|
333
|
+
try {
|
|
334
|
+
removeWorktree(basePath, wtName, { deleteBranch: true, force: true });
|
|
335
|
+
}
|
|
336
|
+
catch { /* ignore cleanup failures */ }
|
|
99
337
|
}
|
|
100
338
|
}
|
|
101
339
|
catch (err) {
|
|
102
340
|
errors.push({ sid: slice.id, error: getErrorMessage(err) });
|
|
103
341
|
// Best-effort cleanup of partially created worktree
|
|
104
342
|
const wtName = `${milestoneId}-${slice.id}`;
|
|
343
|
+
sliceState.workers.delete(slice.id);
|
|
105
344
|
try {
|
|
106
345
|
removeWorktree(basePath, wtName, { deleteBranch: true, force: true });
|
|
107
346
|
}
|
|
@@ -111,6 +350,11 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
|
|
|
111
350
|
// If nothing started, deactivate
|
|
112
351
|
if (started.length === 0) {
|
|
113
352
|
sliceState.active = false;
|
|
353
|
+
removeSliceStateFile(basePath);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
// Persist state for crash recovery (Issue #4980 HIGH-8).
|
|
357
|
+
persistSliceState();
|
|
114
358
|
}
|
|
115
359
|
return { started, errors };
|
|
116
360
|
}
|
|
@@ -120,13 +364,17 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
|
|
|
120
364
|
export function stopSliceParallel() {
|
|
121
365
|
if (!sliceState)
|
|
122
366
|
return;
|
|
367
|
+
const basePath = sliceState.basePath;
|
|
123
368
|
for (const worker of sliceState.workers.values()) {
|
|
124
|
-
|
|
125
|
-
|
|
369
|
+
try {
|
|
370
|
+
if (worker.process) {
|
|
126
371
|
worker.process.kill("SIGTERM");
|
|
127
372
|
}
|
|
128
|
-
|
|
373
|
+
else if (worker.state === "running" && isRecoveredSliceWorkerAlive(worker)) {
|
|
374
|
+
process.kill(worker.pid, "SIGTERM");
|
|
375
|
+
}
|
|
129
376
|
}
|
|
377
|
+
catch { /* already dead */ }
|
|
130
378
|
worker.cleanup?.();
|
|
131
379
|
worker.cleanup = undefined;
|
|
132
380
|
worker.process = null;
|
|
@@ -139,6 +387,9 @@ export function stopSliceParallel() {
|
|
|
139
387
|
catch { /* best-effort cleanup */ }
|
|
140
388
|
}
|
|
141
389
|
sliceState.active = false;
|
|
390
|
+
// Clear persisted state — clean shutdown means no recovery on next start.
|
|
391
|
+
// (Issue #4980 HIGH-8)
|
|
392
|
+
removeSliceStateFile(basePath);
|
|
142
393
|
}
|
|
143
394
|
/**
|
|
144
395
|
* Get aggregate cost across all slice workers.
|
|
@@ -170,6 +421,7 @@ export function resetSliceOrchestrator() {
|
|
|
170
421
|
}
|
|
171
422
|
}
|
|
172
423
|
sliceState = null;
|
|
424
|
+
lastPersistTs = 0;
|
|
173
425
|
}
|
|
174
426
|
// ─── Internal: Conflict Filtering ──────────────────────────────────────────
|
|
175
427
|
/**
|
|
@@ -254,6 +506,7 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
|
|
|
254
506
|
GSD_MILESTONE_LOCK: milestoneId,
|
|
255
507
|
GSD_PROJECT_ROOT: basePath,
|
|
256
508
|
GSD_PARALLEL_WORKER: "1",
|
|
509
|
+
GSD_SLICE_WORKER_TOKEN: worker.workerToken,
|
|
257
510
|
},
|
|
258
511
|
stdio: ["ignore", "pipe", "pipe"],
|
|
259
512
|
detached: false,
|
|
@@ -272,8 +525,17 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
|
|
|
272
525
|
});
|
|
273
526
|
worker.process = child;
|
|
274
527
|
worker.pid = child.pid ?? 0;
|
|
528
|
+
worker.processStartFingerprint = worker.pid > 0
|
|
529
|
+
? readProcessStartFingerprint(worker.pid)
|
|
530
|
+
: null;
|
|
275
531
|
if (!child.pid) {
|
|
276
532
|
worker.process = null;
|
|
533
|
+
worker.pid = 0;
|
|
534
|
+
worker.processStartFingerprint = null;
|
|
535
|
+
try {
|
|
536
|
+
child.kill("SIGTERM");
|
|
537
|
+
}
|
|
538
|
+
catch { /* best-effort */ }
|
|
277
539
|
return false;
|
|
278
540
|
}
|
|
279
541
|
// ── NDJSON stdout monitoring ────────────────────────────────────────
|
|
@@ -346,6 +608,9 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
|
|
|
346
608
|
startedAt: w.startedAt,
|
|
347
609
|
worktreePath: w.worktreePath,
|
|
348
610
|
});
|
|
611
|
+
// Persist worker terminal state for crash recovery.
|
|
612
|
+
// (Issue #4980 HIGH-8)
|
|
613
|
+
persistSliceState();
|
|
349
614
|
});
|
|
350
615
|
return true;
|
|
351
616
|
}
|
|
@@ -374,6 +639,11 @@ function processSliceWorkerLine(_basePath, _milestoneId, sliceId, line) {
|
|
|
374
639
|
sliceState.totalCost += usage.cost;
|
|
375
640
|
}
|
|
376
641
|
worker.completedUnits++;
|
|
642
|
+
// Persist cost / progress updates so a crash mid-run preserves them.
|
|
643
|
+
// Throttled (~1/s per process) so high-frequency message_end traffic
|
|
644
|
+
// does not saturate disk I/O. Worker exit / start / stop paths persist
|
|
645
|
+
// unthrottled to guarantee the terminal state lands. (Issue #4980 HIGH-8)
|
|
646
|
+
persistSliceStateThrottled();
|
|
377
647
|
}
|
|
378
648
|
}
|
|
379
649
|
}
|