@geminixiang/mikan 1.0.0-beta.8 → 1.0.0-beta.80

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 (900) hide show
  1. package/CHANGELOG.md +1149 -0
  2. package/README.md +98 -38
  3. package/dist/adapters/commands/admin.d.ts.map +1 -0
  4. package/dist/{commands → adapters/commands}/admin.js +12 -9
  5. package/dist/adapters/commands/admin.js.map +1 -0
  6. package/dist/{commands → adapters/commands}/auto-reply.d.ts +0 -3
  7. package/dist/adapters/commands/auto-reply.d.ts.map +1 -0
  8. package/dist/adapters/commands/auto-reply.js +47 -0
  9. package/dist/adapters/commands/auto-reply.js.map +1 -0
  10. package/dist/adapters/commands/login.d.ts +6 -0
  11. package/dist/adapters/commands/login.d.ts.map +1 -0
  12. package/dist/adapters/commands/login.js +158 -0
  13. package/dist/adapters/commands/login.js.map +1 -0
  14. package/dist/adapters/commands/manifest.d.ts +17 -0
  15. package/dist/adapters/commands/manifest.d.ts.map +1 -0
  16. package/dist/adapters/commands/manifest.js +121 -0
  17. package/dist/adapters/commands/manifest.js.map +1 -0
  18. package/dist/adapters/commands/model.d.ts +9 -0
  19. package/dist/adapters/commands/model.d.ts.map +1 -0
  20. package/dist/adapters/commands/model.js +136 -0
  21. package/dist/adapters/commands/model.js.map +1 -0
  22. package/dist/adapters/commands/new.d.ts.map +1 -0
  23. package/dist/adapters/commands/new.js +39 -0
  24. package/dist/adapters/commands/new.js.map +1 -0
  25. package/dist/adapters/commands/registry.d.ts +4 -0
  26. package/dist/adapters/commands/registry.d.ts.map +1 -0
  27. package/dist/adapters/commands/registry.js +35 -0
  28. package/dist/adapters/commands/registry.js.map +1 -0
  29. package/dist/{commands → adapters/commands}/sandbox.d.ts +0 -1
  30. package/dist/adapters/commands/sandbox.d.ts.map +1 -0
  31. package/dist/adapters/commands/sandbox.js +125 -0
  32. package/dist/adapters/commands/sandbox.js.map +1 -0
  33. package/dist/adapters/commands/session-view.d.ts.map +1 -0
  34. package/dist/{commands → adapters/commands}/session-view.js +21 -16
  35. package/dist/adapters/commands/session-view.js.map +1 -0
  36. package/dist/adapters/commands/types.d.ts +105 -0
  37. package/dist/adapters/commands/types.d.ts.map +1 -0
  38. package/dist/adapters/commands/types.js.map +1 -0
  39. package/dist/adapters/commands/utils.d.ts +11 -0
  40. package/dist/adapters/commands/utils.d.ts.map +1 -0
  41. package/dist/{commands → adapters/commands}/utils.js +15 -1
  42. package/dist/adapters/commands/utils.js.map +1 -0
  43. package/dist/adapters/discord/bot.d.ts +10 -17
  44. package/dist/adapters/discord/bot.d.ts.map +1 -1
  45. package/dist/adapters/discord/bot.js +232 -297
  46. package/dist/adapters/discord/bot.js.map +1 -1
  47. package/dist/adapters/discord/components.d.ts +4 -0
  48. package/dist/adapters/discord/components.d.ts.map +1 -0
  49. package/dist/adapters/discord/components.js +9 -0
  50. package/dist/adapters/discord/components.js.map +1 -0
  51. package/dist/adapters/discord/context.d.ts +3 -7
  52. package/dist/adapters/discord/context.d.ts.map +1 -1
  53. package/dist/adapters/discord/context.js +55 -219
  54. package/dist/adapters/discord/context.js.map +1 -1
  55. package/dist/adapters/discord/format.d.ts +2 -0
  56. package/dist/adapters/discord/format.d.ts.map +1 -0
  57. package/dist/adapters/discord/format.js +47 -0
  58. package/dist/adapters/discord/format.js.map +1 -0
  59. package/dist/adapters/discord/types.d.ts +90 -1
  60. package/dist/adapters/discord/types.d.ts.map +1 -1
  61. package/dist/adapters/discord/types.js.map +1 -1
  62. package/dist/adapters/github/bot.d.ts +25 -9
  63. package/dist/adapters/github/bot.d.ts.map +1 -1
  64. package/dist/adapters/github/bot.js +326 -154
  65. package/dist/adapters/github/bot.js.map +1 -1
  66. package/dist/adapters/github/client.d.ts +35 -23
  67. package/dist/adapters/github/client.d.ts.map +1 -1
  68. package/dist/adapters/github/client.js +121 -28
  69. package/dist/adapters/github/client.js.map +1 -1
  70. package/dist/adapters/github/context.d.ts +3 -8
  71. package/dist/adapters/github/context.d.ts.map +1 -1
  72. package/dist/adapters/github/context.js +64 -131
  73. package/dist/adapters/github/context.js.map +1 -1
  74. package/dist/adapters/github/github-ops.d.ts +22 -0
  75. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  76. package/dist/adapters/github/github-ops.js +277 -0
  77. package/dist/adapters/github/github-ops.js.map +1 -0
  78. package/dist/adapters/github/ids.d.ts +2 -23
  79. package/dist/adapters/github/ids.d.ts.map +1 -1
  80. package/dist/adapters/github/ids.js +6 -36
  81. package/dist/adapters/github/ids.js.map +1 -1
  82. package/dist/adapters/github/repo.d.ts +8 -0
  83. package/dist/adapters/github/repo.d.ts.map +1 -0
  84. package/dist/adapters/github/repo.js +92 -0
  85. package/dist/adapters/github/repo.js.map +1 -0
  86. package/dist/adapters/github/tool-pack.d.ts +4 -0
  87. package/dist/adapters/github/tool-pack.d.ts.map +1 -0
  88. package/dist/adapters/github/tool-pack.js +45 -0
  89. package/dist/adapters/github/tool-pack.js.map +1 -0
  90. package/dist/adapters/github/tools/checks.d.ts +14 -0
  91. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  92. package/dist/adapters/github/tools/checks.js +80 -0
  93. package/dist/adapters/github/tools/checks.js.map +1 -0
  94. package/dist/adapters/github/tools/issue.d.ts +18 -0
  95. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  96. package/dist/adapters/github/tools/issue.js +44 -0
  97. package/dist/adapters/github/tools/issue.js.map +1 -0
  98. package/dist/adapters/github/tools/pr.d.ts +17 -0
  99. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  100. package/dist/adapters/github/tools/pr.js +42 -0
  101. package/dist/adapters/github/tools/pr.js.map +1 -0
  102. package/dist/adapters/github/tools/read.d.ts +17 -0
  103. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  104. package/dist/adapters/github/tools/read.js +134 -0
  105. package/dist/adapters/github/tools/read.js.map +1 -0
  106. package/dist/adapters/github/tools/review-reply.d.ts +14 -0
  107. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  108. package/dist/adapters/github/tools/review-reply.js +34 -0
  109. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  110. package/dist/adapters/github/tools/sync.d.ts +13 -0
  111. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  112. package/dist/adapters/github/tools/sync.js +29 -0
  113. package/dist/adapters/github/tools/sync.js.map +1 -0
  114. package/dist/adapters/github/types.d.ts +197 -18
  115. package/dist/adapters/github/types.d.ts.map +1 -1
  116. package/dist/adapters/github/types.js.map +1 -1
  117. package/dist/adapters/github/webhook.d.ts +6 -0
  118. package/dist/adapters/github/webhook.d.ts.map +1 -0
  119. package/dist/adapters/github/webhook.js +62 -0
  120. package/dist/adapters/github/webhook.js.map +1 -0
  121. package/dist/adapters/intake.d.ts +4 -10
  122. package/dist/adapters/intake.d.ts.map +1 -1
  123. package/dist/adapters/intake.js +74 -31
  124. package/dist/adapters/intake.js.map +1 -1
  125. package/dist/adapters/markdown-tables.d.ts +8 -0
  126. package/dist/adapters/markdown-tables.d.ts.map +1 -0
  127. package/dist/adapters/markdown-tables.js +111 -0
  128. package/dist/adapters/markdown-tables.js.map +1 -0
  129. package/dist/{platform-messages.d.ts → adapters/messages.d.ts} +3 -2
  130. package/dist/adapters/messages.d.ts.map +1 -0
  131. package/dist/{platform-messages.js → adapters/messages.js} +4 -1
  132. package/dist/adapters/messages.js.map +1 -0
  133. package/dist/adapters/progressive-renderer.d.ts +7 -0
  134. package/dist/adapters/progressive-renderer.d.ts.map +1 -0
  135. package/dist/adapters/progressive-renderer.js +423 -0
  136. package/dist/adapters/progressive-renderer.js.map +1 -0
  137. package/dist/adapters/shared.d.ts +24 -53
  138. package/dist/adapters/shared.d.ts.map +1 -1
  139. package/dist/adapters/shared.js +140 -91
  140. package/dist/adapters/shared.js.map +1 -1
  141. package/dist/adapters/slack/assistant.d.ts +23 -0
  142. package/dist/adapters/slack/assistant.d.ts.map +1 -0
  143. package/dist/adapters/slack/assistant.js +109 -0
  144. package/dist/adapters/slack/assistant.js.map +1 -0
  145. package/dist/adapters/slack/auto-reply-context.d.ts +14 -0
  146. package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -0
  147. package/dist/adapters/slack/auto-reply-context.js +21 -0
  148. package/dist/adapters/slack/auto-reply-context.js.map +1 -0
  149. package/dist/adapters/slack/blocks.d.ts +5 -0
  150. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  151. package/dist/adapters/slack/blocks.js +84 -148
  152. package/dist/adapters/slack/blocks.js.map +1 -1
  153. package/dist/adapters/slack/bot.d.ts +51 -39
  154. package/dist/adapters/slack/bot.d.ts.map +1 -1
  155. package/dist/adapters/slack/bot.js +892 -567
  156. package/dist/adapters/slack/bot.js.map +1 -1
  157. package/dist/adapters/slack/context.d.ts +3 -9
  158. package/dist/adapters/slack/context.d.ts.map +1 -1
  159. package/dist/adapters/slack/context.js +8 -17
  160. package/dist/adapters/slack/context.js.map +1 -1
  161. package/dist/adapters/slack/jev-context.d.ts +17 -0
  162. package/dist/adapters/slack/jev-context.d.ts.map +1 -0
  163. package/dist/adapters/slack/jev-context.js +57 -0
  164. package/dist/adapters/slack/jev-context.js.map +1 -0
  165. package/dist/adapters/slack/markdown.d.ts +2 -0
  166. package/dist/adapters/slack/markdown.d.ts.map +1 -0
  167. package/dist/adapters/slack/markdown.js +43 -0
  168. package/dist/adapters/slack/markdown.js.map +1 -0
  169. package/dist/adapters/slack/persona.d.ts +6 -0
  170. package/dist/adapters/slack/persona.d.ts.map +1 -0
  171. package/dist/adapters/slack/persona.js +13 -0
  172. package/dist/adapters/slack/persona.js.map +1 -0
  173. package/dist/adapters/slack/response-lifecycle.d.ts +3 -4
  174. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  175. package/dist/adapters/slack/response-lifecycle.js +221 -347
  176. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  177. package/dist/adapters/slack/session.d.ts +3 -2
  178. package/dist/adapters/slack/session.d.ts.map +1 -1
  179. package/dist/adapters/slack/session.js +9 -13
  180. package/dist/adapters/slack/session.js.map +1 -1
  181. package/dist/adapters/slack/stream-limits.d.ts +10 -0
  182. package/dist/adapters/slack/stream-limits.d.ts.map +1 -0
  183. package/dist/adapters/slack/stream-limits.js +22 -0
  184. package/dist/adapters/slack/stream-limits.js.map +1 -0
  185. package/dist/adapters/slack/task-intent.d.ts +19 -0
  186. package/dist/adapters/slack/task-intent.d.ts.map +1 -0
  187. package/dist/adapters/slack/task-intent.js +65 -0
  188. package/dist/adapters/slack/task-intent.js.map +1 -0
  189. package/dist/adapters/slack/task-status.d.ts +6 -0
  190. package/dist/adapters/slack/task-status.d.ts.map +1 -0
  191. package/dist/adapters/slack/task-status.js +100 -0
  192. package/dist/adapters/slack/task-status.js.map +1 -0
  193. package/dist/adapters/slack/tool-pack.d.ts +4 -0
  194. package/dist/adapters/slack/tool-pack.d.ts.map +1 -0
  195. package/dist/adapters/slack/tool-pack.js +40 -0
  196. package/dist/adapters/slack/tool-pack.js.map +1 -0
  197. package/dist/adapters/slack/tools/blockkit.d.ts +17 -0
  198. package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -0
  199. package/dist/adapters/slack/tools/blockkit.js +98 -0
  200. package/dist/adapters/slack/tools/blockkit.js.map +1 -0
  201. package/dist/adapters/slack/types.d.ts +91 -5
  202. package/dist/adapters/slack/types.d.ts.map +1 -1
  203. package/dist/adapters/slack/types.js.map +1 -1
  204. package/dist/adapters/slack/update-diagnostics.d.ts +13 -0
  205. package/dist/adapters/slack/update-diagnostics.d.ts.map +1 -0
  206. package/dist/adapters/slack/update-diagnostics.js +128 -0
  207. package/dist/adapters/slack/update-diagnostics.js.map +1 -0
  208. package/dist/adapters/telegram/bot.d.ts +8 -18
  209. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  210. package/dist/adapters/telegram/bot.js +114 -232
  211. package/dist/adapters/telegram/bot.js.map +1 -1
  212. package/dist/adapters/telegram/context.d.ts +3 -7
  213. package/dist/adapters/telegram/context.d.ts.map +1 -1
  214. package/dist/adapters/telegram/context.js +47 -191
  215. package/dist/adapters/telegram/context.js.map +1 -1
  216. package/dist/adapters/telegram/types.d.ts +24 -1
  217. package/dist/adapters/telegram/types.d.ts.map +1 -1
  218. package/dist/adapters/telegram/types.js.map +1 -1
  219. package/dist/adapters/types.d.ts +89 -16
  220. package/dist/adapters/types.d.ts.map +1 -1
  221. package/dist/adapters/types.js.map +1 -1
  222. package/dist/adapters/web/admin/client-assets.d.ts +4 -0
  223. package/dist/adapters/web/admin/client-assets.d.ts.map +1 -0
  224. package/dist/adapters/web/admin/client-assets.js +1766 -0
  225. package/dist/adapters/web/admin/client-assets.js.map +1 -0
  226. package/dist/adapters/web/admin/portal.d.ts +27 -0
  227. package/dist/adapters/web/admin/portal.d.ts.map +1 -0
  228. package/dist/adapters/web/admin/portal.js +1506 -0
  229. package/dist/adapters/web/admin/portal.js.map +1 -0
  230. package/dist/{web → adapters/web}/admin/provider-models.d.ts +1 -1
  231. package/dist/adapters/web/admin/provider-models.d.ts.map +1 -0
  232. package/dist/{web → adapters/web}/admin/provider-models.js +1 -1
  233. package/dist/adapters/web/admin/provider-models.js.map +1 -0
  234. package/dist/adapters/web/admin/types.d.ts +56 -0
  235. package/dist/adapters/web/admin/types.d.ts.map +1 -0
  236. package/dist/adapters/web/admin/types.js.map +1 -0
  237. package/dist/adapters/web/login/oauth.d.ts +4 -0
  238. package/dist/adapters/web/login/oauth.d.ts.map +1 -0
  239. package/dist/{web → adapters/web}/login/oauth.js +61 -100
  240. package/dist/adapters/web/login/oauth.js.map +1 -0
  241. package/dist/adapters/web/login/portal.d.ts +10 -0
  242. package/dist/adapters/web/login/portal.d.ts.map +1 -0
  243. package/dist/{web → adapters/web}/login/portal.js +169 -159
  244. package/dist/adapters/web/login/portal.js.map +1 -0
  245. package/dist/{web → adapters/web}/login/types.d.ts +1 -17
  246. package/dist/adapters/web/login/types.d.ts.map +1 -0
  247. package/dist/adapters/web/login/types.js.map +1 -0
  248. package/dist/adapters/web/portal-shell.d.ts +9 -0
  249. package/dist/adapters/web/portal-shell.d.ts.map +1 -0
  250. package/dist/{portal-shell.js → adapters/web/portal-shell.js} +73 -11
  251. package/dist/adapters/web/portal-shell.js.map +1 -0
  252. package/dist/adapters/web/server.d.ts +33 -0
  253. package/dist/adapters/web/server.d.ts.map +1 -0
  254. package/dist/{web → adapters/web}/server.js +15 -9
  255. package/dist/adapters/web/server.js.map +1 -0
  256. package/dist/adapters/web/session-view/portal.d.ts +19 -0
  257. package/dist/adapters/web/session-view/portal.d.ts.map +1 -0
  258. package/dist/{web → adapters/web}/session-view/portal.js +641 -181
  259. package/dist/adapters/web/session-view/portal.js.map +1 -0
  260. package/dist/{web → adapters/web}/session-view/types.d.ts +1 -6
  261. package/dist/adapters/web/session-view/types.d.ts.map +1 -0
  262. package/dist/adapters/web/session-view/types.js.map +1 -0
  263. package/dist/{web → adapters/web}/token-store.d.ts +0 -3
  264. package/dist/adapters/web/token-store.d.ts.map +1 -0
  265. package/dist/{web → adapters/web}/token-store.js +1 -3
  266. package/dist/adapters/web/token-store.js.map +1 -0
  267. package/dist/adapters/web/types.d.ts.map +1 -0
  268. package/dist/adapters/web/types.js.map +1 -0
  269. package/dist/cli/arg-grammar.d.ts +8 -0
  270. package/dist/cli/arg-grammar.d.ts.map +1 -0
  271. package/dist/cli/arg-grammar.js +47 -0
  272. package/dist/cli/arg-grammar.js.map +1 -0
  273. package/dist/cli/boot.d.ts +4 -0
  274. package/dist/cli/boot.d.ts.map +1 -0
  275. package/dist/cli/boot.js +81 -0
  276. package/dist/cli/boot.js.map +1 -0
  277. package/dist/cli/download.d.ts.map +1 -0
  278. package/dist/cli/download.js +84 -0
  279. package/dist/cli/download.js.map +1 -0
  280. package/dist/cli/office.d.ts +2 -0
  281. package/dist/cli/office.d.ts.map +1 -0
  282. package/dist/cli/office.js +151 -0
  283. package/dist/cli/office.js.map +1 -0
  284. package/dist/cli/onboard.d.ts +8 -0
  285. package/dist/cli/onboard.d.ts.map +1 -0
  286. package/dist/cli/onboard.js +195 -0
  287. package/dist/cli/onboard.js.map +1 -0
  288. package/dist/cli/process-lifecycle.d.ts +14 -0
  289. package/dist/cli/process-lifecycle.d.ts.map +1 -0
  290. package/dist/cli/process-lifecycle.js +61 -0
  291. package/dist/cli/process-lifecycle.js.map +1 -0
  292. package/dist/cli/sandbox.d.ts +3 -0
  293. package/dist/cli/sandbox.d.ts.map +1 -0
  294. package/dist/cli/sandbox.js +80 -0
  295. package/dist/cli/sandbox.js.map +1 -0
  296. package/dist/cli/sessions.d.ts +2 -0
  297. package/dist/cli/sessions.d.ts.map +1 -0
  298. package/dist/cli/sessions.js +78 -0
  299. package/dist/cli/sessions.js.map +1 -0
  300. package/dist/cli/types.d.ts +21 -0
  301. package/dist/cli/types.d.ts.map +1 -0
  302. package/dist/cli/types.js.map +1 -0
  303. package/dist/env-manifest.d.ts +17 -0
  304. package/dist/env-manifest.d.ts.map +1 -0
  305. package/dist/env-manifest.js +249 -0
  306. package/dist/env-manifest.js.map +1 -0
  307. package/dist/events/index.d.ts +122 -0
  308. package/dist/events/index.d.ts.map +1 -0
  309. package/dist/events/index.js +299 -0
  310. package/dist/events/index.js.map +1 -0
  311. package/dist/events/scheduler.d.ts +25 -0
  312. package/dist/events/scheduler.d.ts.map +1 -0
  313. package/dist/events/scheduler.js +223 -0
  314. package/dist/events/scheduler.js.map +1 -0
  315. package/dist/file-guards.d.ts +13 -0
  316. package/dist/file-guards.d.ts.map +1 -0
  317. package/dist/file-guards.js +104 -0
  318. package/dist/file-guards.js.map +1 -0
  319. package/dist/harness/execution-env.d.ts +4 -0
  320. package/dist/harness/execution-env.d.ts.map +1 -0
  321. package/dist/harness/execution-env.js +245 -0
  322. package/dist/harness/execution-env.js.map +1 -0
  323. package/dist/harness/execution-resolver.d.ts +23 -0
  324. package/dist/harness/execution-resolver.d.ts.map +1 -0
  325. package/dist/harness/execution-resolver.js +156 -0
  326. package/dist/harness/execution-resolver.js.map +1 -0
  327. package/dist/harness/http.d.ts +0 -12
  328. package/dist/harness/http.d.ts.map +1 -1
  329. package/dist/harness/http.js +0 -34
  330. package/dist/harness/http.js.map +1 -1
  331. package/dist/harness/index.d.ts +19 -17
  332. package/dist/harness/index.d.ts.map +1 -1
  333. package/dist/harness/index.js +12 -15
  334. package/dist/harness/index.js.map +1 -1
  335. package/dist/harness/jev.d.ts +67 -0
  336. package/dist/harness/jev.d.ts.map +1 -0
  337. package/dist/harness/jev.js +135 -0
  338. package/dist/harness/jev.js.map +1 -0
  339. package/dist/harness/mcp-result.d.ts +20 -0
  340. package/dist/harness/mcp-result.d.ts.map +1 -0
  341. package/dist/harness/mcp-result.js +179 -0
  342. package/dist/harness/mcp-result.js.map +1 -0
  343. package/dist/harness/mcp.d.ts +17 -0
  344. package/dist/harness/mcp.d.ts.map +1 -0
  345. package/dist/harness/mcp.js +243 -0
  346. package/dist/harness/mcp.js.map +1 -0
  347. package/dist/harness/models.d.ts +2 -19
  348. package/dist/harness/models.d.ts.map +1 -1
  349. package/dist/harness/models.js +42 -94
  350. package/dist/harness/models.js.map +1 -1
  351. package/dist/harness/open-connector.d.ts +12 -0
  352. package/dist/harness/open-connector.d.ts.map +1 -0
  353. package/dist/harness/open-connector.js +160 -0
  354. package/dist/harness/open-connector.js.map +1 -0
  355. package/dist/harness/presenter.d.ts +11 -0
  356. package/dist/harness/presenter.d.ts.map +1 -0
  357. package/dist/harness/presenter.js +824 -0
  358. package/dist/harness/presenter.js.map +1 -0
  359. package/dist/harness/prompt.d.ts +18 -0
  360. package/dist/harness/prompt.d.ts.map +1 -0
  361. package/dist/harness/prompt.js +403 -0
  362. package/dist/harness/prompt.js.map +1 -0
  363. package/dist/harness/runner.d.ts +2 -123
  364. package/dist/harness/runner.d.ts.map +1 -1
  365. package/dist/harness/runner.js +547 -415
  366. package/dist/harness/runner.js.map +1 -1
  367. package/dist/harness/session.d.ts +98 -0
  368. package/dist/harness/session.d.ts.map +1 -0
  369. package/dist/harness/session.js +645 -0
  370. package/dist/harness/session.js.map +1 -0
  371. package/dist/harness/skills.d.ts +7 -41
  372. package/dist/harness/skills.d.ts.map +1 -1
  373. package/dist/harness/skills.js +101 -55
  374. package/dist/harness/skills.js.map +1 -1
  375. package/dist/harness/subagent-profiles.d.ts +3 -0
  376. package/dist/harness/subagent-profiles.d.ts.map +1 -0
  377. package/dist/harness/subagent-profiles.js +260 -0
  378. package/dist/harness/subagent-profiles.js.map +1 -0
  379. package/dist/harness/subagent.d.ts +39 -0
  380. package/dist/harness/subagent.d.ts.map +1 -0
  381. package/dist/harness/subagent.js +618 -0
  382. package/dist/harness/subagent.js.map +1 -0
  383. package/dist/harness/tool-args.d.ts +2 -0
  384. package/dist/harness/tool-args.d.ts.map +1 -0
  385. package/dist/harness/tool-args.js +20 -0
  386. package/dist/harness/tool-args.js.map +1 -0
  387. package/dist/harness/tools/attach.d.ts +13 -0
  388. package/dist/harness/tools/attach.d.ts.map +1 -0
  389. package/dist/{adapters/slack → harness}/tools/attach.js +2 -2
  390. package/dist/harness/tools/attach.js.map +1 -0
  391. package/dist/harness/tools/event.d.ts +28 -0
  392. package/dist/harness/tools/event.d.ts.map +1 -0
  393. package/dist/harness/tools/event.js +166 -0
  394. package/dist/harness/tools/event.js.map +1 -0
  395. package/dist/harness/tools/generate-image.d.ts +20 -0
  396. package/dist/harness/tools/generate-image.d.ts.map +1 -0
  397. package/dist/harness/tools/generate-image.js +68 -0
  398. package/dist/harness/tools/generate-image.js.map +1 -0
  399. package/dist/harness/tools/host-fn-tool.d.ts +14 -0
  400. package/dist/harness/tools/host-fn-tool.d.ts.map +1 -0
  401. package/dist/harness/tools/host-fn-tool.js +39 -0
  402. package/dist/harness/tools/host-fn-tool.js.map +1 -0
  403. package/dist/harness/tools/index.d.ts +34 -0
  404. package/dist/harness/tools/index.d.ts.map +1 -0
  405. package/dist/harness/tools/index.js +49 -0
  406. package/dist/harness/tools/index.js.map +1 -0
  407. package/dist/harness/tools/jev-browser.d.ts +42 -0
  408. package/dist/harness/tools/jev-browser.d.ts.map +1 -0
  409. package/dist/harness/tools/jev-browser.js +547 -0
  410. package/dist/harness/tools/jev-browser.js.map +1 -0
  411. package/dist/harness/tools/jev.d.ts +15 -0
  412. package/dist/harness/tools/jev.d.ts.map +1 -0
  413. package/dist/harness/tools/jev.js +98 -0
  414. package/dist/harness/tools/jev.js.map +1 -0
  415. package/dist/harness/tools/pi-tools.d.ts +8 -0
  416. package/dist/harness/tools/pi-tools.d.ts.map +1 -0
  417. package/dist/harness/tools/pi-tools.js +55 -0
  418. package/dist/harness/tools/pi-tools.js.map +1 -0
  419. package/dist/harness/tools/react.d.ts +11 -0
  420. package/dist/harness/tools/react.d.ts.map +1 -0
  421. package/dist/harness/tools/react.js +24 -0
  422. package/dist/harness/tools/react.js.map +1 -0
  423. package/dist/harness/tools/sandbox.d.ts +24 -0
  424. package/dist/harness/tools/sandbox.d.ts.map +1 -0
  425. package/dist/{tools → harness/tools}/sandbox.js +15 -9
  426. package/dist/harness/tools/sandbox.js.map +1 -0
  427. package/dist/harness/tools/secret-redaction.d.ts +9 -0
  428. package/dist/harness/tools/secret-redaction.d.ts.map +1 -0
  429. package/dist/harness/tools/secret-redaction.js +44 -0
  430. package/dist/harness/tools/secret-redaction.js.map +1 -0
  431. package/dist/harness/tools/subagent.d.ts +51 -0
  432. package/dist/harness/tools/subagent.d.ts.map +1 -0
  433. package/dist/harness/tools/subagent.js +581 -0
  434. package/dist/harness/tools/subagent.js.map +1 -0
  435. package/dist/harness/tools/task.d.ts +22 -0
  436. package/dist/harness/tools/task.d.ts.map +1 -0
  437. package/dist/harness/tools/task.js +80 -0
  438. package/dist/harness/tools/task.js.map +1 -0
  439. package/dist/harness/tools/types.d.ts +14 -0
  440. package/dist/harness/tools/types.d.ts.map +1 -0
  441. package/dist/harness/tools/types.js.map +1 -0
  442. package/dist/harness/types.d.ts +345 -29
  443. package/dist/harness/types.d.ts.map +1 -1
  444. package/dist/harness/types.js +1 -1
  445. package/dist/harness/types.js.map +1 -1
  446. package/dist/index.d.ts +36 -9
  447. package/dist/index.d.ts.map +1 -1
  448. package/dist/index.js +19 -7
  449. package/dist/index.js.map +1 -1
  450. package/dist/log.d.ts +0 -1
  451. package/dist/log.d.ts.map +1 -1
  452. package/dist/log.js +3 -13
  453. package/dist/log.js.map +1 -1
  454. package/dist/main.js +300 -217
  455. package/dist/main.js.map +1 -1
  456. package/dist/memory-capture/index.d.ts +15 -0
  457. package/dist/memory-capture/index.d.ts.map +1 -0
  458. package/dist/memory-capture/index.js +199 -0
  459. package/dist/memory-capture/index.js.map +1 -0
  460. package/dist/memory-capture/types.d.ts +27 -0
  461. package/dist/memory-capture/types.d.ts.map +1 -0
  462. package/dist/memory-capture/types.js +2 -0
  463. package/dist/memory-capture/types.js.map +1 -0
  464. package/dist/observability/index.d.ts +30 -0
  465. package/dist/observability/index.d.ts.map +1 -0
  466. package/dist/observability/index.js +273 -0
  467. package/dist/observability/index.js.map +1 -0
  468. package/dist/observability/instrument.js +9 -6
  469. package/dist/observability/instrument.js.map +1 -1
  470. package/dist/observability/otel.d.ts +10 -0
  471. package/dist/observability/otel.d.ts.map +1 -0
  472. package/dist/observability/otel.js +182 -0
  473. package/dist/observability/otel.js.map +1 -0
  474. package/dist/observability/sentry.d.ts +32 -13
  475. package/dist/observability/sentry.d.ts.map +1 -1
  476. package/dist/observability/sentry.js +85 -58
  477. package/dist/observability/sentry.js.map +1 -1
  478. package/dist/observability/types.d.ts +30 -3
  479. package/dist/observability/types.d.ts.map +1 -1
  480. package/dist/observability/types.js.map +1 -1
  481. package/dist/office/index.d.ts +74 -0
  482. package/dist/office/index.d.ts.map +1 -0
  483. package/dist/office/index.js +971 -0
  484. package/dist/office/index.js.map +1 -0
  485. package/dist/office/projection.d.ts +11 -0
  486. package/dist/office/projection.d.ts.map +1 -0
  487. package/dist/office/projection.js +146 -0
  488. package/dist/office/projection.js.map +1 -0
  489. package/dist/office/types.d.ts +55 -0
  490. package/dist/office/types.d.ts.map +1 -0
  491. package/dist/office/types.js +2 -0
  492. package/dist/office/types.js.map +1 -0
  493. package/dist/runtime/conversation-runtime.d.ts +0 -1
  494. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  495. package/dist/runtime/conversation-runtime.js +375 -152
  496. package/dist/runtime/conversation-runtime.js.map +1 -1
  497. package/dist/runtime/session-lifecycle.d.ts +66 -0
  498. package/dist/runtime/session-lifecycle.d.ts.map +1 -0
  499. package/dist/runtime/session-lifecycle.js +393 -0
  500. package/dist/runtime/session-lifecycle.js.map +1 -0
  501. package/dist/runtime/types.d.ts +41 -17
  502. package/dist/runtime/types.d.ts.map +1 -1
  503. package/dist/runtime/types.js.map +1 -1
  504. package/dist/sandbox/cloudflare.d.ts +4 -0
  505. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  506. package/dist/sandbox/cloudflare.js +59 -55
  507. package/dist/sandbox/cloudflare.js.map +1 -1
  508. package/dist/sandbox/container.d.ts +3 -0
  509. package/dist/sandbox/container.d.ts.map +1 -1
  510. package/dist/sandbox/container.js +39 -20
  511. package/dist/sandbox/container.js.map +1 -1
  512. package/dist/sandbox/host.d.ts +3 -0
  513. package/dist/sandbox/host.d.ts.map +1 -1
  514. package/dist/sandbox/host.js +42 -35
  515. package/dist/sandbox/host.js.map +1 -1
  516. package/dist/sandbox/identity.d.ts +13 -0
  517. package/dist/sandbox/identity.d.ts.map +1 -0
  518. package/dist/sandbox/identity.js +49 -0
  519. package/dist/sandbox/identity.js.map +1 -0
  520. package/dist/sandbox/index.d.ts +5 -15
  521. package/dist/sandbox/index.d.ts.map +1 -1
  522. package/dist/sandbox/index.js +5 -51
  523. package/dist/sandbox/index.js.map +1 -1
  524. package/dist/sandbox/layout.d.ts +7 -0
  525. package/dist/sandbox/layout.d.ts.map +1 -0
  526. package/dist/sandbox/layout.js +13 -0
  527. package/dist/sandbox/layout.js.map +1 -0
  528. package/dist/{provisioner.d.ts → sandbox/provisioner.d.ts} +41 -4
  529. package/dist/sandbox/provisioner.d.ts.map +1 -0
  530. package/dist/sandbox/provisioner.js +971 -0
  531. package/dist/sandbox/provisioner.js.map +1 -0
  532. package/dist/sandbox/registry.d.ts +12 -0
  533. package/dist/sandbox/registry.d.ts.map +1 -0
  534. package/dist/sandbox/registry.js +93 -0
  535. package/dist/sandbox/registry.js.map +1 -0
  536. package/dist/sandbox/types.d.ts +23 -25
  537. package/dist/sandbox/types.d.ts.map +1 -1
  538. package/dist/sandbox/types.js.map +1 -1
  539. package/dist/sandbox/utils.d.ts +21 -0
  540. package/dist/sandbox/utils.d.ts.map +1 -1
  541. package/dist/sandbox/utils.js +100 -8
  542. package/dist/sandbox/utils.js.map +1 -1
  543. package/dist/sessions/chat-history-sync.d.ts +17 -0
  544. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  545. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +224 -183
  546. package/dist/sessions/chat-history-sync.js.map +1 -0
  547. package/dist/sessions/history-line.d.ts +9 -0
  548. package/dist/sessions/history-line.d.ts.map +1 -0
  549. package/dist/sessions/history-line.js +25 -0
  550. package/dist/sessions/history-line.js.map +1 -0
  551. package/dist/sessions/migrate-common.d.ts +28 -0
  552. package/dist/sessions/migrate-common.d.ts.map +1 -0
  553. package/dist/sessions/migrate-common.js +80 -0
  554. package/dist/sessions/migrate-common.js.map +1 -0
  555. package/dist/sessions/migrate-pi-084.d.ts +7 -0
  556. package/dist/sessions/migrate-pi-084.d.ts.map +1 -0
  557. package/dist/sessions/migrate-pi-084.js +293 -0
  558. package/dist/sessions/migrate-pi-084.js.map +1 -0
  559. package/dist/sessions/migrate-v3.d.ts +7 -0
  560. package/dist/sessions/migrate-v3.d.ts.map +1 -0
  561. package/dist/sessions/migrate-v3.js +351 -0
  562. package/dist/sessions/migrate-v3.js.map +1 -0
  563. package/dist/sessions/session-key.d.ts +18 -0
  564. package/dist/sessions/session-key.d.ts.map +1 -0
  565. package/dist/sessions/session-key.js +88 -0
  566. package/dist/sessions/session-key.js.map +1 -0
  567. package/dist/sessions/session-store.d.ts +55 -0
  568. package/dist/sessions/session-store.d.ts.map +1 -0
  569. package/dist/sessions/session-store.js +635 -0
  570. package/dist/sessions/session-store.js.map +1 -0
  571. package/dist/sessions/store.d.ts +7 -61
  572. package/dist/sessions/store.d.ts.map +1 -1
  573. package/dist/sessions/store.js +126 -102
  574. package/dist/sessions/store.js.map +1 -1
  575. package/dist/sessions/types.d.ts +59 -8
  576. package/dist/sessions/types.d.ts.map +1 -1
  577. package/dist/sessions/types.js +2 -1
  578. package/dist/sessions/types.js.map +1 -1
  579. package/dist/settings/apply.d.ts +9 -0
  580. package/dist/settings/apply.d.ts.map +1 -0
  581. package/dist/settings/apply.js +31 -0
  582. package/dist/settings/apply.js.map +1 -0
  583. package/dist/settings/index.d.ts +76 -0
  584. package/dist/settings/index.d.ts.map +1 -0
  585. package/dist/settings/index.js +351 -0
  586. package/dist/settings/index.js.map +1 -0
  587. package/dist/settings/migrate.d.ts +16 -0
  588. package/dist/settings/migrate.d.ts.map +1 -0
  589. package/dist/settings/migrate.js +70 -0
  590. package/dist/settings/migrate.js.map +1 -0
  591. package/dist/types.d.ts +231 -173
  592. package/dist/types.d.ts.map +1 -1
  593. package/dist/types.js +1 -3
  594. package/dist/types.js.map +1 -1
  595. package/dist/unknown-values.d.ts +3 -0
  596. package/dist/unknown-values.d.ts.map +1 -0
  597. package/dist/unknown-values.js +7 -0
  598. package/dist/unknown-values.js.map +1 -0
  599. package/dist/vault/index.d.ts +35 -11
  600. package/dist/vault/index.d.ts.map +1 -1
  601. package/dist/vault/index.js +326 -115
  602. package/dist/vault/index.js.map +1 -1
  603. package/dist/vault/types.d.ts +5 -16
  604. package/dist/vault/types.d.ts.map +1 -1
  605. package/dist/vault/types.js.map +1 -1
  606. package/package.json +82 -41
  607. package/dist/adapter.d.ts +0 -2
  608. package/dist/adapter.d.ts.map +0 -1
  609. package/dist/adapter.js +0 -2
  610. package/dist/adapter.js.map +0 -1
  611. package/dist/adapters/slack/tools/attach.d.ts +0 -12
  612. package/dist/adapters/slack/tools/attach.d.ts.map +0 -1
  613. package/dist/adapters/slack/tools/attach.js.map +0 -1
  614. package/dist/adapters/streaming.d.ts +0 -18
  615. package/dist/adapters/streaming.d.ts.map +0 -1
  616. package/dist/adapters/streaming.js +0 -44
  617. package/dist/adapters/streaming.js.map +0 -1
  618. package/dist/adapters/telegram/html.d.ts +0 -3
  619. package/dist/adapters/telegram/html.d.ts.map +0 -1
  620. package/dist/adapters/telegram/html.js +0 -98
  621. package/dist/adapters/telegram/html.js.map +0 -1
  622. package/dist/agent-events.d.ts +0 -5
  623. package/dist/agent-events.d.ts.map +0 -1
  624. package/dist/agent-events.js +0 -26
  625. package/dist/agent-events.js.map +0 -1
  626. package/dist/agent.d.ts +0 -39
  627. package/dist/agent.d.ts.map +0 -1
  628. package/dist/agent.js +0 -1291
  629. package/dist/agent.js.map +0 -1
  630. package/dist/cli/ext-git.d.ts +0 -22
  631. package/dist/cli/ext-git.d.ts.map +0 -1
  632. package/dist/cli/ext-git.js +0 -76
  633. package/dist/cli/ext-git.js.map +0 -1
  634. package/dist/cli/ext.d.ts +0 -2
  635. package/dist/cli/ext.d.ts.map +0 -1
  636. package/dist/cli/ext.js +0 -217
  637. package/dist/cli/ext.js.map +0 -1
  638. package/dist/commands/admin.d.ts.map +0 -1
  639. package/dist/commands/admin.js.map +0 -1
  640. package/dist/commands/auto-reply.d.ts.map +0 -1
  641. package/dist/commands/auto-reply.js +0 -76
  642. package/dist/commands/auto-reply.js.map +0 -1
  643. package/dist/commands/extensions.d.ts +0 -14
  644. package/dist/commands/extensions.d.ts.map +0 -1
  645. package/dist/commands/extensions.js +0 -55
  646. package/dist/commands/extensions.js.map +0 -1
  647. package/dist/commands/login.d.ts +0 -5
  648. package/dist/commands/login.d.ts.map +0 -1
  649. package/dist/commands/login.js +0 -115
  650. package/dist/commands/login.js.map +0 -1
  651. package/dist/commands/model.d.ts +0 -10
  652. package/dist/commands/model.d.ts.map +0 -1
  653. package/dist/commands/model.js +0 -102
  654. package/dist/commands/model.js.map +0 -1
  655. package/dist/commands/new.d.ts.map +0 -1
  656. package/dist/commands/new.js +0 -25
  657. package/dist/commands/new.js.map +0 -1
  658. package/dist/commands/parse.d.ts +0 -7
  659. package/dist/commands/parse.d.ts.map +0 -1
  660. package/dist/commands/parse.js +0 -14
  661. package/dist/commands/parse.js.map +0 -1
  662. package/dist/commands/registry.d.ts +0 -5
  663. package/dist/commands/registry.d.ts.map +0 -1
  664. package/dist/commands/registry.js +0 -31
  665. package/dist/commands/registry.js.map +0 -1
  666. package/dist/commands/sandbox.d.ts.map +0 -1
  667. package/dist/commands/sandbox.js +0 -78
  668. package/dist/commands/sandbox.js.map +0 -1
  669. package/dist/commands/session-view.d.ts.map +0 -1
  670. package/dist/commands/session-view.js.map +0 -1
  671. package/dist/commands/types.d.ts +0 -66
  672. package/dist/commands/types.d.ts.map +0 -1
  673. package/dist/commands/types.js.map +0 -1
  674. package/dist/commands/utils.d.ts +0 -11
  675. package/dist/commands/utils.d.ts.map +0 -1
  676. package/dist/commands/utils.js.map +0 -1
  677. package/dist/config.d.ts +0 -78
  678. package/dist/config.d.ts.map +0 -1
  679. package/dist/config.js +0 -450
  680. package/dist/config.js.map +0 -1
  681. package/dist/download.d.ts.map +0 -1
  682. package/dist/download.js +0 -89
  683. package/dist/download.js.map +0 -1
  684. package/dist/events.d.ts +0 -43
  685. package/dist/events.d.ts.map +0 -1
  686. package/dist/events.js +0 -456
  687. package/dist/events.js.map +0 -1
  688. package/dist/execution-resolver.d.ts +0 -21
  689. package/dist/execution-resolver.d.ts.map +0 -1
  690. package/dist/execution-resolver.js +0 -170
  691. package/dist/execution-resolver.js.map +0 -1
  692. package/dist/harness/auth.d.ts +0 -16
  693. package/dist/harness/auth.d.ts.map +0 -1
  694. package/dist/harness/auth.js +0 -86
  695. package/dist/harness/auth.js.map +0 -1
  696. package/dist/harness/extensions/loader.d.ts +0 -86
  697. package/dist/harness/extensions/loader.d.ts.map +0 -1
  698. package/dist/harness/extensions/loader.js +0 -525
  699. package/dist/harness/extensions/loader.js.map +0 -1
  700. package/dist/harness/extensions/registry.d.ts +0 -23
  701. package/dist/harness/extensions/registry.d.ts.map +0 -1
  702. package/dist/harness/extensions/registry.js +0 -45
  703. package/dist/harness/extensions/registry.js.map +0 -1
  704. package/dist/harness/extensions/types.d.ts +0 -248
  705. package/dist/harness/extensions/types.d.ts.map +0 -1
  706. package/dist/harness/extensions/types.js.map +0 -1
  707. package/dist/harness/session-store.d.ts +0 -77
  708. package/dist/harness/session-store.d.ts.map +0 -1
  709. package/dist/harness/session-store.js +0 -248
  710. package/dist/harness/session-store.js.map +0 -1
  711. package/dist/harness/settings.d.ts +0 -47
  712. package/dist/harness/settings.d.ts.map +0 -1
  713. package/dist/harness/settings.js +0 -29
  714. package/dist/harness/settings.js.map +0 -1
  715. package/dist/model-registry.d.ts +0 -4
  716. package/dist/model-registry.d.ts.map +0 -1
  717. package/dist/model-registry.js +0 -7
  718. package/dist/model-registry.js.map +0 -1
  719. package/dist/platform-messages.d.ts.map +0 -1
  720. package/dist/platform-messages.js.map +0 -1
  721. package/dist/portal-shell.d.ts +0 -4
  722. package/dist/portal-shell.d.ts.map +0 -1
  723. package/dist/portal-shell.js.map +0 -1
  724. package/dist/provisioner.d.ts.map +0 -1
  725. package/dist/provisioner.js +0 -517
  726. package/dist/provisioner.js.map +0 -1
  727. package/dist/runtime/agent-run-controller.d.ts +0 -31
  728. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  729. package/dist/runtime/agent-run-controller.js +0 -195
  730. package/dist/runtime/agent-run-controller.js.map +0 -1
  731. package/dist/sandbox/errors.d.ts +0 -6
  732. package/dist/sandbox/errors.d.ts.map +0 -1
  733. package/dist/sandbox/errors.js +0 -11
  734. package/dist/sandbox/errors.js.map +0 -1
  735. package/dist/sandbox/firecracker.d.ts +0 -15
  736. package/dist/sandbox/firecracker.d.ts.map +0 -1
  737. package/dist/sandbox/firecracker.js +0 -212
  738. package/dist/sandbox/firecracker.js.map +0 -1
  739. package/dist/sandbox/image.d.ts +0 -3
  740. package/dist/sandbox/image.d.ts.map +0 -1
  741. package/dist/sandbox/image.js +0 -27
  742. package/dist/sandbox/image.js.map +0 -1
  743. package/dist/sandbox/path-context.d.ts +0 -3
  744. package/dist/sandbox/path-context.d.ts.map +0 -1
  745. package/dist/sandbox/path-context.js +0 -20
  746. package/dist/sandbox/path-context.js.map +0 -1
  747. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  748. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  749. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  750. package/dist/sessions/metadata.d.ts +0 -3
  751. package/dist/sessions/metadata.d.ts.map +0 -1
  752. package/dist/sessions/metadata.js +0 -11
  753. package/dist/sessions/metadata.js.map +0 -1
  754. package/dist/sessions/policy.d.ts +0 -6
  755. package/dist/sessions/policy.d.ts.map +0 -1
  756. package/dist/sessions/policy.js +0 -24
  757. package/dist/sessions/policy.js.map +0 -1
  758. package/dist/store.d.ts +0 -46
  759. package/dist/store.d.ts.map +0 -1
  760. package/dist/store.js +0 -177
  761. package/dist/store.js.map +0 -1
  762. package/dist/tools/bash.d.ts +0 -10
  763. package/dist/tools/bash.d.ts.map +0 -1
  764. package/dist/tools/bash.js +0 -80
  765. package/dist/tools/bash.js.map +0 -1
  766. package/dist/tools/edit.d.ts +0 -11
  767. package/dist/tools/edit.d.ts.map +0 -1
  768. package/dist/tools/edit.js +0 -131
  769. package/dist/tools/edit.js.map +0 -1
  770. package/dist/tools/event.d.ts +0 -56
  771. package/dist/tools/event.d.ts.map +0 -1
  772. package/dist/tools/event.js +0 -252
  773. package/dist/tools/event.js.map +0 -1
  774. package/dist/tools/index.d.ts +0 -24
  775. package/dist/tools/index.d.ts.map +0 -1
  776. package/dist/tools/index.js +0 -31
  777. package/dist/tools/index.js.map +0 -1
  778. package/dist/tools/react.d.ts +0 -15
  779. package/dist/tools/react.d.ts.map +0 -1
  780. package/dist/tools/react.js +0 -40
  781. package/dist/tools/react.js.map +0 -1
  782. package/dist/tools/read.d.ts +0 -11
  783. package/dist/tools/read.d.ts.map +0 -1
  784. package/dist/tools/read.js +0 -134
  785. package/dist/tools/read.js.map +0 -1
  786. package/dist/tools/sandbox.d.ts +0 -22
  787. package/dist/tools/sandbox.d.ts.map +0 -1
  788. package/dist/tools/sandbox.js.map +0 -1
  789. package/dist/tools/truncate.d.ts +0 -33
  790. package/dist/tools/truncate.d.ts.map +0 -1
  791. package/dist/tools/truncate.js +0 -184
  792. package/dist/tools/truncate.js.map +0 -1
  793. package/dist/tools/types.d.ts +0 -54
  794. package/dist/tools/types.d.ts.map +0 -1
  795. package/dist/tools/types.js.map +0 -1
  796. package/dist/tools/write.d.ts +0 -10
  797. package/dist/tools/write.d.ts.map +0 -1
  798. package/dist/tools/write.js +0 -29
  799. package/dist/tools/write.js.map +0 -1
  800. package/dist/trigger.d.ts +0 -22
  801. package/dist/trigger.d.ts.map +0 -1
  802. package/dist/trigger.js +0 -100
  803. package/dist/trigger.js.map +0 -1
  804. package/dist/utils/date.d.ts +0 -10
  805. package/dist/utils/date.d.ts.map +0 -1
  806. package/dist/utils/date.js +0 -23
  807. package/dist/utils/date.js.map +0 -1
  808. package/dist/utils/env.d.ts +0 -3
  809. package/dist/utils/env.d.ts.map +0 -1
  810. package/dist/utils/env.js +0 -12
  811. package/dist/utils/env.js.map +0 -1
  812. package/dist/utils/file-guards.d.ts +0 -9
  813. package/dist/utils/file-guards.d.ts.map +0 -1
  814. package/dist/utils/file-guards.js +0 -59
  815. package/dist/utils/file-guards.js.map +0 -1
  816. package/dist/utils/fs-atomic.d.ts +0 -10
  817. package/dist/utils/fs-atomic.d.ts.map +0 -1
  818. package/dist/utils/fs-atomic.js +0 -45
  819. package/dist/utils/fs-atomic.js.map +0 -1
  820. package/dist/utils/html.d.ts +0 -2
  821. package/dist/utils/html.d.ts.map +0 -1
  822. package/dist/utils/html.js +0 -4
  823. package/dist/utils/html.js.map +0 -1
  824. package/dist/utils/http-body.d.ts +0 -10
  825. package/dist/utils/http-body.d.ts.map +0 -1
  826. package/dist/utils/http-body.js +0 -34
  827. package/dist/utils/http-body.js.map +0 -1
  828. package/dist/vault/routing.d.ts +0 -3
  829. package/dist/vault/routing.d.ts.map +0 -1
  830. package/dist/vault/routing.js +0 -13
  831. package/dist/vault/routing.js.map +0 -1
  832. package/dist/web/admin/portal.d.ts +0 -5
  833. package/dist/web/admin/portal.d.ts.map +0 -1
  834. package/dist/web/admin/portal.js +0 -2572
  835. package/dist/web/admin/portal.js.map +0 -1
  836. package/dist/web/admin/provider-models.d.ts.map +0 -1
  837. package/dist/web/admin/provider-models.js.map +0 -1
  838. package/dist/web/admin/store.d.ts +0 -13
  839. package/dist/web/admin/store.d.ts.map +0 -1
  840. package/dist/web/admin/store.js +0 -14
  841. package/dist/web/admin/store.js.map +0 -1
  842. package/dist/web/admin/types.d.ts +0 -30
  843. package/dist/web/admin/types.d.ts.map +0 -1
  844. package/dist/web/admin/types.js.map +0 -1
  845. package/dist/web/agent-events/portal.d.ts +0 -3
  846. package/dist/web/agent-events/portal.d.ts.map +0 -1
  847. package/dist/web/agent-events/portal.js +0 -18
  848. package/dist/web/agent-events/portal.js.map +0 -1
  849. package/dist/web/login/oauth.d.ts +0 -6
  850. package/dist/web/login/oauth.d.ts.map +0 -1
  851. package/dist/web/login/oauth.js.map +0 -1
  852. package/dist/web/login/portal.d.ts +0 -17
  853. package/dist/web/login/portal.d.ts.map +0 -1
  854. package/dist/web/login/portal.js.map +0 -1
  855. package/dist/web/login/store.d.ts +0 -8
  856. package/dist/web/login/store.d.ts.map +0 -1
  857. package/dist/web/login/store.js +0 -15
  858. package/dist/web/login/store.js.map +0 -1
  859. package/dist/web/login/types.d.ts.map +0 -1
  860. package/dist/web/login/types.js.map +0 -1
  861. package/dist/web/request.d.ts +0 -3
  862. package/dist/web/request.d.ts.map +0 -1
  863. package/dist/web/request.js +0 -13
  864. package/dist/web/request.js.map +0 -1
  865. package/dist/web/server.d.ts +0 -28
  866. package/dist/web/server.d.ts.map +0 -1
  867. package/dist/web/server.js.map +0 -1
  868. package/dist/web/session-view/command.d.ts +0 -4
  869. package/dist/web/session-view/command.d.ts.map +0 -1
  870. package/dist/web/session-view/command.js +0 -7
  871. package/dist/web/session-view/command.js.map +0 -1
  872. package/dist/web/session-view/portal.d.ts +0 -13
  873. package/dist/web/session-view/portal.d.ts.map +0 -1
  874. package/dist/web/session-view/portal.js.map +0 -1
  875. package/dist/web/session-view/service.d.ts +0 -6
  876. package/dist/web/session-view/service.d.ts.map +0 -1
  877. package/dist/web/session-view/service.js +0 -498
  878. package/dist/web/session-view/service.js.map +0 -1
  879. package/dist/web/session-view/store.d.ts +0 -8
  880. package/dist/web/session-view/store.d.ts.map +0 -1
  881. package/dist/web/session-view/store.js +0 -15
  882. package/dist/web/session-view/store.js.map +0 -1
  883. package/dist/web/session-view/types.d.ts.map +0 -1
  884. package/dist/web/session-view/types.js.map +0 -1
  885. package/dist/web/token-store.d.ts.map +0 -1
  886. package/dist/web/token-store.js.map +0 -1
  887. package/dist/web/types.d.ts.map +0 -1
  888. package/dist/web/types.js.map +0 -1
  889. /package/dist/{commands → adapters/commands}/admin.d.ts +0 -0
  890. /package/dist/{commands → adapters/commands}/new.d.ts +0 -0
  891. /package/dist/{commands → adapters/commands}/session-view.d.ts +0 -0
  892. /package/dist/{commands → adapters/commands}/types.js +0 -0
  893. /package/dist/{harness/extensions → adapters/web/admin}/types.js +0 -0
  894. /package/dist/{tools → adapters/web/login}/types.js +0 -0
  895. /package/dist/{web/admin → adapters/web/session-view}/types.js +0 -0
  896. /package/dist/{web → adapters/web}/types.d.ts +0 -0
  897. /package/dist/{web/login → adapters/web}/types.js +0 -0
  898. /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
  899. /package/dist/{web/session-view → cli}/types.js +0 -0
  900. /package/dist/{web → harness/tools}/types.js +0 -0
