@pasko70/pibo 3.0.0 → 3.0.2

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 (77) hide show
  1. package/README.md +20 -6
  2. package/dist/agent-runtime/context-build.js +26 -2
  3. package/dist/agent-runtime/routed-session.js +108 -9
  4. package/dist/agent-runtimes/codex-native/adapter.js +12 -0
  5. package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
  6. package/dist/agent-runtimes/omp/adapter.js +3 -7
  7. package/dist/agent-runtimes/pi/adapter.js +5 -3
  8. package/dist/agent-runtimes/pi/routed-session.js +49 -8
  9. package/dist/agent-runtimes/pi/runtime.js +6 -0
  10. package/dist/apps/chat/agent-profiles.js +31 -2
  11. package/dist/apps/chat/agent-store.js +43 -3
  12. package/dist/apps/chat/chat-request-normalizers.js +31 -0
  13. package/dist/apps/chat/data/chat-data-mappers.js +61 -11
  14. package/dist/apps/chat/data/session-query-service.js +33 -10
  15. package/dist/apps/chat/data/timeline-query-service.js +60 -35
  16. package/dist/apps/chat/output-compactor.js +144 -21
  17. package/dist/apps/chat/output-event-policy.js +179 -2
  18. package/dist/apps/chat/stream.js +16 -7
  19. package/dist/apps/chat/trace-v2.js +1 -0
  20. package/dist/apps/chat/web-app.js +236 -64
  21. package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-B6GZgWSj.js} +1 -1
  22. package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-BPotHecb.js} +1 -1
  23. package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-BzqQKeVO.js} +1 -1
  24. package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CvaPTBTN.js} +1 -1
  25. package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-paagejNj.js} +1 -1
  26. package/dist/apps/chat-ui/assets/index-6JJV-eic.js +228 -0
  27. package/dist/apps/chat-ui/assets/index-CeL9JPP5.css +1 -0
  28. package/dist/apps/chat-ui/index.html +2 -2
  29. package/dist/apps/chat-vscode-web/assets/{index-Dtw2Z7jz.js → index-U-MSErGa.js} +4 -4
  30. package/dist/apps/chat-vscode-web/index.html +1 -1
  31. package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
  32. package/dist/cli-session/localSessionSource.js +153 -161
  33. package/dist/cli.js +7 -11
  34. package/dist/core/context-build.js +7 -16
  35. package/dist/core/output-persistence-retry.js +484 -0
  36. package/dist/core/output-render-sequence.js +331 -0
  37. package/dist/core/profiles.js +24 -0
  38. package/dist/core/provider-recovery.js +7 -1
  39. package/dist/core/session-model.js +37 -1
  40. package/dist/core/session-router.js +67 -20
  41. package/dist/data/ingest-service.js +91 -9
  42. package/dist/data/navigation-store.js +5 -5
  43. package/dist/data/schema.js +122 -2
  44. package/dist/data/session-store.js +3 -3
  45. package/dist/debug/index.js +44 -2
  46. package/dist/gateway/web.js +8 -8
  47. package/dist/local/client.js +3 -2
  48. package/dist/plugins/context-files-store.js +30 -0
  49. package/dist/plugins/context-files.js +52 -1
  50. package/dist/plugins/registry.js +2 -0
  51. package/dist/plugins/user-profile-resources.js +22 -0
  52. package/dist/reliability/store.js +246 -37
  53. package/dist/session-ui/terminalRows.js +6 -3
  54. package/dist/sessions/pibo-data-store.js +145 -0
  55. package/dist/sessions/store.js +42 -0
  56. package/dist/setup/cli.js +422 -6
  57. package/dist/setup/installation-profiles.js +464 -0
  58. package/dist/shared/deterministic-digest.js +94 -0
  59. package/dist/shared/trace-engine.js +54 -0
  60. package/dist/shared/trace-event-projection.js +124 -67
  61. package/dist/shared/trace-history.js +47 -12
  62. package/dist/shared/trace-live-reducer.js +23 -4
  63. package/dist/shared/trace-nodes.js +17 -28
  64. package/dist/shared/trace-order.js +41 -5
  65. package/dist/shared/trace-page-merge.js +44 -14
  66. package/dist/shared/trace-patch-nodes.js +2 -0
  67. package/dist/shared/trace-subagent-links.js +4 -1
  68. package/dist/shared/trace-tool-identity.js +15 -8
  69. package/dist/signals/projector.js +28 -6
  70. package/dist/subagents/tool.js +7 -3
  71. package/docs/ops/vscode-extension-release.md +9 -1
  72. package/npm-shrinkwrap.json +2 -2
  73. package/package.json +2 -1
  74. package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
  75. package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
  76. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  77. package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
@@ -3,10 +3,19 @@ import { compareTraceNodes, flattenTraceNodes } from "./trace-nodes.js";
3
3
  import { attachAsyncAgentRunNode, isRunStartToolNode } from "./trace-async-agent-runs.js";
