@sentry/junior 0.263.0 → 0.265.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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/{adapter-BIacI0Bt.js → adapter-cUkPH3Om.js} +1 -1
  3. package/dist/{admin-C7vUk7s9.js → admin-Cf0rDmWV.js} +1 -1
  4. package/dist/{agent-runner-BUBas7Yy.js → agent-runner-C6MwHJEs.js} +296 -238
  5. package/dist/api/schema.d.ts +213 -16
  6. package/dist/api/schema.js +3 -3
  7. package/dist/api.js +107 -40
  8. package/dist/{app-Bo0kB987.d.ts → app-Csbi9dW7.d.ts} +4 -3
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +101 -393
  11. package/dist/{assistant-reply-BFmjgJjD.js → assistant-reply-UPA8vKlC.js} +1 -1
  12. package/dist/{automation-CWX0qL8U.js → automation-2RbKzpMT.js} +30 -9
  13. package/dist/{briefs-h55v5lX9.js → briefs-DRjx8kQ9.js} +3 -3
  14. package/dist/{catalog-runtime-Suf-aLlh.js → catalog-runtime-D3qbJlYz.js} +1 -1
  15. package/dist/cli/chat.js +15 -15
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/init.js +4 -18
  18. package/dist/cli/main.js +4 -4
  19. package/dist/cli/plugins.js +4 -4
  20. package/dist/cli/snapshot-create.js +4 -4
  21. package/dist/cli/upgrade.js +3 -3
  22. package/dist/{client-DZBCBj1M.js → client-DX34VNtH.js} +3 -8
  23. package/dist/{config-Dnv8KmNs.js → config-BayQ9hHH.js} +100 -3
  24. package/dist/{connections-Br0_xv27.js → connections-mpN5Lsnj.js} +11 -10
  25. package/dist/{conversation-privacy-FY8yc2q3.js → conversation-privacy-frjQCJa9.js} +1 -1
  26. package/dist/{credential-sync-BvkiyWF5.js → credential-sync-CZmyDNdR.js} +20 -20
  27. package/dist/{agent-hooks-CqnZfuCt.js → credentialed-C6pKlB1p.js} +3197 -66
  28. package/dist/{executor-J9k2sp9m.js → executor-CAr7xQr2.js} +10 -300
  29. package/dist/{images-DV2FlEGL.js → images-B2yA3HWE.js} +2 -2
  30. package/dist/instrumentation.js +41 -25
  31. package/dist/{job-queue-B4yTgklN.js → job-queue-Cjo6Yg_b.js} +53 -14
  32. package/dist/{logging-Dk_FpF36.js → logging-D2QBriUo.js} +19 -32
  33. package/dist/{message-BzseAFH1.js → message-C9ymnpBS.js} +4 -4
  34. package/dist/nitro.d.ts +17 -2
  35. package/dist/nitro.js +26 -27
  36. package/dist/{oauth-callback-server-bTspTMCC.js → oauth-callback-server-BtMf3TPZ.js} +2 -2
  37. package/dist/{prompt-CxatQVaj.js → prompt-CvaDdSX3.js} +1 -1
  38. package/dist/{registry-BFzQFV_y.js → registry-COTeZ2cV.js} +1 -1
  39. package/dist/{resolve-CnRHib0U.js → resolve-g9PKz9pH.js} +7 -106
  40. package/dist/{result-DczfmNBa.js → result-COnC42GR.js} +8 -8
  41. package/dist/{runner-CJd9Wec-.js → runner-BnTNS7x_.js} +10 -10
  42. package/dist/{sandbox-egress-signals-DUmVkOx4.js → sandbox-egress-signals-C_IIdLFk.js} +1 -1
  43. package/dist/{schema-QidbykYl.js → schema-D5Amlzkp.js} +44 -3
  44. package/dist/{sentry-BBUlHih9.js → sentry-MDkYAVPV.js} +3 -6
  45. package/dist/{service-account-BI2kQDrb.js → service-account-CwvToidT.js} +3 -3
  46. package/dist/{service-account-x2QZXm4X.js → service-account-YdYftwV2.js} +5 -5
  47. package/dist/{session-stop-CtyJsnEj.js → session-stop-CpCUy1ZJ.js} +121 -81
  48. package/dist/{skills-Dbzx9-rS.js → skills-Bb2KaJ3m.js} +2 -2
  49. package/dist/{spawn-D8WU99UV.js → spawn-mbTcrLb3.js} +2 -2
  50. package/dist/{store-TKg1HVKx.js → store-CqRnaZSq.js} +3 -3
  51. package/dist/{store-UlOrW6JC.js → store-DdT74Lvq.js} +18 -9
  52. package/dist/{task-queue-BRB49H-J.js → task-queue-8XjDIORF.js} +14 -19
  53. package/dist/{task-runner-C575f2Dd.js → task-runner-6mLmG6g_.js} +46 -28
  54. package/dist/{tasks-LOItiYgL.js → tasks-DGbrur6x.js} +14 -2075
  55. package/dist/{thread-state-IDHdZA7R.js → thread-state-D45lz-oC.js} +5 -5
  56. package/dist/{turn-failure-response-h_LSXJ-M.js → turn-failure-response-CaEPLSBn.js} +7 -7
  57. package/dist/{turn-lifecycle-Cvi6Sb8Y.js → turn-lifecycle-C35IyRGp.js} +1 -1
  58. package/dist/{turn-session-routing-5Nyv4fso.js → turn-session-routing-BKx6rfVd.js} +1 -1
  59. package/dist/{validation-BeTriWC2.js → validation-C0X3CX51.js} +1 -1
  60. package/dist/{version-BQX_hLDC.js → version-BPHgJwnm.js} +1 -1
  61. package/dist/version.js +1 -1
  62. package/dist/{viewer-DmNRM88V.js → viewer-B2wXNspU.js} +21 -5
  63. package/dist/{web-input-BHcGXSox.js → web-input-Y6aXvmfa.js} +145 -46
  64. package/dist/{work-DLJMV02b.js → work-DMbqK0kT.js} +10 -10
  65. package/dist/{worker-vfG2ESg1.js → worker-B-rDJ3XN.js} +8 -6
  66. package/package.json +10 -9
  67. package/dist/profile-Dq5I7TV1.js +0 -582
