@sentry/junior 0.137.0 → 0.139.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.
Files changed (103) hide show
  1. package/dist/{agent-hooks-MZOKEP3J.js → agent-hooks-MJXMD7JP.js} +11 -9
  2. package/dist/api/schema/task.d.ts +100 -1
  3. package/dist/api/schema.d.ts +2 -2
  4. package/dist/api/schema.js +9 -1
  5. package/dist/api.js +202 -55
  6. package/dist/app.d.ts +7 -0
  7. package/dist/app.js +601 -1356
  8. package/dist/{catalog-runtime-MD66YLFV.js → catalog-runtime-GCCFYPIK.js} +6 -6
  9. package/dist/chat/agent/request.d.ts +44 -4
  10. package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
  11. package/dist/chat/agent-dispatch/types.d.ts +1 -1
  12. package/dist/chat/agent-invocations/errors.d.ts +8 -0
  13. package/dist/chat/agent-invocations/spawn.d.ts +12 -0
  14. package/dist/chat/agent-invocations/store.d.ts +44 -0
  15. package/dist/chat/agent-invocations/types.d.ts +449 -0
  16. package/dist/chat/agent-invocations/work.d.ts +31 -0
  17. package/dist/chat/config.d.ts +1 -0
  18. package/dist/chat/conversations/sql/store.d.ts +8 -0
  19. package/dist/chat/conversations/store.d.ts +7 -0
  20. package/dist/chat/credentials/context.d.ts +14 -0
  21. package/dist/chat/experimental.d.ts +16 -0
  22. package/dist/chat/resource-events/actor.d.ts +23 -0
  23. package/dist/chat/runtime/agent-runner.d.ts +2 -1
  24. package/dist/chat/services/auth-pause.d.ts +0 -1
  25. package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
  26. package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
  27. package/dist/chat/state/turn-session.d.ts +1 -5
  28. package/dist/chat/task-execution/state.d.ts +23 -11
  29. package/dist/chat/task-execution/store.d.ts +20 -11
  30. package/dist/chat/task-execution/worker.d.ts +2 -2
  31. package/dist/chat/tasks/execution-stats.d.ts +34 -3
  32. package/dist/chat/tasks/read.d.ts +3 -1
  33. package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
  34. package/dist/chat/tools/types.d.ts +2 -0
  35. package/dist/{chunk-T6Z5S6PQ.js → chunk-2ECYPYKP.js} +1 -1
  36. package/dist/chunk-3T2JAGRH.js +115 -0
  37. package/dist/chunk-3Y47VMHV.js +6 -0
  38. package/dist/chunk-4V4QIYJH.js +41 -0
  39. package/dist/{chunk-SYCX4DOK.js → chunk-5GSZWRE2.js} +434 -65
  40. package/dist/chunk-5IDFPCON.js +46 -0
  41. package/dist/{chunk-36EG34YI.js → chunk-6OHTDR4F.js} +1 -1
  42. package/dist/chunk-6SU2DT27.js +441 -0
  43. package/dist/{chunk-UG6ZYPBI.js → chunk-75I3UBIL.js} +73 -60
  44. package/dist/chunk-AEQISMMU.js +711 -0
  45. package/dist/{chunk-5PNJMOY5.js → chunk-C2N6MNO4.js} +9 -2
  46. package/dist/{chunk-BZAMASJP.js → chunk-CXH25EL4.js} +2 -2
  47. package/dist/{chunk-K2NCQJKH.js → chunk-DW4PMHNW.js} +3 -3
  48. package/dist/chunk-EM225ITJ.js +56 -0
  49. package/dist/{chunk-UOXATPSI.js → chunk-G52UXKGO.js} +7 -5
  50. package/dist/chunk-GI52BW74.js +207 -0
  51. package/dist/{chunk-3Q342EJJ.js → chunk-GWQSZT4C.js} +2 -2
  52. package/dist/{chunk-EPBBYMNE.js → chunk-HUO3UTAQ.js} +1 -1
  53. package/dist/{chunk-WPPXIAFB.js → chunk-IIESAYDL.js} +17 -434
  54. package/dist/chunk-IUFM5JWV.js +24 -0
  55. package/dist/{chunk-UZQ4VY3M.js → chunk-J3S57AXY.js} +1 -1
  56. package/dist/{chunk-DQXAAVTR.js → chunk-J6EJQCHY.js} +46 -1471
  57. package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
  58. package/dist/chunk-MQ5RYATN.js +1523 -0
  59. package/dist/{chunk-5G34IEK2.js → chunk-MXTVUV3I.js} +2 -56
  60. package/dist/{chunk-YRPC6MJY.js → chunk-MXVD2BDS.js} +1 -1
  61. package/dist/{chunk-POBSY47Q.js → chunk-PJW6U46K.js} +337 -306
  62. package/dist/chunk-Q6QZTTLS.js +55 -0
  63. package/dist/{chunk-ZFS67WIR.js → chunk-QTBKPZDA.js} +57 -6
  64. package/dist/chunk-R776HKPK.js +81 -0
  65. package/dist/{chunk-77VDLF3Z.js → chunk-RUIXF3YQ.js} +10 -0
  66. package/dist/chunk-U27DSXKB.js +714 -0
  67. package/dist/{chunk-XZ7NPXAB.js → chunk-U7CMWQ7Z.js} +1 -1
  68. package/dist/{chunk-RB4NJKEX.js → chunk-UDBUS533.js} +125 -51
  69. package/dist/chunk-WQRX57EA.js +668 -0
  70. package/dist/chunk-WZH4JV7R.js +320 -0
  71. package/dist/{chunk-HCIVH3JS.js → chunk-YCTDA7BD.js} +28 -0
  72. package/dist/{chunk-YNFERGKP.js → chunk-YGZ2OLXL.js} +1 -1
  73. package/dist/{chunk-EWDSB24B.js → chunk-ZQCGQMF2.js} +16 -776
  74. package/dist/{chunk-KJ4HGRHN.js → chunk-ZYQSPQYR.js} +515 -601
  75. package/dist/cli/chat.js +68 -29
  76. package/dist/cli/check.js +7 -7
  77. package/dist/cli/plugins.js +15 -13
  78. package/dist/cli/snapshot-create.js +11 -10
  79. package/dist/cli/upgrade.js +8 -8
  80. package/dist/conversation-work-KATFHLG3.js +56 -0
  81. package/dist/db/schema/agent-invocations.d.ts +544 -0
  82. package/dist/db/schema/task-executions.d.ts +23 -2
  83. package/dist/db/schema.d.ts +563 -2
  84. package/dist/{db-66E7VDNC.js → db-UXPPGHDF.js} +7 -7
  85. package/dist/experimental-HH6OM6UH.js +13 -0
  86. package/dist/instrumentation.js +4 -4
  87. package/dist/nitro.js +4 -3
  88. package/dist/{oauth-callback-server-7QPD4Q7P.js → oauth-callback-server-4ZYMD5UA.js} +28 -19
  89. package/dist/{runner-L2BKJKOD.js → runner-MZSEONRZ.js} +32 -18
  90. package/dist/{sandbox-egress-signals-A5HWIQH4.js → sandbox-egress-signals-EKPAVWQL.js} +2 -2
  91. package/dist/{skills-HOQ2NSPW.js → skills-IMVPXSQ7.js} +7 -7
  92. package/dist/spawn-S5LSVOCE.js +38 -0
  93. package/dist/{validation-ENHI7S2C.js → validation-ZKCY2BVR.js} +7 -7
  94. package/dist/version.js +1 -1
  95. package/dist/work-GG3XDYRX.js +46 -0
  96. package/dist/worker-23AUELCJ.js +24 -0
  97. package/migrations/0021_first_warhawk.sql +35 -0
  98. package/migrations/0022_task_execution_status.sql +3 -0
  99. package/migrations/meta/0021_snapshot.json +2176 -0
  100. package/migrations/meta/0022_snapshot.json +2186 -0
  101. package/migrations/meta/_journal.json +15 -1
  102. package/package.json +4 -4
  103. package/dist/chunk-6QW2RRQT.js +0 -233