@@ -1,22 +1,56 @@
1
1
  import { SocketModeClient } from "@slack/socket-mode";
2
- import { WebClient } from "@slack/web-api";
3
- import { existsSync, readFileSync } from "fs";
4
- import { readFile } from "fs/promises";
5
- import { basename, join } from "path";
6
- import { resolveConversationSettings } from "../../config.js";
2
+ import { WebAPIRateLimitedError, WebClient } from "@slack/web-api";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { readFile } from "node:fs/promises";
5
+ import { basename } from "node:path";
6
+ import { createConversationEvent, createConversationMessage, createOfficeAddress, listRegisteredOffices, } from "../../office/index.js";
7
+ import { COMMAND_MANIFEST } from "../commands/manifest.js";
8
+ import { slackConversationAutoReplyMode, resolveConversationSettings, } from "../../settings/index.js";
9
+ import { evaluateWithJev, JevNotConfiguredError } from "../../harness/jev.js";
7
10
  import * as log from "../../log.js";
8
- import { PRODUCT_NAME, formatForceStopped, formatNothingRunning } from "../../platform-messages.js";
9
- import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, resolveOnlyScopedStopTarget, resolveStopTarget, withRetry, } from "../shared.js";
10
- import { processMessageIntake } from "../intake.js";
11
+ import { readTextFileIfExists } from "../../file-guards.js";
12
+ import { PRODUCT_NAME, formatForceStopped } from "../messages.js";
13
+ import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, MessagingIntakeTracker, AttachmentRejectedError, saveIncomingAttachments, writeResponseToFile, withRetry, } from "../shared.js";
14
+ import { matchMagicWord, processMessageIntake } from "../intake.js";
15
+ import { readPlatformChannelKind, recordPlatformChannelKind } from "../../office/projection.js";
16
+ import { AssistantThreadRegistry, handleAgentContextChanged, handleAgentDmOpened, handleAssistantThreadStarted, titleAssistantThread, } from "./assistant.js";
11
17
  import { createSlackAdapters } from "./context.js";
