@openacp/cli 2026.327.5 → 2026.330.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +13 -13
  2. package/dist/adapter-4U6MC5ZS.js +13 -0
  3. package/dist/agent-catalog-SZQQERV7.js +10 -0
  4. package/dist/{agent-dependencies-WS7Z2DFW.js → agent-dependencies-ED2ZTUHG.js} +1 -2
  5. package/dist/{agent-registry-5LZT7CUB.js → agent-registry-YOGP656W.js} +1 -2
  6. package/dist/agent-store-5UHZH2XI.js +8 -0
  7. package/dist/{api-client-AQPNKXI2.js → api-client-XTLRRFPX.js} +1 -2
  8. package/dist/api-server-5VNYFWJE.js +7 -0
  9. package/dist/api-server-JLBDKCU4.js +10 -0
  10. package/dist/{autostart-6JS565RY.js → autostart-CUPZMKKC.js} +3 -4
  11. package/dist/{chunk-WIIZNPCR.js → chunk-2KT6TROD.js} +12 -33
  12. package/dist/chunk-2KT6TROD.js.map +1 -0
  13. package/dist/{chunk-PPSMUECX.js → chunk-2R5XM3ES.js} +2 -2
  14. package/dist/{chunk-SNPYTMPR.js → chunk-3EWTPOF7.js} +2 -2
  15. package/dist/{chunk-YEULD3SG.js → chunk-3NAFXVQM.js} +7 -2
  16. package/dist/{chunk-YEULD3SG.js.map → chunk-3NAFXVQM.js.map} +1 -1
  17. package/dist/{chunk-A6Y4GZM3.js → chunk-566W6INH.js} +2 -2
  18. package/dist/{chunk-ODUM3D6X.js → chunk-5HKQCYOI.js} +1 -39
  19. package/dist/chunk-5HKQCYOI.js.map +1 -0
  20. package/dist/{plugin-installer-QVJP6VKV.js → chunk-5WGVYX3C.js} +18 -5
  21. package/dist/chunk-5WGVYX3C.js.map +1 -0
  22. package/dist/{chunk-XWDW3XBE.js → chunk-5ZNBNIK3.js} +1331 -237
  23. package/dist/chunk-5ZNBNIK3.js.map +1 -0
  24. package/dist/{chunk-XIBG7LSL.js → chunk-7RKPIM3E.js} +482 -175
  25. package/dist/chunk-7RKPIM3E.js.map +1 -0
  26. package/dist/{chunk-WXVT3AOY.js → chunk-7ZCQF6QM.js} +8 -3
  27. package/dist/chunk-7ZCQF6QM.js.map +1 -0
  28. package/dist/{chunk-2YCW3QDV.js → chunk-BTJHGSLM.js} +8 -7
  29. package/dist/chunk-BTJHGSLM.js.map +1 -0
  30. package/dist/{chunk-P2G275VD.js → chunk-CFUJGWOP.js} +3 -3
  31. package/dist/{chunk-RBYBSSGO.js → chunk-FCTC7KDT.js} +2 -2
  32. package/dist/{chunk-QAQDGPB4.js → chunk-GEOXPGCO.js} +3 -3
  33. package/dist/{chunk-BLQUXO7S.js → chunk-IZ5UEZF7.js} +27 -2
  34. package/dist/chunk-IZ5UEZF7.js.map +1 -0
  35. package/dist/{chunk-4GMLGCF2.js → chunk-KDU3ZEWT.js} +2 -2
  36. package/dist/{chunk-QWP76EBW.js → chunk-MITTQMGZ.js} +16 -9
  37. package/dist/chunk-MITTQMGZ.js.map +1 -0
  38. package/dist/{chunk-HRKAXFWR.js → chunk-MPGEHTGE.js} +9 -9
  39. package/dist/{chunk-KMMEFXIE.js → chunk-PA6MNBG4.js} +41 -9
  40. package/dist/chunk-PA6MNBG4.js.map +1 -0
  41. package/dist/{chunk-BQ6FR32N.js → chunk-QWVHCTCA.js} +2 -2
  42. package/dist/{chunk-XMMAGAT4.js → chunk-R6KZYF7D.js} +8 -1
  43. package/dist/{chunk-XMMAGAT4.js.map → chunk-R6KZYF7D.js.map} +1 -1
  44. package/dist/{chunk-AD3X6DGK.js → chunk-TMVTSWVH.js} +75 -13
  45. package/dist/chunk-TMVTSWVH.js.map +1 -0
  46. package/dist/chunk-UWH7KIAA.js +701 -0
  47. package/dist/chunk-UWH7KIAA.js.map +1 -0
  48. package/dist/{chunk-SHTGQGAU.js → chunk-V2YZWYXT.js} +3 -3
  49. package/dist/{chunk-QVMEF6FB.js → chunk-W4LK6WJP.js} +38 -27
  50. package/dist/chunk-W4LK6WJP.js.map +1 -0
  51. package/dist/{chunk-S3ZGPPXY.js → chunk-YIGBJFJL.js} +9 -13
  52. package/dist/{chunk-S3ZGPPXY.js.map → chunk-YIGBJFJL.js.map} +1 -1
  53. package/dist/cli.js +133 -132
  54. package/dist/cli.js.map +1 -1
  55. package/dist/config-KN6NKKPF.js +20 -0
  56. package/dist/config-editor-76RVZS4B.js +10 -0
  57. package/dist/{config-registry-CUMNXFGK.js → config-registry-ZXAIJNYB.js} +2 -3
  58. package/dist/context-NXXW62NJ.js +9 -0
  59. package/dist/core-plugins-OCHKGCIZ.js +22 -0
  60. package/dist/{daemon-PXO5QPCR.js → daemon-XFEMMJSZ.js} +4 -5
  61. package/dist/{dev-loader-DRU3R7ZM.js → dev-loader-7P3HZCIA.js} +1 -3
  62. package/dist/{dev-loader-DRU3R7ZM.js.map → dev-loader-7P3HZCIA.js.map} +1 -1
  63. package/dist/doctor-AV6AUO22.js +9 -0
  64. package/dist/file-service-HHB3JQIO.js +8 -0
  65. package/dist/index.d.ts +141 -187
  66. package/dist/index.js +33 -29
  67. package/dist/index.js.map +1 -1
  68. package/dist/{install-cloudflared-AN24L4DP.js → install-cloudflared-JRJ4BSOM.js} +3 -4
  69. package/dist/{install-cloudflared-AN24L4DP.js.map → install-cloudflared-JRJ4BSOM.js.map} +1 -1
  70. package/dist/{install-context-XPWTFT3J.js → install-context-EHYV5WRY.js} +2 -3
  71. package/dist/{install-context-XPWTFT3J.js.map → install-context-EHYV5WRY.js.map} +1 -1
  72. package/dist/{install-jq-CRVDJGF3.js → install-jq-ISTGT263.js} +3 -4
  73. package/dist/{install-jq-CRVDJGF3.js.map → install-jq-ISTGT263.js.map} +1 -1
  74. package/dist/{integrate-G6CVXTGT.js → integrate-JIEZYDOR.js} +1 -2
  75. package/dist/{integrate-G6CVXTGT.js.map → integrate-JIEZYDOR.js.map} +1 -1
  76. package/dist/{log-LZ7FTRKG.js → log-YZ243M5G.js} +4 -3
  77. package/dist/{main-3GF3EQTE.js → main-L2M4NTJY.js} +135 -50
  78. package/dist/main-L2M4NTJY.js.map +1 -0
  79. package/dist/{menu-YDQ2LWAR.js → menu-ALFN37IR.js} +1 -2
  80. package/dist/notifications-MO23S7S3.js +8 -0
  81. package/dist/{plugin-create-5HQRF2ID.js → plugin-create-EHL76ZZG.js} +1 -2
  82. package/dist/{plugin-create-5HQRF2ID.js.map → plugin-create-EHL76ZZG.js.map} +1 -1
  83. package/dist/plugin-installer-VSTYZSXC.js +9 -0
  84. package/dist/{plugin-registry-WB3DR67H.js → plugin-registry-6J3YSFHF.js} +1 -2
  85. package/dist/{plugin-search-HQ4WQKOF.js → plugin-search-MGKAL5JM.js} +1 -2
  86. package/dist/{plugin-search-HQ4WQKOF.js.map → plugin-search-MGKAL5JM.js.map} +1 -1
  87. package/dist/{post-upgrade-3ADZRMYJ.js → post-upgrade-Y26S2ZQ7.js} +6 -7
  88. package/dist/{post-upgrade-3ADZRMYJ.js.map → post-upgrade-Y26S2ZQ7.js.map} +1 -1
  89. package/dist/{read-text-file-IRZM3QLM.js → read-text-file-DJBTITIB.js} +1 -2
  90. package/dist/{registry-client-AVGRE4CF.js → registry-client-GTBWLXYU.js} +1 -2
  91. package/dist/{security-YNRBW6S7.js → security-2BA265LN.js} +1 -2
  92. package/dist/{settings-manager-MD2U4ZV2.js → settings-manager-B4UN2LAC.js} +1 -2
  93. package/dist/{setup-A7VPW46C.js → setup-E6BNEYCS.js} +109 -83
  94. package/dist/setup-E6BNEYCS.js.map +1 -0
  95. package/dist/speech-SG62JYIF.js +9 -0
  96. package/dist/{suggest-7D6B542M.js → suggest-RST5VOHB.js} +1 -3
  97. package/dist/{suggest-7D6B542M.js.map → suggest-RST5VOHB.js.map} +1 -1
  98. package/dist/telegram-EAVRDNFU.js +7 -0
  99. package/dist/tunnel-HWJ27WDH.js +7 -0
  100. package/dist/{tunnel-service-QJPUYEKU.js → tunnel-service-ZMO4THKE.js} +90 -11
  101. package/dist/tunnel-service-ZMO4THKE.js.map +1 -0
  102. package/dist/{validators-WSTBNKRW.js → validators-GITLOFXC.js} +1 -2
  103. package/dist/{version-NQZBM5M7.js → version-AXXV6IV2.js} +1 -2
  104. package/package.json +1 -3
  105. package/dist/adapter-JQFQ3JAO.js +0 -15
  106. package/dist/adapter-UORRGHNH.js +0 -1030
  107. package/dist/adapter-UORRGHNH.js.map +0 -1
  108. package/dist/agent-catalog-YHBFERYO.js +0 -11
  109. package/dist/agent-store-VSHNY5GT.js +0 -9
  110. package/dist/api-server-7G3ZUZRM.js +0 -8
  111. package/dist/api-server-CAYNPUF2.js +0 -11
  112. package/dist/chunk-2YCW3QDV.js.map +0 -1
  113. package/dist/chunk-32LVIEPW.js +0 -477
  114. package/dist/chunk-32LVIEPW.js.map +0 -1
  115. package/dist/chunk-AD3X6DGK.js.map +0 -1
  116. package/dist/chunk-BLQUXO7S.js.map +0 -1
  117. package/dist/chunk-KMMEFXIE.js.map +0 -1
  118. package/dist/chunk-ODUM3D6X.js.map +0 -1
  119. package/dist/chunk-QVMEF6FB.js.map +0 -1
  120. package/dist/chunk-QWP76EBW.js.map +0 -1
  121. package/dist/chunk-VUNV25KB.js +0 -16
  122. package/dist/chunk-WIIZNPCR.js.map +0 -1
  123. package/dist/chunk-WXVT3AOY.js.map +0 -1
  124. package/dist/chunk-XIBG7LSL.js.map +0 -1
  125. package/dist/chunk-XWDW3XBE.js.map +0 -1
  126. package/dist/chunk-ZHGPZBS4.js +0 -49
  127. package/dist/chunk-ZHGPZBS4.js.map +0 -1
  128. package/dist/chunk-ZNSO2QVC.js +0 -124
  129. package/dist/chunk-ZNSO2QVC.js.map +0 -1
  130. package/dist/config-I4FMCJGZ.js +0 -15
  131. package/dist/config-editor-7PKW42GZ.js +0 -11
  132. package/dist/context-XM6E22LM.js +0 -10
  133. package/dist/core-plugins-Y5US6RED.js +0 -23
  134. package/dist/dist-UHQK5CXN.js +0 -21151
  135. package/dist/dist-UHQK5CXN.js.map +0 -1
  136. package/dist/doctor-QZQAP46W.js +0 -10
  137. package/dist/file-service-EUODJAIT.js +0 -9
  138. package/dist/main-3GF3EQTE.js.map +0 -1
  139. package/dist/notifications-D5BRDNSU.js +0 -9
  140. package/dist/plugin-installer-QVJP6VKV.js.map +0 -1
  141. package/dist/setup-A7VPW46C.js.map +0 -1
  142. package/dist/slack-2XNWBOWH.js +0 -8
  143. package/dist/speech-2GHQNRIO.js +0 -9
  144. package/dist/telegram-E65IWFBW.js +0 -8
  145. package/dist/tunnel-45HA72MB.js +0 -8
  146. package/dist/tunnel-service-QJPUYEKU.js.map +0 -1
  147. package/dist/version-NQZBM5M7.js.map +0 -1
  148. /package/dist/{adapter-JQFQ3JAO.js.map → adapter-4U6MC5ZS.js.map} +0 -0
  149. /package/dist/{agent-catalog-YHBFERYO.js.map → agent-catalog-SZQQERV7.js.map} +0 -0
  150. /package/dist/{agent-dependencies-WS7Z2DFW.js.map → agent-dependencies-ED2ZTUHG.js.map} +0 -0
  151. /package/dist/{agent-registry-5LZT7CUB.js.map → agent-registry-YOGP656W.js.map} +0 -0
  152. /package/dist/{agent-store-VSHNY5GT.js.map → agent-store-5UHZH2XI.js.map} +0 -0
  153. /package/dist/{api-client-AQPNKXI2.js.map → api-client-XTLRRFPX.js.map} +0 -0
  154. /package/dist/{api-server-7G3ZUZRM.js.map → api-server-5VNYFWJE.js.map} +0 -0
  155. /package/dist/{api-server-CAYNPUF2.js.map → api-server-JLBDKCU4.js.map} +0 -0
  156. /package/dist/{autostart-6JS565RY.js.map → autostart-CUPZMKKC.js.map} +0 -0
  157. /package/dist/{chunk-PPSMUECX.js.map → chunk-2R5XM3ES.js.map} +0 -0
  158. /package/dist/{chunk-SNPYTMPR.js.map → chunk-3EWTPOF7.js.map} +0 -0
  159. /package/dist/{chunk-A6Y4GZM3.js.map → chunk-566W6INH.js.map} +0 -0
  160. /package/dist/{chunk-P2G275VD.js.map → chunk-CFUJGWOP.js.map} +0 -0
  161. /package/dist/{chunk-RBYBSSGO.js.map → chunk-FCTC7KDT.js.map} +0 -0
  162. /package/dist/{chunk-QAQDGPB4.js.map → chunk-GEOXPGCO.js.map} +0 -0
  163. /package/dist/{chunk-4GMLGCF2.js.map → chunk-KDU3ZEWT.js.map} +0 -0
  164. /package/dist/{chunk-HRKAXFWR.js.map → chunk-MPGEHTGE.js.map} +0 -0
  165. /package/dist/{chunk-BQ6FR32N.js.map → chunk-QWVHCTCA.js.map} +0 -0
  166. /package/dist/{chunk-SHTGQGAU.js.map → chunk-V2YZWYXT.js.map} +0 -0
  167. /package/dist/{chunk-VUNV25KB.js.map → config-KN6NKKPF.js.map} +0 -0
  168. /package/dist/{config-I4FMCJGZ.js.map → config-editor-76RVZS4B.js.map} +0 -0
  169. /package/dist/{config-editor-7PKW42GZ.js.map → config-registry-ZXAIJNYB.js.map} +0 -0
  170. /package/dist/{config-registry-CUMNXFGK.js.map → context-NXXW62NJ.js.map} +0 -0
  171. /package/dist/{context-XM6E22LM.js.map → core-plugins-OCHKGCIZ.js.map} +0 -0
  172. /package/dist/{core-plugins-Y5US6RED.js.map → daemon-XFEMMJSZ.js.map} +0 -0
  173. /package/dist/{daemon-PXO5QPCR.js.map → doctor-AV6AUO22.js.map} +0 -0
  174. /package/dist/{doctor-QZQAP46W.js.map → file-service-HHB3JQIO.js.map} +0 -0
  175. /package/dist/{file-service-EUODJAIT.js.map → log-YZ243M5G.js.map} +0 -0
  176. /package/dist/{log-LZ7FTRKG.js.map → menu-ALFN37IR.js.map} +0 -0
  177. /package/dist/{menu-YDQ2LWAR.js.map → notifications-MO23S7S3.js.map} +0 -0
  178. /package/dist/{notifications-D5BRDNSU.js.map → plugin-installer-VSTYZSXC.js.map} +0 -0
  179. /package/dist/{plugin-registry-WB3DR67H.js.map → plugin-registry-6J3YSFHF.js.map} +0 -0
  180. /package/dist/{read-text-file-IRZM3QLM.js.map → read-text-file-DJBTITIB.js.map} +0 -0
  181. /package/dist/{registry-client-AVGRE4CF.js.map → registry-client-GTBWLXYU.js.map} +0 -0
  182. /package/dist/{security-YNRBW6S7.js.map → security-2BA265LN.js.map} +0 -0
  183. /package/dist/{settings-manager-MD2U4ZV2.js.map → settings-manager-B4UN2LAC.js.map} +0 -0
  184. /package/dist/{slack-2XNWBOWH.js.map → speech-SG62JYIF.js.map} +0 -0
  185. /package/dist/{speech-2GHQNRIO.js.map → telegram-EAVRDNFU.js.map} +0 -0
  186. /package/dist/{telegram-E65IWFBW.js.map → tunnel-HWJ27WDH.js.map} +0 -0
  187. /package/dist/{tunnel-45HA72MB.js.map → validators-GITLOFXC.js.map} +0 -0
  188. /package/dist/{validators-WSTBNKRW.js.map → version-AXXV6IV2.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AgentCatalog
3
- } from "./chunk-SHTGQGAU.js";
3
+ } from "./chunk-V2YZWYXT.js";
4
4
  import {
5
5
  getAgentCapabilities
6
6
  } from "./chunk-ZSLHHQPQ.js";
