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
|
@@ -1,42 +1,107 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Regression test for #3616: DefaultResourceLoader.reload() must invalidate
|
|
2
|
+
// the jiti module cache before loading extensions, so that edits to
|
|
3
|
+
// extension source on disk are picked up on the next reload (not served
|
|
4
|
+
// stale from memory).
|
|
5
|
+
//
|
|
6
|
+
// Verified end-to-end behaviourally: we write a .ts extension that
|
|
7
|
+
// registers a tool whose NAME is a module-scope constant, load it,
|
|
8
|
+
// rewrite the source with a new constant value, reload(), and assert
|
|
9
|
+
// the observable tool name reflects the NEW source. Without jiti cache
|
|
10
|
+
// invalidation, the second reload would re-register the stale name.
|
|
3
11
|
|
|
4
|
-
import test, { describe } from "node:test";
|
|
5
12
|
import assert from "node:assert/strict";
|
|
6
|
-
import {
|
|
13
|
+
import { mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
7
15
|
import { join } from "node:path";
|
|
16
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
18
|
+
import { DefaultResourceLoader } from "./resource-loader.js";
|
|
19
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
20
|
+
import { resetExtensionLoaderCache } from "./extensions/loader.js";
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
let testDir: string;
|
|
23
|
+
|
|
24
|
+
function writeExtensionWithToolName(extPath: string, toolName: string): void {
|
|
25
|
+
// Extension factory signature expected by the loader. Uses `any` so the
|
|
26
|
+
// test stays decoupled from the ExtensionAPI type shape.
|
|
27
|
+
writeFileSync(
|
|
28
|
+
extPath,
|
|
29
|
+
[
|
|
30
|
+
`const TOOL_NAME = "${toolName}";`,
|
|
31
|
+
`export default function activate(api: any) {`,
|
|
32
|
+
` api.registerTool({`,
|
|
33
|
+
` name: TOOL_NAME,`,
|
|
34
|
+
` label: TOOL_NAME,`,
|
|
35
|
+
` description: "test tool — source-generated name",`,
|
|
36
|
+
` parameters: { type: "object", properties: {}, additionalProperties: false },`,
|
|
37
|
+
` execute: async () => ({ content: [], details: undefined }),`,
|
|
38
|
+
` });`,
|
|
39
|
+
`}`,
|
|
40
|
+
"",
|
|
41
|
+
].join("\n"),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("#3616 — DefaultResourceLoader.reload() invalidates extension module cache", () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
testDir = mkdtempSync(join(tmpdir(), "resource-loader-cache-reset-"));
|
|
48
|
+
// Ensure a clean jiti singleton — prior tests in this process may
|
|
49
|
+
// have populated it with unrelated entries.
|
|
50
|
+
resetExtensionLoaderCache();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
resetExtensionLoaderCache();
|
|
55
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
24
56
|
});
|
|
25
57
|
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const reloadBody = source.slice(reloadStart, reloadStart + 4000);
|
|
58
|
+
it("reload() picks up source edits after the extension has been loaded once", async () => {
|
|
59
|
+
const agentDir = join(testDir, "agent-home");
|
|
60
|
+
const extPath = join(testDir, "reload-probe.ts");
|
|
30
61
|
|
|
31
|
-
|
|
32
|
-
|
|
62
|
+
// v1 — initial content
|
|
63
|
+
writeExtensionWithToolName(extPath, "probe_v1");
|
|
33
64
|
|
|
34
|
-
const
|
|
35
|
-
|
|
65
|
+
const loader = new DefaultResourceLoader({
|
|
66
|
+
cwd: testDir,
|
|
67
|
+
agentDir,
|
|
68
|
+
settingsManager: SettingsManager.inMemory(),
|
|
69
|
+
noExtensions: true,
|
|
70
|
+
noPromptTemplates: true,
|
|
71
|
+
noThemes: true,
|
|
72
|
+
additionalExtensionPaths: [extPath],
|
|
73
|
+
});
|
|
36
74
|
|
|
75
|
+
await loader.reload();
|
|
76
|
+
|
|
77
|
+
const toolsV1 = [...loader.getExtensions().extensions.flatMap((e) => [...e.tools.keys()])];
|
|
78
|
+
assert.ok(
|
|
79
|
+
toolsV1.includes("probe_v1"),
|
|
80
|
+
`first reload should register probe_v1; got=${JSON.stringify(toolsV1)}`,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// v2 — overwrite the source on disk with a different tool name.
|
|
84
|
+
writeExtensionWithToolName(extPath, "probe_v2");
|
|
85
|
+
// Bump mtime explicitly so file-stat–based cache strategies (jiti's
|
|
86
|
+
// moduleCache consults Node's require.cache, which some runtimes
|
|
87
|
+
// tiebreak on mtime) always see "newer than last seen". On Windows
|
|
88
|
+
// NTFS + node the default fs mtime resolution can silently collapse
|
|
89
|
+
// two writes in the same tick to an identical mtime — that fooled
|
|
90
|
+
// the Windows CI run on #3616.
|
|
91
|
+
const futureTime = new Date(Date.now() + 5000);
|
|
92
|
+
utimesSync(extPath, futureTime, futureTime);
|
|
93
|
+
|
|
94
|
+
await loader.reload();
|
|
95
|
+
|
|
96
|
+
const toolsV2 = [...loader.getExtensions().extensions.flatMap((e) => [...e.tools.keys()])];
|
|
97
|
+
assert.ok(
|
|
98
|
+
toolsV2.includes("probe_v2"),
|
|
99
|
+
`second reload must observe the edited source (probe_v2) — if reload() ` +
|
|
100
|
+
`fails to reset the jiti cache, the stale module returns probe_v1. got=${JSON.stringify(toolsV2)}`,
|
|
101
|
+
);
|
|
37
102
|
assert.ok(
|
|
38
|
-
|
|
39
|
-
|
|
103
|
+
!toolsV2.includes("probe_v1"),
|
|
104
|
+
`second reload must NOT still expose the stale probe_v1 name; got=${JSON.stringify(toolsV2)}`,
|
|
40
105
|
);
|
|
41
106
|
});
|
|
42
107
|
});
|
|
@@ -281,7 +281,11 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|
|
281
281
|
assert.equal(result, true, "retry should be initiated");
|
|
282
282
|
|
|
283
283
|
handler.abortRetry();
|
|
284
|
-
|
|
284
|
+
// Yield the microtask queue so any synchronous continuation
|
|
285
|
+
// scheduled by abortRetry() settles before we assert. This is
|
|
286
|
+
// deterministic — no magic-sleep dependency (#4798 / #4784).
|
|
287
|
+
await Promise.resolve();
|
|
288
|
+
await Promise.resolve();
|
|
285
289
|
|
|
286
290
|
assert.equal(continueFn.mock.calls.length, 0, "cancelled retry must not continue after explicit abort");
|
|
287
291
|
const endEvents = emittedEvents.filter((e) => e.type === "auto_retry_end");
|
|
@@ -18,6 +18,7 @@ import type { ModelRegistry } from "./model-registry.js";
|
|
|
18
18
|
import type { SettingsManager } from "./settings-manager.js";
|
|
19
19
|
import { sleep } from "../utils/sleep.js";
|
|
20
20
|
import type { AgentSessionEvent } from "./agent-session.js";
|
|
21
|
+
import { RETRYABLE_ERROR_RE } from "./retryable-error-regex.js";
|
|
21
22
|
|
|
22
23
|
/** Dependencies injected from AgentSession into RetryHandler */
|
|
23
24
|
export interface RetryHandlerDeps {
|
|
@@ -111,14 +112,7 @@ export class RetryHandler {
|
|
|
111
112
|
const contextWindow = this._deps.getModel()?.contextWindow ?? 0;
|
|
112
113
|
if (isContextOverflow(message, contextWindow)) return false;
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
// "temporarily backed off" is intentionally excluded: it is an internally-
|
|
116
|
-
// generated error from getApiKey() when credentials are in a backoff window.
|
|
117
|
-
// Re-entering the retry handler for that message creates a cascade of empty
|
|
118
|
-
// error entries in the session file, breaking resume (#3429).
|
|
119
|
-
return /overloaded|rate.?limit|too many requests|402|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|network.?(?:is\s+)?unavailable|credentials.*expired|requires more credits|can only afford|insufficient credits|not enough credits|extra usage is required|(?:out of|no) extra usage|third.party.*draw from extra|third.party.*not.*available/i.test(
|
|
120
|
-
err,
|
|
121
|
-
);
|
|
115
|
+
return RETRYABLE_ERROR_RE.test(message.errorMessage);
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regex matching retryable provider errors — overloaded, rate limits, transient
|
|
3
|
+
* server/connection failures, credential-backoff, quota/credit issues.
|
|
4
|
+
*
|
|
5
|
+
* Kept in its own zero-import module so tests can consume the live pattern
|
|
6
|
+
* without pulling in the full RetryHandler dependency graph (Agent /
|
|
7
|
+
* FallbackResolver / ModelRegistry / @gsd/pi-ai …). The test in
|
|
8
|
+
* `src/resources/extensions/gsd/tests/provider-errors.test.ts` previously
|
|
9
|
+
* redefined this regex inline, which meant runtime and test could drift
|
|
10
|
+
* silently on every edit (see #4837).
|
|
11
|
+
*
|
|
12
|
+
* "temporarily backed off" is intentionally excluded: it is an internally-
|
|
13
|
+
* generated error from getApiKey() when credentials are in a backoff window.
|
|
14
|
+
* Re-entering the retry handler for that message creates a cascade of empty
|
|
15
|
+
* error entries in the session file, breaking resume (#3429).
|
|
16
|
+
*/
|
|
17
|
+
export const RETRYABLE_ERROR_RE =
|
|
18
|
+
/overloaded|rate.?limit|too many requests|402|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|network.?(?:is\s+)?unavailable|credentials.*expired|requires more credits|can only afford|insufficient credits|not enough credits|extra usage is required|(?:out of|no) extra usage|third.party.*draw from extra|third.party.*not.*available/i;
|
|
@@ -35,6 +35,26 @@ export interface BuildSystemPromptOptions {
|
|
|
35
35
|
contextFiles?: Array<{ path: string; content: string }>;
|
|
36
36
|
/** Pre-loaded skills. */
|
|
37
37
|
skills?: Skill[];
|
|
38
|
+
/**
|
|
39
|
+
* Optional predicate applied to the `skills` list before rendering the
|
|
40
|
+
* <available_skills> catalog. Returning `false` omits a skill from the
|
|
41
|
+
* prompt (the skill remains loaded and invocable by name — only the
|
|
42
|
+
* catalog listing is suppressed).
|
|
43
|
+
*
|
|
44
|
+
* Intended for consumers that can narrow the relevant skill surface
|
|
45
|
+
* (e.g. per-unit-type manifests) to reduce cached system-prompt bloat.
|
|
46
|
+
* When omitted, all non-`disableModelInvocation` skills render — i.e.
|
|
47
|
+
* behavior is unchanged from before this option existed.
|
|
48
|
+
*
|
|
49
|
+
* Contract: the predicate must be **pure and synchronous**. It may be
|
|
50
|
+
* invoked on every system-prompt rebuild (tool-set changes and
|
|
51
|
+
* runtime resource-loader extensions both trigger one), so any state
|
|
52
|
+
* the closure captures should be stable across the rebuild window.
|
|
53
|
+
* If the predicate throws, `buildSystemPrompt` logs a warning and
|
|
54
|
+
* falls back to the unfiltered skill list — callers never see the
|
|
55
|
+
* exception and the session stays consistent.
|
|
56
|
+
*/
|
|
57
|
+
skillFilter?: (skill: Skill) => boolean;
|
|
38
58
|
}
|
|
39
59
|
|
|
40
60
|
/** Build the system prompt with tools, guidelines, and context */
|
|
@@ -48,6 +68,7 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|
|
48
68
|
cwd,
|
|
49
69
|
contextFiles: providedContextFiles,
|
|
50
70
|
skills: providedSkills,
|
|
71
|
+
skillFilter,
|
|
51
72
|
} = options;
|
|
52
73
|
const resolvedCwd = toPosixPath(cwd ?? process.cwd());
|
|
53
74
|
|
|
@@ -66,7 +87,20 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|
|
66
87
|
const appendSection = appendSystemPrompt ? `\n\n${appendSystemPrompt}` : "";
|
|
67
88
|
|
|
68
89
|
const contextFiles = providedContextFiles ?? [];
|
|
69
|
-
const
|
|
90
|
+
const skillsBase = providedSkills ?? [];
|
|
91
|
+
let skills = skillsBase;
|
|
92
|
+
if (skillFilter) {
|
|
93
|
+
try {
|
|
94
|
+
skills = skillsBase.filter(skillFilter);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// A consumer's predicate threw. Fall back to the unfiltered list so
|
|
97
|
+
// the session stays consistent — callers (e.g. AgentSession.setTools)
|
|
98
|
+
// must not be left with updated tools but a stale system prompt.
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
console.warn(`buildSystemPrompt: skillFilter threw; falling back to unfiltered skills. Error: ${message}`);
|
|
101
|
+
skills = skillsBase;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
70
104
|
|
|
71
105
|
if (customPrompt) {
|
|
72
106
|
let prompt = customPrompt;
|
|
@@ -188,6 +188,7 @@ export type { PackageCommand, PackageCommandOptions, PackageCommandRunnerOptions
|
|
|
188
188
|
export { getPackageCommandUsage, parsePackageCommand, runPackageCommand } from "./core/package-commands.js";
|
|
189
189
|
export type { ResourceCollision, ResourceDiagnostic, ResourceLoader } from "./core/resource-loader.js";
|
|
190
190
|
export { DefaultResourceLoader } from "./core/resource-loader.js";
|
|
191
|
+
export { RETRYABLE_ERROR_RE } from "./core/retryable-error-regex.js";
|
|
191
192
|
// SDK for programmatic usage
|
|
192
193
|
export {
|
|
193
194
|
type CreateAgentSessionOptions,
|
package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts
CHANGED
|
@@ -14,12 +14,13 @@ function renderTool(
|
|
|
14
14
|
isError: boolean;
|
|
15
15
|
details?: Record<string, unknown>;
|
|
16
16
|
},
|
|
17
|
+
toolDefinition?: { label?: string },
|
|
17
18
|
): string {
|
|
18
19
|
const component = new ToolExecutionComponent(
|
|
19
20
|
toolName,
|
|
20
21
|
args,
|
|
21
22
|
{},
|
|
22
|
-
|
|
23
|
+
toolDefinition as any,
|
|
23
24
|
{ requestRender() {} } as any,
|
|
24
25
|
);
|
|
25
26
|
component.setExpanded(true);
|
|
@@ -35,12 +36,13 @@ function renderToolCollapsed(
|
|
|
35
36
|
isError: boolean;
|
|
36
37
|
details?: Record<string, unknown>;
|
|
37
38
|
},
|
|
39
|
+
toolDefinition?: { label?: string },
|
|
38
40
|
): string {
|
|
39
41
|
const component = new ToolExecutionComponent(
|
|
40
42
|
toolName,
|
|
41
43
|
args,
|
|
42
44
|
{},
|
|
43
|
-
|
|
45
|
+
toolDefinition as any,
|
|
44
46
|
{ requestRender() {} } as any,
|
|
45
47
|
);
|
|
46
48
|
if (result) component.updateResult(result);
|
|
@@ -110,13 +112,57 @@ describe("ToolExecutionComponent", () => {
|
|
|
110
112
|
{ count: 3, enabled: true, label: "hello" },
|
|
111
113
|
);
|
|
112
114
|
|
|
113
|
-
assert.match(rendered, /
|
|
115
|
+
assert.match(rendered, /Some Unknown Tool/);
|
|
114
116
|
assert.match(rendered, /count=3/);
|
|
115
117
|
assert.match(rendered, /enabled=true/);
|
|
116
118
|
assert.match(rendered, /label="hello"/);
|
|
117
119
|
assert.doesNotMatch(rendered, /^\{$/m);
|
|
118
120
|
});
|
|
119
121
|
|
|
122
|
+
test("frame header prefers toolDefinition.label over raw tool name", () => {
|
|
123
|
+
const rendered = renderToolCollapsed(
|
|
124
|
+
"gsd_slice_complete",
|
|
125
|
+
{ sliceId: "S03" },
|
|
126
|
+
undefined,
|
|
127
|
+
{ label: "Complete Slice" },
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
assert.match(rendered, /Tool Complete Slice/);
|
|
131
|
+
assert.doesNotMatch(rendered, /gsd_slice_complete/);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("frame header strips gsd_ prefix and title-cases when no label is registered", () => {
|
|
135
|
+
const rendered = renderToolCollapsed("gsd_requirement_update", { id: "R005" });
|
|
136
|
+
|
|
137
|
+
assert.match(rendered, /Tool Requirement Update/);
|
|
138
|
+
assert.doesNotMatch(rendered, /gsd_requirement_update/);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("formatCompactArgs truncates long string values inline instead of dumping JSON", () => {
|
|
142
|
+
const longPath = "/Users/alice/.gsd/projects/4dce7b775013/worktrees/slice-S03-some-long-path-that-exceeds-limit";
|
|
143
|
+
const rendered = renderToolCollapsed("gsd_slice_complete", {
|
|
144
|
+
sliceId: "S03",
|
|
145
|
+
milestoneId: "M001",
|
|
146
|
+
worktree: longPath,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
assert.match(rendered, /sliceId="S03"/);
|
|
150
|
+
assert.match(rendered, /milestoneId="M001"/);
|
|
151
|
+
assert.match(rendered, /worktree=".*…"/);
|
|
152
|
+
assert.doesNotMatch(rendered, /"sliceId":\s*"S03"/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("formatCompactArgs shows full string values when expanded", () => {
|
|
156
|
+
const longPath = "/Users/alice/.gsd/projects/4dce7b775013/worktrees/slice-S03-some-long-path-that-exceeds-limit";
|
|
157
|
+
const rendered = renderTool("gsd_slice_complete", {
|
|
158
|
+
sliceId: "S03",
|
|
159
|
+
worktree: longPath,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
assert.match(rendered, new RegExp(longPath.replace(/\//g, "\\/")));
|
|
163
|
+
assert.doesNotMatch(rendered, /…/);
|
|
164
|
+
});
|
|
165
|
+
|
|
120
166
|
test("generic fallback truncates long output when collapsed", () => {
|
|
121
167
|
const longOutput = Array.from({ length: 25 }, (_, i) => `line ${i + 1}`).join("\n");
|
|
122
168
|
const rendered = renderToolCollapsed(
|
|
@@ -37,26 +37,32 @@ describe("DynamicBorder spinner", () => {
|
|
|
37
37
|
border.stopSpinner();
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it("triggers standalone render when no external render occurred recently",
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
it("triggers standalone render when no external render occurred recently", () => {
|
|
41
|
+
mock.timers.enable({ apis: ["setInterval"], now: 0 });
|
|
42
|
+
try {
|
|
43
|
+
const border = new DynamicBorder((s) => s);
|
|
44
|
+
const tui = makeTUI();
|
|
45
|
+
|
|
46
|
+
// Set lastExternalRender to a time well in the past
|
|
47
|
+
const anyBorder = border as any;
|
|
48
|
+
anyBorder.lastExternalRender = 0;
|
|
49
|
+
|
|
50
|
+
border.startSpinner(tui as any, (s) => s);
|
|
51
|
+
const initialCount = tui.renderCount;
|
|
52
|
+
|
|
53
|
+
// Advance exactly one spinner interval (200ms). With mocked timers
|
|
54
|
+
// this is deterministic — no flake under CI load.
|
|
55
|
+
mock.timers.tick(200);
|
|
56
|
+
|
|
57
|
+
assert.ok(
|
|
58
|
+
tui.renderCount > initialCount,
|
|
59
|
+
"spinner should trigger requestRender after one 200ms interval when no recent external render",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
border.stopSpinner();
|
|
63
|
+
} finally {
|
|
64
|
+
mock.timers.reset();
|
|
65
|
+
}
|
|
60
66
|
});
|
|
61
67
|
|
|
62
68
|
it("updates lastExternalRender on each render() call", () => {
|
|
@@ -66,6 +66,21 @@ function parseMcpToolName(name: string): { server: string; tool: string } | null
|
|
|
66
66
|
return { server: rest.slice(0, delim), tool: rest.slice(delim + 2) };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Prettify a raw tool name for display. Prefers the registered `label`
|
|
71
|
+
* ("Complete Slice") when available; otherwise strips a leading `gsd_`
|
|
72
|
+
* prefix and converts snake_case to Title Case.
|
|
73
|
+
*/
|
|
74
|
+
function prettifyToolName(name: string, label?: string): string {
|
|
75
|
+
if (label && label.trim().length > 0) return label;
|
|
76
|
+
const stripped = name.replace(/^gsd_/, "");
|
|
77
|
+
if (stripped.length === 0) return name;
|
|
78
|
+
return stripped
|
|
79
|
+
.split("_")
|
|
80
|
+
.map((word) => (word.length === 0 ? word : word[0].toUpperCase() + word.slice(1)))
|
|
81
|
+
.join(" ");
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
type ToolFrameTone = "pending" | "success" | "error";
|
|
70
85
|
|
|
71
86
|
function trimOuterBlankLines(lines: string[]): string[] {
|
|
@@ -131,15 +146,19 @@ function formatCompactArgs(args: unknown, expanded: boolean): string {
|
|
|
131
146
|
|
|
132
147
|
const allPrimitive = entries.every(([, value]) => {
|
|
133
148
|
const t = typeof value;
|
|
134
|
-
|
|
135
|
-
if (t === "string") return (value as string).length <= COMPACT_ARG_VALUE_LIMIT;
|
|
136
|
-
return value == null;
|
|
149
|
+
return t === "number" || t === "boolean" || t === "string" || value == null;
|
|
137
150
|
});
|
|
138
151
|
|
|
139
152
|
if (allPrimitive) {
|
|
140
153
|
return entries
|
|
141
154
|
.map(([key, value]) => {
|
|
142
|
-
if (typeof value === "string")
|
|
155
|
+
if (typeof value === "string") {
|
|
156
|
+
const truncated =
|
|
157
|
+
!expanded && value.length > COMPACT_ARG_VALUE_LIMIT
|
|
158
|
+
? `${value.slice(0, COMPACT_ARG_VALUE_LIMIT - 1)}…`
|
|
159
|
+
: value;
|
|
160
|
+
return `${key}=${JSON.stringify(truncated)}`;
|
|
161
|
+
}
|
|
143
162
|
if (value == null) return `${key}=null`;
|
|
144
163
|
return `${key}=${String(value)}`;
|
|
145
164
|
})
|
|
@@ -526,7 +545,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
526
545
|
const parsed = parseMcpToolName(this.toolName);
|
|
527
546
|
const frameLabel = parsed
|
|
528
547
|
? `Tool ${parsed.server}·${parsed.tool}`
|
|
529
|
-
: `Tool ${this.
|
|
548
|
+
: `Tool ${prettifyToolName(this.toolName, this.toolDefinition?.label) || "unknown"}`;
|
|
530
549
|
const framed = renderToolFrame(lines, frameWidth, {
|
|
531
550
|
label: frameLabel,
|
|
532
551
|
status: frameStatus,
|
|
@@ -570,12 +589,30 @@ export class ToolExecutionComponent extends Container {
|
|
|
570
589
|
}
|
|
571
590
|
} catch {
|
|
572
591
|
// Fall back to default on error
|
|
573
|
-
this.contentBox.addChild(
|
|
592
|
+
this.contentBox.addChild(
|
|
593
|
+
new Text(
|
|
594
|
+
theme.fg(
|
|
595
|
+
"toolTitle",
|
|
596
|
+
theme.bold(prettifyToolName(this.toolName, this.toolDefinition?.label)),
|
|
597
|
+
),
|
|
598
|
+
0,
|
|
599
|
+
0,
|
|
600
|
+
),
|
|
601
|
+
);
|
|
574
602
|
customRendererHasContent = true;
|
|
575
603
|
}
|
|
576
604
|
} else {
|
|
577
|
-
// No custom renderCall, show tool name
|
|
578
|
-
this.contentBox.addChild(
|
|
605
|
+
// No custom renderCall, show prettified tool name
|
|
606
|
+
this.contentBox.addChild(
|
|
607
|
+
new Text(
|
|
608
|
+
theme.fg(
|
|
609
|
+
"toolTitle",
|
|
610
|
+
theme.bold(prettifyToolName(this.toolName, this.toolDefinition?.label)),
|
|
611
|
+
),
|
|
612
|
+
0,
|
|
613
|
+
0,
|
|
614
|
+
),
|
|
615
|
+
);
|
|
579
616
|
customRendererHasContent = true;
|
|
580
617
|
}
|
|
581
618
|
|
|
@@ -1126,7 +1163,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
1126
1163
|
// cleanly. GSD-registered MCP tools have already had their prefix
|
|
1127
1164
|
// stripped upstream in partial-builder.ts and won't reach this branch.
|
|
1128
1165
|
const parsed = parseMcpToolName(this.toolName);
|
|
1129
|
-
const displayName = parsed
|
|
1166
|
+
const displayName = parsed
|
|
1167
|
+
? parsed.tool
|
|
1168
|
+
: prettifyToolName(this.toolName, this.toolDefinition?.label);
|
|
1130
1169
|
const serverPrefix = parsed ? theme.fg("muted", `${parsed.server}\u00b7`) : "";
|
|
1131
1170
|
text = serverPrefix + theme.fg("toolTitle", theme.bold(displayName));
|
|
1132
1171
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// @gsd/pi-coding-agent + system-prompt-skill-filter.test — coverage for the
|
|
2
|
+
// optional `skillFilter` option added to buildSystemPrompt (RFC #4779). The
|
|
3
|
+
// filter lets consumers narrow the <available_skills> catalog rendered into
|
|
4
|
+
// the cached system prompt without touching skill loading or invocation.
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
|
|
9
|
+
import { buildSystemPrompt } from "../core/system-prompt.js";
|
|
10
|
+
import type { Skill } from "../core/skills.js";
|
|
11
|
+
|
|
12
|
+
function makeSkill(name: string, description = `description for ${name}`): Skill {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
description,
|
|
16
|
+
filePath: `/tmp/${name}/SKILL.md`,
|
|
17
|
+
baseDir: `/tmp/${name}`,
|
|
18
|
+
source: "project",
|
|
19
|
+
disableModelInvocation: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractAvailableSkills(prompt: string): string {
|
|
24
|
+
const start = prompt.indexOf("<available_skills>");
|
|
25
|
+
const end = prompt.indexOf("</available_skills>");
|
|
26
|
+
if (start === -1 || end === -1) return "";
|
|
27
|
+
return prompt.slice(start, end + "</available_skills>".length);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Default branch (no customPrompt) ──────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
test("buildSystemPrompt: skillFilter omits filtered-out skills from <available_skills>", () => {
|
|
33
|
+
const skills = [makeSkill("alpha"), makeSkill("beta"), makeSkill("gamma")];
|
|
34
|
+
const prompt = buildSystemPrompt({
|
|
35
|
+
skills,
|
|
36
|
+
selectedTools: ["read", "Skill"],
|
|
37
|
+
skillFilter: skill => skill.name !== "beta",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const section = extractAvailableSkills(prompt);
|
|
41
|
+
assert.ok(section.length > 0, "catalog section should render");
|
|
42
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
43
|
+
assert.match(section, /<name>gamma<\/name>/);
|
|
44
|
+
assert.doesNotMatch(section, /<name>beta<\/name>/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("buildSystemPrompt: skillFilter omitted preserves pre-filter behavior (all skills render)", () => {
|
|
48
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
49
|
+
const prompt = buildSystemPrompt({
|
|
50
|
+
skills,
|
|
51
|
+
selectedTools: ["read", "Skill"],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const section = extractAvailableSkills(prompt);
|
|
55
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
56
|
+
assert.match(section, /<name>beta<\/name>/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("buildSystemPrompt: skillFilter that rejects every skill suppresses the <available_skills> block", () => {
|
|
60
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
61
|
+
const prompt = buildSystemPrompt({
|
|
62
|
+
skills,
|
|
63
|
+
selectedTools: ["read", "Skill"],
|
|
64
|
+
skillFilter: () => false,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// With zero visible skills, formatSkillsForPrompt returns an empty string,
|
|
68
|
+
// so the opening tag should not appear anywhere.
|
|
69
|
+
assert.ok(!prompt.includes("<available_skills>"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ─── Custom-prompt branch ──────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
test("buildSystemPrompt (customPrompt): skillFilter applies to the catalog appended onto a custom prompt", () => {
|
|
75
|
+
const skills = [makeSkill("alpha"), makeSkill("beta"), makeSkill("gamma")];
|
|
76
|
+
const prompt = buildSystemPrompt({
|
|
77
|
+
customPrompt: "CUSTOM BASE",
|
|
78
|
+
skills,
|
|
79
|
+
selectedTools: ["read", "Skill"],
|
|
80
|
+
skillFilter: skill => skill.name === "alpha",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const section = extractAvailableSkills(prompt);
|
|
84
|
+
assert.match(section, /<name>alpha<\/name>/);
|
|
85
|
+
assert.doesNotMatch(section, /<name>beta<\/name>/);
|
|
86
|
+
assert.doesNotMatch(section, /<name>gamma<\/name>/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ─── Interaction with disableModelInvocation ──────────────────────────────
|
|
90
|
+
|
|
91
|
+
test("buildSystemPrompt: skillFilter composes with disableModelInvocation (both must pass)", () => {
|
|
92
|
+
// A skill already hidden from the catalog by disableModelInvocation must
|
|
93
|
+
// remain hidden even if skillFilter would otherwise admit it. The filter
|
|
94
|
+
// narrows, it does not override the existing invisibility contract.
|
|
95
|
+
const skills: Skill[] = [
|
|
96
|
+
{ ...makeSkill("visible"), disableModelInvocation: false },
|
|
97
|
+
{ ...makeSkill("hidden"), disableModelInvocation: true },
|
|
98
|
+
];
|
|
99
|
+
const prompt = buildSystemPrompt({
|
|
100
|
+
skills,
|
|
101
|
+
selectedTools: ["read", "Skill"],
|
|
102
|
+
skillFilter: () => true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const section = extractAvailableSkills(prompt);
|
|
106
|
+
assert.match(section, /<name>visible<\/name>/);
|
|
107
|
+
assert.doesNotMatch(section, /<name>hidden<\/name>/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ─── Pass-through of non-filtered fields ──────────────────────────────────
|
|
111
|
+
|
|
112
|
+
test("buildSystemPrompt: skillFilter does not affect context files or cwd rendering", () => {
|
|
113
|
+
const skills = [makeSkill("alpha")];
|
|
114
|
+
const prompt = buildSystemPrompt({
|
|
115
|
+
skills,
|
|
116
|
+
cwd: "/tmp/example",
|
|
117
|
+
contextFiles: [{ path: "CLAUDE.md", content: "project instructions" }],
|
|
118
|
+
selectedTools: ["read", "Skill"],
|
|
119
|
+
skillFilter: () => false,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
assert.ok(prompt.includes("/tmp/example"), "cwd should still render");
|
|
123
|
+
assert.ok(prompt.includes("project instructions"), "context files should still render");
|
|
124
|
+
assert.ok(!prompt.includes("<available_skills>"), "no skill catalog when filter rejects all");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ─── Exception safety ─────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
test("buildSystemPrompt: skillFilter that throws falls back to unfiltered list and does not propagate", (t) => {
|
|
130
|
+
// A buggy consumer predicate must not bubble out of buildSystemPrompt.
|
|
131
|
+
// If it did, _rebuildSystemPrompt could unwind mid-setTools() and leave
|
|
132
|
+
// the session with updated tools but a stale system prompt.
|
|
133
|
+
const skills = [makeSkill("alpha"), makeSkill("beta")];
|
|
134
|
+
|
|
135
|
+
// Suppress the console.warn the fallback emits so test output stays clean.
|
|
136
|
+
const originalWarn = console.warn;
|
|
137
|
+
const warnings: string[] = [];
|
|
138
|
+
console.warn = (...args: unknown[]) => { warnings.push(args.join(" ")); };
|
|
139
|
+
t.after(() => { console.warn = originalWarn; });
|
|
140
|
+
|
|
141
|
+
let prompt = "";
|
|
142
|
+
assert.doesNotThrow(() => {
|
|
143
|
+
prompt = buildSystemPrompt({
|
|
144
|
+
skills,
|
|
145
|
+
selectedTools: ["read", "Skill"],
|
|
146
|
+
skillFilter: () => { throw new Error("consumer bug"); },
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const section = extractAvailableSkills(prompt);
|
|
151
|
+
assert.match(section, /<name>alpha<\/name>/, "alpha should render (fallback to unfiltered)");
|
|
152
|
+
assert.match(section, /<name>beta<\/name>/, "beta should render (fallback to unfiltered)");
|
|
153
|
+
assert.ok(
|
|
154
|
+
warnings.some(w => w.includes("skillFilter threw") && w.includes("consumer bug")),
|
|
155
|
+
"fallback should emit an identifying warning",
|
|
156
|
+
);
|
|
157
|
+
});
|