@@ -0,0 +1,441 @@
1
+ import {
2
+ sleep
3
+ } from "./chunk-EM225ITJ.js";
4
+ import {
5
+ getSlackBotToken
6
+ } from "./chunk-75I3UBIL.js";
7
+ import {
8
+ parseSlackChannelReferenceId
9
+ } from "./chunk-F3B2YJ76.js";
10
+ import {
11
+ logWarn,
12
+ setSpanAttributes,
13
+ setSpanStatus,
14
+ withSpan
15
+ } from "./chunk-2ECYPYKP.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
+ };
@@ -3,7 +3,7 @@ import {
3
3
  toCanonicalInputMessage,
4
4
  toCanonicalOutputMessage,
5
5
  toGenAiMessagesTraceAttributes
6
- } from "./chunk-YNFERGKP.js";
6
+ } from "./chunk-YGZ2OLXL.js";
7
7
  import {
8
8
  extractGenAiUsageAttributes,
9
9
  logException,
@@ -11,65 +11,7 @@ import {
11
11
  normalizeGenAiFinishReason,
12
12
  serializeGenAiAttribute,
13
13
  withSpan
14
- } from "./chunk-T6Z5S6PQ.js";
15
-
16
- // src/chat/config.ts
17
- import { getModel as getModel2 } from "@earendil-works/pi-ai/compat";
18
-
19
- // src/chat/optional-string.ts
20
- function toOptionalTrimmed(value) {
21
- if (!value) {
22
- return void 0;
23
- }
24
- const trimmed = value.trim();
25
- return trimmed.length > 0 ? trimmed : void 0;
26
- }
27
-
28
- // src/chat/pi/sdk.ts
29
- import {
30
- calculateCost,
31
- completeSimple,
32
- getEnvApiKey,
33
- getModel,
34
- getModels,
35
- isRetryableAssistantError,
36
- registerApiProvider
37
- } from "@earendil-works/pi-ai/compat";
38
- import {
39
- stream,
40
- streamSimple
41
- } from "@earendil-works/pi-ai/api/anthropic-messages";
42
-
43
- // src/chat/pi/client.ts
44
- import { createGatewayProvider } from "@ai-sdk/gateway";
45
-
46
- // src/chat/pi/gateway-auth.ts
47
- import { getVercelOidcToken } from "@vercel/oidc";
48
- var MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (enable Vercel OIDC or set AI_GATEWAY_API_KEY)";
49
- async function resolveGatewayCredential() {
50
- try {
51
- const oidcToken = toOptionalTrimmed(await getVercelOidcToken());
52
- if (oidcToken) {
53
- return { mode: "oidc", token: oidcToken };
54
- }
55
- } catch {
56
- }
57
- const apiKey = toOptionalTrimmed(getEnvApiKey("vercel-ai-gateway"));
58
- if (apiKey) {
59
- return { mode: "api_key", token: apiKey };
60
- }
61
- return void 0;
62
- }
63
- async function getGatewayApiKey() {
64
- return (await resolveGatewayCredential())?.token;
65
- }
66
-
67
- // src/chat/pi/client.ts
68
- import {
69
- embedMany,
70
- generateObject,
71
- NoObjectGeneratedError
72
- } from "ai";
14
+ } from "./chunk-2ECYPYKP.js";
73
15
 
