@sentry/junior 0.88.0 → 0.90.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 (158) hide show
  1. package/README.md +16 -5
  2. package/dist/{agent-hooks-RIJ5TSWP.js → agent-hooks-MXAF7RQL.js} +10 -10
  3. package/dist/api/people/list.js +8 -8
  4. package/dist/api/people/profile.js +8 -8
  5. package/dist/api-reference.d.ts +1 -1
  6. package/dist/app.js +716 -732
  7. package/dist/{catalog-runtime-MW7XERSU.js → catalog-runtime-DXE6NXHT.js} +5 -5
  8. package/dist/chat/agent/index.d.ts +4 -0
  9. package/dist/chat/agent/prompt.d.ts +78 -0
  10. package/dist/chat/{respond.d.ts → agent/request.d.ts} +98 -55
  11. package/dist/chat/agent/resume.d.ts +65 -0
  12. package/dist/chat/agent/sandbox.d.ts +15 -0
  13. package/dist/chat/agent/session.d.ts +20 -0
  14. package/dist/chat/agent/skills.d.ts +26 -0
  15. package/dist/chat/agent/tools.d.ts +78 -0
  16. package/dist/chat/agent-dispatch/runner.d.ts +3 -5
  17. package/dist/chat/app/production.d.ts +3 -1
  18. package/dist/chat/app/services.d.ts +1 -4
  19. package/dist/chat/logging.d.ts +2 -0
  20. package/dist/chat/mcp/tool-manager.d.ts +1 -6
  21. package/dist/chat/pi/messages.d.ts +18 -2
  22. package/dist/chat/pi/transcript.d.ts +31 -0
  23. package/dist/chat/plugins/agent-hooks.d.ts +2 -2
  24. package/dist/chat/prompt.d.ts +1 -1
  25. package/dist/chat/runtime/agent-continue-runner.d.ts +5 -5
  26. package/dist/chat/runtime/agent-run-outcome.d.ts +21 -0
  27. package/dist/chat/runtime/agent-runner.d.ts +11 -0
  28. package/dist/chat/runtime/delivered-turn-state.d.ts +2 -2
  29. package/dist/chat/runtime/generated-artifacts.d.ts +16 -0
  30. package/dist/chat/runtime/reply-executor.d.ts +2 -2
  31. package/dist/chat/runtime/slack-resume.d.ts +21 -12
  32. package/dist/chat/runtime/turn.d.ts +5 -39
  33. package/dist/chat/services/reply-delivery-plan.d.ts +0 -8
  34. package/dist/chat/services/turn-failure-response.d.ts +4 -4
  35. package/dist/chat/services/turn-result.d.ts +4 -7
  36. package/dist/chat/slack/channel.d.ts +9 -5
  37. package/dist/chat/slack/client.d.ts +3 -1
  38. package/dist/chat/slack/context.d.ts +17 -5
  39. package/dist/chat/slack/id-param.d.ts +25 -0
  40. package/dist/chat/slack/ids.d.ts +18 -4
  41. package/dist/chat/slack/outbound.d.ts +16 -0
  42. package/dist/chat/slack/output.d.ts +3 -3
  43. package/dist/chat/slack/reply.d.ts +4 -7
  44. package/dist/chat/slack/timestamp-param.d.ts +25 -0
  45. package/dist/chat/slack/tools/canvas/context.d.ts +18 -0
  46. package/dist/chat/slack/tools/canvas/create.d.ts +4 -0
  47. package/dist/chat/slack/tools/canvas/edit.d.ts +3 -0
  48. package/dist/chat/slack/tools/canvas/read.d.ts +6 -0
  49. package/dist/chat/slack/tools/canvas/write.d.ts +3 -0
  50. package/dist/chat/{tools/slack → slack/tools}/channel-access.d.ts +4 -3
  51. package/dist/chat/{tools → slack/tools}/channel-capabilities.d.ts +2 -0
  52. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -0
  53. package/dist/chat/{tools/slack → slack/tools}/context.d.ts +5 -4
  54. package/dist/chat/slack/tools/list/add-items.d.ts +3 -0
  55. package/dist/chat/{tools/slack/lists.d.ts → slack/tools/list/api.d.ts} +2 -1
  56. package/dist/chat/slack/tools/list/create.d.ts +3 -0
  57. package/dist/chat/slack/tools/list/get-items.d.ts +3 -0
  58. package/dist/chat/slack/tools/list/update-item.d.ts +3 -0
  59. package/dist/chat/slack/tools/message-add-reaction.d.ts +4 -0
  60. package/dist/chat/slack/tools/send-message.d.ts +11 -0
  61. package/dist/chat/{tools/slack → slack/tools}/slack-message-url.d.ts +5 -5
  62. package/dist/chat/slack/tools/thread-read.d.ts +6 -0
  63. package/dist/chat/slack/tools/user-lookup.d.ts +2 -0
  64. package/dist/chat/slack/users.d.ts +2 -1
  65. package/dist/chat/{services → slack}/vision-context.d.ts +4 -2
  66. package/dist/chat/state/session-log.d.ts +1 -1
  67. package/dist/chat/tool-exposure.d.ts +10 -0
  68. package/dist/chat/tool-support/catalog-tool-call.d.ts +10 -0
  69. package/dist/chat/{tools/execution → tool-support}/normalize-result.d.ts +4 -1
  70. package/dist/chat/{tools/agent-tools.d.ts → tool-support/pi-tool-adapter.d.ts} +3 -9
  71. package/dist/chat/tool-support/schema-summary.d.ts +2 -0
  72. package/dist/chat/{tools → tool-support}/skill/mcp-tool-summary.d.ts +0 -6
  73. package/dist/chat/tool-support/structured-result.d.ts +73 -0
  74. package/dist/chat/tool-support/text-range-result.d.ts +58 -0
  75. package/dist/chat/tool-support/tool-execution-report.d.ts +7 -0
  76. package/dist/chat/tool-support/zod-tool.d.ts +27 -0
  77. package/dist/chat/tools/advisor/tool.d.ts +9 -18
  78. package/dist/chat/tools/definition.d.ts +56 -9
  79. package/dist/chat/tools/execute-tool.d.ts +3 -0
  80. package/dist/chat/tools/index.d.ts +3 -2
  81. package/dist/chat/tools/resource-events.d.ts +3 -13
  82. package/dist/chat/tools/runtime/report-progress.d.ts +1 -3
  83. package/dist/chat/tools/sandbox/bash.d.ts +1 -4
  84. package/dist/chat/tools/sandbox/edit-file.d.ts +25 -7
  85. package/dist/chat/tools/sandbox/file-uploads.d.ts +30 -0
  86. package/dist/chat/tools/sandbox/file-utils.d.ts +10 -1
  87. package/dist/chat/tools/sandbox/find-files.d.ts +12 -11
  88. package/dist/chat/tools/sandbox/grep.d.ts +18 -12
  89. package/dist/chat/tools/sandbox/list-dir.d.ts +15 -7
  90. package/dist/chat/tools/sandbox/read-file.d.ts +2 -31
  91. package/dist/chat/tools/sandbox/write-file.d.ts +1 -4
  92. package/dist/chat/tools/search-tools.d.ts +4 -0
  93. package/dist/chat/tools/skill/call-mcp-tool.d.ts +1 -4
  94. package/dist/chat/tools/skill/load-skill.d.ts +4 -5
  95. package/dist/chat/tools/skill/search-mcp-tools.d.ts +1 -5
  96. package/dist/chat/tools/system-time.d.ts +1 -1
  97. package/dist/chat/tools/types.d.ts +15 -5
  98. package/dist/chat/tools/web/fetch-content.d.ts +4 -2
  99. package/dist/chat/tools/web/fetch-tool.d.ts +4 -4
  100. package/dist/chat/tools/web/image-generate.d.ts +4 -3
  101. package/dist/chat/tools/web/search.d.ts +1 -4
  102. package/dist/{chunk-4IXHIV37.js → chunk-2S7M37HY.js} +6 -8
  103. package/dist/{chunk-L7OHKDOX.js → chunk-3MPOMK3K.js} +58 -7
  104. package/dist/{chunk-V6XDCS3X.js → chunk-5GBUZI6M.js} +4 -4
  105. package/dist/{chunk-7ES37H6U.js → chunk-AN2437E3.js} +4 -4
  106. package/dist/chunk-AOISHLSF.js +747 -0
  107. package/dist/{chunk-NYJLQI42.js → chunk-BKSZL4QO.js} +11257 -11957
  108. package/dist/{chunk-XS6NLQPA.js → chunk-DACFZ5CI.js} +474 -33
  109. package/dist/chunk-GCWD2VHP.js +1216 -0
  110. package/dist/chunk-GHGPTPBL.js +53 -0
  111. package/dist/{chunk-S35CPNPT.js → chunk-HXL2ZV74.js} +40 -216
  112. package/dist/{chunk-PBV4ZIVM.js → chunk-IRRMABQD.js} +1 -1
  113. package/dist/{chunk-2DPZRS3B.js → chunk-IX76WFJV.js} +8 -8
  114. package/dist/{chunk-66NX7MNW.js → chunk-KC42JAAQ.js} +1 -1
  115. package/dist/chunk-LFLTJQVR.js +90 -0
  116. package/dist/{chunk-IATAYWFU.js → chunk-NW7AXDA5.js} +2 -2
  117. package/dist/{chunk-F22AM3OW.js → chunk-NYWPISSO.js} +1 -1
  118. package/dist/chunk-OB42YVAE.js +16 -0
  119. package/dist/chunk-ONFOWI2D.js +955 -0
  120. package/dist/chunk-PHZHJTCK.js +1062 -0
  121. package/dist/{chunk-6UPQ5GTJ.js → chunk-QCKGI74V.js} +7 -7
  122. package/dist/{chunk-YPAE5RH3.js → chunk-RS6ANWVT.js} +1 -1
  123. package/dist/{chunk-C5NCV3OB.js → chunk-TYUXD3FN.js} +1 -1
  124. package/dist/{chunk-KBFQXJL4.js → chunk-XP7F4LYB.js} +5 -7
  125. package/dist/{chunk-VMBZFPZM.js → chunk-ZUK7BL63.js} +187 -9
  126. package/dist/cli/chat.js +55 -40
  127. package/dist/cli/check.js +6 -6
  128. package/dist/cli/init.js +30 -36
  129. package/dist/cli/plugins.js +16 -16
  130. package/dist/cli/snapshot-warmup.js +9 -9
  131. package/dist/cli/upgrade.js +21 -21
  132. package/dist/db-CXI7PR5U.js +18 -0
  133. package/dist/handlers/agent-dispatch.d.ts +3 -3
  134. package/dist/handlers/mcp-oauth-callback.d.ts +3 -3
  135. package/dist/handlers/oauth-callback.d.ts +3 -3
  136. package/dist/instrumentation.js +6 -5
  137. package/dist/nitro.js +3 -7
  138. package/dist/reporting.js +23 -25
  139. package/dist/{runner-BCRF2FCI.js → runner-FKL3RAHA.js} +89 -77
  140. package/dist/{validation-FMJO3L5P.js → validation-I7GD2YWS.js} +6 -6
  141. package/package.json +6 -6
  142. package/dist/chat/respond-helpers.d.ts +0 -82
  143. package/dist/chat/services/attachment-claims.d.ts +0 -2
  144. package/dist/chat/services/channel-intent.d.ts +0 -2
  145. package/dist/chat/tools/sandbox/attach-file.d.ts +0 -7
  146. package/dist/chat/tools/slack/canvas-tools.d.ts +0 -30
  147. package/dist/chat/tools/slack/channel-list-messages.d.ts +0 -9
  148. package/dist/chat/tools/slack/channel-post-message.d.ts +0 -5
  149. package/dist/chat/tools/slack/list-tools.d.ts +0 -21
  150. package/dist/chat/tools/slack/message-add-reaction.d.ts +0 -5
  151. package/dist/chat/tools/slack/thread-read.d.ts +0 -12
  152. package/dist/chat/tools/slack/user-lookup.d.ts +0 -8
  153. package/dist/chunk-AHLSXMOU.js +0 -506
  154. package/dist/chunk-FTMXFBDC.js +0 -49
  155. package/dist/chunk-KBSLCOGE.js +0 -2226
  156. package/dist/chunk-KQKIA4CU.js +0 -186
  157. package/dist/db-37HOGLI7.js +0 -18
  158. /package/dist/chat/{tools/slack/canvases.d.ts → slack/tools/canvas/api.d.ts} +0 -0
