@sentry/junior 0.225.0 → 0.227.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 (65) hide show
  1. package/dist/{agent-hooks-CRuRcOg9.js → agent-hooks-bEwEJhlg.js} +231 -17
  2. package/dist/{agent-runner-Cad1RtWn.js → agent-runner-BGDSjciH.js} +2899 -159
  3. package/dist/api/schema.d.ts +417 -15
  4. package/dist/api/schema.js +4 -4
  5. package/dist/api.js +57 -32
  6. package/dist/{app-BbSD6EtD.d.ts → app-CjRIKZb5.d.ts} +1 -1
  7. package/dist/app.d.ts +1 -1
  8. package/dist/app.js +971 -7685
  9. package/dist/{briefs-BtnkPWT5.js → briefs-BkM_ZW4B.js} +4 -4
  10. package/dist/{catalog-runtime-pv4grNNb.js → catalog-runtime-Cw9V2jpn.js} +1 -1
  11. package/dist/{checkpoint-6fkAdB3g.js → checkpoint-oZYD3-Ar.js} +77 -11
  12. package/dist/cli/chat.js +13 -13
  13. package/dist/cli/check.js +3 -3
  14. package/dist/cli/main.js +1 -1
  15. package/dist/cli/plugins.js +3 -3
  16. package/dist/cli/snapshot-create.js +3 -3
  17. package/dist/cli/upgrade.js +3 -3
  18. package/dist/{client-ihmkx60d.js → client-Pcxo3lGn.js} +2 -2
  19. package/dist/{config-DMg679BW.js → config-CYvEow1F.js} +2 -2
  20. package/dist/{context-Bplcgypl.js → context-0Et9iVSB.js} +1 -1
  21. package/dist/{conversation-Bygi39Xj.js → conversation-BnhOMfEB.js} +113 -25
  22. package/dist/{conversation-privacy-ZYy05Y3q.js → conversation-privacy-hQmyZbOC.js} +1 -1
  23. package/dist/{credential-sync-DCB2SwnQ.js → credential-sync-CUGbiiCM.js} +15 -135
  24. package/dist/{executor-B9R4cHte.js → executor-BXMlFrV6.js} +1 -1
  25. package/dist/history-nukv1YN8.d.ts +127 -0
  26. package/dist/images-8siTAUNW.js +82 -0
  27. package/dist/instrumentation.js +2 -2
  28. package/dist/{vercel-queue-DIkVcfK4.js → job-queue-AI0_QAgc.js} +61 -2
  29. package/dist/{logging-CcTvYQ8A.js → logging-BPBx6WF-.js} +1 -1
  30. package/dist/nitro.d.ts +1 -1
  31. package/dist/nitro.js +3 -4
  32. package/dist/{oauth-authorization-BS4vPJmu.js → oauth-authorization-Cq1GaLIP.js} +1 -1
  33. package/dist/{oauth-callback-server-DnlQsGBJ.js → oauth-callback-server-CnF03VYh.js} +2 -2
  34. package/dist/pending-auth-CHUjr_6d.js +59 -0
  35. package/dist/{profile-CV97YuiL.js → profile-upzC-wN1.js} +4 -4
  36. package/dist/{prompt-CL8zxsmm.js → prompt-CZIQcPHy.js} +1 -1
  37. package/dist/{registry-C8I4IejN.js → registry-DM5aMhjm.js} +2 -2
  38. package/dist/{resolve-DXVAzYa3.js → resolve-sF6Lg7Ay.js} +2 -2
  39. package/dist/{runner-Die1RJ_z.js → runner-Bu2wYpK8.js} +10 -10
  40. package/dist/{sandbox-egress-signals-BZepyVh1.js → sandbox-egress-signals-Dgerhizb.js} +1 -1
  41. package/dist/{schema-BE4uso4i.js → schema-BSiwcvjm.js} +2 -2
  42. package/dist/{skills-DUcDEgDI.js → skills-CAI5s___.js} +2 -2
  43. package/dist/{spawn-o15i3tJu.js → spawn-DRK9TBgY.js} +2 -2
  44. package/dist/{task-queue-D3PLY59C.js → task-queue-iuDAqfZg.js} +3 -4
  45. package/dist/{task-runner-BMl2aM2v.js → task-runner-S3QtH38c.js} +381 -12
  46. package/dist/{thread-state-CAR6gdAm.js → thread-state-C_arVuaN.js} +5 -5
  47. package/dist/{turn-failure-response-CZyoUMkD.js → turn-failure-response-DarwGYgC.js} +9 -10
  48. package/dist/{turn-lifecycle-DPDHKi2U.js → turn-lifecycle-CdiIejXH.js} +1 -1
  49. package/dist/{turn-session-routing-Jkmmxpp-.js → turn-session-routing-DyLGd_wU.js} +1 -1
  50. package/dist/{validation-BcGGxOa6.js → validation-BMig74Zp.js} +1 -1
  51. package/dist/{version-DYcuOFQb.js → version-lhQSfLjx.js} +1 -1
  52. package/dist/version.js +1 -1
  53. package/dist/web-authorization-r2lKYKJ2.js +162 -0
  54. package/dist/web-input-BU-Fu7iq.js +7784 -0
  55. package/dist/{reporting-window-DbUqng0P.js → web-message-id-xPI_EFSC.js} +11 -1
  56. package/dist/{work-Bm90pR9P.js → work-B9koS8sW.js} +7 -8
  57. package/dist/{worker-BvHr6JhE.js → worker-C_iJSRgG.js} +5 -6
  58. package/package.json +5 -4
  59. package/dist/execution-stats-CR9CYpyj.js +0 -373
  60. package/dist/history-yaSjGMD_.d.ts +0 -65
  61. package/dist/job-queue-D2SiTrnt.js +0 -63
  62. package/dist/short-title-3wYH3ipl.js +0 -71
  63. package/dist/stats-DawHjcTw.js +0 -2758
  64. package/dist/web-authorization-B09B8A8R.js +0 -38
  65. package/dist/web-input-CGE58TpK.js +0 -899
