crewly 1.13.1 → 1.14.1

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 (426) hide show
  1. package/config/constants.ts +8 -0
  2. package/config/roles/orchestrator/prompt.md +23 -0
  3. package/config/skills/agent/core/reply-channel/SKILL.md +6 -0
  4. package/config/skills/agent/core/reply-channel/execute.sh +14 -2
  5. package/config/skills/agent/core/slack-post/SKILL.md +126 -0
  6. package/config/skills/agent/core/slack-post/execute.sh +127 -0
  7. package/config/skills/agent/remote-browser/SKILL.md +178 -0
  8. package/config/skills/agent/xiaoyuzhoufm-transcript/SKILL.md +46 -22
  9. package/config/skills/agent/xiaoyuzhoufm-transcript/execute.sh +82 -214
  10. package/config/skills/agent/xiaoyuzhoufm-transcript/skill.json +3 -3
  11. package/config/skills/orchestrator/get-team-status/SKILL.md +5 -1
  12. package/config/skills/orchestrator/get-team-status/execute.sh +28 -2
  13. package/config/slack-app-manifest.json +7 -1
  14. package/dist/backend/backend/src/config/oauth.config.d.ts +93 -19
  15. package/dist/backend/backend/src/config/oauth.config.d.ts.map +1 -1
  16. package/dist/backend/backend/src/config/oauth.config.js +121 -19
  17. package/dist/backend/backend/src/config/oauth.config.js.map +1 -1
  18. package/dist/backend/backend/src/constants.d.ts +60 -0
  19. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  20. package/dist/backend/backend/src/constants.js +74 -0
  21. package/dist/backend/backend/src/constants.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/chat/chat.controller.js +33 -4
  24. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  25. package/dist/backend/backend/src/controllers/credentials/google-oauth.controller.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/credentials/google-oauth.controller.js +27 -9
  27. package/dist/backend/backend/src/controllers/credentials/google-oauth.controller.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  29. package/dist/backend/backend/src/controllers/slack/slack.controller.js +342 -0
  30. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  31. package/dist/backend/backend/src/index.d.ts.map +1 -1
  32. package/dist/backend/backend/src/index.js +31 -7
  33. package/dist/backend/backend/src/index.js.map +1 -1
  34. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +10 -0
  35. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  36. package/dist/backend/backend/src/services/agent/idle-detection.service.js +37 -0
  37. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  38. package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts.map +1 -1
  39. package/dist/backend/backend/src/services/browser/browser-bridge.service.js +13 -11
  40. package/dist/backend/backend/src/services/browser/browser-bridge.service.js.map +1 -1
  41. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts +14 -0
  42. package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts.map +1 -1
  43. package/dist/backend/backend/src/services/browser/browser-proxy.service.js +56 -0
  44. package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
  45. package/dist/backend/backend/src/services/browser/browser-services.registry.d.ts +63 -0
  46. package/dist/backend/backend/src/services/browser/browser-services.registry.d.ts.map +1 -0
  47. package/dist/backend/backend/src/services/browser/browser-services.registry.js +74 -0
  48. package/dist/backend/backend/src/services/browser/browser-services.registry.js.map +1 -0
  49. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +25 -1
  50. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  51. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +19 -7
  52. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  53. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +66 -0
  54. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  55. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +121 -1
  56. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  57. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +35 -0
  58. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +63 -0
  60. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  61. package/dist/backend/backend/src/services/core/storage.service.d.ts +6 -0
  62. package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/core/storage.service.js +1 -1
  64. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  65. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -1
  66. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +6 -2
  67. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -1
  68. package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +14 -0
  70. package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -1
  71. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts +8 -0
  72. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js +31 -3
  74. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js.map +1 -1
  75. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +34 -0
  76. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +99 -10
  78. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  79. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.d.ts +209 -0
  80. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -0
  81. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js +416 -0
  82. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js.map +1 -0
  83. package/dist/backend/backend/src/services/slack/slack-agent-post.service.d.ts +124 -0
  84. package/dist/backend/backend/src/services/slack/slack-agent-post.service.d.ts.map +1 -0
  85. package/dist/backend/backend/src/services/slack/slack-agent-post.service.js +243 -0
  86. package/dist/backend/backend/src/services/slack/slack-agent-post.service.js.map +1 -0
  87. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts +11 -0
  88. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  89. package/dist/backend/backend/src/services/slack/slack-initializer.js +56 -0
  90. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  91. package/dist/backend/backend/src/services/slack/slack-mention-resolver.d.ts +103 -0
  92. package/dist/backend/backend/src/services/slack/slack-mention-resolver.d.ts.map +1 -0
  93. package/dist/backend/backend/src/services/slack/slack-mention-resolver.js +197 -0
  94. package/dist/backend/backend/src/services/slack/slack-mention-resolver.js.map +1 -0
  95. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  96. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +18 -0
  97. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  98. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +329 -0
  99. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -0
  100. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +821 -0
  101. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -0
  102. package/dist/backend/backend/src/services/slack/slack.service.d.ts +116 -1
  103. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  104. package/dist/backend/backend/src/services/slack/slack.service.js +297 -3
  105. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  106. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts +22 -0
  107. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
  108. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +71 -0
  109. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
  110. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts +20 -2
  111. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js +27 -2
  113. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js.map +1 -1
  114. package/dist/backend/backend/src/services/wiki/wiki-reflect-trigger.service.d.ts +36 -2
  115. package/dist/backend/backend/src/services/wiki/wiki-reflect-trigger.service.d.ts.map +1 -1
  116. package/dist/backend/backend/src/services/wiki/wiki-reflect-trigger.service.js +86 -10
  117. package/dist/backend/backend/src/services/wiki/wiki-reflect-trigger.service.js.map +1 -1
  118. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts +37 -0
  119. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts.map +1 -1
  120. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js +74 -8
  121. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js.map +1 -1
  122. package/dist/backend/backend/src/types/slack.types.d.ts +87 -0
  123. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  124. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  125. package/dist/backend/build-info.json +2 -2
  126. package/dist/backend/config/constants.d.ts +8 -0
  127. package/dist/backend/config/constants.d.ts.map +1 -1
  128. package/dist/backend/config/constants.js +8 -0
  129. package/dist/backend/config/constants.js.map +1 -1
  130. package/dist/cli/backend/src/config/oauth.config.d.ts +93 -19
  131. package/dist/cli/backend/src/config/oauth.config.d.ts.map +1 -1
  132. package/dist/cli/backend/src/config/oauth.config.js +121 -19
  133. package/dist/cli/backend/src/config/oauth.config.js.map +1 -1
  134. package/dist/cli/backend/src/constants.d.ts +60 -0
  135. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  136. package/dist/cli/backend/src/constants.js +74 -0
  137. package/dist/cli/backend/src/constants.js.map +1 -1
  138. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +292 -0
  139. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -0
  140. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +420 -0
  141. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -0
  142. package/dist/cli/backend/src/services/chat-v2/chat-v2.mention-resolver.d.ts +134 -0
  143. package/dist/cli/backend/src/services/chat-v2/chat-v2.mention-resolver.d.ts.map +1 -0
  144. package/dist/cli/backend/src/services/chat-v2/chat-v2.mention-resolver.js +232 -0
  145. package/dist/cli/backend/src/services/chat-v2/chat-v2.mention-resolver.js.map +1 -0
  146. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +66 -0
  147. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  148. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +121 -1
  149. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  150. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts +35 -0
  151. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  152. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js +63 -0
  153. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  154. package/dist/cli/backend/src/services/core/storage.service.d.ts +6 -0
  155. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
  156. package/dist/cli/backend/src/services/core/storage.service.js +1 -1
  157. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  158. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -1
  159. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +6 -2
  160. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -1
  161. package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.d.ts +8 -0
  162. package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.d.ts.map +1 -1
  163. package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.js +31 -3
  164. package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.js.map +1 -1
  165. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.d.ts +209 -0
  166. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -0
  167. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js +416 -0
  168. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js.map +1 -0
  169. package/dist/cli/backend/src/services/slack/slack-mention-resolver.d.ts +103 -0
  170. package/dist/cli/backend/src/services/slack/slack-mention-resolver.d.ts.map +1 -0
  171. package/dist/cli/backend/src/services/slack/slack-mention-resolver.js +197 -0
  172. package/dist/cli/backend/src/services/slack/slack-mention-resolver.js.map +1 -0
  173. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  174. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +18 -0
  175. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  176. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +329 -0
  177. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -0
  178. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +821 -0
  179. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -0
  180. package/dist/cli/backend/src/services/slack/slack.service.d.ts +116 -1
  181. package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
  182. package/dist/cli/backend/src/services/slack/slack.service.js +297 -3
  183. package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
  184. package/dist/cli/backend/src/types/slack.types.d.ts +87 -0
  185. package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
  186. package/dist/cli/backend/src/types/slack.types.js.map +1 -1
  187. package/dist/cli/backend/src/utils/team.utils.d.ts +40 -0
  188. package/dist/cli/backend/src/utils/team.utils.d.ts.map +1 -0
  189. package/dist/cli/backend/src/utils/team.utils.js +66 -0
  190. package/dist/cli/backend/src/utils/team.utils.js.map +1 -0
  191. package/dist/cli/cli/src/index.js +0 -0
  192. package/dist/cli/config/constants.d.ts +8 -0
  193. package/dist/cli/config/constants.d.ts.map +1 -1
  194. package/dist/cli/config/constants.js +8 -0
  195. package/dist/cli/config/constants.js.map +1 -1
  196. package/frontend/dist/assets/{index-890d3f9d.js → index-190a957f.js} +294 -294
  197. package/frontend/dist/assets/{index-8205ea5e.css → index-88feb412.css} +1 -1
  198. package/frontend/dist/index.html +2 -2
  199. package/package.json +1 -1
  200. package/config/constants.d.ts.map +0 -1
  201. package/config/index.d.ts.map +0 -1
  202. package/dist/backend/backend/src/controllers/knowledge/index.d.ts +0 -7
  203. package/dist/backend/backend/src/controllers/knowledge/index.d.ts.map +0 -1
  204. package/dist/backend/backend/src/controllers/knowledge/index.js +0 -7
  205. package/dist/backend/backend/src/controllers/knowledge/index.js.map +0 -1
  206. package/dist/backend/backend/src/controllers/knowledge/knowledge.controller.d.ts +0 -71
  207. package/dist/backend/backend/src/controllers/knowledge/knowledge.controller.d.ts.map +0 -1
  208. package/dist/backend/backend/src/controllers/knowledge/knowledge.controller.js +0 -237
  209. package/dist/backend/backend/src/controllers/knowledge/knowledge.controller.js.map +0 -1
  210. package/dist/backend/backend/src/controllers/knowledge/knowledge.routes.d.ts +0 -24
  211. package/dist/backend/backend/src/controllers/knowledge/knowledge.routes.d.ts.map +0 -1
  212. package/dist/backend/backend/src/controllers/knowledge/knowledge.routes.js +0 -34
  213. package/dist/backend/backend/src/controllers/knowledge/knowledge.routes.js.map +0 -1
  214. package/dist/backend/backend/src/controllers/scheduler/unified-scheduler.controller.d.ts +0 -91
  215. package/dist/backend/backend/src/controllers/scheduler/unified-scheduler.controller.d.ts.map +0 -1
  216. package/dist/backend/backend/src/controllers/scheduler/unified-scheduler.controller.js +0 -290
  217. package/dist/backend/backend/src/controllers/scheduler/unified-scheduler.controller.js.map +0 -1
  218. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts +0 -169
  219. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts.map +0 -1
  220. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js +0 -1779
  221. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js.map +0 -1
  222. package/dist/backend/backend/src/index.js.orc-bak-20260529 +0 -3130
  223. package/dist/backend/backend/src/routes/modules/unified-scheduler.routes.d.ts +0 -27
  224. package/dist/backend/backend/src/routes/modules/unified-scheduler.routes.d.ts.map +0 -1
  225. package/dist/backend/backend/src/routes/modules/unified-scheduler.routes.js +0 -40
  226. package/dist/backend/backend/src/routes/modules/unified-scheduler.routes.js.map +0 -1
  227. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts +0 -513
  228. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts.map +0 -1
  229. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js +0 -1568
  230. package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js.map +0 -1
  231. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.d.ts +0 -86
  232. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.d.ts.map +0 -1
  233. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.js +0 -147
  234. package/dist/backend/backend/src/services/agent/crewly-agent/agent-worker.js.map +0 -1
  235. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.d.ts +0 -68
  236. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.d.ts.map +0 -1
  237. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.js +0 -131
  238. package/dist/backend/backend/src/services/agent/crewly-agent/api-client.js.map +0 -1
  239. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.d.ts +0 -130
  240. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.d.ts.map +0 -1
  241. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.js +0 -263
  242. package/dist/backend/backend/src/services/agent/crewly-agent/audit-log.service.js.map +0 -1
  243. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.d.ts +0 -74
  244. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.d.ts.map +0 -1
  245. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.js +0 -140
  246. package/dist/backend/backend/src/services/agent/crewly-agent/audit-trail.service.js.map +0 -1
  247. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.d.ts +0 -29
  248. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.d.ts.map +0 -1
  249. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.js +0 -279
  250. package/dist/backend/backend/src/services/agent/crewly-agent/auditor-tools.js.map +0 -1
  251. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.d.ts +0 -340
  252. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.d.ts.map +0 -1
  253. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.js +0 -1176
  254. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-runtime.service.js.map +0 -1
  255. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.d.ts +0 -79
  256. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.d.ts.map +0 -1
  257. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.js +0 -145
  258. package/dist/backend/backend/src/services/agent/crewly-agent/deepseek-sse-transform.js.map +0 -1
  259. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.d.ts +0 -79
  260. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.d.ts.map +0 -1
  261. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.js +0 -218
  262. package/dist/backend/backend/src/services/agent/crewly-agent/env-isolation.service.js.map +0 -1
  263. package/dist/backend/backend/src/services/agent/crewly-agent/index.d.ts +0 -16
  264. package/dist/backend/backend/src/services/agent/crewly-agent/index.d.ts.map +0 -1
  265. package/dist/backend/backend/src/services/agent/crewly-agent/index.js +0 -16
  266. package/dist/backend/backend/src/services/agent/crewly-agent/index.js.map +0 -1
  267. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.d.ts +0 -135
  268. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.d.ts.map +0 -1
  269. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.js +0 -185
  270. package/dist/backend/backend/src/services/agent/crewly-agent/mcp-tool-bridge.js.map +0 -1
  271. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts +0 -141
  272. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts.map +0 -1
  273. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js +0 -310
  274. package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js.map +0 -1
  275. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.d.ts +0 -91
  276. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.d.ts.map +0 -1
  277. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.js +0 -143
  278. package/dist/backend/backend/src/services/agent/crewly-agent/output-filter.service.js.map +0 -1
  279. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.d.ts +0 -103
  280. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.d.ts.map +0 -1
  281. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.js +0 -256
  282. package/dist/backend/backend/src/services/agent/crewly-agent/prompt-guard.service.js.map +0 -1
  283. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.d.ts +0 -143
  284. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.d.ts.map +0 -1
  285. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.js +0 -264
  286. package/dist/backend/backend/src/services/agent/crewly-agent/rate-limiter.js.map +0 -1
  287. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.d.ts +0 -13
  288. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.d.ts.map +0 -1
  289. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.js +0 -91
  290. package/dist/backend/backend/src/services/agent/crewly-agent/smoke-test.js.map +0 -1
  291. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.d.ts +0 -135
  292. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.d.ts.map +0 -1
  293. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.js +0 -1937
  294. package/dist/backend/backend/src/services/agent/crewly-agent/tool-registry.js.map +0 -1
  295. package/dist/backend/backend/src/services/autonomous/auto-assign.service.d.ts +0 -429
  296. package/dist/backend/backend/src/services/autonomous/auto-assign.service.d.ts.map +0 -1
  297. package/dist/backend/backend/src/services/autonomous/auto-assign.service.js +0 -852
  298. package/dist/backend/backend/src/services/autonomous/auto-assign.service.js.map +0 -1
  299. package/dist/backend/backend/src/services/knowledge/embedding-provider.d.ts +0 -78
  300. package/dist/backend/backend/src/services/knowledge/embedding-provider.d.ts.map +0 -1
  301. package/dist/backend/backend/src/services/knowledge/embedding-provider.js +0 -179
  302. package/dist/backend/backend/src/services/knowledge/embedding-provider.js.map +0 -1
  303. package/dist/backend/backend/src/services/knowledge/fts5-index.service.d.ts +0 -118
  304. package/dist/backend/backend/src/services/knowledge/fts5-index.service.d.ts.map +0 -1
  305. package/dist/backend/backend/src/services/knowledge/fts5-index.service.js +0 -241
  306. package/dist/backend/backend/src/services/knowledge/fts5-index.service.js.map +0 -1
  307. package/dist/backend/backend/src/services/knowledge/fts5-query-sanitizer.d.ts +0 -102
  308. package/dist/backend/backend/src/services/knowledge/fts5-query-sanitizer.d.ts.map +0 -1
  309. package/dist/backend/backend/src/services/knowledge/fts5-query-sanitizer.js +0 -118
  310. package/dist/backend/backend/src/services/knowledge/fts5-query-sanitizer.js.map +0 -1
  311. package/dist/backend/backend/src/services/knowledge/index.d.ts +0 -8
  312. package/dist/backend/backend/src/services/knowledge/index.d.ts.map +0 -1
  313. package/dist/backend/backend/src/services/knowledge/index.js +0 -8
  314. package/dist/backend/backend/src/services/knowledge/index.js.map +0 -1
  315. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.d.ts +0 -241
  316. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.d.ts.map +0 -1
  317. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js +0 -590
  318. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js.map +0 -1
  319. package/dist/backend/backend/src/services/knowledge/knowledge.service.d.ts +0 -153
  320. package/dist/backend/backend/src/services/knowledge/knowledge.service.d.ts.map +0 -1
  321. package/dist/backend/backend/src/services/knowledge/knowledge.service.js +0 -409
  322. package/dist/backend/backend/src/services/knowledge/knowledge.service.js.map +0 -1
  323. package/dist/backend/backend/src/services/knowledge/vector-store.service.d.ts +0 -331
  324. package/dist/backend/backend/src/services/knowledge/vector-store.service.d.ts.map +0 -1
  325. package/dist/backend/backend/src/services/knowledge/vector-store.service.js +0 -830
  326. package/dist/backend/backend/src/services/knowledge/vector-store.service.js.map +0 -1
  327. package/dist/backend/backend/src/services/knowledge/wiki-compiler.service.d.ts +0 -115
  328. package/dist/backend/backend/src/services/knowledge/wiki-compiler.service.d.ts.map +0 -1
  329. package/dist/backend/backend/src/services/knowledge/wiki-compiler.service.js +0 -215
  330. package/dist/backend/backend/src/services/knowledge/wiki-compiler.service.js.map +0 -1
  331. package/dist/backend/backend/src/services/memory/embedding-provider.d.ts +0 -78
  332. package/dist/backend/backend/src/services/memory/embedding-provider.d.ts.map +0 -1
  333. package/dist/backend/backend/src/services/memory/embedding-provider.js +0 -185
  334. package/dist/backend/backend/src/services/memory/embedding-provider.js.map +0 -1
  335. package/dist/backend/backend/src/services/memory/vector-store.service.d.ts +0 -331
  336. package/dist/backend/backend/src/services/memory/vector-store.service.d.ts.map +0 -1
  337. package/dist/backend/backend/src/services/memory/vector-store.service.js +0 -825
  338. package/dist/backend/backend/src/services/memory/vector-store.service.js.map +0 -1
  339. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts +0 -171
  340. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts.map +0 -1
  341. package/dist/backend/backend/src/services/project/task-tracking.service.js +0 -725
  342. package/dist/backend/backend/src/services/project/task-tracking.service.js.map +0 -1
  343. package/dist/backend/backend/src/services/v3/project-task-watcher.service.d.ts +0 -118
  344. package/dist/backend/backend/src/services/v3/project-task-watcher.service.d.ts.map +0 -1
  345. package/dist/backend/backend/src/services/v3/project-task-watcher.service.js +0 -326
  346. package/dist/backend/backend/src/services/v3/project-task-watcher.service.js.map +0 -1
  347. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts +0 -74
  348. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts.map +0 -1
  349. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js +0 -154
  350. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js.map +0 -1
  351. package/dist/backend/backend/src/services/workflow/unified-scheduler.service.d.ts +0 -234
  352. package/dist/backend/backend/src/services/workflow/unified-scheduler.service.d.ts.map +0 -1
  353. package/dist/backend/backend/src/services/workflow/unified-scheduler.service.js +0 -636
  354. package/dist/backend/backend/src/services/workflow/unified-scheduler.service.js.map +0 -1
  355. package/dist/backend/backend/src/services/workflow/unified-scheduler.types.d.ts +0 -156
  356. package/dist/backend/backend/src/services/workflow/unified-scheduler.types.d.ts.map +0 -1
  357. package/dist/backend/backend/src/services/workflow/unified-scheduler.types.js +0 -81
  358. package/dist/backend/backend/src/services/workflow/unified-scheduler.types.js.map +0 -1
  359. package/dist/backend/backend/src/types/auto-assign.types.d.ts +0 -271
  360. package/dist/backend/backend/src/types/auto-assign.types.d.ts.map +0 -1
  361. package/dist/backend/backend/src/types/auto-assign.types.js +0 -136
  362. package/dist/backend/backend/src/types/auto-assign.types.js.map +0 -1
  363. package/dist/backend/backend/src/types/knowledge.types.d.ts +0 -195
  364. package/dist/backend/backend/src/types/knowledge.types.d.ts.map +0 -1
  365. package/dist/backend/backend/src/types/knowledge.types.js +0 -38
  366. package/dist/backend/backend/src/types/knowledge.types.js.map +0 -1
  367. package/dist/backend/backend/src/utils/esm-require.utils.d.ts +0 -111
  368. package/dist/backend/backend/src/utils/esm-require.utils.d.ts.map +0 -1
  369. package/dist/backend/backend/src/utils/esm-require.utils.js +0 -124
  370. package/dist/backend/backend/src/utils/esm-require.utils.js.map +0 -1
  371. package/dist/cli/backend/src/services/knowledge/embedding-provider.d.ts +0 -78
  372. package/dist/cli/backend/src/services/knowledge/embedding-provider.d.ts.map +0 -1
  373. package/dist/cli/backend/src/services/knowledge/embedding-provider.js +0 -179
  374. package/dist/cli/backend/src/services/knowledge/embedding-provider.js.map +0 -1
  375. package/dist/cli/backend/src/services/knowledge/fts5-index.service.d.ts +0 -118
  376. package/dist/cli/backend/src/services/knowledge/fts5-index.service.d.ts.map +0 -1
  377. package/dist/cli/backend/src/services/knowledge/fts5-index.service.js +0 -241
  378. package/dist/cli/backend/src/services/knowledge/fts5-index.service.js.map +0 -1
  379. package/dist/cli/backend/src/services/knowledge/fts5-query-sanitizer.d.ts +0 -102
  380. package/dist/cli/backend/src/services/knowledge/fts5-query-sanitizer.d.ts.map +0 -1
  381. package/dist/cli/backend/src/services/knowledge/fts5-query-sanitizer.js +0 -118
  382. package/dist/cli/backend/src/services/knowledge/fts5-query-sanitizer.js.map +0 -1
  383. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.d.ts +0 -56
  384. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.d.ts.map +0 -1
  385. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.js +0 -91
  386. package/dist/cli/backend/src/services/knowledge/fts5-search-strategy.js.map +0 -1
  387. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts +0 -241
  388. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts.map +0 -1
  389. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js +0 -590
  390. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js.map +0 -1
  391. package/dist/cli/backend/src/services/knowledge/knowledge.service.d.ts +0 -153
  392. package/dist/cli/backend/src/services/knowledge/knowledge.service.d.ts.map +0 -1
  393. package/dist/cli/backend/src/services/knowledge/knowledge.service.js +0 -409
  394. package/dist/cli/backend/src/services/knowledge/knowledge.service.js.map +0 -1
  395. package/dist/cli/backend/src/services/knowledge/learnings-index.service.d.ts +0 -159
  396. package/dist/cli/backend/src/services/knowledge/learnings-index.service.d.ts.map +0 -1
  397. package/dist/cli/backend/src/services/knowledge/learnings-index.service.js +0 -304
  398. package/dist/cli/backend/src/services/knowledge/learnings-index.service.js.map +0 -1
  399. package/dist/cli/backend/src/services/knowledge/vector-store.service.d.ts +0 -331
  400. package/dist/cli/backend/src/services/knowledge/vector-store.service.d.ts.map +0 -1
  401. package/dist/cli/backend/src/services/knowledge/vector-store.service.js +0 -830
  402. package/dist/cli/backend/src/services/knowledge/vector-store.service.js.map +0 -1
  403. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.d.ts +0 -115
  404. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.d.ts.map +0 -1
  405. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.js +0 -215
  406. package/dist/cli/backend/src/services/knowledge/wiki-compiler.service.js.map +0 -1
  407. package/dist/cli/backend/src/services/memory/embedding-provider.d.ts +0 -78
  408. package/dist/cli/backend/src/services/memory/embedding-provider.d.ts.map +0 -1
  409. package/dist/cli/backend/src/services/memory/embedding-provider.js +0 -179
  410. package/dist/cli/backend/src/services/memory/embedding-provider.js.map +0 -1
  411. package/dist/cli/backend/src/services/memory/vector-store.service.d.ts +0 -331
  412. package/dist/cli/backend/src/services/memory/vector-store.service.d.ts.map +0 -1
  413. package/dist/cli/backend/src/services/memory/vector-store.service.js +0 -814
  414. package/dist/cli/backend/src/services/memory/vector-store.service.js.map +0 -1
  415. package/dist/cli/backend/src/services/project/task-tracking.service.d.ts +0 -171
  416. package/dist/cli/backend/src/services/project/task-tracking.service.d.ts.map +0 -1
  417. package/dist/cli/backend/src/services/project/task-tracking.service.js +0 -725
  418. package/dist/cli/backend/src/services/project/task-tracking.service.js.map +0 -1
  419. package/dist/cli/backend/src/types/auto-assign.types.d.ts +0 -271
  420. package/dist/cli/backend/src/types/auto-assign.types.d.ts.map +0 -1
  421. package/dist/cli/backend/src/types/auto-assign.types.js +0 -136
  422. package/dist/cli/backend/src/types/auto-assign.types.js.map +0 -1
  423. package/dist/cli/backend/src/types/knowledge.types.d.ts +0 -195
  424. package/dist/cli/backend/src/types/knowledge.types.d.ts.map +0 -1
  425. package/dist/cli/backend/src/types/knowledge.types.js +0 -38
  426. package/dist/cli/backend/src/types/knowledge.types.js.map +0 -1
