@jagit/hook-copilot 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -65,6 +65,7 @@ interface ModelUsageBucket {
65
65
  cachedInputTokens: number;
66
66
  outputTokens: number;
67
67
  totalTokens: number;
68
+ costUsd: number;
68
69
  observations: number;
69
70
  }
70
71
  interface CopilotDebugUsage {
@@ -75,11 +76,17 @@ interface CopilotDebugUsage {
75
76
  cachedInputTokens: number;
76
77
  outputTokens: number;
77
78
  totalTokens: number;
79
+ costUsd: number | null;
78
80
  sourcePath: string;
79
81
  modelUsage: Record<string, ModelUsageBucket>;
80
82
  }
81
83
  export declare function inferWorkspaceIdBySession(sessionId: string, hookTimestamp?: string, baseDir?: string): WorkspaceCandidate | undefined;
82
- export declare function resolveDebugUsageBySession(sessionId: string | undefined, hookTimestamp?: string, baseDir?: string): CopilotDebugUsage | undefined;
84
+ interface TranscriptPathLocation {
85
+ workspaceId: string;
86
+ sessionId: string;
87
+ }
88
+ export declare function parseTranscriptPathLocation(transcriptPath: string | undefined): TranscriptPathLocation | undefined;
89
+ export declare function resolveDebugUsageBySession(sessionId: string | undefined, hookTimestamp?: string, baseDir?: string, workspaceIdFromTranscript?: string): CopilotDebugUsage | undefined;
83
90
  /**
84
91
  * Build payload from a real VS Code Copilot agent Stop-hook stdin.
85
92
  *
@@ -88,7 +95,7 @@ export declare function resolveDebugUsageBySession(sessionId: string | undefined
88
95
  * VS Code Copilot transcript — Copilot uses seat-based billing and does not
89
96
  * expose per-call telemetry. These fields are reported as 0/null/"copilot".
90
97
  */
91
- export declare function buildPayloadFromStdin(stdin: CopilotStopStdin, read?: (path: string) => CopilotTranscriptEntry[], resolveUsage?: (sessionId: string | undefined, hookTimestamp?: string) => CopilotDebugUsage | undefined): AgentSessionPayload;
98
+ export declare function buildPayloadFromStdin(stdin: CopilotStopStdin, read?: (path: string) => CopilotTranscriptEntry[], resolveUsage?: (sessionId: string | undefined, hookTimestamp?: string, baseDir?: string, workspaceIdFromTranscript?: string) => CopilotDebugUsage | undefined): AgentSessionPayload;
92
99
  /**
93
100
  * Build payload in legacy mode (no stdin) — used when hook-copilot is invoked
94
101
  * via the old shell-wrapper pattern around the Copilot CLI. Token counts are
package/dist/index.js CHANGED
@@ -93,6 +93,23 @@ export function inferWorkspaceIdBySession(sessionId, hookTimestamp, baseDir = WO
93
93
  }
94
94
  return candidates[0];
95
95
  }
96
+ export function parseTranscriptPathLocation(transcriptPath) {
97
+ if (!transcriptPath) {
98
+ return undefined;
99
+ }
100
+ const normalized = transcriptPath.replace(/\\/g, "/");
101
+ const match = normalized.match(/\/workspaceStorage\/([^/]+)\/GitHub\.copilot-chat\/transcripts\/([^/]+)\.jsonl$/);
102
+ if (!match) {
103
+ return undefined;
104
+ }
105
+ return {
106
+ workspaceId: match[1],
107
+ sessionId: match[2],
108
+ };
109
+ }
110
+ function buildMainLogPath(baseDir, workspaceId, sessionId) {
111
+ return join(baseDir, workspaceId, "GitHub.copilot-chat", "debug-logs", sessionId, "main.jsonl");
112
+ }
96
113
  function resolveModelFromObject(obj, fallbackModel) {
97
114
  const keys = ["model", "modelName", "model_name", "resolvedModel", "deployment", "engine"];
98
115
  for (const key of keys) {
@@ -108,6 +125,7 @@ function extractTokensFromObject(obj) {
108
125
  let cachedInputTokens = 0;
109
126
  let outputTokens = 0;
110
127
  let totalTokens = 0;
128
+ let costUsd = 0;
111
129
  let foundAny = false;
112
130
  for (const [rawKey, rawValue] of Object.entries(obj)) {
113
131
  const key = normalizeKey(rawKey);
@@ -135,6 +153,11 @@ function extractTokensFromObject(obj) {
135
153
  foundAny = true;
136
154
  continue;
137
155
  }
156
+ if (["costusd", "cost", "usd", "usdcost"].includes(key)) {
157
+ costUsd += value;
158
+ foundAny = true;
159
+ continue;
160
+ }
138
161
  }
139
162
  if (!foundAny) {
140
163
  return null;
@@ -142,7 +165,7 @@ function extractTokensFromObject(obj) {
142
165
  if (totalTokens === 0) {
143
166
  totalTokens = inputTokens + outputTokens;
144
167
  }
145
- return { inputTokens, cachedInputTokens, outputTokens, totalTokens };
168
+ return { inputTokens, cachedInputTokens, outputTokens, totalTokens, costUsd };
146
169
  }
147
170
  function collectModelUsage(value, usageByModel, currentModel = "copilot") {
148
171
  if (Array.isArray(value)) {
@@ -162,12 +185,14 @@ function collectModelUsage(value, usageByModel, currentModel = "copilot") {
162
185
  cachedInputTokens: 0,
163
186
  outputTokens: 0,
164
187
  totalTokens: 0,
188
+ costUsd: 0,
165
189
  observations: 0,
166
190
  };
167
191
  existing.inputTokens += tokens.inputTokens;
168
192
  existing.cachedInputTokens += tokens.cachedInputTokens;
169
193
  existing.outputTokens += tokens.outputTokens;
170
194
  existing.totalTokens += tokens.totalTokens;
195
+ existing.costUsd += tokens.costUsd;
171
196
  existing.observations += 1;
172
197
  usageByModel.set(resolvedModel, existing);
173
198
  }
@@ -175,17 +200,29 @@ function collectModelUsage(value, usageByModel, currentModel = "copilot") {
175
200
  collectModelUsage(child, usageByModel, resolvedModel);
176
201
  }
177
202
  }
178
- export function resolveDebugUsageBySession(sessionId, hookTimestamp, baseDir = WORKSPACE_STORAGE_DIR) {
203
+ export function resolveDebugUsageBySession(sessionId, hookTimestamp, baseDir = WORKSPACE_STORAGE_DIR, workspaceIdFromTranscript) {
179
204
  if (!sessionId) {
180
205
  return undefined;
181
206
  }
182
- const workspace = inferWorkspaceIdBySession(sessionId, hookTimestamp, baseDir);
183
- if (!workspace) {
184
- return undefined;
207
+ let workspaceId = workspaceIdFromTranscript;
208
+ let mainJsonlPath;
209
+ if (workspaceId) {
210
+ const pathFromTranscript = buildMainLogPath(baseDir, workspaceId, sessionId);
211
+ if (existsSync(pathFromTranscript)) {
212
+ mainJsonlPath = pathFromTranscript;
213
+ }
185
214
  }
186
- const mainJsonlPath = join(workspace.debugLogsDir, sessionId, "main.jsonl");
187
- if (!existsSync(mainJsonlPath)) {
188
- return undefined;
215
+ if (!mainJsonlPath) {
216
+ const workspace = inferWorkspaceIdBySession(sessionId, hookTimestamp, baseDir);
217
+ if (!workspace) {
218
+ return undefined;
219
+ }
220
+ workspaceId = workspace.workspaceId;
221
+ const fallbackPath = buildMainLogPath(baseDir, workspace.workspaceId, sessionId);
222
+ if (!existsSync(fallbackPath)) {
223
+ return undefined;
224
+ }
225
+ mainJsonlPath = fallbackPath;
189
226
  }
190
227
  const usageByModel = new Map();
191
228
  const lines = readFileSync(mainJsonlPath, "utf-8").split("\n");
@@ -205,12 +242,13 @@ export function resolveDebugUsageBySession(sessionId, hookTimestamp, baseDir = W
205
242
  if (usageByModel.size === 0) {
206
243
  return {
207
244
  sessionId,
208
- workspaceId: workspace.workspaceId,
245
+ workspaceId: workspaceId ?? "unknown",
209
246
  model: "copilot",
210
247
  inputTokens: 0,
211
248
  cachedInputTokens: 0,
212
249
  outputTokens: 0,
213
250
  totalTokens: 0,
251
+ costUsd: null,
214
252
  sourcePath: mainJsonlPath,
215
253
  modelUsage: {},
216
254
  };
@@ -222,11 +260,13 @@ export function resolveDebugUsageBySession(sessionId, hookTimestamp, baseDir = W
222
260
  let cachedInputTokens = 0;
223
261
  let outputTokens = 0;
224
262
  let totalTokens = 0;
263
+ let totalCostUsd = 0;
225
264
  for (const [model, usage] of usageByModel.entries()) {
226
265
  inputTokens += usage.inputTokens;
227
266
  cachedInputTokens += usage.cachedInputTokens;
228
267
  outputTokens += usage.outputTokens;
229
268
  totalTokens += usage.totalTokens;
269
+ totalCostUsd += usage.costUsd;
230
270
  if (usage.totalTokens > dominantTotal || (usage.totalTokens === dominantTotal && usage.observations > dominantObs)) {
231
271
  dominantModel = model;
232
272
  dominantTotal = usage.totalTokens;
@@ -236,12 +276,13 @@ export function resolveDebugUsageBySession(sessionId, hookTimestamp, baseDir = W
236
276
  const modelUsage = Object.fromEntries(usageByModel.entries());
237
277
  return {
238
278
  sessionId,
239
- workspaceId: workspace.workspaceId,
279
+ workspaceId: workspaceId ?? "unknown",
240
280
  model: dominantModel,
241
281
  inputTokens,
242
282
  cachedInputTokens,
243
283
  outputTokens,
244
284
  totalTokens,
285
+ costUsd: totalCostUsd > 0 ? totalCostUsd : null,
245
286
  sourcePath: mainJsonlPath,
246
287
  modelUsage,
247
288
  };
@@ -296,11 +337,13 @@ export function buildPayloadFromStdin(stdin, read = readTranscript, resolveUsage
296
337
  })() : [];
297
338
  const toolCallCount = countToolCalls(entries);
298
339
  const startedAt = extractStartTime(entries) ?? stdin.timestamp ?? new Date().toISOString();
299
- const usage = resolveUsage(stdin.session_id, stdin.timestamp);
340
+ const parsedLocation = parseTranscriptPathLocation(stdin.transcript_path);
341
+ const sessionId = parsedLocation?.sessionId ?? stdin.session_id;
342
+ const usage = resolveUsage(sessionId, stdin.timestamp, WORKSPACE_STORAGE_DIR, parsedLocation?.workspaceId);
300
343
  return {
301
344
  tool: "copilot",
302
345
  // session_id is optional per VS Code spec; synthesize a fallback if absent
303
- sessionId: stdin.session_id ?? `copilot-${Date.now()}-${process.pid}`,
346
+ sessionId: sessionId ?? `copilot-${Date.now()}-${process.pid}`,
304
347
  gitUsername: resolveGitUsername(stdin.cwd),
305
348
  // Model/tokens are inferred from debug logs by session_id; falls back safely.
306
349
  model: usage?.model ?? "copilot",
@@ -308,7 +351,7 @@ export function buildPayloadFromStdin(stdin, read = readTranscript, resolveUsage
308
351
  cachedInputTokens: usage?.cachedInputTokens ?? 0,
309
352
  cacheCreationInputTokens: 0,
310
353
  outputTokens: usage?.outputTokens ?? 0,
311
- costUsd: null,
354
+ costUsd: usage?.costUsd ?? null,
312
355
  toolCallCount,
313
356
  startedAt,
314
357
  rawPayload: usage ? {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jagit/hook-copilot",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "jagit-hook-copilot": "dist/index.js"
@@ -9,7 +9,7 @@
9
9
  "dist"
10
10
  ],
11
11
  "dependencies": {
12
- "@jagit/agent-reporter": "0.0.4"
12
+ "@jagit/agent-reporter": "0.0.5"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^25.9.3",