@sentry/junior 0.200.0 → 0.201.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-HMG23EIR.js} +11 -11
  2. package/dist/api.js +25 -25
  3. package/dist/app.js +207 -122
  4. package/dist/{catalog-runtime-CIO3ERVQ.js → catalog-runtime-7TIXWSYG.js} +4 -4
  5. package/dist/{chunk-PIHLVU7T.js → chunk-3XPQV262.js} +6 -6
  6. package/dist/{chunk-PMSENMWY.js → chunk-44W7JB5V.js} +1 -1
  7. package/dist/{chunk-LICM66W2.js → chunk-4DDQVL3Q.js} +2 -2
  8. package/dist/{chunk-22Y5SBDM.js → chunk-5LXHEN3P.js} +8 -8
  9. package/dist/{chunk-5JDNDQHL.js → chunk-6CYKW2J7.js} +1 -1
  10. package/dist/{chunk-JLUCKXGI.js → chunk-6QQYL2XW.js} +6 -6
  11. package/dist/{chunk-RDWWCDA4.js → chunk-73RVLY6A.js} +2 -2
  12. package/dist/{chunk-T6263C3N.js → chunk-7V4KWIXL.js} +2 -2
  13. package/dist/{chunk-ADNSL3MX.js → chunk-7YNSWPVU.js} +8 -8
  14. package/dist/{chunk-TF5TXGRD.js → chunk-APX6WBD7.js} +1 -1
  15. package/dist/{chunk-UR43NMUR.js → chunk-BQFWG7MB.js} +4 -4
  16. package/dist/{chunk-4Q5IR5GA.js → chunk-FNAEGMGB.js} +1 -1
  17. package/dist/{chunk-V2VL335Y.js → chunk-GHZ677GD.js} +21 -21
  18. package/dist/{chunk-5Q6KP6LH.js → chunk-H5SDC32F.js} +1 -1
  19. package/dist/{chunk-OIILBD56.js → chunk-I7F3JVYE.js} +3 -3
  20. package/dist/{chunk-ZPLAAHVP.js → chunk-IXGQ4NTO.js} +5 -5
  21. package/dist/{chunk-PSQ4SWI3.js → chunk-J4AWOSCT.js} +4 -4
  22. package/dist/{chunk-N5VHBUXF.js → chunk-JYJG6LAG.js} +19 -6
  23. package/dist/{chunk-OVHO76WN.js → chunk-KKI5K43Z.js} +3 -3
  24. package/dist/{chunk-YVP6QNBZ.js → chunk-KS5RPVQI.js} +3 -3
  25. package/dist/{chunk-RG74Q34N.js → chunk-LINEEYW6.js} +1 -1
  26. package/dist/{chunk-OTTKTTVI.js → chunk-NN5Y32TJ.js} +1 -1
  27. package/dist/{chunk-AV552V2K.js → chunk-OZ2QJWQF.js} +3 -3
  28. package/dist/{chunk-GFXDMDHR.js → chunk-PJIU4AGA.js} +2 -2
  29. package/dist/{chunk-IAZBUDVU.js → chunk-Q3BTNRDK.js} +36 -30
  30. package/dist/{chunk-SY2RQ77O.js → chunk-QHCI2E2A.js} +1 -1
  31. package/dist/{chunk-752M26LC.js → chunk-RAWQD5DS.js} +1 -1
  32. package/dist/{chunk-7DO36XLF.js → chunk-SIMN6AUL.js} +1 -1
  33. package/dist/{chunk-ODSGDVHT.js → chunk-T4WKGKAW.js} +5 -5
  34. package/dist/{chunk-3WUBDFFL.js → chunk-UEIDAG2O.js} +1 -1
  35. package/dist/{chunk-K2QYWZ7F.js → chunk-UJRD4NN4.js} +14 -3
  36. package/dist/{chunk-JMRHEW6B.js → chunk-VGNL3QUM.js} +3 -3
  37. package/dist/{chunk-LUITHZZL.js → chunk-VQ2XYXLB.js} +8 -8
  38. package/dist/{chunk-M7BHRB3G.js → chunk-VRD7FJZ2.js} +4 -4
  39. package/dist/{chunk-X2Q4H6JQ.js → chunk-X2PTNIN5.js} +311 -166
  40. package/dist/{chunk-5VRPXF4T.js → chunk-Y6EUAX5O.js} +1 -1
  41. package/dist/{chunk-VLBLCJXK.js → chunk-ZPGGVW34.js} +5 -5
  42. package/dist/{chunk-EOOWKTQC.js → chunk-ZXSPPXX3.js} +9 -9
  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-S5DGFIFR.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-JDEUY5KP.js} +29 -29
  52. package/dist/{runner-Z3EQSZ23.js → runner-OQZRPML2.js} +21 -21
  53. package/dist/{skills-CGEPG74A.js → skills-4VE2VHGF.js} +5 -5
  54. package/dist/spawn-4WCYSTFY.js +42 -0
  55. package/dist/task-runner-7SDXKQ3L.js +36 -0
  56. package/dist/{validation-YLALMVMB.js → validation-CX2H5J6S.js} +5 -5
  57. package/dist/version.js +1 -1
  58. package/dist/{work-N6PUG4MY.js → work-GUHO34OE.js} +21 -21
  59. package/dist/{worker-M7464FAO.js → worker-F44XXKBH.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 +5 -5
  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-ZPGGVW34.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-5LXHEN3P.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-3XPQV262.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-UEIDAG2O.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-6QQYL2XW.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-Q3BTNRDK.js";
166
167
  import {
167
168
  parseSlackThreadId
168
- } from "./chunk-RG74Q34N.js";
169
+ } from "./chunk-LINEEYW6.js";
169
170
  import {
170
171
  appendAndEnqueueInboundMessage
171
- } from "./chunk-UR43NMUR.js";
172
+ } from "./chunk-BQFWG7MB.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-73RVLY6A.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-I7F3JVYE.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-FNAEGMGB.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-JYJG6LAG.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-VGNL3QUM.js";
258
259
  import {
259
260
  juniorArtifacts,
261
+ juniorAttachments,
260
262
  juniorIdentities,
261
263
  juniorSchedulerRuns
262
- } from "./chunk-K2QYWZ7F.js";
264
+ } from "./chunk-UJRD4NN4.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-KKI5K43Z.js";
294
296
  import {
295
297
  fenceLock,
296
298
  getStateAdapter,
297
299
  withLock
298
- } from "./chunk-PMSENMWY.js";
300
+ } from "./chunk-44W7JB5V.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-PJIU4AGA.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-QHCI2E2A.js";
351
353
  import {
352
354
  discoverSkills,
353
355
  findSkillByName,
354
356
  loadSkillsByName,
355
357
  parseSkillInvocation
356
- } from "./chunk-T6263C3N.js";
358
+ } from "./chunk-7V4KWIXL.js";
357
359
  import {
358
360
  pluginCatalogRuntime
359
- } from "./chunk-5VRPXF4T.js";
361
+ } from "./chunk-Y6EUAX5O.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-H5SDC32F.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-SIMN6AUL.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,95 @@ 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 attachmentDir = path8.posix.join(ATTACHMENT_DIR, attachment.id);
13953
+ const sandboxPath = path8.posix.join(
13954
+ attachmentDir,
13955
+ path8.posix.basename(attachment.filename)
13956
+ );
13957
+ const mkdir = await args.workspace.runCommand({
13958
+ cmd: "mkdir",
13959
+ args: ["-p", attachmentDir]
13960
+ });
13961
+ if (mkdir.exitCode !== 0) {
13962
+ throw new Error(
13963
+ `Failed to create attachment directory: ${mkdir.stderr}`
13964
+ );
13965
+ }
13966
+ await args.workspace.writeFiles([
13967
+ { content: await readStream(body), path: sandboxPath }
13968
+ ]);
13969
+ return {
13970
+ path: sandboxPath,
13971
+ filename: attachment.filename,
13972
+ mime_type: attachment.contentType,
13973
+ bytes: attachment.bytes
13974
+ };
13975
+ }
13976
+ });
13977
+ }
13978
+
13979
+ // src/chat/tools/search-conversation-events.ts
13980
+ import { z as z59 } from "zod";
13848
13981
  var DEFAULT_LIMIT3 = 25;
