@sentry/junior 0.200.0 → 0.202.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 (65) hide show
  1. package/dist/{agent-hooks-6LGY2MBH.js → agent-hooks-UXITLYOK.js} +11 -11
  2. package/dist/api.js +25 -25
  3. package/dist/app.js +228 -122
  4. package/dist/{catalog-runtime-CIO3ERVQ.js → catalog-runtime-BG3JLLK2.js} +4 -4
  5. package/dist/{chunk-PMSENMWY.js → chunk-2MK3QBGA.js} +1 -1
  6. package/dist/{chunk-EOOWKTQC.js → chunk-3PGQSKJV.js} +9 -9
  7. package/dist/{chunk-ZPLAAHVP.js → chunk-3UI3WFTX.js} +5 -5
  8. package/dist/{chunk-SY2RQ77O.js → chunk-5NOPO64K.js} +1 -1
  9. package/dist/{chunk-JLUCKXGI.js → chunk-5PKWYPFT.js} +6 -6
  10. package/dist/{chunk-GFXDMDHR.js → chunk-63NKKFJL.js} +2 -2
  11. package/dist/{chunk-M7BHRB3G.js → chunk-6AYH6252.js} +4 -4
  12. package/dist/{chunk-RDWWCDA4.js → chunk-7DHZP5KJ.js} +2 -2
  13. package/dist/{chunk-RG74Q34N.js → chunk-A76OIMYN.js} +1 -1
  14. package/dist/{chunk-YVP6QNBZ.js → chunk-AWMS3SPP.js} +3 -3
  15. package/dist/{chunk-ODSGDVHT.js → chunk-B3LY5RCL.js} +5 -5
  16. package/dist/{chunk-4Q5IR5GA.js → chunk-BD2KT3OB.js} +1 -1
  17. package/dist/{chunk-5Q6KP6LH.js → chunk-BXCX5QKA.js} +1 -1
  18. package/dist/{chunk-TF5TXGRD.js → chunk-C6VVF43U.js} +1 -1
  19. package/dist/{chunk-5JDNDQHL.js → chunk-DBVY6HLX.js} +1 -1
  20. package/dist/{chunk-PIHLVU7T.js → chunk-DL2KLKVH.js} +6 -6
  21. package/dist/{chunk-3WUBDFFL.js → chunk-ELWOYVOY.js} +1 -1
  22. package/dist/{chunk-PSQ4SWI3.js → chunk-EWT7H65X.js} +4 -4
  23. package/dist/{chunk-OTTKTTVI.js → chunk-HMREVIO2.js} +2 -2
  24. package/dist/{chunk-LUITHZZL.js → chunk-IIWUT4FO.js} +8 -8
  25. package/dist/{chunk-V2VL335Y.js → chunk-JSJQEWBJ.js} +21 -21
  26. package/dist/{chunk-T6263C3N.js → chunk-KHKKHDLD.js} +2 -2
  27. package/dist/{chunk-OIILBD56.js → chunk-LA6R4A7Q.js} +3 -3
  28. package/dist/{chunk-X2Q4H6JQ.js → chunk-LOOU2DUR.js} +315 -166
  29. package/dist/{chunk-ADNSL3MX.js → chunk-M6FHWXE3.js} +9 -9
  30. package/dist/{chunk-JMRHEW6B.js → chunk-M76HMB7M.js} +3 -3
  31. package/dist/{chunk-7DO36XLF.js → chunk-MAGNK5J4.js} +1 -1
  32. package/dist/{chunk-OVHO76WN.js → chunk-NSXKRGRT.js} +3 -3
  33. package/dist/{chunk-UR43NMUR.js → chunk-OWMPFXJA.js} +4 -4
  34. package/dist/{chunk-K2QYWZ7F.js → chunk-PW4P4JYF.js} +14 -3
  35. package/dist/{chunk-VLBLCJXK.js → chunk-QB54UQUS.js} +5 -5
  36. package/dist/{chunk-752M26LC.js → chunk-QP3OGJXF.js} +1 -1
  37. package/dist/{chunk-LICM66W2.js → chunk-REOIRYPR.js} +2 -2
  38. package/dist/{chunk-AV552V2K.js → chunk-SCOZRLZ4.js} +3 -3
  39. package/dist/{chunk-IAZBUDVU.js → chunk-TXJSXBV5.js} +36 -30
  40. package/dist/{chunk-N5VHBUXF.js → chunk-U2GPUYAL.js} +19 -6
  41. package/dist/{chunk-22Y5SBDM.js → chunk-WQSHDTTI.js} +8 -8
  42. package/dist/{chunk-5VRPXF4T.js → chunk-YDUR3ZYW.js} +1 -1
  43. package/dist/cli/chat.js +33 -33
  44. package/dist/cli/check.js +5 -5
  45. package/dist/cli/plugins.js +12 -12
  46. package/dist/cli/snapshot-create.js +9 -9
  47. package/dist/cli/upgrade.js +6 -6
  48. package/dist/{db-SCTYCGQ5.js → db-3L2MCVRI.js} +6 -6
  49. package/dist/instrumentation.js +3 -3
  50. package/dist/nitro.js +6 -6
  51. package/dist/{oauth-callback-server-QXKBXP5M.js → oauth-callback-server-YVV6C6T2.js} +29 -29
  52. package/dist/{runner-Z3EQSZ23.js → runner-LQ7RETDP.js} +21 -21
  53. package/dist/{skills-CGEPG74A.js → skills-XRWYEDCD.js} +5 -5
  54. package/dist/spawn-RC6SJUAI.js +42 -0
  55. package/dist/task-runner-7DY7ARYH.js +36 -0
  56. package/dist/{validation-YLALMVMB.js → validation-7MNQGCJA.js} +5 -5
  57. package/dist/version.js +1 -1
  58. package/dist/{work-N6PUG4MY.js → work-COALHUCG.js} +21 -21
  59. package/dist/{worker-M7464FAO.js → worker-6MURZIPD.js} +10 -10
  60. package/migrations/0039_attachment_provider.sql +7 -0
  61. package/migrations/meta/0039_snapshot.json +3368 -0
  62. package/migrations/meta/_journal.json +7 -0
  63. package/package.json +6 -6
  64. package/dist/spawn-WBEM46BW.js +0 -42
  65. package/dist/task-runner-2X5MUSHN.js +0 -36