@@ -1,32 +1,33 @@
1
1
  import {
2
2
  createPluginLogger,
3
3
  createPluginState
4
- } from "./chunk-IATAYWFU.js";
4
+ } from "./chunk-NW7AXDA5.js";
5
5
  import {
6
6
  SANDBOX_WORKSPACE_ROOT
7
7
  } from "./chunk-G3E7SCME.js";
8
8
  import {
9
9
  getDb
10
- } from "./chunk-4IXHIV37.js";
11
- import {
12
- isConversationChannel,
13
- isConversationScopedChannel,
14
- isDmChannel,
15
- normalizeSlackConversationId
16
- } from "./chunk-AHLSXMOU.js";
10
+ } from "./chunk-2S7M37HY.js";
17
11
  import {
18
12
  botConfig,
19
13
  completeObject,
20
- embedTexts
21
- } from "./chunk-S35CPNPT.js";
14
+ embedTexts,
15
+ getSlackBotToken
16
+ } from "./chunk-HXL2ZV74.js";
22
17
  import {
23
18
  isActorUserId,
24
- parseActorUserId
25
- } from "./chunk-KQKIA4CU.js";
26
- import {
27
19
  logInfo,
28
- logWarn
29
- } from "./chunk-VMBZFPZM.js";
20
+ logWarn,
21
+ parseActorUserId,
22
+ setSpanAttributes,
23
+ setSpanStatus,
24
+ withSpan
25
+ } from "./chunk-ZUK7BL63.js";
26
+ import {
27
+ parseSlackChannelReferenceId,
28
+ parseSlackMessageTs,
29
+ parseSlackTeamId
30
+ } from "./chunk-LFLTJQVR.js";
30
31
 
