@openacp/cli 2026.327.3 → 2026.328.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 (179) hide show
  1. package/README.md +13 -13
  2. package/dist/adapter-HGJENQCN.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-DSUW637I.js +7 -0
  9. package/dist/api-server-WFB5K6FP.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-QAQDGPB4.js → chunk-43JVXFYP.js} +3 -3
  18. package/dist/{chunk-KMMEFXIE.js → chunk-4B6PCWQP.js} +37 -9
  19. package/dist/chunk-4B6PCWQP.js.map +1 -0
  20. package/dist/{chunk-A6Y4GZM3.js → chunk-566W6INH.js} +2 -2
  21. package/dist/{chunk-ODUM3D6X.js → chunk-5HKQCYOI.js} +1 -39
  22. package/dist/chunk-5HKQCYOI.js.map +1 -0
  23. package/dist/{chunk-36YQ44D7.js → chunk-5TCXYDLR.js} +3 -3
  24. package/dist/{chunk-XIBG7LSL.js → chunk-6VR4GWOO.js} +238 -108
  25. package/dist/chunk-6VR4GWOO.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-HUWOFP2H.js → chunk-E2SLHZAC.js} +8 -12
  29. package/dist/{chunk-HUWOFP2H.js.map → chunk-E2SLHZAC.js.map} +1 -1
  30. package/dist/{chunk-RBYBSSGO.js → chunk-FCTC7KDT.js} +2 -2
  31. package/dist/chunk-I53NEV3S.js +45 -0
  32. package/dist/chunk-I53NEV3S.js.map +1 -0
  33. package/dist/{chunk-2YCW3QDV.js → chunk-IXMIC4GQ.js} +8 -7
  34. package/dist/chunk-IXMIC4GQ.js.map +1 -0
  35. package/dist/{chunk-BLQUXO7S.js → chunk-IZ5UEZF7.js} +27 -2
  36. package/dist/chunk-IZ5UEZF7.js.map +1 -0
  37. package/dist/{chunk-QVMEF6FB.js → chunk-JOMDPFQ2.js} +10 -24
  38. package/dist/chunk-JOMDPFQ2.js.map +1 -0
  39. package/dist/{chunk-4GMLGCF2.js → chunk-JUFN4XMB.js} +2 -2
  40. package/dist/{chunk-AD3X6DGK.js → chunk-NT6FYV27.js} +75 -13
  41. package/dist/chunk-NT6FYV27.js.map +1 -0
  42. package/dist/{chunk-UMT7RU77.js → chunk-QBEQJFGL.js} +9 -9
  43. package/dist/{chunk-XMMAGAT4.js → chunk-R6KZYF7D.js} +8 -1
  44. package/dist/{chunk-XMMAGAT4.js.map → chunk-R6KZYF7D.js.map} +1 -1
  45. package/dist/{chunk-LP45RCA4.js → chunk-RXMWJHWH.js} +1007 -495
  46. package/dist/chunk-RXMWJHWH.js.map +1 -0
  47. package/dist/{chunk-SHTGQGAU.js → chunk-V2YZWYXT.js} +3 -3
  48. package/dist/{chunk-BQ6FR32N.js → chunk-VD3QSMVY.js} +2 -2
  49. package/dist/cli.js +141 -115
  50. package/dist/cli.js.map +1 -1
  51. package/dist/{config-I4FMCJGZ.js → config-UCAFCS5W.js} +3 -4
  52. package/dist/config-editor-OU6PUY66.js +10 -0
  53. package/dist/{config-registry-CUMNXFGK.js → config-registry-ZXAIJNYB.js} +2 -3
  54. package/dist/{context-XM6E22LM.js → context-7MPU7RL5.js} +1 -2
  55. package/dist/core-plugins-R2EVZAJV.js +22 -0
  56. package/dist/{daemon-PXO5QPCR.js → daemon-DTA6KYYY.js} +4 -5
  57. package/dist/{dev-loader-DRU3R7ZM.js → dev-loader-7P3HZCIA.js} +1 -3
  58. package/dist/{dev-loader-DRU3R7ZM.js.map → dev-loader-7P3HZCIA.js.map} +1 -1
  59. package/dist/doctor-D723IB2I.js +9 -0
  60. package/dist/file-service-HHB3JQIO.js +8 -0
  61. package/dist/index.d.ts +41 -150
  62. package/dist/index.js +63 -29
  63. package/dist/index.js.map +1 -1
  64. package/dist/{install-cloudflared-AN24L4DP.js → install-cloudflared-JRJ4BSOM.js} +3 -4
  65. package/dist/{install-cloudflared-AN24L4DP.js.map → install-cloudflared-JRJ4BSOM.js.map} +1 -1
  66. package/dist/{install-context-XPWTFT3J.js → install-context-EHYV5WRY.js} +2 -3
  67. package/dist/{install-context-XPWTFT3J.js.map → install-context-EHYV5WRY.js.map} +1 -1
  68. package/dist/{install-jq-CRVDJGF3.js → install-jq-ISTGT263.js} +3 -4
  69. package/dist/{install-jq-CRVDJGF3.js.map → install-jq-ISTGT263.js.map} +1 -1
  70. package/dist/{integrate-G6CVXTGT.js → integrate-JIEZYDOR.js} +1 -2
  71. package/dist/{integrate-G6CVXTGT.js.map → integrate-JIEZYDOR.js.map} +1 -1
  72. package/dist/{log-LZ7FTRKG.js → log-YZ243M5G.js} +4 -3
  73. package/dist/{main-UVTZ46WP.js → main-RRSX5SRL.js} +117 -40
  74. package/dist/main-RRSX5SRL.js.map +1 -0
  75. package/dist/{menu-YDQ2LWAR.js → menu-ALFN37IR.js} +1 -2
  76. package/dist/notifications-MO23S7S3.js +8 -0
  77. package/dist/{plugin-create-5HQRF2ID.js → plugin-create-EHL76ZZG.js} +1 -2
  78. package/dist/{plugin-create-5HQRF2ID.js.map → plugin-create-EHL76ZZG.js.map} +1 -1
  79. package/dist/plugin-installer-5XHORMLS.js +9 -0
  80. package/dist/{plugin-registry-WB3DR67H.js → plugin-registry-6J3YSFHF.js} +1 -2
  81. package/dist/{plugin-search-HQ4WQKOF.js → plugin-search-MGKAL5JM.js} +1 -2
  82. package/dist/{plugin-search-HQ4WQKOF.js.map → plugin-search-MGKAL5JM.js.map} +1 -1
  83. package/dist/{post-upgrade-3ADZRMYJ.js → post-upgrade-Y26S2ZQ7.js} +6 -7
  84. package/dist/{post-upgrade-3ADZRMYJ.js.map → post-upgrade-Y26S2ZQ7.js.map} +1 -1
  85. package/dist/{read-text-file-IRZM3QLM.js → read-text-file-DJBTITIB.js} +1 -2
  86. package/dist/{registry-client-AVGRE4CF.js → registry-client-GTBWLXYU.js} +1 -2
  87. package/dist/{security-YNRBW6S7.js → security-2BA265LN.js} +1 -2
  88. package/dist/{settings-manager-MD2U4ZV2.js → settings-manager-B4UN2LAC.js} +1 -2
  89. package/dist/{setup-EYAFK2WI.js → setup-OI6A3OXW.js} +109 -80
  90. package/dist/setup-OI6A3OXW.js.map +1 -0
  91. package/dist/speech-GB7PHVQZ.js +9 -0
  92. package/dist/{suggest-7D6B542M.js → suggest-RST5VOHB.js} +1 -3
  93. package/dist/{suggest-7D6B542M.js.map → suggest-RST5VOHB.js.map} +1 -1
  94. package/dist/telegram-UVIAXADE.js +7 -0
  95. package/dist/tunnel-4WNFC7GO.js +7 -0
  96. package/dist/{tunnel-service-QJPUYEKU.js → tunnel-service-I2NFUX3V.js} +3 -4
  97. package/dist/{tunnel-service-QJPUYEKU.js.map → tunnel-service-I2NFUX3V.js.map} +1 -1
  98. package/dist/{validators-WSTBNKRW.js → validators-GITLOFXC.js} +1 -2
  99. package/dist/{version-NQZBM5M7.js → version-AXXV6IV2.js} +1 -2
  100. package/package.json +1 -3
  101. package/dist/adapter-LC2QSDAS.js +0 -15
  102. package/dist/adapter-Y55NXX6I.js +0 -1006
  103. package/dist/adapter-Y55NXX6I.js.map +0 -1
  104. package/dist/agent-catalog-YHBFERYO.js +0 -11
  105. package/dist/agent-store-VSHNY5GT.js +0 -9
  106. package/dist/api-server-7G3ZUZRM.js +0 -8
  107. package/dist/api-server-CAYNPUF2.js +0 -11
  108. package/dist/chunk-2YCW3QDV.js.map +0 -1
  109. package/dist/chunk-3ASUU6WW.js +0 -124
  110. package/dist/chunk-3ASUU6WW.js.map +0 -1
  111. package/dist/chunk-AD3X6DGK.js.map +0 -1
  112. package/dist/chunk-BLQUXO7S.js.map +0 -1
  113. package/dist/chunk-KMMEFXIE.js.map +0 -1
  114. package/dist/chunk-LP45RCA4.js.map +0 -1
  115. package/dist/chunk-ODUM3D6X.js.map +0 -1
  116. package/dist/chunk-QVMEF6FB.js.map +0 -1
  117. package/dist/chunk-TRXBJEZ5.js +0 -447
  118. package/dist/chunk-TRXBJEZ5.js.map +0 -1
  119. package/dist/chunk-VUNV25KB.js +0 -16
  120. package/dist/chunk-WIIZNPCR.js.map +0 -1
  121. package/dist/chunk-WXVT3AOY.js.map +0 -1
  122. package/dist/chunk-XIBG7LSL.js.map +0 -1
  123. package/dist/config-editor-3IKBPZA7.js +0 -11
  124. package/dist/core-plugins-ROU4GPLT.js +0 -23
  125. package/dist/dist-UHQK5CXN.js +0 -21151
  126. package/dist/dist-UHQK5CXN.js.map +0 -1
  127. package/dist/doctor-QZQAP46W.js +0 -10
  128. package/dist/file-service-EUODJAIT.js +0 -9
  129. package/dist/main-UVTZ46WP.js.map +0 -1
  130. package/dist/notifications-D5BRDNSU.js +0 -9
  131. package/dist/plugin-installer-GQ2P3Q3E.js +0 -23
  132. package/dist/plugin-installer-GQ2P3Q3E.js.map +0 -1
  133. package/dist/setup-EYAFK2WI.js.map +0 -1
  134. package/dist/slack-37ZWBDUI.js +0 -8
  135. package/dist/speech-2GHQNRIO.js +0 -9
  136. package/dist/telegram-2ZCCCZIY.js +0 -8
  137. package/dist/tunnel-45HA72MB.js +0 -8
  138. package/dist/version-NQZBM5M7.js.map +0 -1
  139. /package/dist/{adapter-LC2QSDAS.js.map → adapter-HGJENQCN.js.map} +0 -0
  140. /package/dist/{agent-catalog-YHBFERYO.js.map → agent-catalog-SZQQERV7.js.map} +0 -0
  141. /package/dist/{agent-dependencies-WS7Z2DFW.js.map → agent-dependencies-ED2ZTUHG.js.map} +0 -0
  142. /package/dist/{agent-registry-5LZT7CUB.js.map → agent-registry-YOGP656W.js.map} +0 -0
  143. /package/dist/{agent-store-VSHNY5GT.js.map → agent-store-5UHZH2XI.js.map} +0 -0
  144. /package/dist/{api-client-AQPNKXI2.js.map → api-client-XTLRRFPX.js.map} +0 -0
  145. /package/dist/{api-server-7G3ZUZRM.js.map → api-server-DSUW637I.js.map} +0 -0
  146. /package/dist/{api-server-CAYNPUF2.js.map → api-server-WFB5K6FP.js.map} +0 -0
  147. /package/dist/{autostart-6JS565RY.js.map → autostart-CUPZMKKC.js.map} +0 -0
  148. /package/dist/{chunk-PPSMUECX.js.map → chunk-2R5XM3ES.js.map} +0 -0
  149. /package/dist/{chunk-SNPYTMPR.js.map → chunk-3EWTPOF7.js.map} +0 -0
  150. /package/dist/{chunk-QAQDGPB4.js.map → chunk-43JVXFYP.js.map} +0 -0
  151. /package/dist/{chunk-A6Y4GZM3.js.map → chunk-566W6INH.js.map} +0 -0
  152. /package/dist/{chunk-36YQ44D7.js.map → chunk-5TCXYDLR.js.map} +0 -0
  153. /package/dist/{chunk-RBYBSSGO.js.map → chunk-FCTC7KDT.js.map} +0 -0
  154. /package/dist/{chunk-4GMLGCF2.js.map → chunk-JUFN4XMB.js.map} +0 -0
  155. /package/dist/{chunk-UMT7RU77.js.map → chunk-QBEQJFGL.js.map} +0 -0
  156. /package/dist/{chunk-SHTGQGAU.js.map → chunk-V2YZWYXT.js.map} +0 -0
  157. /package/dist/{chunk-BQ6FR32N.js.map → chunk-VD3QSMVY.js.map} +0 -0
  158. /package/dist/{chunk-VUNV25KB.js.map → config-UCAFCS5W.js.map} +0 -0
  159. /package/dist/{config-I4FMCJGZ.js.map → config-editor-OU6PUY66.js.map} +0 -0
  160. /package/dist/{config-editor-3IKBPZA7.js.map → config-registry-ZXAIJNYB.js.map} +0 -0
  161. /package/dist/{config-registry-CUMNXFGK.js.map → context-7MPU7RL5.js.map} +0 -0
  162. /package/dist/{context-XM6E22LM.js.map → core-plugins-R2EVZAJV.js.map} +0 -0
  163. /package/dist/{core-plugins-ROU4GPLT.js.map → daemon-DTA6KYYY.js.map} +0 -0
  164. /package/dist/{daemon-PXO5QPCR.js.map → doctor-D723IB2I.js.map} +0 -0
  165. /package/dist/{doctor-QZQAP46W.js.map → file-service-HHB3JQIO.js.map} +0 -0
  166. /package/dist/{file-service-EUODJAIT.js.map → log-YZ243M5G.js.map} +0 -0
  167. /package/dist/{log-LZ7FTRKG.js.map → menu-ALFN37IR.js.map} +0 -0
  168. /package/dist/{menu-YDQ2LWAR.js.map → notifications-MO23S7S3.js.map} +0 -0
  169. /package/dist/{notifications-D5BRDNSU.js.map → plugin-installer-5XHORMLS.js.map} +0 -0
  170. /package/dist/{plugin-registry-WB3DR67H.js.map → plugin-registry-6J3YSFHF.js.map} +0 -0
  171. /package/dist/{read-text-file-IRZM3QLM.js.map → read-text-file-DJBTITIB.js.map} +0 -0
  172. /package/dist/{registry-client-AVGRE4CF.js.map → registry-client-GTBWLXYU.js.map} +0 -0
  173. /package/dist/{security-YNRBW6S7.js.map → security-2BA265LN.js.map} +0 -0
  174. /package/dist/{settings-manager-MD2U4ZV2.js.map → settings-manager-B4UN2LAC.js.map} +0 -0
  175. /package/dist/{slack-37ZWBDUI.js.map → speech-GB7PHVQZ.js.map} +0 -0
  176. /package/dist/{speech-2GHQNRIO.js.map → telegram-UVIAXADE.js.map} +0 -0
  177. /package/dist/{telegram-2ZCCCZIY.js.map → tunnel-4WNFC7GO.js.map} +0 -0
  178. /package/dist/{tunnel-45HA72MB.js.map → validators-GITLOFXC.js.map} +0 -0
  179. /package/dist/{validators-WSTBNKRW.js.map → version-AXXV6IV2.js.map} +0 -0
