@everworker/oneringai 0.2.0 → 0.2.2

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
@@ -5,6 +5,8 @@ var jose = require('jose');
5
5
  var fs16 = require('fs');
6
6
  var eventemitter3 = require('eventemitter3');
7
7
  var path2 = require('path');
8
+ var TurndownService = require('turndown');
9
+ var readability = require('@mozilla/readability');
8
10
  var os2 = require('os');
9
11
  var OpenAI3 = require('openai');
10
12
  var Anthropic = require('@anthropic-ai/sdk');
@@ -12,6 +14,7 @@ var genai = require('@google/genai');
12
14
  require('zod/v3');
13
15
  var z4mini = require('zod/v4-mini');
14
16
  var z = require('zod/v4');
17
+ var spawn = require('cross-spawn');
15
18
  var process2 = require('process');
16
19
  var stream = require('stream');
17
20
  var fs15 = require('fs/promises');
@@ -19,8 +22,6 @@ var simpleIcons = require('simple-icons');
19
22
  var child_process = require('child_process');
20
23
  var util = require('util');
21
24
  var cheerio = require('cheerio');
22
- var TurndownService = require('turndown');
23
- var readability = require('@mozilla/readability');
24
25
  var vm = require('vm');
25
26
 
26
27
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -46,15 +47,16 @@ function _interopNamespace(e) {
46
47
  var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
47
48
  var fs16__namespace = /*#__PURE__*/_interopNamespace(fs16);
48
49
  var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
50
+ var TurndownService__default = /*#__PURE__*/_interopDefault(TurndownService);
49
51
  var os2__namespace = /*#__PURE__*/_interopNamespace(os2);
50
52
  var OpenAI3__default = /*#__PURE__*/_interopDefault(OpenAI3);
51
53
  var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
52
54
  var z4mini__namespace = /*#__PURE__*/_interopNamespace(z4mini);
53
55
  var z__namespace = /*#__PURE__*/_interopNamespace(z);
56
+ var spawn__default = /*#__PURE__*/_interopDefault(spawn);
54
57
  var process2__default = /*#__PURE__*/_interopDefault(process2);
55
58
  var fs15__namespace = /*#__PURE__*/_interopNamespace(fs15);
56
59
  var simpleIcons__namespace = /*#__PURE__*/_interopNamespace(simpleIcons);
57
- var TurndownService__default = /*#__PURE__*/_interopDefault(TurndownService);
58
60
  var vm__namespace = /*#__PURE__*/_interopNamespace(vm);
59
61
 
60
62
  var __create = Object.create;
@@ -338,6 +340,10 @@ var init_pkce = __esm({
338
340
  });
339
341
 
340
342
  // src/connectors/oauth/flows/AuthCodePKCE.ts
343
+ function isPublicClientError(responseBody) {
344
+ const lower = responseBody.toLowerCase();
345
+ return lower.includes("aadsts700025") || lower.includes("invalid_client") && lower.includes("public");
346
+ }
341
347
  var AuthCodePKCEFlow;
342
348
  var init_AuthCodePKCE = __esm({
343
349
  "src/connectors/oauth/flows/AuthCodePKCE.ts"() {
@@ -433,14 +439,32 @@ var init_AuthCodePKCE = __esm({
433
439
  if (this.config.usePKCE !== false && verifierData) {
434
440
  params.append("code_verifier", verifierData.verifier);
435
441
  }
436
- const response = await fetch(this.config.tokenUrl, {
442
+ let response = await fetch(this.config.tokenUrl, {
437
443
  method: "POST",
438
444
  headers: {
439
445
  "Content-Type": "application/x-www-form-urlencoded"
440
446
  },
441
447
  body: params
442
448
  });
443
- if (!response.ok) {
449
+ if (!response.ok && this.config.clientSecret) {
450
+ const errorText = await response.text();
451
+ if (isPublicClientError(errorText)) {
452
+ params.delete("client_secret");
453
+ response = await fetch(this.config.tokenUrl, {
454
+ method: "POST",
455
+ headers: {
456
+ "Content-Type": "application/x-www-form-urlencoded"
457
+ },
458
+ body: params
459
+ });
460
+ if (!response.ok) {
461
+ const retryError = await response.text();
462
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${retryError}`);
463
+ }
464
+ } else {
465
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`);
466
+ }
467
+ } else if (!response.ok) {
444
468
  const error = await response.text();
445
469
  throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${error}`);
446
470
  }
@@ -486,14 +510,32 @@ var init_AuthCodePKCE = __esm({
486
510
  if (this.config.clientSecret) {
487
511
  params.append("client_secret", this.config.clientSecret);
488
512
  }
489
- const response = await fetch(this.config.tokenUrl, {
513
+ let response = await fetch(this.config.tokenUrl, {
490
514
  method: "POST",
491
515
  headers: {
492
516
  "Content-Type": "application/x-www-form-urlencoded"
493
517
  },
494
518
  body: params
495
519
  });
496
- if (!response.ok) {
520
+ if (!response.ok && this.config.clientSecret) {
521
+ const errorText = await response.text();
522
+ if (isPublicClientError(errorText)) {
523
+ params.delete("client_secret");
524
+ response = await fetch(this.config.tokenUrl, {
525
+ method: "POST",
526
+ headers: {
527
+ "Content-Type": "application/x-www-form-urlencoded"
528
+ },
529
+ body: params
530
+ });
531
+ if (!response.ok) {
532
+ const retryError = await response.text();
533
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${retryError}`);
534
+ }
535
+ } else {
536
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${errorText}`);
537
+ }
538
+ } else if (!response.ok) {
497
539
  const error = await response.text();
498
540
  throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${error}`);
499
541
  }
@@ -2262,6 +2304,66 @@ var init_Connector = __esm({
2262
2304
  }
2263
2305
  });
2264
2306
 
2307
+ // src/core/constants.ts
2308
+ var AGENT_DEFAULTS, TOKEN_ESTIMATION, DOCUMENT_DEFAULTS;
2309
+ var init_constants = __esm({
2310
+ "src/core/constants.ts"() {
2311
+ AGENT_DEFAULTS = {
2312
+ /** Default maximum iterations for agentic loop */
2313
+ MAX_ITERATIONS: 50,
2314
+ /** Default temperature for LLM calls */
2315
+ DEFAULT_TEMPERATURE: 0.7,
2316
+ /** Message injected when max iterations is reached */
2317
+ MAX_ITERATIONS_MESSAGE: `You have reached the maximum iteration limit for this execution. Please:
2318
+ 1. Summarize what you have accomplished so far
2319
+ 2. Explain what remains to be done (if anything)
2320
+ 3. Ask the user if they would like you to continue
2321
+
2322
+ Do NOT use any tools in this response - just provide a clear summary and ask for confirmation to proceed.`
2323
+ };
2324
+ TOKEN_ESTIMATION = {
2325
+ /** Characters per token for code */
2326
+ CODE_CHARS_PER_TOKEN: 3,
2327
+ /** Characters per token for prose */
2328
+ PROSE_CHARS_PER_TOKEN: 4,
2329
+ /** Characters per token for mixed content */
2330
+ MIXED_CHARS_PER_TOKEN: 3.5,
2331
+ /** Default characters per token */
2332
+ DEFAULT_CHARS_PER_TOKEN: 4
2333
+ };
2334
+ DOCUMENT_DEFAULTS = {
2335
+ /** Maximum estimated tokens in output */
2336
+ MAX_OUTPUT_TOKENS: 1e5,
2337
+ /** Maximum output size in bytes (5MB) */
2338
+ MAX_OUTPUT_BYTES: 5 * 1024 * 1024,
2339
+ /** Maximum download size for URL sources (50MB) */
2340
+ MAX_DOWNLOAD_SIZE_BYTES: 50 * 1024 * 1024,
2341
+ /** Download timeout for URL sources */
2342
+ DOWNLOAD_TIMEOUT_MS: 6e4,
2343
+ /** Maximum extracted images from a single document */
2344
+ MAX_EXTRACTED_IMAGES: 50,
2345
+ /** Maximum Excel rows per sheet */
2346
+ MAX_EXCEL_ROWS: 1e3,
2347
+ /** Maximum Excel columns per sheet */
2348
+ MAX_EXCEL_COLUMNS: 50,
2349
+ /** Maximum HTML content length */
2350
+ MAX_HTML_LENGTH: 5e4,
2351
+ /** Characters per token estimate */
2352
+ CHARS_PER_TOKEN: 4,
2353
+ /** Estimated tokens for an image with auto detail */
2354
+ IMAGE_TOKENS_AUTO: 765,
2355
+ /** Estimated tokens for an image with low detail */
2356
+ IMAGE_TOKENS_LOW: 85,
2357
+ /** Image filter defaults */
2358
+ IMAGE_FILTER: {
2359
+ MIN_WIDTH: 50,
2360
+ MIN_HEIGHT: 50,
2361
+ MIN_SIZE_BYTES: 1024
2362
+ }
2363
+ };
2364
+ }
2365
+ });
2366
+
2265
2367
  // node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
2266
2368
  var require_code = __commonJS({
2267
2369
  "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js"(exports$1) {
@@ -14575,491 +14677,961 @@ var require_dist = __commonJS({
14575
14677
  }
14576
14678
  });
14577
14679
 
14578
- // node_modules/isexe/windows.js
14579
- var require_windows = __commonJS({
14580
- "node_modules/isexe/windows.js"(exports$1, module) {
14581
- module.exports = isexe;
14582
- isexe.sync = sync;
14583
- var fs17 = __require("fs");
14584
- function checkPathExt(path6, options) {
14585
- var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
14586
- if (!pathext) {
14587
- return true;
14588
- }
14589
- pathext = pathext.split(";");
14590
- if (pathext.indexOf("") !== -1) {
14591
- return true;
14680
+ // src/capabilities/documents/handlers/TextHandler.ts
14681
+ var CODE_FENCE_FORMATS, TextHandler;
14682
+ var init_TextHandler = __esm({
14683
+ "src/capabilities/documents/handlers/TextHandler.ts"() {
14684
+ init_constants();
14685
+ CODE_FENCE_FORMATS = {
14686
+ json: "json",
14687
+ xml: "xml",
14688
+ yaml: "yaml",
14689
+ yml: "yaml"
14690
+ };
14691
+ TextHandler = class {
14692
+ name = "TextHandler";
14693
+ supportedFormats = ["txt", "md", "json", "xml", "yaml", "yml"];
14694
+ async handle(buffer, filename, format, _options) {
14695
+ const text = buffer.toString("utf-8");
14696
+ const fenceLanguage = CODE_FENCE_FORMATS[format];
14697
+ const content = fenceLanguage ? `\`\`\`${fenceLanguage}
14698
+ ${text}
14699
+ \`\`\`` : text;
14700
+ const sizeBytes = Buffer.byteLength(content, "utf-8");
14701
+ return [
14702
+ {
14703
+ type: "text",
14704
+ content,
14705
+ metadata: {
14706
+ sourceFilename: filename,
14707
+ format,
14708
+ index: 0,
14709
+ sizeBytes,
14710
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
14711
+ }
14712
+ }
14713
+ ];
14592
14714
  }
14593
- for (var i = 0; i < pathext.length; i++) {
14594
- var p = pathext[i].toLowerCase();
14595
- if (p && path6.substr(-p.length).toLowerCase() === p) {
14596
- return true;
14715
+ };
14716
+ }
14717
+ });
14718
+
14719
+ // src/capabilities/documents/handlers/ImageHandler.ts
14720
+ var MIME_TYPES, ImageHandler;
14721
+ var init_ImageHandler = __esm({
14722
+ "src/capabilities/documents/handlers/ImageHandler.ts"() {
14723
+ init_constants();
14724
+ MIME_TYPES = {
14725
+ png: "image/png",
14726
+ jpg: "image/jpeg",
14727
+ jpeg: "image/jpeg",
14728
+ gif: "image/gif",
14729
+ webp: "image/webp",
14730
+ svg: "image/svg+xml"
14731
+ };
14732
+ ImageHandler = class {
14733
+ name = "ImageHandler";
14734
+ supportedFormats = ["png", "jpg", "jpeg", "gif", "webp", "svg"];
14735
+ async handle(buffer, filename, format, _options) {
14736
+ const pieces = [];
14737
+ const mimeType = MIME_TYPES[format] || "application/octet-stream";
14738
+ pieces.push({
14739
+ type: "image",
14740
+ base64: buffer.toString("base64"),
14741
+ mimeType,
14742
+ metadata: {
14743
+ sourceFilename: filename,
14744
+ format,
14745
+ index: 0,
14746
+ sizeBytes: buffer.length,
14747
+ estimatedTokens: DOCUMENT_DEFAULTS.IMAGE_TOKENS_AUTO,
14748
+ label: filename
14749
+ }
14750
+ });
14751
+ if (format === "svg") {
14752
+ const svgText = buffer.toString("utf-8");
14753
+ const sizeBytes = Buffer.byteLength(svgText, "utf-8");
14754
+ pieces.push({
14755
+ type: "text",
14756
+ content: `\`\`\`svg
14757
+ ${svgText}
14758
+ \`\`\``,
14759
+ metadata: {
14760
+ sourceFilename: filename,
14761
+ format,
14762
+ index: 1,
14763
+ section: "SVG source",
14764
+ sizeBytes,
14765
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
14766
+ }
14767
+ });
14597
14768
  }
14769
+ return pieces;
14598
14770
  }
14599
- return false;
14600
- }
14601
- function checkStat(stat6, path6, options) {
14602
- if (!stat6.isSymbolicLink() && !stat6.isFile()) {
14603
- return false;
14604
- }
14605
- return checkPathExt(path6, options);
14606
- }
14607
- function isexe(path6, options, cb) {
14608
- fs17.stat(path6, function(er, stat6) {
14609
- cb(er, er ? false : checkStat(stat6, path6, options));
14610
- });
14611
- }
14612
- function sync(path6, options) {
14613
- return checkStat(fs17.statSync(path6), path6, options);
14614
- }
14771
+ };
14615
14772
  }
14616
14773
  });
14617
-
14618
- // node_modules/isexe/mode.js
14619
- var require_mode = __commonJS({
14620
- "node_modules/isexe/mode.js"(exports$1, module) {
14621
- module.exports = isexe;
14622
- isexe.sync = sync;
14623
- var fs17 = __require("fs");
14624
- function isexe(path6, options, cb) {
14625
- fs17.stat(path6, function(er, stat6) {
14626
- cb(er, er ? false : checkStat(stat6, options));
14627
- });
14628
- }
14629
- function sync(path6, options) {
14630
- return checkStat(fs17.statSync(path6), options);
14774
+ async function getJSDOM() {
14775
+ if (!JSDOM) {
14776
+ const jsdom = await import('jsdom');
14777
+ JSDOM = jsdom.JSDOM;
14778
+ }
14779
+ return JSDOM;
14780
+ }
14781
+ async function htmlToMarkdown(html, url2, maxLength = 5e4) {
14782
+ const JSDOMClass = await getJSDOM();
14783
+ const dom = new JSDOMClass(html, { url: url2 });
14784
+ const document = dom.window.document;
14785
+ let title = document.title || "";
14786
+ let byline;
14787
+ let excerpt;
14788
+ let contentHtml = html;
14789
+ let wasReadabilityUsed = false;
14790
+ try {
14791
+ const clonedDoc = document.cloneNode(true);
14792
+ const reader = new readability.Readability(clonedDoc);
14793
+ const article = reader.parse();
14794
+ if (article && article.content && article.content.length > 100) {
14795
+ contentHtml = article.content;
14796
+ title = article.title || title;
14797
+ byline = article.byline || void 0;
14798
+ excerpt = article.excerpt || void 0;
14799
+ wasReadabilityUsed = true;
14631
14800
  }
14632
- function checkStat(stat6, options) {
14633
- return stat6.isFile() && checkMode(stat6, options);
14801
+ } catch {
14802
+ }
14803
+ const turndown = new TurndownService__default.default({
14804
+ headingStyle: "atx",
14805
+ codeBlockStyle: "fenced",
14806
+ bulletListMarker: "-",
14807
+ emDelimiter: "_"
14808
+ });
14809
+ turndown.remove(["script", "style", "nav", "footer", "aside", "iframe", "noscript"]);
14810
+ turndown.addRule("pre", {
14811
+ filter: ["pre"],
14812
+ replacement: (content, node) => {
14813
+ const element = node;
14814
+ const code = element.querySelector?.("code");
14815
+ const lang = code?.className?.match(/language-(\w+)/)?.[1] || "";
14816
+ const text = code?.textContent || content;
14817
+ return `
14818
+ \`\`\`${lang}
14819
+ ${text}
14820
+ \`\`\`
14821
+ `;
14634
14822
  }
14635
- function checkMode(stat6, options) {
14636
- var mod = stat6.mode;
14637
- var uid = stat6.uid;
14638
- var gid = stat6.gid;
14639
- var myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid();
14640
- var myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid();
14641
- var u = parseInt("100", 8);
14642
- var g = parseInt("010", 8);
14643
- var o = parseInt("001", 8);
14644
- var ug = u | g;
14645
- var ret = mod & o || mod & g && gid === myGid || mod & u && uid === myUid || mod & ug && myUid === 0;
14646
- return ret;
14823
+ });
14824
+ let markdown = turndown.turndown(contentHtml);
14825
+ markdown = markdown.replace(/\n{3,}/g, "\n\n").replace(/^\s+|\s+$/g, "").replace(/[ \t]+$/gm, "");
14826
+ let wasTruncated = false;
14827
+ if (markdown.length > maxLength) {
14828
+ const truncateAt = markdown.lastIndexOf("\n\n", maxLength);
14829
+ if (truncateAt > maxLength * 0.5) {
14830
+ markdown = markdown.slice(0, truncateAt) + "\n\n...[content truncated]";
14831
+ } else {
14832
+ markdown = markdown.slice(0, maxLength) + "...[truncated]";
14647
14833
  }
14834
+ wasTruncated = true;
14835
+ }
14836
+ return {
14837
+ markdown,
14838
+ title,
14839
+ byline,
14840
+ excerpt,
14841
+ wasReadabilityUsed,
14842
+ wasTruncated
14843
+ };
14844
+ }
14845
+ var JSDOM;
14846
+ var init_htmlToMarkdown = __esm({
14847
+ "src/tools/web/htmlToMarkdown.ts"() {
14848
+ JSDOM = null;
14849
+ }
14850
+ });
14851
+
14852
+ // src/capabilities/documents/handlers/HTMLHandler.ts
14853
+ var HTMLHandler;
14854
+ var init_HTMLHandler = __esm({
14855
+ "src/capabilities/documents/handlers/HTMLHandler.ts"() {
14856
+ init_constants();
14857
+ init_htmlToMarkdown();
14858
+ HTMLHandler = class {
14859
+ name = "HTMLHandler";
14860
+ supportedFormats = ["html"];
14861
+ async handle(buffer, filename, format, options) {
14862
+ const html = buffer.toString("utf-8");
14863
+ const maxLength = options.formatOptions?.html?.maxLength ?? DOCUMENT_DEFAULTS.MAX_HTML_LENGTH;
14864
+ const result = await htmlToMarkdown(html, `file://${filename}`, maxLength);
14865
+ const content = result.markdown;
14866
+ const sizeBytes = Buffer.byteLength(content, "utf-8");
14867
+ return [
14868
+ {
14869
+ type: "text",
14870
+ content,
14871
+ metadata: {
14872
+ sourceFilename: filename,
14873
+ format,
14874
+ index: 0,
14875
+ sizeBytes,
14876
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN),
14877
+ label: result.title || void 0
14878
+ }
14879
+ }
14880
+ ];
14881
+ }
14882
+ };
14648
14883
  }
14649
14884
  });
14650
14885
 
14651
- // node_modules/isexe/index.js
14652
- var require_isexe = __commonJS({
14653
- "node_modules/isexe/index.js"(exports$1, module) {
14654
- __require("fs");
14655
- var core;
14656
- if (process.platform === "win32" || global.TESTING_WINDOWS) {
14657
- core = require_windows();
14658
- } else {
14659
- core = require_mode();
14660
- }
14661
- module.exports = isexe;
14662
- isexe.sync = sync;
14663
- function isexe(path6, options, cb) {
14664
- if (typeof options === "function") {
14665
- cb = options;
14666
- options = {};
14667
- }
14668
- if (!cb) {
14669
- if (typeof Promise !== "function") {
14670
- throw new TypeError("callback not provided");
14671
- }
14672
- return new Promise(function(resolve4, reject) {
14673
- isexe(path6, options || {}, function(er, is) {
14674
- if (er) {
14675
- reject(er);
14676
- } else {
14677
- resolve4(is);
14678
- }
14679
- });
14886
+ // src/capabilities/documents/handlers/OfficeHandler.ts
14887
+ async function getParseOffice() {
14888
+ if (!parseOffice) {
14889
+ const mod = await import('officeparser');
14890
+ parseOffice = mod.parseOffice;
14891
+ }
14892
+ return parseOffice;
14893
+ }
14894
+ var parseOffice, OfficeHandler;
14895
+ var init_OfficeHandler = __esm({
14896
+ "src/capabilities/documents/handlers/OfficeHandler.ts"() {
14897
+ init_constants();
14898
+ parseOffice = null;
14899
+ OfficeHandler = class {
14900
+ name = "OfficeHandler";
14901
+ supportedFormats = ["docx", "pptx", "odt", "odp", "ods", "rtf"];
14902
+ async handle(buffer, filename, format, options) {
14903
+ const parse = await getParseOffice();
14904
+ const extractImages = options.extractImages !== false;
14905
+ const includeSpeakerNotes = options.formatOptions?.office?.includeSpeakerNotes !== false;
14906
+ const ast = await parse(buffer, {
14907
+ extractAttachments: extractImages,
14908
+ ignoreNotes: !includeSpeakerNotes
14680
14909
  });
14910
+ const pieces = [];
14911
+ let pieceIndex = 0;
14912
+ const content = ast.content || [];
14913
+ const markdown = this.astToMarkdown(content, format);
14914
+ if (format === "pptx" || format === "odp") {
14915
+ const slides = this.splitBySlides(content);
14916
+ for (let i = 0; i < slides.length; i++) {
14917
+ const slideContent = this.astToMarkdown(slides[i] ?? [], format);
14918
+ if (slideContent.trim()) {
14919
+ const sizeBytes = Buffer.byteLength(slideContent, "utf-8");
14920
+ pieces.push({
14921
+ type: "text",
14922
+ content: slideContent,
14923
+ metadata: {
14924
+ sourceFilename: filename,
14925
+ format,
14926
+ index: pieceIndex++,
14927
+ section: `Slide ${i + 1}`,
14928
+ sizeBytes,
14929
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
14930
+ }
14931
+ });
14932
+ }
14933
+ }
14934
+ } else {
14935
+ if (markdown.trim()) {
14936
+ const sizeBytes = Buffer.byteLength(markdown, "utf-8");
14937
+ pieces.push({
14938
+ type: "text",
14939
+ content: markdown,
14940
+ metadata: {
14941
+ sourceFilename: filename,
14942
+ format,
14943
+ index: pieceIndex++,
14944
+ sizeBytes,
14945
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
14946
+ }
14947
+ });
14948
+ }
14949
+ }
14950
+ if (extractImages && ast.attachments?.length > 0) {
14951
+ for (const attachment of ast.attachments) {
14952
+ if (attachment.type === "image" && attachment.data) {
14953
+ const imageData = attachment.data;
14954
+ const sizeBytes = Math.ceil(imageData.length * 0.75);
14955
+ pieces.push({
14956
+ type: "image",
14957
+ base64: imageData,
14958
+ mimeType: attachment.mimeType || "image/png",
14959
+ metadata: {
14960
+ sourceFilename: filename,
14961
+ format,
14962
+ index: pieceIndex++,
14963
+ sizeBytes,
14964
+ estimatedTokens: DOCUMENT_DEFAULTS.IMAGE_TOKENS_AUTO,
14965
+ label: attachment.altText || attachment.name || void 0
14966
+ }
14967
+ });
14968
+ }
14969
+ }
14970
+ }
14971
+ return pieces;
14681
14972
  }
14682
- core(path6, options || {}, function(er, is) {
14683
- if (er) {
14684
- if (er.code === "EACCES" || options && options.ignoreErrors) {
14685
- er = null;
14686
- is = false;
14973
+ /**
14974
+ * Split AST content into slide groups
14975
+ */
14976
+ splitBySlides(content) {
14977
+ const slides = [];
14978
+ let currentSlide = [];
14979
+ for (const node of content) {
14980
+ if (node.type === "slide") {
14981
+ if (currentSlide.length > 0) {
14982
+ slides.push(currentSlide);
14983
+ }
14984
+ currentSlide = [node];
14985
+ } else {
14986
+ currentSlide.push(node);
14687
14987
  }
14688
14988
  }
14689
- cb(er, is);
14690
- });
14691
- }
14692
- function sync(path6, options) {
14693
- try {
14694
- return core.sync(path6, options || {});
14695
- } catch (er) {
14696
- if (options && options.ignoreErrors || er.code === "EACCES") {
14697
- return false;
14698
- } else {
14699
- throw er;
14989
+ if (currentSlide.length > 0) {
14990
+ slides.push(currentSlide);
14700
14991
  }
14992
+ if (slides.length === 0 && content.length > 0) {
14993
+ slides.push(content);
14994
+ }
14995
+ return slides;
14701
14996
  }
14702
- }
14703
- }
14704
- });
14705
-
14706
- // node_modules/which/which.js
14707
- var require_which = __commonJS({
14708
- "node_modules/which/which.js"(exports$1, module) {
14709
- var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
14710
- var path6 = __require("path");
14711
- var COLON = isWindows ? ";" : ":";
14712
- var isexe = require_isexe();
14713
- var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
14714
- var getPathInfo = (cmd, opt) => {
14715
- const colon = opt.colon || COLON;
14716
- const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [
14717
- // windows always checks the cwd first
14718
- ...isWindows ? [process.cwd()] : [],
14719
- ...(opt.path || process.env.PATH || /* istanbul ignore next: very unusual */
14720
- "").split(colon)
14721
- ];
14722
- const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "";
14723
- const pathExt = isWindows ? pathExtExe.split(colon) : [""];
14724
- if (isWindows) {
14725
- if (cmd.indexOf(".") !== -1 && pathExt[0] !== "")
14726
- pathExt.unshift("");
14997
+ /**
14998
+ * Convert AST nodes to markdown
14999
+ */
15000
+ astToMarkdown(nodes, format) {
15001
+ const parts = [];
15002
+ for (const node of nodes) {
15003
+ const md = this.nodeToMarkdown(node, format);
15004
+ if (md) parts.push(md);
15005
+ }
15006
+ return parts.join("\n\n");
14727
15007
  }
14728
- return {
14729
- pathEnv,
14730
- pathExt,
14731
- pathExtExe
14732
- };
14733
- };
14734
- var which = (cmd, opt, cb) => {
14735
- if (typeof opt === "function") {
14736
- cb = opt;
14737
- opt = {};
14738
- }
14739
- if (!opt)
14740
- opt = {};
14741
- const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
14742
- const found = [];
14743
- const step = (i) => new Promise((resolve4, reject) => {
14744
- if (i === pathEnv.length)
14745
- return opt.all && found.length ? resolve4(found) : reject(getNotFoundError(cmd));
14746
- const ppRaw = pathEnv[i];
14747
- const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
14748
- const pCmd = path6.join(pathPart, cmd);
14749
- const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
14750
- resolve4(subStep(p, i, 0));
14751
- });
14752
- const subStep = (p, i, ii) => new Promise((resolve4, reject) => {
14753
- if (ii === pathExt.length)
14754
- return resolve4(step(i + 1));
14755
- const ext = pathExt[ii];
14756
- isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
14757
- if (!er && is) {
14758
- if (opt.all)
14759
- found.push(p + ext);
14760
- else
14761
- return resolve4(p + ext);
15008
+ /**
15009
+ * Convert a single AST node to markdown
15010
+ */
15011
+ nodeToMarkdown(node, format) {
15012
+ if (!node) return "";
15013
+ switch (node.type) {
15014
+ case "heading": {
15015
+ const level = node.metadata?.level || 1;
15016
+ const prefix = "#".repeat(Math.min(level, 6));
15017
+ return `${prefix} ${node.text || ""}`;
15018
+ }
15019
+ case "paragraph":
15020
+ return this.formatText(node);
15021
+ case "text":
15022
+ return this.formatText(node);
15023
+ case "list": {
15024
+ const items = node.children || [];
15025
+ return items.map((item, i) => {
15026
+ const indent = " ".repeat(node.metadata?.indentation || 0);
15027
+ const prefix = node.metadata?.listType === "ordered" ? `${i + 1}.` : "-";
15028
+ return `${indent}${prefix} ${item.text || this.getNodeText(item)}`;
15029
+ }).join("\n");
15030
+ }
15031
+ case "table": {
15032
+ return this.tableToMarkdown(node);
15033
+ }
15034
+ case "slide": {
15035
+ const slideNum = node.metadata?.slideNumber || "";
15036
+ const childContent = node.children ? node.children.map((c) => this.nodeToMarkdown(c, format)).filter(Boolean).join("\n\n") : node.text || "";
15037
+ return slideNum ? `### Slide ${slideNum}
15038
+
15039
+ ${childContent}` : childContent;
15040
+ }
15041
+ case "note":
15042
+ return `> **Note:** ${node.text || this.getNodeText(node)}`;
15043
+ case "sheet": {
15044
+ const sheetName = node.metadata?.sheetName || "Sheet";
15045
+ const childContent = node.children ? node.children.map((c) => this.nodeToMarkdown(c, format)).filter(Boolean).join("\n") : "";
15046
+ return `## Sheet: ${sheetName}
15047
+
15048
+ ${childContent}`;
15049
+ }
15050
+ case "page": {
15051
+ const pageNum = node.metadata?.pageNumber || "";
15052
+ const childContent = node.children ? node.children.map((c) => this.nodeToMarkdown(c, format)).filter(Boolean).join("\n\n") : node.text || "";
15053
+ return pageNum ? `--- Page ${pageNum} ---
15054
+
15055
+ ${childContent}` : childContent;
15056
+ }
15057
+ case "image":
15058
+ return `[Image: ${node.metadata?.altText || node.metadata?.attachmentName || "embedded image"}]`;
15059
+ case "chart":
15060
+ return `[Chart: ${node.metadata?.attachmentName || "embedded chart"}]`;
15061
+ default:
15062
+ return node.text || this.getNodeText(node);
15063
+ }
15064
+ }
15065
+ /**
15066
+ * Get text from a node recursively
15067
+ */
15068
+ getNodeText(node) {
15069
+ if (node.text) return node.text;
15070
+ if (node.children) {
15071
+ return node.children.map((c) => this.getNodeText(c)).join("");
15072
+ }
15073
+ return "";
15074
+ }
15075
+ /**
15076
+ * Format text with markdown formatting
15077
+ */
15078
+ formatText(node) {
15079
+ if (!node.children || node.children.length === 0) {
15080
+ return node.text || "";
15081
+ }
15082
+ return node.children.map((child) => {
15083
+ let text = child.text || this.getNodeText(child);
15084
+ if (!text) return "";
15085
+ const fmt = child.formatting;
15086
+ if (fmt) {
15087
+ if (fmt.bold) text = `**${text}**`;
15088
+ if (fmt.italic) text = `_${text}_`;
15089
+ if (fmt.strikethrough) text = `~~${text}~~`;
14762
15090
  }
14763
- return resolve4(subStep(p, i, ii + 1));
14764
- });
14765
- });
14766
- return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
14767
- };
14768
- var whichSync = (cmd, opt) => {
14769
- opt = opt || {};
14770
- const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
14771
- const found = [];
14772
- for (let i = 0; i < pathEnv.length; i++) {
14773
- const ppRaw = pathEnv[i];
14774
- const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
14775
- const pCmd = path6.join(pathPart, cmd);
14776
- const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
14777
- for (let j = 0; j < pathExt.length; j++) {
14778
- const cur = p + pathExt[j];
14779
- try {
14780
- const is = isexe.sync(cur, { pathExt: pathExtExe });
14781
- if (is) {
14782
- if (opt.all)
14783
- found.push(cur);
14784
- else
14785
- return cur;
14786
- }
14787
- } catch (ex) {
15091
+ if (child.metadata?.link && child.metadata?.linkType === "external") {
15092
+ text = `[${text}](${child.metadata.link})`;
14788
15093
  }
14789
- }
15094
+ return text;
15095
+ }).join("");
14790
15096
  }
14791
- if (opt.all && found.length)
14792
- return found;
14793
- if (opt.nothrow)
14794
- return null;
14795
- throw getNotFoundError(cmd);
14796
- };
14797
- module.exports = which;
14798
- which.sync = whichSync;
14799
- }
14800
- });
15097
+ /**
15098
+ * Convert table node to markdown table
15099
+ */
15100
+ tableToMarkdown(node) {
15101
+ if (!node.children || node.children.length === 0) return "";
15102
+ const rows = [];
15103
+ for (const row of node.children) {
15104
+ if (row.type === "row" && row.children) {
15105
+ rows.push(row.children.map((cell) => {
15106
+ const text = cell.text || this.getNodeText(cell);
15107
+ return text.replace(/\|/g, "\\|").trim();
15108
+ }));
15109
+ }
15110
+ }
15111
+ if (rows.length === 0) return "";
15112
+ const maxCols = Math.max(...rows.map((r) => r.length));
15113
+ const normalizedRows = rows.map((r) => {
15114
+ while (r.length < maxCols) r.push("");
15115
+ return r;
15116
+ });
15117
+ const firstRow = normalizedRows[0] ?? [];
15118
+ const header = `| ${firstRow.join(" | ")} |`;
15119
+ const separator = `| ${firstRow.map(() => "---").join(" | ")} |`;
15120
+ const body = normalizedRows.slice(1).map((r) => `| ${r.join(" | ")} |`).join("\n");
15121
+ return body ? `${header}
15122
+ ${separator}
15123
+ ${body}` : `${header}
15124
+ ${separator}`;
15125
+ }
15126
+ };
15127
+ }
15128
+ });
15129
+
15130
+ // src/capabilities/documents/handlers/ExcelHandler.ts
15131
+ async function getExcelJS() {
15132
+ if (!ExcelJS) {
15133
+ ExcelJS = await import('exceljs');
15134
+ }
15135
+ return ExcelJS;
15136
+ }
15137
+ var ExcelJS, ExcelHandler;
15138
+ var init_ExcelHandler = __esm({
15139
+ "src/capabilities/documents/handlers/ExcelHandler.ts"() {
15140
+ init_constants();
15141
+ ExcelJS = null;
15142
+ ExcelHandler = class {
15143
+ name = "ExcelHandler";
15144
+ supportedFormats = ["xlsx", "csv"];
15145
+ async handle(buffer, filename, format, options) {
15146
+ const exceljs = await getExcelJS();
15147
+ const Workbook = exceljs.Workbook || exceljs.default?.Workbook;
15148
+ const excelOpts = {
15149
+ maxRows: options.formatOptions?.excel?.maxRows ?? DOCUMENT_DEFAULTS.MAX_EXCEL_ROWS,
15150
+ maxColumns: options.formatOptions?.excel?.maxColumns ?? DOCUMENT_DEFAULTS.MAX_EXCEL_COLUMNS,
15151
+ tableFormat: options.formatOptions?.excel?.tableFormat ?? "markdown",
15152
+ includeFormulas: options.formatOptions?.excel?.includeFormulas ?? false
15153
+ };
15154
+ const workbook = new Workbook();
15155
+ if (format === "csv") {
15156
+ await workbook.csv.read(
15157
+ new (await import('stream')).Readable({
15158
+ read() {
15159
+ this.push(buffer);
15160
+ this.push(null);
15161
+ }
15162
+ })
15163
+ );
15164
+ } else {
15165
+ await workbook.xlsx.load(buffer);
15166
+ }
15167
+ const pieces = [];
15168
+ let pieceIndex = 0;
15169
+ const requestedSheets = options.pages;
15170
+ workbook.eachSheet((worksheet, sheetId) => {
15171
+ if (requestedSheets && requestedSheets.length > 0) {
15172
+ const isRequested = requestedSheets.some((p) => {
15173
+ if (typeof p === "number") return sheetId === p;
15174
+ return worksheet.name === p || String(sheetId) === p;
15175
+ });
15176
+ if (!isRequested) return;
15177
+ }
15178
+ const content = this.sheetToContent(worksheet, excelOpts);
15179
+ if (!content.trim()) return;
15180
+ const sheetContent = format === "csv" ? content : `## Sheet: ${worksheet.name}
14801
15181
 
14802
- // node_modules/path-key/index.js
14803
- var require_path_key = __commonJS({
14804
- "node_modules/path-key/index.js"(exports$1, module) {
14805
- var pathKey = (options = {}) => {
14806
- const environment = options.env || process.env;
14807
- const platform2 = options.platform || process.platform;
14808
- if (platform2 !== "win32") {
14809
- return "PATH";
15182
+ ${content}`;
15183
+ const sizeBytes = Buffer.byteLength(sheetContent, "utf-8");
15184
+ pieces.push({
15185
+ type: "text",
15186
+ content: sheetContent,
15187
+ metadata: {
15188
+ sourceFilename: filename,
15189
+ format,
15190
+ index: pieceIndex++,
15191
+ section: format === "csv" ? void 0 : worksheet.name,
15192
+ sizeBytes,
15193
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15194
+ }
15195
+ });
15196
+ });
15197
+ return pieces;
14810
15198
  }
14811
- return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path";
14812
- };
14813
- module.exports = pathKey;
14814
- module.exports.default = pathKey;
14815
- }
14816
- });
14817
-
14818
- // node_modules/cross-spawn/lib/util/resolveCommand.js
14819
- var require_resolveCommand = __commonJS({
14820
- "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports$1, module) {
14821
- var path6 = __require("path");
14822
- var which = require_which();
14823
- var getPathKey = require_path_key();
14824
- function resolveCommandAttempt(parsed, withoutPathExt) {
14825
- const env = parsed.options.env || process.env;
14826
- const cwd = process.cwd();
14827
- const hasCustomCwd = parsed.options.cwd != null;
14828
- const shouldSwitchCwd = hasCustomCwd && process.chdir !== void 0 && !process.chdir.disabled;
14829
- if (shouldSwitchCwd) {
14830
- try {
14831
- process.chdir(parsed.options.cwd);
14832
- } catch (err) {
15199
+ /**
15200
+ * Convert a worksheet to the configured format
15201
+ */
15202
+ sheetToContent(worksheet, opts) {
15203
+ switch (opts.tableFormat) {
15204
+ case "csv":
15205
+ return this.sheetToCSV(worksheet, opts);
15206
+ case "json":
15207
+ return this.sheetToJSON(worksheet, opts);
15208
+ default:
15209
+ return this.sheetToMarkdownTable(worksheet, opts);
14833
15210
  }
14834
15211
  }
14835
- let resolved;
14836
- try {
14837
- resolved = which.sync(parsed.command, {
14838
- path: env[getPathKey({ env })],
14839
- pathExt: withoutPathExt ? path6.delimiter : void 0
15212
+ /**
15213
+ * Convert worksheet to markdown table
15214
+ */
15215
+ sheetToMarkdownTable(worksheet, opts) {
15216
+ const rows = this.extractRows(worksheet, opts);
15217
+ if (rows.length === 0) return "";
15218
+ const maxCols = Math.min(
15219
+ Math.max(...rows.map((r) => r.length)),
15220
+ opts.maxColumns
15221
+ );
15222
+ const normalizedRows = rows.map((r) => {
15223
+ const truncated = r.slice(0, maxCols);
15224
+ while (truncated.length < maxCols) truncated.push("");
15225
+ return truncated;
14840
15226
  });
14841
- } catch (e) {
14842
- } finally {
14843
- if (shouldSwitchCwd) {
14844
- process.chdir(cwd);
15227
+ const firstRow = normalizedRows[0] ?? [];
15228
+ const header = `| ${firstRow.join(" | ")} |`;
15229
+ const separator = `| ${firstRow.map(() => "---").join(" | ")} |`;
15230
+ const body = normalizedRows.slice(1).map((r) => `| ${r.join(" | ")} |`).join("\n");
15231
+ let result = `${header}
15232
+ ${separator}`;
15233
+ if (body) result += `
15234
+ ${body}`;
15235
+ if (worksheet.rowCount > opts.maxRows) {
15236
+ result += `
15237
+
15238
+ _...truncated (${worksheet.rowCount - opts.maxRows} more rows)_`;
14845
15239
  }
15240
+ return result;
14846
15241
  }
14847
- if (resolved) {
14848
- resolved = path6.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
15242
+ /**
15243
+ * Convert worksheet to CSV
15244
+ */
15245
+ sheetToCSV(worksheet, opts) {
15246
+ const rows = this.extractRows(worksheet, opts);
15247
+ return rows.map(
15248
+ (row) => row.slice(0, opts.maxColumns).map((cell) => {
15249
+ if (cell.includes(",") || cell.includes('"') || cell.includes("\n")) {
15250
+ return `"${cell.replace(/"/g, '""')}"`;
15251
+ }
15252
+ return cell;
15253
+ }).join(",")
15254
+ ).join("\n");
14849
15255
  }
14850
- return resolved;
14851
- }
14852
- function resolveCommand(parsed) {
14853
- return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
14854
- }
14855
- module.exports = resolveCommand;
14856
- }
14857
- });
14858
-
14859
- // node_modules/cross-spawn/lib/util/escape.js
14860
- var require_escape = __commonJS({
14861
- "node_modules/cross-spawn/lib/util/escape.js"(exports$1, module) {
14862
- var metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
14863
- function escapeCommand(arg) {
14864
- arg = arg.replace(metaCharsRegExp, "^$1");
14865
- return arg;
14866
- }
14867
- function escapeArgument(arg, doubleEscapeMetaChars) {
14868
- arg = `${arg}`;
14869
- arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
14870
- arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1");
14871
- arg = `"${arg}"`;
14872
- arg = arg.replace(metaCharsRegExp, "^$1");
14873
- if (doubleEscapeMetaChars) {
14874
- arg = arg.replace(metaCharsRegExp, "^$1");
15256
+ /**
15257
+ * Convert worksheet to JSON
15258
+ */
15259
+ sheetToJSON(worksheet, opts) {
15260
+ const rows = this.extractRows(worksheet, opts);
15261
+ if (rows.length < 2) return "[]";
15262
+ const headers = (rows[0] ?? []).slice(0, opts.maxColumns);
15263
+ const data = rows.slice(1).map((row) => {
15264
+ const obj = {};
15265
+ headers.forEach((header, i) => {
15266
+ if (header && i < row.length) {
15267
+ obj[header] = row[i] ?? "";
15268
+ }
15269
+ });
15270
+ return obj;
15271
+ });
15272
+ return "```json\n" + JSON.stringify(data, null, 2) + "\n```";
14875
15273
  }
14876
- return arg;
14877
- }
14878
- module.exports.command = escapeCommand;
14879
- module.exports.argument = escapeArgument;
14880
- }
14881
- });
14882
-
14883
- // node_modules/shebang-regex/index.js
14884
- var require_shebang_regex = __commonJS({
14885
- "node_modules/shebang-regex/index.js"(exports$1, module) {
14886
- module.exports = /^#!(.*)/;
14887
- }
14888
- });
14889
-
14890
- // node_modules/shebang-command/index.js
14891
- var require_shebang_command = __commonJS({
14892
- "node_modules/shebang-command/index.js"(exports$1, module) {
14893
- var shebangRegex = require_shebang_regex();
14894
- module.exports = (string3 = "") => {
14895
- const match = string3.match(shebangRegex);
14896
- if (!match) {
14897
- return null;
15274
+ /**
15275
+ * Extract rows from worksheet as string arrays
15276
+ */
15277
+ extractRows(worksheet, opts) {
15278
+ const rows = [];
15279
+ let rowCount = 0;
15280
+ worksheet.eachRow({ includeEmpty: false }, (row) => {
15281
+ if (rowCount >= opts.maxRows) return;
15282
+ rowCount++;
15283
+ const cells = [];
15284
+ row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
15285
+ if (colNumber > opts.maxColumns) return;
15286
+ let value = "";
15287
+ if (opts.includeFormulas && cell.formula) {
15288
+ value = `${this.getCellValue(cell)} (=${cell.formula})`;
15289
+ } else {
15290
+ value = this.getCellValue(cell);
15291
+ }
15292
+ while (cells.length < colNumber - 1) cells.push("");
15293
+ cells.push(value);
15294
+ });
15295
+ rows.push(cells);
15296
+ });
15297
+ return rows;
14898
15298
  }
14899
- const [path6, argument] = match[0].replace(/#! ?/, "").split(" ");
14900
- const binary = path6.split("/").pop();
14901
- if (binary === "env") {
14902
- return argument;
15299
+ /**
15300
+ * Get cell value as string
15301
+ */
15302
+ getCellValue(cell) {
15303
+ if (cell.value === null || cell.value === void 0) return "";
15304
+ if (typeof cell.value === "object") {
15305
+ if (cell.value.richText) {
15306
+ return cell.value.richText.map((rt) => rt.text || "").join("");
15307
+ }
15308
+ if (cell.value.hyperlink) {
15309
+ return cell.value.text || cell.value.hyperlink;
15310
+ }
15311
+ if ("result" in cell.value) {
15312
+ return String(cell.value.result ?? "");
15313
+ }
15314
+ if (cell.value instanceof Date) {
15315
+ return cell.value.toISOString().split("T")[0];
15316
+ }
15317
+ return String(cell.value);
15318
+ }
15319
+ return String(cell.value).replace(/\|/g, "\\|");
14903
15320
  }
14904
- return argument ? `${binary} ${argument}` : binary;
14905
15321
  };
14906
15322
  }
14907
15323
  });
14908
15324
 
14909
- // node_modules/cross-spawn/lib/util/readShebang.js
14910
- var require_readShebang = __commonJS({
14911
- "node_modules/cross-spawn/lib/util/readShebang.js"(exports$1, module) {
14912
- var fs17 = __require("fs");
14913
- var shebangCommand = require_shebang_command();
14914
- function readShebang(command) {
14915
- const size = 150;
14916
- const buffer = Buffer.alloc(size);
14917
- let fd;
14918
- try {
14919
- fd = fs17.openSync(command, "r");
14920
- fs17.readSync(fd, buffer, 0, size, 0);
14921
- fs17.closeSync(fd);
14922
- } catch (e) {
14923
- }
14924
- return shebangCommand(buffer.toString());
14925
- }
14926
- module.exports = readShebang;
14927
- }
14928
- });
14929
-
14930
- // node_modules/cross-spawn/lib/parse.js
14931
- var require_parse = __commonJS({
14932
- "node_modules/cross-spawn/lib/parse.js"(exports$1, module) {
14933
- var path6 = __require("path");
14934
- var resolveCommand = require_resolveCommand();
14935
- var escape2 = require_escape();
14936
- var readShebang = require_readShebang();
14937
- var isWin = process.platform === "win32";
14938
- var isExecutableRegExp = /\.(?:com|exe)$/i;
14939
- var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
14940
- function detectShebang(parsed) {
14941
- parsed.file = resolveCommand(parsed);
14942
- const shebang = parsed.file && readShebang(parsed.file);
14943
- if (shebang) {
14944
- parsed.args.unshift(parsed.file);
14945
- parsed.command = shebang;
14946
- return resolveCommand(parsed);
14947
- }
14948
- return parsed.file;
14949
- }
14950
- function parseNonShell(parsed) {
14951
- if (!isWin) {
14952
- return parsed;
14953
- }
14954
- const commandFile = detectShebang(parsed);
14955
- const needsShell = !isExecutableRegExp.test(commandFile);
14956
- if (parsed.options.forceShell || needsShell) {
14957
- const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
14958
- parsed.command = path6.normalize(parsed.command);
14959
- parsed.command = escape2.command(parsed.command);
14960
- parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
14961
- const shellCommand = [parsed.command].concat(parsed.args).join(" ");
14962
- parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`];
14963
- parsed.command = process.env.comspec || "cmd.exe";
14964
- parsed.options.windowsVerbatimArguments = true;
15325
+ // src/capabilities/documents/handlers/PDFHandler.ts
15326
+ async function getUnpdf() {
15327
+ if (!unpdfModule) {
15328
+ const mod = await import('unpdf');
15329
+ unpdfModule = {
15330
+ extractText: mod.extractText,
15331
+ extractImages: mod.extractImages,
15332
+ getMeta: mod.getMeta
15333
+ };
15334
+ }
15335
+ return unpdfModule;
15336
+ }
15337
+ var unpdfModule, PDFHandler;
15338
+ var init_PDFHandler = __esm({
15339
+ "src/capabilities/documents/handlers/PDFHandler.ts"() {
15340
+ init_constants();
15341
+ unpdfModule = null;
15342
+ PDFHandler = class {
15343
+ name = "PDFHandler";
15344
+ supportedFormats = ["pdf"];
15345
+ async handle(buffer, filename, format, options) {
15346
+ const unpdf = await getUnpdf();
15347
+ const pieces = [];
15348
+ let pieceIndex = 0;
15349
+ let metadata = {};
15350
+ const includeMetadata = options.formatOptions?.pdf?.includeMetadata !== false;
15351
+ if (includeMetadata) {
15352
+ try {
15353
+ metadata = await unpdf.getMeta(buffer);
15354
+ } catch {
15355
+ }
15356
+ }
15357
+ const textResult = await unpdf.extractText(buffer, { mergePages: false });
15358
+ const pages = textResult?.pages || textResult?.text ? Array.isArray(textResult.text) ? textResult.text : [textResult.text] : [];
15359
+ const requestedPages = options.pages;
15360
+ const pageEntries = pages.map((text, i) => ({ text, pageNum: i + 1 }));
15361
+ const filteredPages = requestedPages && requestedPages.length > 0 ? pageEntries.filter(
15362
+ (p) => requestedPages.some((rp) => {
15363
+ const num = typeof rp === "string" ? parseInt(rp, 10) : rp;
15364
+ return num === p.pageNum;
15365
+ })
15366
+ ) : pageEntries;
15367
+ if (includeMetadata && metadata?.info) {
15368
+ const metaParts = [];
15369
+ if (metadata.info.Title) metaParts.push(`**Title:** ${metadata.info.Title}`);
15370
+ if (metadata.info.Author) metaParts.push(`**Author:** ${metadata.info.Author}`);
15371
+ if (metadata.info.Subject) metaParts.push(`**Subject:** ${metadata.info.Subject}`);
15372
+ if (metadata.info.Creator) metaParts.push(`**Creator:** ${metadata.info.Creator}`);
15373
+ if (pages.length) metaParts.push(`**Pages:** ${pages.length}`);
15374
+ if (metaParts.length > 0) {
15375
+ const metaContent = metaParts.join("\n");
15376
+ const sizeBytes = Buffer.byteLength(metaContent, "utf-8");
15377
+ pieces.push({
15378
+ type: "text",
15379
+ content: metaContent,
15380
+ metadata: {
15381
+ sourceFilename: filename,
15382
+ format,
15383
+ index: pieceIndex++,
15384
+ section: "Metadata",
15385
+ sizeBytes,
15386
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15387
+ }
15388
+ });
15389
+ }
15390
+ }
15391
+ for (const page of filteredPages) {
15392
+ const text = page.text.trim();
15393
+ if (!text) continue;
15394
+ const sizeBytes = Buffer.byteLength(text, "utf-8");
15395
+ pieces.push({
15396
+ type: "text",
15397
+ content: text,
15398
+ metadata: {
15399
+ sourceFilename: filename,
15400
+ format,
15401
+ index: pieceIndex++,
15402
+ section: `Page ${page.pageNum}`,
15403
+ sizeBytes,
15404
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15405
+ }
15406
+ });
15407
+ }
15408
+ if (options.extractImages !== false) {
15409
+ try {
15410
+ const imagesResult = await unpdf.extractImages(buffer, {});
15411
+ const images = imagesResult?.images || [];
15412
+ for (const img of images) {
15413
+ if (!img.data) continue;
15414
+ const base64 = typeof img.data === "string" ? img.data : Buffer.from(img.data).toString("base64");
15415
+ const sizeBytes = Math.ceil(base64.length * 0.75);
15416
+ pieces.push({
15417
+ type: "image",
15418
+ base64,
15419
+ mimeType: img.mimeType || "image/png",
15420
+ width: img.width,
15421
+ height: img.height,
15422
+ metadata: {
15423
+ sourceFilename: filename,
15424
+ format,
15425
+ index: pieceIndex++,
15426
+ sizeBytes,
15427
+ estimatedTokens: DOCUMENT_DEFAULTS.IMAGE_TOKENS_AUTO,
15428
+ label: img.name || void 0
15429
+ }
15430
+ });
15431
+ }
15432
+ } catch {
15433
+ }
15434
+ }
15435
+ return pieces;
15436
+ }
15437
+ };
15438
+ }
15439
+ });
15440
+
15441
+ // src/capabilities/documents/handlers/index.ts
15442
+ var handlers_exports = {};
15443
+ __export(handlers_exports, {
15444
+ ExcelHandler: () => ExcelHandler,
15445
+ HTMLHandler: () => HTMLHandler,
15446
+ ImageHandler: () => ImageHandler,
15447
+ OfficeHandler: () => OfficeHandler,
15448
+ PDFHandler: () => PDFHandler,
15449
+ TextHandler: () => TextHandler,
15450
+ getDefaultHandlers: () => getDefaultHandlers
15451
+ });
15452
+ function getDefaultHandlers() {
15453
+ return /* @__PURE__ */ new Map([
15454
+ ["text", new TextHandler()],
15455
+ ["image", new ImageHandler()],
15456
+ ["html", new HTMLHandler()],
15457
+ ["office", new OfficeHandler()],
15458
+ ["spreadsheet", new ExcelHandler()],
15459
+ ["pdf", new PDFHandler()]
15460
+ ]);
15461
+ }
15462
+ var init_handlers = __esm({
15463
+ "src/capabilities/documents/handlers/index.ts"() {
15464
+ init_TextHandler();
15465
+ init_ImageHandler();
15466
+ init_HTMLHandler();
15467
+ init_OfficeHandler();
15468
+ init_ExcelHandler();
15469
+ init_PDFHandler();
15470
+ init_TextHandler();
15471
+ init_ImageHandler();
15472
+ init_HTMLHandler();
15473
+ init_OfficeHandler();
15474
+ init_ExcelHandler();
15475
+ init_PDFHandler();
15476
+ }
15477
+ });
15478
+
15479
+ // src/capabilities/documents/transformers/DefaultTransformers.ts
15480
+ function normalizeTable(table) {
15481
+ const lines = table.trim().split("\n");
15482
+ if (lines.length < 2) return table;
15483
+ const rows = lines.map(
15484
+ (line) => line.split("|").slice(1, -1).map((cell) => cell.trim())
15485
+ );
15486
+ const colCount = Math.max(...rows.map((r) => r.length));
15487
+ const colWidths = new Array(colCount).fill(3);
15488
+ for (const row of rows) {
15489
+ for (let i = 0; i < row.length; i++) {
15490
+ const cell = row[i] ?? "";
15491
+ if (cell.match(/^[-:]+$/)) continue;
15492
+ colWidths[i] = Math.max(colWidths[i] ?? 3, cell.length);
15493
+ }
15494
+ }
15495
+ const result = rows.map((row, rowIndex) => {
15496
+ const cells = [];
15497
+ for (let i = 0; i < colCount; i++) {
15498
+ const cell = row[i] || "";
15499
+ if (rowIndex === 1) {
15500
+ cells.push("-".repeat(colWidths[i]));
15501
+ } else {
15502
+ cells.push(cell.padEnd(colWidths[i]));
14965
15503
  }
14966
- return parsed;
14967
15504
  }
14968
- function parse(command, args, options) {
14969
- if (args && !Array.isArray(args)) {
14970
- options = args;
14971
- args = null;
15505
+ return `| ${cells.join(" | ")} |`;
15506
+ });
15507
+ return result.join("\n");
15508
+ }
15509
+ function getDefaultTransformers() {
15510
+ return [
15511
+ documentHeaderTransformer,
15512
+ tableFormattingTransformer,
15513
+ truncationTransformer
15514
+ ];
15515
+ }
15516
+ var documentHeaderTransformer, tableFormattingTransformer, truncationTransformer;
15517
+ var init_DefaultTransformers = __esm({
15518
+ "src/capabilities/documents/transformers/DefaultTransformers.ts"() {
15519
+ init_constants();
15520
+ documentHeaderTransformer = {
15521
+ name: "documentHeaderTransformer",
15522
+ appliesTo: [],
15523
+ // applies to all formats
15524
+ priority: 10,
15525
+ async transform(pieces, context) {
15526
+ if (pieces.length === 0) return pieces;
15527
+ const totalSize = pieces.reduce((sum, p) => sum + p.metadata.sizeBytes, 0);
15528
+ const sizeStr = totalSize > 1024 * 1024 ? `${(totalSize / 1024 / 1024).toFixed(1)}MB` : `${(totalSize / 1024).toFixed(1)}KB`;
15529
+ const header = `# Document: ${context.filename}
15530
+ _Format: ${context.format.toUpperCase()} | Size: ${sizeStr}_`;
15531
+ const headerBytes = Buffer.byteLength(header, "utf-8");
15532
+ const headerPiece = {
15533
+ type: "text",
15534
+ content: header,
15535
+ metadata: {
15536
+ sourceFilename: context.filename,
15537
+ format: context.format,
15538
+ index: -1,
15539
+ // will be re-indexed
15540
+ section: "Header",
15541
+ sizeBytes: headerBytes,
15542
+ estimatedTokens: Math.ceil(headerBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15543
+ }
15544
+ };
15545
+ const result = [headerPiece, ...pieces];
15546
+ result.forEach((p, i) => {
15547
+ p.metadata.index = i;
15548
+ });
15549
+ return result;
14972
15550
  }
14973
- args = args ? args.slice(0) : [];
14974
- options = Object.assign({}, options);
14975
- const parsed = {
14976
- command,
14977
- args,
14978
- options,
14979
- file: void 0,
14980
- original: {
14981
- command,
14982
- args
14983
- }
14984
- };
14985
- return options.shell ? parsed : parseNonShell(parsed);
14986
- }
14987
- module.exports = parse;
14988
- }
14989
- });
14990
-
14991
- // node_modules/cross-spawn/lib/enoent.js
14992
- var require_enoent = __commonJS({
14993
- "node_modules/cross-spawn/lib/enoent.js"(exports$1, module) {
14994
- var isWin = process.platform === "win32";
14995
- function notFoundError(original, syscall) {
14996
- return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
14997
- code: "ENOENT",
14998
- errno: "ENOENT",
14999
- syscall: `${syscall} ${original.command}`,
15000
- path: original.command,
15001
- spawnargs: original.args
15002
- });
15003
- }
15004
- function hookChildProcess(cp, parsed) {
15005
- if (!isWin) {
15006
- return;
15551
+ };
15552
+ tableFormattingTransformer = {
15553
+ name: "tableFormattingTransformer",
15554
+ appliesTo: ["xlsx", "csv"],
15555
+ priority: 50,
15556
+ async transform(pieces, _context) {
15557
+ return pieces.map((piece) => {
15558
+ if (piece.type !== "text") return piece;
15559
+ let content = piece.content;
15560
+ content = content.replace(
15561
+ /(\|[^\n]+\|\n\|[\s\-:|]+\|\n(?:\|[^\n]+\|\n?)*)/g,
15562
+ (table) => normalizeTable(table)
15563
+ );
15564
+ if (content === piece.content) return piece;
15565
+ const sizeBytes = Buffer.byteLength(content, "utf-8");
15566
+ return {
15567
+ ...piece,
15568
+ content,
15569
+ metadata: {
15570
+ ...piece.metadata,
15571
+ sizeBytes,
15572
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15573
+ }
15574
+ };
15575
+ });
15007
15576
  }
15008
- const originalEmit = cp.emit;
15009
- cp.emit = function(name, arg1) {
15010
- if (name === "exit") {
15011
- const err = verifyENOENT(arg1, parsed);
15012
- if (err) {
15013
- return originalEmit.call(cp, "error", err);
15577
+ };
15578
+ truncationTransformer = {
15579
+ name: "truncationTransformer",
15580
+ appliesTo: [],
15581
+ // applies to all formats
15582
+ priority: 1e3,
15583
+ // runs last
15584
+ async transform(pieces, context) {
15585
+ const maxTokens = context.options.maxTokens ?? DOCUMENT_DEFAULTS.MAX_OUTPUT_TOKENS;
15586
+ const maxBytes = context.options.maxOutputBytes ?? DOCUMENT_DEFAULTS.MAX_OUTPUT_BYTES;
15587
+ let totalTokens = 0;
15588
+ let totalBytes = 0;
15589
+ const result = [];
15590
+ for (const piece of pieces) {
15591
+ totalTokens += piece.metadata.estimatedTokens;
15592
+ totalBytes += piece.metadata.sizeBytes;
15593
+ if (totalTokens > maxTokens || totalBytes > maxBytes) {
15594
+ if (piece.type === "text") {
15595
+ const remainingTokens = maxTokens - (totalTokens - piece.metadata.estimatedTokens);
15596
+ const remainingChars = remainingTokens * DOCUMENT_DEFAULTS.CHARS_PER_TOKEN;
15597
+ if (remainingChars > 100) {
15598
+ const content = piece.content;
15599
+ const truncateAt = content.lastIndexOf("\n\n", remainingChars);
15600
+ const cutPoint = truncateAt > remainingChars * 0.3 ? truncateAt : remainingChars;
15601
+ const truncated = content.slice(0, cutPoint) + "\n\n..._[content truncated]_";
15602
+ const sizeBytes = Buffer.byteLength(truncated, "utf-8");
15603
+ result.push({
15604
+ ...piece,
15605
+ content: truncated,
15606
+ metadata: {
15607
+ ...piece.metadata,
15608
+ sizeBytes,
15609
+ estimatedTokens: Math.ceil(sizeBytes / DOCUMENT_DEFAULTS.CHARS_PER_TOKEN)
15610
+ }
15611
+ });
15612
+ }
15613
+ }
15614
+ break;
15014
15615
  }
15616
+ result.push(piece);
15015
15617
  }
15016
- return originalEmit.apply(cp, arguments);
15017
- };
15018
- }
15019
- function verifyENOENT(status, parsed) {
15020
- if (isWin && status === 1 && !parsed.file) {
15021
- return notFoundError(parsed.original, "spawn");
15022
- }
15023
- return null;
15024
- }
15025
- function verifyENOENTSync(status, parsed) {
15026
- if (isWin && status === 1 && !parsed.file) {
15027
- return notFoundError(parsed.original, "spawnSync");
15618
+ return result;
15028
15619
  }
15029
- return null;
15030
- }
15031
- module.exports = {
15032
- hookChildProcess,
15033
- verifyENOENT,
15034
- verifyENOENTSync,
15035
- notFoundError
15036
- };
15037
- }
15038
- });
15039
-
15040
- // node_modules/cross-spawn/index.js
15041
- var require_cross_spawn = __commonJS({
15042
- "node_modules/cross-spawn/index.js"(exports$1, module) {
15043
- var cp = __require("child_process");
15044
- var parse = require_parse();
15045
- var enoent = require_enoent();
15046
- function spawn3(command, args, options) {
15047
- const parsed = parse(command, args, options);
15048
- const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
15049
- enoent.hookChildProcess(spawned, parsed);
15050
- return spawned;
15051
- }
15052
- function spawnSync(command, args, options) {
15053
- const parsed = parse(command, args, options);
15054
- const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
15055
- result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
15056
- return result;
15057
- }
15058
- module.exports = spawn3;
15059
- module.exports.spawn = spawn3;
15060
- module.exports.sync = spawnSync;
15061
- module.exports._parse = parse;
15062
- module.exports._enoent = enoent;
15620
+ };
15621
+ }
15622
+ });
15623
+
15624
+ // src/capabilities/documents/transformers/index.ts
15625
+ var transformers_exports = {};
15626
+ __export(transformers_exports, {
15627
+ documentHeaderTransformer: () => documentHeaderTransformer,
15628
+ getDefaultTransformers: () => getDefaultTransformers,
15629
+ tableFormattingTransformer: () => tableFormattingTransformer,
15630
+ truncationTransformer: () => truncationTransformer
15631
+ });
15632
+ var init_transformers = __esm({
15633
+ "src/capabilities/documents/transformers/index.ts"() {
15634
+ init_DefaultTransformers();
15063
15635
  }
15064
15636
  });
15065
15637
 
@@ -15774,6 +16346,32 @@ var ParallelTasksError = class _ParallelTasksError extends AIError {
15774
16346
  return this.failures.map((f) => f.taskId);
15775
16347
  }
15776
16348
  };
16349
+ var DocumentReadError = class _DocumentReadError extends AIError {
16350
+ constructor(source, message, originalError) {
16351
+ super(
16352
+ `Failed to read document '${source}': ${message}`,
16353
+ "DOCUMENT_READ_ERROR",
16354
+ 500,
16355
+ originalError
16356
+ );
16357
+ this.source = source;
16358
+ this.name = "DocumentReadError";
16359
+ Object.setPrototypeOf(this, _DocumentReadError.prototype);
16360
+ }
16361
+ };
16362
+ var UnsupportedFormatError = class _UnsupportedFormatError extends AIError {
16363
+ constructor(format, family) {
16364
+ super(
16365
+ `Unsupported document format: '${format}'${family ? ` (family: ${family})` : ""}`,
16366
+ "UNSUPPORTED_FORMAT",
16367
+ 400
16368
+ );
16369
+ this.format = format;
16370
+ this.family = family;
16371
+ this.name = "UnsupportedFormatError";
16372
+ Object.setPrototypeOf(this, _UnsupportedFormatError.prototype);
16373
+ }
16374
+ };
15777
16375
  var ContextOverflowError = class _ContextOverflowError extends AIError {
15778
16376
  constructor(message, budget) {
15779
16377
  super(
@@ -18177,24 +18775,9 @@ var ContentType = /* @__PURE__ */ ((ContentType2) => {
18177
18775
  ContentType2["TOOL_RESULT"] = "tool_result";
18178
18776
  return ContentType2;
18179
18777
  })(ContentType || {});
18180
- var AGENT_DEFAULTS = {
18181
- /** Default maximum iterations for agentic loop */
18182
- MAX_ITERATIONS: 50,
18183
- /** Default temperature for LLM calls */
18184
- DEFAULT_TEMPERATURE: 0.7,
18185
- /** Message injected when max iterations is reached */
18186
- MAX_ITERATIONS_MESSAGE: `You have reached the maximum iteration limit for this execution. Please:
18187
- 1. Summarize what you have accomplished so far
18188
- 2. Explain what remains to be done (if anything)
18189
- 3. Ask the user if they would like you to continue
18190
-
18191
- Do NOT use any tools in this response - just provide a clear summary and ask for confirmation to proceed.`
18192
- };
18193
- var TOKEN_ESTIMATION = {
18194
- /** Characters per token for mixed content */
18195
- MIXED_CHARS_PER_TOKEN: 3.5};
18196
18778
 
18197
18779
  // src/core/context-nextgen/BasePluginNextGen.ts
18780
+ init_constants();
18198
18781
  var simpleTokenEstimator = {
18199
18782
  estimateTokens(text) {
18200
18783
  if (!text || text.length === 0) return 0;
@@ -18203,6 +18786,14 @@ var simpleTokenEstimator = {
18203
18786
  estimateDataTokens(data) {
18204
18787
  const text = typeof data === "string" ? data : JSON.stringify(data);
18205
18788
  return this.estimateTokens(text);
18789
+ },
18790
+ estimateImageTokens(width, height, detail) {
18791
+ if (detail === "low") return 85;
18792
+ if (width && height) {
18793
+ const tiles = Math.ceil(width / 512) * Math.ceil(height / 512);
18794
+ return 85 + 170 * tiles;
18795
+ }
18796
+ return 1e3;
18206
18797
  }
18207
18798
  };
18208
18799
  var BasePluginNextGen = class {
@@ -21450,12 +22041,26 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
21450
22041
  return "";
21451
22042
  }
21452
22043
  const id = this.generateId();
21453
- const contentArray = results.map((r) => ({
21454
- type: "tool_result" /* TOOL_RESULT */,
21455
- tool_use_id: r.tool_use_id,
21456
- content: typeof r.content === "string" ? r.content : JSON.stringify(r.content),
21457
- error: r.error
21458
- }));
22044
+ const contentArray = results.map((r) => {
22045
+ let contentStr;
22046
+ let images;
22047
+ if (typeof r.content === "string") {
22048
+ contentStr = r.content;
22049
+ } else if (r.content && Array.isArray(r.content.__images) && r.content.__images.length > 0) {
22050
+ images = r.content.__images;
22051
+ const { __images: _, base64: __, ...rest } = r.content;
22052
+ contentStr = JSON.stringify(rest);
22053
+ } else {
22054
+ contentStr = JSON.stringify(r.content);
22055
+ }
22056
+ return {
22057
+ type: "tool_result" /* TOOL_RESULT */,
22058
+ tool_use_id: r.tool_use_id,
22059
+ content: contentStr,
22060
+ error: r.error,
22061
+ ...images ? { __images: images } : {}
22062
+ };
22063
+ });
21459
22064
  const message = {
21460
22065
  type: "message",
21461
22066
  id,
@@ -21716,12 +22321,29 @@ ${content}`);
21716
22321
  total += this._estimator.estimateDataTokens(c.input || {});
21717
22322
  } else if (c.type === "tool_result" /* TOOL_RESULT */) {
21718
22323
  total += this._estimator.estimateTokens(c.content || "");
22324
+ const images = c.__images;
22325
+ if (images?.length) {
22326
+ for (const _img of images) {
22327
+ total += this._estimateImageTokens();
22328
+ }
22329
+ }
21719
22330
  } else if (c.type === "input_image_url" /* INPUT_IMAGE_URL */) {
21720
- total += 200;
22331
+ const imgContent = c;
22332
+ const detail = imgContent.image_url?.detail;
22333
+ total += this._estimateImageTokens(void 0, void 0, detail);
21721
22334
  }
21722
22335
  }
21723
22336
  return total;
21724
22337
  }
22338
+ /**
22339
+ * Estimate tokens for a single image, using the estimator's image method if available.
22340
+ */
22341
+ _estimateImageTokens(width, height, detail) {
22342
+ if (this._estimator.estimateImageTokens) {
22343
+ return this._estimator.estimateImageTokens(width, height, detail);
22344
+ }
22345
+ return 1e3;
22346
+ }
21725
22347
  // ============================================================================
21726
22348
  // Compaction
21727
22349
  // ============================================================================
@@ -21954,7 +22576,8 @@ ${content}`);
21954
22576
  if (c.type === "tool_result" /* TOOL_RESULT */) {
21955
22577
  const toolResult = c;
21956
22578
  const content = toolResult.content || "";
21957
- if (this.isBinaryContent(content)) {
22579
+ const images = toolResult.__images;
22580
+ if (!images?.length && this.isBinaryContent(content)) {
21958
22581
  truncatedContent.push({
21959
22582
  type: "tool_result" /* TOOL_RESULT */,
21960
22583
  tool_use_id: toolResult.tool_use_id,
@@ -21971,7 +22594,9 @@ ${content}`);
21971
22594
  tool_use_id: toolResult.tool_use_id,
21972
22595
  content: `${truncated}
21973
22596
 
21974
- [TRUNCATED: Original output was ${Math.round(content.length / 1024)}KB. Only first ${Math.round(availableChars / 1024)}KB shown. Consider using more targeted queries.]`
22597
+ [TRUNCATED: Original output was ${Math.round(content.length / 1024)}KB. Only first ${Math.round(availableChars / 1024)}KB shown. Consider using more targeted queries.]`,
22598
+ // Preserve images even when text is truncated — they're handled natively by providers
22599
+ ...images ? { __images: images } : {}
21975
22600
  });
21976
22601
  totalCharsUsed += truncated.length + 150;
21977
22602
  } else if (availableChars > 0) {
@@ -21982,7 +22607,9 @@ ${content}`);
21982
22607
  type: "tool_result" /* TOOL_RESULT */,
21983
22608
  tool_use_id: toolResult.tool_use_id,
21984
22609
  content: "[Output too large - skipped due to context limits. Try a more targeted query.]",
21985
- error: "Output too large"
22610
+ error: "Output too large",
22611
+ // Preserve images even when text is dropped
22612
+ ...images ? { __images: images } : {}
21986
22613
  });
21987
22614
  totalCharsUsed += 100;
21988
22615
  }
@@ -22511,14 +23138,41 @@ var OpenAIResponsesConverter = class {
22511
23138
  arguments: content.arguments
22512
23139
  });
22513
23140
  break;
22514
- case "tool_result":
22515
- const output = typeof content.content === "string" ? content.content : JSON.stringify(content.content);
23141
+ case "tool_result": {
23142
+ const contentImages = content.__images;
23143
+ let outputText;
23144
+ let images;
23145
+ if (contentImages?.length) {
23146
+ outputText = typeof content.content === "string" ? content.content : JSON.stringify(content.content);
23147
+ images = contentImages;
23148
+ } else {
23149
+ const rawOutput = typeof content.content === "string" ? content.content : JSON.stringify(content.content);
23150
+ const extracted = this.extractImagesFromOutput(rawOutput);
23151
+ outputText = extracted.text;
23152
+ images = extracted.images;
23153
+ }
22516
23154
  items.push({
22517
23155
  type: "function_call_output",
22518
23156
  call_id: content.tool_use_id,
22519
- output
23157
+ output: outputText
22520
23158
  });
23159
+ if (images.length > 0) {
23160
+ const imageContent = images.map((img) => ({
23161
+ type: "input_image",
23162
+ image_url: `data:${img.mediaType};base64,${img.base64}`
23163
+ }));
23164
+ items.push({
23165
+ type: "message",
23166
+ role: "user",
23167
+ content: [
23168
+ { type: "input_text", text: "[Screenshot from tool result]" },
23169
+ ...imageContent
23170
+ ],
23171
+ status: "completed"
23172
+ });
23173
+ }
22521
23174
  break;
23175
+ }
22522
23176
  }
22523
23177
  }
22524
23178
  if (messageContent.length > 0) {
@@ -22686,6 +23340,22 @@ var OpenAIResponsesConverter = class {
22686
23340
  }
22687
23341
  };
22688
23342
  }
23343
+ /**
23344
+ * Extract __images from a JSON tool result and return cleaned text + images.
23345
+ * Used by the __images convention for multimodal tool results.
23346
+ */
23347
+ extractImagesFromOutput(output) {
23348
+ try {
23349
+ const parsed = JSON.parse(output);
23350
+ if (parsed && Array.isArray(parsed.__images) && parsed.__images.length > 0) {
23351
+ const images = parsed.__images;
23352
+ const { __images: _, base64: __, ...rest } = parsed;
23353
+ return { text: JSON.stringify(rest), images };
23354
+ }
23355
+ } catch {
23356
+ }
23357
+ return { text: output, images: [] };
23358
+ }
22689
23359
  };
22690
23360
 
22691
23361
  // src/domain/entities/StreamEvent.ts
@@ -23491,6 +24161,7 @@ var AnthropicConverter = class extends BaseConverter {
23491
24161
  /**
23492
24162
  * Convert tool result to Anthropic block
23493
24163
  * Anthropic requires non-empty content when is_error is true
24164
+ * Supports __images convention: tool results with __images get multimodal content
23494
24165
  */
23495
24166
  convertToolResultToAnthropicBlock(resultContent) {
23496
24167
  const isError = !!resultContent.error;
@@ -23503,6 +24174,30 @@ var AnthropicConverter = class extends BaseConverter {
23503
24174
  if (isError && !toolResultContent) {
23504
24175
  toolResultContent = resultContent.error || "Tool execution failed";
23505
24176
  }
24177
+ const images = resultContent.__images?.length ? resultContent.__images : this.extractImages(toolResultContent);
24178
+ if (images) {
24179
+ const textContent = resultContent.__images?.length ? toolResultContent : this.stripImagesFromContent(toolResultContent);
24180
+ const contentBlocks = [];
24181
+ if (textContent.trim()) {
24182
+ contentBlocks.push({ type: "text", text: textContent });
24183
+ }
24184
+ for (const img of images) {
24185
+ contentBlocks.push({
24186
+ type: "image",
24187
+ source: {
24188
+ type: "base64",
24189
+ media_type: img.mediaType || "image/png",
24190
+ data: img.base64
24191
+ }
24192
+ });
24193
+ }
24194
+ return {
24195
+ type: "tool_result",
24196
+ tool_use_id: resultContent.tool_use_id,
24197
+ content: contentBlocks.length > 0 ? contentBlocks : textContent,
24198
+ is_error: isError
24199
+ };
24200
+ }
23506
24201
  return {
23507
24202
  type: "tool_result",
23508
24203
  tool_use_id: resultContent.tool_use_id,
@@ -23510,6 +24205,32 @@ var AnthropicConverter = class extends BaseConverter {
23510
24205
  is_error: isError
23511
24206
  };
23512
24207
  }
24208
+ /**
24209
+ * Extract __images from a JSON-stringified tool result content.
24210
+ * Returns null if no images found.
24211
+ */
24212
+ extractImages(content) {
24213
+ try {
24214
+ const parsed = JSON.parse(content);
24215
+ if (parsed && Array.isArray(parsed.__images) && parsed.__images.length > 0) {
24216
+ return parsed.__images;
24217
+ }
24218
+ } catch {
24219
+ }
24220
+ return null;
24221
+ }
24222
+ /**
24223
+ * Strip __images and base64 fields from JSON content to reduce token usage in text.
24224
+ */
24225
+ stripImagesFromContent(content) {
24226
+ try {
24227
+ const parsed = JSON.parse(content);
24228
+ const { __images: _, base64: __, ...rest } = parsed;
24229
+ return JSON.stringify(rest);
24230
+ } catch {
24231
+ return content;
24232
+ }
24233
+ }
23513
24234
  /**
23514
24235
  * Convert our Tool[] -> Anthropic tools
23515
24236
  * Uses shared conversion utilities (DRY)
@@ -24237,18 +24958,38 @@ var GoogleConverter = class {
24237
24958
  }
24238
24959
  parts.push(functionCallPart);
24239
24960
  break;
24240
- case "tool_result" /* TOOL_RESULT */:
24961
+ case "tool_result" /* TOOL_RESULT */: {
24241
24962
  const functionName = this.toolCallMapping.get(c.tool_use_id) || this.extractToolName(c.tool_use_id);
24963
+ const contentImages = c.__images;
24964
+ let resultText;
24965
+ let resultImages;
24966
+ if (contentImages?.length) {
24967
+ resultText = typeof c.content === "string" ? c.content : JSON.stringify(c.content);
24968
+ resultImages = contentImages;
24969
+ } else {
24970
+ const resultStr = typeof c.content === "string" ? c.content : JSON.stringify(c.content);
24971
+ const extracted = this.extractImagesFromResult(resultStr);
24972
+ resultText = extracted.text;
24973
+ resultImages = extracted.images;
24974
+ }
24242
24975
  parts.push({
24243
24976
  functionResponse: {
24244
24977
  name: functionName,
24245
- // Use actual function name from mapping
24246
24978
  response: {
24247
- result: typeof c.content === "string" ? c.content : c.content
24979
+ result: resultText
24248
24980
  }
24249
24981
  }
24250
24982
  });
24983
+ for (const img of resultImages) {
24984
+ parts.push({
24985
+ inlineData: {
24986
+ mimeType: img.mediaType || "image/png",
24987
+ data: img.base64
24988
+ }
24989
+ });
24990
+ }
24251
24991
  break;
24992
+ }
24252
24993
  }
24253
24994
  }
24254
24995
  return parts;
@@ -24384,6 +25125,22 @@ var GoogleConverter = class {
24384
25125
  reset() {
24385
25126
  this.clearMappings();
24386
25127
  }
25128
+ /**
25129
+ * Extract __images from a JSON tool result and return cleaned text + images.
25130
+ * Used by the __images convention for multimodal tool results.
25131
+ */
25132
+ extractImagesFromResult(content) {
25133
+ try {
25134
+ const parsed = JSON.parse(content);
25135
+ if (parsed && Array.isArray(parsed.__images) && parsed.__images.length > 0) {
25136
+ const images = parsed.__images;
25137
+ const { __images: _, base64: __, ...rest } = parsed;
25138
+ return { text: JSON.stringify(rest), images };
25139
+ }
25140
+ } catch {
25141
+ }
25142
+ return { text: content, images: [] };
25143
+ }
24387
25144
  };
24388
25145
  var GoogleStreamConverter = class {
24389
25146
  responseId = "";
@@ -26346,6 +27103,7 @@ function assertNotDestroyed(obj, operation) {
26346
27103
 
26347
27104
  // src/core/Agent.ts
26348
27105
  init_Metrics();
27106
+ init_constants();
26349
27107
  var Agent = class _Agent extends BaseAgent {
26350
27108
  // ===== Agent-specific State =====
26351
27109
  hookManager;
@@ -27553,6 +28311,9 @@ var Agent = class _Agent extends BaseAgent {
27553
28311
  this._logger.debug("Agent destroyed");
27554
28312
  }
27555
28313
  };
28314
+
28315
+ // src/core/index.ts
28316
+ init_constants();
27556
28317
  (class {
27557
28318
  static DEFAULT_PATHS = [
27558
28319
  "./oneringai.config.json",
@@ -30962,9 +31723,6 @@ var Client = class extends Protocol {
30962
31723
  }
30963
31724
  };
30964
31725
 
30965
- // node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js
30966
- var import_cross_spawn = __toESM(require_cross_spawn());
30967
-
30968
31726
  // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
30969
31727
  var ReadBuffer = class {
30970
31728
  append(chunk) {
@@ -31042,7 +31800,7 @@ var StdioClientTransport = class {
31042
31800
  throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
31043
31801
  }
31044
31802
  return new Promise((resolve4, reject) => {
31045
- this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
31803
+ this._process = spawn__default.default(this._serverParams.command, this._serverParams.args ?? [], {
31046
31804
  // merge default env with server env because mcp server needs some env vars
31047
31805
  env: {
31048
31806
  ...getDefaultEnvironment(),
@@ -38591,6 +39349,494 @@ var ZenRowsProvider = class {
38591
39349
  };
38592
39350
  registerScrapeProvider("zenrows", ZenRowsProvider);
38593
39351
 
39352
+ // src/capabilities/documents/DocumentReader.ts
39353
+ init_constants();
39354
+
39355
+ // src/capabilities/documents/FormatDetector.ts
39356
+ var EXTENSION_MAP = {
39357
+ // Office
39358
+ ".docx": { format: "docx", family: "office", mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
39359
+ ".pptx": { format: "pptx", family: "office", mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
39360
+ ".odt": { format: "odt", family: "office", mimeType: "application/vnd.oasis.opendocument.text" },
39361
+ ".odp": { format: "odp", family: "office", mimeType: "application/vnd.oasis.opendocument.presentation" },
39362
+ ".ods": { format: "ods", family: "office", mimeType: "application/vnd.oasis.opendocument.spreadsheet" },
39363
+ ".rtf": { format: "rtf", family: "office", mimeType: "application/rtf" },
39364
+ // Spreadsheet
39365
+ ".xlsx": { format: "xlsx", family: "spreadsheet", mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
39366
+ ".csv": { format: "csv", family: "spreadsheet", mimeType: "text/csv" },
39367
+ // PDF
39368
+ ".pdf": { format: "pdf", family: "pdf", mimeType: "application/pdf" },
39369
+ // HTML
39370
+ ".html": { format: "html", family: "html", mimeType: "text/html" },
39371
+ ".htm": { format: "html", family: "html", mimeType: "text/html" },
39372
+ // Text
39373
+ ".txt": { format: "txt", family: "text", mimeType: "text/plain" },
39374
+ ".md": { format: "md", family: "text", mimeType: "text/markdown" },
39375
+ ".json": { format: "json", family: "text", mimeType: "application/json" },
39376
+ ".xml": { format: "xml", family: "text", mimeType: "application/xml" },
39377
+ ".yaml": { format: "yaml", family: "text", mimeType: "application/yaml" },
39378
+ ".yml": { format: "yml", family: "text", mimeType: "application/yaml" },
39379
+ // Image
39380
+ ".png": { format: "png", family: "image", mimeType: "image/png" },
39381
+ ".jpg": { format: "jpg", family: "image", mimeType: "image/jpeg" },
39382
+ ".jpeg": { format: "jpeg", family: "image", mimeType: "image/jpeg" },
39383
+ ".gif": { format: "gif", family: "image", mimeType: "image/gif" },
39384
+ ".webp": { format: "webp", family: "image", mimeType: "image/webp" },
39385
+ ".svg": { format: "svg", family: "image", mimeType: "image/svg+xml" }
39386
+ };
39387
+ var MIME_MAP = {
39388
+ "application/pdf": { format: "pdf", family: "pdf" },
39389
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { format: "docx", family: "office" },
39390
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": { format: "pptx", family: "office" },
39391
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { format: "xlsx", family: "spreadsheet" },
39392
+ "application/vnd.oasis.opendocument.text": { format: "odt", family: "office" },
39393
+ "application/vnd.oasis.opendocument.presentation": { format: "odp", family: "office" },
39394
+ "application/vnd.oasis.opendocument.spreadsheet": { format: "ods", family: "office" },
39395
+ "application/rtf": { format: "rtf", family: "office" },
39396
+ "text/rtf": { format: "rtf", family: "office" },
39397
+ "text/csv": { format: "csv", family: "spreadsheet" },
39398
+ "application/csv": { format: "csv", family: "spreadsheet" }
39399
+ };
39400
+ var BINARY_DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
39401
+ ".docx",
39402
+ ".pptx",
39403
+ ".xlsx",
39404
+ ".odt",
39405
+ ".odp",
39406
+ ".ods",
39407
+ ".pdf",
39408
+ ".png",
39409
+ ".jpg",
39410
+ ".jpeg",
39411
+ ".gif",
39412
+ ".webp"
39413
+ ]);
39414
+ var FormatDetector = class _FormatDetector {
39415
+ /**
39416
+ * Detect format from filename and optional buffer
39417
+ */
39418
+ static detect(filename, _buffer) {
39419
+ const ext = _FormatDetector.getExtension(filename);
39420
+ const entry = EXTENSION_MAP[ext];
39421
+ if (!entry) {
39422
+ return {
39423
+ format: "txt",
39424
+ family: "text",
39425
+ mimeType: "text/plain",
39426
+ confidence: "low"
39427
+ };
39428
+ }
39429
+ return {
39430
+ format: entry.format,
39431
+ family: entry.family,
39432
+ mimeType: entry.mimeType,
39433
+ confidence: "high"
39434
+ };
39435
+ }
39436
+ /**
39437
+ * Check if an extension is a supported document format
39438
+ * Used by readFile to detect when to use DocumentReader
39439
+ */
39440
+ static isDocumentFormat(ext) {
39441
+ const normalizedExt = ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
39442
+ return normalizedExt in EXTENSION_MAP;
39443
+ }
39444
+ /**
39445
+ * Check if an extension is a binary document format
39446
+ * (i.e., cannot be read as UTF-8)
39447
+ */
39448
+ static isBinaryDocumentFormat(ext) {
39449
+ const normalizedExt = ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
39450
+ return BINARY_DOCUMENT_EXTENSIONS.has(normalizedExt);
39451
+ }
39452
+ /**
39453
+ * Check if a Content-Type header indicates a document format
39454
+ * Used by webFetch to detect downloadable documents
39455
+ */
39456
+ static isDocumentMimeType(contentType) {
39457
+ const mime = (contentType.split(";")[0] ?? "").trim().toLowerCase();
39458
+ return mime in MIME_MAP;
39459
+ }
39460
+ /**
39461
+ * Detect format from Content-Type header
39462
+ */
39463
+ static detectFromMimeType(contentType) {
39464
+ const mime = (contentType.split(";")[0] ?? "").trim().toLowerCase();
39465
+ const entry = MIME_MAP[mime];
39466
+ if (!entry) return null;
39467
+ const extEntry = Object.values(EXTENSION_MAP).find(
39468
+ (e) => e.format === entry.format
39469
+ );
39470
+ return {
39471
+ format: entry.format,
39472
+ family: entry.family,
39473
+ mimeType: extEntry?.mimeType || mime,
39474
+ confidence: "high"
39475
+ };
39476
+ }
39477
+ /**
39478
+ * Get all supported document extensions
39479
+ */
39480
+ static getSupportedExtensions() {
39481
+ return Object.keys(EXTENSION_MAP);
39482
+ }
39483
+ /**
39484
+ * Get the normalized extension from a filename
39485
+ */
39486
+ static getExtension(filename) {
39487
+ const lastDot = filename.lastIndexOf(".");
39488
+ if (lastDot === -1 || lastDot === filename.length - 1) return "";
39489
+ return filename.slice(lastDot).toLowerCase();
39490
+ }
39491
+ };
39492
+
39493
+ // src/capabilities/documents/DocumentReader.ts
39494
+ var DocumentReader = class _DocumentReader {
39495
+ handlers;
39496
+ config;
39497
+ constructor(config = {}) {
39498
+ this.config = config;
39499
+ this.handlers = config.handlers ? new Map(config.handlers) : /* @__PURE__ */ new Map();
39500
+ }
39501
+ /**
39502
+ * Create a new DocumentReader instance
39503
+ */
39504
+ static create(config = {}) {
39505
+ const reader = new _DocumentReader(config);
39506
+ reader.registerDefaultHandlers();
39507
+ return reader;
39508
+ }
39509
+ /**
39510
+ * Register all default format handlers (lazy-loaded)
39511
+ */
39512
+ registerDefaultHandlers() {
39513
+ }
39514
+ /**
39515
+ * Register a custom format handler
39516
+ */
39517
+ registerHandler(family, handler) {
39518
+ this.handlers.set(family, handler);
39519
+ }
39520
+ /**
39521
+ * Read a document from any source
39522
+ */
39523
+ async read(source, options = {}) {
39524
+ const startTime = Date.now();
39525
+ const warnings = [];
39526
+ const mergedOptions = {
39527
+ ...this.config.defaults,
39528
+ ...options,
39529
+ formatOptions: {
39530
+ ...this.config.defaults?.formatOptions,
39531
+ ...options.formatOptions
39532
+ },
39533
+ imageFilter: {
39534
+ ...this.config.defaults?.imageFilter,
39535
+ ...options.imageFilter
39536
+ }
39537
+ };
39538
+ try {
39539
+ const { buffer, filename } = await this.resolveSource(
39540
+ typeof source === "string" ? this.parseStringSource(source) : source
39541
+ );
39542
+ const detection = FormatDetector.detect(filename, buffer);
39543
+ const handler = await this.getHandler(detection.family);
39544
+ if (!handler) {
39545
+ throw new UnsupportedFormatError(detection.format, detection.family);
39546
+ }
39547
+ let pieces = await handler.handle(buffer, filename, detection.format, mergedOptions);
39548
+ if (mergedOptions.extractImages !== false) {
39549
+ pieces = this.filterImages(pieces, mergedOptions.imageFilter);
39550
+ } else {
39551
+ pieces = pieces.filter((p) => p.type !== "image");
39552
+ }
39553
+ const transformerContext = {
39554
+ filename,
39555
+ format: detection.format,
39556
+ family: detection.family,
39557
+ options: mergedOptions
39558
+ };
39559
+ pieces = await this.runTransformers(pieces, transformerContext, mergedOptions);
39560
+ const metadata = this.assembleMetadata(pieces, filename, detection, startTime);
39561
+ return {
39562
+ success: true,
39563
+ pieces,
39564
+ metadata,
39565
+ warnings
39566
+ };
39567
+ } catch (error) {
39568
+ if (error instanceof DocumentReadError || error instanceof UnsupportedFormatError) {
39569
+ throw error;
39570
+ }
39571
+ throw new DocumentReadError(
39572
+ typeof source === "string" ? source : "path" in source ? source.path : "filename" in source ? source.filename : "unknown",
39573
+ error instanceof Error ? error.message : String(error),
39574
+ error instanceof Error ? error : void 0
39575
+ );
39576
+ }
39577
+ }
39578
+ /**
39579
+ * Parse a string source (auto-detect path vs URL)
39580
+ */
39581
+ parseStringSource(source) {
39582
+ if (source.startsWith("http://") || source.startsWith("https://")) {
39583
+ return { type: "url", url: source };
39584
+ }
39585
+ return { type: "file", path: source };
39586
+ }
39587
+ /**
39588
+ * Resolve any source to a buffer and filename
39589
+ */
39590
+ async resolveSource(source) {
39591
+ switch (source.type) {
39592
+ case "file": {
39593
+ const buffer = await fs15.readFile(source.path);
39594
+ const filename = source.path.split("/").pop() || source.path;
39595
+ return { buffer, filename };
39596
+ }
39597
+ case "url": {
39598
+ const maxSize = this.config.maxDownloadSizeBytes ?? DOCUMENT_DEFAULTS.MAX_DOWNLOAD_SIZE_BYTES;
39599
+ const timeout = this.config.downloadTimeoutMs ?? DOCUMENT_DEFAULTS.DOWNLOAD_TIMEOUT_MS;
39600
+ const controller = new AbortController();
39601
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
39602
+ try {
39603
+ const response = await fetch(source.url, {
39604
+ headers: {
39605
+ ...source.headers,
39606
+ "User-Agent": "OneRingAI-DocumentReader/1.0"
39607
+ },
39608
+ signal: controller.signal
39609
+ });
39610
+ clearTimeout(timeoutId);
39611
+ if (!response.ok) {
39612
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
39613
+ }
39614
+ const contentLength = response.headers.get("content-length");
39615
+ if (contentLength && parseInt(contentLength, 10) > maxSize) {
39616
+ throw new Error(`File too large: ${contentLength} bytes (max: ${maxSize})`);
39617
+ }
39618
+ const arrayBuffer = await response.arrayBuffer();
39619
+ if (arrayBuffer.byteLength > maxSize) {
39620
+ throw new Error(`Downloaded file too large: ${arrayBuffer.byteLength} bytes (max: ${maxSize})`);
39621
+ }
39622
+ const filename = this.extractFilenameFromURL(source.url, response);
39623
+ return { buffer: Buffer.from(arrayBuffer), filename };
39624
+ } catch (error) {
39625
+ clearTimeout(timeoutId);
39626
+ if (error.name === "AbortError") {
39627
+ throw new Error(`Download timed out after ${timeout}ms`);
39628
+ }
39629
+ throw error;
39630
+ }
39631
+ }
39632
+ case "buffer": {
39633
+ const buffer = Buffer.isBuffer(source.buffer) ? source.buffer : Buffer.from(source.buffer);
39634
+ return { buffer, filename: source.filename };
39635
+ }
39636
+ case "blob": {
39637
+ const arrayBuffer = await source.blob.arrayBuffer();
39638
+ return { buffer: Buffer.from(arrayBuffer), filename: source.filename };
39639
+ }
39640
+ }
39641
+ }
39642
+ /**
39643
+ * Extract filename from URL and response headers
39644
+ */
39645
+ extractFilenameFromURL(url2, response) {
39646
+ const disposition = response.headers.get("content-disposition");
39647
+ if (disposition) {
39648
+ const match = disposition.match(/filename[^;=\n]*=(['"]?)([^'"\n;]*)\1/);
39649
+ if (match?.[2]) return match[2];
39650
+ }
39651
+ try {
39652
+ const pathname = new URL(url2).pathname;
39653
+ const basename = pathname.split("/").pop();
39654
+ if (basename && basename.includes(".")) return basename;
39655
+ } catch {
39656
+ }
39657
+ return "document";
39658
+ }
39659
+ /**
39660
+ * Get the handler for a format family, loading defaults lazily
39661
+ */
39662
+ async getHandler(family) {
39663
+ if (this.handlers.has(family)) {
39664
+ return this.handlers.get(family);
39665
+ }
39666
+ try {
39667
+ const { getDefaultHandlers: getDefaultHandlers2 } = await Promise.resolve().then(() => (init_handlers(), handlers_exports));
39668
+ const defaults = getDefaultHandlers2();
39669
+ const handler = defaults.get(family);
39670
+ if (handler) {
39671
+ this.handlers.set(family, handler);
39672
+ return handler;
39673
+ }
39674
+ } catch {
39675
+ }
39676
+ return null;
39677
+ }
39678
+ /**
39679
+ * Filter images based on options
39680
+ */
39681
+ filterImages(pieces, filterOptions) {
39682
+ const minWidth = filterOptions?.minWidth ?? DOCUMENT_DEFAULTS.IMAGE_FILTER.MIN_WIDTH;
39683
+ const minHeight = filterOptions?.minHeight ?? DOCUMENT_DEFAULTS.IMAGE_FILTER.MIN_HEIGHT;
39684
+ const minSizeBytes = filterOptions?.minSizeBytes ?? DOCUMENT_DEFAULTS.IMAGE_FILTER.MIN_SIZE_BYTES;
39685
+ const maxImages = filterOptions?.maxImages ?? DOCUMENT_DEFAULTS.MAX_EXTRACTED_IMAGES;
39686
+ const excludePatterns = filterOptions?.excludePatterns ?? [];
39687
+ let imageCount = 0;
39688
+ return pieces.filter((piece) => {
39689
+ if (piece.type !== "image") return true;
39690
+ const img = piece;
39691
+ if (img.width !== void 0 && img.width < minWidth) return false;
39692
+ if (img.height !== void 0 && img.height < minHeight) return false;
39693
+ if (img.metadata.sizeBytes < minSizeBytes) return false;
39694
+ const label = img.metadata.label || "";
39695
+ if (excludePatterns.some((p) => p.test(label))) return false;
39696
+ imageCount++;
39697
+ if (imageCount > maxImages) return false;
39698
+ return true;
39699
+ });
39700
+ }
39701
+ /**
39702
+ * Run the transformer pipeline
39703
+ */
39704
+ async runTransformers(pieces, context, options) {
39705
+ const transformers = [];
39706
+ if (!options.skipDefaultTransformers) {
39707
+ try {
39708
+ const { getDefaultTransformers: getDefaultTransformers2 } = await Promise.resolve().then(() => (init_transformers(), transformers_exports));
39709
+ transformers.push(...getDefaultTransformers2());
39710
+ } catch {
39711
+ }
39712
+ }
39713
+ if (options.transformers) {
39714
+ transformers.push(...options.transformers);
39715
+ }
39716
+ transformers.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
39717
+ let result = pieces;
39718
+ for (const transformer of transformers) {
39719
+ if (transformer.appliesTo.length === 0 || transformer.appliesTo.includes(context.format)) {
39720
+ result = await transformer.transform(result, context);
39721
+ }
39722
+ }
39723
+ return result;
39724
+ }
39725
+ /**
39726
+ * Assemble metadata from pieces
39727
+ */
39728
+ assembleMetadata(pieces, filename, detection, startTime) {
39729
+ const textPieces = pieces.filter((p) => p.type === "text");
39730
+ const imagePieces = pieces.filter((p) => p.type === "image");
39731
+ const totalSizeBytes = pieces.reduce((sum, p) => sum + p.metadata.sizeBytes, 0);
39732
+ const estimatedTokens = pieces.reduce((sum, p) => sum + p.metadata.estimatedTokens, 0);
39733
+ return {
39734
+ filename,
39735
+ format: detection.format,
39736
+ family: detection.family,
39737
+ mimeType: detection.mimeType,
39738
+ totalPieces: pieces.length,
39739
+ totalTextPieces: textPieces.length,
39740
+ totalImagePieces: imagePieces.length,
39741
+ totalSizeBytes,
39742
+ estimatedTokens,
39743
+ processingTimeMs: Date.now() - startTime
39744
+ };
39745
+ }
39746
+ };
39747
+ function mergeTextPieces(pieces) {
39748
+ return pieces.filter((p) => p.type === "text").map((p) => p.content).join("\n\n");
39749
+ }
39750
+
39751
+ // src/capabilities/documents/index.ts
39752
+ init_handlers();
39753
+ init_transformers();
39754
+
39755
+ // src/utils/documentContentBridge.ts
39756
+ function documentToContent(result, options = {}) {
39757
+ const {
39758
+ imageDetail = "auto",
39759
+ imageFilter,
39760
+ maxImages = 20,
39761
+ mergeAdjacentText = true
39762
+ } = options;
39763
+ const minWidth = imageFilter?.minWidth ?? 0;
39764
+ const minHeight = imageFilter?.minHeight ?? 0;
39765
+ const minSizeBytes = imageFilter?.minSizeBytes ?? 0;
39766
+ const excludePatterns = imageFilter?.excludePatterns ?? [];
39767
+ const contents = [];
39768
+ let imageCount = 0;
39769
+ let pendingText = [];
39770
+ const flushText = () => {
39771
+ if (pendingText.length > 0) {
39772
+ const text = {
39773
+ type: "input_text" /* INPUT_TEXT */,
39774
+ text: pendingText.join("\n\n")
39775
+ };
39776
+ contents.push(text);
39777
+ pendingText = [];
39778
+ }
39779
+ };
39780
+ for (const piece of result.pieces) {
39781
+ if (piece.type === "text") {
39782
+ if (mergeAdjacentText) {
39783
+ pendingText.push(piece.content);
39784
+ } else {
39785
+ const text = {
39786
+ type: "input_text" /* INPUT_TEXT */,
39787
+ text: piece.content
39788
+ };
39789
+ contents.push(text);
39790
+ }
39791
+ } else if (piece.type === "image") {
39792
+ if (piece.width !== void 0 && piece.width < minWidth) continue;
39793
+ if (piece.height !== void 0 && piece.height < minHeight) continue;
39794
+ if (piece.metadata.sizeBytes < minSizeBytes) continue;
39795
+ const label = piece.metadata.label || "";
39796
+ if (excludePatterns.some((p) => p.test(label))) continue;
39797
+ imageCount++;
39798
+ if (imageCount > maxImages) continue;
39799
+ flushText();
39800
+ const imageContent = {
39801
+ type: "input_image_url" /* INPUT_IMAGE_URL */,
39802
+ image_url: {
39803
+ url: `data:${piece.mimeType};base64,${piece.base64}`,
39804
+ detail: imageDetail
39805
+ }
39806
+ };
39807
+ contents.push(imageContent);
39808
+ }
39809
+ }
39810
+ flushText();
39811
+ return contents;
39812
+ }
39813
+ async function readDocumentAsContent(source, options = {}) {
39814
+ const {
39815
+ imageDetail,
39816
+ maxImages,
39817
+ mergeAdjacentText,
39818
+ // imageFilter is shared between both
39819
+ ...readOptions
39820
+ } = options;
39821
+ const contentOptions = {
39822
+ imageDetail,
39823
+ imageFilter: options.imageFilter,
39824
+ maxImages,
39825
+ mergeAdjacentText
39826
+ };
39827
+ const reader = DocumentReader.create();
39828
+ const result = await reader.read(source, readOptions);
39829
+ if (!result.success) {
39830
+ return [
39831
+ {
39832
+ type: "input_text" /* INPUT_TEXT */,
39833
+ text: `[Document read error: ${result.error || "Unknown error"}]`
39834
+ }
39835
+ ];
39836
+ }
39837
+ return documentToContent(result, contentOptions);
39838
+ }
39839
+
38594
39840
  // src/domain/interfaces/IContextStorage.ts
38595
39841
  var CONTEXT_SESSION_FORMAT_VERSION = 1;
38596
39842
 
@@ -40531,6 +41777,21 @@ var ApproximateTokenEstimator = class {
40531
41777
  return 100;
40532
41778
  }
40533
41779
  }
41780
+ /**
41781
+ * Estimate tokens for an image using tile-based model (matches OpenAI pricing).
41782
+ *
41783
+ * - detail='low': 85 tokens
41784
+ * - detail='high' with known dimensions: 85 + 170 per 512×512 tile
41785
+ * - Unknown dimensions: 1000 tokens (conservative default)
41786
+ */
41787
+ estimateImageTokens(width, height, detail) {
41788
+ if (detail === "low") return 85;
41789
+ if (width && height) {
41790
+ const tiles = Math.ceil(width / 512) * Math.ceil(height / 512);
41791
+ return 85 + 170 * tiles;
41792
+ }
41793
+ return 1e3;
41794
+ }
40534
41795
  };
40535
41796
 
40536
41797
  // src/infrastructure/context/estimators/index.ts
@@ -40758,10 +42019,17 @@ var FileContextStorage = class {
40758
42019
  }
40759
42020
  /**
40760
42021
  * Get the storage path (for display/debugging)
42022
+ * @deprecated Use getLocation() instead
40761
42023
  */
40762
42024
  getPath() {
40763
42025
  return this.sessionsDirectory;
40764
42026
  }
42027
+ /**
42028
+ * Get a human-readable storage location string (for display/debugging)
42029
+ */
42030
+ getLocation() {
42031
+ return this.sessionsDirectory;
42032
+ }
40765
42033
  /**
40766
42034
  * Get the agent ID
40767
42035
  */
@@ -41133,7 +42401,7 @@ var FileAgentDefinitionStorage = class {
41133
42401
  function createFileAgentDefinitionStorage(config) {
41134
42402
  return new FileAgentDefinitionStorage(config);
41135
42403
  }
41136
- var MIME_TYPES = {
42404
+ var MIME_TYPES2 = {
41137
42405
  png: "image/png",
41138
42406
  jpeg: "image/jpeg",
41139
42407
  jpg: "image/jpeg",
@@ -41163,7 +42431,7 @@ var FileMediaStorage = class {
41163
42431
  const filePath = path2__namespace.join(dir, filename);
41164
42432
  await fs15__namespace.writeFile(filePath, data);
41165
42433
  const format = metadata.format.toLowerCase();
41166
- const mimeType = MIME_TYPES[format] ?? "application/octet-stream";
42434
+ const mimeType = MIME_TYPES2[format] ?? "application/octet-stream";
41167
42435
  return {
41168
42436
  location: filePath,
41169
42437
  mimeType,
@@ -41208,7 +42476,7 @@ var FileMediaStorage = class {
41208
42476
  const stat6 = await fs15__namespace.stat(filePath);
41209
42477
  if (!stat6.isFile()) continue;
41210
42478
  const ext = path2__namespace.extname(file).slice(1).toLowerCase();
41211
- const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
42479
+ const mimeType = MIME_TYPES2[ext] ?? "application/octet-stream";
41212
42480
  let type;
41213
42481
  for (const prefix of MEDIA_TYPE_PREFIXES) {
41214
42482
  if (file.startsWith(`${prefix}_`)) {
@@ -41700,6 +42968,23 @@ var SERVICE_DEFINITIONS = [
41700
42968
  baseURL: "https://api-m.paypal.com/v2",
41701
42969
  docsURL: "https://developer.paypal.com/docs/api/"
41702
42970
  },
42971
+ {
42972
+ id: "quickbooks",
42973
+ name: "QuickBooks",
42974
+ category: "payments",
42975
+ urlPattern: /quickbooks\.api\.intuit\.com|intuit\.com.*quickbooks/i,
42976
+ baseURL: "https://quickbooks.api.intuit.com/v3",
42977
+ docsURL: "https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/account",
42978
+ commonScopes: ["com.intuit.quickbooks.accounting"]
42979
+ },
42980
+ {
42981
+ id: "ramp",
42982
+ name: "Ramp",
42983
+ category: "payments",
42984
+ urlPattern: /api\.ramp\.com/i,
42985
+ baseURL: "https://api.ramp.com/developer/v1",
42986
+ docsURL: "https://docs.ramp.com/reference"
42987
+ },
41703
42988
  // ============ Cloud Providers ============
41704
42989
  {
41705
42990
  id: "aws",
@@ -43125,7 +44410,9 @@ function getVendorInfo(vendorId) {
43125
44410
  name: a.name,
43126
44411
  type: a.type,
43127
44412
  description: a.description,
43128
- requiredFields: a.requiredFields
44413
+ requiredFields: a.requiredFields,
44414
+ scopes: a.scopes,
44415
+ scopeDescriptions: a.scopeDescriptions
43129
44416
  }))
43130
44417
  };
43131
44418
  }
@@ -43141,7 +44428,9 @@ function listVendors() {
43141
44428
  name: a.name,
43142
44429
  type: a.type,
43143
44430
  description: a.description,
43144
- requiredFields: a.requiredFields
44431
+ requiredFields: a.requiredFields,
44432
+ scopes: a.scopes,
44433
+ scopeDescriptions: a.scopeDescriptions
43145
44434
  }))
43146
44435
  }));
43147
44436
  }
@@ -43190,14 +44479,49 @@ var microsoftTemplate = {
43190
44479
  scopes: [
43191
44480
  "User.Read",
43192
44481
  "Mail.Read",
44482
+ "Mail.ReadWrite",
43193
44483
  "Mail.Send",
43194
- "Files.ReadWrite",
43195
44484
  "Calendars.ReadWrite",
44485
+ "Contacts.Read",
44486
+ "Contacts.ReadWrite",
44487
+ "Files.ReadWrite",
44488
+ "Sites.Read.All",
44489
+ "Sites.ReadWrite.All",
44490
+ "Notes.Read",
44491
+ "Notes.ReadWrite",
44492
+ "Tasks.ReadWrite",
43196
44493
  "ChannelMessage.Send",
43197
44494
  "Team.ReadBasic.All",
43198
44495
  "Chat.ReadWrite",
44496
+ "People.Read",
44497
+ "Presence.Read",
44498
+ "Directory.Read.All",
44499
+ "BookingsAppointment.ReadWrite.All",
43199
44500
  "offline_access"
43200
- ]
44501
+ ],
44502
+ scopeDescriptions: {
44503
+ "User.Read": "Read your profile",
44504
+ "Mail.Read": "Read your email",
44505
+ "Mail.ReadWrite": "Read and write your email",
44506
+ "Mail.Send": "Send email on your behalf",
44507
+ "Calendars.ReadWrite": "Read and write your calendar",
44508
+ "Contacts.Read": "Read your contacts",
44509
+ "Contacts.ReadWrite": "Read and write your contacts",
44510
+ "Files.ReadWrite": "Read and write your files (OneDrive)",
44511
+ "Sites.Read.All": "Read SharePoint sites",
44512
+ "Sites.ReadWrite.All": "Read and write SharePoint sites",
44513
+ "Notes.Read": "Read your OneNote notebooks",
44514
+ "Notes.ReadWrite": "Read and write your OneNote notebooks",
44515
+ "Tasks.ReadWrite": "Read and write your tasks (To Do / Planner)",
44516
+ "ChannelMessage.Send": "Send messages in Teams channels",
44517
+ "Team.ReadBasic.All": "Read Teams basic info",
44518
+ "Chat.ReadWrite": "Read and write Teams chats",
44519
+ "People.Read": "Read your relevant people list",
44520
+ "Presence.Read": "Read user presence information",
44521
+ "Directory.Read.All": "Read directory data (Azure AD)",
44522
+ "BookingsAppointment.ReadWrite.All": "Manage Bookings appointments",
44523
+ "offline_access": "Maintain access (refresh token)"
44524
+ }
43201
44525
  },
43202
44526
  {
43203
44527
  id: "client-credentials",
@@ -43212,7 +44536,10 @@ var microsoftTemplate = {
43212
44536
  flow: "client_credentials",
43213
44537
  tokenUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"
43214
44538
  },
43215
- scopes: ["https://graph.microsoft.com/.default"]
44539
+ scopes: ["https://graph.microsoft.com/.default"],
44540
+ scopeDescriptions: {
44541
+ "https://graph.microsoft.com/.default": "All permissions granted to the app registration"
44542
+ }
43216
44543
  }
43217
44544
  ]
43218
44545
  };
@@ -43246,9 +44573,24 @@ var googleTemplate = {
43246
44573
  "https://www.googleapis.com/auth/drive",
43247
44574
  "https://www.googleapis.com/auth/calendar",
43248
44575
  "https://www.googleapis.com/auth/gmail.readonly",
44576
+ "https://www.googleapis.com/auth/gmail.send",
43249
44577
  "https://www.googleapis.com/auth/spreadsheets",
43250
- "https://www.googleapis.com/auth/documents"
43251
- ]
44578
+ "https://www.googleapis.com/auth/documents",
44579
+ "https://www.googleapis.com/auth/contacts.readonly",
44580
+ "https://www.googleapis.com/auth/tasks",
44581
+ "https://www.googleapis.com/auth/admin.directory.user.readonly"
44582
+ ],
44583
+ scopeDescriptions: {
44584
+ "https://www.googleapis.com/auth/drive": "Read and write Google Drive files",
44585
+ "https://www.googleapis.com/auth/calendar": "Read and write Google Calendar",
44586
+ "https://www.googleapis.com/auth/gmail.readonly": "Read Gmail messages",
44587
+ "https://www.googleapis.com/auth/gmail.send": "Send Gmail messages",
44588
+ "https://www.googleapis.com/auth/spreadsheets": "Read and write Google Sheets",
44589
+ "https://www.googleapis.com/auth/documents": "Read and write Google Docs",
44590
+ "https://www.googleapis.com/auth/contacts.readonly": "Read Google Contacts",
44591
+ "https://www.googleapis.com/auth/tasks": "Read and write Google Tasks",
44592
+ "https://www.googleapis.com/auth/admin.directory.user.readonly": "Read user directory (Admin)"
44593
+ }
43252
44594
  },
43253
44595
  {
43254
44596
  id: "service-account",
@@ -43267,7 +44609,11 @@ var googleTemplate = {
43267
44609
  scopes: [
43268
44610
  "https://www.googleapis.com/auth/cloud-platform",
43269
44611
  "https://www.googleapis.com/auth/drive"
43270
- ]
44612
+ ],
44613
+ scopeDescriptions: {
44614
+ "https://www.googleapis.com/auth/cloud-platform": "Full access to Google Cloud Platform",
44615
+ "https://www.googleapis.com/auth/drive": "Read and write Google Drive files"
44616
+ }
43271
44617
  }
43272
44618
  ]
43273
44619
  };
@@ -43309,7 +44655,19 @@ var slackTemplate = {
43309
44655
  authorizationUrl: "https://slack.com/oauth/v2/authorize",
43310
44656
  tokenUrl: "https://slack.com/api/oauth.v2.access"
43311
44657
  },
43312
- scopes: ["chat:write", "channels:read", "users:read", "im:write", "groups:read"]
44658
+ scopes: ["chat:write", "channels:read", "users:read", "im:write", "groups:read", "files:read", "files:write", "reactions:read", "reactions:write", "team:read"],
44659
+ scopeDescriptions: {
44660
+ "chat:write": "Send messages as the app",
44661
+ "channels:read": "View basic channel info",
44662
+ "users:read": "View people in the workspace",
44663
+ "im:write": "Send direct messages",
44664
+ "groups:read": "View basic private channel info",
44665
+ "files:read": "View files shared in channels",
44666
+ "files:write": "Upload and manage files",
44667
+ "reactions:read": "View emoji reactions",
44668
+ "reactions:write": "Add and remove emoji reactions",
44669
+ "team:read": "View workspace info"
44670
+ }
43313
44671
  }
43314
44672
  ]
43315
44673
  };
@@ -43350,7 +44708,16 @@ var discordTemplate = {
43350
44708
  authorizationUrl: "https://discord.com/api/oauth2/authorize",
43351
44709
  tokenUrl: "https://discord.com/api/oauth2/token"
43352
44710
  },
43353
- scopes: ["identify", "guilds", "guilds.members.read", "messages.read"]
44711
+ scopes: ["identify", "email", "guilds", "guilds.members.read", "messages.read", "bot", "connections"],
44712
+ scopeDescriptions: {
44713
+ "identify": "Access your username and avatar",
44714
+ "email": "Access your email address",
44715
+ "guilds": "View your server list",
44716
+ "guilds.members.read": "Read server member info",
44717
+ "messages.read": "Read messages in accessible channels",
44718
+ "bot": "Add a bot to your servers",
44719
+ "connections": "View your connected accounts"
44720
+ }
43354
44721
  }
43355
44722
  ]
43356
44723
  };
@@ -43417,7 +44784,18 @@ var githubTemplate = {
43417
44784
  authorizationUrl: "https://github.com/login/oauth/authorize",
43418
44785
  tokenUrl: "https://github.com/login/oauth/access_token"
43419
44786
  },
43420
- scopes: ["repo", "read:user", "read:org", "workflow", "gist"]
44787
+ scopes: ["repo", "read:user", "user:email", "read:org", "workflow", "gist", "notifications", "delete_repo", "admin:org"],
44788
+ scopeDescriptions: {
44789
+ "repo": "Full control of private repositories",
44790
+ "read:user": "Read user profile data",
44791
+ "user:email": "Access user email addresses",
44792
+ "read:org": "Read org and team membership",
44793
+ "workflow": "Update GitHub Actions workflows",
44794
+ "gist": "Create and manage gists",
44795
+ "notifications": "Access notifications",
44796
+ "delete_repo": "Delete repositories",
44797
+ "admin:org": "Full control of orgs and teams"
44798
+ }
43421
44799
  },
43422
44800
  {
43423
44801
  id: "github-app",
@@ -43472,7 +44850,13 @@ var gitlabTemplate = {
43472
44850
  authorizationUrl: "https://gitlab.com/oauth/authorize",
43473
44851
  tokenUrl: "https://gitlab.com/oauth/token"
43474
44852
  },
43475
- scopes: ["api", "read_user", "read_repository", "write_repository"]
44853
+ scopes: ["api", "read_user", "read_repository", "write_repository"],
44854
+ scopeDescriptions: {
44855
+ "api": "Full API access",
44856
+ "read_user": "Read user profile",
44857
+ "read_repository": "Read repository contents",
44858
+ "write_repository": "Write to repositories"
44859
+ }
43476
44860
  }
43477
44861
  ]
43478
44862
  };
@@ -43514,7 +44898,14 @@ var jiraTemplate = {
43514
44898
  authorizationUrl: "https://auth.atlassian.com/authorize",
43515
44899
  tokenUrl: "https://auth.atlassian.com/oauth/token"
43516
44900
  },
43517
- scopes: ["read:jira-work", "write:jira-work", "read:jira-user"]
44901
+ scopes: ["read:jira-work", "write:jira-work", "read:jira-user", "manage:jira-project", "manage:jira-configuration"],
44902
+ scopeDescriptions: {
44903
+ "read:jira-work": "Read issues, projects, boards",
44904
+ "write:jira-work": "Create and update issues",
44905
+ "read:jira-user": "Read user information",
44906
+ "manage:jira-project": "Manage projects and components",
44907
+ "manage:jira-configuration": "Manage Jira settings"
44908
+ }
43518
44909
  }
43519
44910
  ]
43520
44911
  };
@@ -43554,7 +44945,14 @@ var confluenceTemplate = {
43554
44945
  authorizationUrl: "https://auth.atlassian.com/authorize",
43555
44946
  tokenUrl: "https://auth.atlassian.com/oauth/token"
43556
44947
  },
43557
- scopes: ["read:confluence-content.all", "write:confluence-content", "read:confluence-space.summary"]
44948
+ scopes: ["read:confluence-content.all", "write:confluence-content", "read:confluence-space.summary", "write:confluence-space", "read:confluence-user"],
44949
+ scopeDescriptions: {
44950
+ "read:confluence-content.all": "Read all pages and blog posts",
44951
+ "write:confluence-content": "Create and update pages",
44952
+ "read:confluence-space.summary": "Read space summaries",
44953
+ "write:confluence-space": "Create and manage spaces",
44954
+ "read:confluence-user": "Read user information"
44955
+ }
43558
44956
  }
43559
44957
  ]
43560
44958
  };
@@ -43593,7 +44991,16 @@ var bitbucketTemplate = {
43593
44991
  authorizationUrl: "https://bitbucket.org/site/oauth2/authorize",
43594
44992
  tokenUrl: "https://bitbucket.org/site/oauth2/access_token"
43595
44993
  },
43596
- scopes: ["repository", "pullrequest", "account"]
44994
+ scopes: ["repository", "repository:write", "pullrequest", "pullrequest:write", "account", "pipeline", "wiki"],
44995
+ scopeDescriptions: {
44996
+ "repository": "Read repositories",
44997
+ "repository:write": "Write to repositories",
44998
+ "pullrequest": "Read pull requests",
44999
+ "pullrequest:write": "Create and update pull requests",
45000
+ "account": "Read account information",
45001
+ "pipeline": "Access Pipelines (CI/CD)",
45002
+ "wiki": "Access repository wiki"
45003
+ }
43597
45004
  }
43598
45005
  ]
43599
45006
  };
@@ -43633,7 +45040,12 @@ var trelloTemplate = {
43633
45040
  authorizationUrl: "https://trello.com/1/authorize",
43634
45041
  tokenUrl: "https://trello.com/1/OAuthGetAccessToken"
43635
45042
  },
43636
- scopes: ["read", "write", "account"]
45043
+ scopes: ["read", "write", "account"],
45044
+ scopeDescriptions: {
45045
+ "read": "Read boards, lists, and cards",
45046
+ "write": "Create and update boards, lists, and cards",
45047
+ "account": "Read member information"
45048
+ }
43637
45049
  }
43638
45050
  ]
43639
45051
  };
@@ -43828,7 +45240,15 @@ var salesforceTemplate = {
43828
45240
  authorizationUrl: "https://login.salesforce.com/services/oauth2/authorize",
43829
45241
  tokenUrl: "https://login.salesforce.com/services/oauth2/token"
43830
45242
  },
43831
- scopes: ["api", "refresh_token", "offline_access"]
45243
+ scopes: ["api", "refresh_token", "offline_access", "chatter_api", "wave_api", "full"],
45244
+ scopeDescriptions: {
45245
+ "api": "Access and manage your data",
45246
+ "refresh_token": "Maintain access with refresh tokens",
45247
+ "offline_access": "Access data while you are offline",
45248
+ "chatter_api": "Access Chatter feeds and posts",
45249
+ "wave_api": "Access Analytics (Wave) API",
45250
+ "full": "Full access to all data"
45251
+ }
43832
45252
  },
43833
45253
  {
43834
45254
  id: "jwt-bearer",
@@ -43883,7 +45303,26 @@ var hubspotTemplate = {
43883
45303
  authorizationUrl: "https://app.hubspot.com/oauth/authorize",
43884
45304
  tokenUrl: "https://api.hubapi.com/oauth/v1/token"
43885
45305
  },
43886
- scopes: ["crm.objects.contacts.read", "crm.objects.contacts.write", "crm.objects.companies.read"]
45306
+ scopes: [
45307
+ "crm.objects.contacts.read",
45308
+ "crm.objects.contacts.write",
45309
+ "crm.objects.companies.read",
45310
+ "crm.objects.companies.write",
45311
+ "crm.objects.deals.read",
45312
+ "crm.objects.deals.write",
45313
+ "tickets",
45314
+ "e-commerce"
45315
+ ],
45316
+ scopeDescriptions: {
45317
+ "crm.objects.contacts.read": "Read contacts",
45318
+ "crm.objects.contacts.write": "Create and update contacts",
45319
+ "crm.objects.companies.read": "Read companies",
45320
+ "crm.objects.companies.write": "Create and update companies",
45321
+ "crm.objects.deals.read": "Read deals",
45322
+ "crm.objects.deals.write": "Create and update deals",
45323
+ "tickets": "Read and write support tickets",
45324
+ "e-commerce": "Access e-commerce data (products, line items)"
45325
+ }
43887
45326
  }
43888
45327
  ]
43889
45328
  };
@@ -43996,6 +45435,86 @@ var paypalTemplate = {
43996
45435
  ]
43997
45436
  };
43998
45437
 
45438
+ // src/connectors/vendors/templates/quickbooks.ts
45439
+ var quickbooksTemplate = {
45440
+ id: "quickbooks",
45441
+ name: "QuickBooks",
45442
+ serviceType: "quickbooks",
45443
+ baseURL: "https://quickbooks.api.intuit.com/v3",
45444
+ docsURL: "https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/account",
45445
+ credentialsSetupURL: "https://developer.intuit.com/app/developer/dashboard",
45446
+ category: "payments",
45447
+ notes: "Use sandbox URL (sandbox-quickbooks.api.intuit.com) for testing. Requires company/realm ID in API paths.",
45448
+ authTemplates: [
45449
+ {
45450
+ id: "oauth-user",
45451
+ name: "OAuth (User Authorization)",
45452
+ type: "oauth",
45453
+ flow: "authorization_code",
45454
+ description: "Standard OAuth 2.0 flow for accessing QuickBooks on behalf of a user. Create an app at developer.intuit.com",
45455
+ requiredFields: ["clientId", "clientSecret", "redirectUri"],
45456
+ optionalFields: ["scope"],
45457
+ defaults: {
45458
+ type: "oauth",
45459
+ flow: "authorization_code",
45460
+ authorizationUrl: "https://appcenter.intuit.com/connect/oauth2",
45461
+ tokenUrl: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
45462
+ },
45463
+ scopes: ["com.intuit.quickbooks.accounting", "com.intuit.quickbooks.payment"]
45464
+ }
45465
+ ]
45466
+ };
45467
+
45468
+ // src/connectors/vendors/templates/ramp.ts
45469
+ var rampTemplate = {
45470
+ id: "ramp",
45471
+ name: "Ramp",
45472
+ serviceType: "ramp",
45473
+ baseURL: "https://api.ramp.com/developer/v1",
45474
+ docsURL: "https://docs.ramp.com",
45475
+ credentialsSetupURL: "https://app.ramp.com/settings/developer",
45476
+ category: "payments",
45477
+ authTemplates: [
45478
+ {
45479
+ id: "oauth-client-credentials",
45480
+ name: "OAuth (Client Credentials)",
45481
+ type: "oauth",
45482
+ flow: "client_credentials",
45483
+ description: "App-level authentication using client credentials. Create an API application in Ramp developer settings",
45484
+ requiredFields: ["clientId", "clientSecret"],
45485
+ defaults: {
45486
+ type: "oauth",
45487
+ flow: "client_credentials",
45488
+ tokenUrl: "https://api.ramp.com/developer/v1/token"
45489
+ }
45490
+ },
45491
+ {
45492
+ id: "oauth-user",
45493
+ name: "OAuth (User Authorization)",
45494
+ type: "oauth",
45495
+ flow: "authorization_code",
45496
+ description: "OAuth 2.0 authorization code flow for accessing Ramp on behalf of a user",
45497
+ requiredFields: ["clientId", "clientSecret", "redirectUri"],
45498
+ optionalFields: ["scope"],
45499
+ defaults: {
45500
+ type: "oauth",
45501
+ flow: "authorization_code",
45502
+ authorizationUrl: "https://app.ramp.com/v1/authorize",
45503
+ tokenUrl: "https://api.ramp.com/developer/v1/token"
45504
+ },
45505
+ scopes: [
45506
+ "transactions:read",
45507
+ "users:read",
45508
+ "users:write",
45509
+ "cards:read",
45510
+ "cards:write",
45511
+ "departments:read",
45512
+ "reimbursements:read"
45513
+ ]
45514
+ }
45515
+ ]
45516
+ };
45517
+
43999
45518
  // src/connectors/vendors/templates/aws.ts
44000
45519
  var awsTemplate = {
44001
45520
  id: "aws",
@@ -44048,7 +45567,16 @@ var dropboxTemplate = {
44048
45567
  tokenUrl: "https://api.dropboxapi.com/oauth2/token",
44049
45568
  usePKCE: true
44050
45569
  },
44051
- scopes: ["files.content.read", "files.content.write", "files.metadata.read"]
45570
+ scopes: ["files.content.read", "files.content.write", "files.metadata.read", "files.metadata.write", "sharing.read", "sharing.write", "account_info.read"],
45571
+ scopeDescriptions: {
45572
+ "files.content.read": "Read file contents",
45573
+ "files.content.write": "Upload and modify files",
45574
+ "files.metadata.read": "Read file and folder metadata",
45575
+ "files.metadata.write": "Modify file and folder metadata",
45576
+ "sharing.read": "View sharing settings",
45577
+ "sharing.write": "Manage sharing settings",
45578
+ "account_info.read": "Read account information"
45579
+ }
44052
45580
  }
44053
45581
  ]
44054
45582
  };
@@ -44076,6 +45604,13 @@ var boxTemplate = {
44076
45604
  flow: "authorization_code",
44077
45605
  authorizationUrl: "https://account.box.com/api/oauth2/authorize",
44078
45606
  tokenUrl: "https://api.box.com/oauth2/token"
45607
+ },
45608
+ scopes: ["root_readwrite", "manage_users", "manage_groups", "manage_enterprise"],
45609
+ scopeDescriptions: {
45610
+ "root_readwrite": "Read and write all files and folders",
45611
+ "manage_users": "Manage enterprise users",
45612
+ "manage_groups": "Manage enterprise groups",
45613
+ "manage_enterprise": "Manage enterprise settings"
44079
45614
  }
44080
45615
  },
44081
45616
  {
@@ -44252,6 +45787,11 @@ var pagerdutyTemplate = {
44252
45787
  flow: "authorization_code",
44253
45788
  authorizationUrl: "https://app.pagerduty.com/oauth/authorize",
44254
45789
  tokenUrl: "https://app.pagerduty.com/oauth/token"
45790
+ },
45791
+ scopes: ["read", "write"],
45792
+ scopeDescriptions: {
45793
+ "read": "Read incidents, services, and schedules",
45794
+ "write": "Create and update incidents and services"
44255
45795
  }
44256
45796
  }
44257
45797
  ]
@@ -44290,6 +45830,14 @@ var sentryTemplate = {
44290
45830
  flow: "authorization_code",
44291
45831
  authorizationUrl: "https://sentry.io/oauth/authorize/",
44292
45832
  tokenUrl: "https://sentry.io/oauth/token/"
45833
+ },
45834
+ scopes: ["project:read", "project:write", "event:read", "org:read", "member:read"],
45835
+ scopeDescriptions: {
45836
+ "project:read": "Read project settings",
45837
+ "project:write": "Manage project settings",
45838
+ "event:read": "Read error events and issues",
45839
+ "org:read": "Read organization info",
45840
+ "member:read": "Read org member info"
44293
45841
  }
44294
45842
  }
44295
45843
  ]
@@ -44487,7 +46035,13 @@ var zendeskTemplate = {
44487
46035
  authorizationUrl: "https://{subdomain}.zendesk.com/oauth/authorizations/new",
44488
46036
  tokenUrl: "https://{subdomain}.zendesk.com/oauth/tokens"
44489
46037
  },
44490
- scopes: ["read", "write", "tickets:read", "tickets:write"]
46038
+ scopes: ["read", "write", "tickets:read", "tickets:write"],
46039
+ scopeDescriptions: {
46040
+ "read": "Read all resources",
46041
+ "write": "Create and update resources",
46042
+ "tickets:read": "Read support tickets",
46043
+ "tickets:write": "Create and update tickets"
46044
+ }
44491
46045
  }
