@sentry/junior 0.174.0 → 0.176.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 (62) hide show
  1. package/dist/{agent-hooks-HBZUWB4D.js → agent-hooks-ZPCHKYXI.js} +13 -12
  2. package/dist/api.js +27 -27
  3. package/dist/app.js +272 -344
  4. package/dist/{catalog-runtime-J3R3LC7Y.js → catalog-runtime-3Z2Y5IP6.js} +5 -5
  5. package/dist/chat/agent-dispatch/heartbeat.d.ts +1 -1
  6. package/dist/chat/agent-dispatch/store.d.ts +3 -16
  7. package/dist/chat/resource-events/catalog.d.ts +2 -0
  8. package/dist/chat/resource-events/notification.d.ts +1 -1
  9. package/dist/chat/resource-events/tool-support.d.ts +4 -0
  10. package/dist/{chunk-47LO5GDT.js → chunk-2N7YRFMW.js} +12 -12
  11. package/dist/{chunk-RL23S5DG.js → chunk-5JB5JJO5.js} +1 -1
  12. package/dist/{chunk-2BNKQSPC.js → chunk-64EGCW34.js} +8 -16
  13. package/dist/{chunk-DIW6IQYK.js → chunk-665JOQEP.js} +6 -6
  14. package/dist/{chunk-WEL4NWRR.js → chunk-6NQJQP7O.js} +5 -5
  15. package/dist/{chunk-5EWBGDW2.js → chunk-AJEZETLT.js} +1 -1
  16. package/dist/{chunk-GWZBBIV6.js → chunk-B65VEBJV.js} +2 -2
  17. package/dist/{chunk-HJFWO72I.js → chunk-B6IOTX7W.js} +1 -1
  18. package/dist/{chunk-T5VGNZ7F.js → chunk-B7U2QBMT.js} +16 -58
  19. package/dist/{chunk-ZBWAXCR3.js → chunk-BNGK2J6X.js} +1 -1
  20. package/dist/{chunk-IYI7RPHU.js → chunk-CI4JX64I.js} +1 -1
  21. package/dist/{chunk-XOXKL2DL.js → chunk-CNC65LIX.js} +1 -1
  22. package/dist/{chunk-4ALVTB7F.js → chunk-COFZR2CO.js} +1 -1
  23. package/dist/{chunk-KIU7RKE5.js → chunk-CTYWFV6L.js} +6 -6
  24. package/dist/{chunk-VZFLAGFK.js → chunk-CUA5SZR5.js} +1 -1
  25. package/dist/{chunk-2E353AHN.js → chunk-DVHYQDGM.js} +1 -1
  26. package/dist/{chunk-BFNNCBTK.js → chunk-FNOLPLXJ.js} +4 -4
  27. package/dist/{chunk-GX2ISGLH.js → chunk-GYRGJVWR.js} +472 -903
  28. package/dist/{chunk-FFUNPZA2.js → chunk-H7KTMGNI.js} +1 -1
  29. package/dist/{chunk-OTAPFODZ.js → chunk-IPOB6IL5.js} +98 -96
  30. package/dist/{chunk-JVORBSUR.js → chunk-JHCAX7XY.js} +3 -3
  31. package/dist/{chunk-BLBKORDG.js → chunk-JIZ3RFER.js} +505 -11
  32. package/dist/{chunk-DRRUFZKZ.js → chunk-KYHHJ6LS.js} +1 -1
  33. package/dist/{chunk-N7Y3T47I.js → chunk-OKXHYVKE.js} +4 -4
  34. package/dist/{chunk-REUKFWLH.js → chunk-PS3ZPRL2.js} +1 -1
  35. package/dist/{chunk-7V25DLCB.js → chunk-Q374NKGS.js} +8 -8
  36. package/dist/{chunk-W6HVH5JJ.js → chunk-RSIHA3XC.js} +5 -5
  37. package/dist/{chunk-Z23POK2V.js → chunk-SBIQ5MZW.js} +1 -1
  38. package/dist/{chunk-WH7F67EN.js → chunk-SGLLADGQ.js} +17 -17
  39. package/dist/{chunk-AJMUVCXW.js → chunk-VUAHVGPY.js} +4 -4
  40. package/dist/{chunk-6FWEUJGH.js → chunk-VVLQGGJJ.js} +1 -1
  41. package/dist/{chunk-OXYAEFKS.js → chunk-Z2YZ6QLG.js} +2 -2
  42. package/dist/cli/chat.js +31 -31
  43. package/dist/cli/check.js +6 -6
  44. package/dist/cli/plugins.js +14 -13
  45. package/dist/cli/snapshot-create.js +10 -10
  46. package/dist/cli/upgrade.js +7 -7
  47. package/dist/{db-6JOWUGLP.js → db-63K2IY36.js} +7 -7
  48. package/dist/instrumentation.js +4 -4
  49. package/dist/nitro.js +2 -2
  50. package/dist/{oauth-callback-server-ZLOKNXOW.js → oauth-callback-server-JZXBDXGO.js} +24 -24
  51. package/dist/{runner-YD4J2GBE.js → runner-3KWZJVK6.js} +16 -16
  52. package/dist/{skills-YMSY2LFN.js → skills-TFQQ3WSE.js} +6 -6
  53. package/dist/spawn-3GVQNWFH.js +37 -0
  54. package/dist/{validation-KAOZ7UHD.js → validation-RKLSF2UH.js} +6 -6
  55. package/dist/version.js +1 -1
  56. package/dist/{work-ILZ3L7AD.js → work-RKDS3J2X.js} +19 -19
  57. package/dist/worker-5RJNDPYW.js +24 -0
  58. package/package.json +4 -4
  59. package/dist/chat/agent-dispatch/signing.d.ts +0 -14
  60. package/dist/handlers/agent-dispatch.d.ts +0 -12
  61. package/dist/spawn-ZVO5IUCR.js +0 -37
  62. package/dist/worker-D34ZL6RU.js +0 -24
