@oh-my-pi/omp-stats 18.2.7 → 18.2.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.2.9] - 2026-09-22
6
+
7
+ ### Fixed
8
+
9
+ - Fixed background statistics spans to use unique identifiers and close jobs correctly.
10
+
5
11
  ## [18.2.5] - 2026-09-17
6
12
 
7
13
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/omp-stats",
4
- "version": "18.2.7",
4
+ "version": "18.2.9",
5
5
  "description": "Local observability dashboard for pi AI usage statistics",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -39,9 +39,9 @@
39
39
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
40
40
  },
41
41
  "dependencies": {
42
- "@oh-my-pi/pi-ai": "18.2.7",
43
- "@oh-my-pi/pi-catalog": "18.2.7",
44
- "@oh-my-pi/pi-utils": "18.2.7",
42
+ "@oh-my-pi/pi-ai": "18.2.9",
43
+ "@oh-my-pi/pi-catalog": "18.2.9",
44
+ "@oh-my-pi/pi-utils": "18.2.9",
45
45
  "@tailwindcss/node": "^4.3.2",
46
46
  "chart.js": "^4.5.1",
47
47
  "lucide-react": "^1.24.0",
package/src/trace.ts CHANGED
@@ -115,6 +115,15 @@ interface PendingToolCall {
115
115
  modelEnd: number;
116
116
  }
117
117
 
118
+ /** Background job opened by an async-running tool result. */
119
+ interface PendingBackgroundJob {
120
+ jobId: string;
121
+ start: number;
122
+ label: string;
123
+ entryId?: string;
124
+ end?: number;
125
+ }
126
+
118
127
  /** Task tool result row, used to place subagent spans on the parent track. */
119
128
  interface TaskResultFact {
120
129
  childId: string;
@@ -280,8 +289,8 @@ function scanTranscript(
280
289
  const pendingTools: PendingToolCall[] = [];
281
290
  const toolStarts = new Map<string, ToolStartFact>();
282
291
  const toolResults = new Map<string, { end: number; isError: boolean; entryId?: string; toolName: string }>();
283
- const backgroundOpens: Array<{ jobId: string; start: number; label: string; entryId?: string }> = [];
284
- const asyncCloses = new Map<string, number>();
292
+ const backgroundJobs: PendingBackgroundJob[] = [];
293
+ const activeBackgroundJobs = new Map<string, PendingBackgroundJob>();
285
294
  const turns: Array<{ time: number; label: string; entryId?: string }> = [];
286
295
  const taskResults: TaskResultFact[] = [];
287
296
 
@@ -467,12 +476,14 @@ function scanTranscript(
467
476
  "jobId" in asyncInfo &&
468
477
  typeof asyncInfo.jobId === "string"
469
478
  ) {
470
- backgroundOpens.push({
479
+ const job: PendingBackgroundJob = {
471
480
  jobId: asyncInfo.jobId,
472
481
  start: end,
473
482
  label: headText(`${msg.toolName ?? "tool"} job`, LABEL_MAX),
474
483
  entryId: entry.id,
475
- });
484
+ };
485
+ backgroundJobs.push(job);
486
+ activeBackgroundJobs.set(job.jobId, job);
476
487
  }
477
488
  if (msg.toolName === "task" && Array.isArray(details?.results)) {
478
489
  for (const result of details.results) {
@@ -499,7 +510,10 @@ function scanTranscript(
499
510
  if (closeAt !== undefined && Array.isArray(jobs)) {
500
511
  for (const job of jobs) {
501
512
  if (!job || typeof job !== "object" || !("jobId" in job) || typeof job.jobId !== "string") continue;
502
- if (!asyncCloses.has(job.jobId)) asyncCloses.set(job.jobId, closeAt);
513
+ const targetJob = activeBackgroundJobs.get(job.jobId);
514
+ if (!targetJob || targetJob.end !== undefined) continue;
515
+ targetJob.end = closeAt;
516
+ activeBackgroundJobs.delete(job.jobId);
503
517
  }
504
518
  }
505
519
  continue;
@@ -549,19 +563,18 @@ function scanTranscript(
549
563
  spans.push(span);
550
564
  }
551
565
 
552
- // Background spans: opened by an async-running tool result, closed by async-result delivery.
553
- for (const open of backgroundOpens) {
554
- const close = asyncCloses.get(open.jobId);
555
- const end = close ?? (lastChainTs || open.start);
566
+ // Background spans: opened by an async-running tool result, closed by the async-result delivery.
567
+ for (const [backgroundIndex, job] of backgroundJobs.entries()) {
568
+ const end = job.end ?? (lastChainTs || job.start);
556
569
  const span: TraceSpan = {
557
- id: `${trackId}:bg:${open.jobId}`,
570
+ id: `${trackId}:bg:${backgroundIndex}:${job.jobId}`,
558
571
  kind: "background",
559
- start: open.start,
560
- end: Math.max(open.start, end),
561
- label: open.label,
572
+ start: job.start,
573
+ end: Math.max(job.start, end),
574
+ label: job.label,
562
575
  };
563
- if (open.entryId) span.entryId = open.entryId;
564
- if (close === undefined) span.unterminated = true;
576
+ if (job.entryId) span.entryId = job.entryId;
577
+ if (job.end === undefined) span.unterminated = true;
565
578
  spans.push(span);
566
579
  }
567
580