@@ -8,31 +8,42 @@ import {
8
8
  readTextFileWithRange
9
9
  } from "./chunk-OYSAN7UX.js";
10
10
  import {
11
+ closeSessionLogger,
11
12
  createChildLogger,
12
13
  createSessionLogger
13
- } from "./chunk-XMMAGAT4.js";
14
+ } from "./chunk-R6KZYF7D.js";
14
15
 
15
16
  // src/core/utils/streams.ts
16
17
  function nodeToWebWritable(nodeStream) {
17
18
  return new WritableStream({
18
19
  write(chunk) {
19
20
  return new Promise((resolve, reject) => {
20
- nodeStream.write(Buffer.from(chunk), (err) => {
21
- if (err) reject(err);
22
- else resolve();
23
- });
21
+ const ok = nodeStream.write(chunk);
22
+ if (ok) {
23
+ resolve();
24
+ return;
25
+ }
26
+ nodeStream.once("drain", resolve);
27
+ nodeStream.once("error", reject);
24
28
  });
29
+ },
30
+ close() {
31
+ nodeStream.end();
32
+ },
33
+ abort(reason) {
34
+ nodeStream.destroy(reason instanceof Error ? reason : new Error(String(reason)));
25
35
  }
26
36
  });
27
37
  }
28
38
  function nodeToWebReadable(nodeStream) {
29
39
  return new ReadableStream({
30
40
  start(controller) {
31
- nodeStream.on("data", (chunk) => {
32
- controller.enqueue(new Uint8Array(chunk));
33
- });
41
+ nodeStream.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk)));
34
42
  nodeStream.on("end", () => controller.close());
35
43
  nodeStream.on("error", (err) => controller.error(err));
44
+ },
45
+ cancel() {
46
+ nodeStream.destroy();
36
47
  }
