@solongate/proxy 0.83.68 → 0.83.70

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.
@@ -297,9 +297,8 @@ function readClaudeCode(transcriptPath) {
297
297
  * for the session, and sending cumulative values to a store that adds rows
298
298
  * would count the whole session again on every turn.
299
299
  */
300
- function readCodex(transcriptPath) {
300
+ function readCodex(transcriptPath, sessionId) {
301
301
  const out = [];
302
- let ordinal = 0;
303
302
  for (const line of tailLines(transcriptPath)) {
304
303
  if (!line || line[0] !== '{') continue;
305
304
  let r;
@@ -308,19 +307,27 @@ function readCodex(transcriptPath) {
308
307
  const p = r.payload;
309
308
  if (!p || p.type !== 'token_count') continue;
310
309
 
311
- ordinal++;
312
310
  const last = p.info && p.info.last_token_usage;
313
311
  if (!last) continue;
314
312
 
315
313
  const total = num(last.total_tokens) || (num(last.input_tokens) + num(last.output_tokens));
316
314
  if (total <= 0) continue;
317
315
 
316
+ // The record's OWN ordinal, never a count of what this read happened to
317
+ // see. Counting within the tail renumbers every event the moment the file
318
+ // grows past the window — the same turn gets a new key, and a new key is a
319
+ // second row. Older rollouts carry no ordinal, so those fall back to the
320
+ // timestamp paired with the running total, which together do not repeat.
321
+ const id = typeof r.ordinal === 'number'
322
+ ? 'o:' + r.ordinal
323
+ : 't:' + (r.timestamp || '') + ':' + num((p.info.total_token_usage || {}).total_tokens);
324
+
318
325
  out.push({
319
- // The rollout file is append-only and the events are in order, so the
320
- // count of token_count events before this one is stable across re-reads
321
- // of the same file. Paired with the timestamp so a truncated tail cannot
322
- // renumber a turn that was already sent.
323
- turn_key: 'tc:' + ordinal + ':' + (r.timestamp || ''),
326
+ // Prefixed with the session, because an ordinal is only unique WITHIN a
327
+ // rollout. The server's key does not include the session resuming a
328
+ // conversation would otherwise double every turn it copies forward so
329
+ // a positional id has to carry its own scope.
330
+ turn_key: (sessionId || 'codex') + ':' + id,
324
331
  // input_tokens INCLUDES cached here, and output INCLUDES reasoning —
325
332
  // the opposite of Claude Code's disjoint buckets. The total is the
326
333
  // provider's own, so the difference never has to be reconciled.
@@ -437,9 +444,10 @@ function readAntigravity(conversationId) {
437
444
  if (input + output <= 0) continue;
438
445
 
439
446
  out.push({
440
- // The generation index, which is this client's own identifier for the
441
- // model call and is stable across re-reads of the same conversation.
442
- turn_key: 'gen:' + row.idx,
447
+ // The generation index, prefixed with the conversation it counts
448
+ // within: an index is only unique inside one database, and the
449
+ // server's key does not include the session.
450
+ turn_key: conversationId + ':gen:' + row.idx,
443
451
  input,
444
452
  output,
445
453
  // The cached half of the prompt, which Antigravity reports separately
@@ -476,7 +484,11 @@ function collectTokens(data, source) {
476
484
  return readAntigravity(id);
477
485
  }
478
486
  if (!transcript || !existsSync(transcript)) return [];
479
- if (source === 'codex') return readCodex(transcript);
487
+ const sessionId = data.session_id || data.sessionId || '';
488
+ if (source === 'codex') return readCodex(transcript, sessionId);
489
+ // Claude Code needs no prefix: requestId is provider-issued and unique
490
+ // across every transcript, which is exactly what makes a resumed session
491
+ // report the same turn once rather than twice.
480
492
  return readClaudeCode(transcript);
481
493
  }
482
494
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.68",
3
+ "version": "0.83.70",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -61,12 +61,12 @@
61
61
  "node": ">=20.0.0"
62
62
  },
63
63
  "optionalDependencies": {
64
- "@solongate/guard-linux-x64": "0.83.68",
65
- "@solongate/guard-linux-arm64": "0.83.68",
66
- "@solongate/guard-darwin-x64": "0.83.68",
67
- "@solongate/guard-darwin-arm64": "0.83.68",
68
- "@solongate/guard-win32-x64": "0.83.68",
69
- "@solongate/guard-win32-arm64": "0.83.68"
64
+ "@solongate/guard-linux-x64": "0.83.70",
65
+ "@solongate/guard-linux-arm64": "0.83.70",
66
+ "@solongate/guard-darwin-x64": "0.83.70",
67
+ "@solongate/guard-darwin-arm64": "0.83.70",
68
+ "@solongate/guard-win32-x64": "0.83.70",
69
+ "@solongate/guard-win32-arm64": "0.83.70"
70
70
  },
71
71
  "dependencies": {
72
72
  "@modelcontextprotocol/sdk": "^1.26.0",