12
- import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/agent-memory-file-manager.js";
18
+ import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/chat-history-sync.js";
19
+ import { conversationIdOf } from "../../sessions/session-key.js";
13
20
  import { isSlackThreadSessionKey, planSlackAdapterSession, planSlackEventAnchorRun, resolveSlackSessionKey, } from "./session.js";
14
- import { reportUserFacingError } from "../../observability/sentry.js";
15
- import { renderSlackBlocks } from "./blocks.js";
21
+ import { reportUserFacingError } from "../../observability/index.js";
22
+ import { recordSlackUpdate } from "./update-diagnostics.js";
23
+ import { renderSlackBlocks, resolveSlackMentions } from "./blocks.js";
24
+ import { querySlackTasks, formatTaskStatus, readTaskRoots, isTaskStatusQuestion, } from "./task-status.js";
25
+ import { buildAutoReplyState, JEV_ADDRESSED_INSTRUCTIONS } from "./auto-reply-context.js";
26
+ import { buildTaskIntentState, classifyTaskIntent } from "./task-intent.js";
27
+ import { StreamStartLimiter } from "./stream-limits.js";
28
+ import { errorMessage, isRecord } from "../../unknown-values.js";
16
29
  const SLACK_EVENT_ANCHOR_TEXT = "Working on it...";