4
4
  import { createRunNotificationNode, parseRunNotificationText } from "./trace-run-notifications.js";
5
5
  import { findLikelyTraceChildSession, isSubagentToolName, } from "./trace-subagent-links.js";
6
+ import { qualifiedToolNodeId } from "./trace-tool-identity.js";
6
7
  export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent, childByParent, linkedChildByToolCallId, historyCoverage, openTranscriptEventIds, sessionStatus) {
7
8
  const payload = storedEvent.payload;
9
+ if (payload.type === "message_started" && payload.source === "user" && payload.eventId) {
10
+ const persistedUser = flattenTraceNodes([...nodes]).find((node) => node.type === "user.message"
11
+ && isPersistedHistorySource(node.source)
12
+ && node.eventId === payload.eventId);
13
+ if (persistedUser)
14
+ applyRenderSequence(persistedUser, storedEvent, payload);
15
+ }
8
16
  const confirmedUserMessage = historyCoverage.mode !== "none" ? confirmedUserMessageEchoNode(nodes, storedEvent) : undefined;
9
17
  if (confirmedUserMessage) {
18
+ applyRenderSequence(confirmedUserMessage, storedEvent, payload);
10
19
  if (payload.type === "message_steered" && payload.activeEventId) {
11
20
  confirmedUserMessage.parentId = messageTurnNodeId(payload.activeEventId);
12
21
  }
@@ -16,21 +25,23 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
16
25
  || (historyCoverage.mode === "product" && isProductHistoryEchoEvent(payload))) &&
17
26
  historyCoversEvent(payload, historyCoverage) &&
18
27
  !shouldKeepTranscriptEchoEvent(payload, openTranscriptEventIds)) {
28
+ applyRenderSequenceToHistoryNode(byId, storedEvent, payload);
19
29
  return;
20
30
  }
21
31
  if (isNativeHistoryToolEchoEvent(payload, historyCoverage, sessionStatus)) {
32
+ applyRenderSequenceToHistoryNode(byId, storedEvent, payload);
22
33
  return;
23
34
  }
