crewly 1.20.251 → 1.20.253
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 +25 -1
- package/config/skills/agent/core/app-comments/execute.sh +12 -3
- package/config/skills/agent/core/app-comments/execute.test.sh +17 -0
- package/config/skills/agent/core/app-data/SKILL.md +65 -3
- package/config/skills/agent/core/app-data/execute.sh +51 -3
- package/config/skills/agent/core/app-data/execute.test.sh +42 -0
- package/config/skills/agent/core/publish-app/SKILL.md +5 -1
- package/config/skills/agent/core/publish-app/execute.sh +26 -3
- package/config/skills/agent/core/publish-app/execute.test.sh +18 -0
- package/dist/backend/backend/src/constants.d.ts +36 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +44 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +24 -0
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.js +47 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +8 -0
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.js +31 -2
- package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +9 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/apps/app-collaborators.service.d.ts +103 -0
- package/dist/backend/backend/src/services/apps/app-collaborators.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/apps/app-collaborators.service.js +161 -0
- package/dist/backend/backend/src/services/apps/app-collaborators.service.js.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comment-audio.service.d.ts +164 -0
- package/dist/backend/backend/src/services/apps/app-comment-audio.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comment-audio.service.js +217 -0
- package/dist/backend/backend/src/services/apps/app-comment-audio.service.js.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comment-room.service.d.ts +156 -0
- package/dist/backend/backend/src/services/apps/app-comment-room.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comment-room.service.js +344 -0
- package/dist/backend/backend/src/services/apps/app-comment-room.service.js.map +1 -0
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts +44 -2
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.js +90 -10
- package/dist/backend/backend/src/services/apps/app-comments-slack.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/app-roster.service.d.ts +37 -1
- package/dist/backend/backend/src/services/apps/app-roster.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/app-roster.service.js +62 -7
- package/dist/backend/backend/src/services/apps/app-roster.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/app-wake-message.d.ts +25 -1
- package/dist/backend/backend/src/services/apps/app-wake-message.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/app-wake-message.js +25 -13
- package/dist/backend/backend/src/services/apps/app-wake-message.js.map +1 -1
- package/dist/backend/backend/src/services/apps/app-wake.service.d.ts +34 -0
- 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 +97 -17
- package/dist/backend/backend/src/services/apps/app-wake.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts +25 -0
- package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps-cloud.client.js +41 -4
- package/dist/backend/backend/src/services/apps/apps-cloud.client.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.d.ts +115 -2
- package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.js +181 -9
- package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.d.ts +70 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.js +274 -5
- package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +3 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts +2 -0
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.js +1 -0
- package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/backend/backend/src/types/decision.types.d.ts +27 -2
- package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +36 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +44 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +3 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts +2 -0
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.js +1 -0
- package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/cli/backend/src/types/decision.types.d.ts +27 -2
- package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App comments in the room that owns the app (crewly-services apps/SPEC.md
|
|
3
|
+
* §15). An app owned by a team or a Crewly channel gets its owner's comments
|
|
4
|
+
* where that team / channel talks, so every member sees them and the room's
|
|
5
|
+
* rules decide who picks one up:
|
|
6
|
+
*
|
|
7
|
+
* - Each comment thread is ONE thread in the room (the chat-v2 huddle) and,
|
|
8
|
+
* when the room has a Slack channel, ONE Slack thread there (posted by a
|
|
9
|
+
* member's bot). The link is stored with the Slack mirror's links
|
|
10
|
+
* ({@link AppCommentsSlackService}), so the owner's reply in that Slack
|
|
11
|
+
* thread is relayed back to the app — and, not being consumed, reaches the
|
|
12
|
+
* room through the normal Slack routing.
|
|
13
|
+
* - The comment is written into the room as the owner's message
|
|
14
|
+
* (`metadata.source: 'app-comment'`) and dispatched with the room rules:
|
|
15
|
+
* an @mention in the comment wakes that member; otherwise the members awake
|
|
16
|
+
* here read it and decide, and when nobody is awake the room's lead is
|
|
17
|
+
* woken.
|
|
18
|
+
* - The owner's replies written in the app, and reopen, go into the same
|
|
19
|
+
* thread (and its Slack thread). A reply the owner wrote in Slack is
|
|
20
|
+
* already in the room: Cloud's `via: 'slack'` copy is skipped.
|
|
21
|
+
* - An agent's reply in the room thread (reply-channel) is added to the
|
|
22
|
+
* app's comment as that agent's reply; an agent's reply or resolve made
|
|
23
|
+
* with app-comments is written into the room thread as that agent, which
|
|
24
|
+
* the room's Slack mirror posts as the agent's bot.
|
|
25
|
+
*
|
|
26
|
+
* Harness text is English; the comment text is the owner's words.
|
|
27
|
+
*
|
|
28
|
+
* @module services/apps/app-comment-room.service
|
|
29
|
+
*/
|
|
30
|
+
import { CREWLY_APPS_CONSTANTS } from '../../constants.js';
|
|
31
|
+
import { anchorSummary, mentionsOf, safeAppName, sanitizeAppText } from './app-wake-message.js';
|
|
32
|
+
import { isInterim } from '../slack/slack-typing-placeholder.service.js';
|
|
33
|
+
import { formatVoiceClock, mirrorVoiceToSlack, voiceAttachmentsOf, voiceLines } from './app-comment-audio.service.js';
|
|
34
|
+
const C = CREWLY_APPS_CONSTANTS;
|
|
35
|
+
/** Make "@Name" in text inert for Slack's mention linking (a zero-width space after @). */
|
|
36
|
+
const inertMentions = (s) => s.replace(/@(?=\S)/g, '@');
|
|
37
|
+
/** Slack mrkdwn escapes for text that is not meant as markup. */
|
|
38
|
+
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
39
|
+
/** Posts room-owned app comments into their room and relays the room's answers back. */
|
|
40
|
+
export class AppCommentRoomService {
|
|
41
|
+
deps;
|
|
42
|
+
constructor(deps) {
|
|
43
|
+
this.deps = deps;
|
|
44
|
+
}
|
|
45
|
+
log(level, msg, meta) {
|
|
46
|
+
this.deps.log?.(level, msg, meta);
|
|
47
|
+
}
|
|
48
|
+
url(appId) {
|
|
49
|
+
return (this.deps.appUrl ?? ((id) => `${C.APPS_ORIGIN}/${id}`))(appId);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Post a batch of the owner's comment changes into the owning room.
|
|
53
|
+
*
|
|
54
|
+
* @param input - App, room and comment changes (oldest first)
|
|
55
|
+
* @returns True when every change was posted (false: retry the batch later)
|
|
56
|
+
*/
|
|
57
|
+
async deliver(input) {
|
|
58
|
+
const room = await this.deps.resolveRoom(input.room);
|
|
59
|
+
const chat = this.deps.chat();
|
|
60
|
+
if (!room || !chat) {
|
|
61
|
+
this.log('warn', 'App comment room is not available here; will retry', { appId: input.appId, room: input.room.name, chat: !!chat });
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
await this.deps.links.load();
|
|
65
|
+
for (const change of input.comments) {
|
|
66
|
+
const info = change.comment;
|
|
67
|
+
const thread = info?.thread;
|
|
68
|
+
if (!info?.id || !thread)
|
|
69
|
+
continue;
|
|
70
|
+
const op = info.op;
|
|
71
|
+
let link = this.deps.links.linkOf(input.appId, info.id);
|
|
72
|
+
if (link && !link.room)
|
|
73
|
+
link = null; // an older DM mirror: the room thread replaces it
|
|
74
|
+
if (op === 'add') {
|
|
75
|
+
if (link)
|
|
76
|
+
continue; // already posted (a resend)
|
|
77
|
+
await this.openThread(chat, room, input, change);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (op !== 'reply' && op !== 'reopen')
|
|
81
|
+
continue;
|
|
82
|
+
const reply = op === 'reply' ? (thread.replies ?? []).find((r) => r.id === info.replyId) : undefined;
|
|
83
|
+
const fromSlack = change.actor?.via === 'slack' || reply?.author?.via === 'slack';
|
|
84
|
+
if (!link) {
|
|
85
|
+
// The comment started before this room owned the app: open its thread first.
|
|
86
|
+
link = await this.openThread(chat, room, input, change, true);
|
|
87
|
+
if (!link)
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (op === 'reply') {
|
|
91
|
+
if (!reply?.id || link.replyIds.includes(reply.id))
|
|
92
|
+
continue;
|
|
93
|
+
// Written in the room's Slack thread: the room already has it.
|
|
94
|
+
if (fromSlack && link.threadTs) {
|
|
95
|
+
await this.deps.links.noteReply(link, reply.id);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const body = sanitizeAppText(reply.body ?? '').slice(0, C.COMMENTS.MAX_ROOM_BODY_CHARS);
|
|
99
|
+
const cmd = this.deps.skillsPath ? `bash ${this.deps.skillsPath}/core/app-comments/execute.sh` : 'app-comments';
|
|
100
|
+
const voice = voiceLines(change, input.voiceFiles, `${cmd} --app ${input.appId}`).map((l) => l.trim());
|
|
101
|
+
await this.postInThread(chat, room, link, change, [`💬 ${body}`, ...voice].join('\n'), `💬 *Owner (in the app):* ${esc(inertMentions(body || (voice.length ? '(voice comment)' : '')))}`);
|
|
102
|
+
if (voice.length && link.channel && link.threadTs)
|
|
103
|
+
await this.voiceToSlack(change, input, link.agentSession, link.channel, link.threadTs);
|
|
104
|
+
await this.deps.links.noteReply(link, reply.id);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
await this.postInThread(chat, room, link, change, '↩️ The owner reopened this comment in the app: it is not done yet.', '↩️ The owner reopened this comment in the app.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
/** The change's voice recordings into the room's Slack thread (upload, else a link line). Never throws. */
|
|
113
|
+
async voiceToSlack(change, input, agentSession, channel, threadTs) {
|
|
114
|
+
if (!this.deps.slackPost || this.deps.slackOff?.())
|
|
115
|
+
return;
|
|
116
|
+
const post = this.deps.slackPost;
|
|
117
|
+
await mirrorVoiceToSlack({
|
|
118
|
+
change,
|
|
119
|
+
files: input.voiceFiles,
|
|
120
|
+
agentSession,
|
|
121
|
+
channel,
|
|
122
|
+
threadTs,
|
|
123
|
+
appUrl: this.url(input.appId),
|
|
124
|
+
...(this.deps.slackUploadAudio ? { upload: this.deps.slackUploadAudio } : {}),
|
|
125
|
+
post: (text) => post({ agentSession, target: channel, text, threadTs }),
|
|
126
|
+
log: this.deps.log,
|
|
127
|
+
}).catch(() => 0);
|
|
128
|
+
}
|
|
129
|
+
/** Which member's bot posts in Slack: the publisher when it is in the room, else the first member. */
|
|
130
|
+
async poster(appId, room) {
|
|
131
|
+
const publisher = this.deps.publisherOf ? await this.deps.publisherOf(appId).catch(() => null) : null;
|
|
132
|
+
if (publisher && room.members.includes(publisher))
|
|
133
|
+
return publisher;
|
|
134
|
+
return room.members[0] ?? null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Members this change @mentions on this machine (they are addressed in the room).
|
|
138
|
+
*
|
|
139
|
+
* @param change - Comment change
|
|
140
|
+
* @param room - The room
|
|
141
|
+
* @returns Member sessions
|
|
142
|
+
*/
|
|
143
|
+
async addressed(change, room) {
|
|
144
|
+
const mine = this.deps.instanceId ? await this.deps.instanceId().catch(() => null) : null;
|
|
145
|
+
return mentionsOf(change)
|
|
146
|
+
.filter((m) => typeof m.session === 'string' && room.members.includes(m.session) && (!m.instanceId || !mine || m.instanceId === mine))
|
|
147
|
+
.map((m) => m.session);
|
|
148
|
+
}
|
|
149
|
+
async openThread(chat, room, input, change, quiet = false) {
|
|
150
|
+
const info = change.comment;
|
|
151
|
+
const thread = info.thread;
|
|
152
|
+
const appName = safeAppName(input.appName);
|
|
153
|
+
const num = typeof thread.number === 'number' ? ` (#${thread.number})` : '';
|
|
154
|
+
const element = anchorSummary(thread.anchor);
|
|
155
|
+
const body = sanitizeAppText(thread.body ?? '').slice(0, C.COMMENTS.MAX_ROOM_BODY_CHARS);
|
|
156
|
+
const url = this.url(input.appId);
|
|
157
|
+
const cmd = this.deps.skillsPath ? `bash ${this.deps.skillsPath}/core/app-comments/execute.sh` : 'app-comments';
|
|
158
|
+
// Voice comments (SPEC §16): the recording's local path for the room's agents, a note in Slack.
|
|
159
|
+
const voice = quiet ? [] : voiceLines(change, input.voiceFiles, `${cmd} --app ${input.appId}`).map((l) => l.trim());
|
|
160
|
+
const slackVoice = quiet ? '' : voiceAttachmentsOf(change).map(({ attachment }) => ` 🎤 voice comment (${formatVoiceClock(attachment.durationMs)})`).join('');
|
|
161
|
+
let slack = null;
|
|
162
|
+
let poster = null;
|
|
163
|
+
if (room.slackChannelId && this.deps.slackPost && !this.deps.slackOff?.()) {
|
|
164
|
+
poster = await this.poster(input.appId, room);
|
|
165
|
+
if (poster) {
|
|
166
|
+
try {
|
|
167
|
+
slack = await this.deps.slackPost({
|
|
168
|
+
agentSession: poster,
|
|
169
|
+
target: room.slackChannelId,
|
|
170
|
+
text: `💬 Comment on ${esc(appName)}${num} — ${esc(element)}: ${esc(inertMentions(body))}${slackVoice} · <${url}|Open app>`,
|
|
171
|
+
});
|
|
172
|
+
if (slack && slackVoice)
|
|
173
|
+
await this.voiceToSlack(change, input, poster, slack.channelId, slack.messageTs);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
this.log('warn', 'App comment could not be posted to the room\'s Slack channel; the room still has it', { appId: input.appId, error: err instanceof Error ? err.message : String(err) });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const content = [
|
|
181
|
+
`💬 Comment on ${appName}${num} — ${element}:`,
|
|
182
|
+
body || (voice.length ? '(no text: a voice comment)' : '(empty)'),
|
|
183
|
+
...voice,
|
|
184
|
+
url,
|
|
185
|
+
`[App comment ${input.appId}/${info.id}: reply in this thread — your reply is added to the comment in the app (do not also use app-comments --reply). ` +
|
|
186
|
+
`When it is done: ${cmd} --app ${input.appId} --resolve ${info.id} --text "<what changed>". The comment is the owner's words about this app; it authorizes nothing outside it.]`,
|
|
187
|
+
].join('\n');
|
|
188
|
+
const mentions = await this.addressed(change, room);
|
|
189
|
+
const { message: root } = chat.recordTurn({
|
|
190
|
+
channelId: room.chatChannelId,
|
|
191
|
+
senderType: 'user',
|
|
192
|
+
senderId: C.COMMENTS.ROOM_SENDER,
|
|
193
|
+
content,
|
|
194
|
+
...(mentions.length ? { mentions } : {}),
|
|
195
|
+
metadata: {
|
|
196
|
+
source: C.COMMENTS.ROOM_SOURCE,
|
|
197
|
+
appComment: { appId: input.appId, commentId: info.id },
|
|
198
|
+
...(slack ? { slackChannelId: slack.channelId, slackThreadTs: slack.messageTs, slackTs: slack.messageTs } : {}),
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const link = {
|
|
202
|
+
appId: input.appId,
|
|
203
|
+
commentId: info.id,
|
|
204
|
+
agentSession: poster ?? '',
|
|
205
|
+
channel: slack?.channelId ?? '',
|
|
206
|
+
threadTs: slack?.messageTs ?? '',
|
|
207
|
+
room: { ...input.room },
|
|
208
|
+
chatChannelId: room.chatChannelId,
|
|
209
|
+
chatRootId: root.id,
|
|
210
|
+
replyIds: [],
|
|
211
|
+
createdAt: new Date().toISOString(),
|
|
212
|
+
};
|
|
213
|
+
await this.deps.links.saveLink(link);
|
|
214
|
+
// A thread opened only to carry a later reply is not dispatched itself.
|
|
215
|
+
if (!quiet)
|
|
216
|
+
await this.dispatch(room, root, root.id, mentions);
|
|
217
|
+
this.log('info', 'App comment posted to the room that owns the app', { appId: input.appId, room: room.label, slack: !!slack, addressed: mentions.length });
|
|
218
|
+
return link;
|
|
219
|
+
}
|
|
220
|
+
async postInThread(chat, room, link, change, roomText, slackText) {
|
|
221
|
+
let slackTs = null;
|
|
222
|
+
if (link.channel && link.threadTs && this.deps.slackPost && !this.deps.slackOff?.()) {
|
|
223
|
+
try {
|
|
224
|
+
const posted = await this.deps.slackPost({ agentSession: link.agentSession, target: link.channel, text: slackText, threadTs: link.threadTs });
|
|
225
|
+
slackTs = posted.messageTs;
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
this.log('warn', 'App comment reply could not be posted to the room\'s Slack thread', { appId: link.appId, error: err instanceof Error ? err.message : String(err) });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const mentions = await this.addressed(change, room);
|
|
232
|
+
const { message } = chat.recordTurn({
|
|
233
|
+
channelId: link.chatChannelId ?? room.chatChannelId,
|
|
234
|
+
senderType: 'user',
|
|
235
|
+
senderId: C.COMMENTS.ROOM_SENDER,
|
|
236
|
+
content: roomText,
|
|
237
|
+
threadId: link.chatRootId,
|
|
238
|
+
...(mentions.length ? { mentions } : {}),
|
|
239
|
+
metadata: {
|
|
240
|
+
source: C.COMMENTS.ROOM_SOURCE,
|
|
241
|
+
appComment: { appId: link.appId, commentId: link.commentId },
|
|
242
|
+
...(slackTs ? { slackChannelId: link.channel, slackTs } : {}),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
await this.dispatch(room, message, link.chatRootId ?? message.id, mentions);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Dispatch with the room rules: @mentioned members must answer; otherwise
|
|
249
|
+
* the members awake here read it and decide, and with nobody awake the
|
|
250
|
+
* room's lead is woken (the dispatcher's no-presence rule).
|
|
251
|
+
*/
|
|
252
|
+
async dispatch(room, message, threadId, mentions) {
|
|
253
|
+
const dispatch = this.deps.dispatch();
|
|
254
|
+
const chat = this.deps.chat();
|
|
255
|
+
const channel = chat?.getChannelForBridge(room.chatChannelId) ?? null;
|
|
256
|
+
if (!dispatch || !channel) {
|
|
257
|
+
this.log('warn', 'App comment written to the room but not dispatched (no dispatcher or room)', { room: room.label });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const awakeHere = room.members.filter((m) => this.deps.isRunning(m));
|
|
261
|
+
await dispatch(channel, { ...message, ...(mentions.length ? { mentions } : {}) }, {
|
|
262
|
+
threadId,
|
|
263
|
+
replyVia: 'reply-channel',
|
|
264
|
+
...(mentions.length === 0 && awakeHere.length > 0 ? { room: { awakeHere, awakeElsewhere: false } } : {}),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* An agent replied with app-comments: write it into the room thread as that
|
|
269
|
+
* agent (the room's Slack mirror posts it as the agent's bot).
|
|
270
|
+
*
|
|
271
|
+
* @returns True when the comment has a room thread (handled here)
|
|
272
|
+
*/
|
|
273
|
+
async agentReplied(appId, commentId, agent, text) {
|
|
274
|
+
await this.deps.links.load();
|
|
275
|
+
const link = this.deps.links.linkOf(appId, commentId);
|
|
276
|
+
if (!link?.room || !link.chatChannelId || !link.chatRootId)
|
|
277
|
+
return false;
|
|
278
|
+
this.agentRow(link, agent, text);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* An agent resolved / reopened with app-comments: one line in the room thread.
|
|
283
|
+
*
|
|
284
|
+
* @returns True when the comment has a room thread
|
|
285
|
+
*/
|
|
286
|
+
async statusChanged(appId, commentId, agent, action) {
|
|
287
|
+
await this.deps.links.load();
|
|
288
|
+
const link = this.deps.links.linkOf(appId, commentId);
|
|
289
|
+
if (!link?.room || !link.chatChannelId || !link.chatRootId)
|
|
290
|
+
return false;
|
|
291
|
+
this.agentRow(link, agent, action === 'resolve' ? '✅ Resolved in the app.' : '↩️ Reopened in the app.');
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
agentRow(link, agent, text) {
|
|
295
|
+
const chat = this.deps.chat();
|
|
296
|
+
if (!chat)
|
|
297
|
+
return;
|
|
298
|
+
try {
|
|
299
|
+
chat.recordTurn({
|
|
300
|
+
channelId: link.chatChannelId,
|
|
301
|
+
senderType: 'agent',
|
|
302
|
+
senderId: agent,
|
|
303
|
+
content: text,
|
|
304
|
+
threadId: link.chatRootId,
|
|
305
|
+
metadata: { source: C.COMMENTS.ROOM_SOURCE, appComment: { appId: link.appId, commentId: link.commentId } },
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
this.log('warn', 'Agent comment reply not shown in the room', { appId: link.appId, error: err instanceof Error ? err.message : String(err) });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* A chat-v2 row was written: an agent's reply in a room-owned comment
|
|
314
|
+
* thread (not one written here) is added to the app's comment as that agent.
|
|
315
|
+
*
|
|
316
|
+
* @param dto - The row
|
|
317
|
+
* @returns True when it was relayed
|
|
318
|
+
*/
|
|
319
|
+
async onChatMessage(dto) {
|
|
320
|
+
if (dto.senderType !== 'agent' || !dto.threadId || !this.deps.relayAgentReply)
|
|
321
|
+
return false;
|
|
322
|
+
if (dto.metadata?.source === C.COMMENTS.ROOM_SOURCE)
|
|
323
|
+
return false;
|
|
324
|
+
// A "working on it" note is not an answer for the app's thread.
|
|
325
|
+
if (isInterim(dto))
|
|
326
|
+
return false;
|
|
327
|
+
await this.deps.links.load();
|
|
328
|
+
const link = this.deps.links.linkOfChatRoot(dto.threadId);
|
|
329
|
+
if (!link?.room || link.chatChannelId !== dto.channelId)
|
|
330
|
+
return false;
|
|
331
|
+
const text = (dto.content ?? '').trim().slice(0, C.COMMENTS.MAX_BODY_CHARS);
|
|
332
|
+
if (!text)
|
|
333
|
+
return false;
|
|
334
|
+
try {
|
|
335
|
+
await this.deps.relayAgentReply(link.appId, link.commentId, dto.senderId, text);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
this.log('warn', 'Agent room reply could not be added to the app comment', { appId: link.appId, error: err instanceof Error ? err.message : String(err) });
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=app-comment-room.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-comment-room.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comment-room.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
* from the normal change feed. Slack-originated replies are never mirrored
|
|
16
16
|
* back to Slack.
|
|
17
17
|
*
|
|
18
|
+
* Rooms (crewly-services apps/SPEC.md §15): for an app owned by a team or a
|
|
19
|
+
* channel, {@link AppCommentRoomService} posts the comment into that room and
|
|
20
|
+
* its Slack channel and stores the link here too (`room` set). The owner's
|
|
21
|
+
* reply in such a Slack thread is relayed to Cloud the same way but NOT
|
|
22
|
+
* consumed: the room's members see it through the normal Slack room routing
|
|
23
|
+
* (room rules decide who answers), and the room service skips Cloud's
|
|
24
|
+
* `via: 'slack'` echo, so nobody gets it twice.
|
|
25
|
+
*
|
|
18
26
|
* Off switch: `CREWLY_APP_COMMENTS_SLACK=off`. A failed Slack post never
|
|
19
27
|
* affects comment delivery. Harness text is English; the comment text is the
|
|
20
28
|
* owner's words.
|
|
@@ -22,14 +30,27 @@
|
|
|
22
30
|
* @module services/apps/app-comments-slack.service
|
|
23
31
|
*/
|
|
24
32
|
import { type AppChange } from './app-wake-message.js';
|
|
25
|
-
|
|
33
|
+
import { type SlackAudioUpload, type VoiceFiles } from './app-comment-audio.service.js';
|
|
34
|
+
/** The team or channel a room-owned comment thread lives in. */
|
|
35
|
+
export interface CommentRoomRef {
|
|
36
|
+
kind: 'team' | 'channel';
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
/** One comment thread's Slack thread (and, for a room owner, its room thread). */
|
|
26
41
|
export interface CommentSlackLink {
|
|
27
42
|
appId: string;
|
|
28
43
|
commentId: string;
|
|
29
44
|
/** The agent whose bot posted the root (and posts the rest) */
|
|
30
45
|
agentSession: string;
|
|
46
|
+
/** Slack channel and root ts ('' when the room has no Slack channel) */
|
|
31
47
|
channel: string;
|
|
32
48
|
threadTs: string;
|
|
49
|
+
/** Set for a comment delivered to the room (team / channel) that owns the app */
|
|
50
|
+
room?: CommentRoomRef;
|
|
51
|
+
/** The room's chat-v2 channel (huddle) and the thread root row there */
|
|
52
|
+
chatChannelId?: string;
|
|
53
|
+
chatRootId?: string;
|
|
33
54
|
/** Reply ids already shown in Slack (so two recipients / a resend never double-post) */
|
|
34
55
|
replyIds: string[];
|
|
35
56
|
createdAt: string;
|
|
@@ -57,6 +78,8 @@ export interface CommentSlackDeps {
|
|
|
57
78
|
channelId: string;
|
|
58
79
|
messageTs: string;
|
|
59
80
|
}>;
|
|
81
|
+
/** Upload a voice comment recording into the thread as the agent's bot (SPEC §16); absent = a link line instead */
|
|
82
|
+
uploadAudio?: SlackAudioUpload;
|
|
60
83
|
/** The Slack DM channel id of an agent with the owner, when it has one */
|
|
61
84
|
dmChannelOf: (agentSession: string) => string | null;
|
|
62
85
|
/** The Slack channel id of the agent's team channel, when it has one */
|
|
@@ -83,6 +106,7 @@ export declare class AppCommentsSlackService {
|
|
|
83
106
|
private readonly file;
|
|
84
107
|
private links;
|
|
85
108
|
private byThread;
|
|
109
|
+
private byRoot;
|
|
86
110
|
private loaded;
|
|
87
111
|
private chain;
|
|
88
112
|
private readonly seenInbound;
|
|
@@ -92,6 +116,22 @@ export declare class AppCommentsSlackService {
|
|
|
92
116
|
/** Load the mapping once (call at boot so the inbound check can be synchronous). */
|
|
93
117
|
load(): Promise<void>;
|
|
94
118
|
private index;
|
|
119
|
+
/**
|
|
120
|
+
* Store a link made elsewhere (the room service), replacing any earlier one
|
|
121
|
+
* for the same comment.
|
|
122
|
+
*
|
|
123
|
+
* @param link - The link
|
|
124
|
+
*/
|
|
125
|
+
saveLink(link: CommentSlackLink): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Remember that a reply is shown in the thread (so a resend never doubles it).
|
|
128
|
+
*
|
|
129
|
+
* @param link - The link
|
|
130
|
+
* @param replyId - Cloud reply id
|
|
131
|
+
*/
|
|
132
|
+
noteReply(link: CommentSlackLink, replyId: string): Promise<void>;
|
|
133
|
+
/** The room-owned comment thread whose root is this chat-v2 row, if any. */
|
|
134
|
+
linkOfChatRoot(chatRootId: string): CommentSlackLink | null;
|
|
95
135
|
private persist;
|
|
96
136
|
/** The Slack thread of a comment, if one exists. */
|
|
97
137
|
linkOf(appId: string, commentId: string): CommentSlackLink | null;
|
|
@@ -105,8 +145,10 @@ export declare class AppCommentsSlackService {
|
|
|
105
145
|
* @param appName - App name
|
|
106
146
|
* @param changes - The batch's comment changes
|
|
107
147
|
*/
|
|
108
|
-
mirrorOwnerComments(session: string, appId: string, appName: string, changes: AppChange[]): Promise<void>;
|
|
148
|
+
mirrorOwnerComments(session: string, appId: string, appName: string, changes: AppChange[], voiceFiles?: VoiceFiles): Promise<void>;
|
|
109
149
|
private remember;
|
|
150
|
+
/** A change's voice recordings into the thread (upload as the bot, else a link line). Never throws. */
|
|
151
|
+
private voiceIn;
|
|
110
152
|
private openThread;
|
|
111
153
|
private postIn;
|
|
112
154
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-comments-slack.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comments-slack.service.ts"],"names":[],"mappings":"AAAA
|
|
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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"}
|