@openacp/cli 2026.327.5 → 2026.330.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +13 -13
  2. package/dist/adapter-4U6MC5ZS.js +13 -0
  3. package/dist/agent-catalog-SZQQERV7.js +10 -0
  4. package/dist/{agent-dependencies-WS7Z2DFW.js → agent-dependencies-ED2ZTUHG.js} +1 -2
  5. package/dist/{agent-registry-5LZT7CUB.js → agent-registry-YOGP656W.js} +1 -2
  6. package/dist/agent-store-5UHZH2XI.js +8 -0
  7. package/dist/{api-client-AQPNKXI2.js → api-client-XTLRRFPX.js} +1 -2
  8. package/dist/api-server-5VNYFWJE.js +7 -0
  9. package/dist/api-server-JLBDKCU4.js +10 -0
  10. package/dist/{autostart-6JS565RY.js → autostart-CUPZMKKC.js} +3 -4
  11. package/dist/{chunk-WIIZNPCR.js → chunk-2KT6TROD.js} +12 -33
  12. package/dist/chunk-2KT6TROD.js.map +1 -0
  13. package/dist/{chunk-PPSMUECX.js → chunk-2R5XM3ES.js} +2 -2
  14. package/dist/{chunk-SNPYTMPR.js → chunk-3EWTPOF7.js} +2 -2
  15. package/dist/{chunk-YEULD3SG.js → chunk-3NAFXVQM.js} +7 -2
  16. package/dist/{chunk-YEULD3SG.js.map → chunk-3NAFXVQM.js.map} +1 -1
  17. package/dist/{chunk-A6Y4GZM3.js → chunk-566W6INH.js} +2 -2
  18. package/dist/{chunk-ODUM3D6X.js → chunk-5HKQCYOI.js} +1 -39
  19. package/dist/chunk-5HKQCYOI.js.map +1 -0
  20. package/dist/{plugin-installer-QVJP6VKV.js → chunk-5WGVYX3C.js} +18 -5
  21. package/dist/chunk-5WGVYX3C.js.map +1 -0
  22. package/dist/{chunk-XWDW3XBE.js → chunk-5ZNBNIK3.js} +1331 -237
  23. package/dist/chunk-5ZNBNIK3.js.map +1 -0
  24. package/dist/{chunk-XIBG7LSL.js → chunk-7RKPIM3E.js} +482 -175
  25. package/dist/chunk-7RKPIM3E.js.map +1 -0
  26. package/dist/{chunk-WXVT3AOY.js → chunk-7ZCQF6QM.js} +8 -3
  27. package/dist/chunk-7ZCQF6QM.js.map +1 -0
  28. package/dist/{chunk-2YCW3QDV.js → chunk-BTJHGSLM.js} +8 -7
  29. package/dist/chunk-BTJHGSLM.js.map +1 -0
  30. package/dist/{chunk-P2G275VD.js → chunk-CFUJGWOP.js} +3 -3
  31. package/dist/{chunk-RBYBSSGO.js → chunk-FCTC7KDT.js} +2 -2
  32. package/dist/{chunk-QAQDGPB4.js → chunk-GEOXPGCO.js} +3 -3
  33. package/dist/{chunk-BLQUXO7S.js → chunk-IZ5UEZF7.js} +27 -2
  34. package/dist/chunk-IZ5UEZF7.js.map +1 -0
  35. package/dist/{chunk-4GMLGCF2.js → chunk-KDU3ZEWT.js} +2 -2
  36. package/dist/{chunk-QWP76EBW.js → chunk-MITTQMGZ.js} +16 -9
  37. package/dist/chunk-MITTQMGZ.js.map +1 -0
  38. package/dist/{chunk-HRKAXFWR.js → chunk-MPGEHTGE.js} +9 -9
  39. package/dist/{chunk-KMMEFXIE.js → chunk-PA6MNBG4.js} +41 -9
  40. package/dist/chunk-PA6MNBG4.js.map +1 -0
  41. package/dist/{chunk-BQ6FR32N.js → chunk-QWVHCTCA.js} +2 -2
  42. package/dist/{chunk-XMMAGAT4.js → chunk-R6KZYF7D.js} +8 -1
  43. package/dist/{chunk-XMMAGAT4.js.map → chunk-R6KZYF7D.js.map} +1 -1
  44. package/dist/{chunk-AD3X6DGK.js → chunk-TMVTSWVH.js} +75 -13
  45. package/dist/chunk-TMVTSWVH.js.map +1 -0
  46. package/dist/chunk-UWH7KIAA.js +701 -0
  47. package/dist/chunk-UWH7KIAA.js.map +1 -0
  48. package/dist/{chunk-SHTGQGAU.js → chunk-V2YZWYXT.js} +3 -3
  49. package/dist/{chunk-QVMEF6FB.js → chunk-W4LK6WJP.js} +38 -27
  50. package/dist/chunk-W4LK6WJP.js.map +1 -0
  51. package/dist/{chunk-S3ZGPPXY.js → chunk-YIGBJFJL.js} +9 -13
  52. package/dist/{chunk-S3ZGPPXY.js.map → chunk-YIGBJFJL.js.map} +1 -1
  53. package/dist/cli.js +133 -132
  54. package/dist/cli.js.map +1 -1
  55. package/dist/config-KN6NKKPF.js +20 -0
  56. package/dist/config-editor-76RVZS4B.js +10 -0
  57. package/dist/{config-registry-CUMNXFGK.js → config-registry-ZXAIJNYB.js} +2 -3
  58. package/dist/context-NXXW62NJ.js +9 -0
  59. package/dist/core-plugins-OCHKGCIZ.js +22 -0
  60. package/dist/{daemon-PXO5QPCR.js → daemon-XFEMMJSZ.js} +4 -5
  61. package/dist/{dev-loader-DRU3R7ZM.js → dev-loader-7P3HZCIA.js} +1 -3
  62. package/dist/{dev-loader-DRU3R7ZM.js.map → dev-loader-7P3HZCIA.js.map} +1 -1
  63. package/dist/doctor-AV6AUO22.js +9 -0
  64. package/dist/file-service-HHB3JQIO.js +8 -0
  65. package/dist/index.d.ts +141 -187
  66. package/dist/index.js +33 -29
  67. package/dist/index.js.map +1 -1
  68. package/dist/{install-cloudflared-AN24L4DP.js → install-cloudflared-JRJ4BSOM.js} +3 -4
  69. package/dist/{install-cloudflared-AN24L4DP.js.map → install-cloudflared-JRJ4BSOM.js.map} +1 -1
  70. package/dist/{install-context-XPWTFT3J.js → install-context-EHYV5WRY.js} +2 -3
  71. package/dist/{install-context-XPWTFT3J.js.map → install-context-EHYV5WRY.js.map} +1 -1
  72. package/dist/{install-jq-CRVDJGF3.js → install-jq-ISTGT263.js} +3 -4
  73. package/dist/{install-jq-CRVDJGF3.js.map → install-jq-ISTGT263.js.map} +1 -1
  74. package/dist/{integrate-G6CVXTGT.js → integrate-JIEZYDOR.js} +1 -2
  75. package/dist/{integrate-G6CVXTGT.js.map → integrate-JIEZYDOR.js.map} +1 -1
  76. package/dist/{log-LZ7FTRKG.js → log-YZ243M5G.js} +4 -3
  77. package/dist/{main-3GF3EQTE.js → main-L2M4NTJY.js} +135 -50
  78. package/dist/main-L2M4NTJY.js.map +1 -0
  79. package/dist/{menu-YDQ2LWAR.js → menu-ALFN37IR.js} +1 -2
  80. package/dist/notifications-MO23S7S3.js +8 -0
  81. package/dist/{plugin-create-5HQRF2ID.js → plugin-create-EHL76ZZG.js} +1 -2
  82. package/dist/{plugin-create-5HQRF2ID.js.map → plugin-create-EHL76ZZG.js.map} +1 -1
  83. package/dist/plugin-installer-VSTYZSXC.js +9 -0
  84. package/dist/{plugin-registry-WB3DR67H.js → plugin-registry-6J3YSFHF.js} +1 -2
  85. package/dist/{plugin-search-HQ4WQKOF.js → plugin-search-MGKAL5JM.js} +1 -2
  86. package/dist/{plugin-search-HQ4WQKOF.js.map → plugin-search-MGKAL5JM.js.map} +1 -1
  87. package/dist/{post-upgrade-3ADZRMYJ.js → post-upgrade-Y26S2ZQ7.js} +6 -7
  88. package/dist/{post-upgrade-3ADZRMYJ.js.map → post-upgrade-Y26S2ZQ7.js.map} +1 -1
  89. package/dist/{read-text-file-IRZM3QLM.js → read-text-file-DJBTITIB.js} +1 -2
  90. package/dist/{registry-client-AVGRE4CF.js → registry-client-GTBWLXYU.js} +1 -2
  91. package/dist/{security-YNRBW6S7.js → security-2BA265LN.js} +1 -2
  92. package/dist/{settings-manager-MD2U4ZV2.js → settings-manager-B4UN2LAC.js} +1 -2
  93. package/dist/{setup-A7VPW46C.js → setup-E6BNEYCS.js} +109 -83
  94. package/dist/setup-E6BNEYCS.js.map +1 -0
  95. package/dist/speech-SG62JYIF.js +9 -0
  96. package/dist/{suggest-7D6B542M.js → suggest-RST5VOHB.js} +1 -3
  97. package/dist/{suggest-7D6B542M.js.map → suggest-RST5VOHB.js.map} +1 -1
  98. package/dist/telegram-EAVRDNFU.js +7 -0
  99. package/dist/tunnel-HWJ27WDH.js +7 -0
  100. package/dist/{tunnel-service-QJPUYEKU.js → tunnel-service-ZMO4THKE.js} +90 -11
  101. package/dist/tunnel-service-ZMO4THKE.js.map +1 -0
  102. package/dist/{validators-WSTBNKRW.js → validators-GITLOFXC.js} +1 -2
  103. package/dist/{version-NQZBM5M7.js → version-AXXV6IV2.js} +1 -2
  104. package/package.json +1 -3
  105. package/dist/adapter-JQFQ3JAO.js +0 -15
  106. package/dist/adapter-UORRGHNH.js +0 -1030
  107. package/dist/adapter-UORRGHNH.js.map +0 -1
  108. package/dist/agent-catalog-YHBFERYO.js +0 -11
  109. package/dist/agent-store-VSHNY5GT.js +0 -9
  110. package/dist/api-server-7G3ZUZRM.js +0 -8
  111. package/dist/api-server-CAYNPUF2.js +0 -11
  112. package/dist/chunk-2YCW3QDV.js.map +0 -1
  113. package/dist/chunk-32LVIEPW.js +0 -477
  114. package/dist/chunk-32LVIEPW.js.map +0 -1
  115. package/dist/chunk-AD3X6DGK.js.map +0 -1
  116. package/dist/chunk-BLQUXO7S.js.map +0 -1
  117. package/dist/chunk-KMMEFXIE.js.map +0 -1
  118. package/dist/chunk-ODUM3D6X.js.map +0 -1
  119. package/dist/chunk-QVMEF6FB.js.map +0 -1
  120. package/dist/chunk-QWP76EBW.js.map +0 -1
  121. package/dist/chunk-VUNV25KB.js +0 -16
  122. package/dist/chunk-WIIZNPCR.js.map +0 -1
  123. package/dist/chunk-WXVT3AOY.js.map +0 -1
  124. package/dist/chunk-XIBG7LSL.js.map +0 -1
  125. package/dist/chunk-XWDW3XBE.js.map +0 -1
  126. package/dist/chunk-ZHGPZBS4.js +0 -49
  127. package/dist/chunk-ZHGPZBS4.js.map +0 -1
  128. package/dist/chunk-ZNSO2QVC.js +0 -124
  129. package/dist/chunk-ZNSO2QVC.js.map +0 -1
  130. package/dist/config-I4FMCJGZ.js +0 -15
  131. package/dist/config-editor-7PKW42GZ.js +0 -11
  132. package/dist/context-XM6E22LM.js +0 -10
  133. package/dist/core-plugins-Y5US6RED.js +0 -23
  134. package/dist/dist-UHQK5CXN.js +0 -21151
  135. package/dist/dist-UHQK5CXN.js.map +0 -1
  136. package/dist/doctor-QZQAP46W.js +0 -10
  137. package/dist/file-service-EUODJAIT.js +0 -9
  138. package/dist/main-3GF3EQTE.js.map +0 -1
  139. package/dist/notifications-D5BRDNSU.js +0 -9
  140. package/dist/plugin-installer-QVJP6VKV.js.map +0 -1
  141. package/dist/setup-A7VPW46C.js.map +0 -1
  142. package/dist/slack-2XNWBOWH.js +0 -8
  143. package/dist/speech-2GHQNRIO.js +0 -9
  144. package/dist/telegram-E65IWFBW.js +0 -8
  145. package/dist/tunnel-45HA72MB.js +0 -8
  146. package/dist/tunnel-service-QJPUYEKU.js.map +0 -1
  147. package/dist/version-NQZBM5M7.js.map +0 -1
  148. /package/dist/{adapter-JQFQ3JAO.js.map → adapter-4U6MC5ZS.js.map} +0 -0
  149. /package/dist/{agent-catalog-YHBFERYO.js.map → agent-catalog-SZQQERV7.js.map} +0 -0
  150. /package/dist/{agent-dependencies-WS7Z2DFW.js.map → agent-dependencies-ED2ZTUHG.js.map} +0 -0
  151. /package/dist/{agent-registry-5LZT7CUB.js.map → agent-registry-YOGP656W.js.map} +0 -0
  152. /package/dist/{agent-store-VSHNY5GT.js.map → agent-store-5UHZH2XI.js.map} +0 -0
  153. /package/dist/{api-client-AQPNKXI2.js.map → api-client-XTLRRFPX.js.map} +0 -0
  154. /package/dist/{api-server-7G3ZUZRM.js.map → api-server-5VNYFWJE.js.map} +0 -0
  155. /package/dist/{api-server-CAYNPUF2.js.map → api-server-JLBDKCU4.js.map} +0 -0
  156. /package/dist/{autostart-6JS565RY.js.map → autostart-CUPZMKKC.js.map} +0 -0
  157. /package/dist/{chunk-PPSMUECX.js.map → chunk-2R5XM3ES.js.map} +0 -0
  158. /package/dist/{chunk-SNPYTMPR.js.map → chunk-3EWTPOF7.js.map} +0 -0
  159. /package/dist/{chunk-A6Y4GZM3.js.map → chunk-566W6INH.js.map} +0 -0
  160. /package/dist/{chunk-P2G275VD.js.map → chunk-CFUJGWOP.js.map} +0 -0
  161. /package/dist/{chunk-RBYBSSGO.js.map → chunk-FCTC7KDT.js.map} +0 -0
  162. /package/dist/{chunk-QAQDGPB4.js.map → chunk-GEOXPGCO.js.map} +0 -0
  163. /package/dist/{chunk-4GMLGCF2.js.map → chunk-KDU3ZEWT.js.map} +0 -0
  164. /package/dist/{chunk-HRKAXFWR.js.map → chunk-MPGEHTGE.js.map} +0 -0
  165. /package/dist/{chunk-BQ6FR32N.js.map → chunk-QWVHCTCA.js.map} +0 -0
  166. /package/dist/{chunk-SHTGQGAU.js.map → chunk-V2YZWYXT.js.map} +0 -0
  167. /package/dist/{chunk-VUNV25KB.js.map → config-KN6NKKPF.js.map} +0 -0
  168. /package/dist/{config-I4FMCJGZ.js.map → config-editor-76RVZS4B.js.map} +0 -0
  169. /package/dist/{config-editor-7PKW42GZ.js.map → config-registry-ZXAIJNYB.js.map} +0 -0
  170. /package/dist/{config-registry-CUMNXFGK.js.map → context-NXXW62NJ.js.map} +0 -0
  171. /package/dist/{context-XM6E22LM.js.map → core-plugins-OCHKGCIZ.js.map} +0 -0
  172. /package/dist/{core-plugins-Y5US6RED.js.map → daemon-XFEMMJSZ.js.map} +0 -0
  173. /package/dist/{daemon-PXO5QPCR.js.map → doctor-AV6AUO22.js.map} +0 -0
  174. /package/dist/{doctor-QZQAP46W.js.map → file-service-HHB3JQIO.js.map} +0 -0
  175. /package/dist/{file-service-EUODJAIT.js.map → log-YZ243M5G.js.map} +0 -0
  176. /package/dist/{log-LZ7FTRKG.js.map → menu-ALFN37IR.js.map} +0 -0
  177. /package/dist/{menu-YDQ2LWAR.js.map → notifications-MO23S7S3.js.map} +0 -0
  178. /package/dist/{notifications-D5BRDNSU.js.map → plugin-installer-VSTYZSXC.js.map} +0 -0
  179. /package/dist/{plugin-registry-WB3DR67H.js.map → plugin-registry-6J3YSFHF.js.map} +0 -0
  180. /package/dist/{read-text-file-IRZM3QLM.js.map → read-text-file-DJBTITIB.js.map} +0 -0
  181. /package/dist/{registry-client-AVGRE4CF.js.map → registry-client-GTBWLXYU.js.map} +0 -0
  182. /package/dist/{security-YNRBW6S7.js.map → security-2BA265LN.js.map} +0 -0
  183. /package/dist/{settings-manager-MD2U4ZV2.js.map → settings-manager-B4UN2LAC.js.map} +0 -0
  184. /package/dist/{slack-2XNWBOWH.js.map → speech-SG62JYIF.js.map} +0 -0
  185. /package/dist/{speech-2GHQNRIO.js.map → telegram-EAVRDNFU.js.map} +0 -0
  186. /package/dist/{telegram-E65IWFBW.js.map → tunnel-HWJ27WDH.js.map} +0 -0
  187. /package/dist/{tunnel-45HA72MB.js.map → validators-GITLOFXC.js.map} +0 -0
  188. /package/dist/{validators-WSTBNKRW.js.map → version-AXXV6IV2.js.map} +0 -0