@@ -1,19 +1,19 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { $t as isToolResultMessage, Qt as isToolResultError, Xt as isAssistantMessage, en as normalizeToolNameFromResult, qn as postSlackMessage } from "./tasks-LOItiYgL.js";
3
- import { f as logException, m as logWarn, p as logInfo } from "./logging-Dk_FpF36.js";
4
- import { n as botConfig } from "./config-Dnv8KmNs.js";
5
- import { f as getSqlExecutor, u as getConversationStore } from "./viewer-DmNRM88V.js";
2
+ import { $t as isToolResultMessage, Qt as isToolResultError, Xt as isAssistantMessage, en as normalizeToolNameFromResult } from "./tasks-DGbrur6x.js";
3
+ import { n as botConfig } from "./config-BayQ9hHH.js";
4
+ import { f as logException, m as logWarn, p as logInfo } from "./logging-D2QBriUo.js";
5
+ import { d as getConversationStore, p as getSqlExecutor } from "./viewer-B2wXNspU.js";
6
6
  import { s as juniorConversations } from "./agent-invocations-CxKldAeB.js";
7
- import "./executor-J9k2sp9m.js";
8
- import { h as credentialSubjectSchema } from "./registry-BFzQFV_y.js";
9
- import { gt as JUNIOR_THREAD_STATE_TTL_MS, rt as verifyTimerWatchCredentialSubject, u as getPlugins, xt as ToolInputError } from "./agent-hooks-CqnZfuCt.js";
10
- import { r as getStateAdapter } from "./adapter-BIacI0Bt.js";
11
- import { r as EVENT_AUTHOR_ID } from "./prompt-CxatQVaj.js";
12
- import { r as sanitizeAssistantText } from "./assistant-reply-BFmjgJjD.js";
13
- import { r as appendAndEnqueueInboundMessage } from "./store-UlOrW6JC.js";
7
+ import "./executor-CAr7xQr2.js";
8
+ import { h as credentialSubjectSchema } from "./registry-COTeZ2cV.js";
9
+ import { Yt as verifyTimerWatchCredentialSubject, ln as JUNIOR_THREAD_STATE_TTL_MS, mn as ToolInputError, p as getPlugins, vt as postSlackMessage } from "./credentialed-C6pKlB1p.js";
10
+ import { r as getStateAdapter } from "./adapter-cUkPH3Om.js";
11
+ import { r as EVENT_AUTHOR_ID } from "./prompt-CvaDdSX3.js";
12
+ import { r as sanitizeAssistantText } from "./assistant-reply-UPA8vKlC.js";
13
+ import { r as appendAndEnqueueInboundMessage } from "./store-DdT74Lvq.js";
14
14
  import { z } from "zod";
15
15
  import { eq, inArray } from "drizzle-orm";
16
- import { eventMatchFieldsSchema, eventMatchSchema, normalizeEventIdentifier, objectTypeSchema } from "@sentry/junior-plugin-api";
16
+ import { describeEventTrigger, eventMatchFieldsSchema, eventMatchSchema, eventTriggerProblems, normalizeEventIdentifier, objectTypeSchema } from "@sentry/junior-plugin-api";
17
17
  // src/chat/automation-result.ts
18
18
  /**
19
19
  * Automation run result.
@@ -329,10 +329,10 @@ async function maybePostAutomatedTurnLimitNotice(args) {
329
329
  // src/chat/task-input.ts
330
330
  /**
331
331
  * Shared closing lines. Automations pass outcomes and end with a declared
332
- * result. Watches pass no outcomes and reply in the Conversation.
332
+ * result. Watches follow the Conversation's reply rules.
333
333
  */
334
334
  function replyContractLines(outcomes) {
335
- if (!outcomes) return ["When you reply, follow any reply format in the instructions.", "Briefly report what you did or what is needed next."];
335
+ if (!outcomes) return ["When you reply, follow any reply format in the instructions."];
336
336
  return [`End with \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\`.`];
337
337
  }
