@sentry/junior 0.136.1 → 0.138.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-2KDSPPO4.js → agent-hooks-SCR74Q3E.js} +11 -9
- package/dist/api/conversations/projection.d.ts +1 -0
- package/dist/api/conversations/stats.d.ts +1 -0
- package/dist/api/schema/conversation.d.ts +2 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +187 -46
- package/dist/app.d.ts +8 -1
- package/dist/app.js +519 -860
- package/dist/{catalog-runtime-WSZQGC4D.js → catalog-runtime-IU3QF4GG.js} +5 -5
- package/dist/chat/agent/request.d.ts +32 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/agent-invocations/errors.d.ts +8 -0
- package/dist/chat/agent-invocations/spawn.d.ts +12 -0
- package/dist/chat/agent-invocations/store.d.ts +44 -0
- package/dist/chat/agent-invocations/types.d.ts +449 -0
- package/dist/chat/agent-invocations/work.d.ts +31 -0
- package/dist/chat/config.d.ts +1 -0
- package/dist/chat/configuration/defaults.d.ts +1 -1
- package/dist/chat/conversations/sql/store.d.ts +8 -0
- package/dist/chat/conversations/store.d.ts +9 -1
- package/dist/chat/experimental.d.ts +16 -0
- package/dist/chat/pi/client.d.ts +1 -8
- package/dist/chat/pi/gateway-auth.d.ts +16 -0
- package/dist/chat/runtime/agent-runner.d.ts +2 -1
- package/dist/chat/sandbox/docker.d.ts +17 -0
- package/dist/chat/services/agent-continue.d.ts +2 -0
- package/dist/chat/services/auth-pause.d.ts +0 -1
- package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/subscribed-reply-router-policy.d.ts +12 -0
- package/dist/chat/services/turn-session-routing.d.ts +17 -0
- package/dist/chat/slack/message.d.ts +6 -1
- package/dist/chat/slack/source-link.d.ts +8 -2
- package/dist/chat/slack/team-domain.d.ts +4 -0
- package/dist/chat/source.d.ts +5 -5
- package/dist/chat/state/turn-session.d.ts +0 -2
- package/dist/chat/task-execution/state.d.ts +23 -11
- package/dist/chat/task-execution/store.d.ts +20 -11
- package/dist/chat/task-execution/worker.d.ts +2 -2
- package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
- package/dist/chat/tools/types.d.ts +2 -0
- package/dist/chunk-3Y47VMHV.js +6 -0
- package/dist/chunk-5IDFPCON.js +46 -0
- package/dist/chunk-5LTJ4ITU.js +1523 -0
- package/dist/{chunk-KLBYCSLJ.js → chunk-5MXWHTCK.js} +17 -434
- package/dist/{chunk-CZZDCH3Q.js → chunk-A7WCHXZV.js} +206 -78
- package/dist/{chunk-4YQ6I6VF.js → chunk-BOLJOV5T.js} +2 -56
- package/dist/{chunk-XCZ6COFO.js → chunk-BYW4B4PR.js} +90 -54
- package/dist/{chunk-JQRTXJHD.js → chunk-C7ZVPO4Z.js} +2 -1
- package/dist/{chunk-MW2QRPGW.js → chunk-DCLLQGS6.js} +119 -50
- package/dist/{chunk-CDVCCOMF.js → chunk-E5UYAIKJ.js} +75 -1475
- package/dist/{chunk-3SUK3UXX.js → chunk-ELKW5FIB.js} +5 -5
- package/dist/chunk-EM225ITJ.js +56 -0
- package/dist/{chunk-Y6JNHTRD.js → chunk-FEUNPTGV.js} +34 -70
- package/dist/{chunk-TYNMSQKA.js → chunk-FNLBTB4K.js} +1 -1
- package/dist/{chunk-TRZSM67C.js → chunk-HCIVH3JS.js} +1 -0
- package/dist/chunk-HIWJWWMT.js +668 -0
- package/dist/chunk-IUFM5JWV.js +24 -0
- package/dist/{chunk-XWPA5KZF.js → chunk-JNNJZURV.js} +1 -1
- package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
- package/dist/{chunk-BG3KWWDJ.js → chunk-KDCHVT25.js} +1 -1
- package/dist/{chunk-VH6MI4L7.js → chunk-KFRHRSCJ.js} +1 -1
- package/dist/chunk-LPVUNO2W.js +320 -0
- package/dist/{chunk-G66RYKRS.js → chunk-N7WARMC6.js} +16 -776
- package/dist/chunk-PB3CXNVL.js +41 -0
- package/dist/chunk-Q6QZTTLS.js +55 -0
- package/dist/{chunk-3QNJOHKG.js → chunk-QS527AV5.js} +1 -1
- package/dist/chunk-R776HKPK.js +81 -0
- package/dist/{chunk-OTJMQYVP.js → chunk-RZIBSCYC.js} +517 -608
- package/dist/chunk-SD3OFRWY.js +714 -0
- package/dist/{chunk-HIRIWTWD.js → chunk-SIPKWSXK.js} +58 -10
- package/dist/chunk-TCS76X5D.js +441 -0
- package/dist/{chunk-UX2TNH5D.js → chunk-TDJCLNDH.js} +3 -3
- package/dist/{chunk-NVVBEJUB.js → chunk-TUUDXEXU.js} +336 -300
- package/dist/{chunk-22Z6MGMK.js → chunk-VPZFVJND.js} +1 -1
- package/dist/{chunk-RFDD3Z2I.js → chunk-YU5QUF67.js} +1 -1
- package/dist/chunk-ZBSMCXKV.js +710 -0
- package/dist/{chunk-FSYXYBWU.js → chunk-ZXNTJJIE.js} +2 -2
- package/dist/cli/chat.js +65 -26
- package/dist/cli/check.js +7 -7
- package/dist/cli/plugins.js +14 -12
- package/dist/cli/snapshot-create.js +10 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/conversation-work-KATFHLG3.js +56 -0
- package/dist/db/schema/agent-invocations.d.ts +544 -0
- package/dist/db/schema.d.ts +543 -1
- package/dist/{db-Z2NNL2JC.js → db-UO2IWXGA.js} +7 -7
- package/dist/experimental-HH6OM6UH.js +13 -0
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -3
- package/dist/{oauth-callback-server-PO3M5JMG.js → oauth-callback-server-MAQI43LX.js} +26 -18
- package/dist/{runner-QHQYZYJA.js → runner-PIUQEXKE.js} +26 -14
- package/dist/{skills-KXMT3DRA.js → skills-4ARJADHQ.js} +6 -6
- package/dist/spawn-G2AWPMCT.js +38 -0
- package/dist/{validation-Y42ULTT6.js → validation-S3EE3HWL.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/work-UARRUAW3.js +46 -0
- package/dist/worker-DOCIRVD3.js +24 -0
- package/migrations/0021_first_warhawk.sql +35 -0
- package/migrations/meta/0021_snapshot.json +2176 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +6 -5
|
@@ -2,7 +2,10 @@ import {
|
|
|
2
2
|
relayLocalOAuthCallback
|
|
3
3
|
} from "./chunk-FA6POGLH.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
ensureConversationWake,
|
|
6
|
+
requestConversationWork
|
|
7
|
+
} from "./chunk-5LTJ4ITU.js";
|
|
8
|
+
import {
|
|
6
9
|
buildDeliveredTurnStatePatch,
|
|
7
10
|
finalizeFailedTurnReplyWithEvent,
|
|
8
11
|
getInterruptionMarker,
|
|
@@ -12,37 +15,44 @@ import {
|
|
|
12
15
|
getTurnUserSlackMessageTs,
|
|
13
16
|
persistAuthPauseTurnState,
|
|
14
17
|
requireTurnFailureEventId,
|
|
18
|
+
resolveTurnSessionRouting,
|
|
15
19
|
scheduleSessionCompletedPluginTasks
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FEUNPTGV.js";
|
|
21
|
+
import {
|
|
22
|
+
ConversationTurnLifecycleService
|
|
23
|
+
} from "./chunk-Q6QZTTLS.js";
|
|
17
24
|
import {
|
|
18
25
|
buildSentryConversationUrl
|
|
19
26
|
} from "./chunk-4CODA7P5.js";
|
|
20
27
|
import {
|
|
21
28
|
getVercelConversationWorkQueue
|
|
22
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-KCBZSJRU.js";
|
|
23
30
|
import {
|
|
24
|
-
RetryableDeliveryError,
|
|
25
|
-
addReactionToMessage,
|
|
26
31
|
createUserTokenStore,
|
|
27
32
|
deleteMcpAuthSession,
|
|
28
|
-
escapeSlackMrkdwnText,
|
|
29
33
|
finalizeMcpAuthorization,
|
|
30
|
-
formatProviderLabel,
|
|
31
|
-
formatSlackLink,
|
|
32
34
|
getMcpAuthSession,
|
|
33
35
|
getMcpProviderErrorAttributes,
|
|
34
36
|
getMcpStoredOAuthCredentials,
|
|
37
|
+
resolvePluginOAuthAccount
|
|
38
|
+
} from "./chunk-N7WARMC6.js";
|
|
39
|
+
import {
|
|
40
|
+
RetryableDeliveryError
|
|
41
|
+
} from "./chunk-R776HKPK.js";
|
|
42
|
+
import {
|
|
43
|
+
addReactionToMessage,
|
|
44
|
+
escapeSlackMrkdwnText,
|
|
45
|
+
formatProviderLabel,
|
|
46
|
+
formatSlackLink,
|
|
35
47
|
normalizeSlackReplyMarkdown,
|
|
36
48
|
normalizeSlackStatusText,
|
|
37
49
|
parseOAuthStatePayload,
|
|
38
50
|
postSlackMessage,
|
|
39
51
|
removeReactionFromMessage,
|
|
40
52
|
resolveBaseUrl,
|
|
41
|
-
resolvePluginOAuthAccount,
|
|
42
53
|
truncateStatusText
|
|
43
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-SD3OFRWY.js";
|
|
44
55
|
import {
|
|
45
|
-
JUNIOR_THREAD_STATE_TTL_MS,
|
|
46
56
|
TurnSliceLimitExceededError,
|
|
47
57
|
abandonAgentTurnSessionRecord,
|
|
48
58
|
buildConversationContext,
|
|
@@ -72,34 +82,32 @@ import {
|
|
|
72
82
|
recordDeliveredAssistantMessage,
|
|
73
83
|
turnHasReply,
|
|
74
84
|
updateConversationStats
|
|
75
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-TUUDXEXU.js";
|
|
76
86
|
import {
|
|
77
87
|
parseSlackThreadId,
|
|
78
88
|
readSlackRawMessageContext,
|
|
79
89
|
resolveSlackChannelIdFromMessage,
|
|
80
90
|
resolveSlackChannelIdFromThreadId
|
|
81
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-FNLBTB4K.js";
|
|
82
92
|
import {
|
|
83
|
-
SlackActionError,
|
|
84
93
|
getDashboardConversationLink,
|
|
94
|
+
getPluginSlackConversationLink
|
|
95
|
+
} from "./chunk-5MXWHTCK.js";
|
|
96
|
+
import {
|
|
97
|
+
SlackActionError,
|
|
85
98
|
getHeaderString,
|
|
86
|
-
getPluginSlackConversationLink,
|
|
87
99
|
getSlackClient,
|
|
88
100
|
isDmChannel,
|
|
89
101
|
normalizeSlackConversationId
|
|
90
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-TCS76X5D.js";
|
|
91
103
|
import {
|
|
92
104
|
getConversationEventStore,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
requireSlackDestination,
|
|
96
|
-
sameDestination
|
|
97
|
-
} from "./chunk-HIRIWTWD.js";
|
|
105
|
+
requireSlackDestination
|
|
106
|
+
} from "./chunk-SIPKWSXK.js";
|
|
98
107
|
import {
|
|
99
108
|
acquireActiveLock,
|
|
100
|
-
getDefaultRedisStateAdapterFor,
|
|
101
109
|
getStateAdapter
|
|
102
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-BOLJOV5T.js";
|
|
103
111
|
import {
|
|
104
112
|
FUNCTION_TIMEOUT_BUFFER_SECONDS,
|
|
105
113
|
botConfig,
|
|
@@ -108,37 +116,34 @@ import {
|
|
|
108
116
|
getRuntimeMetadata,
|
|
109
117
|
normalizeSlackEmojiName,
|
|
110
118
|
standardModelId
|
|
111
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-BYW4B4PR.js";
|
|
112
120
|
import {
|
|
113
121
|
discoverSkills
|
|
114
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-ZXNTJJIE.js";
|
|
115
123
|
import {
|
|
116
124
|
pluginCatalogRuntime
|
|
117
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-QS527AV5.js";
|
|
118
126
|
import {
|
|
119
127
|
buildOAuthTokenRequest,
|
|
120
128
|
hasRequiredOAuthScope,
|
|
121
129
|
parseOAuthTokenResponse
|
|
122
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-KFRHRSCJ.js";
|
|
123
131
|
import {
|
|
124
132
|
createSlackResumeActor,
|
|
125
|
-
isUserActor
|
|
126
|
-
parseStoredSlackActor
|
|
133
|
+
isUserActor
|
|
127
134
|
} from "./chunk-HZF5P5UN.js";
|
|
128
135
|
import {
|
|
129
136
|
parseSlackMessageTs
|
|
130
137
|
} from "./chunk-F3B2YJ76.js";
|
|
131
138
|
import {
|
|
132
139
|
buildTurnFailureResponse,
|
|
133
|
-
isRecord,
|
|
134
140
|
logException,
|
|
135
141
|
logInfo,
|
|
136
142
|
logWarn,
|
|
137
143
|
setTags,
|
|
138
|
-
toOptionalNumber,
|
|
139
144
|
toOptionalString,
|
|
140
145
|
withLogContext
|
|
141
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-JNNJZURV.js";
|
|
142
147
|
import {
|
|
143
148
|
homeDir
|
|
144
149
|
} from "./chunk-H4HBKISO.js";
|
|
@@ -203,7 +208,7 @@ var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
|
203
208
|
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
204
209
|
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
205
210
|
function createAssistantStatusScheduler(args) {
|
|
206
|
-
const
|
|
211
|
+
const now = args.now ?? (() => Date.now());
|
|
207
212
|
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
208
213
|
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
209
214
|
const random = args.random ?? Math.random;
|
|
@@ -267,7 +272,7 @@ function createAssistantStatusScheduler(args) {
|
|
|
267
272
|
}
|
|
268
273
|
currentVisibleStatus = text;
|
|
269
274
|
currentLoadingMessages = nextLoadingMessages;
|
|
270
|
-
lastStatusAt =
|
|
275
|
+
lastStatusAt = now();
|
|
271
276
|
scheduleRotation();
|
|
272
277
|
await enqueueStatusUpdate(async () => {
|
|
273
278
|
await args.sendStatus(text, nextLoadingMessages);
|
|
@@ -354,7 +359,7 @@ function createAssistantStatusScheduler(args) {
|
|
|
354
359
|
}
|
|
355
360
|
return;
|
|
356
361
|
}
|
|
357
|
-
const elapsed =
|
|
362
|
+
const elapsed = now() - lastStatusAt;
|
|
358
363
|
const waitMs = Math.max(
|
|
359
364
|
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
360
365
|
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
@@ -1739,1410 +1744,6 @@ async function resumeSlackTurnInContext(args) {
|
|
|
1739
1744
|
return true;
|
|
1740
1745
|
}
|
|
1741
1746
|
|
|
1742
|
-
// src/chat/task-execution/state.ts
|
|
1743
|
-
import { randomUUID } from "crypto";
|
|
1744
|
-
import { destinationSchema } from "@sentry/junior-plugin-api";
|
|
1745
|
-
import { z } from "zod";
|
|
1746
|
-
var CONVERSATION_PREFIX = "junior:conversation";
|
|
1747
|
-
var CONVERSATION_SCHEMA_VERSION = 2;
|
|
1748
|
-
var CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH = 1e4;
|
|
1749
|
-
var CONVERSATION_INDEX_LOCK_TTL_MS = 1e4;
|
|
1750
|
-
var CONVERSATION_INDEX_LOCK_WAIT_MS = 2e3;
|
|
1751
|
-
var CONVERSATION_INDEX_LOCK_RETRY_MS = 25;
|
|
1752
|
-
var CONVERSATION_MUTATION_LOCK_TTL_MS = 1e4;
|
|
1753
|
-
var CONVERSATION_MUTATION_WAIT_MS = 1e4;
|
|
1754
|
-
var CONVERSATION_MUTATION_RETRY_MS = 25;
|
|
1755
|
-
var InvalidConversationRecordError = class extends Error {
|
|
1756
|
-
constructor(conversationId) {
|
|
1757
|
-
super(`Conversation record is invalid for ${conversationId}`);
|
|
1758
|
-
this.name = "InvalidConversationRecordError";
|
|
1759
|
-
}
|
|
1760
|
-
};
|
|
1761
|
-
function isInvalidConversationRecordError(error) {
|
|
1762
|
-
return error instanceof InvalidConversationRecordError;
|
|
1763
|
-
}
|
|
1764
|
-
var ConversationMutationFencedError = class extends Error {
|
|
1765
|
-
constructor(conversationId) {
|
|
1766
|
-
super(
|
|
1767
|
-
`Conversation mutation lock was lost before write for ${conversationId}`
|
|
1768
|
-
);
|
|
1769
|
-
this.name = "ConversationMutationFencedError";
|
|
1770
|
-
}
|
|
1771
|
-
};
|
|
1772
|
-
var CONVERSATION_BY_ACTIVITY_INDEX_KEY = `${CONVERSATION_PREFIX}:by-activity`;
|
|
1773
|
-
var CONVERSATION_ACTIVE_INDEX_KEY = `${CONVERSATION_PREFIX}:active`;
|
|
1774
|
-
var CONVERSATION_WORK_LEASE_TTL_MS = 9e4;
|
|
1775
|
-
var CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15e3;
|
|
1776
|
-
var CONVERSATION_WORK_STALE_ENQUEUE_MS = 6e4;
|
|
1777
|
-
var CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
|
|
1778
|
-
var inboundMessageSourceSchema = z.enum([
|
|
1779
|
-
"api",
|
|
1780
|
-
"internal",
|
|
1781
|
-
"local",
|
|
1782
|
-
"plugin",
|
|
1783
|
-
"resource_event",
|
|
1784
|
-
"scheduler",
|
|
1785
|
-
"slack"
|
|
1786
|
-
]);
|
|
1787
|
-
var agentInputSchema = z.object({
|
|
1788
|
-
attachments: z.array(z.unknown()).optional(),
|
|
1789
|
-
authorId: z.string().min(1).optional(),
|
|
1790
|
-
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
1791
|
-
text: z.string()
|
|
1792
|
-
}).strict().refine((input) => input.text.trim() || input.attachments?.length, {
|
|
1793
|
-
message: "agent input requires text or attachments"
|
|
1794
|
-
});
|
|
1795
|
-
var inboundMessageDeliverySchema = z.enum(["defer", "interrupt"]);
|
|
1796
|
-
var inboundMessageSchema = z.object({
|
|
1797
|
-
attemptCount: z.number().finite().optional(),
|
|
1798
|
-
conversationId: z.string().refine((value) => value.trim().length > 0),
|
|
1799
|
-
createdAtMs: z.number().finite(),
|
|
1800
|
-
delivery: inboundMessageDeliverySchema,
|
|
1801
|
-
destination: destinationSchema,
|
|
1802
|
-
inboundMessageId: z.string().refine((value) => value.trim().length > 0),
|
|
1803
|
-
injectedAtMs: z.number().finite().optional(),
|
|
1804
|
-
input: agentInputSchema,
|
|
1805
|
-
receivedAtMs: z.number().finite(),
|
|
1806
|
-
source: inboundMessageSourceSchema
|
|
1807
|
-
}).strict();
|
|
1808
|
-
function conversationKey(conversationId) {
|
|
1809
|
-
return `${CONVERSATION_PREFIX}:${conversationId}`;
|
|
1810
|
-
}
|
|
1811
|
-
function indexLockKey(indexKey) {
|
|
1812
|
-
return `${indexKey}:lock`;
|
|
1813
|
-
}
|
|
1814
|
-
function mutationLockKey(conversationId) {
|
|
1815
|
-
return `${CONVERSATION_PREFIX}:mutation:${conversationId}`;
|
|
1816
|
-
}
|
|
1817
|
-
function now() {
|
|
1818
|
-
return Date.now();
|
|
1819
|
-
}
|
|
1820
|
-
function compareMessages(left, right) {
|
|
1821
|
-
return left.createdAtMs - right.createdAtMs || left.receivedAtMs - right.receivedAtMs || left.inboundMessageId.localeCompare(right.inboundMessageId);
|
|
1822
|
-
}
|
|
1823
|
-
function inputHasAttachments(input) {
|
|
1824
|
-
return Array.isArray(input.attachments) && input.attachments.length > 0;
|
|
1825
|
-
}
|
|
1826
|
-
function upgradedPendingMessage(stored, duplicate) {
|
|
1827
|
-
const promotesInterrupt = stored.delivery === "defer" && duplicate.delivery === "interrupt";
|
|
1828
|
-
const delivery = promotesInterrupt ? "interrupt" : stored.delivery;
|
|
1829
|
-
const input = promotesInterrupt ? {
|
|
1830
|
-
...duplicate.input,
|
|
1831
|
-
...inputHasAttachments(duplicate.input) || !inputHasAttachments(stored.input) ? {} : { attachments: stored.input.attachments }
|
|
1832
|
-
} : inputHasAttachments(duplicate.input) && !inputHasAttachments(stored.input) ? duplicate.input : stored.input;
|
|
1833
|
-
if (input === stored.input && delivery === stored.delivery) {
|
|
1834
|
-
return stored;
|
|
1835
|
-
}
|
|
1836
|
-
return { ...stored, delivery, input };
|
|
1837
|
-
}
|
|
1838
|
-
function compareIndexDescending(left, right) {
|
|
1839
|
-
return right.score - left.score || right.conversationId.localeCompare(left.conversationId);
|
|
1840
|
-
}
|
|
1841
|
-
function compareIndexAscending(left, right) {
|
|
1842
|
-
return left.score - right.score || left.conversationId.localeCompare(right.conversationId);
|
|
1843
|
-
}
|
|
1844
|
-
function uniqueStrings(values) {
|
|
1845
|
-
return [...new Set(values)];
|
|
1846
|
-
}
|
|
1847
|
-
function normalizeSource(value) {
|
|
1848
|
-
const parsed = inboundMessageSourceSchema.safeParse(value);
|
|
1849
|
-
return parsed.success ? parsed.data : void 0;
|
|
1850
|
-
}
|
|
1851
|
-
function normalizeExecutionStatus(value) {
|
|
1852
|
-
if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
|
|
1853
|
-
return value;
|
|
1854
|
-
}
|
|
1855
|
-
return void 0;
|
|
1856
|
-
}
|
|
1857
|
-
function normalizeMessage(value, schemaVersion) {
|
|
1858
|
-
const candidate = schemaVersion === 1 && isRecord(value) && value.delivery === void 0 ? {
|
|
1859
|
-
...value,
|
|
1860
|
-
delivery: "defer"
|
|
1861
|
-
} : value;
|
|
1862
|
-
const parsed = inboundMessageSchema.safeParse(candidate);
|
|
1863
|
-
return parsed.success ? parsed.data : void 0;
|
|
1864
|
-
}
|
|
1865
|
-
function isFinalAttempt(message) {
|
|
1866
|
-
return (message.attemptCount ?? 0) >= CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS - 1;
|
|
1867
|
-
}
|
|
1868
|
-
function normalizeActor(value) {
|
|
1869
|
-
return parseStoredSlackActor(value);
|
|
1870
|
-
}
|
|
1871
|
-
function normalizeLease(value) {
|
|
1872
|
-
if (!isRecord(value)) {
|
|
1873
|
-
return void 0;
|
|
1874
|
-
}
|
|
1875
|
-
const token = toOptionalString(value.token);
|
|
1876
|
-
const acquiredAtMs = toOptionalNumber(value.acquiredAtMs);
|
|
1877
|
-
const lastCheckInAtMs = toOptionalNumber(value.lastCheckInAtMs);
|
|
1878
|
-
const expiresAtMs = toOptionalNumber(value.expiresAtMs);
|
|
1879
|
-
if (!token || typeof acquiredAtMs !== "number" || typeof lastCheckInAtMs !== "number" || typeof expiresAtMs !== "number") {
|
|
1880
|
-
return void 0;
|
|
1881
|
-
}
|
|
1882
|
-
return {
|
|
1883
|
-
token,
|
|
1884
|
-
acquiredAtMs,
|
|
1885
|
-
lastCheckInAtMs,
|
|
1886
|
-
expiresAtMs
|
|
1887
|
-
};
|
|
1888
|
-
}
|
|
1889
|
-
function normalizeExecution(conversationId, value, schemaVersion) {
|
|
1890
|
-
if (!isRecord(value)) {
|
|
1891
|
-
return void 0;
|
|
1892
|
-
}
|
|
1893
|
-
const status = normalizeExecutionStatus(value.status);
|
|
1894
|
-
if (!status) {
|
|
1895
|
-
return void 0;
|
|
1896
|
-
}
|
|
1897
|
-
const pendingMessages2 = [];
|
|
1898
|
-
if (Array.isArray(value.pendingMessages)) {
|
|
1899
|
-
for (const rawMessage of value.pendingMessages) {
|
|
1900
|
-
const message = normalizeMessage(rawMessage, schemaVersion);
|
|
1901
|
-
if (!message || message.conversationId !== conversationId) {
|
|
1902
|
-
return void 0;
|
|
1903
|
-
}
|
|
1904
|
-
if (message.injectedAtMs !== void 0) {
|
|
1905
|
-
continue;
|
|
1906
|
-
}
|
|
1907
|
-
pendingMessages2.push(message);
|
|
1908
|
-
}
|
|
1909
|
-
pendingMessages2.sort(compareMessages);
|
|
1910
|
-
}
|
|
1911
|
-
const inboundMessageIds = Array.isArray(value.inboundMessageIds) ? uniqueStrings(
|
|
1912
|
-
value.inboundMessageIds.map((id) => typeof id === "string" ? id : void 0).filter((id) => Boolean(id))
|
|
1913
|
-
) : [];
|
|
1914
|
-
const lease = normalizeLease(value.lease);
|
|
1915
|
-
const normalizedStatus = status === "idle" && lease ? "running" : status === "idle" && pendingMessages2.length > 0 ? "pending" : status;
|
|
1916
|
-
return {
|
|
1917
|
-
status: normalizedStatus,
|
|
1918
|
-
inboundMessageIds: uniqueStrings([
|
|
1919
|
-
...inboundMessageIds,
|
|
1920
|
-
...pendingMessages2.map((message) => message.inboundMessageId)
|
|
1921
|
-
]),
|
|
1922
|
-
pendingCount: pendingMessages2.length,
|
|
1923
|
-
pendingMessages: pendingMessages2,
|
|
1924
|
-
lease,
|
|
1925
|
-
lastCheckpointAtMs: toOptionalNumber(value.lastCheckpointAtMs),
|
|
1926
|
-
lastEnqueuedAtMs: toOptionalNumber(value.lastEnqueuedAtMs),
|
|
1927
|
-
runId: toOptionalString(value.runId),
|
|
1928
|
-
updatedAtMs: toOptionalNumber(value.updatedAtMs)
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
function normalizeConversation(conversationId, value) {
|
|
1932
|
-
if (!isRecord(value) || value.schemaVersion !== 1 && value.schemaVersion !== CONVERSATION_SCHEMA_VERSION) {
|
|
1933
|
-
return void 0;
|
|
1934
|
-
}
|
|
1935
|
-
const schemaVersion = value.schemaVersion;
|
|
1936
|
-
const storedConversationId = toOptionalString(value.conversationId);
|
|
1937
|
-
const createdAtMs = toOptionalNumber(value.createdAtMs);
|
|
1938
|
-
const lastActivityAtMs = toOptionalNumber(value.lastActivityAtMs);
|
|
1939
|
-
const updatedAtMs = toOptionalNumber(value.updatedAtMs);
|
|
1940
|
-
const execution = normalizeExecution(
|
|
1941
|
-
conversationId,
|
|
1942
|
-
value.execution,
|
|
1943
|
-
schemaVersion
|
|
1944
|
-
);
|
|
1945
|
-
const destination = value.destination === void 0 ? void 0 : parseDestination(value.destination);
|
|
1946
|
-
if (storedConversationId !== conversationId || typeof createdAtMs !== "number" || typeof lastActivityAtMs !== "number" || typeof updatedAtMs !== "number" || !execution || value.destination !== void 0 && !destination) {
|
|
1947
|
-
return void 0;
|
|
1948
|
-
}
|
|
1949
|
-
if (execution.pendingMessages.length > 0 && (!destination || execution.pendingMessages.some(
|
|
1950
|
-
(message) => !sameDestination(message.destination, destination)
|
|
1951
|
-
))) {
|
|
1952
|
-
return void 0;
|
|
1953
|
-
}
|
|
1954
|
-
return {
|
|
1955
|
-
schemaVersion: CONVERSATION_SCHEMA_VERSION,
|
|
1956
|
-
conversationId,
|
|
1957
|
-
createdAtMs,
|
|
1958
|
-
lastActivityAtMs,
|
|
1959
|
-
updatedAtMs,
|
|
1960
|
-
execution,
|
|
1961
|
-
...destination ? { destination } : {},
|
|
1962
|
-
...toOptionalString(value.title) ? { title: toOptionalString(value.title) } : {},
|
|
1963
|
-
...toOptionalString(value.channelName) ? { channelName: toOptionalString(value.channelName) } : {},
|
|
1964
|
-
...normalizeActor(value.actor) ? { actor: normalizeActor(value.actor) } : {},
|
|
1965
|
-
...normalizeSource(value.source) ? { source: normalizeSource(value.source) } : {}
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1968
|
-
function emptyConversation(args) {
|
|
1969
|
-
return {
|
|
1970
|
-
schemaVersion: CONVERSATION_SCHEMA_VERSION,
|
|
1971
|
-
conversationId: args.conversationId,
|
|
1972
|
-
createdAtMs: args.nowMs,
|
|
1973
|
-
lastActivityAtMs: args.nowMs,
|
|
1974
|
-
updatedAtMs: args.nowMs,
|
|
1975
|
-
...args.destination ? { destination: args.destination } : {},
|
|
1976
|
-
...args.source ? { source: args.source } : {},
|
|
1977
|
-
execution: {
|
|
1978
|
-
status: "idle",
|
|
1979
|
-
inboundMessageIds: [],
|
|
1980
|
-
pendingCount: 0,
|
|
1981
|
-
pendingMessages: [],
|
|
1982
|
-
updatedAtMs: args.nowMs
|
|
1983
|
-
}
|
|
1984
|
-
};
|
|
1985
|
-
}
|
|
1986
|
-
function isLeaseActive(lease, nowMs) {
|
|
1987
|
-
return Boolean(lease && lease.expiresAtMs > nowMs);
|
|
1988
|
-
}
|
|
1989
|
-
function pendingMessages(conversation) {
|
|
1990
|
-
return [...conversation.execution.pendingMessages].sort(compareMessages);
|
|
1991
|
-
}
|
|
1992
|
-
function isRunnableStatus(status) {
|
|
1993
|
-
return status !== "failed" && status !== "idle";
|
|
1994
|
-
}
|
|
1995
|
-
function hasRunnableWork(conversation) {
|
|
1996
|
-
return isRunnableStatus(conversation.execution.status) || pendingMessages(conversation).length > 0;
|
|
1997
|
-
}
|
|
1998
|
-
function executionWithPendingMessages(execution, pending) {
|
|
1999
|
-
const pendingMessages2 = [...pending].sort(compareMessages);
|
|
2000
|
-
const status = execution.status === "idle" && execution.lease ? "running" : execution.status === "idle" && pendingMessages2.length > 0 ? "pending" : execution.status;
|
|
2001
|
-
return {
|
|
2002
|
-
...execution,
|
|
2003
|
-
status,
|
|
2004
|
-
inboundMessageIds: uniqueStrings([
|
|
2005
|
-
...execution.inboundMessageIds,
|
|
2006
|
-
...pendingMessages2.map((message) => message.inboundMessageId)
|
|
2007
|
-
]),
|
|
2008
|
-
pendingMessages: pendingMessages2,
|
|
2009
|
-
pendingCount: pendingMessages2.length
|
|
2010
|
-
};
|
|
2011
|
-
}
|
|
2012
|
-
function withExecutionUpdate(conversation, execution, nowMs) {
|
|
2013
|
-
return {
|
|
2014
|
-
...conversation,
|
|
2015
|
-
updatedAtMs: nowMs,
|
|
2016
|
-
execution: {
|
|
2017
|
-
...executionWithPendingMessages(execution, execution.pendingMessages),
|
|
2018
|
-
updatedAtMs: nowMs
|
|
2019
|
-
}
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
async function getConnectedState(stateAdapter) {
|
|
2023
|
-
const state = stateAdapter ?? getStateAdapter();
|
|
2024
|
-
await state.connect();
|
|
2025
|
-
return state;
|
|
2026
|
-
}
|
|
2027
|
-
async function sleep(ms) {
|
|
2028
|
-
await new Promise((resolve) => {
|
|
2029
|
-
const timer = setTimeout(resolve, ms);
|
|
2030
|
-
timer.unref?.();
|
|
2031
|
-
});
|
|
2032
|
-
}
|
|
2033
|
-
async function withIndexLock(state, indexKey, callback) {
|
|
2034
|
-
const startedAtMs = now();
|
|
2035
|
-
let lock;
|
|
2036
|
-
while (true) {
|
|
2037
|
-
lock = await state.acquireLock(
|
|
2038
|
-
indexLockKey(indexKey),
|
|
2039
|
-
CONVERSATION_INDEX_LOCK_TTL_MS
|
|
2040
|
-
);
|
|
2041
|
-
if (lock) {
|
|
2042
|
-
break;
|
|
2043
|
-
}
|
|
2044
|
-
if (now() - startedAtMs >= CONVERSATION_INDEX_LOCK_WAIT_MS) {
|
|
2045
|
-
throw new Error(
|
|
2046
|
-
`Could not acquire conversation index lock for ${indexKey}`
|
|
2047
|
-
);
|
|
2048
|
-
}
|
|
2049
|
-
await sleep(CONVERSATION_INDEX_LOCK_RETRY_MS);
|
|
2050
|
-
}
|
|
2051
|
-
try {
|
|
2052
|
-
return await callback();
|
|
2053
|
-
} finally {
|
|
2054
|
-
await state.releaseLock(lock);
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
function normalizeIndexEntry(value) {
|
|
2058
|
-
if (!isRecord(value)) {
|
|
2059
|
-
return void 0;
|
|
2060
|
-
}
|
|
2061
|
-
const conversationId = toOptionalString(value.conversationId);
|
|
2062
|
-
const score = toOptionalNumber(value.score);
|
|
2063
|
-
if (!conversationId || typeof score !== "number") {
|
|
2064
|
-
return void 0;
|
|
2065
|
-
}
|
|
2066
|
-
return { conversationId, score };
|
|
2067
|
-
}
|
|
2068
|
-
function uniqueIndexEntries(value) {
|
|
2069
|
-
if (!Array.isArray(value)) {
|
|
2070
|
-
return [];
|
|
2071
|
-
}
|
|
2072
|
-
const entries = /* @__PURE__ */ new Map();
|
|
2073
|
-
for (const item of value) {
|
|
2074
|
-
const entry = normalizeIndexEntry(item);
|
|
2075
|
-
if (!entry) {
|
|
2076
|
-
continue;
|
|
2077
|
-
}
|
|
2078
|
-
const existing = entries.get(entry.conversationId);
|
|
2079
|
-
if (!existing || entry.score > existing.score) {
|
|
2080
|
-
entries.set(entry.conversationId, entry);
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
return [...entries.values()];
|
|
2084
|
-
}
|
|
2085
|
-
function retainedIndexEntries(indexKey, entries) {
|
|
2086
|
-
if (indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
|
|
2087
|
-
return entries.sort(compareIndexDescending).slice(0, CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH);
|
|
2088
|
-
}
|
|
2089
|
-
if (indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
|
|
2090
|
-
return entries.sort(compareIndexAscending);
|
|
2091
|
-
}
|
|
2092
|
-
throw new Error(`Unknown conversation index ${indexKey}`);
|
|
2093
|
-
}
|
|
2094
|
-
function redisIndexKey(indexKey) {
|
|
2095
|
-
const prefix = getChatConfig().state.keyPrefix;
|
|
2096
|
-
return [...prefix ? [prefix] : [], indexKey].join(":");
|
|
2097
|
-
}
|
|
2098
|
-
function parseRedisIndexEntries(values) {
|
|
2099
|
-
if (!Array.isArray(values)) {
|
|
2100
|
-
return [];
|
|
2101
|
-
}
|
|
2102
|
-
const entries = [];
|
|
2103
|
-
for (let index = 0; index < values.length; index += 2) {
|
|
2104
|
-
const conversationId = toOptionalString(values[index]);
|
|
2105
|
-
const score = typeof values[index + 1] === "number" ? values[index + 1] : Number(values[index + 1]);
|
|
2106
|
-
if (!conversationId || !Number.isFinite(score)) {
|
|
2107
|
-
continue;
|
|
2108
|
-
}
|
|
2109
|
-
entries.push({ conversationId, score });
|
|
2110
|
-
}
|
|
2111
|
-
return entries;
|
|
2112
|
-
}
|
|
2113
|
-
function redisConversationIndexStore(client) {
|
|
2114
|
-
const upsertBoundedActivityScript = `
|
|
2115
|
-
redis.call("ZADD", KEYS[1], ARGV[1], ARGV[2])
|
|
2116
|
-
redis.call("PEXPIRE", KEYS[1], ARGV[3])
|
|
2117
|
-
local extra = redis.call("ZCARD", KEYS[1]) - tonumber(ARGV[4])
|
|
2118
|
-
if extra > 0 then
|
|
2119
|
-
redis.call("ZREMRANGEBYRANK", KEYS[1], 0, extra - 1)
|
|
2120
|
-
end
|
|
2121
|
-
return 1
|
|
2122
|
-
`;
|
|
2123
|
-
return {
|
|
2124
|
-
async list(args) {
|
|
2125
|
-
const key = redisIndexKey(args.indexKey);
|
|
2126
|
-
const limit = args.limit;
|
|
2127
|
-
const offset = Math.max(0, args.offset ?? 0);
|
|
2128
|
-
if (limit === 0) {
|
|
2129
|
-
return [];
|
|
2130
|
-
}
|
|
2131
|
-
const values = args.scoreMax !== void 0 ? await client.sendCommand([
|
|
2132
|
-
"ZRANGEBYSCORE",
|
|
2133
|
-
key,
|
|
2134
|
-
"-inf",
|
|
2135
|
-
String(args.scoreMax),
|
|
2136
|
-
"WITHSCORES",
|
|
2137
|
-
...limit !== void 0 || offset > 0 ? ["LIMIT", String(offset), String(limit ?? 1e9)] : []
|
|
2138
|
-
]) : await client.sendCommand([
|
|
2139
|
-
args.order === "asc" ? "ZRANGE" : "ZREVRANGE",
|
|
2140
|
-
key,
|
|
2141
|
-
String(offset),
|
|
2142
|
-
String(
|
|
2143
|
-
limit === void 0 ? -1 : offset + Math.max(0, limit - 1)
|
|
2144
|
-
),
|
|
2145
|
-
"WITHSCORES"
|
|
2146
|
-
]);
|
|
2147
|
-
return parseRedisIndexEntries(values);
|
|
2148
|
-
},
|
|
2149
|
-
async remove(args) {
|
|
2150
|
-
await client.sendCommand([
|
|
2151
|
-
"ZREM",
|
|
2152
|
-
redisIndexKey(args.indexKey),
|
|
2153
|
-
args.conversationId
|
|
2154
|
-
]);
|
|
2155
|
-
},
|
|
2156
|
-
async upsert(args) {
|
|
2157
|
-
const key = redisIndexKey(args.indexKey);
|
|
2158
|
-
if (args.indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
|
|
2159
|
-
await client.sendCommand([
|
|
2160
|
-
"EVAL",
|
|
2161
|
-
upsertBoundedActivityScript,
|
|
2162
|
-
"1",
|
|
2163
|
-
key,
|
|
2164
|
-
String(args.score),
|
|
2165
|
-
args.conversationId,
|
|
2166
|
-
String(JUNIOR_THREAD_STATE_TTL_MS),
|
|
2167
|
-
String(CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH)
|
|
2168
|
-
]);
|
|
2169
|
-
return;
|
|
2170
|
-
}
|
|
2171
|
-
if (args.indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
|
|
2172
|
-
await client.sendCommand([
|
|
2173
|
-
"ZADD",
|
|
2174
|
-
key,
|
|
2175
|
-
String(args.score),
|
|
2176
|
-
args.conversationId
|
|
2177
|
-
]);
|
|
2178
|
-
await client.sendCommand([
|
|
2179
|
-
"PEXPIRE",
|
|
2180
|
-
key,
|
|
2181
|
-
String(JUNIOR_THREAD_STATE_TTL_MS)
|
|
2182
|
-
]);
|
|
2183
|
-
return;
|
|
2184
|
-
}
|
|
2185
|
-
throw new Error(`Unknown conversation index ${args.indexKey}`);
|
|
2186
|
-
}
|
|
2187
|
-
};
|
|
2188
|
-
}
|
|
2189
|
-
function emulatedConversationIndexStore(state) {
|
|
2190
|
-
const readIndex = async (indexKey) => uniqueIndexEntries(await state.get(indexKey));
|
|
2191
|
-
const writeIndex = async (indexKey, entries) => {
|
|
2192
|
-
await state.set(indexKey, entries, JUNIOR_THREAD_STATE_TTL_MS);
|
|
2193
|
-
};
|
|
2194
|
-
return {
|
|
2195
|
-
async list(args) {
|
|
2196
|
-
const entries = (await readIndex(args.indexKey)).filter(
|
|
2197
|
-
(entry) => args.scoreMax === void 0 ? true : entry.score <= args.scoreMax
|
|
2198
|
-
).sort(
|
|
2199
|
-
args.order === "asc" ? compareIndexAscending : compareIndexDescending
|
|
2200
|
-
);
|
|
2201
|
-
const offset = Math.max(0, args.offset ?? 0);
|
|
2202
|
-
return entries.slice(
|
|
2203
|
-
offset,
|
|
2204
|
-
args.limit === void 0 ? entries.length : offset + args.limit
|
|
2205
|
-
);
|
|
2206
|
-
},
|
|
2207
|
-
async remove(args) {
|
|
2208
|
-
await withIndexLock(state, args.indexKey, async () => {
|
|
2209
|
-
const entries = await readIndex(args.indexKey);
|
|
2210
|
-
const next = entries.filter(
|
|
2211
|
-
(entry) => entry.conversationId !== args.conversationId
|
|
2212
|
-
);
|
|
2213
|
-
if (next.length === entries.length) {
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
await writeIndex(args.indexKey, next);
|
|
2217
|
-
});
|
|
2218
|
-
},
|
|
2219
|
-
async upsert(args) {
|
|
2220
|
-
await withIndexLock(state, args.indexKey, async () => {
|
|
2221
|
-
const entries = await readIndex(args.indexKey);
|
|
2222
|
-
const withoutCurrent = entries.filter(
|
|
2223
|
-
(entry) => entry.conversationId !== args.conversationId
|
|
2224
|
-
);
|
|
2225
|
-
const next = retainedIndexEntries(args.indexKey, [
|
|
2226
|
-
...withoutCurrent,
|
|
2227
|
-
{ conversationId: args.conversationId, score: args.score }
|
|
2228
|
-
]);
|
|
2229
|
-
await writeIndex(args.indexKey, next);
|
|
2230
|
-
});
|
|
2231
|
-
}
|
|
2232
|
-
};
|
|
2233
|
-
}
|
|
2234
|
-
async function getConversationIndexStore(state) {
|
|
2235
|
-
const redisStateAdapter = await getDefaultRedisStateAdapterFor(state);
|
|
2236
|
-
if (redisStateAdapter) {
|
|
2237
|
-
return redisConversationIndexStore(redisStateAdapter.getClient());
|
|
2238
|
-
}
|
|
2239
|
-
return emulatedConversationIndexStore(state);
|
|
2240
|
-
}
|
|
2241
|
-
async function upsertIndexEntry(args) {
|
|
2242
|
-
const index = await getConversationIndexStore(args.state);
|
|
2243
|
-
await index.upsert({
|
|
2244
|
-
conversationId: args.conversationId,
|
|
2245
|
-
indexKey: args.indexKey,
|
|
2246
|
-
score: args.score
|
|
2247
|
-
});
|
|
2248
|
-
}
|
|
2249
|
-
async function removeIndexEntry(args) {
|
|
2250
|
-
const index = await getConversationIndexStore(args.state);
|
|
2251
|
-
await index.remove({
|
|
2252
|
-
conversationId: args.conversationId,
|
|
2253
|
-
indexKey: args.indexKey
|
|
2254
|
-
});
|
|
2255
|
-
}
|
|
2256
|
-
async function acquireMutationLock(state, conversationId) {
|
|
2257
|
-
const startedAtMs = now();
|
|
2258
|
-
while (true) {
|
|
2259
|
-
const lock = await state.acquireLock(
|
|
2260
|
-
mutationLockKey(conversationId),
|
|
2261
|
-
CONVERSATION_MUTATION_LOCK_TTL_MS
|
|
2262
|
-
);
|
|
2263
|
-
if (lock) {
|
|
2264
|
-
return lock;
|
|
2265
|
-
}
|
|
2266
|
-
if (now() - startedAtMs >= CONVERSATION_MUTATION_WAIT_MS) {
|
|
2267
|
-
throw new Error(
|
|
2268
|
-
`Could not acquire conversation mutation lock for ${conversationId}`
|
|
2269
|
-
);
|
|
2270
|
-
}
|
|
2271
|
-
await sleep(CONVERSATION_MUTATION_RETRY_MS);
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
async function withConversationMutation(args, callback) {
|
|
2275
|
-
const state = await getConnectedState(args.state);
|
|
2276
|
-
const lock = await acquireMutationLock(state, args.conversationId);
|
|
2277
|
-
try {
|
|
2278
|
-
return await callback(state, lock);
|
|
2279
|
-
} finally {
|
|
2280
|
-
await state.releaseLock(lock);
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
async function readConversation(state, conversationId) {
|
|
2284
|
-
const raw = await state.get(conversationKey(conversationId));
|
|
2285
|
-
if (raw == null) {
|
|
2286
|
-
return void 0;
|
|
2287
|
-
}
|
|
2288
|
-
const conversation = normalizeConversation(conversationId, raw);
|
|
2289
|
-
if (!conversation) {
|
|
2290
|
-
throw new InvalidConversationRecordError(conversationId);
|
|
2291
|
-
}
|
|
2292
|
-
return conversation;
|
|
2293
|
-
}
|
|
2294
|
-
async function writeConversation(state, lock, conversation) {
|
|
2295
|
-
const execution = executionWithPendingMessages(
|
|
2296
|
-
conversation.execution,
|
|
2297
|
-
conversation.execution.pendingMessages
|
|
2298
|
-
);
|
|
2299
|
-
const next = {
|
|
2300
|
-
...conversation,
|
|
2301
|
-
execution
|
|
2302
|
-
};
|
|
2303
|
-
const fenced = await state.extendLock(
|
|
2304
|
-
lock,
|
|
2305
|
-
CONVERSATION_MUTATION_LOCK_TTL_MS
|
|
2306
|
-
);
|
|
2307
|
-
if (!fenced) {
|
|
2308
|
-
throw new ConversationMutationFencedError(next.conversationId);
|
|
2309
|
-
}
|
|
2310
|
-
await state.set(
|
|
2311
|
-
conversationKey(next.conversationId),
|
|
2312
|
-
next,
|
|
2313
|
-
JUNIOR_THREAD_STATE_TTL_MS
|
|
2314
|
-
);
|
|
2315
|
-
await upsertIndexEntry({
|
|
2316
|
-
state,
|
|
2317
|
-
indexKey: CONVERSATION_BY_ACTIVITY_INDEX_KEY,
|
|
2318
|
-
conversationId: next.conversationId,
|
|
2319
|
-
score: next.lastActivityAtMs
|
|
2320
|
-
});
|
|
2321
|
-
if (!hasRunnableWork(next)) {
|
|
2322
|
-
await removeIndexEntry({
|
|
2323
|
-
state,
|
|
2324
|
-
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2325
|
-
conversationId: next.conversationId
|
|
2326
|
-
});
|
|
2327
|
-
return;
|
|
2328
|
-
}
|
|
2329
|
-
await upsertIndexEntry({
|
|
2330
|
-
state,
|
|
2331
|
-
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2332
|
-
conversationId: next.conversationId,
|
|
2333
|
-
score: next.execution.updatedAtMs ?? next.updatedAtMs
|
|
2334
|
-
});
|
|
2335
|
-
}
|
|
2336
|
-
function assertSameConversationDestination(args) {
|
|
2337
|
-
if (!args.current || sameDestination(args.current, args.next)) {
|
|
2338
|
-
return;
|
|
2339
|
-
}
|
|
2340
|
-
throw new Error(
|
|
2341
|
-
`Conversation destination changed for ${args.conversationId}`
|
|
2342
|
-
);
|
|
2343
|
-
}
|
|
2344
|
-
function conversationWorkState(conversation) {
|
|
2345
|
-
const lease = conversation.execution.lease;
|
|
2346
|
-
return {
|
|
2347
|
-
...conversation,
|
|
2348
|
-
lastEnqueuedAtMs: conversation.execution.lastEnqueuedAtMs,
|
|
2349
|
-
...lease ? {
|
|
2350
|
-
lease: {
|
|
2351
|
-
acquiredAtMs: lease.acquiredAtMs,
|
|
2352
|
-
lastCheckInAtMs: lease.lastCheckInAtMs,
|
|
2353
|
-
leaseExpiresAtMs: lease.expiresAtMs,
|
|
2354
|
-
leaseToken: lease.token
|
|
2355
|
-
}
|
|
2356
|
-
} : {},
|
|
2357
|
-
messages: pendingMessages(conversation),
|
|
2358
|
-
needsRun: hasRunnableWork(conversation)
|
|
2359
|
-
};
|
|
2360
|
-
}
|
|
2361
|
-
async function getConversation(args) {
|
|
2362
|
-
const state = await getConnectedState(args.state);
|
|
2363
|
-
return await readConversation(state, args.conversationId);
|
|
2364
|
-
}
|
|
2365
|
-
async function getConversationWorkState(args) {
|
|
2366
|
-
const conversation = await getConversation(args);
|
|
2367
|
-
return conversation ? conversationWorkState(conversation) : void 0;
|
|
2368
|
-
}
|
|
2369
|
-
async function appendInboundMessage(args) {
|
|
2370
|
-
const nowMs = args.nowMs ?? now();
|
|
2371
|
-
return await withConversationMutation(
|
|
2372
|
-
{ conversationId: args.message.conversationId, state: args.state },
|
|
2373
|
-
async (state, lock) => {
|
|
2374
|
-
const current = await readConversation(state, args.message.conversationId) ?? emptyConversation({
|
|
2375
|
-
conversationId: args.message.conversationId,
|
|
2376
|
-
destination: args.message.destination,
|
|
2377
|
-
nowMs,
|
|
2378
|
-
source: args.message.source
|
|
2379
|
-
});
|
|
2380
|
-
assertSameConversationDestination({
|
|
2381
|
-
conversationId: args.message.conversationId,
|
|
2382
|
-
current: current.destination,
|
|
2383
|
-
next: args.message.destination
|
|
2384
|
-
});
|
|
2385
|
-
const existingPending = current.execution.pendingMessages.some(
|
|
2386
|
-
(message) => message.inboundMessageId === args.message.inboundMessageId
|
|
2387
|
-
);
|
|
2388
|
-
const existing = current.execution.inboundMessageIds.includes(
|
|
2389
|
-
args.message.inboundMessageId
|
|
2390
|
-
);
|
|
2391
|
-
if (existing) {
|
|
2392
|
-
if (!existingPending) {
|
|
2393
|
-
return { status: "duplicate" };
|
|
2394
|
-
}
|
|
2395
|
-
const nextStatus = current.execution.status === "idle" ? "pending" : current.execution.status;
|
|
2396
|
-
await writeConversation(
|
|
2397
|
-
state,
|
|
2398
|
-
lock,
|
|
2399
|
-
withExecutionUpdate(
|
|
2400
|
-
current,
|
|
2401
|
-
{
|
|
2402
|
-
...current.execution,
|
|
2403
|
-
status: nextStatus,
|
|
2404
|
-
pendingMessages: current.execution.pendingMessages.map(
|
|
2405
|
-
(message) => message.inboundMessageId === args.message.inboundMessageId ? upgradedPendingMessage(message, args.message) : message
|
|
2406
|
-
)
|
|
2407
|
-
},
|
|
2408
|
-
nowMs
|
|
2409
|
-
)
|
|
2410
|
-
);
|
|
2411
|
-
return { status: "duplicate" };
|
|
2412
|
-
}
|
|
2413
|
-
const status = current.execution.lease && current.execution.status === "running" ? "running" : current.execution.lease ? "awaiting_resume" : "pending";
|
|
2414
|
-
const next = {
|
|
2415
|
-
...current,
|
|
2416
|
-
destination: current.destination ?? args.message.destination,
|
|
2417
|
-
source: current.source ?? args.message.source,
|
|
2418
|
-
lastActivityAtMs: nowMs
|
|
2419
|
-
};
|
|
2420
|
-
await writeConversation(
|
|
2421
|
-
state,
|
|
2422
|
-
lock,
|
|
2423
|
-
withExecutionUpdate(
|
|
2424
|
-
next,
|
|
2425
|
-
{
|
|
2426
|
-
...current.execution,
|
|
2427
|
-
status,
|
|
2428
|
-
inboundMessageIds: [
|
|
2429
|
-
...current.execution.inboundMessageIds,
|
|
2430
|
-
args.message.inboundMessageId
|
|
2431
|
-
],
|
|
2432
|
-
pendingMessages: [
|
|
2433
|
-
...current.execution.pendingMessages,
|
|
2434
|
-
args.message
|
|
2435
|
-
].sort(compareMessages)
|
|
2436
|
-
},
|
|
2437
|
-
nowMs
|
|
2438
|
-
)
|
|
2439
|
-
);
|
|
2440
|
-
return { status: "appended" };
|
|
2441
|
-
}
|
|
2442
|
-
);
|
|
2443
|
-
}
|
|
2444
|
-
async function requestConversationWork(args) {
|
|
2445
|
-
const nowMs = args.nowMs ?? now();
|
|
2446
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2447
|
-
const existing = await readConversation(state, args.conversationId);
|
|
2448
|
-
if (existing) {
|
|
2449
|
-
assertSameConversationDestination({
|
|
2450
|
-
conversationId: args.conversationId,
|
|
2451
|
-
current: existing.destination,
|
|
2452
|
-
next: args.destination
|
|
2453
|
-
});
|
|
2454
|
-
}
|
|
2455
|
-
const current = existing ?? emptyConversation({
|
|
2456
|
-
conversationId: args.conversationId,
|
|
2457
|
-
destination: args.destination,
|
|
2458
|
-
nowMs
|
|
2459
|
-
});
|
|
2460
|
-
const status = current.execution.lease ? "awaiting_resume" : "pending";
|
|
2461
|
-
await writeConversation(
|
|
2462
|
-
state,
|
|
2463
|
-
lock,
|
|
2464
|
-
withExecutionUpdate(
|
|
2465
|
-
{
|
|
2466
|
-
...current,
|
|
2467
|
-
destination: current.destination ?? args.destination
|
|
2468
|
-
},
|
|
2469
|
-
{
|
|
2470
|
-
...current.execution,
|
|
2471
|
-
status
|
|
2472
|
-
},
|
|
2473
|
-
nowMs
|
|
2474
|
-
)
|
|
2475
|
-
);
|
|
2476
|
-
return { status: existing === void 0 ? "created" : "updated" };
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
async function markConversationWorkEnqueued(args) {
|
|
2480
|
-
const nowMs = args.nowMs ?? now();
|
|
2481
|
-
await withConversationMutation(args, async (state, lock) => {
|
|
2482
|
-
const current = await readConversation(state, args.conversationId);
|
|
2483
|
-
if (!current) {
|
|
2484
|
-
return;
|
|
2485
|
-
}
|
|
2486
|
-
await writeConversation(
|
|
2487
|
-
state,
|
|
2488
|
-
lock,
|
|
2489
|
-
withExecutionUpdate(
|
|
2490
|
-
current,
|
|
2491
|
-
{
|
|
2492
|
-
...current.execution,
|
|
2493
|
-
lastEnqueuedAtMs: nowMs
|
|
2494
|
-
},
|
|
2495
|
-
nowMs
|
|
2496
|
-
)
|
|
2497
|
-
);
|
|
2498
|
-
});
|
|
2499
|
-
}
|
|
2500
|
-
async function clearConsumedConversationWake(args) {
|
|
2501
|
-
const nowMs = args.nowMs ?? now();
|
|
2502
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2503
|
-
const current = await readConversation(state, args.conversationId);
|
|
2504
|
-
if (!current || hasRunnableWork(current) || current.execution.lastEnqueuedAtMs === void 0) {
|
|
2505
|
-
return false;
|
|
2506
|
-
}
|
|
2507
|
-
await writeConversation(
|
|
2508
|
-
state,
|
|
2509
|
-
lock,
|
|
2510
|
-
withExecutionUpdate(
|
|
2511
|
-
current,
|
|
2512
|
-
{
|
|
2513
|
-
...current.execution,
|
|
2514
|
-
lastEnqueuedAtMs: void 0
|
|
2515
|
-
},
|
|
2516
|
-
nowMs
|
|
2517
|
-
)
|
|
2518
|
-
);
|
|
2519
|
-
return true;
|
|
2520
|
-
});
|
|
2521
|
-
}
|
|
2522
|
-
async function startConversationWork(args) {
|
|
2523
|
-
const nowMs = args.nowMs ?? now();
|
|
2524
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2525
|
-
const current = await readConversation(state, args.conversationId);
|
|
2526
|
-
if (!current) {
|
|
2527
|
-
return { status: "no_work" };
|
|
2528
|
-
}
|
|
2529
|
-
if (isLeaseActive(current.execution.lease, nowMs)) {
|
|
2530
|
-
return {
|
|
2531
|
-
status: "active",
|
|
2532
|
-
leaseExpiresAtMs: current.execution.lease.expiresAtMs
|
|
2533
|
-
};
|
|
2534
|
-
}
|
|
2535
|
-
if (!hasRunnableWork(current)) {
|
|
2536
|
-
return { status: "no_work" };
|
|
2537
|
-
}
|
|
2538
|
-
const lease = {
|
|
2539
|
-
token: randomUUID(),
|
|
2540
|
-
acquiredAtMs: nowMs,
|
|
2541
|
-
lastCheckInAtMs: nowMs,
|
|
2542
|
-
expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
|
|
2543
|
-
};
|
|
2544
|
-
await writeConversation(
|
|
2545
|
-
state,
|
|
2546
|
-
lock,
|
|
2547
|
-
withExecutionUpdate(
|
|
2548
|
-
current,
|
|
2549
|
-
{
|
|
2550
|
-
...current.execution,
|
|
2551
|
-
lease,
|
|
2552
|
-
status: current.execution.status === "awaiting_resume" ? "awaiting_resume" : "running",
|
|
2553
|
-
runId: current.execution.runId ?? randomUUID(),
|
|
2554
|
-
lastEnqueuedAtMs: void 0
|
|
2555
|
-
},
|
|
2556
|
-
nowMs
|
|
2557
|
-
)
|
|
2558
|
-
);
|
|
2559
|
-
return {
|
|
2560
|
-
status: "acquired",
|
|
2561
|
-
leaseToken: lease.token,
|
|
2562
|
-
leaseExpiresAtMs: lease.expiresAtMs
|
|
2563
|
-
};
|
|
2564
|
-
});
|
|
2565
|
-
}
|
|
2566
|
-
async function checkInConversationWork(args) {
|
|
2567
|
-
const nowMs = args.nowMs ?? now();
|
|
2568
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2569
|
-
const current = await readConversation(state, args.conversationId);
|
|
2570
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2571
|
-
return false;
|
|
2572
|
-
}
|
|
2573
|
-
await writeConversation(
|
|
2574
|
-
state,
|
|
2575
|
-
lock,
|
|
2576
|
-
withExecutionUpdate(
|
|
2577
|
-
current,
|
|
2578
|
-
{
|
|
2579
|
-
...current.execution,
|
|
2580
|
-
lease: {
|
|
2581
|
-
...current.execution.lease,
|
|
2582
|
-
lastCheckInAtMs: nowMs,
|
|
2583
|
-
expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
|
|
2584
|
-
}
|
|
2585
|
-
},
|
|
2586
|
-
nowMs
|
|
2587
|
-
)
|
|
2588
|
-
);
|
|
2589
|
-
return true;
|
|
2590
|
-
});
|
|
2591
|
-
}
|
|
2592
|
-
async function drainConversationMailbox(args) {
|
|
2593
|
-
const nowMs = args.nowMs ?? now();
|
|
2594
|
-
const pending = await withConversationMutation(args, async (state) => {
|
|
2595
|
-
const current = await readConversation(state, args.conversationId);
|
|
2596
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2597
|
-
throw new Error(
|
|
2598
|
-
`Conversation lease is not held for ${args.conversationId}`
|
|
2599
|
-
);
|
|
2600
|
-
}
|
|
2601
|
-
return pendingMessages(current);
|
|
2602
|
-
});
|
|
2603
|
-
if (pending.length === 0) {
|
|
2604
|
-
return { changed: false, messages: [] };
|
|
2605
|
-
}
|
|
2606
|
-
const result = await args.handle(pending);
|
|
2607
|
-
const pendingIds = new Set(
|
|
2608
|
-
pending.map((message) => message.inboundMessageId)
|
|
2609
|
-
);
|
|
2610
|
-
const acknowledgedIds = new Set(
|
|
2611
|
-
result ?? pending.map((message) => message.inboundMessageId)
|
|
2612
|
-
);
|
|
2613
|
-
for (const inboundMessageId of acknowledgedIds) {
|
|
2614
|
-
if (!pendingIds.has(inboundMessageId)) {
|
|
2615
|
-
throw new Error(
|
|
2616
|
-
`Conversation mailbox drain result is not pending for ${args.conversationId}`
|
|
2617
|
-
);
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
if (acknowledgedIds.size === 0) {
|
|
2621
|
-
return { changed: false, messages: [] };
|
|
2622
|
-
}
|
|
2623
|
-
await withConversationMutation(args, async (state, lock) => {
|
|
2624
|
-
const current = await readConversation(state, args.conversationId);
|
|
2625
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2626
|
-
throw new Error(
|
|
2627
|
-
`Conversation lease is not held for ${args.conversationId}`
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
const pendingMessages2 = current.execution.pendingMessages.filter(
|
|
2631
|
-
(message) => !acknowledgedIds.has(message.inboundMessageId)
|
|
2632
|
-
);
|
|
2633
|
-
await writeConversation(
|
|
2634
|
-
state,
|
|
2635
|
-
lock,
|
|
2636
|
-
withExecutionUpdate(
|
|
2637
|
-
current,
|
|
2638
|
-
{
|
|
2639
|
-
...current.execution,
|
|
2640
|
-
status: current.execution.status === "pending" && pendingMessages2.length === 0 ? "running" : current.execution.status,
|
|
2641
|
-
pendingMessages: pendingMessages2
|
|
2642
|
-
},
|
|
2643
|
-
nowMs
|
|
2644
|
-
)
|
|
2645
|
-
);
|
|
2646
|
-
});
|
|
2647
|
-
return {
|
|
2648
|
-
changed: true,
|
|
2649
|
-
messages: pending.filter(
|
|
2650
|
-
(message) => acknowledgedIds.has(message.inboundMessageId)
|
|
2651
|
-
)
|
|
2652
|
-
};
|
|
2653
|
-
}
|
|
2654
|
-
async function ackMessages(args) {
|
|
2655
|
-
const nowMs = args.nowMs ?? now();
|
|
2656
|
-
const inboundMessageIds = new Set(args.inboundMessageIds);
|
|
2657
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2658
|
-
const current = await readConversation(state, args.conversationId);
|
|
2659
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2660
|
-
return false;
|
|
2661
|
-
}
|
|
2662
|
-
if (inboundMessageIds.size === 0) {
|
|
2663
|
-
return true;
|
|
2664
|
-
}
|
|
2665
|
-
const pendingMessages2 = current.execution.pendingMessages.filter(
|
|
2666
|
-
(message) => !inboundMessageIds.has(message.inboundMessageId)
|
|
2667
|
-
);
|
|
2668
|
-
if (pendingMessages2.length === current.execution.pendingMessages.length) {
|
|
2669
|
-
return true;
|
|
2670
|
-
}
|
|
2671
|
-
await writeConversation(
|
|
2672
|
-
state,
|
|
2673
|
-
lock,
|
|
2674
|
-
withExecutionUpdate(
|
|
2675
|
-
current,
|
|
2676
|
-
{
|
|
2677
|
-
...current.execution,
|
|
2678
|
-
pendingMessages: pendingMessages2
|
|
2679
|
-
},
|
|
2680
|
-
nowMs
|
|
2681
|
-
)
|
|
2682
|
-
);
|
|
2683
|
-
return true;
|
|
2684
|
-
});
|
|
2685
|
-
}
|
|
2686
|
-
async function requestConversationContinuation(args) {
|
|
2687
|
-
const nowMs = args.nowMs ?? now();
|
|
2688
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2689
|
-
const current = await readConversation(state, args.conversationId);
|
|
2690
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2691
|
-
return false;
|
|
2692
|
-
}
|
|
2693
|
-
assertSameConversationDestination({
|
|
2694
|
-
conversationId: args.conversationId,
|
|
2695
|
-
current: current.destination,
|
|
2696
|
-
next: args.destination
|
|
2697
|
-
});
|
|
2698
|
-
await writeConversation(
|
|
2699
|
-
state,
|
|
2700
|
-
lock,
|
|
2701
|
-
withExecutionUpdate(
|
|
2702
|
-
current,
|
|
2703
|
-
{
|
|
2704
|
-
...current.execution,
|
|
2705
|
-
status: "awaiting_resume"
|
|
2706
|
-
},
|
|
2707
|
-
nowMs
|
|
2708
|
-
)
|
|
2709
|
-
);
|
|
2710
|
-
return true;
|
|
2711
|
-
});
|
|
2712
|
-
}
|
|
2713
|
-
async function beginConversationResume(args) {
|
|
2714
|
-
const nowMs = args.nowMs ?? now();
|
|
2715
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2716
|
-
const current = await readConversation(state, args.conversationId);
|
|
2717
|
-
if (!current || current.execution.lease?.token !== args.leaseToken || current.execution.status !== "awaiting_resume") {
|
|
2718
|
-
return false;
|
|
2719
|
-
}
|
|
2720
|
-
await writeConversation(
|
|
2721
|
-
state,
|
|
2722
|
-
lock,
|
|
2723
|
-
withExecutionUpdate(
|
|
2724
|
-
current,
|
|
2725
|
-
{
|
|
2726
|
-
...current.execution,
|
|
2727
|
-
status: "running"
|
|
2728
|
-
},
|
|
2729
|
-
nowMs
|
|
2730
|
-
)
|
|
2731
|
-
);
|
|
2732
|
-
return true;
|
|
2733
|
-
});
|
|
2734
|
-
}
|
|
2735
|
-
async function releaseConversationWork(args) {
|
|
2736
|
-
const nowMs = args.nowMs ?? now();
|
|
2737
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2738
|
-
const current = await readConversation(state, args.conversationId);
|
|
2739
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2740
|
-
return false;
|
|
2741
|
-
}
|
|
2742
|
-
await writeConversation(
|
|
2743
|
-
state,
|
|
2744
|
-
lock,
|
|
2745
|
-
withExecutionUpdate(
|
|
2746
|
-
current,
|
|
2747
|
-
{
|
|
2748
|
-
...current.execution,
|
|
2749
|
-
lease: void 0,
|
|
2750
|
-
status: current.execution.status === "running" ? "pending" : current.execution.status
|
|
2751
|
-
},
|
|
2752
|
-
nowMs
|
|
2753
|
-
)
|
|
2754
|
-
);
|
|
2755
|
-
return true;
|
|
2756
|
-
});
|
|
2757
|
-
}
|
|
2758
|
-
async function completeConversationWork(args) {
|
|
2759
|
-
const nowMs = args.nowMs ?? now();
|
|
2760
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2761
|
-
const current = await readConversation(state, args.conversationId);
|
|
2762
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2763
|
-
return "lost_lease";
|
|
2764
|
-
}
|
|
2765
|
-
const hasPending = pendingMessages(current).length > 0;
|
|
2766
|
-
const needsRun = current.execution.status === "awaiting_resume";
|
|
2767
|
-
const runnable = needsRun || hasPending;
|
|
2768
|
-
await writeConversation(
|
|
2769
|
-
state,
|
|
2770
|
-
lock,
|
|
2771
|
-
withExecutionUpdate(
|
|
2772
|
-
current,
|
|
2773
|
-
{
|
|
2774
|
-
...current.execution,
|
|
2775
|
-
lease: void 0,
|
|
2776
|
-
status: needsRun ? "awaiting_resume" : hasPending ? "pending" : "idle",
|
|
2777
|
-
runId: runnable ? current.execution.runId : void 0
|
|
2778
|
-
},
|
|
2779
|
-
nowMs
|
|
2780
|
-
)
|
|
2781
|
-
);
|
|
2782
|
-
return runnable ? "pending" : "completed";
|
|
2783
|
-
});
|
|
2784
|
-
}
|
|
2785
|
-
async function recordAttemptFailure(args) {
|
|
2786
|
-
const nowMs = args.nowMs ?? now();
|
|
2787
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2788
|
-
const current = await readConversation(state, args.conversationId);
|
|
2789
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2790
|
-
return {
|
|
2791
|
-
status: "lost_lease",
|
|
2792
|
-
pendingCount: 0,
|
|
2793
|
-
deadLetteredMessages: []
|
|
2794
|
-
};
|
|
2795
|
-
}
|
|
2796
|
-
const pendingIds = new Set(
|
|
2797
|
-
current.execution.pendingMessages.map(
|
|
2798
|
-
(message) => message.inboundMessageId
|
|
2799
|
-
)
|
|
2800
|
-
);
|
|
2801
|
-
if (args.inboundMessageIds.length === 0 || args.inboundMessageIds.some((id) => !pendingIds.has(id))) {
|
|
2802
|
-
return {
|
|
2803
|
-
status: "skipped",
|
|
2804
|
-
pendingCount: current.execution.pendingMessages.length,
|
|
2805
|
-
deadLetteredMessages: []
|
|
2806
|
-
};
|
|
2807
|
-
}
|
|
2808
|
-
const attemptedIds = new Set(args.inboundMessageIds);
|
|
2809
|
-
const deadLetteredMessages = [];
|
|
2810
|
-
const pendingMessages2 = [];
|
|
2811
|
-
for (const message of current.execution.pendingMessages) {
|
|
2812
|
-
if (!attemptedIds.has(message.inboundMessageId)) {
|
|
2813
|
-
pendingMessages2.push(message);
|
|
2814
|
-
continue;
|
|
2815
|
-
}
|
|
2816
|
-
const attempted = {
|
|
2817
|
-
...message,
|
|
2818
|
-
attemptCount: (message.attemptCount ?? 0) + 1
|
|
2819
|
-
};
|
|
2820
|
-
if (attempted.attemptCount >= CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS) {
|
|
2821
|
-
deadLetteredMessages.push(attempted);
|
|
2822
|
-
continue;
|
|
2823
|
-
}
|
|
2824
|
-
pendingMessages2.push(attempted);
|
|
2825
|
-
}
|
|
2826
|
-
await writeConversation(
|
|
2827
|
-
state,
|
|
2828
|
-
lock,
|
|
2829
|
-
withExecutionUpdate(
|
|
2830
|
-
current,
|
|
2831
|
-
{
|
|
2832
|
-
...current.execution,
|
|
2833
|
-
pendingMessages: pendingMessages2
|
|
2834
|
-
},
|
|
2835
|
-
nowMs
|
|
2836
|
-
)
|
|
2837
|
-
);
|
|
2838
|
-
return {
|
|
2839
|
-
status: "recorded",
|
|
2840
|
-
pendingCount: pendingMessages2.length,
|
|
2841
|
-
deadLetteredMessages
|
|
2842
|
-
};
|
|
2843
|
-
});
|
|
2844
|
-
}
|
|
2845
|
-
async function deadLetterAttempt(args) {
|
|
2846
|
-
const nowMs = args.nowMs ?? now();
|
|
2847
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2848
|
-
const current = await readConversation(state, args.conversationId);
|
|
2849
|
-
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2850
|
-
return "lost_lease";
|
|
2851
|
-
}
|
|
2852
|
-
const runnable = pendingMessages(current).length > 0;
|
|
2853
|
-
await writeConversation(
|
|
2854
|
-
state,
|
|
2855
|
-
lock,
|
|
2856
|
-
withExecutionUpdate(
|
|
2857
|
-
current,
|
|
2858
|
-
{
|
|
2859
|
-
...current.execution,
|
|
2860
|
-
lease: void 0,
|
|
2861
|
-
status: runnable ? "pending" : "failed",
|
|
2862
|
-
runId: runnable ? current.execution.runId : void 0
|
|
2863
|
-
},
|
|
2864
|
-
nowMs
|
|
2865
|
-
)
|
|
2866
|
-
);
|
|
2867
|
-
return runnable ? "pending" : "failed";
|
|
2868
|
-
});
|
|
2869
|
-
}
|
|
2870
|
-
async function clearExpiredConversationLease(args) {
|
|
2871
|
-
const nowMs = args.nowMs ?? now();
|
|
2872
|
-
return await withConversationMutation(args, async (state, lock) => {
|
|
2873
|
-
const current = await readConversation(state, args.conversationId);
|
|
2874
|
-
if (!current?.execution.lease || current.execution.lease.expiresAtMs > nowMs) {
|
|
2875
|
-
return false;
|
|
2876
|
-
}
|
|
2877
|
-
await writeConversation(
|
|
2878
|
-
state,
|
|
2879
|
-
lock,
|
|
2880
|
-
withExecutionUpdate(
|
|
2881
|
-
current,
|
|
2882
|
-
{
|
|
2883
|
-
...current.execution,
|
|
2884
|
-
lease: void 0,
|
|
2885
|
-
status: "awaiting_resume"
|
|
2886
|
-
},
|
|
2887
|
-
nowMs
|
|
2888
|
-
)
|
|
2889
|
-
);
|
|
2890
|
-
return true;
|
|
2891
|
-
});
|
|
2892
|
-
}
|
|
2893
|
-
async function removeActiveConversation(args) {
|
|
2894
|
-
const state = await getConnectedState(args.state);
|
|
2895
|
-
await removeIndexEntry({
|
|
2896
|
-
state,
|
|
2897
|
-
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2898
|
-
conversationId: args.conversationId
|
|
2899
|
-
});
|
|
2900
|
-
}
|
|
2901
|
-
async function listActiveConversationIds(args = {}) {
|
|
2902
|
-
const state = await getConnectedState(args.state);
|
|
2903
|
-
const index = await getConversationIndexStore(state);
|
|
2904
|
-
const entries = await index.list({
|
|
2905
|
-
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2906
|
-
limit: args.limit,
|
|
2907
|
-
order: "asc",
|
|
2908
|
-
scoreMax: args.staleBeforeMs
|
|
2909
|
-
});
|
|
2910
|
-
return entries.map((entry) => entry.conversationId);
|
|
2911
|
-
}
|
|
2912
|
-
|
|
2913
|
-
// src/chat/task-execution/store.ts
|
|
2914
|
-
function metadataStore(options) {
|
|
2915
|
-
return options.conversationStore ?? getConversationStore();
|
|
2916
|
-
}
|
|
2917
|
-
function duplicateInboundNudgeIdempotencyKey(message, nowMs) {
|
|
2918
|
-
return `duplicate:${message.conversationId}:${message.inboundMessageId}:${nowMs}`;
|
|
2919
|
-
}
|
|
2920
|
-
function hasRecentEnqueueMarker(conversation, nowMs) {
|
|
2921
|
-
const lastEnqueuedAtMs = conversation.execution.lastEnqueuedAtMs;
|
|
2922
|
-
return typeof lastEnqueuedAtMs === "number" && lastEnqueuedAtMs + CONVERSATION_WORK_STALE_ENQUEUE_MS > nowMs;
|
|
2923
|
-
}
|
|
2924
|
-
function now2() {
|
|
2925
|
-
return Date.now();
|
|
2926
|
-
}
|
|
2927
|
-
async function recordExecutionMetadata(args) {
|
|
2928
|
-
const conversation = await getConversation({
|
|
2929
|
-
conversationId: args.conversationId,
|
|
2930
|
-
state: args.state
|
|
2931
|
-
});
|
|
2932
|
-
if (!conversation) {
|
|
2933
|
-
return;
|
|
2934
|
-
}
|
|
2935
|
-
await metadataStore(args).recordExecution({
|
|
2936
|
-
channelName: conversation.channelName,
|
|
2937
|
-
conversationId: conversation.conversationId,
|
|
2938
|
-
createdAtMs: conversation.createdAtMs,
|
|
2939
|
-
destination: conversation.destination,
|
|
2940
|
-
execution: {
|
|
2941
|
-
lastCheckpointAtMs: conversation.execution.lastCheckpointAtMs,
|
|
2942
|
-
lastEnqueuedAtMs: conversation.execution.lastEnqueuedAtMs,
|
|
2943
|
-
runId: conversation.execution.runId,
|
|
2944
|
-
status: conversation.execution.status,
|
|
2945
|
-
updatedAtMs: conversation.execution.updatedAtMs
|
|
2946
|
-
},
|
|
2947
|
-
lastActivityAtMs: conversation.lastActivityAtMs,
|
|
2948
|
-
metrics: null,
|
|
2949
|
-
actor: conversation.actor,
|
|
2950
|
-
source: conversation.source,
|
|
2951
|
-
title: conversation.title,
|
|
2952
|
-
updatedAtMs: conversation.updatedAtMs
|
|
2953
|
-
});
|
|
2954
|
-
}
|
|
2955
|
-
async function getConversationWorkState2(args) {
|
|
2956
|
-
return await getConversationWorkState(args);
|
|
2957
|
-
}
|
|
2958
|
-
function countPendingConversationMessages(conversation) {
|
|
2959
|
-
return conversation.execution.pendingMessages.length;
|
|
2960
|
-
}
|
|
2961
|
-
function hasRunnableConversationWork(conversation) {
|
|
2962
|
-
return conversation.execution.status !== "idle" || countPendingConversationMessages(conversation) > 0;
|
|
2963
|
-
}
|
|
2964
|
-
async function ensureConversationWake(args) {
|
|
2965
|
-
const nowMs = args.nowMs ?? now2();
|
|
2966
|
-
const conversation = await getConversation({
|
|
2967
|
-
conversationId: args.conversationId,
|
|
2968
|
-
state: args.state
|
|
2969
|
-
});
|
|
2970
|
-
if (!conversation || !hasRunnableConversationWork(conversation)) {
|
|
2971
|
-
return { status: "no_work" };
|
|
2972
|
-
}
|
|
2973
|
-
if (!conversation.destination) {
|
|
2974
|
-
return { status: "no_work" };
|
|
2975
|
-
}
|
|
2976
|
-
if (args.replaceExistingWake !== true && conversation.execution.lease && conversation.execution.lease.expiresAtMs > nowMs) {
|
|
2977
|
-
return { status: "lease_active" };
|
|
2978
|
-
}
|
|
2979
|
-
if (args.replaceExistingWake !== true && hasRecentEnqueueMarker(conversation, nowMs)) {
|
|
2980
|
-
return { status: "already_enqueued" };
|
|
2981
|
-
}
|
|
2982
|
-
const queueResult = await args.queue.send(
|
|
2983
|
-
{
|
|
2984
|
-
conversationId: args.conversationId
|
|
2985
|
-
},
|
|
2986
|
-
{
|
|
2987
|
-
delayMs: args.delayMs,
|
|
2988
|
-
idempotencyKey: args.idempotencyKey
|
|
2989
|
-
}
|
|
2990
|
-
);
|
|
2991
|
-
await markConversationWorkEnqueued2({
|
|
2992
|
-
conversationId: args.conversationId,
|
|
2993
|
-
conversationStore: args.conversationStore,
|
|
2994
|
-
nowMs,
|
|
2995
|
-
state: args.state
|
|
2996
|
-
});
|
|
2997
|
-
return { status: "enqueued", queueMessageId: queueResult?.messageId };
|
|
2998
|
-
}
|
|
2999
|
-
async function appendAndEnqueueInboundMessage(args) {
|
|
3000
|
-
const nowMs = args.nowMs ?? now2();
|
|
3001
|
-
const appendResult = await appendInboundMessage({
|
|
3002
|
-
message: args.message,
|
|
3003
|
-
nowMs,
|
|
3004
|
-
state: args.state
|
|
3005
|
-
});
|
|
3006
|
-
let idempotencyKey = args.message.inboundMessageId;
|
|
3007
|
-
if (appendResult.status === "duplicate") {
|
|
3008
|
-
const conversation = await getConversation({
|
|
3009
|
-
conversationId: args.message.conversationId,
|
|
3010
|
-
state: args.state
|
|
3011
|
-
});
|
|
3012
|
-
if (!conversation) {
|
|
3013
|
-
return appendResult;
|
|
3014
|
-
}
|
|
3015
|
-
if (hasRecentEnqueueMarker(conversation, nowMs)) {
|
|
3016
|
-
await recordExecutionMetadata({
|
|
3017
|
-
conversationId: args.message.conversationId,
|
|
3018
|
-
conversationStore: args.conversationStore,
|
|
3019
|
-
state: args.state
|
|
3020
|
-
});
|
|
3021
|
-
return appendResult;
|
|
3022
|
-
}
|
|
3023
|
-
const duplicateStillPending = conversation.execution.pendingMessages.some(
|
|
3024
|
-
(message) => message.inboundMessageId === args.message.inboundMessageId
|
|
3025
|
-
);
|
|
3026
|
-
if (!duplicateStillPending) {
|
|
3027
|
-
await recordExecutionMetadata({
|
|
3028
|
-
conversationId: args.message.conversationId,
|
|
3029
|
-
conversationStore: args.conversationStore,
|
|
3030
|
-
state: args.state
|
|
3031
|
-
});
|
|
3032
|
-
return appendResult;
|
|
3033
|
-
}
|
|
3034
|
-
idempotencyKey = duplicateInboundNudgeIdempotencyKey(args.message, nowMs);
|
|
3035
|
-
}
|
|
3036
|
-
const wake = await ensureConversationWake({
|
|
3037
|
-
conversationId: args.message.conversationId,
|
|
3038
|
-
conversationStore: args.conversationStore,
|
|
3039
|
-
idempotencyKey,
|
|
3040
|
-
nowMs,
|
|
3041
|
-
queue: args.queue,
|
|
3042
|
-
state: args.state
|
|
3043
|
-
});
|
|
3044
|
-
if (wake.status !== "enqueued") {
|
|
3045
|
-
await recordExecutionMetadata({
|
|
3046
|
-
conversationId: args.message.conversationId,
|
|
3047
|
-
conversationStore: args.conversationStore,
|
|
3048
|
-
state: args.state
|
|
3049
|
-
});
|
|
3050
|
-
}
|
|
3051
|
-
return {
|
|
3052
|
-
...appendResult,
|
|
3053
|
-
...wake.status === "enqueued" ? { queueMessageId: wake.queueMessageId } : {}
|
|
3054
|
-
};
|
|
3055
|
-
}
|
|
3056
|
-
async function clearConsumedConversationWake2(args) {
|
|
3057
|
-
const result = await clearConsumedConversationWake(args);
|
|
3058
|
-
if (result) {
|
|
3059
|
-
await recordExecutionMetadata(args);
|
|
3060
|
-
}
|
|
3061
|
-
return result;
|
|
3062
|
-
}
|
|
3063
|
-
async function requestConversationWork2(args) {
|
|
3064
|
-
const result = await requestConversationWork(args);
|
|
3065
|
-
await recordExecutionMetadata({
|
|
3066
|
-
conversationId: args.conversationId,
|
|
3067
|
-
conversationStore: args.conversationStore,
|
|
3068
|
-
state: args.state
|
|
3069
|
-
});
|
|
3070
|
-
return result;
|
|
3071
|
-
}
|
|
3072
|
-
async function markConversationWorkEnqueued2(args) {
|
|
3073
|
-
await markConversationWorkEnqueued(args);
|
|
3074
|
-
await recordExecutionMetadata(args);
|
|
3075
|
-
}
|
|
3076
|
-
async function startConversationWork2(args) {
|
|
3077
|
-
const result = await startConversationWork(args);
|
|
3078
|
-
await recordExecutionMetadata(args);
|
|
3079
|
-
return result;
|
|
3080
|
-
}
|
|
3081
|
-
async function checkInConversationWork2(args) {
|
|
3082
|
-
const result = await checkInConversationWork(args);
|
|
3083
|
-
if (result) {
|
|
3084
|
-
await recordExecutionMetadata(args);
|
|
3085
|
-
}
|
|
3086
|
-
return result;
|
|
3087
|
-
}
|
|
3088
|
-
async function drainConversationMailbox2(args) {
|
|
3089
|
-
const result = await drainConversationMailbox(args);
|
|
3090
|
-
if (result.changed) {
|
|
3091
|
-
await recordExecutionMetadata(args);
|
|
3092
|
-
}
|
|
3093
|
-
return result.messages;
|
|
3094
|
-
}
|
|
3095
|
-
async function ackMessages2(args) {
|
|
3096
|
-
const result = await ackMessages(args);
|
|
3097
|
-
await recordExecutionMetadata(args);
|
|
3098
|
-
return result;
|
|
3099
|
-
}
|
|
3100
|
-
async function requestConversationContinuation2(args) {
|
|
3101
|
-
const result = await requestConversationContinuation(args);
|
|
3102
|
-
await recordExecutionMetadata(args);
|
|
3103
|
-
return result;
|
|
3104
|
-
}
|
|
3105
|
-
async function beginConversationResume2(args) {
|
|
3106
|
-
const result = await beginConversationResume(args);
|
|
3107
|
-
if (result) {
|
|
3108
|
-
await recordExecutionMetadata(args);
|
|
3109
|
-
}
|
|
3110
|
-
return result;
|
|
3111
|
-
}
|
|
3112
|
-
async function releaseConversationWork2(args) {
|
|
3113
|
-
const result = await releaseConversationWork(args);
|
|
3114
|
-
await recordExecutionMetadata(args);
|
|
3115
|
-
return result;
|
|
3116
|
-
}
|
|
3117
|
-
async function completeConversationWork2(args) {
|
|
3118
|
-
const result = await completeConversationWork(args);
|
|
3119
|
-
await recordExecutionMetadata(args);
|
|
3120
|
-
return result;
|
|
3121
|
-
}
|
|
3122
|
-
async function recordAttemptFailure2(args) {
|
|
3123
|
-
const result = await recordAttemptFailure(args);
|
|
3124
|
-
if (result.status === "recorded") {
|
|
3125
|
-
await recordExecutionMetadata(args);
|
|
3126
|
-
}
|
|
3127
|
-
return result;
|
|
3128
|
-
}
|
|
3129
|
-
async function deadLetterAttempt2(args) {
|
|
3130
|
-
const result = await deadLetterAttempt(args);
|
|
3131
|
-
await recordExecutionMetadata(args);
|
|
3132
|
-
return result;
|
|
3133
|
-
}
|
|
3134
|
-
async function clearExpiredConversationLease2(args) {
|
|
3135
|
-
const result = await clearExpiredConversationLease(args);
|
|
3136
|
-
await recordExecutionMetadata(args);
|
|
3137
|
-
return result;
|
|
3138
|
-
}
|
|
3139
|
-
async function removeActiveConversation2(args) {
|
|
3140
|
-
return await removeActiveConversation(args);
|
|
3141
|
-
}
|
|
3142
|
-
async function listActiveConversationIds2(args = {}) {
|
|
3143
|
-
return await listActiveConversationIds(args);
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
1747
|
// src/chat/services/agent-continue.ts
|
|
3147
1748
|
async function getAwaitingAgentContinueRequest(args) {
|
|
3148
1749
|
const sessionRecord = await getAgentTurnSessionRecord(
|
|
@@ -3152,19 +1753,31 @@ async function getAwaitingAgentContinueRequest(args) {
|
|
|
3152
1753
|
if (!sessionRecord || sessionRecord.state !== "awaiting_resume" || sessionRecord.resumeReason !== "timeout" && sessionRecord.resumeReason !== "yield" && sessionRecord.resumeReason !== "retry" || sessionRecord.resumeReason === "timeout" && sessionRecord.sliceId < 2) {
|
|
3153
1754
|
return void 0;
|
|
3154
1755
|
}
|
|
3155
|
-
|
|
1756
|
+
let routing;
|
|
1757
|
+
try {
|
|
1758
|
+
routing = await resolveTurnSessionRouting({
|
|
1759
|
+
conversationId: args.conversationId,
|
|
1760
|
+
conversationStore: args.conversationStore
|
|
1761
|
+
});
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
await failAgentTurnSessionRecord({
|
|
1764
|
+
conversationId: args.conversationId,
|
|
1765
|
+
expectedVersion: sessionRecord.version,
|
|
1766
|
+
sessionId: args.sessionId,
|
|
1767
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1768
|
+
});
|
|
3156
1769
|
return void 0;
|
|
3157
1770
|
}
|
|
3158
1771
|
return {
|
|
3159
1772
|
conversationId: args.conversationId,
|
|
3160
|
-
destination:
|
|
1773
|
+
destination: routing.destination,
|
|
3161
1774
|
sessionId: args.sessionId,
|
|
3162
1775
|
expectedVersion: sessionRecord.version
|
|
3163
1776
|
};
|
|
3164
1777
|
}
|
|
3165
1778
|
async function scheduleAgentContinue(request, options = {}) {
|
|
3166
1779
|
const nowMs = options.nowMs ?? Date.now();
|
|
3167
|
-
await
|
|
1780
|
+
await requestConversationWork({
|
|
3168
1781
|
conversationId: request.conversationId,
|
|
3169
1782
|
destination: request.destination,
|
|
3170
1783
|
nowMs,
|
|
@@ -3444,12 +2057,17 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
3444
2057
|
});
|
|
3445
2058
|
return false;
|
|
3446
2059
|
}
|
|
3447
|
-
|
|
2060
|
+
let routing;
|
|
2061
|
+
try {
|
|
2062
|
+
routing = await resolveTurnSessionRouting({
|
|
2063
|
+
conversationId: authSession.conversationId
|
|
2064
|
+
});
|
|
2065
|
+
} catch (error) {
|
|
3448
2066
|
await failAgentTurnSessionRecord({
|
|
3449
2067
|
conversationId: authSession.conversationId,
|
|
3450
2068
|
expectedVersion: lockedSessionRecord.version,
|
|
3451
2069
|
sessionId: lockedSessionId,
|
|
3452
|
-
errorMessage:
|
|
2070
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
3453
2071
|
});
|
|
3454
2072
|
return false;
|
|
3455
2073
|
}
|
|
@@ -3483,7 +2101,7 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
3483
2101
|
},
|
|
3484
2102
|
actor,
|
|
3485
2103
|
destination,
|
|
3486
|
-
source:
|
|
2104
|
+
source: routing.source,
|
|
3487
2105
|
toolChannelId: authSession.toolChannelId ?? lockedArtifacts.assistantContextChannelId ?? authSession.channelId
|
|
3488
2106
|
},
|
|
3489
2107
|
policy: {
|
|
@@ -4073,12 +2691,17 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
|
|
|
4073
2691
|
});
|
|
4074
2692
|
return false;
|
|
4075
2693
|
}
|
|
4076
|
-
|
|
2694
|
+
let routing;
|
|
2695
|
+
try {
|
|
2696
|
+
routing = await resolveTurnSessionRouting({
|
|
2697
|
+
conversationId: stored.resumeConversationId
|
|
2698
|
+
});
|
|
2699
|
+
} catch (error) {
|
|
4077
2700
|
await failAgentTurnSessionRecord({
|
|
4078
2701
|
conversationId: stored.resumeConversationId,
|
|
4079
2702
|
expectedVersion: lockedSessionRecord.version,
|
|
4080
2703
|
sessionId: lockedSessionId,
|
|
4081
|
-
errorMessage:
|
|
2704
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
4082
2705
|
});
|
|
4083
2706
|
return false;
|
|
4084
2707
|
}
|
|
@@ -4117,7 +2740,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
|
|
|
4117
2740
|
},
|
|
4118
2741
|
actor,
|
|
4119
2742
|
destination,
|
|
4120
|
-
source:
|
|
2743
|
+
source: routing.source,
|
|
4121
2744
|
toolChannelId: lockedArtifacts.assistantContextChannelId ?? stored.channelId
|
|
4122
2745
|
},
|
|
4123
2746
|
policy: {
|
|
@@ -4436,12 +3059,12 @@ import { createHmac, createSecretKey, timingSafeEqual } from "crypto";
|
|
|
4436
3059
|
import {
|
|
4437
3060
|
pluginStoredTokensSchema
|
|
4438
3061
|
} from "@sentry/junior-plugin-api";
|
|
4439
|
-
import { z
|
|
3062
|
+
import { z } from "zod";
|
|
4440
3063
|
var LOCAL_CREDENTIAL_SYNC_CONTEXT = "junior.local-credential-sync.v1";
|
|
4441
3064
|
var LOCAL_CREDENTIAL_SYNC_MAX_AGE_MS = 6e4;
|
|
4442
|
-
var localCredentialSyncSchema =
|
|
4443
|
-
createdAtMs:
|
|
4444
|
-
provider:
|
|
3065
|
+
var localCredentialSyncSchema = z.object({
|
|
3066
|
+
createdAtMs: z.number().finite(),
|
|
3067
|
+
provider: z.string().regex(/^[a-z][a-z0-9-]*$/),
|
|
4445
3068
|
tokens: pluginStoredTokensSchema
|
|
4446
3069
|
}).strict();
|
|
4447
3070
|
function syncSecret() {
|
|
@@ -4515,29 +3138,6 @@ async function receiveLocalOAuthCredential(request) {
|
|
|
4515
3138
|
}
|
|
4516
3139
|
|
|
4517
3140
|
export {
|
|
4518
|
-
isInvalidConversationRecordError,
|
|
4519
|
-
CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
|
|
4520
|
-
CONVERSATION_WORK_STALE_ENQUEUE_MS,
|
|
4521
|
-
isFinalAttempt,
|
|
4522
|
-
getConversationWorkState2 as getConversationWorkState,
|
|
4523
|
-
countPendingConversationMessages,
|
|
4524
|
-
hasRunnableConversationWork,
|
|
4525
|
-
ensureConversationWake,
|
|
4526
|
-
appendAndEnqueueInboundMessage,
|
|
4527
|
-
clearConsumedConversationWake2 as clearConsumedConversationWake,
|
|
4528
|
-
startConversationWork2 as startConversationWork,
|
|
4529
|
-
checkInConversationWork2 as checkInConversationWork,
|
|
4530
|
-
drainConversationMailbox2 as drainConversationMailbox,
|
|
4531
|
-
ackMessages2 as ackMessages,
|
|
4532
|
-
requestConversationContinuation2 as requestConversationContinuation,
|
|
4533
|
-
beginConversationResume2 as beginConversationResume,
|
|
4534
|
-
releaseConversationWork2 as releaseConversationWork,
|
|
4535
|
-
completeConversationWork2 as completeConversationWork,
|
|
4536
|
-
recordAttemptFailure2 as recordAttemptFailure,
|
|
4537
|
-
deadLetterAttempt2 as deadLetterAttempt,
|
|
4538
|
-
clearExpiredConversationLease2 as clearExpiredConversationLease,
|
|
4539
|
-
removeActiveConversation2 as removeActiveConversation,
|
|
4540
|
-
listActiveConversationIds2 as listActiveConversationIds,
|
|
4541
3141
|
createSlackAdapterAssistantStatusSession,
|
|
4542
3142
|
splitSlackReplyText,
|
|
4543
3143
|
buildSlackOutputMessage,
|