74
16
  // src/chat/services/provider-error.ts
75
17
  var ProviderError = class extends Error {
@@ -253,6 +195,64 @@ function getProviderErrorAttributes(error) {
253
195
  };
254
196
  }
255
197
 
198
+ // src/chat/config.ts
199
+ import { getModel as getModel2 } from "@earendil-works/pi-ai/compat";
200
+
201
+ // src/chat/optional-string.ts
202
+ function toOptionalTrimmed(value) {
203
+ if (!value) {
204
+ return void 0;
205
+ }
206
+ const trimmed = value.trim();
207
+ return trimmed.length > 0 ? trimmed : void 0;
208
+ }
209
+
210
+ // src/chat/pi/sdk.ts
211
+ import {
212
+ calculateCost,
213
+ completeSimple,
214
+ getEnvApiKey,
215
+ getModel,
216
+ getModels,
217
+ isRetryableAssistantError,
218
+ registerApiProvider
219
+ } from "@earendil-works/pi-ai/compat";
220
+ import {
221
+ stream,
222
+ streamSimple
223
+ } from "@earendil-works/pi-ai/api/anthropic-messages";
224
+
225
+ // src/chat/pi/client.ts
226
+ import { createGatewayProvider } from "@ai-sdk/gateway";
227
+
228
+ // src/chat/pi/gateway-auth.ts
229
+ import { getVercelOidcToken } from "@vercel/oidc";
230
+ var MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (enable Vercel OIDC or set AI_GATEWAY_API_KEY)";
231
+ async function resolveGatewayCredential() {
232
+ try {
233
+ const oidcToken = toOptionalTrimmed(await getVercelOidcToken());
234
+ if (oidcToken) {
235
+ return { mode: "oidc", token: oidcToken };
236
+ }
237
+ } catch {
238
+ }
239
+ const apiKey = toOptionalTrimmed(getEnvApiKey("vercel-ai-gateway"));
240
+ if (apiKey) {
241
+ return { mode: "api_key", token: apiKey };
242
+ }
243
+ return void 0;
244
+ }
245
+ async function getGatewayApiKey() {
246
+ return (await resolveGatewayCredential())?.token;
247
+ }
248
+
249
+ // src/chat/pi/client.ts
250
+ import {
251
+ embedMany,
252
+ generateObject,
253
+ NoObjectGeneratedError
254
+ } from "ai";
255
+
256
256
  // src/chat/xml.ts
257
257
  function escapeXml(value) {
258
258
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
@@ -779,6 +779,16 @@ function parseSlashCommand(rawValue) {
779
779
  }
780
780
  return command;
781
781
  }
782
+ function parseConversationWorkEnabled(rawValue) {
783
+ const value = toOptionalTrimmed(rawValue)?.toLowerCase();
784
+ if (value === void 0 || value === "true") {
785
+ return true;
786
+ }
787
+ if (value === "false") {
788
+ return false;
789
+ }
790
+ throw new Error("JUNIOR_CONVERSATION_WORK_ENABLED must be true or false");
791
+ }
782
792
  function parseCrossActorMidRunMode(rawValue) {
783
793
  const value = toOptionalTrimmed(rawValue) ?? "follow_up";
784
794
  if (value === "follow_up" || value === "steer") {
@@ -949,6 +959,9 @@ function readChatConfig(env = process.env, functionMaxDurationSeconds = DEFAULT_
949
959
  return {
950
960
  bot: readBotConfig(env, resolvedFunctionMaxDurationSeconds),
951
961
  functionMaxDurationSeconds: resolvedFunctionMaxDurationSeconds,
962
+ conversationWorkEnabled: parseConversationWorkEnabled(
963
+ env.JUNIOR_CONVERSATION_WORK_ENABLED
964
+ ),
952
965
  conversationWorkSoftYieldAfterMs: resolveConversationWorkSoftYieldAfterMs(
953
966
  resolvedFunctionMaxDurationSeconds
954
967
  ),