37
48
  });
38
49
  }
@@ -131,8 +142,8 @@ var TypedEmitter = class {
131
142
  // src/core/agents/agent-instance.ts
132
143
  import { spawn as spawn2, execFileSync } from "child_process";
133
144
  import { Transform } from "stream";
134
- import fs from "fs";
135
- import path from "path";
145
+ import fs2 from "fs";
146
+ import path2 from "path";
136
147
  import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
137
148
  import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
138
149
 
@@ -286,15 +297,48 @@ var McpManager = class {
286
297
  }
287
298
  };
288
299
 
300
+ // src/core/utils/debug-tracer.ts
301
+ import fs from "fs";
302
+ import path from "path";
303
+ var DEBUG_ENABLED = process.env.OPENACP_DEBUG === "true" || process.env.OPENACP_DEBUG === "1";
304
+ var DebugTracer = class {
305
+ constructor(sessionId, workingDirectory) {
306
+ this.sessionId = sessionId;
307
+ this.workingDirectory = workingDirectory;
308
+ this.logDir = path.join(workingDirectory, ".log");
309
+ }
310
+ dirCreated = false;
311
+ logDir;
312
+ log(layer, data) {
313
+ try {
314
+ if (!this.dirCreated) {
315
+ fs.mkdirSync(this.logDir, { recursive: true });
316
+ this.dirCreated = true;
317
+ }
318
+ const filePath = path.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
319
+ const line = JSON.stringify({ ts: Date.now(), ...data }) + "\n";
320
+ fs.appendFileSync(filePath, line);
321
+ } catch {
322
+ }
323
+ }
324
+ /** No-op cleanup — establishes the pattern for future async implementations */
325
+ destroy() {
326
+ }
327
+ };
328
+ function createDebugTracer(sessionId, workingDirectory) {
329
+ if (!DEBUG_ENABLED) return null;
330
+ return new DebugTracer(sessionId, workingDirectory);
331
+ }
332
+
289
333
  // src/core/agents/agent-instance.ts
290
334
  var log = createChildLogger({ module: "agent-instance" });