@@ -7,7 +7,7 @@ import {
7
7
  PluginCredentialFailureError,
8
8
  agentNameSchema,
9
9
  createPluginAuthOrchestration
10
- } from "./chunk-VLBLCJXK.js";
10
+ } from "./chunk-QB54UQUS.js";
11
11
  import {
12
12
  parseSandboxEgressAuthRequiredSignal,
13
13
  parseSandboxEgressPermissionDeniedSignal,
@@ -38,7 +38,7 @@ import {
38
38
  searchSlackUsers,
39
39
  selectPluginGrant,
40
40
  toMcpProviderError
41
- } from "./chunk-22Y5SBDM.js";
41
+ } from "./chunk-WQSHDTTI.js";
42
42
  import {
43
43
  RetryableDeliveryError,
44
44
  actorFromRun,
@@ -53,7 +53,7 @@ import {
53
53
  surfaceFromRun,
54
54
  toolInvocationDestination,
55
55
  uploadFilesToConversation
56
- } from "./chunk-PIHLVU7T.js";
56
+ } from "./chunk-DL2KLKVH.js";
57
57
  import {
58
58
  CooperativeTurnYieldError,
59
59
  NO_REPLY_MARKER,
@@ -65,7 +65,7 @@ import {
65
65
  isNoReplyMarker,
66
66
  isTurnInputCommitLostError,
67
67
  sanitizeAssistantText
68
- } from "./chunk-3WUBDFFL.js";
68
+ } from "./chunk-ELWOYVOY.js";
69
69
  import {
70
70
  AGENTS_REMOVAL_NOTICE,
71
71
  AGENTS_REPLACEMENT_NOTICE,
@@ -121,7 +121,7 @@ import {
121
121
  stripRuntimeTurnContext,
122
122
  trimTrailingAssistantMessages,
123
123
  truncateText
124
- } from "./chunk-JLUCKXGI.js";
124
+ } from "./chunk-5PKWYPFT.js";
125
125
  import {
126
126
  SCHEDULED_TASK_SYSTEM_ACTOR,
127
127
  WORKSPACE_SNAPSHOT_FAILED_EVENT,
@@ -147,6 +147,7 @@ import {
147
147
  makeStructuredToolOutput,
148
148
  normalizeEventIdentifier,
149
149
  pluginSupportsEvent,
150
+ readLiveAttachment,
150
151
  readScheduledTask,
151
152
  registeredEventTaskTriggerSchema,
152
153
  registeredEventTypeSchema,
@@ -162,13 +163,13 @@ import {
162
163
  workspaceSnapshotFinishedEvent,
163
164
  workspaceSnapshotWatch,
164
165
  writableEventTask
165
- } from "./chunk-IAZBUDVU.js";
166
+ } from "./chunk-TXJSXBV5.js";
166
167
  import {
167
168
  parseSlackThreadId
168
- } from "./chunk-RG74Q34N.js";
169
+ } from "./chunk-A76OIMYN.js";
169
170
  import {
170
171
  appendAndEnqueueInboundMessage
171
- } from "./chunk-UR43NMUR.js";
172
+ } from "./chunk-OWMPFXJA.js";
172
173
  import {
173
174
  SHORT_TITLE_MAX_LENGTH,
174
175
  ToolInputError,
@@ -176,7 +177,7 @@ import {
176
177
  generateShortTitle,
177
178
  hasAgentTurnUsage,
178
179
  resolveTaskTitle
179
- } from "./chunk-RDWWCDA4.js";
180
+ } from "./chunk-7DHZP5KJ.js";
180
181
  import {
181
182
  bindSandboxFileSystem,
182
183
  createSandboxSession,
@@ -187,7 +188,7 @@ import {
187
188
  resolve,
188
189
  setSnapshotSpanAttributes,
189
190
  stopSession
190
- } from "./chunk-OIILBD56.js";
191
+ } from "./chunk-LA6R4A7Q.js";
191
192
  import {
192
193
  privateTraceResultAttributes
193
194
  } from "./chunk-UIE3R5XU.js";
@@ -197,7 +198,7 @@ import {
197
198
  } from "./chunk-X6GDDWQH.js";
198
199
  import {
199
200
  getTurnRequestDeadline
200
- } from "./chunk-4Q5IR5GA.js";
201
+ } from "./chunk-BD2KT3OB.js";
201
202
  import {
202
203
  getVercelConversationWorkQueue
203
204
  } from "./chunk-DKIJ2RP5.js";
@@ -244,7 +245,7 @@ import {
244
245
  updateWorkspace,
245
246
  withSlackRetries,
246
247
  workspaceRepoCheckoutPath
247
- } from "./chunk-N5VHBUXF.js";
248
+ } from "./chunk-U2GPUYAL.js";
248
249
  import {
249
250
  JUNIOR_THREAD_STATE_TTL_MS
250
251
  } from "./chunk-3Y47VMHV.js";
@@ -254,12 +255,13 @@ import {
254
255
  getConversationStore,
255
256
  getDb,
256
257
  getSqlExecutor
257
- } from "./chunk-JMRHEW6B.js";
258
+ } from "./chunk-M76HMB7M.js";
258
259
  import {
259
260
  juniorArtifacts,
261
+ juniorAttachments,
260
262
  juniorIdentities,
261
263
  juniorSchedulerRuns
262
- } from "./chunk-K2QYWZ7F.js";
264
+ } from "./chunk-PW4P4JYF.js";
263
265
  import {
264
266
  SANDBOX_DATA_ROOT,
265
267
  SANDBOX_REPOS_ROOT,
@@ -290,12 +292,12 @@ import {
290
292
  throwSandboxOperationError,
291
293
  workspaceSnapshotNotReadyUserMessage,
292
294
  wrapSandboxSetupError
293
- } from "./chunk-OVHO76WN.js";
295
+ } from "./chunk-NSXKRGRT.js";
294
296
  import {
295
297
  fenceLock,
296
298
  getStateAdapter,
297
299
  withLock
298
- } from "./chunk-PMSENMWY.js";
300
+ } from "./chunk-2MK3QBGA.js";
299
301
  import {
300
302
  KNOWN_CONVERSATION_EVENT_TYPES,
301
303
  contextProvenance,
@@ -328,7 +330,7 @@ import {
328
330
  resolveGatewayCredential,
329
331
  resolveGatewayModel,
330
332
  unwrapCurrentInstruction
331
- } from "./chunk-GFXDMDHR.js";
333
+ } from "./chunk-63NKKFJL.js";
332
334
  import {
333
335
  ModelProfileNotConfiguredError,
334
336
  TURN_REASONING_LEVELS,
@@ -347,16 +349,16 @@ import {
347
349
  toGenAiMessagesTraceAttributes,
348
350
  toGenAiPayloadMetadata,
349
351
  toGenAiPayloadTraceAttributes
350
- } from "./chunk-SY2RQ77O.js";
352
+ } from "./chunk-5NOPO64K.js";
351
353
  import {
352
354
  discoverSkills,
353
355
  findSkillByName,
354
356
  loadSkillsByName,
355
357
  parseSkillInvocation
356
- } from "./chunk-T6263C3N.js";
358
+ } from "./chunk-KHKKHDLD.js";
357
359
  import {
358
360
  pluginCatalogRuntime
359
- } from "./chunk-5VRPXF4T.js";
361
+ } from "./chunk-YDUR3ZYW.js";
360
362
  import {
361
363
  CredentialUnavailableError,
362
364
  OAuthProviderError,
@@ -364,7 +366,7 @@ import {
364
366
  getOAuthProviderErrorAttributes,
365
367
  resolveAuthTokenPlaceholder,
366
368
  resolvePluginCommandEnv
367
- } from "./chunk-5Q6KP6LH.js";
369
+ } from "./chunk-BXCX5QKA.js";
368
370
  import {
369
371
  credentialContextForActor,
370
372
  credentialUserSubjectId
@@ -400,7 +402,7 @@ import {
400
402
  summarizeMessageText,
401
403
  withLogContext,
402
404
  withSpan
403
- } from "./chunk-7DO36XLF.js";
405
+ } from "./chunk-MAGNK5J4.js";
404
406
  import {
405
407
  startInactiveSpan,
406
408
  withActiveSpan
@@ -2052,6 +2054,18 @@ function buildTurnContextPrompt(params) {
2052
2054
  return sections.join("\n\n");
2053
2055
  }
2054
2056
 
2057
+ // src/chat/attachments/media.ts
2058
+ var VISION_IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Set([
2059
+ "image/gif",
2060
+ "image/jpeg",
2061
+ "image/png",
2062
+ "image/webp"
2063
+ ]);
2064
+ function isVisionImageMediaType(mediaType) {
2065
+ const normalized = mediaType.split(";", 1)[0]?.trim().toLowerCase();
2066
+ return normalized ? VISION_IMAGE_MEDIA_TYPES.has(normalized) : false;
2067
+ }
2068
+
2055
2069
  // src/chat/agent/prompt.ts
2056
2070
  var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
2057
2071
  var MAX_ROUTER_ATTACHMENT_PREVIEW_CHARS = 2e3;
@@ -2178,7 +2192,7 @@ function buildUserTurnInput(args) {
2178
2192
  });
