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
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
3
  import {
4
+ QQChannel,
4
5
  createMcpRuntime
5
- } from "./chunk-YFP3MYMY.js";
6
+ } from "./chunk-EMMENC4O.js";
6
7
  import {
7
8
  buildCodeToolset
8
- } from "./chunk-XD6P7AFH.js";
9
+ } from "./chunk-7M4YYMKW.js";
9
10
  import {
10
11
  Eventizer,
11
12
  autoResolveVerdict
12
- } from "./chunk-ZXSCAODE.js";
13
- import "./chunk-DQ6K5ZQ7.js";
13
+ } from "./chunk-OB4BUJBL.js";
14
+ import "./chunk-3WGTGXO4.js";
14
15
  import "./chunk-EQATK2L2.js";
15
16
  import {
16
17
  CacheFirstLoop,
@@ -19,14 +20,15 @@ import {
19
20
  listFilesWithStatsAsync,
20
21
  parseAtQuery,
21
22
  rankPickerCandidates
22
- } from "./chunk-JBH5RM7X.js";
23
- import "./chunk-6QC5RQLE.js";
24
- import "./chunk-HUILPCYX.js";
23
+ } from "./chunk-OPYALNTT.js";
24
+ import "./chunk-5OHHAQ4W.js";
25
+ import "./chunk-MRZG4GBF.js";
25
26
  import "./chunk-HIYTRCSW.js";
26
27
  import {
27
28
  MemoryStore,
28
29
  codeSystemPrompt
29
- } from "./chunk-BQ6HC66J.js";
30
+ } from "./chunk-2QSTA2QV.js";
31
+ import "./chunk-FEZK652I.js";
30
32
  import {
31
33
  canonicalPresetName,
32
34
  resolvePreset
@@ -37,21 +39,21 @@ import {
37
39
  import {
38
40
  DeepSeekClient,
39
41
  pickPrimaryBalance
40
- } from "./chunk-DWPAKZTY.js";
42
+ } from "./chunk-TE5UIIFL.js";
41
43
  import "./chunk-25T6CVUP.js";
42
44
  import {
43
45
  loadDotenv
44
46
  } from "./chunk-2UQP6H6T.js";
45
- import "./chunk-KVZZ5U75.js";
47
+ import "./chunk-V4Y732RQ.js";
46
48
  import {
47
49
  pauseGate
48
- } from "./chunk-TRSAHHCL.js";
50
+ } from "./chunk-WZGNXR6E.js";
49
51
  import {
50
52
  SkillStore
51
53
  } from "./chunk-FY4S7TJZ.js";
52
54
  import "./chunk-PLHAZOLZ.js";
53
- import "./chunk-T5A7EY6B.js";
54
- import "./chunk-GH7DC2Y5.js";
55
+ import "./chunk-HR5NBKEM.js";
56
+ import "./chunk-NMQSUNLB.js";
55
57
  import "./chunk-S4XVGLRW.js";