31
32
  // src/chat/plugins/agent-hooks.ts
32
33
  import { promptMessageSchema } from "@sentry/junior-plugin-api";
@@ -68,15 +69,7 @@ function createPluginEmbedder(pluginName, runtime = {}) {
68
69
  };
69
70
  }
70
71
 
71
- // src/chat/slack/timestamp.ts
72
- import { z } from "zod";
73
- var slackMessageTsSchema = z.string().trim().regex(/^\d+(?:\.\d+)?$/).brand();
74
- function parseSlackMessageTs(value) {
75
- const parsed = slackMessageTsSchema.safeParse(value);
76
- return parsed.success ? parsed.data : void 0;
77
- }
78
-
79
- // src/chat/tools/slack/context.ts
72
+ // src/chat/slack/tools/context.ts
80
73
  function getSlackToolContext(context) {
81
74
  if (context.source.platform !== "slack") {
82
75
  return void 0;
@@ -84,20 +77,455 @@ function getSlackToolContext(context) {
84
77
  if (context.destination.platform !== "slack") {
85
78
  throw new TypeError("Slack source requires a Slack destination");
86
79
  }
80
+ const destinationChannelId = parseSlackChannelReferenceId(
81
+ context.destination.channelId
82
+ );
83
+ const sourceChannelId = parseSlackChannelReferenceId(
84
+ context.source.channelId
85
+ );
86
+ const teamId = parseSlackTeamId(context.source.teamId);
87
+ if (!destinationChannelId || !sourceChannelId || !teamId) {
88
+ return void 0;
89
+ }
87
90
  return {
88
91
  destination: context.destination,
89
92
  source: context.source,
90
93
  requester: context.requester?.platform === "slack" ? context.requester : void 0,
91
- destinationChannelId: context.destination.channelId,
94
+ destinationChannelId,
92
95
  messageTs: parseSlackMessageTs(context.source.messageTs),
93
- sourceChannelId: context.source.channelId,
94
- teamId: context.source.teamId,
95
- threadTs: context.source.threadTs
96
+ sourceChannelId,
97
+ teamId,
98
+ threadTs: parseSlackMessageTs(context.source.threadTs)
96
99
  };
97
100
  }
98
101
 
99
102
  // src/chat/credentials/subject.ts
100
103
  import { createHmac, timingSafeEqual } from "crypto";
104
+
105
+ // src/chat/slack/client.ts
106
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
107
+ import { WebClient } from "@slack/web-api";
108
+
109
+ // src/chat/slack/workspace-context.ts
110
+ import { AsyncLocalStorage } from "async_hooks";
111
+ var workspaceTeamIdStorage = new AsyncLocalStorage();
112
+ function runWithWorkspaceTeamId(teamId, fn) {
113
+ if (!teamId) return fn();
114
+ return workspaceTeamIdStorage.run(teamId, fn);
115
+ }
116
+ function getWorkspaceTeamId() {
117
+ return workspaceTeamIdStorage.getStore();
118
+ }
119
+
120
+ // src/chat/slack/client.ts
121
+ var SlackActionError = class extends Error {
122
+ code;
123
+ apiError;
124
+ needed;
125
+ provided;
126
+ statusCode;
127
+ requestId;
128
+ errorData;
129
+ retryAfterSeconds;
130
+ detail;
131
+ detailLine;
132
+ detailRule;
133
+ constructor(message, code, options = {}) {
134
+ super(message);
135
+ this.name = "SlackActionError";
136
+ this.code = code;
137
+ this.apiError = options.apiError;
138
+ this.needed = options.needed;
139
+ this.provided = options.provided;
140
+ this.statusCode = options.statusCode;
141
+ this.requestId = options.requestId;
142
+ this.errorData = options.errorData;
143
+ this.retryAfterSeconds = options.retryAfterSeconds;
144
+ this.detail = options.detail;
145
+ this.detailLine = options.detailLine;
146
+ this.detailRule = options.detailRule;
147
+ }
148
+ };
149
+ function serializeSlackErrorData(data) {
150
+ if (!data || typeof data !== "object") {
151
+ return void 0;
152
+ }
153
+ const filtered = Object.fromEntries(
154
+ Object.entries(data).filter(
155
+ ([key]) => key !== "error"
156
+ )
157
+ );
158
+ if (Object.keys(filtered).length === 0) {
159
+ return void 0;
160
+ }
161
+ try {
162
+ const serialized = JSON.stringify(filtered);
163
+ return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
164
+ } catch {
165
+ return void 0;
166
+ }
167
+ }
168
+ function getHeaderString(headers, name) {
169
+ if (!headers || typeof headers !== "object") {
170
+ return void 0;
171
+ }
172
+ const key = name.toLowerCase();
173
+ const entries = headers;
174
+ for (const [entryKey, value] of Object.entries(entries)) {
175
+ if (entryKey.toLowerCase() !== key) continue;
176
+ if (typeof value === "string") return value;
177
+ if (Array.isArray(value)) {
178
+ const first = value.find((entry) => typeof entry === "string");
179
+ return typeof first === "string" ? first : void 0;
180
+ }
181
+ }
182
+ return void 0;
183
+ }
184
+ function parseSlackCanvasDetail(detail) {
185
+ if (typeof detail !== "string") {
186
+ return {};
187
+ }
188
+ const trimmed = detail.trim();
189
+ if (!trimmed) {
190
+ return {};
191
+ }
192
+ const parsed = {
193
+ detail: trimmed
194
+ };
195
+ const lineMatch = trimmed.match(/line\s+(\d+):/i);
196
+ if (lineMatch) {
197
+ const line = Number.parseInt(lineMatch[1] ?? "", 10);
198
+ if (Number.isFinite(line)) {
199
+ parsed.detailLine = line;
200
+ }
201
+ }
202
+ if (/unsupported heading depth/i.test(trimmed)) {
203
+ parsed.detailRule = "unsupported_heading_depth";
204
+ }
205
+ return parsed;
206
+ }
207
+ var installationTokenStorage = new AsyncLocalStorage2();
208
+ var clientsByToken = /* @__PURE__ */ new Map();
209
+ function runWithSlackInstallationToken(token, fn) {
210
+ const trimmed = token.trim();
211
+ if (!trimmed) {
212
+ throw new SlackActionError(
213
+ "Slack installation token binding requires a non-empty token",
214
+ "missing_token"
215
+ );
216
+ }
217
+ return installationTokenStorage.run({ token: trimmed }, fn);
218
+ }
219
+ function resolveSlackToken() {
220
+ const ambientToken = installationTokenStorage.getStore()?.token;
221
+ if (ambientToken) {
222
+ return ambientToken;
223
+ }
224
+ const envToken = getSlackBotToken();
225
+ if (envToken) {
226
+ return envToken;
227
+ }
228
+ const teamId = getWorkspaceTeamId();
229
+ if (teamId) {
230
+ throw new SlackActionError(
231
+ `Slack call is scoped to workspace ${teamId} but no installation token is bound and no default bot token is configured`,
232
+ "missing_token"
233
+ );
234
+ }
235
+ throw new SlackActionError(
236
+ "SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack Web API actions in this service",
237
+ "missing_token"
238
+ );
239
+ }
240
+ function normalizeSlackConversationId(channelId) {
241
+ return parseSlackChannelReferenceId(channelId);
242
+ }
243
+ function getClient() {
244
+ const token = resolveSlackToken();
245
+ let cached = clientsByToken.get(token);
246
+ if (!cached) {
247
+ cached = new WebClient(token, {
248
+ retryConfig: { retries: 0 },
249
+ rejectRateLimitedCalls: true
250
+ });
251
+ clientsByToken.set(token, cached);
252
+ }
253
+ return cached;
254
+ }
255
+ function mapSlackError(error) {
256
+ if (error instanceof SlackActionError) {
257
+ return error;
258
+ }
259
+ const candidate = error;
260
+ const apiError = candidate.data?.error;
261
+ const message = candidate.message ?? "Slack action failed";
262
+ const baseOptions = {
263
+ apiError,
264
+ statusCode: candidate.statusCode,
265
+ requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
266
+ errorData: serializeSlackErrorData(candidate.data),
267
+ ...parseSlackCanvasDetail(candidate.data?.detail)
268
+ };
269
+ if (apiError === "missing_scope") {
270
+ return new SlackActionError(message, "missing_scope", {
271
+ ...baseOptions,
272
+ needed: candidate.data?.needed,
273
+ provided: candidate.data?.provided
274
+ });
275
+ }
276
+ if (apiError === "not_in_channel") {
277
+ return new SlackActionError(message, "not_in_channel", baseOptions);
278
+ }
279
+ if (apiError === "restricted_action_read_only_channel") {
280
+ return new SlackActionError(message, "read_only_channel", baseOptions);
281
+ }
282
+ if (apiError === "already_reacted") {
283
+ return new SlackActionError(message, "already_reacted", baseOptions);
284
+ }
285
+ if (apiError === "no_reaction") {
286
+ return new SlackActionError(message, "no_reaction", baseOptions);
287
+ }
288
+ if (apiError === "invalid_arguments") {
289
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
290
+ }
291
+ if (apiError === "invalid_cursor") {
292
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
293
+ }
294
+ if (apiError === "invalid_name") {
295
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
296
+ }
297
+ if (apiError === "not_found" || apiError === "channel_not_found" || apiError === "message_not_found") {
298
+ return new SlackActionError(message, "not_found", baseOptions);
299
+ }
300
+ if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
301
+ return new SlackActionError(message, "feature_unavailable", baseOptions);
302
+ }
303
+ if (apiError === "canvas_creation_failed") {
304
+ return new SlackActionError(message, "canvas_creation_failed", baseOptions);
305
+ }
306
+ if (apiError === "canvas_editing_failed") {
307
+ return new SlackActionError(message, "canvas_editing_failed", baseOptions);
308
+ }
309
+ if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
310
+ return new SlackActionError(message, "rate_limited", {
311
+ ...baseOptions,
312
+ retryAfterSeconds: candidate.retryAfter
313
+ });
314
+ }
315
+ return new SlackActionError(message, "internal_error", baseOptions);
316
+ }
317
+ function sleep(ms) {
318
+ return new Promise((resolve) => setTimeout(resolve, ms));
319
+ }
320
+ var MAX_RETRY_DELAY_MS = 1e4;
321
+ var MAX_TOTAL_RETRY_DELAY_MS = 2e4;
322
+ var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
323
+ "ECONNREFUSED",
324
+ "ECONNRESET",
325
+ "ENOTFOUND",
326
+ "EAI_AGAIN"
327
+ ]);
328
+ var TIMEOUT_ERROR_CODES = /* @__PURE__ */ new Set([
329
+ "ETIMEDOUT",
330
+ "ECONNABORTED",
331
+ "ESOCKETTIMEDOUT"
332
+ ]);
333
+ function findNetworkErrorCode(error, depth = 0) {
334
+ if (!error || typeof error !== "object" || depth > 4) {
335
+ return void 0;
336
+ }
337
+ const candidate = error;
338
+ if (typeof candidate.code === "string" && (CONNECTION_ERROR_CODES.has(candidate.code) || TIMEOUT_ERROR_CODES.has(candidate.code))) {
339
+ return candidate.code;
340
+ }
341
+ return findNetworkErrorCode(candidate.original, depth + 1) ?? findNetworkErrorCode(candidate.cause, depth + 1);
342
+ }
343
+ function hasSocketHangUpMessage(error) {
344
+ return error instanceof Error && error.message.toLowerCase().includes("socket hang up");
345
+ }
346
+ function classifySlackRetry(raw, mapped) {
347
+ if (mapped.code === "rate_limited") {
348
+ return "rate_limited";
349
+ }
350
+ if (mapped.statusCode !== void 0 && mapped.statusCode >= 500) {
351
+ return "server_error";
352
+ }
353
+ const networkCode = findNetworkErrorCode(raw);
354
+ if (networkCode && CONNECTION_ERROR_CODES.has(networkCode)) {
355
+ return "connection";
356
+ }
357
+ if (networkCode && TIMEOUT_ERROR_CODES.has(networkCode)) {
358
+ return "timeout";
359
+ }
360
+ if (hasSocketHangUpMessage(raw)) {
361
+ return "connection";
362
+ }
363
+ return "none";
364
+ }
365
+ async function withSlackRetries(task, maxAttempts = 3, context = {}) {
366
+ let attempt = 0;
367
+ let totalDelayMs = 0;
368
+ const action = context.action ?? "unknown";
369
+ while (attempt < maxAttempts) {
370
+ attempt += 1;
371
+ const attemptNumber = attempt;
372
+ try {
373
+ return await withSpan(
374
+ `POST slack.com/api/${action}`,
375
+ "http.client",
376
+ {},
377
+ async () => {
378
+ try {
379
+ return await task();
380
+ } catch (error) {
381
+ const mapped = mapSlackError(error);
382
+ const errorAttrs = {
383
+ "error.type": mapped.code
384
+ };
385
+ if (mapped.apiError) {
386
+ errorAttrs["app.slack.api_error_code"] = mapped.apiError;
387
+ }
388
+ if (mapped.code === "rate_limited") {
389
+ errorAttrs["http.response.status_code"] = 429;
390
+ if (mapped.retryAfterSeconds) {
391
+ errorAttrs["app.slack.retry_after_ms"] = mapped.retryAfterSeconds * 1e3;
392
+ }
393
+ } else if (mapped.statusCode != null) {
394
+ errorAttrs["http.response.status_code"] = mapped.statusCode;
395
+ }
396
+ setSpanAttributes(errorAttrs);
397
+ setSpanStatus("error");
398
+ throw error;
399
+ }
400
+ },
401
+ {
402
+ "http.request.method": "POST",
403
+ "server.address": "slack.com",
404
+ "url.scheme": "https",
405
+ "url.path": `/api/${action}`,
406
+ "app.slack.method": action,
407
+ "app.retry.max_attempts": maxAttempts,
408
+ ...attemptNumber > 1 ? { "http.resend_count": attemptNumber - 1 } : {},
409
+ ...context.attributes ?? {},
410
+ ...context.spanAttributes ?? {}
411
+ }
412
+ );
413
+ } catch (error) {
414
+ const mapped = mapSlackError(error);
415
+ const retryClass = classifySlackRetry(error, mapped);
416
+ const isRetryable = retryClass === "rate_limited" || retryClass === "connection" || retryClass === "server_error" || retryClass === "timeout" && context.idempotent === true;
417
+ const remainingDelayBudgetMs = MAX_TOTAL_RETRY_DELAY_MS - totalDelayMs;
418
+ const baseLogAttributes = {
419
+ "app.slack.action": action,
420
+ "app.slack.error_code": mapped.code,
421
+ ...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
422
+ ...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
423
+ ...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
424
+ ...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
425
+ ...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
426
+ ...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
427
+ ...context.attributes ?? {}
428
+ };
429
+ if (!isRetryable || attempt >= maxAttempts || remainingDelayBudgetMs <= 0) {
430
+ logWarn(
431
+ "slack_action_failed",
432
+ {},
433
+ {
434
+ ...baseLogAttributes,
435
+ ...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
436
+ },
437
+ "Slack action failed"
438
+ );
439
+ throw mapped;
440
+ }
441
+ logWarn(
442
+ "slack_action_retrying",
443
+ {},
444
+ {
445
+ ...baseLogAttributes,
446
+ "app.slack.retry_attempt": attempt,
447
+ "app.slack.retry_class": retryClass
448
+ },
449
+ "Retrying Slack action after transient failure"
450
+ );
451
+ const retryAfterMs = retryClass === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
452
+ const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
453
+ const delayMs = Math.min(
454
+ retryAfterMs ?? backoffMs,
455
+ MAX_RETRY_DELAY_MS,
456
+ remainingDelayBudgetMs
457
+ );
458
+ totalDelayMs += delayMs;
459
+ await sleep(delayMs);
460
+ }
461
+ }
462
+ throw new SlackActionError(
463
+ "Slack action exhausted retries",
464
+ "internal_error"
465
+ );
466
+ }
467
+ function getSlackClient() {
468
+ return getClient();
469
+ }
470
+ function isDmChannel(channelId) {
471
+ const normalized = normalizeSlackConversationId(channelId);
472
+ return Boolean(normalized && normalized.startsWith("D"));
473
+ }
474
+ function isConversationScopedChannel(channelId) {
475
+ const normalized = normalizeSlackConversationId(channelId);
476
+ if (!normalized) return false;
477
+ return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
478
+ }
479
+ function isConversationChannel(channelId) {
480
+ const normalized = normalizeSlackConversationId(channelId);
481
+ if (!normalized) return false;
482
+ return normalized.startsWith("C") || normalized.startsWith("G");
483
+ }
484
+ async function getFilePermalink(fileId) {
485
+ const client = getClient();
486
+ const response = await withSlackRetries(
487
+ () => client.files.info({
488
+ file: fileId
489
+ }),
490
+ 3,
491
+ {
492
+ action: "files.info",
493
+ idempotent: true,
494
+ spanAttributes: { "app.slack.file_id": fileId }
495
+ }
496
+ );
497
+ return response.file?.permalink;
498
+ }
499
+ async function downloadPrivateSlackFile(url) {
500
+ const token = resolveSlackToken();
501
+ return withSpan(
502
+ "GET files.slack.com",
503
+ "http.client",
504
+ {},
505
+ async () => {
506
+ const response = await fetch(url, {
507
+ headers: {
508
+ Authorization: `Bearer ${token}`
509
+ }
510
+ });
511
+ setSpanAttributes({ "http.response.status_code": response.status });
512
+ if (!response.ok) {
513
+ setSpanAttributes({ "error.type": String(response.status) });
514
+ setSpanStatus("error");
515
+ throw new Error(`Slack file download failed: ${response.status}`);
516
+ }
517
+ return Buffer.from(await response.arrayBuffer());
518
+ },
519
+ {
520
+ "http.request.method": "GET",
521
+ "server.address": "files.slack.com",
522
+ "url.scheme": "https",
523
+ "app.slack.method": "files.download"
524
+ }
525
+ );
526
+ }
527
+
528
+ // src/chat/credentials/subject.ts
101
529
  var CREDENTIAL_SUBJECT_HMAC_CONTEXT = "junior.credential_subject.v1";