2179
2193
  continue;
2180
2194
  }
2181
- if (attachment.mediaType.startsWith("image/")) {
2195
+ if (isVisionImageMediaType(attachment.mediaType)) {
2182
2196
  if (!attachment.data) {
2183
2197
  throw new Error("Image attachment is missing image data");
2184
2198
  }
@@ -2966,8 +2980,8 @@ function sandboxProxyUrl(credentialToken) {
2966
2980
  "Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
2967
2981
  );
2968
2982
  }
2969
- const path8 = credentialToken ? `${SANDBOX_EGRESS_PROXY_PATH}/${credentialToken}` : SANDBOX_EGRESS_PROXY_PATH;
2970
- return new URL(path8, baseUrl).toString();
2983
+ const path9 = credentialToken ? `${SANDBOX_EGRESS_PROXY_PATH}/${credentialToken}` : SANDBOX_EGRESS_PROXY_PATH;
2984
+ return new URL(path9, baseUrl).toString();
2971
2985
  }
2972
2986
  function buildSandboxEgressNetworkPolicy(input) {
2973
2987
  const allow = {
@@ -5291,8 +5305,8 @@ function parseToolContent(content) {
5291
5305
  }
5292
5306
  return content;
5293
5307
  }
5294
- function formatZodPath(path8) {
5295
- return path8.length > 0 ? path8.map(String).join(".") : "root";
5308
+ function formatZodPath(path9) {
5309
+ return path9.length > 0 ? path9.map(String).join(".") : "root";
5296
5310
  }
5297
5311
  function formatToolInputError(error) {
5298
5312
  const details = error.issues.slice(0, 5).map((issue) => `${formatZodPath(issue.path)}: ${issue.message}`).join("; ");
@@ -6193,8 +6207,8 @@ function sliceFileContent(params) {
6193
6207
  } : void 0
6194
6208
  });
6195
6209
  }
