@sentry/junior 0.82.0 → 0.84.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 (45) hide show
  1. package/dist/{agent-hooks-5ZRILRC3.js → agent-hooks-XDS5DIVS.js} +8 -8
  2. package/dist/api-reference.d.ts +1 -1
  3. package/dist/app.d.ts +1 -1
  4. package/dist/app.js +3833 -3263
  5. package/dist/chat/config.d.ts +1 -1
  6. package/dist/chat/conversations/store.d.ts +1 -1
  7. package/dist/chat/plugins/agent-hooks.d.ts +3 -3
  8. package/dist/chat/resource-events/ingest.d.ts +23 -0
  9. package/dist/chat/resource-events/notification.d.ts +20 -0
  10. package/dist/chat/resource-events/store.d.ts +79 -0
  11. package/dist/chat/services/pending-auth.d.ts +5 -0
  12. package/dist/chat/slack/client.d.ts +3 -1
  13. package/dist/chat/state/session-log.d.ts +4 -0
  14. package/dist/chat/task-execution/slack-work.d.ts +22 -0
  15. package/dist/chat/task-execution/state.d.ts +8 -2
  16. package/dist/chat/task-execution/store.d.ts +30 -8
  17. package/dist/chat/tools/resource-events.d.ts +19 -0
  18. package/dist/{chunk-PQ2U2AO3.js → chunk-AAD2D7K3.js} +11 -11
  19. package/dist/{chunk-ABRNFE3N.js → chunk-ALL7GG6U.js} +7 -6
  20. package/dist/{chunk-4XHCVBXH.js → chunk-BAPRSWNW.js} +81 -13
  21. package/dist/{chunk-NC6LR6U4.js → chunk-BFPOSIYV.js} +725 -169
  22. package/dist/{chunk-GUO4EE7L.js → chunk-CWMMGUWM.js} +1 -1
  23. package/dist/{chunk-YA2JCC7G.js → chunk-JUI6XIHU.js} +13 -5
  24. package/dist/{chunk-TR2G37II.js → chunk-KNVN4HUT.js} +8 -8
  25. package/dist/{chunk-X2FL5ZH5.js → chunk-MJ4E2PK7.js} +1 -1
  26. package/dist/{chunk-3JKTVW4R.js → chunk-MZ4DUVFD.js} +2 -2
  27. package/dist/{chunk-2AJ4TEKE.js → chunk-S7SC5IUJ.js} +26 -4
  28. package/dist/{chunk-IOBSRZK5.js → chunk-SBYMRDH7.js} +1 -1
  29. package/dist/{chunk-LX5GBMEP.js → chunk-ZPD5YQFZ.js} +2 -2
  30. package/dist/cli/chat.js +4 -4
  31. package/dist/cli/plugins.js +6 -6
  32. package/dist/cli/snapshot-warmup.js +3 -3
  33. package/dist/cli/upgrade.js +16 -32
  34. package/dist/{db-GQJKBX5W.js → db-7RRWCYF3.js} +3 -3
  35. package/dist/handlers/github-webhook/check-suite.d.ts +3 -0
  36. package/dist/handlers/github-webhook/pull-request-review.d.ts +3 -0
  37. package/dist/handlers/github-webhook/pull-request.d.ts +3 -0
  38. package/dist/handlers/github-webhook/resource.d.ts +10 -0
  39. package/dist/handlers/github-webhook.d.ts +15 -0
  40. package/dist/nitro.js +3 -3
  41. package/dist/reporting/conversations.d.ts +79 -2
  42. package/dist/reporting.d.ts +12 -4
  43. package/dist/reporting.js +431 -27
  44. package/dist/{runner-VRFJLG2M.js → runner-CJL5IKFO.js} +9 -9
  45. package/package.json +6 -6
