@letta-ai/letta-code 0.30.24 → 0.30.26
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/dist/agent-presets.js +1 -339
- package/dist/agent-presets.js.map +2 -2
- package/dist/channels-slack.js +319 -41
- package/dist/channels-slack.js.map +4 -3
- package/dist/channels-telegram.js +424 -0
- package/dist/channels-telegram.js.map +14 -0
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/client-skills-watcher.d.ts +23 -0
- package/dist/types/agent/client-skills-watcher.d.ts.map +1 -0
- package/dist/types/agent/client-skills.d.ts.map +1 -1
- package/dist/types/agent/modify.d.ts.map +1 -1
- package/dist/types/backend/dev/pi-model-factory.d.ts.map +1 -1
- package/dist/types/channels/slack/attachment-primitives.d.ts +69 -0
- package/dist/types/channels/slack/attachment-primitives.d.ts.map +1 -0
- package/dist/types/channels/telegram/debounce.d.ts +22 -0
- package/dist/types/channels/telegram/debounce.d.ts.map +1 -0
- package/dist/types/channels/telegram/ingress.d.ts +35 -0
- package/dist/types/channels/telegram/ingress.d.ts.map +1 -0
- package/dist/types/channels/telegram/message-action-contract.d.ts +16 -0
- package/dist/types/channels/telegram/message-action-contract.d.ts.map +1 -0
- package/dist/types/channels/telegram/message-shapes.d.ts +154 -0
- package/dist/types/channels/telegram/message-shapes.d.ts.map +1 -0
- package/dist/types/channels/telegram/outbound.d.ts +21 -0
- package/dist/types/channels/telegram/outbound.d.ts.map +1 -0
- package/dist/types/channels-slack.d.ts +2 -0
- package/dist/types/channels-slack.d.ts.map +1 -1
- package/dist/types/channels-telegram.d.ts +20 -0
- package/dist/types/channels-telegram.d.ts.map +1 -0
- package/dist/types/cli/helpers/stream-debug.d.ts +6 -0
- package/dist/types/cli/helpers/stream-debug.d.ts.map +1 -0
- package/dist/types/cli/helpers/stream-stall-reconciler.d.ts +16 -0
- package/dist/types/cli/helpers/stream-stall-reconciler.d.ts.map +1 -0
- package/dist/types/cli/helpers/stream.d.ts +1 -0
- package/dist/types/cli/helpers/stream.d.ts.map +1 -1
- package/dist/types/tools/impl/bash.d.ts.map +1 -1
- package/dist/types/tools/impl/exec-command.d.ts +1 -0
- package/dist/types/tools/impl/exec-command.d.ts.map +1 -1
- package/dist/types/tools/impl/github-pull-request-tracker.d.ts +2 -0
- package/dist/types/tools/impl/github-pull-request-tracker.d.ts.map +1 -1
- package/dist/types/tools/impl/monitor.d.ts.map +1 -1
- package/dist/types/tools/impl/process_manager.d.ts +7 -1
- package/dist/types/tools/impl/process_manager.d.ts.map +1 -1
- package/dist/types/tools/impl/task.d.ts +2 -0
- package/dist/types/tools/impl/task.d.ts.map +1 -1
- package/dist/types/utils/openai-reasoning-effort.d.ts +6 -0
- package/dist/types/utils/openai-reasoning-effort.d.ts.map +1 -0
- package/dist/types/websocket/listener/constants.d.ts +0 -2
- package/dist/types/websocket/listener/constants.d.ts.map +1 -1
- package/dist/types/websocket/listener/types.d.ts +3 -0
- package/dist/types/websocket/listener/types.d.ts.map +1 -1
- package/letta.js +2741 -2642
- package/package.json +12 -1
- package/scripts/source-file-size-baseline.json +6 -7
- package/skills/teleporting-between-environments/SKILL.md +23 -9
package/dist/channels-slack.js
CHANGED
|
@@ -1,17 +1,291 @@
|
|
|
1
|
+
// src/channels/slack/attachment-primitives.ts
|
|
2
|
+
var MAX_SLACK_ATTACHMENTS = 8;
|
|
3
|
+
var ALLOWED_SLACK_HOST_SUFFIXES = [
|
|
4
|
+
"slack.com",
|
|
5
|
+
"slack-edge.com",
|
|
6
|
+
"slack-files.com"
|
|
7
|
+
];
|
|
8
|
+
function asRecord(value) {
|
|
9
|
+
return value && typeof value === "object" ? value : null;
|
|
10
|
+
}
|
|
11
|
+
function isNonEmptyString(value) {
|
|
12
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
13
|
+
}
|
|
14
|
+
function normalizeSlackFileLike(value) {
|
|
15
|
+
const record = asRecord(value);
|
|
16
|
+
if (!record)
|
|
17
|
+
return null;
|
|
18
|
+
return {
|
|
19
|
+
id: isNonEmptyString(record.id) ? record.id : undefined,
|
|
20
|
+
name: isNonEmptyString(record.name) ? record.name : undefined,
|
|
21
|
+
mimetype: isNonEmptyString(record.mimetype) ? record.mimetype : undefined,
|
|
22
|
+
size: typeof record.size === "number" ? record.size : undefined,
|
|
23
|
+
url_private: isNonEmptyString(record.url_private) ? record.url_private : undefined,
|
|
24
|
+
url_private_download: isNonEmptyString(record.url_private_download) ? record.url_private_download : undefined
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function normalizeSlackAttachmentLike(value) {
|
|
28
|
+
const record = asRecord(value);
|
|
29
|
+
if (!record)
|
|
30
|
+
return null;
|
|
31
|
+
const files = Array.isArray(record.files) ? record.files.map((entry) => normalizeSlackFileLike(entry)).filter((entry) => Boolean(entry)) : undefined;
|
|
32
|
+
return {
|
|
33
|
+
image_url: isNonEmptyString(record.image_url) ? record.image_url : undefined,
|
|
34
|
+
files
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function collectSlackFiles(rawEvent) {
|
|
38
|
+
const record = asRecord(rawEvent);
|
|
39
|
+
if (!record)
|
|
40
|
+
return [];
|
|
41
|
+
const deduped = new Map;
|
|
42
|
+
const push = (file) => {
|
|
43
|
+
if (!file)
|
|
44
|
+
return;
|
|
45
|
+
const key = file.id ?? file.url_private_download ?? file.url_private ?? `${file.name ?? "attachment"}:${file.mimetype ?? ""}`;
|
|
46
|
+
deduped.set(key, file);
|
|
47
|
+
};
|
|
48
|
+
if (Array.isArray(record.files)) {
|
|
49
|
+
for (const entry of record.files)
|
|
50
|
+
push(normalizeSlackFileLike(entry));
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(record.attachments)) {
|
|
53
|
+
record.attachments.map((entry) => normalizeSlackAttachmentLike(entry)).filter((entry) => Boolean(entry)).forEach((attachment, index) => {
|
|
54
|
+
for (const file of attachment.files ?? [])
|
|
55
|
+
push(file);
|
|
56
|
+
if (attachment.image_url) {
|
|
57
|
+
push({
|
|
58
|
+
id: `attachment-image-${index}`,
|
|
59
|
+
name: `attachment-image-${index}.png`,
|
|
60
|
+
url_private: attachment.image_url
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return Array.from(deduped.values()).slice(0, MAX_SLACK_ATTACHMENTS);
|
|
66
|
+
}
|
|
67
|
+
function nextCursor(page) {
|
|
68
|
+
const value = page.response_metadata?.next_cursor;
|
|
69
|
+
return isNonEmptyString(value) ? value.trim() : undefined;
|
|
70
|
+
}
|
|
71
|
+
async function resolveSlackMessageFiles(params) {
|
|
72
|
+
if (isNonEmptyString(params.threadTs)) {
|
|
73
|
+
let cursor;
|
|
74
|
+
do {
|
|
75
|
+
const page2 = await params.client.conversations.replies({
|
|
76
|
+
channel: params.channelId,
|
|
77
|
+
ts: params.threadTs,
|
|
78
|
+
limit: 200,
|
|
79
|
+
inclusive: true,
|
|
80
|
+
...cursor ? { cursor } : {}
|
|
81
|
+
});
|
|
82
|
+
const message2 = (page2.messages ?? []).find((entry) => entry.ts === params.messageTs);
|
|
83
|
+
if (message2)
|
|
84
|
+
return collectSlackFiles(message2);
|
|
85
|
+
cursor = nextCursor(page2);
|
|
86
|
+
} while (cursor);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const page = await params.client.conversations.history({
|
|
90
|
+
channel: params.channelId,
|
|
91
|
+
oldest: params.messageTs,
|
|
92
|
+
latest: params.messageTs,
|
|
93
|
+
inclusive: true,
|
|
94
|
+
limit: 1
|
|
95
|
+
});
|
|
96
|
+
const message = (page.messages ?? []).find((entry) => entry.ts === params.messageTs);
|
|
97
|
+
return message ? collectSlackFiles(message) : null;
|
|
98
|
+
}
|
|
99
|
+
function isAllowedSlackHostname(hostname) {
|
|
100
|
+
const normalized = hostname.trim().toLowerCase();
|
|
101
|
+
return ALLOWED_SLACK_HOST_SUFFIXES.some((suffix) => normalized === suffix || normalized.endsWith(`.${suffix}`));
|
|
102
|
+
}
|
|
103
|
+
function assertSlackFileUrl(rawUrl) {
|
|
104
|
+
const parsed = new URL(rawUrl);
|
|
105
|
+
if (parsed.protocol !== "https:") {
|
|
106
|
+
throw new Error(`Unsupported Slack file protocol: ${parsed.protocol}`);
|
|
107
|
+
}
|
|
108
|
+
if (!isAllowedSlackHostname(parsed.hostname)) {
|
|
109
|
+
throw new Error(`Refusing non-Slack attachment host: ${parsed.hostname}`);
|
|
110
|
+
}
|
|
111
|
+
return parsed;
|
|
112
|
+
}
|
|
113
|
+
function extensionForMimeType(mimeType) {
|
|
114
|
+
switch (mimeType?.toLowerCase()) {
|
|
115
|
+
case "image/png":
|
|
116
|
+
return ".png";
|
|
117
|
+
case "image/jpeg":
|
|
118
|
+
return ".jpg";
|
|
119
|
+
case "image/gif":
|
|
120
|
+
return ".gif";
|
|
121
|
+
case "image/webp":
|
|
122
|
+
return ".webp";
|
|
123
|
+
case "audio/aac":
|
|
124
|
+
return ".aac";
|
|
125
|
+
case "audio/m4a":
|
|
126
|
+
case "audio/mp4":
|
|
127
|
+
case "audio/x-m4a":
|
|
128
|
+
return ".m4a";
|
|
129
|
+
case "audio/mpeg":
|
|
130
|
+
return ".mp3";
|
|
131
|
+
case "audio/ogg":
|
|
132
|
+
return ".ogg";
|
|
133
|
+
case "audio/wav":
|
|
134
|
+
case "audio/x-wav":
|
|
135
|
+
return ".wav";
|
|
136
|
+
case "audio/webm":
|
|
137
|
+
return ".webm";
|
|
138
|
+
case "application/pdf":
|
|
139
|
+
return ".pdf";
|
|
140
|
+
case "text/plain":
|
|
141
|
+
return ".txt";
|
|
142
|
+
default:
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function pathBaseName(pathname) {
|
|
147
|
+
const trimmed = pathname.replace(/\/+$/, "");
|
|
148
|
+
const separator = trimmed.lastIndexOf("/");
|
|
149
|
+
return trimmed.slice(separator + 1);
|
|
150
|
+
}
|
|
151
|
+
function extensionName(name) {
|
|
152
|
+
const base = pathBaseName(name);
|
|
153
|
+
if (base === "..")
|
|
154
|
+
return "";
|
|
155
|
+
const index = base.lastIndexOf(".");
|
|
156
|
+
return index > 0 ? base.slice(index) : "";
|
|
157
|
+
}
|
|
158
|
+
function resolveMimeType(name, fallback) {
|
|
159
|
+
if (fallback)
|
|
160
|
+
return fallback;
|
|
161
|
+
switch (extensionName(name).toLowerCase()) {
|
|
162
|
+
case ".png":
|
|
163
|
+
return "image/png";
|
|
164
|
+
case ".jpg":
|
|
165
|
+
case ".jpeg":
|
|
166
|
+
return "image/jpeg";
|
|
167
|
+
case ".gif":
|
|
168
|
+
return "image/gif";
|
|
169
|
+
case ".webp":
|
|
170
|
+
return "image/webp";
|
|
171
|
+
case ".aac":
|
|
172
|
+
return "audio/aac";
|
|
173
|
+
case ".m4a":
|
|
174
|
+
return "audio/mp4";
|
|
175
|
+
case ".mp3":
|
|
176
|
+
return "audio/mpeg";
|
|
177
|
+
case ".oga":
|
|
178
|
+
case ".ogg":
|
|
179
|
+
case ".opus":
|
|
180
|
+
return "audio/ogg";
|
|
181
|
+
case ".wav":
|
|
182
|
+
return "audio/wav";
|
|
183
|
+
case ".webm":
|
|
184
|
+
return "audio/webm";
|
|
185
|
+
case ".pdf":
|
|
186
|
+
return "application/pdf";
|
|
187
|
+
case ".txt":
|
|
188
|
+
case ".md":
|
|
189
|
+
return "text/plain";
|
|
190
|
+
default:
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function isGenericSlackMimeType(mimeType) {
|
|
195
|
+
const normalized = mimeType?.trim().toLowerCase();
|
|
196
|
+
return normalized === "application/octet-stream" || normalized === "binary/octet-stream";
|
|
197
|
+
}
|
|
198
|
+
function resolveSlackFileName(params) {
|
|
199
|
+
const hintedName = params.file.name ?? (params.url ? pathBaseName(new URL(params.url).pathname) : undefined) ?? `${params.file.id ?? "attachment"}${extensionForMimeType(params.file.mimetype)}`;
|
|
200
|
+
return extensionName(hintedName) || !params.mimeType ? hintedName : `${hintedName}${extensionForMimeType(params.mimeType)}`;
|
|
201
|
+
}
|
|
202
|
+
function resolveSlackFileMimeType(params) {
|
|
203
|
+
const preferredMimeType = params.responseMimeType && !isGenericSlackMimeType(params.responseMimeType) ? params.responseMimeType : params.file.mimetype && !isGenericSlackMimeType(params.file.mimetype) ? params.file.mimetype : undefined;
|
|
204
|
+
return resolveMimeType(params.fileName, preferredMimeType);
|
|
205
|
+
}
|
|
206
|
+
function resolveSlackFileMetadata(params) {
|
|
207
|
+
const hintedName = resolveSlackFileName({
|
|
208
|
+
file: params.file,
|
|
209
|
+
url: params.url
|
|
210
|
+
});
|
|
211
|
+
const mimeType = resolveSlackFileMimeType({
|
|
212
|
+
file: params.file,
|
|
213
|
+
fileName: hintedName,
|
|
214
|
+
responseMimeType: params.responseMimeType
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
fileName: resolveSlackFileName({
|
|
218
|
+
file: params.file,
|
|
219
|
+
url: params.url,
|
|
220
|
+
mimeType
|
|
221
|
+
}),
|
|
222
|
+
mimeType
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function parseContentLength(response) {
|
|
226
|
+
const header = response.headers.get("content-length");
|
|
227
|
+
if (!header)
|
|
228
|
+
return;
|
|
229
|
+
const value = Number(header);
|
|
230
|
+
return Number.isFinite(value) ? value : undefined;
|
|
231
|
+
}
|
|
232
|
+
async function fetchSlackFile(params) {
|
|
233
|
+
const rawUrl = params.file.url_private_download ?? params.file.url_private;
|
|
234
|
+
if (!rawUrl) {
|
|
235
|
+
throw new Error("Slack attachment does not include a private download URL.");
|
|
236
|
+
}
|
|
237
|
+
const parsed = assertSlackFileUrl(rawUrl);
|
|
238
|
+
const fetcher = params.fetcher ?? globalThis.fetch;
|
|
239
|
+
const authHeaders = { Authorization: `Bearer ${params.token}` };
|
|
240
|
+
const initial = await fetcher(parsed.href, {
|
|
241
|
+
headers: authHeaders,
|
|
242
|
+
redirect: "manual",
|
|
243
|
+
...params.signal ? { signal: params.signal } : {}
|
|
244
|
+
});
|
|
245
|
+
let response = initial;
|
|
246
|
+
if (initial.status >= 300 && initial.status < 400) {
|
|
247
|
+
const location = initial.headers.get("location");
|
|
248
|
+
if (location) {
|
|
249
|
+
const resolved = new URL(location, parsed.href);
|
|
250
|
+
response = await fetcher(resolved.href, {
|
|
251
|
+
...resolved.origin === parsed.origin ? { headers: authHeaders } : {},
|
|
252
|
+
redirect: "follow",
|
|
253
|
+
...params.signal ? { signal: params.signal } : {}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!response.ok) {
|
|
258
|
+
throw new Error(`Slack attachment fetch failed with HTTP ${response.status}.`);
|
|
259
|
+
}
|
|
260
|
+
if (!response.body) {
|
|
261
|
+
throw new Error("Slack attachment response did not include a body.");
|
|
262
|
+
}
|
|
263
|
+
const responseMimeType = response.headers.get("content-type")?.split(";")[0]?.trim() || undefined;
|
|
264
|
+
const metadata = resolveSlackFileMetadata({
|
|
265
|
+
file: params.file,
|
|
266
|
+
url: rawUrl,
|
|
267
|
+
responseMimeType
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
body: response.body,
|
|
271
|
+
...metadata,
|
|
272
|
+
contentLength: parseContentLength(response)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
1
275
|
// src/channels/slack/bot-policy.ts
|
|
2
276
|
function resolveSlackAllowBotsMode(value) {
|
|
3
277
|
if (value === "mentions")
|
|
4
278
|
return "mentions";
|
|
5
279
|
return "off";
|
|
6
280
|
}
|
|
7
|
-
function
|
|
281
|
+
function isNonEmptyString2(value) {
|
|
8
282
|
return typeof value === "string" && value.length > 0;
|
|
9
283
|
}
|
|
10
284
|
function isSlackBotAuthoredInboundMessage(message) {
|
|
11
|
-
return
|
|
285
|
+
return isNonEmptyString2(message.bot_id) || message.subtype === "bot_message";
|
|
12
286
|
}
|
|
13
287
|
function isOwnSlackBotInboundMessage(params) {
|
|
14
|
-
return
|
|
288
|
+
return isNonEmptyString2(params.botUserId) && params.message.user === params.botUserId || isNonEmptyString2(params.botId) && params.message.bot_id === params.botId;
|
|
15
289
|
}
|
|
16
290
|
function shouldAcceptSlackInboundBotMessage(params) {
|
|
17
291
|
if (isOwnSlackBotInboundMessage({
|
|
@@ -28,11 +302,11 @@ function shouldAcceptSlackInboundBotMessage(params) {
|
|
|
28
302
|
return mode === "mentions" && params.wasMentioned;
|
|
29
303
|
}
|
|
30
304
|
// src/channels/slack/public-utils.ts
|
|
31
|
-
function
|
|
305
|
+
function isNonEmptyString3(value) {
|
|
32
306
|
return typeof value === "string" && value.length > 0;
|
|
33
307
|
}
|
|
34
308
|
function firstNonEmptyString(...values) {
|
|
35
|
-
return values.find(
|
|
309
|
+
return values.find(isNonEmptyString3);
|
|
36
310
|
}
|
|
37
311
|
function resolveSlackChatType(chatId) {
|
|
38
312
|
return chatId.startsWith("D") ? "direct" : "channel";
|
|
@@ -189,13 +463,13 @@ function hasRecordValue(value) {
|
|
|
189
463
|
return value !== null && typeof value === "object";
|
|
190
464
|
}
|
|
191
465
|
function hasSlackMention(text, userId) {
|
|
192
|
-
return
|
|
466
|
+
return isNonEmptyString3(text) && isNonEmptyString3(userId) && (text.includes(`<@${userId}>`) || text.includes(`<@${userId}|`));
|
|
193
467
|
}
|
|
194
468
|
function isBotAuthoredMessage(message) {
|
|
195
|
-
return
|
|
469
|
+
return isNonEmptyString3(message.bot_id) || message.subtype === "bot_message";
|
|
196
470
|
}
|
|
197
471
|
function resolveMessageSubtypeIgnoreReason(message) {
|
|
198
|
-
const subtype =
|
|
472
|
+
const subtype = isNonEmptyString3(message.subtype) ? message.subtype : null;
|
|
199
473
|
if (!subtype) {
|
|
200
474
|
return null;
|
|
201
475
|
}
|
|
@@ -215,14 +489,14 @@ function shouldSkipSlackMessageByLastSeen(params) {
|
|
|
215
489
|
}
|
|
216
490
|
function resolveSlackMessageIngressPolicy(params) {
|
|
217
491
|
const { message } = params;
|
|
218
|
-
if (!
|
|
492
|
+
if (!isNonEmptyString3(message.channel)) {
|
|
219
493
|
return { shouldRoute: false, reason: "missing_channel" };
|
|
220
494
|
}
|
|
221
495
|
const senderId = firstNonEmptyString(message.user, message.bot_id);
|
|
222
496
|
if (!senderId) {
|
|
223
497
|
return { shouldRoute: false, reason: "missing_sender" };
|
|
224
498
|
}
|
|
225
|
-
if (!
|
|
499
|
+
if (!isNonEmptyString3(message.ts)) {
|
|
226
500
|
return { shouldRoute: false, reason: "missing_timestamp" };
|
|
227
501
|
}
|
|
228
502
|
if (message.hidden === true) {
|
|
@@ -234,10 +508,10 @@ function resolveSlackMessageIngressPolicy(params) {
|
|
|
234
508
|
}
|
|
235
509
|
const chatType = resolveSlackChatType(message.channel);
|
|
236
510
|
const threadId = chatType === "direct" ? firstNonEmptyString(message.thread_ts) ?? null : firstNonEmptyString(message.thread_ts, message.ts) ?? null;
|
|
237
|
-
if (chatType === "channel" && !
|
|
511
|
+
if (chatType === "channel" && !isNonEmptyString3(message.thread_ts)) {
|
|
238
512
|
return { shouldRoute: false, reason: "top_level_channel_message" };
|
|
239
513
|
}
|
|
240
|
-
const rawText =
|
|
514
|
+
const rawText = isNonEmptyString3(message.text) ? message.text : "";
|
|
241
515
|
const wasMentioned = hasSlackMention(rawText, params.botUserId);
|
|
242
516
|
if (chatType === "channel" && wasMentioned && params.appMentionEventWillHandleMentions === true) {
|
|
243
517
|
return { shouldRoute: false, reason: "handled_by_app_mention" };
|
|
@@ -248,8 +522,8 @@ function resolveSlackMessageIngressPolicy(params) {
|
|
|
248
522
|
shouldRoute: true,
|
|
249
523
|
channelId: message.channel,
|
|
250
524
|
senderId,
|
|
251
|
-
...
|
|
252
|
-
...
|
|
525
|
+
...isNonEmptyString3(message.user) ? { senderUserId: message.user } : {},
|
|
526
|
+
...isNonEmptyString3(message.bot_id) ? { senderBotId: message.bot_id } : {},
|
|
253
527
|
messageId: message.ts,
|
|
254
528
|
threadId,
|
|
255
529
|
chatType,
|
|
@@ -263,23 +537,23 @@ function resolveSlackMessageIngressPolicy(params) {
|
|
|
263
537
|
}
|
|
264
538
|
function resolveSlackAppMentionIngressPolicy(params) {
|
|
265
539
|
const { event } = params;
|
|
266
|
-
if (!
|
|
540
|
+
if (!isNonEmptyString3(event.channel)) {
|
|
267
541
|
return { shouldRoute: false, reason: "missing_channel" };
|
|
268
542
|
}
|
|
269
543
|
const senderId = firstNonEmptyString(event.user, event.bot_id);
|
|
270
544
|
if (!senderId) {
|
|
271
545
|
return { shouldRoute: false, reason: "missing_sender" };
|
|
272
546
|
}
|
|
273
|
-
if (!
|
|
547
|
+
if (!isNonEmptyString3(event.ts)) {
|
|
274
548
|
return { shouldRoute: false, reason: "missing_timestamp" };
|
|
275
549
|
}
|
|
276
|
-
const rawText =
|
|
550
|
+
const rawText = isNonEmptyString3(event.text) ? event.text : "";
|
|
277
551
|
return {
|
|
278
552
|
shouldRoute: true,
|
|
279
553
|
channelId: event.channel,
|
|
280
554
|
senderId,
|
|
281
|
-
...
|
|
282
|
-
...
|
|
555
|
+
...isNonEmptyString3(event.user) ? { senderUserId: event.user } : {},
|
|
556
|
+
...isNonEmptyString3(event.bot_id) ? { senderBotId: event.bot_id } : {},
|
|
283
557
|
messageId: event.ts,
|
|
284
558
|
threadId: firstNonEmptyString(event.thread_ts, event.ts) ?? event.ts,
|
|
285
559
|
chatType: "channel",
|
|
@@ -296,16 +570,16 @@ function resolveSlackReactionIngressPolicy(params) {
|
|
|
296
570
|
if (item?.type !== "message") {
|
|
297
571
|
return { shouldRoute: false, reason: "invalid_reaction_item" };
|
|
298
572
|
}
|
|
299
|
-
if (!
|
|
573
|
+
if (!isNonEmptyString3(item.channel)) {
|
|
300
574
|
return { shouldRoute: false, reason: "missing_channel" };
|
|
301
575
|
}
|
|
302
|
-
if (!
|
|
576
|
+
if (!isNonEmptyString3(params.event.user)) {
|
|
303
577
|
return { shouldRoute: false, reason: "missing_sender" };
|
|
304
578
|
}
|
|
305
|
-
if (!
|
|
579
|
+
if (!isNonEmptyString3(item.ts)) {
|
|
306
580
|
return { shouldRoute: false, reason: "missing_timestamp" };
|
|
307
581
|
}
|
|
308
|
-
if (!
|
|
582
|
+
if (!isNonEmptyString3(params.event.reaction)) {
|
|
309
583
|
return { shouldRoute: false, reason: "missing_reaction" };
|
|
310
584
|
}
|
|
311
585
|
if (params.event.user === params.botUserId) {
|
|
@@ -322,7 +596,7 @@ function resolveSlackReactionIngressPolicy(params) {
|
|
|
322
596
|
action: params.action,
|
|
323
597
|
emoji: params.event.reaction,
|
|
324
598
|
targetMessageId,
|
|
325
|
-
...
|
|
599
|
+
...isNonEmptyString3(params.event.item_user) ? { targetSenderId: params.event.item_user } : {}
|
|
326
600
|
};
|
|
327
601
|
return {
|
|
328
602
|
shouldRoute: true,
|
|
@@ -481,13 +755,13 @@ function getFallbackModelEntries(byHandle) {
|
|
|
481
755
|
}
|
|
482
756
|
|
|
483
757
|
// src/channels/slack/utils.ts
|
|
484
|
-
function
|
|
758
|
+
function isNonEmptyString4(value) {
|
|
485
759
|
return typeof value === "string" && value.length > 0;
|
|
486
760
|
}
|
|
487
761
|
function firstNonEmptyString2(...values) {
|
|
488
|
-
return values.find(
|
|
762
|
+
return values.find(isNonEmptyString4);
|
|
489
763
|
}
|
|
490
|
-
function
|
|
764
|
+
function asRecord2(value) {
|
|
491
765
|
return value && typeof value === "object" ? value : null;
|
|
492
766
|
}
|
|
493
767
|
var IGNORED_SLACK_MESSAGE_SUBTYPES2 = new Set([
|
|
@@ -522,12 +796,12 @@ var WRAPPER_SLACK_MESSAGE_SUBTYPES2 = new Set([
|
|
|
522
796
|
"message_replied"
|
|
523
797
|
]);
|
|
524
798
|
function getSlackActionRecord(action, body) {
|
|
525
|
-
const directAction =
|
|
799
|
+
const directAction = asRecord2(action);
|
|
526
800
|
if (directAction) {
|
|
527
801
|
return directAction;
|
|
528
802
|
}
|
|
529
|
-
const actions =
|
|
530
|
-
return Array.isArray(actions) ?
|
|
803
|
+
const actions = asRecord2(body)?.actions;
|
|
804
|
+
return Array.isArray(actions) ? asRecord2(actions[0]) : null;
|
|
531
805
|
}
|
|
532
806
|
|
|
533
807
|
// src/channels/slack/model-picker-blocks.ts
|
|
@@ -537,7 +811,7 @@ var SLACK_MODEL_OPTION_VALUE_LIMIT = 75;
|
|
|
537
811
|
var SLACK_MODEL_SELECT_ACTION_ID = "letta_channel_model_select";
|
|
538
812
|
function resolveSlackSelectedModel(action, body) {
|
|
539
813
|
const actionRecord = getSlackActionRecord(action, body);
|
|
540
|
-
const selectedOption =
|
|
814
|
+
const selectedOption = asRecord2(actionRecord?.selected_option);
|
|
541
815
|
return firstNonEmptyString2(selectedOption?.value, actionRecord?.value) ?? null;
|
|
542
816
|
}
|
|
543
817
|
function escapeSlackMrkdwn(text) {
|
|
@@ -897,14 +1171,14 @@ function formatSlackToolNameForDisplay(toolName) {
|
|
|
897
1171
|
return toolName;
|
|
898
1172
|
}
|
|
899
1173
|
function resolveSlackConcreteActivity(event) {
|
|
900
|
-
if (event.kind === "command" &&
|
|
1174
|
+
if (event.kind === "command" && isNonEmptyString3(event.command)) {
|
|
901
1175
|
return sanitizeSlackStatusText(formatSlackToolNameForDisplay(event.command), SLACK_LOADING_MESSAGE_MAX);
|
|
902
1176
|
}
|
|
903
|
-
if (event.kind !== "tool" || !
|
|
1177
|
+
if (event.kind !== "tool" || !isNonEmptyString3(event.toolName) || event.toolName.toLowerCase() === "messagechannel") {
|
|
904
1178
|
return null;
|
|
905
1179
|
}
|
|
906
1180
|
for (const description of [event.toolTitle, event.toolDetails]) {
|
|
907
|
-
if (!
|
|
1181
|
+
if (!isNonEmptyString3(description)) {
|
|
908
1182
|
continue;
|
|
909
1183
|
}
|
|
910
1184
|
const sanitized = sanitizeSlackStatusText(description, SLACK_LOADING_MESSAGE_MAX);
|
|
@@ -1056,22 +1330,22 @@ function createSlackStatusController(params) {
|
|
|
1056
1330
|
const keepaliveByConversation = new Map;
|
|
1057
1331
|
const clearedStaleReplyKeys = new Set;
|
|
1058
1332
|
function getConversationKey(source) {
|
|
1059
|
-
return source.channel === "slack" &&
|
|
1333
|
+
return source.channel === "slack" && isNonEmptyString3(source.agentId) && isNonEmptyString3(source.conversationId) ? `${source.agentId}:${source.conversationId}` : null;
|
|
1060
1334
|
}
|
|
1061
1335
|
function getLifecycleReplyKey(source) {
|
|
1062
|
-
if (source.channel !== "slack" || !
|
|
1336
|
+
if (source.channel !== "slack" || !isNonEmptyString3(source.chatId)) {
|
|
1063
1337
|
return null;
|
|
1064
1338
|
}
|
|
1065
1339
|
const replyToMessageId = resolveSlackProgressThreadTs(source);
|
|
1066
|
-
return
|
|
1340
|
+
return isNonEmptyString3(replyToMessageId) ? `${source.chatId}:${replyToMessageId}` : null;
|
|
1067
1341
|
}
|
|
1068
1342
|
function getLifecycleErrorReplyKey(source) {
|
|
1069
|
-
if (source.channel !== "slack" || !
|
|
1343
|
+
if (source.channel !== "slack" || !isNonEmptyString3(source.chatId)) {
|
|
1070
1344
|
return null;
|
|
1071
1345
|
}
|
|
1072
1346
|
if (source.chatType === "direct" || resolveSlackChatType(source.chatId) === "direct") {
|
|
1073
1347
|
const replyToMessageId = resolveSlackSourceThreadTs(source);
|
|
1074
|
-
return
|
|
1348
|
+
return isNonEmptyString3(replyToMessageId) ? `${source.chatId}:${replyToMessageId}` : `${source.chatId}:direct`;
|
|
1075
1349
|
}
|
|
1076
1350
|
return getLifecycleReplyKey(source);
|
|
1077
1351
|
}
|
|
@@ -1230,7 +1504,7 @@ ${loadingText}`;
|
|
|
1230
1504
|
markAutoClearedByKey(key);
|
|
1231
1505
|
},
|
|
1232
1506
|
markAutoClearedForMessage(msg) {
|
|
1233
|
-
if (
|
|
1507
|
+
if (isNonEmptyString3(msg.agentId) && isNonEmptyString3(msg.conversationId)) {
|
|
1234
1508
|
markAutoClearedByKey(`${msg.agentId}:${msg.conversationId}`);
|
|
1235
1509
|
return;
|
|
1236
1510
|
}
|
|
@@ -1269,6 +1543,8 @@ export {
|
|
|
1269
1543
|
resolveSlackReactionIngressPolicy,
|
|
1270
1544
|
resolveSlackOutboundThreadTs,
|
|
1271
1545
|
resolveSlackMessageIngressPolicy,
|
|
1546
|
+
resolveSlackMessageFiles,
|
|
1547
|
+
resolveSlackFileMetadata,
|
|
1272
1548
|
resolveSlackConcreteActivity,
|
|
1273
1549
|
resolveSlackChatType,
|
|
1274
1550
|
resolveSlackAppMentionIngressPolicy,
|
|
@@ -1277,13 +1553,15 @@ export {
|
|
|
1277
1553
|
isProcessableSlackInboundMessage,
|
|
1278
1554
|
isOwnSlackBotInboundMessage,
|
|
1279
1555
|
formatSlackLifecycleErrorMessage,
|
|
1556
|
+
fetchSlackFile,
|
|
1280
1557
|
createSlackStatusController,
|
|
1281
1558
|
createSlackMessageActionAdapter,
|
|
1282
1559
|
createSlackChannelSender,
|
|
1560
|
+
collectSlackFiles,
|
|
1283
1561
|
buildSlackModelPickerBlocks,
|
|
1284
1562
|
SLACK_MODEL_SELECT_ACTION_ID,
|
|
1285
1563
|
SLACK_ASSISTANT_WORKING_STATUS,
|
|
1286
1564
|
SLACK_ASSISTANT_STARTUP_STATUS
|
|
1287
1565
|
};
|
|
1288
1566
|
|
|
1289
|
-
//# debugId=
|
|
1567
|
+
//# debugId=DB8AFA417A95EA8864756E2164756E21
|