@sentry/junior 0.116.1 → 0.118.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 (93) hide show
  1. package/dist/{agent-hooks-GUAAS3XH.js → agent-hooks-J2FB7XD6.js} +8 -10
  2. package/dist/api/conversations/detail.d.ts +7 -0
  3. package/dist/api/conversations/event-list.d.ts +7 -0
  4. package/dist/api/conversations/events.d.ts +1 -1
  5. package/dist/api/schema/conversation.d.ts +28 -0
  6. package/dist/api/schema/stats.d.ts +22 -0
  7. package/dist/api/schema.d.ts +2 -0
  8. package/dist/api/schema.js +7 -3
  9. package/dist/api/stats.d.ts +16 -0
  10. package/dist/api/user-pages/routes.d.ts +4 -0
  11. package/dist/api.js +192 -18
  12. package/dist/app.js +53 -46
  13. package/dist/{catalog-runtime-WMPC4J4U.js → catalog-runtime-UHHLU53I.js} +5 -5
  14. package/dist/chat/agent/prompt.d.ts +6 -2
  15. package/dist/chat/agent/request.d.ts +2 -1
  16. package/dist/chat/agent/resume.d.ts +2 -0
  17. package/dist/chat/agent/tools.d.ts +1 -0
  18. package/dist/chat/conversations/history.d.ts +22 -1
  19. package/dist/chat/conversations/projection.d.ts +6 -0
  20. package/dist/chat/mcp/client.d.ts +4 -0
  21. package/dist/chat/mcp/errors.d.ts +22 -1
  22. package/dist/chat/oauth-response.d.ts +21 -0
  23. package/dist/chat/plugins/prompt.d.ts +9 -0
  24. package/dist/chat/plugins/types.d.ts +2 -21
  25. package/dist/chat/plugins/user-pages.d.ts +15 -0
  26. package/dist/chat/plugins/viewer-actors.d.ts +6 -0
  27. package/dist/chat/prompt.d.ts +1 -2
  28. package/dist/chat/sandbox/paths.d.ts +2 -0
  29. package/dist/chat/sandbox/sandbox.d.ts +3 -1
  30. package/dist/chat/sandbox/snapshot/install.d.ts +6 -0
  31. package/dist/chat/sandbox/snapshot/profile.d.ts +14 -0
  32. package/dist/chat/sandbox/snapshot/resolve.d.ts +22 -0
  33. package/dist/chat/sandbox/snapshot/span.d.ts +2 -0
  34. package/dist/chat/sandbox/workspace.d.ts +1 -0
  35. package/dist/chat/services/context-compaction-marker.d.ts +1 -0
  36. package/dist/chat/services/turn-session-record.d.ts +2 -0
  37. package/dist/chat/skills.d.ts +1 -2
  38. package/dist/chat/state/turn-session.d.ts +2 -0
  39. package/dist/chat/tools/index.d.ts +4 -1
  40. package/dist/chat/tools/query-conversation-events.d.ts +1 -1
  41. package/dist/chat/tools/sandbox/file-utils.d.ts +11 -0
  42. package/dist/chat/tools/sandbox/find-files.d.ts +2 -1
  43. package/dist/chat/tools/sandbox/grep.d.ts +2 -1
  44. package/dist/chat/tools/sandbox/view-image.d.ts +11 -0
  45. package/dist/chat/tools/types.d.ts +7 -0
  46. package/dist/{chunk-XA3VLFPK.js → chunk-3LGCVRBQ.js} +154 -20
  47. package/dist/{chunk-JPKGW6L6.js → chunk-3MUSSFMP.js} +78 -39
  48. package/dist/{chunk-7CSVG54J.js → chunk-3N3FMCKN.js} +30 -2
  49. package/dist/{chunk-VBMMMVVQ.js → chunk-4LPPH7Y6.js} +405 -446
  50. package/dist/{chunk-2YYPXQNQ.js → chunk-4SOQPUF4.js} +1 -1
  51. package/dist/{chunk-H35BDAUS.js → chunk-6BGUAOYP.js} +8 -8
  52. package/dist/{chunk-GDAXWEIJ.js → chunk-6K2QE2UI.js} +731 -424
  53. package/dist/{chunk-DLLW5ITL.js → chunk-7GADTCNS.js} +30 -9
  54. package/dist/{chunk-TC4ZEM2D.js → chunk-7SM3P4XR.js} +28 -1
  55. package/dist/{chunk-WKU7TY22.js → chunk-FIANEE7Z.js} +3 -1
  56. package/dist/{chunk-RXRQJF7Y.js → chunk-GCGJGSQL.js} +21 -0
  57. package/dist/{chunk-ZYHLNTC2.js → chunk-GT2ZUALJ.js} +584 -25
  58. package/dist/{chunk-KNCELO2M.js → chunk-HN6LFYG3.js} +1 -1
  59. package/dist/{chunk-N3C4XCRE.js → chunk-HWUBBHLK.js} +1 -1
  60. package/dist/{chunk-BFYVIOTS.js → chunk-KC5EE27Y.js} +13 -3
  61. package/dist/chunk-L72GDKSN.js +37 -0
  62. package/dist/{chunk-F3SWGSPG.js → chunk-MLZFK4KP.js} +1 -1
  63. package/dist/{chunk-RYFAVWP3.js → chunk-PJPKHGLG.js} +34 -15
  64. package/dist/{chunk-CY7NXM32.js → chunk-PWRFTDTY.js} +4 -2
  65. package/dist/{chunk-WLT72MRP.js → chunk-QWCF4RAC.js} +1 -1
  66. package/dist/{chunk-CGXLCMWI.js → chunk-S6R23MFB.js} +117 -17
  67. package/dist/{chunk-OY2PQQ4P.js → chunk-UXLYAVYL.js} +1 -1
  68. package/dist/cli/chat.js +23 -24
  69. package/dist/cli/check.js +6 -6
  70. package/dist/cli/main.js +1 -1
  71. package/dist/cli/plugins.js +12 -14
  72. package/dist/cli/{snapshot-warmup.js → snapshot-create.js} +17 -20
  73. package/dist/cli/upgrade.js +7 -7
  74. package/dist/db/schema/stats.d.ts +93 -0
  75. package/dist/db/schema.d.ts +94 -1
  76. package/dist/{db-FH4R5ZQ4.js → db-PMRHFSYL.js} +6 -6
  77. package/dist/handlers/oauth-html.d.ts +4 -2
  78. package/dist/instrumentation.js +2 -2
  79. package/dist/nitro.js +3 -3
  80. package/dist/{oauth-callback-server-C2SSJ3FQ.js → oauth-callback-server-QJUC7S4X.js} +15 -17
  81. package/dist/{plugins-IUJA4VBU.js → plugins-HW4VWW6G.js} +1 -1
  82. package/dist/{runner-NAALFIXM.js → runner-PZMV6JQW.js} +10 -12
  83. package/dist/{skills-UN2JXYYF.js → skills-W4JETYSV.js} +6 -6
  84. package/dist/stats.d.ts +18 -0
  85. package/dist/{validation-EEF4ROHR.js → validation-IJLNHZSU.js} +6 -6
  86. package/dist/version.js +1 -1
  87. package/migrations/0011_sour_golden_guardian.sql +8 -0
  88. package/migrations/meta/0011_snapshot.json +1195 -0
  89. package/migrations/meta/_journal.json +8 -1
  90. package/package.json +5 -5
  91. package/dist/chat/sandbox/runtime-dependency-snapshots.d.ts +0 -20
  92. package/dist/chunk-4ZNGQH7C.js +0 -25
  93. package/dist/chunk-FAEALXEH.js +0 -517
@@ -2,6 +2,8 @@ import {
2
2
  authorizationId
3
3
  } from "./chunk-DIB4GX3G.js";
4
4
  import {
5
+ McpProviderError,
6
+ McpToolError,
5
7
  RetryableDeliveryError,
6
8
  actorFromRouting,
7
9
  addReactionToMessage,
@@ -12,6 +14,9 @@ import {
12
14
  deliverOAuthAuthorization,
13
15
  formatProviderLabel,
14
16
  getMcpAuthSession,
17
+ getMcpAwareErrorType,
18
+ getMcpAwareTelemetryMessage,
19
+ getMcpProviderErrorAttributes,
15
20
  getSlackMessagePermalink,
16
21
  hasEgressCredentialHooks,
17
22
  issuePluginCredential,
@@ -23,9 +28,10 @@ import {
23
28
  slackOutputPolicy,
24
29
  startOAuthFlow,
25
30
  surfaceFromRouting,
31
+ toMcpProviderError,
26
32
  toolInvocationDestination,
27
33
  uploadFilesToConversation
28
- } from "./chunk-CGXLCMWI.js";
34
+ } from "./chunk-S6R23MFB.js";
29
35
  import {
30
36
  CooperativeTurnYieldError,
31
37
  JUNIOR_THREAD_STATE_TTL_MS,
@@ -67,7 +73,7 @@ import {
67
73
  stripRuntimeTurnContext,
68
74
  trimTrailingAssistantMessages,
69
75
  updateConversationStats
70
- } from "./chunk-RYFAVWP3.js";
76
+ } from "./chunk-PJPKHGLG.js";
71
77
  import {
72
78
  parseSandboxEgressAuthRequiredSignal,
73
79
  parseSandboxEgressPermissionDeniedSignal,
@@ -78,20 +84,23 @@ import {
78
84
  bindSandboxFileSystem,
79
85
  buildNonInteractiveShellScript,
80
86
  createSandboxSession,
81
- getRuntimeDependencyProfileHash,
82
87
  getSandboxResources,
83
88
  getVercelSandboxCredentials,
89
+ hash,
84
90
  isAlreadyExistsError,
91
+ isMissingError,
85
92
  isSandboxCommandStreamInterruptedError,
86
93
  isSandboxMissingError,
87
94
  isSandboxUnavailableError,
88
- isSnapshotMissingError,
89
95
  isSnapshottingError,
90
- resolveRuntimeDependencySnapshot,
96
+ resolve,
91
97
  runNonInteractiveCommand,
92
98
  throwSandboxOperationError,
93
99
  wrapSandboxSetupError
94
- } from "./chunk-VBMMMVVQ.js";
100
+ } from "./chunk-4LPPH7Y6.js";
101
+ import {
102
+ incrementStat
103
+ } from "./chunk-L72GDKSN.js";
95
104
  import {
96
105
  addAgentTurnUsage,
97
106
  hasAgentTurnUsage
@@ -100,30 +109,20 @@ import {
100
109
  privateTraceResultAttributes
101
110
  } from "./chunk-UIE3R5XU.js";
102
111
  import {
112
+ SlackActionError,
103
113
  createPluginHookRunner,
114
+ downloadPrivateSlackFile,
115
+ getFilePermalink,
104
116
  getPluginSystemPromptContributions,
105
117
  getPluginTools,
106
118
  getPluginUserPromptContributions,
107
- getSlackToolContext,
108
- resolveChannelCapabilities
109
- } from "./chunk-ZYHLNTC2.js";
110
- import {
111
- SANDBOX_DATA_ROOT,
112
- SANDBOX_SKILLS_ROOT,
113
- SANDBOX_WORKSPACE_ROOT,
114
- getStateAdapter,
115
- sandboxSkillDir,
116
- sandboxSkillFile
117
- } from "./chunk-7CSVG54J.js";
118
- import {
119
- SlackActionError,
120
- downloadPrivateSlackFile,
121
- getFilePermalink,
122
119
  getSlackClient,
120
+ getSlackToolContext,
123
121
  isConversationScopedChannel,
124
122
  normalizeSlackConversationId,
123
+ resolveChannelCapabilities,
125
124
  withSlackRetries
126
- } from "./chunk-FAEALXEH.js";
125
+ } from "./chunk-GT2ZUALJ.js";
127
126
  import {
128
127
  KNOWN_CONVERSATION_EVENT_TYPES,
129
128
  contextProvenance,
@@ -132,11 +131,19 @@ import {
132
131
  getConversationStore,
133
132
  instructionActors,
134
133
  instructionProvenanceFor
135
- } from "./chunk-BFYVIOTS.js";
134
+ } from "./chunk-KC5EE27Y.js";
136
135
  import {
136
+ SANDBOX_DATA_ROOT,
137
+ SANDBOX_SKILLS_ROOT,
138
+ SANDBOX_WORKSPACE_ROOT,
139
+ getStateAdapter,
140
+ sandboxSkillDir,
141
+ sandboxSkillFile,
142
+ sandboxSkillPathResolution,
137
143
  sleep
138
- } from "./chunk-4ZNGQH7C.js";
144
+ } from "./chunk-3N3FMCKN.js";
139
145
  import {
146
+ ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
140
147
  COMPACTION_SUMMARY_PREFIX,
141
148
  DEFAULT_HANDOFF_MODEL_PROFILE,
142
149
  GEN_AI_PROVIDER_NAME,
@@ -165,7 +172,7 @@ import {
165
172
  renderCurrentInstruction,
166
173
  resolveGatewayModel,
167
174
  unwrapCurrentInstruction
168
- } from "./chunk-CY7NXM32.js";
175
+ } from "./chunk-PWRFTDTY.js";
169
176
  import {
170
177
  canExposeConversationPayload,
171
178
  parseSlackThreadId,
@@ -182,15 +189,18 @@ import {
182
189
  findSkillByName,
183
190
  loadSkillsByName,
184
191
  parseSkillInvocation
185
- } from "./chunk-DLLW5ITL.js";
192
+ } from "./chunk-7GADTCNS.js";
186
193
  import {
187
194
  pluginCatalogRuntime
188
- } from "./chunk-OY2PQQ4P.js";
195
+ } from "./chunk-UXLYAVYL.js";
189
196
  import {
190
197
  CredentialUnavailableError,
198
+ OAuthProviderError,
199
+ fetchWithBoundedOAuthErrorBodies,
200
+ getOAuthProviderErrorAttributes,
191
201
  resolveAuthTokenPlaceholder,
192
202
  resolvePluginCommandEnv
193
- } from "./chunk-XA3VLFPK.js";
203
+ } from "./chunk-3LGCVRBQ.js";
194
204
  import {
195
205
  credentialUserSubjectId
196
206
  } from "./chunk-W3U3YQYI.js";
