@newrelic/preflight 1.15.6 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/alerts/alert-snapshot-collector.d.ts +2 -3
  2. package/dist/alerts/alert-snapshot-collector.d.ts.map +1 -1
  3. package/dist/alerts/alert-snapshot-collector.js +1 -1
  4. package/dist/alerts/alert-snapshot-collector.js.map +1 -1
  5. package/dist/config.d.ts +3 -0
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +3 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/dashboard/dashboard-server.d.ts.map +1 -1
  10. package/dist/dashboard/dashboard-server.js +15 -0
  11. package/dist/dashboard/dashboard-server.js.map +1 -1
  12. package/dist/dashboard/routes/api-handler.d.ts +26 -18
  13. package/dist/dashboard/routes/api-handler.d.ts.map +1 -1
  14. package/dist/dashboard/routes/api-handler.js +71 -23
  15. package/dist/dashboard/routes/api-handler.js.map +1 -1
  16. package/dist/data/copilot-pricing/README.md +42 -0
  17. package/dist/data/copilot-pricing/pricing.json +54 -0
  18. package/dist/data/copilot-sdk-extension/extension.mjs +123 -0
  19. package/dist/deploy/data-paths.d.ts +6 -4
  20. package/dist/deploy/data-paths.d.ts.map +1 -1
  21. package/dist/deploy/data-paths.js +5 -3
  22. package/dist/deploy/data-paths.js.map +1 -1
  23. package/dist/hooks/collector-script.d.ts +12 -3
  24. package/dist/hooks/collector-script.d.ts.map +1 -1
  25. package/dist/hooks/collector-script.js +70 -18
  26. package/dist/hooks/collector-script.js.map +1 -1
  27. package/dist/hooks/copilot-sdk-usage-mapper.d.ts +45 -0
  28. package/dist/hooks/copilot-sdk-usage-mapper.d.ts.map +1 -0
  29. package/dist/hooks/copilot-sdk-usage-mapper.js +73 -0
  30. package/dist/hooks/copilot-sdk-usage-mapper.js.map +1 -0
  31. package/dist/hooks/copilot-usage-watcher.d.ts +109 -0
  32. package/dist/hooks/copilot-usage-watcher.d.ts.map +1 -0
  33. package/dist/hooks/copilot-usage-watcher.js +436 -0
  34. package/dist/hooks/copilot-usage-watcher.js.map +1 -0
  35. package/dist/hooks/parent-transcript-watcher.d.ts +11 -1
  36. package/dist/hooks/parent-transcript-watcher.d.ts.map +1 -1
  37. package/dist/hooks/parent-transcript-watcher.js +71 -15
  38. package/dist/hooks/parent-transcript-watcher.js.map +1 -1
  39. package/dist/hooks/subagent-watcher.d.ts +14 -0
  40. package/dist/hooks/subagent-watcher.d.ts.map +1 -1
  41. package/dist/hooks/subagent-watcher.js +50 -6
  42. package/dist/hooks/subagent-watcher.js.map +1 -1
  43. package/dist/index.d.ts +11 -11
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +162 -70
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/budget-tracker.d.ts +4 -3
  48. package/dist/metrics/budget-tracker.d.ts.map +1 -1
  49. package/dist/metrics/budget-tracker.js +4 -3
  50. package/dist/metrics/budget-tracker.js.map +1 -1
  51. package/dist/metrics/copilot-pricing-overlay.d.ts +42 -0
  52. package/dist/metrics/copilot-pricing-overlay.d.ts.map +1 -0
  53. package/dist/metrics/copilot-pricing-overlay.js +97 -0
  54. package/dist/metrics/copilot-pricing-overlay.js.map +1 -0
  55. package/dist/metrics/cost-tracker.d.ts +14 -0
  56. package/dist/metrics/cost-tracker.d.ts.map +1 -1
  57. package/dist/metrics/cost-tracker.js +2 -0
  58. package/dist/metrics/cost-tracker.js.map +1 -1
  59. package/dist/metrics/git-efficiency-tracker.d.ts +12 -1
  60. package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -1
  61. package/dist/metrics/git-efficiency-tracker.js +18 -3
  62. package/dist/metrics/git-efficiency-tracker.js.map +1 -1
  63. package/dist/metrics/instruction-drift-tracker.d.ts.map +1 -1
  64. package/dist/metrics/instruction-drift-tracker.js +1 -2
  65. package/dist/metrics/instruction-drift-tracker.js.map +1 -1
  66. package/dist/metrics/local-session-aggregator.d.ts +164 -0
  67. package/dist/metrics/local-session-aggregator.d.ts.map +1 -0
  68. package/dist/metrics/local-session-aggregator.js +444 -0
  69. package/dist/metrics/local-session-aggregator.js.map +1 -0
  70. package/dist/platforms/copilot-adapter.d.ts +1 -1
  71. package/dist/platforms/copilot-adapter.d.ts.map +1 -1
  72. package/dist/platforms/copilot-adapter.js +64 -10
  73. package/dist/platforms/copilot-adapter.js.map +1 -1
  74. package/dist/platforms/copilot-sdk-adapter.d.ts +24 -0
  75. package/dist/platforms/copilot-sdk-adapter.d.ts.map +1 -0
  76. package/dist/platforms/copilot-sdk-adapter.js +173 -0
  77. package/dist/platforms/copilot-sdk-adapter.js.map +1 -0
  78. package/dist/platforms/index.d.ts +1 -0
  79. package/dist/platforms/index.d.ts.map +1 -1
  80. package/dist/platforms/index.js +1 -0
  81. package/dist/platforms/index.js.map +1 -1
  82. package/dist/platforms/platform-registry.d.ts.map +1 -1
  83. package/dist/platforms/platform-registry.js +13 -11
  84. package/dist/platforms/platform-registry.js.map +1 -1
  85. package/dist/storage/session-store.d.ts +43 -10
  86. package/dist/storage/session-store.d.ts.map +1 -1
  87. package/dist/storage/session-store.js +238 -20
  88. package/dist/storage/session-store.js.map +1 -1
  89. package/dist/web/assets/{index-BasTEtUs.js → index-Bwc4qaqj.js} +1 -1
  90. package/dist/web/assets/index-DwdSz9gz.css +2 -0
  91. package/dist/web/index.html +2 -2
  92. package/package.json +2 -2
  93. package/dist/web/assets/index-BI-2ELf2.css +0 -2
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Maps GitHub Copilot SDK `assistant.usage` events to Preflight's `mode:
3
+ * 'token'` buffer-line contract, and resolves the buffer file path — the
4
+ * shared, tested logic behind `copilot-sdk-extension/extension.mjs` (a
5
+ * hand-maintained plain-JS mirror of this file; Copilot SDK extensions must
6
+ * be written in JavaScript, so the logic can't be imported directly — see
7
+ * that file's header comment).
8
+ *
9
+ * Field mapping verified against two real `assistant.usage` events captured
10
+ * live from a Copilot SDK session, via the Copilot CLI host (see
11
+ * copilot-sdk-usage-mapper.test.ts):
12
+ * the SDK's `inputTokens` is inclusive of BOTH cache-read and cache-write
13
+ * (unlike VS Code's debug log, which is cache-read-inclusive only) — proven
14
+ * by cross-checking against the SDK's own `copilotUsage.tokenDetails`
15
+ * per-token-type breakdown on both samples.
16
+ */
17
+ import { homedir } from 'node:os';
18
+ import { resolve } from 'node:path';
19
+ const SESSION_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
20
+ function num(value) {
21
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
22
+ }
23
+ /**
24
+ * Maps an `assistant.usage` event's `data` payload to a `mode: 'token'`
25
+ * buffer line. Returns `null` when the payload is missing the two fields a
26
+ * usable line requires — `model` (priced downstream) and `apiCallId` (the
27
+ * stable per-call dedup key `HookEventProcessor.handleTokenEvent()` uses) —
28
+ * rather than emitting a line with fabricated values. Never throws.
29
+ */
30
+ export function mapAssistantUsageEvent(data, sessionId, timestampMs) {
31
+ if (typeof data !== 'object' || data === null)
32
+ return null;
33
+ const d = data;
34
+ const model = typeof d.model === 'string' && d.model.length > 0 ? d.model : null;
35
+ if (!model)
36
+ return null;
37
+ const apiCallId = typeof d.apiCallId === 'string' && d.apiCallId.length > 0 ? d.apiCallId : null;
38
+ if (!apiCallId)
39
+ return null;
40
+ const inputTokensRaw = num(d.inputTokens);
41
+ const cacheReadTokens = num(d.cacheReadTokens);
42
+ const cacheCreationTokens = num(d.cacheWriteTokens);
43
+ return {
44
+ mode: 'token',
45
+ tool: 'copilot-sdk-usage',
46
+ timestamp: timestampMs,
47
+ sessionId,
48
+ messageId: apiCallId,
49
+ model,
50
+ // SDK's inputTokens is inclusive of both cache-read and cache-write —
51
+ // subtract both so the uncached remainder isn't double-billed at the
52
+ // base input rate (see header comment).
53
+ inputTokens: Math.max(0, inputTokensRaw - cacheReadTokens - cacheCreationTokens),
54
+ outputTokens: num(d.outputTokens),
55
+ cacheReadTokens,
56
+ cacheCreationTokens,
57
+ };
58
+ }
59
+ /** Storage dir for cursor/buffer files — same env var and default as collector-script.ts. */
60
+ export function resolveStoragePath() {
61
+ return process.env.NEW_RELIC_AI_MCP_STORAGE_PATH ?? resolve(homedir(), '.newrelic-preflight');
62
+ }
63
+ /**
64
+ * Resolves `<storagePath>/buffer-<sessionId>.jsonl`. Validates `sessionId`
65
+ * against the same pattern collector-script.ts uses, returning `null` for an
66
+ * invalid id rather than letting it escape the storage dir.
67
+ */
68
+ export function resolveBufferPath(storagePath, sessionId) {
69
+ if (!SESSION_ID_RE.test(sessionId))
70
+ return null;
71
+ return resolve(storagePath, `buffer-${sessionId}.jsonl`);
72
+ }
73
+ //# sourceMappingURL=copilot-sdk-usage-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-sdk-usage-mapper.js","sourceRoot":"","sources":["../../src/hooks/copilot-sdk-usage-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,wBAAwB,CAAC;AAe/C,SAAS,GAAG,CAAC,KAAc;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAa,EACb,SAAiB,EACjB,WAAmB;IAEnB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAE1C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACjG,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,eAAe,GAAG,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,mBAAmB,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAEpD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,WAAW;QACtB,SAAS;QACT,SAAS,EAAE,SAAS;QACpB,KAAK;QACL,sEAAsE;QACtE,qEAAqE;QACrE,wCAAwC;QACxC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,eAAe,GAAG,mBAAmB,CAAC;QAChF,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACjC,eAAe;QACf,mBAAmB;KACpB,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAChG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAmB,EAAE,SAAiB;IACtE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,OAAO,CAAC,WAAW,EAAE,UAAU,SAAS,QAAQ,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Copilot Usage Watcher — polls VS Code Copilot agent debug logs and emits
3
+ * one `mode: 'token'` line per LLM request into the session's hook buffer,
4
+ * giving Copilot sessions token-exact cost (the same fidelity
5
+ * ParentTranscriptWatcher gives Claude Code sessions) instead of
6
+ * content-size estimation.
7
+ *
8
+ * Source of truth: VS Code writes per-request records to
9
+ * `<userDataDir>/workspaceStorage/<hash>/GitHub.copilot-chat/debug-logs/<sessionId>/main.jsonl`,
10
+ * where `<sessionId>` is the same chat session id VS Code sends as
11
+ * `session_id` in agent hook payloads — so events emitted here land in the
12
+ * same `buffer-<sessionId>.jsonl` the hook collector writes to. Each usage
13
+ * record is `{ type: 'llm_request', sid, ts, attrs: { model, inputTokens,
14
+ * outputTokens, cachedTokens, responseId, copilotUsageNanoAiu, ... } }`.
15
+ * This path + schema are produced by VS Code's `chatDebugFileLoggerService.ts`
16
+ * (formerly `microsoft/vscode-copilot-chat`, now merged into
17
+ * `microsoft/vscode` under `extensions/copilot/` and archived); the record
18
+ * shape is documented in its `otel-data-flow.html` and cross-checked against a
19
+ * real main.jsonl (VS Code 1.109 / Copilot Chat).
20
+ * The hooks doc notes transcript/debug-log formats are not a stable API
21
+ * (code.visualstudio.com/docs/copilot/customization/hooks) — same stability
22
+ * tier as the Claude Code transcript format the parent/subagent watchers
23
+ * already depend on; schema drift degrades to zero emissions, never a crash.
24
+ *
25
+ * Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
26
+ * cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
27
+ * poll, bounded partial-line carry, duplicate emissions after a crash are
28
+ * deduped downstream by (sessionId, messageId) in
29
+ * `HookEventProcessor.handleTokenEvent()` — `responseId` serves as the
30
+ * message id.
31
+ */
32
+ import type { LocalStore } from '../storage/local-store.js';
33
+ /**
34
+ * Default VS Code `workspaceStorage` roots per OS (stable + Insiders), plus
35
+ * the remote-server roots used by WSL/SSH/dev-container sessions.
36
+ * Sourced from VS Code's documented user-data locations
37
+ * (code.visualstudio.com/docs/configure/command-line#_advanced-cli-options).
38
+ */
39
+ export declare function defaultWorkspaceStorageRoots(): string[];
40
+ export interface CopilotUsageWatcherOptions {
41
+ /** Storage path for cursor state + buffers (defaults to ~/.newrelic-preflight). */
42
+ readonly storagePath?: string;
43
+ /** VS Code workspaceStorage roots to scan; defaults to per-OS conventions. */
44
+ readonly workspaceStorageRoots?: string[];
45
+ /** Poll interval in ms. Default 2000. */
46
+ readonly pollIntervalMs?: number;
47
+ /** Cold-scan eligibility window (mtime) for discovered logs. Default 24h. */
48
+ readonly discoveryHours?: number;
49
+ /**
50
+ * If provided, only this session's debug log is processed (the `--stdio`
51
+ * case). Default: every recent session log (matches `--local` semantics).
52
+ */
53
+ readonly parentSessionId?: string;
54
+ /** LocalStore, used to exclude sessions already owned by a live --stdio watcher in unscoped mode. */
55
+ readonly localStore?: LocalStore;
56
+ }
57
+ export interface CopilotUsageWatcherHealth {
58
+ readonly filesWatched: number;
59
+ readonly linesRead: number;
60
+ readonly bytesRead: number;
61
+ readonly parseErrors: number;
62
+ /**
63
+ * True when at least one VS Code workspaceStorage root exists but no Copilot
64
+ * `debug-logs` directory was found in any of them — the signature of the
65
+ * off-by-default `github.copilot.chat.agentDebugLog.fileLogging.enabled`
66
+ * setting. Lets callers distinguish "integration broken" from "prerequisite
67
+ * not enabled" instead of silently reporting zero cost. False when no root
68
+ * exists at all (VS Code absent / wrong OS path — cannot conclude).
69
+ */
70
+ readonly debugLoggingLikelyDisabled: boolean;
71
+ }
72
+ export declare class CopilotUsageWatcher {
73
+ private readonly storagePath;
74
+ private readonly workspaceStorageRoots;
75
+ private readonly pollIntervalMs;
76
+ private readonly discoveryHours;
77
+ private readonly parentSessionFilter;
78
+ private readonly localStore;
79
+ private intervalId;
80
+ private readonly partialByPath;
81
+ private readonly decoderByPath;
82
+ private filesWatched;
83
+ private linesRead;
84
+ private bytesRead;
85
+ private parseErrors;
86
+ private debugLoggingLikelyDisabled;
87
+ private warnedDebugLoggingDisabled;
88
+ constructor(options?: CopilotUsageWatcherOptions);
89
+ start(): void;
90
+ stop(): void;
91
+ getHealth(): CopilotUsageWatcherHealth;
92
+ /** One discovery + tail pass. Exported for direct testing. */
93
+ poll(): void;
94
+ private discoverFiles;
95
+ private tailFile;
96
+ private evictStalePartials;
97
+ /**
98
+ * Parse a debug-log JSONL line, returning non-null only for an
99
+ * `llm_request` record carrying a model, responseId, and token counts.
100
+ */
101
+ private tryParseLine;
102
+ private cursorPath;
103
+ private readCursor;
104
+ private writeCursor;
105
+ /** Append into the session's hook buffer, same path convention as the collector. */
106
+ private appendToBuffer;
107
+ private recordError;
108
+ }
109
+ //# sourceMappingURL=copilot-usage-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-usage-watcher.d.ts","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAmBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAiC5D;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAiBvD;AAMD,MAAM,WAAW,0BAA0B;IACzC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1C,yCAAyC;IACzC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,qGAAqG;IACrG,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAC;CAC9C;AAqBD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IAEpD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAElE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,0BAA0B,CAAS;gBAE/B,OAAO,GAAE,0BAA+B;IASpD,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAOZ,SAAS,IAAI,yBAAyB;IAUtC,8DAA8D;IAC9D,IAAI,IAAI,IAAI;IAiBZ,OAAO,CAAC,aAAa;IAuErB,OAAO,CAAC,QAAQ;IA+FhB,OAAO,CAAC,kBAAkB;IAW1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,WAAW;IAWnB,oFAAoF;IACpF,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,WAAW;CAIpB"}
@@ -0,0 +1,436 @@
1
+ /**
2
+ * Copilot Usage Watcher — polls VS Code Copilot agent debug logs and emits
3
+ * one `mode: 'token'` line per LLM request into the session's hook buffer,
4
+ * giving Copilot sessions token-exact cost (the same fidelity
5
+ * ParentTranscriptWatcher gives Claude Code sessions) instead of
6
+ * content-size estimation.
7
+ *
8
+ * Source of truth: VS Code writes per-request records to
9
+ * `<userDataDir>/workspaceStorage/<hash>/GitHub.copilot-chat/debug-logs/<sessionId>/main.jsonl`,
10
+ * where `<sessionId>` is the same chat session id VS Code sends as
11
+ * `session_id` in agent hook payloads — so events emitted here land in the
12
+ * same `buffer-<sessionId>.jsonl` the hook collector writes to. Each usage
13
+ * record is `{ type: 'llm_request', sid, ts, attrs: { model, inputTokens,
14
+ * outputTokens, cachedTokens, responseId, copilotUsageNanoAiu, ... } }`.
15
+ * This path + schema are produced by VS Code's `chatDebugFileLoggerService.ts`
16
+ * (formerly `microsoft/vscode-copilot-chat`, now merged into
17
+ * `microsoft/vscode` under `extensions/copilot/` and archived); the record
18
+ * shape is documented in its `otel-data-flow.html` and cross-checked against a
19
+ * real main.jsonl (VS Code 1.109 / Copilot Chat).
20
+ * The hooks doc notes transcript/debug-log formats are not a stable API
21
+ * (code.visualstudio.com/docs/copilot/customization/hooks) — same stability
22
+ * tier as the Claude Code transcript format the parent/subagent watchers
23
+ * already depend on; schema drift degrades to zero emissions, never a crash.
24
+ *
25
+ * Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
26
+ * cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
27
+ * poll, bounded partial-line carry, duplicate emissions after a crash are
28
+ * deduped downstream by (sessionId, messageId) in
29
+ * `HookEventProcessor.handleTokenEvent()` — `responseId` serves as the
30
+ * message id.
31
+ */
32
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, statSync, writeFileSync, } from 'node:fs';
33
+ import { homedir } from 'node:os';
34
+ import { join } from 'node:path';
35
+ import { StringDecoder } from 'node:string_decoder';
36
+ import { createLogger } from '../shared/index.js';
37
+ const logger = createLogger('copilot-usage-watcher');
38
+ // ---------------------------------------------------------------------------
39
+ // Constants
40
+ // ---------------------------------------------------------------------------
41
+ const DEFAULT_POLL_INTERVAL_MS = 2_000;
42
+ const DEFAULT_DISCOVERY_HOURS = 24;
43
+ // Copilot debug-log lines embed full inputMessages payloads and routinely run
44
+ // hundreds of KiB — far larger than transcript lines. A bigger window keeps a
45
+ // single record from spanning many polls (each carry-over persists the whole
46
+ // partial line into the cursor file).
47
+ const MAX_BYTES_PER_POLL = 1024 * 1024;
48
+ /** See SubagentWatcher's identical constant for the OOM-bug rationale this guards against. */
49
+ const MAX_PARTIAL_LINE_BYTES = 4 * 1024 * 1024; // 4 MiB — sized to the larger log lines
50
+ const SESSION_ID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
51
+ const COPILOT_LOG_SUBPATH = join('GitHub.copilot-chat', 'debug-logs');
52
+ /**
53
+ * VS Code Remote (WSL, SSH, dev containers, Codespaces) `workspaceStorage`
54
+ * roots. When the Copilot Chat extension runs in a remote extension host, it
55
+ * writes debug logs under the server's user-data dir rather than the desktop
56
+ * one, so these must be searched alongside the local roots or remote sessions
57
+ * silently yield zero token events (and therefore $0.00 cost).
58
+ */
59
+ function remoteWorkspaceStorageRoots(home) {
60
+ return ['.vscode-server', '.vscode-server-insiders', '.vscode-remote'].map((dir) => join(home, dir, 'data', 'User', 'workspaceStorage'));
61
+ }
62
+ /**
63
+ * Default VS Code `workspaceStorage` roots per OS (stable + Insiders), plus
64
+ * the remote-server roots used by WSL/SSH/dev-container sessions.
65
+ * Sourced from VS Code's documented user-data locations
66
+ * (code.visualstudio.com/docs/configure/command-line#_advanced-cli-options).
67
+ */
68
+ export function defaultWorkspaceStorageRoots() {
69
+ const home = homedir();
70
+ const variants = ['Code', 'Code - Insiders'];
71
+ const remote = remoteWorkspaceStorageRoots(home);
72
+ if (process.platform === 'darwin') {
73
+ return [
74
+ ...variants.map((v) => join(home, 'Library', 'Application Support', v, 'User', 'workspaceStorage')),
75
+ ...remote,
76
+ ];
77
+ }
78
+ if (process.platform === 'win32') {
79
+ const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
80
+ return [...variants.map((v) => join(appData, v, 'User', 'workspaceStorage')), ...remote];
81
+ }
82
+ return [...variants.map((v) => join(home, '.config', v, 'User', 'workspaceStorage')), ...remote];
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // CopilotUsageWatcher
86
+ // ---------------------------------------------------------------------------
87
+ export class CopilotUsageWatcher {
88
+ storagePath;
89
+ workspaceStorageRoots;
90
+ pollIntervalMs;
91
+ discoveryHours;
92
+ parentSessionFilter;
93
+ localStore;
94
+ intervalId = null;
95
+ partialByPath = new Map();
96
+ decoderByPath = new Map();
97
+ filesWatched = 0;
98
+ linesRead = 0;
99
+ bytesRead = 0;
100
+ parseErrors = 0;
101
+ debugLoggingLikelyDisabled = false;
102
+ warnedDebugLoggingDisabled = false;
103
+ constructor(options = {}) {
104
+ this.storagePath = options.storagePath ?? join(homedir(), '.newrelic-preflight');
105
+ this.workspaceStorageRoots = options.workspaceStorageRoots ?? defaultWorkspaceStorageRoots();
106
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
107
+ this.discoveryHours = options.discoveryHours ?? DEFAULT_DISCOVERY_HOURS;
108
+ this.parentSessionFilter = options.parentSessionId ?? null;
109
+ this.localStore = options.localStore;
110
+ }
111
+ start() {
112
+ if (this.intervalId !== null)
113
+ return;
114
+ this.intervalId = setInterval(() => {
115
+ try {
116
+ this.poll();
117
+ }
118
+ catch (err) {
119
+ this.recordError(err);
120
+ }
121
+ }, this.pollIntervalMs);
122
+ this.intervalId.unref?.();
123
+ }
124
+ stop() {
125
+ if (this.intervalId !== null) {
126
+ clearInterval(this.intervalId);
127
+ this.intervalId = null;
128
+ }
129
+ }
130
+ getHealth() {
131
+ return {
132
+ filesWatched: this.filesWatched,
133
+ linesRead: this.linesRead,
134
+ bytesRead: this.bytesRead,
135
+ parseErrors: this.parseErrors,
136
+ debugLoggingLikelyDisabled: this.debugLoggingLikelyDisabled,
137
+ };
138
+ }
139
+ /** One discovery + tail pass. Exported for direct testing. */
140
+ poll() {
141
+ const files = this.discoverFiles();
142
+ this.filesWatched = files.length;
143
+ this.evictStalePartials(files);
144
+ for (const file of files) {
145
+ try {
146
+ this.tailFile(file.path, file.sessionId);
147
+ }
148
+ catch (err) {
149
+ this.recordError(err);
150
+ }
151
+ }
152
+ }
153
+ // -------------------------------------------------------------------------
154
+ // Discovery
155
+ // -------------------------------------------------------------------------
156
+ discoverFiles() {
157
+ const out = [];
158
+ const cutoffMs = Date.now() - this.discoveryHours * 3_600_000;
159
+ // Unscoped (--local) mode: skip sessions a live --stdio process already
160
+ // tails, so two processes never race on the same cursor file — identical
161
+ // to ParentTranscriptWatcher's unfiltered discovery.
162
+ const liveOwnedSessionIds = this.parentSessionFilter === null
163
+ ? (this.localStore?.getActiveSessionIdsFromHeartbeats() ?? null)
164
+ : null;
165
+ let anyRootExists = false;
166
+ let anyDebugLogDir = false;
167
+ for (const root of this.workspaceStorageRoots) {
168
+ if (!existsSync(root))
169
+ continue;
170
+ anyRootExists = true;
171
+ let workspaceHashes;
172
+ try {
173
+ workspaceHashes = readdirSync(root);
174
+ }
175
+ catch {
176
+ continue;
177
+ }
178
+ for (const hash of workspaceHashes) {
179
+ const logsDir = join(root, hash, COPILOT_LOG_SUBPATH);
180
+ if (!existsSync(logsDir))
181
+ continue;
182
+ anyDebugLogDir = true;
183
+ let sessionDirs;
184
+ try {
185
+ sessionDirs = readdirSync(logsDir);
186
+ }
187
+ catch {
188
+ continue;
189
+ }
190
+ for (const sessionId of sessionDirs) {
191
+ if (!SESSION_ID_RE.test(sessionId))
192
+ continue;
193
+ if (this.parentSessionFilter !== null && sessionId !== this.parentSessionFilter) {
194
+ continue;
195
+ }
196
+ if (liveOwnedSessionIds?.has(sessionId))
197
+ continue;
198
+ const logPath = join(logsDir, sessionId, 'main.jsonl');
199
+ try {
200
+ const st = statSync(logPath);
201
+ if (!st.isFile())
202
+ continue;
203
+ // Scoped mode always tails its own session; unscoped applies the
204
+ // recency window so old logs don't trigger unbounded cold scans.
205
+ if (this.parentSessionFilter === null && st.mtimeMs < cutoffMs)
206
+ continue;
207
+ }
208
+ catch {
209
+ continue;
210
+ }
211
+ out.push({ path: logPath, sessionId });
212
+ }
213
+ }
214
+ }
215
+ // A VS Code install is present (root exists) yet no Copilot debug-logs
216
+ // directory anywhere ⇒ the off-by-default fileLogging setting is almost
217
+ // certainly not enabled. Warn once so a zero-cost session reads as a
218
+ // missing prerequisite rather than a broken integration.
219
+ this.debugLoggingLikelyDisabled = anyRootExists && !anyDebugLogDir;
220
+ if (this.debugLoggingLikelyDisabled && !this.warnedDebugLoggingDisabled) {
221
+ this.warnedDebugLoggingDisabled = true;
222
+ logger.warn('No Copilot debug-logs directory found; token-exact cost is unavailable. ' +
223
+ 'Enable "github.copilot.chat.agentDebugLog.fileLogging.enabled" in VS Code ' +
224
+ 'settings.json and reload the window.');
225
+ }
226
+ return out;
227
+ }
228
+ // -------------------------------------------------------------------------
229
+ // Tailing
230
+ // -------------------------------------------------------------------------
231
+ tailFile(path, dirSessionId) {
232
+ const cursorPath = this.cursorPath(dirSessionId);
233
+ const persisted = this.readCursor(cursorPath);
234
+ const memPartial = this.partialByPath.get(path);
235
+ const startCursor = memPartial !== undefined
236
+ ? { bytePos: persisted.bytePos, partialLine: memPartial }
237
+ : persisted;
238
+ let size;
239
+ try {
240
+ size = statSync(path).size;
241
+ }
242
+ catch {
243
+ return;
244
+ }
245
+ // Truncated/rotated file: restart from 0.
246
+ const fromPos = startCursor.bytePos > size ? 0 : startCursor.bytePos;
247
+ if (fromPos >= size)
248
+ return;
249
+ const toRead = Math.min(size - fromPos, MAX_BYTES_PER_POLL);
250
+ const buf = Buffer.alloc(toRead);
251
+ let actuallyRead = 0;
252
+ let fd = null;
253
+ try {
254
+ fd = openSync(path, 'r');
255
+ actuallyRead = readSync(fd, buf, 0, toRead, fromPos);
256
+ }
257
+ catch (err) {
258
+ this.recordError(err);
259
+ return;
260
+ }
261
+ finally {
262
+ if (fd !== null)
263
+ closeSync(fd);
264
+ }
265
+ if (actuallyRead <= 0)
266
+ return;
267
+ this.bytesRead += actuallyRead;
268
+ let decoder = this.decoderByPath.get(path);
269
+ if (!decoder) {
270
+ decoder = new StringDecoder('utf8');
271
+ this.decoderByPath.set(path, decoder);
272
+ }
273
+ const combined = startCursor.partialLine + decoder.write(buf.subarray(0, actuallyRead));
274
+ const nextBytePos = fromPos + actuallyRead;
275
+ const lastNewline = combined.lastIndexOf('\n');
276
+ let lines = [];
277
+ let newPartial = combined;
278
+ if (lastNewline >= 0) {
279
+ lines = combined.slice(0, lastNewline).split('\n');
280
+ newPartial = combined.slice(lastNewline + 1);
281
+ }
282
+ if (newPartial.length > MAX_PARTIAL_LINE_BYTES) {
283
+ newPartial = '';
284
+ this.parseErrors += 1;
285
+ }
286
+ for (const line of lines) {
287
+ if (!line)
288
+ continue;
289
+ this.linesRead += 1;
290
+ const parsed = this.tryParseLine(line);
291
+ if (parsed === null)
292
+ continue;
293
+ const sessionId = parsed.sessionId ?? dirSessionId;
294
+ this.appendToBuffer(sessionId, {
295
+ mode: 'token',
296
+ tool: 'copilot-usage',
297
+ timestamp: parsed.timestampMs,
298
+ sessionId,
299
+ messageId: parsed.responseId,
300
+ model: parsed.model,
301
+ // VS Code's inputTokens is cache-INCLUSIVE (verified against a real
302
+ // main.jsonl: each request's cachedTokens ≈ the previous request's
303
+ // inputTokens). CostTracker follows the Anthropic convention where
304
+ // input excludes cache reads, so emit only the uncached remainder —
305
+ // otherwise cached tokens are double-billed at full input rate.
306
+ inputTokens: Math.max(0, parsed.inputTokens - parsed.cachedTokens),
307
+ outputTokens: parsed.outputTokens,
308
+ // `cachedTokens` is the only cache figure the debug-log schema exposes,
309
+ // and it is cache-READ (confirmed against a real main.jsonl: it tracks
310
+ // the previous request's input). KNOWN ESTIMATION GAP: VS Code does not
311
+ // surface cache-CREATION (cache-write) tokens separately, so any turn
312
+ // with a fresh cache write has those tokens folded into `inputTokens`
313
+ // above and billed at the base input rate instead of the premium
314
+ // `cacheCreationPerMTok` rate (src/shared/pricing.ts). This causes a
315
+ // small, consistent UNDER-billing on cache-write turns. We report 0
316
+ // rather than guess a split the source doesn't provide; revisit if VS
317
+ // Code adds a cache-creation field to the schema.
318
+ cacheReadTokens: parsed.cachedTokens,
319
+ cacheCreationTokens: 0,
320
+ });
321
+ }
322
+ this.writeCursor(cursorPath, nextBytePos, newPartial);
323
+ if (newPartial.length > 0)
324
+ this.partialByPath.set(path, newPartial);
325
+ else
326
+ this.partialByPath.delete(path);
327
+ }
328
+ evictStalePartials(files) {
329
+ if (this.partialByPath.size === 0 && this.decoderByPath.size === 0)
330
+ return;
331
+ const live = new Set(files.map((f) => f.path));
332
+ for (const path of this.partialByPath.keys()) {
333
+ if (!live.has(path))
334
+ this.partialByPath.delete(path);
335
+ }
336
+ for (const path of this.decoderByPath.keys()) {
337
+ if (!live.has(path))
338
+ this.decoderByPath.delete(path);
339
+ }
340
+ }
341
+ /**
342
+ * Parse a debug-log JSONL line, returning non-null only for an
343
+ * `llm_request` record carrying a model, responseId, and token counts.
344
+ */
345
+ tryParseLine(line) {
346
+ let parsed;
347
+ try {
348
+ parsed = JSON.parse(line);
349
+ }
350
+ catch {
351
+ this.parseErrors += 1;
352
+ return null;
353
+ }
354
+ if (!parsed || typeof parsed !== 'object')
355
+ return null;
356
+ const obj = parsed;
357
+ if (obj.type !== 'llm_request')
358
+ return null;
359
+ const attrs = obj.attrs;
360
+ if (!attrs || typeof attrs !== 'object')
361
+ return null;
362
+ const a = attrs;
363
+ const model = typeof a.model === 'string' && a.model.length > 0 ? a.model : null;
364
+ if (!model)
365
+ return null;
366
+ const responseId = typeof a.responseId === 'string' && a.responseId.length > 0 ? a.responseId : null;
367
+ if (!responseId)
368
+ return null;
369
+ const timestampMs = typeof obj.ts === 'number' && Number.isFinite(obj.ts) && obj.ts > 0 ? obj.ts : Date.now();
370
+ const sid = typeof obj.sid === 'string' && SESSION_ID_RE.test(obj.sid) ? obj.sid : null;
371
+ return {
372
+ timestampMs,
373
+ sessionId: sid,
374
+ responseId,
375
+ model,
376
+ inputTokens: num(a.inputTokens),
377
+ outputTokens: num(a.outputTokens),
378
+ cachedTokens: num(a.cachedTokens),
379
+ };
380
+ }
381
+ // -------------------------------------------------------------------------
382
+ // Buffer + cursor I/O
383
+ // -------------------------------------------------------------------------
384
+ cursorPath(sessionId) {
385
+ return join(this.storagePath, `.copilot-usage-pos-${sessionId}`);
386
+ }
387
+ readCursor(cursorPath) {
388
+ if (!existsSync(cursorPath))
389
+ return { bytePos: 0, partialLine: '' };
390
+ try {
391
+ const parsed = JSON.parse(readFileSync(cursorPath, 'utf-8').trim());
392
+ const bytePos = typeof parsed.bytePos === 'number' && parsed.bytePos >= 0 ? parsed.bytePos : 0;
393
+ const partialLine = typeof parsed.partialLine === 'string' ? parsed.partialLine : '';
394
+ return { bytePos, partialLine };
395
+ }
396
+ catch {
397
+ return { bytePos: 0, partialLine: '' };
398
+ }
399
+ }
400
+ writeCursor(cursorPath, bytePos, partialLine) {
401
+ try {
402
+ if (!existsSync(this.storagePath)) {
403
+ mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
404
+ }
405
+ writeFileSync(cursorPath, JSON.stringify({ bytePos, partialLine }), { mode: 0o600 });
406
+ }
407
+ catch (err) {
408
+ this.recordError(err);
409
+ }
410
+ }
411
+ /** Append into the session's hook buffer, same path convention as the collector. */
412
+ appendToBuffer(sessionId, event) {
413
+ try {
414
+ if (!existsSync(this.storagePath)) {
415
+ mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
416
+ }
417
+ appendFileSync(join(this.storagePath, `buffer-${sessionId}.jsonl`), JSON.stringify(event) + '\n', {
418
+ mode: 0o600,
419
+ });
420
+ }
421
+ catch (err) {
422
+ this.recordError(err);
423
+ }
424
+ }
425
+ recordError(err) {
426
+ const message = err instanceof Error ? err.message : String(err);
427
+ logger.warn('CopilotUsageWatcher error', { message: message.slice(0, 200) });
428
+ }
429
+ }
430
+ // ---------------------------------------------------------------------------
431
+ // Helpers
432
+ // ---------------------------------------------------------------------------
433
+ function num(v) {
434
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0;
435
+ }
436
+ //# sourceMappingURL=copilot-usage-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-usage-watcher.js","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"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"}