291
335
  function findPackageRoot(startDir) {
292
336
  let dir = startDir;
293
- while (dir !== path.dirname(dir)) {
294
- if (fs.existsSync(path.join(dir, "package.json"))) {
337
+ while (dir !== path2.dirname(dir)) {
338
+ if (fs2.existsSync(path2.join(dir, "package.json"))) {
295
339
  return dir;
296
340
  }
297
- dir = path.dirname(dir);
341
+ dir = path2.dirname(dir);
298
342
  }
299
343
  return startDir;
300
344
  }
@@ -306,26 +350,26 @@ function resolveAgentCommand(cmd) {
306
350
  }
307
351
  for (const root of searchRoots) {
308
352
  const packageDirs = [
309
- path.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
310
- path.resolve(root, "node_modules", cmd, "dist", "index.js")
353
+ path2.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
354
+ path2.resolve(root, "node_modules", cmd, "dist", "index.js")
311
355
  ];
312
356
  for (const jsPath of packageDirs) {
313
- if (fs.existsSync(jsPath)) {
357
+ if (fs2.existsSync(jsPath)) {
314
358
  return { command: process.execPath, args: [jsPath] };
315
359
  }
316
360
  }
317
361
  }
318
362
  for (const root of searchRoots) {
319
- const localBin = path.resolve(root, "node_modules", ".bin", cmd);
320
- if (fs.existsSync(localBin)) {
321
- const content = fs.readFileSync(localBin, "utf-8");
363
+ const localBin = path2.resolve(root, "node_modules", ".bin", cmd);
364
+ if (fs2.existsSync(localBin)) {
365
+ const content = fs2.readFileSync(localBin, "utf-8");
322
366
  if (content.startsWith("#!/usr/bin/env node")) {
323
367
  return { command: process.execPath, args: [localBin] };
324
368
  }
325
369
  const match = content.match(/"([^"]+\.js)"/);
326
370
  if (match) {
327
- const target = path.resolve(path.dirname(localBin), match[1]);
328
- if (fs.existsSync(target)) {
371
+ const target = path2.resolve(path2.dirname(localBin), match[1]);
372
+ if (fs2.existsSync(target)) {
329
373
  return { command: process.execPath, args: [target] };
330
374
  }
331
375
  }
@@ -334,7 +378,7 @@ function resolveAgentCommand(cmd) {
334
378
  try {
335
379
  const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
336
380
  if (fullPath) {
337
- const content = fs.readFileSync(fullPath, "utf-8");
381
+ const content = fs2.readFileSync(fullPath, "utf-8");
338
382
  if (content.startsWith("#!/usr/bin/env node")) {
339
383
  return { command: process.execPath, args: [fullPath] };
340
384
  }
@@ -349,10 +393,12 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
349
393
  stderrCapture;
350
394
  terminalManager = new TerminalManager();
351
395
  static mcpManager = new McpManager();
396
+ _destroying = false;
352
397
  sessionId;
353
398
  agentName;
354
399
  promptCapabilities;
355
400
  middlewareChain;
401
+ debugTracer = null;
356
402
  // Callback — set by core when wiring events
357
403
  onPermissionRequest = async () => "";
358
404
  constructor(agentName) {
@@ -395,20 +441,28 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
395
441
  });
396
442
  const stdinLogger = new Transform({
397
443
  transform(chunk, _enc, cb) {
398
- log.debug(
399
- { direction: "send", raw: chunk.toString().trimEnd() },
400
- "ACP raw"
401
- );
444
+ if (instance.debugTracer) {
445
+ const raw = chunk.toString().trimEnd();
446
+ try {
447
+ instance.debugTracer.log("acp", { dir: "send", data: JSON.parse(raw) });
448
+ } catch {
449
+ instance.debugTracer.log("acp", { dir: "send", data: raw });
450
+ }
451
+ }
402
452
  cb(null, chunk);
403
453
  }
404
454
  });
405
455
  stdinLogger.pipe(instance.child.stdin);
406
456
  const stdoutLogger = new Transform({
407
457
  transform(chunk, _enc, cb) {
408
- log.debug(
409
- { direction: "recv", raw: chunk.toString().trimEnd() },
410
- "ACP raw"
411
- );
458
+ if (instance.debugTracer) {
459
+ const raw = chunk.toString().trimEnd();
460
+ try {
461
+ instance.debugTracer.log("acp", { dir: "recv", data: JSON.parse(raw) });
462
+ } catch {
463
+ instance.debugTracer.log("acp", { dir: "recv", data: raw });
464
+ }
465
+ }
412
466
  cb(null, chunk);
413
467
  }
414
468
  });
@@ -442,15 +496,17 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
442
496
  }
443
497
  setupCrashDetection() {
444
498
  this.child.on("exit", (code, signal) => {
499
+ if (this._destroying) return;
445
500
  log.info(
446
501
  { sessionId: this.sessionId, exitCode: code, signal },
447
502
  "Agent process exited"
448
503
  );
449
- if (code !== 0 && code !== null) {
504
+ if (code !== 0 && code !== null || signal) {
450
505
  const stderr = this.stderrCapture.getLastLines();
451
506
  this.emit("agent_event", {
452
507
  type: "error",
453
- message: `Agent crashed (exit code ${code})
508
+ message: signal ? `Agent killed by signal ${signal}
509
+ ${stderr}` : `Agent crashed (exit code ${code})
454
510
  ${stderr}`
455
511
  });
456
512
  }
@@ -475,6 +531,7 @@ ${stderr}`
475
531
  mcpServers: resolvedMcp
476
532
  });
477
533
  instance.sessionId = response.sessionId;
534
+ instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
478
535
  instance.setupCrashDetection();
479
536
  log.info(
480
537
  { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
@@ -495,6 +552,7 @@ ${stderr}`
495
552
  cwd: workingDirectory
496
553
  });
497
554
  instance.sessionId = response.sessionId;
555
+ instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
498
556
  log.info(
499
557
  { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
500
558
  "Agent resume complete"
@@ -510,6 +568,7 @@ ${stderr}`
510
568
  mcpServers: resolvedMcp
511
569
  });
512
570
  instance.sessionId = response.sessionId;
571
+ instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
513
572
  log.info(
514
573
  { sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
515
574
  "Agent fallback spawn complete"
@@ -698,8 +757,8 @@ ${stderr}`
698
757
  writePath = result.path;
699
758
  writeContent = result.content;
700
759
  }
701
- await fs.promises.mkdir(path.dirname(writePath), { recursive: true });
702
- await fs.promises.writeFile(writePath, writeContent, "utf-8");
760
+ await fs2.promises.mkdir(path2.dirname(writePath), { recursive: true });
761
+ await fs2.promises.writeFile(writePath, writeContent, "utf-8");
703
762
  return {};
704
763
  },
705
764
  // ── Terminal operations (delegated to TerminalManager) ─────────────
@@ -783,10 +842,10 @@ ${stderr}`
783
842
  for (const att of attachments ?? []) {
784
843
  const tooLarge = att.size > 10 * 1024 * 1024;
785
844
  if (att.type === "image" && this.promptCapabilities?.image && !tooLarge && SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {
786
- const data = await fs.promises.readFile(att.filePath);
845
+ const data = await fs2.promises.readFile(att.filePath);
787
846
  contentBlocks.push({ type: "image", data: data.toString("base64"), mimeType: att.mimeType });
788
847
  } else if (att.type === "audio" && this.promptCapabilities?.audio && !tooLarge) {
789
- const data = await fs.promises.readFile(att.filePath);
848
+ const data = await fs2.promises.readFile(att.filePath);
790
849
  contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
791
850
  } else {
792
851
  if ((att.type === "image" || att.type === "audio") && !tooLarge) {
@@ -810,11 +869,23 @@ ${stderr}`
810
869
  await this.connection.cancel({ sessionId: this.sessionId });
811
870
  }
812
871
  async destroy() {
872
+ this._destroying = true;
813
873
  this.terminalManager.destroyAll();
814
- this.child.kill("SIGTERM");
815
- setTimeout(() => {
816
- if (!this.child.killed) this.child.kill("SIGKILL");
817
- }, 1e4);
874
+ if (this.child.exitCode !== null) return;
875
+ await new Promise((resolve) => {
876
+ this.child.on("exit", () => {
877
+ clearTimeout(forceKillTimer);
878
+ resolve();
879
+ });
880
+ this.child.kill("SIGTERM");
881
+ const forceKillTimer = setTimeout(() => {
882
+ if (this.child.exitCode === null) this.child.kill("SIGKILL");
883
+ resolve();
884
+ }, 1e4);
885
+ if (typeof forceKillTimer === "object" && forceKillTimer !== null && "unref" in forceKillTimer) {
886
+ forceKillTimer.unref();
887
+ }
888
+ });
818
889
  }
819
890
  };
820
891
 
@@ -921,6 +992,9 @@ var PermissionGate = class {
921
992
  this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
922
993
  }
923
994
  setPending(request) {
995
+ if (!this.settled && this.rejectFn) {
996
+ this.rejectFn(new Error("Superseded by new permission request"));
997
+ }
924
998
  this.request = request;
925
999
  this.settled = false;
926
1000
  this.clearTimeout();
@@ -930,6 +1004,9 @@ var PermissionGate = class {
930
1004
  this.timeoutTimer = setTimeout(() => {
931
1005
  this.reject("Permission request timed out (no response received)");
932
1006
  }, this.timeoutMs);
1007
+ if (typeof this.timeoutTimer === "object" && "unref" in this.timeoutTimer) {
1008
+ this.timeoutTimer.unref();
1009
+ }
933
1010
  });
934
1011
  }
935
1012
  resolve(optionId) {
@@ -971,7 +1048,7 @@ var PermissionGate = class {
971
1048
 
972
1049
  // src/core/sessions/session.ts
973
1050
  import { nanoid } from "nanoid";
974
- import * as fs2 from "fs";
1051
+ import * as fs3 from "fs";
975
1052
  var moduleLog = createChildLogger({ module: "session" });
976
1053
  var TTS_PROMPT_INSTRUCTION = `
977
1054
 
@@ -982,7 +1059,7 @@ var TTS_TIMEOUT_MS = 3e4;
982
1059
  var VALID_TRANSITIONS = {
983
1060
  initializing: /* @__PURE__ */ new Set(["active", "error"]),
984
1061
  active: /* @__PURE__ */ new Set(["error", "finished", "cancelled"]),
985
- error: /* @__PURE__ */ new Set(["active"]),
1062
+ error: /* @__PURE__ */ new Set(["active", "cancelled"]),
986
1063
  cancelled: /* @__PURE__ */ new Set(["active"]),
987
1064
  finished: /* @__PURE__ */ new Set()
988
1065
  };
@@ -1097,30 +1174,28 @@ var Session = class extends TypedEmitter {
1097
1174
  await this.runWarmup();
1098
1175
  return;
1099
1176
  }
1177
+ if (this._status === "finished") return;
1100
1178
  this.promptCount++;
1101
- if (this._status === "initializing") {
1179
+ if (this._status === "initializing" || this._status === "cancelled" || this._status === "error") {
1102
1180
  this.activate();
1103
1181
  }
1104
1182
  const promptStart = Date.now();
1105
1183
  this.log.debug("Prompt execution started");
1106
- if (this.pendingContext) {
1184
+ const contextUsed = this.pendingContext;
1185
+ if (contextUsed) {
1107
1186
  text = `[CONVERSATION HISTORY - This is context from previous sessions, not current conversation]
1108
1187
 
1109
- ${this.pendingContext}
1188
+ ${contextUsed}
1110
1189
 
1111
1190
  [END CONVERSATION HISTORY]
1112
1191
 
1113
1192
  ${text}`;
1114
- this.pendingContext = null;
1115
1193
  this.log.debug("Context injected into prompt");
1116
1194
  }
1117
1195
  const processed = await this.maybeTranscribeAudio(text, attachments);
1118
1196
  const ttsActive = this.voiceMode !== "off" && !!this.speechService?.isTTSAvailable();
1119
1197
  if (ttsActive) {
1120
1198
  processed.text += TTS_PROMPT_INSTRUCTION;
1121
- if (this.voiceMode === "next") {
1122
- this.voiceMode = "off";
1123
- }
1124
1199
  }
1125
1200
  let accumulatedText = "";
1126
1201
  const accumulatorListener = ttsActive ? (event) => {
@@ -1141,6 +1216,12 @@ ${text}`;
1141
1216
  if (response && typeof response === "object" && "stopReason" in response) {
1142
1217
  stopReason = response.stopReason ?? "end_turn";
1143
1218
  }
1219
+ if (contextUsed) {
1220
+ this.pendingContext = null;
1221
+ }
1222
+ if (ttsActive && this.voiceMode === "next") {
1223
+ this.voiceMode = "off";
1224
+ }
1144
1225
  } finally {
1145
1226
  if (accumulatorListener) {
1146
1227
  this.off("agent_event", accumulatorListener);
@@ -1184,7 +1265,7 @@ ${text}`;
1184
1265
  try {
1185
1266
  const audioPath = att.originalFilePath || att.filePath;
1186
1267
  const audioMime = att.originalFilePath ? "audio/ogg" : att.mimeType;
1187
- const audioBuffer = await fs2.promises.readFile(audioPath);
1268
+ const audioBuffer = await fs3.promises.readFile(audioPath);
1188
1269
  const result = await this.speechService.transcribe(audioBuffer, audioMime);
1189
1270
  this.log.info({ provider: "stt", duration: result.duration }, "Voice transcribed");
1190
1271
  this.emit("agent_event", {
@@ -1220,20 +1301,26 @@ ${result.text}` : result.text;
1220
1301
  ttsText = ttsText.slice(0, TTS_MAX_LENGTH);
1221
1302
  }
1222
1303
  try {
1223
- const timeoutPromise = new Promise(
1224
- (_, reject) => setTimeout(() => reject(new Error("TTS synthesis timed out")), TTS_TIMEOUT_MS)
1225
- );
1226
- const result = await Promise.race([
1227
- this.speechService.synthesize(ttsText),
1228
- timeoutPromise
1229
- ]);
1230
- const base64 = result.audioBuffer.toString("base64");
1231
- this.emit("agent_event", {
1232
- type: "audio_content",
1233
- data: base64,
1234
- mimeType: result.mimeType
1304
+ let ttsTimer;
1305
+ const timeoutPromise = new Promise((_, reject) => {
1306
+ ttsTimer = setTimeout(() => reject(new Error("TTS synthesis timed out")), TTS_TIMEOUT_MS);
1235
1307
  });
1236
- this.log.info("TTS synthesis completed");
1308
+ try {
1309
+ const result = await Promise.race([
1310
+ this.speechService.synthesize(ttsText),
1311
+ timeoutPromise
1312
+ ]);
1313
+ const base64 = result.audioBuffer.toString("base64");
1314
+ this.emit("agent_event", {
1315
+ type: "audio_content",
1316
+ data: base64,
1317
+ mimeType: result.mimeType
1318
+ });
1319
+ this.emit("agent_event", { type: "tts_strip" });
1320
+ this.log.info("TTS synthesis completed");
1321
+ } finally {
1322
+ clearTimeout(ttsTimer);
1323
+ }
1237
1324
  } catch (err) {
1238
1325
  this.log.warn({ err }, "TTS synthesis failed, skipping");
1239
1326
  }
@@ -1337,7 +1424,12 @@ ${result.text}` : result.text;
1337
1424
  }
1338
1425
  async destroy() {
1339
1426
  this.log.info("Session destroyed");
1427
+ if (this.permissionGate.isPending) {
1428
+ this.permissionGate.reject("Session destroyed");
1429
+ }
1430
+ this.queue.clear();
1340
1431
  await this.agentInstance.destroy();
1432
+ closeSessionLogger(this.log);
1341
1433
  }
1342
1434
  };
1343
1435
 
@@ -1426,8 +1518,12 @@ var SessionManager = class {
1426
1518
  async cancelSession(sessionId) {
1427
1519
  const session = this.sessions.get(sessionId);
1428
1520
  if (session) {
1429
- await session.abortPrompt();
1521
+ try {
1522
+ await session.abortPrompt();
1523
+ } catch {
1524
+ }
1430
1525
  session.markCancelled();
1526
+ this.sessions.delete(sessionId);
1431
1527
  }
1432
1528
  if (this.store) {
1433
1529
  const record = this.store.get(sessionId);
@@ -1458,6 +1554,25 @@ var SessionManager = class {
1458
1554
  await this.store.remove(sessionId);
1459
1555
  this.eventBus?.emit("session:deleted", { sessionId });
1460
1556
  }
1557
+ /**
1558
+ * Graceful shutdown: persist session state without killing agent subprocesses.
1559
+ * Agent processes will exit naturally when the parent process terminates.
1560
+ */
1561
+ async shutdownAll() {
1562
+ if (this.store) {
1563
+ for (const session of this.sessions.values()) {
1564
+ const record = this.store.get(session.id);
1565
+ if (record) {
1566
+ await this.store.save({ ...record, status: "finished" });
1567
+ }
1568
+ }
1569
+ }
1570
+ this.sessions.clear();
1571
+ }
1572
+ /**
1573
+ * Forcefully destroy all sessions (kill agent subprocesses).
1574
+ * Use only when sessions must be fully torn down (e.g. archive).
1575
+ */
1461
1576
  async destroyAll() {
1462
1577
  if (this.store) {
1463
1578
  for (const session of this.sessions.values()) {
@@ -1494,15 +1609,29 @@ var SessionBridge = class {
1494
1609
  sessionEventHandler;
1495
1610
  statusChangeHandler;
1496
1611
  namedHandler;
1612
+ get tracer() {
1613
+ return this.session.agentInstance.debugTracer ?? null;
1614
+ }
1497
1615
  /** Send message to adapter, optionally running through message:outgoing middleware */
1498
1616
  async sendMessage(sessionId, message) {
1499
- const mw = this.deps.middlewareChain;
1500
- if (mw) {
1501
- const result = await mw.execute("message:outgoing", { sessionId, message }, async (m) => m);
1502
- if (!result) return;
1503
- this.adapter.sendMessage(sessionId, result.message);
1504
- } else {
1505
- this.adapter.sendMessage(sessionId, message);
1617
+ try {
1618
+ const mw = this.deps.middlewareChain;
1619
+ if (mw) {
1620
+ const result = await mw.execute("message:outgoing", { sessionId, message }, async (m) => m);
1621
+ this.tracer?.log("core", { step: "middleware:outgoing", sessionId, hook: "message:outgoing", blocked: !result });
1622
+ if (!result) return;
1623
+ this.tracer?.log("core", { step: "dispatch", sessionId, message: result.message });
1624
+ this.adapter.sendMessage(sessionId, result.message).catch((err) => {
1625
+ log2.error({ err, sessionId }, "Failed to send message to adapter");
1626
+ });
1627
+ } else {
1628
+ this.tracer?.log("core", { step: "dispatch", sessionId, message });
1629
+ this.adapter.sendMessage(sessionId, message).catch((err) => {
1630
+ log2.error({ err, sessionId }, "Failed to send message to adapter");
1631
+ });
1632
+ }
1633
+ } catch (err) {
1634
+ log2.error({ err, sessionId }, "Error in sendMessage middleware");
1506
1635
  }
1507
1636
  }
1508
1637
  connect() {
@@ -1538,23 +1667,37 @@ var SessionBridge = class {
1538
1667
  }
1539
1668
  wireSessionToAdapter() {
1540
1669
  this.sessionEventHandler = (event) => {
1670
+ this.tracer?.log("core", { step: "agent_event", sessionId: this.session.id, event });
1541
1671
  const mw = this.deps.middlewareChain;
1542
1672
  if (mw) {
1543
1673
  mw.execute("agent:beforeEvent", { sessionId: this.session.id, event }, async (e) => e).then((result) => {
1674
+ this.tracer?.log("core", { step: "middleware:before", sessionId: this.session.id, hook: "agent:beforeEvent", blocked: !result });
1544
1675
  if (!result) return;
1545
- const transformedEvent = result.event;
1546
- const outgoing = this.handleAgentEvent(transformedEvent);
1547
- mw.execute("agent:afterEvent", {
1548
- sessionId: this.session.id,
1549
- event: transformedEvent,
1550
- outgoingMessage: outgoing ?? { type: "text", text: "" }
1551
- }, async (e) => e).catch(() => {
1552
- });
1676
+ try {
1677
+ const transformedEvent = result.event;
1678
+ const outgoing = this.handleAgentEvent(transformedEvent);
1679
+ mw.execute("agent:afterEvent", {
1680
+ sessionId: this.session.id,
1681
+ event: transformedEvent,
1682
+ outgoingMessage: outgoing ?? { type: "text", text: "" }
1683
+ }, async (e) => e).catch(() => {
1684
+ });
1685
+ } catch (err) {
1686
+ log2.error({ err, sessionId: this.session.id }, "Error handling agent event after middleware");
1687
+ }
1553
1688
  }).catch(() => {
1554
- this.handleAgentEvent(event);
1689
+ try {
1690
+ this.handleAgentEvent(event);
1691
+ } catch (err) {
1692
+ log2.error({ err, sessionId: this.session.id }, "Error handling agent event (middleware fallback)");
1693
+ }
1555
1694
  });
1556
1695
  } else {
1557
- this.handleAgentEvent(event);
1696
+ try {
1697
+ this.handleAgentEvent(event);
1698
+ } catch (err) {
1699
+ log2.error({ err, sessionId: this.session.id }, "Error handling agent event");
1700
+ }
1558
1701
  }
1559
1702
  };
1560
1703
  this.session.on("agent_event", this.sessionEventHandler);
@@ -1578,6 +1721,7 @@ var SessionBridge = class {
1578
1721
  case "plan":
1579
1722
  case "usage":
1580
1723
  outgoing = this.deps.messageTransformer.transform(event, ctx);
1724
+ this.tracer?.log("core", { step: "transform", sessionId: this.session.id, input: event, output: outgoing });
1581
1725
  this.sendMessage(this.session.id, outgoing);
1582
1726
  break;
1583
1727
  case "session_end":
@@ -1607,12 +1751,12 @@ var SessionBridge = class {
1607
1751
  break;
1608
1752
  case "image_content": {
1609
1753
  if (this.deps.fileService) {
1610
- const fs5 = this.deps.fileService;
1754
+ const fs6 = this.deps.fileService;
1611
1755
  const sid = this.session.id;
1612
1756
  const { data, mimeType } = event;
1613
1757
  const buffer = Buffer.from(data, "base64");
1614
- const ext = fs5.extensionFromMime(mimeType);
1615
- fs5.saveFile(sid, `agent-image${ext}`, buffer, mimeType).then((att) => {
1758
+ const ext = fs6.extensionFromMime(mimeType);
1759
+ fs6.saveFile(sid, `agent-image${ext}`, buffer, mimeType).then((att) => {
1616
1760
  this.sendMessage(sid, {
1617
1761
  type: "attachment",
1618
1762
  text: "",
@@ -1624,12 +1768,12 @@ var SessionBridge = class {
1624
1768
  }
1625
1769
  case "audio_content": {
1626
1770
  if (this.deps.fileService) {
1627
- const fs5 = this.deps.fileService;
1771
+ const fs6 = this.deps.fileService;
1628
1772
  const sid = this.session.id;
1629
1773
  const { data, mimeType } = event;
1630
1774
  const buffer = Buffer.from(data, "base64");
1631
- const ext = fs5.extensionFromMime(mimeType);
1632
- fs5.saveFile(sid, `agent-audio${ext}`, buffer, mimeType).then((att) => {
1775
+ const ext = fs6.extensionFromMime(mimeType);
1776
+ fs6.saveFile(sid, `agent-audio${ext}`, buffer, mimeType).then((att) => {
1633
1777
  this.sendMessage(sid, {
1634
1778
  type: "attachment",
1635
1779
  text: "",
@@ -1678,6 +1822,9 @@ var SessionBridge = class {
1678
1822
  outgoing = this.deps.messageTransformer.transform(event);
1679
1823
  this.sendMessage(this.session.id, outgoing);
1680
1824
  break;
1825
+ case "tts_strip":
1826
+ this.adapter.stripTTSBlock?.(this.session.id);
1827
+ break;
1681
1828
  }
1682
1829
  this.deps.eventBus?.emit("agent:event", {
1683
1830
  sessionId: this.session.id,
@@ -1780,7 +1927,7 @@ var SessionBridge = class {
1780
1927
  sessionId: this.session.id,
1781
1928
  status: to
1782
1929
  });
1783
- if (to === "finished" || to === "cancelled") {
1930
+ if (to === "finished") {
1784
1931
  queueMicrotask(() => this.disconnect());
1785
1932
  }
1786
1933
  };
@@ -1803,14 +1950,22 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
1803
1950
  let info = null;
1804
1951
  if (meta) {
1805
1952
  const m = meta;
1806
- const claudeCode = m?.claudeCode;
1807
- const tr = claudeCode?.toolResponse;
1808
- const file = tr?.file;
1809
- if (typeof file?.filePath === "string" && typeof file?.content === "string") {
1810
- info = { filePath: file.filePath, content: file.content };
1811
- }
1812
- if (!info && typeof tr?.filePath === "string" && typeof tr?.content === "string") {
1813
- info = { filePath: tr.filePath, content: tr.content };
1953
+ const toolResponse = resolveToolResponse(m);
1954
+ if (toolResponse) {
1955
+ const file = toolResponse.file;
1956
+ if (typeof file?.filePath === "string" && typeof file?.content === "string") {
1957
+ info = { filePath: file.filePath, content: file.content };
1958
+ }
1959
+ if (!info && typeof toolResponse.filePath === "string" && typeof toolResponse.originalFile === "string") {
1960
+ const originalFile = toolResponse.originalFile;
1961
+ const oldString = typeof toolResponse.oldString === "string" ? toolResponse.oldString : void 0;
1962
+ const newString = typeof toolResponse.newString === "string" ? toolResponse.newString : void 0;
1963
+ const newContent = oldString && newString ? originalFile.replace(oldString, newString) : originalFile;
1964
+ info = { filePath: toolResponse.filePath, content: newContent, oldContent: originalFile };
1965
+ }
1966
+ if (!info && typeof toolResponse.filePath === "string" && typeof toolResponse.content === "string") {
1967
+ info = { filePath: toolResponse.filePath, content: toolResponse.content };
1968
+ }
1814
1969
  }
1815
1970
  }
1816
1971
  if (!info && rawInput && typeof rawInput === "object") {
@@ -1819,7 +1974,19 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
1819
1974
  if (typeof filePath === "string") {
1820
1975
  const parsed = content ? parseContent(content) : null;
1821
1976
  const riContent = typeof ri?.content === "string" ? ri.content : void 0;
1822
- info = { filePath, content: parsed?.content || riContent, oldContent: parsed?.oldContent };
1977
+ if (kind === "edit") {
1978
+ const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : void 0;
1979
+ const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : void 0;
1980
+ if (newStr) {
1981
+ info = { filePath, content: newStr, oldContent: oldStr };
1982
+ } else {
1983
+ info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent };
1984
+ }
1985
+ } else if (kind === "write") {
1986
+ info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent };
1987
+ } else {
1988
+ info = { filePath, content: parsed?.content || riContent, oldContent: parsed?.oldContent };
1989
+ }
1823
1990
  }
1824
1991
  }
1825
1992
  if (!info && content) {
@@ -1833,6 +2000,16 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
1833
2000
  if (!info.filePath || !info.content) return null;
1834
2001
  return info;
1835
2002
  }
2003
+ function resolveToolResponse(meta) {
2004
+ const claudeCode = meta.claudeCode;
2005
+ if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === "object") {
2006
+ return claudeCode.toolResponse;
2007
+ }
2008
+ if (meta.toolResponse && typeof meta.toolResponse === "object") {
2009
+ return meta.toolResponse;
2010
+ }
2011
+ return void 0;
2012
+ }
1836
2013
  function parseContent(content) {
1837
2014
  if (typeof content === "string") {
1838
2015
  return { content };
@@ -1891,7 +2068,30 @@ function parseContent(content) {
1891
2068
 
1892
2069
  // src/core/message-transformer.ts
1893
2070
  var log3 = createChildLogger({ module: "message-transformer" });
2071
+ function computeLineDiff(oldStr, newStr) {
2072
+ const oldLines = oldStr ? oldStr.split("\n") : [];
2073
+ const newLines = newStr ? newStr.split("\n") : [];
2074
+ let prefixLen = 0;
2075
+ const minLen = Math.min(oldLines.length, newLines.length);
2076
+ while (prefixLen < minLen && oldLines[prefixLen] === newLines[prefixLen]) {
2077
+ prefixLen++;
2078
+ }
2079
+ let suffixLen = 0;
2080
+ const maxSuffix = minLen - prefixLen;
2081
+ while (suffixLen < maxSuffix && oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]) {
2082
+ suffixLen++;
2083
+ }
2084
+ return {
2085
+ added: Math.max(0, newLines.length - prefixLen - suffixLen),
2086
+ removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
2087
+ };
2088
+ }
1894
2089
  var MessageTransformer = class {
2090
+ tunnelService;
2091
+ /** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
2092
+ toolRawInputCache = /* @__PURE__ */ new Map();
2093
+ /** Cache viewer links generated from intermediate updates so completion events carry them */
2094
+ toolViewerCache = /* @__PURE__ */ new Map();
1895
2095
  constructor(tunnelService) {
1896
2096
  this.tunnelService = tunnelService;
1897
2097
  }
@@ -1902,6 +2102,9 @@ var MessageTransformer = class {
1902
2102
  case "thought":
1903
2103
  return { type: "thought", text: event.content };
1904
2104
  case "tool_call": {
2105
+ if (event.id && this.isNonEmptyInput(event.rawInput)) {
2106
+ this.toolRawInputCache.set(event.id, event.rawInput);
2107
+ }
1905
2108
  const meta = event.meta;
1906
2109
  const metadata = {
1907
2110
  id: event.id,
@@ -1919,6 +2122,14 @@ var MessageTransformer = class {
1919
2122
  return { type: "tool_call", text: event.name, metadata };
1920
2123
  }
1921
2124
  case "tool_update": {
2125
+ if (event.id && this.isNonEmptyInput(event.rawInput)) {
2126
+ this.toolRawInputCache.set(event.id, event.rawInput);
2127
+ }
2128
+ const cachedRawInput = event.id ? this.toolRawInputCache.get(event.id) : void 0;
2129
+ const effectiveRawInput = this.isNonEmptyInput(event.rawInput) ? event.rawInput : cachedRawInput;
2130
+ if (event.id && (event.status === "completed" || event.status === "done" || event.status === "failed" || event.status === "error")) {
2131
+ this.toolRawInputCache.delete(event.id);
2132
+ }
1922
2133
  const meta = event.meta;
1923
2134
  const metadata = {
1924
2135
  id: event.id,
@@ -1926,12 +2137,28 @@ var MessageTransformer = class {
1926
2137
  kind: event.kind,
1927
2138
  status: event.status,
1928
2139
  content: event.content,
1929
- rawInput: event.rawInput,
2140
+ rawInput: effectiveRawInput,
1930
2141
  displaySummary: meta?.displaySummary,
1931
2142
  displayTitle: meta?.displayTitle,
1932
2143
  displayKind: meta?.displayKind
1933
2144
  };
1934
- this.enrichWithViewerLinks(event, metadata, sessionContext);
2145
+ const enrichEvent = { ...event, rawInput: effectiveRawInput };
2146
+ this.enrichWithViewerLinks(enrichEvent, metadata, sessionContext);
2147
+ if (event.id) {
2148
+ const cached = this.toolViewerCache.get(event.id);
2149
+ if (cached) {
2150
+ metadata.viewerLinks = cached.viewerLinks;
2151
+ metadata.viewerFilePath = cached.viewerFilePath;
2152
+ } else if (metadata.viewerLinks) {
2153
+ this.toolViewerCache.set(event.id, {
2154
+ viewerLinks: metadata.viewerLinks,
2155
+ viewerFilePath: metadata.viewerFilePath
2156
+ });
2157
+ }
2158
+ if (event.status === "completed" || event.status === "done" || event.status === "failed" || event.status === "error") {
2159
+ this.toolViewerCache.delete(event.id);
2160
+ }
2161
+ }
1935
2162
  return { type: "tool_update", text: "", metadata };
1936
2163
  }
1937
2164
  case "plan":
@@ -1947,7 +2174,7 @@ var MessageTransformer = class {
1947
2174
  metadata: {
1948
2175
  tokensUsed: event.tokensUsed,
1949
2176
  contextSize: event.contextSize,
1950
- cost: event.cost
2177
+ cost: event.cost?.amount
1951
2178
  }
1952
2179
  };
1953
2180
  case "session_end":
@@ -2001,12 +2228,38 @@ var MessageTransformer = class {
2001
2228
  return { type: "text", text: "" };
2002
2229
  }
2003
2230
  }
2231
+ /** Check if rawInput is a non-empty object (not null, not {}) */
2232
+ isNonEmptyInput(input) {
2233
+ return input !== null && input !== void 0 && typeof input === "object" && !Array.isArray(input) && Object.keys(input).length > 0;
2234
+ }
2004
2235
  enrichWithViewerLinks(event, metadata, sessionContext) {
2005
- if (!this.tunnelService || !sessionContext) return;
2006
- const name = "name" in event ? event.name || "" : "";
2007
2236
  const kind = "kind" in event ? event.kind : void 0;
2237
+ if (!metadata.diffStats && (kind === "edit" || kind === "write")) {
2238
+ const ri = event.rawInput;
2239
+ if (ri) {
2240
+ const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
2241
+ const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
2242
+ if (oldStr !== null && newStr !== null) {
2243
+ const stats = computeLineDiff(oldStr, newStr);
2244
+ if (stats.added > 0 || stats.removed > 0) {
2245
+ metadata.diffStats = stats;
2246
+ }
2247
+ } else if (oldStr === null && newStr !== null && kind === "write") {
2248
+ const added = newStr.split("\n").length;
2249
+ if (added > 0) metadata.diffStats = { added, removed: 0 };
2250
+ }
2251
+ }
2252
+ }
2253
+ if (!this.tunnelService || !sessionContext) {
2254
+ log3.debug(
2255
+ { hasTunnel: !!this.tunnelService, hasCtx: !!sessionContext, kind },
2256
+ "enrichWithViewerLinks: skipping (no tunnel or session context)"
2257
+ );
2258
+ return;
2259
+ }
2260
+ const name = "name" in event ? event.name || "" : "";
2008
2261
  log3.debug(
2009
- { name, kind, status: event.status, hasContent: !!event.content },
2262
+ { name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
2010
2263
  "enrichWithViewerLinks: inspecting event"
2011
2264
  );
2012
2265
  const fileInfo = extractFileInfo(
@@ -2016,7 +2269,18 @@ var MessageTransformer = class {
2016
2269
  event.rawInput,
2017
2270
  event.meta
2018
2271
  );
2019
- if (!fileInfo) return;
2272
+ if (!fileInfo) {
2273
+ log3.debug(
2274
+ { name, kind, hasContent: !!event.content, hasRawInput: !!event.rawInput, hasMeta: !!event.meta },
2275
+ "enrichWithViewerLinks: extractFileInfo returned null"
2276
+ );
2277
+ return;
2278
+ }
2279
+ const publicUrl = this.tunnelService.getPublicUrl();
2280
+ if (publicUrl.startsWith("http://localhost") || publicUrl.startsWith("http://127.0.0.1")) {
2281
+ log3.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping (no public tunnel URL)");
2282
+ return;
2283
+ }
2020
2284
  log3.info(
2021
2285
  {
2022
2286
  name,
@@ -2037,6 +2301,12 @@ var MessageTransformer = class {
2037
2301
  sessionContext.workingDirectory
2038
2302
  );
2039
2303
  if (id2) viewerLinks.diff = this.tunnelService.diffUrl(id2);
2304
+ if (!metadata.diffStats) {
2305
+ const stats = computeLineDiff(fileInfo.oldContent, fileInfo.content);
2306
+ if (stats.added > 0 || stats.removed > 0) {
2307
+ metadata.diffStats = stats;
2308
+ }
2309
+ }
2040
2310
  }
2041
2311
  const id = store.storeFile(
2042
2312
  sessionContext.id,
@@ -2152,12 +2422,12 @@ var EventBus = class extends TypedEmitter {
2152
2422
  };
2153
2423
 
2154
2424
  // src/core/core.ts
2155
- import path4 from "path";
2425
+ import path5 from "path";
2156
2426
  import os from "os";
2157
2427
 
2158
2428
  // src/core/sessions/session-store.ts
2159
- import fs3 from "fs";
2160
- import path2 from "path";
2429
+ import fs4 from "fs";
2430
+ import path3 from "path";
2161
2431
  var log5 = createChildLogger({ module: "session-store" });
2162
2432
  var DEBOUNCE_MS = 2e3;
2163
2433
  var JsonFileSessionStore = class {
@@ -2219,9 +2489,9 @@ var JsonFileSessionStore = class {
2219
2489
  version: 1,
2220
2490
  sessions: Object.fromEntries(this.records)
2221
2491
  };
2222
- const dir = path2.dirname(this.filePath);
2223
- if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
2224
- fs3.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
2492
+ const dir = path3.dirname(this.filePath);
2493
+ if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
2494
+ fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
2225
2495
  }
2226
2496
  destroy() {
2227
2497
  if (this.debounceTimer) clearTimeout(this.debounceTimer);
@@ -2234,10 +2504,10 @@ var JsonFileSessionStore = class {
2234
2504
  }
2235
2505
  }
2236
2506
  load() {
2237
- if (!fs3.existsSync(this.filePath)) return;
2507
+ if (!fs4.existsSync(this.filePath)) return;
2238
2508
  try {
2239
2509
  const raw = JSON.parse(
2240
- fs3.readFileSync(this.filePath, "utf-8")
2510
+ fs4.readFileSync(this.filePath, "utf-8")
2241
2511
  );
2242
2512
  if (raw.version !== 1) {
2243
2513
  log5.warn(
@@ -2251,7 +2521,11 @@ var JsonFileSessionStore = class {
2251
2521
  }
2252
2522
  log5.debug({ count: this.records.size }, "Loaded session records");
2253
2523
  } catch (err) {
2254
- log5.error({ err }, "Failed to load session store");
2524
+ log5.error({ err }, "Failed to load session store, backing up corrupt file");
2525
+ try {
2526
+ fs4.renameSync(this.filePath, `${this.filePath}.bak`);
2527
+ } catch {
2528
+ }
2255
2529
  }
2256
2530
  }
2257
2531
  cleanup() {
@@ -2392,6 +2666,7 @@ var MiddlewareChain = class {
2392
2666
  const existing = this.chains.get(hook);
2393
2667
  if (existing) {
2394
2668
  existing.push(entry);
2669
+ existing.sort((a, b) => a.priority - b.priority);
2395
2670
  } else {
2396
2671
  this.chains.set(hook, [entry]);
2397
2672
  }
@@ -2401,7 +2676,7 @@ var MiddlewareChain = class {
2401
2676
  if (!handlers || handlers.length === 0) {
2402
2677
  return coreHandler(payload);
2403
2678
  }
2404
- const sorted = [...handlers].sort((a, b) => a.priority - b.priority);
2679
+ const sorted = handlers;
2405
2680
  let cachedResult = void 0;
2406
2681
  const buildNext = (index, currentPayload) => {
2407
2682
  return async () => {
@@ -2431,15 +2706,16 @@ var MiddlewareChain = class {
2431
2706
  return nextResult;
2432
2707
  };
2433
2708
  let handlerResult;
2709
+ let timeoutTimer;
2434
2710
  try {
2435
2711
  const timeoutPromise = new Promise((_, reject) => {
2436
- const timer = setTimeout(
2712
+ timeoutTimer = setTimeout(
2437
2713
  () => reject(new Error(`Middleware timeout: ${entry.pluginName} on hook ${hook}`)),
2438
2714
  MIDDLEWARE_TIMEOUT_MS
2439
2715
  );
2440
- if (typeof timer === "object" && timer !== null && "unref" in timer) {
2716
+ if (typeof timeoutTimer === "object" && timeoutTimer !== null && "unref" in timeoutTimer) {
2441
2717
  ;
2442
- timer.unref();
2718
+ timeoutTimer.unref();
2443
2719
  }
2444
2720
  });
2445
2721
  handlerResult = await Promise.race([
@@ -2452,6 +2728,8 @@ var MiddlewareChain = class {
2452
2728
  }
2453
2729
  this.errorTracker?.increment(entry.pluginName);
2454
2730
  return nextFn();
2731
+ } finally {
2732
+ clearTimeout(timeoutTimer);
2455
2733
  }
2456
2734
  if (handlerResult === null) {
2457
2735
  return null;
@@ -2519,26 +2797,27 @@ var ErrorTracker = class {
2519
2797
  };
2520
2798
 
2521
2799
  // src/core/plugin/plugin-storage.ts
2522
- import fs4 from "fs";
2523
- import path3 from "path";
2800
+ import fs5 from "fs";
2801
+ import path4 from "path";
2524
2802
  var PluginStorageImpl = class {
2525
2803
  kvPath;
2526
2804
  dataDir;
2527
2805
  writeChain = Promise.resolve();
2528
2806
  constructor(baseDir) {
2529
- this.dataDir = path3.join(baseDir, "data");
2530
- this.kvPath = path3.join(baseDir, "kv.json");
2807
+ this.dataDir = path4.join(baseDir, "data");
2808
+ this.kvPath = path4.join(baseDir, "kv.json");
2809
+ fs5.mkdirSync(baseDir, { recursive: true });
2531
2810
  }
2532
2811
  readKv() {
2533
2812
  try {
2534
- const raw = fs4.readFileSync(this.kvPath, "utf-8");
2813
+ const raw = fs5.readFileSync(this.kvPath, "utf-8");
2535
2814
  return JSON.parse(raw);
2536
2815
  } catch {
2537
2816
  return {};
2538
2817
  }
2539
2818
  }
2540
2819
  writeKv(data) {
2541
- fs4.writeFileSync(this.kvPath, JSON.stringify(data), "utf-8");
2820
+ fs5.writeFileSync(this.kvPath, JSON.stringify(data), "utf-8");
2542
2821
  }
2543
2822
  async get(key) {
2544
2823
  const data = this.readKv();
@@ -2564,7 +2843,7 @@ var PluginStorageImpl = class {
2564
2843
  return Object.keys(this.readKv());
2565
2844
  }
2566
2845
  getDataDir() {
2567
- fs4.mkdirSync(this.dataDir, { recursive: true });
2846
+ fs5.mkdirSync(this.dataDir, { recursive: true });
2568
2847
  return this.dataDir;
2569
2848
  }
2570
2849
  };
@@ -2780,6 +3059,9 @@ var LifecycleManager = class {
2780
3059
  get failedPlugins() {
2781
3060
  return [...this._failed];
2782
3061
  }
3062
+ get registry() {
3063
+ return this.pluginRegistry;
3064
+ }
2783
3065
  constructor(opts) {
2784
3066
  this.serviceRegistry = opts?.serviceRegistry ?? new ServiceRegistry();
2785
3067
  this.middlewareChain = opts?.middlewareChain ?? new MiddlewareChain();
@@ -2821,6 +3103,8 @@ var LifecycleManager = class {
2821
3103
  try {
2822
3104
  sorted = resolveLoadOrder(allForResolution);
2823
3105
  } catch (err) {
3106
+ const error = err instanceof Error ? err : new Error(String(err));
3107
+ this.log?.error(`Plugin dependency resolution failed: ${error.message}`);
2824
3108
  for (const p of plugins) {
2825
3109
  this._failed.add(p.name);
2826
3110
  }
@@ -2859,7 +3143,11 @@ var LifecycleManager = class {
2859
3143
  settings: this.settingsManager.createAPI(plugin.name),
2860
3144
  log: pluginLog
2861
3145
  };
2862
- const newSettings = await plugin.migrate(migrateCtx, oldSettings, registryEntry.version);
3146
+ const newSettings = await withTimeout(
3147
+ plugin.migrate(migrateCtx, oldSettings, registryEntry.version),
3148
+ SETUP_TIMEOUT_MS,
3149
+ `${plugin.name}.migrate()`
3150
+ );
2863
3151
  if (newSettings && typeof newSettings === "object") {
2864
3152
  await migrateCtx.settings.setAll(newSettings);
2865
3153
  }
@@ -2872,11 +3160,23 @@ var LifecycleManager = class {
2872
3160
  let pluginConfig;
2873
3161
  if (this.settingsManager) {
2874
3162
  pluginConfig = await this.settingsManager.loadSettings(plugin.name);
3163
+ const settingsPath = this.settingsManager.getSettingsPath(plugin.name);
3164
+ this.getPluginLogger(plugin.name).debug(`Settings loaded from ${settingsPath}: ${Object.keys(pluginConfig).length} keys`);
2875
3165
  if (Object.keys(pluginConfig).length === 0) {
2876
3166
  pluginConfig = resolvePluginConfig(plugin.name, this.config);
2877
3167
  }
2878
3168
  } else {
2879
3169
  pluginConfig = resolvePluginConfig(plugin.name, this.config);
3170
+ this.getPluginLogger(plugin.name).debug("No settingsManager, using legacy config");
3171
+ }
3172
+ if (plugin.settingsSchema && this.settingsManager) {
3173
+ const validation = this.settingsManager.validateSettings(plugin.name, pluginConfig, plugin.settingsSchema);
3174
+ if (!validation.valid) {
3175
+ this._failed.add(plugin.name);
3176
+ this.getPluginLogger(plugin.name).error(`Settings validation failed: ${validation.errors?.join("; ")}`);
3177
+ this.eventBus?.emit("plugin:failed", { name: plugin.name, error: `Settings validation failed: ${validation.errors?.join("; ")}` });
3178
+ continue;
3179
+ }
2880
3180
  }
2881
3181
  const ctx = createPluginContext({
2882
3182
  pluginName: plugin.name,
@@ -2964,20 +3264,25 @@ var OpenACPCore = class {
2964
3264
  sessionFactory;
2965
3265
  lifecycleManager;
2966
3266
  // --- Lazy getters: resolve from ServiceRegistry (populated by plugins during boot) ---
3267
+ getService(name) {
3268
+ const svc = this.lifecycleManager.serviceRegistry.get(name);
3269
+ if (!svc) throw new Error(`Service '${name}' not registered \u2014 is the ${name} plugin loaded?`);
3270
+ return svc;
3271
+ }
2967
3272
  get securityGuard() {
2968
- return this.lifecycleManager.serviceRegistry.get("security");
3273
+ return this.getService("security");
2969
3274
  }
2970
3275
  get notificationManager() {
2971
- return this.lifecycleManager.serviceRegistry.get("notifications");
3276
+ return this.getService("notifications");
2972
3277
  }
2973
3278
  get fileService() {
2974
- return this.lifecycleManager.serviceRegistry.get("file-service");
3279
+ return this.getService("file-service");
2975
3280
  }
2976
3281
  get speechService() {
2977
- return this.lifecycleManager.serviceRegistry.get("speech");
3282
+ return this.getService("speech");
2978
3283
  }
2979
3284
  get contextManager() {
2980
- return this.lifecycleManager.serviceRegistry.get("context");
3285
+ return this.getService("context");
2981
3286
  }
2982
3287
  constructor(configManager) {
2983
3288
  this.configManager = configManager;
@@ -2985,7 +3290,7 @@ var OpenACPCore = class {
2985
3290
  this.agentCatalog = new AgentCatalog();
2986
3291
  this.agentCatalog.load();
2987
3292
  this.agentManager = new AgentManager(this.agentCatalog);
2988
- const storePath = path4.join(os.homedir(), ".openacp", "sessions.json");
3293
+ const storePath = path5.join(os.homedir(), ".openacp", "sessions.json");
2989
3294
  this.sessionStore = new JsonFileSessionStore(
2990
3295
  storePath,
2991
3296
  config.sessionStore.ttlDays
@@ -3008,7 +3313,7 @@ var OpenACPCore = class {
3008
3313
  sessions: this.sessionManager,
3009
3314
  config: this.configManager,
3010
3315
  core: this,
3011
- storagePath: path4.join(os.homedir(), ".openacp", "plugins", "data"),
3316
+ storagePath: path5.join(os.homedir(), ".openacp", "plugins", "data"),
3012
3317
  log: createChildLogger({ module: "plugin" })
3013
3318
  });
3014
3319
  this.sessionFactory.middlewareChain = this.lifecycleManager.middlewareChain;
@@ -3017,7 +3322,7 @@ var OpenACPCore = class {
3017
3322
  "config:changed",
3018
3323
  async ({ path: configPath, value }) => {
3019
3324
  if (configPath === "logging.level" && typeof value === "string") {
3020
- const { setLogLevel } = await import("./log-LZ7FTRKG.js");
3325
+ const { setLogLevel } = await import("./log-YZ243M5G.js");
3021
3326
  setLogLevel(value);
3022
3327
  log6.info({ level: value }, "Log level changed at runtime");
3023
3328
  }
@@ -3041,7 +3346,7 @@ var OpenACPCore = class {
3041
3346
  }
3042
3347
  set tunnelService(service) {
3043
3348
  this._tunnelService = service;
3044
- this.messageTransformer = new MessageTransformer(service);
3349
+ this.messageTransformer.tunnelService = service;
3045
3350
  }
3046
3351
  registerAdapter(name, adapter) {
3047
3352
  this.adapters.set(name, adapter);
@@ -3056,14 +3361,17 @@ var OpenACPCore = class {
3056
3361
  }
3057
3362
  async stop() {
3058
3363
  try {
3059
- await this.notificationManager.notifyAll({
3060
- sessionId: "system",
3061
- type: "error",
3062
- summary: "OpenACP is shutting down"
3063
- });
3364
+ const nm = this.lifecycleManager.serviceRegistry.get("notifications");
3365
+ if (nm) {
3366
+ await nm.notifyAll({
3367
+ sessionId: "system",
3368
+ type: "error",
3369
+ summary: "OpenACP is shutting down"
3370
+ });
3371
+ }
3064
3372
  } catch {
3065
3373
  }
3066
- await this.sessionManager.destroyAll();
3374
+ await this.sessionManager.shutdownAll();
3067
3375
  for (const adapter of this.adapters.values()) {
3068
3376
  await adapter.stop();
3069
3377
  }
@@ -3071,30 +3379,30 @@ var OpenACPCore = class {
3071
3379
  // --- Archive ---
3072
3380
  async archiveSession(sessionId) {
3073
3381
  const session = this.sessionManager.getSession(sessionId);
3074
- const record = this.sessionManager.getSessionRecord(sessionId);
3075
- if (!session && !record) return { ok: false, error: "Session not found" };
3076
- const channelId = session?.channelId ?? record?.channelId;
3077
- if (!channelId) return { ok: false, error: "No channel for session" };
3078
- const adapter = this.adapters.get(channelId);
3382
+ if (!session) return { ok: false, error: "Session not found (must be in memory)" };
3383
+ if (session.status !== "active" && session.status !== "cancelled" && session.status !== "error") {
3384
+ return { ok: false, error: `Cannot archive session in '${session.status}' state` };
3385
+ }
3386
+ const adapter = this.adapters.get(session.channelId);
3079
3387
  if (!adapter) return { ok: false, error: "Adapter not found for session" };
3388
+ if (!adapter.archiveSessionTopic) return { ok: false, error: "Adapter does not support topic archiving" };
3080
3389
  try {
3081
- if (session) {
3082
- await adapter.archiveSessionTopic?.(session.id);
3083
- } else {
3084
- await adapter.deleteSessionThread?.(sessionId);
3085
- }
3086
- if (session) {
3087
- try {
3088
- await this.sessionManager.cancelSession(sessionId);
3089
- } catch {
3090
- } finally {
3091
- session.archiving = false;
3390
+ const newThreadId = await adapter.archiveSessionTopic(session.id);
3391
+ session.threadId = newThreadId;
3392
+ try {
3393
+ const platform = {};
3394
+ if (session.channelId === "telegram") {
3395
+ platform.topicId = Number(newThreadId);
3396
+ } else {
3397
+ platform.threadId = newThreadId;
3092
3398
  }
3399
+ await this.sessionManager.patchRecord(sessionId, { platform });
3400
+ } catch (patchErr) {
3401
+ log6.warn({ err: patchErr, sessionId }, "Failed to update session record after archive \u2014 session will work but may not survive restart");
3093
3402
  }
3094
- await this.sessionManager.removeRecord(sessionId);
3095
- return { ok: true };
3403
+ return { ok: true, newThreadId };
3096
3404
  } catch (err) {
3097
- if (session) session.archiving = false;
3405
+ session.archiving = false;
3098
3406
  return { ok: false, error: err.message };
3099
3407
  }
3100
3408
  }
@@ -3153,6 +3461,9 @@ var OpenACPCore = class {
3153
3461
  // --- Unified Session Creation Pipeline ---
3154
3462
  async createSession(params) {
3155
3463
  const session = await this.sessionFactory.create(params);
3464
+ if (params.threadId) {
3465
+ session.threadId = params.threadId;
3466
+ }
3156
3467
  const adapter = this.adapters.get(params.channelId);
3157
3468
  if (params.createThread && adapter) {
3158
3469
  const threadId = await adapter.createSessionThread(
@@ -3391,7 +3702,7 @@ var OpenACPCore = class {
3391
3702
  );
3392
3703
  return null;
3393
3704
  }
3394
- if (record.status === "error") {
3705
+ if (record.status === "error" || record.status === "cancelled") {
3395
3706
  log6.debug(
3396
3707
  {
3397
3708
  threadId: message.threadId,
@@ -3418,9 +3729,9 @@ var OpenACPCore = class {
3418
3729
  workingDirectory: record.workingDir,
3419
3730
  resumeAgentSessionId: record.agentSessionId,
3420
3731
  existingSessionId: record.sessionId,
3421
- initialName: record.name
3732
+ initialName: record.name,
3733
+ threadId: message.threadId
3422
3734
  });
3423
- session.threadId = message.threadId;
3424
3735
  session.activate();
3425
3736
  session.dangerousMode = record.dangerousMode ?? false;
3426
3737
  log6.info(
@@ -3468,7 +3779,7 @@ var CommandRegistry = class _CommandRegistry {
3468
3779
  commands = /* @__PURE__ */ new Map();
3469
3780
  /** Adapter-specific overrides: `channelId:commandName` → RegisteredCommand */
3470
3781
  overrides = /* @__PURE__ */ new Map();
3471
- static ADAPTER_SCOPES = /* @__PURE__ */ new Set(["telegram", "discord", "slack"]);
3782
+ static ADAPTER_SCOPES = /* @__PURE__ */ new Set(["telegram", "discord"]);
3472
3783
  /**
3473
3784
  * Register a command definition.
3474
3785
  * @param def - Command definition
@@ -3503,22 +3814,18 @@ var CommandRegistry = class _CommandRegistry {
3503
3814
  get(name) {
3504
3815
  return this.commands.get(name);
3505
3816
  }
3506
- /** Remove a command by short name. Also removes its qualified name entry. */
3817
+ /** Remove a command by name (short or qualified). Also removes its qualified name entry. */
3507
3818
  unregister(name) {
3508
3819
  const cmd = this.commands.get(name);
3509
- if (cmd) {
3510
- this.commands.delete(name);
3511
- if (cmd.scope) {
3512
- this.commands.delete(`${cmd.scope}:${name}`);
3513
- }
3514
- }
3515
- if (!cmd) {
3516
- this.commands.delete(name);
3820
+ if (!cmd) return;
3821
+ this.commands.delete(name);
3822
+ if (cmd.scope) {
3823
+ this.commands.delete(`${cmd.scope}:${cmd.name}`);
3824
+ this.commands.delete(cmd.name);
3517
3825
  }
3518
3826
  }
3519
3827
  /** Remove all commands registered by a given plugin. */
3520
3828
  unregisterByPlugin(pluginName) {
3521
- const scope = _CommandRegistry.extractScope(pluginName);
3522
3829
  const toDelete = [];
3523
3830
  for (const [key, cmd] of this.commands) {
3524
3831
  if (cmd.pluginName === pluginName) {
@@ -3607,4 +3914,4 @@ export {
3607
3914
  OpenACPCore,
3608
3915
  CommandRegistry
3609
3916
  };
3610
- //# sourceMappingURL=chunk-XIBG7LSL.js.map
3917
+ //# sourceMappingURL=chunk-7RKPIM3E.js.map