13849
13982
  var MAX_LIMIT2 = 100;
13850
13983
  var MAX_EVENT_DATA_BYTES = 4e3;
13851
13984
  var MAX_EVENTS_JSON_BYTES = 2e4;
13852
13985
  var MAX_IDENTITY_CHARS = 256;
13853
13986
  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.")
13987
+ var knownEventTypeSchema = z59.enum(KNOWN_CONVERSATION_EVENT_TYPES);
13988
+ var projectedEventSchema = z59.object({
13989
+ seq: z59.number().int().nonnegative().describe("Stable event position for pagination."),
13990
+ history_version: z59.number().int().nonnegative().describe("Agent history generation after replacement."),
13991
+ created_at: z59.iso.datetime().describe("When the event was recorded."),
13992
+ idempotency_key: z59.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
13993
+ data: z59.record(z59.string(), z59.unknown()).describe("Bounded event data.")
13861
13994
  }).strict();
13862
13995
  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.")
13996
+ conversation_id: z59.string().min(1),
13997
+ events: z59.array(projectedEventSchema),
13998
+ has_older: z59.boolean().describe("Whether matching older events exist."),
13999
+ has_newer: z59.boolean().describe("Whether matching newer events exist."),
14000
+ omitted_event_count: z59.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
13868
14001
  });