@@ -1,8 +1,11 @@
1
+ import {
2
+ JUNIOR_THREAD_STATE_TTL_MS
3
+ } from "./chunk-3Y47VMHV.js";
1
4
  import {
2
5
  getConversationEventStore,
3
6
  getDb,
4
7
  getSqlExecutor
5
- } from "./chunk-JVORBSUR.js";
8
+ } from "./chunk-JHCAX7XY.js";
6
9
  import {
7
10
  juniorConversationAnnotations,
8
11
  juniorConversationEvents,
@@ -11,23 +14,23 @@ import {
11
14
  juniorUsers,
12
15
  juniorWorkspaceRepos,
13
16
  juniorWorkspaces
14
- } from "./chunk-VZFLAGFK.js";
17
+ } from "./chunk-CUA5SZR5.js";
15
18
  import {
16
19
  SANDBOX_WORKSPACE_ROOT,
17
20
  hash,
18
21
  runNonInteractiveCommand,
19
22
  sleep
20
- } from "./chunk-XOXKL2DL.js";
23
+ } from "./chunk-CNC65LIX.js";
21
24
  import {
22
25
  getStateAdapter
23
- } from "./chunk-HJFWO72I.js";
26
+ } from "./chunk-B6IOTX7W.js";
24
27
  import {
25
28
  botConfig,
26
29
  completeObject,
27
30
  embedTexts,
28
31
  getSlackBotToken,
29
32
  standardModelId
30
- } from "./chunk-OXYAEFKS.js";
33
+ } from "./chunk-Z2YZ6QLG.js";
31
34
  import {
32
35
  isActorUserId,
33
36
  parseActorUserId,
@@ -43,7 +46,7 @@ import {
43
46
  setSpanAttributes,
44
47
  setSpanStatus,
45
48
  withSpan
46
- } from "./chunk-Z23POK2V.js";
49
+ } from "./chunk-SBIQ5MZW.js";
47
50
 
48
51
  // src/chat/plugins/agent-hooks.ts
49
52
  import {
@@ -893,6 +896,447 @@ function createResourceEventTeamIdResolver() {
893
896
  };
894
897
  }
895
898
 