24
35
  if (payload.type === "assistant_delta") {
25
- mergeAssistantDeltaEvent(nodes, byId, payload, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource);
36
+ mergeAssistantDeltaEvent(nodes, byId, payload, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
26
37
  return;
27
38
  }
28
39
  if (payload.type === "thinking_delta") {
29
- mergeThinkingDeltaEvent(nodes, byId, payload, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource);
40
+ mergeThinkingDeltaEvent(nodes, byId, payload, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
30
41
  return;
31
42
  }
32
43
  if (payload.type === "assistant_message") {
33
- const node = assistantMessageNodeFromEvent(piboSessionId, payload, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource);
44
+ const node = assistantMessageNodeFromEvent(piboSessionId, payload, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
34
45
  const existing = byId.get(node.id) ?? findMatchingContentNode(byId, node);
35
46
  if (existing) {
36
47
  mergeAssistantMessageEvent(existing, node);
@@ -40,7 +51,7 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
40
51
  byId.set(node.id, node);
41
52
  return;
42
53
  }
43
- const node = traceNodeFromEvent(piboSessionId, payload, childByParent, linkedChildByToolCallId, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource);
54
+ const node = traceNodeFromEvent(piboSessionId, payload, childByParent, linkedChildByToolCallId, sessionStatus, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
44
55
  if (!node)
45
56
  return;
46
57
  applyStoredPayloadRef(node, payload, storedEvent.storedPayloadRef);
@@ -89,8 +100,7 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
89
100
  }
90
101
  }
91
102
  if (node.toolCallId) {
92
- const existing = [...byId.values()].find((candidate) => candidate.toolCallId === node.toolCallId &&
93
- (candidate.type === "tool.call" || candidate.type === "agent.delegation"));
103
+ const existing = byId.get(node.id) ?? findUniqueLegacyToolTarget(byId, node, payload);
94
104
  if (existing) {
95
105
  if (isRunStartToolNode(existing) && node.type === "agent.delegation") {
96
106
  attachAsyncAgentRunNode(existing, piboSessionId, storedEvent.createdAt, node);
@@ -125,6 +135,36 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
125
135
  for (const indexed of flattenTraceNodes([node]))
126
136
  byId.set(indexed.id, indexed);
127
137
  }
138
+ function findUniqueLegacyToolTarget(byId, update, event) {
139
+ if ("eventId" in event && event.eventId)
140
+ return undefined;
141
+ const candidates = [...byId.values()].filter((candidate) => candidate.toolCallId === update.toolCallId
142
+ && (candidate.type === "tool.call" || candidate.type === "tool.result" || candidate.type === "agent.delegation"));
143
+ return candidates.length === 1 ? candidates[0] : undefined;
144
+ }
145
+ function applyRenderSequenceToHistoryNode(byId, storedEvent, event) {
146
+ const stableKey = eventStableKey(event, traceEventInstanceKey(storedEvent.eventSequence, storedEvent.streamId, event, storedEvent.id));
147
+ const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
148
+ const target = [...byId.values()].find((node) => node.stableKey === stableKey
149
+ || ("toolCallId" in event
150
+ && typeof event.toolCallId === "string"
151
+ && node.toolCallId === event.toolCallId
152
+ && (!eventId || !node.eventId || node.eventId === eventId)));
153
+ if (target)
154
+ applyRenderSequence(target, storedEvent, event);
155
+ }
156
+ function applyRenderSequence(node, storedEvent, event) {
157
+ const renderSequence = storedEvent.renderSequence ?? event.renderSequence;
158
+ const eventSequence = storedEvent.eventSequence ?? storedEvent.streamId;
159
+ if (renderSequence === undefined && eventSequence === undefined)
160
+ return;
161
+ const orderKey = node.orderKey ?? eventTraceOrder(eventSequence, eventNodeKind(event.type));
162
+ node.orderKey = {
163
+ ...orderKey,
164
+ ...(eventSequence === undefined ? {} : { turnSeq: eventSequence, eventSequence }),
165
+ ...(renderSequence === undefined ? {} : { renderSequence }),
166
+ };
167
+ }
128
168
  function applyStoredPayloadRef(node, event, storedPayloadRef) {
129
169
  if (!storedPayloadRef)
130
170
  return;
@@ -135,10 +175,11 @@ function applyStoredPayloadRef(node, event, storedPayloadRef) {
135
175
  node.payloadRefs = { ...node.payloadRefs, output: storedPayloadRef };
136
176
  }
137
177
  }
138
- function assistantMessageNodeFromEvent(piboSessionId, event, createdAt, eventSequence, streamId, streamFrameIndex, traceSource) {
178
+ function assistantMessageNodeFromEvent(piboSessionId, event, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
139
179
  const eventId = typeof event.eventId === "string" ? event.eventId : undefined;
140
180
  const assistantId = assistantEventNodeId(event);
141
- const id = assistantId ? assistantMessageNodeId(assistantId) : `event:${event.type}:${cryptoSafeId(event)}`;
181
+ const fallbackIdentity = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
182
+ const id = assistantId ? assistantMessageNodeId(assistantId) : `event:${event.type}:${fallbackIdentity}`;
142
183
  return {
143
184
  id,
144
185
  piboSessionId,
@@ -152,8 +193,8 @@ function assistantMessageNodeFromEvent(piboSessionId, event, createdAt, eventSeq
152
193
  summary: event.text,
153
194
  output: event.text,
154
195
  source: "event-log",
155
- stableKey: assistantId ? `assistant:${assistantId}` : eventStableKey(event),
156
- orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource),
196
+ stableKey: assistantId ? `assistant:${assistantId}` : eventStableKey(event, fallbackIdentity),
197
+ orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource, event.renderSequence, createdAt),
157
198
  children: [],
158
199
  };
159
200
  }
@@ -267,8 +308,9 @@ export function reconcileTranscriptUserMessages(nodes, events, turnTimings = [])
267
308
  continue;
268
309
  const payloadEventId = typeof event.eventId === "string" ? event.eventId : undefined;
269
310
  const eventId = payloadEventId ?? storedEvent.eventId;
270
- const canonicalId = `event:${event.type}:${payloadEventId ?? cryptoSafeId(event)}`;
271
- const stableKey = eventStableKey(event);
311
+ const fallbackIdentity = storedEvent.id ? `instance:${storedEvent.id}` : liveProjectionIdentity(event);
312
+ const canonicalId = `event:${event.type}:${payloadEventId ?? fallbackIdentity}`;
313
+ const stableKey = eventStableKey(event, fallbackIdentity);
272
314
  const text = typeof event.text === "string" ? event.text : undefined;
273
315
  const identityMatchIndex = transcriptUsers.findIndex((node) => transcriptUserMessageMatchesIdentity(node, canonicalId, stableKey, eventId));
274
316
  const matchIndex = identityMatchIndex !== -1
@@ -294,8 +336,9 @@ function confirmedUserMessageEchoNode(nodes, event) {
294
336
  return undefined;
295
337
  const payloadEventId = typeof payload.eventId === "string" ? payload.eventId : undefined;
296
338
  const eventId = payloadEventId ?? event.eventId;
297
- const canonicalId = `event:${payload.type}:${payloadEventId ?? cryptoSafeId(payload)}`;
298
- const stableKey = eventStableKey(payload);
339
+ const fallbackIdentity = event.id ? `instance:${event.id}` : liveProjectionIdentity(payload);
340
+ const canonicalId = `event:${payload.type}:${payloadEventId ?? fallbackIdentity}`;
341
+ const stableKey = eventStableKey(payload, fallbackIdentity);
299
342
  const text = typeof payload.text === "string" ? payload.text : undefined;
300
343
  const transcriptUsers = flattenTraceNodes([...nodes]).filter((node) => node.type === "user.message" && isPersistedHistorySource(node.source));
301
344
  const identityMatch = transcriptUsers.find((node) => transcriptUserMessageMatchesIdentity(node, canonicalId, stableKey, eventId));
@@ -377,9 +420,10 @@ export function latestTraceStreamId(events, initial) {
377
420
  return latest;
378
421
  }
379
422
  // ── event → node helpers ─────────────────────────────────────────
380
- function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByToolCallId, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource) {
423
+ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByToolCallId, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
381
424
  const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
382
- const id = `event:${event.type}:${eventId ?? cryptoSafeId(event)}`;
425
+ const fallbackIdentity = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
426
+ const id = `event:${event.type}:${eventId ?? fallbackIdentity}`;
383
427
  const turnParentId = eventId ? messageTurnNodeId(eventId) : undefined;
384
428
  const base = {
385
429
  id,
@@ -387,8 +431,8 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
387
431
  eventId,
388
432
  startedAt: createdAt,
389
433
  source: "event-log",
390
- stableKey: eventStableKey(event),
391
- orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource),
434
+ stableKey: eventStableKey(event, fallbackIdentity),
435
+ orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource, event.renderSequence, createdAt),
392
436
  children: [],
393
437
  };
394
438
  switch (event.type) {
@@ -403,7 +447,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
403
447
  startedAt: createdAt,
404
448
  source: "event-log",
405
449
  stableKey: eventId ? `run-notification:${eventId}` : id,
406
- orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource),
450
+ orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource, event.renderSequence, createdAt),
407
451
  notification,
408
452
  });
409
453
  }
