@pasko70/pibo 1.16.5 → 2.0.0
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/auth.js +28 -0
- package/dist/agent-runtime/capabilities.js +221 -0
- package/dist/agent-runtime/context-build.js +313 -0
- package/dist/agent-runtime/contract.js +45 -0
- package/dist/agent-runtime/errors.js +57 -0
- package/dist/agent-runtime/events.js +1 -0
- package/dist/agent-runtime/history.js +1 -0
- package/dist/agent-runtime/profile-validation.js +109 -0
- package/dist/agent-runtime/registry.js +608 -0
- package/dist/agent-runtime/resource-files.js +87 -0
- package/dist/agent-runtime/resource-service.js +635 -0
- package/dist/agent-runtime/resources.js +1 -0
- package/dist/agent-runtime/routed-session.js +931 -0
- package/dist/agent-runtime/testing/contract.js +51 -0
- package/dist/agent-runtime/testing/fake-adapter.js +192 -0
- package/dist/agent-runtime/types.js +3 -0
- package/dist/agent-runtimes/codex-native/adapter.js +905 -0
- package/dist/agent-runtimes/codex-native/auth.js +476 -0
- package/dist/agent-runtimes/codex-native/client.js +734 -0
- package/dist/agent-runtimes/codex-native/config.js +152 -0
- package/dist/agent-runtimes/codex-native/history.js +283 -0
- package/dist/agent-runtimes/codex-native/mcp-stdio-launcher.js +89 -0
- package/dist/agent-runtimes/codex-native/models.js +581 -0
- package/dist/agent-runtimes/codex-native/process.js +459 -0
- package/dist/agent-runtimes/codex-native/protocol-types.js +1 -0
- package/dist/agent-runtimes/codex-native/protocol-version.js +7 -0
- package/dist/agent-runtimes/codex-native/redaction.js +31 -0
- package/dist/agent-runtimes/codex-native/requests.js +378 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +445 -0
- package/dist/agent-runtimes/codex-native/thread.js +388 -0
- package/dist/agent-runtimes/codex-native/turn.js +849 -0
- package/dist/agent-runtimes/pi/adapter.js +653 -0
- package/dist/agent-runtimes/pi/auth.js +455 -0
- package/dist/agent-runtimes/pi/history.js +640 -0
- package/dist/agent-runtimes/pi/model-catalog.js +87 -0
- package/dist/agent-runtimes/pi/routed-session.js +1464 -0
- package/dist/agent-runtimes/pi/runtime.js +534 -0
- package/dist/agent-runtimes/pi/tool-compiler.js +69 -0
- package/dist/apps/chat/agent-profiles.js +40 -23
- package/dist/apps/chat/agent-store.js +111 -64
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-request-normalizers.js +47 -0
- package/dist/apps/chat/data/chat-data-mappers.js +25 -9
- package/dist/apps/chat/data/history-query-service.js +108 -0
- package/dist/apps/chat/data/session-query-service.js +24 -3
- package/dist/apps/chat/data/timeline-query-service.js +3 -3
- package/dist/apps/chat/model-catalog.js +1 -55
- package/dist/apps/chat/provider-auth-actions.js +171 -23
- package/dist/apps/chat/stream.js +12 -0
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/trace.js +126 -415
- package/dist/apps/chat/web-app.js +582 -157
- package/dist/apps/chat-ui/assets/{dist-QXwINWYY.js → dist-9v2ZXT6V.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CvZNkkOi.js → dist-B1LVAytl.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-_SP-MjLq.js → dist-C6LPDFXU.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bad4Yz4P.js → dist-DVtXxfHc.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-xy-zomwl.js → dist-DW4Qrfbd.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DI4i80O2.js → dist-DmbsHdMC.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D17yAUqp.js → dist-DyvwuSFO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bc3Vbgjh.js → dist-I26VB0QO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BzqH7w58.js → dist-RtHbJRPV.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CMDJ-WZm.js → dist-sZgJkWZb.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BPbNvcZe.css +1 -0
- package/dist/apps/chat-ui/assets/index-CIOf2uL2.js +237 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-1ivRyjFs.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.0.0.vsix +0 -0
- package/dist/auth/login-actions.js +5 -251
- package/dist/cli-session/localSessionSource.js +8 -1
- package/dist/core/context-build.js +124 -20
- package/dist/core/default-profile.js +8 -0
- package/dist/core/profiles.js +30 -3
- package/dist/core/routed-session.js +5 -1446
- package/dist/core/runtime.js +5 -561
- package/dist/core/session-router.js +637 -83
- package/dist/data/cli.js +20 -3
- package/dist/data/schema.js +104 -1
- package/dist/data/session-store.js +24 -1
- package/dist/debug/events.js +25 -10
- package/dist/debug/failures.js +23 -3
- package/dist/debug/index.js +24 -6
- package/dist/debug/messages.js +87 -7
- package/dist/debug/persisted-payloads.js +31 -0
- package/dist/debug/pty.js +7 -6
- package/dist/debug/runtime-binding.js +29 -0
- package/dist/debug/session.js +137 -0
- package/dist/debug/summary.js +12 -0
- package/dist/debug/telemetry.js +28 -0
- package/dist/debug/tools.js +12 -2
- package/dist/debug/trace.js +154 -76
- package/dist/gateway/server.js +87 -2
- package/dist/gateway/tool.js +5 -5
- package/dist/index.js +21 -2
- package/dist/loops/tools.js +13 -12
- package/dist/mcp/agent-context.js +13 -4
- package/dist/mcp/client.js +10 -6
- package/dist/mcp/config.js +5 -2
- package/dist/mcp/runtime-session.js +166 -0
- package/dist/plugins/builtin.js +191 -53
- package/dist/plugins/codex-native.js +29 -0
- package/dist/plugins/registry.js +66 -2
- package/dist/runs/tools.js +26 -25
- package/dist/session-ui/terminalRows.js +2 -2
- package/dist/sessions/pibo-data-store.js +125 -16
- package/dist/sessions/runtime-binding.js +107 -0
- package/dist/sessions/sqlite-store.js +239 -18
- package/dist/sessions/store.js +86 -7
- package/dist/shared/trace-engine.js +18 -9
- package/dist/shared/trace-event-projection.js +29 -11
- package/dist/shared/{trace-transcript.js → trace-history.js} +193 -195
- package/dist/shared/trace-order.js +9 -4
- package/dist/shared/trace-page-merge.js +1 -1
- package/dist/signals/projector.js +20 -2
- package/dist/signals/registry.js +11 -0
- package/dist/subagents/tool.js +8 -6
- package/dist/tools/codex-browser.js +20 -19
- package/dist/tools/codex-compat.js +10 -9
- package/dist/tools/codex-image-generation.js +8 -6
- package/dist/tools/contract.js +45 -0
- package/dist/tools/credential-registry.js +182 -0
- package/dist/tools/mcp-bridge.js +511 -0
- package/dist/tools/payload-writer.js +17 -0
- package/dist/tools/runtime/tool.js +10 -9
- package/dist/tools/schema.js +9 -0
- package/dist/tools/session-service.js +193 -0
- package/dist/tools/session-tool-set.js +85 -0
- package/dist/web-annotations/tools.js +17 -16
- package/docs/README.md +11 -0
- package/package.json +3 -2
- package/skills/builtin/pibo-agent-runtime-adapter/SKILL.md +183 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/README.md +10 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/evals.json +45 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/orion-full-harness.md +60 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/relay-partial-harness.md +52 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/capabilities-and-designer.md +194 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/history-debug-and-security.md +213 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/interfaces-and-registration.md +214 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/lifecycle-bindings-and-events.md +193 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/portable-delivery-and-native-behavior.md +226 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/testing-migration-and-validation.md +256 -0
- package/dist/apps/chat-ui/assets/index-C45B0rw7.js +0 -237
- package/dist/apps/chat-ui/assets/index-E0RmuCkY.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-D-tSsXLG.js +0 -41
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.16.5.vsix +0 -0
|
@@ -1,43 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { historyTraceOrder } from "./trace-order.js";
|
|
2
2
|
import { attachAsyncAgentRunNode, reconcileAsyncAgentRunStatuses } from "./trace-async-agent-runs.js";
|
|
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
|
-
import { assistantMessageNodeId, thinkingNodeId } from "./trace-event-projection.js";
|
|
7
|
-
export function
|
|
8
|
-
if (sessionStatus !== "running" ||
|
|
9
|
-
return entries;
|
|
10
|
-
const userTimingAssignments =
|
|
6
|
+
import { assistantMessageNodeId, messageTurnNodeId, thinkingNodeId } from "./trace-event-projection.js";
|
|
7
|
+
export function projectHistoryEntries(entries, sessionStatus, openHistoryEventIds, turnTimings = []) {
|
|
8
|
+
if (sessionStatus !== "running" || openHistoryEventIds.size === 0)
|
|
9
|
+
return [...entries];
|
|
10
|
+
const userTimingAssignments = assignHistoryUserTimings(entries, turnTimings);
|
|
11
11
|
let lastUserMessageIndex = -1;
|
|
12
12
|
let lastUserEventId;
|
|
13
13
|
for (let index = 0; index < entries.length; index += 1) {
|
|
14
14
|
const entry = entries[index];
|
|
15
|
-
if (entry.type !== "message" ||
|
|
15
|
+
if (entry.type !== "message" || entry.role !== "user")
|
|
16
16
|
continue;
|
|
17
17
|
lastUserMessageIndex = index;
|
|
18
|
-
lastUserEventId =
|
|
19
|
-
? entry.
|
|
18
|
+
lastUserEventId = entry.turnId && openHistoryEventIds.has(entry.turnId)
|
|
19
|
+
? entry.turnId
|
|
20
20
|
: userTimingAssignments.get(index)?.eventId;
|
|
21
21
|
}
|
|
22
|
-
return lastUserMessageIndex !== -1 && lastUserEventId &&
|
|
22
|
+
return lastUserMessageIndex !== -1 && lastUserEventId && openHistoryEventIds.has(lastUserEventId)
|
|
23
23
|
? entries.slice(0, lastUserMessageIndex)
|
|
24
|
-
: entries;
|
|
24
|
+
: [...entries];
|
|
25
25
|
}
|
|
26
|
-
export function
|
|
26
|
+
export function traceNodesFromHistoryEntries(piboSessionId, entries, turnTimings = []) {
|
|
27
27
|
const nodes = [];
|
|
28
|
-
const turnTimingAssignments =
|
|
29
|
-
const userTimingAssignments =
|
|
28
|
+
const turnTimingAssignments = assignHistoryTurnTimings(entries, turnTimings);
|
|
29
|
+
const userTimingAssignments = assignHistoryUserTimings(entries, turnTimings);
|
|
30
30
|
let assistantTurnIndex = 0;
|
|
31
31
|
for (let index = 0; index < entries.length; index += 1) {
|
|
32
32
|
const entry = entries[index];
|
|
33
33
|
if (entry.type === "message") {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
nodes.push(createUserMessageNode(piboSessionId, entry, messageContent(entry), index, userTimingAssignments.get(index)));
|
|
34
|
+
if (entry.role === "user") {
|
|
35
|
+
nodes.push(createUserMessageNode(piboSessionId, entry, index, userTimingAssignments.get(index)));
|
|
37
36
|
}
|
|
38
|
-
else if (role === "assistant" || role === "
|
|
37
|
+
else if (entry.role === "assistant" || entry.role === "tool") {
|
|
39
38
|
const turn = collectAssistantTurn(entries, index);
|
|
40
|
-
const hasAssistant = turn.entries.some(({ entry: turnEntry }) =>
|
|
39
|
+
const hasAssistant = turn.entries.some(({ entry: turnEntry }) => turnEntry.role === "assistant");
|
|
41
40
|
const timing = hasAssistant ? turnTimingAssignments[assistantTurnIndex] : undefined;
|
|
42
41
|
nodes.push(...createAssistantTurnNodes(piboSessionId, turn.entries, timing));
|
|
43
42
|
if (hasAssistant)
|
|
@@ -46,18 +45,20 @@ export function traceNodesFromEntries(piboSessionId, entries, turnTimings = [])
|
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
else if (entry.type === "session_info" && entry.name) {
|
|
48
|
+
const source = traceSource(entry.source);
|
|
49
|
+
const entryIndex = historyIndex(entry, index);
|
|
49
50
|
nodes.push({
|
|
50
|
-
id:
|
|
51
|
-
entryId: entry.
|
|
51
|
+
id: historyEntryNodeId(entry),
|
|
52
|
+
entryId: entry.nativeEntryId,
|
|
52
53
|
piboSessionId,
|
|
53
54
|
type: "execution.command",
|
|
54
55
|
title: "Session Info",
|
|
55
56
|
status: "done",
|
|
56
|
-
startedAt: entry.
|
|
57
|
+
startedAt: entry.createdAt,
|
|
57
58
|
output: { name: entry.name },
|
|
58
|
-
source
|
|
59
|
-
stableKey:
|
|
60
|
-
orderKey:
|
|
59
|
+
source,
|
|
60
|
+
stableKey: historyEntryStableKey(entry),
|
|
61
|
+
orderKey: historyTraceOrder(entryIndex, 0, "execution.command", source),
|
|
61
62
|
children: [],
|
|
62
63
|
});
|
|
63
64
|
}
|
|
@@ -66,67 +67,64 @@ export function traceNodesFromEntries(piboSessionId, entries, turnTimings = [])
|
|
|
66
67
|
sortTraceNodes(nodes);
|
|
67
68
|
return nodes;
|
|
68
69
|
}
|
|
69
|
-
function
|
|
70
|
-
return entry.message.role;
|
|
71
|
-
}
|
|
72
|
-
function messageContent(entry) {
|
|
73
|
-
return entry.message.content;
|
|
74
|
-
}
|
|
75
|
-
function messageParts(entry) {
|
|
76
|
-
const content = messageContent(entry);
|
|
77
|
-
if (typeof content === "string")
|
|
78
|
-
return [{ type: "text", text: content }];
|
|
79
|
-
return Array.isArray(content) ? content : [];
|
|
80
|
-
}
|
|
81
|
-
function collectTranscriptTurnTimingTargets(entries) {
|
|
70
|
+
function collectHistoryTurnTimingTargets(entries) {
|
|
82
71
|
const targets = [];
|
|
83
72
|
let latestUserText;
|
|
84
73
|
let latestUserEntryIndex;
|
|
74
|
+
let latestTurnId;
|
|
85
75
|
for (let index = 0; index < entries.length; index += 1) {
|
|
86
76
|
const entry = entries[index];
|
|
87
77
|
if (entry.type !== "message")
|
|
88
78
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
latestUserText = normalizedPrompt(extractText(messageContent(entry)));
|
|
79
|
+
if (entry.role === "user") {
|
|
80
|
+
latestUserText = normalizedPrompt(historyMessageText(entry));
|
|
92
81
|
latestUserEntryIndex = index;
|
|
82
|
+
latestTurnId = entry.turnId;
|
|
93
83
|
continue;
|
|
94
84
|
}
|
|
95
|
-
if (role !== "assistant" && role !== "
|
|
85
|
+
if (entry.role !== "assistant" && entry.role !== "tool")
|
|
96
86
|
continue;
|
|
97
87
|
const turn = collectAssistantTurn(entries, index);
|
|
98
|
-
const lastAssistant = [...turn.entries].reverse().find(({ entry: turnEntry }) =>
|
|
88
|
+
const lastAssistant = [...turn.entries].reverse().find(({ entry: turnEntry }) => turnEntry.role === "assistant");
|
|
99
89
|
if (lastAssistant) {
|
|
100
90
|
targets.push({
|
|
101
91
|
prompt: latestUserText,
|
|
102
92
|
userEntryIndex: latestUserEntryIndex,
|
|
103
|
-
assistantAt: parsedTimestamp(lastAssistant.entry.
|
|
93
|
+
assistantAt: parsedTimestamp(lastAssistant.entry.createdAt),
|
|
104
94
|
settled: isSettledAssistantEntry(lastAssistant.entry),
|
|
95
|
+
turnId: lastAssistant.entry.turnId ?? latestTurnId,
|
|
105
96
|
});
|
|
106
97
|
}
|
|
107
98
|
index = turn.nextIndex - 1;
|
|
108
99
|
}
|
|
109
100
|
return targets;
|
|
110
101
|
}
|
|
111
|
-
function
|
|
102
|
+
function assignHistoryTurnTimings(entries, turnTimings) {
|
|
112
103
|
const messageTurnTimings = turnTimings.filter((timing) => timing.userMessageType !== "message_steered");
|
|
113
|
-
const
|
|
114
|
-
const
|
|
104
|
+
const timingByEventId = new Map(messageTurnTimings.map((timing) => [timing.eventId, timing]));
|
|
105
|
+
const historyTurns = collectHistoryTurnTimingTargets(entries);
|
|
106
|
+
const assignments = historyTurns.map((turn) => turn.turnId ? timingByEventId.get(turn.turnId) : undefined);
|
|
115
107
|
let timingCursor = messageTurnTimings.length - 1;
|
|
116
|
-
for (let turnIndex =
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
for (let turnIndex = historyTurns.length - 1; turnIndex >= 0; turnIndex -= 1) {
|
|
109
|
+
if (assignments[turnIndex]) {
|
|
110
|
+
const assignedIndex = messageTurnTimings.indexOf(assignments[turnIndex]);
|
|
111
|
+
if (assignedIndex >= 0)
|
|
112
|
+
timingCursor = Math.min(timingCursor, assignedIndex - 1);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const historyTurn = historyTurns[turnIndex];
|
|
116
|
+
if (!historyTurn?.prompt)
|
|
119
117
|
continue;
|
|
120
118
|
let matchedIndex;
|
|
121
119
|
let matchedDistance = Number.POSITIVE_INFINITY;
|
|
122
120
|
for (let timingIndex = timingCursor; timingIndex >= 0; timingIndex -= 1) {
|
|
123
121
|
const timing = messageTurnTimings[timingIndex];
|
|
124
|
-
if (normalizedPrompt(timing?.userText) !==
|
|
122
|
+
if (normalizedPrompt(timing?.userText) !== historyTurn.prompt)
|
|
125
123
|
continue;
|
|
126
124
|
const completedAt = parsedTimestamp(timing?.completedAt);
|
|
127
|
-
const distance = completedAt === undefined ||
|
|
125
|
+
const distance = completedAt === undefined || historyTurn.assistantAt === undefined
|
|
128
126
|
? Number.POSITIVE_INFINITY
|
|
129
|
-
: Math.abs(completedAt -
|
|
127
|
+
: Math.abs(completedAt - historyTurn.assistantAt);
|
|
130
128
|
if (matchedIndex === undefined || distance < matchedDistance) {
|
|
131
129
|
matchedIndex = timingIndex;
|
|
132
130
|
matchedDistance = distance;
|
|
@@ -139,18 +137,19 @@ function assignTranscriptTurnTimings(entries, turnTimings) {
|
|
|
139
137
|
}
|
|
140
138
|
return assignments;
|
|
141
139
|
}
|
|
142
|
-
function
|
|
140
|
+
function assignHistoryUserTimings(entries, turnTimings) {
|
|
143
141
|
const users = [];
|
|
144
142
|
const userPositionByEntryIndex = new Map();
|
|
145
143
|
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
|
|
146
144
|
const entry = entries[entryIndex];
|
|
147
|
-
if (entry.type !== "message" ||
|
|
145
|
+
if (entry.type !== "message" || entry.role !== "user")
|
|
148
146
|
continue;
|
|
149
147
|
userPositionByEntryIndex.set(entryIndex, users.length);
|
|
150
148
|
users.push({
|
|
151
149
|
entryIndex,
|
|
152
|
-
entryId: entry.
|
|
153
|
-
|
|
150
|
+
entryId: entry.nativeEntryId,
|
|
151
|
+
turnId: entry.turnId,
|
|
152
|
+
prompt: normalizedPrompt(historyMessageText(entry)),
|
|
154
153
|
});
|
|
155
154
|
}
|
|
156
155
|
if (!users.length || !turnTimings.length)
|
|
@@ -159,20 +158,21 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
159
158
|
const anchorByUserPosition = new Map();
|
|
160
159
|
const assignedTimingIndexes = new Set();
|
|
161
160
|
for (let userPosition = 0; userPosition < users.length; userPosition += 1) {
|
|
162
|
-
const
|
|
161
|
+
const user = users[userPosition];
|
|
162
|
+
const timingIndex = timingIndexByEventId.get(user.turnId ?? user.entryId ?? "");
|
|
163
163
|
if (timingIndex === undefined || assignedTimingIndexes.has(timingIndex))
|
|
164
164
|
continue;
|
|
165
165
|
anchorByUserPosition.set(userPosition, timingIndex);
|
|
166
166
|
assignedTimingIndexes.add(timingIndex);
|
|
167
167
|
}
|
|
168
|
-
const
|
|
168
|
+
const historyTurns = collectHistoryTurnTimingTargets(entries);
|
|
169
169
|
let timingUpperBound = turnTimings.length - 1;
|
|
170
|
-
for (let turnIndex =
|
|
171
|
-
const
|
|
172
|
-
const userPosition =
|
|
170
|
+
for (let turnIndex = historyTurns.length - 1; turnIndex >= 0; turnIndex -= 1) {
|
|
171
|
+
const historyTurn = historyTurns[turnIndex];
|
|
172
|
+
const userPosition = historyTurn?.userEntryIndex === undefined
|
|
173
173
|
? undefined
|
|
174
|
-
: userPositionByEntryIndex.get(
|
|
175
|
-
if (!
|
|
174
|
+
: userPositionByEntryIndex.get(historyTurn.userEntryIndex);
|
|
175
|
+
if (!historyTurn?.prompt || userPosition === undefined)
|
|
176
176
|
continue;
|
|
177
177
|
const exactAnchor = anchorByUserPosition.get(userPosition);
|
|
178
178
|
if (exactAnchor !== undefined) {
|
|
@@ -184,7 +184,7 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
184
184
|
const timing = turnTimings[timingIndex];
|
|
185
185
|
if (assignedTimingIndexes.has(timingIndex) || timing.userMessageType !== "message_steered")
|
|
186
186
|
continue;
|
|
187
|
-
if (normalizedPrompt(timing.userText) !==
|
|
187
|
+
if (normalizedPrompt(timing.userText) !== historyTurn.prompt)
|
|
188
188
|
continue;
|
|
189
189
|
steeringTimingIndex = timingIndex;
|
|
190
190
|
break;
|
|
@@ -195,7 +195,7 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
195
195
|
timingUpperBound = steeringTimingIndex - 1;
|
|
196
196
|
continue;
|
|
197
197
|
}
|
|
198
|
-
if (!
|
|
198
|
+
if (!historyTurn.settled)
|
|
199
199
|
continue;
|
|
200
200
|
let matchedTimingIndex;
|
|
201
201
|
let matchedDistance = Number.POSITIVE_INFINITY;
|
|
@@ -203,12 +203,12 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
203
203
|
const timing = turnTimings[timingIndex];
|
|
204
204
|
if (assignedTimingIndexes.has(timingIndex) || timing.userMessageType === "message_steered" || !timing.completedAt)
|
|
205
205
|
continue;
|
|
206
|
-
if (normalizedPrompt(timing.userText) !==
|
|
206
|
+
if (normalizedPrompt(timing.userText) !== historyTurn.prompt)
|
|
207
207
|
continue;
|
|
208
208
|
const completedAt = parsedTimestamp(timing.completedAt);
|
|
209
|
-
const distance = completedAt === undefined ||
|
|
209
|
+
const distance = completedAt === undefined || historyTurn.assistantAt === undefined
|
|
210
210
|
? Number.POSITIVE_INFINITY
|
|
211
|
-
: Math.abs(completedAt -
|
|
211
|
+
: Math.abs(completedAt - historyTurn.assistantAt);
|
|
212
212
|
if (matchedTimingIndex === undefined || distance < matchedDistance) {
|
|
213
213
|
matchedTimingIndex = timingIndex;
|
|
214
214
|
matchedDistance = distance;
|
|
@@ -224,9 +224,8 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
224
224
|
const anchors = [...anchorByUserPosition.entries()]
|
|
225
225
|
.map(([userPosition, timingIndex]) => ({ userPosition, timingIndex }))
|
|
226
226
|
.sort((left, right) => left.userPosition - right.userPosition);
|
|
227
|
-
for (const anchor of anchors)
|
|
227
|
+
for (const anchor of anchors)
|
|
228
228
|
assignments.set(users[anchor.userPosition].entryIndex, turnTimings[anchor.timingIndex]);
|
|
229
|
-
}
|
|
230
229
|
const boundaries = [
|
|
231
230
|
{ userPosition: -1, timingIndex: -1 },
|
|
232
231
|
...anchors,
|
|
@@ -255,22 +254,6 @@ function assignTranscriptUserTimings(entries, turnTimings) {
|
|
|
255
254
|
}
|
|
256
255
|
return assignments;
|
|
257
256
|
}
|
|
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
|
-
}
|
|
264
|
-
function normalizedPrompt(value) {
|
|
265
|
-
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
266
|
-
return normalized || undefined;
|
|
267
|
-
}
|
|
268
|
-
function parsedTimestamp(value) {
|
|
269
|
-
if (!value)
|
|
270
|
-
return undefined;
|
|
271
|
-
const timestamp = new Date(value).getTime();
|
|
272
|
-
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
273
|
-
}
|
|
274
257
|
function collectAssistantTurn(entries, startIndex) {
|
|
275
258
|
const turnEntries = [];
|
|
276
259
|
let index = startIndex;
|
|
@@ -278,49 +261,53 @@ function collectAssistantTurn(entries, startIndex) {
|
|
|
278
261
|
const entry = entries[index];
|
|
279
262
|
if (entry.type !== "message")
|
|
280
263
|
break;
|
|
281
|
-
|
|
282
|
-
if (role !== "assistant" && role !== "toolResult")
|
|
264
|
+
if (entry.role !== "assistant" && entry.role !== "tool")
|
|
283
265
|
break;
|
|
284
266
|
turnEntries.push({ entry, index });
|
|
285
267
|
index += 1;
|
|
286
268
|
}
|
|
287
269
|
return { entries: turnEntries, nextIndex: index };
|
|
288
270
|
}
|
|
289
|
-
function createUserMessageNode(piboSessionId, entry,
|
|
290
|
-
const text =
|
|
271
|
+
function createUserMessageNode(piboSessionId, entry, entryIndex, timing) {
|
|
272
|
+
const text = historyMessageText(entry);
|
|
291
273
|
const notification = parseRunNotificationText(text);
|
|
274
|
+
const source = traceSource(entry.source);
|
|
275
|
+
const orderIndex = historyIndex(entry, entryIndex);
|
|
292
276
|
if (notification) {
|
|
293
277
|
return createRunNotificationNode({
|
|
294
|
-
id:
|
|
295
|
-
entryId: entry.
|
|
278
|
+
id: historyEntryNodeId(entry),
|
|
279
|
+
entryId: entry.nativeEntryId,
|
|
296
280
|
piboSessionId,
|
|
297
|
-
startedAt: entry.
|
|
298
|
-
orderKey:
|
|
299
|
-
source
|
|
300
|
-
stableKey:
|
|
281
|
+
startedAt: entry.createdAt,
|
|
282
|
+
orderKey: historyTraceOrder(orderIndex, 0, "yielded.run", source),
|
|
283
|
+
source,
|
|
284
|
+
stableKey: historyEntryStableKey(entry),
|
|
301
285
|
notification,
|
|
302
286
|
});
|
|
303
287
|
}
|
|
304
288
|
const userMessageType = timing?.userMessageType ?? "message_queued";
|
|
305
|
-
const
|
|
289
|
+
const eventId = entry.turnId ?? timing?.eventId;
|
|
290
|
+
const eventIdentity = eventId ? `event:${userMessageType}:${eventId}` : undefined;
|
|
306
291
|
return {
|
|
307
|
-
id: eventIdentity ??
|
|
308
|
-
entryId: entry.
|
|
292
|
+
id: eventIdentity ?? historyEntryNodeId(entry),
|
|
293
|
+
entryId: entry.nativeEntryId,
|
|
309
294
|
piboSessionId,
|
|
295
|
+
eventId,
|
|
310
296
|
type: "user.message",
|
|
311
297
|
title: "User Message",
|
|
312
|
-
status: "done",
|
|
313
|
-
startedAt: entry.
|
|
298
|
+
status: entry.status === "running" ? "running" : entry.status === "error" ? "error" : "done",
|
|
299
|
+
startedAt: entry.createdAt,
|
|
314
300
|
summary: text,
|
|
315
301
|
output: text,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
302
|
+
error: entry.error,
|
|
303
|
+
source,
|
|
304
|
+
stableKey: eventIdentity ?? historyEntryStableKey(entry),
|
|
305
|
+
orderKey: historyTraceOrder(orderIndex, 0, "user.message", source),
|
|
319
306
|
children: [],
|
|
320
307
|
};
|
|
321
308
|
}
|
|
322
309
|
function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
323
|
-
const firstAssistant = entries.find(({ entry }) =>
|
|
310
|
+
const firstAssistant = entries.find(({ entry }) => entry.role === "assistant");
|
|
324
311
|
if (!firstAssistant)
|
|
325
312
|
return [];
|
|
326
313
|
const orderedNodes = [];
|
|
@@ -328,60 +315,59 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
328
315
|
let assistantPartOrdinal = 0;
|
|
329
316
|
let reasoningPartOrdinal = 0;
|
|
330
317
|
for (const { entry, index: entryIndex } of entries) {
|
|
331
|
-
if (
|
|
318
|
+
if (entry.role === "tool") {
|
|
332
319
|
mergePersistedToolResult(toolsByCallId, orderedNodes, entry, piboSessionId, entryIndex);
|
|
333
320
|
continue;
|
|
334
321
|
}
|
|
335
|
-
const responseStatus =
|
|
336
|
-
const responseError = messageError(entry.message);
|
|
322
|
+
const responseStatus = historyMessageStatus(entry);
|
|
337
323
|
let responseNode;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (
|
|
324
|
+
const parts = historyMessageParts(entry);
|
|
325
|
+
for (const [contentPartIndex, part] of parts.entries()) {
|
|
326
|
+
if (part.type === "reasoning" && hasVisibleText(part.text)) {
|
|
341
327
|
const thinkingIndex = timing?.reasoningIndices?.[reasoningPartOrdinal] ?? reasoningPartOrdinal;
|
|
342
328
|
orderedNodes.push(createReasoningNode({
|
|
343
329
|
piboSessionId,
|
|
344
330
|
entry,
|
|
345
331
|
entryIndex,
|
|
346
|
-
contentPartIndex
|
|
332
|
+
contentPartIndex,
|
|
347
333
|
thinkingIndex,
|
|
348
|
-
eventId: timing?.eventId,
|
|
349
|
-
thinking:
|
|
334
|
+
eventId: entry.turnId ?? timing?.eventId,
|
|
335
|
+
thinking: part.text,
|
|
350
336
|
}));
|
|
351
337
|
reasoningPartOrdinal += 1;
|
|
352
338
|
}
|
|
353
|
-
else if (
|
|
339
|
+
else if (part.type === "text" && part.text !== "") {
|
|
354
340
|
if (!responseNode) {
|
|
355
|
-
const assistantIndex = timing?.assistantIndices?.[assistantPartOrdinal] ?? assistantPartOrdinal;
|
|
341
|
+
const assistantIndex = entry.assistantIndex ?? timing?.assistantIndices?.[assistantPartOrdinal] ?? assistantPartOrdinal;
|
|
356
342
|
responseNode = createAssistantMessageNode({
|
|
357
343
|
piboSessionId,
|
|
358
344
|
entry,
|
|
359
345
|
entryIndex,
|
|
360
|
-
contentPartIndex
|
|
346
|
+
contentPartIndex,
|
|
361
347
|
assistantIndex,
|
|
362
|
-
eventId: timing?.eventId,
|
|
348
|
+
eventId: entry.turnId ?? timing?.eventId,
|
|
363
349
|
status: responseStatus,
|
|
364
|
-
text:
|
|
365
|
-
error:
|
|
350
|
+
text: part.text,
|
|
351
|
+
error: entry.error,
|
|
366
352
|
children: [],
|
|
367
|
-
startedAt: entry.
|
|
353
|
+
startedAt: entry.createdAt,
|
|
368
354
|
});
|
|
369
355
|
orderedNodes.push(responseNode);
|
|
370
356
|
}
|
|
371
357
|
else {
|
|
372
|
-
responseNode.summary = `${typeof responseNode.summary === "string" ? responseNode.summary : ""}${
|
|
373
|
-
responseNode.output = `${typeof responseNode.output === "string" ? responseNode.output : ""}${
|
|
358
|
+
responseNode.summary = `${typeof responseNode.summary === "string" ? responseNode.summary : ""}${part.text}`;
|
|
359
|
+
responseNode.output = `${typeof responseNode.output === "string" ? responseNode.output : ""}${part.text}`;
|
|
374
360
|
}
|
|
375
361
|
}
|
|
376
|
-
else if (
|
|
377
|
-
const toolNode = createToolCallNode(piboSessionId, entry, entryIndex,
|
|
362
|
+
else if (part.type === "tool_call") {
|
|
363
|
+
const toolNode = createToolCallNode(piboSessionId, entry, entryIndex, contentPartIndex, part);
|
|
378
364
|
orderedNodes.push(toolNode);
|
|
379
|
-
toolsByCallId.set(
|
|
365
|
+
toolsByCallId.set(part.toolCallId, toolNode);
|
|
380
366
|
}
|
|
381
367
|
}
|
|
382
368
|
if (responseNode) {
|
|
383
369
|
responseNode.status = responseStatus;
|
|
384
|
-
responseNode.error =
|
|
370
|
+
responseNode.error = entry.error;
|
|
385
371
|
assistantPartOrdinal += 1;
|
|
386
372
|
}
|
|
387
373
|
}
|
|
@@ -394,44 +380,47 @@ function createAssistantTurnNodes(piboSessionId, entries, timing) {
|
|
|
394
380
|
}
|
|
395
381
|
function createReasoningNode(input) {
|
|
396
382
|
const eventIdentity = input.eventId ? `${input.eventId}:thinking:${input.thinkingIndex}` : undefined;
|
|
383
|
+
const source = traceSource(input.entry.source);
|
|
397
384
|
return {
|
|
398
|
-
id: eventIdentity ? thinkingNodeId(eventIdentity) :
|
|
399
|
-
entryId: input.entry.
|
|
385
|
+
id: eventIdentity ? thinkingNodeId(eventIdentity) : `${historyEntryNodeId(input.entry)}:thinking:${input.contentPartIndex}`,
|
|
386
|
+
entryId: input.entry.nativeEntryId,
|
|
400
387
|
piboSessionId: input.piboSessionId,
|
|
388
|
+
eventId: input.eventId,
|
|
389
|
+
parentId: input.entry.source === "product" && input.eventId ? messageTurnNodeId(input.eventId) : undefined,
|
|
401
390
|
type: "model.reasoning",
|
|
402
391
|
title: "Thinking",
|
|
403
392
|
status: "done",
|
|
404
|
-
startedAt: input.entry.
|
|
393
|
+
startedAt: input.entry.createdAt,
|
|
405
394
|
summary: input.thinking,
|
|
406
395
|
output: input.thinking,
|
|
407
|
-
source
|
|
408
|
-
stableKey: eventIdentity ? `reasoning:${eventIdentity}` :
|
|
409
|
-
orderKey:
|
|
396
|
+
source,
|
|
397
|
+
stableKey: eventIdentity ? `reasoning:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:thinking:${input.contentPartIndex}`,
|
|
398
|
+
orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "model.reasoning", source),
|
|
410
399
|
children: [],
|
|
411
400
|
};
|
|
412
401
|
}
|
|
413
402
|
function createToolCallNode(piboSessionId, entry, entryIndex, contentPartIndex, part) {
|
|
414
|
-
const
|
|
415
|
-
const toolCallId = typeof part.id === "string" ? part.id : undefined;
|
|
403
|
+
const source = traceSource(entry.source);
|
|
416
404
|
return {
|
|
417
|
-
id:
|
|
418
|
-
entryId: entry.
|
|
405
|
+
id: `tool:${part.toolCallId}`,
|
|
406
|
+
entryId: entry.nativeEntryId,
|
|
419
407
|
piboSessionId,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
408
|
+
eventId: entry.turnId,
|
|
409
|
+
parentId: entry.source === "product" && entry.turnId ? messageTurnNodeId(entry.turnId) : undefined,
|
|
410
|
+
toolCallId: part.toolCallId,
|
|
411
|
+
type: isSubagentToolName(part.toolName) ? "agent.delegation" : "tool.call",
|
|
412
|
+
title: part.toolName,
|
|
423
413
|
status: "done",
|
|
424
|
-
startedAt: entry.
|
|
425
|
-
input: part.
|
|
426
|
-
source
|
|
427
|
-
stableKey:
|
|
428
|
-
orderKey:
|
|
414
|
+
startedAt: entry.createdAt,
|
|
415
|
+
input: part.input ?? {},
|
|
416
|
+
source,
|
|
417
|
+
stableKey: `tool:${part.toolCallId}`,
|
|
418
|
+
orderKey: historyTraceOrder(historyIndex(entry, entryIndex), contentPartIndex, isSubagentToolName(part.toolName) ? "agent.delegation" : "tool.call", source),
|
|
429
419
|
children: [],
|
|
430
420
|
};
|
|
431
421
|
}
|
|
432
422
|
function mergePersistedToolResult(toolsByCallId, childNodes, entry, piboSessionId, entryIndex) {
|
|
433
|
-
const
|
|
434
|
-
const toolCallId = typeof message.toolCallId === "string" ? message.toolCallId : undefined;
|
|
423
|
+
const toolCallId = entry.toolCallId;
|
|
435
424
|
if (!toolCallId)
|
|
436
425
|
return;
|
|
437
426
|
let toolNode = toolsByCallId.get(toolCallId);
|
|
@@ -440,87 +429,96 @@ function mergePersistedToolResult(toolsByCallId, childNodes, entry, piboSessionI
|
|
|
440
429
|
childNodes.push(toolNode);
|
|
441
430
|
toolsByCallId.set(toolCallId, toolNode);
|
|
442
431
|
}
|
|
443
|
-
toolNode.status =
|
|
444
|
-
toolNode.completedAt = entry.
|
|
445
|
-
toolNode.output =
|
|
446
|
-
toolNode.error =
|
|
447
|
-
attachAsyncAgentRunNode(toolNode, piboSessionId, entry.
|
|
432
|
+
toolNode.status = entry.isError === true || entry.status === "error" ? "error" : "done";
|
|
433
|
+
toolNode.completedAt = entry.createdAt;
|
|
434
|
+
toolNode.output = entry.result ?? { content: entry.content };
|
|
435
|
+
toolNode.error = toolNode.status === "error" ? stringifyPreview(toolNode.output) : undefined;
|
|
436
|
+
attachAsyncAgentRunNode(toolNode, piboSessionId, entry.createdAt);
|
|
448
437
|
}
|
|
449
438
|
function createMissingToolResultNode(piboSessionId, entry, entryIndex, toolCallId) {
|
|
450
|
-
const
|
|
439
|
+
const source = traceSource(entry.source);
|
|
451
440
|
return {
|
|
452
441
|
id: `tool:${toolCallId}`,
|
|
453
|
-
entryId: entry.
|
|
442
|
+
entryId: entry.nativeEntryId,
|
|
454
443
|
piboSessionId,
|
|
444
|
+
eventId: entry.turnId,
|
|
455
445
|
toolCallId,
|
|
456
446
|
type: "tool.result",
|
|
457
|
-
title:
|
|
447
|
+
title: entry.toolName ?? "Tool Result",
|
|
458
448
|
status: "done",
|
|
459
|
-
startedAt: entry.
|
|
460
|
-
source
|
|
449
|
+
startedAt: entry.createdAt,
|
|
450
|
+
source,
|
|
461
451
|
stableKey: `tool:${toolCallId}`,
|
|
462
|
-
orderKey:
|
|
452
|
+
orderKey: historyTraceOrder(historyIndex(entry, entryIndex), 0, "tool.result", source),
|
|
463
453
|
children: [],
|
|
464
454
|
};
|
|
465
455
|
}
|
|
466
|
-
function toolResultOutput(message) {
|
|
467
|
-
if (message.details === undefined)
|
|
468
|
-
return { content: message.content };
|
|
469
|
-
return { content: message.content, details: message.details };
|
|
470
|
-
}
|
|
471
456
|
function createAssistantMessageNode(input) {
|
|
472
457
|
const eventIdentity = input.eventId ? `${input.eventId}:assistant:${input.assistantIndex}` : undefined;
|
|
458
|
+
const source = traceSource(input.entry.source);
|
|
473
459
|
return {
|
|
474
|
-
id: eventIdentity ? assistantMessageNodeId(eventIdentity) :
|
|
475
|
-
entryId: input.entry.
|
|
460
|
+
id: eventIdentity ? assistantMessageNodeId(eventIdentity) : `${historyEntryNodeId(input.entry)}:response`,
|
|
461
|
+
entryId: input.entry.nativeEntryId,
|
|
476
462
|
piboSessionId: input.piboSessionId,
|
|
477
463
|
eventId: input.eventId,
|
|
464
|
+
parentId: input.entry.source === "product" && input.eventId ? messageTurnNodeId(input.eventId) : undefined,
|
|
478
465
|
type: "assistant.message",
|
|
479
466
|
title: "Agent Message",
|
|
480
467
|
status: input.status,
|
|
481
|
-
startedAt: input.startedAt ?? input.entry.
|
|
468
|
+
startedAt: input.startedAt ?? input.entry.createdAt,
|
|
482
469
|
completedAt: input.completedAt,
|
|
483
470
|
summary: input.text,
|
|
484
471
|
output: input.text,
|
|
485
472
|
error: input.error,
|
|
486
|
-
source
|
|
487
|
-
stableKey: eventIdentity ? `assistant:${eventIdentity}` :
|
|
488
|
-
orderKey:
|
|
473
|
+
source,
|
|
474
|
+
stableKey: eventIdentity ? `assistant:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:response:${input.contentPartIndex}`,
|
|
475
|
+
orderKey: historyTraceOrder(historyIndex(input.entry, input.entryIndex), input.contentPartIndex, "assistant.message", source),
|
|
489
476
|
children: input.children ?? [],
|
|
490
477
|
};
|
|
491
478
|
}
|
|
492
|
-
function
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
479
|
+
function historyMessageParts(entry) {
|
|
480
|
+
return typeof entry.content === "string" ? [{ type: "text", text: entry.content }] : [...entry.content];
|
|
481
|
+
}
|
|
482
|
+
function historyMessageText(entry) {
|
|
483
|
+
if (typeof entry.content === "string")
|
|
484
|
+
return entry.content;
|
|
485
|
+
return entry.content.map((part) => part.type === "text" ? part.text : "").join("");
|
|
486
|
+
}
|
|
487
|
+
function historyMessageStatus(entry) {
|
|
488
|
+
if (entry.status === "error" || entry.error)
|
|
489
|
+
return "error";
|
|
490
|
+
if (entry.status === "running")
|
|
491
|
+
return "running";
|
|
499
492
|
return "done";
|
|
500
493
|
}
|
|
501
|
-
function
|
|
502
|
-
|
|
494
|
+
function isSettledAssistantEntry(entry) {
|
|
495
|
+
return entry.status !== "running" && entry.metadata?.stopReason !== "toolUse" && entry.metadata?.stopReason !== "tool_use";
|
|
496
|
+
}
|
|
497
|
+
function historyEntryNodeId(entry) {
|
|
498
|
+
return entry.source === "native" && entry.nativeEntryId ? `entry:${entry.nativeEntryId}` : `history:${entry.id}`;
|
|
499
|
+
}
|
|
500
|
+
function historyEntryStableKey(entry) {
|
|
501
|
+
return historyEntryNodeId(entry);
|
|
502
|
+
}
|
|
503
|
+
function historyIndex(entry, fallback) {
|
|
504
|
+
return entry.sequence ?? fallback;
|
|
505
|
+
}
|
|
506
|
+
function traceSource(source) {
|
|
507
|
+
return source === "native" ? "transcript" : "product-history";
|
|
508
|
+
}
|
|
509
|
+
function normalizedPrompt(value) {
|
|
510
|
+
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
511
|
+
return normalized || undefined;
|
|
512
|
+
}
|
|
513
|
+
function parsedTimestamp(value) {
|
|
514
|
+
if (!value)
|
|
503
515
|
return undefined;
|
|
504
|
-
const
|
|
505
|
-
return
|
|
516
|
+
const timestamp = new Date(value).getTime();
|
|
517
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
506
518
|
}
|
|
507
519
|
function hasVisibleText(value) {
|
|
508
520
|
return typeof value === "string" && value.trim().length > 0;
|
|
509
521
|
}
|
|
510
|
-
function extractText(content) {
|
|
511
|
-
if (typeof content === "string")
|
|
512
|
-
return content;
|
|
513
|
-
if (!Array.isArray(content))
|
|
514
|
-
return "";
|
|
515
|
-
return content
|
|
516
|
-
.map((part) => {
|
|
517
|
-
const typed = part;
|
|
518
|
-
if (typed.type === "text" && typeof typed.text === "string")
|
|
519
|
-
return typed.text;
|
|
520
|
-
return "";
|
|
521
|
-
})
|
|
522
|
-
.join("");
|
|
523
|
-
}
|
|
524
522
|
function stringifyPreview(value) {
|
|
525
523
|
if (typeof value === "string")
|
|
526
524
|
return value;
|