@x12i/ai 0.0.0-stage → 1.1.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/LICENSE +21 -0
- package/README.md +531 -3
- package/dist/chunk-RP5SPJKM.js +1179 -0
- package/dist/chunk-RP5SPJKM.js.map +1 -0
- package/dist/index.d.ts +288 -0
- package/dist/index.js +514 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +906 -0
- package/dist/react.js +3067 -0
- package/dist/react.js.map +1 -0
- package/dist/stream-C2tzoSII.d.ts +482 -0
- package/dist/styles.css +1225 -0
- package/package.json +78 -3
|
@@ -0,0 +1,1179 @@
|
|
|
1
|
+
// src/messages.ts
|
|
2
|
+
function messageText(message) {
|
|
3
|
+
return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
4
|
+
}
|
|
5
|
+
function messageReasoning(message) {
|
|
6
|
+
return message.parts.filter((part) => part.type === "reasoning").map((part) => part.text).join("\n\n");
|
|
7
|
+
}
|
|
8
|
+
function pendingApprovals(message) {
|
|
9
|
+
return message.parts.filter(
|
|
10
|
+
(part) => part.type === "tool" && part.state === "needs-approval"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function threadPendingApprovals(messages) {
|
|
14
|
+
return messages.flatMap(pendingApprovals);
|
|
15
|
+
}
|
|
16
|
+
function messageToolCalls(message) {
|
|
17
|
+
return message.parts.filter((part) => part.type === "tool");
|
|
18
|
+
}
|
|
19
|
+
function messageSources(message) {
|
|
20
|
+
return message.parts.filter((part) => part.type === "source");
|
|
21
|
+
}
|
|
22
|
+
function messageFiles(message) {
|
|
23
|
+
return message.parts.filter((part) => part.type === "file");
|
|
24
|
+
}
|
|
25
|
+
function messageData(message) {
|
|
26
|
+
return message.parts.filter((part) => part.type === "data");
|
|
27
|
+
}
|
|
28
|
+
function findToolCall(messages, callId) {
|
|
29
|
+
for (let m = messages.length - 1; m >= 0; m -= 1) {
|
|
30
|
+
const message = messages[m];
|
|
31
|
+
if (!message) continue;
|
|
32
|
+
for (let p = message.parts.length - 1; p >= 0; p -= 1) {
|
|
33
|
+
const part = message.parts[p];
|
|
34
|
+
if (part && part.type === "tool" && part.id === callId) return { message, part };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
function isStreaming(message) {
|
|
40
|
+
return message.status === "streaming";
|
|
41
|
+
}
|
|
42
|
+
function lastMessageWithRole(messages, role) {
|
|
43
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
44
|
+
const message = messages[i];
|
|
45
|
+
if (message && message.role === role) return message;
|
|
46
|
+
}
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/thread-state.ts
|
|
51
|
+
var TERMINAL_TOOL_STATES = /* @__PURE__ */ new Set(["succeeded", "failed", "denied"]);
|
|
52
|
+
function isTerminalToolState(state) {
|
|
53
|
+
return TERMINAL_TOOL_STATES.has(state);
|
|
54
|
+
}
|
|
55
|
+
function isBusy(state) {
|
|
56
|
+
return state.status === "submitted" || state.status === "streaming";
|
|
57
|
+
}
|
|
58
|
+
function createThreadState(messages = []) {
|
|
59
|
+
return {
|
|
60
|
+
messages: [...messages],
|
|
61
|
+
status: "idle",
|
|
62
|
+
error: null,
|
|
63
|
+
activeMessageId: null,
|
|
64
|
+
branches: {}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function defined(value) {
|
|
68
|
+
const result = {};
|
|
69
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
70
|
+
if (entry !== void 0) result[key] = entry;
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
function shallowEqual(a, b) {
|
|
75
|
+
const aKeys = Object.keys(a);
|
|
76
|
+
const bKeys = Object.keys(b);
|
|
77
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
78
|
+
const left = a;
|
|
79
|
+
const right = b;
|
|
80
|
+
return aKeys.every((key) => Object.is(left[key], right[key]));
|
|
81
|
+
}
|
|
82
|
+
function indexOfMessage(messages, id) {
|
|
83
|
+
return messages.findIndex((message) => message.id === id);
|
|
84
|
+
}
|
|
85
|
+
function replaceAt(items, index, item) {
|
|
86
|
+
const copy = items.slice();
|
|
87
|
+
copy[index] = item;
|
|
88
|
+
return copy;
|
|
89
|
+
}
|
|
90
|
+
function withMessage(state, index, message) {
|
|
91
|
+
const current = state.messages[index];
|
|
92
|
+
if (current === message) return state;
|
|
93
|
+
return { ...state, messages: replaceAt(state.messages, index, message) };
|
|
94
|
+
}
|
|
95
|
+
function uniqueId(messages, prefix, start) {
|
|
96
|
+
let n = start;
|
|
97
|
+
while (messages.some((message) => message.id === `${prefix}${n}`)) n += 1;
|
|
98
|
+
return `${prefix}${n}`;
|
|
99
|
+
}
|
|
100
|
+
function closeReasoning(parts, at, durationMs) {
|
|
101
|
+
const last = parts[parts.length - 1];
|
|
102
|
+
if (!last || last.type !== "reasoning" || last.durationMs !== void 0) return parts;
|
|
103
|
+
let duration = durationMs;
|
|
104
|
+
if (duration === void 0 && at !== void 0 && last.startedAt !== void 0) {
|
|
105
|
+
duration = Math.max(0, at - last.startedAt);
|
|
106
|
+
}
|
|
107
|
+
if (duration === void 0) return parts;
|
|
108
|
+
return replaceAt(parts, parts.length - 1, { ...last, durationMs: duration });
|
|
109
|
+
}
|
|
110
|
+
function appendPart(parts, part, at) {
|
|
111
|
+
return [...closeReasoning(parts, at), part];
|
|
112
|
+
}
|
|
113
|
+
function completeMessage(messages, id) {
|
|
114
|
+
const index = indexOfMessage(messages, id);
|
|
115
|
+
const message = messages[index];
|
|
116
|
+
if (index < 0 || !message || message.status !== "streaming") return messages;
|
|
117
|
+
return replaceAt(messages, index, { ...message, status: "complete" });
|
|
118
|
+
}
|
|
119
|
+
function beginAssistant(state, action) {
|
|
120
|
+
const id = action.id ?? uniqueId(state.messages, "assistant-", state.messages.length + 1);
|
|
121
|
+
let messages = state.messages;
|
|
122
|
+
if (state.activeMessageId !== null && state.activeMessageId !== id) {
|
|
123
|
+
messages = completeMessage(messages, state.activeMessageId);
|
|
124
|
+
}
|
|
125
|
+
const index = indexOfMessage(messages, id);
|
|
126
|
+
if (index >= 0) {
|
|
127
|
+
const existing = messages[index];
|
|
128
|
+
if (!existing || existing.role !== "assistant") return state;
|
|
129
|
+
if (existing.status === "streaming" && state.activeMessageId === id && state.status === "streaming" && messages === state.messages) {
|
|
130
|
+
return state;
|
|
131
|
+
}
|
|
132
|
+
const reopened = { ...existing, status: "streaming" };
|
|
133
|
+
delete reopened.error;
|
|
134
|
+
messages = replaceAt(messages, index, reopened);
|
|
135
|
+
} else {
|
|
136
|
+
messages = [
|
|
137
|
+
...messages,
|
|
138
|
+
defined({
|
|
139
|
+
id,
|
|
140
|
+
role: "assistant",
|
|
141
|
+
parts: [],
|
|
142
|
+
status: "streaming",
|
|
143
|
+
createdAt: action.createdAt,
|
|
144
|
+
metadata: action.metadata
|
|
145
|
+
})
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
return { ...state, messages, status: "streaming", error: null, activeMessageId: id };
|
|
149
|
+
}
|
|
150
|
+
function resolveTarget(state, messageId, options = {}) {
|
|
151
|
+
const id = messageId ?? state.activeMessageId ?? void 0;
|
|
152
|
+
if (id !== void 0) {
|
|
153
|
+
const index2 = indexOfMessage(state.messages, id);
|
|
154
|
+
const message2 = state.messages[index2];
|
|
155
|
+
if (index2 >= 0 && message2) {
|
|
156
|
+
if (!options.anyRole) {
|
|
157
|
+
if (message2.role !== "assistant") return null;
|
|
158
|
+
if (message2.status === "stopped" || message2.status === "error") return null;
|
|
159
|
+
}
|
|
160
|
+
return { state, index: index2, message: message2 };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (options.createIfMissing === false) return null;
|
|
164
|
+
const created = beginAssistant(state, { id });
|
|
165
|
+
if (created === state || created.activeMessageId === null) return null;
|
|
166
|
+
const index = indexOfMessage(created.messages, created.activeMessageId);
|
|
167
|
+
const message = created.messages[index];
|
|
168
|
+
if (!message) return null;
|
|
169
|
+
return { state: created, index, message };
|
|
170
|
+
}
|
|
171
|
+
function writeParts(target, parts) {
|
|
172
|
+
if (parts === target.message.parts) return target.state;
|
|
173
|
+
return withMessage(target.state, target.index, { ...target.message, parts });
|
|
174
|
+
}
|
|
175
|
+
function findToolLocation(messages, callId) {
|
|
176
|
+
for (let m = messages.length - 1; m >= 0; m -= 1) {
|
|
177
|
+
const message = messages[m];
|
|
178
|
+
if (!message) continue;
|
|
179
|
+
for (let p = message.parts.length - 1; p >= 0; p -= 1) {
|
|
180
|
+
const part = message.parts[p];
|
|
181
|
+
if (part && part.type === "tool" && part.id === callId) {
|
|
182
|
+
return { messageIndex: m, partIndex: p, part };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function writeToolPart(state, location, next) {
|
|
189
|
+
if (next === location.part) return state;
|
|
190
|
+
const message = state.messages[location.messageIndex];
|
|
191
|
+
if (!message) return state;
|
|
192
|
+
const parts = replaceAt(message.parts, location.partIndex, next);
|
|
193
|
+
return withMessage(state, location.messageIndex, { ...message, parts });
|
|
194
|
+
}
|
|
195
|
+
function applyToolPatch(part, patch, at) {
|
|
196
|
+
const next = { ...part };
|
|
197
|
+
if (patch.name !== void 0) next.name = patch.name;
|
|
198
|
+
if (patch.input !== void 0) next.input = patch.input;
|
|
199
|
+
if (patch.output !== void 0) next.output = patch.output;
|
|
200
|
+
if (patch.error !== void 0) next.error = patch.error;
|
|
201
|
+
if (patch.errorCode !== void 0) next.errorCode = patch.errorCode;
|
|
202
|
+
if (patch.durationMs !== void 0) next.durationMs = patch.durationMs;
|
|
203
|
+
if (patch.approval !== void 0) next.approval = { ...part.approval, ...patch.approval };
|
|
204
|
+
if (patch.state !== void 0) {
|
|
205
|
+
const regressing = isTerminalToolState(part.state) && !isTerminalToolState(patch.state);
|
|
206
|
+
if (!regressing) next.state = patch.state;
|
|
207
|
+
}
|
|
208
|
+
if (isTerminalToolState(next.state) && next.durationMs === void 0 && next.startedAt !== void 0 && at !== void 0) {
|
|
209
|
+
next.durationMs = Math.max(0, at - next.startedAt);
|
|
210
|
+
}
|
|
211
|
+
return shallowEqual(next, part) ? part : next;
|
|
212
|
+
}
|
|
213
|
+
function newToolPart(callId, name, state, at) {
|
|
214
|
+
return defined({ type: "tool", id: callId, name, state, startedAt: at });
|
|
215
|
+
}
|
|
216
|
+
function addUserMessage(state, action) {
|
|
217
|
+
if (indexOfMessage(state.messages, action.id) >= 0) return state;
|
|
218
|
+
const parts = [];
|
|
219
|
+
if (action.parts) parts.push(...action.parts);
|
|
220
|
+
if (action.text !== void 0 && action.text !== "") parts.push({ type: "text", text: action.text });
|
|
221
|
+
for (const file of action.attachments ?? []) parts.push(file);
|
|
222
|
+
if (parts.length === 0) return state;
|
|
223
|
+
let messages = state.messages;
|
|
224
|
+
if (state.activeMessageId !== null) messages = completeMessage(messages, state.activeMessageId);
|
|
225
|
+
const message = defined({
|
|
226
|
+
id: action.id,
|
|
227
|
+
role: "user",
|
|
228
|
+
parts,
|
|
229
|
+
createdAt: action.createdAt
|
|
230
|
+
});
|
|
231
|
+
const status = action.status ?? "submitted";
|
|
232
|
+
return {
|
|
233
|
+
...state,
|
|
234
|
+
messages: [...messages, message],
|
|
235
|
+
status,
|
|
236
|
+
error: status === "error" ? state.error : null,
|
|
237
|
+
activeMessageId: null
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function appendText(state, action) {
|
|
241
|
+
if (action.delta === "") return state;
|
|
242
|
+
const target = resolveTarget(state, action.messageId);
|
|
243
|
+
if (!target) return state;
|
|
244
|
+
const parts = closeReasoning(target.message.parts, action.at);
|
|
245
|
+
const last = parts[parts.length - 1];
|
|
246
|
+
const next = last && last.type === "text" ? replaceAt(parts, parts.length - 1, { ...last, text: last.text + action.delta }) : [...parts, { type: "text", text: action.delta }];
|
|
247
|
+
return writeParts(target, next);
|
|
248
|
+
}
|
|
249
|
+
function appendReasoning(state, action) {
|
|
250
|
+
if (action.delta === "") return state;
|
|
251
|
+
const target = resolveTarget(state, action.messageId);
|
|
252
|
+
if (!target) return state;
|
|
253
|
+
const parts = target.message.parts;
|
|
254
|
+
const last = parts[parts.length - 1];
|
|
255
|
+
if (last && last.type === "reasoning" && last.durationMs === void 0) {
|
|
256
|
+
return writeParts(target, replaceAt(parts, parts.length - 1, { ...last, text: last.text + action.delta }));
|
|
257
|
+
}
|
|
258
|
+
const part = defined({
|
|
259
|
+
type: "reasoning",
|
|
260
|
+
text: action.delta,
|
|
261
|
+
startedAt: action.at
|
|
262
|
+
});
|
|
263
|
+
return writeParts(target, [...parts, part]);
|
|
264
|
+
}
|
|
265
|
+
function endReasoning(state, action) {
|
|
266
|
+
const id = action.messageId ?? state.activeMessageId;
|
|
267
|
+
if (id === null || id === void 0) return state;
|
|
268
|
+
const index = indexOfMessage(state.messages, id);
|
|
269
|
+
const message = state.messages[index];
|
|
270
|
+
if (index < 0 || !message) return state;
|
|
271
|
+
const parts = closeReasoning(message.parts, action.at, action.durationMs);
|
|
272
|
+
if (parts === message.parts) return state;
|
|
273
|
+
return withMessage(state, index, { ...message, parts });
|
|
274
|
+
}
|
|
275
|
+
function startToolCall(state, action) {
|
|
276
|
+
const wanted = action.state ?? (action.requiresApproval ? "needs-approval" : "running");
|
|
277
|
+
const existing = findToolLocation(state.messages, action.callId);
|
|
278
|
+
if (existing) {
|
|
279
|
+
const patch = { name: action.name, state: wanted };
|
|
280
|
+
if (action.input !== void 0) patch.input = action.input;
|
|
281
|
+
if (action.approvalPrompt !== void 0) patch.approval = { prompt: action.approvalPrompt };
|
|
282
|
+
return writeToolPart(state, existing, applyToolPatch(existing.part, patch, action.at));
|
|
283
|
+
}
|
|
284
|
+
const target = resolveTarget(state, action.messageId);
|
|
285
|
+
if (!target) return state;
|
|
286
|
+
const part = newToolPart(action.callId, action.name, wanted, action.at);
|
|
287
|
+
if (action.input !== void 0) part.input = action.input;
|
|
288
|
+
if (action.approvalPrompt !== void 0) part.approval = { prompt: action.approvalPrompt };
|
|
289
|
+
return writeParts(target, appendPart(target.message.parts, part, action.at));
|
|
290
|
+
}
|
|
291
|
+
function updateToolCall(state, action) {
|
|
292
|
+
const existing = findToolLocation(state.messages, action.callId);
|
|
293
|
+
if (existing) {
|
|
294
|
+
return writeToolPart(state, existing, applyToolPatch(existing.part, action.patch, action.at));
|
|
295
|
+
}
|
|
296
|
+
const target = resolveTarget(state, action.messageId);
|
|
297
|
+
if (!target) return state;
|
|
298
|
+
const created = applyToolPatch(
|
|
299
|
+
newToolPart(action.callId, action.patch.name ?? action.callId, action.patch.state ?? "running", action.at),
|
|
300
|
+
action.patch,
|
|
301
|
+
action.at
|
|
302
|
+
);
|
|
303
|
+
return writeParts(target, appendPart(target.message.parts, created, action.at));
|
|
304
|
+
}
|
|
305
|
+
function finishToolCall(state, action) {
|
|
306
|
+
const failed = action.error !== void 0 || action.errorCode !== void 0;
|
|
307
|
+
const patch = { state: failed ? "failed" : "succeeded" };
|
|
308
|
+
if (action.output !== void 0) patch.output = action.output;
|
|
309
|
+
if (action.error !== void 0) patch.error = action.error;
|
|
310
|
+
if (action.errorCode !== void 0) patch.errorCode = action.errorCode;
|
|
311
|
+
if (action.durationMs !== void 0) patch.durationMs = action.durationMs;
|
|
312
|
+
const existing = findToolLocation(state.messages, action.callId);
|
|
313
|
+
if (existing) {
|
|
314
|
+
if (existing.part.state === "denied") return state;
|
|
315
|
+
return writeToolPart(state, existing, applyToolPatch(existing.part, patch, action.at));
|
|
316
|
+
}
|
|
317
|
+
const target = resolveTarget(state, action.messageId);
|
|
318
|
+
if (!target) return state;
|
|
319
|
+
const created = applyToolPatch(
|
|
320
|
+
newToolPart(action.callId, action.name ?? action.callId, "running", action.at),
|
|
321
|
+
patch,
|
|
322
|
+
action.at
|
|
323
|
+
);
|
|
324
|
+
return writeParts(target, appendPart(target.message.parts, created, action.at));
|
|
325
|
+
}
|
|
326
|
+
function toolDecision(state, action) {
|
|
327
|
+
const location = findToolLocation(state.messages, action.callId);
|
|
328
|
+
if (!location || location.part.state !== "needs-approval") return state;
|
|
329
|
+
const reason = action.reason?.trim();
|
|
330
|
+
const approval = { ...location.part.approval };
|
|
331
|
+
if (reason) approval.reason = reason;
|
|
332
|
+
const next = { ...location.part, state: action.decision, approval };
|
|
333
|
+
if (Object.keys(approval).length === 0) delete next.approval;
|
|
334
|
+
return writeToolPart(state, location, next);
|
|
335
|
+
}
|
|
336
|
+
function addSource(state, action) {
|
|
337
|
+
const target = resolveTarget(state, action.messageId);
|
|
338
|
+
if (!target) return state;
|
|
339
|
+
const parts = target.message.parts;
|
|
340
|
+
const existingIndex = parts.findIndex((part) => part.type === "source" && part.id === action.source.id);
|
|
341
|
+
const incoming = { type: "source", ...defined(action.source) };
|
|
342
|
+
if (existingIndex >= 0) {
|
|
343
|
+
const current = parts[existingIndex];
|
|
344
|
+
const merged = { ...current, ...incoming };
|
|
345
|
+
return writeParts(target, shallowEqual(current, merged) ? parts : replaceAt(parts, existingIndex, merged));
|
|
346
|
+
}
|
|
347
|
+
return writeParts(target, [...parts, incoming]);
|
|
348
|
+
}
|
|
349
|
+
function addPart(state, action) {
|
|
350
|
+
const { part } = action;
|
|
351
|
+
if (part.type === "source") {
|
|
352
|
+
return addSource(state, {
|
|
353
|
+
type: "add-source",
|
|
354
|
+
source: { id: part.id, title: part.title, url: part.url, snippet: part.snippet },
|
|
355
|
+
...action.messageId ? { messageId: action.messageId } : {}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (part.type === "tool" && findToolLocation(state.messages, part.id)) return state;
|
|
359
|
+
const target = resolveTarget(state, action.messageId);
|
|
360
|
+
if (!target) return state;
|
|
361
|
+
return writeParts(target, appendPart(target.message.parts, part, void 0));
|
|
362
|
+
}
|
|
363
|
+
function attachFiles(state, action) {
|
|
364
|
+
if (action.files.length === 0) return state;
|
|
365
|
+
const fallback = state.messages[state.messages.length - 1]?.id;
|
|
366
|
+
const id = action.messageId ?? state.activeMessageId ?? fallback;
|
|
367
|
+
if (id === void 0 || id === null) return state;
|
|
368
|
+
const target = resolveTarget(state, id, { anyRole: true, createIfMissing: false });
|
|
369
|
+
if (!target) return state;
|
|
370
|
+
const known = new Set(
|
|
371
|
+
target.message.parts.filter((part) => part.type === "file").map((part) => part.id ?? part.url)
|
|
372
|
+
);
|
|
373
|
+
const added = action.files.filter((file) => {
|
|
374
|
+
const key = file.id ?? file.url;
|
|
375
|
+
if (known.has(key)) return false;
|
|
376
|
+
known.add(key);
|
|
377
|
+
return true;
|
|
378
|
+
});
|
|
379
|
+
if (added.length === 0) return state;
|
|
380
|
+
return writeParts(target, [...target.message.parts, ...added.map((file) => ({ ...file, type: "file" }))]);
|
|
381
|
+
}
|
|
382
|
+
function finishMessage(state, action) {
|
|
383
|
+
const id = action.messageId ?? state.activeMessageId;
|
|
384
|
+
let next = state;
|
|
385
|
+
if (id !== null && id !== void 0) {
|
|
386
|
+
const index = indexOfMessage(state.messages, id);
|
|
387
|
+
const message = state.messages[index];
|
|
388
|
+
if (index >= 0 && message) {
|
|
389
|
+
const parts = closeReasoning(message.parts, action.at);
|
|
390
|
+
const status = message.status === "stopped" || message.status === "error" ? message.status : "complete";
|
|
391
|
+
const updated = defined({
|
|
392
|
+
...message,
|
|
393
|
+
parts,
|
|
394
|
+
status,
|
|
395
|
+
usage: action.usage ?? message.usage,
|
|
396
|
+
finishReason: action.finishReason ?? message.finishReason
|
|
397
|
+
});
|
|
398
|
+
next = shallowEqual(updated, message) ? state : withMessage(state, index, updated);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const concernsActive = id === next.activeMessageId || next.activeMessageId === null;
|
|
402
|
+
if (concernsActive && (next.status === "streaming" || next.status === "submitted")) {
|
|
403
|
+
return { ...next, status: "idle", activeMessageId: null };
|
|
404
|
+
}
|
|
405
|
+
return next;
|
|
406
|
+
}
|
|
407
|
+
function toThreadErrorValue(error) {
|
|
408
|
+
return typeof error === "string" ? { message: error, kind: "unknown", retryable: true } : error;
|
|
409
|
+
}
|
|
410
|
+
function failMessage(state, action) {
|
|
411
|
+
const error = toThreadErrorValue(action.error);
|
|
412
|
+
const id = action.messageId ?? state.activeMessageId;
|
|
413
|
+
let messages = state.messages;
|
|
414
|
+
if (id !== null && id !== void 0) {
|
|
415
|
+
const index = indexOfMessage(messages, id);
|
|
416
|
+
const message = messages[index];
|
|
417
|
+
if (index >= 0 && message && message.role === "assistant") {
|
|
418
|
+
if (message.status === "streaming" && message.parts.length === 0) {
|
|
419
|
+
messages = messages.filter((_, i) => i !== index);
|
|
420
|
+
} else {
|
|
421
|
+
messages = replaceAt(messages, index, {
|
|
422
|
+
...message,
|
|
423
|
+
parts: closeReasoning(message.parts, action.at),
|
|
424
|
+
status: "error",
|
|
425
|
+
error
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return { ...state, messages, status: "error", error, activeMessageId: null };
|
|
431
|
+
}
|
|
432
|
+
function abortMessage(state, action) {
|
|
433
|
+
const id = action.messageId ?? state.activeMessageId;
|
|
434
|
+
let messages = state.messages;
|
|
435
|
+
if (id !== null && id !== void 0) {
|
|
436
|
+
const index = indexOfMessage(messages, id);
|
|
437
|
+
const message = messages[index];
|
|
438
|
+
if (index >= 0 && message && message.role === "assistant" && message.status === "streaming") {
|
|
439
|
+
if (message.parts.length === 0) {
|
|
440
|
+
messages = messages.filter((_, i) => i !== index);
|
|
441
|
+
} else {
|
|
442
|
+
const parts = closeReasoning(message.parts, action.at).map((part) => {
|
|
443
|
+
if (part.type === "tool" && (part.state === "running" || part.state === "queued")) {
|
|
444
|
+
return { ...part, state: "failed", errorCode: "aborted" };
|
|
445
|
+
}
|
|
446
|
+
return part;
|
|
447
|
+
});
|
|
448
|
+
messages = replaceAt(messages, index, { ...message, parts, status: "stopped" });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!isBusy(state) && messages === state.messages) return state;
|
|
453
|
+
return {
|
|
454
|
+
...state,
|
|
455
|
+
messages,
|
|
456
|
+
status: isBusy(state) ? "idle" : state.status,
|
|
457
|
+
activeMessageId: null
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function regenerate(state) {
|
|
461
|
+
let end = state.messages.length;
|
|
462
|
+
while (end > 0) {
|
|
463
|
+
const last2 = state.messages[end - 1];
|
|
464
|
+
if (last2 && (last2.role === "assistant" || last2.role === "tool")) end -= 1;
|
|
465
|
+
else break;
|
|
466
|
+
}
|
|
467
|
+
const last = state.messages[end - 1];
|
|
468
|
+
if (!last || last.role !== "user") return state;
|
|
469
|
+
const messages = state.messages.slice(0, end);
|
|
470
|
+
return {
|
|
471
|
+
...state,
|
|
472
|
+
messages,
|
|
473
|
+
branches: pruneBranches(state.branches, messages),
|
|
474
|
+
status: "submitted",
|
|
475
|
+
error: null,
|
|
476
|
+
activeMessageId: null
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function editedParts(old, action) {
|
|
480
|
+
if (action.parts) return action.parts;
|
|
481
|
+
const text = action.text ?? "";
|
|
482
|
+
const parts = [];
|
|
483
|
+
let placed = false;
|
|
484
|
+
for (const part of old.parts) {
|
|
485
|
+
if (part.type === "text") {
|
|
486
|
+
if (!placed && text !== "") parts.push({ type: "text", text });
|
|
487
|
+
placed = true;
|
|
488
|
+
} else {
|
|
489
|
+
parts.push(part);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (!placed && text !== "") parts.unshift({ type: "text", text });
|
|
493
|
+
return parts;
|
|
494
|
+
}
|
|
495
|
+
function branchParentKey(messages, index) {
|
|
496
|
+
return index <= 0 ? "" : messages[index - 1]?.id ?? "";
|
|
497
|
+
}
|
|
498
|
+
function insertAt(items, index, item) {
|
|
499
|
+
const copy = items.slice();
|
|
500
|
+
copy.splice(Math.min(Math.max(index, 0), copy.length), 0, item);
|
|
501
|
+
return copy;
|
|
502
|
+
}
|
|
503
|
+
function editMessage(state, action) {
|
|
504
|
+
const index = indexOfMessage(state.messages, action.messageId);
|
|
505
|
+
const old = state.messages[index];
|
|
506
|
+
if (index < 0 || !old) return state;
|
|
507
|
+
const parts = editedParts(old, action);
|
|
508
|
+
if (parts.length === 0) return state;
|
|
509
|
+
if (old.role !== "user") {
|
|
510
|
+
const edited2 = {
|
|
511
|
+
...old,
|
|
512
|
+
parts,
|
|
513
|
+
metadata: { ...old.metadata, edited: true }
|
|
514
|
+
};
|
|
515
|
+
return withMessage(state, index, edited2);
|
|
516
|
+
}
|
|
517
|
+
const mode = action.mode ?? "truncate";
|
|
518
|
+
const baseMetadata = { ...old.metadata, edited: true };
|
|
519
|
+
if (mode === "truncate") {
|
|
520
|
+
const edited2 = defined({
|
|
521
|
+
...old,
|
|
522
|
+
parts,
|
|
523
|
+
metadata: baseMetadata,
|
|
524
|
+
createdAt: action.createdAt ?? old.createdAt
|
|
525
|
+
});
|
|
526
|
+
const messages2 = [...state.messages.slice(0, index), edited2];
|
|
527
|
+
return {
|
|
528
|
+
...state,
|
|
529
|
+
messages: messages2,
|
|
530
|
+
branches: pruneBranches(state.branches, messages2),
|
|
531
|
+
status: "submitted",
|
|
532
|
+
error: null,
|
|
533
|
+
activeMessageId: null
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const newId = action.newId ?? uniqueId(state.messages, `${old.id}~`, (state.branches[branchParentKey(state.messages, index)]?.alternatives.length ?? 0) + 1);
|
|
537
|
+
if (newId !== old.id && indexOfMessage(state.messages, newId) >= 0) return state;
|
|
538
|
+
const parentKey = branchParentKey(state.messages, index);
|
|
539
|
+
const currentTail = state.messages.slice(index);
|
|
540
|
+
const edited = defined({
|
|
541
|
+
id: newId,
|
|
542
|
+
role: "user",
|
|
543
|
+
parts,
|
|
544
|
+
parentId: old.id,
|
|
545
|
+
createdAt: action.createdAt,
|
|
546
|
+
metadata: baseMetadata
|
|
547
|
+
});
|
|
548
|
+
const existing = state.branches[parentKey];
|
|
549
|
+
const all = existing ? insertAt(existing.alternatives, existing.activeIndex, currentTail) : [currentTail];
|
|
550
|
+
const point = { parentId: parentKey, alternatives: all, activeIndex: all.length };
|
|
551
|
+
const messages = [...state.messages.slice(0, index), edited];
|
|
552
|
+
return {
|
|
553
|
+
...state,
|
|
554
|
+
messages,
|
|
555
|
+
branches: pruneBranches({ ...state.branches, [parentKey]: point }, messages),
|
|
556
|
+
status: "submitted",
|
|
557
|
+
error: null,
|
|
558
|
+
activeMessageId: null
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function switchBranch(state, action) {
|
|
562
|
+
if (isBusy(state)) return state;
|
|
563
|
+
const point = state.branches[action.parentId];
|
|
564
|
+
if (!point) return state;
|
|
565
|
+
const count = point.alternatives.length + 1;
|
|
566
|
+
if (!Number.isInteger(action.index) || action.index < 0 || action.index >= count) return state;
|
|
567
|
+
if (action.index === point.activeIndex) return state;
|
|
568
|
+
let forkIndex = 0;
|
|
569
|
+
if (action.parentId !== "") {
|
|
570
|
+
const parentIndex = indexOfMessage(state.messages, action.parentId);
|
|
571
|
+
if (parentIndex < 0) return state;
|
|
572
|
+
forkIndex = parentIndex + 1;
|
|
573
|
+
}
|
|
574
|
+
const all = insertAt(point.alternatives, point.activeIndex, state.messages.slice(forkIndex));
|
|
575
|
+
const nextTail = all[action.index];
|
|
576
|
+
if (!nextTail) return state;
|
|
577
|
+
const alternatives = all.filter((_, i) => i !== action.index);
|
|
578
|
+
const messages = [...state.messages.slice(0, forkIndex), ...nextTail];
|
|
579
|
+
return {
|
|
580
|
+
...state,
|
|
581
|
+
messages,
|
|
582
|
+
branches: { ...state.branches, [action.parentId]: { parentId: action.parentId, alternatives, activeIndex: action.index } }
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function pruneBranches(branches, messages) {
|
|
586
|
+
const keys = Object.keys(branches);
|
|
587
|
+
if (keys.length === 0) return branches;
|
|
588
|
+
const reachable = /* @__PURE__ */ new Set([""]);
|
|
589
|
+
for (const message of messages) reachable.add(message.id);
|
|
590
|
+
const kept = /* @__PURE__ */ new Set();
|
|
591
|
+
let changed = true;
|
|
592
|
+
while (changed) {
|
|
593
|
+
changed = false;
|
|
594
|
+
for (const key of keys) {
|
|
595
|
+
const point = branches[key];
|
|
596
|
+
if (!point || kept.has(key) || !reachable.has(key)) continue;
|
|
597
|
+
kept.add(key);
|
|
598
|
+
changed = true;
|
|
599
|
+
for (const tail of point.alternatives) for (const message of tail) reachable.add(message.id);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (kept.size === keys.length) return branches;
|
|
603
|
+
const result = {};
|
|
604
|
+
for (const key of keys) {
|
|
605
|
+
const point = branches[key];
|
|
606
|
+
if (point && kept.has(key)) result[key] = point;
|
|
607
|
+
}
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
function threadReducer(state, action) {
|
|
611
|
+
switch (action.type) {
|
|
612
|
+
case "batch": {
|
|
613
|
+
let next = state;
|
|
614
|
+
for (const inner of action.actions) next = threadReducer(next, inner);
|
|
615
|
+
return next;
|
|
616
|
+
}
|
|
617
|
+
case "set-messages":
|
|
618
|
+
return { ...createThreadState(action.messages) };
|
|
619
|
+
case "add-message": {
|
|
620
|
+
if (indexOfMessage(state.messages, action.message.id) >= 0) return state;
|
|
621
|
+
return { ...state, messages: [...state.messages, action.message] };
|
|
622
|
+
}
|
|
623
|
+
case "add-user-message":
|
|
624
|
+
return addUserMessage(state, action);
|
|
625
|
+
case "begin-assistant-message":
|
|
626
|
+
return beginAssistant(state, action);
|
|
627
|
+
case "append-text":
|
|
628
|
+
return appendText(state, action);
|
|
629
|
+
case "append-reasoning":
|
|
630
|
+
return appendReasoning(state, action);
|
|
631
|
+
case "end-reasoning":
|
|
632
|
+
return endReasoning(state, action);
|
|
633
|
+
case "start-tool-call":
|
|
634
|
+
return startToolCall(state, action);
|
|
635
|
+
case "update-tool-call":
|
|
636
|
+
return updateToolCall(state, action);
|
|
637
|
+
case "finish-tool-call":
|
|
638
|
+
return finishToolCall(state, action);
|
|
639
|
+
case "tool-decision":
|
|
640
|
+
return toolDecision(state, action);
|
|
641
|
+
case "add-source":
|
|
642
|
+
return addSource(state, action);
|
|
643
|
+
case "add-part":
|
|
644
|
+
return addPart(state, action);
|
|
645
|
+
case "attach-files":
|
|
646
|
+
return attachFiles(state, action);
|
|
647
|
+
case "finish":
|
|
648
|
+
return finishMessage(state, action);
|
|
649
|
+
case "error":
|
|
650
|
+
return failMessage(state, action);
|
|
651
|
+
case "abort":
|
|
652
|
+
return abortMessage(state, action);
|
|
653
|
+
case "regenerate":
|
|
654
|
+
return regenerate(state);
|
|
655
|
+
case "edit-message":
|
|
656
|
+
return editMessage(state, action);
|
|
657
|
+
case "switch-branch":
|
|
658
|
+
return switchBranch(state, action);
|
|
659
|
+
case "dismiss-error": {
|
|
660
|
+
if (state.error === null && state.status !== "error") return state;
|
|
661
|
+
return { ...state, error: null, status: state.status === "error" ? "idle" : state.status };
|
|
662
|
+
}
|
|
663
|
+
case "clear": {
|
|
664
|
+
if (state.messages.length === 0 && state.status === "idle" && state.error === null && Object.keys(state.branches).length === 0) {
|
|
665
|
+
return state;
|
|
666
|
+
}
|
|
667
|
+
return createThreadState();
|
|
668
|
+
}
|
|
669
|
+
default:
|
|
670
|
+
return state;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function getBranchInfo(state, messageId) {
|
|
674
|
+
const index = indexOfMessage(state.messages, messageId);
|
|
675
|
+
if (index < 0) return void 0;
|
|
676
|
+
const parentId = branchParentKey(state.messages, index);
|
|
677
|
+
const point = state.branches[parentId];
|
|
678
|
+
if (!point) return void 0;
|
|
679
|
+
return { parentId, index: point.activeIndex, count: point.alternatives.length + 1 };
|
|
680
|
+
}
|
|
681
|
+
function getBranchInfoMap(state) {
|
|
682
|
+
const result = {};
|
|
683
|
+
for (const message of state.messages) {
|
|
684
|
+
const info = getBranchInfo(state, message.id);
|
|
685
|
+
if (info) result[message.id] = info;
|
|
686
|
+
}
|
|
687
|
+
return result;
|
|
688
|
+
}
|
|
689
|
+
function lastUserMessage(state) {
|
|
690
|
+
for (let i = state.messages.length - 1; i >= 0; i -= 1) {
|
|
691
|
+
const message = state.messages[i];
|
|
692
|
+
if (message && message.role === "user") return message;
|
|
693
|
+
}
|
|
694
|
+
return void 0;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/errors.ts
|
|
698
|
+
function readNumber(source, key) {
|
|
699
|
+
if (typeof source !== "object" || source === null) return void 0;
|
|
700
|
+
const value = source[key];
|
|
701
|
+
return typeof value === "number" ? value : void 0;
|
|
702
|
+
}
|
|
703
|
+
function readString(source, key) {
|
|
704
|
+
if (typeof source !== "object" || source === null) return void 0;
|
|
705
|
+
const value = source[key];
|
|
706
|
+
return typeof value === "string" ? value : void 0;
|
|
707
|
+
}
|
|
708
|
+
function isAbortError(error) {
|
|
709
|
+
if (typeof error !== "object" || error === null) return false;
|
|
710
|
+
const name = readString(error, "name");
|
|
711
|
+
return name === "AbortError" || readString(error, "code") === "ABORT_ERR";
|
|
712
|
+
}
|
|
713
|
+
function defaultRetryable(kind) {
|
|
714
|
+
return kind !== "moderation" && kind !== "auth";
|
|
715
|
+
}
|
|
716
|
+
function toThreadError(error) {
|
|
717
|
+
if (typeof error === "string") {
|
|
718
|
+
return { message: error, kind: "unknown", retryable: true };
|
|
719
|
+
}
|
|
720
|
+
const status = readNumber(error, "status") ?? readNumber(error, "statusCode");
|
|
721
|
+
const code = readString(error, "code") ?? readString(error, "type");
|
|
722
|
+
const message = readString(error, "message") ?? (error === void 0 || error === null ? "" : String(error));
|
|
723
|
+
const haystack = `${code ?? ""} ${message}`.toLowerCase();
|
|
724
|
+
let kind = "unknown";
|
|
725
|
+
if (status === 429 || /rate[\s_-]?limit|too many requests|quota/.test(haystack)) {
|
|
726
|
+
kind = "rate-limit";
|
|
727
|
+
} else if (status === 401 || status === 403 || /unauthori[sz]ed|invalid[\s_-]?api[\s_-]?key|authentication/.test(haystack)) {
|
|
728
|
+
kind = "auth";
|
|
729
|
+
} else if (typeof error === "object" && error !== null && readString(error, "name") === "TypeError" || /failed to fetch|network|offline|econn\w*|enotfound|etimedout|timeout|socket hang up|load failed/.test(
|
|
730
|
+
haystack
|
|
731
|
+
)) {
|
|
732
|
+
kind = "network";
|
|
733
|
+
} else if (/content[\s_-]?(filter|policy)|moderation|safety|refusal|flagged/.test(haystack)) {
|
|
734
|
+
kind = "moderation";
|
|
735
|
+
} else if (status !== void 0 && status >= 500) {
|
|
736
|
+
kind = "network";
|
|
737
|
+
}
|
|
738
|
+
const result = { message, kind, retryable: defaultRetryable(kind) };
|
|
739
|
+
if (code !== void 0) result.code = code;
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/stream.ts
|
|
744
|
+
function streamEventToAction(state, event, options = {}) {
|
|
745
|
+
const messageId = event.messageId ?? state.activeMessageId ?? options.messageId;
|
|
746
|
+
const at = options.at;
|
|
747
|
+
const withId = messageId === void 0 ? {} : { messageId };
|
|
748
|
+
const withAt = at === void 0 ? {} : { at };
|
|
749
|
+
switch (event.type) {
|
|
750
|
+
case "start": {
|
|
751
|
+
const id = event.messageId ?? options.messageId;
|
|
752
|
+
return {
|
|
753
|
+
type: "begin-assistant-message",
|
|
754
|
+
...id === void 0 ? {} : { id },
|
|
755
|
+
...event.createdAt === void 0 ? {} : { createdAt: event.createdAt },
|
|
756
|
+
...event.metadata === void 0 ? {} : { metadata: event.metadata }
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
case "text-delta":
|
|
760
|
+
return { type: "append-text", delta: event.delta, ...withId, ...withAt };
|
|
761
|
+
case "reasoning-delta":
|
|
762
|
+
return { type: "append-reasoning", delta: event.delta, ...withId, ...withAt };
|
|
763
|
+
case "reasoning-end":
|
|
764
|
+
return {
|
|
765
|
+
type: "end-reasoning",
|
|
766
|
+
...event.durationMs === void 0 ? {} : { durationMs: event.durationMs },
|
|
767
|
+
...withId,
|
|
768
|
+
...withAt
|
|
769
|
+
};
|
|
770
|
+
case "tool-call":
|
|
771
|
+
return {
|
|
772
|
+
type: "start-tool-call",
|
|
773
|
+
callId: event.callId,
|
|
774
|
+
name: event.name,
|
|
775
|
+
...event.input === void 0 ? {} : { input: event.input },
|
|
776
|
+
...event.state === void 0 ? {} : { state: event.state },
|
|
777
|
+
...event.requiresApproval === void 0 ? {} : { requiresApproval: event.requiresApproval },
|
|
778
|
+
...event.approvalPrompt === void 0 ? {} : { approvalPrompt: event.approvalPrompt },
|
|
779
|
+
...withId,
|
|
780
|
+
...withAt
|
|
781
|
+
};
|
|
782
|
+
case "tool-update": {
|
|
783
|
+
const patch = {};
|
|
784
|
+
if (event.state !== void 0) patch.state = event.state;
|
|
785
|
+
if (event.name !== void 0) patch.name = event.name;
|
|
786
|
+
if (event.input !== void 0) patch.input = event.input;
|
|
787
|
+
if (event.output !== void 0) patch.output = event.output;
|
|
788
|
+
if (event.error !== void 0) patch.error = event.error;
|
|
789
|
+
if (event.approval !== void 0) patch.approval = event.approval;
|
|
790
|
+
return { type: "update-tool-call", callId: event.callId, patch, ...withId, ...withAt };
|
|
791
|
+
}
|
|
792
|
+
case "tool-result":
|
|
793
|
+
return {
|
|
794
|
+
type: "finish-tool-call",
|
|
795
|
+
callId: event.callId,
|
|
796
|
+
...event.name === void 0 ? {} : { name: event.name },
|
|
797
|
+
...event.output === void 0 ? {} : { output: event.output },
|
|
798
|
+
...event.error === void 0 ? {} : { error: event.error },
|
|
799
|
+
...event.durationMs === void 0 ? {} : { durationMs: event.durationMs },
|
|
800
|
+
...withId,
|
|
801
|
+
...withAt
|
|
802
|
+
};
|
|
803
|
+
case "source":
|
|
804
|
+
return {
|
|
805
|
+
type: "add-source",
|
|
806
|
+
source: {
|
|
807
|
+
id: event.id,
|
|
808
|
+
title: event.title,
|
|
809
|
+
url: event.url,
|
|
810
|
+
...event.snippet === void 0 ? {} : { snippet: event.snippet }
|
|
811
|
+
},
|
|
812
|
+
...withId
|
|
813
|
+
};
|
|
814
|
+
case "file":
|
|
815
|
+
return {
|
|
816
|
+
type: "attach-files",
|
|
817
|
+
files: [
|
|
818
|
+
{
|
|
819
|
+
type: "file",
|
|
820
|
+
...event.id === void 0 ? {} : { id: event.id },
|
|
821
|
+
name: event.name,
|
|
822
|
+
mediaType: event.mediaType,
|
|
823
|
+
url: event.url,
|
|
824
|
+
...event.size === void 0 ? {} : { size: event.size }
|
|
825
|
+
}
|
|
826
|
+
],
|
|
827
|
+
...withId
|
|
828
|
+
};
|
|
829
|
+
case "data":
|
|
830
|
+
return { type: "add-part", part: { type: "data", kind: event.kind, data: event.data }, ...withId };
|
|
831
|
+
case "finish":
|
|
832
|
+
return {
|
|
833
|
+
type: "finish",
|
|
834
|
+
...event.usage === void 0 ? {} : { usage: event.usage },
|
|
835
|
+
...event.finishReason === void 0 ? {} : { finishReason: event.finishReason },
|
|
836
|
+
...withId,
|
|
837
|
+
...withAt
|
|
838
|
+
};
|
|
839
|
+
case "error":
|
|
840
|
+
return { type: "error", error: event.error, ...withId, ...withAt };
|
|
841
|
+
case "abort":
|
|
842
|
+
return { type: "abort", ...withId, ...withAt };
|
|
843
|
+
default:
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function applyStreamEvent(state, event, options = {}) {
|
|
848
|
+
const action = streamEventToAction(state, event, options);
|
|
849
|
+
return action === null ? state : threadReducer(state, action);
|
|
850
|
+
}
|
|
851
|
+
function applyStreamEvents(state, events, options = {}) {
|
|
852
|
+
return events.reduce((current, event) => applyStreamEvent(current, event, options), state);
|
|
853
|
+
}
|
|
854
|
+
function isTerminalStreamEvent(event) {
|
|
855
|
+
return event.type === "finish" || event.type === "error" || event.type === "abort";
|
|
856
|
+
}
|
|
857
|
+
function createAbortableStream(source, signal) {
|
|
858
|
+
async function* run() {
|
|
859
|
+
if (signal.aborted) return;
|
|
860
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
861
|
+
let onAbort;
|
|
862
|
+
const aborted = new Promise((resolve) => {
|
|
863
|
+
onAbort = () => resolve({ aborted: true });
|
|
864
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
865
|
+
});
|
|
866
|
+
try {
|
|
867
|
+
while (true) {
|
|
868
|
+
let result;
|
|
869
|
+
try {
|
|
870
|
+
result = await Promise.race([iterator.next(), aborted]);
|
|
871
|
+
} catch (error) {
|
|
872
|
+
if (signal.aborted) return;
|
|
873
|
+
throw error;
|
|
874
|
+
}
|
|
875
|
+
if ("aborted" in result) return;
|
|
876
|
+
if (result.done) return;
|
|
877
|
+
yield result.value;
|
|
878
|
+
if (signal.aborted) return;
|
|
879
|
+
}
|
|
880
|
+
} finally {
|
|
881
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
882
|
+
try {
|
|
883
|
+
Promise.resolve(iterator.return?.()).catch(() => {
|
|
884
|
+
});
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return run();
|
|
890
|
+
}
|
|
891
|
+
function errorEvent(error) {
|
|
892
|
+
return { type: "error", error: toThreadError(error) };
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/citations.ts
|
|
896
|
+
var MARKER = /(?<![\\\]!])\[(\d{1,3}(?:\s*[,–-]\s*\d{1,3})*)\](?![([:])/g;
|
|
897
|
+
function expand(list) {
|
|
898
|
+
const numbers = [];
|
|
899
|
+
for (const piece of list.split(",")) {
|
|
900
|
+
const range = piece.trim().split(/\s*[–-]\s*/);
|
|
901
|
+
const first = Number(range[0]);
|
|
902
|
+
const last = range.length > 1 ? Number(range[1]) : first;
|
|
903
|
+
if (!Number.isInteger(first) || !Number.isInteger(last)) return null;
|
|
904
|
+
if (last < first || last - first > 20) return null;
|
|
905
|
+
for (let n = first; n <= last; n += 1) numbers.push(n);
|
|
906
|
+
}
|
|
907
|
+
return [...new Set(numbers)];
|
|
908
|
+
}
|
|
909
|
+
function parseCitations(text, options = {}) {
|
|
910
|
+
const segments = [];
|
|
911
|
+
let cursor = 0;
|
|
912
|
+
for (const match of text.matchAll(MARKER)) {
|
|
913
|
+
const numbers = expand(match[1] ?? "");
|
|
914
|
+
if (!numbers || numbers.length === 0) continue;
|
|
915
|
+
if (options.maxIndex !== void 0 && numbers.some((n) => n < 1 || n > (options.maxIndex ?? 0))) continue;
|
|
916
|
+
if (match.index > cursor) segments.push({ type: "text", text: text.slice(cursor, match.index) });
|
|
917
|
+
segments.push({ type: "citation", numbers, raw: match[0] });
|
|
918
|
+
cursor = match.index + match[0].length;
|
|
919
|
+
}
|
|
920
|
+
if (cursor < text.length) segments.push({ type: "text", text: text.slice(cursor) });
|
|
921
|
+
return segments;
|
|
922
|
+
}
|
|
923
|
+
function citedNumbers(text, options = {}) {
|
|
924
|
+
const numbers = /* @__PURE__ */ new Set();
|
|
925
|
+
for (const segment of parseCitations(text, options)) {
|
|
926
|
+
if (segment.type === "citation") for (const n of segment.numbers) numbers.add(n);
|
|
927
|
+
}
|
|
928
|
+
return [...numbers].sort((a, b) => a - b);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/format.ts
|
|
932
|
+
function totalTokens(usage) {
|
|
933
|
+
if (usage.totalTokens !== void 0) return usage.totalTokens;
|
|
934
|
+
if (usage.inputTokens === void 0 && usage.outputTokens === void 0) return void 0;
|
|
935
|
+
return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
936
|
+
}
|
|
937
|
+
function mergeUsage(a, b) {
|
|
938
|
+
if (!a) return b;
|
|
939
|
+
if (!b) return a;
|
|
940
|
+
const result = {};
|
|
941
|
+
const keys = [
|
|
942
|
+
"inputTokens",
|
|
943
|
+
"outputTokens",
|
|
944
|
+
"totalTokens",
|
|
945
|
+
"reasoningTokens",
|
|
946
|
+
"cachedInputTokens"
|
|
947
|
+
];
|
|
948
|
+
for (const key of keys) {
|
|
949
|
+
const left = a[key];
|
|
950
|
+
const right = b[key];
|
|
951
|
+
if (left === void 0 && right === void 0) continue;
|
|
952
|
+
result[key] = (left ?? 0) + (right ?? 0);
|
|
953
|
+
}
|
|
954
|
+
return result;
|
|
955
|
+
}
|
|
956
|
+
function formatTokenCount(count, locale) {
|
|
957
|
+
if (!Number.isFinite(count)) return "";
|
|
958
|
+
return new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1 }).format(count);
|
|
959
|
+
}
|
|
960
|
+
function formatUsage(usage, locale) {
|
|
961
|
+
const result = {};
|
|
962
|
+
if (usage.inputTokens !== void 0) result.input = formatTokenCount(usage.inputTokens, locale);
|
|
963
|
+
if (usage.outputTokens !== void 0) result.output = formatTokenCount(usage.outputTokens, locale);
|
|
964
|
+
const total = totalTokens(usage);
|
|
965
|
+
if (total !== void 0) result.total = formatTokenCount(total, locale);
|
|
966
|
+
if (usage.reasoningTokens !== void 0) result.reasoning = formatTokenCount(usage.reasoningTokens, locale);
|
|
967
|
+
if (usage.cachedInputTokens !== void 0) result.cachedInput = formatTokenCount(usage.cachedInputTokens, locale);
|
|
968
|
+
return result;
|
|
969
|
+
}
|
|
970
|
+
function unitFormat(locale, unit, maximumFractionDigits = 0) {
|
|
971
|
+
return new Intl.NumberFormat(locale, { style: "unit", unit, unitDisplay: "narrow", maximumFractionDigits });
|
|
972
|
+
}
|
|
973
|
+
function formatDuration(ms, locale) {
|
|
974
|
+
if (!Number.isFinite(ms) || ms < 0) return "";
|
|
975
|
+
if (ms < 1e3) return unitFormat(locale, "millisecond").format(Math.round(ms));
|
|
976
|
+
const totalSeconds = ms / 1e3;
|
|
977
|
+
if (totalSeconds < 10) return unitFormat(locale, "second", 1).format(Math.round(totalSeconds * 10) / 10);
|
|
978
|
+
if (totalSeconds < 60) return unitFormat(locale, "second").format(Math.round(totalSeconds));
|
|
979
|
+
const wholeSeconds = Math.round(totalSeconds);
|
|
980
|
+
const hours = Math.floor(wholeSeconds / 3600);
|
|
981
|
+
const minutes = Math.floor(wholeSeconds % 3600 / 60);
|
|
982
|
+
const seconds = wholeSeconds % 60;
|
|
983
|
+
const pieces = [];
|
|
984
|
+
if (hours > 0) pieces.push(unitFormat(locale, "hour").format(hours));
|
|
985
|
+
if (minutes > 0 || hours > 0) pieces.push(unitFormat(locale, "minute").format(minutes));
|
|
986
|
+
if (hours === 0 && seconds > 0) pieces.push(unitFormat(locale, "second").format(seconds));
|
|
987
|
+
return new Intl.ListFormat(locale, { type: "unit", style: "narrow" }).format(pieces);
|
|
988
|
+
}
|
|
989
|
+
function formatBytes(bytes, locale) {
|
|
990
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
991
|
+
const units = ["byte", "kilobyte", "megabyte", "gigabyte"];
|
|
992
|
+
let value = bytes;
|
|
993
|
+
let index = 0;
|
|
994
|
+
while (value >= 1e3 && index < units.length - 1) {
|
|
995
|
+
value /= 1e3;
|
|
996
|
+
index += 1;
|
|
997
|
+
}
|
|
998
|
+
const unit = units[index] ?? "byte";
|
|
999
|
+
return new Intl.NumberFormat(locale, {
|
|
1000
|
+
style: "unit",
|
|
1001
|
+
unit,
|
|
1002
|
+
unitDisplay: "short",
|
|
1003
|
+
maximumFractionDigits: index === 0 ? 0 : 1
|
|
1004
|
+
}).format(value);
|
|
1005
|
+
}
|
|
1006
|
+
function splitCompletedSentences(text) {
|
|
1007
|
+
let boundary = 0;
|
|
1008
|
+
let inFence = false;
|
|
1009
|
+
let offset = 0;
|
|
1010
|
+
const lines = text.split("\n");
|
|
1011
|
+
lines.forEach((line, index) => {
|
|
1012
|
+
const isLast = index === lines.length - 1;
|
|
1013
|
+
const start = offset;
|
|
1014
|
+
offset += line.length + 1;
|
|
1015
|
+
if (/^\s{0,3}(```|~~~)/.test(line)) {
|
|
1016
|
+
inFence = !inFence;
|
|
1017
|
+
if (!inFence && !isLast) boundary = Math.max(boundary, offset - 1);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (inFence) return;
|
|
1021
|
+
const stops = isLast ? /[.!?…。!?]+(?=\s)/g : /[.!?…。!?]+(?=\s|$)/g;
|
|
1022
|
+
for (const match of line.matchAll(stops)) {
|
|
1023
|
+
boundary = Math.max(boundary, start + match.index + match[0].length);
|
|
1024
|
+
}
|
|
1025
|
+
if (!isLast) boundary = Math.max(boundary, offset - 1);
|
|
1026
|
+
});
|
|
1027
|
+
return { complete: text.slice(0, boundary), rest: text.slice(boundary) };
|
|
1028
|
+
}
|
|
1029
|
+
function markdownToPlainText(markdown) {
|
|
1030
|
+
return markdown.replace(/```[^\n]*\n([\s\S]*?)(?:```|$)/g, "$1").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/\[\d{1,3}(?:\s*[,–-]\s*\d{1,3})*\]/g, "").replace(/^\s{0,3}#{1,6}\s+/gm, "").replace(/^\s{0,3}>\s?/gm, "").replace(/^\s*[-*+]\s+/gm, "").replace(/^\s*\|?[\s:|-]{3,}\|?\s*$/gm, "").replace(/\|/g, " ").replace(/(\*\*|__|~~|`)/g, "").replace(/(^|[\s(])[*_]([^\s*_][^*_]*)[*_](?=$|[\s).,!?:;])/g, "$1$2").replace(/\s+/g, " ").trim();
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/markdown-safety.ts
|
|
1034
|
+
var SAFE_LINK_PROTOCOLS = ["http:", "https:", "mailto:", "tel:"];
|
|
1035
|
+
var SAFE_IMAGE_PROTOCOLS = ["http:", "https:"];
|
|
1036
|
+
var IGNORED_IN_SCHEME = /[\u0000-\u0020\u007f-\u009f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g;
|
|
1037
|
+
var SCHEME = /^([a-z][a-z0-9+.-]*):/i;
|
|
1038
|
+
function sanitizeUrl(url, options = {}) {
|
|
1039
|
+
if (typeof url !== "string") return null;
|
|
1040
|
+
const trimmed = url.trim();
|
|
1041
|
+
if (trimmed === "") return null;
|
|
1042
|
+
const protocols = options.protocols ?? SAFE_LINK_PROTOCOLS;
|
|
1043
|
+
const allowRelative = options.allowRelative ?? true;
|
|
1044
|
+
const probe = trimmed.replace(IGNORED_IN_SCHEME, "");
|
|
1045
|
+
const colon = probe.indexOf(":");
|
|
1046
|
+
const firstSeparator = probe.search(/[/?#]/);
|
|
1047
|
+
const hasScheme = colon !== -1 && (firstSeparator === -1 || colon < firstSeparator);
|
|
1048
|
+
if (!hasScheme) {
|
|
1049
|
+
return allowRelative ? trimmed : null;
|
|
1050
|
+
}
|
|
1051
|
+
const scheme = SCHEME.exec(probe);
|
|
1052
|
+
if (!scheme) return null;
|
|
1053
|
+
const protocol = `${(scheme[1] ?? "").toLowerCase()}:`;
|
|
1054
|
+
return protocols.includes(protocol) ? trimmed : null;
|
|
1055
|
+
}
|
|
1056
|
+
function isSafeUrl(url, options = {}) {
|
|
1057
|
+
return sanitizeUrl(url, options) !== null;
|
|
1058
|
+
}
|
|
1059
|
+
var DATA_IMAGE = /^data:image\/(?:png|jpe?g|gif|webp|avif);base64,[a-z0-9+/=\s]+$/i;
|
|
1060
|
+
function sanitizeImageUrl(url, options = {}) {
|
|
1061
|
+
if (typeof url !== "string") return null;
|
|
1062
|
+
const trimmed = url.trim();
|
|
1063
|
+
if (options.allowDataImages && DATA_IMAGE.test(trimmed)) return trimmed;
|
|
1064
|
+
if (options.allowBlob && /^blob:/i.test(trimmed)) return trimmed;
|
|
1065
|
+
return sanitizeUrl(trimmed, { protocols: SAFE_IMAGE_PROTOCOLS });
|
|
1066
|
+
}
|
|
1067
|
+
function isExternalUrl(url) {
|
|
1068
|
+
return /^(?:https?:)?\/\//i.test(url.trim().replace(IGNORED_IN_SCHEME, ""));
|
|
1069
|
+
}
|
|
1070
|
+
function getUrlHostname(url) {
|
|
1071
|
+
try {
|
|
1072
|
+
const parsed = new URL(url);
|
|
1073
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1074
|
+
return parsed.hostname.replace(/^www\./i, "");
|
|
1075
|
+
} catch {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function splitMarkdownBlocks(markdown) {
|
|
1080
|
+
if (markdown === "") return [];
|
|
1081
|
+
if (/^ {0,3}\[[^\]\n]+\]:/m.test(markdown) || /\[\^[^\]\n]+\]/.test(markdown)) return [markdown];
|
|
1082
|
+
const LIST_MARKER = /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:\s|$)/;
|
|
1083
|
+
const lines = markdown.split("\n");
|
|
1084
|
+
const blocks = [];
|
|
1085
|
+
let current = "";
|
|
1086
|
+
let kind = null;
|
|
1087
|
+
let fence = null;
|
|
1088
|
+
let afterBlank = false;
|
|
1089
|
+
const flush = () => {
|
|
1090
|
+
if (current !== "") blocks.push(current);
|
|
1091
|
+
current = "";
|
|
1092
|
+
kind = null;
|
|
1093
|
+
afterBlank = false;
|
|
1094
|
+
};
|
|
1095
|
+
const continues = (line, previous) => {
|
|
1096
|
+
if (previous === "sticky") return true;
|
|
1097
|
+
if (previous === "list") return LIST_MARKER.test(line) || /^(?: {2,}|\t)/.test(line);
|
|
1098
|
+
if (previous === "quote") return /^ {0,3}>/.test(line);
|
|
1099
|
+
return false;
|
|
1100
|
+
};
|
|
1101
|
+
lines.forEach((line, index) => {
|
|
1102
|
+
const text = index === lines.length - 1 ? line : `${line}
|
|
1103
|
+
`;
|
|
1104
|
+
const fenceMatch = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
1105
|
+
if (fence !== null) {
|
|
1106
|
+
current += text;
|
|
1107
|
+
if (fenceMatch && fenceMatch[1] && fenceMatch[1][0] === fence[0] && fenceMatch[1].length >= fence.length && (fenceMatch[2] ?? "").trim() === "") {
|
|
1108
|
+
fence = null;
|
|
1109
|
+
}
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (line.trim() === "") {
|
|
1113
|
+
current += text;
|
|
1114
|
+
afterBlank = kind !== null;
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
if (afterBlank && !continues(line, kind)) flush();
|
|
1118
|
+
afterBlank = false;
|
|
1119
|
+
if (kind === null) {
|
|
1120
|
+
if (LIST_MARKER.test(line)) kind = "list";
|
|
1121
|
+
else if (/^ {0,3}>/.test(line)) kind = "quote";
|
|
1122
|
+
else if (/^(?:\t| {4,}| {0,3}<)/.test(line)) kind = "sticky";
|
|
1123
|
+
else kind = "plain";
|
|
1124
|
+
}
|
|
1125
|
+
current += text;
|
|
1126
|
+
const marker = fenceMatch?.[1];
|
|
1127
|
+
if (marker && !(marker[0] === "`" && (fenceMatch?.[2] ?? "").includes("`"))) fence = marker;
|
|
1128
|
+
});
|
|
1129
|
+
flush();
|
|
1130
|
+
return blocks;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
export {
|
|
1134
|
+
messageText,
|
|
1135
|
+
messageReasoning,
|
|
1136
|
+
pendingApprovals,
|
|
1137
|
+
threadPendingApprovals,
|
|
1138
|
+
messageToolCalls,
|
|
1139
|
+
messageSources,
|
|
1140
|
+
messageFiles,
|
|
1141
|
+
messageData,
|
|
1142
|
+
findToolCall,
|
|
1143
|
+
isStreaming,
|
|
1144
|
+
lastMessageWithRole,
|
|
1145
|
+
isTerminalToolState,
|
|
1146
|
+
isBusy,
|
|
1147
|
+
createThreadState,
|
|
1148
|
+
threadReducer,
|
|
1149
|
+
getBranchInfo,
|
|
1150
|
+
getBranchInfoMap,
|
|
1151
|
+
lastUserMessage,
|
|
1152
|
+
isAbortError,
|
|
1153
|
+
toThreadError,
|
|
1154
|
+
streamEventToAction,
|
|
1155
|
+
applyStreamEvent,
|
|
1156
|
+
applyStreamEvents,
|
|
1157
|
+
isTerminalStreamEvent,
|
|
1158
|
+
createAbortableStream,
|
|
1159
|
+
errorEvent,
|
|
1160
|
+
parseCitations,
|
|
1161
|
+
citedNumbers,
|
|
1162
|
+
totalTokens,
|
|
1163
|
+
mergeUsage,
|
|
1164
|
+
formatTokenCount,
|
|
1165
|
+
formatUsage,
|
|
1166
|
+
formatDuration,
|
|
1167
|
+
formatBytes,
|
|
1168
|
+
splitCompletedSentences,
|
|
1169
|
+
markdownToPlainText,
|
|
1170
|
+
SAFE_LINK_PROTOCOLS,
|
|
1171
|
+
SAFE_IMAGE_PROTOCOLS,
|
|
1172
|
+
sanitizeUrl,
|
|
1173
|
+
isSafeUrl,
|
|
1174
|
+
sanitizeImageUrl,
|
|
1175
|
+
isExternalUrl,
|
|
1176
|
+
getUrlHostname,
|
|
1177
|
+
splitMarkdownBlocks
|
|
1178
|
+
};
|
|
1179
|
+
//# sourceMappingURL=chunk-RP5SPJKM.js.map
|