@ekairos/events 1.22.67-beta.development.0 → 1.22.69-beta.development.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/react.context-event-parts.d.ts +18 -0
- package/dist/react.context-event-parts.js +509 -0
- package/dist/react.d.ts +7 -42
- package/dist/react.js +4 -87
- package/dist/react.step-stream.d.ts +39 -0
- package/dist/react.step-stream.js +581 -0
- package/dist/react.types.d.ts +121 -0
- package/dist/react.types.js +2 -0
- package/dist/react.use-context.d.ts +7 -0
- package/dist/react.use-context.js +867 -0
- package/package.json +3 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ContextEventForUI, ContextStepRuntime } from "./react.types";
|
|
2
|
+
type StepStreamChunk = Record<string, unknown>;
|
|
3
|
+
export type PersistedContextTree = {
|
|
4
|
+
persistedEvents: ContextEventForUI[];
|
|
5
|
+
persistedExecutions: Array<Record<string, unknown>>;
|
|
6
|
+
filteredSteps: Array<Record<string, unknown>>;
|
|
7
|
+
persistedPartsByStep: Map<string, Array<Record<string, unknown>>>;
|
|
8
|
+
reactionEventIdByExecutionId: Map<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export declare function extractPersistedContextTree(persistedContext: Record<string, unknown> | null): PersistedContextTree;
|
|
11
|
+
export declare function buildContextStepViews(params: {
|
|
12
|
+
filteredSteps: Array<Record<string, unknown>>;
|
|
13
|
+
persistedPartsByStep: Map<string, Array<Record<string, unknown>>>;
|
|
14
|
+
}): ContextStepRuntime[];
|
|
15
|
+
export declare function buildLiveEventFromStepChunks(params: {
|
|
16
|
+
eventId: string;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
chunks: StepStreamChunk[];
|
|
19
|
+
}): ContextEventForUI;
|
|
20
|
+
export declare function consumePersistedContextStepStream(params: {
|
|
21
|
+
db: any;
|
|
22
|
+
signal: AbortSignal;
|
|
23
|
+
clientId?: string | null;
|
|
24
|
+
streamId?: string | null;
|
|
25
|
+
byteOffset?: number;
|
|
26
|
+
onByteOffset?: (byteOffset: number) => void;
|
|
27
|
+
onChunk?: (chunk: StepStreamChunk, info: {
|
|
28
|
+
parsedByteOffset: number;
|
|
29
|
+
streamByteOffset: number;
|
|
30
|
+
rawLine: string;
|
|
31
|
+
}) => Promise<void> | void;
|
|
32
|
+
onDone?: () => Promise<void> | void;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
export declare function buildEventStepsIndex(params: {
|
|
35
|
+
executions: Array<Record<string, unknown>>;
|
|
36
|
+
steps: ContextStepRuntime[];
|
|
37
|
+
}): Map<string, ContextStepRuntime[]>;
|
|
38
|
+
export declare function isUserEvent(event: ContextEventForUI | null | undefined): boolean;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Allow as PartialJsonAllow, parse as parsePartialJson } from "partial-json";
|
|
3
|
+
import { ASSISTANT_MESSAGE_TYPE, INPUT_TEXT_ITEM_TYPE } from "./react.types";
|
|
4
|
+
function parseContextStepStreamChunk(value) {
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
const parsed = JSON.parse(value);
|
|
7
|
+
return parsed && typeof parsed === "object"
|
|
8
|
+
? parsed
|
|
9
|
+
: {};
|
|
10
|
+
}
|
|
11
|
+
return value && typeof value === "object" ? value : {};
|
|
12
|
+
}
|
|
13
|
+
function asRecord(value) {
|
|
14
|
+
return value && typeof value === "object" ? value : {};
|
|
15
|
+
}
|
|
16
|
+
function optionalRecord(value) {
|
|
17
|
+
return value && typeof value === "object" ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
function firstLinkedRecord(value) {
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
return asRecord(value[0]);
|
|
22
|
+
}
|
|
23
|
+
return asRecord(value);
|
|
24
|
+
}
|
|
25
|
+
function linkedId(value) {
|
|
26
|
+
return asString(firstLinkedRecord(value).id);
|
|
27
|
+
}
|
|
28
|
+
function asString(value) {
|
|
29
|
+
if (typeof value === "string")
|
|
30
|
+
return value;
|
|
31
|
+
if (value === null || value === undefined)
|
|
32
|
+
return "";
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
function asFiniteNumber(value) {
|
|
36
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
37
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
38
|
+
}
|
|
39
|
+
function asBoolean(value) {
|
|
40
|
+
if (typeof value === "boolean")
|
|
41
|
+
return value;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function resolveInstantStreamsApi(db) {
|
|
45
|
+
return db?.streams ?? db?.core?.streams ?? db?._core?.streams ?? null;
|
|
46
|
+
}
|
|
47
|
+
function decodeStreamChunkValue(value) {
|
|
48
|
+
if (typeof value === "string")
|
|
49
|
+
return value;
|
|
50
|
+
if (value instanceof Uint8Array)
|
|
51
|
+
return new TextDecoder().decode(value);
|
|
52
|
+
if (value instanceof ArrayBuffer)
|
|
53
|
+
return new TextDecoder().decode(value);
|
|
54
|
+
if (ArrayBuffer.isView(value)) {
|
|
55
|
+
return new TextDecoder().decode(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
56
|
+
}
|
|
57
|
+
return String(value ?? "");
|
|
58
|
+
}
|
|
59
|
+
function parseActionInputText(value) {
|
|
60
|
+
if (!value)
|
|
61
|
+
return undefined;
|
|
62
|
+
try {
|
|
63
|
+
return parsePartialJson(value, PartialJsonAllow.OBJ |
|
|
64
|
+
PartialJsonAllow.ARR |
|
|
65
|
+
PartialJsonAllow.STR |
|
|
66
|
+
PartialJsonAllow.NUM |
|
|
67
|
+
PartialJsonAllow.BOOL |
|
|
68
|
+
PartialJsonAllow.NULL);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function readActionInputDelta(params) {
|
|
75
|
+
return (asString(params.chunk.text) ||
|
|
76
|
+
asString(params.data.text) ||
|
|
77
|
+
asString(params.data.delta) ||
|
|
78
|
+
asString(params.data.inputTextDelta) ||
|
|
79
|
+
asString(params.raw.inputTextDelta) ||
|
|
80
|
+
asString(params.raw.delta) ||
|
|
81
|
+
asString(params.rawParams.delta));
|
|
82
|
+
}
|
|
83
|
+
function readActionOutputDelta(params) {
|
|
84
|
+
return (asString(params.chunk.text) ||
|
|
85
|
+
asString(params.data.text) ||
|
|
86
|
+
asString(params.data.delta) ||
|
|
87
|
+
asString(params.data.outputTextDelta) ||
|
|
88
|
+
asString(params.raw.outputTextDelta) ||
|
|
89
|
+
asString(params.raw.delta) ||
|
|
90
|
+
asString(params.rawParams.delta));
|
|
91
|
+
}
|
|
92
|
+
function formatDate(value) {
|
|
93
|
+
if (value instanceof Date)
|
|
94
|
+
return value.toISOString();
|
|
95
|
+
if (typeof value === "string")
|
|
96
|
+
return value;
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function sortEvents(events) {
|
|
100
|
+
const parseTs = (raw) => {
|
|
101
|
+
if (raw instanceof Date)
|
|
102
|
+
return raw.getTime();
|
|
103
|
+
if (typeof raw === "number" && Number.isFinite(raw))
|
|
104
|
+
return raw;
|
|
105
|
+
if (typeof raw === "string") {
|
|
106
|
+
const ms = new Date(raw).getTime();
|
|
107
|
+
return Number.isFinite(ms) ? ms : 0;
|
|
108
|
+
}
|
|
109
|
+
return 0;
|
|
110
|
+
};
|
|
111
|
+
return events.slice().sort((a, b) => {
|
|
112
|
+
const aMs = parseTs(a.createdAt);
|
|
113
|
+
const bMs = parseTs(b.createdAt);
|
|
114
|
+
if (aMs !== bMs)
|
|
115
|
+
return aMs - bMs;
|
|
116
|
+
return String(a.id).localeCompare(String(b.id));
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function sanitizeStreamRecord(value) {
|
|
120
|
+
const record = firstLinkedRecord(value);
|
|
121
|
+
const entries = Object.entries(record).filter(([key]) => key !== "hashedReconnectToken");
|
|
122
|
+
return entries.length > 0 ? Object.fromEntries(entries) : null;
|
|
123
|
+
}
|
|
124
|
+
function readStreamInfo(value) {
|
|
125
|
+
const record = firstLinkedRecord(value);
|
|
126
|
+
if (Object.keys(record).length === 0)
|
|
127
|
+
return null;
|
|
128
|
+
return {
|
|
129
|
+
id: asString(record.id) || null,
|
|
130
|
+
clientId: asString(record.clientId) || null,
|
|
131
|
+
done: asBoolean(record.done),
|
|
132
|
+
size: asFiniteNumber(record.size),
|
|
133
|
+
machineId: asString(record.machineId) || null,
|
|
134
|
+
createdAt: formatDate(record.createdAt),
|
|
135
|
+
updatedAt: formatDate(record.updatedAt),
|
|
136
|
+
raw: sanitizeStreamRecord(value),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export function extractPersistedContextTree(persistedContext) {
|
|
140
|
+
const persistedExecutions = Array.isArray(persistedContext?.executions)
|
|
141
|
+
? persistedContext.executions
|
|
142
|
+
: [];
|
|
143
|
+
const contextItems = Array.isArray(persistedContext?.items)
|
|
144
|
+
? persistedContext.items
|
|
145
|
+
: [];
|
|
146
|
+
const executionItems = persistedExecutions.flatMap((execution) => Array.isArray(execution.items)
|
|
147
|
+
? execution.items
|
|
148
|
+
: []);
|
|
149
|
+
const persistedEventsById = new Map();
|
|
150
|
+
for (const event of [...contextItems, ...executionItems]) {
|
|
151
|
+
if (!event?.id)
|
|
152
|
+
continue;
|
|
153
|
+
persistedEventsById.set(String(event.id), event);
|
|
154
|
+
}
|
|
155
|
+
const persistedEvents = sortEvents([...persistedEventsById.values()]);
|
|
156
|
+
const persistedSteps = persistedExecutions.flatMap((execution) => {
|
|
157
|
+
const stepRows = Array.isArray(execution.steps)
|
|
158
|
+
? execution.steps
|
|
159
|
+
: [];
|
|
160
|
+
return stepRows.map((step) => ({
|
|
161
|
+
...step,
|
|
162
|
+
execution: step.execution && typeof step.execution === "object"
|
|
163
|
+
? step.execution
|
|
164
|
+
: { id: execution.id },
|
|
165
|
+
}));
|
|
166
|
+
});
|
|
167
|
+
const executionIds = new Set(persistedExecutions.map((row) => asString(row.id)).filter(Boolean));
|
|
168
|
+
const filteredSteps = persistedSteps.filter((row) => executionIds.has(asString(asRecord(row.execution).id)));
|
|
169
|
+
const filteredStepIds = new Set(filteredSteps.map((row) => asString(row.id)).filter(Boolean));
|
|
170
|
+
const persistedParts = filteredSteps.flatMap((step) => {
|
|
171
|
+
const stepRow = step;
|
|
172
|
+
const partRows = Array.isArray(stepRow.parts)
|
|
173
|
+
? stepRow.parts
|
|
174
|
+
: [];
|
|
175
|
+
return partRows.map((part) => ({
|
|
176
|
+
...part,
|
|
177
|
+
step: part.step && typeof part.step === "object"
|
|
178
|
+
? part.step
|
|
179
|
+
: { id: stepRow.id },
|
|
180
|
+
}));
|
|
181
|
+
});
|
|
182
|
+
const filteredParts = persistedParts.filter((row) => filteredStepIds.has(asString(asRecord(row.step).id)));
|
|
183
|
+
const persistedPartsByStep = new Map();
|
|
184
|
+
for (const row of filteredParts) {
|
|
185
|
+
const stepId = asString(asRecord(row.step).id);
|
|
186
|
+
if (!stepId)
|
|
187
|
+
continue;
|
|
188
|
+
const bucket = persistedPartsByStep.get(stepId) ?? [];
|
|
189
|
+
bucket.push(row);
|
|
190
|
+
persistedPartsByStep.set(stepId, bucket);
|
|
191
|
+
}
|
|
192
|
+
const reactionEventIdByExecutionId = new Map();
|
|
193
|
+
for (const execution of persistedExecutions) {
|
|
194
|
+
const executionId = asString(execution.id);
|
|
195
|
+
const reactionId = linkedId(execution.reaction);
|
|
196
|
+
if (!executionId || !reactionId)
|
|
197
|
+
continue;
|
|
198
|
+
reactionEventIdByExecutionId.set(executionId, reactionId);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
persistedEvents,
|
|
202
|
+
persistedExecutions,
|
|
203
|
+
filteredSteps,
|
|
204
|
+
persistedPartsByStep,
|
|
205
|
+
reactionEventIdByExecutionId,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export function buildContextStepViews(params) {
|
|
209
|
+
return params.filteredSteps
|
|
210
|
+
.map((row) => {
|
|
211
|
+
const stepId = asString(row.id);
|
|
212
|
+
const storedPartRows = params.persistedPartsByStep.get(stepId) ?? [];
|
|
213
|
+
const parts = storedPartRows
|
|
214
|
+
.map((partRow) => asRecord(partRow.part))
|
|
215
|
+
.filter((part) => Object.keys(part).length > 0);
|
|
216
|
+
const createdAt = formatDate(row.createdAt) || new Date().toISOString();
|
|
217
|
+
const status = asString(row.status) || "unknown";
|
|
218
|
+
return {
|
|
219
|
+
stepId,
|
|
220
|
+
executionId: linkedId(row.execution) || null,
|
|
221
|
+
createdAt,
|
|
222
|
+
updatedAt: formatDate(row.updatedAt),
|
|
223
|
+
status,
|
|
224
|
+
iteration: asFiniteNumber(row.iteration),
|
|
225
|
+
streamId: asString(row.streamId) || null,
|
|
226
|
+
streamClientId: asString(row.streamClientId) || null,
|
|
227
|
+
streamStartedAt: formatDate(row.streamStartedAt),
|
|
228
|
+
streamFinishedAt: formatDate(row.streamFinishedAt),
|
|
229
|
+
streamAbortReason: asString(row.streamAbortReason) || null,
|
|
230
|
+
stream: readStreamInfo(row.stream),
|
|
231
|
+
streamReader: null,
|
|
232
|
+
parts,
|
|
233
|
+
};
|
|
234
|
+
})
|
|
235
|
+
.sort((a, b) => {
|
|
236
|
+
const aCreatedAt = new Date(a.streamStartedAt || 0).getTime();
|
|
237
|
+
const bCreatedAt = new Date(b.streamStartedAt || 0).getTime();
|
|
238
|
+
if (aCreatedAt !== bCreatedAt)
|
|
239
|
+
return aCreatedAt - bCreatedAt;
|
|
240
|
+
return String(a.stepId).localeCompare(String(b.stepId));
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
export function buildLiveEventFromStepChunks(params) {
|
|
244
|
+
const textSegments = [];
|
|
245
|
+
const reasoningSegments = [];
|
|
246
|
+
const actionParts = new Map();
|
|
247
|
+
let completed = false;
|
|
248
|
+
let reasoningCompleted = false;
|
|
249
|
+
for (const rawChunk of params.chunks) {
|
|
250
|
+
const chunk = asRecord(rawChunk);
|
|
251
|
+
const sequence = asFiniteNumber(chunk.sequence) ?? 0;
|
|
252
|
+
const chunkType = asString(chunk.chunkType);
|
|
253
|
+
if (chunkType === "chunk.text_delta") {
|
|
254
|
+
const text = asString(chunk.text) ||
|
|
255
|
+
asString(asRecord(chunk.data).text) ||
|
|
256
|
+
asString(asRecord(chunk.data).delta) ||
|
|
257
|
+
asString(asRecord(chunk.raw).delta) ||
|
|
258
|
+
asString(asRecord(chunk.raw).textDelta);
|
|
259
|
+
if (text)
|
|
260
|
+
textSegments.push({ sequence, text });
|
|
261
|
+
}
|
|
262
|
+
if (chunkType === "chunk.reasoning_delta") {
|
|
263
|
+
const text = asString(chunk.text) ||
|
|
264
|
+
asString(asRecord(chunk.data).text) ||
|
|
265
|
+
asString(asRecord(chunk.data).delta) ||
|
|
266
|
+
asString(asRecord(chunk.raw).delta);
|
|
267
|
+
if (text)
|
|
268
|
+
reasoningSegments.push({ sequence, text });
|
|
269
|
+
}
|
|
270
|
+
if (chunkType === "chunk.reasoning_end") {
|
|
271
|
+
reasoningCompleted = true;
|
|
272
|
+
}
|
|
273
|
+
if (chunkType === "chunk.action_started" ||
|
|
274
|
+
chunkType === "chunk.action_input_delta" ||
|
|
275
|
+
chunkType === "chunk.action_completed" ||
|
|
276
|
+
chunkType === "chunk.action_failed") {
|
|
277
|
+
const data = asRecord(chunk.data);
|
|
278
|
+
const raw = asRecord(chunk.raw);
|
|
279
|
+
const rawParams = asRecord(raw.params);
|
|
280
|
+
const rawItem = asRecord(rawParams.item);
|
|
281
|
+
const actionRef = asString(chunk.actionRef) ||
|
|
282
|
+
asString(chunk.providerPartId) ||
|
|
283
|
+
asString(data.actionCallId) ||
|
|
284
|
+
asString(data.toolCallId) ||
|
|
285
|
+
asString(data.callId) ||
|
|
286
|
+
asString(data.itemId) ||
|
|
287
|
+
asString(data.id) ||
|
|
288
|
+
asString(rawParams.callId) ||
|
|
289
|
+
asString(rawParams.itemId) ||
|
|
290
|
+
asString(rawItem.id) ||
|
|
291
|
+
asString(raw.toolCallId) ||
|
|
292
|
+
asString(raw.id);
|
|
293
|
+
if (!actionRef)
|
|
294
|
+
continue;
|
|
295
|
+
const toolName = asString(data.actionName) ||
|
|
296
|
+
asString(data.toolName) ||
|
|
297
|
+
asString(rawParams.tool) ||
|
|
298
|
+
asString(rawItem.command ? "sandbox_run_command" : "") ||
|
|
299
|
+
asString(raw.actionName) ||
|
|
300
|
+
asString(raw.toolName) ||
|
|
301
|
+
asString(raw.name) ||
|
|
302
|
+
actionParts.get(actionRef)?.toolName ||
|
|
303
|
+
"reactorAction";
|
|
304
|
+
const previous = actionParts.get(actionRef);
|
|
305
|
+
const inputDeltaText = readActionInputDelta({
|
|
306
|
+
chunk,
|
|
307
|
+
data,
|
|
308
|
+
raw,
|
|
309
|
+
rawParams,
|
|
310
|
+
});
|
|
311
|
+
const outputDeltaText = readActionOutputDelta({
|
|
312
|
+
chunk,
|
|
313
|
+
data,
|
|
314
|
+
raw,
|
|
315
|
+
rawParams,
|
|
316
|
+
});
|
|
317
|
+
const nextInputDeltaText = chunkType === "chunk.action_input_delta"
|
|
318
|
+
? `${previous?.inputDeltaText ?? ""}${inputDeltaText}`
|
|
319
|
+
: previous?.inputDeltaText ?? "";
|
|
320
|
+
const parsedInputDelta = parseActionInputText(nextInputDeltaText);
|
|
321
|
+
const input = data.input ??
|
|
322
|
+
data.arguments ??
|
|
323
|
+
raw.input ??
|
|
324
|
+
raw.args ??
|
|
325
|
+
raw.arguments ??
|
|
326
|
+
rawParams.arguments ??
|
|
327
|
+
(nextInputDeltaText ? parsedInputDelta : undefined) ??
|
|
328
|
+
previous?.input;
|
|
329
|
+
const nextOutputDeltaText = chunkType === "chunk.action_completed" && outputDeltaText
|
|
330
|
+
? `${previous?.outputDeltaText ?? ""}${outputDeltaText}`
|
|
331
|
+
: previous?.outputDeltaText ?? "";
|
|
332
|
+
const output = data.output ??
|
|
333
|
+
data.result ??
|
|
334
|
+
rawParams.result ??
|
|
335
|
+
raw.output ??
|
|
336
|
+
raw.result ??
|
|
337
|
+
(nextOutputDeltaText
|
|
338
|
+
? {
|
|
339
|
+
text: nextOutputDeltaText,
|
|
340
|
+
}
|
|
341
|
+
: previous?.output);
|
|
342
|
+
actionParts.set(actionRef, {
|
|
343
|
+
startedSequence: previous?.startedSequence ??
|
|
344
|
+
(chunkType === "chunk.action_started" || chunkType === "chunk.action_input_delta"
|
|
345
|
+
? sequence
|
|
346
|
+
: undefined),
|
|
347
|
+
terminalSequence: chunkType === "chunk.action_completed" ||
|
|
348
|
+
chunkType === "chunk.action_failed"
|
|
349
|
+
? sequence
|
|
350
|
+
: previous?.terminalSequence,
|
|
351
|
+
toolName,
|
|
352
|
+
hasStarted: previous?.hasStarted ||
|
|
353
|
+
chunkType === "chunk.action_started" ||
|
|
354
|
+
chunkType === "chunk.action_input_delta",
|
|
355
|
+
inputDeltaText: nextInputDeltaText,
|
|
356
|
+
outputDeltaText: nextOutputDeltaText,
|
|
357
|
+
input: optionalRecord(input) ??
|
|
358
|
+
(input !== undefined
|
|
359
|
+
? input
|
|
360
|
+
: previous?.input),
|
|
361
|
+
output: optionalRecord(output) ??
|
|
362
|
+
(output !== undefined
|
|
363
|
+
? output
|
|
364
|
+
: previous?.output),
|
|
365
|
+
errorText: chunkType === "chunk.action_failed"
|
|
366
|
+
? asString(chunk.text) ||
|
|
367
|
+
asString(data.text) ||
|
|
368
|
+
asString(data.error) ||
|
|
369
|
+
asString(asRecord(data.error).message) ||
|
|
370
|
+
asString(rawParams.error) ||
|
|
371
|
+
asString(asRecord(rawParams.error).message) ||
|
|
372
|
+
asString(asRecord(raw.error).message) ||
|
|
373
|
+
undefined
|
|
374
|
+
: previous?.errorText,
|
|
375
|
+
terminalStatus: chunkType === "chunk.action_failed"
|
|
376
|
+
? "failed"
|
|
377
|
+
: chunkType === "chunk.action_completed"
|
|
378
|
+
? "completed"
|
|
379
|
+
: previous?.terminalStatus,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
if (chunkType === "chunk.finish") {
|
|
383
|
+
completed = true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const parts = [];
|
|
387
|
+
if (reasoningSegments.length > 0) {
|
|
388
|
+
parts.push({
|
|
389
|
+
sequence: reasoningSegments[reasoningSegments.length - 1].sequence,
|
|
390
|
+
part: {
|
|
391
|
+
type: "reasoning",
|
|
392
|
+
content: {
|
|
393
|
+
text: reasoningSegments.map((row) => row.text).join(""),
|
|
394
|
+
state: completed || reasoningCompleted ? "done" : "streaming",
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (textSegments.length > 0) {
|
|
400
|
+
parts.push({
|
|
401
|
+
sequence: textSegments[textSegments.length - 1].sequence,
|
|
402
|
+
part: {
|
|
403
|
+
type: "message",
|
|
404
|
+
content: {
|
|
405
|
+
text: textSegments.map((row) => row.text).join(""),
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
for (const [toolCallId, action] of actionParts) {
|
|
411
|
+
if (action.hasStarted) {
|
|
412
|
+
parts.push({
|
|
413
|
+
sequence: action.startedSequence ?? action.terminalSequence ?? 0,
|
|
414
|
+
part: {
|
|
415
|
+
type: "action",
|
|
416
|
+
content: {
|
|
417
|
+
status: "started",
|
|
418
|
+
actionName: action.toolName,
|
|
419
|
+
actionCallId: toolCallId,
|
|
420
|
+
input: action.input ?? {},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (action.terminalStatus === "completed") {
|
|
426
|
+
parts.push({
|
|
427
|
+
sequence: action.terminalSequence ?? action.startedSequence ?? 0,
|
|
428
|
+
part: {
|
|
429
|
+
type: "action",
|
|
430
|
+
content: {
|
|
431
|
+
status: "completed",
|
|
432
|
+
actionName: action.toolName,
|
|
433
|
+
actionCallId: toolCallId,
|
|
434
|
+
output: action.output ?? {},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (action.terminalStatus === "failed") {
|
|
440
|
+
parts.push({
|
|
441
|
+
sequence: action.terminalSequence ?? action.startedSequence ?? 0,
|
|
442
|
+
part: {
|
|
443
|
+
type: "action",
|
|
444
|
+
content: {
|
|
445
|
+
status: "failed",
|
|
446
|
+
actionName: action.toolName,
|
|
447
|
+
actionCallId: toolCallId,
|
|
448
|
+
error: {
|
|
449
|
+
message: action.errorText || "Action failed.",
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const sortedParts = parts
|
|
457
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
458
|
+
.map((entry) => entry.part);
|
|
459
|
+
return {
|
|
460
|
+
id: params.eventId,
|
|
461
|
+
type: ASSISTANT_MESSAGE_TYPE,
|
|
462
|
+
channel: "web",
|
|
463
|
+
createdAt: params.createdAt,
|
|
464
|
+
status: completed ? "completed" : "pending",
|
|
465
|
+
content: { parts: sortedParts },
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
export async function consumePersistedContextStepStream(params) {
|
|
469
|
+
const startOffset = Math.max(0, params.byteOffset ?? 0);
|
|
470
|
+
const streams = resolveInstantStreamsApi(params.db);
|
|
471
|
+
if (!streams?.createReadStream) {
|
|
472
|
+
throw new Error("InstantDB streams are not available on the provided database.");
|
|
473
|
+
}
|
|
474
|
+
const stream = streams.createReadStream(params.streamId
|
|
475
|
+
? {
|
|
476
|
+
streamId: params.streamId,
|
|
477
|
+
byteOffset: startOffset,
|
|
478
|
+
}
|
|
479
|
+
: {
|
|
480
|
+
clientId: params.clientId || undefined,
|
|
481
|
+
byteOffset: startOffset,
|
|
482
|
+
});
|
|
483
|
+
const reader = stream.getReader();
|
|
484
|
+
const encoder = new TextEncoder();
|
|
485
|
+
let nextByteOffset = startOffset;
|
|
486
|
+
let parsedByteOffset = startOffset;
|
|
487
|
+
let buffer = "";
|
|
488
|
+
let finished = false;
|
|
489
|
+
const handleAbort = () => {
|
|
490
|
+
void reader.cancel().catch(() => { });
|
|
491
|
+
};
|
|
492
|
+
params.signal.addEventListener("abort", handleAbort, { once: true });
|
|
493
|
+
try {
|
|
494
|
+
while (!params.signal.aborted) {
|
|
495
|
+
const { value, done } = await reader.read();
|
|
496
|
+
if (done) {
|
|
497
|
+
finished = !params.signal.aborted;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
const raw = decodeStreamChunkValue(value);
|
|
501
|
+
if (!raw)
|
|
502
|
+
continue;
|
|
503
|
+
nextByteOffset += encoder.encode(raw).length;
|
|
504
|
+
buffer += raw;
|
|
505
|
+
const lines = buffer.split("\n");
|
|
506
|
+
buffer = lines.pop() ?? "";
|
|
507
|
+
for (const line of lines) {
|
|
508
|
+
if (params.signal.aborted)
|
|
509
|
+
break;
|
|
510
|
+
const trimmed = line.trim();
|
|
511
|
+
if (!trimmed)
|
|
512
|
+
continue;
|
|
513
|
+
parsedByteOffset += encoder.encode(`${line}\n`).length;
|
|
514
|
+
params.onByteOffset?.(parsedByteOffset);
|
|
515
|
+
await params.onChunk?.(parseContextStepStreamChunk(trimmed), {
|
|
516
|
+
parsedByteOffset,
|
|
517
|
+
streamByteOffset: nextByteOffset,
|
|
518
|
+
rawLine: trimmed,
|
|
519
|
+
});
|
|
520
|
+
if (params.signal.aborted)
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (finished) {
|
|
525
|
+
const trailing = buffer.trim();
|
|
526
|
+
if (trailing) {
|
|
527
|
+
parsedByteOffset += encoder.encode(buffer).length;
|
|
528
|
+
params.onByteOffset?.(parsedByteOffset);
|
|
529
|
+
await params.onChunk?.(parseContextStepStreamChunk(trailing), {
|
|
530
|
+
parsedByteOffset,
|
|
531
|
+
streamByteOffset: nextByteOffset,
|
|
532
|
+
rawLine: trailing,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
await params.onDone?.();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
finally {
|
|
539
|
+
params.signal.removeEventListener("abort", handleAbort);
|
|
540
|
+
try {
|
|
541
|
+
await reader.cancel();
|
|
542
|
+
}
|
|
543
|
+
catch { }
|
|
544
|
+
reader.releaseLock();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
export function buildEventStepsIndex(params) {
|
|
548
|
+
const reactionEventIdByExecutionId = new Map();
|
|
549
|
+
for (const execution of params.executions) {
|
|
550
|
+
const executionId = asString(execution.id);
|
|
551
|
+
const reactionId = linkedId(execution.reaction);
|
|
552
|
+
if (!executionId || !reactionId)
|
|
553
|
+
continue;
|
|
554
|
+
reactionEventIdByExecutionId.set(executionId, reactionId);
|
|
555
|
+
}
|
|
556
|
+
const stepsByEventId = new Map();
|
|
557
|
+
for (const step of params.steps) {
|
|
558
|
+
if (!step.executionId)
|
|
559
|
+
continue;
|
|
560
|
+
const reactionId = reactionEventIdByExecutionId.get(step.executionId);
|
|
561
|
+
if (!reactionId)
|
|
562
|
+
continue;
|
|
563
|
+
const bucket = stepsByEventId.get(reactionId) ?? [];
|
|
564
|
+
bucket.push(step);
|
|
565
|
+
stepsByEventId.set(reactionId, bucket);
|
|
566
|
+
}
|
|
567
|
+
for (const [eventId, steps] of stepsByEventId) {
|
|
568
|
+
stepsByEventId.set(eventId, steps.slice().sort((a, b) => {
|
|
569
|
+
const aStarted = new Date(a.streamStartedAt || 0).getTime();
|
|
570
|
+
const bStarted = new Date(b.streamStartedAt || 0).getTime();
|
|
571
|
+
if (aStarted !== bStarted)
|
|
572
|
+
return aStarted - bStarted;
|
|
573
|
+
return String(a.stepId).localeCompare(String(b.stepId));
|
|
574
|
+
}));
|
|
575
|
+
}
|
|
576
|
+
return stepsByEventId;
|
|
577
|
+
}
|
|
578
|
+
export function isUserEvent(event) {
|
|
579
|
+
const type = String(event?.type ?? "");
|
|
580
|
+
return type === INPUT_TEXT_ITEM_TYPE || type === "input" || type.startsWith("user.");
|
|
581
|
+
}
|