crewly 1.20.143 → 1.20.144

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 (154) hide show
  1. package/config/constants.ts +59 -0
  2. package/config/index.ts +2 -0
  3. package/config/skills/orchestrator/delegate-task/execute.sh +11 -0
  4. package/config/skills/orchestrator/delegate-task/execute.test.sh +38 -0
  5. package/config/skills/orchestrator/reply-chat/SKILL.md +11 -1
  6. package/config/skills/orchestrator/reply-chat/execute.sh +13 -1
  7. package/config/skills/orchestrator/reply-slack/SKILL.md +1 -0
  8. package/config/skills/orchestrator/reply-slack/execute.sh +11 -2
  9. package/dist/backend/backend/src/constants.d.ts +43 -0
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +24 -1
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  14. package/dist/backend/backend/src/controllers/chat/chat.controller.js +36 -1
  15. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  16. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  17. package/dist/backend/backend/src/controllers/slack/slack.controller.js +69 -7
  18. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +4 -1
  20. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  21. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +9 -2
  22. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  23. package/dist/backend/backend/src/index.d.ts +8 -0
  24. package/dist/backend/backend/src/index.d.ts.map +1 -1
  25. package/dist/backend/backend/src/index.js +71 -2
  26. package/dist/backend/backend/src/index.js.map +1 -1
  27. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  28. package/dist/backend/backend/src/services/agent/agent-registration.service.js +5 -0
  29. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  30. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
  31. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  32. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
  33. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  34. package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
  35. package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
  36. package/dist/backend/backend/src/services/orc/orc-reply-route.service.js +300 -0
  37. package/dist/backend/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
  38. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +32 -2
  39. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +59 -5
  41. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  42. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +4 -2
  43. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +6 -3
  45. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  46. package/dist/backend/backend/src/services/system/auto-update.service.d.ts +292 -0
  47. package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/system/auto-update.service.js +590 -0
  49. package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -0
  50. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +265 -0
  51. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/system/auto-update.utils.js +397 -0
  53. package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -0
  54. package/dist/backend/backend/src/services/system/version-check.service.d.ts +15 -1
  55. package/dist/backend/backend/src/services/system/version-check.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/system/version-check.service.js +21 -2
  57. package/dist/backend/backend/src/services/system/version-check.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
  59. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +14 -2
  61. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  62. package/dist/backend/backend/src/services/v3/cascade-request-status.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/v3/cascade-request-status.js +16 -1
  64. package/dist/backend/backend/src/services/v3/cascade-request-status.js.map +1 -1
  65. package/dist/backend/backend/src/services/v3/request-completion.d.ts +128 -0
  66. package/dist/backend/backend/src/services/v3/request-completion.d.ts.map +1 -0
  67. package/dist/backend/backend/src/services/v3/request-completion.js +311 -0
  68. package/dist/backend/backend/src/services/v3/request-completion.js.map +1 -0
  69. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.d.ts.map +1 -1
  70. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js +3 -0
  71. package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js.map +1 -1
  72. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js +17 -3
  74. package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js.map +1 -1
  75. package/dist/backend/backend/src/services/v3/v3-data.service.d.ts +8 -0
  76. package/dist/backend/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/v3/v3-data.service.js +11 -1
  78. package/dist/backend/backend/src/services/v3/v3-data.service.js.map +1 -1
  79. package/dist/backend/backend/src/types/settings.types.d.ts +8 -0
  80. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  81. package/dist/backend/backend/src/types/settings.types.js +4 -0
  82. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  83. package/dist/backend/build-info.json +2 -2
  84. package/dist/backend/config/constants.d.ts +56 -0
  85. package/dist/backend/config/constants.d.ts.map +1 -1
  86. package/dist/backend/config/constants.js +57 -0
  87. package/dist/backend/config/constants.js.map +1 -1
  88. package/dist/backend/config/index.d.ts +2 -2
  89. package/dist/backend/config/index.d.ts.map +1 -1
  90. package/dist/backend/config/index.js +2 -2
  91. package/dist/backend/config/index.js.map +1 -1
  92. package/dist/cli/backend/src/constants.d.ts +43 -0
  93. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  94. package/dist/cli/backend/src/constants.js +24 -1
  95. package/dist/cli/backend/src/constants.js.map +1 -1
  96. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  97. package/dist/cli/backend/src/services/agent/agent-registration.service.js +5 -0
  98. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  99. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
  100. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  101. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
  102. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  103. package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
  104. package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
  105. package/dist/cli/backend/src/services/orc/orc-reply-route.service.js +300 -0
  106. package/dist/cli/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
  107. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +265 -0
  108. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
  109. package/dist/cli/backend/src/services/system/auto-update.utils.js +397 -0
  110. package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -0
  111. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
  112. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  113. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +14 -2
  114. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  115. package/dist/cli/backend/src/services/v3/request-completion.d.ts +128 -0
  116. package/dist/cli/backend/src/services/v3/request-completion.d.ts.map +1 -0
  117. package/dist/cli/backend/src/services/v3/request-completion.js +311 -0
  118. package/dist/cli/backend/src/services/v3/request-completion.js.map +1 -0
  119. package/dist/cli/backend/src/services/v3/v3-data.service.d.ts +8 -0
  120. package/dist/cli/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
  121. package/dist/cli/backend/src/services/v3/v3-data.service.js +11 -1
  122. package/dist/cli/backend/src/services/v3/v3-data.service.js.map +1 -1
  123. package/dist/cli/backend/src/types/settings.types.d.ts +8 -0
  124. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  125. package/dist/cli/backend/src/types/settings.types.js +4 -0
  126. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  127. package/dist/cli/cli/src/commands/start.d.ts.map +1 -1
  128. package/dist/cli/cli/src/commands/start.js +14 -3
  129. package/dist/cli/cli/src/commands/start.js.map +1 -1
  130. package/dist/cli/cli/src/commands/update-status.d.ts +85 -0
  131. package/dist/cli/cli/src/commands/update-status.d.ts.map +1 -0
  132. package/dist/cli/cli/src/commands/update-status.js +162 -0
  133. package/dist/cli/cli/src/commands/update-status.js.map +1 -0
  134. package/dist/cli/cli/src/constants.d.ts +1 -1
  135. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  136. package/dist/cli/cli/src/constants.js +1 -1
  137. package/dist/cli/cli/src/constants.js.map +1 -1
  138. package/dist/cli/cli/src/index.js +7 -0
  139. package/dist/cli/cli/src/index.js.map +1 -1
  140. package/dist/cli/cli/src/utils/backend-respawn.d.ts +50 -0
  141. package/dist/cli/cli/src/utils/backend-respawn.d.ts.map +1 -0
  142. package/dist/cli/cli/src/utils/backend-respawn.js +59 -0
  143. package/dist/cli/cli/src/utils/backend-respawn.js.map +1 -0
  144. package/dist/cli/config/constants.d.ts +56 -0
  145. package/dist/cli/config/constants.d.ts.map +1 -1
  146. package/dist/cli/config/constants.js +57 -0
  147. package/dist/cli/config/constants.js.map +1 -1
  148. package/dist/cli/config/index.d.ts +2 -2
  149. package/dist/cli/config/index.d.ts.map +1 -1
  150. package/dist/cli/config/index.js +2 -2
  151. package/dist/cli/config/index.js.map +1 -1
  152. package/frontend/dist/assets/{index-e05af897.js → index-71670b91.js} +22 -22
  153. package/frontend/dist/index.html +1 -1
  154. package/package.json +1 -1
