@dudko.dev/agent-web 0.0.20 → 0.0.22

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;
@@ -1732,7 +2003,8 @@ var resolveThinking = (setting) => {
1732
2003
  const cfg = normalize(setting);
1733
2004
  if (!cfg) return {};
1734
2005
  const level = cfg.level ?? "medium";
1735
- if (level === "none") return { reasoning: "none" };
2006
+ const webllm = (on) => ({ "web-llm": { extra_body: { enable_thinking: on } } });
2007
+ if (level === "none") return { reasoning: "none", providerOptions: webllm(false) };
1736
2008
  const includeThoughts = cfg.includeThoughts !== false;
1737
2009
  const budget = cfg.budgetTokens;
1738
2010
  if (typeof budget === "number" && budget > 0) {
@@ -1740,16 +2012,18 @@ var resolveThinking = (setting) => {
1740
2012
  reasoning: level,
1741
2013
  providerOptions: {
1742
2014
  anthropic: { thinking: { type: "enabled", budgetTokens: budget } },
1743
- google: { thinkingConfig: { thinkingBudget: budget, includeThoughts } }
2015
+ google: { thinkingConfig: { thinkingBudget: budget, includeThoughts } },
2016
+ ...webllm(true)
1744
2017
  }
1745
2018
  };
1746
2019
  }
1747
- if (!includeThoughts) return { reasoning: level };
2020
+ if (!includeThoughts) return { reasoning: level, providerOptions: webllm(true) };
1748
2021
  return {
1749
2022
  reasoning: level,
1750
2023
  providerOptions: {
1751
2024
  google: { thinkingConfig: { includeThoughts: true } },
1752
- openai: { reasoningSummary: "auto" }
2025
+ openai: { reasoningSummary: "auto" },
2026
+ ...webllm(true)
1753
2027
  }
1754
2028
  };
1755
2029
  };
@@ -1771,90 +2045,8 @@ var mergeProviderOptions = (...maps) => {
1771
2045
  return out;
1772
2046
  };
1773
2047
 
1774
- // src/images.ts
1775
- var DATA_URL_RE = /^data:([^;,]+)?;base64,(.*)$/s;
1776
- var HTTP_RE = /^https?:\/\//i;
1777
- var mediaTypeOf = (f) => {
1778
- if (f.mediaType) return f.mediaType;
1779
- if (typeof f.data === "string") {
1780
- const m = DATA_URL_RE.exec(f.data);
1781
- if (m?.[1]) return m[1];
1782
- }
1783
- const url = f.data instanceof URL ? f.data.href : typeof f.data === "string" ? f.data : "";
1784
- if (HTTP_RE.test(url) || f.name) {
1785
- const ext = (f.name ?? new URL(url, "http://x").pathname).split(".").pop()?.toLowerCase();
1786
- if (ext === "pdf") return "application/pdf";
1787
- if (ext && ["png", "jpg", "jpeg", "gif", "webp"].includes(ext)) {
1788
- return `image/${ext === "jpg" ? "jpeg" : ext}`;
1789
- }
1790
- }
1791
- return void 0;
1792
- };
1793
- var attachmentKind = (f) => {
1794
- const mt = mediaTypeOf(f) ?? "";
1795
- if (mt.startsWith("image/")) return "image";
1796
- if (mt === "application/pdf") return "pdf";
1797
- return "file";
1798
- };
1799
- var KIND_LABEL = {
1800
- image: "images",
1801
- pdf: "PDF files",
1802
- file: "file attachments"
1803
- };
1804
- var KIND_HINT = {
1805
- image: "Remove the image, or switch to a vision-capable model such as Gemini, Claude or GPT-4o/GPT-5.",
1806
- 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.",
1807
- file: "Paste its text instead, or switch to a model that accepts this file type."
1808
- };
1809
- var AttachmentsNotSupportedError = class extends Error {
1810
- kind;
1811
- constructor(model, kind, detail) {
1812
- super(
1813
- `The model "${model}" can't take ${KIND_LABEL[kind]}${detail ? ` (${detail})` : ""}. ${KIND_HINT[kind]}`
1814
- );
1815
- this.name = "AttachmentsNotSupportedError";
1816
- this.kind = kind;
1817
- }
1818
- toJSON() {
1819
- return this.message;
1820
- }
1821
- };
1822
- var ImagesNotSupportedError = class extends AttachmentsNotSupportedError {
1823
- constructor(model, detail) {
1824
- super(model, "image", detail);
1825
- this.name = "ImagesNotSupportedError";
1826
- }
1827
- };
1828
- var unsupportedAttachment = (model, kind, detail) => kind === "image" ? new ImagesNotSupportedError(model, detail) : new AttachmentsNotSupportedError(model, kind, detail);
1829
- var dataOf = (f) => {
1830
- if (typeof f.data === "string") {
1831
- const m = DATA_URL_RE.exec(f.data);
1832
- if (m) return m[2];
1833
- if (HTTP_RE.test(f.data)) return new URL(f.data);
1834
- }
1835
- return f.data;
1836
- };
1837
- var toFilePart = (f) => {
1838
- const kind = attachmentKind(f);
1839
- const mediaType = mediaTypeOf(f) ?? (kind === "image" ? "image" : "application/octet-stream");
1840
- return {
1841
- type: "file",
1842
- data: dataOf(f),
1843
- mediaType,
1844
- ...f.name ? { filename: f.name } : {}
1845
- };
1846
- };
1847
- var toImagePart = toFilePart;
1848
- var modelLabel = (model) => {
1849
- if (typeof model === "string") return model;
1850
- const m = model;
1851
- return m?.modelId ? `${m.modelId}${m.provider ? ` (${m.provider})` : ""}` : "this model";
1852
- };
1853
- 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;
1854
- var isAttachmentRefusal = (err) => REFUSAL_RE.test(err instanceof Error ? `${err.name} ${err.message}` : String(err));
1855
- var isImageRefusal = isAttachmentRefusal;
1856
-
1857
2048
  // src/agent/internal.ts
