@sentry/junior 0.136.1 → 0.138.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-2KDSPPO4.js → agent-hooks-SCR74Q3E.js} +11 -9
- package/dist/api/conversations/projection.d.ts +1 -0
- package/dist/api/conversations/stats.d.ts +1 -0
- package/dist/api/schema/conversation.d.ts +2 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +187 -46
- package/dist/app.d.ts +8 -1
- package/dist/app.js +519 -860
- package/dist/{catalog-runtime-WSZQGC4D.js → catalog-runtime-IU3QF4GG.js} +5 -5
- package/dist/chat/agent/request.d.ts +32 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/agent-invocations/errors.d.ts +8 -0
- package/dist/chat/agent-invocations/spawn.d.ts +12 -0
- package/dist/chat/agent-invocations/store.d.ts +44 -0
- package/dist/chat/agent-invocations/types.d.ts +449 -0
- package/dist/chat/agent-invocations/work.d.ts +31 -0
- package/dist/chat/config.d.ts +1 -0
- package/dist/chat/configuration/defaults.d.ts +1 -1
- package/dist/chat/conversations/sql/store.d.ts +8 -0
- package/dist/chat/conversations/store.d.ts +9 -1
- package/dist/chat/experimental.d.ts +16 -0
- package/dist/chat/pi/client.d.ts +1 -8
- package/dist/chat/pi/gateway-auth.d.ts +16 -0
- package/dist/chat/runtime/agent-runner.d.ts +2 -1
- package/dist/chat/sandbox/docker.d.ts +17 -0
- package/dist/chat/services/agent-continue.d.ts +2 -0
- package/dist/chat/services/auth-pause.d.ts +0 -1
- package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/subscribed-reply-router-policy.d.ts +12 -0
- package/dist/chat/services/turn-session-routing.d.ts +17 -0
- package/dist/chat/slack/message.d.ts +6 -1
- package/dist/chat/slack/source-link.d.ts +8 -2
- package/dist/chat/slack/team-domain.d.ts +4 -0
- package/dist/chat/source.d.ts +5 -5
- package/dist/chat/state/turn-session.d.ts +0 -2
- package/dist/chat/task-execution/state.d.ts +23 -11
- package/dist/chat/task-execution/store.d.ts +20 -11
- package/dist/chat/task-execution/worker.d.ts +2 -2
- package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
- package/dist/chat/tools/types.d.ts +2 -0
- package/dist/chunk-3Y47VMHV.js +6 -0
- package/dist/chunk-5IDFPCON.js +46 -0
- package/dist/chunk-5LTJ4ITU.js +1523 -0
- package/dist/{chunk-KLBYCSLJ.js → chunk-5MXWHTCK.js} +17 -434
- package/dist/{chunk-CZZDCH3Q.js → chunk-A7WCHXZV.js} +206 -78
- package/dist/{chunk-4YQ6I6VF.js → chunk-BOLJOV5T.js} +2 -56
- package/dist/{chunk-XCZ6COFO.js → chunk-BYW4B4PR.js} +90 -54
- package/dist/{chunk-JQRTXJHD.js → chunk-C7ZVPO4Z.js} +2 -1
- package/dist/{chunk-MW2QRPGW.js → chunk-DCLLQGS6.js} +119 -50
- package/dist/{chunk-CDVCCOMF.js → chunk-E5UYAIKJ.js} +75 -1475
- package/dist/{chunk-3SUK3UXX.js → chunk-ELKW5FIB.js} +5 -5
- package/dist/chunk-EM225ITJ.js +56 -0
- package/dist/{chunk-Y6JNHTRD.js → chunk-FEUNPTGV.js} +34 -70
- package/dist/{chunk-TYNMSQKA.js → chunk-FNLBTB4K.js} +1 -1
- package/dist/{chunk-TRZSM67C.js → chunk-HCIVH3JS.js} +1 -0
- package/dist/chunk-HIWJWWMT.js +668 -0
- package/dist/chunk-IUFM5JWV.js +24 -0
- package/dist/{chunk-XWPA5KZF.js → chunk-JNNJZURV.js} +1 -1
- package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
- package/dist/{chunk-BG3KWWDJ.js → chunk-KDCHVT25.js} +1 -1
- package/dist/{chunk-VH6MI4L7.js → chunk-KFRHRSCJ.js} +1 -1
- package/dist/chunk-LPVUNO2W.js +320 -0
- package/dist/{chunk-G66RYKRS.js → chunk-N7WARMC6.js} +16 -776
- package/dist/chunk-PB3CXNVL.js +41 -0
- package/dist/chunk-Q6QZTTLS.js +55 -0
- package/dist/{chunk-3QNJOHKG.js → chunk-QS527AV5.js} +1 -1
- package/dist/chunk-R776HKPK.js +81 -0
- package/dist/{chunk-OTJMQYVP.js → chunk-RZIBSCYC.js} +517 -608
- package/dist/chunk-SD3OFRWY.js +714 -0
- package/dist/{chunk-HIRIWTWD.js → chunk-SIPKWSXK.js} +58 -10
- package/dist/chunk-TCS76X5D.js +441 -0
- package/dist/{chunk-UX2TNH5D.js → chunk-TDJCLNDH.js} +3 -3
- package/dist/{chunk-NVVBEJUB.js → chunk-TUUDXEXU.js} +336 -300
- package/dist/{chunk-22Z6MGMK.js → chunk-VPZFVJND.js} +1 -1
- package/dist/{chunk-RFDD3Z2I.js → chunk-YU5QUF67.js} +1 -1
- package/dist/chunk-ZBSMCXKV.js +710 -0
- package/dist/{chunk-FSYXYBWU.js → chunk-ZXNTJJIE.js} +2 -2
- package/dist/cli/chat.js +65 -26
- package/dist/cli/check.js +7 -7
- package/dist/cli/plugins.js +14 -12
- package/dist/cli/snapshot-create.js +10 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/conversation-work-KATFHLG3.js +56 -0
- package/dist/db/schema/agent-invocations.d.ts +544 -0
- package/dist/db/schema.d.ts +543 -1
- package/dist/{db-Z2NNL2JC.js → db-UO2IWXGA.js} +7 -7
- package/dist/experimental-HH6OM6UH.js +13 -0
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -3
- package/dist/{oauth-callback-server-PO3M5JMG.js → oauth-callback-server-MAQI43LX.js} +26 -18
- package/dist/{runner-QHQYZYJA.js → runner-PIUQEXKE.js} +26 -14
- package/dist/{skills-KXMT3DRA.js → skills-4ARJADHQ.js} +6 -6
- package/dist/spawn-G2AWPMCT.js +38 -0
- package/dist/{validation-Y42ULTT6.js → validation-S3EE3HWL.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/work-UARRUAW3.js +46 -0
- package/dist/worker-DOCIRVD3.js +24 -0
- package/migrations/0021_first_warhawk.sql +35 -0
- package/migrations/meta/0021_snapshot.json +2176 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +6 -5
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
juniorConversations,
|
|
6
6
|
juniorIdentities,
|
|
7
7
|
juniorUsers
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DCLLQGS6.js";
|
|
9
9
|
import {
|
|
10
10
|
TURN_REASONING_LEVELS,
|
|
11
11
|
getChatConfig,
|
|
12
12
|
modelProfileSchema
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-BYW4B4PR.js";
|
|
14
14
|
import {
|
|
15
15
|
juniorDestinations
|
|
16
16
|
} from "./chunk-VFUK3X5B.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-F3B2YJ76.js";
|
|
21
21
|
import {
|
|
22
22
|
normalizeIdentityEmail
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JNNJZURV.js";
|
|
24
24
|
|
|
25
25
|
// src/chat/conversations/sql/store.ts
|
|
26
26
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -182,15 +182,12 @@ function normalizeSessionSource(value) {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
const threadTs = value.threadTs?.trim();
|
|
185
|
-
if (!threadTs) {
|
|
186
|
-
return void 0;
|
|
187
|
-
}
|
|
188
185
|
return {
|
|
189
186
|
platform: "slack",
|
|
190
187
|
visibility: value.visibility,
|
|
191
188
|
teamId: value.teamId,
|
|
192
189
|
channelId: value.channelId,
|
|
193
|
-
threadTs
|
|
190
|
+
...threadTs ? { threadTs } : {}
|
|
194
191
|
};
|
|
195
192
|
}
|
|
196
193
|
function parseSessionSource(value) {
|
|
@@ -262,6 +259,12 @@ function privacyFromLocationRow(row) {
|
|
|
262
259
|
|
|
263
260
|
// src/chat/conversations/sql/store.ts
|
|
264
261
|
var CONVERSATION_MUTATION_LOCK_PREFIX = "junior_conversation";
|
|
262
|
+
async function withConversationMutationLock(executor, conversationId, callback) {
|
|
263
|
+
return await executor.withLock(
|
|
264
|
+
`${CONVERSATION_MUTATION_LOCK_PREFIX}:${conversationId}`,
|
|
265
|
+
async () => await executor.transaction(callback)
|
|
266
|
+
);
|
|
267
|
+
}
|
|
265
268
|
function now() {
|
|
266
269
|
return Date.now();
|
|
267
270
|
}
|
|
@@ -552,6 +555,49 @@ var SqlStore = class {
|
|
|
552
555
|
async bindProviderConversation(args) {
|
|
553
556
|
await bindProviderConversation(this.executor, args);
|
|
554
557
|
}
|
|
558
|
+
async createChild(args) {
|
|
559
|
+
const nowMs = args.nowMs ?? now();
|
|
560
|
+
await this.withConversationMutation(args.parentConversationId, async () => {
|
|
561
|
+
await this.withConversationMutation(
|
|
562
|
+
args.childConversationId,
|
|
563
|
+
async () => {
|
|
564
|
+
const existing = await this.get({
|
|
565
|
+
conversationId: args.childConversationId
|
|
566
|
+
});
|
|
567
|
+
if (existing) {
|
|
568
|
+
if (existing.lineage?.parentConversationId !== args.parentConversationId) {
|
|
569
|
+
throw new Error(
|
|
570
|
+
`Conversation lineage changed for ${args.childConversationId}`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const parent = await this.get({
|
|
576
|
+
conversationId: args.parentConversationId
|
|
577
|
+
});
|
|
578
|
+
if (!parent) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`Conversation parent is missing for ${args.childConversationId}`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (parent.lineage) {
|
|
584
|
+
throw new Error("Recursive agent delegation is not enabled");
|
|
585
|
+
}
|
|
586
|
+
const child = {
|
|
587
|
+
...emptyConversation({
|
|
588
|
+
conversationId: args.childConversationId,
|
|
589
|
+
nowMs,
|
|
590
|
+
source: args.source
|
|
591
|
+
}),
|
|
592
|
+
lineage: {
|
|
593
|
+
parentConversationId: args.parentConversationId
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
await this.upsertConversation({ conversation: child });
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
555
601
|
async get(args) {
|
|
556
602
|
const row = await this.readConversationRow(args.conversationId);
|
|
557
603
|
if (!row) {
|
|
@@ -698,9 +744,10 @@ var SqlStore = class {
|
|
|
698
744
|
}
|
|
699
745
|
/** Serialize all durable mutations for one conversation inside a SQL transaction. */
|
|
700
746
|
async withConversationMutation(conversationId, callback) {
|
|
701
|
-
return await
|
|
702
|
-
|
|
703
|
-
|
|
747
|
+
return await withConversationMutationLock(
|
|
748
|
+
this.executor,
|
|
749
|
+
conversationId,
|
|
750
|
+
callback
|
|
704
751
|
);
|
|
705
752
|
}
|
|
706
753
|
async readConversationRow(conversationId) {
|
|
@@ -1669,6 +1716,7 @@ export {
|
|
|
1669
1716
|
parseSessionSource,
|
|
1670
1717
|
bindProviderConversation,
|
|
1671
1718
|
locationFromRow,
|
|
1719
|
+
withConversationMutationLock,
|
|
1672
1720
|
instructionProvenanceFor,
|
|
1673
1721
|
contextProvenance,
|
|
1674
1722
|
sameActorIdentity,
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sleep
|
|
3
|
+
} from "./chunk-EM225ITJ.js";
|
|
4
|
+
import {
|
|
5
|
+
getSlackBotToken
|
|
6
|
+
} from "./chunk-BYW4B4PR.js";
|
|
7
|
+
import {
|
|
8
|
+
parseSlackChannelReferenceId
|
|
9
|
+
} from "./chunk-F3B2YJ76.js";
|
|
10
|
+
import {
|
|
11
|
+
logWarn,
|
|
12
|
+
setSpanAttributes,
|
|
13
|
+
setSpanStatus,
|
|
14
|
+
withSpan
|
|
15
|
+
} from "./chunk-JNNJZURV.js";
|
|
16
|
+
|
|
17
|
+
// src/chat/slack/client.ts
|
|
18
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
19
|
+
import { WebClient } from "@slack/web-api";
|
|
20
|
+
|
|
21
|
+
// src/chat/slack/workspace-context.ts
|
|
22
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
23
|
+
var workspaceTeamIdStorage = new AsyncLocalStorage();
|
|
24
|
+
function runWithWorkspaceTeamId(teamId, fn) {
|
|
25
|
+
if (!teamId) return fn();
|
|
26
|
+
return workspaceTeamIdStorage.run(teamId, fn);
|
|
27
|
+
}
|
|
28
|
+
function getWorkspaceTeamId() {
|
|
29
|
+
return workspaceTeamIdStorage.getStore();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/chat/slack/client.ts
|
|
33
|
+
var SlackActionError = class extends Error {
|
|
34
|
+
code;
|
|
35
|
+
apiError;
|
|
36
|
+
needed;
|
|
37
|
+
provided;
|
|
38
|
+
statusCode;
|
|
39
|
+
requestId;
|
|
40
|
+
errorData;
|
|
41
|
+
retryAfterSeconds;
|
|
42
|
+
detail;
|
|
43
|
+
detailLine;
|
|
44
|
+
detailRule;
|
|
45
|
+
constructor(message, code, options = {}) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "SlackActionError";
|
|
48
|
+
this.code = code;
|
|
49
|
+
this.apiError = options.apiError;
|
|
50
|
+
this.needed = options.needed;
|
|
51
|
+
this.provided = options.provided;
|
|
52
|
+
this.statusCode = options.statusCode;
|
|
53
|
+
this.requestId = options.requestId;
|
|
54
|
+
this.errorData = options.errorData;
|
|
55
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
56
|
+
this.detail = options.detail;
|
|
57
|
+
this.detailLine = options.detailLine;
|
|
58
|
+
this.detailRule = options.detailRule;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function serializeSlackErrorData(data) {
|
|
62
|
+
if (!data || typeof data !== "object") {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
const filtered = Object.fromEntries(
|
|
66
|
+
Object.entries(data).filter(
|
|
67
|
+
([key]) => key !== "error"
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
if (Object.keys(filtered).length === 0) {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const serialized = JSON.stringify(filtered);
|
|
75
|
+
return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
|
|
76
|
+
} catch {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function getHeaderString(headers, name) {
|
|
81
|
+
if (!headers || typeof headers !== "object") {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
const key = name.toLowerCase();
|
|
85
|
+
const entries = headers;
|
|
86
|
+
for (const [entryKey, value] of Object.entries(entries)) {
|
|
87
|
+
if (entryKey.toLowerCase() !== key) continue;
|
|
88
|
+
if (typeof value === "string") return value;
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
const first = value.find((entry) => typeof entry === "string");
|
|
91
|
+
return typeof first === "string" ? first : void 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
function parseSlackCanvasDetail(detail) {
|
|
97
|
+
if (typeof detail !== "string") {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
const trimmed = detail.trim();
|
|
101
|
+
if (!trimmed) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
const parsed = {
|
|
105
|
+
detail: trimmed
|
|
106
|
+
};
|
|
107
|
+
const lineMatch = trimmed.match(/line\s+(\d+):/i);
|
|
108
|
+
if (lineMatch) {
|
|
109
|
+
const line = Number.parseInt(lineMatch[1] ?? "", 10);
|
|
110
|
+
if (Number.isFinite(line)) {
|
|
111
|
+
parsed.detailLine = line;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (/unsupported heading depth/i.test(trimmed)) {
|
|
115
|
+
parsed.detailRule = "unsupported_heading_depth";
|
|
116
|
+
}
|
|
117
|
+
return parsed;
|
|
118
|
+
}
|
|
119
|
+
var installationTokenStorage = new AsyncLocalStorage2();
|
|
120
|
+
var clientsByToken = /* @__PURE__ */ new Map();
|
|
121
|
+
function runWithSlackInstallationToken(token, fn) {
|
|
122
|
+
const trimmed = token.trim();
|
|
123
|
+
if (!trimmed) {
|
|
124
|
+
throw new SlackActionError(
|
|
125
|
+
"Slack installation token binding requires a non-empty token",
|
|
126
|
+
"missing_token"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return installationTokenStorage.run({ token: trimmed }, fn);
|
|
130
|
+
}
|
|
131
|
+
function resolveSlackToken() {
|
|
132
|
+
const ambientToken = installationTokenStorage.getStore()?.token;
|
|
133
|
+
if (ambientToken) {
|
|
134
|
+
return ambientToken;
|
|
135
|
+
}
|
|
136
|
+
const envToken = getSlackBotToken();
|
|
137
|
+
if (envToken) {
|
|
138
|
+
return envToken;
|
|
139
|
+
}
|
|
140
|
+
const teamId = getWorkspaceTeamId();
|
|
141
|
+
if (teamId) {
|
|
142
|
+
throw new SlackActionError(
|
|
143
|
+
`Slack call is scoped to workspace ${teamId} but no installation token is bound and no default bot token is configured`,
|
|
144
|
+
"missing_token"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
throw new SlackActionError(
|
|
148
|
+
"SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack Web API actions in this service",
|
|
149
|
+
"missing_token"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
function normalizeSlackConversationId(channelId) {
|
|
153
|
+
return parseSlackChannelReferenceId(channelId);
|
|
154
|
+
}
|
|
155
|
+
function getClient() {
|
|
156
|
+
const token = resolveSlackToken();
|
|
157
|
+
let cached = clientsByToken.get(token);
|
|
158
|
+
if (!cached) {
|
|
159
|
+
cached = new WebClient(token, {
|
|
160
|
+
retryConfig: { retries: 0 },
|
|
161
|
+
rejectRateLimitedCalls: true
|
|
162
|
+
});
|
|
163
|
+
clientsByToken.set(token, cached);
|
|
164
|
+
}
|
|
165
|
+
return cached;
|
|
166
|
+
}
|
|
167
|
+
function mapSlackError(error) {
|
|
168
|
+
if (error instanceof SlackActionError) {
|
|
169
|
+
return error;
|
|
170
|
+
}
|
|
171
|
+
const candidate = error;
|
|
172
|
+
const apiError = candidate.data?.error;
|
|
173
|
+
const message = candidate.message ?? "Slack action failed";
|
|
174
|
+
const baseOptions = {
|
|
175
|
+
apiError,
|
|
176
|
+
statusCode: candidate.statusCode,
|
|
177
|
+
requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
|
|
178
|
+
errorData: serializeSlackErrorData(candidate.data),
|
|
179
|
+
...parseSlackCanvasDetail(candidate.data?.detail)
|
|
180
|
+
};
|
|
181
|
+
if (apiError === "missing_scope") {
|
|
182
|
+
return new SlackActionError(message, "missing_scope", {
|
|
183
|
+
...baseOptions,
|
|
184
|
+
needed: candidate.data?.needed,
|
|
185
|
+
provided: candidate.data?.provided
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (apiError === "not_in_channel") {
|
|
189
|
+
return new SlackActionError(message, "not_in_channel", baseOptions);
|
|
190
|
+
}
|
|
191
|
+
if (apiError === "restricted_action_read_only_channel") {
|
|
192
|
+
return new SlackActionError(message, "read_only_channel", baseOptions);
|
|
193
|
+
}
|
|
194
|
+
if (apiError === "already_reacted") {
|
|
195
|
+
return new SlackActionError(message, "already_reacted", baseOptions);
|
|
196
|
+
}
|
|
197
|
+
if (apiError === "no_reaction") {
|
|
198
|
+
return new SlackActionError(message, "no_reaction", baseOptions);
|
|
199
|
+
}
|
|
200
|
+
if (apiError === "invalid_arguments") {
|
|
201
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
202
|
+
}
|
|
203
|
+
if (apiError === "invalid_cursor") {
|
|
204
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
205
|
+
}
|
|
206
|
+
if (apiError === "invalid_name") {
|
|
207
|
+
return new SlackActionError(message, "invalid_arguments", baseOptions);
|
|
208
|
+
}
|
|
209
|
+
if (apiError === "not_found" || apiError === "channel_not_found" || apiError === "message_not_found") {
|
|
210
|
+
return new SlackActionError(message, "not_found", baseOptions);
|
|
211
|
+
}
|
|
212
|
+
if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
|
|
213
|
+
return new SlackActionError(message, "feature_unavailable", baseOptions);
|
|
214
|
+
}
|
|
215
|
+
if (apiError === "canvas_creation_failed") {
|
|
216
|
+
return new SlackActionError(message, "canvas_creation_failed", baseOptions);
|
|
217
|
+
}
|
|
218
|
+
if (apiError === "canvas_editing_failed") {
|
|
219
|
+
return new SlackActionError(message, "canvas_editing_failed", baseOptions);
|
|
220
|
+
}
|
|
221
|
+
if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
|
|
222
|
+
return new SlackActionError(message, "rate_limited", {
|
|
223
|
+
...baseOptions,
|
|
224
|
+
retryAfterSeconds: candidate.retryAfter
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return new SlackActionError(message, "internal_error", baseOptions);
|
|
228
|
+
}
|
|
229
|
+
var MAX_RETRY_DELAY_MS = 1e4;
|
|
230
|
+
var MAX_TOTAL_RETRY_DELAY_MS = 2e4;
|
|
231
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
232
|
+
"ECONNREFUSED",
|
|
233
|
+
"ECONNRESET",
|
|
234
|
+
"ENOTFOUND",
|
|
235
|
+
"EAI_AGAIN"
|
|
236
|
+
]);
|
|
237
|
+
var TIMEOUT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
238
|
+
"ETIMEDOUT",
|
|
239
|
+
"ECONNABORTED",
|
|
240
|
+
"ESOCKETTIMEDOUT"
|
|
241
|
+
]);
|
|
242
|
+
function findNetworkErrorCode(error, depth = 0) {
|
|
243
|
+
if (!error || typeof error !== "object" || depth > 4) {
|
|
244
|
+
return void 0;
|
|
245
|
+
}
|
|
246
|
+
const candidate = error;
|
|
247
|
+
if (typeof candidate.code === "string" && (CONNECTION_ERROR_CODES.has(candidate.code) || TIMEOUT_ERROR_CODES.has(candidate.code))) {
|
|
248
|
+
return candidate.code;
|
|
249
|
+
}
|
|
250
|
+
return findNetworkErrorCode(candidate.original, depth + 1) ?? findNetworkErrorCode(candidate.cause, depth + 1);
|
|
251
|
+
}
|
|
252
|
+
function hasSocketHangUpMessage(error) {
|
|
253
|
+
return error instanceof Error && error.message.toLowerCase().includes("socket hang up");
|
|
254
|
+
}
|
|
255
|
+
function classifySlackRetry(raw, mapped) {
|
|
256
|
+
if (mapped.code === "rate_limited") {
|
|
257
|
+
return "rate_limited";
|
|
258
|
+
}
|
|
259
|
+
if (mapped.statusCode !== void 0 && mapped.statusCode >= 500) {
|
|
260
|
+
return "server_error";
|
|
261
|
+
}
|
|
262
|
+
const networkCode = findNetworkErrorCode(raw);
|
|
263
|
+
if (networkCode && CONNECTION_ERROR_CODES.has(networkCode)) {
|
|
264
|
+
return "connection";
|
|
265
|
+
}
|
|
266
|
+
if (networkCode && TIMEOUT_ERROR_CODES.has(networkCode)) {
|
|
267
|
+
return "timeout";
|
|
268
|
+
}
|
|
269
|
+
if (hasSocketHangUpMessage(raw)) {
|
|
270
|
+
return "connection";
|
|
271
|
+
}
|
|
272
|
+
return "none";
|
|
273
|
+
}
|
|
274
|
+
async function withSlackRetries(task, maxAttempts = 3, context = {}) {
|
|
275
|
+
let attempt = 0;
|
|
276
|
+
let totalDelayMs = 0;
|
|
277
|
+
const action = context.action ?? "unknown";
|
|
278
|
+
while (attempt < maxAttempts) {
|
|
279
|
+
attempt += 1;
|
|
280
|
+
const attemptNumber = attempt;
|
|
281
|
+
try {
|
|
282
|
+
return await withSpan(
|
|
283
|
+
`POST slack.com/api/${action}`,
|
|
284
|
+
"http.client",
|
|
285
|
+
{},
|
|
286
|
+
async () => {
|
|
287
|
+
try {
|
|
288
|
+
return await task();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
const mapped = mapSlackError(error);
|
|
291
|
+
const errorAttrs = {
|
|
292
|
+
"error.type": mapped.code
|
|
293
|
+
};
|
|
294
|
+
if (mapped.apiError) {
|
|
295
|
+
errorAttrs["app.slack.api_error_code"] = mapped.apiError;
|
|
296
|
+
}
|
|
297
|
+
if (mapped.code === "rate_limited") {
|
|
298
|
+
errorAttrs["http.response.status_code"] = 429;
|
|
299
|
+
if (mapped.retryAfterSeconds) {
|
|
300
|
+
errorAttrs["app.slack.retry_after_ms"] = mapped.retryAfterSeconds * 1e3;
|
|
301
|
+
}
|
|
302
|
+
} else if (mapped.statusCode != null) {
|
|
303
|
+
errorAttrs["http.response.status_code"] = mapped.statusCode;
|
|
304
|
+
}
|
|
305
|
+
setSpanAttributes(errorAttrs);
|
|
306
|
+
setSpanStatus("error");
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"http.request.method": "POST",
|
|
312
|
+
"server.address": "slack.com",
|
|
313
|
+
"url.scheme": "https",
|
|
314
|
+
"url.path": `/api/${action}`,
|
|
315
|
+
"app.slack.method": action,
|
|
316
|
+
"app.retry.max_attempts": maxAttempts,
|
|
317
|
+
...attemptNumber > 1 ? { "http.resend_count": attemptNumber - 1 } : {},
|
|
318
|
+
...context.attributes ?? {},
|
|
319
|
+
...context.spanAttributes ?? {}
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
const mapped = mapSlackError(error);
|
|
324
|
+
const retryClass = classifySlackRetry(error, mapped);
|
|
325
|
+
const isRetryable = retryClass === "rate_limited" || retryClass === "connection" || retryClass === "server_error" || retryClass === "timeout" && context.idempotent === true;
|
|
326
|
+
const remainingDelayBudgetMs = MAX_TOTAL_RETRY_DELAY_MS - totalDelayMs;
|
|
327
|
+
const baseLogAttributes = {
|
|
328
|
+
"app.slack.action": action,
|
|
329
|
+
"app.slack.error_code": mapped.code,
|
|
330
|
+
...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
|
|
331
|
+
...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
|
|
332
|
+
...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
|
|
333
|
+
...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
|
|
334
|
+
...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
|
|
335
|
+
...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
|
|
336
|
+
...context.attributes ?? {}
|
|
337
|
+
};
|
|
338
|
+
if (!isRetryable || attempt >= maxAttempts || remainingDelayBudgetMs <= 0) {
|
|
339
|
+
logWarn("slack.action.failed", {
|
|
340
|
+
...baseLogAttributes,
|
|
341
|
+
...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
|
|
342
|
+
});
|
|
343
|
+
throw mapped;
|
|
344
|
+
}
|
|
345
|
+
logWarn("slack.action.retrying", {
|
|
346
|
+
...baseLogAttributes,
|
|
347
|
+
"app.slack.retry_attempt": attempt,
|
|
348
|
+
"app.slack.retry_class": retryClass
|
|
349
|
+
});
|
|
350
|
+
const retryAfterMs = retryClass === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
|
|
351
|
+
const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
|
|
352
|
+
const delayMs = Math.min(
|
|
353
|
+
retryAfterMs ?? backoffMs,
|
|
354
|
+
MAX_RETRY_DELAY_MS,
|
|
355
|
+
remainingDelayBudgetMs
|
|
356
|
+
);
|
|
357
|
+
totalDelayMs += delayMs;
|
|
358
|
+
await sleep(delayMs);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
throw new SlackActionError(
|
|
362
|
+
"Slack action exhausted retries",
|
|
363
|
+
"internal_error"
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
function getSlackClient() {
|
|
367
|
+
return getClient();
|
|
368
|
+
}
|
|
369
|
+
function isDmChannel(channelId) {
|
|
370
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
371
|
+
return Boolean(normalized && normalized.startsWith("D"));
|
|
372
|
+
}
|
|
373
|
+
function isConversationScopedChannel(channelId) {
|
|
374
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
375
|
+
if (!normalized) return false;
|
|
376
|
+
return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
|
|
377
|
+
}
|
|
378
|
+
function isConversationChannel(channelId) {
|
|
379
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
380
|
+
if (!normalized) return false;
|
|
381
|
+
return normalized.startsWith("C") || normalized.startsWith("G");
|
|
382
|
+
}
|
|
383
|
+
async function getFilePermalink(fileId) {
|
|
384
|
+
const client = getClient();
|
|
385
|
+
const response = await withSlackRetries(
|
|
386
|
+
() => client.files.info({
|
|
387
|
+
file: fileId
|
|
388
|
+
}),
|
|
389
|
+
3,
|
|
390
|
+
{
|
|
391
|
+
action: "files.info",
|
|
392
|
+
idempotent: true,
|
|
393
|
+
spanAttributes: { "app.slack.file_id": fileId }
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
return response.file?.permalink;
|
|
397
|
+
}
|
|
398
|
+
async function downloadPrivateSlackFile(url) {
|
|
399
|
+
const token = resolveSlackToken();
|
|
400
|
+
return withSpan(
|
|
401
|
+
"GET files.slack.com",
|
|
402
|
+
"http.client",
|
|
403
|
+
{},
|
|
404
|
+
async () => {
|
|
405
|
+
const response = await fetch(url, {
|
|
406
|
+
headers: {
|
|
407
|
+
Authorization: `Bearer ${token}`
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
setSpanAttributes({ "http.response.status_code": response.status });
|
|
411
|
+
if (!response.ok) {
|
|
412
|
+
setSpanAttributes({ "error.type": String(response.status) });
|
|
413
|
+
setSpanStatus("error");
|
|
414
|
+
throw new Error(`Slack file download failed: ${response.status}`);
|
|
415
|
+
}
|
|
416
|
+
return Buffer.from(await response.arrayBuffer());
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"http.request.method": "GET",
|
|
420
|
+
"server.address": "files.slack.com",
|
|
421
|
+
"url.scheme": "https",
|
|
422
|
+
"app.slack.method": "files.download"
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export {
|
|
428
|
+
runWithWorkspaceTeamId,
|
|
429
|
+
getWorkspaceTeamId,
|
|
430
|
+
SlackActionError,
|
|
431
|
+
getHeaderString,
|
|
432
|
+
runWithSlackInstallationToken,
|
|
433
|
+
normalizeSlackConversationId,
|
|
434
|
+
withSlackRetries,
|
|
435
|
+
getSlackClient,
|
|
436
|
+
isDmChannel,
|
|
437
|
+
isConversationScopedChannel,
|
|
438
|
+
isConversationChannel,
|
|
439
|
+
getFilePermalink,
|
|
440
|
+
downloadPrivateSlackFile
|
|
441
|
+
};
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
canRouteResourceEvents,
|
|
6
6
|
getPlugins
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5MXWHTCK.js";
|
|
8
8
|
import {
|
|
9
9
|
getDb
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SIPKWSXK.js";
|
|
11
11
|
import {
|
|
12
12
|
juniorEventTasks,
|
|
13
13
|
juniorSchedulerRuns,
|
|
14
14
|
juniorSchedulerTasks,
|
|
15
15
|
juniorStats
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DCLLQGS6.js";
|
|
17
17
|
import {
|
|
18
18
|
juniorDestinations
|
|
19
19
|
} from "./chunk-VFUK3X5B.js";
|