@ramarivera/coding-agent-langfuse 0.1.40 → 0.1.42

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.
@@ -39,6 +39,7 @@ type BackfillOptions = {
39
39
  limit?: number;
40
40
  sinceMs?: number;
41
41
  untilMs?: number;
42
+ sessionIds: Set<string>;
42
43
  batchSize: number;
43
44
  maxRequestBytes: number;
44
45
  maxFieldBytes: number;
@@ -63,7 +64,10 @@ type FollowSummary = RunSummary & {
63
64
  declare const allAgents: AgentName[];
64
65
  declare const defaultEndpoint = "https://langfuse.ai.roxasroot.net/otel/v1/traces";
65
66
  declare function parseArgs(argv: string[]): BackfillOptions;
66
- declare function codexEvents(homeDir: string): BackfillEvent[];
67
+ declare function codexEvents(homeDir: string, options?: {
68
+ sessionIds?: Set<string>;
69
+ sinceMs?: number;
70
+ }): BackfillEvent[];
67
71
  declare function claudeEvents(homeDir: string): BackfillEvent[];
68
72
  declare function piEvents(homeDir: string): BackfillEvent[];
69
73
  declare function grokEvents(homeDir: string): BackfillEvent[];
package/dist/backfill.js CHANGED
@@ -40,6 +40,7 @@ Options:
40
40
  --home PATH Home directory to scan (default: current user home)
41
41
  --since ISO_OR_MS Only import events at or after this timestamp
42
42
  --until ISO_OR_MS Only import events before or at this timestamp
43
+ --session-id ID Only import one session id; repeat or comma-separate
43
44
  --limit N Stop after N unsent events
44
45
  --batch-size N OTLP spans per POST (default: 50)
45
46
  --max-request-bytes N Split OTLP POSTs below this JSON byte size (default: ${defaultMaxRequestBytes})
@@ -74,6 +75,7 @@ function parseArgs(argv) {
74
75
  let pollIntervalMs = 5_000;
75
76
  let idleExitAfterMs;
76
77
  const agents = new Set(allAgents);
78
+ const sessionIds = new Set();
77
79
  for (let i = 0; i < argv.length; i++) {
78
80
  const arg = argv[i];
79
81
  const next = () => {
@@ -138,6 +140,13 @@ function parseArgs(argv) {
138
140
  else if (arg === "--until") {
139
141
  untilMs = parseTime(next());
140
142
  }
143
+ else if (arg === "--session-id") {
144
+ for (const item of next().split(",")) {
145
+ const sessionId = item.trim();
146
+ if (sessionId.length > 0)
147
+ sessionIds.add(sessionId);
148
+ }
149
+ }
141
150
  else {
142
151
  throw new Error(`Unknown argument '${arg}'`);
143
152
  }
@@ -176,6 +185,7 @@ function parseArgs(argv) {
176
185
  limit,
177
186
  sinceMs,
178
187
  untilMs,
188
+ sessionIds,
179
189
  batchSize,
180
190
  maxRequestBytes,
181
191
  maxFieldBytes,
@@ -235,7 +245,7 @@ function listFiles(root, predicate) {
235
245
  }
236
246
  if (stat.isDirectory())
237
247
  stack.push(path);
238
- else if (stat.isFile() && predicate(path))
248
+ else if (stat.isFile() && predicate(path, stat))
239
249
  out.push(path);
240
250
  }
241
251
  }
@@ -378,8 +388,33 @@ function isGenerationEvent(event) {
378
388
  return event.usage !== undefined && event.role !== "user" &&
379
389
  event.role !== "developer" && event.role !== "system";
380
390
  }
381
- function codexEvents(homeDir) {
382
- const files = listFiles(join(homeDir, ".codex/sessions"), (path) => path.endsWith(".jsonl"));
391
+ function codexSessionPathTimeMs(path) {
392
+ const rolloutMatch = path.match(/rollout-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})/);
393
+ if (rolloutMatch) {
394
+ const [, year, month, day, hour, minute, second] = rolloutMatch;
395
+ return Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
396
+ }
397
+ const folderMatch = path.match(/[\\/]sessions[\\/](\d{4})[\\/](\d{2})[\\/](\d{2})[\\/]/);
398
+ if (!folderMatch)
399
+ return undefined;
400
+ const [, year, month, day] = folderMatch;
401
+ return Date.UTC(Number(year), Number(month) - 1, Number(day));
402
+ }
403
+ function fileMayContainCodexWindow(path, stat, options) {
404
+ if (options.sessionIds !== undefined &&
405
+ options.sessionIds.size > 0 &&
406
+ ![...options.sessionIds].some((sessionId) => path.includes(sessionId))) {
407
+ return false;
408
+ }
409
+ if (options.sinceMs === undefined)
410
+ return true;
411
+ if (stat.mtimeMs >= options.sinceMs)
412
+ return true;
413
+ const pathTimeMs = codexSessionPathTimeMs(path);
414
+ return pathTimeMs === undefined || pathTimeMs >= options.sinceMs;
415
+ }
416
+ function codexEvents(homeDir, options = {}) {
417
+ const files = listFiles(join(homeDir, ".codex/sessions"), (path, stat) => path.endsWith(".jsonl") && fileMayContainCodexWindow(path, stat, options));
383
418
  const seenTokenCounts = new Set();
384
419
  return files.flatMap((path) => {
385
420
  const rows = parseJsonl(path).map(asRecord);
@@ -1388,7 +1423,10 @@ function describeError(error) {
1388
1423
  function discoverEvents(options) {
1389
1424
  const providers = {
1390
1425
  claude: (inner) => claudeEvents(inner.homeDir),
1391
- codex: (inner) => codexEvents(inner.homeDir),
1426
+ codex: (inner) => codexEvents(inner.homeDir, {
1427
+ sessionIds: inner.sessionIds,
1428
+ sinceMs: inner.sinceMs,
1429
+ }),
1392
1430
  grok: (inner) => grokEvents(inner.homeDir),
1393
1431
  opencode: (inner) => opencodeEvents(inner.homeDir, {
1394
1432
  rowLimit: inner.limit,
@@ -1402,6 +1440,7 @@ function discoverEvents(options) {
1402
1440
  .flatMap((agent) => providers[agent](options))
1403
1441
  .filter((event) => options.sinceMs === undefined || event.startMs >= options.sinceMs)
1404
1442
  .filter((event) => options.untilMs === undefined || event.startMs <= options.untilMs)
1443
+ .filter((event) => options.sessionIds.size === 0 || options.sessionIds.has(event.sessionId))
1405
1444
  .sort((a, b) => a.startMs - b.startMs);
1406
1445
  }
1407
1446
  async function run(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramarivera/coding-agent-langfuse",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Universal coding-agent Langfuse backfiller and live OTLP helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",