56
58
  import {
57
59
  deleteSession,
@@ -62,9 +64,9 @@ import {
62
64
  sessionPath,
63
65
  timestampSuffix
64
66
  } from "./chunk-RRXUIPWG.js";
65
- import "./chunk-DN4B5S6Y.js";
66
- import "./chunk-QCFLPSPH.js";
67
- import "./chunk-NRQ5UP5T.js";
67
+ import "./chunk-JMDE6IO3.js";
68
+ import "./chunk-OG5JANQ4.js";
69
+ import "./chunk-YW63N3ZR.js";
68
70
  import {
69
71
  describeQQAccess,
70
72
  isPlausibleKey,
@@ -92,7 +94,7 @@ import {
92
94
  saveReasoningEffort,
93
95
  saveWorkspaceDir,
94
96
  writeConfig
95
- } from "./chunk-6CRPCJAU.js";
97
+ } from "./chunk-CDVSFSAK.js";
96
98
  import {
97
99
  VERSION
98
100
  } from "./chunk-XXC2BYTV.js";
@@ -172,15 +174,12 @@ function toAccess(config) {
172
174
  function loadDesktopQQState(path) {
173
175
  const config = loadQQConfig(path);
174
176
  const configured = Boolean(config.appId && config.appSecret);
175
- const enabled = config.enabled === true;
176
177
  return {
177
178
  ...config,
178
179
  sandbox: config.sandbox ?? false,
179
- enabled,
180
+ enabled: config.enabled === true,
180
181
  configured,
181
- // Never true — the desktop process doesn't host a QQChannel (#1317).
182
- connected: false,
183
- enabledForCli: configured && enabled,
182
+ runtimeState: "disconnected",
184
183
  appIdPreview: toPreview(config.appId),
185
184
  access: toAccess(config)
186
185
  };
@@ -208,9 +207,32 @@ function setDesktopQQEnabled(enabled, path) {
208
207
  }
209
208
 
210
209
  // src/cli/commands/desktop.ts
210
+ var desktopQqRuntimeSnapshot = {
211
+ runtimeState: "disconnected"
212
+ };
213
+ var STDOUT_BACKPRESSURE_WAIT = new Int32Array(new SharedArrayBuffer(4));
214
+ function writeAllSync(fd, buffer, opts = {}) {
215
+ const write = opts.write ?? writeSync;
216
+ const wait = opts.wait ?? (() => Atomics.wait(STDOUT_BACKPRESSURE_WAIT, 0, 0, 5));
217
+ let offset = 0;
218
+ while (offset < buffer.length) {
219
+ let written;
220
+ try {
221
+ written = write(fd, buffer, offset, buffer.length - offset);
222
+ } catch (err) {
223
+ if (err.code === "EAGAIN") {
224
+ wait();
225
+ continue;
226
+ }
227
+ throw err;
228
+ }
229
+ if (written <= 0) throw new Error("stdout write returned 0 bytes");
230
+ offset += written;
231
+ }
232
+ }
211
233
  function emit(ev, tabId) {
212
234
  const payload = tabId ? { ...ev, tabId } : ev;
213
- writeSync(1, Buffer.from(`${JSON.stringify(payload)}
235
+ writeAllSync(1, Buffer.from(`${JSON.stringify(payload)}
214
236
  `, "utf8"));
215
237
  }
216
238
  function tailLines(s, n) {
@@ -287,7 +309,16 @@ function emitSettings(tab) {
287
309
  );
288
310
  }
289
311
  function emitQQSettings(tab) {
290
- emit({ type: "$qq_settings", ...loadDesktopQQState() }, tab.id);
312
+ const base = loadDesktopQQState();
313
+ emit(
314
+ {
315
+ type: "$qq_settings",
316
+ ...base,
317
+ runtimeState: desktopQqRuntimeSnapshot.runtimeState,
318
+ lastError: desktopQqRuntimeSnapshot.lastError
319
+ },
320
+ tab.id
321
+ );
291
322
  }
292
323
  async function emitBalance(tab) {
293
324
  if (!tab.runtime) return;
@@ -379,7 +410,8 @@ function emitCtxBreakdown(tab) {
379
410
  try {
380
411
  const sys = countTokensBounded(tab.runtime.loop.prefix.system);
381
412
  const tools = countTokensBounded(JSON.stringify(tab.runtime.loop.prefix.toolSpecs));
382
- emit({ type: "$ctx_breakdown", reservedTokens: sys + tools }, tab.id);
413
+ const logTokens = tab.runtime.loop.getCurrentLogTokens();
414
+ emit({ type: "$ctx_breakdown", reservedTokens: sys + tools, logTokens }, tab.id);
383
415
  } catch {
384
416
  }
385
417
  }
@@ -540,6 +572,319 @@ async function desktopCommand(opts) {
540
572
  const id = tabContext.getStore();
541
573
  return id ? tabs.get(id) : void 0;
542
574
  }
575
+ let first;
576
+ const qqRuntime = {
577
+ channel: null,
578
+ runtimeState: "disconnected",
579
+ lastError: void 0,
580
+ pendingGateId: null,
581
+ interaction: { kind: null, payload: null },
582
+ replyThisTurn: false
583
+ };
584
+ function currentQqSettings() {
585
+ const base = loadDesktopQQState();
586
+ return {
587
+ type: "$qq_settings",
588
+ ...base,
589
+ runtimeState: qqRuntime.runtimeState,
590
+ lastError: qqRuntime.lastError
591
+ };
592
+ }
593
+ function activeDesktopTab() {
594
+ return (lastActiveTabId ? tabs.get(lastActiveTabId) : void 0) ?? first;
595
+ }
596
+ function broadcastQQSettings() {
597
+ for (const tab of tabs.values()) emit(currentQqSettings(), tab.id);
598
+ }
599
+ function setQQRuntimeState(runtimeState, lastError) {
600
+ qqRuntime.runtimeState = runtimeState;
601
+ qqRuntime.lastError = lastError;
602
+ desktopQqRuntimeSnapshot.runtimeState = runtimeState;
603
+ desktopQqRuntimeSnapshot.lastError = lastError;
604
+ broadcastQQSettings();
605
+ }
606
+ function sendQQInfo(message) {
607
+ const tab = activeDesktopTab();
608
+ if (tab) {
609
+ emit(
610
+ {
611
+ type: "status",
612
+ id: Date.now(),
613
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
614
+ turn: 0,
615
+ text: message
616
+ },
617
+ tab.id
618
+ );
619
+ }
620
+ void qqRuntime.channel?.sendResponse(message).catch((err) => {
621
+ const active = activeDesktopTab();
622
+ if (active) {
623
+ emit({ type: "$error", message: `qq send failed: ${err.message}` }, active.id);
624
+ }
625
+ });
626
+ }
627
+ function parseIndexedChoice(text) {
628
+ const rawIndex = text.match(/^(\d+)/)?.[1];
629
+ return rawIndex ? Number.parseInt(rawIndex, 10) - 1 : -1;
630
+ }
631
+ function parseRunPermissionChoice(text) {
632
+ const lower = text.toLowerCase();
633
+ if (lower.includes("1") || lower.includes("run")) return "run_once";
634
+ if (lower.includes("2") || lower.includes("always")) return "always_allow";
635
+ return "deny";
636
+ }
637
+ function parsePlanChoice(text) {
638
+ const lower = text.toLowerCase();
639
+ if (lower.includes("1") || lower.includes("approve")) return "approve";
640
+ if (lower.includes("2") || lower.includes("refine")) return "refine";
641
+ return "cancel";
642
+ }
643
+ function parseCheckpointChoice(text) {
644
+ const lower = text.toLowerCase();
645
+ if (lower.includes("1") || lower.includes("continue")) return "continue";
646
+ if (lower.includes("2") || lower.includes("revise")) return "revise";
647
+ return "stop";
648
+ }
649
+ function parseRevisionChoice(text) {
650
+ const lower = text.toLowerCase();
651
+ if (lower.includes("1") || lower.includes("accept")) return "accept";
652
+ if (lower.includes("2") || lower.includes("reject")) return "reject";
653
+ return "cancel";
654
+ }
655
+ function stripFollowupPrefix(text) {
656
+ return text.replace(
657
+ /^(?:\d+\s*|approve\s*|refine\s*|cancel\s*|continue\s*|revise\s*|stop\s*|accept\s*|reject\s*|run\s*|always\s*|deny\s*)/iu,
658
+ ""
659
+ ).trim();
660
+ }
661
+ function handleQQPauseReply(text) {
662
+ if (qqRuntime.interaction.kind === null || qqRuntime.pendingGateId === null) return false;
663
+ qqRuntime.replyThisTurn = true;
664
+ const followup = stripFollowupPrefix(text);
665
+ const interaction = qqRuntime.interaction;
666
+ qqRuntime.interaction = { kind: null, payload: null };
667
+ const gateId = qqRuntime.pendingGateId;
668
+ qqRuntime.pendingGateId = null;
669
+ switch (interaction.kind) {
670
+ case "run_command":
671
+ case "run_background":
672
+ case "path_access":
673
+ pauseGate.resolve(gateId, parseRunPermissionChoice(text));
674
+ return true;
675
+ case "plan_proposed": {
676
+ const payload = interaction.payload ?? {};
677
+ const choice = parsePlanChoice(text);
678
+ if (choice === "cancel") {
679
+ pauseGate.cancel(gateId);
680
+ } else {
681
+ pauseGate.resolve(gateId, {
682
+ type: choice === "approve" ? "approve" : "refine",
683
+ feedback: followup,
684
+ override: {
685
+ plan: payload.plan ?? "",
686
+ mode: choice === "approve" ? "approve" : "refine"
687
+ }
688
+ });
689
+ }
690
+ return true;
691
+ }
692
+ case "plan_checkpoint": {
693
+ const payload = interaction.payload ?? {};
694
+ const choice = parseCheckpointChoice(text);
695
+ if (choice === "revise") {
696
+ pauseGate.resolve(gateId, {
697
+ type: "revise",
698
+ feedback: followup,
699
+ checkpoint: { stepId: payload.stepId ?? "", title: payload.title }
700
+ });
701
+ } else {
702
+ pauseGate.resolve(gateId, { type: choice });
703
+ }
704
+ return true;
705
+ }
706
+ case "plan_revision":
707
+ pauseGate.resolve(gateId, parseRevisionChoice(text));
708
+ return true;
709
+ case "choice": {
710
+ const payload = interaction.payload ?? {};
711
+ const options = payload.options ?? [];
712
+ const pickedIndex = parseIndexedChoice(text);
713
+ if (pickedIndex >= 0 && pickedIndex < options.length) {
714
+ const selected = options[pickedIndex];
715
+ if (selected) pauseGate.resolve(gateId, { type: "pick", optionId: selected.id });
716
+ return true;
717
+ }
718
+ for (const option of options) {
719
+ if (text.toLowerCase().includes(option.title.toLowerCase())) {
720
+ pauseGate.resolve(gateId, { type: "pick", optionId: option.id });
721
+ return true;
722
+ }
723
+ }
724
+ pauseGate.resolve(
725
+ gateId,
726
+ payload.allowCustom ? { type: "text", text } : { type: "cancel" }
727
+ );
728
+ return true;
729
+ }
730
+ default:
731
+ return false;
732
+ }
733
+ }
734
+ function handleQQPauseRequest(tab, kind, payload) {
735
+ if (!qqRuntime.channel) return;
736
+ qqRuntime.interaction = { kind, payload };
737
+ let qqMessage = "";
738
+ switch (kind) {
739
+ case "run_command":
740
+ case "run_background": {
741
+ const p = payload;
742
+ qqMessage = `Need confirmation
743
+
744
+ Command: \`${p.command}\`
745
+
746
+ Reply with:
747
+ 1. Run once
748
+ 2. Always allow
749
+ 3. Deny`;
750
+ break;
751
+ }
752
+ case "path_access": {
753
+ const p = payload;
754
+ const intentText = p.intent === "read" ? "Read" : "Write";
755
+ qqMessage = `Need file access confirmation
756
+
757
+ Action: ${intentText}
758
+ Path: ${p.path}
759
+ Tool: ${p.toolName}
760
+
761
+ Reply with:
762
+ 1. Run once
763
+ 2. Always allow
764
+ 3. Deny`;
765
+ break;
766
+ }
767
+ case "plan_proposed": {
768
+ const p = payload;
769
+ qqMessage = `Plan confirmation
770
+
771
+ ${p.plan}
772
+
773
+ Reply with:
774
+ 1. Approve
775
+ 2. Refine
776
+ 3. Cancel`;
777
+ break;
778
+ }
779
+ case "plan_checkpoint": {
780
+ const p = payload;
781
+ qqMessage = `Step complete (${tab.completedStepIds.size}/${tab.planTotalSteps})
782
+
783
+ ${p.title ? `Step: ${p.title}
784
+ ` : ""}Result: ${p.result}
785
+
786
+ Reply with:
787
+ 1. Continue
788
+ 2. Revise
789
+ 3. Stop`;
790
+ break;
791
+ }
792
+ case "plan_revision": {
793
+ const p = payload;
794
+ qqMessage = `Plan revision proposed
795
+
796
+ ${p.reason}
797
+
798
+ Reply with:
799
+ 1. Accept
800
+ 2. Reject
801
+ 3. Cancel`;
802
+ break;
803
+ }
804
+ case "choice": {
805
+ const p = payload;
806
+ const optionsList = p.options.map((opt, idx) => `${idx + 1}. ${opt.title}`).join("\n");
807
+ qqMessage = `Please choose
808
+
809
+ ${p.question}
810
+
811
+ Options:
812
+ ${optionsList}${p.allowCustom ? "\n\n(You can also reply with custom text.)" : ""}`;
813
+ break;
814
+ }
815
+ }
816
+ if (qqMessage) {
817
+ void qqRuntime.channel.sendResponse(qqMessage).catch((err) => {
818
+ emit({ type: "$error", message: `qq send failed: ${err.message}` }, tab.id);
819
+ });
820
+ }
821
+ }
822
+ async function startDesktopQQ(shouldPersistEnabled = true) {
823
+ const current = loadQQConfig();
824
+ if (!(current.appId && current.appSecret)) {
825
+ throw new Error("QQ App ID and App Secret are required.");
826
+ }
827
+ if (qqRuntime.channel) {
828
+ qqRuntime.channel.refreshAccessConfig();
829
+ setQQRuntimeState("connected");
830
+ return;
831
+ }
832
+ setQQRuntimeState("connecting");
833
+ const channel = new QQChannel({
834
+ onSubmitMessage: (text) => {
835
+ const tab = activeDesktopTab();
836
+ if (!tab) return;
837
+ const trimmed = text.trim();
838
+ if (!trimmed) return;
839
+ emit(
840
+ {
841
+ type: "user.message",
842
+ id: Date.now(),
843
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
844
+ turn: 0,
845
+ text: trimmed
846
+ },
847
+ tab.id
848
+ );
849
+ if (handleQQPauseReply(trimmed)) return;
850
+ if (tab.aborter) {
851
+ void channel.sendResponse(
852
+ "Session is busy. Wait for the current turn or reply to the pending prompt."
853
+ ).catch(() => void 0);
854
+ return;
855
+ }
856
+ qqRuntime.replyThisTurn = true;
857
+ void runTurn(tab, trimmed, true);
858
+ },
859
+ onError: (message) => {
860
+ const tab = activeDesktopTab();
861
+ setQQRuntimeState("failed", message);
862
+ if (tab) emit({ type: "$error", message: `QQ: ${message}` }, tab.id);
863
+ }
864
+ });
865
+ try {
866
+ await channel.start();
867
+ qqRuntime.channel = channel;
868
+ if (shouldPersistEnabled) setDesktopQQEnabled(true);
869
+ setQQRuntimeState("connected");
870
+ } catch (err) {
871
+ await channel.stop().catch(() => void 0);
872
+ qqRuntime.channel = null;
873
+ if (shouldPersistEnabled) setDesktopQQEnabled(false);
874
+ setQQRuntimeState("failed", err.message);
875
+ throw err;
876
+ }
877
+ }
878
+ async function stopDesktopQQ(shouldDisable = true) {
879
+ const channel = qqRuntime.channel;
880
+ qqRuntime.channel = null;
881
+ qqRuntime.interaction = { kind: null, payload: null };
882
+ qqRuntime.pendingGateId = null;
883
+ qqRuntime.replyThisTurn = false;
884
+ if (channel) await channel.stop();
885
+ if (shouldDisable) setDesktopQQEnabled(false);
886
+ setQQRuntimeState("disconnected");
887
+ }
543
888
  function createTabSkeleton(initialDir) {
544
889
  const dir = resolve(initialDir ?? opts.dir ?? loadWorkspaceDir() ?? process.cwd());
545
890
  pushRecentWorkspace(dir);
@@ -667,10 +1012,12 @@ async function desktopCommand(opts) {
667
1012
  persistOpenTabs();
668
1013
  emit({ type: "$tab_closed" }, tab.id);
669
1014
  }
670
- async function runTurn(tab, text) {
1015
+ async function runTurn(tab, text, fromQQ = false) {
671
1016
  if (!tab.runtime) return;
672
1017
  const rt = tab.runtime;
673
1018
  tab.aborter = new AbortController();
1019
+ qqRuntime.replyThisTurn = fromQQ;
1020
+ let lastAssistantText = "";
674
1021
  if (tab.currentSession) {
675
1022
  const existing = loadSessionMeta(tab.currentSession).summary;
676
1023
  if (!existing || !existing.trim()) {
@@ -686,6 +1033,9 @@ async function desktopCommand(opts) {
686
1033
  await tabContext.run(tab.id, async () => {
687
1034
  try {
688
1035
  for await (const ev of rt.loop.step(text)) {
1036
+ if (ev.role === "assistant_final" && ev.content) {
1037
+ lastAssistantText = ev.content;
1038
+ }
689
1039
  for (const kev of rt.eventizer.consume(ev, rt.ctx)) emit(kev, tab.id);
690
1040
  if (ev.role === "tool" && (ev.toolName === "remember" || ev.toolName === "forget")) {
691
1041
  emitMemory(tab);
@@ -696,6 +1046,12 @@ async function desktopCommand(opts) {
696
1046
  emit({ type: "$error", message: err.message }, tab.id);
697
1047
  } finally {
698
1048
  tab.aborter = null;
1049
+ if (fromQQ && lastAssistantText && qqRuntime.channel && qqRuntime.replyThisTurn) {
1050
+ await qqRuntime.channel.sendResponse(lastAssistantText).catch((err) => {
1051
+ emit({ type: "$error", message: `qq send failed: ${err.message}` }, tab.id);
1052
+ });
1053
+ }
1054
+ qqRuntime.replyThisTurn = false;
699
1055
  emit({ type: "$turn_complete" }, tab.id);
700
1056
  if (tab.planTotalSteps > 0 && tab.completedStepIds.size >= tab.planTotalSteps) {
701
1057
  tab.completedStepIds.clear();
@@ -828,11 +1184,11 @@ async function desktopCommand(opts) {
828
1184
  emit({ type: "$plan_cleared" }, tab.id);
829
1185
  }
830
1186
  }
831
- let first;
832
1187
  let shuttingDown = false;
833
1188
  async function gracefulShutdown() {
834
1189
  if (shuttingDown) return;
835
1190
  shuttingDown = true;
1191
+ await stopDesktopQQ(false).catch(() => void 0);
836
1192
  await Promise.allSettled(
837
1193
  [...tabs.values()].map((t) => t.toolset?.jobs.shutdown(1500) ?? Promise.resolve())
838
1194
  );
@@ -848,6 +1204,7 @@ async function desktopCommand(opts) {
848
1204
  const tab = activeRunningTab();
849
1205
  const tabId = tab?.id;
850
1206
  if (tab) tab.pendingGateIds.add(req.id);
1207
+ qqRuntime.pendingGateId = req.id;
851
1208
  const auto = autoResolveVerdict(req, loadEditMode());
852
1209
  if (auto !== null) {
853
1210
  if (req.kind === "plan_checkpoint") {
@@ -874,6 +1231,7 @@ async function desktopCommand(opts) {
874
1231
  { type: "$confirm_required", id: req.id, kind: req.kind, command: payload.command ?? "" },
875
1232
  tabId
876
1233
  );
1234
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
877
1235
  return;
878
1236
  }
879
1237
  if (req.kind === "path_access") {
@@ -890,6 +1248,7 @@ async function desktopCommand(opts) {
890
1248
  },
891
1249
  tabId
892
1250
  );
1251
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
893
1252
  return;
894
1253
  }
895
1254
  if (req.kind === "choice") {
@@ -904,6 +1263,7 @@ async function desktopCommand(opts) {
904
1263
  },
905
1264
  tabId
906
1265
  );
1266
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
907
1267
  return;
908
1268
  }
909
1269
  if (req.kind === "plan_proposed") {
@@ -922,6 +1282,7 @@ async function desktopCommand(opts) {
922
1282
  },
923
1283
  tabId
924
1284
  );
1285
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
925
1286
  return;
926
1287
  }
927
1288
  if (req.kind === "plan_checkpoint") {
@@ -950,6 +1311,7 @@ async function desktopCommand(opts) {
950
1311
  },
951
1312
  tabId
952
1313
  );
1314
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
953
1315
  return;
954
1316
  }
955
1317
  if (req.kind === "plan_revision") {
@@ -964,6 +1326,7 @@ async function desktopCommand(opts) {
964
1326
  },
965
1327
  tabId
966
1328
  );
1329
+ if (tab) handleQQPauseRequest(tab, req.kind, payload);
967
1330
  return;
968
1331
  }
969
1332
  const exhaustive = req.kind;
@@ -1035,6 +1398,12 @@ async function desktopCommand(opts) {
1035
1398
  const activeIdx = savedTabs.findIndex((t) => t.active);
1036
1399
  lastActiveTabId = ((activeIdx >= 0 ? restored[activeIdx] : first) ?? first).id;
1037
1400
  persistOpenTabs();
1401
+ const qqConfig = loadQQConfig();
1402
+ if (qqConfig.enabled && qqConfig.appId && qqConfig.appSecret) {
1403
+ void startDesktopQQ(false).catch(() => void 0);
1404
+ } else {
1405
+ broadcastQQSettings();
1406
+ }
1038
1407
  const rl = createInterface({ input: stdin });
1039
1408
  rl.on("line", (line) => {
1040
1409
  const trimmed = line.trim();
@@ -1126,6 +1495,25 @@ async function desktopCommand(opts) {
1126
1495
  }
1127
1496
  return;
1128
1497
  }
1498
+ if (msg.cmd === "desktop_resync") {
1499
+ const hasKey = !!loadApiKey();
1500
+ for (const t of tabs.values()) {
1501
+ emit(
1502
+ { type: "$tab_opened", workspaceDir: t.rootDir, active: t.id === lastActiveTabId },
1503
+ t.id
1504
+ );
1505
+ emitSessions(t);
1506
+ emitSettings(t);
1507
+ emitMcpSpecs(t);
1508
+ emitSkills(t);
1509
+ emitMemory(t);
1510
+ emitQQSettings(t);
1511
+ if (!hasKey) emit({ type: "$needs_setup", reason: "no_api_key" }, t.id);
1512
+ else if (t.toolset) emit({ type: "$ready" }, t.id);
1513
+ void emitBalance(t);
1514
+ }
1515
+ return;
1516
+ }
1129
1517
  if (msg.cmd === "jobs_list") {
1130
1518
  emitJobs();
1131
1519
  return;
@@ -1367,18 +1755,38 @@ ${found.body}${argsLine}`;
1367
1755
  if (msg.cmd === "qq_connect") {
1368
1756
  try {
1369
1757
  const current = loadQQConfig();
1370
- setDesktopQQEnabled(true);
1371
1758
  emit(
1372
1759
  {
1373
1760
  type: "status",
1374
1761
  id: Date.now(),
1375
1762
  ts: (/* @__PURE__ */ new Date()).toISOString(),
1376
1763
  turn: 0,
1377
- text: `QQ enabled for CLI (${current.sandbox ? "sandbox" : "production"}) \u2014 start the bot by running \`reasonix\` in a terminal`
1764
+ text: `QQ connecting (${current.sandbox ? "sandbox" : "production"})`
1378
1765
  },
1379
1766
  tab.id
1380
1767
  );
1381
- emitQQSettings(tab);
1768
+ void startDesktopQQ(true).then(
1769
+ () => {
1770
+ emit(
1771
+ {
1772
+ type: "status",
1773
+ id: Date.now(),
1774
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1775
+ turn: 0,
1776
+ text: `QQ connected (${current.sandbox ? "sandbox" : "production"})`
1777
+ },
1778
+ tab.id
1779
+ );
1780
+ emitQQSettings(tab);
1781
+ },
1782
+ (err) => {
1783
+ emit(
1784
+ { type: "$error", message: `qq_connect failed: ${err.message}` },
1785
+ tab.id
1786
+ );
1787
+ emitQQSettings(tab);
1788
+ }
1789
+ );
1382
1790
  } catch (err) {
1383
1791
  emit({ type: "$error", message: `qq_connect failed: ${err.message}` }, tab.id);
1384
1792
  emitQQSettings(tab);
@@ -1387,18 +1795,27 @@ ${found.body}${argsLine}`;
1387
1795
  }
1388
1796
  if (msg.cmd === "qq_disconnect") {
1389
1797
  try {
1390
- setDesktopQQEnabled(false);
1391
- emit(
1392
- {
1393
- type: "status",
1394
- id: Date.now(),
1395
- ts: (/* @__PURE__ */ new Date()).toISOString(),
1396
- turn: 0,
1397
- text: "QQ disabled for CLI (next `reasonix` terminal session won't auto-start the bot)"
1798
+ void stopDesktopQQ(true).then(
1799
+ () => {
1800
+ emit(
1801
+ {
1802
+ type: "status",
1803
+ id: Date.now(),
1804
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1805
+ turn: 0,
1806
+ text: "QQ disabled"
1807
+ },
1808
+ tab.id
1809
+ );
1810
+ emitQQSettings(tab);
1398
1811
  },
1399
- tab.id
1812
+ (err) => {
1813
+ emit(
1814
+ { type: "$error", message: `qq_disconnect failed: ${err.message}` },
1815
+ tab.id
1816
+ );
1817
+ }
1400
1818
  );
1401
- emitQQSettings(tab);
1402
1819
  } catch (err) {
1403
1820
  emit(
1404
1821
  { type: "$error", message: `qq_disconnect failed: ${err.message}` },
@@ -1481,7 +1898,7 @@ ${found.body}${argsLine}`;
1481
1898
  }
1482
1899
  if (msg.cmd === "compact_history") {
1483
1900
  if (!tab.runtime) return;
1484
- void tab.runtime.loop.compactHistory().catch((err) => {
1901
+ void tab.runtime.loop.compactHistory().then(() => emitCtxBreakdown(tab)).catch((err) => {
1485
1902
  emit({ type: "$error", message: `/compact failed: ${err.message}` }, tab.id);
1486
1903
  });
1487
1904
  return;
@@ -1535,6 +1952,7 @@ ${found.body}${argsLine}`;
1535
1952
  }
1536
1953
  export {
1537
1954
  desktopCommand,
1538
- installDesktopCrashGuards
1955
+ installDesktopCrashGuards,
1956
+ writeAllSync
1539
1957
  };
1540
- //# sourceMappingURL=desktop-EG6P5SF2.js.map
1958
+ //# sourceMappingURL=desktop-562OPWIU.js.map