reasonix 0.52.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +1 -0
  2. package/README.zh-CN.md +1 -0
  3. package/dashboard/dist/app.css +1 -1
  4. package/dashboard/dist/app.js +13 -13
  5. package/dashboard/dist/app.js.map +1 -1
  6. package/dist/cli/{acp-NEUYWGUU.js → acp-ABNDGEYC.js} +63 -30
  7. package/dist/cli/acp-ABNDGEYC.js.map +1 -0
  8. package/dist/cli/chat-377YZV56.js +49 -0
  9. package/dist/cli/{chunk-FY5UERSG.js → chunk-2WZT27GR.js} +9 -9
  10. package/dist/cli/{chunk-B4MOGWHW.js → chunk-4EHRIP5U.js} +7 -7
  11. package/dist/cli/chunk-4EHRIP5U.js.map +1 -0
  12. package/dist/cli/{chunk-RCC73DWQ.js → chunk-4SBXAHR6.js} +4 -4
  13. package/dist/cli/{chunk-5YLEKX2V.js → chunk-4V4TKQMB.js} +4 -4
  14. package/dist/cli/{chunk-5YLEKX2V.js.map → chunk-4V4TKQMB.js.map} +1 -1
  15. package/dist/cli/chunk-7ZO6H6ZK.js +54 -0
  16. package/dist/cli/chunk-7ZO6H6ZK.js.map +1 -0
  17. package/dist/cli/{chunk-3OXD5CBM.js → chunk-A6GSOADP.js} +17870 -16070
  18. package/dist/cli/chunk-A6GSOADP.js.map +1 -0
  19. package/dist/cli/{chunk-Z663GVUB.js → chunk-APOSDBAU.js} +3 -3
  20. package/dist/cli/{chunk-CTRM32BP.js → chunk-B5JISV5I.js} +2 -2
  21. package/dist/cli/{chunk-HNZ4727T.js → chunk-DFHI2MRB.js} +412 -152
  22. package/dist/cli/chunk-DFHI2MRB.js.map +1 -0
  23. package/dist/cli/{chunk-CGVW5W7N.js → chunk-EPIHGOM3.js} +14 -14
  24. package/dist/cli/{chunk-CGVW5W7N.js.map → chunk-EPIHGOM3.js.map} +1 -1
  25. package/dist/cli/{chunk-77JIQ7SL.js → chunk-EQFZIHKJ.js} +8 -8
  26. package/dist/cli/chunk-EQFZIHKJ.js.map +1 -0
  27. package/dist/cli/{chunk-XNMXVL6C.js → chunk-FB27YXPX.js} +2 -2
  28. package/dist/cli/{chunk-ARBGTNHM.js → chunk-FK7NXDRP.js} +2 -2
  29. package/dist/cli/{chunk-AOYUW3HR.js → chunk-GCNBIWK7.js} +22 -2
  30. package/dist/cli/chunk-GCNBIWK7.js.map +1 -0
  31. package/dist/cli/{chunk-XBYHNZ5Z.js → chunk-GMQVINZK.js} +13 -5
  32. package/dist/cli/chunk-GMQVINZK.js.map +1 -0
  33. package/dist/cli/{chunk-MVLPXZAA.js → chunk-GOASYYZ4.js} +43 -11
  34. package/dist/cli/{chunk-MVLPXZAA.js.map → chunk-GOASYYZ4.js.map} +1 -1
  35. package/dist/cli/{chunk-WPY7AFS6.js → chunk-I4SH5Z7S.js} +2 -2
  36. package/dist/cli/{chunk-MW64SQUE.js → chunk-J26XOB2T.js} +2 -2
  37. package/dist/cli/{chunk-DLTE4GBY.js → chunk-J4MYMBJ7.js} +3 -3
  38. package/dist/cli/{chunk-CFJY64UA.js → chunk-LRO63VNK.js} +2 -2
  39. package/dist/cli/{chunk-XUZHBQSM.js → chunk-MQJR7YQ2.js} +2 -2
  40. package/dist/cli/{chunk-CPCUMMSR.js → chunk-NVI4XPOQ.js} +3 -3
  41. package/dist/cli/{chunk-RHQOGG43.js → chunk-OHSVEXFF.js} +3 -3
  42. package/dist/cli/chunk-OHSVEXFF.js.map +1 -0
  43. package/dist/cli/{chunk-AMSK3ZLB.js → chunk-P5SUHDUQ.js} +2 -2
  44. package/dist/cli/{chunk-GFJJEW3Z.js → chunk-QSKDP3OS.js} +55 -5
  45. package/dist/cli/chunk-QSKDP3OS.js.map +1 -0
  46. package/dist/cli/{chunk-D6WRFR6V.js → chunk-R7JMQMLD.js} +6 -5
  47. package/dist/cli/chunk-R7JMQMLD.js.map +1 -0
  48. package/dist/cli/{chunk-VVPV5HU6.js → chunk-RRZIIMAF.js} +2 -2
  49. package/dist/cli/{chunk-GNRKXRRE.js → chunk-S3QII236.js} +369 -359
  50. package/dist/cli/{chunk-GNRKXRRE.js.map → chunk-S3QII236.js.map} +1 -1
  51. package/dist/cli/{chunk-HI6THNAZ.js → chunk-TGP7JGHN.js} +32 -14
  52. package/dist/cli/chunk-TGP7JGHN.js.map +1 -0
  53. package/dist/cli/{chunk-CLHMV6OL.js → chunk-U7G72DHQ.js} +83 -42
  54. package/dist/cli/chunk-U7G72DHQ.js.map +1 -0
  55. package/dist/cli/{chunk-OMNRXZNA.js → chunk-URAI4YRL.js} +2 -2
  56. package/dist/cli/{chunk-6QBUXA73.js → chunk-V4AXMN4X.js} +2 -2
  57. package/dist/cli/{chunk-2W4F3RIZ.js → chunk-XHP6NYOT.js} +3 -2
  58. package/dist/cli/{chunk-2W4F3RIZ.js.map → chunk-XHP6NYOT.js.map} +1 -1
  59. package/dist/cli/{code-WN6D4VZO.js → code-JPFZJYVW.js} +34 -34
  60. package/dist/cli/{commands-DHETOY7O.js → commands-IUL2CLKH.js} +4 -4
  61. package/dist/cli/{commit-BBUYAKZV.js → commit-JT7LYBTL.js} +3 -3
  62. package/dist/cli/{config-KV7VV5LG.js → config-T4RWI5NG.js} +6 -2
  63. package/dist/cli/{desktop-LJVXWXNF.js → desktop-AUBW2SLL.js} +122 -38
  64. package/dist/cli/desktop-AUBW2SLL.js.map +1 -0
  65. package/dist/cli/devtools-O5HOMAGZ.js +3 -0
  66. package/dist/cli/diff-NINZHUJR.js +165 -0
  67. package/dist/cli/diff-NINZHUJR.js.map +1 -0
  68. package/dist/cli/{doctor-GI5LOEZL.js → doctor-OMAYGY4F.js} +10 -10
  69. package/dist/cli/{events-LBKMLFM4.js → events-5IVFJ4H3.js} +5 -5
  70. package/dist/cli/index.js +38 -38
  71. package/dist/cli/{mcp-DKEJK5ND.js → mcp-ECGJACAP.js} +3 -3
  72. package/dist/cli/{mcp-browse-V7KWDY32.js → mcp-browse-NGEOHVJB.js} +15 -15
  73. package/dist/cli/mcp-browse-NGEOHVJB.js.map +1 -0
  74. package/dist/cli/{mcp-inspect-MTABNHVM.js → mcp-inspect-ZIMNRG7G.js} +5 -5
  75. package/dist/cli/{prompt-ATI7DKHF.js → prompt-JCC3A7AA.js} +5 -5
  76. package/dist/cli/{prune-sessions-YQQSZTZS.js → prune-sessions-TE4BJYO2.js} +4 -4
  77. package/dist/cli/{replay-ZJQ4I4CJ.js → replay-2UUTCRTG.js} +29 -29
  78. package/dist/cli/replay-2UUTCRTG.js.map +1 -0
  79. package/dist/cli/{run-HFPRNWJY.js → run-ABQYOPVM.js} +22 -22
  80. package/dist/cli/{server-UHKO2VVM.js → server-MPCXIW2O.js} +27 -25
  81. package/dist/cli/{server-UHKO2VVM.js.map → server-MPCXIW2O.js.map} +1 -1
  82. package/dist/cli/{sessions-IQEWWUH3.js → sessions-YBPRGIAF.js} +16 -16
  83. package/dist/cli/{setup-5BYKCL62.js → setup-A34LF2QE.js} +42 -42
  84. package/dist/cli/setup-A34LF2QE.js.map +1 -0
  85. package/dist/cli/{stats-OFCGOQMZ.js → stats-GKG5JZQX.js} +6 -6
  86. package/dist/cli/stats-GKG5JZQX.js.map +1 -0
  87. package/dist/cli/{version-EODUFAAI.js → version-JD6QSM4X.js} +16 -16
  88. package/dist/index.d.ts +36 -5
  89. package/dist/index.js +443 -73
  90. package/dist/index.js.map +1 -1
  91. package/package.json +7 -2
  92. package/dist/cli/acp-NEUYWGUU.js.map +0 -1
  93. package/dist/cli/chat-QA6IVFJD.js +0 -49
  94. package/dist/cli/chunk-3OXD5CBM.js.map +0 -1
  95. package/dist/cli/chunk-77JIQ7SL.js.map +0 -1
  96. package/dist/cli/chunk-AOYUW3HR.js.map +0 -1
  97. package/dist/cli/chunk-B4MOGWHW.js.map +0 -1
  98. package/dist/cli/chunk-CLHMV6OL.js.map +0 -1
  99. package/dist/cli/chunk-D6WRFR6V.js.map +0 -1
  100. package/dist/cli/chunk-GFJJEW3Z.js.map +0 -1
  101. package/dist/cli/chunk-HI6THNAZ.js.map +0 -1
  102. package/dist/cli/chunk-HNZ4727T.js.map +0 -1
  103. package/dist/cli/chunk-I3NE5S63.js +0 -54
  104. package/dist/cli/chunk-I3NE5S63.js.map +0 -1
  105. package/dist/cli/chunk-RHQOGG43.js.map +0 -1
  106. package/dist/cli/chunk-XBYHNZ5Z.js.map +0 -1
  107. package/dist/cli/desktop-LJVXWXNF.js.map +0 -1
  108. package/dist/cli/diff-2JHMQAHI.js +0 -165
  109. package/dist/cli/diff-2JHMQAHI.js.map +0 -1
  110. package/dist/cli/mcp-browse-V7KWDY32.js.map +0 -1
  111. package/dist/cli/replay-ZJQ4I4CJ.js.map +0 -1
  112. package/dist/cli/setup-5BYKCL62.js.map +0 -1
  113. /package/dist/cli/{chat-QA6IVFJD.js.map → chat-377YZV56.js.map} +0 -0
  114. /package/dist/cli/{chunk-FY5UERSG.js.map → chunk-2WZT27GR.js.map} +0 -0
  115. /package/dist/cli/{chunk-RCC73DWQ.js.map → chunk-4SBXAHR6.js.map} +0 -0
  116. /package/dist/cli/{chunk-Z663GVUB.js.map → chunk-APOSDBAU.js.map} +0 -0
  117. /package/dist/cli/{chunk-CTRM32BP.js.map → chunk-B5JISV5I.js.map} +0 -0
  118. /package/dist/cli/{chunk-XNMXVL6C.js.map → chunk-FB27YXPX.js.map} +0 -0
  119. /package/dist/cli/{chunk-ARBGTNHM.js.map → chunk-FK7NXDRP.js.map} +0 -0
  120. /package/dist/cli/{chunk-WPY7AFS6.js.map → chunk-I4SH5Z7S.js.map} +0 -0
  121. /package/dist/cli/{chunk-MW64SQUE.js.map → chunk-J26XOB2T.js.map} +0 -0
  122. /package/dist/cli/{chunk-DLTE4GBY.js.map → chunk-J4MYMBJ7.js.map} +0 -0
  123. /package/dist/cli/{chunk-CFJY64UA.js.map → chunk-LRO63VNK.js.map} +0 -0
  124. /package/dist/cli/{chunk-XUZHBQSM.js.map → chunk-MQJR7YQ2.js.map} +0 -0
  125. /package/dist/cli/{chunk-CPCUMMSR.js.map → chunk-NVI4XPOQ.js.map} +0 -0
  126. /package/dist/cli/{chunk-AMSK3ZLB.js.map → chunk-P5SUHDUQ.js.map} +0 -0
  127. /package/dist/cli/{chunk-VVPV5HU6.js.map → chunk-RRZIIMAF.js.map} +0 -0
  128. /package/dist/cli/{chunk-OMNRXZNA.js.map → chunk-URAI4YRL.js.map} +0 -0
  129. /package/dist/cli/{chunk-6QBUXA73.js.map → chunk-V4AXMN4X.js.map} +0 -0
  130. /package/dist/cli/{code-WN6D4VZO.js.map → code-JPFZJYVW.js.map} +0 -0
  131. /package/dist/cli/{commands-DHETOY7O.js.map → commands-IUL2CLKH.js.map} +0 -0
  132. /package/dist/cli/{commit-BBUYAKZV.js.map → commit-JT7LYBTL.js.map} +0 -0
  133. /package/dist/cli/{config-KV7VV5LG.js.map → config-T4RWI5NG.js.map} +0 -0
  134. /package/dist/cli/{doctor-GI5LOEZL.js.map → devtools-O5HOMAGZ.js.map} +0 -0
  135. /package/dist/cli/{prompt-ATI7DKHF.js.map → doctor-OMAYGY4F.js.map} +0 -0
  136. /package/dist/cli/{events-LBKMLFM4.js.map → events-5IVFJ4H3.js.map} +0 -0
  137. /package/dist/cli/{mcp-DKEJK5ND.js.map → mcp-ECGJACAP.js.map} +0 -0
  138. /package/dist/cli/{mcp-inspect-MTABNHVM.js.map → mcp-inspect-ZIMNRG7G.js.map} +0 -0
  139. /package/dist/cli/{stats-OFCGOQMZ.js.map → prompt-JCC3A7AA.js.map} +0 -0
  140. /package/dist/cli/{prune-sessions-YQQSZTZS.js.map → prune-sessions-TE4BJYO2.js.map} +0 -0
  141. /package/dist/cli/{run-HFPRNWJY.js.map → run-ABQYOPVM.js.map} +0 -0
  142. /package/dist/cli/{sessions-IQEWWUH3.js.map → sessions-YBPRGIAF.js.map} +0 -0
  143. /package/dist/cli/{version-EODUFAAI.js.map → version-JD6QSM4X.js.map} +0 -0
