@sentry/junior 0.126.1 → 0.128.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 (124) hide show
  1. package/dist/{agent-hooks-U5YGLX6Y.js → agent-hooks-F75ANGBV.js} +9 -9
  2. package/dist/api/people/spend.d.ts +4 -0
  3. package/dist/api/people/spend.query.d.ts +3 -0
  4. package/dist/api/schema/person.d.ts +9 -0
  5. package/dist/api/schema.d.ts +2 -2
  6. package/dist/api/schema.js +3 -1
  7. package/dist/api.js +141 -40
  8. package/dist/app.js +289 -146
  9. package/dist/{catalog-runtime-OUPLWD7F.js → catalog-runtime-ZPSA5QM2.js} +6 -6
  10. package/dist/chat/agent-dispatch/context.d.ts +17 -0
  11. package/dist/chat/agent-dispatch/store.d.ts +1 -1
  12. package/dist/chat/agent-dispatch/work.d.ts +1 -1
  13. package/dist/chat/credentials/context.d.ts +27 -0
  14. package/dist/chat/credentials/subject.d.ts +15 -0
  15. package/dist/chat/event-tasks/ingest.d.ts +9 -0
  16. package/dist/chat/event-tasks/store.d.ts +15 -0
  17. package/dist/chat/event-tasks/tool-support.d.ts +140 -0
  18. package/dist/chat/event-tasks/types.d.ts +44 -0
  19. package/dist/chat/plugins/agent-hooks.d.ts +4 -2
  20. package/dist/chat/plugins/viewer.d.ts +16 -0
  21. package/dist/chat/resource-events/catalog.d.ts +21 -0
  22. package/dist/chat/resource-events/ingest.d.ts +2 -1
  23. package/dist/chat/resource-events/notification.d.ts +2 -2
  24. package/dist/chat/resource-events/store.d.ts +11 -9
  25. package/dist/chat/resource-events/tool-support.d.ts +10 -0
  26. package/dist/chat/resource-events/workspace.d.ts +4 -0
  27. package/dist/chat/sandbox/command-result.d.ts +3 -22
  28. package/dist/chat/sandbox/egress/schemas.d.ts +11 -0
  29. package/dist/chat/services/guardian-action-policy.d.ts +1 -1
  30. package/dist/chat/slack/dispatch-turn.d.ts +1 -1
  31. package/dist/chat/slack/tools/canvas/create.d.ts +0 -16
  32. package/dist/chat/slack/tools/canvas/edit.d.ts +0 -16
  33. package/dist/chat/slack/tools/canvas/read.d.ts +0 -16
  34. package/dist/chat/slack/tools/canvas/write.d.ts +0 -16
  35. package/dist/chat/slack/tools/channel-list-messages.d.ts +0 -16
  36. package/dist/chat/slack/tools/conversation-search.d.ts +0 -16
  37. package/dist/chat/slack/tools/list/add-items.d.ts +0 -16
  38. package/dist/chat/slack/tools/list/create.d.ts +0 -16
  39. package/dist/chat/slack/tools/list/get-items.d.ts +0 -16
  40. package/dist/chat/slack/tools/list/update-item.d.ts +0 -16
  41. package/dist/chat/slack/tools/message-add-reaction.d.ts +0 -16
  42. package/dist/chat/slack/tools/public-search.d.ts +0 -16
  43. package/dist/chat/slack/tools/send-files.d.ts +10 -28
  44. package/dist/chat/slack/tools/thread-read.d.ts +0 -16
  45. package/dist/chat/slack/tools/user-lookup.d.ts +0 -16
  46. package/dist/chat/task-execution/slack-work.d.ts +4 -4
  47. package/dist/chat/tool-support/action-review.d.ts +1 -1
  48. package/dist/chat/tool-support/structured-result.d.ts +17 -46
  49. package/dist/chat/tool-support/text-range-result.d.ts +8 -32
  50. package/dist/chat/tool-support/zod-tool.d.ts +6 -7
  51. package/dist/chat/tools/create-event-task.d.ts +65 -0
  52. package/dist/chat/tools/definition.d.ts +4 -0
  53. package/dist/chat/tools/delete-event-task.d.ts +57 -0
  54. package/dist/chat/tools/event-tasks.d.ts +5 -0
  55. package/dist/chat/tools/handoff/tool.d.ts +0 -16
  56. package/dist/chat/tools/list-event-tasks.d.ts +55 -0
  57. package/dist/chat/tools/list-resource-event-subscriptions.d.ts +42 -0
  58. package/dist/chat/tools/query-conversation-events.d.ts +0 -16
  59. package/dist/chat/tools/resource-events.d.ts +2 -1
  60. package/dist/chat/tools/runtime/report-progress.d.ts +0 -16
  61. package/dist/chat/tools/sandbox/edit-file.d.ts +0 -23
  62. package/dist/chat/tools/sandbox/file-utils.d.ts +2 -17
  63. package/dist/chat/tools/sandbox/find-files.d.ts +5 -8
  64. package/dist/chat/tools/sandbox/grep.d.ts +1 -4
  65. package/dist/chat/tools/sandbox/list-dir.d.ts +1 -4
  66. package/dist/chat/tools/search-resource-event-types.d.ts +44 -0
  67. package/dist/chat/tools/search-tools.d.ts +0 -16
  68. package/dist/chat/tools/skill/load-skill.d.ts +0 -20
  69. package/dist/chat/tools/skill/search-mcp-tools.d.ts +0 -16
  70. package/dist/chat/tools/stop-watching-resources.d.ts +28 -0
  71. package/dist/chat/tools/system-time.d.ts +0 -16
  72. package/dist/chat/tools/types.d.ts +8 -4
  73. package/dist/chat/tools/update-event-task.d.ts +66 -0
  74. package/dist/chat/tools/watch-resource-events.d.ts +59 -0
  75. package/dist/chat/tools/web/fetch-content.d.ts +0 -2
  76. package/dist/chat/tools/web/fetch-tool.d.ts +0 -16
  77. package/dist/chat/tools/web/image-generate.d.ts +0 -16
  78. package/dist/chat/tools/web/search.d.ts +0 -16
  79. package/dist/{chunk-QVSYR5CV.js → chunk-2HTHX6XV.js} +4 -4
  80. package/dist/{chunk-Y2QRKPI7.js → chunk-3PKF6YNU.js} +1 -1
  81. package/dist/{chunk-GLNB2R42.js → chunk-5KKIBIIP.js} +12 -12
  82. package/dist/{chunk-W3U3YQYI.js → chunk-77VDLF3Z.js} +18 -1
  83. package/dist/{chunk-YZHA2L7P.js → chunk-A76LKS5W.js} +230 -102
  84. package/dist/{chunk-6ZBSTNYN.js → chunk-AFJF3RGU.js} +9 -0
  85. package/dist/{chunk-PD6J33TO.js → chunk-B6E3YA2H.js} +1 -1
  86. package/dist/{chunk-S4LM6UB7.js → chunk-B7FPRGUJ.js} +3 -3
  87. package/dist/{chunk-R3GXQJEE.js → chunk-BY43CEXN.js} +2 -2
  88. package/dist/{chunk-HDSDCEPK.js → chunk-EPBBYMNE.js} +1 -1
  89. package/dist/{chunk-YFE6VNHU.js → chunk-FJEIX3AH.js} +2 -2
  90. package/dist/{chunk-F2ARYRKC.js → chunk-G6XQODHM.js} +1 -1
  91. package/dist/{chunk-KBW6J5GA.js → chunk-HBFQLSGV.js} +4 -4
  92. package/dist/{chunk-YUPNY2HZ.js → chunk-JDCPDJ54.js} +2 -2
  93. package/dist/{chunk-WIIPHGJY.js → chunk-K5ZB3WWH.js} +2 -2
  94. package/dist/{chunk-OG6E44DH.js → chunk-KNRODVH2.js} +6 -6
  95. package/dist/{chunk-W3JHZHAN.js → chunk-KTGJAWRO.js} +1 -1
  96. package/dist/{chunk-7VF3JAVF.js → chunk-NHWE6Y7G.js} +7 -7
  97. package/dist/{chunk-HXWOZ4HG.js → chunk-OLGB5Y7R.js} +2 -2
  98. package/dist/{chunk-QMK2VF6K.js → chunk-PBWUUVVG.js} +1 -1
  99. package/dist/{chunk-7ZMY4GCG.js → chunk-RYWMDDWK.js} +1 -1
  100. package/dist/{chunk-S4PUICSB.js → chunk-SHMHHXD5.js} +34 -5
  101. package/dist/{chunk-JKV244BI.js → chunk-WZ365SQC.js} +1 -1
  102. package/dist/{chunk-SD2VFSN6.js → chunk-YY6CVET7.js} +1428 -1116
  103. package/dist/{chunk-U36WL4OL.js → chunk-Z4KTQMWW.js} +1 -1
  104. package/dist/cli/chat.js +27 -27
  105. package/dist/cli/check.js +7 -7
  106. package/dist/cli/plugins.js +13 -13
  107. package/dist/cli/snapshot-create.js +10 -10
  108. package/dist/cli/upgrade.js +8 -8
  109. package/dist/db/schema/event-tasks.d.ts +161 -0
  110. package/dist/db/schema.d.ts +163 -1
  111. package/dist/{db-FQID26JW.js → db-FYJYPAKG.js} +7 -7
  112. package/dist/instrumentation.js +4 -4
  113. package/dist/nitro.js +2 -2
  114. package/dist/{oauth-callback-server-Z5MHHKFD.js → oauth-callback-server-W3PPIHCS.js} +18 -18
  115. package/dist/{runner-PGPGKYSF.js → runner-5NOFUYKR.js} +11 -11
  116. package/dist/{sandbox-egress-signals-CDFAP67O.js → sandbox-egress-signals-A5HWIQH4.js} +2 -2
  117. package/dist/{skills-BCQHR636.js → skills-ENIBKF5V.js} +7 -7
  118. package/dist/{validation-QP3FYBLZ.js → validation-BHLBOO6K.js} +7 -7
  119. package/dist/version.js +1 -1
  120. package/migrations/0015_magical_ezekiel_stane.sql +11 -0
  121. package/migrations/meta/0015_snapshot.json +1360 -0
  122. package/migrations/meta/_journal.json +7 -0
  123. package/package.json +5 -5
  124. package/dist/chat/plugins/viewer-actors.d.ts +0 -6