102
530
  var CREDENTIAL_SUBJECT_SIGNATURE_VERSION = "v1";
103
531
  function getCredentialSubjectSecret() {
@@ -189,17 +617,18 @@ function verifySlackDirectCredentialSubject(input) {
189
617
  return timingSafeMatch(expected, binding.signature);
190
618
  }
191
619
 
192
- // src/chat/tools/channel-capabilities.ts
620
+ // src/chat/slack/tools/channel-capabilities.ts
193
621
  function resolveChannelCapabilities(channelId) {
194
622
  return {
195
623
  canCreateCanvas: isConversationScopedChannel(channelId),
624
+ canSendMessage: isConversationScopedChannel(channelId),
196
625
  canPostToChannel: isConversationChannel(channelId),
197
626
  canAddReactions: isConversationScopedChannel(channelId)
198
627
  };
199
628
  }
200
629
 
201
630
  // src/chat/plugins/agent-hooks.ts
202
- import { z as z2 } from "zod";
631
+ import { z } from "zod";
203
632
  var PluginHookDeniedError = class extends Error {
204
633
  constructor(message) {
205
634
  super(message);
@@ -226,8 +655,8 @@ var PLUGIN_ROUTE_METHODS = /* @__PURE__ */ new Set([
226
655
  "ALL"
227
656
  ]);
228
657
  var PLUGIN_PROMPT_CONTRIBUTION_TOTAL_MAX_CHARS = 16e3;
229
- var systemPromptMessageArraySchema = z2.array(promptMessageSchema);
230
- var userPromptMessageArraySchema = z2.array(promptMessageSchema);
658
+ var systemPromptMessageArraySchema = z.array(promptMessageSchema);
659
+ var userPromptMessageArraySchema = z.array(promptMessageSchema);
231
660
  function isRecord(value) {
232
661
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
233
662
  }
@@ -545,6 +974,7 @@ function getPluginTools(context) {
545
974
  name: localName,
546
975
  plugin: pluginName
547
976
  };
977
+ definition.exposure = "deferred";
548
978
  tools[name] = definition;
549
979
  }
550
980
  }
@@ -992,8 +1422,19 @@ function createPluginHookRunner(input = {}) {
992
1422
  export {
993
1423
  createPluginModel,
994
1424
  createPluginEmbedder,
995
- parseSlackMessageTs,
996
1425
  getSlackToolContext,
1426
+ runWithWorkspaceTeamId,
1427
+ getWorkspaceTeamId,
1428
+ SlackActionError,
1429
+ getHeaderString,
1430
+ runWithSlackInstallationToken,
1431
+ normalizeSlackConversationId,
1432
+ withSlackRetries,
1433
+ getSlackClient,
1434
+ isDmChannel,
1435
+ isConversationScopedChannel,
1436
+ getFilePermalink,
1437
+ downloadPrivateSlackFile,
997
1438
  bindSlackDirectCredentialSubject,
998
1439
  verifySlackDirectCredentialSubject,
999
1440
  resolveChannelCapabilities,