13869
14002
  function createSearchConversationEventsTool(context) {
13870
14003
  return zodTool({
@@ -13877,16 +14010,16 @@ function createSearchConversationEventsTool(context) {
13877
14010
  openWorldHint: false,
13878
14011
  readOnlyHint: true
13879
14012
  },
13880
- inputSchema: z58.object({
13881
- conversation_id: z58.string().trim().min(1).nullable().describe(
14013
+ inputSchema: z59.object({
14014
+ conversation_id: z59.string().trim().min(1).nullable().describe(
13882
14015
  "Conversation ID to search. Omit or null for the current conversation."
13883
14016
  ).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(
14017
+ after_seq: z59.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
14018
+ before_seq: z59.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
14019
+ limit: z59.number().int().min(1).max(MAX_LIMIT2).nullable().describe(
13887
14020
  `Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT2}.`
13888
14021
  ).optional(),
13889
- types: z58.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
14022
+ types: z59.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
13890
14023
  }).strict(),
13891
14024
  outputSchema: searchConversationEventsOutputSchema,
13892
14025
  execute: async (input) => {
@@ -14135,7 +14268,7 @@ function stripReplacementHistoryField(value) {
14135
14268
  }
14136
14269
 
14137
14270
  // src/chat/tools/handoff/tool.ts
14138
- import { z as z59 } from "zod";
14271
+ import { z as z60 } from "zod";
14139
14272
  var HANDOFF_TOOL_NAME = "handoff";
14140
14273
  function formatHandoffProfiles(handoff) {
14141
14274
  return handoff.profiles.map(
@@ -14143,7 +14276,7 @@ function formatHandoffProfiles(handoff) {
14143
14276
  ).join("\n");
14144
14277
  }
14145
14278
  function createHandoffTool(handoff) {
14146
- const profileSchema = z59.enum(
14279
+ const profileSchema = z60.enum(
14147
14280
  handoff.profiles.map((profile) => profile.name)
14148
14281
  );
14149
14282
  const handoffResultSchema = juniorToolOutputSchema.extend({
@@ -14168,7 +14301,7 @@ function createHandoffTool(handoff) {
14168
14301
  ${profileList}`
14169
14302
  ].join(" "),
14170
14303
  executionMode: "sequential",
14171
- inputSchema: z59.object({
14304
+ inputSchema: z60.object({
14172
14305
  profile: profileSchema.describe(
14173
14306
  "Exact configured profile name. Choose the profile whose description best fits the remaining task. If none clearly fits better, do not call handoff."
14174
14307
  )
@@ -14191,7 +14324,7 @@ ${profileList}`
14191
14324
  }
14192
14325
 
14193
14326
  // src/chat/tools/web/fetch-tool.ts
14194
- import { z as z60 } from "zod";
14327
+ import { z as z61 } from "zod";
14195
14328
 
14196
14329
  // src/chat/tools/web/constants.ts
14197
14330
  var USER_AGENT = "junior-bot/0.1";
@@ -14640,9 +14773,9 @@ function createWebFetchTool(hooks, options = {}) {
14640
14773
  openWorldHint: true,
14641
14774
  readOnlyHint: true
14642
14775
  },
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()
14776
+ inputSchema: z61.object({
14777
+ url: z61.string().min(1).describe("HTTP(S) URL to fetch."),
14778
+ max_chars: z61.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
14646
14779
  }),
14647
14780
  outputSchema: juniorToolOutputSchema,
14648
14781
  execute: async ({ url, max_chars }) => {
@@ -14687,7 +14820,7 @@ function createWebFetchTool(hooks, options = {}) {
14687
14820
  }
14688
14821
 
14689
14822
  // src/chat/tools/web/search.ts
14690
- import { z as z61 } from "zod";
14823
+ import { z as z62 } from "zod";
14691
14824
  import { generateText } from "ai";
14692
14825
  import { createGatewayProvider } from "@ai-sdk/gateway";
14693
14826
  var SEARCH_TIMEOUT_MS = 6e4;
@@ -14729,9 +14862,9 @@ function createWebSearchTool(modelId, override) {
14729
14862
  openWorldHint: true,
14730
14863
  readOnlyHint: true
14731
14864
  },
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()
14865
+ inputSchema: z62.object({
14866
+ query: z62.string().min(1).max(500).describe("Search query."),
14867
+ max_results: z62.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
14735
14868
  }),
14736
14869
  outputSchema: juniorToolOutputSchema,
14737
14870
  execute: async ({ query, max_results }) => {
@@ -14773,7 +14906,7 @@ function createWebSearchTool(modelId, override) {
14773
14906
  }
14774
14907
 
14775
14908
  // src/chat/tools/sandbox/write-file.ts
14776
- import { z as z62 } from "zod";
14909
+ import { z as z63 } from "zod";
14777
14910
  function createWriteFileTool() {
14778
14911
  return zodTool({
14779
14912
  annotations: {
@@ -14784,9 +14917,9 @@ function createWriteFileTool() {
14784
14917
  },
14785
14918
  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
14919
  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.")
14920
+ inputSchema: z63.object({
14921
+ path: z63.string().min(1).describe("Path to write in the sandbox workspace."),
14922
+ content: z63.string().describe("UTF-8 file content to write.")
14790
14923
  }),
14791
14924
  outputSchema: juniorToolOutputSchema,
14792
14925
  execute: async () => {
@@ -14798,7 +14931,7 @@ function createWriteFileTool() {
14798
14931
  }
14799
14932
 
14800
14933
  // src/chat/workspaces/tools.ts
14801
- import { z as z63 } from "zod";
14934
+ import { z as z64 } from "zod";
14802
14935
  import {
14803
14936
  resourceEventSubscriptionResultSchema
14804
14937
  } from "@sentry/junior-plugin-api";
@@ -14830,16 +14963,16 @@ async function stopWorkspaceSnapshotWatch(input) {
14830
14963
  });
14831
14964
  }
14832
14965
  }
14833
- var repoSchema = z63.object({
14834
- provider: z63.string(),
14835
- repo: z63.string(),
14836
- checkout_path: z63.string()
14966
+ var repoSchema = z64.object({
14967
+ provider: z64.string(),
14968
+ repo: z64.string(),
14969
+ checkout_path: z64.string()
14837
14970
  });
14838
- var workspaceSchema = z63.object({
14839
- id: z63.string(),
14840
- name: z63.string(),
14841
- setup_script: z63.string(),
14842
- repos: z63.array(repoSchema)
14971
+ var workspaceSchema = z64.object({
14972
+ id: z64.string(),
14973
+ name: z64.string(),
14974
+ setup_script: z64.string(),
14975
+ repos: z64.array(repoSchema)
14843
14976
  });
14844
14977
  function view(workspace) {
14845
14978
  return {
@@ -14872,18 +15005,18 @@ function workspaceProviderNames() {
14872
15005
  }
14873
15006
  function workspaceWriteSchemas(providers) {
14874
15007
  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.")
15008
+ const repoInputSchema = z64.object({
15009
+ provider: z64.enum(providers).describe(`Repository provider. Supported: ${providerNames}.`),
15010
+ repo: z64.string().trim().min(1).describe("Repository in owner/name format.")
14878
15011
  }).strict();
14879
- const workspaceInputSchema = z63.object({
14880
- name: z63.string().trim().min(1).describe(
15012
+ const workspaceInputSchema = z64.object({
15013
+ name: z64.string().trim().min(1).describe(
14881
15014
  "Workspace name. Use lowercase letters, digits, underscores, or hyphens."
14882
15015
  ),
14883
- setup_script: z63.string().nullable().describe(
15016
+ setup_script: z64.string().nullable().describe(
14884
15017
  "Optional shell script to run after repository checkout whenever this Workspace starts. Omit or use null for no setup script."
14885
15018
  ).optional(),
14886
- repos: z63.array(repoInputSchema).max(32).describe("Complete repository list.")
15019
+ repos: z64.array(repoInputSchema).max(32).describe("Complete repository list.")
14887
15020
  }).strict();
14888
15021
  return { providerNames, workspaceInputSchema };
14889
15022
  }
@@ -14958,7 +15091,7 @@ function createWorkspaceWriteTools(providers) {
14958
15091
  },
14959
15092
  description: `Replace an install-wide Workspace recipe. Repository providers: ${providerNames}. Use the Workspace ID returned by listWorkspaces.`,
14960
15093
  inputSchema: workspaceInputSchema.extend({
14961
- id: z63.string().uuid().describe("Workspace ID from listWorkspaces.")
15094
+ id: z64.string().uuid().describe("Workspace ID from listWorkspaces.")
14962
15095
  }).strict(),
14963
15096
  prepareArguments(args) {
14964
15097
  const input = args;
@@ -14991,11 +15124,11 @@ function createWorkspaceWriteTools(providers) {
14991
15124
  readOnlyHint: false
14992
15125
  },
14993
15126
  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.")
15127
+ inputSchema: z64.object({
15128
+ id: z64.string().uuid().describe("Workspace ID from listWorkspaces.")
14996
15129
  }).strict(),
14997
15130
  outputSchema: juniorToolOutputSchema.extend({
14998
- deleted: z63.literal(true)
15131
+ deleted: z64.literal(true)
14999
15132
  }),
15000
15133
  async execute({ id }) {
15001
15134
  const deleted = await workspaceWrite(() => deleteWorkspace(id));
@@ -15020,10 +15153,10 @@ function createWorkspaceTools(context) {
15020
15153
  readOnlyHint: true
15021
15154
  },
15022
15155
  description: "List named repository workspaces that can replace the current sandbox.",
15023
- inputSchema: z63.object({}).strict(),
15156
+ inputSchema: z64.object({}).strict(),
15024
15157
  outputSchema: juniorToolOutputSchema.extend({
15025
- active_workspace_id: z63.string().nullable(),
15026
- workspaces: z63.array(workspaceSchema)
15158
+ active_workspace_id: z64.string().nullable(),
15159
+ workspaces: z64.array(workspaceSchema)
15027
15160
  }),
15028
15161
  async execute() {
15029
15162
  return {
@@ -15041,12 +15174,12 @@ function createWorkspaceTools(context) {
15041
15174
  readOnlyHint: false
15042
15175
  },
15043
15176
  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.")
15177
+ inputSchema: z64.object({
15178
+ name: z64.string().trim().min(1).describe("Exact workspace name.")
15046
15179
  }).strict(),
15047
15180
  outputSchema: juniorToolOutputSchema.extend({
15048
15181
  workspace: workspaceSchema,
15049
- status: z63.enum(["ready", "building"]),
15182
+ status: z64.enum(["ready", "building"]),
15050
15183
  subscription: resourceEventSubscriptionResultSchema.optional().describe(
15051
15184
  "Present when the snapshot ready and failed events are already watched for this conversation."
15052
15185
  )
@@ -15178,6 +15311,12 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
15178
15311
  );
15179
15312
  }
15180
15313
  if (context.attachmentStorage) {
15314
+ tools.loadAttachment = createLoadAttachmentTool({
15315
+ conversationId: context.conversationId,
15316
+ db: getSqlExecutor(),
15317
+ storage: context.attachmentStorage,
15318
+ workspace: context.workspace
15319
+ });
15181
15320
  tools.publishImage = createPublishImageTool({
15182
15321
  conversationId: context.conversationId,
15183
15322
  db: getSqlExecutor(),
@@ -15203,7 +15342,10 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
15203
15342
  tools.slackCanvasRead = createSlackCanvasReadTool();
15204
15343
  tools.slackCanvasEdit = createSlackCanvasEditTool(state);
15205
15344
  tools.slackCanvasWrite = createSlackCanvasWriteTool(state);
15206
- tools.slackThreadRead = createSlackThreadReadTool(slackContext);
15345
+ tools.slackThreadRead = createSlackThreadReadTool(slackContext, {
15346
+ conversationId: context.conversationId,
15347
+ db: getSqlExecutor()
15348
+ });
15207
15349
  tools.slackChannelJoin = createSlackChannelJoinTool(slackContext);
15208
15350
  if (context.conversationPrivacy === "public") {
15209
15351
  tools.searchConversationMessages = createSlackConversationMessageSearchTool(
@@ -15832,7 +15974,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
15832
15974
  }
15833
15975
 
15834
15976
  // src/chat/tools/execute-tool.ts
15835
- import { z as z64 } from "zod";
15977
+ import { z as z65 } from "zod";
15836
15978
  var EXECUTE_TOOL_NAME = "executeTool";
15837
15979
  function createExecuteToolTool() {
15838
15980
  return zodTool({
@@ -15844,11 +15986,11 @@ function createExecuteToolTool() {
15844
15986
  },
15845
15987
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
15846
15988
  executionMode: "sequential",
15847
- inputSchema: z64.object({
15848
- tool_name: z64.string().min(1).describe(
15989
+ inputSchema: z65.object({
15990
+ tool_name: z65.string().min(1).describe(
15849
15991
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
15850
15992
  ),
15851
- arguments: z64.record(z64.string(), z64.unknown()).describe(
15993
+ arguments: z65.record(z65.string(), z65.unknown()).describe(
15852
15994
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
15853
15995
  ).optional()
15854
15996
  }).strict(),
@@ -15888,7 +16030,7 @@ function planToolExposure(tools) {
15888
16030
  }
15889
16031
 
15890
16032
  // src/chat/tools/search-tools.ts
15891
- import { z as z65 } from "zod";
16033
+ import { z as z66 } from "zod";
15892
16034
  var SEARCH_TOOLS_NAME = "searchTools";
15893
16035
  var DEFAULT_MAX_RESULTS2 = 5;
15894
16036
  var MAX_RESULTS3 = 20;
@@ -15933,29 +16075,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
15933
16075
  "you",
15934
16076
  "your"
15935
16077
  ]);
15936
- var searchToolsSourceSchema = z65.object({
15937
- id: z65.string(),
15938
- description: z65.string()
16078
+ var searchToolsSourceSchema = z66.object({
16079
+ id: z66.string(),
16080
+ description: z66.string()
15939
16081
  }).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())
16082
+ var searchToolsToolSchema = z66.object({
16083
+ tool_name: z66.string(),
16084
+ description: z66.string(),
16085
+ exposure: z66.enum(["direct", "deferred", "modelOnly", "hidden"]),
16086
+ source: z66.string().optional(),
16087
+ input_schema: z66.unknown(),
16088
+ call_notes: z66.array(z66.string()),
16089
+ annotations: z66.record(z66.string(), z66.unknown())
15948
16090
  }).strict();
15949
16091
  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)
16092
+ query: z66.string().nullable(),
16093
+ source: z66.string().nullable(),
16094
+ sources: z66.array(searchToolsSourceSchema),
16095
+ total_catalog_tools: z66.number().int().nonnegative(),
16096
+ total_eligible_tools: z66.number().int().nonnegative(),
16097
+ total_matches: z66.number().int().nonnegative(),
16098
+ returned_tools: z66.number().int().nonnegative(),
16099
+ execution_tool: z66.literal("executeTool"),
16100
+ tools: z66.array(searchToolsToolSchema)
15959
16101
  }).strict();
15960
16102
  function terms(value) {
15961
16103
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -16166,14 +16308,14 @@ function createSearchToolsTool(catalogTools) {
16166
16308
  openWorldHint: false,
16167
16309
  readOnlyHint: true
16168
16310
  },
16169
- inputSchema: z65.object({
16170
- query: z65.string().nullable().describe(
16311
+ inputSchema: z66.object({
16312
+ query: z66.string().nullable().describe(
16171
16313
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
16172
16314
  ).optional(),
16173
- source: z65.string().nullable().describe(
16315
+ source: z66.string().nullable().describe(
16174
16316
  "Optional source id to search within, such as a plugin source returned in sources."
16175
16317
  ).optional(),
16176
- max_results: z65.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
16318
+ max_results: z66.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
16177
16319
  }).strict(),
16178
16320
  outputSchema: searchToolsOutputSchema,
16179
16321
  privateTraceResult: (result) => ({
@@ -17471,7 +17613,7 @@ function createPluginEgress(deps) {
17471
17613
  }
17472
17614
 
17473
17615
  // src/chat/services/guardian-action-review.ts
17474
- import { z as z66 } from "zod";
17616
+ import { z as z67 } from "zod";
17475
17617
 
17476
17618
  // src/chat/services/guardian-action-policy.ts
17477
17619
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -17545,17 +17687,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
17545
17687
  - 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
17688
 
17547
17689
  // 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"])
17690
+ var guardianDecisionSchema = z67.object({
17691
+ decision: z67.enum(["allow", "ask", "deny"]),
17692
+ reason: z67.string().trim().min(1).max(500),
17693
+ riskLevel: z67.enum(["low", "medium", "high", "critical"]),
17694
+ userAuthorization: z67.enum(["high", "medium", "low", "unknown"])
17553
17695
  }).strict();
17554
17696
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
17555
17697
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
17556
17698
  var MAX_PROPOSAL_CHARS = 192e3;
17557
17699
  function guardianPrompt(proposal) {
17558
- const proposalJson = JSON.stringify(z66.json().parse(proposal), null, 2);
17700
+ const proposalJson = JSON.stringify(z67.json().parse(proposal), null, 2);
17559
17701
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
17560
17702
  throw new Error(
17561
17703
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -20245,6 +20387,7 @@ export {
20245
20387
  setConfigDefaults,
20246
20388
  getConfigDefaults,
20247
20389
  shouldEmitDevAgentTrace,
20390
+ isVisionImageMediaType,
20248
20391
  buildSteeringPiMessage,
20249
20392
  SANDBOX_EGRESS_PROXY_PATH,
20250
20393
  parseSandboxEgressCredentialToken,
@@ -20269,6 +20412,8 @@ export {
20269
20412
  listThreadReplies,
20270
20413
  renderBlockText,
20271
20414
  renderAttachmentText,
20415
+ recordSlackAttachment,
20416
+ readAttachmentVisionSummary,
20272
20417
  readPublicArtifact,
20273
20418
  publicArtifactHeaders,
20274
20419
  unpublishedArtifactHeaders,