@@ -419,8 +463,6 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
419
463
  }
420
464
  case "message_started":
421
465
  case "message_finished":
422
- if (event.source === "service")
423
- return undefined;
424
466
  return {
425
467
  ...base,
426
468
  id: eventId ? messageTurnNodeId(eventId) : id,
@@ -466,16 +508,18 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
466
508
  case "tool_execution_started":
467
509
  case "tool_execution_updated":
468
510
  case "tool_execution_finished": {
511
+ const toolNodeId = toolInvocationNodeId(event);
469
512
  const subagentTool = isSubagentToolName(event.toolName);
470
- const linkedPiboSessionId = linkedChildByToolCallId.get(event.toolCallId) ??
513
+ const linkedPiboSessionId = linkedChildByToolCallId.get(toolNodeId) ??
471
514
  (subagentTool
472
515
  ? findLikelyTraceChildSession(piboSessionId, event.toolName, event, childByParent)
473
516
  : undefined);
474
517
  return {
475
518
  ...base,
476
- id: `tool:${event.toolCallId}`,
519
+ id: toolNodeId,
477
520
  parentId: turnParentId,
478
521
  toolCallId: event.toolCallId,
522
+ toolInvocationOrdinal: event.toolInvocationOrdinal ?? 0,
479
523
  intent: event.intent,
480
524
  type: subagentTool ? "agent.delegation" : "tool.call",
481
525
  title: event.toolName,
@@ -498,26 +542,28 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
498
542
  ? stringifyPreview(event.result)
499
543
  : undefined,
500
544
  linkedPiboSessionId,
501
- stableKey: `tool:${event.toolCallId}`,
545
+ stableKey: toolNodeId,
502
546
  children: [],
503
547
  };
504
548
  }
505
549
  case "subagent_session": {
506
550
  const childPiboSessionId = nonEmptyString(event.childPiboSessionId);
507
- const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
551
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
552
+ const toolNodeId = event.toolCallId ? toolInvocationNodeId(event) : undefined;
508
553
  return {
509
554
  ...base,
510
- id: event.toolCallId ? `tool:${event.toolCallId}` : `event:subagent_session:${eventInstanceKey}`,
555
+ id: toolNodeId ?? `event:subagent_session:${eventInstanceKey}`,
511
556
  eventId,
512
557
  toolCallId: event.toolCallId,
558
+ toolInvocationOrdinal: event.toolInvocationOrdinal,
513
559
  type: "agent.delegation",
514
560
  title: event.toolName,
515
561
  status: sessionStatus === "running" ? "running" : "done",
516
562
  summary: event.subagentName,
517
563
  input: { subagentName: event.subagentName, threadKey: event.threadKey },
518
564
  linkedPiboSessionId: childPiboSessionId,
519
- stableKey: event.toolCallId
520
- ? `tool:${event.toolCallId}`
565
+ stableKey: toolNodeId
566
+ ? toolNodeId
521
567
  : childPiboSessionId ? `subagent:${childPiboSessionId}` : `subagent:event:${eventInstanceKey}`,
522
568
  children: [],
523
569
  };
@@ -534,7 +580,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
534
580
  output: event.result,
535
581
  };
536
582
  case "compaction_start": {
537
- const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
583
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
538
584
  return {
539
585
  ...base,
540
586
  id: `event:compaction:${eventInstanceKey}`,
@@ -547,7 +593,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
547
593
  };
548
594
  }
549
595
  case "compaction_end": {
550
- const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
596
+ const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
551
597
  return {
552
598
  ...base,
553
599
  id: `event:compaction:end:${eventInstanceKey}`,
@@ -577,11 +623,12 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
577
623
  return undefined;
578
624
  }
579
625
  }
580
- function mergeAssistantDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource) {
626
+ function mergeAssistantDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
581
627
  if (typeof event.text !== "string" || event.text.length === 0)
582
628
  return;
583
629
  const assistantId = assistantEventNodeId(event);
584
- const id = assistantId ? assistantMessageNodeId(assistantId) : `event:assistant_delta:${cryptoSafeId(event)}`;
630
+ const fallbackIdentity = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
631
+ const id = assistantId ? assistantMessageNodeId(assistantId) : `event:assistant_delta:${fallbackIdentity}`;
585
632
  const existing = byId.get(id);
586
633
  if (existing) {
587
634
  const text = `${typeof existing.output === "string" ? existing.output : ""}${event.text}`;
@@ -603,7 +650,7 @@ function mergeAssistantDeltaEvent(nodes, byId, event, sessionStatus, createdAt,
603
650
  output: event.text,
604
651
  source: "event-log",
605
652
  stableKey: assistantId ? `assistant:${assistantId}` : id,
606
- orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource),
653
+ orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource, event.renderSequence, createdAt),
607
654
  children: [],
608
655
  };
