crewly 1.20.233 → 1.20.236
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/skills/agent/core/app-comments/SKILL.md +2 -0
- package/dist/backend/backend/src/constants.d.ts +9 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +6 -1
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +10 -0
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +17 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +193 -124
- 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 +30 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.js +5 -9
- package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +127 -0
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/resource-mode.service.js +264 -0
- package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -0
- package/dist/backend/backend/src/services/agent/restore-queue.d.ts +103 -0
- package/dist/backend/backend/src/services/agent/restore-queue.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/restore-queue.js +184 -0
- package/dist/backend/backend/src/services/agent/restore-queue.js.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts +129 -0
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.js +235 -0
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.js.map +1 -0
- package/dist/backend/backend/src/services/apps/app-wake.service.d.ts +8 -1
- package/dist/backend/backend/src/services/apps/app-wake.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/app-wake.service.js +8 -0
- package/dist/backend/backend/src/services/apps/app-wake.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.d.ts +5 -0
- package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.js +8 -2
- package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.d.ts +16 -0
- package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.js +74 -0
- package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +13 -0
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +29 -0
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +52 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +47 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js +3 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +23 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +134 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +16 -2
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/backend/backend/src/types/settings.types.d.ts +4 -0
- package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/settings.types.js +8 -0
- package/dist/backend/backend/src/types/settings.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +9 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +6 -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 +30 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +127 -0
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/resource-mode.service.js +264 -0
- package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/restore-queue.d.ts +103 -0
- package/dist/cli/backend/src/services/agent/restore-queue.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/restore-queue.js +184 -0
- package/dist/cli/backend/src/services/agent/restore-queue.js.map +1 -0
- package/dist/cli/backend/src/services/core/system-health.util.d.ts +89 -0
- package/dist/cli/backend/src/services/core/system-health.util.d.ts.map +1 -0
- package/dist/cli/backend/src/services/core/system-health.util.js +220 -0
- package/dist/cli/backend/src/services/core/system-health.util.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +16 -2
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/cli/backend/src/types/settings.types.d.ts +4 -0
- package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/settings.types.js +8 -0
- package/dist/cli/backend/src/types/settings.types.js.map +1 -1
- package/frontend/dist/assets/{index-bc4e2a93.js → index-a719c15a.js} +54 -54
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staggered boot restore: agents with work in hand come back one at a time
|
|
3
|
+
* (each Claude Code relaunch on a loaded machine takes minutes), the owner's
|
|
4
|
+
* agents first. An on-demand wake for an agent still waiting in the queue
|
|
5
|
+
* moves it up instead of starting a second launch in parallel.
|
|
6
|
+
*
|
|
7
|
+
* @module services/agent/restore-queue
|
|
8
|
+
*/
|
|
9
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
10
|
+
/** Defaults for the restore queue. */
|
|
11
|
+
export const RESTORE_QUEUE_CONSTANTS = {
|
|
12
|
+
DEFAULT_CONCURRENCY: 1,
|
|
13
|
+
MAX_CONCURRENCY: 3,
|
|
14
|
+
/** Per-agent budget before the queue moves on and retries it later */
|
|
15
|
+
DEFAULT_TIMEOUT_MS: 6 * 60 * 1000,
|
|
16
|
+
/** Times an agent that timed out is tried again, at the back of the queue */
|
|
17
|
+
MAX_RETRIES: 1,
|
|
18
|
+
};
|
|
19
|
+
/** Restore priority tiers, lowest first. */
|
|
20
|
+
export const RESTORE_TIER = { OWNER: 1, ORCHESTRATOR: 2, LEAD: 3, OTHER: 4 };
|
|
21
|
+
/**
|
|
22
|
+
* Concurrency from `CREWLY_RESTORE_CONCURRENCY`, clamped to 1..3.
|
|
23
|
+
*
|
|
24
|
+
* @param raw - Env value
|
|
25
|
+
* @returns Concurrency
|
|
26
|
+
*/
|
|
27
|
+
export function restoreConcurrencyFromEnv(raw = process.env.CREWLY_RESTORE_CONCURRENCY) {
|
|
28
|
+
const n = Number.parseInt(raw ?? '', 10);
|
|
29
|
+
if (!Number.isFinite(n) || n < 1)
|
|
30
|
+
return RESTORE_QUEUE_CONSTANTS.DEFAULT_CONCURRENCY;
|
|
31
|
+
return Math.min(n, RESTORE_QUEUE_CONSTANTS.MAX_CONCURRENCY);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Order entries: tier first; inside the owner tier, oldest owner message first.
|
|
35
|
+
*
|
|
36
|
+
* @param entries - Entries to order
|
|
37
|
+
* @returns A new, ordered array
|
|
38
|
+
*/
|
|
39
|
+
export function orderRestoreEntries(entries) {
|
|
40
|
+
return entries
|
|
41
|
+
.map((e, i) => ({ e, i }))
|
|
42
|
+
.sort((a, b) => a.e.tier - b.e.tier
|
|
43
|
+
|| (a.e.tier === RESTORE_TIER.OWNER ? (a.e.ownerSince ?? Infinity) - (b.e.ownerSince ?? Infinity) : 0)
|
|
44
|
+
|| a.i - b.i)
|
|
45
|
+
.map((x) => x.e);
|
|
46
|
+
}
|
|
47
|
+
/** The queue itself. */
|
|
48
|
+
export class RestoreQueue {
|
|
49
|
+
concurrency;
|
|
50
|
+
timeoutMs;
|
|
51
|
+
pending = [];
|
|
52
|
+
active = new Set();
|
|
53
|
+
counters = { started: 0, ready: 0, failed: 0, timedOut: 0 };
|
|
54
|
+
running = false;
|
|
55
|
+
done = Promise.resolve();
|
|
56
|
+
logger;
|
|
57
|
+
constructor(concurrency = restoreConcurrencyFromEnv(), timeoutMs = Number(process.env.CREWLY_RESTORE_TIMEOUT_MS) || RESTORE_QUEUE_CONSTANTS.DEFAULT_TIMEOUT_MS) {
|
|
58
|
+
this.concurrency = concurrency;
|
|
59
|
+
this.timeoutMs = timeoutMs;
|
|
60
|
+
this.logger = LoggerService.getInstance().createComponentLogger('RestoreQueue');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Plan and run the restore. Resolves when every entry is ready, failed or
|
|
64
|
+
* out of retries. Returns the same promise while already running.
|
|
65
|
+
*
|
|
66
|
+
* @param entries - Agents to restore
|
|
67
|
+
* @returns Promise settled when the queue is drained
|
|
68
|
+
*/
|
|
69
|
+
start(entries) {
|
|
70
|
+
if (this.running)
|
|
71
|
+
return this.done;
|
|
72
|
+
this.pending = orderRestoreEntries(entries).map((entry) => ({ entry, attempt: 0, promoted: false }));
|
|
73
|
+
this.counters = { started: 0, ready: 0, failed: 0, timedOut: 0 };
|
|
74
|
+
this.running = true;
|
|
75
|
+
this.logger.info('Restore plan', {
|
|
76
|
+
concurrency: this.concurrency,
|
|
77
|
+
order: this.pending.map((s, i) => `${i + 1}. ${s.entry.name} (${s.entry.reason})`),
|
|
78
|
+
});
|
|
79
|
+
const workers = Array.from({ length: this.concurrency }, () => this.worker());
|
|
80
|
+
this.done = Promise.all(workers).then(() => {
|
|
81
|
+
this.running = false;
|
|
82
|
+
this.logger.info('Restore queue drained', { ...this.counters });
|
|
83
|
+
});
|
|
84
|
+
return this.done;
|
|
85
|
+
}
|
|
86
|
+
/** Whether the agent is still waiting (not yet started) in the queue. */
|
|
87
|
+
isPending(name) {
|
|
88
|
+
return this.pending.some((s) => s.entry.name === name);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* An on-demand wake arrived for an agent. Owner-triggered wakes move it to
|
|
92
|
+
* the front (behind earlier promoted ones); other wakes keep queue order.
|
|
93
|
+
*
|
|
94
|
+
* @param name - Session name
|
|
95
|
+
* @param ownerTriggered - The wake carries an owner message
|
|
96
|
+
* @returns True when the agent is waiting in the queue (the caller must not start it)
|
|
97
|
+
*/
|
|
98
|
+
wake(name, ownerTriggered) {
|
|
99
|
+
const at = this.pending.findIndex((s) => s.entry.name === name);
|
|
100
|
+
if (at < 0)
|
|
101
|
+
return false;
|
|
102
|
+
if (ownerTriggered && !this.pending[at].promoted) {
|
|
103
|
+
const [slot] = this.pending.splice(at, 1);
|
|
104
|
+
slot.promoted = true;
|
|
105
|
+
const after = this.pending.reduce((n, s, i) => (s.promoted ? i + 1 : n), 0);
|
|
106
|
+
this.pending.splice(after, 0, slot);
|
|
107
|
+
this.logger.info('Owner wake moved agent to the front of the restore queue', { name, position: after + 1 });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.logger.info('Wake for an agent waiting in the restore queue; keeping queue order', { name, ownerTriggered });
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/** Progress for /health. */
|
|
115
|
+
stats() {
|
|
116
|
+
return { running: this.running, pending: this.pending.length, ...this.counters, order: this.pending.map((s) => s.entry.name) };
|
|
117
|
+
}
|
|
118
|
+
async worker() {
|
|
119
|
+
for (;;) {
|
|
120
|
+
const slot = this.pending.shift();
|
|
121
|
+
if (!slot)
|
|
122
|
+
return;
|
|
123
|
+
await this.runSlot(slot);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async runSlot({ entry, attempt, promoted }) {
|
|
127
|
+
const { name } = entry;
|
|
128
|
+
this.active.add(name);
|
|
129
|
+
this.counters.started++;
|
|
130
|
+
this.logger.info('Restoring agent', { name, attempt, reason: entry.reason, remaining: this.pending.length });
|
|
131
|
+
let timer;
|
|
132
|
+
const TIMEOUT = Symbol('timeout');
|
|
133
|
+
try {
|
|
134
|
+
const outcome = await Promise.race([
|
|
135
|
+
entry.run(attempt),
|
|
136
|
+
new Promise((resolve) => { timer = setTimeout(() => resolve(TIMEOUT), this.timeoutMs); }),
|
|
137
|
+
]);
|
|
138
|
+
if (outcome === TIMEOUT) {
|
|
139
|
+
this.counters.timedOut++;
|
|
140
|
+
if (attempt < RESTORE_QUEUE_CONSTANTS.MAX_RETRIES) {
|
|
141
|
+
this.logger.warn('Agent did not register in time; moving on and retrying it later', { name, timeoutMs: this.timeoutMs });
|
|
142
|
+
this.pending.push({ entry, attempt: attempt + 1, promoted });
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.counters.failed++;
|
|
146
|
+
this.logger.warn('Agent did not register after retry; giving up', { name });
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (!outcome.success) {
|
|
151
|
+
this.counters.failed++;
|
|
152
|
+
this.logger.warn('Restore failed', { name });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.counters.ready++;
|
|
156
|
+
this.logger.info('Agent ready', { name });
|
|
157
|
+
try {
|
|
158
|
+
await entry.onReady?.();
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
this.logger.warn('Post-restore step failed (non-fatal)', { name, error: err instanceof Error ? err.message : String(err) });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
this.counters.failed++;
|
|
166
|
+
this.logger.error('Restore threw', { name, error: err instanceof Error ? err.message : String(err) });
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
if (timer)
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
this.active.delete(name);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let instance = null;
|
|
176
|
+
/** The process-wide restore queue. */
|
|
177
|
+
export function getRestoreQueue() {
|
|
178
|
+
return (instance ??= new RestoreQueue());
|
|
179
|
+
}
|
|
180
|
+
/** Test hook: drop the singleton. */
|
|
181
|
+
export function resetRestoreQueue() {
|
|
182
|
+
instance = null;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=restore-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore-queue.js","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/restore-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crewly App comments <-> Slack, both ways (owner request 2026-10-05: "a
|
|
3
|
+
* reply on either side is visible on both").
|
|
4
|
+
*
|
|
5
|
+
* - An owner comment (new, or a reply) delivered to an agent is also posted in
|
|
6
|
+
* Slack as that agent's bot — in its DM with the owner, else its team
|
|
7
|
+
* channel — as the root of ONE Slack thread per comment thread. The
|
|
8
|
+
* `{appId, commentId} <-> {channel, thread_ts}` mapping is persisted in
|
|
9
|
+
* `<CREWLY_HOME>/apps/comment-slack-links.json`.
|
|
10
|
+
* - An agent's reply / resolve / reopen on the comment is posted into that
|
|
11
|
+
* thread.
|
|
12
|
+
* - The owner replying in the mapped Slack thread is relayed to Cloud as an
|
|
13
|
+
* owner reply (`via: 'slack'`) and CONSUMED here (inbound interceptor), so it
|
|
14
|
+
* is not also routed to the agent as a chat message. The agent gets it once,
|
|
15
|
+
* from the normal change feed. Slack-originated replies are never mirrored
|
|
16
|
+
* back to Slack.
|
|
17
|
+
*
|
|
18
|
+
* Off switch: `CREWLY_APP_COMMENTS_SLACK=off`. A failed Slack post never
|
|
19
|
+
* affects comment delivery. Harness text is English; the comment text is the
|
|
20
|
+
* owner's words.
|
|
21
|
+
*
|
|
22
|
+
* @module services/apps/app-comments-slack.service
|
|
23
|
+
*/
|
|
24
|
+
import { type AppChange } from './app-wake-message.js';
|
|
25
|
+
/** One comment thread's Slack thread. */
|
|
26
|
+
export interface CommentSlackLink {
|
|
27
|
+
appId: string;
|
|
28
|
+
commentId: string;
|
|
29
|
+
/** The agent whose bot posted the root (and posts the rest) */
|
|
30
|
+
agentSession: string;
|
|
31
|
+
channel: string;
|
|
32
|
+
threadTs: string;
|
|
33
|
+
/** Reply ids already shown in Slack (so two recipients / a resend never double-post) */
|
|
34
|
+
replyIds: string[];
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
/** The inbound Slack message slice the interceptor reads. */
|
|
38
|
+
export interface InboundSlackMessage {
|
|
39
|
+
channelId: string;
|
|
40
|
+
ts: string;
|
|
41
|
+
threadTs?: string;
|
|
42
|
+
userId?: string;
|
|
43
|
+
text?: string;
|
|
44
|
+
authorAgentSession?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Collaborators (all injectable). */
|
|
47
|
+
export interface CommentSlackDeps {
|
|
48
|
+
/** Crewly home directory */
|
|
49
|
+
homeDir: string;
|
|
50
|
+
/** Post as the agent's bot */
|
|
51
|
+
post: (req: {
|
|
52
|
+
agentSession: string;
|
|
53
|
+
target: string;
|
|
54
|
+
text: string;
|
|
55
|
+
threadTs?: string;
|
|
56
|
+
}) => Promise<{
|
|
57
|
+
channelId: string;
|
|
58
|
+
messageTs: string;
|
|
59
|
+
}>;
|
|
60
|
+
/** The Slack DM channel id of an agent with the owner, when it has one */
|
|
61
|
+
dmChannelOf: (agentSession: string) => string | null;
|
|
62
|
+
/** The Slack channel id of the agent's team channel, when it has one */
|
|
63
|
+
teamChannelOf: (agentSession: string) => Promise<string | null>;
|
|
64
|
+
/** Display name of an agent */
|
|
65
|
+
nameOf?: (agentSession: string) => string;
|
|
66
|
+
/** Where the app opens for the owner */
|
|
67
|
+
appUrl?: (appId: string) => string;
|
|
68
|
+
/** Add the owner's reply to the Cloud thread */
|
|
69
|
+
relayOwnerReply: (appId: string, commentId: string, text: string, slackUserId: string) => Promise<void>;
|
|
70
|
+
/** Whether a Slack user is the owner */
|
|
71
|
+
isOwner: (userId: string) => boolean;
|
|
72
|
+
/** The owner's Slack message in this thread was handled (stops "unanswered" tracking) */
|
|
73
|
+
noteHandled?: (channel: string, threadTs: string) => void;
|
|
74
|
+
/** Env override (tests) */
|
|
75
|
+
env?: () => string | undefined;
|
|
76
|
+
log?: (level: 'info' | 'warn', msg: string, meta?: Record<string, unknown>) => void;
|
|
77
|
+
}
|
|
78
|
+
/** Whether the mirror is switched off. */
|
|
79
|
+
export declare function commentsSlackOff(env?: string | undefined): boolean;
|
|
80
|
+
/** Mirrors App comments to Slack and Slack thread replies back. */
|
|
81
|
+
export declare class AppCommentsSlackService {
|
|
82
|
+
private readonly deps;
|
|
83
|
+
private readonly file;
|
|
84
|
+
private links;
|
|
85
|
+
private byThread;
|
|
86
|
+
private loaded;
|
|
87
|
+
private chain;
|
|
88
|
+
private readonly seenInbound;
|
|
89
|
+
constructor(deps: CommentSlackDeps);
|
|
90
|
+
private off;
|
|
91
|
+
private log;
|
|
92
|
+
/** Load the mapping once (call at boot so the inbound check can be synchronous). */
|
|
93
|
+
load(): Promise<void>;
|
|
94
|
+
private index;
|
|
95
|
+
private persist;
|
|
96
|
+
/** The Slack thread of a comment, if one exists. */
|
|
97
|
+
linkOf(appId: string, commentId: string): CommentSlackLink | null;
|
|
98
|
+
/** Serialize work so a root is never created twice. */
|
|
99
|
+
private enqueue;
|
|
100
|
+
/**
|
|
101
|
+
* Owner comment changes just delivered to `session`: post new ones to Slack.
|
|
102
|
+
*
|
|
103
|
+
* @param session - The agent they were delivered to
|
|
104
|
+
* @param appId - App
|
|
105
|
+
* @param appName - App name
|
|
106
|
+
* @param changes - The batch's comment changes
|
|
107
|
+
*/
|
|
108
|
+
mirrorOwnerComments(session: string, appId: string, appName: string, changes: AppChange[]): Promise<void>;
|
|
109
|
+
private remember;
|
|
110
|
+
private openThread;
|
|
111
|
+
private postIn;
|
|
112
|
+
/**
|
|
113
|
+
* An agent replied on a comment in the app: show it in the thread.
|
|
114
|
+
*
|
|
115
|
+
* @param replier - The replying agent session
|
|
116
|
+
*/
|
|
117
|
+
agentReplied(appId: string, commentId: string, replier: string, text: string): Promise<void>;
|
|
118
|
+
/** An agent resolved / reopened the comment in the app: one short line in the thread. */
|
|
119
|
+
statusChanged(appId: string, commentId: string, by: string, action: 'resolve' | 'reopen'): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Inbound Slack message hook (synchronous, for the bridge interceptor):
|
|
122
|
+
* the owner's reply in a mapped thread goes to the app comment instead of
|
|
123
|
+
* the normal routing.
|
|
124
|
+
*
|
|
125
|
+
* @returns True when the message was consumed
|
|
126
|
+
*/
|
|
127
|
+
interceptInbound(message: InboundSlackMessage): boolean;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=app-comments-slack.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-comments-slack.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comments-slack.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crewly App comments <-> Slack, both ways (owner request 2026-10-05: "a
|
|
3
|
+
* reply on either side is visible on both").
|
|
4
|
+
*
|
|
5
|
+
* - An owner comment (new, or a reply) delivered to an agent is also posted in
|
|
6
|
+
* Slack as that agent's bot — in its DM with the owner, else its team
|
|
7
|
+
* channel — as the root of ONE Slack thread per comment thread. The
|
|
8
|
+
* `{appId, commentId} <-> {channel, thread_ts}` mapping is persisted in
|
|
9
|
+
* `<CREWLY_HOME>/apps/comment-slack-links.json`.
|
|
10
|
+
* - An agent's reply / resolve / reopen on the comment is posted into that
|
|
11
|
+
* thread.
|
|
12
|
+
* - The owner replying in the mapped Slack thread is relayed to Cloud as an
|
|
13
|
+
* owner reply (`via: 'slack'`) and CONSUMED here (inbound interceptor), so it
|
|
14
|
+
* is not also routed to the agent as a chat message. The agent gets it once,
|
|
15
|
+
* from the normal change feed. Slack-originated replies are never mirrored
|
|
16
|
+
* back to Slack.
|
|
17
|
+
*
|
|
18
|
+
* Off switch: `CREWLY_APP_COMMENTS_SLACK=off`. A failed Slack post never
|
|
19
|
+
* affects comment delivery. Harness text is English; the comment text is the
|
|
20
|
+
* owner's words.
|
|
21
|
+
*
|
|
22
|
+
* @module services/apps/app-comments-slack.service
|
|
23
|
+
*/
|
|
24
|
+
import fs from 'fs/promises';
|
|
25
|
+
import path from 'path';
|
|
26
|
+
import { CREWLY_APPS_CONSTANTS } from '../../constants.js';
|
|
27
|
+
import { anchorSummary } from './app-wake-message.js';
|
|
28
|
+
/** Slack mrkdwn escapes for text that is not meant as markup. */
|
|
29
|
+
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
30
|
+
/** Whether the mirror is switched off. */
|
|
31
|
+
export function commentsSlackOff(env = process.env.CREWLY_APP_COMMENTS_SLACK) {
|
|
32
|
+
return (env ?? '').trim().toLowerCase() === 'off';
|
|
33
|
+
}
|
|
34
|
+
/** Mirrors App comments to Slack and Slack thread replies back. */
|
|
35
|
+
export class AppCommentsSlackService {
|
|
36
|
+
deps;
|
|
37
|
+
file;
|
|
38
|
+
links = new Map();
|
|
39
|
+
byThread = new Map();
|
|
40
|
+
loaded = null;
|
|
41
|
+
chain = Promise.resolve();
|
|
42
|
+
seenInbound = new Set();
|
|
43
|
+
constructor(deps) {
|
|
44
|
+
this.deps = deps;
|
|
45
|
+
this.file = path.join(deps.homeDir, 'apps', 'comment-slack-links.json');
|
|
46
|
+
}
|
|
47
|
+
off() {
|
|
48
|
+
return commentsSlackOff(this.deps.env ? this.deps.env() : process.env.CREWLY_APP_COMMENTS_SLACK);
|
|
49
|
+
}
|
|
50
|
+
log(level, msg, meta) {
|
|
51
|
+
this.deps.log?.(level, msg, meta);
|
|
52
|
+
}
|
|
53
|
+
/** Load the mapping once (call at boot so the inbound check can be synchronous). */
|
|
54
|
+
load() {
|
|
55
|
+
this.loaded ??= (async () => {
|
|
56
|
+
try {
|
|
57
|
+
const raw = JSON.parse(await fs.readFile(this.file, 'utf-8'));
|
|
58
|
+
for (const l of raw.links ?? [])
|
|
59
|
+
this.index(l);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* none yet */
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
return this.loaded;
|
|
66
|
+
}
|
|
67
|
+
index(l) {
|
|
68
|
+
this.links.set(`${l.appId}:${l.commentId}`, l);
|
|
69
|
+
this.byThread.set(`${l.channel}:${l.threadTs}`, l);
|
|
70
|
+
}
|
|
71
|
+
async persist() {
|
|
72
|
+
await fs.mkdir(path.dirname(this.file), { recursive: true });
|
|
73
|
+
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
74
|
+
await fs.writeFile(tmp, JSON.stringify({ links: [...this.links.values()] }, null, 2), 'utf-8');
|
|
75
|
+
await fs.rename(tmp, this.file);
|
|
76
|
+
}
|
|
77
|
+
/** The Slack thread of a comment, if one exists. */
|
|
78
|
+
linkOf(appId, commentId) {
|
|
79
|
+
return this.links.get(`${appId}:${commentId}`) ?? null;
|
|
80
|
+
}
|
|
81
|
+
/** Serialize work so a root is never created twice. */
|
|
82
|
+
enqueue(work) {
|
|
83
|
+
const next = this.chain.then(work).catch((err) => this.log('warn', 'App comment Slack mirror failed', { error: err instanceof Error ? err.message : String(err) }));
|
|
84
|
+
this.chain = next;
|
|
85
|
+
return next;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Owner comment changes just delivered to `session`: post new ones to Slack.
|
|
89
|
+
*
|
|
90
|
+
* @param session - The agent they were delivered to
|
|
91
|
+
* @param appId - App
|
|
92
|
+
* @param appName - App name
|
|
93
|
+
* @param changes - The batch's comment changes
|
|
94
|
+
*/
|
|
95
|
+
mirrorOwnerComments(session, appId, appName, changes) {
|
|
96
|
+
if (this.off() || changes.length === 0)
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
return this.enqueue(async () => {
|
|
99
|
+
await this.load();
|
|
100
|
+
for (const c of changes) {
|
|
101
|
+
const info = c.comment;
|
|
102
|
+
const thread = info?.thread;
|
|
103
|
+
if (!info?.id || !thread)
|
|
104
|
+
continue;
|
|
105
|
+
const op = info.op;
|
|
106
|
+
if (op !== 'add' && op !== 'reply' && op !== 'reopen')
|
|
107
|
+
continue;
|
|
108
|
+
const reply = op === 'reply' ? (thread.replies ?? []).find((r) => r.id === info.replyId) : undefined;
|
|
109
|
+
// Written in Slack: it is already there.
|
|
110
|
+
if (reply?.author?.via === 'slack' || c.actor?.via === 'slack')
|
|
111
|
+
continue;
|
|
112
|
+
let link = this.linkOf(appId, info.id);
|
|
113
|
+
if (op === 'add' && link)
|
|
114
|
+
continue; // a second recipient of the same comment
|
|
115
|
+
if (!link) {
|
|
116
|
+
link = await this.openThread(session, appId, appName, info.id, anchorSummary(thread.anchor), thread.body ?? '');
|
|
117
|
+
if (!link)
|
|
118
|
+
continue;
|
|
119
|
+
if (op === 'add')
|
|
120
|
+
continue; // the root IS the comment
|
|
121
|
+
}
|
|
122
|
+
if (op === 'reply' && reply?.id && reply.body !== undefined) {
|
|
123
|
+
if (link.replyIds.includes(reply.id))
|
|
124
|
+
continue;
|
|
125
|
+
if (await this.postIn(link, `💬 ${esc(reply.body)}`))
|
|
126
|
+
await this.remember(link, reply.id);
|
|
127
|
+
}
|
|
128
|
+
else if (op === 'reopen') {
|
|
129
|
+
await this.postIn(link, '↩️ The owner reopened this comment in the app.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async remember(link, replyId) {
|
|
135
|
+
link.replyIds = [...link.replyIds, replyId].slice(-50);
|
|
136
|
+
await this.persist();
|
|
137
|
+
}
|
|
138
|
+
async openThread(session, appId, appName, commentId, element, body) {
|
|
139
|
+
const target = this.deps.dmChannelOf(session) ?? (await this.deps.teamChannelOf(session));
|
|
140
|
+
if (!target) {
|
|
141
|
+
this.log('info', 'App comment not mirrored to Slack: the agent has no DM or team channel', { session, appId });
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const url = (this.deps.appUrl ?? ((id) => `${CREWLY_APPS_CONSTANTS.APPS_ORIGIN}/${id}`))(appId);
|
|
145
|
+
const text = `💬 Comment on ${esc(appName)} — ${esc(element)}: ${esc(body)} · <${url}|Open app>`;
|
|
146
|
+
const posted = await this.deps.post({ agentSession: session, target, text });
|
|
147
|
+
const link = { appId, commentId, agentSession: session, channel: posted.channelId, threadTs: posted.messageTs, replyIds: [], createdAt: new Date().toISOString() };
|
|
148
|
+
this.index(link);
|
|
149
|
+
await this.persist();
|
|
150
|
+
return link;
|
|
151
|
+
}
|
|
152
|
+
async postIn(link, text) {
|
|
153
|
+
try {
|
|
154
|
+
await this.deps.post({ agentSession: link.agentSession, target: link.channel, text, threadTs: link.threadTs });
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
this.log('warn', 'App comment Slack post failed', { appId: link.appId, error: err instanceof Error ? err.message : String(err) });
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* An agent replied on a comment in the app: show it in the thread.
|
|
164
|
+
*
|
|
165
|
+
* @param replier - The replying agent session
|
|
166
|
+
*/
|
|
167
|
+
agentReplied(appId, commentId, replier, text) {
|
|
168
|
+
if (this.off())
|
|
169
|
+
return Promise.resolve();
|
|
170
|
+
return this.enqueue(async () => {
|
|
171
|
+
await this.load();
|
|
172
|
+
const link = this.linkOf(appId, commentId);
|
|
173
|
+
if (!link)
|
|
174
|
+
return;
|
|
175
|
+
// The thread's bot is the one in this channel; name a different replier.
|
|
176
|
+
const who = replier !== link.agentSession ? `*${esc(this.deps.nameOf?.(replier) ?? replier)}:* ` : '';
|
|
177
|
+
await this.postIn(link, `${who}${esc(text)}`);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/** An agent resolved / reopened the comment in the app: one short line in the thread. */
|
|
181
|
+
statusChanged(appId, commentId, by, action) {
|
|
182
|
+
if (this.off())
|
|
183
|
+
return Promise.resolve();
|
|
184
|
+
return this.enqueue(async () => {
|
|
185
|
+
await this.load();
|
|
186
|
+
const link = this.linkOf(appId, commentId);
|
|
187
|
+
if (!link)
|
|
188
|
+
return;
|
|
189
|
+
const name = esc(this.deps.nameOf?.(by) ?? by);
|
|
190
|
+
await this.postIn(link, action === 'resolve' ? `✅ Resolved by ${name}.` : `↩️ Reopened by ${name}.`);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Inbound Slack message hook (synchronous, for the bridge interceptor):
|
|
195
|
+
* the owner's reply in a mapped thread goes to the app comment instead of
|
|
196
|
+
* the normal routing.
|
|
197
|
+
*
|
|
198
|
+
* @returns True when the message was consumed
|
|
199
|
+
*/
|
|
200
|
+
interceptInbound(message) {
|
|
201
|
+
if (this.off() || !message.threadTs || message.threadTs === message.ts)
|
|
202
|
+
return false;
|
|
203
|
+
if (message.authorAgentSession || !message.userId)
|
|
204
|
+
return false;
|
|
205
|
+
const link = this.byThread.get(`${message.channelId}:${message.threadTs}`);
|
|
206
|
+
if (!link)
|
|
207
|
+
return false;
|
|
208
|
+
if (!this.deps.isOwner(message.userId))
|
|
209
|
+
return false;
|
|
210
|
+
const text = (message.text ?? '').trim();
|
|
211
|
+
if (!text)
|
|
212
|
+
return false;
|
|
213
|
+
if (this.seenInbound.has(message.ts))
|
|
214
|
+
return true; // Slack redelivery
|
|
215
|
+
this.seenInbound.add(message.ts);
|
|
216
|
+
if (this.seenInbound.size > 200)
|
|
217
|
+
this.seenInbound.delete(this.seenInbound.values().next().value);
|
|
218
|
+
const userId = message.userId;
|
|
219
|
+
void this.enqueue(async () => {
|
|
220
|
+
try {
|
|
221
|
+
await this.deps.relayOwnerReply(link.appId, link.commentId, text.slice(0, CREWLY_APPS_CONSTANTS.COMMENTS.MAX_BODY_CHARS), userId);
|
|
222
|
+
this.log('info', 'Slack thread reply added to the app comment', { appId: link.appId, commentId: link.commentId });
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
this.log('warn', 'Slack thread reply could not be added to the app comment', { appId: link.appId, error: err instanceof Error ? err.message : String(err) });
|
|
226
|
+
await this.postIn(link, "⚠️ I couldn't add that reply to the comment in the app. Please reply there instead.");
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// The owner was answered by the comment path: nothing is owed in this thread.
|
|
230
|
+
this.deps.noteHandled?.(message.channelId, message.threadTs);
|
|
231
|
+
setTimeout(() => this.deps.noteHandled?.(message.channelId, message.threadTs), 1500).unref?.();
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=app-comments-slack.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-comments-slack.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comments-slack.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
*/
|
|
66
66
|
import { type ComponentLogger } from '../core/logger.service.js';
|
|
67
67
|
import type { AppsRegistryService } from './apps-registry.service.js';
|
|
68
|
-
import { type AppCommentThread } from './app-wake-message.js';
|
|
68
|
+
import { type AppChange, type AppCommentThread } from './app-wake-message.js';
|
|
69
69
|
/** The Apps API slice the poller needs. */
|
|
70
70
|
export interface AppWakeClient {
|
|
71
71
|
isAvailable(): boolean;
|
|
@@ -110,6 +110,13 @@ export interface AppWakeServiceDeps {
|
|
|
110
110
|
isLocalAgent?: (session: string) => Promise<boolean>;
|
|
111
111
|
/** This instance's Cloud id (to tell its mentions from another machine's) */
|
|
112
112
|
instanceId?: () => Promise<string | null>;
|
|
113
|
+
/** The owner's comment changes were delivered to `session` (Slack mirror; never awaited, never fails delivery) */
|
|
114
|
+
onCommentsDelivered?: (info: {
|
|
115
|
+
session: string;
|
|
116
|
+
appId: string;
|
|
117
|
+
appName: string;
|
|
118
|
+
comments: AppChange[];
|
|
119
|
+
}) => void;
|
|
113
120
|
/** Pushes this instance's agent roster to Cloud when it changed */
|
|
114
121
|
roster?: {
|
|
115
122
|
pushIfChanged(): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-wake.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-wake.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAGH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEhF,OAAO,KAAK,EAAE,mBAAmB,EAAsC,MAAM,4BAA4B,CAAC;AAC1G,OAAO,
|
|
1
|
+
{"version":3,"file":"app-wake.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-wake.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -865,6 +865,14 @@ export class AppWakeService {
|
|
|
865
865
|
mset.add(seq);
|
|
866
866
|
this.mentionDelivered = mset;
|
|
867
867
|
}
|
|
868
|
+
if (batch.comments.length > 0 && this.deps.onCommentsDelivered) {
|
|
869
|
+
try {
|
|
870
|
+
this.deps.onCommentsDelivered({ session: batch.session ?? ORCHESTRATOR_SESSION_NAME, appId: batch.appId, appName: app?.name ?? batch.appName ?? batch.appId, comments: batch.comments });
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
/* a mirror problem never fails delivery */
|
|
874
|
+
}
|
|
875
|
+
}
|
|
868
876
|
this.logger.info('Woke agent for app changes', {
|
|
869
877
|
appId: batch.appId,
|
|
870
878
|
session: batch.session ?? 'orchestrator',
|