@@ -0,0 +1,300 @@
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
+ import { ORC_REPLY_ROUTE_CONSTANTS } from '../../constants.js';
32
+ /**
33
+ * `[CHAT:<id>…]` / `[GCHAT:<id>…]` at the start of a delivered message. Both
34
+ * delivery paths put it first: the chat-v2 dispatcher (`[CHAT:<channel>]
35
+ * <sender@name>`) and the message queue (`[CHAT:<conversation>:<fp>] …`).
36
+ * System deliveries (`[SYSTEM]`, `[CREWLY-DISPATCH]`, reminders) never do.
37
+ */
38
+ const CHAT_PREFIX = /^\s*\[G?CHAT:([^\]\s]+)[^\]]*\]/;
39
+ /**
40
+ * The message queue appends `:<last 8 chars of the message id>` to the
41
+ * conversation id as a delivery fingerprint.
42
+ */
43
+ const QUEUE_FINGERPRINT_SUFFIX = /^(.+):[A-Za-z0-9-]{8}$/;
44
+ /** `[SLACK:<channel>]` or `[SLACK:<channel>:<threadTs>]`, anywhere. */
45
+ const SLACK_MARKER = /\[SLACK:([^:\]\s]+)(?::([^\]\s]+))?\]/;
46
+ /**
47
+ * Parses the routing header of a message delivered to an agent.
48
+ *
49
+ * @param message - The exact text delivered to the agent
50
+ * @returns The origin, or null for system-originated deliveries
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * parseInboundOrigin('[CHAT:a721f48d] <owner@Orchestrator>\n\nhi');
55
+ * // → { conversationId: 'a721f48d' }
56
+ * ```
57
+ */
58
+ export function parseInboundOrigin(message) {
59
+ const chat = CHAT_PREFIX.exec(message);
60
+ if (!chat)
61
+ return null;
62
+ const raw = chat[1];
63
+ const fp = QUEUE_FINGERPRINT_SUFFIX.exec(raw);
64
+ const origin = { conversationId: fp ? fp[1] : raw };
65
+ const slack = SLACK_MARKER.exec(message);
66
+ if (slack) {
67
+ origin.slackChannelId = slack[1];
68
+ if (slack[2])
69
+ origin.slackThreadTs = slack[2];
70
+ }
71
+ return origin;
72
+ }
73
+ /**
74
+ * Whether a Slack channel id is a direct-message conversation.
75
+ *
76
+ * @param slackChannelId - Slack channel id
77
+ * @returns True for `D…` ids
78
+ */
79
+ export function isSlackDm(slackChannelId) {
80
+ return slackChannelId.startsWith(ORC_REPLY_ROUTE_CONSTANTS.SLACK_DM_PREFIX);
81
+ }
82
+ /**
83
+ * Tracks turn origins and decides reply targets. Process-wide singleton; all
84
+ * state is in memory (a restart starts every agent on a fresh turn anyway).
85
+ */
86
+ export class OrcReplyRouteService {
87
+ static instance = null;
88
+ sessions = new Map();
89
+ /**
90
+ * @returns The shared instance
91
+ */
92
+ static getInstance() {
93
+ if (!OrcReplyRouteService.instance)
94
+ OrcReplyRouteService.instance = new OrcReplyRouteService();
95
+ return OrcReplyRouteService.instance;
96
+ }
97
+ /** Drops the shared instance (tests). */
98
+ static resetInstance() {
99
+ OrcReplyRouteService.instance = null;
100
+ }
101
+ /**
102
+ * Records a message delivered to an agent. User-originated messages (those
103
+ * with a `[CHAT:…]` header) become the session's turn origin; system
104
+ * deliveries leave the origin alone, so a system turn that follows a user
105
+ * turn still answers the user where they asked.
106
+ *
107
+ * @param sessionName - Receiving agent session
108
+ * @param message - Exact delivered text
109
+ * @param now - Clock override for tests
110
+ * @returns The new origin, or null when the message was system-originated
111
+ */
112
+ noteDelivery(sessionName, message, now = Date.now()) {
113
+ const parsed = parseInboundOrigin(message);
114
+ if (!parsed)
115
+ return null;
116
+ const state = this.stateFor(sessionName);
117
+ const origin = { ...parsed, receivedAt: now };
118
+ state.origin = origin;
119
+ remember(state.conversations, origin.conversationId, now);
120
+ if (origin.slackChannelId)
121
+ remember(state.slackChannels, origin.slackChannelId, now);
122
+ return origin;
123
+ }
124
+ /**
125
+ * The session's turn origin, however old.
126
+ *
127
+ * @param sessionName - Agent session
128
+ * @returns The last user-originated origin, or undefined
129
+ */
130
+ getLastOrigin(sessionName) {
131
+ return this.sessions.get(sessionName)?.origin;
132
+ }
133
+ /**
134
+ * The session's turn origin if it is still fresh.
135
+ *
136
+ * @param sessionName - Agent session
137
+ * @param now - Clock override for tests
138
+ * @returns The origin within {@link ORC_REPLY_ROUTE_CONSTANTS.ORIGIN_TTL_MS}
139
+ */
140
+ getFreshOrigin(sessionName, now = Date.now()) {
141
+ const origin = this.getLastOrigin(sessionName);
142
+ if (!origin)
143
+ return undefined;
144
+ return now - origin.receivedAt <= ORC_REPLY_ROUTE_CONSTANTS.ORIGIN_TTL_MS ? origin : undefined;
145
+ }
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, requested, options = {}) {
164
+ const now = options.now ?? Date.now();
165
+ if (!requested) {
166
+ const last = this.getLastOrigin(sessionName);
167
+ return last
168
+ ? { action: 'defaulted-to-origin', conversationId: last.conversationId, origin: last, reason: 'no conversation named — using the conversation the turn came from' }
169
+ : { action: 'no-origin', reason: 'no conversation named and no turn origin known' };
170
+ }
171
+ const origin = this.getFreshOrigin(sessionName, now);
172
+ if (!origin)
173
+ return { action: 'as-requested', conversationId: requested, reason: 'no fresh turn origin' };
174
+ if (requested === origin.conversationId) {
175
+ return { action: 'as-requested', conversationId: requested, origin, reason: 'is the turn origin' };
176
+ }
177
+ if (options.crossPost) {
178
+ return { action: 'as-requested', conversationId: requested, origin, reason: 'explicit cross-post' };
179
+ }
180
+ if (this.receivedRecently(this.sessions.get(sessionName)?.conversations, requested, now)) {
181
+ return { action: 'as-requested', conversationId: requested, origin, reason: 'received a user message there recently' };
182
+ }
183
+ return {
184
+ action: 'rerouted-to-origin',
185
+ conversationId: origin.conversationId,
186
+ origin,
187
+ reason: 'named a conversation the turn did not come from and no user wrote in recently',
188
+ };
189
+ }
190
+ /**
191
+ * Decides whether a Slack post is a misdirected reply. Only DMs are
192
+ * candidates — posting in a channel is how the orchestrator notifies teams
193
+ * and delegates, so channel posts are always sent as asked.
194
+ *
195
+ * @param sessionName - Posting agent session
196
+ * @param slackChannelId - Slack channel the caller named
197
+ * @param options - Cross-post flag, clock, and the Slack DM the origin
198
+ * conversation is bridged to (when the delivered message had no marker)
199
+ * @returns The decision
200
+ */
201
+ resolveSlackReply(sessionName, slackChannelId, options = {}) {
202
+ const now = options.now ?? Date.now();
203
+ if (!isSlackDm(slackChannelId)) {
204
+ return { action: 'as-requested', reason: 'channel post — cross-posting allowed' };
205
+ }
206
+ const origin = this.getFreshOrigin(sessionName, now);
207
+ if (!origin)
208
+ return { action: 'as-requested', reason: 'no fresh turn origin' };
209
+ const originSlack = origin.slackChannelId ?? options.originSlackChannelId;
210
+ if (originSlack === slackChannelId) {
211
+ return { action: 'as-requested', origin, reason: 'is the turn origin' };
212
+ }
213
+ if (options.crossPost)
214
+ return { action: 'as-requested', origin, reason: 'explicit cross-post' };
215
+ if (this.receivedRecently(this.sessions.get(sessionName)?.slackChannels, slackChannelId, now)) {
216
+ return { action: 'as-requested', origin, reason: 'received a user message there recently' };
217
+ }
218
+ return {
219
+ action: 'rerouted-to-origin',
220
+ origin,
221
+ reason: 'posted to a Slack DM the turn did not come from and no user wrote in recently',
222
+ };
223
+ }
224
+ /**
225
+ * Plans where a Slack post by `sessionName` should go.
226
+ *
227
+ * When {@link resolveSlackReply} re-routes, the destination is the origin's
228
+ * own Slack conversation if the delivered message carried a `[SLACK:…]`
229
+ * marker (the master-bot bridge), else the origin chat-v2 conversation —
230
+ * which the Slack DM bridge mirrors into the agent-bot DM the owner wrote
231
+ * in, under the right bot.
232
+ *
233
+ * @param sessionName - Posting agent session
234
+ * @param target - Slack channel/thread the caller named, and its cross-post flag
235
+ * @param findDmSlackChannel - Maps a chat-v2 conversation to the Slack DM it
236
+ * is bridged to, when it is
237
+ * @param now - Clock override for tests
238
+ * @returns The plan and the decision behind it
239
+ */
240
+ planSlackReply(sessionName, target, findDmSlackChannel, now = Date.now()) {
241
+ const fresh = this.getFreshOrigin(sessionName, now);
242
+ const originSlackChannelId = fresh && !fresh.slackChannelId ? findDmSlackChannel(fresh.conversationId) : undefined;
243
+ const decision = this.resolveSlackReply(sessionName, target.channelId, {
244
+ crossPost: target.crossPost,
245
+ originSlackChannelId,
246
+ now,
247
+ });
248
+ if (decision.action !== 'rerouted-to-origin' || !decision.origin) {
249
+ return { plan: { kind: 'slack', channelId: target.channelId, threadTs: target.threadTs }, decision };
250
+ }
251
+ const origin = decision.origin;
252
+ const plan = origin.slackChannelId
253
+ ? { kind: 'slack', channelId: origin.slackChannelId, threadTs: origin.slackThreadTs }
254
+ : { kind: 'conversation', conversationId: origin.conversationId };
255
+ return { plan, decision };
256
+ }
257
+ /**
258
+ * Whether an inbound map saw `key` within the recent-inbound window.
259
+ *
260
+ * @param seen - Inbound map
261
+ * @param key - Conversation or Slack channel id
262
+ * @param now - Epoch ms
263
+ * @returns True when recent
264
+ */
265
+ receivedRecently(seen, key, now) {
266
+ const at = seen?.get(key);
267
+ return at !== undefined && now - at <= ORC_REPLY_ROUTE_CONSTANTS.RECENT_INBOUND_MS;
268
+ }
269
+ /**
270
+ * @param sessionName - Agent session
271
+ * @returns Its state, created on first use
272
+ */
273
+ stateFor(sessionName) {
274
+ let state = this.sessions.get(sessionName);
275
+ if (!state) {
276
+ state = { conversations: new Map(), slackChannels: new Map() };
277
+ this.sessions.set(sessionName, state);
278
+ }
279
+ return state;
280
+ }
281
+ }
282
+ /**
283
+ * Records `key` as seen at `at`, keeping the map bounded (oldest first out;
284
+ * Map preserves insertion order, and re-inserting moves a key to the end).
285
+ *
286
+ * @param map - Inbound map
287
+ * @param key - Conversation or Slack channel id
288
+ * @param at - Epoch ms
289
+ */
290
+ function remember(map, key, at) {
291
+ map.delete(key);
292
+ map.set(key, at);
293
+ while (map.size > ORC_REPLY_ROUTE_CONSTANTS.MAX_TRACKED_CONVERSATIONS) {
294
+ const oldest = map.keys().next().value;
295
+ if (oldest === undefined)
296
+ break;
297
+ map.delete(oldest);
298
+ }
299
+ }
300
+ //# sourceMappingURL=orc-reply-route.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orc-reply-route.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/orc/orc-reply-route.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Auto-update helpers — pure / filesystem-only pieces of the self-update
3
+ * (specs/auto-update.md), kept apart from the service so each rule is
4
+ * testable without timers:
5
+ *
6
+ * - where the running copy lives and what kind of install it is (npm global,
7
+ * user-prefix npm, dev checkout, anything else);
8
+ * - the `npm install -g` arguments that target that same prefix;
9
+ * - the on/off decision (setting + env override);
10
+ * - whether a supervisor will bring the backend back after RESTART_REQUESTED;
11
+ * - the status file (`crewly update-status`) and the pending-upgrade marker.
12
+ *
13
+ * @module services/system/auto-update.utils
14
+ */
15
+ /** What kind of install the running copy is. */
16
+ export type InstallKind = 'npm-global' | 'dev-checkout' | 'unmanaged';
17
+ /** Where and how the running copy is installed. */
18
+ export interface InstallInfo {
19
+ /** Install kind */
20
+ kind: InstallKind;
21
+ /** Real path of the Crewly package root (null when not found) */
22
+ packageRoot: string | null;
23
+ /** npm prefix the package lives under (npm-global only) */
24
+ prefix: string | null;
25
+ /** Human-readable reason, used in the skip log line */
26
+ detail: string;
27
+ }
28
+ /** Why auto-update is on or off. */
29
+ export type AutoUpdateSwitch = 'enabled' | 'disabled-setting' | 'disabled-env' | 'enabled-env';
30
+ /** How the last auto-update attempt ended. */
31
+ export type AutoUpdateOutcome = 'up-to-date' | 'deferred-busy' | 'installed-restarting' | 'upgraded' | 'install-failed' | 'verify-failed' | 'restart-unavailable' | 'check-failed' | 'skipped';
32
+ /** Persisted status of the auto-updater (`<crewlyHome>/auto-update-state.json`). */
33
+ export interface AutoUpdateState {
34
+ /** Version this backend is running */
35
+ currentVersion: string | null;
36
+ /** Latest version the registry reported */
37
+ latestVersion: string | null;
38
+ /** ISO time of the last registry check */
39
+ lastCheckAt: string | null;
40
+ /** Whether the updater would act, and why not when it would not */
41
+ mode: string;
42
+ /** How the last attempt ended */
43
+ lastResult: {
44
+ outcome: AutoUpdateOutcome;
45
+ at: string;
46
+ version?: string;
47
+ message?: string;
48
+ } | null;
49
+ /** Failed attempts in a row (reset on success) */
50
+ consecutiveFailures: number;
51
+ /** ISO time before which no new attempt is made */
52
+ backoffUntil: string | null;
53
+ /** Target version the owner was already told about (one failure DM per version) */
54
+ failureNotifiedVersion: string | null;
55
+ }
56
+ /** Marker persisted before an upgrade restart and read on the next boot. */
57
+ export interface PendingUpgradeMarker {
58
+ /** Version the process was running before the restart */
59
+ fromVersion: string;
60
+ /** Version that was installed */
61
+ toVersion: string;
62
+ /** ISO time the marker was written */
63
+ at: string;
64
+ }
65
+ /**
66
+ * Resolve symlinks when the path exists.
67
+ *
68
+ * @param p - Path
69
+ * @returns Real path, or the resolved path when it does not exist
70
+ */
71
+ export declare function realOrResolved(p: string): string;
72
+ /**
73
+ * Walk up from `startDir` to the directory whose package.json is `crewly`.
74
+ *
75
+ * @param startDir - Directory to start from
76
+ * @returns The package root, or null when none is found
77
+ */
78
+ export declare function findCrewlyPackageRoot(startDir: string): string | null;
79
+ /**
80
+ * Package root of the running backend, anchored on the entry script
81
+ * (`process.argv[1]`, e.g. `<root>/dist/backend/backend/src/index.js`) rather
82
+ * than the cwd — after `npm install -g` replaces the package directory the
83
+ * old cwd no longer exists.
84
+ *
85
+ * @param argv1 - Entry script path (defaults to process.argv[1])
86
+ * @param cwd - Fallback start directory
87
+ * @returns Real path of the package root, or null
88
+ */
89
+ export declare function resolveRunningPackageRoot(argv1?: string | undefined, cwd?: string): string | null;
90
+ /**
91
+ * The npm prefix a package root was installed under, when it is a global
92
+ * install: `<prefix>/lib/node_modules/crewly` on POSIX,
93
+ * `<prefix>\node_modules\crewly` on Windows. Covers the default global prefix
94
+ * (nvm, Homebrew, /usr/local) and the user prefix (`<crewlyHome>/npm-global`)
95
+ * alike — installing with `--prefix <that>` replaces exactly the running copy
96
+ * even when a different `npm` is first on PATH.
97
+ *
98
+ * @param packageRoot - Real path of the package root
99
+ * @param platform - Platform (tests)
100
+ * @returns The prefix, or null for anything that is not a global install
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * derivePrefixFromPackageRoot('/usr/local/lib/node_modules/crewly'); // '/usr/local'
105
+ * derivePrefixFromPackageRoot('/home/me/.crewly/npm-global/lib/node_modules/crewly'); // '/home/me/.crewly/npm-global'
106
+ * derivePrefixFromPackageRoot('/home/me/proj/node_modules/crewly'); // null (local dependency)
107
+ * ```
108
+ */
109
+ export declare function derivePrefixFromPackageRoot(packageRoot: string, platform?: NodeJS.Platform): string | null;
110
+ /**
111
+ * Whether a directory is a git working tree root (`.git` directory, or the
112
+ * `.git` file of a worktree).
113
+ *
114
+ * @param dir - Directory
115
+ * @returns True when `.git` exists there
116
+ */
117
+ export declare function isGitWorkingTree(dir: string): boolean;
118
+ /**
119
+ * Classify the running install.
120
+ *
121
+ * - `dev-checkout`: the package root is a git working tree (e.g. the owner's
122
+ * `crewly/` repo running its own build, or an `npm link` that resolves into
123
+ * it). It must never npm-install over itself.
124
+ * - `npm-global`: the root sits in `<prefix>/lib/node_modules/crewly`.
125
+ * - `unmanaged`: anything else (Docker image at /app, a local dependency, an
126
+ * npx cache) — nobody owns an in-place upgrade there.
127
+ *
128
+ * @param packageRoot - Real path of the package root (null when unknown)
129
+ * @param platform - Platform (tests)
130
+ * @returns Install info
131
+ */
132
+ export declare function detectInstall(packageRoot: string | null, platform?: NodeJS.Platform): InstallInfo;
133
+ /**
134
+ * `npm` arguments that install `crewly@<version>` over the running copy.
135
+ *
136
+ * @param prefix - Prefix from {@link derivePrefixFromPackageRoot}
137
+ * @param version - Exact target version
138
+ * @returns Arguments for npm
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * npmInstallArgs('/usr/local', '1.20.144');
143
+ * // ['install', '-g', '--prefix', '/usr/local', 'crewly@1.20.144']
144
+ * ```
145
+ */
146
+ export declare function npmInstallArgs(prefix: string, version: string): string[];
147
+ /**
148
+ * The npm executable that belongs to the running node, when it sits next to
149
+ * it (nvm, official installers); plain `npm` from PATH otherwise.
150
+ *
151
+ * @param execPath - Running node binary (defaults to process.execPath)
152
+ * @param platform - Platform (tests)
153
+ * @returns Command to run
154
+ */
155
+ export declare function resolveNpmCommand(execPath?: string, platform?: NodeJS.Platform): string;
156
+ /**
157
+ * Version in `<packageRoot>/package.json`, read fresh from disk.
158
+ *
159
+ * @param packageRoot - Package root
160
+ * @returns The version, or null when unreadable
161
+ */
162
+ export declare function readInstalledVersion(packageRoot: string): string | null;
163
+ /**
164
+ * Whether `latest` is a strictly newer semver than `current`
165
+ * (numeric major.minor.patch; pre-release tags are ignored).
166
+ *
167
+ * @param latest - Candidate version
168
+ * @param current - Running version
169
+ * @returns True when latest > current
170
+ */
171
+ export declare function isNewerVersion(latest: string, current: string): boolean;
172
+ /**
173
+ * Resolve the on/off switch: the env override wins over the setting.
174
+ *
175
+ * @param settingValue - `settings.general.autoUpdate` (undefined = default on)
176
+ * @param env - Environment
177
+ * @returns The switch state
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * resolveAutoUpdateSwitch(true, { CREWLY_AUTO_UPDATE: '0' }); // 'disabled-env'
182
+ * resolveAutoUpdateSwitch(false, {}); // 'disabled-setting'
183
+ * ```
184
+ */
185
+ export declare function resolveAutoUpdateSwitch(settingValue: boolean | undefined, env?: NodeJS.ProcessEnv): AutoUpdateSwitch;
186
+ /**
187
+ * Whether a switch state means "on".
188
+ *
189
+ * @param state - Switch state
190
+ * @returns True when enabled
191
+ */
192
+ export declare function isSwitchOn(state: AutoUpdateSwitch): boolean;
193
+ /**
194
+ * Read the command line of a process (POSIX only; `/proc` on Linux, `ps`
195
+ * elsewhere).
196
+ *
197
+ * @param pid - Process id
198
+ * @returns The command line, or null
199
+ */
200
+ export declare function readProcessCommandLine(pid: number): string | null;
201
+ /**
202
+ * Whether a supervisor will respawn the backend after it exits with
203
+ * RESTART_REQUESTED. That supervisor is `crewly start` (foreground, under the
204
+ * macOS login-item wrapper, under systemd, or under the desktop app). New
205
+ * CLIs say so in the env; an older CLI parent is recognised by its command line.
206
+ *
207
+ * @param env - Environment
208
+ * @param parentCommandLine - Reader for the parent's command line (tests)
209
+ * @returns True when a respawning parent is present
210
+ */
211
+ export declare function hasRestartSupervisor(env?: NodeJS.ProcessEnv, parentCommandLine?: () => string | null): boolean;
212
+ /**
213
+ * A fresh, empty state.
214
+ *
215
+ * @returns Default state
216
+ */
217
+ export declare function emptyAutoUpdateState(): AutoUpdateState;
218
+ /**
219
+ * Read the status file; missing or malformed → empty state.
220
+ *
221
+ * @param file - Path of the status file
222
+ * @returns The state
223
+ */
224
+ export declare function readAutoUpdateState(file: string): AutoUpdateState;
225
+ /**
226
+ * Write the status file atomically (temp file + rename).
227
+ *
228
+ * @param file - Path of the status file
229
+ * @param state - State to write
230
+ */
231
+ export declare function writeAutoUpdateState(file: string, state: AutoUpdateState): void;
232
+ /**
233
+ * Write the pending-upgrade marker.
234
+ *
235
+ * @param file - Marker path
236
+ * @param marker - Contents
237
+ */
238
+ export declare function writePendingMarker(file: string, marker: PendingUpgradeMarker): void;
239
+ /**
240
+ * Read and delete the pending-upgrade marker. It is removed even when
241
+ * malformed, so a crash loop can never repeat the notice.
242
+ *
243
+ * @param file - Marker path
244
+ * @returns The marker, or null when absent or malformed
245
+ */
246
+ export declare function consumePendingMarker(file: string): PendingUpgradeMarker | null;
247
+ /**
248
+ * Owner notice after a successful auto-upgrade.
249
+ *
250
+ * @param version - New version
251
+ * @param deviceName - This machine's name
252
+ * @returns The one-line notice
253
+ */
254
+ export declare function composeUpgradedNotice(version: string, deviceName: string): string;
255
+ /**
256
+ * Owner notice after repeated failures.
257
+ *
258
+ * @param version - Target version
259
+ * @param deviceName - This machine's name
260
+ * @param failures - Failures in a row
261
+ * @param reason - Last failure reason
262
+ * @returns The notice text
263
+ */
264
+ export declare function composeFailureNotice(version: string, deviceName: string, failures: number, reason: string): string;
265
+ //# sourceMappingURL=auto-update.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-update.utils.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/system/auto-update.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAsBH,gDAAgD;AAChD,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,WAAW,CAAC;AAEtE,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC3B,mBAAmB;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,iEAAiE;IACjE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,kBAAkB,GAAG,cAAc,GAAG,aAAa,CAAC;AAE/F,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAC1B,YAAY,GACZ,eAAe,GACf,sBAAsB,GACtB,UAAU,GACV,gBAAgB,GAChB,eAAe,GACf,qBAAqB,GACrB,cAAc,GACd,SAAS,CAAC;AAEb,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC/B,sCAAsC;IACtC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,UAAU,EAAE;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClG,kDAAkD;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mFAAmF;IACnF,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBrE;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,MAAM,GAAG,SAA2B,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlH;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,MAAM,GAAG,IAAI,CAS5H;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,WAAW,CAYnH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAExE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAyB,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,MAAM,CAI3H;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAUvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAKjI;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,iBAAiB,GAAE,MAAM,MAAM,GAAG,IAAiD,GACjF,OAAO,CAIT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,eAAe,CAWtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAK/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAGnF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAiB9E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElH"}