@@ -1,1030 +0,0 @@
1
- import {
2
- BaseRenderer,
3
- MessagingAdapter
4
- } from "./chunk-32LVIEPW.js";
5
- import {
6
- createChildLogger
7
- } from "./chunk-XMMAGAT4.js";
8
- import "./chunk-VUNV25KB.js";
9
-
10
- // src/plugins/slack/adapter.ts
11
- import fs from "fs";
12
- import { App } from "@slack/bolt";
13
- import { WebClient } from "@slack/web-api";
14
-
15
- // src/plugins/slack/utils.ts
16
- function isAudioClip(file) {
17
- return file.mimetype === "video/mp4" && file.name?.startsWith("audio_message") || file.mimetype?.startsWith("audio/");
18
- }
19
- var SECTION_LIMIT = 3e3;
20
- function splitSafe(text, limit = SECTION_LIMIT) {
21
- if (text.length <= limit) return [text];
22
- const chunks = [];
23
- let remaining = text;
24
- while (remaining.length > 0) {
25
- if (remaining.length <= limit) {
26
- chunks.push(remaining);
27
- break;
28
- }
29
- let cut = remaining.lastIndexOf("\n", limit);
30
- if (cut <= 0) cut = limit;
31
- chunks.push(remaining.slice(0, cut));
32
- remaining = remaining.slice(cut).trimStart();
33
- }
34
- return chunks;
35
- }
36
-
37
- // src/plugins/slack/formatter.ts
38
- function markdownToMrkdwn(text) {
39
- return text.replace(/^#{1,6}\s+(.+)$/gm, "\0BOLD\0$1\0BOLD\0").replace(/\*\*(.+?)\*\*/g, "\0BOLD\0$1\0BOLD\0").replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "_$1_").replace(/\x00BOLD\x00(.+?)\x00BOLD\x00/g, "*$1*").replace(/~~(.+?)~~/g, "~$1~").replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "<$2|$1>").replace(/^[ \t]*[-*]\s+/gm, "\u2022 ").trim();
40
- }
41
- var SECTION_LIMIT2 = 3e3;
42
- function section(text) {
43
- return { type: "section", text: { type: "mrkdwn", text: text.slice(0, SECTION_LIMIT2) } };
44
- }
45
- function context(text) {
46
- return { type: "context", elements: [{ type: "mrkdwn", text }] };
47
- }
48
- var SlackFormatter = class {
49
- formatOutgoing(message) {
50
- switch (message.type) {
51
- case "text": {
52
- const text = message.text ?? "";
53
- if (!text.trim()) return [];
54
- const converted = markdownToMrkdwn(text);
55
- return splitSafe(converted).map((chunk) => section(chunk));
56
- }
57
- case "thought":
58
- return [context(`\u{1F4AD} _${(message.text ?? "").slice(0, 500)}_`)];
59
- case "tool_call": {
60
- const name = message.metadata?.name ?? "tool";
61
- const input = message.metadata?.input;
62
- const inputStr = input ? `
63
- \`\`\`
64
- ${JSON.stringify(input, null, 2).slice(0, 500)}
65
- \`\`\`` : "";
66
- return [context(`\u{1F527} \`${name}\`${inputStr}`)];
67
- }
68
- case "tool_update": {
69
- const name = message.metadata?.name ?? "tool";
70
- const status = message.metadata?.status ?? "done";
71
- const icon = status === "error" ? "\u274C" : "\u2705";
72
- return [context(`${icon} \`${name}\` \u2014 ${status}`)];
73
- }
74
- case "plan":
75
- return [
76
- { type: "divider" },
77
- section(`\u{1F4CB} *Plan*
78
- ${message.text ?? ""}`)
79
- ];
80
- case "usage": {
81
- const meta = message.metadata ?? {};
82
- const parts = [
83
- meta.input_tokens != null ? `in: ${meta.input_tokens}` : null,
84
- meta.output_tokens != null ? `out: ${meta.output_tokens}` : null,
85
- meta.cost_usd != null ? `$${Number(meta.cost_usd).toFixed(4)}` : null
86
- ].filter((p) => p !== null);
87
- return parts.length ? [context(`\u{1F4CA} ${parts.join(" \xB7 ")}`)] : [];
88
- }
89
- case "session_end":
90
- return this.formatSessionEnd(message.text);
91
- case "error":
92
- return [section(`\u26A0\uFE0F *Error:* ${message.text ?? "Unknown error"}`)];
93
- default:
94
- return [];
95
- }
96
- }
97
- formatPermissionRequest(req) {
98
- return [
99
- section(`\u{1F510} *Permission Request*
100
- ${req.description}`),
101
- {
102
- type: "actions",
103
- block_id: `perm_${req.id}`,
104
- elements: req.options.map((opt) => ({
105
- type: "button",
106
- text: { type: "plain_text", text: opt.label, emoji: true },
107
- value: `${req.id}:${opt.id}`,
108
- action_id: `perm_action_${opt.id}_${req.id}`,
109
- style: opt.isAllow ? "primary" : "danger"
110
- }))
111
- }
112
- ];
113
- }
114
- formatNotification(text) {
115
- return [section(text)];
116
- }
117
- formatSessionEnd(reason) {
118
- return [
119
- { type: "divider" },
120
- context(`\u2705 Session ended${reason ? ` \u2014 ${reason}` : ""}`)
121
- ];
122
- }
123
- };
124
-
125
- // src/plugins/slack/renderer.ts
126
- var SlackRenderer = class extends BaseRenderer {
127
- formatter;
128
- constructor(formatter) {
129
- super();
130
- this.formatter = formatter ?? new SlackFormatter();
131
- }
132
- renderText(content) {
133
- const blocks = this.formatter.formatOutgoing(content);
134
- return {
135
- body: content.text ?? "",
136
- format: "structured",
137
- components: blocks
138
- };
139
- }
140
- renderThought(content, _verbosity) {
141
- const blocks = this.formatter.formatOutgoing(content);
142
- return {
143
- body: content.text ?? "",
144
- format: "structured",
145
- components: blocks
146
- };
147
- }
148
- renderToolCall(content, _verbosity) {
149
- const blocks = this.formatter.formatOutgoing(content);
150
- return {
151
- body: content.text ?? "",
152
- format: "structured",
153
- components: blocks
154
- };
155
- }
156
- renderToolUpdate(content, _verbosity) {
157
- const blocks = this.formatter.formatOutgoing(content);
158
- return {
159
- body: content.text ?? "",
160
- format: "structured",
161
- components: blocks
162
- };
163
- }
164
- renderPlan(content) {
165
- const blocks = this.formatter.formatOutgoing(content);
166
- return {
167
- body: content.text ?? "",
168
- format: "structured",
169
- components: blocks
170
- };
171
- }
172
- renderUsage(content, _verbosity) {
173
- const blocks = this.formatter.formatOutgoing(content);
174
- return {
175
- body: content.text ?? "",
176
- format: "structured",
177
- components: blocks
178
- };
179
- }
180
- renderError(content) {
181
- const blocks = this.formatter.formatOutgoing(content);
182
- return {
183
- body: content.text ?? "",
184
- format: "structured",
185
- components: blocks
186
- };
187
- }
188
- renderSessionEnd(content) {
189
- const blocks = this.formatter.formatSessionEnd(content.text);
190
- return {
191
- body: content.text ?? "Session ended",
192
- format: "structured",
193
- components: blocks
194
- };
195
- }
196
- renderPermission(request) {
197
- const blocks = this.formatter.formatPermissionRequest(request);
198
- return {
199
- body: request.description,
200
- format: "structured",
201
- components: blocks,
202
- actions: request.options.map((o) => ({
203
- id: o.id,
204
- label: o.label,
205
- isAllow: o.isAllow
206
- }))
207
- };
208
- }
209
- renderNotification(notification) {
210
- const emoji = {
211
- completed: "\u2705",
212
- error: "\u274C",
213
- permission: "\u{1F510}",
214
- input_required: "\u{1F4AC}",
215
- budget_warning: "\u26A0\uFE0F"
216
- };
217
- const icon = emoji[notification.type] || "\u2139\uFE0F";
218
- const text = `${icon} *${notification.sessionName ?? "Session"}*
219
- ${notification.summary}`;
220
- const blocks = this.formatter.formatNotification(text);
221
- return {
222
- body: text,
223
- format: "structured",
224
- components: blocks
225
- };
226
- }
227
- renderSystemMessage(content) {
228
- const mrkdwn = markdownToMrkdwn(content.text ?? "");
229
- return {
230
- body: mrkdwn,
231
- format: "structured",
232
- components: [
233
- {
234
- type: "section",
235
- text: { type: "mrkdwn", text: mrkdwn }
236
- }
237
- ]
238
- };
239
- }
240
- renderModeChange(content) {
241
- const modeId = content.metadata?.modeId ?? "";
242
- const text = `\u{1F504} *Mode:* ${modeId}`;
243
- return {
244
- body: text,
245
- format: "structured",
246
- components: [
247
- {
248
- type: "context",
249
- elements: [{ type: "mrkdwn", text }]
250
- }
251
- ]
252
- };
253
- }
254
- renderConfigUpdate() {
255
- const text = "\u2699\uFE0F *Config updated*";
256
- return {
257
- body: text,
258
- format: "structured",
259
- components: [
260
- {
261
- type: "context",
262
- elements: [{ type: "mrkdwn", text }]
263
- }
264
- ]
265
- };
266
- }
267
- renderModelUpdate(content) {
268
- const modelId = content.metadata?.modelId ?? "";
269
- const text = `\u{1F916} *Model:* ${modelId}`;
270
- return {
271
- body: text,
272
- format: "structured",
273
- components: [
274
- {
275
- type: "context",
276
- elements: [{ type: "mrkdwn", text }]
277
- }
278
- ]
279
- };
280
- }
281
- };
282
-
283
- // src/plugins/slack/send-queue.ts
284
- import PQueue from "p-queue";
285
- var METHOD_RPM = {
286
- "chat.postMessage": 50,
287
- // Tier 3
288
- "chat.update": 50,
289
- // Tier 3
290
- "conversations.create": 20,
291
- // Tier 2
292
- "conversations.rename": 20,
293
- // Tier 2
294
- "conversations.archive": 20,
295
- // Tier 2
296
- "conversations.invite": 20,
297
- // Tier 2
298
- "conversations.join": 20,
299
- // Tier 2
300
- "conversations.unarchive": 20,
301
- // Tier 2
302
- "conversations.info": 50
303
- // Tier 3
304
- };
305
- var SlackSendQueue = class {
306
- constructor(client) {
307
- this.client = client;
308
- for (const [method, rpm] of Object.entries(METHOD_RPM)) {
309
- this.queues.set(method, new PQueue({
310
- interval: Math.ceil(6e4 / rpm),
311
- intervalCap: 1,
312
- carryoverConcurrencyCount: true
313
- }));
314
- }
315
- }
316
- queues = /* @__PURE__ */ new Map();
317
- async enqueue(method, params) {
318
- const queue = this.queues.get(method);
319
- if (!queue) throw new Error(`Unknown Slack method: ${method}`);
320
- return queue.add(() => this.client.apiCall(method, params));
321
- }
322
- };
323
-
324
- // src/plugins/slack/slug.ts
325
- import { customAlphabet } from "nanoid";
326
- var nanoidAlpha = customAlphabet("abcdefghijklmnopqrstuvwxyz0123456789", 4);
327
- function toSlug(name, prefix = "openacp") {
328
- const base = name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 60);
329
- const suffix = nanoidAlpha();
330
- return `${prefix}-${base}-${suffix}`.replace(/-+/g, "-");
331
- }
332
-
333
- // src/plugins/slack/channel-manager.ts
334
- var SlackChannelManager = class {
335
- constructor(queue, config) {
336
- this.queue = queue;
337
- this.config = config;
338
- }
339
- async createChannel(sessionId, sessionName) {
340
- let lastError;
341
- for (let attempt = 0; attempt < 3; attempt++) {
342
- const finalSlug = toSlug(sessionName, this.config.channelPrefix ?? "openacp");
343
- try {
344
- const res = await this.queue.enqueue(
345
- "conversations.create",
346
- { name: finalSlug, is_private: true }
347
- );
348
- const channelId = res.channel.id;
349
- const userIds = this.config.allowedUserIds ?? [];
350
- if (userIds.length > 0) {
351
- await this.queue.enqueue("conversations.invite", {
352
- channel: channelId,
353
- users: userIds.join(",")
354
- });
355
- }
356
- return { channelId, channelSlug: finalSlug };
357
- } catch (err) {
358
- if (err?.data?.error === "name_taken" && attempt < 2) {
359
- lastError = err;
360
- continue;
361
- }
362
- throw err;
363
- }
364
- }
365
- throw lastError;
366
- }
367
- async archiveChannel(channelId) {
368
- await this.queue.enqueue("conversations.archive", { channel: channelId });
369
- }
370
- async notifyChannel(text) {
371
- if (this.config.notificationChannelId) {
372
- await this.queue.enqueue("chat.postMessage", {
373
- channel: this.config.notificationChannelId,
374
- text
375
- });
376
- }
377
- }
378
- };
379
-
380
- // src/plugins/slack/permission-handler.ts
381
- var SlackPermissionHandler = class {
382
- constructor(queue, onResponse) {
383
- this.queue = queue;
384
- this.onResponse = onResponse;
385
- }
386
- pendingMessages = /* @__PURE__ */ new Map();
387
- trackPendingMessage(requestId, channelId, messageTs) {
388
- this.pendingMessages.set(requestId, { channelId, messageTs });
389
- }
390
- async cleanupSession(channelId) {
391
- for (const [requestId, info] of this.pendingMessages) {
392
- if (info.channelId !== channelId) continue;
393
- await this.queue.enqueue("chat.update", {
394
- channel: info.channelId,
395
- ts: info.messageTs,
396
- blocks: []
397
- });
398
- this.pendingMessages.delete(requestId);
399
- }
400
- }
401
- register(app) {
402
- app.action(
403
- /^perm_action_/,
404
- async ({ ack, body, action }) => {
405
- await ack();
406
- const value = action.value ?? "";
407
- const colonIdx = value.indexOf(":");
408
- if (colonIdx === -1) return;
409
- const requestId = value.slice(0, colonIdx);
410
- const optionId = value.slice(colonIdx + 1);
411
- this.onResponse(requestId, optionId);
412
- this.pendingMessages.delete(requestId);
413
- const message = body.message;
414
- if (message) {
415
- await this.queue.enqueue("chat.update", {
416
- channel: body.channel?.id ?? "",
417
- ts: message.ts,
418
- text: `\u2705 Permission response: *${optionId}*`,
419
- blocks: []
420
- });
421
- }
422
- }
423
- );
424
- }
425
- };
426
-
427
- // src/plugins/slack/event-router.ts
428
- var log = createChildLogger({ module: "slack-event-router" });
429
- var SlackEventRouter = class {
430
- constructor(sessionLookup, onIncoming, botUserId, notificationChannelId, onNewSession, config, globalAllowedUserIds = []) {
431
- this.sessionLookup = sessionLookup;
432
- this.onIncoming = onIncoming;
433
- this.botUserId = botUserId;
434
- this.notificationChannelId = notificationChannelId;
435
- this.onNewSession = onNewSession;
436
- this.config = config;
437
- this.globalAllowedUserIds = globalAllowedUserIds;
438
- }
439
- isAllowedUser(userId) {
440
- const slackAllowed = this.config.allowedUserIds ?? [];
441
- const allowed = slackAllowed.length > 0 ? slackAllowed : this.globalAllowedUserIds;
442
- if (allowed.length === 0) return true;
443
- return allowed.includes(userId);
444
- }
445
- register(app) {
446
- app.message(async ({ message }) => {
447
- log.debug({ message }, "Slack raw message event");
448
- const msg = message;
449
- if (msg.bot_id) return;
450
- const subtype = msg.subtype;
451
- if (subtype && subtype !== "file_share") return;
452
- const channelId = msg.channel;
453
- const text = msg.text ?? "";
454
- const userId = msg.user ?? "";
455
- const files = msg.files?.map((f) => ({
456
- id: f.id,
457
- name: f.name,
458
- mimetype: f.mimetype,
459
- size: f.size,
460
- url_private: f.url_private
461
- }));
462
- log.debug({ channelId, userId, text }, "Slack message received");
463
- if (userId === this.botUserId) return;
464
- if (!this.isAllowedUser(userId)) {
465
- log.warn({ userId }, "slack: message from non-allowed user rejected");
466
- return;
467
- }
468
- const session = this.sessionLookup(channelId);
469
- if (session) {
470
- log.debug({ channelId, sessionSlug: session.channelSlug }, "Routing to session");
471
- this.onIncoming(session.channelSlug, text, userId, files);
472
- return;
473
- }
474
- log.debug({ channelId, notificationChannelId: this.notificationChannelId }, "No session found for channel");
475
- if (this.notificationChannelId && channelId === this.notificationChannelId) {
476
- this.onNewSession(text, userId);
477
- return;
478
- }
479
- });
480
- }
481
- };
482
-
483
- // src/plugins/slack/text-buffer.ts
484
- var log2 = createChildLogger({ module: "slack-text-buffer" });
485
- var FLUSH_IDLE_MS = 2e3;
486
- var SlackTextBuffer = class {
487
- constructor(channelId, sessionId, queue) {
488
- this.channelId = channelId;
489
- this.sessionId = sessionId;
490
- this.queue = queue;
491
- }
492
- buffer = "";
493
- timer;
494
- flushPromise;
495
- lastMessageTs;
496
- lastPostedText;
497
- append(text) {
498
- if (!text) return;
499
- this.buffer += text;
500
- this.resetTimer();
501
- }
502
- resetTimer() {
503
- if (this.timer) clearTimeout(this.timer);
504
- this.timer = setTimeout(() => {
505
- this.timer = void 0;
506
- this.flush().catch((err) => log2.error({ err, sessionId: this.sessionId }, "Text buffer flush error"));
507
- }, FLUSH_IDLE_MS);
508
- }
509
- async flush() {
510
- if (this.flushPromise) return this.flushPromise;
511
- const text = this.buffer.trim();
512
- if (!text) return;
513
- this.buffer = "";
514
- if (this.timer) {
515
- clearTimeout(this.timer);
516
- this.timer = void 0;
517
- }
518
- this.flushPromise = (async () => {
519
- try {
520
- const converted = markdownToMrkdwn(text);
521
- const chunks = splitSafe(converted);
522
- for (const chunk of chunks) {
523
- if (!chunk.trim()) continue;
524
- const result = await this.queue.enqueue("chat.postMessage", {
525
- channel: this.channelId,
526
- text: chunk,
527
- blocks: [{ type: "section", text: { type: "mrkdwn", text: chunk } }]
528
- });
529
- this.lastMessageTs = result?.ts;
530
- this.lastPostedText = chunk;
531
- }
532
- } finally {
533
- this.flushPromise = void 0;
534
- if (this.buffer.trim()) {
535
- await this.flush();
536
- }
537
- }
538
- })();
539
- return this.flushPromise;
540
- }
541
- destroy() {
542
- if (this.timer) {
543
- clearTimeout(this.timer);
544
- this.timer = void 0;
545
- }
546
- this.buffer = "";
547
- }
548
- /** Remove [TTS]...[/TTS] blocks — from buffer if unflushed, or edit posted message */
549
- async stripTtsBlock() {
550
- if (/\[TTS\][\s\S]*?\[\/TTS\]/.test(this.buffer)) {
551
- this.buffer = this.buffer.replace(/\[TTS\][\s\S]*?\[\/TTS\]/g, "").replace(/\s{2,}/g, " ").trim();
552
- return;
553
- }
554
- if (this.lastMessageTs && this.lastPostedText && /\[TTS\][\s\S]*?\[\/TTS\]/.test(this.lastPostedText)) {
555
- const cleaned = this.lastPostedText.replace(/\[TTS\][\s\S]*?\[\/TTS\]/g, "").replace(/\s{2,}/g, " ").trim();
556
- if (cleaned) {
557
- await this.queue.enqueue("chat.update", {
558
- channel: this.channelId,
559
- ts: this.lastMessageTs,
560
- text: cleaned,
561
- blocks: [{ type: "section", text: { type: "mrkdwn", text: cleaned } }]
562
- });
563
- }
564
- this.lastPostedText = cleaned;
565
- }
566
- }
567
- };
568
-
569
- // src/plugins/slack/adapter.ts
570
- var log3 = createChildLogger({ module: "slack" });
571
- var SlackAdapter = class extends MessagingAdapter {
572
- name = "slack";
573
- renderer;
574
- capabilities = {
575
- streaming: true,
576
- richFormatting: true,
577
- threads: true,
578
- reactions: false,
579
- fileUpload: true,
580
- voice: true
581
- };
582
- core;
583
- app;
584
- webClient;
585
- queue;
586
- formatter;
587
- channelManager;
588
- permissionHandler;
589
- eventRouter;
590
- sessions = /* @__PURE__ */ new Map();
591
- textBuffers = /* @__PURE__ */ new Map();
592
- botUserId = "";
593
- slackConfig;
594
- fileService;
595
- constructor(core, config) {
596
- super(
597
- { configManager: core.configManager },
598
- { ...config, maxMessageLength: 3e3, enabled: config.enabled ?? true }
599
- );
600
- this.core = core;
601
- this.slackConfig = config;
602
- this.formatter = new SlackFormatter();
603
- this.renderer = new SlackRenderer(this.formatter);
604
- }
605
- async start() {
606
- const { botToken, appToken, signingSecret } = this.slackConfig;
607
- if (!botToken || !appToken || !signingSecret) {
608
- throw new Error("Slack adapter requires botToken, appToken, and signingSecret");
609
- }
610
- this.app = new App({
611
- token: botToken,
612
- appToken,
613
- signingSecret,
614
- socketMode: true
615
- });
616
- this.webClient = new WebClient(botToken);
617
- this.queue = new SlackSendQueue(this.webClient);
618
- this.fileService = this.core.fileService;
619
- const authResult = await this.webClient.auth.test();
620
- if (!authResult.user_id) {
621
- throw new Error("Slack auth.test() did not return user_id \u2014 verify botToken is valid");
622
- }
623
- this.botUserId = authResult.user_id;
624
- log3.info({ botUserId: this.botUserId }, "Slack bot authenticated");
625
- this.channelManager = new SlackChannelManager(this.queue, this.slackConfig);
626
- this.permissionHandler = new SlackPermissionHandler(
627
- this.queue,
628
- (requestId, optionId) => {
629
- for (const [sessionId, _meta] of this.sessions) {
630
- const session = this.core.sessionManager.getSession(sessionId);
631
- if (session && session.permissionGate.requestId === requestId) {
632
- session.permissionGate.resolve(optionId);
633
- log3.info({ sessionId, requestId, optionId }, "Permission resolved");
634
- return;
635
- }
636
- }
637
- log3.warn({ requestId, optionId }, "No matching session found for permission response");
638
- }
639
- );
640
- this.permissionHandler.register(this.app);
641
- this.eventRouter = new SlackEventRouter(
642
- (slackChannelId) => {
643
- for (const meta of this.sessions.values()) {
644
- if (meta.channelId === slackChannelId) return meta;
645
- }
646
- return void 0;
647
- },
648
- (sessionChannelSlug, text, userId, files) => {
649
- const processFiles = async () => {
650
- if (!files?.length) return void 0;
651
- const audioFiles = files.filter((f) => isAudioClip(f));
652
- if (!audioFiles.length) return void 0;
653
- const attachments = [];
654
- for (const file of audioFiles) {
655
- const buffer = await this.downloadSlackFile(file.url_private);
656
- if (!buffer) continue;
657
- const mimeType = file.mimetype === "video/mp4" ? "audio/mp4" : file.mimetype;
658
- const sessionId = this.core.sessionManager.getSessionByThread("slack", sessionChannelSlug)?.id;
659
- if (!sessionId) continue;
660
- const att = await this.fileService.saveFile(sessionId, file.name, buffer, mimeType);
661
- attachments.push(att);
662
- }
663
- return attachments.length > 0 ? attachments : void 0;
664
- };
665
- processFiles().then((attachments) => {
666
- this.core.handleMessage({
667
- channelId: "slack",
668
- threadId: sessionChannelSlug,
669
- userId,
670
- text,
671
- attachments
672
- }).catch((err) => log3.error({ err }, "handleMessage error"));
673
- }).catch((err) => log3.error({ err }, "Failed to process audio files"));
674
- },
675
- this.botUserId,
676
- this.slackConfig.notificationChannelId,
677
- // onNewSession: reply with guidance when user messages the notification channel
678
- async (_text, _userId) => {
679
- if (this.slackConfig.notificationChannelId) {
680
- await this.queue.enqueue("chat.postMessage", {
681
- channel: this.slackConfig.notificationChannelId,
682
- text: "\u{1F4AC} To start a new session, use the `/openacp-new` slash command in any channel."
683
- }).catch((err) => log3.warn({ err }, "Failed to send onNewSession reply"));
684
- }
685
- },
686
- this.slackConfig,
687
- this.core.configManager.get().security.allowedUserIds
688
- );
689
- this.eventRouter.register(this.app);
690
- await this.app.start();
691
- log3.info("Slack adapter started (Socket Mode)");
692
- if (this.slackConfig.autoCreateSession !== false) {
693
- await this._createStartupSession();
694
- }
695
- }
696
- async downloadSlackFile(url) {
697
- try {
698
- const resp = await fetch(url, {
699
- headers: { Authorization: `Bearer ${this.slackConfig.botToken}` }
700
- });
701
- if (!resp.ok) {
702
- log3.warn({ status: resp.status }, "Failed to download Slack file");
703
- return null;
704
- }
705
- const contentType = resp.headers.get("content-type") ?? "";
706
- if (contentType.includes("text/html")) {
707
- log3.warn("Slack file download returned HTML instead of binary \u2014 bot likely missing files:read scope. Reinstall the Slack app with files:read scope.");
708
- return null;
709
- }
710
- return Buffer.from(await resp.arrayBuffer());
711
- } catch (err) {
712
- log3.error({ err }, "Error downloading Slack file");
713
- return null;
714
- }
715
- }
716
- async uploadAudioFile(channelId, att) {
717
- const fileBuffer = await fs.promises.readFile(att.filePath);
718
- await this.webClient.files.uploadV2({
719
- channel_id: channelId,
720
- file: fileBuffer,
721
- filename: att.fileName
722
- });
723
- }
724
- async _createStartupSession() {
725
- try {
726
- let reuseChannelId = this.slackConfig.startupChannelId;
727
- if (reuseChannelId) {
728
- try {
729
- const info = await this.queue.enqueue(
730
- "conversations.info",
731
- { channel: reuseChannelId }
732
- );
733
- const channel = info?.channel;
734
- if (!channel || typeof channel.is_archived !== "boolean") {
735
- log3.warn({ reuseChannelId }, "Unexpected conversations.info response shape, creating new channel");
736
- reuseChannelId = void 0;
737
- } else if (channel.is_archived) {
738
- await this.queue.enqueue("conversations.unarchive", { channel: reuseChannelId });
739
- log3.info({ channelId: reuseChannelId }, "Unarchived startup channel for reuse");
740
- }
741
- } catch {
742
- reuseChannelId = void 0;
743
- }
744
- }
745
- if (reuseChannelId) {
746
- let hasSession = false;
747
- for (const m of this.sessions.values()) {
748
- if (m.channelId === reuseChannelId) {
749
- hasSession = true;
750
- break;
751
- }
752
- }
753
- if (!hasSession) {
754
- const session = await this.core.handleNewSession("slack", void 0, void 0, { createThread: false });
755
- const slug = `startup-${session.id.slice(0, 8)}`;
756
- this.sessions.set(session.id, { channelId: reuseChannelId, channelSlug: slug });
757
- session.threadId = slug;
758
- await this.core.sessionManager.patchRecord(session.id, {
759
- platform: { topicId: slug }
760
- });
761
- log3.info({ sessionId: session.id, channelId: reuseChannelId }, "Reused startup channel");
762
- }
763
- } else {
764
- const session = await this.core.handleNewSession("slack", void 0, void 0, { createThread: true });
765
- if (!session.threadId) {
766
- log3.error({ sessionId: session.id }, "Startup session created without threadId");
767
- return;
768
- }
769
- const meta = this.sessions.get(session.id);
770
- if (meta) {
771
- await this.core.configManager.save(
772
- { channels: { slack: { startupChannelId: meta.channelId } } }
773
- );
774
- log3.info({ sessionId: session.id, channelId: meta.channelId }, "Saved startup channel to config");
775
- }
776
- }
777
- if (this.slackConfig.notificationChannelId) {
778
- const startupMeta = [...this.sessions.values()].find(
779
- (m) => m.channelId === (reuseChannelId ?? this.slackConfig.startupChannelId)
780
- );
781
- if (startupMeta) {
782
- await this.queue.enqueue("chat.postMessage", {
783
- channel: this.slackConfig.notificationChannelId,
784
- text: `\u2705 OpenACP ready \u2014 chat with the agent in <#${startupMeta.channelId}>`
785
- });
786
- }
787
- }
788
- } catch (err) {
789
- log3.error({ err }, "Failed to create/reuse Slack startup session");
790
- }
791
- }
792
- async stop() {
793
- for (const [sessionId, buf] of this.textBuffers) {
794
- try {
795
- await buf.flush();
796
- } catch (err) {
797
- log3.warn({ err, sessionId }, "Flush failed during stop");
798
- }
799
- buf.destroy();
800
- }
801
- this.textBuffers.clear();
802
- await this.app.stop();
803
- log3.info("Slack adapter stopped");
804
- }
805
- // --- MessagingAdapter implementations ---
806
- async createSessionThread(sessionId, name) {
807
- const meta = await this.channelManager.createChannel(sessionId, name);
808
- this.sessions.set(sessionId, meta);
809
- log3.info({ sessionId, channelId: meta.channelId, slug: meta.channelSlug }, "Session channel created");
810
- return meta.channelSlug;
811
- }
812
- async renameSessionThread(sessionId, newName) {
813
- const meta = this.sessions.get(sessionId);
814
- if (!meta) return;
815
- const newSlug = toSlug(newName, this.slackConfig.channelPrefix ?? "openacp");
816
- try {
817
- await this.queue.enqueue("conversations.rename", {
818
- channel: meta.channelId,
819
- name: newSlug
820
- });
821
- meta.channelSlug = newSlug;
822
- const session = this.core.sessionManager.getSession(sessionId);
823
- if (session) session.threadId = newSlug;
824
- const existingRecord = this.core.sessionManager.getSessionRecord(sessionId);
825
- await this.core.sessionManager.patchRecord(sessionId, {
826
- name: newName,
827
- platform: { ...existingRecord?.platform ?? {}, topicId: newSlug }
828
- });
829
- log3.info({ sessionId, newSlug }, "Session channel renamed");
830
- } catch (err) {
831
- log3.warn({ err, sessionId }, "Failed to rename Slack channel");
832
- }
833
- }
834
- async deleteSessionThread(sessionId) {
835
- const meta = this.sessions.get(sessionId);
836
- if (!meta) return;
837
- try {
838
- await this.permissionHandler.cleanupSession(meta.channelId);
839
- } catch (err) {
840
- log3.warn({ err, sessionId }, "Failed to clean up permission buttons");
841
- }
842
- try {
843
- await this.channelManager.archiveChannel(meta.channelId);
844
- log3.info({ sessionId, channelId: meta.channelId }, "Session channel archived");
845
- } catch (err) {
846
- log3.warn({ err, sessionId }, "Failed to archive Slack channel");
847
- }
848
- this.sessions.delete(sessionId);
849
- const buf = this.textBuffers.get(sessionId);
850
- if (buf) {
851
- buf.destroy();
852
- this.textBuffers.delete(sessionId);
853
- }
854
- }
855
- _sessionMetas = /* @__PURE__ */ new Map();
856
- getSessionMeta(sessionId) {
857
- return this._sessionMetas.get(sessionId);
858
- }
859
- getTextBuffer(sessionId, channelId) {
860
- let buf = this.textBuffers.get(sessionId);
861
- if (!buf) {
862
- buf = new SlackTextBuffer(channelId, sessionId, this.queue);
863
- this.textBuffers.set(sessionId, buf);
864
- }
865
- return buf;
866
- }
867
- async sendMessage(sessionId, content) {
868
- const meta = this.sessions.get(sessionId);
869
- if (!meta) {
870
- log3.warn({ sessionId }, "No Slack channel for session, skipping message");
871
- return;
872
- }
873
- this._sessionMetas.set(sessionId, meta);
874
- try {
875
- await super.sendMessage(sessionId, content);
876
- } finally {
877
- this._sessionMetas.delete(sessionId);
878
- }
879
- }
880
- // --- Handler overrides (dispatched by base class) ---
881
- async handleText(sessionId, content) {
882
- const meta = this.getSessionMeta(sessionId);
883
- if (!meta) return;
884
- const buf = this.getTextBuffer(sessionId, meta.channelId);
885
- buf.append(content.text ?? "");
886
- }
887
- async handleSessionEnd(sessionId, content) {
888
- const meta = this.getSessionMeta(sessionId);
889
- if (!meta) return;
890
- await this.flushTextBuffer(sessionId);
891
- const blocks = this.formatter.formatOutgoing(content);
892
- if (blocks.length === 0) return;
893
- try {
894
- await this.queue.enqueue("chat.postMessage", {
895
- channel: meta.channelId,
896
- text: content.text ?? content.type,
897
- blocks
898
- });
899
- } catch (err) {
900
- log3.error({ err, sessionId, type: content.type }, "Failed to post Slack message");
901
- }
902
- }
903
- async handleError(sessionId, content) {
904
- const meta = this.getSessionMeta(sessionId);
905
- if (!meta) return;
906
- await this.flushTextBuffer(sessionId);
907
- const blocks = this.formatter.formatOutgoing(content);
908
- if (blocks.length === 0) return;
909
- try {
910
- await this.queue.enqueue("chat.postMessage", {
911
- channel: meta.channelId,
912
- text: content.text ?? content.type,
913
- blocks
914
- });
915
- } catch (err) {
916
- log3.error({ err, sessionId, type: content.type }, "Failed to post Slack message");
917
- }
918
- }
919
- async handleAttachment(sessionId, content) {
920
- const meta = this.getSessionMeta(sessionId);
921
- if (!meta || !content.attachment) return;
922
- if (content.attachment.type === "audio") {
923
- try {
924
- await this.uploadAudioFile(meta.channelId, content.attachment);
925
- const buf = this.textBuffers.get(sessionId);
926
- if (buf) await buf.stripTtsBlock();
927
- } catch (err) {
928
- log3.error({ err, sessionId }, "Failed to upload audio to Slack");
929
- }
930
- }
931
- }
932
- async handleThought(sessionId, content, _verbosity) {
933
- await this.postFormattedMessage(sessionId, content);
934
- }
935
- async handleToolCall(sessionId, content, _verbosity) {
936
- await this.postFormattedMessage(sessionId, content);
937
- }
938
- async handleToolUpdate(sessionId, content, _verbosity) {
939
- await this.postFormattedMessage(sessionId, content);
940
- }
941
- async handlePlan(sessionId, content, _verbosity) {
942
- await this.postFormattedMessage(sessionId, content);
943
- }
944
- async handleUsage(sessionId, content, _verbosity) {
945
- await this.postFormattedMessage(sessionId, content);
946
- }
947
- async handleSystem(sessionId, content) {
948
- await this.postFormattedMessage(sessionId, content);
949
- }
950
- // --- Private helpers ---
951
- async flushTextBuffer(sessionId) {
952
- const buf = this.textBuffers.get(sessionId);
953
- if (buf) {
954
- try {
955
- await buf.flush();
956
- } catch (err) {
957
- log3.warn({ err, sessionId }, "Flush failed on session_end");
958
- }
959
- buf.destroy();
960
- this.textBuffers.delete(sessionId);
961
- }
962
- }
963
- async postFormattedMessage(sessionId, content) {
964
- const meta = this.getSessionMeta(sessionId);
965
- if (!meta) return;
966
- const blocks = this.formatter.formatOutgoing(content);
967
- if (blocks.length === 0) return;
968
- try {
969
- await this.queue.enqueue("chat.postMessage", {
970
- channel: meta.channelId,
971
- text: content.text ?? content.type,
972
- blocks
973
- });
974
- } catch (err) {
975
- log3.error({ err, sessionId, type: content.type }, "Failed to post Slack message");
976
- }
977
- }
978
- // NOTE: Async flow — different from Telegram adapter.
979
- // Telegram: sendPermissionRequest awaits user response inline.
980
- // Slack: posts interactive buttons and returns immediately.
981
- // Resolution happens asynchronously via the Bolt action handler in
982
- // SlackPermissionHandler, which calls the PermissionResponseCallback
983
- // passed during construction. The callback iterates sessions to find
984
- // the matching permissionGate and resolves it.
985
- async sendPermissionRequest(sessionId, request) {
986
- const meta = this.sessions.get(sessionId);
987
- if (!meta) return;
988
- log3.info({ sessionId, requestId: request.id }, "Sending Slack permission request");
989
- const blocks = this.formatter.formatPermissionRequest(request);
990
- try {
991
- const result = await this.queue.enqueue("chat.postMessage", {
992
- channel: meta.channelId,
993
- text: `Permission request: ${request.description}`,
994
- blocks
995
- });
996
- const ts = result?.ts;
997
- if (ts) {
998
- this.permissionHandler.trackPendingMessage(request.id, meta.channelId, ts);
999
- }
1000
- } catch (err) {
1001
- log3.error({ err, sessionId }, "Failed to post Slack permission request");
1002
- }
1003
- }
1004
- async sendNotification(notification) {
1005
- if (!this.slackConfig.notificationChannelId) return;
1006
- const emoji = {
1007
- completed: "\u2705",
1008
- error: "\u274C",
1009
- permission: "\u{1F510}",
1010
- input_required: "\u{1F4AC}"
1011
- };
1012
- const icon = emoji[notification.type] ?? "\u2139\uFE0F";
1013
- const text = `${icon} *${notification.sessionName ?? "Session"}*
1014
- ${notification.summary}`;
1015
- const blocks = this.formatter.formatNotification(text);
1016
- try {
1017
- await this.queue.enqueue("chat.postMessage", {
1018
- channel: this.slackConfig.notificationChannelId,
1019
- text,
1020
- blocks
1021
- });
1022
- } catch (err) {
1023
- log3.warn({ err, sessionId: notification.sessionId }, "Failed to send Slack notification");
1024
- }
1025
- }
1026
- };
1027
- export {
1028
- SlackAdapter
1029
- };
1030
- //# sourceMappingURL=adapter-UORRGHNH.js.map