609
656
  nodes.push(node);
@@ -621,11 +668,12 @@ function mergeAssistantMessageEvent(target, update) {
621
668
  target.error = update.error ?? target.error;
622
669
  target.completedAt = update.completedAt ?? target.completedAt;
623
670
  }
624
- function mergeThinkingDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource) {
671
+ function mergeThinkingDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
625
672
  if (typeof event.text !== "string" || event.text.length === 0)
626
673
  return;
627
674
  const thinkingId = thinkingEventNodeId(event);
628
- const id = thinkingId ? thinkingNodeId(thinkingId) : `event:thinking_delta:${cryptoSafeId(event)}`;
675
+ const fallbackIdentity = traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId);
676
+ const id = thinkingId ? thinkingNodeId(thinkingId) : `event:thinking_delta:${fallbackIdentity}`;
629
677
  const stableKey = thinkingId ? `reasoning:${thinkingId}` : id;
630
678
  const existing = byId.get(id) ?? [...byId.values()].find((candidate) => candidate.type === "model.reasoning" && candidate.stableKey === stableKey);
631
679
  if (existing) {
@@ -648,7 +696,7 @@ function mergeThinkingDeltaEvent(nodes, byId, event, sessionStatus, createdAt, e
648
696
  output: event.text,
649
697
  source: "event-log",
650
698
  stableKey,
651
- orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource),
699
+ orderKey: eventTraceNodeOrder(eventSequence, event.type, streamId, streamFrameIndex, traceSource, event.renderSequence, createdAt),
652
700
  children: [],
653
701
  };
654
702
  nodes.push(node);
@@ -848,11 +896,24 @@ function isOpenTranscriptEvent(event) {
848
896
  event.type === "thinking_delta" ||
849
897
  event.type === "thinking_finished");
850
898
  }
