reasonix 0.47.1 → 0.48.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 (122) hide show
  1. package/README.md +5 -26
  2. package/README.zh-CN.md +5 -26
  3. package/dist/cli/{acp-GEOAKSTU.js → acp-4ROCGYNH.js} +17 -17
  4. package/dist/cli/{chat-YTPATMMG.js → chat-GZNB5625.js} +24 -24
  5. package/dist/cli/{chunk-BQ6HC66J.js → chunk-2QSTA2QV.js} +3 -13
  6. package/dist/cli/chunk-2QSTA2QV.js.map +1 -0
  7. package/dist/cli/{chunk-DQ6K5ZQ7.js → chunk-3WGTGXO4.js} +2 -2
  8. package/dist/cli/{chunk-6QC5RQLE.js → chunk-5OHHAQ4W.js} +2 -2
  9. package/dist/cli/{chunk-XMHP7BEE.js → chunk-6MZTZO7A.js} +514 -791
  10. package/dist/cli/chunk-6MZTZO7A.js.map +1 -0
  11. package/dist/cli/chunk-7M4YYMKW.js +5198 -0
  12. package/dist/cli/chunk-7M4YYMKW.js.map +1 -0
  13. package/dist/cli/{chunk-KYQVQ5X4.js → chunk-B5CZL2SE.js} +9 -4
  14. package/dist/cli/chunk-B5CZL2SE.js.map +1 -0
  15. package/dist/cli/chunk-CDVSFSAK.js +17732 -0
  16. package/dist/cli/chunk-CDVSFSAK.js.map +1 -0
  17. package/dist/cli/{chunk-TRWHTFG7.js → chunk-DOWEOA6E.js} +2 -2
  18. package/dist/cli/chunk-EMMENC4O.js +831 -0
  19. package/dist/cli/chunk-EMMENC4O.js.map +1 -0
  20. package/dist/cli/{chunk-TDHXB2ER.js → chunk-H4CCXMDD.js} +2 -2
  21. package/dist/cli/{chunk-T5A7EY6B.js → chunk-HR5NBKEM.js} +2 -2
  22. package/dist/cli/{chunk-CNG32VAB.js → chunk-I4M5QJNL.js} +2 -2
  23. package/dist/cli/{chunk-5QCB62C4.js → chunk-J2TQAWOM.js} +135 -18
  24. package/dist/cli/{chunk-5QCB62C4.js.map → chunk-J2TQAWOM.js.map} +1 -1
  25. package/dist/cli/{chunk-DN4B5S6Y.js → chunk-JMDE6IO3.js} +2 -2
  26. package/dist/cli/{chunk-4MFCAZ2W.js → chunk-MOJYKO2A.js} +3 -3
  27. package/dist/cli/{chunk-HUILPCYX.js → chunk-MRZG4GBF.js} +3 -3
  28. package/dist/cli/{chunk-GH7DC2Y5.js → chunk-NMQSUNLB.js} +2 -2
  29. package/dist/cli/{chunk-ZXSCAODE.js → chunk-OB4BUJBL.js} +67 -2
  30. package/dist/cli/chunk-OB4BUJBL.js.map +1 -0
  31. package/dist/cli/{chunk-QCFLPSPH.js → chunk-OG5JANQ4.js} +2 -2
  32. package/dist/cli/{chunk-JBH5RM7X.js → chunk-OPYALNTT.js} +326 -55
  33. package/dist/cli/chunk-OPYALNTT.js.map +1 -0
  34. package/dist/cli/{chunk-CCJAP7G3.js → chunk-RUDBUHO4.js} +2 -2
  35. package/dist/cli/{chunk-2XY77LW7.js → chunk-S2RMQULY.js} +56 -24
  36. package/dist/cli/chunk-S2RMQULY.js.map +1 -0
  37. package/dist/cli/{chunk-DWPAKZTY.js → chunk-TE5UIIFL.js} +2 -2
  38. package/dist/cli/{chunk-KVZZ5U75.js → chunk-V4Y732RQ.js} +2 -2
  39. package/dist/cli/{chunk-TRSAHHCL.js → chunk-WZGNXR6E.js} +3 -3
  40. package/dist/cli/chunk-WZGNXR6E.js.map +1 -0
  41. package/dist/cli/{chunk-NRQ5UP5T.js → chunk-YW63N3ZR.js} +116 -28
  42. package/dist/cli/chunk-YW63N3ZR.js.map +1 -0
  43. package/dist/cli/{code-Q4NRVEDG.js → code-PMPJWXEO.js} +30 -31
  44. package/dist/cli/code-PMPJWXEO.js.map +1 -0
  45. package/dist/cli/{commands-4CDI4GFM.js → commands-QS6TG4G3.js} +4 -4
  46. package/dist/cli/{commit-GW7LDQP5.js → commit-XPRSKUBF.js} +3 -3
  47. package/dist/cli/{desktop-EG6P5SF2.js → desktop-562OPWIU.js} +461 -43
  48. package/dist/cli/desktop-562OPWIU.js.map +1 -0
  49. package/dist/cli/{diff-VI2YX4FN.js → diff-I6W4AUWJ.js} +8 -8
  50. package/dist/cli/{doctor-CQTTZP27.js → doctor-6XVZKT4U.js} +9 -9
  51. package/dist/cli/index.js +52 -40
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/{mcp-J2UCD4RZ.js → mcp-7W7ANO2Y.js} +2 -2
  54. package/dist/cli/{mcp-browse-GSX34JEK.js → mcp-browse-LA4I4YIZ.js} +2 -2
  55. package/dist/cli/{mcp-inspect-RRFYF4ZV.js → mcp-inspect-LWXXU7BY.js} +2 -2
  56. package/dist/cli/{prompt-5TQPIVHV.js → prompt-RKZD4X6Y.js} +3 -3
  57. package/dist/cli/{replay-MJCEMODU.js → replay-2X7MVXOI.js} +8 -8
  58. package/dist/cli/{run-P4D5VDYE.js → run-TPKXIJ27.js} +13 -13
  59. package/dist/cli/{server-C25JNNZV.js → server-NHQ3QXOZ.js} +15 -14
  60. package/dist/cli/{server-C25JNNZV.js.map → server-NHQ3QXOZ.js.map} +1 -1
  61. package/dist/cli/{sessions-QIONZJQ6.js → sessions-2A4DGSHA.js} +12 -12
  62. package/dist/cli/{setup-NLQ6G5G4.js → setup-GOLP7J4C.js} +5 -5
  63. package/dist/cli/{stats-DFZEXHP4.js → stats-CGDAFDKI.js} +6 -6
  64. package/dist/cli/{version-GR3X3MPI.js → version-FIL4ZFOS.js} +12 -12
  65. package/dist/grammars/tree-sitter-go.wasm +0 -0
  66. package/dist/grammars/tree-sitter-java.wasm +0 -0
  67. package/dist/grammars/tree-sitter-javascript.wasm +0 -0
  68. package/dist/grammars/tree-sitter-python.wasm +0 -0
  69. package/dist/grammars/tree-sitter-rust.wasm +0 -0
  70. package/dist/grammars/tree-sitter-tsx.wasm +0 -0
  71. package/dist/grammars/tree-sitter-typescript.wasm +0 -0
  72. package/dist/grammars/web-tree-sitter.wasm +0 -0
  73. package/dist/index.d.ts +38 -10
  74. package/dist/index.js +488 -87
  75. package/dist/index.js.map +1 -1
  76. package/package.json +13 -4
  77. package/dist/cli/chunk-2XY77LW7.js.map +0 -1
  78. package/dist/cli/chunk-6CRPCJAU.js +0 -3141
  79. package/dist/cli/chunk-6CRPCJAU.js.map +0 -1
  80. package/dist/cli/chunk-BQ6HC66J.js.map +0 -1
  81. package/dist/cli/chunk-JBH5RM7X.js.map +0 -1
  82. package/dist/cli/chunk-KYQVQ5X4.js.map +0 -1
  83. package/dist/cli/chunk-NRQ5UP5T.js.map +0 -1
  84. package/dist/cli/chunk-TRSAHHCL.js.map +0 -1
  85. package/dist/cli/chunk-XD6P7AFH.js +0 -375
  86. package/dist/cli/chunk-XD6P7AFH.js.map +0 -1
  87. package/dist/cli/chunk-XMHP7BEE.js.map +0 -1
  88. package/dist/cli/chunk-YFP3MYMY.js +0 -323
  89. package/dist/cli/chunk-YFP3MYMY.js.map +0 -1
  90. package/dist/cli/chunk-ZXSCAODE.js.map +0 -1
  91. package/dist/cli/code-Q4NRVEDG.js.map +0 -1
  92. package/dist/cli/desktop-EG6P5SF2.js.map +0 -1
  93. /package/dist/cli/{acp-GEOAKSTU.js.map → acp-4ROCGYNH.js.map} +0 -0
  94. /package/dist/cli/{chat-YTPATMMG.js.map → chat-GZNB5625.js.map} +0 -0
  95. /package/dist/cli/{chunk-DQ6K5ZQ7.js.map → chunk-3WGTGXO4.js.map} +0 -0
  96. /package/dist/cli/{chunk-6QC5RQLE.js.map → chunk-5OHHAQ4W.js.map} +0 -0
  97. /package/dist/cli/{chunk-TRWHTFG7.js.map → chunk-DOWEOA6E.js.map} +0 -0
  98. /package/dist/cli/{chunk-TDHXB2ER.js.map → chunk-H4CCXMDD.js.map} +0 -0
  99. /package/dist/cli/{chunk-T5A7EY6B.js.map → chunk-HR5NBKEM.js.map} +0 -0
  100. /package/dist/cli/{chunk-CNG32VAB.js.map → chunk-I4M5QJNL.js.map} +0 -0
  101. /package/dist/cli/{chunk-DN4B5S6Y.js.map → chunk-JMDE6IO3.js.map} +0 -0
  102. /package/dist/cli/{chunk-4MFCAZ2W.js.map → chunk-MOJYKO2A.js.map} +0 -0
  103. /package/dist/cli/{chunk-HUILPCYX.js.map → chunk-MRZG4GBF.js.map} +0 -0
  104. /package/dist/cli/{chunk-GH7DC2Y5.js.map → chunk-NMQSUNLB.js.map} +0 -0
  105. /package/dist/cli/{chunk-QCFLPSPH.js.map → chunk-OG5JANQ4.js.map} +0 -0
  106. /package/dist/cli/{chunk-CCJAP7G3.js.map → chunk-RUDBUHO4.js.map} +0 -0
  107. /package/dist/cli/{chunk-DWPAKZTY.js.map → chunk-TE5UIIFL.js.map} +0 -0
  108. /package/dist/cli/{chunk-KVZZ5U75.js.map → chunk-V4Y732RQ.js.map} +0 -0
  109. /package/dist/cli/{commands-4CDI4GFM.js.map → commands-QS6TG4G3.js.map} +0 -0
  110. /package/dist/cli/{commit-GW7LDQP5.js.map → commit-XPRSKUBF.js.map} +0 -0
  111. /package/dist/cli/{diff-VI2YX4FN.js.map → diff-I6W4AUWJ.js.map} +0 -0
  112. /package/dist/cli/{doctor-CQTTZP27.js.map → doctor-6XVZKT4U.js.map} +0 -0
  113. /package/dist/cli/{mcp-J2UCD4RZ.js.map → mcp-7W7ANO2Y.js.map} +0 -0
  114. /package/dist/cli/{mcp-browse-GSX34JEK.js.map → mcp-browse-LA4I4YIZ.js.map} +0 -0
  115. /package/dist/cli/{mcp-inspect-RRFYF4ZV.js.map → mcp-inspect-LWXXU7BY.js.map} +0 -0
  116. /package/dist/cli/{prompt-5TQPIVHV.js.map → prompt-RKZD4X6Y.js.map} +0 -0
  117. /package/dist/cli/{replay-MJCEMODU.js.map → replay-2X7MVXOI.js.map} +0 -0
  118. /package/dist/cli/{run-P4D5VDYE.js.map → run-TPKXIJ27.js.map} +0 -0
  119. /package/dist/cli/{sessions-QIONZJQ6.js.map → sessions-2A4DGSHA.js.map} +0 -0
  120. /package/dist/cli/{setup-NLQ6G5G4.js.map → setup-GOLP7J4C.js.map} +0 -0
  121. /package/dist/cli/{stats-DFZEXHP4.js.map → stats-CGDAFDKI.js.map} +0 -0
  122. /package/dist/cli/{version-GR3X3MPI.js.map → version-FIL4ZFOS.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-BQ6HC66J.js";
6
+ } from "./chunk-2QSTA2QV.js";
7
7
  import {
8
8
  countTokens,
9
9
  countTokensBounded,
@@ -12,12 +12,12 @@ import {
12
12
  } from "./chunk-6OWJV3YW.js";
13
13
  import {
14
14
  Usage
15
- } from "./chunk-DWPAKZTY.js";
15
+ } from "./chunk-TE5UIIFL.js";
16
16
  import {
17
17
  applyEdit,
18
18
  applyMultiEdit,
19
19
  pauseGate
20
- } from "./chunk-TRSAHHCL.js";
20
+ } from "./chunk-WZGNXR6E.js";
21
21
  import {
22
22
  NEGATIVE_CLAIM_RULE,
23
23
  PROJECT_MEMORY_FILES,
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  formatHookOutcomeMessage,
30
30
  runHooks
31
- } from "./chunk-GH7DC2Y5.js";
31
+ } from "./chunk-NMQSUNLB.js";
32
32
  import {
33
33
  ignoredByLayers,
34
34
  loadGitignoreAt,
@@ -46,21 +46,23 @@ import {
46
46
  DEEPSEEK_CONTEXT_TOKENS,
47
47
  DEFAULT_CONTEXT_TOKENS,
48
48
  SessionStats
49
- } from "./chunk-QCFLPSPH.js";
49
+ } from "./chunk-OG5JANQ4.js";
50
50
  import {
51
51
  t
52
- } from "./chunk-NRQ5UP5T.js";
52
+ } from "./chunk-YW63N3ZR.js";
53
53
  import {
54
54
  DEFAULT_INDEX_EXCLUDES,
55
55
  addProjectPathAllowed,
56
+ loadExaApiKey,
56
57
  loadMemoryTypeRegistry,
57
58
  loadMetasoApiKey,
59
+ loadPerplexityApiKey,
58
60
  loadProjectPathAllowed,
59
61
  loadTavilyApiKey,
60
62
  require_picomatch,
61
63
  webSearchEndpoint,
62
64
  webSearchEngine
63
- } from "./chunk-6CRPCJAU.js";
65
+ } from "./chunk-CDVSFSAK.js";
64
66
  import {
65
67
  __commonJS,
66
68
  __esm,
@@ -6314,8 +6316,8 @@ var ToolRegistry = class {
6314
6316
  _resultAugmenter = null;
6315
6317
  /** Per-tool fingerprint of the last call that failed schema validation. Cleared by any successful validation for that tool. */
6316
6318
  _lastMalformed = /* @__PURE__ */ new Map();
6317
- /** Per-tool fingerprint of the last host-side interceptor rejection. */
6318
- _lastInterceptorRejection = /* @__PURE__ */ new Map();
6319
+ /** Per-tool fingerprint of the last host-side gate rejection. */
6320
+ _lastGateRejection = /* @__PURE__ */ new Map();
6319
6321
  constructor(opts = {}) {
6320
6322
  this._autoFlatten = opts.autoFlatten !== false;
6321
6323
  }
@@ -6402,20 +6404,21 @@ var ToolRegistry = class {
6402
6404
  if (!tool) {
6403
6405
  return JSON.stringify({ error: `unknown tool: ${name}` });
6404
6406
  }
6405
- const fingerprint = fingerprintArgs(argumentsRaw);
6407
+ const rawFingerprint = rawFingerprintArgs(argumentsRaw);
6406
6408
  let args;
6407
6409
  try {
6408
6410
  args = typeof argumentsRaw === "string" ? argumentsRaw.trim() ? JSON.parse(argumentsRaw) ?? {} : {} : argumentsRaw ?? {};
6409
6411
  } catch (err) {
6410
6412
  return this._noteMalformed(
6411
6413
  name,
6412
- fingerprint,
6414
+ rawFingerprint,
6413
6415
  `invalid tool arguments JSON: ${err.message}`
6414
6416
  );
6415
6417
  }
6416
6418
  if (tool.flatSchema && args && typeof args === "object" && hasDotKey(args)) {
6417
6419
  args = nestArguments(args);
6418
6420
  }
6421
+ const fingerprint = fingerprintArgs(args);
6419
6422
  const missing = tool.parameters ? missingRequiredParam(tool.parameters, args) : null;
6420
6423
  if (missing) {
6421
6424
  return this._noteMalformed(
@@ -6436,7 +6439,7 @@ var ToolRegistry = class {
6436
6439
  try {
6437
6440
  const short = await interceptor(name, args);
6438
6441
  if (typeof short === "string") {
6439
- const guarded = this._noteInterceptorRejection(name, fingerprint, short);
6442
+ const guarded = this._noteGateRejection(name, fingerprint, short);
6440
6443
  return this._augmentResult(name, args, guarded);
6441
6444
  }
6442
6445
  } catch (err) {
@@ -6445,7 +6448,6 @@ var ToolRegistry = class {
6445
6448
  });
6446
6449
  }
6447
6450
  }
6448
- this._lastInterceptorRejection.delete(name);
6449
6451
  if (opts.signal?.aborted) {
6450
6452
  return JSON.stringify({
6451
6453
  error: `${name}: aborted before dispatch (user interrupt)`,
@@ -6483,6 +6485,7 @@ var ToolRegistry = class {
6483
6485
  finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });
6484
6486
  }
6485
6487
  }
6488
+ finalResult = this._noteGateRejection(name, fingerprint, finalResult);
6486
6489
  return this._augmentResult(name, args, finalResult);
6487
6490
  }
6488
6491
  _augmentResult(name, args, result) {
@@ -6506,18 +6509,18 @@ var ToolRegistry = class {
6506
6509
  }
6507
6510
  return JSON.stringify({ error: `${name}: ${detail}` });
6508
6511
  }
6509
- _noteInterceptorRejection(name, fingerprint, result) {
6510
- const reason = rejectedReason(result);
6512
+ _noteGateRejection(name, fingerprint, result) {
6513
+ const reason = rejectedReason(name, result);
6511
6514
  if (!reason) {
6512
- this._lastInterceptorRejection.delete(name);
6515
+ this._lastGateRejection.delete(name);
6513
6516
  return result;
6514
6517
  }
6515
6518
  const key = `${reason}:${fingerprint}`;
6516
- const prev = this._lastInterceptorRejection.get(name);
6517
- this._lastInterceptorRejection.set(name, key);
6519
+ const prev = this._lastGateRejection.get(name);
6520
+ this._lastGateRejection.set(name, key);
6518
6521
  if (prev === key) {
6519
6522
  return JSON.stringify({
6520
- error: `${name}: same call was just rejected by ${reason} \u2014 do not retry identical args. Switch to read-only exploration, submit or revise the plan, or choose a different tool call.`,
6523
+ error: `${name}: same call was just rejected by ${reason} \u2014 do not retry identical args. ${rejectionRecoveryHint(reason)}`,
6521
6524
  rejectedReason: reason,
6522
6525
  consecutiveInterceptorRejection: true
6523
6526
  });
@@ -6525,16 +6528,44 @@ var ToolRegistry = class {
6525
6528
  return result;
6526
6529
  }
6527
6530
  };
6528
- function rejectedReason(result) {
6531
+ function rejectedReason(name, result) {
6532
+ const textReason = plainTextRejectedReason(name, result);
6533
+ if (textReason) return textReason;
6529
6534
  try {
6530
6535
  const parsed = JSON.parse(result);
6531
6536
  if (!parsed || typeof parsed !== "object") return null;
6532
6537
  const reason = parsed.rejectedReason;
6533
- return typeof reason === "string" && reason ? reason : null;
6538
+ if (typeof reason === "string" && reason) return reason;
6539
+ const error = parsed.error;
6540
+ if (typeof error === "string") return plainTextRejectedReason(name, error);
6541
+ return null;
6534
6542
  } catch {
6535
6543
  return null;
6536
6544
  }
6537
6545
  }
6546
+ function plainTextRejectedReason(name, result) {
6547
+ if ((name === "edit_file" || name === "write_file") && /rejected this edit/i.test(result)) {
6548
+ return "edit-gate";
6549
+ }
6550
+ if ((name === "run_command" || name === "run_background") && /\buser denied:/i.test(result)) {
6551
+ return "shell-gate";
6552
+ }
6553
+ return null;
6554
+ }
6555
+ function rejectionRecoveryHint(reason) {
6556
+ switch (reason) {
6557
+ case "edit-gate":
6558
+ return "Do not re-emit the same edit. Try a genuinely different edit or ask the user how to proceed.";
6559
+ case "shell-gate":
6560
+ return "Do not retry the same command. Use an allowlisted/read-only command, wait for approval, or ask the user how to proceed.";
6561
+ case "engineering-lifecycle":
6562
+ return "Switch to read-only exploration, submit or revise the plan, or choose a different tool call.";
6563
+ case "engineering-lifecycle-evidence":
6564
+ return "Submit completion evidence or revise/checkpoint the plan before marking the step complete.";
6565
+ default:
6566
+ return "Choose a different tool call or ask the user how to proceed.";
6567
+ }
6568
+ }
6538
6569
  function isReadOnlyCall(tool, args) {
6539
6570
  if (tool.readOnlyCheck) {
6540
6571
  try {
@@ -6553,14 +6584,27 @@ function hasDotKey(obj) {
6553
6584
  }
6554
6585
  return false;
6555
6586
  }
6556
- function fingerprintArgs(argumentsRaw) {
6587
+ function rawFingerprintArgs(argumentsRaw) {
6557
6588
  if (typeof argumentsRaw === "string") return argumentsRaw;
6589
+ return fingerprintArgs(argumentsRaw);
6590
+ }
6591
+ function fingerprintArgs(args) {
6558
6592
  try {
6559
- return JSON.stringify(argumentsRaw);
6593
+ return JSON.stringify(sortJson(args));
6560
6594
  } catch {
6561
6595
  return "";
6562
6596
  }
6563
6597
  }
6598
+ function sortJson(value) {
6599
+ if (Array.isArray(value)) return value.map(sortJson);
6600
+ if (!value || typeof value !== "object") return value;
6601
+ const out = {};
6602
+ for (const key of Object.keys(value).sort()) {
6603
+ const item = value[key];
6604
+ if (item !== void 0) out[key] = sortJson(item);
6605
+ }
6606
+ return out;
6607
+ }
6564
6608
  function missingRequiredParam(schema, args) {
6565
6609
  const required = schema.required;
6566
6610
  if (!required || required.length === 0) return null;
@@ -6745,6 +6789,20 @@ var ContextManager = class {
6745
6789
  this.deps = deps;
6746
6790
  }
6747
6791
  deps;
6792
+ /** Real-time token count of the current log — used by Desktop to refresh the
6793
+ * context meter after /compact when no API usage event is available. */
6794
+ getLogTokens() {
6795
+ const entries = this.deps.log.toMessages();
6796
+ let total = 0;
6797
+ for (const e of entries) {
6798
+ const content = typeof e.content === "string" ? e.content : "";
6799
+ total += countTokensBounded(content);
6800
+ if (e.role === "assistant" && Array.isArray(e.tool_calls) && e.tool_calls.length > 0) {
6801
+ total += countTokensBounded(JSON.stringify(e.tool_calls));
6802
+ }
6803
+ }
6804
+ return total;
6805
+ }
6748
6806
  /** Decision after a turn's response — fold, exit with summary, or carry on. */
6749
6807
  decideAfterUsage(usage, model, alreadyFoldedThisTurn) {
6750
6808
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
@@ -7096,13 +7154,11 @@ async function* forceSummaryAfterIterLimit(ctx, opts) {
7096
7154
  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."
7097
7155
  });
7098
7156
  const summaryModel = "deepseek-v4-flash";
7099
- const summaryEffort = "high";
7100
7157
  const resp = await ctx.client.chat({
7101
7158
  model: summaryModel,
7102
7159
  messages,
7103
7160
  signal: ctx.signal,
7104
- thinking: thinkingModeForModel(summaryModel),
7105
- reasoningEffort: summaryEffort
7161
+ thinking: "disabled"
7106
7162
  });
7107
7163
  const rawContent = resp.content?.trim() ?? "";
7108
7164
  const cleaned = stripHallucinatedToolMarkup(rawContent);
@@ -7733,6 +7789,10 @@ var CacheFirstLoop = class {
7733
7789
  async compactHistory(opts) {
7734
7790
  return this.context.fold(this.model, opts);
7735
7791
  }
7792
+ /** Real-time token count of the current log — forwarded to Desktop for meter refresh. */
7793
+ getCurrentLogTokens() {
7794
+ return this.context.getLogTokens();
7795
+ }
7736
7796
  appendAndPersist(message) {
7737
7797
  this.log.append(message);
7738
7798
  if (this.sessionName) {
@@ -8048,21 +8108,24 @@ ${reason}`
8048
8108
  const toolSpecs = this.prefix.tools();
8049
8109
  for (let iter = 0; ; iter++) {
8050
8110
  if (signal.aborted) {
8051
- yield {
8052
- turn: this._turn,
8053
- role: "warning",
8054
- content: t("loop.abortedAtIter", { iter })
8055
- };
8056
- const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
8057
- this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));
8058
- yield {
8059
- turn: this._turn,
8060
- role: "assistant_final",
8061
- content: stoppedMsg,
8062
- forcedSummary: true
8063
- };
8064
- yield { turn: this._turn, role: "done", content: stoppedMsg };
8065
- this._turnAbort = new AbortController();
8111
+ try {
8112
+ yield {
8113
+ turn: this._turn,
8114
+ role: "warning",
8115
+ content: t("loop.abortedAtIter", { iter })
8116
+ };
8117
+ const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
8118
+ this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));
8119
+ yield {
8120
+ turn: this._turn,
8121
+ role: "assistant_final",
8122
+ content: stoppedMsg,
8123
+ forcedSummary: true
8124
+ };
8125
+ yield { turn: this._turn, role: "done", content: stoppedMsg };
8126
+ } finally {
8127
+ this._turnAbort = new AbortController();
8128
+ }
8066
8129
  return;
8067
8130
  }
8068
8131
  if (iter > 0) {
@@ -8238,8 +8301,11 @@ ${reason}`
8238
8301
  }
8239
8302
  } catch (err) {
8240
8303
  if (signal.aborted) {
8241
- yield { turn: this._turn, role: "done", content: "" };
8242
- this._turnAbort = new AbortController();
8304
+ try {
8305
+ yield { turn: this._turn, role: "done", content: "" };
8306
+ } finally {
8307
+ this._turnAbort = new AbortController();
8308
+ }
8243
8309
  return;
8244
8310
  }
8245
8311
  const probe = is5xxError(err) ? await probeDeepSeekReachable(this.client) : void 0;
@@ -9273,6 +9339,8 @@ var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (
9273
9339
  var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
9274
9340
  var METASO_ENDPOINT = "https://metaso.cn/api/v1";
9275
9341
  var TAVILY_ENDPOINT = "https://api.tavily.com/search";
9342
+ var PERPLEXITY_ENDPOINT = "https://api.perplexity.ai/chat/completions";
9343
+ var EXA_ENDPOINT = "https://api.exa.ai/answer";
9276
9344
  function searchStatusError(status) {
9277
9345
  if (status === 429) return t("webErrors.rateLimit429");
9278
9346
  if (status === 403) return t("webErrors.forbidden403");
@@ -9295,6 +9363,12 @@ async function webSearch(query, opts = {}) {
9295
9363
  if (opts.engine === "tavily") {
9296
9364
  return searchTavily(query, opts);
9297
9365
  }
9366
+ if (opts.engine === "perplexity") {
9367
+ return searchPerplexity(query, opts);
9368
+ }
9369
+ if (opts.engine === "exa") {
9370
+ return searchExa(query, opts);
9371
+ }
9298
9372
  return searchMojeek(query, opts);
9299
9373
  }
9300
9374
  async function searchMojeek(query, opts = {}) {
@@ -9478,6 +9552,121 @@ async function searchTavily(query, opts = {}) {
9478
9552
  snippet: r.content ?? ""
9479
9553
  }));
9480
9554
  }
9555
+ async function searchPerplexity(query, opts = {}) {
9556
+ const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
9557
+ const apiKey = loadPerplexityApiKey();
9558
+ if (!apiKey) throw new Error(t("webErrors.perplexityMissingKey"));
9559
+ let resp;
9560
+ try {
9561
+ resp = await fetch(PERPLEXITY_ENDPOINT, {
9562
+ method: "POST",
9563
+ headers: {
9564
+ Authorization: `Bearer ${apiKey}`,
9565
+ "Content-Type": "application/json"
9566
+ },
9567
+ body: JSON.stringify({
9568
+ model: "sonar",
9569
+ messages: [{ role: "user", content: query }],
9570
+ max_tokens: 1024,
9571
+ return_related_questions: false
9572
+ }),
9573
+ signal: opts.signal
9574
+ });
9575
+ } catch (err) {
9576
+ if (err instanceof TypeError && err.message.includes("fetch")) {
9577
+ throw new Error(t("webErrors.cannotReach", { endpoint: PERPLEXITY_ENDPOINT }));
9578
+ }
9579
+ throw err;
9580
+ }
9581
+ if (!resp.ok) {
9582
+ if (resp.status === 401 || resp.status === 403) {
9583
+ throw new Error(t("webErrors.perplexityUnauthorized"));
9584
+ }
9585
+ if (resp.status === 429) throw new Error(t("webErrors.perplexityRateLimit"));
9586
+ throw new Error(t("webErrors.perplexityServerError", { status: resp.status }));
9587
+ }
9588
+ const raw = await resp.text();
9589
+ let data;
9590
+ try {
9591
+ data = JSON.parse(raw);
9592
+ } catch {
9593
+ throw new Error(t("webErrors.perplexityParseError", { status: resp.status }));
9594
+ }
9595
+ const answer = data.choices?.[0]?.message?.content ?? "";
9596
+ const citations = Array.isArray(data.citations) ? data.citations : [];
9597
+ const results = [];
9598
+ if (answer) {
9599
+ results.push({ title: answer, url: "", snippet: "", answer });
9600
+ }
9601
+ const count = Math.min(citations.length, topK);
9602
+ for (let i = 0; i < count; i++) {
9603
+ const c = citations[i];
9604
+ if (typeof c === "string") {
9605
+ results.push({ title: `Source ${i + 1}`, url: c, snippet: "" });
9606
+ } else if (c && typeof c === "object" && typeof c.url === "string") {
9607
+ const item = c;
9608
+ results.push({
9609
+ title: typeof item.title === "string" ? item.title : `Source ${i + 1}`,
9610
+ url: item.url,
9611
+ snippet: ""
9612
+ });
9613
+ }
9614
+ }
9615
+ return results;
9616
+ }
9617
+ async function searchExa(query, opts = {}) {
9618
+ const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
9619
+ const apiKey = loadExaApiKey();
9620
+ if (!apiKey) throw new Error(t("webErrors.exaMissingKey"));
9621
+ let resp;
9622
+ try {
9623
+ resp = await fetch(EXA_ENDPOINT, {
9624
+ method: "POST",
9625
+ headers: {
9626
+ "x-api-key": apiKey,
9627
+ "Content-Type": "application/json"
9628
+ },
9629
+ body: JSON.stringify({ query, text: true }),
9630
+ signal: opts.signal
9631
+ });
9632
+ } catch (err) {
9633
+ if (err instanceof TypeError && err.message.includes("fetch")) {
9634
+ throw new Error(t("webErrors.cannotReach", { endpoint: EXA_ENDPOINT }));
9635
+ }
9636
+ throw err;
9637
+ }
9638
+ if (!resp.ok) {
9639
+ if (resp.status === 401 || resp.status === 403) {
9640
+ throw new Error(t("webErrors.exaUnauthorized"));
9641
+ }
9642
+ if (resp.status === 429) throw new Error(t("webErrors.exaRateLimit"));
9643
+ throw new Error(t("webErrors.exaServerError", { status: resp.status }));
9644
+ }
9645
+ const raw = await resp.text();
9646
+ let data;
9647
+ try {
9648
+ data = JSON.parse(raw);
9649
+ } catch {
9650
+ throw new Error(t("webErrors.exaParseError", { status: resp.status }));
9651
+ }
9652
+ const answer = data.answer ?? "";
9653
+ const citations = data.citations ?? [];
9654
+ const results = [];
9655
+ if (answer) {
9656
+ results.push({ title: answer, url: "", snippet: "", answer });
9657
+ }
9658
+ const count = Math.min(citations.length, topK);
9659
+ for (let i = 0; i < count; i++) {
9660
+ const c = citations[i];
9661
+ if (!c.url) continue;
9662
+ results.push({
9663
+ title: c.title || `Source ${i + 1}`,
9664
+ url: c.url,
9665
+ snippet: c.text ?? ""
9666
+ });
9667
+ }
9668
+ return results;
9669
+ }
9481
9670
  function parseSearxngHtmlResults(html) {
9482
9671
  const root = (0, import_node_html_parser.parse)(html);
9483
9672
  const results = [];
@@ -9696,7 +9885,7 @@ function registerWebTools(registry, opts = {}) {
9696
9885
  const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
9697
9886
  registry.register({
9698
9887
  name: "web_search",
9699
- 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.",
9888
+ 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.",
9700
9889
  readOnly: true,
9701
9890
  parallelSafe: true,
9702
9891
  parameters: {
@@ -9705,7 +9894,7 @@ function registerWebTools(registry, opts = {}) {
9705
9894
  query: { type: "string", description: "Natural-language search query." },
9706
9895
  topK: {
9707
9896
  type: "integer",
9708
- description: `Number of results to return (1..10). Default ${defaultTopK}.`
9897
+ description: `Number of results to return. Default ${defaultTopK}.`
9709
9898
  }
9710
9899
  },
9711
9900
  required: ["query"]
@@ -9749,14 +9938,30 @@ ${page.text}`;
9749
9938
  return registry;
9750
9939
  }
9751
9940
  function formatSearchResults(query, results) {
9752
- const lines = [`query: ${query}`, `
9753
- results (${results.length}):`];
9754
- results.forEach((r, i) => {
9941
+ const lines = [`query: ${query}`];
9942
+ const hasAnswer = results.length > 0 && results[0]?.url === "" && results[0]?.answer;
9943
+ if (hasAnswer) {
9944
+ lines.push("\nanswer:");
9945
+ lines.push(` ${results[0].answer}`);
9946
+ const sources = results.slice(1);
9755
9947
  lines.push(`
9948
+ sources (${sources.length}):`);
9949
+ sources.forEach((r, i) => {
9950
+ lines.push(`
9756
9951
  ${i + 1}. ${r.title}`);
9757
- lines.push(` ${r.url}`);
9758
- if (r.snippet) lines.push(` ${r.snippet}`);
9759
- });
9952
+ lines.push(` ${r.url}`);
9953
+ if (r.snippet) lines.push(` ${r.snippet}`);
9954
+ });
9955
+ } else {
9956
+ lines.push(`
9957
+ results (${results.length}):`);
9958
+ results.forEach((r, i) => {
9959
+ lines.push(`
9960
+ ${i + 1}. ${r.title}`);
9961
+ lines.push(` ${r.url}`);
9962
+ if (r.snippet) lines.push(` ${r.snippet}`);
9963
+ });
9964
+ }
9760
9965
  return lines.join("\n");
9761
9966
  }
9762
9967
 
@@ -10423,7 +10628,9 @@ var DEFAULT_OUTLINE_THRESHOLD_BYTES = 64 * 1024;
10423
10628
  var DEFAULT_MAX_LIST_BYTES = 256 * 1024;
10424
10629
  var HARD_MAX_FILE_BYTES = 32 * 1024 * 1024;
10425
10630
  var OUTLINE_HEAD_LINES = 80;
10426
- var SKIP_DIR_NAMES = new Set(DEFAULT_INDEX_EXCLUDES.dirs);
10631
+ var SKIP_DIR_NAMES = new Set(
10632
+ DEFAULT_INDEX_EXCLUDES.dirs.filter((d) => d !== ".reasonix")
10633
+ );
10427
10634
  var BINARY_EXTENSIONS = new Set(DEFAULT_INDEX_EXCLUDES.exts);
10428
10635
  function displayRel3(rootDir, full) {
10429
10636
  return pathMod4.relative(rootDir, full).replaceAll("\\", "/");
@@ -11150,6 +11357,21 @@ function sanitizeEvidence(raw) {
11150
11357
  }
11151
11358
  return out.length > 0 ? out : void 0;
11152
11359
  }
11360
+ function summarizeEvidence(evidence) {
11361
+ if (!evidence || evidence.length === 0) return void 0;
11362
+ const parts = evidence.map((item) => `${item.kind}: ${item.summary}`);
11363
+ return parts.join("; ");
11364
+ }
11365
+ function compactStepCompletion(update) {
11366
+ const compact = {
11367
+ kind: "step_completed",
11368
+ stepId: update.stepId,
11369
+ result: update.result
11370
+ };
11371
+ const evidenceSummary = summarizeEvidence(update.evidence);
11372
+ if (evidenceSummary) compact.evidenceSummary = evidenceSummary;
11373
+ return compact;
11374
+ }
11153
11375
  function registerSubmitPlan(registry, opts) {
11154
11376
  registry.register({
11155
11377
  name: "submit_plan",
@@ -11263,9 +11485,9 @@ function registerMarkStepComplete(registry, opts) {
11263
11485
  opts.onStepCompleted?.(update);
11264
11486
  const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({
11265
11487
  kind: "plan_checkpoint",
11266
- payload: { stepId, title, result, notes }
11488
+ payload: { stepId, title, result, notes, completion: update }
11267
11489
  });
11268
- if (verdict.type === "continue") return JSON.stringify(update);
11490
+ if (verdict.type === "continue") return JSON.stringify(compactStepCompletion(update));
11269
11491
  if (verdict.type === "revise") {
11270
11492
  if (verdict.feedback) return `revision requested: ${verdict.feedback}`;
11271
11493
  throw new Error("user requested revision at checkpoint");
@@ -11478,6 +11700,7 @@ function nextRunId() {
11478
11700
  runIdCounter++;
11479
11701
  return `sub-${runIdCounter.toString(36)}`;
11480
11702
  }
11703
+ var SHARED_SUBAGENT_SINK = { current: null };
11481
11704
  var SUBAGENT_BASE_SYSTEM = `You are a Reasonix subagent. The parent agent spawned you to handle one focused subtask, then return.
11482
11705
 
11483
11706
  Rules:
@@ -11576,12 +11799,40 @@ async function spawnSubagent(opts) {
11576
11799
  let toolIter = 0;
11577
11800
  let summarisingEmitted = false;
11578
11801
  let forcedSummaryFired = false;
11802
+ let outputChars = 0;
11803
+ let reasoningChars = 0;
11804
+ let toolReadChars = 0;
11805
+ let lastStreamEmitAt = 0;
11806
+ let charsSinceLastEmit = 0;
11807
+ const STREAM_EMIT_INTERVAL_MS = 200;
11808
+ const STREAM_EMIT_CHARS = 400;
11809
+ const maybeEmitStreamProgress = (now, force) => {
11810
+ if (!sink?.current) return;
11811
+ if (!force && now - lastStreamEmitAt < STREAM_EMIT_INTERVAL_MS && charsSinceLastEmit < STREAM_EMIT_CHARS) {
11812
+ return;
11813
+ }
11814
+ lastStreamEmitAt = now;
11815
+ charsSinceLastEmit = 0;
11816
+ sink.current({
11817
+ kind: "stream-progress",
11818
+ runId,
11819
+ task: taskPreview,
11820
+ skillName,
11821
+ model,
11822
+ iter: toolIter,
11823
+ elapsedMs: now - startedAt,
11824
+ outputChars,
11825
+ reasoningChars,
11826
+ toolReadChars
11827
+ });
11828
+ };
11579
11829
  try {
11580
11830
  for await (const ev of childLoop.step(opts.task)) {
11581
11831
  sink?.current?.({ kind: "inner", runId, task: taskPreview, skillName, model, inner: ev });
11582
11832
  if (ev.role === "tool") {
11583
11833
  toolIter++;
11584
11834
  summarisingEmitted = false;
11835
+ toolReadChars += ev.content?.length ?? 0;
11585
11836
  sink?.current?.({
11586
11837
  kind: "progress",
11587
11838
  runId,
@@ -11591,6 +11842,17 @@ async function spawnSubagent(opts) {
11591
11842
  iter: toolIter,
11592
11843
  elapsedMs: Date.now() - startedAt
11593
11844
  });
11845
+ maybeEmitStreamProgress(Date.now(), true);
11846
+ }
11847
+ if (ev.role === "assistant_delta") {
11848
+ const dContent = ev.content?.length ?? 0;
11849
+ const dReason = ev.reasoningDelta?.length ?? 0;
11850
+ if (dContent > 0 || dReason > 0) {
11851
+ outputChars += dContent;
11852
+ reasoningChars += dReason;
11853
+ charsSinceLastEmit += dContent + dReason;
11854
+ maybeEmitStreamProgress(Date.now(), false);
11855
+ }
11594
11856
  }
11595
11857
  if (ev.role === "assistant_delta" && !summarisingEmitted && (ev.content ?? "").length > 0) {
11596
11858
  summarisingEmitted = true;
@@ -11831,6 +12093,14 @@ function applyEditBlock(block, rootDir) {
11831
12093
  message: "SEARCH text does not match the current file content exactly"
11832
12094
  };
11833
12095
  }
12096
+ const nextIdx = content.indexOf(adaptedSearch, idx + 1);
12097
+ if (nextIdx !== -1) {
12098
+ return {
12099
+ path: block.path,
12100
+ status: "not-found",
12101
+ message: "SEARCH text appears multiple times; include more context to disambiguate"
12102
+ };
12103
+ }
11834
12104
  const replaced = `${content.slice(0, idx)}${adaptedReplace}${content.slice(idx + adaptedSearch.length)}`;
11835
12105
  const outBuf = Buffer.from(replaced, "utf8");
11836
12106
  ftruncateSync(fd, outBuf.length);
@@ -11942,6 +12212,7 @@ export {
11942
12212
  registerChoiceTool,
11943
12213
  registerPlanTool,
11944
12214
  registerTodoTool,
12215
+ SHARED_SUBAGENT_SINK,
11945
12216
  spawnSubagent,
11946
12217
  formatSubagentResult,
11947
12218
  webFetch,
@@ -11957,4 +12228,4 @@ export {
11957
12228
  he/he.js:
11958
12229
  (*! https://mths.be/he v1.2.0 by @mathias | MIT license *)
11959
12230
  */
11960
- //# sourceMappingURL=chunk-JBH5RM7X.js.map
12231
+ //# sourceMappingURL=chunk-OPYALNTT.js.map