@@ -214,7 +224,7 @@ import {
214
224
  summarizeMessageText,
215
225
  withLogContext,
216
226
  withSpan
217
- } from "./chunk-F3SWGSPG.js";
227
+ } from "./chunk-MLZFK4KP.js";
218
228
  import {
219
229
  parseSlackChannelId,
220
230
  parseSlackMessageTs,
@@ -1658,13 +1668,10 @@ function formatSkillEntry(skill) {
1658
1668
  lines.push(" </skill>");
1659
1669
  return lines;
1660
1670
  }
1661
- function formatAvailableSkillsForPrompt(skills, invocation) {
1671
+ function formatAvailableSkillsForPrompt(skills) {
1662
1672
  const autoSelectable = skills.filter(
1663
1673
  (s) => s.disableModelInvocation !== true
1664
1674
  );
1665
- const invokedExplicitOnly = invocation ? skills.filter(
1666
- (s) => s.disableModelInvocation === true && s.name === invocation.skillName
1667
- ) : [];
1668
1675
  const sections = [];
1669
1676
  if (autoSelectable.length > 0) {
1670
1677
  const available = [
@@ -1677,17 +1684,6 @@ function formatAvailableSkillsForPrompt(skills, invocation) {
1677
1684
  available.push("</available-skills>");
1678
1685
  sections.push(available.join("\n"));
1679
1686
  }
1680
- if (invokedExplicitOnly.length > 0) {
1681
- const userCallable = [
1682
- "<user-callable-skills>",
1683
- "The user's current message explicitly references this skill by name. Load it when relevant to the request."
1684
- ];
1685
- for (const skill of invokedExplicitOnly) {
1686
- userCallable.push(...formatSkillEntry(skill));
1687
- }
1688
- userCallable.push("</user-callable-skills>");
1689
- sections.push(userCallable.join("\n"));
1690
- }
1691
1687
  return sections.length > 0 ? sections.join("\n") : null;
1692
1688
  }
1693
1689
  function formatActiveMcpCatalogsForPrompt(catalogs) {
@@ -1800,7 +1796,8 @@ var TOOL_CALL_STYLE_RULES = [
1800
1796
  "- Keep tool-call explanations separate from final answers; final answers should report results, evidence, or blockers."
1801
1797
  ];
1802
1798
  var SKILL_POLICY_RULES = [
1803
- "- Only load skills listed in `<available-skills>`, `<user-callable-skills>`, or named by `<explicit-skill-trigger>`. Never guess or invent a skill name.",
1799
+ "- A `<skill>` block in the current user turn is already loaded. Follow its instructions directly and do not call `loadSkill` for that skill.",
1800
+ "- Otherwise, only load skills listed in `<available-skills>`. Never guess or invent a skill name.",
1804
1801
  "- Load one skill at a time. After `loadSkill`, follow the instructions returned by that tool result."
1805
1802
  ];
1806
1803
  var EXECUTION_CONTRACT_RULES = [
@@ -1985,14 +1982,6 @@ function buildContextSection(params) {
1985
1982
  ])
1986
1983
  );
1987
1984
  }
1988
- if (params.invocation) {
1989
- blocks.push(
1990
- renderTag("explicit-skill-trigger", [
1991
- "Treat this skill as selected. Load it unless the tool says it is unavailable.",
1992
- `/${escapeXml(params.invocation.skillName)}`
1993
- ])
1994
- );
1995
- }
1996
1985
  const body = blocks.map((block) => block.join("\n")).join("\n\n");
1997
1986
  if (!body) {
1998
1987
  return null;
@@ -2002,8 +1991,7 @@ function buildContextSection(params) {
2002
1991
  function buildCapabilitiesSection(params) {
2003
1992
  const blocks = [];
2004
1993
  const availableSkills = formatAvailableSkillsForPrompt(
2005
- params.availableSkills,
2006
- params.invocation
1994
+ params.availableSkills
2007
1995
  );
2008
1996
  if (availableSkills) {
2009
1997
  blocks.push(availableSkills);
@@ -2084,7 +2072,6 @@ function buildTurnContextPrompt(params) {
2084
2072
  includeSessionContext ? buildCapabilitiesSection({
2085
2073
  availableSkills: params.availableSkills,
2086
2074
  activeMcpCatalogs: params.activeMcpCatalogs ?? [],
2087
- invocation: params.invocation,
2088
2075
  toolGuidance: params.toolGuidance ?? []
2089
2076
  }) : null,
2090
2077
  pluginPromptContributions,
@@ -2092,8 +2079,7 @@ function buildTurnContextPrompt(params) {
2092
2079
  actor: params.actor,
2093
2080
  artifactState: params.artifactState,
2094
2081
  configuration: params.configuration,
2095
- dispatch: params.dispatch,
2096
- invocation: params.invocation
2082
+ dispatch: params.dispatch
2097
2083
  }) : null,
2098
2084
  includeSessionContext ? buildRuntimeSection(params.runtime ?? {}) : null
2099
2085
  ].filter((section) => Boolean(section));
@@ -2136,6 +2122,17 @@ function buildUserTurnText(userInput, conversationContext, actor) {
2136
2122
  currentInstruction
2137
2123
  ].join("\n");
2138
2124
  }
2125
+ function renderExplicitSkillInstructions(skill) {
2126
+ return [
2127
+ "<skill>",
2128
+ `<name>${escapeXml(skill.name)}</name>`,
2129
+ `<path>${escapeXml(sandboxSkillFile(skill.name))}</path>`,
2130
+ `<working_directory>${escapeXml(sandboxSkillDir(skill.name))}</working_directory>`,
2131
+ `<path_resolution>${escapeXml(sandboxSkillPathResolution(skill.name))}</path_resolution>`,
2132
+ skill.body,
2133
+ "</skill>"
2134
+ ].join("\n");
2135
+ }
2139
2136
  function encodeNonImageAttachmentForPrompt(attachment) {
2140
2137
  const base64 = attachment.data.toString("base64");
2141
2138
  const wasTruncated = base64.length > MAX_INLINE_ATTACHMENT_BASE64_CHARS;
@@ -2302,21 +2299,62 @@ function withoutTrailingUncheckpointedUserPrompt(messages, userContentParts) {
2302
2299
  function userPromptContentMatches(storedContent, currentContent) {
2303
2300
  return isDeepStrictEqual(storedContent, currentContent);
2304
2301
  }
2302
+ function isUserContentPart(value) {
2303
+ if (typeof value !== "object" || value === null || !("type" in value)) {
2304
+ return false;
2305
+ }
2306
+ if (value.type === "text") {
2307
+ return "text" in value && typeof value.text === "string";
2308
+ }
2309
+ return value.type === "image" && "data" in value && typeof value.data === "string" && "mimeType" in value && typeof value.mimeType === "string";
2310
+ }
2311
+ function checkpointedPromptContent(args) {
2312
+ if (!args.messages || args.turnStartMessageIndex === void 0 || args.turnStartMessageIndex !== args.messages.length - 1) {
2313
+ return void 0;
2314
+ }
2315
+ const message = args.messages[args.turnStartMessageIndex];
2316
+ if (message?.role !== "user" || !Array.isArray(message.content)) {
2317
+ return void 0;
2318
+ }
2319
+ const durableMessage = stripRuntimeTurnContext([message])[0];
2320
+ if (!userPromptContentMatches(durableMessage?.content, args.userContentParts)) {
2321
+ return void 0;
2322
+ }
2323
+ const content = message.content;
2324
+ if (!content.every(isUserContentPart)) {
2325
+ return void 0;
2326
+ }
2327
+ return content;
2328
+ }
2305
2329
  async function assemblePrompt(args) {
2306
2330
  const source = args.routing.source;
2307
2331
  const hasPromptCheckpoint = args.resumedFromSessionRecord && args.existingTurnStartMessageIndex !== void 0;
2308
2332
  const shouldPromptAgent = !args.resumedFromSessionRecord || !hasPromptCheckpoint;
2333
+ const requestContentParts = [
2334
+ ...args.explicitSkill ? [
2335
+ {
2336
+ type: "text",
2337
+ text: renderExplicitSkillInstructions(args.explicitSkill)
2338
+ }
2339
+ ] : [],
2340
+ ...args.userContentParts
2341
+ ];
2309
2342
  const promptHistoryMessages = shouldPromptAgent ? withoutTrailingUncheckpointedUserPrompt(
2310
2343
  args.priorPiMessages,
2311
- args.userContentParts
2344
+ requestContentParts
2312
2345
  ) : args.existingSessionPiMessages;
2313
- const needsBootstrapContextForPrompt = shouldPromptAgent && !hasRuntimeTurnContext(promptHistoryMessages);
2346
+ const replayedPromptContent = shouldPromptAgent && !args.resumedFromSessionRecord ? checkpointedPromptContent({
2347
+ messages: args.existingSessionPiMessages,
2348
+ turnStartMessageIndex: args.existingTurnStartMessageIndex,
2349
+ userContentParts: requestContentParts
2350
+ }) : void 0;
2351
+ const needsBootstrapContextForPrompt = shouldPromptAgent && !replayedPromptContent && !hasRuntimeTurnContext(promptHistoryMessages);
2314
2352
  const systemPromptContributions = await getPluginSystemPromptContributions(source);
2315
2353
  const pluginSystemPrompt = buildPluginSystemPromptContributions(
2316
2354
  systemPromptContributions
2317
2355
  );
2318
2356
  const baseInstructions = [buildSystemPrompt({ source }), pluginSystemPrompt].filter((section) => Boolean(section)).join("\n\n");
2319
- const pluginUserPromptContributions = !shouldPromptAgent ? [] : await getPluginUserPromptContributions({
2357
+ const pluginUserPromptContributions = !shouldPromptAgent || replayedPromptContent ? [] : await getPluginUserPromptContributions({
2320
2358
  context: args.toolRuntimeContext
2321
2359
  });
2322
2360
  const turnContextPrompt = needsBootstrapContextForPrompt || pluginUserPromptContributions.length > 0 ? buildTurnContextPrompt({
@@ -2334,13 +2372,15 @@ async function assemblePrompt(args) {
2334
2372
  destination: args.routing.destination,
2335
2373
  source
2336
2374
  } : void 0,
2337
- invocation: args.invocation,
2338
2375
  actor: isUserActor(args.currentActor) ? args.currentActor : void 0,
2339
2376
  artifactState: args.artifactState,
2340
2377
  configuration: args.configurationValues
2341
2378
  }) : null;
2342
2379
  const turnContextParts = turnContextPrompt ? [{ type: "text", text: turnContextPrompt }] : [];
2343
- const promptContentParts = [...turnContextParts, ...args.userContentParts];
2380
+ const promptContentParts = replayedPromptContent ?? [
2381
+ ...turnContextParts,
2382
+ ...requestContentParts
2383
+ ];
2344
2384
  const inputMessages = [
2345
2385
  {
2346
2386
  role: "system",
@@ -2371,7 +2411,10 @@ async function assemblePrompt(args) {
2371
2411
  inputMessagesAttribute,
2372
2412
  promptContentParts,
2373
2413
  promptHistoryMessages,
2374
- shouldPromptAgent
2414
+ shouldPromptAgent,
2415
+ turnContexts: pluginUserPromptContributions.flatMap(
2416
+ (contribution) => contribution.context ? [contribution.context] : []
2417
+ )
2375
2418
  };
2376
2419
  }
2377
2420
 
@@ -3028,6 +3071,7 @@ import { Sandbox } from "@vercel/sandbox";
3028
3071
  // src/chat/sandbox/skill-sync.ts
3029
3072
  import fs3 from "fs/promises";
3030
3073
  import path3 from "path";
3074
+ var DIRECTORY_CREATE_CONCURRENCY = 8;
3031
3075
  function toPosixRelative(base, absolute) {
3032
3076
  return path3.relative(base, absolute).split(path3.sep).join("/");
3033
3077
  }
@@ -3102,6 +3146,16 @@ function collectDirectories(filesToWrite, workspaceRoot) {
3102
3146
  (directory) => directory === workspaceRoot || directory.startsWith(`${workspaceRoot}/`)
3103
3147
  ).sort((a, b) => a.length - b.length);
3104
3148
  }
3149
+ function groupDirectoriesByDepth(directories) {
3150
+ const groups = /* @__PURE__ */ new Map();
3151
+ for (const directory of directories) {
3152
+ const depth = directory.split("/").filter(Boolean).length;
3153
+ const group = groups.get(depth) ?? [];
3154
+ group.push(directory);
3155
+ groups.set(depth, group);
3156
+ }
3157
+ return [...groups.entries()].sort(([left], [right]) => left - right).map(([, group]) => group);
3158
+ }
3105
3159
  function resolveHostSkillPath(availableSkills, sandboxPath) {
3106
3160
  const normalizedPath = path3.posix.normalize(sandboxPath.trim());
3107
3161
  for (const skill of availableSkills) {
@@ -3171,13 +3225,21 @@ async function syncSkillsToSandbox(params) {
3171
3225
  },
3172
3226
  async () => {
3173
3227
  try {
3174
- for (const directory of directories) {
3175
- try {
3176
- await params.sandbox.mkDir(directory);
3177
- } catch (error) {
3178
- if (!isAlreadyExistsError(error)) {
3179
- throw error;
3180
- }
3228
+ for (const directoriesAtDepth of groupDirectoriesByDepth(
3229
+ directories
3230
+ )) {
3231
+ for (let offset = 0; offset < directoriesAtDepth.length; offset += DIRECTORY_CREATE_CONCURRENCY) {
3232
+ await Promise.all(
3233
+ directoriesAtDepth.slice(offset, offset + DIRECTORY_CREATE_CONCURRENCY).map(async (directory) => {
3234
+ try {
3235
+ await params.sandbox.mkDir(directory);
3236
+ } catch (error) {
3237
+ if (!isAlreadyExistsError(error)) {
3238
+ throw error;
3239
+ }
3240
+ }
3241
+ })
3242
+ );
3181
3243
  }
3182
3244
  }
3183
3245
  await params.sandbox.writeFiles(filesToWrite);
@@ -3240,7 +3302,7 @@ function createSandboxRuntime(options) {
3240
3302
  let closed = false;
3241
3303
  const timeoutMs = options.timeoutMs ?? 1e3 * 60 * 30;
3242
3304
  const traceContext = options.traceContext ?? {};
3243
- const dependencyProfileHash = getRuntimeDependencyProfileHash(SANDBOX_RUNTIME);
3305
+ const dependencyProfileHash = hash(SANDBOX_RUNTIME);
3244
3306
  const resolveCommandEnv = options.commandEnv ?? (async () => ({}));
3245
3307
  const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
3246
3308
  const invalidateSession = (sessionId) => {
@@ -3402,13 +3464,13 @@ function createSandboxRuntime(options) {
3402
3464
  sandboxName
3403
3465
  );
3404
3466
  } catch (error) {
3405
- if (!isSnapshotMissingError(error)) {
3467
+ if (!isMissingError(error)) {
3406
3468
  throw error;
3407
3469
  }
3408
3470
  setSpanAttributes({
3409
3471
  "app.sandbox.snapshot.rebuild_after_missing": true
3410
3472
  });
3411
- const rebuiltSnapshot = await resolveRuntimeDependencySnapshot({
3473
+ const rebuiltSnapshot = await resolve({
3412
3474
  runtime,
3413
3475
  timeoutMs,
3414
3476
  forceRebuild: true,
@@ -3439,7 +3501,7 @@ function createSandboxRuntime(options) {
3439
3501
  "app.sandbox.runtime": runtime
3440
3502
  },
3441
3503
  async () => {
3442
- const snapshot = await resolveRuntimeDependencySnapshot({
3504
+ const snapshot = await resolve({
3443
3505
  runtime,
3444
3506
  timeoutMs
3445
3507
  });
@@ -3774,6 +3836,7 @@ var RIPGREP_EXCLUDED_GLOBS = [
3774
3836
  "!**/.git/**",
3775
3837
  "!**/node_modules/**"
3776
3838
  ];
3839
+ var DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
3777
3840
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
3778
3841
  function positiveInteger(value) {
3779
3842
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -4361,7 +4424,7 @@ function createEditFileTool() {
4361
4424
  // src/chat/tools/sandbox/find-files.ts
4362
4425
  import path5 from "path";
4363
4426
  import { z as z5 } from "zod";
4364
- var DEFAULT_FIND_LIMIT = 1e3;
4427
+ var DEFAULT_FIND_LIMIT = 100;
4365
4428
  async function findFiles(params) {
4366
4429
  if (!params.pattern.trim()) {
4367
4430
  throw new Error("pattern is required");
@@ -4372,6 +4435,7 @@ async function findFiles(params) {
4372
4435
  return await findFilesWithRipgrep({
4373
4436
  fs: params.fs,
4374
4437
  limit,
4438
+ onTelemetry: params.onTelemetry,
4375
4439
  path: params.path,
4376
4440
  pattern: params.pattern,
4377
4441
  root,
@@ -4440,25 +4504,45 @@ async function findFilesWithRipgrep(params) {
4440
4504
  throw error;
4441
4505
  }
4442
4506
  const location = getRipgrepSearchLocation(params.root, rootIsDirectory);
4443
- const args = [
4507
+ const ripgrepArgs = [
4444
4508
  "--files",
4445
4509
  "--null",
4446
4510
  "--hidden",
4447
- "--sort=path",
4448
4511
  "--glob",
4449
4512
  params.pattern
4450
4513
  ];
4451
4514
  for (const excludedGlob of RIPGREP_EXCLUDED_GLOBS) {
4452
- args.push("--glob", excludedGlob);
4515
+ ripgrepArgs.push("--glob", excludedGlob);
4453
4516
  }
4454
- args.push("--", location.target);
4517
+ ripgrepArgs.push("--", location.target);
4455
4518
  const result = await params.runCommand({
4456
- cmd: "rg",
4457
- args,
4458
- cwd: location.cwd
4519
+ cmd: "bash",
4520
+ args: [
4521
+ "-c",
4522
+ [
4523
+ `rg "$@" | head -z -n ${params.limit + 1}`,
4524
+ 'statuses=("${PIPESTATUS[@]}")',
4525
+ "if (( statuses[1] != 0 )); then",
4526
+ ' exit "${statuses[1]}"',
4527
+ "fi",
4528
+ "if (( statuses[0] == 0 || statuses[0] == 1 || statuses[0] == 141 )); then",
4529
+ " exit 0",
4530
+ "fi",
4531
+ 'exit "${statuses[0]}"'
4532
+ ].join("\n"),
4533
+ "find-files",
4534
+ ...ripgrepArgs
4535
+ ],
4536
+ cwd: location.cwd,
4537
+ timeoutMs: DEFAULT_SEARCH_COMMAND_TIMEOUT_MS
4459
4538
  });
4460
- if (result.exitCode !== 0 && result.exitCode !== 1) {
4539
+ if (result.exitCode !== 0) {
4461
4540
  const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
4541
+ if (/error parsing glob|invalid glob/i.test(detail)) {
4542
+ throw new ToolInputError(`Invalid glob pattern: ${params.pattern}`, {
4543
+ cause: new Error(detail)
4544
+ });
4545
+ }
4462
4546
  throw new Error(`ripgrep file search failed: ${detail}`);
4463
4547
  }
4464
4548
  const allPaths = result.stdout.split("\0").filter(Boolean).map((filePath) => {
@@ -4482,7 +4566,7 @@ async function findFilesWithRipgrep(params) {
4482
4566
  const text = notices.length > 0 ? `${bounded.content}
4483
4567
 
4484
4568
  [${notices.join(" ")}]` : bounded.content;
4485
- return makeStructuredToolResult(
4569
+ const response = makeStructuredToolResult(
4486
4570
  {
4487
4571
  ok: true,
4488
4572
  status: "success",
@@ -4499,6 +4583,19 @@ async function findFilesWithRipgrep(params) {
4499
4583
  },
4500
4584
  { content: [{ type: "text", text }] }
4501
4585
  );
4586
+ params.onTelemetry?.({
4587
+ emittedLineCount: relativePaths.length,
4588
+ limit: params.limit,
4589
+ limitReached,
4590
+ parsedRecordCount: allPaths.length,
4591
+ rawOutputBytes: Buffer.byteLength(result.stdout, "utf8"),
4592
+ resultBytes: response.content.reduce(
4593
+ (total, item) => total + (item.type === "text" ? Buffer.byteLength(item.text, "utf8") : 0),
4594
+ 0
4595
+ ),
4596
+ resultCount: relativePaths.length
4597
+ });
4598
+ return response;
4502
4599
  }
4503
4600
  function createFindFilesTool() {
4504
4601
  return zodTool({
@@ -4511,7 +4608,7 @@ function createFindFilesTool() {
4511
4608
  path: z5.string().min(1).describe(
4512
4609
  "Directory or file path in the sandbox workspace. Defaults to the workspace root."
4513
4610
  ).optional(),
4514
- limit: z5.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
4611
+ limit: z5.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 100.").optional()
4515
4612
  }),
4516
4613
  outputSchema: juniorToolResultSchema,
4517
4614
  execute: async () => {
@@ -4564,6 +4661,7 @@ async function grepFiles(params) {
4564
4661
  ignoreCase: params.ignoreCase,
4565
4662
  limit,
4566
4663
  literal: params.literal,
4664
+ onTelemetry: params.onTelemetry,
4567
4665
  path: params.path,
4568
4666
  pattern: params.pattern,
4569
4667
  root,
@@ -4711,7 +4809,13 @@ async function grepFilesWithRipgrep(params) {
4711
4809
  throw error;
4712
4810
  }
4713
4811
  const location = getRipgrepSearchLocation(params.root, rootIsDirectory);
4714
- const args = ["--json", "--line-number", "--hidden", "--sort=path"];
4812
+ const args = [
4813
+ "--json",
4814
+ "--line-number",
4815
+ "--hidden",
4816
+ "--max-count",
4817
+ String(params.limit + 1)
4818
+ ];
4715
4819
  if (params.ignoreCase) {
4716
4820
  args.push("--ignore-case");
4717
4821
  }
@@ -4731,7 +4835,8 @@ async function grepFilesWithRipgrep(params) {
4731
4835
  const result = await params.runCommand({
4732
4836
  cmd: "rg",
4733
4837
  args,
4734
- cwd: location.cwd
4838
+ cwd: location.cwd,
4839
+ timeoutMs: DEFAULT_SEARCH_COMMAND_TIMEOUT_MS
4735
4840
  });
4736
4841
  if (result.exitCode !== 0 && result.exitCode !== 1) {
4737
4842
  const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
@@ -4745,6 +4850,7 @@ async function grepFilesWithRipgrep(params) {
4745
4850
  const recordsByPath = /* @__PURE__ */ new Map();
4746
4851
  const pathOrder = [];
4747
4852
  let totalMatches = 0;
4853
+ let parsedRecordCount = 0;
4748
4854
  for (const rawLine of normalizeToLf(result.stdout).split("\n")) {
4749
4855
  if (!rawLine) {
4750
4856
  continue;
@@ -4755,6 +4861,7 @@ async function grepFilesWithRipgrep(params) {
4755
4861
  } catch (error) {
4756
4862
  throw new Error("ripgrep returned invalid JSON output", { cause: error });
4757
4863
  }
4864
+ parsedRecordCount += 1;
4758
4865
  if (record.type !== "match" && record.type !== "context") {
4759
4866
  continue;
4760
4867
  }
@@ -4836,7 +4943,7 @@ async function grepFilesWithRipgrep(params) {
4836
4943
  if (bounded.truncated) {
4837
4944
  notices.push(`${MAX_TEXT_CHARS} character output limit reached.`);
4838
4945
  }
4839
- return makeStructuredToolResult({
4946
+ const response = makeStructuredToolResult({
4840
4947
  ok: true,
4841
4948
  status: "success",
4842
4949
  target: params.path ?? ".",
@@ -4855,6 +4962,19 @@ async function grepFilesWithRipgrep(params) {
4855
4962
  ...matchLimitReached ? { match_limit_reached: params.limit } : {},
4856
4963
  ...lineTruncated ? { line_truncated: true } : {}
4857
4964
  });
4965
+ params.onTelemetry?.({
4966
+ emittedLineCount: output.length,
4967
+ limit: params.limit,
4968
+ limitReached: matchLimitReached,
4969
+ parsedRecordCount,
4970
+ rawOutputBytes: Buffer.byteLength(result.stdout, "utf8"),
4971
+ resultBytes: response.content.reduce(
4972
+ (total, item) => total + (item.type === "text" ? Buffer.byteLength(item.text, "utf8") : 0),
4973
+ 0
4974
+ ),
4975
+ resultCount: matchCount
4976
+ });
4977
+ return response;
4858
4978
  }
4859
4979
  function createGrepTool() {
4860
4980
  return zodTool({
@@ -5194,11 +5314,12 @@ function createSandbox(options) {
5194
5314
  onSandboxPrepare: options.prepare,
5195
5315
  onSandboxRefChanged: options.onSandboxRefChanged
5196
5316
  });
5197
- const createToolCallContext = (signal) => {
5317
+ const createToolCallContext = (signal, setToolCallSpanAttributes) => {
5198
5318
  let fileSystemPromise;
5199
5319
  let commandRunnerPromise;
5200
5320
  return {
5201
5321
  signal,
5322
+ setToolCallSpanAttributes,
5202
5323
  fileSystem() {
5203
5324
  signal?.throwIfAborted();
5204
5325
  return fileSystemPromise ??= runtime.tools().then(({ fs: fs5 }) => {
@@ -5219,10 +5340,31 @@ function createSandbox(options) {
5219
5340
  };
5220
5341
  };
5221
5342
  const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
5222
- const withSandboxToolSpan = (name, op, attributes, callback) => withSandboxSpan(name, op, attributes, async () => {
5343
+ const withSandboxToolSpan = (name, op, attributes, callback) => withSandboxSpan(name, op, attributes, async (setSpanAttributes2) => {
5223
5344
  await runtime.refreshNetworkPolicy(getTracePropagationHeaders());
5224
- return await callback();
5345
+ return await callback(setSpanAttributes2);
5225
5346
  });
5347
+ const searchTelemetryAttributes = (telemetry) => ({
5348
+ "app.sandbox.search.raw_output_bytes": telemetry.rawOutputBytes,
5349
+ "app.sandbox.search.parsed_records": telemetry.parsedRecordCount,
5350
+ "app.sandbox.search.result_count": telemetry.resultCount,
5351
+ "app.sandbox.search.emitted_lines": telemetry.emittedLineCount,
5352
+ "app.sandbox.search.result_bytes": telemetry.resultBytes,
5353
+ "app.sandbox.search.limit": telemetry.limit,
5354
+ "app.sandbox.search.limit_reached": telemetry.limitReached
5355
+ });
5356
+ const recordSearchTelemetry = (telemetry, setSearchSpanAttributes, setToolCallSpanAttributes) => {
5357
+ const attributes = searchTelemetryAttributes(telemetry);
5358
+ for (const setAttributes of [
5359
+ setSearchSpanAttributes,
5360
+ setToolCallSpanAttributes
5361
+ ]) {
5362
+ try {
5363
+ setAttributes?.(attributes);
5364
+ } catch {
5365
+ }
5366
+ }
5367
+ };
5226
5368
  const logSandboxBootRequest = (trigger, details = {}) => {
5227
5369
  if (runtime.sandboxRef()) {
5228
5370
  return;
@@ -5467,10 +5609,15 @@ function createSandbox(options) {
5467
5609
  {
5468
5610
  "app.sandbox.pattern.length": pattern.length
5469
5611
  },
5470
- async () => {
5612
+ async (setSearchSpanAttributes) => {
5471
5613
  const response = await grepFiles({
5472
5614
  fs: fileSystem,
5473
5615
  runCommand: await context.commandRunner(),
5616
+ onTelemetry: (telemetry) => recordSearchTelemetry(
5617
+ telemetry,
5618
+ setSearchSpanAttributes,
5619
+ context.setToolCallSpanAttributes
5620
+ ),
5474
5621
  pattern,
5475
5622
  ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
5476
5623
  ...typeof rawInput.glob === "string" ? { glob: rawInput.glob } : {},
@@ -5499,10 +5646,15 @@ function createSandbox(options) {
5499
5646
  {
5500
5647
  "app.sandbox.pattern.length": pattern.length
5501
5648
  },
5502
- async () => {
5649
+ async (setSearchSpanAttributes) => {
5503
5650
  const response = await findFiles({
5504
5651
  fs: fileSystem,
5505
5652
  runCommand: await context.commandRunner(),
5653
+ onTelemetry: (telemetry) => recordSearchTelemetry(
5654
+ telemetry,
5655
+ setSearchSpanAttributes,
5656
+ context.setToolCallSpanAttributes
5657
+ ),
5506
5658
  pattern,
5507
5659
  ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
5508
5660
  ...limit ? { limit } : {}
@@ -5535,7 +5687,10 @@ function createSandbox(options) {
5535
5687
  };
5536
5688
  const execute = async (params) => {
5537
5689
  const rawInput = params.input ?? {};
5538
- const context = createToolCallContext(params.signal);
5690
+ const context = createToolCallContext(
5691
+ params.signal,
5692
+ params.setToolCallSpanAttributes
5693
+ );
5539
5694
  const bashCommand2 = params.toolName === "bash" ? String(rawInput.command ?? "").trim() : void 0;
5540
5695
  if (params.toolName === "bash") {
5541
5696
  if (!bashCommand2) {
@@ -5846,6 +6001,7 @@ function createAgentSandbox(options) {
5846
6001
  }
5847
6002
 
5848
6003
  // src/chat/mcp/client.ts
6004
+ import { AsyncLocalStorage } from "async_hooks";
5849
6005
  import { Client } from "@modelcontextprotocol/sdk/client";
5850
6006
  import {
5851
6007
  UnauthorizedError
@@ -5887,18 +6043,25 @@ var McpAuthorizationRequiredError = class extends Error {
5887
6043
  this.provider = provider;
5888
6044
  }
5889
6045
  };
6046
+ function isMissingSessionError(error) {
6047
+ return error instanceof StreamableHTTPError && (error.code === 404 || /Session not found/i.test(error.message));
6048
+ }
5890
6049
  var PluginMcpClient = class {
5891
6050
  constructor(plugin, options = {}) {
5892
6051
  this.plugin = plugin;
5893
6052
  this.options = options;
6053
+ const url = plugin.manifest.mcp?.url;
6054
+ this.resourceHost = url ? new URL(url).hostname : void 0;
5894
6055
  }
5895
6056
  plugin;
5896
6057
  options;
5897
6058
  client;
5898
6059
  clientPromise;
5899
6060
  lastAttemptedTransportSessionId;
6061
+ providerStatusStore = new AsyncLocalStorage();
5900
6062
  transport;
5901
6063
  listedTools;
6064
+ resourceHost;
5902
6065
  async listTools() {
5903
6066
  if (this.listedTools) {
5904
6067
  return [...this.listedTools];
@@ -5910,7 +6073,8 @@ var PluginMcpClient = class {
5910
6073
  let cursor;
5911
6074
  do {
5912
6075
  const result = await this.wrapAuth(
5913
- client.listTools(cursor ? { cursor } : void 0)
6076
+ () => client.listTools(cursor ? { cursor } : void 0),
6077
+ "list_tools"
5914
6078
  );
5915
6079
  await this.syncTransportSessionId();
5916
6080
  for (const tool of result.tools) {
@@ -5943,10 +6107,11 @@ var PluginMcpClient = class {
5943
6107
  });
5944
6108
  }
5945
6109
  const result = await this.wrapAuth(
5946
- client.callTool({
6110
+ () => client.callTool({
5947
6111
  name,
5948
6112
  arguments: args ?? {}
5949
- })
6113
+ }),
6114
+ "call_tool"
5950
6115
  );
5951
6116
  await this.syncTransportSessionId();
5952
6117
  return result;
@@ -5964,7 +6129,7 @@ var PluginMcpClient = class {
5964
6129
  throw error;
5965
6130
  }
5966
6131
  await this.clearStoredTransportSessionId();
5967
- await this.disposeClient();
6132
+ await this.cleanupClientAfterFailure();
5968
6133
  return await operation();
5969
6134
  }
5970
6135
  }
@@ -6000,7 +6165,12 @@ var PluginMcpClient = class {
6000
6165
  this.lastAttemptedTransportSessionId = sessionId;
6001
6166
  const transport = new StreamableHTTPClientTransport(new URL(mcp.url), {
6002
6167
  ...Object.keys(requestInit).length > 0 ? { requestInit } : {},
6003
- ...this.options.fetch ? { fetch: this.options.fetch } : {},
6168
+ fetch: fetchWithBoundedOAuthErrorBodies(this.options.fetch, (status) => {
6169
+ const store = this.providerStatusStore.getStore();
6170
+ if (store) {
6171
+ store.status = status;
6172
+ }
6173
+ }),
6004
6174
  ...this.options.authProvider ? { authProvider: this.options.authProvider } : {},
6005
6175
  ...sessionId ? { sessionId } : {}
6006
6176
  });
@@ -6009,48 +6179,79 @@ var PluginMcpClient = class {
6009
6179
  });
6010
6180
  this.transport = transport;
6011
6181
  try {
6012
- await this.wrapAuth(client.connect(transport));
6182
+ await this.wrapAuth(() => client.connect(transport), "connect");
6013
6183
  this.client = client;
6014
6184
  await this.syncTransportSessionId();
6015
6185
  return client;
6016
6186
  } catch (error) {
6017
6187
  await this.syncTransportSessionId();
6018
- await this.disposeClient();
6188
+ await this.cleanupClientAfterFailure();
6019
6189
  throw error;
6020
6190
  }
6021
6191
  }
6022
- async wrapAuth(promise) {
6023
- try {
6024
- return await promise;
6025
- } catch (error) {
6026
- if (error instanceof McpAuthorizationRequiredError) {
6027
- throw error;
6028
- }
6029
- if (error instanceof UnauthorizedError) {
6030
- throw new McpAuthorizationRequiredError(
6031
- this.plugin.manifest.name,
6032
- `MCP authorization required for plugin "${this.plugin.manifest.name}"`
6033
- );
6192
+ async wrapAuth(operation, phase) {
6193
+ const store = {};
6194
+ return await this.providerStatusStore.run(store, async () => {
6195
+ try {
6196
+ return await operation();
6197
+ } catch (error) {
6198
+ if (error instanceof McpAuthorizationRequiredError) {
6199
+ throw error;
6200
+ }
6201
+ if (error instanceof UnauthorizedError) {
6202
+ throw new McpAuthorizationRequiredError(
6203
+ this.plugin.manifest.name,
6204
+ `MCP authorization required for plugin "${this.plugin.manifest.name}"`
6205
+ );
6206
+ }
6207
+ throw toMcpProviderError(error, {
6208
+ phase,
6209
+ provider: this.plugin.manifest.name,
6210
+ missingSession: isMissingSessionError(error) || void 0,
6211
+ resourceHost: this.resourceHost,
6212
+ status: store.status
6213
+ });
6034
6214
  }
6035
- throw error;
6036
- }
6215
+ });
6037
6216
  }
6038
6217
  async shouldResetMissingSession(error) {
6039
- if (!(error instanceof StreamableHTTPError && (error.code === 404 || /Session not found/i.test(error.message)))) {
6218
+ if (!(error instanceof McpProviderError && error.missingSession || isMissingSessionError(error))) {
6040
6219
  return false;
6041
6220
  }
6042
6221
  return Boolean(
6043
6222
  this.transport?.sessionId ?? this.lastAttemptedTransportSessionId ?? await this.getStoredTransportSessionId()
6044
6223
  );
6045
6224
  }
6046
- async disposeClient() {
6225
+ resetClientState() {
6047
6226
  const transport = this.transport;
6048
6227
  this.listedTools = void 0;
6049
6228
  this.transport = void 0;
6050
6229
  this.client = void 0;
6051
6230
  this.clientPromise = void 0;
6052
- if (transport) {
6231
+ return transport;
6232
+ }
6233
+ async cleanupClientAfterFailure() {
6234
+ const transport = this.resetClientState();
6235
+ if (!transport) {
6236
+ return;
6237
+ }
6238
+ try {
6053
6239
  await transport.close();
6240
+ } catch {
6241
+ }
6242
+ }
6243
+ async disposeClient() {
6244
+ const transport = this.resetClientState();
6245
+ if (transport) {
6246
+ try {
6247
+ await transport.close();
6248
+ } catch (error) {
6249
+ throw toMcpProviderError(error, {
6250
+ phase: "close",
6251
+ provider: this.plugin.manifest.name,
6252
+ resourceHost: this.resourceHost
6253
+ });
6254
+ }
6054
6255
  }
6055
6256
  }
6056
6257
  async getStoredTransportSessionId() {
@@ -6073,29 +6274,6 @@ var PluginMcpClient = class {
6073
6274
  }
6074
6275
  };
6075
6276
 
6076
- // src/chat/mcp/errors.ts
6077
- var McpToolError = class extends Error {
6078
- constructor(message) {
6079
- super(message);
6080
- this.name = "McpToolError";
6081
- }
6082
- };
6083
- function getMcpAwareErrorType(error, fallback) {
6084
- if (error instanceof McpToolError) {
6085
- return "tool_error";
6086
- }
6087
- return error instanceof Error ? error.name : fallback;
6088
- }
6089
- function getMcpAwareErrorMessage(error) {
6090
- return error instanceof Error ? error.message : String(error);
6091
- }
6092
- function getMcpAwareTelemetryMessage(error, privacy) {
6093
- if (privacy === "private" && error instanceof McpToolError) {
6094
- return "MCP tool call failed";
6095
- }
6096
- return getMcpAwareErrorMessage(error);
6097
- }
6098
-
6099
6277
  // src/chat/mcp/tool-manager.ts
6100
6278
  var MAX_TOOL_RESULT_TOKENS = 1e4;
6101
6279
  var MAX_TOOL_RESULT_BYTES = 32 * 1024;
@@ -6469,6 +6647,7 @@ var McpToolManager = class {
6469
6647
  }
6470
6648
  const errorAttributes = {
6471
6649
  ...baseAttributes,
6650
+ ...getMcpProviderErrorAttributes(error),
6472
6651
  "error.type": getMcpAwareErrorType(error, "mcp_tool_error"),
6473
6652
  "exception.message": getMcpAwareTelemetryMessage(
6474
6653
  error,
@@ -6840,7 +7019,7 @@ async function loadSkillFromHost(availableSkills, skillName) {
6840
7019
  skill_dir: skillDir,
6841
7020
  working_directory: skillDir,
6842
7021
  location: skillFilePath,
6843
- path_resolution: `Resolve relative paths in this skill against ${skillDir}. For bash commands from this skill, cd to ${skillDir} first or use absolute paths.`,
7022
+ path_resolution: sandboxSkillPathResolution(skill.name),
6844
7023
  instructions: loaded.body
6845
7024
  };
6846
7025
  }
@@ -7207,13 +7386,77 @@ function createSearchMcpToolsTool(mcpToolManager) {
7207
7386
  });
7208
7387
  }
7209
7388
 
7210
- // src/chat/tools/runtime/report-progress.ts
7389
+ // src/chat/tools/sandbox/view-image.ts
7211
7390
  import { z as z15 } from "zod";
7391
+ var MAX_VIEW_IMAGE_BYTES = 5 * 1024 * 1024;
7392
+ var viewImageInputSchema = z15.object({
7393
+ path: z15.string().min(1).describe("Path to an image file in the sandbox workspace.")
7394
+ }).strict();
7395
+ function detectImageMimeType(data) {
7396
+ if (data.length >= 8 && data.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
7397
+ return "image/png";
7398
+ }
7399
+ if (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255) {
7400
+ return "image/jpeg";
7401
+ }
7402
+ if (data.length >= 6 && (data.subarray(0, 6).toString("ascii") === "GIF87a" || data.subarray(0, 6).toString("ascii") === "GIF89a")) {
7403
+ return "image/gif";
7404
+ }
7405
+ if (data.length >= 12 && data.subarray(0, 4).toString("ascii") === "RIFF" && data.subarray(8, 12).toString("ascii") === "WEBP") {
7406
+ return "image/webp";
7407
+ }
7408
+ return void 0;
7409
+ }
7410
+ function createViewImageTool(sandbox, supportsImageInput, deps = {}) {
7411
+ return zodTool({
7412
+ 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.",
7413
+ exposure: "direct",
7414
+ inputSchema: viewImageInputSchema,
7415
+ async execute({ path: path9 }) {
7416
+ if (!supportsImageInput()) {
7417
+ throw new ToolInputError(
7418
+ "viewImage is unavailable because the current model does not support image inputs"
7419
+ );
7420
+ }
7421
+ const targetPath = normalizeSandboxPath(path9);
7422
+ const data = deps.readFile ? await deps.readFile(targetPath) : await sandbox.readFileToBuffer({ path: targetPath });
7423
+ if (!data) {
7424
+ throw new SandboxFileNotFoundError(targetPath);
7425
+ }
7426
+ if (data.byteLength === 0) {
7427
+ throw new ToolInputError(`image file is empty: ${targetPath}`);
7428
+ }
7429
+ if (data.byteLength > MAX_VIEW_IMAGE_BYTES) {
7430
+ throw new ToolInputError(
7431
+ `image exceeds ${MAX_VIEW_IMAGE_BYTES} bytes: ${targetPath} (${data.byteLength} bytes)`
7432
+ );
7433
+ }
7434
+ const mimeType = detectImageMimeType(data);
7435
+ if (!mimeType) {
7436
+ throw new ToolInputError(
7437
+ `unsupported image format: ${targetPath}; use PNG, JPEG, GIF, or WebP`
7438
+ );
7439
+ }
7440
+ return {
7441
+ content: [
7442
+ {
7443
+ type: "image",
7444
+ data: data.toString("base64"),
7445
+ mimeType
7446
+ }
7447
+ ]
7448
+ };
7449
+ }
7450
+ });
7451
+ }
7452
+
7453
+ // src/chat/tools/runtime/report-progress.ts
7454
+ import { z as z16 } from "zod";
7212
7455
  function createReportProgressTool() {
7213
7456
  return zodTool({
7214
7457
  description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks'). Skip trivial direct answers, generic filler, and minor substeps.",
7215
- inputSchema: z15.object({
7216
- message: z15.string().min(1).describe("Short user-facing progress message.")
7458
+ inputSchema: z16.object({
7459
+ message: z16.string().min(1).describe("Short user-facing progress message.")
7217
7460
  }),
7218
7461
  outputSchema: juniorToolResultSchema,
7219
7462
  execute: async () => ({
@@ -7224,34 +7467,34 @@ function createReportProgressTool() {
7224
7467
  }
7225
7468
 
7226
7469
  // src/chat/tools/resource-events.ts
7227
- import { z as z17 } from "zod";
7470
+ import { z as z18 } from "zod";
7228
7471
 
7229
7472
  // src/chat/resource-events/store.ts
7230
7473
  import { createHash } from "crypto";
7231
7474
  import { destinationSchema } from "@sentry/junior-plugin-api";
7232
- import { z as z16 } from "zod";
7475
+ import { z as z17 } from "zod";
7233
7476
  var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription";
7234
7477
  var INDEX_LOCK_TTL_MS = 1e4;
7235
7478
  var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
7236
7479
  var SUBSCRIPTION_LOCK_WAIT_MS = 1e4;
7237
7480
  var SUBSCRIPTION_LOCK_RETRY_MS = 25;
7238
7481
  var SUBSCRIPTION_LOCK_HEARTBEAT_MS = 3e3;
7239
- var subscriptionStatusSchema = z16.enum(["active", "cancelled", "completed"]);
7240
- var subscriptionIdIndexSchema = z16.array(z16.string().min(1));
7241
- var subscriptionSchema = z16.object({
7242
- conversationId: z16.string().min(1),
7243
- createdAtMs: z16.number().finite(),
7482
+ var subscriptionStatusSchema = z17.enum(["active", "cancelled", "completed"]);
7483
+ var subscriptionIdIndexSchema = z17.array(z17.string().min(1));
7484
+ var subscriptionSchema = z17.object({
7485
+ conversationId: z17.string().min(1),
7486
+ createdAtMs: z17.number().finite(),
7244
7487
  destination: destinationSchema,
7245
- events: z16.array(z16.string().min(1)).min(1),
7246
- expiresAtMs: z16.number().finite(),
7247
- id: z16.string().min(1),
7248
- intent: z16.string().min(1),
7249
- label: z16.string().min(1),
7250
- provider: z16.string().min(1),
7251
- resourceRef: z16.string().min(1),
7252
- resourceType: z16.string().min(1),
7488
+ events: z17.array(z17.string().min(1)).min(1),
7489
+ expiresAtMs: z17.number().finite(),
7490
+ id: z17.string().min(1),
7491
+ intent: z17.string().min(1),
7492
+ label: z17.string().min(1),
7493
+ provider: z17.string().min(1),
7494
+ resourceRef: z17.string().min(1),
7495
+ resourceType: z17.string().min(1),
7253
7496
  status: subscriptionStatusSchema,
7254
- updatedAtMs: z16.number().finite()
7497
+ updatedAtMs: z17.number().finite()
7255
7498
  }).strict();
7256
7499
  function digest(value) {
7257
7500
  return createHash("sha256").update(value).digest("hex").slice(0, 32);
@@ -7272,8 +7515,8 @@ function indexLockKey(key) {
7272
7515
  return `${key}:lock`;
7273
7516
  }
7274
7517
  async function sleep2(ms) {
7275
- await new Promise((resolve) => {
7276
- const timer = setTimeout(resolve, ms);
7518
+ await new Promise((resolve2) => {
7519
+ const timer = setTimeout(resolve2, ms);
7277
7520
  timer.unref?.();
7278
7521
  });
7279
7522
  }
@@ -7607,18 +7850,18 @@ var RESOURCE_WATCH_TOOL_SOURCE = {
7607
7850
  id: "resource-watches",
7608
7851
  description: "Inspect or stop resource watches for the current conversation."
7609
7852
  };
7610
- var subscribeInputSchema = z17.object({
7611
- resourceRef: z17.string().describe("Opaque resource ref copied from a subscribable tool result."),
7612
- provider: z17.string().describe("Provider that owns the resource ref."),
7613
- resourceType: z17.string().describe("Provider-defined resource type from the subscribable hint."),
7614
- label: z17.string().describe("Human-readable resource label from the subscribable hint."),
7615
- events: z17.array(z17.string()).min(1).describe(
7853
+ var subscribeInputSchema = z18.object({
7854
+ resourceRef: z18.string().describe("Opaque resource ref copied from a subscribable tool result."),
7855
+ provider: z18.string().describe("Provider that owns the resource ref."),
7856
+ resourceType: z18.string().describe("Provider-defined resource type from the subscribable hint."),
7857
+ label: z18.string().describe("Human-readable resource label from the subscribable hint."),
7858
+ events: z18.array(z18.string()).min(1).describe(
7616
7859
  "High-signal event names to deliver to this conversation when they occur."
7617
7860
  ),
7618
- intent: z17.string().describe(
7861
+ intent: z18.string().describe(
7619
7862
  "Concise reason this conversation wants these events, used when an event arrives."
7620
7863
  ),
7621
- ttlMs: z17.coerce.number().describe(
7864
+ ttlMs: z18.coerce.number().describe(
7622
7865
  "How long to keep the subscription active. Defaults to 14 days and is capped at 30 days."
7623
7866
  ).optional()
7624
7867
  });
@@ -7711,7 +7954,7 @@ function createListResourceEventSubscriptionsTool(context) {
7711
7954
  description: "List active resource event subscriptions for the current conversation.",
7712
7955
  exposure: "deferred",
7713
7956
  source: RESOURCE_WATCH_TOOL_SOURCE,
7714
- inputSchema: z17.object({}),
7957
+ inputSchema: z18.object({}),
7715
7958
  outputSchema: juniorToolResultSchema,
7716
7959
  async execute() {
7717
7960
  const conversationId = requireConversationContext(context);
@@ -7744,7 +7987,7 @@ function createStopWatchingResourcesTool(context) {
7744
7987
  description: "Stop every resource watch for the current conversation. Infer the user's intent from context instead of requiring a special command, and call this tool before confirming that watching stopped.",
7745
7988
  exposure: "deferred",
7746
7989
  source: RESOURCE_WATCH_TOOL_SOURCE,
7747
- inputSchema: z17.object({}),
7990
+ inputSchema: z18.object({}),
7748
7991
  outputSchema: juniorToolResultSchema,
7749
7992
  async execute() {
7750
7993
  const conversationId = requireConversationContext(context);
@@ -7851,11 +8094,11 @@ async function listThreadReplies(input) {
7851
8094
  }
7852
8095
 
7853
8096
  // src/chat/slack/timestamp-param.ts
7854
- import { z as z18 } from "zod";
8097
+ import { z as z19 } from "zod";
7855
8098
  function slackTimestampParam(description) {
7856
- return z18.preprocess(
8099
+ return z19.preprocess(
7857
8100
  (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
7858
- z18.string().min(1)
8101
+ z19.string().min(1)
7859
8102
  ).describe(description);
7860
8103
  }
7861
8104
  function optionalSlackTimestampParam(description) {
@@ -7886,10 +8129,10 @@ function parseRequiredSlackTimestampParam(field, value) {
7886
8129
  }
7887
8130
 
7888
8131
  // src/chat/slack/tools/channel-list-messages.ts
7889
- import { z as z19 } from "zod";
7890
- var booleanInput2 = (description) => z19.preprocess(
8132
+ import { z as z20 } from "zod";
8133
+ var booleanInput2 = (description) => z20.preprocess(
7891
8134
  (value) => value === "true" ? true : value === "false" ? false : value,
7892
- z19.boolean()
8135
+ z20.boolean()
7893
8136
  ).describe(description);
7894
8137
  function normalizeRangeTimestamp(field, value, targetChannelId) {
7895
8138
  if (value === void 0) {
@@ -7916,9 +8159,9 @@ function createSlackChannelListMessagesTool(context) {
7916
8159
  return zodTool({
7917
8160
  description: "List channel messages from Slack history in the active channel context. Use when the user asks for recent or historical channel context outside this thread. Do not use for live monitoring or when current thread context already answers the question.",
7918
8161
  annotations: { readOnlyHint: true, destructiveHint: false },
7919
- inputSchema: z19.object({
7920
- limit: z19.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
7921
- cursor: z19.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
8162
+ inputSchema: z20.object({
8163
+ limit: z20.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
8164
+ cursor: z20.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
7922
8165
  oldest: optionalSlackTimestampParam(
7923
8166
  "Optional oldest message timestamp (Slack ts) for range filtering."
7924
8167
  ),
@@ -7928,7 +8171,7 @@ function createSlackChannelListMessagesTool(context) {
7928
8171
  inclusive: booleanInput2(
7929
8172
  "Whether oldest/latest bounds should be inclusive."
7930
8173
  ).optional(),
7931
- max_pages: z19.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
8174
+ max_pages: z20.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
7932
8175
  }),
7933
8176
  outputSchema: juniorToolResultSchema,
7934
8177
  execute: async ({
@@ -8002,20 +8245,20 @@ function createSlackChannelListMessagesTool(context) {
8002
8245
  }
8003
8246
 
8004
8247
  // src/chat/slack/tools/conversation-search.ts
8005
- import { z as z20 } from "zod";
8248
+ import { z as z21 } from "zod";
8006
8249
  var DEFAULT_LIMIT = 5;
8007
8250
  var conversationSearchOutputSchema = juniorToolResultSchema.extend({
8008
- query: z20.string(),
8009
- count: z20.number().int().nonnegative(),
8010
- threads: z20.array(
8011
- z20.object({
8012
- conversation_id: z20.string().min(1),
8013
- thread_ts: z20.string().min(1),
8014
- message_id: z20.string().min(1),
8015
- message_role: z20.enum(["assistant", "user"]),
8016
- message_timestamp: z20.string().datetime(),
8017
- excerpt: z20.string(),
8018
- permalink: z20.string().url().optional()
8251
+ query: z21.string(),
8252
+ count: z21.number().int().nonnegative(),
8253
+ threads: z21.array(
8254
+ z21.object({
8255
+ conversation_id: z21.string().min(1),
8256
+ thread_ts: z21.string().min(1),
8257
+ message_id: z21.string().min(1),
8258
+ message_role: z21.enum(["assistant", "user"]),
8259
+ message_timestamp: z21.string().datetime(),
8260
+ excerpt: z21.string(),
8261
+ permalink: z21.string().url().optional()
8019
8262
  }).strict()
8020
8263
  )
8021
8264
  });
@@ -8028,9 +8271,9 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
8028
8271
  description: "Search retained public Junior conversation threads in the current Slack workspace."
8029
8272
  },
8030
8273
  annotations: { readOnlyHint: true, destructiveHint: false },
8031
- inputSchema: z20.object({
8032
- query: z20.string().trim().min(1).max(200).describe("Words or a short phrase to find in prior conversations."),
8033
- limit: z20.number().int().min(1).max(10).describe("Maximum number of prior conversation threads to return.").nullable().optional()
8274
+ inputSchema: z21.object({
8275
+ query: z21.string().trim().min(1).max(200).describe("Words or a short phrase to find in prior conversations."),
8276
+ limit: z21.number().int().min(1).max(10).describe("Maximum number of prior conversation threads to return.").nullable().optional()
8034
8277
  }),
8035
8278
  outputSchema: conversationSearchOutputSchema,
8036
8279
  execute: async ({ query, limit }) => {
@@ -8077,27 +8320,27 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
8077
8320
  }
8078
8321
 
8079
8322
  // src/chat/slack/tools/public-search.ts
8080
- import { z as z21 } from "zod";
8323
+ import { z as z22 } from "zod";
8081
8324
  var DEFAULT_LIMIT2 = 10;
8082
- var optionalTimestampSchema = z21.preprocess(
8325
+ var optionalTimestampSchema = z22.preprocess(
8083
8326
  (value) => typeof value === "string" && value.trim() === "" ? void 0 : value,
8084
- z21.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
8327
+ z22.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
8085
8328
  );
8086
- var searchMessageSchema = z21.object({
8087
- author_name: z21.string().optional(),
8088
- author_user_id: z21.string().optional(),
8089
- channel_id: z21.string().min(1),
8090
- channel_name: z21.string().optional(),
8091
- message_ts: z21.string().min(1),
8092
- content: z21.string(),
8093
- is_author_bot: z21.boolean().optional(),
8094
- permalink: z21.string().url()
8329
+ var searchMessageSchema = z22.object({
8330
+ author_name: z22.string().optional(),
8331
+ author_user_id: z22.string().optional(),
8332
+ channel_id: z22.string().min(1),
8333
+ channel_name: z22.string().optional(),
8334
+ message_ts: z22.string().min(1),
8335
+ content: z22.string(),
8336
+ is_author_bot: z22.boolean().optional(),
8337
+ permalink: z22.string().url()
8095
8338
  });
8096
8339
  var publicSearchOutputSchema = juniorToolResultSchema.extend({
8097
- query: z21.string(),
8098
- count: z21.number().int().nonnegative(),
8099
- messages: z21.array(searchMessageSchema),
8100
- next_cursor: z21.string().optional()
8340
+ query: z22.string(),
8341
+ count: z22.number().int().nonnegative(),
8342
+ messages: z22.array(searchMessageSchema),
8343
+ next_cursor: z22.string().optional()
8101
8344
  });
8102
8345
  function normalizeMessage(value) {
8103
8346
  const parsed = searchMessageSchema.safeParse(value);
@@ -8119,8 +8362,8 @@ function createSlackPublicSearchTool(actionToken) {
8119
8362
  return zodTool({
8120
8363
  description: "Search public Slack channel messages across the current workspace. Use when the user asks about company activity, announcements, public mentions, or context outside the active channel. Search only when requested or clearly needed, prefer focused keywords and time bounds, and cite returned permalinks. This never searches private channels or DMs.",
8121
8364
  annotations: { readOnlyHint: true, destructiveHint: false },
8122
- inputSchema: z21.object({
8123
- query: z21.string().trim().min(1).max(500).describe(
8365
+ inputSchema: z22.object({
8366
+ query: z22.string().trim().min(1).max(500).describe(
8124
8367
  "A focused Slack search query, including Slack search filters when useful."
8125
8368
  ),
8126
8369
  after: optionalTimestampSchema.describe(
@@ -8129,10 +8372,10 @@ function createSlackPublicSearchTool(actionToken) {
8129
8372
  before: optionalTimestampSchema.describe(
8130
8373
  "Optional Unix timestamp upper bound."
8131
8374
  ),
8132
- cursor: z21.string().min(1).describe("Cursor for the next result page.").optional(),
8133
- limit: z21.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
8134
- sort: z21.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
8135
- sort_dir: z21.enum(["asc", "desc"]).describe("Sort direction.").optional()
8375
+ cursor: z22.string().min(1).describe("Cursor for the next result page.").optional(),
8376
+ limit: z22.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
8377
+ sort: z22.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
8378
+ sort_dir: z22.enum(["asc", "desc"]).describe("Sort direction.").optional()
8136
8379
  }),
8137
8380
  outputSchema: publicSearchOutputSchema,
8138
8381
  execute: async ({
@@ -8198,7 +8441,7 @@ function createSlackPublicSearchTool(actionToken) {
8198
8441
  }
8199
8442
 
8200
8443
  // src/chat/slack/tools/message-add-reaction.ts
8201
- import { z as z22 } from "zod";
8444
+ import { z as z23 } from "zod";
8202
8445
 
8203
8446
  // src/chat/tools/idempotency.ts
8204
8447
  function stableSerialize(value) {
@@ -8222,8 +8465,8 @@ function createOperationKey(toolName, input) {
8222
8465
  function createSlackMessageAddReactionTool(context, state) {
8223
8466
  return zodTool({
8224
8467
  description: "Add an emoji reaction to the current inbound Slack message. Use when the user asks for a reaction on the current message without another target. Provide a Slack emoji alias name (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`), not a unicode emoji glyph. The target message is injected by runtime context; do not use this for arbitrary historical messages.",
8225
- inputSchema: z22.object({
8226
- emoji: z22.string().min(1).max(64).describe(
8468
+ inputSchema: z23.object({
8469
+ emoji: z23.string().min(1).max(64).describe(
8227
8470
  "Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
8228
8471
  )
8229
8472
  }),
@@ -8274,18 +8517,18 @@ function createSlackMessageAddReactionTool(context, state) {
8274
8517
 
8275
8518
  // src/chat/slack/tools/send-files.ts
8276
8519
  import { createHash as createHash2 } from "crypto";
8277
- import { z as z23 } from "zod";
8278
- var sendFilesDataSchema = z23.object({
8279
- channel_id: z23.string().min(1),
8280
- deduplicated: z23.boolean().optional(),
8281
- file_count: z23.number().int().nonnegative(),
8282
- file_ids: z23.array(z23.string().min(1)).optional(),
8283
- thread_ts: z23.string().min(1)
8520
+ import { z as z24 } from "zod";
8521
+ var sendFilesDataSchema = z24.object({
8522
+ channel_id: z24.string().min(1),
8523
+ deduplicated: z24.boolean().optional(),
8524
+ file_count: z24.number().int().nonnegative(),
8525
+ file_ids: z24.array(z24.string().min(1)).optional(),
8526
+ thread_ts: z24.string().min(1)
8284
8527
  });
8285
8528
  var sendFilesResultSchema = juniorToolResultSchema.extend({
8286
- ok: z23.literal(true),
8287
- status: z23.literal("success"),
8288
- target: z23.string().min(1),
8529
+ ok: z24.literal(true),
8530
+ status: z24.literal("success"),
8531
+ target: z24.string().min(1),
8289
8532
  data: sendFilesDataSchema
8290
8533
  });
8291
8534
  function normalizeFiles(files) {
@@ -8310,8 +8553,8 @@ function fileOperationInput(files) {
8310
8553
  function createSendFilesTool(context, state, materializeFile) {
8311
8554
  return zodTool({
8312
8555
  description: "Send one or more sandbox files into the active Slack conversation. Use when the user asks to attach, send, or share files here, in this conversation, or in this thread. Do not use for ordinary assistant text, top-level channel posts, other named channels, inline @mentions, or pinging mentioned users.",
8313
- inputSchema: z23.object({
8314
- files: z23.array(sandboxFileReferenceSchema).min(1).describe(
8556
+ inputSchema: z24.object({
8557
+ files: z24.array(sandboxFileReferenceSchema).min(1).describe(
8315
8558
  "One or more existing sandbox files. Objects returned by imageGenerate or webFetch can be passed unchanged."
8316
8559
  )
8317
8560
  }),
@@ -8615,13 +8858,13 @@ function storedCanvasUrl(state, canvasId) {
8615
8858
  }
8616
8859
 
8617
8860
  // src/chat/slack/tools/canvas/create.ts
8618
- import { z as z24 } from "zod";
8861
+ import { z as z25 } from "zod";
8619
8862
  function createSlackCanvasCreateTool(context, state) {
8620
8863
  return zodTool({
8621
8864
  description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, reply with one or two short sentences plus the canvas link; do not recap the canvas contents. Do not use for short answers that fit cleanly in one normal thread reply.",
8622
- inputSchema: z24.object({
8623
- title: z24.string().min(1).max(160).describe("Canvas title."),
8624
- markdown: z24.string().min(1).describe("Canvas markdown body content.")
8865
+ inputSchema: z25.object({
8866
+ title: z25.string().min(1).max(160).describe("Canvas title."),
8867
+ markdown: z25.string().min(1).describe("Canvas markdown body content.")
8625
8868
  }),
8626
8869
  outputSchema: juniorToolResultSchema,
8627
8870
  execute: async ({ title, markdown }) => {
@@ -8684,35 +8927,35 @@ function createSlackCanvasCreateTool(context, state) {
8684
8927
  }
8685
8928
 
8686
8929
  // src/chat/slack/tools/canvas/edit.ts
8687
- import { z as z25 } from "zod";
8930
+ import { z as z26 } from "zod";
8688
8931
  function prepareCanvasEditArguments(input) {
8689
8932
  return prepareTextReplacementArguments(input);
8690
8933
  }
8691
- var editReplacementSchema2 = z25.object({
8692
- oldText: z25.string().min(1).describe(
8934
+ var editReplacementSchema2 = z26.object({
8935
+ oldText: z26.string().min(1).describe(
8693
8936
  "Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
8694
8937
  ),
8695
- newText: z25.string().describe("Replacement Canvas markdown for this edit.")
8938
+ newText: z26.string().describe("Replacement Canvas markdown for this edit.")
8696
8939
  });
8697
8940
  var slackCanvasEditOutputSchema = juniorToolResultSchema.extend({
8698
- canvas_id: z25.string().optional(),
8699
- title: z25.string().optional(),
8700
- permalink: z25.string().optional(),
8701
- diff: z25.string().optional(),
8702
- first_changed_line: z25.number().int().positive().optional(),
8703
- replacements: z25.number().int().nonnegative().optional(),
8704
- normalized_heading_count: z25.number().int().nonnegative().optional(),
8705
- summary: z25.string().optional(),
8706
- deduplicated: z25.boolean().optional()
8941
+ canvas_id: z26.string().optional(),
8942
+ title: z26.string().optional(),
8943
+ permalink: z26.string().optional(),
8944
+ diff: z26.string().optional(),
8945
+ first_changed_line: z26.number().int().positive().optional(),
8946
+ replacements: z26.number().int().nonnegative().optional(),
8947
+ normalized_heading_count: z26.number().int().nonnegative().optional(),
8948
+ summary: z26.string().optional(),
8949
+ deduplicated: z26.boolean().optional()
8707
8950
  }).strict();
8708
8951
  function createSlackCanvasEditTool(state) {
8709
8952
  return zodTool({
8710
8953
  description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content; prefer this over slackCanvasWrite for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same canvas: use one edits[] call.",
8711
8954
  prepareArguments: prepareCanvasEditArguments,
8712
8955
  executionMode: "sequential",
8713
- inputSchema: z25.object({
8714
- canvas: z25.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
8715
- edits: z25.array(editReplacementSchema2).min(1).describe(
8956
+ inputSchema: z26.object({
8957
+ canvas: z26.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
8958
+ edits: z26.array(editReplacementSchema2).min(1).describe(
8716
8959
  "Exact replacements matched against the current Canvas body, not incrementally."
8717
8960
  )
8718
8961
  }),
@@ -8790,29 +9033,29 @@ function createSlackCanvasEditTool(state) {
8790
9033
  }
8791
9034
 
8792
9035
  // src/chat/slack/tools/canvas/read.ts
8793
- import { z as z26 } from "zod";
9036
+ import { z as z27 } from "zod";
8794
9037
  var slackCanvasReadOutputSchema = juniorToolResultSchema.extend({
8795
- canvas_id: z26.string().optional(),
8796
- title: z26.string().optional(),
8797
- permalink: z26.string().optional(),
8798
- mimetype: z26.string().optional(),
8799
- filetype: z26.string().optional(),
8800
- original_byte_length: z26.number().int().nonnegative().optional(),
8801
- content: z26.string().optional(),
8802
- start_line: z26.number().int().positive().optional(),
8803
- end_line: z26.number().int().nonnegative().optional(),
8804
- total_lines: z26.number().int().nonnegative().optional()
9038
+ canvas_id: z27.string().optional(),
9039
+ title: z27.string().optional(),
9040
+ permalink: z27.string().optional(),
9041
+ mimetype: z27.string().optional(),
9042
+ filetype: z27.string().optional(),
9043
+ original_byte_length: z27.number().int().nonnegative().optional(),
9044
+ content: z27.string().optional(),
9045
+ start_line: z27.number().int().positive().optional(),
9046
+ end_line: z27.number().int().nonnegative().optional(),
9047
+ total_lines: z27.number().int().nonnegative().optional()
8805
9048
  }).strict();
8806
9049
  function createSlackCanvasReadTool() {
8807
9050
  return zodTool({
8808
9051
  description: "Read a bounded line range from a Slack canvas as markdown. Use when you need exact Canvas contents to verify facts or make edits safely. Do not use for generic web pages \u2014 use webFetch for those.",
8809
9052
  annotations: { readOnlyHint: true, destructiveHint: false },
8810
- inputSchema: z26.object({
8811
- canvas: z26.string().min(1).describe(
9053
+ inputSchema: z27.object({
9054
+ canvas: z27.string().min(1).describe(
8812
9055
  "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
8813
9056
  ),
8814
- offset: z26.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
8815
- limit: z26.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
9057
+ offset: z27.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
9058
+ limit: z27.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
8816
9059
  }),
8817
9060
  outputSchema: slackCanvasReadOutputSchema,
8818
9061
  execute: async ({ canvas, offset, limit }) => {
@@ -8869,14 +9112,14 @@ function createSlackCanvasReadTool() {
8869
9112
  }
8870
9113
 
8871
9114
  // src/chat/slack/tools/canvas/write.ts
8872
- import { z as z27 } from "zod";
9115
+ import { z as z28 } from "zod";
8873
9116
  function createSlackCanvasWriteTool(state) {
8874
9117
  return zodTool({
8875
9118
  description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation; use slackCanvasEdit for targeted changes to existing canvas content.",
8876
9119
  executionMode: "sequential",
8877
- inputSchema: z27.object({
8878
- canvas: z27.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
8879
- content: z27.string().describe("UTF-8 markdown content to write.")
9120
+ inputSchema: z28.object({
9121
+ canvas: z28.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
9122
+ content: z28.string().describe("UTF-8 markdown content to write.")
8880
9123
  }),
8881
9124
  outputSchema: juniorToolResultSchema,
8882
9125
  execute: async ({ canvas, content }) => {
@@ -9112,12 +9355,12 @@ async function updateListItem(input) {
9112
9355
  }
9113
9356
 
9114
9357
  // src/chat/slack/id-param.ts
9115
- import { z as z28 } from "zod";
9358
+ import { z as z29 } from "zod";
9116
9359
  function slackChannelIdParam(description) {
9117
- return z28.string().min(1).describe(description);
9360
+ return z29.string().min(1).describe(description);
9118
9361
  }
9119
9362
  function slackUserIdParam(description) {
9120
- return z28.string().min(1).describe(description);
9363
+ return z29.string().min(1).describe(description);
9121
9364
  }
9122
9365
  function parseRequiredSlackChannelIdParam(field, value) {
9123
9366
  const channelId = parseSlackChannelId(value);
@@ -9141,16 +9384,16 @@ function parseRequiredSlackUserIdParam(field, value) {
9141
9384
  }
9142
9385
 
9143
9386
  // src/chat/slack/tools/list/add-items.ts
9144
- import { z as z29 } from "zod";
9387
+ import { z as z30 } from "zod";
9145
9388
  function createSlackListAddItemsTool(state) {
9146
9389
  return zodTool({
9147
9390
  description: "Add tasks to the active Slack list tracked in artifact context. Use when the user wants actionable items recorded in the current thread list. Do not use when no list exists and list creation was not requested.",
9148
- inputSchema: z29.object({
9149
- items: z29.array(z29.string().min(1)).min(1).max(25).describe("List item titles to create."),
9391
+ inputSchema: z30.object({
9392
+ items: z30.array(z30.string().min(1)).min(1).max(25).describe("List item titles to create."),
9150
9393
  assignee_user_id: slackUserIdParam(
9151
9394
  "Optional Slack user ID assigned to all created items."
9152
9395
  ).optional(),
9153
- due_date: z29.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
9396
+ due_date: z30.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
9154
9397
  }),
9155
9398
  outputSchema: juniorToolResultSchema,
9156
9399
  execute: async ({ items, assignee_user_id, due_date }) => {
@@ -9204,12 +9447,12 @@ function createSlackListAddItemsTool(state) {
9204
9447
  }
9205
9448
 
9206
9449
  // src/chat/slack/tools/list/create.ts
9207
- import { z as z30 } from "zod";
9450
+ import { z as z31 } from "zod";
9208
9451
  function createSlackListCreateTool(state) {
9209
9452
  return zodTool({
9210
9453
  description: "Create a Slack todo list for action tracking. Use when the user needs structured tasks with ownership/completion tracking. Do not use for one-off notes without task management needs.",
9211
- inputSchema: z30.object({
9212
- name: z30.string().min(1).max(160).describe("Name for the new Slack list.")
9454
+ inputSchema: z31.object({
9455
+ name: z31.string().min(1).max(160).describe("Name for the new Slack list.")
9213
9456
  }),
9214
9457
  outputSchema: juniorToolResultSchema,
9215
9458
  execute: async ({ name }) => {
@@ -9241,13 +9484,13 @@ function createSlackListCreateTool(state) {
9241
9484
  }
9242
9485
 
9243
9486
  // src/chat/slack/tools/list/get-items.ts
9244
- import { z as z31 } from "zod";
9487
+ import { z as z32 } from "zod";
9245
9488
  function createSlackListGetItemsTool(state) {
9246
9489
  return zodTool({
9247
9490
  description: "Read items from the active Slack list tracked in artifact context. Use when the user asks for task status, open items, or list contents. Do not use when list state is already known from the immediately prior result.",
9248
9491
  annotations: { readOnlyHint: true, destructiveHint: false },
9249
- inputSchema: z31.object({
9250
- limit: z31.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
9492
+ inputSchema: z32.object({
9493
+ limit: z32.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
9251
9494
  }),
9252
9495
  outputSchema: juniorToolResultSchema,
9253
9496
  execute: async ({ limit }) => {
@@ -9272,21 +9515,21 @@ function createSlackListGetItemsTool(state) {
9272
9515
  }
9273
9516
 
9274
9517
  // src/chat/slack/tools/list/update-item.ts
9275
- import { z as z32 } from "zod";
9276
- var booleanInput3 = (description) => z32.preprocess(
9518
+ import { z as z33 } from "zod";
9519
+ var booleanInput3 = (description) => z33.preprocess(
9277
9520
  (value) => value === "true" ? true : value === "false" ? false : value,
9278
- z32.boolean()
9521
+ z33.boolean()
9279
9522
  ).describe(description);
9280
- var updateListItemInputSchema = z32.union([
9281
- z32.object({
9282
- item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
9523
+ var updateListItemInputSchema = z33.union([
9524
+ z33.object({
9525
+ item_id: z33.string().min(1).describe("ID of the Slack list item to update."),
9283
9526
  completed: booleanInput3("Optional completion status update."),
9284
- title: z32.string().min(1).describe("Optional new item title.").optional()
9527
+ title: z33.string().min(1).describe("Optional new item title.").optional()
9285
9528
  }),
9286
- z32.object({
9287
- item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
9529
+ z33.object({
9530
+ item_id: z33.string().min(1).describe("ID of the Slack list item to update."),
9288
9531
  completed: booleanInput3("Optional completion status update.").optional(),
9289
- title: z32.string().min(1).describe("Optional new item title.")
9532
+ title: z33.string().min(1).describe("Optional new item title.")
9290
9533
  })
9291
9534
  ]);
9292
9535
  function createSlackListUpdateItemTool(state) {
@@ -9362,7 +9605,7 @@ async function checkSlackChannelReadAccess(args) {
9362
9605
  }
9363
9606
 
9364
9607
  // src/chat/slack/tools/thread-read.ts
9365
- import { z as z33 } from "zod";
9608
+ import { z as z34 } from "zod";
9366
9609
 
9367
9610
  // src/chat/slack/tools/slack-message-url.ts
9368
9611
  var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
@@ -9535,8 +9778,8 @@ function createSlackThreadReadTool(context, deps = {}) {
9535
9778
  return zodTool({
9536
9779
  description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Only the current conversation and public channels Junior has seen in this workspace are readable.",
9537
9780
  annotations: { readOnlyHint: true, destructiveHint: false },
9538
- inputSchema: z33.object({
9539
- url: z33.string().min(1).describe(
9781
+ inputSchema: z34.object({
9782
+ url: z34.string().min(1).describe(
9540
9783
  "Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
9541
9784
  ).optional(),
9542
9785
  channel_id: slackChannelIdParam(
@@ -9545,8 +9788,8 @@ function createSlackThreadReadTool(context, deps = {}) {
9545
9788
  ts: slackTimestampParam(
9546
9789
  "Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
9547
9790
  ).optional(),
9548
- limit: z33.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
9549
- max_pages: z33.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
9791
+ limit: z34.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
9792
+ max_pages: z34.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
9550
9793
  }),
9551
9794
  outputSchema: juniorToolResultSchema,
9552
9795
  execute: async ({ url, channel_id, ts, limit, max_pages }) => {
@@ -9802,29 +10045,29 @@ async function searchSlackUsers(options) {
9802
10045
  }
9803
10046
 
9804
10047
  // src/chat/slack/tools/user-lookup.ts
9805
- import { z as z34 } from "zod";
9806
- var booleanInput4 = (description) => z34.preprocess(
10048
+ import { z as z35 } from "zod";
10049
+ var booleanInput4 = (description) => z35.preprocess(
9807
10050
  (value) => value === "true" ? true : value === "false" ? false : value,
9808
- z34.boolean()
10051
+ z35.boolean()
9809
10052
  ).describe(description);
9810
10053
  function createSlackUserLookupTool() {
9811
10054
  return zodTool({
9812
10055
  description: "Look up Slack user profiles by user ID, email, or name search. Use when you need to identify a user, resolve cross-platform identity, or look up profile details like title or status. Returns profile fields including custom fields. For user ID lookup, pass a Slack user ID (e.g. U039RR91S). For search, pass a name query.",
9813
10056
  annotations: { readOnlyHint: true, destructiveHint: false },
9814
- inputSchema: z34.object({
10057
+ inputSchema: z35.object({
9815
10058
  user_id: slackUserIdParam(
9816
10059
  "Slack user ID to look up (e.g. U039RR91S). Mutually exclusive with email and query."
9817
10060
  ).optional(),
9818
- email: z34.string().min(3).describe(
10061
+ email: z35.string().min(3).describe(
9819
10062
  "Email address to look up. Mutually exclusive with user_id and query."
9820
10063
  ).optional(),
9821
- query: z34.string().min(2).describe(
10064
+ query: z35.string().min(2).describe(
9822
10065
  "Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
9823
10066
  ).optional(),
9824
- limit: z34.coerce.number().int().min(1).max(20).describe(
10067
+ limit: z35.coerce.number().int().min(1).max(20).describe(
9825
10068
  "Maximum number of results to return for name search. Defaults to 10."
9826
10069
  ).optional(),
9827
- max_pages: z34.coerce.number().int().min(1).max(5).describe(
10070
+ max_pages: z35.coerce.number().int().min(1).max(5).describe(
9828
10071
  "Maximum number of Slack API pages to scan for name search. Defaults to 3."
9829
10072
  ).optional(),
9830
10073
  include_bots: booleanInput4(
@@ -9928,18 +10171,18 @@ function createSlackUserLookupTool() {
9928
10171
  }
9929
10172
 
9930
10173
  // src/chat/tools/system-time.ts
9931
- import { z as z35 } from "zod";
10174
+ import { z as z36 } from "zod";
9932
10175
  var systemTimeOutputSchema = juniorToolResultSchema.extend({
9933
- unix_ms: z35.number(),
9934
- iso_utc: z35.string(),
9935
- iso_local: z35.string(),
9936
- timezone_offset_minutes: z35.number()
10176
+ unix_ms: z36.number(),
10177
+ iso_utc: z36.string(),
10178
+ iso_local: z36.string(),
10179
+ timezone_offset_minutes: z36.number()
9937
10180
  });
9938
10181
  function createSystemTimeTool() {
9939
10182
  return zodTool({
9940
10183
  description: "Return current system time in UTC and local ISO formats. Use when the user asks for current time/date context. Do not use as a substitute for historical or timezone-conversion research.",
9941
10184
  annotations: { readOnlyHint: true, destructiveHint: false },
9942
- inputSchema: z35.object({}),
10185
+ inputSchema: z36.object({}),
9943
10186
  outputSchema: systemTimeOutputSchema,
9944
10187
  privateTraceResult: (result) => ({
9945
10188
  ok: result.ok,
@@ -9968,32 +10211,32 @@ function createSystemTimeTool() {
9968
10211
  }
9969
10212
 
9970
10213
  // src/chat/tools/query-conversation-events.ts
9971
- import { z as z36 } from "zod";
10214
+ import { z as z37 } from "zod";
9972
10215
  var DEFAULT_LIMIT3 = 25;
9973
10216
  var MAX_LIMIT = 100;
9974
10217
  var CONVERSATION_EVENTS_TOOL_SOURCE = {
9975
10218
  id: "conversation-events",
9976
10219
  description: "Inspect Junior's durable conversation event log for debugging runtime behavior."
9977
10220
  };
9978
- var knownEventTypeSchema = z36.enum(KNOWN_CONVERSATION_EVENT_TYPES);
9979
- var projectedEventSchema = z36.object({
9980
- seq: z36.number().int().nonnegative().describe("Stable event position; use it for pagination bounds."),
9981
- history_version: z36.number().int().nonnegative().describe(
10221
+ var knownEventTypeSchema = z37.enum(KNOWN_CONVERSATION_EVENT_TYPES);
10222
+ var projectedEventSchema = z37.object({
10223
+ seq: z37.number().int().nonnegative().describe("Stable event position; use it for pagination bounds."),
10224
+ history_version: z37.number().int().nonnegative().describe(
9982
10225
  "Model-history generation. It increments when handoff or compaction replaces active model context."
9983
10226
  ),
9984
- created_at: z36.iso.datetime().describe("When the event was recorded."),
9985
- idempotency_key: z36.string().min(1).optional().describe("Retry-stable event identity, when one was assigned."),
9986
- data: z36.record(z36.string(), z36.unknown()).describe(
10227
+ created_at: z37.iso.datetime().describe("When the event was recorded."),
10228
+ idempotency_key: z37.string().min(1).optional().describe("Retry-stable event identity, when one was assigned."),
10229
+ data: z37.record(z37.string(), z37.unknown()).describe(
9987
10230
  "Event payload. The type field identifies the event; replacement-history events report replacement_history_count instead of replaying the full model context."
9988
10231
  )
9989
10232
  }).strict();
9990
10233
  var queryConversationEventsOutputSchema = juniorToolResultSchema.extend({
9991
- conversation_id: z36.string().min(1),
9992
- events: z36.array(projectedEventSchema),
9993
- has_older: z36.boolean().describe(
10234
+ conversation_id: z37.string().min(1),
10235
+ events: z37.array(projectedEventSchema),
10236
+ has_older: z37.boolean().describe(
9994
10237
  "Whether matching events exist before this page; request them with before_seq set to the first returned seq."
9995
10238
  ),
9996
- has_newer: z36.boolean().describe(
10239
+ has_newer: z37.boolean().describe(
9997
10240
  "Whether matching events exist after this page; request them with after_seq set to the last returned seq."
9998
10241
  )
9999
10242
  });
@@ -10003,20 +10246,20 @@ function createQueryConversationEventsTool(context) {
10003
10246
  exposure: "deferred",
10004
10247
  source: CONVERSATION_EVENTS_TOOL_SOURCE,
10005
10248
  annotations: { readOnlyHint: true, destructiveHint: false },
10006
- inputSchema: z36.object({
10007
- conversation_id: z36.string().trim().min(1).describe(
10249
+ inputSchema: z37.object({
10250
+ conversation_id: z37.string().trim().min(1).describe(
10008
10251
  "Conversation ID to inspect, such as slack:{channelId}:{threadTs} or a child conversation id."
10009
10252
  ),
10010
- after_seq: z36.number().int().nonnegative().nullable().describe(
10253
+ after_seq: z37.number().int().nonnegative().nullable().describe(
10011
10254
  "Exclusive lower bound on event seq. When set, returns the next newer page in ascending order."
10012
10255
  ).optional(),
10013
- before_seq: z36.number().int().nonnegative().nullable().describe(
10256
+ before_seq: z37.number().int().nonnegative().nullable().describe(
10014
10257
  "Exclusive upper bound on event seq. Combine with defaults or after_seq to page older or bounded ranges."
10015
10258
  ).optional(),
10016
- limit: z36.number().int().min(1).max(MAX_LIMIT).nullable().describe(
10259
+ limit: z37.number().int().min(1).max(MAX_LIMIT).nullable().describe(
10017
10260
  `Maximum events to return. Defaults to ${DEFAULT_LIMIT3}; max ${MAX_LIMIT}.`
10018
10261
  ).optional(),
10019
- types: z36.array(knownEventTypeSchema).min(1).nullable().describe(
10262
+ types: z37.array(knownEventTypeSchema).min(1).nullable().describe(
10020
10263
  "Optional event-type filter. Omit to return every durable event type."
10021
10264
  ).optional()
10022
10265
  }).strict(),
@@ -10206,10 +10449,10 @@ function stripReplacementHistoryField(value) {
10206
10449
  }
10207
10450
 
10208
10451
  // src/chat/tools/handoff/tool.ts
10209
- import { z as z37 } from "zod";
10452
+ import { z as z38 } from "zod";
10210
10453
  var HANDOFF_TOOL_NAME = "handoff";
10211
10454
  function createHandoffTool(handoff) {
10212
- const profileSchema = z37.enum(handoff.profiles);
10455
+ const profileSchema = z38.enum(handoff.profiles);
10213
10456
  const handoffResultSchema = juniorToolResultSchema.extend({
10214
10457
  model_profile: profileSchema
10215
10458
  });
@@ -10217,7 +10460,7 @@ function createHandoffTool(handoff) {
10217
10460
  return zodTool({
10218
10461
  description: `Switch to another execution profile and continue the same task. Profiles: ${profileNames}. Call this as the only tool.`,
10219
10462
  executionMode: "sequential",
10220
- inputSchema: z37.object({
10463
+ inputSchema: z38.object({
10221
10464
  profile: profileSchema.describe("Target execution profile")
10222
10465
  }).strict(),
10223
10466
  outputSchema: handoffResultSchema,
@@ -10240,7 +10483,7 @@ function createHandoffTool(handoff) {
10240
10483
  }
10241
10484
 
10242
10485
  // src/chat/tools/web/fetch-tool.ts
10243
- import { z as z38 } from "zod";
10486
+ import { z as z39 } from "zod";
10244
10487
 
10245
10488
  // src/chat/tools/web/constants.ts
10246
10489
  var USER_AGENT = "junior-bot/0.1";
@@ -10368,7 +10611,7 @@ async function fetchWithPinnedLookup(url, resolved, signal) {
10368
10611
  }
10369
10612
  const client = url.protocol === "https:" ? https : http;
10370
10613
  const lookup = createPinnedLookup(resolved);
10371
- return new Promise((resolve, reject) => {
10614
+ return new Promise((resolve2, reject) => {
10372
10615
  const request = client.request(
10373
10616
  {
10374
10617
  protocol: url.protocol,
@@ -10398,7 +10641,7 @@ async function fetchWithPinnedLookup(url, resolved, signal) {
10398
10641
  }
10399
10642
  const rawStatus = response.statusCode ?? 500;
10400
10643
  const status = rawStatus >= 200 && rawStatus <= 599 ? rawStatus : 502;
10401
- resolve(
10644
+ resolve2(
10402
10645
  new Response(Buffer.concat(chunks), {
10403
10646
  status,
10404
10647
  headers
@@ -10690,9 +10933,9 @@ function createWebFetchTool(hooks, options = {}) {
10690
10933
  destructiveHint: false,
10691
10934
  openWorldHint: true
10692
10935
  },
10693
- inputSchema: z38.object({
10694
- url: z38.string().min(1).describe("HTTP(S) URL to fetch."),
10695
- max_chars: z38.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
10936
+ inputSchema: z39.object({
10937
+ url: z39.string().min(1).describe("HTTP(S) URL to fetch."),
10938
+ max_chars: z39.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
10696
10939
  }),
10697
10940
  outputSchema: juniorToolResultSchema,
10698
10941
  execute: async ({ url, max_chars }) => {
@@ -10753,7 +10996,7 @@ function createWebFetchTool(hooks, options = {}) {
10753
10996
  }
10754
10997
 
10755
10998
  // src/chat/tools/web/search.ts
10756
- import { z as z39 } from "zod";
10999
+ import { z as z40 } from "zod";
10757
11000
  import { generateText } from "ai";
10758
11001
  import { createGatewayProvider } from "@ai-sdk/gateway";
10759
11002
  var SEARCH_TIMEOUT_MS = 6e4;
@@ -10802,9 +11045,9 @@ function createWebSearchTool(modelId, override) {
10802
11045
  destructiveHint: false,
10803
11046
  openWorldHint: true
10804
11047
  },
10805
- inputSchema: z39.object({
10806
- query: z39.string().min(1).max(500).describe("Search query."),
10807
- max_results: z39.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
11048
+ inputSchema: z40.object({
11049
+ query: z40.string().min(1).max(500).describe("Search query."),
11050
+ max_results: z40.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
10808
11051
  }),
10809
11052
  outputSchema: juniorToolResultSchema,
10810
11053
  execute: async ({ query, max_results }) => {
@@ -10873,14 +11116,14 @@ function createWebSearchTool(modelId, override) {
10873
11116
  }
10874
11117
 
10875
11118
  // src/chat/tools/sandbox/write-file.ts
10876
- import { z as z40 } from "zod";
11119
+ import { z as z41 } from "zod";
10877
11120
  function createWriteFileTool() {
10878
11121
  return zodTool({
10879
11122
  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.",
10880
11123
  executionMode: "sequential",
10881
- inputSchema: z40.object({
10882
- path: z40.string().min(1).describe("Path to write in the sandbox workspace."),
10883
- content: z40.string().describe("UTF-8 file content to write.")
11124
+ inputSchema: z41.object({
11125
+ path: z41.string().min(1).describe("Path to write in the sandbox workspace."),
11126
+ content: z41.string().describe("UTF-8 file content to write.")
10884
11127
  }),
10885
11128
  outputSchema: juniorToolResultSchema,
10886
11129
  execute: async () => {
@@ -10920,7 +11163,7 @@ function createToolState(hooks, context) {
10920
11163
  }
10921
11164
  };
10922
11165
  }
10923
- function createTools(availableSkills, hooks = {}, context) {
11166
+ function createTools(availableSkills, hooks = {}, context, options = {}) {
10924
11167
  const state = createToolState(hooks, context);
10925
11168
  const slackContext = getSlackToolContext(context);
10926
11169
  const slackSourceCapabilities = slackContext ? resolveChannelCapabilities(slackContext.sourceChannelId) : void 0;
@@ -10928,9 +11171,11 @@ function createTools(availableSkills, hooks = {}, context) {
10928
11171
  slackContext && slackSourceCapabilities?.canSendFiles
10929
11172
  );
10930
11173
  const tools = {
10931
- loadSkill: createLoadSkillTool(availableSkills, {
10932
- onSkillLoaded: hooks.onSkillLoaded
10933
- }),
11174
+ ...options.includeLoadSkill === false ? {} : {
11175
+ loadSkill: createLoadSkillTool(availableSkills, {
11176
+ onSkillLoaded: hooks.onSkillLoaded
11177
+ })
11178
+ },
10934
11179
  reportProgress: createReportProgressTool(),
10935
11180
  systemTime: createSystemTimeTool(),
10936
11181
  bash: createBashTool(),
@@ -10952,6 +11197,13 @@ function createTools(availableSkills, hooks = {}, context) {
10952
11197
  if (context.conversationId) {
10953
11198
  tools.queryConversationEvents = createQueryConversationEventsTool(context);
10954
11199
  }
11200
+ if (context.supportsImageInput) {
11201
+ tools.viewImage = createViewImageTool(
11202
+ context.workspace,
11203
+ context.supportsImageInput,
11204
+ hooks.toolOverrides?.viewImage
11205
+ );
11206
+ }
10955
11207
  if (hooks.writeGeneratedArtifacts) {
10956
11208
  tools.imageGenerate = createImageGenerateTool(
10957
11209
  {
@@ -11546,6 +11798,8 @@ function isPluginToolInputError(error) {
11546
11798
  }
11547
11799
  function getToolErrorType(error) {
11548
11800
  if (error instanceof McpToolError) return "tool_error";
11801
+ if (error instanceof McpProviderError) return "mcp_provider_error";
11802
+ if (error instanceof OAuthProviderError) return "oauth_provider_error";
11549
11803
  if (error instanceof ToolInputError || isPluginToolInputError(error)) {
11550
11804
  return "tool_input_error";
11551
11805
  }
@@ -11568,6 +11822,8 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
11568
11822
  const errorMessage = getMcpAwareTelemetryMessage(error, conversationPrivacy);
11569
11823
  const errorAttributes = {
11570
11824
  "error.type": errorType,
11825
+ ...getMcpProviderErrorAttributes(error),
11826
+ ...getOAuthProviderErrorAttributes(error),
11571
11827
  ...error instanceof PluginCredentialFailureError ? { "app.credential.provider": error.provider } : {}
11572
11828
  };
11573
11829
  if (setExecutionSpanAttributes) {
@@ -11600,7 +11856,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
11600
11856
  "gen_ai.operation.name": "execute_tool",
11601
11857
  "gen_ai.tool.name": toolName,
11602
11858
  ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
11603
- "error.type": errorType,
11859
+ ...errorAttributes,
11604
11860
  "exception.message": errorMessage
11605
11861
  },
11606
11862
  "Agent tool call failed"
@@ -11616,6 +11872,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
11616
11872
  "gen_ai.operation.name": "execute_tool",
11617
11873
  "gen_ai.tool.name": toolName,
11618
11874
  ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
11875
+ ...errorAttributes,
11619
11876
  ...getToolErrorAttributes(error)
11620
11877
  },
11621
11878
  "Agent tool call failed"
@@ -11625,17 +11882,17 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
11625
11882
  }
11626
11883
 
11627
11884
  // src/chat/tools/execute-tool.ts
11628
- import { z as z41 } from "zod";
11885
+ import { z as z42 } from "zod";
11629
11886
  var EXECUTE_TOOL_NAME = "executeTool";
11630
11887
  function createExecuteToolTool() {
11631
11888
  return zodTool({
11632
11889
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
11633
11890
  executionMode: "sequential",
11634
- inputSchema: z41.object({
11635
- tool_name: z41.string().min(1).describe(
11891
+ inputSchema: z42.object({
11892
+ tool_name: z42.string().min(1).describe(
11636
11893
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
11637
11894
  ),
11638
- arguments: z41.record(z41.string(), z41.unknown()).describe(
11895
+ arguments: z42.record(z42.string(), z42.unknown()).describe(
11639
11896
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
11640
11897
  ).optional()
11641
11898
  }).strict(),
@@ -11675,42 +11932,42 @@ function planToolExposure(tools) {
11675
11932
  }
11676
11933
 
11677
11934
  // src/chat/tools/search-tools.ts
11678
- import { z as z42 } from "zod";
11935
+ import { z as z43 } from "zod";
11679
11936
  var SEARCH_TOOLS_NAME = "searchTools";
11680
11937
  var DEFAULT_MAX_RESULTS2 = 5;
11681
11938
  var MAX_RESULTS3 = 20;
11682
11939
  var MODEL_VISIBLE_DESCRIPTION_CAP = 180;
11683
- var searchToolsSourceSchema = z42.object({
11684
- id: z42.string(),
11685
- description: z42.string()
11940
+ var searchToolsSourceSchema = z43.object({
11941
+ id: z43.string(),
11942
+ description: z43.string()
11686
11943
  }).strict();
11687
- var toolCallExampleSchema = z42.object({
11688
- tool_name: z42.string(),
11689
- arguments: z42.record(z42.string(), z42.string())
11944
+ var toolCallExampleSchema = z43.object({
11945
+ tool_name: z43.string(),
11946
+ arguments: z43.record(z43.string(), z43.string())
11690
11947
  }).strict();
11691
- var searchToolsToolSchema = z42.object({
11692
- tool_name: z42.string(),
11693
- description: z42.string(),
11694
- exposure: z42.enum(["direct", "deferred", "modelOnly", "hidden"]),
11695
- source: z42.string().optional(),
11696
- signature: z42.string(),
11948
+ var searchToolsToolSchema = z43.object({
11949
+ tool_name: z43.string(),
11950
+ description: z43.string(),
11951
+ exposure: z43.enum(["direct", "deferred", "modelOnly", "hidden"]),
11952
+ source: z43.string().optional(),
11953
+ signature: z43.string(),
11697
11954
  call: toolCallExampleSchema,
11698
- input_schema: z42.unknown(),
11699
- input_schema_summary: z42.string(),
11700
- call_notes: z42.array(z42.string()),
11701
- annotations: z42.record(z42.string(), z42.unknown())
11955
+ input_schema: z43.unknown(),
11956
+ input_schema_summary: z43.string(),
11957
+ call_notes: z43.array(z43.string()),
11958
+ annotations: z43.record(z43.string(), z43.unknown())
11702
11959
  }).strict();
11703
11960
  var searchToolsOutputSchema = juniorToolResultSchema.extend({
11704
- query: z42.string().nullable(),
11705
- source: z42.string().nullable(),
11706
- sources: z42.array(searchToolsSourceSchema),
11707
- total_catalog_tools: z42.number().int().nonnegative(),
11708
- total_eligible_tools: z42.number().int().nonnegative(),
11709
- total_matches: z42.number().int().nonnegative(),
11710
- returned_tools: z42.number().int().nonnegative(),
11711
- execution_tool: z42.literal("executeTool"),
11961
+ query: z43.string().nullable(),
11962
+ source: z43.string().nullable(),
11963
+ sources: z43.array(searchToolsSourceSchema),
11964
+ total_catalog_tools: z43.number().int().nonnegative(),
11965
+ total_eligible_tools: z43.number().int().nonnegative(),
11966
+ total_matches: z43.number().int().nonnegative(),
11967
+ returned_tools: z43.number().int().nonnegative(),
11968
+ execution_tool: z43.literal("executeTool"),
11712
11969
  execution_example: toolCallExampleSchema,
11713
- tools: z42.array(searchToolsToolSchema)
11970
+ tools: z43.array(searchToolsToolSchema)
11714
11971
  }).strict();
11715
11972
  function normalize2(value) {
11716
11973
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
@@ -11902,14 +12159,14 @@ function createSearchToolsTool(catalogTools) {
11902
12159
  return zodTool({
11903
12160
  description: renderSearchToolsDescription(knownSources),
11904
12161
  annotations: { readOnlyHint: true, destructiveHint: false },
11905
- inputSchema: z42.object({
11906
- query: z42.string().nullable().describe(
12162
+ inputSchema: z43.object({
12163
+ query: z43.string().nullable().describe(
11907
12164
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
11908
12165
  ).optional(),
11909
- source: z42.string().nullable().describe(
12166
+ source: z43.string().nullable().describe(
11910
12167
  "Optional source id to search within, such as a plugin source returned in sources."
11911
12168
  ).optional(),
11912
- max_results: z42.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
12169
+ max_results: z43.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
11913
12170
  }).strict(),
11914
12171
  outputSchema: searchToolsOutputSchema,
11915
12172
  privateTraceResult: (result) => ({
@@ -12027,7 +12284,8 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12027
12284
  const result = isSandbox ? await sandboxTools.execute({
12028
12285
  toolName,
12029
12286
  input: sandboxInput,
12030
- ...signal ? { signal } : {}
12287
+ ...signal ? { signal } : {},
12288
+ ...toolName === "grep" || toolName === "findFiles" ? { setToolCallSpanAttributes: setSpanAttributes2 } : {}
12031
12289
  }) : await toolDef.execute(toolInput, {
12032
12290
  experimental_context: sandbox,
12033
12291
  ...signal ? { signal } : {},
@@ -13174,6 +13432,27 @@ function createPluginEgress(deps) {
13174
13432
  }
13175
13433
 
13176
13434
  // src/chat/agent/tools.ts
13435
+ async function tryRecordSkillLoadStat(skill, spanContext) {
13436
+ if (!process.env.DATABASE_URL) return;
13437
+ try {
13438
+ await incrementStat({
13439
+ namespace: skill.pluginProvider ?? "junior",
13440
+ metric: "skill_load",
13441
+ name: skill.name
13442
+ });
13443
+ } catch (error) {
13444
+ logWarn(
13445
+ "skill_load_stat_failed",
13446
+ spanContext,
13447
+ {
13448
+ "app.skill.name": skill.name,
13449
+ "app.plugin.name": skill.pluginProvider,
13450
+ "exception.message": error instanceof Error ? error.message : String(error)
13451
+ },
13452
+ "Failed to record skill load stat"
13453
+ );
13454
+ }
13455
+ }
13177
13456
  async function wireAgentTools(args) {
13178
13457
  const runSource = args.routing.source;
13179
13458
  const credentialUserId = args.routing.credentialContext ? credentialUserSubjectId(args.routing.credentialContext) : void 0;
@@ -13248,6 +13527,9 @@ async function wireAgentTools(args) {
13248
13527
  const loadableSkills = args.availableSkills.filter(
13249
13528
  (skill) => skill.disableModelInvocation !== true || skill.name === args.invokedSkill?.name
13250
13529
  );
13530
+ const explicitSkillLoaded = Boolean(
13531
+ args.invokedSkill && args.activeSkills.some((skill) => skill.name === args.invokedSkill?.name)
13532
+ );
13251
13533
  const commonToolRuntimeContext = {
13252
13534
  conversationId: args.conversationId,
13253
13535
  userText: args.userInput,
@@ -13272,6 +13554,7 @@ async function wireAgentTools(args) {
13272
13554
  }),
13273
13555
  mcpToolManager,
13274
13556
  workspace: agentSandbox.workspace,
13557
+ supportsImageInput: args.supportsImageInput,
13275
13558
  surface: args.surface,
13276
13559
  ...args.requestHandoff ? { handoff: args.requestHandoff } : {}
13277
13560
  };
@@ -13327,6 +13610,7 @@ async function wireAgentTools(args) {
13327
13610
  if (await mcpToolManager.activateForSkill(effective)) {
13328
13611
  await args.recordConnectedMcpProvider(effective.pluginProvider);
13329
13612
  }
13613
+ await tryRecordSkillLoadStat(effective, args.spanContext);
13330
13614
  if (mcpAuth.getPendingPause()) {
13331
13615
  return void 0;
13332
13616
  }
@@ -13345,7 +13629,8 @@ async function wireAgentTools(args) {
13345
13629
  };
13346
13630
  }
13347
13631
  },
13348
- toolRuntimeContext
13632
+ toolRuntimeContext,
13633
+ { includeLoadSkill: !explicitSkillLoaded }
13349
13634
  );
13350
13635
  const plannedToolExposure = planToolExposure(
13351
13636
  tools
@@ -13430,7 +13715,11 @@ async function wireAgentTools(args) {
13430
13715
  "mcp_tool_manager_close_failed",
13431
13716
  {},
13432
13717
  {
13433
- "exception.message": closeError instanceof Error ? closeError.message : String(closeError)
13718
+ ...getMcpProviderErrorAttributes(closeError),
13719
+ "exception.message": getMcpAwareTelemetryMessage(
13720
+ closeError,
13721
+ args.conversationPrivacy
13722
+ )
13434
13723
  },
13435
13724
  "Failed to close MCP tool manager"
13436
13725
  );
@@ -13454,6 +13743,7 @@ function createResumeState(args) {
13454
13743
  let latestSafeBoundaryMessages = [];
13455
13744
  let timedOut = false;
13456
13745
  let resumeMessages = [];
13746
+ let turnContexts = [];
13457
13747
  let turnStartMessageIndex;
13458
13748
  const currentSliceId = args.sessionRecordState.currentSliceId;
13459
13749
  const currentDurationMs = () => Date.now() - args.startedAtMs;
@@ -13487,6 +13777,9 @@ function createResumeState(args) {
13487
13777
  setBeforeMessageCount(count) {
13488
13778
  beforeMessageCount = count;
13489
13779
  },
13780
+ setTurnContexts(contexts) {
13781
+ turnContexts = contexts;
13782
+ },
13490
13783
  /** Adopt an already committed epoch replacement as every resume baseline. */
13491
13784
  adoptCommittedBoundary(messages) {
13492
13785
  latestSafeBoundaryMessages = [...messages];
@@ -13514,6 +13807,7 @@ function createResumeState(args) {
13514
13807
  sliceId: currentSliceId,
13515
13808
  messages,
13516
13809
  ...trailingMessageProvenance ? { trailingMessageProvenance } : {},
13810
+ ...turnContexts.length > 0 ? { turnContexts } : {},
13517
13811
  ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
13518
13812
  });
13519
13813
  if (!persisted) {
@@ -13780,6 +14074,27 @@ function renderSummaryInput(piMessages, conversationContext) {
13780
14074
  }
13781
14075
  async function summarizeContext(args, deps) {
13782
14076
  const source = renderSummaryInput(args.piMessages, args.conversationContext);
14077
+ const instructions = args.purpose === "active_turn" ? [
14078
+ "You are performing an ACTIVE-TURN CONTEXT CHECKPOINT COMPACTION for Junior.",
14079
+ "Create concise internal continuation state for the same agent run, which must continue the unfinished task immediately after this checkpoint.",
14080
+ "",
14081
+ "Include:",
14082
+ "- Work completed and concrete outcomes",
14083
+ "- Exact work currently in progress",
14084
+ "- The immediate next action",
14085
+ "- Durable constraints, user preferences, IDs, URLs, artifacts, sandbox references, auth state, and unresolved blockers",
14086
+ "",
14087
+ "Do not write a user-facing reply, announce a plan, or imply that the task is complete unless the source history proves it is complete."
14088
+ ] : [
14089
+ "You are performing a CONTEXT CHECKPOINT COMPACTION for Junior.",
14090
+ "Create a concise continuation summary for the agent that will continue this Slack thread.",
14091
+ "",
14092
+ "Include:",
14093
+ "- Current outstanding asks",
14094
+ "- Key decisions, completed work, and outcomes",
14095
+ "- Durable constraints, user preferences, IDs, URLs, artifacts, canvas links, sandbox references, and auth state",
14096
+ "- Clear next steps and unresolved blockers"
14097
+ ];
13783
14098
  const result = await deps.completeText({
13784
14099
  modelId: botConfig.fastModelId,
13785
14100
  messageAttributeMode: "metadata",
@@ -13789,14 +14104,7 @@ async function summarizeContext(args, deps) {
13789
14104
  {
13790
14105
  role: "user",
13791
14106
  content: [
13792
- "You are performing a CONTEXT CHECKPOINT COMPACTION for Junior.",
13793
- "Create a concise continuation summary for the agent that will continue this Slack thread.",
13794
- "",
13795
- "Include:",
13796
- "- Current outstanding asks",
13797
- "- Key decisions, completed work, and outcomes",
13798
- "- Durable constraints, user preferences, IDs, URLs, artifacts, canvas links, sandbox references, and auth state",
13799
- "- Clear next steps and unresolved blockers",
14107
+ ...instructions,
13800
14108
  "",
13801
14109
  "Do not invent details. Do not include raw secrets or credentials.",
13802
14110
  "",
@@ -14022,7 +14330,8 @@ async function compactActiveContextIfNeeded(args, deps) {
14022
14330
  summary = await summarizeContext(
14023
14331
  {
14024
14332
  ...args,
14025
- piMessages: args.piMessages
14333
+ piMessages: args.piMessages,
14334
+ purpose: "active_turn"
14026
14335
  },
14027
14336
  deps
14028
14337
  );
@@ -14052,22 +14361,15 @@ async function compactActiveContextIfNeeded(args, deps) {
14052
14361
  );
14053
14362
  return { compacted: false, reason: "summary_failed" };
14054
14363
  }
14055
- const summaryText = `${COMPACTION_SUMMARY_PREFIX}
14056
- ${summary}`;
14057
- const runtimeMessage = retainRuntimeTurnContext(args.piMessages).at(-1);
14058
- const summaryMessages = runtimeMessage ? [
14059
- {
14060
- ...runtimeMessage,
14061
- content: [
14062
- ...runtimeMessage.content,
14063
- { type: "text", text: summaryText }
14064
- ]
14065
- }
14066
- ] : [userMessage(summaryText)];
14364
+ const summaryMessage = userMessage(
14365
+ `${ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX}
14366
+ ${summary}`
14367
+ );
14368
+ const runtimeMessages = retainRuntimeTurnContext(args.piMessages);
14067
14369
  const retainedInstruction = pendingMessages.length === 0 ? await loadLastInstruction(args.conversationId) : void 0;
14068
14370
  const instructionMessages = retainedInstruction ? [retainedInstruction.message] : pendingMessages.map((entry) => entry.message);
14069
14371
  const instructionProvenance = retainedInstruction ? [retainedInstruction.provenance] : pendingMessages.map((entry) => entry.provenance);
14070
- const messages = [...summaryMessages, ...instructionMessages];
14372
+ const messages = [...runtimeMessages, ...instructionMessages, summaryMessage];
14071
14373
  const replacementInputTokens = estimateHistoryTokens(messages);
14072
14374
  if (replacementInputTokens >= inputLimitTokens) {
14073
14375
  throw new ContextInputLimitExceededError(
@@ -14100,10 +14402,11 @@ ${summary}`;
14100
14402
  },
14101
14403
  summary,
14102
14404
  replacementHistory: replacementMessages.map((message, index) => {
14103
- const provenance = index === 0 ? contextProvenance : instructionProvenance[index - 1];
14405
+ const isSummary = index === replacementMessages.length - 1;
14406
+ const provenance = isSummary ? contextProvenance : instructionProvenance[index];
14104
14407
  return {
14105
14408
  item: historyItemFromPiMessage(message, provenance),
14106
- ...index === 1 && retainedInstruction ? { sourceEventSeq: retainedInstruction.sourceEventSeq } : {}
14409
+ ...index === 0 && retainedInstruction ? { sourceEventSeq: retainedInstruction.sourceEventSeq } : {}
14107
14410
  };
14108
14411
  })
14109
14412
  }
@@ -14130,12 +14433,12 @@ var AssistantMessageDeliveryError = class extends Error {
14130
14433
  originalError;
14131
14434
  };
14132
14435
  function waitForAbortSettlement(promise, timeoutMs) {
14133
- return new Promise((resolve) => {
14436
+ return new Promise((resolve2) => {
14134
14437
  let done = false;
14135
14438
  const timeoutId = setTimeout(() => {
14136
14439
  if (!done) {
14137
14440
  done = true;
14138
- resolve(false);
14441
+ resolve2(false);
14139
14442
  }
14140
14443
  }, timeoutMs);
14141
14444
  timeoutId.unref?.();
@@ -14144,14 +14447,14 @@ function waitForAbortSettlement(promise, timeoutMs) {
14144
14447
  if (!done) {
14145
14448
  done = true;
14146
14449
  clearTimeout(timeoutId);
14147
- resolve(true);
14450
+ resolve2(true);
14148
14451
  }
14149
14452
  },
14150
14453
  () => {
14151
14454
  if (!done) {
14152
14455
  done = true;
14153
14456
  clearTimeout(timeoutId);
14154
- resolve(true);
14457
+ resolve2(true);
14155
14458
  }
14156
14459
  }
14157
14460
  );
@@ -14374,6 +14677,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14374
14677
  skillSandbox,
14375
14678
  syncLoadedSkillNamesForResume
14376
14679
  });
14680
+ const explicitSkill = invokedSkill ? activeSkills.find((skill) => skill.name === invokedSkill.name) ?? null : null;
14377
14681
  const { routerBlocks, userContentParts } = buildPromptInput(input);
14378
14682
  const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...input.piMessages ?? []];
14379
14683
  const storedTurnRoute = await loadTurnRoute({ conversationId, turnId });
@@ -14577,6 +14881,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14577
14881
  skillSandbox,
14578
14882
  spanContext,
14579
14883
  state,
14884
+ supportsImageInput: () => resolveGatewayModel(activeModelId).input.includes("image"),
14580
14885
  surface,
14581
14886
  syncLoadedSkillNamesForResume,
14582
14887
  toolCalls,
@@ -14592,7 +14897,8 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14592
14897
  inputMessagesAttribute,
14593
14898
  promptContentParts,
14594
14899
  promptHistoryMessages,
14595
- shouldPromptAgent
14900
+ shouldPromptAgent,
14901
+ turnContexts
14596
14902
  } = await assemblePrompt({
14597
14903
  activeMcpCatalogs: wiring.activeMcpCatalogs,
14598
14904
  currentActor: actor,
@@ -14602,7 +14908,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14602
14908
  conversationPrivacy,
14603
14909
  existingSessionPiMessages: existingSessionRecord?.piMessages,
14604
14910
  existingTurnStartMessageIndex: existingSessionRecord?.turnStartMessageIndex,
14605
- invocation: skillInvocation,
14911
+ explicitSkill,
14606
14912
  priorPiMessages,
14607
14913
  resumedFromSessionRecord,
14608
14914
  routing,
@@ -14611,6 +14917,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14611
14917
  toolRuntimeContext: wiring.toolRuntimeContext,
14612
14918
  userContentParts
14613
14919
  });
14920
+ runResume.setTurnContexts(turnContexts);
14614
14921
  const applyPendingHandoff = () => {
14615
14922
  if (!pendingHandoff) {
14616
14923
  return void 0;