851
- function eventTraceNodeOrder(eventSequence, type, streamId, streamFrameIndex, traceSource) {
852
- if (traceSource === "live") {
853
- return liveTraceOrder(streamId, streamFrameIndex, eventNodeKind(type));
854
- }
855
- return eventTraceOrder(eventSequence, eventNodeKind(type));
899
+ function eventTraceNodeOrder(eventSequence, type, streamId, streamFrameIndex, traceSource, renderSequence, createdAt) {
900
+ const order = traceSource === "live"
901
+ ? liveTraceOrder(streamId, streamFrameIndex, eventNodeKind(type))
902
+ : eventTraceOrder(eventSequence ?? streamId, eventNodeKind(type));
903
+ // Modern render sequences encode the first-visible millisecond plus a local
904
+ // slot. Deriving chronology from that immutable segment position keeps a
905
+ // live node in the same place when its persisted final receives a later
906
+ // database created_at timestamp.
907
+ const renderChronologyMs = renderSequence !== undefined && renderSequence >= 946_684_800_000_000
908
+ ? Math.floor(renderSequence / 1_000)
909
+ : undefined;
910
+ const chronologyMs = renderChronologyMs
911
+ ?? (createdAt && Number.isFinite(Date.parse(createdAt)) ? Date.parse(createdAt) : undefined);
912
+ return {
913
+ ...order,
914
+ ...(renderSequence === undefined ? {} : { renderSequence }),
915
+ ...(chronologyMs === undefined ? {} : { chronologyMs }),
916
+ };
856
917
  }
857
918
  function eventNodeKind(type) {
858
919
  switch (type) {
@@ -886,16 +947,16 @@ function eventNodeKind(type) {
886
947
  return "execution.command";
887
948
  }
888
949
  }
889
- function eventStableKey(event) {
950
+ function eventStableKey(event, fallbackIdentity) {
890
951
  const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
891
952
  if (event.type === "tool_call" ||
892
953
  event.type === "tool_execution_started" ||
893
954
  event.type === "tool_execution_updated" ||
894
955
  event.type === "tool_execution_finished") {
895
- return `tool:${event.toolCallId}`;
956
+ return toolInvocationNodeId(event);
896
957
  }
897
958
  if (event.type === "subagent_session" && event.toolCallId)
898
- return `tool:${event.toolCallId}`;
959
+ return toolInvocationNodeId(event);
899
960
  if (eventId && (event.type === "message_started" || event.type === "message_finished"))
900
961
  return `turn:${eventId}`;
901
962
  if (event.type === "thinking_started" ||
@@ -910,7 +971,7 @@ function eventStableKey(event) {
910
971
  if (assistantId)
911
972
  return `assistant:${assistantId}`;
912
973
  }
913
- return `event:${event.type}:${eventId ?? cryptoSafeId(event)}`;
974
+ return `event:${event.type}:${eventId ?? fallbackIdentity ?? liveProjectionIdentity(event)}`;
914
975
  }
915
976
  export function messageTurnNodeId(eventId) {
916
977
  return `event:message:${eventId}`;
@@ -1016,36 +1077,32 @@ function parseTimestamp(value) {
1016
1077
  const timestamp = new Date(value).getTime();
1017
1078
  return Number.isFinite(timestamp) ? timestamp : undefined;
1018
1079
  }
1019
- function traceEventInstanceKey(eventSequence, streamId, event) {
1080
+ function traceEventInstanceKey(eventSequence, streamId, event, storedInstanceId) {
1020
1081
  if (eventSequence !== undefined)
1021
1082
  return `sequence:${eventSequence}`;
1022
1083
  if (streamId !== undefined)
1023
1084
  return `stream:${streamId}`;
1024
- return cryptoSafeId(event);
1085
+ if (storedInstanceId)
1086
+ return `instance:${storedInstanceId}`;
1087
+ return liveProjectionIdentity(event);
1088
+ }
1089
+ function toolInvocationNodeId(event) {
1090
+ const eventId = event.eventId
1091
+ ?? (event.renderSequence !== undefined ? `render:${event.renderSequence}` : "unscoped");
1092
+ return qualifiedToolNodeId(event.toolCallId ?? "unlinked", eventId, event.toolInvocationOrdinal ?? 0);
1025
1093
  }
1026
1094
  function nonEmptyString(value) {
1027
1095
  if (typeof value !== "string")
1028
1096
  return undefined;
1029
1097
  return value.trim() || undefined;
1030
1098
  }
1031
- function cryptoSafeId(value) {
1032
- return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value))).slice(0, 48);
1033
- }
1034
- function base64UrlEncode(bytes) {
1035
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1036
- let output = "";
1037
- let index = 0;
1038
- for (; index + 2 < bytes.length; index += 3) {
1039
- const value = (bytes[index] << 16) | (bytes[index + 1] << 8) | bytes[index + 2];
1040
- output += alphabet[(value >> 18) & 63] + alphabet[(value >> 12) & 63] + alphabet[(value >> 6) & 63] + alphabet[value & 63];
1041
- }
1042
- if (index < bytes.length) {
1043
- const first = bytes[index];
1044
- const second = index + 1 < bytes.length ? bytes[index + 1] : 0;
1045
- const value = (first << 16) | (second << 8);
1046
- output += alphabet[(value >> 18) & 63] + alphabet[(value >> 12) & 63];
1047
- if (index + 1 < bytes.length)
1048
- output += alphabet[(value >> 6) & 63];
1049
- }
1050
- return output;
1099
+ function liveProjectionIdentity(value) {
1100
+ const existing = liveProjectionIdentityCache.get(value);
1101
+ if (existing)
1102
+ return existing;
1103
+ const identity = `live:${++liveProjectionIdentitySequence}`;
1104
+ liveProjectionIdentityCache.set(value, identity);
1105
+ return identity;
1051
1106
  }
1107
+ const liveProjectionIdentityCache = new WeakMap();
1108
+ let liveProjectionIdentitySequence = 0;
@@ -1,12 +1,12 @@
1
1
  import { historyReconciliationDigest, historyReconciliationEntrySignature, } from "../agent-runtime/history.js";
2
2
  import { historyTraceOrder } from "./trace-order.js";
3
3
  import { attachAsyncAgentRunNode, reconcileAsyncAgentRunStatuses } from "./trace-async-agent-runs.js";
4
- import { sortTraceNodes } from "./trace-nodes.js";
4
+ import { flattenTraceNodes, sortTraceNodes } from "./trace-nodes.js";
5
5
  import { createRunNotificationNode, parseRunNotificationText } from "./trace-run-notifications.js";
6
6
  import { isSubagentToolName } from "./trace-subagent-links.js";
7
7
  import { assistantMessageNodeId, messageTurnNodeId, thinkingNodeId } from "./trace-event-projection.js";
8
8
  import { TRACE_RECONCILIATION_ENTRY_CAP, TRACE_RECONCILIATION_TIMING_CAP } from "./trace-limits.js";
9
- import { qualifiedHistoryToolNodeId } from "./trace-tool-identity.js";
9
+ import { qualifiedToolNodeId } from "./trace-tool-identity.js";
10
10
  const MAX_CONFIDENT_TIMESTAMP_DISTANCE_MS = 5 * 60 * 1_000;
11
11
  const PERSISTED_TIMESTAMP_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|([+-])(\d{2}):(\d{2}))$/;
12
12
  export function projectHistoryEntries(entries, sessionStatus, openHistoryEventIds, turnTimings = [], reconciliationProof, reconciliationAuthoritative = false) {
@@ -73,15 +73,44 @@ export function traceNodesFromHistoryEntries(piboSessionId, entries, turnTimings
73
73
  output: { name: entry.name },
74
74
  source,
75
75
  stableKey: historyEntryStableKey(entry),
76
- orderKey: historyTraceOrder(entryIndex, 0, "execution.command", source),
76
+ orderKey: historyTraceOrder(entryIndex, 0, "execution.command", source, entry.createdAt),
77
77
  children: [],
78
78
  });
79
79
  }
80
80
  }
81
+ applyMonotoneHistoryChronology(nodes, entries);
81
82
  reconcileAsyncAgentRunStatuses(nodes);
82
83
  sortTraceNodes(nodes);
83
84
  return nodes;
84
85
  }
