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.
Files changed (94) hide show
  1. package/config/skills/agent/core/app-comments/SKILL.md +25 -1
  2. package/config/skills/agent/core/app-comments/execute.sh +12 -3
  3. package/config/skills/agent/core/app-comments/execute.test.sh +17 -0
  4. package/config/skills/agent/core/app-data/SKILL.md +65 -3
  5. package/config/skills/agent/core/app-data/execute.sh +51 -3
  6. package/config/skills/agent/core/app-data/execute.test.sh +42 -0
  7. package/config/skills/agent/core/publish-app/SKILL.md +5 -1
  8. package/config/skills/agent/core/publish-app/execute.sh +26 -3
  9. package/config/skills/agent/core/publish-app/execute.test.sh +18 -0
  10. package/dist/backend/backend/src/constants.d.ts +36 -0
  11. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  12. package/dist/backend/backend/src/constants.js +44 -0
  13. package/dist/backend/backend/src/constants.js.map +1 -1
  14. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +24 -0
  15. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
  16. package/dist/backend/backend/src/controllers/apps/apps.controller.js +47 -1
  17. package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
  18. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +8 -0
  19. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/apps/apps.routes.js +31 -2
  21. package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
  22. package/dist/backend/backend/src/index.d.ts.map +1 -1
  23. package/dist/backend/backend/src/index.js +9 -0
  24. package/dist/backend/backend/src/index.js.map +1 -1
  25. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
  26. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  27. package/dist/backend/backend/src/services/apps/app-collaborators.service.d.ts +103 -0
  28. package/dist/backend/backend/src/services/apps/app-collaborators.service.d.ts.map +1 -0
  29. package/dist/backend/backend/src/services/apps/app-collaborators.service.js +161 -0
  30. package/dist/backend/backend/src/services/apps/app-collaborators.service.js.map +1 -0
  31. package/dist/backend/backend/src/services/apps/app-comment-audio.service.d.ts +164 -0
  32. package/dist/backend/backend/src/services/apps/app-comment-audio.service.d.ts.map +1 -0
  33. package/dist/backend/backend/src/services/apps/app-comment-audio.service.js +217 -0
  34. package/dist/backend/backend/src/services/apps/app-comment-audio.service.js.map +1 -0
  35. package/dist/backend/backend/src/services/apps/app-comment-room.service.d.ts +156 -0
  36. package/dist/backend/backend/src/services/apps/app-comment-room.service.d.ts.map +1 -0
  37. package/dist/backend/backend/src/services/apps/app-comment-room.service.js +344 -0
  38. package/dist/backend/backend/src/services/apps/app-comment-room.service.js.map +1 -0
  39. package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts +44 -2
  40. package/dist/backend/backend/src/services/apps/app-comments-slack.service.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/apps/app-comments-slack.service.js +90 -10
  42. package/dist/backend/backend/src/services/apps/app-comments-slack.service.js.map +1 -1
  43. package/dist/backend/backend/src/services/apps/app-roster.service.d.ts +37 -1
  44. package/dist/backend/backend/src/services/apps/app-roster.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/apps/app-roster.service.js +62 -7
  46. package/dist/backend/backend/src/services/apps/app-roster.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/apps/app-wake-message.d.ts +25 -1
  48. package/dist/backend/backend/src/services/apps/app-wake-message.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/apps/app-wake-message.js +25 -13
  50. package/dist/backend/backend/src/services/apps/app-wake-message.js.map +1 -1
  51. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts +34 -0
  52. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/apps/app-wake.service.js +97 -17
  54. package/dist/backend/backend/src/services/apps/app-wake.service.js.map +1 -1
  55. package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts +25 -0
  56. package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/apps/apps-cloud.client.js +41 -4
  58. package/dist/backend/backend/src/services/apps/apps-cloud.client.js.map +1 -1
  59. package/dist/backend/backend/src/services/apps/apps.service.d.ts +115 -2
  60. package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/apps/apps.service.js +181 -9
  62. package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
  63. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts +70 -1
  64. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
  65. package/dist/backend/backend/src/services/apps/apps.wiring.js +274 -5
  66. package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
  67. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +1 -1
  68. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +3 -0
  70. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  71. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +2 -0
  72. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/decisions/decision.service.js +1 -0
  74. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  75. package/dist/backend/backend/src/types/decision.types.d.ts +27 -2
  76. package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
  77. package/dist/backend/build-info.json +2 -2
  78. package/dist/cli/backend/src/constants.d.ts +36 -0
  79. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  80. package/dist/cli/backend/src/constants.js +44 -0
  81. package/dist/cli/backend/src/constants.js.map +1 -1
  82. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -1
  83. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  84. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +1 -1
  85. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  86. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +3 -0
  87. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  88. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +2 -0
  89. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  90. package/dist/cli/backend/src/services/decisions/decision.service.js +1 -0
  91. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  92. package/dist/cli/backend/src/types/decision.types.d.ts +27 -2
  93. package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
  94. package/package.json +1 -1
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Voice comments on Crewly Apps (crewly-services apps/SPEC.md §16).
3
+ *
4
+ * The owner records audio in the app's comment box; Cloud stores it and puts
5
+ * `attachments: [{ kind: 'audio', blobId, mime, durationMs, size }]` on the
6
+ * comment or reply. When such a comment is delivered — to an agent, to the
7
+ * room (team / channel) that owns the app, or mirrored to Slack — this module
8
+ * downloads the recording to `<CREWLY_HOME>/tmp/app-comment-audio/` (next to
9
+ * Slack's downloaded files) and builds the line that tells the agent where it
10
+ * is and to transcribe it with the transcribe-audio skill, the same way a
11
+ * Slack voice clip reaches an agent.
12
+ *
13
+ * A failed download never blocks delivery: the line then names the command
14
+ * that fetches it (`app-comments --audio <comment id>`).
15
+ *
16
+ * @module services/apps/app-comment-audio.service
17
+ */
18
+ import fs from 'fs/promises';
19
+ import path from 'path';
20
+ import { CREWLY_APPS_CONSTANTS } from '../../constants.js';
21
+ const C = CREWLY_APPS_CONSTANTS;
22
+ const V = C.VOICE;
23
+ const APP_ID_RE = /^[a-z0-9]{10}$/;
24
+ /**
25
+ * "0:42" / "2:05" for a length in milliseconds.
26
+ *
27
+ * @param ms - Milliseconds
28
+ * @returns m:ss
29
+ */
30
+ export function formatVoiceClock(ms) {
31
+ const total = Math.max(0, Math.floor((typeof ms === 'number' && Number.isFinite(ms) ? ms : 0) / 1000));
32
+ const m = Math.floor(total / 60);
33
+ const s = total % 60;
34
+ return `${m}:${s < 10 ? '0' : ''}${s}`;
35
+ }
36
+ /** Valid audio attachments of a list (untrusted input). */
37
+ function audioOf(list) {
38
+ if (!Array.isArray(list))
39
+ return [];
40
+ return list.filter((a) => !!a && typeof a === 'object' && a.kind === 'audio' && typeof a.blobId === 'string' && V.BLOB_ID_PATTERN.test(a.blobId));
41
+ }
42
+ /**
43
+ * The recordings a comment change is about: the comment's own for a new
44
+ * comment or a reopen, the reply's for a reply.
45
+ *
46
+ * @param change - A comment change (its `thread` as Cloud sent it)
47
+ * @returns Attachments with the comment id
48
+ */
49
+ export function voiceAttachmentsOf(change) {
50
+ const info = change.comment;
51
+ const thread = info?.thread;
52
+ if (!info?.id || !thread || !C.COMMENTS.ID_PATTERN.test(info.id))
53
+ return [];
54
+ const list = info.op === 'reply' ? (thread.replies ?? []).find((r) => r.id === info.replyId)?.attachments : info.op === 'add' || info.op === 'reopen' ? thread.attachments : undefined;
55
+ return audioOf(list).map((attachment) => ({ commentId: info.id, attachment }));
56
+ }
57
+ /**
58
+ * Every recording in a thread (the comment and all replies).
59
+ *
60
+ * @param thread - The thread
61
+ * @returns Attachments
62
+ */
63
+ export function threadVoiceAttachments(thread) {
64
+ if (!thread)
65
+ return [];
66
+ return [...audioOf(thread.attachments), ...(thread.replies ?? []).flatMap((r) => audioOf(r.attachments))];
67
+ }
68
+ /**
69
+ * The lines an agent reads for a comment change's recordings (indented under
70
+ * the comment). Downloaded: the path and the instruction to transcribe it
71
+ * first. Not downloaded: the reason and the command that fetches it.
72
+ *
73
+ * @param change - The comment change
74
+ * @param files - Recordings fetched for this delivery
75
+ * @param fetchCmd - `bash …/app-comments/execute.sh --app <id>` (the fallback command)
76
+ * @returns Lines (empty when the change has no recording)
77
+ */
78
+ export function voiceLines(change, files, fetchCmd) {
79
+ const out = [];
80
+ for (const { commentId, attachment } of voiceAttachmentsOf(change)) {
81
+ const f = files?.[attachment.blobId];
82
+ const clock = formatVoiceClock(f?.durationMs ?? attachment.durationMs);
83
+ if (f?.path) {
84
+ const json = JSON.stringify(f.path);
85
+ out.push(` Voice comment (${clock}): ${f.path} — transcribe it with the transcribe-audio skill before acting: {"audioFile":${json}}. ` +
86
+ 'If it answers "needsSetup": true, run install-skill --id transcribe-audio first; do not guess what it says.');
87
+ }
88
+ else {
89
+ out.push(` Voice comment (${clock}): not downloaded yet${f?.error ? ` (${f.error})` : ''}. Fetch it with: ${fetchCmd} --audio ${commentId} ` +
90
+ '— then transcribe it with the transcribe-audio skill before acting.');
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ /** Downloads voice comment recordings to this machine. */
96
+ export class AppCommentAudioService {
97
+ deps;
98
+ constructor(deps) {
99
+ this.deps = deps;
100
+ }
101
+ dir() {
102
+ return path.join(this.deps.homeDir(), V.DIR);
103
+ }
104
+ /**
105
+ * One recording, cached by app + blob id.
106
+ *
107
+ * @param appId - App
108
+ * @param commentId - Thread it is attached to
109
+ * @param a - The attachment
110
+ * @returns The local file (or why not)
111
+ */
112
+ async fetchOne(appId, commentId, a) {
113
+ const base = { blobId: a.blobId, commentId, durationMs: typeof a.durationMs === 'number' ? a.durationMs : 0, mime: typeof a.mime === 'string' ? a.mime : 'audio/webm' };
114
+ if (!APP_ID_RE.test(appId) || !C.COMMENTS.ID_PATTERN.test(commentId) || !V.BLOB_ID_PATTERN.test(a.blobId))
115
+ return { ...base, error: 'bad id' };
116
+ const ext = V.EXT[base.mime] ?? 'audio';
117
+ const file = path.join(this.dir(), `${appId}-${a.blobId}.${ext}`);
118
+ try {
119
+ const st = await fs.stat(file).catch(() => null);
120
+ if (st && st.size > 0)
121
+ return { ...base, path: file };
122
+ const { data } = await this.deps.client.download(`/apps/${appId}/comments/${commentId}/audio/${a.blobId}`, { maxBytes: V.MAX_BYTES, timeoutMs: V.DOWNLOAD_TIMEOUT_MS });
123
+ if (data.length === 0)
124
+ return { ...base, error: 'empty recording' };
125
+ await fs.mkdir(this.dir(), { recursive: true });
126
+ const tmp = `${file}.${process.pid}.tmp`;
127
+ await fs.writeFile(tmp, data, { mode: 0o600 });
128
+ await fs.rename(tmp, file);
129
+ this.deps.log?.('info', 'Voice comment downloaded', { appId, commentId, blobId: a.blobId, bytes: data.length });
130
+ return { ...base, path: file };
131
+ }
132
+ catch (err) {
133
+ const why = err instanceof Error ? err.message : String(err);
134
+ this.deps.log?.('warn', 'Voice comment could not be downloaded', { appId, commentId, blobId: a.blobId, error: why });
135
+ return { ...base, error: why.slice(0, 120) };
136
+ }
137
+ }
138
+ /**
139
+ * The recordings of a batch of comment changes (at most VOICE.MAX_PER_WAKE).
140
+ * Never throws.
141
+ *
142
+ * @param appId - App
143
+ * @param comments - Comment changes
144
+ * @returns Files by blob id (empty when none)
145
+ */
146
+ async forChanges(appId, comments) {
147
+ const out = {};
148
+ const wanted = comments.flatMap((c) => voiceAttachmentsOf(c)).slice(-V.MAX_PER_WAKE);
149
+ for (const { commentId, attachment } of wanted) {
150
+ if (out[attachment.blobId])
151
+ continue;
152
+ out[attachment.blobId] = await this.fetchOne(appId, commentId, attachment).catch((err) => ({
153
+ blobId: attachment.blobId,
154
+ commentId,
155
+ durationMs: attachment.durationMs ?? 0,
156
+ mime: attachment.mime ?? 'audio/webm',
157
+ error: err instanceof Error ? err.message : String(err),
158
+ }));
159
+ }
160
+ return out;
161
+ }
162
+ /**
163
+ * Every recording of one thread (`app-comments --audio <id>`).
164
+ *
165
+ * @param appId - App
166
+ * @param commentId - Thread
167
+ * @param thread - The thread as Cloud returned it
168
+ * @returns Files, oldest first
169
+ */
170
+ async forThread(appId, commentId, thread) {
171
+ const out = [];
172
+ for (const a of threadVoiceAttachments(thread))
173
+ out.push(await this.fetchOne(appId, commentId, a));
174
+ return out;
175
+ }
176
+ }
177
+ /**
178
+ * Show a comment change's recordings in its Slack thread: each file uploaded
179
+ * as the thread's bot when it is on this machine and an uploader is wired;
180
+ * otherwise (or when the upload fails) one line with a link to the app,
181
+ * where the owner can play it.
182
+ *
183
+ * @param input - The change, its downloaded files, the thread and the senders
184
+ * @returns How many recordings were uploaded
185
+ */
186
+ export async function mirrorVoiceToSlack(input) {
187
+ let uploaded = 0;
188
+ for (const { attachment } of voiceAttachmentsOf(input.change)) {
189
+ const f = input.files?.[attachment.blobId];
190
+ const clock = formatVoiceClock(f?.durationMs ?? attachment.durationMs);
191
+ if (f?.path && input.upload && input.agentSession) {
192
+ try {
193
+ await input.upload({
194
+ agentSession: input.agentSession,
195
+ channel: input.channel,
196
+ threadTs: input.threadTs,
197
+ filePath: f.path,
198
+ filename: path.basename(f.path),
199
+ title: `Voice comment (${clock})`,
200
+ });
201
+ uploaded++;
202
+ continue;
203
+ }
204
+ catch (err) {
205
+ input.log?.('warn', 'Voice comment could not be uploaded to Slack; posting a link instead', { error: err instanceof Error ? err.message : String(err) });
206
+ }
207
+ }
208
+ try {
209
+ await input.post(`🎤 Voice comment (${clock}) — listen in the app: <${input.appUrl}|Open app>`);
210
+ }
211
+ catch {
212
+ /* a mirror problem never fails delivery */
213
+ }
214
+ }
215
+ return uploaded;
216
+ }
217
+ //# sourceMappingURL=app-comment-audio.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-comment-audio.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comment-audio.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
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 type { ChatChannelDTO, ChatMessageDTO } from '../chat-v2/types.js';
31
+ import type { RecordTurnInput } from '../chat-v2/chat-v2.service.js';
32
+ import type { DispatchMessageOptions } from '../chat-v2/chat-v2.dispatcher.service.js';
33
+ import { type AppChange } from './app-wake-message.js';
34
+ import type { AppCommentsSlackService, CommentRoomRef } from './app-comments-slack.service.js';
35
+ import { type SlackAudioUpload, type VoiceFiles } from './app-comment-audio.service.js';
36
+ /** Where a room-owned comment goes on this machine. */
37
+ export interface ResolvedRoom {
38
+ /** chat-v2 huddle id of the room */
39
+ chatChannelId: string;
40
+ /** Readable name (`#daily-brief`, `Dev team`) */
41
+ label: string;
42
+ /** The room's Slack channel, when it has one and Slack is connected */
43
+ slackChannelId: string | null;
44
+ /** Member agent sessions */
45
+ members: string[];
46
+ }
47
+ /** A room-owned batch of the owner's comment changes. */
48
+ export interface RoomDelivery {
49
+ appId: string;
50
+ appName: string;
51
+ room: CommentRoomRef;
52
+ comments: AppChange[];
53
+ /** Voice comment recordings downloaded for this batch, by blob id (SPEC §16) */
54
+ voiceFiles?: VoiceFiles;
55
+ }
56
+ /** The chat-v2 slice used here. */
57
+ export interface RoomChatApi {
58
+ recordTurn(input: RecordTurnInput): {
59
+ message: ChatMessageDTO;
60
+ };
61
+ getChannelForBridge(channelId: string): ChatChannelDTO | null;
62
+ }
63
+ /** Collaborators (all injectable). */
64
+ export interface AppCommentRoomDeps {
65
+ /** Find the room on this machine (null: it is gone) */
66
+ resolveRoom: (room: CommentRoomRef) => Promise<ResolvedRoom | null>;
67
+ chat: () => RoomChatApi | null;
68
+ /** The chat-v2 dispatcher's `dispatchMessage` (null before it is wired) */
69
+ dispatch: () => ((channel: ChatChannelDTO, message: ChatMessageDTO, options?: DispatchMessageOptions) => Promise<unknown>) | null;
70
+ /** Whether an agent's session runs now (who is awake in the room) */
71
+ isRunning: (session: string) => boolean;
72
+ /** Upload a voice comment recording into the room's Slack thread as an agent's bot (SPEC §16) */
73
+ slackUploadAudio?: SlackAudioUpload;
74
+ /** Post into Slack as an agent's bot */
75
+ slackPost?: (req: {
76
+ agentSession: string;
77
+ target: string;
78
+ text: string;
79
+ threadTs?: string;
80
+ }) => Promise<{
81
+ channelId: string;
82
+ messageTs: string;
83
+ }>;
84
+ /** The link store shared with the Slack mirror */
85
+ links: Pick<AppCommentsSlackService, 'load' | 'linkOf' | 'saveLink' | 'noteReply' | 'linkOfChatRoot'>;
86
+ /** The agent that publishes an app here (preferred poster in Slack) */
87
+ publisherOf?: (appId: string) => Promise<string | null>;
88
+ /** This instance's Cloud id (to tell its mentions from another machine's) */
89
+ instanceId?: () => Promise<string | null>;
90
+ /** Add an agent's room reply to the app comment, as that agent */
91
+ relayAgentReply?: (appId: string, commentId: string, agentSession: string, text: string) => Promise<void>;
92
+ /** Agent skills root, for the command named in the room message */
93
+ skillsPath?: string;
94
+ /** Where the app opens */
95
+ appUrl?: (appId: string) => string;
96
+ /** Off switch for the Slack side (`CREWLY_APP_COMMENTS_SLACK=off`) */
97
+ slackOff?: () => boolean;
98
+ log?: (level: 'info' | 'warn', msg: string, meta?: Record<string, unknown>) => void;
99
+ }
100
+ /** Posts room-owned app comments into their room and relays the room's answers back. */
101
+ export declare class AppCommentRoomService {
102
+ private readonly deps;
103
+ constructor(deps: AppCommentRoomDeps);
104
+ private log;
105
+ private url;
106
+ /**
107
+ * Post a batch of the owner's comment changes into the owning room.
108
+ *
109
+ * @param input - App, room and comment changes (oldest first)
110
+ * @returns True when every change was posted (false: retry the batch later)
111
+ */
112
+ deliver(input: RoomDelivery): Promise<boolean>;
113
+ /** The change's voice recordings into the room's Slack thread (upload, else a link line). Never throws. */
114
+ private voiceToSlack;
115
+ /** Which member's bot posts in Slack: the publisher when it is in the room, else the first member. */
116
+ private poster;
117
+ /**
118
+ * Members this change @mentions on this machine (they are addressed in the room).
119
+ *
120
+ * @param change - Comment change
121
+ * @param room - The room
122
+ * @returns Member sessions
123
+ */
124
+ private addressed;
125
+ private openThread;
126
+ private postInThread;
127
+ /**
128
+ * Dispatch with the room rules: @mentioned members must answer; otherwise
129
+ * the members awake here read it and decide, and with nobody awake the
130
+ * room's lead is woken (the dispatcher's no-presence rule).
131
+ */
132
+ private dispatch;
133
+ /**
134
+ * An agent replied with app-comments: write it into the room thread as that
135
+ * agent (the room's Slack mirror posts it as the agent's bot).
136
+ *
137
+ * @returns True when the comment has a room thread (handled here)
138
+ */
139
+ agentReplied(appId: string, commentId: string, agent: string, text: string): Promise<boolean>;
140
+ /**
141
+ * An agent resolved / reopened with app-comments: one line in the room thread.
142
+ *
143
+ * @returns True when the comment has a room thread
144
+ */
145
+ statusChanged(appId: string, commentId: string, agent: string, action: 'resolve' | 'reopen'): Promise<boolean>;
146
+ private agentRow;
147
+ /**
148
+ * A chat-v2 row was written: an agent's reply in a room-owned comment
149
+ * thread (not one written here) is added to the app's comment as that agent.
150
+ *
151
+ * @param dto - The row
152
+ * @returns True when it was relayed
153
+ */
154
+ onChatMessage(dto: ChatMessageDTO): Promise<boolean>;
155
+ }
156
+ //# sourceMappingURL=app-comment-room.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-comment-room.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/app-comment-room.service.ts"],"names":[],"mappings":"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"}