@@ -13,7 +13,7 @@ import {
13
13
  persistAuthPauseTurnState,
14
14
  requireTurnFailureEventId,
15
15
  scheduleSessionCompletedPluginTasks
16
- } from "./chunk-OG6E44DH.js";
16
+ } from "./chunk-KNRODVH2.js";
17
17
  import {
18
18
  buildSentryConversationUrl
19
19
  } from "./chunk-4CODA7P5.js";
@@ -40,7 +40,7 @@ import {
40
40
  resolveBaseUrl,
41
41
  resolvePluginOAuthAccount,
42
42
  truncateStatusText
43
- } from "./chunk-7VF3JAVF.js";
43
+ } from "./chunk-NHWE6Y7G.js";
44
44
  import {
45
45
  JUNIOR_THREAD_STATE_TTL_MS,
46
46
  TurnSliceLimitExceededError,
@@ -73,13 +73,13 @@ import {
73
73
  recordDeliveredAssistantMessage,
74
74
  turnHasReply,
75
75
  updateConversationStats
76
- } from "./chunk-KBW6J5GA.js";
76
+ } from "./chunk-HBFQLSGV.js";
77
77
  import {
78
78
  parseSlackThreadId,
79
79
  readSlackRawMessageContext,
80
80
  resolveSlackChannelIdFromMessage,
81
81
  resolveSlackChannelIdFromThreadId
82
- } from "./chunk-QMK2VF6K.js";
82
+ } from "./chunk-PBWUUVVG.js";
83
83
  import {
84
84
  SlackActionError,
85
85
  getDashboardConversationLink,
@@ -88,19 +88,19 @@ import {
88
88
  getSlackClient,
89
89
  isDmChannel,
90
90
  normalizeSlackConversationId
91
- } from "./chunk-YZHA2L7P.js";
91
+ } from "./chunk-A76LKS5W.js";
92
92
  import {
93
93
  getConversationEventStore,
94
94
  getConversationStore,
95
95
  parseDestination,
96
96
  requireSlackDestination,
97
97
  sameDestination
98
- } from "./chunk-S4LM6UB7.js";
98
+ } from "./chunk-B7FPRGUJ.js";
99
99
  import {
100
100
  acquireActiveLock,
101
101
  getDefaultRedisStateAdapterFor,
102
102
  getStateAdapter
103
- } from "./chunk-F2ARYRKC.js";
103
+ } from "./chunk-G6XQODHM.js";
104
104
  import {
105
105
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
106
106
  botConfig,
@@ -109,18 +109,18 @@ import {
109
109
  getRuntimeMetadata,
110
110
  normalizeSlackEmojiName,
111
111
  standardModelId
112
- } from "./chunk-WIIPHGJY.js";
112
+ } from "./chunk-K5ZB3WWH.js";
113
113
  import {
114
114
  discoverSkills
115
- } from "./chunk-R3GXQJEE.js";
115
+ } from "./chunk-BY43CEXN.js";
116
116
  import {
117
117
  pluginCatalogRuntime
118
- } from "./chunk-JKV244BI.js";
118
+ } from "./chunk-WZ365SQC.js";
119
119
  import {
120
120
  buildOAuthTokenRequest,
121
121
  hasRequiredOAuthScope,
122
122
  parseOAuthTokenResponse
123
- } from "./chunk-HXWOZ4HG.js";
123
+ } from "./chunk-OLGB5Y7R.js";
124
124
  import {
125
125
  createSlackResumeActor,
126
126
  isUserActor,
@@ -139,7 +139,7 @@ import {
139
139
  toOptionalNumber,
140
140
  toOptionalString,
141
141
  withLogContext
142
- } from "./chunk-PD6J33TO.js";
142
+ } from "./chunk-B6E3YA2H.js";
143
143
  import {
144
144
  homeDir
145
145
  } from "./chunk-H4HBKISO.js";