86
+ function applyMonotoneHistoryChronology(nodes, entries) {
87
+ const nextValid = new Array(entries.length);
88
+ let next;
89
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
90
+ const parsed = parsedTimestamp(entries[index]?.createdAt);
91
+ if (parsed !== undefined)
92
+ next = parsed;
93
+ nextValid[index] = next;
94
+ }
95
+ const chronologyByIndex = new Map();
96
+ let previous;
97
+ for (let index = 0; index < entries.length; index += 1) {
98
+ const entry = entries[index];
99
+ const parsed = parsedTimestamp(entry.createdAt);
100
+ const candidate = parsed ?? nextValid[index] ?? previous;
101
+ if (candidate === undefined)
102
+ continue;
103
+ const monotone = previous === undefined ? candidate : Math.max(previous, candidate);
104
+ chronologyByIndex.set(historyIndex(entry, index), monotone);
105
+ previous = monotone;
106
+ }
107
+ for (const node of flattenTraceNodes(nodes)) {
108
+ if (!node.orderKey)
109
+ continue;
110
+ const chronologyMs = chronologyByIndex.get(node.orderKey.transcriptIndex ?? node.orderKey.turnSeq);
111
+ node.orderKey = { ...node.orderKey, chronologyMs };
112
+ }
113
+ }
85
114
  function collectHistoryTurnTimingTargets(entries) {
86
115
  const targets = [];
87
116
  let latestUserText;
@@ -630,6 +659,8 @@ function findConfidentTimingMatch(target, turnTimings) {
630
659
  }
631
660
  function collectAssistantTurn(entries, startIndex) {
632
661
  const turnEntries = [];
662
+ const first = entries[startIndex];
663
+ const explicitTurnId = first?.type === "message" ? first.turnId : undefined;
633
664
  let index = startIndex;
634
665
  while (index < entries.length) {
635
666
  const entry = entries[index];
@@ -637,6 +668,8 @@ function collectAssistantTurn(entries, startIndex) {
637
668
  break;
638
669
  if (entry.role !== "assistant" && entry.role !== "tool")
639
670
  break;
671
+ if (index !== startIndex && explicitTurnId && entry.turnId && entry.turnId !== explicitTurnId)
672
+ break;
640
673
  turnEntries.push({ entry, index });
641
674
  index += 1;
642
675
  }
@@ -653,7 +686,7 @@ function createUserMessageNode(piboSessionId, entry, entryIndex, timing, fallbac
653
686
  entryId: entry.nativeEntryId,
654
687
  piboSessionId,
655
688
  startedAt: entry.createdAt,
656
- orderKey: historyTraceOrder(orderIndex, 0, "yielded.run", source),
689
+ orderKey: historyTraceOrder(orderIndex, 0, "yielded.run", source, entry.createdAt),
657
690
  source,
658
691
  stableKey: historyEntryStableKey(entry),
659
692
  notification,
@@ -680,7 +713,7 @@ function createUserMessageNode(piboSessionId, entry, entryIndex, timing, fallbac
680
713
  error: entry.error,
681
714
  source,
682
715
  stableKey: eventIdentity ?? historyEntryStableKey(entry),
683
- orderKey: historyTraceOrder(orderIndex, 0, "user.message", source),
716
+ orderKey: historyTraceOrder(orderIndex, 0, "user.message", source, entry.createdAt),
684
717
  children: [],
685
718
  };
686
719
  }
@@ -783,7 +816,7 @@ function createReasoningNode(input) {
783
816
  output: input.thinking,
784
817
  source,
785
818
  stableKey: eventIdentity ? `reasoning:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:thinking:${input.contentPartIndex}`,
786
- orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "model.reasoning", source),
819
+ orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "model.reasoning", source, input.entry.createdAt),
787
820
  children: [],
788
821
  };
789
822
  }
