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.
Files changed (100) hide show
  1. package/config/skills/agent/core/app-comments/SKILL.md +2 -0
  2. package/dist/backend/backend/src/constants.d.ts +9 -0
  3. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  4. package/dist/backend/backend/src/constants.js +6 -1
  5. package/dist/backend/backend/src/constants.js.map +1 -1
  6. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  7. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +10 -0
  8. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  9. package/dist/backend/backend/src/index.d.ts +17 -0
  10. package/dist/backend/backend/src/index.d.ts.map +1 -1
  11. package/dist/backend/backend/src/index.js +193 -124
  12. package/dist/backend/backend/src/index.js.map +1 -1
  13. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  14. package/dist/backend/backend/src/services/agent/agent-registration.service.js +30 -0
  15. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  16. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  17. package/dist/backend/backend/src/services/agent/idle-detection.service.js +5 -9
  18. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  19. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +127 -0
  20. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
  21. package/dist/backend/backend/src/services/agent/resource-mode.service.js +264 -0
  22. package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -0
  23. package/dist/backend/backend/src/services/agent/restore-queue.d.ts +103 -0
  24. package/dist/backend/backend/src/services/agent/restore-queue.d.ts.map +1 -0
  25. package/dist/backend/backend/src/services/agent/restore-queue.js +184 -0
  26. package/dist/backend/backend/src/services/agent/restore-queue.js.map +1 -0
  27. package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts +129 -0
  28. package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts.map +1 -0
  29. package/dist/backend/backend/src/services/apps/app-comments-slack.service.js +235 -0
  30. package/dist/backend/backend/src/services/apps/app-comments-slack.service.js.map +1 -0
  31. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts +8 -1
  32. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/apps/app-wake.service.js +8 -0
  34. package/dist/backend/backend/src/services/apps/app-wake.service.js.map +1 -1
  35. package/dist/backend/backend/src/services/apps/apps.service.d.ts +5 -0
  36. package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/apps/apps.service.js +8 -2
  38. package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts +16 -0
  40. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/apps/apps.wiring.js +74 -0
  42. package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
  43. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +13 -0
  44. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +29 -0
  46. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +52 -1
  48. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +47 -0
  50. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
  51. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +1 -0
  53. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
  54. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts +1 -1
  55. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js +3 -0
  57. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js.map +1 -1
  58. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +23 -0
  59. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +134 -1
  61. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
  62. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
  63. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  64. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +16 -2
  65. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  66. package/dist/backend/backend/src/types/settings.types.d.ts +4 -0
  67. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  68. package/dist/backend/backend/src/types/settings.types.js +8 -0
  69. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  70. package/dist/backend/build-info.json +2 -2
  71. package/dist/cli/backend/src/constants.d.ts +9 -0
  72. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  73. package/dist/cli/backend/src/constants.js +6 -1
  74. package/dist/cli/backend/src/constants.js.map +1 -1
  75. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  76. package/dist/cli/backend/src/services/agent/agent-registration.service.js +30 -0
  77. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  78. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +127 -0
  79. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -0
  80. package/dist/cli/backend/src/services/agent/resource-mode.service.js +264 -0
  81. package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -0
  82. package/dist/cli/backend/src/services/agent/restore-queue.d.ts +103 -0
  83. package/dist/cli/backend/src/services/agent/restore-queue.d.ts.map +1 -0
  84. package/dist/cli/backend/src/services/agent/restore-queue.js +184 -0
  85. package/dist/cli/backend/src/services/agent/restore-queue.js.map +1 -0
  86. package/dist/cli/backend/src/services/core/system-health.util.d.ts +89 -0
  87. package/dist/cli/backend/src/services/core/system-health.util.d.ts.map +1 -0
  88. package/dist/cli/backend/src/services/core/system-health.util.js +220 -0
  89. package/dist/cli/backend/src/services/core/system-health.util.js.map +1 -0
  90. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
  91. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  92. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +16 -2
  93. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  94. package/dist/cli/backend/src/types/settings.types.d.ts +4 -0
  95. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  96. package/dist/cli/backend/src/types/settings.types.js +8 -0
  97. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  98. package/frontend/dist/assets/{index-bc4e2a93.js → index-a719c15a.js} +54 -54
  99. package/frontend/dist/index.html +1 -1
  100. 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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAkB,MAAM,uBAAuB,CAAC;AAkDtE,iEAAiE;AACjE,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAExG,0CAA0C;AAC1C,MAAM,UAAU,gBAAgB,CAAC,MAA0B,OAAO,CAAC,GAAG,CAAC,yBAAyB;IAC9F,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;AACpD,CAAC;AAED,mEAAmE;AACnE,MAAM,OAAO,uBAAuB;IAQL;IAPZ,IAAI,CAAS;IACtB,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC5C,QAAQ,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC/C,MAAM,GAAyB,IAAI,CAAC;IACpC,KAAK,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjD,YAA6B,IAAsB;QAAtB,SAAI,GAAJ,IAAI,CAAkB;QACjD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,0BAA0B,CAAC,CAAC;IAC1E,CAAC;IAEO,GAAG;QACT,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACnG,CAAC;IAEO,GAAG,CAAC,KAAsB,EAAE,GAAW,EAAE,IAA8B;QAC7E,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,oFAAoF;IACpF,IAAI;QACF,IAAI,CAAC,MAAM,KAAK,CAAC,KAAK,IAAI,EAAE;YAC1B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAmC,CAAC;gBAChG,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;oBAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjD,CAAC;YAAC,MAAM,CAAC;gBACP,cAAc;YAChB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,CAAmB;QAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;QAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/F,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,KAAa,EAAE,SAAiB;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,IAAI,SAAS,EAAE,CAAC,IAAI,IAAI,CAAC;IACzD,CAAC;IAED,uDAAuD;IAC/C,OAAO,CAAC,IAAyB;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,iCAAiC,EAAE,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACpK,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,mBAAmB,CAAC,OAAe,EAAE,KAAa,EAAE,OAAe,EAAE,OAAoB;QACvF,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC;gBACvB,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC;gBAC5B,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,MAAM;oBAAE,SAAS;gBACnC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;gBACnB,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,OAAO,IAAI,EAAE,KAAK,QAAQ;oBAAE,SAAS;gBAChE,MAAM,KAAK,GAAG,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACrG,yCAAyC;gBACzC,IAAK,KAAK,EAAE,MAAuC,EAAE,GAAG,KAAK,OAAO,IAAK,CAAC,CAAC,KAAsC,EAAE,GAAG,KAAK,OAAO;oBAAE,SAAS;gBAC7I,IAAI,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,KAAK,KAAK,IAAI,IAAI;oBAAE,SAAS,CAAC,yCAAyC;gBAC7E,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;oBAChH,IAAI,CAAC,IAAI;wBAAE,SAAS;oBACpB,IAAI,EAAE,KAAK,KAAK;wBAAE,SAAS,CAAC,0BAA0B;gBACxD,CAAC;gBACD,IAAI,EAAE,KAAK,OAAO,IAAI,KAAK,EAAE,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;wBAAE,SAAS;oBAC/C,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;wBAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC5F,CAAC;qBAAM,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;oBAC3B,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,gDAAgD,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,IAAsB,EAAE,OAAe;QAC5D,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,KAAa,EAAE,OAAe,EAAE,SAAiB,EAAE,OAAe,EAAE,IAAY;QACxH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1F,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,wEAAwE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/G,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,qBAAqB,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChG,MAAM,IAAI,GAAG,iBAAiB,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC;QACnG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAqB,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACrL,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,IAAsB,EAAE,IAAY;QACvD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC/G,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,+BAA+B,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClI,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,KAAa,EAAE,SAAiB,EAAE,OAAe,EAAE,IAAY;QAC1E,IAAI,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,yEAAyE;YACzE,MAAM,GAAG,GAAG,OAAO,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACtG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yFAAyF;IACzF,aAAa,CAAC,KAAa,EAAE,SAAiB,EAAE,EAAU,EAAE,MAA4B;QACtF,IAAI,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,IAAI,GAAG,CAAC,CAAC,CAAC,kBAAkB,IAAI,GAAG,CAAC,CAAC;QACvG,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,gBAAgB,CAAC,OAA4B;QAC3C,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QACrF,IAAI,OAAO,CAAC,kBAAkB,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAChE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,mBAAmB;QACtE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,GAAG;YAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC,CAAC;QAC3G,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC3B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;gBAClI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,6CAA6C,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YACpH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,0DAA0D,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC7J,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,qFAAqF,CAAC,CAAC;YACjH,CAAC;QACH,CAAC,CAAC,CAAC;QACH,8EAA8E;QAC9E,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC7D,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,QAAkB,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QACzG,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -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":"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"}
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',