crewly 1.20.143 → 1.20.145

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 (232) hide show
  1. package/config/constants.ts +59 -0
  2. package/config/index.ts +2 -0
  3. package/config/roles/orchestrator/prompt.md +9 -0
  4. package/config/skills/orchestrator/delegate-task/execute.sh +11 -0
  5. package/config/skills/orchestrator/delegate-task/execute.test.sh +38 -0
  6. package/config/skills/orchestrator/harness-login/SKILL.md +88 -0
  7. package/config/skills/orchestrator/harness-login/execute.sh +75 -0
  8. package/config/skills/orchestrator/harness-login/execute.test.sh +72 -0
  9. package/config/skills/orchestrator/reply-chat/SKILL.md +11 -1
  10. package/config/skills/orchestrator/reply-chat/execute.sh +13 -1
  11. package/config/skills/orchestrator/reply-slack/SKILL.md +1 -0
  12. package/config/skills/orchestrator/reply-slack/execute.sh +11 -2
  13. package/dist/backend/backend/src/constants.d.ts +55 -0
  14. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  15. package/dist/backend/backend/src/constants.js +36 -1
  16. package/dist/backend/backend/src/constants.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/chat/chat.controller.js +36 -1
  19. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts +1 -0
  21. package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/harness/harness.routes.js +3 -0
  23. package/dist/backend/backend/src/controllers/harness/harness.routes.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/harness/owner-login.controller.d.ts +61 -0
  25. package/dist/backend/backend/src/controllers/harness/owner-login.controller.d.ts.map +1 -0
  26. package/dist/backend/backend/src/controllers/harness/owner-login.controller.js +133 -0
  27. package/dist/backend/backend/src/controllers/harness/owner-login.controller.js.map +1 -0
  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 +69 -7
  30. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  31. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +4 -1
  32. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +9 -2
  34. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  35. package/dist/backend/backend/src/index.d.ts +8 -0
  36. package/dist/backend/backend/src/index.d.ts.map +1 -1
  37. package/dist/backend/backend/src/index.js +75 -3
  38. package/dist/backend/backend/src/index.js.map +1 -1
  39. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/agent/agent-registration.service.js +28 -1
  41. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  42. package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts +11 -1
  43. package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/agent/codex-runtime.service.js +18 -0
  45. package/dist/backend/backend/src/services/agent/codex-runtime.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +12 -0
  47. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +18 -3
  49. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  50. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +29 -0
  51. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +48 -0
  53. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  54. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  55. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
  56. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  57. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
  58. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
  60. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  61. package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts +147 -5
  62. package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/harness/harness-relogin.service.js +210 -20
  64. package/dist/backend/backend/src/services/harness/harness-relogin.service.js.map +1 -1
  65. package/dist/backend/backend/src/services/harness/owner-login-request.d.ts +87 -0
  66. package/dist/backend/backend/src/services/harness/owner-login-request.d.ts.map +1 -0
  67. package/dist/backend/backend/src/services/harness/owner-login-request.js +191 -0
  68. package/dist/backend/backend/src/services/harness/owner-login-request.js.map +1 -0
  69. package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
  70. package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
  71. package/dist/backend/backend/src/services/orc/orc-reply-route.service.js +300 -0
  72. package/dist/backend/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
  73. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +32 -2
  74. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  75. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +59 -5
  76. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  77. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +4 -2
  78. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  79. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +6 -3
  80. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  81. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts +68 -9
  82. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js +121 -11
  84. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js.map +1 -1
  85. package/dist/backend/backend/src/services/system/auto-update.service.d.ts +292 -0
  86. package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -0
  87. package/dist/backend/backend/src/services/system/auto-update.service.js +590 -0
  88. package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -0
  89. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +265 -0
  90. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
  91. package/dist/backend/backend/src/services/system/auto-update.utils.js +397 -0
  92. package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -0
  93. package/dist/backend/backend/src/services/system/version-check.service.d.ts +15 -1
  94. package/dist/backend/backend/src/services/system/version-check.service.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/system/version-check.service.js +21 -2
  96. package/dist/backend/backend/src/services/system/version-check.service.js.map +1 -1
  97. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
  98. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  99. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +14 -2
  100. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  101. package/dist/backend/backend/src/services/v3/cascade-request-status.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/v3/cascade-request-status.js +16 -1
  103. package/dist/backend/backend/src/services/v3/cascade-request-status.js.map +1 -1
  104. package/dist/backend/backend/src/services/v3/request-completion.d.ts +128 -0
  105. package/dist/backend/backend/src/services/v3/request-completion.d.ts.map +1 -0
  106. package/dist/backend/backend/src/services/v3/request-completion.js +311 -0
  107. package/dist/backend/backend/src/services/v3/request-completion.js.map +1 -0
  108. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.d.ts.map +1 -1
  109. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js +3 -0
  110. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js.map +1 -1
  111. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js +17 -3
  113. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js.map +1 -1
  114. package/dist/backend/backend/src/services/v3/v3-data.service.d.ts +8 -0
  115. package/dist/backend/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
  116. package/dist/backend/backend/src/services/v3/v3-data.service.js +11 -1
  117. package/dist/backend/backend/src/services/v3/v3-data.service.js.map +1 -1
  118. package/dist/backend/backend/src/types/settings.types.d.ts +8 -0
  119. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  120. package/dist/backend/backend/src/types/settings.types.js +4 -0
  121. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  122. package/dist/backend/backend/src/utils/agent-reply-filter.utils.d.ts +75 -0
  123. package/dist/backend/backend/src/utils/agent-reply-filter.utils.d.ts.map +1 -0
  124. package/dist/backend/backend/src/utils/agent-reply-filter.utils.js +127 -0
  125. package/dist/backend/backend/src/utils/agent-reply-filter.utils.js.map +1 -0
  126. package/dist/backend/build-info.json +2 -2
  127. package/dist/backend/config/constants.d.ts +56 -0
  128. package/dist/backend/config/constants.d.ts.map +1 -1
  129. package/dist/backend/config/constants.js +57 -0
  130. package/dist/backend/config/constants.js.map +1 -1
  131. package/dist/backend/config/index.d.ts +2 -2
  132. package/dist/backend/config/index.d.ts.map +1 -1
  133. package/dist/backend/config/index.js +2 -2
  134. package/dist/backend/config/index.js.map +1 -1
  135. package/dist/cli/backend/src/constants.d.ts +55 -0
  136. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  137. package/dist/cli/backend/src/constants.js +36 -1
  138. package/dist/cli/backend/src/constants.js.map +1 -1
  139. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  140. package/dist/cli/backend/src/services/agent/agent-registration.service.js +28 -1
  141. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  142. package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts +11 -1
  143. package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
  144. package/dist/cli/backend/src/services/agent/codex-runtime.service.js +18 -0
  145. package/dist/cli/backend/src/services/agent/codex-runtime.service.js.map +1 -1
  146. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +12 -0
  147. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  148. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +18 -3
  149. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  150. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts +29 -0
  151. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  152. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js +48 -0
  153. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  154. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  155. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
  156. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  157. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
  158. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  159. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
  160. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  161. package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts +147 -5
  162. package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts.map +1 -1
  163. package/dist/cli/backend/src/services/harness/harness-relogin.service.js +210 -20
  164. package/dist/cli/backend/src/services/harness/harness-relogin.service.js.map +1 -1
  165. package/dist/cli/backend/src/services/harness/owner-login-request.d.ts +87 -0
  166. package/dist/cli/backend/src/services/harness/owner-login-request.d.ts.map +1 -0
  167. package/dist/cli/backend/src/services/harness/owner-login-request.js +191 -0
  168. package/dist/cli/backend/src/services/harness/owner-login-request.js.map +1 -0
  169. package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
  170. package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
  171. package/dist/cli/backend/src/services/orc/orc-reply-route.service.js +300 -0
  172. package/dist/cli/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
  173. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +265 -0
  174. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
  175. package/dist/cli/backend/src/services/system/auto-update.utils.js +397 -0
  176. package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -0
  177. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
  178. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  179. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +14 -2
  180. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  181. package/dist/cli/backend/src/services/v3/request-completion.d.ts +128 -0
  182. package/dist/cli/backend/src/services/v3/request-completion.d.ts.map +1 -0
  183. package/dist/cli/backend/src/services/v3/request-completion.js +311 -0
  184. package/dist/cli/backend/src/services/v3/request-completion.js.map +1 -0
  185. package/dist/cli/backend/src/services/v3/v3-data.service.d.ts +8 -0
  186. package/dist/cli/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
  187. package/dist/cli/backend/src/services/v3/v3-data.service.js +11 -1
  188. package/dist/cli/backend/src/services/v3/v3-data.service.js.map +1 -1
  189. package/dist/cli/backend/src/types/settings.types.d.ts +8 -0
  190. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  191. package/dist/cli/backend/src/types/settings.types.js +4 -0
  192. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  193. package/dist/cli/backend/src/utils/agent-reply-filter.utils.d.ts +75 -0
  194. package/dist/cli/backend/src/utils/agent-reply-filter.utils.d.ts.map +1 -0
  195. package/dist/cli/backend/src/utils/agent-reply-filter.utils.js +127 -0
  196. package/dist/cli/backend/src/utils/agent-reply-filter.utils.js.map +1 -0
  197. package/dist/cli/cli/src/commands/start.d.ts.map +1 -1
  198. package/dist/cli/cli/src/commands/start.js +14 -3
  199. package/dist/cli/cli/src/commands/start.js.map +1 -1
  200. package/dist/cli/cli/src/commands/update-status.d.ts +85 -0
  201. package/dist/cli/cli/src/commands/update-status.d.ts.map +1 -0
  202. package/dist/cli/cli/src/commands/update-status.js +162 -0
  203. package/dist/cli/cli/src/commands/update-status.js.map +1 -0
  204. package/dist/cli/cli/src/constants.d.ts +1 -1
  205. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  206. package/dist/cli/cli/src/constants.js +1 -1
  207. package/dist/cli/cli/src/constants.js.map +1 -1
  208. package/dist/cli/cli/src/index.js +7 -0
  209. package/dist/cli/cli/src/index.js.map +1 -1
  210. package/dist/cli/cli/src/utils/backend-respawn.d.ts +50 -0
  211. package/dist/cli/cli/src/utils/backend-respawn.d.ts.map +1 -0
  212. package/dist/cli/cli/src/utils/backend-respawn.js +59 -0
  213. package/dist/cli/cli/src/utils/backend-respawn.js.map +1 -0
  214. package/dist/cli/config/constants.d.ts +56 -0
  215. package/dist/cli/config/constants.d.ts.map +1 -1
  216. package/dist/cli/config/constants.js +57 -0
  217. package/dist/cli/config/constants.js.map +1 -1
  218. package/dist/cli/config/index.d.ts +2 -2
  219. package/dist/cli/config/index.d.ts.map +1 -1
  220. package/dist/cli/config/index.js +2 -2
  221. package/dist/cli/config/index.js.map +1 -1
  222. package/frontend/dist/assets/{index-e05af897.js → index-71670b91.js} +22 -22
  223. package/frontend/dist/index.html +1 -1
  224. package/package.json +1 -1
  225. package/packages/crewly-agent/src/runtime/agent-runner.service.test.ts +39 -0
  226. package/packages/crewly-agent/src/runtime/agent-runner.service.ts +17 -17
  227. package/packages/crewly-agent/src/runtime/interactive-login-guard.test.ts +60 -0
  228. package/packages/crewly-agent/src/runtime/interactive-login-guard.ts +76 -0
  229. package/packages/crewly-agent/src/runtime/reply-tool-calls.test.ts +53 -0
  230. package/packages/crewly-agent/src/runtime/reply-tool-calls.ts +76 -0
  231. package/packages/crewly-agent/src/runtime/tool-registry.test.ts +8 -0
  232. package/packages/crewly-agent/src/runtime/tool-registry.ts +14 -0
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Owner login requests — recognising "log harness X in" in plain words.
3
+ *
4
+ * Two consumers:
5
+ * - {@link parseOwnerLoginRequest}: the Slack DM interceptor. An owner who
6
+ * writes 「重新登录 claude」, 「换个账号登录 claude」 or "relogin codex" in the
7
+ * orchestrator's DM gets the login flow directly, without the LLM. The
8
+ * match is deliberately strict — the whole message must be the request —
9
+ * so a sentence that merely mentions a login still goes to the orc.
10
+ * - {@link isOwnerLoginRequestEvidence}: the orchestrator's `harness-login`
11
+ * skill. The orc may start a login only when a real owner message asked for
12
+ * one; this is the evidence check over the owner's recent messages.
13
+ *
14
+ * Incident 2026-09-26: the orc ran `claude setup-token` in its one-shot bash
15
+ * tool (the process died with the call, every pasted code went stale) and
16
+ * then claimed several times that it had sent a new link. The broker-backed
17
+ * flow started from here keeps the login alive in its own PTY.
18
+ *
19
+ * @module services/harness/owner-login-request
20
+ */
21
+ import { HARNESS_CONSTANTS } from '../../constants.js';
22
+ /** Harness names and aliases → id, longest first so "claude code" wins over "claude". */
23
+ const HARNESS_NAME_PATTERNS = [
24
+ { re: /^claude[\s_-]*code$/, id: HARNESS_CONSTANTS.IDS.CLAUDE_CODE },
25
+ { re: /^claude$/, id: HARNESS_CONSTANTS.IDS.CLAUDE_CODE },
26
+ { re: /^codex(?:[\s_-]*cli)?$/, id: HARNESS_CONSTANTS.IDS.CODEX_CLI },
27
+ { re: /^antigravity(?:[\s_-]*cli)?$/, id: HARNESS_CONSTANTS.IDS.ANTIGRAVITY_CLI },
28
+ { re: /^agy$/, id: HARNESS_CONSTANTS.IDS.ANTIGRAVITY_CLI },
29
+ { re: /^gemini(?:[\s_-]*cli)?$/, id: HARNESS_CONSTANTS.IDS.GEMINI_CLI },
30
+ ];
31
+ /** Any harness mention inside a longer message (evidence check). */
32
+ const HARNESS_MENTIONS = [
33
+ { re: /claude/i, id: HARNESS_CONSTANTS.IDS.CLAUDE_CODE },
34
+ { re: /codex/i, id: HARNESS_CONSTANTS.IDS.CODEX_CLI },
35
+ { re: /antigravity|\bagy\b/i, id: HARNESS_CONSTANTS.IDS.ANTIGRAVITY_CLI },
36
+ { re: /gemini/i, id: HARNESS_CONSTANTS.IDS.GEMINI_CLI },
37
+ ];
38
+ /**
39
+ * Names that are coding assistants Crewly does not run. "登录 cursor" is
40
+ * answered with the supported list; any other unknown word after a login
41
+ * verb ("登录 gmail") is not a harness request and goes to the orc.
42
+ */
43
+ const OTHER_CODING_TOOLS = /^(?:cursor|copilot|github\s*copilot|aider|opencode|cline|windsurf|kimi(?:\s*cli)?|qwen(?:\s*code)?|amp|goose|kiro|trae)$/;
44
+ /** Generic words for "the coding assistant" — a request for a harness without naming one. */
45
+ const GENERIC_HARNESS_WORDS = /^(?:harness|编程助手|编码助手|代码助手|ai\s*harness|cli)$/;
46
+ /** Chinese login verbs. The account-switch forms come first so they win. */
47
+ const ZH_VERB = '(?:换(?:一)?个(?:新)?账号(?:重新)?登[录陆]|换(?:一)?个号(?:重新)?登[录陆]|换账号(?:重新)?登[录陆]|换号(?:重新)?登[录陆]|用(?:另|别)(?:一)?个账号(?:重新)?登[录陆]|切换账号|换(?:一)?个(?:新)?账号|换账号|换号|重新登[录陆入]|重登[录陆]?|再登[录陆]|登[录陆入])';
48
+ /** English login verbs. */
49
+ const EN_VERB = '(?:re-?log\\s*in(?:\\s+to)?|re-?login(?:\\s+to)?|log\\s*in(?:\\s+to)?|login(?:\\s+to)?|sign\\s*in(?:\\s+to)?|re-?auth(?:enticate)?|switch\\s+(?:the\\s+)?accounts?(?:\\s+(?:for|of|on))?)';
50
+ /** Polite lead-ins that do not change the request. */
51
+ const PREFIX = '(?:(?:please|pls|can\\s+you|could\\s+you)\\s+)?(?:帮我|帮忙|麻烦(?:你)?|请(?:你)?|给我|你)?\\s*(?:(?:把|给|帮)\\s*)?';
52
+ /** Harmless endings. No 吗/了: "登录 claude 了吗" asks about a login, it does not request one. */
53
+ const SUFFIX = '\\s*(?:一下|下|吧)?\\s*[.。!!~~]*';
54
+ /** A name after or before the verb: up to three ASCII words, fewest first ("claude code", not "claude account"). */
55
+ const NAME = '([a-z][a-z0-9_-]*(?:\\s+[a-z][a-z0-9_-]*){0,2}?|编程助手|编码助手|代码助手)';
56
+ /** Optional "account" noun after the name ("claude 的账号", "claude account"). */
57
+ const ACCOUNT_NOUN = '(?:\\s*(?:的)?(?:账号|帐号|account))?';
58
+ /** "登录 claude" / "relogin to codex" / "换个账号登录 claude code". */
59
+ const VERB_FIRST = new RegExp(`^${PREFIX}(${ZH_VERB}|${EN_VERB})\\s*(?:到\\s*)?${NAME}${ACCOUNT_NOUN}${SUFFIX}$`, 'i');
60
+ /** "claude 重新登录" / "codex login" / "把 claude 换个账号登录". */
61
+ const NAME_FIRST = new RegExp(`^${PREFIX}${NAME}${ACCOUNT_NOUN}\\s*(?:重新)?(${ZH_VERB}|${EN_VERB})${SUFFIX}$`, 'i');
62
+ /** "给 claude 换个账号" / "switch claude account" / "switch claude to another account". */
63
+ const SWITCH_NAME = new RegExp(`^${PREFIX}(?:switch|change)\\s+${NAME}\\s+(?:to\\s+(?:another|a\\s+different|a\\s+new)\\s+)?accounts?${SUFFIX}$`, 'i');
64
+ /** A bare verb ("重新登录", "login") — a request with no harness named. */
65
+ const BARE_VERB = new RegExp(`^${PREFIX}(${ZH_VERB}|${EN_VERB})${SUFFIX}$`, 'i');
66
+ /** Words that mark an account switch. */
67
+ const SWITCH_WORDS = /换|切换|switch|another\s+account|different\s+account|other\s+account|另一个账号|别的账号|其他账号/i;
68
+ /**
69
+ * Normalise a DM for matching: drop Slack mention/link markup, collapse
70
+ * whitespace, lower-case, trim.
71
+ *
72
+ * @param text - Raw message text
73
+ * @returns Normalised text
74
+ */
75
+ export function normaliseLoginText(text) {
76
+ return text
77
+ .replace(/<@[A-Z0-9]+>/g, ' ')
78
+ .replace(/<([^|>]+)\|([^>]+)>/g, '$2')
79
+ .replace(/[「」“”"'`]/g, ' ')
80
+ .replace(/\s+/g, ' ')
81
+ .trim()
82
+ .toLowerCase();
83
+ }
84
+ /**
85
+ * Resolve a harness name or alias as written by a person.
86
+ *
87
+ * @param name - e.g. "claude code", "Claude", "agy", "codex cli"
88
+ * @returns The harness id, or null
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * resolveHarnessName('claude code'); // 'claude-code'
93
+ * ```
94
+ */
95
+ export function resolveHarnessName(name) {
96
+ const key = name.trim().toLowerCase().replace(/\s+/g, ' ');
97
+ for (const { re, id } of HARNESS_NAME_PATTERNS) {
98
+ if (re.test(key))
99
+ return id;
100
+ }
101
+ return null;
102
+ }
103
+ /**
104
+ * Turn a matched name into a request, or null when the name is not about a
105
+ * coding assistant at all ("登录 gmail" is for the orc, not for us).
106
+ *
107
+ * @param rawName - Name as matched
108
+ * @param switchAccount - Whether the owner asked for a different account
109
+ * @returns The request or null
110
+ */
111
+ function requestForName(rawName, switchAccount) {
112
+ const name = rawName.trim();
113
+ const harnessId = resolveHarnessName(name);
114
+ if (harnessId)
115
+ return { kind: 'harness', harnessId, switchAccount };
116
+ if (GENERIC_HARNESS_WORDS.test(name))
117
+ return { kind: 'unknown', name: null, switchAccount };
118
+ if (OTHER_CODING_TOOLS.test(name))
119
+ return { kind: 'unknown', name, switchAccount };
120
+ return null;
121
+ }
122
+ /**
123
+ * Recognise an owner's DM that asks Crewly to log a harness in (or switch
124
+ * its account). The whole message must be the request; anything longer or
125
+ * different returns null and is handled by the orchestrator as usual.
126
+ *
127
+ * @param text - Owner DM text
128
+ * @returns The request, or null when the message is not a login request
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * parseOwnerLoginRequest('换个账号登录 claude');
133
+ * // { kind: 'harness', harnessId: 'claude-code', switchAccount: true }
134
+ * parseOwnerLoginRequest('重新登录');
135
+ * // { kind: 'unknown', name: null, switchAccount: false }
136
+ * ```
137
+ */
138
+ export function parseOwnerLoginRequest(text) {
139
+ if (typeof text !== 'string')
140
+ return null;
141
+ const normalised = normaliseLoginText(text);
142
+ if (!normalised || normalised.length > HARNESS_CONSTANTS.OWNER_LOGIN.MAX_TRIGGER_LENGTH)
143
+ return null;
144
+ const switchAccount = SWITCH_WORDS.test(normalised);
145
+ // Each form in turn; a form whose "name" is not a harness ("relogin" read
146
+ // as name "re" + verb "login") does not end the search.
147
+ const verbFirst = VERB_FIRST.exec(normalised);
148
+ const fromVerbFirst = verbFirst ? requestForName(verbFirst[2], switchAccount) : null;
149
+ if (fromVerbFirst)
150
+ return fromVerbFirst;
151
+ const nameFirst = NAME_FIRST.exec(normalised);
152
+ const fromNameFirst = nameFirst ? requestForName(nameFirst[1], switchAccount) : null;
153
+ if (fromNameFirst)
154
+ return fromNameFirst;
155
+ const switchName = SWITCH_NAME.exec(normalised);
156
+ const fromSwitch = switchName ? requestForName(switchName[1], true) : null;
157
+ if (fromSwitch)
158
+ return fromSwitch;
159
+ if (BARE_VERB.test(normalised))
160
+ return { kind: 'unknown', name: null, switchAccount };
161
+ return null;
162
+ }
163
+ /** A login verb anywhere in a message (evidence check; broader than the trigger). */
164
+ const LOGIN_MENTION = /登[录陆入]|重登|换(?:一)?个?账号|切换账号|换号|re-?log\s*in|re-?login|\blog\s*in\b|\blog\s+(?:\S+\s+){1,2}in\b|\blogin\b|sign\s*in|setup-token|\/login|switch\s+(?:the\s+)?accounts?|login\s+link|登[录陆]链接/i;
165
+ /** Status questions — asking whether it is logged in is not asking to log in. */
166
+ const STATUS_QUESTION = /登[录陆]了吗|登[录陆]状态|是否(?:已经)?登[录陆]|有没有登[录陆]|登[录陆]过期了吗|(?:is|are)\s+\S+\s+logged\s+in|login\s+status|still\s+logged\s+in/i;
167
+ /**
168
+ * Whether one owner message asks for a (re-)login of a harness. Mirrors the
169
+ * install-skill approval check: the orchestrator's claim is not trusted, the
170
+ * owner's own words are.
171
+ *
172
+ * Counts a message with a login verb that either names this harness or names
173
+ * none (「不 我要重新登陆一个账号」 in a conversation about Claude). A message
174
+ * naming only other harnesses, or a status question, does not count.
175
+ *
176
+ * @param message - Owner message text
177
+ * @param harnessId - Harness the orchestrator wants to log in
178
+ * @returns True when the message is evidence for that login
179
+ */
180
+ export function isOwnerLoginRequestEvidence(message, harnessId) {
181
+ if (typeof message !== 'string' || !message.trim())
182
+ return false;
183
+ const parsed = parseOwnerLoginRequest(message);
184
+ if (parsed?.kind === 'harness')
185
+ return parsed.harnessId === harnessId;
186
+ if (!LOGIN_MENTION.test(message) || STATUS_QUESTION.test(message))
187
+ return false;
188
+ const named = HARNESS_MENTIONS.filter(({ re }) => re.test(message)).map(({ id }) => id);
189
+ return named.length === 0 || named.includes(harnessId);
190
+ }
191
+ //# sourceMappingURL=owner-login-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-login-request.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/owner-login-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Orchestrator reply routing — "answer where you were asked".
3
+ *
4
+ * Tracks, per agent session, the conversation its latest user-originated
5
+ * message came from (the *turn origin*), and decides where a reply the
6
+ * orchestrator posts during or right after that turn should land.
7
+ *
8
+ * Incident 2026-09-26 03:23–03:34 UTC: the owner was talking to the orc in a
9
+ * Slack DM (D0C381XPD3L → chat-v2 channel a721f48d). A reconciler WorkItem
10
+ * redispatch then started a system turn with no `[CHAT:…]` prefix. In it the
11
+ * orc:
12
+ * - filed `report-status [BLOCKED]` and `reply-chat` without a
13
+ * conversationId; `/api/chat/agent-response` fell back to
14
+ * `ChatService.getCurrentConversation()`, which only sees system-owned
15
+ * channels — never the owner's orchestrator DM — and returned #think-tank
16
+ * (b79a6e4d), so the summary was mirrored into Slack #pro-think-tank;
17
+ * - called `reply-slack` on D0AC7NF5N7L, the owner's DM with the *master*
18
+ * Crewly bot, not the orc-bot DM the conversation was in.
19
+ * The question in that summary ("reply start Ella") never reached the owner.
20
+ *
21
+ * What counts as "the reply": anything the orchestrator posts to a
22
+ * user-facing conversation through `/api/chat/agent-response` (reply-chat,
23
+ * report-status) or to a Slack DM through `/api/slack/send` (reply-slack).
24
+ * It is re-routed to the turn origin when it targets a conversation the orc
25
+ * has not received a user message from recently, unless the caller marked it
26
+ * as a deliberate cross-post. Posts to Slack channels (team notifications,
27
+ * delegation) and `reply-channel` writes are not replies and are untouched.
28
+ *
29
+ * @module services/orc/orc-reply-route.service
30
+ */
31
+ /** Where an agent's current turn came from. */
32
+ export interface TurnOrigin {
33
+ /** chat-v2 channel / conversation id the user message was recorded in. */
34
+ conversationId: string;
35
+ /** Slack channel, when the delivered message carried a `[SLACK:…]` marker. */
36
+ slackChannelId?: string;
37
+ /** Slack thread, when the marker named one. */
38
+ slackThreadTs?: string;
39
+ /** Epoch ms the message was delivered to the agent. */
40
+ receivedAt: number;
41
+ }
42
+ /** Parsed routing header of a delivered message. */
43
+ export type InboundOrigin = Omit<TurnOrigin, 'receivedAt'>;
44
+ /** How {@link OrcReplyRouteService.resolveConversationReply} decided. */
45
+ export type ReplyRouteAction =
46
+ /** Post where the caller asked. */
47
+ 'as-requested'
48
+ /** Caller named no conversation; the turn origin was used. */
49
+ | 'defaulted-to-origin'
50
+ /** Caller named a stale conversation; the turn origin was used instead. */
51
+ | 'rerouted-to-origin'
52
+ /** Caller named nothing and no origin is known; caller keeps its fallback. */
53
+ | 'no-origin';
54
+ /** Result of a reply-target decision. */
55
+ export interface ReplyRouteDecision {
56
+ action: ReplyRouteAction;
57
+ /** Target to use; undefined only for `no-origin`. */
58
+ conversationId?: string;
59
+ /** Why, for logs. */
60
+ reason: string;
61
+ /** The origin consulted, if any. */
62
+ origin?: TurnOrigin;
63
+ }
64
+ /** Result of a Slack reply-target decision. */
65
+ export interface SlackReplyRouteDecision {
66
+ action: 'as-requested' | 'rerouted-to-origin';
67
+ reason: string;
68
+ origin?: TurnOrigin;
69
+ }
70
+ /** Where a Slack post should actually go. */
71
+ export type SlackReplyPlan = {
72
+ kind: 'slack';
73
+ channelId: string;
74
+ threadTs?: string;
75
+ } | {
76
+ kind: 'conversation';
77
+ conversationId: string;
78
+ };
79
+ /** Options shared by the resolve methods. */
80
+ export interface ResolveOptions {
81
+ /** Caller explicitly marked the post as a deliberate cross-post. */
82
+ crossPost?: boolean;
83
+ /** Clock override for tests. */
84
+ now?: number;
85
+ }
86
+ /**
87
+ * Parses the routing header of a message delivered to an agent.
88
+ *
89
+ * @param message - The exact text delivered to the agent
90
+ * @returns The origin, or null for system-originated deliveries
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * parseInboundOrigin('[CHAT:a721f48d] <owner@Orchestrator>\n\nhi');
95
+ * // → { conversationId: 'a721f48d' }
96
+ * ```
97
+ */
98
+ export declare function parseInboundOrigin(message: string): InboundOrigin | null;
99
+ /**
100
+ * Whether a Slack channel id is a direct-message conversation.
101
+ *
102
+ * @param slackChannelId - Slack channel id
103
+ * @returns True for `D…` ids
104
+ */
105
+ export declare function isSlackDm(slackChannelId: string): boolean;
106
+ /**
107
+ * Tracks turn origins and decides reply targets. Process-wide singleton; all
108
+ * state is in memory (a restart starts every agent on a fresh turn anyway).
109
+ */
110
+ export declare class OrcReplyRouteService {
111
+ private static instance;
112
+ private readonly sessions;
113
+ /**
114
+ * @returns The shared instance
115
+ */
116
+ static getInstance(): OrcReplyRouteService;
117
+ /** Drops the shared instance (tests). */
118
+ static resetInstance(): void;
119
+ /**
120
+ * Records a message delivered to an agent. User-originated messages (those
121
+ * with a `[CHAT:…]` header) become the session's turn origin; system
122
+ * deliveries leave the origin alone, so a system turn that follows a user
123
+ * turn still answers the user where they asked.
124
+ *
125
+ * @param sessionName - Receiving agent session
126
+ * @param message - Exact delivered text
127
+ * @param now - Clock override for tests
128
+ * @returns The new origin, or null when the message was system-originated
129
+ */
130
+ noteDelivery(sessionName: string, message: string, now?: number): TurnOrigin | null;
131
+ /**
132
+ * The session's turn origin, however old.
133
+ *
134
+ * @param sessionName - Agent session
135
+ * @returns The last user-originated origin, or undefined
136
+ */
137
+ getLastOrigin(sessionName: string): TurnOrigin | undefined;
138
+ /**
139
+ * The session's turn origin if it is still fresh.
140
+ *
141
+ * @param sessionName - Agent session
142
+ * @param now - Clock override for tests
143
+ * @returns The origin within {@link ORC_REPLY_ROUTE_CONSTANTS.ORIGIN_TTL_MS}
144
+ */
145
+ getFreshOrigin(sessionName: string, now?: number): TurnOrigin | undefined;
146
+ /**
147
+ * Decides which conversation a chat reply (reply-chat, report-status)
148
+ * belongs in.
149
+ *
150
+ * - No conversation named → the turn origin (any age: it is still a better
151
+ * guess than "the newest channel in the system"); none known → caller's
152
+ * own fallback.
153
+ * - A conversation named → kept when it is the origin, when the session
154
+ * received a user message there recently, when the caller marked a
155
+ * cross-post, or when there is no fresh origin to prefer. Otherwise the
156
+ * reply goes to the origin.
157
+ *
158
+ * @param sessionName - Replying agent session
159
+ * @param requested - Conversation the caller named, if any
160
+ * @param options - Cross-post flag and clock
161
+ * @returns The decision
162
+ */
163
+ resolveConversationReply(sessionName: string, requested: string | undefined, options?: ResolveOptions): ReplyRouteDecision;
164
+ /**
165
+ * Decides whether a Slack post is a misdirected reply. Only DMs are
166
+ * candidates — posting in a channel is how the orchestrator notifies teams
167
+ * and delegates, so channel posts are always sent as asked.
168
+ *
169
+ * @param sessionName - Posting agent session
170
+ * @param slackChannelId - Slack channel the caller named
171
+ * @param options - Cross-post flag, clock, and the Slack DM the origin
172
+ * conversation is bridged to (when the delivered message had no marker)
173
+ * @returns The decision
174
+ */
175
+ resolveSlackReply(sessionName: string, slackChannelId: string, options?: ResolveOptions & {
176
+ originSlackChannelId?: string;
177
+ }): SlackReplyRouteDecision;
178
+ /**
179
+ * Plans where a Slack post by `sessionName` should go.
180
+ *
181
+ * When {@link resolveSlackReply} re-routes, the destination is the origin's
182
+ * own Slack conversation if the delivered message carried a `[SLACK:…]`
183
+ * marker (the master-bot bridge), else the origin chat-v2 conversation —
184
+ * which the Slack DM bridge mirrors into the agent-bot DM the owner wrote
185
+ * in, under the right bot.
186
+ *
187
+ * @param sessionName - Posting agent session
188
+ * @param target - Slack channel/thread the caller named, and its cross-post flag
189
+ * @param findDmSlackChannel - Maps a chat-v2 conversation to the Slack DM it
190
+ * is bridged to, when it is
191
+ * @param now - Clock override for tests
192
+ * @returns The plan and the decision behind it
193
+ */
194
+ planSlackReply(sessionName: string, target: {
195
+ channelId: string;
196
+ threadTs?: string;
197
+ crossPost?: boolean;
198
+ }, findDmSlackChannel: (conversationId: string) => string | undefined, now?: number): {
199
+ plan: SlackReplyPlan;
200
+ decision: SlackReplyRouteDecision;
201
+ };
202
+ /**
203
+ * Whether an inbound map saw `key` within the recent-inbound window.
204
+ *
205
+ * @param seen - Inbound map
206
+ * @param key - Conversation or Slack channel id
207
+ * @param now - Epoch ms
208
+ * @returns True when recent
209
+ */
210
+ private receivedRecently;
211
+ /**
212
+ * @param sessionName - Agent session
213
+ * @returns Its state, created on first use
214
+ */
215
+ private stateFor;
216
+ }
217
+ //# sourceMappingURL=orc-reply-route.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orc-reply-route.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/orc/orc-reply-route.service.ts"],"names":[],"mappings":"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"}