@sentry/junior 0.116.1 → 0.118.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/{agent-hooks-GUAAS3XH.js → agent-hooks-J2FB7XD6.js} +8 -10
- package/dist/api/conversations/detail.d.ts +7 -0
- package/dist/api/conversations/event-list.d.ts +7 -0
- package/dist/api/conversations/events.d.ts +1 -1
- package/dist/api/schema/conversation.d.ts +28 -0
- package/dist/api/schema/stats.d.ts +22 -0
- package/dist/api/schema.d.ts +2 -0
- package/dist/api/schema.js +7 -3
- package/dist/api/stats.d.ts +16 -0
- package/dist/api/user-pages/routes.d.ts +4 -0
- package/dist/api.js +192 -18
- package/dist/app.js +53 -46
- package/dist/{catalog-runtime-WMPC4J4U.js → catalog-runtime-UHHLU53I.js} +5 -5
- package/dist/chat/agent/prompt.d.ts +6 -2
- package/dist/chat/agent/request.d.ts +2 -1
- package/dist/chat/agent/resume.d.ts +2 -0
- package/dist/chat/agent/tools.d.ts +1 -0
- package/dist/chat/conversations/history.d.ts +22 -1
- package/dist/chat/conversations/projection.d.ts +6 -0
- package/dist/chat/mcp/client.d.ts +4 -0
- package/dist/chat/mcp/errors.d.ts +22 -1
- package/dist/chat/oauth-response.d.ts +21 -0
- package/dist/chat/plugins/prompt.d.ts +9 -0
- package/dist/chat/plugins/types.d.ts +2 -21
- package/dist/chat/plugins/user-pages.d.ts +15 -0
- package/dist/chat/plugins/viewer-actors.d.ts +6 -0
- package/dist/chat/prompt.d.ts +1 -2
- package/dist/chat/sandbox/paths.d.ts +2 -0
- package/dist/chat/sandbox/sandbox.d.ts +3 -1
- package/dist/chat/sandbox/snapshot/install.d.ts +6 -0
- package/dist/chat/sandbox/snapshot/profile.d.ts +14 -0
- package/dist/chat/sandbox/snapshot/resolve.d.ts +22 -0
- package/dist/chat/sandbox/snapshot/span.d.ts +2 -0
- package/dist/chat/sandbox/workspace.d.ts +1 -0
- package/dist/chat/services/context-compaction-marker.d.ts +1 -0
- package/dist/chat/services/turn-session-record.d.ts +2 -0
- package/dist/chat/skills.d.ts +1 -2
- package/dist/chat/state/turn-session.d.ts +2 -0
- package/dist/chat/tools/index.d.ts +4 -1
- package/dist/chat/tools/query-conversation-events.d.ts +1 -1
- package/dist/chat/tools/sandbox/file-utils.d.ts +11 -0
- package/dist/chat/tools/sandbox/find-files.d.ts +2 -1
- package/dist/chat/tools/sandbox/grep.d.ts +2 -1
- package/dist/chat/tools/sandbox/view-image.d.ts +11 -0
- package/dist/chat/tools/types.d.ts +7 -0
- package/dist/{chunk-XA3VLFPK.js → chunk-3LGCVRBQ.js} +154 -20
- package/dist/{chunk-JPKGW6L6.js → chunk-3MUSSFMP.js} +78 -39
- package/dist/{chunk-7CSVG54J.js → chunk-3N3FMCKN.js} +30 -2
- package/dist/{chunk-VBMMMVVQ.js → chunk-4LPPH7Y6.js} +405 -446
- package/dist/{chunk-2YYPXQNQ.js → chunk-4SOQPUF4.js} +1 -1
- package/dist/{chunk-H35BDAUS.js → chunk-6BGUAOYP.js} +8 -8
- package/dist/{chunk-GDAXWEIJ.js → chunk-6K2QE2UI.js} +731 -424
- package/dist/{chunk-DLLW5ITL.js → chunk-7GADTCNS.js} +30 -9
- package/dist/{chunk-TC4ZEM2D.js → chunk-7SM3P4XR.js} +28 -1
- package/dist/{chunk-WKU7TY22.js → chunk-FIANEE7Z.js} +3 -1
- package/dist/{chunk-RXRQJF7Y.js → chunk-GCGJGSQL.js} +21 -0
- package/dist/{chunk-ZYHLNTC2.js → chunk-GT2ZUALJ.js} +584 -25
- package/dist/{chunk-KNCELO2M.js → chunk-HN6LFYG3.js} +1 -1
- package/dist/{chunk-N3C4XCRE.js → chunk-HWUBBHLK.js} +1 -1
- package/dist/{chunk-BFYVIOTS.js → chunk-KC5EE27Y.js} +13 -3
- package/dist/chunk-L72GDKSN.js +37 -0
- package/dist/{chunk-F3SWGSPG.js → chunk-MLZFK4KP.js} +1 -1
- package/dist/{chunk-RYFAVWP3.js → chunk-PJPKHGLG.js} +34 -15
- package/dist/{chunk-CY7NXM32.js → chunk-PWRFTDTY.js} +4 -2
- package/dist/{chunk-WLT72MRP.js → chunk-QWCF4RAC.js} +1 -1
- package/dist/{chunk-CGXLCMWI.js → chunk-S6R23MFB.js} +117 -17
- package/dist/{chunk-OY2PQQ4P.js → chunk-UXLYAVYL.js} +1 -1
- package/dist/cli/chat.js +23 -24
- package/dist/cli/check.js +6 -6
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +12 -14
- package/dist/cli/{snapshot-warmup.js → snapshot-create.js} +17 -20
- package/dist/cli/upgrade.js +7 -7
- package/dist/db/schema/stats.d.ts +93 -0
- package/dist/db/schema.d.ts +94 -1
- package/dist/{db-FH4R5ZQ4.js → db-PMRHFSYL.js} +6 -6
- package/dist/handlers/oauth-html.d.ts +4 -2
- package/dist/instrumentation.js +2 -2
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-C2SSJ3FQ.js → oauth-callback-server-QJUC7S4X.js} +15 -17
- package/dist/{plugins-IUJA4VBU.js → plugins-HW4VWW6G.js} +1 -1
- package/dist/{runner-NAALFIXM.js → runner-PZMV6JQW.js} +10 -12
- package/dist/{skills-UN2JXYYF.js → skills-W4JETYSV.js} +6 -6
- package/dist/stats.d.ts +18 -0
- package/dist/{validation-EEF4ROHR.js → validation-IJLNHZSU.js} +6 -6
- package/dist/version.js +1 -1
- package/migrations/0011_sour_golden_guardian.sql +8 -0
- package/migrations/meta/0011_snapshot.json +1195 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -5
- package/dist/chat/sandbox/runtime-dependency-snapshots.d.ts +0 -20
- package/dist/chunk-4ZNGQH7C.js +0 -25
- package/dist/chunk-FAEALXEH.js +0 -517
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-7CSVG54J.js";
|
|
2
|
+
getDb
|
|
3
|
+
} from "./chunk-KC5EE27Y.js";
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
isConversationScopedChannel,
|
|
9
|
-
isDmChannel,
|
|
10
|
-
normalizeSlackConversationId
|
|
11
|
-
} from "./chunk-FAEALXEH.js";
|
|
5
|
+
juniorConversationAnnotations
|
|
6
|
+
} from "./chunk-GCGJGSQL.js";
|
|
12
7
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
SANDBOX_WORKSPACE_ROOT,
|
|
9
|
+
getStateAdapter,
|
|
10
|
+
sleep
|
|
11
|
+
} from "./chunk-3N3FMCKN.js";
|
|
15
12
|
import {
|
|
16
13
|
botConfig,
|
|
17
14
|
completeObject,
|
|
18
15
|
embedTexts,
|
|
16
|
+
getSlackBotToken,
|
|
19
17
|
standardModelId
|
|
20
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-PWRFTDTY.js";
|
|
21
19
|
import {
|
|
22
20
|
isActorUserId,
|
|
23
21
|
parseActorUserId
|
|
@@ -25,8 +23,11 @@ import {
|
|
|
25
23
|
import {
|
|
26
24
|
logException,
|
|
27
25
|
logInfo,
|
|
28
|
-
logWarn
|
|
29
|
-
|
|
26
|
+
logWarn,
|
|
27
|
+
setSpanAttributes,
|
|
28
|
+
setSpanStatus,
|
|
29
|
+
withSpan
|
|
30
|
+
} from "./chunk-MLZFK4KP.js";
|
|
30
31
|
import {
|
|
31
32
|
parseSlackChannelReferenceId,
|
|
32
33
|
parseSlackMessageTs,
|
|
@@ -35,10 +36,72 @@ import {
|
|
|
35
36
|
|
|
36
37
|
// src/chat/plugins/agent-hooks.ts
|
|
37
38
|
import {
|
|
39
|
+
promptContextSchema,
|
|
38
40
|
promptMessageSchema,
|
|
39
41
|
resourceEventSchema
|
|
40
42
|
} from "@sentry/junior-plugin-api";
|
|
41
43
|
|
|
44
|
+
// src/chat/plugins/annotations.ts
|
|
45
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
46
|
+
import {
|
|
47
|
+
conversationAnnotationInputSchema
|
|
48
|
+
} from "@sentry/junior-plugin-api";
|
|
49
|
+
function createPluginAnnotations(args) {
|
|
50
|
+
return {
|
|
51
|
+
async upsert(input) {
|
|
52
|
+
const annotation = conversationAnnotationInputSchema.parse(input);
|
|
53
|
+
const now = /* @__PURE__ */ new Date();
|
|
54
|
+
await args.db.insert(juniorConversationAnnotations).values({
|
|
55
|
+
annotation,
|
|
56
|
+
conversationId: args.conversationId,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
key: annotation.key,
|
|
59
|
+
kind: annotation.kind,
|
|
60
|
+
plugin: args.plugin,
|
|
61
|
+
updatedAt: now
|
|
62
|
+
}).onConflictDoUpdate({
|
|
63
|
+
target: [
|
|
64
|
+
juniorConversationAnnotations.conversationId,
|
|
65
|
+
juniorConversationAnnotations.plugin,
|
|
66
|
+
juniorConversationAnnotations.kind,
|
|
67
|
+
juniorConversationAnnotations.key
|
|
68
|
+
],
|
|
69
|
+
set: { annotation, updatedAt: now }
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
async remove(kind, key) {
|
|
73
|
+
await args.db.delete(juniorConversationAnnotations).where(
|
|
74
|
+
and(
|
|
75
|
+
eq(
|
|
76
|
+
juniorConversationAnnotations.conversationId,
|
|
77
|
+
args.conversationId
|
|
78
|
+
),
|
|
79
|
+
eq(juniorConversationAnnotations.plugin, args.plugin),
|
|
80
|
+
eq(juniorConversationAnnotations.kind, kind),
|
|
81
|
+
eq(juniorConversationAnnotations.key, key)
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
async list() {
|
|
86
|
+
return (await listConversationAnnotations(args.db, args.conversationId)).filter((a) => a.plugin === args.plugin);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async function listConversationAnnotations(db, conversationId) {
|
|
91
|
+
const rows = await db.select().from(juniorConversationAnnotations).where(eq(juniorConversationAnnotations.conversationId, conversationId)).orderBy(desc(juniorConversationAnnotations.updatedAt));
|
|
92
|
+
return rows.flatMap((row) => {
|
|
93
|
+
const parsed = conversationAnnotationInputSchema.safeParse(row.annotation);
|
|
94
|
+
return parsed.success ? [
|
|
95
|
+
{
|
|
96
|
+
...parsed.data,
|
|
97
|
+
plugin: row.plugin,
|
|
98
|
+
createdAt: row.createdAt.toISOString(),
|
|
99
|
+
updatedAt: row.updatedAt.toISOString()
|
|
100
|
+
}
|
|
101
|
+
] : [];
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
42
105
|
// src/chat/plugins/logging.ts
|
|
43
106
|
function createPluginLogger(plugin) {
|
|
44
107
|
return {
|
|
@@ -260,6 +323,428 @@ function getSlackToolContext(context) {
|
|
|
260
323
|
|
|
261
324
|
// src/chat/credentials/subject.ts
|
|
262
325
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
326
|
+
|
|
327
|
+
// src/chat/slack/client.ts
|
|
328
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
329
|
+
import { WebClient } from "@slack/web-api";
|
|
330
|
+
|
|
331
|
+
// src/chat/slack/workspace-context.ts
|
|
332
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
333
|
+
var workspaceTeamIdStorage = new AsyncLocalStorage();
|
|
334
|
+
function runWithWorkspaceTeamId(teamId, fn) {
|
|
335
|
+
if (!teamId) return fn();
|
|
336
|
+
return workspaceTeamIdStorage.run(teamId, fn);
|
|
337
|
+
}
|
|
338
|
+
function getWorkspaceTeamId() {
|
|
339
|
+
return workspaceTeamIdStorage.getStore();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/chat/slack/client.ts
|
|
343
|
+
var SlackActionError = class extends Error {
|
|
344
|
+
code;
|
|
345
|
+
apiError;
|
|
346
|
+
needed;
|
|
347
|
+
provided;
|
|
348
|
+
statusCode;
|
|
349
|
+
requestId;
|
|
350
|
+
errorData;
|
|
351
|
+
retryAfterSeconds;
|
|
352
|
+
detail;
|
|
353
|
+
detailLine;
|
|
354
|
+
detailRule;
|
|
355
|
+
constructor(message, code, options = {}) {
|
|
356
|
+
super(message);
|
|
357
|
+
this.name = "SlackActionError";
|
|
358
|
+
this.code = code;
|
|
359
|
+
this.apiError = options.apiError;
|
|
360
|
+
this.needed = options.needed;
|
|
361
|
+
this.provided = options.provided;
|
|
362
|
+
this.statusCode = options.statusCode;
|
|
363
|
+
this.requestId = options.requestId;
|
|
364
|
+
this.errorData = options.errorData;
|
|
365
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
366
|
+
this.detail = options.detail;
|
|
367
|
+
this.detailLine = options.detailLine;
|
|
368
|
+
this.detailRule = options.detailRule;
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
function serializeSlackErrorData(data) {
|
|
372
|
+
if (!data || typeof data !== "object") {
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
const filtered = Object.fromEntries(
|
|
376
|
+
Object.entries(data).filter(
|
|
377
|
+
([key]) => key !== "error"
|
|
378
|
+
)
|
|
379
|
+
);
|
|
380
|
+
if (Object.keys(filtered).length === 0) {
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const serialized = JSON.stringify(filtered);
|
|
385
|
+
return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
|
|
386
|
+
} catch {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function getHeaderString(headers, name) {
|
|
391
|
+
if (!headers || typeof headers !== "object") {
|
|
392
|
+
return void 0;
|
|
393
|
+
}
|
|
394
|
+
const key = name.toLowerCase();
|
|
395
|
+
const entries = headers;
|
|
396
|
+
for (const [entryKey, value] of Object.entries(entries)) {
|
|
397
|
+
if (entryKey.toLowerCase() !== key) continue;
|
|
398
|
+
if (typeof value === "string") return value;
|
|
399
|
+
if (Array.isArray(value)) {
|
|
400
|
+
const first = value.find((entry) => typeof entry === "string");
|
|
401
|
+
return typeof first === "string" ? first : void 0;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return void 0;
|
|
405
|
+
}
|
|
406
|
+
function parseSlackCanvasDetail(detail) {
|
|
407
|
+
if (typeof detail !== "string") {
|
|
408
|
+
return {};
|
|
409
|
+
}
|
|
410
|
+
const trimmed = detail.trim();
|
|
411
|
+
if (!trimmed) {
|
|
412
|
+
return {};
|
|
413
|
+
}
|
|
414
|
+
const parsed = {
|
|
415
|
+
detail: trimmed
|
|
416
|
+
};
|
|
417
|
+
const lineMatch = trimmed.match(/line\s+(\d+):/i);
|
|
418
|
+
if (lineMatch) {
|
|
419
|
+
const line = Number.parseInt(lineMatch[1] ?? "", 10);
|
|
420
|
+
if (Number.isFinite(line)) {
|
|
421
|
+
parsed.detailLine = line;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (/unsupported heading depth/i.test(trimmed)) {
|
|
425
|
+
parsed.detailRule = "unsupported_heading_depth";
|
|
426
|
+
}
|
|
427
|
+
return parsed;
|
|
428
|
+
}
|
|
429
|
+
var installationTokenStorage = new AsyncLocalStorage2();
|
|
430
|
+
var clientsByToken = /* @__PURE__ */ new Map();
|
|
431
|
+
function runWithSlackInstallationToken(token, fn) {
|
|
432
|
+
const trimmed = token.trim();
|
|
433
|
+
if (!trimmed) {
|
|
434
|
+
throw new SlackActionError(
|
|
435
|
+
"Slack installation token binding requires a non-empty token",
|
|
436
|
+
"missing_token"
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
return installationTokenStorage.run({ token: trimmed }, fn);
|
|
440
|
+
}
|
|
441
|
+
function resolveSlackToken() {
|
|
442
|
+
const ambientToken = installationTokenStorage.getStore()?.token;
|
|
443
|
+
if (ambientToken) {
|
|
444
|
+
return ambientToken;
|
|
445
|
+
}
|
|
446
|
+
const envToken = getSlackBotToken();
|
|
447
|
+
if (envToken) {
|
|
448
|
+
return envToken;
|
|
449
|
+
}
|
|
450
|
+
const teamId = getWorkspaceTeamId();
|
|
451
|
+
if (teamId) {
|
|
452
|
+
throw new SlackActionError(
|
|
453
|
+
`Slack call is scoped to workspace ${teamId} but no installation token is bound and no default bot token is configured`,
|
|
454
|
+
"missing_token"
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
throw new SlackActionError(
|
|
458
|
+
"SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack Web API actions in this service",
|
|
459
|
+
"missing_token"
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
function normalizeSlackConversationId(channelId) {
|
|
463
|
+
return parseSlackChannelReferenceId(channelId);
|
|
464
|
+
}
|
|
465
|
+
function getClient() {
|
|
466
|
+
const token = resolveSlackToken();
|
|
467
|
+
let cached = clientsByToken.get(token);
|
|
468
|
+
if (!cached) {
|
|
469
|
+
cached = new WebClient(token, {
|
|
470
|
+
retryConfig: { retries: 0 },
|
|
471
|
+
rejectRateLimitedCalls: true
|
|
472
|
+
});
|
|
473
|
+
clientsByToken.set(token, cached);
|
|
474
|
+
}
|
|
475
|
+
return cached;
|
|
476
|
+
}
|
|
477
|
+
function mapSlackError(error) {
|
|
478
|
+
if (error instanceof SlackActionError) {
|
|
479
|
+
return error;
|
|
480
|
+
}
|
|
481
|
+
const candidate = error;
|
|
482
|
+
const apiError = candidate.data?.error;
|
|
483
|
+
const message = candidate.message ?? "Slack action failed";
|
|
484
|
+
const baseOptions = {
|
|
485
|
+
apiError,
|
|
486
|
+
statusCode: candidate.statusCode,
|
|
487
|
+
requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
|
|
488
|
+
errorData: serializeSlackErrorData(candidate.data),
|
|
489
|
+
...parseSlackCanvasDetail(candidate.data?.detail)
|
|
490
|
+
};
|
|
491
|
+
if (apiError === "missing_scope") {
|
|
492
|
+
return new SlackActionError(message, "missing_scope", {
|
|
493
|
+
...baseOptions,
|
|
494
|
+
needed: candidate.data?.needed,
|
|
495
|
+
provided: candidate.data?.provided
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
if (apiError === "not_in_channel") {
|
|
499
|
+
return new SlackActionError(message, "not_in_channel", baseOptions);
|
|
500
|
+
}
|
|
501
|
+
if (apiError === "restricted_action_read_only_channel") {
|
|
502
|
+
return new SlackActionError(message, "read_only_channel", baseOptions);
|
|
503
|
+
}
|
|
504
|
+
if (apiError === "already_reacted") {
|
|
505
|
+
return new SlackActionError(message, "already_reacted", baseOptions);
|
|
506
|
+
}
|
|
507
|
+
if (apiError === "no_reaction") {
|
|
508
|
+
return new SlackActionError(message, "no_reaction", baseOptions);
|
|
509
|
+
}
|
|
510
|
+
if (apiError === "invalid_arguments") {
|
|
511
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
512
|
+
}
|
|
513
|
+
if (apiError === "invalid_cursor") {
|
|
514
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
515
|
+
}
|
|
516
|
+
if (apiError === "invalid_name") {
|
|
517
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
518
|
+
}
|
|
519
|
+
if (apiError === "not_found" || apiError === "channel_not_found" || apiError === "message_not_found") {
|
|
520
|
+
return new SlackActionError(message, "not_found", baseOptions);
|
|
521
|
+
}
|
|
522
|
+
if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
|
|
523
|
+
return new SlackActionError(message, "feature_unavailable", baseOptions);
|
|
524
|
+
}
|
|
525
|
+
if (apiError === "canvas_creation_failed") {
|
|
526
|
+
return new SlackActionError(message, "canvas_creation_failed", baseOptions);
|
|
527
|
+
}
|
|
528
|
+
if (apiError === "canvas_editing_failed") {
|
|
529
|
+
return new SlackActionError(message, "canvas_editing_failed", baseOptions);
|
|
530
|
+
}
|
|
531
|
+
if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
|
|
532
|
+
return new SlackActionError(message, "rate_limited", {
|
|
533
|
+
...baseOptions,
|
|
534
|
+
retryAfterSeconds: candidate.retryAfter
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return new SlackActionError(message, "internal_error", baseOptions);
|
|
538
|
+
}
|
|
539
|
+
var MAX_RETRY_DELAY_MS = 1e4;
|
|
540
|
+
var MAX_TOTAL_RETRY_DELAY_MS = 2e4;
|
|
541
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
542
|
+
"ECONNREFUSED",
|
|
543
|
+
"ECONNRESET",
|
|
544
|
+
"ENOTFOUND",
|
|
545
|
+
"EAI_AGAIN"
|
|
546
|
+
]);
|
|
547
|
+
var TIMEOUT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
548
|
+
"ETIMEDOUT",
|
|
549
|
+
"ECONNABORTED",
|
|
550
|
+
"ESOCKETTIMEDOUT"
|
|
551
|
+
]);
|
|
552
|
+
function findNetworkErrorCode(error, depth = 0) {
|
|
553
|
+
if (!error || typeof error !== "object" || depth > 4) {
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
const candidate = error;
|
|
557
|
+
if (typeof candidate.code === "string" && (CONNECTION_ERROR_CODES.has(candidate.code) || TIMEOUT_ERROR_CODES.has(candidate.code))) {
|
|
558
|
+
return candidate.code;
|
|
559
|
+
}
|
|
560
|
+
return findNetworkErrorCode(candidate.original, depth + 1) ?? findNetworkErrorCode(candidate.cause, depth + 1);
|
|
561
|
+
}
|
|
562
|
+
function hasSocketHangUpMessage(error) {
|
|
563
|
+
return error instanceof Error && error.message.toLowerCase().includes("socket hang up");
|
|
564
|
+
}
|
|
565
|
+
function classifySlackRetry(raw, mapped) {
|
|
566
|
+
if (mapped.code === "rate_limited") {
|
|
567
|
+
return "rate_limited";
|
|
568
|
+
}
|
|
569
|
+
if (mapped.statusCode !== void 0 && mapped.statusCode >= 500) {
|
|
570
|
+
return "server_error";
|
|
571
|
+
}
|
|
572
|
+
const networkCode = findNetworkErrorCode(raw);
|
|
573
|
+
if (networkCode && CONNECTION_ERROR_CODES.has(networkCode)) {
|
|
574
|
+
return "connection";
|
|
575
|
+
}
|
|
576
|
+
if (networkCode && TIMEOUT_ERROR_CODES.has(networkCode)) {
|
|
577
|
+
return "timeout";
|
|
578
|
+
}
|
|
579
|
+
if (hasSocketHangUpMessage(raw)) {
|
|
580
|
+
return "connection";
|
|
581
|
+
}
|
|
582
|
+
return "none";
|
|
583
|
+
}
|
|
584
|
+
async function withSlackRetries(task, maxAttempts = 3, context = {}) {
|
|
585
|
+
let attempt = 0;
|
|
586
|
+
let totalDelayMs = 0;
|
|
587
|
+
const action = context.action ?? "unknown";
|
|
588
|
+
while (attempt < maxAttempts) {
|
|
589
|
+
attempt += 1;
|
|
590
|
+
const attemptNumber = attempt;
|
|
591
|
+
try {
|
|
592
|
+
return await withSpan(
|
|
593
|
+
`POST slack.com/api/${action}`,
|
|
594
|
+
"http.client",
|
|
595
|
+
{},
|
|
596
|
+
async () => {
|
|
597
|
+
try {
|
|
598
|
+
return await task();
|
|
599
|
+
} catch (error) {
|
|
600
|
+
const mapped = mapSlackError(error);
|
|
601
|
+
const errorAttrs = {
|
|
602
|
+
"error.type": mapped.code
|
|
603
|
+
};
|
|
604
|
+
if (mapped.apiError) {
|
|
605
|
+
errorAttrs["app.slack.api_error_code"] = mapped.apiError;
|
|
606
|
+
}
|
|
607
|
+
if (mapped.code === "rate_limited") {
|
|
608
|
+
errorAttrs["http.response.status_code"] = 429;
|
|
609
|
+
if (mapped.retryAfterSeconds) {
|
|
610
|
+
errorAttrs["app.slack.retry_after_ms"] = mapped.retryAfterSeconds * 1e3;
|
|
611
|
+
}
|
|
612
|
+
} else if (mapped.statusCode != null) {
|
|
613
|
+
errorAttrs["http.response.status_code"] = mapped.statusCode;
|
|
614
|
+
}
|
|
615
|
+
setSpanAttributes(errorAttrs);
|
|
616
|
+
setSpanStatus("error");
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"http.request.method": "POST",
|
|
622
|
+
"server.address": "slack.com",
|
|
623
|
+
"url.scheme": "https",
|
|
624
|
+
"url.path": `/api/${action}`,
|
|
625
|
+
"app.slack.method": action,
|
|
626
|
+
"app.retry.max_attempts": maxAttempts,
|
|
627
|
+
...attemptNumber > 1 ? { "http.resend_count": attemptNumber - 1 } : {},
|
|
628
|
+
...context.attributes ?? {},
|
|
629
|
+
...context.spanAttributes ?? {}
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
} catch (error) {
|
|
633
|
+
const mapped = mapSlackError(error);
|
|
634
|
+
const retryClass = classifySlackRetry(error, mapped);
|
|
635
|
+
const isRetryable = retryClass === "rate_limited" || retryClass === "connection" || retryClass === "server_error" || retryClass === "timeout" && context.idempotent === true;
|
|
636
|
+
const remainingDelayBudgetMs = MAX_TOTAL_RETRY_DELAY_MS - totalDelayMs;
|
|
637
|
+
const baseLogAttributes = {
|
|
638
|
+
"app.slack.action": action,
|
|
639
|
+
"app.slack.error_code": mapped.code,
|
|
640
|
+
...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
|
|
641
|
+
...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
|
|
642
|
+
...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
|
|
643
|
+
...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
|
|
644
|
+
...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
|
|
645
|
+
...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
|
|
646
|
+
...context.attributes ?? {}
|
|
647
|
+
};
|
|
648
|
+
if (!isRetryable || attempt >= maxAttempts || remainingDelayBudgetMs <= 0) {
|
|
649
|
+
logWarn(
|
|
650
|
+
"slack_action_failed",
|
|
651
|
+
{},
|
|
652
|
+
{
|
|
653
|
+
...baseLogAttributes,
|
|
654
|
+
...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
|
|
655
|
+
},
|
|
656
|
+
"Slack action failed"
|
|
657
|
+
);
|
|
658
|
+
throw mapped;
|
|
659
|
+
}
|
|
660
|
+
logWarn(
|
|
661
|
+
"slack_action_retrying",
|
|
662
|
+
{},
|
|
663
|
+
{
|
|
664
|
+
...baseLogAttributes,
|
|
665
|
+
"app.slack.retry_attempt": attempt,
|
|
666
|
+
"app.slack.retry_class": retryClass
|
|
667
|
+
},
|
|
668
|
+
"Retrying Slack action after transient failure"
|
|
669
|
+
);
|
|
670
|
+
const retryAfterMs = retryClass === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
|
|
671
|
+
const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
|
|
672
|
+
const delayMs = Math.min(
|
|
673
|
+
retryAfterMs ?? backoffMs,
|
|
674
|
+
MAX_RETRY_DELAY_MS,
|
|
675
|
+
remainingDelayBudgetMs
|
|
676
|
+
);
|
|
677
|
+
totalDelayMs += delayMs;
|
|
678
|
+
await sleep(delayMs);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
throw new SlackActionError(
|
|
682
|
+
"Slack action exhausted retries",
|
|
683
|
+
"internal_error"
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
function getSlackClient() {
|
|
687
|
+
return getClient();
|
|
688
|
+
}
|
|
689
|
+
function isDmChannel(channelId) {
|
|
690
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
691
|
+
return Boolean(normalized && normalized.startsWith("D"));
|
|
692
|
+
}
|
|
693
|
+
function isConversationScopedChannel(channelId) {
|
|
694
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
695
|
+
if (!normalized) return false;
|
|
696
|
+
return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
|
|
697
|
+
}
|
|
698
|
+
function isConversationChannel(channelId) {
|
|
699
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
700
|
+
if (!normalized) return false;
|
|
701
|
+
return normalized.startsWith("C") || normalized.startsWith("G");
|
|
702
|
+
}
|
|
703
|
+
async function getFilePermalink(fileId) {
|
|
704
|
+
const client = getClient();
|
|
705
|
+
const response = await withSlackRetries(
|
|
706
|
+
() => client.files.info({
|
|
707
|
+
file: fileId
|
|
708
|
+
}),
|
|
709
|
+
3,
|
|
710
|
+
{
|
|
711
|
+
action: "files.info",
|
|
712
|
+
idempotent: true,
|
|
713
|
+
spanAttributes: { "app.slack.file_id": fileId }
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
return response.file?.permalink;
|
|
717
|
+
}
|
|
718
|
+
async function downloadPrivateSlackFile(url) {
|
|
719
|
+
const token = resolveSlackToken();
|
|
720
|
+
return withSpan(
|
|
721
|
+
"GET files.slack.com",
|
|
722
|
+
"http.client",
|
|
723
|
+
{},
|
|
724
|
+
async () => {
|
|
725
|
+
const response = await fetch(url, {
|
|
726
|
+
headers: {
|
|
727
|
+
Authorization: `Bearer ${token}`
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
setSpanAttributes({ "http.response.status_code": response.status });
|
|
731
|
+
if (!response.ok) {
|
|
732
|
+
setSpanAttributes({ "error.type": String(response.status) });
|
|
733
|
+
setSpanStatus("error");
|
|
734
|
+
throw new Error(`Slack file download failed: ${response.status}`);
|
|
735
|
+
}
|
|
736
|
+
return Buffer.from(await response.arrayBuffer());
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
"http.request.method": "GET",
|
|
740
|
+
"server.address": "files.slack.com",
|
|
741
|
+
"url.scheme": "https",
|
|
742
|
+
"app.slack.method": "files.download"
|
|
743
|
+
}
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/chat/credentials/subject.ts
|
|
263
748
|
var CREDENTIAL_SUBJECT_HMAC_CONTEXT = "junior.credential_subject.v1";
|
|
264
749
|
var CREDENTIAL_SUBJECT_SIGNATURE_VERSION = "v1";
|
|
265
750
|
function getCredentialSubjectSecret() {
|
|
@@ -426,8 +911,9 @@ var PLUGIN_ROUTE_METHODS = /* @__PURE__ */ new Set([
|
|
|
426
911
|
"ALL"
|
|
427
912
|
]);
|
|
428
913
|
var PLUGIN_PROMPT_CONTRIBUTION_TOTAL_MAX_CHARS = 16e3;
|
|
914
|
+
var PLUGIN_PROMPT_CONTEXT_MAX_BYTES = 8e3;
|
|
915
|
+
var PLUGIN_PROMPT_CONTEXT_TOTAL_MAX_BYTES = 16e3;
|
|
429
916
|
var systemPromptMessageArraySchema = z.array(promptMessageSchema);
|
|
430
|
-
var userPromptMessageArraySchema = z.array(promptMessageSchema);
|
|
431
917
|
function isRecord(value) {
|
|
432
918
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
433
919
|
}
|
|
@@ -492,6 +978,53 @@ function toPromptContributionContext(args) {
|
|
|
492
978
|
text: args.message.text
|
|
493
979
|
};
|
|
494
980
|
}
|
|
981
|
+
function normalizePromptContext(value) {
|
|
982
|
+
const serialized = JSON.stringify(value);
|
|
983
|
+
if (serialized === void 0 || Buffer.byteLength(serialized, "utf8") > PLUGIN_PROMPT_CONTEXT_MAX_BYTES) {
|
|
984
|
+
throw new TypeError("Plugin prompt context exceeded its serialized budget");
|
|
985
|
+
}
|
|
986
|
+
const parsed = JSON.parse(serialized);
|
|
987
|
+
if (!isRecord(parsed)) {
|
|
988
|
+
throw new TypeError("Plugin prompt context must be a JSON object");
|
|
989
|
+
}
|
|
990
|
+
return parsed;
|
|
991
|
+
}
|
|
992
|
+
function toUserPromptContributionContext(args) {
|
|
993
|
+
const message = promptMessageSchema.safeParse(args.value);
|
|
994
|
+
if (message.success) {
|
|
995
|
+
return toPromptContributionContext({
|
|
996
|
+
hookName: "userPrompt",
|
|
997
|
+
index: args.index,
|
|
998
|
+
message: message.data,
|
|
999
|
+
pluginName: args.pluginName
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
if (!isRecord(args.value) || typeof args.value.renderPrompt !== "function") {
|
|
1003
|
+
return void 0;
|
|
1004
|
+
}
|
|
1005
|
+
const context = promptContextSchema.safeParse(args.value.context);
|
|
1006
|
+
if (!context.success) {
|
|
1007
|
+
return void 0;
|
|
1008
|
+
}
|
|
1009
|
+
const text = promptMessageSchema.safeParse({
|
|
1010
|
+
text: args.value.renderPrompt()
|
|
1011
|
+
});
|
|
1012
|
+
if (!text.success) {
|
|
1013
|
+
return void 0;
|
|
1014
|
+
}
|
|
1015
|
+
return {
|
|
1016
|
+
id: `userPrompt:${args.index}`,
|
|
1017
|
+
pluginName: args.pluginName,
|
|
1018
|
+
text: text.data.text,
|
|
1019
|
+
context: {
|
|
1020
|
+
content: normalizePromptContext(context.data.content),
|
|
1021
|
+
kind: context.data.kind,
|
|
1022
|
+
loadedAtMs: Date.now(),
|
|
1023
|
+
pluginName: args.pluginName,
|
|
1024
|
+
version: context.data.version
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
495
1028
|
function logInvalidPromptContributions(args) {
|
|
496
1029
|
logWarn(
|
|
497
1030
|
"plugin_prompt_contribution_result_invalid",
|
|
@@ -606,6 +1139,7 @@ async function getPluginSystemPromptContributions(source) {
|
|
|
606
1139
|
async function getPluginUserPromptContributions(args) {
|
|
607
1140
|
const contributions = [];
|
|
608
1141
|
let totalChars = 0;
|
|
1142
|
+
let totalContextBytes = 0;
|
|
609
1143
|
for (const plugin of getPlugins()) {
|
|
610
1144
|
const pluginName = plugin.manifest.name;
|
|
611
1145
|
const hook = plugin.hooks?.userPrompt;
|
|
@@ -619,27 +1153,38 @@ async function getPluginUserPromptContributions(args) {
|
|
|
619
1153
|
if (rawResult === void 0) {
|
|
620
1154
|
continue;
|
|
621
1155
|
}
|
|
622
|
-
|
|
623
|
-
if (!result.success) {
|
|
1156
|
+
if (!Array.isArray(rawResult)) {
|
|
624
1157
|
logInvalidPromptContributions({
|
|
625
1158
|
hookName: "userPrompt",
|
|
626
1159
|
pluginName
|
|
627
1160
|
});
|
|
628
1161
|
continue;
|
|
629
1162
|
}
|
|
630
|
-
const acceptedContributions =
|
|
631
|
-
(
|
|
1163
|
+
const acceptedContributions = rawResult.map(
|
|
1164
|
+
(value, index) => toUserPromptContributionContext({ index, pluginName, value })
|
|
1165
|
+
);
|
|
1166
|
+
if (acceptedContributions.some((contribution) => !contribution)) {
|
|
1167
|
+
logInvalidPromptContributions({
|
|
632
1168
|
hookName: "userPrompt",
|
|
633
|
-
index,
|
|
634
|
-
message,
|
|
635
1169
|
pluginName
|
|
636
|
-
})
|
|
1170
|
+
});
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
const validContributions = acceptedContributions.filter(
|
|
1174
|
+
(contribution) => contribution !== void 0
|
|
637
1175
|
);
|
|
638
|
-
const pluginContributionChars =
|
|
1176
|
+
const pluginContributionChars = validContributions.reduce(
|
|
639
1177
|
(sum, contribution) => sum + contribution.text.length,
|
|
640
1178
|
0
|
|
641
1179
|
);
|
|
642
|
-
|
|
1180
|
+
const pluginContextBytes = validContributions.reduce(
|
|
1181
|
+
(sum, contribution) => sum + (contribution.context ? Buffer.byteLength(
|
|
1182
|
+
JSON.stringify(contribution.context.content),
|
|
1183
|
+
"utf8"
|
|
1184
|
+
) : 0),
|
|
1185
|
+
0
|
|
1186
|
+
);
|
|
1187
|
+
if (totalChars + pluginContributionChars > PLUGIN_PROMPT_CONTRIBUTION_TOTAL_MAX_CHARS || totalContextBytes + pluginContextBytes > PLUGIN_PROMPT_CONTEXT_TOTAL_MAX_BYTES) {
|
|
643
1188
|
logWarn(
|
|
644
1189
|
"plugin_user_prompt_contribution_budget_exceeded",
|
|
645
1190
|
{},
|
|
@@ -651,7 +1196,8 @@ async function getPluginUserPromptContributions(args) {
|
|
|
651
1196
|
continue;
|
|
652
1197
|
}
|
|
653
1198
|
totalChars += pluginContributionChars;
|
|
654
|
-
|
|
1199
|
+
totalContextBytes += pluginContextBytes;
|
|
1200
|
+
contributions.push(...validContributions);
|
|
655
1201
|
} catch (error) {
|
|
656
1202
|
logWarn(
|
|
657
1203
|
"plugin_user_prompt_hook_failed",
|
|
@@ -1299,6 +1845,7 @@ function createPluginHookRunner(input = {}) {
|
|
|
1299
1845
|
}
|
|
1300
1846
|
|
|
1301
1847
|
export {
|
|
1848
|
+
listConversationAnnotations,
|
|
1302
1849
|
createPluginLogger,
|
|
1303
1850
|
createPluginModel,
|
|
1304
1851
|
createPluginEmbedder,
|
|
@@ -1306,6 +1853,18 @@ export {
|
|
|
1306
1853
|
setDashboardConversationLinkOptions,
|
|
1307
1854
|
getDashboardConversationLink,
|
|
1308
1855
|
getSlackToolContext,
|
|
1856
|
+
runWithWorkspaceTeamId,
|
|
1857
|
+
getWorkspaceTeamId,
|
|
1858
|
+
SlackActionError,
|
|
1859
|
+
getHeaderString,
|
|
1860
|
+
runWithSlackInstallationToken,
|
|
1861
|
+
normalizeSlackConversationId,
|
|
1862
|
+
withSlackRetries,
|
|
1863
|
+
getSlackClient,
|
|
1864
|
+
isDmChannel,
|
|
1865
|
+
isConversationScopedChannel,
|
|
1866
|
+
getFilePermalink,
|
|
1867
|
+
downloadPrivateSlackFile,
|
|
1309
1868
|
bindSlackDirectCredentialSubject,
|
|
1310
1869
|
verifySlackDirectCredentialSubject,
|
|
1311
1870
|
bindScheduledTaskCredentialSubject,
|