@@ -18,9 +18,16 @@ var scheduledTaskCredentialSubjectBindingSchema = z.object({
18
18
  taskId: exactNonBlankStringSchema,
19
19
  signature: z.string().min(1)
20
20
  }).strict();
21
+ var eventTaskCredentialSubjectBindingSchema = z.object({
22
+ type: z.literal("event-task"),
23
+ plugin: z.string().min(1),
24
+ taskId: exactNonBlankStringSchema,
25
+ signature: z.string().min(1)
26
+ }).strict();
21
27
  var credentialSubjectBindingSchema = z.discriminatedUnion("type", [
22
28
  slackDirectCredentialSubjectBindingSchema,
23
- scheduledTaskCredentialSubjectBindingSchema
29
+ scheduledTaskCredentialSubjectBindingSchema,
30
+ eventTaskCredentialSubjectBindingSchema
24
31
  ]);
25
32
  var credentialUserActorSchema = z.object({
26
33
  type: z.literal("user"),
@@ -46,6 +53,16 @@ var credentialSubjectSchema = z.discriminatedUnion("allowedWhen", [
46
53
  }).strict().refine((subject) => subject.binding.taskId === subject.taskId, {
47
54
  message: "Scheduled task credential subject requires task binding",
48
55
  path: ["binding"]
56
+ }),
57
+ z.object({
58
+ type: z.literal("user"),
59
+ userId: exactActorIdSchema,
60
+ allowedWhen: z.literal("event-task"),
61
+ taskId: exactNonBlankStringSchema,
62
+ binding: eventTaskCredentialSubjectBindingSchema
63
+ }).strict().refine((subject) => subject.binding.taskId === subject.taskId, {
64
+ message: "Event task credential subject requires task binding",
65
+ path: ["binding"]
49
66
  })
50
67
  ]);
