@sentry/junior 0.67.3 → 0.69.0
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/app.js +1465 -814
- package/dist/build/virtual-config.d.ts +2 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +2 -2
- package/dist/chat/agent-dispatch/store.d.ts +4 -1
- package/dist/chat/agent-dispatch/types.d.ts +2 -4
- package/dist/chat/agent-dispatch/validation.d.ts +3 -2
- package/dist/chat/credentials/context.d.ts +49 -24
- package/dist/chat/credentials/user-token-store.d.ts +6 -0
- package/dist/chat/destination.d.ts +12 -0
- package/dist/chat/ingress/message-router.d.ts +1 -1
- package/dist/chat/mcp/auth-store.d.ts +2 -0
- package/dist/chat/mcp/oauth.d.ts +2 -0
- package/dist/chat/oauth-flow.d.ts +7 -0
- package/dist/chat/plugins/agent-hooks.d.ts +9 -9
- package/dist/chat/plugins/auth/auth-token-placeholder.d.ts +2 -2
- package/dist/chat/plugins/auth/oauth-request.d.ts +3 -1
- package/dist/chat/plugins/credential-hooks.d.ts +34 -0
- package/dist/chat/plugins/logging.d.ts +1 -1
- package/dist/chat/plugins/state.d.ts +1 -1
- package/dist/chat/plugins/types.d.ts +19 -23
- package/dist/chat/respond.d.ts +2 -0
- package/dist/chat/runtime/reply-executor.d.ts +3 -1
- package/dist/chat/runtime/slack-runtime.d.ts +8 -3
- package/dist/chat/sandbox/egress-credentials.d.ts +33 -0
- package/dist/chat/sandbox/egress-schemas.d.ts +105 -0
- package/dist/chat/sandbox/egress-session.d.ts +17 -17
- package/dist/chat/sandbox/sandbox.d.ts +3 -0
- package/dist/chat/sandbox/session.d.ts +1 -0
- package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -0
- package/dist/chat/services/pending-auth.d.ts +2 -0
- package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -0
- package/dist/chat/services/provider-retry.d.ts +13 -4
- package/dist/chat/services/timeout-resume.d.ts +2 -0
- package/dist/chat/services/turn-session-record.d.ts +6 -0
- package/dist/chat/slack/attachment-fetchers.d.ts +11 -0
- package/dist/chat/slack/footer.d.ts +2 -7
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/state/turn-session.d.ts +4 -0
- package/dist/chat/task-execution/queue.d.ts +2 -0
- package/dist/chat/task-execution/store.d.ts +5 -0
- package/dist/chat/task-execution/vercel-callback.d.ts +4 -0
- package/dist/chat/task-execution/vercel-queue.d.ts +2 -0
- package/dist/chat/task-execution/worker.d.ts +4 -2
- package/dist/chat/tools/slack/context.d.ts +3 -0
- package/dist/chat/tools/types.d.ts +21 -2
- package/dist/chunk-76YMBKW7.js +326 -0
- package/dist/{chunk-PIVOJIUD.js → chunk-B5HKWWQB.js} +9 -5
- package/dist/chunk-BBXYXOJW.js +1858 -0
- package/dist/{chunk-V47RLIO2.js → chunk-GT67ZWZQ.js} +4 -4
- package/dist/{chunk-75UZ4JLC.js → chunk-IGLNC5H6.js} +21 -9
- package/dist/{chunk-EBVQXCD2.js → chunk-JS4HURDT.js} +362 -280
- package/dist/{chunk-UQQSW7QB.js → chunk-N3MORKTH.js} +74 -331
- package/dist/chunk-R62YWUNO.js +264 -0
- package/dist/{chunk-OIIXZOOC.js → chunk-UXG6TU2U.js} +311 -2015
- package/dist/cli/check.js +4 -4
- package/dist/cli/snapshot-warmup.js +5 -4
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +21 -19
- package/dist/plugins.d.ts +2 -2
- package/dist/reporting.d.ts +2 -2
- package/dist/reporting.js +13 -11
- package/package.json +6 -4
- package/dist/chat/plugins/auth/github-app-broker.d.ts +0 -4
- package/dist/chat/plugins/github-permissions.d.ts +0 -11
- package/dist/chat/queue/thread-message-dispatcher.d.ts +0 -33
- package/dist/chunk-KVZL5NZS.js +0 -519
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SANDBOX_DATA_ROOT,
|
|
3
3
|
SANDBOX_WORKSPACE_ROOT,
|
|
4
|
-
botConfig,
|
|
5
|
-
getChatConfig,
|
|
6
4
|
getConnectedStateContext,
|
|
7
|
-
getSlackBotToken,
|
|
8
5
|
getStateAdapter,
|
|
9
|
-
parseSlackThreadId,
|
|
10
6
|
sandboxSkillDir
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-R62YWUNO.js";
|
|
12
8
|
import {
|
|
13
9
|
isActorUserId,
|
|
10
|
+
parseActorUserId
|
|
11
|
+
} from "./chunk-UXG6TU2U.js";
|
|
12
|
+
import {
|
|
13
|
+
isConversationChannel,
|
|
14
|
+
isConversationScopedChannel,
|
|
15
|
+
isDmChannel,
|
|
16
|
+
normalizeSlackConversationId,
|
|
17
|
+
parseDestination
|
|
18
|
+
} from "./chunk-76YMBKW7.js";
|
|
19
|
+
import {
|
|
20
|
+
TURN_CONTEXT_TAG,
|
|
21
|
+
botConfig,
|
|
22
|
+
getChatConfig,
|
|
23
|
+
parseSlackThreadId
|
|
24
|
+
} from "./chunk-JS4HURDT.js";
|
|
25
|
+
import {
|
|
14
26
|
isRecord,
|
|
27
|
+
listReferenceFiles,
|
|
15
28
|
logException,
|
|
16
29
|
logInfo,
|
|
17
30
|
logWarn,
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
soulPathCandidates,
|
|
32
|
+
worldPathCandidates
|
|
33
|
+
} from "./chunk-BBXYXOJW.js";
|
|
20
34
|
import {
|
|
21
35
|
sentry_exports
|
|
22
36
|
} from "./chunk-Z3YD6NHK.js";
|
|
23
|
-
import {
|
|
24
|
-
listReferenceFiles,
|
|
25
|
-
soulPathCandidates,
|
|
26
|
-
worldPathCandidates
|
|
27
|
-
} from "./chunk-KVZL5NZS.js";
|
|
28
37
|
|
|
29
38
|
// src/chat/plugins/logging.ts
|
|
30
39
|
function createAgentPluginLogger(plugin) {
|
|
@@ -152,277 +161,6 @@ function createPluginState(plugin, options) {
|
|
|
152
161
|
|
|
153
162
|
// src/chat/credentials/subject.ts
|
|
154
163
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
155
|
-
|
|
156
|
-
// src/chat/slack/client.ts
|
|
157
|
-
import { WebClient } from "@slack/web-api";
|
|
158
|
-
var SlackActionError = class extends Error {
|
|
159
|
-
code;
|
|
160
|
-
apiError;
|
|
161
|
-
needed;
|
|
162
|
-
provided;
|
|
163
|
-
statusCode;
|
|
164
|
-
requestId;
|
|
165
|
-
errorData;
|
|
166
|
-
retryAfterSeconds;
|
|
167
|
-
detail;
|
|
168
|
-
detailLine;
|
|
169
|
-
detailRule;
|
|
170
|
-
constructor(message, code, options = {}) {
|
|
171
|
-
super(message);
|
|
172
|
-
this.name = "SlackActionError";
|
|
173
|
-
this.code = code;
|
|
174
|
-
this.apiError = options.apiError;
|
|
175
|
-
this.needed = options.needed;
|
|
176
|
-
this.provided = options.provided;
|
|
177
|
-
this.statusCode = options.statusCode;
|
|
178
|
-
this.requestId = options.requestId;
|
|
179
|
-
this.errorData = options.errorData;
|
|
180
|
-
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
181
|
-
this.detail = options.detail;
|
|
182
|
-
this.detailLine = options.detailLine;
|
|
183
|
-
this.detailRule = options.detailRule;
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
function serializeSlackErrorData(data) {
|
|
187
|
-
if (!data || typeof data !== "object") {
|
|
188
|
-
return void 0;
|
|
189
|
-
}
|
|
190
|
-
const filtered = Object.fromEntries(
|
|
191
|
-
Object.entries(data).filter(
|
|
192
|
-
([key2]) => key2 !== "error"
|
|
193
|
-
)
|
|
194
|
-
);
|
|
195
|
-
if (Object.keys(filtered).length === 0) {
|
|
196
|
-
return void 0;
|
|
197
|
-
}
|
|
198
|
-
try {
|
|
199
|
-
const serialized = JSON.stringify(filtered);
|
|
200
|
-
return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
|
|
201
|
-
} catch {
|
|
202
|
-
return void 0;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
function getHeaderString(headers, name) {
|
|
206
|
-
if (!headers || typeof headers !== "object") {
|
|
207
|
-
return void 0;
|
|
208
|
-
}
|
|
209
|
-
const key2 = name.toLowerCase();
|
|
210
|
-
const entries = headers;
|
|
211
|
-
for (const [entryKey, value] of Object.entries(entries)) {
|
|
212
|
-
if (entryKey.toLowerCase() !== key2) continue;
|
|
213
|
-
if (typeof value === "string") return value;
|
|
214
|
-
if (Array.isArray(value)) {
|
|
215
|
-
const first = value.find((entry) => typeof entry === "string");
|
|
216
|
-
return typeof first === "string" ? first : void 0;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return void 0;
|
|
220
|
-
}
|
|
221
|
-
function parseSlackCanvasDetail(detail) {
|
|
222
|
-
if (typeof detail !== "string") {
|
|
223
|
-
return {};
|
|
224
|
-
}
|
|
225
|
-
const trimmed = detail.trim();
|
|
226
|
-
if (!trimmed) {
|
|
227
|
-
return {};
|
|
228
|
-
}
|
|
229
|
-
const parsed = {
|
|
230
|
-
detail: trimmed
|
|
231
|
-
};
|
|
232
|
-
const lineMatch = trimmed.match(/line\s+(\d+):/i);
|
|
233
|
-
if (lineMatch) {
|
|
234
|
-
const line = Number.parseInt(lineMatch[1] ?? "", 10);
|
|
235
|
-
if (Number.isFinite(line)) {
|
|
236
|
-
parsed.detailLine = line;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (/unsupported heading depth/i.test(trimmed)) {
|
|
240
|
-
parsed.detailRule = "unsupported_heading_depth";
|
|
241
|
-
}
|
|
242
|
-
return parsed;
|
|
243
|
-
}
|
|
244
|
-
var client = null;
|
|
245
|
-
function normalizeSlackConversationId(channelId) {
|
|
246
|
-
if (!channelId) return void 0;
|
|
247
|
-
const trimmed = channelId.trim();
|
|
248
|
-
if (!trimmed) return void 0;
|
|
249
|
-
if (!trimmed.startsWith("slack:")) {
|
|
250
|
-
return trimmed;
|
|
251
|
-
}
|
|
252
|
-
const parts = trimmed.split(":");
|
|
253
|
-
return parts[1]?.trim() || void 0;
|
|
254
|
-
}
|
|
255
|
-
function getClient() {
|
|
256
|
-
if (client) return client;
|
|
257
|
-
const token = getSlackBotToken();
|
|
258
|
-
if (!token) {
|
|
259
|
-
throw new SlackActionError(
|
|
260
|
-
"SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack canvas/list actions in this service",
|
|
261
|
-
"missing_token"
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
client = new WebClient(token);
|
|
265
|
-
return client;
|
|
266
|
-
}
|
|
267
|
-
function mapSlackError(error) {
|
|
268
|
-
if (error instanceof SlackActionError) {
|
|
269
|
-
return error;
|
|
270
|
-
}
|
|
271
|
-
const candidate = error;
|
|
272
|
-
const apiError = candidate.data?.error;
|
|
273
|
-
const message = candidate.message ?? "Slack action failed";
|
|
274
|
-
const baseOptions = {
|
|
275
|
-
apiError,
|
|
276
|
-
statusCode: candidate.statusCode,
|
|
277
|
-
requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
|
|
278
|
-
errorData: serializeSlackErrorData(candidate.data),
|
|
279
|
-
...parseSlackCanvasDetail(candidate.data?.detail)
|
|
280
|
-
};
|
|
281
|
-
if (apiError === "missing_scope") {
|
|
282
|
-
return new SlackActionError(message, "missing_scope", {
|
|
283
|
-
...baseOptions,
|
|
284
|
-
needed: candidate.data?.needed,
|
|
285
|
-
provided: candidate.data?.provided
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
if (apiError === "not_in_channel") {
|
|
289
|
-
return new SlackActionError(message, "not_in_channel", baseOptions);
|
|
290
|
-
}
|
|
291
|
-
if (apiError === "already_reacted") {
|
|
292
|
-
return new SlackActionError(message, "already_reacted", baseOptions);
|
|
293
|
-
}
|
|
294
|
-
if (apiError === "no_reaction") {
|
|
295
|
-
return new SlackActionError(message, "no_reaction", baseOptions);
|
|
296
|
-
}
|
|
297
|
-
if (apiError === "invalid_arguments") {
|
|
298
|
-
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
299
|
-
}
|
|
300
|
-
if (apiError === "invalid_cursor") {
|
|
301
|
-
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
302
|
-
}
|
|
303
|
-
if (apiError === "invalid_name") {
|
|
304
|
-
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
305
|
-
}
|
|
306
|
-
if (apiError === "not_found" || apiError === "channel_not_found" || apiError === "message_not_found") {
|
|
307
|
-
return new SlackActionError(message, "not_found", baseOptions);
|
|
308
|
-
}
|
|
309
|
-
if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
|
|
310
|
-
return new SlackActionError(message, "feature_unavailable", baseOptions);
|
|
311
|
-
}
|
|
312
|
-
if (apiError === "canvas_creation_failed") {
|
|
313
|
-
return new SlackActionError(message, "canvas_creation_failed", baseOptions);
|
|
314
|
-
}
|
|
315
|
-
if (apiError === "canvas_editing_failed") {
|
|
316
|
-
return new SlackActionError(message, "canvas_editing_failed", baseOptions);
|
|
317
|
-
}
|
|
318
|
-
if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
|
|
319
|
-
return new SlackActionError(message, "rate_limited", {
|
|
320
|
-
...baseOptions,
|
|
321
|
-
retryAfterSeconds: candidate.retryAfter
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
return new SlackActionError(message, "internal_error", baseOptions);
|
|
325
|
-
}
|
|
326
|
-
function sleep(ms) {
|
|
327
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
328
|
-
}
|
|
329
|
-
async function withSlackRetries(task, maxAttempts = 3, context = {}) {
|
|
330
|
-
let attempt = 0;
|
|
331
|
-
while (attempt < maxAttempts) {
|
|
332
|
-
attempt += 1;
|
|
333
|
-
try {
|
|
334
|
-
return await task();
|
|
335
|
-
} catch (error) {
|
|
336
|
-
const mapped = mapSlackError(error);
|
|
337
|
-
const isRetryable = mapped.code === "rate_limited";
|
|
338
|
-
const baseLogAttributes = {
|
|
339
|
-
"app.slack.action": context.action ?? "unknown",
|
|
340
|
-
"app.slack.error_code": mapped.code,
|
|
341
|
-
...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
|
|
342
|
-
...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
|
|
343
|
-
...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
|
|
344
|
-
...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
|
|
345
|
-
...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
|
|
346
|
-
...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
|
|
347
|
-
...context.attributes ?? {}
|
|
348
|
-
};
|
|
349
|
-
if (!isRetryable || attempt >= maxAttempts) {
|
|
350
|
-
logWarn(
|
|
351
|
-
"slack_action_failed",
|
|
352
|
-
{},
|
|
353
|
-
{
|
|
354
|
-
...baseLogAttributes,
|
|
355
|
-
...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
|
|
356
|
-
},
|
|
357
|
-
"Slack action failed"
|
|
358
|
-
);
|
|
359
|
-
throw mapped;
|
|
360
|
-
}
|
|
361
|
-
logWarn(
|
|
362
|
-
"slack_action_retrying",
|
|
363
|
-
{},
|
|
364
|
-
{
|
|
365
|
-
...baseLogAttributes,
|
|
366
|
-
"app.slack.retry_attempt": attempt
|
|
367
|
-
},
|
|
368
|
-
"Retrying Slack action after transient failure"
|
|
369
|
-
);
|
|
370
|
-
const retryAfterMs = mapped.code === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
|
|
371
|
-
const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
|
|
372
|
-
await sleep(retryAfterMs ?? backoffMs);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
throw new SlackActionError(
|
|
376
|
-
"Slack action exhausted retries",
|
|
377
|
-
"internal_error"
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
function getSlackClient() {
|
|
381
|
-
return getClient();
|
|
382
|
-
}
|
|
383
|
-
function isDmChannel(channelId) {
|
|
384
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
385
|
-
return Boolean(normalized && normalized.startsWith("D"));
|
|
386
|
-
}
|
|
387
|
-
function isConversationScopedChannel(channelId) {
|
|
388
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
389
|
-
if (!normalized) return false;
|
|
390
|
-
return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
|
|
391
|
-
}
|
|
392
|
-
function isConversationChannel(channelId) {
|
|
393
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
394
|
-
if (!normalized) return false;
|
|
395
|
-
return normalized.startsWith("C") || normalized.startsWith("G");
|
|
396
|
-
}
|
|
397
|
-
async function getFilePermalink(fileId) {
|
|
398
|
-
const client2 = getClient();
|
|
399
|
-
const response = await withSlackRetries(
|
|
400
|
-
() => client2.files.info({
|
|
401
|
-
file: fileId
|
|
402
|
-
})
|
|
403
|
-
);
|
|
404
|
-
return response.file?.permalink;
|
|
405
|
-
}
|
|
406
|
-
async function downloadPrivateSlackFile(url) {
|
|
407
|
-
const token = getSlackBotToken();
|
|
408
|
-
if (!token) {
|
|
409
|
-
throw new SlackActionError(
|
|
410
|
-
"SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack file downloads in this service",
|
|
411
|
-
"missing_token"
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
const response = await fetch(url, {
|
|
415
|
-
headers: {
|
|
416
|
-
Authorization: `Bearer ${token}`
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
if (!response.ok) {
|
|
420
|
-
throw new Error(`Slack file download failed: ${response.status}`);
|
|
421
|
-
}
|
|
422
|
-
return Buffer.from(await response.arrayBuffer());
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// src/chat/credentials/subject.ts
|
|
426
164
|
var CREDENTIAL_SUBJECT_HMAC_CONTEXT = "junior.credential_subject.v1";
|
|
427
165
|
var CREDENTIAL_SUBJECT_SIGNATURE_VERSION = "v1";
|
|
428
166
|
function getCredentialSubjectSecret() {
|
|
@@ -514,6 +252,15 @@ function verifySlackDirectCredentialSubject(input) {
|
|
|
514
252
|
return timingSafeMatch(expected, binding.signature);
|
|
515
253
|
}
|
|
516
254
|
|
|
255
|
+
// src/chat/tools/channel-capabilities.ts
|
|
256
|
+
function resolveChannelCapabilities(channelId) {
|
|
257
|
+
return {
|
|
258
|
+
canCreateCanvas: isConversationScopedChannel(channelId),
|
|
259
|
+
canPostToChannel: isConversationChannel(channelId),
|
|
260
|
+
canAddReactions: isConversationScopedChannel(channelId)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
517
264
|
// src/chat/plugins/agent-hooks.ts
|
|
518
265
|
var AgentPluginHookDeniedError = class extends Error {
|
|
519
266
|
constructor(message) {
|
|
@@ -540,6 +287,9 @@ var AGENT_PLUGIN_ROUTE_METHODS = /* @__PURE__ */ new Set([
|
|
|
540
287
|
"OPTIONS",
|
|
541
288
|
"ALL"
|
|
542
289
|
]);
|
|
290
|
+
function isRecord2(value) {
|
|
291
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
292
|
+
}
|
|
543
293
|
function validateLegacyStatePrefixes(plugin) {
|
|
544
294
|
const prefixes = plugin.legacyStatePrefixes;
|
|
545
295
|
if (prefixes === void 0) {
|
|
@@ -547,7 +297,7 @@ function validateLegacyStatePrefixes(plugin) {
|
|
|
547
297
|
}
|
|
548
298
|
if (!Array.isArray(prefixes)) {
|
|
549
299
|
throw new Error(
|
|
550
|
-
`
|
|
300
|
+
`Plugin "${plugin.name}" legacyStatePrefixes must be an array`
|
|
551
301
|
);
|
|
552
302
|
}
|
|
553
303
|
const allowedPrefix = `junior:${plugin.name}`;
|
|
@@ -555,12 +305,12 @@ function validateLegacyStatePrefixes(plugin) {
|
|
|
555
305
|
const prefix = typeof rawPrefix === "string" ? rawPrefix.trim() : "";
|
|
556
306
|
if (!prefix) {
|
|
557
307
|
throw new Error(
|
|
558
|
-
`
|
|
308
|
+
`Plugin "${plugin.name}" legacy state prefixes must be non-empty strings`
|
|
559
309
|
);
|
|
560
310
|
}
|
|
561
311
|
if (prefix !== allowedPrefix && !prefix.startsWith(`${allowedPrefix}:`)) {
|
|
562
312
|
throw new Error(
|
|
563
|
-
`
|
|
313
|
+
`Plugin "${plugin.name}" legacy state prefix "${prefix}" must stay under "${allowedPrefix}"`
|
|
564
314
|
);
|
|
565
315
|
}
|
|
566
316
|
}
|
|
@@ -570,11 +320,11 @@ function validateAgentPlugins(plugins) {
|
|
|
570
320
|
for (const plugin of plugins) {
|
|
571
321
|
if (!AGENT_PLUGIN_NAME_RE.test(plugin.name)) {
|
|
572
322
|
throw new Error(
|
|
573
|
-
`
|
|
323
|
+
`Plugin name "${plugin.name}" must be a lowercase plugin identifier`
|
|
574
324
|
);
|
|
575
325
|
}
|
|
576
326
|
if (seen.has(plugin.name)) {
|
|
577
|
-
throw new Error(`Duplicate
|
|
327
|
+
throw new Error(`Duplicate plugin name "${plugin.name}"`);
|
|
578
328
|
}
|
|
579
329
|
seen.add(plugin.name);
|
|
580
330
|
validateLegacyStatePrefixes(plugin);
|
|
@@ -599,18 +349,22 @@ function getAgentPluginTools(context) {
|
|
|
599
349
|
continue;
|
|
600
350
|
}
|
|
601
351
|
const log = createAgentPluginLogger(plugin.name);
|
|
352
|
+
const destination = context.destination;
|
|
602
353
|
const credentialSubject = createSlackDirectCredentialSubject({
|
|
603
354
|
channelId: context.channelId,
|
|
604
355
|
teamId: context.teamId,
|
|
605
356
|
userId: context.requester?.userId
|
|
606
357
|
});
|
|
358
|
+
const pluginCapabilities = resolveChannelCapabilities(context.channelId);
|
|
607
359
|
const pluginTools = hook({
|
|
608
360
|
plugin: { name: plugin.name },
|
|
609
361
|
log,
|
|
610
362
|
requester: context.requester,
|
|
611
|
-
channelCapabilities:
|
|
363
|
+
channelCapabilities: pluginCapabilities,
|
|
612
364
|
channelId: context.channelId,
|
|
365
|
+
conversationId: context.conversationId,
|
|
613
366
|
...credentialSubject ? { credentialSubject } : {},
|
|
367
|
+
...destination ? { destination } : {},
|
|
614
368
|
teamId: context.teamId,
|
|
615
369
|
messageTs: context.messageTs,
|
|
616
370
|
threadTs: context.threadTs,
|
|
@@ -622,12 +376,12 @@ function getAgentPluginTools(context) {
|
|
|
622
376
|
for (const [name, tool] of Object.entries(pluginTools)) {
|
|
623
377
|
if (!AGENT_PLUGIN_TOOL_NAME_RE.test(name)) {
|
|
624
378
|
throw new Error(
|
|
625
|
-
`
|
|
379
|
+
`Plugin tool "${name}" from plugin "${plugin.name}" must be a camelCase identifier`
|
|
626
380
|
);
|
|
627
381
|
}
|
|
628
382
|
if (tools[name]) {
|
|
629
383
|
throw new Error(
|
|
630
|
-
`Duplicate
|
|
384
|
+
`Duplicate plugin tool "${name}" from plugin "${plugin.name}"`
|
|
631
385
|
);
|
|
632
386
|
}
|
|
633
387
|
tools[name] = tool;
|
|
@@ -639,19 +393,19 @@ function routeMethods(route, pluginName) {
|
|
|
639
393
|
const methods = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
|
|
640
394
|
if (methods.length === 0) {
|
|
641
395
|
throw new Error(
|
|
642
|
-
`
|
|
396
|
+
`Plugin route "${route.path}" from plugin "${pluginName}" must declare at least one method`
|
|
643
397
|
);
|
|
644
398
|
}
|
|
645
399
|
for (const method of methods) {
|
|
646
400
|
if (!AGENT_PLUGIN_ROUTE_METHODS.has(method)) {
|
|
647
401
|
throw new Error(
|
|
648
|
-
`
|
|
402
|
+
`Plugin route "${route.path}" from plugin "${pluginName}" has invalid method "${String(method)}"`
|
|
649
403
|
);
|
|
650
404
|
}
|
|
651
405
|
}
|
|
652
406
|
if (methods.includes("ALL") && methods.length > 1) {
|
|
653
407
|
throw new Error(
|
|
654
|
-
`
|
|
408
|
+
`Plugin route "${route.path}" from plugin "${pluginName}" must not combine ALL with explicit methods`
|
|
655
409
|
);
|
|
656
410
|
}
|
|
657
411
|
return methods;
|
|
@@ -672,38 +426,36 @@ function getAgentPluginRoutes() {
|
|
|
672
426
|
});
|
|
673
427
|
if (!Array.isArray(pluginRoutes)) {
|
|
674
428
|
throw new Error(
|
|
675
|
-
`
|
|
429
|
+
`Plugin routes hook from plugin "${plugin.name}" must return an array`
|
|
676
430
|
);
|
|
677
431
|
}
|
|
678
432
|
for (const route of pluginRoutes) {
|
|
679
433
|
if (!isRecord2(route)) {
|
|
680
434
|
throw new Error(
|
|
681
|
-
`
|
|
435
|
+
`Plugin route from plugin "${plugin.name}" must be an object`
|
|
682
436
|
);
|
|
683
437
|
}
|
|
684
438
|
if (typeof route.path !== "string" || !route.path.startsWith("/")) {
|
|
685
439
|
throw new Error(
|
|
686
|
-
`
|
|
440
|
+
`Plugin route "${route.path}" from plugin "${plugin.name}" must start with /`
|
|
687
441
|
);
|
|
688
442
|
}
|
|
689
443
|
if (typeof route.handler !== "function") {
|
|
690
444
|
throw new Error(
|
|
691
|
-
`
|
|
445
|
+
`Plugin route "${route.path}" from plugin "${plugin.name}" must provide a handler`
|
|
692
446
|
);
|
|
693
447
|
}
|
|
694
448
|
const methods = routeMethods(route, plugin.name);
|
|
695
449
|
const pathMethods = methodsByPath.get(route.path) ?? /* @__PURE__ */ new Set();
|
|
696
450
|
if (pathMethods.has("ALL") || methods.includes("ALL") && pathMethods.size > 0) {
|
|
697
451
|
throw new Error(
|
|
698
|
-
`
|
|
452
|
+
`Plugin route "${route.path}" conflicts with an ALL route for the same path`
|
|
699
453
|
);
|
|
700
454
|
}
|
|
701
455
|
for (const method of methods) {
|
|
702
456
|
const key2 = `${method}:${route.path}`;
|
|
703
457
|
if (seen.has(key2)) {
|
|
704
|
-
throw new Error(
|
|
705
|
-
`Duplicate trusted plugin route "${method} ${route.path}"`
|
|
706
|
-
);
|
|
458
|
+
throw new Error(`Duplicate plugin route "${method} ${route.path}"`);
|
|
707
459
|
}
|
|
708
460
|
seen.add(key2);
|
|
709
461
|
pathMethods.add(method);
|
|
@@ -727,13 +479,13 @@ function trustedSlackConversationUrl(pluginName, link) {
|
|
|
727
479
|
parsed = new URL(url);
|
|
728
480
|
} catch (error) {
|
|
729
481
|
throw new Error(
|
|
730
|
-
`
|
|
482
|
+
`Plugin "${pluginName}" slackConversationLink must return an absolute http(s) URL`,
|
|
731
483
|
{ cause: error }
|
|
732
484
|
);
|
|
733
485
|
}
|
|
734
486
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
735
487
|
throw new Error(
|
|
736
|
-
`
|
|
488
|
+
`Plugin "${pluginName}" slackConversationLink must return an absolute http(s) URL`
|
|
737
489
|
);
|
|
738
490
|
}
|
|
739
491
|
return parsed.toString();
|
|
@@ -923,7 +675,7 @@ async function getAgentPluginOperationalReports(nowMs = Date.now()) {
|
|
|
923
675
|
})
|
|
924
676
|
);
|
|
925
677
|
} catch (error) {
|
|
926
|
-
log.error("
|
|
678
|
+
log.error("Plugin operational report failed", {
|
|
927
679
|
error: error instanceof Error ? error.message : String(error)
|
|
928
680
|
});
|
|
929
681
|
reports.push(failedOperationalReport({ nowMs, pluginName: plugin.name }));
|
|
@@ -931,9 +683,6 @@ async function getAgentPluginOperationalReports(nowMs = Date.now()) {
|
|
|
931
683
|
}
|
|
932
684
|
return reports;
|
|
933
685
|
}
|
|
934
|
-
function isRecord2(value) {
|
|
935
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
936
|
-
}
|
|
937
686
|
function normalizeEnv(value) {
|
|
938
687
|
if (!isRecord2(value)) {
|
|
939
688
|
return {};
|
|
@@ -1072,9 +821,6 @@ function GET() {
|
|
|
1072
821
|
import fs from "fs";
|
|
1073
822
|
import path from "path";
|
|
1074
823
|
|
|
1075
|
-
// src/chat/turn-context-tag.ts
|
|
1076
|
-
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
1077
|
-
|
|
1078
824
|
// src/chat/interruption-marker.ts
|
|
1079
825
|
var INTERRUPTED_MARKER = "\n\n[Response interrupted before completion]";
|
|
1080
826
|
function getInterruptionMarker() {
|
|
@@ -2202,20 +1948,20 @@ function commitEntries(existingMessages, nextMessages, sessionId, entries) {
|
|
|
2202
1948
|
return [resetEntry(nextMessages, nextSessionId(entries))];
|
|
2203
1949
|
}
|
|
2204
1950
|
function redisStore(redisStateAdapter) {
|
|
2205
|
-
const
|
|
1951
|
+
const client = redisStateAdapter.getClient();
|
|
2206
1952
|
return {
|
|
2207
1953
|
async append({ entries, scope, ttlMs }) {
|
|
2208
1954
|
const listKey = key(scope);
|
|
2209
1955
|
if (entries.length > 0) {
|
|
2210
|
-
await
|
|
1956
|
+
await client.rPush(
|
|
2211
1957
|
listKey,
|
|
2212
1958
|
entries.map((entry) => JSON.stringify(entry))
|
|
2213
1959
|
);
|
|
2214
1960
|
}
|
|
2215
|
-
await
|
|
1961
|
+
await client.pExpire(listKey, Math.max(1, ttlMs));
|
|
2216
1962
|
},
|
|
2217
1963
|
async read(scope) {
|
|
2218
|
-
const values = await
|
|
1964
|
+
const values = await client.lRange(key(scope), 0, -1);
|
|
2219
1965
|
return values.map(decode);
|
|
2220
1966
|
}
|
|
2221
1967
|
};
|
|
@@ -2441,7 +2187,8 @@ function parseAgentTurnSessionFields(parsed) {
|
|
|
2441
2187
|
const requester = parseAgentTurnRequester(parsed.requester);
|
|
2442
2188
|
const startedAtMs = toFiniteNonNegativeNumber(parsed.startedAtMs);
|
|
2443
2189
|
const surface = parseAgentTurnSurface(parsed.surface);
|
|
2444
|
-
|
|
2190
|
+
const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
|
|
2191
|
+
if (typeof conversationId !== "string" || typeof sessionId !== "string" || sliceId === void 0 || version === void 0 || updatedAtMs === void 0 || parsed.destination !== void 0 && !destination) {
|
|
2445
2192
|
return void 0;
|
|
2446
2193
|
}
|
|
2447
2194
|
return {
|
|
@@ -2458,6 +2205,7 @@ function parseAgentTurnSessionFields(parsed) {
|
|
|
2458
2205
|
cumulativeDurationMs,
|
|
2459
2206
|
...logSessionId ? { logSessionId } : {},
|
|
2460
2207
|
...cumulativeUsage ? { cumulativeUsage } : {},
|
|
2208
|
+
...destination ? { destination } : {},
|
|
2461
2209
|
...requester ? { requester } : {},
|
|
2462
2210
|
...Array.isArray(parsed.loadedSkillNames) ? {
|
|
2463
2211
|
loadedSkillNames: parsed.loadedSkillNames.filter(
|
|
@@ -2547,6 +2295,7 @@ function materializeAgentTurnSessionRecord(stored, piMessages) {
|
|
|
2547
2295
|
updatedAtMs: stored.updatedAtMs,
|
|
2548
2296
|
piMessages,
|
|
2549
2297
|
cumulativeDurationMs: stored.cumulativeDurationMs,
|
|
2298
|
+
...stored.destination ? { destination: stored.destination } : {},
|
|
2550
2299
|
...stored.cumulativeUsage ? { cumulativeUsage: stored.cumulativeUsage } : {},
|
|
2551
2300
|
...stored.resumeReason ? { resumeReason: stored.resumeReason } : {},
|
|
2552
2301
|
...stored.errorMessage ? { errorMessage: stored.errorMessage } : {},
|
|
@@ -2610,6 +2359,7 @@ function buildStoredRecord(args) {
|
|
|
2610
2359
|
...args.logSessionId ? { logSessionId: args.logSessionId } : {},
|
|
2611
2360
|
cumulativeDurationMs: args.cumulativeDurationMs,
|
|
2612
2361
|
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
2362
|
+
...args.destination ? { destination: args.destination } : {},
|
|
2613
2363
|
...args.requester ? { requester: args.requester } : {},
|
|
2614
2364
|
...Array.isArray(args.loadedSkillNames) ? {
|
|
2615
2365
|
loadedSkillNames: args.loadedSkillNames.filter(
|
|
@@ -2665,6 +2415,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
2665
2415
|
...parsed.logSessionId ? { logSessionId: parsed.logSessionId } : {},
|
|
2666
2416
|
cumulativeDurationMs: args.existing.cumulativeDurationMs,
|
|
2667
2417
|
...args.existing.cumulativeUsage ? { cumulativeUsage: args.existing.cumulativeUsage } : {},
|
|
2418
|
+
...args.existing.destination ? { destination: args.existing.destination } : {},
|
|
2668
2419
|
...args.existing.loadedSkillNames ? { loadedSkillNames: args.existing.loadedSkillNames } : {},
|
|
2669
2420
|
...args.existing.requester ? { requester: args.existing.requester } : {},
|
|
2670
2421
|
...args.existing.resumeReason ? { resumeReason: args.existing.resumeReason } : {},
|
|
@@ -2705,6 +2456,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
2705
2456
|
previousVersion: existingRecord?.version,
|
|
2706
2457
|
cumulativeDurationMs: toFiniteNonNegativeNumber(args.cumulativeDurationMs) ?? existingRecord?.cumulativeDurationMs ?? 0,
|
|
2707
2458
|
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
2459
|
+
...args.destination ?? existingRecord?.destination ? { destination: args.destination ?? existingRecord?.destination } : {},
|
|
2708
2460
|
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
2709
2461
|
...args.requester ?? existingRecord?.requester ? { requester: args.requester ?? existingRecord?.requester } : {},
|
|
2710
2462
|
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
@@ -2738,6 +2490,7 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
2738
2490
|
updatedAtMs: nowMs,
|
|
2739
2491
|
cumulativeDurationMs: toFiniteNonNegativeNumber(args.cumulativeDurationMs) ?? existing?.cumulativeDurationMs ?? 0,
|
|
2740
2492
|
...args.cumulativeUsage ?? existing?.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage ?? existing?.cumulativeUsage } : {},
|
|
2493
|
+
...args.destination ?? existing?.destination ? { destination: args.destination ?? existing?.destination } : {},
|
|
2741
2494
|
...args.requester ?? existing?.requester ? { requester: args.requester ?? existing?.requester } : {},
|
|
2742
2495
|
...Array.isArray(args.loadedSkillNames) ? {
|
|
2743
2496
|
loadedSkillNames: args.loadedSkillNames.filter(
|
|
@@ -2828,8 +2581,8 @@ function buildSentryWebBaseUrl(dsn) {
|
|
|
2828
2581
|
return `${dsn.protocol}://${dsn.host}${port}${path2}`;
|
|
2829
2582
|
}
|
|
2830
2583
|
function buildSentryConversationUrl(conversationId) {
|
|
2831
|
-
const
|
|
2832
|
-
const dsn =
|
|
2584
|
+
const client = sentry_exports.getClient();
|
|
2585
|
+
const dsn = client?.getDsn();
|
|
2833
2586
|
if (!dsn?.host || !dsn.projectId) {
|
|
2834
2587
|
return void 0;
|
|
2835
2588
|
}
|
|
@@ -2847,8 +2600,8 @@ function buildSentryConversationUrl(conversationId) {
|
|
|
2847
2600
|
return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path2}`;
|
|
2848
2601
|
}
|
|
2849
2602
|
function buildSentryTraceUrl(traceId) {
|
|
2850
|
-
const
|
|
2851
|
-
const dsn =
|
|
2603
|
+
const client = sentry_exports.getClient();
|
|
2604
|
+
const dsn = client?.getDsn();
|
|
2852
2605
|
if (!dsn?.host || !dsn.projectId) {
|
|
2853
2606
|
return void 0;
|
|
2854
2607
|
}
|
|
@@ -2937,18 +2690,9 @@ function formatSlackConversationRedactedLabel(context) {
|
|
|
2937
2690
|
export {
|
|
2938
2691
|
createAgentPluginLogger,
|
|
2939
2692
|
createPluginState,
|
|
2940
|
-
SlackActionError,
|
|
2941
|
-
getHeaderString,
|
|
2942
|
-
normalizeSlackConversationId,
|
|
2943
|
-
withSlackRetries,
|
|
2944
|
-
getSlackClient,
|
|
2945
|
-
isDmChannel,
|
|
2946
|
-
isConversationScopedChannel,
|
|
2947
|
-
isConversationChannel,
|
|
2948
|
-
getFilePermalink,
|
|
2949
|
-
downloadPrivateSlackFile,
|
|
2950
2693
|
bindSlackDirectCredentialSubject,
|
|
2951
2694
|
verifySlackDirectCredentialSubject,
|
|
2695
|
+
resolveChannelCapabilities,
|
|
2952
2696
|
validateAgentPlugins,
|
|
2953
2697
|
setAgentPlugins,
|
|
2954
2698
|
getAgentPlugins,
|
|
@@ -2958,7 +2702,6 @@ export {
|
|
|
2958
2702
|
getAgentPluginOperationalReports,
|
|
2959
2703
|
createAgentPluginHookRunner,
|
|
2960
2704
|
GET,
|
|
2961
|
-
TURN_CONTEXT_TAG,
|
|
2962
2705
|
getInterruptionMarker,
|
|
2963
2706
|
truncateStatusText,
|
|
2964
2707
|
normalizeSlackStatusText,
|