@@ -1,16 +1,16 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { a as embedTexts, r as completeObject } from "./client-ihmkx60d.js";
3
- import { F as normalizeIdentityEmail, I as isRecord$2, d as logException, f as logInfo, p as logWarn, v as setSpanAttributes, w as withSpan, y as setSpanStatus } from "./logging-CcTvYQ8A.js";
2
+ import { a as embedTexts, r as completeObject } from "./client-Pcxo3lGn.js";
3
+ import { F as normalizeIdentityEmail, I as isRecord$2, d as logException, f as logInfo, p as logWarn, v as setSpanAttributes, w as withSpan, y as setSpanStatus } from "./logging-BPBx6WF-.js";
4
4
  import { a as getClient$1 } from "./sentry-CgWZM_kV.js";
5
- import { t as canExposeConversationPayload } from "./conversation-privacy-ZYy05Y3q.js";
6
- import { a as getChatConfig, n as botConfig, s as getSlackBotToken } from "./config-DMg679BW.js";
5
+ import { t as canExposeConversationPayload } from "./conversation-privacy-hQmyZbOC.js";
6
+ import { a as getChatConfig, n as botConfig, s as getSlackBotToken } from "./config-CYvEow1F.js";
7
7
  import { r as defaultModelId } from "./model-profile-BwU8mqwD.js";
8
- import { _ as parseSlackMessageTs, c as isActorUserId, h as parseSlackTeamId, m as parseSlackChannelReferenceId, u as parseActorUserId } from "./context-Bplcgypl.js";
8
+ import { b as parseSlackMessageTs, c as isActorUserId, h as parseSlackTeamId, m as parseSlackChannelReferenceId, u as parseActorUserId } from "./context-0Et9iVSB.js";
9
9
  import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