51
68
  var credentialContextSchema = z.union([
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  getConversationEventStore,
3
3
  getDb
4
- } from "./chunk-S4LM6UB7.js";
4
+ } from "./chunk-B7FPRGUJ.js";
5
5
  import {
6
6
  juniorConversationAnnotations,
7
7
  juniorConversationEvents,
8
8
  juniorIdentities,
9
9
  juniorUsers
10
- } from "./chunk-S4PUICSB.js";
10
+ } from "./chunk-SHMHHXD5.js";
11
11
  import {
12
12
  SANDBOX_WORKSPACE_ROOT,
13
13
  getStateAdapter,
14
14
  sleep
15
- } from "./chunk-F2ARYRKC.js";
15
+ } from "./chunk-G6XQODHM.js";
16
16
  import {
17
17
  botConfig,
18
18
  completeObject,
19
19
  embedTexts,
20
20
  getSlackBotToken,
21
21
  standardModelId
22
- } from "./chunk-WIIPHGJY.js";
22
+ } from "./chunk-K5ZB3WWH.js";
23
23
  import {
24
24
  isActorUserId,
25
25
  parseActorUserId
@@ -37,14 +37,16 @@ import {
37
37
  setSpanAttributes,
38
38
  setSpanStatus,
39
39
  withSpan
40
- } from "./chunk-PD6J33TO.js";
40
+ } from "./chunk-B6E3YA2H.js";
41
41
 
42
42
  // src/chat/plugins/agent-hooks.ts
43
43
  import {
44
44
  missingToolAnnotationKeys,
45
+ normalizeResourceEventIdentifier,
45
46
  promptContextSchema,
46
47
  promptMessageSchema,
47
- resourceEventSchema
48
+ pluginResourceEventsSchema,
49
+ resourceEventInputSchema
48
50
  } from "@sentry/junior-plugin-api";
49
51
 
50
52
  // src/chat/plugins/annotations.ts
@@ -422,88 +424,6 @@ function getDashboardConversationLink(conversationId) {
422
424
  return `${baseURL}${path}`;
423
425
  }
424
426
 
425
- // src/chat/slack/tools/context.ts
426
- function getSlackToolContext(context) {
427
- if (context.source.platform !== "slack") {
428
- return void 0;
429
- }
430
- if (context.destination.platform !== "slack") {
431
- throw new TypeError("Slack source requires a Slack destination");
432
- }
433
- const destinationChannelId = parseSlackChannelReferenceId(
434
- context.destination.channelId
435
- );
436
- const sourceChannelId = parseSlackChannelReferenceId(
437
- context.source.channelId
438
- );
439
- const teamId = parseSlackTeamId(context.source.teamId);
440
- if (!destinationChannelId || !sourceChannelId || !teamId) {
441
- return void 0;
442
- }
443
- return {
444
- destination: context.destination,
445
- source: context.source,
446
- actor: context.actor?.platform === "slack" ? context.actor : void 0,
447
- destinationChannelId,
448
- messageTs: parseSlackMessageTs(context.source.messageTs),
449
- sourceChannelId,
450
- teamId,
451
- threadTs: parseSlackMessageTs(context.source.threadTs)
452
- };
453
- }
454
-
455
- // src/chat/plugins/viewer-actors.ts
456
- import { and as and3, eq as eq3 } from "drizzle-orm";
457
- import {
458
- actorSchema
459
- } from "@sentry/junior-plugin-api";
460
- async function readViewerActorsFromSql(db, email) {
461
- const normalizedEmail = normalizeIdentityEmail(email);
462
- if (!normalizedEmail) return [];
463
- const rows = await db.select({
464
- displayName: juniorIdentities.displayName,
465
- email: juniorUsers.primaryEmail,
466
- handle: juniorIdentities.handle,
467
- provider: juniorIdentities.provider,
468
- providerSubjectId: juniorIdentities.providerSubjectId,
469
- providerTenantId: juniorIdentities.providerTenantId
470
- }).from(juniorIdentities).innerJoin(juniorUsers, eq3(juniorUsers.id, juniorIdentities.userId)).where(
471
- and3(
472
- eq3(juniorUsers.primaryEmailNormalized, normalizedEmail),
473
- eq3(juniorIdentities.kind, "user")
474
- )
475
- );
476
- const actors = [];
477
- for (const row of rows) {
478
- const candidate = row.provider === "slack" ? {
479
- platform: "slack",
480
- teamId: row.providerTenantId,
481
- userId: row.providerSubjectId,
482
- email: row.email,
483
- ...row.displayName ? { fullName: row.displayName } : {},
484
- ...row.handle ? { userName: row.handle } : {}
485
- } : row.provider === "local" ? {
486
- platform: "local",
487
- userId: row.providerSubjectId,
488
- email: row.email,
489
- ...row.displayName ? { fullName: row.displayName } : {},
490
- ...row.handle ? { userName: row.handle } : {}
491
- } : void 0;
492
- if (!candidate) continue;
493
- const parsed = actorSchema.safeParse(candidate);
494
- if (parsed.success && parsed.data.platform !== "system") {
495
- actors.push(parsed.data);
496
- }
497
- }
498
- return actors;
499
- }
500
- async function readViewerActors(email) {
501
- return await readViewerActorsFromSql(getDb(), email);
502
- }
503
-
504
- // src/chat/credentials/subject.ts
505
- import { createHmac, timingSafeEqual } from "crypto";
506
-
507
427
  // src/chat/slack/client.ts