@@ -10,7 +10,7 @@ import {
10
10
  isSnapshotMissingError,
11
11
  resolveRuntimeDependencySnapshot,
12
12
  runNonInteractiveCommand
13
- } from "./chunk-3JKTVW4R.js";
13
+ } from "./chunk-MZ4DUVFD.js";
14
14
  import {
15
15
  pluginTaskId,
16
16
  pluginTaskParamsSchema,
@@ -31,7 +31,7 @@ import {
31
31
  recordSubagentStarted,
32
32
  recordToolExecutionStarted,
33
33
  upsertAgentTurnSessionRecord
34
- } from "./chunk-YA2JCC7G.js";
34
+ } from "./chunk-JUI6XIHU.js";
35
35
  import {
36
36
  createPluginEmbedder,
37
37
  createPluginHookRunner,
@@ -42,14 +42,14 @@ import {
42
42
  getPlugins,
43
43
  getSlackToolContext,
44
44
  resolveChannelCapabilities
45
- } from "./chunk-TR2G37II.js";
45
+ } from "./chunk-KNVN4HUT.js";
46
46
  import {
47
47
  createPluginLogger,
48
48
  createPluginState
49
- } from "./chunk-GUO4EE7L.js";
49
+ } from "./chunk-CWMMGUWM.js";
50
50
  import {
51
51
  getDb
52
- } from "./chunk-PQ2U2AO3.js";
52
+ } from "./chunk-AAD2D7K3.js";
53
53
  import {
54
54
  SANDBOX_DATA_ROOT,
55
55
  SANDBOX_SKILLS_ROOT,
@@ -60,7 +60,7 @@ import {
60
60
  import {
61
61
  acquireActiveLock,
62
62
  getStateAdapter
63
- } from "./chunk-X2FL5ZH5.js";
63
+ } from "./chunk-MJ4E2PK7.js";
64
64
  import {
65
65
  SlackActionError,
66
66
  downloadPrivateSlackFile,
@@ -71,7 +71,7 @@ import {
71
71
  normalizeSlackConversationId,
72
72
  parseDestination,
73
73
  withSlackRetries
74
- } from "./chunk-4XHCVBXH.js";
74
+ } from "./chunk-BAPRSWNW.js";
75
75
  import {
76
76
  GEN_AI_PROVIDER_NAME,
77
77
  GEN_AI_SERVER_ADDRESS,
@@ -104,7 +104,7 @@ import {
104
104
  toObservablePromptPart,
105
105
  trimTrailingAssistantMessages,
106
106
  upsertActiveSkill
107
- } from "./chunk-ABRNFE3N.js";
107
+ } from "./chunk-ALL7GG6U.js";
108
108
  import {
109
109
  parseSlackThreadId,
110
110
  resolveConversationPrivacy,
@@ -777,8 +777,8 @@ var StateAdapterTokenStore = class {
777
777
  async set(userId, provider, tokens) {
778
778
  const parsed = storedTokensSchema.parse(tokens);
779
779
  const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
780
- const ttlMs = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
781
- await this.state.set(tokenKey(userId, provider), parsed, ttlMs);
780
+ const ttlMs2 = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
781
+ await this.state.set(tokenKey(userId, provider), parsed, ttlMs2);
782
782
  }
783
783
  async delete(userId, provider) {
784
784
  await this.state.delete(tokenKey(userId, provider));
@@ -3770,9 +3770,476 @@ function createReportProgressTool() {
3770
3770
  });
3771
3771
  }
3772
3772
 
3773
- // src/chat/tools/slack/channel-list-messages.ts
3773
+ // src/chat/tools/resource-events.ts
3774
3774
  import { Type as Type13 } from "@sinclair/typebox";
3775
3775
 
3776
+ // src/chat/resource-events/store.ts
3777
+ import { createHash } from "crypto";
3778
+ import { destinationSchema } from "@sentry/junior-plugin-api";
3779
+ import { z } from "zod";
3780
+ var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription";
3781
+ var INDEX_LOCK_TTL_MS = 1e4;
3782
+ var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
3783
+ var subscriptionStatusSchema = z.enum(["active", "cancelled", "completed"]);
3784
+ var subscriptionIdIndexSchema = z.array(z.string().min(1));
3785
+ var subscriptionSchema = z.object({
3786
+ conversationId: z.string().min(1),
3787
+ createdAtMs: z.number().finite(),
3788
+ destination: destinationSchema,
3789
+ events: z.array(z.string().min(1)).min(1),
3790
+ expiresAtMs: z.number().finite(),
3791
+ id: z.string().min(1),
3792
+ intent: z.string().min(1),
3793
+ label: z.string().min(1),
3794
+ provider: z.string().min(1),
3795
+ resourceRef: z.string().min(1),
3796
+ resourceType: z.string().min(1),
3797
+ status: subscriptionStatusSchema,
3798
+ updatedAtMs: z.number().finite()
3799
+ }).strict();
3800
+ function digest(value) {
3801
+ return createHash("sha256").update(value).digest("hex").slice(0, 32);
3802
+ }
3803
+ function subscriptionKey(id) {
3804
+ return `${RESOURCE_EVENT_PREFIX}:record:${id}`;
3805
+ }
3806
+ function subscriptionLockKey(id) {
3807
+ return `${RESOURCE_EVENT_PREFIX}:lock:${id}`;
3808
+ }
3809
+ function resourceIndexKey(provider, resourceRef) {
3810
+ return `${RESOURCE_EVENT_PREFIX}:resource:${digest(`${provider}\0${resourceRef}`)}`;
3811
+ }
3812
+ function conversationIndexKey(conversationId) {
3813
+ return `${RESOURCE_EVENT_PREFIX}:conversation:${digest(conversationId)}`;
3814
+ }
3815
+ function indexLockKey(key) {
3816
+ return `${key}:lock`;
3817
+ }
3818
+ function ttlUntil(expiresAtMs, nowMs) {
3819
+ return Math.max(1, expiresAtMs - nowMs);
3820
+ }
3821
+ async function readSubscriptionIdIndex(state, key) {
3822
+ const value = await state.get(key);
3823
+ if (value === void 0 || value === null) {
3824
+ return [];
3825
+ }
3826
+ return subscriptionIdIndexSchema.parse(value);
3827
+ }
3828
+ function buildSubscriptionId(input) {
3829
+ const eventKey = [...new Set(input.events)].sort().join("\0");
3830
+ return `resub_${digest(
3831
+ `${input.provider}\0${input.resourceRef}\0${input.conversationId}\0${eventKey}`
3832
+ )}`;
3833
+ }
3834
+ async function withIndexLock(state, key, callback) {
3835
+ const lock = await state.acquireLock(indexLockKey(key), INDEX_LOCK_TTL_MS);
3836
+ if (!lock) {
3837
+ throw new Error(`Could not acquire resource event index lock for ${key}`);
3838
+ }
3839
+ try {
3840
+ return await callback();
3841
+ } finally {
3842
+ await state.releaseLock(lock);
3843
+ }
3844
+ }
3845
+ async function addToIndex(state, key, subscriptionId, nowMs) {
3846
+ await withIndexLock(state, key, async () => {
3847
+ const ids = [...new Set(await readSubscriptionIdIndex(state, key))];
3848
+ const next = ids.includes(subscriptionId) ? ids : [...ids, subscriptionId];
3849
+ await state.set(key, next, await indexTtlMs(state, next, nowMs));
3850
+ });
3851
+ }
3852
+ async function removeFromIndex(state, key, subscriptionId, nowMs) {
3853
+ await withIndexLock(state, key, async () => {
3854
+ const existing = await readSubscriptionIdIndex(state, key);
3855
+ const next = existing.filter((id) => id !== subscriptionId);
3856
+ await state.set(key, next, await indexTtlMs(state, next, nowMs));
3857
+ });
3858
+ }
3859
+ async function indexTtlMs(state, subscriptionIds, nowMs) {
3860
+ const records = await Promise.all(
3861
+ subscriptionIds.map(
3862
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
3863
+ )
3864
+ );
3865
+ const latestExpiresAtMs = Math.max(
3866
+ nowMs,
3867
+ ...records.filter(
3868
+ (record) => record !== void 0 && activeAt(record, nowMs)
3869
+ ).map((record) => record.expiresAtMs)
3870
+ );
3871
+ return ttlUntil(latestExpiresAtMs, nowMs);
3872
+ }
3873
+ function parseSubscription(value) {
3874
+ if (value === void 0 || value === null) {
3875
+ return void 0;
3876
+ }
3877
+ return subscriptionSchema.parse(value);
3878
+ }
3879
+ function activeAt(subscription, nowMs) {
3880
+ return subscription.status === "active" && subscription.expiresAtMs > nowMs;
3881
+ }
3882
+ function matchesEvent(subscription, input) {
3883
+ return subscription.provider === input.provider && subscription.resourceRef === input.resourceRef && subscription.events.includes(input.eventType) && activeAt(subscription, input.nowMs);
3884
+ }
3885
+ async function createResourceEventSubscription(input, options = {}) {
3886
+ const state = options.state ?? getStateAdapter();
3887
+ await state.connect();
3888
+ const nowMs = options.nowMs ?? Date.now();
3889
+ const events = [...new Set(input.events.map((event) => event.trim()))].filter(
3890
+ Boolean
3891
+ );
3892
+ if (events.length === 0) {
3893
+ throw new Error("Resource event subscription requires at least one event");
3894
+ }
3895
+ if (input.expiresAtMs <= nowMs) {
3896
+ throw new Error("Resource event subscription expiry must be in the future");
3897
+ }
3898
+ const id = buildSubscriptionId({
3899
+ conversationId: input.conversationId,
3900
+ events,
3901
+ provider: input.provider,
3902
+ resourceRef: input.resourceRef
3903
+ });
3904
+ const record = {
3905
+ conversationId: input.conversationId,
3906
+ createdAtMs: nowMs,
3907
+ destination: input.destination,
3908
+ events,
3909
+ expiresAtMs: input.expiresAtMs,
3910
+ id,
3911
+ intent: input.intent,
3912
+ label: input.label,
3913
+ provider: input.provider,
3914
+ resourceRef: input.resourceRef,
3915
+ resourceType: input.resourceType,
3916
+ status: "active",
3917
+ updatedAtMs: nowMs
3918
+ };
3919
+ const parsed = subscriptionSchema.parse(record);
3920
+ await state.set(
3921
+ subscriptionKey(id),
3922
+ parsed,
3923
+ ttlUntil(parsed.expiresAtMs, nowMs)
3924
+ );
3925
+ await addToIndex(
3926
+ state,
3927
+ resourceIndexKey(input.provider, input.resourceRef),
3928
+ id,
3929
+ nowMs
3930
+ );
3931
+ await addToIndex(
3932
+ state,
3933
+ conversationIndexKey(input.conversationId),
3934
+ id,
3935
+ nowMs
3936
+ );
3937
+ return parsed;
3938
+ }
3939
+ async function listResourceEventSubscriptions(input) {
3940
+ const state = input.state ?? getStateAdapter();
3941
+ await state.connect();
3942
+ const nowMs = input.nowMs ?? Date.now();
3943
+ const ids = await readSubscriptionIdIndex(
3944
+ state,
3945
+ conversationIndexKey(input.conversationId)
3946
+ );
3947
+ const records = await Promise.all(
3948
+ ids.map(
3949
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
3950
+ )
3951
+ );
3952
+ return records.filter(
3953
+ (record) => record !== void 0 && record.conversationId === input.conversationId && activeAt(record, nowMs)
3954
+ ).sort((left, right) => left.createdAtMs - right.createdAtMs);
3955
+ }
3956
+ async function cancelResourceEventSubscription(input) {
3957
+ const state = input.state ?? getStateAdapter();
3958
+ await state.connect();
3959
+ const lock = await state.acquireLock(
3960
+ subscriptionLockKey(input.id),
3961
+ SUBSCRIPTION_LOCK_TTL_MS
3962
+ );
3963
+ if (!lock) {
3964
+ throw new Error(`Could not acquire subscription lock for ${input.id}`);
3965
+ }
3966
+ try {
3967
+ const current = parseSubscription(
3968
+ await state.get(subscriptionKey(input.id))
3969
+ );
3970
+ if (!current || current.conversationId !== input.conversationId) {
3971
+ return void 0;
3972
+ }
3973
+ const nowMs = input.nowMs ?? Date.now();
3974
+ const next = {
3975
+ ...current,
3976
+ status: "cancelled",
3977
+ updatedAtMs: nowMs
3978
+ };
3979
+ await state.set(
3980
+ subscriptionKey(input.id),
3981
+ next,
3982
+ JUNIOR_THREAD_STATE_TTL_MS
3983
+ );
3984
+ await removeFromIndex(
3985
+ state,
3986
+ resourceIndexKey(current.provider, current.resourceRef),
3987
+ input.id,
3988
+ nowMs
3989
+ );
3990
+ await removeFromIndex(
3991
+ state,
3992
+ conversationIndexKey(current.conversationId),
3993
+ input.id,
3994
+ nowMs
3995
+ );
3996
+ return next;
3997
+ } finally {
3998
+ await state.releaseLock(lock);
3999
+ }
4000
+ }
4001
+ async function findMatchingResourceEventSubscriptions(input) {
4002
+ const state = input.state ?? getStateAdapter();
4003
+ await state.connect();
4004
+ const nowMs = input.nowMs ?? Date.now();
4005
+ const ids = await readSubscriptionIdIndex(
4006
+ state,
4007
+ resourceIndexKey(input.provider, input.resourceRef)
4008
+ );
4009
+ const records = await Promise.all(
4010
+ ids.map(
4011
+ async (id) => parseSubscription(await state.get(subscriptionKey(id)))
4012
+ )
4013
+ );
4014
+ return records.filter(
4015
+ (record) => record !== void 0 && matchesEvent(record, {
4016
+ eventType: input.eventType,
4017
+ nowMs,
4018
+ provider: input.provider,
4019
+ resourceRef: input.resourceRef
4020
+ })
4021
+ );
4022
+ }
4023
+ async function deliverResourceEventSubscription(input) {
4024
+ const state = input.state ?? getStateAdapter();
4025
+ await state.connect();
4026
+ const lock = await state.acquireLock(
4027
+ subscriptionLockKey(input.subscription.id),
4028
+ SUBSCRIPTION_LOCK_TTL_MS
4029
+ );
4030
+ if (!lock) {
4031
+ throw new Error(
4032
+ `Resource event subscription delivery lock busy: ${input.subscription.id}`
4033
+ );
4034
+ }
4035
+ try {
4036
+ const nowMs = input.nowMs ?? Date.now();
4037
+ const current = parseSubscription(
4038
+ await state.get(subscriptionKey(input.subscription.id))
4039
+ );
4040
+ if (!current || !matchesEvent(current, {
4041
+ eventType: input.eventType,
4042
+ nowMs,
4043
+ provider: input.provider,
4044
+ resourceRef: input.resourceRef
4045
+ })) {
4046
+ return false;
4047
+ }
4048
+ const delivered = await input.deliver(current);
4049
+ if (input.terminal) {
4050
+ const latest = parseSubscription(
4051
+ await state.get(subscriptionKey(current.id))
4052
+ );
4053
+ if (!latest || latest.status !== current.status || latest.updatedAtMs !== current.updatedAtMs) {
4054
+ return delivered;
4055
+ }
4056
+ const next = {
4057
+ ...current,
4058
+ status: "completed",
4059
+ updatedAtMs: nowMs
4060
+ };
4061
+ await state.set(
4062
+ subscriptionKey(current.id),
4063
+ next,
4064
+ JUNIOR_THREAD_STATE_TTL_MS
4065
+ );
4066
+ await removeFromIndex(
4067
+ state,
4068
+ resourceIndexKey(current.provider, current.resourceRef),
4069
+ current.id,
4070
+ nowMs
4071
+ );
4072
+ await removeFromIndex(
4073
+ state,
4074
+ conversationIndexKey(current.conversationId),
4075
+ current.id,
4076
+ nowMs
4077
+ );
4078
+ }
4079
+ return delivered;
4080
+ } finally {
4081
+ await state.releaseLock(lock);
4082
+ }
4083
+ }
4084
+
4085
+ // src/chat/tools/resource-events.ts
4086
+ var DEFAULT_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
4087
+ var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
4088
+ var subscribeInputSchema = Type13.Object(
4089
+ {
4090
+ resourceRef: Type13.String({
4091
+ description: "Opaque resource ref copied from a subscribable tool result."
4092
+ }),
4093
+ provider: Type13.String({
4094
+ description: "Provider that owns the resource ref."
4095
+ }),
4096
+ resourceType: Type13.String({
4097
+ description: "Provider-defined resource type from the subscribable hint."
4098
+ }),
4099
+ label: Type13.String({
4100
+ description: "Human-readable resource label from the subscribable hint."
4101
+ }),
4102
+ events: Type13.Array(Type13.String(), {
4103
+ description: "High-signal event names to deliver to this conversation when they occur.",
4104
+ minItems: 1
4105
+ }),
4106
+ intent: Type13.String({
4107
+ description: "Concise reason this conversation wants these events, used when an event arrives."
4108
+ }),
4109
+ ttlMs: Type13.Optional(
4110
+ Type13.Number({
4111
+ description: "How long to keep the subscription active. Defaults to 14 days and is capped at 30 days."
4112
+ })
4113
+ )
4114
+ },
4115
+ { additionalProperties: false }
4116
+ );
4117
+ var cancelInputSchema = Type13.Object(
4118
+ {
4119
+ subscriptionId: Type13.String({
4120
+ description: "Subscription id returned by subscribeToResourceEvents or listResourceEventSubscriptions."
4121
+ })
4122
+ },
4123
+ { additionalProperties: false }
4124
+ );
4125
+ function requireConversationContext(context) {
4126
+ if (!context.conversationId) {
4127
+ throw new Error("Resource event subscriptions require a conversation");
4128
+ }
4129
+ if (context.destination.platform !== "slack") {
4130
+ throw new Error(
4131
+ "Resource event subscriptions currently require Slack delivery"
4132
+ );
4133
+ }
4134
+ if (!isSlackThreadConversationId(context.conversationId)) {
4135
+ throw new Error(
4136
+ "Resource event subscriptions require a Slack thread conversation"
4137
+ );
4138
+ }
4139
+ return context.conversationId;
4140
+ }
4141
+ function canUseResourceEventSubscriptionTools(context) {
4142
+ return context.destination.platform === "slack" && Boolean(
4143
+ context.conversationId && isSlackThreadConversationId(context.conversationId)
4144
+ );
4145
+ }
4146
+ function isSlackThreadConversationId(conversationId) {
4147
+ const parts = conversationId.split(":");
4148
+ return parts.length === 3 && parts[0] === "slack" && Boolean(parts[1]) && Boolean(parts[2]);
4149
+ }
4150
+ function cleanStrings(values) {
4151
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
4152
+ }
4153
+ function ttlMs(input) {
4154
+ if (input.ttlMs === void 0) {
4155
+ return DEFAULT_TTL_MS;
4156
+ }
4157
+ if (!Number.isFinite(input.ttlMs) || input.ttlMs <= 0) {
4158
+ throw new Error("ttlMs must be a positive finite number");
4159
+ }
4160
+ return Math.min(input.ttlMs, MAX_TTL_MS);
4161
+ }
4162
+ function createSubscribeToResourceEventsTool(context) {
4163
+ return tool({
4164
+ description: "Subscribe the current conversation to high-signal events for a resource returned by a subscribable tool result. Matching events are queued as normal conversation messages; they do not interrupt active work.",
4165
+ inputSchema: subscribeInputSchema,
4166
+ async execute(input) {
4167
+ const conversationId = requireConversationContext(context);
4168
+ const events = cleanStrings(input.events);
4169
+ const intent = input.intent.trim();
4170
+ if (!intent) {
4171
+ throw new Error("intent is required");
4172
+ }
4173
+ const nowMs = Date.now();
4174
+ const subscription = await createResourceEventSubscription({
4175
+ conversationId,
4176
+ destination: context.destination,
4177
+ events,
4178
+ expiresAtMs: nowMs + ttlMs(input),
4179
+ intent,
4180
+ label: input.label.trim(),
4181
+ provider: input.provider.trim(),
4182
+ resourceRef: input.resourceRef.trim(),
4183
+ resourceType: input.resourceType.trim()
4184
+ });
4185
+ return {
4186
+ id: subscription.id,
4187
+ status: subscription.status,
4188
+ resourceRef: subscription.resourceRef,
4189
+ events: subscription.events,
4190
+ expiresAtMs: subscription.expiresAtMs
4191
+ };
4192
+ }
4193
+ });
4194
+ }
4195
+ function createListResourceEventSubscriptionsTool(context) {
4196
+ return tool({
4197
+ description: "List active resource event subscriptions for the current conversation.",
4198
+ inputSchema: Type13.Object({}, { additionalProperties: false }),
4199
+ async execute() {
4200
+ const conversationId = requireConversationContext(context);
4201
+ const subscriptions = await listResourceEventSubscriptions({
4202
+ conversationId
4203
+ });
4204
+ return {
4205
+ subscriptions: subscriptions.map((subscription) => ({
4206
+ id: subscription.id,
4207
+ label: subscription.label,
4208
+ resourceRef: subscription.resourceRef,
4209
+ provider: subscription.provider,
4210
+ resourceType: subscription.resourceType,
4211
+ events: subscription.events,
4212
+ intent: subscription.intent,
4213
+ expiresAtMs: subscription.expiresAtMs
4214
+ }))
4215
+ };
4216
+ }
4217
+ });
4218
+ }
4219
+ function createCancelResourceEventSubscriptionTool(context) {
4220
+ return tool({
4221
+ description: "Cancel a resource event subscription for the current conversation.",
4222
+ inputSchema: cancelInputSchema,
4223
+ async execute(input) {
4224
+ const conversationId = requireConversationContext(context);
4225
+ const subscription = await cancelResourceEventSubscription({
4226
+ conversationId,
4227
+ id: input.subscriptionId
4228
+ });
4229
+ if (!subscription) {
4230
+ throw new Error("Resource event subscription was not found");
4231
+ }
4232
+ return {
4233
+ id: subscription.id,
4234
+ status: subscription.status
4235
+ };
4236
+ }
4237
+ });
4238
+ }
4239
+
4240
+ // src/chat/tools/slack/channel-list-messages.ts
4241
+ import { Type as Type14 } from "@sinclair/typebox";
4242
+
3776
4243
  // src/chat/slack/channel.ts
3777
4244
  async function listChannelMessages(input) {
3778
4245
  const client = getSlackClient();
@@ -3862,39 +4329,39 @@ function createSlackChannelListMessagesTool(context) {
3862
4329
  return tool({
3863
4330
  description: "List channel messages from Slack history in the active channel context. Use when the user asks for recent or historical channel context outside this thread. Do not use for live monitoring or when current thread context already answers the question.",
3864
4331
  annotations: { readOnlyHint: true, destructiveHint: false },
3865
- inputSchema: Type13.Object({
3866
- limit: Type13.Optional(
3867
- Type13.Integer({
4332
+ inputSchema: Type14.Object({
4333
+ limit: Type14.Optional(
4334
+ Type14.Integer({
3868
4335
  minimum: 1,
3869
4336
  maximum: 1e3,
3870
4337
  description: "Maximum number of messages to return across pages."
3871
4338
  })
3872
4339
  ),
3873
- cursor: Type13.Optional(
3874
- Type13.String({
4340
+ cursor: Type14.Optional(
4341
+ Type14.String({
3875
4342
  minLength: 1,
3876
4343
  description: "Optional cursor to continue from a prior call."
3877
4344
  })
3878
4345
  ),
3879
- oldest: Type13.Optional(
3880
- Type13.String({
4346
+ oldest: Type14.Optional(
4347
+ Type14.String({
3881
4348
  minLength: 1,
3882
4349
  description: "Optional oldest message timestamp (Slack ts) for range filtering."
3883
4350
  })
3884
4351
  ),
3885
- latest: Type13.Optional(
3886
- Type13.String({
4352
+ latest: Type14.Optional(
4353
+ Type14.String({
3887
4354
  minLength: 1,
3888
4355
  description: "Optional latest message timestamp (Slack ts) for range filtering."
3889
4356
  })
3890
4357
  ),
3891
- inclusive: Type13.Optional(
3892
- Type13.Boolean({
4358
+ inclusive: Type14.Optional(
4359
+ Type14.Boolean({
3893
4360
  description: "Whether oldest/latest bounds should be inclusive."
3894
4361
  })
3895
4362
  ),
3896
- max_pages: Type13.Optional(
3897
- Type13.Integer({
4363
+ max_pages: Type14.Optional(
4364
+ Type14.Integer({
3898
4365
  minimum: 1,
3899
4366
  maximum: 10,
3900
4367
  description: "Maximum number of API pages to traverse in a single call."
@@ -3954,7 +4421,7 @@ function createSlackChannelListMessagesTool(context) {
3954
4421
  }
3955
4422
 
3956
4423
  // src/chat/tools/slack/channel-post-message.ts
3957
- import { Type as Type14 } from "@sinclair/typebox";
4424
+ import { Type as Type15 } from "@sinclair/typebox";
3958
4425
 
3959
4426
  // src/chat/slack/outbound.ts
3960
4427
  var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
@@ -3998,7 +4465,13 @@ async function getPermalinkBestEffort(args) {
3998
4465
  message_ts: args.messageTs
3999
4466
  }),
4000
4467
  3,
4001
- { action: "chat.getPermalink" }
4468
+ {
4469
+ action: "chat.getPermalink",
4470
+ spanAttributes: {
4471
+ "app.slack.channel_id": args.channelId,
4472
+ "app.slack.message_ts": args.messageTs
4473
+ }
4474
+ }
4002
4475
  );
4003
4476
  return response.permalink;
4004
4477
  } catch {
@@ -4025,7 +4498,13 @@ async function postSlackMessage(input) {
4025
4498
  ...threadTs ? { thread_ts: threadTs } : {}
4026
4499
  }),
4027
4500
  3,
4028
- { action: "chat.postMessage" }
4501
+ {
4502
+ action: "chat.postMessage",
4503
+ spanAttributes: {
4504
+ "app.slack.channel_id": channelId,
4505
+ ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
4506
+ }
4507
+ }
4029
4508
  );
4030
4509
  if (!response.ts) {
4031
4510
  throw new Error("Slack message posted without ts");
@@ -4055,7 +4534,13 @@ async function deleteSlackMessage(input) {
4055
4534
  ts: timestamp
4056
4535
  }),
4057
4536
  3,
4058
- { action: "chat.delete" }
4537
+ {
4538
+ action: "chat.delete",
4539
+ spanAttributes: {
4540
+ "app.slack.channel_id": channelId,
4541
+ "app.slack.message_ts": timestamp
4542
+ }
4543
+ }
4059
4544
  );
4060
4545
  }
4061
4546
  async function postSlackEphemeralMessage(input) {
@@ -4083,7 +4568,14 @@ async function postSlackEphemeralMessage(input) {
4083
4568
  ...threadTs ? { thread_ts: threadTs } : {}
4084
4569
  }),
4085
4570
  3,
4086
- { action: "chat.postEphemeral" }
4571
+ {
4572
+ action: "chat.postEphemeral",
4573
+ spanAttributes: {
4574
+ "app.slack.channel_id": channelId,
4575
+ "app.slack.user_id": userId,
4576
+ ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
4577
+ }
4578
+ }
4087
4579
  );
4088
4580
  return {
4089
4581
  messageTs: response.message_ts
@@ -4120,7 +4612,13 @@ async function uploadFilesToThread(input) {
4120
4612
  file_uploads: fileUploads
4121
4613
  }),
4122
4614
  3,
4123
- { action: "filesUploadV2" }
4615
+ {
4616
+ action: "filesUploadV2",
4617
+ spanAttributes: {
4618
+ "app.slack.channel_id": channelId,
4619
+ "app.slack.thread_ts": threadTs
4620
+ }
4621
+ }
4124
4622
  );
4125
4623
  }
4126
4624
  async function addReactionToMessage(input) {
@@ -4144,7 +4642,14 @@ async function addReactionToMessage(input) {
4144
4642
  name: emoji
4145
4643
  }),
4146
4644
  3,
4147
- { action: "reactions.add" }
4645
+ {
4646
+ action: "reactions.add",
4647
+ spanAttributes: {
4648
+ "app.slack.channel_id": channelId,
4649
+ "app.slack.message_ts": timestamp,
4650
+ "app.slack.reaction": emoji
4651
+ }
4652
+ }
4148
4653
  );
4149
4654
  } catch (error) {
4150
4655
  if (error instanceof SlackActionError && error.code === "already_reacted") {
@@ -4175,7 +4680,14 @@ async function removeReactionFromMessage(input) {
4175
4680
  name: emoji
4176
4681
  }),
4177
4682
  3,
4178
- { action: "reactions.remove" }
4683
+ {
4684
+ action: "reactions.remove",
4685
+ spanAttributes: {
4686
+ "app.slack.channel_id": channelId,
4687
+ "app.slack.message_ts": timestamp,
4688
+ "app.slack.reaction": emoji
4689
+ }
4690
+ }
4179
4691
  );
4180
4692
  } catch (error) {
4181
4693
  if (error instanceof SlackActionError && error.code === "no_reaction") {
@@ -4206,8 +4718,8 @@ function createOperationKey(toolName, input) {
4206
4718
  function createSlackChannelPostMessageTool(context, state) {
4207
4719
  return tool({
4208
4720
  description: "Post a new top-level message to the current Slack channel. Use only when the user explicitly asks to post/send/share/say something to the channel. Do not use for thread replies, inline @mentions, or pinging mentioned users.",
4209
- inputSchema: Type14.Object({
4210
- text: Type14.String({
4721
+ inputSchema: Type15.Object({
4722
+ text: Type15.String({
4211
4723
  minLength: 1,
4212
4724
  maxLength: 4e4,
4213
4725
  description: "Slack mrkdwn text to post."
@@ -4247,12 +4759,12 @@ function createSlackChannelPostMessageTool(context, state) {
4247
4759
  }
4248
4760
 
4249
4761
  // src/chat/tools/slack/message-add-reaction.ts
4250
- import { Type as Type15 } from "@sinclair/typebox";
4762
+ import { Type as Type16 } from "@sinclair/typebox";
4251
4763
  function createSlackMessageAddReactionTool(context, state) {
4252
4764
  return tool({
4253
4765
  description: "Add an emoji reaction to the current inbound Slack message. Use sparingly for lightweight acknowledgements. Provide a Slack emoji alias name (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`), not a unicode emoji glyph. The target message is injected by runtime context; do not use this for arbitrary historical messages.",
4254
- inputSchema: Type15.Object({
4255
- emoji: Type15.String({
4766
+ inputSchema: Type16.Object({
4767
+ emoji: Type16.String({
4256
4768
  minLength: 1,
4257
4769
  maxLength: 64,
4258
4770
  description: "Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
@@ -4304,7 +4816,7 @@ function createSlackMessageAddReactionTool(context, state) {
4304
4816
  }
4305
4817
 
4306
4818
  // src/chat/tools/slack/canvas-tools.ts
4307
- import { Type as Type16 } from "@sinclair/typebox";
4819
+ import { Type as Type17 } from "@sinclair/typebox";
4308
4820
 
4309
4821
  // src/chat/slack/canvas-references.ts
4310
4822
  var SLACK_FILE_ID_PATTERN = /^F[A-Z0-9]{4,}$/i;
@@ -4549,13 +5061,13 @@ function resolveCanvasTarget(canvas) {
4549
5061
  }
4550
5062
  return { ok: true, canvasId };
4551
5063
  }
4552
- var editReplacementSchema2 = Type16.Object(
5064
+ var editReplacementSchema2 = Type17.Object(
4553
5065
  {
4554
- oldText: Type16.String({
5066
+ oldText: Type17.String({
4555
5067
  minLength: 1,
4556
5068
  description: "Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
4557
5069
  }),
4558
- newText: Type16.String({
5070
+ newText: Type17.String({
4559
5071
  description: "Replacement Canvas markdown for this edit."
4560
5072
  })
4561
5073
  },
@@ -4564,13 +5076,13 @@ var editReplacementSchema2 = Type16.Object(
4564
5076
  function createSlackCanvasCreateTool(context, state) {
4565
5077
  return tool({
4566
5078
  description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, reply with one or two short sentences plus the canvas link; do not recap the canvas contents. Do not use for short answers that fit cleanly in one normal thread reply.",
4567
- inputSchema: Type16.Object({
4568
- title: Type16.String({
5079
+ inputSchema: Type17.Object({
5080
+ title: Type17.String({
4569
5081
  minLength: 1,
4570
5082
  maxLength: 160,
4571
5083
  description: "Canvas title."
4572
5084
  }),
4573
- markdown: Type16.String({
5085
+ markdown: Type17.String({
4574
5086
  minLength: 1,
4575
5087
  description: "Canvas markdown body content."
4576
5088
  })
@@ -4636,20 +5148,20 @@ function createSlackCanvasReadTool() {
4636
5148
  return tool({
4637
5149
  description: "Read a bounded line range from a Slack canvas as markdown. Use when you need exact Canvas contents to verify facts or make edits safely. Do not use for generic web pages \u2014 use webFetch for those.",
4638
5150
  annotations: { readOnlyHint: true, destructiveHint: false },
4639
- inputSchema: Type16.Object(
5151
+ inputSchema: Type17.Object(
4640
5152
  {
4641
- canvas: Type16.String({
5153
+ canvas: Type17.String({
4642
5154
  minLength: 1,
4643
5155
  description: "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
4644
5156
  }),
4645
- offset: Type16.Optional(
4646
- Type16.Integer({
5157
+ offset: Type17.Optional(
5158
+ Type17.Integer({
4647
5159
  minimum: 1,
4648
5160
  description: "1-indexed line number to start reading from."
4649
5161
  })
4650
5162
  ),
4651
- limit: Type16.Optional(
4652
- Type16.Integer({
5163
+ limit: Type17.Optional(
5164
+ Type17.Integer({
4653
5165
  minimum: 1,
4654
5166
  description: "Maximum number of lines to read. Defaults to 1000."
4655
5167
  })
@@ -4710,13 +5222,13 @@ function createSlackCanvasEditTool(state) {
4710
5222
  description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content; prefer this over slackCanvasWrite for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same canvas: use one edits[] call.",
4711
5223
  prepareArguments: prepareCanvasEditArguments,
4712
5224
  executionMode: "sequential",
4713
- inputSchema: Type16.Object(
5225
+ inputSchema: Type17.Object(
4714
5226
  {
4715
- canvas: Type16.String({
5227
+ canvas: Type17.String({
4716
5228
  minLength: 1,
4717
5229
  description: "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."
4718
5230
  }),
4719
- edits: Type16.Array(editReplacementSchema2, {
5231
+ edits: Type17.Array(editReplacementSchema2, {
4720
5232
  minItems: 1,
4721
5233
  description: "Exact replacements matched against the current Canvas body, not incrementally."
4722
5234
  })
@@ -4796,13 +5308,13 @@ function createSlackCanvasWriteTool(state) {
4796
5308
  return tool({
4797
5309
  description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation; use slackCanvasEdit for targeted changes to existing canvas content.",
4798
5310
  executionMode: "sequential",
4799
- inputSchema: Type16.Object(
5311
+ inputSchema: Type17.Object(
4800
5312
  {
4801
- canvas: Type16.String({
5313
+ canvas: Type17.String({
4802
5314
  minLength: 1,
4803
5315
  description: "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."
4804
5316
  }),
4805
- content: Type16.String({
5317
+ content: Type17.String({
4806
5318
  description: "UTF-8 markdown content to write."
4807
5319
  })
4808
5320
  },
@@ -4863,7 +5375,7 @@ function createSlackCanvasWriteTool(state) {
4863
5375
  }
4864
5376
 
4865
5377
  // src/chat/tools/slack/list-tools.ts
4866
- import { Type as Type17 } from "@sinclair/typebox";
5378
+ import { Type as Type18 } from "@sinclair/typebox";
4867
5379
 
4868
5380
  // src/chat/tools/slack/lists.ts
4869
5381
  function normalizeKey(value) {
@@ -4919,7 +5431,9 @@ async function createTodoList(name) {
4919
5431
  name,
4920
5432
  schema: DEFAULT_TODO_SCHEMA,
4921
5433
  todo_mode: true
4922
- })
5434
+ }),
5435
+ 3,
5436
+ { action: "slackLists.create" }
4923
5437
  );
4924
5438
  if (!result.list_id) {
4925
5439
  throw new Error("Slack list was created without list_id");
@@ -4965,7 +5479,9 @@ async function addListItems(input) {
4965
5479
  () => client.slackLists.items.create({
4966
5480
  list_id: input.listId,
4967
5481
  initial_fields: initialFields
4968
- })
5482
+ }),
5483
+ 3,
5484
+ { action: "slackLists.items.create" }
4969
5485
  );
4970
5486
  if (response.item?.id) {
4971
5487
  createdItemIds.push(response.item.id);
@@ -4987,7 +5503,9 @@ async function listItems(listId, limit = 100) {
4987
5503
  list_id: listId,
4988
5504
  limit: cappedLimit,
4989
5505
  cursor
4990
- })
5506
+ }),
5507
+ 3,
5508
+ { action: "slackLists.items.list" }
4991
5509
  );
4992
5510
  const remaining = cappedLimit - items.length;
4993
5511
  if (remaining <= 0) {
@@ -5029,7 +5547,9 @@ async function updateListItem(input) {
5029
5547
  () => client.slackLists.items.update({
5030
5548
  list_id: input.listId,
5031
5549
  cells
5032
- })
5550
+ }),
5551
+ 3,
5552
+ { action: "slackLists.items.update" }
5033
5553
  );
5034
5554
  }
5035
5555
 
@@ -5037,8 +5557,8 @@ async function updateListItem(input) {
5037
5557
  function createSlackListCreateTool(state) {
5038
5558
  return tool({
5039
5559
  description: "Create a Slack todo list for action tracking. Use when the user needs structured tasks with ownership/completion tracking. Do not use for one-off notes without task management needs.",
5040
- inputSchema: Type17.Object({
5041
- name: Type17.String({
5560
+ inputSchema: Type18.Object({
5561
+ name: Type18.String({
5042
5562
  minLength: 1,
5043
5563
  maxLength: 160,
5044
5564
  description: "Name for the new Slack list."
@@ -5073,20 +5593,20 @@ function createSlackListCreateTool(state) {
5073
5593
  function createSlackListAddItemsTool(state) {
5074
5594
  return tool({
5075
5595
  description: "Add tasks to the active Slack list tracked in artifact context. Use when the user wants actionable items recorded in the current thread list. Do not use when no list exists and list creation was not requested.",
5076
- inputSchema: Type17.Object({
5077
- items: Type17.Array(Type17.String({ minLength: 1 }), {
5596
+ inputSchema: Type18.Object({
5597
+ items: Type18.Array(Type18.String({ minLength: 1 }), {
5078
5598
  minItems: 1,
5079
5599
  maxItems: 25,
5080
5600
  description: "List item titles to create."
5081
5601
  }),
5082
- assignee_user_id: Type17.Optional(
5083
- Type17.String({
5602
+ assignee_user_id: Type18.Optional(
5603
+ Type18.String({
5084
5604
  minLength: 1,
5085
5605
  description: "Optional Slack user ID assigned to all created items."
5086
5606
  })
5087
5607
  ),
5088
- due_date: Type17.Optional(
5089
- Type17.String({
5608
+ due_date: Type18.Optional(
5609
+ Type18.String({
5090
5610
  pattern: "^\\d{4}-\\d{2}-\\d{2}$",
5091
5611
  description: "Optional due date in YYYY-MM-DD format."
5092
5612
  })
@@ -5136,9 +5656,9 @@ function createSlackListGetItemsTool(state) {
5136
5656
  return tool({
5137
5657
  description: "Read items from the active Slack list tracked in artifact context. Use when the user asks for task status, open items, or list contents. Do not use when list state is already known from the immediately prior result.",
5138
5658
  annotations: { readOnlyHint: true, destructiveHint: false },
5139
- inputSchema: Type17.Object({
5140
- limit: Type17.Optional(
5141
- Type17.Integer({
5659
+ inputSchema: Type18.Object({
5660
+ limit: Type18.Optional(
5661
+ Type18.Integer({
5142
5662
  minimum: 1,
5143
5663
  maximum: 200,
5144
5664
  description: "Maximum number of list items to return."
@@ -5163,19 +5683,19 @@ function createSlackListGetItemsTool(state) {
5163
5683
  function createSlackListUpdateItemTool(state) {
5164
5684
  return tool({
5165
5685
  description: "Update an item in the active Slack list tracked in artifact context (title/completion). Use when the user asks to mark progress or rename a tracked task. Do not use to add new tasks.",
5166
- inputSchema: Type17.Object(
5686
+ inputSchema: Type18.Object(
5167
5687
  {
5168
- item_id: Type17.String({
5688
+ item_id: Type18.String({
5169
5689
  minLength: 1,
5170
5690
  description: "ID of the Slack list item to update."
5171
5691
  }),
5172
- completed: Type17.Optional(
5173
- Type17.Boolean({
5692
+ completed: Type18.Optional(
5693
+ Type18.Boolean({
5174
5694
  description: "Optional completion status update."
5175
5695
  })
5176
5696
  ),
5177
- title: Type17.Optional(
5178
- Type17.String({
5697
+ title: Type18.Optional(
5698
+ Type18.String({
5179
5699
  minLength: 1,
5180
5700
  description: "Optional new item title."
5181
5701
  })
@@ -5225,7 +5745,7 @@ function createSlackListUpdateItemTool(state) {
5225
5745
  }
5226
5746
 
5227
5747
  // src/chat/tools/slack/thread-read.ts
5228
- import { Type as Type18 } from "@sinclair/typebox";
5748
+ import { Type as Type19 } from "@sinclair/typebox";
5229
5749
 
5230
5750
  // src/chat/tools/slack/slack-message-url.ts
5231
5751
  var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
@@ -5407,34 +5927,34 @@ function createSlackThreadReadTool(context) {
5407
5927
  return tool({
5408
5928
  description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Public channel links can be read if the bot has access; private channels and DMs are only readable when they are the current conversation.",
5409
5929
  annotations: { readOnlyHint: true, destructiveHint: false },
5410
- inputSchema: Type18.Object({
5411
- url: Type18.Optional(
5412
- Type18.String({
5930
+ inputSchema: Type19.Object({
5931
+ url: Type19.Optional(
5932
+ Type19.String({
5413
5933
  minLength: 1,
5414
5934
  description: "Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
5415
5935
  })
5416
5936
  ),
5417
- channel_id: Type18.Optional(
5418
- Type18.String({
5937
+ channel_id: Type19.Optional(
5938
+ Type19.String({
5419
5939
  minLength: 1,
5420
5940
  description: "Slack channel/conversation ID (e.g. C123). Use with `ts` as an alternative to `url`."
5421
5941
  })
5422
5942
  ),
5423
- ts: Type18.Optional(
5424
- Type18.String({
5943
+ ts: Type19.Optional(
5944
+ Type19.String({
5425
5945
  minLength: 1,
5426
5946
  description: "Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
5427
5947
  })
5428
5948
  ),
5429
- limit: Type18.Optional(
5430
- Type18.Integer({
5949
+ limit: Type19.Optional(
5950
+ Type19.Integer({
5431
5951
  minimum: 1,
5432
5952
  maximum: 1e3,
5433
5953
  description: "Maximum number of thread messages to fetch."
5434
5954
  })
5435
5955
  ),
5436
- max_pages: Type18.Optional(
5437
- Type18.Integer({
5956
+ max_pages: Type19.Optional(
5957
+ Type19.Integer({
5438
5958
  minimum: 1,
5439
5959
  maximum: 10,
5440
5960
  description: "Maximum number of Slack API pages to traverse."
@@ -5529,7 +6049,7 @@ function createSlackThreadReadTool(context) {
5529
6049
  }
5530
6050
 
5531
6051
  // src/chat/tools/slack/user-lookup.ts
5532
- import { Type as Type19 } from "@sinclair/typebox";
6052
+ import { Type as Type20 } from "@sinclair/typebox";
5533
6053
 
5534
6054
  // src/chat/slack/users.ts
5535
6055
  function normalizeUser(raw) {
@@ -5675,41 +6195,41 @@ function createSlackUserLookupTool() {
5675
6195
  return tool({
5676
6196
  description: "Look up Slack user profiles by user ID, email, or name search. Use when you need to identify a user, resolve cross-platform identity, or look up profile details like title or status. Returns profile fields including custom fields. For user ID lookup, pass a Slack user ID (e.g. U039RR91S). For search, pass a name query.",
5677
6197
  annotations: { readOnlyHint: true, destructiveHint: false },
5678
- inputSchema: Type19.Object({
5679
- user_id: Type19.Optional(
5680
- Type19.String({
6198
+ inputSchema: Type20.Object({
6199
+ user_id: Type20.Optional(
6200
+ Type20.String({
5681
6201
  minLength: 1,
5682
6202
  description: "Slack user ID to look up (e.g. U039RR91S). Mutually exclusive with email and query."
5683
6203
  })
5684
6204
  ),
5685
- email: Type19.Optional(
5686
- Type19.String({
6205
+ email: Type20.Optional(
6206
+ Type20.String({
5687
6207
  minLength: 3,
5688
6208
  description: "Email address to look up. Mutually exclusive with user_id and query."
5689
6209
  })
5690
6210
  ),
5691
- query: Type19.Optional(
5692
- Type19.String({
6211
+ query: Type20.Optional(
6212
+ Type20.String({
5693
6213
  minLength: 2,
5694
6214
  description: "Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
5695
6215
  })
5696
6216
  ),
5697
- limit: Type19.Optional(
5698
- Type19.Integer({
6217
+ limit: Type20.Optional(
6218
+ Type20.Integer({
5699
6219
  minimum: 1,
5700
6220
  maximum: 20,
5701
6221
  description: "Maximum number of results to return for name search. Defaults to 10."
5702
6222
  })
5703
6223
  ),
5704
- max_pages: Type19.Optional(
5705
- Type19.Integer({
6224
+ max_pages: Type20.Optional(
6225
+ Type20.Integer({
5706
6226
  minimum: 1,
5707
6227
  maximum: 5,
5708
6228
  description: "Maximum number of Slack API pages to scan for name search. Defaults to 3."
5709
6229
  })
5710
6230
  ),
5711
- include_bots: Type19.Optional(
5712
- Type19.Boolean({
6231
+ include_bots: Type20.Optional(
6232
+ Type20.Boolean({
5713
6233
  description: "Include bot accounts in name search results. Defaults to false."
5714
6234
  })
5715
6235
  )
@@ -5788,12 +6308,12 @@ function createSlackUserLookupTool() {
5788
6308
  }
5789
6309
 
5790
6310
  // src/chat/tools/system-time.ts
5791
- import { Type as Type20 } from "@sinclair/typebox";
6311
+ import { Type as Type21 } from "@sinclair/typebox";
5792
6312
  function createSystemTimeTool() {
5793
6313
  return tool({
5794
6314
  description: "Return current system time in UTC and local ISO formats. Use when the user asks for current time/date context. Do not use as a substitute for historical or timezone-conversion research.",
5795
6315
  annotations: { readOnlyHint: true, destructiveHint: false },
5796
- inputSchema: Type20.Object({}),
6316
+ inputSchema: Type21.Object({}),
5797
6317
  execute: async () => {
5798
6318
  const now = /* @__PURE__ */ new Date();
5799
6319
  return {
@@ -5811,7 +6331,7 @@ function createSystemTimeTool() {
5811
6331
  import {
5812
6332
  Agent
5813
6333
  } from "@earendil-works/pi-agent-core";
5814
- import { Type as Type21 } from "@sinclair/typebox";
6334
+ import { Type as Type22 } from "@sinclair/typebox";
5815
6335
  import { THREAD_STATE_TTL_MS } from "chat";
5816
6336
 
5817
6337
  // src/chat/tools/advisor/session-store.ts
@@ -5899,12 +6419,12 @@ function createAdvisorTool(context) {
5899
6419
  const spanContext = context.logContext ?? {};
5900
6420
  return tool({
5901
6421
  description: ADVISOR_TOOL_DESCRIPTION,
5902
- inputSchema: Type21.Object({
5903
- question: Type21.String({
6422
+ inputSchema: Type22.Object({
6423
+ question: Type22.String({
5904
6424
  minLength: 1,
5905
6425
  description: "Focused advisor question or decision point."
5906
6426
  }),
5907
- context: Type21.String({
6427
+ context: Type22.String({
5908
6428
  minLength: 1,
5909
6429
  description: "Curated evidence packet: relevant requirements, constraints, current plan, alternatives, code snippets, diffs, command output, and open questions."
5910
6430
  })
@@ -5933,12 +6453,16 @@ function createAdvisorTool(context) {
5933
6453
  const conversationId = context.conversationId;
5934
6454
  const advisorSessionKey = getAdvisorSessionKey(conversationId);
5935
6455
  const subagentInvocationId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : `advisor:${Date.now()}`;
5936
- const endSubagent = async (outcome, errorCode) => recordSubagentEnded({
6456
+ const endSubagent = async (outcome, errorCode, transcriptRange) => recordSubagentEnded({
5937
6457
  conversationId,
5938
6458
  sessionId: context.parentSessionId,
5939
6459
  subagentInvocationId,
5940
6460
  outcome,
5941
6461
  ...errorCode ? { errorCode } : {},
6462
+ ...transcriptRange ? {
6463
+ transcriptEndMessageIndex: transcriptRange.endMessageIndex,
6464
+ transcriptStartMessageIndex: transcriptRange.startMessageIndex
6465
+ } : {},
5942
6466
  ttlMs: THREAD_STATE_TTL_MS
5943
6467
  });
5944
6468
  await recordSubagentStarted({
@@ -6057,7 +6581,10 @@ function createAdvisorTool(context) {
6057
6581
  "Advisor guidance is unavailable because advisor history could not be saved. Retry the advisor call or continue without assuming advisor history."
6058
6582
  );
6059
6583
  }
6060
- await endSubagent("success");
6584
+ await endSubagent("success", void 0, {
6585
+ endMessageIndex: advisorAgent.state.messages.length,
6586
+ startMessageIndex: beforeMessageCount
6587
+ });
6061
6588
  setSpanStatus("ok");
6062
6589
  return success(memo);
6063
6590
  },
@@ -6080,7 +6607,7 @@ function createAdvisorTool(context) {
6080
6607
  }
6081
6608
 
6082
6609
  // src/chat/tools/web/fetch-tool.ts
6083
- import { Type as Type22 } from "@sinclair/typebox";
6610
+ import { Type as Type23 } from "@sinclair/typebox";
6084
6611
 
6085
6612
  // src/chat/tools/web/constants.ts
6086
6613
  var USER_AGENT = "junior-bot/0.1";
@@ -6525,13 +7052,13 @@ function createWebFetchTool(hooks) {
6525
7052
  destructiveHint: false,
6526
7053
  openWorldHint: true
6527
7054
  },
6528
- inputSchema: Type22.Object({
6529
- url: Type22.String({
7055
+ inputSchema: Type23.Object({
7056
+ url: Type23.String({
6530
7057
  minLength: 1,
6531
7058
  description: "HTTP(S) URL to fetch."
6532
7059
  }),
6533
- max_chars: Type22.Optional(
6534
- Type22.Integer({
7060
+ max_chars: Type23.Optional(
7061
+ Type23.Integer({
6535
7062
  minimum: 500,
6536
7063
  maximum: MAX_FETCH_CHARS,
6537
7064
  description: "Optional maximum number of extracted characters to return."
@@ -6595,7 +7122,7 @@ function createWebFetchTool(hooks) {
6595
7122
  import { generateText } from "ai";
6596
7123
  import { createGatewayProvider } from "@ai-sdk/gateway";
6597
7124
  import { getModel } from "@earendil-works/pi-ai";
6598
- import { Type as Type23 } from "@sinclair/typebox";
7125
+ import { Type as Type24 } from "@sinclair/typebox";
6599
7126
  var SEARCH_TIMEOUT_MS = 6e4;
6600
7127
  var MAX_RESULTS2 = 5;
6601
7128
  var DEFAULT_SEARCH_MODEL = getModel("vercel-ai-gateway", "openai/gpt-5.4").id;
@@ -6643,14 +7170,14 @@ function createWebSearchTool(override) {
6643
7170
  destructiveHint: false,
6644
7171
  openWorldHint: true
6645
7172
  },
6646
- inputSchema: Type23.Object({
6647
- query: Type23.String({
7173
+ inputSchema: Type24.Object({
7174
+ query: Type24.String({
6648
7175
  minLength: 1,
6649
7176
  maxLength: 500,
6650
7177
  description: "Search query."
6651
7178
  }),
6652
- max_results: Type23.Optional(
6653
- Type23.Integer({
7179
+ max_results: Type24.Optional(
7180
+ Type24.Integer({
6654
7181
  minimum: 1,
6655
7182
  maximum: MAX_RESULTS2,
6656
7183
  description: "Max results to return."
@@ -6722,18 +7249,18 @@ function createWebSearchTool(override) {
6722
7249
  }
6723
7250
 
6724
7251
  // src/chat/tools/sandbox/write-file.ts
6725
- import { Type as Type24 } from "@sinclair/typebox";
7252
+ import { Type as Type25 } from "@sinclair/typebox";
6726
7253
  function createWriteFileTool() {
6727
7254
  return tool({
6728
7255
  description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or deliberate full-file replacement after validation; use editFile instead for targeted changes to existing files. Do not use for exploratory analysis-only turns.",
6729
7256
  executionMode: "sequential",
6730
- inputSchema: Type24.Object(
7257
+ inputSchema: Type25.Object(
6731
7258
  {
6732
- path: Type24.String({
7259
+ path: Type25.String({
6733
7260
  minLength: 1,
6734
7261
  description: "Path to write in the sandbox workspace."
6735
7262
  }),
6736
- content: Type24.String({
7263
+ content: Type25.String({
6737
7264
  description: "UTF-8 file content to write."
6738
7265
  })
6739
7266
  },
@@ -6802,6 +7329,11 @@ function createTools(availableSkills, hooks = {}, context) {
6802
7329
  if (context.advisor) {
6803
7330
  tools.advisor = createAdvisorTool(context.advisor);
6804
7331
  }
7332
+ if (canUseResourceEventSubscriptionTools(context)) {
7333
+ tools.subscribeToResourceEvents = createSubscribeToResourceEventsTool(context);
7334
+ tools.listResourceEventSubscriptions = createListResourceEventSubscriptionsTool(context);
7335
+ tools.cancelResourceEventSubscription = createCancelResourceEventSubscriptionTool(context);
7336
+ }
6805
7337
  if (context.mcpToolManager) {
6806
7338
  tools.searchMcpTools = createSearchMcpToolsTool(context.mcpToolManager);
6807
7339
  tools.callMcpTool = createCallMcpToolTool(context.mcpToolManager);
@@ -7063,7 +7595,14 @@ async function deliverPrivateMessage(input) {
7063
7595
  }
7064
7596
  }
7065
7597
  try {
7066
- const dmChannelId = (await client.conversations.open({ users: input.userId })).channel?.id;
7598
+ const dmChannelId = (await withSlackRetries(
7599
+ () => client.conversations.open({ users: input.userId }),
7600
+ 3,
7601
+ {
7602
+ action: "conversations.open",
7603
+ spanAttributes: { "app.slack.user_id": input.userId }
7604
+ }
7605
+ )).channel?.id;
7067
7606
  if (!dmChannelId) {
7068
7607
  logWarn(
7069
7608
  "oauth_dm_fallback_failed",
@@ -7167,59 +7706,59 @@ async function startOAuthFlow(provider, input) {
7167
7706
  import { createHmac, randomUUID, timingSafeEqual } from "crypto";
7168
7707
 
7169
7708
  // src/chat/sandbox/egress/schemas.ts
7170
- import { z } from "zod";
7709
+ import { z as z2 } from "zod";
7171
7710
  import {
7172
7711
  pluginAuthorizationSchema,
7173
7712
  pluginCredentialHeaderTransformSchema,
7174
7713
  pluginGrantSchema,
7175
7714
  pluginProviderAccountSchema
7176
7715
  } from "@sentry/junior-plugin-api";
7177
- var finiteNumberSchema = z.number().refine(Number.isFinite);
7178
- var httpStatusSchema = z.number().int().min(100).max(599);
7179
- var providerNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
7180
- var credentialSignalKindSchema = z.enum(["auth_required", "unavailable"]);
7716
+ var finiteNumberSchema = z2.number().refine(Number.isFinite);
7717
+ var httpStatusSchema = z2.number().int().min(100).max(599);
7718
+ var providerNameSchema = z2.string().regex(/^[a-z][a-z0-9-]*$/);
7719
+ var credentialSignalKindSchema = z2.enum(["auth_required", "unavailable"]);
7181
7720
  var sandboxEgressGrantSchema = pluginGrantSchema;
7182
- var sandboxEgressCredentialContextSchema = z.object({
7721
+ var sandboxEgressCredentialContextSchema = z2.object({
7183
7722
  credentials: credentialContextSchema,
7184
- egressId: z.string().min(1),
7723
+ egressId: z2.string().min(1),
7185
7724
  expiresAtMs: finiteNumberSchema,
7186
- contextId: z.string().min(1)
7725
+ contextId: z2.string().min(1)
7187
7726
  }).strict();
7188
- var sandboxEgressCredentialLeaseSchema = z.object({
7727
+ var sandboxEgressCredentialLeaseSchema = z2.object({
7189
7728
  account: pluginProviderAccountSchema.optional(),
7190
7729
  authorization: pluginAuthorizationSchema.optional(),
7191
7730
  grant: sandboxEgressGrantSchema,
7192
7731
  provider: providerNameSchema,
7193
- expiresAt: z.string().min(1),
7194
- headerTransforms: z.array(pluginCredentialHeaderTransformSchema).min(1)
7732
+ expiresAt: z2.string().min(1),
7733
+ headerTransforms: z2.array(pluginCredentialHeaderTransformSchema).min(1)
7195
7734
  }).strict();
7196
- var sandboxEgressAuthRequiredSignalSchema = z.object({
7735
+ var sandboxEgressAuthRequiredSignalSchema = z2.object({
7197
7736
  authorization: pluginAuthorizationSchema.optional(),
7198
7737
  grant: sandboxEgressGrantSchema,
7199
7738
  kind: credentialSignalKindSchema.default("auth_required"),
7200
7739
  provider: providerNameSchema,
7201
- message: z.string().optional(),
7740
+ message: z2.string().optional(),
7202
7741
  createdAtMs: finiteNumberSchema
7203
7742
  }).strict().superRefine((signal, ctx) => {
7204
7743
  if (signal.authorization && signal.authorization.provider !== signal.provider) {
7205
7744
  ctx.addIssue({
7206
- code: z.ZodIssueCode.custom,
7745
+ code: z2.ZodIssueCode.custom,
7207
7746
  message: "Auth signal authorization provider must match provider",
7208
7747
  path: ["authorization", "provider"]
7209
7748
  });
7210
7749
  }
7211
7750
  });
7212
- var sandboxEgressPermissionDeniedSignalSchema = z.object({
7751
+ var sandboxEgressPermissionDeniedSignalSchema = z2.object({
7213
7752
  account: pluginProviderAccountSchema.optional(),
7214
- acceptedPermissions: z.string().optional(),
7753
+ acceptedPermissions: z2.string().optional(),
7215
7754
  grant: sandboxEgressGrantSchema,
7216
- message: z.string().min(1),
7755
+ message: z2.string().min(1),
7217
7756
  provider: providerNameSchema,
7218
- source: z.literal("upstream"),
7219
- sso: z.string().optional(),
7757
+ source: z2.literal("upstream"),
7758
+ sso: z2.string().optional(),
7220
7759
  status: httpStatusSchema,
7221
- upstreamHost: z.string().min(1),
7222
- upstreamPath: z.string().min(1),
7760
+ upstreamHost: z2.string().min(1),
7761
+ upstreamPath: z2.string().min(1),
7223
7762
  createdAtMs: finiteNumberSchema
7224
7763
  }).strict();
7225
7764
  function parseSandboxEgressAuthRequiredSignal(value) {
@@ -7296,12 +7835,12 @@ function getSandboxEgressSecret() {
7296
7835
  throw new Error("Cannot determine sandbox egress secret (set JUNIOR_SECRET)");
7297
7836
  }
7298
7837
  function createSandboxEgressCredentialToken(input) {
7299
- const ttlMs = Math.max(1, input.ttlMs ?? DEFAULT_SESSION_TTL_MS);
7838
+ const ttlMs2 = Math.max(1, input.ttlMs ?? DEFAULT_SESSION_TTL_MS);
7300
7839
  const now = Date.now();
7301
7840
  const context = {
7302
7841
  credentials: input.credentials,
7303
7842
  egressId: input.egressId,
7304
- expiresAtMs: now + ttlMs,
7843
+ expiresAtMs: now + ttlMs2,
7305
7844
  contextId: randomUUID()
7306
7845
  };
7307
7846
  const payload = `${SANDBOX_EGRESS_TOKEN_VERSION}.${base64Url(
@@ -7337,7 +7876,7 @@ async function setSandboxEgressCredentialLease(context, lease) {
7337
7876
  if (!Number.isFinite(leaseExpiresAtMs) || leaseExpiresAtMs <= Date.now()) {
7338
7877
  return;
7339
7878
  }
7340
- const ttlMs = Math.max(
7879
+ const ttlMs2 = Math.max(
7341
7880
  1,
7342
7881
  Math.min(leaseExpiresAtMs, context.expiresAtMs) - Date.now()
7343
7882
  );
@@ -7346,7 +7885,7 @@ async function setSandboxEgressCredentialLease(context, lease) {
7346
7885
  await state.set(
7347
7886
  leaseKey(lease.provider, lease.grant.name, context),
7348
7887
  lease,
7349
- ttlMs
7888
+ ttlMs2
7350
7889
  );
7351
7890
  }
7352
7891
  async function getSandboxEgressCredentialLease(provider, grantName, context) {
@@ -7360,7 +7899,7 @@ async function clearSandboxEgressCredentialLease(provider, grantName, context) {
7360
7899
  await state.delete(leaseKey(provider, grantName, context));
7361
7900
  }
7362
7901
  async function setSandboxEgressAuthRequiredSignal(context, signal) {
7363
- const ttlMs = Math.max(1, context.expiresAtMs - Date.now());
7902
+ const ttlMs2 = Math.max(1, context.expiresAtMs - Date.now());
7364
7903
  const state = getStateAdapter();
7365
7904
  await state.connect();
7366
7905
  await state.set(
@@ -7369,11 +7908,11 @@ async function setSandboxEgressAuthRequiredSignal(context, signal) {
7369
7908
  ...signal,
7370
7909
  createdAtMs: Date.now()
7371
7910
  },
7372
- ttlMs
7911
+ ttlMs2
7373
7912
  );
7374
7913
  }
7375
7914
  async function setSandboxEgressPermissionDeniedSignal(context, signal) {
7376
- const ttlMs = Math.max(1, context.expiresAtMs - Date.now());
7915
+ const ttlMs2 = Math.max(1, context.expiresAtMs - Date.now());
7377
7916
  const state = getStateAdapter();
7378
7917
  await state.connect();
7379
7918
  await state.set(
@@ -7382,7 +7921,7 @@ async function setSandboxEgressPermissionDeniedSignal(context, signal) {
7382
7921
  ...signal,
7383
7922
  createdAtMs: Date.now()
7384
7923
  },
7385
- ttlMs
7924
+ ttlMs2
7386
7925
  );
7387
7926
  }
7388
7927
  async function clearSandboxEgressSignals(egressId) {
@@ -9383,12 +9922,12 @@ async function getMcpAuthSession(authSessionId) {
9383
9922
  const stateAdapter = await getConnectedStateAdapter();
9384
9923
  return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
9385
9924
  }
9386
- async function putMcpAuthSession(session, ttlMs = MCP_AUTH_SESSION_TTL_MS) {
9925
+ async function putMcpAuthSession(session, ttlMs2 = MCP_AUTH_SESSION_TTL_MS) {
9387
9926
  const stateAdapter = await getConnectedStateAdapter();
9388
9927
  await stateAdapter.set(
9389
9928
  sessionKey(session.authSessionId),
9390
9929
  JSON.stringify(session),
9391
- ttlMs
9930
+ ttlMs2
9392
9931
  );
9393
9932
  const nextIndex = parseSessionIndex(
9394
9933
  await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
@@ -9399,7 +9938,7 @@ async function putMcpAuthSession(session, ttlMs = MCP_AUTH_SESSION_TTL_MS) {
9399
9938
  await stateAdapter.set(
9400
9939
  sessionIndexKey(session.userId, session.provider),
9401
9940
  JSON.stringify(nextIndex),
9402
- ttlMs
9941
+ ttlMs2
9403
9942
  );
9404
9943
  }
9405
9944
  async function patchMcpAuthSession(authSessionId, patch) {
@@ -9479,12 +10018,12 @@ async function getMcpStoredOAuthCredentials(userId, provider) {
9479
10018
  await stateAdapter.get(credentialsKey(userId, provider))
9480
10019
  );
9481
10020
  }
9482
- async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs = MCP_AUTH_CREDENTIALS_TTL_MS) {
10021
+ async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs2 = MCP_AUTH_CREDENTIALS_TTL_MS) {
9483
10022
  const stateAdapter = await getConnectedStateAdapter();
9484
10023
  await stateAdapter.set(
9485
10024
  credentialsKey(userId, provider),
9486
10025
  JSON.stringify(value),
9487
- ttlMs
10026
+ ttlMs2
9488
10027
  );
9489
10028
  }
9490
10029
  async function deleteMcpStoredOAuthCredentials(userId, provider) {
@@ -9497,7 +10036,7 @@ async function getMcpServerSessionId(userId, provider) {
9497
10036
  await stateAdapter.get(serverSessionKey(userId, provider))
9498
10037
  )?.sessionId;
9499
10038
  }
9500
- async function putMcpServerSessionId(userId, provider, sessionId, ttlMs = MCP_SERVER_SESSION_TTL_MS) {
10039
+ async function putMcpServerSessionId(userId, provider, sessionId, ttlMs2 = MCP_SERVER_SESSION_TTL_MS) {
9501
10040
  const stateAdapter = await getConnectedStateAdapter();
9502
10041
  await stateAdapter.set(
9503
10042
  serverSessionKey(userId, provider),
@@ -9505,7 +10044,7 @@ async function putMcpServerSessionId(userId, provider, sessionId, ttlMs = MCP_SE
9505
10044
  sessionId,
9506
10045
  updatedAtMs: Date.now()
9507
10046
  }),
9508
- ttlMs
10047
+ ttlMs2
9509
10048
  );
9510
10049
  }
9511
10050
  async function deleteMcpServerSessionId(userId, provider) {
@@ -9525,6 +10064,15 @@ async function unlinkProvider(userId, provider, userTokenStore) {
9525
10064
 
9526
10065
  // src/chat/services/pending-auth.ts
9527
10066
  var AUTH_LINK_REUSE_WINDOW_MS = 10 * 60 * 1e3;
10067
+ var NON_REQUEST_SKIPPED_REASON_PREFIXES = [
10068
+ "directed_to_other_party:",
10069
+ "side_conversation:"
10070
+ ];
10071
+ function isSkippedNonRequest(reason) {
10072
+ return NON_REQUEST_SKIPPED_REASON_PREFIXES.some(
10073
+ (prefix) => reason?.startsWith(prefix)
10074
+ );
10075
+ }
9528
10076
  function canReusePendingAuthLink(args) {
9529
10077
  const { pendingAuth } = args;
9530
10078
  if (!pendingAuth) {
@@ -9568,6 +10116,12 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
9568
10116
  if (message?.role !== "user") {
9569
10117
  continue;
9570
10118
  }
10119
+ if (message.author?.isBot) {
10120
+ continue;
10121
+ }
10122
+ if (isSkippedNonRequest(message.meta?.skippedReason)) {
10123
+ continue;
10124
+ }
9571
10125
  return buildDeterministicTurnId(message.id) === pendingAuth.sessionId;
9572
10126
  }
9573
10127
  return false;
@@ -10207,7 +10761,7 @@ function buildTurnResult(input) {
10207
10761
  }
10208
10762
 
10209
10763
  // src/chat/services/turn-thinking-level.ts
10210
- import { z as z2 } from "zod";
10764
+ import { z as z3 } from "zod";
10211
10765
  var CLASSIFIER_CONFIDENCE_THRESHOLD = 0.75;
10212
10766
  var MAX_ROUTER_CONTEXT_CHARS = 8e3;
10213
10767
  var ROUTER_CONTEXT_HEAD_CHARS = 3e3;
@@ -10239,13 +10793,13 @@ function coerceClassifierConfidence(value) {
10239
10793
  }
10240
10794
  return CONFIDENCE_LABELS[trimmed] ?? value;
10241
10795
  }
10242
- var turnExecutionProfileSchema = z2.object({
10243
- thinking_level: z2.enum(TURN_THINKING_LEVELS),
10244
- confidence: z2.preprocess(
10796
+ var turnExecutionProfileSchema = z3.object({
10797
+ thinking_level: z3.enum(TURN_THINKING_LEVELS),
10798
+ confidence: z3.preprocess(
10245
10799
  coerceClassifierConfidence,
10246
- z2.number().min(0).max(1)
10800
+ z3.number().min(0).max(1)
10247
10801
  ),
10248
- reason: z2.string().min(1)
10802
+ reason: z3.string().min(1)
10249
10803
  });
10250
10804
  var DEFAULT_THINKING_LEVEL = "medium";
10251
10805
  var THINKING_LEVEL_RANK = {
@@ -14215,6 +14769,8 @@ export {
14215
14769
  setConfigDefaults,
14216
14770
  getConfigDefaults,
14217
14771
  createUserTokenStore,
14772
+ findMatchingResourceEventSubscriptions,
14773
+ deliverResourceEventSubscription,
14218
14774
  listThreadReplies,
14219
14775
  postSlackMessage,
14220
14776
  deleteSlackMessage,