899
+ // src/chat/resource-events/store.ts
900
+ import { createHash as createHash2 } from "crypto";
901
+ import {
902
+ destinationSchema,
903
+ resourceEventTypeSchema
904
+ } from "@sentry/junior-plugin-api";
905
+ import { z as z2 } from "zod";
906
+ var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription:v3";
907
+ var INDEX_LOCK_TTL_MS = 1e4;
908
+ var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
909
+ var SUBSCRIPTION_LOCK_WAIT_MS = 1e4;
910
+ var SUBSCRIPTION_LOCK_RETRY_MS = 25;
911
+ var SUBSCRIPTION_LOCK_HEARTBEAT_MS = 3e3;
912
+ var subscriptionStatusSchema = z2.enum(["active", "cancelled", "completed"]);
913
+ var subscriptionIdIndexSchema = z2.array(z2.string().min(1));
914
+ var subscriptionSchema = z2.object({
915
+ conversationId: z2.string().min(1),
916
+ createdAtMs: z2.number().finite(),
917
+ destination: destinationSchema,
918
+ events: z2.array(resourceEventTypeSchema).min(1),
919
+ expiresAtMs: z2.number().finite(),
920
+ id: z2.string().min(1),
921
+ intent: z2.string().min(1),
922
+ label: z2.string().min(1),
923
+ namespace: z2.string().min(1),
924
+ identifier: z2.string().min(1),
925
+ resourceType: z2.string().min(1),
926
+ status: subscriptionStatusSchema,
927
+ updatedAtMs: z2.number().finite()
928
+ }).strict();
929
+ function digest(value) {
930
+ return createHash2("sha256").update(value).digest("hex").slice(0, 32);
931
+ }
932
+ function subscriptionKey(id) {
933
+ return `${RESOURCE_EVENT_PREFIX}:record:${id}`;
934
+ }
935
+ function subscriptionLockKey(id) {
936
+ return `${RESOURCE_EVENT_PREFIX}:lock:${id}`;
937
+ }
938
+ function resourceIndexKey(teamId, namespace, identifier) {
939
+ return `${RESOURCE_EVENT_PREFIX}:resource:${digest(`${teamId}\0${namespace}\0${identifier}`)}`;
940
+ }
941
+ function subscriptionTeamId(destination) {
942
+ if (destination.platform !== "slack") {
943
+ throw new Error("Resource event subscriptions require a Slack destination");
944
+ }
945
+ return destination.teamId;
946
+ }
947
+ function conversationIndexKey(conversationId) {
948
+ return `${RESOURCE_EVENT_PREFIX}:conversation:${digest(conversationId)}`;
949
+ }
950
+ function indexLockKey(key) {
951
+ return `${key}:lock`;
952
+ }
953
+ async function sleep2(ms) {
954
+ await new Promise((resolve) => {
955
+ const timer = setTimeout(resolve, ms);
956
+ timer.unref?.();
957
+ });
958
+ }
959
+ async function acquireSubscriptionLock(state, subscriptionId, waitDeadlineMs = Date.now() + SUBSCRIPTION_LOCK_WAIT_MS) {
960
+ while (true) {
961
+ const lock = await state.acquireLock(
962
+ subscriptionLockKey(subscriptionId),
963
+ SUBSCRIPTION_LOCK_TTL_MS
964
+ );
965
+ if (lock) {
966
+ return lock;
967
+ }
968
+ if (Date.now() >= waitDeadlineMs) {
969
+ throw new Error(
970
+ `Could not acquire resource event subscription lock for ${subscriptionId}`
971
+ );
972
+ }
973
+ await sleep2(SUBSCRIPTION_LOCK_RETRY_MS);
974
+ }
975
+ }
976
+ async function withSubscriptionLock(state, subscriptionId, callback, waitDeadlineMs) {
977
+ const lock = await acquireSubscriptionLock(
978
+ state,
979
+ subscriptionId,
980
+ waitDeadlineMs
981
+ );
982
+ let lockLostError;
983
+ let heartbeatError;
984
+ let heartbeat;
985
+ const timer = setInterval(() => {
986
+ if (heartbeat) {
987
+ return;
988
+ }
989
+ heartbeat = state.extendLock(lock, SUBSCRIPTION_LOCK_TTL_MS).then((extended) => {
990
+ if (extended) {
991
+ heartbeatError = void 0;
992
+ } else {
993
+ lockLostError = new Error(
994
+ `Resource event subscription lock was lost for ${subscriptionId}`
995
+ );
996
+ }
997
+ }).catch((error) => {
998
+ heartbeatError = error instanceof Error ? error : new Error(`Failed to extend subscription lock: ${String(error)}`);
999
+ }).finally(() => {
1000
+ heartbeat = void 0;
1001
+ });
1002
+ }, SUBSCRIPTION_LOCK_HEARTBEAT_MS);
1003
+ timer.unref?.();
1004
+ let result;
1005
+ try {
1006
+ result = await callback();
1007
+ } finally {
1008
+ clearInterval(timer);
1009
+ await heartbeat;
1010
+ await state.releaseLock(lock);
1011
+ }
1012
+ const lockError = lockLostError ?? heartbeatError;
1013
+ if (lockError) {
1014
+ throw lockError;
1015
+ }
1016
+ return result;
1017
+ }
1018
+ function ttlUntil(expiresAtMs, nowMs) {
1019
+ return Math.max(1, expiresAtMs - nowMs);
1020
+ }
1021
+ async function readSubscriptionIdIndex(state, key) {
1022
+ const value = await state.get(key);
1023
+ if (value === void 0 || value === null) {
1024
+ return [];
1025
+ }
1026
+ return subscriptionIdIndexSchema.parse(value);
1027
+ }
1028
+ function buildSubscriptionId(input) {
1029
+ const eventKey = [...new Set(input.events)].sort().join("\0");
1030
+ return `resub_${digest(
1031
+ `${input.teamId}\0${input.namespace}\0${input.identifier}\0${input.conversationId}\0${eventKey}`
1032
+ )}`;
1033
+ }
1034
+ async function withIndexLock(state, key, callback) {
1035
+ const lock = await state.acquireLock(indexLockKey(key), INDEX_LOCK_TTL_MS);
1036
+ if (!lock) {
1037
+ throw new Error(`Could not acquire resource event index lock for ${key}`);
1038
+ }
1039
+ try {
1040
+ return await callback();
1041
+ } finally {
1042
+ await state.releaseLock(lock);
1043
+ }
1044
+ }
1045
+ async function addToIndex(state, key, subscriptionId, nowMs) {
1046
+ await withIndexLock(state, key, async () => {
1047
+ const ids = [...new Set(await readSubscriptionIdIndex(state, key))];
1048
+ const next = ids.includes(subscriptionId) ? ids : [...ids, subscriptionId];
1049
+ await state.set(key, next, await indexTtlMs(state, next, nowMs));
1050
+ });
1051
+ }
1052
+ async function removeFromIndex(state, key, subscriptionId, nowMs) {
1053
+ await withIndexLock(state, key, async () => {
1054
+ const existing = await readSubscriptionIdIndex(state, key);
1055
+ const next = existing.filter((id) => id !== subscriptionId);
1056
+ await state.set(key, next, await indexTtlMs(state, next, nowMs));
1057
+ });
1058
+ }
1059
+ async function indexTtlMs(state, subscriptionIds, nowMs) {
1060
+ const records = await Promise.all(
1061
+ subscriptionIds.map(
1062
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
1063
+ )
1064
+ );
1065
+ const latestExpiresAtMs = Math.max(
1066
+ nowMs,
1067
+ ...records.filter(
1068
+ (record) => record !== void 0 && activeAt(record, nowMs)
1069
+ ).map((record) => record.expiresAtMs)
1070
+ );
1071
+ return ttlUntil(latestExpiresAtMs, nowMs);
1072
+ }
1073
+ function parseSubscription(value) {
1074
+ if (value === void 0 || value === null) {
1075
+ return void 0;
1076
+ }
1077
+ return subscriptionSchema.parse(value);
1078
+ }
1079
+ function activeAt(subscription, nowMs) {
1080
+ return subscription.status === "active" && subscription.expiresAtMs > nowMs;
1081
+ }
1082
+ function matchesEvent(subscription, input) {
1083
+ return subscription.namespace === input.namespace && subscription.identifier === input.identifier && subscriptionTeamId(subscription.destination) === input.teamId && subscription.events.includes(input.eventType) && activeAt(subscription, input.nowMs);
1084
+ }
1085
+ async function createResourceEventSubscription(input, options = {}) {
1086
+ const state = options.state ?? getStateAdapter();
1087
+ await state.connect();
1088
+ const nowMs = options.nowMs ?? Date.now();
1089
+ const events = [...new Set(input.events.map((event) => event.trim()))].filter(
1090
+ Boolean
1091
+ );
1092
+ if (events.length === 0) {
1093
+ throw new Error("Resource event subscription requires at least one event");
1094
+ }
1095
+ if (input.expiresAtMs <= nowMs) {
1096
+ throw new Error("Resource event subscription expiry must be in the future");
1097
+ }
1098
+ const id = buildSubscriptionId({
1099
+ conversationId: input.conversationId,
1100
+ events,
1101
+ namespace: input.namespace,
1102
+ identifier: input.identifier,
1103
+ teamId: subscriptionTeamId(input.destination)
1104
+ });
1105
+ const record = {
1106
+ conversationId: input.conversationId,
1107
+ createdAtMs: nowMs,
1108
+ destination: input.destination,
1109
+ events,
1110
+ expiresAtMs: input.expiresAtMs,
1111
+ id,
1112
+ intent: input.intent,
1113
+ label: input.label,
1114
+ namespace: input.namespace,
1115
+ identifier: input.identifier,
1116
+ resourceType: input.resourceType,
1117
+ status: "active",
1118
+ updatedAtMs: nowMs
1119
+ };
1120
+ const parsed = subscriptionSchema.parse(record);
1121
+ await state.set(
1122
+ subscriptionKey(id),
1123
+ parsed,
1124
+ ttlUntil(parsed.expiresAtMs, nowMs)
1125
+ );
1126
+ await addToIndex(
1127
+ state,
1128
+ resourceIndexKey(
1129
+ subscriptionTeamId(parsed.destination),
1130
+ input.namespace,
1131
+ input.identifier
1132
+ ),
1133
+ id,
1134
+ nowMs
1135
+ );
1136
+ await addToIndex(
1137
+ state,
1138
+ conversationIndexKey(input.conversationId),
1139
+ id,
1140
+ nowMs
1141
+ );
1142
+ return parsed;
1143
+ }
1144
+ async function listResourceEventSubscriptions(input) {
1145
+ const state = input.state ?? getStateAdapter();
1146
+ await state.connect();
1147
+ const nowMs = input.nowMs ?? Date.now();
1148
+ const ids = await readSubscriptionIdIndex(
1149
+ state,
1150
+ conversationIndexKey(input.conversationId)
1151
+ );
1152
+ const records = await Promise.all(
1153
+ ids.map(
1154
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
1155
+ )
1156
+ );
1157
+ return records.filter(
1158
+ (record) => record !== void 0 && record.conversationId === input.conversationId && activeAt(record, nowMs)
1159
+ ).sort((left, right) => left.createdAtMs - right.createdAtMs);
1160
+ }
1161
+ async function cancelResourceEventSubscription(input) {
1162
+ const state = input.state ?? getStateAdapter();
1163
+ await state.connect();
1164
+ return await withSubscriptionLock(state, input.id, async () => {
1165
+ const current = parseSubscription(
1166
+ await state.get(subscriptionKey(input.id))
1167
+ );
1168
+ if (!current || current.conversationId !== input.conversationId) {
1169
+ return void 0;
1170
+ }
1171
+ const nowMs = input.nowMs ?? Date.now();
1172
+ const next = {
1173
+ ...current,
1174
+ status: "cancelled",
1175
+ updatedAtMs: nowMs
1176
+ };
1177
+ await state.set(
1178
+ subscriptionKey(input.id),
1179
+ next,
1180
+ JUNIOR_THREAD_STATE_TTL_MS
1181
+ );
1182
+ await removeFromIndex(
1183
+ state,
1184
+ resourceIndexKey(
1185
+ subscriptionTeamId(current.destination),
1186
+ current.namespace,
1187
+ current.identifier
1188
+ ),
1189
+ input.id,
1190
+ nowMs
1191
+ );
1192
+ await removeFromIndex(
1193
+ state,
1194
+ conversationIndexKey(current.conversationId),
1195
+ input.id,
1196
+ nowMs
1197
+ );
1198
+ return next;
1199
+ });
1200
+ }
1201
+ async function cancelSubscriptions(input) {
1202
+ const subscriptions = await listResourceEventSubscriptions(input);
1203
+ for (const subscription of subscriptions) {
1204
+ await cancelResourceEventSubscription({
1205
+ conversationId: input.conversationId,
1206
+ id: subscription.id,
1207
+ nowMs: input.nowMs,
1208
+ state: input.state
1209
+ });
1210
+ }
1211
+ }
1212
+ async function findMatchingResourceEventSubscriptions(input) {
1213
+ const state = input.state ?? getStateAdapter();
1214
+ await state.connect();
1215
+ const nowMs = input.nowMs ?? Date.now();
1216
+ const ids = await readSubscriptionIdIndex(
1217
+ state,
1218
+ resourceIndexKey(input.teamId, input.namespace, input.identifier)
1219
+ );
1220
+ const records = await Promise.all(
1221
+ ids.map(
1222
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
1223
+ )
1224
+ );
1225
+ return records.filter(
1226
+ (record) => record !== void 0 && matchesEvent(record, {
1227
+ eventType: input.eventType,
1228
+ nowMs,
1229
+ namespace: input.namespace,
1230
+ identifier: input.identifier,
1231
+ teamId: input.teamId
1232
+ })
1233
+ );
1234
+ }
1235
+ async function deliverResourceEventSubscription(input) {
1236
+ const state = input.state ?? getStateAdapter();
1237
+ await state.connect();
1238
+ return await withSubscriptionLock(
1239
+ state,
1240
+ input.subscription.id,
1241
+ async () => {
1242
+ const nowMs = input.nowMs ?? Date.now();
1243
+ const current = parseSubscription(
1244
+ await state.get(subscriptionKey(input.subscription.id))
1245
+ );
1246
+ if (!current || !matchesEvent(current, {
1247
+ eventType: input.eventType,
1248
+ nowMs,
1249
+ namespace: input.namespace,
1250
+ identifier: input.identifier,
1251
+ teamId: input.teamId
1252
+ })) {
1253
+ return false;
1254
+ }
1255
+ const delivered = await input.deliver(current);
1256
+ if (input.terminal) {
1257
+ const latest = parseSubscription(
1258
+ await state.get(subscriptionKey(current.id))
1259
+ );
1260
+ if (!latest || latest.status !== current.status || latest.updatedAtMs !== current.updatedAtMs) {
1261
+ return delivered;
1262
+ }
1263
+ const next = {
1264
+ ...current,
1265
+ status: "completed",
1266
+ updatedAtMs: nowMs
1267
+ };
1268
+ await state.set(
1269
+ subscriptionKey(current.id),
1270
+ next,
1271
+ JUNIOR_THREAD_STATE_TTL_MS
1272
+ );
1273
+ await removeFromIndex(
1274
+ state,
1275
+ resourceIndexKey(
1276
+ subscriptionTeamId(current.destination),
1277
+ current.namespace,
1278
+ current.identifier
1279
+ ),
1280
+ current.id,
1281
+ nowMs
1282
+ );
1283
+ await removeFromIndex(
1284
+ state,
1285
+ conversationIndexKey(current.conversationId),
1286
+ current.id,
1287
+ nowMs
1288
+ );
1289
+ }
1290
+ return delivered;
1291
+ },
1292
+ input.waitDeadlineMs
1293
+ );
1294
+ }
1295
+
1296
+ // src/chat/tools/execution/tool-input-error.ts
1297
+ var ToolInputError = class extends Error {
1298
+ constructor(message, options) {
1299
+ super(message, options);
1300
+ this.name = "ToolInputError";
1301
+ }
1302
+ };
1303
+
1304
+ // src/chat/resource-events/tool-support.ts
1305
+ var RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
1306
+ var RESOURCE_SUBSCRIPTION_MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
1307
+ var STOP_WATCHING_TOOL_NAME = "stopWatchingResources";
1308
+ var RESOURCE_WATCH_TOOL_SOURCE = {
1309
+ id: "resource-watches",
1310
+ description: "Inspect or stop resource watches for the current conversation."
1311
+ };
1312
+ function requireResourceWatchConversation(context) {
1313
+ if (!context.conversationId) {
1314
+ throw new ToolInputError(
1315
+ "Resource event subscriptions require a conversation"
1316
+ );
1317
+ }
1318
+ if (context.destination.platform !== "slack") {
1319
+ throw new ToolInputError(
1320
+ "Resource event subscriptions currently require Slack delivery"
1321
+ );
1322
+ }
1323
+ if (!isSlackThreadConversationId(context.conversationId)) {
1324
+ throw new ToolInputError(
1325
+ "Resource event subscriptions require a Slack thread conversation"
1326
+ );
1327
+ }
1328
+ return context.conversationId;
1329
+ }
1330
+ function canUseResourceEventSubscriptionTools(context) {
1331
+ return context.destination.platform === "slack" && Boolean(
1332
+ context.conversationId && isSlackThreadConversationId(context.conversationId)
1333
+ );
1334
+ }
1335
+ function isSlackThreadConversationId(conversationId) {
1336
+ const parts = conversationId.split(":");
1337
+ return parts.length === 3 && parts[0] === "slack" && Boolean(parts[1]) && Boolean(parts[2]);
1338
+ }
1339
+
896
1340
  // src/chat/slack/tool-support/context.ts
