@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
@@ -7,21 +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
- evaluateNoise,
15
- extractContentText,
16
- formatTokens,
17
- formatToolSummary,
18
- formatToolTitle,
19
- progressBar,
20
- resolveToolIcon,
21
- splitMessage,
22
- stripCodeFences,
23
- truncateContent
24
- } from "./chunk-32LVIEPW.js";
10
+ } from "./chunk-GEOXPGCO.js";
25
11
  import {
26
12
  CheckpointReader,
27
13
  DEFAULT_MAX_TOKENS
@@ -31,10 +17,10 @@ import {
31
17
  getSafeFields,
32
18
  isHotReloadable,
33
19
  resolveOptions
34
- } from "./chunk-ODUM3D6X.js";
20
+ } from "./chunk-5HKQCYOI.js";
35
21
  import {
36
22
  createChildLogger
37
- } from "./chunk-XMMAGAT4.js";
23
+ } from "./chunk-R6KZYF7D.js";
38
24
 
39
25
  // src/plugins/telegram/adapter.ts
40
26
  import { Bot, InputFile } from "grammy";
@@ -79,6 +65,266 @@ function buildDeepLink(chatId, threadId, messageId) {
79
65
  // src/plugins/telegram/commands/new-session.ts
80
66
  import { InlineKeyboard as InlineKeyboard2 } from "grammy";
81
67
 
68
+ // src/core/adapter-primitives/format-types.ts
69
+ var STATUS_ICONS = {
70
+ pending: "\u23F3",
71
+ in_progress: "\u{1F504}",
72
+ completed: "\u2705",
73
+ failed: "\u274C",
74
+ cancelled: "\u{1F6AB}",
75
+ running: "\u{1F504}",
76
+ done: "\u2705",
77
+ error: "\u274C"
78
+ };
79
+ var KIND_ICONS = {
80
+ read: "\u{1F4D6}",
81
+ edit: "\u270F\uFE0F",
82
+ write: "\u270F\uFE0F",
83
+ delete: "\u{1F5D1}\uFE0F",
84
+ execute: "\u25B6\uFE0F",
85
+ command: "\u25B6\uFE0F",
86
+ bash: "\u25B6\uFE0F",
87
+ terminal: "\u25B6\uFE0F",
88
+ search: "\u{1F50D}",
89
+ web: "\u{1F310}",
90
+ fetch: "\u{1F310}",
91
+ agent: "\u{1F9E0}",
92
+ think: "\u{1F9E0}",
93
+ install: "\u{1F4E6}",
94
+ move: "\u{1F4E6}",
95
+ other: "\u{1F6E0}\uFE0F"
96
+ };
97
+ var KIND_LABELS = {
98
+ read: "Read",
99
+ edit: "Edit",
100
+ write: "Write",
101
+ delete: "Delete",
102
+ execute: "Run",
103
+ bash: "Bash",
104
+ command: "Run",
105
+ terminal: "Terminal",
106
+ search: "Search",
107
+ web: "Web",
108
+ fetch: "Fetch",
109
+ agent: "Agent",
110
+ think: "Agent",
111
+ install: "Install",
112
+ move: "Move"
113
+ };
114
+
115
+ // src/core/adapter-primitives/format-utils.ts
116
+ function progressBar(ratio, length = 10) {
117
+ const filled = Math.round(Math.min(ratio, 1) * length);
118
+ return "\u2593".repeat(filled) + "\u2591".repeat(length - filled);
119
+ }
120
+ function formatTokens(n) {
121
+ if (n >= 1e6) {
122
+ const m = n / 1e6;
123
+ return m % 1 === 0 ? `${m}M` : `${parseFloat(m.toFixed(1))}M`;
124
+ }
125
+ if (n >= 1e3) {
126
+ const k = n / 1e3;
127
+ return k % 1 === 0 ? `${k}k` : `${parseFloat(k.toFixed(1))}k`;
128
+ }
129
+ return String(n);
130
+ }
131
+ function stripCodeFences(text) {
132
+ return text.replace(/```\w*\n?/g, "").replace(/```$/gm, "").trim();
133
+ }
134
+ function truncateContent(text, maxLen) {
135
+ if (text.length <= maxLen) return text;
136
+ return text.slice(0, maxLen) + "\n\u2026 (truncated)";
137
+ }
138
+ function splitMessage(text, maxLength) {
139
+ if (text.length <= maxLength) return [text];
140
+ const chunks = [];
141
+ let remaining = text;
142
+ while (remaining.length > 0) {
143
+ if (remaining.length <= maxLength) {
144
+ chunks.push(remaining);
145
+ break;
146
+ }
147
+ const wouldLeaveSmall = remaining.length < maxLength * 1.3;
148
+ const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
149
+ const threshold = maxLength * 0.2;
150
+ let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
151
+ if (splitAt === -1 || splitAt < threshold) {
152
+ splitAt = remaining.lastIndexOf("\n", searchLimit);
153
+ }
154
+ if (splitAt === -1 || splitAt < threshold) {
155
+ splitAt = searchLimit;
156
+ }
157
+ const candidate = remaining.slice(0, splitAt);
158
+ const fences = candidate.match(/```/g);
159
+ if (fences && fences.length % 2 !== 0) {
160
+ const closingFence = remaining.indexOf("```", splitAt);
161
+ if (closingFence !== -1) {
162
+ const afterFence = remaining.indexOf("\n", closingFence + 3);
163
+ const fenceSplit = afterFence !== -1 ? afterFence + 1 : closingFence + 3;
164
+ if (fenceSplit <= maxLength * 2) {
165
+ splitAt = fenceSplit;
166
+ }
167
+ }
168
+ }
169
+ chunks.push(remaining.slice(0, splitAt));
170
+ remaining = remaining.slice(splitAt).replace(/^\n+/, "");
171
+ }
172
+ return chunks;
173
+ }
174
+
175
+ // src/core/adapter-primitives/message-formatter.ts
176
+ function extractContentText(content, depth = 0) {
177
+ if (!content || depth > 5) return "";
178
+ if (typeof content === "string") return content;
179
+ if (Array.isArray(content)) {
180
+ return content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
181
+ }
182
+ if (typeof content !== "object") return String(content);
183
+ const obj = content;
184
+ if (obj.text && typeof obj.text === "string") return obj.text;
185
+ if (obj.content) {
186
+ if (typeof obj.content === "string") return obj.content;
187
+ if (Array.isArray(obj.content)) {
188
+ return obj.content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
189
+ }
190
+ return extractContentText(obj.content, depth + 1);
191
+ }
192
+ if (obj.input) return extractContentText(obj.input, depth + 1);
193
+ if (obj.output) return extractContentText(obj.output, depth + 1);
194
+ const keys = Object.keys(obj).filter((k) => k !== "type");
195
+ if (keys.length === 0) return "";
196
+ try {
197
+ return JSON.stringify(obj, null, 2);
198
+ } catch {
199
+ return "";
200
+ }
201
+ }
202
+ function parseRawInput(rawInput) {
203
+ try {
204
+ if (typeof rawInput === "string") {
205
+ return JSON.parse(rawInput);
206
+ }
207
+ if (typeof rawInput === "object" && rawInput !== null) {
208
+ return rawInput;
209
+ }
210
+ } catch {
211
+ }
212
+ return {};
213
+ }
214
+ function formatToolSummary(name, rawInput, displaySummary) {
215
+ if (displaySummary && typeof displaySummary === "string") {
216
+ return displaySummary;
217
+ }
218
+ const args = parseRawInput(rawInput);
219
+ const lowerName = name.toLowerCase();
220
+ if (lowerName === "read") {
221
+ const fp = args.file_path ?? args.filePath ?? "";
222
+ const limit = args.limit ? ` (${args.limit} lines)` : "";
223
+ return fp ? `\u{1F4D6} Read ${fp}${limit}` : `\u{1F527} ${name}`;
224
+ }
225
+ if (lowerName === "edit") {
226
+ const fp = args.file_path ?? args.filePath ?? "";
227
+ return fp ? `\u270F\uFE0F Edit ${fp}` : `\u{1F527} ${name}`;
228
+ }
229
+ if (lowerName === "write") {
230
+ const fp = args.file_path ?? args.filePath ?? "";
231
+ return fp ? `\u{1F4DD} Write ${fp}` : `\u{1F527} ${name}`;
232
+ }
233
+ if (lowerName === "bash" || lowerName === "terminal") {
234
+ const cmd = String(args.command ?? args.cmd ?? "").slice(0, 60);
235
+ return cmd ? `\u25B6\uFE0F Run: ${cmd}` : `\u25B6\uFE0F Terminal`;
236
+ }
237
+ if (lowerName === "grep") {
238
+ const pattern = args.pattern ?? "";
239
+ const path2 = args.path ?? "";
240
+ return pattern ? `\u{1F50D} Grep "${pattern}"${path2 ? ` in ${path2}` : ""}` : `\u{1F527} ${name}`;
241
+ }
242
+ if (lowerName === "glob") {
243
+ const pattern = args.pattern ?? "";
244
+ return pattern ? `\u{1F50D} Glob ${pattern}` : `\u{1F527} ${name}`;
245
+ }
246
+ if (lowerName === "agent") {
247
+ const desc = String(args.description ?? "").slice(0, 60);
248
+ return desc ? `\u{1F9E0} Agent: ${desc}` : `\u{1F527} ${name}`;
249
+ }
250
+ if (lowerName === "webfetch" || lowerName === "web_fetch") {
251
+ const url = String(args.url ?? "").slice(0, 60);
252
+ return url ? `\u{1F310} Fetch ${url}` : `\u{1F527} ${name}`;
253
+ }
254
+ if (lowerName === "websearch" || lowerName === "web_search") {
255
+ const query = String(args.query ?? "").slice(0, 60);
256
+ return query ? `\u{1F310} Search "${query}"` : `\u{1F527} ${name}`;
257
+ }
258
+ return `\u{1F527} ${name}`;
259
+ }
260
+ function formatToolTitle(name, rawInput, displayTitle) {
261
+ if (displayTitle && typeof displayTitle === "string") {
262
+ return displayTitle;
263
+ }
264
+ const args = parseRawInput(rawInput);
265
+ const lowerName = name.toLowerCase();
266
+ if (["read", "edit", "write"].includes(lowerName)) {
267
+ return String(args.file_path ?? args.filePath ?? name);
268
+ }
269
+ if (lowerName === "bash" || lowerName === "terminal") {
270
+ return String(args.command ?? args.cmd ?? name).slice(0, 60);
271
+ }
272
+ if (lowerName === "grep") {
273
+ const pattern = args.pattern ?? "";
274
+ const path2 = args.path ?? "";
275
+ return pattern ? `"${pattern}"${path2 ? ` in ${path2}` : ""}` : name;
276
+ }
277
+ if (lowerName === "glob") {
278
+ return String(args.pattern ?? name);
279
+ }
280
+ if (lowerName === "agent") {
281
+ return String(args.description ?? name).slice(0, 60);
282
+ }
283
+ if (["webfetch", "web_fetch"].includes(lowerName)) {
284
+ return String(args.url ?? name).slice(0, 60);
285
+ }
286
+ if (["websearch", "web_search"].includes(lowerName)) {
287
+ return String(args.query ?? name).slice(0, 60);
288
+ }
289
+ return name;
290
+ }
291
+ function resolveToolIcon(tool) {
292
+ const statusIcon = STATUS_ICONS[tool.status || ""];
293
+ if (statusIcon) return statusIcon;
294
+ const kind = tool.displayKind ?? tool.kind;
295
+ if (kind && KIND_ICONS[kind]) return KIND_ICONS[kind];
296
+ return "\u{1F527}";
297
+ }
298
+ var NOISE_RULES = [
299
+ {
300
+ match: (name) => name.toLowerCase() === "ls",
301
+ action: "hide"
302
+ },
303
+ {
304
+ match: (_name, kind, rawInput) => {
305
+ if (kind !== "read") return false;
306
+ const args = parseRawInput(rawInput);
307
+ const p = String(args.file_path ?? args.filePath ?? args.path ?? "");
308
+ return p.endsWith("/");
309
+ },
310
+ action: "hide"
311
+ },
312
+ {
313
+ match: (name) => name.toLowerCase() === "glob",
314
+ action: "hide"
315
+ },
316
+ {
317
+ match: (name) => name.toLowerCase() === "grep",
318
+ action: "hide"
319
+ }
320
+ ];
321
+ function evaluateNoise(name, kind, rawInput) {
322
+ for (const rule of NOISE_RULES) {
323
+ if (rule.match(name, kind, rawInput)) return rule.action;
324
+ }
325
+ return null;
326
+ }
327
+
82
328
  // src/plugins/telegram/formatting.ts
83
329
  function escapeHtml(text) {
84
330
  if (!text) return "";
@@ -171,23 +417,16 @@ function formatPlan(plan) {
171
417
  return `<b>Plan:</b>
172
418
  ${lines.join("\n")}`;
173
419
  }
174
- function formatUsage(usage, verbosity = "medium") {
175
- const { tokensUsed, contextSize, cost } = usage;
420
+ function formatUsage(usage, _verbosity = "medium") {
421
+ const { tokensUsed, contextSize } = usage;
176
422
  if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
177
- if (verbosity === "medium") {
178
- const costStr = cost != null ? ` \xB7 $${cost.toFixed(2)}` : "";
179
- return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens${costStr}`;
180
- }
181
423
  if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
182
424
  const ratio = tokensUsed / contextSize;
183
425
  const pct = Math.round(ratio * 100);
184
426
  const bar = progressBar(ratio);
185
427
  const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
186
- let text = `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
428
+ return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
187
429
  ${bar} ${pct}%`;
188
- if (cost != null) text += `
189
- \u{1F4B0} $${cost.toFixed(2)}`;
190
- return text;
191
430
  }
192
431
  function splitMessage2(text, maxLength = 3800) {
193
432
  return splitMessage(text, maxLength);
@@ -197,61 +436,105 @@ function renderToolCard(snap) {
197
436
  const { totalVisible, completedVisible, allComplete } = snap;
198
437
  const headerCheck = allComplete ? " \u2705" : "";
199
438
  if (totalVisible > 0) {
200
- sections.push(
201
- `<b>\u{1F4CB} Tools (${completedVisible}/${totalVisible})</b>${headerCheck}`
202
- );
439
+ sections.push(`<b>\u{1F4CB} Tools (${completedVisible}/${totalVisible})</b>${headerCheck}`);
203
440
  }
204
- const visible = snap.entries.filter((e) => !e.hidden);
205
- const completed = visible.filter(
206
- (e) => e.status === "completed" || e.status === "done" || e.status === "failed"
207
- );
208
- const running = visible.filter(
209
- (e) => e.status !== "completed" && e.status !== "done" && e.status !== "failed"
210
- );
211
- for (const entry of completed) {
212
- let line = `${entry.icon} ${escapeHtml(entry.label)}`;
213
- if (entry.viewerLinks) {
214
- const links = [];
215
- const fileName = entry.viewerFilePath?.split("/").pop() ?? "";
216
- if (entry.viewerLinks.file)
217
- links.push(
218
- `\u{1F4C4} <a href="${escapeHtml(entry.viewerLinks.file)}">View ${escapeHtml(fileName || "file")}</a>`
219
- );
220
- if (entry.viewerLinks.diff)
221
- links.push(
222
- `\u{1F4DD} <a href="${escapeHtml(entry.viewerLinks.diff)}">View diff</a>`
223
- );
224
- if (links.length > 0) line += `
225
- ${links.join(" \xB7 ")}`;
226
- }
227
- sections.push(line);
441
+ const DONE = /* @__PURE__ */ new Set(["completed", "done", "failed", "error"]);
442
+ const visible = snap.specs.filter((s) => !s.isHidden);
443
+ const completed = visible.filter((s) => DONE.has(s.status));
444
+ const running = visible.filter((s) => !DONE.has(s.status));
445
+ for (const spec of completed) {
446
+ sections.push(renderSpecSection(spec));
228
447
  }
229
448
  if (snap.planEntries && snap.planEntries.length > 0) {
230
- const planDone = snap.planEntries.filter(
231
- (e) => e.status === "completed"
232
- ).length;
449
+ const planDone = snap.planEntries.filter((e) => e.status === "completed").length;
233
450
  const planTotal = snap.planEntries.length;
234
451
  sections.push(`\u2500\u2500 Plan: ${planDone}/${planTotal} \u2500\u2500`);
235
- const statusIcon = {
236
- completed: "\u2705",
237
- in_progress: "\u{1F504}",
238
- pending: "\u2B1C"
239
- };
452
+ const statusIcon = { completed: "\u2705", in_progress: "\u{1F504}", pending: "\u2B1C" };
240
453
  for (let i = 0; i < snap.planEntries.length; i++) {
241
454
  const e = snap.planEntries[i];
242
- const icon = statusIcon[e.status] ?? "\u2B1C";
243
- sections.push(`${icon} ${i + 1}. ${escapeHtml(e.content)}`);
455
+ sections.push(`${statusIcon[e.status] ?? "\u2B1C"} ${i + 1}. ${escapeHtml(e.content)}`);
244
456
  }
245
457
  sections.push("\u2500\u2500\u2500\u2500");
246
458
  }
247
- for (const entry of running) {
248
- sections.push(`${entry.icon} ${escapeHtml(entry.label)}`);
459
+ for (const spec of running) {
460
+ sections.push(renderSpecSection(spec));
249
461
  }
250
- if (snap.usage?.tokensUsed != null) {
251
- if (sections.length > 0) sections.push("\u2500\u2500\u2500");
252
- sections.push(formatUsage(snap.usage, snap.verbosity));
462
+ return sections.join("\n\n");
463
+ }
464
+ var FILE_KINDS = /* @__PURE__ */ new Set(["read", "edit", "write", "delete"]);
465
+ function shortenTitle(title, kind) {
466
+ if (!FILE_KINDS.has(kind) || !title.includes("/")) return title;
467
+ const parenIdx = title.indexOf(" (");
468
+ const pathPart = parenIdx > 0 ? title.slice(0, parenIdx) : title;
469
+ const rangePart = parenIdx > 0 ? title.slice(parenIdx) : "";
470
+ const fileName = pathPart.split("/").pop() || pathPart;
471
+ return fileName + rangePart;
472
+ }
473
+ function renderSpecSection(spec) {
474
+ const lines = [];
475
+ const DONE = /* @__PURE__ */ new Set(["completed", "done", "failed", "error"]);
476
+ const statusPrefix = spec.status === "error" || spec.status === "failed" ? "\u274C " : DONE.has(spec.status) ? "\u2705 " : "\u{1F504} ";
477
+ const kindLabel = KIND_LABELS[spec.kind];
478
+ const displayTitle = shortenTitle(spec.title, spec.kind);
479
+ const hasUniqueTitle = displayTitle && displayTitle.toLowerCase() !== kindLabel?.toLowerCase() && displayTitle.toLowerCase() !== spec.kind;
480
+ let titleLine;
481
+ if (kindLabel) {
482
+ titleLine = hasUniqueTitle ? `${statusPrefix}${spec.icon} <b>${kindLabel}</b> \xB7 ${escapeHtml(displayTitle)}` : `${statusPrefix}${spec.icon} <b>${kindLabel}</b>`;
483
+ } else {
484
+ titleLine = `${statusPrefix}${spec.icon} ${escapeHtml(displayTitle)}`;
485
+ }
486
+ if (spec.diffStats) {
487
+ const { added, removed } = spec.diffStats;
488
+ if (added > 0 && removed > 0) titleLine += ` \xB7 <i>+${added}/-${removed} lines</i>`;
489
+ else if (added > 0) titleLine += ` \xB7 <i>+${added} lines</i>`;
490
+ else if (removed > 0) titleLine += ` \xB7 <i>-${removed} lines</i>`;
491
+ }
492
+ lines.push(titleLine);
493
+ if (spec.description) lines.push(` <i>${escapeHtml(spec.description)}</i>`);
494
+ if (spec.command) lines.push(` <code>${escapeHtml(spec.command)}</code>`);
495
+ if (spec.inputContent) {
496
+ const truncated = spec.inputContent.length > 800 ? spec.inputContent.slice(0, 797) + "\u2026" : spec.inputContent;
497
+ lines.push(` <pre><code>${escapeHtml(truncated)}</code></pre>`);
498
+ }
499
+ if (spec.outputSummary) lines.push(` \xB7 ${escapeHtml(spec.outputSummary)}`);
500
+ if (spec.outputContent || spec.outputFallbackContent) {
501
+ const raw = spec.outputContent ?? spec.outputFallbackContent;
502
+ const truncated = raw.length > 800 ? raw.slice(0, 797) + "\u2026" : raw;
503
+ lines.push(` <pre><code>${escapeHtml(truncated)}</code></pre>`);
504
+ }
505
+ if (spec.viewerLinks?.file || spec.viewerLinks?.diff || spec.outputViewerLink) {
506
+ const linkParts = [];
507
+ const shortName = displayTitle || kindLabel || spec.kind;
508
+ if (spec.viewerLinks?.file)
509
+ linkParts.push(`<a href="${escapeHtml(spec.viewerLinks.file)}">View ${escapeHtml(shortName)}</a>`);
510
+ if (spec.viewerLinks?.diff)
511
+ linkParts.push(`<a href="${escapeHtml(spec.viewerLinks.diff)}">View diff</a>`);
512
+ if (spec.outputViewerLink)
513
+ linkParts.push(`<a href="${escapeHtml(spec.outputViewerLink)}">View output</a>`);
514
+ lines.push(` ${linkParts.join(" \xB7 ")}`);
515
+ }
516
+ return lines.join("\n");
517
+ }
518
+ var TELEGRAM_MAX_LENGTH = 4096;
519
+ function splitToolCardText(text) {
520
+ if (text.length <= TELEGRAM_MAX_LENGTH) return [text];
521
+ const sections = text.split("\n\n");
522
+ const chunks = [];
523
+ let current = "";
524
+ for (const section of sections) {
525
+ const safeSection = section.length > TELEGRAM_MAX_LENGTH ? section.slice(0, TELEGRAM_MAX_LENGTH - 3) + "..." : section;
526
+ const candidate = current ? `${current}
527
+
528
+ ${safeSection}` : safeSection;
529
+ if (candidate.length > TELEGRAM_MAX_LENGTH && current) {
530
+ chunks.push(current);
531
+ current = safeSection;
532
+ } else {
533
+ current = candidate;
534
+ }
253
535
  }
254
- return sections.join("\n");
536
+ if (current) chunks.push(current);
537
+ return chunks;
255
538
  }
256
539
 
257
540
  // src/plugins/telegram/commands/admin.ts
@@ -440,6 +723,15 @@ function setupTTSCallbacks(bot, core) {
440
723
  }
441
724
  return;
442
725
  }
726
+ if (session.voiceMode !== "on" && !core.speechService?.isTTSAvailable()) {
727
+ try {
728
+ await ctx.answerCallbackQuery({
729
+ text: "\u26A0\uFE0F TTS provider not installed. Use /tts install to set up."
730
+ });
731
+ } catch {
732
+ }
733
+ return;
734
+ }
443
735
  const newMode = session.voiceMode === "on" ? "off" : "on";
444
736
  session.setVoiceMode(newMode);
445
737
  const toastText = newMode === "on" ? "\u{1F50A} Text to Speech enabled" : "\u{1F507} Text to Speech disabled";
@@ -476,6 +768,15 @@ async function handleTTS(ctx, core) {
476
768
  }
477
769
  const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
478
770
  const arg = args[0]?.toLowerCase();
771
+ if (arg === "on" || !arg) {
772
+ if (!core.speechService?.isTTSAvailable()) {
773
+ await ctx.reply(
774
+ "\u26A0\uFE0F TTS provider not installed.\n\nUse <code>/tts install</code> to install Edge TTS plugin.",
775
+ { parse_mode: "HTML" }
776
+ );
777
+ return;
778
+ }
779
+ }
479
780
  if (arg === "on") {
480
781
  session.setVoiceMode("on");
481
782
  await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", {
@@ -491,33 +792,75 @@ async function handleTTS(ctx, core) {
491
792
  });
492
793
  }
493
794
  }
494
- var VERBOSITY_LABELS = {
795
+ async function handleVerbosity(ctx, core) {
796
+ await ctx.reply("\u26A0\uFE0F <code>/verbosity</code> is deprecated. Use <code>/outputmode</code> instead.", { parse_mode: "HTML" });
797
+ await handleOutputMode(ctx, core);
798
+ }
799
+ var OUTPUT_MODE_LABELS = {
495
800
  low: "\u{1F507} Low",
496
801
  medium: "\u{1F4CA} Medium",
497
- high: "\u{1F4D6} High"
802
+ high: "\u{1F50D} High"
498
803
  };
499
- async function handleVerbosity(ctx, core) {
804
+ async function handleOutputMode(ctx, core) {
500
805
  const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
501
- const arg = args[0]?.toLowerCase();
502
- if (arg === "low" || arg === "medium" || arg === "high") {
806
+ const arg0 = args[0]?.toLowerCase();
807
+ const arg1 = args[1]?.toLowerCase();
808
+ if (arg0 === "session") {
809
+ const chatId = ctx.chat?.id;
810
+ const threadId = ctx.message?.message_thread_id;
811
+ if (!chatId || threadId === void 0) {
812
+ await ctx.reply("\u26A0\uFE0F This command must be used in a session topic.", { parse_mode: "HTML" });
813
+ return;
814
+ }
815
+ const session = core.sessionManager.getSessionByThread(
816
+ "telegram",
817
+ String(threadId)
818
+ );
819
+ if (!session) {
820
+ await ctx.reply("\u26A0\uFE0F No active session found for this topic.", { parse_mode: "HTML" });
821
+ return;
822
+ }
823
+ if (arg1 === "reset") {
824
+ await core.sessionManager.patchRecord(session.id, { outputMode: void 0 });
825
+ await ctx.reply("\u{1F504} Session output mode reset to adapter default.", { parse_mode: "HTML" });
826
+ } else if (arg1 === "low" || arg1 === "medium" || arg1 === "high") {
827
+ await core.sessionManager.patchRecord(session.id, { outputMode: arg1 });
828
+ await ctx.reply(
829
+ `${OUTPUT_MODE_LABELS[arg1]} Session output mode set to <b>${arg1}</b>.`,
830
+ { parse_mode: "HTML" }
831
+ );
832
+ } else {
833
+ const record = core.sessionManager.getSessionRecord(session.id);
834
+ const current = record?.outputMode ?? "(adapter default)";
835
+ await ctx.reply(
836
+ `\u{1F4CA} Session output mode: <b>${current}</b>
837
+
838
+ Usage: <code>/outputmode session low|medium|high|reset</code>`,
839
+ { parse_mode: "HTML" }
840
+ );
841
+ }
842
+ return;
843
+ }
844
+ if (arg0 === "low" || arg0 === "medium" || arg0 === "high") {
503
845
  await core.configManager.save(
504
- { channels: { telegram: { displayVerbosity: arg } } },
505
- "channels.telegram.displayVerbosity"
846
+ { channels: { telegram: { outputMode: arg0 } } },
847
+ "channels.telegram.outputMode"
506
848
  );
507
849
  await ctx.reply(
508
- `${VERBOSITY_LABELS[arg]} Display verbosity set to <b>${arg}</b>.`,
850
+ `${OUTPUT_MODE_LABELS[arg0]} Output mode set to <b>${arg0}</b>.`,
509
851
  { parse_mode: "HTML" }
510
852
  );
511
853
  } else {
512
- const current = core.configManager.get().channels?.telegram?.displayVerbosity ?? "medium";
854
+ const current = core.configManager.get().channels?.telegram?.outputMode ?? "medium";
513
855
  await ctx.reply(
514
- `\u{1F4CA} Current verbosity: <b>${current}</b>
856
+ `\u{1F4CA} Current output mode: <b>${current}</b>
515
857
 
516
- Usage: <code>/verbosity low|medium|high</code>
858
+ Usage: <code>/outputmode low|medium|high</code>
859
+ Session override: <code>/outputmode session low|medium|high|reset</code>
517
860
 
518
- \u2022 <b>low</b> \u2014 minimal output, title only
519
- \u2022 <b>medium</b> \u2014 balanced (default)
520
- \u2022 <b>high</b> \u2014 full detail with content`,
861
+ \u2022 <b>low</b> \u2014 minimal: title only
862
+ \u2022 <b>medium</b> \u2014 balanced: description + output summary (default)
863
+ \u2022 <b>high</b> \u2014 full detail: inline output, IN/OUT blocks`,
521
864
  { parse_mode: "HTML" }
522
865
  );
523
866
  }
@@ -527,12 +870,12 @@ function setupVerbosityCallbacks(bot, core) {
527
870
  const level = ctx.callbackQuery.data.slice(3);
528
871
  if (level !== "low" && level !== "medium" && level !== "high") return;
529
872
  await core.configManager.save(
530
- { channels: { telegram: { displayVerbosity: level } } },
531
- "channels.telegram.displayVerbosity"
873
+ { channels: { telegram: { outputMode: level } } },
874
+ "channels.telegram.outputMode"
532
875
  );
533
876
  try {
534
877
  await ctx.answerCallbackQuery({
535
- text: `${VERBOSITY_LABELS[level]} Verbosity: ${level}`
878
+ text: `${OUTPUT_MODE_LABELS[level]} Output mode: ${level}`
536
879
  });
537
880
  } catch {
538
881
  }
@@ -546,7 +889,7 @@ async function handleUpdate(ctx, core) {
546
889
  );
547
890
  return;
548
891
  }
549
- const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-NQZBM5M7.js");
892
+ const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-AXXV6IV2.js");
550
893
  const current = getCurrentVersion();
551
894
  const statusMsg = await ctx.reply(
552
895
  `\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`,
@@ -1290,9 +1633,22 @@ async function handleArchive(ctx, core) {
1290
1633
  if (!threadId) return;
1291
1634
  const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
1292
1635
  const record = !session ? core.sessionManager.getRecordByThread("telegram", String(threadId)) : void 0;
1293
- const identifier = session?.id ?? record?.sessionId ?? `topic:${threadId}`;
1636
+ if (!session && !record) {
1637
+ await ctx.reply("This topic is not linked to a session.", { parse_mode: "HTML" });
1638
+ return;
1639
+ }
1640
+ const identifier = session?.id ?? record?.sessionId;
1641
+ if (!identifier) {
1642
+ await ctx.reply("Could not determine session for this topic.", { parse_mode: "HTML" });
1643
+ return;
1644
+ }
1645
+ const status = session?.status ?? record?.status;
1646
+ if (status === "initializing") {
1647
+ await ctx.reply("Cannot archive a session that is still initializing. Wait for it to become active.", { parse_mode: "HTML" });
1648
+ return;
1649
+ }
1294
1650
  await ctx.reply(
1295
- "\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>",
1651
+ "\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>",
1296
1652
  {
1297
1653
  parse_mode: "HTML",
1298
1654
  reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${identifier}`).text("\u274C Cancel", `ar:no:${identifier}`)
@@ -1312,37 +1668,21 @@ async function handleArchiveConfirm(ctx, core, chatId) {
1312
1668
  await ctx.editMessageText("Archive cancelled.", { parse_mode: "HTML" });
1313
1669
  return;
1314
1670
  }
1315
- await ctx.editMessageText("\u{1F504} Archiving...", { parse_mode: "HTML" });
1316
- if (identifier.startsWith("topic:")) {
1317
- const topicId = Number(identifier.slice("topic:".length));
1318
- try {
1319
- await ctx.api.deleteForumTopic(chatId, topicId);
1320
- core.notificationManager.notifyAll({
1321
- sessionId: "system",
1322
- sessionName: `Orphan topic #${topicId}`,
1323
- type: "completed",
1324
- summary: `Orphan topic #${topicId} archived and deleted.`
1325
- });
1326
- } catch (err) {
1327
- core.notificationManager.notifyAll({
1328
- sessionId: "system",
1329
- sessionName: `Orphan topic #${topicId}`,
1330
- type: "error",
1331
- summary: `Failed to delete orphan topic #${topicId}: ${err.message}`
1332
- });
1333
- }
1334
- return;
1335
- }
1336
1671
  const result = await core.archiveSession(identifier);
1337
1672
  if (result.ok) {
1338
- core.notificationManager.notifyAll({
1339
- sessionId: identifier,
1340
- type: "completed",
1341
- summary: `Session archived and deleted.`
1342
- });
1673
+ const adapter = core.adapters.get("telegram");
1674
+ if (adapter) {
1675
+ try {
1676
+ await adapter.sendMessage(identifier, {
1677
+ type: "text",
1678
+ text: "Chat history cleared. Session is still active \u2014 send a message to continue."
1679
+ });
1680
+ } catch {
1681
+ }
1682
+ }
1343
1683
  } else {
1344
1684
  try {
1345
- await ctx.editMessageText(`\u274C Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
1685
+ await ctx.editMessageText(`Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
1346
1686
  } catch {
1347
1687
  core.notificationManager.notifyAll({
1348
1688
  sessionId: identifier,
@@ -1552,10 +1892,10 @@ Downloading... ${bar} ${percent}%`, { parse_mode: "HTML" });
1552
1892
  };
1553
1893
  const result = await catalog.install(nameOrId, progress);
1554
1894
  if (result.ok) {
1555
- const { getAgentCapabilities } = await import("./agent-dependencies-WS7Z2DFW.js");
1895
+ const { getAgentCapabilities } = await import("./agent-dependencies-ED2ZTUHG.js");
1556
1896
  const caps = getAgentCapabilities(result.agentKey);
1557
1897
  if (caps.integration) {
1558
- const { installIntegration } = await import("./integrate-G6CVXTGT.js");
1898
+ const { installIntegration } = await import("./integrate-JIEZYDOR.js");
1559
1899
  const intResult = await installIntegration(result.agentKey, caps.integration);
1560
1900
  if (intResult.success) {
1561
1901
  try {
@@ -1625,7 +1965,7 @@ function buildProgressBar(percent) {
1625
1965
  // src/plugins/telegram/commands/integrate.ts
1626
1966
  import { InlineKeyboard as InlineKeyboard5 } from "grammy";
1627
1967
  async function handleIntegrate(ctx, _core) {
1628
- const { listIntegrations } = await import("./integrate-G6CVXTGT.js");
1968
+ const { listIntegrations } = await import("./integrate-JIEZYDOR.js");
1629
1969
  const agents = listIntegrations();
1630
1970
  const keyboard = new InlineKeyboard5();
1631
1971
  for (const agent of agents) {
@@ -1658,7 +1998,7 @@ function setupIntegrateCallbacks(bot, core) {
1658
1998
  } catch {
1659
1999
  }
1660
2000
  if (data === "i:back") {
1661
- const { listIntegrations } = await import("./integrate-G6CVXTGT.js");
2001
+ const { listIntegrations } = await import("./integrate-JIEZYDOR.js");
1662
2002
  const agents = listIntegrations();
1663
2003
  const keyboard2 = new InlineKeyboard5();
1664
2004
  for (const agent of agents) {
@@ -1678,7 +2018,7 @@ Select an agent to manage its integrations.`,
1678
2018
  const agentMatch = data.match(/^i:agent:(.+)$/);
1679
2019
  if (agentMatch) {
1680
2020
  const agentName2 = agentMatch[1];
1681
- const { getIntegration: getIntegration2 } = await import("./integrate-G6CVXTGT.js");
2021
+ const { getIntegration: getIntegration2 } = await import("./integrate-JIEZYDOR.js");
1682
2022
  const integration2 = getIntegration2(agentName2);
1683
2023
  if (!integration2) {
1684
2024
  await ctx.reply(`\u274C No integration available for '${escapeHtml(agentName2)}'.`, { parse_mode: "HTML" });
@@ -1705,7 +2045,7 @@ ${integration2.items.map((i) => `\u2022 <b>${escapeHtml(i.name)}</b> \u2014 ${es
1705
2045
  const action = actionMatch[1];
1706
2046
  const agentName = actionMatch[2];
1707
2047
  const itemId = actionMatch[3];
1708
- const { getIntegration } = await import("./integrate-G6CVXTGT.js");
2048
+ const { getIntegration } = await import("./integrate-JIEZYDOR.js");
1709
2049
  const integration = getIntegration(agentName);
1710
2050
  if (!integration) return;
1711
2051
  const item = integration.items.find((i) => i.id === itemId);
@@ -2189,7 +2529,7 @@ Tap to change:`, {
2189
2529
  await ctx.answerCallbackQuery();
2190
2530
  } catch {
2191
2531
  }
2192
- const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-YDQ2LWAR.js");
2532
+ const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-ALFN37IR.js");
2193
2533
  try {
2194
2534
  await ctx.editMessageText(`<b>OpenACP Menu</b>
2195
2535
  Choose an action:`, {
@@ -2511,6 +2851,7 @@ function setupCommands(bot, core, chatId, assistant) {
2511
2851
  bot.command("archive", (ctx) => handleArchive(ctx, core));
2512
2852
  bot.command("text_to_speech", (ctx) => handleTTS(ctx, core));
2513
2853
  bot.command("verbosity", (ctx) => handleVerbosity(ctx, core));
2854
+ bot.command("outputmode", (ctx) => handleOutputMode(ctx, core));
2514
2855
  bot.command("resume", (ctx) => handleResume(ctx, core, chatId, assistant));
2515
2856
  }
2516
2857
  function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSession) {
@@ -2598,7 +2939,8 @@ var STATIC_COMMANDS = [
2598
2939
  { command: "tunnels", description: "List active tunnels" },
2599
2940
  { command: "archive", description: "Archive session topic (recreate with clean history)" },
2600
2941
  { command: "text_to_speech", description: "Toggle Text to Speech (/text_to_speech on, /text_to_speech off)" },
2601
- { command: "verbosity", description: "Set display verbosity (/verbosity low|medium|high)" },
2942
+ { command: "verbosity", description: "Deprecated: use /outputmode instead" },
2943
+ { command: "outputmode", description: "Control output display level (low/medium/high)" },
2602
2944
  { command: "resume", description: "Resume with conversation history from Entire checkpoints" }
2603
2945
  ];
2604
2946
 
@@ -3303,34 +3645,29 @@ function redirectToAssistant(chatId, assistantTopicId) {
3303
3645
 
3304
3646
  // src/core/adapter-primitives/primitives/tool-card-state.ts
3305
3647
  var DEBOUNCE_MS = 500;
3648
+ var DONE_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "failed", "error"]);
3306
3649
  var ToolCardState = class {
3307
- entries = [];
3650
+ specs = [];
3308
3651
  planEntries;
3309
3652
  usage;
3310
3653
  finalized = false;
3311
3654
  isFirstFlush = true;
3312
3655
  debounceTimer;
3313
- verbosity;
3314
3656
  onFlush;
3315
3657
  constructor(config) {
3316
- this.verbosity = config.verbosity;
3317
3658
  this.onFlush = config.onFlush;
3318
3659
  }
3319
- addTool(meta, kind, rawInput) {
3320
- if (this.finalized) return;
3321
- const hidden = this.verbosity !== "high" && evaluateNoise(meta.name, kind, rawInput) !== null;
3322
- const entry = {
3323
- id: meta.id,
3324
- name: meta.name,
3325
- kind,
3326
- status: meta.status ?? "running",
3327
- icon: resolveToolIcon({ status: meta.status ?? "running", kind }),
3328
- label: formatToolSummary(meta.name, rawInput, meta.displaySummary),
3329
- viewerLinks: meta.viewerLinks,
3330
- viewerFilePath: meta.viewerFilePath,
3331
- hidden
3332
- };
3333
- this.entries.push(entry);
3660
+ updateFromSpec(spec) {
3661
+ const existingIdx = this.specs.findIndex((s) => s.id === spec.id);
3662
+ if (existingIdx >= 0) {
3663
+ this.specs[existingIdx] = spec;
3664
+ } else {
3665
+ this.specs.push(spec);
3666
+ }
3667
+ if (this.finalized) {
3668
+ this.onFlush(this.snapshot());
3669
+ return;
3670
+ }
3334
3671
  if (this.isFirstFlush) {
3335
3672
  this.isFirstFlush = false;
3336
3673
  this.flush();
@@ -3338,20 +3675,10 @@ var ToolCardState = class {
3338
3675
  this.scheduleFlush();
3339
3676
  }
3340
3677
  }
3341
- updateTool(id, status, viewerLinks, viewerFilePath) {
3342
- if (this.finalized) return;
3343
- const entry = this.entries.find((e) => e.id === id);
3344
- if (!entry) return;
3345
- entry.status = status;
3346
- entry.icon = resolveToolIcon({ status, kind: entry.kind });
3347
- if (viewerLinks) entry.viewerLinks = viewerLinks;
3348
- if (viewerFilePath) entry.viewerFilePath = viewerFilePath;
3349
- this.scheduleFlush();
3350
- }
3351
3678
  updatePlan(entries) {
3352
3679
  if (this.finalized) return;
3353
3680
  this.planEntries = entries;
3354
- if (this.entries.length === 0 && this.isFirstFlush) {
3681
+ if (this.specs.length === 0 && this.isFirstFlush) {
3355
3682
  this.isFirstFlush = false;
3356
3683
  this.flush();
3357
3684
  } else {
@@ -3361,7 +3688,7 @@ var ToolCardState = class {
3361
3688
  appendUsage(usage) {
3362
3689
  if (this.finalized) return;
3363
3690
  this.usage = usage;
3364
- this.flush();
3691
+ this.scheduleFlush();
3365
3692
  }
3366
3693
  finalize() {
3367
3694
  if (this.finalized) return;
@@ -3374,23 +3701,19 @@ var ToolCardState = class {
3374
3701
  this.clearDebounce();
3375
3702
  }
3376
3703
  hasContent() {
3377
- return this.entries.length > 0 || this.planEntries !== void 0;
3704
+ return this.specs.length > 0 || this.planEntries !== void 0;
3378
3705
  }
3379
3706
  snapshot() {
3380
- const visible = this.entries.filter((e) => !e.hidden);
3381
- const completedVisible = visible.filter(
3382
- (e) => e.status === "completed" || e.status === "done"
3383
- ).length;
3707
+ const visible = this.specs.filter((s) => !s.isHidden);
3708
+ const completedVisible = visible.filter((s) => DONE_STATUSES.has(s.status)).length;
3384
3709
  const allComplete = visible.length > 0 && completedVisible === visible.length;
3385
3710
  return {
3386
- entries: this.entries,
3711
+ specs: this.specs,
3387
3712
  planEntries: this.planEntries,
3388
3713
  usage: this.usage,
3389
- visibleCount: visible.length,
3390
3714
  totalVisible: visible.length,
3391
3715
  completedVisible,
3392
- allComplete,
3393
- verbosity: this.verbosity
3716
+ allComplete
3394
3717
  };
3395
3718
  }
3396
3719
  flush() {
@@ -3412,37 +3735,283 @@ var ToolCardState = class {
3412
3735
  }
3413
3736
  };
3414
3737
 
3738
+ // src/core/adapter-primitives/stream-accumulator.ts
3739
+ var ToolStateMap = class {
3740
+ entries = /* @__PURE__ */ new Map();
3741
+ pendingUpdates = /* @__PURE__ */ new Map();
3742
+ /**
3743
+ * Creates or updates an entry from a tool_call event.
3744
+ * If a pending update exists for this id, applies it immediately.
3745
+ */
3746
+ upsert(meta, kind, rawInput) {
3747
+ const isNoise = evaluateNoise(meta.name, kind, rawInput) !== null;
3748
+ const entry = {
3749
+ id: meta.id,
3750
+ name: meta.name,
3751
+ kind,
3752
+ rawInput,
3753
+ content: null,
3754
+ status: meta.status ?? "running",
3755
+ viewerLinks: meta.viewerLinks,
3756
+ displaySummary: meta.displaySummary,
3757
+ displayTitle: meta.displayTitle,
3758
+ displayKind: meta.displayKind,
3759
+ isNoise
3760
+ };
3761
+ this.entries.set(meta.id, entry);
3762
+ const pending = this.pendingUpdates.get(meta.id);
3763
+ if (pending) {
3764
+ this.pendingUpdates.delete(meta.id);
3765
+ this._applyUpdate(entry, pending);
3766
+ }
3767
+ return entry;
3768
+ }
3769
+ /**
3770
+ * Updates an existing entry from a tool_call_update event.
3771
+ * If the entry doesn't exist yet (out-of-order delivery), buffers the update.
3772
+ */
3773
+ merge(id, status, rawInput, content, viewerLinks, diffStats) {
3774
+ const entry = this.entries.get(id);
3775
+ if (!entry) {
3776
+ this.pendingUpdates.set(id, { status, rawInput, content, viewerLinks, diffStats });
3777
+ return void 0;
3778
+ }
3779
+ this._applyUpdate(entry, { status, rawInput, content, viewerLinks, diffStats });
3780
+ return entry;
3781
+ }
3782
+ _applyUpdate(entry, update) {
3783
+ entry.status = update.status;
3784
+ if (update.rawInput !== void 0) {
3785
+ entry.rawInput = update.rawInput;
3786
+ }
3787
+ if (update.content !== void 0) {
3788
+ entry.content = update.content ?? null;
3789
+ }
3790
+ if (update.viewerLinks !== void 0) {
3791
+ entry.viewerLinks = update.viewerLinks;
3792
+ }
3793
+ if (update.diffStats !== void 0) {
3794
+ entry.diffStats = update.diffStats;
3795
+ }
3796
+ }
3797
+ get(id) {
3798
+ return this.entries.get(id);
3799
+ }
3800
+ clear() {
3801
+ this.entries.clear();
3802
+ this.pendingUpdates.clear();
3803
+ }
3804
+ };
3805
+ var ThoughtBuffer = class {
3806
+ chunks = [];
3807
+ sealed = false;
3808
+ append(chunk) {
3809
+ if (this.sealed) return;
3810
+ this.chunks.push(chunk);
3811
+ }
3812
+ seal() {
3813
+ this.sealed = true;
3814
+ return this.chunks.join("");
3815
+ }
3816
+ getText() {
3817
+ return this.chunks.join("");
3818
+ }
3819
+ isSealed() {
3820
+ return this.sealed;
3821
+ }
3822
+ reset() {
3823
+ this.chunks = [];
3824
+ this.sealed = false;
3825
+ }
3826
+ };
3827
+
3828
+ // src/core/adapter-primitives/display-spec-builder.ts
3829
+ var EXECUTE_KINDS = /* @__PURE__ */ new Set(["execute", "bash", "command", "terminal"]);
3830
+ var INLINE_MAX_LINES = 15;
3831
+ var INLINE_MAX_CHARS = 800;
3832
+ function asRecord(value) {
3833
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
3834
+ return value;
3835
+ }
3836
+ return {};
3837
+ }
3838
+ function capitalize(s) {
3839
+ return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
3840
+ }
3841
+ function buildTitle(entry, kind) {
3842
+ if (entry.displayTitle) return entry.displayTitle;
3843
+ if (entry.displaySummary) return entry.displaySummary;
3844
+ const input = asRecord(entry.rawInput);
3845
+ if (kind === "read") {
3846
+ const filePath = typeof input.file_path === "string" ? input.file_path : null;
3847
+ if (filePath) {
3848
+ const startLine = typeof input.start_line === "number" ? input.start_line : null;
3849
+ const endLine = typeof input.end_line === "number" ? input.end_line : null;
3850
+ if (startLine !== null && endLine !== null) return `${filePath} (lines ${startLine}\u2013${endLine})`;
3851
+ if (startLine !== null) return `${filePath} (from line ${startLine})`;
3852
+ const offset = typeof input.offset === "number" ? input.offset : null;
3853
+ const limit = typeof input.limit === "number" ? input.limit : null;
3854
+ if (offset !== null && limit !== null) return `${filePath} (lines ${offset}\u2013${offset + limit - 1})`;
3855
+ if (offset !== null) return `${filePath} (from line ${offset})`;
3856
+ return filePath;
3857
+ }
3858
+ return capitalize(entry.name);
3859
+ }
3860
+ if (kind === "edit" || kind === "write" || kind === "delete") {
3861
+ const filePath = typeof input.file_path === "string" ? input.file_path : typeof input.path === "string" ? input.path : null;
3862
+ if (filePath) return filePath;
3863
+ return capitalize(entry.name);
3864
+ }
3865
+ if (EXECUTE_KINDS.has(kind)) {
3866
+ const description = typeof input.description === "string" ? input.description : null;
3867
+ if (description) return description;
3868
+ const command = typeof input.command === "string" ? input.command : null;
3869
+ if (command) return command.length > 60 ? command.slice(0, 57) + "..." : command;
3870
+ return capitalize(entry.name);
3871
+ }
3872
+ if (kind === "agent") {
3873
+ const skill = typeof input.skill === "string" ? input.skill : null;
3874
+ const description = typeof input.description === "string" ? input.description : null;
3875
+ const subtype = typeof input.subagent_type === "string" ? input.subagent_type : null;
3876
+ if (skill) return skill;
3877
+ if (description) return description.length > 60 ? description.slice(0, 57) + "..." : description;
3878
+ if (subtype) return subtype;
3879
+ return capitalize(entry.name);
3880
+ }
3881
+ if (kind === "search") {
3882
+ const pattern = typeof input.pattern === "string" ? input.pattern : typeof input.query === "string" ? input.query : null;
3883
+ if (pattern) {
3884
+ let title = `${capitalize(entry.name)} "${pattern}"`;
3885
+ const glob = typeof input.glob === "string" ? input.glob : null;
3886
+ const type = typeof input.type === "string" ? input.type : null;
3887
+ if (glob) title += ` (glob: ${glob})`;
3888
+ else if (type) title += ` (type: ${type})`;
3889
+ return title;
3890
+ }
3891
+ return capitalize(entry.name);
3892
+ }
3893
+ if (entry.name.toLowerCase() === "skill" && typeof input.skill === "string" && input.skill) {
3894
+ return input.skill;
3895
+ }
3896
+ return entry.name;
3897
+ }
3898
+ function buildOutputSummary(content) {
3899
+ const lines = content.split("\n").length;
3900
+ return `${lines} line${lines === 1 ? "" : "s"} of output`;
3901
+ }
3902
+ function isTitleFromCommand(title, command) {
3903
+ return title === command || command.length > 60 && title === command.slice(0, 57) + "...";
3904
+ }
3905
+ var DisplaySpecBuilder = class {
3906
+ constructor(tunnelService) {
3907
+ this.tunnelService = tunnelService;
3908
+ }
3909
+ buildToolSpec(entry, mode, sessionContext) {
3910
+ const effectiveKind = entry.displayKind ?? entry.kind;
3911
+ const icon = KIND_ICONS[effectiveKind] ?? KIND_ICONS["other"] ?? "\u{1F6E0}\uFE0F";
3912
+ const title = buildTitle(entry, effectiveKind);
3913
+ const isHidden = entry.isNoise && mode !== "high";
3914
+ const includeMeta = mode !== "low";
3915
+ const input = asRecord(entry.rawInput);
3916
+ const rawDescription = typeof input.description === "string" ? input.description : null;
3917
+ const descLower = rawDescription?.toLowerCase();
3918
+ const description = includeMeta && rawDescription && rawDescription !== title && descLower !== effectiveKind && descLower !== entry.name.toLowerCase() ? rawDescription : null;
3919
+ const rawCommand = EXECUTE_KINDS.has(effectiveKind) && typeof input.command === "string" ? input.command : null;
3920
+ const command = includeMeta && rawCommand && !isTitleFromCommand(title, rawCommand) ? rawCommand : null;
3921
+ const inputContent = null;
3922
+ const content = entry.content;
3923
+ let outputSummary = null;
3924
+ let outputContent = null;
3925
+ let outputViewerLink = void 0;
3926
+ let outputFallbackContent = void 0;
3927
+ if (content && content.trim().length > 0 && includeMeta) {
3928
+ outputSummary = buildOutputSummary(content);
3929
+ const isLong = content.split("\n").length > INLINE_MAX_LINES || content.length > INLINE_MAX_CHARS;
3930
+ if (isLong) {
3931
+ const publicUrl = this.tunnelService?.getPublicUrl();
3932
+ const hasPublicTunnel = !!publicUrl && !publicUrl.startsWith("http://localhost") && !publicUrl.startsWith("http://127.0.0.1");
3933
+ if (this.tunnelService && sessionContext && hasPublicTunnel) {
3934
+ const label = typeof input.command === "string" ? input.command : entry.name;
3935
+ const id = this.tunnelService.getStore().storeOutput(sessionContext.id, label, content);
3936
+ if (id !== null) {
3937
+ outputViewerLink = this.tunnelService.outputUrl(id);
3938
+ }
3939
+ } else if (mode === "high") {
3940
+ outputFallbackContent = content;
3941
+ }
3942
+ } else if (mode === "high") {
3943
+ outputContent = content;
3944
+ }
3945
+ }
3946
+ const diffStats = includeMeta ? entry.diffStats ?? null : null;
3947
+ return {
3948
+ id: entry.id,
3949
+ kind: effectiveKind,
3950
+ icon,
3951
+ title,
3952
+ description,
3953
+ command,
3954
+ inputContent,
3955
+ outputSummary,
3956
+ outputContent,
3957
+ diffStats,
3958
+ viewerLinks: entry.viewerLinks,
3959
+ outputViewerLink,
3960
+ outputFallbackContent,
3961
+ status: entry.status,
3962
+ isNoise: entry.isNoise,
3963
+ isHidden
3964
+ };
3965
+ }
3966
+ buildThoughtSpec(content, mode) {
3967
+ const indicator = "Thinking...";
3968
+ return {
3969
+ indicator,
3970
+ content: mode === "high" ? content : null
3971
+ };
3972
+ }
3973
+ };
3974
+
3415
3975
  // src/plugins/telegram/activity.ts
3416
3976
  var log10 = createChildLogger({ module: "telegram:activity" });
3417
3977
  var THINKING_REFRESH_MS = 15e3;
3418
3978
  var THINKING_MAX_MS = 3 * 60 * 1e3;
3419
3979
  var ThinkingIndicator = class {
3420
- constructor(api, chatId, threadId, sendQueue) {
3980
+ constructor(api, chatId, threadId, sendQueue, sessionId = "", tracer = null) {
3421
3981
  this.api = api;
3422
3982
  this.chatId = chatId;
3423
3983
  this.threadId = threadId;
3424
3984
  this.sendQueue = sendQueue;
3985
+ this.sessionId = sessionId;
3986
+ this.tracer = tracer;
3425
3987
  }
3426
3988
  msgId;
3427
3989
  sending = false;
3428
3990
  dismissed = false;
3429
3991
  refreshTimer;
3430
3992
  showTime = 0;
3993
+ tracer;
3431
3994
  async show() {
3432
- if (this.msgId || this.sending || this.dismissed) return;
3995
+ if (this.sending || this.dismissed) return;
3996
+ if (this.msgId) return;
3433
3997
  this.sending = true;
3434
3998
  this.showTime = Date.now();
3999
+ const text = "\u{1F4AD} <i>Thinking...</i>";
3435
4000
  try {
3436
4001
  const result = await this.sendQueue.enqueue(
3437
- () => this.api.sendMessage(this.chatId, "\u{1F4AD} <i>Thinking...</i>", {
4002
+ () => this.api.sendMessage(this.chatId, text, {
3438
4003
  message_thread_id: this.threadId,
3439
4004
  parse_mode: "HTML",
3440
4005
  disable_notification: true
3441
4006
  })
3442
4007
  );
3443
- if (result && !this.dismissed) {
3444
- this.msgId = result.message_id;
3445
- this.startRefreshTimer();
4008
+ if (result) {
4009
+ this.tracer?.log("telegram", { action: "thinking:show", sessionId: this.sessionId, msgId: result.message_id });
4010
+ if (this.dismissed) {
4011
+ } else {
4012
+ this.msgId = result.message_id;
4013
+ this.startRefreshTimer();
4014
+ }
3446
4015
  }
3447
4016
  } catch (err) {
3448
4017
  log10.warn({ err }, "ThinkingIndicator.show() failed");
@@ -3450,11 +4019,27 @@ var ThinkingIndicator = class {
3450
4019
  this.sending = false;
3451
4020
  }
3452
4021
  }
3453
- /** Clear state stops refresh timer, no Telegram API call */
3454
- dismiss() {
4022
+ /** Edit the indicator message to append a viewer link, then dismiss. */
4023
+ async finalizeWithViewerLink(url) {
4024
+ this.stopRefreshTimer();
4025
+ if (this.msgId && !this.dismissed) {
4026
+ const text = `\u{1F4AD} <i>Thinking...</i> <a href="${escapeHtml(url)}">View thinking</a>`;
4027
+ await this.sendQueue.enqueue(() => {
4028
+ if (!this.msgId) return Promise.resolve(void 0);
4029
+ return this.api.editMessageText(this.chatId, this.msgId, text, { parse_mode: "HTML" });
4030
+ }).catch(() => {
4031
+ });
4032
+ }
3455
4033
  this.dismissed = true;
3456
4034
  this.msgId = void 0;
4035
+ }
4036
+ /** Dismiss indicator: stops refresh timer. Message is left in chat to reduce API calls. */
4037
+ async dismiss() {
4038
+ if (this.dismissed) return;
4039
+ this.dismissed = true;
4040
+ this.tracer?.log("telegram", { action: "thinking:dismiss", sessionId: this.sessionId });
3457
4041
  this.stopRefreshTimer();
4042
+ this.msgId = void 0;
3458
4043
  }
3459
4044
  /** Reset for a new prompt cycle */
3460
4045
  reset() {
@@ -3468,12 +4053,13 @@ var ThinkingIndicator = class {
3468
4053
  return;
3469
4054
  }
3470
4055
  const elapsed = Math.round((Date.now() - this.showTime) / 1e3);
4056
+ const refreshText = `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`;
3471
4057
  this.sendQueue.enqueue(() => {
3472
4058
  if (this.dismissed || !this.msgId) return Promise.resolve(void 0);
3473
4059
  return this.api.editMessageText(
3474
4060
  this.chatId,
3475
4061
  this.msgId,
3476
- `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`,
4062
+ refreshText,
3477
4063
  { parse_mode: "HTML" }
3478
4064
  );
3479
4065
  }).then(() => {
@@ -3489,13 +4075,14 @@ var ThinkingIndicator = class {
3489
4075
  }
3490
4076
  };
3491
4077
  var ToolCard = class {
3492
- constructor(api, chatId, threadId, sendQueue, verbosity) {
4078
+ constructor(api, chatId, threadId, sendQueue, sessionId = "", tracer = null) {
3493
4079
  this.api = api;
3494
4080
  this.chatId = chatId;
3495
4081
  this.threadId = threadId;
3496
4082
  this.sendQueue = sendQueue;
4083
+ this.tracer = tracer;
4084
+ this.sessionId = sessionId;
3497
4085
  this.state = new ToolCardState({
3498
- verbosity,
3499
4086
  onFlush: (snapshot) => {
3500
4087
  this.flushPromise = this.flushPromise.then(() => this._sendOrEdit(snapshot)).catch(() => {
3501
4088
  });
@@ -3506,11 +4093,11 @@ var ToolCard = class {
3506
4093
  msgId;
3507
4094
  lastSentText;
3508
4095
  flushPromise = Promise.resolve();
3509
- addTool(meta, kind, rawInput) {
3510
- this.state.addTool(meta, kind, rawInput);
3511
- }
3512
- updateTool(id, status, viewerLinks, viewerFilePath) {
3513
- this.state.updateTool(id, status, viewerLinks, viewerFilePath);
4096
+ overflowMsgIds = [];
4097
+ tracer;
4098
+ sessionId;
4099
+ updateFromSpec(spec) {
4100
+ this.state.updateFromSpec(spec);
3514
4101
  }
3515
4102
  updatePlan(entries) {
3516
4103
  this.state.updatePlan(entries);
@@ -3532,26 +4119,58 @@ var ToolCard = class {
3532
4119
  return this.msgId;
3533
4120
  }
3534
4121
  async _sendOrEdit(snapshot) {
3535
- const text = renderToolCard(snapshot);
3536
- if (!text) return;
3537
- if (this.msgId && text === this.lastSentText) return;
3538
- this.lastSentText = text;
4122
+ let snapshotToRender = snapshot;
4123
+ let fullText = renderToolCard(snapshotToRender);
4124
+ if (fullText.length > 4096) {
4125
+ snapshotToRender = {
4126
+ ...snapshot,
4127
+ specs: snapshot.specs.map(
4128
+ (s) => s.outputContent ? { ...s, outputContent: null } : s
4129
+ )
4130
+ };
4131
+ fullText = renderToolCard(snapshotToRender);
4132
+ }
4133
+ if (!fullText) return;
4134
+ if (this.msgId && fullText === this.lastSentText) return;
4135
+ this.lastSentText = fullText;
4136
+ const chunks = splitToolCardText(fullText);
4137
+ this.tracer?.log("telegram", { action: "toolCard:render", sessionId: this.sessionId, chunks: chunks.length, total: snapshot.totalVisible, completed: snapshot.completedVisible, allComplete: snapshot.allComplete, msgId: this.msgId, entries: snapshot.specs.map((s) => ({ id: s.id, kind: s.kind, icon: s.icon, title: s.title })), html: fullText });
3539
4138
  try {
4139
+ const firstChunk = chunks[0];
3540
4140
  if (this.msgId) {
3541
4141
  await this.sendQueue.enqueue(
3542
- () => this.api.editMessageText(this.chatId, this.msgId, text, {
3543
- parse_mode: "HTML"
3544
- })
4142
+ () => this.api.editMessageText(this.chatId, this.msgId, firstChunk, { parse_mode: "HTML" })
3545
4143
  );
4144
+ this.tracer?.log("telegram", { action: "telegram:edit", sessionId: this.sessionId, msgId: this.msgId, html: firstChunk });
3546
4145
  } else {
3547
4146
  const result = await this.sendQueue.enqueue(
3548
- () => this.api.sendMessage(this.chatId, text, {
4147
+ () => this.api.sendMessage(this.chatId, firstChunk, {
3549
4148
  message_thread_id: this.threadId,
3550
4149
  parse_mode: "HTML",
3551
4150
  disable_notification: true
3552
4151
  })
3553
4152
  );
3554
4153
  if (result) this.msgId = result.message_id;
4154
+ this.tracer?.log("telegram", { action: "telegram:send", sessionId: this.sessionId, msgId: result?.message_id, html: firstChunk });
4155
+ }
4156
+ for (let i = 1; i < chunks.length; i++) {
4157
+ const overflowIdx = i - 1;
4158
+ if (overflowIdx < this.overflowMsgIds.length) {
4159
+ await this.sendQueue.enqueue(
4160
+ () => this.api.editMessageText(this.chatId, this.overflowMsgIds[overflowIdx], chunks[i], { parse_mode: "HTML" })
4161
+ );
4162
+ this.tracer?.log("telegram", { action: "telegram:edit:overflow", sessionId: this.sessionId, msgId: this.overflowMsgIds[overflowIdx] });
4163
+ } else {
4164
+ const result = await this.sendQueue.enqueue(
4165
+ () => this.api.sendMessage(this.chatId, chunks[i], {
4166
+ message_thread_id: this.threadId,
4167
+ parse_mode: "HTML",
4168
+ disable_notification: true
4169
+ })
4170
+ );
4171
+ if (result) this.overflowMsgIds.push(result.message_id);
4172
+ this.tracer?.log("telegram", { action: "telegram:send:overflow", sessionId: this.sessionId, msgId: result?.message_id });
4173
+ }
3555
4174
  }
3556
4175
  } catch (err) {
3557
4176
  log10.warn({ err }, "[ToolCard] send/edit failed");
@@ -3559,59 +4178,140 @@ var ToolCard = class {
3559
4178
  }
3560
4179
  };
3561
4180
  var ActivityTracker = class {
3562
- constructor(api, chatId, threadId, sendQueue, verbosity = "medium") {
4181
+ constructor(api, chatId, threadId, sendQueue, outputMode = "medium", sessionId = "", tracer = null, tunnelService, sessionContext) {
3563
4182
  this.api = api;
3564
4183
  this.chatId = chatId;
3565
4184
  this.threadId = threadId;
3566
4185
  this.sendQueue = sendQueue;
3567
- this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue);
3568
- this.toolCard = new ToolCard(api, chatId, threadId, sendQueue, verbosity);
4186
+ this._outputMode = outputMode;
4187
+ this.tracer = tracer;
4188
+ this.sessionId = sessionId;
4189
+ this.sessionContext = sessionContext;
4190
+ this.tunnelService = tunnelService;
4191
+ this.specBuilder = new DisplaySpecBuilder(tunnelService);
4192
+ this.toolStateMap = new ToolStateMap();
4193
+ this.thoughtBuffer = new ThoughtBuffer();
4194
+ this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue, sessionId, tracer);
4195
+ this.toolCard = new ToolCard(api, chatId, threadId, sendQueue, sessionId, tracer);
3569
4196
  }
3570
4197
  isFirstEvent = true;
3571
4198
  thinking;
3572
4199
  toolCard;
4200
+ previousToolCard;
4201
+ toolStateMap;
4202
+ previousToolStateMap;
4203
+ specBuilder;
4204
+ thoughtBuffer;
4205
+ _outputMode;
4206
+ tracer;
4207
+ sessionId;
4208
+ sessionContext;
4209
+ tunnelService;
4210
+ setOutputMode(mode) {
4211
+ this._outputMode = mode;
4212
+ }
3573
4213
  async onNewPrompt() {
4214
+ this.tracer?.log("telegram", { action: "tracker:newPrompt", sessionId: this.sessionId });
3574
4215
  this.isFirstEvent = true;
3575
- this.thinking.dismiss();
4216
+ this.thoughtBuffer.reset();
4217
+ await this.thinking.dismiss();
3576
4218
  this.thinking.reset();
4219
+ await this.toolCard.finalize();
4220
+ this.previousToolCard = void 0;
4221
+ this.previousToolStateMap = void 0;
4222
+ this.toolStateMap.clear();
4223
+ this.toolCard = new ToolCard(this.api, this.chatId, this.threadId, this.sendQueue, this.sessionId, this.tracer);
3577
4224
  }
3578
- async onThought() {
4225
+ async onThought(text) {
4226
+ this.tracer?.log("telegram", { action: "tracker:thought", sessionId: this.sessionId });
3579
4227
  this.isFirstEvent = false;
4228
+ if (!this.thoughtBuffer.isSealed()) this.thoughtBuffer.append(text);
4229
+ await this.sealToolCardIfNeeded();
3580
4230
  await this.thinking.show();
3581
4231
  }
3582
4232
  async onTextStart() {
4233
+ this.tracer?.log("telegram", { action: "tracker:textStart", sessionId: this.sessionId });
3583
4234
  this.isFirstEvent = false;
3584
- this.thinking.dismiss();
4235
+ this.thoughtBuffer.seal();
4236
+ if (this._outputMode === "high" && this.tunnelService && this.sessionContext) {
4237
+ const thoughtText = this.thoughtBuffer.getText();
4238
+ if (thoughtText.trim().length > 0) {
4239
+ const id = this.tunnelService.getStore().storeOutput(
4240
+ this.sessionContext.id,
4241
+ "thinking",
4242
+ thoughtText
4243
+ );
4244
+ if (id !== null) {
4245
+ await this.thinking.finalizeWithViewerLink(this.tunnelService.outputUrl(id));
4246
+ } else {
4247
+ await this.thinking.dismiss();
4248
+ }
4249
+ } else {
4250
+ await this.thinking.dismiss();
4251
+ }
4252
+ } else {
4253
+ await this.thinking.dismiss();
4254
+ }
4255
+ await this.sealToolCardIfNeeded();
3585
4256
  }
3586
4257
  async onToolCall(meta, kind, rawInput) {
4258
+ this.tracer?.log("telegram", { action: "tracker:toolCall", sessionId: this.sessionId, meta, kind, rawInput });
3587
4259
  this.isFirstEvent = false;
3588
- this.thinking.dismiss();
4260
+ await this.thinking.dismiss();
3589
4261
  this.thinking.reset();
3590
- this.toolCard.addTool(meta, kind, rawInput);
3591
- }
3592
- async onToolUpdate(id, status, viewerLinks, viewerFilePath) {
3593
- this.toolCard.updateTool(id, status, viewerLinks, viewerFilePath);
4262
+ const entry = this.toolStateMap.upsert(meta, kind, rawInput);
4263
+ const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);
4264
+ this.toolCard.updateFromSpec(spec);
4265
+ }
4266
+ async onToolUpdate(id, status, viewerLinks, viewerFilePath, content, rawInput, diffStats) {
4267
+ this.tracer?.log("telegram", { action: "tracker:toolUpdate", sessionId: this.sessionId, toolId: id, status, viewerLinks, viewerFilePath, hasPrevCard: !!this.previousToolCard });
4268
+ if (this.previousToolStateMap?.get(id)) {
4269
+ this.previousToolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);
4270
+ const prevEntry = this.previousToolStateMap.get(id);
4271
+ if (prevEntry) {
4272
+ const prevSpec = this.specBuilder.buildToolSpec(prevEntry, this._outputMode, this.sessionContext);
4273
+ this.previousToolCard?.updateFromSpec(prevSpec);
4274
+ }
4275
+ }
4276
+ const existed = !!this.toolStateMap.get(id);
4277
+ const entry = this.toolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);
4278
+ if (!existed || !entry) return;
4279
+ if (viewerLinks || entry.viewerLinks) {
4280
+ log10.debug({ toolId: id, status, hasIncomingLinks: !!viewerLinks, hasEntryLinks: !!entry.viewerLinks, entryLinks: entry.viewerLinks }, "toolUpdate: viewer links trace");
4281
+ }
4282
+ const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);
4283
+ this.toolCard.updateFromSpec(spec);
3594
4284
  }
3595
4285
  async onPlan(entries) {
4286
+ this.tracer?.log("telegram", { action: "tracker:plan", sessionId: this.sessionId, entries });
3596
4287
  this.isFirstEvent = false;
3597
- this.thinking.dismiss();
4288
+ await this.thinking.dismiss();
3598
4289
  this.toolCard.updatePlan(entries);
3599
4290
  }
3600
- async sendUsage(usage) {
3601
- this.toolCard.appendUsage(usage);
4291
+ /** @deprecated Usage is now sent as a separate message by the adapter */
4292
+ async sendUsage(_usage) {
3602
4293
  }
3603
- getUsageMsgId() {
4294
+ getToolCardMsgId() {
3604
4295
  return this.toolCard.getMsgId();
3605
4296
  }
3606
4297
  async cleanup() {
3607
- this.thinking.dismiss();
4298
+ await this.thinking.dismiss();
3608
4299
  await this.toolCard.finalize();
3609
4300
  this.toolCard.destroy();
3610
4301
  }
3611
4302
  destroy() {
3612
- this.thinking.dismiss();
4303
+ void this.thinking.dismiss();
3613
4304
  this.toolCard.destroy();
3614
4305
  }
4306
+ async sealToolCardIfNeeded() {
4307
+ if (!this.toolCard.hasContent()) return;
4308
+ this.tracer?.log("telegram", { action: "tracker:seal", sessionId: this.sessionId });
4309
+ await this.toolCard.finalize();
4310
+ this.previousToolCard = this.toolCard;
4311
+ this.previousToolStateMap = this.toolStateMap;
4312
+ this.toolStateMap = new ToolStateMap();
4313
+ this.toolCard = new ToolCard(this.api, this.chatId, this.threadId, this.sendQueue, this.sessionId, this.tracer);
4314
+ }
3615
4315
  };
3616
4316
 
3617
4317
  // src/core/adapter-primitives/primitives/send-queue.ts
@@ -3872,12 +4572,13 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
3872
4572
  // src/plugins/telegram/streaming.ts
3873
4573
  var FLUSH_INTERVAL = 5e3;
3874
4574
  var MessageDraft = class {
3875
- constructor(bot, chatId, threadId, sendQueue, sessionId) {
4575
+ constructor(bot, chatId, threadId, sendQueue, sessionId, tracer = null) {
3876
4576
  this.bot = bot;
3877
4577
  this.chatId = chatId;
3878
4578
  this.threadId = threadId;
3879
4579
  this.sendQueue = sendQueue;
3880
4580
  this.sessionId = sessionId;
4581
+ this.tracer = tracer;
3881
4582
  }
3882
4583
  buffer = "";
3883
4584
  messageId;
@@ -3886,6 +4587,7 @@ var MessageDraft = class {
3886
4587
  flushPromise = Promise.resolve();
3887
4588
  lastSentBuffer = "";
3888
4589
  displayTruncated = false;
4590
+ tracer;
3889
4591
  append(text) {
3890
4592
  if (!text) return;
3891
4593
  this.buffer += text;
@@ -3930,6 +4632,7 @@ var MessageDraft = class {
3930
4632
  );
3931
4633
  if (result) {
3932
4634
  this.messageId = result.message_id;
4635
+ this.tracer?.log("telegram", { action: "draft:send", sessionId: this.sessionId, msgId: result.message_id, textLen: snapshot.length, truncated, text: snapshot });
3933
4636
  if (!truncated) {
3934
4637
  this.lastSentBuffer = snapshot;
3935
4638
  this.displayTruncated = false;
@@ -3950,6 +4653,7 @@ var MessageDraft = class {
3950
4653
  { type: "text", key: this.sessionId }
3951
4654
  );
3952
4655
  if (result !== void 0) {
4656
+ this.tracer?.log("telegram", { action: "draft:edit", sessionId: this.sessionId, msgId: this.messageId, textLen: snapshot.length, truncated, text: snapshot });
3953
4657
  if (!truncated) {
3954
4658
  this.lastSentBuffer = snapshot;
3955
4659
  this.displayTruncated = false;
@@ -3962,6 +4666,7 @@ var MessageDraft = class {
3962
4666
  }
3963
4667
  }
3964
4668
  async finalize() {
4669
+ this.tracer?.log("telegram", { action: "draft:finalize", sessionId: this.sessionId, bufferLen: this.buffer.length, msgId: this.messageId });
3965
4670
  if (this.flushTimer) {
3966
4671
  clearTimeout(this.flushTimer);
3967
4672
  this.flushTimer = void 0;
@@ -3981,6 +4686,7 @@ var MessageDraft = class {
3981
4686
  }),
3982
4687
  { type: "other" }
3983
4688
  );
4689
+ this.tracer?.log("telegram", { action: "draft:finalize:edit", sessionId: this.sessionId, msgId: this.messageId });
3984
4690
  } else {
3985
4691
  const msg = await this.sendQueue.enqueue(
3986
4692
  () => this.bot.api.sendMessage(this.chatId, fullHtml, {
@@ -3991,6 +4697,7 @@ var MessageDraft = class {
3991
4697
  { type: "other" }
3992
4698
  );
3993
4699
  if (msg) this.messageId = msg.message_id;
4700
+ this.tracer?.log("telegram", { action: "draft:finalize:send", sessionId: this.sessionId, msgId: msg?.message_id });
3994
4701
  }
3995
4702
  return this.messageId;
3996
4703
  } catch {
@@ -4026,6 +4733,7 @@ var MessageDraft = class {
4026
4733
  chunkPromises.push(promise);
4027
4734
  }
4028
4735
  await Promise.all(chunkPromises);
4736
+ this.tracer?.log("telegram", { action: "draft:finalize:split", sessionId: this.sessionId, chunks: mdChunks.length });
4029
4737
  return this.messageId;
4030
4738
  }
4031
4739
  getMessageId() {
@@ -4060,7 +4768,8 @@ var DraftManager = class {
4060
4768
  }
4061
4769
  drafts = /* @__PURE__ */ new Map();
4062
4770
  textBuffers = /* @__PURE__ */ new Map();
4063
- getOrCreate(sessionId, threadId) {
4771
+ finalizedDrafts = /* @__PURE__ */ new Map();
4772
+ getOrCreate(sessionId, threadId, tracer = null) {
4064
4773
  let draft = this.drafts.get(sessionId);
4065
4774
  if (!draft) {
4066
4775
  draft = new MessageDraft(
@@ -4068,7 +4777,8 @@ var DraftManager = class {
4068
4777
  this.chatId,
4069
4778
  threadId,
4070
4779
  this.sendQueue,
4071
- sessionId
4780
+ sessionId,
4781
+ tracer
4072
4782
  );
4073
4783
  this.drafts.set(sessionId, draft);
4074
4784
  }
@@ -4095,6 +4805,9 @@ var DraftManager = class {
4095
4805
  if (!draft) return;
4096
4806
  this.drafts.delete(sessionId);
4097
4807
  const finalMsgId = await draft.finalize();
4808
+ if (finalMsgId) {
4809
+ this.finalizedDrafts.set(sessionId, { messageId: finalMsgId, draft });
4810
+ }
4098
4811
  if (assistantSessionId && sessionId === assistantSessionId) {
4099
4812
  const fullText = this.textBuffers.get(sessionId);
4100
4813
  this.textBuffers.delete(sessionId);
@@ -4117,9 +4830,25 @@ var DraftManager = class {
4117
4830
  this.textBuffers.delete(sessionId);
4118
4831
  }
4119
4832
  }
4833
+ /**
4834
+ * Strip a regex pattern from the active or finalized draft for a session.
4835
+ * Used by tts_strip to remove [TTS]...[/TTS] blocks after TTS audio is sent.
4836
+ */
4837
+ async stripPattern(sessionId, pattern) {
4838
+ const draft = this.drafts.get(sessionId);
4839
+ if (draft) {
4840
+ await draft.stripPattern(pattern);
4841
+ return;
4842
+ }
4843
+ const finalized = this.finalizedDrafts.get(sessionId);
4844
+ if (finalized) {
4845
+ await finalized.draft.stripPattern(pattern);
4846
+ }
4847
+ }
4120
4848
  cleanup(sessionId) {
4121
4849
  this.drafts.delete(sessionId);
4122
4850
  this.textBuffers.delete(sessionId);
4851
+ this.finalizedDrafts.delete(sessionId);
4123
4852
  }
4124
4853
  };
4125
4854
 
@@ -4218,6 +4947,231 @@ var SkillCommandManager = class {
4218
4947
  }
4219
4948
  };
4220
4949
 
4950
+ // src/core/adapter-primitives/messaging-adapter.ts
4951
+ var HIDDEN_ON_LOW = /* @__PURE__ */ new Set(["thought", "usage"]);
4952
+ var MessagingAdapter = class {
4953
+ constructor(context, adapterConfig) {
4954
+ this.context = context;
4955
+ this.adapterConfig = adapterConfig;
4956
+ }
4957
+ // === Message dispatch flow ===
4958
+ async sendMessage(sessionId, content) {
4959
+ const verbosity = this.getVerbosity();
4960
+ if (!this.shouldDisplay(content, verbosity)) return;
4961
+ await this.dispatchMessage(sessionId, content, verbosity);
4962
+ }
4963
+ async dispatchMessage(sessionId, content, verbosity) {
4964
+ switch (content.type) {
4965
+ case "text":
4966
+ return this.handleText(sessionId, content);
4967
+ case "thought":
4968
+ return this.handleThought(sessionId, content, verbosity);
4969
+ case "tool_call":
4970
+ return this.handleToolCall(sessionId, content, verbosity);
4971
+ case "tool_update":
4972
+ return this.handleToolUpdate(sessionId, content, verbosity);
4973
+ case "plan":
4974
+ return this.handlePlan(sessionId, content, verbosity);
4975
+ case "usage":
4976
+ return this.handleUsage(sessionId, content, verbosity);
4977
+ case "error":
4978
+ return this.handleError(sessionId, content);
4979
+ case "attachment":
4980
+ return this.handleAttachment(sessionId, content);
4981
+ case "system_message":
4982
+ return this.handleSystem(sessionId, content);
4983
+ case "session_end":
4984
+ return this.handleSessionEnd(sessionId, content);
4985
+ case "mode_change":
4986
+ return this.handleModeChange(sessionId, content);
4987
+ case "config_update":
4988
+ return this.handleConfigUpdate(sessionId, content);
4989
+ case "model_update":
4990
+ return this.handleModelUpdate(sessionId, content);
4991
+ case "user_replay":
4992
+ return this.handleUserReplay(sessionId, content);
4993
+ case "resource":
4994
+ return this.handleResource(sessionId, content);
4995
+ case "resource_link":
4996
+ return this.handleResourceLink(sessionId, content);
4997
+ }
4998
+ }
4999
+ // === Default handlers — all protected, all overridable ===
5000
+ async handleText(_sessionId, _content) {
5001
+ }
5002
+ async handleThought(_sessionId, _content, _verbosity) {
5003
+ }
5004
+ async handleToolCall(_sessionId, _content, _verbosity) {
5005
+ }
5006
+ async handleToolUpdate(_sessionId, _content, _verbosity) {
5007
+ }
5008
+ async handlePlan(_sessionId, _content, _verbosity) {
5009
+ }
5010
+ async handleUsage(_sessionId, _content, _verbosity) {
5011
+ }
5012
+ async handleError(_sessionId, _content) {
5013
+ }
5014
+ async handleAttachment(_sessionId, _content) {
5015
+ }
5016
+ async handleSystem(_sessionId, _content) {
5017
+ }
5018
+ async handleSessionEnd(_sessionId, _content) {
5019
+ }
5020
+ async handleModeChange(_sessionId, _content) {
5021
+ }
5022
+ async handleConfigUpdate(_sessionId, _content) {
5023
+ }
5024
+ async handleModelUpdate(_sessionId, _content) {
5025
+ }
5026
+ async handleUserReplay(_sessionId, _content) {
5027
+ }
5028
+ async handleResource(_sessionId, _content) {
5029
+ }
5030
+ async handleResourceLink(_sessionId, _content) {
5031
+ }
5032
+ // === Helpers ===
5033
+ getVerbosity() {
5034
+ const config = this.context.configManager.get();
5035
+ const channelConfig = config.channels;
5036
+ const v = channelConfig?.[this.name]?.displayVerbosity ?? this.adapterConfig.displayVerbosity;
5037
+ if (v === "low" || v === "high") return v;
5038
+ return "medium";
5039
+ }
5040
+ shouldDisplay(content, verbosity) {
5041
+ if (verbosity === "low" && HIDDEN_ON_LOW.has(content.type)) return false;
5042
+ if (content.type === "tool_call") {
5043
+ const meta = content.metadata ?? {};
5044
+ const toolName = meta.name ?? content.text ?? "";
5045
+ const toolKind = String(meta.kind ?? "other");
5046
+ const noiseAction = evaluateNoise(toolName, toolKind, meta.rawInput);
5047
+ if (noiseAction === "hide" && verbosity !== "high") return false;
5048
+ if (noiseAction === "collapse" && verbosity === "low") return false;
5049
+ }
5050
+ return true;
5051
+ }
5052
+ };
5053
+
5054
+ // src/core/adapter-primitives/rendering/renderer.ts
5055
+ var BaseRenderer = class {
5056
+ renderText(content) {
5057
+ return { body: content.text, format: "plain" };
5058
+ }
5059
+ renderToolCall(content, verbosity) {
5060
+ const meta = content.metadata ?? {};
5061
+ const name = meta.name ?? content.text ?? "Tool";
5062
+ const icon = resolveToolIcon(meta);
5063
+ const label = verbosity === "low" ? formatToolTitle(
5064
+ name,
5065
+ meta.rawInput,
5066
+ meta.displayTitle
5067
+ ) : formatToolSummary(
5068
+ name,
5069
+ meta.rawInput,
5070
+ meta.displaySummary
5071
+ );
5072
+ return { body: `${icon} ${label}`, format: "plain" };
5073
+ }
5074
+ renderToolUpdate(content, verbosity) {
5075
+ const meta = content.metadata ?? {};
5076
+ const name = meta.name ?? content.text ?? "Tool";
5077
+ const icon = resolveToolIcon(meta);
5078
+ const label = verbosity === "low" ? formatToolTitle(
5079
+ name,
5080
+ meta.rawInput,
5081
+ meta.displayTitle
5082
+ ) : formatToolSummary(
5083
+ name,
5084
+ meta.rawInput,
5085
+ meta.displaySummary
5086
+ );
5087
+ return { body: `${icon} ${label}`, format: "plain" };
5088
+ }
5089
+ renderPlan(content) {
5090
+ const entries = content.metadata?.entries ?? [];
5091
+ const lines = entries.map((e, i) => {
5092
+ const icon = e.status === "completed" ? "\u2705" : e.status === "in_progress" ? "\u{1F504}" : "\u2B1C";
5093
+ return `${icon} ${i + 1}. ${e.content}`;
5094
+ });
5095
+ return { body: `\u{1F4CB} Plan
5096
+ ${lines.join("\n")}`, format: "plain" };
5097
+ }
5098
+ renderUsage(content, verbosity) {
5099
+ const meta = content.metadata;
5100
+ if (!meta?.tokensUsed)
5101
+ return { body: "\u{1F4CA} Usage data unavailable", format: "plain" };
5102
+ const costStr = meta.cost != null ? ` \xB7 $${meta.cost.toFixed(2)}` : "";
5103
+ if (verbosity === "medium") {
5104
+ return {
5105
+ body: `\u{1F4CA} ${formatTokens(meta.tokensUsed)} tokens${costStr}`,
5106
+ format: "plain"
5107
+ };
5108
+ }
5109
+ if (!meta.contextSize)
5110
+ return {
5111
+ body: `\u{1F4CA} ${formatTokens(meta.tokensUsed)} tokens`,
5112
+ format: "plain"
5113
+ };
5114
+ const ratio = meta.tokensUsed / meta.contextSize;
5115
+ const pct = Math.round(ratio * 100);
5116
+ const bar = progressBar(ratio);
5117
+ let text = `\u{1F4CA} ${formatTokens(meta.tokensUsed)} / ${formatTokens(meta.contextSize)} tokens
5118
+ ${bar} ${pct}%`;
5119
+ if (meta.cost != null) text += `
5120
+ \u{1F4B0} $${meta.cost.toFixed(2)}`;
5121
+ return { body: text, format: "plain" };
5122
+ }
5123
+ renderPermission(request) {
5124
+ return {
5125
+ body: request.description,
5126
+ format: "plain",
5127
+ actions: request.options.map((o) => ({
5128
+ id: o.id,
5129
+ label: o.label,
5130
+ isAllow: o.isAllow
5131
+ }))
5132
+ };
5133
+ }
5134
+ renderError(content) {
5135
+ return { body: `\u274C Error: ${content.text}`, format: "plain" };
5136
+ }
5137
+ renderNotification(notification) {
5138
+ const emoji = {
5139
+ completed: "\u2705",
5140
+ error: "\u274C",
5141
+ permission: "\u{1F510}",
5142
+ input_required: "\u{1F4AC}",
5143
+ budget_warning: "\u26A0\uFE0F"
5144
+ };
5145
+ return {
5146
+ body: `${emoji[notification.type] || "\u2139\uFE0F"} ${notification.sessionName || "Session"}
5147
+ ${notification.summary}`,
5148
+ format: "plain"
5149
+ };
5150
+ }
5151
+ renderSystemMessage(content) {
5152
+ return { body: content.text, format: "plain" };
5153
+ }
5154
+ renderModeChange(content) {
5155
+ const modeId = content.metadata?.modeId ?? "";
5156
+ return { body: `\u{1F504} Mode: ${modeId}`, format: "plain" };
5157
+ }
5158
+ renderConfigUpdate() {
5159
+ return { body: "\u2699\uFE0F Config updated", format: "plain" };
5160
+ }
5161
+ renderModelUpdate(content) {
5162
+ const modelId = content.metadata?.modelId ?? "";
5163
+ return { body: `\u{1F916} Model: ${modelId}`, format: "plain" };
5164
+ }
5165
+ renderResource(content) {
5166
+ const uri = content.metadata?.uri ?? "";
5167
+ return { body: `\u{1F4C4} Resource: ${content.text} (${uri})`, format: "plain" };
5168
+ }
5169
+ renderResourceLink(content) {
5170
+ const uri = content.metadata?.uri ?? "";
5171
+ return { body: `\u{1F517} ${content.text}: ${uri}`, format: "plain" };
5172
+ }
5173
+ };
5174
+
4221
5175
  // src/plugins/telegram/renderer.ts
4222
5176
  var TelegramRenderer = class extends BaseRenderer {
4223
5177
  renderToolCall(content, verbosity) {
@@ -4286,6 +5240,28 @@ var TelegramRenderer = class extends BaseRenderer {
4286
5240
  }
4287
5241
  };
4288
5242
 
5243
+ // src/core/adapter-primitives/output-mode-resolver.ts
5244
+ var VALID_MODES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
5245
+ function toOutputMode(v) {
5246
+ return typeof v === "string" && VALID_MODES.has(v) ? v : void 0;
5247
+ }
5248
+ var OutputModeResolver = class {
5249
+ resolve(configManager, adapterName, sessionId, sessionManager) {
5250
+ const config = configManager.get();
5251
+ let mode = toOutputMode(config.outputMode) ?? "medium";
5252
+ const channels = config.channels;
5253
+ const channelCfg = channels?.[adapterName];
5254
+ const adapterMode = toOutputMode(channelCfg?.outputMode);
5255
+ if (adapterMode) mode = adapterMode;
5256
+ if (sessionId && sessionManager) {
5257
+ const session = sessionManager.getSession(sessionId);
5258
+ const sessionMode = session?.record?.outputMode;
5259
+ if (sessionMode) mode = sessionMode;
5260
+ }
5261
+ return mode;
5262
+ }
5263
+ };
5264
+
4289
5265
  // src/plugins/telegram/adapter.ts
4290
5266
  var log12 = createChildLogger({ module: "telegram" });
4291
5267
  function patchedFetch(input, init) {
@@ -4322,6 +5298,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4322
5298
  assistantTopicId;
4323
5299
  sendQueue = new SendQueue({ minInterval: 3e3 });
4324
5300
  _sessionThreadIds = /* @__PURE__ */ new Map();
5301
+ outputModeResolver = new OutputModeResolver();
4325
5302
  // Extracted managers
4326
5303
  draftManager;
4327
5304
  skillManager;
@@ -4336,17 +5313,29 @@ var TelegramAdapter = class extends MessagingAdapter {
4336
5313
  }
4337
5314
  return threadId;
4338
5315
  }
4339
- getOrCreateTracker(sessionId, threadId, verbosity = "medium") {
5316
+ getOrCreateTracker(sessionId, threadId, outputMode = "medium") {
4340
5317
  let tracker = this.sessionTrackers.get(sessionId);
4341
5318
  if (!tracker) {
5319
+ const tunnelService = this.core.lifecycleManager?.serviceRegistry?.get("tunnel");
5320
+ const session = this.core.sessionManager.getSession(sessionId);
5321
+ const sessionContext = session ? {
5322
+ id: sessionId,
5323
+ workingDirectory: session.workingDirectory
5324
+ } : void 0;
4342
5325
  tracker = new ActivityTracker(
4343
5326
  this.bot.api,
4344
5327
  this.telegramConfig.chatId,
4345
5328
  threadId,
4346
5329
  this.sendQueue,
4347
- verbosity
5330
+ outputMode,
5331
+ sessionId,
5332
+ this.getTracer(sessionId),
5333
+ tunnelService,
5334
+ sessionContext
4348
5335
  );
4349
5336
  this.sessionTrackers.set(sessionId, tracker);
5337
+ } else {
5338
+ tracker.setOutputMode(outputMode);
4350
5339
  }
4351
5340
  return tracker;
4352
5341
  }
@@ -4600,7 +5589,7 @@ var TelegramAdapter = class extends MessagingAdapter {
4600
5589
  });
4601
5590
  return;
4602
5591
  }
4603
- const { getAgentCapabilities } = await import("./agent-registry-5LZT7CUB.js");
5592
+ const { getAgentCapabilities } = await import("./agent-registry-YOGP656W.js");
4604
5593
  const caps = getAgentCapabilities(agentName);
4605
5594
  if (!caps.supportsResume || !caps.resumeCommand) {
4606
5595
  await ctx.reply("This agent does not support session transfer.", {
@@ -4681,6 +5670,11 @@ var TelegramAdapter = class extends MessagingAdapter {
4681
5670
  }
4682
5671
  }
4683
5672
  async stop() {
5673
+ for (const tracker of this.sessionTrackers.values()) {
5674
+ tracker.destroy();
5675
+ }
5676
+ this.sessionTrackers.clear();
5677
+ this.sendQueue.clear();
4684
5678
  if (this.assistantSession) {
4685
5679
  await this.assistantSession.destroy();
4686
5680
  }
@@ -4821,8 +5815,10 @@ ${lines.join("\n")}`;
4821
5815
  "telegram",
4822
5816
  String(threadId)
4823
5817
  )?.id;
4824
- if (sessionId)
5818
+ if (sessionId) {
5819
+ this.getTracer(sessionId)?.log("telegram", { action: "incoming:message", sessionId, userId: String(ctx.from?.id), text: ctx.message?.text });
4825
5820
  await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5821
+ }
4826
5822
  if (sessionId) {
4827
5823
  const tracker = this.sessionTrackers.get(sessionId);
4828
5824
  if (tracker) await tracker.onNewPrompt();
@@ -4905,6 +5901,18 @@ ${lines.join("\n")}`;
4905
5901
  });
4906
5902
  }
4907
5903
  // --- MessagingAdapter overrides ---
5904
+ /**
5905
+ * Per-session serial dispatch queues.
5906
+ * SessionBridge fires sendMessage() as fire-and-forget, so multiple events
5907
+ * (tool_call, tool_update, text) can arrive concurrently. Without serialization,
5908
+ * fast handlers (tool_update) overtake slow ones (tool_call with draftManager.finalize),
5909
+ * causing out-of-order processing where a tool's completion update is processed before
5910
+ * its creation event. This queue ensures events are processed in the order they arrive.
5911
+ */
5912
+ _dispatchQueues = /* @__PURE__ */ new Map();
5913
+ getTracer(sessionId) {
5914
+ return this.core.sessionManager.getSession(sessionId)?.agentInstance?.debugTracer ?? null;
5915
+ }
4908
5916
  async sendMessage(sessionId, content) {
4909
5917
  if (this.assistantInitializing && sessionId === this.assistantSession?.id)
4910
5918
  return;
@@ -4919,33 +5927,61 @@ ${lines.join("\n")}`;
4919
5927
  );
4920
5928
  return;
4921
5929
  }
4922
- this._sessionThreadIds.set(sessionId, threadId);
4923
- try {
4924
- await super.sendMessage(sessionId, content);
4925
- } finally {
4926
- this._sessionThreadIds.delete(sessionId);
4927
- }
5930
+ const prev = this._dispatchQueues.get(sessionId) ?? Promise.resolve();
5931
+ const next = prev.then(async () => {
5932
+ this.getTracer(sessionId)?.log("telegram", { action: "dispatch:enter", sessionId, message: content });
5933
+ this._sessionThreadIds.set(sessionId, threadId);
5934
+ try {
5935
+ await super.sendMessage(sessionId, content);
5936
+ } finally {
5937
+ this._sessionThreadIds.delete(sessionId);
5938
+ }
5939
+ }).catch((err) => {
5940
+ log12.warn({ err, sessionId }, "Dispatch queue error");
5941
+ });
5942
+ this._dispatchQueues.set(sessionId, next);
5943
+ await next;
4928
5944
  }
4929
- async handleThought(sessionId, _content, _verbosity) {
5945
+ async handleThought(sessionId, content, _verbosity) {
5946
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:thought", sessionId, text: content.text });
4930
5947
  const threadId = this.getThreadId(sessionId);
4931
- const tracker = this.getOrCreateTracker(sessionId, threadId);
4932
- await tracker.onThought();
5948
+ const mode = this.outputModeResolver.resolve(
5949
+ this.context.configManager,
5950
+ this.name,
5951
+ sessionId,
5952
+ this.core.sessionManager
5953
+ );
5954
+ const tracker = this.getOrCreateTracker(sessionId, threadId, mode);
5955
+ await tracker.onThought(content.text);
4933
5956
  }
4934
5957
  async handleText(sessionId, content) {
5958
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:text", sessionId, text: content.text });
4935
5959
  const threadId = this.getThreadId(sessionId);
4936
5960
  if (!this.draftManager.hasDraft(sessionId)) {
4937
- const tracker = this.getOrCreateTracker(sessionId, threadId);
4938
- tracker.onTextStart().catch(() => {
4939
- });
5961
+ const mode = this.outputModeResolver.resolve(
5962
+ this.context.configManager,
5963
+ this.name,
5964
+ sessionId,
5965
+ this.core.sessionManager
5966
+ );
5967
+ const tracker = this.getOrCreateTracker(sessionId, threadId, mode);
5968
+ await tracker.onTextStart();
4940
5969
  }
4941
- const draft = this.draftManager.getOrCreate(sessionId, threadId);
5970
+ const draft = this.draftManager.getOrCreate(sessionId, threadId, this.getTracer(sessionId));
4942
5971
  draft.append(content.text);
4943
5972
  this.draftManager.appendText(sessionId, content.text);
4944
5973
  }
4945
- async handleToolCall(sessionId, content, verbosity) {
5974
+ async handleToolCall(sessionId, content, _verbosity) {
4946
5975
  const threadId = this.getThreadId(sessionId);
4947
5976
  const meta = content.metadata ?? {};
4948
- const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
5977
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:toolCall", sessionId, toolId: meta.id, toolName: meta.name, kind: meta.kind, status: meta.status, displaySummary: meta.displaySummary, rawInput: meta.rawInput });
5978
+ const mode = this.outputModeResolver.resolve(
5979
+ this.context.configManager,
5980
+ this.name,
5981
+ sessionId,
5982
+ this.core.sessionManager
5983
+ );
5984
+ const tracker = this.getOrCreateTracker(sessionId, threadId, mode);
4949
5985
  await this.draftManager.finalize(sessionId, this.assistantSession?.id);
4950
5986
  await tracker.onToolCall(
4951
5987
  {
@@ -4965,22 +6001,39 @@ ${lines.join("\n")}`;
4965
6001
  meta.rawInput
4966
6002
  );
4967
6003
  }
4968
- async handleToolUpdate(sessionId, content, verbosity) {
6004
+ async handleToolUpdate(sessionId, content, _verbosity) {
4969
6005
  const threadId = this.getThreadId(sessionId);
4970
6006
  const meta = content.metadata ?? {};
4971
- const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
6007
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:toolUpdate", sessionId, toolId: meta.id, name: meta.name, kind: meta.kind, status: meta.status, viewerLinks: meta.viewerLinks, viewerFilePath: meta.viewerFilePath });
6008
+ const mode = this.outputModeResolver.resolve(
6009
+ this.context.configManager,
6010
+ this.name,
6011
+ sessionId,
6012
+ this.core.sessionManager
6013
+ );
6014
+ const tracker = this.getOrCreateTracker(sessionId, threadId, mode);
4972
6015
  await tracker.onToolUpdate(
4973
6016
  meta.id ?? "",
4974
6017
  meta.status ?? "completed",
4975
6018
  meta.viewerLinks,
4976
- meta.viewerFilePath
6019
+ meta.viewerFilePath,
6020
+ typeof meta.content === "string" ? meta.content : null,
6021
+ meta.rawInput ?? void 0,
6022
+ meta.diffStats
4977
6023
  );
4978
6024
  }
4979
- async handlePlan(sessionId, content, verbosity) {
6025
+ async handlePlan(sessionId, content, _verbosity) {
4980
6026
  const threadId = this.getThreadId(sessionId);
4981
6027
  const meta = content.metadata ?? {};
4982
6028
  const entries = meta.entries ?? [];
4983
- const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
6029
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:plan", sessionId, entryCount: entries.length });
6030
+ const mode = this.outputModeResolver.resolve(
6031
+ this.context.configManager,
6032
+ this.name,
6033
+ sessionId,
6034
+ this.core.sessionManager
6035
+ );
6036
+ const tracker = this.getOrCreateTracker(sessionId, threadId, mode);
4984
6037
  await tracker.onPlan(
4985
6038
  entries.map((e) => ({
4986
6039
  content: e.content,
@@ -4992,15 +6045,27 @@ ${lines.join("\n")}`;
4992
6045
  async handleUsage(sessionId, content, verbosity) {
4993
6046
  const threadId = this.getThreadId(sessionId);
4994
6047
  const meta = content.metadata;
6048
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:usage", sessionId, tokensUsed: meta?.tokensUsed, contextSize: meta?.contextSize, cost: meta?.cost });
4995
6049
  await this.draftManager.finalize(sessionId, this.assistantSession?.id);
4996
- const tracker = this.getOrCreateTracker(sessionId, threadId, verbosity);
4997
- await tracker.sendUsage(meta ?? {});
6050
+ const usageText = formatUsage(meta ?? {}, verbosity);
6051
+ let usageMsgId;
6052
+ try {
6053
+ const result = await this.sendQueue.enqueue(
6054
+ () => this.bot.api.sendMessage(this.telegramConfig.chatId, usageText, {
6055
+ message_thread_id: threadId,
6056
+ parse_mode: "HTML",
6057
+ disable_notification: true
6058
+ })
6059
+ );
6060
+ usageMsgId = result?.message_id;
6061
+ } catch (err) {
6062
+ log12.warn({ err, sessionId }, "Failed to send usage message");
6063
+ }
4998
6064
  if (this.notificationTopicId && sessionId !== this.assistantSession?.id) {
4999
6065
  const sess = this.core.sessionManager.getSession(sessionId);
5000
6066
  const sessionName = sess?.name || "Session";
5001
6067
  const chatIdStr = String(this.telegramConfig.chatId);
5002
6068
  const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
5003
- const usageMsgId = tracker.getUsageMsgId();
5004
6069
  const deepLink = usageMsgId ? `https://t.me/c/${numericId}/${threadId}/${usageMsgId}` : `https://t.me/c/${numericId}/${threadId}`;
5005
6070
  const text = `\u2705 <b>${escapeHtml(sessionName)}</b>
5006
6071
  Task completed.
@@ -5017,6 +6082,7 @@ Task completed.
5017
6082
  }
5018
6083
  }
5019
6084
  async handleAttachment(sessionId, content) {
6085
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:attachment", sessionId, type: content.attachment?.type, fileName: content.attachment?.fileName });
5020
6086
  const threadId = this.getThreadId(sessionId);
5021
6087
  if (!content.attachment) return;
5022
6088
  const { attachment } = content;
@@ -5072,6 +6138,7 @@ Task completed.
5072
6138
  }
5073
6139
  }
5074
6140
  async handleSessionEnd(sessionId, _content) {
6141
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:sessionEnd", sessionId });
5075
6142
  const threadId = this.getThreadId(sessionId);
5076
6143
  await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5077
6144
  this.draftManager.cleanup(sessionId);
@@ -5091,6 +6158,7 @@ Task completed.
5091
6158
  }
5092
6159
  }
5093
6160
  async handleError(sessionId, content) {
6161
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:error", sessionId, text: content.text });
5094
6162
  const threadId = this.getThreadId(sessionId);
5095
6163
  await this.draftManager.finalize(sessionId, this.assistantSession?.id);
5096
6164
  const tracker = this.sessionTrackers.get(sessionId);
@@ -5111,6 +6179,7 @@ Task completed.
5111
6179
  );
5112
6180
  }
5113
6181
  async handleSystem(sessionId, content) {
6182
+ this.getTracer(sessionId)?.log("telegram", { action: "handle:system", sessionId, text: content.text });
5114
6183
  const threadId = this.getThreadId(sessionId);
5115
6184
  await this.sendQueue.enqueue(
5116
6185
  () => this.bot.api.sendMessage(
@@ -5125,6 +6194,7 @@ Task completed.
5125
6194
  );
5126
6195
  }
5127
6196
  async sendPermissionRequest(sessionId, request) {
6197
+ this.getTracer(sessionId)?.log("telegram", { action: "permission:send", sessionId, requestId: request.id, description: request.description });
5128
6198
  log12.info({ sessionId, requestId: request.id }, "Permission request sent");
5129
6199
  const session = this.core.sessionManager.getSession(sessionId);
5130
6200
  if (!session) return;
@@ -5133,6 +6203,7 @@ Task completed.
5133
6203
  );
5134
6204
  }
5135
6205
  async sendNotification(notification) {
6206
+ this.getTracer(notification.sessionId)?.log("telegram", { action: "notification:send", sessionId: notification.sessionId, type: notification.type });
5136
6207
  if (notification.sessionId === this.assistantSession?.id) return;
5137
6208
  log12.info(
5138
6209
  { sessionId: notification.sessionId, type: notification.type },
@@ -5172,12 +6243,14 @@ Task completed.
5172
6243
  );
5173
6244
  }
5174
6245
  async createSessionThread(sessionId, name) {
6246
+ this.getTracer(sessionId)?.log("telegram", { action: "thread:create", sessionId, name });
5175
6247
  log12.info({ sessionId, name }, "Session topic created");
5176
6248
  return String(
5177
6249
  await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
5178
6250
  );
5179
6251
  }
5180
6252
  async renameSessionThread(sessionId, newName) {
6253
+ this.getTracer(sessionId)?.log("telegram", { action: "thread:rename", sessionId, newName });
5181
6254
  const session = this.core.sessionManager.getSession(sessionId);
5182
6255
  if (!session) return;
5183
6256
  await renameSessionTopic(
@@ -5283,10 +6356,14 @@ Task completed.
5283
6356
  async cleanupSkillCommands(sessionId) {
5284
6357
  await this.skillManager.cleanup(sessionId);
5285
6358
  }
6359
+ async stripTTSBlock(sessionId) {
6360
+ await this.draftManager.stripPattern(sessionId, /\[TTS\][\s\S]*?\[\/TTS\]/g);
6361
+ }
5286
6362
  async archiveSessionTopic(sessionId) {
6363
+ this.getTracer(sessionId)?.log("telegram", { action: "thread:archive", sessionId });
5287
6364
  const core = this.core;
5288
6365
  const session = core.sessionManager.getSession(sessionId);
5289
- if (!session) return;
6366
+ if (!session) throw new Error("Session not found");
5290
6367
  const chatId = this.telegramConfig.chatId;
5291
6368
  const oldTopicId = Number(session.threadId);
5292
6369
  session.archiving = true;
@@ -5299,12 +6376,29 @@ Task completed.
5299
6376
  this.sessionTrackers.delete(session.id);
5300
6377
  }
5301
6378
  await deleteSessionTopic(this.bot, chatId, oldTopicId);
6379
+ const topicName = session.name ?? `Session ${session.id.slice(0, 6)}`;
6380
+ const newTopicId = await createSessionTopic(this.bot, chatId, topicName);
6381
+ session.archiving = false;
6382
+ return String(newTopicId);
5302
6383
  }
5303
6384
  };
5304
6385
 
5305
6386
  export {
6387
+ STATUS_ICONS,
6388
+ KIND_ICONS,
6389
+ progressBar,
6390
+ formatTokens,
6391
+ stripCodeFences,
6392
+ truncateContent,
6393
+ splitMessage,
6394
+ extractContentText,
6395
+ formatToolSummary,
6396
+ formatToolTitle,
6397
+ resolveToolIcon,
5306
6398
  PRODUCT_GUIDE,
5307
6399
  SendQueue,
6400
+ MessagingAdapter,
6401
+ BaseRenderer,
5308
6402
  TelegramAdapter
5309
6403
  };
5310
- //# sourceMappingURL=chunk-XWDW3XBE.js.map
6404
+ //# sourceMappingURL=chunk-5ZNBNIK3.js.map