2049
+ init_images();
1858
2050
  var promptFor = (ctx, prompt) => ctx.images?.length ? {
1859
2051
  messages: [
1860
2052
  {
@@ -1977,7 +2169,7 @@ var executeStep = async (ctx, goal, step, index, total, done) => {
1977
2169
  }
1978
2170
  }
1979
2171
  }).catch((err) => {
1980
- throw imageRefusal(ctx, err, ctx.executorModel) ?? err;
2172
+ throw imageRefusal(ctx, err, ctx.executorModel) ?? contextOverflowOf(err, ctx.executorModel) ?? err;
1981
2173
  });
1982
2174
  ctx.log.debug("executor text:", loop.text);
1983
2175
  const { summary, blocked } = splitBlocker(loop.text);
@@ -2022,6 +2214,11 @@ var replanWanted = async (trigger, result, opts = {}) => {
2022
2214
  ]);
2023
2215
  };
2024
2216
 
2217
+ // src/agent/planner.ts
2218
+ init_context_window();
2219
+ init_generate();
2220
+ init_util();
2221
+
2025
2222
  // src/agent/schemas.ts
2026
2223
  var import_zod = require("zod");
2027
2224
  var PlanStepSchema = import_zod.z.object({
@@ -2101,12 +2298,14 @@ ${nudge}` : goal;
2101
2298
  if (ctx.signal?.aborted) throw err;
2102
2299
  const refused = imageRefusal(ctx, err, ctx.plannerModel);
2103
2300
  if (refused) throw refused;
2301
+ const overflow = contextOverflowOf(err, ctx.plannerModel);
2302
+ if (overflow) throw overflow;
2104
2303
  ctx.log.warn("planner: native structured output failed, salvaging:", asMessage(err));
2105
2304
  ctx.emit({ type: "retry", phase: "plan", attempt: 1, error: asMessage(err) });
2106
2305
  }
2107
2306
  }
2108
2307
  const result = await generate(ctx.plannerModel, commonFor("prompted")).catch((err) => {
2109
- throw imageRefusal(ctx, err, ctx.plannerModel) ?? err;
2308
+ throw imageRefusal(ctx, err, ctx.plannerModel) ?? contextOverflowOf(err, ctx.plannerModel) ?? err;
2110
2309
  });
2111
2310
  const parsed = parsePlannerResponse(result.text);
2112
2311
  ctx.log.debug("planner (prompted) raw:", result.text);
@@ -2126,6 +2325,8 @@ ${nudge}` : goal;
2126
2325
  var asMessage = (err) => err instanceof Error ? err.message : String(err);
2127
2326
 
2128
2327
  // src/agent/replanner.ts
2328
+ init_generate();
2329
+ init_util();
2129
2330
  var decideReplan = async (ctx, goal, done, remaining) => {
2130
2331
  const state = await ctx.state();
2131
2332
  const active = ctx.activeSkills?.() ?? [];
@@ -2174,6 +2375,9 @@ var decideReplan = async (ctx, goal, done, remaining) => {
2174
2375
  };
2175
2376
 
2176
2377
  // src/agent/synthesizer.ts
2378
+ init_context_window();
2379
+ init_generate();
2380
+ init_util();
2177
2381
  var synthesizeAnswer = async (ctx, goal, done, findings = []) => {
2178
2382
  const state = await ctx.state();
2179
2383
  const active = ctx.activeSkills?.() ?? [];
@@ -2201,7 +2405,7 @@ var synthesizeAnswer = async (ctx, goal, done, findings = []) => {
2201
2405
  }
2202
2406
  if (part.type === "error") {
2203
2407
  const err = part.error instanceof Error ? part.error : new Error(String(part.error));
2204
- throw imageRefusal(ctx, err, ctx.synthesizerModel) ?? err;
2408
+ throw imageRefusal(ctx, err, ctx.synthesizerModel) ?? contextOverflowOf(err, ctx.synthesizerModel) ?? err;
2205
2409
  }
2206
2410
  if (part.type !== "text-delta") continue;
2207
2411
  const delta = part.text;
@@ -2247,6 +2451,9 @@ var createLogger = (level = "warn", sink = console) => {
2247
2451
  };
2248
2452
  };
2249
2453
 
2454
+ // src/agent/runner.ts
2455
+ init_images();
2456
+
2250
2457
  // src/providers/capabilities.ts
2251
2458
  var CLOUD = /* @__PURE__ */ new Set([
2252
2459
  "openai",
@@ -2259,10 +2466,11 @@ var CLOUD = /* @__PURE__ */ new Set([
2259
2466
  ]);
2260
2467
  var supportsNativeTools = (p) => CLOUD.has(p);
2261
2468
  var supportsStructuredOutput = (p) => CLOUD.has(p);
2262
- var directBrowserOk = (p) => p === "google" || p === "gateway" || p === "openai-compatible" || p === "anthropic";
2469
+ var directBrowserOk = (p) => p !== "web-llm";
2263
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;
2264
2472
  var TEXT_ONLY_MODEL_RE = /gpt-3\.5|o1-mini|o3-mini|deepseek|text-(davinci|embedding)|embedding|babbage|davinci|codestral/i;
2265
- 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;
2266
2474
  var PDF_PROVIDER_RE = /^(google|anthropic|openai|azure|vertex|bedrock|gemini)/i;
2267
2475
  var supportsImages = (model) => {
2268
2476
  if (typeof model === "string") {
@@ -2273,14 +2481,16 @@ var supportsImages = (model) => {
2273
2481
  if (!model || typeof model !== "object") return void 0;
2274
2482
  const provider = String(model.provider ?? "");
2275
2483
  const id = String(model.modelId ?? "");
2276
- if (LOCAL_RE2.test(provider)) return false;
2277
- 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;
2278
2487
  return VISION_PROVIDER_RE.test(provider) ? true : void 0;
2279
2488
  };
2280
2489
  var supportsPdf = (model) => {
2281
2490
  const vision = supportsImages(model);
2282
2491
  if (vision === false) return false;
2283
2492
  const provider = typeof model === "string" ? model.split("/")[0] ?? "" : String(model?.provider ?? "");
2493
+ if (LOCAL_RE2.test(provider) || /moonshot/i.test(provider)) return false;
2284
2494
  return PDF_PROVIDER_RE.test(provider) ? true : void 0;
2285
2495
  };
2286
2496
 
@@ -2317,6 +2527,10 @@ var createAgent = async (config) => {
2317
2527
  ]);
2318
2528
  const plannerMode = selectToolMode(plannerModel, cfg.toolMode);
2319
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;
2320
2534
  const baseTools = filterTools(
2321
2535
  config.tools ?? {},
2322
2536
  config.availableTools,
@@ -2329,7 +2543,8 @@ var createAgent = async (config) => {
2329
2543
  skillNames.add(s.name);
2330
2544
  }
2331
2545
  const catalog = toolCatalogOf(baseTools);
2332
- 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;
2333
2548
  const reserved = [
2334
2549
  ...skills.length ? SKILL_TOOL_NAMES : [],
2335
2550
  ...strategy === "search" ? [FIND_TOOLS_NAME] : []
@@ -2355,14 +2570,15 @@ var createAgent = async (config) => {
2355
2570
  const caching = resolveCaching(config.promptCaching);
2356
2571
  const textOnly = executorMode === "prompted";
2357
2572
  const accepts = {
2358
- 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)),
2359
2575
  pdf: config.inputs?.pdf ?? (textOnly ? false : supportsPdf(executorModel)),
2360
2576
  file: config.inputs?.files ?? (textOnly ? false : void 0)
2361
2577
  };
2362
2578
  const approval = createApprovalState(config.toolApproval);
2363
2579
  const log = createLogger(cfg.logLevel, config.logger);
2364
2580
  log.info(
2365
- `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}`
2366
2582
  );
2367
2583
  const run = async (goal, opts = {}) => {
2368
2584
  const emit = (event) => {
@@ -2473,7 +2689,7 @@ var createAgent = async (config) => {
2473
2689
  } catch {
2474
2690
  }
2475
2691
  }
2476
- if (config.memory && config.compaction && cfg.compaction.auto && history.length > 0) {
2692
+ if (config.memory && byTokens && cfg.compaction.auto && history.length > 0) {
2477
2693
  const before = messageTokens(history);
2478
2694
  if (before > cfg.compaction.thresholdTokens) {
2479
2695
  const compacted = await compressHistory(history, synthesizerModel, {
@@ -2685,7 +2901,8 @@ var createAgent = async (config) => {
2685
2901
  }
2686
2902
  }
2687
2903
  if (isAborted()) return stop();
2688
- 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.";
2689
2906
  if (cfg.synthesize) {
2690
2907
  try {
2691
2908
  const synth = await synthesizeAnswer(ctx, text, done, findings);
@@ -2700,12 +2917,12 @@ var createAgent = async (config) => {
2700
2917
  emit({ type: "final", text: summary });
2701
2918
  await remember({ role: "assistant", content: summary });
2702
2919
  result.final = summary;
2703
- if (config.memory && (config.compaction ? cfg.compaction.auto : cfg.compressAfterChars > 0)) {
2920
+ if (config.memory && (byTokens ? cfg.compaction.auto : cfg.compressAfterChars > 0)) {
2704
2921
  try {
2705
2922
  const transcript = await config.memory.load(sessionId);
2706
2923
  const before = messageTokens(transcript);
2707
2924
  const compacted = await compressHistory(transcript, synthesizerModel, {
2708
- ...config.compaction ? {
2925
+ ...byTokens ? {
2709
2926
  thresholdTokens: cfg.compaction.thresholdTokens,
2710
2927
  keepRecent: cfg.compaction.keepRecentTurns,
2711
2928
  summaryMaxTokens: cfg.budgets.compaction
@@ -2777,6 +2994,7 @@ var createAgent = async (config) => {
2777
2994
  },
2778
2995
  listTools: () => catalog.map((e) => ({ ...e })),
2779
2996
  toolStrategy: strategy,
2997
+ contextWindowTokens: cfg.compaction.contextWindowTokens,
2780
2998
  skills: skills.map((s) => ({ name: s.name, description: s.description })),
2781
2999
  capabilities: { images: accepts.image, pdf: accepts.pdf, files: accepts.file },
2782
3000
  models: { planner: plannerModel, executor: executorModel, synthesizer: synthesizerModel }
@@ -2784,7 +3002,8 @@ var createAgent = async (config) => {
2784
3002
  };
2785
3003
 
2786
3004
  // src/subagent/tool.ts
2787
- var import_ai7 = require("ai");
3005
+ var import_ai8 = require("ai");
3006
+ init_util();
2788
3007
 
2789
3008
  // src/subagent/protocol.ts
2790
3009
  var toCloneable = (value) => {
@@ -2886,7 +3105,7 @@ var createSubagentTool = (opts) => {
2886
3105
  Object.entries(opts.tools ?? {}).map(async ([name, t2]) => ({
2887
3106
  name,
2888
3107
  description: typeof t2.description === "string" ? t2.description : "",
2889
- inputSchema: toCloneable(await (0, import_ai7.asSchema)(t2.inputSchema).jsonSchema),
3108
+ inputSchema: toCloneable(await (0, import_ai8.asSchema)(t2.inputSchema).jsonSchema),
2890
3109
  readOnly: isReadOnlyTool(t2)
2891
3110
  }))
2892
3111
  );
@@ -2959,9 +3178,9 @@ var createSubagentTool = (opts) => {
2959
3178
  safePost(worker, { type: "run", task, config, proxied });
2960
3179
  });
2961
3180
  };
2962
- const t = (0, import_ai7.tool)({
3181
+ const t = (0, import_ai8.tool)({
2963
3182
  description: opts.description,
2964
- inputSchema: (0, import_ai7.jsonSchema)({
3183
+ inputSchema: (0, import_ai8.jsonSchema)({
2965
3184
  type: "object",
2966
3185
  properties: {
2967
3186
  task: {
@@ -3021,7 +3240,7 @@ var createSubagentTool = (opts) => {
3021
3240
  };
3022
3241
 
3023
3242
  // src/subagent/worker.ts
3024
- var import_ai8 = require("ai");
3243
+ var import_ai9 = require("ai");
3025
3244
 
3026
3245
  // src/storage/db.ts
3027
3246
  var import_idb = require("idb");
@@ -3188,9 +3407,9 @@ var serveSubagentWorker = (opts = {}) => {
3188
3407
  const pending = /* @__PURE__ */ new Map();
3189
3408
  let callSeq = 0;
3190
3409
  const proxy = (spec) => {
3191
- const t = (0, import_ai8.dynamicTool)({
3410
+ const t = (0, import_ai9.dynamicTool)({
3192
3411
  description: spec.description,
3193
- inputSchema: (0, import_ai8.jsonSchema)(spec.inputSchema),
3412
+ inputSchema: (0, import_ai9.jsonSchema)(spec.inputSchema),
3194
3413
  execute: (input, options) => new Promise((resolve, reject) => {
3195
3414
  const callId = `call-${callSeq += 1}`;
3196
3415
  const signal = options?.abortSignal;
@@ -3268,6 +3487,9 @@ var serveSubagentWorker = (opts = {}) => {
3268
3487
  });
3269
3488
  };
3270
3489
 
3490
+ // src/index.ts
3491
+ init_images();
3492
+
3271
3493
  // src/files/vfs.ts
3272
3494
  var MIME = {
3273
3495
  txt: "text/plain",
@@ -3417,15 +3639,15 @@ var VirtualFileSystem = class {
3417
3639
  };
3418
3640
 
3419
3641
  // src/files/tools.ts
3420
- var import_ai9 = require("ai");
3642
+ var import_ai10 = require("ai");
3421
3643
  var createFileTools = (vfs, opts = {}) => {
3422
3644
  const p = opts.prefix ?? "fs_";
3423
3645
  const maxRead = opts.maxReadChars ?? 5e4;
3424
3646
  const tools = {
3425
3647
  [`${p}list`]: markReadOnly(
3426
- (0, import_ai9.tool)({
3648
+ (0, import_ai10.tool)({
3427
3649
  description: "List the files in the workspace (attachments the user added, files you wrote). Optionally only under a directory prefix.",
3428
- inputSchema: (0, import_ai9.jsonSchema)({
3650
+ inputSchema: (0, import_ai10.jsonSchema)({
3429
3651
  type: "object",
3430
3652
  properties: { prefix: { type: "string", description: 'Directory prefix, e.g. "/docs"' } },
3431
3653
  additionalProperties: false
@@ -3438,9 +3660,9 @@ var createFileTools = (vfs, opts = {}) => {
3438
3660
  })
3439
3661
  ),
3440
3662
  [`${p}read`]: markReadOnly(
3441
- (0, import_ai9.tool)({
3663
+ (0, import_ai10.tool)({
3442
3664
  description: "Read a text file from the workspace by its path.",
3443
- inputSchema: (0, import_ai9.jsonSchema)({
3665
+ inputSchema: (0, import_ai10.jsonSchema)({
3444
3666
  type: "object",
3445
3667
  properties: { path: { type: "string", description: 'Absolute path, e.g. "/notes.md"' } },
3446
3668
  required: ["path"],
@@ -3462,9 +3684,9 @@ var createFileTools = (vfs, opts = {}) => {
3462
3684
  )
3463
3685
  };
3464
3686
  if (opts.readOnly) return tools;
3465
- tools[`${p}write`] = (0, import_ai9.tool)({
3687
+ tools[`${p}write`] = (0, import_ai10.tool)({
3466
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.",
3467
- inputSchema: (0, import_ai9.jsonSchema)({
3689
+ inputSchema: (0, import_ai10.jsonSchema)({
3468
3690
  type: "object",
3469
3691
  properties: {
3470
3692
  path: { type: "string", description: 'Absolute path, e.g. "/reports/summary.md"' },
@@ -3479,9 +3701,9 @@ var createFileTools = (vfs, opts = {}) => {
3479
3701
  return { written: info.path, size: info.size };
3480
3702
  }
3481
3703
  });
3482
- tools[`${p}delete`] = (0, import_ai9.tool)({
3704
+ tools[`${p}delete`] = (0, import_ai10.tool)({
3483
3705
  description: "Delete a file from the workspace.",
3484
- inputSchema: (0, import_ai9.jsonSchema)({
3706
+ inputSchema: (0, import_ai10.jsonSchema)({
3485
3707
  type: "object",
3486
3708
  properties: { path: { type: "string" } },
3487
3709
  required: ["path"],
@@ -3494,12 +3716,15 @@ var createFileTools = (vfs, opts = {}) => {
3494
3716
 
3495
3717
  // src/index.ts
3496
3718
  init_webllm();
3719
+ init_context_window();
3720
+ init_generate();
3721
+ init_util();
3497
3722
 
3498
3723
  // src/tools/define.ts
3499
- var import_ai10 = require("ai");
3724
+ var import_ai11 = require("ai");
3500
3725
  var defineTool = (config) => {
3501
3726
  const { promptHint, readOnly, ...rest } = config;
3502
- const t = (0, import_ai10.tool)(rest);
3727
+ const t = (0, import_ai11.tool)(rest);
3503
3728
  if (promptHint) t.promptHint = promptHint;
3504
3729
  if (readOnly) t.readOnly = true;
3505
3730
  return t;
@@ -3554,9 +3779,13 @@ var IndexedDBStore = class {
3554
3779
  return (await db.getAllKeys(SESSIONS_STORE)).map(String);
3555
3780
  }
3556
3781
  };
3782
+
3783
+ // src/index.ts
3784
+ init_compress();
3557
3785
  // Annotate the CommonJS export names for ESM import in node:
3558
3786
  0 && (module.exports = {
3559
3787
  AttachmentsNotSupportedError,
3788
+ ContextWindowExceededError,
3560
3789
  FILES_STORE,
3561
3790
  FIND_TOOLS_NAME,
3562
3791
  ImagesNotSupportedError,
@@ -3572,10 +3801,12 @@ var IndexedDBStore = class {
3572
3801
  SESSIONS_STORE,
3573
3802
  SKILL_TOOL_NAMES,
3574
3803
  TOOL_APPROVAL_MODES,
3804
+ TOOL_SEARCH_WINDOW_SHARE,
3575
3805
  ToolBudgetError,
3576
3806
  ToolDeniedError,
3577
3807
  VaultCredentialStore,
3578
3808
  VirtualFileSystem,
3809
+ WEBLLM_DEFAULT_CONTEXT_WINDOW,
3579
3810
  attachmentKind,
3580
3811
  buildModelFromStage,
3581
3812
  bytesToBase64,
@@ -3583,6 +3814,8 @@ var IndexedDBStore = class {
3583
3814
  compactHistory,
3584
3815
  compactSteps,
3585
3816
  compressHistory,
3817
+ contextOverflowOf,
3818
+ contextWindowOf,
3586
3819
  createAgent,
3587
3820
  createFileTools,
3588
3821
  createFindToolsTool,
@@ -3604,6 +3837,7 @@ var IndexedDBStore = class {
3604
3837
  estimateMessagesTokens,
3605
3838
  estimateTokens,
3606
3839
  executeStep,
3840
+ fitCompactionToWindow,
3607
3841
  generate,
3608
3842
  generateStructured,
3609
3843
  getOrCreateVaultKey,
@@ -3662,9 +3896,12 @@ var IndexedDBStore = class {
3662
3896
  toImagePart,
3663
3897
  tokenize,
3664
3898
  toolCatalogOf,
3899
+ toolDefinitionTokens,
3665
3900
  toolRunContextOf,
3666
3901
  unloadWebLLMModel,
3902
+ webLLMContextWindow,
3667
3903
  withRollingBreakpoint,
3668
- withSystem
3904
+ withSystem,
3905
+ withWebLLMContextWindow
3669
3906
  });
3670
3907
  //# sourceMappingURL=index.cjs.map