@sentry/junior 0.32.0 → 0.34.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.
package/dist/app.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  findSkillByName,
4
4
  loadSkillsByName,
5
5
  parseSkillInvocation
6
- } from "./chunk-EHXMTKBA.js";
6
+ } from "./chunk-LAD5O3RX.js";
7
7
  import {
8
8
  GEN_AI_PROVIDER_NAME,
9
9
  MISSING_GATEWAY_CREDENTIALS_ERROR,
@@ -30,7 +30,7 @@ import {
30
30
  runNonInteractiveCommand,
31
31
  sandboxSkillDir,
32
32
  sandboxSkillFile
33
- } from "./chunk-3M7ZD6FF.js";
33
+ } from "./chunk-HZIJ4BSE.js";
34
34
  import {
35
35
  CredentialUnavailableError,
36
36
  buildOAuthTokenRequest,
@@ -53,6 +53,7 @@ import {
53
53
  logException,
54
54
  logInfo,
55
55
  logWarn,
56
+ mergeHeaderTransforms,
56
57
  parseOAuthTokenResponse,
57
58
  resolveAuthTokenPlaceholder,
58
59
  resolveErrorReference,
@@ -64,7 +65,7 @@ import {
64
65
  toOptionalString,
65
66
  withContext,
66
67
  withSpan
67
- } from "./chunk-XARRBRQV.js";
68
+ } from "./chunk-QZRPUFO6.js";
68
69
  import "./chunk-Z3YD6NHK.js";
69
70
  import {
70
71
  discoverInstalledPluginPackageContent,
@@ -1131,7 +1132,6 @@ async function postSlackMessage(input) {
1131
1132
  () => getSlackClient().chat.postMessage({
1132
1133
  channel: channelId,
1133
1134
  text,
1134
- mrkdwn: true,
1135
1135
  ...input.blocks?.length ? {
1136
1136
  blocks: input.blocks
1137
1137
  } : {},
@@ -3134,11 +3134,11 @@ function buildBehaviorSection() {
3134
3134
  ].join("\n\n");
3135
3135
  }
3136
3136
  function buildOutputSection() {
3137
- const openTag = `<output format="slack-mrkdwn" max_inline_chars="${slackOutputPolicy.maxInlineChars}" max_inline_lines="${slackOutputPolicy.maxInlineLines}">`;
3137
+ const openTag = `<output format="slack-markdown" max_inline_chars="${slackOutputPolicy.maxInlineChars}" max_inline_lines="${slackOutputPolicy.maxInlineLines}">`;
3138
3138
  return [
3139
3139
  openTag,
3140
3140
  "- Start with the answer or result, not internal process narration.",
3141
- "- Use Slack-friendly mrkdwn: bolded section labels instead of headings, no markdown tables or markdown links, and plain URLs.",
3141
+ "- Use Slack-flavored Markdown: **bold** section labels, `code`, [text](url) links, bullet lists, and fenced code blocks. No tables. When the answer primarily lists several URLs, show each URL bare instead of as a labeled link.",
3142
3142
  "- Keep replies brief and scannable; use bullets or short code blocks when helpful, and one compact thread reply when it fits.",
3143
3143
  "- When a research or document-style answer would benefit from continuation, multiple sections, or future reference value, create a Slack canvas and keep the thread reply to one or two short sentences plus the link; do not recap the canvas contents.",
3144
3144
  "- Unless a successful Slack side-effect tool intentionally satisfied the request by itself, end every turn with a final user-facing markdown response.",
@@ -3367,7 +3367,7 @@ var SkillCapabilityRuntime = class {
3367
3367
  throw new Error("Credential enablement requires requester context");
3368
3368
  }
3369
3369
  const plugin = getPluginDefinition(provider);
3370
- if (!plugin?.manifest.credentials) {
3370
+ if (!plugin?.manifest.credentials && !plugin?.manifest.apiHeaders) {
3371
3371
  return void 0;
3372
3372
  }
3373
3373
  const existing = this.enabledByProvider.get(provider);
@@ -3499,19 +3499,22 @@ var TestCredentialBroker = class {
3499
3499
  async issue(input) {
3500
3500
  const token = process.env.EVAL_TEST_CREDENTIAL_TOKEN?.trim() || "eval-test-token";
3501
3501
  const expiresAt = new Date(Date.now() + 5 * 60 * 1e3).toISOString();
3502
+ const env = this.config.envKey && this.config.placeholder ? { [this.config.envKey]: this.config.placeholder } : {};
3503
+ const tokenTransforms = this.config.domains?.map((domain) => ({
3504
+ domain,
3505
+ headers: {
3506
+ ...this.config.apiHeaders ?? {},
3507
+ Authorization: `Bearer ${token}`
3508
+ }
3509
+ })) ?? [];
3502
3510
  return {
3503
3511
  id: randomUUID2(),
3504
3512
  provider: this.config.provider,
3505
- env: {
3506
- [this.config.envKey]: this.config.placeholder
3507
- },
3508
- headerTransforms: this.config.domains.map((domain) => ({
3509
- domain,
3510
- headers: {
3511
- ...this.config.apiHeaders ?? {},
3512
- Authorization: `Bearer ${token}`
3513
- }
3514
- })),
3513
+ env,
3514
+ headerTransforms: mergeHeaderTransforms([
3515
+ ...this.config.headerTransforms?.() ?? [],
3516
+ ...tokenTransforms
3517
+ ]),
3515
3518
  expiresAt,
3516
3519
  metadata: {
3517
3520
  reason: input.reason
@@ -3521,24 +3524,50 @@ var TestCredentialBroker = class {
3521
3524
  };
3522
3525
 
3523
3526
  // src/chat/capabilities/factory.ts
3527
+ var ENV_PLACEHOLDER_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
3524
3528
  function createUserTokenStore() {
3525
3529
  return new StateAdapterTokenStore(getStateAdapter());
3526
3530
  }
3531
+ function resolveTestApiHeaderTransforms(manifest) {
3532
+ const { apiDomains, apiHeaders } = manifest;
3533
+ if (!apiDomains || !apiHeaders) {
3534
+ return [];
3535
+ }
3536
+ const headers = Object.fromEntries(
3537
+ Object.entries(apiHeaders).map(([key, value]) => [
3538
+ key,
3539
+ value.replace(ENV_PLACEHOLDER_RE, (_match, name) => {
3540
+ return `eval-test-${String(name).toLowerCase().replaceAll("_", "-")}`;
3541
+ })
3542
+ ])
3543
+ );
3544
+ return apiDomains.map((domain) => ({ domain, headers }));
3545
+ }
3527
3546
  function createSkillCapabilityRuntime(options = {}) {
3528
3547
  logCapabilityCatalogLoadedOnce();
3529
3548
  const useTestBroker = process.env.EVAL_ENABLE_TEST_CREDENTIALS === "1";
3530
3549
  const userTokenStore = createUserTokenStore();
3531
3550
  const brokersByProvider = {};
3532
3551
  for (const plugin of getPluginProviders()) {
3533
- const { credentials, name } = plugin.manifest;
3552
+ const { apiHeaders, credentials, name } = plugin.manifest;
3553
+ if (!credentials && !apiHeaders) {
3554
+ continue;
3555
+ }
3534
3556
  if (!credentials) {
3557
+ brokersByProvider[name] = useTestBroker ? new TestCredentialBroker({
3558
+ provider: name,
3559
+ headerTransforms: () => resolveTestApiHeaderTransforms(plugin.manifest)
3560
+ }) : createPluginBroker(name, { userTokenStore });
3535
3561
  continue;
3536
3562
  }
3537
3563
  const placeholder = resolveAuthTokenPlaceholder(credentials);
3538
3564
  brokersByProvider[name] = useTestBroker ? new TestCredentialBroker({
3539
3565
  provider: name,
3540
3566
  domains: credentials.apiDomains,
3541
- apiHeaders: credentials.apiHeaders,
3567
+ ...credentials.apiHeaders ? { apiHeaders: credentials.apiHeaders } : {},
3568
+ ...apiHeaders ? {
3569
+ headerTransforms: () => resolveTestApiHeaderTransforms(plugin.manifest)
3570
+ } : {},
3542
3571
  envKey: credentials.authTokenEnv,
3543
3572
  placeholder
3544
3573
  }) : createPluginBroker(name, { userTokenStore });
@@ -4109,7 +4138,6 @@ var PluginMcpClient = class {
4109
4138
  setSpanAttributes({
4110
4139
  "mcp.method.name": MCP_TOOLS_CALL_METHOD,
4111
4140
  "gen_ai.operation.name": "execute_tool",
4112
- "gen_ai.tool.name": name,
4113
4141
  ...this.transport?.sessionId ? { "mcp.session.id": this.transport.sessionId } : {},
4114
4142
  ...this.transport?.protocolVersion ? { "mcp.protocol.version": this.transport.protocolVersion } : {},
4115
4143
  ...getMcpNetworkAttributes(url)
@@ -4459,7 +4487,6 @@ var McpToolManager = class {
4459
4487
  execute: async (args) => {
4460
4488
  const resolvedArgs = typeof args === "object" && args !== null ? args : {};
4461
4489
  const baseAttributes = {
4462
- "gen_ai.tool.name": tool2.name,
4463
4490
  "mcp.method.name": "tools/call"
4464
4491
  };
4465
4492
  setSpanAttributes(baseAttributes);
@@ -8753,12 +8780,6 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
8753
8780
  execute: async (toolCallId, params) => {
8754
8781
  const normalizedToolCallId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
8755
8782
  const toolArgumentsAttribute = serializeGenAiAttribute(params);
8756
- const traceToolContext = {
8757
- ...spanContext,
8758
- conversationId: spanContext.conversationId,
8759
- turnId: spanContext.turnId,
8760
- agentId: spanContext.agentId
8761
- };
8762
8783
  if (toolName === "reportProgress") {
8763
8784
  const status = buildReportedProgressStatus(params);
8764
8785
  if (status) {
@@ -8813,9 +8834,8 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
8813
8834
  details: normalized.details
8814
8835
  });
8815
8836
  }
8816
- const toolResultAttribute = serializeGenAiAttribute(
8817
- normalized.details
8818
- );
8837
+ const resultAttributeValue = normalized.details && typeof normalized.details === "object" && "rawResult" in normalized.details && normalized.details.rawResult !== void 0 ? normalized.details.rawResult : normalized.details;
8838
+ const toolResultAttribute = serializeGenAiAttribute(resultAttributeValue);
8819
8839
  if (toolResultAttribute) {
8820
8840
  setSpanAttributes({
8821
8841
  "gen_ai.tool.call.result": toolResultAttribute
@@ -8831,7 +8851,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
8831
8851
  toolName,
8832
8852
  normalizedToolCallId,
8833
8853
  shouldTrace,
8834
- traceToolContext
8854
+ spanContext
8835
8855
  );
8836
8856
  }
8837
8857
  },
@@ -9889,13 +9909,17 @@ function commandTargetsProvider(provider, command, details) {
9889
9909
  }
9890
9910
  const plugin = getPluginDefinition(provider);
9891
9911
  const candidates = /* @__PURE__ */ new Set([provider.toLowerCase()]);
9892
- const credentials = plugin?.manifest.credentials;
9912
+ const manifest = plugin?.manifest;
9913
+ const credentials = manifest?.credentials;
9893
9914
  if (credentials) {
9894
9915
  candidates.add(credentials.authTokenEnv.toLowerCase());
9895
9916
  for (const domain of credentials.apiDomains) {
9896
9917
  candidates.add(domain.toLowerCase());
9897
9918
  }
9898
9919
  }
9920
+ for (const domain of manifest?.apiDomains ?? []) {
9921
+ candidates.add(domain.toLowerCase());
9922
+ }
9899
9923
  const combinedText = `${normalizedCommand}
9900
9924
  ${details.stdout?.toLowerCase() ?? ""}
9901
9925
  ${details.stderr?.toLowerCase() ?? ""}`;
@@ -11230,25 +11254,25 @@ function buildSlackReplyFooter(args) {
11230
11254
  return items.length > 0 ? { items } : void 0;
11231
11255
  }
11232
11256
  function buildSlackReplyBlocks(text, footer) {
11233
- if (!text.trim() || !footer?.items.length) {
11257
+ if (!text.trim()) {
11234
11258
  return void 0;
11235
11259
  }
11236
- return [
11237
- {
11238
- type: "section",
11239
- text: {
11240
- type: "mrkdwn",
11241
- text
11242
- }
11243
- },
11260
+ const blocks = [
11244
11261
  {
11262
+ type: "markdown",
11263
+ text
11264
+ }
11265
+ ];
11266
+ if (footer?.items.length) {
11267
+ blocks.push({
11245
11268
  type: "context",
11246
11269
  elements: footer.items.map((item) => ({
11247
11270
  type: "mrkdwn",
11248
11271
  text: `*${escapeSlackMrkdwn(item.label)}:* ${escapeSlackMrkdwn(item.value)}`
11249
11272
  }))
11250
- }
11251
- ];
11273
+ });
11274
+ }
11275
+ return blocks;
11252
11276
  }
11253
11277
 
11254
11278
  // src/chat/slack/reply.ts
@@ -11372,11 +11396,13 @@ async function postSlackApiReplyPosts(args) {
11372
11396
  let messageTs;
11373
11397
  try {
11374
11398
  if (post.text.trim().length > 0) {
11399
+ const footer = index === lastTextPostIndex ? args.footer : void 0;
11400
+ const blocks = buildSlackReplyBlocks(post.text, footer);
11375
11401
  const response = await postSlackMessage({
11376
11402
  channelId: args.channelId,
11377
11403
  threadTs: args.threadTs,
11378
11404
  text: post.text,
11379
- ...index === lastTextPostIndex && args.footer ? { blocks: buildSlackReplyBlocks(post.text, args.footer) } : {}
11405
+ ...blocks ? { blocks } : {}
11380
11406
  });
11381
11407
  messageTs = response.ts;
11382
11408
  lastPostedMessageTs = response.ts;
@@ -13484,6 +13510,7 @@ function buildFailureMessage(reference) {
13484
13510
  }
13485
13511
  function buildLogContext(deps, args) {
13486
13512
  return {
13513
+ conversationId: args.threadId ?? args.runId,
13487
13514
  slackThreadId: args.threadId,
13488
13515
  slackUserId: args.requesterId,
13489
13516
  slackUserName: args.requesterUserName,
@@ -13608,78 +13635,6 @@ function createSlackTurnRuntime(deps) {
13608
13635
  const threadId = deps.getThreadId(thread, message);
13609
13636
  const channelId = deps.getChannelId(thread, message);
13610
13637
  const runId = deps.getRunId(thread, message);
13611
- const rawUserText = message.text;
13612
- const userText = deps.stripLeadingBotMention(rawUserText, {
13613
- stripLeadingSlackMentionToken: Boolean(message.isMention)
13614
- });
13615
- const context = {
13616
- threadId,
13617
- requesterId: message.author.userId,
13618
- channelId,
13619
- runId
13620
- };
13621
- const preflightDecision = getSubscribedReplyPreflightDecision({
13622
- botUserName: deps.assistantUserName,
13623
- rawText: rawUserText,
13624
- text: userText,
13625
- isExplicitMention: Boolean(message.isMention)
13626
- });
13627
- if (preflightDecision && !preflightDecision.shouldReply) {
13628
- const reason = preflightDecision.reasonDetail ? `${preflightDecision.reason}:${preflightDecision.reasonDetail}` : preflightDecision.reason;
13629
- await skipSubscribedMessage({
13630
- thread,
13631
- message,
13632
- decision: { shouldReply: false, reason },
13633
- context,
13634
- userText
13635
- });
13636
- return;
13637
- }
13638
- const preparedState = await deps.prepareTurnState({
13639
- thread,
13640
- message,
13641
- userText,
13642
- explicitMention: Boolean(message.isMention),
13643
- context
13644
- });
13645
- await deps.persistPreparedState({
13646
- thread,
13647
- preparedState
13648
- });
13649
- const decision = await deps.decideSubscribedReply({
13650
- rawText: rawUserText,
13651
- text: userText,
13652
- conversationContext: deps.getPreparedConversationContext(preparedState),
13653
- hasAttachments: message.attachments.length > 0,
13654
- isExplicitMention: Boolean(message.isMention),
13655
- context
13656
- });
13657
- if (await maybeHandleThreadOptOutDecision({
13658
- thread,
13659
- decision,
13660
- beforeFirstResponsePost: hooks?.beforeFirstResponsePost
13661
- })) {
13662
- await skipSubscribedMessage({
13663
- thread,
13664
- message,
13665
- decision,
13666
- context,
13667
- preparedState,
13668
- userText
13669
- });
13670
- return;
13671
- }
13672
- if (!decision.shouldReply) {
13673
- await skipSubscribedMessage({
13674
- thread,
13675
- message,
13676
- decision,
13677
- context,
13678
- preparedState,
13679
- userText
13680
- });
13681
- return;
13682
- }
13683
13638
  await deps.withSpan(
13684
13639
  "chat.turn",
13685
13640
  "chat.turn",
@@ -13691,6 +13646,78 @@ function createSlackTurnRuntime(deps) {
13691
13646
  runId
13692
13647
  }),
13693
13648
  async () => {
13649
+ const rawUserText = message.text;
13650
+ const userText = deps.stripLeadingBotMention(rawUserText, {
13651
+ stripLeadingSlackMentionToken: Boolean(message.isMention)
13652
+ });
13653
+ const context = {
13654
+ threadId,
13655
+ requesterId: message.author.userId,
13656
+ channelId,
13657
+ runId
13658
+ };
13659
+ const preflightDecision = getSubscribedReplyPreflightDecision({
13660
+ botUserName: deps.assistantUserName,
13661
+ rawText: rawUserText,
13662
+ text: userText,
13663
+ isExplicitMention: Boolean(message.isMention)
13664
+ });
13665
+ if (preflightDecision && !preflightDecision.shouldReply) {
13666
+ const reason = preflightDecision.reasonDetail ? `${preflightDecision.reason}:${preflightDecision.reasonDetail}` : preflightDecision.reason;
13667
+ await skipSubscribedMessage({
13668
+ thread,
13669
+ message,
13670
+ decision: { shouldReply: false, reason },
13671
+ context,
13672
+ userText
13673
+ });
13674
+ return;
13675
+ }
13676
+ const preparedState = await deps.prepareTurnState({
13677
+ thread,
13678
+ message,
13679
+ userText,
13680
+ explicitMention: Boolean(message.isMention),
13681
+ context
13682
+ });
13683
+ await deps.persistPreparedState({
13684
+ thread,
13685
+ preparedState
13686
+ });
13687
+ const decision = await deps.decideSubscribedReply({
13688
+ rawText: rawUserText,
13689
+ text: userText,
13690
+ conversationContext: deps.getPreparedConversationContext(preparedState),
13691
+ hasAttachments: message.attachments.length > 0,
13692
+ isExplicitMention: Boolean(message.isMention),
13693
+ context
13694
+ });
13695
+ if (await maybeHandleThreadOptOutDecision({
13696
+ thread,
13697
+ decision,
13698
+ beforeFirstResponsePost: hooks?.beforeFirstResponsePost
13699
+ })) {
13700
+ await skipSubscribedMessage({
13701
+ thread,
13702
+ message,
13703
+ decision,
13704
+ context,
13705
+ preparedState,
13706
+ userText
13707
+ });
13708
+ return;
13709
+ }
13710
+ if (!decision.shouldReply) {
13711
+ await skipSubscribedMessage({
13712
+ thread,
13713
+ message,
13714
+ decision,
13715
+ context,
13716
+ preparedState,
13717
+ userText
13718
+ });
13719
+ return;
13720
+ }
13694
13721
  await deps.replyToThread(thread, message, {
13695
13722
  explicitMention: Boolean(message.isMention),
13696
13723
  preparedState,
@@ -15317,6 +15344,24 @@ function nonEmptyString(value) {
15317
15344
  return trimmed || void 0;
15318
15345
  }
15319
15346
 
15347
+ // src/chat/ingress/workspace-membership.ts
15348
+ import { AsyncLocalStorage } from "async_hooks";
15349
+ var workspaceTeamIdStorage = new AsyncLocalStorage();
15350
+ function runWithWorkspaceTeamId(teamId, fn) {
15351
+ if (!teamId) return fn();
15352
+ return workspaceTeamIdStorage.run(teamId, fn);
15353
+ }
15354
+ function isExternalSlackUser(raw) {
15355
+ if (!raw) return false;
15356
+ const workspaceTeamId = workspaceTeamIdStorage.getStore();
15357
+ if (!workspaceTeamId) return false;
15358
+ const userTeam = typeof raw.user_team === "string" ? raw.user_team : void 0;
15359
+ if (userTeam) return userTeam !== workspaceTeamId;
15360
+ const sourceTeam = typeof raw.source_team === "string" ? raw.source_team : void 0;
15361
+ if (sourceTeam) return sourceTeam !== workspaceTeamId;
15362
+ return false;
15363
+ }
15364
+
15320
15365
  // src/chat/ingress/junior-chat.ts
15321
15366
  function enqueueBackgroundTask(options, task) {
15322
15367
  if (!options?.waitUntil) {
@@ -15355,6 +15400,9 @@ var JuniorChat = class extends Chat {
15355
15400
  (async () => {
15356
15401
  try {
15357
15402
  const message = await messageOrFactory();
15403
+ if (isExternalSlackUser(message.raw)) {
15404
+ return;
15405
+ }
15358
15406
  const normalized = normalizeIncomingSlackThreadId(
15359
15407
  threadId,
15360
15408
  message
@@ -15373,6 +15421,9 @@ var JuniorChat = class extends Chat {
15373
15421
  );
15374
15422
  return;
15375
15423
  }
15424
+ if (isExternalSlackUser(messageOrFactory.raw)) {
15425
+ return;
15426
+ }
15376
15427
  enqueueBackgroundTask(
15377
15428
  options,
15378
15429
  (async () => {
@@ -15824,12 +15875,16 @@ function extractMessageChangedMention(body, botUserId, adapter) {
15824
15875
  const userId = event.message.user ?? "unknown";
15825
15876
  const threadId = `slack:${channelId}:${threadTs}`;
15826
15877
  const teamId = typeof body.team_id === "string" ? body.team_id : void 0;
15878
+ const userTeam = typeof event.message.user_team === "string" ? event.message.user_team : void 0;
15879
+ const sourceTeam = typeof event.message.source_team === "string" ? event.message.source_team : void 0;
15827
15880
  const raw = {
15828
15881
  channel: channelId,
15829
15882
  ts: messageTs,
15830
15883
  thread_ts: threadTs,
15831
15884
  user: userId,
15832
- ...teamId ? { team_id: teamId } : {}
15885
+ ...teamId ? { team_id: teamId } : {},
15886
+ ...userTeam ? { user_team: userTeam } : {},
15887
+ ...sourceTeam ? { source_team: sourceTeam } : {}
15833
15888
  };
15834
15889
  const message = new Message({
15835
15890
  id: getEditedMentionMessageId(messageTs),
@@ -15931,6 +15986,7 @@ async function handlePlatformWebhook(request, platform, waitUntil, bot = getProd
15931
15986
  return new Response(`Unknown platform: ${platform}`, { status: 404 });
15932
15987
  }
15933
15988
  let rebuiltRequest = request;
15989
+ let slackWorkspaceTeamId;
15934
15990
  if (platform === "slack") {
15935
15991
  const rawBody = await request.text();
15936
15992
  let parsedBody;
@@ -15939,15 +15995,19 @@ async function handlePlatformWebhook(request, platform, waitUntil, bot = getProd
15939
15995
  } catch {
15940
15996
  parsedBody = void 0;
15941
15997
  }
15998
+ slackWorkspaceTeamId = getSlackPayloadTeamId(parsedBody);
15942
15999
  if (parsedBody && isMessageChangedEnvelope(parsedBody)) {
15943
16000
  try {
15944
- await handleAuthenticatedSlackMessageChangedMention({
15945
- body: parsedBody,
15946
- bot,
15947
- rawBody,
15948
- request,
15949
- waitUntil
15950
- });
16001
+ await runWithWorkspaceTeamId(
16002
+ slackWorkspaceTeamId,
16003
+ () => handleAuthenticatedSlackMessageChangedMention({
16004
+ body: parsedBody,
16005
+ bot,
16006
+ rawBody,
16007
+ request,
16008
+ waitUntil
16009
+ })
16010
+ );
15951
16011
  } catch (error) {
15952
16012
  logException(error, "slack_message_changed_side_channel_failed");
15953
16013
  }
@@ -15965,9 +16025,12 @@ async function handlePlatformWebhook(request, platform, waitUntil, bot = getProd
15965
16025
  requestContext,
15966
16026
  async () => {
15967
16027
  try {
15968
- const response = await handler(rebuiltRequest, {
15969
- waitUntil: (task) => waitUntil(task)
15970
- });
16028
+ const response = await runWithWorkspaceTeamId(
16029
+ slackWorkspaceTeamId,
16030
+ () => handler(rebuiltRequest, {
16031
+ waitUntil: (task) => waitUntil(task)
16032
+ })
16033
+ );
15971
16034
  if (response.status >= 400) {
15972
16035
  let responseBodySnippet;
15973
16036
  try {
@@ -7,7 +7,7 @@ import {
7
7
  serializeGenAiAttribute,
8
8
  setSpanAttributes,
9
9
  withSpan
10
- } from "./chunk-XARRBRQV.js";
10
+ } from "./chunk-QZRPUFO6.js";
11
11
 
12
12
  // src/chat/state/adapter.ts
13
13
  import { createMemoryState } from "@chat-adapter/state-memory";
@@ -128,68 +128,73 @@ async function completeText(params) {
128
128
  const apiKey = getPiGatewayApiKeyOverride();
129
129
  const requestMessagesAttribute = serializeGenAiAttribute(params.messages);
130
130
  const systemInstructionsAttribute = params.system ? serializeGenAiAttribute([{ type: "text", content: params.system }]) : void 0;
131
- const startAttributes = {
131
+ const baseAttributes = {
132
132
  "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
133
133
  "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
134
134
  "gen_ai.request.model": params.modelId,
135
+ ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
136
+ };
137
+ const startAttributes = {
138
+ ...baseAttributes,
135
139
  ...systemInstructionsAttribute ? { "gen_ai.system_instructions": systemInstructionsAttribute } : {},
136
140
  ...requestMessagesAttribute ? { "gen_ai.input.messages": requestMessagesAttribute } : {},
137
- "app.ai.auth_mode": apiKey ? "oidc" : "api_key",
138
- ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
141
+ "app.ai.auth_mode": apiKey ? "oidc" : "api_key"
139
142
  };
140
- setSpanAttributes(startAttributes);
141
- const message = await completeSimple(
142
- model,
143
- {
144
- systemPrompt: params.system,
145
- messages: params.messages
143
+ return withSpan(
144
+ "ai.chat_completion",
145
+ "gen_ai.chat",
146
+ { modelId: params.modelId },
147
+ async () => {
148
+ const message = await completeSimple(
149
+ model,
150
+ {
151
+ systemPrompt: params.system,
152
+ messages: params.messages
153
+ },
154
+ {
155
+ ...apiKey ? { apiKey } : {},
156
+ temperature: params.temperature,
157
+ maxTokens: params.maxTokens,
158
+ reasoning: params.thinkingLevel,
159
+ signal: params.signal,
160
+ metadata: params.metadata
161
+ }
162
+ );
163
+ const outputText = extractText(message);
164
+ const outputMessagesAttribute = serializeGenAiAttribute([
165
+ {
166
+ role: "assistant",
167
+ content: outputText ? [{ type: "text", text: outputText }] : []
168
+ }
169
+ ]);
170
+ const usageAttributes = extractGenAiUsageAttributes(message);
171
+ const endAttributes = {
172
+ ...baseAttributes,
173
+ ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
174
+ ...usageAttributes,
175
+ ...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {}
176
+ };
177
+ setSpanAttributes(endAttributes);
178
+ if (message.stopReason === "error") {
179
+ const providerMessage = message.errorMessage?.trim() || "Unknown provider error";
180
+ logWarn(
181
+ "ai_completion_provider_error",
182
+ {},
183
+ {
184
+ ...baseAttributes,
185
+ "error.message": providerMessage
186
+ },
187
+ "AI completion returned provider error"
188
+ );
189
+ throw new Error(`AI provider error: ${providerMessage}`);
190
+ }
191
+ return {
192
+ message,
193
+ text: outputText
194
+ };
146
195
  },
147
- {
148
- ...apiKey ? { apiKey } : {},
149
- temperature: params.temperature,
150
- maxTokens: params.maxTokens,
151
- reasoning: params.thinkingLevel,
152
- signal: params.signal,
153
- metadata: params.metadata
154
- }
196
+ startAttributes
155
197
  );
156
- const outputText = extractText(message);
157
- const outputMessagesAttribute = serializeGenAiAttribute([
158
- {
159
- role: "assistant",
160
- content: outputText ? [{ type: "text", text: outputText }] : []
161
- }
162
- ]);
163
- const usageAttributes = extractGenAiUsageAttributes(message);
164
- const endAttributes = {
165
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
166
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
167
- "gen_ai.request.model": params.modelId,
168
- ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
169
- ...usageAttributes,
170
- ...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {},
171
- ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
172
- };
173
- setSpanAttributes(endAttributes);
174
- if (message.stopReason === "error") {
175
- const providerMessage = message.errorMessage?.trim() || "Unknown provider error";
176
- logWarn(
177
- "ai_completion_provider_error",
178
- {},
179
- {
180
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
181
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
182
- "gen_ai.request.model": params.modelId,
183
- "error.message": providerMessage
184
- },
185
- "AI completion returned provider error"
186
- );
187
- throw new Error(`AI provider error: ${providerMessage}`);
188
- }
189
- return {
190
- message,
191
- text: outputText
192
- };
193
198
  }
194
199
  async function completeObject(params) {
195
200
  const startedAt = Date.now();
@@ -2,7 +2,7 @@ import {
2
2
  getPluginForSkillPath,
3
3
  getPluginSkillRoots,
4
4
  logWarn
5
- } from "./chunk-XARRBRQV.js";
5
+ } from "./chunk-QZRPUFO6.js";
6
6
  import {
7
7
  skillRoots
8
8
  } from "./chunk-XPXD3FCE.js";
@@ -971,6 +971,9 @@ function setLogContext(context) {
971
971
  );
972
972
  contextStorage.enterWith(merged);
973
973
  }
974
+ function getLogContextAttributes() {
975
+ return contextStorage.getStore() ?? {};
976
+ }
974
977
  function createLogContextFromRequest(request, context = {}) {
975
978
  const url = new URL(request.url);
976
979
  return {
@@ -1052,20 +1055,20 @@ async function withSpan(name, op, context, callback, attributes = {}) {
1052
1055
  normalizedAttributes[key] = normalizedValue;
1053
1056
  }
1054
1057
  }
1055
- return withLogContext(
1056
- context,
1057
- () => sentry_exports.startSpan(
1058
+ return withLogContext(context, () => {
1059
+ const inheritedAttributes = getLogContextAttributes();
1060
+ return sentry_exports.startSpan(
1058
1061
  {
1059
1062
  name,
1060
1063
  op,
1061
1064
  attributes: {
1062
- ...toSpanAttributes(context),
1065
+ ...inheritedAttributes,
1063
1066
  ...normalizedAttributes
1064
1067
  }
1065
1068
  },
1066
1069
  callback
1067
- )
1068
- );
1070
+ );
1071
+ });
1069
1072
  }
1070
1073
  function setSpanAttributes(attributes) {
1071
1074
  const sentry = sentry_exports;
@@ -1246,6 +1249,7 @@ var SHORT_CONFIG_KEY_RE = /^[a-z0-9]+(\.[a-z0-9-]+)*$/;
1246
1249
  var TARGET_FLAG_RE = /^-{1,2}[A-Za-z0-9][A-Za-z0-9-]*$/;
1247
1250
  var AUTH_TOKEN_ENV_RE = /^[A-Z][A-Z0-9_]*$/;
1248
1251
  var ENV_VAR_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
1252
+ var ENV_PLACEHOLDER_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
1249
1253
  var API_DOMAIN_RE = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1250
1254
  var RUNTIME_POSTINSTALL_CMD_RE = /^[A-Za-z0-9._/-]+$/;
1251
1255
  var RESERVED_AUTHORIZE_PARAM_KEYS = /* @__PURE__ */ new Set([
@@ -1340,7 +1344,7 @@ var stringMapSchema = z.record(z.string(), z.unknown()).transform((record, ctx)
1340
1344
  seen.add(normalizedKey);
1341
1345
  result[key] = rawValue.trim();
1342
1346
  }
1343
- return Object.keys(result).length > 0 ? result : void 0;
1347
+ return result;
1344
1348
  });
1345
1349
  var apiDomainsSchema = z.array(z.unknown()).min(1, {
1346
1350
  error: "must be a non-empty array of strings"
@@ -1433,6 +1437,8 @@ var manifestSourceSchema = z.object({
1433
1437
  "config-keys": z.array(z.string(), {
1434
1438
  error: "must be an array when provided"
1435
1439
  }).optional(),
1440
+ "api-domains": apiDomainsSchema.optional(),
1441
+ "api-headers": stringMapSchema.optional(),
1436
1442
  credentials: z.record(z.string(), z.unknown(), {
1437
1443
  error: "must be an object when provided"
1438
1444
  }).optional(),
@@ -1470,7 +1476,11 @@ function normalizeStringMap(value, prefix, options = {}) {
1470
1476
  if (!value) {
1471
1477
  return void 0;
1472
1478
  }
1473
- for (const key of Object.keys(value)) {
1479
+ const keys = Object.keys(value);
1480
+ if (keys.length === 0) {
1481
+ return void 0;
1482
+ }
1483
+ for (const key of keys) {
1474
1484
  const normalizedKey = key.toLowerCase();
1475
1485
  if (options.reservedKeys?.has(normalizedKey)) {
1476
1486
  throw new Error(`${prefix}.${key} is reserved by the runtime`);
@@ -1481,6 +1491,31 @@ function normalizeStringMap(value, prefix, options = {}) {
1481
1491
  }
1482
1492
  return value;
1483
1493
  }
1494
+ function assertDeclaredEnvReferences(value, envVars, context) {
1495
+ for (const match of value.matchAll(ENV_PLACEHOLDER_RE)) {
1496
+ const name = match[1];
1497
+ if (!Object.prototype.hasOwnProperty.call(envVars, name)) {
1498
+ throw new Error(
1499
+ `${context} references env var ${name} which is not declared in env-vars`
1500
+ );
1501
+ }
1502
+ if (envVars[name]?.default !== void 0) {
1503
+ throw new Error(
1504
+ `${context} references env var ${name}, but API header env vars must not declare defaults`
1505
+ );
1506
+ }
1507
+ }
1508
+ }
1509
+ function normalizeRequiredApiHeaders(value, prefix, envVars) {
1510
+ const apiHeaders = normalizeStringMap(value, prefix);
1511
+ if (!apiHeaders) {
1512
+ throw new Error(`${prefix} must contain at least one header`);
1513
+ }
1514
+ for (const [key, headerValue] of Object.entries(apiHeaders)) {
1515
+ assertDeclaredEnvReferences(headerValue, envVars, `${prefix}.${key}`);
1516
+ }
1517
+ return apiHeaders;
1518
+ }
1484
1519
  function normalizeCredentials(data, name) {
1485
1520
  const schema = data.type === "oauth-bearer" ? oauthBearerCredentialsSchema : data.type === "github-app" ? githubAppCredentialsSchema : void 0;
1486
1521
  if (!schema) {
@@ -1493,30 +1528,28 @@ function normalizeCredentials(data, name) {
1493
1528
  throw new Error(issueMessage(result.error, `Plugin ${name} credentials`));
1494
1529
  }
1495
1530
  if (result.data.type === "oauth-bearer") {
1531
+ const apiHeaders2 = result.data["api-headers"] ? normalizeStringMap(
1532
+ result.data["api-headers"],
1533
+ `Plugin ${name} credentials.api-headers`,
1534
+ { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1535
+ ) : void 0;
1496
1536
  return {
1497
1537
  type: "oauth-bearer",
1498
1538
  apiDomains: result.data["api-domains"],
1499
- ...result.data["api-headers"] ? {
1500
- apiHeaders: normalizeStringMap(
1501
- result.data["api-headers"],
1502
- `Plugin ${name} credentials.api-headers`,
1503
- { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1504
- )
1505
- } : {},
1539
+ ...apiHeaders2 ? { apiHeaders: apiHeaders2 } : {},
1506
1540
  authTokenEnv: result.data["auth-token-env"],
1507
1541
  ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {}
1508
1542
  };
1509
1543
  }
1544
+ const apiHeaders = result.data["api-headers"] ? normalizeStringMap(
1545
+ result.data["api-headers"],
1546
+ `Plugin ${name} credentials.api-headers`,
1547
+ { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1548
+ ) : void 0;
1510
1549
  return {
1511
1550
  type: "github-app",
1512
1551
  apiDomains: result.data["api-domains"],
1513
- ...result.data["api-headers"] ? {
1514
- apiHeaders: normalizeStringMap(
1515
- result.data["api-headers"],
1516
- `Plugin ${name} credentials.api-headers`,
1517
- { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1518
- )
1519
- } : {},
1552
+ ...apiHeaders ? { apiHeaders } : {},
1520
1553
  authTokenEnv: result.data["auth-token-env"],
1521
1554
  ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {},
1522
1555
  appIdEnv: result.data["app-id-env"],
@@ -1650,7 +1683,6 @@ function normalizeRuntimePostinstall(commands, name) {
1650
1683
  }
1651
1684
  return parsed.length > 0 ? parsed : void 0;
1652
1685
  }
1653
- var ENV_PLACEHOLDER_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
1654
1686
  var envVarDeclarationSchema = z.preprocess(
1655
1687
  (value) => value === null || value === void 0 ? {} : value,
1656
1688
  z.object({
@@ -1714,16 +1746,13 @@ function normalizeMcp(data, envVars, name) {
1714
1746
  if (!result.success) {
1715
1747
  throw new Error(issueMessage(result.error, `Plugin ${name} mcp`));
1716
1748
  }
1749
+ const headers = result.data.headers ? normalizeStringMap(result.data.headers, `Plugin ${name} mcp.headers`, {
1750
+ forbiddenKeys: FORBIDDEN_API_HEADER_NAMES
1751
+ }) : void 0;
1717
1752
  return {
1718
1753
  transport: "http",
1719
1754
  url: result.data.url,
1720
- ...result.data.headers ? {
1721
- headers: normalizeStringMap(
1722
- result.data.headers,
1723
- `Plugin ${name} mcp.headers`,
1724
- { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1725
- )
1726
- } : {},
1755
+ ...headers ? { headers } : {},
1727
1756
  ...result.data["allowed-tools"] ? { allowedTools: result.data["allowed-tools"] } : {}
1728
1757
  };
1729
1758
  }
@@ -1761,6 +1790,16 @@ function parsePluginManifest(raw, dir) {
1761
1790
  `Plugin ${parsedYaml.name ?? "unknown"} config-keys must be an array when provided`
1762
1791
  );
1763
1792
  }
1793
+ if (path3 === "api-domains") {
1794
+ throw new Error(
1795
+ `Plugin ${parsedYaml.name ?? "unknown"} api-domains must be a non-empty array of domains`
1796
+ );
1797
+ }
1798
+ if (path3 === "api-headers") {
1799
+ throw new Error(
1800
+ `Plugin ${parsedYaml.name ?? "unknown"} api-headers must be an object when provided`
1801
+ );
1802
+ }
1764
1803
  if (path3 === "credentials") {
1765
1804
  throw new Error(
1766
1805
  `Plugin ${parsedYaml.name ?? "unknown"} credentials must be an object when provided`
@@ -1813,16 +1852,29 @@ function parsePluginManifest(raw, dir) {
1813
1852
  }
1814
1853
  return `${data.name}.${key}`;
1815
1854
  });
1855
+ const envVars = data["env-vars"] ? normalizeEnvVars(data["env-vars"], data.name) : {};
1856
+ const apiHeaders = data["api-headers"] ? normalizeRequiredApiHeaders(
1857
+ data["api-headers"],
1858
+ `Plugin ${data.name} api-headers`,
1859
+ envVars
1860
+ ) : void 0;
1861
+ if (apiHeaders && !data["api-domains"]) {
1862
+ throw new Error(`Plugin ${data.name} api-headers requires api-domains`);
1863
+ }
1864
+ if (data["api-domains"] && !apiHeaders) {
1865
+ throw new Error(`Plugin ${data.name} api-domains requires api-headers`);
1866
+ }
1816
1867
  const credentials = data.credentials ? normalizeCredentials(data.credentials, data.name) : void 0;
1817
1868
  const runtimeDependencies = data["runtime-dependencies"] ? normalizeRuntimeDependencies(data["runtime-dependencies"], data.name) : void 0;
1818
1869
  const runtimePostinstall = data["runtime-postinstall"] ? normalizeRuntimePostinstall(data["runtime-postinstall"], data.name) : void 0;
1819
- const envVars = data["env-vars"] ? normalizeEnvVars(data["env-vars"], data.name) : {};
1820
1870
  const mcp = data.mcp ? normalizeMcp(data.mcp, envVars, data.name) : void 0;
1821
1871
  const manifest = {
1822
1872
  name: data.name,
1823
1873
  description: data.description,
1824
1874
  capabilities,
1825
1875
  configKeys,
1876
+ ...data["api-domains"] ? { apiDomains: data["api-domains"] } : {},
1877
+ ...apiHeaders ? { apiHeaders } : {},
1826
1878
  ...Object.keys(envVars).length > 0 ? { envVars } : {},
1827
1879
  ...credentials ? { credentials } : {},
1828
1880
  ...runtimeDependencies ? { runtimeDependencies } : {},
@@ -1903,7 +1955,76 @@ import { readFileSync, readdirSync, statSync } from "fs";
1903
1955
  import path2 from "path";
1904
1956
 
1905
1957
  // src/chat/plugins/auth/github-app-broker.ts
1906
- import { createPrivateKey, createSign, randomUUID } from "crypto";
1958
+ import { createPrivateKey, createSign, randomUUID as randomUUID2 } from "crypto";
1959
+
1960
+ // src/chat/credentials/header-transforms.ts
1961
+ function mergeHeaderTransforms(transforms) {
1962
+ const byDomain = /* @__PURE__ */ new Map();
1963
+ for (const transform of transforms) {
1964
+ byDomain.set(transform.domain, {
1965
+ ...byDomain.get(transform.domain) ?? {},
1966
+ ...transform.headers
1967
+ });
1968
+ }
1969
+ return [...byDomain.entries()].map(([domain, headers]) => ({
1970
+ domain,
1971
+ headers
1972
+ }));
1973
+ }
1974
+
1975
+ // src/chat/plugins/auth/api-headers-broker.ts
1976
+ import { randomUUID } from "crypto";
1977
+ var MAX_LEASE_MS = 60 * 60 * 1e3;
1978
+ var ENV_PLACEHOLDER_RE2 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
1979
+ function resolveHeaders(provider, headers) {
1980
+ return Object.fromEntries(
1981
+ Object.entries(headers).map(([key, value]) => {
1982
+ const resolved = value.replace(ENV_PLACEHOLDER_RE2, (_match, name) => {
1983
+ const envName = name;
1984
+ const envValue = process.env[envName]?.trim();
1985
+ if (!envValue) {
1986
+ throw new Error(
1987
+ `Missing ${envName} for API header provider "${provider}"`
1988
+ );
1989
+ }
1990
+ return envValue;
1991
+ });
1992
+ return [key, resolved];
1993
+ })
1994
+ );
1995
+ }
1996
+ function resolveApiHeaderTransforms(manifest) {
1997
+ const { apiDomains, apiHeaders } = manifest;
1998
+ if (!apiDomains || !apiHeaders) {
1999
+ return [];
2000
+ }
2001
+ const resolvedHeaders = resolveHeaders(manifest.name, apiHeaders);
2002
+ return apiDomains.map((domain) => ({
2003
+ domain,
2004
+ headers: resolvedHeaders
2005
+ }));
2006
+ }
2007
+ function createApiHeadersBroker(manifest) {
2008
+ const provider = manifest.name;
2009
+ return {
2010
+ async issue(input) {
2011
+ const headerTransforms = resolveApiHeaderTransforms(manifest);
2012
+ if (headerTransforms.length === 0) {
2013
+ throw new Error(`No API headers configured for plugin "${provider}"`);
2014
+ }
2015
+ return {
2016
+ id: randomUUID(),
2017
+ provider,
2018
+ env: {},
2019
+ headerTransforms,
2020
+ expiresAt: new Date(Date.now() + MAX_LEASE_MS).toISOString(),
2021
+ metadata: {
2022
+ reason: input.reason
2023
+ }
2024
+ };
2025
+ }
2026
+ };
2027
+ }
1907
2028
 
1908
2029
  // src/chat/plugins/auth/auth-token-placeholder.ts
1909
2030
  var DEFAULT_PLACEHOLDERS = {
@@ -1915,7 +2036,7 @@ function resolveAuthTokenPlaceholder(credentials) {
1915
2036
  }
1916
2037
 
1917
2038
  // src/chat/plugins/auth/github-app-broker.ts
1918
- var MAX_LEASE_MS = 60 * 60 * 1e3;
2039
+ var MAX_LEASE_MS2 = 60 * 60 * 1e3;
1919
2040
  function base64Url(input) {
1920
2041
  return Buffer.from(input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
1921
2042
  }
@@ -2060,6 +2181,7 @@ function createGitHubAppBroker(manifest, credentials) {
2060
2181
  } = credentials;
2061
2182
  const apiBase = `https://${apiDomains[0]}`;
2062
2183
  const placeholder = resolveAuthTokenPlaceholder(credentials);
2184
+ const pluginHeaderTransforms = () => resolveApiHeaderTransforms(manifest);
2063
2185
  const GIT_DOMAIN = "github.com";
2064
2186
  const GIT_CAPABILITIES = /* @__PURE__ */ new Set([
2065
2187
  `${provider}.contents.read`,
@@ -2097,16 +2219,19 @@ function createGitHubAppBroker(manifest, credentials) {
2097
2219
  const now = Date.now();
2098
2220
  if (cached && cached.expiresAt - now > 2 * 60 * 1e3) {
2099
2221
  return {
2100
- id: randomUUID(),
2222
+ id: randomUUID2(),
2101
2223
  provider,
2102
2224
  env: { [authTokenEnv]: placeholder },
2103
- headerTransforms: leaseDomains.map((domain) => ({
2104
- domain,
2105
- headers: {
2106
- ...apiHeaders ?? {},
2107
- Authorization: authorizationFor(domain, cached.token)
2108
- }
2109
- })),
2225
+ headerTransforms: mergeHeaderTransforms([
2226
+ ...pluginHeaderTransforms(),
2227
+ ...leaseDomains.map((domain) => ({
2228
+ domain,
2229
+ headers: {
2230
+ ...apiHeaders ?? {},
2231
+ Authorization: authorizationFor(domain, cached.token)
2232
+ }
2233
+ }))
2234
+ ]),
2110
2235
  expiresAt: new Date(cached.expiresAt).toISOString(),
2111
2236
  metadata: {
2112
2237
  installationId: String(cached.installationId),
@@ -2130,7 +2255,7 @@ function createGitHubAppBroker(manifest, credentials) {
2130
2255
  const providerExpiresAtMs = Date.parse(accessTokenResponse.expires_at);
2131
2256
  const expiresAtMs = Math.min(
2132
2257
  providerExpiresAtMs,
2133
- Date.now() + MAX_LEASE_MS
2258
+ Date.now() + MAX_LEASE_MS2
2134
2259
  );
2135
2260
  tokenCache.set(cacheKey, {
2136
2261
  installationId,
@@ -2138,16 +2263,22 @@ function createGitHubAppBroker(manifest, credentials) {
2138
2263
  expiresAt: expiresAtMs
2139
2264
  });
2140
2265
  return {
2141
- id: randomUUID(),
2266
+ id: randomUUID2(),
2142
2267
  provider,
2143
2268
  env: { [authTokenEnv]: placeholder },
2144
- headerTransforms: leaseDomains.map((domain) => ({
2145
- domain,
2146
- headers: {
2147
- ...apiHeaders ?? {},
2148
- Authorization: authorizationFor(domain, accessTokenResponse.token)
2149
- }
2150
- })),
2269
+ headerTransforms: mergeHeaderTransforms([
2270
+ ...pluginHeaderTransforms(),
2271
+ ...leaseDomains.map((domain) => ({
2272
+ domain,
2273
+ headers: {
2274
+ ...apiHeaders ?? {},
2275
+ Authorization: authorizationFor(
2276
+ domain,
2277
+ accessTokenResponse.token
2278
+ )
2279
+ }
2280
+ }))
2281
+ ]),
2151
2282
  expiresAt: new Date(expiresAtMs).toISOString(),
2152
2283
  metadata: {
2153
2284
  installationId: String(installationId),
@@ -2159,7 +2290,7 @@ function createGitHubAppBroker(manifest, credentials) {
2159
2290
  }
2160
2291
 
2161
2292
  // src/chat/plugins/auth/oauth-bearer-broker.ts
2162
- import { randomUUID as randomUUID2 } from "crypto";
2293
+ import { randomUUID as randomUUID3 } from "crypto";
2163
2294
 
2164
2295
  // src/chat/credentials/broker.ts
2165
2296
  var CredentialUnavailableError = class extends Error {
@@ -2270,7 +2401,7 @@ function parseOAuthTokenResponse(data, fallbackScope) {
2270
2401
  }
2271
2402
 
2272
2403
  // src/chat/plugins/auth/oauth-bearer-broker.ts
2273
- var MAX_LEASE_MS2 = 60 * 60 * 1e3;
2404
+ var MAX_LEASE_MS3 = 60 * 60 * 1e3;
2274
2405
  var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
2275
2406
  async function refreshAccessToken(refreshToken, oauth, fallbackScope) {
2276
2407
  const clientId = process.env[oauth.clientIdEnv]?.trim();
@@ -2302,21 +2433,25 @@ async function refreshAccessToken(refreshToken, oauth, fallbackScope) {
2302
2433
  return parseOAuthTokenResponse(data, fallbackScope);
2303
2434
  }
2304
2435
  function getLeaseExpiry(expiresAt) {
2305
- return expiresAt ? Math.min(expiresAt, Date.now() + MAX_LEASE_MS2) : Date.now() + MAX_LEASE_MS2;
2436
+ return expiresAt ? Math.min(expiresAt, Date.now() + MAX_LEASE_MS3) : Date.now() + MAX_LEASE_MS3;
2306
2437
  }
2307
2438
  function createOAuthBearerBroker(manifest, credentials, deps) {
2308
2439
  const provider = manifest.name;
2309
2440
  const { apiDomains, apiHeaders, authTokenEnv } = credentials;
2310
2441
  const authTokenPlaceholder = resolveAuthTokenPlaceholder(credentials);
2442
+ const pluginHeaderTransforms = () => resolveApiHeaderTransforms(manifest);
2311
2443
  function buildLease(token, expiresAtMs, reason) {
2312
2444
  return {
2313
- id: randomUUID2(),
2445
+ id: randomUUID3(),
2314
2446
  provider,
2315
2447
  env: { [authTokenEnv]: authTokenPlaceholder },
2316
- headerTransforms: apiDomains.map((domain) => ({
2317
- domain,
2318
- headers: { ...apiHeaders ?? {}, Authorization: `Bearer ${token}` }
2319
- })),
2448
+ headerTransforms: mergeHeaderTransforms([
2449
+ ...pluginHeaderTransforms(),
2450
+ ...apiDomains.map((domain) => ({
2451
+ domain,
2452
+ headers: { ...apiHeaders ?? {}, Authorization: `Bearer ${token}` }
2453
+ }))
2454
+ ]),
2320
2455
  expiresAt: new Date(expiresAtMs).toISOString(),
2321
2456
  metadata: { reason }
2322
2457
  };
@@ -2327,7 +2462,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
2327
2462
  const oauth = manifest.oauth;
2328
2463
  if (!oauth) {
2329
2464
  if (envToken) {
2330
- return buildLease(envToken, Date.now() + MAX_LEASE_MS2, input.reason);
2465
+ return buildLease(envToken, Date.now() + MAX_LEASE_MS3, input.reason);
2331
2466
  }
2332
2467
  throw new CredentialUnavailableError(
2333
2468
  provider,
@@ -2734,11 +2869,15 @@ function createPluginBroker(provider, deps) {
2734
2869
  throw new Error(`Unknown plugin provider: "${provider}"`);
2735
2870
  }
2736
2871
  const { credentials, name } = plugin.manifest;
2737
- if (!credentials) {
2738
- throw new Error(`Provider "${name}" has no credentials configured`);
2872
+ if (!credentials && !plugin.manifest.apiHeaders) {
2873
+ throw new Error(
2874
+ `Provider "${name}" has no credentials or API headers configured`
2875
+ );
2739
2876
  }
2740
2877
  let broker;
2741
- if (credentials.type === "oauth-bearer") {
2878
+ if (!credentials) {
2879
+ broker = createApiHeadersBroker(plugin.manifest);
2880
+ } else if (credentials.type === "oauth-bearer") {
2742
2881
  broker = createOAuthBearerBroker(plugin.manifest, credentials, deps);
2743
2882
  } else if (credentials.type === "github-app") {
2744
2883
  broker = createGitHubAppBroker(plugin.manifest, credentials);
@@ -2773,6 +2912,7 @@ export {
2773
2912
  serializeGenAiAttribute,
2774
2913
  extractGenAiUsageSummary,
2775
2914
  extractGenAiUsageAttributes,
2915
+ mergeHeaderTransforms,
2776
2916
  resolveAuthTokenPlaceholder,
2777
2917
  parsePluginManifest,
2778
2918
  CredentialUnavailableError,
package/dist/cli/check.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseSkillFile
3
- } from "../chunk-EHXMTKBA.js";
3
+ } from "../chunk-LAD5O3RX.js";
4
4
  import {
5
5
  parsePluginManifest
6
- } from "../chunk-XARRBRQV.js";
6
+ } from "../chunk-QZRPUFO6.js";
7
7
  import "../chunk-Z3YD6NHK.js";
8
8
  import "../chunk-XPXD3FCE.js";
9
9
  import "../chunk-2KG3PWR4.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  disconnectStateAdapter,
3
3
  resolveRuntimeDependencySnapshot
4
- } from "../chunk-3M7ZD6FF.js";
4
+ } from "../chunk-HZIJ4BSE.js";
5
5
  import {
6
6
  getPluginProviders,
7
7
  getPluginRuntimeDependencies,
8
8
  getPluginRuntimePostinstall
9
- } from "../chunk-XARRBRQV.js";
9
+ } from "../chunk-QZRPUFO6.js";
10
10
  import "../chunk-Z3YD6NHK.js";
11
11
  import "../chunk-XPXD3FCE.js";
12
12
  import "../chunk-2KG3PWR4.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"