508
428
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
509
429
  import { WebClient } from "@slack/web-api";
@@ -914,7 +834,138 @@ async function downloadPrivateSlackFile(url) {
914
834
  );
915
835
  }
916
836
 
837
+ // src/chat/resource-events/workspace.ts
838
+ function canRouteResourceEvents() {
839
+ return Boolean(getSlackBotToken());
840
+ }
841
+ function createResourceEventTeamIdResolver() {
842
+ let pending;
843
+ return async () => {
844
+ if (!canRouteResourceEvents()) return void 0;
845
+ pending ??= getSlackClient().auth.test().then((result) => {
846
+ const teamId = result.team_id?.trim();
847
+ if (!teamId) {
848
+ throw new Error("Slack auth.test did not return a workspace team id");
849
+ }
850
+ return teamId;
851
+ }).catch((error) => {
852
+ pending = void 0;
853
+ throw error;
854
+ });
855
+ return await pending;
856
+ };
857
+ }
858
+
859
+ // src/chat/slack/tools/context.ts
860
+ function getSlackToolContext(context) {
861
+ if (context.source.platform !== "slack") {
862
+ return void 0;
863
+ }
864
+ if (context.destination.platform !== "slack") {
865
+ throw new TypeError("Slack source requires a Slack destination");
866
+ }
867
+ const destinationChannelId = parseSlackChannelReferenceId(
868
+ context.destination.channelId
869
+ );
870
+ const sourceChannelId = parseSlackChannelReferenceId(
871
+ context.source.channelId
872
+ );
873
+ const teamId = parseSlackTeamId(context.source.teamId);
874
+ if (!destinationChannelId || !sourceChannelId || !teamId) {
875
+ return void 0;
876
+ }
877
+ return {
878
+ destination: context.destination,
879
+ source: context.source,
880
+ actor: context.actor?.platform === "slack" ? context.actor : void 0,
881
+ destinationChannelId,
882
+ messageTs: parseSlackMessageTs(context.source.messageTs),
883
+ sourceChannelId,
884
+ teamId,
885
+ threadTs: parseSlackMessageTs(context.source.threadTs)
886
+ };
887
+ }
888
+
889
+ // src/chat/plugins/viewer.ts
890
+ import { randomUUID } from "crypto";
891
+ import { and as and3, asc, eq as eq3 } from "drizzle-orm";
892
+ import {
893
+ identitySchema,
894
+ userSchema
895
+ } from "@sentry/junior-plugin-api";
896
+ function identityFromRow(row) {
897
+ return identitySchema.parse({
898
+ id: row.id,
899
+ provider: row.provider,
900
+ providerSubjectId: row.providerSubjectId,
901
+ ...row.providerTenantId ? { providerTenantId: row.providerTenantId } : {},
902
+ ...row.displayName ? { displayName: row.displayName } : {},
903
+ ...row.handle ? { handle: row.handle } : {}
904
+ });
905
+ }
906
+ async function readUserById(db, userRow) {
907
+ const identityRows = await db.select().from(juniorIdentities).where(
908
+ and3(
909
+ eq3(juniorIdentities.userId, userRow.id),
910
+ eq3(juniorIdentities.kind, "user")
911
+ )
912
+ ).orderBy(
913
+ asc(juniorIdentities.provider),
914
+ asc(juniorIdentities.providerTenantId),
915
+ asc(juniorIdentities.providerSubjectId)
916
+ );
917
+ return userSchema.parse({
918
+ email: userRow.primaryEmail,
919
+ id: userRow.id,
920
+ identities: identityRows.map(identityFromRow),
921
+ ...userRow.displayName ? { displayName: userRow.displayName } : {}
922
+ });
923
+ }
924
+ async function resolveViewerUserFromSql(db, email) {
925
+ const normalizedEmail = normalizeIdentityEmail(email);
926
+ if (!normalizedEmail) return void 0;
927
+ const now = /* @__PURE__ */ new Date();
928
+ const rows = await db.insert(juniorUsers).values({
929
+ id: randomUUID(),
930
+ primaryEmail: email.trim(),
931
+ primaryEmailNormalized: normalizedEmail,
932
+ displayName: null,
933
+ createdAt: now,
934
+ updatedAt: now
935
+ }).onConflictDoNothing({ target: juniorUsers.primaryEmailNormalized }).returning();
936
+ const userRow = rows[0] ?? (await db.select().from(juniorUsers).where(eq3(juniorUsers.primaryEmailNormalized, normalizedEmail)).limit(1))[0];
937
+ return userRow ? await readUserById(db, userRow) : void 0;
938
+ }
939
+ async function resolveViewerUser(email) {
940
+ return await resolveViewerUserFromSql(getDb(), email);
941
+ }
942
+ async function readActorIdentityFromSql(db, actor) {
943
+ if (actor.platform === "system") return void 0;
944
+ const providerTenantId = actor.platform === "slack" ? actor.teamId : "";
945
+ const rows = await db.select({
946
+ identity: juniorIdentities,
947
+ user: juniorUsers
948
+ }).from(juniorIdentities).leftJoin(juniorUsers, eq3(juniorUsers.id, juniorIdentities.userId)).where(
949
+ and3(
950
+ eq3(juniorIdentities.kind, "user"),
951
+ eq3(juniorIdentities.provider, actor.platform),
952
+ eq3(juniorIdentities.providerTenantId, providerTenantId),
953
+ eq3(juniorIdentities.providerSubjectId, actor.userId)
954
+ )
955
+ ).limit(1);
956
+ const row = rows[0];
957
+ if (!row) return void 0;
958
+ return {
959
+ identity: identityFromRow(row.identity),
960
+ ...row.user ? { user: await readUserById(db, row.user) } : {}
961
+ };
962
+ }
963
+ async function readActorIdentity(actor) {
964
+ return await readActorIdentityFromSql(getDb(), actor);
965
+ }
966
+
917
967
  // src/chat/credentials/subject.ts