897
1341
  function getSlackToolContext(context) {
898
1342
  if (context.source.platform !== "slack") {
@@ -1029,8 +1473,8 @@ function buildPayload(parts) {
1029
1473
  return [CREDENTIAL_SUBJECT_HMAC_CONTEXT, ...parts].join("\0");
1030
1474
  }
1031
1475
  function signPayload(secret, payload) {
1032
- const digest = createHmac("sha256", secret).update(payload).digest("hex");
1033
- return `${CREDENTIAL_SUBJECT_SIGNATURE_VERSION}=${digest}`;
1476
+ const digest2 = createHmac("sha256", secret).update(payload).digest("hex");
1477
+ return `${CREDENTIAL_SUBJECT_SIGNATURE_VERSION}=${digest2}`;
1034
1478
  }
1035
1479
  function timingSafeMatch(expected, actual) {
1036
1480
  const expectedBuffer = Buffer.from(expected);
@@ -1201,7 +1645,7 @@ function resolveChannelCapabilities(channelId) {
1201
1645
  }
1202
1646
 
1203
1647
  // src/chat/plugins/agent-hooks.ts
1204
- import { z as z2 } from "zod";
1648
+ import { z as z3 } from "zod";
1205
1649
 
1206
1650
  // src/chat/workspaces/checkout-path.ts
1207
1651
  function tryWorkspaceRepoCheckoutPath(repo) {
@@ -1655,7 +2099,7 @@ var PLUGIN_ROUTE_METHODS = /* @__PURE__ */ new Set([
1655
2099
  var PLUGIN_PROMPT_CONTRIBUTION_TOTAL_MAX_CHARS = 16e3;
1656
2100
  var PLUGIN_PROMPT_CONTEXT_MAX_BYTES = 8e3;
1657
2101
  var PLUGIN_PROMPT_CONTEXT_TOTAL_MAX_BYTES = 16e3;
1658
- var systemPromptMessageArraySchema = z2.array(promptMessageSchema);
2102
+ var systemPromptMessageArraySchema = z3.array(promptMessageSchema);
1659
2103
  function isRecord(value) {
1660
2104
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1661
2105
  }
@@ -2033,8 +2477,45 @@ function getPluginTools(context, sandbox = createSandboxCapability(context.works
2033
2477
  plugin: pluginName
2034
2478
  }) : void 0;
2035
2479
  const mcp = pluginMcpContext(plugin, context);
2480
+ const canSubscribe = context.source.platform === "slack" && context.destination.platform === "slack" && Boolean(context.conversationId) && canRouteResourceEvents() && Boolean(plugin.resourceEvents) && plugin.resourceEvents?.isEnabled?.() !== false;
2036
2481
  const resourceEvents = {
2037
- canSubscribe: context.source.platform === "slack" && canRouteResourceEvents()
2482
+ canSubscribe,
2483
+ async subscribe(input) {
2484
+ if (!canSubscribe || context.destination.platform !== "slack") {
2485
+ throw new Error(
2486
+ "Resource subscriptions are not available in this conversation."
2487
+ );
2488
+ }
2489
+ const registration = plugin.resourceEvents;
2490
+ const resourceType = registration?.resourceTypes.find(
2491
+ (candidate) => candidate.type === input.resource.type
2492
+ );
2493
+ if (input.resource.namespace !== pluginName || !resourceType || input.events.length === 0 || input.events.some(
2494
+ (eventType) => !resourceType.supportedEvents.includes(eventType)
2495
+ )) {
2496
+ throw new Error(
2497
+ "Resource subscription contains an event or resource that the plugin does not support."
2498
+ );
2499
+ }
2500
+ const subscription = await createResourceEventSubscription({
2501
+ conversationId: context.conversationId,
2502
+ destination: context.destination,
2503
+ events: input.events,
2504
+ expiresAtMs: Date.now() + RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS,
2505
+ intent: input.intent,
2506
+ label: input.resource.label,
2507
+ namespace: pluginName,
2508
+ identifier: normalizeResourceEventIdentifier(
2509
+ registration,
2510
+ input.resource.identifier
2511
+ ),
2512
+ resourceType: input.resource.type
2513
+ });
2514
+ return {
2515
+ events: subscription.events,
2516
+ id: subscription.id
2517
+ };
2518
+ }
2038
2519
  };
2039
2520
  const resolveActor = context.resolveActorIdentity ?? (async () => void 0);
2040
2521
  const common = {
@@ -2808,6 +3289,7 @@ function createPluginHookRunner(input = {}) {
2808
3289
  }
2809
3290
 
2810
3291
  export {
3292
+ ToolInputError,
2811
3293
  listConversationAnnotations,
2812
3294
  listConversationAnnotationsById,
2813
3295
  createPluginConversationEvents,
@@ -2833,6 +3315,18 @@ export {
2833
3315
  downloadPrivateSlackFile,
2834
3316
  canRouteResourceEvents,
2835
3317
  createResourceEventTeamIdResolver,
3318
+ createResourceEventSubscription,
3319
+ listResourceEventSubscriptions,
3320
+ cancelResourceEventSubscription,
3321
+ cancelSubscriptions,
3322
+ findMatchingResourceEventSubscriptions,
3323
+ deliverResourceEventSubscription,
3324
+ RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS,
3325
+ RESOURCE_SUBSCRIPTION_MAX_TTL_MS,
3326
+ STOP_WATCHING_TOOL_NAME,
3327
+ RESOURCE_WATCH_TOOL_SOURCE,
3328
+ requireResourceWatchConversation,
3329
+ canUseResourceEventSubscriptionTools,
2836
3330
  getSlackToolContext,
2837
3331
  resolveViewerUser,
2838
3332
  findUserByEmail,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JUNIOR_VERSION
3
- } from "./chunk-2E353AHN.js";
3
+ } from "./chunk-DVHYQDGM.js";
4
4
 
5
5
  // src/deployment.ts
6
6
  var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
@@ -8,14 +8,14 @@ import {
8
8
  getConversationStore,
9
9
  parseDestination,
10
10
  sameDestination
11
- } from "./chunk-JVORBSUR.js";
11
+ } from "./chunk-JHCAX7XY.js";
12
12
  import {
13
13
  getDefaultRedisStateAdapterFor,
14
14
  getStateAdapter
15
- } from "./chunk-HJFWO72I.js";
15
+ } from "./chunk-B6IOTX7W.js";
16
16
  import {
17
17
  getChatConfig
18
- } from "./chunk-OXYAEFKS.js";
18
+ } from "./chunk-Z2YZ6QLG.js";
19
19
  import {
20
20
  parseStoredSlackActor
21
21
  } from "./chunk-M63DETLL.js";
@@ -23,7 +23,7 @@ import {
23
23
  isRecord,
24
24
  toOptionalNumber,
25
25
  toOptionalString
26
- } from "./chunk-Z23POK2V.js";
26
+ } from "./chunk-SBIQ5MZW.js";
27
27
 
28
28
  // src/chat/task-execution/state.ts
29
29
  import { randomUUID } from "crypto";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
3
3
  getChatConfig
4
- } from "./chunk-OXYAEFKS.js";
4
+ } from "./chunk-Z2YZ6QLG.js";
5
5
 
6
6
  // src/chat/runtime/request-deadline.ts
7
7
  import { AsyncLocalStorage } from "async_hooks";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-RF6J3DIO.js";
4
4
  import {
5
5
  resolveBaseUrl
6
- } from "./chunk-DIW6IQYK.js";
6
+ } from "./chunk-665JOQEP.js";
7
7
  import {
8
8
  isExperimentalFeatureEnabled
9
9
  } from "./chunk-VRS6WDK5.js";