@@ -7,7 +7,12 @@ import {
7
7
  decodeFileBuffer,
8
8
  encodeFile,
9
9
  pauseGate
10
- } from "./chunk-5YLEKX2V.js";
10
+ } from "./chunk-4V4TKQMB.js";
11
+ import {
12
+ DEEPSEEK_CONTEXT_TOKENS,
13
+ DEFAULT_CONTEXT_TOKENS,
14
+ SessionStats
15
+ } from "./chunk-V4AXMN4X.js";
11
16
  import {
12
17
  MemoryStore,
13
18
  NEGATIVE_CLAIM_RULE,
@@ -16,16 +21,19 @@ import {
16
21
  TUI_FORMATTING_RULES,
17
22
  memoryEnabled,
18
23
  sanitizeMemoryName
19
- } from "./chunk-DLTE4GBY.js";
24
+ } from "./chunk-J4MYMBJ7.js";
20
25
  import {
21
26
  countTokens,
22
27
  countTokensBounded,
23
28
  estimateRequestTokens
24
29
  } from "./chunk-BOWSNGQC.js";
30
+ import {
31
+ Usage
32
+ } from "./chunk-QSKDP3OS.js";
25
33
  import {
26
34
  formatHookOutcomeMessage,
27
35
  runHooks
28
- } from "./chunk-CPCUMMSR.js";
36
+ } from "./chunk-NVI4XPOQ.js";
29
37
  import {
30
38
  TtlLruCache,
31
39
  ignoredByLayers,
@@ -40,25 +48,19 @@ import {
40
48
  patchSessionMeta,
41
49
  rewriteSession,
42
50
  timestampSuffix
43
- } from "./chunk-AMSK3ZLB.js";
44
- import {
45
- Usage
46
- } from "./chunk-GFJJEW3Z.js";
47
- import {
48
- DEEPSEEK_CONTEXT_TOKENS,
49
- DEFAULT_CONTEXT_TOKENS,
50
- SessionStats
51
- } from "./chunk-6QBUXA73.js";
51
+ } from "./chunk-P5SUHDUQ.js";
52
52
  import {
53
53
  t
54
- } from "./chunk-CLHMV6OL.js";
54
+ } from "./chunk-U7G72DHQ.js";
55
55
  import {
56
56
  DEFAULT_INDEX_EXCLUDES,
57
57
  ToolRateLimiter,
58
58
  addProjectPathAllowed,
59
+ loadBraveApiKey,
59
60
  loadExaApiKey,
60
61
  loadMemoryTypeRegistry,
61
62
  loadMetasoApiKey,
63
+ loadOllamaApiKey,
62
64
  loadPerplexityApiKey,
63
65
  loadProjectPathAllowed,
64
66
  loadTavilyApiKey,
@@ -66,7 +68,7 @@ import {
66
68
  require_picomatch,
67
69
  webSearchEndpoint,
68
70
  webSearchEngine
69
- } from "./chunk-AOYUW3HR.js";
71
+ } from "./chunk-GCNBIWK7.js";
70
72
  import {
71
73
  __commonJS,
72
74
  __esm,
@@ -2892,7 +2894,7 @@ function isQuote(c) {
2892
2894
  function isWhitespace(c) {
2893
2895
  return c === 32 || c === 9 || c === 10 || c === 12 || c === 13;
2894
2896
  }
2895
- function parse(selector) {
2897
+ function parse2(selector) {
2896
2898
  const subselects = [];
2897
2899
  const endIndex = parseSelector(subselects, `${selector}`, 0);
2898
2900
  if (endIndex < selector.length) {
@@ -3324,7 +3326,7 @@ __export(es_exports, {
3324
3326
  IgnoreCaseMode: () => IgnoreCaseMode,
3325
3327
  SelectorType: () => SelectorType,
3326
3328
  isTraversal: () => isTraversal,
3327
- parse: () => parse,
3329
+ parse: () => parse2,
3328
3330
  stringify: () => stringify
3329
3331
  });
3330
3332
  var init_es = __esm({
@@ -3616,7 +3618,7 @@ var require_parse = __commonJS({
3616
3618
  var whitespace = /* @__PURE__ */ new Set([9, 10, 12, 13, 32]);
3617
3619
  var ZERO = "0".charCodeAt(0);
3618
3620
  var NINE = "9".charCodeAt(0);
3619
- function parse2(formula) {
3621
+ function parse3(formula) {
3620
3622
  formula = formula.trim().toLowerCase();
3621
3623
  if (formula === "even") {
3622
3624
  return [2, 0];
@@ -3668,7 +3670,7 @@ var require_parse = __commonJS({
3668
3670
  }
3669
3671
  }
3670
3672
  }
3671
- exports.parse = parse2;
3673
+ exports.parse = parse3;
3672
3674
  }
3673
3675
  });
3674
3676
 
@@ -5118,7 +5120,7 @@ var require_html = __commonJS({
5118
5120
  }).join("");
5119
5121
  }
5120
5122
  set innerHTML(content) {
5121
- const r = parse2(content, this._parseOptions);
5123
+ const r = parse3(content, this._parseOptions);
5122
5124
  const nodes = r.childNodes.length ? r.childNodes : [new text_1.default(content, this)];
5123
5125
  resetParent(nodes, this);
5124
5126
  resetParent(this.childNodes, null);
@@ -5129,7 +5131,7 @@ var require_html = __commonJS({
5129
5131
  content = [content];
5130
5132
  } else if (typeof content == "string") {
5131
5133
  options = Object.assign(Object.assign({}, this._parseOptions), options);
5132
- const r = parse2(content, options);
5134
+ const r = parse3(content, options);
5133
5135
  content = r.childNodes.length ? r.childNodes : [new text_1.default(r.innerHTML, this)];
5134
5136
  }
5135
5137
  resetParent(this.childNodes, null);
@@ -5143,7 +5145,7 @@ var require_html = __commonJS({
5143
5145
  if (node instanceof node_1.default) {
5144
5146
  return [node];
5145
5147
  } else if (typeof node == "string") {
5146
- const r = parse2(node, this._parseOptions);
5148
+ const r = parse3(node, this._parseOptions);
5147
5149
  return r.childNodes.length ? r.childNodes : [new text_1.default(node, this)];
5148
5150
  }
5149
5151
  return [];
@@ -5527,7 +5529,7 @@ var require_html = __commonJS({
5527
5529
  if (arguments.length < 2) {
5528
5530
  throw new Error("2 arguments required");
5529
5531
  }
5530
- const p = parse2(html, this._parseOptions);
5532
+ const p = parse3(html, this._parseOptions);
5531
5533
  if (where === "afterend") {
5532
5534
  this.after(...p.childNodes);
5533
5535
  } else if (where === "afterbegin") {
@@ -5673,7 +5675,7 @@ var require_html = __commonJS({
5673
5675
  }
5674
5676
  /** Clone this Node */
5675
5677
  clone() {
5676
- return parse2(this.toString(), this._parseOptions).firstChild;
5678
+ return parse3(this.toString(), this._parseOptions).firstChild;
5677
5679
  }
5678
5680
  };
5679
5681
  exports.default = HTMLElement;
@@ -5875,7 +5877,7 @@ var require_html = __commonJS({
5875
5877
  return stack;
5876
5878
  }
5877
5879
  exports.base_parse = base_parse;
5878
- function parse2(data, options = {}) {
5880
+ function parse3(data, options = {}) {
5879
5881
  const stack = base_parse(data, options);
5880
5882
  const [root] = stack;
5881
5883
  while (stack.length > 1) {
@@ -5903,7 +5905,7 @@ var require_html = __commonJS({
5903
5905
  }
5904
5906
  return root;
5905
5907
  }
5906
- exports.parse = parse2;
5908
+ exports.parse = parse3;
5907
5909
  function resolveInsertable(insertable) {
5908
5910
  return insertable.map((val) => {
5909
5911
  if (typeof val === "string") {
@@ -5971,18 +5973,18 @@ var require_dist = __commonJS({
5971
5973
  var parse_1 = __importDefault(require_parse2());
5972
5974
  var valid_1 = __importDefault(require_valid());
5973
5975
  exports.valid = valid_1.default;
5974
- function parse2(data, options = {}) {
5976
+ function parse3(data, options = {}) {
5975
5977
  return (0, parse_1.default)(data, options);
5976
5978
  }
5977
- exports.default = parse2;
5978
- exports.parse = parse2;
5979
- parse2.parse = parse_1.default;
5980
- parse2.HTMLElement = html_1.default;
5981
- parse2.CommentNode = comment_1.default;
5982
- parse2.valid = valid_1.default;
5983
- parse2.Node = node_1.default;
5984
- parse2.TextNode = text_1.default;
5985
- parse2.NodeType = type_1.default;
5979
+ exports.default = parse3;
5980
+ exports.parse = parse3;
5981
+ parse3.parse = parse_1.default;
5982
+ parse3.HTMLElement = html_1.default;
5983
+ parse3.CommentNode = comment_1.default;
5984
+ parse3.valid = valid_1.default;
5985
+ parse3.Node = node_1.default;
5986
+ parse3.TextNode = text_1.default;
5987
+ parse3.NodeType = type_1.default;
5986
5988
  }
5987
5989
  });
5988
5990
 
@@ -6171,8 +6173,8 @@ function truncateForModel(s, maxChars, extraNote) {
6171
6173
  if (s.length <= maxChars) return s;
6172
6174
  const tailBudget = Math.min(1024, Math.floor(maxChars * 0.1));
6173
6175
  const headBudget = Math.max(0, maxChars - tailBudget);
6174
- const head = s.slice(0, headBudget);
6175
- const tail = s.slice(-tailBudget);
6176
+ const head = sliceAlignedToCodepoint(s, headBudget);
6177
+ const tail = sliceSuffixAlignedToCodepoint(s, tailBudget);
6176
6178
  const dropped = s.length - head.length - tail.length;
6177
6179
  const note = extraNote ? ` \u2014 ${extraNote}` : "";
6178
6180
  return `${head}
@@ -6181,6 +6183,21 @@ function truncateForModel(s, maxChars, extraNote) {
6181
6183
 
6182
6184
  ${tail}`;
6183
6185
  }
6186
+ function sliceAlignedToCodepoint(s, end) {
6187
+ if (end <= 0) return "";
6188
+ if (end >= s.length) return s;
6189
+ const last = s.charCodeAt(end - 1);
6190
+ if (last >= 55296 && last <= 56319) return s.slice(0, end - 1);
6191
+ return s.slice(0, end);
6192
+ }
6193
+ function sliceSuffixAlignedToCodepoint(s, len) {
6194
+ if (len <= 0) return "";
6195
+ if (len >= s.length) return s;
6196
+ const start = s.length - len;
6197
+ const first = s.charCodeAt(start);
6198
+ if (first >= 56320 && first <= 57343) return s.slice(start + 1);
6199
+ return s.slice(start);
6200
+ }
6184
6201
  function truncateForModelByTokens(s, maxTokens, extraNote) {
6185
6202
  if (maxTokens <= 0) return "";
6186
6203
  if (s.length <= maxTokens) return s;
@@ -6218,28 +6235,28 @@ function sizePrefixToTokens(s, budget) {
6218
6235
  let size = Math.min(s.length, budget * 4);
6219
6236
  for (let iter = 0; iter < 6; iter++) {
6220
6237
  if (size <= 0) return "";
6221
- const slice = s.slice(0, size);
6238
+ const slice = sliceAlignedToCodepoint(s, size);
6222
6239
  const count = countTokens(slice);
6223
6240
  if (count <= budget) return slice;
6224
6241
  const next = Math.floor(size * (budget / count) * 0.95);
6225
- if (next >= size) return s.slice(0, Math.max(0, size - 1));
6242
+ if (next >= size) return sliceAlignedToCodepoint(s, Math.max(0, size - 1));
6226
6243
  size = next;
6227
6244
  }
6228
- return s.slice(0, Math.max(0, size));
6245
+ return sliceAlignedToCodepoint(s, Math.max(0, size));
6229
6246
  }
6230
6247
  function sizeSuffixToTokens(s, budget) {
6231
6248
  if (budget <= 0 || s.length === 0) return "";
6232
6249
  let size = Math.min(s.length, budget * 4);
6233
6250
  for (let iter = 0; iter < 6; iter++) {
6234
6251
  if (size <= 0) return "";
6235
- const slice = s.slice(-size);
6252
+ const slice = sliceSuffixAlignedToCodepoint(s, size);
6236
6253
  const count = countTokens(slice);
6237
6254
  if (count <= budget) return slice;
6238
6255
  const next = Math.floor(size * (budget / count) * 0.95);
6239
- if (next >= size) return s.slice(-Math.max(0, size - 1));
6256
+ if (next >= size) return sliceSuffixAlignedToCodepoint(s, Math.max(0, size - 1));
6240
6257
  size = next;
6241
6258
  }
6242
- return s.slice(-Math.max(0, size));
6259
+ return sliceSuffixAlignedToCodepoint(s, Math.max(0, size));
6243
6260
  }
6244
6261
  function blockToString(block) {
6245
6262
  if (block.type === "text") return block.text;
@@ -6327,14 +6344,19 @@ import {
6327
6344
  writeFileSync
6328
6345
  } from "fs";
6329
6346
  import { homedir } from "os";
6330
- import { join, relative, resolve } from "path";
6347
+ import { join, parse, relative, resolve } from "path";
6331
6348
  var TRUNCATED_DIR = "truncated-results";
6332
6349
  var DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
6333
6350
  function sanitizeToolName(name) {
6334
6351
  return name.replace(/[^\w\-]/g, "_").slice(0, 48) || "unknown";
6335
6352
  }
6353
+ function useHomeFallback(rootDir) {
6354
+ if (!rootDir) return true;
6355
+ const abs = resolve(rootDir);
6356
+ return abs === parse(abs).root;
6357
+ }
6336
6358
  function storageDir(rootDir) {
6337
- const base = rootDir ? join(resolve(rootDir), ".reasonix") : join(homedir(), ".reasonix");
6359
+ const base = useHomeFallback(rootDir) ? join(homedir(), ".reasonix") : join(resolve(rootDir), ".reasonix");
6338
6360
  return join(base, TRUNCATED_DIR);
6339
6361
  }
6340
6362
  function resultFilename(toolName) {
@@ -6356,7 +6378,7 @@ function saveTruncatedResult(content, toolName, rootDir) {
6356
6378
  chmodSync(absPath, 384);
6357
6379
  } catch {
6358
6380
  }
6359
- if (rootDir) {
6381
+ if (!useHomeFallback(rootDir)) {
6360
6382
  const absRoot = resolve(rootDir);
6361
6383
  return relative(absRoot, absPath).replaceAll("\\", "/");
6362
6384
  }
@@ -6730,6 +6752,105 @@ function missingRequiredParam(schema, args) {
6730
6752
  return null;
6731
6753
  }
6732
6754
 
6755
+ // src/loop/errors.ts
6756
+ function formatLoopError(err, probe, opts) {
6757
+ const msg = err.message ?? "";
6758
+ if (msg.includes("maximum context length")) {
6759
+ const reqMatch = msg.match(/requested\s+(\d+)\s+tokens/);
6760
+ const requested = reqMatch ? `${Number(reqMatch[1]).toLocaleString()} tokens` : t("errors.contextOverflowTooMany");
6761
+ return t("errors.contextOverflow", { requested });
6762
+ }
6763
+ const m = /^DeepSeek (\d{3}):\s*([\s\S]*)$/.exec(msg);
6764
+ if (!m) return msg;
6765
+ const status = m[1] ?? "";
6766
+ const body = m[2] ?? "";
6767
+ const inner = extractDeepSeekErrorMessage(body);
6768
+ if (status === "401") return t("errors.auth401", { inner });
6769
+ if (status === "402") return t("errors.balance402", { inner });
6770
+ if (status === "422") return t("errors.badparam422", { inner });
6771
+ if (status === "400") return t("errors.badrequest400", { inner });
6772
+ if (status === "429") return t("errors.concurrency429", { inner });
6773
+ if (is5xxStatus(status)) return format5xx(status, probe, opts?.upstreamHost);
6774
+ return msg;
6775
+ }
6776
+ function is5xxError(err) {
6777
+ if (!(err instanceof Error)) return false;
6778
+ const m = /^DeepSeek (5\d{2}):/.exec(err.message ?? "");
6779
+ return m !== null;
6780
+ }
6781
+ function is4xxError(err) {
6782
+ if (!(err instanceof Error)) return false;
6783
+ return /^DeepSeek (4\d{2}):/.test(err.message ?? "");
6784
+ }
6785
+ function errorMeta(err) {
6786
+ if (!(err instanceof Error)) return {};
6787
+ const code = "code" in err && typeof err.code === "string" ? err.code : void 0;
6788
+ const phase = "phase" in err && typeof err.phase === "string" ? err.phase : void 0;
6789
+ return { code, phase };
6790
+ }
6791
+ async function probeDeepSeekReachable(client, timeoutMs = 1500) {
6792
+ const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });
6793
+ return { reachable: balance !== null };
6794
+ }
6795
+ function isDeepSeekHost(baseUrl) {
6796
+ if (!baseUrl) return false;
6797
+ try {
6798
+ const host = new URL(baseUrl).hostname.toLowerCase();
6799
+ return host === "api.deepseek.com";
6800
+ } catch {
6801
+ return false;
6802
+ }
6803
+ }
6804
+ function is5xxStatus(status) {
6805
+ return status === "500" || status === "502" || status === "503" || status === "504";
6806
+ }
6807
+ function format5xx(status, probe, upstreamHost) {
6808
+ if (upstreamHost !== void 0 && !isDeepSeekHost(upstreamHost)) {
6809
+ return formatUpstream5xx(status, upstreamHost);
6810
+ }
6811
+ return formatDeepSeek5xx(status, probe);
6812
+ }
6813
+ function formatDeepSeek5xx(status, probe) {
6814
+ const head = t("errors.deepseek5xxHead", { status });
6815
+ const probeNote = probe === void 0 ? "" : probe.reachable ? t("errors.deepseek5xxReachable") : t("errors.deepseek5xxUnreachable");
6816
+ const action = probe?.reachable === false ? t("errors.deepseek5xxActionNetwork") : t("errors.deepseek5xxActionRetry");
6817
+ return `${head}${probeNote}${action}`;
6818
+ }
6819
+ function formatUpstream5xx(status, baseUrl) {
6820
+ let host = baseUrl;
6821
+ try {
6822
+ host = new URL(baseUrl).host || baseUrl;
6823
+ } catch {
6824
+ }
6825
+ const head = t("errors.upstream5xxHead", { status, host });
6826
+ const action = t("errors.upstream5xxActionRetry");
6827
+ return `${head}${action}`;
6828
+ }
6829
+ function reasonPrefixFor(reason) {
6830
+ if (reason === "aborted") return t("errors.reasonAborted");
6831
+ if (reason === "context-guard") return t("errors.reasonContextGuard");
6832
+ return t("errors.reasonStuck");
6833
+ }
6834
+ function errorLabelFor(reason) {
6835
+ if (reason === "aborted") return t("errors.labelAborted");
6836
+ if (reason === "context-guard") return t("errors.labelContextGuard");
6837
+ return t("errors.labelStuck");
6838
+ }
6839
+ function extractDeepSeekErrorMessage(body) {
6840
+ const trimmed = body.trim();
6841
+ if (!trimmed) return t("errors.innerNoMessage");
6842
+ try {
6843
+ const parsed = JSON.parse(trimmed);
6844
+ if (parsed && typeof parsed === "object") {
6845
+ const obj = parsed;
6846
+ if (obj.error && typeof obj.error.message === "string") return obj.error.message;
6847
+ if (typeof obj.message === "string") return obj.message;
6848
+ }
6849
+ } catch {
6850
+ }
6851
+ return trimmed;
6852
+ }
6853
+
6733
6854
  // src/memory/runtime.ts
6734
6855
  import { createHash } from "crypto";
6735
6856
  var ImmutablePrefix = class {
@@ -7227,95 +7348,6 @@ async function* dispatchToolCallsChunked(repairedCalls, ctx) {
7227
7348
  }
7228
7349
  }
7229
7350
 
7230
- // src/loop/errors.ts
7231
- function formatLoopError(err, probe, opts) {
7232
- const msg = err.message ?? "";
7233
- if (msg.includes("maximum context length")) {
7234
- const reqMatch = msg.match(/requested\s+(\d+)\s+tokens/);
7235
- const requested = reqMatch ? `${Number(reqMatch[1]).toLocaleString()} tokens` : t("errors.contextOverflowTooMany");
7236
- return t("errors.contextOverflow", { requested });
7237
- }
7238
- const m = /^DeepSeek (\d{3}):\s*([\s\S]*)$/.exec(msg);
7239
- if (!m) return msg;
7240
- const status = m[1] ?? "";
7241
- const body = m[2] ?? "";
7242
- const inner = extractDeepSeekErrorMessage(body);
7243
- if (status === "401") return t("errors.auth401", { inner });
7244
- if (status === "402") return t("errors.balance402", { inner });
7245
- if (status === "422") return t("errors.badparam422", { inner });
7246
- if (status === "400") return t("errors.badrequest400", { inner });
7247
- if (status === "429") return t("errors.concurrency429", { inner });
7248
- if (is5xxStatus(status)) return format5xx(status, probe, opts?.upstreamHost);
7249
- return msg;
7250
- }
7251
- function is5xxError(err) {
7252
- if (!(err instanceof Error)) return false;
7253
- const m = /^DeepSeek (5\d{2}):/.exec(err.message ?? "");
7254
- return m !== null;
7255
- }
7256
- async function probeDeepSeekReachable(client, timeoutMs = 1500) {
7257
- const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });
7258
- return { reachable: balance !== null };
7259
- }
7260
- function isDeepSeekHost(baseUrl) {
7261
- if (!baseUrl) return false;
7262
- try {
7263
- const host = new URL(baseUrl).hostname.toLowerCase();
7264
- return host === "api.deepseek.com";
7265
- } catch {
7266
- return false;
7267
- }
7268
- }
7269
- function is5xxStatus(status) {
7270
- return status === "500" || status === "502" || status === "503" || status === "504";
7271
- }
7272
- function format5xx(status, probe, upstreamHost) {
7273
- if (upstreamHost !== void 0 && !isDeepSeekHost(upstreamHost)) {
7274
- return formatUpstream5xx(status, upstreamHost);
7275
- }
7276
- return formatDeepSeek5xx(status, probe);
7277
- }
7278
- function formatDeepSeek5xx(status, probe) {
7279
- const head = t("errors.deepseek5xxHead", { status });
7280
- const probeNote = probe === void 0 ? "" : probe.reachable ? t("errors.deepseek5xxReachable") : t("errors.deepseek5xxUnreachable");
7281
- const action = probe?.reachable === false ? t("errors.deepseek5xxActionNetwork") : t("errors.deepseek5xxActionRetry");
7282
- return `${head}${probeNote}${action}`;
7283
- }
7284
- function formatUpstream5xx(status, baseUrl) {
7285
- let host = baseUrl;
7286
- try {
7287
- host = new URL(baseUrl).host || baseUrl;
7288
- } catch {
7289
- }
7290
- const head = t("errors.upstream5xxHead", { status, host });
7291
- const action = t("errors.upstream5xxActionRetry");
7292
- return `${head}${action}`;
7293
- }
7294
- function reasonPrefixFor(reason) {
7295
- if (reason === "aborted") return t("errors.reasonAborted");
7296
- if (reason === "context-guard") return t("errors.reasonContextGuard");
7297
- return t("errors.reasonStuck");
7298
- }
7299
- function errorLabelFor(reason) {
7300
- if (reason === "aborted") return t("errors.labelAborted");
7301
- if (reason === "context-guard") return t("errors.labelContextGuard");
7302
- return t("errors.labelStuck");
7303
- }
7304
- function extractDeepSeekErrorMessage(body) {
7305
- const trimmed = body.trim();
7306
- if (!trimmed) return t("errors.innerNoMessage");
7307
- try {
7308
- const parsed = JSON.parse(trimmed);
7309
- if (parsed && typeof parsed === "object") {
7310
- const obj = parsed;
7311
- if (obj.error && typeof obj.error.message === "string") return obj.error.message;
7312
- if (typeof obj.message === "string") return obj.message;
7313
- }
7314
- } catch {
7315
- }
7316
- return trimmed;
7317
- }
7318
-
7319
7351
  // src/loop/force-summary.ts
7320
7352
  async function* forceSummaryAfterIterLimit(ctx, opts) {
7321
7353
  try {
@@ -7325,9 +7357,8 @@ async function* forceSummaryAfterIterLimit(ctx, opts) {
7325
7357
  role: "user",
7326
7358
  content: "The turn is being force-summarized (context guard or stuck-state). Summarize in plain prose what you learned from the tool results above. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks \u2014 they will be silently discarded. Just plain text."
7327
7359
  });
7328
- const summaryModel = "deepseek-v4-flash";
7329
7360
  const resp = await ctx.client.chat({
7330
- model: summaryModel,
7361
+ model: ctx.model,
7331
7362
  messages,
7332
7363
  signal: ctx.signal,
7333
7364
  thinking: "disabled"
@@ -7339,8 +7370,8 @@ async function* forceSummaryAfterIterLimit(ctx, opts) {
7339
7370
  const annotated = `${reasonPrefix}
7340
7371
 
7341
7372
  ${summary}`;
7342
- const summaryStats = ctx.recordStats(summaryModel, resp.usage ?? new Usage());
7343
- ctx.appendAndPersist(buildAssistantMessage(summary, [], summaryModel, resp.reasoningContent));
7373
+ const summaryStats = ctx.recordStats(ctx.model, resp.usage ?? new Usage());
7374
+ ctx.appendAndPersist(buildAssistantMessage(summary, [], ctx.model, resp.reasoningContent));
7344
7375
  yield {
7345
7376
  turn: ctx.turn,
7346
7377
  role: "assistant_final",
@@ -7351,11 +7382,18 @@ ${summary}`;
7351
7382
  yield { turn: ctx.turn, role: "done", content: summary };
7352
7383
  } catch (err) {
7353
7384
  const label = errorLabelFor(opts.reason);
7385
+ const message = t("summary.failedAfterReason", { label, message: err.message });
7354
7386
  yield {
7355
7387
  turn: ctx.turn,
7356
7388
  role: "error",
7357
7389
  content: "",
7358
- error: t("summary.failedAfterReason", { label, message: err.message })
7390
+ error: message,
7391
+ errorDetail: {
7392
+ name: "ForceSummaryFailed",
7393
+ message,
7394
+ retryable: true,
7395
+ recoverable: true
7396
+ }
7359
7397
  };
7360
7398
  yield { turn: ctx.turn, role: "done", content: "" };
7361
7399
  }
@@ -8434,14 +8472,21 @@ ${reason}`
8434
8472
  if (this.budgetUsd !== null) {
8435
8473
  const spent = this.stats.totalCost;
8436
8474
  if (spent >= this.budgetUsd) {
8475
+ const message = t("loop.budgetExhausted", {
8476
+ spent: spent.toFixed(4),
8477
+ cap: this.budgetUsd.toFixed(2)
8478
+ });
8437
8479
  yield {
8438
8480
  turn: this._turn,
8439
8481
  role: "error",
8440
8482
  content: "",
8441
- error: t("loop.budgetExhausted", {
8442
- spent: spent.toFixed(4),
8443
- cap: this.budgetUsd.toFixed(2)
8444
- })
8483
+ error: message,
8484
+ errorDetail: {
8485
+ name: "BudgetExhausted",
8486
+ message,
8487
+ retryable: false,
8488
+ recoverable: false
8489
+ }
8445
8490
  };
8446
8491
  this._steerQueue.length = 0;
8447
8492
  return;
@@ -8595,11 +8640,22 @@ ${reason}`
8595
8640
  const upstreamHost = this.client.baseUrl;
8596
8641
  const dsHost = isDeepSeekHost(upstreamHost);
8597
8642
  const probe = is5xxError(err) && dsHost ? await probeDeepSeekReachable(this.client) : void 0;
8643
+ const cause = err instanceof Error ? err : new Error(String(err));
8644
+ const retryable = !is4xxError(cause) && cause.name !== "AbortError";
8645
+ const { code, phase } = errorMeta(cause);
8598
8646
  yield {
8599
8647
  turn: this._turn,
8600
8648
  role: "error",
8601
8649
  content: "",
8602
- error: formatLoopError(err, probe, { upstreamHost })
8650
+ error: formatLoopError(err, probe, { upstreamHost }),
8651
+ errorDetail: {
8652
+ name: cause.name,
8653
+ message: cause.message,
8654
+ phase,
8655
+ code,
8656
+ retryable,
8657
+ recoverable: false
8658
+ }
8603
8659
  };
8604
8660
  this._steerQueue.length = 0;
8605
8661
  return;
@@ -8744,7 +8800,8 @@ ${reason}`
8744
8800
  buildMessages: () => this.buildMessages(),
8745
8801
  appendAndPersist: (m) => this.appendAndPersist(m),
8746
8802
  recordStats: (model, usage) => this.stats.record(this._turn, model, usage),
8747
- turn: this._turn
8803
+ turn: this._turn,
8804
+ model: this.model
8748
8805
  };
8749
8806
  }
8750
8807
  async run(userInput, onEvent) {
@@ -9576,6 +9633,9 @@ var METASO_ENDPOINT = "https://metaso.cn/api/v1";
9576
9633
  var TAVILY_ENDPOINT = "https://api.tavily.com/search";
9577
9634
  var PERPLEXITY_ENDPOINT = "https://api.perplexity.ai/chat/completions";
9578
9635
  var EXA_ENDPOINT = "https://api.exa.ai/answer";
9636
+ var BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search";
9637
+ var OLLAMA_WEB_SEARCH_ENDPOINT = "https://ollama.com/api/web_search";
9638
+ var OLLAMA_WEB_FETCH_ENDPOINT = "https://ollama.com/api/web_fetch";
9579
9639
  var FETCH_MAX_REDIRECTS = 5;
9580
9640
  function searchStatusError(status) {
9581
9641
  if (status === 429) return t("webErrors.rateLimit429");
@@ -9627,6 +9687,22 @@ function isInternalAddress(address) {
9627
9687
  if (family === 6) return isPrivateIpv6(address);
9628
9688
  return false;
9629
9689
  }
9690
+ async function dohResolve(host) {
9691
+ const url = new URL("https://1.1.1.1/dns-query");
9692
+ url.searchParams.set("name", host);
9693
+ url.searchParams.set("type", "A");
9694
+ const resp = await fetch(url.toString(), {
9695
+ headers: { Accept: "application/dns-json" },
9696
+ signal: AbortSignal.timeout(5e3)
9697
+ });
9698
+ if (!resp.ok) throw new Error(`DoH resolve failed: HTTP ${resp.status} for ${host}`);
9699
+ const data = await resp.json();
9700
+ if (data.Status !== 0)
9701
+ throw new Error(`DoH resolve failed: DNS status ${data.Status} for ${host}`);
9702
+ const addresses = (data.Answer ?? []).filter((a) => a.type === 1).map((a) => a.data);
9703
+ if (addresses.length === 0) throw new Error(`DoH resolve returned no A records for ${host}`);
9704
+ return addresses;
9705
+ }
9630
9706
  async function assertPublicHttpUrl(rawUrl) {
9631
9707
  const url = new URL(rawUrl);
9632
9708
  if (url.protocol !== "http:" && url.protocol !== "https:") {
@@ -9634,10 +9710,22 @@ async function assertPublicHttpUrl(rawUrl) {
9634
9710
  }
9635
9711
  const host = url.hostname;
9636
9712
  const literal = isIP(host);
9637
- const addresses = literal ? [host] : (await lookup(host, { all: true, verbatim: true })).map((entry) => entry.address);
9638
- if (addresses.length === 0 || addresses.some(isInternalAddress)) {
9713
+ if (literal) {
9714
+ if (isInternalAddress(host)) {
9715
+ throw new Error(`web_fetch refuses internal or reserved host: ${host}`);
9716
+ }
9717
+ return url;
9718
+ }
9719
+ const sysAddrs = (await lookup(host, { all: true, verbatim: true })).map((e) => e.address);
9720
+ if (sysAddrs.length === 0) {
9639
9721
  throw new Error(`web_fetch refuses internal or reserved host: ${host}`);
9640
9722
  }
9723
+ if (sysAddrs.some(isInternalAddress)) {
9724
+ const dohAddrs = await dohResolve(host).catch(() => null);
9725
+ if (!dohAddrs || dohAddrs.some(isInternalAddress)) {
9726
+ throw new Error(`web_fetch refuses internal or reserved host: ${host}`);
9727
+ }
9728
+ }
9641
9729
  return url;
9642
9730
  }
9643
9731
  function redirectLocation(resp, currentUrl) {
@@ -9662,6 +9750,12 @@ async function webSearch(query, opts = {}) {
9662
9750
  if (opts.engine === "exa") {
9663
9751
  return searchExa(query, opts);
9664
9752
  }
9753
+ if (opts.engine === "ollama") {
9754
+ return searchOllama(query, opts);
9755
+ }
9756
+ if (opts.engine === "brave") {
9757
+ return searchBrave(query, opts);
9758
+ }
9665
9759
  return searchBing(query, opts);
9666
9760
  }
9667
9761
  async function searchBing(query, opts = {}) {
@@ -9961,6 +10055,154 @@ async function searchExa(query, opts = {}) {
9961
10055
  }
9962
10056
  return results;
9963
10057
  }
10058
+ async function searchOllama(query, opts = {}) {
10059
+ const topK = Math.max(1, Math.min(10, opts.topK ?? DEFAULT_TOPK));
10060
+ const apiKey = loadOllamaApiKey(opts.configPath);
10061
+ if (!apiKey) {
10062
+ throw new Error(
10063
+ "web_search: Ollama web search requires an API key \u2014 set OLLAMA_API_KEY, `ollamaApiKey`, or use /search-engine ollama <key>."
10064
+ );
10065
+ }
10066
+ let resp;
10067
+ try {
10068
+ resp = await fetch(OLLAMA_WEB_SEARCH_ENDPOINT, {
10069
+ method: "POST",
10070
+ headers: {
10071
+ Authorization: `Bearer ${apiKey}`,
10072
+ "Content-Type": "application/json",
10073
+ Accept: "application/json"
10074
+ },
10075
+ body: JSON.stringify({ query, max_results: topK }),
10076
+ signal: opts.signal
10077
+ });
10078
+ } catch (err) {
10079
+ if (err instanceof TypeError && err.message.includes("fetch")) {
10080
+ throw new Error(t("webErrors.cannotReach", { endpoint: OLLAMA_WEB_SEARCH_ENDPOINT }));
10081
+ }
10082
+ throw err;
10083
+ }
10084
+ if (!resp.ok) {
10085
+ if (resp.status === 401 || resp.status === 403) {
10086
+ throw new Error("web_search: Ollama API key rejected \u2014 check OLLAMA_API_KEY.");
10087
+ }
10088
+ if (resp.status === 429) {
10089
+ throw new Error("web_search: Ollama web search is rate-limited or quota-limited.");
10090
+ }
10091
+ throw new Error(`web_search: Ollama web search returned HTTP ${resp.status}.`);
10092
+ }
10093
+ let data;
10094
+ try {
10095
+ data = await resp.json();
10096
+ } catch {
10097
+ throw new Error(`web_search: Ollama returned unparseable response (HTTP ${resp.status}).`);
10098
+ }
10099
+ return (data.results ?? []).slice(0, topK).map((r, i) => ({
10100
+ title: r.title || `Result ${i + 1}`,
10101
+ url: r.url || "",
10102
+ snippet: r.content ?? ""
10103
+ }));
10104
+ }
10105
+ async function searchBrave(query, opts = {}) {
10106
+ const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
10107
+ const apiKey = loadBraveApiKey(opts.configPath);
10108
+ if (!apiKey) throw new Error(t("webErrors.braveMissingKey"));
10109
+ const url = `${BRAVE_ENDPOINT}?q=${encodeURIComponent(query)}&count=${topK}`;
10110
+ let resp;
10111
+ try {
10112
+ resp = await fetch(url, {
10113
+ headers: {
10114
+ Accept: "application/json",
10115
+ "Accept-Encoding": "gzip",
10116
+ "X-Subscription-Token": apiKey
10117
+ },
10118
+ signal: opts.signal
10119
+ });
10120
+ } catch (err) {
10121
+ if (err instanceof TypeError && err.message.includes("fetch")) {
10122
+ throw new Error(t("webErrors.cannotReach", { endpoint: BRAVE_ENDPOINT }));
10123
+ }
10124
+ throw err;
10125
+ }
10126
+ if (!resp.ok) {
10127
+ if (resp.status === 401 || resp.status === 403) {
10128
+ throw new Error(t("webErrors.braveUnauthorized"));
10129
+ }
10130
+ if (resp.status === 429) {
10131
+ throw new Error(t("webErrors.braveRateLimit"));
10132
+ }
10133
+ throw new Error(t("webErrors.braveServerError", { status: resp.status }));
10134
+ }
10135
+ const raw = await resp.text();
10136
+ let data;
10137
+ try {
10138
+ data = JSON.parse(raw);
10139
+ } catch {
10140
+ throw new Error(t("webErrors.braveParseError", { status: resp.status }));
10141
+ }
10142
+ const results = data.web?.results ?? [];
10143
+ return results.slice(0, topK).map((r) => ({
10144
+ title: r.title ?? "",
10145
+ url: r.url ?? "",
10146
+ snippet: r.description ?? ""
10147
+ }));
10148
+ }
10149
+ async function webFetchOllama(url, opts = {}) {
10150
+ const apiKey = loadOllamaApiKey(opts.configPath);
10151
+ if (!apiKey) {
10152
+ throw new Error(
10153
+ "web_fetch: Ollama web fetch requires an API key \u2014 set OLLAMA_API_KEY, `ollamaApiKey`, or use /search-engine ollama <key>."
10154
+ );
10155
+ }
10156
+ const ctrl = new AbortController();
10157
+ const timeout = setTimeout(
10158
+ () => ctrl.abort(
10159
+ new Error(
10160
+ t("webErrors.fetchTimeout", { ms: opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS, url })
10161
+ )
10162
+ ),
10163
+ opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS
10164
+ );
10165
+ const signal = opts.signal ? AbortSignal.any([opts.signal, ctrl.signal]) : ctrl.signal;
10166
+ let resp;
10167
+ try {
10168
+ resp = await fetch(OLLAMA_WEB_FETCH_ENDPOINT, {
10169
+ method: "POST",
10170
+ headers: {
10171
+ Authorization: `Bearer ${apiKey}`,
10172
+ "Content-Type": "application/json",
10173
+ Accept: "application/json"
10174
+ },
10175
+ body: JSON.stringify({ url }),
10176
+ signal
10177
+ });
10178
+ } finally {
10179
+ clearTimeout(timeout);
10180
+ }
10181
+ if (!resp.ok) {
10182
+ if (resp.status === 401 || resp.status === 403) {
10183
+ throw new Error("web_fetch: Ollama API key rejected \u2014 check OLLAMA_API_KEY.");
10184
+ }
10185
+ if (resp.status === 429) {
10186
+ throw new Error("web_fetch: Ollama web fetch is rate-limited or quota-limited.");
10187
+ }
10188
+ throw new Error(`web_fetch: Ollama web fetch returned HTTP ${resp.status} for ${url}.`);
10189
+ }
10190
+ let data;
10191
+ try {
10192
+ data = await resp.json();
10193
+ } catch {
10194
+ throw new Error(`web_fetch: Ollama returned unparseable response for ${url}.`);
10195
+ }
10196
+ const maxChars = opts.maxChars ?? DEFAULT_FETCH_MAX_CHARS;
10197
+ const text = data.content ?? "";
10198
+ return {
10199
+ url,
10200
+ title: data.title,
10201
+ text: text.length > maxChars ? text.slice(0, maxChars) : text,
10202
+ truncated: text.length > maxChars,
10203
+ links: data.links
10204
+ };
10205
+ }
9964
10206
  function parseSearxngHtmlResults(html) {
9965
10207
  const root = (0, import_node_html_parser.parse)(html);
9966
10208
  const results = [];
@@ -10186,13 +10428,14 @@ function registerWebTools(registry, opts = {}) {
10186
10428
  required: ["query"]
10187
10429
  },
10188
10430
  fn: async (args, ctx) => {
10189
- const engine = webSearchEngine();
10190
- const endpoint = webSearchEndpoint();
10431
+ const engine = webSearchEngine(opts.configPath);
10432
+ const endpoint = webSearchEndpoint(opts.configPath);
10191
10433
  const results = await webSearch(args.query, {
10192
10434
  topK: args.topK ?? defaultTopK,
10193
10435
  signal: ctx?.signal,
10194
10436
  engine,
10195
- endpoint
10437
+ endpoint,
10438
+ configPath: opts.configPath
10196
10439
  });
10197
10440
  return formatSearchResults(args.query, results);
10198
10441
  }
@@ -10213,6 +10456,22 @@ function registerWebTools(registry, opts = {}) {
10213
10456
  if (!/^https?:\/\//i.test(args.url)) {
10214
10457
  throw new Error(t("webErrors.fetchInvalidUrl"));
10215
10458
  }
10459
+ if (webSearchEngine(opts.configPath) === "ollama") {
10460
+ const page2 = await webFetchOllama(args.url, {
10461
+ maxChars: maxFetchChars,
10462
+ signal: ctx?.signal,
10463
+ configPath: opts.configPath
10464
+ });
10465
+ const header2 = page2.title ? `${page2.title}
10466
+ ${page2.url}` : page2.url;
10467
+ const links = page2.links?.length ? `
10468
+
10469
+ links:
10470
+ ${page2.links.join("\n")}` : "";
10471
+ return `${header2}
10472
+
10473
+ ${page2.text}${links}`;
10474
+ }
10216
10475
  const page = await webFetch(args.url, { maxChars: maxFetchChars, signal: ctx?.signal });
10217
10476
  const header = page.title ? `${page.title}
10218
10477
  ${page.url}` : page.url;
@@ -12592,6 +12851,7 @@ export {
12592
12851
  ToolRegistry,
12593
12852
  registerSingleMcpTool,
12594
12853
  bridgeMcpTools,
12854
+ errorMeta,
12595
12855
  isDeepSeekHost,
12596
12856
  ImmutablePrefix,
12597
12857
  CacheFirstLoop,
@@ -12626,4 +12886,4 @@ export {
12626
12886
  he/he.js:
12627
12887
  (*! https://mths.be/he v1.2.0 by @mathias | MIT license *)
12628
12888
  */
12629
- //# sourceMappingURL=chunk-HNZ4727T.js.map
12889
+ //# sourceMappingURL=chunk-DFHI2MRB.js.map