gsd-pi 2.78.0 → 2.78.1-dev.82bcf6b71
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 +26 -12
- 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-direct-dispatch.js +55 -21
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +69 -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 +75 -13
- package/dist/resources/extensions/gsd/auto.js +39 -14
- 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/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -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/prompts/rewrite-docs.md +2 -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 +28 -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/resources/skills/lint/SKILL.md +4 -0
- package/dist/resources/skills/review/SKILL.md +4 -0
- package/dist/resources/skills/test/SKILL.md +3 -0
- 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 +19 -19
- 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 +19 -19
- 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 +26 -12
- 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-direct-dispatch.ts +60 -24
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +66 -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 +97 -12
- package/src/resources/extensions/gsd/auto.ts +37 -10
- 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/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -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/prompts/rewrite-docs.md +2 -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 +179 -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/stash-pop-gsd-conflict.test.ts +8 -2
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +12 -6
- 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-path-injection.test.ts +235 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +85 -0
- 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 +28 -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/src/resources/skills/lint/SKILL.md +4 -0
- package/src/resources/skills/review/SKILL.md +4 -0
- package/src/resources/skills/test/SKILL.md +3 -0
- 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 → hcvW7f3yv1JHzlWe7tIc6}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → hcvW7f3yv1JHzlWe7tIc6}/_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
|
+
}
|