@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/dist/index.cjs CHANGED
@@ -30,6 +30,364 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
+ // src/llm/util.ts
34
+ var clip, promptOf, normalizeUsage, timeoutSignal;
35
+ var init_util = __esm({
36
+ "src/llm/util.ts"() {
37
+ "use strict";
38
+ clip = (s, max) => s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
39
+ promptOf = (opts) => opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? "" };
40
+ normalizeUsage = (u) => {
41
+ const inputTokens = u?.inputTokens ?? 0;
42
+ const outputTokens = u?.outputTokens ?? 0;
43
+ return {
44
+ inputTokens,
45
+ outputTokens,
46
+ totalTokens: u?.totalTokens ?? inputTokens + outputTokens,
47
+ reasoningTokens: u?.outputTokenDetails?.reasoningTokens ?? u?.reasoningTokens ?? 0,
48
+ cachedInputTokens: u?.inputTokenDetails?.cacheReadTokens ?? u?.cachedInputTokens ?? 0,
49
+ cacheWriteTokens: u?.inputTokenDetails?.cacheWriteTokens ?? 0
50
+ };
51
+ };
52
+ timeoutSignal = (signal, ms) => {
53
+ if (!ms || ms <= 0) return signal;
54
+ const timeout = AbortSignal.timeout(ms);
55
+ if (!signal) return timeout;
56
+ return typeof AbortSignal.any === "function" ? AbortSignal.any([signal, timeout]) : signal;
57
+ };
58
+ }
59
+ });
60
+
61
+ // src/llm/generate.ts
62
+ var import_ai, callSettings, generate, stream, generateStructured;
63
+ var init_generate = __esm({
64
+ "src/llm/generate.ts"() {
65
+ "use strict";
66
+ import_ai = require("ai");
67
+ init_util();
68
+ callSettings = (opts) => ({
69
+ ...opts.system !== void 0 ? { instructions: opts.system } : {},
70
+ maxOutputTokens: opts.maxOutputTokens,
71
+ temperature: opts.temperature,
72
+ ...opts.reasoning ? { reasoning: opts.reasoning } : {},
73
+ ...opts.providerOptions ? { providerOptions: opts.providerOptions } : {},
74
+ abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
75
+ });
76
+ generate = (model, opts = {}) => (0, import_ai.generateText)({
77
+ model,
78
+ ...callSettings(opts),
79
+ ...promptOf(opts),
80
+ tools: opts.tools
81
+ });
82
+ stream = (model, opts = {}) => (0, import_ai.streamText)({
83
+ model,
84
+ ...callSettings(opts),
85
+ ...promptOf(opts),
86
+ tools: opts.tools
87
+ });
88
+ generateStructured = (model, schema, opts = {}) => (0, import_ai.generateObject)({
89
+ model,
90
+ schema,
91
+ ...callSettings(opts),
92
+ ...promptOf(opts)
93
+ });
94
+ }
95
+ });
96
+
97
+ // src/memory/compress.ts
98
+ var resolveCompaction, estimateTokens, messageTokens, SUMMARY_SYSTEM, compressHistory, compactHistory, TRACE_SYSTEM, compactSteps;
99
+ var init_compress = __esm({
100
+ "src/memory/compress.ts"() {
101
+ "use strict";
102
+ init_generate();
103
+ init_util();
104
+ resolveCompaction = (c) => {
105
+ const contextWindowTokens = c?.contextWindowTokens ?? 128e3;
106
+ return {
107
+ auto: c?.auto ?? true,
108
+ contextWindowTokens,
109
+ thresholdTokens: c?.thresholdTokens ?? Math.floor(contextWindowTokens / 2),
110
+ keepRecentTurns: c?.keepRecentTurns ?? 4,
111
+ keepRecentSteps: c?.keepRecentSteps ?? 3,
112
+ summaryMaxTokens: c?.summaryMaxTokens ?? 1024,
113
+ maxToolOutputChars: c?.maxToolOutputChars ?? 2e4,
114
+ clearToolResultsAfterTokens: c?.clearToolResultsAfterTokens ?? Math.floor(contextWindowTokens / 4),
115
+ keepToolResults: c?.keepToolResults ?? 3
116
+ };
117
+ };
118
+ estimateTokens = (text) => Math.ceil((text ?? "").length / 4);
119
+ messageTokens = (msgs) => msgs.reduce((n, m) => n + estimateTokens(`${m.role}: ${m.content}
120
+ `), 0);
121
+ 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.";
122
+ compressHistory = async (messages, model, opts = {}) => {
123
+ const keepRecent = opts.keepRecent ?? 6;
124
+ if (messages.length <= keepRecent + 1) return messages;
125
+ if (!opts.force) {
126
+ if (opts.thresholdTokens !== void 0) {
127
+ if (opts.thresholdTokens <= 0 || messageTokens(messages) <= opts.thresholdTokens) {
128
+ return messages;
129
+ }
130
+ } else {
131
+ const maxChars = opts.maxChars ?? 12e3;
132
+ const chars = messages.reduce((n, m) => n + m.content.length, 0);
133
+ if (maxChars <= 0 || chars <= maxChars) return messages;
134
+ }
135
+ }
136
+ const head = messages.slice(0, messages.length - keepRecent);
137
+ const tail = messages.slice(messages.length - keepRecent);
138
+ const transcript = head.map((m) => `${m.role}: ${m.content}`).join("\n").slice(-48e3);
139
+ try {
140
+ const result = await generate(model, {
141
+ system: SUMMARY_SYSTEM,
142
+ prompt: transcript,
143
+ maxOutputTokens: opts.summaryMaxTokens ?? 400,
144
+ timeoutMs: opts.timeoutMs,
145
+ abortSignal: opts.abortSignal
146
+ });
147
+ opts.onUsage?.(normalizeUsage(result.usage));
148
+ const clean = (result.text || "").trim();
149
+ if (!clean) return messages;
150
+ const summaryMsg = {
151
+ role: "system",
152
+ content: `Summary of earlier conversation:
153
+ ${clean}`,
154
+ ts: head[0]?.ts
155
+ };
156
+ return [summaryMsg, ...tail];
157
+ } catch {
158
+ return messages;
159
+ }
160
+ };
161
+ compactHistory = compressHistory;
162
+ 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.";
163
+ compactSteps = async (done, model, opts) => {
164
+ const keepRecent = opts.keepRecent ?? 3;
165
+ if (done.length <= keepRecent + 1) return done;
166
+ if (!opts.force && estimateTokens(done.join("\n")) <= opts.thresholdTokens) return done;
167
+ const head = done.slice(0, done.length - keepRecent);
168
+ try {
169
+ const result = await generate(model, {
170
+ system: TRACE_SYSTEM,
171
+ prompt: head.map((d, i) => `${i + 1}. ${d}`).join("\n"),
172
+ maxOutputTokens: opts.summaryMaxTokens ?? 600,
173
+ timeoutMs: opts.timeoutMs,
174
+ abortSignal: opts.abortSignal
175
+ });
176
+ opts.onUsage?.(normalizeUsage(result.usage));
177
+ const clean = (result.text || "").trim();
178
+ if (!clean) return done;
179
+ return [`Summary of ${head.length} earlier step(s): ${clean}`, ...done.slice(head.length)];
180
+ } catch {
181
+ return done;
182
+ }
183
+ };
184
+ }
185
+ });
186
+
187
+ // src/images.ts
188
+ var DATA_URL_RE, HTTP_RE, mediaTypeOf, attachmentKind, KIND_LABEL, KIND_HINT, AttachmentsNotSupportedError, ImagesNotSupportedError, unsupportedAttachment, dataOf, toFilePart, toImagePart, modelLabel, REFUSAL_RE, isAttachmentRefusal, isImageRefusal;
189
+ var init_images = __esm({
190
+ "src/images.ts"() {
191
+ "use strict";
192
+ DATA_URL_RE = /^data:([^;,]+)?;base64,(.*)$/s;
193
+ HTTP_RE = /^https?:\/\//i;
194
+ mediaTypeOf = (f) => {
195
+ if (f.mediaType) return f.mediaType;
196
+ if (typeof f.data === "string") {
197
+ const m = DATA_URL_RE.exec(f.data);
198
+ if (m?.[1]) return m[1];
199
+ }
200
+ const url = f.data instanceof URL ? f.data.href : typeof f.data === "string" ? f.data : "";
201
+ if (HTTP_RE.test(url) || f.name) {
202
+ const ext = (f.name ?? new URL(url, "http://x").pathname).split(".").pop()?.toLowerCase();
203
+ if (ext === "pdf") return "application/pdf";
204
+ if (ext && ["png", "jpg", "jpeg", "gif", "webp"].includes(ext)) {
205
+ return `image/${ext === "jpg" ? "jpeg" : ext}`;
206
+ }
207
+ }
208
+ return void 0;
209
+ };
210
+ attachmentKind = (f) => {
211
+ const mt = mediaTypeOf(f) ?? "";
212
+ if (mt.startsWith("image/")) return "image";
213
+ if (mt === "application/pdf") return "pdf";
214
+ return "file";
215
+ };
216
+ KIND_LABEL = {
217
+ image: "images",
218
+ pdf: "PDF files",
219
+ file: "file attachments"
220
+ };
221
+ KIND_HINT = {
222
+ image: "Remove the image, or switch to a vision-capable model such as Gemini, Claude or GPT-4o/GPT-5.",
223
+ 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.",
224
+ file: "Paste its text instead, or switch to a model that accepts this file type."
225
+ };
226
+ AttachmentsNotSupportedError = class extends Error {
227
+ kind;
228
+ constructor(model, kind, detail) {
229
+ super(
230
+ `The model "${model}" can't take ${KIND_LABEL[kind]}${detail ? ` (${detail})` : ""}. ${KIND_HINT[kind]}`
231
+ );
232
+ this.name = "AttachmentsNotSupportedError";
233
+ this.kind = kind;
234
+ }
235
+ toJSON() {
236
+ return this.message;
237
+ }
238
+ };
239
+ ImagesNotSupportedError = class extends AttachmentsNotSupportedError {
240
+ constructor(model, detail) {
241
+ super(model, "image", detail);
242
+ this.name = "ImagesNotSupportedError";
243
+ }
244
+ };
245
+ unsupportedAttachment = (model, kind, detail) => kind === "image" ? new ImagesNotSupportedError(model, detail) : new AttachmentsNotSupportedError(model, kind, detail);
246
+ dataOf = (f) => {
247
+ if (typeof f.data === "string") {
248
+ const m = DATA_URL_RE.exec(f.data);
249
+ if (m) return m[2];
250
+ if (HTTP_RE.test(f.data)) return new URL(f.data);
251
+ }
252
+ return f.data;
253
+ };
254
+ toFilePart = (f) => {
255
+ const kind = attachmentKind(f);
256
+ const mediaType = mediaTypeOf(f) ?? (kind === "image" ? "image" : "application/octet-stream");
257
+ return {
258
+ type: "file",
259
+ data: dataOf(f),
260
+ mediaType,
261
+ ...f.name ? { filename: f.name } : {}
262
+ };
263
+ };
264
+ toImagePart = toFilePart;
265
+ modelLabel = (model) => {
266
+ if (typeof model === "string") return model;
267
+ const m = model;
268
+ return m?.modelId ? `${m.modelId}${m.provider ? ` (${m.provider})` : ""}` : "this model";
269
+ };
270
+ 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;
271
+ isAttachmentRefusal = (err) => REFUSAL_RE.test(err instanceof Error ? `${err.name} ${err.message}` : String(err));
272
+ isImageRefusal = isAttachmentRefusal;
273
+ }
274
+ });
275
+
276
+ // src/context-window.ts
277
+ var import_ai2, WEBLLM_DEFAULT_CONTEXT_WINDOW, withWebLLMContextWindow, webLLMContextWindow, contextWindowOf, fitCompactionToWindow, toolDefinitionTokens, TOOL_SEARCH_WINDOW_SHARE, ContextWindowExceededError, OVERFLOW_RE, WEBLLM_SIZES_RE, contextOverflowOf;
278
+ var init_context_window = __esm({
279
+ "src/context-window.ts"() {
280
+ "use strict";
281
+ import_ai2 = require("ai");
282
+ init_images();
283
+ init_compress();
284
+ WEBLLM_DEFAULT_CONTEXT_WINDOW = 4096;
285
+ withWebLLMContextWindow = (appConfig, modelId, tokens) => ({
286
+ ...appConfig,
287
+ model_list: appConfig.model_list.map(
288
+ (r) => r.model_id === modelId ? { ...r, overrides: { ...r.overrides, context_window_size: tokens } } : r
289
+ )
290
+ });
291
+ webLLMContextWindow = (model) => {
292
+ const m = model;
293
+ if (!m || typeof m !== "object" || m.provider !== "web-llm") return void 0;
294
+ const id = String(m.modelId ?? "");
295
+ try {
296
+ const live = m.engine?.loadedModelIdToPipeline?.get?.(id)?.contextWindowSize;
297
+ if (typeof live === "number" && live > 0) return live;
298
+ } catch {
299
+ }
300
+ const record = m.config?.options?.engineConfig?.appConfig?.model_list?.find(
301
+ (r) => r.model_id === id
302
+ );
303
+ const configured = record?.overrides?.context_window_size;
304
+ if (typeof configured === "number" && configured > 0) return configured;
305
+ return /-1k$/i.test(id) ? 1024 : WEBLLM_DEFAULT_CONTEXT_WINDOW;
306
+ };
307
+ contextWindowOf = (model) => {
308
+ const webllm = webLLMContextWindow(model);
309
+ if (webllm !== void 0) return webllm;
310
+ const m = model;
311
+ if (m && typeof m === "object" && m.provider === "browser-ai") {
312
+ try {
313
+ const w = m.getContextWindow?.();
314
+ return typeof w === "number" && w > 0 ? w : void 0;
315
+ } catch {
316
+ return void 0;
317
+ }
318
+ }
319
+ return void 0;
320
+ };
321
+ fitCompactionToWindow = (resolved, configured, modelWindow) => {
322
+ const window = Math.min(
323
+ configured?.contextWindowTokens ?? modelWindow ?? resolved.contextWindowTokens,
324
+ modelWindow ?? Infinity
325
+ );
326
+ if (window === resolved.contextWindowTokens && modelWindow === void 0) return resolved;
327
+ const half = Math.floor(window / 2);
328
+ const quarter = Math.floor(window / 4);
329
+ const clearAfter = configured?.clearToolResultsAfterTokens;
330
+ return {
331
+ ...resolved,
332
+ contextWindowTokens: window,
333
+ thresholdTokens: Math.min(configured?.thresholdTokens ?? half, half),
334
+ // 0 = never stays never.
335
+ clearToolResultsAfterTokens: clearAfter === 0 ? 0 : Math.min(clearAfter ?? quarter, quarter),
336
+ // One result may take a quarter of the window (~4 chars per token).
337
+ maxToolOutputChars: configured?.maxToolOutputChars === 0 ? 0 : Math.min(configured?.maxToolOutputChars ?? 2e4, window),
338
+ // A small window holds fewer verbatim results.
339
+ keepToolResults: configured?.keepToolResults ?? Math.max(1, Math.min(3, Math.floor(window / 8192)))
340
+ };
341
+ };
342
+ toolDefinitionTokens = async (tools) => {
343
+ const sizes = await Promise.all(
344
+ Object.entries(tools).map(async ([name, t]) => {
345
+ let parameters;
346
+ try {
347
+ parameters = await (0, import_ai2.asSchema)(t.inputSchema).jsonSchema;
348
+ } catch {
349
+ parameters = void 0;
350
+ }
351
+ return estimateTokens(JSON.stringify({ name, description: t.description ?? "", parameters }));
352
+ })
353
+ );
354
+ return sizes.reduce((a, b) => a + b, 0);
355
+ };
356
+ TOOL_SEARCH_WINDOW_SHARE = 0.25;
357
+ ContextWindowExceededError = class extends Error {
358
+ /** The prompt's size as the provider counted it, when it said. */
359
+ promptTokens;
360
+ /** The model's window as the provider reported it, when it said. */
361
+ contextWindowTokens;
362
+ constructor(model, promptTokens, contextWindowTokens) {
363
+ const sizes = promptTokens && contextWindowTokens ? ` (${promptTokens} tokens; the window is ${contextWindowTokens})` : "";
364
+ super(
365
+ `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.`
366
+ );
367
+ this.name = "ContextWindowExceededError";
368
+ this.promptTokens = promptTokens;
369
+ this.contextWindowTokens = contextWindowTokens;
370
+ }
371
+ toJSON() {
372
+ return this.message;
373
+ }
374
+ };
375
+ 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;
376
+ WEBLLM_SIZES_RE = /number of prompt tokens:\s*(\d+);\s*context window size:\s*(\d+)/i;
377
+ contextOverflowOf = (err, model) => {
378
+ if (err instanceof ContextWindowExceededError) return err;
379
+ const text = err instanceof Error ? `${err.name} ${err.message}` : String(err);
380
+ if (!OVERFLOW_RE.test(text)) return void 0;
381
+ const sizes = WEBLLM_SIZES_RE.exec(text);
382
+ return new ContextWindowExceededError(
383
+ modelLabel(model),
384
+ sizes ? Number(sizes[1]) : void 0,
385
+ sizes ? Number(sizes[2]) : webLLMContextWindow(model) ?? void 0
386
+ );
387
+ };
388
+ }
389
+ });
390
+
33
391
  // src/providers/webllm.ts
