gsd-pi 2.78.0 → 2.78.1-dev.8a893322c
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 +60 -23
- package/dist/bundled-resource-path.d.ts +7 -0
- package/dist/bundled-resource-path.js +34 -2
- package/dist/claude-cli-check.js +104 -33
- package/dist/cli-policy.d.ts +13 -0
- package/dist/cli-policy.js +17 -0
- package/dist/cli.js +95 -55
- package/dist/headless-query.d.ts +22 -0
- package/dist/headless-query.js +43 -8
- package/dist/headless.d.ts +10 -0
- package/dist/headless.js +16 -1
- package/dist/loader.js +9 -13
- package/dist/onboarding.d.ts +10 -0
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +5 -2
- package/dist/resource-loader.d.ts +5 -2
- package/dist/resource-loader.js +30 -13
- package/dist/resources/.managed-resources-content-hash +1 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +128 -32
- package/dist/resources/extensions/google-search/index.js +2 -6
- package/dist/resources/extensions/gsd/auto/loop.js +23 -0
- package/dist/resources/extensions/gsd/auto/phases.js +5 -13
- package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +5 -6
- package/dist/resources/extensions/gsd/auto-dashboard.js +3 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +63 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +60 -13
- package/dist/resources/extensions/gsd/auto.js +14 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +112 -31
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
- package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +22 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +45 -8
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +121 -3
- package/dist/resources/extensions/gsd/commands/catalog.js +76 -5
- package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +8 -0
- package/dist/resources/extensions/gsd/commands-config.js +3 -2
- package/dist/resources/extensions/gsd/commands-extensions.js +46 -3
- package/dist/resources/extensions/gsd/commands-handlers.js +3 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
- package/dist/resources/extensions/gsd/commands-worktree.js +309 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +10 -8
- package/dist/resources/extensions/gsd/git-service.js +12 -5
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +25 -24
- package/dist/resources/extensions/gsd/home-dir.js +16 -0
- package/dist/resources/extensions/gsd/key-manager.js +2 -1
- package/dist/resources/extensions/gsd/memory-store.js +66 -31
- package/dist/resources/extensions/gsd/migrate/command.js +3 -2
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
- package/dist/resources/extensions/gsd/model-router.js +114 -9
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
- package/dist/resources/extensions/gsd/preferences-models.js +91 -15
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
- package/dist/resources/extensions/gsd/preferences.js +5 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +10 -0
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +10 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
- package/dist/resources/extensions/gsd/state.js +42 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
- package/dist/resources/extensions/gsd/unit-context-manifest.js +29 -4
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
- package/dist/resources/extensions/gsd/worktree-command.js +26 -46
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -1
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -13
- package/dist/resources/extensions/gsd/worktree-root.js +124 -0
- package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
- package/dist/resources/extensions/gsd/worktree.js +4 -115
- package/dist/resources/extensions/mcp-client/index.js +6 -9
- package/dist/resources/extensions/ollama/index.js +15 -2
- package/dist/resources/extensions/ollama/model-capabilities.js +31 -0
- package/dist/resources/extensions/ollama/ollama-client.js +40 -4
- package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
- package/dist/resources/extensions/subagent/index.js +324 -178
- package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/rtk-shared.d.ts +3 -0
- package/dist/rtk-shared.js +17 -0
- package/dist/rtk.d.ts +2 -5
- package/dist/rtk.js +3 -20
- package/dist/runtime-checks.d.ts +27 -0
- package/dist/runtime-checks.js +38 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- 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 +44 -4
- 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 +4 -2
- 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 +15 -15
- 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/2556.0527fea66e123b7f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-9bf2e0c50fb2ca05.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.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 +2 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/welcome-screen.js +27 -1
- package/dist/worktree-cli.d.ts +1 -0
- package/dist/worktree-cli.js +9 -3
- package/dist/worktree-status-banner.d.ts +1 -0
- package/dist/worktree-status-banner.js +132 -0
- package/package.json +1 -3
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
- package/packages/mcp-server/dist/alias-telemetry.js +30 -0
- package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +74 -46
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
- package/packages/mcp-server/src/alias-telemetry.ts +30 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +78 -0
- package/packages/mcp-server/src/workflow-tools.ts +93 -58
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +13 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
- package/packages/pi-ai/src/types.ts +13 -0
- package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +4 -0
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
- package/packages/pi-coding-agent/src/core/messages.ts +4 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +130 -30
- package/src/resources/extensions/google-search/index.ts +2 -9
- package/src/resources/extensions/gsd/auto/loop.ts +24 -2
- package/src/resources/extensions/gsd/auto/phases.ts +6 -14
- package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
- package/src/resources/extensions/gsd/auto/session.ts +5 -6
- package/src/resources/extensions/gsd/auto/types.ts +1 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +3 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +60 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +82 -12
- package/src/resources/extensions/gsd/auto.ts +14 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +121 -31
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
- package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +20 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +50 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +141 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +82 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands-config.ts +3 -2
- package/src/resources/extensions/gsd/commands-extensions.ts +43 -3
- package/src/resources/extensions/gsd/commands-handlers.ts +3 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
- package/src/resources/extensions/gsd/commands-worktree.ts +383 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +12 -7
- package/src/resources/extensions/gsd/git-service.ts +13 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -2
- package/src/resources/extensions/gsd/guided-flow.ts +27 -26
- package/src/resources/extensions/gsd/home-dir.ts +19 -0
- package/src/resources/extensions/gsd/journal.ts +4 -1
- package/src/resources/extensions/gsd/key-manager.ts +2 -1
- package/src/resources/extensions/gsd/memory-store.ts +81 -28
- package/src/resources/extensions/gsd/migrate/command.ts +3 -2
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
- package/src/resources/extensions/gsd/model-router.ts +172 -9
- package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
- package/src/resources/extensions/gsd/preferences-models.ts +101 -15
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +10 -0
- package/src/resources/extensions/gsd/prompts/refine-slice.md +10 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
- package/src/resources/extensions/gsd/state.ts +42 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +24 -5
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +50 -27
- package/src/resources/extensions/gsd/tests/commands-extensions-version-compare.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +25 -65
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/home-dir.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +72 -1
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
- package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +17 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +34 -33
- package/src/resources/extensions/gsd/tests/worktree.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +131 -1
- package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +44 -12
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
- package/src/resources/extensions/gsd/worktree-command.ts +31 -44
- package/src/resources/extensions/gsd/worktree-manager.ts +40 -1
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -14
- package/src/resources/extensions/gsd/worktree-root.ts +144 -0
- package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
- package/src/resources/extensions/gsd/worktree.ts +8 -119
- package/src/resources/extensions/mcp-client/index.ts +6 -10
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
- package/src/resources/extensions/ollama/index.ts +16 -2
- package/src/resources/extensions/ollama/model-capabilities.ts +34 -0
- package/src/resources/extensions/ollama/ollama-client.ts +41 -4
- package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +96 -0
- package/src/resources/extensions/ollama/tests/ollama-client-timeout-env.test.ts +147 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
- package/src/resources/extensions/subagent/index.ts +165 -7
- package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
- package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
- /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → QK8fABiGPmonfTgboN0Y9}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → QK8fABiGPmonfTgboN0Y9}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
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
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { buildTokenTelemetryRecord, emitTokenTelemetry } from "../core/token-telemetry.js";
|
|
10
|
+
function makeAssistantMessage(overrides = {}) {
|
|
11
|
+
return {
|
|
12
|
+
role: "assistant",
|
|
13
|
+
content: [],
|
|
14
|
+
api: "anthropic-messages",
|
|
15
|
+
provider: "anthropic",
|
|
16
|
+
model: "claude-sonnet-4-6",
|
|
17
|
+
usage: {
|
|
18
|
+
input: 100,
|
|
19
|
+
output: 50,
|
|
20
|
+
cacheRead: 0,
|
|
21
|
+
cacheWrite: 0,
|
|
22
|
+
totalTokens: 150,
|
|
23
|
+
cost: { input: 0.3, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 1.05 },
|
|
24
|
+
},
|
|
25
|
+
stopReason: "stop",
|
|
26
|
+
timestamp: 1700000000000,
|
|
27
|
+
...overrides,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// ─── buildTokenTelemetryRecord ─────────────────────────────────────────────
|
|
31
|
+
describe("buildTokenTelemetryRecord", () => {
|
|
32
|
+
test("captures all fields from a typical message", () => {
|
|
33
|
+
const msg = makeAssistantMessage();
|
|
34
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
35
|
+
assert.deepEqual(record, {
|
|
36
|
+
ts: 1700000000000,
|
|
37
|
+
model: "claude-sonnet-4-6",
|
|
38
|
+
stopReason: "stop",
|
|
39
|
+
input: 100,
|
|
40
|
+
output: 50,
|
|
41
|
+
cacheRead: 0,
|
|
42
|
+
cacheWrite: 0,
|
|
43
|
+
costTotal: 1.05,
|
|
44
|
+
cacheHitRatio: 0,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
test("cacheHitRatio = read / (read + input) when both present", () => {
|
|
48
|
+
const msg = makeAssistantMessage({
|
|
49
|
+
usage: {
|
|
50
|
+
input: 200,
|
|
51
|
+
output: 50,
|
|
52
|
+
cacheRead: 800,
|
|
53
|
+
cacheWrite: 0,
|
|
54
|
+
totalTokens: 1050,
|
|
55
|
+
cost: { input: 0.6, output: 0.75, cacheRead: 0.24, cacheWrite: 0, total: 1.59 },
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
59
|
+
assert.equal(record.cacheRead, 800);
|
|
60
|
+
assert.equal(record.input, 200);
|
|
61
|
+
assert.equal(record.cacheHitRatio, 0.8);
|
|
62
|
+
});
|
|
63
|
+
test("cacheHitRatio = 0 when both read and input are 0 (no division-by-zero)", () => {
|
|
64
|
+
const msg = makeAssistantMessage({
|
|
65
|
+
usage: {
|
|
66
|
+
input: 0,
|
|
67
|
+
output: 50,
|
|
68
|
+
cacheRead: 0,
|
|
69
|
+
cacheWrite: 0,
|
|
70
|
+
totalTokens: 50,
|
|
71
|
+
cost: { input: 0, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 0.75 },
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
75
|
+
assert.equal(record.cacheHitRatio, 0);
|
|
76
|
+
});
|
|
77
|
+
test("cacheHitRatio = 1 when only cacheRead present (full hit)", () => {
|
|
78
|
+
const msg = makeAssistantMessage({
|
|
79
|
+
usage: {
|
|
80
|
+
input: 0,
|
|
81
|
+
output: 50,
|
|
82
|
+
cacheRead: 5000,
|
|
83
|
+
cacheWrite: 0,
|
|
84
|
+
totalTokens: 5050,
|
|
85
|
+
cost: { input: 0, output: 0.75, cacheRead: 1.5, cacheWrite: 0, total: 2.25 },
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
89
|
+
assert.equal(record.cacheHitRatio, 1);
|
|
90
|
+
});
|
|
91
|
+
test("cacheWrite is captured (the cache-miss-with-cache-control case from #5019)", () => {
|
|
92
|
+
const msg = makeAssistantMessage({
|
|
93
|
+
usage: {
|
|
94
|
+
input: 50,
|
|
95
|
+
output: 100,
|
|
96
|
+
cacheRead: 0,
|
|
97
|
+
cacheWrite: 5000,
|
|
98
|
+
totalTokens: 5150,
|
|
99
|
+
cost: { input: 0.15, output: 1.5, cacheRead: 0, cacheWrite: 18.75, total: 20.4 },
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
103
|
+
assert.equal(record.cacheWrite, 5000);
|
|
104
|
+
assert.equal(record.cacheHitRatio, 0, "no read = ratio 0 even when write is large");
|
|
105
|
+
});
|
|
106
|
+
test("error stopReason is captured verbatim", () => {
|
|
107
|
+
const msg = makeAssistantMessage({ stopReason: "error", errorMessage: "rate_limit" });
|
|
108
|
+
assert.equal(buildTokenTelemetryRecord(msg).stopReason, "error");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// ─── emitTokenTelemetry ────────────────────────────────────────────────────
|
|
112
|
+
describe("emitTokenTelemetry", () => {
|
|
113
|
+
let captured;
|
|
114
|
+
let originalWrite;
|
|
115
|
+
let originalEnv;
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
captured = [];
|
|
118
|
+
originalWrite = process.stderr.write.bind(process.stderr);
|
|
119
|
+
// Replace stderr.write with a capture; preserve the same return contract.
|
|
120
|
+
process.stderr.write = ((chunk) => {
|
|
121
|
+
captured.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
originalEnv = process.env.PI_TOKEN_TELEMETRY;
|
|
125
|
+
});
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
process.stderr.write = originalWrite;
|
|
128
|
+
if (originalEnv === undefined) {
|
|
129
|
+
delete process.env.PI_TOKEN_TELEMETRY;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
process.env.PI_TOKEN_TELEMETRY = originalEnv;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
test("silent by default (env var unset)", () => {
|
|
136
|
+
delete process.env.PI_TOKEN_TELEMETRY;
|
|
137
|
+
emitTokenTelemetry(makeAssistantMessage());
|
|
138
|
+
assert.equal(captured.length, 0);
|
|
139
|
+
});
|
|
140
|
+
test("silent when env var has any non-'1' value", () => {
|
|
141
|
+
process.env.PI_TOKEN_TELEMETRY = "true"; // not literally "1"
|
|
142
|
+
emitTokenTelemetry(makeAssistantMessage());
|
|
143
|
+
assert.equal(captured.length, 0, "only literal '1' should enable telemetry");
|
|
144
|
+
});
|
|
145
|
+
test("emits a single JSON line when PI_TOKEN_TELEMETRY=1", () => {
|
|
146
|
+
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
147
|
+
emitTokenTelemetry(makeAssistantMessage());
|
|
148
|
+
assert.equal(captured.length, 1);
|
|
149
|
+
assert.ok(captured[0].endsWith("\n"), "line must terminate with newline");
|
|
150
|
+
const parsed = JSON.parse(captured[0].trimEnd());
|
|
151
|
+
assert.equal(parsed.model, "claude-sonnet-4-6");
|
|
152
|
+
assert.equal(parsed.input, 100);
|
|
153
|
+
assert.equal(parsed.output, 50);
|
|
154
|
+
});
|
|
155
|
+
test("emitted JSON has the documented shape", () => {
|
|
156
|
+
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
157
|
+
emitTokenTelemetry(makeAssistantMessage());
|
|
158
|
+
const parsed = JSON.parse(captured[0].trimEnd());
|
|
159
|
+
const keys = Object.keys(parsed).sort();
|
|
160
|
+
assert.deepEqual(keys, [
|
|
161
|
+
"cacheHitRatio",
|
|
162
|
+
"cacheRead",
|
|
163
|
+
"cacheWrite",
|
|
164
|
+
"costTotal",
|
|
165
|
+
"input",
|
|
166
|
+
"model",
|
|
167
|
+
"output",
|
|
168
|
+
"stopReason",
|
|
169
|
+
"ts",
|
|
170
|
+
]);
|
|
171
|
+
});
|
|
172
|
+
test("never throws — telemetry must not break the agent loop", () => {
|
|
173
|
+
process.env.PI_TOKEN_TELEMETRY = "1";
|
|
174
|
+
// Force a write failure to exercise the swallow path.
|
|
175
|
+
process.stderr.write = (() => {
|
|
176
|
+
throw new Error("simulated stderr failure");
|
|
177
|
+
});
|
|
178
|
+
assert.doesNotThrow(() => emitTokenTelemetry(makeAssistantMessage()));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=token-telemetry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-telemetry.test.js","sourceRoot":"","sources":["../../src/tests/token-telemetry.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @gsd/pi-coding-agent + token-telemetry.test — coverage for #5023.\n// Verifies the env-gated emitter:\n// - is silent by default (no behavior change for existing users)\n// - emits a single valid JSON line when PI_TOKEN_TELEMETRY=1\n// - record shape captures the cache breakdown the providers already extract\n// - cacheHitRatio math is correct, including the no-input edge case\n\nimport { describe, test, beforeEach, afterEach } from \"node:test\";\nimport assert from \"node:assert/strict\";\n\nimport type { AssistantMessage } from \"@gsd/pi-ai\";\n\nimport { buildTokenTelemetryRecord, emitTokenTelemetry } from \"../core/token-telemetry.js\";\n\nfunction makeAssistantMessage(overrides: Partial<AssistantMessage> = {}): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"anthropic\",\n\t\tmodel: \"claude-sonnet-4-6\",\n\t\tusage: {\n\t\t\tinput: 100,\n\t\t\toutput: 50,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 150,\n\t\t\tcost: { input: 0.3, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 1.05 },\n\t\t},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: 1700000000000,\n\t\t...overrides,\n\t};\n}\n\n// ─── buildTokenTelemetryRecord ─────────────────────────────────────────────\n\ndescribe(\"buildTokenTelemetryRecord\", () => {\n\ttest(\"captures all fields from a typical message\", () => {\n\t\tconst msg = makeAssistantMessage();\n\t\tconst record = buildTokenTelemetryRecord(msg);\n\t\tassert.deepEqual(record, {\n\t\t\tts: 1700000000000,\n\t\t\tmodel: \"claude-sonnet-4-6\",\n\t\t\tstopReason: \"stop\",\n\t\t\tinput: 100,\n\t\t\toutput: 50,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\tcostTotal: 1.05,\n\t\t\tcacheHitRatio: 0,\n\t\t});\n\t});\n\n\ttest(\"cacheHitRatio = read / (read + input) when both present\", () => {\n\t\tconst msg = makeAssistantMessage({\n\t\t\tusage: {\n\t\t\t\tinput: 200,\n\t\t\t\toutput: 50,\n\t\t\t\tcacheRead: 800,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 1050,\n\t\t\t\tcost: { input: 0.6, output: 0.75, cacheRead: 0.24, cacheWrite: 0, total: 1.59 },\n\t\t\t},\n\t\t});\n\t\tconst record = buildTokenTelemetryRecord(msg);\n\t\tassert.equal(record.cacheRead, 800);\n\t\tassert.equal(record.input, 200);\n\t\tassert.equal(record.cacheHitRatio, 0.8);\n\t});\n\n\ttest(\"cacheHitRatio = 0 when both read and input are 0 (no division-by-zero)\", () => {\n\t\tconst msg = makeAssistantMessage({\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 50,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 50,\n\t\t\t\tcost: { input: 0, output: 0.75, cacheRead: 0, cacheWrite: 0, total: 0.75 },\n\t\t\t},\n\t\t});\n\t\tconst record = buildTokenTelemetryRecord(msg);\n\t\tassert.equal(record.cacheHitRatio, 0);\n\t});\n\n\ttest(\"cacheHitRatio = 1 when only cacheRead present (full hit)\", () => {\n\t\tconst msg = makeAssistantMessage({\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 50,\n\t\t\t\tcacheRead: 5000,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 5050,\n\t\t\t\tcost: { input: 0, output: 0.75, cacheRead: 1.5, cacheWrite: 0, total: 2.25 },\n\t\t\t},\n\t\t});\n\t\tconst record = buildTokenTelemetryRecord(msg);\n\t\tassert.equal(record.cacheHitRatio, 1);\n\t});\n\n\ttest(\"cacheWrite is captured (the cache-miss-with-cache-control case from #5019)\", () => {\n\t\tconst msg = makeAssistantMessage({\n\t\t\tusage: {\n\t\t\t\tinput: 50,\n\t\t\t\toutput: 100,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 5000,\n\t\t\t\ttotalTokens: 5150,\n\t\t\t\tcost: { input: 0.15, output: 1.5, cacheRead: 0, cacheWrite: 18.75, total: 20.4 },\n\t\t\t},\n\t\t});\n\t\tconst record = buildTokenTelemetryRecord(msg);\n\t\tassert.equal(record.cacheWrite, 5000);\n\t\tassert.equal(record.cacheHitRatio, 0, \"no read = ratio 0 even when write is large\");\n\t});\n\n\ttest(\"error stopReason is captured verbatim\", () => {\n\t\tconst msg = makeAssistantMessage({ stopReason: \"error\", errorMessage: \"rate_limit\" });\n\t\tassert.equal(buildTokenTelemetryRecord(msg).stopReason, \"error\");\n\t});\n});\n\n// ─── emitTokenTelemetry ────────────────────────────────────────────────────\n\ndescribe(\"emitTokenTelemetry\", () => {\n\tlet captured: string[];\n\tlet originalWrite: typeof process.stderr.write;\n\tlet originalEnv: string | undefined;\n\n\tbeforeEach(() => {\n\t\tcaptured = [];\n\t\toriginalWrite = process.stderr.write.bind(process.stderr);\n\t\t// Replace stderr.write with a capture; preserve the same return contract.\n\t\tprocess.stderr.write = ((chunk: string | Uint8Array): boolean => {\n\t\t\tcaptured.push(typeof chunk === \"string\" ? chunk : Buffer.from(chunk).toString());\n\t\t\treturn true;\n\t\t}) as typeof process.stderr.write;\n\t\toriginalEnv = process.env.PI_TOKEN_TELEMETRY;\n\t});\n\n\tafterEach(() => {\n\t\tprocess.stderr.write = originalWrite;\n\t\tif (originalEnv === undefined) {\n\t\t\tdelete process.env.PI_TOKEN_TELEMETRY;\n\t\t} else {\n\t\t\tprocess.env.PI_TOKEN_TELEMETRY = originalEnv;\n\t\t}\n\t});\n\n\ttest(\"silent by default (env var unset)\", () => {\n\t\tdelete process.env.PI_TOKEN_TELEMETRY;\n\t\temitTokenTelemetry(makeAssistantMessage());\n\t\tassert.equal(captured.length, 0);\n\t});\n\n\ttest(\"silent when env var has any non-'1' value\", () => {\n\t\tprocess.env.PI_TOKEN_TELEMETRY = \"true\"; // not literally \"1\"\n\t\temitTokenTelemetry(makeAssistantMessage());\n\t\tassert.equal(captured.length, 0, \"only literal '1' should enable telemetry\");\n\t});\n\n\ttest(\"emits a single JSON line when PI_TOKEN_TELEMETRY=1\", () => {\n\t\tprocess.env.PI_TOKEN_TELEMETRY = \"1\";\n\t\temitTokenTelemetry(makeAssistantMessage());\n\t\tassert.equal(captured.length, 1);\n\t\tassert.ok(captured[0].endsWith(\"\\n\"), \"line must terminate with newline\");\n\t\tconst parsed = JSON.parse(captured[0].trimEnd());\n\t\tassert.equal(parsed.model, \"claude-sonnet-4-6\");\n\t\tassert.equal(parsed.input, 100);\n\t\tassert.equal(parsed.output, 50);\n\t});\n\n\ttest(\"emitted JSON has the documented shape\", () => {\n\t\tprocess.env.PI_TOKEN_TELEMETRY = \"1\";\n\t\temitTokenTelemetry(makeAssistantMessage());\n\t\tconst parsed = JSON.parse(captured[0].trimEnd());\n\t\tconst keys = Object.keys(parsed).sort();\n\t\tassert.deepEqual(keys, [\n\t\t\t\"cacheHitRatio\",\n\t\t\t\"cacheRead\",\n\t\t\t\"cacheWrite\",\n\t\t\t\"costTotal\",\n\t\t\t\"input\",\n\t\t\t\"model\",\n\t\t\t\"output\",\n\t\t\t\"stopReason\",\n\t\t\t\"ts\",\n\t\t]);\n\t});\n\n\ttest(\"never throws — telemetry must not break the agent loop\", () => {\n\t\tprocess.env.PI_TOKEN_TELEMETRY = \"1\";\n\t\t// Force a write failure to exercise the swallow path.\n\t\tprocess.stderr.write = (() => {\n\t\t\tthrow new Error(\"simulated stderr failure\");\n\t\t}) as typeof process.stderr.write;\n\t\tassert.doesNotThrow(() => emitTokenTelemetry(makeAssistantMessage()));\n\t});\n});\n"]}
|
|
@@ -78,6 +78,7 @@ import { getLatestCompactionEntry } from "./session-manager.js";
|
|
|
78
78
|
import type { SettingsManager } from "./settings-manager.js";
|
|
79
79
|
import { BUILTIN_SLASH_COMMANDS, type SlashCommandInfo, type SlashCommandLocation } from "./slash-commands.js";
|
|
80
80
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
81
|
+
import { emitTokenTelemetry } from "./token-telemetry.js";
|
|
81
82
|
import type { BashOperations } from "./tools/bash.js";
|
|
82
83
|
import { createAllTools } from "./tools/index.js";
|
|
83
84
|
|
|
@@ -470,6 +471,12 @@ export class AgentSession {
|
|
|
470
471
|
this._cumulativeOutputTokens += assistantMsg.usage?.output ?? 0;
|
|
471
472
|
this._cumulativeToolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
472
473
|
|
|
474
|
+
// Per-call token telemetry (off by default; gated by PI_TOKEN_TELEMETRY=1).
|
|
475
|
+
// Note: a turn that retries emits one record per attempt — group by
|
|
476
|
+
// session/turn downstream if you want a deduplicated view. Both records
|
|
477
|
+
// are valid (each was a billed/attempted API call). #5023
|
|
478
|
+
emitTokenTelemetry(assistantMsg);
|
|
479
|
+
|
|
473
480
|
if (assistantMsg.stopReason !== "error") {
|
|
474
481
|
this._compactionOrchestrator.clearOverflowRecovery();
|
|
475
482
|
}
|
|
@@ -207,6 +207,10 @@ export function convertToLlm(messages: AgentMessage[]): Message[] {
|
|
|
207
207
|
{ type: "text" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },
|
|
208
208
|
],
|
|
209
209
|
timestamp: m.timestamp,
|
|
210
|
+
// Stable point in the conversation — earn prompt-cache reads on the
|
|
211
|
+
// summary + kept history block on every post-compaction turn until
|
|
212
|
+
// the next compaction. (#5027)
|
|
213
|
+
cacheBreakpoint: true,
|
|
210
214
|
};
|
|
211
215
|
case "user":
|
|
212
216
|
case "assistant":
|
|
@@ -5,14 +5,17 @@ import { getApiProvider } from "@gsd/pi-ai";
|
|
|
5
5
|
import { AuthStorage, type AuthStorageData } from "./auth-storage.js";
|
|
6
6
|
import { ModelRegistry } from "./model-registry.js";
|
|
7
7
|
|
|
8
|
-
function createRegistry(
|
|
8
|
+
function createRegistry(
|
|
9
|
+
hasAuthFn?: (provider: string) => boolean,
|
|
10
|
+
getApiKeyFn?: (provider: string) => Promise<string | undefined>,
|
|
11
|
+
): ModelRegistry {
|
|
9
12
|
const authStorage = {
|
|
10
13
|
setFallbackResolver: () => {},
|
|
11
14
|
onCredentialChange: () => {},
|
|
12
15
|
getOAuthProviders: () => [],
|
|
13
16
|
get: () => undefined,
|
|
14
17
|
hasAuth: hasAuthFn ?? (() => false),
|
|
15
|
-
getApiKey: async () => undefined,
|
|
18
|
+
getApiKey: async (provider: string) => getApiKeyFn ? getApiKeyFn(provider) : undefined,
|
|
16
19
|
} as unknown as AuthStorage;
|
|
17
20
|
|
|
18
21
|
return new ModelRegistry(authStorage, "");
|
|
@@ -311,6 +314,12 @@ describe("ModelRegistry authMode — isProviderRequestReady", () => {
|
|
|
311
314
|
const registry = createRegistry(() => true);
|
|
312
315
|
assert.equal(registry.isProviderRequestReady("anthropic"), true);
|
|
313
316
|
});
|
|
317
|
+
|
|
318
|
+
it("returns false for denylisted providers even when auth exists", () => {
|
|
319
|
+
const registry = createRegistry(() => true);
|
|
320
|
+
registry.setDisabledModelProviders(["anthropic"]);
|
|
321
|
+
assert.equal(registry.isProviderRequestReady("anthropic"), false);
|
|
322
|
+
});
|
|
314
323
|
});
|
|
315
324
|
|
|
316
325
|
// ─── isReady callback ─────────────────────────────────────────────────────────
|
|
@@ -414,6 +423,17 @@ describe("ModelRegistry authMode — getAvailable", () => {
|
|
|
414
423
|
assert.equal(available.length, 0);
|
|
415
424
|
});
|
|
416
425
|
|
|
426
|
+
it("excludes denylisted providers from available models", () => {
|
|
427
|
+
const registry = createRegistry(() => true);
|
|
428
|
+
registry.setDisabledModelProviders(["google-gemini-cli"]);
|
|
429
|
+
const available = registry.getAvailable();
|
|
430
|
+
assert.equal(
|
|
431
|
+
available.some((m) => m.provider === "google-gemini-cli"),
|
|
432
|
+
false,
|
|
433
|
+
"google-gemini-cli models must be hidden when provider is denylisted",
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
417
437
|
it("prunes Codex models removed from ChatGPT-backed openai-codex OAuth", () => {
|
|
418
438
|
const registry = createInMemoryRegistry({
|
|
419
439
|
"openai-codex": {
|
|
@@ -481,6 +501,16 @@ describe("ModelRegistry authMode — getApiKey", () => {
|
|
|
481
501
|
const key = await registry.getApiKeyForProvider("anthropic");
|
|
482
502
|
assert.equal(key, undefined);
|
|
483
503
|
});
|
|
504
|
+
|
|
505
|
+
it("still resolves provider keys for denylisted providers", async () => {
|
|
506
|
+
const registry = createRegistry(
|
|
507
|
+
() => true,
|
|
508
|
+
async (provider: string) => provider === "google-gemini-cli" ? "ya29.test-token" : undefined,
|
|
509
|
+
);
|
|
510
|
+
registry.setDisabledModelProviders(["google-gemini-cli"]);
|
|
511
|
+
const key = await registry.getApiKeyForProvider("google-gemini-cli");
|
|
512
|
+
assert.equal(key, "ya29.test-token");
|
|
513
|
+
});
|
|
484
514
|
});
|
|
485
515
|
|
|
486
516
|
// ─── streamSimple apiKey stripping ────────────────────────────────────────────
|
|
@@ -247,6 +247,7 @@ export class ModelRegistry {
|
|
|
247
247
|
private discoveryCache: ModelDiscoveryCache;
|
|
248
248
|
private customProviderApiKeys: Map<string, string> = new Map();
|
|
249
249
|
private registeredProviders: Map<string, ProviderConfigInput> = new Map();
|
|
250
|
+
private disabledModelProviders: Set<string> = new Set();
|
|
250
251
|
private loadError: string | undefined = undefined;
|
|
251
252
|
|
|
252
253
|
constructor(
|
|
@@ -553,6 +554,25 @@ export class ModelRegistry {
|
|
|
553
554
|
return this.models.filter((m) => this.isProviderRequestReady(m.provider));
|
|
554
555
|
}
|
|
555
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Set provider IDs that should be excluded from model selection/routing.
|
|
559
|
+
* This does not affect direct tool auth flows that resolve provider keys.
|
|
560
|
+
*/
|
|
561
|
+
setDisabledModelProviders(providers: string[]): void {
|
|
562
|
+
this.disabledModelProviders = new Set(
|
|
563
|
+
providers
|
|
564
|
+
.map((provider) => provider.trim().toLowerCase())
|
|
565
|
+
.filter((provider) => provider.length > 0),
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Get current provider denylist used for model selection/routing.
|
|
571
|
+
*/
|
|
572
|
+
getDisabledModelProviders(): string[] {
|
|
573
|
+
return Array.from(this.disabledModelProviders);
|
|
574
|
+
}
|
|
575
|
+
|
|
556
576
|
/**
|
|
557
577
|
* Get auth mode for a provider.
|
|
558
578
|
* Defaults to "apiKey" for built-ins and providers without explicit mode.
|
|
@@ -570,6 +590,7 @@ export class ModelRegistry {
|
|
|
570
590
|
* Whether a provider can be used for requests/fallback without hard auth gating.
|
|
571
591
|
*/
|
|
572
592
|
isProviderRequestReady(provider: string): boolean {
|
|
593
|
+
if (this.disabledModelProviders.has(provider.trim().toLowerCase())) return false;
|
|
573
594
|
const config = this.registeredProviders.get(provider);
|
|
574
595
|
if (config?.isReady) return config.isReady();
|
|
575
596
|
const authMode = this.getProviderAuthMode(provider);
|
|
@@ -55,6 +55,19 @@ export interface BuildSystemPromptOptions {
|
|
|
55
55
|
* exception and the session stays consistent.
|
|
56
56
|
*/
|
|
57
57
|
skillFilter?: (skill: Skill) => boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Append a `Current date and time: <toLocaleString>` line to the system
|
|
60
|
+
* prompt. Default: `false`.
|
|
61
|
+
*
|
|
62
|
+
* Anthropic prompt caching matches on byte-for-byte prefix equality.
|
|
63
|
+
* Embedding a per-call timestamp in the system prompt invalidates the
|
|
64
|
+
* cache on every request, forcing a full re-write that costs *more*
|
|
65
|
+
* than an uncached call (cache-write premium). Most agentic flows do
|
|
66
|
+
* not need wall-clock awareness in the system prompt — opt in only
|
|
67
|
+
* when the consumer genuinely needs it (e.g. a clock-sensitive agent),
|
|
68
|
+
* and inject it via a non-cached channel (user message) when possible.
|
|
69
|
+
*/
|
|
70
|
+
includeDateTime?: boolean;
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
/** Build the system prompt with tools, guidelines, and context */
|
|
@@ -69,20 +82,25 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|
|
69
82
|
contextFiles: providedContextFiles,
|
|
70
83
|
skills: providedSkills,
|
|
71
84
|
skillFilter,
|
|
85
|
+
includeDateTime = false,
|
|
72
86
|
} = options;
|
|
73
87
|
const resolvedCwd = toPosixPath(cwd ?? process.cwd());
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
// Per-call timestamps invalidate Anthropic prompt caching (the cache
|
|
90
|
+
// matches on byte-for-byte prefix equality). Compute lazily and only
|
|
91
|
+
// when explicitly opted in via `includeDateTime`.
|
|
92
|
+
const dateTimeLine = includeDateTime
|
|
93
|
+
? `\nCurrent date and time: ${new Date().toLocaleString("en-US", {
|
|
94
|
+
weekday: "long",
|
|
95
|
+
year: "numeric",
|
|
96
|
+
month: "long",
|
|
97
|
+
day: "numeric",
|
|
98
|
+
hour: "2-digit",
|
|
99
|
+
minute: "2-digit",
|
|
100
|
+
second: "2-digit",
|
|
101
|
+
timeZoneName: "short",
|
|
102
|
+
})}`
|
|
103
|
+
: "";
|
|
86
104
|
|
|
87
105
|
const appendSection = appendSystemPrompt ? `\n\n${appendSystemPrompt}` : "";
|
|
88
106
|
|
|
@@ -124,8 +142,8 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|
|
124
142
|
prompt += formatSkillsForPrompt(skills);
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
// Add date/time and working directory last
|
|
128
|
-
prompt +=
|
|
145
|
+
// Add date/time (only when opted in — see includeDateTime docs) and working directory last
|
|
146
|
+
prompt += dateTimeLine;
|
|
129
147
|
prompt += `\nCurrent working directory: ${resolvedCwd}`;
|
|
130
148
|
|
|
131
149
|
// Append promptGuidelines from extension-registered tools.
|
|
@@ -272,8 +290,8 @@ Pi documentation (read only when the user asks about pi itself, its SDK, extensi
|
|
|
272
290
|
prompt += formatSkillsForPrompt(skills);
|
|
273
291
|
}
|
|
274
292
|
|
|
275
|
-
// Add date/time and working directory last
|
|
276
|
-
prompt +=
|
|
293
|
+
// Add date/time (only when opted in — see includeDateTime docs) and working directory last
|
|
294
|
+
prompt += dateTimeLine;
|
|
277
295
|
prompt += `\nCurrent working directory: ${resolvedCwd}`;
|
|
278
296
|
|
|
279
297
|
return prompt;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @gsd/pi-coding-agent + token-telemetry — opt-in per-call token observability
|
|
2
|
+
//
|
|
3
|
+
// Emits a single JSON line per assistant message to stderr when
|
|
4
|
+
// `PI_TOKEN_TELEMETRY=1` is set. Captures the cache_read_input_tokens and
|
|
5
|
+
// cache_creation_input_tokens fields the providers already extract — so we
|
|
6
|
+
// can empirically measure prompt-cache effectiveness (e.g. for #5019 and
|
|
7
|
+
// future cache strategy work). Off by default — no behavior change.
|
|
8
|
+
//
|
|
9
|
+
// Capture pattern: `PI_TOKEN_TELEMETRY=1 npm start 2> token-telemetry.jsonl`
|
|
10
|
+
|
|
11
|
+
import type { AssistantMessage } from "@gsd/pi-ai";
|
|
12
|
+
|
|
13
|
+
/** Schema of one telemetry line. JSON-stable for downstream ingestion. */
|
|
14
|
+
export interface TokenTelemetryRecord {
|
|
15
|
+
ts: number;
|
|
16
|
+
model: string;
|
|
17
|
+
stopReason: string;
|
|
18
|
+
input: number;
|
|
19
|
+
output: number;
|
|
20
|
+
cacheRead: number;
|
|
21
|
+
cacheWrite: number;
|
|
22
|
+
/**
|
|
23
|
+
* `usage.cost.total` from the provider. `0` when the provider's cost
|
|
24
|
+
* registry has no rates for this model (e.g. unknown third-party providers)
|
|
25
|
+
* — distinguish from a true zero-cost call by checking your model registry.
|
|
26
|
+
*/
|
|
27
|
+
costTotal: number;
|
|
28
|
+
/**
|
|
29
|
+
* Fraction of new prompt tokens served from cache:
|
|
30
|
+
* `cacheRead / (cacheRead + input)`. Range [0, 1].
|
|
31
|
+
* - `0` when neither cacheRead nor input is present (no division by zero).
|
|
32
|
+
* - `1` on a full cache hit (input = 0, cacheRead > 0).
|
|
33
|
+
* Note: `input` here is `input_tokens` from the API, which already excludes
|
|
34
|
+
* cache reads/writes — the denominator is total prompt tokens consumed.
|
|
35
|
+
*/
|
|
36
|
+
cacheHitRatio: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Build a telemetry record from a finished assistant message. */
|
|
40
|
+
export function buildTokenTelemetryRecord(msg: AssistantMessage): TokenTelemetryRecord {
|
|
41
|
+
const input = msg.usage?.input ?? 0;
|
|
42
|
+
const output = msg.usage?.output ?? 0;
|
|
43
|
+
const cacheRead = msg.usage?.cacheRead ?? 0;
|
|
44
|
+
const cacheWrite = msg.usage?.cacheWrite ?? 0;
|
|
45
|
+
const costTotal = msg.usage?.cost?.total ?? 0;
|
|
46
|
+
const denom = cacheRead + input;
|
|
47
|
+
const cacheHitRatio = denom > 0 ? cacheRead / denom : 0;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
ts: msg.timestamp,
|
|
51
|
+
model: msg.model,
|
|
52
|
+
stopReason: msg.stopReason,
|
|
53
|
+
input,
|
|
54
|
+
output,
|
|
55
|
+
cacheRead,
|
|
56
|
+
cacheWrite,
|
|
57
|
+
costTotal,
|
|
58
|
+
cacheHitRatio,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Emit a token-telemetry line if `PI_TOKEN_TELEMETRY=1`. No-op otherwise.
|
|
64
|
+
*
|
|
65
|
+
* Writes to stderr so it doesn't interfere with TUI/stdout. One JSON object
|
|
66
|
+
* per line. Errors during emission are swallowed — telemetry must never
|
|
67
|
+
* break the agent loop.
|
|
68
|
+
*/
|
|
69
|
+
export function emitTokenTelemetry(msg: AssistantMessage): void {
|
|
70
|
+
if (process.env.PI_TOKEN_TELEMETRY !== "1") return;
|
|
71
|
+
try {
|
|
72
|
+
const record = buildTokenTelemetryRecord(msg);
|
|
73
|
+
process.stderr.write(`${JSON.stringify(record)}\n`);
|
|
74
|
+
} catch {
|
|
75
|
+
// Telemetry must never break the agent loop. Swallow.
|
|
76
|
+
}
|
|
77
|
+
}
|