44492
46046
  ]
44493
46047
  };
@@ -44565,7 +46119,19 @@ var shopifyTemplate = {
44565
46119
  authorizationUrl: "https://{subdomain}.myshopify.com/admin/oauth/authorize",
44566
46120
  tokenUrl: "https://{subdomain}.myshopify.com/admin/oauth/access_token"
44567
46121
  },
44568
- scopes: ["read_products", "write_products", "read_orders", "write_orders"]
46122
+ scopes: ["read_products", "write_products", "read_orders", "write_orders", "read_customers", "write_customers", "read_inventory", "write_inventory", "read_fulfillments", "write_fulfillments"],
46123
+ scopeDescriptions: {
46124
+ "read_products": "Read products and collections",
46125
+ "write_products": "Create and update products",
46126
+ "read_orders": "Read orders and transactions",
46127
+ "write_orders": "Create and update orders",
46128
+ "read_customers": "Read customer information",
46129
+ "write_customers": "Create and update customers",
46130
+ "read_inventory": "Read inventory levels",
46131
+ "write_inventory": "Update inventory levels",
46132
+ "read_fulfillments": "Read fulfillment data",
46133
+ "write_fulfillments": "Create and update fulfillments"
46134
+ }
44569
46135
  }
44570
46136
  ]
44571
46137
  };