@@ -7,20 +7,7 @@ import {
7
7
  } from "./chunk-AFKX424Q.js";
8
8
  import {
9
9
  DoctorEngine
10
- } from "./chunk-QAQDGPB4.js";
11
- import {
12
- BaseRenderer,
13
- MessagingAdapter,
14
- extractContentText,
15
- formatTokens,
16
- formatToolSummary,
17
- formatToolTitle,
18
- progressBar,
19
- resolveToolIcon,
20
- splitMessage,
21
- stripCodeFences,
22
- truncateContent
23
- } from "./chunk-TRXBJEZ5.js";
10
+ } from "./chunk-43JVXFYP.js";
24
11
  import {
25
12
  CheckpointReader,
26
13
  DEFAULT_MAX_TOKENS
@@ -30,10 +17,10 @@ import {
30
17
  getSafeFields,
31
18
  isHotReloadable,
32
19
  resolveOptions
33
- } from "./chunk-ODUM3D6X.js";
20
+ } from "./chunk-5HKQCYOI.js";
34
21
  import {
35
22
  createChildLogger
36
- } from "./chunk-XMMAGAT4.js";
23
+ } from "./chunk-R6KZYF7D.js";
37
24
 
38
25
  // src/plugins/telegram/adapter.ts
39
26
  import { Bot, InputFile } from "grammy";
