@sentry/junior 0.114.0 → 0.115.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 (88) hide show
  1. package/dist/{agent-hooks-WNBUY4OB.js → agent-hooks-4EUUXP2F.js} +12 -11
  2. package/dist/api/conversations/detail.d.ts +2 -0
  3. package/dist/api/conversations/event-list.d.ts +2 -0
  4. package/dist/api/health.d.ts +7 -0
  5. package/dist/api/plugin-reports.d.ts +62 -0
  6. package/dist/api/plugins.d.ts +9 -0
  7. package/dist/api/runtime.d.ts +24 -0
  8. package/dist/api/schema/conversation.d.ts +8 -0
  9. package/dist/api/schema.d.ts +2 -2
  10. package/dist/api/schema.js +6 -6
  11. package/dist/api/skills.d.ts +6 -0
  12. package/dist/api.js +123 -99
  13. package/dist/app.js +1104 -5006
  14. package/dist/catalog-runtime-Y32UEQLB.js +18 -0
  15. package/dist/chat/agent/request.d.ts +6 -0
  16. package/dist/chat/agent/sandbox.d.ts +2 -0
  17. package/dist/chat/agent/tools.d.ts +2 -0
  18. package/dist/chat/config.d.ts +2 -0
  19. package/dist/chat/conversations/history.d.ts +6 -0
  20. package/dist/chat/local/credential-sync.d.ts +5 -0
  21. package/dist/chat/local/oauth-relay.d.ts +4 -0
  22. package/dist/chat/mcp/oauth.d.ts +1 -0
  23. package/dist/chat/oauth-authorization.d.ts +17 -0
  24. package/dist/chat/oauth-flow.d.ts +9 -0
  25. package/dist/chat/sandbox/egress/schemas.d.ts +46 -0
  26. package/dist/chat/sandbox/egress/signals.d.ts +9 -0
  27. package/dist/chat/sandbox/sandbox.d.ts +2 -0
  28. package/dist/chat/sentry-payload-filter.d.ts +4 -4
  29. package/dist/chat/services/mcp-auth-orchestration.d.ts +5 -3
  30. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -0
  31. package/dist/chat/slack/message-control.d.ts +2 -0
  32. package/dist/chat/slack/oauth-authorization-message.d.ts +3 -0
  33. package/dist/chat/task-execution/slack-work.d.ts +2 -0
  34. package/dist/chat/tool-support/zod-tool.d.ts +1 -1
  35. package/dist/chat/tools/definition.d.ts +5 -0
  36. package/dist/{chunk-TLGM5X2M.js → chunk-2QG4MZQ7.js} +2 -2
  37. package/dist/{chunk-WV4UJBAS.js → chunk-43RYMWJ2.js} +523 -2373
  38. package/dist/chunk-4CODA7P5.js +43 -0
  39. package/dist/{chunk-YU6PO42Q.js → chunk-67CVNDJ7.js} +1 -1
  40. package/dist/{chunk-OSKEC4XU.js → chunk-6FKLPVF3.js} +1 -1
  41. package/dist/{chunk-2ZTTXTSZ.js → chunk-6OPTF4BQ.js} +10 -4
  42. package/dist/{chunk-W6RDUAO3.js → chunk-6V762AB4.js} +1 -1
  43. package/dist/{chunk-ABPSDO6M.js → chunk-AGLQYELI.js} +77 -8
  44. package/dist/{chunk-6QWPVTMO.js → chunk-CEYOL62C.js} +9 -7
  45. package/dist/{chunk-RD7EJI7H.js → chunk-CWEZEPD4.js} +3 -3
  46. package/dist/chunk-DIB4GX3G.js +8 -0
  47. package/dist/{chunk-K2W4CK7K.js → chunk-EW5MLKT5.js} +10 -75
  48. package/dist/{chunk-42WRQLJ6.js → chunk-F3B2YJ76.js} +0 -14
  49. package/dist/chunk-FA6POGLH.js +70 -0
  50. package/dist/{chunk-PM3PX42K.js → chunk-FHC4JUSF.js} +1 -1
  51. package/dist/{chunk-6Y5L4BOM.js → chunk-FXOYURO4.js} +5 -3
  52. package/dist/{chunk-S4QDH23A.js → chunk-G36V2YGB.js} +83 -81
  53. package/dist/{chunk-O4GT7LYB.js → chunk-G5SX4UPR.js} +3 -43
  54. package/dist/chunk-HDSDCEPK.js +80 -0
  55. package/dist/{chunk-FIGAF6KL.js → chunk-HWCQRGLL.js} +5 -3
  56. package/dist/{chunk-C4MZ3GP5.js → chunk-HZF5P5UN.js} +1 -1
  57. package/dist/chunk-LYA2AB5G.js +1914 -0
  58. package/dist/{chunk-4FUEG3QY.js → chunk-NVUQSFWX.js} +2 -2
  59. package/dist/chunk-OB42YVAE.js +16 -0
  60. package/dist/{chunk-XLFBQJUO.js → chunk-PVZHG2XZ.js} +4 -4
  61. package/dist/{chunk-VNNBSKCO.js → chunk-RAVKCKXA.js} +7 -7
  62. package/dist/{chunk-ZGXH3REA.js → chunk-SYJT7AKB.js} +12 -2
  63. package/dist/{chunk-5CZ7RETL.js → chunk-TVE25LPW.js} +1 -1
  64. package/dist/chunk-W3U3YQYI.js +71 -0
  65. package/dist/chunk-YCLO7D7M.js +4267 -0
  66. package/dist/cli/chat.js +72 -40
  67. package/dist/cli/check.js +10 -8
  68. package/dist/cli/init.js +1 -0
  69. package/dist/cli/plugins.js +16 -14
  70. package/dist/cli/snapshot-warmup.js +13 -11
  71. package/dist/cli/upgrade.js +10 -8
  72. package/dist/{db-4VLP7KBF.js → db-KBSJHAZU.js} +8 -7
  73. package/dist/handlers/sandbox-egress-signals.d.ts +4 -0
  74. package/dist/instrumentation.js +72 -4
  75. package/dist/nitro.js +2 -2
  76. package/dist/oauth-callback-server-EYRMJTCS.js +231 -0
  77. package/dist/oauth-relay-3ZU72UGL.js +9 -0
  78. package/dist/reporting-schema.d.ts +12 -4
  79. package/dist/reporting.d.ts +5 -5
  80. package/dist/{runner-IPXLWQOF.js → runner-RQGZ5Z2S.js} +139 -72
  81. package/dist/sandbox-egress-signals-CDFAP67O.js +75 -0
  82. package/dist/{skills-QCNZISNF.js → skills-JJQIE3BT.js} +10 -8
  83. package/dist/validation-MZ47PZP5.js +21 -0
  84. package/dist/version.js +1 -1
  85. package/package.json +5 -5
  86. package/dist/catalog-runtime-6O4M6JGA.js +0 -16
  87. package/dist/chat/oauth-authorization-message.d.ts +0 -6
  88. package/dist/validation-I2BW3UHD.js +0 -19
@@ -1,3 +1,31 @@
1
+ import {
2
+ authorizationId
3
+ } from "./chunk-DIB4GX3G.js";
4
+ import {
5
+ RetryableDeliveryError,
6
+ actorFromRouting,
7
+ addReactionToMessage,
8
+ assertRunRoutingConsistency,
9
+ createMcpOAuthClientProvider,
10
+ createUserTokenStore,
11
+ deleteMcpAuthSession,
12
+ deliverOAuthAuthorization,
13
+ formatProviderLabel,
14
+ getMcpAuthSession,
15
+ getSlackMessagePermalink,
16
+ hasEgressCredentialHooks,
17
+ issuePluginCredential,
18
+ issueProviderCredentialLease,
19
+ onPluginEgressResponse,
20
+ patchMcpAuthSession,
21
+ resolveBaseUrl,
22
+ selectPluginGrant,
23
+ slackOutputPolicy,
24
+ startOAuthFlow,
25
+ surfaceFromRouting,
26
+ toolInvocationDestination,
27
+ uploadFilesToConversation
28
+ } from "./chunk-LYA2AB5G.js";
1
29
  import {
2
30
  CooperativeTurnYieldError,
3
31
  JUNIOR_THREAD_STATE_TTL_MS,
@@ -10,7 +38,6 @@ import {
10
38
  extractAssistantText,
11
39
  getAgentContextCompactionTriggerTokens,
12
40
  getAgentContextInputLimitTokens,
13
- getInterruptionMarker,
14
41
  getPiMessageRole,
15
42
  getTerminalAssistantMessages,
16
43
  hasRuntimeTurnContext,
@@ -38,7 +65,13 @@ import {
38
65
  stripRuntimeTurnContext,
39
66
  trimTrailingAssistantMessages,
40
67
  updateConversationStats
41
- } from "./chunk-6QWPVTMO.js";
68
+ } from "./chunk-CEYOL62C.js";
69
+ import {
70
+ parseSandboxEgressAuthRequiredSignal,
71
+ parseSandboxEgressPermissionDeniedSignal,
72
+ sandboxEgressCredentialContextSchema,
73
+ sandboxEgressCredentialLeaseSchema
74
+ } from "./chunk-HDSDCEPK.js";
42
75
  import {
43
76
  buildNonInteractiveShellScript,
44
77
  createSandboxSession,
@@ -55,7 +88,7 @@ import {
55
88
  runNonInteractiveCommand,
56
89
  throwSandboxOperationError,
57
90
  wrapSandboxSetupError
58
- } from "./chunk-XLFBQJUO.js";
91
+ } from "./chunk-PVZHG2XZ.js";
59
92
  import {
60
93
  addAgentTurnUsage,
61
94
  hasAgentTurnUsage
@@ -65,43 +98,37 @@ import {
65
98
  } from "./chunk-UIE3R5XU.js";
66
99
  import {
67
100
  createPluginHookRunner,
68
- createPluginLogger,
69
101
  getPluginSystemPromptContributions,
70
102
  getPluginTools,
71
103
  getPluginUserPromptContributions,
72
- getPlugins,
73
104
  getSlackToolContext,
74
105
  resolveChannelCapabilities
75
- } from "./chunk-VNNBSKCO.js";
106
+ } from "./chunk-RAVKCKXA.js";
76
107
  import {
77
108
  SANDBOX_DATA_ROOT,
78
109
  SANDBOX_SKILLS_ROOT,
79
110
  SANDBOX_WORKSPACE_ROOT,
80
- acquireActiveLock,
81
111
  getStateAdapter,
82
112
  sandboxSkillDir,
83
113
  sandboxSkillFile
84
- } from "./chunk-YU6PO42Q.js";
114
+ } from "./chunk-67CVNDJ7.js";
85
115
  import {
86
116
  SlackActionError,
87
117
  downloadPrivateSlackFile,
88
118
  getFilePermalink,
89
119
  getSlackClient,
90
120
  isConversationScopedChannel,
91
- isDmChannel,
92
121
  normalizeSlackConversationId,
93
122
  withSlackRetries
94
- } from "./chunk-RD7EJI7H.js";
123
+ } from "./chunk-CWEZEPD4.js";
95
124
  import {
96
125
  contextProvenance,
97
126
  getConversationEventStore,
98
127
  getConversationSearchStore,
99
128
  getConversationStore,
100
- getDb,
101
129
  instructionActors,
102
- instructionProvenanceFor,
103
- parseDestination
104
- } from "./chunk-FIGAF6KL.js";
130
+ instructionProvenanceFor
131
+ } from "./chunk-HWCQRGLL.js";
105
132
  import {
106
133
  sleep
107
134
  } from "./chunk-4ZNGQH7C.js";
@@ -134,7 +161,7 @@ import {
134
161
  renderCurrentInstruction,
135
162
  resolveGatewayModel,
136
163
  unwrapCurrentInstruction
137
- } from "./chunk-ZGXH3REA.js";
164
+ } from "./chunk-SYJT7AKB.js";
138
165
  import {
139
166
  parseSlackThreadId,
140
167
  resolveConversationPrivacy,
@@ -144,26 +171,27 @@ import {
144
171
  toGenAiMessagesTraceAttributes,
145
172
  toGenAiPayloadMetadata,
146
173
  toGenAiPayloadTraceAttributes
147
- } from "./chunk-6Y5L4BOM.js";
174
+ } from "./chunk-FXOYURO4.js";
148
175
  import {
149
176
  discoverSkills,
150
177
  findSkillByName,
151
178
  loadSkillsByName,
152
179
  parseSkillInvocation
153
- } from "./chunk-TLGM5X2M.js";
180
+ } from "./chunk-2QG4MZQ7.js";
154
181
  import {
155
182
  pluginCatalogRuntime
156
- } from "./chunk-OSKEC4XU.js";
183
+ } from "./chunk-6FKLPVF3.js";
157
184
  import {
158
185
  CredentialUnavailableError,
159
- credentialContextSchema,
160
- credentialUserSubjectId,
161
186
  resolveAuthTokenPlaceholder,
162
187
  resolvePluginCommandEnv
163
- } from "./chunk-K2W4CK7K.js";
188
+ } from "./chunk-EW5MLKT5.js";
189
+ import {
190
+ credentialUserSubjectId
191
+ } from "./chunk-W3U3YQYI.js";
164
192
  import {
165
193
  isUserActor
166
- } from "./chunk-C4MZ3GP5.js";
194
+ } from "./chunk-HZF5P5UN.js";
167
195
  import {
168
196
  extractGenAiUsageAttributes,
169
197
  extractGenAiUsageSummary,
@@ -181,13 +209,12 @@ import {
181
209
  summarizeMessageText,
182
210
  withLogContext,
183
211
  withSpan
184
- } from "./chunk-4FUEG3QY.js";
212
+ } from "./chunk-NVUQSFWX.js";
185
213
  import {
186
- isRecord,
187
214
  parseSlackChannelId,
188
215
  parseSlackMessageTs,
189
216
  parseSlackUserId
190
- } from "./chunk-42WRQLJ6.js";
217
+ } from "./chunk-F3B2YJ76.js";
191
218
  import {
192
219
  startInactiveSpan,
193
220
  withActiveSpan
@@ -920,12 +947,12 @@ function sortJsonValue(value) {
920
947
  Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, sortJsonValue(item)])
921
948
  );
922
949
  }