338
338
  function oneLine(value) {
@@ -449,13 +449,23 @@ function hasEventCatalogEntries(catalog) {
449
449
  /**
450
450
  * Catalog entries that durable event automations may select.
451
451
  *
452
- * Core Workspace snapshot events only wake temporary watches. Snapshot finish
453
- * does not run event-automation ingest, so those namespaces stay out of create/update
454
- * schemas and trigger availability. The core `slack` namespace stays in: Slack
455
- * ingress runs event-automation ingest for channel messages.
452
+ * Resource types with `automation: false` stay out of create/update schemas
453
+ * and trigger availability. A namespace with no such resource type left is
454
+ * removed.
456
455
  */
457
456
  function automationEventCatalog(catalog) {
458
- return Object.fromEntries(Object.entries(catalog).filter(([namespace]) => namespace !== CORE_EVENT_NAMESPACE));
457
+ return Object.fromEntries(Object.entries(catalog).flatMap(([namespace, registration]) => {
458
+ const resourceTypes = registration.resourceTypes.filter((resourceType) => registration.limits?.[resourceType.type]?.automation !== false);
459
+ return resourceTypes.length > 0 ? [[namespace, {
460
+ ...registration,
461
+ resourceTypes
462
+ }]] : [];
463
+ }));
464
+ }
465
+ /** Return why watchEvents cannot watch one resource type, if it cannot. */
466
+ function eventWatchUnavailable(catalog, namespace, resourceType) {
467
+ const limits = catalog[namespace]?.limits?.[resourceType];
468
+ return limits?.watch === false ? limits.watchUnavailableMessage : void 0;
459
469
  }
460
470
  function enumSchema(values, unavailableMessage) {
461
471
  if (values.length === 0) return z.string().refine(() => false, unavailableMessage);
@@ -473,46 +483,45 @@ function registeredEventTypeSchema(catalog) {
473
483
  function registeredResourceTypeSchema(catalog) {
474
484
  return enumSchema([...new Set(Object.values(catalog).flatMap((registration) => registration.resourceTypes.map((resourceType) => resourceType.type)))].sort(), "No resource types are enabled.");
475
485
  }
476
- /** Return whether one enabled plugin declared an event type. */
477
- function pluginSupportsEvent(catalog, namespace, resourceType, eventType) {
478
- return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.supportedEvents.includes(eventType) ?? false;
479
- }
480
486
  /** Return app guidance for one registered resource type and event type. */
481
487
  function eventGuidance(catalog, namespace, resourceType, eventType) {
482
488
  return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.guidance?.[eventType];
483
489
  }
484
- /** Return declared match keys for one registered resource type. */
485
- function eventMatchFields(catalog, namespace, resourceType) {
486
- return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.matchFields;
487
- }
488
490
  /** Build the match object the tools accept for one resource type. */
489
491
  function registeredEventMatchSchema() {
490
- return eventMatchSchema.optional().describe("Optional exact values from the resource type matchFields. A single value must equal the event data. A list means the event value may be any one entry. Omit a key to ignore it. Unmatched events are dropped with no agent turn.");
492
+ return eventMatchSchema.optional().describe("Optional filters: exact values for the resource type matchFields. A single value must equal the event data. A list means the event value may be any one entry. For a field with list: true, the event value is a list, and it matches when it includes the value or any entry. A field with events is set only on those events, so every selected event must be in that list. Omit a key to ignore it. Unmatched events are dropped with no agent turn.");
493
+ }
494
+ /**
495
+ * List why a selection cannot work with the enabled plugins. Use it for new
496
+ * selections and for saved triggers, which can go stale after a plugin renames
497
+ * or removes a resource type, event, or filter.
498
+ */
499
+ function registeredEventTriggerProblems(catalog, selection, options) {
500
+ return eventTriggerProblems(catalog[selection.namespace]?.resourceTypes ?? [], selection, options);
491
501
  }
492
- /** Reject match keys the resource type does not list. */
493
- function requireSupportedEventMatch(catalog, input) {
494
- const match = input.match;
502
+ /**
503
+ * Describe a selection with the enabled plugins, for the dashboard and Slack.
504
+ * Its problems are the same as `registeredEventTriggerProblems`.
505
+ */
506
+ function describeRegisteredEventTrigger(catalog, selection) {
507
+ return describeEventTrigger(catalog[selection.namespace]?.resourceTypes ?? [], selection);
508
+ }
509
+ function uniqueMatchValues(match) {
495
510
  if (!match || Object.keys(match).length === 0) return void 0;
496
- const fields = eventMatchFields(catalog, input.namespace, input.resourceType);
497
- if (!fields || Object.keys(fields).length === 0) throw new ToolInputError(`Resource type "${input.namespace}:${input.resourceType}" does not support match.`);
498
- const normalized = {};
499
- for (const [key, value] of Object.entries(match)) {
500
- const field = fields[key];
501
- if (!field) throw new ToolInputError(`Resource type "${input.namespace}:${input.resourceType}" does not support match key "${key}".`);
502
- if (Array.isArray(value)) {
503
- if (field.kind === "boolean") throw new ToolInputError(`Match key "${key}" is boolean and cannot use a list.`);
504
- for (const entry of value) {
505
- if (typeof entry !== field.kind) throw new ToolInputError(`Match key "${key}" expects ${field.kind} values.`);
506
- if (field.enum && typeof entry === "string" && !field.enum.includes(entry)) throw new ToolInputError(`Match key "${key}" does not allow value "${entry}".`);
507
- }
508
- normalized[key] = [...new Set(value)];
509
- continue;
510
- }
511
- if (typeof value !== field.kind) throw new ToolInputError(`Match key "${key}" expects a ${field.kind} value.`);
512
- if (field.enum && typeof value === "string" && !field.enum.includes(value)) throw new ToolInputError(`Match key "${key}" does not allow value "${value}".`);
513
- normalized[key] = value;
511
+ return Object.fromEntries(Object.entries(match).map(([key, value]) => [key, Array.isArray(value) ? [...new Set(value)] : value]));
512
+ }
513
+ /**
514
+ * Reject an event automation trigger or a Watch that cannot work, with every
515
+ * problem in one message for the agent. Return its match without repeated
516
+ * list values.
517
+ */
518
+ function requireValidEventTrigger(catalog, selection, target) {
519
+ const problems = registeredEventTriggerProblems(catalog, selection, { inputKeys: true });
520
+ if (problems.length > 0) {
521
+ const resource = `${catalog[selection.namespace]?.displayName ?? selection.namespace} ${selection.resourceType.replaceAll("_", " ")}`;
522
+ throw new ToolInputError([target === "watch" ? `This ${resource} watch cannot be created.` : `This ${resource} trigger cannot be saved.`, ...problems.map((problem) => problem.message)].join(" "));
514
523
  }
515
- return Object.keys(normalized).length > 0 ? normalized : void 0;
524
+ return uniqueMatchValues(selection.match);
516
525
  }
517
526
  /**
518
527
  * Normalize one selector with the convention declared by its plugin.
@@ -524,7 +533,7 @@ function requireEventIdentifier(catalog, input) {
524
533
  const registration = catalog[input.namespace];
525
534
  const identifier = normalizeEventIdentifier(registration, input.identifier);
526
535
  const resourceTypes = registration?.resourceTypes ?? [];
527
- const fits = (resourceType) => resourceType.identifier !== void 0 && identifier.search(resourceType.identifier.pattern) !== -1;
536
+ const fits = (resourceType) => resourceType.identifier !== void 0 && new RegExp(resourceType.identifier.pattern).test(identifier);
528
537
  const resourceType = resourceTypes.find((candidate) => candidate.type === input.resourceType);
529
538
  if (!resourceType?.identifier || fits(resourceType)) return identifier;
530
539
  const fitting = resourceTypes.filter(fits).map((candidate) => candidate.type);
@@ -539,18 +548,22 @@ const WORKSPACE_SNAPSHOT_FAILED_EVENT = "workspace_snapshot.failed";
539
548
  * Core catalog entry for Workspace snapshot ready and failed events.
540
549
  *
541
550
  * Uses the same registration shape as plugins so search, guidance, and tool
542
- * schemas treat these events like any other resource type.
551
+ * schemas treat these events like any other resource type. Snapshot finish
552
+ * does not run event-automation ingest, so only watches can use these events.
543
553
  */
544
554
  function workspaceSnapshotEvents() {
545
- return { resourceTypes: [{
546
- type: WORKSPACE_SNAPSHOT_RESOURCE_TYPE,
547
- supportedEvents: [WORKSPACE_SNAPSHOT_READY_EVENT, WORKSPACE_SNAPSHOT_FAILED_EVENT],
548
- suggestedEvents: [],
549
- guidance: {
550
- [WORKSPACE_SNAPSHOT_READY_EVENT]: "Call switchWorkspace again with the same Workspace name. Do not create another watch when switchWorkspace already returned a subscription.",
551
- [WORKSPACE_SNAPSHOT_FAILED_EVENT]: "Report the snapshot failure. Do not keep waiting for this Workspace unless the user asks to retry."
552
- }
553
- }] };
555
+ return {
556
+ resourceTypes: [{
557
+ type: WORKSPACE_SNAPSHOT_RESOURCE_TYPE,
558
+ supportedEvents: [WORKSPACE_SNAPSHOT_READY_EVENT, WORKSPACE_SNAPSHOT_FAILED_EVENT],
559
+ suggestedEvents: [],
560
+ guidance: {
561
+ [WORKSPACE_SNAPSHOT_READY_EVENT]: "Call switchWorkspace again with the same Workspace name. Do not create another watch when switchWorkspace already returned a subscription.",
562
+ [WORKSPACE_SNAPSHOT_FAILED_EVENT]: "Report the snapshot failure. Do not keep waiting for this Workspace unless the user asks to retry."
563
+ }
564
+ }],
565
+ limits: { [WORKSPACE_SNAPSHOT_RESOURCE_TYPE]: { automation: false } }
566
+ };
554
567
  }
555
568
  /** Describe the events that report when a snapshot build ends. */
556
569
  function workspaceSnapshotWatch(input) {
@@ -603,19 +616,28 @@ const CHANNEL_MESSAGE_MATCH_FIELDS = eventMatchFieldsSchema.parse({ userId: {
603
616
  kind: "string",
604
617
  description: "Slack user ID of the message author. Required to run on bot posts, such as alert bots."
605
618
  } });
606
- /** Event registration for new top-level Slack channel messages. */
619
+ /**
620
+ * Event registration for new top-level Slack channel messages. Only event
621
+ * automations can use it, so watchEvents rejects it.
622
+ */
607
623
  function slackChannelMessageEvents() {
608
- return { resourceTypes: [{
609
- type: CHANNEL_RESOURCE_TYPE,
610
- identifier: {
611
- format: `"${SLACK_CURRENT_CHANNEL_IDENTIFIER}" for the channel where the automation is created`,
612
- pattern: /^(?:current_channel|[CG][A-Z0-9]{2,})$/
613
- },
614
- supportedEvents: [MESSAGE_POSTED_EVENT],
615
- suggestedEvents: [],
616
- matchFields: CHANNEL_MESSAGE_MATCH_FIELDS,
617
- guidance: { [MESSAGE_POSTED_EVENT]: "A person or bot posted a new top-level message in this channel. A bot post runs the automation only when match names the bot in userId. A reply goes in that message's thread. If the instructions do not apply to this message, end with no action." }
618
- }] };
624
+ return {
625
+ resourceTypes: [{
626
+ type: CHANNEL_RESOURCE_TYPE,
627
+ identifier: {
628
+ format: `"${SLACK_CURRENT_CHANNEL_IDENTIFIER}" for the channel where the automation is created`,
629
+ pattern: String.raw`^(?:current_channel|[CG][A-Z0-9]{2,})$`
630
+ },
631
+ supportedEvents: [MESSAGE_POSTED_EVENT],
632
+ suggestedEvents: [],
633
+ matchFields: CHANNEL_MESSAGE_MATCH_FIELDS,
634
+ guidance: { [MESSAGE_POSTED_EVENT]: "A person or bot posted a new top-level message in this channel. A bot post runs the automation only when match names the bot in userId. A reply goes in that message's thread. If the instructions do not apply to this message, end with no action." }
635
+ }],
636
+ limits: { [CHANNEL_RESOURCE_TYPE]: {
637
+ watch: false,
638
+ watchUnavailableMessage: "Slack channel messages can trigger only an event automation in that channel. Use createEventAutomation."
639
+ } }
640
+ };
619
641
  }
620
642
  /**
621
643
  * Bind a Slack channel-message automation to its own channel.
@@ -691,19 +713,37 @@ function slackChannelMessageReplyThreadTs(event) {
691
713
  * registration. Plugins may not use the `junior` or `slack` namespaces.
692
714
  */
693
715
  function getEventCatalog() {
716
+ const snapshot = workspaceSnapshotEvents();
694
717
  const catalog = {
695
- [CORE_EVENT_NAMESPACE]: { resourceTypes: [...workspaceSnapshotEvents().resourceTypes, {
696
- type: "timer",
697
- supportedEvents: ["timer.fired"],
698
- suggestedEvents: []
699
- }] },
700
- [SLACK_EVENT_NAMESPACE]: slackChannelMessageEvents()
718
+ [CORE_EVENT_NAMESPACE]: {
719
+ displayName: botConfig.userName,
720
+ resourceTypes: [...snapshot.resourceTypes, {
721
+ type: "timer",
722
+ supportedEvents: ["timer.fired"],
723
+ suggestedEvents: []
724
+ }],
725
+ limits: {
726
+ ...snapshot.limits,
727
+ timer: {
728
+ automation: false,
729
+ watch: false,
730
+ watchUnavailableMessage: "Use watchTimer to create a timer Watch."
731
+ }
732
+ }
733
+ },
734
+ [SLACK_EVENT_NAMESPACE]: {
735
+ ...slackChannelMessageEvents(),
736
+ displayName: "Slack"
737
+ }
701
738
  };
702
739
  for (const plugin of getPlugins()) {
703
740
  const registration = plugin.events;
704
741
  if (!registration || registration.isEnabled?.() === false) continue;
705
742
  if (plugin.manifest.name === "junior" || plugin.manifest.name === "slack") throw new Error(`Plugin "${plugin.manifest.name}" cannot register events; that namespace is reserved for core events`);
706
- catalog[plugin.manifest.name] = registration;
743
+ catalog[plugin.manifest.name] = {
744
+ ...registration,
745
+ displayName: plugin.manifest.displayName
746
+ };
707
747
  }
708
748
  return catalog;
709
749
  }
@@ -956,4 +996,4 @@ async function skipInputBeforeConversationStop(context) {
956
996
  await context.attempt.ack();
957
997
  return { status: "completed" };
958
998
  }
959
- export { requireSupportedEventMatch as A, readAutomationResult as B, eventNamespaceSchema as C, registeredEventTypeSchema as D, registeredEventMatchSchema as E, resetAutomatedTurnLimit as F, runDispatchOutcome as H, FINISH_AUTOMATION_RUN_TOOL_NAME as I, automationResultSchema as L, renderTaskInput as M, maybePostAutomatedTurnLimitNotice as N, registeredResourceTypeSchema as O, recordFinishedTurnForAutomatedLimit as P, deliversFinalText as R, eventGuidance as S, pluginSupportsEvent as T, remindMissingAutomationResult as V, WORKSPACE_SNAPSHOT_FAILED_EVENT as _, EVENT_WAIT_MS as a, workspaceSnapshotWatch as b, isEventMailboxMetadata as c, getEventCatalog as d, SLACK_EVENT_NAMESPACE as f, slackChannelMessageRunsAutomation as g, slackChannelMessageReplyThreadTs as h, skipInputBeforeConversationStop as i, joinMailboxText as j, requireEventIdentifier as k, renderEventBatchText as l, slackChannelMessageEvent as m, markConversationsStopped as n, enqueueEventNotification as o, requireSlackChannelMessageScope as p, session_stop_exports as r, eventBatchMessageEvents as s, getConversationStoppedAtMs as t, timerWatchCredentialSubject as u, WORKSPACE_SNAPSHOT_READY_EVENT as v, hasEventCatalogEntries as w, automationEventCatalog as x, workspaceSnapshotFinishedEvent as y, finishedRunReply as z };
999
+ export { requireEventIdentifier as A, finishedRunReply as B, eventGuidance as C, registeredEventMatchSchema as D, hasEventCatalogEntries as E, recordFinishedTurnForAutomatedLimit as F, remindMissingAutomationResult as H, resetAutomatedTurnLimit as I, FINISH_AUTOMATION_RUN_TOOL_NAME as L, joinMailboxText as M, renderTaskInput as N, registeredEventTypeSchema as O, maybePostAutomatedTurnLimitNotice as P, automationResultSchema as R, describeRegisteredEventTrigger as S, eventWatchUnavailable as T, runDispatchOutcome as U, readAutomationResult as V, WORKSPACE_SNAPSHOT_FAILED_EVENT as _, EVENT_WAIT_MS as a, workspaceSnapshotWatch as b, isEventMailboxMetadata as c, getEventCatalog as d, SLACK_EVENT_NAMESPACE as f, slackChannelMessageRunsAutomation as g, slackChannelMessageReplyThreadTs as h, skipInputBeforeConversationStop as i, requireValidEventTrigger as j, registeredResourceTypeSchema as k, renderEventBatchText as l, slackChannelMessageEvent as m, markConversationsStopped as n, enqueueEventNotification as o, requireSlackChannelMessageScope as p, session_stop_exports as r, eventBatchMessageEvents as s, getConversationStoppedAtMs as t, timerWatchCredentialSubject as u, WORKSPACE_SNAPSHOT_READY_EVENT as v, eventNamespaceSchema as w, automationEventCatalog as x, workspaceSnapshotFinishedEvent as y, deliversFinalText as z };
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { m as logWarn } from "./logging-Dk_FpF36.js";
2
+ import { m as logWarn } from "./logging-D2QBriUo.js";
3
3
  import { s as skillRoots } from "./package-discovery-DJBompdd.js";
4
- import { n as pluginCatalogRuntime } from "./catalog-runtime-Suf-aLlh.js";
4
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-D3qbJlYz.js";
5
5
  import path from "node:path";
6
6
  import { z } from "zod";
7
7
  import fs from "node:fs/promises";
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { $ as toolInvocationDestination, Q as actorFromRun } from "./agent-runner-BUBas7Yy.js";
3
- import { n as createAndEnqueueAgentInvocation } from "./work-DLJMV02b.js";
2
+ import { $ as actorFromRun, et as toolInvocationDestination } from "./agent-runner-C6MwHJEs.js";
3
+ import { n as createAndEnqueueAgentInvocation } from "./work-DMbqK0kT.js";
4
4
  // src/chat/agent-invocations/spawn.ts
5
5
  var spawn_exports = /* @__PURE__ */ __exportAll({ bindSpawnAgent: () => bindSpawnAgent });
6
6
  /** Bind a Run's Actor and credentials to new Agent invocations. */
@@ -1,8 +1,8 @@
1
1
  import { l as TURN_REASONING_LEVELS, s as modelProfileSchema } from "./model-profile-Bg8EoCKD.js";
2
- import { f as getSqlExecutor, u as getConversationStore } from "./viewer-DmNRM88V.js";
2
+ import { d as getConversationStore, p as getSqlExecutor } from "./viewer-B2wXNspU.js";
3
3
  import { a as juniorAgentInvocations, i as juniorAgentBindings, n as AGENT_INVOCATION_PARENT_MAILBOX_STATUSES, t as AGENT_INVOCATION_MAILBOX_STATUSES } from "./agent-invocations-CxKldAeB.js";
4
- import "./executor-J9k2sp9m.js";
5
- import { p as credentialContextSchema } from "./registry-BFzQFV_y.js";
4
+ import "./executor-CAr7xQr2.js";
5
+ import { p as credentialContextSchema } from "./registry-COTeZ2cV.js";
6
6
  import { z } from "zod";
7
7
  import { createHash } from "node:crypto";
8
8
  import { and, asc, count, countDistinct, desc, eq, inArray, lt, ne, or, sql } from "drizzle-orm";
@@ -1,10 +1,10 @@
1
- import { B as toOptionalNumber, V as toOptionalString, z as isRecord } from "./logging-Dk_FpF36.js";
2
- import { i as getChatConfig } from "./config-Dnv8KmNs.js";
1
+ import { i as getChatConfig } from "./config-BayQ9hHH.js";
2
+ import { B as toOptionalNumber, V as toOptionalString, z as isRecord } from "./logging-D2QBriUo.js";
3
3
  import { s as parseStoredSlackActor } from "./actor-tlxzqaRe.js";
4
- import { B as sameDestination, R as parseDestination, u as getConversationStore } from "./viewer-DmNRM88V.js";
5
- import { gt as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-CqnZfuCt.js";
6
- import { n as getDefaultRedisStateAdapterFor, r as getStateAdapter } from "./adapter-BIacI0Bt.js";
7
- import "./job-queue-B4yTgklN.js";
4
+ import { V as sameDestination, d as getConversationStore, z as parseDestination } from "./viewer-B2wXNspU.js";
5
+ import { ln as JUNIOR_THREAD_STATE_TTL_MS } from "./credentialed-C6pKlB1p.js";
6
+ import { n as getDefaultRedisStateAdapterFor, r as getStateAdapter } from "./adapter-cUkPH3Om.js";
7
+ import "./job-queue-Cjo6Yg_b.js";
8
8
  import { z } from "zod";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { destinationSchema } from "@sentry/junior-plugin-api";
@@ -66,6 +66,11 @@ const agentInputSchema = z.object({
66
66
  }).strict().refine((input) => input.text.trim() || input.attachments?.length, { message: "agent input requires text or attachments" });
67
67
  /** Durable delivery modes for pending inbound mailbox work. */
68
68
  const inboundMessageDeliverySchema = z.enum(["defer", "interrupt"]);
69
+ /** Sentry trace propagation headers captured when an inbound message arrives. */
70
+ const inboundMessageTraceContextSchema = z.object({
71
+ baggage: z.string().optional(),
72
+ sentryTrace: z.string().min(1)
73
+ }).optional().catch(void 0);
69
74
  /** Redis mailbox shape kept for deployed workers that still require this field. */
70
75
  const storedInboundMessageSchema = z.object({
71
76
  attemptCount: z.number().finite().optional(),
@@ -77,7 +82,8 @@ const storedInboundMessageSchema = z.object({
77
82
  injectedAtMs: z.number().finite().optional(),
78
83
  input: agentInputSchema,
79
84
  receivedAtMs: z.number().finite(),
80
- source: inboundMessageSourceSchema
85
+ source: inboundMessageSourceSchema,
86
+ traceContext: inboundMessageTraceContextSchema
81
87
  }).strict().extend({ publishExternally: z.boolean() });
82
88
  function conversationKey(conversationId) {
83
89
  return `${CONVERSATION_PREFIX}:${conversationId}`;
@@ -139,8 +145,11 @@ function normalizeExecutionStatus(value) {
139
145
  function normalizeMessage(value) {
140
146
  const parsed = storedInboundMessageSchema.safeParse(value);
141
147
  if (!parsed.success) return;
142
- const { publishExternally: _publishExternally, ...message } = parsed.data;
143
- return message;
148
+ const { publishExternally: _publishExternally, traceContext, ...message } = parsed.data;
149
+ return traceContext ? {
150
+ ...message,
151
+ traceContext
152
+ } : message;
144
153
  }
145
154
  function storeMessage(message, publishExternally) {
146
155
  return {
@@ -1,10 +1,9 @@
1
- import { m as logWarn } from "./logging-Dk_FpF36.js";
2
- import { c as QUEUE_SIGNATURE_MAX_AGE_MS, d as createVercelQueueClient, l as signQueueMessage, p as runWithTurnRequestDeadline, u as verifyQueueMessage } from "./job-queue-B4yTgklN.js";
1
+ import { m as logWarn } from "./logging-D2QBriUo.js";
2
+ import { c as QUEUE_SIGNATURE_MAX_AGE_MS, d as getQueueProvider, l as signQueueMessage, p as runWithTurnRequestDeadline, u as verifyQueueMessage } from "./job-queue-Cjo6Yg_b.js";
3
3
  import { z } from "zod";
4
4
  import { createHash } from "node:crypto";
5
- import { handleCallback, registerDevConsumer } from "@vercel/queue";
6
5
  // src/chat/queue/callback.ts
7
- /** Shared Vercel Queue callback wiring for signed messages. */
6
+ /** Shared queue callback wiring for signed messages. */
8
7
  /** Build the HTTP callback and local-dev consumer for one signed queue. */
9
8
  function queueCallback(options) {
10
9
  const consume = async (message, metadata) => {
@@ -35,24 +34,20 @@ function queueCallback(options) {
35
34
  }
36
35
  if (options.maxDeliveries !== null && metadata.deliveryCount >= options.maxDeliveries) return { acknowledge: true };
37
36
  };
38
- const visibility = options.visibilityTimeoutSeconds === void 0 ? {} : { visibilityTimeoutSeconds: options.visibilityTimeoutSeconds };
37
+ const consumer = {
38
+ consumerGroup: options.consumerGroup,
39
+ handler,
40
+ retry,
41
+ topic: options.topic,
42
+ visibilityTimeoutSeconds: options.visibilityTimeoutSeconds
43
+ };
39
44
  return {
40
45
  /** Run a message that an in-process queue already trusts. */
41
46
  consume,
42
- create: () => handleCallback(handler, {
43
- retry,
44
- ...visibility
45
- }),
47
+ create: () => getQueueProvider().createCallback(consumer),
46
48
  registerDev: () => {
47
49
  if (process.env.NODE_ENV !== "development") return;
48
- return registerDevConsumer({
49
- client: createVercelQueueClient(),
50
- consumerGroup: options.consumerGroup,
51
- handler,
52
- retry,
53
- topic: options.topic,
54
- ...visibility
55
- });
50
+ return getQueueProvider().registerDevConsumer(consumer);
56
51
  }
57
52
  };
58
53
  }
@@ -114,7 +109,7 @@ function pluginTaskCallback() {
114
109
  maxDeliveries: PLUGIN_TASK_MAX_DELIVERIES,
115
110
  onRejected: logPluginTaskQueueMessageRejected,
116
111
  run: async (message) => {
117
- const { processPluginTask } = await import("./task-runner-C575f2Dd.js").then((n) => n.r);
112
+ const { processPluginTask } = await import("./task-runner-6mLmG6g_.js").then((n) => n.r);
118
113
  await processPluginTask(message);
119
114
  },
120
115
  topic: PLUGIN_TASK_QUEUE_TOPIC,
@@ -123,7 +118,7 @@ function pluginTaskCallback() {
123
118
  }
124
119
  /** Send one plugin task through the shared signed delivery path. */
125
120
  async function sendVercelPluginTask(message) {
126
- await createVercelQueueClient().send(PLUGIN_TASK_QUEUE_TOPIC, signPluginTaskQueueMessage(message), {
121
+ await getQueueProvider().send(PLUGIN_TASK_QUEUE_TOPIC, signPluginTaskQueueMessage(message), {
127
122
  idempotencyKey: pluginTaskId(message),
128
123
  retentionSeconds: QUEUE_SIGNATURE_MAX_AGE_MS / 1e3
129
124
  });
@@ -1,17 +1,17 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { $t as isToolResultMessage, Gt as getPiMessageRole, Kn as openSlackDirectMessage, Nt as getTurnRecord, Qt as isToolResultError, Rn as escapeSlackMrkdwnText, X as hydrateConversationMessages, Yt as instructionTextForProjection, en as normalizeToolNameFromResult, i as readScheduledAutomation, l as blockEventAutomation, m as getEventAutomation, nn as stripRuntimeTurnContext, nt as fallbackShortTitle, qn as postSlackMessage, zn as formatSlackLink } from "./tasks-LOItiYgL.js";
3
- import { g as runBestEffort, m as logWarn } from "./logging-Dk_FpF36.js";
2
+ import { $t as isToolResultMessage, Gt as getPiMessageRole, Nt as getTurnRecord, Qt as isToolResultError, X as hydrateConversationMessages, Yt as instructionTextForProjection, en as normalizeToolNameFromResult, i as readScheduledAutomation, l as blockEventAutomation, m as getEventAutomation, nn as stripRuntimeTurnContext, nt as fallbackShortTitle } from "./tasks-DGbrur6x.js";
3
+ import { g as runBestEffort, m as logWarn } from "./logging-D2QBriUo.js";
4
4
  import { g as parseSlackMessageTs } from "./actor-tlxzqaRe.js";
5
- import { T as agentTurnTotalTokens, d as getDb, r as readActorIdentity, u as getConversationStore, w as agentTurnCostUsd } from "./viewer-DmNRM88V.js";
5
+ import { E as agentTurnTotalTokens, T as agentTurnCostUsd, d as getConversationStore, f as getDb, r as readActorIdentity } from "./viewer-B2wXNspU.js";
6
6
  import { s as juniorConversations } from "./agent-invocations-CxKldAeB.js";
7
- import { a as juniorAutomationRuns, s as juniorAutomations } from "./executor-J9k2sp9m.js";
7
+ import { a as juniorAutomationRuns, o as juniorAutomationVersions, s as juniorAutomations } from "./executor-CAr7xQr2.js";
8
8
  import { m as sameActorIdentity } from "./history-AY8rs-FR.js";
9
- import { h as credentialSubjectSchema } from "./registry-BFzQFV_y.js";
10
- import { Ct as createPluginState, Et as createPluginLogger, It as createPluginConversationEvents, Qt as createPluginCache, Tt as createPluginModel, gt as JUNIOR_THREAD_STATE_TTL_MS, p as coreTaskRegistrations, u as getPlugins, wt as createPluginEmbedder, yt as getDashboardTaskLink } from "./agent-hooks-CqnZfuCt.js";
11
- import { r as getStateAdapter } from "./adapter-BIacI0Bt.js";
12
- import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-IDHdZA7R.js";
13
- import { t as resolveConversationRouting } from "./turn-session-routing-5Nyv4fso.js";
14
- import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-BRB49H-J.js";
9
+ import { h as credentialSubjectSchema } from "./registry-COTeZ2cV.js";
10
+ import { An as createPluginLogger, Bn as createPluginConversationEvents, Dn as createPluginState, On as createPluginEmbedder, _t as openSlackDirectMessage, fn as getDashboardTaskLink, g as coreTaskRegistrations, kn as createPluginModel, ln as JUNIOR_THREAD_STATE_TTL_MS, lt as escapeSlackMrkdwnText, nr as createPluginCache, p as getPlugins, ut as formatSlackLink, vt as postSlackMessage } from "./credentialed-C6pKlB1p.js";
11
+ import { r as getStateAdapter } from "./adapter-cUkPH3Om.js";
12
+ import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-D45lz-oC.js";
13
+ import { t as resolveConversationRouting } from "./turn-session-routing-BKx6rfVd.js";
14
+ import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-8XjDIORF.js";
15
15
  import { z } from "zod";
16
16
  import { createHash } from "node:crypto";
17
17
  import { and, asc, count, desc, eq, gte, inArray, isNotNull, lte, max, sql } from "drizzle-orm";
@@ -318,46 +318,64 @@ async function readAutomationTitlesByConversationId(conversations) {
318
318
  }
319
319
  /** Load newest-first executions for one automation, with conversation titles when present. */
320
320
  async function readAutomationExecutions(args) {
321
- return (await getDb().select({
322
- conversationId: juniorAutomationRuns.conversationId,
323
- durationMs: juniorConversations.durationMs,
324
- executedAtMs: juniorAutomationRuns.completedAtMs,
325
- executionId: juniorAutomationRuns.id,
326
- status: juniorAutomationRuns.status,
327
- title: juniorConversations.title,
328
- usage: juniorConversations.usage
329
- }).from(juniorAutomationRuns).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId)).where(and(isExecution(), eq(juniorAutomationRuns.automationId, args.automationId))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => mapAutomationExecutionRecord(row));
321
+ return (await getDb().select(executionColumns).from(juniorAutomationRuns).leftJoin(juniorConversations, executionConversation()).leftJoin(juniorAutomationVersions, executionVersion()).where(and(isExecution(), eq(juniorAutomationRuns.automationId, args.automationId))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => mapAutomationExecutionRecord(row));
330
322
  }
331
323
  /** Load newest-first executions for the supplied viewer-visible automations. */
332
324
  async function readAutomationRuns(args) {
333
325
  if (args.automationIds.length === 0) return [];
334
326
  return (await getDb().select({
335
- conversationId: juniorAutomationRuns.conversationId,
336
- durationMs: juniorConversations.durationMs,
337
- executedAtMs: juniorAutomationRuns.completedAtMs,
338
- executionId: juniorAutomationRuns.id,
327
+ ...executionColumns,
339
328
  kind: juniorAutomations.kind,
340
- status: juniorAutomationRuns.status,
341
- automationId: juniorAutomationRuns.automationId,
342
- title: juniorConversations.title,
343
- usage: juniorConversations.usage
344
- }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId)).where(and(isExecution(), inArray(juniorAutomationRuns.automationId, args.automationIds))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => ({
329
+ automationId: juniorAutomationRuns.automationId
330
+ }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).leftJoin(juniorConversations, executionConversation()).leftJoin(juniorAutomationVersions, executionVersion()).where(and(isExecution(), inArray(juniorAutomationRuns.automationId, args.automationIds))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => ({
345
331
  ...mapAutomationExecutionRecord(row),
346
332
  kind: row.kind,
347
333
  automationId: row.automationId
348
334
  }));
349
335
  }
336
+ /** Columns that describe one execution, its Conversation, and its version. */
337
+ const executionColumns = {
338
+ automationVersion: juniorAutomationRuns.automationVersion,
339
+ conversationId: juniorAutomationRuns.conversationId,
340
+ credentialMode: sql`${juniorAutomationVersions.definition}->>'credentialMode'`,
341
+ durationMs: juniorConversations.durationMs,
342
+ errorMessage: juniorAutomationRuns.errorMessage,
343
+ executedAtMs: juniorAutomationRuns.completedAtMs,
344
+ executionId: juniorAutomationRuns.id,
345
+ scheduledForMs: juniorAutomationRuns.scheduledForMs,
346
+ startedAtMs: juniorAutomationRuns.startedAtMs,
347
+ status: juniorAutomationRuns.status,
348
+ title: juniorConversations.title,
349
+ usage: juniorConversations.usage
350
+ };
351
+ function executionConversation() {
352
+ return eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId);
353
+ }
354
+ function executionVersion() {
355
+ return and(eq(juniorAutomationVersions.automationId, juniorAutomationRuns.automationId), eq(juniorAutomationVersions.version, juniorAutomationRuns.automationVersion));
356
+ }
357
+ function isoTime(ms) {
358
+ return ms === null ? void 0 : new Date(ms).toISOString();
359
+ }
350
360
  function mapAutomationExecutionRecord(row) {
351
361
  const title = row.title?.trim();
352
362
  const costUsd = agentTurnCostUsd(row.usage ?? void 0);
353
363
  const totalTokens = agentTurnTotalTokens(row.usage ?? void 0);
354
364
  const durationMs = typeof row.durationMs === "number" && Number.isFinite(row.durationMs) && row.durationMs > 0 ? row.durationMs : void 0;
365
+ const error = row.errorMessage?.trim();
366
+ const scheduledFor = isoTime(row.scheduledForMs);
367
+ const startedAt = isoTime(row.startedAtMs);
355
368
  return {
369
+ ...row.automationVersion !== null ? { automationVersion: row.automationVersion } : void 0,
356
370
  ...row.conversationId ? { conversationId: row.conversationId } : void 0,
357
371
  ...costUsd !== void 0 ? { costUsd } : void 0,
372
+ ...row.credentialMode === "creator" || row.credentialMode === "system" ? { credentialMode: row.credentialMode } : void 0,
358
373
  ...durationMs !== void 0 ? { durationMs } : void 0,
374
+ ...error ? { error } : void 0,
359
375
  executedAt: new Date(row.executedAtMs).toISOString(),
360
376
  executionId: row.executionId,
377
+ ...scheduledFor ? { scheduledFor } : void 0,
378
+ ...startedAt ? { startedAt } : void 0,
361
379
  status: row.status,
362
380
  ...title ? { title } : void 0,
363
381
  ...totalTokens !== void 0 ? { totalTokens } : void 0