@pasko70/pibo 3.4.4 → 3.5.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/agent-runtime/routed-session.js +111 -25
- package/dist/agent-runtimes/codex-native/turn.js +27 -3
- package/dist/agent-runtimes/pi/adapter.js +1 -0
- package/dist/agent-runtimes/pi/routed-session.js +2 -2
- package/dist/apps/chat/bounded-event-stream.js +98 -0
- package/dist/apps/chat/chat-settings-routes.js +3 -3
- package/dist/apps/chat/data/chat-data-mappers.js +40 -14
- package/dist/apps/chat/data/event-command-service.js +30 -21
- package/dist/apps/chat/data/history-query-service.js +34 -25
- package/dist/apps/chat/data/read-state-service.js +37 -3
- package/dist/apps/chat/data/session-query-service.js +19 -11
- package/dist/apps/chat/data/timeline-query-service.js +19 -7
- package/dist/apps/chat/message-command-dispatcher.js +149 -0
- package/dist/apps/chat/output-compactor.js +9 -0
- package/dist/apps/chat/output-event-policy.js +9 -1
- package/dist/apps/chat/stream.js +21 -3
- package/dist/apps/chat/telemetry-retention-service.js +113 -8
- package/dist/apps/chat/trace-response-cache.js +46 -0
- package/dist/apps/chat/trace-v2.js +12 -6
- package/dist/apps/chat/trace.js +1 -1
- package/dist/apps/chat/web-app.js +645 -297
- package/dist/apps/chat-ui/assets/{dist-V06sfuZa.js → dist-B-auLrzD.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CH3SvpYV.js → dist-BA_dsINH.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-23lt_7qm.js → dist-eJZar_0-.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bf2KScPo.js → dist-wNNR2Bci.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DsgL8w-W.js → dist-zcmEsIEp.js} +1 -1
- package/dist/apps/chat-ui/assets/index-DEkbN5Vo.js +229 -0
- package/dist/apps/chat-ui/assets/index-DZK6Tzil.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-DZgW1fCB.js +44 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/cli-session/localSessionSource.js +3 -2
- package/dist/compute/pool/seeds.js +25 -3
- package/dist/core/events.js +4 -0
- package/dist/core/output-render-sequence.js +65 -6
- package/dist/core/provider-capacity.js +33 -0
- package/dist/core/provider-telemetry.js +21 -6
- package/dist/core/runtime-capacity.js +174 -0
- package/dist/core/runtime-telemetry.js +40 -12
- package/dist/core/session-router.js +178 -13
- package/dist/data/async-chat-reads.js +38 -0
- package/dist/data/async-chat-storage.js +96 -0
- package/dist/data/async-telemetry-maintenance.js +9 -0
- package/dist/data/bounded-worker-client.js +256 -0
- package/dist/data/chat-read-projections.js +159 -0
- package/dist/data/chat-read-worker.js +73 -0
- package/dist/data/chat-storage-worker.js +167 -0
- package/dist/data/ingest-service.js +152 -18
- package/dist/data/message-command-store.js +290 -0
- package/dist/data/payload-store.js +92 -11
- package/dist/data/pibo-store.js +10 -8
- package/dist/data/schema.js +39 -4
- package/dist/data/session-store.js +3 -1
- package/dist/data/storage-backup.js +278 -0
- package/dist/data/storage-maintenance.js +344 -0
- package/dist/data/storage-verification-worker.js +25 -0
- package/dist/data/telemetry-capture.js +188 -0
- package/dist/data/telemetry-command.js +3 -0
- package/dist/data/telemetry-maintenance-worker.js +40 -0
- package/dist/data/telemetry-maintenance.js +110 -0
- package/dist/data/telemetry-retention.js +16 -7
- package/dist/data/telemetry-worker.js +111 -0
- package/dist/data/telemetry-writer.js +150 -83
- package/dist/data/telemetry.js +5 -0
- package/dist/debug/index.js +207 -1
- package/dist/debug/message-queue.js +108 -0
- package/dist/debug/output-collision-repair.js +140 -0
- package/dist/debug/output-integrity.js +38 -2
- package/dist/debug/output-repair.js +1 -0
- package/dist/debug/storage-backup.js +41 -0
- package/dist/debug/storage-maintenance.js +78 -0
- package/dist/debug/telemetry-capture.js +66 -0
- package/dist/gateway/cli.js +71 -7
- package/dist/gateway/server.js +3 -0
- package/dist/providers/openai-gpt56.js +11 -6
- package/dist/reliability/store.js +119 -23
- package/dist/session-ui/terminalRows.js +54 -13
- package/dist/sessions/pibo-data-store.js +19 -14
- package/dist/shared/debug-features.js +4 -0
- package/dist/shared/model-inference-metrics.js +23 -0
- package/dist/shared/trace-event-projection.js +69 -2
- package/dist/shared/trace-history.js +9 -0
- package/dist/shared/trace-live-reducer.js +1 -0
- package/dist/shared/trace-patch-nodes.js +19 -0
- package/dist/web/channel.js +117 -12
- package/dist/web/http.js +135 -41
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-BOceJ0jM.css +0 -1
- package/dist/apps/chat-ui/assets/index-BOemYq-V.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-CMwTB8o8.js +0 -43
|
@@ -16,14 +16,15 @@ export function buildCompactTerminalRows(traceView, options) {
|
|
|
16
16
|
if (!traceView)
|
|
17
17
|
return [];
|
|
18
18
|
const turnById = mapTurnNodes(traceView.nodes);
|
|
19
|
+
const showToolDebugMetrics = Boolean(options.debugMode && (options.debugFeatures?.toolMetrics ?? true));
|
|
19
20
|
const flatNodes = flattenTraceNodes(traceView.nodes)
|
|
20
21
|
.sort((left, right) => compareTraceNodes(left.node, right.node))
|
|
21
22
|
.filter((item) => item.node.type !== "agent.turn" && (options.showThinking || item.node.type !== "model.reasoning"));
|
|
22
23
|
const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
|
|
23
24
|
applyCompletedTurnTiming(candidates, turnById);
|
|
24
25
|
const reconciled = reconcileConceptualRowCandidates(candidates);
|
|
25
|
-
const rows =
|
|
26
|
-
? groupRelatedToolCandidates(reconciled).map((candidate) => candidate.row)
|
|
26
|
+
const rows = (options.toolDisplayMode ?? "default") === "default"
|
|
27
|
+
? groupRelatedToolCandidates(reconciled, showToolDebugMetrics).map((candidate) => candidate.row)
|
|
27
28
|
: reconciled.map((candidate) => candidate.row);
|
|
28
29
|
return applyToolDisplayMode(rows, options.toolDisplayMode ?? "default");
|
|
29
30
|
}
|
|
@@ -159,18 +160,34 @@ function createRowCandidate(node, turnId) {
|
|
|
159
160
|
};
|
|
160
161
|
break;
|
|
161
162
|
}
|
|
163
|
+
const isToolCall = node.type === "tool.call" || node.type === "tool.result" || (node.type === "agent.delegation" && Boolean(node.toolCallId));
|
|
164
|
+
const reference = isToolCall ? toolCallReference(node) : undefined;
|
|
162
165
|
return {
|
|
163
166
|
...candidate,
|
|
164
167
|
row: {
|
|
165
168
|
...candidate.row,
|
|
166
169
|
id: compactTerminalRowIdentity(node),
|
|
167
170
|
intent: node.intent,
|
|
168
|
-
isToolCall
|
|
171
|
+
isToolCall,
|
|
172
|
+
toolCallReference: reference,
|
|
173
|
+
expandable: reference || Object.values(node.payloadRefs ?? {}).some(Boolean) ? true : candidate.row.expandable,
|
|
169
174
|
toolMetrics: node.toolMetrics,
|
|
175
|
+
modelInferences: node.modelInferences,
|
|
170
176
|
...debugFields(node),
|
|
171
177
|
},
|
|
172
178
|
};
|
|
173
179
|
}
|
|
180
|
+
function toolCallReference(node) {
|
|
181
|
+
if (!node.toolCallId)
|
|
182
|
+
return undefined;
|
|
183
|
+
const parsed = parseTraceToolNodeIdentity(node.id);
|
|
184
|
+
return {
|
|
185
|
+
traceNodeId: node.id,
|
|
186
|
+
toolCallId: node.toolCallId,
|
|
187
|
+
eventId: node.eventId ?? parsed?.qualifier?.eventId,
|
|
188
|
+
invocationOrdinal: node.toolInvocationOrdinal ?? parsed?.qualifier?.invocationOrdinal,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
174
191
|
export function compactTerminalRowIdentity(node) {
|
|
175
192
|
if (node.type === "tool.call" || node.type === "tool.result" || node.type === "agent.delegation") {
|
|
176
193
|
if (node.toolCallId) {
|
|
@@ -226,6 +243,7 @@ function reconcileConceptualRowCandidates(candidates) {
|
|
|
226
243
|
output: candidate.row.output ?? existing.row.output,
|
|
227
244
|
error: candidate.row.error ?? existing.row.error,
|
|
228
245
|
payloadRefs: { ...existing.row.payloadRefs, ...candidate.row.payloadRefs },
|
|
246
|
+
modelInferences: mergeModelInferences(existing.row.modelInferences, candidate.row.modelInferences),
|
|
229
247
|
imagePreviews: mergeImagePreviews(existing.row.imagePreviews, candidate.row.imagePreviews),
|
|
230
248
|
},
|
|
231
249
|
};
|
|
@@ -310,6 +328,16 @@ function assistantPartIndex(rowId) {
|
|
|
310
328
|
const index = Number(match[1]);
|
|
311
329
|
return Number.isSafeInteger(index) ? index : undefined;
|
|
312
330
|
}
|
|
331
|
+
function mergeModelInferences(existing, candidate) {
|
|
332
|
+
if (!existing?.length)
|
|
333
|
+
return candidate;
|
|
334
|
+
if (!candidate?.length)
|
|
335
|
+
return existing;
|
|
336
|
+
const merged = new Map(existing.map((record) => [record.id, record]));
|
|
337
|
+
for (const record of candidate)
|
|
338
|
+
merged.set(record.id, record);
|
|
339
|
+
return [...merged.values()];
|
|
340
|
+
}
|
|
313
341
|
function mergeImagePreviews(existing, candidate) {
|
|
314
342
|
if (!existing?.length)
|
|
315
343
|
return candidate?.slice(0, MAX_COMPACT_TERMINAL_IMAGE_PREVIEWS);
|
|
@@ -329,6 +357,9 @@ function applyCompletedTurnTiming(candidates, turnById) {
|
|
|
329
357
|
continue;
|
|
330
358
|
const turnCandidates = candidates.filter((candidate) => candidate.turnId === turn.id);
|
|
331
359
|
const finalCandidate = turnCandidates.at(-1);
|
|
360
|
+
if (finalCandidate && turn.modelInferences?.length) {
|
|
361
|
+
finalCandidate.row.modelInferences = mergeModelInferences(finalCandidate.row.modelInferences, turn.modelInferences);
|
|
362
|
+
}
|
|
332
363
|
if (finalCandidate?.row.kind !== "message.assistant" || finalCandidate.row.status === "running")
|
|
333
364
|
continue;
|
|
334
365
|
finalCandidate.row.startedAt = turn.startedAt;
|
|
@@ -362,7 +393,8 @@ function createUserMessageRow(node) {
|
|
|
362
393
|
lines: [{ prefix: "prompt", tokens: [token(text)] }],
|
|
363
394
|
sourceNodeIds: [node.id],
|
|
364
395
|
forkEntryId: node.entryId,
|
|
365
|
-
pendingMessageDelivery: pendingUserMessageDelivery(node),
|
|
396
|
+
pendingMessageDelivery: pendingUserMessageDelivery(node) ?? (node.status === "running" && node.messageDeliveryState ? "queue" : undefined),
|
|
397
|
+
messageDeliveryState: node.messageDeliveryState,
|
|
366
398
|
startedAt: node.startedAt,
|
|
367
399
|
output: text,
|
|
368
400
|
payloadRefs: node.payloadRefs,
|
|
@@ -657,9 +689,16 @@ function createCompactionRow(node) {
|
|
|
657
689
|
input: node.input,
|
|
658
690
|
output: node.output,
|
|
659
691
|
error: node.error,
|
|
660
|
-
|
|
692
|
+
compactionStats: node.compactionStats,
|
|
693
|
+
compactionMarkdown: compactionMarkdown(node.output),
|
|
694
|
+
expandable: node.status === "error" && (node.input !== undefined || node.output !== undefined || Boolean(node.error)),
|
|
661
695
|
};
|
|
662
696
|
}
|
|
697
|
+
function compactionMarkdown(value) {
|
|
698
|
+
if (!isRecord(value))
|
|
699
|
+
return undefined;
|
|
700
|
+
return stringValue(value.summary);
|
|
701
|
+
}
|
|
663
702
|
function createExecutionCommandRow(node) {
|
|
664
703
|
if (node.title === "status") {
|
|
665
704
|
return createStatusToolRow(node);
|
|
@@ -903,12 +942,12 @@ function isThinkingOutput(value) {
|
|
|
903
942
|
function isThinkingLevelSetOutput(value) {
|
|
904
943
|
return isRecord(value) && value.action === "set_thinking_level";
|
|
905
944
|
}
|
|
906
|
-
function groupRelatedToolCandidates(candidates) {
|
|
945
|
+
function groupRelatedToolCandidates(candidates, imagesOnly = false) {
|
|
907
946
|
const grouped = [];
|
|
908
947
|
for (let index = 0; index < candidates.length; index += 1) {
|
|
909
948
|
const candidate = candidates[index];
|
|
910
949
|
const groupKind = candidateGroupKind(candidate);
|
|
911
|
-
if (!groupKind) {
|
|
950
|
+
if (!groupKind || (imagesOnly && groupKind !== "images")) {
|
|
912
951
|
grouped.push(candidate);
|
|
913
952
|
continue;
|
|
914
953
|
}
|
|
@@ -966,13 +1005,14 @@ function createExploringGroup(candidates) {
|
|
|
966
1005
|
}] : []),
|
|
967
1006
|
],
|
|
968
1007
|
sourceNodeIds: candidates.flatMap((candidate) => candidate.row.sourceNodeIds),
|
|
1008
|
+
modelInferences: candidates.reduce((records, candidate) => mergeModelInferences(records, candidate.row.modelInferences), undefined),
|
|
969
1009
|
eventId: firstRow?.eventId,
|
|
970
1010
|
runId: firstRow?.runId,
|
|
971
1011
|
orderSource: firstRow?.orderSource,
|
|
972
1012
|
orderStreamId: firstRow?.orderStreamId,
|
|
973
1013
|
orderStreamFrameIndex: firstRow?.orderStreamFrameIndex,
|
|
974
1014
|
detailItems,
|
|
975
|
-
expandable: detailItems.some((item) => item.input !== undefined || item.output !== undefined || Boolean(item.error)),
|
|
1015
|
+
expandable: detailItems.some((item) => item.toolCallReference || item.input !== undefined || item.output !== undefined || Boolean(item.error)),
|
|
976
1016
|
previewOmission: omittedDetailCount > 0 ? {
|
|
977
1017
|
source: "details",
|
|
978
1018
|
visibleLineCount: visibleDetailItems.length,
|
|
@@ -988,21 +1028,20 @@ function createImageGroup(candidates) {
|
|
|
988
1028
|
const omittedDetailCount = Math.max(0, detailItems.length - visibleDetailItems.length);
|
|
989
1029
|
const firstRow = candidates[0]?.row;
|
|
990
1030
|
const firstId = firstRow?.id ?? "images";
|
|
991
|
-
const lastId = candidates[candidates.length - 1]?.row.id ?? firstId;
|
|
992
1031
|
const status = candidates.some((candidate) => candidate.row.status === "running")
|
|
993
1032
|
? "running"
|
|
994
1033
|
: candidates.some((candidate) => candidate.row.status === "error")
|
|
995
1034
|
? "error"
|
|
996
1035
|
: "done";
|
|
997
1036
|
return {
|
|
998
|
-
id:
|
|
1037
|
+
id: firstId,
|
|
999
1038
|
kind: "tool.group.images",
|
|
1000
1039
|
status,
|
|
1001
1040
|
errorKind: status === "error" ? "tool" : undefined,
|
|
1002
1041
|
lines: [
|
|
1003
1042
|
{
|
|
1004
1043
|
prefix: "bullet",
|
|
1005
|
-
tokens: [token(status === "running" ?
|
|
1044
|
+
tokens: [token(status === "running" ? `Viewing ${detailItems.length} images` : status === "error" ? `${detailItems.length} image reads · error` : `${detailItems.length} ${detailItems.length === 1 ? "Image Viewed" : "Images Viewed"}`, toneForStatus(status), "semibold")],
|
|
1006
1045
|
},
|
|
1007
1046
|
...visibleDetailItems.map((item, index) => ({
|
|
1008
1047
|
prefix: index === 0 ? "detail" : "continuation",
|
|
@@ -1014,14 +1053,15 @@ function createImageGroup(candidates) {
|
|
|
1014
1053
|
}] : []),
|
|
1015
1054
|
],
|
|
1016
1055
|
sourceNodeIds: candidates.flatMap((candidate) => candidate.row.sourceNodeIds),
|
|
1056
|
+
modelInferences: candidates.reduce((records, candidate) => mergeModelInferences(records, candidate.row.modelInferences), undefined),
|
|
1017
1057
|
eventId: firstRow?.eventId,
|
|
1018
1058
|
runId: firstRow?.runId,
|
|
1019
1059
|
orderSource: firstRow?.orderSource,
|
|
1020
1060
|
orderStreamId: firstRow?.orderStreamId,
|
|
1021
1061
|
orderStreamFrameIndex: firstRow?.orderStreamFrameIndex,
|
|
1022
1062
|
detailItems,
|
|
1023
|
-
expandable: detailItems.some((item) => item.input !== undefined || item.output !== undefined || Boolean(item.error)),
|
|
1024
|
-
imagePreviews: detailItems.flatMap((item) => item.imagePreviews ?? [])
|
|
1063
|
+
expandable: detailItems.some((item) => item.toolCallReference || item.input !== undefined || item.output !== undefined || Boolean(item.error)),
|
|
1064
|
+
imagePreviews: detailItems.flatMap((item) => item.imagePreviews ?? []),
|
|
1025
1065
|
previewOmission: omittedDetailCount > 0 ? {
|
|
1026
1066
|
source: "details",
|
|
1027
1067
|
visibleLineCount: visibleDetailItems.length,
|
|
@@ -1043,6 +1083,7 @@ function detailItemsForGroup(candidates, kind) {
|
|
|
1043
1083
|
error: candidate.row.error,
|
|
1044
1084
|
payloadRefs: candidate.row.payloadRefs,
|
|
1045
1085
|
linkedPiboSessionId: candidate.row.linkedPiboSessionId,
|
|
1086
|
+
toolCallReference: candidate.row.toolCallReference,
|
|
1046
1087
|
previewOmission: candidate.row.previewOmission,
|
|
1047
1088
|
imagePreviews: candidate.row.imagePreviews,
|
|
1048
1089
|
};
|
|
@@ -49,6 +49,7 @@ export class PiboDataSessionStore {
|
|
|
49
49
|
const row = this.db.prepare(`${SESSION_SELECT} WHERE s.id = ? AND s.deleted_at IS NULL`).get(id);
|
|
50
50
|
return row ? sessionFromRow(row) : undefined;
|
|
51
51
|
}
|
|
52
|
+
getStructureRevision() { return this.db.prepare("SELECT revision FROM chat_navigation_clock WHERE id=1").get().revision; }
|
|
52
53
|
list() {
|
|
53
54
|
return this.db.prepare(`${SESSION_SELECT} WHERE s.deleted_at IS NULL ORDER BY s.updated_at DESC`).all().map(sessionFromRow);
|
|
54
55
|
}
|
|
@@ -226,21 +227,21 @@ export class PiboDataSessionStore {
|
|
|
226
227
|
if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0)
|
|
227
228
|
continue;
|
|
228
229
|
maximum = Math.max(maximum, index);
|
|
229
|
-
persistedParts.push({ index, attributes });
|
|
230
|
+
persistedParts.push({ index, type: row.type, attributes });
|
|
230
231
|
}
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
232
|
+
// A completed turn or an exact terminal-part fingerprint is durable
|
|
233
|
+
// evidence for replay. Nonterminal parts remain unattached while the turn
|
|
234
|
+
// is open because another producer may still own them.
|
|
235
|
+
const matchesReplay = ({ type, attributes }) => (turnCompleted || outputPartEventIsTerminal(type))
|
|
236
|
+
&& (attributes.outputPartFingerprint === input.fingerprint
|
|
237
|
+
|| attributes.identityFingerprint === input.identityFingerprint);
|
|
238
|
+
const replay = (input.suppliedIndex === undefined
|
|
239
|
+
? undefined
|
|
240
|
+
: persistedParts.find((part) => part.index === input.suppliedIndex && matchesReplay(part)))
|
|
241
|
+
?? (turnCompleted ? persistedParts.find(matchesReplay) : undefined);
|
|
242
|
+
if (replay) {
|
|
243
|
+
this.observeOutputPartIndex(input, replay.index);
|
|
244
|
+
return replay.index;
|
|
244
245
|
}
|
|
245
246
|
const minimum = Math.max(input.proposedIndex, maximum + 1);
|
|
246
247
|
const row = this.db.prepare(`
|
|
@@ -537,6 +538,7 @@ export class PiboDataSessionStore {
|
|
|
537
538
|
actorId: session.id,
|
|
538
539
|
event,
|
|
539
540
|
createdAt: at,
|
|
541
|
+
persistenceProvenance: { producer: "runtime-recovery", projection: "product-history", phase: "startup-recovery" },
|
|
540
542
|
});
|
|
541
543
|
results.push({
|
|
542
544
|
turnId: recovered.turn.turnId,
|
|
@@ -725,6 +727,9 @@ function outputPartEventTypes(kind) {
|
|
|
725
727
|
case "compaction": return ["compaction_start", "compaction_end"];
|
|
726
728
|
}
|
|
727
729
|
}
|
|
730
|
+
function outputPartEventIsTerminal(type) {
|
|
731
|
+
return type === "assistant_message" || type === "thinking_finished" || type === "assistant_usage" || type === "compaction_end";
|
|
732
|
+
}
|
|
728
733
|
function parseJsonObject(json) {
|
|
729
734
|
if (!json)
|
|
730
735
|
return {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function modelInferenceInputTokens(metrics) {
|
|
2
|
+
const total = tokenCount(metrics?.totalTokens);
|
|
3
|
+
const output = tokenCount(metrics?.outputTokens);
|
|
4
|
+
if (total !== undefined && output !== undefined)
|
|
5
|
+
return Math.max(0, total - output);
|
|
6
|
+
return tokenCount(metrics?.inputTokens);
|
|
7
|
+
}
|
|
8
|
+
export function modelInferenceCachedInputTokens(metrics) {
|
|
9
|
+
const cacheRead = tokenCount(metrics?.cacheReadTokens);
|
|
10
|
+
const cacheWrite = tokenCount(metrics?.cacheWriteTokens);
|
|
11
|
+
if (cacheRead === undefined && cacheWrite === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
return (cacheRead ?? 0) + (cacheWrite ?? 0);
|
|
14
|
+
}
|
|
15
|
+
export function modelInferenceUncachedInputTokens(metrics) {
|
|
16
|
+
const input = modelInferenceInputTokens(metrics);
|
|
17
|
+
if (input === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
return Math.max(0, input - (modelInferenceCachedInputTokens(metrics) ?? 0));
|
|
20
|
+
}
|
|
21
|
+
function tokenCount(value) {
|
|
22
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : undefined;
|
|
23
|
+
}
|
|
@@ -6,6 +6,10 @@ import { findLikelyTraceChildSession, isSubagentToolName, } from "./trace-subage
|
|
|
6
6
|
import { qualifiedToolNodeId } from "./trace-tool-identity.js";
|
|
7
7
|
export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent, childByParent, linkedChildByToolCallId, historyCoverage, openTranscriptEventIds, sessionStatus) {
|
|
8
8
|
const payload = storedEvent.payload;
|
|
9
|
+
if (payload.type === "assistant_usage") {
|
|
10
|
+
attachModelInferenceToLatestOutput(nodes, byId, payload, storedEvent);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
if (payload.type === "message_started" && payload.source === "user" && payload.eventId) {
|
|
10
14
|
const persistedUser = flattenTraceNodes([...nodes]).find((node) => node.type === "user.message"
|
|
11
15
|
&& isPersistedHistorySource(node.source)
|
|
@@ -42,6 +46,7 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
|
|
|
42
46
|
}
|
|
43
47
|
if (payload.type === "assistant_message") {
|
|
44
48
|
const node = assistantMessageNodeFromEvent(piboSessionId, payload, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
|
|
49
|
+
applyStoredPayloadRef(node, payload, storedEvent.storedPayloadRef);
|
|
45
50
|
const existing = byId.get(node.id) ?? findMatchingContentNode(byId, node);
|
|
46
51
|
if (existing) {
|
|
47
52
|
mergeAssistantMessageEvent(existing, node);
|
|
@@ -153,8 +158,12 @@ function applyRenderSequenceToHistoryNode(byId, storedEvent, event) {
|
|
|
153
158
|
&& typeof event.toolCallId === "string"
|
|
154
159
|
&& node.toolCallId === event.toolCallId
|
|
155
160
|
&& (!eventId || !node.eventId || node.eventId === eventId)));
|
|
156
|
-
if (target)
|
|
157
|
-
|
|
161
|
+
if (!target)
|
|
162
|
+
return;
|
|
163
|
+
applyRenderSequence(target, storedEvent, event);
|
|
164
|
+
if (event.type === "tool_execution_finished" && event.toolMetrics) {
|
|
165
|
+
target.toolMetrics = event.toolMetrics;
|
|
166
|
+
}
|
|
158
167
|
}
|
|
159
168
|
function applyRenderSequence(node, storedEvent, event) {
|
|
160
169
|
const renderSequence = storedEvent.renderSequence ?? event.renderSequence;
|
|
@@ -171,6 +180,9 @@ function applyRenderSequence(node, storedEvent, event) {
|
|
|
171
180
|
function applyStoredPayloadRef(node, event, storedPayloadRef) {
|
|
172
181
|
if (!storedPayloadRef)
|
|
173
182
|
return;
|
|
183
|
+
if (event.type === "assistant_message" || event.type === "thinking_finished") {
|
|
184
|
+
node.payloadRefs = { ...node.payloadRefs, [storedPayloadRef.payloadKind ?? "output"]: storedPayloadRef };
|
|
185
|
+
}
|
|
174
186
|
if (event.type === "tool_call" || event.type === "tool_execution_started") {
|
|
175
187
|
node.payloadRefs = { ...node.payloadRefs, input: storedPayloadRef };
|
|
176
188
|
}
|
|
@@ -524,6 +536,57 @@ export function latestTraceStreamId(events, initial) {
|
|
|
524
536
|
}
|
|
525
537
|
return latest;
|
|
526
538
|
}
|
|
539
|
+
function attachModelInferenceToLatestOutput(nodes, byId, event, storedEvent) {
|
|
540
|
+
const eventId = event.eventId;
|
|
541
|
+
const candidates = flattenTraceNodes(nodes)
|
|
542
|
+
.filter((node) => node.eventId === eventId && (event.inferenceTarget || traceNodeStartedBeforeInference(node, storedEvent)) && (node.type === "assistant.message"
|
|
543
|
+
|| node.type === "model.reasoning"
|
|
544
|
+
|| node.type === "tool.call"
|
|
545
|
+
|| node.type === "agent.delegation"))
|
|
546
|
+
.sort(compareTraceNodes);
|
|
547
|
+
const anchor = event.inferenceTarget;
|
|
548
|
+
const turnNode = eventId ? byId.get(messageTurnNodeId(eventId)) : undefined;
|
|
549
|
+
const target = anchor
|
|
550
|
+
? (anchor.type === "tool"
|
|
551
|
+
? candidates.find((node) => node.toolCallId === anchor.toolCallId)
|
|
552
|
+
: anchor.type === "assistant"
|
|
553
|
+
? candidates.find((node) => node.stableKey === `assistant:${eventId}:assistant:${anchor.assistantIndex}`)
|
|
554
|
+
: turnNode) ?? turnNode
|
|
555
|
+
: candidates.at(-1) ?? turnNode;
|
|
556
|
+
if (!target)
|
|
557
|
+
return;
|
|
558
|
+
const id = event.inferenceId
|
|
559
|
+
? `${eventId ?? storedEvent.piboSessionId}:inference:${event.inferenceId}`
|
|
560
|
+
: eventId ? `${eventId}:usage:${event.usageIndex ?? 0}` : storedEvent.id;
|
|
561
|
+
const record = {
|
|
562
|
+
id,
|
|
563
|
+
completedAt: storedEvent.createdAt,
|
|
564
|
+
metrics: {
|
|
565
|
+
...(event.inputTokens === undefined ? {} : { inputTokens: event.inputTokens }),
|
|
566
|
+
...(event.outputTokens === undefined ? {} : { outputTokens: event.outputTokens }),
|
|
567
|
+
...(event.cacheReadTokens === undefined ? {} : { cacheReadTokens: event.cacheReadTokens }),
|
|
568
|
+
...(event.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: event.cacheWriteTokens }),
|
|
569
|
+
...(event.reasoningTokens === undefined ? {} : { reasoningTokens: event.reasoningTokens }),
|
|
570
|
+
totalTokens: event.totalTokens,
|
|
571
|
+
...(event.costUsd === undefined ? {} : { costUsd: event.costUsd }),
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
target.modelInferences = [...(target.modelInferences ?? []).filter((item) => item.id !== id), record];
|
|
575
|
+
}
|
|
576
|
+
function traceNodeStartedBeforeInference(node, storedEvent) {
|
|
577
|
+
const inferenceRenderSequence = storedEvent.renderSequence;
|
|
578
|
+
const nodeRenderSequence = node.orderKey?.renderSequence;
|
|
579
|
+
if (inferenceRenderSequence !== undefined && nodeRenderSequence !== undefined) {
|
|
580
|
+
return nodeRenderSequence <= inferenceRenderSequence;
|
|
581
|
+
}
|
|
582
|
+
const inferenceSequence = storedEvent.eventSequence ?? storedEvent.streamId;
|
|
583
|
+
const nodeSequence = node.orderKey?.eventSequence ?? node.orderKey?.streamId;
|
|
584
|
+
if (inferenceSequence !== undefined && nodeSequence !== undefined)
|
|
585
|
+
return nodeSequence <= inferenceSequence;
|
|
586
|
+
const inferenceTime = Date.parse(storedEvent.createdAt);
|
|
587
|
+
const nodeTime = Date.parse(node.startedAt ?? node.completedAt ?? "");
|
|
588
|
+
return !Number.isFinite(inferenceTime) || !Number.isFinite(nodeTime) || nodeTime <= inferenceTime;
|
|
589
|
+
}
|
|
527
590
|
// ── event → node helpers ─────────────────────────────────────────
|
|
528
591
|
function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByToolCallId, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
|
|
529
592
|
const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
|
|
@@ -562,6 +625,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
562
625
|
type: "user.message",
|
|
563
626
|
title: "User Message",
|
|
564
627
|
status: isOptimisticUserMessageEvent(event) ? "running" : "done",
|
|
628
|
+
messageDeliveryState: isOptimisticUserMessageEvent(event) ? "sending" : undefined,
|
|
565
629
|
summary: event.text,
|
|
566
630
|
output: event.text,
|
|
567
631
|
};
|
|
@@ -711,6 +775,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
711
775
|
input: { reason: event.reason },
|
|
712
776
|
output: event.result,
|
|
713
777
|
error: event.errorMessage,
|
|
778
|
+
compactionStats: event.compactionStats,
|
|
714
779
|
stableKey: `compaction:${eventInstanceKey}`,
|
|
715
780
|
};
|
|
716
781
|
}
|
|
@@ -768,6 +833,7 @@ function findMatchingContentNode(byId, update) {
|
|
|
768
833
|
return [...byId.values()].find((candidate) => candidate.type === update.type && candidate.stableKey === update.stableKey);
|
|
769
834
|
}
|
|
770
835
|
function mergeAssistantMessageEvent(target, update) {
|
|
836
|
+
target.payloadRefs = { ...target.payloadRefs, ...update.payloadRefs };
|
|
771
837
|
target.status = update.status;
|
|
772
838
|
target.summary = update.summary ?? target.summary;
|
|
773
839
|
target.output = update.output ?? target.output;
|
|
@@ -1163,6 +1229,7 @@ function mergeCompactionEvent(target, update) {
|
|
|
1163
1229
|
target.input = update.input ?? target.input;
|
|
1164
1230
|
target.output = update.output ?? target.output;
|
|
1165
1231
|
target.error = update.error ?? target.error;
|
|
1232
|
+
target.compactionStats = update.compactionStats ?? target.compactionStats;
|
|
1166
1233
|
target.completedAt = update.completedAt ?? target.completedAt;
|
|
1167
1234
|
}
|
|
1168
1235
|
function hasVisibleText(value) {
|
|
@@ -710,6 +710,7 @@ function createUserMessageNode(piboSessionId, entry, entryIndex, timing, fallbac
|
|
|
710
710
|
startedAt: entry.createdAt,
|
|
711
711
|
summary: text,
|
|
712
712
|
output: text,
|
|
713
|
+
payloadRefs: historyContentPayloadRef(entry),
|
|
713
714
|
error: entry.error,
|
|
714
715
|
source,
|
|
715
716
|
stableKey: eventIdentity ?? historyEntryStableKey(entry),
|
|
@@ -902,6 +903,7 @@ function createAssistantMessageNode(input) {
|
|
|
902
903
|
completedAt: input.completedAt,
|
|
903
904
|
summary: input.text,
|
|
904
905
|
output: input.text,
|
|
906
|
+
payloadRefs: historyContentPayloadRef(input.entry),
|
|
905
907
|
error: input.error,
|
|
906
908
|
source,
|
|
907
909
|
stableKey: eventIdentity ? `assistant:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:response:${input.contentPartIndex}`,
|
|
@@ -909,6 +911,13 @@ function createAssistantMessageNode(input) {
|
|
|
909
911
|
children: input.children ?? [],
|
|
910
912
|
};
|
|
911
913
|
}
|
|
914
|
+
/** Preserve a stored full body when the bounded history page contains its preview. */
|
|
915
|
+
function historyContentPayloadRef(entry) {
|
|
916
|
+
const ref = entry.source === "product" ? entry.metadata?.tracePayloadRef : undefined;
|
|
917
|
+
if (!ref || typeof ref !== "object" || Array.isArray(ref) || typeof ref.ref !== "string" || typeof ref.byteLength !== "number" || typeof ref.preview !== "string")
|
|
918
|
+
return undefined;
|
|
919
|
+
return { output: ref };
|
|
920
|
+
}
|
|
912
921
|
function historyMessageParts(entry) {
|
|
913
922
|
return typeof entry.content === "string" ? [{ type: "text", text: entry.content }] : [...entry.content];
|
|
914
923
|
}
|
|
@@ -165,6 +165,7 @@ function makeStored(streamEvent, piboSessionId, type, payload, nextSequence, now
|
|
|
165
165
|
streamId: typeof streamEvent.streamId === "number" ? streamEvent.streamId : undefined,
|
|
166
166
|
streamFrameIndex,
|
|
167
167
|
traceSource: "live",
|
|
168
|
+
storedPayloadRef: streamEvent.storedPayloadRef,
|
|
168
169
|
eventId: typeof positionedPayload.eventId === "string" ? positionedPayload.eventId : undefined,
|
|
169
170
|
type,
|
|
170
171
|
createdAt: streamEventCreatedAt(streamEvent) ?? now(),
|
|
@@ -64,6 +64,7 @@ function traceNodeShallowEqual(left, right) {
|
|
|
64
64
|
left.toolMetrics?.inputTokens === right.toolMetrics?.inputTokens &&
|
|
65
65
|
left.toolMetrics?.outputTokens === right.toolMetrics?.outputTokens &&
|
|
66
66
|
left.toolMetrics?.tokenBasis === right.toolMetrics?.tokenBasis &&
|
|
67
|
+
modelInferenceRecordsEqual(left.modelInferences, right.modelInferences) &&
|
|
67
68
|
left.summary === right.summary &&
|
|
68
69
|
left.input === right.input &&
|
|
69
70
|
left.output === right.output &&
|
|
@@ -73,6 +74,24 @@ function traceNodeShallowEqual(left, right) {
|
|
|
73
74
|
left.stableKey === right.stableKey &&
|
|
74
75
|
traceOrderKeyEqual(left.orderKey, right.orderKey));
|
|
75
76
|
}
|
|
77
|
+
function modelInferenceRecordsEqual(left, right) {
|
|
78
|
+
if (left === right)
|
|
79
|
+
return true;
|
|
80
|
+
if (!left || !right || left.length !== right.length)
|
|
81
|
+
return false;
|
|
82
|
+
return left.every((item, index) => {
|
|
83
|
+
const other = right[index];
|
|
84
|
+
return item.id === other?.id
|
|
85
|
+
&& item.completedAt === other.completedAt
|
|
86
|
+
&& item.metrics.inputTokens === other.metrics.inputTokens
|
|
87
|
+
&& item.metrics.outputTokens === other.metrics.outputTokens
|
|
88
|
+
&& item.metrics.cacheReadTokens === other.metrics.cacheReadTokens
|
|
89
|
+
&& item.metrics.cacheWriteTokens === other.metrics.cacheWriteTokens
|
|
90
|
+
&& item.metrics.reasoningTokens === other.metrics.reasoningTokens
|
|
91
|
+
&& item.metrics.totalTokens === other.metrics.totalTokens
|
|
92
|
+
&& item.metrics.costUsd === other.metrics.costUsd;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
76
95
|
function traceOrderKeyEqual(left, right) {
|
|
77
96
|
if (left === right)
|
|
78
97
|
return true;
|