10
- import { C as juniorCodeRepositories, D as juniorIdentities, E as juniorConversations, O as juniorUsers, S as juniorCodeChanges, T as conversationReadColumns, _ as CONVERSATION_METRICS, g as juniorConversationBriefs, h as juniorConversationBindings, i as juniorWorkspaces, m as juniorConversationParticipants, n as juniorSnapshots, r as juniorWorkspaceRepos, t as createJuniorSqlExecutor, v as juniorConversationMetrics, x as juniorConversationAnnotations, y as juniorConversationEvents } from "./executor-B9R4cHte.js";
11
- import { C as conversationReportEventSchema, G as historyReplacementSchema, K as newConversationEventSchema, L as conversationBriefSchema, N as readMessageCards, R as parseBriefInput, V as conversationEventDataSchema, W as decodeStoredConversationEvent, Z as usageSchema } from "./conversation-Bygi39Xj.js";
12
- import { N as SANDBOX_WORKSPACE_ROOT, k as sleep$1, l as isSandboxMissingError, n as hash, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-CV97YuiL.js";
13
- import { n as briefEntriesFromReportEvents, s as generateBrief, t as BRIEF_PROMPT } from "./prompt-CL8zxsmm.js";
10
+ import { C as juniorCodeRepositories, D as juniorIdentities, E as juniorConversations, O as juniorUsers, S as juniorCodeChanges, T as conversationReadColumns, _ as CONVERSATION_METRICS, g as juniorConversationBriefs, h as juniorConversationBindings, i as juniorWorkspaces, m as juniorConversationParticipants, n as juniorSnapshots, r as juniorWorkspaceRepos, t as createJuniorSqlExecutor, v as juniorConversationMetrics, w as juniorAttachments, x as juniorConversationAnnotations, y as juniorConversationEvents } from "./executor-BXMlFrV6.js";
11
+ import { C as conversationReportEventSchema, H as conversationBriefSchema, I as readMessageAttachments, K as conversationEventDataSchema, N as readMessageCards, Q as encodeHistoryPayload, U as parseBriefInput, X as historyReplacementSchema, Y as decodeStoredConversationEvent, Z as newConversationEventSchema, rt as usageSchema } from "./conversation-BnhOMfEB.js";
12
+ import { C as getDefaultRedisStateAdapterFor, N as SANDBOX_WORKSPACE_ROOT, k as sleep$1, l as isSandboxMissingError, n as hash, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-upzC-wN1.js";
13
+ import { n as briefEntriesFromReportEvents, s as generateBrief, t as BRIEF_PROMPT } from "./prompt-CZIQcPHy.js";
14
14
  import { AsyncLocalStorage } from "node:async_hooks";
15
15
  import { z } from "zod";
16
16
  import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
@@ -1189,15 +1189,16 @@ function eventUnarchivesConversation(data) {
1189
1189
  /** Split validated event data into column-lifted and JSON payload fields. */
1190
1190
  function insertFromEvent(conversationId, seq, historyVersion, event, actorIdentityId) {
1191
1191
  const stripped = stripPayloadAuthorIdentityId(event.data);
1192
- const { type, ...payload } = conversationEventDataSchema.parse(stripped);
1192
+ const data = conversationEventDataSchema.parse(stripped);
1193
+ const encoded = encodeHistoryPayload(data);
1193
1194
  return {
1194
1195
  conversationId,
1195
1196
  seq,
1196
1197
  historyVersion,
1197
- schemaVersion: 1,
1198
+ schemaVersion: encoded.schemaVersion,
1198
1199
  idempotencyKey: event.idempotencyKey ?? null,
1199
- type,
1200
- payload: sanitizePostgresJson(payload),
1200
+ type: data.type,
1201
+ payload: sanitizePostgresJson(encoded.payload),
1201
1202
  actorIdentityId: actorIdentityId ?? null,
1202
1203
  createdAt: new Date(event.createdAtMs)
1203
1204
  };
@@ -1733,8 +1734,9 @@ async function annotateToolResult(conversationId, plugin, result) {
1733
1734
  const details = isRecord$2(result.details) ? result.details : result;
1734
1735
  if (details.timed_out === true || details.isError === true || !Array.isArray(details.objectAnnotations)) return result;
1735
1736
  const cards = await saveObjectAnnotations(conversationId, plugin, details.objectAnnotations);
1737
+ const { objectAnnotations: _annotations, ...rest } = details;
1736
1738
  const annotated = {
1737
- ...details,
1739
+ ...rest,
1738
1740
  objectCards: cards
1739
1741
  };
1740
1742
  if (isRecord$2(result.details)) return {
@@ -1806,6 +1808,63 @@ const briefUpdatedEvent = defineConversationEvent({
1806
1808
  }
1807
1809
  });
1808
1810
  //#endregion
1811
+ //#region src/chat/slack/attachments.ts
1812
+ /** Save the vision summary for a stored Slack attachment. */
1813
+ async function recordSlackAttachment(args) {
1814
+ await args.db.db().update(juniorAttachments).set({ visionSummary: args.visionSummary }).where(and(eq(juniorAttachments.id, args.attachmentId), eq(juniorAttachments.conversationId, args.conversationId), isNull(juniorAttachments.deleteRequestedAt)));
1815
+ }
1816
+ /** Read a saved vision summary for one live attachment. */
1817
+ async function readAttachmentVisionSummary(args) {
1818
+ const [row] = await args.db.db().select({ visionSummary: juniorAttachments.visionSummary }).from(juniorAttachments).where(and(eq(juniorAttachments.id, args.attachmentId), eq(juniorAttachments.conversationId, args.conversationId), isNull(juniorAttachments.deleteRequestedAt)));
1819
+ return row?.visionSummary;
1820
+ }
1821
+ /** Match live conversation attachments to Slack file ids. */
1822
+ async function matchSlackAttachments(args) {
1823
+ if (args.providerIds.length === 0) return [];
1824
+ return await args.db.db().select({
1825
+ id: juniorAttachments.id,
1826
+ providerId: juniorAttachments.providerId,
1827
+ bytes: juniorAttachments.bytes,
1828
+ contentType: juniorAttachments.contentType,
1829
+ filename: juniorAttachments.filename
1830
+ }).from(juniorAttachments).where(and(eq(juniorAttachments.conversationId, args.conversationId), eq(juniorAttachments.provider, "slack"), inArray(juniorAttachments.providerId, args.providerIds), isNull(juniorAttachments.deleteRequestedAt)));
1831
+ }
1832
+ /** Read the Slack file identity retained by the Chat SDK. */
1833
+ function getSlackFileId(attachment) {
1834
+ const metadataId = attachment.fetchMetadata?.fileId;
1835
+ if (metadataId) return metadataId;
1836
+ return (attachment.url ?? attachment.fetchMetadata?.url ?? "").match(/(?:^|[-/])(F[A-Z0-9]+)(?:[-/]|$)/i)?.[1];
1837
+ }
1838
+ //#endregion
1839
+ //#region src/api/conversations/message-attachments.ts
1840
+ /** Resolve saved Slack file references without exposing private storage details. */
1841
+ async function hydrateMessageAttachments(db, conversationId, events) {
1842
+ const messages = events.flatMap((event) => event.data.type === "message" ? [event.data] : []);
1843
+ const ids = [...new Set(messages.flatMap((message) => slackFileIds(message.meta)))];
1844
+ if (!ids.length) return;
1845
+ const attachments = await matchSlackAttachments({
1846
+ db,
1847
+ conversationId,
1848
+ providerIds: ids
1849
+ });
1850
+ const byFileId = new Map(attachments.map(({ providerId, ...attachment }) => [providerId, attachment]));
1851
+ for (const message of messages) {
1852
+ const stored = readMessageAttachments(message.meta?.attachments);
1853
+ const matched = slackFileIds(message.meta).flatMap((id) => {
1854
+ const attachment = byFileId.get(id);
1855
+ return attachment ? [attachment] : [];
1856
+ });
1857
+ if (matched.length) message.meta = {
1858
+ ...message.meta,
1859
+ attachments: [...stored, ...matched]
1860
+ };
1861
+ }
1862
+ }
1863
+ function slackFileIds(meta) {
1864
+ const ids = meta?.slackFileIds ?? meta?.imageFileIds;
1865
+ return Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : [];
1866
+ }
1867
+ //#endregion
1809
1868
  //#region src/chat/sentry-links.ts
1810
1869
  function getSentryOrgSlug() {
1811
1870
  return process.env.SENTRY_ORG_SLUG?.trim() || void 0;
@@ -2056,7 +2115,10 @@ function reportEventData(args) {
2056
2115
  ...typeof data.meta?.explicitMention === "boolean" ? { explicitMention: data.meta.explicitMention } : void 0,
2057
2116
  ...args.canExposePayload && typeof data.meta?.trustedSummary === "string" ? { trustedSummary: data.meta.trustedSummary } : void 0,
2058
2117
  ...args.canExposePayload && (data.meta?.cards || data.meta?.objectCards) ? { cards: readMessageCards(data.meta) } : void 0,
2059
- ...args.canExposePayload ? { text: data.text } : { redacted: true }
2118
+ ...args.canExposePayload ? {
2119
+ text: data.text,
2120
+ ...data.meta?.attachments ? { attachments: readMessageAttachments(data.meta.attachments) } : void 0
2121
+ } : { redacted: true }
2060
2122
  };
2061
2123
  }
2062
2124
  case "message_handled": return {
@@ -2286,6 +2348,7 @@ async function projectConversationEventRows(executor, args) {
2286
2348
  toolCallIds: toolResultIds,
2287
2349
  types: ["tool_execution_started"]
2288
2350
  });
2351
+ if (args.canExposePayload) await hydrateMessageAttachments(executor, args.conversationId, events);
2289
2352
  return projectConversationReportEventPage({
2290
2353
  canExposePayload: args.canExposePayload,
2291
2354
  events,
@@ -3077,6 +3140,78 @@ function getDashboardTaskLink(taskId) {
3077
3140
  /** Retain expiring conversation/runtime state long enough for resume and dashboard history. */
3078
3141
  const JUNIOR_THREAD_STATE_TTL_MS = 6048e5;
3079
3142
  //#endregion
3143
+ //#region src/chat/events/timer-index.ts
3144
+ const DUE_KEY = "junior:watches:due";
3145
+ const CLAIM_MS = 12e4;
3146
+ const BATCH_SIZE = 25;
3147
+ const entriesSchema = z.array(z.object({
3148
+ id: z.string(),
3149
+ atMs: z.number()
3150
+ }));
3151
+ /** Keep timer index operations atomic in Redis and the local memory adapter. */
3152
+ async function updateTimerIndex(state, operation) {
3153
+ const redis = await getDefaultRedisStateAdapterFor(state);
3154
+ if (redis) {
3155
+ const prefix = getChatConfig().state.keyPrefix;
3156
+ const key = prefix ? `${prefix}:${DUE_KEY}` : DUE_KEY;
3157
+ const client = redis.getClient();
3158
+ if (operation.kind === "add") {
3159
+ await client.sendCommand([
3160
+ "ZADD",
3161
+ key,
3162
+ String(operation.atMs),
3163
+ operation.id
3164
+ ]);
3165
+ return [];
3166
+ }
3167
+ if (operation.kind === "remove") {
3168
+ await client.sendCommand([
3169
+ "ZREM",
3170
+ key,
3171
+ operation.id
3172
+ ]);
3173
+ return [];
3174
+ }
3175
+ return await client.sendCommand([
3176
+ "EVAL",
3177
+ `local ids = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'LIMIT', 0, ARGV[3])
3178
+ for _, id in ipairs(ids) do
3179
+ redis.call('ZADD', KEYS[1], ARGV[2], id)
3180
+ end
3181
+ return ids`,
3182
+ "1",
3183
+ key,
3184
+ String(operation.nowMs),
3185
+ String(operation.nowMs + CLAIM_MS),
3186
+ String(BATCH_SIZE)
3187
+ ]);
3188
+ }
3189
+ const lock = await state.acquireLock(`${DUE_KEY}:lock`, 1e4);
3190
+ if (!lock) throw new Error("Could not acquire timer index lock");
3191
+ try {
3192
+ let entries = entriesSchema.parse(await state.get(DUE_KEY) ?? []);
3193
+ let ids = [];
3194
+ if (operation.kind === "claim") {
3195
+ ids = entries.filter((entry) => entry.atMs <= operation.nowMs).sort((a, b) => a.atMs - b.atMs).slice(0, BATCH_SIZE).map((entry) => entry.id);
3196
+ const claimed = new Set(ids);
3197
+ entries = entries.map((entry) => claimed.has(entry.id) ? {
3198
+ ...entry,
3199
+ atMs: operation.nowMs + CLAIM_MS
3200
+ } : entry);
3201
+ } else {
3202
+ entries = entries.filter((entry) => entry.id !== operation.id);
3203
+ if (operation.kind === "add") entries.push({
3204
+ id: operation.id,
3205
+ atMs: operation.atMs
3206
+ });
3207
+ }
3208
+ await state.set(DUE_KEY, entries);
3209
+ return ids;
3210
+ } finally {
3211
+ await state.releaseLock(lock);
3212
+ }
3213
+ }
3214
+ //#endregion
3080
3215
  //#region src/chat/events/store.ts
3081
3216
  const EVENT_PREFIX = "junior:resource_event_subscription:v5";
3082
3217
  const INDEX_LOCK_TTL_MS = 1e4;
@@ -3095,6 +3230,7 @@ const subscriptionSchema = z.object({
3095
3230
  createdAtMs: z.number().finite(),
3096
3231
  events: z.array(eventTypeSchema).min(1),
3097
3232
  expiresAtMs: z.number().finite(),
3233
+ firesAtMs: z.number().finite().optional(),
3098
3234
  id: z.string().min(1),
3099
3235
  intent: z.string().min(1),
3100
3236
  label: z.string().min(1),
@@ -3252,6 +3388,70 @@ async function createWatch(input, options = {}) {
3252
3388
  await addToIndex(state, conversationIndexKey(input.conversationId), id, nowMs);
3253
3389
  return parsed;
3254
3390
  }
3391
+ /** Create a retry-stable timer without keeping a process open. */
3392
+ async function createTimerWatch(input) {
3393
+ const state = getStateAdapter();
3394
+ await state.connect();
3395
+ const identifier = digest(`${input.conversationId}\0${input.toolCallId}`);
3396
+ const selector = {
3397
+ conversationId: input.conversationId,
3398
+ namespace: "junior",
3399
+ identifier,
3400
+ events: ["timer.fired"]
3401
+ };
3402
+ const id = buildSubscriptionId(selector);
3403
+ return await withSubscriptionLock(state, id, async () => {
3404
+ const existing = parseSubscription(await state.get(subscriptionKey(id)));
3405
+ if (existing) return existing;
3406
+ const nowMs = Date.now();
3407
+ const firesAtMs = nowMs + input.afterMs;
3408
+ const expiresAtMs = firesAtMs + 864e5;
3409
+ const record = subscriptionSchema.parse({
3410
+ ...selector,
3411
+ id,
3412
+ firesAtMs,
3413
+ expiresAtMs,
3414
+ resourceType: "timer",
3415
+ label: "Timer",
3416
+ intent: input.intent,
3417
+ status: "active",
3418
+ createdAtMs: nowMs,
3419
+ updatedAtMs: nowMs
3420
+ });
3421
+ await withIndexLock(state, conversationIndexKey(input.conversationId), async () => {
3422
+ const key = conversationIndexKey(input.conversationId);
3423
+ const ids = [.../* @__PURE__ */ new Set([...await readSubscriptionIdIndex(state, key), id])];
3424
+ await state.set(key, ids, Math.max(ttlUntil(expiresAtMs, nowMs), await indexTtlMs(state, ids, nowMs)));
3425
+ await state.set(resourceIndexKey("junior", identifier), [id], ttlUntil(expiresAtMs, nowMs));
3426
+ await updateTimerIndex(state, {
3427
+ kind: "add",
3428
+ id,
3429
+ atMs: firesAtMs
3430
+ });
3431
+ await state.set(subscriptionKey(id), record, ttlUntil(expiresAtMs, nowMs));
3432
+ });
3433
+ return record;
3434
+ });
3435
+ }
3436
+ /** Claim live timer Watches and discard stale due-index entries. */
3437
+ async function takeDueTimerWatches(nowMs) {
3438
+ const state = getStateAdapter();
3439
+ await state.connect();
3440
+ const watches = [];
3441
+ for (const id of await updateTimerIndex(state, {
3442
+ kind: "claim",
3443
+ nowMs
3444
+ })) await withSubscriptionLock(state, id, async () => {
3445
+ const watch = parseSubscription(await state.get(subscriptionKey(id)));
3446
+ if (watch && activeAt(watch, nowMs) && watch.firesAtMs !== void 0) {
3447
+ if (watch.firesAtMs <= nowMs) watches.push(watch);
3448
+ } else await updateTimerIndex(state, {
3449
+ kind: "remove",
3450
+ id
3451
+ });
3452
+ });
3453
+ return watches;
3454
+ }
3255
3455
  /** List active subscriptions bound to one conversation. */
3256
3456
  async function listWatches(input) {
3257
3457
  const state = input.state ?? getStateAdapter();
@@ -3268,6 +3468,7 @@ async function cancelWatch(input) {
3268
3468
  const current = parseSubscription(await state.get(subscriptionKey(input.id)));
3269
3469
  if (!current || current.conversationId !== input.conversationId) return;
3270
3470
  const nowMs = input.nowMs ?? Date.now();
3471
+ if (current.status === "completed") return current;
3271
3472
  const next = {
3272
3473
  ...current,
3273
3474
  status: "cancelled",
@@ -3276,6 +3477,10 @@ async function cancelWatch(input) {
3276
3477
  await state.set(subscriptionKey(input.id), next, JUNIOR_THREAD_STATE_TTL_MS);
3277
3478
  await removeFromIndex(state, resourceIndexKey(current.namespace, current.identifier), input.id, nowMs);
3278
3479
  await removeFromIndex(state, conversationIndexKey(current.conversationId), input.id, nowMs);
3480
+ if (current.firesAtMs !== void 0) await updateTimerIndex(state, {
3481
+ kind: "remove",
3482
+ id: input.id
3483
+ });
3279
3484
  return next;
3280
3485
  });
3281
3486
  }
@@ -3356,6 +3561,10 @@ async function deliverWatch(input) {
3356
3561
  await removeFromIndex(state, resourceIndexKey(current.namespace, current.identifier), current.id, nowMs);
3357
3562
  await removeFromIndex(state, conversationIndexKey(current.conversationId), current.id, nowMs);
3358
3563
  }
3564
+ if (input.terminal && current.firesAtMs !== void 0) await updateTimerIndex(state, {
3565
+ kind: "remove",
3566
+ id: current.id
3567
+ });
3359
3568
  return delivered;
3360
3569
  }, input.waitDeadlineMs);
3361
3570
  }
@@ -3769,7 +3978,12 @@ async function withSlackRetries(task, maxAttempts = 3, context = {}) {
3769
3978
  * is workspace-scoped and no installation token can be resolved, so a write
3770
3979
  * never goes out with another workspace's credentials.
3771
3980
  */
3772
- function getSlackClient() {
3981
+ function getSlackClient(options) {
3982
+ if (options) return new WebClient(resolveSlackToken(), {
3983
+ retryConfig: { retries: 0 },
3984
+ rejectRateLimitedCalls: true,
3985
+ timeout: options.timeoutMs
3986
+ });
3773
3987
  return getClient();
3774
3988
  }
3775
3989
  /**
@@ -5437,4 +5651,4 @@ function createPluginHookRunner(input = {}) {
5437
5651
  };
5438
5652
  }
5439
5653
  //#endregion
5440
- export { resolveViewerUser as $, locationFromRow as $t, tryWorkspaceRepoCheckoutPath as A, createPluginConversationEvents as At, downloadPrivateSlackFile as B, agentsInstructionsUpdatedEvent as Bt, invalidateReadySnapshot as C, conversationArchivedForEmail as Ct, SANDBOX_RUNTIME as D, conversationNotArchivedForEmail as Dt, setWorkspaceSnapshotBuild as E, conversationHasParticipantUser as Et, bindSlackDirectCredentialSubject as F, saveObjectAnnotations as Ft, isDmChannel as G, getConversationMessageSearchStore as Gt, getHeaderString as H, authenticationUnlinkedEvent as Ht, verifyEventAutomationCredentialSubject as I, createPluginAnnotations as It, withSlackRetries as J, getSqlExecutor as Jt, normalizeSlackConversationId as K, getConversationStore as Kt, verifyScheduledAutomationCredentialSubject as L, listConversationAnnotations as Lt, resolveChannelCapabilities as M, readConversationEventPage as Mt, bindEventAutomationCredentialSubject as N, buildSentryConversationUrl as Nt, deleteWorkspaceSnapshotBuilders as O, conversationNotArchivedForUser as Ot, bindScheduledAutomationCredentialSubject as P, resolveRootVisibility as Pt, readActorIdentity as Q, withConversationMutationLock as Qt, verifySlackDirectCredentialSubject as R, listConversationAnnotationsById as Rt, WorkspaceValidationError as S, conversationArchiveTimes as St, setWorkspaceSnapshot as T, conversationHasParticipantEmail as Tt, getSlackClient as U, db_exports as Ut, getFilePermalink as V, authenticationLinkedEvent as Vt, isConversationScopedChannel as W, getConversationEventStore as Wt, runWithWorkspaceTeamId as X, withConversationEventLock as Xt, getWorkspaceTeamId as Y, createSqlConversationEventStore as Yt, findUserByEmail as Z, sanitizePostgresJson as Zt, deleteWorkspace as _, createPluginState as _t, getPluginRoutes as a, agentTurnUsageSchema as an, STOP_WATCHING_TOOL_NAME as at, listWorkspaces as b, createPluginLogger as bt, getPluginTools as c, deleteProviderIdentityForSlackUser as cn, collectEventMatchKeys as ct, setPlugins as d, createSlackDestination as dn, findMatchingWatches as dt, bindProviderConversation as en, updateViewerDisplayName as et, validatePlugins as f, parseDestination as fn, listWatches as ft, createWorkspace as g, setDashboardConversationLinkOptions as gt, setBriefsConfig as h, getDashboardTaskLink as ht, getPluginApiRoutes as i, agentTurnTotalTokens as in, RESOURCE_WATCH_TOOL_SOURCE as it, workspaceRepoCheckoutPath as j, readLatestConversationBrief as jt, getWorkspaceSnapshotBuilder as k, viewerConversationMembership as kt, getPluginUserPromptContributions as l, upsertIdentity as ln, createWatch as lt, registration_exports as m, sameDestination as mn, getDashboardConversationLink as mt, applyPluginFormatMarkdown as n, addAgentTurnUsage as nn, RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS as nt, getPluginSlackConversationLink as o, hasAgentTurnUsage as on, cancelSubscriptions as ot, coreTaskRegistrations as p, requireSlackDestination as pn, JUNIOR_THREAD_STATE_TTL_MS as pt, runWithSlackInstallationToken as q, getDb as qt, createPluginHookRunner as r, agentTurnCostUsd as rn, RESOURCE_SUBSCRIPTION_MAX_TTL_MS as rt, getPluginSystemPromptContributions as s, resolveRootConversationId as sn, cancelWatch as st, agent_hooks_exports as t, parseSessionSource as tn, getSlackToolContext as tt, getPlugins as u, upsertLinkedIdentity as un, deliverWatch as ut, getWorkspace as v, createPluginEmbedder as vt, loadSnapshotsForProfile as w, conversationArchivedForUser as wt, updateWorkspace as x, readConversationAccessFromSql as xt, getWorkspaceByName as y, createPluginModel as yt, SlackActionError as z, JUNIOR_NATIVE_EVENT_NAMESPACE as zt };
5654
+ export { resolveViewerUser as $, getDb as $t, tryWorkspaceRepoCheckoutPath as A, conversationNotArchivedForUser as At, downloadPrivateSlackFile as B, resolveRootVisibility as Bt, invalidateReadySnapshot as C, readConversationAccessFromSql as Ct, SANDBOX_RUNTIME as D, conversationHasParticipantEmail as Dt, setWorkspaceSnapshotBuild as E, conversationArchivedForUser as Et, bindSlackDirectCredentialSubject as F, buildSentryConversationUrl as Ft, isDmChannel as G, JUNIOR_NATIVE_EVENT_NAMESPACE as Gt, getHeaderString as H, createPluginAnnotations as Ht, verifyEventAutomationCredentialSubject as I, getSlackFileId as It, withSlackRetries as J, authenticationUnlinkedEvent as Jt, normalizeSlackConversationId as K, agentsInstructionsUpdatedEvent as Kt, verifyScheduledAutomationCredentialSubject as L, matchSlackAttachments as Lt, resolveChannelCapabilities as M, createPluginConversationEvents as Mt, bindEventAutomationCredentialSubject as N, readLatestConversationBrief as Nt, deleteWorkspaceSnapshotBuilders as O, conversationHasParticipantUser as Ot, bindScheduledAutomationCredentialSubject as P, readConversationEventPage as Pt, readActorIdentity as Q, getConversationStore as Qt, verifySlackDirectCredentialSubject as R, readAttachmentVisionSummary as Rt, WorkspaceValidationError as S, createPluginLogger as St, setWorkspaceSnapshot as T, conversationArchivedForEmail as Tt, getSlackClient as U, listConversationAnnotations as Ut, getFilePermalink as V, saveObjectAnnotations as Vt, isConversationScopedChannel as W, listConversationAnnotationsById as Wt, runWithWorkspaceTeamId as X, getConversationEventStore as Xt, getWorkspaceTeamId as Y, db_exports as Yt, findUserByEmail as Z, getConversationMessageSearchStore as Zt, deleteWorkspace as _, parseDestination as _n, getDashboardTaskLink as _t, getPluginRoutes as a, bindProviderConversation as an, STOP_WATCHING_TOOL_NAME as at, listWorkspaces as b, createPluginEmbedder as bt, getPluginTools as c, agentTurnCostUsd as cn, collectEventMatchKeys as ct, setPlugins as d, hasAgentTurnUsage as dn, deliverWatch as dt, getSqlExecutor as en, updateViewerDisplayName as et, validatePlugins as f, resolveRootConversationId as fn, findMatchingWatches as ft, createWorkspace as g, createSlackDestination as gn, getDashboardConversationLink as gt, setBriefsConfig as h, upsertLinkedIdentity as hn, JUNIOR_THREAD_STATE_TTL_MS as ht, getPluginApiRoutes as i, locationFromRow as in, RESOURCE_WATCH_TOOL_SOURCE as it, workspaceRepoCheckoutPath as j, viewerConversationMembership as jt, getWorkspaceSnapshotBuilder as k, conversationNotArchivedForEmail as kt, getPluginUserPromptContributions as l, agentTurnTotalTokens as ln, createTimerWatch as lt, registration_exports as m, upsertIdentity as mn, takeDueTimerWatches as mt, applyPluginFormatMarkdown as n, withConversationEventLock as nn, RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS as nt, getPluginSlackConversationLink as o, parseSessionSource as on, cancelSubscriptions as ot, coreTaskRegistrations as p, deleteProviderIdentityForSlackUser as pn, listWatches as pt, runWithSlackInstallationToken as q, authenticationLinkedEvent as qt, createPluginHookRunner as r, withConversationMutationLock as rn, RESOURCE_SUBSCRIPTION_MAX_TTL_MS as rt, getPluginSystemPromptContributions as s, addAgentTurnUsage as sn, cancelWatch as st, agent_hooks_exports as t, createSqlConversationEventStore as tn, getSlackToolContext as tt, getPlugins as u, agentTurnUsageSchema as un, createWatch as ut, getWorkspace as v, requireSlackDestination as vn, setDashboardConversationLinkOptions as vt, loadSnapshotsForProfile as w, conversationArchiveTimes as wt, updateWorkspace as x, createPluginModel as xt, getWorkspaceByName as y, sameDestination as yn, createPluginState as yt, SlackActionError as z, recordSlackAttachment as zt };