968
+ import { createHmac, timingSafeEqual } from "crypto";
918
969
  var CREDENTIAL_SUBJECT_HMAC_CONTEXT = "junior.credential_subject.v1";
919
970
  var CREDENTIAL_SUBJECT_SIGNATURE_VERSION = "v1";
920
971
  function getCredentialSubjectSecret() {
@@ -1039,6 +1090,51 @@ function verifyScheduledTaskCredentialSubject(input) {
1039
1090
  );
1040
1091
  return timingSafeMatch(expected, binding.signature);
1041
1092
  }
1093
+ function bindEventTaskCredentialSubject(input) {
1094
+ const secret = getCredentialSubjectSecret();
1095
+ const plugin = input.plugin.trim();
1096
+ const userId = parseActorUserId(input.subject.userId);
1097
+ if (!secret || plugin !== "junior" || !userId || input.subject.allowedWhen !== "event-task") {
1098
+ return void 0;
1099
+ }
1100
+ const taskId = input.subject.taskId;
1101
+ if (!taskId || taskId !== taskId.trim()) {
1102
+ return void 0;
1103
+ }
1104
+ return {
1105
+ type: "user",
1106
+ userId,
1107
+ allowedWhen: "event-task",
1108
+ taskId,
1109
+ binding: {
1110
+ type: "event-task",
1111
+ plugin,
1112
+ taskId,
1113
+ signature: signPayload(
1114
+ secret,
1115
+ buildPayload(["event-task", plugin, taskId, userId])
1116
+ )
1117
+ }
1118
+ };
1119
+ }
1120
+ function verifyEventTaskCredentialSubject(input) {
1121
+ const secret = getCredentialSubjectSecret();
1122
+ const { subject } = input;
1123
+ const binding = subject.binding;
1124
+ if (!secret || input.plugin !== "junior" || subject.type !== "user" || !isActorUserId(subject.userId) || subject.allowedWhen !== "event-task" || !subject.taskId || binding.type !== "event-task" || binding.plugin !== input.plugin || binding.taskId !== subject.taskId) {
1125
+ return false;
1126
+ }
1127
+ const expected = signPayload(
1128
+ secret,
1129
+ buildPayload([
1130
+ "event-task",
1131
+ binding.plugin,
1132
+ binding.taskId,
1133
+ subject.userId
1134
+ ])
1135
+ );
1136
+ return timingSafeMatch(expected, binding.signature);
1137
+ }
1042
1138
 
