reasonix 0.46.0 → 0.47.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 (159) hide show
  1. package/README.md +64 -12
  2. package/README.zh-CN.md +54 -9
  3. package/dashboard/dist/app.js +293 -66
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/{acp-LGBLHBKY.js → acp-QK3DMC53.js} +22 -22
  6. package/dist/cli/chat-VV5UWY4V.js +51 -0
  7. package/dist/cli/{chunk-AVFXO2EZ.js → chunk-24A7FHGJ.js} +148 -16
  8. package/dist/cli/chunk-24A7FHGJ.js.map +1 -0
  9. package/dist/cli/chunk-25T6CVUP.js +0 -0
  10. package/dist/cli/chunk-2UQP6H6T.js +0 -0
  11. package/dist/cli/chunk-5QCB62C4.js +0 -0
  12. package/dist/cli/{chunk-YY227BIQ.js → chunk-6J6BSUCR.js} +2 -2
  13. package/dist/cli/chunk-6OWJV3YW.js +0 -0
  14. package/dist/cli/{chunk-A3TSSDS2.js → chunk-BWYVFFKR.js} +2 -2
  15. package/dist/cli/{chunk-C53JQES5.js → chunk-BYYVYJDX.js} +3 -3
  16. package/dist/cli/{chunk-HNXDZGC6.js → chunk-CI2PF5QX.js} +2 -2
  17. package/dist/cli/{chunk-GTZTQNX5.js → chunk-COWPEX54.js} +19 -9
  18. package/dist/cli/chunk-COWPEX54.js.map +1 -0
  19. package/dist/cli/{chunk-QJDDIK3Z.js → chunk-E5WCLUIU.js} +2 -2
  20. package/dist/cli/{chunk-NVURFF27.js → chunk-EQATK2L2.js} +2 -2
  21. package/dist/cli/{chunk-HKWSPKMU.js → chunk-FDKOUJKZ.js} +8 -8
  22. package/dist/cli/chunk-FEZK652I.js +0 -0
  23. package/dist/cli/{chunk-TEUDEGX2.js → chunk-FY4S7TJZ.js} +19 -5
  24. package/dist/cli/chunk-FY4S7TJZ.js.map +1 -0
  25. package/dist/cli/{chunk-RDRC3XDT.js → chunk-GDKB2PPK.js} +2 -2
  26. package/dist/cli/{chunk-XSU4QVFW.js → chunk-HIYTRCSW.js} +27 -14
  27. package/dist/cli/chunk-HIYTRCSW.js.map +1 -0
  28. package/dist/cli/{chunk-WL6SNQ5T.js → chunk-ICAFSZHS.js} +307 -114
  29. package/dist/cli/chunk-ICAFSZHS.js.map +1 -0
  30. package/dist/cli/{chunk-KQU2TYIL.js → chunk-ICSYGIPN.js} +1916 -1098
  31. package/dist/cli/chunk-ICSYGIPN.js.map +1 -0
  32. package/dist/cli/chunk-J5XJHLWM.js +0 -0
  33. package/dist/cli/chunk-JMBMLOBP.js +0 -0
  34. package/dist/cli/{chunk-MJ6W5UN3.js → chunk-K6GUKSXH.js} +3 -2
  35. package/dist/cli/chunk-K6GUKSXH.js.map +1 -0
  36. package/dist/cli/{chunk-IJ7JA32V.js → chunk-KDRUEXII.js} +189 -26
  37. package/dist/cli/chunk-KDRUEXII.js.map +1 -0
  38. package/dist/cli/{chunk-4HCP2UQW.js → chunk-LBLR4CUZ.js} +2 -2
  39. package/dist/cli/{chunk-2425HK6U.js → chunk-LGEKVMMV.js} +7 -2
  40. package/dist/cli/{chunk-2425HK6U.js.map → chunk-LGEKVMMV.js.map} +1 -1
  41. package/dist/cli/{chunk-I4L2GTSE.js → chunk-OJVITDGB.js} +2 -2
  42. package/dist/cli/chunk-PLHAZOLZ.js +0 -0
  43. package/dist/cli/{chunk-W7YGWUWU.js → chunk-QVDWH2A2.js} +3 -3
  44. package/dist/cli/{chunk-R3CTO2HM.js → chunk-QVUFWDD2.js} +2 -2
  45. package/dist/cli/{chunk-HVUZWNSP.js → chunk-R6GQKKBW.js} +2 -2
  46. package/dist/cli/{chunk-5ACMUK4Q.js → chunk-RRXUIPWG.js} +20 -18
  47. package/dist/cli/chunk-RRXUIPWG.js.map +1 -0
  48. package/dist/cli/chunk-S4XVGLRW.js +0 -0
  49. package/dist/cli/chunk-SZ5XES2N.js +0 -0
  50. package/dist/cli/{chunk-CXVWUPA3.js → chunk-TKVXTQ3T.js} +26 -26
  51. package/dist/cli/chunk-TKVXTQ3T.js.map +1 -0
  52. package/dist/cli/chunk-TUK7OWJA.js +0 -0
  53. package/dist/cli/{chunk-JNAQYELD.js → chunk-UDVFBEXC.js} +3 -3
  54. package/dist/cli/{chunk-CBIQWMS6.js → chunk-VC2CQA5D.js} +9 -9
  55. package/dist/cli/{chunk-ZZYBBX5N.js → chunk-VJMBISEI.js} +23 -9
  56. package/dist/cli/chunk-VJMBISEI.js.map +1 -0
  57. package/dist/cli/{chunk-WK3UFQY3.js → chunk-VKYSZKH2.js} +2 -2
  58. package/dist/cli/{chunk-LIR2HBQH.js → chunk-VMUUFWFF.js} +2 -2
  59. package/dist/cli/{chunk-V26WPN3J.js → chunk-VNQGCA3Q.js} +28 -1
  60. package/dist/cli/chunk-VNQGCA3Q.js.map +1 -0
  61. package/dist/cli/{chunk-5I2C4JEO.js → chunk-WF7TPVZM.js} +6 -6
  62. package/dist/cli/{chunk-5I2C4JEO.js.map → chunk-WF7TPVZM.js.map} +1 -1
  63. package/dist/cli/chunk-X53B3JIX.js +0 -0
  64. package/dist/cli/chunk-XJXDHAES.js +0 -0
  65. package/dist/cli/chunk-XXC2BYTV.js +0 -0
  66. package/dist/cli/{chunk-4CTDEJUF.js → chunk-YDPLF7XR.js} +26 -14
  67. package/dist/cli/chunk-YDPLF7XR.js.map +1 -0
  68. package/dist/cli/chunk-ZZM6QJ4W.js +0 -0
  69. package/dist/cli/{code-DFHSASJ4.js → code-C24TUAE5.js} +39 -35
  70. package/dist/cli/code-C24TUAE5.js.map +1 -0
  71. package/dist/cli/{commands-OCU42XG4.js → commands-RR3GIYOK.js} +4 -4
  72. package/dist/cli/{commit-XCQIQCYG.js → commit-FSHPIINM.js} +3 -3
  73. package/dist/cli/{desktop-ZCUG7LMF.js → desktop-7NCHPEFB.js} +263 -36
  74. package/dist/cli/desktop-7NCHPEFB.js.map +1 -0
  75. package/dist/cli/devtools-HW3WDT3Q.js +0 -0
  76. package/dist/cli/{diff-66B2KWOJ.js → diff-RAAHHLHV.js} +8 -8
  77. package/dist/cli/{doctor-Y73CPPRZ.js → doctor-PKVQIXRT.js} +9 -9
  78. package/dist/cli/{events-NGZ2OJYH.js → events-VRYXOSKI.js} +3 -3
  79. package/dist/cli/index.js +84 -92
  80. package/dist/cli/index.js.map +1 -1
  81. package/dist/cli/{mcp-MPVGBBJF.js → mcp-CRJ26PP4.js} +2 -2
  82. package/dist/cli/{mcp-browse-4XOTC3FJ.js → mcp-browse-QPAOWZOP.js} +2 -2
  83. package/dist/cli/{mcp-inspect-CEMGKKAH.js → mcp-inspect-CVCLABRS.js} +4 -4
  84. package/dist/cli/{prompt-2D7ID24X.js → prompt-SKYXERSI.js} +4 -4
  85. package/dist/cli/{prune-sessions-OJEYYLHY.js → prune-sessions-SEWX7GP6.js} +2 -2
  86. package/dist/cli/{replay-AKYQNAQJ.js → replay-KPDW2ZMJ.js} +9 -9
  87. package/dist/cli/{run-5DPQFSP6.js → run-WIKDIXTG.js} +18 -19
  88. package/dist/cli/run-WIKDIXTG.js.map +1 -0
  89. package/dist/cli/{server-TQ2IHYQJ.js → server-P6V2G3P6.js} +82 -34
  90. package/dist/cli/server-P6V2G3P6.js.map +1 -0
  91. package/dist/cli/{sessions-KY54NG45.js → sessions-2NULRMSA.js} +29 -15
  92. package/dist/cli/sessions-2NULRMSA.js.map +1 -0
  93. package/dist/cli/{setup-XPIOZWS7.js → setup-Y5WDBQFL.js} +8 -8
  94. package/dist/cli/setup-Y5WDBQFL.js.map +1 -0
  95. package/dist/cli/{stats-X2VTWKNS.js → stats-T7BL2YOR.js} +6 -6
  96. package/dist/cli/update-6ITLPRDV.js +0 -0
  97. package/dist/cli/{version-7O6A5T7Q.js → version-3KWDNWLN.js} +15 -15
  98. package/dist/index.d.ts +54 -23
  99. package/dist/index.js +1613 -1152
  100. package/dist/index.js.map +1 -1
  101. package/package.json +1 -1
  102. package/dist/cli/.-3G6VX5S7.js +0 -327
  103. package/dist/cli/.-6YRPB2C7.js +0 -329
  104. package/dist/cli/.-EYSVINK3.js +0 -317
  105. package/dist/cli/chat-ECK5ZGMV.js +0 -51
  106. package/dist/cli/chunk-4CTDEJUF.js.map +0 -1
  107. package/dist/cli/chunk-5ACMUK4Q.js.map +0 -1
  108. package/dist/cli/chunk-AVFXO2EZ.js.map +0 -1
  109. package/dist/cli/chunk-CXVWUPA3.js.map +0 -1
  110. package/dist/cli/chunk-GTZTQNX5.js.map +0 -1
  111. package/dist/cli/chunk-IJ7JA32V.js.map +0 -1
  112. package/dist/cli/chunk-KQU2TYIL.js.map +0 -1
  113. package/dist/cli/chunk-MJ6W5UN3.js.map +0 -1
  114. package/dist/cli/chunk-TEUDEGX2.js.map +0 -1
  115. package/dist/cli/chunk-V26WPN3J.js.map +0 -1
  116. package/dist/cli/chunk-WL6SNQ5T.js.map +0 -1
  117. package/dist/cli/chunk-XSU4QVFW.js.map +0 -1
  118. package/dist/cli/chunk-ZZYBBX5N.js.map +0 -1
  119. package/dist/cli/code-DFHSASJ4.js.map +0 -1
  120. package/dist/cli/desktop-ZCUG7LMF.js.map +0 -1
  121. package/dist/cli/doctor-Y73CPPRZ.js.map +0 -1
  122. package/dist/cli/prompt-2D7ID24X.js.map +0 -1
  123. package/dist/cli/run-5DPQFSP6.js.map +0 -1
  124. package/dist/cli/server-TQ2IHYQJ.js.map +0 -1
  125. package/dist/cli/sessions-KY54NG45.js.map +0 -1
  126. package/dist/cli/setup-XPIOZWS7.js.map +0 -1
  127. package/dist/cli/stats-X2VTWKNS.js.map +0 -1
  128. /package/dist/cli/{acp-LGBLHBKY.js.map → acp-QK3DMC53.js.map} +0 -0
  129. /package/dist/cli/{.-3G6VX5S7.js.map → chat-VV5UWY4V.js.map} +0 -0
  130. /package/dist/cli/{chunk-YY227BIQ.js.map → chunk-6J6BSUCR.js.map} +0 -0
  131. /package/dist/cli/{chunk-A3TSSDS2.js.map → chunk-BWYVFFKR.js.map} +0 -0
  132. /package/dist/cli/{chunk-C53JQES5.js.map → chunk-BYYVYJDX.js.map} +0 -0
  133. /package/dist/cli/{chunk-HNXDZGC6.js.map → chunk-CI2PF5QX.js.map} +0 -0
  134. /package/dist/cli/{chunk-QJDDIK3Z.js.map → chunk-E5WCLUIU.js.map} +0 -0
  135. /package/dist/cli/{chunk-NVURFF27.js.map → chunk-EQATK2L2.js.map} +0 -0
  136. /package/dist/cli/{chunk-HKWSPKMU.js.map → chunk-FDKOUJKZ.js.map} +0 -0
  137. /package/dist/cli/{chunk-RDRC3XDT.js.map → chunk-GDKB2PPK.js.map} +0 -0
  138. /package/dist/cli/{chunk-4HCP2UQW.js.map → chunk-LBLR4CUZ.js.map} +0 -0
  139. /package/dist/cli/{chunk-I4L2GTSE.js.map → chunk-OJVITDGB.js.map} +0 -0
  140. /package/dist/cli/{chunk-W7YGWUWU.js.map → chunk-QVDWH2A2.js.map} +0 -0
  141. /package/dist/cli/{chunk-R3CTO2HM.js.map → chunk-QVUFWDD2.js.map} +0 -0
  142. /package/dist/cli/{chunk-HVUZWNSP.js.map → chunk-R6GQKKBW.js.map} +0 -0
  143. /package/dist/cli/{chunk-JNAQYELD.js.map → chunk-UDVFBEXC.js.map} +0 -0
  144. /package/dist/cli/{chunk-CBIQWMS6.js.map → chunk-VC2CQA5D.js.map} +0 -0
  145. /package/dist/cli/{chunk-WK3UFQY3.js.map → chunk-VKYSZKH2.js.map} +0 -0
  146. /package/dist/cli/{chunk-LIR2HBQH.js.map → chunk-VMUUFWFF.js.map} +0 -0
  147. /package/dist/cli/{commands-OCU42XG4.js.map → commands-RR3GIYOK.js.map} +0 -0
  148. /package/dist/cli/{commit-XCQIQCYG.js.map → commit-FSHPIINM.js.map} +0 -0
  149. /package/dist/cli/{diff-66B2KWOJ.js.map → diff-RAAHHLHV.js.map} +0 -0
  150. /package/dist/cli/{.-6YRPB2C7.js.map → doctor-PKVQIXRT.js.map} +0 -0
  151. /package/dist/cli/{events-NGZ2OJYH.js.map → events-VRYXOSKI.js.map} +0 -0
  152. /package/dist/cli/{mcp-MPVGBBJF.js.map → mcp-CRJ26PP4.js.map} +0 -0
  153. /package/dist/cli/{mcp-browse-4XOTC3FJ.js.map → mcp-browse-QPAOWZOP.js.map} +0 -0
  154. /package/dist/cli/{mcp-inspect-CEMGKKAH.js.map → mcp-inspect-CVCLABRS.js.map} +0 -0
  155. /package/dist/cli/{.-EYSVINK3.js.map → prompt-SKYXERSI.js.map} +0 -0
  156. /package/dist/cli/{prune-sessions-OJEYYLHY.js.map → prune-sessions-SEWX7GP6.js.map} +0 -0
  157. /package/dist/cli/{replay-AKYQNAQJ.js.map → replay-KPDW2ZMJ.js.map} +0 -0
  158. /package/dist/cli/{chat-ECK5ZGMV.js.map → stats-T7BL2YOR.js.map} +0 -0
  159. /package/dist/cli/{version-7O6A5T7Q.js.map → version-3KWDNWLN.js.map} +0 -0
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
3
3
  import {
4
4
  MemoryStore,
5
5
  sanitizeMemoryName
6
- } from "./chunk-JNAQYELD.js";
6
+ } from "./chunk-UDVFBEXC.js";
7
7
  import {
8
8
  countTokens,
9
9
  countTokensBounded,
@@ -12,23 +12,23 @@ import {
12
12
  } from "./chunk-6OWJV3YW.js";
13
13
  import {
14
14
  Usage
15
- } from "./chunk-V26WPN3J.js";
15
+ } from "./chunk-VNQGCA3Q.js";
16
16
  import {
17
17
  applyEdit,
18
18
  applyMultiEdit,
19
19
  pauseGate
20
- } from "./chunk-RDRC3XDT.js";
20
+ } from "./chunk-GDKB2PPK.js";
21
21
  import {
22
22
  NEGATIVE_CLAIM_RULE,
23
23
  PROJECT_MEMORY_FILES,
24
24
  PROJECT_MEMORY_MAX_CHARS,
25
25
  TUI_FORMATTING_RULES,
26
26
  memoryEnabled
27
- } from "./chunk-TEUDEGX2.js";
27
+ } from "./chunk-FY4S7TJZ.js";
28
28
  import {
29
29
  formatHookOutcomeMessage,
30
30
  runHooks
31
- } from "./chunk-A3TSSDS2.js";
31
+ } from "./chunk-BWYVFFKR.js";
32
32
  import {
33
33
  ignoredByLayers,
34
34
  loadGitignoreAt,
@@ -41,25 +41,26 @@ import {
41
41
  loadSessionMeta,
42
42
  rewriteSession,
43
43
  timestampSuffix
44
- } from "./chunk-5ACMUK4Q.js";
44
+ } from "./chunk-RRXUIPWG.js";
45
45
  import {
46
46
  DEEPSEEK_CONTEXT_TOKENS,
47
47
  DEFAULT_CONTEXT_TOKENS,
48
48
  SessionStats
49
- } from "./chunk-ZZYBBX5N.js";
49
+ } from "./chunk-VJMBISEI.js";
50
50
  import {
51
51
  t
52
- } from "./chunk-IJ7JA32V.js";
52
+ } from "./chunk-KDRUEXII.js";
53
53
  import {
54
54
  DEFAULT_INDEX_EXCLUDES,
55
55
  addProjectPathAllowed,
56
56
  loadMemoryTypeRegistry,
57
57
  loadMetasoApiKey,
58
58
  loadProjectPathAllowed,
59
+ loadTavilyApiKey,
59
60
  require_picomatch,
60
61
  webSearchEndpoint,
61
62
  webSearchEngine
62
- } from "./chunk-AVFXO2EZ.js";
63
+ } from "./chunk-24A7FHGJ.js";
63
64
  import {
64
65
  __commonJS,
65
66
  __esm,
@@ -6124,11 +6125,42 @@ async function bridgeMcpTools(client, opts = {}) {
6124
6125
  return { ...result, env };
6125
6126
  }
6126
6127
  function flattenMcpResult(result, opts = {}) {
6128
+ validateResultShape(result);
6127
6129
  const parts = result.content.map(blockToString);
6128
6130
  const joined = parts.join("\n").trim();
6129
6131
  const prefixed = result.isError ? `ERROR: ${joined || "(no error message from server)"}` : joined;
6130
6132
  return opts.maxChars ? truncateForModel(prefixed, opts.maxChars) : prefixed;
6131
6133
  }
6134
+ function validateResultShape(result) {
6135
+ if (typeof result !== "object" || !result)
6136
+ throw new Error(`MCP server returned non-object result: ${typeof result}`);
6137
+ const { content, isError: _isError } = result;
6138
+ if (!Array.isArray(content))
6139
+ throw new Error(`MCP server returned result with non-array content: ${typeof content}`);
6140
+ for (let i = 0; i < content.length; i++) {
6141
+ const block = content[i];
6142
+ if (typeof block !== "object" || !block)
6143
+ throw new Error(`MCP server returned result.content[${i}] is not an object`);
6144
+ if (block.type !== "text" && block.type !== "image")
6145
+ throw new Error(
6146
+ `MCP server returned result.content[${i}] with unknown type ${JSON.stringify(block.type)}`
6147
+ );
6148
+ if (block.type === "text" && typeof block.text !== "string")
6149
+ throw new Error(
6150
+ `MCP server returned result.content[${i}] with non-string text (${typeof block.text})`
6151
+ );
6152
+ if (block.type === "image") {
6153
+ if (typeof block.data !== "string")
6154
+ throw new Error(
6155
+ `MCP server returned result.content[${i}] with non-string data (${typeof block.data})`
6156
+ );
6157
+ if (typeof block.mimeType !== "string")
6158
+ throw new Error(
6159
+ `MCP server returned result.content[${i}] with non-string mimeType (${typeof block.mimeType})`
6160
+ );
6161
+ }
6162
+ }
6163
+ }
6132
6164
  function truncateForModel(s, maxChars) {
6133
6165
  if (s.length <= maxChars) return s;
6134
6166
  const tailBudget = Math.min(1024, Math.floor(maxChars * 0.1));
@@ -6394,6 +6426,12 @@ var ToolRegistry = class {
6394
6426
  });
6395
6427
  }