@@ -44598,6 +46164,8 @@ var allVendorTemplates = [
44598
46164
  // Payments
44599
46165
  stripeTemplate,
44600
46166
  paypalTemplate,
46167
+ quickbooksTemplate,
46168
+ rampTemplate,
44601
46169
  // Cloud
44602
46170
  awsTemplate,
44603
46171
  // Storage
@@ -44660,6 +46228,9 @@ var VENDOR_ICON_MAP = {
44660
46228
  // Payments
44661
46229
  stripe: "stripe",
44662
46230
  paypal: "paypal",
46231
+ quickbooks: "quickbooks",
46232
+ ramp: null,
46233
+ // No Simple Icon available
44663
46234
  // Email
44664
46235
  sendgrid: "sendgrid",
44665
46236
  mailchimp: "mailchimp",
@@ -44706,6 +46277,8 @@ var FALLBACK_PLACEHOLDERS = {
44706
46277
  // Email (trademark removed)
44707
46278
  sendgrid: { color: "#1A82E2", letter: "S" },
44708
46279
  postmark: { color: "#FFDE00", letter: "P" },
46280
+ // Payments (no Simple Icon available)
46281
+ ramp: { color: "#F2C94C", letter: "R" },
44709
46282
  // Search (no Simple Icon available)
44710
46283
  serper: { color: "#4A90A4", letter: "S" },
44711
46284
  tavily: { color: "#7C3AED", letter: "T" },
@@ -45485,13 +47058,30 @@ function extractNumber(text, patterns = [
45485
47058
  var tools_exports = {};
45486
47059
  __export(tools_exports, {
45487
47060
  ConnectorTools: () => ConnectorTools,
47061
+ DEFAULT_DESKTOP_CONFIG: () => DEFAULT_DESKTOP_CONFIG,
45488
47062
  DEFAULT_FILESYSTEM_CONFIG: () => DEFAULT_FILESYSTEM_CONFIG,
45489
47063
  DEFAULT_SHELL_CONFIG: () => DEFAULT_SHELL_CONFIG,
47064
+ DESKTOP_TOOL_NAMES: () => DESKTOP_TOOL_NAMES,
47065
+ DocumentReader: () => DocumentReader,
45490
47066
  FileMediaOutputHandler: () => FileMediaStorage,
47067
+ FormatDetector: () => FormatDetector,
47068
+ NutTreeDriver: () => NutTreeDriver,
45491
47069
  ToolRegistry: () => ToolRegistry,
47070
+ applyHumanDelay: () => applyHumanDelay,
45492
47071
  bash: () => bash,
45493
47072
  createBashTool: () => createBashTool,
45494
47073
  createCreatePRTool: () => createCreatePRTool,
47074
+ createDesktopGetCursorTool: () => createDesktopGetCursorTool,
47075
+ createDesktopGetScreenSizeTool: () => createDesktopGetScreenSizeTool,
47076
+ createDesktopKeyboardKeyTool: () => createDesktopKeyboardKeyTool,
47077
+ createDesktopKeyboardTypeTool: () => createDesktopKeyboardTypeTool,
47078
+ createDesktopMouseClickTool: () => createDesktopMouseClickTool,
47079
+ createDesktopMouseDragTool: () => createDesktopMouseDragTool,
47080
+ createDesktopMouseMoveTool: () => createDesktopMouseMoveTool,
47081
+ createDesktopMouseScrollTool: () => createDesktopMouseScrollTool,
47082
+ createDesktopScreenshotTool: () => createDesktopScreenshotTool,
47083
+ createDesktopWindowFocusTool: () => createDesktopWindowFocusTool,
47084
+ createDesktopWindowListTool: () => createDesktopWindowListTool,
45495
47085
  createEditFileTool: () => createEditFileTool,
45496
47086
  createExecuteJavaScriptTool: () => createExecuteJavaScriptTool,
45497
47087
  createGetPRTool: () => createGetPRTool,
@@ -45511,12 +47101,25 @@ __export(tools_exports, {
45511
47101
  createWebScrapeTool: () => createWebScrapeTool,
45512
47102
  createWebSearchTool: () => createWebSearchTool,
45513
47103
  createWriteFileTool: () => createWriteFileTool,
47104
+ desktopGetCursor: () => desktopGetCursor,
47105
+ desktopGetScreenSize: () => desktopGetScreenSize,
47106
+ desktopKeyboardKey: () => desktopKeyboardKey,
47107
+ desktopKeyboardType: () => desktopKeyboardType,
47108
+ desktopMouseClick: () => desktopMouseClick,
47109
+ desktopMouseDrag: () => desktopMouseDrag,
47110
+ desktopMouseMove: () => desktopMouseMove,
47111
+ desktopMouseScroll: () => desktopMouseScroll,
47112
+ desktopScreenshot: () => desktopScreenshot,
47113
+ desktopTools: () => desktopTools,
47114
+ desktopWindowFocus: () => desktopWindowFocus,
47115
+ desktopWindowList: () => desktopWindowList,
45514
47116
  developerTools: () => developerTools,
45515
47117
  editFile: () => editFile,
45516
47118
  executeJavaScript: () => executeJavaScript,
45517
47119
  expandTilde: () => expandTilde,
45518
47120
  getAllBuiltInTools: () => getAllBuiltInTools,
45519
47121
  getBackgroundOutput: () => getBackgroundOutput,
47122
+ getDesktopDriver: () => getDesktopDriver,
45520
47123
  getMediaOutputHandler: () => getMediaOutputHandler,
45521
47124
  getMediaStorage: () => getMediaStorage,
45522
47125
  getToolByName: () => getToolByName,
@@ -45531,8 +47134,11 @@ __export(tools_exports, {
45531
47134
  jsonManipulator: () => jsonManipulator,
45532
47135
  killBackgroundProcess: () => killBackgroundProcess,
45533
47136
  listDirectory: () => listDirectory,
47137
+ mergeTextPieces: () => mergeTextPieces,
47138
+ parseKeyCombo: () => parseKeyCombo,
45534
47139
  parseRepository: () => parseRepository,
45535
47140
  readFile: () => readFile5,
47141
+ resetDefaultDriver: () => resetDefaultDriver,
45536
47142
  resolveRepository: () => resolveRepository,
45537
47143
  setMediaOutputHandler: () => setMediaOutputHandler,
45538
47144
  setMediaStorage: () => setMediaStorage,
@@ -45578,13 +47184,11 @@ var DEFAULT_FILESYSTEM_CONFIG = {
45578
47184
  ".avi",
45579
47185
  ".mov",
45580
47186
  ".mkv",
45581
- ".pdf",
47187
+ // Note: .pdf, .docx, .xlsx, .pptx are NOT excluded — DocumentReader handles them
45582
47188
  ".doc",
45583
- ".docx",
45584
47189
  ".xls",
45585
- ".xlsx",
45586
47190
  ".ppt",
45587
- ".pptx",
47191
+ // Legacy Office formats not yet supported
45588
47192
  ".woff",
45589
47193
  ".woff2",
45590
47194
  ".ttf",
@@ -45592,6 +47196,9 @@ var DEFAULT_FILESYSTEM_CONFIG = {
45592
47196
  ".otf"
45593
47197
  ]
45594
47198
  };
47199
+ function toForwardSlash(p) {
47200
+ return path2.sep === "\\" ? p.replace(/\\/g, "/") : p;
47201
+ }
45595
47202
  function validatePath(inputPath, config = {}) {
45596
47203
  const workingDir = config.workingDirectory || process.cwd();
45597
47204
  const allowedDirs = config.allowedDirectories || [];
@@ -45608,7 +47215,8 @@ function validatePath(inputPath, config = {}) {
45608
47215
  } else {
45609
47216
  resolvedPath = path2.resolve(workingDir, expandedPath);
45610
47217
  }
45611
- const pathSegments = resolvedPath.split("/").filter(Boolean);
47218
+ const normalizedResolved = toForwardSlash(resolvedPath);
47219
+ const pathSegments = normalizedResolved.split("/").filter(Boolean);
45612
47220
  for (const blocked of blockedDirs) {
45613
47221
  if (!blocked.includes("/")) {
45614
47222
  if (pathSegments.includes(blocked)) {
@@ -45619,8 +47227,8 @@ function validatePath(inputPath, config = {}) {
45619
47227
  };
45620
47228
  }
45621
47229
  } else {
45622
- const blockedPath = path2.isAbsolute(blocked) ? blocked : path2.resolve(workingDir, blocked);
45623
- if (resolvedPath.startsWith(blockedPath + "/") || resolvedPath === blockedPath) {
47230
+ const blockedPath = toForwardSlash(path2.isAbsolute(blocked) ? blocked : path2.resolve(workingDir, blocked));
47231
+ if (normalizedResolved.startsWith(blockedPath + "/") || normalizedResolved === blockedPath) {
45624
47232
  return {
45625
47233
  valid: false,
45626
47234
  resolvedPath,
@@ -45632,8 +47240,8 @@ function validatePath(inputPath, config = {}) {
45632
47240
  if (allowedDirs.length > 0) {
45633
47241
  let isAllowed = false;
45634
47242
  for (const allowed of allowedDirs) {
45635
- const allowedPath = path2.isAbsolute(allowed) ? allowed : path2.resolve(workingDir, allowed);
45636
- if (resolvedPath.startsWith(allowedPath + "/") || resolvedPath === allowedPath) {
47243
+ const allowedPath = toForwardSlash(path2.isAbsolute(allowed) ? allowed : path2.resolve(workingDir, allowed));
47244
+ if (normalizedResolved.startsWith(allowedPath + "/") || normalizedResolved === allowedPath) {
45637
47245
  isAllowed = true;
45638
47246
  break;
45639
47247
  }
@@ -45667,7 +47275,7 @@ function createReadFileTool(config = {}) {
45667
47275
  type: "function",
45668
47276
  function: {
45669
47277
  name: "read_file",
45670
- description: `Read content from a file on the local filesystem.
47278
+ description: `Read content from a file on the local filesystem. Supports text files AND binary document formats \u2014 PDF, DOCX, PPTX, XLSX, ODS, ODT, ODP, and images (PNG, JPG, GIF, WEBP) are automatically converted to markdown text.
45671
47279
 
45672
47280
  USAGE:
45673
47281
  - The file_path parameter must be an absolute path, not a relative path
@@ -45676,20 +47284,34 @@ USAGE:
45676
47284
  - Any lines longer than 2000 characters will be truncated
45677
47285
  - Results are returned with line numbers starting at 1
45678
47286
 
47287
+ DOCUMENT SUPPORT:
47288
+ - PDF files: extracted as markdown text with per-page sections
47289
+ - Word documents (.docx): converted to markdown preserving headings, lists, tables
47290
+ - PowerPoint (.pptx): extracted slide-by-slide as markdown
47291
+ - Excel (.xlsx) / CSV / ODS: tables converted to markdown tables
47292
+ - OpenDocument (.odt, .odp, .ods): converted like their MS Office equivalents
47293
+ - Images (.png, .jpg, .gif, .webp): described as image metadata
47294
+ - Binary documents are auto-detected by extension \u2014 just pass the file path
47295
+
45679
47296
  WHEN TO USE:
45680
47297
  - To read source code files before making edits
45681
47298
  - To understand file contents and structure
45682
47299
  - To read configuration files
45683
47300
  - To examine log files or data files
47301
+ - To read PDF, Word, Excel, PowerPoint, or other document files as text
45684
47302
 
45685
47303
  IMPORTANT:
45686
47304
  - Always read a file before attempting to edit it
45687
47305
  - Use offset/limit for very large files to read in chunks
45688
47306
  - The tool will return an error if the file doesn't exist
47307
+ - offset/limit are ignored for binary document formats (full document is always returned)
45689
47308
 
45690
47309
  EXAMPLES:
45691
47310
  - Read entire file: { "file_path": "/path/to/file.ts" }
45692
- - Read lines 100-200: { "file_path": "/path/to/file.ts", "offset": 100, "limit": 100 }`,
47311
+ - Read lines 100-200: { "file_path": "/path/to/file.ts", "offset": 100, "limit": 100 }
47312
+ - Read a PDF: { "file_path": "/path/to/report.pdf" }
47313
+ - Read an Excel file: { "file_path": "/path/to/data.xlsx" }
47314
+ - Read a Word doc: { "file_path": "/path/to/document.docx" }`,
45693
47315
  parameters: {
45694
47316
  type: "object",
45695
47317
  properties: {
@@ -45751,6 +47373,32 @@ EXAMPLES:
45751
47373
  size: stats.size
45752
47374
  };
45753
47375
  }
47376
+ const ext = path2.extname(resolvedPath).toLowerCase();
47377
+ if (FormatDetector.isBinaryDocumentFormat(ext)) {
47378
+ try {
47379
+ const reader = DocumentReader.create(mergedConfig.documentReaderConfig);
47380
+ const result2 = await reader.read(
47381
+ { type: "file", path: resolvedPath },
47382
+ {
47383
+ extractImages: false,
47384
+ ...mergedConfig.documentReaderConfig?.defaults
47385
+ }
47386
+ );
47387
+ if (result2.success) {
47388
+ const content2 = mergeTextPieces(result2.pieces);
47389
+ return {
47390
+ success: true,
47391
+ content: content2,
47392
+ lines: content2.split("\n").length,
47393
+ truncated: false,
47394
+ encoding: "document",
47395
+ size: stats.size,
47396
+ path: file_path
47397
+ };
47398
+ }
47399
+ } catch {
47400
+ }
47401
+ }
45754
47402
  const content = await fs15.readFile(resolvedPath, "utf-8");
45755
47403
  const allLines = content.split("\n");
45756
47404
  const totalLines = allLines.length;
@@ -46072,7 +47720,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
46072
47720
  for (const entry of entries) {
46073
47721
  if (results.length >= config.maxResults) break;
46074
47722
  const fullPath = path2.join(dir, entry.name);
46075
- const relativePath = path2.relative(baseDir, fullPath);
47723
+ const relativePath = toForwardSlash(path2.relative(baseDir, fullPath));
46076
47724
  if (entry.isDirectory()) {
46077
47725
  const isBlocked = config.blockedDirectories.some(
46078
47726
  (blocked) => entry.name === blocked || relativePath.includes(`/${blocked}/`) || relativePath.startsWith(`${blocked}/`)
@@ -46435,7 +48083,7 @@ WHEN TO USE:
46435
48083
  );
46436
48084
  if (matches.length > 0) {
46437
48085
  filesMatched++;
46438
- const relativePath = path2.relative(resolvedPath, file) || file;
48086
+ const relativePath = toForwardSlash(path2.relative(resolvedPath, file)) || file;
46439
48087
  for (const match of matches) {
46440
48088
  match.file = relativePath;
46441
48089
  }
@@ -46501,7 +48149,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
46501
48149
  for (const entry of dirEntries) {
46502
48150
  if (entries.length >= config.maxResults) break;
46503
48151
  const fullPath = path2.join(dir, entry.name);
46504
- const relativePath = path2.relative(baseDir, fullPath);
48152
+ const relativePath = toForwardSlash(path2.relative(baseDir, fullPath));
46505
48153
  if (entry.isDirectory() && config.blockedDirectories.includes(entry.name)) {
46506
48154
  continue;
46507
48155
  }
@@ -46716,6 +48364,8 @@ function createBashTool(config = {}) {
46716
48364
  name: "bash",
46717
48365
  description: `Execute shell commands with optional timeout.
46718
48366
 
48367
+ SHELL: This tool uses ${mergedConfig.shell}${process.platform === "win32" ? " (Windows). Use Windows command syntax (dir, type, del, etc.), NOT Unix commands (ls, cat, rm, etc.). Use \\ as path separator or quote paths with forward slashes." : " (Unix). Use standard Unix command syntax."}
48368
+
46719
48369
  USAGE:
46720
48370
  - Execute any shell command
46721
48371
  - Working directory persists between commands
@@ -46732,9 +48382,11 @@ For file operations, prefer dedicated tools:
46732
48382
 
46733
48383
  BEST PRACTICES:
46734
48384
  - Always quote file paths with spaces: cd "/path with spaces"
46735
- - Use absolute paths when possible
48385
+ - Use absolute paths when possible${process.platform === "win32" ? `
46736
48386
  - Chain dependent commands with &&: git add . && git commit -m "msg"
46737
- - Use ; only when you don't care if earlier commands fail
48387
+ - Use PowerShell syntax if cmd.exe is insufficient` : `
48388
+ - Chain dependent commands with &&: git add . && git commit -m "msg"
48389
+ - Use ; only when you don't care if earlier commands fail`}
46738
48390
  - Avoid interactive commands (no -i flags)
46739
48391
 
46740
48392
  GIT SAFETY:
@@ -47451,92 +49103,30 @@ function detectContentQuality(html, text, $) {
47451
49103
  issues
47452
49104
  };
47453
49105
  }
47454
- var JSDOM = null;
47455
- async function getJSDOM() {
47456
- if (!JSDOM) {
47457
- const jsdom = await import('jsdom');
47458
- JSDOM = jsdom.JSDOM;
47459
- }
47460
- return JSDOM;
47461
- }
47462
- async function htmlToMarkdown(html, url2, maxLength = 5e4) {
47463
- const JSDOMClass = await getJSDOM();
47464
- const dom = new JSDOMClass(html, { url: url2 });
47465
- const document = dom.window.document;
47466
- let title = document.title || "";
47467
- let byline;
47468
- let excerpt;
47469
- let contentHtml = html;
47470
- let wasReadabilityUsed = false;
47471
- try {
47472
- const clonedDoc = document.cloneNode(true);
47473
- const reader = new readability.Readability(clonedDoc);
47474
- const article = reader.parse();
47475
- if (article && article.content && article.content.length > 100) {
47476
- contentHtml = article.content;
47477
- title = article.title || title;
47478
- byline = article.byline || void 0;
47479
- excerpt = article.excerpt || void 0;
47480
- wasReadabilityUsed = true;
47481
- }
47482
- } catch {
47483
- }
47484
- const turndown = new TurndownService__default.default({
47485
- headingStyle: "atx",
47486
- codeBlockStyle: "fenced",
47487
- bulletListMarker: "-",
47488
- emDelimiter: "_"
47489
- });
47490
- turndown.remove(["script", "style", "nav", "footer", "aside", "iframe", "noscript"]);
47491
- turndown.addRule("pre", {
47492
- filter: ["pre"],
47493
- replacement: (content, node) => {
47494
- const element = node;
47495
- const code = element.querySelector?.("code");
47496
- const lang = code?.className?.match(/language-(\w+)/)?.[1] || "";
47497
- const text = code?.textContent || content;
47498
- return `
47499
- \`\`\`${lang}
47500
- ${text}
47501
- \`\`\`
47502
- `;
47503
- }
47504
- });
47505
- let markdown = turndown.turndown(contentHtml);
47506
- markdown = markdown.replace(/\n{3,}/g, "\n\n").replace(/^\s+|\s+$/g, "").replace(/[ \t]+$/gm, "");
47507
- let wasTruncated = false;
47508
- if (markdown.length > maxLength) {
47509
- const truncateAt = markdown.lastIndexOf("\n\n", maxLength);
47510
- if (truncateAt > maxLength * 0.5) {
47511
- markdown = markdown.slice(0, truncateAt) + "\n\n...[content truncated]";
47512
- } else {
47513
- markdown = markdown.slice(0, maxLength) + "...[truncated]";
47514
- }
47515
- wasTruncated = true;
47516
- }
47517
- return {
47518
- markdown,
47519
- title,
47520
- byline,
47521
- excerpt,
47522
- wasReadabilityUsed,
47523
- wasTruncated
47524
- };
47525
- }
47526
49106
 
47527
49107
  // src/tools/web/webFetch.ts
49108
+ init_htmlToMarkdown();
47528
49109
  var webFetch = {
47529
49110
  definition: {
47530
49111
  type: "function",
47531
49112
  function: {
47532
49113
  name: "web_fetch",
47533
- description: `Fetch and extract text content from a web page URL.
49114
+ description: `Fetch and extract content from a URL \u2014 works with web pages AND document files (PDF, DOCX, XLSX, PPTX, etc.). Document URLs are automatically detected and converted to markdown text.
47534
49115
 
47535
- IMPORTANT: This tool performs a simple HTTP fetch and HTML parsing. It works well for:
49116
+ WEB PAGES:
49117
+ This tool performs HTTP fetch and HTML parsing. It works well for:
47536
49118
  - Static websites (blogs, documentation, articles)
47537
49119
  - Server-rendered HTML pages
47538
49120
  - Content that doesn't require JavaScript
47539
49121
 
49122
+ DOCUMENT URLs:
49123
+ When the URL points to a document file (detected via Content-Type header or URL extension), the document is automatically downloaded and converted to markdown:
49124
+ - PDF files: extracted as markdown with per-page sections
49125
+ - Word (.docx), PowerPoint (.pptx): converted to structured markdown
49126
+ - Excel (.xlsx), CSV, ODS: tables converted to markdown tables
49127
+ - OpenDocument formats (.odt, .odp, .ods): converted like MS Office equivalents
49128
+ - Returns contentType: "document" and includes documentMetadata in the result
49129
+
47540
49130
  LIMITATIONS:
47541
49131
  - Cannot execute JavaScript
47542
49132
  - May fail on React/Vue/Angular sites (will return low quality score)
@@ -47556,8 +49146,8 @@ RETURNS:
47556
49146
  success: boolean,
47557
49147
  url: string,
47558
49148
  title: string,
47559
- content: string, // Clean markdown (converted from HTML via Readability + Turndown)
47560
- contentType: string, // 'html' | 'json' | 'text' | 'error'
49149
+ content: string, // Clean markdown (converted from HTML or document)
49150
+ contentType: string, // 'html' | 'json' | 'text' | 'document' | 'error'
47561
49151
  qualityScore: number, // 0-100 (quality of extraction)
47562
49152
  requiresJS: boolean, // True if site likely needs JavaScript
47563
49153
  suggestedAction: string, // Suggestion if quality is low
@@ -47565,20 +49155,24 @@ RETURNS:
47565
49155
  excerpt: string, // Short summary excerpt (if extracted)
47566
49156
  byline: string, // Author info (if extracted)
47567
49157
  wasTruncated: boolean, // True if content was truncated
49158
+ documentMetadata: object, // Document metadata (format, pages, etc.) \u2014 only for document URLs
47568
49159
  error: string // Error message if failed
47569
49160
  }
47570
49161
 
47571
- EXAMPLE:
47572
- To fetch a blog post:
49162
+ EXAMPLES:
49163
+ Fetch a blog post:
47573
49164
  {
47574
49165
  url: "https://example.com/blog/article"
47575
49166
  }
47576
49167
 
47577
- With custom user agent:
49168
+ Fetch a PDF document:
49169
+ {
49170
+ url: "https://example.com/reports/q4-2025.pdf"
49171
+ }
49172
+
49173
+ Fetch an Excel spreadsheet:
47578
49174
  {
47579
- url: "https://example.com/page",
47580
- userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
47581
- timeout: 15000
49175
+ url: "https://example.com/data/metrics.xlsx"
47582
49176
  }`,
47583
49177
  parameters: {
47584
49178
  type: "object",
@@ -47642,6 +49236,51 @@ With custom user agent:
47642
49236
  };
47643
49237
  }
47644
49238
  const contentType = response.headers.get("content-type") || "";
49239
+ const urlExt = (() => {
49240
+ try {
49241
+ const pathname = new URL(args.url).pathname;
49242
+ const ext = pathname.split(".").pop()?.toLowerCase();
49243
+ return ext ? `.${ext}` : "";
49244
+ } catch {
49245
+ return "";
49246
+ }
49247
+ })();
49248
+ if (FormatDetector.isDocumentMimeType(contentType) || FormatDetector.isBinaryDocumentFormat(urlExt)) {
49249
+ try {
49250
+ const arrayBuffer = await response.arrayBuffer();
49251
+ const buffer = Buffer.from(arrayBuffer);
49252
+ const disposition = response.headers.get("content-disposition");
49253
+ let filename = "document";
49254
+ if (disposition) {
49255
+ const match = disposition.match(/filename[^;=\n]*=(['"]?)([^'"\n;]*)\1/);
49256
+ if (match?.[2]) filename = match[2];
49257
+ } else {
49258
+ try {
49259
+ const basename = new URL(args.url).pathname.split("/").pop();
49260
+ if (basename && basename.includes(".")) filename = basename;
49261
+ } catch {
49262
+ }
49263
+ }
49264
+ const reader = DocumentReader.create();
49265
+ const result = await reader.read(
49266
+ { type: "buffer", buffer, filename },
49267
+ { extractImages: false }
49268
+ );
49269
+ if (result.success) {
49270
+ return {
49271
+ success: true,
49272
+ url: args.url,
49273
+ title: `Document: ${filename}`,
49274
+ content: mergeTextPieces(result.pieces),
49275
+ contentType: "document",
49276
+ qualityScore: 100,
49277
+ requiresJS: false,
49278
+ documentMetadata: result.metadata
49279
+ };
49280
+ }
49281
+ } catch {
49282
+ }
49283
+ }
47645
49284
  if (contentType.includes("application/json")) {
47646
49285
  const json = await response.json();
47647
49286
  return {
@@ -47908,6 +49547,10 @@ For JS-heavy sites:
47908
49547
  const api = await tryAPI(args, startTime, attemptedMethods);
47909
49548
  if (api.success) return api;
47910
49549
  if (native.success) return native;
49550
+ const errors = [];
49551
+ if (native.error) errors.push(`native: ${native.error}`);
49552
+ if (api.error) errors.push(`api(${connector.name}): ${api.error}`);
49553
+ const detail = errors.length > 0 ? errors.join(" | ") : "Unknown failure";
47911
49554
  return {
47912
49555
  success: false,
47913
49556
  url: args.url,
@@ -47916,7 +49559,7 @@ For JS-heavy sites:
47916
49559
  content: "",
47917
49560
  durationMs: Date.now() - startTime,
47918
49561
  attemptedMethods,
47919
- error: "All scraping methods failed. Site may have bot protection."
49562
+ error: `All scraping methods failed. ${detail}`
47920
49563
  };
47921
49564
  },
47922
49565
  describeCall: (args) => args.url
@@ -49580,6 +51223,819 @@ function registerGitHubTools() {
49580
51223
  // src/tools/github/index.ts
49581
51224
  registerGitHubTools();
49582
51225
 
51226
+ // src/tools/desktop/types.ts
51227
+ var DEFAULT_DESKTOP_CONFIG = {
51228
+ driver: null,
51229
+ // Lazy-initialized
51230
+ humanDelay: [50, 150],
51231
+ humanizeMovement: false
51232
+ };
51233
+ async function applyHumanDelay(config) {
51234
+ const [min, max] = config.humanDelay ?? DEFAULT_DESKTOP_CONFIG.humanDelay;
51235
+ if (min === 0 && max === 0) return;
51236
+ const delay = min + Math.random() * (max - min);
51237
+ await new Promise((resolve4) => setTimeout(resolve4, delay));
51238
+ }
51239
+ var DESKTOP_TOOL_NAMES = [
51240
+ "desktop_screenshot",
51241
+ "desktop_mouse_move",
51242
+ "desktop_mouse_click",
51243
+ "desktop_mouse_drag",
51244
+ "desktop_mouse_scroll",
51245
+ "desktop_get_cursor",
51246
+ "desktop_keyboard_type",
51247
+ "desktop_keyboard_key",
51248
+ "desktop_get_screen_size",
51249
+ "desktop_window_list",
51250
+ "desktop_window_focus"
51251
+ ];
51252
+
51253
+ // src/tools/desktop/driver/NutTreeDriver.ts
51254
+ var KEY_MAP = {
51255
+ // Modifiers
51256
+ ctrl: "LeftControl",
51257
+ control: "LeftControl",
51258
+ cmd: "LeftCmd",
51259
+ command: "LeftCmd",
51260
+ meta: "LeftCmd",
51261
+ super: "LeftCmd",
51262
+ alt: "LeftAlt",
51263
+ option: "LeftAlt",
51264
+ shift: "LeftShift",
51265
+ // Navigation
51266
+ enter: "Return",
51267
+ return: "Return",
51268
+ tab: "Tab",
51269
+ escape: "Escape",
51270
+ esc: "Escape",
51271
+ backspace: "Backspace",
51272
+ delete: "Delete",
51273
+ space: "Space",
51274
+ // Arrow keys
51275
+ up: "Up",
51276
+ down: "Down",
51277
+ left: "Left",
51278
+ right: "Right",
51279
+ // Function keys
51280
+ f1: "F1",
51281
+ f2: "F2",
51282
+ f3: "F3",
51283
+ f4: "F4",
51284
+ f5: "F5",
51285
+ f6: "F6",
51286
+ f7: "F7",
51287
+ f8: "F8",
51288
+ f9: "F9",
51289
+ f10: "F10",
51290
+ f11: "F11",
51291
+ f12: "F12",
51292
+ // Other
51293
+ home: "Home",
51294
+ end: "End",
51295
+ pageup: "PageUp",
51296
+ pagedown: "PageDown",
51297
+ insert: "Insert",
51298
+ printscreen: "Print",
51299
+ capslock: "CapsLock",
51300
+ numlock: "NumLock",
51301
+ scrolllock: "ScrollLock"
51302
+ };
51303
+ function parseKeyCombo(keys, KeyEnum) {
51304
+ const parts = keys.toLowerCase().split("+").map((k) => k.trim());
51305
+ const result = [];
51306
+ for (const part of parts) {
51307
+ const mapped = KEY_MAP[part];
51308
+ if (mapped && KeyEnum[mapped] !== void 0) {
51309
+ result.push(KeyEnum[mapped]);
51310
+ continue;
51311
+ }
51312
+ if (part.length === 1) {
51313
+ const upper = part.toUpperCase();
51314
+ if (KeyEnum[upper] !== void 0) {
51315
+ result.push(KeyEnum[upper]);
51316
+ continue;
51317
+ }
51318
+ }
51319
+ const pascal = part.charAt(0).toUpperCase() + part.slice(1);
51320
+ if (KeyEnum[pascal] !== void 0) {
51321
+ result.push(KeyEnum[pascal]);
51322
+ continue;
51323
+ }
51324
+ if (KeyEnum[part] !== void 0) {
51325
+ result.push(KeyEnum[part]);
51326
+ continue;
51327
+ }
51328
+ throw new Error(`Unknown key: "${part}". Available modifiers: ctrl, cmd, alt, shift. Common keys: enter, tab, escape, space, up, down, left, right, f1-f12, a-z, 0-9`);
51329
+ }
51330
+ return result;
51331
+ }
51332
+ async function encodeRGBAToPNG(data, width, height) {
51333
+ const { PNG } = await import('pngjs');
51334
+ const png = new PNG({ width, height });
51335
+ const sourceBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
51336
+ sourceBuffer.copy(png.data, 0, 0, width * height * 4);
51337
+ return PNG.sync.write(png);
51338
+ }
51339
+ var NutTreeDriver = class {
51340
+ _isInitialized = false;
51341
+ _scaleFactor = 1;
51342
+ // Lazy-loaded nut-tree modules
51343
+ _nut = null;
51344
+ // Cache of Window objects keyed by windowHandle, populated by getWindowList()
51345
+ _windowCache = /* @__PURE__ */ new Map();
51346
+ get isInitialized() {
51347
+ return this._isInitialized;
51348
+ }
51349
+ get scaleFactor() {
51350
+ return this._scaleFactor;
51351
+ }
51352
+ async initialize() {
51353
+ if (this._isInitialized) return;
51354
+ try {
51355
+ this._nut = await import('@nut-tree-fork/nut-js');
51356
+ } catch {
51357
+ throw new Error(
51358
+ "@nut-tree-fork/nut-js is not installed. Install it to use desktop automation tools:\n npm install @nut-tree-fork/nut-js"
51359
+ );
51360
+ }
51361
+ try {
51362
+ const { mouse, keyboard } = this._nut;
51363
+ if (mouse.config) {
51364
+ mouse.config.mouseSpeed = 1e4;
51365
+ mouse.config.autoDelayMs = 0;
51366
+ }
51367
+ if (keyboard.config) {
51368
+ keyboard.config.autoDelayMs = 0;
51369
+ }
51370
+ } catch {
51371
+ }
51372
+ try {
51373
+ const { screen } = this._nut;
51374
+ const logicalWidth = await screen.width();
51375
+ const screenshotImage = await screen.grab();
51376
+ const physicalWidth = screenshotImage.width;
51377
+ this._scaleFactor = physicalWidth / logicalWidth;
51378
+ } catch (err) {
51379
+ if (err.message?.includes("permission") || err.message?.includes("accessibility")) {
51380
+ throw new Error(
51381
+ "Desktop automation requires accessibility permissions.\nOn macOS: System Settings \u2192 Privacy & Security \u2192 Accessibility \u2192 Enable your terminal app."
51382
+ );
51383
+ }
51384
+ this._scaleFactor = 1;
51385
+ }
51386
+ this._isInitialized = true;
51387
+ }
51388
+ assertInitialized() {
51389
+ if (!this._isInitialized) {
51390
+ throw new Error("NutTreeDriver not initialized. Call initialize() first.");
51391
+ }
51392
+ }
51393
+ /** Convert physical (screenshot) coords to logical (OS) coords */
51394
+ toLogical(x, y) {
51395
+ return {
51396
+ x: Math.round(x / this._scaleFactor),
51397
+ y: Math.round(y / this._scaleFactor)
51398
+ };
51399
+ }
51400
+ /** Convert logical (OS) coords to physical (screenshot) coords */
51401
+ toPhysical(x, y) {
51402
+ return {
51403
+ x: Math.round(x * this._scaleFactor),
51404
+ y: Math.round(y * this._scaleFactor)
51405
+ };
51406
+ }
51407
+ // ===== Screen =====
51408
+ async screenshot(region) {
51409
+ this.assertInitialized();
51410
+ const { screen } = this._nut;
51411
+ let image;
51412
+ if (region) {
51413
+ const { Region } = this._nut;
51414
+ const logTopLeft = this.toLogical(region.x, region.y);
51415
+ const logicalWidth = Math.round(region.width / this._scaleFactor);
51416
+ const logicalHeight = Math.round(region.height / this._scaleFactor);
51417
+ const nutRegion = new Region(logTopLeft.x, logTopLeft.y, logicalWidth, logicalHeight);
51418
+ image = await screen.grabRegion(nutRegion);
51419
+ } else {
51420
+ image = await screen.grab();
51421
+ }
51422
+ const pngBuffer = await encodeRGBAToPNG(image.data, image.width, image.height);
51423
+ const base64 = pngBuffer.toString("base64");
51424
+ return {
51425
+ base64,
51426
+ width: image.width,
51427
+ height: image.height
51428
+ };
51429
+ }
51430
+ async getScreenSize() {
51431
+ this.assertInitialized();
51432
+ const { screen } = this._nut;
51433
+ const logicalWidth = await screen.width();
51434
+ const logicalHeight = await screen.height();
51435
+ return {
51436
+ physicalWidth: Math.round(logicalWidth * this._scaleFactor),
51437
+ physicalHeight: Math.round(logicalHeight * this._scaleFactor),
51438
+ logicalWidth,
51439
+ logicalHeight,
51440
+ scaleFactor: this._scaleFactor
51441
+ };
51442
+ }
51443
+ // ===== Mouse =====
51444
+ async mouseMove(x, y) {
51445
+ this.assertInitialized();
51446
+ const { mouse, straightTo, Point } = this._nut;
51447
+ const logical = this.toLogical(x, y);
51448
+ await mouse.move(straightTo(new Point(logical.x, logical.y)));
51449
+ }
51450
+ async mouseClick(x, y, button, clickCount) {
51451
+ this.assertInitialized();
51452
+ const { mouse, straightTo, Point, Button } = this._nut;
51453
+ const nutButton = button === "right" ? Button.RIGHT : button === "middle" ? Button.MIDDLE : Button.LEFT;
51454
+ const logical = this.toLogical(x, y);
51455
+ await mouse.move(straightTo(new Point(logical.x, logical.y)));
51456
+ for (let i = 0; i < clickCount; i++) {
51457
+ await mouse.click(nutButton);
51458
+ }
51459
+ }
51460
+ async mouseDrag(startX, startY, endX, endY, button) {
51461
+ this.assertInitialized();
51462
+ const { mouse, straightTo, Point, Button } = this._nut;
51463
+ const nutButton = button === "right" ? Button.RIGHT : button === "middle" ? Button.MIDDLE : Button.LEFT;
51464
+ const logicalStart = this.toLogical(startX, startY);
51465
+ const logicalEnd = this.toLogical(endX, endY);
51466
+ await mouse.move(straightTo(new Point(logicalStart.x, logicalStart.y)));
51467
+ await mouse.pressButton(nutButton);
51468
+ await mouse.move(straightTo(new Point(logicalEnd.x, logicalEnd.y)));
51469
+ await mouse.releaseButton(nutButton);
51470
+ }
51471
+ async mouseScroll(deltaX, deltaY, x, y) {
51472
+ this.assertInitialized();
51473
+ const { mouse, straightTo, Point } = this._nut;
51474
+ if (x !== void 0 && y !== void 0) {
51475
+ const logical = this.toLogical(x, y);
51476
+ await mouse.move(straightTo(new Point(logical.x, logical.y)));
51477
+ }
51478
+ if (deltaY !== 0) {
51479
+ if (deltaY > 0) {
51480
+ await mouse.scrollDown(Math.abs(deltaY));
51481
+ } else {
51482
+ await mouse.scrollUp(Math.abs(deltaY));
51483
+ }
51484
+ }
51485
+ if (deltaX !== 0) {
51486
+ if (deltaX > 0) {
51487
+ await mouse.scrollRight(Math.abs(deltaX));
51488
+ } else {
51489
+ await mouse.scrollLeft(Math.abs(deltaX));
51490
+ }
51491
+ }
51492
+ }
51493
+ async getCursorPosition() {
51494
+ this.assertInitialized();
51495
+ const { mouse } = this._nut;
51496
+ const pos = await mouse.getPosition();
51497
+ return this.toPhysical(pos.x, pos.y);
51498
+ }
51499
+ // ===== Keyboard =====
51500
+ async keyboardType(text, delay) {
51501
+ this.assertInitialized();
51502
+ const { keyboard } = this._nut;
51503
+ const prevDelay = keyboard.config.autoDelayMs;
51504
+ if (delay !== void 0) {
51505
+ keyboard.config.autoDelayMs = delay;
51506
+ }
51507
+ try {
51508
+ await keyboard.type(text);
51509
+ } finally {
51510
+ if (delay !== void 0) {
51511
+ keyboard.config.autoDelayMs = prevDelay;
51512
+ }
51513
+ }
51514
+ }
51515
+ async keyboardKey(keys) {
51516
+ this.assertInitialized();
51517
+ const { keyboard, Key } = this._nut;
51518
+ const parsedKeys = parseKeyCombo(keys, Key);
51519
+ if (parsedKeys.length === 1) {
51520
+ await keyboard.pressKey(parsedKeys[0]);
51521
+ await keyboard.releaseKey(parsedKeys[0]);
51522
+ } else {
51523
+ for (const key of parsedKeys) {
51524
+ await keyboard.pressKey(key);
51525
+ }
51526
+ for (const key of [...parsedKeys].reverse()) {
51527
+ await keyboard.releaseKey(key);
51528
+ }
51529
+ }
51530
+ }
51531
+ // ===== Windows =====
51532
+ async getWindowList() {
51533
+ this.assertInitialized();
51534
+ const { getWindows } = this._nut;
51535
+ try {
51536
+ const windows = await getWindows();
51537
+ const result = [];
51538
+ this._windowCache.clear();
51539
+ for (const win of windows) {
51540
+ try {
51541
+ const handle = win.windowHandle;
51542
+ if (handle === void 0 || handle === null) continue;
51543
+ const title = await win.title;
51544
+ const region = await win.region;
51545
+ this._windowCache.set(handle, win);
51546
+ result.push({
51547
+ id: handle,
51548
+ title: title || "",
51549
+ bounds: region ? {
51550
+ x: Math.round(region.left * this._scaleFactor),
51551
+ y: Math.round(region.top * this._scaleFactor),
51552
+ width: Math.round(region.width * this._scaleFactor),
51553
+ height: Math.round(region.height * this._scaleFactor)
51554
+ } : void 0
51555
+ });
51556
+ } catch {
51557
+ }
51558
+ }
51559
+ return result;
51560
+ } catch {
51561
+ return [];
51562
+ }
51563
+ }
51564
+ async focusWindow(windowId) {
51565
+ this.assertInitialized();
51566
+ let target = this._windowCache.get(windowId);
51567
+ if (!target) {
51568
+ const { getWindows } = this._nut;
51569
+ const windows = await getWindows();
51570
+ target = windows.find((w) => w.windowHandle === windowId);
51571
+ }
51572
+ if (!target) {
51573
+ throw new Error(`Window with ID ${windowId} not found. Call desktop_window_list first to get current window IDs.`);
51574
+ }
51575
+ await target.focus();
51576
+ }
51577
+ };
51578
+
51579
+ // src/tools/desktop/getDriver.ts
51580
+ var defaultDriver = null;
51581
+ async function getDesktopDriver(config) {
51582
+ if (config?.driver) {
51583
+ if (!config.driver.isInitialized) {
51584
+ await config.driver.initialize();
51585
+ }
51586
+ return config.driver;
51587
+ }
51588
+ if (!defaultDriver) {
51589
+ defaultDriver = new NutTreeDriver();
51590
+ }
51591
+ if (!defaultDriver.isInitialized) {
51592
+ await defaultDriver.initialize();
51593
+ }
51594
+ return defaultDriver;
51595
+ }
51596
+ function resetDefaultDriver() {
51597
+ defaultDriver = null;
51598
+ }
51599
+
51600
+ // src/tools/desktop/screenshot.ts
51601
+ function createDesktopScreenshotTool(config) {
51602
+ return {
51603
+ definition: {
51604
+ type: "function",
51605
+ function: {
51606
+ name: "desktop_screenshot",
51607
+ description: `Take a screenshot of the entire screen or a specific region. Returns the screenshot image for visual analysis. Use this to see what's on screen before performing actions. IMPORTANT: If you capture a region, element positions in the image are relative to the region's top-left corner. To click an element at image position (ix, iy), you must use screen coordinates (ix + region.x, iy + region.y). Prefer full-screen screenshots to avoid coordinate offset errors.`,
51608
+ parameters: {
51609
+ type: "object",
51610
+ properties: {
51611
+ region: {
51612
+ type: "object",
51613
+ description: "Optional region to capture (in physical pixel coordinates). Omit to capture full screen.",
51614
+ properties: {
51615
+ x: { type: "number", description: "Left edge X coordinate" },
51616
+ y: { type: "number", description: "Top edge Y coordinate" },
51617
+ width: { type: "number", description: "Width in pixels" },
51618
+ height: { type: "number", description: "Height in pixels" }
51619
+ },
51620
+ required: ["x", "y", "width", "height"]
51621
+ }
51622
+ },
51623
+ required: []
51624
+ }
51625
+ }
51626
+ },
51627
+ describeCall: (args) => {
51628
+ if (args.region) {
51629
+ return `region (${args.region.x},${args.region.y}) ${args.region.width}x${args.region.height}`;
51630
+ }
51631
+ return "full screen";
51632
+ },
51633
+ execute: async (args) => {
51634
+ try {
51635
+ const driver = await getDesktopDriver(config);
51636
+ const screenshot = await driver.screenshot(args.region);
51637
+ return {
51638
+ success: true,
51639
+ width: screenshot.width,
51640
+ height: screenshot.height,
51641
+ base64: screenshot.base64,
51642
+ __images: [{ base64: screenshot.base64, mediaType: "image/png" }],
51643
+ // Include region info so the agent can compute screen coordinates:
51644
+ // screen_x = image_x + regionOffsetX, screen_y = image_y + regionOffsetY
51645
+ ...args.region ? { regionOffsetX: args.region.x, regionOffsetY: args.region.y } : {}
51646
+ };
51647
+ } catch (err) {
51648
+ return { success: false, error: err.message };
51649
+ }
51650
+ }
51651
+ };
51652
+ }
51653
+ var desktopScreenshot = createDesktopScreenshotTool();
51654
+
51655
+ // src/tools/desktop/mouseMove.ts
51656
+ function createDesktopMouseMoveTool(config) {
51657
+ return {
51658
+ definition: {
51659
+ type: "function",
51660
+ function: {
51661
+ name: "desktop_mouse_move",
51662
+ description: `Move the mouse cursor to the specified (x, y) position. Coordinates are in screenshot pixel space (full screen). Returns the actual cursor position after the move for verification.`,
51663
+ parameters: {
51664
+ type: "object",
51665
+ properties: {
51666
+ x: { type: "number", description: "X coordinate (in screenshot pixels)" },
51667
+ y: { type: "number", description: "Y coordinate (in screenshot pixels)" }
51668
+ },
51669
+ required: ["x", "y"]
51670
+ }
51671
+ }
51672
+ },
51673
+ describeCall: (args) => `(${args.x}, ${args.y})`,
51674
+ execute: async (args) => {
51675
+ try {
51676
+ const driver = await getDesktopDriver(config);
51677
+ await driver.mouseMove(args.x, args.y);
51678
+ await applyHumanDelay(config ?? {});
51679
+ const actual = await driver.getCursorPosition();
51680
+ return { success: true, x: actual.x, y: actual.y };
51681
+ } catch (err) {
51682
+ return { success: false, error: err.message };
51683
+ }
51684
+ }
51685
+ };
51686
+ }
51687
+ var desktopMouseMove = createDesktopMouseMoveTool();
51688
+
51689
+ // src/tools/desktop/mouseClick.ts
51690
+ function createDesktopMouseClickTool(config) {
51691
+ return {
51692
+ definition: {
51693
+ type: "function",
51694
+ function: {
51695
+ name: "desktop_mouse_click",
51696
+ description: `Click the mouse at the specified position or at the current cursor position. Supports left/right/middle button and single/double/triple click.`,
51697
+ parameters: {
51698
+ type: "object",
51699
+ properties: {
51700
+ x: { type: "number", description: "X coordinate to click (in screenshot pixels). Omit to click at current position." },
51701
+ y: { type: "number", description: "Y coordinate to click (in screenshot pixels). Omit to click at current position." },
51702
+ button: {
51703
+ type: "string",
51704
+ enum: ["left", "right", "middle"],
51705
+ description: 'Mouse button to click. Default: "left"'
51706
+ },
51707
+ clickCount: {
51708
+ type: "number",
51709
+ description: "Number of clicks (1=single, 2=double, 3=triple). Default: 1"
51710
+ }
51711
+ },
51712
+ required: []
51713
+ }
51714
+ }
51715
+ },
51716
+ describeCall: (args) => {
51717
+ const pos = args.x !== void 0 ? `(${args.x}, ${args.y})` : "current position";
51718
+ const btn = args.button && args.button !== "left" ? ` ${args.button}` : "";
51719
+ const count = args.clickCount && args.clickCount > 1 ? ` x${args.clickCount}` : "";
51720
+ return `${pos}${btn}${count}`;
51721
+ },
51722
+ execute: async (args) => {
51723
+ try {
51724
+ const driver = await getDesktopDriver(config);
51725
+ const button = args.button ?? "left";
51726
+ const clickCount = args.clickCount ?? 1;
51727
+ if (args.x !== void 0 && args.y !== void 0) {
51728
+ await driver.mouseClick(args.x, args.y, button, clickCount);
51729
+ } else {
51730
+ const pos = await driver.getCursorPosition();
51731
+ await driver.mouseClick(pos.x, pos.y, button, clickCount);
51732
+ }
51733
+ await applyHumanDelay(config ?? {});
51734
+ const actual = await driver.getCursorPosition();
51735
+ return { success: true, x: actual.x, y: actual.y, button, clickCount };
51736
+ } catch (err) {
51737
+ return { success: false, error: err.message };
51738
+ }
51739
+ }
51740
+ };
51741
+ }
51742
+ var desktopMouseClick = createDesktopMouseClickTool();
51743
+
51744
+ // src/tools/desktop/mouseDrag.ts
51745
+ function createDesktopMouseDragTool(config) {
51746
+ return {
51747
+ definition: {
51748
+ type: "function",
51749
+ function: {
51750
+ name: "desktop_mouse_drag",
51751
+ description: `Drag the mouse from one position to another. Presses the button at the start position, moves to the end position, then releases.`,
51752
+ parameters: {
51753
+ type: "object",
51754
+ properties: {
51755
+ startX: { type: "number", description: "Start X coordinate (in screenshot pixels)" },
51756
+ startY: { type: "number", description: "Start Y coordinate (in screenshot pixels)" },
51757
+ endX: { type: "number", description: "End X coordinate (in screenshot pixels)" },
51758
+ endY: { type: "number", description: "End Y coordinate (in screenshot pixels)" },
51759
+ button: {
51760
+ type: "string",
51761
+ enum: ["left", "right", "middle"],
51762
+ description: 'Mouse button to use for dragging. Default: "left"'
51763
+ }
51764
+ },
51765
+ required: ["startX", "startY", "endX", "endY"]
51766
+ }
51767
+ }
51768
+ },
51769
+ describeCall: (args) => `(${args.startX},${args.startY}) \u2192 (${args.endX},${args.endY})`,
51770
+ execute: async (args) => {
51771
+ try {
51772
+ const driver = await getDesktopDriver(config);
51773
+ await driver.mouseDrag(args.startX, args.startY, args.endX, args.endY, args.button ?? "left");
51774
+ await applyHumanDelay(config ?? {});
51775
+ return { success: true };
51776
+ } catch (err) {
51777
+ return { success: false, error: err.message };
51778
+ }
51779
+ }
51780
+ };
51781
+ }
51782
+ var desktopMouseDrag = createDesktopMouseDragTool();
51783
+
51784
+ // src/tools/desktop/mouseScroll.ts
51785
+ function createDesktopMouseScrollTool(config) {
51786
+ return {
51787
+ definition: {
51788
+ type: "function",
51789
+ function: {
51790
+ name: "desktop_mouse_scroll",
51791
+ description: `Scroll the mouse wheel. Positive deltaY scrolls down, negative scrolls up. Positive deltaX scrolls right, negative scrolls left. Optionally specify position to scroll at.`,
51792
+ parameters: {
51793
+ type: "object",
51794
+ properties: {
51795
+ deltaX: { type: "number", description: "Horizontal scroll amount. Positive=right, negative=left. Default: 0" },
51796
+ deltaY: { type: "number", description: "Vertical scroll amount. Positive=down, negative=up. Default: 0" },
51797
+ x: { type: "number", description: "X coordinate to scroll at (in screenshot pixels). Omit to scroll at current position." },
51798
+ y: { type: "number", description: "Y coordinate to scroll at (in screenshot pixels). Omit to scroll at current position." }
51799
+ },
51800
+ required: []
51801
+ }
51802
+ }
51803
+ },
51804
+ describeCall: (args) => {
51805
+ const parts = [];
51806
+ if (args.deltaY) parts.push(args.deltaY > 0 ? `down ${args.deltaY}` : `up ${Math.abs(args.deltaY)}`);
51807
+ if (args.deltaX) parts.push(args.deltaX > 0 ? `right ${args.deltaX}` : `left ${Math.abs(args.deltaX)}`);
51808
+ if (args.x !== void 0) parts.push(`at (${args.x},${args.y})`);
51809
+ return parts.join(", ") || "no-op";
51810
+ },
51811
+ execute: async (args) => {
51812
+ try {
51813
+ const driver = await getDesktopDriver(config);
51814
+ await driver.mouseScroll(args.deltaX ?? 0, args.deltaY ?? 0, args.x, args.y);
51815
+ await applyHumanDelay(config ?? {});
51816
+ return { success: true };
51817
+ } catch (err) {
51818
+ return { success: false, error: err.message };
51819
+ }
51820
+ }
51821
+ };
51822
+ }
51823
+ var desktopMouseScroll = createDesktopMouseScrollTool();
51824
+
51825
+ // src/tools/desktop/getCursor.ts
51826
+ function createDesktopGetCursorTool(config) {
51827
+ return {
51828
+ definition: {
51829
+ type: "function",
51830
+ function: {
51831
+ name: "desktop_get_cursor",
51832
+ description: `Get the current mouse cursor position in screenshot pixel coordinates.`,
51833
+ parameters: {
51834
+ type: "object",
51835
+ properties: {},
51836
+ required: []
51837
+ }
51838
+ }
51839
+ },
51840
+ describeCall: () => "get cursor position",
51841
+ execute: async () => {
51842
+ try {
51843
+ const driver = await getDesktopDriver(config);
51844
+ const pos = await driver.getCursorPosition();
51845
+ return { success: true, x: pos.x, y: pos.y };
51846
+ } catch (err) {
51847
+ return { success: false, error: err.message };
51848
+ }
51849
+ }
51850
+ };
51851
+ }
51852
+ var desktopGetCursor = createDesktopGetCursorTool();
51853
+
51854
+ // src/tools/desktop/keyboardType.ts
51855
+ function createDesktopKeyboardTypeTool(config) {
51856
+ return {
51857
+ definition: {
51858
+ type: "function",
51859
+ function: {
51860
+ name: "desktop_keyboard_type",
51861
+ description: `Type text using the keyboard. Each character is typed as a keypress. Use this for entering text into focused input fields.`,
51862
+ parameters: {
51863
+ type: "object",
51864
+ properties: {
51865
+ text: { type: "string", description: "The text to type" },
51866
+ delay: { type: "number", description: "Delay in ms between each keystroke. Default: uses system default." }
51867
+ },
51868
+ required: ["text"]
51869
+ }
51870
+ }
51871
+ },
51872
+ describeCall: (args) => {
51873
+ const preview = args.text.length > 30 ? args.text.slice(0, 27) + "..." : args.text;
51874
+ return `"${preview}"`;
51875
+ },
51876
+ execute: async (args) => {
51877
+ try {
51878
+ const driver = await getDesktopDriver(config);
51879
+ await driver.keyboardType(args.text, args.delay);
51880
+ await applyHumanDelay(config ?? {});
51881
+ return { success: true };
51882
+ } catch (err) {
51883
+ return { success: false, error: err.message };
51884
+ }
51885
+ }
51886
+ };
51887
+ }
51888
+ var desktopKeyboardType = createDesktopKeyboardTypeTool();
51889
+
51890
+ // src/tools/desktop/keyboardKey.ts
51891
+ function createDesktopKeyboardKeyTool(config) {
51892
+ return {
51893
+ definition: {
51894
+ type: "function",
51895
+ function: {
51896
+ name: "desktop_keyboard_key",
51897
+ description: `Press a keyboard shortcut or special key. Use "+" to combine keys (e.g., "ctrl+c", "cmd+shift+s", "enter", "tab", "escape"). Modifiers: ctrl, cmd/command, alt/option, shift. Special keys: enter, tab, escape, backspace, delete, space, up, down, left, right, f1-f12, home, end, pageup, pagedown.`,
51898
+ parameters: {
51899
+ type: "object",
51900
+ properties: {
51901
+ keys: {
51902
+ type: "string",
51903
+ description: 'Key combo string (e.g., "ctrl+c", "enter", "cmd+shift+s")'
51904
+ }
51905
+ },
51906
+ required: ["keys"]
51907
+ }
51908
+ }
51909
+ },
51910
+ describeCall: (args) => args.keys,
51911
+ execute: async (args) => {
51912
+ try {
51913
+ const driver = await getDesktopDriver(config);
51914
+ await driver.keyboardKey(args.keys);
51915
+ await applyHumanDelay(config ?? {});
51916
+ return { success: true };
51917
+ } catch (err) {
51918
+ return { success: false, error: err.message };
51919
+ }
51920
+ }
51921
+ };
51922
+ }
51923
+ var desktopKeyboardKey = createDesktopKeyboardKeyTool();
51924
+
51925
+ // src/tools/desktop/getScreenSize.ts
51926
+ function createDesktopGetScreenSizeTool(config) {
51927
+ return {
51928
+ definition: {
51929
+ type: "function",
51930
+ function: {
51931
+ name: "desktop_get_screen_size",
51932
+ description: `Get the screen dimensions. Returns physical pixel size (screenshot space), logical size (OS coordinates), and the scale factor (e.g., 2.0 on Retina displays). All desktop tool coordinates use physical pixel space.`,
51933
+ parameters: {
51934
+ type: "object",
51935
+ properties: {},
51936
+ required: []
51937
+ }
51938
+ }
51939
+ },
51940
+ describeCall: () => "get screen size",
51941
+ execute: async () => {
51942
+ try {
51943
+ const driver = await getDesktopDriver(config);
51944
+ const size = await driver.getScreenSize();
51945
+ return {
51946
+ success: true,
51947
+ physicalWidth: size.physicalWidth,
51948
+ physicalHeight: size.physicalHeight,
51949
+ logicalWidth: size.logicalWidth,
51950
+ logicalHeight: size.logicalHeight,
51951
+ scaleFactor: size.scaleFactor
51952
+ };
51953
+ } catch (err) {
51954
+ return { success: false, error: err.message };
51955
+ }
51956
+ }
51957
+ };
51958
+ }
51959
+ var desktopGetScreenSize = createDesktopGetScreenSizeTool();
51960
+
51961
+ // src/tools/desktop/windowList.ts
51962
+ function createDesktopWindowListTool(config) {
51963
+ return {
51964
+ definition: {
51965
+ type: "function",
51966
+ function: {
51967
+ name: "desktop_window_list",
51968
+ description: `List all visible windows on the desktop. Returns window IDs, titles, application names, and bounds. Use the window ID with desktop_window_focus to bring a window to the foreground.`,
51969
+ parameters: {
51970
+ type: "object",
51971
+ properties: {},
51972
+ required: []
51973
+ }
51974
+ }
51975
+ },
51976
+ describeCall: () => "list windows",
51977
+ execute: async () => {
51978
+ try {
51979
+ const driver = await getDesktopDriver(config);
51980
+ const windows = await driver.getWindowList();
51981
+ return { success: true, windows };
51982
+ } catch (err) {
51983
+ return { success: false, error: err.message };
51984
+ }
51985
+ }
51986
+ };
51987
+ }
51988
+ var desktopWindowList = createDesktopWindowListTool();
51989
+
51990
+ // src/tools/desktop/windowFocus.ts
51991
+ function createDesktopWindowFocusTool(config) {
51992
+ return {
51993
+ definition: {
51994
+ type: "function",
51995
+ function: {
51996
+ name: "desktop_window_focus",
51997
+ description: `Focus (bring to front) a window by its ID. Use desktop_window_list to get available window IDs.`,
51998
+ parameters: {
51999
+ type: "object",
52000
+ properties: {
52001
+ windowId: {
52002
+ type: "number",
52003
+ description: "The window ID from desktop_window_list"
52004
+ }
52005
+ },
52006
+ required: ["windowId"]
52007
+ }
52008
+ }
52009
+ },
52010
+ describeCall: (args) => `window ${args.windowId}`,
52011
+ execute: async (args) => {
52012
+ try {
52013
+ const driver = await getDesktopDriver(config);
52014
+ await driver.focusWindow(args.windowId);
52015
+ return { success: true };
52016
+ } catch (err) {
52017
+ return { success: false, error: err.message };
52018
+ }
52019
+ }
52020
+ };
52021
+ }
52022
+ var desktopWindowFocus = createDesktopWindowFocusTool();
52023
+
52024
+ // src/tools/desktop/index.ts
52025
+ var desktopTools = [
52026
+ desktopScreenshot,
52027
+ desktopMouseMove,
52028
+ desktopMouseClick,
52029
+ desktopMouseDrag,
52030
+ desktopMouseScroll,
52031
+ desktopGetCursor,
52032
+ desktopKeyboardType,
52033
+ desktopKeyboardKey,
52034
+ desktopGetScreenSize,
52035
+ desktopWindowList,
52036
+ desktopWindowFocus
52037
+ ];
52038
+
49583
52039
  // src/tools/registry.generated.ts
49584
52040
  var toolRegistry = [
49585
52041
  {
@@ -49591,6 +52047,105 @@ var toolRegistry = [
49591
52047
  tool: executeJavaScript,
49592
52048
  safeByDefault: false
49593
52049
  },
52050
+ {
52051
+ name: "desktop_get_cursor",
52052
+ exportName: "desktopGetCursor",
52053
+ displayName: "Desktop Get Cursor",
52054
+ category: "desktop",
52055
+ description: "Get the current mouse cursor position in screenshot pixel coordinates.",
52056
+ tool: desktopGetCursor,
52057
+ safeByDefault: false
52058
+ },
52059
+ {
52060
+ name: "desktop_get_screen_size",
52061
+ exportName: "desktopGetScreenSize",
52062
+ displayName: "Desktop Get Screen Size",
52063
+ category: "desktop",
52064
+ description: "Get the screen dimensions. Returns physical pixel size (screenshot space), logical size (OS coordinates), and the scale factor (e.g., 2.0 on Retina displays). All desktop tool coordinates use physical",
52065
+ tool: desktopGetScreenSize,
52066
+ safeByDefault: false
52067
+ },
52068
+ {
52069
+ name: "desktop_keyboard_key",
52070
+ exportName: "desktopKeyboardKey",
52071
+ displayName: "Desktop Keyboard Key",
52072
+ category: "desktop",
52073
+ description: 'Press a keyboard shortcut or special key. Use "+" to combine keys (e.g., "ctrl+c", "cmd+shift+s", "enter", "tab", "escape"). Modifiers: ctrl, cmd/command, alt/option, shift. Special keys: enter, tab, ',
52074
+ tool: desktopKeyboardKey,
52075
+ safeByDefault: false
52076
+ },
52077
+ {
52078
+ name: "desktop_keyboard_type",
52079
+ exportName: "desktopKeyboardType",
52080
+ displayName: "Desktop Keyboard Type",
52081
+ category: "desktop",
52082
+ description: "Type text using the keyboard. Each character is typed as a keypress. Use this for entering text into focused input fields.",
52083
+ tool: desktopKeyboardType,
52084
+ safeByDefault: false
52085
+ },
52086
+ {
52087
+ name: "desktop_mouse_click",
52088
+ exportName: "desktopMouseClick",
52089
+ displayName: "Desktop Mouse Click",
52090
+ category: "desktop",
52091
+ description: "Click the mouse at the specified position or at the current cursor position. Supports left/right/middle button and single/double/triple click.",
52092
+ tool: desktopMouseClick,
52093
+ safeByDefault: false
52094
+ },
52095
+ {
52096
+ name: "desktop_mouse_drag",
52097
+ exportName: "desktopMouseDrag",
52098
+ displayName: "Desktop Mouse Drag",
52099
+ category: "desktop",
52100
+ description: "Drag the mouse from one position to another. Presses the button at the start position, moves to the end position, then releases.",
52101
+ tool: desktopMouseDrag,
52102
+ safeByDefault: false
52103
+ },
52104
+ {
52105
+ name: "desktop_mouse_move",
52106
+ exportName: "desktopMouseMove",
52107
+ displayName: "Desktop Mouse Move",
52108
+ category: "desktop",
52109
+ description: "Move the mouse cursor to the specified (x, y) position. Coordinates are in screenshot pixel space (full screen). Returns the actual cursor position after the move for verification.",
52110
+ tool: desktopMouseMove,
52111
+ safeByDefault: false
52112
+ },
52113
+ {
52114
+ name: "desktop_mouse_scroll",
52115
+ exportName: "desktopMouseScroll",
52116
+ displayName: "Desktop Mouse Scroll",
52117
+ category: "desktop",
52118
+ description: "Scroll the mouse wheel. Positive deltaY scrolls down, negative scrolls up. Positive deltaX scrolls right, negative scrolls left. Optionally specify position to scroll at.",
52119
+ tool: desktopMouseScroll,
52120
+ safeByDefault: false
52121
+ },
52122
+ {
52123
+ name: "desktop_screenshot",
52124
+ exportName: "desktopScreenshot",
52125
+ displayName: "Desktop Screenshot",
52126
+ category: "desktop",
52127
+ description: "Take a screenshot of the entire screen or a specific region. Returns the screenshot image for visual analysis. Use this to see what's on screen before performing actions. IMPORTANT: If you capture a r",
52128
+ tool: desktopScreenshot,
52129
+ safeByDefault: false
52130
+ },
52131
+ {
52132
+ name: "desktop_window_focus",
52133
+ exportName: "desktopWindowFocus",
52134
+ displayName: "Desktop Window Focus",
52135
+ category: "desktop",
52136
+ description: "Focus (bring to front) a window by its ID. Use desktop_window_list to get available window IDs.",
52137
+ tool: desktopWindowFocus,
52138
+ safeByDefault: false
52139
+ },
52140
+ {
52141
+ name: "desktop_window_list",
52142
+ exportName: "desktopWindowList",
52143
+ displayName: "Desktop Window List",
52144
+ category: "desktop",
52145
+ description: "List all visible windows on the desktop. Returns window IDs, titles, application names, and bounds. Use the window ID with desktop_window_focus to bring a window to the foreground.",
52146
+ tool: desktopWindowList,
52147
+ safeByDefault: false
52148
+ },
49594
52149
  {
49595
52150
  name: "edit_file",
49596
52151
  exportName: "editFile",
@@ -49632,7 +52187,7 @@ var toolRegistry = [
49632
52187
  exportName: "readFile",
49633
52188
  displayName: "Read File",
49634
52189
  category: "filesystem",
49635
- description: "Read content from a file on the local filesystem.",
52190
+ description: "Read content from a file on the local filesystem. Supports text files AND binary document formats \u2014 PDF, DOCX, PPTX, XLSX, ODS, ODT, ODP, and images (PNG, JPG, GIF, WEBP) are automatically converted t",
49636
52191
  tool: readFile5,
49637
52192
  safeByDefault: true
49638
52193
  },
@@ -49668,7 +52223,7 @@ var toolRegistry = [
49668
52223
  exportName: "webFetch",
49669
52224
  displayName: "Web Fetch",
49670
52225
  category: "web",
49671
- description: "Fetch and extract text content from a web page URL.",
52226
+ description: "Fetch and extract content from a URL \u2014 works with web pages AND document files (PDF, DOCX, XLSX, PPTX, etc.). Document URLs are automatically detected and converted to markdown text.",
49672
52227
  tool: webFetch,
49673
52228
  safeByDefault: true
49674
52229
  }
@@ -50050,6 +52605,7 @@ exports.DEFAULT_ALLOWLIST = DEFAULT_ALLOWLIST;
50050
52605
  exports.DEFAULT_CHECKPOINT_STRATEGY = DEFAULT_CHECKPOINT_STRATEGY;
50051
52606
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG2;
50052
52607
  exports.DEFAULT_CONTEXT_CONFIG = DEFAULT_CONTEXT_CONFIG;
52608
+ exports.DEFAULT_DESKTOP_CONFIG = DEFAULT_DESKTOP_CONFIG;
50053
52609
  exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
50054
52610
  exports.DEFAULT_FILESYSTEM_CONFIG = DEFAULT_FILESYSTEM_CONFIG;
50055
52611
  exports.DEFAULT_HISTORY_MANAGER_CONFIG = DEFAULT_HISTORY_MANAGER_CONFIG;
@@ -50057,8 +52613,10 @@ exports.DEFAULT_MEMORY_CONFIG = DEFAULT_MEMORY_CONFIG;
50057
52613
  exports.DEFAULT_PERMISSION_CONFIG = DEFAULT_PERMISSION_CONFIG;
50058
52614
  exports.DEFAULT_RATE_LIMITER_CONFIG = DEFAULT_RATE_LIMITER_CONFIG;
50059
52615
  exports.DEFAULT_SHELL_CONFIG = DEFAULT_SHELL_CONFIG;
52616
+ exports.DESKTOP_TOOL_NAMES = DESKTOP_TOOL_NAMES;
50060
52617
  exports.DefaultCompactionStrategy = DefaultCompactionStrategy;
50061
52618
  exports.DependencyCycleError = DependencyCycleError;
52619
+ exports.DocumentReader = DocumentReader;
50062
52620
  exports.ErrorHandler = ErrorHandler;
50063
52621
  exports.ExecutionContext = ExecutionContext;
50064
52622
  exports.ExternalDependencyHandler = ExternalDependencyHandler;
@@ -50069,6 +52627,7 @@ exports.FileMediaOutputHandler = FileMediaStorage;
50069
52627
  exports.FileMediaStorage = FileMediaStorage;
50070
52628
  exports.FilePersistentInstructionsStorage = FilePersistentInstructionsStorage;
50071
52629
  exports.FileStorage = FileStorage;
52630
+ exports.FormatDetector = FormatDetector;
50072
52631
  exports.HookManager = HookManager;
50073
52632
  exports.IMAGE_MODELS = IMAGE_MODELS;
50074
52633
  exports.IMAGE_MODEL_REGISTRY = IMAGE_MODEL_REGISTRY;
@@ -50097,6 +52656,7 @@ exports.MemoryEvictionCompactor = MemoryEvictionCompactor;
50097
52656
  exports.MessageBuilder = MessageBuilder;
50098
52657
  exports.MessageRole = MessageRole;
50099
52658
  exports.ModelNotSupportedError = ModelNotSupportedError;
52659
+ exports.NutTreeDriver = NutTreeDriver;
50100
52660
  exports.ParallelTasksError = ParallelTasksError;
50101
52661
  exports.PersistentInstructionsPluginNextGen = PersistentInstructionsPluginNextGen;
50102
52662
  exports.PlanningAgent = PlanningAgent;
@@ -50173,6 +52733,17 @@ exports.createAuthenticatedFetch = createAuthenticatedFetch;
50173
52733
  exports.createBashTool = createBashTool;
50174
52734
  exports.createConnectorFromTemplate = createConnectorFromTemplate;
50175
52735
  exports.createCreatePRTool = createCreatePRTool;
52736
+ exports.createDesktopGetCursorTool = createDesktopGetCursorTool;
52737
+ exports.createDesktopGetScreenSizeTool = createDesktopGetScreenSizeTool;
52738
+ exports.createDesktopKeyboardKeyTool = createDesktopKeyboardKeyTool;
52739
+ exports.createDesktopKeyboardTypeTool = createDesktopKeyboardTypeTool;
52740
+ exports.createDesktopMouseClickTool = createDesktopMouseClickTool;
52741
+ exports.createDesktopMouseDragTool = createDesktopMouseDragTool;
52742
+ exports.createDesktopMouseMoveTool = createDesktopMouseMoveTool;
52743
+ exports.createDesktopMouseScrollTool = createDesktopMouseScrollTool;
52744
+ exports.createDesktopScreenshotTool = createDesktopScreenshotTool;
52745
+ exports.createDesktopWindowFocusTool = createDesktopWindowFocusTool;
52746
+ exports.createDesktopWindowListTool = createDesktopWindowListTool;
50176
52747
  exports.createEditFileTool = createEditFileTool;
50177
52748
  exports.createEstimator = createEstimator;
50178
52749
  exports.createExecuteJavaScriptTool = createExecuteJavaScriptTool;
@@ -50203,9 +52774,22 @@ exports.createVideoProvider = createVideoProvider;
50203
52774
  exports.createVideoTools = createVideoTools;
50204
52775
  exports.createWriteFileTool = createWriteFileTool;
50205
52776
  exports.defaultDescribeCall = defaultDescribeCall;
52777
+ exports.desktopGetCursor = desktopGetCursor;
52778
+ exports.desktopGetScreenSize = desktopGetScreenSize;
52779
+ exports.desktopKeyboardKey = desktopKeyboardKey;
52780
+ exports.desktopKeyboardType = desktopKeyboardType;
52781
+ exports.desktopMouseClick = desktopMouseClick;
52782
+ exports.desktopMouseDrag = desktopMouseDrag;
52783
+ exports.desktopMouseMove = desktopMouseMove;
52784
+ exports.desktopMouseScroll = desktopMouseScroll;
52785
+ exports.desktopScreenshot = desktopScreenshot;
52786
+ exports.desktopTools = desktopTools;
52787
+ exports.desktopWindowFocus = desktopWindowFocus;
52788
+ exports.desktopWindowList = desktopWindowList;
50206
52789
  exports.detectDependencyCycle = detectDependencyCycle;
50207
52790
  exports.detectServiceFromURL = detectServiceFromURL;
50208
52791
  exports.developerTools = developerTools;
52792
+ exports.documentToContent = documentToContent;
50209
52793
  exports.editFile = editFile;
50210
52794
  exports.evaluateCondition = evaluateCondition;
50211
52795
  exports.extractJSON = extractJSON;
@@ -50229,6 +52813,7 @@ exports.getAllVendorTemplates = getAllVendorTemplates;
50229
52813
  exports.getBackgroundOutput = getBackgroundOutput;
50230
52814
  exports.getConnectorTools = getConnectorTools;
50231
52815
  exports.getCredentialsSetupURL = getCredentialsSetupURL;
52816
+ exports.getDesktopDriver = getDesktopDriver;
50232
52817
  exports.getDocsURL = getDocsURL;
50233
52818
  exports.getImageModelInfo = getImageModelInfo;
50234
52819
  exports.getImageModelsByVendor = getImageModelsByVendor;
@@ -50296,10 +52881,14 @@ exports.listVendors = listVendors;
50296
52881
  exports.listVendorsByAuthType = listVendorsByAuthType;
50297
52882
  exports.listVendorsByCategory = listVendorsByCategory;
50298
52883
  exports.listVendorsWithLogos = listVendorsWithLogos;
52884
+ exports.mergeTextPieces = mergeTextPieces;
52885
+ exports.parseKeyCombo = parseKeyCombo;
50299
52886
  exports.parseRepository = parseRepository;
50300
52887
  exports.readClipboardImage = readClipboardImage;
52888
+ exports.readDocumentAsContent = readDocumentAsContent;
50301
52889
  exports.readFile = readFile5;
50302
52890
  exports.registerScrapeProvider = registerScrapeProvider;
52891
+ exports.resetDefaultDriver = resetDefaultDriver;
50303
52892
  exports.resolveConnector = resolveConnector;
50304
52893
  exports.resolveDependencies = resolveDependencies;
50305
52894
  exports.resolveRepository = resolveRepository;