17
- // Slack WebClient errors carry either `code: "rate_limited"` (retry-after) or
18
- // the legacy `data.error === "rate_limited"` / 429 status shape.
30
+ function hasMessageContent(message, includeBlocks = true) {
31
+ const content = [message.text, message.files];
32
+ if (includeBlocks)
33
+ content.push(message.blocks, message.attachments);
34
+ return content.some((part) => !!part?.length);
35
+ }
36
+ function hasBotIdentity(message) {
37
+ return !!message.bot_id || message.subtype === "bot_message";
38
+ }
39
+ const USER_MESSAGE_SUBTYPES = new Set([undefined, "file_share"]);
40
+ const BOT_MESSAGE_SUBTYPES = new Set([...USER_MESSAGE_SUBTYPES, "bot_message"]);
41
+ const MAX_STREAM_TEXT_CHARS = 12_000;
42
+ export function chunkStreamText(text, limit = MAX_STREAM_TEXT_CHARS) {
43
+ if (text.length <= limit)
44
+ return [text];
45
+ const chunks = [];
46
+ for (let index = 0; index < text.length; index += limit) {
47
+ chunks.push(text.slice(index, index + limit));
48
+ }
49
+ return chunks;
50
+ }
19
51
  function slackIsRateLimited(err) {
52
+ if (err instanceof WebAPIRateLimitedError)
53
+ return true;
20
54
  if (err.code === "rate_limited")
21
55
  return true;
22
56
  const data = err.data;
@@ -55,67 +89,172 @@ function buildSlackAppMessageText(event) {
55
89
  const deduped = parts.filter((part, index) => parts.indexOf(part) === index);
56
90
  return deduped.join("\n");
57
91
  }
58
- // ---------------------------------------------------------------------------
59
- // Shared mrkdwn truncation helper
60
- // ---------------------------------------------------------------------------
61
92
  const MRKDWN_CONTEXT_TEXT_LIMIT = 3000;
62
- /**
63
- * Build a Slack context block whose text is capped at the mrkdwn limit.
64
- * Used for muted diagnostics and ephemeral command responses.
65
- */
66
93
  export function buildMrkdwnContextBlock(text) {
67
94
  const blockText = text.length > MRKDWN_CONTEXT_TEXT_LIMIT
68
95
  ? text.substring(0, MRKDWN_CONTEXT_TEXT_LIMIT - 20) + "\n_(truncated)_"
69
96
  : text;
70
97
  return { type: "context", elements: [{ type: "mrkdwn", text: blockText }] };
71
98
  }
72
- // ============================================================================
73
- // SlackMessagingBot
74
- // ============================================================================
99
+ class AttachmentDownloadHttpError extends Error {
100
+ constructor(message, status) {
101
+ super(message);
102
+ this.status = status;
103
+ }
104
+ }
105
+ function isRetryableAttachmentDownloadError(error) {
106
+ if (error instanceof AttachmentRejectedError)
107
+ return false;
108
+ if (!(error instanceof AttachmentDownloadHttpError))
109
+ return true;
110
+ return error.status === 408 || error.status === 429 || error.status >= 500;
111
+ }
112
+ function assertSlackFileUrl(url) {
113
+ const { protocol, hostname } = new URL(url);
114
+ if (protocol === "https:" && (hostname === "slack.com" || hostname.endsWith(".slack.com")))
115
+ return;
116
+ throw new AttachmentRejectedError(`refusing to send the bot token to ${hostname}`);
117
+ }
75
118
  export class SlackMessagingBot {
119
+ office(channelId) {
120
+ return this.workspace.office(createOfficeAddress("slack", channelId));
121
+ }
122
+ conversationDir(channelId) {
123
+ return this.office(channelId).dir;
124
+ }
125
+ resolveReplyMode(address) {
126
+ return (resolveConversationSettings(this.workspace.office(address)).slack?.replyMode ?? "top-level");
127
+ }
76
128
  createContext(event) {
77
- return createSlackAdapters(event, this, {
78
- replyMode: resolveConversationSettings(join(this.workingDir, event.conversationId)).slack?.replyMode ??
79
- "top-level",
129
+ const context = createSlackAdapters(event, this, {
130
+ replyMode: this.resolveReplyMode(event.address),
80
131
  });
132
+ if (event.conversationKind === "direct") {
133
+ context.responder.getTaskStatus = (key) => querySlackTasks(this.conversationDir(event.channel), event.channel, this.handler
134
+ .getRunningSessions()
135
+ .filter((s) => s.address.platform === "slack" && s.address.conversationId === event.channel), key);
136
+ }
137
+ if (event.conversationKind === "direct" &&
138
+ event.thread_ts &&
139
+ this.isTaskThread(event.channel, event.thread_ts)) {
140
+ let notified = false;
141
+ context.responder.notifyCompletion = async () => {
142
+ if (notified)
143
+ return;
144
+ const text = `<@${event.user}> 這一輪處理已結束,請查看上方結果。`;
145
+ const ts = await this.postInThread(event.channel, event.thread_ts, text);
146
+ notified = true;
147
+ this.logBotResponse(event.channel, text, ts, event.thread_ts);
148
+ };
149
+ }
150
+ if (event.conversationKind === "direct" && !event.thread_ts) {
151
+ context.responder.startTask = async (message, task) => {
152
+ if (this.stopped)
153
+ throw new Error("Slack is shutting down; task was not started.");
154
+ await context.responder.deleteResponse();
155
+ const root = await this.postMessage(event.channel, message);
156
+ this.logToFile(event.channel, {
157
+ date: new Date().toISOString(),
158
+ ts: root,
159
+ user: "bot",
160
+ text: message,
161
+ isMessagingBot: true,
162
+ taskRoot: true,
163
+ });
164
+ const sessionKey = resolveSlackSessionKey(event.channel, root);
165
+ try {
166
+ registerThreadSession({
167
+ conversationDir: this.conversationDir(event.channel),
168
+ sessionKey,
169
+ });
170
+ const child = {
171
+ ...event,
172
+ ts: `task:${root}`,
173
+ thread_ts: root,
174
+ sessionKey,
175
+ text: task,
176
+ };
177
+ if (!this.getQueue(sessionKey).enqueue(async () => {
178
+ try {
179
+ await this.handler.handleEvent({
180
+ ...child,
181
+ attachments: child.attachments?.map((a) => ({
182
+ name: a.original,
183
+ localPath: a.localPath,
184
+ })),
185
+ }, this, this.createContext(child));
186
+ }
187
+ catch (error) {
188
+ reportUserFacingError(error, {
189
+ domain: "mikan",
190
+ surface: "task_handoff",
191
+ operation: "start_task_run",
192
+ severity: "error",
193
+ platform: "slack",
194
+ context: { conversationId: event.channel, sessionKey, threadTs: root },
195
+ });
196
+ await this.postInThread(event.channel, root, "Task could not start. Please reply here to try again.");
197
+ throw error;
198
+ }
199
+ }))
200
+ throw new Error("Task queue is closed.");
201
+ }
202
+ catch (error) {
203
+ await this.updateMessage(event.channel, root, `${message}\n\nTask could not start.`);
204
+ throw error;
205
+ }
206
+ return sessionKey;
207
+ };
208
+ }
209
+ return context;
81
210
  }
82
211
  constructor(handler, config) {
212
+ this.statusUpdates = new Map();
83
213
  this.botUserId = null;
84
214
  this.botId = null;
85
215
  this.teamId = null;
86
216
  this.ownMentionRegex = null;
87
- this.startupTs = null; // Messages older than this are just logged, not processed
217
+ this.startupTs = null;
88
218
  this.users = new Map();
89
219
  this.channels = new Map();
220
+ this.assistantThreads = new AssistantThreadRegistry();
221
+ this.streamStarts = new StreamStartLimiter();
222
+ this.stopped = false;
90
223
  this.queues = new Map();
91
- this.eventsWatcher = null;
224
+ this.intake = new MessagingIntakeTracker("Slack");
225
+ this.eventScheduler = null;
92
226
  this.handler = handler;
93
- this.workingDir = config.workingDir;
94
- this.store = config.store;
95
- this.socketClient = new SocketModeClient({
96
- appToken: config.appToken,
97
- // Default 5s is too tight: brief event-loop stalls (e.g. backfill, sync fs)
98
- // cause false pong timeouts; 4 in a row makes Slack drop the socket.
99
- clientPingTimeout: 12_000,
227
+ this.workspace = config.workspace;
228
+ this.botToken = config.botToken;
229
+ this.fetchFn = config.fetch ?? ((input, init) => fetch(input, init));
230
+ this.socketClient =
231
+ config.socket ??
232
+ new SocketModeClient({
233
+ appToken: config.appToken,
234
+ clientPingTimeout: 12_000,
235
+ });
236
+ this.webClient = config.webApi ?? new WebClient(config.botToken, { fetch: config.fetch });
237
+ this.statusClient = new WebClient(config.botToken, {
238
+ timeout: 3000,
239
+ retryConfig: { retries: 0 },
240
+ rejectRateLimitedCalls: true,
241
+ fetch: config.fetch,
100
242
  });
101
- this.webClient = new WebClient(config.botToken);
102
243
  }
103
- setEventsWatcher(watcher) {
104
- this.eventsWatcher = watcher;
244
+ setEventScheduler(scheduler) {
245
+ this.eventScheduler = scheduler;
105
246
  }
106
- // ==========================================================================
107
- // Public API
108
- // ==========================================================================
109
247
  async start() {
248
+ this.stopped = false;
110
249
  const auth = await this.webClient.auth.test();
111
250
  this.botUserId = auth.user_id;
112
251
  this.botId = typeof auth.bot_id === "string" ? auth.bot_id : null;
113
252
  this.teamId = typeof auth.team_id === "string" ? auth.team_id : null;
114
253
  await Promise.all([this.fetchUsers(), this.fetchChannels()]);
254
+ if (this.stopped)
255
+ return;
115
256
  log.logInfo(`Loaded ${this.channels.size} channels, ${this.users.size} users`);
116
- // Record startup time before opening the socket. Slack may replay older events;
117
- // those should be logged but not processed. Backfill runs in the background up
118
- // to this timestamp so startup is not blocked by one history call per channel.
257
+ this.backfillChannelKinds();
119
258
  this.startupTs = (Date.now() / 1000).toFixed(6);
120
259
  this.setupEventHandlers();
121
260
  await this.socketClient.start();
@@ -124,6 +263,16 @@ export class SlackMessagingBot {
124
263
  log.logWarning("Slack backfill failed", String(error));
125
264
  });
126
265
  }
266
+ async stop() {
267
+ this.stopped = true;
268
+ try {
269
+ await this.socketClient.disconnect();
270
+ }
271
+ finally {
272
+ await this.intake.close();
273
+ await Promise.all([...this.queues.values()].map((queue) => queue.close()));
274
+ }
275
+ }
127
276
  getUser(userId) {
128
277
  return this.users.get(userId);
129
278
  }
@@ -145,54 +294,64 @@ export class SlackMessagingBot {
145
294
  }
146
295
  return source.replace(this.ownMentionRegex, "").trim();
147
296
  }
148
- async postMessage(channel, text) {
297
+ resolveMentions(text) {
298
+ return resolveSlackMentions(text, this.users.values());
299
+ }
300
+ async postMessage(channel, text, threadTs, identity) {
149
301
  return slackRetry(async () => {
150
- const payload = { channel, ...renderSlackBlocks(text) };
302
+ const payload = {
303
+ channel,
304
+ thread_ts: threadTs,
305
+ username: identity?.username,
306
+ icon_emoji: identity?.iconEmoji,
307
+ ...renderSlackBlocks(this.resolveMentions(text)),
308
+ };
151
309
  const result = await this.webClient.chat.postMessage(payload);
152
310
  return result.ts;
153
311
  });
154
312
  }
155
313
  async addReaction(channel, messageTs, emoji) {
156
- // Slack reaction names are colon-free short names; strip any wrapping colons.
157
314
  const name = emoji.replace(/^:|:$/g, "");
158
315
  await slackRetry(async () => {
159
316
  try {
160
317
  await this.webClient.reactions.add({ channel, timestamp: messageTs, name });
161
318
  }
162
319
  catch (err) {
163
- // Re-reacting with the same emoji is not an error worth surfacing.
164
320
  if (err?.data?.error === "already_reacted")
165
321
  return;
166
322
  throw err;
167
323
  }
168
324
  });
169
325
  }
170
- async postEphemeral(channel, user, text, threadTs) {
326
+ async postEphemeral(channel, user, text, threadTs, blocks) {
171
327
  return slackRetry(async () => {
172
328
  await this.webClient.chat.postEphemeral({
173
329
  channel,
174
330
  user,
175
331
  text,
176
- ...(threadTs ? { thread_ts: threadTs } : {}),
332
+ blocks: blocks,
333
+ thread_ts: threadTs || undefined,
177
334
  });
178
335
  });
179
336
  }
180
337
  async postEphemeralBlocks(channel, user, text, blocks, threadTs) {
338
+ return this.postEphemeral(channel, user, text, threadTs, blocks);
339
+ }
340
+ async postMessageBlocks(channel, text, blocks, threadTs) {
181
341
  return slackRetry(async () => {
182
- await this.webClient.chat.postEphemeral({
342
+ const payload = {
183
343
  channel,
184
- user,
185
344
  text,
186
345
  blocks: blocks,
187
- ...(threadTs ? { thread_ts: threadTs } : {}),
188
- });
346
+ thread_ts: threadTs,
347
+ };
348
+ const result = await this.webClient.chat.postMessage(payload);
349
+ return result.ts;
189
350
  });
190
351
  }
191
- async postMessageBlocks(channel, text, blocks) {
352
+ async updateMessageBlocks(channel, ts, text, blocks) {
192
353
  return slackRetry(async () => {
193
- const payload = { channel, text, blocks: blocks };
194
- const result = await this.webClient.chat.postMessage(payload);
195
- return result.ts;
354
+ await this.webClient.chat.update({ channel, ts, text, blocks: blocks });
196
355
  });
197
356
  }
198
357
  async postPrivate(conversationId, userId, text) {
@@ -205,7 +364,7 @@ export class SlackMessagingBot {
205
364
  }
206
365
  await this.postEphemeralBlocks(conversationId, userId, text, [buildMrkdwnContextBlock(text)]);
207
366
  }
208
- async openDirectMessage(userId) {
367
+ async openDirectConversation(userId) {
209
368
  return slackRetry(async () => {
210
369
  const result = await this.webClient.conversations.open({ users: userId });
211
370
  const channelId = result.channel?.id;
@@ -215,33 +374,101 @@ export class SlackMessagingBot {
215
374
  return channelId;
216
375
  });
217
376
  }
377
+ async fetchHistory(channel, options) {
378
+ const limit = Math.min(Math.max(options?.limit ?? 200, 1), 999);
379
+ const threadTs = options?.threadTs;
380
+ return slackRetry(async () => {
381
+ const result = threadTs
382
+ ? await this.webClient.conversations.replies({
383
+ channel,
384
+ ts: threadTs,
385
+ oldest: options?.oldest || undefined,
386
+ inclusive: options?.oldest ? false : undefined,
387
+ limit,
388
+ })
389
+ : await this.webClient.conversations.history({
390
+ channel,
391
+ oldest: options?.oldest || undefined,
392
+ inclusive: options?.oldest ? false : undefined,
393
+ limit,
394
+ });
395
+ const messages = (result.messages ?? []);
396
+ const mapped = messages
397
+ .filter((msg) => !!msg.ts)
398
+ .filter((msg) => msg.ts !== threadTs)
399
+ .map((msg) => {
400
+ const user = msg.user ? this.users.get(msg.user) : undefined;
401
+ const message = {
402
+ ts: msg.ts,
403
+ text: msg.text ?? "",
404
+ isBot: !!msg.bot_id || msg.subtype === "bot_message" || user?.isBot === true,
405
+ };
406
+ if (msg.thread_ts && msg.thread_ts !== msg.ts)
407
+ message.threadTs = msg.thread_ts;
408
+ if (msg.user)
409
+ message.userId = msg.user;
410
+ if (user)
411
+ message.userName = user.userName;
412
+ return message;
413
+ });
414
+ return threadTs ? mapped : mapped.toReversed();
415
+ });
416
+ }
417
+ async listUsers() {
418
+ await slackRetry(() => this.fetchUsers());
419
+ return this.getAllUsers().map((user) => ({
420
+ id: user.id,
421
+ userName: user.userName,
422
+ displayName: user.displayName,
423
+ isBot: user.isBot === true,
424
+ }));
425
+ }
218
426
  async updateMessage(channel, ts, text) {
219
427
  return slackRetry(async () => {
220
- const payload = { channel, ts, ...renderSlackBlocks(text) };
221
- await this.webClient.chat.update(payload);
428
+ const payload = { channel, ts, ...renderSlackBlocks(this.resolveMentions(text)) };
429
+ try {
430
+ await this.webClient.chat.update(payload);
431
+ }
432
+ catch (error) {
433
+ recordSlackUpdate(this, { channel, ts }, text, payload, { error });
434
+ throw error;
435
+ }
436
+ recordSlackUpdate(this, { channel, ts }, text, payload, { success: true });
222
437
  });
223
438
  }
439
+ tryReserveStreamStart() {
440
+ return this.streamStarts.tryReserve();
441
+ }
224
442
  async startMessageStream(channel, text, threadTs, recipientUserId) {
225
- return slackRetry(async () => {
443
+ const [head, ...tail] = chunkStreamText(text);
444
+ const ts = await slackRetry(async () => {
226
445
  const result = await this.webClient.apiCall("chat.startStream", {
227
446
  channel,
228
- markdown_text: text,
229
- ...(threadTs ? { thread_ts: threadTs } : {}),
230
- ...(this.teamId ? { recipient_team_id: this.teamId } : {}),
231
- ...(recipientUserId ? { recipient_user_id: recipientUserId } : {}),
447
+ markdown_text: this.resolveMentions(head ?? ""),
448
+ thread_ts: threadTs || undefined,
449
+ recipient_team_id: this.teamId || undefined,
450
+ recipient_user_id: recipientUserId || undefined,
232
451
  });
233
- const ts = result.ts;
234
- if (!ts)
452
+ const streamTs = result.ts;
453
+ if (!streamTs)
235
454
  throw new Error("Slack chat.startStream did not return ts");
236
- return ts;
455
+ return streamTs;
237
456
  });
457
+ for (const part of tail)
458
+ await this.appendStreamChunk(channel, ts, part);
459
+ return ts;
238
460
  }
239
461
  async appendMessageStream(channel, ts, text) {
462
+ for (const part of chunkStreamText(text)) {
463
+ await this.appendStreamChunk(channel, ts, part);
464
+ }
465
+ }
466
+ async appendStreamChunk(channel, ts, text) {
240
467
  return slackRetry(async () => {
241
468
  await this.webClient.apiCall("chat.appendStream", {
242
469
  channel,
243
470
  ts,
244
- markdown_text: text,
471
+ markdown_text: this.resolveMentions(text),
245
472
  });
246
473
  });
247
474
  }
@@ -255,38 +482,83 @@ export class SlackMessagingBot {
255
482
  await this.webClient.chat.delete({ channel, ts });
256
483
  });
257
484
  }
258
- // ==========================================================================
259
- // Slack Assistant API (AI assistant experience)
260
- // ==========================================================================
261
- /** Set the status for an assistant thread (shows "thinking" state) */
262
485
  async setAssistantStatus(channel, threadTs, status) {
263
- return slackRetry(async () => {
264
- await this.webClient.assistant.threads.setStatus({
486
+ const key = `${channel}:${threadTs}`;
487
+ const previous = this.statusUpdates.get(key) ?? Promise.resolve();
488
+ const update = previous
489
+ .catch(() => undefined)
490
+ .then(async () => {
491
+ await this.statusClient.assistant.threads.setStatus({
265
492
  channel_id: channel,
266
493
  thread_ts: threadTs,
267
494
  status,
268
495
  });
269
496
  });
497
+ this.statusUpdates.set(key, update);
498
+ try {
499
+ await update;
500
+ }
501
+ finally {
502
+ if (this.statusUpdates.get(key) === update)
503
+ this.statusUpdates.delete(key);
504
+ }
270
505
  }
271
- async postInThread(channel, threadTs, text) {
506
+ async setAssistantSuggestedPrompts(channel, threadTs, prompts) {
272
507
  return slackRetry(async () => {
273
- const payload = { channel, thread_ts: threadTs, ...renderSlackBlocks(text) };
274
- const result = await this.webClient.chat.postMessage(payload);
275
- return result.ts;
508
+ await this.webClient.assistant.threads.setSuggestedPrompts({
509
+ channel_id: channel,
510
+ thread_ts: threadTs || undefined,
511
+ prompts,
512
+ });
276
513
  });
277
514
  }
278
- async postInThreadBlocks(channel, threadTs, text, blocks) {
515
+ async setAssistantTitle(channel, threadTs, title) {
279
516
  return slackRetry(async () => {
280
- const payload = {
281
- channel,
517
+ await this.webClient.assistant.threads.setTitle({
518
+ channel_id: channel,
282
519
  thread_ts: threadTs,
283
- text, // fallback for notifications
284
- blocks: blocks,
285
- };
286
- const result = await this.webClient.chat.postMessage(payload);
287
- return result.ts;
520
+ title,
521
+ });
288
522
  });
289
523
  }
524
+ assistantOps() {
525
+ return {
526
+ postInThread: (channel, threadTs, text) => this.postInThread(channel, threadTs, text),
527
+ setSuggestedPrompts: (channel, threadTs, prompts) => this.setAssistantSuggestedPrompts(channel, threadTs, prompts),
528
+ setTitle: (channel, threadTs, title) => this.setAssistantTitle(channel, threadTs, title),
529
+ channelName: (channelId) => this.channels.get(channelId)?.name,
530
+ };
531
+ }
532
+ handleAssistantThreadStarted({ event, ack }) {
533
+ ack();
534
+ const payload = event;
535
+ if (!payload.assistant_thread)
536
+ return;
537
+ void handleAssistantThreadStarted(this.assistantOps(), this.assistantThreads, payload.assistant_thread);
538
+ }
539
+ handleAgentContextChangedEvent({ event, ack }) {
540
+ ack();
541
+ const payload = event;
542
+ if (payload.assistant_thread) {
543
+ handleAgentContextChanged(this.assistantThreads, payload.assistant_thread);
544
+ return;
545
+ }
546
+ const channelId = payload.channel_id ?? payload.channel;
547
+ if (channelId) {
548
+ handleAgentContextChanged(this.assistantThreads, {
549
+ channel_id: channelId,
550
+ context: payload.context,
551
+ });
552
+ }
553
+ }
554
+ async postInThread(channel, threadTs, text, identity) {
555
+ return identity
556
+ ? this.postMessage(channel, text, threadTs, identity)
557
+ : this.postMessage(channel, text, threadTs);
558
+ }
559
+ async postInThreadBlocks(channel, threadTs, text, blocks) {
560
+ return this.postMessageBlocks(channel, text, blocks, threadTs);
561
+ }
290
562
  async uploadFile(channel, filePath, title, threadTs) {
291
563
  return slackRetry(async () => {
292
564
  const fileName = title || basename(filePath);
@@ -301,18 +573,40 @@ export class SlackMessagingBot {
301
573
  });
302
574
  }
303
575
  logToFile(channel, entry) {
304
- appendChannelLog(this.workingDir, channel, entry);
576
+ appendChannelLog(this.workspace.office(createOfficeAddress("slack", channel)), entry);
305
577
  }
306
578
  logBotResponse(channel, text, ts, threadTs, slackBlocks) {
307
- appendBotResponseLog(this.workingDir, channel, text, ts, threadTs, {
579
+ appendBotResponseLog(this.workspace.office(createOfficeAddress("slack", channel)), text, ts, threadTs, {
308
580
  platform: "slack",
309
- ...(slackBlocks ? { slackBlocks } : {}),
581
+ slackBlocks,
310
582
  });
311
583
  }
584
+ ownsBlockKitMessage(channel, ts, threadTs) {
585
+ const content = readTextFileIfExists(this.office(channel).logPath);
586
+ if (content === undefined)
587
+ return false;
588
+ for (const line of content.trim().split("\n").toReversed()) {
589
+ try {
590
+ const entry = JSON.parse(line);
591
+ if (!isRecord(entry) || entry.ts !== ts)
592
+ continue;
593
+ return (entry.isMessagingBot === true &&
594
+ entry.platform === "slack" &&
595
+ Array.isArray(entry.slackBlocks) &&
596
+ entry.threadTs === threadTs);
597
+ }
598
+ catch {
599
+ continue;
600
+ }
601
+ }
602
+ return false;
603
+ }
312
604
  getMessagingInfo() {
313
605
  return {
314
606
  name: "slack",
315
- formattingGuide: "## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: `code`, Block: ```code```, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).",
607
+ workspaceId: this.teamId || undefined,
608
+ trustModel: "membership",
609
+ formattingGuide: "## Slack Formatting\nWrite standard Markdown/GFM: **bold**, _italic_, ~~strike~~, `code`, fenced code blocks, [links](url), lists, and pipe tables (rendered as native Slack tables).\nDo NOT use Slack mrkdwn syntax like *single-asterisk bold* or <url|label> links.",
316
610
  channels: this.getAllChannels().map((c) => ({ id: c.id, name: c.name })),
317
611
  users: this.getAllUsers().map((u) => ({
318
612
  id: u.id,
@@ -324,29 +618,24 @@ export class SlackMessagingBot {
324
618
  },
325
619
  };
326
620
  }
327
- // ==========================================================================
328
- // Events Integration
329
- // ==========================================================================
330
- /**
331
- * Enqueue an event for processing. Always queues (no "already working" rejection).
332
- * Returns true if enqueued, false if queue is full (max 5).
333
- */
334
621
  enqueueEvent(event) {
335
- const conversationId = event.conversationId;
622
+ if (this.stopped)
623
+ return false;
624
+ const conversationId = event.address.conversationId;
336
625
  const queue = this.getQueue(conversationId);
337
626
  if (queue.size() >= 5) {
338
627
  log.logWarning(`Event queue full for ${conversationId}, discarding: ${event.text.substring(0, 50)}`);
339
628
  return false;
340
629
  }
341
630
  log.logInfo(`Enqueueing event for ${conversationId}: ${event.text.substring(0, 50)}`);
342
- queue.enqueue(async () => {
631
+ return queue.enqueue(async () => {
343
632
  let anchorTs;
344
633
  if (!event.thread_ts) {
345
634
  try {
346
635
  anchorTs = await this.postMessage(conversationId, SLACK_EVENT_ANCHOR_TEXT);
347
636
  }
348
637
  catch (err) {
349
- log.logWarning(`Failed to post Slack event anchor for ${conversationId}`, err instanceof Error ? err.message : String(err));
638
+ log.logWarning(`Failed to post Slack event anchor for ${conversationId}`, errorMessage(err));
350
639
  reportUserFacingError(err, {
351
640
  domain: "events",
352
641
  surface: "event_delivery",
@@ -367,15 +656,17 @@ export class SlackMessagingBot {
367
656
  const eventForRun = eventPlan.event;
368
657
  if (eventPlan.initialMessageTs && eventForRun.sessionKey) {
369
658
  registerThreadSession({
370
- conversationDir: join(this.workingDir, conversationId),
659
+ conversationDir: this.conversationDir(conversationId),
371
660
  sessionKey: eventForRun.sessionKey,
372
661
  });
373
662
  }
374
663
  const runQueueKey = planSlackAdapterSession(eventForRun, {
375
664
  initialMessageTs: eventPlan.initialMessageTs,
376
665
  }).sessionKey;
377
- this.getQueue(runQueueKey).enqueue(async () => {
378
- const slackEvent = {
666
+ const run = async () => {
667
+ const slackEvent = createConversationEvent({
668
+ platform: "slack",
669
+ address: eventForRun.address,
379
670
  type: eventForRun.type,
380
671
  conversationId,
381
672
  conversationKind: eventForRun.conversationKind,
@@ -384,29 +675,28 @@ export class SlackMessagingBot {
384
675
  thread_ts: eventForRun.thread_ts,
385
676
  user: eventForRun.user,
386
677
  text: eventForRun.text,
387
- attachments: eventForRun.attachments?.map((attachment) => ({
388
- original: attachment.name,
389
- localPath: attachment.localPath,
390
- })),
678
+ attachments: eventForRun.attachments,
391
679
  sessionKey: eventForRun.sessionKey,
392
- };
680
+ });
393
681
  const context = createSlackAdapters(slackEvent, this, {
394
682
  initialMessageTs: eventPlan.initialMessageTs,
395
- replyMode: resolveConversationSettings(join(this.workingDir, eventForRun.conversationId)).slack
396
- ?.replyMode ?? "top-level",
683
+ replyMode: this.resolveReplyMode(eventForRun.address),
397
684
  });
398
685
  return this.handler.handleEvent(eventForRun, this, context);
399
- });
686
+ };
687
+ const runQueue = this.getQueue(runQueueKey);
688
+ if (!runQueue.enqueue(run)) {
689
+ await runQueue.close();
690
+ await run();
691
+ }
400
692
  });
401
- return true;
402
693
  }
403
- // ==========================================================================
404
- // Private - Event Handlers
405
- // ==========================================================================
406
694
  getQueue(channelId) {
407
695
  let queue = this.queues.get(channelId);
408
696
  if (!queue) {
409
697
  queue = new MessagingEventQueue("Slack");
698
+ if (this.stopped)
699
+ void queue.close();
410
700
  this.queues.set(channelId, queue);
411
701
  }
412
702
  return queue;
@@ -414,21 +704,91 @@ export class SlackMessagingBot {
414
704
  resolveQueueKey(conversationId, sessionKey) {
415
705
  if (!isSlackThreadSessionKey(sessionKey))
416
706
  return sessionKey;
417
- if (this.handler.isRunning(sessionKey))
707
+ if (this.handler.isRunning(createOfficeAddress("slack", conversationId), sessionKey)) {
418
708
  return sessionKey;
709
+ }
419
710
  return this.hasKnownThreadSession(conversationId, sessionKey) ? sessionKey : conversationId;
420
711
  }
712
+ isTaskThread(channel, root) {
713
+ return readTaskRoots(this.conversationDir(channel)).has(root);
714
+ }
715
+ hasRunningTaskThread(channel) {
716
+ const roots = readTaskRoots(this.conversationDir(channel));
717
+ if (!roots.size)
718
+ return false;
719
+ return this.handler
720
+ .getRunningSessions()
721
+ .some((s) => s.address.platform === "slack" &&
722
+ s.address.conversationId === channel &&
723
+ [...roots.keys()].some((root) => resolveSlackSessionKey(channel, root) === s.sessionKey));
724
+ }
421
725
  hasKnownThreadSession(conversationId, sessionKey) {
422
726
  return hasMaterializedChatSession({
423
- conversationDir: join(this.workingDir, conversationId),
727
+ conversationDir: this.conversationDir(conversationId),
424
728
  sessionKey,
425
729
  });
426
730
  }
731
+ channelKindFor(channelId) {
732
+ if (channelId.startsWith("D"))
733
+ return "im";
734
+ const channel = this.channels.get(channelId);
735
+ if (!channel)
736
+ return undefined;
737
+ if (channel.name.startsWith("DM:"))
738
+ return "im";
739
+ if (channel.isExternallyShared)
740
+ return "external";
741
+ if (channel.isPrivate === true)
742
+ return "private_channel";
743
+ if (channel.isPrivate === false)
744
+ return "public_channel";
745
+ return undefined;
746
+ }
747
+ backfillChannelKinds() {
748
+ let recorded = 0;
749
+ for (const record of listRegisteredOffices(this.workspace.stateDir)) {
750
+ if (record.platform !== "slack")
751
+ continue;
752
+ const kind = this.channelKindFor(record.conversationId);
753
+ if (!kind)
754
+ continue;
755
+ try {
756
+ const office = this.workspace.office(record);
757
+ if (readPlatformChannelKind(office) === kind)
758
+ continue;
759
+ recordPlatformChannelKind(office, kind);
760
+ recorded++;
761
+ }
762
+ catch (err) {
763
+ log.logWarning("Failed to backfill Slack channel kind", String(err));
764
+ }
765
+ }
766
+ if (recorded > 0)
767
+ log.logInfo(`Recorded channel kind for ${recorded} Slack offices`);
768
+ }
427
769
  processSlackMessageIntake(options) {
428
- void processMessageIntake({
770
+ const kind = this.channelKindFor(options.event.address.conversationId);
771
+ if (kind) {
772
+ try {
773
+ recordPlatformChannelKind(this.workspace.office(options.event.address), kind);
774
+ }
775
+ catch (err) {
776
+ log.logWarning("Failed to record Slack channel kind", String(err));
777
+ }
778
+ }
779
+ const absorbAttachmentFailure = () => {
780
+ void options.attachmentsPromise.catch((err) => {
781
+ log.logWarning("Failed to log Slack message", String(err));
782
+ });
783
+ };
784
+ return processMessageIntake({
429
785
  eventBase: options.event,
430
- workingDir: this.workingDir,
431
- isAutoReplyCandidate: options.isAutoReplyCandidate,
786
+ addressed: options.addressed,
787
+ magicWord: {
788
+ addressed: options.magicWordAddressed ?? options.addressed,
789
+ scopeFallback: "top-level",
790
+ },
791
+ busyPolicy: "queue",
432
792
  logEntryBase: {},
433
793
  processAttachments: () => options.attachmentsPromise,
434
794
  queueKey: options.queueKey,
@@ -436,26 +796,16 @@ export class SlackMessagingBot {
436
796
  handler: this.handler,
437
797
  bot: this,
438
798
  createContext: (event) => this.createContext(event),
439
- onNotTriggered: options.onNotTriggered,
440
799
  deferAttachmentsUntilRun: true,
800
+ }).then((outcome) => {
801
+ if (outcome !== "enqueued")
802
+ absorbAttachmentFailure();
803
+ }, (err) => {
804
+ log.logWarning("Slack message intake failed", String(err));
805
+ absorbAttachmentFailure();
441
806
  });
442
807
  }
443
- buildHomeView() {
444
- const blocks = [
445
- {
446
- type: "section",
447
- text: {
448
- type: "mrkdwn",
449
- text: `*${PRODUCT_NAME}*\nStart a new task or check on running work.`,
450
- },
451
- accessory: {
452
- type: "image",
453
- image_url: "https://media1.tenor.com/m/lfDATg4Bhc0AAAAC/happy-cat.gif",
454
- alt_text: PRODUCT_NAME,
455
- },
456
- },
457
- ];
458
- // --- Running tasks ---
808
+ appendRunningTasks(blocks) {
459
809
  const runningSessions = this.handler.getRunningSessions();
460
810
  blocks.push({ type: "divider" }, {
461
811
  type: "header",
@@ -470,61 +820,51 @@ export class SlackMessagingBot {
470
820
  type: "context",
471
821
  elements: [{ type: "mrkdwn", text: "_No tasks running right now._" }],
472
822
  });
823
+ return;
473
824
  }
474
- else {
475
- // Threshold for "stuck" detection (10 minutes)
476
- const STUCK_THRESHOLD_MS = 10 * 60 * 1000;
477
- for (const session of runningSessions) {
478
- const channelId = session.sessionKey.split(":")[0];
479
- const channel = this.channels.get(channelId);
480
- const channelName = channel ? `#${channel.name}` : channelId;
481
- const elapsed = Math.floor((Date.now() - session.startedAt) / 60000);
482
- const elapsedStr = elapsed < 1 ? "<1 min" : `${elapsed} min`;
483
- // Check if task might be stuck
484
- const lastActivity = session.lastActivityAt ? Date.now() - session.lastActivityAt : 0;
485
- const isStuck = lastActivity > STUCK_THRESHOLD_MS;
486
- const statusText = isStuck ? "_stuck_" : "_running_";
487
- // Build status line: channel · status · time · step
488
- let statusLine = `${statusText} · ${elapsedStr}`;
489
- if (session.currentTool) {
490
- statusLine += ` · ${session.currentTool}`;
491
- }
492
- if (isStuck && lastActivity > 0) {
493
- const inactiveMin = Math.floor(lastActivity / 60000);
494
- statusLine += ` · idle ${inactiveMin}m`;
495
- }
496
- // Use context block for gray small text (like "No scheduled jobs.")
825
+ const stuckThresholdMs = 10 * 60 * 1000;
826
+ for (const session of runningSessions) {
827
+ const channelId = conversationIdOf(session.sessionKey);
828
+ const channelName = this.channels.get(channelId)?.name;
829
+ const elapsed = Math.floor((Date.now() - session.startedAt) / 60000);
830
+ const elapsedStr = elapsed < 1 ? "<1 min" : `${elapsed} min`;
831
+ const lastActivity = session.lastActivityAt ? Date.now() - session.lastActivityAt : 0;
832
+ const isStuck = lastActivity > stuckThresholdMs;
833
+ let statusLine = `${isStuck ? "_stuck_" : "_running_"} · ${elapsedStr}`;
834
+ if (session.currentTool)
835
+ statusLine += ` · ${session.currentTool}`;
836
+ if (isStuck)
837
+ statusLine += ` · idle ${Math.floor(lastActivity / 60000)}m`;
838
+ blocks.push({
839
+ type: "context",
840
+ elements: [
841
+ {
842
+ type: "mrkdwn",
843
+ text: `*${channelName ? `#${channelName}` : channelId}* · ${statusLine}`,
844
+ },
845
+ ],
846
+ });
847
+ if (isStuck) {
497
848
  blocks.push({
498
849
  type: "context",
499
850
  elements: [
851
+ { type: "mrkdwn", text: " " },
500
852
  {
501
- type: "mrkdwn",
502
- text: `*${channelName}* · ${statusLine}`,
853
+ type: "button",
854
+ text: { type: "plain_text", text: "Force Stop", emoji: true },
855
+ action_id: `force_stop_${session.sessionKey.replace(/:/g, "_")}`,
856
+ value: session.sessionKey,
857
+ style: "danger",
503
858
  },
504
859
  ],
505
860
  });
506
- // Add Force Stop button as separate element if stuck
507
- if (isStuck) {
508
- blocks.push({
509
- type: "context",
510
- elements: [
511
- {
512
- type: "mrkdwn",
513
- text: " ",
514
- },
515
- {
516
- type: "button",
517
- text: { type: "plain_text", text: "Force Stop", emoji: true },
518
- action_id: `force_stop_${session.sessionKey.replace(/:/g, "_")}`,
519
- style: "danger",
520
- },
521
- ],
522
- });
523
- }
524
861
  }
525
862
  }
526
- // --- Cron jobs ---
527
- const periodicEvents = this.eventsWatcher?.getPeriodicEvents() ?? [];
863
+ }
864
+ appendScheduledJobs(blocks, dmChannelId) {
865
+ const periodicEvents = dmChannelId && this.eventScheduler
866
+ ? this.eventScheduler.periodicEvents(createOfficeAddress("slack", dmChannelId))
867
+ : [];
528
868
  blocks.push({ type: "divider" }, {
529
869
  type: "header",
530
870
  text: {
@@ -538,34 +878,46 @@ export class SlackMessagingBot {
538
878
  type: "context",
539
879
  elements: [{ type: "mrkdwn", text: "_No scheduled jobs._" }],
540
880
  });
881
+ return;
541
882
  }
542
- else {
543
- for (const ev of periodicEvents) {
544
- const channelLabel = ev.platform === "slack"
545
- ? (() => {
546
- const channel = this.channels.get(ev.conversationId);
547
- const channelName = channel ? `#${channel.name}` : ev.conversationId;
548
- return `${ev.platform}:${channelName}`;
549
- })()
550
- : `${ev.platform}:${ev.conversationId}`;
551
- const nextStr = ev.nextRun
552
- ? new Date(ev.nextRun).toLocaleString("en-US", {
553
- month: "short",
554
- day: "numeric",
555
- hour: "2-digit",
556
- minute: "2-digit",
557
- })
558
- : "—";
559
- blocks.push({
560
- type: "section",
561
- text: {
562
- type: "mrkdwn",
563
- text: `*${ev.text}*\n└ \`${ev.schedule}\` · ${channelLabel} · Next: ${nextStr}`,
564
- },
565
- });
566
- }
883
+ for (const event of periodicEvents) {
884
+ const channel = event.platform === "slack" ? this.channels.get(event.conversationId) : undefined;
885
+ const channelId = channel ? `#${channel.name}` : event.conversationId;
886
+ const channelLabel = `${event.platform}:${channelId}`;
887
+ const nextRun = event.nextRun
888
+ ? new Date(event.nextRun).toLocaleString("en-US", {
889
+ month: "short",
890
+ day: "numeric",
891
+ hour: "2-digit",
892
+ minute: "2-digit",
893
+ })
894
+ : "—";
895
+ blocks.push({
896
+ type: "section",
897
+ text: {
898
+ type: "mrkdwn",
899
+ text: `*${event.text}*\n└ \`${event.schedule}\` · ${channelLabel} · Next: ${nextRun}`,
900
+ },
901
+ });
567
902
  }
568
- // --- Footer ---
903
+ }
904
+ buildHomeView(dmChannelId) {
905
+ const blocks = [
906
+ {
907
+ type: "section",
908
+ text: {
909
+ type: "mrkdwn",
910
+ text: `*${PRODUCT_NAME}*\nStart a new task or check on running work.`,
911
+ },
912
+ accessory: {
913
+ type: "image",
914
+ image_url: "https://media1.tenor.com/m/lfDATg4Bhc0AAAAC/happy-cat.gif",
915
+ alt_text: PRODUCT_NAME,
916
+ },
917
+ },
918
+ ];
919
+ this.appendRunningTasks(blocks);
920
+ this.appendScheduledJobs(blocks, dmChannelId);
569
921
  blocks.push({ type: "divider" }, {
570
922
  type: "context",
571
923
  elements: [
@@ -574,55 +926,35 @@ export class SlackMessagingBot {
574
926
  });
575
927
  return { type: "home", blocks: blocks };
576
928
  }
577
- resolveStopTarget(channelId, threadTs) {
578
- const directTarget = resolveStopTarget({
579
- handler: this.handler,
580
- conversationId: channelId,
581
- sessionKey: resolveSlackSessionKey(channelId, threadTs),
582
- });
583
- if (directTarget)
584
- return directTarget;
585
- if (threadTs)
586
- return null;
587
- return resolveOnlyScopedStopTarget(this.handler, channelId);
588
- }
589
- isStopText(text) {
590
- const normalized = text.trim().toLowerCase();
591
- return normalized === "stop" || normalized === "/stop";
592
- }
593
- createCommandAdapters(conversationId, userId, userName, text, ts, options = {}) {
594
- const message = {
929
+ createCommandAdapters(input) {
930
+ const { conversationId, userId, userName, text, ts, ...options } = input;
931
+ const message = createConversationMessage({
932
+ platform: "slack",
933
+ conversationId,
595
934
  id: ts,
596
- sessionKey: conversationId,
935
+ sessionKey: options.sessionKey ?? conversationId,
597
936
  conversationKind: options.ephemeralChannelId ? "shared" : "direct",
598
937
  userId,
599
938
  userName,
600
939
  text,
601
940
  attachments: [],
602
- };
603
- const respond = async (responseText) => {
604
- if (options.ephemeralChannelId) {
605
- await this.postEphemeral(options.ephemeralChannelId, userId, responseText, options.threadTs);
606
- return;
607
- }
608
- const messageTs = await this.postMessage(conversationId, responseText);
609
- this.logBotResponse(conversationId, responseText, messageTs);
610
- };
611
- const respondMuted = async (responseText) => {
612
- const blocks = [buildMrkdwnContextBlock(responseText)];
941
+ });
942
+ const respond = async (responseText, blocks) => {
613
943
  if (options.ephemeralChannelId) {
614
- await this.postEphemeralBlocks(options.ephemeralChannelId, userId, responseText, blocks, options.threadTs);
944
+ await this.postEphemeral(options.ephemeralChannelId, userId, responseText, options.threadTs, blocks);
615
945
  return;
616
946
  }
617
- const messageTs = await this.postMessageBlocks(conversationId, responseText, blocks);
947
+ const messageTs = blocks
948
+ ? await this.postMessageBlocks(conversationId, responseText, blocks)
949
+ : await this.postMessage(conversationId, responseText);
618
950
  this.logBotResponse(conversationId, responseText, messageTs);
619
951
  };
620
952
  const responder = {
621
953
  respond,
622
- replaceResponse: respond,
954
+ replaceResponse: (responseText) => respond(responseText),
623
955
  respondDiagnostic: async (responseText, responseOptions) => {
624
956
  if (responseOptions?.style === "muted") {
625
- await respondMuted(responseText);
957
+ await respond(responseText, [buildMrkdwnContextBlock(responseText)]);
626
958
  return;
627
959
  }
628
960
  await respond(responseOptions?.style === "error" ? `_${responseText}_` : responseText);
@@ -639,6 +971,7 @@ export class SlackMessagingBot {
639
971
  deleteResponse: async () => { },
640
972
  };
641
973
  return {
974
+ address: message.address,
642
975
  message,
643
976
  responder,
644
977
  platform: this.getMessagingInfo(),
@@ -664,66 +997,42 @@ export class SlackMessagingBot {
664
997
  text: commandText,
665
998
  attachments: [],
666
999
  isMessagingBot: false,
667
- ...(threadTs ? { threadTs } : {}),
1000
+ threadTs: threadTs || undefined,
668
1001
  });
669
- const event = {
670
- type: options.type ?? (isDirectMessage ? "dm" : "mention"),
1002
+ const event = createConversationEvent({
1003
+ platform: "slack",
671
1004
  conversationId,
1005
+ type: options.type ?? (isDirectMessage ? "dm" : "mention"),
672
1006
  conversationKind: isDirectMessage ? "direct" : "shared",
673
1007
  ts: eventTs,
674
1008
  user: payload.user_id,
675
1009
  text: commandText,
676
1010
  attachments: [],
677
- ...(threadTs ? { thread_ts: threadTs } : {}),
1011
+ thread_ts: threadTs || undefined,
678
1012
  sessionKey,
679
- };
680
- const context = this.createCommandAdapters(conversationId, payload.user_id, userName, commandText, eventTs, isDirectMessage ? { threadTs } : { ephemeralChannelId: conversationId, threadTs });
681
- return { event, context };
682
- }
683
- async routeSlashLoginCommand(payload) {
684
- const { event, context } = this.buildSlashCommandEvent(payload, {
685
- type: payload.channel_id.startsWith("D") ? "dm" : "private_command",
686
- includeText: true,
687
1013
  });
688
- await this.handler.handleEvent(event, this, context);
689
- }
690
- async routeSlashNewCommand(payload) {
691
- const conversationId = payload.channel_id;
692
- if (!conversationId.startsWith("D")) {
693
- await this.postEphemeral(conversationId, payload.user_id, `為了避免誤清除共享上下文,${payload.command} 目前只能在與 ${PRODUCT_NAME} 的私訊中使用。`);
694
- return;
695
- }
696
- const createdAt = new Date();
697
- const eventTs = (createdAt.getTime() / 1000).toFixed(6);
698
- const userName = payload.user_name ?? this.getUser(payload.user_id)?.userName;
699
- this.logToFile(conversationId, {
700
- date: createdAt.toISOString(),
701
- ts: eventTs,
702
- user: payload.user_id,
1014
+ const context = this.createCommandAdapters({
1015
+ conversationId,
1016
+ userId: payload.user_id,
703
1017
  userName,
704
- text: payload.command,
705
- attachments: [],
706
- isMessagingBot: false,
1018
+ text: commandText,
1019
+ ts: eventTs,
1020
+ ephemeralChannelId: isDirectMessage ? undefined : conversationId,
1021
+ threadTs,
1022
+ sessionKey,
707
1023
  });
708
- const commandMessagingBot = {
709
- start: async () => { },
710
- postMessage: async (_channel, text) => this.postMessage(conversationId, text),
711
- updateMessage: async (channel, ts, text) => this.updateMessage(channel, ts, text),
712
- enqueueEvent: (event) => this.enqueueEvent(event),
713
- getMessagingInfo: () => this.getMessagingInfo(),
714
- };
715
- await this.handler.handleNewCommand(conversationId, conversationId, commandMessagingBot);
716
- }
717
- async routeSlashModelCommand(payload) {
718
- const { event, context } = this.buildSlashCommandEvent(payload, { includeText: true });
719
- await this.handler.handleEvent(event, this, context);
720
- }
721
- async routeSlashSessionCommand(payload) {
722
- const { event, context } = this.buildSlashCommandEvent(payload, { thread: true });
723
- await this.handler.handleEvent(event, this, context);
1024
+ return { event, context };
724
1025
  }
725
- async routeSlashAdminCommand(payload) {
726
- const { event, context } = this.buildSlashCommandEvent(payload, { thread: true });
1026
+ async routeSlashCommand(route, payload) {
1027
+ const { event, context } = this.buildSlashCommandEvent(payload, {
1028
+ includeText: route.includeText,
1029
+ thread: route.thread,
1030
+ type: route.privateCommand
1031
+ ? payload.channel_id.startsWith("D")
1032
+ ? "dm"
1033
+ : "private_command"
1034
+ : undefined,
1035
+ });
727
1036
  await this.handler.handleEvent(event, this, context);
728
1037
  }
729
1038
  setupEventHandlers() {
@@ -736,38 +1045,39 @@ export class SlackMessagingBot {
736
1045
  this.socketClient.on("unable_to_socket_mode_start", (err) => {
737
1046
  log.logWarning("Slack socket unable_to_start", err ? String(err) : "");
738
1047
  });
739
- this.socketClient.on("app_mention", (payload) => this.handleAppMention(payload));
740
- this.socketClient.on("message", (payload) => this.handleMessageEvent(payload));
741
- this.socketClient.on("slash_commands", (payload) => void this.handleSlashCommand(payload));
742
- this.socketClient.on("app_home_opened", (payload) => this.handleAppHomeOpened(payload));
743
- this.socketClient.on("block_actions", (payload) => void this.handleBlockAction(payload));
744
- this.socketClient.on("interactive", (payload) => void this.handleBlockAction(payload));
745
- }
746
- handleAppMention({ event, ack }) {
1048
+ this.socketClient.on("app_mention", (payload) => this.intake.run(() => this.handleAppMention(payload)));
1049
+ this.socketClient.on("message", (payload) => this.intake.run(() => this.handleMessageEvent(payload)));
1050
+ this.socketClient.on("slash_commands", (payload) => this.intake.run(() => this.handleSlashCommand(payload)));
1051
+ this.socketClient.on("app_home_opened", (payload) => this.intake.run(() => this.handleAppHomeOpened(payload)));
1052
+ this.socketClient.on("assistant_thread_started", (payload) => this.intake.run(() => this.handleAssistantThreadStarted(payload)));
1053
+ this.socketClient.on("assistant_thread_context_changed", (payload) => this.intake.run(() => this.handleAgentContextChangedEvent(payload)));
1054
+ this.socketClient.on("app_context_changed", (payload) => this.intake.run(() => this.handleAgentContextChangedEvent(payload)));
1055
+ const blockAction = ({ body, ack }) => this.intake.run(() => this.handleBlockAction({ body: body, ack }));
1056
+ this.socketClient.on("block_actions", blockAction);
1057
+ this.socketClient.on("interactive", blockAction);
1058
+ }
1059
+ async handleAppMention({ event, ack }) {
747
1060
  const e = event;
748
- // Skip DMs (handled by message event)
749
1061
  if (e.channel.startsWith("D")) {
750
1062
  ack();
751
1063
  return;
752
1064
  }
753
- // Top-level mentions use a persistent channel session.
754
- // Thread replies get their own isolated session (channelId:thread_ts).
755
1065
  const sessionKey = resolveSlackSessionKey(e.channel, e.thread_ts);
756
1066
  const mentionText = this.stripOwnMention(e.text);
757
- const slackEvent = {
758
- type: "mention",
1067
+ const slackEvent = createConversationEvent({
1068
+ platform: "slack",
759
1069
  conversationId: e.channel,
1070
+ type: "mention",
760
1071
  conversationKind: "shared",
761
- channel: e.channel,
762
1072
  ts: e.ts,
763
1073
  thread_ts: e.thread_ts,
764
1074
  user: e.user,
765
1075
  text: mentionText || "Please respond to the recent conversation context.",
766
- files: e.files,
767
1076
  sessionKey,
768
- };
1077
+ channel: e.channel,
1078
+ files: e.files,
1079
+ });
769
1080
  const attachmentsPromise = this.logUserMessage(slackEvent);
770
- // Only trigger processing for messages AFTER startup (not replayed old messages)
771
1081
  if (this.startupTs && e.ts < this.startupTs) {
772
1082
  log.logInfo(`[${e.channel}] Logged old message (pre-startup), not triggering: ${slackEvent.text.substring(0, 30)}`);
773
1083
  void attachmentsPromise.catch((err) => {
@@ -776,90 +1086,156 @@ export class SlackMessagingBot {
776
1086
  ack();
777
1087
  return;
778
1088
  }
779
- // Check for stop command - execute immediately, don't queue!
780
- if (this.isStopText(slackEvent.text)) {
781
- const stopTarget = this.resolveStopTarget(e.channel, e.thread_ts);
782
- if (stopTarget) {
783
- this.handler.handleStop(stopTarget, e.channel, this);
784
- }
785
- else {
786
- this.postMessage(e.channel, formatNothingRunning("slack"));
787
- }
788
- void attachmentsPromise.catch((err) => {
789
- log.logWarning("Failed to log Slack message", String(err));
790
- });
791
- ack();
792
- return;
793
- }
794
- this.processSlackMessageIntake({
1089
+ const intake = this.processSlackMessageIntake({
795
1090
  event: slackEvent,
796
1091
  attachmentsPromise,
797
1092
  queueKey: this.resolveQueueKey(e.channel, sessionKey),
798
- isAutoReplyCandidate: false,
1093
+ addressed: true,
799
1094
  });
800
1095
  ack();
1096
+ await intake;
801
1097
  }
802
- handleMessageEvent({ event, ack }) {
803
- const e = event;
804
- const hasFiles = !!e.files && e.files.length > 0;
805
- const hasSlackContent = !!e.text || hasFiles || !!e.blocks?.length || !!e.attachments?.length;
806
- const isOwnMessagingBotMessage = (!!e.user && e.user === this.botUserId) || (!!this.botId && e.bot_id === this.botId);
807
- if (isOwnMessagingBotMessage) {
1098
+ admitHumanMessage(event, ack) {
1099
+ const hasSlackContent = hasMessageContent(event);
1100
+ const isOwnMessage = event.user === this.botUserId || (!!this.botId && event.bot_id === this.botId);
1101
+ if (isOwnMessage) {
808
1102
  ack();
809
- return;
1103
+ return false;
810
1104
  }
811
- const isExternalMessagingBotMessage = !!e.bot_id || e.subtype === "bot_message";
812
- if (isExternalMessagingBotMessage) {
813
- if (e.subtype !== undefined && e.subtype !== "bot_message" && e.subtype !== "file_share") {
814
- ack();
815
- return;
816
- }
817
- if (!hasSlackContent) {
818
- ack();
819
- return;
1105
+ const authorIsKnownHuman = !!event.user && this.users.get(event.user)?.isBot === false;
1106
+ const isExternalMessage = event.subtype === "bot_message" || (!!event.bot_id && !authorIsKnownHuman);
1107
+ if (isExternalMessage) {
1108
+ const supportedSubtype = BOT_MESSAGE_SUBTYPES.has(event.subtype);
1109
+ if (supportedSubtype && hasSlackContent) {
1110
+ void this.logExternalMessagingBotMessage(event).catch((err) => {
1111
+ log.logWarning("Failed to log Slack bot message", String(err));
1112
+ });
820
1113
  }
821
- void this.logExternalMessagingBotMessage(e).catch((err) => {
822
- log.logWarning("Failed to log Slack bot message", String(err));
823
- });
824
1114
  ack();
825
- return;
1115
+ return false;
826
1116
  }
827
- if (!e.user) {
828
- ack();
829
- return;
1117
+ const isSupportedUserMessage = !!event.user && USER_MESSAGE_SUBTYPES.has(event.subtype) && hasSlackContent;
1118
+ if (isSupportedUserMessage)
1119
+ return true;
1120
+ ack();
1121
+ return false;
1122
+ }
1123
+ async deliverTaskUpdate(event, attachmentsPromise) {
1124
+ if (matchMagicWord(event.text) === "stop" || event.text.startsWith("/"))
1125
+ return false;
1126
+ event.attachments = await attachmentsPromise;
1127
+ const context = this.createContext(event);
1128
+ const post = (text) => event.thread_ts
1129
+ ? this.postInThread(event.channel, event.thread_ts, text)
1130
+ : this.postMessage(event.channel, text);
1131
+ try {
1132
+ const tasks = await context.responder.getTaskStatus(event.thread_ts ? event.sessionKey : undefined);
1133
+ if (!tasks.length)
1134
+ return false;
1135
+ const active = tasks.filter((t) => t.status === "running" || t.status === "stopping");
1136
+ const user = this.users.get(event.user);
1137
+ const intent = event.attachments?.length
1138
+ ? event.thread_ts
1139
+ ? "steer"
1140
+ : "request"
1141
+ : await classifyTaskIntent(buildTaskIntentState(this.conversationDir(event.channel), event, {
1142
+ speaker: user?.displayName ?? user?.userName,
1143
+ tasks,
1144
+ resolveName: (id) => this.users.get(id)?.displayName ?? this.users.get(id)?.userName,
1145
+ botUserId: this.botUserId,
1146
+ }), event.text, { conversationId: event.channel, inTaskThread: !!event.thread_ts });
1147
+ if (intent === "status") {
1148
+ const selection = event.thread_ts ? tasks : active.length ? active : tasks.slice(0, 1);
1149
+ const text = selection.length > 1
1150
+ ? "目前有多個任務在處理,請到你想查詢的任務對話串詢問,避免弄錯。"
1151
+ : formatTaskStatus(selection);
1152
+ const ts = await post(text);
1153
+ this.logBotResponse(event.channel, text, ts, event.thread_ts);
1154
+ return true;
1155
+ }
1156
+ if (intent === "steer") {
1157
+ const target = event.thread_ts
1158
+ ? context.message
1159
+ : active.length === 1
1160
+ ? {
1161
+ ...context.message,
1162
+ sessionKey: active[0].sessionKey,
1163
+ threadTs: active[0].threadTs,
1164
+ }
1165
+ : undefined;
1166
+ if (target && (await this.handler.steer?.(target))) {
1167
+ const text = "收到補充,會在下一個處理步驟納入;目前的操作不會立即中斷。";
1168
+ const ts = await post(text);
1169
+ this.logBotResponse(event.channel, text, ts, event.thread_ts);
1170
+ return true;
1171
+ }
1172
+ }
830
1173
  }
831
- if (e.subtype !== undefined && e.subtype !== "file_share") {
832
- ack();
833
- return;
1174
+ catch (error) {
1175
+ await post(error instanceof Error ? error.message : "Could not deliver task update.");
1176
+ return true;
834
1177
  }
835
- if (!hasSlackContent) {
836
- ack();
837
- return;
1178
+ return false;
1179
+ }
1180
+ async evaluateJevAddressed(event) {
1181
+ if (!event.text.trim() || matchMagicWord(event.text) === "stop")
1182
+ return false;
1183
+ try {
1184
+ const user = this.users.get(event.user);
1185
+ const state = buildAutoReplyState(this.conversationDir(event.channel), event, {
1186
+ speaker: user?.displayName ?? user?.userName,
1187
+ resolveName: (id) => this.users.get(id)?.displayName ?? this.users.get(id)?.userName,
1188
+ botUserId: this.botUserId,
1189
+ });
1190
+ const result = await evaluateWithJev(state, { addressed: { type: "boolean", instructions: JEV_ADDRESSED_INSTRUCTIONS } }, { caller: "slack_auto_reply" });
1191
+ const probability = result.answers.addressed.probability;
1192
+ log.logInfo(`[${event.channel}] jev auto-reply: probability=${probability.toFixed(2)} addressed=${probability > 0.5} text="${event.text.slice(0, 80)}"`);
1193
+ return probability > 0.5;
1194
+ }
1195
+ catch (err) {
1196
+ if (err instanceof JevNotConfiguredError) {
1197
+ log.logWarning("Slack auto-reply jev mode requires OPENROUTER_API_KEY; treating message as unaddressed", String(err));
1198
+ return false;
1199
+ }
1200
+ reportUserFacingError(err, {
1201
+ domain: "chat_platform",
1202
+ surface: "slack_auto_reply_jev",
1203
+ operation: "evaluate",
1204
+ severity: "warning",
1205
+ context: { conversationId: event.address.conversationId },
1206
+ });
1207
+ return false;
838
1208
  }
839
- const isDM = e.channel_type === "im";
1209
+ }
1210
+ async handleMessageEvent({ event, ack }) {
1211
+ const e = event;
1212
+ if (!this.admitHumanMessage(e, ack))
1213
+ return;
1214
+ const isDM = e.channel_type === "im" || e.channel.startsWith("D");
840
1215
  const conversationKind = isDM ? "direct" : "shared";
841
1216
  const isMessagingBotMention = e.text?.includes(`<@${this.botUserId}>`);
842
- // Skip channel @mentions - already handled by app_mention event
843
1217
  if (!isDM && isMessagingBotMention) {
844
1218
  ack();
845
1219
  return;
846
1220
  }
847
- const isThreadReply = !!e.thread_ts;
848
1221
  const sessionKey = isDM ? resolveSlackSessionKey(e.channel, e.thread_ts) : undefined;
849
- const slackEvent = {
850
- type: isDM ? "dm" : "mention",
1222
+ if (isDM && e.thread_ts && e.text) {
1223
+ void titleAssistantThread(this.assistantOps(), this.assistantThreads, e.channel, e.thread_ts, this.stripOwnMention(e.text));
1224
+ }
1225
+ const slackEvent = createConversationEvent({
1226
+ platform: "slack",
851
1227
  conversationId: e.channel,
1228
+ type: (isDM ? "dm" : "mention"),
852
1229
  conversationKind,
853
- channel: e.channel,
854
1230
  ts: e.ts,
855
1231
  thread_ts: e.thread_ts,
856
1232
  user: e.user,
857
1233
  text: this.stripOwnMention(e.text),
858
- files: e.files,
859
1234
  sessionKey,
860
- };
1235
+ channel: e.channel,
1236
+ files: e.files,
1237
+ });
861
1238
  const attachmentsPromise = this.logUserMessage(slackEvent);
862
- // Only trigger processing for messages AFTER startup (not replayed old messages)
863
1239
  if (this.startupTs && e.ts < this.startupTs) {
864
1240
  log.logInfo(`[${e.channel}] Skipping old message (pre-startup): ${slackEvent.text.substring(0, 30)}`);
865
1241
  void attachmentsPromise.catch((err) => {
@@ -868,136 +1244,77 @@ export class SlackMessagingBot {
868
1244
  ack();
869
1245
  return;
870
1246
  }
871
- // Stop command for DM only (app_mention handles shared-channel "@mikan stop").
872
- if (isDM && this.isStopText(slackEvent.text)) {
873
- const stopTarget = this.resolveStopTarget(e.channel, e.thread_ts);
874
- if (stopTarget) {
875
- this.handler.handleStop(stopTarget, e.channel, this);
876
- }
877
- else {
878
- this.postMessage(e.channel, formatNothingRunning("slack"));
879
- }
880
- void attachmentsPromise.catch((err) => {
881
- log.logWarning("Failed to log Slack message", String(err));
882
- });
883
- ack();
884
- return;
885
- }
886
- const enqueueTriggered = () => {
887
- const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
888
- // Auto-reply top-level channel messages start with no sessionKey because
889
- // they are only candidates until the policy allows them. Once triggered,
890
- // persist the resolved key on the event; otherwise the runtime fallback
891
- // treats the message ts as a thread session (`channel:ts`) instead of the
892
- // persistent top-level channel session.
893
- slackEvent.sessionKey = activeSessionKey;
894
- this.processSlackMessageIntake({
895
- event: slackEvent,
896
- attachmentsPromise,
897
- queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
898
- isAutoReplyCandidate: false,
899
- });
900
- };
901
- const logOnly = () => {
902
- void attachmentsPromise.catch((err) => {
903
- log.logWarning("Failed to log Slack message", String(err));
904
- });
905
- };
906
- if (isDM) {
907
- enqueueTriggered();
908
- ack();
909
- return;
910
- }
911
- if (isThreadReply) {
912
- logOnly();
1247
+ const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
1248
+ slackEvent.sessionKey = activeSessionKey;
1249
+ const taskControl = isDM &&
1250
+ (e.thread_ts
1251
+ ? this.isTaskThread(e.channel, e.thread_ts)
1252
+ : this.hasRunningTaskThread(e.channel) || isTaskStatusQuestion(slackEvent.text));
1253
+ if (taskControl) {
913
1254
  ack();
914
- return;
1255
+ if (await this.deliverTaskUpdate(slackEvent, attachmentsPromise))
1256
+ return;
915
1257
  }
916
- const activeSessionKey = resolveSlackSessionKey(e.channel, e.thread_ts);
917
- slackEvent.sessionKey = activeSessionKey;
918
- this.processSlackMessageIntake({
1258
+ const autoReplyMode = isDM
1259
+ ? "off"
1260
+ : slackConversationAutoReplyMode(this.workspace.office(slackEvent.address));
1261
+ const autoReply = autoReplyMode === "on"
1262
+ ? true
1263
+ : autoReplyMode === "jev"
1264
+ ? await this.evaluateJevAddressed(slackEvent)
1265
+ : false;
1266
+ const intake = this.processSlackMessageIntake({
919
1267
  event: slackEvent,
920
1268
  attachmentsPromise,
921
1269
  queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
922
- isAutoReplyCandidate: true,
923
- onNotTriggered: logOnly,
1270
+ addressed: isDM || autoReply,
1271
+ magicWordAddressed: isDM,
924
1272
  });
925
- ack();
1273
+ if (!taskControl)
1274
+ ack();
1275
+ await intake;
926
1276
  }
927
- async handleSlashCommand({ body, ack, }) {
1277
+ async handleSlashCommand({ body, ack }) {
928
1278
  const payload = body;
929
1279
  await ack();
930
1280
  if (!payload.command || !payload.channel_id || !payload.user_id) {
931
1281
  return;
932
1282
  }
933
1283
  const { command, text, channel_id, user_id, user_name, thread_ts } = payload;
934
- let handlerPromise = null;
935
- if (command === "/pi-login") {
936
- handlerPromise = this.routeSlashLoginCommand({
1284
+ const entry = COMMAND_MANIFEST.find((candidate) => candidate.slackCommand === command);
1285
+ if (!entry)
1286
+ return;
1287
+ if (!entry.slackRoute)
1288
+ return;
1289
+ try {
1290
+ await this.routeSlashCommand(entry.slackRoute, {
937
1291
  command,
938
1292
  text,
939
1293
  channel_id,
940
1294
  user_id,
941
1295
  user_name,
942
- });
943
- }
944
- else if (command === "/pi-new") {
945
- handlerPromise = this.routeSlashNewCommand({ command, channel_id, user_id, user_name });
946
- }
947
- else if (command === "/pi-session") {
948
- handlerPromise = this.routeSlashSessionCommand({
949
- command,
950
- channel_id,
951
- user_id,
952
- user_name,
953
1296
  thread_ts,
954
1297
  });
955
1298
  }
956
- else if (command === "/pi-model") {
957
- handlerPromise = this.routeSlashModelCommand({
958
- command,
959
- text,
960
- channel_id,
961
- user_id,
962
- user_name,
963
- });
964
- }
965
- else if (command === "/pi-sandbox" ||
966
- command === "/pi-auto-reply" ||
967
- command === "/pi-extensions") {
968
- handlerPromise = this.routeSlashModelCommand({
969
- command,
970
- text,
971
- channel_id,
972
- user_id,
973
- user_name,
974
- });
1299
+ catch (err) {
1300
+ log.logWarning("Slack slash command error", errorMessage(err));
975
1301
  }
976
- else if (command === "/pi-admin") {
977
- handlerPromise = this.routeSlashAdminCommand({
978
- command,
979
- channel_id,
980
- user_id,
981
- user_name,
982
- thread_ts,
983
- });
984
- }
985
- if (!handlerPromise) {
986
- return;
987
- }
988
- handlerPromise.catch((err) => {
989
- log.logWarning("Slack slash command error", err instanceof Error ? err.message : String(err));
990
- });
991
1302
  }
992
1303
  handleAppHomeOpened({ event, ack }) {
993
1304
  const e = event;
994
1305
  ack();
1306
+ if (e.tab === "messages") {
1307
+ if (e.channel) {
1308
+ void handleAgentDmOpened(this.assistantOps(), this.assistantThreads, e.channel, e.context);
1309
+ }
1310
+ return;
1311
+ }
995
1312
  if (e.tab !== "home")
996
1313
  return;
997
1314
  this.webClient.views
998
1315
  .publish({
999
1316
  user_id: e.user,
1000
- view: this.buildHomeView(),
1317
+ view: this.buildHomeView(e.channel),
1001
1318
  })
1002
1319
  .catch((err) => {
1003
1320
  log.logWarning(`Failed to publish App Home view`, String(err));
@@ -1015,21 +1332,18 @@ export class SlackMessagingBot {
1015
1332
  return;
1016
1333
  }
1017
1334
  ack();
1018
- const sessionKey = action.action_id.replace("force_stop_", "").replace(/_/g, ":");
1335
+ const sessionKey = action.value ?? action.action_id.replace("force_stop_", "").replace(/_/g, ":");
1019
1336
  const userId = body.user?.id;
1020
- const channelId = body.container?.channel_id || sessionKey.split(":")[0];
1337
+ const channelId = body.container?.channel_id || conversationIdOf(sessionKey);
1021
1338
  log.logInfo(`[Force Stop] User ${userId} requested force stop for ${sessionKey}`);
1022
- // Use handler's forceStop method
1023
- this.handler.forceStop(sessionKey);
1024
- // Notify in channel
1339
+ this.handler.forceStop(createOfficeAddress("slack", channelId), sessionKey);
1025
1340
  await this.postMessage(channelId, formatForceStopped("slack", userId ?? "unknown"));
1026
- // Refresh home tab
1027
1341
  if (userId) {
1028
- this.webClient.views
1029
- .publish({
1342
+ this.openDirectConversation(userId)
1343
+ .then((dmChannelId) => this.webClient.views.publish({
1030
1344
  user_id: userId,
1031
- view: this.buildHomeView(),
1032
- })
1345
+ view: this.buildHomeView(dmChannelId),
1346
+ }))
1033
1347
  .catch((err) => {
1034
1348
  log.logWarning(`Failed to refresh App Home view`, String(err));
1035
1349
  });
@@ -1045,6 +1359,8 @@ export class SlackMessagingBot {
1045
1359
  const selectedOptions = Array.isArray(action.selected_options)
1046
1360
  ? action.selected_options
1047
1361
  : undefined;
1362
+ const threadTs = container.thread_ts;
1363
+ const sessionKey = resolveSlackSessionKey(channelId, threadTs);
1048
1364
  const selectedText = selectedOption?.text?.text ?? selectedOption?.value;
1049
1365
  const selectedTexts = selectedOptions?.map((option) => option.text?.text ?? option.value);
1050
1366
  const valueText = selectedTexts?.length
@@ -1052,12 +1368,10 @@ export class SlackMessagingBot {
1052
1368
  : (selectedText ?? action.value ?? action.action_id);
1053
1369
  const text = `[Slack action] ${action.action_id}: ${valueText}`;
1054
1370
  const ts = `action:${Date.now()}`;
1055
- const threadTs = container.thread_ts;
1056
- const sessionKey = resolveSlackSessionKey(channelId, threadTs);
1057
1371
  this.logToFile(channelId, {
1058
1372
  date: new Date().toISOString(),
1059
1373
  ts,
1060
- ...(threadTs ? { threadTs } : {}),
1374
+ threadTs: threadTs || undefined,
1061
1375
  user: userId,
1062
1376
  userName: body.user?.username ?? body.user?.name,
1063
1377
  text,
@@ -1080,50 +1394,87 @@ export class SlackMessagingBot {
1080
1394
  messageTs: container.message_ts,
1081
1395
  },
1082
1396
  });
1083
- const event = {
1084
- type: "slack_action",
1397
+ const event = createConversationEvent({
1398
+ platform: "slack",
1085
1399
  conversationId: channelId,
1400
+ type: "slack_action",
1086
1401
  conversationKind: channelId.startsWith("D") ? "direct" : "shared",
1087
1402
  ts,
1088
1403
  user: userId,
1089
1404
  text,
1090
1405
  attachments: [],
1091
- ...(threadTs ? { thread_ts: threadTs } : {}),
1406
+ thread_ts: threadTs || undefined,
1092
1407
  sessionKey,
1093
- };
1408
+ });
1094
1409
  this.getQueue(this.resolveQueueKey(channelId, sessionKey)).enqueue(async () => {
1095
1410
  const slackEvent = {
1096
- type: event.conversationKind === "direct" ? "dm" : "mention",
1097
- conversationId: channelId,
1098
- conversationKind: event.conversationKind,
1411
+ ...createConversationEvent({
1412
+ platform: "slack",
1413
+ conversationId: channelId,
1414
+ type: (event.conversationKind === "direct" ? "dm" : "mention"),
1415
+ conversationKind: event.conversationKind,
1416
+ ts,
1417
+ thread_ts: threadTs,
1418
+ user: userId,
1419
+ text,
1420
+ attachments: [],
1421
+ sessionKey,
1422
+ }),
1099
1423
  channel: channelId,
1100
- ts,
1101
- thread_ts: threadTs,
1102
- user: userId,
1103
- text,
1104
1424
  attachments: [],
1105
- sessionKey,
1106
1425
  };
1107
1426
  return this.handler.handleEvent(event, this, this.createContext(slackEvent));
1108
1427
  });
1109
1428
  }
1110
- /**
1111
- * Log a user message to log.jsonl after attachments are ready.
1112
- */
1429
+ async processAttachments(channelId, files, timestamp) {
1430
+ const items = [];
1431
+ for (const file of files) {
1432
+ const url = file.url_private_download || file.url_private;
1433
+ if (!url)
1434
+ continue;
1435
+ if (!file.name) {
1436
+ throw new Error(`Attachment missing name for URL: ${url}`);
1437
+ }
1438
+ items.push({
1439
+ name: file.name,
1440
+ timestampMs: Math.floor(parseFloat(timestamp) * 1000),
1441
+ download: (destPath) => this.downloadSlackFile(url, destPath),
1442
+ });
1443
+ }
1444
+ const office = this.workspace.office(createOfficeAddress("slack", channelId));
1445
+ const { saved, failed } = await saveIncomingAttachments(office, items);
1446
+ const firstFailure = failed[0];
1447
+ if (firstFailure) {
1448
+ const { name, error } = firstFailure;
1449
+ const errorMsg = errorMessage(error);
1450
+ throw new Error(`Failed to download attachment ${name}: ${errorMsg}`, { cause: error });
1451
+ }
1452
+ return saved;
1453
+ }
1454
+ async downloadSlackFile(url, destPath) {
1455
+ assertSlackFileUrl(url);
1456
+ await withRetry(async () => {
1457
+ const response = await this.fetchFn(url, {
1458
+ headers: { Authorization: `Bearer ${this.botToken}` },
1459
+ });
1460
+ if (!response.ok) {
1461
+ throw new AttachmentDownloadHttpError(`HTTP ${response.status}: ${response.statusText}`, response.status);
1462
+ }
1463
+ await writeResponseToFile(response, destPath);
1464
+ }, { maxAttempts: 3, baseDelayMs: 250, isRateLimited: isRetryableAttachmentDownloadError });
1465
+ }
1113
1466
  async logUserMessage(event) {
1114
1467
  const user = this.users.get(event.user);
1115
1468
  let attachments = [];
1116
1469
  let attachmentError;
1117
1470
  if (event.files) {
1118
1471
  try {
1119
- attachments = await this.store.processAttachments(event.channel, event.files, event.ts);
1472
+ attachments = await this.processAttachments(event.channel, event.files, event.ts);
1120
1473
  }
1121
1474
  catch (err) {
1122
1475
  attachmentError = err;
1123
1476
  }
1124
1477
  }
1125
- // Always write the text log, even if attachment processing failed — we want
1126
- // a record of the user message regardless of file-handling errors.
1127
1478
  this.logToFile(event.channel, {
1128
1479
  date: new Date(parseFloat(event.ts) * 1000).toISOString(),
1129
1480
  ts: event.ts,
@@ -1141,7 +1492,7 @@ export class SlackMessagingBot {
1141
1492
  }
1142
1493
  async logExternalMessagingBotMessage(event) {
1143
1494
  const attachments = event.files
1144
- ? await this.store.processAttachments(event.channel, event.files, event.ts)
1495
+ ? await this.processAttachments(event.channel, event.files, event.ts)
1145
1496
  : [];
1146
1497
  const botName = event.username ?? event.bot_profile?.name ?? event.bot_profile?.real_name ?? event.bot_id;
1147
1498
  this.logToFile(event.channel, {
@@ -1160,11 +1511,8 @@ export class SlackMessagingBot {
1160
1511
  });
1161
1512
  return attachments;
1162
1513
  }
1163
- // ==========================================================================
1164
- // Private - Backfill
1165
- // ==========================================================================
1166
1514
  async getExistingTimestamps(channelId) {
1167
- const logPath = join(this.workingDir, channelId, "log.jsonl");
1515
+ const { logPath } = this.office(channelId);
1168
1516
  const timestamps = new Set();
1169
1517
  if (!existsSync(logPath))
1170
1518
  return timestamps;
@@ -1172,19 +1520,30 @@ export class SlackMessagingBot {
1172
1520
  const lines = content.trim().split("\n").filter(Boolean);
1173
1521
  for (let i = 0; i < lines.length; i++) {
1174
1522
  try {
1175
- const entry = JSON.parse(lines[i]);
1523
+ const entry = JSON.parse(lines[i] ?? "");
1176
1524
  if (entry.ts)
1177
1525
  timestamps.add(entry.ts);
1178
1526
  }
1179
1527
  catch (err) {
1180
- log.logWarning(`Skipping malformed log entry at ${logPath}:${i + 1}`, err instanceof Error ? err.message : String(err));
1528
+ log.logWarning(`Skipping malformed log entry at ${logPath}:${i + 1}`, errorMessage(err));
1181
1529
  }
1182
1530
  }
1183
1531
  return timestamps;
1184
1532
  }
1533
+ isBackfillableMessage(message) {
1534
+ if (message.user === this.botUserId)
1535
+ return true;
1536
+ if (hasBotIdentity(message)) {
1537
+ if (this.botId && message.bot_id === this.botId)
1538
+ return false;
1539
+ return BOT_MESSAGE_SUBTYPES.has(message.subtype) && hasMessageContent(message);
1540
+ }
1541
+ return (!!message.user &&
1542
+ USER_MESSAGE_SUBTYPES.has(message.subtype) &&
1543
+ hasMessageContent(message, false));
1544
+ }
1185
1545
  async backfillChannel(channelId, upperBoundTs) {
1186
1546
  const existingTs = await this.getExistingTimestamps(channelId);
1187
- // Find the biggest ts in log.jsonl
1188
1547
  let lastLoggedTs;
1189
1548
  for (const ts of existingTs) {
1190
1549
  if (!lastLoggedTs || parseFloat(ts) > parseFloat(lastLoggedTs))
@@ -1197,8 +1556,8 @@ export class SlackMessagingBot {
1197
1556
  do {
1198
1557
  const result = await this.webClient.conversations.history({
1199
1558
  channel: channelId,
1200
- oldest: lastLoggedTs, // Only fetch messages newer than what we have
1201
- latest: upperBoundTs, // Do not race live socket events after startup
1559
+ oldest: lastLoggedTs,
1560
+ latest: upperBoundTs,
1202
1561
  inclusive: false,
1203
1562
  limit: 1000,
1204
1563
  cursor,
@@ -1209,40 +1568,15 @@ export class SlackMessagingBot {
1209
1568
  cursor = result.response_metadata?.next_cursor;
1210
1569
  pageCount++;
1211
1570
  } while (cursor && pageCount < maxPages);
1212
- // Filter: include mikan's messages, external app/bot messages, and user messages.
1213
1571
  const relevantMessages = allMessages.filter((msg) => {
1214
1572
  if (!msg.ts || existingTs.has(msg.ts))
1215
- return false; // Skip duplicates
1216
- if (msg.user === this.botUserId)
1217
- return true;
1218
- const isExternalMessagingBotMessage = !!msg.bot_id || msg.subtype === "bot_message";
1219
- if (isExternalMessagingBotMessage) {
1220
- if (this.botId && msg.bot_id === this.botId)
1221
- return false;
1222
- if (msg.subtype !== undefined &&
1223
- msg.subtype !== "bot_message" &&
1224
- msg.subtype !== "file_share") {
1225
- return false;
1226
- }
1227
- return (!!msg.text ||
1228
- !!(msg.files && msg.files.length > 0) ||
1229
- !!msg.blocks?.length ||
1230
- !!msg.attachments?.length);
1231
- }
1232
- if (msg.subtype !== undefined && msg.subtype !== "file_share")
1233
- return false;
1234
- if (!msg.user)
1235
- return false;
1236
- if (!msg.text && (!msg.files || msg.files.length === 0))
1237
1573
  return false;
1238
- return true;
1574
+ return this.isBackfillableMessage(msg);
1239
1575
  });
1240
- // Reverse to chronological order
1241
1576
  relevantMessages.reverse();
1242
- // Log each message to log.jsonl
1243
1577
  for (const msg of relevantMessages) {
1244
1578
  const isMikanMessage = msg.user === this.botUserId;
1245
- const isExternalMessagingBotMessage = !isMikanMessage && (!!msg.bot_id || msg.subtype === "bot_message");
1579
+ const isExternalMessagingBotMessage = !isMikanMessage && hasBotIdentity(msg);
1246
1580
  if (isExternalMessagingBotMessage) {
1247
1581
  await this.logExternalMessagingBotMessage({ ...msg, channel: channelId, ts: msg.ts });
1248
1582
  continue;
@@ -1250,7 +1584,7 @@ export class SlackMessagingBot {
1250
1584
  const user = this.users.get(msg.user);
1251
1585
  const text = this.stripOwnMention(msg.text);
1252
1586
  const attachments = msg.files
1253
- ? await this.store.processAttachments(channelId, msg.files, msg.ts)
1587
+ ? await this.processAttachments(channelId, msg.files, msg.ts)
1254
1588
  : [];
1255
1589
  this.logToFile(channelId, {
1256
1590
  date: new Date(parseFloat(msg.ts) * 1000).toISOString(),
@@ -1268,11 +1602,9 @@ export class SlackMessagingBot {
1268
1602
  }
1269
1603
  async backfillAllChannels(upperBoundTs) {
1270
1604
  const startTime = Date.now();
1271
- // Only backfill channels that already have a log.jsonl (mikan has interacted with them before)
1272
1605
  const channelsToBackfill = [];
1273
1606
  for (const [channelId, channel] of this.channels) {
1274
- const logPath = join(this.workingDir, channelId, "log.jsonl");
1275
- if (existsSync(logPath)) {
1607
+ if (existsSync(this.office(channelId).logPath)) {
1276
1608
  channelsToBackfill.push([channelId, channel]);
1277
1609
  }
1278
1610
  }
@@ -1288,38 +1620,32 @@ export class SlackMessagingBot {
1288
1620
  catch (error) {
1289
1621
  log.logWarning(`Failed to backfill #${channel.name}`, String(error));
1290
1622
  }
1291
- // Add delay between channels to avoid hitting Slack rate limits
1292
- if (channelId !== channelsToBackfill[channelsToBackfill.length - 1][0]) {
1623
+ if (channelId !== channelsToBackfill[channelsToBackfill.length - 1]?.[0]) {
1293
1624
  await new Promise((resolve) => setTimeout(resolve, 500));
1294
1625
  }
1295
1626
  }
1296
1627
  const durationMs = Date.now() - startTime;
1297
1628
  log.logBackfillComplete(totalMessages, durationMs);
1298
1629
  }
1299
- // ==========================================================================
1300
- // Private - Fetch Users/Channels
1301
- // ==========================================================================
1302
1630
  async fetchUsers() {
1303
1631
  let cursor;
1304
1632
  do {
1305
1633
  const result = await this.webClient.users.list({ limit: 200, cursor });
1306
1634
  const members = result.members;
1307
- if (members) {
1308
- for (const u of members) {
1309
- if (u.id && u.name && !u.deleted) {
1310
- this.users.set(u.id, {
1311
- id: u.id,
1312
- userName: u.name,
1313
- displayName: u.real_name || u.name,
1314
- });
1315
- }
1316
- }
1635
+ for (const u of members ?? []) {
1636
+ if (!u.id || !u.name || u.deleted)
1637
+ continue;
1638
+ this.users.set(u.id, {
1639
+ id: u.id,
1640
+ userName: u.name,
1641
+ displayName: u.real_name || u.name,
1642
+ isBot: !!u.is_bot,
1643
+ });
1317
1644
  }
1318
1645
  cursor = result.response_metadata?.next_cursor;
1319
1646
  } while (cursor);
1320
1647
  }
1321
1648
  async fetchChannels() {
1322
- // Fetch public/private channels
1323
1649
  let cursor;
1324
1650
  do {
1325
1651
  const result = await this.webClient.conversations.list({
@@ -1329,16 +1655,18 @@ export class SlackMessagingBot {
1329
1655
  cursor,
1330
1656
  });
1331
1657
  const channels = result.channels;
1332
- if (channels) {
1333
- for (const c of channels) {
1334
- if (c.id && c.name && c.is_member) {
1335
- this.channels.set(c.id, { id: c.id, name: c.name });
1336
- }
1337
- }
1658
+ for (const c of channels ?? []) {
1659
+ if (!c.id || !c.name || !c.is_member)
1660
+ continue;
1661
+ this.channels.set(c.id, {
1662
+ id: c.id,
1663
+ name: c.name,
1664
+ isPrivate: typeof c.is_private === "boolean" ? c.is_private : undefined,
1665
+ isExternallyShared: c.is_shared || c.is_ext_shared ? true : undefined,
1666
+ });
1338
1667
  }
1339
1668
  cursor = result.response_metadata?.next_cursor;
1340
1669
  } while (cursor);
1341
- // Also fetch DM channels (IMs)
1342
1670
  cursor = undefined;
1343
1671
  do {
1344
1672
  const result = await this.webClient.conversations.list({
@@ -1347,15 +1675,12 @@ export class SlackMessagingBot {
1347
1675
  cursor,
1348
1676
  });
1349
1677
  const ims = result.channels;
1350
- if (ims) {
1351
- for (const im of ims) {
1352
- if (im.id) {
1353
- // Use user's name as channel name for DMs
1354
- const user = im.user ? this.users.get(im.user) : undefined;
1355
- const name = user ? `DM:${user.userName}` : `DM:${im.id}`;
1356
- this.channels.set(im.id, { id: im.id, name });
1357
- }
1358
- }
1678
+ for (const im of ims ?? []) {
1679
+ if (!im.id)
1680
+ continue;
1681
+ const user = im.user ? this.users.get(im.user) : undefined;
1682
+ const name = user ? `DM:${user.userName}` : `DM:${im.id}`;
1683
+ this.channels.set(im.id, { id: im.id, name });
1359
1684
  }
1360
1685
  cursor = result.response_metadata?.next_cursor;
1361
1686
  } while (cursor);