gsd-pi 2.78.1-dev.84a383f51 → 2.78.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/cli.js +55 -95
- package/dist/headless-query.d.ts +0 -22
- package/dist/headless-query.js +4 -24
- package/dist/headless.d.ts +0 -10
- package/dist/headless.js +1 -16
- package/dist/loader.js +10 -7
- package/dist/onboarding.d.ts +0 -10
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +2 -5
- package/dist/resource-loader.d.ts +2 -5
- package/dist/resource-loader.js +5 -28
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +601 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +651 -0
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +91 -0
- package/dist/resources/extensions/gsd/auto/loop.js +0 -23
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +1 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -3
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -43
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +0 -30
- package/dist/resources/extensions/gsd/auto.js +5 -14
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -14
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +5 -7
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +4 -5
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +31 -94
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +6 -11
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +8 -34
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +2 -38
- package/dist/resources/extensions/gsd/commands/catalog.js +5 -69
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -22
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -4
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +1 -39
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +5 -12
- package/dist/resources/extensions/gsd/gsd-db.js +2 -11
- package/dist/resources/extensions/gsd/guided-flow.js +23 -23
- package/dist/resources/extensions/gsd/memory-store.js +31 -66
- package/dist/resources/extensions/gsd/model-router.js +9 -114
- package/dist/resources/extensions/gsd/native-git-bridge.js +1 -7
- package/dist/resources/extensions/gsd/preferences-models.js +15 -91
- package/dist/resources/extensions/gsd/preferences-types.js +0 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +0 -32
- package/dist/resources/extensions/gsd/preferences.js +3 -5
- package/dist/resources/extensions/gsd/prompt-loader.js +12 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -9
- package/dist/resources/extensions/gsd/state.js +0 -42
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +0 -1
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +112 -0
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +23 -0
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +5 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -18
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +1 -3
- package/dist/resources/extensions/gsd/worktree-command.js +46 -26
- package/dist/resources/extensions/mcp-client/index.js +3 -6
- package/dist/resources/extensions/slash-commands/create-extension.js +22 -36
- package/dist/resources/skills/create-gsd-extension/SKILL.md +5 -9
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +12 -32
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
- package/dist/rtk-shared.d.ts +0 -3
- package/dist/rtk-shared.js +0 -17
- package/dist/rtk.d.ts +5 -2
- package/dist/rtk.js +20 -3
- 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 +13 -13
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +4 -44
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -4
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- 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/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +20 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/package.json +1 -2
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +46 -74
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +0 -26
- package/packages/mcp-server/src/workflow-tools.ts +58 -93
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +19 -48
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +0 -13
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +3 -24
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +0 -26
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +20 -52
- package/packages/pi-ai/src/types.ts +0 -13
- package/packages/pi-ai/src/utils/repair-tool-json.ts +3 -24
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +0 -32
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +0 -6
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +0 -4
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +2 -19
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +0 -10
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +0 -18
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +0 -13
- 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 -20
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -14
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +0 -7
- package/packages/pi-coding-agent/src/core/messages.ts +0 -4
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +2 -32
- package/packages/pi-coding-agent/src/core/model-registry.ts +0 -21
- package/packages/pi-coding-agent/src/core/system-prompt.ts +15 -33
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -17
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +3 -17
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +3 -20
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop.ts +2 -24
- package/src/resources/extensions/gsd/auto/phases.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +1 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -3
- package/src/resources/extensions/gsd/auto/types.ts +0 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +8 -46
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +4 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +0 -38
- package/src/resources/extensions/gsd/auto.ts +4 -14
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +13 -15
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -8
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +9 -10
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +31 -102
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +6 -12
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -39
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +11 -39
- package/src/resources/extensions/gsd/commands/catalog.ts +5 -75
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -22
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -15
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -4
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +1 -39
- package/src/resources/extensions/gsd/doctor-types.ts +1 -3
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +5 -13
- package/src/resources/extensions/gsd/gsd-db.ts +2 -12
- package/src/resources/extensions/gsd/guided-flow.ts +25 -25
- package/src/resources/extensions/gsd/memory-store.ts +28 -81
- package/src/resources/extensions/gsd/model-router.ts +9 -172
- package/src/resources/extensions/gsd/native-git-bridge.ts +1 -7
- package/src/resources/extensions/gsd/preferences-models.ts +15 -101
- package/src/resources/extensions/gsd/preferences-types.ts +0 -6
- package/src/resources/extensions/gsd/preferences-validation.ts +0 -35
- package/src/resources/extensions/gsd/preferences.ts +2 -16
- package/src/resources/extensions/gsd/prompt-loader.ts +12 -26
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -9
- package/src/resources/extensions/gsd/state.ts +0 -42
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +0 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1 -178
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +0 -58
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -21
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +211 -138
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +59 -142
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +4 -7
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +32 -89
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +23 -41
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +43 -3
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +87 -22
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +118 -7
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +60 -18
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +76 -14
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +83 -22
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +63 -1
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +1 -26
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +0 -30
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +4 -14
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +12 -22
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +1 -64
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +0 -128
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +1 -33
- package/src/resources/extensions/gsd/tests/model-router.test.ts +8 -169
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +0 -8
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +43 -32
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +10 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -127
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +19 -168
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +1 -7
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -23
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +4 -51
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +16 -7
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +1 -15
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +0 -15
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -17
- package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +1 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/gsd/worktree-command.ts +44 -31
- package/src/resources/extensions/mcp-client/index.ts +3 -6
- package/src/resources/extensions/slash-commands/create-extension.ts +24 -38
- package/src/resources/skills/create-gsd-extension/SKILL.md +5 -9
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +12 -32
- package/dist/cli-policy.d.ts +0 -13
- package/dist/cli-policy.js +0 -17
- package/dist/resources/.managed-resources-content-hash +0 -1
- package/dist/resources/extensions/gsd/auto-runtime-state.js +0 -31
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +0 -36
- package/dist/resources/extensions/gsd/worktree-session-state.js +0 -33
- package/dist/runtime-checks.d.ts +0 -27
- package/dist/runtime-checks.js +0 -38
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +0 -1
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +0 -1
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +0 -1
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +0 -1
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +0 -1
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +0 -10
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +0 -2
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +0 -20
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-9bf2e0c50fb2ca05.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +0 -1
- package/dist/worktree-status-banner.d.ts +0 -1
- package/dist/worktree-status-banner.js +0 -132
- package/packages/mcp-server/dist/alias-telemetry.d.ts +0 -8
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +0 -1
- package/packages/mcp-server/dist/alias-telemetry.js +0 -30
- package/packages/mcp-server/dist/alias-telemetry.js.map +0 -1
- package/packages/mcp-server/src/alias-telemetry.test.ts +0 -78
- package/packages/mcp-server/src/alias-telemetry.ts +0 -30
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +0 -2
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +0 -231
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +0 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +0 -289
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +0 -37
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +0 -49
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +0 -133
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +0 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +0 -78
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +0 -1
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +0 -181
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +0 -1
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +0 -77
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +0 -212
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +0 -102
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +0 -200
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +0 -2
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +0 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +0 -161
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +0 -1
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +0 -219
- package/src/resources/extensions/gsd/auto-runtime-state.ts +0 -51
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +0 -47
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +0 -116
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +0 -100
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +0 -93
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +0 -81
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +0 -57
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +0 -145
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +0 -124
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +0 -152
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +0 -50
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +0 -93
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +0 -101
- package/src/resources/extensions/gsd/worktree-session-state.ts +0 -35
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +0 -91
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +0 -58
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → 7afp7gq8-DVbxum83zRQ-}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → 7afp7gq8-DVbxum83zRQ-}/_ssgManifest.js +0 -0
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
// Runtime regression tests for the post-compaction tool-card cleanup and the
|
|
2
|
-
// green-bordered success-notification rendering. Replaces the source-grep
|
|
3
|
-
// `src/tests/tui-running-and-success-box.test.ts` that was deleted in #4875
|
|
4
|
-
// (tracked as #4872).
|
|
5
|
-
//
|
|
6
|
-
// The previous tests asserted on identifier presence and method signature
|
|
7
|
-
// shape via regex. A regression that routed `success` notifications through
|
|
8
|
-
// `showStatus` (dim text) by accident would not have failed because the
|
|
9
|
-
// `showSuccess` method would still exist and still match the regex. These
|
|
10
|
-
// tests instead drive the components through the actual scenario and assert
|
|
11
|
-
// on rendered output.
|
|
12
|
-
|
|
13
|
-
import assert from "node:assert/strict";
|
|
14
|
-
import { describe, it, before } from "node:test";
|
|
15
|
-
|
|
16
|
-
import { Container, Text } from "@gsd/pi-tui";
|
|
17
|
-
import stripAnsi from "strip-ansi";
|
|
18
|
-
|
|
19
|
-
import { initTheme, theme } from "../theme/theme.js";
|
|
20
|
-
import { DynamicBorder } from "./dynamic-border.js";
|
|
21
|
-
import { ToolExecutionComponent } from "./tool-execution.js";
|
|
22
|
-
|
|
23
|
-
// Theme is a globalThis-shared singleton that throws if not initialized.
|
|
24
|
-
// Initialize once before any test that exercises themed rendering.
|
|
25
|
-
before(() => {
|
|
26
|
-
initTheme("dark");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
interface MockTui {
|
|
30
|
-
renderCount: number;
|
|
31
|
-
requestRender(): void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function makeMockTUI(): MockTui {
|
|
35
|
-
return {
|
|
36
|
-
renderCount: 0,
|
|
37
|
-
requestRender() {
|
|
38
|
-
this.renderCount++;
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ─── Bug 1: tool cards stuck in "Running" after compaction ──────────────
|
|
44
|
-
|
|
45
|
-
describe("ToolExecutionComponent post-compaction cleanup", () => {
|
|
46
|
-
it("renders 'Running' status while the tool call has no result", () => {
|
|
47
|
-
// Baseline: a freshly-constructed component (mid-stream) must show
|
|
48
|
-
// the running badge — this is the state we need to flip OUT of when
|
|
49
|
-
// compaction removes the result message.
|
|
50
|
-
const ui = makeMockTUI();
|
|
51
|
-
const c = new ToolExecutionComponent(
|
|
52
|
-
"read_file",
|
|
53
|
-
{ path: "/tmp/x.txt" },
|
|
54
|
-
{},
|
|
55
|
-
undefined,
|
|
56
|
-
ui as never,
|
|
57
|
-
);
|
|
58
|
-
const rendered = c.render(60).map(stripAnsi).join("\n");
|
|
59
|
-
assert.ok(
|
|
60
|
-
rendered.includes("Running"),
|
|
61
|
-
"freshly constructed component should render 'Running' badge",
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("markHistoricalNoResult flips a stuck tool card OUT of 'Running'", () => {
|
|
66
|
-
// Real bug: after session-history replay (post-compaction or session
|
|
67
|
-
// switch), tool calls without matching tool_result messages stay in
|
|
68
|
-
// isPartial = true forever. markHistoricalNoResult must produce a
|
|
69
|
-
// rendered output that no longer reads "Running".
|
|
70
|
-
const ui = makeMockTUI();
|
|
71
|
-
const c = new ToolExecutionComponent(
|
|
72
|
-
"read_file",
|
|
73
|
-
{ path: "/tmp/x.txt" },
|
|
74
|
-
{},
|
|
75
|
-
undefined,
|
|
76
|
-
ui as never,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
c.markHistoricalNoResult();
|
|
80
|
-
|
|
81
|
-
const rendered = c.render(60).map(stripAnsi).join("\n");
|
|
82
|
-
assert.ok(
|
|
83
|
-
!rendered.includes("Running"),
|
|
84
|
-
"after markHistoricalNoResult, the tool card must NOT render 'Running' — got:\n" +
|
|
85
|
-
rendered,
|
|
86
|
-
);
|
|
87
|
-
assert.ok(
|
|
88
|
-
rendered.includes("Done"),
|
|
89
|
-
"flipped card should render 'Done' status (no-result success)",
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("markHistoricalNoResult is idempotent when a real result already exists", () => {
|
|
94
|
-
// Late-arriving stream events must not clobber legitimate results.
|
|
95
|
-
// We observe the no-clobber via behaviour: a card that completed
|
|
96
|
-
// with a real result keeps its "Done" badge and content even if
|
|
97
|
-
// markHistoricalNoResult fires again afterwards.
|
|
98
|
-
const ui = makeMockTUI();
|
|
99
|
-
const c = new ToolExecutionComponent(
|
|
100
|
-
"read_file",
|
|
101
|
-
{ path: "/tmp/x.txt" },
|
|
102
|
-
{},
|
|
103
|
-
undefined,
|
|
104
|
-
ui as never,
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
// Use the public completion path the runtime calls.
|
|
108
|
-
c.updateResult(
|
|
109
|
-
{
|
|
110
|
-
content: [{ type: "text", text: "real-result-payload" }],
|
|
111
|
-
isError: false,
|
|
112
|
-
},
|
|
113
|
-
false, // isPartial = false → "Done"
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const before = c.render(60).map(stripAnsi).join("\n");
|
|
117
|
-
assert.ok(before.includes("Done"), "after complete(), card shows 'Done'");
|
|
118
|
-
|
|
119
|
-
c.markHistoricalNoResult(); // must early-return
|
|
120
|
-
const after = c.render(60).map(stripAnsi).join("\n");
|
|
121
|
-
assert.equal(
|
|
122
|
-
after,
|
|
123
|
-
before,
|
|
124
|
-
"markHistoricalNoResult must NOT mutate state when a real result is already present",
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// ─── Bug 2: success notifications render as a green bordered box ────────
|
|
130
|
-
|
|
131
|
-
describe("Success-notification rendering — DynamicBorder + success theme", () => {
|
|
132
|
-
// We drive the same component composition that interactive-mode.showSuccess
|
|
133
|
-
// builds (DynamicBorder + Text + DynamicBorder, all themed with
|
|
134
|
-
// theme.fg("success", …)) and assert on the rendered output, rather than
|
|
135
|
-
// trying to instantiate the full InteractiveMode (which requires a real
|
|
136
|
-
// session, bridge, and TUI runtime).
|
|
137
|
-
function buildSuccessNotification(message: string): Container {
|
|
138
|
-
const c = new Container();
|
|
139
|
-
const successColor = (text: string) => theme.fg("success", text);
|
|
140
|
-
c.addChild(new DynamicBorder(successColor));
|
|
141
|
-
c.addChild(new Text(theme.fg("success", message), 1, 0));
|
|
142
|
-
c.addChild(new DynamicBorder(successColor));
|
|
143
|
-
return c;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
it("renders a top and bottom horizontal border framing the message", () => {
|
|
147
|
-
const c = buildSuccessNotification("Milestone M042 ready.");
|
|
148
|
-
const lines = c.render(40);
|
|
149
|
-
|
|
150
|
-
// First and last lines should be horizontal-rule borders
|
|
151
|
-
// (DynamicBorder renders a row of "─" characters).
|
|
152
|
-
const firstStripped = stripAnsi(lines[0]);
|
|
153
|
-
const lastStripped = stripAnsi(lines[lines.length - 1]);
|
|
154
|
-
|
|
155
|
-
assert.ok(
|
|
156
|
-
firstStripped.includes("─"),
|
|
157
|
-
`first line should be a border (got: ${JSON.stringify(firstStripped)})`,
|
|
158
|
-
);
|
|
159
|
-
assert.ok(
|
|
160
|
-
lastStripped.includes("─"),
|
|
161
|
-
`last line should be a border (got: ${JSON.stringify(lastStripped)})`,
|
|
162
|
-
);
|
|
163
|
-
assert.ok(
|
|
164
|
-
lines.length >= 3,
|
|
165
|
-
`success notification must have at least 3 lines (top border, message, bottom border) — got ${lines.length}`,
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("the message text appears between the two borders", () => {
|
|
170
|
-
const c = buildSuccessNotification("Milestone M042 ready.");
|
|
171
|
-
const lines = c.render(40);
|
|
172
|
-
|
|
173
|
-
const messageRow = lines.findIndex((l) => stripAnsi(l).includes("Milestone M042 ready."));
|
|
174
|
-
assert.ok(messageRow > 0, "message must appear after the top border");
|
|
175
|
-
assert.ok(
|
|
176
|
-
messageRow < lines.length - 1,
|
|
177
|
-
"message must appear before the bottom border",
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("borders carry ANSI styling (not plain dim text like showStatus)", () => {
|
|
182
|
-
// Goodhart-resistant: if a regression routed success through
|
|
183
|
-
// showStatus, the borders would be missing AND the rendered text
|
|
184
|
-
// would be plain (or only dim-styled, not success-colored). We
|
|
185
|
-
// observe the border lines carry ANSI escape codes — the literal
|
|
186
|
-
// foreground color depends on the theme (which is system-dependent
|
|
187
|
-
// in CI), so we don't pin a specific code but DO require styling
|
|
188
|
-
// to be present.
|
|
189
|
-
const c = buildSuccessNotification("ok");
|
|
190
|
-
const lines = c.render(40);
|
|
191
|
-
const top = lines[0];
|
|
192
|
-
assert.ok(
|
|
193
|
-
top !== stripAnsi(top),
|
|
194
|
-
"top border must contain ANSI styling — a plain unstyled border " +
|
|
195
|
-
"would indicate the rendering bypassed theme.fg('success', ...)",
|
|
196
|
-
);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("plain Text status (the showStatus path) does NOT produce a bordered box", () => {
|
|
200
|
-
// Counter-test: this is what the bug looked like — a single dim Text
|
|
201
|
-
// with no surrounding border. If anyone ever "fixes" the showSuccess
|
|
202
|
-
// regression by also adding borders to showStatus, this test will
|
|
203
|
-
// surface the conflation.
|
|
204
|
-
const plain = new Text(theme.fg("dim", "Milestone M042 ready."), 1, 0);
|
|
205
|
-
const lines = plain.render(40);
|
|
206
|
-
const joined = lines.map(stripAnsi).join("\n");
|
|
207
|
-
assert.ok(
|
|
208
|
-
!joined.includes("─"),
|
|
209
|
-
"plain Text (showStatus path) must NOT contain border characters",
|
|
210
|
-
);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// @gsd/pi-coding-agent + system-prompt-cache-stability.test — regression
|
|
2
|
-
// coverage for #5019. The system prompt must NOT include a per-call timestamp
|
|
3
|
-
// by default; embedding `Date.toLocaleString()` in the cached prefix
|
|
4
|
-
// invalidates Anthropic prompt-cache hits on every request and incurs the
|
|
5
|
-
// cache-write premium. The opt-in `includeDateTime` flag preserves the prior
|
|
6
|
-
// behavior for callers that explicitly want clock awareness.
|
|
7
|
-
|
|
8
|
-
import test from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
|
|
11
|
-
import { buildSystemPrompt } from "../core/system-prompt.js";
|
|
12
|
-
|
|
13
|
-
// ─── Default branch (no customPrompt) ──────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
test("buildSystemPrompt: default omits 'Current date and time' line", () => {
|
|
16
|
-
const prompt = buildSystemPrompt({ selectedTools: ["read", "edit"] });
|
|
17
|
-
assert.ok(
|
|
18
|
-
!prompt.includes("Current date and time:"),
|
|
19
|
-
`prompt should not include the dateTime line by default. Got:\n${prompt}`,
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("buildSystemPrompt: includeDateTime=true emits 'Current date and time' line", () => {
|
|
24
|
-
const prompt = buildSystemPrompt({
|
|
25
|
-
selectedTools: ["read", "edit"],
|
|
26
|
-
includeDateTime: true,
|
|
27
|
-
});
|
|
28
|
-
assert.match(
|
|
29
|
-
prompt,
|
|
30
|
-
/Current date and time: /,
|
|
31
|
-
"prompt should include the dateTime line when explicitly opted in",
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("buildSystemPrompt: includeDateTime=false explicit also omits the line", () => {
|
|
36
|
-
const prompt = buildSystemPrompt({
|
|
37
|
-
selectedTools: ["read", "edit"],
|
|
38
|
-
includeDateTime: false,
|
|
39
|
-
});
|
|
40
|
-
assert.ok(!prompt.includes("Current date and time:"));
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// ─── Custom-prompt branch ──────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
test("buildSystemPrompt (customPrompt): default omits 'Current date and time' line", () => {
|
|
46
|
-
const prompt = buildSystemPrompt({
|
|
47
|
-
customPrompt: "CUSTOM BASE",
|
|
48
|
-
selectedTools: ["read"],
|
|
49
|
-
});
|
|
50
|
-
assert.ok(!prompt.includes("Current date and time:"));
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("buildSystemPrompt (customPrompt): includeDateTime=true emits the line", () => {
|
|
54
|
-
const prompt = buildSystemPrompt({
|
|
55
|
-
customPrompt: "CUSTOM BASE",
|
|
56
|
-
selectedTools: ["read"],
|
|
57
|
-
includeDateTime: true,
|
|
58
|
-
});
|
|
59
|
-
assert.match(prompt, /Current date and time: /);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// ─── Cache-stability invariant ─────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
test("buildSystemPrompt: two back-to-back default calls produce identical prompts", async () => {
|
|
65
|
-
// The bug: the previous default-on `dateTime` line included `second: "2-digit"`,
|
|
66
|
-
// so two calls within the same second could match but any longer gap busted
|
|
67
|
-
// the cache. Asserting equality across a deliberate sub-second sleep proves
|
|
68
|
-
// the byte-for-byte stability that Anthropic prompt caching requires.
|
|
69
|
-
const first = buildSystemPrompt({ selectedTools: ["read", "edit"], cwd: "/tmp/example" });
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
71
|
-
const second = buildSystemPrompt({ selectedTools: ["read", "edit"], cwd: "/tmp/example" });
|
|
72
|
-
assert.equal(
|
|
73
|
-
first,
|
|
74
|
-
second,
|
|
75
|
-
"system prompt must be byte-for-byte stable across calls so the prompt cache can hit",
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("buildSystemPrompt: includeDateTime=true intentionally produces different prompts across the second boundary", async () => {
|
|
80
|
-
// Inverse of the cache-stability test: when callers opt in, the dateTime
|
|
81
|
-
// line is expected to vary. This documents the trade-off the flag exists
|
|
82
|
-
// to surface.
|
|
83
|
-
const first = buildSystemPrompt({
|
|
84
|
-
selectedTools: ["read"],
|
|
85
|
-
cwd: "/tmp/example",
|
|
86
|
-
includeDateTime: true,
|
|
87
|
-
});
|
|
88
|
-
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
89
|
-
const second = buildSystemPrompt({
|
|
90
|
-
selectedTools: ["read"],
|
|
91
|
-
cwd: "/tmp/example",
|
|
92
|
-
includeDateTime: true,
|
|
93
|
-
});
|
|
94
|
-
assert.notEqual(first, second, "opt-in dateTime is expected to vary across the second boundary");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// ─── Cwd preservation ──────────────────────────────────────────────────────
|
|
98
|
-
|
|
99
|
-
test("buildSystemPrompt: 'Current working directory' line is preserved (only dateTime is removed)", () => {
|
|
100
|
-
const prompt = buildSystemPrompt({ selectedTools: ["read"], cwd: "/tmp/example" });
|
|
101
|
-
assert.match(prompt, /Current working directory: \/tmp\/example/);
|
|
102
|
-
});
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// @gsd/pi-coding-agent + token-telemetry.test — coverage for #5023.
|
|
2
|
-
// Verifies the env-gated emitter:
|
|
3
|
-
// - is silent by default (no behavior change for existing users)
|
|
4
|
-
// - emits a single valid JSON line when PI_TOKEN_TELEMETRY=1
|
|
5
|
-
// - record shape captures the cache breakdown the providers already extract
|
|
6
|
-
// - cacheHitRatio math is correct, including the no-input edge case
|
|
7
|
-
|
|
8
|
-
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
|
|
11
|
-
import type { AssistantMessage } from "@gsd/pi-ai";
|
|
12
|
-
|
|
13
|
-
import { buildTokenTelemetryRecord, emitTokenTelemetry } from "../core/token-telemetry.js";
|
|
14
|
-
|
|
15
|
-
function makeAssistantMessage(overrides: Partial<AssistantMessage> = {}): AssistantMessage {
|
|
16
|
-
return {
|
|
17
|
-
role: "assistant",
|
|
18
|
-
content: [],
|
|
19
|
-
api: "anthropic-messages",
|
|
20
|
-
provider: "anthropic",
|
|
21
|
-
model: "claude-sonnet-4-6",
|
|
22
|
-
usage: {
|
|
23
|
-
input: 100,
|
|
24
|
-
output: 50,
|
|
25
|
-
cacheRead: 0,
|
|
26
|
-
cacheWrite: 0,
|
|
27
|
-
totalTokens: 150,
|
|
28
|
-
cost: { input: 0.3, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 1.05 },
|
|
29
|
-
},
|
|
30
|
-
stopReason: "stop",
|
|
31
|
-
timestamp: 1700000000000,
|
|
32
|
-
...overrides,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ─── buildTokenTelemetryRecord ─────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
describe("buildTokenTelemetryRecord", () => {
|
|
39
|
-
test("captures all fields from a typical message", () => {
|
|
40
|
-
const msg = makeAssistantMessage();
|
|
41
|
-
const record = buildTokenTelemetryRecord(msg);
|
|
42
|
-
assert.deepEqual(record, {
|
|
43
|
-
ts: 1700000000000,
|
|
44
|
-
model: "claude-sonnet-4-6",
|
|
45
|
-
stopReason: "stop",
|
|
46
|
-
input: 100,
|
|
47
|
-
output: 50,
|
|
48
|
-
cacheRead: 0,
|
|
49
|
-
cacheWrite: 0,
|
|
50
|
-
costTotal: 1.05,
|
|
51
|
-
cacheHitRatio: 0,
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("cacheHitRatio = read / (read + input) when both present", () => {
|
|
56
|
-
const msg = makeAssistantMessage({
|
|
57
|
-
usage: {
|
|
58
|
-
input: 200,
|
|
59
|
-
output: 50,
|
|
60
|
-
cacheRead: 800,
|
|
61
|
-
cacheWrite: 0,
|
|
62
|
-
totalTokens: 1050,
|
|
63
|
-
cost: { input: 0.6, output: 0.75, cacheRead: 0.24, cacheWrite: 0, total: 1.59 },
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
const record = buildTokenTelemetryRecord(msg);
|
|
67
|
-
assert.equal(record.cacheRead, 800);
|
|
68
|
-
assert.equal(record.input, 200);
|
|
69
|
-
assert.equal(record.cacheHitRatio, 0.8);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("cacheHitRatio = 0 when both read and input are 0 (no division-by-zero)", () => {
|
|
73
|
-
const msg = makeAssistantMessage({
|
|
74
|
-
usage: {
|
|
75
|
-
input: 0,
|
|
76
|
-
output: 50,
|
|
77
|
-
cacheRead: 0,
|
|
78
|
-
cacheWrite: 0,
|
|
79
|
-
totalTokens: 50,
|
|
80
|
-
cost: { input: 0, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 0.75 },
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
const record = buildTokenTelemetryRecord(msg);
|
|
84
|
-
assert.equal(record.cacheHitRatio, 0);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("cacheHitRatio = 1 when only cacheRead present (full hit)", () => {
|
|
88
|
-
const msg = makeAssistantMessage({
|
|
89
|
-
usage: {
|
|
90
|
-
input: 0,
|
|
91
|
-
output: 50,
|
|
92
|
-
cacheRead: 5000,
|
|
93
|
-
cacheWrite: 0,
|
|
94
|
-
totalTokens: 5050,
|
|
95
|
-
cost: { input: 0, output: 0.75, cacheRead: 1.5, cacheWrite: 0, total: 2.25 },
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
const record = buildTokenTelemetryRecord(msg);
|
|
99
|
-
assert.equal(record.cacheHitRatio, 1);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("cacheWrite is captured (the cache-miss-with-cache-control case from #5019)", () => {
|
|
103
|
-
const msg = makeAssistantMessage({
|
|
104
|
-
usage: {
|
|
105
|
-
input: 50,
|
|
106
|
-
output: 100,
|
|
107
|
-
cacheRead: 0,
|
|
108
|
-
cacheWrite: 5000,
|
|
109
|
-
totalTokens: 5150,
|
|
110
|
-
cost: { input: 0.15, output: 1.5, cacheRead: 0, cacheWrite: 18.75, total: 20.4 },
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
const record = buildTokenTelemetryRecord(msg);
|
|
114
|
-
assert.equal(record.cacheWrite, 5000);
|
|
115
|
-
assert.equal(record.cacheHitRatio, 0, "no read = ratio 0 even when write is large");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("error stopReason is captured verbatim", () => {
|
|
119
|
-
const msg = makeAssistantMessage({ stopReason: "error", errorMessage: "rate_limit" });
|
|
120
|
-
assert.equal(buildTokenTelemetryRecord(msg).stopReason, "error");
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// ─── emitTokenTelemetry ────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
describe("emitTokenTelemetry", () => {
|
|
127
|
-
let captured: string[];
|
|
128
|
-
let originalWrite: typeof process.stderr.write;
|
|
129
|
-
let originalEnv: string | undefined;
|
|
130
|
-
|
|
131
|
-
beforeEach(() => {
|
|
132
|
-
captured = [];
|
|
133
|
-
originalWrite = process.stderr.write.bind(process.stderr);
|
|
134
|
-
// Replace stderr.write with a capture; preserve the same return contract.
|
|
135
|
-
process.stderr.write = ((chunk: string | Uint8Array): boolean => {
|
|
136
|
-
captured.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
|
|
137
|
-
return true;
|
|
138
|
-
}) as typeof process.stderr.write;
|
|
139
|
-
originalEnv = process.env.PI_TOKEN_TELEMETRY;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
afterEach(() => {
|
|
143
|
-
process.stderr.write = originalWrite;
|
|
144
|
-
if (originalEnv === undefined) {
|
|
145
|
-
delete process.env.PI_TOKEN_TELEMETRY;
|
|
146
|
-
} else {
|
|
147
|
-
process.env.PI_TOKEN_TELEMETRY = originalEnv;
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("silent by default (env var unset)", () => {
|
|
152
|
-
delete process.env.PI_TOKEN_TELEMETRY;
|
|
153
|
-
emitTokenTelemetry(makeAssistantMessage());
|
|
154
|
-
assert.equal(captured.length, 0);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("silent when env var has any non-'1' value", () => {
|
|
158
|
-
process.env.PI_TOKEN_TELEMETRY = "true"; // not literally "1"
|
|
159
|
-
emitTokenTelemetry(makeAssistantMessage());
|
|
160
|
-
assert.equal(captured.length, 0, "only literal '1' should enable telemetry");
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("emits a single JSON line when PI_TOKEN_TELEMETRY=1", () => {
|
|
164
|
-
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
165
|
-
emitTokenTelemetry(makeAssistantMessage());
|
|
166
|
-
assert.equal(captured.length, 1);
|
|
167
|
-
assert.ok(captured[0].endsWith("\n"), "line must terminate with newline");
|
|
168
|
-
const parsed = JSON.parse(captured[0].trimEnd());
|
|
169
|
-
assert.equal(parsed.model, "claude-sonnet-4-6");
|
|
170
|
-
assert.equal(parsed.input, 100);
|
|
171
|
-
assert.equal(parsed.output, 50);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("emitted JSON has the documented shape", () => {
|
|
175
|
-
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
176
|
-
emitTokenTelemetry(makeAssistantMessage());
|
|
177
|
-
const parsed = JSON.parse(captured[0].trimEnd());
|
|
178
|
-
const keys = Object.keys(parsed).sort();
|
|
179
|
-
assert.deepEqual(keys, [
|
|
180
|
-
"cacheHitRatio",
|
|
181
|
-
"cacheRead",
|
|
182
|
-
"cacheWrite",
|
|
183
|
-
"costTotal",
|
|
184
|
-
"input",
|
|
185
|
-
"model",
|
|
186
|
-
"output",
|
|
187
|
-
"stopReason",
|
|
188
|
-
"ts",
|
|
189
|
-
]);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("never throws — telemetry must not break the agent loop", () => {
|
|
193
|
-
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
194
|
-
// Force a write failure to exercise the swallow path.
|
|
195
|
-
process.stderr.write = (() => {
|
|
196
|
-
throw new Error("simulated stderr failure");
|
|
197
|
-
}) as typeof process.stderr.write;
|
|
198
|
-
assert.doesNotThrow(() => emitTokenTelemetry(makeAssistantMessage()));
|
|
199
|
-
});
|
|
200
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"leak-fixes-runtime.test.d.ts","sourceRoot":"","sources":["../../../src/components/__tests__/leak-fixes-runtime.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// Runtime regression tests for the long-running-session leak fixes.
|
|
2
|
-
//
|
|
3
|
-
// Replaces the source-grep file `src/tests/session-memory-leaks.test.ts`
|
|
4
|
-
// (deleted in #4875, tracked as #4873). The previous tests asserted on
|
|
5
|
-
// identifier presence (`_prevRender`, `_lastMessage`, `MAX_CHAT_COMPONENTS`)
|
|
6
|
-
// in source — a regression that set `MAX_CHAT_COMPONENTS = Number.MAX_SAFE_INTEGER`
|
|
7
|
-
// or replaced `setText` with an inline mutation would still pass.
|
|
8
|
-
//
|
|
9
|
-
// Each test below drives the actual component through a long-running scenario
|
|
10
|
-
// and asserts on observable behaviour.
|
|
11
|
-
import { describe, it, mock, beforeEach, afterEach } from "node:test";
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
import { Container } from "../../tui.js";
|
|
14
|
-
import { Loader } from "../loader.js";
|
|
15
|
-
import { Text } from "../text.js";
|
|
16
|
-
function makeMockTUI() {
|
|
17
|
-
return { requestRender: mock.fn() };
|
|
18
|
-
}
|
|
19
|
-
// ─── Container render-skip ──────────────────────────────────────────────
|
|
20
|
-
describe("Container.render skips work when output is unchanged", () => {
|
|
21
|
-
it("returns the SAME array reference across two renders with no changes", () => {
|
|
22
|
-
// The container caches `_prevRender` so doRender() can short-circuit
|
|
23
|
-
// the post-processing (image-line scan, applyLineResets, line diffs).
|
|
24
|
-
// The contract for the optimization is reference equality of the
|
|
25
|
-
// returned array — that's what the consumer in tui.ts checks.
|
|
26
|
-
const c = new Container();
|
|
27
|
-
c.addChild(new Text("hello", 0, 0));
|
|
28
|
-
const first = c.render(20);
|
|
29
|
-
const second = c.render(20);
|
|
30
|
-
assert.strictEqual(second, first, "Container must return the same array reference when content is unchanged " +
|
|
31
|
-
"(reference equality is the consumer-visible contract for the skip)");
|
|
32
|
-
});
|
|
33
|
-
it("returns a DIFFERENT array reference after addChild invalidates the cache", () => {
|
|
34
|
-
// If a regression caused `_prevRender` to never reset, downstream
|
|
35
|
-
// rendering would skip post-processing for new components — visible
|
|
36
|
-
// as missing/stale UI. Behaviourally we observe that adding a child
|
|
37
|
-
// breaks the reference-equality contract on the next render.
|
|
38
|
-
const c = new Container();
|
|
39
|
-
c.addChild(new Text("a", 0, 0));
|
|
40
|
-
const first = c.render(20);
|
|
41
|
-
c.addChild(new Text("b", 0, 0));
|
|
42
|
-
const second = c.render(20);
|
|
43
|
-
assert.notStrictEqual(second, first, "adding a child must invalidate the cached render reference");
|
|
44
|
-
});
|
|
45
|
-
it("returns a different reference after content changes", () => {
|
|
46
|
-
const t = new Text("hello", 0, 0);
|
|
47
|
-
const c = new Container();
|
|
48
|
-
c.addChild(t);
|
|
49
|
-
const first = c.render(20);
|
|
50
|
-
t.setText("world");
|
|
51
|
-
const second = c.render(20);
|
|
52
|
-
assert.notStrictEqual(second, first, "changing a child's text must produce a fresh array (not the cached one)");
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
// ─── Loader frame isolation ─────────────────────────────────────────────
|
|
56
|
-
describe("Loader does not mutate Text cache on every spinner tick", () => {
|
|
57
|
-
let tui;
|
|
58
|
-
let loader;
|
|
59
|
-
beforeEach(() => {
|
|
60
|
-
tui = makeMockTUI();
|
|
61
|
-
mock.timers.enable({ apis: ["setInterval"] });
|
|
62
|
-
});
|
|
63
|
-
afterEach(() => {
|
|
64
|
-
try {
|
|
65
|
-
loader?.stop();
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
/* best-effort */
|
|
69
|
-
}
|
|
70
|
-
mock.timers.reset();
|
|
71
|
-
});
|
|
72
|
-
it("does not invalidate Text's render cache across N spinner ticks", () => {
|
|
73
|
-
// Loader extends Text. Text caches its rendered lines keyed by
|
|
74
|
-
// (text, width). The leak was: the old Loader called setText()
|
|
75
|
-
// on every 80ms tick, which always cleared the cache, forcing a
|
|
76
|
-
// re-wrap of the message text.
|
|
77
|
-
// Behavioural test: render the loader, capture the cached array
|
|
78
|
-
// reference returned by Text.render, advance many ticks, and
|
|
79
|
-
// assert the underlying Text cache was NOT invalidated (we observe
|
|
80
|
-
// this via reference equality of the cached lines slice in the
|
|
81
|
-
// returned array — the second `result[*]` segment from super.render
|
|
82
|
-
// stays identity-stable when the cache survives).
|
|
83
|
-
loader = new Loader(tui, (s) => s, (s) => s, "the-message");
|
|
84
|
-
const before = loader.render(40);
|
|
85
|
-
// Run 50 frame intervals — the spinner should rotate, but the
|
|
86
|
-
// underlying message text wrapping must not change.
|
|
87
|
-
mock.timers.tick(80 * 50);
|
|
88
|
-
const after = loader.render(40);
|
|
89
|
-
// The message portion (everything after the first `""` padding line
|
|
90
|
-
// and after the spinner glyph + space prefix on the first content
|
|
91
|
-
// line) must be byte-identical across renders.
|
|
92
|
-
assert.equal(before.length, after.length, "render shape stable across ticks");
|
|
93
|
-
// Trailing message lines (index 2 onwards) come straight from Text's
|
|
94
|
-
// cache without any spinner mutation. They must be reference-equal
|
|
95
|
-
// substrings — same string contents byte-for-byte.
|
|
96
|
-
for (let i = 2; i < before.length; i++) {
|
|
97
|
-
assert.equal(after[i], before[i], `message line ${i} must remain byte-identical across spinner ticks ` +
|
|
98
|
-
`(cache must not be invalidated by frame rotation)`);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
it("setMessage invalidates Text cache and a new message is reflected", () => {
|
|
102
|
-
// Quiescence is conditional on the message being unchanged — we
|
|
103
|
-
// must still see updates when it actually changes. This is the
|
|
104
|
-
// counter-test that prevents a false-positive "cache always stable"
|
|
105
|
-
// fix that would freeze the loader text.
|
|
106
|
-
loader = new Loader(tui, (s) => s, (s) => s, "first");
|
|
107
|
-
const beforeFirstRender = loader.render(40).join("\n");
|
|
108
|
-
loader.setMessage("second");
|
|
109
|
-
const afterChange = loader.render(40).join("\n");
|
|
110
|
-
assert.ok(afterChange.includes("second"), "new message must render");
|
|
111
|
-
assert.ok(!afterChange.includes("first") || beforeFirstRender !== afterChange, "render output must change when message changes");
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
// ─── Text.setText early-return guard ────────────────────────────────────
|
|
115
|
-
describe("Text.setText returns early when value is unchanged", () => {
|
|
116
|
-
it("does not invalidate the cached render when setText receives the same value", () => {
|
|
117
|
-
// The setText early-return is the runtime guard that keeps the
|
|
118
|
-
// Loader-and-Text cache stable. We observe it via reference equality
|
|
119
|
-
// of the rendered output across a setText(SAME) call.
|
|
120
|
-
const t = new Text("identical", 0, 0);
|
|
121
|
-
const first = t.render(30);
|
|
122
|
-
t.setText("identical"); // same value — must NOT clear the cache
|
|
123
|
-
const second = t.render(30);
|
|
124
|
-
assert.strictEqual(second, first, "setText with an unchanged value must leave the render cache intact");
|
|
125
|
-
});
|
|
126
|
-
it("invalidates cache when the value actually changes", () => {
|
|
127
|
-
const t = new Text("one", 0, 0);
|
|
128
|
-
const first = t.render(30);
|
|
129
|
-
t.setText("two");
|
|
130
|
-
const second = t.render(30);
|
|
131
|
-
assert.notStrictEqual(second, first, "setText with a new value must produce a fresh render result");
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
// ─── Heap growth bound (gated on --expose-gc) ───────────────────────────
|
|
135
|
-
describe("Long-running render loop does not leak heap (forced-GC bound)", () => {
|
|
136
|
-
const hasGc = typeof globalThis.gc === "function";
|
|
137
|
-
it("renders a Container 5000 times with stable heapUsed (within 2x baseline)", { skip: !hasGc ? "requires --expose-gc to drive deterministic GC between snapshots" : false }, () => {
|
|
138
|
-
// This is the on-the-wire memory-soak test. The naive version
|
|
139
|
-
// (no forced GC) is flaky because Node's GC is opportunistic;
|
|
140
|
-
// any background allocation can shift heapUsed past an absolute
|
|
141
|
-
// bound. We instead force GC between snapshots and assert a
|
|
142
|
-
// RELATIVE bound: post-iteration heap must be within 2x of the
|
|
143
|
-
// post-warmup baseline.
|
|
144
|
-
const gc = globalThis.gc;
|
|
145
|
-
const c = new Container();
|
|
146
|
-
const t = new Text("steady-state", 0, 0);
|
|
147
|
-
c.addChild(t);
|
|
148
|
-
// Warm up so the JIT and any lazy initial allocations settle.
|
|
149
|
-
for (let i = 0; i < 200; i++)
|
|
150
|
-
c.render(40);
|
|
151
|
-
gc();
|
|
152
|
-
const baseline = process.memoryUsage().heapUsed;
|
|
153
|
-
for (let i = 0; i < 5000; i++)
|
|
154
|
-
c.render(40);
|
|
155
|
-
gc();
|
|
156
|
-
const after = process.memoryUsage().heapUsed;
|
|
157
|
-
assert.ok(after < baseline * 2, `heapUsed must stay within 2x baseline after 5000 renders ` +
|
|
158
|
-
`(baseline=${baseline}B, after=${after}B, ratio=${(after / baseline).toFixed(2)})`);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
//# sourceMappingURL=leak-fixes-runtime.test.js.map
|