@@ -798,6 +831,7 @@ function createToolCallNode(piboSessionId, entry, entryIndex, contentPartIndex,
798
831
  eventId,
799
832
  parentId: historyTurnParentId(entry, eventId),
800
833
  toolCallId: part.toolCallId,
834
+ toolInvocationOrdinal,
801
835
  type: isSubagentToolName(part.toolName) ? "agent.delegation" : "tool.call",
802
836
  title: part.toolName,
803
837
  status: "done",
@@ -805,7 +839,7 @@ function createToolCallNode(piboSessionId, entry, entryIndex, contentPartIndex,
805
839
  input: part.input ?? {},
806
840
  source,
807
841
  stableKey: toolNodeId,
808
- orderKey: historyTraceOrder(historyIndex(entry, entryIndex), contentPartIndex, isSubagentToolName(part.toolName) ? "agent.delegation" : "tool.call", source),
842
+ orderKey: historyTraceOrder(historyIndex(entry, entryIndex), contentPartIndex, isSubagentToolName(part.toolName) ? "agent.delegation" : "tool.call", source, entry.createdAt),
809
843
  children: [],
810
844
  };
811
845
  }
@@ -840,13 +874,14 @@ function createMissingToolResultNode(piboSessionId, entry, entryIndex, toolCallI
840
874
  eventId,
841
875
  parentId: historyTurnParentId(entry, eventId),
842
876
  toolCallId,
877
+ toolInvocationOrdinal,
843
878
  type: "tool.result",
844
879
  title: entry.toolName ?? "Tool Result",
845
880
  status: "done",
846
881
  startedAt: entry.createdAt,
847
882
  source,
848
883
  stableKey: toolNodeId,
849
- orderKey: historyTraceOrder(historyIndex(entry, entryIndex), 0, "tool.result", source),
884
+ orderKey: historyTraceOrder(historyIndex(entry, entryIndex), 0, "tool.result", source, entry.createdAt),
850
885
  children: [],
851
886
  };
852
887
  }
@@ -870,7 +905,7 @@ function createAssistantMessageNode(input) {
870
905
  error: input.error,
871
906
  source,
872
907
  stableKey: eventIdentity ? `assistant:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:response:${input.contentPartIndex}`,
873
- orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "assistant.message", source),
908
+ orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "assistant.message", source, input.startedAt ?? input.entry.createdAt),
874
909
  children: input.children ?? [],
875
910
  };
876
911
  }
@@ -890,6 +925,8 @@ function historyMessageStatus(entry) {
890
925
  return "done";
891
926
  }
892
927
  function canonicalProductTurnId(entry, timing, fallbackEventId) {
928
+ if (entry.source === "product" && entry.turnId)
929
+ return entry.turnId;
893
930
  return fallbackEventId ?? timing?.eventId ?? entry.turnId;
894
931
  }
895
932
  function nativeHistoryTurnId(entry) {
@@ -904,9 +941,7 @@ function historyEntryNodeId(entry) {
904
941
  return entry.source === "native" && entry.nativeEntryId ? `entry:${entry.nativeEntryId}` : `history:${entry.id}`;
905
942
  }
906
943
  function historyToolNodeId(entry, toolCallId, eventId, toolInvocationOrdinal, qualifyProjectionIdentity) {
907
- if (entry.source !== "native" || !qualifyProjectionIdentity)
908
- return `tool:${toolCallId}`;
909
- return qualifiedHistoryToolNodeId(toolCallId, eventId ?? `native-history-tool:${entry.historyPosition ?? entry.id}`, toolInvocationOrdinal);
944
+ return qualifiedToolNodeId(toolCallId, eventId ?? `native-history-tool:${entry.historyPosition ?? entry.id}`, toolInvocationOrdinal);
910
945
  }
911
946
  function nativeToolCallIdsRequiringQualification(entries, reconciliationProof) {
912
947
  const localNativeToolCallIds = nativeToolCallIds(entries);