1043
1139
  // src/chat/slack/tools/channel-capabilities.ts
1044
1140
  function resolveChannelCapabilities(channelId) {
@@ -1249,6 +1345,9 @@ function validatePlugins(plugins) {
1249
1345
  if (seen.has(name)) {
1250
1346
  throw new Error(`Duplicate plugin name "${name}"`);
1251
1347
  }
1348
+ if (plugin.resourceEvents !== void 0 && !pluginResourceEventsSchema.safeParse(plugin.resourceEvents).success) {
1349
+ throw new Error(`Plugin "${name}" resourceEvents is invalid`);
1350
+ }
1252
1351
  for (const [taskName, task] of Object.entries(plugin.tasks ?? {})) {
1253
1352
  if (!PLUGIN_TOOL_NAME_RE.test(taskName)) {
1254
1353
  throw new Error(
@@ -1420,6 +1519,10 @@ function getPluginTools(context) {
1420
1519
  plugin: pluginName
1421
1520
  }) : void 0;
1422
1521
  const mcp = pluginMcpContext(plugin, context);
1522
+ const resourceEvents = {
1523
+ canSubscribe: context.source.platform === "slack" && canRouteResourceEvents()
1524
+ };
1525
+ const resolveActor = context.resolveActorIdentity ?? (async () => void 0);
1423
1526
  let pluginContext;
1424
1527
  if (context.source.platform === "slack") {
1425
1528
  if (context.destination.platform !== "slack") {
@@ -1440,7 +1543,9 @@ function getPluginTools(context) {
1440
1543
  egress: context.egress,
1441
1544
  ...mcp ? { mcp } : {},
1442
1545
  model: createPluginModel(pluginName, plugin.model),
1443
- state: createPluginState(pluginName)
1546
+ resourceEvents,
1547
+ state: createPluginState(pluginName),
1548
+ users: { resolveActor }
1444
1549
  };
1445
1550
  } else {
1446
1551
  if (context.destination.platform !== "local") {
@@ -1460,7 +1565,9 @@ function getPluginTools(context) {
1460
1565
  egress: context.egress,
1461
1566
  ...mcp ? { mcp } : {},
1462
1567
  model: createPluginModel(pluginName, plugin.model),
1463
- state: createPluginState(pluginName)
1568
+ resourceEvents,
1569
+ state: createPluginState(pluginName),
1570
+ users: { resolveActor }
1464
1571
  };
1465
1572
  }
1466
1573
  const pluginTools = hook(pluginContext);
@@ -1525,6 +1632,21 @@ function routeMethods(route, pluginName) {
1525
1632
  }
1526
1633
  return methods;
1527
1634
  }
1635
+ function requirePublishedResourceEvent(plugin, eventType) {
1636
+ const registration = plugin.resourceEvents;
1637
+ if (!registration || registration.isEnabled?.() === false) {
1638
+ throw new Error(
1639
+ `Plugin "${plugin.manifest.name}" cannot publish resource events without an active registration`
1640
+ );
1641
+ }
1642
+ if (!registration.resourceTypes.some(
1643
+ (resourceType) => resourceType.supportedEvents.includes(eventType)
1644
+ )) {
1645
+ throw new Error(
1646
+ `Plugin "${plugin.manifest.name}" did not register resource event "${eventType}"`
1647
+ );
1648
+ }
1649
+ }
1528
1650
  function getPluginRoutes(options) {
1529
1651
  const routes = [];
1530
1652
  const seen = /* @__PURE__ */ new Set();
@@ -1546,13 +1668,16 @@ function getPluginRoutes(options) {
1546
1668
  },
1547
1669
  resourceEvents: {
1548
1670
  async publish(event) {
1549
- const parsed = resourceEventSchema.parse(event);
1550
- if (parsed.provider !== pluginName) {
1551
- throw new Error(
1552
- `Plugin "${pluginName}" cannot publish resource events for provider "${parsed.provider}"`
1553
- );
1554
- }
1555
- await options.resourceEvents.publish(parsed);
1671
+ const parsed = resourceEventInputSchema.parse(event);
1672
+ requirePublishedResourceEvent(plugin, parsed.eventType);
1673
+ await options.resourceEvents.publish({
1674
+ ...parsed,
1675
+ identifier: normalizeResourceEventIdentifier(
1676
+ plugin.resourceEvents,
1677
+ parsed.identifier
1678
+ ),
1679
+ namespace: pluginName
1680
+ });
1556
1681
  }
1557
1682
  }
1558
1683
  });
@@ -1612,9 +1737,7 @@ function getPluginApiRoutes() {
1612
1737
  const app = hook({
1613
1738
  ...basePluginContext(plugin),
1614
1739
  eventStats: createPluginConversationEventStats(plugin),
1615
- viewer: {
1616
- actors: readViewerActors
1617
- }
1740
+ users: { resolve: resolveViewerUser }
1618
1741
  });
1619
1742
  if (app === void 0) {
1620
1743
  continue;
@@ -2083,8 +2206,6 @@ export {
2083
2206
  createPluginState,
2084
2207
  setDashboardConversationLinkOptions,
2085
2208
  getDashboardConversationLink,
2086
- getSlackToolContext,
2087
- readViewerActors,
2088
2209
  runWithWorkspaceTeamId,
2089
2210
  getWorkspaceTeamId,
2090
2211
  SlackActionError,
@@ -2097,10 +2218,17 @@ export {
2097
2218
  isConversationScopedChannel,
2098
2219
  getFilePermalink,
2099
2220
  downloadPrivateSlackFile,
2221
+ canRouteResourceEvents,
2222
+ createResourceEventTeamIdResolver,
2223
+ getSlackToolContext,
2224
+ resolveViewerUser,
2225
+ readActorIdentity,
2100
2226
  bindSlackDirectCredentialSubject,
2101
2227
  verifySlackDirectCredentialSubject,
2102
2228
  bindScheduledTaskCredentialSubject,
2103
2229
  verifyScheduledTaskCredentialSubject,
2230
+ bindEventTaskCredentialSubject,
2231
+ verifyEventTaskCredentialSubject,
2104
2232
  resolveChannelCapabilities,
2105
2233
  PluginHookDeniedError,
2106
2234
  validatePlugins,
@@ -523,6 +523,14 @@ var actorProfileReportSchema = z3.object({
523
523
  windowEnd: z3.string(),
524
524
  windowStart: z3.string()
525
525
  }).strict();
526
+ var personalSpendReportSchema = z3.object({
527
+ generatedAt: z3.string(),
528
+ sevenDaysUsd: z3.number().finite().nonnegative(),
529
+ source: z3.literal("conversation_index"),
530
+ thirtyDaysUsd: z3.number().finite().nonnegative(),
531
+ windowEnd: z3.string(),
532
+ windowStart: z3.string()
533
+ }).strict();
526
534
 
527
535
  // src/api/schema/location.ts
528
536
  import { z as z4 } from "zod";
@@ -619,6 +627,7 @@ export {
619
627
  personParamsSchema,
620
628
  actorDirectoryReportSchema,
621
629
  actorProfileReportSchema,
630
+ personalSpendReportSchema,
622
631
  locationParamsSchema,
623
632
  locationActivityDayReportSchema,
624
633
  locationDirectoryReportSchema,
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-6GWA276C.js";
10
10
  import {
11
11
  getDeploymentTelemetryAttributes
12
- } from "./chunk-7ZMY4GCG.js";
12
+ } from "./chunk-RYWMDDWK.js";
13
13
 
14
14
  // src/chat/logging.ts
15
15
  import path from "path";
@@ -4,12 +4,12 @@ import {
4
4
  juniorConversations,
5
5
  juniorIdentities,
6
6
  juniorUsers
7
- } from "./chunk-S4PUICSB.js";
7
+ } from "./chunk-SHMHHXD5.js";
8
8
  import {
9
9
  TURN_REASONING_LEVELS,
10
10
  getChatConfig,
11
11
  modelProfileSchema
12
- } from "./chunk-WIIPHGJY.js";
12
+ } from "./chunk-K5ZB3WWH.js";
13
13
  import {
14
14
  juniorDestinations
15
15
  } from "./chunk-VFUK3X5B.js";
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-F3B2YJ76.js";
20
20
  import {
21
21
  normalizeIdentityEmail
22
- } from "./chunk-PD6J33TO.js";
22
+ } from "./chunk-B6E3YA2H.js";
23
23
 
24
24
  // src/chat/conversations/sql/store.ts
25
25
  import { randomUUID as randomUUID2 } from "crypto";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-JKV244BI.js";
3
+ } from "./chunk-WZ365SQC.js";
4
4
  import {
5
5
  logWarn
6
- } from "./chunk-PD6J33TO.js";
6
+ } from "./chunk-B6E3YA2H.js";
7
7
  import {
8
8
  skillRoots
9
9
  } from "./chunk-H4HBKISO.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  credentialContextSchema
3
- } from "./chunk-W3U3YQYI.js";
3
+ } from "./chunk-77VDLF3Z.js";
4
4
 
5
5
  // src/chat/sandbox/egress/schemas.ts
6
6
  import { z } from "zod";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getDb
3
- } from "./chunk-S4LM6UB7.js";
3
+ } from "./chunk-B7FPRGUJ.js";
4
4
  import {
5
5
  juniorStats
6
- } from "./chunk-S4PUICSB.js";
6
+ } from "./chunk-SHMHHXD5.js";
7
7
 
8
8
  // src/stats.ts
9
9
  import { and, asc, gte, lte, sql } from "drizzle-orm";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getChatConfig
3
- } from "./chunk-WIIPHGJY.js";
3
+ } from "./chunk-K5ZB3WWH.js";
4
4
 
5
5
  // src/chat/state/adapter.ts
6
6
  import { createMemoryState } from "@chat-adapter/state-memory";