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.
- package/config/constants.ts +59 -0
- package/config/index.ts +2 -0
- package/config/skills/orchestrator/delegate-task/execute.sh +11 -0
- package/config/skills/orchestrator/delegate-task/execute.test.sh +38 -0
- package/config/skills/orchestrator/reply-chat/SKILL.md +11 -1
- package/config/skills/orchestrator/reply-chat/execute.sh +13 -1
- package/config/skills/orchestrator/reply-slack/SKILL.md +1 -0
- package/config/skills/orchestrator/reply-slack/execute.sh +11 -2
- package/dist/backend/backend/src/constants.d.ts +43 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +24 -1
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +36 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +69 -7
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +4 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +9 -2
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +8 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +71 -2
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +5 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
- package/dist/backend/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/orc/orc-reply-route.service.js +300 -0
- package/dist/backend/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +32 -2
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +59 -5
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +4 -2
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +6 -3
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/system/auto-update.service.d.ts +292 -0
- package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/system/auto-update.service.js +590 -0
- package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -0
- package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +265 -0
- package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/system/auto-update.utils.js +397 -0
- package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -0
- package/dist/backend/backend/src/services/system/version-check.service.d.ts +15 -1
- package/dist/backend/backend/src/services/system/version-check.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/system/version-check.service.js +21 -2
- package/dist/backend/backend/src/services/system/version-check.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +14 -2
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/cascade-request-status.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/cascade-request-status.js +16 -1
- package/dist/backend/backend/src/services/v3/cascade-request-status.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-completion.d.ts +128 -0
- package/dist/backend/backend/src/services/v3/request-completion.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/request-completion.js +311 -0
- package/dist/backend/backend/src/services/v3/request-completion.js.map +1 -0
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js +3 -0
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js +17 -3
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts +8 -0
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.js +11 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.js.map +1 -1
- package/dist/backend/backend/src/types/settings.types.d.ts +8 -0
- package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/settings.types.js +4 -0
- package/dist/backend/backend/src/types/settings.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/backend/config/constants.d.ts +56 -0
- package/dist/backend/config/constants.d.ts.map +1 -1
- package/dist/backend/config/constants.js +57 -0
- package/dist/backend/config/constants.js.map +1 -1
- package/dist/backend/config/index.d.ts +2 -2
- package/dist/backend/config/index.d.ts.map +1 -1
- package/dist/backend/config/index.js +2 -2
- package/dist/backend/config/index.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +43 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +24 -1
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +5 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +15 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +20 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts +217 -0
- package/dist/cli/backend/src/services/orc/orc-reply-route.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orc/orc-reply-route.service.js +300 -0
- package/dist/cli/backend/src/services/orc/orc-reply-route.service.js.map +1 -0
- package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +265 -0
- package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/system/auto-update.utils.js +397 -0
- package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +7 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +14 -2
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/request-completion.d.ts +128 -0
- package/dist/cli/backend/src/services/v3/request-completion.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/request-completion.js +311 -0
- package/dist/cli/backend/src/services/v3/request-completion.js.map +1 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.d.ts +8 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/v3-data.service.js +11 -1
- package/dist/cli/backend/src/services/v3/v3-data.service.js.map +1 -1
- package/dist/cli/backend/src/types/settings.types.d.ts +8 -0
- package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/settings.types.js +4 -0
- package/dist/cli/backend/src/types/settings.types.js.map +1 -1
- package/dist/cli/cli/src/commands/start.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/start.js +14 -3
- package/dist/cli/cli/src/commands/start.js.map +1 -1
- package/dist/cli/cli/src/commands/update-status.d.ts +85 -0
- package/dist/cli/cli/src/commands/update-status.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/update-status.js +162 -0
- package/dist/cli/cli/src/commands/update-status.js.map +1 -0
- package/dist/cli/cli/src/constants.d.ts +1 -1
- package/dist/cli/cli/src/constants.d.ts.map +1 -1
- package/dist/cli/cli/src/constants.js +1 -1
- package/dist/cli/cli/src/constants.js.map +1 -1
- package/dist/cli/cli/src/index.js +7 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/backend-respawn.d.ts +50 -0
- package/dist/cli/cli/src/utils/backend-respawn.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/backend-respawn.js +59 -0
- package/dist/cli/cli/src/utils/backend-respawn.js.map +1 -0
- package/dist/cli/config/constants.d.ts +56 -0
- package/dist/cli/config/constants.d.ts.map +1 -1
- package/dist/cli/config/constants.js +57 -0
- package/dist/cli/config/constants.js.map +1 -1
- package/dist/cli/config/index.d.ts +2 -2
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +2 -2
- package/dist/cli/config/index.js.map +1 -1
- package/frontend/dist/assets/{index-e05af897.js → index-71670b91.js} +22 -22
- package/frontend/dist/index.html +1 -1
- 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":"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"}
|