@@ -12,27 +12,27 @@ import {
12
12
  getPlugins,
13
13
  getSlackClient,
14
14
  withSlackRetries
15
- } from "./chunk-BLBKORDG.js";
15
+ } from "./chunk-JIZ3RFER.js";
16
16
  import {
17
17
  getDb,
18
18
  parseDestination
19
- } from "./chunk-JVORBSUR.js";
19
+ } from "./chunk-JHCAX7XY.js";
20
20
  import {
21
21
  sleep
22
- } from "./chunk-XOXKL2DL.js";
22
+ } from "./chunk-CNC65LIX.js";
23
23
  import {
24
24
  acquireActiveLock,
25
25
  getStateAdapter
26
- } from "./chunk-HJFWO72I.js";
26
+ } from "./chunk-B6IOTX7W.js";
27
27
  import {
28
28
  pluginCatalogRuntime
29
- } from "./chunk-RL23S5DG.js";
29
+ } from "./chunk-5JB5JJO5.js";
30
30
  import {
31
31
  fetchWithBoundedOAuthErrorBodies
32
- } from "./chunk-IYI7RPHU.js";
32
+ } from "./chunk-CI4JX64I.js";
33
33
  import {
34
34
  isRecord
35
- } from "./chunk-Z23POK2V.js";
35
+ } from "./chunk-SBIQ5MZW.js";
36
36
 
37
37
  // src/chat/capabilities/router.ts
38
38
  var ProviderCredentialRouter = class {