923
- function isRecord2(value) {
950
+ function isRecord(value) {
924
951
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
925
952
  }
926
953
  function injectContinuationToolName(details, toolName) {
927
954
  const parsed = juniorToolResultSchema.parse(details);
928
- if (!isRecord2(parsed.continuation)) {
955
+ if (!isRecord(parsed.continuation)) {
929
956
  return parsed;
930
957
  }
931
958
  return juniorToolResultWithBoundContinuationSchema.parse({
@@ -1299,69 +1326,6 @@ var AuthorizationFlowDisabledError = class extends Error {
1299
1326
  }
1300
1327
  };
1301
1328
 
1302
- // src/chat/agent/request.ts
1303
- var RetryableDeliveryError = class extends Error {
1304
- constructor(cause) {
1305
- super("Assistant delivery was transient or ambiguous", { cause });
1306
- this.name = "RetryableDeliveryError";
1307
- }
1308
- };
1309
- function actorFromRouting(routing) {
1310
- if (routing.dispatch?.actor) {
1311
- return routing.dispatch.actor;
1312
- }
1313
- if (routing.actor) {
1314
- return routing.actor;
1315
- }
1316
- if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
1317
- return routing.credentialContext.actor;
1318
- }
1319
- return void 0;
1320
- }
1321
- function assertRunRoutingConsistency(request) {
1322
- const { destination, source } = request.routing;
1323
- if (source.platform !== destination.platform) {
1324
- throw new TypeError("Run source and destination platforms do not match");
1325
- }
1326
- if (source.platform === "slack" && destination.platform === "slack") {
1327
- if (source.teamId !== destination.teamId) {
1328
- throw new TypeError("Slack source and destination teams do not match");
1329
- }
1330
- } else if (source.platform === "local" && destination.platform === "local" && (source.conversationId !== request.conversationId || destination.conversationId !== request.conversationId)) {
1331
- throw new TypeError(
1332
- "Local source, destination, and run conversation IDs do not match"
1333
- );
1334
- }
1335
- const actor = request.routing.dispatch?.actor ?? request.routing.actor;
1336
- if (!actor || actor.platform === "system") {
1337
- return;
1338
- }
1339
- if (actor.platform !== destination.platform) {
1340
- throw new TypeError(
1341
- `Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
1342
- );
1343
- }
1344
- if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
1345
- throw new TypeError("Slack actor team does not match destination team");
1346
- }
1347
- }
1348
- function toolInvocationDestination(routing) {
1349
- if (routing.destination.platform !== "slack" || !routing.toolChannelId) {
1350
- return routing.destination;
1351
- }
1352
- return {
1353
- platform: "slack",
1354
- teamId: routing.destination.teamId,
1355
- channelId: routing.toolChannelId
1356
- };
1357
- }
1358
- function surfaceFromRouting(routing) {
1359
- if (routing.surface) {
1360
- return routing.surface;
1361
- }
1362
- return routing.source.platform === "slack" ? "slack" : "internal";
1363
- }
1364
-
1365
1329
  // src/chat/agent/session.ts
1366
1330
  async function restoreSessionRecord(args) {
1367
1331
  const sessionRecordState = await loadTurnSessionRecord({
@@ -1387,7 +1351,7 @@ function parseMcpProviderFromToolName(toolName) {
1387
1351
 
1388
1352
  // src/chat/pi/derived-state.ts
1389
1353
  var MCP_BRIDGE_TOOLS = /* @__PURE__ */ new Set(["callMcpTool", "searchMcpTools"]);
1390
- function isRecord3(value) {
1354
+ function isRecord2(value) {
1391
1355
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1392
1356
  }
1393
1357
  function providerFromToolName(value) {
@@ -1409,7 +1373,7 @@ function addBridgeToolProvider(toolName, value, providers) {
1409
1373
  if (bridgeTool === "searchMcpTools") {
1410
1374
  for (const argsKey of ["input", "args", "arguments", "params"]) {
1411
1375
  const args = value[argsKey];
1412
- if (isRecord3(args)) {
1376
+ if (isRecord2(args)) {
1413
1377
  addString(providers, args.provider);
1414
1378
  }
1415
1379
  }
@@ -1418,7 +1382,7 @@ function addBridgeToolProvider(toolName, value, providers) {
1418
1382
  if (bridgeTool === "callMcpTool") {
1419
1383
  for (const argsKey of ["input", "args", "arguments", "params"]) {
1420
1384
  const args = value[argsKey];
1421
- if (isRecord3(args)) {
1385
+ if (isRecord2(args)) {
1422
1386
  addString(providers, providerFromToolName(args.tool_name));
1423
1387
  }
1424
1388
  }
@@ -1431,18 +1395,18 @@ function addMcpResultProvider(message, providers) {
1431
1395
  return;
1432
1396
  }
1433
1397
  if (toolName === "loadSkill") {
1434
- if (isRecord3(message.details)) {
1398
+ if (isRecord2(message.details)) {
1435
1399
  addString(providers, message.details.mcp_provider);
1436
1400
  }
1437
1401
  addString(providers, message.mcp_provider);
1438
1402
  return;
1439
1403
  }
1440
1404
  if (toolName === "searchMcpTools") {
1441
- if (isRecord3(message.details)) {
1405
+ if (isRecord2(message.details)) {
1442
1406
  addString(providers, message.details.provider);
1443
1407
  if (Array.isArray(message.details.tools)) {
1444
1408
  for (const tool of message.details.tools) {
1445
- if (isRecord3(tool)) {
1409
+ if (isRecord2(tool)) {
1446
1410
  addString(providers, providerFromToolName(tool.tool_name));
1447
1411
  }
1448
1412
  }
@@ -1454,11 +1418,11 @@ function addMcpResultProvider(message, providers) {
1454
1418
  if (toolName === "callMcpTool") {
1455
1419
  for (const argsKey of ["input", "args", "arguments", "params"]) {
1456
1420
  const args = message[argsKey];
1457
- if (isRecord3(args)) {
1421
+ if (isRecord2(args)) {
1458
1422
  addString(providers, providerFromToolName(args.tool_name));
1459
1423
  }
1460
1424
  }
1461
- if (isRecord3(message.details)) {
1425
+ if (isRecord2(message.details)) {
1462
1426
  addString(providers, message.details.provider);
1463
1427
  addString(providers, providerFromToolName(message.details.tool_name));
1464
1428
  }
@@ -1466,7 +1430,7 @@ function addMcpResultProvider(message, providers) {
1466
1430
  }
1467
1431
  }
1468
1432
  function scanMcpProviders(message, providers) {
1469
- if (!isRecord3(message)) {
1433
+ if (!isRecord2(message)) {
1470
1434
  return;
1471
1435
  }
1472
1436
  if (message.role === "toolResult") {
@@ -1478,15 +1442,15 @@ function scanMcpProviders(message, providers) {
1478
1442
  return;
1479
1443
  }
1480
1444
  for (const part of content) {
1481
- if (!isRecord3(part)) {
1445
+ if (!isRecord2(part)) {
1482
1446
  continue;
1483
1447
  }
1484
1448
  addBridgeToolProvider(getToolName(part), part, providers);
1485
1449
  }
1486
1450
  }
1487
1451
  function scanLoadedSkills(message, skills) {
1488
- if (isRecord3(message) && message.role === "toolResult" && message.toolName === "loadSkill" && message.isError !== true) {
1489
- if (isRecord3(message.details)) {
1452
+ if (isRecord2(message) && message.role === "toolResult" && message.toolName === "loadSkill" && message.isError !== true) {
1453
+ if (isRecord2(message.details)) {
1490
1454
  addString(skills, message.details.skill_name);
1491
1455
  }
1492
1456
  addString(skills, message.skill_name);
@@ -1573,424 +1537,6 @@ import { isDeepStrictEqual } from "util";
1573
1537
  // src/chat/prompt.ts
1574
1538
  import fs2 from "fs";
1575
1539
  import path2 from "path";
1576
-
1577
- // src/chat/slack/status-format.ts
1578
- var SLACK_STATUS_MAX_LENGTH = 50;
1579
- function truncateStatusText(text) {
1580
- const trimmed = text.trim();
1581
- if (!trimmed) {
1582
- return "";
1583
- }
1584
- if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
1585
- return trimmed;
1586
- }
1587
- return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
1588
- }
1589
-
1590
- // src/chat/slack/mrkdwn.ts
1591
- function escapeSlackMrkdwnText(text) {
1592
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1593
- }
1594
- function escapeSlackLinkUrl(url) {
1595
- return url.replaceAll("&", "&amp;").replaceAll("<", "%3C").replaceAll(">", "%3E");
1596
- }
1597
- function formatSlackLink(url, label) {
1598
- return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
1599
- }
1600
- function readInlineCodeSpan(line, start) {
1601
- if (line[start] !== "`") {
1602
- return void 0;
1603
- }
1604
- let n = 1;
1605
- while (line[start + n] === "`") {
1606
- n++;
1607
- }
1608
- const marker = "`".repeat(n);
1609
- let search = start + n;
1610
- while (search < line.length) {
1611
- const close = line.indexOf(marker, search);
1612
- if (close === -1) {
1613
- return void 0;
1614
- }
1615
- const after = close + n;
1616
- if (line[after] !== "`") {
1617
- return { text: line.slice(start, after), end: after };
1618
- }
1619
- search = after + 1;
1620
- }
1621
- return void 0;
1622
- }
1623
- function readExistingSlackAngleToken(line, start) {
1624
- if (line[start] !== "<") {
1625
- return void 0;
1626
- }
1627
- const close = line.indexOf(">", start + 1);
1628
- if (close === -1) {
1629
- return void 0;
1630
- }
1631
- const body = line.slice(start + 1, close);
1632
- if (/^(?:https?:\/\/|@|#|!)/.test(body)) {
1633
- return { text: line.slice(start, close + 1), end: close + 1 };
1634
- }
1635
- return void 0;
1636
- }
1637
- function readMarkdownLink(line, start) {
1638
- if (line[start] !== "[") {
1639
- return void 0;
1640
- }
1641
- const labelEnd = line.indexOf("](", start + 1);
1642
- if (labelEnd === -1) {
1643
- return void 0;
1644
- }
1645
- const destStart = labelEnd + 2;
1646
- if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) {
1647
- return void 0;
1648
- }
1649
- const closeParens = line.indexOf(")", destStart);
1650
- if (closeParens === -1) {
1651
- return void 0;
1652
- }
1653
- return { text: line.slice(start, closeParens + 1), end: closeParens + 1 };
1654
- }
1655
- function hasUnmatchedClosingParen(text) {
1656
- let balance = 0;
1657
- for (const ch of text) {
1658
- if (ch === "(") balance++;
1659
- else if (ch === ")") balance--;
1660
- }
1661
- return balance < 0;
1662
- }
1663
- function readBareUrl(line, start) {
1664
- let end = start;
1665
- while (end < line.length) {
1666
- const ch = line[end];
1667
- if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === '"' || ch === "`" || ch === "|" || ch === "*") {
1668
- break;
1669
- }
1670
- end++;
1671
- }
1672
- if (end === start) {
1673
- return void 0;
1674
- }
1675
- let raw = line.slice(start, end);
1676
- let suffix = "";
1677
- const peel = () => {
1678
- suffix = raw.slice(-1) + suffix;
1679
- raw = raw.slice(0, -1);
1680
- };
1681
- const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
1682
- while (raw.length > 0 && shouldPeel()) {
1683
- peel();
1684
- }
1685
- if (!/^https?:\/\/.+/.test(raw)) {
1686
- return void 0;
1687
- }
1688
- return { url: raw, suffix, end };
1689
- }
1690
- function wrapBareUrlsOnLine(line) {
1691
- let result = "";
1692
- let i = 0;
1693
- while (i < line.length) {
1694
- const codeSpan = readInlineCodeSpan(line, i);
1695
- if (codeSpan) {
1696
- result += codeSpan.text;
1697
- i = codeSpan.end;
1698
- continue;
1699
- }
1700
- const angleToken = readExistingSlackAngleToken(line, i);
1701
- if (angleToken) {
1702
- result += angleToken.text;
1703
- i = angleToken.end;
1704
- continue;
1705
- }
1706
- const mdLink = readMarkdownLink(line, i);
1707
- if (mdLink) {
1708
- result += mdLink.text;
1709
- i = mdLink.end;
1710
- continue;
1711
- }
1712
- if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
1713
- const parsed = readBareUrl(line, i);
1714
- if (parsed) {
1715
- result += `<${parsed.url}>${parsed.suffix}`;
1716
- i = parsed.end;
1717
- continue;
1718
- }
1719
- }
1720
- result += line[i];
1721
- i++;
1722
- }
1723
- return result;
1724
- }
1725
- function wrapBareUrls(text) {
1726
- const lines = text.split("\n");
1727
- const out = [];
1728
- let inCodeBlock = false;
1729
- for (const line of lines) {
1730
- if (line.trimStart().startsWith("```")) {
1731
- inCodeBlock = !inCodeBlock;
1732
- out.push(line);
1733
- continue;
1734
- }
1735
- out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
1736
- }
1737
- return out.join("\n");
1738
- }
1739
- function ensureBlockSpacing(text) {
1740
- const codeBlockPattern = /^```/;
1741
- const listItemPattern = /^[-*•]\s|^\d+\.\s/;
1742
- const lines = text.split("\n");
1743
- const result = [];
1744
- let inCodeBlock = false;
1745
- for (let i = 0; i < lines.length; i++) {
1746
- const line = lines[i];
1747
- const isCodeFence = codeBlockPattern.test(line.trimStart());
1748
- if (isCodeFence) {
1749
- if (!inCodeBlock) {
1750
- const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
1751
- if (prev2 !== void 0 && prev2.trim() !== "") {
1752
- result.push("");
1753
- }
1754
- }
1755
- inCodeBlock = !inCodeBlock;
1756
- result.push(line);
1757
- continue;
1758
- }
1759
- if (inCodeBlock) {
1760
- result.push(line);
1761
- continue;
1762
- }
1763
- const prev = result.length > 0 ? result[result.length - 1] : void 0;
1764
- if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
1765
- result.push("");
1766
- }
1767
- result.push(line);
1768
- }
1769
- return result.join("\n");
1770
- }
1771
- function normalizeSlackReplyMarkdown(text) {
1772
- let normalized = text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
1773
- normalized = wrapBareUrls(normalized);
1774
- normalized = ensureBlockSpacing(normalized);
1775
- return normalized.replace(/\n{3,}/g, "\n\n").trim();
1776
- }
1777
- function normalizeSlackStatusText(text) {
1778
- const trimmed = text.trim();
1779
- if (!trimmed) {
1780
- return "";
1781
- }
1782
- return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
1783
- }
1784
-
1785
- // src/chat/slack/output.ts
1786
- var MAX_INLINE_CHARS = 2200;
1787
- var MAX_INLINE_LINES = 45;
1788
- var CONTINUED_MARKER = "\n\n[Continued below]";
1789
- function countSlackLines(text) {
1790
- if (!text) {
1791
- return 0;
1792
- }
1793
- return text.split("\n").length;
1794
- }
1795
- function fitsInlineBudget(text, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
1796
- return text.length <= maxChars && countSlackLines(text) <= maxLines;
1797
- }
1798
- function findSplitIndex(text, maxChars) {
1799
- if (text.length <= maxChars) {
1800
- return text.length;
1801
- }
1802
- const bounded = text.slice(0, maxChars);
1803
- const newlineIndex = bounded.lastIndexOf("\n");
1804
- if (newlineIndex > 0) {
1805
- return newlineIndex;
1806
- }
1807
- const spaceIndex = bounded.lastIndexOf(" ");
1808
- if (spaceIndex > 0) {
1809
- return spaceIndex;
1810
- }
1811
- return maxChars;
1812
- }
1813
- function splitByLineBudget(text, maxLines) {
1814
- if (maxLines <= 0) {
1815
- return "";
1816
- }
1817
- const lines = text.split("\n");
1818
- if (lines.length <= maxLines) {
1819
- return text;
1820
- }
1821
- return lines.slice(0, maxLines).join("\n");
1822
- }
1823
- function reserveInlineBudgetForSuffix(suffix, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
1824
- return {
1825
- maxChars: Math.max(1, maxChars - suffix.length),
1826
- maxLines: Math.max(1, maxLines - Math.max(0, countSlackLines(suffix) - 1))
1827
- };
1828
- }
1829
- function forceSplitBudget(text, budget) {
1830
- const lineCount = countSlackLines(text);
1831
- return {
1832
- maxChars: text.length <= budget.maxChars ? Math.max(1, text.length - 1) : budget.maxChars,
1833
- maxLines: lineCount <= budget.maxLines ? Math.max(1, lineCount - 1) : budget.maxLines
1834
- };
1835
- }
1836
- function getFenceContinuation(text) {
1837
- let open;
1838
- for (const line of text.split("\n")) {
1839
- const trimmed = line.trimStart();
1840
- const openerMatch = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
1841
- if (!openerMatch) {
1842
- continue;
1843
- }
1844
- if (!open) {
1845
- open = {
1846
- fence: openerMatch[1],
1847
- openerLine: trimmed
1848
- };
1849
- continue;
1850
- }
1851
- if (new RegExp(`^${open.fence}\\s*$`).test(trimmed)) {
1852
- open = void 0;
1853
- }
1854
- }
1855
- if (!open) {
1856
- return null;
1857
- }
1858
- return {
1859
- closeSuffix: text.endsWith("\n") ? open.fence : `
1860
- ${open.fence}`,
1861
- reopenPrefix: `${open.openerLine}
1862
- `
1863
- };
1864
- }
1865
- function appendSlackSuffix(text, marker) {
1866
- const carryover = getFenceContinuation(text);
1867
- return `${text}${carryover?.closeSuffix ?? ""}${marker}`;
1868
- }
1869
- function stripTrailingContinuationMarker(text) {
1870
- return text.endsWith(CONTINUED_MARKER) ? text.slice(0, -CONTINUED_MARKER.length) : text;
1871
- }
1872
- function takeSlackContinuationChunk(text, budget) {
1873
- let { prefix, rest } = takeSlackInlinePrefix(text, budget);
1874
- if (!rest) {
1875
- ({ prefix, rest } = takeSlackInlinePrefix(
1876
- text,
1877
- forceSplitBudget(text, budget)
1878
- ));
1879
- }
1880
- let carryover = rest ? getFenceContinuation(prefix) : null;
1881
- if (!carryover) {
1882
- return { prefix, rest };
1883
- }
1884
- const carryoverBudget = reserveInlineBudgetForSuffix(
1885
- `${carryover.closeSuffix}${CONTINUED_MARKER}`
1886
- );
1887
- ({ prefix, rest } = takeSlackInlinePrefix(text, carryoverBudget));
1888
- if (!rest) {
1889
- ({ prefix, rest } = takeSlackInlinePrefix(
1890
- text,
1891
- forceSplitBudget(text, carryoverBudget)
1892
- ));
1893
- }
1894
- carryover = rest ? getFenceContinuation(prefix) : null;
1895
- if (!carryover) {
1896
- return { prefix, rest };
1897
- }
1898
- return {
1899
- prefix,
1900
- rest: `${carryover.reopenPrefix}${rest}`
1901
- };
1902
- }
1903
- function takeSlackContinuationPrefix(text, options) {
1904
- const budget = {
1905
- maxChars: options?.maxChars ?? getSlackContinuationBudget().maxChars,
1906
- maxLines: options?.maxLines ?? getSlackContinuationBudget().maxLines
1907
- };
1908
- const { prefix, rest } = (() => {
1909
- if (options?.forceSplit) {
1910
- return takeSlackContinuationChunk(text, budget);
1911
- }
1912
- const initial = takeSlackInlinePrefix(text, budget);
1913
- return initial.rest ? takeSlackContinuationChunk(text, budget) : initial;
1914
- })();
1915
- return {
1916
- prefix,
1917
- renderedPrefix: rest ? appendSlackSuffix(prefix, CONTINUED_MARKER) : prefix,
1918
- rest
1919
- };
1920
- }
1921
- function takeSlackInlinePrefix(text, options) {
1922
- const maxChars = options?.maxChars ?? MAX_INLINE_CHARS;
1923
- const maxLines = options?.maxLines ?? MAX_INLINE_LINES;
1924
- const normalized = text.replace(/\r\n?/g, "\n");
1925
- if (!normalized) {
1926
- return { prefix: "", rest: "" };
1927
- }
1928
- if (fitsInlineBudget(normalized, maxChars, maxLines)) {
1929
- return { prefix: normalized, rest: "" };
1930
- }
1931
- const lineBounded = splitByLineBudget(normalized, maxLines);
1932
- const cutIndex = findSplitIndex(lineBounded, maxChars);
1933
- const prefix = lineBounded.slice(0, cutIndex).trimEnd();
1934
- if (prefix) {
1935
- return {
1936
- prefix,
1937
- rest: normalized.slice(prefix.length).trimStart()
1938
- };
1939
- }
1940
- const hardPrefix = normalized.slice(0, Math.max(1, maxChars)).trimEnd();
1941
- return {
1942
- prefix: hardPrefix || normalized.slice(0, Math.max(1, maxChars)),
1943
- rest: normalized.slice(hardPrefix.length || Math.max(1, maxChars)).trimStart()
1944
- };
1945
- }
1946
- function splitSlackReplyText(text, options) {
1947
- const normalized = normalizeSlackReplyMarkdown(text);
1948
- if (!normalized) {
1949
- return [];
1950
- }
1951
- const chunks = [];
1952
- const continuationBudget = reserveInlineBudgetForSuffix(CONTINUED_MARKER);
1953
- let remaining = normalized;
1954
- while (remaining) {
1955
- const fitsFinalChunk = options?.interrupted ? fitsInlineBudget(appendSlackSuffix(remaining, getInterruptionMarker())) : fitsInlineBudget(remaining);
1956
- if (fitsFinalChunk) {
1957
- chunks.push(
1958
- options?.interrupted ? appendSlackSuffix(remaining, getInterruptionMarker()) : remaining
1959
- );
1960
- break;
1961
- }
1962
- const { renderedPrefix, rest } = takeSlackContinuationPrefix(remaining, {
1963
- ...continuationBudget,
1964
- forceSplit: true
1965
- });
1966
- chunks.push(renderedPrefix);
1967
- remaining = rest;
1968
- }
1969
- if (chunks.length === 2) {
1970
- chunks[0] = stripTrailingContinuationMarker(chunks[0] ?? "");
1971
- }
1972
- return chunks;
1973
- }
1974
- function getSlackContinuationBudget() {
1975
- return reserveInlineBudgetForSuffix(CONTINUED_MARKER);
1976
- }
1977
- function buildSlackOutputMessage(text) {
1978
- const normalized = normalizeSlackReplyMarkdown(text);
1979
- if (!normalized) {
1980
- throw new Error(
1981
- `Slack output normalized to empty content: original_length=${text.length} parsed_length=${normalized.length}`
1982
- );
1983
- }
1984
- return {
1985
- markdown: normalized
1986
- };
1987
- }
1988
- var slackOutputPolicy = {
1989
- maxInlineChars: MAX_INLINE_CHARS,
1990
- maxInlineLines: MAX_INLINE_LINES
1991
- };
1992
-
1993
- // src/chat/prompt.ts
1994
1540
  var DEFAULT_SOUL = "You are Junior, a practical and concise assistant.";
1995
1541
  function getLoggedMarkdownFiles() {
1996
1542
  const globalState = globalThis;
@@ -2824,160 +2370,6 @@ async function assemblePrompt(args) {
2824
2370
  };
2825
2371
  }
2826
2372
 
2827
- // src/chat/capabilities/catalog.ts
2828
- var cachedCatalog;
2829
- function getCapabilityCatalog() {
2830
- const signature = pluginCatalogRuntime.getSignature();
2831
- if (cachedCatalog?.signature === signature) return cachedCatalog;
2832
- const providers = pluginCatalogRuntime.getCapabilityProviders();
2833
- const capabilityToProvider = /* @__PURE__ */ new Map();
2834
- for (const provider of providers) {
2835
- for (const capability of provider.capabilities) {
2836
- if (capabilityToProvider.has(capability)) {
2837
- throw new Error(
2838
- `Duplicate capability registration for "${capability}"`
2839
- );
2840
- }
2841
- capabilityToProvider.set(capability, provider);
2842
- }
2843
- }
2844
- cachedCatalog = { signature, providers, capabilityToProvider };
2845
- return cachedCatalog;
2846
- }
2847
- var catalogLogged = false;
2848
- function logCapabilityCatalogLoadedOnce() {
2849
- if (catalogLogged) return;
2850
- catalogLogged = true;
2851
- const { providers } = getCapabilityCatalog();
2852
- const capabilityNames = providers.flatMap((p) => p.capabilities).sort();
2853
- const configKeys = [
2854
- ...new Set(providers.flatMap((p) => p.configKeys))
2855
- ].sort();
2856
- logInfo(
2857
- "capability_catalog_loaded",
2858
- {},
2859
- {
2860
- "app.capability.providers": providers.map((p) => p.provider),
2861
- "app.capability.count": capabilityNames.length,
2862
- "app.capability.names": capabilityNames,
2863
- "app.config.key_count": configKeys.length,
2864
- "app.config.keys": configKeys
2865
- },
2866
- "Loaded capability provider catalog"
2867
- );
2868
- }
2869
-
2870
- // src/chat/capabilities/router.ts
2871
- var ProviderCredentialRouter = class {
2872
- brokersByProvider;
2873
- constructor(input) {
2874
- this.brokersByProvider = input.brokersByProvider;
2875
- }
2876
- async issue(input) {
2877
- const broker = this.brokersByProvider[input.provider];
2878
- if (!broker) {
2879
- throw new Error(
2880
- `No credential broker registered for provider: ${input.provider}`
2881
- );
2882
- }
2883
- return await broker.issue({
2884
- context: input.context,
2885
- reason: input.reason
2886
- });
2887
- }
2888
- };
2889
-
2890
- // src/chat/credentials/user-token-store.ts
2891
- import {
2892
- pluginStoredTokensSchema
2893
- } from "@sentry/junior-plugin-api";
2894
- var storedTokensSchema = pluginStoredTokensSchema;
2895
-
2896
- // src/chat/credentials/state-adapter-token-store.ts
2897
- var KEY_PREFIX = "oauth-token";
2898
- var BUFFER_MS = 24 * 60 * 60 * 1e3;
2899
- var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
2900
- var REFRESH_LOCK_WAIT_MS = 3e4;
2901
- var REFRESH_LOCK_RETRY_MS = 100;
2902
- function tokenKey(userId, provider) {
2903
- return `${KEY_PREFIX}:${userId}:${provider}`;
2904
- }
2905
- function refreshLockKey(userId, provider) {
2906
- return `${tokenKey(userId, provider)}:refresh`;
2907
- }
2908
- var StateAdapterTokenStore = class {
2909
- state;
2910
- constructor(stateAdapter) {
2911
- this.state = stateAdapter;
2912
- }
2913
- async get(userId, provider) {
2914
- const stored = await this.state.get(tokenKey(userId, provider));
2915
- return stored === null || stored === void 0 ? void 0 : storedTokensSchema.parse(stored);
2916
- }
2917
- async set(userId, provider, tokens) {
2918
- const parsed = storedTokensSchema.parse(tokens);
2919
- const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
2920
- const ttlMs2 = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
2921
- await this.state.set(tokenKey(userId, provider), parsed, ttlMs2);
2922
- }
2923
- async delete(userId, provider) {
2924
- await this.state.delete(tokenKey(userId, provider));
2925
- }
2926
- /** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
2927
- async withRefresh(userId, provider, callback) {
2928
- const lockKey = refreshLockKey(userId, provider);
2929
- const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
2930
- while (true) {
2931
- const lock = await acquireActiveLock(this.state, lockKey);
2932
- if (lock) {
2933
- try {
2934
- return await callback();
2935
- } finally {
2936
- await this.state.releaseLock(lock);
2937
- }
2938
- }
2939
- if (Date.now() >= deadline) {
2940
- throw new Error(`Could not acquire OAuth token refresh lock`);
2941
- }
2942
- await sleep(REFRESH_LOCK_RETRY_MS);
2943
- }
2944
- }
2945
- };
2946
-
2947
- // src/chat/capabilities/factory.ts
2948
- var sandboxEgressRouters = /* @__PURE__ */ new WeakMap();
2949
- function createUserTokenStore() {
2950
- return new StateAdapterTokenStore(getStateAdapter());
2951
- }
2952
- function createProviderCredentialRouter(userTokenStore) {
2953
- logCapabilityCatalogLoadedOnce();
2954
- const brokersByProvider = {};
2955
- for (const plugin of pluginCatalogRuntime.getProviders()) {
2956
- const { name } = plugin.manifest;
2957
- if (!plugin.manifest.credentials && !plugin.manifest.apiHeaders) {
2958
- continue;
2959
- }
2960
- brokersByProvider[name] = pluginCatalogRuntime.createBroker(name, {
2961
- userTokenStore
2962
- });
2963
- }
2964
- return new ProviderCredentialRouter({ brokersByProvider });
2965
- }
2966
- function getSandboxEgressRouter() {
2967
- const stateAdapter = getStateAdapter();
2968
- let router = sandboxEgressRouters.get(stateAdapter);
2969
- if (!router) {
2970
- router = createProviderCredentialRouter(
2971
- new StateAdapterTokenStore(stateAdapter)
2972
- );
2973
- sandboxEgressRouters.set(stateAdapter, router);
2974
- }
2975
- return router;
2976
- }
2977
- async function issueProviderCredentialLease(input) {
2978
- return await getSandboxEgressRouter().issue(input);
2979
- }
2980
-
2981
2373
  // src/chat/capabilities/jr-rpc-command.ts
2982
2374
  import { Bash, defineCommand } from "just-bash";
2983
2375
  function commandResult(input) {
@@ -3248,631 +2640,96 @@ function createJrRpcCommand(deps) {
3248
2640
  "jr-rpc config get <key>",
3249
2641
  "jr-rpc config set <key> <value> [--json]",
3250
2642
  "jr-rpc config unset <key>",
3251
- "jr-rpc config list [--prefix <value>]",
3252
- "jr-rpc plugins list"
3253
- ].join("\n");
3254
- const verb = (args[0] ?? "").trim();
3255
- if (verb === "config") {
3256
- return handleConfigCommand(args.slice(1), deps);
3257
- }
3258
- if (verb === "plugins") {
3259
- return handlePluginsCommand(args.slice(1));
3260
- }
3261
- return commandResult({
3262
- stderr: `Unsupported jr-rpc command. Use:
3263
- ${usage}
3264
- `,
3265
- exitCode: 2
3266
- });
3267
- });
3268
- }
3269
- async function maybeExecuteJrRpcCustomCommand(command, deps) {
3270
- const normalized = command.trim();
3271
- if (!/^jr-rpc(?:\s|$)/.test(normalized)) {
3272
- return { handled: false };
3273
- }
3274
- const shell = new Bash({
3275
- customCommands: [createJrRpcCommand(deps)]
3276
- });
3277
- const execResult = await shell.exec(normalized);
3278
- return {
3279
- handled: true,
3280
- result: {
3281
- ok: execResult.exitCode === 0,
3282
- command: normalized,
3283
- cwd: "/",
3284
- exit_code: execResult.exitCode,
3285
- signal: null,
3286
- timed_out: false,
3287
- stdout: execResult.stdout,
3288
- stderr: execResult.stderr,
3289
- stdout_truncated: false,
3290
- stderr_truncated: false
3291
- }
3292
- };
3293
- }
3294
-
3295
- // src/chat/sandbox/command-result.ts
3296
- function formatSandboxCommandResult(params) {
3297
- return makeStructuredToolResult({
3298
- ok: params.ok,
3299
- status: params.ok ? "success" : "error",
3300
- target: params.command,
3301
- data: {
3302
- command: params.command,
3303
- cwd: params.cwd,
3304
- exit_code: params.exit_code,
3305
- signal: params.signal,
3306
- timed_out: params.timed_out,
3307
- aborted: Boolean(params.aborted),
3308
- stdout: params.stdout,
3309
- stderr: params.stderr,
3310
- stdout_truncated: params.stdout_truncated,
3311
- stderr_truncated: params.stderr_truncated
3312
- },
3313
- truncated: params.stdout_truncated || params.stderr_truncated,
3314
- ...!params.ok ? {
3315
- error: {
3316
- kind: params.aborted ? "outcome_unknown" : params.timed_out ? "timeout" : "nonzero_exit",
3317
- message: params.aborted ? "Command was interrupted before its outcome was confirmed. It may have produced side effects; reconcile external state before retrying or reporting failure." : params.stderr.trim() || `Command exited with code ${params.exit_code}`,
3318
- retryable: params.timed_out
3319
- }
3320
- } : {},
3321
- command: params.command,
3322
- cwd: params.cwd,
3323
- exit_code: params.exit_code,
3324
- signal: params.signal,
3325
- timed_out: params.timed_out,
3326
- aborted: Boolean(params.aborted),
3327
- stdout: params.stdout,
3328
- stderr: params.stderr,
3329
- stdout_truncated: params.stdout_truncated,
3330
- stderr_truncated: params.stderr_truncated,
3331
- ...params.auth_required ? { auth_required: params.auth_required } : {},
3332
- ...params.permission_denied ? { permission_denied: params.permission_denied } : {}
3333
- });
3334
- }
3335
-
3336
- // src/chat/sandbox/sandbox.ts
3337
- import fs4 from "fs/promises";
3338
-
3339
- // src/chat/oauth-flow.ts
3340
- import { randomBytes } from "crypto";
3341
- import {
3342
- sourceSchema
3343
- } from "@sentry/junior-plugin-api";
3344
-
3345
- // src/chat/slack/outbound.ts
3346
- var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
3347
- function requireSlackConversationId(channelId, action) {
3348
- const normalized = normalizeSlackConversationId(channelId);
3349
- if (!normalized) {
3350
- throw new Error(`${action} requires a valid channel ID`);
3351
- }
3352
- return normalized;
3353
- }
3354
- function requireSlackThreadTimestamp(threadTs, action) {
3355
- const normalized = threadTs.trim();
3356
- if (!normalized) {
3357
- throw new Error(`${action} requires a thread timestamp`);
3358
- }
3359
- return normalized;
3360
- }
3361
- function requireSlackMessageTimestamp(timestamp, action) {
3362
- const normalized = parseSlackMessageTs(timestamp);
3363
- if (!normalized) {
3364
- throw new Error(`${action} requires a target message timestamp`);
3365
- }
3366
- return normalized;
3367
- }
3368
- function requireSlackMessageText(text, action) {
3369
- if (text.trim().length === 0) {
3370
- throw new Error(`${action} requires non-empty text`);
3371
- }
3372
- if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) {
3373
- throw new Error(
3374
- `${action} text exceeds Slack's 40000 character truncation limit`
3375
- );
3376
- }
3377
- return text;
3378
- }
3379
- async function getSlackMessagePermalink(args) {
3380
- try {
3381
- const response = await withSlackRetries(
3382
- () => getSlackClient().chat.getPermalink({
3383
- channel: args.channelId,
3384
- message_ts: args.messageTs
3385
- }),
3386
- 3,
3387
- {
3388
- action: "chat.getPermalink",
3389
- idempotent: true,
3390
- spanAttributes: {
3391
- "app.slack.channel_id": args.channelId,
3392
- "app.slack.message_ts": args.messageTs
3393
- }
3394
- }
3395
- );
3396
- return response.permalink;
3397
- } catch {
3398
- return void 0;
3399
- }
3400
- }
3401
- async function postSlackMessage(input) {
3402
- const channelId = requireSlackConversationId(
3403
- input.channelId,
3404
- "Slack message posting"
3405
- );
3406
- const text = requireSlackMessageText(input.text, "Slack message posting");
3407
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(
3408
- input.threadTs,
3409
- "Slack thread message posting"
3410
- ) : void 0;
3411
- const response = await withSlackRetries(
3412
- () => getSlackClient().chat.postMessage({
3413
- channel: channelId,
3414
- text,
3415
- ...input.blocks?.length ? {
3416
- blocks: input.blocks
3417
- } : {},
3418
- ...threadTs ? { thread_ts: threadTs } : {}
3419
- }),
3420
- 3,
3421
- {
3422
- action: "chat.postMessage",
3423
- spanAttributes: {
3424
- "app.slack.channel_id": channelId,
3425
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
3426
- }
3427
- }
3428
- );
3429
- const messageTs = parseSlackMessageTs(response.ts);
3430
- if (!messageTs) {
3431
- throw new Error("Slack message posted without ts");
3432
- }
3433
- return {
3434
- ts: messageTs,
3435
- ...input.includePermalink ? {
3436
- permalink: await getSlackMessagePermalink({
3437
- channelId,
3438
- messageTs
3439
- })
3440
- } : {}
3441
- };
3442
- }
3443
- async function postSlackEphemeralMessage(input) {
3444
- const channelId = requireSlackConversationId(
3445
- input.channelId,
3446
- "Slack ephemeral message posting"
3447
- );
3448
- const userId = parseSlackUserId(input.userId);
3449
- if (!userId) {
3450
- throw new Error("Slack ephemeral message posting requires a user ID");
3451
- }
3452
- const text = requireSlackMessageText(
3453
- input.text,
3454
- "Slack ephemeral message posting"
3455
- );
3456
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(
3457
- input.threadTs,
3458
- "Slack ephemeral thread message posting"
3459
- ) : void 0;
3460
- const response = await withSlackRetries(
3461
- () => getSlackClient().chat.postEphemeral({
3462
- channel: channelId,
3463
- user: userId,
3464
- text,
3465
- ...threadTs ? { thread_ts: threadTs } : {}
3466
- }),
3467
- 3,
3468
- {
3469
- action: "chat.postEphemeral",
3470
- spanAttributes: {
3471
- "app.slack.channel_id": channelId,
3472
- "app.slack.user_id": userId,
3473
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
3474
- }
3475
- }
3476
- );
3477
- return {
3478
- messageTs: response.message_ts
3479
- };
3480
- }
3481
- async function uploadFilesToConversation(input) {
3482
- const channelId = requireSlackConversationId(
3483
- input.channelId,
3484
- "Slack file upload"
3485
- );
3486
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
3487
- if (input.files.length === 0) {
3488
- throw new Error("Slack file upload requires at least one file");
3489
- }
3490
- const fileUploads = input.files.map((file) => {
3491
- const filename = file.filename.trim();
3492
- if (!filename) {
3493
- throw new Error(
3494
- "Slack file upload requires every file to have a filename"
3495
- );
3496
- }
3497
- return {
3498
- file: file.data,
3499
- filename
3500
- };
3501
- });
3502
- const response = await withSlackRetries(
3503
- () => getSlackClient().filesUploadV2({
3504
- channel_id: channelId,
3505
- ...threadTs ? { thread_ts: threadTs } : {},
3506
- file_uploads: fileUploads
3507
- }),
3508
- 3,
3509
- {
3510
- action: "filesUploadV2",
3511
- spanAttributes: {
3512
- "app.slack.channel_id": channelId,
3513
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
3514
- }
3515
- }
3516
- );
3517
- return {
3518
- files: (response.files ?? []).flatMap(
3519
- (completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})
3520
- )
3521
- };
3522
- }
3523
- async function addReactionToMessage(input) {
3524
- const channelId = requireSlackConversationId(
3525
- input.channelId,
3526
- "Slack reaction"
3527
- );
3528
- const timestamp = requireSlackMessageTimestamp(
3529
- input.timestamp,
3530
- "Slack reaction"
3531
- );
3532
- const emoji = normalizeSlackEmojiName(input.emoji);
3533
- if (!emoji) {
3534
- throw new Error("Slack reaction requires a valid emoji alias name");
3535
- }
3536
- try {
3537
- await withSlackRetries(
3538
- () => getSlackClient().reactions.add({
3539
- channel: channelId,
3540
- timestamp,
3541
- name: emoji
3542
- }),
3543
- 3,
3544
- {
3545
- action: "reactions.add",
3546
- idempotent: true,
3547
- spanAttributes: {
3548
- "app.slack.channel_id": channelId,
3549
- "app.slack.message_ts": timestamp,
3550
- "app.slack.reaction": emoji
3551
- }
3552
- }
3553
- );
3554
- } catch (error) {
3555
- if (error instanceof SlackActionError && error.code === "already_reacted") {
3556
- return { ok: true };
3557
- }
3558
- throw error;
3559
- }
3560
- return { ok: true };
3561
- }
3562
- async function removeReactionFromMessage(input) {
3563
- const channelId = requireSlackConversationId(
3564
- input.channelId,
3565
- "Slack reaction removal"
3566
- );
3567
- const timestamp = requireSlackMessageTimestamp(
3568
- input.timestamp,
3569
- "Slack reaction removal"
3570
- );
3571
- const emoji = normalizeSlackEmojiName(input.emoji);
3572
- if (!emoji) {
3573
- throw new Error("Slack reaction removal requires a valid emoji alias name");
3574
- }
3575
- try {
3576
- await withSlackRetries(
3577
- () => getSlackClient().reactions.remove({
3578
- channel: channelId,
3579
- timestamp,
3580
- name: emoji
3581
- }),
3582
- 3,
3583
- {
3584
- action: "reactions.remove",
3585
- idempotent: true,
3586
- spanAttributes: {
3587
- "app.slack.channel_id": channelId,
3588
- "app.slack.message_ts": timestamp,
3589
- "app.slack.reaction": emoji
3590
- }
3591
- }
3592
- );
3593
- } catch (error) {
3594
- if (error instanceof SlackActionError && error.code === "no_reaction") {
3595
- return { ok: true };
3596
- }
3597
- throw error;
3598
- }
3599
- return { ok: true };
3600
- }
3601
-
3602
- // src/chat/oauth-authorization-message.ts
3603
- function formatOAuthAuthorizationMessage(args) {
3604
- return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
3605
- }
3606
-
3607
- // src/chat/oauth-flow.ts
3608
- var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
3609
- function optionalString(value) {
3610
- return typeof value === "string" ? value : void 0;
3611
- }
3612
- function parseOAuthStatePayload(value) {
3613
- if (!isRecord(value)) {
3614
- return void 0;
3615
- }
3616
- if (typeof value.userId !== "string" || typeof value.provider !== "string") {
3617
- return void 0;
3618
- }
3619
- const destination = parseDestination(value.destination);
3620
- if (value.destination !== void 0 && !destination) {
3621
- return void 0;
3622
- }
3623
- const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
3624
- if (value.source !== void 0 && (!source || !source.success)) {
3625
- return void 0;
3626
- }
3627
- return {
3628
- userId: value.userId,
3629
- provider: value.provider,
3630
- ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
3631
- ...destination ? { destination } : {},
3632
- ...source?.success ? { source: source.data } : {},
3633
- ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
3634
- ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
3635
- ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
3636
- ...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
3637
- };
3638
- }
3639
- function formatProviderLabel(provider) {
3640
- const displayName = pluginCatalogRuntime.getDisplayName(provider);
3641
- if (!displayName) {
3642
- throw new Error(`Unknown plugin provider display name: "${provider}"`);
3643
- }
3644
- return displayName;
3645
- }
3646
- function resolveBaseUrl() {
3647
- const explicit = process.env.JUNIOR_BASE_URL?.trim();
3648
- if (explicit) return explicit;
3649
- const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
3650
- if (vercelProd) return `https://${vercelProd}`;
3651
- const vercelUrl = process.env.VERCEL_URL?.trim();
3652
- if (vercelUrl) return `https://${vercelUrl}`;
3653
- return void 0;
3654
- }
3655
- async function deliverPrivateMessage(input) {
3656
- let client;
3657
- try {
3658
- client = getSlackClient();
3659
- } catch {
3660
- logWarn(
3661
- "oauth_private_delivery_skip",
3662
- {},
3663
- { "app.reason": "missing_bot_token" },
3664
- "Skipped private message delivery \u2014 no SLACK_BOT_TOKEN"
3665
- );
3666
- return false;
3667
- }
3668
- if (input.channelId) {
3669
- try {
3670
- if (isDmChannel(input.channelId)) {
3671
- await postSlackMessage({
3672
- channelId: input.channelId,
3673
- text: input.text,
3674
- threadTs: input.threadTs
3675
- });
3676
- } else {
3677
- await postSlackEphemeralMessage({
3678
- channelId: input.channelId,
3679
- userId: input.userId,
3680
- text: input.text,
3681
- threadTs: input.threadTs
3682
- });
3683
- }
3684
- return "in_context";
3685
- } catch (error) {
3686
- logWarn(
3687
- "oauth_private_delivery_failed",
3688
- {},
3689
- {
3690
- "app.slack.error": error instanceof Error ? error.message : String(error),
3691
- "app.slack.channel": input.channelId
3692
- },
3693
- "Private message delivery failed, falling back to DM"
3694
- );
3695
- }
3696
- }
3697
- try {
3698
- const dmChannelId = (await withSlackRetries(
3699
- () => client.conversations.open({ users: input.userId }),
3700
- 3,
3701
- {
3702
- action: "conversations.open",
3703
- spanAttributes: { "app.slack.user_id": input.userId }
3704
- }
3705
- )).channel?.id;
3706
- if (!dmChannelId) {
3707
- logWarn(
3708
- "oauth_dm_fallback_failed",
3709
- {},
3710
- { "app.reason": "no_dm_channel_id" },
3711
- "conversations.open returned no channel ID"
3712
- );
3713
- return false;
3714
- }
3715
- await postSlackMessage({ channelId: dmChannelId, text: input.text });
3716
- return "fallback_dm";
3717
- } catch (error) {
3718
- logWarn(
3719
- "oauth_dm_fallback_failed",
3720
- {},
3721
- {
3722
- "app.slack.error": error instanceof Error ? error.message : String(error)
3723
- },
3724
- "DM fallback delivery failed"
3725
- );
3726
- return false;
3727
- }
3728
- }
3729
- async function startOAuthFlow(provider, input) {
3730
- const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
3731
- if (!providerConfig) {
3732
- return {
3733
- ok: false,
3734
- error: `Provider "${provider}" does not support OAuth authorization`
3735
- };
3736
- }
3737
- const clientId = process.env[providerConfig.clientIdEnv]?.trim();
3738
- if (!clientId) {
3739
- return {
3740
- ok: false,
3741
- error: `Missing ${providerConfig.clientIdEnv} environment variable`
3742
- };
3743
- }
3744
- const baseUrl = resolveBaseUrl();
3745
- if (!baseUrl) {
3746
- return {
3747
- ok: false,
3748
- error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
3749
- };
3750
- }
3751
- const state = randomBytes(32).toString("hex");
3752
- const requestedScope = input.scope ?? providerConfig.scope;
3753
- await getStateAdapter().set(
3754
- `oauth-state:${state}`,
3755
- {
3756
- userId: input.actorId,
3757
- provider,
3758
- ...input.channelId ? { channelId: input.channelId } : {},
3759
- ...input.destination ? { destination: input.destination } : {},
3760
- ...input.source ? { source: input.source } : {},
3761
- ...input.threadTs ? { threadTs: input.threadTs } : {},
3762
- ...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : {},
3763
- ...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
3764
- ...requestedScope ? { scope: requestedScope } : {}
3765
- },
3766
- OAUTH_STATE_TTL_MS
3767
- );
3768
- const authorizeParams = new URLSearchParams({
3769
- client_id: clientId,
3770
- state,
3771
- redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
3772
- response_type: "code"
2643
+ "jr-rpc config list [--prefix <value>]",
2644
+ "jr-rpc plugins list"
2645
+ ].join("\n");
2646
+ const verb = (args[0] ?? "").trim();
2647
+ if (verb === "config") {
2648
+ return handleConfigCommand(args.slice(1), deps);
2649
+ }
2650
+ if (verb === "plugins") {
2651
+ return handlePluginsCommand(args.slice(1));
2652
+ }
2653
+ return commandResult({
2654
+ stderr: `Unsupported jr-rpc command. Use:
2655
+ ${usage}
2656
+ `,
2657
+ exitCode: 2
2658
+ });
3773
2659
  });
