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