@@ -78,6 +65,249 @@ function buildDeepLink(chatId, threadId, messageId) {
78
65
  // src/plugins/telegram/commands/new-session.ts
79
66
  import { InlineKeyboard as InlineKeyboard2 } from "grammy";
80
67
 
68
+ // src/core/adapter-primitives/format-utils.ts
69
+ function progressBar(ratio, length = 10) {
70
+ const filled = Math.round(Math.min(ratio, 1) * length);
71
+ return "\u2593".repeat(filled) + "\u2591".repeat(length - filled);
72
+ }
73
+ function formatTokens(n) {
74
+ if (n >= 1e6) {
75
+ const m = n / 1e6;
76
+ return m % 1 === 0 ? `${m}M` : `${parseFloat(m.toFixed(1))}M`;
77
+ }
78
+ if (n >= 1e3) {
79
+ const k = n / 1e3;
80
+ return k % 1 === 0 ? `${k}k` : `${parseFloat(k.toFixed(1))}k`;
81
+ }
82
+ return String(n);
83
+ }
84
+ function stripCodeFences(text) {
85
+ return text.replace(/```\w*\n?/g, "").replace(/```$/gm, "").trim();
86
+ }
87
+ function truncateContent(text, maxLen) {
88
+ if (text.length <= maxLen) return text;
89
+ return text.slice(0, maxLen) + "\n\u2026 (truncated)";
90
+ }
91
+ function splitMessage(text, maxLength) {
92
+ if (text.length <= maxLength) return [text];
93
+ const chunks = [];
94
+ let remaining = text;
95
+ while (remaining.length > 0) {
96
+ if (remaining.length <= maxLength) {
97
+ chunks.push(remaining);
98
+ break;
99
+ }
100
+ const wouldLeaveSmall = remaining.length < maxLength * 1.3;
101
+ const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
102
+ const threshold = maxLength * 0.2;
103
+ let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
104
+ if (splitAt === -1 || splitAt < threshold) {
105
+ splitAt = remaining.lastIndexOf("\n", searchLimit);
106
+ }
107
+ if (splitAt === -1 || splitAt < threshold) {
108
+ splitAt = searchLimit;
109
+ }
110
+ const candidate = remaining.slice(0, splitAt);
111
+ const fences = candidate.match(/```/g);
112
+ if (fences && fences.length % 2 !== 0) {
113
+ const closingFence = remaining.indexOf("```", splitAt);
114
+ if (closingFence !== -1) {
115
+ const afterFence = remaining.indexOf("\n", closingFence + 3);
116
+ const fenceSplit = afterFence !== -1 ? afterFence + 1 : closingFence + 3;
117
+ if (fenceSplit <= maxLength * 2) {
118
+ splitAt = fenceSplit;
119
+ }
120
+ }
121
+ }
122
+ chunks.push(remaining.slice(0, splitAt));
123
+ remaining = remaining.slice(splitAt).replace(/^\n+/, "");
124
+ }
125
+ return chunks;
126
+ }
127
+
128
+ // src/core/adapter-primitives/format-types.ts
129
+ var STATUS_ICONS = {
130
+ pending: "\u23F3",
131
+ in_progress: "\u{1F504}",
132
+ completed: "\u2705",
133
+ failed: "\u274C",
134
+ cancelled: "\u{1F6AB}",
135
+ running: "\u{1F504}",
136
+ done: "\u2705",
137
+ error: "\u274C"
138
+ };
139
+ var KIND_ICONS = {
140
+ read: "\u{1F4D6}",
141
+ edit: "\u270F\uFE0F",
142
+ write: "\u270F\uFE0F",
143
+ delete: "\u{1F5D1}\uFE0F",
144
+ execute: "\u25B6\uFE0F",
145
+ command: "\u25B6\uFE0F",
146
+ bash: "\u25B6\uFE0F",
147
+ terminal: "\u25B6\uFE0F",
148
+ search: "\u{1F50D}",
149
+ web: "\u{1F310}",
150
+ fetch: "\u{1F310}",
151
+ agent: "\u{1F9E0}",
152
+ think: "\u{1F9E0}",
153
+ install: "\u{1F4E6}",
154
+ move: "\u{1F4E6}",
155
+ other: "\u{1F6E0}\uFE0F"
156
+ };
157
+
158
+ // src/core/adapter-primitives/message-formatter.ts
159
+ function extractContentText(content, depth = 0) {
160
+ if (!content || depth > 5) return "";
161
+ if (typeof content === "string") return content;
162
+ if (Array.isArray(content)) {
163
+ return content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
164
+ }
165
+ if (typeof content !== "object") return String(content);
166
+ const obj = content;
167
+ if (obj.text && typeof obj.text === "string") return obj.text;
168
+ if (obj.content) {
169
+ if (typeof obj.content === "string") return obj.content;
170
+ if (Array.isArray(obj.content)) {
171
+ return obj.content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
172
+ }
173
+ return extractContentText(obj.content, depth + 1);
174
+ }
175
+ if (obj.input) return extractContentText(obj.input, depth + 1);
176
+ if (obj.output) return extractContentText(obj.output, depth + 1);
177
+ const keys = Object.keys(obj).filter((k) => k !== "type");
178
+ if (keys.length === 0) return "";
179
+ try {
180
+ return JSON.stringify(obj, null, 2);
181
+ } catch {
182
+ return "";
183
+ }
184
+ }
185
+ function parseRawInput(rawInput) {
186
+ try {
187
+ if (typeof rawInput === "string") {
188
+ return JSON.parse(rawInput);
189
+ }
190
+ if (typeof rawInput === "object" && rawInput !== null) {
191
+ return rawInput;
192
+ }
193
+ } catch {
194
+ }
195
+ return {};
196
+ }
197
+ function formatToolSummary(name, rawInput, displaySummary) {
198
+ if (displaySummary && typeof displaySummary === "string") {
199
+ return displaySummary;
200
+ }
201
+ const args = parseRawInput(rawInput);
202
+ const lowerName = name.toLowerCase();
203
+ if (lowerName === "read") {
204
+ const fp = args.file_path ?? args.filePath ?? "";
205
+ const limit = args.limit ? ` (${args.limit} lines)` : "";
206
+ return fp ? `\u{1F4D6} Read ${fp}${limit}` : `\u{1F527} ${name}`;
207
+ }
208
+ if (lowerName === "edit") {
209
+ const fp = args.file_path ?? args.filePath ?? "";
210
+ return fp ? `\u270F\uFE0F Edit ${fp}` : `\u{1F527} ${name}`;
211
+ }
212
+ if (lowerName === "write") {
213
+ const fp = args.file_path ?? args.filePath ?? "";
214
+ return fp ? `\u{1F4DD} Write ${fp}` : `\u{1F527} ${name}`;
215
+ }
216
+ if (lowerName === "bash" || lowerName === "terminal") {
217
+ const cmd = String(args.command ?? args.cmd ?? "").slice(0, 60);
218
+ return cmd ? `\u25B6\uFE0F Run: ${cmd}` : `\u25B6\uFE0F Terminal`;
219
+ }
220
+ if (lowerName === "grep") {
221
+ const pattern = args.pattern ?? "";
222
+ const path2 = args.path ?? "";
223
+ return pattern ? `\u{1F50D} Grep "${pattern}"${path2 ? ` in ${path2}` : ""}` : `\u{1F527} ${name}`;
224
+ }
225
+ if (lowerName === "glob") {
226
+ const pattern = args.pattern ?? "";
227
+ return pattern ? `\u{1F50D} Glob ${pattern}` : `\u{1F527} ${name}`;
228
+ }
229
+ if (lowerName === "agent") {
230
+ const desc = String(args.description ?? "").slice(0, 60);
231
+ return desc ? `\u{1F9E0} Agent: ${desc}` : `\u{1F527} ${name}`;
232
+ }
233
+ if (lowerName === "webfetch" || lowerName === "web_fetch") {
234
+ const url = String(args.url ?? "").slice(0, 60);
235
+ return url ? `\u{1F310} Fetch ${url}` : `\u{1F527} ${name}`;
236
+ }
237
+ if (lowerName === "websearch" || lowerName === "web_search") {
238
+ const query = String(args.query ?? "").slice(0, 60);
239
+ return query ? `\u{1F310} Search "${query}"` : `\u{1F527} ${name}`;
240
+ }
241
+ return `\u{1F527} ${name}`;
242
+ }
243
+ function formatToolTitle(name, rawInput, displayTitle) {
244
+ if (displayTitle && typeof displayTitle === "string") {
245
+ return displayTitle;
246
+ }
247
+ const args = parseRawInput(rawInput);
248
+ const lowerName = name.toLowerCase();
249
+ if (["read", "edit", "write"].includes(lowerName)) {
250
+ return String(args.file_path ?? args.filePath ?? name);
251
+ }
252
+ if (lowerName === "bash" || lowerName === "terminal") {
253
+ return String(args.command ?? args.cmd ?? name).slice(0, 60);
254
+ }
255
+ if (lowerName === "grep") {
256
+ const pattern = args.pattern ?? "";
257
+ const path2 = args.path ?? "";
258
+ return pattern ? `"${pattern}"${path2 ? ` in ${path2}` : ""}` : name;
259
+ }
260
+ if (lowerName === "glob") {
261
+ return String(args.pattern ?? name);
262
+ }
263
+ if (lowerName === "agent") {
264
+ return String(args.description ?? name).slice(0, 60);
265
+ }
266
+ if (["webfetch", "web_fetch"].includes(lowerName)) {
267
+ return String(args.url ?? name).slice(0, 60);
268
+ }
269
+ if (["websearch", "web_search"].includes(lowerName)) {
270
+ return String(args.query ?? name).slice(0, 60);
271
+ }
272
+ return name;
273
+ }
274
+ function resolveToolIcon(tool) {
275
+ const statusIcon = STATUS_ICONS[tool.status || ""];
276
+ if (statusIcon) return statusIcon;
277
+ const kind = tool.displayKind ?? tool.kind;
278
+ if (kind && KIND_ICONS[kind]) return KIND_ICONS[kind];
279
+ return "\u{1F527}";
280
+ }
281
+ var NOISE_RULES = [
282
+ {
283
+ match: (name) => name.toLowerCase() === "ls",
284
+ action: "hide"
285
+ },
286
+ {
287
+ match: (_name, kind, rawInput) => {
288
+ if (kind !== "read") return false;
289
+ const args = parseRawInput(rawInput);
290
+ const p = String(args.file_path ?? args.filePath ?? args.path ?? "");
291
+ return p.endsWith("/");
292
+ },
293
+ action: "hide"
294
+ },
295
+ {
296
+ match: (name) => name.toLowerCase() === "glob",
297
+ action: "hide"
298
+ },
299
+ {
300
+ match: (name) => name.toLowerCase() === "grep",
301
+ action: "hide"
302
+ }
303
+ ];
304
+ function evaluateNoise(name, kind, rawInput) {
305
+ for (const rule of NOISE_RULES) {
306
+ if (rule.match(name, kind, rawInput)) return rule.action;
307
+ }
308
+ return null;
309
+ }
310
+
81
311
  // src/plugins/telegram/formatting.ts
82
312
  function escapeHtml(text) {
83
313
  if (!text) return "";
@@ -125,15 +355,7 @@ function formatToolCall(tool, verbosity = "medium") {
125
355
  return text;
126
356
  }
127
357
  function formatToolUpdate(update, verbosity = "medium") {
128
- const si = resolveToolIcon(update);
129
- const name = update.name || "Tool";
130
- const label = verbosity === "low" ? formatToolTitle(name, update.rawInput, update.displayTitle) : formatToolSummary(name, update.rawInput, update.displaySummary);
131
- let text = `${si} <b>${escapeHtml(label)}</b>`;
132
- text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
133
- if (verbosity === "high") {
134
- text += formatHighDetails(update.rawInput, update.content, 3800);
135
- }
136
- return text;
358
+ return formatToolCall(update, verbosity);
137
359
  }
138
360
  function formatHighDetails(rawInput, content, maxLen) {
139
361
  let text = "";
@@ -165,12 +387,8 @@ function formatViewerLinks(links, filePath) {
165
387
  \u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
166
388
  return text;
167
389
  }
168
- function formatPlan(plan, verbosity = "medium") {
390
+ function formatPlan(plan) {
169
391
  const { entries } = plan;
170
- if (verbosity === "medium") {
171
- const done = entries.filter((e) => e.status === "completed").length;
172
- return `\u{1F4CB} <b>Plan:</b> ${done}/${entries.length} steps completed`;
173
- }
174
392
  const statusIcon = {
175
393
  pending: "\u2B1C",
176
394
  in_progress: "\u{1F504}",
@@ -182,27 +400,97 @@ function formatPlan(plan, verbosity = "medium") {
182
400
  return `<b>Plan:</b>
183
401
  ${lines.join("\n")}`;
184
402
  }
185
- function formatUsage(usage, verbosity = "medium") {
186
- const { tokensUsed, contextSize, cost } = usage;
403
+ function formatUsage(usage, _verbosity = "medium") {
404
+ const { tokensUsed, contextSize } = usage;
187
405
  if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
188
- if (verbosity === "medium") {
189
- const costStr = cost != null ? ` \xB7 $${cost.toFixed(2)}` : "";
190
- return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens${costStr}`;
191
- }
192
406
  if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
193
407
  const ratio = tokensUsed / contextSize;
194
408
  const pct = Math.round(ratio * 100);
195
409
  const bar = progressBar(ratio);
196
410
  const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
197
- let text = `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
411
+ return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
198
412
  ${bar} ${pct}%`;
199
- if (cost != null) text += `
200
- \u{1F4B0} $${cost.toFixed(2)}`;
201
- return text;
202
413
  }
203
414
  function splitMessage2(text, maxLength = 3800) {
204
415
  return splitMessage(text, maxLength);
205
416
  }
417
+ function renderToolCard(snap) {
418
+ const sections = [];
419
+ const { totalVisible, completedVisible, allComplete } = snap;
420
+ const headerCheck = allComplete ? " \u2705" : "";
421
+ if (totalVisible > 0) {
422
+ sections.push(
423
+ `<b>\u{1F4CB} Tools (${completedVisible}/${totalVisible})</b>${headerCheck}`
424
+ );
425
+ }
426
+ const visible = snap.entries.filter((e) => !e.hidden);
427
+ const completed = visible.filter(
428
+ (e) => e.status === "completed" || e.status === "done" || e.status === "failed"
429
+ );
430
+ const running = visible.filter(
431
+ (e) => e.status !== "completed" && e.status !== "done" && e.status !== "failed"
432
+ );
433
+ for (const entry of completed) {
434
+ let line = `${entry.icon} ${escapeHtml(entry.label)}`;
435
+ if (entry.viewerLinks) {
436
+ const links = [];
437
+ const fileName = entry.viewerFilePath?.split("/").pop() ?? "";
438
+ if (entry.viewerLinks.file)
439
+ links.push(
440
+ `<a href="${escapeHtml(entry.viewerLinks.file)}">View ${escapeHtml(fileName || "file")}</a>`
441
+ );
442
+ if (entry.viewerLinks.diff)
443
+ links.push(
444
+ `<a href="${escapeHtml(entry.viewerLinks.diff)}">View diff</a>`
445
+ );
446
+ if (links.length > 0) line += `
447
+ ${links.join(" \xB7 ")}`;
448
+ }
449
+ sections.push(line);
450
+ }
451
+ if (snap.planEntries && snap.planEntries.length > 0) {
452
+ const planDone = snap.planEntries.filter(
453
+ (e) => e.status === "completed"
454
+ ).length;
455
+ const planTotal = snap.planEntries.length;
456
+ sections.push(`\u2500\u2500 Plan: ${planDone}/${planTotal} \u2500\u2500`);
457
+ const statusIcon = {
458
+ completed: "\u2705",
459
+ in_progress: "\u{1F504}",
460
+ pending: "\u2B1C"
461
+ };
462
+ for (let i = 0; i < snap.planEntries.length; i++) {
463
+ const e = snap.planEntries[i];
464
+ const icon = statusIcon[e.status] ?? "\u2B1C";
465
+ sections.push(`${icon} ${i + 1}. ${escapeHtml(e.content)}`);
466
+ }
467
+ sections.push("\u2500\u2500\u2500\u2500");
468
+ }
469
+ for (const entry of running) {
470
+ sections.push(`${entry.icon} ${escapeHtml(entry.label)}`);
471
+ }
472
+ return sections.join("\n\n");
473
+ }
474
+ var TELEGRAM_MAX_LENGTH = 4096;
475
+ function splitToolCardText(text) {
476
+ if (text.length <= TELEGRAM_MAX_LENGTH) return [text];
477
+ const sections = text.split("\n\n");
478
+ const chunks = [];
479
+ let current = "";
480
+ for (const section of sections) {
481
+ const candidate = current ? `${current}
482
+
483
+ ${section}` : section;
484
+ if (candidate.length > TELEGRAM_MAX_LENGTH && current) {
485
+ chunks.push(current);
486
+ current = section;
487
+ } else {
488
+ current = candidate;
489
+ }
490
+ }
491
+ if (current) chunks.push(current);
492
+ return chunks;
493
+ }
206
494
 
207
495
  // src/plugins/telegram/commands/admin.ts
208
496
  import { InlineKeyboard } from "grammy";
@@ -390,6 +678,15 @@ function setupTTSCallbacks(bot, core) {
390
678
  }
391
679
  return;
392
680
  }
681
+ if (session.voiceMode !== "on" && !core.speechService?.isTTSAvailable()) {
682
+ try {
683
+ await ctx.answerCallbackQuery({
684
+ text: "\u26A0\uFE0F TTS provider not installed. Use /tts install to set up."
685
+ });
686
+ } catch {
687
+ }
688
+ return;
689
+ }
393
690
  const newMode = session.voiceMode === "on" ? "off" : "on";
394
691
  session.setVoiceMode(newMode);
395
692
  const toastText = newMode === "on" ? "\u{1F50A} Text to Speech enabled" : "\u{1F507} Text to Speech disabled";
@@ -426,6 +723,15 @@ async function handleTTS(ctx, core) {
426
723
  }
427
724
  const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
428
725
  const arg = args[0]?.toLowerCase();
726
+ if (arg === "on" || !arg) {
727
+ if (!core.speechService?.isTTSAvailable()) {
728
+ await ctx.reply(
729
+ "\u26A0\uFE0F TTS provider not installed.\n\nUse <code>/tts install</code> to install Edge TTS plugin.",
730
+ { parse_mode: "HTML" }
731
+ );
732
+ return;
733
+ }
734
+ }
429
735
  if (arg === "on") {
430
736
  session.setVoiceMode("on");
431
737
  await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", {
@@ -496,7 +802,7 @@ async function handleUpdate(ctx, core) {
496
802
  );
497
803
  return;
498
804
  }
499
- const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-NQZBM5M7.js");
805
+ const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-AXXV6IV2.js");
500
806
  const current = getCurrentVersion();
501
807
  const statusMsg = await ctx.reply(
502
808
  `\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`,
@@ -1240,9 +1546,22 @@ async function handleArchive(ctx, core) {
1240
1546
  if (!threadId) return;
1241
1547
  const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
1242
1548
  const record = !session ? core.sessionManager.getRecordByThread("telegram", String(threadId)) : void 0;
1243
- const identifier = session?.id ?? record?.sessionId ?? `topic:${threadId}`;
1549
+ if (!session && !record) {
1550
+ await ctx.reply("This topic is not linked to a session.", { parse_mode: "HTML" });
1551
+ return;
1552
+ }
1553
+ const identifier = session?.id ?? record?.sessionId;
1554
+ if (!identifier) {
1555
+ await ctx.reply("Could not determine session for this topic.", { parse_mode: "HTML" });
1556
+ return;
1557
+ }
1558
+ const status = session?.status ?? record?.status;
1559
+ if (status === "initializing") {
1560
+ await ctx.reply("Cannot archive a session that is still initializing. Wait for it to become active.", { parse_mode: "HTML" });
1561
+ return;
1562
+ }
1244
1563
  await ctx.reply(
1245
- "\u26A0\uFE0F <b>Archive this session?</b>\n\nThis will:\n\u2022 Delete this topic and all messages\n\u2022 Stop the agent session (if running)\n\u2022 Remove the session record\n\n<i>This action cannot be undone.</i>",
1564
+ "\u26A0\uFE0F <b>Archive this session?</b>\n\nThis will:\n\u2022 Delete this topic and recreate it (clearing chat history)\n\u2022 The agent session will keep running\n\n<i>Chat history cannot be recovered.</i>",
1246
1565
  {
1247
1566
  parse_mode: "HTML",
1248
1567
  reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${identifier}`).text("\u274C Cancel", `ar:no:${identifier}`)
@@ -1262,37 +1581,21 @@ async function handleArchiveConfirm(ctx, core, chatId) {
1262
1581
  await ctx.editMessageText("Archive cancelled.", { parse_mode: "HTML" });
1263
1582
  return;
1264
1583
  }
1265
- await ctx.editMessageText("\u{1F504} Archiving...", { parse_mode: "HTML" });
1266
- if (identifier.startsWith("topic:")) {
1267
- const topicId = Number(identifier.slice("topic:".length));
1268
- try {
1269
- await ctx.api.deleteForumTopic(chatId, topicId);
1270
- core.notificationManager.notifyAll({
1271
- sessionId: "system",
1272
- sessionName: `Orphan topic #${topicId}`,
1273
- type: "completed",
1274
- summary: `Orphan topic #${topicId} archived and deleted.`
1275
- });
1276
- } catch (err) {
1277
- core.notificationManager.notifyAll({
1278
- sessionId: "system",
1279
- sessionName: `Orphan topic #${topicId}`,
1280
- type: "error",
1281
- summary: `Failed to delete orphan topic #${topicId}: ${err.message}`
1282
- });
1283
- }
1284
- return;
1285
- }
1286
1584
  const result = await core.archiveSession(identifier);
1287
1585
  if (result.ok) {
1288
- core.notificationManager.notifyAll({
1289
- sessionId: identifier,
1290
- type: "completed",
1291
- summary: `Session archived and deleted.`
1292
- });
1586
+ const adapter = core.adapters.get("telegram");
1587
+ if (adapter) {
1588
+ try {
1589
+ await adapter.sendMessage(identifier, {
1590
+ type: "text",
1591
+ text: "Chat history cleared. Session is still active \u2014 send a message to continue."
1592
+ });
1593
+ } catch {
1594
+ }
1595
+ }
1293
1596
  } else {
1294
1597
  try {
1295
- await ctx.editMessageText(`\u274C Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
1598
+ await ctx.editMessageText(`Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
1296
1599
  } catch {
1297
1600
  core.notificationManager.notifyAll({
1298
1601
  sessionId: identifier,
@@ -1502,10 +1805,10 @@ Downloading... ${bar} ${percent}%`, { parse_mode: "HTML" });
1502
1805
  };
1503
1806
  const result = await catalog.install(nameOrId, progress);
1504
1807
  if (result.ok) {
1505
- const { getAgentCapabilities } = await import("./agent-dependencies-WS7Z2DFW.js");
1808
+ const { getAgentCapabilities } = await import("./agent-dependencies-ED2ZTUHG.js");
1506
1809
  const caps = getAgentCapabilities(result.agentKey);
1507
1810
  if (caps.integration) {
1508
- const { installIntegration } = await import("./integrate-G6CVXTGT.js");
1811
+ const { installIntegration } = await import("./integrate-JIEZYDOR.js");
1509
1812
  const intResult = await installIntegration(result.agentKey, caps.integration);
1510
1813
  if (intResult.success) {
1511
1814
  try {
@@ -1575,7 +1878,7 @@ function buildProgressBar(percent) {
1575
1878
  // src/plugins/telegram/commands/integrate.ts
1576
1879
  import { InlineKeyboard as InlineKeyboard5 } from "grammy";
1577
1880
  async function handleIntegrate(ctx, _core) {
1578
- const { listIntegrations } = await import("./integrate-G6CVXTGT.js");
1881
+ const { listIntegrations } = await import("./integrate-JIEZYDOR.js");
1579
1882
  const agents = listIntegrations();
1580
1883
  const keyboard = new InlineKeyboard5();
1581
1884
  for (const agent of agents) {
@@ -1608,7 +1911,7 @@ function setupIntegrateCallbacks(bot, core) {
1608
1911
  } catch {
1609
1912
  }
1610
1913
  if (data === "i:back") {
1611
- const { listIntegrations } = await import("./integrate-G6CVXTGT.js");
1914
+ const { listIntegrations } = await import("./integrate-JIEZYDOR.js");
1612
1915
  const agents = listIntegrations();
1613
1916
  const keyboard2 = new InlineKeyboard5();
1614
1917
  for (const agent of agents) {
@@ -1628,7 +1931,7 @@ Select an agent to manage its integrations.`,
1628
1931
  const agentMatch = data.match(/^i:agent:(.+)$/);
1629
1932
  if (agentMatch) {
1630
1933
  const agentName2 = agentMatch[1];
1631
- const { getIntegration: getIntegration2 } = await import("./integrate-G6CVXTGT.js");
1934
+ const { getIntegration: getIntegration2 } = await import("./integrate-JIEZYDOR.js");
1632
1935
  const integration2 = getIntegration2(agentName2);
1633
1936
  if (!integration2) {
1634
1937
  await ctx.reply(`\u274C No integration available for '${escapeHtml(agentName2)}'.`, { parse_mode: "HTML" });
@@ -1655,7 +1958,7 @@ ${integration2.items.map((i) => `\u2022 <b>${escapeHtml(i.name)}</b> \u2014 ${es
1655
1958
  const action = actionMatch[1];
1656
1959
  const agentName = actionMatch[2];
1657
1960
  const itemId = actionMatch[3];
1658
- const { getIntegration } = await import("./integrate-G6CVXTGT.js");
1961
+ const { getIntegration } = await import("./integrate-JIEZYDOR.js");
1659
1962
  const integration = getIntegration(agentName);
1660
1963
  if (!integration) return;
1661
1964
  const item = integration.items.find((i) => i.id === itemId);
@@ -2139,7 +2442,7 @@ Tap to change:`, {
2139
2442
  await ctx.answerCallbackQuery();
2140
2443
  } catch {
2141
2444
  }
2142
- const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-YDQ2LWAR.js");
2445
+ const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-ALFN37IR.js");
2143
2446
  try {
2144
2447
  await ctx.editMessageText(`<b>OpenACP Menu</b>
2145
2448
  Choose an action:`, {
@@ -3251,6 +3554,117 @@ function redirectToAssistant(chatId, assistantTopicId) {
3251
3554
  return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
3252
3555
  }
3253
3556
 
3557
+ // src/core/adapter-primitives/primitives/tool-card-state.ts
3558
+ var DEBOUNCE_MS = 500;
3559
+ var ToolCardState = class {
3560
+ entries = [];
3561
+ planEntries;
3562
+ usage;
3563
+ finalized = false;
3564
+ isFirstFlush = true;
3565
+ debounceTimer;
3566
+ verbosity;
3567
+ onFlush;
3568
+ constructor(config) {
3569
+ this.verbosity = config.verbosity;
3570
+ this.onFlush = config.onFlush;
3571
+ }
3572
+ addTool(meta, kind, rawInput) {
3573
+ if (this.finalized) return;
3574
+ const hidden = this.verbosity !== "high" && evaluateNoise(meta.name, kind, rawInput) !== null;
3575
+ const entry = {
3576
+ id: meta.id,
3577
+ name: meta.name,
3578
+ kind,
3579
+ status: meta.status ?? "running",
3580
+ icon: resolveToolIcon({ status: meta.status ?? "running", kind }),
3581
+ label: formatToolSummary(meta.name, rawInput, meta.displaySummary),
3582
+ viewerLinks: meta.viewerLinks,
3583
+ viewerFilePath: meta.viewerFilePath,
3584
+ hidden
3585
+ };
3586
+ this.entries.push(entry);
3587
+ if (this.isFirstFlush) {
3588
+ this.isFirstFlush = false;
3589
+ this.flush();
3590
+ } else {
3591
+ this.scheduleFlush();
3592
+ }
3593
+ }
3594
+ updateTool(id, status, viewerLinks, viewerFilePath) {
3595
+ if (this.finalized) return;
3596
+ const entry = this.entries.find((e) => e.id === id);
3597
+ if (!entry) return;
3598
+ entry.status = status;
3599
+ entry.icon = resolveToolIcon({ status, kind: entry.kind });
3600
+ if (viewerLinks) entry.viewerLinks = viewerLinks;
3601
+ if (viewerFilePath) entry.viewerFilePath = viewerFilePath;
3602
+ this.scheduleFlush();
3603
+ }
3604
+ updatePlan(entries) {
3605
+ if (this.finalized) return;
3606
+ this.planEntries = entries;
3607
+ if (this.entries.length === 0 && this.isFirstFlush) {
3608
+ this.isFirstFlush = false;
3609
+ this.flush();
3610
+ } else {
3611
+ this.scheduleFlush();
3612
+ }
3613
+ }
3614
+ appendUsage(usage) {
3615
+ if (this.finalized) return;
3616
+ this.usage = usage;
3617
+ this.scheduleFlush();
3618
+ }
3619
+ finalize() {
3620
+ if (this.finalized) return;
3621
+ this.finalized = true;
3622
+ this.clearDebounce();
3623
+ this.flush();
3624
+ }
3625
+ destroy() {
3626
+ this.finalized = true;
3627
+ this.clearDebounce();
3628
+ }
3629
+ hasContent() {
3630
+ return this.entries.length > 0 || this.planEntries !== void 0;
3631
+ }
3632
+ snapshot() {
3633
+ const visible = this.entries.filter((e) => !e.hidden);
3634
+ const completedVisible = visible.filter(
3635
+ (e) => e.status === "completed" || e.status === "done"
3636
+ ).length;
3637
+ const allComplete = visible.length > 0 && completedVisible === visible.length;
3638
+ return {
3639
+ entries: this.entries,
3640
+ planEntries: this.planEntries,
3641
+ usage: this.usage,
3642
+ visibleCount: visible.length,
3643
+ totalVisible: visible.length,
3644
+ completedVisible,
3645
+ allComplete,
3646
+ verbosity: this.verbosity
3647
+ };
3648
+ }
3649
+ flush() {
3650
+ this.clearDebounce();
3651
+ this.onFlush(this.snapshot());
3652
+ }
3653
+ scheduleFlush() {
3654
+ this.clearDebounce();
3655
+ this.debounceTimer = setTimeout(() => {
3656
+ this.debounceTimer = void 0;
3657
+ this.flush();
3658
+ }, DEBOUNCE_MS);
3659
+ }
3660
+ clearDebounce() {
3661
+ if (this.debounceTimer) {
3662
+ clearTimeout(this.debounceTimer);
3663
+ this.debounceTimer = void 0;
3664
+ }
3665
+ }
3666
+ };
3667
+
3254
3668
  // src/plugins/telegram/activity.ts
3255
3669
  var log10 = createChildLogger({ module: "telegram:activity" });
3256
3670
  var THINKING_REFRESH_MS = 15e3;
@@ -3279,9 +3693,16 @@ var ThinkingIndicator = class {
3279
3693
  disable_notification: true
3280
3694
  })
3281
3695
  );
3282
- if (result && !this.dismissed) {
3283
- this.msgId = result.message_id;
3284
- this.startRefreshTimer();
3696
+ if (result) {
3697
+ if (this.dismissed) {
3698
+ try {
3699
+ await this.api.deleteMessage(this.chatId, result.message_id);
3700
+ } catch {
3701
+ }
3702
+ } else {
3703
+ this.msgId = result.message_id;
3704
+ this.startRefreshTimer();
3705
+ }
3285
3706
  }
3286
3707
  } catch (err) {
3287
3708
  log10.warn({ err }, "ThinkingIndicator.show() failed");
@@ -3289,11 +3710,19 @@ var ThinkingIndicator = class {
3289
3710
  this.sending = false;
3290
3711
  }
3291
3712
  }
3292
- /** Clear state stops refresh timer, no Telegram API call */
3293
- dismiss() {
3713
+ /** Dismiss indicator: stops refresh timer and deletes the Telegram message */
3714
+ async dismiss() {
3715
+ if (this.dismissed) return;
3294
3716
  this.dismissed = true;
3295
- this.msgId = void 0;
3296
3717
  this.stopRefreshTimer();
3718
+ const msgId = this.msgId;
3719
+ this.msgId = void 0;
3720
+ if (msgId) {
3721
+ try {
3722
+ await this.api.deleteMessage(this.chatId, msgId);
3723
+ } catch {
3724
+ }
3725
+ }
3297
3726
  }
3298
3727
  /** Reset for a new prompt cycle */
3299
3728
  reset() {
@@ -3308,20 +3737,14 @@ var ThinkingIndicator = class {
3308
3737
  }
3309
3738
  const elapsed = Math.round((Date.now() - this.showTime) / 1e3);
3310
3739
  this.sendQueue.enqueue(() => {
3311
- if (this.dismissed) return Promise.resolve(void 0);
3312
- return this.api.sendMessage(
3740
+ if (this.dismissed || !this.msgId) return Promise.resolve(void 0);
3741
+ return this.api.editMessageText(
3313
3742
  this.chatId,
3743
+ this.msgId,
3314
3744
  `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`,
3315
- {
3316
- message_thread_id: this.threadId,
3317
- parse_mode: "HTML",
3318
- disable_notification: true
3319
- }
3745
+ { parse_mode: "HTML" }
3320
3746
  );
3321
- }).then((result) => {
3322
- if (result && !this.dismissed) {
3323
- this.msgId = result.message_id;
3324
- }
3747
+ }).then(() => {
3325
3748
  }).catch(() => {
3326
3749
  });
3327
3750
  }, THINKING_REFRESH_MS);
@@ -3333,135 +3756,67 @@ var ThinkingIndicator = class {
3333
3756
  }
3334
3757
  }
3335
3758
  };
3336
- var UsageMessage = class {
3337
- constructor(api, chatId, threadId, sendQueue) {
3759
+ var ToolCard = class {
3760
+ constructor(api, chatId, threadId, sendQueue, verbosity) {
3338
3761
  this.api = api;
3339
3762
  this.chatId = chatId;
3340
3763
  this.threadId = threadId;
3341
3764
  this.sendQueue = sendQueue;
3342
- }
3343
- msgId;
3344
- async send(usage, verbosity = "medium") {
3345
- const text = formatUsage(usage, verbosity);
3346
- try {
3347
- if (this.msgId) {
3348
- await this.sendQueue.enqueue(
3349
- () => this.api.editMessageText(this.chatId, this.msgId, text, {
3350
- parse_mode: "HTML"
3351
- })
3352
- );
3353
- } else {
3354
- const result = await this.sendQueue.enqueue(
3355
- () => this.api.sendMessage(this.chatId, text, {
3356
- message_thread_id: this.threadId,
3357
- parse_mode: "HTML",
3358
- disable_notification: true
3359
- })
3360
- );
3361
- if (result) this.msgId = result.message_id;
3765
+ this.state = new ToolCardState({
3766
+ verbosity,
3767
+ onFlush: (snapshot) => {
3768
+ this.flushPromise = this.flushPromise.then(() => this._sendOrEdit(snapshot)).catch(() => {
3769
+ });
3362
3770
  }
3363
- } catch (err) {
3364
- log10.warn({ err }, "UsageMessage.send() failed");
3365
- }
3366
- }
3367
- getMsgId() {
3368
- return this.msgId;
3369
- }
3370
- async delete() {
3371
- if (!this.msgId) return;
3372
- const id = this.msgId;
3373
- this.msgId = void 0;
3374
- try {
3375
- await this.sendQueue.enqueue(
3376
- () => this.api.deleteMessage(this.chatId, id)
3377
- );
3378
- } catch (err) {
3379
- log10.warn({ err }, "UsageMessage.delete() failed");
3380
- }
3381
- }
3382
- };
3383
- function formatPlanCard(entries, verbosity = "medium") {
3384
- if (verbosity === "medium") {
3385
- const done2 = entries.filter((e) => e.status === "completed").length;
3386
- return `\u{1F4CB} <b>Plan:</b> ${done2}/${entries.length} steps completed`;
3387
- }
3388
- const statusIcon = {
3389
- completed: "\u2705",
3390
- in_progress: "\u{1F504}",
3391
- pending: "\u2B1C",
3392
- failed: "\u274C"
3393
- };
3394
- const total = entries.length;
3395
- const done = entries.filter((e) => e.status === "completed").length;
3396
- const ratio = total > 0 ? done / total : 0;
3397
- const filled = Math.round(ratio * 10);
3398
- const bar = "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
3399
- const pct = Math.round(ratio * 100);
3400
- const header = `\u{1F4CB} <b>Plan</b>
3401
- ${bar} ${pct}% \xB7 ${done}/${total}`;
3402
- const lines = entries.map((e, i) => {
3403
- const icon = statusIcon[e.status] ?? "\u2B1C";
3404
- return `${icon} ${i + 1}. ${e.content}`;
3405
- });
3406
- return [header, ...lines].join("\n");
3407
- }
3408
- var PlanCard = class {
3409
- constructor(api, chatId, threadId, sendQueue) {
3410
- this.api = api;
3411
- this.chatId = chatId;
3412
- this.threadId = threadId;
3413
- this.sendQueue = sendQueue;
3771
+ });
3414
3772
  }
3773
+ state;
3415
3774
  msgId;
3416
- flushPromise = Promise.resolve();
3417
- latestEntries;
3418
3775
  lastSentText;
3419
- flushTimer;
3420
- verbosity = "medium";
3421
- setVerbosity(v) {
3422
- this.verbosity = v;
3776
+ flushPromise = Promise.resolve();
3777
+ overflowCount = 0;
3778
+ addTool(meta, kind, rawInput) {
3779
+ this.state.addTool(meta, kind, rawInput);
3423
3780
  }
3424
- update(entries) {
3425
- this.latestEntries = entries;
3426
- if (this.flushTimer) clearTimeout(this.flushTimer);
3427
- this.flushTimer = setTimeout(() => {
3428
- this.flushTimer = void 0;
3429
- this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
3430
- });
3431
- }, 3500);
3781
+ updateTool(id, status, viewerLinks, viewerFilePath) {
3782
+ this.state.updateTool(id, status, viewerLinks, viewerFilePath);
3783
+ }
3784
+ updatePlan(entries) {
3785
+ this.state.updatePlan(entries);
3786
+ }
3787
+ appendUsage(usage) {
3788
+ this.state.appendUsage(usage);
3432
3789
  }
3433
3790
  async finalize() {
3434
- if (!this.latestEntries) return;
3435
- if (this.flushTimer) {
3436
- clearTimeout(this.flushTimer);
3437
- this.flushTimer = void 0;
3438
- }
3439
- await this.flushPromise;
3440
- this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
3441
- });
3791
+ this.state.finalize();
3442
3792
  await this.flushPromise;
3443
3793
  }
3444
3794
  destroy() {
3445
- if (this.flushTimer) {
3446
- clearTimeout(this.flushTimer);
3447
- this.flushTimer = void 0;
3448
- }
3795
+ this.state.destroy();
3796
+ }
3797
+ hasContent() {
3798
+ return this.state.hasContent();
3449
3799
  }
3450
- async _flush() {
3451
- if (!this.latestEntries) return;
3452
- const text = formatPlanCard(this.latestEntries, this.verbosity);
3453
- if (this.msgId && text === this.lastSentText) return;
3454
- this.lastSentText = text;
3800
+ getMsgId() {
3801
+ return this.msgId;
3802
+ }
3803
+ async _sendOrEdit(snapshot) {
3804
+ const fullText = renderToolCard(snapshot);
3805
+ if (!fullText) return;
3806
+ if (this.msgId && fullText === this.lastSentText) return;
3807
+ this.lastSentText = fullText;
3808
+ const chunks = splitToolCardText(fullText);
3455
3809
  try {
3810
+ const firstChunk = chunks[0];
3456
3811
  if (this.msgId) {
3457
3812
  await this.sendQueue.enqueue(
3458
- () => this.api.editMessageText(this.chatId, this.msgId, text, {
3813
+ () => this.api.editMessageText(this.chatId, this.msgId, firstChunk, {
3459
3814
  parse_mode: "HTML"
3460
3815
  })
3461
3816
  );
3462
3817
  } else {
3463
3818
  const result = await this.sendQueue.enqueue(
3464
- () => this.api.sendMessage(this.chatId, text, {
3819
+ () => this.api.sendMessage(this.chatId, firstChunk, {
3465
3820
  message_thread_id: this.threadId,
3466
3821
  parse_mode: "HTML",
3467
3822
  disable_notification: true
@@ -3469,83 +3824,100 @@ var PlanCard = class {
3469
3824
  );
3470
3825
  if (result) this.msgId = result.message_id;
3471
3826
  }
3827
+ for (let i = 1; i < chunks.length; i++) {
3828
+ if (i > this.overflowCount) {
3829
+ await this.sendQueue.enqueue(
3830
+ () => this.api.sendMessage(this.chatId, chunks[i], {
3831
+ message_thread_id: this.threadId,
3832
+ parse_mode: "HTML",
3833
+ disable_notification: true
3834
+ })
3835
+ );
3836
+ this.overflowCount = i;
3837
+ }
3838
+ }
3472
3839
  } catch (err) {
3473
- log10.warn({ err }, "PlanCard flush failed");
3840
+ log10.warn({ err }, "[ToolCard] send/edit failed");
3474
3841
  }
3475
3842
  }
3476
3843
  };
3477
3844
  var ActivityTracker = class {
3478
- constructor(api, chatId, threadId, sendQueue) {
3845
+ constructor(api, chatId, threadId, sendQueue, verbosity = "medium") {
3479
3846
  this.api = api;
3480
3847
  this.chatId = chatId;
3481
3848
  this.threadId = threadId;
3482
3849
  this.sendQueue = sendQueue;
3850
+ this.verbosity = verbosity;
3483
3851
  this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue);
3484
- this.planCard = new PlanCard(api, chatId, threadId, sendQueue);
3485
- this.usage = new UsageMessage(api, chatId, threadId, sendQueue);
3852
+ this.toolCard = new ToolCard(api, chatId, threadId, sendQueue, verbosity);
3486
3853
  }
3487
3854
  isFirstEvent = true;
3488
- hasPlanCard = false;
3489
3855
  thinking;
3490
- planCard;
3491
- usage;
3856
+ toolCard;
3857
+ verbosity;
3492
3858
  async onNewPrompt() {
3493
3859
  this.isFirstEvent = true;
3494
- this.hasPlanCard = false;
3495
- this.thinking.dismiss();
3860
+ await this.thinking.dismiss();
3496
3861
  this.thinking.reset();
3862
+ await this.toolCard.finalize();
3863
+ this.toolCard = new ToolCard(
3864
+ this.api,
3865
+ this.chatId,
3866
+ this.threadId,
3867
+ this.sendQueue,
3868
+ this.verbosity
3869
+ );
3497
3870
  }
3498
3871
  async onThought() {
3499
- await this._firstEventGuard();
3872
+ this.isFirstEvent = false;
3873
+ await this.sealToolCardIfNeeded();
3500
3874
  await this.thinking.show();
3501
3875
  }
3502
- async onPlan(entries, verbosity) {
3503
- await this._firstEventGuard();
3504
- this.thinking.dismiss();
3505
- this.hasPlanCard = true;
3506
- if (verbosity) this.planCard.setVerbosity(verbosity);
3507
- this.planCard.update(entries);
3876
+ async onTextStart() {
3877
+ this.isFirstEvent = false;
3878
+ await this.thinking.dismiss();
3879
+ await this.sealToolCardIfNeeded();
3508
3880
  }
3509
- async onToolCall() {
3510
- await this._firstEventGuard();
3511
- this.thinking.dismiss();
3881
+ async onToolCall(meta, kind, rawInput) {
3882
+ this.isFirstEvent = false;
3883
+ await this.thinking.dismiss();
3512
3884
  this.thinking.reset();
3885
+ this.toolCard.addTool(meta, kind, rawInput);
3886
+ }
3887
+ /** Finalize current tool card and create a fresh one (when interrupted by text/thought) */
3888
+ async sealToolCardIfNeeded() {
3889
+ if (!this.toolCard.hasContent()) return;
3890
+ await this.toolCard.finalize();
3891
+ this.toolCard = new ToolCard(
3892
+ this.api,
3893
+ this.chatId,
3894
+ this.threadId,
3895
+ this.sendQueue,
3896
+ this.verbosity
3897
+ );
3513
3898
  }
3514
- async onTextStart() {
3515
- await this._firstEventGuard();
3516
- this.thinking.dismiss();
3899
+ async onToolUpdate(id, status, viewerLinks, viewerFilePath) {
3900
+ this.toolCard.updateTool(id, status, viewerLinks, viewerFilePath);
3517
3901
  }
3518
- async sendUsage(data, verbosity = "medium") {
3519
- await this.usage.send(data, verbosity);
3902
+ async onPlan(entries) {
3903
+ this.isFirstEvent = false;
3904
+ await this.thinking.dismiss();
3905
+ this.toolCard.updatePlan(entries);
3520
3906
  }
3521
- getUsageMsgId() {
3522
- return this.usage.getMsgId();
3907
+ /** @deprecated Usage is now sent as a separate message by the adapter */
3908
+ async sendUsage(_usage) {
3523
3909
  }
3524
- async onComplete() {
3525
- if (this.hasPlanCard) {
3526
- await this.planCard.finalize();
3527
- } else {
3528
- try {
3529
- await this.sendQueue.enqueue(
3530
- () => this.api.sendMessage(this.chatId, "\u2705 <b>Done</b>", {
3531
- message_thread_id: this.threadId,
3532
- parse_mode: "HTML",
3533
- disable_notification: true
3534
- })
3535
- );
3536
- } catch (err) {
3537
- log10.warn({ err }, "ActivityTracker.onComplete() Done send failed");
3538
- }
3539
- }
3910
+ getToolCardMsgId() {
3911
+ return this.toolCard.getMsgId();
3540
3912
  }
3541
- destroy() {
3542
- this.thinking.dismiss();
3543
- this.planCard.destroy();
3913
+ async cleanup() {
3914
+ await this.thinking.dismiss();
3915
+ await this.toolCard.finalize();
3916
+ this.toolCard.destroy();
3544
3917
  }
3545
- async _firstEventGuard() {
3546
- if (!this.isFirstEvent) return;
3547
- this.isFirstEvent = false;
3548
- await this.usage.delete();
3918
+ destroy() {
3919
+ void this.thinking.dismiss();
3920
+ this.toolCard.destroy();
3549
3921
  }
3550
3922
  };
3551
3923
 
@@ -3804,143 +4176,6 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
3804
4176
  });
3805
4177
  }
3806
4178
 
3807
- // src/core/adapter-primitives/primitives/tool-call-tracker.ts
3808
- var ToolCallTracker = class {
3809
- sessions = /* @__PURE__ */ new Map();
3810
- track(sessionId, meta, messageId) {
3811
- if (!this.sessions.has(sessionId)) {
3812
- this.sessions.set(sessionId, /* @__PURE__ */ new Map());
3813
- }
3814
- this.sessions.get(sessionId).set(meta.id, { ...meta, messageId });
3815
- }
3816
- update(sessionId, toolId, status, patch) {
3817
- const tool = this.sessions.get(sessionId)?.get(toolId);
3818
- if (!tool) return null;
3819
- tool.status = status;
3820
- if (patch?.viewerLinks) tool.viewerLinks = patch.viewerLinks;
3821
- if (patch?.viewerFilePath) tool.viewerFilePath = patch.viewerFilePath;
3822
- if (patch?.name) tool.name = patch.name;
3823
- if (patch?.kind) tool.kind = patch.kind;
3824
- return tool;
3825
- }
3826
- getActive(sessionId) {
3827
- const session = this.sessions.get(sessionId);
3828
- return session ? [...session.values()] : [];
3829
- }
3830
- clear(sessionId) {
3831
- this.sessions.delete(sessionId);
3832
- }
3833
- clearAll() {
3834
- this.sessions.clear();
3835
- }
3836
- };
3837
-
3838
- // src/plugins/telegram/tool-call-tracker.ts
3839
- var log11 = createChildLogger({ module: "tool-call-tracker" });
3840
- var TelegramToolCallTracker = class {
3841
- constructor(bot, chatId, sendQueue) {
3842
- this.bot = bot;
3843
- this.chatId = chatId;
3844
- this.sendQueue = sendQueue;
3845
- }
3846
- tracker = new ToolCallTracker();
3847
- /** Platform-specific ready-promise per tool call, keyed by `${sessionId}:${toolId}`. */
3848
- readyMap = /* @__PURE__ */ new Map();
3849
- async trackNewCall(sessionId, threadId, meta, verbosity = "medium") {
3850
- let resolveReady;
3851
- const ready = new Promise((r) => {
3852
- resolveReady = r;
3853
- });
3854
- const key = `${sessionId}:${meta.id}`;
3855
- this.readyMap.set(key, { ready });
3856
- this.tracker.track(sessionId, meta, "0");
3857
- try {
3858
- const msg = await this.sendQueue.enqueue(
3859
- () => this.bot.api.sendMessage(this.chatId, formatToolCall(meta, verbosity), {
3860
- message_thread_id: threadId,
3861
- parse_mode: "HTML",
3862
- disable_notification: true
3863
- })
3864
- );
3865
- const tracked = this.tracker.update(sessionId, meta.id, meta.status ?? "running");
3866
- if (tracked) {
3867
- tracked.messageId = String(msg.message_id);
3868
- }
3869
- } finally {
3870
- resolveReady();
3871
- }
3872
- }
3873
- async updateCall(sessionId, meta, verbosity = "medium") {
3874
- const key = `${sessionId}:${meta.id}`;
3875
- const readyState = this.readyMap.get(key);
3876
- const tracked = this.tracker.update(sessionId, meta.id, meta.status, {
3877
- viewerLinks: meta.viewerLinks,
3878
- viewerFilePath: meta.viewerFilePath,
3879
- name: meta.name,
3880
- kind: meta.kind
3881
- });
3882
- if (!tracked) return;
3883
- const isTerminal = meta.status === "completed" || meta.status === "failed";
3884
- if (!isTerminal) return;
3885
- if (readyState) {
3886
- await readyState.ready;
3887
- }
3888
- const msgId = Number(tracked.messageId);
3889
- log11.debug(
3890
- {
3891
- toolId: meta.id,
3892
- status: meta.status,
3893
- hasViewerLinks: !!tracked.viewerLinks,
3894
- viewerLinks: tracked.viewerLinks,
3895
- name: tracked.name,
3896
- msgId
3897
- },
3898
- "Tool completed, preparing edit"
3899
- );
3900
- const merged = {
3901
- id: meta.id,
3902
- name: tracked.name,
3903
- kind: tracked.kind,
3904
- rawInput: tracked.rawInput,
3905
- viewerLinks: tracked.viewerLinks,
3906
- viewerFilePath: tracked.viewerFilePath,
3907
- displaySummary: tracked.displaySummary,
3908
- displayTitle: tracked.displayTitle,
3909
- displayKind: tracked.displayKind,
3910
- status: meta.status,
3911
- content: meta.content
3912
- };
3913
- const formattedText = formatToolUpdate(merged, verbosity);
3914
- try {
3915
- await this.sendQueue.enqueue(
3916
- () => this.bot.api.editMessageText(
3917
- this.chatId,
3918
- msgId,
3919
- formattedText,
3920
- { parse_mode: "HTML" }
3921
- )
3922
- );
3923
- } catch (err) {
3924
- log11.warn(
3925
- {
3926
- err,
3927
- msgId,
3928
- textLen: formattedText.length,
3929
- hasViewerLinks: !!merged.viewerLinks
3930
- },
3931
- "Tool update edit failed"
3932
- );
3933
- }
3934
- }
3935
- cleanup(sessionId) {
3936
- const active = this.tracker.getActive(sessionId);
3937
- for (const tool of active) {
3938
- this.readyMap.delete(`${sessionId}:${tool.id}`);
3939
- }
3940
- this.tracker.clear(sessionId);
3941
- }
3942
- };
3943
-
3944
4179
  // src/plugins/telegram/streaming.ts
3945
4180
  var FLUSH_INTERVAL = 5e3;
3946
4181
  var MessageDraft = class {
@@ -4132,6 +4367,7 @@ var DraftManager = class {
4132
4367
  }
4133
4368
  drafts = /* @__PURE__ */ new Map();
4134
4369
  textBuffers = /* @__PURE__ */ new Map();
4370
+ finalizedDrafts = /* @__PURE__ */ new Map();
4135
4371
  getOrCreate(sessionId, threadId) {
4136
4372
  let draft = this.drafts.get(sessionId);
4137
4373
  if (!draft) {
@@ -4167,6 +4403,9 @@ var DraftManager = class {
4167
4403
  if (!draft) return;
4168
4404
  this.drafts.delete(sessionId);
4169
4405
  const finalMsgId = await draft.finalize();
4406
+ if (finalMsgId) {
4407
+ this.finalizedDrafts.set(sessionId, { messageId: finalMsgId, draft });
4408
+ }
4170
4409
  if (assistantSessionId && sessionId === assistantSessionId) {
4171
4410
  const fullText = this.textBuffers.get(sessionId);
4172
4411
  this.textBuffers.delete(sessionId);
@@ -4189,14 +4428,30 @@ var DraftManager = class {
4189
4428
  this.textBuffers.delete(sessionId);
4190
4429
  }
4191
4430
  }
4431
+ /**
4432
+ * Strip a regex pattern from the active or finalized draft for a session.
4433
+ * Used by tts_strip to remove [TTS]...[/TTS] blocks after TTS audio is sent.
4434
+ */
4435
+ async stripPattern(sessionId, pattern) {
4436
+ const draft = this.drafts.get(sessionId);
4437
+ if (draft) {
4438
+ await draft.stripPattern(pattern);
4439
+ return;
4440
+ }
4441
+ const finalized = this.finalizedDrafts.get(sessionId);
4442
+ if (finalized) {
4443
+ await finalized.draft.stripPattern(pattern);
4444
+ }
4445
+ }
4192
4446
  cleanup(sessionId) {
4193
4447
  this.drafts.delete(sessionId);
4194
4448
  this.textBuffers.delete(sessionId);
4449
+ this.finalizedDrafts.delete(sessionId);
4195
4450
  }
4196
4451
  };
4197
4452
 
4198
4453
  // src/plugins/telegram/skill-command-manager.ts
4199
- var log12 = createChildLogger({ module: "skill-commands" });
4454
+ var log11 = createChildLogger({ module: "skill-commands" });
4200
4455
  var SkillCommandManager = class {
4201
4456
  // sessionId → pinned msgId
4202
4457
  constructor(bot, chatId, sendQueue, sessionManager) {
@@ -4262,7 +4517,7 @@ var SkillCommandManager = class {
4262
4517
  disable_notification: true
4263
4518
  });
4264
4519
  } catch (err) {
4265
- log12.error({ err, sessionId }, "Failed to send skill commands");
4520
+ log11.error({ err, sessionId }, "Failed to send skill commands");
4266
4521
  }
4267
4522
  }
4268
4523
  async cleanup(sessionId) {
@@ -4290,26 +4545,263 @@ var SkillCommandManager = class {
4290
4545
  }
4291
4546
  };
4292
4547
 
4548
+ // src/core/adapter-primitives/messaging-adapter.ts
4549
+ var HIDDEN_ON_LOW = /* @__PURE__ */ new Set(["thought", "usage"]);
4550
+ var MessagingAdapter = class {
4551
+ constructor(context, adapterConfig) {
4552
+ this.context = context;
4553
+ this.adapterConfig = adapterConfig;
4554
+ }
4555
+ // === Message dispatch flow ===
4556
+ async sendMessage(sessionId, content) {
4557
+ const verbosity = this.getVerbosity();
4558
+ if (!this.shouldDisplay(content, verbosity)) return;
4559
+ await this.dispatchMessage(sessionId, content, verbosity);
4560
+ }
4561
+ async dispatchMessage(sessionId, content, verbosity) {
4562
+ switch (content.type) {
4563
+ case "text":
4564
+ return this.handleText(sessionId, content);
4565
+ case "thought":
4566
+ return this.handleThought(sessionId, content, verbosity);
4567
+ case "tool_call":
4568
+ return this.handleToolCall(sessionId, content, verbosity);
4569
+ case "tool_update":
4570
+ return this.handleToolUpdate(sessionId, content, verbosity);
4571
+ case "plan":
4572
+ return this.handlePlan(sessionId, content, verbosity);
4573
+ case "usage":
4574
+ return this.handleUsage(sessionId, content, verbosity);
4575
+ case "error":
4576
+ return this.handleError(sessionId, content);
4577
+ case "attachment":
4578
+ return this.handleAttachment(sessionId, content);
4579
+ case "system_message":
4580
+ return this.handleSystem(sessionId, content);
4581
+ case "session_end":
4582
+ return this.handleSessionEnd(sessionId, content);
4583
+ case "mode_change":
4584
+ return this.handleModeChange(sessionId, content);
4585
+ case "config_update":
4586
+ return this.handleConfigUpdate(sessionId, content);
4587
+ case "model_update":
4588
+ return this.handleModelUpdate(sessionId, content);
4589
+ case "user_replay":
4590
+ return this.handleUserReplay(sessionId, content);
4591
+ case "resource":
4592
+ return this.handleResource(sessionId, content);
4593
+ case "resource_link":
4594
+ return this.handleResourceLink(sessionId, content);
4595
+ }
4596
+ }
4597
+ // === Default handlers — all protected, all overridable ===
4598
+ async handleText(_sessionId, _content) {
4599
+ }
4600
+ async handleThought(_sessionId, _content, _verbosity) {
4601
+ }
4602
+ async handleToolCall(_sessionId, _content, _verbosity) {
4603
+ }
4604
+ async handleToolUpdate(_sessionId, _content, _verbosity) {
4605
+ }
4606
+ async handlePlan(_sessionId, _content, _verbosity) {
4607
+ }
4608
+ async handleUsage(_sessionId, _content, _verbosity) {
4609
+ }
4610
+ async handleError(_sessionId, _content) {
4611
+ }
4612
+ async handleAttachment(_sessionId, _content) {
4613
+ }
4614
+ async handleSystem(_sessionId, _content) {
4615
+ }
4616
+ async handleSessionEnd(_sessionId, _content) {
4617
+ }
4618
+ async handleModeChange(_sessionId, _content) {
4619
+ }
4620
+ async handleConfigUpdate(_sessionId, _content) {
4621
+ }
4622
+ async handleModelUpdate(_sessionId, _content) {
4623
+ }
4624
+ async handleUserReplay(_sessionId, _content) {
4625
+ }
4626
+ async handleResource(_sessionId, _content) {
4627
+ }
4628
+ async handleResourceLink(_sessionId, _content) {
4629
+ }
4630
+ // === Helpers ===
4631
+ getVerbosity() {
4632
+ const config = this.context.configManager.get();
4633
+ const channelConfig = config.channels;
4634
+ const v = channelConfig?.[this.name]?.displayVerbosity ?? this.adapterConfig.displayVerbosity;
4635
+ if (v === "low" || v === "high") return v;
4636
+ return "medium";
4637
+ }
4638
+ shouldDisplay(content, verbosity) {
4639
+ if (verbosity === "low" && HIDDEN_ON_LOW.has(content.type)) return false;
4640
+ if (content.type === "tool_call") {
4641
+ const meta = content.metadata ?? {};
4642
+ const toolName = meta.name ?? content.text ?? "";
4643
+ const toolKind = String(meta.kind ?? "other");
4644
+ const noiseAction = evaluateNoise(toolName, toolKind, meta.rawInput);
4645
+ if (noiseAction === "hide" && verbosity !== "high") return false;
4646
+ if (noiseAction === "collapse" && verbosity === "low") return false;
4647
+ }
4648
+ return true;
4649
+ }
4650
+ };
4651
+
4652
+ // src/core/adapter-primitives/rendering/renderer.ts
4653
+ var BaseRenderer = class {
4654
+ renderText(content) {
4655
+ return { body: content.text, format: "plain" };
4656
+ }
4657
+ renderToolCall(content, verbosity) {
4658
+ const meta = content.metadata ?? {};
4659
+ const name = meta.name ?? content.text ?? "Tool";
4660
+ const icon = resolveToolIcon(meta);
4661
+ const label = verbosity === "low" ? formatToolTitle(
4662
+ name,
4663
+ meta.rawInput,
4664
+ meta.displayTitle
4665
+ ) : formatToolSummary(
4666
+ name,
4667
+ meta.rawInput,
4668
+ meta.displaySummary
4669
+ );
4670
+ return { body: `${icon} ${label}`, format: "plain" };
4671
+ }
4672
+ renderToolUpdate(content, verbosity) {
4673
+ const meta = content.metadata ?? {};
4674
+ const name = meta.name ?? content.text ?? "Tool";
4675
+ const icon = resolveToolIcon(meta);
4676
+ const label = verbosity === "low" ? formatToolTitle(
4677
+ name,
4678
+ meta.rawInput,
4679
+ meta.displayTitle
4680
+ ) : formatToolSummary(
4681
+ name,
4682
+ meta.rawInput,
4683
+ meta.displaySummary
4684
+ );
4685
+ return { body: `${icon} ${label}`, format: "plain" };
4686
+ }
4687
+ renderPlan(content) {
4688
+ const entries = content.metadata?.entries ?? [];
4689
+ const lines = entries.map((e, i) => {
4690
+ const icon = e.status === "completed" ? "\u2705" : e.status === "in_progress" ? "\u{1F504}" : "\u2B1C";
4691
+ return `${icon} ${i + 1}. ${e.content}`;
4692
+ });
4693
+ return { body: `\u{1F4CB} Plan
4694
+ ${lines.join("\n")}`, format: "plain" };
4695
+ }
4696
+ renderUsage(content, verbosity) {
4697
+ const meta = content.metadata;
4698
+ if (!meta?.tokensUsed)
4699
+ return { body: "\u{1F4CA} Usage data unavailable", format: "plain" };
4700
+ const costStr = meta.cost != null ? ` \xB7 $${meta.cost.toFixed(2)}` : "";
4701
+ if (verbosity === "medium") {
4702
+ return {
4703
+ body: `\u{1F4CA} ${formatTokens(meta.tokensUsed)} tokens${costStr}`,
4704
+ format: "plain"
4705
+ };
4706
+ }
4707
+ if (!meta.contextSize)
4708
+ return {
4709
+ body: `\u{1F4CA} ${formatTokens(meta.tokensUsed)} tokens`,
4710
+ format: "plain"
4711
+ };
4712
+ const ratio = meta.tokensUsed / meta.contextSize;
4713
+ const pct = Math.round(ratio * 100);
4714
+ const bar = progressBar(ratio);
4715
+ let text = `\u{1F4CA} ${formatTokens(meta.tokensUsed)} / ${formatTokens(meta.contextSize)} tokens
4716
+ ${bar} ${pct}%`;
4717
+ if (meta.cost != null) text += `
4718
+ \u{1F4B0} $${meta.cost.toFixed(2)}`;
4719
+ return { body: text, format: "plain" };
4720
+ }
4721
+ renderPermission(request) {
4722
+ return {
4723
+ body: request.description,
4724
+ format: "plain",
4725
+ actions: request.options.map((o) => ({
4726
+ id: o.id,
4727
+ label: o.label,
4728
+ isAllow: o.isAllow
4729
+ }))
4730
+ };
4731
+ }
4732
+ renderError(content) {
4733
+ return { body: `\u274C Error: ${content.text}`, format: "plain" };
4734
+ }
4735
+ renderNotification(notification) {
4736
+ const emoji = {
4737
+ completed: "\u2705",
4738
+ error: "\u274C",
4739
+ permission: "\u{1F510}",
4740
+ input_required: "\u{1F4AC}",
4741
+ budget_warning: "\u26A0\uFE0F"
4742
+ };
4743
+ return {
4744
+ body: `${emoji[notification.type] || "\u2139\uFE0F"} ${notification.sessionName || "Session"}
4745
+ ${notification.summary}`,
4746
+ format: "plain"
4747
+ };
4748
+ }
4749
+ renderSystemMessage(content) {
4750
+ return { body: content.text, format: "plain" };
4751
+ }
4752
+ renderModeChange(content) {
4753
+ const modeId = content.metadata?.modeId ?? "";
4754
+ return { body: `\u{1F504} Mode: ${modeId}`, format: "plain" };
4755
+ }
4756
+ renderConfigUpdate() {
4757
+ return { body: "\u2699\uFE0F Config updated", format: "plain" };
4758
+ }
4759
+ renderModelUpdate(content) {
4760
+ const modelId = content.metadata?.modelId ?? "";
4761
+ return { body: `\u{1F916} Model: ${modelId}`, format: "plain" };
4762
+ }
4763
+ renderResource(content) {
4764
+ const uri = content.metadata?.uri ?? "";
4765
+ return { body: `\u{1F4C4} Resource: ${content.text} (${uri})`, format: "plain" };
4766
+ }
4767
+ renderResourceLink(content) {
4768
+ const uri = content.metadata?.uri ?? "";
4769
+ return { body: `\u{1F517} ${content.text}: ${uri}`, format: "plain" };
4770
+ }
4771
+ };
4772
+
4293
4773
  // src/plugins/telegram/renderer.ts
4294
4774
  var TelegramRenderer = class extends BaseRenderer {
4295
4775
  renderToolCall(content, verbosity) {
4296
4776
  const meta = content.metadata ?? {};
4297
- return { body: formatToolCall(meta, verbosity), format: "html" };
4777
+ return {
4778
+ body: formatToolCall(meta, verbosity),
4779
+ format: "html"
4780
+ };
4298
4781
  }
4299
4782
  renderToolUpdate(content, verbosity) {
4300
4783
  const meta = content.metadata ?? {};
4301
- return { body: formatToolUpdate(meta, verbosity), format: "html" };
4784
+ return {
4785
+ body: formatToolUpdate(meta, verbosity),
4786
+ format: "html"
4787
+ };
4302
4788
  }
4303
- renderPlan(content, verbosity) {
4789
+ renderPlan(content) {
4304
4790
  const meta = content.metadata;
4305
- return { body: formatPlan({ entries: meta?.entries ?? [] }, verbosity), format: "html" };
4791
+ return {
4792
+ body: formatPlan({ entries: meta?.entries ?? [] }),
4793
+ format: "html"
4794
+ };
4306
4795
  }
4307
4796
  renderUsage(content, verbosity) {
4308
4797
  const meta = content.metadata;
4309
4798
  return { body: formatUsage(meta ?? {}, verbosity), format: "html" };
4310
4799
  }
4311
4800
  renderError(content) {
4312
- return { body: `\u274C <b>Error:</b> ${escapeHtml(content.text)}`, format: "html" };
4801
+ return {
4802
+ body: `\u274C <b>Error:</b> ${escapeHtml(content.text)}`,
4803
+ format: "html"
4804
+ };
4313
4805
  }
4314
4806
  renderNotification(notification) {
4315
4807
  const emoji = {
@@ -4329,19 +4821,25 @@ var TelegramRenderer = class extends BaseRenderer {
4329
4821
  }
4330
4822
  renderModeChange(content) {
4331
4823
  const modeId = content.metadata?.modeId ?? "";
4332
- return { body: `\u{1F504} <b>Mode:</b> ${escapeHtml(String(modeId))}`, format: "html" };
4824
+ return {
4825
+ body: `\u{1F504} <b>Mode:</b> ${escapeHtml(String(modeId))}`,
4826
+ format: "html"
4827
+ };
4333
4828
  }
4334
4829
  renderConfigUpdate() {
4335
4830
  return { body: "\u2699\uFE0F <b>Config updated</b>", format: "html" };
4336
4831
  }
4337
4832
  renderModelUpdate(content) {
4338
4833
  const modelId = content.metadata?.modelId ?? "";
4339
- return { body: `\u{1F916} <b>Model:</b> ${escapeHtml(String(modelId))}`, format: "html" };
4834
+ return {
4835
+ body: `\u{1F916} <b>Model:</b> ${escapeHtml(String(modelId))}`,
4836
+ format: "html"
4837
+ };
4340
4838
  }
4341
4839
  };
4342
4840
 
4343
4841
  // src/plugins/telegram/adapter.ts
4344
- var log13 = createChildLogger({ module: "telegram" });
4842
+ var log12 = createChildLogger({ module: "telegram" });
4345
4843
  function patchedFetch(input, init) {
4346
4844
  if (init?.signal && !(init.signal instanceof AbortSignal)) {
4347
4845
  const nativeController = new AbortController();
@@ -4377,7 +4875,6 @@ var TelegramAdapter = class extends MessagingAdapter {
4377
4875
  sendQueue = new SendQueue({ minInterval: 3e3 });
4378
4876
  _sessionThreadIds = /* @__PURE__ */ new Map();
4379
4877
  // Extracted managers
4380
- toolTracker;
4381
4878
  draftManager;
4382
4879
  skillManager;
4383
4880
  fileService;
@@ -4391,24 +4888,26 @@ var TelegramAdapter = class extends MessagingAdapter {
4391
4888
  }
4392
4889
  return threadId;
4393
4890
  }
4394
- getOrCreateTracker(sessionId, threadId) {
4891
+ getOrCreateTracker(sessionId, threadId, verbosity = "medium") {
4395
4892
  let tracker = this.sessionTrackers.get(sessionId);
4396
4893
  if (!tracker) {
4397
4894
  tracker = new ActivityTracker(
4398
4895
  this.bot.api,
4399
4896
  this.telegramConfig.chatId,
4400
4897
  threadId,
4401
- this.sendQueue
4898
+ this.sendQueue,
4899
+ verbosity
4402
4900
  );
4403
4901
  this.sessionTrackers.set(sessionId, tracker);
4404
4902
  }
4405
4903
  return tracker;
4406
4904
  }
4407
4905
  constructor(core, config) {
4408
- super(
4409
- { configManager: core.configManager },
4410
- { ...config, maxMessageLength: 4096, enabled: config.enabled ?? true }
4411
- );
4906
+ super({ configManager: core.configManager }, {
4907
+ ...config,
4908
+ maxMessageLength: 4096,
4909
+ enabled: config.enabled ?? true
4910
+ });
4412
4911
  this.core = core;
4413
4912
  this.telegramConfig = config;
4414
4913
  }
@@ -4420,11 +4919,6 @@ var TelegramAdapter = class extends MessagingAdapter {
4420
4919
  }
4421
4920
  });
4422
4921
  this.fileService = this.core.fileService;
4423
- this.toolTracker = new TelegramToolCallTracker(
4424
- this.bot,
4425
- this.telegramConfig.chatId,
4426
- this.sendQueue
4427
- );
4428
4922
  this.draftManager = new DraftManager(
4429
4923
  this.bot,
4430
4924
  this.telegramConfig.chatId,
@@ -4438,7 +4932,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4438
4932
  );
4439
4933
  this.bot.catch((err) => {
4440
4934
  const rootCause = err.error instanceof Error ? err.error : err;
4441
- log13.error({ err: rootCause }, "Telegram bot error");
4935
+ log12.error({ err: rootCause }, "Telegram bot error");
4442
4936
  });
4443
4937
  this.bot.api.config.use(async (prev, method, payload, signal) => {
4444
4938
  const maxRetries = 3;
@@ -4456,7 +4950,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4456
4950
  if (rateLimitedMethods.includes(method)) {
4457
4951
  this.sendQueue.onRateLimited();
4458
4952
  }
4459
- log13.warn(
4953
+ log12.warn(
4460
4954
  { method, retryAfter, attempt: attempt + 1 },
4461
4955
  "Rate limited by Telegram, retrying"
4462
4956
  );
@@ -4503,7 +4997,9 @@ var TelegramAdapter = class extends MessagingAdapter {
4503
4997
  this.bot.on("message:text", async (ctx, next) => {
4504
4998
  const text = ctx.message?.text;
4505
4999
  if (!text?.startsWith("/")) return next();
4506
- const registry = this.core.lifecycleManager?.serviceRegistry?.get("command-registry");
5000
+ const registry = this.core.lifecycleManager?.serviceRegistry?.get(
5001
+ "command-registry"
5002
+ );
4507
5003
  if (!registry) return next();
4508
5004
  const rawCommand = text.split(" ")[0].slice(1);
4509
5005
  const atIdx = rawCommand.indexOf("@");
@@ -4516,7 +5012,10 @@ var TelegramAdapter = class extends MessagingAdapter {
4516
5012
  const chatId = ctx.chat.id;
4517
5013
  const topicId = ctx.message.message_thread_id;
4518
5014
  try {
4519
- const sessionId = topicId != null ? this.core.sessionManager.getSessionByThread("telegram", String(topicId))?.id ?? null : null;
5015
+ const sessionId = topicId != null ? this.core.sessionManager.getSessionByThread(
5016
+ "telegram",
5017
+ String(topicId)
5018
+ )?.id ?? null : null;
4520
5019
  const response = await registry.execute(text, {
4521
5020
  raw: "",
4522
5021
  sessionId,
@@ -4526,7 +5025,11 @@ var TelegramAdapter = class extends MessagingAdapter {
4526
5025
  if (typeof content === "string") {
4527
5026
  await ctx.reply(content);
4528
5027
  } else if (typeof content === "object" && content !== null && "type" in content) {
4529
- await this.renderCommandResponse(content, chatId, topicId);
5028
+ await this.renderCommandResponse(
5029
+ content,
5030
+ chatId,
5031
+ topicId
5032
+ );
4530
5033
  }
4531
5034
  }
4532
5035
  });
@@ -4541,12 +5044,17 @@ var TelegramAdapter = class extends MessagingAdapter {
4541
5044
  this.bot.callbackQuery(/^c\//, async (ctx) => {
4542
5045
  const data = ctx.callbackQuery.data;
4543
5046
  const command = this.fromCallbackData(data);
4544
- const registry = this.core.lifecycleManager?.serviceRegistry?.get("command-registry");
5047
+ const registry = this.core.lifecycleManager?.serviceRegistry?.get(
5048
+ "command-registry"
5049
+ );
4545
5050
  if (!registry) return;
4546
5051
  const chatId = ctx.chat.id;
4547
5052
  const topicId = ctx.callbackQuery.message?.message_thread_id;
4548
5053
  try {
4549
- const sessionId = topicId != null ? this.core.sessionManager.getSessionByThread("telegram", String(topicId))?.id ?? null : null;
5054
+ const sessionId = topicId != null ? this.core.sessionManager.getSessionByThread(
5055
+ "telegram",
5056
+ String(topicId)
5057
+ )?.id ?? null : null;
4550
5058
  const response = await registry.execute(command, {
4551
5059
  raw: "",
4552
5060
  sessionId,
@@ -4644,7 +5152,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4644
5152
  });
4645
5153
  return;
4646
5154
  }
4647
- const { getAgentCapabilities } = await import("./agent-registry-5LZT7CUB.js");
5155
+ const { getAgentCapabilities } = await import("./agent-registry-YOGP656W.js");
4648
5156
  const caps = getAgentCapabilities(agentName);
4649
5157
  if (!caps.supportsResume || !caps.resumeCommand) {
4650
5158
  await ctx.reply("This agent does not support session transfer.", {
@@ -4666,7 +5174,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4666
5174
  this.setupRoutes();
4667
5175
  this.bot.start({
4668
5176
  allowed_updates: ["message", "callback_query"],
4669
- onStart: () => log13.info(
5177
+ onStart: () => log12.info(
4670
5178
  { chatId: this.telegramConfig.chatId },
4671
5179
  "Telegram bot started"
4672
5180
  )
@@ -4690,10 +5198,10 @@ var TelegramAdapter = class extends MessagingAdapter {
4690
5198
  reply_markup: buildMenuKeyboard()
4691
5199
  });
4692
5200
  } catch (err) {
4693
- log13.warn({ err }, "Failed to send welcome message");
5201
+ log12.warn({ err }, "Failed to send welcome message");
4694
5202
  }
4695
5203
  try {
4696
- log13.info("Spawning assistant session...");
5204
+ log12.info("Spawning assistant session...");
4697
5205
  const { session, ready } = await spawnAssistant(
4698
5206
  this.core,
4699
5207
  this,
@@ -4701,19 +5209,19 @@ var TelegramAdapter = class extends MessagingAdapter {
4701
5209
  );
4702
5210
  this.assistantSession = session;
4703
5211
  this.assistantInitializing = true;
4704
- log13.info(
5212
+ log12.info(
4705
5213
  { sessionId: session.id },
4706
5214
  "Assistant session ready, system prompt running in background"
4707
5215
  );
4708
5216
  ready.then(() => {
4709
5217
  this.assistantInitializing = false;
4710
- log13.info(
5218
+ log12.info(
4711
5219
  { sessionId: session.id },
4712
5220
  "Assistant ready for user messages"
4713
5221
  );
4714
5222
  });
4715
5223
  } catch (err) {
4716
- log13.error({ err }, "Failed to spawn assistant");
5224
+ log12.error({ err }, "Failed to spawn assistant");
4717
5225
  this.bot.api.sendMessage(
4718
5226
  this.telegramConfig.chatId,
4719
5227
  `\u26A0\uFE0F <b>Failed to start assistant session.</b>
@@ -4725,11 +5233,16 @@ var TelegramAdapter = class extends MessagingAdapter {
4725
5233
  }
4726
5234
  }
4727
5235
  async stop() {
5236
+ for (const tracker of this.sessionTrackers.values()) {
5237
+ tracker.destroy();
5238
+ }
5239
+ this.sessionTrackers.clear();
5240
+ this.sendQueue.clear();
4728
5241
  if (this.assistantSession) {
4729
5242
  await this.assistantSession.destroy();
4730
5243
  }
4731
5244
  await this.bot.stop();
4732
- log13.info("Telegram bot stopped");
5245
+ log12.info("Telegram bot stopped");
4733
5246
  }
4734
5247
  // --- CommandRegistry response rendering ---
4735
5248
  async renderCommandResponse(response, chatId, topicId) {
@@ -4857,7 +5370,7 @@ ${lines.join("\n")}`;
4857
5370
  ctx.replyWithChatAction("typing").catch(() => {
4858
5371
  });
4859
5372
  handleAssistantMessage(this.assistantSession, forwardText).catch(
4860
- (err) => log13.error({ err }, "Assistant error")
5373
+ (err) => log12.error({ err }, "Assistant error")
4861
5374
  );
4862
5375
  return;
4863
5376
  }
@@ -4878,7 +5391,7 @@ ${lines.join("\n")}`;
4878
5391
  threadId: String(threadId),
4879
5392
  userId: String(ctx.from.id),
4880
5393
  text: forwardText
4881
- }).catch((err) => log13.error({ err }, "handleMessage error"));
5394
+ }).catch((err) => log12.error({ err }, "handleMessage error"));
4882
5395
  });
4883
5396
  this.bot.on("message:photo", async (ctx) => {
4884
5397
  const threadId = ctx.message.message_thread_id;
@@ -4957,7 +5470,7 @@ ${lines.join("\n")}`;
4957
5470
  if (session.archiving) return;
4958
5471
  const threadId = Number(session.threadId);
4959
5472
  if (!threadId || isNaN(threadId)) {
4960
- log13.warn(
5473
+ log12.warn(
4961
5474
  { sessionId, threadId: session.threadId },
4962
5475
  "Session has no valid threadId, skipping message"
4963
5476
  );
@@ -4989,15 +5502,9 @@ ${lines.join("\n")}`;
4989
5502
  async handleToolCall(sessionId, content, verbosity) {
4990
5503
  const threadId = this.getThreadId(sessionId);
4991
5504
  const meta = content.metadata ?? {};
4992
- const tracker = this.getOrCreateTracker(sessionId, threadId);
4993
- await tracker.onToolCall();
4994
- await this.draftManager.finalize(
4995
- sessionId,
4996
- this.assistantSession?.id
4997
- );
4998
- await this.toolTracker.trackNewCall(
4999
- sessionId,
5000
- threadId,
5505
+ const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
5506
+ await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5507
+ await tracker.onToolCall(
5001
5508
  {
5002
5509
  id: meta.id ?? "",
5003
5510
  name: meta.name ?? content.text ?? "Tool",
@@ -5011,58 +5518,57 @@ ${lines.join("\n")}`;
5011
5518
  displayTitle: meta.displayTitle,
5012
5519
  displayKind: meta.displayKind
5013
5520
  },
5014
- verbosity
5521
+ String(meta.kind ?? ""),
5522
+ meta.rawInput
5015
5523
  );
5016
5524
  }
5017
5525
  async handleToolUpdate(sessionId, content, verbosity) {
5526
+ const threadId = this.getThreadId(sessionId);
5018
5527
  const meta = content.metadata ?? {};
5019
- await this.toolTracker.updateCall(
5020
- sessionId,
5021
- {
5022
- id: meta.id ?? "",
5023
- name: meta.name ?? content.text ?? "",
5024
- kind: meta.kind,
5025
- status: meta.status ?? "completed",
5026
- content: meta.content,
5027
- rawInput: meta.rawInput,
5028
- viewerLinks: meta.viewerLinks,
5029
- viewerFilePath: meta.viewerFilePath,
5030
- displaySummary: meta.displaySummary,
5031
- displayTitle: meta.displayTitle,
5032
- displayKind: meta.displayKind
5033
- },
5034
- verbosity
5528
+ const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
5529
+ await tracker.onToolUpdate(
5530
+ meta.id ?? "",
5531
+ meta.status ?? "completed",
5532
+ meta.viewerLinks,
5533
+ meta.viewerFilePath
5035
5534
  );
5036
5535
  }
5037
5536
  async handlePlan(sessionId, content, verbosity) {
5038
5537
  const threadId = this.getThreadId(sessionId);
5039
5538
  const meta = content.metadata ?? {};
5040
5539
  const entries = meta.entries ?? [];
5041
- const tracker = this.getOrCreateTracker(sessionId, threadId);
5540
+ const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
5042
5541
  await tracker.onPlan(
5043
5542
  entries.map((e) => ({
5044
5543
  content: e.content,
5045
5544
  status: e.status,
5046
5545
  priority: e.priority ?? "medium"
5047
- })),
5048
- verbosity
5546
+ }))
5049
5547
  );
5050
5548
  }
5051
5549
  async handleUsage(sessionId, content, verbosity) {
5052
5550
  const threadId = this.getThreadId(sessionId);
5053
5551
  const meta = content.metadata;
5054
- await this.draftManager.finalize(
5055
- sessionId,
5056
- this.assistantSession?.id
5057
- );
5058
- const tracker = this.getOrCreateTracker(sessionId, threadId);
5059
- await tracker.sendUsage(meta ?? {}, verbosity);
5552
+ await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5553
+ const usageText = formatUsage(meta ?? {}, verbosity);
5554
+ let usageMsgId;
5555
+ try {
5556
+ const result = await this.sendQueue.enqueue(
5557
+ () => this.bot.api.sendMessage(this.telegramConfig.chatId, usageText, {
5558
+ message_thread_id: threadId,
5559
+ parse_mode: "HTML",
5560
+ disable_notification: true
5561
+ })
5562
+ );
5563
+ usageMsgId = result?.message_id;
5564
+ } catch (err) {
5565
+ log12.warn({ err, sessionId }, "Failed to send usage message");
5566
+ }
5060
5567
  if (this.notificationTopicId && sessionId !== this.assistantSession?.id) {
5061
5568
  const sess = this.core.sessionManager.getSession(sessionId);
5062
5569
  const sessionName = sess?.name || "Session";
5063
5570
  const chatIdStr = String(this.telegramConfig.chatId);
5064
5571
  const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
5065
- const usageMsgId = tracker.getUsageMsgId();
5066
5572
  const deepLink = usageMsgId ? `https://t.me/c/${numericId}/${threadId}/${usageMsgId}` : `https://t.me/c/${numericId}/${threadId}`;
5067
5573
  const text = `\u2705 <b>${escapeHtml(sessionName)}</b>
5068
5574
  Task completed.
@@ -5083,7 +5589,7 @@ Task completed.
5083
5589
  if (!content.attachment) return;
5084
5590
  const { attachment } = content;
5085
5591
  if (attachment.size > 50 * 1024 * 1024) {
5086
- log13.warn(
5592
+ log12.warn(
5087
5593
  {
5088
5594
  sessionId,
5089
5595
  fileName: attachment.fileName,
@@ -5127,7 +5633,7 @@ Task completed.
5127
5633
  );
5128
5634
  }
5129
5635
  } catch (err) {
5130
- log13.error(
5636
+ log12.error(
5131
5637
  { err, sessionId, fileName: attachment.fileName },
5132
5638
  "Failed to send attachment"
5133
5639
  );
@@ -5135,38 +5641,26 @@ Task completed.
5135
5641
  }
5136
5642
  async handleSessionEnd(sessionId, _content) {
5137
5643
  const threadId = this.getThreadId(sessionId);
5138
- await this.draftManager.finalize(
5139
- sessionId,
5140
- this.assistantSession?.id
5141
- );
5644
+ await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5142
5645
  this.draftManager.cleanup(sessionId);
5143
- this.toolTracker.cleanup(sessionId);
5144
5646
  await this.skillManager.cleanup(sessionId);
5145
5647
  const tracker = this.sessionTrackers.get(sessionId);
5146
5648
  if (tracker) {
5147
- await tracker.onComplete();
5148
- tracker.destroy();
5649
+ await tracker.cleanup();
5149
5650
  this.sessionTrackers.delete(sessionId);
5150
5651
  } else {
5151
5652
  await this.sendQueue.enqueue(
5152
- () => this.bot.api.sendMessage(
5153
- this.telegramConfig.chatId,
5154
- `\u2705 <b>Done</b>`,
5155
- {
5156
- message_thread_id: threadId,
5157
- parse_mode: "HTML",
5158
- disable_notification: true
5159
- }
5160
- )
5653
+ () => this.bot.api.sendMessage(this.telegramConfig.chatId, `\u2705 <b>Done</b>`, {
5654
+ message_thread_id: threadId,
5655
+ parse_mode: "HTML",
5656
+ disable_notification: true
5657
+ })
5161
5658
  );
5162
5659
  }
5163
5660
  }
5164
5661
  async handleError(sessionId, content) {
5165
5662
  const threadId = this.getThreadId(sessionId);
5166
- await this.draftManager.finalize(
5167
- sessionId,
5168
- this.assistantSession?.id
5169
- );
5663
+ await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5170
5664
  const tracker = this.sessionTrackers.get(sessionId);
5171
5665
  if (tracker) {
5172
5666
  tracker.destroy();
@@ -5199,7 +5693,7 @@ Task completed.
5199
5693
  );
5200
5694
  }
5201
5695
  async sendPermissionRequest(sessionId, request) {
5202
- log13.info({ sessionId, requestId: request.id }, "Permission request sent");
5696
+ log12.info({ sessionId, requestId: request.id }, "Permission request sent");
5203
5697
  const session = this.core.sessionManager.getSession(sessionId);
5204
5698
  if (!session) return;
5205
5699
  await this.sendQueue.enqueue(
@@ -5208,7 +5702,7 @@ Task completed.
5208
5702
  }
5209
5703
  async sendNotification(notification) {
5210
5704
  if (notification.sessionId === this.assistantSession?.id) return;
5211
- log13.info(
5705
+ log12.info(
5212
5706
  { sessionId: notification.sessionId, type: notification.type },
5213
5707
  "Notification sent"
5214
5708
  );
@@ -5246,7 +5740,7 @@ Task completed.
5246
5740
  );
5247
5741
  }
5248
5742
  async createSessionThread(sessionId, name) {
5249
- log13.info({ sessionId, name }, "Session topic created");
5743
+ log12.info({ sessionId, name }, "Session topic created");
5250
5744
  return String(
5251
5745
  await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
5252
5746
  );
@@ -5270,7 +5764,7 @@ Task completed.
5270
5764
  try {
5271
5765
  await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
5272
5766
  } catch (err) {
5273
- log13.warn(
5767
+ log12.warn(
5274
5768
  { err, sessionId, topicId },
5275
5769
  "Failed to delete forum topic (may already be deleted)"
5276
5770
  );
@@ -5300,7 +5794,7 @@ Task completed.
5300
5794
  const buffer = Buffer.from(await response.arrayBuffer());
5301
5795
  return { buffer, filePath: file.file_path };
5302
5796
  } catch (err) {
5303
- log13.error({ err }, "Failed to download file from Telegram");
5797
+ log12.error({ err }, "Failed to download file from Telegram");
5304
5798
  return null;
5305
5799
  }
5306
5800
  }
@@ -5321,7 +5815,7 @@ Task completed.
5321
5815
  try {
5322
5816
  buffer = await this.fileService.convertOggToWav(buffer);
5323
5817
  } catch (err) {
5324
- log13.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
5818
+ log12.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
5325
5819
  fileName = "voice.ogg";
5326
5820
  mimeType = "audio/ogg";
5327
5821
  originalFilePath = void 0;
@@ -5352,21 +5846,23 @@ Task completed.
5352
5846
  userId: String(userId),
5353
5847
  text,
5354
5848
  attachments: [att]
5355
- }).catch((err) => log13.error({ err }, "handleMessage error"));
5849
+ }).catch((err) => log12.error({ err }, "handleMessage error"));
5356
5850
  }
5357
5851
  async cleanupSkillCommands(sessionId) {
5358
5852
  await this.skillManager.cleanup(sessionId);
5359
5853
  }
5854
+ async stripTTSBlock(sessionId) {
5855
+ await this.draftManager.stripPattern(sessionId, /\[TTS\][\s\S]*?\[\/TTS\]/g);
5856
+ }
5360
5857
  async archiveSessionTopic(sessionId) {
5361
5858
  const core = this.core;
5362
5859
  const session = core.sessionManager.getSession(sessionId);
5363
- if (!session) return;
5860
+ if (!session) throw new Error("Session not found");
5364
5861
  const chatId = this.telegramConfig.chatId;
5365
5862
  const oldTopicId = Number(session.threadId);
5366
5863
  session.archiving = true;
5367
5864
  await this.draftManager.finalize(session.id, this.assistantSession?.id);
5368
5865
  this.draftManager.cleanup(session.id);
5369
- this.toolTracker.cleanup(session.id);
5370
5866
  await this.skillManager.cleanup(session.id);
5371
5867
  const tracker = this.sessionTrackers.get(session.id);
5372
5868
  if (tracker) {
@@ -5374,13 +5870,29 @@ Task completed.
5374
5870
  this.sessionTrackers.delete(session.id);
5375
5871
  }
5376
5872
  await deleteSessionTopic(this.bot, chatId, oldTopicId);
5873
+ const topicName = session.name ?? `Session ${session.id.slice(0, 6)}`;
5874
+ const newTopicId = await createSessionTopic(this.bot, chatId, topicName);
5875
+ session.archiving = false;
5876
+ return String(newTopicId);
5377
5877
  }
5378
5878
  };
5379
5879
 
5380
5880
  export {
5881
+ progressBar,
5882
+ formatTokens,
5883
+ stripCodeFences,
5884
+ truncateContent,
5885
+ splitMessage,
5886
+ STATUS_ICONS,
5887
+ KIND_ICONS,
5888
+ extractContentText,
5889
+ formatToolSummary,
5890
+ formatToolTitle,
5891
+ resolveToolIcon,
5381
5892
  PRODUCT_GUIDE,
5382
5893
  SendQueue,
5383
- ToolCallTracker,
5894
+ MessagingAdapter,
5895
+ BaseRenderer,
5384
5896
  TelegramAdapter
5385
5897
  };
5386
- //# sourceMappingURL=chunk-LP45RCA4.js.map
5898
+ //# sourceMappingURL=chunk-RXMWJHWH.js.map