3774
- if (requestedScope) {
3775
- authorizeParams.set("scope", requestedScope);
3776
- }
3777
- for (const [key, value] of Object.entries(
3778
- providerConfig.authorizeParams ?? {}
3779
- )) {
3780
- authorizeParams.set(key, value);
2660
+ }
2661
+ async function maybeExecuteJrRpcCustomCommand(command, deps) {
2662
+ const normalized = command.trim();
2663
+ if (!/^jr-rpc(?:\s|$)/.test(normalized)) {
2664
+ return { handled: false };
3781
2665
  }
3782
- logInfo(
3783
- "jr_rpc_oauth_start",
3784
- {},
3785
- {
3786
- "app.credential.provider": provider,
3787
- ...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : {}
3788
- },
3789
- "Initiated OAuth authorization code flow"
3790
- );
3791
- const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
2666
+ const shell = new Bash({
2667
+ customCommands: [createJrRpcCommand(deps)]
2668
+ });
2669
+ const execResult = await shell.exec(normalized);
3792
2670
  return {
3793
- ok: true,
3794
- delivery: await deliverPrivateMessage({
3795
- channelId: input.channelId,
3796
- threadTs: input.threadTs,
3797
- userId: input.actorId,
3798
- text: formatOAuthAuthorizationMessage({
3799
- authorizationUrl,
3800
- label: `Click here to link your ${formatProviderLabel(provider)} account`,
3801
- completionText: "Once you've authorized, you'll see a confirmation in Slack."
3802
- })
3803
- })
2671
+ handled: true,
2672
+ result: {
2673
+ ok: execResult.exitCode === 0,
2674
+ command: normalized,
2675
+ cwd: "/",
2676
+ exit_code: execResult.exitCode,
2677
+ signal: null,
2678
+ timed_out: false,
2679
+ stdout: execResult.stdout,
2680
+ stderr: execResult.stderr,
2681
+ stdout_truncated: false,
2682
+ stderr_truncated: false
2683
+ }
3804
2684
  };
3805
2685
  }
3806
2686
 