34
392
  var webllm_exports = {};
35
393
  __export(webllm_exports, {
@@ -38,14 +396,15 @@ __export(webllm_exports, {
38
396
  preloadWebLLMModel: () => preloadWebLLMModel,
39
397
  unloadWebLLMModel: () => unloadWebLLMModel
40
398
  });
41
- var import_ai2, isWebGPUAvailable, preloadWebLLMModel, unloadWebLLMModel, createWebLLMModel;
399
+ var import_ai3, isWebGPUAvailable, preloadWebLLMModel, unloadWebLLMModel, createWebLLMModel;
42
400
  var init_webllm = __esm({
43
401
  "src/providers/webllm.ts"() {
44
402
  "use strict";
45
- import_ai2 = require("ai");
403
+ import_ai3 = require("ai");
404
+ init_context_window();
46
405
  isWebGPUAvailable = () => typeof navigator !== "undefined" && "gpu" in navigator;
47
406
  preloadWebLLMModel = async (model) => {
48
- await (0, import_ai2.generateText)({ model, prompt: "ok", maxOutputTokens: 1 });
407
+ await (0, import_ai3.generateText)({ model, prompt: "ok", maxOutputTokens: 1 });
49
408
  };
50
409
  unloadWebLLMModel = async (model) => {
51
410
  const engine = model.engine;
@@ -63,7 +422,29 @@ var init_webllm = __esm({
63
422
  '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'
64
423
  );
65
424
  }
66
- const { preload = true, ...settings } = options ?? {};
425
+ const { preload = true, contextWindowTokens, ...settings } = options ?? {};
426
+ if (contextWindowTokens) {
427
+ const engineConfig = settings.engineConfig ?? {};
428
+ let base = engineConfig.appConfig;
429
+ if (!base) {
430
+ try {
431
+ const spec = "@mlc-ai/web-llm";
432
+ base = (await import(
433
+ /* webpackIgnore: true */
434
+ /* @vite-ignore */
435
+ spec
436
+ )).prebuiltAppConfig;
437
+ } catch {
438
+ throw new Error(
439
+ 'contextWindowTokens needs "@mlc-ai/web-llm" (for its prebuilt model list), or pass engineConfig.appConfig yourself.'
440
+ );
441
+ }
442
+ }
443
+ settings.engineConfig = {
444
+ ...engineConfig,
445
+ appConfig: withWebLLMContextWindow(base, model, contextWindowTokens)
446
+ };
447
+ }
67
448
  const languageModel = mod.webLLM(
68
449
  model,
69
450
  settings
@@ -78,6 +459,7 @@ var init_webllm = __esm({
78
459
  var src_exports = {};
79
460
  __export(src_exports, {
80
461
  AttachmentsNotSupportedError: () => AttachmentsNotSupportedError,
462
+ ContextWindowExceededError: () => ContextWindowExceededError,
81
463
  FILES_STORE: () => FILES_STORE,
82
464
  FIND_TOOLS_NAME: () => FIND_TOOLS_NAME,
83
465
  ImagesNotSupportedError: () => ImagesNotSupportedError,
@@ -93,10 +475,12 @@ __export(src_exports, {
93
475
  SESSIONS_STORE: () => SESSIONS_STORE,
94
476
  SKILL_TOOL_NAMES: () => SKILL_TOOL_NAMES,
95
477
  TOOL_APPROVAL_MODES: () => TOOL_APPROVAL_MODES,
478
+ TOOL_SEARCH_WINDOW_SHARE: () => TOOL_SEARCH_WINDOW_SHARE,
96
479
  ToolBudgetError: () => ToolBudgetError,
97
480
  ToolDeniedError: () => ToolDeniedError,
98
481
  VaultCredentialStore: () => VaultCredentialStore,
99
482
  VirtualFileSystem: () => VirtualFileSystem,
483
+ WEBLLM_DEFAULT_CONTEXT_WINDOW: () => WEBLLM_DEFAULT_CONTEXT_WINDOW,
100
484
  attachmentKind: () => attachmentKind,
101
485
  buildModelFromStage: () => buildModelFromStage,
102
486
  bytesToBase64: () => bytesToBase64,
@@ -104,6 +488,8 @@ __export(src_exports, {
104
488
  compactHistory: () => compactHistory,
105
489
  compactSteps: () => compactSteps,
106
490
  compressHistory: () => compressHistory,
491
+ contextOverflowOf: () => contextOverflowOf,
492
+ contextWindowOf: () => contextWindowOf,
107
493
  createAgent: () => createAgent,
108
494
  createFileTools: () => createFileTools,
109
495
  createFindToolsTool: () => createFindToolsTool,
@@ -125,6 +511,7 @@ __export(src_exports, {
125
511
  estimateMessagesTokens: () => messageTokens,
126
512
  estimateTokens: () => estimateTokens,
127
513
  executeStep: () => executeStep,
514
+ fitCompactionToWindow: () => fitCompactionToWindow,
128
515
  generate: () => generate,
129
516
  generateStructured: () => generateStructured,
130
517
  getOrCreateVaultKey: () => getOrCreateVaultKey,
@@ -183,10 +570,13 @@ __export(src_exports, {
183
570
  toImagePart: () => toImagePart,
184
571
  tokenize: () => tokenize,
185
572
  toolCatalogOf: () => toolCatalogOf,
573
+ toolDefinitionTokens: () => toolDefinitionTokens,
186
574
  toolRunContextOf: () => toolRunContextOf,
187
575
  unloadWebLLMModel: () => unloadWebLLMModel,
576
+ webLLMContextWindow: () => webLLMContextWindow,
188
577
  withRollingBreakpoint: () => withRollingBreakpoint,
189
- withSystem: () => withSystem
578
+ withSystem: () => withSystem,
579
+ withWebLLMContextWindow: () => withWebLLMContextWindow
190
580
  });
191
581
  module.exports = __toCommonJS(src_exports);
192
582
 
@@ -233,142 +623,8 @@ var withRollingBreakpoint = (messages, caching) => {
233
623
  return out;
234
624
  };
235
625
 
236
- // src/llm/generate.ts
237
- var import_ai = require("ai");
238
-
239
- // src/llm/util.ts
240
- var clip = (s, max) => s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
241
- var promptOf = (opts) => opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? "" };
242
- var normalizeUsage = (u) => {
243
- const inputTokens = u?.inputTokens ?? 0;
244
- const outputTokens = u?.outputTokens ?? 0;
245
- return {
246
- inputTokens,
247
- outputTokens,
248
- totalTokens: u?.totalTokens ?? inputTokens + outputTokens,
249
- reasoningTokens: u?.outputTokenDetails?.reasoningTokens ?? u?.reasoningTokens ?? 0,
250
- cachedInputTokens: u?.inputTokenDetails?.cacheReadTokens ?? u?.cachedInputTokens ?? 0,
251
- cacheWriteTokens: u?.inputTokenDetails?.cacheWriteTokens ?? 0
252
- };
253
- };
254
- var timeoutSignal = (signal, ms) => {
255
- if (!ms || ms <= 0) return signal;
256
- const timeout = AbortSignal.timeout(ms);
257
- if (!signal) return timeout;
258
- return typeof AbortSignal.any === "function" ? AbortSignal.any([signal, timeout]) : signal;
259
- };
260
-
261
- // src/llm/generate.ts
262
- var callSettings = (opts) => ({
263
- ...opts.system !== void 0 ? { instructions: opts.system } : {},
264
- maxOutputTokens: opts.maxOutputTokens,
265
- temperature: opts.temperature,
266
- ...opts.reasoning ? { reasoning: opts.reasoning } : {},
267
- ...opts.providerOptions ? { providerOptions: opts.providerOptions } : {},
268
- abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
269
- });
270
- var generate = (model, opts = {}) => (0, import_ai.generateText)({
271
- model,
272
- ...callSettings(opts),
273
- ...promptOf(opts),
274
- tools: opts.tools
275
- });
276
- var stream = (model, opts = {}) => (0, import_ai.streamText)({
277
- model,
278
- ...callSettings(opts),
279
- ...promptOf(opts),
280
- tools: opts.tools
281
- });
282
- var generateStructured = (model, schema, opts = {}) => (0, import_ai.generateObject)({
283
- model,
284
- schema,
285
- ...callSettings(opts),
286
- ...promptOf(opts)
287
- });
288
-
289
- // src/memory/compress.ts
290
- var resolveCompaction = (c) => {
291
- const contextWindowTokens = c?.contextWindowTokens ?? 128e3;
292
- return {
293
- auto: c?.auto ?? true,
294
- contextWindowTokens,
295
- thresholdTokens: c?.thresholdTokens ?? Math.floor(contextWindowTokens / 2),
296
- keepRecentTurns: c?.keepRecentTurns ?? 4,
297
- keepRecentSteps: c?.keepRecentSteps ?? 3,
298
- summaryMaxTokens: c?.summaryMaxTokens ?? 1024,
299
- maxToolOutputChars: c?.maxToolOutputChars ?? 2e4,
300
- clearToolResultsAfterTokens: c?.clearToolResultsAfterTokens ?? Math.floor(contextWindowTokens / 4),
301
- keepToolResults: c?.keepToolResults ?? 3
302
- };
303
- };
304
- var estimateTokens = (text) => Math.ceil((text ?? "").length / 4);
305
- var messageTokens = (msgs) => msgs.reduce((n, m) => n + estimateTokens(`${m.role}: ${m.content}
306
- `), 0);
307
- 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.";
308
- var compressHistory = async (messages, model, opts = {}) => {
309
- const keepRecent = opts.keepRecent ?? 6;
310
- if (messages.length <= keepRecent + 1) return messages;
311
- if (!opts.force) {
312
- if (opts.thresholdTokens !== void 0) {
313
- if (opts.thresholdTokens <= 0 || messageTokens(messages) <= opts.thresholdTokens) {
314
- return messages;
315
- }
316
- } else {
317
- const maxChars = opts.maxChars ?? 12e3;
318
- const chars = messages.reduce((n, m) => n + m.content.length, 0);
319
- if (maxChars <= 0 || chars <= maxChars) return messages;
320
- }
321
- }
322
- const head = messages.slice(0, messages.length - keepRecent);
323
- const tail = messages.slice(messages.length - keepRecent);
324
- const transcript = head.map((m) => `${m.role}: ${m.content}`).join("\n").slice(-48e3);
325
- try {
326
- const result = await generate(model, {
327
- system: SUMMARY_SYSTEM,
328
- prompt: transcript,
329
- maxOutputTokens: opts.summaryMaxTokens ?? 400,
330
- timeoutMs: opts.timeoutMs,
331
- abortSignal: opts.abortSignal
332
- });
333
- opts.onUsage?.(normalizeUsage(result.usage));
334
- const clean = (result.text || "").trim();
335
- if (!clean) return messages;
336
- const summaryMsg = {
337
- role: "system",
338
- content: `Summary of earlier conversation:
339
- ${clean}`,
340
- ts: head[0]?.ts
341
- };
342
- return [summaryMsg, ...tail];
343
- } catch {
344
- return messages;
345
- }
346
- };
347
- var compactHistory = compressHistory;
348
- 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.";
349
- var compactSteps = async (done, model, opts) => {
350
- const keepRecent = opts.keepRecent ?? 3;
351
- if (done.length <= keepRecent + 1) return done;
352
- if (!opts.force && estimateTokens(done.join("\n")) <= opts.thresholdTokens) return done;
353
- const head = done.slice(0, done.length - keepRecent);
354
- try {
355
- const result = await generate(model, {
356
- system: TRACE_SYSTEM,
357
- prompt: head.map((d, i) => `${i + 1}. ${d}`).join("\n"),
358
- maxOutputTokens: opts.summaryMaxTokens ?? 600,
359
- timeoutMs: opts.timeoutMs,
360
- abortSignal: opts.abortSignal
361
- });
362
- opts.onUsage?.(normalizeUsage(result.usage));
363
- const clean = (result.text || "").trim();
364
- if (!clean) return done;
365
- return [`Summary of ${head.length} earlier step(s): ${clean}`, ...done.slice(head.length)];
366
- } catch {
367
- return done;
368
- }
369
- };
370
-
371
626
  // src/config.ts
627
+ init_compress();
372
628
  var resolveConfig = (c) => ({
373
629
  clientName: c.clientName ?? "agent-web",
374
630
  sessionId: c.sessionId ?? "default",
@@ -432,6 +688,11 @@ var ToolBudgetError = class extends Error {
432
688
  }
433
689
  };
434
690
 
691
+ // src/agent/runner.ts
692
+ init_context_window();
693
+ init_util();
694
+ init_compress();
695
+
435
696
  // src/providers/types.ts
436
697
  var isDirectModel = (m) => typeof m === "string" || typeof m === "object" && m !== null && "specificationVersion" in m;
437
698
  var isProviderSpec = (m) => typeof m === "object" && m !== null && !isDirectModel(m) && typeof m.providerType === "string";
@@ -620,6 +881,7 @@ var buildModelFromStage = async (resolved, credentials, clientName = "agent-web"
620
881
  var resolveModel = (input, credentials, clientName) => buildModelFromStage(resolveStage(input, void 0, "model"), credentials, clientName);
621
882
 
622
883
  // src/prompts.ts
884
+ init_util();
623
885
  var numbered = (items, empty) => items.length ? items.map((s, i) => `${i + 1}. ${s}`).join("\n") : empty;
624
886
  var stateBlock = (state) => state && state.trim() ? `
625
887
 
@@ -744,7 +1006,8 @@ var withSystem = (base, extra) => extra && extra.trim() ? `${extra.trim()}
744
1006
  ${base}` : base;
745
1007
 
746
1008
  // src/skills.ts
747
- var import_ai3 = require("ai");
1009
+ var import_ai4 = require("ai");
1010
+ init_util();
748
1011
  var SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
749
1012
  var SKILL_TOOL_NAMES = ["load_skill", "read_skill_file"];
750
1013
  var defineSkill = (skill) => {
@@ -845,9 +1108,9 @@ ${s.content}${files}`, budget);
845
1108
  var createSkillTools = (skills, onLoad) => {
846
1109
  const byName = new Map(skills.map((s) => [s.name, s]));
847
1110
  const known = skills.map((s) => s.name).join(", ");
848
- const loadSkill = (0, import_ai3.tool)({
1111
+ const loadSkill = (0, import_ai4.tool)({
849
1112
  description: "Load the full instructions of a skill from the SKILLS list before doing work it covers.",
850
- inputSchema: (0, import_ai3.jsonSchema)({
1113
+ inputSchema: (0, import_ai4.jsonSchema)({
851
1114
  type: "object",
852
1115
  properties: { name: { type: "string", description: "The skill name" } },
853
1116
  required: ["name"],
@@ -864,9 +1127,9 @@ var createSkillTools = (skills, onLoad) => {
864
1127
  };
865
1128
  }
866
1129
  });
867
- const readFile = (0, import_ai3.tool)({
1130
+ const readFile = (0, import_ai4.tool)({
868
1131
  description: "Read one of a skill's bundled files (paths are listed by load_skill).",
869
- inputSchema: (0, import_ai3.jsonSchema)({
1132
+ inputSchema: (0, import_ai4.jsonSchema)({
870
1133
  type: "object",
871
1134
  properties: {
872
1135
  name: { type: "string", description: "The skill name" },
@@ -1100,7 +1363,7 @@ var dispatch = async (action, tools, ctx = {}) => {
1100
1363
  };
1101
1364
 
1102
1365
  // src/tools/search.ts
1103
- var import_ai4 = require("ai");
1366
+ var import_ai5 = require("ai");
1104
1367
  var FIND_TOOLS_NAME = "find_tools";
1105
1368
  var toolCatalogOf = (tools) => Object.entries(tools).map(([name, t]) => {
1106
1369
  const sep = name.indexOf("__");
@@ -1170,9 +1433,9 @@ var renderSearchCatalog = (catalog, budget = 12e3) => {
1170
1433
  return lines.join("\n");
1171
1434
  };
1172
1435
  var createFindToolsTool = (catalog, onFound, hintOf) => {
1173
- const t = (0, import_ai4.tool)({
1436
+ const t = (0, import_ai5.tool)({
1174
1437
  description: "Search the full tool catalogue by keywords and make the matching tools callable from the next step on. Use it whenever the tool you need is not in your current tool list.",
1175
- inputSchema: (0, import_ai4.jsonSchema)({
1438
+ inputSchema: (0, import_ai5.jsonSchema)({
1176
1439
  type: "object",
1177
1440
  properties: {
1178
1441
  query: { type: "string", description: "Keywords: what the tool should do" },
@@ -1206,7 +1469,7 @@ var createFindToolsTool = (catalog, onFound, hintOf) => {
1206
1469
  };
1207
1470
 
1208
1471
  // src/tools/wrap.ts
1209
- var import_ai5 = require("ai");
1472
+ var import_ai6 = require("ai");
1210
1473
  var toolRunContextOf = (options) => options?.agentRun;
1211
1474
  var truncated = (s, max) => s.length > max ? `${s.slice(0, max)}\u2026 [truncated ${s.length - max} chars]` : s;
1212
1475
  var toText = (v) => {
@@ -1330,15 +1593,18 @@ var schemaHintOf = async (t) => {
1330
1593
  const own = promptHintOf(t);
1331
1594
  if (own) return own;
1332
1595
  try {
1333
- const schema = await (0, import_ai5.asSchema)(t.inputSchema).jsonSchema;
1596
+ const schema = await (0, import_ai6.asSchema)(t.inputSchema).jsonSchema;
1334
1597
  return schema && typeof schema === "object" ? objectHint(schema) : void 0;
1335
1598
  } catch {
1336
1599
  return void 0;
1337
1600
  }
1338
1601
  };
1339
1602
 
1603
+ // src/agent/executor.ts
1604
+ init_context_window();
1605
+
1340
1606
  // src/llm/tool-loop.ts
1341
- var import_ai6 = require("ai");
1607
+ var import_ai7 = require("ai");
1342
1608
 
1343
1609
  // src/agent/loop-types.ts
1344
1610
  var BLOCKER = "[BLOCKER]";
@@ -1504,6 +1770,7 @@ var parsePlainText = (raw) => {
1504
1770
  };
1505
1771
 
1506
1772
  // src/llm/context-editing.ts
1773
+ init_compress();
1507
1774
  var STUB = (name) => `[${name} result cleared to save context \u2014 call the tool again if you still need it]`;
1508
1775
  var estimate = (messages) => {
1509
1776
  try {
@@ -1548,6 +1815,7 @@ var createToolResultClearer = (opts, onCleared) => {
1548
1815
  };
1549
1816
 
1550
1817
  // src/llm/tool-loop.ts
1818
+ init_util();
1551
1819
  var runToolLoop = (model, opts) => opts.mode === "prompted" ? runPrompted(model, opts) : runNative(model, opts);
1552
1820
  var activeOf = (opts) => typeof opts.activeTools === "function" ? opts.activeTools() : opts.activeTools;
1553
1821
  var callExtras = (opts) => ({
@@ -1561,7 +1829,7 @@ var runNative = async (model, opts) => {
1561
1829
  const toolCalls = [];
1562
1830
  const pending = /* @__PURE__ */ new Map();
1563
1831
  let stoppedByBudget = false;
1564
- const stopWhen = [(0, import_ai6.stepCountIs)(opts.maxSteps ?? 4)];
1832
+ const stopWhen = [(0, import_ai7.stepCountIs)(opts.maxSteps ?? 4)];
1565
1833
  if (opts.shouldStop) {
1566
1834
  const shouldStop = opts.shouldStop;
1567
1835
  stopWhen.push(({ steps }) => {
@@ -1576,7 +1844,7 @@ var runNative = async (model, opts) => {
1576
1844
  const caching = opts.caching?.enabled ? opts.caching : void 0;
1577
1845
  const clear = opts.clearToolResults ? createToolResultClearer(opts.clearToolResults, opts.callbacks?.onToolResultsCleared) : void 0;
1578
1846
  const editMessages = caching || clear;
1579
- const result = (0, import_ai6.streamText)({
1847
+ const result = (0, import_ai7.streamText)({
1580
1848
  model,
1581
1849
  tools: opts.tools,
1582
1850
  ...initialActive ? { activeTools: initialActive } : {},
@@ -1683,7 +1951,7 @@ var runPrompted = async (model, opts) => {
1683
1951
  break;
1684
1952
  }
1685
1953
  const before = new Set(Object.keys(callable()));
1686
- const result = await (0, import_ai6.generateText)({
1954
+ const result = await (0, import_ai7.generateText)({
1687
1955
  model,
1688
1956
  ...callExtras(opts),
1689
1957
  messages: opts.caching ? withRollingBreakpoint(messages, opts.caching) : messages,
@@ -1721,6 +1989,9 @@ var runPrompted = async (model, opts) => {
1721
1989
  return { text, toolCalls, usage, stoppedByBudget };
1722
1990
  };
1723
1991
 
1992
+ // src/agent/executor.ts
1993
+ init_util();
1994
+
1724
1995
  // src/thinking.ts
1725
1996
  var normalize = (setting) => {
1726
1997
  if (setting === void 0 || setting === false) return void 0;
@@ -1774,90 +2045,8 @@ var mergeProviderOptions = (...maps) => {
1774
2045
  return out;
1775
2046
  };
1776
2047
 
1777
- // src/images.ts
1778
- var DATA_URL_RE = /^data:([^;,]+)?;base64,(.*)$/s;
1779
- var HTTP_RE = /^https?:\/\//i;
1780
- var mediaTypeOf = (f) => {
1781
- if (f.mediaType) return f.mediaType;
1782
- if (typeof f.data === "string") {
1783
- const m = DATA_URL_RE.exec(f.data);
1784
- if (m?.[1]) return m[1];
1785
- }
1786
- const url = f.data instanceof URL ? f.data.href : typeof f.data === "string" ? f.data : "";
1787
- if (HTTP_RE.test(url) || f.name) {
1788
- const ext = (f.name ?? new URL(url, "http://x").pathname).split(".").pop()?.toLowerCase();
1789
- if (ext === "pdf") return "application/pdf";
1790
- if (ext && ["png", "jpg", "jpeg", "gif", "webp"].includes(ext)) {
1791
- return `image/${ext === "jpg" ? "jpeg" : ext}`;
1792
- }
1793
- }
1794
- return void 0;
1795
- };
1796
- var attachmentKind = (f) => {
1797
- const mt = mediaTypeOf(f) ?? "";
1798
- if (mt.startsWith("image/")) return "image";
1799
- if (mt === "application/pdf") return "pdf";
1800
- return "file";
1801
- };
1802
- var KIND_LABEL = {
1803
- image: "images",
1804
- pdf: "PDF files",
1805
- file: "file attachments"
1806
- };
1807
- var KIND_HINT = {
1808
- image: "Remove the image, or switch to a vision-capable model such as Gemini, Claude or GPT-4o/GPT-5.",
1809
- 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.",
1810
- file: "Paste its text instead, or switch to a model that accepts this file type."
1811
- };
1812
- var AttachmentsNotSupportedError = class extends Error {
1813
- kind;
1814
- constructor(model, kind, detail) {
1815
- super(
1816
- `The model "${model}" can't take ${KIND_LABEL[kind]}${detail ? ` (${detail})` : ""}. ${KIND_HINT[kind]}`
1817
- );
1818
- this.name = "AttachmentsNotSupportedError";
1819
- this.kind = kind;
1820
- }
1821
- toJSON() {
1822
- return this.message;
1823
- }
1824
- };
1825
- var ImagesNotSupportedError = class extends AttachmentsNotSupportedError {
1826
- constructor(model, detail) {
1827
- super(model, "image", detail);
1828
- this.name = "ImagesNotSupportedError";
1829
- }
1830
- };
1831
- var unsupportedAttachment = (model, kind, detail) => kind === "image" ? new ImagesNotSupportedError(model, detail) : new AttachmentsNotSupportedError(model, kind, detail);
1832
- var dataOf = (f) => {
1833
- if (typeof f.data === "string") {
1834
- const m = DATA_URL_RE.exec(f.data);
1835
- if (m) return m[2];
1836
- if (HTTP_RE.test(f.data)) return new URL(f.data);
1837
- }
1838
- return f.data;
1839
- };
1840
- var toFilePart = (f) => {
1841
- const kind = attachmentKind(f);
1842
- const mediaType = mediaTypeOf(f) ?? (kind === "image" ? "image" : "application/octet-stream");
1843
- return {
1844
- type: "file",
1845
- data: dataOf(f),
1846
- mediaType,
1847
- ...f.name ? { filename: f.name } : {}
1848
- };
1849
- };
1850
- var toImagePart = toFilePart;
1851
- var modelLabel = (model) => {
1852
- if (typeof model === "string") return model;
1853
- const m = model;
1854
- return m?.modelId ? `${m.modelId}${m.provider ? ` (${m.provider})` : ""}` : "this model";
1855
- };
1856
- 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;
1857
- var isAttachmentRefusal = (err) => REFUSAL_RE.test(err instanceof Error ? `${err.name} ${err.message}` : String(err));
1858
- var isImageRefusal = isAttachmentRefusal;
1859
-
1860
2048
  // src/agent/internal.ts
2049
+ init_images();
1861
2050
  var promptFor = (ctx, prompt) => ctx.images?.length ? {
1862
2051
  messages: [
1863
2052
  {
@@ -1980,7 +2169,7 @@ var executeStep = async (ctx, goal, step, index, total, done) => {
1980
2169
  }
1981
2170
  }
1982
2171
  }).catch((err) => {
1983
- throw imageRefusal(ctx, err, ctx.executorModel) ?? err;
2172
+ throw imageRefusal(ctx, err, ctx.executorModel) ?? contextOverflowOf(err, ctx.executorModel) ?? err;
1984
2173
  });
1985
2174
  ctx.log.debug("executor text:", loop.text);
1986
2175
  const { summary, blocked } = splitBlocker(loop.text);
@@ -2025,6 +2214,11 @@ var replanWanted = async (trigger, result, opts = {}) => {
2025
2214
  ]);
2026
2215
  };
2027
2216
 
2217
+ // src/agent/planner.ts
2218
+ init_context_window();
2219
+ init_generate();
2220
+ init_util();
2221
+
2028
2222
  // src/agent/schemas.ts
2029
2223
  var import_zod = require("zod");
2030
2224
  var PlanStepSchema = import_zod.z.object({
@@ -2104,12 +2298,14 @@ ${nudge}` : goal;
2104
2298
  if (ctx.signal?.aborted) throw err;
2105
2299
  const refused = imageRefusal(ctx, err, ctx.plannerModel);
2106
2300
  if (refused) throw refused;
2301
+ const overflow = contextOverflowOf(err, ctx.plannerModel);
2302
+ if (overflow) throw overflow;
2107
2303
  ctx.log.warn("planner: native structured output failed, salvaging:", asMessage(err));
2108
2304
  ctx.emit({ type: "retry", phase: "plan", attempt: 1, error: asMessage(err) });
2109
2305
  }
2110
2306
  }
2111
2307
  const result = await generate(ctx.plannerModel, commonFor("prompted")).catch((err) => {
2112
- throw imageRefusal(ctx, err, ctx.plannerModel) ?? err;
2308
+ throw imageRefusal(ctx, err, ctx.plannerModel) ?? contextOverflowOf(err, ctx.plannerModel) ?? err;
2113
2309
  });
2114
2310
  const parsed = parsePlannerResponse(result.text);
2115
2311
  ctx.log.debug("planner (prompted) raw:", result.text);
@@ -2129,6 +2325,8 @@ ${nudge}` : goal;
2129
2325
  var asMessage = (err) => err instanceof Error ? err.message : String(err);
2130
2326
 
2131
2327
  // src/agent/replanner.ts
2328
+ init_generate();
2329
+ init_util();
2132
2330
  var decideReplan = async (ctx, goal, done, remaining) => {
2133
2331
  const state = await ctx.state();
2134
2332
  const active = ctx.activeSkills?.() ?? [];
@@ -2177,6 +2375,9 @@ var decideReplan = async (ctx, goal, done, remaining) => {
2177
2375
  };
2178
2376
 
2179
2377
  // src/agent/synthesizer.ts
2378
+ init_context_window();
2379
+ init_generate();
2380
+ init_util();
2180
2381
  var synthesizeAnswer = async (ctx, goal, done, findings = []) => {
2181
2382
  const state = await ctx.state();
2182
2383
  const active = ctx.activeSkills?.() ?? [];
@@ -2204,7 +2405,7 @@ var synthesizeAnswer = async (ctx, goal, done, findings = []) => {
2204
2405
  }
2205
2406
  if (part.type === "error") {
2206
2407
  const err = part.error instanceof Error ? part.error : new Error(String(part.error));
2207
- throw imageRefusal(ctx, err, ctx.synthesizerModel) ?? err;
2408
+ throw imageRefusal(ctx, err, ctx.synthesizerModel) ?? contextOverflowOf(err, ctx.synthesizerModel) ?? err;
2208
2409
  }
2209
2410
  if (part.type !== "text-delta") continue;
2210
2411
  const delta = part.text;
@@ -2250,6 +2451,9 @@ var createLogger = (level = "warn", sink = console) => {
2250
2451
  };
2251
2452
  };
2252
2453
 
2454
+ // src/agent/runner.ts
2455
+ init_images();
2456
+
2253
2457
  // src/providers/capabilities.ts
2254
2458
  var CLOUD = /* @__PURE__ */ new Set([
2255
2459
  "openai",
@@ -2262,10 +2466,11 @@ var CLOUD = /* @__PURE__ */ new Set([
2262
2466
  ]);
2263
2467
  var supportsNativeTools = (p) => CLOUD.has(p);
2264
2468
  var supportsStructuredOutput = (p) => CLOUD.has(p);
2265
- var directBrowserOk = (p) => p === "google" || p === "gateway" || p === "openai-compatible" || p === "anthropic";
2469
+ var directBrowserOk = (p) => p !== "web-llm";
2266
2470
  var LOCAL_RE2 = /web-?llm|mlc|browser-ai|transformers|built-?in/i;
2471
+ var LOCAL_VISION_RE = /vision|[-_]vl\b|[-_]vl[-_]|llava|smolvlm|moondream|gemma-?3n/i;
2267
2472
  var TEXT_ONLY_MODEL_RE = /gpt-3\.5|o1-mini|o3-mini|deepseek|text-(davinci|embedding)|embedding|babbage|davinci|codestral/i;
2268
- var VISION_PROVIDER_RE = /^(google|anthropic|openai|azure|xai|vertex|bedrock|gemini)/i;
2473
+ var VISION_PROVIDER_RE = /^(google|anthropic|openai|azure|xai|vertex|bedrock|gemini|moonshot|mistral)/i;
2269
2474
  var PDF_PROVIDER_RE = /^(google|anthropic|openai|azure|vertex|bedrock|gemini)/i;
2270
2475
  var supportsImages = (model) => {
2271
2476
  if (typeof model === "string") {
@@ -2276,14 +2481,16 @@ var supportsImages = (model) => {
2276
2481
  if (!model || typeof model !== "object") return void 0;
2277
2482
  const provider = String(model.provider ?? "");
2278
2483
  const id = String(model.modelId ?? "");
2279
- if (LOCAL_RE2.test(provider)) return false;
2280
- if (/deepseek/i.test(provider) || TEXT_ONLY_MODEL_RE.test(id)) return false;
2484
+ if (LOCAL_RE2.test(provider)) return LOCAL_VISION_RE.test(id);
2485
+ if (/deepseek/i.test(provider)) return /flash/i.test(id);
2486
+ if (TEXT_ONLY_MODEL_RE.test(id)) return false;
2281
2487
  return VISION_PROVIDER_RE.test(provider) ? true : void 0;
2282
2488
  };
2283
2489
  var supportsPdf = (model) => {
2284
2490
  const vision = supportsImages(model);
2285
2491
  if (vision === false) return false;
2286
2492
  const provider = typeof model === "string" ? model.split("/")[0] ?? "" : String(model?.provider ?? "");
2493
+ if (LOCAL_RE2.test(provider) || /moonshot/i.test(provider)) return false;
2287
2494
  return PDF_PROVIDER_RE.test(provider) ? true : void 0;
2288
2495
  };
2289
2496
 
@@ -2320,6 +2527,10 @@ var createAgent = async (config) => {
2320
2527
  ]);
2321
2528
  const plannerMode = selectToolMode(plannerModel, cfg.toolMode);
2322
2529
  const executorMode = selectToolMode(executorModel, cfg.toolMode);
2530
+ const modelWindows = [executorModel, plannerModel, synthesizerModel].map(contextWindowOf).filter((w) => w !== void 0);
2531
+ const modelWindow = modelWindows.length ? Math.min(...modelWindows) : void 0;
2532
+ cfg.compaction = fitCompactionToWindow(cfg.compaction, config.compaction, modelWindow);
2533
+ const byTokens = Boolean(config.compaction) || modelWindow !== void 0;
2323
2534
  const baseTools = filterTools(
2324
2535
  config.tools ?? {},
2325
2536
  config.availableTools,
@@ -2332,7 +2543,8 @@ var createAgent = async (config) => {
2332
2543
  skillNames.add(s.name);
2333
2544
  }
2334
2545
  const catalog = toolCatalogOf(baseTools);
2335
- const strategy = cfg.toolSelectionStrategy === "auto" ? catalog.length > cfg.toolSearchThreshold ? "search" : "all" : cfg.toolSelectionStrategy;
2546
+ const toolTokens = cfg.toolSelectionStrategy === "auto" ? await toolDefinitionTokens(baseTools) : 0;
2547
+ const strategy = cfg.toolSelectionStrategy === "auto" ? catalog.length > cfg.toolSearchThreshold || toolTokens > cfg.compaction.contextWindowTokens * TOOL_SEARCH_WINDOW_SHARE ? "search" : "all" : cfg.toolSelectionStrategy;
2336
2548
  const reserved = [
2337
2549
  ...skills.length ? SKILL_TOOL_NAMES : [],
2338
2550
  ...strategy === "search" ? [FIND_TOOLS_NAME] : []
@@ -2358,14 +2570,15 @@ var createAgent = async (config) => {
2358
2570
  const caching = resolveCaching(config.promptCaching);
2359
2571
  const textOnly = executorMode === "prompted";
2360
2572
  const accepts = {
2361
- image: config.inputs?.images ?? config.vision ?? (textOnly ? false : supportsImages(executorModel)),
2573
+ // A local vision model (the prompted path carries images too) says so by its id.
2574
+ image: config.inputs?.images ?? config.vision ?? (textOnly ? supportsImages(executorModel) === true : supportsImages(executorModel)),
2362
2575
  pdf: config.inputs?.pdf ?? (textOnly ? false : supportsPdf(executorModel)),
2363
2576
  file: config.inputs?.files ?? (textOnly ? false : void 0)
2364
2577
  };
2365
2578
  const approval = createApprovalState(config.toolApproval);
2366
2579
  const log = createLogger(cfg.logLevel, config.logger);
2367
2580
  log.info(
2368
- `agent ready \u2014 ${catalog.length} tool(s) (${strategy}), skills: [${[...skillNames].join(", ") || "none"}], planner mode: ${plannerMode}, executor mode: ${executorMode}, approval: ${approval.mode}`
2581
+ `agent ready \u2014 ${catalog.length} tool(s) (${strategy}, ~${toolTokens || "?"} tokens), window ${cfg.compaction.contextWindowTokens}${modelWindow ? " (the model\u2019s)" : ""}, skills: [${[...skillNames].join(", ") || "none"}], planner mode: ${plannerMode}, executor mode: ${executorMode}, approval: ${approval.mode}`
2369
2582
  );
2370
2583
  const run = async (goal, opts = {}) => {
2371
2584
  const emit = (event) => {
@@ -2476,7 +2689,7 @@ var createAgent = async (config) => {
2476
2689
  } catch {
2477
2690
  }
2478
2691
  }
2479
- if (config.memory && config.compaction && cfg.compaction.auto && history.length > 0) {
2692
+ if (config.memory && byTokens && cfg.compaction.auto && history.length > 0) {
2480
2693
  const before = messageTokens(history);
2481
2694
  if (before > cfg.compaction.thresholdTokens) {
2482
2695
  const compacted = await compressHistory(history, synthesizerModel, {
@@ -2688,7 +2901,8 @@ var createAgent = async (config) => {
2688
2901
  }
2689
2902
  }
2690
2903
  if (isAborted()) return stop();
2691
- let summary = "Done \u2014 the changes have been applied.";
2904
+ const stepWords = result.trace.map((r) => r.summary.trim()).filter((t) => t && !/^(Executed \d+ tool call\(s\)\.|Step produced no output\.)$/.test(t));
2905
+ let summary = stepWords.length ? stepWords.join("\n\n") : "Done \u2014 the changes have been applied.";
2692
2906
  if (cfg.synthesize) {
2693
2907
  try {
2694
2908
  const synth = await synthesizeAnswer(ctx, text, done, findings);
@@ -2703,12 +2917,12 @@ var createAgent = async (config) => {
2703
2917
  emit({ type: "final", text: summary });
2704
2918
  await remember({ role: "assistant", content: summary });
2705
2919
  result.final = summary;
2706
- if (config.memory && (config.compaction ? cfg.compaction.auto : cfg.compressAfterChars > 0)) {
2920
+ if (config.memory && (byTokens ? cfg.compaction.auto : cfg.compressAfterChars > 0)) {
2707
2921
  try {
2708
2922
  const transcript = await config.memory.load(sessionId);
2709
2923
  const before = messageTokens(transcript);
2710
2924
  const compacted = await compressHistory(transcript, synthesizerModel, {
2711
- ...config.compaction ? {
2925
+ ...byTokens ? {
2712
2926
  thresholdTokens: cfg.compaction.thresholdTokens,
2713
2927
  keepRecent: cfg.compaction.keepRecentTurns,
2714
2928
  summaryMaxTokens: cfg.budgets.compaction
@@ -2780,6 +2994,7 @@ var createAgent = async (config) => {
2780
2994
  },
2781
2995
  listTools: () => catalog.map((e) => ({ ...e })),
2782
2996
  toolStrategy: strategy,
2997
+ contextWindowTokens: cfg.compaction.contextWindowTokens,
2783
2998
  skills: skills.map((s) => ({ name: s.name, description: s.description })),
2784
2999
  capabilities: { images: accepts.image, pdf: accepts.pdf, files: accepts.file },
2785
3000
  models: { planner: plannerModel, executor: executorModel, synthesizer: synthesizerModel }
@@ -2787,7 +3002,8 @@ var createAgent = async (config) => {
2787
3002
  };
2788
3003
 
2789
3004
  // src/subagent/tool.ts
2790
- var import_ai7 = require("ai");
3005
+ var import_ai8 = require("ai");
3006
+ init_util();
2791
3007
 
2792
3008
  // src/subagent/protocol.ts
2793
3009
  var toCloneable = (value) => {
@@ -2889,7 +3105,7 @@ var createSubagentTool = (opts) => {
2889
3105
  Object.entries(opts.tools ?? {}).map(async ([name, t2]) => ({
2890
3106
  name,
2891
3107
  description: typeof t2.description === "string" ? t2.description : "",
2892
- inputSchema: toCloneable(await (0, import_ai7.asSchema)(t2.inputSchema).jsonSchema),
3108
+ inputSchema: toCloneable(await (0, import_ai8.asSchema)(t2.inputSchema).jsonSchema),
2893
3109
  readOnly: isReadOnlyTool(t2)
2894
3110
  }))
2895
3111
  );
@@ -2962,9 +3178,9 @@ var createSubagentTool = (opts) => {
2962
3178
  safePost(worker, { type: "run", task, config, proxied });
2963
3179
  });
2964
3180
  };
2965
- const t = (0, import_ai7.tool)({
3181
+ const t = (0, import_ai8.tool)({
2966
3182
  description: opts.description,
2967
- inputSchema: (0, import_ai7.jsonSchema)({
3183
+ inputSchema: (0, import_ai8.jsonSchema)({
2968
3184
  type: "object",
2969
3185
  properties: {
2970
3186
  task: {
@@ -3024,7 +3240,7 @@ var createSubagentTool = (opts) => {
3024
3240
  };
3025
3241
 
3026
3242
  // src/subagent/worker.ts
3027
- var import_ai8 = require("ai");
3243
+ var import_ai9 = require("ai");
3028
3244
 
3029
3245
  // src/storage/db.ts
3030
3246
  var import_idb = require("idb");
@@ -3191,9 +3407,9 @@ var serveSubagentWorker = (opts = {}) => {
3191
3407
  const pending = /* @__PURE__ */ new Map();
3192
3408
  let callSeq = 0;
3193
3409
  const proxy = (spec) => {
3194
- const t = (0, import_ai8.dynamicTool)({
3410
+ const t = (0, import_ai9.dynamicTool)({
3195
3411
  description: spec.description,
3196
- inputSchema: (0, import_ai8.jsonSchema)(spec.inputSchema),
3412
+ inputSchema: (0, import_ai9.jsonSchema)(spec.inputSchema),
3197
3413
  execute: (input, options) => new Promise((resolve, reject) => {
3198
3414
  const callId = `call-${callSeq += 1}`;
3199
3415
  const signal = options?.abortSignal;
@@ -3271,6 +3487,9 @@ var serveSubagentWorker = (opts = {}) => {
3271
3487
  });
3272
3488
  };
3273
3489
 
3490
+ // src/index.ts
3491
+ init_images();
3492
+
3274
3493
  // src/files/vfs.ts
3275
3494
  var MIME = {
3276
3495
  txt: "text/plain",
@@ -3420,15 +3639,15 @@ var VirtualFileSystem = class {
3420
3639
  };
3421
3640
 
3422
3641
  // src/files/tools.ts
3423
- var import_ai9 = require("ai");
3642
+ var import_ai10 = require("ai");
3424
3643
  var createFileTools = (vfs, opts = {}) => {
3425
3644
  const p = opts.prefix ?? "fs_";
3426
3645
  const maxRead = opts.maxReadChars ?? 5e4;
3427
3646
  const tools = {
3428
3647
  [`${p}list`]: markReadOnly(
3429
- (0, import_ai9.tool)({
3648
+ (0, import_ai10.tool)({
3430
3649
  description: "List the files in the workspace (attachments the user added, files you wrote). Optionally only under a directory prefix.",
3431
- inputSchema: (0, import_ai9.jsonSchema)({
3650
+ inputSchema: (0, import_ai10.jsonSchema)({
3432
3651
  type: "object",
3433
3652
  properties: { prefix: { type: "string", description: 'Directory prefix, e.g. "/docs"' } },
3434
3653
  additionalProperties: false
@@ -3441,9 +3660,9 @@ var createFileTools = (vfs, opts = {}) => {
3441
3660
  })
3442
3661
  ),
3443
3662
  [`${p}read`]: markReadOnly(
3444
- (0, import_ai9.tool)({
3663
+ (0, import_ai10.tool)({
3445
3664
  description: "Read a text file from the workspace by its path.",
3446
- inputSchema: (0, import_ai9.jsonSchema)({
3665
+ inputSchema: (0, import_ai10.jsonSchema)({
3447
3666
  type: "object",
3448
3667
  properties: { path: { type: "string", description: 'Absolute path, e.g. "/notes.md"' } },
3449
3668
  required: ["path"],
@@ -3465,9 +3684,9 @@ var createFileTools = (vfs, opts = {}) => {
3465
3684
  )
3466
3685
  };
3467
3686
  if (opts.readOnly) return tools;
3468
- tools[`${p}write`] = (0, import_ai9.tool)({
3687
+ tools[`${p}write`] = (0, import_ai10.tool)({
3469
3688
  description: "Write a text file to the workspace (creates or replaces it). Use it for reports, drafts, data the user should be able to open.",
3470
- inputSchema: (0, import_ai9.jsonSchema)({
3689
+ inputSchema: (0, import_ai10.jsonSchema)({
3471
3690
  type: "object",
3472
3691
  properties: {
3473
3692
  path: { type: "string", description: 'Absolute path, e.g. "/reports/summary.md"' },
@@ -3482,9 +3701,9 @@ var createFileTools = (vfs, opts = {}) => {
3482
3701
  return { written: info.path, size: info.size };
3483
3702
  }
3484
3703
  });
3485
- tools[`${p}delete`] = (0, import_ai9.tool)({
3704
+ tools[`${p}delete`] = (0, import_ai10.tool)({
3486
3705
  description: "Delete a file from the workspace.",
3487
- inputSchema: (0, import_ai9.jsonSchema)({
3706
+ inputSchema: (0, import_ai10.jsonSchema)({
3488
3707
  type: "object",
3489
3708
  properties: { path: { type: "string" } },
3490
3709
  required: ["path"],
@@ -3497,12 +3716,15 @@ var createFileTools = (vfs, opts = {}) => {
3497
3716
 
3498
3717
  // src/index.ts
3499
3718
  init_webllm();
3719
+ init_context_window();
3720
+ init_generate();
3721
+ init_util();
3500
3722
 
3501
3723
  // src/tools/define.ts
3502
- var import_ai10 = require("ai");
3724
+ var import_ai11 = require("ai");
3503
3725
  var defineTool = (config) => {
3504
3726
  const { promptHint, readOnly, ...rest } = config;
3505
- const t = (0, import_ai10.tool)(rest);
3727
+ const t = (0, import_ai11.tool)(rest);
3506
3728
  if (promptHint) t.promptHint = promptHint;
3507
3729
  if (readOnly) t.readOnly = true;
3508
3730
  return t;
@@ -3557,9 +3779,13 @@ var IndexedDBStore = class {
3557
3779
  return (await db.getAllKeys(SESSIONS_STORE)).map(String);
3558
3780
  }
3559
3781
  };
3782
+
3783
+ // src/index.ts
3784
+ init_compress();
3560
3785
  // Annotate the CommonJS export names for ESM import in node:
3561
3786
  0 && (module.exports = {
3562
3787
  AttachmentsNotSupportedError,
3788
+ ContextWindowExceededError,
3563
3789
  FILES_STORE,
3564
3790
  FIND_TOOLS_NAME,
3565
3791
  ImagesNotSupportedError,
@@ -3575,10 +3801,12 @@ var IndexedDBStore = class {
3575
3801
  SESSIONS_STORE,
3576
3802
  SKILL_TOOL_NAMES,
3577
3803
  TOOL_APPROVAL_MODES,
3804
+ TOOL_SEARCH_WINDOW_SHARE,
3578
3805
  ToolBudgetError,
3579
3806
  ToolDeniedError,
3580
3807
  VaultCredentialStore,
3581
3808
  VirtualFileSystem,
3809
+ WEBLLM_DEFAULT_CONTEXT_WINDOW,
3582
3810
  attachmentKind,
3583
3811
  buildModelFromStage,
3584
3812
  bytesToBase64,
@@ -3586,6 +3814,8 @@ var IndexedDBStore = class {
3586
3814
  compactHistory,
3587
3815
  compactSteps,
3588
3816
  compressHistory,
3817
+ contextOverflowOf,
3818
+ contextWindowOf,
3589
3819
  createAgent,
3590
3820
  createFileTools,
3591
3821
  createFindToolsTool,
@@ -3607,6 +3837,7 @@ var IndexedDBStore = class {
3607
3837
  estimateMessagesTokens,
3608
3838
  estimateTokens,
3609
3839
  executeStep,
3840
+ fitCompactionToWindow,
3610
3841
  generate,
3611
3842
  generateStructured,
3612
3843
  getOrCreateVaultKey,
@@ -3665,9 +3896,12 @@ var IndexedDBStore = class {
3665
3896
  toImagePart,
3666
3897
  tokenize,
3667
3898
  toolCatalogOf,
3899
+ toolDefinitionTokens,
3668
3900
  toolRunContextOf,
3669
3901
  unloadWebLLMModel,
3902
+ webLLMContextWindow,
3670
3903
  withRollingBreakpoint,
3671
- withSystem
3904
+ withSystem,
3905
+ withWebLLMContextWindow
3672
3906
  });
3673
3907
  //# sourceMappingURL=index.cjs.map