@@ -0,0 +1,821 @@
1
+ /**
2
+ * Slack Team Channel Service
3
+ *
4
+ * Makes a Crewly team a first-class Slack participant: one Slack channel per
5
+ * team, backed by one chat-v2 huddle whose roster is the team's members.
6
+ *
7
+ * Slack #team-alpha ──inbound──▶ chat-v2 huddle ──dispatcher──▶ every member
8
+ * ▲ (@'d must reply)
9
+ * └──outbound, posted as the agent (username/icon)── reply-channel
10
+ *
11
+ * Responsibilities:
12
+ * - Mapping store (`~/.crewly/slack-team-channels.json`): create/link/unlink.
13
+ * - Lifecycle sync via `StorageService.onStorageEvent`: newly created team →
14
+ * channel + huddle + welcome (existing teams are opted in from Settings);
15
+ * member change → roster; archive/delete → archive both.
16
+ * - Inbound routing for mapped channels (called by the orchestrator bridge):
17
+ * persist into the huddle with Slack thread correlation, resolve `@name`
18
+ * mentions, dispatch, answer typos with "did you mean".
19
+ * - Outbound mirror: agent messages recorded in a mapped huddle are posted to
20
+ * the Slack thread under the agent's own name and icon.
21
+ *
22
+ * Nothing here is orchestrator-specific — the orchestrator keeps handling DMs
23
+ * and unmapped channels exactly as before.
24
+ *
25
+ * @module services/slack/slack-team-channel.service
26
+ */
27
+ import * as path from 'path';
28
+ import { promises as fs } from 'fs';
29
+ import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
30
+ import { atomicWriteJson, safeReadJson } from '../../utils/file-io.utils.js';
31
+ import { LoggerService } from '../core/logger.service.js';
32
+ import { SLACK_TEAM_CHANNEL_CONSTANTS } from '../../constants.js';
33
+ import { resolveSlackMentions } from './slack-mention-resolver.js';
34
+ const EMPTY_STORE = {
35
+ version: 1,
36
+ autoCreate: true,
37
+ channelPrefix: '',
38
+ mappings: [],
39
+ };
40
+ /**
41
+ * Turn a team name into a Slack-legal channel name: lower-case, letters of
42
+ * any script, digits, `-` and `_`; whitespace and punctuation collapse to
43
+ * `-`; at most 80 characters.
44
+ *
45
+ * @param teamName - The team display name
46
+ * @param prefix - Optional prefix (e.g. `crew-`), sanitised the same way
47
+ * @returns The channel name without `#`, never empty
48
+ *
49
+ * @example
50
+ * slackChannelNameFor('Growth Team!', 'crew-') // 'crew-growth-team'
51
+ */
52
+ export function slackChannelNameFor(teamName, prefix = '') {
53
+ const clean = (s) => s
54
+ .toLowerCase()
55
+ .replace(/[^\p{L}\p{N}_-]+/gu, '-')
56
+ .replace(/-{2,}/g, '-')
57
+ .replace(/^-+|-+$/g, '');
58
+ const body = clean(teamName) || 'team';
59
+ const p = clean(prefix);
60
+ const joined = p ? `${p}-${body}` : body;
61
+ return joined.slice(0, SLACK_TEAM_CHANNEL_CONSTANTS.MAX_CHANNEL_NAME_LENGTH).replace(/-+$/g, '') || 'team';
62
+ }
63
+ /**
64
+ * The agents that take part in a team channel: every member except the
65
+ * orchestrator role (the orc is not a huddle participant — the point of
66
+ * team channels is talking to the team without it).
67
+ *
68
+ * @param team - The team
69
+ * @returns Members with a session name, orchestrator excluded
70
+ */
71
+ export function teamChannelMembers(team) {
72
+ return (team.members ?? []).filter((m) => !!m.sessionName && m.role !== 'orchestrator');
73
+ }
74
+ /**
75
+ * Pick the Slack identity an agent posts under.
76
+ *
77
+ * `username` is the member's display name. The icon is the member avatar
78
+ * when it is a Slack emoji name (`:rocket:`) or a URL; otherwise a per-role
79
+ * emoji, then the default robot.
80
+ *
81
+ * @param member - The team member, or undefined when the session is unknown
82
+ * @param sessionName - Fallback display name
83
+ * @returns The identity fields for SlackOutgoingMessage
84
+ */
85
+ export function slackIdentityFor(member, sessionName) {
86
+ const username = member?.name?.trim() || sessionName;
87
+ const avatar = member?.avatar?.trim() ?? '';
88
+ if (/^:[a-z0-9_+-]+:$/i.test(avatar)) {
89
+ return { username, iconEmoji: avatar };
90
+ }
91
+ if (/^https?:\/\//i.test(avatar)) {
92
+ return { username, iconUrl: avatar };
93
+ }
94
+ const roleEmoji = member?.role
95
+ ? SLACK_TEAM_CHANNEL_CONSTANTS.ROLE_ICON_EMOJI[member.role]
96
+ : undefined;
97
+ return { username, iconEmoji: roleEmoji ?? SLACK_TEAM_CHANNEL_CONSTANTS.DEFAULT_ICON_EMOJI };
98
+ }
99
+ /**
100
+ * Service — see module docs.
101
+ */
102
+ export class SlackTeamChannelService {
103
+ logger;
104
+ deps;
105
+ storePath;
106
+ store = null;
107
+ loading = null;
108
+ unsubscribeStorage = null;
109
+ unsubscribeIdentity = null;
110
+ onChatMessage = (dto) => {
111
+ void this.mirrorOutbound(dto);
112
+ };
113
+ started = false;
114
+ /** Per-team serialisation so two team-saved events cannot create two channels. */
115
+ inflight = new Map();
116
+ constructor(deps) {
117
+ this.deps = deps;
118
+ this.storePath =
119
+ deps.storePath ?? path.join(getCrewlyHomePath(), SLACK_TEAM_CHANNEL_CONSTANTS.STORE_FILENAME);
120
+ this.logger = LoggerService.getInstance().createComponentLogger('SlackTeamChannels');
121
+ }
122
+ // -------------------------------------------------------------------------
123
+ // Lifecycle
124
+ // -------------------------------------------------------------------------
125
+ /**
126
+ * Subscribe to team lifecycle events and chat-v2 agent messages. Idempotent.
127
+ * Safe to call before Slack is connected — every Slack call is guarded by
128
+ * `isConnected()` at the time it runs.
129
+ */
130
+ async start() {
131
+ if (this.started)
132
+ return;
133
+ await this.load();
134
+ this.unsubscribeStorage = this.deps.storage.onStorageEvent((event) => this.handleStorageEvent(event));
135
+ this.deps.chat.on('chat_message', this.onChatMessage);
136
+ // A freshly installed agent bot gets invited into every channel of a
137
+ // team it belongs to, so it can post there under its own identity.
138
+ this.unsubscribeIdentity =
139
+ this.deps.identities?.onInstalled((record) => {
140
+ void this.inviteInstalledEverywhere(record.agentSession);
141
+ }) ?? null;
142
+ this.started = true;
143
+ this.logger.info('Slack team channels started', {
144
+ mappings: this.store?.mappings.length ?? 0,
145
+ autoCreate: this.store?.autoCreate ?? true,
146
+ });
147
+ }
148
+ /** Undo {@link start}. */
149
+ stop() {
150
+ if (!this.started)
151
+ return;
152
+ this.unsubscribeStorage?.();
153
+ this.unsubscribeStorage = null;
154
+ this.unsubscribeIdentity?.();
155
+ this.unsubscribeIdentity = null;
156
+ this.deps.chat.off('chat_message', this.onChatMessage);
157
+ this.started = false;
158
+ }
159
+ // -------------------------------------------------------------------------
160
+ // Store
161
+ // -------------------------------------------------------------------------
162
+ /** Read the store from disk once; later calls return the cached copy. */
163
+ async load() {
164
+ if (this.store)
165
+ return this.store;
166
+ if (!this.loading) {
167
+ this.loading = safeReadJson(this.storePath, EMPTY_STORE).then((raw) => {
168
+ const store = {
169
+ version: 1,
170
+ autoCreate: raw?.autoCreate !== false,
171
+ channelPrefix: typeof raw?.channelPrefix === 'string' ? raw.channelPrefix : '',
172
+ mappings: Array.isArray(raw?.mappings) ? raw.mappings.filter(isMapping) : [],
173
+ };
174
+ this.store = store;
175
+ return store;
176
+ });
177
+ }
178
+ return this.loading;
179
+ }
180
+ async save() {
181
+ const store = await this.load();
182
+ await fs.mkdir(path.dirname(this.storePath), { recursive: true });
183
+ await atomicWriteJson(this.storePath, store);
184
+ }
185
+ /**
186
+ * Current settings (auto-create + prefix).
187
+ *
188
+ * @returns The settings half of the store
189
+ */
190
+ async getSettings() {
191
+ const s = await this.load();
192
+ return { autoCreate: s.autoCreate, channelPrefix: s.channelPrefix };
193
+ }
194
+ /**
195
+ * Update settings. Only the keys present in `patch` change.
196
+ *
197
+ * @param patch - Partial settings
198
+ * @returns The settings after the update
199
+ */
200
+ async updateSettings(patch) {
201
+ const s = await this.load();
202
+ if (typeof patch.autoCreate === 'boolean')
203
+ s.autoCreate = patch.autoCreate;
204
+ if (typeof patch.channelPrefix === 'string')
205
+ s.channelPrefix = patch.channelPrefix.trim();
206
+ await this.save();
207
+ return this.getSettings();
208
+ }
209
+ /**
210
+ * All mappings, in creation order.
211
+ *
212
+ * @returns Copies of every mapping
213
+ */
214
+ async listMappings() {
215
+ const s = await this.load();
216
+ return s.mappings.map((m) => ({ ...m }));
217
+ }
218
+ /**
219
+ * Mapping for a team, if any. Synchronous read of the cached store — the
220
+ * bridge calls this on every inbound Slack message.
221
+ *
222
+ * @param teamId - Crewly team id
223
+ * @returns The mapping, or null
224
+ */
225
+ findByTeamId(teamId) {
226
+ return this.store?.mappings.find((m) => m.teamId === teamId) ?? null;
227
+ }
228
+ /**
229
+ * Mapping for a Slack channel, if any (sync, cached store).
230
+ *
231
+ * @param slackChannelId - Slack channel id
232
+ * @returns The mapping, or null
233
+ */
234
+ findBySlackChannelId(slackChannelId) {
235
+ return this.store?.mappings.find((m) => m.slackChannelId === slackChannelId) ?? null;
236
+ }
237
+ /**
238
+ * Mapping for a chat-v2 huddle, if any (sync, cached store).
239
+ *
240
+ * @param chatChannelId - chat-v2 channel id
241
+ * @returns The mapping, or null
242
+ */
243
+ findByChatChannelId(chatChannelId) {
244
+ return this.store?.mappings.find((m) => m.chatChannelId === chatChannelId) ?? null;
245
+ }
246
+ /**
247
+ * Every non-archived team with its mapping (or null). One call for the
248
+ * Settings UI.
249
+ *
250
+ * @returns Teams in storage order
251
+ */
252
+ async listTeamsWithMappings() {
253
+ await this.load();
254
+ const teams = await this.deps.storage.getTeams();
255
+ return teams
256
+ .filter((t) => !t.archived)
257
+ .map((t) => ({
258
+ teamId: t.id,
259
+ teamName: t.name,
260
+ memberCount: teamChannelMembers(t).length,
261
+ mapping: this.findByTeamId(t.id),
262
+ }));
263
+ }
264
+ /**
265
+ * Look a team up by id.
266
+ *
267
+ * @param teamId - Crewly team id
268
+ * @returns The team, or null
269
+ */
270
+ async getTeam(teamId) {
271
+ const teams = await this.deps.storage.getTeams();
272
+ return teams.find((t) => t.id === teamId) ?? null;
273
+ }
274
+ // -------------------------------------------------------------------------
275
+ // Create / link / unlink
276
+ // -------------------------------------------------------------------------
277
+ /**
278
+ * Make sure `team` has a Slack channel and a huddle. Creates the Slack
279
+ * channel (or reuses one with the same name) unless `slackChannelId`
280
+ * names an existing channel to link. Idempotent: an existing mapping is
281
+ * returned after a roster sync.
282
+ *
283
+ * @param team - The team
284
+ * @param options - `slackChannelId` to link an existing channel instead of creating
285
+ * @returns The mapping
286
+ * @throws Error when Slack is not connected or the channel cannot be created/found
287
+ */
288
+ async ensureTeamChannel(team, options = {}) {
289
+ return this.serialised(team.id, async () => {
290
+ await this.load();
291
+ const existing = this.findByTeamId(team.id);
292
+ if (existing) {
293
+ await this.syncTeamMembers(team, existing);
294
+ return existing;
295
+ }
296
+ if (!this.deps.slack.isConnected()) {
297
+ throw new Error('Slack is not connected');
298
+ }
299
+ let channel;
300
+ let autoCreated;
301
+ if (options.slackChannelId) {
302
+ const info = await this.deps.slack.getChannelInfo(options.slackChannelId);
303
+ if (!info)
304
+ throw new Error(`Slack channel not found: ${options.slackChannelId}`);
305
+ if (info.isArchived)
306
+ throw new Error(`Slack channel is archived: ${info.name}`);
307
+ await this.deps.slack.joinChannel(info.id).catch((err) => {
308
+ // Private channels cannot be joined; the bot must be invited by a human.
309
+ this.logger.warn('Could not join linked channel — invite the bot manually', {
310
+ channel: info.name,
311
+ error: err instanceof Error ? err.message : String(err),
312
+ });
313
+ });
314
+ channel = info;
315
+ autoCreated = false;
316
+ }
317
+ else {
318
+ const store = await this.load();
319
+ channel = await this.deps.slack.createChannel(slackChannelNameFor(team.name, store.channelPrefix));
320
+ autoCreated = true;
321
+ const purpose = team.description?.trim() || `Crewly team "${team.name}"`;
322
+ await this.deps.slack.setChannelPurpose(channel.id, purpose).catch((err) => {
323
+ this.logger.debug('setPurpose failed (non-critical)', {
324
+ error: err instanceof Error ? err.message : String(err),
325
+ });
326
+ });
327
+ }
328
+ const members = teamChannelMembers(team);
329
+ const huddle = this.deps.chat.createHuddle({
330
+ name: `#${channel.name}`,
331
+ purpose: `Slack team channel for ${team.name}`,
332
+ // A brand-new team may have no members yet; keep the huddle valid
333
+ // with a placeholder that the first roster sync replaces.
334
+ memberSessions: members.length > 0 ? members.map((m) => m.sessionName) : [`team:${team.id}`],
335
+ principal: { userId: 'system', source: 'oss' },
336
+ });
337
+ const mapping = {
338
+ teamId: team.id,
339
+ slackChannelId: channel.id,
340
+ slackChannelName: channel.name,
341
+ chatChannelId: huddle.id,
342
+ createdAt: (this.deps.now?.() ?? new Date()).toISOString(),
343
+ autoCreated,
344
+ };
345
+ const store = await this.load();
346
+ store.mappings.push(mapping);
347
+ await this.save();
348
+ this.logger.info('Team channel ready', {
349
+ teamId: team.id,
350
+ teamName: team.name,
351
+ slackChannel: `#${channel.name}`,
352
+ huddle: huddle.id,
353
+ members: members.length,
354
+ autoCreated,
355
+ });
356
+ await this.postWelcome(mapping, team, members);
357
+ await this.ensureIdentities(team, mapping);
358
+ return mapping;
359
+ });
360
+ }
361
+ /**
362
+ * Remove a team's mapping. Optionally archives the Slack channel; the
363
+ * huddle is always archived (it is Crewly's own object).
364
+ *
365
+ * @param teamId - Crewly team id
366
+ * @param options - `archiveSlackChannel` (default false — leave the humans' channel alone)
367
+ * @returns True when a mapping existed
368
+ */
369
+ async unlinkTeam(teamId, options = {}) {
370
+ return this.serialised(teamId, async () => {
371
+ const store = await this.load();
372
+ const idx = store.mappings.findIndex((m) => m.teamId === teamId);
373
+ if (idx < 0)
374
+ return false;
375
+ const [mapping] = store.mappings.splice(idx, 1);
376
+ await this.save();
377
+ this.deps.chat.archiveChannelForBridge(mapping.chatChannelId);
378
+ if (options.archiveSlackChannel && this.deps.slack.isConnected()) {
379
+ await this.deps.slack.archiveChannel(mapping.slackChannelId).catch((err) => {
380
+ this.logger.warn('Could not archive Slack channel', {
381
+ channel: mapping.slackChannelName,
382
+ error: err instanceof Error ? err.message : String(err),
383
+ });
384
+ });
385
+ }
386
+ this.logger.info('Team channel unlinked', {
387
+ teamId,
388
+ slackChannel: `#${mapping.slackChannelName}`,
389
+ archivedSlack: !!options.archiveSlackChannel,
390
+ });
391
+ return true;
392
+ });
393
+ }
394
+ /**
395
+ * Reconcile the huddle roster with the team's current members.
396
+ *
397
+ * @param team - The team
398
+ * @param mapping - Its mapping (looked up when omitted)
399
+ * @returns The roster diff, or null when the team is unmapped
400
+ */
401
+ async syncTeamMembers(team, mapping) {
402
+ await this.load();
403
+ const m = mapping ?? this.findByTeamId(team.id);
404
+ if (!m)
405
+ return null;
406
+ const sessions = teamChannelMembers(team).map((x) => x.sessionName);
407
+ if (sessions.length === 0)
408
+ return { added: [], removed: [] };
409
+ const diff = this.deps.chat.setHuddleMembers(m.chatChannelId, sessions);
410
+ if (diff.added.length || diff.removed.length) {
411
+ this.logger.info('Team channel roster synced', { teamId: team.id, ...diff });
412
+ }
413
+ await this.ensureIdentities(team, m);
414
+ return diff;
415
+ }
416
+ // -------------------------------------------------------------------------
417
+ // Team lifecycle
418
+ // -------------------------------------------------------------------------
419
+ /**
420
+ * React to a storage event. Public so the bridge/tests can drive it
421
+ * directly; `start()` subscribes it.
422
+ *
423
+ * @param event - The storage event
424
+ */
425
+ async handleStorageEvent(event) {
426
+ try {
427
+ if (event.kind === 'team-deleted') {
428
+ await this.unlinkTeam(event.teamId, { archiveSlackChannel: true });
429
+ return;
430
+ }
431
+ const team = event.team;
432
+ await this.load();
433
+ const mapping = this.findByTeamId(team.id);
434
+ if (team.archived) {
435
+ if (mapping)
436
+ await this.unlinkTeam(team.id, { archiveSlackChannel: true });
437
+ return;
438
+ }
439
+ if (mapping) {
440
+ await this.syncTeamMembers(team, mapping);
441
+ return;
442
+ }
443
+ // Auto-create only for teams that were just created. Every status
444
+ // write (activity monitor, registration) also lands here as a
445
+ // team-saved event; turning each of those into a Slack channel would
446
+ // carpet the workspace with channels for teams that pre-date this
447
+ // feature. Existing teams get a channel from Settings → Team Channels.
448
+ if (!event.created)
449
+ return;
450
+ const settings = await this.getSettings();
451
+ if (!settings.autoCreate || !this.deps.slack.isConnected())
452
+ return;
453
+ await this.ensureTeamChannel(team);
454
+ }
455
+ catch (err) {
456
+ this.logger.warn('Team lifecycle sync failed', {
457
+ kind: event.kind,
458
+ error: err instanceof Error ? err.message : String(err),
459
+ });
460
+ }
461
+ }
462
+ // -------------------------------------------------------------------------
463
+ // Inbound (Slack → huddle)
464
+ // -------------------------------------------------------------------------
465
+ /**
466
+ * Route an inbound Slack message that arrived in a mapped channel.
467
+ *
468
+ * 1. Persist into the huddle: a top-level Slack message becomes a chat-v2
469
+ * root carrying `slackThreadTs = ts`; a Slack thread reply is filed
470
+ * under the root that carries `slackThreadTs = thread_ts` (or becomes
471
+ * a root itself when that root predates the mapping).
472
+ * 2. Resolve `@name` tokens against the team's members → `mentions`.
473
+ * 3. Dispatch through the chat-v2 dispatcher (`huddle-broadcast`) with
474
+ * the thread id and the `reply-channel` hint.
475
+ * 4. Answer unknown `@names` in-thread with suggestions.
476
+ *
477
+ * @param message - The inbound Slack message
478
+ * @returns The routing result, or null when the channel is not mapped
479
+ */
480
+ async routeInbound(message) {
481
+ await this.load();
482
+ const mapping = this.findBySlackChannelId(message.channelId);
483
+ if (!mapping)
484
+ return null;
485
+ const channel = this.deps.chat.getChannelForBridge(mapping.chatChannelId);
486
+ if (!channel || channel.archivedAt) {
487
+ this.logger.warn('Mapped huddle missing or archived — dropping mapping', {
488
+ teamId: mapping.teamId,
489
+ huddle: mapping.chatChannelId,
490
+ });
491
+ await this.unlinkTeam(mapping.teamId);
492
+ return null;
493
+ }
494
+ const team = (await this.deps.storage.getTeams()).find((t) => t.id === mapping.teamId);
495
+ const members = team ? teamChannelMembers(team) : [];
496
+ if (this.deps.identities)
497
+ await this.deps.identities.load();
498
+ const candidates = members.map((m) => ({
499
+ name: m.name,
500
+ sessionName: m.sessionName,
501
+ botUserId: this.deps.identities?.get(m.sessionName)?.botUserId,
502
+ }));
503
+ const resolved = resolveSlackMentions(message.text ?? '', candidates);
504
+ // Thread correlation.
505
+ const slackThreadTs = message.threadTs || message.ts;
506
+ let threadId;
507
+ if (message.threadTs) {
508
+ const root = this.deps.chat.findSlackThreadRoot(mapping.chatChannelId, message.threadTs);
509
+ threadId = root?.id;
510
+ }
511
+ const senderId = message.user?.name || message.userId || 'slack-user';
512
+ const { message: persisted } = this.deps.chat.recordTurn({
513
+ channelId: mapping.chatChannelId,
514
+ senderType: 'user',
515
+ senderId,
516
+ content: message.text ?? '',
517
+ threadId,
518
+ mentions: resolved.mentions,
519
+ metadata: {
520
+ source: 'slack',
521
+ slackChannelId: message.channelId,
522
+ slackThreadTs,
523
+ slackTs: message.ts,
524
+ slackUserId: message.userId,
525
+ },
526
+ });
527
+ await this.deps.slack
528
+ .addReaction(message.channelId, message.ts, SLACK_TEAM_CHANNEL_CONSTANTS.INBOUND_REACTION)
529
+ .catch(() => undefined);
530
+ const dispatcher = this.deps.getDispatcher();
531
+ let dispatch = null;
532
+ if (dispatcher) {
533
+ dispatch = await dispatcher.dispatchMessage(channel, persisted, {
534
+ threadId: threadId ?? persisted.id,
535
+ replyVia: 'reply-channel',
536
+ });
537
+ }
538
+ else {
539
+ this.logger.warn('No chat dispatcher wired — message persisted but not delivered', {
540
+ huddle: mapping.chatChannelId,
541
+ });
542
+ }
543
+ if (resolved.unknown.length > 0) {
544
+ await this.postUnknownMentionHint(message, resolved.unknown, candidates);
545
+ }
546
+ this.logger.info('Slack team message routed', {
547
+ teamId: mapping.teamId,
548
+ slackChannel: `#${mapping.slackChannelName}`,
549
+ mentions: resolved.mentions,
550
+ unknown: resolved.unknown.map((u) => u.token),
551
+ strategy: dispatch?.strategy ?? 'none',
552
+ threaded: !!threadId,
553
+ });
554
+ return { mapping, message: persisted, mentions: resolved.mentions, dispatch };
555
+ }
556
+ // -------------------------------------------------------------------------
557
+ // Outbound (huddle → Slack)
558
+ // -------------------------------------------------------------------------
559
+ /**
560
+ * Post an agent's huddle message to the mapped Slack channel under the
561
+ * agent's identity. Called for every chat-v2 `chat_message`; ignores
562
+ * anything that is not an agent message in a mapped huddle.
563
+ *
564
+ * @param dto - The chat-v2 message
565
+ * @returns True when a Slack post was attempted
566
+ */
567
+ async mirrorOutbound(dto) {
568
+ try {
569
+ if (dto.senderType !== 'agent')
570
+ return false;
571
+ if (dto.metadata?.source === 'slack')
572
+ return false;
573
+ await this.load();
574
+ const mapping = this.findByChatChannelId(dto.channelId);
575
+ if (!mapping)
576
+ return false;
577
+ if (!this.deps.slack.isConnected())
578
+ return false;
579
+ const threadTs = this.resolveOutboundThreadTs(mapping, dto);
580
+ const team = (await this.deps.storage.getTeams()).find((t) => t.id === mapping.teamId);
581
+ const member = team?.members.find((m) => m.sessionName === dto.senderId);
582
+ // A real bot user (Cloud-provisioned identity) beats the cosmetic
583
+ // username/icon override.
584
+ const installed = this.deps.identities?.getInstalled(dto.senderId) ?? null;
585
+ const identity = installed ? { botToken: installed.botToken } : slackIdentityFor(member, dto.senderId);
586
+ await this.deps.slack.sendMessage({
587
+ channelId: mapping.slackChannelId,
588
+ text: dto.content,
589
+ threadTs,
590
+ skipChatV2Mirror: true,
591
+ ...identity,
592
+ });
593
+ return true;
594
+ }
595
+ catch (err) {
596
+ this.logger.warn('Outbound mirror to Slack failed', {
597
+ channelId: dto.channelId,
598
+ sender: dto.senderId,
599
+ error: err instanceof Error ? err.message : String(err),
600
+ });
601
+ return false;
602
+ }
603
+ }
604
+ /**
605
+ * Which Slack thread an agent reply belongs to: its chat-v2 thread root's
606
+ * `slackThreadTs`; failing that, the latest Slack-origin root in the
607
+ * huddle; failing that, the channel top level.
608
+ */
609
+ resolveOutboundThreadTs(mapping, dto) {
610
+ if (dto.threadId) {
611
+ const root = this.deps.chat.getMessageForBridge(dto.threadId);
612
+ const ts = root?.metadata?.slackThreadTs;
613
+ if (typeof ts === 'string' && ts)
614
+ return ts;
615
+ }
616
+ const latest = this.deps.chat.findLatestSlackRoot(mapping.chatChannelId);
617
+ const ts = latest?.metadata?.slackThreadTs;
618
+ return typeof ts === 'string' && ts ? ts : undefined;
619
+ }
620
+ // -------------------------------------------------------------------------
621
+ // Agent identities (real bot users)
622
+ // -------------------------------------------------------------------------
623
+ /**
624
+ * Give every member of a mapped team a real Slack identity: ask Cloud for
625
+ * one where missing, announce the owner's install links in the team's
626
+ * channel (once per agent per channel), and invite installed bots into the
627
+ * channel (once). Silent no-op when identities are unavailable (no Cloud
628
+ * login) or the owner has not stored a config token yet.
629
+ *
630
+ * @param team - The team
631
+ * @param mapping - Its channel mapping
632
+ * @returns Counts, for logging and the REST surface
633
+ */
634
+ async ensureIdentities(team, mapping) {
635
+ const identities = this.deps.identities;
636
+ const result = { provisioned: 0, announced: 0, invited: 0, skipped: null };
637
+ if (!identities || !identities.isAvailable()) {
638
+ result.skipped = 'identities unavailable';
639
+ return result;
640
+ }
641
+ await identities.load();
642
+ const pendingLinks = [];
643
+ for (const member of teamChannelMembers(team)) {
644
+ let record = identities.get(member.sessionName);
645
+ if (!record || record.status !== 'installed') {
646
+ try {
647
+ record = await identities.provision(member.sessionName, member.name, `${member.name} — ${member.role} on ${team.name} (Crewly)`);
648
+ result.provisioned += 1;
649
+ }
650
+ catch (err) {
651
+ const message = err instanceof Error ? err.message : String(err);
652
+ // A missing/invalid config token stops the whole pass — every
653
+ // member would fail the same way. Anything else is per-member.
654
+ if (/config_token|not_configured|not_logged_in/i.test(message) || /config(uration)? token/i.test(message)) {
655
+ this.logger.info('Agent identities skipped', { reason: message });
656
+ result.skipped = message;
657
+ break;
658
+ }
659
+ this.logger.warn('Could not provision Slack identity', { agent: member.sessionName, error: message });
660
+ continue;
661
+ }
662
+ }
663
+ if (record.status === 'pending_install' && record.installUrl && !record.announcedIn.includes(mapping.slackChannelId)) {
664
+ pendingLinks.push({ name: member.name, url: record.installUrl, session: member.sessionName });
665
+ }
666
+ if (record.status === 'installed' && record.botUserId && !record.invitedTo.includes(mapping.slackChannelId)) {
667
+ if (await this.inviteBot(mapping, member.sessionName, record.botUserId))
668
+ result.invited += 1;
669
+ }
670
+ }
671
+ if (pendingLinks.length > 0) {
672
+ const lines = pendingLinks.map((p) => `• *${p.name}* → <${p.url}|安装 ${p.name}>`);
673
+ await this.deps.slack
674
+ .sendMessage({
675
+ channelId: mapping.slackChannelId,
676
+ text: [
677
+ `:id: 给 *${team.name}* 的 ${pendingLinks.length} 位成员创建了 Slack 身份,点一下安装(每个各一次):`,
678
+ ...lines,
679
+ '_安装后它们会以自己的名字出现在成员列表里,可以直接 @。_',
680
+ ].join('\n'),
681
+ skipChatV2Mirror: true,
682
+ })
683
+ .then(async () => {
684
+ for (const p of pendingLinks)
685
+ await identities.markChannel(p.session, { announcedIn: mapping.slackChannelId });
686
+ result.announced = pendingLinks.length;
687
+ })
688
+ .catch((err) => {
689
+ this.logger.warn('Could not announce install links', { error: err instanceof Error ? err.message : String(err) });
690
+ });
691
+ }
692
+ return result;
693
+ }
694
+ /**
695
+ * Invite a newly installed agent bot into every channel of every team it
696
+ * belongs to. Runs from the identity service's `onInstalled` hook.
697
+ *
698
+ * @param agentSession - The agent that just got its bot user
699
+ */
700
+ async inviteInstalledEverywhere(agentSession) {
701
+ const identities = this.deps.identities;
702
+ if (!identities)
703
+ return;
704
+ const record = identities.get(agentSession);
705
+ if (!record?.botUserId)
706
+ return;
707
+ await this.load();
708
+ const teams = await this.deps.storage.getTeams();
709
+ for (const team of teams) {
710
+ if (!teamChannelMembers(team).some((m) => m.sessionName === agentSession))
711
+ continue;
712
+ const mapping = this.findByTeamId(team.id);
713
+ if (!mapping || record.invitedTo.includes(mapping.slackChannelId))
714
+ continue;
715
+ await this.inviteBot(mapping, agentSession, record.botUserId);
716
+ }
717
+ }
718
+ /** Invite one bot user into a mapped channel and remember it. */
719
+ async inviteBot(mapping, agentSession, botUserId) {
720
+ try {
721
+ await this.deps.slack.inviteToChannel(mapping.slackChannelId, [botUserId]);
722
+ }
723
+ catch (err) {
724
+ const message = err instanceof Error ? err.message : String(err);
725
+ if (!/already_in_channel/.test(message)) {
726
+ this.logger.warn('Could not invite agent bot into channel', { agentSession, channel: mapping.slackChannelName, error: message });
727
+ return false;
728
+ }
729
+ }
730
+ await this.deps.identities?.markChannel(agentSession, { invitedTo: mapping.slackChannelId });
731
+ this.logger.info('Agent bot invited into team channel', { agentSession, channel: `#${mapping.slackChannelName}` });
732
+ return true;
733
+ }
734
+ // -------------------------------------------------------------------------
735
+ // Slack-side messages
736
+ // -------------------------------------------------------------------------
737
+ async postWelcome(mapping, team, members) {
738
+ const roster = members.length > 0
739
+ ? members.map((m) => `• *${m.name}* (${m.role}) — \`@${m.name}\``).join('\n')
740
+ : '_(还没有成员,成员加入后会自动同步)_';
741
+ const text = [
742
+ `:tada: 团队 *${team.name}* 的频道已就绪。`,
743
+ '',
744
+ '成员:',
745
+ roster,
746
+ '',
747
+ '直接发言,全队都能看到;`@名字` 可以点名某个 agent 必须回复。回复会以各自的名字出现在 thread 里。',
748
+ ].join('\n');
749
+ await this.deps.slack
750
+ .sendMessage({ channelId: mapping.slackChannelId, text, skipChatV2Mirror: true })
751
+ .catch((err) => {
752
+ this.logger.debug('Welcome message failed (non-critical)', {
753
+ error: err instanceof Error ? err.message : String(err),
754
+ });
755
+ });
756
+ }
757
+ async postUnknownMentionHint(message, unknown, candidates) {
758
+ const lines = unknown.map((u) => u.suggestions.length > 0
759
+ ? `没有叫 \`@${u.token}\` 的成员,你是想找 ${u.suggestions.map((s) => `\`@${s}\``).join(' / ')} 吗?`
760
+ : `没有叫 \`@${u.token}\` 的成员。`);
761
+ const rosterHint = candidates.length > 0
762
+ ? `本频道的成员:${candidates.map((c) => `\`@${c.name}\``).join(' ')}`
763
+ : '本频道目前没有成员。';
764
+ await this.deps.slack
765
+ .sendMessage({
766
+ channelId: message.channelId,
767
+ threadTs: message.threadTs || message.ts,
768
+ text: `${lines.join('\n')}\n${rosterHint}\n_(消息已经发给全队;只有被正确 @ 的成员会被要求必须回复。)_`,
769
+ skipChatV2Mirror: true,
770
+ })
771
+ .catch(() => undefined);
772
+ }
773
+ // -------------------------------------------------------------------------
774
+ // Helpers
775
+ // -------------------------------------------------------------------------
776
+ /** Run `fn` after any in-flight operation for the same team finishes. */
777
+ serialised(teamId, fn) {
778
+ const prev = this.inflight.get(teamId) ?? Promise.resolve();
779
+ const next = prev.catch(() => undefined).then(fn);
780
+ this.inflight.set(teamId, next);
781
+ const cleanup = () => {
782
+ if (this.inflight.get(teamId) === next)
783
+ this.inflight.delete(teamId);
784
+ };
785
+ // `then(cleanup, cleanup)` rather than `finally`: a rejected `next` is
786
+ // returned to the caller, and a second derived promise must not turn
787
+ // that same rejection into an unhandled one.
788
+ next.then(cleanup, cleanup);
789
+ return next;
790
+ }
791
+ }
792
+ /** Type guard for persisted mapping rows. */
793
+ function isMapping(value) {
794
+ const v = value;
795
+ return (!!v &&
796
+ typeof v.teamId === 'string' &&
797
+ typeof v.slackChannelId === 'string' &&
798
+ typeof v.chatChannelId === 'string');
799
+ }
800
+ // ---------------------------------------------------------------------------
801
+ // Singleton
802
+ // ---------------------------------------------------------------------------
803
+ let instance = null;
804
+ /**
805
+ * Install the process-wide instance (composition root).
806
+ *
807
+ * @param service - The configured service
808
+ */
809
+ export function setSlackTeamChannelService(service) {
810
+ instance = service;
811
+ }
812
+ /**
813
+ * The process-wide instance, or null before the composition root wires it
814
+ * (e.g. Slack not configured).
815
+ *
816
+ * @returns The service or null
817
+ */
818
+ export function getSlackTeamChannelService() {
819
+ return instance;
820
+ }
821
+ //# sourceMappingURL=slack-team-channel.service.js.map