@dudko.dev/agent-web 0.0.21 → 0.0.23
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/README.md +14 -8
- package/dist/agent-web.js +104 -104
- package/dist/agent-web.js.map +1 -1
- package/dist/chunk-YA6TQQ3Q.js +425 -0
- package/dist/chunk-YA6TQQ3Q.js.map +1 -0
- package/dist/index.cjs +504 -270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -9
- package/dist/index.d.ts +102 -9
- package/dist/index.js +76 -244
- package/dist/index.js.map +1 -1
- package/dist/{webllm-XUBUNKFN.js → webllm-QTJ3EGTG.js} +2 -2
- package/package.json +10 -10
- package/dist/chunk-IT65EW77.js +0 -38
- package/dist/chunk-IT65EW77.js.map +0 -1
- /package/dist/{webllm-XUBUNKFN.js.map → webllm-QTJ3EGTG.js.map} +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// src/providers/webllm.ts
|
|
2
|
+
import { generateText as generateText2 } from "ai";
|
|
3
|
+
|
|
4
|
+
// src/context-window.ts
|
|
5
|
+
import { asSchema } from "ai";
|
|
6
|
+
|
|
7
|
+
// src/images.ts
|
|
8
|
+
var DATA_URL_RE = /^data:([^;,]+)?;base64,(.*)$/s;
|
|
9
|
+
var HTTP_RE = /^https?:\/\//i;
|
|
10
|
+
var mediaTypeOf = (f) => {
|
|
11
|
+
if (f.mediaType) return f.mediaType;
|
|
12
|
+
if (typeof f.data === "string") {
|
|
13
|
+
const m = DATA_URL_RE.exec(f.data);
|
|
14
|
+
if (m?.[1]) return m[1];
|
|
15
|
+
}
|
|
16
|
+
const url = f.data instanceof URL ? f.data.href : typeof f.data === "string" ? f.data : "";
|
|
17
|
+
if (HTTP_RE.test(url) || f.name) {
|
|
18
|
+
const ext = (f.name ?? new URL(url, "http://x").pathname).split(".").pop()?.toLowerCase();
|
|
19
|
+
if (ext === "pdf") return "application/pdf";
|
|
20
|
+
if (ext && ["png", "jpg", "jpeg", "gif", "webp"].includes(ext)) {
|
|
21
|
+
return `image/${ext === "jpg" ? "jpeg" : ext}`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return void 0;
|
|
25
|
+
};
|
|
26
|
+
var attachmentKind = (f) => {
|
|
27
|
+
const mt = mediaTypeOf(f) ?? "";
|
|
28
|
+
if (mt.startsWith("image/")) return "image";
|
|
29
|
+
if (mt === "application/pdf") return "pdf";
|
|
30
|
+
return "file";
|
|
31
|
+
};
|
|
32
|
+
var KIND_LABEL = {
|
|
33
|
+
image: "images",
|
|
34
|
+
pdf: "PDF files",
|
|
35
|
+
file: "file attachments"
|
|
36
|
+
};
|
|
37
|
+
var KIND_HINT = {
|
|
38
|
+
image: "Remove the image, or switch to a vision-capable model such as Gemini, Claude or GPT-4o/GPT-5.",
|
|
39
|
+
pdf: "Convert the PDF to text first (e.g. PDF \u2192 Markdown), or switch to a model that reads PDFs such as Gemini, Claude or GPT-4o/GPT-5.",
|
|
40
|
+
file: "Paste its text instead, or switch to a model that accepts this file type."
|
|
41
|
+
};
|
|
42
|
+
var AttachmentsNotSupportedError = class extends Error {
|
|
43
|
+
kind;
|
|
44
|
+
constructor(model, kind, detail) {
|
|
45
|
+
super(
|
|
46
|
+
`The model "${model}" can't take ${KIND_LABEL[kind]}${detail ? ` (${detail})` : ""}. ${KIND_HINT[kind]}`
|
|
47
|
+
);
|
|
48
|
+
this.name = "AttachmentsNotSupportedError";
|
|
49
|
+
this.kind = kind;
|
|
50
|
+
}
|
|
51
|
+
toJSON() {
|
|
52
|
+
return this.message;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var ImagesNotSupportedError = class extends AttachmentsNotSupportedError {
|
|
56
|
+
constructor(model, detail) {
|
|
57
|
+
super(model, "image", detail);
|
|
58
|
+
this.name = "ImagesNotSupportedError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var unsupportedAttachment = (model, kind, detail) => kind === "image" ? new ImagesNotSupportedError(model, detail) : new AttachmentsNotSupportedError(model, kind, detail);
|
|
62
|
+
var dataOf = (f) => {
|
|
63
|
+
if (typeof f.data === "string") {
|
|
64
|
+
const m = DATA_URL_RE.exec(f.data);
|
|
65
|
+
if (m) return m[2];
|
|
66
|
+
if (HTTP_RE.test(f.data)) return new URL(f.data);
|
|
67
|
+
}
|
|
68
|
+
return f.data;
|
|
69
|
+
};
|
|
70
|
+
var toFilePart = (f) => {
|
|
71
|
+
const kind = attachmentKind(f);
|
|
72
|
+
const mediaType = mediaTypeOf(f) ?? (kind === "image" ? "image" : "application/octet-stream");
|
|
73
|
+
return {
|
|
74
|
+
type: "file",
|
|
75
|
+
data: dataOf(f),
|
|
76
|
+
mediaType,
|
|
77
|
+
...f.name ? { filename: f.name } : {}
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
var toImagePart = toFilePart;
|
|
81
|
+
var modelLabel = (model) => {
|
|
82
|
+
if (typeof model === "string") return model;
|
|
83
|
+
const m = model;
|
|
84
|
+
return m?.modelId ? `${m.modelId}${m.provider ? ` (${m.provider})` : ""}` : "this model";
|
|
85
|
+
};
|
|
86
|
+
var REFUSAL_RE = /image|vision|multimodal|multi-modal|media type|mediatype|pdf|document|unsupported (file|content|part|mime)|does not support (file|content)|invalid content type|image_url|inline_?data|file_?data/i;
|
|
87
|
+
var isAttachmentRefusal = (err) => REFUSAL_RE.test(err instanceof Error ? `${err.name} ${err.message}` : String(err));
|
|
88
|
+
var isImageRefusal = isAttachmentRefusal;
|
|
89
|
+
|
|
90
|
+
// src/llm/generate.ts
|
|
91
|
+
import {
|
|
92
|
+
generateObject,
|
|
93
|
+
generateText,
|
|
94
|
+
streamText
|
|
95
|
+
} from "ai";
|
|
96
|
+
|
|
97
|
+
// src/llm/util.ts
|
|
98
|
+
var clip = (s, max) => s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
99
|
+
var promptOf = (opts) => opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? "" };
|
|
100
|
+
var normalizeUsage = (u) => {
|
|
101
|
+
const inputTokens = u?.inputTokens ?? 0;
|
|
102
|
+
const outputTokens = u?.outputTokens ?? 0;
|
|
103
|
+
return {
|
|
104
|
+
inputTokens,
|
|
105
|
+
outputTokens,
|
|
106
|
+
totalTokens: u?.totalTokens ?? inputTokens + outputTokens,
|
|
107
|
+
reasoningTokens: u?.outputTokenDetails?.reasoningTokens ?? u?.reasoningTokens ?? 0,
|
|
108
|
+
cachedInputTokens: u?.inputTokenDetails?.cacheReadTokens ?? u?.cachedInputTokens ?? 0,
|
|
109
|
+
cacheWriteTokens: u?.inputTokenDetails?.cacheWriteTokens ?? 0
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var timeoutSignal = (signal, ms) => {
|
|
113
|
+
if (!ms || ms <= 0) return signal;
|
|
114
|
+
const timeout = AbortSignal.timeout(ms);
|
|
115
|
+
if (!signal) return timeout;
|
|
116
|
+
return typeof AbortSignal.any === "function" ? AbortSignal.any([signal, timeout]) : signal;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/llm/generate.ts
|
|
120
|
+
var callSettings = (opts) => ({
|
|
121
|
+
...opts.system !== void 0 ? { instructions: opts.system } : {},
|
|
122
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
123
|
+
temperature: opts.temperature,
|
|
124
|
+
...opts.reasoning ? { reasoning: opts.reasoning } : {},
|
|
125
|
+
...opts.providerOptions ? { providerOptions: opts.providerOptions } : {},
|
|
126
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
127
|
+
});
|
|
128
|
+
var generate = (model, opts = {}) => generateText({
|
|
129
|
+
model,
|
|
130
|
+
...callSettings(opts),
|
|
131
|
+
...promptOf(opts),
|
|
132
|
+
tools: opts.tools
|
|
133
|
+
});
|
|
134
|
+
var stream = (model, opts = {}) => streamText({
|
|
135
|
+
model,
|
|
136
|
+
...callSettings(opts),
|
|
137
|
+
...promptOf(opts),
|
|
138
|
+
tools: opts.tools
|
|
139
|
+
});
|
|
140
|
+
var generateStructured = (model, schema, opts = {}) => generateObject({
|
|
141
|
+
model,
|
|
142
|
+
schema,
|
|
143
|
+
...callSettings(opts),
|
|
144
|
+
...promptOf(opts)
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// src/memory/compress.ts
|
|
148
|
+
var resolveCompaction = (c) => {
|
|
149
|
+
const contextWindowTokens = c?.contextWindowTokens ?? 128e3;
|
|
150
|
+
return {
|
|
151
|
+
auto: c?.auto ?? true,
|
|
152
|
+
contextWindowTokens,
|
|
153
|
+
thresholdTokens: c?.thresholdTokens ?? Math.floor(contextWindowTokens / 2),
|
|
154
|
+
keepRecentTurns: c?.keepRecentTurns ?? 4,
|
|
155
|
+
keepRecentSteps: c?.keepRecentSteps ?? 3,
|
|
156
|
+
summaryMaxTokens: c?.summaryMaxTokens ?? 1024,
|
|
157
|
+
maxToolOutputChars: c?.maxToolOutputChars ?? 2e4,
|
|
158
|
+
clearToolResultsAfterTokens: c?.clearToolResultsAfterTokens ?? Math.floor(contextWindowTokens / 4),
|
|
159
|
+
keepToolResults: c?.keepToolResults ?? 3
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
var estimateTokens = (text) => Math.ceil((text ?? "").length / 4);
|
|
163
|
+
var messageTokens = (msgs) => msgs.reduce((n, m) => n + estimateTokens(`${m.role}: ${m.content}
|
|
164
|
+
`), 0);
|
|
165
|
+
var SUMMARY_SYSTEM = "Summarize the following conversation into a compact set of durable facts, decisions, identifiers, open tasks and user preferences. Keep names, numbers and IDs verbatim. Output plain text only.";
|
|
166
|
+
var compressHistory = async (messages, model, opts = {}) => {
|
|
167
|
+
const keepRecent = opts.keepRecent ?? 6;
|
|
168
|
+
if (messages.length <= keepRecent + 1) return messages;
|
|
169
|
+
if (!opts.force) {
|
|
170
|
+
if (opts.thresholdTokens !== void 0) {
|
|
171
|
+
if (opts.thresholdTokens <= 0 || messageTokens(messages) <= opts.thresholdTokens) {
|
|
172
|
+
return messages;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
const maxChars = opts.maxChars ?? 12e3;
|
|
176
|
+
const chars = messages.reduce((n, m) => n + m.content.length, 0);
|
|
177
|
+
if (maxChars <= 0 || chars <= maxChars) return messages;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const head = messages.slice(0, messages.length - keepRecent);
|
|
181
|
+
const tail = messages.slice(messages.length - keepRecent);
|
|
182
|
+
const transcript = head.map((m) => `${m.role}: ${m.content}`).join("\n").slice(-48e3);
|
|
183
|
+
try {
|
|
184
|
+
const result = await generate(model, {
|
|
185
|
+
system: SUMMARY_SYSTEM,
|
|
186
|
+
prompt: transcript,
|
|
187
|
+
maxOutputTokens: opts.summaryMaxTokens ?? 400,
|
|
188
|
+
timeoutMs: opts.timeoutMs,
|
|
189
|
+
abortSignal: opts.abortSignal
|
|
190
|
+
});
|
|
191
|
+
opts.onUsage?.(normalizeUsage(result.usage));
|
|
192
|
+
const clean = (result.text || "").trim();
|
|
193
|
+
if (!clean) return messages;
|
|
194
|
+
const summaryMsg = {
|
|
195
|
+
role: "system",
|
|
196
|
+
content: `Summary of earlier conversation:
|
|
197
|
+
${clean}`,
|
|
198
|
+
ts: head[0]?.ts
|
|
199
|
+
};
|
|
200
|
+
return [summaryMsg, ...tail];
|
|
201
|
+
} catch {
|
|
202
|
+
return messages;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var compactHistory = compressHistory;
|
|
206
|
+
var TRACE_SYSTEM = "Summarize these completed agent steps into a compact progress note: what was done, what was found (keep names, numbers and IDs verbatim) and what failed. Plain text only.";
|
|
207
|
+
var compactSteps = async (done, model, opts) => {
|
|
208
|
+
const keepRecent = opts.keepRecent ?? 3;
|
|
209
|
+
if (done.length <= keepRecent + 1) return done;
|
|
210
|
+
if (!opts.force && estimateTokens(done.join("\n")) <= opts.thresholdTokens) return done;
|
|
211
|
+
const head = done.slice(0, done.length - keepRecent);
|
|
212
|
+
try {
|
|
213
|
+
const result = await generate(model, {
|
|
214
|
+
system: TRACE_SYSTEM,
|
|
215
|
+
prompt: head.map((d, i) => `${i + 1}. ${d}`).join("\n"),
|
|
216
|
+
maxOutputTokens: opts.summaryMaxTokens ?? 600,
|
|
217
|
+
timeoutMs: opts.timeoutMs,
|
|
218
|
+
abortSignal: opts.abortSignal
|
|
219
|
+
});
|
|
220
|
+
opts.onUsage?.(normalizeUsage(result.usage));
|
|
221
|
+
const clean = (result.text || "").trim();
|
|
222
|
+
if (!clean) return done;
|
|
223
|
+
return [`Summary of ${head.length} earlier step(s): ${clean}`, ...done.slice(head.length)];
|
|
224
|
+
} catch {
|
|
225
|
+
return done;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// src/context-window.ts
|
|
230
|
+
var WEBLLM_DEFAULT_CONTEXT_WINDOW = 4096;
|
|
231
|
+
var withWebLLMContextWindow = (appConfig, modelId, tokens) => ({
|
|
232
|
+
...appConfig,
|
|
233
|
+
model_list: appConfig.model_list.map(
|
|
234
|
+
(r) => r.model_id === modelId ? { ...r, overrides: { ...r.overrides, context_window_size: tokens } } : r
|
|
235
|
+
)
|
|
236
|
+
});
|
|
237
|
+
var webLLMContextWindow = (model) => {
|
|
238
|
+
const m = model;
|
|
239
|
+
if (!m || typeof m !== "object" || m.provider !== "web-llm") return void 0;
|
|
240
|
+
const id = String(m.modelId ?? "");
|
|
241
|
+
try {
|
|
242
|
+
const live = m.engine?.loadedModelIdToPipeline?.get?.(id)?.contextWindowSize;
|
|
243
|
+
if (typeof live === "number" && live > 0) return live;
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
const record = m.config?.options?.engineConfig?.appConfig?.model_list?.find(
|
|
247
|
+
(r) => r.model_id === id
|
|
248
|
+
);
|
|
249
|
+
const configured = record?.overrides?.context_window_size;
|
|
250
|
+
if (typeof configured === "number" && configured > 0) return configured;
|
|
251
|
+
return /-1k$/i.test(id) ? 1024 : WEBLLM_DEFAULT_CONTEXT_WINDOW;
|
|
252
|
+
};
|
|
253
|
+
var contextWindowOf = (model) => {
|
|
254
|
+
const webllm = webLLMContextWindow(model);
|
|
255
|
+
if (webllm !== void 0) return webllm;
|
|
256
|
+
const m = model;
|
|
257
|
+
if (m && typeof m === "object" && m.provider === "browser-ai") {
|
|
258
|
+
try {
|
|
259
|
+
const w = m.getContextWindow?.();
|
|
260
|
+
return typeof w === "number" && w > 0 ? w : void 0;
|
|
261
|
+
} catch {
|
|
262
|
+
return void 0;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return void 0;
|
|
266
|
+
};
|
|
267
|
+
var fitCompactionToWindow = (resolved, configured, modelWindow) => {
|
|
268
|
+
const window = Math.min(
|
|
269
|
+
configured?.contextWindowTokens ?? modelWindow ?? resolved.contextWindowTokens,
|
|
270
|
+
modelWindow ?? Infinity
|
|
271
|
+
);
|
|
272
|
+
if (window === resolved.contextWindowTokens && modelWindow === void 0) return resolved;
|
|
273
|
+
const half = Math.floor(window / 2);
|
|
274
|
+
const quarter = Math.floor(window / 4);
|
|
275
|
+
const clearAfter = configured?.clearToolResultsAfterTokens;
|
|
276
|
+
return {
|
|
277
|
+
...resolved,
|
|
278
|
+
contextWindowTokens: window,
|
|
279
|
+
thresholdTokens: Math.min(configured?.thresholdTokens ?? half, half),
|
|
280
|
+
// 0 = never stays never.
|
|
281
|
+
clearToolResultsAfterTokens: clearAfter === 0 ? 0 : Math.min(clearAfter ?? quarter, quarter),
|
|
282
|
+
// One result may take a quarter of the window (~4 chars per token).
|
|
283
|
+
maxToolOutputChars: configured?.maxToolOutputChars === 0 ? 0 : Math.min(configured?.maxToolOutputChars ?? 2e4, window),
|
|
284
|
+
// A small window holds fewer verbatim results.
|
|
285
|
+
keepToolResults: configured?.keepToolResults ?? Math.max(1, Math.min(3, Math.floor(window / 8192)))
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
var toolDefinitionTokens = async (tools) => {
|
|
289
|
+
const sizes = await Promise.all(
|
|
290
|
+
Object.entries(tools).map(async ([name, t]) => {
|
|
291
|
+
let parameters;
|
|
292
|
+
try {
|
|
293
|
+
parameters = await asSchema(t.inputSchema).jsonSchema;
|
|
294
|
+
} catch {
|
|
295
|
+
parameters = void 0;
|
|
296
|
+
}
|
|
297
|
+
return estimateTokens(JSON.stringify({ name, description: t.description ?? "", parameters }));
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
return sizes.reduce((a, b) => a + b, 0);
|
|
301
|
+
};
|
|
302
|
+
var TOOL_SEARCH_WINDOW_SHARE = 0.25;
|
|
303
|
+
var ContextWindowExceededError = class extends Error {
|
|
304
|
+
/** The prompt's size as the provider counted it, when it said. */
|
|
305
|
+
promptTokens;
|
|
306
|
+
/** The model's window as the provider reported it, when it said. */
|
|
307
|
+
contextWindowTokens;
|
|
308
|
+
constructor(model, promptTokens, contextWindowTokens) {
|
|
309
|
+
const sizes = promptTokens && contextWindowTokens ? ` (${promptTokens} tokens; the window is ${contextWindowTokens})` : "";
|
|
310
|
+
super(
|
|
311
|
+
`The conversation no longer fits the context window of "${model}"${sizes}. Start a new chat or compact this one, connect fewer tools, or switch to a model with a larger window.`
|
|
312
|
+
);
|
|
313
|
+
this.name = "ContextWindowExceededError";
|
|
314
|
+
this.promptTokens = promptTokens;
|
|
315
|
+
this.contextWindowTokens = contextWindowTokens;
|
|
316
|
+
}
|
|
317
|
+
toJSON() {
|
|
318
|
+
return this.message;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
var OVERFLOW_RE = /prompt tokens exceed context window|context window size|context_length_exceeded|maximum context length|prompt is too long|input token count.*exceeds|exceeds the maximum number of tokens|exceeds the available context size|context length exceeded|too many tokens in (the )?(prompt|input)/i;
|
|
322
|
+
var WEBLLM_SIZES_RE = /number of prompt tokens:\s*(\d+);\s*context window size:\s*(\d+)/i;
|
|
323
|
+
var contextOverflowOf = (err, model) => {
|
|
324
|
+
if (err instanceof ContextWindowExceededError) return err;
|
|
325
|
+
const text = err instanceof Error ? `${err.name} ${err.message}` : String(err);
|
|
326
|
+
if (!OVERFLOW_RE.test(text)) return void 0;
|
|
327
|
+
const sizes = WEBLLM_SIZES_RE.exec(text);
|
|
328
|
+
return new ContextWindowExceededError(
|
|
329
|
+
modelLabel(model),
|
|
330
|
+
sizes ? Number(sizes[1]) : void 0,
|
|
331
|
+
sizes ? Number(sizes[2]) : webLLMContextWindow(model) ?? void 0
|
|
332
|
+
);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// src/providers/webllm.ts
|
|
336
|
+
var isWebGPUAvailable = () => typeof navigator !== "undefined" && "gpu" in navigator;
|
|
337
|
+
var preloadWebLLMModel = async (model) => {
|
|
338
|
+
await generateText2({ model, prompt: "ok", maxOutputTokens: 1 });
|
|
339
|
+
};
|
|
340
|
+
var unloadWebLLMModel = async (model) => {
|
|
341
|
+
const engine = model.engine;
|
|
342
|
+
try {
|
|
343
|
+
await engine?.unload?.();
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var createWebLLMModel = async (model, options) => {
|
|
348
|
+
let mod;
|
|
349
|
+
try {
|
|
350
|
+
mod = await import("@browser-ai/web-llm");
|
|
351
|
+
} catch {
|
|
352
|
+
throw new Error(
|
|
353
|
+
'Local models require "@browser-ai/web-llm" and its peer "@mlc-ai/web-llm". Install: npm install @browser-ai/web-llm @mlc-ai/web-llm'
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const { preload = true, contextWindowTokens, ...settings } = options ?? {};
|
|
357
|
+
if (contextWindowTokens) {
|
|
358
|
+
const engineConfig = settings.engineConfig ?? {};
|
|
359
|
+
let base = engineConfig.appConfig;
|
|
360
|
+
if (!base) {
|
|
361
|
+
try {
|
|
362
|
+
const spec = "@mlc-ai/web-llm";
|
|
363
|
+
base = (await import(
|
|
364
|
+
/* webpackIgnore: true */
|
|
365
|
+
/* @vite-ignore */
|
|
366
|
+
spec
|
|
367
|
+
)).prebuiltAppConfig;
|
|
368
|
+
} catch {
|
|
369
|
+
throw new Error(
|
|
370
|
+
'contextWindowTokens needs "@mlc-ai/web-llm" (for its prebuilt model list), or pass engineConfig.appConfig yourself.'
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
settings.engineConfig = {
|
|
375
|
+
...engineConfig,
|
|
376
|
+
appConfig: withWebLLMContextWindow(base, model, contextWindowTokens)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const languageModel = mod.webLLM(
|
|
380
|
+
model,
|
|
381
|
+
settings
|
|
382
|
+
);
|
|
383
|
+
if (preload) await preloadWebLLMModel(languageModel);
|
|
384
|
+
return languageModel;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
export {
|
|
388
|
+
clip,
|
|
389
|
+
promptOf,
|
|
390
|
+
normalizeUsage,
|
|
391
|
+
timeoutSignal,
|
|
392
|
+
generate,
|
|
393
|
+
stream,
|
|
394
|
+
generateStructured,
|
|
395
|
+
resolveCompaction,
|
|
396
|
+
estimateTokens,
|
|
397
|
+
messageTokens,
|
|
398
|
+
compressHistory,
|
|
399
|
+
compactHistory,
|
|
400
|
+
compactSteps,
|
|
401
|
+
mediaTypeOf,
|
|
402
|
+
attachmentKind,
|
|
403
|
+
AttachmentsNotSupportedError,
|
|
404
|
+
ImagesNotSupportedError,
|
|
405
|
+
unsupportedAttachment,
|
|
406
|
+
toFilePart,
|
|
407
|
+
toImagePart,
|
|
408
|
+
modelLabel,
|
|
409
|
+
isAttachmentRefusal,
|
|
410
|
+
isImageRefusal,
|
|
411
|
+
WEBLLM_DEFAULT_CONTEXT_WINDOW,
|
|
412
|
+
withWebLLMContextWindow,
|
|
413
|
+
webLLMContextWindow,
|
|
414
|
+
contextWindowOf,
|
|
415
|
+
fitCompactionToWindow,
|
|
416
|
+
toolDefinitionTokens,
|
|
417
|
+
TOOL_SEARCH_WINDOW_SHARE,
|
|
418
|
+
ContextWindowExceededError,
|
|
419
|
+
contextOverflowOf,
|
|
420
|
+
isWebGPUAvailable,
|
|
421
|
+
preloadWebLLMModel,
|
|
422
|
+
unloadWebLLMModel,
|
|
423
|
+
createWebLLMModel
|
|
424
|
+
};
|
|
425
|
+
//# sourceMappingURL=chunk-YA6TQQ3Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/webllm.ts","../src/context-window.ts","../src/images.ts","../src/llm/generate.ts","../src/llm/util.ts","../src/memory/compress.ts"],"sourcesContent":["import { generateText, type LanguageModel } from 'ai'\nimport { withWebLLMContextWindow, type WebLLMAppConfigLike } from '../context-window.js'\n\n/** WebGPU feature-detect — WebLLM requires it. Call before offering local models. */\nexport const isWebGPUAvailable = (): boolean =>\n typeof navigator !== 'undefined' && 'gpu' in navigator\n\nexport interface WebLLMModelOptions {\n /**\n * Progress callback for weight download / engine init. `report.progress` is\n * 0..1. Forwarded to WebLLM's `initProgressCallback`.\n */\n initProgressCallback?: (report: { progress: number; text: string }) => void\n /**\n * Download the weights and initialize the engine NOW (default true).\n *\n * The underlying provider initializes lazily on the first generation — with\n * a multi-GB model that means the download would silently happen on the\n * user's first message, with `initProgressCallback` never firing at a moment\n * the host can show it. Preloading runs a 1-token warm-up so the returned\n * model is truly ready and progress is reported during creation. Re-creating\n * an already-downloaded model is fast (weights live in the browser cache).\n *\n * Set to `false` to keep the lazy behavior.\n */\n preload?: boolean\n /**\n * The context window to load the model with, in tokens. WebLLM loads its\n * prebuilt models with 4096 (to save VRAM), far below what Qwen3 or Llama\n * 3.x were trained on; a larger window costs KV-cache memory, not a new\n * download. Applied through `engineConfig.appConfig` (WebLLM's prebuilt\n * config with this model's `context_window_size` overridden) unless you pass\n * an `appConfig` of your own; see `withWebLLMContextWindow`.\n */\n contextWindowTokens?: number\n /** Any other @browser-ai/web-llm setting (temperature, worker handler, …). */\n [k: string]: unknown\n}\n\n/**\n * Force a model to download its weights and initialize its engine by running\n * a 1-token generation. Exposed for hosts that create models with\n * `preload: false` and want to warm them up later (e.g. behind their own UI).\n */\nexport const preloadWebLLMModel = async (model: LanguageModel): Promise<void> => {\n await generateText({ model, prompt: 'ok', maxOutputTokens: 1 })\n}\n\n/**\n * Best-effort release of a model's GPU memory (call when switching models —\n * an abandoned engine otherwise holds its buffers until the page reloads).\n *\n * `@browser-ai/web-llm` keeps its MLCEngine private and exposes no public\n * `unload()`, so this reaches into the instance defensively; it is a no-op on\n * an uninitialized model or if the provider's internals change shape.\n */\nexport const unloadWebLLMModel = async (model: LanguageModel): Promise<void> => {\n const engine = (model as { engine?: { unload?: () => Promise<void> } }).engine\n try {\n await engine?.unload?.()\n } catch {\n /* best-effort */\n }\n}\n\n/**\n * Build an AI SDK `LanguageModel` backed by WebLLM (WebGPU) via the community\n * provider `@browser-ai/web-llm` (an optional peer, dynamically imported so it\n * is code-split and never loads until a local model is actually used). Accepts\n * any WebLLM prebuilt model id, e.g. \"Llama-3.2-3B-Instruct-q4f16_1-MLC\".\n *\n * By default the weights are downloaded eagerly with progress (see\n * `WebLLMModelOptions.preload`).\n */\nexport const createWebLLMModel = async (\n model: string,\n options?: WebLLMModelOptions,\n): Promise<LanguageModel> => {\n let mod: typeof import('@browser-ai/web-llm')\n try {\n mod = await import('@browser-ai/web-llm')\n } catch {\n throw new Error(\n 'Local models require \"@browser-ai/web-llm\" and its peer \"@mlc-ai/web-llm\". Install: npm install @browser-ai/web-llm @mlc-ai/web-llm',\n )\n }\n const { preload = true, contextWindowTokens, ...settings } = options ?? {}\n if (contextWindowTokens) {\n const engineConfig = (settings.engineConfig ?? {}) as { appConfig?: WebLLMAppConfigLike }\n let base = engineConfig.appConfig\n if (!base) {\n try {\n const spec = '@mlc-ai/web-llm'\n base = (\n (await import(/* webpackIgnore: true */ /* @vite-ignore */ spec)) as {\n prebuiltAppConfig: WebLLMAppConfigLike\n }\n ).prebuiltAppConfig\n } catch {\n throw new Error(\n 'contextWindowTokens needs \"@mlc-ai/web-llm\" (for its prebuilt model list), or pass engineConfig.appConfig yourself.',\n )\n }\n }\n settings.engineConfig = {\n ...engineConfig,\n appConfig: withWebLLMContextWindow(base, model, contextWindowTokens),\n }\n }\n // webLLM(modelId, settings) returns a LanguageModel. The model id is a strict\n // union in the provider's types; we accept any string and cast at the seam.\n const languageModel = mod.webLLM(\n model as Parameters<typeof mod.webLLM>[0],\n settings as Parameters<typeof mod.webLLM>[1],\n )\n if (preload) await preloadWebLLMModel(languageModel)\n return languageModel\n}\n","import { asSchema, type ToolSet } from 'ai'\nimport { modelLabel } from './images.js'\nimport {\n estimateTokens,\n type CompactionConfig,\n type ResolvedCompaction,\n} from './memory/compress.js'\n\n/**\n * The model's context window is a hard limit: a prompt that does not fit is\n * refused outright (WebLLM: \"Prompt tokens exceed context window size\"). The\n * agent sizes everything that grows — compaction thresholds, tool-result\n * clearing, the cap on one tool result, whether tools are listed or searched —\n * from the window, so it has to be the model's real one.\n *\n * Cloud windows are large and the host states them (`compaction.\n * contextWindowTokens`); a local WebLLM model's window is small (4096 tokens\n * by default — WebLLM's choice to save VRAM, not the model's limit) and is read\n * from the model itself.\n */\n\ninterface WebLLMRecordLike {\n model_id: string\n overrides?: { context_window_size?: number } & Record<string, unknown>\n}\n\n/** The shape of a WebLLM `AppConfig` this module touches. */\nexport interface WebLLMAppConfigLike {\n model_list: WebLLMRecordLike[]\n}\n\n/** WebLLM's default window for its prebuilt models. */\nexport const WEBLLM_DEFAULT_CONTEXT_WINDOW = 4096\n\n/**\n * A copy of a WebLLM app config (e.g. `prebuiltAppConfig` from\n * `@mlc-ai/web-llm`) with one model's context window set. Pass it as\n * `engineConfig.appConfig` when building the model: the weights are the same,\n * only the KV cache (VRAM) grows with the window. Models trained on long\n * contexts (Qwen3, Llama 3.x) take far more than the default 4096; models\n * trained on 4k (Llama 2) don't.\n */\nexport const withWebLLMContextWindow = <T extends WebLLMAppConfigLike>(\n appConfig: T,\n modelId: string,\n tokens: number,\n): T => ({\n ...appConfig,\n model_list: appConfig.model_list.map((r) =>\n r.model_id === modelId\n ? { ...r, overrides: { ...r.overrides, context_window_size: tokens } }\n : r,\n ),\n})\n\ninterface WebLLMModelLike {\n provider?: string\n modelId?: string\n engine?: {\n loadedModelIdToPipeline?: { get?: (id: string) => { contextWindowSize?: number } | undefined }\n }\n config?: { options?: { engineConfig?: { appConfig?: WebLLMAppConfigLike } } }\n}\n\n/**\n * The context window of a WebLLM model (`@browser-ai/web-llm`), or undefined\n * for any other model. Read from the loaded engine when it runs in this\n * thread, else from the app config it was built with, else WebLLM's defaults\n * (1024 for its \"-1k\" builds, 4096 otherwise). The provider keeps these\n * private, so this reaches in defensively.\n */\nexport const webLLMContextWindow = (model: unknown): number | undefined => {\n const m = model as WebLLMModelLike | undefined\n if (!m || typeof m !== 'object' || m.provider !== 'web-llm') return undefined\n const id = String(m.modelId ?? '')\n try {\n const live = m.engine?.loadedModelIdToPipeline?.get?.(id)?.contextWindowSize\n if (typeof live === 'number' && live > 0) return live\n } catch {\n /* the engine lives in a worker, or changed shape */\n }\n const record = m.config?.options?.engineConfig?.appConfig?.model_list?.find(\n (r) => r.model_id === id,\n )\n const configured = record?.overrides?.context_window_size\n if (typeof configured === 'number' && configured > 0) return configured\n return /-1k$/i.test(id) ? 1024 : WEBLLM_DEFAULT_CONTEXT_WINDOW\n}\n\n/**\n * The model's own context window when it can be known (local models), else\n * undefined: WebLLM's (see above), or the browser's built-in model's\n * (`@browser-ai/core` reports it once its session exists).\n */\nexport const contextWindowOf = (model: unknown): number | undefined => {\n const webllm = webLLMContextWindow(model)\n if (webllm !== undefined) return webllm\n const m = model as { provider?: string; getContextWindow?: () => number | undefined } | undefined\n if (m && typeof m === 'object' && m.provider === 'browser-ai') {\n try {\n const w = m.getContextWindow?.()\n return typeof w === 'number' && w > 0 ? w : undefined\n } catch {\n return undefined\n }\n }\n return undefined\n}\n\n/**\n * Fit the compaction settings to the model's window: the window in use is the\n * configured one, never larger than the model's own, and every size derived\n * from it shrinks with it — also a size the host set for a larger window.\n */\nexport const fitCompactionToWindow = (\n resolved: ResolvedCompaction,\n configured: CompactionConfig | undefined,\n modelWindow: number | undefined,\n): ResolvedCompaction => {\n const window = Math.min(\n configured?.contextWindowTokens ?? modelWindow ?? resolved.contextWindowTokens,\n modelWindow ?? Infinity,\n )\n if (window === resolved.contextWindowTokens && modelWindow === undefined) return resolved\n const half = Math.floor(window / 2)\n const quarter = Math.floor(window / 4)\n const clearAfter = configured?.clearToolResultsAfterTokens\n return {\n ...resolved,\n contextWindowTokens: window,\n thresholdTokens: Math.min(configured?.thresholdTokens ?? half, half),\n // 0 = never stays never.\n clearToolResultsAfterTokens: clearAfter === 0 ? 0 : Math.min(clearAfter ?? quarter, quarter),\n // One result may take a quarter of the window (~4 chars per token).\n maxToolOutputChars:\n configured?.maxToolOutputChars === 0\n ? 0\n : Math.min(configured?.maxToolOutputChars ?? 20_000, window),\n // A small window holds fewer verbatim results.\n keepToolResults:\n configured?.keepToolResults ?? Math.max(1, Math.min(3, Math.floor(window / 8192))),\n }\n}\n\n/**\n * Estimated tokens of the tool definitions a model is sent (name, description,\n * JSON schema of the input) — for tools natively, or rendered into the prompt.\n */\nexport const toolDefinitionTokens = async (tools: ToolSet): Promise<number> => {\n const sizes = await Promise.all(\n Object.entries(tools).map(async ([name, t]) => {\n let parameters: unknown\n try {\n parameters = await asSchema(t.inputSchema as never).jsonSchema\n } catch {\n parameters = undefined\n }\n return estimateTokens(JSON.stringify({ name, description: t.description ?? '', parameters }))\n }),\n )\n return sizes.reduce((a, b) => a + b, 0)\n}\n\n/** 'auto' switches to tool search once the definitions would take this share of the window. */\nexport const TOOL_SEARCH_WINDOW_SHARE = 0.25\n\n/** Raised when a prompt does not fit the model's context window. */\nexport class ContextWindowExceededError extends Error {\n /** The prompt's size as the provider counted it, when it said. */\n readonly promptTokens?: number\n /** The model's window as the provider reported it, when it said. */\n readonly contextWindowTokens?: number\n constructor(model: string, promptTokens?: number, contextWindowTokens?: number) {\n const sizes =\n promptTokens && contextWindowTokens\n ? ` (${promptTokens} tokens; the window is ${contextWindowTokens})`\n : ''\n super(\n `The conversation no longer fits the context window of \"${model}\"${sizes}. Start a new chat or compact this one, connect fewer tools, or switch to a model with a larger window.`,\n )\n this.name = 'ContextWindowExceededError'\n this.promptTokens = promptTokens\n this.contextWindowTokens = contextWindowTokens\n }\n\n toJSON(): string {\n return this.message\n }\n}\n\n// What providers say when the prompt is too long: WebLLM, OpenAI\n// (context_length_exceeded), Anthropic, Gemini, llama.cpp, vLLM / OpenRouter.\nconst OVERFLOW_RE =\n /prompt tokens exceed context window|context window size|context_length_exceeded|maximum context length|prompt is too long|input token count.*exceeds|exceeds the maximum number of tokens|exceeds the available context size|context length exceeded|too many tokens in (the )?(prompt|input)/i\nconst WEBLLM_SIZES_RE = /number of prompt tokens:\\s*(\\d+);\\s*context window size:\\s*(\\d+)/i\n\n/**\n * The clear error to raise when a provider refused a prompt for its length\n * (else undefined) — instead of the provider's raw text.\n */\nexport const contextOverflowOf = (\n err: unknown,\n model: unknown,\n): ContextWindowExceededError | undefined => {\n if (err instanceof ContextWindowExceededError) return err\n const text = err instanceof Error ? `${err.name} ${err.message}` : String(err)\n if (!OVERFLOW_RE.test(text)) return undefined\n const sizes = WEBLLM_SIZES_RE.exec(text)\n return new ContextWindowExceededError(\n modelLabel(model),\n sizes ? Number(sizes[1]) : undefined,\n sizes ? Number(sizes[2]) : (webLLMContextWindow(model) ?? undefined),\n )\n}\n","import type { FilePart } from 'ai'\n\n/**\n * Attachments sent with a run: images, PDFs and other files the provider can\n * read, given as bytes / base64 / a data URL — or as an http(s) URL, which\n * providers that accept links (Gemini, Claude, OpenAI) fetch themselves and the\n * AI SDK downloads for the rest (from the browser: subject to CORS).\n */\nexport interface RunFile {\n /** Base64 (no prefix), a data URL (\"data:image/png;base64,…\"), raw bytes, or an http(s) URL. */\n data: string | Uint8Array | URL\n /** e.g. \"image/png\", \"application/pdf\" — read from a data URL when omitted. */\n mediaType?: string\n /** Shown in the transcript, in memory and in errors. */\n name?: string\n}\n\n/** An image attachment (same shape; kept as its own name for clarity). */\nexport type RunImage = RunFile\n\n/** The kinds of attachment the agent reasons about separately. */\nexport type AttachmentKind = 'image' | 'pdf' | 'file'\n\nconst DATA_URL_RE = /^data:([^;,]+)?;base64,(.*)$/s\nconst HTTP_RE = /^https?:\\/\\//i\n\n/** The media type of an attachment (declared, from a data URL, or from a URL's extension). */\nexport const mediaTypeOf = (f: RunFile): string | undefined => {\n if (f.mediaType) return f.mediaType\n if (typeof f.data === 'string') {\n const m = DATA_URL_RE.exec(f.data)\n if (m?.[1]) return m[1]\n }\n const url = f.data instanceof URL ? f.data.href : typeof f.data === 'string' ? f.data : ''\n if (HTTP_RE.test(url) || f.name) {\n const ext = (f.name ?? new URL(url, 'http://x').pathname).split('.').pop()?.toLowerCase()\n if (ext === 'pdf') return 'application/pdf'\n if (ext && ['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext)) {\n return `image/${ext === 'jpg' ? 'jpeg' : ext}`\n }\n }\n return undefined\n}\n\nexport const attachmentKind = (f: RunFile): AttachmentKind => {\n const mt = mediaTypeOf(f) ?? ''\n if (mt.startsWith('image/')) return 'image'\n if (mt === 'application/pdf') return 'pdf'\n return 'file'\n}\n\nconst KIND_LABEL: Record<AttachmentKind, string> = {\n image: 'images',\n pdf: 'PDF files',\n file: 'file attachments',\n}\n\nconst KIND_HINT: Record<AttachmentKind, string> = {\n image:\n 'Remove the image, or switch to a vision-capable model such as Gemini, Claude or GPT-4o/GPT-5.',\n pdf: 'Convert the PDF to text first (e.g. PDF → Markdown), or switch to a model that reads PDFs such as Gemini, Claude or GPT-4o/GPT-5.',\n file: 'Paste its text instead, or switch to a model that accepts this file type.',\n}\n\n/** Raised when attachments are sent to a model that cannot take them. */\nexport class AttachmentsNotSupportedError extends Error {\n readonly kind: AttachmentKind\n constructor(model: string, kind: AttachmentKind, detail?: string) {\n super(\n `The model \"${model}\" can't take ${KIND_LABEL[kind]}${detail ? ` (${detail})` : ''}. ${KIND_HINT[kind]}`,\n )\n this.name = 'AttachmentsNotSupportedError'\n this.kind = kind\n }\n\n toJSON(): string {\n return this.message\n }\n}\n\n/** The image flavour of {@link AttachmentsNotSupportedError}. */\nexport class ImagesNotSupportedError extends AttachmentsNotSupportedError {\n constructor(model: string, detail?: string) {\n super(model, 'image', detail)\n this.name = 'ImagesNotSupportedError'\n }\n}\n\n/** The error for a kind: images get the dedicated class. */\nexport const unsupportedAttachment = (\n model: string,\n kind: AttachmentKind,\n detail?: string,\n): AttachmentsNotSupportedError =>\n kind === 'image'\n ? new ImagesNotSupportedError(model, detail)\n : new AttachmentsNotSupportedError(model, kind, detail)\n\nconst dataOf = (f: RunFile): string | Uint8Array | URL => {\n if (typeof f.data === 'string') {\n const m = DATA_URL_RE.exec(f.data)\n if (m) return m[2]\n if (HTTP_RE.test(f.data)) return new URL(f.data)\n }\n return f.data\n}\n\n/**\n * An AI SDK file part (images included — the v7 SDK deprecates the separate\n * image part). An http(s) URL is passed as a URL: providers that accept links\n * fetch it themselves, and the SDK downloads it for the rest.\n */\nexport const toFilePart = (f: RunFile): FilePart => {\n const kind = attachmentKind(f)\n const mediaType = mediaTypeOf(f) ?? (kind === 'image' ? 'image' : 'application/octet-stream')\n return {\n type: 'file',\n data: dataOf(f),\n mediaType,\n ...(f.name ? { filename: f.name } : {}),\n }\n}\n\n/** Back-compat name. */\nexport const toImagePart = toFilePart\n\n/** A short, human label of a model for messages. */\nexport const modelLabel = (model: unknown): string => {\n if (typeof model === 'string') return model\n const m = model as { provider?: string; modelId?: string } | undefined\n return m?.modelId ? `${m.modelId}${m.provider ? ` (${m.provider})` : ''}` : 'this model'\n}\n\nconst REFUSAL_RE =\n /image|vision|multimodal|multi-modal|media type|mediatype|pdf|document|unsupported (file|content|part|mime)|does not support (file|content)|invalid content type|image_url|inline_?data|file_?data/i\n\n/** Does a provider error look like a refusal of attachment input? */\nexport const isAttachmentRefusal = (err: unknown): boolean =>\n REFUSAL_RE.test(err instanceof Error ? `${err.name} ${err.message}` : String(err))\n\n/** Back-compat name. */\nexport const isImageRefusal = isAttachmentRefusal\n","import {\n generateObject,\n generateText,\n streamText,\n type LanguageModel,\n type ModelMessage,\n type SystemModelMessage,\n type ToolSet,\n} from 'ai'\nimport type { ZodType } from 'zod'\nimport type { ProviderOptionsMap, ThinkingLevel } from '../thinking.js'\nimport { promptOf, timeoutSignal } from './util.js'\n\n/** Options shared by the low-level generation helpers. Provide `prompt` OR `messages`. */\nexport interface GenerateOptions {\n /** The system prompt — a string, or a SystemModelMessage (e.g. with a cache breakpoint). */\n system?: string | SystemModelMessage\n prompt?: string\n messages?: ModelMessage[]\n tools?: ToolSet\n maxOutputTokens?: number\n temperature?: number\n /** Portable thinking level (see `resolveThinking`). */\n reasoning?: ThinkingLevel\n /** Provider-keyed options (thinking budgets, cache keys, …). */\n providerOptions?: ProviderOptionsMap\n abortSignal?: AbortSignal\n /** Time-box the call; combined with `abortSignal` (0/undefined = no timeout). */\n timeoutMs?: number\n}\n\n/** The settings every helper forwards; omits undefined keys the SDK would reject. */\nconst callSettings = (opts: GenerateOptions) => ({\n ...(opts.system !== undefined ? { instructions: opts.system } : {}),\n maxOutputTokens: opts.maxOutputTokens,\n temperature: opts.temperature,\n ...(opts.reasoning ? { reasoning: opts.reasoning } : {}),\n ...(opts.providerOptions ? { providerOptions: opts.providerOptions as never } : {}),\n abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs),\n})\n\n/**\n * One-shot text generation. Thin, provider-agnostic wrapper over the AI SDK's\n * `generateText` — the same call works against a cloud model, a local WebLLM\n * model, or Chrome/Edge built-in AI. Returns the full AI SDK result (`.text`,\n * `.usage`, `.toolCalls`, `.steps`, …).\n */\nexport const generate = (\n model: LanguageModel,\n opts: GenerateOptions = {},\n): ReturnType<typeof generateText> =>\n generateText({\n model,\n ...callSettings(opts),\n ...promptOf(opts),\n tools: opts.tools,\n })\n\n/** Streaming text generation. Returns the AI SDK `streamText` result (`.textStream`, `.fullStream`). */\nexport const stream = (\n model: LanguageModel,\n opts: GenerateOptions = {},\n): ReturnType<typeof streamText> =>\n streamText({\n model,\n ...callSettings(opts),\n ...promptOf(opts),\n tools: opts.tools,\n })\n\n/**\n * Structured output constrained to a Zod schema, via the AI SDK's\n * `generateObject`. Cloud models and WebLLM both support this; tiny local\n * models can be unreliable — the agent's prompted mode is the fallback.\n * Returns the AI SDK result (`.object`, `.usage`).\n */\nexport const generateStructured = <OBJECT>(\n model: LanguageModel,\n schema: ZodType<OBJECT>,\n opts: GenerateOptions = {},\n) =>\n generateObject({\n model,\n schema,\n ...callSettings(opts),\n ...promptOf(opts),\n })\n","import type { ModelMessage } from 'ai'\nimport type { IUsage } from '../agent/loop-types.js'\n\n/** Truncate to `max` characters, ellipsising the overflow. */\nexport const clip = (s: string, max: number): string =>\n s.length > max ? `${s.slice(0, max - 1)}…` : s\n\n/**\n * The AI SDK prompt is `{ prompt } XOR { messages }` — passing both keys (even\n * with one undefined) breaks the discriminated union. Pick exactly one.\n */\nexport const promptOf = (opts: {\n prompt?: string\n messages?: ModelMessage[]\n}): { prompt: string } | { messages: ModelMessage[] } =>\n opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? '' }\n\n/** The AI SDK usage shape, every field optional (providers omit what they don't report). */\nexport interface SdkUsageLike {\n inputTokens?: number\n outputTokens?: number\n totalTokens?: number\n inputTokenDetails?: { cacheReadTokens?: number; cacheWriteTokens?: number }\n outputTokenDetails?: { reasoningTokens?: number }\n /** Pre-v6 flat fields, still emitted by some providers. */\n reasoningTokens?: number\n cachedInputTokens?: number\n}\n\n/** Normalise the AI SDK usage shape (fields can be undefined) into IUsage. */\nexport const normalizeUsage = (u?: SdkUsageLike): IUsage => {\n const inputTokens = u?.inputTokens ?? 0\n const outputTokens = u?.outputTokens ?? 0\n return {\n inputTokens,\n outputTokens,\n totalTokens: u?.totalTokens ?? inputTokens + outputTokens,\n reasoningTokens: u?.outputTokenDetails?.reasoningTokens ?? u?.reasoningTokens ?? 0,\n cachedInputTokens: u?.inputTokenDetails?.cacheReadTokens ?? u?.cachedInputTokens ?? 0,\n cacheWriteTokens: u?.inputTokenDetails?.cacheWriteTokens ?? 0,\n }\n}\n\n/**\n * Combine the caller's AbortSignal with a timeout so a hung generation can't\n * freeze a run. Returns the original signal when no timeout is set. Uses the\n * standard `AbortSignal.timeout`/`AbortSignal.any` (Node ≥ 20, modern browsers).\n */\nexport const timeoutSignal = (signal?: AbortSignal, ms?: number): AbortSignal | undefined => {\n if (!ms || ms <= 0) return signal\n const timeout = AbortSignal.timeout(ms)\n if (!signal) return timeout\n return typeof AbortSignal.any === 'function' ? AbortSignal.any([signal, timeout]) : signal\n}\n","import type { LanguageModel } from 'ai'\nimport { generate } from '../llm/generate.js'\nimport { normalizeUsage } from '../llm/util.js'\nimport type { IUsage } from '../agent/loop-types.js'\nimport type { StoredMessage } from './store.js'\n\n/**\n * Context compaction. A model's window is finite and every token in it is paid\n * for on every call, so long transcripts and long runs are folded into a\n * summary: the oldest material is summarised by a model, the most recent is\n * kept verbatim.\n */\nexport interface CompactionConfig {\n /** Compact automatically before planning and between steps (default true). */\n auto?: boolean\n /** The model's context window, in tokens (default 128 000). */\n contextWindowTokens?: number\n /** Compact once the estimated context exceeds this (default: half the window). */\n thresholdTokens?: number\n /** Transcript messages kept verbatim (default 4). */\n keepRecentTurns?: number\n /** Executed steps kept verbatim in the run's trace (default 3). */\n keepRecentSteps?: number\n /** Output cap of a summary call (default 1024). */\n summaryMaxTokens?: number\n /** Cap on ONE tool result as the model sees it, in chars (default 20 000; 0 = no cap). */\n maxToolOutputChars?: number\n /**\n * Inside one step's tool loop: once its context passes this many tokens,\n * replace the oldest tool results with one-line stubs (context editing;\n * default: a quarter of the window; 0 = never).\n */\n clearToolResultsAfterTokens?: number\n /** Most recent tool results kept verbatim when clearing (default 3). */\n keepToolResults?: number\n}\n\nexport interface ResolvedCompaction {\n auto: boolean\n contextWindowTokens: number\n thresholdTokens: number\n keepRecentTurns: number\n keepRecentSteps: number\n summaryMaxTokens: number\n maxToolOutputChars: number\n clearToolResultsAfterTokens: number\n keepToolResults: number\n}\n\nexport const resolveCompaction = (c: CompactionConfig | undefined): ResolvedCompaction => {\n const contextWindowTokens = c?.contextWindowTokens ?? 128_000\n return {\n auto: c?.auto ?? true,\n contextWindowTokens,\n thresholdTokens: c?.thresholdTokens ?? Math.floor(contextWindowTokens / 2),\n keepRecentTurns: c?.keepRecentTurns ?? 4,\n keepRecentSteps: c?.keepRecentSteps ?? 3,\n summaryMaxTokens: c?.summaryMaxTokens ?? 1024,\n maxToolOutputChars: c?.maxToolOutputChars ?? 20_000,\n clearToolResultsAfterTokens:\n c?.clearToolResultsAfterTokens ?? Math.floor(contextWindowTokens / 4),\n keepToolResults: c?.keepToolResults ?? 3,\n }\n}\n\n/** Cheap, provider-independent token estimate (~4 chars per token). */\nexport const estimateTokens = (text: string): number => Math.ceil((text ?? '').length / 4)\n\nconst messageTokens = (msgs: StoredMessage[]): number =>\n msgs.reduce((n, m) => n + estimateTokens(`${m.role}: ${m.content}\\n`), 0)\n\nexport interface CompressOptions {\n /** Compress once the transcript exceeds this many characters (0 = never). */\n maxChars?: number\n /** Compress once the transcript's estimated tokens exceed this (wins over maxChars). */\n thresholdTokens?: number\n /** How many most-recent messages to keep verbatim. */\n keepRecent?: number\n /** Output cap of the summary call. */\n summaryMaxTokens?: number\n /** Compact even when below the threshold (manual \"compact now\"). */\n force?: boolean\n /** Time-box the summarisation call so it can never hang the caller. */\n timeoutMs?: number\n abortSignal?: AbortSignal\n /** Receives the summary call's token usage. */\n onUsage?: (usage: IUsage) => void\n}\n\nconst SUMMARY_SYSTEM =\n 'Summarize the following conversation into a compact set of durable facts, decisions, ' +\n 'identifiers, open tasks and user preferences. Keep names, numbers and IDs verbatim. ' +\n 'Output plain text only.'\n\n/**\n * When the transcript grows too long, summarise everything except the last\n * `keepRecent` messages into a single system message (via the model), so the\n * session stays within the model's context window. Returns the same array\n * unchanged when no compression is needed; never throws.\n */\nexport const compressHistory = async (\n messages: StoredMessage[],\n model: LanguageModel,\n opts: CompressOptions = {},\n): Promise<StoredMessage[]> => {\n const keepRecent = opts.keepRecent ?? 6\n if (messages.length <= keepRecent + 1) return messages\n if (!opts.force) {\n if (opts.thresholdTokens !== undefined) {\n if (opts.thresholdTokens <= 0 || messageTokens(messages) <= opts.thresholdTokens) {\n return messages\n }\n } else {\n const maxChars = opts.maxChars ?? 12_000\n const chars = messages.reduce((n, m) => n + m.content.length, 0)\n if (maxChars <= 0 || chars <= maxChars) return messages\n }\n }\n const head = messages.slice(0, messages.length - keepRecent)\n const tail = messages.slice(messages.length - keepRecent)\n // The newest part of the head matters most; keep the tail end of it when it\n // has to be cut to fit the summariser's own window.\n const transcript = head\n .map((m) => `${m.role}: ${m.content}`)\n .join('\\n')\n .slice(-48_000)\n try {\n const result = await generate(model, {\n system: SUMMARY_SYSTEM,\n prompt: transcript,\n maxOutputTokens: opts.summaryMaxTokens ?? 400,\n timeoutMs: opts.timeoutMs,\n abortSignal: opts.abortSignal,\n })\n opts.onUsage?.(normalizeUsage(result.usage))\n const clean = (result.text || '').trim()\n if (!clean) return messages\n const summaryMsg: StoredMessage = {\n role: 'system',\n content: `Summary of earlier conversation:\\n${clean}`,\n ts: head[0]?.ts,\n }\n return [summaryMsg, ...tail]\n } catch {\n return messages\n }\n}\n\n/** Alias with the sibling package's name; same behaviour as compressHistory. */\nexport const compactHistory = compressHistory\n\nconst TRACE_SYSTEM =\n 'Summarize these completed agent steps into a compact progress note: what was done, ' +\n 'what was found (keep names, numbers and IDs verbatim) and what failed. Plain text only.'\n\n/**\n * Fold the oldest entries of a run's step log into one summary entry, keeping\n * the last `keepRecent` verbatim. Returns the input unchanged when it fits the\n * threshold or on any failure; never throws.\n */\nexport const compactSteps = async (\n done: string[],\n model: LanguageModel,\n opts: CompressOptions & { thresholdTokens: number },\n): Promise<string[]> => {\n const keepRecent = opts.keepRecent ?? 3\n if (done.length <= keepRecent + 1) return done\n if (!opts.force && estimateTokens(done.join('\\n')) <= opts.thresholdTokens) return done\n const head = done.slice(0, done.length - keepRecent)\n try {\n const result = await generate(model, {\n system: TRACE_SYSTEM,\n prompt: head.map((d, i) => `${i + 1}. ${d}`).join('\\n'),\n maxOutputTokens: opts.summaryMaxTokens ?? 600,\n timeoutMs: opts.timeoutMs,\n abortSignal: opts.abortSignal,\n })\n opts.onUsage?.(normalizeUsage(result.usage))\n const clean = (result.text || '').trim()\n if (!clean) return done\n return [`Summary of ${head.length} earlier step(s): ${clean}`, ...done.slice(head.length)]\n } catch {\n return done\n }\n}\n\nexport { messageTokens as estimateMessagesTokens }\n"],"mappings":";AAAA,SAAS,gBAAAA,qBAAwC;;;ACAjD,SAAS,gBAA8B;;;ACuBvC,IAAM,cAAc;AACpB,IAAM,UAAU;AAGT,IAAM,cAAc,CAAC,MAAmC;AAC7D,MAAI,EAAE,UAAW,QAAO,EAAE;AAC1B,MAAI,OAAO,EAAE,SAAS,UAAU;AAC9B,UAAM,IAAI,YAAY,KAAK,EAAE,IAAI;AACjC,QAAI,IAAI,CAAC,EAAG,QAAO,EAAE,CAAC;AAAA,EACxB;AACA,QAAM,MAAM,EAAE,gBAAgB,MAAM,EAAE,KAAK,OAAO,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AACxF,MAAI,QAAQ,KAAK,GAAG,KAAK,EAAE,MAAM;AAC/B,UAAM,OAAO,EAAE,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,UAAU,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AACxF,QAAI,QAAQ,MAAO,QAAO;AAC1B,QAAI,OAAO,CAAC,OAAO,OAAO,QAAQ,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AAC9D,aAAO,SAAS,QAAQ,QAAQ,SAAS,GAAG;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAAC,MAA+B;AAC5D,QAAM,KAAK,YAAY,CAAC,KAAK;AAC7B,MAAI,GAAG,WAAW,QAAQ,EAAG,QAAO;AACpC,MAAI,OAAO,kBAAmB,QAAO;AACrC,SAAO;AACT;AAEA,IAAM,aAA6C;AAAA,EACjD,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AACR;AAEA,IAAM,YAA4C;AAAA,EAChD,OACE;AAAA,EACF,KAAK;AAAA,EACL,MAAM;AACR;AAGO,IAAM,+BAAN,cAA2C,MAAM;AAAA,EAC7C;AAAA,EACT,YAAY,OAAe,MAAsB,QAAiB;AAChE;AAAA,MACE,cAAc,KAAK,gBAAgB,WAAW,IAAI,CAAC,GAAG,SAAS,KAAK,MAAM,MAAM,EAAE,KAAK,UAAU,IAAI,CAAC;AAAA,IACxG;AACA,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AACF;AAGO,IAAM,0BAAN,cAAsC,6BAA6B;AAAA,EACxE,YAAY,OAAe,QAAiB;AAC1C,UAAM,OAAO,SAAS,MAAM;AAC5B,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,wBAAwB,CACnC,OACA,MACA,WAEA,SAAS,UACL,IAAI,wBAAwB,OAAO,MAAM,IACzC,IAAI,6BAA6B,OAAO,MAAM,MAAM;AAE1D,IAAM,SAAS,CAAC,MAA0C;AACxD,MAAI,OAAO,EAAE,SAAS,UAAU;AAC9B,UAAM,IAAI,YAAY,KAAK,EAAE,IAAI;AACjC,QAAI,EAAG,QAAO,EAAE,CAAC;AACjB,QAAI,QAAQ,KAAK,EAAE,IAAI,EAAG,QAAO,IAAI,IAAI,EAAE,IAAI;AAAA,EACjD;AACA,SAAO,EAAE;AACX;AAOO,IAAM,aAAa,CAAC,MAAyB;AAClD,QAAM,OAAO,eAAe,CAAC;AAC7B,QAAM,YAAY,YAAY,CAAC,MAAM,SAAS,UAAU,UAAU;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,OAAO,CAAC;AAAA,IACd;AAAA,IACA,GAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;AAAA,EACvC;AACF;AAGO,IAAM,cAAc;AAGpB,IAAM,aAAa,CAAC,UAA2B;AACpD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,IAAI;AACV,SAAO,GAAG,UAAU,GAAG,EAAE,OAAO,GAAG,EAAE,WAAW,KAAK,EAAE,QAAQ,MAAM,EAAE,KAAK;AAC9E;AAEA,IAAM,aACJ;AAGK,IAAM,sBAAsB,CAAC,QAClC,WAAW,KAAK,eAAe,QAAQ,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,OAAO,GAAG,CAAC;AAG5E,IAAM,iBAAiB;;;AC7I9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;;;ACJA,IAAM,OAAO,CAAC,GAAW,QAC9B,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,WAAM;AAMxC,IAAM,WAAW,CAAC,SAIvB,KAAK,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,EAAE,QAAQ,KAAK,UAAU,GAAG;AAerE,IAAM,iBAAiB,CAAC,MAA6B;AAC1D,QAAM,cAAc,GAAG,eAAe;AACtC,QAAM,eAAe,GAAG,gBAAgB;AACxC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,GAAG,eAAe,cAAc;AAAA,IAC7C,iBAAiB,GAAG,oBAAoB,mBAAmB,GAAG,mBAAmB;AAAA,IACjF,mBAAmB,GAAG,mBAAmB,mBAAmB,GAAG,qBAAqB;AAAA,IACpF,kBAAkB,GAAG,mBAAmB,oBAAoB;AAAA,EAC9D;AACF;AAOO,IAAM,gBAAgB,CAAC,QAAsB,OAAyC;AAC3F,MAAI,CAAC,MAAM,MAAM,EAAG,QAAO;AAC3B,QAAM,UAAU,YAAY,QAAQ,EAAE;AACtC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,YAAY,QAAQ,aAAa,YAAY,IAAI,CAAC,QAAQ,OAAO,CAAC,IAAI;AACtF;;;ADrBA,IAAM,eAAe,CAAC,UAA2B;AAAA,EAC/C,GAAI,KAAK,WAAW,SAAY,EAAE,cAAc,KAAK,OAAO,IAAI,CAAC;AAAA,EACjE,iBAAiB,KAAK;AAAA,EACtB,aAAa,KAAK;AAAA,EAClB,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,EACtD,GAAI,KAAK,kBAAkB,EAAE,iBAAiB,KAAK,gBAAyB,IAAI,CAAC;AAAA,EACjF,aAAa,cAAc,KAAK,aAAa,KAAK,SAAS;AAC7D;AAQO,IAAM,WAAW,CACtB,OACA,OAAwB,CAAC,MAEzB,aAAa;AAAA,EACX;AAAA,EACA,GAAG,aAAa,IAAI;AAAA,EACpB,GAAG,SAAS,IAAI;AAAA,EAChB,OAAO,KAAK;AACd,CAAC;AAGI,IAAM,SAAS,CACpB,OACA,OAAwB,CAAC,MAEzB,WAAW;AAAA,EACT;AAAA,EACA,GAAG,aAAa,IAAI;AAAA,EACpB,GAAG,SAAS,IAAI;AAAA,EAChB,OAAO,KAAK;AACd,CAAC;AAQI,IAAM,qBAAqB,CAChC,OACA,QACA,OAAwB,CAAC,MAEzB,eAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA,GAAG,aAAa,IAAI;AAAA,EACpB,GAAG,SAAS,IAAI;AAClB,CAAC;;;AErCI,IAAM,oBAAoB,CAAC,MAAwD;AACxF,QAAM,sBAAsB,GAAG,uBAAuB;AACtD,SAAO;AAAA,IACL,MAAM,GAAG,QAAQ;AAAA,IACjB;AAAA,IACA,iBAAiB,GAAG,mBAAmB,KAAK,MAAM,sBAAsB,CAAC;AAAA,IACzE,iBAAiB,GAAG,mBAAmB;AAAA,IACvC,iBAAiB,GAAG,mBAAmB;AAAA,IACvC,kBAAkB,GAAG,oBAAoB;AAAA,IACzC,oBAAoB,GAAG,sBAAsB;AAAA,IAC7C,6BACE,GAAG,+BAA+B,KAAK,MAAM,sBAAsB,CAAC;AAAA,IACtE,iBAAiB,GAAG,mBAAmB;AAAA,EACzC;AACF;AAGO,IAAM,iBAAiB,CAAC,SAAyB,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC;AAEzF,IAAM,gBAAgB,CAAC,SACrB,KAAK,OAAO,CAAC,GAAG,MAAM,IAAI,eAAe,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO;AAAA,CAAI,GAAG,CAAC;AAoB1E,IAAM,iBACJ;AAUK,IAAM,kBAAkB,OAC7B,UACA,OACA,OAAwB,CAAC,MACI;AAC7B,QAAM,aAAa,KAAK,cAAc;AACtC,MAAI,SAAS,UAAU,aAAa,EAAG,QAAO;AAC9C,MAAI,CAAC,KAAK,OAAO;AACf,QAAI,KAAK,oBAAoB,QAAW;AACtC,UAAI,KAAK,mBAAmB,KAAK,cAAc,QAAQ,KAAK,KAAK,iBAAiB;AAChF,eAAO;AAAA,MACT;AAAA,IACF,OAAO;AACL,YAAM,WAAW,KAAK,YAAY;AAClC,YAAM,QAAQ,SAAS,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,QAAQ,QAAQ,CAAC;AAC/D,UAAI,YAAY,KAAK,SAAS,SAAU,QAAO;AAAA,IACjD;AAAA,EACF;AACA,QAAM,OAAO,SAAS,MAAM,GAAG,SAAS,SAAS,UAAU;AAC3D,QAAM,OAAO,SAAS,MAAM,SAAS,SAAS,UAAU;AAGxD,QAAM,aAAa,KAChB,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EACpC,KAAK,IAAI,EACT,MAAM,KAAO;AAChB,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,OAAO;AAAA,MACnC,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,iBAAiB,KAAK,oBAAoB;AAAA,MAC1C,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB,CAAC;AACD,SAAK,UAAU,eAAe,OAAO,KAAK,CAAC;AAC3C,UAAM,SAAS,OAAO,QAAQ,IAAI,KAAK;AACvC,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,aAA4B;AAAA,MAChC,MAAM;AAAA,MACN,SAAS;AAAA,EAAqC,KAAK;AAAA,MACnD,IAAI,KAAK,CAAC,GAAG;AAAA,IACf;AACA,WAAO,CAAC,YAAY,GAAG,IAAI;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,IAAM,iBAAiB;AAE9B,IAAM,eACJ;AAQK,IAAM,eAAe,OAC1B,MACA,OACA,SACsB;AACtB,QAAM,aAAa,KAAK,cAAc;AACtC,MAAI,KAAK,UAAU,aAAa,EAAG,QAAO;AAC1C,MAAI,CAAC,KAAK,SAAS,eAAe,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,gBAAiB,QAAO;AACnF,QAAM,OAAO,KAAK,MAAM,GAAG,KAAK,SAAS,UAAU;AACnD,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,OAAO;AAAA,MACnC,QAAQ;AAAA,MACR,QAAQ,KAAK,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,MACtD,iBAAiB,KAAK,oBAAoB;AAAA,MAC1C,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB,CAAC;AACD,SAAK,UAAU,eAAe,OAAO,KAAK,CAAC;AAC3C,UAAM,SAAS,OAAO,QAAQ,IAAI,KAAK;AACvC,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,CAAC,cAAc,KAAK,MAAM,qBAAqB,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,EAC3F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AJxJO,IAAM,gCAAgC;AAUtC,IAAM,0BAA0B,CACrC,WACA,SACA,YACO;AAAA,EACP,GAAG;AAAA,EACH,YAAY,UAAU,WAAW;AAAA,IAAI,CAAC,MACpC,EAAE,aAAa,UACX,EAAE,GAAG,GAAG,WAAW,EAAE,GAAG,EAAE,WAAW,qBAAqB,OAAO,EAAE,IACnE;AAAA,EACN;AACF;AAkBO,IAAM,sBAAsB,CAAC,UAAuC;AACzE,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,EAAE,aAAa,UAAW,QAAO;AACpE,QAAM,KAAK,OAAO,EAAE,WAAW,EAAE;AACjC,MAAI;AACF,UAAM,OAAO,EAAE,QAAQ,yBAAyB,MAAM,EAAE,GAAG;AAC3D,QAAI,OAAO,SAAS,YAAY,OAAO,EAAG,QAAO;AAAA,EACnD,QAAQ;AAAA,EAER;AACA,QAAM,SAAS,EAAE,QAAQ,SAAS,cAAc,WAAW,YAAY;AAAA,IACrE,CAAC,MAAM,EAAE,aAAa;AAAA,EACxB;AACA,QAAM,aAAa,QAAQ,WAAW;AACtC,MAAI,OAAO,eAAe,YAAY,aAAa,EAAG,QAAO;AAC7D,SAAO,QAAQ,KAAK,EAAE,IAAI,OAAO;AACnC;AAOO,IAAM,kBAAkB,CAAC,UAAuC;AACrE,QAAM,SAAS,oBAAoB,KAAK;AACxC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,IAAI;AACV,MAAI,KAAK,OAAO,MAAM,YAAY,EAAE,aAAa,cAAc;AAC7D,QAAI;AACF,YAAM,IAAI,EAAE,mBAAmB;AAC/B,aAAO,OAAO,MAAM,YAAY,IAAI,IAAI,IAAI;AAAA,IAC9C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOO,IAAM,wBAAwB,CACnC,UACA,YACA,gBACuB;AACvB,QAAM,SAAS,KAAK;AAAA,IAClB,YAAY,uBAAuB,eAAe,SAAS;AAAA,IAC3D,eAAe;AAAA,EACjB;AACA,MAAI,WAAW,SAAS,uBAAuB,gBAAgB,OAAW,QAAO;AACjF,QAAM,OAAO,KAAK,MAAM,SAAS,CAAC;AAClC,QAAM,UAAU,KAAK,MAAM,SAAS,CAAC;AACrC,QAAM,aAAa,YAAY;AAC/B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,qBAAqB;AAAA,IACrB,iBAAiB,KAAK,IAAI,YAAY,mBAAmB,MAAM,IAAI;AAAA;AAAA,IAEnE,6BAA6B,eAAe,IAAI,IAAI,KAAK,IAAI,cAAc,SAAS,OAAO;AAAA;AAAA,IAE3F,oBACE,YAAY,uBAAuB,IAC/B,IACA,KAAK,IAAI,YAAY,sBAAsB,KAAQ,MAAM;AAAA;AAAA,IAE/D,iBACE,YAAY,mBAAmB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA,EACrF;AACF;AAMO,IAAM,uBAAuB,OAAO,UAAoC;AAC7E,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,OAAO,QAAQ,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM;AAC7C,UAAI;AACJ,UAAI;AACF,qBAAa,MAAM,SAAS,EAAE,WAAoB,EAAE;AAAA,MACtD,QAAQ;AACN,qBAAa;AAAA,MACf;AACA,aAAO,eAAe,KAAK,UAAU,EAAE,MAAM,aAAa,EAAE,eAAe,IAAI,WAAW,CAAC,CAAC;AAAA,IAC9F,CAAC;AAAA,EACH;AACA,SAAO,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACxC;AAGO,IAAM,2BAA2B;AAGjC,IAAM,6BAAN,cAAyC,MAAM;AAAA;AAAA,EAE3C;AAAA;AAAA,EAEA;AAAA,EACT,YAAY,OAAe,cAAuB,qBAA8B;AAC9E,UAAM,QACJ,gBAAgB,sBACZ,KAAK,YAAY,0BAA0B,mBAAmB,MAC9D;AACN;AAAA,MACE,0DAA0D,KAAK,IAAI,KAAK;AAAA,IAC1E;AACA,SAAK,OAAO;AACZ,SAAK,eAAe;AACpB,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AACF;AAIA,IAAM,cACJ;AACF,IAAM,kBAAkB;AAMjB,IAAM,oBAAoB,CAC/B,KACA,UAC2C;AAC3C,MAAI,eAAe,2BAA4B,QAAO;AACtD,QAAM,OAAO,eAAe,QAAQ,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,OAAO,GAAG;AAC7E,MAAI,CAAC,YAAY,KAAK,IAAI,EAAG,QAAO;AACpC,QAAM,QAAQ,gBAAgB,KAAK,IAAI;AACvC,SAAO,IAAI;AAAA,IACT,WAAW,KAAK;AAAA,IAChB,QAAQ,OAAO,MAAM,CAAC,CAAC,IAAI;AAAA,IAC3B,QAAQ,OAAO,MAAM,CAAC,CAAC,IAAK,oBAAoB,KAAK,KAAK;AAAA,EAC5D;AACF;;;ADjNO,IAAM,oBAAoB,MAC/B,OAAO,cAAc,eAAe,SAAS;AAuCxC,IAAM,qBAAqB,OAAO,UAAwC;AAC/E,QAAMC,cAAa,EAAE,OAAO,QAAQ,MAAM,iBAAiB,EAAE,CAAC;AAChE;AAUO,IAAM,oBAAoB,OAAO,UAAwC;AAC9E,QAAM,SAAU,MAAwD;AACxE,MAAI;AACF,UAAM,QAAQ,SAAS;AAAA,EACzB,QAAQ;AAAA,EAER;AACF;AAWO,IAAM,oBAAoB,OAC/B,OACA,YAC2B;AAC3B,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,OAAO,qBAAqB;AAAA,EAC1C,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,EAAE,UAAU,MAAM,qBAAqB,GAAG,SAAS,IAAI,WAAW,CAAC;AACzE,MAAI,qBAAqB;AACvB,UAAM,eAAgB,SAAS,gBAAgB,CAAC;AAChD,QAAI,OAAO,aAAa;AACxB,QAAI,CAAC,MAAM;AACT,UAAI;AACF,cAAM,OAAO;AACb,gBACG,MAAM;AAAA;AAAA;AAAA,UAAoD;AAAA,WAG3D;AAAA,MACJ,QAAQ;AACN,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,aAAS,eAAe;AAAA,MACtB,GAAG;AAAA,MACH,WAAW,wBAAwB,MAAM,OAAO,mBAAmB;AAAA,IACrE;AAAA,EACF;AAGA,QAAM,gBAAgB,IAAI;AAAA,IACxB;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAS,OAAM,mBAAmB,aAAa;AACnD,SAAO;AACT;","names":["generateText","generateText"]}
|