@sentry/junior 0.1.0 → 0.1.1
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/README.md +111 -2
- package/dist/bot-6KXJ366H.js +16 -0
- package/dist/{chunk-7E56WM6K.js → chunk-5LLCJPTH.js} +856 -49
- package/dist/{bot-DLML4Z7F.js → chunk-CJFEZLEN.js} +59 -31
- package/dist/{chunk-OD6TOSY4.js → chunk-OVG2HBNM.js} +1 -1
- package/dist/handlers/queue-callback.js +6 -8
- package/dist/handlers/router.js +2 -2
- package/dist/handlers/webhooks.js +1 -1
- package/dist/{route-XLYK6CKP.js → route-DMVINKJW.js} +4 -9
- package/package.json +3 -3
- package/dist/channel-HJO33DGJ.js +0 -18
- package/dist/chunk-GDNDYMGX.js +0 -333
- package/dist/chunk-MM3YNA4F.js +0 -203
- package/dist/chunk-ZA2IDPVG.js +0 -39
- package/dist/chunk-ZBFSIN6G.js +0 -323
- package/dist/client-3GAEMIQ3.js +0 -10
package/dist/chunk-MM3YNA4F.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getSlackClient,
|
|
3
|
-
normalizeSlackConversationId,
|
|
4
|
-
withSlackRetries
|
|
5
|
-
} from "./chunk-GDNDYMGX.js";
|
|
6
|
-
|
|
7
|
-
// src/chat/slack-actions/channel.ts
|
|
8
|
-
async function postMessageToChannel(input) {
|
|
9
|
-
const client = getSlackClient();
|
|
10
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
11
|
-
if (!channelId) {
|
|
12
|
-
throw new Error("Slack channel message posting requires a valid channel ID");
|
|
13
|
-
}
|
|
14
|
-
const response = await withSlackRetries(
|
|
15
|
-
() => client.chat.postMessage({
|
|
16
|
-
channel: channelId,
|
|
17
|
-
text: input.text,
|
|
18
|
-
mrkdwn: true
|
|
19
|
-
}),
|
|
20
|
-
3,
|
|
21
|
-
{ action: "chat.postMessage" }
|
|
22
|
-
);
|
|
23
|
-
if (!response.ts) {
|
|
24
|
-
throw new Error("Slack channel message posted without ts");
|
|
25
|
-
}
|
|
26
|
-
let permalink;
|
|
27
|
-
try {
|
|
28
|
-
const permalinkResponse = await withSlackRetries(
|
|
29
|
-
() => client.chat.getPermalink({
|
|
30
|
-
channel: channelId,
|
|
31
|
-
message_ts: response.ts
|
|
32
|
-
}),
|
|
33
|
-
3,
|
|
34
|
-
{ action: "chat.getPermalink" }
|
|
35
|
-
);
|
|
36
|
-
permalink = permalinkResponse.permalink;
|
|
37
|
-
} catch {
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
ts: response.ts,
|
|
41
|
-
permalink
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
async function addReactionToMessage(input) {
|
|
45
|
-
const client = getSlackClient();
|
|
46
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
47
|
-
if (!channelId) {
|
|
48
|
-
throw new Error("Slack reaction requires a valid channel ID");
|
|
49
|
-
}
|
|
50
|
-
const timestamp = input.timestamp.trim();
|
|
51
|
-
if (!timestamp) {
|
|
52
|
-
throw new Error("Slack reaction requires a target message timestamp");
|
|
53
|
-
}
|
|
54
|
-
const emoji = input.emoji.trim().replaceAll(":", "");
|
|
55
|
-
if (!emoji) {
|
|
56
|
-
throw new Error("Slack reaction requires a non-empty emoji name");
|
|
57
|
-
}
|
|
58
|
-
await withSlackRetries(
|
|
59
|
-
() => client.reactions.add({
|
|
60
|
-
channel: channelId,
|
|
61
|
-
timestamp,
|
|
62
|
-
name: emoji
|
|
63
|
-
}),
|
|
64
|
-
3,
|
|
65
|
-
{ action: "reactions.add" }
|
|
66
|
-
);
|
|
67
|
-
return { ok: true };
|
|
68
|
-
}
|
|
69
|
-
async function removeReactionFromMessage(input) {
|
|
70
|
-
const client = getSlackClient();
|
|
71
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
72
|
-
if (!channelId) {
|
|
73
|
-
throw new Error("Slack reaction requires a valid channel ID");
|
|
74
|
-
}
|
|
75
|
-
const timestamp = input.timestamp.trim();
|
|
76
|
-
if (!timestamp) {
|
|
77
|
-
throw new Error("Slack reaction requires a target message timestamp");
|
|
78
|
-
}
|
|
79
|
-
const emoji = input.emoji.trim().replaceAll(":", "");
|
|
80
|
-
if (!emoji) {
|
|
81
|
-
throw new Error("Slack reaction requires a non-empty emoji name");
|
|
82
|
-
}
|
|
83
|
-
await withSlackRetries(
|
|
84
|
-
() => client.reactions.remove({
|
|
85
|
-
channel: channelId,
|
|
86
|
-
timestamp,
|
|
87
|
-
name: emoji
|
|
88
|
-
}),
|
|
89
|
-
3,
|
|
90
|
-
{ action: "reactions.remove" }
|
|
91
|
-
);
|
|
92
|
-
return { ok: true };
|
|
93
|
-
}
|
|
94
|
-
async function listChannelMessages(input) {
|
|
95
|
-
const client = getSlackClient();
|
|
96
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
97
|
-
if (!channelId) {
|
|
98
|
-
throw new Error("Slack channel history lookup requires a valid channel ID");
|
|
99
|
-
}
|
|
100
|
-
const targetLimit = Math.max(1, Math.min(input.limit, 1e3));
|
|
101
|
-
const maxPages = Math.max(1, Math.min(input.maxPages ?? 5, 10));
|
|
102
|
-
const messages = [];
|
|
103
|
-
let cursor = input.cursor;
|
|
104
|
-
let pages = 0;
|
|
105
|
-
while (messages.length < targetLimit && pages < maxPages) {
|
|
106
|
-
pages += 1;
|
|
107
|
-
const pageLimit = Math.max(1, Math.min(200, targetLimit - messages.length));
|
|
108
|
-
const response = await withSlackRetries(
|
|
109
|
-
() => client.conversations.history({
|
|
110
|
-
channel: channelId,
|
|
111
|
-
limit: pageLimit,
|
|
112
|
-
cursor,
|
|
113
|
-
oldest: input.oldest,
|
|
114
|
-
latest: input.latest,
|
|
115
|
-
inclusive: input.inclusive
|
|
116
|
-
}),
|
|
117
|
-
3,
|
|
118
|
-
{ action: "conversations.history" }
|
|
119
|
-
);
|
|
120
|
-
const batch = response.messages ?? [];
|
|
121
|
-
messages.push(...batch);
|
|
122
|
-
cursor = response.response_metadata?.next_cursor || void 0;
|
|
123
|
-
if (!cursor) {
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
messages: messages.slice(0, targetLimit),
|
|
129
|
-
nextCursor: cursor
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
async function listChannelMembers(input) {
|
|
133
|
-
const client = getSlackClient();
|
|
134
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
135
|
-
if (!channelId) {
|
|
136
|
-
throw new Error("Slack channel member lookup requires a valid channel ID");
|
|
137
|
-
}
|
|
138
|
-
const targetLimit = Math.max(1, Math.min(input.limit, 200));
|
|
139
|
-
const response = await withSlackRetries(
|
|
140
|
-
() => client.conversations.members({
|
|
141
|
-
channel: channelId,
|
|
142
|
-
limit: targetLimit,
|
|
143
|
-
cursor: input.cursor
|
|
144
|
-
}),
|
|
145
|
-
3,
|
|
146
|
-
{ action: "conversations.members" }
|
|
147
|
-
);
|
|
148
|
-
const members = (response.members ?? []).slice(0, targetLimit);
|
|
149
|
-
return {
|
|
150
|
-
members: members.map((userId) => ({ user_id: userId })),
|
|
151
|
-
nextCursor: response.response_metadata?.next_cursor || void 0
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
async function listThreadReplies(input) {
|
|
155
|
-
const client = getSlackClient();
|
|
156
|
-
const channelId = normalizeSlackConversationId(input.channelId);
|
|
157
|
-
if (!channelId) {
|
|
158
|
-
throw new Error("Slack thread reply lookup requires a valid channel ID");
|
|
159
|
-
}
|
|
160
|
-
const targetLimit = Math.max(1, Math.min(input.limit ?? 1e3, 1e3));
|
|
161
|
-
const maxPages = Math.max(1, Math.min(input.maxPages ?? 10, 10));
|
|
162
|
-
const pendingTargets = new Set(
|
|
163
|
-
(input.targetMessageTs ?? []).filter((value) => typeof value === "string" && value.length > 0)
|
|
164
|
-
);
|
|
165
|
-
const replies = [];
|
|
166
|
-
let cursor;
|
|
167
|
-
let pages = 0;
|
|
168
|
-
while (replies.length < targetLimit && pages < maxPages) {
|
|
169
|
-
pages += 1;
|
|
170
|
-
const pageLimit = Math.max(1, Math.min(200, targetLimit - replies.length));
|
|
171
|
-
const response = await withSlackRetries(
|
|
172
|
-
() => client.conversations.replies({
|
|
173
|
-
channel: channelId,
|
|
174
|
-
ts: input.threadTs,
|
|
175
|
-
limit: pageLimit,
|
|
176
|
-
cursor
|
|
177
|
-
}),
|
|
178
|
-
3,
|
|
179
|
-
{ action: "conversations.replies" }
|
|
180
|
-
);
|
|
181
|
-
const batch = response.messages ?? [];
|
|
182
|
-
replies.push(...batch);
|
|
183
|
-
for (const reply of batch) {
|
|
184
|
-
if (typeof reply.ts === "string" && pendingTargets.size > 0) {
|
|
185
|
-
pendingTargets.delete(reply.ts);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
cursor = response.response_metadata?.next_cursor || void 0;
|
|
189
|
-
if (!cursor || pendingTargets.size === 0) {
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return replies.slice(0, targetLimit);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export {
|
|
197
|
-
postMessageToChannel,
|
|
198
|
-
addReactionToMessage,
|
|
199
|
-
removeReactionFromMessage,
|
|
200
|
-
listChannelMessages,
|
|
201
|
-
listChannelMembers,
|
|
202
|
-
listThreadReplies
|
|
203
|
-
};
|
package/dist/chunk-ZA2IDPVG.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// src/chat/queue/client.ts
|
|
2
|
-
import { handleCallback, send } from "@vercel/queue";
|
|
3
|
-
var DEFAULT_TOPIC_NAME = "junior-thread-message";
|
|
4
|
-
var MAX_DELIVERY_ATTEMPTS = 10;
|
|
5
|
-
function getThreadMessageTopic() {
|
|
6
|
-
return DEFAULT_TOPIC_NAME;
|
|
7
|
-
}
|
|
8
|
-
async function enqueueThreadMessage(payload, options) {
|
|
9
|
-
const result = await send(getThreadMessageTopic(), payload, {
|
|
10
|
-
...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
|
|
11
|
-
});
|
|
12
|
-
return result.messageId ?? void 0;
|
|
13
|
-
}
|
|
14
|
-
function createQueueCallbackHandler(handler) {
|
|
15
|
-
return handleCallback(
|
|
16
|
-
async (message, metadata) => {
|
|
17
|
-
await handler(message, {
|
|
18
|
-
messageId: metadata.messageId,
|
|
19
|
-
deliveryCount: metadata.deliveryCount,
|
|
20
|
-
topicName: metadata.topicName
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
retry: (_error, metadata) => {
|
|
25
|
-
if (metadata.deliveryCount >= MAX_DELIVERY_ATTEMPTS) {
|
|
26
|
-
return { acknowledge: true };
|
|
27
|
-
}
|
|
28
|
-
const backoffSeconds = Math.min(300, Math.max(5, metadata.deliveryCount * 5));
|
|
29
|
-
return { afterSeconds: backoffSeconds };
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
getThreadMessageTopic,
|
|
37
|
-
enqueueThreadMessage,
|
|
38
|
-
createQueueCallbackHandler
|
|
39
|
-
};
|
package/dist/chunk-ZBFSIN6G.js
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
hasRedisConfig
|
|
3
|
-
} from "./chunk-GDNDYMGX.js";
|
|
4
|
-
|
|
5
|
-
// src/chat/state.ts
|
|
6
|
-
import { createRedisState } from "@chat-adapter/state-redis";
|
|
7
|
-
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
8
|
-
var MIN_LOCK_TTL_MS = 1e3 * 60 * 5;
|
|
9
|
-
var QUEUE_INGRESS_DEDUP_PREFIX = "junior:queue_ingress";
|
|
10
|
-
var QUEUE_MESSAGE_PROCESSING_PREFIX = "junior:queue_message";
|
|
11
|
-
var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
|
|
12
|
-
var QUEUE_MESSAGE_PROCESSING_TTL_MS = 30 * 60 * 1e3;
|
|
13
|
-
var QUEUE_MESSAGE_COMPLETED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
14
|
-
var QUEUE_MESSAGE_FAILED_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
15
|
-
var AGENT_TURN_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
16
|
-
var CLAIM_OR_RECLAIM_PROCESSING_SCRIPT = `
|
|
17
|
-
local key = KEYS[1]
|
|
18
|
-
local nowMs = tonumber(ARGV[1])
|
|
19
|
-
local ttlMs = tonumber(ARGV[2])
|
|
20
|
-
local payload = ARGV[3]
|
|
21
|
-
local current = redis.call("get", key)
|
|
22
|
-
|
|
23
|
-
if not current then
|
|
24
|
-
redis.call("set", key, payload, "PX", ttlMs)
|
|
25
|
-
return 1
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
local ok, parsed = pcall(cjson.decode, current)
|
|
29
|
-
if not ok or type(parsed) ~= "table" then
|
|
30
|
-
return 0
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
local status = parsed["status"]
|
|
34
|
-
if status == "failed" then
|
|
35
|
-
redis.call("set", key, payload, "PX", ttlMs)
|
|
36
|
-
return 3
|
|
37
|
-
end
|
|
38
|
-
if status ~= "processing" then
|
|
39
|
-
return 0
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
local updatedAtMs = tonumber(parsed["updatedAtMs"])
|
|
43
|
-
if not updatedAtMs then
|
|
44
|
-
return 0
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
if updatedAtMs + ttlMs < nowMs then
|
|
48
|
-
redis.call("set", key, payload, "PX", ttlMs)
|
|
49
|
-
return 2
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
return 0
|
|
53
|
-
`;
|
|
54
|
-
var UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT = `
|
|
55
|
-
local key = KEYS[1]
|
|
56
|
-
local ownerToken = ARGV[1]
|
|
57
|
-
local ttlMs = tonumber(ARGV[2])
|
|
58
|
-
local payload = ARGV[3]
|
|
59
|
-
local current = redis.call("get", key)
|
|
60
|
-
|
|
61
|
-
if not current then
|
|
62
|
-
return 0
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
local ok, parsed = pcall(cjson.decode, current)
|
|
66
|
-
if not ok or type(parsed) ~= "table" then
|
|
67
|
-
return 0
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
local currentOwner = parsed["ownerToken"]
|
|
71
|
-
local status = parsed["status"]
|
|
72
|
-
if currentOwner ~= ownerToken then
|
|
73
|
-
return 0
|
|
74
|
-
end
|
|
75
|
-
if status ~= "processing" then
|
|
76
|
-
return 0
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
redis.call("set", key, payload, "PX", ttlMs)
|
|
80
|
-
return 1
|
|
81
|
-
`;
|
|
82
|
-
function createQueuedStateAdapter(base) {
|
|
83
|
-
const acquireLock = async (threadId, ttlMs) => {
|
|
84
|
-
const effectiveTtlMs = Math.max(ttlMs, MIN_LOCK_TTL_MS);
|
|
85
|
-
const lock = await base.acquireLock(threadId, effectiveTtlMs);
|
|
86
|
-
return lock;
|
|
87
|
-
};
|
|
88
|
-
return {
|
|
89
|
-
connect: () => base.connect(),
|
|
90
|
-
disconnect: () => base.disconnect(),
|
|
91
|
-
subscribe: (threadId) => base.subscribe(threadId),
|
|
92
|
-
unsubscribe: (threadId) => base.unsubscribe(threadId),
|
|
93
|
-
isSubscribed: (threadId) => base.isSubscribed(threadId),
|
|
94
|
-
acquireLock,
|
|
95
|
-
releaseLock: (lock) => base.releaseLock(lock),
|
|
96
|
-
extendLock: (lock, ttlMs) => base.extendLock(lock, Math.max(ttlMs, MIN_LOCK_TTL_MS)),
|
|
97
|
-
get: (key) => base.get(key),
|
|
98
|
-
set: (key, value, ttlMs) => base.set(key, value, ttlMs),
|
|
99
|
-
setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
|
|
100
|
-
delete: (key) => base.delete(key)
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function createStateAdapter() {
|
|
104
|
-
if (process.env.JUNIOR_STATE_ADAPTER?.trim().toLowerCase() === "memory") {
|
|
105
|
-
_redisStateAdapter = void 0;
|
|
106
|
-
return createQueuedStateAdapter(createMemoryState());
|
|
107
|
-
}
|
|
108
|
-
if (!hasRedisConfig()) {
|
|
109
|
-
throw new Error("REDIS_URL is required for durable Slack thread state");
|
|
110
|
-
}
|
|
111
|
-
const redisState = createRedisState({
|
|
112
|
-
url: process.env.REDIS_URL
|
|
113
|
-
});
|
|
114
|
-
_redisStateAdapter = redisState;
|
|
115
|
-
return createQueuedStateAdapter(redisState);
|
|
116
|
-
}
|
|
117
|
-
var _stateAdapter;
|
|
118
|
-
var _redisStateAdapter;
|
|
119
|
-
function getRedisStateAdapter() {
|
|
120
|
-
if (!_redisStateAdapter) {
|
|
121
|
-
getStateAdapter();
|
|
122
|
-
}
|
|
123
|
-
if (!_redisStateAdapter) {
|
|
124
|
-
throw new Error("Redis state adapter is unavailable for this runtime");
|
|
125
|
-
}
|
|
126
|
-
return _redisStateAdapter;
|
|
127
|
-
}
|
|
128
|
-
function queueMessageKey(rawKey) {
|
|
129
|
-
return `${QUEUE_MESSAGE_PROCESSING_PREFIX}:${rawKey}`;
|
|
130
|
-
}
|
|
131
|
-
function parseQueueMessageState(value) {
|
|
132
|
-
if (typeof value !== "string") {
|
|
133
|
-
return void 0;
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
const parsed = JSON.parse(value);
|
|
137
|
-
if (!parsed || parsed.status !== "processing" && parsed.status !== "completed" && parsed.status !== "failed" || typeof parsed.updatedAtMs !== "number") {
|
|
138
|
-
return void 0;
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
status: parsed.status,
|
|
142
|
-
updatedAtMs: parsed.updatedAtMs,
|
|
143
|
-
...typeof parsed.ownerToken === "string" ? { ownerToken: parsed.ownerToken } : {},
|
|
144
|
-
...typeof parsed.queueMessageId === "string" ? { queueMessageId: parsed.queueMessageId } : {},
|
|
145
|
-
...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {}
|
|
146
|
-
};
|
|
147
|
-
} catch {
|
|
148
|
-
return void 0;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function agentTurnSessionKey(conversationId, sessionId) {
|
|
152
|
-
return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
|
|
153
|
-
}
|
|
154
|
-
function isRecord(value) {
|
|
155
|
-
return typeof value === "object" && value !== null;
|
|
156
|
-
}
|
|
157
|
-
function parseAgentTurnSessionCheckpoint(value) {
|
|
158
|
-
if (typeof value !== "string") {
|
|
159
|
-
return void 0;
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const parsed = JSON.parse(value);
|
|
163
|
-
if (!isRecord(parsed)) {
|
|
164
|
-
return void 0;
|
|
165
|
-
}
|
|
166
|
-
const status = parsed.state;
|
|
167
|
-
if (status !== "running" && status !== "awaiting_resume" && status !== "completed" && status !== "failed") {
|
|
168
|
-
return void 0;
|
|
169
|
-
}
|
|
170
|
-
const conversationId = parsed.conversationId;
|
|
171
|
-
const sessionId = parsed.sessionId;
|
|
172
|
-
const sliceId = parsed.sliceId;
|
|
173
|
-
const checkpointVersion = parsed.checkpointVersion;
|
|
174
|
-
const updatedAtMs = parsed.updatedAtMs;
|
|
175
|
-
if (typeof conversationId !== "string" || typeof sessionId !== "string" || typeof sliceId !== "number" || typeof checkpointVersion !== "number" || typeof updatedAtMs !== "number") {
|
|
176
|
-
return void 0;
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
checkpointVersion,
|
|
180
|
-
conversationId,
|
|
181
|
-
sessionId,
|
|
182
|
-
sliceId,
|
|
183
|
-
state: status,
|
|
184
|
-
updatedAtMs,
|
|
185
|
-
piMessages: Array.isArray(parsed.piMessages) ? parsed.piMessages : [],
|
|
186
|
-
...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
|
|
187
|
-
...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {}
|
|
188
|
-
};
|
|
189
|
-
} catch {
|
|
190
|
-
return void 0;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function getStateAdapter() {
|
|
194
|
-
if (!_stateAdapter) {
|
|
195
|
-
_stateAdapter = createStateAdapter();
|
|
196
|
-
}
|
|
197
|
-
return _stateAdapter;
|
|
198
|
-
}
|
|
199
|
-
async function claimQueueIngressDedup(rawKey, ttlMs) {
|
|
200
|
-
await getStateAdapter().connect();
|
|
201
|
-
const key = `${QUEUE_INGRESS_DEDUP_PREFIX}:${rawKey}`;
|
|
202
|
-
const result = await getRedisStateAdapter().getClient().set(key, "1", {
|
|
203
|
-
NX: true,
|
|
204
|
-
PX: ttlMs
|
|
205
|
-
});
|
|
206
|
-
return result === "OK";
|
|
207
|
-
}
|
|
208
|
-
async function hasQueueIngressDedup(rawKey) {
|
|
209
|
-
await getStateAdapter().connect();
|
|
210
|
-
const key = `${QUEUE_INGRESS_DEDUP_PREFIX}:${rawKey}`;
|
|
211
|
-
const value = await getRedisStateAdapter().getClient().get(key);
|
|
212
|
-
return typeof value === "string" && value.length > 0;
|
|
213
|
-
}
|
|
214
|
-
async function getQueueMessageProcessingState(rawKey) {
|
|
215
|
-
await getStateAdapter().connect();
|
|
216
|
-
const state = await getStateAdapter().get(queueMessageKey(rawKey));
|
|
217
|
-
return parseQueueMessageState(state);
|
|
218
|
-
}
|
|
219
|
-
async function acquireQueueMessageProcessingOwnership(args) {
|
|
220
|
-
await getStateAdapter().connect();
|
|
221
|
-
const key = queueMessageKey(args.rawKey);
|
|
222
|
-
const nowMs = Date.now();
|
|
223
|
-
const payload = JSON.stringify({
|
|
224
|
-
status: "processing",
|
|
225
|
-
updatedAtMs: nowMs,
|
|
226
|
-
ownerToken: args.ownerToken,
|
|
227
|
-
...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
|
|
228
|
-
});
|
|
229
|
-
const result = await getRedisStateAdapter().getClient().eval(CLAIM_OR_RECLAIM_PROCESSING_SCRIPT, {
|
|
230
|
-
keys: [key],
|
|
231
|
-
arguments: [String(nowMs), String(QUEUE_MESSAGE_PROCESSING_TTL_MS), payload]
|
|
232
|
-
});
|
|
233
|
-
if (result === 1) {
|
|
234
|
-
return "acquired";
|
|
235
|
-
}
|
|
236
|
-
if (result === 2) {
|
|
237
|
-
return "reclaimed";
|
|
238
|
-
}
|
|
239
|
-
if (result === 3) {
|
|
240
|
-
return "recovered";
|
|
241
|
-
}
|
|
242
|
-
return "blocked";
|
|
243
|
-
}
|
|
244
|
-
async function refreshQueueMessageProcessingOwnership(args) {
|
|
245
|
-
await getStateAdapter().connect();
|
|
246
|
-
const nowMs = Date.now();
|
|
247
|
-
const payload = JSON.stringify({
|
|
248
|
-
status: "processing",
|
|
249
|
-
updatedAtMs: nowMs,
|
|
250
|
-
ownerToken: args.ownerToken,
|
|
251
|
-
...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
|
|
252
|
-
});
|
|
253
|
-
const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
|
|
254
|
-
keys: [queueMessageKey(args.rawKey)],
|
|
255
|
-
arguments: [args.ownerToken, String(QUEUE_MESSAGE_PROCESSING_TTL_MS), payload]
|
|
256
|
-
});
|
|
257
|
-
return result === 1;
|
|
258
|
-
}
|
|
259
|
-
async function completeQueueMessageProcessingOwnership(args) {
|
|
260
|
-
await getStateAdapter().connect();
|
|
261
|
-
const payload = JSON.stringify({
|
|
262
|
-
status: "completed",
|
|
263
|
-
updatedAtMs: Date.now(),
|
|
264
|
-
ownerToken: args.ownerToken,
|
|
265
|
-
...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
|
|
266
|
-
});
|
|
267
|
-
const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
|
|
268
|
-
keys: [queueMessageKey(args.rawKey)],
|
|
269
|
-
arguments: [args.ownerToken, String(QUEUE_MESSAGE_COMPLETED_TTL_MS), payload]
|
|
270
|
-
});
|
|
271
|
-
return result === 1;
|
|
272
|
-
}
|
|
273
|
-
async function failQueueMessageProcessingOwnership(args) {
|
|
274
|
-
await getStateAdapter().connect();
|
|
275
|
-
const payload = JSON.stringify({
|
|
276
|
-
status: "failed",
|
|
277
|
-
updatedAtMs: Date.now(),
|
|
278
|
-
ownerToken: args.ownerToken,
|
|
279
|
-
errorMessage: args.errorMessage,
|
|
280
|
-
...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
|
|
281
|
-
});
|
|
282
|
-
const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
|
|
283
|
-
keys: [queueMessageKey(args.rawKey)],
|
|
284
|
-
arguments: [args.ownerToken, String(QUEUE_MESSAGE_FAILED_TTL_MS), payload]
|
|
285
|
-
});
|
|
286
|
-
return result === 1;
|
|
287
|
-
}
|
|
288
|
-
async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
|
|
289
|
-
await getStateAdapter().connect();
|
|
290
|
-
const value = await getStateAdapter().get(agentTurnSessionKey(conversationId, sessionId));
|
|
291
|
-
return parseAgentTurnSessionCheckpoint(value);
|
|
292
|
-
}
|
|
293
|
-
async function upsertAgentTurnSessionCheckpoint(args) {
|
|
294
|
-
await getStateAdapter().connect();
|
|
295
|
-
const existing = await getAgentTurnSessionCheckpoint(args.conversationId, args.sessionId);
|
|
296
|
-
const checkpoint = {
|
|
297
|
-
checkpointVersion: (existing?.checkpointVersion ?? 0) + 1,
|
|
298
|
-
conversationId: args.conversationId,
|
|
299
|
-
sessionId: args.sessionId,
|
|
300
|
-
sliceId: args.sliceId,
|
|
301
|
-
state: args.state,
|
|
302
|
-
updatedAtMs: Date.now(),
|
|
303
|
-
piMessages: Array.isArray(args.piMessages) ? args.piMessages : [],
|
|
304
|
-
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
305
|
-
...typeof args.resumedFromSliceId === "number" ? { resumedFromSliceId: args.resumedFromSliceId } : {}
|
|
306
|
-
};
|
|
307
|
-
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
308
|
-
await getStateAdapter().set(agentTurnSessionKey(args.conversationId, args.sessionId), JSON.stringify(checkpoint), ttlMs);
|
|
309
|
-
return checkpoint;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export {
|
|
313
|
-
getStateAdapter,
|
|
314
|
-
claimQueueIngressDedup,
|
|
315
|
-
hasQueueIngressDedup,
|
|
316
|
-
getQueueMessageProcessingState,
|
|
317
|
-
acquireQueueMessageProcessingOwnership,
|
|
318
|
-
refreshQueueMessageProcessingOwnership,
|
|
319
|
-
completeQueueMessageProcessingOwnership,
|
|
320
|
-
failQueueMessageProcessingOwnership,
|
|
321
|
-
getAgentTurnSessionCheckpoint,
|
|
322
|
-
upsertAgentTurnSessionCheckpoint
|
|
323
|
-
};
|