3807
- // src/chat/sandbox/egress/session.ts
3808
- import { createHmac, randomUUID, timingSafeEqual } from "crypto";
3809
-
3810
- // src/chat/sandbox/egress/schemas.ts
3811
- import { z as z3 } from "zod";
3812
- import {
3813
- pluginAuthorizationSchema,
3814
- pluginCredentialHeaderTransformSchema,
3815
- pluginGrantSchema,
3816
- pluginProviderAccountSchema
3817
- } from "@sentry/junior-plugin-api";
3818
- var finiteNumberSchema = z3.number().refine(Number.isFinite);
3819
- var httpStatusSchema = z3.number().int().min(100).max(599);
3820
- var providerNameSchema = z3.string().regex(/^[a-z][a-z0-9-]*$/);
3821
- var credentialSignalKindSchema = z3.enum(["auth_required", "unavailable"]);
3822
- var sandboxEgressGrantSchema = pluginGrantSchema;
3823
- var sandboxEgressCredentialContextSchema = z3.object({
3824
- credentials: credentialContextSchema,
3825
- egressId: z3.string().min(1),
3826
- expiresAtMs: finiteNumberSchema,
3827
- contextId: z3.string().min(1)
3828
- }).strict();
3829
- var sandboxEgressCredentialLeaseSchema = z3.object({
3830
- account: pluginProviderAccountSchema.optional(),
3831
- authorization: pluginAuthorizationSchema.optional(),
3832
- grant: sandboxEgressGrantSchema,
3833
- provider: providerNameSchema,
3834
- expiresAt: z3.string().min(1),
3835
- headerTransforms: z3.array(pluginCredentialHeaderTransformSchema).min(1)
3836
- }).strict();
3837
- var sandboxEgressAuthRequiredSignalSchema = z3.object({
3838
- authorization: pluginAuthorizationSchema.optional(),
3839
- grant: sandboxEgressGrantSchema,
3840
- kind: credentialSignalKindSchema.default("auth_required"),
3841
- provider: providerNameSchema,
3842
- message: z3.string().optional(),
3843
- createdAtMs: finiteNumberSchema
3844
- }).strict().superRefine((signal, ctx) => {
3845
- if (signal.authorization && signal.authorization.provider !== signal.provider) {
3846
- ctx.addIssue({
3847
- code: z3.ZodIssueCode.custom,
3848
- message: "Auth signal authorization provider must match provider",
3849
- path: ["authorization", "provider"]
3850
- });
3851
- }
3852
- });
3853
- var sandboxEgressPermissionDeniedSignalSchema = z3.object({
3854
- account: pluginProviderAccountSchema.optional(),
3855
- acceptedPermissions: z3.string().optional(),
3856
- grant: sandboxEgressGrantSchema,
3857
- message: z3.string().min(1),
3858
- provider: providerNameSchema,
3859
- source: z3.literal("upstream"),
3860
- sso: z3.string().optional(),
3861
- status: httpStatusSchema,
3862
- upstreamHost: z3.string().min(1),
3863
- upstreamPath: z3.string().min(1),
3864
- createdAtMs: finiteNumberSchema
3865
- }).strict();
3866
- function parseSandboxEgressAuthRequiredSignal(value) {
3867
- const result = sandboxEgressAuthRequiredSignalSchema.safeParse(value);
3868
- return result.success ? result.data : void 0;
3869
- }
3870
- function parseSandboxEgressPermissionDeniedSignal(value) {
3871
- const result = sandboxEgressPermissionDeniedSignalSchema.safeParse(value);
3872
- return result.success ? result.data : void 0;
2687
+ // src/chat/sandbox/command-result.ts
2688
+ function formatSandboxCommandResult(params) {
2689
+ return makeStructuredToolResult({
2690
+ ok: params.ok,
2691
+ status: params.ok ? "success" : "error",
2692
+ target: params.command,
2693
+ data: {
2694
+ command: params.command,
2695
+ cwd: params.cwd,
2696
+ exit_code: params.exit_code,
2697
+ signal: params.signal,
2698
+ timed_out: params.timed_out,
2699
+ aborted: Boolean(params.aborted),
2700
+ stdout: params.stdout,
2701
+ stderr: params.stderr,
2702
+ stdout_truncated: params.stdout_truncated,
2703
+ stderr_truncated: params.stderr_truncated
2704
+ },
2705
+ truncated: params.stdout_truncated || params.stderr_truncated,
2706
+ ...!params.ok ? {
2707
+ error: {
2708
+ kind: params.aborted ? "outcome_unknown" : params.timed_out ? "timeout" : "nonzero_exit",
2709
+ message: params.aborted ? "Command was interrupted before its outcome was confirmed. It may have produced side effects; reconcile external state before retrying or reporting failure." : params.stderr.trim() || `Command exited with code ${params.exit_code}`,
2710
+ retryable: params.timed_out
2711
+ }
2712
+ } : {},
2713
+ command: params.command,
2714
+ cwd: params.cwd,
2715
+ exit_code: params.exit_code,
2716
+ signal: params.signal,
2717
+ timed_out: params.timed_out,
2718
+ aborted: Boolean(params.aborted),
2719
+ stdout: params.stdout,
2720
+ stderr: params.stderr,
2721
+ stdout_truncated: params.stdout_truncated,
2722
+ stderr_truncated: params.stderr_truncated,
2723
+ ...params.auth_required ? { auth_required: params.auth_required } : {},
2724
+ ...params.permission_denied ? { permission_denied: params.permission_denied } : {}
2725
+ });
3873
2726
  }
3874
2727
 
2728
+ // src/chat/sandbox/sandbox.ts
2729
+ import fs4 from "fs/promises";
2730
+
3875
2731
  // src/chat/sandbox/egress/session.ts
2732
+ import { createHmac, randomUUID, timingSafeEqual } from "crypto";
3876
2733
  var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
3877
2734
  var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
3878
2735
  var SANDBOX_EGRESS_HMAC_CONTEXT = "junior.sandbox_egress.v1";
@@ -5321,10 +4178,10 @@ function validateAndApplyTextEdits(content, edits, targetName) {
5321
4178
  }
5322
4179
 
5323
4180
  // src/chat/tools/sandbox/edit-file.ts
5324
- import { z as z5 } from "zod";
4181
+ import { z as z4 } from "zod";
5325
4182
 
5326
4183
  // src/chat/tool-support/zod-tool.ts
5327
- import { z as z4 } from "zod";
4184
+ import { z as z3 } from "zod";
5328
4185
  function isContentOnlyToolResult(value) {
5329
4186
  return value !== null && typeof value === "object" && Array.isArray(value.content) && !("details" in value);
5330
4187
  }
@@ -5348,7 +4205,7 @@ function zodTool(definition) {
5348
4205
  const { inputSchema, outputSchema, prepareArguments, execute, ...toolDef } = definition;
5349
4206
  let modelInputSchema;
5350
4207
  try {
5351
- modelInputSchema = z4.toJSONSchema(inputSchema);
4208
+ modelInputSchema = z3.toJSONSchema(inputSchema);
5352
4209
  } catch (error) {
5353
4210
  throw new TypeError(
5354
4211
  "zodTool() inputSchema must be representable as JSON Schema.",
@@ -5358,7 +4215,7 @@ function zodTool(definition) {
5358
4215
  let modelOutputSchema;
5359
4216
  if (outputSchema) {
5360
4217
  try {
5361
- modelOutputSchema = z4.toJSONSchema(outputSchema);
4218
+ modelOutputSchema = z3.toJSONSchema(outputSchema);
5362
4219
  } catch (error) {
5363
4220
  throw new TypeError(
5364
4221
  "zodTool() outputSchema must be representable as JSON Schema.",
@@ -5494,20 +4351,20 @@ async function editFile(params) {
5494
4351
  truncated: diff.truncated
5495
4352
  });
5496
4353
  }
5497
- var editReplacementSchema = z5.object({
5498
- oldText: z5.string().min(1).describe(
4354
+ var editReplacementSchema = z4.object({
4355
+ oldText: z4.string().min(1).describe(
5499
4356
  "Exact text to replace. It must be unique in the original file and must not overlap another edit."
5500
4357
  ),
5501
- newText: z5.string().describe("Replacement text for this edit.")
4358
+ newText: z4.string().describe("Replacement text for this edit.")
5502
4359
  });
5503
4360
  function createEditFileTool() {
5504
4361
  return zodTool({
5505
4362
  description: "Edit one sandbox workspace file with exact text replacements. Use for precise changes to existing files; prefer this over writeFile for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same file: use one edits[] call.",
5506
4363
  prepareArguments: prepareEditFileArguments,
5507
4364
  executionMode: "sequential",
5508
- inputSchema: z5.object({
5509
- path: z5.string().min(1).describe("Path to edit in the sandbox workspace."),
5510
- edits: z5.array(editReplacementSchema).min(1).describe(
4365
+ inputSchema: z4.object({
4366
+ path: z4.string().min(1).describe("Path to edit in the sandbox workspace."),
4367
+ edits: z4.array(editReplacementSchema).min(1).describe(
5511
4368
  "Exact replacements matched against the original file, not incrementally."
5512
4369
  )
5513
4370
  }),
@@ -5522,7 +4379,7 @@ function createEditFileTool() {
5522
4379
 
5523
4380
  // src/chat/tools/sandbox/find-files.ts
5524
4381
  import path5 from "path";
5525
- import { z as z6 } from "zod";
4382
+ import { z as z5 } from "zod";
5526
4383
  var DEFAULT_FIND_LIMIT = 1e3;
5527
4384
  async function findFiles(params) {
5528
4385
  if (!params.pattern.trim()) {
@@ -5582,14 +4439,14 @@ function createFindFilesTool() {
5582
4439
  return zodTool({
5583
4440
  description: "Find sandbox workspace files by glob pattern. Returns bounded paths relative to the search root and skips dependency/cache directories.",
5584
4441
  annotations: { readOnlyHint: true, destructiveHint: false },
5585
- inputSchema: z6.object({
5586
- pattern: z6.string().min(1).describe(
4442
+ inputSchema: z5.object({
4443
+ pattern: z5.string().min(1).describe(
5587
4444
  "Glob pattern to match, for example '*.ts', '**/*.json', or 'src/**/*.test.ts'."
5588
4445
  ),
5589
- path: z6.string().min(1).describe(
4446
+ path: z5.string().min(1).describe(
5590
4447
  "Directory or file path in the sandbox workspace. Defaults to the workspace root."
5591
4448
  ).optional(),
5592
- limit: z6.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
4449
+ limit: z5.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
5593
4450
  }),
5594
4451
  outputSchema: juniorToolResultSchema,
5595
4452
  execute: async () => {
@@ -5602,12 +4459,12 @@ function createFindFilesTool() {
5602
4459
 
5603
4460
  // src/chat/tools/sandbox/grep.ts
5604
4461
  import path6 from "path";
5605
- import { z as z7 } from "zod";
4462
+ import { z as z6 } from "zod";
5606
4463
  var DEFAULT_GREP_LIMIT = 100;
5607
4464
  var MAX_GREP_LINE_CHARS = 500;
5608
- var booleanInput = (description) => z7.preprocess(
4465
+ var booleanInput = (description) => z6.preprocess(
5609
4466
  (value) => value === "true" ? true : value === "false" ? false : value,
5610
- z7.boolean()
4467
+ z6.boolean()
5611
4468
  ).describe(description);
5612
4469
  function truncateGrepLine(value) {
5613
4470
  if (value.length <= MAX_GREP_LINE_CHARS) {
@@ -5756,20 +4613,20 @@ function createGrepTool() {
5756
4613
  return zodTool({
5757
4614
  description: "Search sandbox workspace file contents. Returns bounded matching lines with file paths and line numbers. Respects path/glob filters and includes truncation notices.",
5758
4615
  annotations: { readOnlyHint: true, destructiveHint: false },
5759
- inputSchema: z7.object({
5760
- pattern: z7.string().min(1).describe("Regex pattern or literal text to search for."),
5761
- path: z7.string().min(1).describe(
4616
+ inputSchema: z6.object({
4617
+ pattern: z6.string().min(1).describe("Regex pattern or literal text to search for."),
4618
+ path: z6.string().min(1).describe(
5762
4619
  "Directory or file path in the sandbox workspace. Defaults to the workspace root."
5763
4620
  ).optional(),
5764
- glob: z7.string().min(1).describe("Optional glob filter such as '*.ts' or '**/*.test.ts'.").optional(),
4621
+ glob: z6.string().min(1).describe("Optional glob filter such as '*.ts' or '**/*.test.ts'.").optional(),
5765
4622
  ignoreCase: booleanInput(
5766
4623
  "Whether matching should ignore case."
5767
4624
  ).optional(),
5768
4625
  literal: booleanInput(
5769
4626
  "Treat pattern as literal text instead of regex."
5770
4627
  ).optional(),
5771
- context: z7.coerce.number().int().min(0).describe("Number of surrounding context lines to include.").optional(),
5772
- limit: z7.coerce.number().int().min(1).describe("Maximum matches to return. Defaults to 100.").optional()
4628
+ context: z6.coerce.number().int().min(0).describe("Number of surrounding context lines to include.").optional(),
4629
+ limit: z6.coerce.number().int().min(1).describe("Maximum matches to return. Defaults to 100.").optional()
5773
4630
  }),
5774
4631
  outputSchema: juniorToolResultSchema,
5775
4632
  execute: async () => {
@@ -5780,7 +4637,7 @@ function createGrepTool() {
5780
4637
 
5781
4638
  // src/chat/tools/sandbox/list-dir.ts
5782
4639
  import path7 from "path";
5783
- import { z as z8 } from "zod";
4640
+ import { z as z7 } from "zod";
5784
4641
  var DEFAULT_LIST_LIMIT = 500;
5785
4642
  async function listDir(params) {
5786
4643
  const dirPath = resolveWorkspacePath(params.path);
@@ -5861,11 +4718,11 @@ function createListDirTool() {
5861
4718
  return zodTool({
5862
4719
  description: "List a sandbox workspace directory. Returns sorted entries with '/' suffixes for directories and bounded truncation notices.",
5863
4720
  annotations: { readOnlyHint: true, destructiveHint: false },
5864
- inputSchema: z8.object({
5865
- path: z8.string().min(1).describe(
4721
+ inputSchema: z7.object({
4722
+ path: z7.string().min(1).describe(
5866
4723
  "Directory path in the sandbox workspace. Defaults to the workspace root."
5867
4724
  ).optional(),
5868
- limit: z8.coerce.number().int().min(1).describe("Maximum entries to return. Defaults to 500.").optional()
4725
+ limit: z7.coerce.number().int().min(1).describe("Maximum entries to return. Defaults to 500.").optional()
5869
4726
  }),
5870
4727
  outputSchema: juniorToolResultSchema,
5871
4728
  execute: async () => {
@@ -5877,7 +4734,7 @@ function createListDirTool() {
5877
4734
  }
5878
4735
 
5879
4736
  // src/chat/tools/sandbox/read-file.ts
5880
- import { z as z9 } from "zod";
4737
+ import { z as z8 } from "zod";
5881
4738
 
5882
4739
  // src/chat/tool-support/text-range-result.ts
5883
4740
  var DEFAULT_READ_LIMIT = 1e3;
@@ -5952,10 +4809,10 @@ function createReadFileTool() {
5952
4809
  return zodTool({
5953
4810
  description: "Read a bounded line range from a file in the sandbox workspace. Use when you need exact file contents to verify facts or make edits safely. Prefer grep/findFiles/listDir for broad discovery.",
5954
4811
  annotations: { readOnlyHint: true, destructiveHint: false },
5955
- inputSchema: z9.object({
5956
- path: z9.string().min(1).describe("Path to the file in the sandbox workspace."),
5957
- offset: z9.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
5958
- limit: z9.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
4812
+ inputSchema: z8.object({
4813
+ path: z8.string().min(1).describe("Path to the file in the sandbox workspace."),
4814
+ offset: z8.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
4815
+ limit: z8.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
5959
4816
  }),
5960
4817
  outputSchema: juniorToolResultSchema,
5961
4818
  execute: async () => {
@@ -6020,6 +4877,30 @@ function createSandbox(options) {
6020
4877
  });
6021
4878
  return token;
6022
4879
  };
4880
+ const clearEgressSignals = async (egressId) => {
4881
+ if (!options.egressSignals) {
4882
+ await clearSandboxEgressSignals(egressId);
4883
+ return;
4884
+ }
4885
+ await options.egressSignals.clear(
4886
+ sandboxEgressCredentialTokenFor(egressId)
4887
+ );
4888
+ };
4889
+ const consumeEgressSignals = async (egressId) => {
4890
+ if (options.egressSignals) {
4891
+ return await options.egressSignals.consume(
4892
+ sandboxEgressCredentialTokenFor(egressId)
4893
+ );
4894
+ }
4895
+ const [authRequired, permissionDenied] = await Promise.all([
4896
+ consumeSandboxEgressAuthRequiredSignal(egressId),
4897
+ consumeSandboxEgressPermissionDeniedSignal(egressId)
4898
+ ]);
4899
+ return {
4900
+ ...authRequired ? { authRequired } : {},
4901
+ ...permissionDenied ? { permissionDenied } : {}
4902
+ };
4903
+ };
6023
4904
  const runtime = createSandboxRuntime({
6024
4905
  sandboxRef: options.sandboxRef,
6025
4906
  skills: options.skills,
@@ -6063,7 +4944,7 @@ function createSandbox(options) {
6063
4944
  "app.sandbox.command_length": command.length
6064
4945
  });
6065
4946
  const { bash: executeBash, sessionId: activeSessionId } = await runtime.tools();
6066
- await clearSandboxEgressSignals(activeSessionId);
4947
+ await clearEgressSignals(activeSessionId);
6067
4948
  const result = await withSandboxToolSpan(
6068
4949
  "bash",
6069
4950
  "process.exec",
@@ -6101,8 +4982,7 @@ function createSandbox(options) {
6101
4982
  }
6102
4983
  }
6103
4984
  );
6104
- const authRequired = await consumeSandboxEgressAuthRequiredSignal(activeSessionId);
6105
- const permissionDenied = await consumeSandboxEgressPermissionDeniedSignal(activeSessionId);
4985
+ const { authRequired, permissionDenied } = await consumeEgressSignals(activeSessionId);
6106
4986
  return formatSandboxCommandResult({
6107
4987
  ok: result.exitCode === 0,
6108
4988
  command,
@@ -6421,25 +5301,25 @@ function createSandbox(options) {
6421
5301
 
6422
5302
  // src/chat/tools/sandbox/file-uploads.ts
6423
5303
  import path8 from "path";
6424
- import { z as z10 } from "zod";
5304
+ import { z as z9 } from "zod";
6425
5305
  var MAX_SANDBOX_FILE_UPLOAD_BYTES = 10 * 1024 * 1024;
6426
5306
  var SANDBOX_ARTIFACTS_DIR = "/tmp/junior/artifacts";
6427
- var sandboxFileReferenceSchema = z10.object({
6428
- path: z10.string().min(1).describe(
5307
+ var sandboxFileReferenceSchema = z9.object({
5308
+ path: z9.string().min(1).describe(
6429
5309
  "Exact source path of an existing sandbox file. Preserve paths returned by other tools unchanged; do not move or rewrite them before sending."
6430
5310
  ),
6431
- filename: z10.string().min(1).nullable().optional().describe(
5311
+ filename: z9.string().min(1).nullable().optional().describe(
6432
5312
  "Optional filename override shown to the recipient. Null is treated as omitted."
6433
5313
  ),
6434
- mimeType: z10.string().min(1).nullable().optional().describe("Optional MIME type override. Null is treated as omitted."),
6435
- bytes: z10.number().int().nonnegative().nullable().optional().describe(
5314
+ mimeType: z9.string().min(1).nullable().optional().describe("Optional MIME type override. Null is treated as omitted."),
5315
+ bytes: z9.number().int().nonnegative().nullable().optional().describe(
6436
5316
  "Optional known file size returned by a producing tool. Null is treated as omitted; sending tools validate the file contents directly."
6437
5317
  )
6438
5318
  });
6439
5319
  var generatedArtifactFileRefSchema = sandboxFileReferenceSchema.extend({
6440
- filename: z10.string().min(1),
6441
- mimeType: z10.string().min(1).optional(),
6442
- bytes: z10.number().int().nonnegative()
5320
+ filename: z9.string().min(1),
5321
+ mimeType: z9.string().min(1).optional(),
5322
+ bytes: z9.number().int().nonnegative()
6443
5323
  }).strict();
6444
5324
  var MIME_BY_EXTENSION = {
6445
5325
  ".png": "image/png",
@@ -6603,6 +5483,7 @@ function createAgentSandbox(options) {
6603
5483
  referenceFiles: listReferenceFiles(),
6604
5484
  traceContext: options.traceContext,
6605
5485
  tracePropagation: options.tracePropagation,
5486
+ egressSignals: options.egressSignals,
6606
5487
  credentialEgress: options.credentialEgress,
6607
5488
  prepare: options.prepareSandbox,
6608
5489
  onSandboxRefChanged: async (sandboxRef) => {
@@ -7342,13 +6223,13 @@ function serializeMcpPayload(payload, privacy, options = {}) {
7342
6223
  }
7343
6224
 
7344
6225
  // src/chat/tools/sandbox/bash.ts
7345
- import { z as z11 } from "zod";
6226
+ import { z as z10 } from "zod";
7346
6227
  function createBashTool2() {
7347
6228
  return zodTool({
7348
6229
  description: "Run a bash command inside the isolated sandbox workspace. Use this for repository inspection/execution tasks that need shell access. Do not use for network-sensitive or destructive actions unless explicitly required.",
7349
- inputSchema: z11.object({
7350
- command: z11.string().min(1).describe("Bash command to run inside the sandbox."),
7351
- timeoutMs: z11.coerce.number().int().min(1e3).describe(
6230
+ inputSchema: z10.object({
6231
+ command: z10.string().min(1).describe("Bash command to run inside the sandbox."),
6232
+ timeoutMs: z10.coerce.number().int().min(1e3).describe(
7352
6233
  "Optional command timeout in milliseconds. Use for commands that may hang."
7353
6234
  ).optional()
7354
6235
  }),
@@ -7362,14 +6243,14 @@ function createBashTool2() {
7362
6243
  }
7363
6244
 
7364
6245
  // src/chat/tools/web/image-generate.ts
7365
- import { z as z12 } from "zod";
6246
+ import { z as z11 } from "zod";
7366
6247
  var imageGenerateOutputSchema = juniorToolResultSchema.extend({
7367
- model: z12.string(),
7368
- prompt: z12.string(),
7369
- enrichedPrompt: z12.string(),
7370
- image_count: z12.number().int().nonnegative(),
7371
- images: z12.array(generatedArtifactFileRefSchema),
7372
- delivery: z12.string()
6248
+ model: z11.string(),
6249
+ prompt: z11.string(),
6250
+ enrichedPrompt: z11.string(),
6251
+ image_count: z11.number().int().nonnegative(),
6252
+ images: z11.array(generatedArtifactFileRefSchema),
6253
+ delivery: z11.string()
7373
6254
  }).strict();
7374
6255
  var ENRICHMENT_SYSTEM_PROMPT = `You are an image prompt enrichment agent. Your job is to rewrite image generation requests to reflect a specific visual identity and mood.
7375
6256
 
@@ -7430,8 +6311,8 @@ function parseImageGenerationError(status, body, model) {
7430
6311
  function createImageGenerateTool(hooks, options, deps = {}) {
7431
6312
  return zodTool({
7432
6313
  description: "Generate images from a prompt. Use when the user wants to visually show or represent something \u2014 feelings, concepts, art, humor, or any visual idea. Also use for explicit image creation requests.",
7433
- inputSchema: z12.object({
7434
- prompt: z12.string().min(1).max(4e3).describe("Image generation prompt.")
6314
+ inputSchema: z11.object({
6315
+ prompt: z11.string().min(1).max(4e3).describe("Image generation prompt.")
7435
6316
  }),
7436
6317
  outputSchema: imageGenerateOutputSchema,
7437
6318
  execute: async ({ prompt }) => {
@@ -7517,7 +6398,7 @@ function createImageGenerateTool(hooks, options, deps = {}) {
7517
6398
  }
7518
6399
 
7519
6400
  // src/chat/tools/skill/call-mcp-tool.ts
7520
- import { z as z13 } from "zod";
6401
+ import { z as z12 } from "zod";
7521
6402
  function resolveMcpArguments(input) {
7522
6403
  const extraKeys = Object.keys(input).filter(
7523
6404
  (key) => key !== "tool_name" && key !== "arguments"
@@ -7553,9 +6434,9 @@ function missingToolMessage(toolName, provider) {
7553
6434
  function createCallMcpToolTool(mcpToolManager) {
7554
6435
  return zodTool({
7555
6436
  description: "Call an active MCP tool by exact tool_name. Use searchMcpTools to discover tool names and schemas; copy required provider fields into arguments. Do not call with only tool_name unless the discovered tool has no arguments. Authorization is handled by the runtime when required.",
7556
- inputSchema: z13.object({
7557
- tool_name: z13.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
7558
- arguments: z13.record(z13.string(), z13.unknown()).describe(
6437
+ inputSchema: z12.object({
6438
+ tool_name: z12.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
6439
+ arguments: z12.record(z12.string(), z12.unknown()).describe(
7559
6440
  'Arguments matching the disclosed MCP tool schema, for example { "query": "..." } when searchMcpTools shows query is required.'
7560
6441
  ).optional()
7561
6442
  }).passthrough(),
@@ -7596,7 +6477,7 @@ function createCallMcpToolTool(mcpToolManager) {
7596
6477
  }
7597
6478
 
7598
6479
  // src/chat/tools/skill/load-skill.ts
7599
- import { z as z14 } from "zod";
6480
+ import { z as z13 } from "zod";
7600
6481
  function toLoadedSkill(result, availableSkills) {
7601
6482
  if (result.ok !== true || typeof result.skill_name !== "string" || typeof result.description !== "string" || typeof result.skill_dir !== "string" || typeof result.instructions !== "string") {
7602
6483
  return null;
@@ -7645,8 +6526,8 @@ async function loadSkillFromHost(availableSkills, skillName) {
7645
6526
  function createLoadSkillTool(availableSkills, options) {
7646
6527
  return zodTool({
7647
6528
  description: "Load a skill by name for this turn. The result includes working_directory; resolve skill paths there and run skill-owned bash commands from there or with absolute paths. When the result includes mcp_provider, use searchMcpTools before callMcpTool. Use when a request clearly matches a known skill.",
7648
- inputSchema: z14.object({
7649
- skill_name: z14.string().min(1).describe("Skill name to load, without the leading slash.")
6529
+ inputSchema: z13.object({
6530
+ skill_name: z13.string().min(1).describe("Skill name to load, without the leading slash.")
7650
6531
  }),
7651
6532
  outputSchema: juniorToolResultSchema,
7652
6533
  execute: async ({ skill_name }) => {
@@ -7664,7 +6545,7 @@ function createLoadSkillTool(availableSkills, options) {
7664
6545
  }
7665
6546
 
7666
6547
  // src/chat/tools/skill/search-mcp-tools.ts
7667
- import { z as z15 } from "zod";
6548
+ import { z as z14 } from "zod";
7668
6549
 
7669
6550
  // src/chat/tool-support/schema-summary.ts
7670
6551
  function getSchemaProperties(schema) {
@@ -7794,37 +6675,37 @@ function toActiveMcpCatalogSummaries(toolDefs) {
7794
6675
  // src/chat/tools/skill/search-mcp-tools.ts
7795
6676
  var DEFAULT_MAX_RESULTS = 5;
7796
6677
  var MAX_RESULTS = 20;
7797
- var providerSummarySchema = z15.object({
7798
- provider: z15.string(),
7799
- description: z15.string(),
7800
- active: z15.boolean()
6678
+ var providerSummarySchema = z14.object({
6679
+ provider: z14.string(),
6680
+ description: z14.string(),
6681
+ active: z14.boolean()
7801
6682
  }).strict();
7802
- var mcpCallExampleSchema = z15.object({
7803
- tool_name: z15.string(),
7804
- arguments: z15.record(z15.string(), z15.string())
6683
+ var mcpCallExampleSchema = z14.object({
6684
+ tool_name: z14.string(),
6685
+ arguments: z14.record(z14.string(), z14.string())
7805
6686
  }).strict();
7806
- var exposedToolSummarySchema = z15.object({
7807
- tool_name: z15.string(),
7808
- mcp_tool_name: z15.string(),
7809
- provider: z15.string(),
7810
- title: z15.string().optional(),
7811
- description: z15.string(),
7812
- signature: z15.string(),
6687
+ var exposedToolSummarySchema = z14.object({
6688
+ tool_name: z14.string(),
6689
+ mcp_tool_name: z14.string(),
6690
+ provider: z14.string(),
6691
+ title: z14.string().optional(),
6692
+ description: z14.string(),
6693
+ signature: z14.string(),
7813
6694
  call: mcpCallExampleSchema,
7814
- input_schema: z15.record(z15.string(), z15.unknown()),
7815
- input_schema_summary: z15.string(),
7816
- output_schema: z15.record(z15.string(), z15.unknown()).optional(),
7817
- annotations: z15.record(z15.string(), z15.unknown()).optional()
6695
+ input_schema: z14.record(z14.string(), z14.unknown()),
6696
+ input_schema_summary: z14.string(),
6697
+ output_schema: z14.record(z14.string(), z14.unknown()).optional(),
6698
+ annotations: z14.record(z14.string(), z14.unknown()).optional()
7818
6699
  }).strict();
7819
6700
  var searchMcpToolsOutputSchema = juniorToolResultSchema.extend({
7820
- query: z15.string().nullable(),
7821
- provider: z15.string().nullable(),
7822
- total_active_tools: z15.number().int().nonnegative(),
7823
- returned_tools: z15.number().int().nonnegative(),
7824
- execution_tool: z15.literal("callMcpTool"),
6701
+ query: z14.string().nullable(),
6702
+ provider: z14.string().nullable(),
6703
+ total_active_tools: z14.number().int().nonnegative(),
6704
+ returned_tools: z14.number().int().nonnegative(),
6705
+ execution_tool: z14.literal("callMcpTool"),
7825
6706
  execution_example: mcpCallExampleSchema,
7826
- available_providers: z15.array(providerSummarySchema),
7827
- tools: z15.array(exposedToolSummarySchema)
6707
+ available_providers: z14.array(providerSummarySchema),
6708
+ tools: z14.array(exposedToolSummarySchema)
7828
6709
  }).strict();
7829
6710
  function normalize(value) {
7830
6711
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
@@ -7944,14 +6825,14 @@ function searchProviderCatalog(providers, query) {
7944
6825
  function createSearchMcpToolsTool(mcpToolManager) {
7945
6826
  return zodTool({
7946
6827
  description: "List or search MCP providers and active MCP tools. When provider is supplied and not yet active, Junior connects to it on demand and returns tool descriptors including schemas. Without provider, returns active tools plus matching configured providers without connecting. Use when choosing a provider tool or when callMcpTool arguments are unclear.",
7947
- inputSchema: z15.object({
7948
- query: z15.string().min(1).describe(
6828
+ inputSchema: z14.object({
6829
+ query: z14.string().min(1).describe(
7949
6830
  "Optional search terms describing the MCP tool or arguments needed."
7950
6831
  ).optional(),
7951
- provider: z15.string().min(1).describe(
6832
+ provider: z14.string().min(1).describe(
7952
6833
  "Optional provider name to list or search within. If configured but not yet connected, Junior activates it on demand."
7953
6834
  ).optional(),
7954
- max_results: z15.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
6835
+ max_results: z14.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
7955
6836
  }).strict(),
7956
6837
  outputSchema: searchMcpToolsOutputSchema,
7957
6838
  privateTraceResult: (result) => ({
@@ -8006,12 +6887,12 @@ function createSearchMcpToolsTool(mcpToolManager) {
8006
6887
  }
8007
6888
 
8008
6889
  // src/chat/tools/runtime/report-progress.ts
8009
- import { z as z16 } from "zod";
6890
+ import { z as z15 } from "zod";
8010
6891
  function createReportProgressTool() {
8011
6892
  return zodTool({
8012
6893
  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.",
8013
- inputSchema: z16.object({
8014
- message: z16.string().min(1).describe("Short user-facing progress message.")
6894
+ inputSchema: z15.object({
6895
+ message: z15.string().min(1).describe("Short user-facing progress message.")
8015
6896
  }),
8016
6897
  outputSchema: juniorToolResultSchema,
8017
6898
  execute: async () => ({
@@ -8022,34 +6903,34 @@ function createReportProgressTool() {
8022
6903
  }
8023
6904
 
8024
6905
  // src/chat/tools/resource-events.ts
8025
- import { z as z18 } from "zod";
6906
+ import { z as z17 } from "zod";
8026
6907
 
8027
6908
  // src/chat/resource-events/store.ts
8028
6909
  import { createHash } from "crypto";
8029
6910
  import { destinationSchema } from "@sentry/junior-plugin-api";
8030
- import { z as z17 } from "zod";
6911
+ import { z as z16 } from "zod";
8031
6912
  var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription";
8032
6913
  var INDEX_LOCK_TTL_MS = 1e4;
8033
6914
  var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
8034
6915
  var SUBSCRIPTION_LOCK_WAIT_MS = 1e4;
8035
6916
  var SUBSCRIPTION_LOCK_RETRY_MS = 25;
8036
6917
  var SUBSCRIPTION_LOCK_HEARTBEAT_MS = 3e3;
8037
- var subscriptionStatusSchema = z17.enum(["active", "cancelled", "completed"]);
8038
- var subscriptionIdIndexSchema = z17.array(z17.string().min(1));
8039
- var subscriptionSchema = z17.object({
8040
- conversationId: z17.string().min(1),
8041
- createdAtMs: z17.number().finite(),
6918
+ var subscriptionStatusSchema = z16.enum(["active", "cancelled", "completed"]);
6919
+ var subscriptionIdIndexSchema = z16.array(z16.string().min(1));
6920
+ var subscriptionSchema = z16.object({
6921
+ conversationId: z16.string().min(1),
6922
+ createdAtMs: z16.number().finite(),
8042
6923
  destination: destinationSchema,
8043
- events: z17.array(z17.string().min(1)).min(1),
8044
- expiresAtMs: z17.number().finite(),
8045
- id: z17.string().min(1),
8046
- intent: z17.string().min(1),
8047
- label: z17.string().min(1),
8048
- provider: z17.string().min(1),
8049
- resourceRef: z17.string().min(1),
8050
- resourceType: z17.string().min(1),
6924
+ events: z16.array(z16.string().min(1)).min(1),
6925
+ expiresAtMs: z16.number().finite(),
6926
+ id: z16.string().min(1),
6927
+ intent: z16.string().min(1),
6928
+ label: z16.string().min(1),
6929
+ provider: z16.string().min(1),
6930
+ resourceRef: z16.string().min(1),
6931
+ resourceType: z16.string().min(1),
8051
6932
  status: subscriptionStatusSchema,
8052
- updatedAtMs: z17.number().finite()
6933
+ updatedAtMs: z16.number().finite()
8053
6934
  }).strict();
8054
6935
  function digest(value) {
8055
6936
  return createHash("sha256").update(value).digest("hex").slice(0, 32);
@@ -8405,18 +7286,18 @@ var RESOURCE_WATCH_TOOL_SOURCE = {
8405
7286
  id: "resource-watches",
8406
7287
  description: "Inspect or stop resource watches for the current conversation."
8407
7288
  };
8408
- var subscribeInputSchema = z18.object({
8409
- resourceRef: z18.string().describe("Opaque resource ref copied from a subscribable tool result."),
8410
- provider: z18.string().describe("Provider that owns the resource ref."),
8411
- resourceType: z18.string().describe("Provider-defined resource type from the subscribable hint."),
8412
- label: z18.string().describe("Human-readable resource label from the subscribable hint."),
8413
- events: z18.array(z18.string()).min(1).describe(
7289
+ var subscribeInputSchema = z17.object({
7290
+ resourceRef: z17.string().describe("Opaque resource ref copied from a subscribable tool result."),
7291
+ provider: z17.string().describe("Provider that owns the resource ref."),
7292
+ resourceType: z17.string().describe("Provider-defined resource type from the subscribable hint."),
7293
+ label: z17.string().describe("Human-readable resource label from the subscribable hint."),
7294
+ events: z17.array(z17.string()).min(1).describe(
8414
7295
  "High-signal event names to deliver to this conversation when they occur."
8415
7296
  ),
8416
- intent: z18.string().describe(
7297
+ intent: z17.string().describe(
8417
7298
  "Concise reason this conversation wants these events, used when an event arrives."
8418
7299
  ),
8419
- ttlMs: z18.coerce.number().describe(
7300
+ ttlMs: z17.coerce.number().describe(
8420
7301
  "How long to keep the subscription active. Defaults to 14 days and is capped at 30 days."
8421
7302
  ).optional()
8422
7303
  });
@@ -8509,7 +7390,7 @@ function createListResourceEventSubscriptionsTool(context) {
8509
7390
  description: "List active resource event subscriptions for the current conversation.",
8510
7391
  exposure: "deferred",
8511
7392
  source: RESOURCE_WATCH_TOOL_SOURCE,
8512
- inputSchema: z18.object({}),
7393
+ inputSchema: z17.object({}),
8513
7394
  outputSchema: juniorToolResultSchema,
8514
7395
  async execute() {
8515
7396
  const conversationId = requireConversationContext(context);
@@ -8542,7 +7423,7 @@ function createStopWatchingResourcesTool(context) {
8542
7423
  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.",
8543
7424
  exposure: "deferred",
8544
7425
  source: RESOURCE_WATCH_TOOL_SOURCE,
8545
- inputSchema: z18.object({}),
7426
+ inputSchema: z17.object({}),
8546
7427
  outputSchema: juniorToolResultSchema,
8547
7428
  async execute() {
8548
7429
  const conversationId = requireConversationContext(context);
@@ -8649,11 +7530,11 @@ async function listThreadReplies(input) {
8649
7530
  }
8650
7531
 
8651
7532
  // src/chat/slack/timestamp-param.ts
8652
- import { z as z19 } from "zod";
7533
+ import { z as z18 } from "zod";
8653
7534
  function slackTimestampParam(description) {
8654
- return z19.preprocess(
7535
+ return z18.preprocess(
8655
7536
  (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
8656
- z19.string().min(1)
7537
+ z18.string().min(1)
8657
7538
  ).describe(description);
8658
7539
  }
8659
7540
  function optionalSlackTimestampParam(description) {
@@ -8684,10 +7565,10 @@ function parseRequiredSlackTimestampParam(field, value) {
8684
7565
  }
8685
7566
 
8686
7567
  // src/chat/slack/tools/channel-list-messages.ts
8687
- import { z as z20 } from "zod";
8688
- var booleanInput2 = (description) => z20.preprocess(
7568
+ import { z as z19 } from "zod";
7569
+ var booleanInput2 = (description) => z19.preprocess(
8689
7570
  (value) => value === "true" ? true : value === "false" ? false : value,
8690
- z20.boolean()
7571
+ z19.boolean()
8691
7572
  ).describe(description);
8692
7573
  function normalizeRangeTimestamp(field, value, targetChannelId) {
8693
7574
  if (value === void 0) {
@@ -8714,9 +7595,9 @@ function createSlackChannelListMessagesTool(context) {
8714
7595
  return zodTool({
8715
7596
  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.",
8716
7597
  annotations: { readOnlyHint: true, destructiveHint: false },
8717
- inputSchema: z20.object({
8718
- limit: z20.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
8719
- cursor: z20.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
7598
+ inputSchema: z19.object({
7599
+ limit: z19.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
7600
+ cursor: z19.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
8720
7601
  oldest: optionalSlackTimestampParam(
8721
7602
  "Optional oldest message timestamp (Slack ts) for range filtering."
8722
7603
  ),
@@ -8726,7 +7607,7 @@ function createSlackChannelListMessagesTool(context) {
8726
7607
  inclusive: booleanInput2(
8727
7608
  "Whether oldest/latest bounds should be inclusive."
8728
7609
  ).optional(),
8729
- max_pages: z20.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
7610
+ max_pages: z19.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
8730
7611
  }),
8731
7612
  outputSchema: juniorToolResultSchema,
8732
7613
  execute: async ({
@@ -8800,20 +7681,20 @@ function createSlackChannelListMessagesTool(context) {
8800
7681
  }
8801
7682
 
8802
7683
  // src/chat/slack/tools/conversation-search.ts
8803
- import { z as z21 } from "zod";
7684
+ import { z as z20 } from "zod";
8804
7685
  var DEFAULT_LIMIT = 5;
8805
7686
  var conversationSearchOutputSchema = juniorToolResultSchema.extend({
8806
- query: z21.string(),
8807
- count: z21.number().int().nonnegative(),
8808
- threads: z21.array(
8809
- z21.object({
8810
- conversation_id: z21.string().min(1),
8811
- thread_ts: z21.string().min(1),
8812
- message_id: z21.string().min(1),
8813
- message_role: z21.enum(["assistant", "user"]),
8814
- message_timestamp: z21.string().datetime(),
8815
- excerpt: z21.string(),
8816
- permalink: z21.string().url().optional()
7687
+ query: z20.string(),
7688
+ count: z20.number().int().nonnegative(),
7689
+ threads: z20.array(
7690
+ z20.object({
7691
+ conversation_id: z20.string().min(1),
7692
+ thread_ts: z20.string().min(1),
7693
+ message_id: z20.string().min(1),
7694
+ message_role: z20.enum(["assistant", "user"]),
7695
+ message_timestamp: z20.string().datetime(),
7696
+ excerpt: z20.string(),
7697
+ permalink: z20.string().url().optional()
8817
7698
  }).strict()
8818
7699
  )
8819
7700
  });
@@ -8826,9 +7707,9 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
8826
7707
  description: "Search retained public Junior conversation threads in the current Slack workspace."
8827
7708
  },
8828
7709
  annotations: { readOnlyHint: true, destructiveHint: false },
8829
- inputSchema: z21.object({
8830
- query: z21.string().trim().min(1).max(200).describe("Words or a short phrase to find in prior conversations."),
8831
- limit: z21.number().int().min(1).max(10).describe("Maximum number of prior conversation threads to return.").nullable().optional()
7710
+ inputSchema: z20.object({
7711
+ query: z20.string().trim().min(1).max(200).describe("Words or a short phrase to find in prior conversations."),
7712
+ limit: z20.number().int().min(1).max(10).describe("Maximum number of prior conversation threads to return.").nullable().optional()
8832
7713
  }),
8833
7714
  outputSchema: conversationSearchOutputSchema,
8834
7715
  execute: async ({ query, limit }) => {
@@ -8875,27 +7756,27 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
8875
7756
  }
8876
7757
 
8877
7758
  // src/chat/slack/tools/public-search.ts
8878
- import { z as z22 } from "zod";
7759
+ import { z as z21 } from "zod";
8879
7760
  var DEFAULT_LIMIT2 = 10;
8880
- var optionalTimestampSchema = z22.preprocess(
7761
+ var optionalTimestampSchema = z21.preprocess(
8881
7762
  (value) => typeof value === "string" && value.trim() === "" ? void 0 : value,
8882
- z22.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
7763
+ z21.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
8883
7764
  );
8884
- var searchMessageSchema = z22.object({
8885
- author_name: z22.string().optional(),
8886
- author_user_id: z22.string().optional(),
8887
- channel_id: z22.string().min(1),
8888
- channel_name: z22.string().optional(),
8889
- message_ts: z22.string().min(1),
8890
- content: z22.string(),
8891
- is_author_bot: z22.boolean().optional(),
8892
- permalink: z22.string().url()
7765
+ var searchMessageSchema = z21.object({
7766
+ author_name: z21.string().optional(),
7767
+ author_user_id: z21.string().optional(),
7768
+ channel_id: z21.string().min(1),
7769
+ channel_name: z21.string().optional(),
7770
+ message_ts: z21.string().min(1),
7771
+ content: z21.string(),
7772
+ is_author_bot: z21.boolean().optional(),
7773
+ permalink: z21.string().url()
8893
7774
  });
8894
7775
  var publicSearchOutputSchema = juniorToolResultSchema.extend({
8895
- query: z22.string(),
8896
- count: z22.number().int().nonnegative(),
8897
- messages: z22.array(searchMessageSchema),
8898
- next_cursor: z22.string().optional()
7776
+ query: z21.string(),
7777
+ count: z21.number().int().nonnegative(),
7778
+ messages: z21.array(searchMessageSchema),
7779
+ next_cursor: z21.string().optional()
8899
7780
  });
8900
7781
  function normalizeMessage(value) {
8901
7782
  const parsed = searchMessageSchema.safeParse(value);
@@ -8917,8 +7798,8 @@ function createSlackPublicSearchTool(actionToken) {
8917
7798
  return zodTool({
8918
7799
  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.",
8919
7800
  annotations: { readOnlyHint: true, destructiveHint: false },
8920
- inputSchema: z22.object({
8921
- query: z22.string().trim().min(1).max(500).describe(
7801
+ inputSchema: z21.object({
7802
+ query: z21.string().trim().min(1).max(500).describe(
8922
7803
  "A focused Slack search query, including Slack search filters when useful."
8923
7804
  ),
8924
7805
  after: optionalTimestampSchema.describe(
@@ -8927,10 +7808,10 @@ function createSlackPublicSearchTool(actionToken) {
8927
7808
  before: optionalTimestampSchema.describe(
8928
7809
  "Optional Unix timestamp upper bound."
8929
7810
  ),
8930
- cursor: z22.string().min(1).describe("Cursor for the next result page.").optional(),
8931
- limit: z22.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
8932
- sort: z22.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
8933
- sort_dir: z22.enum(["asc", "desc"]).describe("Sort direction.").optional()
7811
+ cursor: z21.string().min(1).describe("Cursor for the next result page.").optional(),
7812
+ limit: z21.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
7813
+ sort: z21.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
7814
+ sort_dir: z21.enum(["asc", "desc"]).describe("Sort direction.").optional()
8934
7815
  }),
8935
7816
  outputSchema: publicSearchOutputSchema,
8936
7817
  execute: async ({
@@ -8996,7 +7877,7 @@ function createSlackPublicSearchTool(actionToken) {
8996
7877
  }
8997
7878
 
8998
7879
  // src/chat/slack/tools/message-add-reaction.ts
8999
- import { z as z23 } from "zod";
7880
+ import { z as z22 } from "zod";
9000
7881
 
9001
7882
  // src/chat/tools/idempotency.ts
9002
7883
  function stableSerialize(value) {
@@ -9020,8 +7901,8 @@ function createOperationKey(toolName, input) {
9020
7901
  function createSlackMessageAddReactionTool(context, state) {
9021
7902
  return zodTool({
9022
7903
  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.",
9023
- inputSchema: z23.object({
9024
- emoji: z23.string().min(1).max(64).describe(
7904
+ inputSchema: z22.object({
7905
+ emoji: z22.string().min(1).max(64).describe(
9025
7906
  "Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
9026
7907
  )
9027
7908
  }),
@@ -9072,18 +7953,18 @@ function createSlackMessageAddReactionTool(context, state) {
9072
7953
 
9073
7954
  // src/chat/slack/tools/send-files.ts
9074
7955
  import { createHash as createHash2 } from "crypto";
9075
- import { z as z24 } from "zod";
9076
- var sendFilesDataSchema = z24.object({
9077
- channel_id: z24.string().min(1),
9078
- deduplicated: z24.boolean().optional(),
9079
- file_count: z24.number().int().nonnegative(),
9080
- file_ids: z24.array(z24.string().min(1)).optional(),
9081
- thread_ts: z24.string().min(1)
7956
+ import { z as z23 } from "zod";
7957
+ var sendFilesDataSchema = z23.object({
7958
+ channel_id: z23.string().min(1),
7959
+ deduplicated: z23.boolean().optional(),
7960
+ file_count: z23.number().int().nonnegative(),
7961
+ file_ids: z23.array(z23.string().min(1)).optional(),
7962
+ thread_ts: z23.string().min(1)
9082
7963
  });
9083
7964
  var sendFilesResultSchema = juniorToolResultSchema.extend({
9084
- ok: z24.literal(true),
9085
- status: z24.literal("success"),
9086
- target: z24.string().min(1),
7965
+ ok: z23.literal(true),
7966
+ status: z23.literal("success"),
7967
+ target: z23.string().min(1),
9087
7968
  data: sendFilesDataSchema
9088
7969
  });
9089
7970
  function normalizeFiles(files) {
@@ -9108,8 +7989,8 @@ function fileOperationInput(files) {
9108
7989
  function createSendFilesTool(context, state, materializeFile) {
9109
7990
  return zodTool({
9110
7991
  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.",
9111
- inputSchema: z24.object({
9112
- files: z24.array(sandboxFileReferenceSchema).min(1).describe(
7992
+ inputSchema: z23.object({
7993
+ files: z23.array(sandboxFileReferenceSchema).min(1).describe(
9113
7994
  "One or more existing sandbox files. Objects returned by imageGenerate or webFetch can be passed unchanged."
9114
7995
  )
9115
7996
  }),
@@ -9413,13 +8294,13 @@ function storedCanvasUrl(state, canvasId) {
9413
8294
  }
9414
8295
 
9415
8296
  // src/chat/slack/tools/canvas/create.ts
9416
- import { z as z25 } from "zod";
8297
+ import { z as z24 } from "zod";
9417
8298
  function createSlackCanvasCreateTool(context, state) {
9418
8299
  return zodTool({
9419
8300
  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.",
9420
- inputSchema: z25.object({
9421
- title: z25.string().min(1).max(160).describe("Canvas title."),
9422
- markdown: z25.string().min(1).describe("Canvas markdown body content.")
8301
+ inputSchema: z24.object({
8302
+ title: z24.string().min(1).max(160).describe("Canvas title."),
8303
+ markdown: z24.string().min(1).describe("Canvas markdown body content.")
9423
8304
  }),
9424
8305
  outputSchema: juniorToolResultSchema,
9425
8306
  execute: async ({ title, markdown }) => {
@@ -9482,35 +8363,35 @@ function createSlackCanvasCreateTool(context, state) {
9482
8363
  }
9483
8364
 
9484
8365
  // src/chat/slack/tools/canvas/edit.ts
9485
- import { z as z26 } from "zod";
8366
+ import { z as z25 } from "zod";
9486
8367
  function prepareCanvasEditArguments(input) {
9487
8368
  return prepareTextReplacementArguments(input);
9488
8369
  }
9489
- var editReplacementSchema2 = z26.object({
9490
- oldText: z26.string().min(1).describe(
8370
+ var editReplacementSchema2 = z25.object({
8371
+ oldText: z25.string().min(1).describe(
9491
8372
  "Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
9492
8373
  ),
9493
- newText: z26.string().describe("Replacement Canvas markdown for this edit.")
8374
+ newText: z25.string().describe("Replacement Canvas markdown for this edit.")
9494
8375
  });
9495
8376
  var slackCanvasEditOutputSchema = juniorToolResultSchema.extend({
9496
- canvas_id: z26.string().optional(),
9497
- title: z26.string().optional(),
9498
- permalink: z26.string().optional(),
9499
- diff: z26.string().optional(),
9500
- first_changed_line: z26.number().int().positive().optional(),
9501
- replacements: z26.number().int().nonnegative().optional(),
9502
- normalized_heading_count: z26.number().int().nonnegative().optional(),
9503
- summary: z26.string().optional(),
9504
- deduplicated: z26.boolean().optional()
8377
+ canvas_id: z25.string().optional(),
8378
+ title: z25.string().optional(),
8379
+ permalink: z25.string().optional(),
8380
+ diff: z25.string().optional(),
8381
+ first_changed_line: z25.number().int().positive().optional(),
8382
+ replacements: z25.number().int().nonnegative().optional(),
8383
+ normalized_heading_count: z25.number().int().nonnegative().optional(),
8384
+ summary: z25.string().optional(),
8385
+ deduplicated: z25.boolean().optional()
9505
8386
  }).strict();
9506
8387
  function createSlackCanvasEditTool(state) {
9507
8388
  return zodTool({
9508
8389
  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.",
9509
8390
  prepareArguments: prepareCanvasEditArguments,
9510
8391
  executionMode: "sequential",
9511
- inputSchema: z26.object({
9512
- canvas: z26.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
9513
- edits: z26.array(editReplacementSchema2).min(1).describe(
8392
+ inputSchema: z25.object({
8393
+ canvas: z25.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
8394
+ edits: z25.array(editReplacementSchema2).min(1).describe(
9514
8395
  "Exact replacements matched against the current Canvas body, not incrementally."
9515
8396
  )
9516
8397
  }),
@@ -9588,29 +8469,29 @@ function createSlackCanvasEditTool(state) {
9588
8469
  }
9589
8470
 
9590
8471
  // src/chat/slack/tools/canvas/read.ts
9591
- import { z as z27 } from "zod";
8472
+ import { z as z26 } from "zod";
9592
8473
  var slackCanvasReadOutputSchema = juniorToolResultSchema.extend({
9593
- canvas_id: z27.string().optional(),
9594
- title: z27.string().optional(),
9595
- permalink: z27.string().optional(),
9596
- mimetype: z27.string().optional(),
9597
- filetype: z27.string().optional(),
9598
- original_byte_length: z27.number().int().nonnegative().optional(),
9599
- content: z27.string().optional(),
9600
- start_line: z27.number().int().positive().optional(),
9601
- end_line: z27.number().int().nonnegative().optional(),
9602
- total_lines: z27.number().int().nonnegative().optional()
8474
+ canvas_id: z26.string().optional(),
8475
+ title: z26.string().optional(),
8476
+ permalink: z26.string().optional(),
8477
+ mimetype: z26.string().optional(),
8478
+ filetype: z26.string().optional(),
8479
+ original_byte_length: z26.number().int().nonnegative().optional(),
8480
+ content: z26.string().optional(),
8481
+ start_line: z26.number().int().positive().optional(),
8482
+ end_line: z26.number().int().nonnegative().optional(),
8483
+ total_lines: z26.number().int().nonnegative().optional()
9603
8484
  }).strict();
9604
8485
  function createSlackCanvasReadTool() {
9605
8486
  return zodTool({
9606
8487
  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.",
9607
8488
  annotations: { readOnlyHint: true, destructiveHint: false },
9608
- inputSchema: z27.object({
9609
- canvas: z27.string().min(1).describe(
8489
+ inputSchema: z26.object({
8490
+ canvas: z26.string().min(1).describe(
9610
8491
  "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
9611
8492
  ),
9612
- offset: z27.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
9613
- limit: z27.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
8493
+ offset: z26.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
8494
+ limit: z26.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
9614
8495
  }),
9615
8496
  outputSchema: slackCanvasReadOutputSchema,
9616
8497
  execute: async ({ canvas, offset, limit }) => {
@@ -9667,14 +8548,14 @@ function createSlackCanvasReadTool() {
9667
8548
  }
9668
8549
 
9669
8550
  // src/chat/slack/tools/canvas/write.ts
9670
- import { z as z28 } from "zod";
8551
+ import { z as z27 } from "zod";
9671
8552
  function createSlackCanvasWriteTool(state) {
9672
8553
  return zodTool({
9673
8554
  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.",
9674
8555
  executionMode: "sequential",
9675
- inputSchema: z28.object({
9676
- canvas: z28.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
9677
- content: z28.string().describe("UTF-8 markdown content to write.")
8556
+ inputSchema: z27.object({
8557
+ canvas: z27.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
8558
+ content: z27.string().describe("UTF-8 markdown content to write.")
9678
8559
  }),
9679
8560
  outputSchema: juniorToolResultSchema,
9680
8561
  execute: async ({ canvas, content }) => {
@@ -9910,12 +8791,12 @@ async function updateListItem(input) {
9910
8791
  }
9911
8792
 
9912
8793
  // src/chat/slack/id-param.ts
9913
- import { z as z29 } from "zod";
8794
+ import { z as z28 } from "zod";
9914
8795
  function slackChannelIdParam(description) {
9915
- return z29.string().min(1).describe(description);
8796
+ return z28.string().min(1).describe(description);
9916
8797
  }
9917
8798
  function slackUserIdParam(description) {
9918
- return z29.string().min(1).describe(description);
8799
+ return z28.string().min(1).describe(description);
9919
8800
  }
9920
8801
  function parseRequiredSlackChannelIdParam(field, value) {
9921
8802
  const channelId = parseSlackChannelId(value);
@@ -9939,16 +8820,16 @@ function parseRequiredSlackUserIdParam(field, value) {
9939
8820
  }
9940
8821
 
9941
8822
  // src/chat/slack/tools/list/add-items.ts
9942
- import { z as z30 } from "zod";
8823
+ import { z as z29 } from "zod";
9943
8824
  function createSlackListAddItemsTool(state) {
9944
8825
  return zodTool({
9945
8826
  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.",
9946
- inputSchema: z30.object({
9947
- items: z30.array(z30.string().min(1)).min(1).max(25).describe("List item titles to create."),
8827
+ inputSchema: z29.object({
8828
+ items: z29.array(z29.string().min(1)).min(1).max(25).describe("List item titles to create."),
9948
8829
  assignee_user_id: slackUserIdParam(
9949
8830
  "Optional Slack user ID assigned to all created items."
9950
8831
  ).optional(),
9951
- due_date: z30.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
8832
+ due_date: z29.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
9952
8833
  }),
9953
8834
  outputSchema: juniorToolResultSchema,
9954
8835
  execute: async ({ items, assignee_user_id, due_date }) => {
@@ -10002,12 +8883,12 @@ function createSlackListAddItemsTool(state) {
10002
8883
  }
10003
8884
 
10004
8885
  // src/chat/slack/tools/list/create.ts
10005
- import { z as z31 } from "zod";
8886
+ import { z as z30 } from "zod";
10006
8887
  function createSlackListCreateTool(state) {
10007
8888
  return zodTool({
10008
8889
  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.",
10009
- inputSchema: z31.object({
10010
- name: z31.string().min(1).max(160).describe("Name for the new Slack list.")
8890
+ inputSchema: z30.object({
8891
+ name: z30.string().min(1).max(160).describe("Name for the new Slack list.")
10011
8892
  }),
10012
8893
  outputSchema: juniorToolResultSchema,
10013
8894
  execute: async ({ name }) => {
@@ -10039,13 +8920,13 @@ function createSlackListCreateTool(state) {
10039
8920
  }
10040
8921
 
10041
8922
  // src/chat/slack/tools/list/get-items.ts
10042
- import { z as z32 } from "zod";
8923
+ import { z as z31 } from "zod";
10043
8924
  function createSlackListGetItemsTool(state) {
10044
8925
  return zodTool({
10045
8926
  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.",
10046
8927
  annotations: { readOnlyHint: true, destructiveHint: false },
10047
- inputSchema: z32.object({
10048
- limit: z32.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
8928
+ inputSchema: z31.object({
8929
+ limit: z31.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
10049
8930
  }),
10050
8931
  outputSchema: juniorToolResultSchema,
10051
8932
  execute: async ({ limit }) => {
@@ -10070,21 +8951,21 @@ function createSlackListGetItemsTool(state) {
10070
8951
  }
10071
8952
 
10072
8953
  // src/chat/slack/tools/list/update-item.ts
10073
- import { z as z33 } from "zod";
10074
- var booleanInput3 = (description) => z33.preprocess(
8954
+ import { z as z32 } from "zod";
8955
+ var booleanInput3 = (description) => z32.preprocess(
10075
8956
  (value) => value === "true" ? true : value === "false" ? false : value,
10076
- z33.boolean()
8957
+ z32.boolean()
10077
8958
  ).describe(description);
10078
- var updateListItemInputSchema = z33.union([
10079
- z33.object({
10080
- item_id: z33.string().min(1).describe("ID of the Slack list item to update."),
8959
+ var updateListItemInputSchema = z32.union([
8960
+ z32.object({
8961
+ item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
10081
8962
  completed: booleanInput3("Optional completion status update."),
10082
- title: z33.string().min(1).describe("Optional new item title.").optional()
8963
+ title: z32.string().min(1).describe("Optional new item title.").optional()
10083
8964
  }),
10084
- z33.object({
10085
- item_id: z33.string().min(1).describe("ID of the Slack list item to update."),
8965
+ z32.object({
8966
+ item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
10086
8967
  completed: booleanInput3("Optional completion status update.").optional(),
10087
- title: z33.string().min(1).describe("Optional new item title.")
8968
+ title: z32.string().min(1).describe("Optional new item title.")
10088
8969
  })
10089
8970
  ]);
10090
8971
  function createSlackListUpdateItemTool(state) {
@@ -10160,7 +9041,7 @@ async function checkSlackChannelReadAccess(args) {
10160
9041
  }
10161
9042
 
10162
9043
  // src/chat/slack/tools/thread-read.ts
10163
- import { z as z34 } from "zod";
9044
+ import { z as z33 } from "zod";
10164
9045
 
10165
9046
  // src/chat/slack/tools/slack-message-url.ts
10166
9047
  var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
@@ -10333,8 +9214,8 @@ function createSlackThreadReadTool(context, deps = {}) {
10333
9214
  return zodTool({
10334
9215
  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.",
10335
9216
  annotations: { readOnlyHint: true, destructiveHint: false },
10336
- inputSchema: z34.object({
10337
- url: z34.string().min(1).describe(
9217
+ inputSchema: z33.object({
9218
+ url: z33.string().min(1).describe(
10338
9219
  "Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
10339
9220
  ).optional(),
10340
9221
  channel_id: slackChannelIdParam(
@@ -10343,8 +9224,8 @@ function createSlackThreadReadTool(context, deps = {}) {
10343
9224
  ts: slackTimestampParam(
10344
9225
  "Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
10345
9226
  ).optional(),
10346
- limit: z34.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
10347
- max_pages: z34.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
9227
+ limit: z33.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
9228
+ max_pages: z33.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
10348
9229
  }),
10349
9230
  outputSchema: juniorToolResultSchema,
10350
9231
  execute: async ({ url, channel_id, ts, limit, max_pages }) => {
@@ -10600,29 +9481,29 @@ async function searchSlackUsers(options) {
10600
9481
  }
10601
9482
 
10602
9483
  // src/chat/slack/tools/user-lookup.ts
10603
- import { z as z35 } from "zod";
10604
- var booleanInput4 = (description) => z35.preprocess(
9484
+ import { z as z34 } from "zod";
9485
+ var booleanInput4 = (description) => z34.preprocess(
10605
9486
  (value) => value === "true" ? true : value === "false" ? false : value,
10606
- z35.boolean()
9487
+ z34.boolean()
10607
9488
  ).describe(description);
10608
9489
  function createSlackUserLookupTool() {
10609
9490
  return zodTool({
10610
9491
  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.",
10611
9492
  annotations: { readOnlyHint: true, destructiveHint: false },
10612
- inputSchema: z35.object({
9493
+ inputSchema: z34.object({
10613
9494
  user_id: slackUserIdParam(
10614
9495
  "Slack user ID to look up (e.g. U039RR91S). Mutually exclusive with email and query."
10615
9496
  ).optional(),
10616
- email: z35.string().min(3).describe(
9497
+ email: z34.string().min(3).describe(
10617
9498
  "Email address to look up. Mutually exclusive with user_id and query."
10618
9499
  ).optional(),
10619
- query: z35.string().min(2).describe(
9500
+ query: z34.string().min(2).describe(
10620
9501
  "Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
10621
9502
  ).optional(),
10622
- limit: z35.coerce.number().int().min(1).max(20).describe(
9503
+ limit: z34.coerce.number().int().min(1).max(20).describe(
10623
9504
  "Maximum number of results to return for name search. Defaults to 10."
10624
9505
  ).optional(),
10625
- max_pages: z35.coerce.number().int().min(1).max(5).describe(
9506
+ max_pages: z34.coerce.number().int().min(1).max(5).describe(
10626
9507
  "Maximum number of Slack API pages to scan for name search. Defaults to 3."
10627
9508
  ).optional(),
10628
9509
  include_bots: booleanInput4(
@@ -10726,18 +9607,18 @@ function createSlackUserLookupTool() {
10726
9607
  }
10727
9608
 
10728
9609
  // src/chat/tools/system-time.ts
10729
- import { z as z36 } from "zod";
9610
+ import { z as z35 } from "zod";
10730
9611
  var systemTimeOutputSchema = juniorToolResultSchema.extend({
10731
- unix_ms: z36.number(),
10732
- iso_utc: z36.string(),
10733
- iso_local: z36.string(),
10734
- timezone_offset_minutes: z36.number()
9612
+ unix_ms: z35.number(),
9613
+ iso_utc: z35.string(),
9614
+ iso_local: z35.string(),
9615
+ timezone_offset_minutes: z35.number()
10735
9616
  });
10736
9617
  function createSystemTimeTool() {
10737
9618
  return zodTool({
10738
9619
  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.",
10739
9620
  annotations: { readOnlyHint: true, destructiveHint: false },
10740
- inputSchema: z36.object({}),
9621
+ inputSchema: z35.object({}),
10741
9622
  outputSchema: systemTimeOutputSchema,
10742
9623
  privateTraceResult: (result) => ({
10743
9624
  ok: result.ok,
@@ -10766,10 +9647,10 @@ function createSystemTimeTool() {
10766
9647
  }
10767
9648
 
10768
9649
  // src/chat/tools/handoff/tool.ts
10769
- import { z as z37 } from "zod";
9650
+ import { z as z36 } from "zod";
10770
9651
  var HANDOFF_TOOL_NAME = "handoff";
10771
9652
  function createHandoffTool(handoff) {
10772
- const profileSchema = z37.enum(handoff.profiles);
9653
+ const profileSchema = z36.enum(handoff.profiles);
10773
9654
  const handoffResultSchema = juniorToolResultSchema.extend({
10774
9655
  model_profile: profileSchema
10775
9656
  });
@@ -10777,7 +9658,7 @@ function createHandoffTool(handoff) {
10777
9658
  return zodTool({
10778
9659
  description: `Switch to another execution profile and continue the same task. Profiles: ${profileNames}. Call this as the only tool.`,
10779
9660
  executionMode: "sequential",
10780
- inputSchema: z37.object({
9661
+ inputSchema: z36.object({
10781
9662
  profile: profileSchema.describe("Target execution profile")
10782
9663
  }).strict(),
10783
9664
  outputSchema: handoffResultSchema,
@@ -10800,7 +9681,7 @@ function createHandoffTool(handoff) {
10800
9681
  }
10801
9682
 
10802
9683
  // src/chat/tools/web/fetch-tool.ts
10803
- import { z as z38 } from "zod";
9684
+ import { z as z37 } from "zod";
10804
9685
 
10805
9686
  // src/chat/tools/web/constants.ts
10806
9687
  var USER_AGENT = "junior-bot/0.1";
@@ -11250,9 +10131,9 @@ function createWebFetchTool(hooks, options = {}) {
11250
10131
  destructiveHint: false,
11251
10132
  openWorldHint: true
11252
10133
  },
11253
- inputSchema: z38.object({
11254
- url: z38.string().min(1).describe("HTTP(S) URL to fetch."),
11255
- max_chars: z38.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
10134
+ inputSchema: z37.object({
10135
+ url: z37.string().min(1).describe("HTTP(S) URL to fetch."),
10136
+ max_chars: z37.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
11256
10137
  }),
11257
10138
  outputSchema: juniorToolResultSchema,
11258
10139
  execute: async ({ url, max_chars }) => {
@@ -11313,7 +10194,7 @@ function createWebFetchTool(hooks, options = {}) {
11313
10194
  }
11314
10195
 
11315
10196
  // src/chat/tools/web/search.ts
11316
- import { z as z39 } from "zod";
10197
+ import { z as z38 } from "zod";
11317
10198
  import { generateText } from "ai";
11318
10199
  import { createGatewayProvider } from "@ai-sdk/gateway";
11319
10200
  var SEARCH_TIMEOUT_MS = 6e4;
@@ -11362,9 +10243,9 @@ function createWebSearchTool(modelId, override) {
11362
10243
  destructiveHint: false,
11363
10244
  openWorldHint: true
11364
10245
  },
11365
- inputSchema: z39.object({
11366
- query: z39.string().min(1).max(500).describe("Search query."),
11367
- max_results: z39.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
10246
+ inputSchema: z38.object({
10247
+ query: z38.string().min(1).max(500).describe("Search query."),
10248
+ max_results: z38.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
11368
10249
  }),
11369
10250
  outputSchema: juniorToolResultSchema,
11370
10251
  execute: async ({ query, max_results }) => {
@@ -11433,14 +10314,14 @@ function createWebSearchTool(modelId, override) {
11433
10314
  }
11434
10315
 
11435
10316
  // src/chat/tools/sandbox/write-file.ts
11436
- import { z as z40 } from "zod";
10317
+ import { z as z39 } from "zod";
11437
10318
  function createWriteFileTool() {
11438
10319
  return zodTool({
11439
10320
  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.",
11440
10321
  executionMode: "sequential",
11441
- inputSchema: z40.object({
11442
- path: z40.string().min(1).describe("Path to write in the sandbox workspace."),
11443
- content: z40.string().describe("UTF-8 file content to write.")
10322
+ inputSchema: z39.object({
10323
+ path: z39.string().min(1).describe("Path to write in the sandbox workspace."),
10324
+ content: z39.string().describe("UTF-8 file content to write.")
11444
10325
  }),
11445
10326
  outputSchema: juniorToolResultSchema,
11446
10327
  execute: async () => {
@@ -11799,11 +10680,11 @@ function replaceGeneratedTextContent(content, text) {
11799
10680
  ...withoutFirstGeneratedTextContent(content)
11800
10681
  ];
11801
10682
  }
11802
- function isRecord4(value) {
10683
+ function isRecord3(value) {
11803
10684
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
11804
10685
  }
11805
10686
  function hasContinuation(value) {
11806
- return isRecord4(value) && isRecord4(value.continuation);
10687
+ return isRecord3(value) && isRecord3(value.continuation);
11807
10688
  }
11808
10689
  function stringField(record, key) {
11809
10690
  const value = record[key];
@@ -11818,7 +10699,7 @@ function stringListField(record, key) {
11818
10699
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
11819
10700
  }
11820
10701
  function accountText(value) {
11821
- if (!isRecord4(value)) {
10702
+ if (!isRecord3(value)) {
11822
10703
  return void 0;
11823
10704
  }
11824
10705
  const label = stringField(value, "label") || stringField(value, "id");
@@ -11829,7 +10710,7 @@ function accountText(value) {
11829
10710
  return id && id !== label ? `${label} (${id})` : label;
11830
10711
  }
11831
10712
  function upstreamPermissionDeniedText(value) {
11832
- if (!isRecord4(value) || !isRecord4(value.permission_denied)) {
10713
+ if (!isRecord3(value) || !isRecord3(value.permission_denied)) {
11833
10714
  return void 0;
11834
10715
  }
11835
10716
  const signal = value.permission_denied;
@@ -11844,7 +10725,7 @@ function upstreamPermissionDeniedText(value) {
11844
10725
  if (!provider || !message || !upstreamHost || !upstreamPath || !status) {
11845
10726
  return void 0;
11846
10727
  }
11847
- const grant = isRecord4(signal.grant) ? signal.grant : {};
10728
+ const grant = isRecord3(signal.grant) ? signal.grant : {};
11848
10729
  const grantName = stringField(grant, "name");
11849
10730
  const grantAccess = stringField(grant, "access");
11850
10731
  const grantReason = stringField(grant, "reason");
@@ -11967,9 +10848,6 @@ function pluginAuthRequiredSignal(details) {
11967
10848
  ...parsedSignal.authorization ? { authorization: parsedSignal.authorization } : {}
11968
10849
  };
11969
10850
  }
11970
- function authorizationId(args) {
11971
- return `${args.sessionId}:${args.kind}:${args.provider}`;
11972
- }
11973
10851
  function createPluginAuthOrchestration(input) {
11974
10852
  let pendingPause;
11975
10853
  const startAuthorizationPause = async (provider, options) => {
@@ -12006,7 +10884,8 @@ function createPluginAuthOrchestration(input) {
12006
10884
  threadTs: input.threadTs,
12007
10885
  ...options?.scope ? { scope: options.scope } : {},
12008
10886
  resumeConversationId: input.conversationId,
12009
- resumeSessionId: input.sessionId
10887
+ resumeSessionId: input.sessionId,
10888
+ authorization: input.authorization
12010
10889
  });
12011
10890
  if (!oauthResult.ok) {
12012
10891
  throw new Error(oauthResult.error);
@@ -12184,17 +11063,17 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
12184
11063
  }
12185
11064
 
12186
11065
  // src/chat/tools/execute-tool.ts
12187
- import { z as z41 } from "zod";
11066
+ import { z as z40 } from "zod";
12188
11067
  var EXECUTE_TOOL_NAME = "executeTool";
12189
11068
  function createExecuteToolTool() {
12190
11069
  return zodTool({
12191
11070
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
12192
11071
  executionMode: "sequential",
12193
- inputSchema: z41.object({
12194
- tool_name: z41.string().min(1).describe(
11072
+ inputSchema: z40.object({
11073
+ tool_name: z40.string().min(1).describe(
12195
11074
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
12196
11075
  ),
12197
- arguments: z41.record(z41.string(), z41.unknown()).describe(
11076
+ arguments: z40.record(z40.string(), z40.unknown()).describe(
12198
11077
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
12199
11078
  ).optional()
12200
11079
  }).strict(),
@@ -12234,42 +11113,42 @@ function planToolExposure(tools) {
12234
11113
  }
12235
11114
 
12236
11115
  // src/chat/tools/search-tools.ts
12237
- import { z as z42 } from "zod";
11116
+ import { z as z41 } from "zod";
12238
11117
  var SEARCH_TOOLS_NAME = "searchTools";
12239
11118
  var DEFAULT_MAX_RESULTS2 = 5;
12240
11119
  var MAX_RESULTS3 = 20;
12241
11120
  var MODEL_VISIBLE_DESCRIPTION_CAP = 180;
12242
- var searchToolsSourceSchema = z42.object({
12243
- id: z42.string(),
12244
- description: z42.string()
11121
+ var searchToolsSourceSchema = z41.object({
11122
+ id: z41.string(),
11123
+ description: z41.string()
12245
11124
  }).strict();
12246
- var toolCallExampleSchema = z42.object({
12247
- tool_name: z42.string(),
12248
- arguments: z42.record(z42.string(), z42.string())
11125
+ var toolCallExampleSchema = z41.object({
11126
+ tool_name: z41.string(),
11127
+ arguments: z41.record(z41.string(), z41.string())
12249
11128
  }).strict();
12250
- var searchToolsToolSchema = z42.object({
12251
- tool_name: z42.string(),
12252
- description: z42.string(),
12253
- exposure: z42.enum(["direct", "deferred", "modelOnly", "hidden"]),
12254
- source: z42.string().optional(),
12255
- signature: z42.string(),
11129
+ var searchToolsToolSchema = z41.object({
11130
+ tool_name: z41.string(),
11131
+ description: z41.string(),
11132
+ exposure: z41.enum(["direct", "deferred", "modelOnly", "hidden"]),
11133
+ source: z41.string().optional(),
11134
+ signature: z41.string(),
12256
11135
  call: toolCallExampleSchema,
12257
- input_schema: z42.unknown(),
12258
- input_schema_summary: z42.string(),
12259
- call_notes: z42.array(z42.string()),
12260
- annotations: z42.record(z42.string(), z42.unknown())
11136
+ input_schema: z41.unknown(),
11137
+ input_schema_summary: z41.string(),
11138
+ call_notes: z41.array(z41.string()),
11139
+ annotations: z41.record(z41.string(), z41.unknown())
12261
11140
  }).strict();
12262
11141
  var searchToolsOutputSchema = juniorToolResultSchema.extend({
12263
- query: z42.string().nullable(),
12264
- source: z42.string().nullable(),
12265
- sources: z42.array(searchToolsSourceSchema),
12266
- total_catalog_tools: z42.number().int().nonnegative(),
12267
- total_eligible_tools: z42.number().int().nonnegative(),
12268
- total_matches: z42.number().int().nonnegative(),
12269
- returned_tools: z42.number().int().nonnegative(),
12270
- execution_tool: z42.literal("executeTool"),
11142
+ query: z41.string().nullable(),
11143
+ source: z41.string().nullable(),
11144
+ sources: z41.array(searchToolsSourceSchema),
11145
+ total_catalog_tools: z41.number().int().nonnegative(),
11146
+ total_eligible_tools: z41.number().int().nonnegative(),
11147
+ total_matches: z41.number().int().nonnegative(),
11148
+ returned_tools: z41.number().int().nonnegative(),
11149
+ execution_tool: z41.literal("executeTool"),
12271
11150
  execution_example: toolCallExampleSchema,
12272
- tools: z42.array(searchToolsToolSchema)
11151
+ tools: z41.array(searchToolsToolSchema)
12273
11152
  }).strict();
12274
11153
  function normalize2(value) {
12275
11154
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
@@ -12461,14 +11340,14 @@ function createSearchToolsTool(catalogTools) {
12461
11340
  return zodTool({
12462
11341
  description: renderSearchToolsDescription(knownSources),
12463
11342
  annotations: { readOnlyHint: true, destructiveHint: false },
12464
- inputSchema: z42.object({
12465
- query: z42.string().nullable().describe(
11343
+ inputSchema: z41.object({
11344
+ query: z41.string().nullable().describe(
12466
11345
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
12467
11346
  ).optional(),
12468
- source: z42.string().nullable().describe(
11347
+ source: z41.string().nullable().describe(
12469
11348
  "Optional source id to search within, such as a plugin source returned in sources."
12470
11349
  ).optional(),
12471
- max_results: z42.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
11350
+ max_results: z41.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
12472
11351
  }).strict(),
12473
11352
  outputSchema: searchToolsOutputSchema,
12474
11353
  privateTraceResult: (result) => ({
@@ -12754,542 +11633,12 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12754
11633
  }));
12755
11634
  }
12756
11635
 
12757
- // src/chat/mcp/oauth.ts
12758
- import { randomUUID as randomUUID3 } from "crypto";
12759
- import { StreamableHTTPClientTransport as StreamableHTTPClientTransport2 } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
12760
-
12761
- // src/chat/mcp/auth-store.ts
12762
- import {
12763
- sourceSchema as sourceSchema2
12764
- } from "@sentry/junior-plugin-api";
12765
- var MCP_AUTH_SESSION_PREFIX = "junior:mcp_oauth_attempt:v2";
12766
- var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
12767
- var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_oauth_attempt_index:v2";
12768
- var MCP_SERVER_SESSION_PREFIX = "junior:mcp_server_session";
12769
- var MCP_AUTH_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
12770
- var MCP_AUTH_CREDENTIALS_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
12771
- var MCP_SERVER_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
12772
- function sessionKey(authSessionId) {
12773
- return `${MCP_AUTH_SESSION_PREFIX}:${authSessionId}`;
12774
- }
12775
- function credentialsKey(userId, provider) {
12776
- return `${MCP_AUTH_CREDENTIALS_PREFIX}:${userId}:${provider}`;
12777
- }
12778
- function sessionIndexKey(userId, provider) {
12779
- return `${MCP_AUTH_SESSION_INDEX_PREFIX}:${userId}:${provider}`;
12780
- }
12781
- function serverSessionKey(userId, provider) {
12782
- return `${MCP_SERVER_SESSION_PREFIX}:${userId}:${provider}`;
12783
- }
12784
- function parseSessionIndex(value) {
12785
- if (typeof value !== "string") {
12786
- return [];
12787
- }
12788
- try {
12789
- const parsed = JSON.parse(value);
12790
- if (!Array.isArray(parsed)) {
12791
- return [];
12792
- }
12793
- return [
12794
- ...new Set(parsed.filter((id) => typeof id === "string"))
12795
- ];
12796
- } catch {
12797
- return [];
12798
- }
12799
- }
12800
- function parseMcpAuthSession(value) {
12801
- if (typeof value !== "string") {
12802
- return void 0;
12803
- }
12804
- try {
12805
- const parsed = JSON.parse(value);
12806
- if (!isRecord(parsed)) {
12807
- return void 0;
12808
- }
12809
- if (parsed.schemaVersion !== 2 || typeof parsed.authSessionId !== "string" || typeof parsed.provider !== "string" || typeof parsed.userId !== "string" || typeof parsed.conversationId !== "string" || typeof parsed.sessionId !== "string" || typeof parsed.userMessage !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.updatedAtMs !== "number") {
12810
- return void 0;
12811
- }
12812
- const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
12813
- if (parsed.destination !== void 0 && !destination) {
12814
- return void 0;
12815
- }
12816
- const source = parsed.source === void 0 ? void 0 : sourceSchema2.safeParse(parsed.source);
12817
- if (parsed.source !== void 0 && (!source || !source.success)) {
12818
- return void 0;
12819
- }
12820
- return {
12821
- schemaVersion: 2,
12822
- authSessionId: parsed.authSessionId,
12823
- provider: parsed.provider,
12824
- userId: parsed.userId,
12825
- conversationId: parsed.conversationId,
12826
- ...destination ? { destination } : {},
12827
- ...source?.success ? { source: source.data } : {},
12828
- sessionId: parsed.sessionId,
12829
- userMessage: parsed.userMessage,
12830
- createdAtMs: parsed.createdAtMs,
12831
- updatedAtMs: parsed.updatedAtMs,
12832
- ...typeof parsed.channelId === "string" ? { channelId: parsed.channelId } : {},
12833
- ...typeof parsed.threadTs === "string" ? { threadTs: parsed.threadTs } : {},
12834
- ...typeof parsed.toolChannelId === "string" ? { toolChannelId: parsed.toolChannelId } : {},
12835
- ...isRecord(parsed.configuration) ? { configuration: parsed.configuration } : {},
12836
- ...isRecord(parsed.artifactState) ? { artifactState: parsed.artifactState } : {},
12837
- ...typeof parsed.authorizationUrl === "string" ? { authorizationUrl: parsed.authorizationUrl } : {},
12838
- ...typeof parsed.codeVerifier === "string" ? { codeVerifier: parsed.codeVerifier } : {}
12839
- };
12840
- } catch {
12841
- return void 0;
12842
- }
12843
- }
12844
- function parseServerSession(value) {
12845
- if (typeof value !== "string") {
12846
- return void 0;
12847
- }
12848
- try {
12849
- const parsed = JSON.parse(value);
12850
- if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || typeof parsed.updatedAtMs !== "number") {
12851
- return void 0;
12852
- }
12853
- return {
12854
- sessionId: parsed.sessionId,
12855
- updatedAtMs: parsed.updatedAtMs
12856
- };
12857
- } catch {
12858
- return void 0;
12859
- }
12860
- }
12861
- function parseStoredCredentials(value) {
12862
- if (typeof value !== "string") {
12863
- return void 0;
12864
- }
12865
- try {
12866
- const parsed = JSON.parse(value);
12867
- if (!isRecord(parsed)) {
12868
- return void 0;
12869
- }
12870
- return {
12871
- ...isRecord(parsed.clientInformation) ? {
12872
- clientInformation: parsed.clientInformation
12873
- } : {},
12874
- ...isRecord(parsed.discoveryState) ? {
12875
- discoveryState: parsed.discoveryState
12876
- } : {},
12877
- ...isRecord(parsed.tokens) ? { tokens: parsed.tokens } : {}
12878
- };
12879
- } catch {
12880
- return void 0;
12881
- }
12882
- }
12883
- async function getConnectedStateAdapter() {
12884
- const stateAdapter = getStateAdapter();
12885
- await stateAdapter.connect();
12886
- return stateAdapter;
12887
- }
12888
- async function getMcpAuthSession(authSessionId) {
12889
- const stateAdapter = await getConnectedStateAdapter();
12890
- return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
12891
- }
12892
- async function putMcpAuthSession(session, ttlMs2 = MCP_AUTH_SESSION_TTL_MS) {
12893
- const stateAdapter = await getConnectedStateAdapter();
12894
- await stateAdapter.set(
12895
- sessionKey(session.authSessionId),
12896
- JSON.stringify(session),
12897
- ttlMs2
12898
- );
12899
- const nextIndex = parseSessionIndex(
12900
- await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
12901
- );
12902
- if (!nextIndex.includes(session.authSessionId)) {
12903
- nextIndex.push(session.authSessionId);
12904
- }
12905
- await stateAdapter.set(
12906
- sessionIndexKey(session.userId, session.provider),
12907
- JSON.stringify(nextIndex),
12908
- ttlMs2
12909
- );
12910
- }
12911
- async function patchMcpAuthSession(authSessionId, patch) {
12912
- const current = await getMcpAuthSession(authSessionId);
12913
- if (!current) {
12914
- throw new Error(`Unknown MCP auth session: ${authSessionId}`);
12915
- }
12916
- const next = {
12917
- ...current,
12918
- ...patch,
12919
- schemaVersion: 2,
12920
- authSessionId: current.authSessionId,
12921
- provider: current.provider,
12922
- userId: current.userId,
12923
- conversationId: current.conversationId,
12924
- ...current.destination ? { destination: current.destination } : {},
12925
- sessionId: current.sessionId,
12926
- userMessage: current.userMessage,
12927
- createdAtMs: current.createdAtMs,
12928
- updatedAtMs: Date.now()
12929
- };
12930
- await putMcpAuthSession(next);
12931
- return next;
12932
- }
12933
- async function deleteMcpAuthSession(authSessionId) {
12934
- const stateAdapter = await getConnectedStateAdapter();
12935
- const current = parseMcpAuthSession(
12936
- await stateAdapter.get(sessionKey(authSessionId))
12937
- );
12938
- await stateAdapter.delete(sessionKey(authSessionId));
12939
- if (!current) {
12940
- return;
12941
- }
12942
- const nextIndex = parseSessionIndex(
12943
- await stateAdapter.get(sessionIndexKey(current.userId, current.provider))
12944
- ).filter((id) => id !== authSessionId);
12945
- if (nextIndex.length > 0) {
12946
- await stateAdapter.set(
12947
- sessionIndexKey(current.userId, current.provider),
12948
- JSON.stringify(nextIndex),
12949
- MCP_AUTH_SESSION_TTL_MS
12950
- );
12951
- return;
12952
- }
12953
- await stateAdapter.delete(sessionIndexKey(current.userId, current.provider));
12954
- }
12955
- async function deleteMcpAuthSessionsForUserProvider(userId, provider) {
12956
- const stateAdapter = await getConnectedStateAdapter();
12957
- const indexKey = sessionIndexKey(userId, provider);
12958
- const authSessionIds = parseSessionIndex(await stateAdapter.get(indexKey));
12959
- for (const authSessionId of authSessionIds) {
12960
- await stateAdapter.delete(sessionKey(authSessionId));
12961
- }
12962
- await stateAdapter.delete(indexKey);
12963
- }
12964
- async function getMcpStoredOAuthCredentials(userId, provider) {
12965
- const stateAdapter = await getConnectedStateAdapter();
12966
- return parseStoredCredentials(
12967
- await stateAdapter.get(credentialsKey(userId, provider))
12968
- );
12969
- }
12970
- async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs2 = MCP_AUTH_CREDENTIALS_TTL_MS) {
12971
- const stateAdapter = await getConnectedStateAdapter();
12972
- await stateAdapter.set(
12973
- credentialsKey(userId, provider),
12974
- JSON.stringify(value),
12975
- ttlMs2
12976
- );
12977
- }
12978
- async function deleteMcpStoredOAuthCredentials(userId, provider) {
12979
- const stateAdapter = await getConnectedStateAdapter();
12980
- await stateAdapter.delete(credentialsKey(userId, provider));
12981
- }
12982
- async function getMcpServerSessionId(userId, provider) {
12983
- const stateAdapter = await getConnectedStateAdapter();
12984
- return parseServerSession(
12985
- await stateAdapter.get(serverSessionKey(userId, provider))
12986
- )?.sessionId;
12987
- }
12988
- async function putMcpServerSessionId(userId, provider, sessionId, ttlMs2 = MCP_SERVER_SESSION_TTL_MS) {
12989
- const stateAdapter = await getConnectedStateAdapter();
12990
- await stateAdapter.set(
12991
- serverSessionKey(userId, provider),
12992
- JSON.stringify({
12993
- sessionId,
12994
- updatedAtMs: Date.now()
12995
- }),
12996
- ttlMs2
12997
- );
12998
- }
12999
- async function deleteMcpServerSessionId(userId, provider) {
13000
- const stateAdapter = await getConnectedStateAdapter();
13001
- await stateAdapter.delete(serverSessionKey(userId, provider));
13002
- }
13003
-
13004
- // src/chat/mcp/oauth-provider.ts
13005
- function createClientMetadata(callbackUrl) {
13006
- return {
13007
- client_name: "Junior MCP Client",
13008
- redirect_uris: [callbackUrl],
13009
- grant_types: ["authorization_code", "refresh_token"],
13010
- response_types: ["code"],
13011
- token_endpoint_auth_method: "none"
13012
- };
13013
- }
13014
- function clientAllowsRedirectUri(clientInformation, callbackUrl) {
13015
- const redirectUris = clientInformation.redirect_uris;
13016
- if (!Array.isArray(redirectUris)) {
13017
- return true;
13018
- }
13019
- return redirectUris.includes(callbackUrl);
13020
- }
13021
- var StateBackedMcpOAuthClientProvider = class {
13022
- constructor(authSessionId, callbackUrl, sessionContext, runCredentialMutation) {
13023
- this.authSessionId = authSessionId;
13024
- this.callbackUrl = callbackUrl;
13025
- this.sessionContext = sessionContext;
13026
- this.runCredentialMutation = runCredentialMutation;
13027
- this.clientMetadata = createClientMetadata(callbackUrl);
13028
- }
13029
- authSessionId;
13030
- callbackUrl;
13031
- sessionContext;
13032
- runCredentialMutation;
13033
- clientMetadata;
13034
- get redirectUrl() {
13035
- return this.callbackUrl;
13036
- }
13037
- async state() {
13038
- return this.authSessionId;
13039
- }
13040
- async clientInformation() {
13041
- const session = await this.getCredentialContext();
13042
- const credentials = await getMcpStoredOAuthCredentials(
13043
- session.userId,
13044
- session.provider
13045
- );
13046
- const clientInformation = credentials?.clientInformation;
13047
- if (!clientInformation) {
13048
- return void 0;
13049
- }
13050
- if (clientAllowsRedirectUri(clientInformation, this.callbackUrl)) {
13051
- return clientInformation;
13052
- }
13053
- return void 0;
13054
- }
13055
- async saveClientInformation(clientInformation) {
13056
- await this.mutateCredentials(async () => {
13057
- const session = await this.getCredentialContext();
13058
- const credentials = await getMcpStoredOAuthCredentials(
13059
- session.userId,
13060
- session.provider
13061
- ) ?? {};
13062
- await putMcpStoredOAuthCredentials(session.userId, session.provider, {
13063
- ...credentials,
13064
- clientInformation
13065
- });
13066
- });
13067
- }
13068
- async tokens() {
13069
- const session = await this.getCredentialContext();
13070
- const credentials = await getMcpStoredOAuthCredentials(
13071
- session.userId,
13072
- session.provider
13073
- );
13074
- return credentials?.tokens;
13075
- }
13076
- async saveTokens(tokens) {
13077
- await this.mutateCredentials(async () => {
13078
- const session = await this.getCredentialContext();
13079
- const credentials = await getMcpStoredOAuthCredentials(
13080
- session.userId,
13081
- session.provider
13082
- ) ?? {};
13083
- await putMcpStoredOAuthCredentials(session.userId, session.provider, {
13084
- ...credentials,
13085
- tokens
13086
- });
13087
- });
13088
- }
13089
- async redirectToAuthorization(authorizationUrl) {
13090
- const existing = await getMcpAuthSession(this.authSessionId);
13091
- if (existing?.authorizationUrl && existing.authorizationUrl !== authorizationUrl.toString()) {
13092
- throw new Error("MCP OAuth authorization attempt is already initialized");
13093
- }
13094
- await this.ensureSession({
13095
- authorizationUrl: authorizationUrl.toString()
13096
- });
13097
- }
13098
- async saveCodeVerifier(codeVerifier) {
13099
- const existing = await getMcpAuthSession(this.authSessionId);
13100
- if (existing?.codeVerifier && existing.codeVerifier !== codeVerifier) {
13101
- throw new Error("MCP OAuth authorization attempt is already initialized");
13102
- }
13103
- await this.ensureSession({ codeVerifier });
13104
- }
13105
- async codeVerifier() {
13106
- const session = await this.requireSession();
13107
- if (!session.codeVerifier) {
13108
- throw new Error("Missing MCP OAuth code verifier");
13109
- }
13110
- return session.codeVerifier;
13111
- }
13112
- async saveDiscoveryState(state) {
13113
- await this.mutateCredentials(async () => {
13114
- const session = await this.getCredentialContext();
13115
- const credentials = await getMcpStoredOAuthCredentials(
13116
- session.userId,
13117
- session.provider
13118
- ) ?? {};
13119
- await putMcpStoredOAuthCredentials(session.userId, session.provider, {
13120
- ...credentials,
13121
- discoveryState: state
13122
- });
13123
- });
13124
- }
13125
- async discoveryState() {
13126
- const session = await this.getCredentialContext();
13127
- const credentials = await getMcpStoredOAuthCredentials(
13128
- session.userId,
13129
- session.provider
13130
- );
13131
- return credentials?.discoveryState;
13132
- }
13133
- async invalidateCredentials(scope) {
13134
- await this.mutateCredentials(async () => {
13135
- const session = await this.getCredentialContext();
13136
- const credentials = await getMcpStoredOAuthCredentials(
13137
- session.userId,
13138
- session.provider
13139
- ) ?? {};
13140
- await putMcpStoredOAuthCredentials(session.userId, session.provider, {
13141
- ...scope === "tokens" || scope === "all" ? {} : credentials.tokens ? { tokens: credentials.tokens } : {},
13142
- ...scope === "client" || scope === "all" ? {} : credentials.clientInformation ? { clientInformation: credentials.clientInformation } : {},
13143
- ...scope === "discovery" || scope === "all" ? {} : credentials.discoveryState ? { discoveryState: credentials.discoveryState } : {}
13144
- });
13145
- });
13146
- }
13147
- async getMcpServerSessionId() {
13148
- const session = await this.getCredentialContext();
13149
- return await getMcpServerSessionId(session.userId, session.provider);
13150
- }
13151
- async saveMcpServerSessionId(sessionId) {
13152
- await this.mutateCredentials(async () => {
13153
- const session = await this.getCredentialContext();
13154
- if (!sessionId) {
13155
- await deleteMcpServerSessionId(session.userId, session.provider);
13156
- return;
13157
- }
13158
- await putMcpServerSessionId(session.userId, session.provider, sessionId);
13159
- });
13160
- }
13161
- /** Route shared credential writes through callback-owned freshness control. */
13162
- async mutateCredentials(mutation) {
13163
- return this.runCredentialMutation ? await this.runCredentialMutation(mutation) : await mutation();
13164
- }
13165
- async getCredentialContext() {
13166
- return this.sessionContext ?? await this.requireSession();
13167
- }
13168
- async ensureSession(patch) {
13169
- const existing = await getMcpAuthSession(this.authSessionId);
13170
- if (existing) {
13171
- return await patchMcpAuthSession(this.authSessionId, patch);
13172
- }
13173
- if (!this.sessionContext) {
13174
- throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
13175
- }
13176
- const now = Date.now();
13177
- const nextSession = {
13178
- schemaVersion: 2,
13179
- authSessionId: this.authSessionId,
13180
- ...this.sessionContext,
13181
- ...patch,
13182
- createdAtMs: now,
13183
- updatedAtMs: now
13184
- };
13185
- await putMcpAuthSession(nextSession);
13186
- return nextSession;
13187
- }
13188
- async requireSession() {
13189
- const session = await getMcpAuthSession(this.authSessionId);
13190
- if (!session) {
13191
- throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
13192
- }
13193
- return session;
13194
- }
13195
- };
13196
-
13197
- // src/chat/mcp/oauth.ts
13198
- function getMcpOAuthCallbackPath(provider) {
13199
- return `/api/oauth/callback/mcp/${provider}`;
13200
- }
13201
- function requirePluginWithMcp(provider) {
13202
- const plugin = pluginCatalogRuntime.getDefinition(provider);
13203
- if (!plugin?.manifest.mcp) {
13204
- throw new Error(`Plugin "${provider}" does not support MCP`);
13205
- }
13206
- return plugin;
13207
- }
13208
- async function createMcpOAuthClientProvider(input) {
13209
- requirePluginWithMcp(input.provider);
13210
- const baseUrl = resolveBaseUrl();
13211
- if (!baseUrl) {
13212
- throw new Error(
13213
- "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
13214
- );
13215
- }
13216
- const authSessionId = randomUUID3();
13217
- return new StateBackedMcpOAuthClientProvider(
13218
- authSessionId,
13219
- `${baseUrl}${getMcpOAuthCallbackPath(input.provider)}`,
13220
- {
13221
- provider: input.provider,
13222
- userId: input.userId,
13223
- conversationId: input.conversationId,
13224
- ...input.destination ? { destination: input.destination } : {},
13225
- ...input.source ? { source: input.source } : {},
13226
- sessionId: input.sessionId,
13227
- userMessage: input.userMessage,
13228
- ...input.channelId ? { channelId: input.channelId } : {},
13229
- ...input.threadTs ? { threadTs: input.threadTs } : {},
13230
- ...input.toolChannelId ? { toolChannelId: input.toolChannelId } : {},
13231
- ...input.configuration ? { configuration: input.configuration } : {},
13232
- ...input.artifactState ? { artifactState: input.artifactState } : {}
13233
- }
13234
- );
13235
- }
13236
- async function finalizeMcpAuthorization(provider, authSessionId, authorizationCode, runCredentialMutation) {
13237
- const plugin = requirePluginWithMcp(provider);
13238
- const mcp = plugin.manifest.mcp;
13239
- if (!mcp) {
13240
- throw new Error(`Plugin "${provider}" does not support MCP`);
13241
- }
13242
- const session = await getMcpAuthSession(authSessionId);
13243
- if (!session) {
13244
- throw new Error(`Unknown MCP auth session: ${authSessionId}`);
13245
- }
13246
- if (session.provider !== provider) {
13247
- throw new Error(
13248
- `MCP auth session provider mismatch: expected "${provider}", got "${session.provider}"`
13249
- );
13250
- }
13251
- const baseUrl = resolveBaseUrl();
13252
- if (!baseUrl) {
13253
- throw new Error(
13254
- "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
13255
- );
13256
- }
13257
- const callbackUrl = `${baseUrl}${getMcpOAuthCallbackPath(provider)}`;
13258
- const authProvider = new StateBackedMcpOAuthClientProvider(
13259
- authSessionId,
13260
- callbackUrl,
13261
- void 0,
13262
- runCredentialMutation
13263
- );
13264
- const requestInit = {};
13265
- if (mcp.headers && Object.keys(mcp.headers).length > 0) {
13266
- requestInit.headers = new Headers(mcp.headers);
13267
- }
13268
- const transport = new StreamableHTTPClientTransport2(new URL(mcp.url), {
13269
- ...Object.keys(requestInit).length > 0 ? { requestInit } : {},
13270
- authProvider
13271
- });
13272
- try {
13273
- await transport.finishAuth(authorizationCode);
13274
- } finally {
13275
- await transport.close();
13276
- }
13277
- const nextSession = await getMcpAuthSession(authSessionId);
13278
- if (!nextSession) {
13279
- throw new Error(`Unknown MCP auth session: ${authSessionId}`);
13280
- }
13281
- return nextSession;
13282
- }
13283
-
13284
11636
  // src/chat/services/mcp-auth-orchestration.ts
13285
11637
  var McpAuthorizationPauseError = class extends AuthorizationPauseError {
13286
11638
  constructor(provider, providerDisplayName, disposition, requestText) {
13287
11639
  super("mcp", provider, providerDisplayName, disposition, requestText);
13288
11640
  }
13289
11641
  };
13290
- function authorizationId2(args) {
13291
- return `${args.sessionId}:${args.kind}:${args.provider}`;
13292
- }
13293
11642
  function createMcpAuthOrchestration(input) {
13294
11643
  let pendingPause;
13295
11644
  const authSessionIdsByProvider = /* @__PURE__ */ new Map();
@@ -13314,7 +11663,8 @@ function createMcpAuthOrchestration(input) {
13314
11663
  ...input.threadTs ? { threadTs: input.threadTs } : {},
13315
11664
  ...input.toolChannelId ? { toolChannelId: input.toolChannelId } : {},
13316
11665
  configuration: input.getConfiguration(),
13317
- artifactState: input.getArtifactState()
11666
+ artifactState: input.getArtifactState(),
11667
+ createAuthorizationState: input.authorization?.createState
13318
11668
  });
13319
11669
  authSessionIdsByProvider.set(plugin.manifest.name, provider.authSessionId);
13320
11670
  return provider;
@@ -13374,15 +11724,16 @@ function createMcpAuthOrchestration(input) {
13374
11724
  );
13375
11725
  }
13376
11726
  await recordPendingAuth(nextPendingAuth);
13377
- const delivery = await deliverPrivateMessage({
11727
+ const authorizationRequest = {
11728
+ authorizationUrl: authSession.authorizationUrl,
11729
+ label: `Click here to link your ${providerLabel} MCP access`,
11730
+ completionText: "Once you've authorized, Junior will continue automatically."
11731
+ };
11732
+ const delivery = await deliverOAuthAuthorization(authorizationRequest, {
11733
+ authorization: input.authorization,
13378
11734
  channelId: authSession.channelId,
13379
11735
  threadTs: authSession.threadTs,
13380
- userId: authSession.userId,
13381
- text: formatOAuthAuthorizationMessage({
13382
- authorizationUrl: authSession.authorizationUrl,
13383
- label: `Click here to link your ${providerLabel} MCP access`,
13384
- completionText: "Once you've authorized, this thread will continue automatically."
13385
- })
11736
+ userId: authSession.userId
13386
11737
  });
13387
11738
  if (!delivery) {
13388
11739
  await deleteMcpAuthSession(authSessionId);
@@ -13405,7 +11756,7 @@ function createMcpAuthOrchestration(input) {
13405
11756
  kind: "mcp",
13406
11757
  provider,
13407
11758
  actorId,
13408
- authorizationId: authorizationId2({
11759
+ authorizationId: authorizationId({
13409
11760
  kind: "mcp",
13410
11761
  provider,
13411
11762
  sessionId
@@ -13429,197 +11780,7 @@ function createMcpAuthOrchestration(input) {
13429
11780
  }
13430
11781
 
13431
11782
  // src/chat/egress/plugin.ts
13432
- import { randomUUID as randomUUID4 } from "crypto";
13433
-
13434
- // src/chat/plugins/credential-hooks.ts
13435
- import {
13436
- pluginAuthorizationSchema as pluginAuthorizationSchema2,
13437
- pluginCredentialResultSchema,
13438
- pluginGrantSchema as pluginGrantSchema2,
13439
- pluginProviderAccountSchema as pluginProviderAccountSchema2
13440
- } from "@sentry/junior-plugin-api";
13441
- function parseSchema(schema, value, message) {
13442
- const result = schema.safeParse(value);
13443
- if (!result.success) {
13444
- throw new Error(message);
13445
- }
13446
- return result.data;
13447
- }
13448
- function parseAuthorization(value, pluginName) {
13449
- if (value === void 0) {
13450
- return void 0;
13451
- }
13452
- const authorization = parseSchema(
13453
- pluginAuthorizationSchema2,
13454
- value,
13455
- `Plugin "${pluginName}" grant authorization is invalid`
13456
- );
13457
- if (authorization.provider !== pluginName) {
13458
- throw new Error(
13459
- `Plugin "${pluginName}" grant authorization provider must match the issuing plugin`
13460
- );
13461
- }
13462
- return authorization;
13463
- }
13464
- function parseGrant(value, pluginName) {
13465
- return parseSchema(
13466
- pluginGrantSchema2,
13467
- value,
13468
- `Plugin "${pluginName}" grantForEgress returned an invalid grant`
13469
- );
13470
- }
13471
- function pluginFor(provider) {
13472
- return getPlugins().find((candidate) => candidate.manifest.name === provider);
13473
- }
13474
- function basePluginContext(plugin) {
13475
- const pluginName = plugin.manifest.name;
13476
- return {
13477
- plugin: { name: pluginName },
13478
- log: createPluginLogger(pluginName),
13479
- db: getDb()
13480
- };
13481
- }
13482
- function parseCredentialResult(value, pluginName) {
13483
- const result = parseSchema(
13484
- pluginCredentialResultSchema,
13485
- value,
13486
- `Plugin "${pluginName}" issueCredential result is invalid`
13487
- );
13488
- if (result.type === "lease") {
13489
- parseAuthorization(result.lease.authorization, pluginName);
13490
- return result;
13491
- }
13492
- if (result.type === "unavailable") {
13493
- return result;
13494
- }
13495
- parseAuthorization(result.authorization, pluginName);
13496
- return result;
13497
- }
13498
- async function selectPluginGrant(input) {
13499
- const plugin = pluginFor(input.provider);
13500
- const hook = plugin?.hooks?.grantForEgress;
13501
- if (!plugin || !hook) {
13502
- return void 0;
13503
- }
13504
- const result = await hook({
13505
- ...basePluginContext(plugin),
13506
- request: {
13507
- ...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
13508
- method: input.method,
13509
- ...input.operation ? { operation: input.operation } : {},
13510
- url: input.upstreamUrl.toString()
13511
- }
13512
- });
13513
- return result === void 0 ? void 0 : parseGrant(result, plugin.manifest.name);
13514
- }
13515
- async function onPluginEgressResponse(input) {
13516
- const plugin = pluginFor(input.provider);
13517
- const hook = plugin?.hooks?.onEgressResponse;
13518
- if (!plugin || !hook) {
13519
- return {};
13520
- }
13521
- let permissionDenied;
13522
- await hook({
13523
- ...basePluginContext(plugin),
13524
- grant: input.grant,
13525
- permissionDenied(message) {
13526
- const trimmed = message.trim();
13527
- if (!trimmed) {
13528
- throw new Error(
13529
- `Plugin "${plugin.manifest.name}" onEgressResponse permissionDenied message is empty`
13530
- );
13531
- }
13532
- permissionDenied = { message: trimmed };
13533
- },
13534
- request: {
13535
- method: input.method,
13536
- ...input.operation ? { operation: input.operation } : {},
13537
- url: input.upstreamUrl.toString()
13538
- },
13539
- response: input.response
13540
- });
13541
- return permissionDenied ? { permissionDenied } : {};
13542
- }
13543
- function hasEgressCredentialHooks(provider) {
13544
- const hooks = pluginFor(provider)?.hooks;
13545
- return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
13546
- }
13547
- async function resolvePluginOAuthAccount(input) {
13548
- const plugin = pluginFor(input.provider);
13549
- const hook = plugin?.hooks?.resolveOAuthAccount;
13550
- if (!plugin || !hook) {
13551
- return void 0;
13552
- }
13553
- const account = await hook({
13554
- ...basePluginContext(plugin),
13555
- tokens: input.tokens
13556
- });
13557
- return account === void 0 ? void 0 : parseSchema(
13558
- pluginProviderAccountSchema2,
13559
- account,
13560
- `Plugin "${plugin.manifest.name}" resolveOAuthAccount returned an invalid account`
13561
- );
13562
- }
13563
- async function issuePluginCredential(input) {
13564
- const plugin = pluginFor(input.provider);
13565
- const hook = plugin?.hooks?.issueCredential;
13566
- if (!plugin || !hook) {
13567
- throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
13568
- }
13569
- const currentUserId = "type" in input.actor ? input.actor.userId : void 0;
13570
- const credentialSubjectUserId = input.credentialSubject?.userId;
13571
- const result = await hook({
13572
- ...basePluginContext(plugin),
13573
- actor: input.actor,
13574
- grant: input.grant,
13575
- ...input.credentialSubject ? { credentialSubject: input.credentialSubject } : {},
13576
- tokens: {
13577
- ...currentUserId ? {
13578
- currentUser: {
13579
- userId: currentUserId,
13580
- get: async () => await input.userTokenStore.get(
13581
- currentUserId,
13582
- plugin.manifest.name
13583
- ),
13584
- set: async (tokens) => {
13585
- await input.userTokenStore.set(
13586
- currentUserId,
13587
- plugin.manifest.name,
13588
- tokens
13589
- );
13590
- },
13591
- withRefresh: async (callback) => await input.userTokenStore.withRefresh(
13592
- currentUserId,
13593
- plugin.manifest.name,
13594
- callback
13595
- )
13596
- }
13597
- } : {},
13598
- ...credentialSubjectUserId ? {
13599
- credentialSubject: {
13600
- userId: credentialSubjectUserId,
13601
- get: async () => await input.userTokenStore.get(
13602
- credentialSubjectUserId,
13603
- plugin.manifest.name
13604
- ),
13605
- set: async (tokens) => {
13606
- await input.userTokenStore.set(
13607
- credentialSubjectUserId,
13608
- plugin.manifest.name,
13609
- tokens
13610
- );
13611
- },
13612
- withRefresh: async (callback) => await input.userTokenStore.withRefresh(
13613
- credentialSubjectUserId,
13614
- plugin.manifest.name,
13615
- callback
13616
- )
13617
- }
13618
- } : {}
13619
- }
13620
- });
13621
- return parseCredentialResult(result, plugin.manifest.name);
13622
- }
11783
+ import { randomUUID as randomUUID3 } from "crypto";
13623
11784
 
13624
11785
  // src/chat/sandbox/egress/credentials.ts
13625
11786
  var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
@@ -14392,8 +12553,8 @@ async function executeCredentialedEgressRequest(input) {
14392
12553
  function credentialContextForPluginEgress(credentialContext) {
14393
12554
  return {
14394
12555
  credentials: credentialContext,
14395
- contextId: randomUUID4(),
14396
- egressId: `plugin-egress:${randomUUID4()}`,
12556
+ contextId: randomUUID3(),
12557
+ egressId: `plugin-egress:${randomUUID3()}`,
14397
12558
  expiresAtMs: Date.now() + 30 * 60 * 1e3
14398
12559
  };
14399
12560
  }
@@ -14464,6 +12625,7 @@ async function wireAgentTools(args) {
14464
12625
  skills: args.availableSkills,
14465
12626
  traceContext: args.spanContext,
14466
12627
  tracePropagation: args.policy.sandboxTracePropagation,
12628
+ egressSignals: args.policy.sandboxEgressSignals,
14467
12629
  credentialEgress: args.routing.credentialContext,
14468
12630
  actor: args.currentActor,
14469
12631
  channelConfiguration: args.policy.channelConfiguration,
@@ -14494,7 +12656,8 @@ async function wireAgentTools(args) {
14494
12656
  args.artifactStatePatch
14495
12657
  ),
14496
12658
  recordPendingAuth: args.durability.recordPendingAuth,
14497
- authorizationFlowMode: args.policy.authorizationFlowMode
12659
+ authorizationFlowMode: args.policy.authorizationFlowMode,
12660
+ authorization: args.authorization
14498
12661
  });
14499
12662
  const pluginAuth = createPluginAuthOrchestration({
14500
12663
  abortAgent: args.abortAgent,
@@ -14509,7 +12672,8 @@ async function wireAgentTools(args) {
14509
12672
  pendingAuth: args.state.pendingAuth,
14510
12673
  recordPendingAuth: args.durability.recordPendingAuth,
14511
12674
  authorizationFlowMode: args.policy.authorizationFlowMode,
14512
- userTokenStore
12675
+ userTokenStore,
12676
+ authorization: args.authorization
14513
12677
  });
14514
12678
  const mcpToolManager = new McpToolManager(
14515
12679
  pluginCatalogRuntime.getMcpProviders(),
@@ -15178,6 +13342,7 @@ ${summary}`)
15178
13342
  retainedMessageCount: replacement.length - 1,
15179
13343
  summaryChars: summary.length
15180
13344
  },
13345
+ summary,
15181
13346
  replacementHistory: replacement.map((message, index) => {
15182
13347
  const sourceEventSeq = index < retained.length ? sourceProjection.seqs[retained[index].sourceIndex] : void 0;
15183
13348
  return {
@@ -15211,8 +13376,9 @@ async function compactContextForHandoff(args, deps) {
15211
13376
  if (!runtimeMessage) {
15212
13377
  throw new Error("Handoff requires the current runtime turn context");
15213
13378
  }
13379
+ const generatedSummary = await summarizeContext(args, deps);
15214
13380
  const summary = `${MODEL_HANDOFF_SUMMARY_PREFIX}
15215
- ${await summarizeContext(args, deps)}`;
13381
+ ${generatedSummary}`;
15216
13382
  const message = {
15217
13383
  ...runtimeMessage,
15218
13384
  content: [
@@ -15231,6 +13397,7 @@ ${await summarizeContext(args, deps)}`;
15231
13397
  modelId: args.target.modelId,
15232
13398
  ...args.target.reasoningLevel ? { reasoningLevel: args.target.reasoningLevel } : {},
15233
13399
  ...args.triggeringToolCallId ? { triggeringToolCallId: args.triggeringToolCallId } : {},
13400
+ summary: generatedSummary,
15234
13401
  replacementHistory: replacementMessages.map((replacementMessage) => ({
15235
13402
  message: replacementMessage,
15236
13403
  provenance: contextProvenance
@@ -15330,6 +13497,7 @@ ${summary}`;
15330
13497
  retainedMessageCount: pendingMessages.length,
15331
13498
  summaryChars: summary.length
15332
13499
  },
13500
+ summary,
15333
13501
  replacementHistory: replacementMessages.map((message, index) => ({
15334
13502
  message,
15335
13503
  provenance: index === 0 ? contextProvenance : pendingMessages[index - 1]?.provenance ?? contextProvenance
@@ -15432,6 +13600,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15432
13600
  const state = request.state ?? {};
15433
13601
  const observers = request.observers ?? {};
15434
13602
  const delivery = request.delivery;
13603
+ const authorization = request.authorization;
15435
13604
  const durability = request.durability ?? {};
15436
13605
  signal?.throwIfAborted();
15437
13606
  assertRunRoutingConsistency(request);
@@ -15783,6 +13952,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15783
13952
  connectedMcpProviders,
15784
13953
  conversationPrivacy,
15785
13954
  durability,
13955
+ authorization,
15786
13956
  generatedFiles,
15787
13957
  invokedSkill,
15788
13958
  observers,
@@ -16443,24 +14613,12 @@ export {
16443
14613
  getConfigDefaults,
16444
14614
  shouldEmitDevAgentTrace,
16445
14615
  AuthorizationFlowDisabledError,
16446
- RetryableDeliveryError,
16447
- truncateStatusText,
16448
- escapeSlackMrkdwnText,
16449
- formatSlackLink,
16450
- normalizeSlackStatusText,
16451
- splitSlackReplyText,
16452
- buildSlackOutputMessage,
16453
14616
  buildSteeringPiMessage,
16454
- createUserTokenStore,
16455
- postSlackMessage,
16456
- addReactionToMessage,
16457
- removeReactionFromMessage,
16458
- parseOAuthStatePayload,
16459
- formatProviderLabel,
16460
- resolveBaseUrl,
16461
- startOAuthFlow,
16462
14617
  SANDBOX_EGRESS_PROXY_PATH,
16463
14618
  parseSandboxEgressCredentialToken,
14619
+ clearSandboxEgressSignals,
14620
+ consumeSandboxEgressAuthRequiredSignal,
14621
+ consumeSandboxEgressPermissionDeniedSignal,
16464
14622
  normalizeSandboxEgressTracePropagationDomains,
16465
14623
  shouldPropagateSandboxEgressTrace,
16466
14624
  resolveSandboxEgressProviderForHost,
@@ -16472,14 +14630,6 @@ export {
16472
14630
  appendSlackLegacyAttachmentText,
16473
14631
  latestReportedProgress,
16474
14632
  PluginCredentialFailureError,
16475
- getMcpAuthSession,
16476
- deleteMcpAuthSession,
16477
- deleteMcpAuthSessionsForUserProvider,
16478
- getMcpStoredOAuthCredentials,
16479
- deleteMcpStoredOAuthCredentials,
16480
- deleteMcpServerSessionId,
16481
- finalizeMcpAuthorization,
16482
- resolvePluginOAuthAccount,
16483
14633
  executeCredentialedEgressRequest,
16484
14634
  createContextCompactor,
16485
14635
  executeAgentRun,