@pasko70/pibo 1.11.4 → 1.12.1
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/dist/apps/chat/chat-trace-helpers.js +59 -2
- package/dist/apps/chat/data/timeline-query-service.js +1 -1
- package/dist/apps/chat/web-app.js +16 -8
- package/dist/apps/chat-ui/assets/{dist-BXjLdcn6.js → dist-3bD-92Wo.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-BQK_wUMR.js → chat-ui/assets/dist-BFjy5Y59.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-Dtv5S2qd.js → chat-ui/assets/dist-BKwQlbhS.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-DT9QkgTN.js → chat-ui/assets/dist-Cp68y_h5.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D9B41N2f.js → dist-DE0y_EI7.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-TS4EA6YQ.js → dist-DPb0yDi4.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-B07Whoa6.js → chat-ui/assets/dist-D_dQ5GaA.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-yjwDcRbw.js → dist-Daq2KRpq.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CjXSEbsr.js → dist-aZnuCezL.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CSBDio7C.js → dist-q6Vpm5yH.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BJoo_GJP.css +1 -0
- package/dist/apps/chat-ui/assets/index-DXPKuGfs.js +237 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-B3dSrp-L.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/{chat-ui/assets/dist-BeKdUsyg.js → context-files-ui/assets/dist-BYcsFgKp.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-TG9QLHKj.js → dist-Bdudyq9I.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-BFlEjppv.js → dist-BvIW3V94.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-De6eI3Cr.js → dist-CSLUWBzz.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-CNv02m3N.js → context-files-ui/assets/dist-CT-91DFe.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-BtQ20SMU.js → context-files-ui/assets/dist-DcsDivye.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-BACxmdQ2.js → dist-DeozI4lc.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-D68eIqmX.js → dist-f2E1-leP.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-CigbyRK5.js → dist-sus90AFR.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-TAhl1jC0.js → context-files-ui/assets/dist-uFphVGi8.js} +1 -1
- package/dist/apps/context-files-ui/assets/index-CQ25ablG.js +202 -0
- package/dist/apps/context-files-ui/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.12.1.vsix +0 -0
- package/dist/core/gateway-resource-guard.js +46 -15
- package/dist/core/routed-session.js +40 -1
- package/dist/core/runtime.js +2 -0
- package/dist/core/session-errors.js +3 -0
- package/dist/core/session-router.js +25 -2
- package/dist/core/transcript-integrity.js +431 -0
- package/dist/data/pibo-store.js +2 -0
- package/dist/data/telemetry.js +148 -0
- package/dist/debug/index.js +11 -0
- package/dist/debug/web.js +1 -1
- package/dist/gateway/server.js +1 -0
- package/dist/reliability/store.js +11 -6
- package/dist/runs/registry.js +38 -1
- package/dist/runs/resource-isolation.js +437 -0
- package/dist/runs/tools.js +6 -3
- package/dist/sessions/pibo-data-store.js +114 -0
- package/dist/shared/trace-engine.js +3 -3
- package/dist/shared/trace-event-projection.js +165 -36
- package/dist/shared/trace-nodes.js +6 -4
- package/dist/shared/trace-page-merge.js +106 -2
- package/dist/shared/trace-transcript.js +194 -36
- package/dist/tools/cdp-client.js +87 -3
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-BoRl0OB_.js +0 -473
- package/dist/apps/chat-ui/assets/index-aTOOomMF.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-DGcSd2ZH.js +0 -275
- package/dist/apps/context-files-ui/assets/index-B5j33Q2B.js +0 -438
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.11.4.vsix +0 -0
|
@@ -3,29 +3,37 @@ import { attachAsyncAgentRunNode, reconcileAsyncAgentRunStatuses } from "./trace
|
|
|
3
3
|
import { sortTraceNodes } from "./trace-nodes.js";
|
|
4
4
|
import { createRunNotificationNode, parseRunNotificationText } from "./trace-run-notifications.js";
|
|
5
5
|
import { isSubagentToolName } from "./trace-subagent-links.js";
|
|
6
|
-
|
|
6
|
+
import { assistantMessageNodeId, thinkingNodeId } from "./trace-event-projection.js";
|
|
7
|
+
export function projectTranscriptEntries(entries, sessionStatus, openTranscriptEventIds, turnTimings = []) {
|
|
7
8
|
if (sessionStatus !== "running" || openTranscriptEventIds.size === 0)
|
|
8
9
|
return entries;
|
|
10
|
+
const userTimingAssignments = assignTranscriptUserTimings(entries, turnTimings);
|
|
9
11
|
let lastUserMessageIndex = -1;
|
|
10
|
-
|
|
12
|
+
let lastUserEventId;
|
|
13
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
11
14
|
const entry = entries[index];
|
|
12
|
-
if (entry.type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (entry.type !== "message" || messageRole(entry) !== "user")
|
|
16
|
+
continue;
|
|
17
|
+
lastUserMessageIndex = index;
|
|
18
|
+
lastUserEventId = openTranscriptEventIds.has(entry.id)
|
|
19
|
+
? entry.id
|
|
20
|
+
: userTimingAssignments.get(index)?.eventId;
|
|
16
21
|
}
|
|
17
|
-
return lastUserMessageIndex
|
|
22
|
+
return lastUserMessageIndex !== -1 && lastUserEventId && openTranscriptEventIds.has(lastUserEventId)
|
|
23
|
+
? entries.slice(0, lastUserMessageIndex)
|
|
24
|
+
: entries;
|
|
18
25
|
}
|
|
19
26
|
export function traceNodesFromEntries(piboSessionId, entries, turnTimings = []) {
|
|
20
27
|
const nodes = [];
|
|
21
28
|
const turnTimingAssignments = assignTranscriptTurnTimings(entries, turnTimings);
|
|
29
|
+
const userTimingAssignments = assignTranscriptUserTimings(entries, turnTimings);
|
|
22
30
|
let assistantTurnIndex = 0;
|
|
23
31
|
for (let index = 0; index < entries.length; index += 1) {
|
|
24
32
|
const entry = entries[index];
|
|
25
33
|
if (entry.type === "message") {
|
|
26
34
|
const role = messageRole(entry);
|
|
27
35
|
if (role === "user") {
|
|
28
|
-
nodes.push(createUserMessageNode(piboSessionId, entry, messageContent(entry), index));
|
|
36
|
+
nodes.push(createUserMessageNode(piboSessionId, entry, messageContent(entry), index, userTimingAssignments.get(index)));
|
|
29
37
|
}
|
|
30
38
|
else if (role === "assistant" || role === "toolResult") {
|
|
31
39
|
const turn = collectAssistantTurn(entries, index);
|
|
@@ -70,9 +78,10 @@ function messageParts(entry) {
|
|
|
70
78
|
return [{ type: "text", text: content }];
|
|
71
79
|
return Array.isArray(content) ? content : [];
|
|
72
80
|
}
|
|
73
|
-
function
|
|
74
|
-
const
|
|
81
|
+
function collectTranscriptTurnTimingTargets(entries) {
|
|
82
|
+
const targets = [];
|
|
75
83
|
let latestUserText;
|
|
84
|
+
let latestUserEntryIndex;
|
|
76
85
|
for (let index = 0; index < entries.length; index += 1) {
|
|
77
86
|
const entry = entries[index];
|
|
78
87
|
if (entry.type !== "message")
|
|
@@ -80,6 +89,7 @@ function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
|
80
89
|
const role = messageRole(entry);
|
|
81
90
|
if (role === "user") {
|
|
82
91
|
latestUserText = normalizedPrompt(extractText(messageContent(entry)));
|
|
92
|
+
latestUserEntryIndex = index;
|
|
83
93
|
continue;
|
|
84
94
|
}
|
|
85
95
|
if (role !== "assistant" && role !== "toolResult")
|
|
@@ -87,12 +97,22 @@ function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
|
87
97
|
const turn = collectAssistantTurn(entries, index);
|
|
88
98
|
const lastAssistant = [...turn.entries].reverse().find(({ entry: turnEntry }) => messageRole(turnEntry) === "assistant");
|
|
89
99
|
if (lastAssistant) {
|
|
90
|
-
|
|
100
|
+
targets.push({
|
|
101
|
+
prompt: latestUserText,
|
|
102
|
+
userEntryIndex: latestUserEntryIndex,
|
|
103
|
+
assistantAt: parsedTimestamp(lastAssistant.entry.timestamp),
|
|
104
|
+
settled: isSettledAssistantEntry(lastAssistant.entry),
|
|
105
|
+
});
|
|
91
106
|
}
|
|
92
107
|
index = turn.nextIndex - 1;
|
|
93
108
|
}
|
|
109
|
+
return targets;
|
|
110
|
+
}
|
|
111
|
+
function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
112
|
+
const messageTurnTimings = turnTimings.filter((timing) => timing.userMessageType !== "message_steered");
|
|
113
|
+
const transcriptTurns = collectTranscriptTurnTimingTargets(entries);
|
|
94
114
|
const assignments = Array(transcriptTurns.length).fill(undefined);
|
|
95
|
-
let timingCursor =
|
|
115
|
+
let timingCursor = messageTurnTimings.length - 1;
|
|
96
116
|
for (let turnIndex = transcriptTurns.length - 1; turnIndex >= 0; turnIndex -= 1) {
|
|
97
117
|
const transcriptTurn = transcriptTurns[turnIndex];
|
|
98
118
|
if (!transcriptTurn?.prompt)
|
|
@@ -100,7 +120,7 @@ function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
|
100
120
|
let matchedIndex;
|
|
101
121
|
let matchedDistance = Number.POSITIVE_INFINITY;
|
|
102
122
|
for (let timingIndex = timingCursor; timingIndex >= 0; timingIndex -= 1) {
|
|
103
|
-
const timing =
|
|
123
|
+
const timing = messageTurnTimings[timingIndex];
|
|
104
124
|
if (normalizedPrompt(timing?.userText) !== transcriptTurn.prompt)
|
|
105
125
|
continue;
|
|
106
126
|
const completedAt = parsedTimestamp(timing?.completedAt);
|
|
@@ -114,11 +134,133 @@ function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
|
114
134
|
}
|
|
115
135
|
if (matchedIndex === undefined)
|
|
116
136
|
continue;
|
|
117
|
-
assignments[turnIndex] =
|
|
137
|
+
assignments[turnIndex] = messageTurnTimings[matchedIndex];
|
|
118
138
|
timingCursor = matchedIndex - 1;
|
|
119
139
|
}
|
|
120
140
|
return assignments;
|
|
121
141
|
}
|
|
142
|
+
function assignTranscriptUserTimings(entries, turnTimings) {
|
|
143
|
+
const users = [];
|
|
144
|
+
const userPositionByEntryIndex = new Map();
|
|
145
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
|
|
146
|
+
const entry = entries[entryIndex];
|
|
147
|
+
if (entry.type !== "message" || messageRole(entry) !== "user")
|
|
148
|
+
continue;
|
|
149
|
+
userPositionByEntryIndex.set(entryIndex, users.length);
|
|
150
|
+
users.push({
|
|
151
|
+
entryIndex,
|
|
152
|
+
entryId: entry.id,
|
|
153
|
+
prompt: normalizedPrompt(extractText(messageContent(entry))),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (!users.length || !turnTimings.length)
|
|
157
|
+
return new Map();
|
|
158
|
+
const timingIndexByEventId = new Map(turnTimings.map((timing, timingIndex) => [timing.eventId, timingIndex]));
|
|
159
|
+
const anchorByUserPosition = new Map();
|
|
160
|
+
const assignedTimingIndexes = new Set();
|
|
161
|
+
for (let userPosition = 0; userPosition < users.length; userPosition += 1) {
|
|
162
|
+
const timingIndex = timingIndexByEventId.get(users[userPosition].entryId);
|
|
163
|
+
if (timingIndex === undefined || assignedTimingIndexes.has(timingIndex))
|
|
164
|
+
continue;
|
|
165
|
+
anchorByUserPosition.set(userPosition, timingIndex);
|
|
166
|
+
assignedTimingIndexes.add(timingIndex);
|
|
167
|
+
}
|
|
168
|
+
const transcriptTurns = collectTranscriptTurnTimingTargets(entries);
|
|
169
|
+
let timingUpperBound = turnTimings.length - 1;
|
|
170
|
+
for (let turnIndex = transcriptTurns.length - 1; turnIndex >= 0; turnIndex -= 1) {
|
|
171
|
+
const transcriptTurn = transcriptTurns[turnIndex];
|
|
172
|
+
const userPosition = transcriptTurn?.userEntryIndex === undefined
|
|
173
|
+
? undefined
|
|
174
|
+
: userPositionByEntryIndex.get(transcriptTurn.userEntryIndex);
|
|
175
|
+
if (!transcriptTurn?.prompt || userPosition === undefined)
|
|
176
|
+
continue;
|
|
177
|
+
const exactAnchor = anchorByUserPosition.get(userPosition);
|
|
178
|
+
if (exactAnchor !== undefined) {
|
|
179
|
+
timingUpperBound = Math.min(timingUpperBound, exactAnchor - 1);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
let steeringTimingIndex;
|
|
183
|
+
for (let timingIndex = timingUpperBound; timingIndex >= 0; timingIndex -= 1) {
|
|
184
|
+
const timing = turnTimings[timingIndex];
|
|
185
|
+
if (assignedTimingIndexes.has(timingIndex) || timing.userMessageType !== "message_steered")
|
|
186
|
+
continue;
|
|
187
|
+
if (normalizedPrompt(timing.userText) !== transcriptTurn.prompt)
|
|
188
|
+
continue;
|
|
189
|
+
steeringTimingIndex = timingIndex;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
if (steeringTimingIndex !== undefined) {
|
|
193
|
+
anchorByUserPosition.set(userPosition, steeringTimingIndex);
|
|
194
|
+
assignedTimingIndexes.add(steeringTimingIndex);
|
|
195
|
+
timingUpperBound = steeringTimingIndex - 1;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!transcriptTurn.settled)
|
|
199
|
+
continue;
|
|
200
|
+
let matchedTimingIndex;
|
|
201
|
+
let matchedDistance = Number.POSITIVE_INFINITY;
|
|
202
|
+
for (let timingIndex = timingUpperBound; timingIndex >= 0; timingIndex -= 1) {
|
|
203
|
+
const timing = turnTimings[timingIndex];
|
|
204
|
+
if (assignedTimingIndexes.has(timingIndex) || timing.userMessageType === "message_steered" || !timing.completedAt)
|
|
205
|
+
continue;
|
|
206
|
+
if (normalizedPrompt(timing.userText) !== transcriptTurn.prompt)
|
|
207
|
+
continue;
|
|
208
|
+
const completedAt = parsedTimestamp(timing.completedAt);
|
|
209
|
+
const distance = completedAt === undefined || transcriptTurn.assistantAt === undefined
|
|
210
|
+
? Number.POSITIVE_INFINITY
|
|
211
|
+
: Math.abs(completedAt - transcriptTurn.assistantAt);
|
|
212
|
+
if (matchedTimingIndex === undefined || distance < matchedDistance) {
|
|
213
|
+
matchedTimingIndex = timingIndex;
|
|
214
|
+
matchedDistance = distance;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (matchedTimingIndex === undefined)
|
|
218
|
+
continue;
|
|
219
|
+
anchorByUserPosition.set(userPosition, matchedTimingIndex);
|
|
220
|
+
assignedTimingIndexes.add(matchedTimingIndex);
|
|
221
|
+
timingUpperBound = matchedTimingIndex - 1;
|
|
222
|
+
}
|
|
223
|
+
const assignments = new Map();
|
|
224
|
+
const anchors = [...anchorByUserPosition.entries()]
|
|
225
|
+
.map(([userPosition, timingIndex]) => ({ userPosition, timingIndex }))
|
|
226
|
+
.sort((left, right) => left.userPosition - right.userPosition);
|
|
227
|
+
for (const anchor of anchors) {
|
|
228
|
+
assignments.set(users[anchor.userPosition].entryIndex, turnTimings[anchor.timingIndex]);
|
|
229
|
+
}
|
|
230
|
+
const boundaries = [
|
|
231
|
+
{ userPosition: -1, timingIndex: -1 },
|
|
232
|
+
...anchors,
|
|
233
|
+
{ userPosition: users.length, timingIndex: turnTimings.length },
|
|
234
|
+
];
|
|
235
|
+
for (let boundaryIndex = 0; boundaryIndex < boundaries.length - 1; boundaryIndex += 1) {
|
|
236
|
+
const previous = boundaries[boundaryIndex];
|
|
237
|
+
const next = boundaries[boundaryIndex + 1];
|
|
238
|
+
let timingCursor = next.timingIndex - 1;
|
|
239
|
+
for (let userPosition = next.userPosition - 1; userPosition > previous.userPosition; userPosition -= 1) {
|
|
240
|
+
const user = users[userPosition];
|
|
241
|
+
if (assignments.has(user.entryIndex) || !user.prompt)
|
|
242
|
+
continue;
|
|
243
|
+
for (let timingIndex = timingCursor; timingIndex > previous.timingIndex; timingIndex -= 1) {
|
|
244
|
+
if (assignedTimingIndexes.has(timingIndex))
|
|
245
|
+
continue;
|
|
246
|
+
const timing = turnTimings[timingIndex];
|
|
247
|
+
if (normalizedPrompt(timing.userText) !== user.prompt)
|
|
248
|
+
continue;
|
|
249
|
+
assignments.set(user.entryIndex, timing);
|
|
250
|
+
assignedTimingIndexes.add(timingIndex);
|
|
251
|
+
timingCursor = timingIndex - 1;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return assignments;
|
|
257
|
+
}
|
|
258
|
+
function isSettledAssistantEntry(entry) {
|
|
259
|
+
const message = entry.message;
|
|
260
|
+
if (message.stopReason === "toolUse" || message.stopReason === "tool_use")
|
|
261
|
+
return false;
|
|
262
|
+
return message.status !== "streaming" && message.status !== "in_progress";
|
|
263
|
+
}
|
|
122
264
|
function normalizedPrompt(value) {
|
|
123
265
|
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
124
266
|
return normalized || undefined;
|
|
@@ -144,7 +286,7 @@ function collectAssistantTurn(entries, startIndex) {
|
|
|
144
286
|
}
|
|
145
287
|
return { entries: turnEntries, nextIndex: index };
|
|
146
288
|
}
|
|
147
|
-
function createUserMessageNode(piboSessionId, entry, content, entryIndex) {
|
|
289
|
+
function createUserMessageNode(piboSessionId, entry, content, entryIndex, timing) {
|
|
148
290
|
const text = extractText(content);
|
|
149
291
|
const notification = parseRunNotificationText(text);
|
|
150
292
|
if (notification) {
|
|
@@ -159,8 +301,10 @@ function createUserMessageNode(piboSessionId, entry, content, entryIndex) {
|
|
|
159
301
|
notification,
|
|
160
302
|
});
|
|
161
303
|
}
|
|
304
|
+
const userMessageType = timing?.userMessageType ?? "message_queued";
|
|
305
|
+
const eventIdentity = timing ? `event:${userMessageType}:${timing.eventId}` : undefined;
|
|
162
306
|
return {
|
|
163
|
-
id: `entry:${entry.id}`,
|
|
307
|
+
id: eventIdentity ?? `entry:${entry.id}`,
|
|
164
308
|
entryId: entry.id,
|
|
165
309
|
piboSessionId,
|
|
166
310
|
type: "user.message",
|
|
@@ -170,7 +314,7 @@ function createUserMessageNode(piboSessionId, entry, content, entryIndex) {
|
|
|
170
314
|
summary: text,
|
|
171
315
|
output: text,
|
|
172
316
|
source: "transcript",
|
|
173
|
-
stableKey: `entry:${entry.id}`,
|
|
317
|
+
stableKey: eventIdentity ?? `entry:${entry.id}`,
|
|
174
318
|
orderKey: transcriptTraceOrder(entryIndex, 0, "user.message"),
|
|
175
319
|
children: [],
|
|
176
320
|
};
|
|
@@ -181,7 +325,8 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
181
325
|
return [];
|
|
182
326
|
const orderedNodes = [];
|
|
183
327
|
const toolsByCallId = new Map();
|
|
184
|
-
let
|
|
328
|
+
let assistantPartOrdinal = 0;
|
|
329
|
+
let reasoningPartOrdinal = 0;
|
|
185
330
|
for (const { entry, index: entryIndex } of entries) {
|
|
186
331
|
if (messageRole(entry) === "toolResult") {
|
|
187
332
|
mergePersistedToolResult(toolsByCallId, orderedNodes, entry, piboSessionId, entryIndex);
|
|
@@ -193,16 +338,28 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
193
338
|
for (const [index, part] of messageParts(entry).entries()) {
|
|
194
339
|
const typed = part;
|
|
195
340
|
if (typed.type === "thinking" && typeof typed.thinking === "string" && hasVisibleText(typed.thinking)) {
|
|
196
|
-
|
|
341
|
+
const thinkingIndex = timing?.reasoningIndices?.[reasoningPartOrdinal] ?? reasoningPartOrdinal;
|
|
342
|
+
orderedNodes.push(createReasoningNode({
|
|
343
|
+
piboSessionId,
|
|
344
|
+
entry,
|
|
345
|
+
entryIndex,
|
|
346
|
+
contentPartIndex: index,
|
|
347
|
+
thinkingIndex,
|
|
348
|
+
eventId: timing?.eventId,
|
|
349
|
+
thinking: typed.thinking,
|
|
350
|
+
}));
|
|
351
|
+
reasoningPartOrdinal += 1;
|
|
197
352
|
}
|
|
198
353
|
else if (typed.type === "text" && typeof typed.text === "string" && typed.text !== "") {
|
|
199
354
|
if (!responseNode) {
|
|
355
|
+
const assistantIndex = timing?.assistantIndices?.[assistantPartOrdinal] ?? assistantPartOrdinal;
|
|
200
356
|
responseNode = createAssistantMessageNode({
|
|
201
|
-
id: `entry:${entry.id}:response`,
|
|
202
357
|
piboSessionId,
|
|
203
358
|
entry,
|
|
204
359
|
entryIndex,
|
|
205
360
|
contentPartIndex: index,
|
|
361
|
+
assistantIndex,
|
|
362
|
+
eventId: timing?.eventId,
|
|
206
363
|
status: responseStatus,
|
|
207
364
|
text: typed.text,
|
|
208
365
|
error: responseError,
|
|
@@ -225,9 +382,7 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
225
382
|
if (responseNode) {
|
|
226
383
|
responseNode.status = responseStatus;
|
|
227
384
|
responseNode.error = responseError;
|
|
228
|
-
|
|
229
|
-
responseNode.stableKey = `assistant:${timing.eventId}:assistant:${assistantIndex}`;
|
|
230
|
-
assistantIndex += 1;
|
|
385
|
+
assistantPartOrdinal += 1;
|
|
231
386
|
}
|
|
232
387
|
}
|
|
233
388
|
const finalNode = orderedNodes.at(-1);
|
|
@@ -237,20 +392,21 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
237
392
|
}
|
|
238
393
|
return orderedNodes;
|
|
239
394
|
}
|
|
240
|
-
function createReasoningNode(
|
|
395
|
+
function createReasoningNode(input) {
|
|
396
|
+
const eventIdentity = input.eventId ? `${input.eventId}:thinking:${input.thinkingIndex}` : undefined;
|
|
241
397
|
return {
|
|
242
|
-
id: `entry:${entry.id}:thinking:${
|
|
243
|
-
entryId: entry.id,
|
|
244
|
-
piboSessionId,
|
|
398
|
+
id: eventIdentity ? thinkingNodeId(eventIdentity) : `entry:${input.entry.id}:thinking:${input.contentPartIndex}`,
|
|
399
|
+
entryId: input.entry.id,
|
|
400
|
+
piboSessionId: input.piboSessionId,
|
|
245
401
|
type: "model.reasoning",
|
|
246
402
|
title: "Thinking",
|
|
247
403
|
status: "done",
|
|
248
|
-
startedAt: entry.timestamp,
|
|
249
|
-
summary: thinking,
|
|
250
|
-
output: thinking,
|
|
404
|
+
startedAt: input.entry.timestamp,
|
|
405
|
+
summary: input.thinking,
|
|
406
|
+
output: input.thinking,
|
|
251
407
|
source: "transcript",
|
|
252
|
-
stableKey: `entry:${entry.id}:thinking:${
|
|
253
|
-
orderKey: transcriptTraceOrder(entryIndex,
|
|
408
|
+
stableKey: eventIdentity ? `reasoning:${eventIdentity}` : `entry:${input.entry.id}:thinking:${input.contentPartIndex}`,
|
|
409
|
+
orderKey: transcriptTraceOrder(input.entryIndex, input.contentPartIndex, "model.reasoning"),
|
|
254
410
|
children: [],
|
|
255
411
|
};
|
|
256
412
|
}
|
|
@@ -258,7 +414,7 @@ function createToolCallNode(piboSessionId, entry, entryIndex, contentPartIndex,
|
|
|
258
414
|
const name = typeof part.name === "string" ? part.name : "Tool Call";
|
|
259
415
|
const toolCallId = typeof part.id === "string" ? part.id : undefined;
|
|
260
416
|
return {
|
|
261
|
-
id: `entry:${entry.id}:tool:${
|
|
417
|
+
id: toolCallId ? `tool:${toolCallId}` : `entry:${entry.id}:tool:${contentPartIndex}`,
|
|
262
418
|
entryId: entry.id,
|
|
263
419
|
piboSessionId,
|
|
264
420
|
toolCallId,
|
|
@@ -293,7 +449,7 @@ function mergePersistedToolResult(toolsByCallId, childNodes, entry, piboSessionI
|
|
|
293
449
|
function createMissingToolResultNode(piboSessionId, entry, entryIndex, toolCallId) {
|
|
294
450
|
const message = entry.message;
|
|
295
451
|
return {
|
|
296
|
-
id: `
|
|
452
|
+
id: `tool:${toolCallId}`,
|
|
297
453
|
entryId: entry.id,
|
|
298
454
|
piboSessionId,
|
|
299
455
|
toolCallId,
|
|
@@ -313,10 +469,12 @@ function toolResultOutput(message) {
|
|
|
313
469
|
return { content: message.content, details: message.details };
|
|
314
470
|
}
|
|
315
471
|
function createAssistantMessageNode(input) {
|
|
472
|
+
const eventIdentity = input.eventId ? `${input.eventId}:assistant:${input.assistantIndex}` : undefined;
|
|
316
473
|
return {
|
|
317
|
-
id: input.id
|
|
474
|
+
id: eventIdentity ? assistantMessageNodeId(eventIdentity) : `entry:${input.entry.id}:response`,
|
|
318
475
|
entryId: input.entry.id,
|
|
319
476
|
piboSessionId: input.piboSessionId,
|
|
477
|
+
eventId: input.eventId,
|
|
320
478
|
type: "assistant.message",
|
|
321
479
|
title: "Agent Message",
|
|
322
480
|
status: input.status,
|
|
@@ -326,7 +484,7 @@ function createAssistantMessageNode(input) {
|
|
|
326
484
|
output: input.text,
|
|
327
485
|
error: input.error,
|
|
328
486
|
source: "transcript",
|
|
329
|
-
stableKey: `entry:${input.entry.id}:response:${input.contentPartIndex}`,
|
|
487
|
+
stableKey: eventIdentity ? `assistant:${eventIdentity}` : `entry:${input.entry.id}:response:${input.contentPartIndex}`,
|
|
330
488
|
orderKey: transcriptTraceOrder(input.entryIndex, input.contentPartIndex, "assistant.message"),
|
|
331
489
|
children: input.children ?? [],
|
|
332
490
|
};
|
package/dist/tools/cdp-client.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
export const DEFAULT_CDP_URL = "http://127.0.0.1:56663";
|
|
2
3
|
export const DEFAULT_CDP_TIMEOUT_MS = 2_500;
|
|
4
|
+
const CDP_JSON_CHUNK_SIZE = 256 * 1024;
|
|
3
5
|
export class CdpClient {
|
|
4
6
|
webSocketUrl;
|
|
5
7
|
nextId = 0;
|
|
@@ -32,10 +34,11 @@ export class CdpClient {
|
|
|
32
34
|
socket.addEventListener("open", succeed);
|
|
33
35
|
socket.addEventListener("message", (event) => this.handleMessage(String(event.data)));
|
|
34
36
|
socket.addEventListener("error", () => fail(new Error(`CDP WebSocket error connecting to ${this.webSocketUrl}`)));
|
|
35
|
-
socket.addEventListener("close", () => {
|
|
37
|
+
socket.addEventListener("close", (event) => {
|
|
38
|
+
const detail = `code ${event.code}${event.reason ? `: ${event.reason}` : ""}`;
|
|
36
39
|
for (const [id, pending] of this.pending) {
|
|
37
40
|
clearTimeout(pending.timer);
|
|
38
|
-
pending.reject(new Error(
|
|
41
|
+
pending.reject(new Error(`CDP target closed during ${pending.method} (${detail})`));
|
|
39
42
|
this.pending.delete(id);
|
|
40
43
|
}
|
|
41
44
|
});
|
|
@@ -51,11 +54,17 @@ export class CdpClient {
|
|
|
51
54
|
this.pending.delete(id);
|
|
52
55
|
reject(new Error(`Timed out waiting for CDP method ${method}`));
|
|
53
56
|
}, timeoutMs);
|
|
54
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
57
|
+
this.pending.set(id, { method, resolve, reject, timer });
|
|
55
58
|
});
|
|
56
59
|
this.socket.send(JSON.stringify(payload));
|
|
57
60
|
return promise;
|
|
58
61
|
}
|
|
62
|
+
sendDetached(method, params) {
|
|
63
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
64
|
+
return;
|
|
65
|
+
const id = ++this.nextId;
|
|
66
|
+
this.socket.send(JSON.stringify(params ? { id, method, params } : { id, method }));
|
|
67
|
+
}
|
|
59
68
|
async evaluate(expression, timeoutMs = 10_000) {
|
|
60
69
|
const response = await this.send("Runtime.evaluate", {
|
|
61
70
|
expression,
|
|
@@ -67,6 +76,72 @@ export class CdpClient {
|
|
|
67
76
|
throw new Error(`Browser evaluation failed: ${JSON.stringify(response.exceptionDetails)}`);
|
|
68
77
|
return response.result?.value;
|
|
69
78
|
}
|
|
79
|
+
async evaluateJson(expression, timeoutMs = 10_000) {
|
|
80
|
+
const deadlineMs = Date.now() + timeoutMs;
|
|
81
|
+
const cleanupReserveMs = Math.min(100, Math.max(1, Math.floor(timeoutMs / 10)));
|
|
82
|
+
const transferDeadlineMs = deadlineMs - cleanupReserveMs;
|
|
83
|
+
const storageKey = `__piboCdpJsonResult_${randomUUID()}`;
|
|
84
|
+
const keyLiteral = JSON.stringify(storageKey);
|
|
85
|
+
try {
|
|
86
|
+
const descriptor = await this.evaluate(`(async () => {
|
|
87
|
+
const deadlineMs = ${deadlineMs};
|
|
88
|
+
if (Date.now() >= deadlineMs) throw new Error("CDP JSON evaluation exceeded its deadline");
|
|
89
|
+
const state = { cancelled: false, json: undefined, cleanupTimer: undefined };
|
|
90
|
+
Object.defineProperty(globalThis, ${keyLiteral}, { value: state, configurable: true });
|
|
91
|
+
state.cleanupTimer = setTimeout(() => {
|
|
92
|
+
state.cancelled = true;
|
|
93
|
+
if (globalThis[${keyLiteral}] === state) delete globalThis[${keyLiteral}];
|
|
94
|
+
}, Math.max(0, deadlineMs - Date.now()));
|
|
95
|
+
try {
|
|
96
|
+
const value = await (${expression});
|
|
97
|
+
const json = JSON.stringify(value);
|
|
98
|
+
if (typeof json !== "string") throw new Error("CDP evaluation result is not JSON serializable");
|
|
99
|
+
if (Date.now() >= deadlineMs || state.cancelled || globalThis[${keyLiteral}] !== state) throw new Error("CDP JSON evaluation exceeded its deadline");
|
|
100
|
+
state.json = json;
|
|
101
|
+
return { length: json.length };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
state.cancelled = true;
|
|
104
|
+
clearTimeout(state.cleanupTimer);
|
|
105
|
+
if (globalThis[${keyLiteral}] === state) delete globalThis[${keyLiteral}];
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
})()`, remainingTime(transferDeadlineMs, "evaluating JSON through CDP"));
|
|
109
|
+
if (!Number.isInteger(descriptor?.length) || descriptor.length < 0)
|
|
110
|
+
throw new Error("CDP evaluation returned an invalid JSON result length");
|
|
111
|
+
const chunks = [];
|
|
112
|
+
for (let offset = 0; offset < descriptor.length; offset += CDP_JSON_CHUNK_SIZE) {
|
|
113
|
+
const end = Math.min(descriptor.length, offset + CDP_JSON_CHUNK_SIZE);
|
|
114
|
+
chunks.push(await this.evaluate(`(() => {
|
|
115
|
+
const state = globalThis[${keyLiteral}];
|
|
116
|
+
if (!state || typeof state.json !== "string") throw new Error("CDP JSON result storage is unavailable");
|
|
117
|
+
return state.json.slice(${offset}, ${end});
|
|
118
|
+
})()`, remainingTime(transferDeadlineMs, "retrieving JSON chunks through CDP")));
|
|
119
|
+
}
|
|
120
|
+
return JSON.parse(chunks.join(""));
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
const cleanupExpression = `(() => {
|
|
124
|
+
const state = globalThis[${keyLiteral}];
|
|
125
|
+
if (state && typeof state === "object") {
|
|
126
|
+
state.cancelled = true;
|
|
127
|
+
clearTimeout(state.cleanupTimer);
|
|
128
|
+
}
|
|
129
|
+
return delete globalThis[${keyLiteral}];
|
|
130
|
+
})()`;
|
|
131
|
+
const cleanupTimeoutMs = remainingTimeOrZero(deadlineMs);
|
|
132
|
+
if (cleanupTimeoutMs > 0) {
|
|
133
|
+
await this.evaluate(cleanupExpression, cleanupTimeoutMs).catch(() => undefined);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.sendDetached("Runtime.evaluate", {
|
|
137
|
+
expression: cleanupExpression,
|
|
138
|
+
awaitPromise: true,
|
|
139
|
+
returnByValue: true,
|
|
140
|
+
userGesture: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
70
145
|
close() {
|
|
71
146
|
try {
|
|
72
147
|
this.socket?.close();
|
|
@@ -187,3 +262,12 @@ function optionalStringValue(value) {
|
|
|
187
262
|
const text = stringValue(value);
|
|
188
263
|
return text || undefined;
|
|
189
264
|
}
|
|
265
|
+
function remainingTime(deadlineMs, operation) {
|
|
266
|
+
const remainingMs = remainingTimeOrZero(deadlineMs);
|
|
267
|
+
if (remainingMs <= 0)
|
|
268
|
+
throw new Error(`Timed out ${operation}`);
|
|
269
|
+
return remainingMs;
|
|
270
|
+
}
|
|
271
|
+
function remainingTimeOrZero(deadlineMs) {
|
|
272
|
+
return Math.max(0, Math.floor(deadlineMs - Date.now()));
|
|
273
|
+
}
|