6396
6428
  }
6429
+ if (opts.signal?.aborted) {
6430
+ return JSON.stringify({
6431
+ error: `${name}: aborted before dispatch (user interrupt)`,
6432
+ rejectedReason: "aborted"
6433
+ });
6434
+ }
6397
6435
  let finalResult;
6398
6436
  try {
6399
6437
  try {
@@ -7180,42 +7218,6 @@ function* hookWarnings(outcomes, turn) {
7180
7218
  }
7181
7219
  }
7182
7220
 
7183
- // src/loop/turn-failure-tracker.ts
7184
- var FAILURE_ESCALATION_THRESHOLD = 3;
7185
- var TurnFailureTracker = class {
7186
- count = 0;
7187
- types = {};
7188
- threshold;
7189
- constructor(threshold = FAILURE_ESCALATION_THRESHOLD) {
7190
- this.threshold = threshold;
7191
- }
7192
- reset() {
7193
- this.count = 0;
7194
- this.types = {};
7195
- }
7196
- /** True ONLY on the call where the count crosses the configured threshold. */
7197
- noteAndCrossedThreshold(resultJson, repair) {
7198
- const before = this.count;
7199
- const bump = (kind, by = 1) => {
7200
- this.count += by;
7201
- this.types[kind] = (this.types[kind] ?? 0) + by;
7202
- };
7203
- if (resultJson.includes('"error"') && resultJson.includes("search text not found")) {
7204
- bump("search-mismatch");
7205
- }
7206
- if (repair) {
7207
- if (repair.scavenged > 0) bump("scavenged", repair.scavenged);
7208
- if (repair.truncationsFixed > 0) bump("truncated", repair.truncationsFixed);
7209
- if (repair.stormsBroken > 0) bump("repeat-loop", repair.stormsBroken);
7210
- }
7211
- return before < this.threshold && this.count >= this.threshold;
7212
- }
7213
- formatBreakdown() {
7214
- const parts = Object.entries(this.types).filter(([, n]) => n > 0).map(([kind, n]) => `${n}\xD7 ${kind}`);
7215
- return parts.length > 0 ? parts.join(", ") : `${this.count} repair/error signal(s)`;
7216
- }
7217
- };
7218
-
7219
7221
  // src/repair/scavenge.ts
7220
7222
  var MAX_SCAVENGE_INPUT = 100 * 1024;
7221
7223
  function scavengeToolCalls(reasoningContent, opts) {
@@ -7595,7 +7597,6 @@ var CacheFirstLoop = class {
7595
7597
  }
7596
7598
  _proArmedForNextTurn = false;
7597
7599
  _escalateThisTurn = false;
7598
- _turnFailures;
7599
7600
  _turnSelfCorrected = false;
7600
7601
  _foldedThisTurn = false;
7601
7602
  context;
@@ -7614,9 +7615,6 @@ var CacheFirstLoop = class {
7614
7615
  this.reasoningEffort = opts.reasoningEffort ?? "max";
7615
7616
  if (opts.autoEscalate !== void 0) this.autoEscalate = opts.autoEscalate;
7616
7617
  this.budgetUsd = typeof opts.budgetUsd === "number" && opts.budgetUsd > 0 ? opts.budgetUsd : null;
7617
- this._turnFailures = new TurnFailureTracker(
7618
- resolveFailureThreshold(opts.failureThreshold, FAILURE_ESCALATION_THRESHOLD)
7619
- );
7620
7618
  this.hooks = opts.hooks ?? [];
7621
7619
  this.hookCwd = opts.hookCwd ?? process.cwd();
7622
7620
  this.confirmationGate = opts.confirmationGate ?? pauseGate;
@@ -7724,7 +7722,6 @@ var CacheFirstLoop = class {
7724
7722
  this._inflight.clear();
7725
7723
  this.stats.reset();
7726
7724
  this._turn = 0;
7727
- this._turnFailures.reset();
7728
7725
  this._budgetWarned = false;
7729
7726
  let systemRebuilt = false;
7730
7727
  if (this._rebuildSystem) {
@@ -7795,13 +7792,6 @@ var CacheFirstLoop = class {
7795
7792
  modelForCurrentCall() {
7796
7793
  return this._escalateThisTurn ? ESCALATION_MODEL : this.model;
7797
7794
  }
7798
- /** Returns true ONLY on the tipping call — caller surfaces a one-shot warning. */
7799
- noteToolFailureSignal(resultJson, repair) {
7800
- if (!this._turnFailures.noteAndCrossedThreshold(resultJson, repair)) return false;
7801
- if (this._escalateThisTurn || !this.autoEscalate) return false;
7802
- this._escalateThisTurn = true;
7803
- return true;
7804
- }
7805
7795
  /** A call counts as mutating when its definition reports `readOnly !== true` and any dynamic `readOnlyCheck` doesn't override that for these args. */
7806
7796
  isMutating(call) {
7807
7797
  const name = call.function?.name;
@@ -7925,6 +7915,32 @@ ${reason}`
7925
7915
  }
7926
7916
  return userText;
7927
7917
  }
7918
+ /** Rewind to the N-th user turn (0-indexed). Drops that turn + everything after. */
7919
+ rewindToUserTurn(userTurnIndex) {
7920
+ const entries = this.log.entries;
7921
+ let count = 0;
7922
+ let targetIdx = -1;
7923
+ for (let i = 0; i < entries.length; i++) {
7924
+ if (entries[i].role !== "user") continue;
7925
+ if (count === userTurnIndex) {
7926
+ targetIdx = i;
7927
+ break;
7928
+ }
7929
+ count++;
7930
+ }
7931
+ if (targetIdx < 0) return null;
7932
+ const raw = entries[targetIdx].content;
7933
+ const userText = typeof raw === "string" ? raw : "";
7934
+ const preserved = entries.slice(0, targetIdx).map((m) => ({ ...m }));
7935
+ this.log.compactInPlace(preserved);
7936
+ if (this.sessionName) {
7937
+ try {
7938
+ rewriteSession(this.sessionName, preserved);
7939
+ } catch {
7940
+ }
7941
+ }
7942
+ return userText;
7943
+ }
7928
7944
  async *step(userInput) {
7929
7945
  this._steerConsumed = false;
7930
7946
  if (this.budgetUsd !== null) {
@@ -7956,7 +7972,6 @@ ${reason}`
7956
7972
  this._turn++;
7957
7973
  this.scratch.reset();
7958
7974
  this.repair.resetStorm();
7959
- this._turnFailures.reset();
7960
7975
  this._turnSelfCorrected = false;
7961
7976
  this._escalateThisTurn = false;
7962
7977
  this._foldedThisTurn = false;
@@ -8227,17 +8242,6 @@ ${reason}`
8227
8242
  stats: turnStats,
8228
8243
  repair: report
8229
8244
  };
8230
- if (this.noteToolFailureSignal("", report)) {
8231
- yield {
8232
- turn: this._turn,
8233
- role: "warning",
8234
- content: t("loop.autoEscalation", {
8235
- model: ESCALATION_MODEL,
8236
- breakdown: this._turnFailures.formatBreakdown(),
8237
- fallback: this.model
8238
- })
8239
- };
8240
- }
8241
8245
  const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
8242
8246
  if (allSuppressed && !this._turnSelfCorrected) {
8243
8247
  this._turnSelfCorrected = true;
@@ -8377,17 +8381,6 @@ ${reason}`
8377
8381
  name,
8378
8382
  content: result
8379
8383
  });
8380
- if (this.noteToolFailureSignal(result)) {
8381
- yield {
8382
- turn: this._turn,
8383
- role: "warning",
8384
- content: t("loop.autoEscalation", {
8385
- model: ESCALATION_MODEL,
8386
- breakdown: this._turnFailures.formatBreakdown(),
8387
- fallback: this.model
8388
- })
8389
- };
8390
- }
8391
8384
  yield {
8392
8385
  turn: this._turn,
8393
8386
  role: "tool",
@@ -8425,19 +8418,6 @@ function parsePositiveIntEnv(raw) {
8425
8418
  const n = Number.parseInt(raw, 10);
8426
8419
  return Number.isFinite(n) && n > 0 ? n : void 0;
8427
8420
  }
8428
- var FAILURE_THRESHOLD_MIN = 1;
8429
- var FAILURE_THRESHOLD_MAX = 20;
8430
- function resolveFailureThreshold(raw, fallback) {
8431
- if (raw === void 0) return fallback;
8432
- if (!Number.isInteger(raw) || raw < FAILURE_THRESHOLD_MIN || raw > FAILURE_THRESHOLD_MAX) {
8433
- process.stderr.write(
8434
- `\u25B2 ignoring escalation failureThreshold=${raw} (must be an integer in [${FAILURE_THRESHOLD_MIN},${FAILURE_THRESHOLD_MAX}]) \u2014 using default ${fallback}
8435
- `
8436
- );
8437
- return fallback;
8438
- }
8439
- return raw;
8440
- }
8441
8421
 
8442
8422
  // src/at-mentions.ts
8443
8423
  import { existsSync, readFileSync, readdirSync, statSync } from "fs";
@@ -9241,6 +9221,7 @@ var FETCH_MAX_BYTES = 10 * 1024 * 1024;
9241
9221
  var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
9242
9222
  var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
9243
9223
  var METASO_ENDPOINT = "https://metaso.cn/api/v1";
9224
+ var TAVILY_ENDPOINT = "https://api.tavily.com/search";
9244
9225
  function searchStatusError(status) {
9245
9226
  if (status === 429) return t("webErrors.rateLimit429");
9246
9227
  if (status === 403) return t("webErrors.forbidden403");
@@ -9260,6 +9241,9 @@ async function webSearch(query, opts = {}) {
9260
9241
  if (opts.engine === "searxng") {
9261
9242
  return searchSearxng(query, opts);
9262
9243
  }
9244
+ if (opts.engine === "tavily") {
9245
+ return searchTavily(query, opts);
9246
+ }
9263
9247
  return searchMojeek(query, opts);
9264
9248
  }
9265
9249
  async function searchMojeek(query, opts = {}) {
@@ -9394,6 +9378,55 @@ async function searchMetaso(query, opts = {}) {
9394
9378
  snippet: wp.snippet ?? wp.summary ?? ""
9395
9379
  }));
9396
9380
  }
9381
+ async function searchTavily(query, opts = {}) {
9382
+ const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
9383
+ const apiKey = loadTavilyApiKey();
9384
+ if (!apiKey) throw new Error(t("webErrors.tavilyMissingKey"));
9385
+ let resp;
9386
+ try {
9387
+ resp = await fetch(TAVILY_ENDPOINT, {
9388
+ method: "POST",
9389
+ headers: {
9390
+ "Content-Type": "application/json",
9391
+ Accept: "application/json"
9392
+ },
9393
+ body: JSON.stringify({
9394
+ api_key: apiKey,
9395
+ query,
9396
+ search_depth: "basic",
9397
+ max_results: topK,
9398
+ include_answer: false,
9399
+ include_raw_content: false,
9400
+ include_images: false
9401
+ }),
9402
+ signal: opts.signal
9403
+ });
9404
+ } catch (err) {
9405
+ if (err instanceof TypeError && err.message.includes("fetch")) {
9406
+ throw new Error(t("webErrors.cannotReach", { endpoint: TAVILY_ENDPOINT }));
9407
+ }
9408
+ throw err;
9409
+ }
9410
+ if (!resp.ok) {
9411
+ if (resp.status === 401 || resp.status === 403) {
9412
+ throw new Error(t("webErrors.tavilyUnauthorized"));
9413
+ }
9414
+ if (resp.status === 429) throw new Error(t("webErrors.tavilyRateLimit"));
9415
+ throw new Error(t("webErrors.tavilyServerError", { status: resp.status }));
9416
+ }
9417
+ let data;
9418
+ try {
9419
+ data = await resp.json();
9420
+ } catch {
9421
+ throw new Error(t("webErrors.tavilyParseError", { status: resp.status }));
9422
+ }
9423
+ const results = data.results ?? [];
9424
+ return results.slice(0, topK).map((r) => ({
9425
+ title: r.title,
9426
+ url: r.url,
9427
+ snippet: r.content ?? ""
9428
+ }));
9429
+ }
9397
9430
  function parseSearxngHtmlResults(html) {
9398
9431
  const root = (0, import_node_html_parser.parse)(html);
9399
9432
  const results = [];
@@ -9612,7 +9645,7 @@ function registerWebTools(registry, opts = {}) {
9612
9645
  const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
9613
9646
  registry.register({
9614
9647
  name: "web_search",
9615
- description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso.",
9648
+ description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso|tavily.",
9616
9649
  readOnly: true,
9617
9650
  parallelSafe: true,
9618
9651
  parameters: {
@@ -9684,13 +9717,13 @@ import * as pathMod4 from "path";
9684
9717
  // src/memory/subdir.ts
9685
9718
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
9686
9719
  import { dirname, join as join2, relative as relative2, resolve as resolve2 } from "path";
9687
- function findSubdirMemoryAncestors(absPath, rootDir) {
9720
+ function findDirMemory(absDir, rootDir) {
9688
9721
  const root = resolve2(rootDir);
9689
- const target = resolve2(absPath);
9722
+ const target = resolve2(absDir);
9690
9723
  const rel = relative2(root, target);
9691
- if (!rel || rel.startsWith("..")) return [];
9724
+ if (rel.startsWith("..")) return [];
9692
9725
  const found = [];
9693
- let cur = dirname(target);
9726
+ let cur = target;
9694
9727
  while (cur !== root) {
9695
9728
  const r = relative2(root, cur);
9696
9729
  if (!r || r.startsWith("..")) break;
@@ -9707,6 +9740,9 @@ function findSubdirMemoryAncestors(absPath, rootDir) {
9707
9740
  }
9708
9741
  return found;
9709
9742
  }
9743
+ function findSubdirMemoryAncestors(absPath, rootDir) {
9744
+ return findDirMemory(dirname(resolve2(absPath)), rootDir);
9745
+ }
9710
9746
  function readSubdirMemoryContent(path) {
9711
9747
  let raw;
9712
9748
  try {
@@ -9975,6 +10011,129 @@ function formatOutline(entries) {
9975
10011
  // src/tools/fs/search.ts
9976
10012
  import { promises as fs2 } from "fs";
9977
10013
  import * as pathMod3 from "path";
10014
+
10015
+ // src/tools/fs/regex-runner.ts
10016
+ import { Worker } from "worker_threads";
10017
+ var WORKER_SOURCE = `
10018
+ const { parentPort } = require("node:worker_threads");
10019
+ parentPort.on("message", (msg) => {
10020
+ const { id, text, source, flags } = msg;
10021
+ let re;
10022
+ try {
10023
+ re = new RegExp(source, flags);
10024
+ } catch (err) {
10025
+ parentPort.postMessage({ id, error: (err && err.message) ? err.message : String(err) });
10026
+ return;
10027
+ }
10028
+ const lines = text.split(/\\r?\\n/);
10029
+ const hits = [];
10030
+ for (let i = 0; i < lines.length; i++) {
10031
+ if (re.test(lines[i])) hits.push(i);
10032
+ }
10033
+ parentPort.postMessage({ id, hits });
10034
+ });
10035
+ `;
10036
+ var DEFAULT_TIMEOUT_MS = 6e4;
10037
+ var RegexRunner = class {
10038
+ worker = null;
10039
+ pending = /* @__PURE__ */ new Map();
10040
+ nextId = 1;
10041
+ defaultTimeoutMs;
10042
+ constructor(opts = {}) {
10043
+ this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
10044
+ }
10045
+ testLines(text, source, flags, opts = {}) {
10046
+ return new Promise((resolve5, reject) => {
10047
+ if (opts.signal?.aborted) {
10048
+ reject(new Error("regex evaluation aborted"));
10049
+ return;
10050
+ }
10051
+ if (!this.worker) this.worker = this.spawn();
10052
+ const id = this.nextId++;
10053
+ const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
10054
+ const timer = setTimeout(() => {
10055
+ this.pending.delete(id);
10056
+ this.killWorker();
10057
+ reject(new Error(`regex evaluation exceeded ${timeoutMs}ms`));
10058
+ }, timeoutMs);
10059
+ const entry = { resolve: resolve5, reject, timer };
10060
+ if (opts.signal) {
10061
+ entry.signal = opts.signal;
10062
+ entry.onAbort = () => {
10063
+ this.pending.delete(id);
10064
+ clearTimeout(timer);
10065
+ this.killWorker();
10066
+ reject(new Error("regex evaluation aborted"));
10067
+ };
10068
+ opts.signal.addEventListener("abort", entry.onAbort, { once: true });
10069
+ }
10070
+ this.pending.set(id, entry);
10071
+ this.worker.postMessage({ id, text, source, flags });
10072
+ });
10073
+ }
10074
+ async shutdown() {
10075
+ if (this.worker) {
10076
+ const w = this.worker;
10077
+ this.worker = null;
10078
+ await w.terminate();
10079
+ }
10080
+ for (const entry of this.pending.values()) {
10081
+ clearTimeout(entry.timer);
10082
+ if (entry.onAbort && entry.signal) {
10083
+ entry.signal.removeEventListener("abort", entry.onAbort);
10084
+ }
10085
+ entry.reject(new Error("regex runner shut down"));
10086
+ }
10087
+ this.pending.clear();
10088
+ }
10089
+ spawn() {
10090
+ const w = new Worker(WORKER_SOURCE, { eval: true });
10091
+ w.on("message", (msg) => {
10092
+ const entry = this.pending.get(msg.id);
10093
+ if (!entry) return;
10094
+ clearTimeout(entry.timer);
10095
+ if (entry.onAbort && entry.signal) {
10096
+ entry.signal.removeEventListener("abort", entry.onAbort);
10097
+ }
10098
+ this.pending.delete(msg.id);
10099
+ if (msg.error !== void 0) entry.reject(new Error(msg.error));
10100
+ else entry.resolve(msg.hits ?? []);
10101
+ });
10102
+ w.on("error", (err) => {
10103
+ if (this.worker !== w) return;
10104
+ this.failPending(err);
10105
+ });
10106
+ w.on("exit", () => {
10107
+ if (this.worker !== w) return;
10108
+ this.worker = null;
10109
+ if (this.pending.size > 0) this.failPending(new Error("regex worker exited"));
10110
+ });
10111
+ return w;
10112
+ }
10113
+ killWorker() {
10114
+ if (!this.worker) return;
10115
+ const w = this.worker;
10116
+ this.worker = null;
10117
+ void w.terminate();
10118
+ }
10119
+ failPending(err) {
10120
+ for (const entry of this.pending.values()) {
10121
+ clearTimeout(entry.timer);
10122
+ if (entry.onAbort && entry.signal) {
10123
+ entry.signal.removeEventListener("abort", entry.onAbort);
10124
+ }
10125
+ entry.reject(err);
10126
+ }
10127
+ this.pending.clear();
10128
+ }
10129
+ };
10130
+ var _runner = null;
10131
+ function getRegexRunner() {
10132
+ if (!_runner) _runner = new RegexRunner();
10133
+ return _runner;
10134
+ }
10135
+
10136
+ // src/tools/fs/search.ts
9978
10137
  function throwIfAborted(signal) {
9979
10138
  if (!signal?.aborted) return;
9980
10139
  throw new DOMException("search aborted by user", "AbortError");
@@ -10027,17 +10186,20 @@ async function searchFiles(ctx, startAbs, args) {
10027
10186
  }
10028
10187
  var MAX_HITS_PER_FILE = 30;
10029
10188
  var SUMMARY_MODE_TRIGGER_RATIO = 0.8;
10189
+ var WALK_DEADLINE_MS = 12e4;
10030
10190
  async function searchContent(ctx, startAbs, args) {
10031
10191
  throwIfAborted(args.signal);
10032
10192
  const caseSensitive = args.case_sensitive === true;
10033
10193
  const includeDeps = args.include_deps === true;
10034
10194
  const ctxLines = Math.max(0, Math.min(20, Math.floor(args.context ?? 0)));
10035
10195
  const summaryOnly = args.summary_only === true;
10036
- let re = null;
10196
+ const reFlags = caseSensitive ? "" : "i";
10197
+ let reSource = null;
10037
10198
  try {
10038
- re = new RegExp(args.pattern, caseSensitive ? "" : "i");
10199
+ new RegExp(args.pattern, reFlags);
10200
+ reSource = args.pattern;
10039
10201
  } catch {
10040
- re = null;
10202
+ reSource = null;
10041
10203
  }
10042
10204
  const needle = caseSensitive ? args.pattern : args.pattern.toLowerCase();
10043
10205
  const matches = [];
@@ -10047,6 +10209,15 @@ async function searchContent(ctx, startAbs, args) {
10047
10209
  let summaryMode = summaryOnly;
10048
10210
  let summaryNoticeEmitted = false;
10049
10211
  const fileHitCounts = /* @__PURE__ */ new Map();
10212
+ const regexSkippedFiles = [];
10213
+ const t0 = Date.now();
10214
+ const throwIfTimedOut = () => {
10215
+ if (Date.now() - t0 > WALK_DEADLINE_MS) {
10216
+ throw new Error(
10217
+ `search_content exceeded ${WALK_DEADLINE_MS}ms \u2014 narrow the scope (path/glob) or simplify the pattern`
10218
+ );
10219
+ }
10220
+ };
10050
10221
  const pushLine = (out) => {
10051
10222
  if (totalBytes + out.length + 1 > ctx.maxListBytes) {
10052
10223
  matches.push(`[\u2026 truncated at ${ctx.maxListBytes} bytes \u2014 refine pattern or path \u2026]`);
@@ -10081,6 +10252,7 @@ async function searchContent(ctx, startAbs, args) {
10081
10252
  for (const e of entries) {
10082
10253
  if (truncated) return;
10083
10254
  throwIfAborted(args.signal);
10255
+ throwIfTimedOut();
10084
10256
  if (e.isDirectory()) {
10085
10257
  if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
10086
10258
  await walk2(pathMod3.join(dir, e.name));
@@ -10117,13 +10289,25 @@ async function searchContent(ctx, startAbs, args) {
10117
10289
  const text = raw.toString("utf8");
10118
10290
  const rel = displayRel2(ctx.rootDir, full);
10119
10291
  const lines = text.split(/\r?\n/);
10120
- const hits = [];
10121
- for (let li = 0; li < lines.length; li++) {
10122
- throwIfAborted(args.signal);
10123
- const line = lines[li];
10124
- const lineForCheck = caseSensitive ? line : line.toLowerCase();
10125
- const hit = re ? re.test(line) : lineForCheck.includes(needle);
10126
- if (hit) hits.push(li);
10292
+ let hits;
10293
+ if (reSource !== null) {
10294
+ try {
10295
+ hits = await getRegexRunner().testLines(text, reSource, reFlags, {
10296
+ signal: args.signal
10297
+ });
10298
+ } catch (err) {
10299
+ const reason = err.message;
10300
+ if (reason.includes("aborted")) throw err;
10301
+ regexSkippedFiles.push({ rel, reason });
10302
+ continue;
10303
+ }
10304
+ } else {
10305
+ hits = [];
10306
+ for (let li = 0; li < lines.length; li++) {
10307
+ throwIfAborted(args.signal);
10308
+ const lineForCheck = caseSensitive ? lines[li] : lines[li].toLowerCase();
10309
+ if (lineForCheck.includes(needle)) hits.push(li);
10310
+ }
10127
10311
  }
10128
10312
  scanned++;
10129
10313
  if (hits.length === 0) continue;
@@ -10172,6 +10356,11 @@ async function searchContent(ctx, startAbs, args) {
10172
10356
  }
10173
10357
  };
10174
10358
  await walk2(startAbs);
10359
+ if (regexSkippedFiles.length > 0) {
10360
+ pushLine(
10361
+ `[regex timed out on ${regexSkippedFiles.length} file${regexSkippedFiles.length === 1 ? "" : "s"} \u2014 pattern may have catastrophic backtracking; first: ${regexSkippedFiles[0].rel}]`
10362
+ );
10363
+ }
10175
10364
  if (matches.length === 0) {
10176
10365
  return scanned === 0 ? "(no files scanned \u2014 path empty or all files filtered out)" : `(no matches across ${scanned} file${scanned === 1 ? "" : "s"})`;
10177
10366
  }
@@ -10236,11 +10425,15 @@ function registerFilesystemTools(registry, opts) {
10236
10425
  const sessionApproved = /* @__PURE__ */ new Set();
10237
10426
  const shownSubdirMemory = /* @__PURE__ */ new Set();
10238
10427
  function withSubdirMemory(absPath, body) {
10239
- if (!memoryEnabled()) return body;
10240
- const ancestors = findSubdirMemoryAncestors(absPath, rootDir);
10241
- if (ancestors.length === 0) return body;
10428
+ return prependMemorySections(findSubdirMemoryAncestors(absPath, rootDir), body);
10429
+ }
10430
+ function withDirMemory(absDir, body) {
10431
+ return prependMemorySections(findDirMemory(absDir, rootDir), body);
10432
+ }
10433
+ function prependMemorySections(memPaths, body) {
10434
+ if (!memoryEnabled() || memPaths.length === 0) return body;
10242
10435
  const sections = [];
10243
- for (const memPath of [...ancestors].reverse()) {
10436
+ for (const memPath of [...memPaths].reverse()) {
10244
10437
  if (shownSubdirMemory.has(memPath)) continue;
10245
10438
  const content = readSubdirMemoryContent(memPath);
10246
10439
  if (!content) continue;
@@ -10433,7 +10626,7 @@ ${slice.join("\n")}`);
10433
10626
  for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
10434
10627
  lines.push(e.isDirectory() ? `${e.name}/` : e.name);
10435
10628
  }
10436
- return lines.join("\n") || "(empty directory)";
10629
+ return withDirMemory(abs, lines.join("\n") || "(empty directory)");
10437
10630
  }
10438
10631
  });
10439
10632
  registry.register({
@@ -11655,4 +11848,4 @@ export {
11655
11848
  he/he.js:
11656
11849
  (*! https://mths.be/he v1.2.0 by @mathias | MIT license *)
11657
11850
  */
11658
- //# sourceMappingURL=chunk-WL6SNQ5T.js.map
11851
+ //# sourceMappingURL=chunk-ICAFSZHS.js.map