6196
- function missingFileResult(path8) {
6197
- throw new ToolInputError(`File not found: ${path8}`);
6210
+ function missingFileResult(path9) {
6211
+ throw new ToolInputError(`File not found: ${path9}`);
6198
6212
  }
6199
6213
 
6200
6214
  // src/chat/tools/sandbox/read-file.ts
@@ -6845,9 +6859,9 @@ var MIME_BY_EXTENSION = {
6845
6859
  ".log": "text/plain"
6846
6860
  };
6847
6861
  var SandboxFileNotFoundError = class extends ToolInputError {
6848
- constructor(path8) {
6849
- super(`failed to read file: ${path8}`);
6850
- this.path = path8;
6862
+ constructor(path9) {
6863
+ super(`failed to read file: ${path9}`);
6864
+ this.path = path9;
6851
6865
  }
6852
6866
  path;
6853
6867
  };
@@ -8688,13 +8702,13 @@ function createViewImageTool(sandbox, supportsImageInput, deps = {}) {
8688
8702
  description: "View an image file from the sandbox workspace when visual inspection is needed. Supports PNG, JPEG, GIF, and WebP images up to 5 MB.",
8689
8703
  exposure: "direct",
8690
8704
  inputSchema: viewImageInputSchema,
8691
- async execute({ path: path8 }) {
8705
+ async execute({ path: path9 }) {
8692
8706
  if (!supportsImageInput()) {
8693
8707
  throw new ToolInputError(
8694
8708
  "viewImage is unavailable because the current model does not support image inputs"
8695
8709
  );
8696
8710
  }
8697
- const targetPath = normalizeSandboxPath(path8);
8711
+ const targetPath = normalizeSandboxPath(path9);
8698
8712
  const data = deps.readFile ? await deps.readFile(targetPath) : await sandbox.readFileToBuffer({ path: targetPath });
8699
8713
  if (!data) {
8700
8714
  throw new SandboxFileNotFoundError(targetPath);
@@ -13113,9 +13127,45 @@ function renderAttachmentText(input) {
13113
13127
  return rendered.slice(0, MAX_ATTACHMENT_TEXT_CHARS);
13114
13128
  }
13115
13129
 
13130
+ // src/chat/slack/attachments.ts
13131
+ import { and as and2, eq as eq2, inArray as inArray2, isNull } from "drizzle-orm";
13132
+ async function recordSlackAttachment(args) {
13133
+ await args.db.db().update(juniorAttachments).set({ visionSummary: args.visionSummary }).where(
13134
+ and2(
13135
+ eq2(juniorAttachments.id, args.attachmentId),
13136
+ eq2(juniorAttachments.conversationId, args.conversationId),
13137
+ isNull(juniorAttachments.deleteRequestedAt)
13138
+ )
13139
+ );
13140
+ }
13141
+ async function readAttachmentVisionSummary(args) {
13142
+ const [row] = await args.db.db().select({ visionSummary: juniorAttachments.visionSummary }).from(juniorAttachments).where(
13143
+ and2(
13144
+ eq2(juniorAttachments.id, args.attachmentId),
13145
+ eq2(juniorAttachments.conversationId, args.conversationId),
13146
+ isNull(juniorAttachments.deleteRequestedAt)
13147
+ )
13148
+ );
13149
+ return row?.visionSummary;
13150
+ }
13151
+ async function matchSlackAttachments(args) {
13152
+ if (args.providerIds.length === 0) return [];
13153
+ return await args.db.db().select({
13154
+ id: juniorAttachments.id,
13155
+ providerId: juniorAttachments.providerId
13156
+ }).from(juniorAttachments).where(
13157
+ and2(
13158
+ eq2(juniorAttachments.conversationId, args.conversationId),
13159
+ eq2(juniorAttachments.provider, "slack"),
13160
+ inArray2(juniorAttachments.providerId, args.providerIds),
13161
+ isNull(juniorAttachments.deleteRequestedAt)
13162
+ )
13163
+ );
13164
+ }
13165
+
13116
13166
  // src/chat/slack/tools/thread-read.ts
13117
13167
  var MAX_THREAD_READ_CHARS = 4e4;
13118
- function sanitizeMessage(msg) {
13168
+ function sanitizeMessage(msg, attachmentIdsBySlackFileId) {
13119
13169
  const attachmentText = renderAttachmentText(msg.attachments);
13120
13170
  return {
13121
13171
  ts: msg.ts,
@@ -13131,7 +13181,8 @@ function sanitizeMessage(msg) {
13131
13181
  id: f.id,
13132
13182
  name: f.name,
13133
13183
  mimetype: f.mimetype,
13134
- size: f.size
13184
+ size: f.size,
13185
+ ...f.id && attachmentIdsBySlackFileId.get(f.id) ? { attachment_id: attachmentIdsBySlackFileId.get(f.id) } : void 0
13135
13186
  }))
13136
13187
  } : void 0
13137
13188
  };
@@ -13149,7 +13200,7 @@ function truncateMessages(messages, maxChars) {
13149
13200
  }
13150
13201
  return { messages: kept, omitted: messages.length - kept.length };
13151
13202
  }
13152
- function createSlackThreadReadTool(context) {
13203
+ function createSlackThreadReadTool(context, attachmentDeps) {
13153
13204
  return zodTool({
13154
13205
  description: "Read a Slack thread from a shared archive URL or explicit channel + timestamp. Works for the current conversation and public channels.",
13155
13206
  annotations: {
@@ -13268,7 +13319,22 @@ function createSlackThreadReadTool(context) {
13268
13319
  }
13269
13320
  const root = replies[0];
13270
13321
  const resolvedThreadTs = threadTs ?? root?.thread_ts ?? root?.ts ?? lookupTs;
13271
- const sanitized = replies.map(sanitizeMessage);
13322
+ const slackFileIds = replies.flatMap(
13323
+ (reply) => (reply.files ?? []).map((file) => file.id).filter((fileId) => Boolean(fileId))
13324
+ );
13325
+ const storedAttachments = attachmentDeps ? await matchSlackAttachments({
13326
+ conversationId: attachmentDeps.conversationId,
13327
+ db: attachmentDeps.db,
13328
+ providerIds: slackFileIds
13329
+ }) : [];
13330
+ const attachmentIdsBySlackFileId = new Map(
13331
+ storedAttachments.flatMap(
13332
+ (attachment) => attachment.providerId ? [[attachment.providerId, attachment.id]] : []
13333
+ )
13334
+ );
13335
+ const sanitized = replies.map(
13336
+ (reply) => sanitizeMessage(reply, attachmentIdsBySlackFileId)
13337
+ );
13272
13338
  const { messages, omitted } = truncateMessages(
13273
13339
  sanitized,
13274
13340
  MAX_THREAD_READ_CHARS
@@ -13290,7 +13356,7 @@ function createSlackThreadReadTool(context) {
13290
13356
  }
13291
13357
 
13292
13358
  // src/chat/tools/user-lookup.ts
13293
- import { and as and2, eq as eq2, or, sql } from "drizzle-orm";
13359
+ import { and as and3, eq as eq3, or, sql } from "drizzle-orm";
13294
13360
  import { alias } from "drizzle-orm/pg-core";
13295
13361
  import { z as z54 } from "zod";
13296
13362
  var linkedSlackIdentity = alias(juniorIdentities, "user_lookup_slack_identity");
@@ -13356,12 +13422,12 @@ async function storedSlackUserByEmail(teamId, email) {
13356
13422
  const emailNormalized = normalizeIdentityEmail(email);
13357
13423
  if (!emailNormalized) return void 0;
13358
13424
  const identities = await getSqlExecutor().db().select().from(juniorIdentities).where(
13359
- and2(
13360
- eq2(juniorIdentities.provider, "slack"),
13361
- eq2(juniorIdentities.providerTenantId, teamId),
13362
- eq2(juniorIdentities.kind, "user"),
13363
- eq2(juniorIdentities.emailVerified, true),
13364
- eq2(juniorIdentities.emailNormalized, emailNormalized)
13425
+ and3(
13426
+ eq3(juniorIdentities.provider, "slack"),
13427
+ eq3(juniorIdentities.providerTenantId, teamId),
13428
+ eq3(juniorIdentities.kind, "user"),
13429
+ eq3(juniorIdentities.emailVerified, true),
13430
+ eq3(juniorIdentities.emailNormalized, emailNormalized)
13365
13431
  )
13366
13432
  ).limit(2);
13367
13433
  if (identities.length > 1) {
@@ -13413,18 +13479,18 @@ async function resolveSlack(teamId, query) {
13413
13479
  async function resolveStoredProvider(teamId, provider, query) {
13414
13480
  const rows = await getSqlExecutor().db().select({ slack: linkedSlackIdentity }).from(juniorIdentities).innerJoin(
13415
13481
  linkedSlackIdentity,
13416
- and2(
13417
- eq2(linkedSlackIdentity.userId, juniorIdentities.userId),
13418
- eq2(linkedSlackIdentity.kind, "user"),
13419
- eq2(linkedSlackIdentity.provider, "slack"),
13420
- eq2(linkedSlackIdentity.providerTenantId, teamId)
13482
+ and3(
13483
+ eq3(linkedSlackIdentity.userId, juniorIdentities.userId),
13484
+ eq3(linkedSlackIdentity.kind, "user"),
13485
+ eq3(linkedSlackIdentity.provider, "slack"),
13486
+ eq3(linkedSlackIdentity.providerTenantId, teamId)
13421
13487
  )
13422
13488
  ).where(
13423
- and2(
13424
- eq2(juniorIdentities.kind, "user"),
13425
- eq2(juniorIdentities.provider, provider),
13489
+ and3(
13490
+ eq3(juniorIdentities.kind, "user"),
13491
+ eq3(juniorIdentities.provider, provider),
13426
13492
  or(
13427
- eq2(juniorIdentities.providerSubjectId, query),
13493
+ eq3(juniorIdentities.providerSubjectId, query),
13428
13494
  sql`lower(${juniorIdentities.handle}) = lower(${query})`
13429
13495
  )
13430
13496
  )
@@ -13558,7 +13624,7 @@ import { z as z56 } from "zod";
13558
13624
 
13559
13625
  // src/chat/artifacts/store.ts
13560
13626
  import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
13561
- import { and as and3, eq as eq3, isNull } from "drizzle-orm";
13627
+ import { and as and4, eq as eq4, isNull as isNull2 } from "drizzle-orm";
13562
13628
  var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
13563
13629
  var IMAGE_TYPES = {
13564
13630
  gif: "image/gif",
@@ -13645,9 +13711,9 @@ async function publishImage(args) {
13645
13711
  const [existing] = await args.db.db().select({
13646
13712
  id: juniorArtifacts.id
13647
13713
  }).from(juniorArtifacts).where(
13648
- and3(
13649
- eq3(juniorArtifacts.conversationId, args.conversationId),
13650
- eq3(juniorArtifacts.sha256, sha256)
13714
+ and4(
13715
+ eq4(juniorArtifacts.conversationId, args.conversationId),
13716
+ eq4(juniorArtifacts.sha256, sha256)
13651
13717
  )
13652
13718
  );
13653
13719
  const id = existing?.id ?? randomUUID4();
@@ -13708,9 +13774,9 @@ async function unpublishArtifact(args) {
13708
13774
  }
13709
13775
  const now = args.now ?? /* @__PURE__ */ new Date();
13710
13776
  const updated = await args.db.db().update(juniorArtifacts).set({ deleteRequestedAt: now }).where(
13711
- and3(
13712
- eq3(juniorArtifacts.id, parsed.id),
13713
- eq3(juniorArtifacts.conversationId, args.conversationId)
13777
+ and4(
13778
+ eq4(juniorArtifacts.id, parsed.id),
13779
+ eq4(juniorArtifacts.conversationId, args.conversationId)
13714
13780
  )
13715
13781
  ).returning({ id: juniorArtifacts.id });
13716
13782
  if (updated.length === 0) {
@@ -13727,10 +13793,10 @@ async function readPublicArtifact(args) {
13727
13793
  contentType: juniorArtifacts.contentType,
13728
13794
  storageKey: juniorArtifacts.storageKey
13729
13795
  }).from(juniorArtifacts).where(
13730
- and3(
13731
- eq3(juniorArtifacts.id, parsed.id),
13732
- eq3(juniorArtifacts.public, true),
13733
- isNull(juniorArtifacts.deleteRequestedAt)
13796
+ and4(
13797
+ eq4(juniorArtifacts.id, parsed.id),
13798
+ eq4(juniorArtifacts.public, true),
13799
+ isNull2(juniorArtifacts.deleteRequestedAt)
13734
13800
  )
13735
13801
  );
13736
13802
  if (!row) return null;
@@ -13770,20 +13836,20 @@ function createPublishImageTool(args) {
13770
13836
  },
13771
13837
  approvalMode: "review",
13772
13838
  description: "Publish one sandbox image to a durable public HTTPS URL. Anyone on the internet with the URL can view the image. Use only when an external destination needs a public image URL. Do not use for private file sharing or private conversation storage. Supports PNG, JPEG, GIF, and WebP up to 10 MB.",
13773
- describeProposal: ({ path: path8 }) => `Publish sandbox image ${path8} to a public internet URL.`,
13839
+ describeProposal: ({ path: path9 }) => `Publish sandbox image ${path9} to a public internet URL.`,
13774
13840
  executionMode: "sequential",
13775
13841
  inputSchema: z56.object({
13776
13842
  path: z56.string().min(1).describe("Exact sandbox path of an existing image file.")
13777
13843
  }).strict(),
13778
13844
  outputSchema: outputSchema5,
13779
- async execute({ path: path8 }) {
13845
+ async execute({ path: path9 }) {
13780
13846
  const publicBaseUrl = (args.publicBaseUrl ?? resolveBaseUrl)()?.trim();
13781
13847
  if (!publicBaseUrl) {
13782
13848
  throw new Error(
13783
13849
  "publishImage requires JUNIOR_BASE_URL or a Vercel deployment URL."
13784
13850
  );
13785
13851
  }
13786
- const targetPath = normalizeSandboxPath(path8);
13852
+ const targetPath = normalizeSandboxPath(path9);
13787
13853
  const body = await args.workspace.readFileToBuffer({ path: targetPath });
13788
13854
  if (!body) {
13789
13855
  throw new SandboxFileNotFoundError(targetPath);
@@ -13843,28 +13909,99 @@ function createUnpublishImageTool(args) {
13843
13909
  });
13844
13910
  }
13845
13911
 
13846
- // src/chat/tools/search-conversation-events.ts
13912
+ // src/chat/tools/load-attachment.ts
13913
+ import path8 from "path";
13847
13914
  import { z as z58 } from "zod";
13915
+ var ATTACHMENT_DIR = ".junior/attachments";
13916
+ async function readStream(stream) {
13917
+ const chunks = [];
13918
+ const reader = stream.getReader();
13919
+ for (; ; ) {
13920
+ const { done, value } = await reader.read();
13921
+ if (done) break;
13922
+ chunks.push(value);
13923
+ }
13924
+ return Buffer.concat(chunks);
13925
+ }
13926
+ function createLoadAttachmentTool(args) {
13927
+ return zodTool({
13928
+ description: "Load a stored attachment for this conversation into the sandbox. Use the attachment_id shown in attachment context or Slack read results.",
13929
+ annotations: {
13930
+ destructiveHint: false,
13931
+ idempotentHint: true,
13932
+ openWorldHint: false,
13933
+ readOnlyHint: false
13934
+ },
13935
+ inputSchema: z58.object({
13936
+ attachment_id: z58.string().min(1).describe("Stored attachment ID.")
13937
+ }),
13938
+ outputSchema: juniorToolOutputSchema,
13939
+ execute: async ({ attachment_id }) => {
13940
+ const attachment = await readLiveAttachment({
13941
+ attachmentId: attachment_id,
13942
+ conversationId: args.conversationId,
13943
+ db: args.db
13944
+ });
13945
+ if (!attachment || attachment.storageProvider !== args.storage.provider) {
13946
+ throw new ToolInputError("Attachment not found in this conversation.");
13947
+ }
13948
+ const body = await args.storage.get(attachment.storageKey);
13949
+ if (!body) {
13950
+ throw new ToolInputError("Stored attachment contents are unavailable.");
13951
+ }
13952
+ const data = await readStream(body);
13953
+ if (data.byteLength !== attachment.bytes) {
13954
+ throw new Error(
13955
+ `Attachment ${attachment.id} read ${data.byteLength} bytes, expected ${attachment.bytes}.`
13956
+ );
13957
+ }
13958
+ const attachmentDir = path8.posix.join(ATTACHMENT_DIR, attachment.id);
13959
+ const sandboxPath = path8.posix.join(
13960
+ attachmentDir,
13961
+ path8.posix.basename(attachment.filename)
13962
+ );
13963
+ const mkdir = await args.workspace.runCommand({
13964
+ cmd: "mkdir",
13965
+ args: ["-p", attachmentDir]
13966
+ });
13967
+ if (mkdir.exitCode !== 0) {
13968
+ throw new Error(
13969
+ `Failed to create attachment directory: ${mkdir.stderr}`
13970
+ );
13971
+ }
13972
+ await args.workspace.writeFiles([{ content: data, path: sandboxPath }]);
13973
+ return {
13974
+ path: sandboxPath,
13975
+ filename: attachment.filename,
13976
+ mime_type: attachment.contentType,
13977
+ bytes: attachment.bytes
13978
+ };
13979
+ }
13980
+ });
13981
+ }
13982
+
13983
+ // src/chat/tools/search-conversation-events.ts
13984
+ import { z as z59 } from "zod";
13848
13985
  var DEFAULT_LIMIT3 = 25;
13849
13986
  var MAX_LIMIT2 = 100;
13850
13987
  var MAX_EVENT_DATA_BYTES = 4e3;
13851
13988
  var MAX_EVENTS_JSON_BYTES = 2e4;
13852
13989
  var MAX_IDENTITY_CHARS = 256;
13853
13990
  var textEncoder = new TextEncoder();
13854
- var knownEventTypeSchema = z58.enum(KNOWN_CONVERSATION_EVENT_TYPES);
13855
- var projectedEventSchema = z58.object({
13856
- seq: z58.number().int().nonnegative().describe("Stable event position for pagination."),
13857
- history_version: z58.number().int().nonnegative().describe("Agent history generation after replacement."),
13858
- created_at: z58.iso.datetime().describe("When the event was recorded."),
13859
- idempotency_key: z58.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
13860
- data: z58.record(z58.string(), z58.unknown()).describe("Bounded event data.")
13991
+ var knownEventTypeSchema = z59.enum(KNOWN_CONVERSATION_EVENT_TYPES);
13992
+ var projectedEventSchema = z59.object({
13993
+ seq: z59.number().int().nonnegative().describe("Stable event position for pagination."),
13994
+ history_version: z59.number().int().nonnegative().describe("Agent history generation after replacement."),
13995
+ created_at: z59.iso.datetime().describe("When the event was recorded."),
13996
+ idempotency_key: z59.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
13997
+ data: z59.record(z59.string(), z59.unknown()).describe("Bounded event data.")
13861
13998
  }).strict();
13862
13999
  var searchConversationEventsOutputSchema = juniorToolOutputSchema.extend({
13863
- conversation_id: z58.string().min(1),
13864
- events: z58.array(projectedEventSchema),
13865
- has_older: z58.boolean().describe("Whether matching older events exist."),
13866
- has_newer: z58.boolean().describe("Whether matching newer events exist."),
13867
- omitted_event_count: z58.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
14000
+ conversation_id: z59.string().min(1),
14001
+ events: z59.array(projectedEventSchema),
14002
+ has_older: z59.boolean().describe("Whether matching older events exist."),
14003
+ has_newer: z59.boolean().describe("Whether matching newer events exist."),
14004
+ omitted_event_count: z59.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
13868
14005
  });
13869
14006
  function createSearchConversationEventsTool(context) {
13870
14007
  return zodTool({
@@ -13877,16 +14014,16 @@ function createSearchConversationEventsTool(context) {
13877
14014
  openWorldHint: false,
13878
14015
  readOnlyHint: true
13879
14016
  },
13880
- inputSchema: z58.object({
13881
- conversation_id: z58.string().trim().min(1).nullable().describe(
14017
+ inputSchema: z59.object({
14018
+ conversation_id: z59.string().trim().min(1).nullable().describe(
13882
14019
  "Conversation ID to search. Omit or null for the current conversation."
13883
14020
  ).optional(),
13884
- after_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
13885
- before_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
13886
- limit: z58.number().int().min(1).max(MAX_LIMIT2).nullable().describe(
14021
+ after_seq: z59.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
14022
+ before_seq: z59.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
14023
+ limit: z59.number().int().min(1).max(MAX_LIMIT2).nullable().describe(
13887
14024
  `Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT2}.`
13888
14025
  ).optional(),
13889
- types: z58.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
14026
+ types: z59.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
13890
14027
  }).strict(),
13891
14028
  outputSchema: searchConversationEventsOutputSchema,
13892
14029
  execute: async (input) => {
@@ -14135,7 +14272,7 @@ function stripReplacementHistoryField(value) {
14135
14272
  }
14136
14273
 
14137
14274
  // src/chat/tools/handoff/tool.ts
14138
- import { z as z59 } from "zod";
14275
+ import { z as z60 } from "zod";
14139
14276
  var HANDOFF_TOOL_NAME = "handoff";
14140
14277
  function formatHandoffProfiles(handoff) {
14141
14278
  return handoff.profiles.map(
@@ -14143,7 +14280,7 @@ function formatHandoffProfiles(handoff) {
14143
14280
  ).join("\n");
14144
14281
  }
14145
14282
  function createHandoffTool(handoff) {
14146
- const profileSchema = z59.enum(
14283
+ const profileSchema = z60.enum(
14147
14284
  handoff.profiles.map((profile) => profile.name)
14148
14285
  );
14149
14286
  const handoffResultSchema = juniorToolOutputSchema.extend({
@@ -14168,7 +14305,7 @@ function createHandoffTool(handoff) {
14168
14305
  ${profileList}`
14169
14306
  ].join(" "),
14170
14307
  executionMode: "sequential",
14171
- inputSchema: z59.object({
14308
+ inputSchema: z60.object({
14172
14309
  profile: profileSchema.describe(
14173
14310
  "Exact configured profile name. Choose the profile whose description best fits the remaining task. If none clearly fits better, do not call handoff."
14174
14311
  )
@@ -14191,7 +14328,7 @@ ${profileList}`
14191
14328
  }
14192
14329
 
14193
14330
  // src/chat/tools/web/fetch-tool.ts
14194
- import { z as z60 } from "zod";
14331
+ import { z as z61 } from "zod";
14195
14332
 
14196
14333
  // src/chat/tools/web/constants.ts
14197
14334
  var USER_AGENT = "junior-bot/0.1";
@@ -14640,9 +14777,9 @@ function createWebFetchTool(hooks, options = {}) {
14640
14777
  openWorldHint: true,
14641
14778
  readOnlyHint: true
14642
14779
  },
14643
- inputSchema: z60.object({
14644
- url: z60.string().min(1).describe("HTTP(S) URL to fetch."),
14645
- max_chars: z60.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
14780
+ inputSchema: z61.object({
14781
+ url: z61.string().min(1).describe("HTTP(S) URL to fetch."),
14782
+ max_chars: z61.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
14646
14783
  }),
14647
14784
  outputSchema: juniorToolOutputSchema,
14648
14785
  execute: async ({ url, max_chars }) => {
@@ -14687,7 +14824,7 @@ function createWebFetchTool(hooks, options = {}) {
14687
14824
  }
14688
14825
 
14689
14826
  // src/chat/tools/web/search.ts
14690
- import { z as z61 } from "zod";
14827
+ import { z as z62 } from "zod";
14691
14828
  import { generateText } from "ai";
14692
14829
  import { createGatewayProvider } from "@ai-sdk/gateway";
14693
14830
  var SEARCH_TIMEOUT_MS = 6e4;
@@ -14729,9 +14866,9 @@ function createWebSearchTool(modelId, override) {
14729
14866
  openWorldHint: true,
14730
14867
  readOnlyHint: true
14731
14868
  },
14732
- inputSchema: z61.object({
14733
- query: z61.string().min(1).max(500).describe("Search query."),
14734
- max_results: z61.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
14869
+ inputSchema: z62.object({
14870
+ query: z62.string().min(1).max(500).describe("Search query."),
14871
+ max_results: z62.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
14735
14872
  }),
14736
14873
  outputSchema: juniorToolOutputSchema,
14737
14874
  execute: async ({ query, max_results }) => {
@@ -14773,7 +14910,7 @@ function createWebSearchTool(modelId, override) {
14773
14910
  }
14774
14911
 
14775
14912
  // src/chat/tools/sandbox/write-file.ts
14776
- import { z as z62 } from "zod";
14913
+ import { z as z63 } from "zod";
14777
14914
  function createWriteFileTool() {
14778
14915
  return zodTool({
14779
14916
  annotations: {
@@ -14784,9 +14921,9 @@ function createWriteFileTool() {
14784
14921
  },
14785
14922
  description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or deliberate full-file replacement after validation; use editFile instead for targeted changes to existing files. Do not use for exploratory analysis-only turns.",
14786
14923
  executionMode: "sequential",
14787
- inputSchema: z62.object({
14788
- path: z62.string().min(1).describe("Path to write in the sandbox workspace."),
14789
- content: z62.string().describe("UTF-8 file content to write.")
14924
+ inputSchema: z63.object({
14925
+ path: z63.string().min(1).describe("Path to write in the sandbox workspace."),
14926
+ content: z63.string().describe("UTF-8 file content to write.")
14790
14927
  }),
14791
14928
  outputSchema: juniorToolOutputSchema,
14792
14929
  execute: async () => {
@@ -14798,7 +14935,7 @@ function createWriteFileTool() {
14798
14935
  }
14799
14936
 
14800
14937
  // src/chat/workspaces/tools.ts
14801
- import { z as z63 } from "zod";
14938
+ import { z as z64 } from "zod";
14802
14939
  import {
14803
14940
  resourceEventSubscriptionResultSchema
14804
14941
  } from "@sentry/junior-plugin-api";
@@ -14830,16 +14967,16 @@ async function stopWorkspaceSnapshotWatch(input) {
14830
14967
  });
14831
14968
  }
14832
14969
  }
14833
- var repoSchema = z63.object({
14834
- provider: z63.string(),
14835
- repo: z63.string(),
14836
- checkout_path: z63.string()
14970
+ var repoSchema = z64.object({
14971
+ provider: z64.string(),
14972
+ repo: z64.string(),
14973
+ checkout_path: z64.string()
14837
14974
  });
14838
- var workspaceSchema = z63.object({
14839
- id: z63.string(),
14840
- name: z63.string(),
14841
- setup_script: z63.string(),
14842
- repos: z63.array(repoSchema)
14975
+ var workspaceSchema = z64.object({
14976
+ id: z64.string(),
14977
+ name: z64.string(),
14978
+ setup_script: z64.string(),
14979
+ repos: z64.array(repoSchema)
14843
14980
  });
14844
14981
  function view(workspace) {
14845
14982
  return {
@@ -14872,18 +15009,18 @@ function workspaceProviderNames() {
14872
15009
  }
14873
15010
  function workspaceWriteSchemas(providers) {
14874
15011
  const providerNames = providers.map((provider) => `\`${provider}\``).join(", ");
14875
- const repoInputSchema = z63.object({
14876
- provider: z63.enum(providers).describe(`Repository provider. Supported: ${providerNames}.`),
14877
- repo: z63.string().trim().min(1).describe("Repository in owner/name format.")
15012
+ const repoInputSchema = z64.object({
15013
+ provider: z64.enum(providers).describe(`Repository provider. Supported: ${providerNames}.`),
15014
+ repo: z64.string().trim().min(1).describe("Repository in owner/name format.")
14878
15015
  }).strict();
14879
- const workspaceInputSchema = z63.object({
14880
- name: z63.string().trim().min(1).describe(
15016
+ const workspaceInputSchema = z64.object({
15017
+ name: z64.string().trim().min(1).describe(
14881
15018
  "Workspace name. Use lowercase letters, digits, underscores, or hyphens."
14882
15019
  ),
14883
- setup_script: z63.string().nullable().describe(
15020
+ setup_script: z64.string().nullable().describe(
14884
15021
  "Optional shell script to run after repository checkout whenever this Workspace starts. Omit or use null for no setup script."
14885
15022
  ).optional(),
14886
- repos: z63.array(repoInputSchema).max(32).describe("Complete repository list.")
15023
+ repos: z64.array(repoInputSchema).max(32).describe("Complete repository list.")
14887
15024
  }).strict();
14888
15025
  return { providerNames, workspaceInputSchema };
14889
15026
  }
@@ -14958,7 +15095,7 @@ function createWorkspaceWriteTools(providers) {
14958
15095
  },
14959
15096
  description: `Replace an install-wide Workspace recipe. Repository providers: ${providerNames}. Use the Workspace ID returned by listWorkspaces.`,
14960
15097
  inputSchema: workspaceInputSchema.extend({
14961
- id: z63.string().uuid().describe("Workspace ID from listWorkspaces.")
15098
+ id: z64.string().uuid().describe("Workspace ID from listWorkspaces.")
14962
15099
  }).strict(),
14963
15100
  prepareArguments(args) {
14964
15101
  const input = args;
@@ -14991,11 +15128,11 @@ function createWorkspaceWriteTools(providers) {
14991
15128
  readOnlyHint: false
14992
15129
  },
14993
15130
  description: "Delete one install-wide Workspace recipe. Use the Workspace ID returned by listWorkspaces.",
14994
- inputSchema: z63.object({
14995
- id: z63.string().uuid().describe("Workspace ID from listWorkspaces.")
15131
+ inputSchema: z64.object({
15132
+ id: z64.string().uuid().describe("Workspace ID from listWorkspaces.")
14996
15133
  }).strict(),
14997
15134
  outputSchema: juniorToolOutputSchema.extend({
14998
- deleted: z63.literal(true)
15135
+ deleted: z64.literal(true)
14999
15136
  }),
15000
15137
  async execute({ id }) {
15001
15138
  const deleted = await workspaceWrite(() => deleteWorkspace(id));
@@ -15020,10 +15157,10 @@ function createWorkspaceTools(context) {
15020
15157
  readOnlyHint: true
15021
15158
  },
15022
15159
  description: "List named repository workspaces that can replace the current sandbox.",
15023
- inputSchema: z63.object({}).strict(),
15160
+ inputSchema: z64.object({}).strict(),
15024
15161
  outputSchema: juniorToolOutputSchema.extend({
15025
- active_workspace_id: z63.string().nullable(),
15026
- workspaces: z63.array(workspaceSchema)
15162
+ active_workspace_id: z64.string().nullable(),
15163
+ workspaces: z64.array(workspaceSchema)
15027
15164
  }),
15028
15165
  async execute() {
15029
15166
  return {
@@ -15041,12 +15178,12 @@ function createWorkspaceTools(context) {
15041
15178
  readOnlyHint: false
15042
15179
  },
15043
15180
  description: "Replace the current sandbox with a named repository Workspace. Files in the old sandbox do not carry over. Returns ready after the switch, or building with a temporary subscription when the snapshot is still being prepared. When building, wait for the ready event and call switchWorkspace again with the same name.",
15044
- inputSchema: z63.object({
15045
- name: z63.string().trim().min(1).describe("Exact workspace name.")
15181
+ inputSchema: z64.object({
15182
+ name: z64.string().trim().min(1).describe("Exact workspace name.")
15046
15183
  }).strict(),
15047
15184
  outputSchema: juniorToolOutputSchema.extend({
15048
15185
  workspace: workspaceSchema,
15049
- status: z63.enum(["ready", "building"]),
15186
+ status: z64.enum(["ready", "building"]),
15050
15187
  subscription: resourceEventSubscriptionResultSchema.optional().describe(
15051
15188
  "Present when the snapshot ready and failed events are already watched for this conversation."
15052
15189
  )
@@ -15178,6 +15315,12 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
15178
15315
  );
15179
15316
  }
15180
15317
  if (context.attachmentStorage) {
15318
+ tools.loadAttachment = createLoadAttachmentTool({
15319
+ conversationId: context.conversationId,
15320
+ db: getSqlExecutor(),
15321
+ storage: context.attachmentStorage,
15322
+ workspace: context.workspace
15323
+ });
15181
15324
  tools.publishImage = createPublishImageTool({
15182
15325
  conversationId: context.conversationId,
15183
15326
  db: getSqlExecutor(),
@@ -15203,7 +15346,10 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
15203
15346
  tools.slackCanvasRead = createSlackCanvasReadTool();
15204
15347
  tools.slackCanvasEdit = createSlackCanvasEditTool(state);
15205
15348
  tools.slackCanvasWrite = createSlackCanvasWriteTool(state);
15206
- tools.slackThreadRead = createSlackThreadReadTool(slackContext);
15349
+ tools.slackThreadRead = createSlackThreadReadTool(slackContext, {
15350
+ conversationId: context.conversationId,
15351
+ db: getSqlExecutor()
15352
+ });
15207
15353
  tools.slackChannelJoin = createSlackChannelJoinTool(slackContext);
15208
15354
  if (context.conversationPrivacy === "public") {
15209
15355
  tools.searchConversationMessages = createSlackConversationMessageSearchTool(
@@ -15832,7 +15978,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
15832
15978
  }
15833
15979
 
15834
15980
  // src/chat/tools/execute-tool.ts
15835
- import { z as z64 } from "zod";
15981
+ import { z as z65 } from "zod";
15836
15982
  var EXECUTE_TOOL_NAME = "executeTool";
15837
15983
  function createExecuteToolTool() {
15838
15984
  return zodTool({
@@ -15844,11 +15990,11 @@ function createExecuteToolTool() {
15844
15990
  },
15845
15991
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
15846
15992
  executionMode: "sequential",
15847
- inputSchema: z64.object({
15848
- tool_name: z64.string().min(1).describe(
15993
+ inputSchema: z65.object({
15994
+ tool_name: z65.string().min(1).describe(
15849
15995
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
15850
15996
  ),
15851
- arguments: z64.record(z64.string(), z64.unknown()).describe(
15997
+ arguments: z65.record(z65.string(), z65.unknown()).describe(
15852
15998
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
15853
15999
  ).optional()
15854
16000
  }).strict(),
@@ -15888,7 +16034,7 @@ function planToolExposure(tools) {
15888
16034
  }
15889
16035
 
15890
16036
  // src/chat/tools/search-tools.ts
15891
- import { z as z65 } from "zod";
16037
+ import { z as z66 } from "zod";
15892
16038
  var SEARCH_TOOLS_NAME = "searchTools";
15893
16039
  var DEFAULT_MAX_RESULTS2 = 5;
15894
16040
  var MAX_RESULTS3 = 20;
@@ -15933,29 +16079,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
15933
16079
  "you",
15934
16080
  "your"
15935
16081
  ]);
15936
- var searchToolsSourceSchema = z65.object({
15937
- id: z65.string(),
15938
- description: z65.string()
16082
+ var searchToolsSourceSchema = z66.object({
16083
+ id: z66.string(),
16084
+ description: z66.string()
15939
16085
  }).strict();
15940
- var searchToolsToolSchema = z65.object({
15941
- tool_name: z65.string(),
15942
- description: z65.string(),
15943
- exposure: z65.enum(["direct", "deferred", "modelOnly", "hidden"]),
15944
- source: z65.string().optional(),
15945
- input_schema: z65.unknown(),
15946
- call_notes: z65.array(z65.string()),
15947
- annotations: z65.record(z65.string(), z65.unknown())
16086
+ var searchToolsToolSchema = z66.object({
16087
+ tool_name: z66.string(),
16088
+ description: z66.string(),
16089
+ exposure: z66.enum(["direct", "deferred", "modelOnly", "hidden"]),
16090
+ source: z66.string().optional(),
16091
+ input_schema: z66.unknown(),
16092
+ call_notes: z66.array(z66.string()),
16093
+ annotations: z66.record(z66.string(), z66.unknown())
15948
16094
  }).strict();
15949
16095
  var searchToolsOutputSchema = juniorToolOutputSchema.extend({
15950
- query: z65.string().nullable(),
15951
- source: z65.string().nullable(),
15952
- sources: z65.array(searchToolsSourceSchema),
15953
- total_catalog_tools: z65.number().int().nonnegative(),
15954
- total_eligible_tools: z65.number().int().nonnegative(),
15955
- total_matches: z65.number().int().nonnegative(),
15956
- returned_tools: z65.number().int().nonnegative(),
15957
- execution_tool: z65.literal("executeTool"),
15958
- tools: z65.array(searchToolsToolSchema)
16096
+ query: z66.string().nullable(),
16097
+ source: z66.string().nullable(),
16098
+ sources: z66.array(searchToolsSourceSchema),
16099
+ total_catalog_tools: z66.number().int().nonnegative(),
16100
+ total_eligible_tools: z66.number().int().nonnegative(),
16101
+ total_matches: z66.number().int().nonnegative(),
16102
+ returned_tools: z66.number().int().nonnegative(),
16103
+ execution_tool: z66.literal("executeTool"),
16104
+ tools: z66.array(searchToolsToolSchema)
15959
16105
  }).strict();
15960
16106
  function terms(value) {
15961
16107
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -16166,14 +16312,14 @@ function createSearchToolsTool(catalogTools) {
16166
16312
  openWorldHint: false,
16167
16313
  readOnlyHint: true
16168
16314
  },
16169
- inputSchema: z65.object({
16170
- query: z65.string().nullable().describe(
16315
+ inputSchema: z66.object({
16316
+ query: z66.string().nullable().describe(
16171
16317
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
16172
16318
  ).optional(),
16173
- source: z65.string().nullable().describe(
16319
+ source: z66.string().nullable().describe(
16174
16320
  "Optional source id to search within, such as a plugin source returned in sources."
16175
16321
  ).optional(),
16176
- max_results: z65.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
16322
+ max_results: z66.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
16177
16323
  }).strict(),
16178
16324
  outputSchema: searchToolsOutputSchema,
16179
16325
  privateTraceResult: (result) => ({
@@ -17471,7 +17617,7 @@ function createPluginEgress(deps) {
17471
17617
  }
17472
17618
 
17473
17619
  // src/chat/services/guardian-action-review.ts
17474
- import { z as z66 } from "zod";
17620
+ import { z as z67 } from "zod";
17475
17621
 
17476
17622
  // src/chat/services/guardian-action-policy.ts
17477
17623
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -17545,17 +17691,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
17545
17691
  - Keep the reason to one concise, specific sentence grounded in the main risk or authorization fact. For ask, name the exact action, target, and material side effects the user must confirm.`;
17546
17692
 
17547
17693
  // src/chat/services/guardian-action-review.ts
17548
- var guardianDecisionSchema = z66.object({
17549
- decision: z66.enum(["allow", "ask", "deny"]),
17550
- reason: z66.string().trim().min(1).max(500),
17551
- riskLevel: z66.enum(["low", "medium", "high", "critical"]),
17552
- userAuthorization: z66.enum(["high", "medium", "low", "unknown"])
17694
+ var guardianDecisionSchema = z67.object({
17695
+ decision: z67.enum(["allow", "ask", "deny"]),
17696
+ reason: z67.string().trim().min(1).max(500),
17697
+ riskLevel: z67.enum(["low", "medium", "high", "critical"]),
17698
+ userAuthorization: z67.enum(["high", "medium", "low", "unknown"])
17553
17699
  }).strict();
17554
17700
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
17555
17701
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
17556
17702
  var MAX_PROPOSAL_CHARS = 192e3;
17557
17703
  function guardianPrompt(proposal) {
17558
- const proposalJson = JSON.stringify(z66.json().parse(proposal), null, 2);
17704
+ const proposalJson = JSON.stringify(z67.json().parse(proposal), null, 2);
17559
17705
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
17560
17706
  throw new Error(
17561
17707
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -20245,6 +20391,7 @@ export {
20245
20391
  setConfigDefaults,
20246
20392
  getConfigDefaults,
20247
20393
  shouldEmitDevAgentTrace,
20394
+ isVisionImageMediaType,
20248
20395
  buildSteeringPiMessage,
20249
20396
  SANDBOX_EGRESS_PROXY_PATH,
20250
20397
  parseSandboxEgressCredentialToken,
@@ -20269,6 +20416,8 @@ export {
20269
20416
  listThreadReplies,
20270
20417
  renderBlockText,
20271
20418
  renderAttachmentText,
20419
+ recordSlackAttachment,
20420
+ readAttachmentVisionSummary,
20272
20421
  readPublicArtifact,
20273
20422
  publicArtifactHeaders,
20274
20423
  unpublishedArtifactHeaders,