@pasko70/pibo 3.0.1 → 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 +53 -13
  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.1.vsix +0 -0
@@ -1,62 +1,90 @@
1
1
  import { assistantOutputKey, isLiveOnlyOutputEvent, thinkingOutputKey, toolOutputKey } from "./output-event-policy.js";
2
+ const MAX_TRACKED_COMPACTOR_SESSIONS = 1_024;
2
3
  export class OutputCompactor {
3
4
  assistantBuffers = new Map();
4
5
  thinkingBuffers = new Map();
5
6
  toolSnapshots = new Map();
7
+ sessionRecency = new Map();
8
+ sessionBufferCounts = new Map();
6
9
  compact(event) {
10
+ const prepared = this.prepare(event);
11
+ prepared.ack();
12
+ return prepared;
13
+ }
14
+ prepare(event) {
7
15
  const persistedEvents = [];
8
16
  const snapshots = [];
17
+ let liveEvents = [event];
18
+ const mutations = [];
9
19
  switch (event.type) {
10
20
  case "assistant_delta": {
11
21
  const key = assistantOutputKey(event);
12
22
  const previous = this.assistantBuffers.get(key);
13
- const text = `${previous?.text ?? ""}${event.text}`;
14
- this.assistantBuffers.set(key, { event, text });
15
- snapshots.push({ ...event, text });
23
+ const next = { event, text: `${previous?.text ?? ""}${event.text}` };
24
+ mutations.push(() => this.setBuffer(this.assistantBuffers, key, next, event.piboSessionId));
25
+ snapshots.push({ ...event, text: next.text });
16
26
  break;
17
27
  }
18
28
  case "assistant_message": {
19
29
  const key = assistantOutputKey(event);
20
30
  const buffered = this.assistantBuffers.get(key);
21
- this.assistantBuffers.delete(key);
22
- persistedEvents.push({ ...event, text: event.text || buffered?.text || "" });
31
+ const canonical = { ...event, text: event.text || buffered?.text || "" };
32
+ mutations.push(() => {
33
+ this.deleteBuffer(this.assistantBuffers, key, buffered, event.piboSessionId);
34
+ });
35
+ persistedEvents.push(canonical);
36
+ liveEvents = [canonical];
23
37
  break;
24
38
  }
25
39
  case "thinking_started": {
26
40
  const key = thinkingOutputKey(event);
27
- this.thinkingBuffers.set(key, { base: event, text: "" });
41
+ const next = { base: event, text: "" };
42
+ mutations.push(() => {
43
+ if (!this.thinkingBuffers.has(key))
44
+ this.setBuffer(this.thinkingBuffers, key, next, event.piboSessionId);
45
+ });
28
46
  persistedEvents.push(event);
29
47
  break;
30
48
  }
31
49
  case "thinking_delta": {
32
50
  const key = thinkingOutputKey(event);
33
51
  const previous = this.thinkingBuffers.get(key);
34
- const text = `${previous?.text ?? ""}${event.text}`;
35
- this.thinkingBuffers.set(key, { base: previous?.base ?? event, text });
36
- snapshots.push({ ...event, text });
52
+ const next = { base: previous?.base ?? event, text: `${previous?.text ?? ""}${event.text}` };
53
+ mutations.push(() => this.setBuffer(this.thinkingBuffers, key, next, event.piboSessionId));
54
+ snapshots.push({ ...event, text: next.text });
37
55
  break;
38
56
  }
39
57
  case "thinking_finished": {
40
58
  const key = thinkingOutputKey(event);
41
59
  const buffered = this.thinkingBuffers.get(key);
42
- this.thinkingBuffers.delete(key);
43
- persistedEvents.push({ ...event, text: event.text || buffered?.text || "" });
60
+ const canonical = { ...event, text: event.text || buffered?.text || "" };
61
+ mutations.push(() => {
62
+ this.deleteBuffer(this.thinkingBuffers, key, buffered, event.piboSessionId);
63
+ });
64
+ persistedEvents.push(canonical);
65
+ liveEvents = [canonical];
44
66
  break;
45
67
  }
46
68
  case "tool_execution_updated": {
47
- this.toolSnapshots.set(toolOutputKey(event), event);
69
+ const key = toolOutputKey(event);
70
+ mutations.push(() => this.setBuffer(this.toolSnapshots, key, event, event.piboSessionId));
48
71
  snapshots.push(event);
49
72
  break;
50
73
  }
51
74
  case "tool_execution_finished": {
52
- this.toolSnapshots.delete(toolOutputKey(event));
75
+ const key = toolOutputKey(event);
76
+ const previous = this.toolSnapshots.get(key);
77
+ mutations.push(() => {
78
+ this.deleteBuffer(this.toolSnapshots, key, previous, event.piboSessionId);
79
+ });
53
80
  persistedEvents.push(event);
54
81
  break;
55
82
  }
56
83
  case "message_finished":
57
84
  case "session_error": {
58
- persistedEvents.push(...this.flushForBoundary(event));
59
- persistedEvents.push(event);
85
+ const flushed = this.prepareBoundaryFlush(event, mutations);
86
+ persistedEvents.push(...flushed, event);
87
+ liveEvents = [...flushed, event];
60
88
  break;
61
89
  }
62
90
  default:
@@ -64,7 +92,23 @@ export class OutputCompactor {
64
92
  persistedEvents.push(event);
65
93
  break;
66
94
  }
67
- return { liveEvents: [event], persistedEvents, snapshots };
95
+ let settled = false;
96
+ return {
97
+ liveEvents,
98
+ persistedEvents,
99
+ snapshots,
100
+ ack: () => {
101
+ if (settled)
102
+ return;
103
+ settled = true;
104
+ for (const mutate of mutations)
105
+ mutate();
106
+ this.touchSession(event.piboSessionId);
107
+ },
108
+ rollback: () => {
109
+ settled = true;
110
+ },
111
+ };
68
112
  }
69
113
  snapshotsForSession(piboSessionId) {
70
114
  const snapshots = [];
@@ -86,29 +130,68 @@ export class OutputCompactor {
86
130
  }
87
131
  return snapshots;
88
132
  }
89
- flushForBoundary(event) {
133
+ disposeSession(piboSessionId) {
134
+ for (const [key, buffer] of this.assistantBuffers) {
135
+ if (buffer.event.piboSessionId === piboSessionId)
136
+ this.deleteBuffer(this.assistantBuffers, key, buffer, piboSessionId);
137
+ }
138
+ for (const [key, buffer] of this.thinkingBuffers) {
139
+ if (buffer.base.piboSessionId === piboSessionId)
140
+ this.deleteBuffer(this.thinkingBuffers, key, buffer, piboSessionId);
141
+ }
142
+ for (const [key, event] of this.toolSnapshots) {
143
+ if (event.piboSessionId === piboSessionId)
144
+ this.deleteBuffer(this.toolSnapshots, key, event, piboSessionId);
145
+ }
146
+ this.sessionBufferCounts.delete(piboSessionId);
147
+ this.sessionRecency.delete(piboSessionId);
148
+ }
149
+ disposeAll() {
150
+ this.assistantBuffers.clear();
151
+ this.thinkingBuffers.clear();
152
+ this.toolSnapshots.clear();
153
+ this.sessionRecency.clear();
154
+ this.sessionBufferCounts.clear();
155
+ }
156
+ debugState() {
157
+ return {
158
+ sessionCount: this.sessionRecency.size,
159
+ completedSessionCount: [...this.sessionRecency.keys()].filter((sessionId) => !this.sessionHasBuffers(sessionId)).length,
160
+ sessions: [...this.sessionRecency.keys()],
161
+ assistantBufferCount: this.assistantBuffers.size,
162
+ thinkingBufferCount: this.thinkingBuffers.size,
163
+ toolSnapshotCount: this.toolSnapshots.size,
164
+ };
165
+ }
166
+ prepareBoundaryFlush(event, mutations) {
90
167
  const flushed = [];
91
- for (const [key, buffer] of Array.from(this.assistantBuffers.entries())) {
168
+ for (const [key, buffer] of this.assistantBuffers) {
92
169
  if (!matchesBoundary(buffer.event, event))
93
170
  continue;
94
- this.assistantBuffers.delete(key);
171
+ mutations.push(() => {
172
+ this.deleteBuffer(this.assistantBuffers, key, buffer, buffer.event.piboSessionId);
173
+ });
95
174
  flushed.push({
96
175
  type: "assistant_message",
97
176
  piboSessionId: buffer.event.piboSessionId,
98
177
  eventId: buffer.event.eventId,
178
+ renderSequence: buffer.event.renderSequence,
99
179
  assistantIndex: buffer.event.assistantIndex,
100
180
  contentIndex: buffer.event.contentIndex,
101
181
  text: buffer.text,
102
182
  });
103
183
  }
104
- for (const [key, buffer] of Array.from(this.thinkingBuffers.entries())) {
184
+ for (const [key, buffer] of this.thinkingBuffers) {
105
185
  if (!matchesBoundary(buffer.base, event))
106
186
  continue;
107
- this.thinkingBuffers.delete(key);
187
+ mutations.push(() => {
188
+ this.deleteBuffer(this.thinkingBuffers, key, buffer, buffer.base.piboSessionId);
189
+ });
108
190
  flushed.push({
109
191
  type: "thinking_finished",
110
192
  piboSessionId: buffer.base.piboSessionId,
111
193
  eventId: buffer.base.eventId,
194
+ renderSequence: buffer.base.renderSequence,
112
195
  thinkingIndex: "thinkingIndex" in buffer.base ? buffer.base.thinkingIndex : undefined,
113
196
  contentIndex: buffer.base.contentIndex,
114
197
  text: buffer.text,
@@ -116,6 +199,46 @@ export class OutputCompactor {
116
199
  }
117
200
  return flushed;
118
201
  }
202
+ touchSession(piboSessionId) {
203
+ this.sessionRecency.delete(piboSessionId);
204
+ this.sessionRecency.set(piboSessionId, true);
205
+ let completedCount = [...this.sessionRecency.keys()].filter((sessionId) => !this.sessionHasBuffers(sessionId)).length;
206
+ while (completedCount > MAX_TRACKED_COMPACTOR_SESSIONS) {
207
+ let removed = false;
208
+ for (const sessionId of this.sessionRecency.keys()) {
209
+ if (this.sessionHasBuffers(sessionId))
210
+ continue;
211
+ this.sessionRecency.delete(sessionId);
212
+ completedCount -= 1;
213
+ removed = true;
214
+ break;
215
+ }
216
+ if (!removed)
217
+ break;
218
+ }
219
+ }
220
+ sessionHasBuffers(piboSessionId) {
221
+ return (this.sessionBufferCounts.get(piboSessionId) ?? 0) > 0;
222
+ }
223
+ setBuffer(map, key, value, piboSessionId) {
224
+ if (!map.has(key))
225
+ this.sessionBufferCounts.set(piboSessionId, (this.sessionBufferCounts.get(piboSessionId) ?? 0) + 1);
226
+ setLatest(map, key, value);
227
+ }
228
+ deleteBuffer(map, key, expected, piboSessionId) {
229
+ if (expected === undefined || map.get(key) !== expected)
230
+ return;
231
+ map.delete(key);
232
+ const next = (this.sessionBufferCounts.get(piboSessionId) ?? 1) - 1;
233
+ if (next > 0)
234
+ this.sessionBufferCounts.set(piboSessionId, next);
235
+ else
236
+ this.sessionBufferCounts.delete(piboSessionId);
237
+ }
238
+ }
239
+ function setLatest(map, key, value) {
240
+ map.delete(key);
241
+ map.set(key, value);
119
242
  }
120
243
  function matchesBoundary(bufferEvent, boundary) {
121
244
  if (bufferEvent.piboSessionId !== boundary.piboSessionId)
@@ -2,7 +2,184 @@ export function isLiveOnlyOutputEvent(event) {
2
2
  return event.type === "assistant_delta" || event.type === "thinking_delta" || event.type === "tool_execution_updated";
3
3
  }
4
4
  export function isPersistableOutputEvent(event) {
5
- return !isLiveOnlyOutputEvent(event);
5
+ return isPiboOutputEvent(event) && !isLiveOnlyOutputEvent(event);
6
+ }
7
+ export function isPiboOutputEvent(value) {
8
+ if (!isRecord(value) || typeof value.type !== "string" || typeof value.piboSessionId !== "string")
9
+ return false;
10
+ if (value.eventId !== undefined && typeof value.eventId !== "string")
11
+ return false;
12
+ if (value.renderSequence !== undefined && !isPositiveSafeInteger(value.renderSequence))
13
+ return false;
14
+ if (value.toolInvocationOrdinal !== undefined && !isNonNegativeSafeInteger(value.toolInvocationOrdinal))
15
+ return false;
16
+ if (value.provenance !== undefined && !isMessageProvenance(value.provenance))
17
+ return false;
18
+ switch (value.type) {
19
+ case "message_queued":
20
+ return typeof value.text === "string"
21
+ && isNonNegativeSafeInteger(value.queuedMessages)
22
+ && isOptionalEventSource(value.source);
23
+ case "message_steered":
24
+ return typeof value.text === "string"
25
+ && isOptionalEventSource(value.source)
26
+ && isOptionalString(value.activeEventId);
27
+ case "message_started":
28
+ return typeof value.text === "string" && isOptionalEventSource(value.source);
29
+ case "assistant_delta":
30
+ case "assistant_message":
31
+ return typeof value.text === "string"
32
+ && isOptionalNonNegativeSafeInteger(value.assistantIndex)
33
+ && isOptionalNonNegativeSafeInteger(value.contentIndex);
34
+ case "thinking_delta":
35
+ return typeof value.text === "string"
36
+ && isOptionalNonNegativeSafeInteger(value.thinkingIndex)
37
+ && isOptionalNonNegativeSafeInteger(value.contentIndex);
38
+ case "message_finished":
39
+ return isOptionalEventSource(value.source);
40
+ case "thinking_started":
41
+ return isOptionalNonNegativeSafeInteger(value.thinkingIndex) && isOptionalNonNegativeSafeInteger(value.contentIndex);
42
+ case "thinking_finished":
43
+ return isOptionalString(value.text)
44
+ && isOptionalNonNegativeSafeInteger(value.thinkingIndex)
45
+ && isOptionalNonNegativeSafeInteger(value.contentIndex);
46
+ case "tool_call":
47
+ return hasOwn(value, "args")
48
+ && typeof value.toolCallId === "string"
49
+ && typeof value.toolName === "string"
50
+ && typeof value.argsComplete === "boolean"
51
+ && isOptionalString(value.intent);
52
+ case "tool_execution_started":
53
+ return hasOwn(value, "args")
54
+ && typeof value.toolCallId === "string"
55
+ && typeof value.toolName === "string"
56
+ && isOptionalString(value.intent);
57
+ case "tool_execution_updated":
58
+ return hasOwn(value, "args")
59
+ && hasOwn(value, "partialResult")
60
+ && typeof value.toolCallId === "string"
61
+ && typeof value.toolName === "string"
62
+ && isOptionalString(value.intent);
63
+ case "tool_execution_finished":
64
+ return hasOwn(value, "result")
65
+ && typeof value.toolCallId === "string"
66
+ && typeof value.toolName === "string"
67
+ && typeof value.isError === "boolean"
68
+ && isOptionalString(value.intent);
69
+ case "subagent_session":
70
+ return typeof value.toolName === "string"
71
+ && typeof value.subagentName === "string"
72
+ && typeof value.childPiboSessionId === "string"
73
+ && isOptionalString(value.requestId)
74
+ && isOptionalString(value.toolCallId)
75
+ && isOptionalString(value.threadKey);
76
+ case "assistant_usage":
77
+ return isFiniteNumber(value.totalTokens)
78
+ && isOptionalNonNegativeSafeInteger(value.usageIndex)
79
+ && ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens", "reasoningTokens"].every((key) => isOptionalFiniteNumber(value[key]))
80
+ && isOptionalFiniteNumber(value.costUsd);
81
+ case "compaction_start":
82
+ return typeof value.reason === "string" && isOptionalNonNegativeSafeInteger(value.compactionIndex);
83
+ case "compaction_end":
84
+ return typeof value.reason === "string"
85
+ && typeof value.aborted === "boolean"
86
+ && isOptionalNonNegativeSafeInteger(value.compactionIndex)
87
+ && isOptionalString(value.errorMessage);
88
+ case "approval_requested":
89
+ return isApprovalRequest(value.request);
90
+ case "approval_resolved":
91
+ case "user_input_resolved":
92
+ return typeof value.requestId === "string" && isRuntimeRequestResolution(value.resolution);
93
+ case "user_input_requested":
94
+ return isUserInputRequest(value.request);
95
+ case "execution_result":
96
+ return typeof value.action === "string" && hasOwn(value, "result");
97
+ case "session_error":
98
+ return typeof value.error === "string" && (value.errorDetails === undefined || isRecord(value.errorDetails));
99
+ case "pi_event":
100
+ return hasOwn(value, "event");
101
+ default:
102
+ return false;
103
+ }
104
+ }
105
+ function isRecord(value) {
106
+ return !!value && typeof value === "object" && !Array.isArray(value);
107
+ }
108
+ function hasOwn(value, key) {
109
+ return Object.prototype.hasOwnProperty.call(value, key);
110
+ }
111
+ function isFiniteNumber(value) {
112
+ return typeof value === "number" && Number.isFinite(value);
113
+ }
114
+ function isOptionalFiniteNumber(value) {
115
+ return value === undefined || isFiniteNumber(value);
116
+ }
117
+ function isOptionalString(value) {
118
+ return value === undefined || typeof value === "string";
119
+ }
120
+ function isOptionalNonNegativeSafeInteger(value) {
121
+ return value === undefined || isNonNegativeSafeInteger(value);
122
+ }
123
+ function isPositiveSafeInteger(value) {
124
+ return Number.isSafeInteger(value) && value > 0;
125
+ }
126
+ function isNonNegativeSafeInteger(value) {
127
+ return Number.isSafeInteger(value) && value >= 0;
128
+ }
129
+ function isRuntimeRequestResolution(value) {
130
+ return value === "responded" || value === "cleared" || value === "aborted" || value === "expired";
131
+ }
132
+ function isOptionalEventSource(value) {
133
+ return value === undefined || value === "user" || value === "ui" || value === "service" || value === "actor";
134
+ }
135
+ function isMessageProvenance(value) {
136
+ if (!isRecord(value) || typeof value.kind !== "string")
137
+ return false;
138
+ if (value.kind === "loop-run") {
139
+ return typeof value.jobId === "string"
140
+ && typeof value.runId === "string"
141
+ && (value.cause === undefined || value.cause === "run-reminder")
142
+ && isOptionalString(value.rootEventId);
143
+ }
144
+ if (value.kind === "subagent-request") {
145
+ return typeof value.requestId === "string"
146
+ && typeof value.controllerPiboSessionId === "string"
147
+ && isOptionalString(value.loopJobId)
148
+ && isOptionalString(value.loopRunId);
149
+ }
150
+ return false;
151
+ }
152
+ function isApprovalRequest(value) {
153
+ return isRecord(value)
154
+ && typeof value.requestId === "string"
155
+ && typeof value.requestType === "string"
156
+ && isOptionalString(value.title)
157
+ && isOptionalString(value.detail)
158
+ && (value.decisions === undefined || (Array.isArray(value.decisions)
159
+ && value.decisions.every((decision) => isRecord(decision)
160
+ && typeof decision.id === "string"
161
+ && typeof decision.label === "string"
162
+ && isOptionalString(decision.description))));
163
+ }
164
+ function isUserInputRequest(value) {
165
+ return isRecord(value)
166
+ && typeof value.requestId === "string"
167
+ && Array.isArray(value.questions)
168
+ && isOptionalBoolean(value.blocking)
169
+ && value.questions.every((question) => isRecord(question)
170
+ && typeof question.id === "string"
171
+ && typeof question.question === "string"
172
+ && isOptionalString(question.header)
173
+ && isOptionalBoolean(question.multiSelect)
174
+ && isOptionalBoolean(question.allowFreeform)
175
+ && isOptionalBoolean(question.secret)
176
+ && (question.options === undefined || (Array.isArray(question.options)
177
+ && question.options.every((option) => isRecord(option)
178
+ && typeof option.label === "string"
179
+ && isOptionalString(option.description)))));
180
+ }
181
+ function isOptionalBoolean(value) {
182
+ return value === undefined || typeof value === "boolean";
6
183
  }
7
184
  export function assistantOutputKey(event) {
8
185
  const partIndex = event.assistantIndex ?? event.contentIndex ?? 0;
@@ -13,5 +190,5 @@ export function thinkingOutputKey(event) {
13
190
  return [event.piboSessionId, event.eventId ?? "", partIndex].join(":");
14
191
  }
15
192
  export function toolOutputKey(event) {
16
- return [event.piboSessionId, event.eventId ?? "", event.toolCallId].join(":");
193
+ return [event.piboSessionId, event.eventId ?? "", event.toolCallId, event.toolInvocationOrdinal ?? 0].join(":");
17
194
  }
@@ -58,7 +58,7 @@ export function chatStreamFramesFromOutputEvent(event, state, options = {}) {
58
58
  }
59
59
  break;
60
60
  case "tool_call":
61
- ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId);
61
+ ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId, event.toolInvocationOrdinal);
62
62
  frames.push({
63
63
  type: "TOOL_CALL_ARGS",
64
64
  toolCallId: event.toolCallId,
@@ -71,7 +71,7 @@ export function chatStreamFramesFromOutputEvent(event, state, options = {}) {
71
71
  });
72
72
  break;
73
73
  case "tool_execution_started": {
74
- const started = ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId);
74
+ const started = ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId, event.toolInvocationOrdinal);
75
75
  if (!started && event.intent) {
76
76
  frames.push({
77
77
  type: "TOOL_CALL_START",
@@ -85,7 +85,7 @@ export function chatStreamFramesFromOutputEvent(event, state, options = {}) {
85
85
  break;
86
86
  }
87
87
  case "tool_execution_updated":
88
- ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId);
88
+ ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, event.args, event.intent, eventId, event.toolInvocationOrdinal);
89
89
  frames.push({
90
90
  type: "TOOL_CALL_ARGS",
91
91
  toolCallId: event.toolCallId,
@@ -99,7 +99,7 @@ export function chatStreamFramesFromOutputEvent(event, state, options = {}) {
99
99
  });
100
100
  break;
101
101
  case "tool_execution_finished":
102
- ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, undefined, event.intent, eventId);
102
+ ensureToolCallStarted(frames, state, event.toolCallId, event.toolName, undefined, event.intent, eventId, event.toolInvocationOrdinal);
103
103
  frames.push({ type: "TOOL_CALL_RESULT", toolCallId: event.toolCallId, toolName: event.toolName, result: event.result, isError: event.isError, ...(event.intent ? { intent: event.intent } : {}), runId: eventId });
104
104
  break;
105
105
  case "subagent_session":
@@ -135,6 +135,14 @@ export function chatStreamFramesFromOutputEvent(event, state, options = {}) {
135
135
  }
136
136
  if (options.includeRawEvent ?? true)
137
137
  frames.push({ type: "RAW_EVENT", event });
138
+ if (event.renderSequence !== undefined) {
139
+ for (const frame of frames)
140
+ frame.renderSequence = event.renderSequence;
141
+ }
142
+ if (event.toolInvocationOrdinal !== undefined) {
143
+ for (const frame of frames)
144
+ frame.toolInvocationOrdinal = event.toolInvocationOrdinal;
145
+ }
138
146
  return frames;
139
147
  }
140
148
  function reasoningIdFromOutputEvent(event) {
@@ -162,10 +170,11 @@ function ensureReasoningStarted(frames, state, messageId, runId) {
162
170
  state.reasoningMessageIds.add(messageId);
163
171
  frames.push({ type: "REASONING_MESSAGE_START", messageId, runId });
164
172
  }
165
- function ensureToolCallStarted(frames, state, toolCallId, toolName, args, intent, runId) {
166
- if (state.toolCallIds.has(toolCallId))
173
+ function ensureToolCallStarted(frames, state, toolCallId, toolName, args, intent, runId, toolInvocationOrdinal) {
174
+ const invocationKey = JSON.stringify([runId ?? "", toolCallId, toolInvocationOrdinal ?? 0]);
175
+ if (state.toolCallIds.has(invocationKey))
167
176
  return false;
168
- state.toolCallIds.add(toolCallId);
177
+ state.toolCallIds.add(invocationKey);
169
178
  frames.push({ type: "TOOL_CALL_START", toolCallId, toolName, args, ...(intent ? { intent } : {}), runId });
170
179
  return true;
171
180
  }
@@ -255,6 +255,7 @@ function compactTraceNode(node, payloadStore, piboSessionId, depth) {
255
255
  payloadRefs: Object.keys(payloadRefs).length ? payloadRefs : undefined,
256
256
  linkedPiboSessionId: node.linkedPiboSessionId,
257
257
  toolCallId: node.toolCallId,
258
+ toolInvocationOrdinal: node.toolInvocationOrdinal,
258
259
  runId: node.runId,
259
260
  intent: node.intent,
260
261
  eventId: node.eventId,