@sentry/junior 0.108.0 → 0.110.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-NU5HK3PS.js → agent-hooks-ELT4ODFF.js} +12 -13
- package/dist/api/conversations/access.d.ts +9 -0
- package/dist/api/conversations/aggregate.d.ts +12 -1
- package/dist/api/conversations/archive.d.ts +3 -5
- package/dist/api/conversations/detail.d.ts +156 -7
- package/dist/api/conversations/events.d.ts +2 -2
- package/dist/api/conversations/list.d.ts +57 -6
- package/dist/api/conversations/projection.d.ts +6 -7
- package/dist/api/conversations/reporting.d.ts +10 -4
- package/dist/api/conversations/routes.d.ts +2 -1
- package/dist/api/conversations/shared.d.ts +1 -1
- package/dist/api/conversations/stats.d.ts +37 -6
- package/dist/api/conversations/stats.query.d.ts +1 -1
- package/dist/api/conversations/usage.d.ts +9 -0
- package/dist/api/http.d.ts +7 -0
- package/dist/api/locations/detail.d.ts +102 -6
- package/dist/api/locations/list.d.ts +43 -5
- package/dist/api/locations/query.d.ts +4 -2
- package/dist/api/locations/routes.d.ts +2 -1
- package/dist/api/people/list.d.ts +28 -6
- package/dist/api/people/list.query.d.ts +1 -1
- package/dist/api/people/profile.d.ts +96 -7
- package/dist/api/people/profile.query.d.ts +4 -2
- package/dist/api/people/routes.d.ts +2 -1
- package/dist/api/people/shared.d.ts +1219 -2
- package/dist/api/route.d.ts +18 -4
- package/dist/api/schema/common.d.ts +6 -0
- package/dist/api/{conversations/schema.d.ts → schema/conversation.d.ts} +88 -9
- package/dist/api/{locations/schema.d.ts → schema/location.d.ts} +5 -0
- package/dist/api/{people/schema.d.ts → schema/person.d.ts} +37 -45
- package/dist/api/schema.d.ts +8 -22
- package/dist/api/schema.js +10 -2
- package/dist/api.d.ts +5 -1
- package/dist/api.js +838 -369
- package/dist/app.d.ts +1 -0
- package/dist/app.js +1564 -486
- package/dist/catalog-runtime-4GI22PXK.js +16 -0
- package/dist/chat/agent/resume.d.ts +11 -12
- package/dist/chat/conversations/history.d.ts +3 -0
- package/dist/chat/conversations/sql/privacy.d.ts +3 -15
- package/dist/chat/conversations/sql/store.d.ts +0 -7
- package/dist/chat/log-context.d.ts +40 -0
- package/dist/chat/logging.d.ts +3 -26
- package/dist/chat/pi/messages.d.ts +2 -2
- package/dist/chat/pi/sql-model-usage.d.ts +4 -1
- package/dist/chat/runtime/slack-resume.d.ts +2 -2
- package/dist/chat/services/auth-pause.d.ts +4 -0
- package/dist/chat/services/context-compaction.d.ts +1 -0
- package/dist/chat/services/turn-session-record.d.ts +0 -2
- package/dist/chat/slack/reply.d.ts +7 -19
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/task-execution/queue-signing.d.ts +10 -8
- package/dist/chat/task-execution/queue.d.ts +9 -7
- package/dist/chat/task-execution/slack-work.d.ts +6 -6
- package/dist/{chunk-UD6THJ2I.js → chunk-2T7TIEXI.js} +197 -12
- package/dist/{chunk-CEA3A3UA.js → chunk-2ZTTXTSZ.js} +24 -1
- package/dist/{chunk-XKB7LGIW.js → chunk-3S7YYHBI.js} +3 -139
- package/dist/chunk-3TV7OEHL.js +10 -0
- package/dist/{chunk-A5CO2EHL.js → chunk-3UBQI73J.js} +11 -15
- package/dist/{chunk-7FBGKXPE.js → chunk-42WRQLJ6.js} +10 -33
- package/dist/{chunk-TE4QHJH4.js → chunk-4QU44P7Q.js} +135 -595
- package/dist/{chunk-RT5BUHR5.js → chunk-6Y5L4BOM.js} +3 -5
- package/dist/{chunk-VIXFG2VC.js → chunk-C4MZ3GP5.js} +1 -2
- package/dist/{chunk-3RGQLX2F.js → chunk-C5XAFTHY.js} +1011 -29
- package/dist/{chunk-PUAMO4PI.js → chunk-FPK7XY3F.js} +139 -99
- package/dist/{chunk-H3QYZL7K.js → chunk-GK47NBDE.js} +28 -3
- package/dist/{chunk-O24R4DCK.js → chunk-IYKPU7H6.js} +94 -56
- package/dist/chunk-JQFJO5WH.js +477 -0
- package/dist/{chunk-VH6KWKG2.js → chunk-L5XQPJ7T.js} +3 -3
- package/dist/{chunk-2XXEKR44.js → chunk-MIAKQNYX.js} +43 -31
- package/dist/{chunk-KPMPQ6AA.js → chunk-RHHRKSPI.js} +249 -223
- package/dist/{chunk-YT72TOEK.js → chunk-SBOBTS27.js} +2 -2
- package/dist/{chunk-B5I5LMSP.js → chunk-W6RJZG7F.js} +5 -7
- package/dist/chunk-W75ZPAGG.js +44 -0
- package/dist/{chunk-SS67LUOK.js → chunk-WNOVRQ67.js} +6 -7
- package/dist/{chunk-YIPD3L6L.js → chunk-Y3H2DN6F.js} +8 -10
- package/dist/{chunk-DY7TRYB5.js → chunk-Y5FK3CNG.js} +1 -1
- package/dist/chunk-ZKIOWEFL.js +125 -0
- package/dist/cli/chat.js +23 -24
- package/dist/cli/check.js +9 -7
- package/dist/cli/plugins.js +16 -17
- package/dist/cli/snapshot-warmup.js +11 -10
- package/dist/cli/upgrade.js +314 -3505
- package/dist/db/db.d.ts +1 -1
- package/dist/db/schema/conversations.d.ts +17 -0
- package/dist/db/schema.d.ts +17 -0
- package/dist/{db-AMRBAT5D.js → db-Y6W6CWWW.js} +8 -7
- package/dist/instrumentation.js +4 -3
- package/dist/nitro.js +4 -4
- package/dist/reporting-schema.d.ts +46 -0
- package/dist/{runner-TQH5GAJ4.js → runner-BBFV5GLR.js} +17 -21
- package/dist/{skills-ITNHBARB.js → skills-AR4AF32L.js} +8 -6
- package/dist/{validation-AGQ46ZGV.js → validation-FBIEPTC2.js} +8 -6
- package/dist/version.d.ts +2 -0
- package/dist/version.js +7 -0
- package/migrations/0006_drop_legacy_conversation_messages.sql +1 -0
- package/migrations/0007_thick_sugar_man.sql +23 -0
- package/migrations/README.md +12 -9
- package/migrations/meta/0006_snapshot.json +948 -0
- package/migrations/meta/0007_snapshot.json +1013 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +9 -5
- package/dist/catalog-runtime-FOFNPR3P.js +0 -14
- package/dist/chunk-AHJR2IFF.js +0 -115
- package/dist/chunk-B7PUBQQM.js +0 -53
- package/dist/chunk-G3E7SCME.js +0 -28
- package/dist/chunk-J3B3FPP2.js +0 -1061
- package/dist/chunk-OB42YVAE.js +0 -16
- package/dist/chunk-TWINAEZQ.js +0 -1283
package/dist/app.js
CHANGED
|
@@ -6,45 +6,23 @@ import {
|
|
|
6
6
|
processPluginTask,
|
|
7
7
|
requireTurnFailureEventId,
|
|
8
8
|
scheduleSessionCompletedPluginTasks
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import {
|
|
11
|
-
CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
|
|
12
|
-
CONVERSATION_WORK_STALE_ENQUEUE_MS,
|
|
13
|
-
ackMessages,
|
|
14
|
-
appendInboundMessage,
|
|
15
|
-
checkInConversationWork,
|
|
16
|
-
clearConsumedConversationWake,
|
|
17
|
-
clearExpiredConversationLease,
|
|
18
|
-
completeConversationWork,
|
|
19
|
-
deadLetterAttempt,
|
|
20
|
-
drainConversationMailbox,
|
|
21
|
-
getConversation,
|
|
22
|
-
getConversationWorkState,
|
|
23
|
-
isFinalAttempt,
|
|
24
|
-
isInvalidConversationRecordError,
|
|
25
|
-
listActiveConversationIds,
|
|
26
|
-
markConversationWorkEnqueued,
|
|
27
|
-
recordAttemptFailure,
|
|
28
|
-
releaseConversationWork,
|
|
29
|
-
removeActiveConversation,
|
|
30
|
-
requestConversationContinuation,
|
|
31
|
-
requestConversationWork,
|
|
32
|
-
startConversationWork
|
|
33
|
-
} from "./chunk-TWINAEZQ.js";
|
|
9
|
+
} from "./chunk-3UBQI73J.js";
|
|
34
10
|
import {
|
|
35
11
|
GET,
|
|
36
12
|
buildSentryConversationUrl,
|
|
37
13
|
conversationVisibilityFromSlackChannelType,
|
|
38
|
-
resolveRootVisibility,
|
|
39
14
|
resolveSlackChannelTypeFromMessage,
|
|
40
15
|
resolveSlackConversationContext
|
|
41
|
-
} from "./chunk-
|
|
42
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-3S7YYHBI.js";
|
|
17
|
+
import "./chunk-2ZTTXTSZ.js";
|
|
43
18
|
import {
|
|
19
|
+
ConversationQueueMessageRejectedError,
|
|
20
|
+
conversationQueueMessageSchema,
|
|
44
21
|
getVercelConversationWorkQueue,
|
|
22
|
+
isConversationQueueMessageRejectedError,
|
|
45
23
|
resolveConversationWorkQueueTopic,
|
|
46
24
|
verifyConversationQueueMessage
|
|
47
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-MIAKQNYX.js";
|
|
48
26
|
import {
|
|
49
27
|
PLUGIN_TASK_QUEUE_TOPIC,
|
|
50
28
|
createVercelQueueClient,
|
|
@@ -53,7 +31,7 @@ import {
|
|
|
53
31
|
import {
|
|
54
32
|
validatePluginEgressCredentialHooks,
|
|
55
33
|
validatePluginRegistrations
|
|
56
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-Y5FK3CNG.js";
|
|
57
35
|
import {
|
|
58
36
|
defineJuniorPlugins,
|
|
59
37
|
pluginCatalogConfigFromEnv,
|
|
@@ -106,21 +84,28 @@ import {
|
|
|
106
84
|
splitSlackReplyText,
|
|
107
85
|
startOAuthFlow,
|
|
108
86
|
truncateStatusText
|
|
109
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-RHHRKSPI.js";
|
|
110
88
|
import {
|
|
111
89
|
ConversationTurnBoundaryError,
|
|
112
90
|
CooperativeTurnYieldError,
|
|
91
|
+
JUNIOR_THREAD_STATE_TTL_MS,
|
|
113
92
|
TurnInputCommitLostError,
|
|
114
93
|
TurnInputDeferredError,
|
|
115
94
|
TurnSliceLimitExceededError,
|
|
95
|
+
abandonAgentTurnSessionRecord,
|
|
116
96
|
buildConversationContext,
|
|
117
97
|
buildDeterministicAssistantMessageId,
|
|
118
98
|
buildDeterministicTurnId,
|
|
119
99
|
buildTurnLimitResponse,
|
|
120
100
|
clearPendingAuth,
|
|
121
101
|
coerceThreadArtifactsState,
|
|
102
|
+
coerceThreadConversationState,
|
|
103
|
+
commitMessages,
|
|
122
104
|
completeDeliveredTurn,
|
|
123
105
|
createConversationMemoryService,
|
|
106
|
+
failAgentTurnSessionRecord,
|
|
107
|
+
getAgentTurnSessionRecord,
|
|
108
|
+
getAgentTurnSessionRecordForResume,
|
|
124
109
|
getChannelConfigurationService,
|
|
125
110
|
getChannelConfigurationServiceById,
|
|
126
111
|
getConversationMessageSlackTs,
|
|
@@ -135,6 +120,9 @@ import {
|
|
|
135
120
|
isPendingAuthLatestRequest,
|
|
136
121
|
isTurnInputCommitLostError,
|
|
137
122
|
isTurnInputDeferredError,
|
|
123
|
+
listAgentTurnSessionSummariesForConversation,
|
|
124
|
+
loadConversationProjection,
|
|
125
|
+
loadProjection,
|
|
138
126
|
markConversationMessage,
|
|
139
127
|
markTurnClosed,
|
|
140
128
|
markTurnFailed,
|
|
@@ -148,33 +136,23 @@ import {
|
|
|
148
136
|
persistThreadStateById,
|
|
149
137
|
persistWithRetry,
|
|
150
138
|
persistYieldSessionRecord,
|
|
139
|
+
recordAgentTurnSessionSummary,
|
|
140
|
+
recordAuthorizationCompleted,
|
|
151
141
|
recordDeliveredAssistantMessage,
|
|
152
142
|
startActiveTurn,
|
|
153
143
|
turnHasReply,
|
|
154
144
|
updateConversationStats,
|
|
155
145
|
upsertConversationMessage
|
|
156
|
-
} from "./chunk-
|
|
157
|
-
import
|
|
158
|
-
JUNIOR_THREAD_STATE_TTL_MS,
|
|
159
|
-
abandonAgentTurnSessionRecord,
|
|
160
|
-
coerceThreadConversationState,
|
|
161
|
-
commitMessages,
|
|
162
|
-
failAgentTurnSessionRecord,
|
|
163
|
-
getAgentTurnSessionRecord,
|
|
164
|
-
getAgentTurnSessionRecordForResume,
|
|
165
|
-
listAgentTurnSessionSummariesForConversation,
|
|
166
|
-
loadConversationProjection,
|
|
167
|
-
loadProjection,
|
|
168
|
-
recordAgentTurnSessionSummary,
|
|
169
|
-
recordAuthorizationCompleted
|
|
170
|
-
} from "./chunk-J3B3FPP2.js";
|
|
171
|
-
import "./chunk-B5I5LMSP.js";
|
|
146
|
+
} from "./chunk-C5XAFTHY.js";
|
|
147
|
+
import "./chunk-W6RJZG7F.js";
|
|
172
148
|
import "./chunk-MU6HHZEN.js";
|
|
173
149
|
import "./chunk-PDO5BLNM.js";
|
|
174
150
|
import "./chunk-UIE3R5XU.js";
|
|
175
151
|
import {
|
|
176
152
|
bindScheduledTaskCredentialSubject,
|
|
177
153
|
bindSlackDirectCredentialSubject,
|
|
154
|
+
createPluginLogger,
|
|
155
|
+
createPluginState,
|
|
178
156
|
getDashboardConversationLink,
|
|
179
157
|
getPluginApiRoutes,
|
|
180
158
|
getPluginRoutes,
|
|
@@ -185,16 +163,12 @@ import {
|
|
|
185
163
|
validatePlugins,
|
|
186
164
|
verifyScheduledTaskCredentialSubject,
|
|
187
165
|
verifySlackDirectCredentialSubject
|
|
188
|
-
} from "./chunk-
|
|
189
|
-
import {
|
|
190
|
-
createPluginLogger,
|
|
191
|
-
createPluginState
|
|
192
|
-
} from "./chunk-AHJR2IFF.js";
|
|
193
|
-
import "./chunk-G3E7SCME.js";
|
|
166
|
+
} from "./chunk-2T7TIEXI.js";
|
|
194
167
|
import {
|
|
195
168
|
acquireActiveLock,
|
|
169
|
+
getDefaultRedisStateAdapterFor,
|
|
196
170
|
getStateAdapter
|
|
197
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-GK47NBDE.js";
|
|
198
172
|
import {
|
|
199
173
|
SlackActionError,
|
|
200
174
|
downloadPrivateSlackFile,
|
|
@@ -205,18 +179,25 @@ import {
|
|
|
205
179
|
normalizeSlackConversationId,
|
|
206
180
|
runWithSlackInstallationToken,
|
|
207
181
|
runWithWorkspaceTeamId
|
|
208
|
-
} from "./chunk-
|
|
182
|
+
} from "./chunk-L5XQPJ7T.js";
|
|
209
183
|
import {
|
|
210
184
|
contextProvenance,
|
|
185
|
+
createSlackDestination,
|
|
186
|
+
destinationKey,
|
|
211
187
|
getConversationEventStore,
|
|
212
188
|
getConversationStore,
|
|
213
189
|
getDb,
|
|
214
190
|
getSqlExecutor,
|
|
215
191
|
instructionProvenanceFor,
|
|
216
|
-
|
|
217
|
-
|
|
192
|
+
parseDestination,
|
|
193
|
+
requireSlackDestination,
|
|
194
|
+
sameDestination,
|
|
218
195
|
withConversationEventLock
|
|
219
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-4QU44P7Q.js";
|
|
197
|
+
import {
|
|
198
|
+
juniorConversationEvents,
|
|
199
|
+
juniorConversations
|
|
200
|
+
} from "./chunk-JQFJO5WH.js";
|
|
220
201
|
import {
|
|
221
202
|
sleep
|
|
222
203
|
} from "./chunk-4ZNGQH7C.js";
|
|
@@ -240,7 +221,7 @@ import {
|
|
|
240
221
|
standardModelId,
|
|
241
222
|
stripRuntimeTurnContext,
|
|
242
223
|
trimTrailingAssistantMessages
|
|
243
|
-
} from "./chunk-
|
|
224
|
+
} from "./chunk-WNOVRQ67.js";
|
|
244
225
|
import {
|
|
245
226
|
juniorDestinations
|
|
246
227
|
} from "./chunk-VFUK3X5B.js";
|
|
@@ -249,32 +230,28 @@ import {
|
|
|
249
230
|
readSlackRawMessageContext,
|
|
250
231
|
resolveSlackChannelIdFromMessage,
|
|
251
232
|
resolveSlackChannelIdFromThreadId
|
|
252
|
-
} from "./chunk-
|
|
253
|
-
import {
|
|
254
|
-
createSlackDestination,
|
|
255
|
-
destinationKey,
|
|
256
|
-
parseDestination,
|
|
257
|
-
requireSlackDestination,
|
|
258
|
-
sameDestination
|
|
259
|
-
} from "./chunk-B7PUBQQM.js";
|
|
233
|
+
} from "./chunk-6Y5L4BOM.js";
|
|
260
234
|
import {
|
|
261
235
|
discoverSkills
|
|
262
|
-
} from "./chunk-
|
|
236
|
+
} from "./chunk-SBOBTS27.js";
|
|
237
|
+
import {
|
|
238
|
+
pluginCatalogRuntime
|
|
239
|
+
} from "./chunk-3TV7OEHL.js";
|
|
263
240
|
import {
|
|
264
241
|
buildOAuthTokenRequest,
|
|
265
242
|
credentialSubjectSchema,
|
|
266
243
|
hasRequiredOAuthScope,
|
|
267
|
-
parseOAuthTokenResponse
|
|
268
|
-
|
|
269
|
-
} from "./chunk-YIPD3L6L.js";
|
|
244
|
+
parseOAuthTokenResponse
|
|
245
|
+
} from "./chunk-Y3H2DN6F.js";
|
|
270
246
|
import {
|
|
271
247
|
createActor,
|
|
272
248
|
createSlackActor,
|
|
273
249
|
createSlackResumeActor,
|
|
274
250
|
isActorUserId,
|
|
275
251
|
isUserActor,
|
|
276
|
-
parseActorUserId
|
|
277
|
-
|
|
252
|
+
parseActorUserId,
|
|
253
|
+
parseStoredSlackActor
|
|
254
|
+
} from "./chunk-C4MZ3GP5.js";
|
|
278
255
|
import {
|
|
279
256
|
buildTurnFailureResponse,
|
|
280
257
|
createChatSdkLogger,
|
|
@@ -290,22 +267,27 @@ import {
|
|
|
290
267
|
setTags,
|
|
291
268
|
withContext,
|
|
292
269
|
withSpan
|
|
293
|
-
} from "./chunk-
|
|
270
|
+
} from "./chunk-IYKPU7H6.js";
|
|
294
271
|
import {
|
|
272
|
+
isRecord,
|
|
273
|
+
parseSlackChannelId,
|
|
274
|
+
parseSlackMessageTs,
|
|
275
|
+
toOptionalNumber,
|
|
295
276
|
toOptionalString
|
|
296
|
-
} from "./chunk-
|
|
277
|
+
} from "./chunk-42WRQLJ6.js";
|
|
297
278
|
import {
|
|
298
279
|
continueTrace
|
|
299
280
|
} from "./chunk-6GWA276C.js";
|
|
300
281
|
import "./chunk-B3IT5END.js";
|
|
301
|
-
import {
|
|
302
|
-
JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE,
|
|
303
|
-
parseSlackChannelId,
|
|
304
|
-
parseSlackMessageTs
|
|
305
|
-
} from "./chunk-7FBGKXPE.js";
|
|
306
282
|
import {
|
|
307
283
|
homeDir
|
|
308
284
|
} from "./chunk-3V7OFJZC.js";
|
|
285
|
+
import {
|
|
286
|
+
JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
|
|
287
|
+
} from "./chunk-W75ZPAGG.js";
|
|
288
|
+
import {
|
|
289
|
+
JUNIOR_VERSION
|
|
290
|
+
} from "./chunk-ZKIOWEFL.js";
|
|
309
291
|
import "./chunk-MLKGABMK.js";
|
|
310
292
|
|
|
311
293
|
// src/app.ts
|
|
@@ -353,53 +335,26 @@ function buildSlackReplyBlocks(text, footer) {
|
|
|
353
335
|
}
|
|
354
336
|
|
|
355
337
|
// src/chat/slack/reply.ts
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const hasVisibleDelivery = post.text.trim().length > 0;
|
|
375
|
-
if (hasVisibleDelivery) {
|
|
376
|
-
await args.beforePost?.();
|
|
377
|
-
}
|
|
378
|
-
let messageTs;
|
|
379
|
-
try {
|
|
380
|
-
if (post.text.trim().length > 0) {
|
|
381
|
-
const footer = index === lastTextPostIndex ? args.footer : void 0;
|
|
382
|
-
const blocks = buildSlackReplyBlocks(post.text, footer);
|
|
383
|
-
const response = await postSlackMessage({
|
|
384
|
-
channelId: args.channelId,
|
|
385
|
-
threadTs: args.threadTs,
|
|
386
|
-
text: post.text,
|
|
387
|
-
...blocks ? { blocks } : {}
|
|
388
|
-
});
|
|
389
|
-
messageTs = response.ts;
|
|
390
|
-
lastPostedMessageTs = response.ts;
|
|
391
|
-
}
|
|
392
|
-
continue;
|
|
393
|
-
} catch (error) {
|
|
394
|
-
await args.onPostError?.({
|
|
395
|
-
error,
|
|
396
|
-
messageTs,
|
|
397
|
-
stage: post.stage
|
|
398
|
-
});
|
|
399
|
-
throw error;
|
|
400
|
-
}
|
|
338
|
+
async function sendSlackReply(args) {
|
|
339
|
+
const chunks = splitSlackReplyText(args.text);
|
|
340
|
+
const footer = buildSlackReplyFooter({
|
|
341
|
+
conversationId: args.conversationId
|
|
342
|
+
});
|
|
343
|
+
let lastMessageTs;
|
|
344
|
+
for (const [index, text] of chunks.entries()) {
|
|
345
|
+
const blocks = buildSlackReplyBlocks(
|
|
346
|
+
text,
|
|
347
|
+
index === chunks.length - 1 ? footer : void 0
|
|
348
|
+
);
|
|
349
|
+
const response = await postSlackMessage({
|
|
350
|
+
channelId: args.channelId,
|
|
351
|
+
threadTs: args.threadTs,
|
|
352
|
+
text,
|
|
353
|
+
...blocks ? { blocks } : {}
|
|
354
|
+
});
|
|
355
|
+
lastMessageTs = response.ts;
|
|
401
356
|
}
|
|
402
|
-
return
|
|
357
|
+
return lastMessageTs;
|
|
403
358
|
}
|
|
404
359
|
|
|
405
360
|
// src/chat/slack/errors.ts
|
|
@@ -927,8 +882,8 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
927
882
|
const turnId = getDispatchTurnId(dispatch.id);
|
|
928
883
|
const logContext = {
|
|
929
884
|
conversationId,
|
|
930
|
-
|
|
931
|
-
|
|
885
|
+
messageConversationId: conversationId,
|
|
886
|
+
destinationName: dispatch.destination.channelId,
|
|
932
887
|
runId: dispatch.id,
|
|
933
888
|
actorType: dispatch.actor.platform,
|
|
934
889
|
actorId: dispatch.actor.name,
|
|
@@ -1018,15 +973,15 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
1018
973
|
let resultMessageTs;
|
|
1019
974
|
let assistantMessageDelivered = turnHasReply(conversation, turnId);
|
|
1020
975
|
const deliverAssistantMessage = async (assistantMessage) => {
|
|
1021
|
-
|
|
1022
|
-
if (posts.length === 0) {
|
|
976
|
+
if (!assistantMessage.text.trim()) {
|
|
1023
977
|
return;
|
|
1024
978
|
}
|
|
1025
979
|
failureCode = "delivery_failed";
|
|
1026
980
|
try {
|
|
1027
|
-
resultMessageTs = await
|
|
981
|
+
resultMessageTs = await sendSlackReply({
|
|
1028
982
|
channelId: dispatch.destination.channelId,
|
|
1029
|
-
|
|
983
|
+
conversationId,
|
|
984
|
+
text: assistantMessage.text
|
|
1030
985
|
});
|
|
1031
986
|
} catch (error) {
|
|
1032
987
|
if (isRetryableSlackPostError(error)) {
|
|
@@ -1222,145 +1177,1266 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
1222
1177
|
destinationVisibility: dispatch.destinationVisibility,
|
|
1223
1178
|
source: dispatch.source,
|
|
1224
1179
|
actor: dispatch.actor,
|
|
1225
|
-
surface: "api"
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1180
|
+
surface: "api"
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
if (statePersisted) {
|
|
1184
|
+
if (failure) {
|
|
1185
|
+
await turnLifecycle.fail({
|
|
1186
|
+
conversationId,
|
|
1187
|
+
turnId,
|
|
1188
|
+
createdAtMs: Date.now(),
|
|
1189
|
+
failureCode: "model_execution_failed",
|
|
1190
|
+
...modelFailureEventId ? { eventId: modelFailureEventId } : {}
|
|
1191
|
+
});
|
|
1192
|
+
} else {
|
|
1193
|
+
await turnLifecycle.complete({
|
|
1194
|
+
conversationId,
|
|
1195
|
+
turnId,
|
|
1196
|
+
createdAtMs: Date.now(),
|
|
1197
|
+
outcome: assistantMessageDelivered ? "success" : "no_reply"
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
lifecycleTerminalized = true;
|
|
1201
|
+
}
|
|
1202
|
+
dispatch = await markDispatch({
|
|
1203
|
+
dispatch,
|
|
1204
|
+
status: failure ? "failed" : "completed",
|
|
1205
|
+
...failure ? { errorMessage: failure } : {},
|
|
1206
|
+
resultMessageTs
|
|
1207
|
+
});
|
|
1208
|
+
if (!failure) {
|
|
1209
|
+
try {
|
|
1210
|
+
await scheduleCompletedTasks({
|
|
1211
|
+
conversationId,
|
|
1212
|
+
sessionId: turnId
|
|
1213
|
+
});
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
logException(
|
|
1216
|
+
error,
|
|
1217
|
+
"plugin_session_completed_task_schedule_failed",
|
|
1218
|
+
logContext,
|
|
1219
|
+
{},
|
|
1220
|
+
"Plugin session.completed task scheduling failed"
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
if (error instanceof AuthorizationFlowDisabledError) {
|
|
1226
|
+
if (lifecycleStarted && !lifecycleTerminalized) {
|
|
1227
|
+
await turnLifecycle.fail({
|
|
1228
|
+
conversationId,
|
|
1229
|
+
turnId,
|
|
1230
|
+
createdAtMs: Date.now(),
|
|
1231
|
+
failureCode: "agent_run_failed"
|
|
1232
|
+
});
|
|
1233
|
+
lifecycleTerminalized = true;
|
|
1234
|
+
}
|
|
1235
|
+
await markDispatch({
|
|
1236
|
+
dispatch,
|
|
1237
|
+
status: "blocked",
|
|
1238
|
+
errorMessage: `Dispatch requires ${error.provider} authorization.`
|
|
1239
|
+
});
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
if (error instanceof PluginCredentialFailureError) {
|
|
1243
|
+
if (lifecycleStarted && !lifecycleTerminalized) {
|
|
1244
|
+
await turnLifecycle.fail({
|
|
1245
|
+
conversationId,
|
|
1246
|
+
turnId,
|
|
1247
|
+
createdAtMs: Date.now(),
|
|
1248
|
+
failureCode: "agent_run_failed"
|
|
1249
|
+
});
|
|
1250
|
+
lifecycleTerminalized = true;
|
|
1251
|
+
}
|
|
1252
|
+
await markDispatch({
|
|
1253
|
+
dispatch,
|
|
1254
|
+
status: "blocked",
|
|
1255
|
+
errorMessage: error.message
|
|
1256
|
+
});
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
const eventId = logException(
|
|
1260
|
+
error,
|
|
1261
|
+
"agent_dispatch_run_failed",
|
|
1262
|
+
{
|
|
1263
|
+
...logContext,
|
|
1264
|
+
modelId: standardModelId(botConfig)
|
|
1265
|
+
},
|
|
1266
|
+
{},
|
|
1267
|
+
"Agent dispatch failed"
|
|
1268
|
+
);
|
|
1269
|
+
if (lifecycleStarted && !lifecycleTerminalized) {
|
|
1270
|
+
await turnLifecycle.fail({
|
|
1271
|
+
conversationId,
|
|
1272
|
+
turnId,
|
|
1273
|
+
createdAtMs: Date.now(),
|
|
1274
|
+
failureCode,
|
|
1275
|
+
...eventId ? { eventId } : {}
|
|
1232
1276
|
});
|
|
1277
|
+
lifecycleTerminalized = true;
|
|
1278
|
+
}
|
|
1279
|
+
await markDispatch({
|
|
1280
|
+
dispatch,
|
|
1281
|
+
status: "failed",
|
|
1282
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1283
|
+
});
|
|
1284
|
+
} finally {
|
|
1285
|
+
await stateAdapter.releaseLock(destinationLock);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/handlers/agent-dispatch.ts
|
|
1290
|
+
async function POST(request, waitUntil, options) {
|
|
1291
|
+
const payload = await verifyDispatchCallbackRequest(request);
|
|
1292
|
+
if (!payload) {
|
|
1293
|
+
return new Response("Unauthorized", { status: 401 });
|
|
1294
|
+
}
|
|
1295
|
+
waitUntil(
|
|
1296
|
+
() => runAgentDispatchSlice(payload, {
|
|
1297
|
+
agentRunner: options.agentRunner
|
|
1298
|
+
}).catch((error) => {
|
|
1299
|
+
logException(
|
|
1300
|
+
error,
|
|
1301
|
+
"agent_dispatch_handler_failed",
|
|
1302
|
+
{},
|
|
1303
|
+
{ "app.dispatch.id": payload.id },
|
|
1304
|
+
"Agent dispatch handler failed"
|
|
1305
|
+
);
|
|
1306
|
+
})
|
|
1307
|
+
);
|
|
1308
|
+
return new Response("Accepted", { status: 202 });
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/handlers/heartbeat.ts
|
|
1312
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1313
|
+
|
|
1314
|
+
// src/chat/task-execution/state.ts
|
|
1315
|
+
import { randomUUID } from "crypto";
|
|
1316
|
+
var CONVERSATION_PREFIX = "junior:conversation";
|
|
1317
|
+
var CONVERSATION_SCHEMA_VERSION = 1;
|
|
1318
|
+
var CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH = 1e4;
|
|
1319
|
+
var CONVERSATION_INDEX_LOCK_TTL_MS = 1e4;
|
|
1320
|
+
var CONVERSATION_INDEX_LOCK_WAIT_MS = 2e3;
|
|
1321
|
+
var CONVERSATION_INDEX_LOCK_RETRY_MS = 25;
|
|
1322
|
+
var CONVERSATION_MUTATION_LOCK_TTL_MS = 1e4;
|
|
1323
|
+
var CONVERSATION_MUTATION_WAIT_MS = 1e4;
|
|
1324
|
+
var CONVERSATION_MUTATION_RETRY_MS = 25;
|
|
1325
|
+
var InvalidConversationRecordError = class extends Error {
|
|
1326
|
+
constructor(conversationId) {
|
|
1327
|
+
super(`Conversation record is invalid for ${conversationId}`);
|
|
1328
|
+
this.name = "InvalidConversationRecordError";
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
function isInvalidConversationRecordError(error) {
|
|
1332
|
+
return error instanceof InvalidConversationRecordError;
|
|
1333
|
+
}
|
|
1334
|
+
var ConversationMutationFencedError = class extends Error {
|
|
1335
|
+
constructor(conversationId) {
|
|
1336
|
+
super(
|
|
1337
|
+
`Conversation mutation lock was lost before write for ${conversationId}`
|
|
1338
|
+
);
|
|
1339
|
+
this.name = "ConversationMutationFencedError";
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
var CONVERSATION_BY_ACTIVITY_INDEX_KEY = `${CONVERSATION_PREFIX}:by-activity`;
|
|
1343
|
+
var CONVERSATION_ACTIVE_INDEX_KEY = `${CONVERSATION_PREFIX}:active`;
|
|
1344
|
+
var CONVERSATION_WORK_LEASE_TTL_MS = 9e4;
|
|
1345
|
+
var CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15e3;
|
|
1346
|
+
var CONVERSATION_WORK_STALE_ENQUEUE_MS = 6e4;
|
|
1347
|
+
var CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
|
|
1348
|
+
function conversationKey(conversationId) {
|
|
1349
|
+
return `${CONVERSATION_PREFIX}:${conversationId}`;
|
|
1350
|
+
}
|
|
1351
|
+
function indexLockKey(indexKey) {
|
|
1352
|
+
return `${indexKey}:lock`;
|
|
1353
|
+
}
|
|
1354
|
+
function mutationLockKey(conversationId) {
|
|
1355
|
+
return `${CONVERSATION_PREFIX}:mutation:${conversationId}`;
|
|
1356
|
+
}
|
|
1357
|
+
function now() {
|
|
1358
|
+
return Date.now();
|
|
1359
|
+
}
|
|
1360
|
+
function compareMessages(left, right) {
|
|
1361
|
+
return left.createdAtMs - right.createdAtMs || left.receivedAtMs - right.receivedAtMs || left.inboundMessageId.localeCompare(right.inboundMessageId);
|
|
1362
|
+
}
|
|
1363
|
+
function inputHasAttachments(input) {
|
|
1364
|
+
return Array.isArray(input.attachments) && input.attachments.length > 0;
|
|
1365
|
+
}
|
|
1366
|
+
function upgradedPendingMessage(stored, duplicate) {
|
|
1367
|
+
if (!inputHasAttachments(duplicate.input) || inputHasAttachments(stored.input)) {
|
|
1368
|
+
return stored;
|
|
1369
|
+
}
|
|
1370
|
+
return { ...stored, input: duplicate.input };
|
|
1371
|
+
}
|
|
1372
|
+
function compareIndexDescending(left, right) {
|
|
1373
|
+
return right.score - left.score || right.conversationId.localeCompare(left.conversationId);
|
|
1374
|
+
}
|
|
1375
|
+
function compareIndexAscending(left, right) {
|
|
1376
|
+
return left.score - right.score || left.conversationId.localeCompare(right.conversationId);
|
|
1377
|
+
}
|
|
1378
|
+
function uniqueStrings(values) {
|
|
1379
|
+
return [...new Set(values)];
|
|
1380
|
+
}
|
|
1381
|
+
function normalizeSource(value) {
|
|
1382
|
+
if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "resource_event" || value === "scheduler" || value === "slack") {
|
|
1383
|
+
return value;
|
|
1384
|
+
}
|
|
1385
|
+
return void 0;
|
|
1386
|
+
}
|
|
1387
|
+
function normalizeExecutionStatus(value) {
|
|
1388
|
+
if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
|
|
1389
|
+
return value;
|
|
1390
|
+
}
|
|
1391
|
+
return void 0;
|
|
1392
|
+
}
|
|
1393
|
+
function normalizeMetadata2(value) {
|
|
1394
|
+
if (!isRecord(value)) {
|
|
1395
|
+
return void 0;
|
|
1396
|
+
}
|
|
1397
|
+
return value;
|
|
1398
|
+
}
|
|
1399
|
+
function normalizeInput(value) {
|
|
1400
|
+
if (!isRecord(value)) {
|
|
1401
|
+
return void 0;
|
|
1402
|
+
}
|
|
1403
|
+
const text = typeof value.text === "string" ? value.text : void 0;
|
|
1404
|
+
const attachments = Array.isArray(value.attachments) ? [...value.attachments] : void 0;
|
|
1405
|
+
if (text === void 0 || !text.trim() && !attachments?.length) {
|
|
1406
|
+
return void 0;
|
|
1407
|
+
}
|
|
1408
|
+
return {
|
|
1409
|
+
text,
|
|
1410
|
+
authorId: toOptionalString(value.authorId),
|
|
1411
|
+
attachments,
|
|
1412
|
+
metadata: normalizeMetadata2(value.metadata)
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
function normalizeMessage(value) {
|
|
1416
|
+
if (!isRecord(value)) {
|
|
1417
|
+
return void 0;
|
|
1418
|
+
}
|
|
1419
|
+
const conversationId = toOptionalString(value.conversationId);
|
|
1420
|
+
const inboundMessageId = toOptionalString(value.inboundMessageId);
|
|
1421
|
+
const source = normalizeSource(value.source);
|
|
1422
|
+
const destination = parseDestination(value.destination);
|
|
1423
|
+
const createdAtMs = toOptionalNumber(value.createdAtMs);
|
|
1424
|
+
const receivedAtMs = toOptionalNumber(value.receivedAtMs);
|
|
1425
|
+
const input = normalizeInput(value.input);
|
|
1426
|
+
if (!conversationId || !destination || !inboundMessageId || !source || typeof createdAtMs !== "number" || typeof receivedAtMs !== "number" || !input) {
|
|
1427
|
+
return void 0;
|
|
1428
|
+
}
|
|
1429
|
+
return {
|
|
1430
|
+
conversationId,
|
|
1431
|
+
destination,
|
|
1432
|
+
inboundMessageId,
|
|
1433
|
+
source,
|
|
1434
|
+
createdAtMs,
|
|
1435
|
+
receivedAtMs,
|
|
1436
|
+
input,
|
|
1437
|
+
injectedAtMs: toOptionalNumber(value.injectedAtMs),
|
|
1438
|
+
attemptCount: toOptionalNumber(value.attemptCount)
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function isFinalAttempt(message) {
|
|
1442
|
+
return (message.attemptCount ?? 0) >= CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS - 1;
|
|
1443
|
+
}
|
|
1444
|
+
function normalizeActor(value) {
|
|
1445
|
+
return parseStoredSlackActor(value);
|
|
1446
|
+
}
|
|
1447
|
+
function normalizeLease(value) {
|
|
1448
|
+
if (!isRecord(value)) {
|
|
1449
|
+
return void 0;
|
|
1450
|
+
}
|
|
1451
|
+
const token = toOptionalString(value.token);
|
|
1452
|
+
const acquiredAtMs = toOptionalNumber(value.acquiredAtMs);
|
|
1453
|
+
const lastCheckInAtMs = toOptionalNumber(value.lastCheckInAtMs);
|
|
1454
|
+
const expiresAtMs = toOptionalNumber(value.expiresAtMs);
|
|
1455
|
+
if (!token || typeof acquiredAtMs !== "number" || typeof lastCheckInAtMs !== "number" || typeof expiresAtMs !== "number") {
|
|
1456
|
+
return void 0;
|
|
1457
|
+
}
|
|
1458
|
+
return {
|
|
1459
|
+
token,
|
|
1460
|
+
acquiredAtMs,
|
|
1461
|
+
lastCheckInAtMs,
|
|
1462
|
+
expiresAtMs
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
function normalizeExecution(conversationId, value) {
|
|
1466
|
+
if (!isRecord(value)) {
|
|
1467
|
+
return void 0;
|
|
1468
|
+
}
|
|
1469
|
+
const status = normalizeExecutionStatus(value.status);
|
|
1470
|
+
if (!status) {
|
|
1471
|
+
return void 0;
|
|
1472
|
+
}
|
|
1473
|
+
const pendingMessages2 = Array.isArray(value.pendingMessages) ? value.pendingMessages.map(normalizeMessage).filter((message) => Boolean(message)).filter((message) => message.conversationId === conversationId).filter((message) => message.injectedAtMs === void 0).sort(compareMessages) : [];
|
|
1474
|
+
const inboundMessageIds = Array.isArray(value.inboundMessageIds) ? uniqueStrings(
|
|
1475
|
+
value.inboundMessageIds.map((id) => typeof id === "string" ? id : void 0).filter((id) => Boolean(id))
|
|
1476
|
+
) : [];
|
|
1477
|
+
const lease = normalizeLease(value.lease);
|
|
1478
|
+
const normalizedStatus = status === "idle" && lease ? "running" : status === "idle" && pendingMessages2.length > 0 ? "pending" : status;
|
|
1479
|
+
return {
|
|
1480
|
+
status: normalizedStatus,
|
|
1481
|
+
inboundMessageIds: uniqueStrings([
|
|
1482
|
+
...inboundMessageIds,
|
|
1483
|
+
...pendingMessages2.map((message) => message.inboundMessageId)
|
|
1484
|
+
]),
|
|
1485
|
+
pendingCount: pendingMessages2.length,
|
|
1486
|
+
pendingMessages: pendingMessages2,
|
|
1487
|
+
lease,
|
|
1488
|
+
lastCheckpointAtMs: toOptionalNumber(value.lastCheckpointAtMs),
|
|
1489
|
+
lastEnqueuedAtMs: toOptionalNumber(value.lastEnqueuedAtMs),
|
|
1490
|
+
runId: toOptionalString(value.runId),
|
|
1491
|
+
updatedAtMs: toOptionalNumber(value.updatedAtMs)
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function normalizeConversation(conversationId, value) {
|
|
1495
|
+
if (!isRecord(value) || value.schemaVersion !== CONVERSATION_SCHEMA_VERSION) {
|
|
1496
|
+
return void 0;
|
|
1497
|
+
}
|
|
1498
|
+
const storedConversationId = toOptionalString(value.conversationId);
|
|
1499
|
+
const createdAtMs = toOptionalNumber(value.createdAtMs);
|
|
1500
|
+
const lastActivityAtMs = toOptionalNumber(value.lastActivityAtMs);
|
|
1501
|
+
const updatedAtMs = toOptionalNumber(value.updatedAtMs);
|
|
1502
|
+
const execution = normalizeExecution(conversationId, value.execution);
|
|
1503
|
+
const destination = value.destination === void 0 ? void 0 : parseDestination(value.destination);
|
|
1504
|
+
if (storedConversationId !== conversationId || typeof createdAtMs !== "number" || typeof lastActivityAtMs !== "number" || typeof updatedAtMs !== "number" || !execution || value.destination !== void 0 && !destination) {
|
|
1505
|
+
return void 0;
|
|
1506
|
+
}
|
|
1507
|
+
if (execution.pendingMessages.length > 0 && (!destination || execution.pendingMessages.some(
|
|
1508
|
+
(message) => !sameDestination(message.destination, destination)
|
|
1509
|
+
))) {
|
|
1510
|
+
return void 0;
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
schemaVersion: CONVERSATION_SCHEMA_VERSION,
|
|
1514
|
+
conversationId,
|
|
1515
|
+
createdAtMs,
|
|
1516
|
+
lastActivityAtMs,
|
|
1517
|
+
updatedAtMs,
|
|
1518
|
+
execution,
|
|
1519
|
+
...destination ? { destination } : {},
|
|
1520
|
+
...toOptionalString(value.title) ? { title: toOptionalString(value.title) } : {},
|
|
1521
|
+
...toOptionalString(value.channelName) ? { channelName: toOptionalString(value.channelName) } : {},
|
|
1522
|
+
...normalizeActor(value.actor) ? { actor: normalizeActor(value.actor) } : {},
|
|
1523
|
+
...normalizeSource(value.source) ? { source: normalizeSource(value.source) } : {}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
function emptyConversation(args) {
|
|
1527
|
+
return {
|
|
1528
|
+
schemaVersion: CONVERSATION_SCHEMA_VERSION,
|
|
1529
|
+
conversationId: args.conversationId,
|
|
1530
|
+
createdAtMs: args.nowMs,
|
|
1531
|
+
lastActivityAtMs: args.nowMs,
|
|
1532
|
+
updatedAtMs: args.nowMs,
|
|
1533
|
+
...args.destination ? { destination: args.destination } : {},
|
|
1534
|
+
...args.source ? { source: args.source } : {},
|
|
1535
|
+
execution: {
|
|
1536
|
+
status: "idle",
|
|
1537
|
+
inboundMessageIds: [],
|
|
1538
|
+
pendingCount: 0,
|
|
1539
|
+
pendingMessages: [],
|
|
1540
|
+
updatedAtMs: args.nowMs
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
function isLeaseActive(lease, nowMs) {
|
|
1545
|
+
return Boolean(lease && lease.expiresAtMs > nowMs);
|
|
1546
|
+
}
|
|
1547
|
+
function pendingMessages(conversation) {
|
|
1548
|
+
return [...conversation.execution.pendingMessages].sort(compareMessages);
|
|
1549
|
+
}
|
|
1550
|
+
function isRunnableStatus(status) {
|
|
1551
|
+
return status !== "failed" && status !== "idle";
|
|
1552
|
+
}
|
|
1553
|
+
function hasRunnableWork(conversation) {
|
|
1554
|
+
return isRunnableStatus(conversation.execution.status) || pendingMessages(conversation).length > 0;
|
|
1555
|
+
}
|
|
1556
|
+
function executionWithPendingMessages(execution, pending) {
|
|
1557
|
+
const pendingMessages2 = [...pending].sort(compareMessages);
|
|
1558
|
+
const status = execution.status === "idle" && execution.lease ? "running" : execution.status === "idle" && pendingMessages2.length > 0 ? "pending" : execution.status;
|
|
1559
|
+
return {
|
|
1560
|
+
...execution,
|
|
1561
|
+
status,
|
|
1562
|
+
inboundMessageIds: uniqueStrings([
|
|
1563
|
+
...execution.inboundMessageIds,
|
|
1564
|
+
...pendingMessages2.map((message) => message.inboundMessageId)
|
|
1565
|
+
]),
|
|
1566
|
+
pendingMessages: pendingMessages2,
|
|
1567
|
+
pendingCount: pendingMessages2.length
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
function withExecutionUpdate(conversation, execution, nowMs) {
|
|
1571
|
+
return {
|
|
1572
|
+
...conversation,
|
|
1573
|
+
updatedAtMs: nowMs,
|
|
1574
|
+
execution: {
|
|
1575
|
+
...executionWithPendingMessages(execution, execution.pendingMessages),
|
|
1576
|
+
updatedAtMs: nowMs
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
async function getConnectedState(stateAdapter) {
|
|
1581
|
+
const state = stateAdapter ?? getStateAdapter();
|
|
1582
|
+
await state.connect();
|
|
1583
|
+
return state;
|
|
1584
|
+
}
|
|
1585
|
+
async function sleep2(ms) {
|
|
1586
|
+
await new Promise((resolve) => {
|
|
1587
|
+
const timer = setTimeout(resolve, ms);
|
|
1588
|
+
timer.unref?.();
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
async function withIndexLock(state, indexKey, callback) {
|
|
1592
|
+
const startedAtMs = now();
|
|
1593
|
+
let lock;
|
|
1594
|
+
while (true) {
|
|
1595
|
+
lock = await state.acquireLock(
|
|
1596
|
+
indexLockKey(indexKey),
|
|
1597
|
+
CONVERSATION_INDEX_LOCK_TTL_MS
|
|
1598
|
+
);
|
|
1599
|
+
if (lock) {
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
if (now() - startedAtMs >= CONVERSATION_INDEX_LOCK_WAIT_MS) {
|
|
1603
|
+
throw new Error(
|
|
1604
|
+
`Could not acquire conversation index lock for ${indexKey}`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
await sleep2(CONVERSATION_INDEX_LOCK_RETRY_MS);
|
|
1608
|
+
}
|
|
1609
|
+
try {
|
|
1610
|
+
return await callback();
|
|
1611
|
+
} finally {
|
|
1612
|
+
await state.releaseLock(lock);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function normalizeIndexEntry(value) {
|
|
1616
|
+
if (!isRecord(value)) {
|
|
1617
|
+
return void 0;
|
|
1618
|
+
}
|
|
1619
|
+
const conversationId = toOptionalString(value.conversationId);
|
|
1620
|
+
const score = toOptionalNumber(value.score);
|
|
1621
|
+
if (!conversationId || typeof score !== "number") {
|
|
1622
|
+
return void 0;
|
|
1623
|
+
}
|
|
1624
|
+
return { conversationId, score };
|
|
1625
|
+
}
|
|
1626
|
+
function uniqueIndexEntries(value) {
|
|
1627
|
+
if (!Array.isArray(value)) {
|
|
1628
|
+
return [];
|
|
1629
|
+
}
|
|
1630
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1631
|
+
for (const item of value) {
|
|
1632
|
+
const entry = normalizeIndexEntry(item);
|
|
1633
|
+
if (!entry) {
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
const existing = entries.get(entry.conversationId);
|
|
1637
|
+
if (!existing || entry.score > existing.score) {
|
|
1638
|
+
entries.set(entry.conversationId, entry);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return [...entries.values()];
|
|
1642
|
+
}
|
|
1643
|
+
function retainedIndexEntries(indexKey, entries) {
|
|
1644
|
+
if (indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
|
|
1645
|
+
return entries.sort(compareIndexDescending).slice(0, CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH);
|
|
1646
|
+
}
|
|
1647
|
+
if (indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
|
|
1648
|
+
return entries.sort(compareIndexAscending);
|
|
1649
|
+
}
|
|
1650
|
+
throw new Error(`Unknown conversation index ${indexKey}`);
|
|
1651
|
+
}
|
|
1652
|
+
function redisIndexKey(indexKey) {
|
|
1653
|
+
const prefix = getChatConfig().state.keyPrefix;
|
|
1654
|
+
return [...prefix ? [prefix] : [], indexKey].join(":");
|
|
1655
|
+
}
|
|
1656
|
+
function parseRedisIndexEntries(values) {
|
|
1657
|
+
if (!Array.isArray(values)) {
|
|
1658
|
+
return [];
|
|
1659
|
+
}
|
|
1660
|
+
const entries = [];
|
|
1661
|
+
for (let index = 0; index < values.length; index += 2) {
|
|
1662
|
+
const conversationId = toOptionalString(values[index]);
|
|
1663
|
+
const score = typeof values[index + 1] === "number" ? values[index + 1] : Number(values[index + 1]);
|
|
1664
|
+
if (!conversationId || !Number.isFinite(score)) {
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
entries.push({ conversationId, score });
|
|
1668
|
+
}
|
|
1669
|
+
return entries;
|
|
1670
|
+
}
|
|
1671
|
+
function redisConversationIndexStore(client) {
|
|
1672
|
+
const upsertBoundedActivityScript = `
|
|
1673
|
+
redis.call("ZADD", KEYS[1], ARGV[1], ARGV[2])
|
|
1674
|
+
redis.call("PEXPIRE", KEYS[1], ARGV[3])
|
|
1675
|
+
local extra = redis.call("ZCARD", KEYS[1]) - tonumber(ARGV[4])
|
|
1676
|
+
if extra > 0 then
|
|
1677
|
+
redis.call("ZREMRANGEBYRANK", KEYS[1], 0, extra - 1)
|
|
1678
|
+
end
|
|
1679
|
+
return 1
|
|
1680
|
+
`;
|
|
1681
|
+
return {
|
|
1682
|
+
async list(args) {
|
|
1683
|
+
const key = redisIndexKey(args.indexKey);
|
|
1684
|
+
const limit = args.limit;
|
|
1685
|
+
const offset = Math.max(0, args.offset ?? 0);
|
|
1686
|
+
if (limit === 0) {
|
|
1687
|
+
return [];
|
|
1688
|
+
}
|
|
1689
|
+
const values = args.scoreMax !== void 0 ? await client.sendCommand([
|
|
1690
|
+
"ZRANGEBYSCORE",
|
|
1691
|
+
key,
|
|
1692
|
+
"-inf",
|
|
1693
|
+
String(args.scoreMax),
|
|
1694
|
+
"WITHSCORES",
|
|
1695
|
+
...limit !== void 0 || offset > 0 ? ["LIMIT", String(offset), String(limit ?? 1e9)] : []
|
|
1696
|
+
]) : await client.sendCommand([
|
|
1697
|
+
args.order === "asc" ? "ZRANGE" : "ZREVRANGE",
|
|
1698
|
+
key,
|
|
1699
|
+
String(offset),
|
|
1700
|
+
String(
|
|
1701
|
+
limit === void 0 ? -1 : offset + Math.max(0, limit - 1)
|
|
1702
|
+
),
|
|
1703
|
+
"WITHSCORES"
|
|
1704
|
+
]);
|
|
1705
|
+
return parseRedisIndexEntries(values);
|
|
1706
|
+
},
|
|
1707
|
+
async remove(args) {
|
|
1708
|
+
await client.sendCommand([
|
|
1709
|
+
"ZREM",
|
|
1710
|
+
redisIndexKey(args.indexKey),
|
|
1711
|
+
args.conversationId
|
|
1712
|
+
]);
|
|
1713
|
+
},
|
|
1714
|
+
async upsert(args) {
|
|
1715
|
+
const key = redisIndexKey(args.indexKey);
|
|
1716
|
+
if (args.indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
|
|
1717
|
+
await client.sendCommand([
|
|
1718
|
+
"EVAL",
|
|
1719
|
+
upsertBoundedActivityScript,
|
|
1720
|
+
"1",
|
|
1721
|
+
key,
|
|
1722
|
+
String(args.score),
|
|
1723
|
+
args.conversationId,
|
|
1724
|
+
String(JUNIOR_THREAD_STATE_TTL_MS),
|
|
1725
|
+
String(CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH)
|
|
1726
|
+
]);
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
if (args.indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
|
|
1730
|
+
await client.sendCommand([
|
|
1731
|
+
"ZADD",
|
|
1732
|
+
key,
|
|
1733
|
+
String(args.score),
|
|
1734
|
+
args.conversationId
|
|
1735
|
+
]);
|
|
1736
|
+
await client.sendCommand([
|
|
1737
|
+
"PEXPIRE",
|
|
1738
|
+
key,
|
|
1739
|
+
String(JUNIOR_THREAD_STATE_TTL_MS)
|
|
1740
|
+
]);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
throw new Error(`Unknown conversation index ${args.indexKey}`);
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
function emulatedConversationIndexStore(state) {
|
|
1748
|
+
const readIndex = async (indexKey) => uniqueIndexEntries(await state.get(indexKey));
|
|
1749
|
+
const writeIndex = async (indexKey, entries) => {
|
|
1750
|
+
await state.set(indexKey, entries, JUNIOR_THREAD_STATE_TTL_MS);
|
|
1751
|
+
};
|
|
1752
|
+
return {
|
|
1753
|
+
async list(args) {
|
|
1754
|
+
const entries = (await readIndex(args.indexKey)).filter(
|
|
1755
|
+
(entry) => args.scoreMax === void 0 ? true : entry.score <= args.scoreMax
|
|
1756
|
+
).sort(
|
|
1757
|
+
args.order === "asc" ? compareIndexAscending : compareIndexDescending
|
|
1758
|
+
);
|
|
1759
|
+
const offset = Math.max(0, args.offset ?? 0);
|
|
1760
|
+
return entries.slice(
|
|
1761
|
+
offset,
|
|
1762
|
+
args.limit === void 0 ? entries.length : offset + args.limit
|
|
1763
|
+
);
|
|
1764
|
+
},
|
|
1765
|
+
async remove(args) {
|
|
1766
|
+
await withIndexLock(state, args.indexKey, async () => {
|
|
1767
|
+
const entries = await readIndex(args.indexKey);
|
|
1768
|
+
const next = entries.filter(
|
|
1769
|
+
(entry) => entry.conversationId !== args.conversationId
|
|
1770
|
+
);
|
|
1771
|
+
if (next.length === entries.length) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
await writeIndex(args.indexKey, next);
|
|
1775
|
+
});
|
|
1776
|
+
},
|
|
1777
|
+
async upsert(args) {
|
|
1778
|
+
await withIndexLock(state, args.indexKey, async () => {
|
|
1779
|
+
const entries = await readIndex(args.indexKey);
|
|
1780
|
+
const withoutCurrent = entries.filter(
|
|
1781
|
+
(entry) => entry.conversationId !== args.conversationId
|
|
1782
|
+
);
|
|
1783
|
+
const next = retainedIndexEntries(args.indexKey, [
|
|
1784
|
+
...withoutCurrent,
|
|
1785
|
+
{ conversationId: args.conversationId, score: args.score }
|
|
1786
|
+
]);
|
|
1787
|
+
await writeIndex(args.indexKey, next);
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
async function getConversationIndexStore(state) {
|
|
1793
|
+
const redisStateAdapter = await getDefaultRedisStateAdapterFor(state);
|
|
1794
|
+
if (redisStateAdapter) {
|
|
1795
|
+
return redisConversationIndexStore(redisStateAdapter.getClient());
|
|
1796
|
+
}
|
|
1797
|
+
return emulatedConversationIndexStore(state);
|
|
1798
|
+
}
|
|
1799
|
+
async function upsertIndexEntry(args) {
|
|
1800
|
+
const index = await getConversationIndexStore(args.state);
|
|
1801
|
+
await index.upsert({
|
|
1802
|
+
conversationId: args.conversationId,
|
|
1803
|
+
indexKey: args.indexKey,
|
|
1804
|
+
score: args.score
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
async function removeIndexEntry(args) {
|
|
1808
|
+
const index = await getConversationIndexStore(args.state);
|
|
1809
|
+
await index.remove({
|
|
1810
|
+
conversationId: args.conversationId,
|
|
1811
|
+
indexKey: args.indexKey
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
async function acquireMutationLock(state, conversationId) {
|
|
1815
|
+
const startedAtMs = now();
|
|
1816
|
+
while (true) {
|
|
1817
|
+
const lock = await state.acquireLock(
|
|
1818
|
+
mutationLockKey(conversationId),
|
|
1819
|
+
CONVERSATION_MUTATION_LOCK_TTL_MS
|
|
1820
|
+
);
|
|
1821
|
+
if (lock) {
|
|
1822
|
+
return lock;
|
|
1823
|
+
}
|
|
1824
|
+
if (now() - startedAtMs >= CONVERSATION_MUTATION_WAIT_MS) {
|
|
1825
|
+
throw new Error(
|
|
1826
|
+
`Could not acquire conversation mutation lock for ${conversationId}`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
await sleep2(CONVERSATION_MUTATION_RETRY_MS);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
async function withConversationMutation(args, callback) {
|
|
1833
|
+
const state = await getConnectedState(args.state);
|
|
1834
|
+
const lock = await acquireMutationLock(state, args.conversationId);
|
|
1835
|
+
try {
|
|
1836
|
+
return await callback(state, lock);
|
|
1837
|
+
} finally {
|
|
1838
|
+
await state.releaseLock(lock);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
async function readConversation(state, conversationId) {
|
|
1842
|
+
const raw = await state.get(conversationKey(conversationId));
|
|
1843
|
+
if (raw == null) {
|
|
1844
|
+
return void 0;
|
|
1845
|
+
}
|
|
1846
|
+
const conversation = normalizeConversation(conversationId, raw);
|
|
1847
|
+
if (!conversation) {
|
|
1848
|
+
throw new InvalidConversationRecordError(conversationId);
|
|
1849
|
+
}
|
|
1850
|
+
return conversation;
|
|
1851
|
+
}
|
|
1852
|
+
async function writeConversation(state, lock, conversation) {
|
|
1853
|
+
const execution = executionWithPendingMessages(
|
|
1854
|
+
conversation.execution,
|
|
1855
|
+
conversation.execution.pendingMessages
|
|
1856
|
+
);
|
|
1857
|
+
const next = {
|
|
1858
|
+
...conversation,
|
|
1859
|
+
execution
|
|
1860
|
+
};
|
|
1861
|
+
const fenced = await state.extendLock(
|
|
1862
|
+
lock,
|
|
1863
|
+
CONVERSATION_MUTATION_LOCK_TTL_MS
|
|
1864
|
+
);
|
|
1865
|
+
if (!fenced) {
|
|
1866
|
+
throw new ConversationMutationFencedError(next.conversationId);
|
|
1867
|
+
}
|
|
1868
|
+
await state.set(
|
|
1869
|
+
conversationKey(next.conversationId),
|
|
1870
|
+
next,
|
|
1871
|
+
JUNIOR_THREAD_STATE_TTL_MS
|
|
1872
|
+
);
|
|
1873
|
+
await upsertIndexEntry({
|
|
1874
|
+
state,
|
|
1875
|
+
indexKey: CONVERSATION_BY_ACTIVITY_INDEX_KEY,
|
|
1876
|
+
conversationId: next.conversationId,
|
|
1877
|
+
score: next.lastActivityAtMs
|
|
1878
|
+
});
|
|
1879
|
+
if (!hasRunnableWork(next)) {
|
|
1880
|
+
await removeIndexEntry({
|
|
1881
|
+
state,
|
|
1882
|
+
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
1883
|
+
conversationId: next.conversationId
|
|
1884
|
+
});
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
await upsertIndexEntry({
|
|
1888
|
+
state,
|
|
1889
|
+
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
1890
|
+
conversationId: next.conversationId,
|
|
1891
|
+
score: next.execution.updatedAtMs ?? next.updatedAtMs
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
function assertSameConversationDestination(args) {
|
|
1895
|
+
if (!args.current || sameDestination(args.current, args.next)) {
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
throw new Error(
|
|
1899
|
+
`Conversation destination changed for ${args.conversationId}`
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
function conversationWorkState(conversation) {
|
|
1903
|
+
const lease = conversation.execution.lease;
|
|
1904
|
+
return {
|
|
1905
|
+
...conversation,
|
|
1906
|
+
lastEnqueuedAtMs: conversation.execution.lastEnqueuedAtMs,
|
|
1907
|
+
...lease ? {
|
|
1908
|
+
lease: {
|
|
1909
|
+
acquiredAtMs: lease.acquiredAtMs,
|
|
1910
|
+
lastCheckInAtMs: lease.lastCheckInAtMs,
|
|
1911
|
+
leaseExpiresAtMs: lease.expiresAtMs,
|
|
1912
|
+
leaseToken: lease.token
|
|
1913
|
+
}
|
|
1914
|
+
} : {},
|
|
1915
|
+
messages: pendingMessages(conversation),
|
|
1916
|
+
needsRun: hasRunnableWork(conversation)
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
async function getConversation(args) {
|
|
1920
|
+
const state = await getConnectedState(args.state);
|
|
1921
|
+
return await readConversation(state, args.conversationId);
|
|
1922
|
+
}
|
|
1923
|
+
async function getConversationWorkState(args) {
|
|
1924
|
+
const conversation = await getConversation(args);
|
|
1925
|
+
return conversation ? conversationWorkState(conversation) : void 0;
|
|
1926
|
+
}
|
|
1927
|
+
async function appendInboundMessage(args) {
|
|
1928
|
+
const nowMs = args.nowMs ?? now();
|
|
1929
|
+
return await withConversationMutation(
|
|
1930
|
+
{ conversationId: args.message.conversationId, state: args.state },
|
|
1931
|
+
async (state, lock) => {
|
|
1932
|
+
const current = await readConversation(state, args.message.conversationId) ?? emptyConversation({
|
|
1933
|
+
conversationId: args.message.conversationId,
|
|
1934
|
+
destination: args.message.destination,
|
|
1935
|
+
nowMs,
|
|
1936
|
+
source: args.message.source
|
|
1937
|
+
});
|
|
1938
|
+
assertSameConversationDestination({
|
|
1939
|
+
conversationId: args.message.conversationId,
|
|
1940
|
+
current: current.destination,
|
|
1941
|
+
next: args.message.destination
|
|
1942
|
+
});
|
|
1943
|
+
const existingPending = current.execution.pendingMessages.some(
|
|
1944
|
+
(message) => message.inboundMessageId === args.message.inboundMessageId
|
|
1945
|
+
);
|
|
1946
|
+
const existing = current.execution.inboundMessageIds.includes(
|
|
1947
|
+
args.message.inboundMessageId
|
|
1948
|
+
);
|
|
1949
|
+
if (existing) {
|
|
1950
|
+
if (!existingPending) {
|
|
1951
|
+
return { status: "duplicate" };
|
|
1952
|
+
}
|
|
1953
|
+
const nextStatus = current.execution.status === "idle" ? "pending" : current.execution.status;
|
|
1954
|
+
await writeConversation(
|
|
1955
|
+
state,
|
|
1956
|
+
lock,
|
|
1957
|
+
withExecutionUpdate(
|
|
1958
|
+
current,
|
|
1959
|
+
{
|
|
1960
|
+
...current.execution,
|
|
1961
|
+
status: nextStatus,
|
|
1962
|
+
pendingMessages: current.execution.pendingMessages.map(
|
|
1963
|
+
(message) => message.inboundMessageId === args.message.inboundMessageId ? upgradedPendingMessage(message, args.message) : message
|
|
1964
|
+
)
|
|
1965
|
+
},
|
|
1966
|
+
nowMs
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
return { status: "duplicate" };
|
|
1970
|
+
}
|
|
1971
|
+
const status = current.execution.lease && current.execution.status === "running" ? "running" : current.execution.lease ? "awaiting_resume" : "pending";
|
|
1972
|
+
const next = {
|
|
1973
|
+
...current,
|
|
1974
|
+
destination: current.destination ?? args.message.destination,
|
|
1975
|
+
source: current.source ?? args.message.source,
|
|
1976
|
+
lastActivityAtMs: nowMs
|
|
1977
|
+
};
|
|
1978
|
+
await writeConversation(
|
|
1979
|
+
state,
|
|
1980
|
+
lock,
|
|
1981
|
+
withExecutionUpdate(
|
|
1982
|
+
next,
|
|
1983
|
+
{
|
|
1984
|
+
...current.execution,
|
|
1985
|
+
status,
|
|
1986
|
+
inboundMessageIds: [
|
|
1987
|
+
...current.execution.inboundMessageIds,
|
|
1988
|
+
args.message.inboundMessageId
|
|
1989
|
+
],
|
|
1990
|
+
pendingMessages: [
|
|
1991
|
+
...current.execution.pendingMessages,
|
|
1992
|
+
args.message
|
|
1993
|
+
].sort(compareMessages)
|
|
1994
|
+
},
|
|
1995
|
+
nowMs
|
|
1996
|
+
)
|
|
1997
|
+
);
|
|
1998
|
+
return { status: "appended" };
|
|
1999
|
+
}
|
|
2000
|
+
);
|
|
2001
|
+
}
|
|
2002
|
+
async function requestConversationWork(args) {
|
|
2003
|
+
const nowMs = args.nowMs ?? now();
|
|
2004
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2005
|
+
const existing = await readConversation(state, args.conversationId);
|
|
2006
|
+
if (existing) {
|
|
2007
|
+
assertSameConversationDestination({
|
|
2008
|
+
conversationId: args.conversationId,
|
|
2009
|
+
current: existing.destination,
|
|
2010
|
+
next: args.destination
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
const current = existing ?? emptyConversation({
|
|
2014
|
+
conversationId: args.conversationId,
|
|
2015
|
+
destination: args.destination,
|
|
2016
|
+
nowMs
|
|
2017
|
+
});
|
|
2018
|
+
const status = current.execution.lease ? "awaiting_resume" : "pending";
|
|
2019
|
+
await writeConversation(
|
|
2020
|
+
state,
|
|
2021
|
+
lock,
|
|
2022
|
+
withExecutionUpdate(
|
|
2023
|
+
{
|
|
2024
|
+
...current,
|
|
2025
|
+
destination: current.destination ?? args.destination
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
...current.execution,
|
|
2029
|
+
status
|
|
2030
|
+
},
|
|
2031
|
+
nowMs
|
|
2032
|
+
)
|
|
2033
|
+
);
|
|
2034
|
+
return { status: existing === void 0 ? "created" : "updated" };
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
async function markConversationWorkEnqueued(args) {
|
|
2038
|
+
const nowMs = args.nowMs ?? now();
|
|
2039
|
+
await withConversationMutation(args, async (state, lock) => {
|
|
2040
|
+
const current = await readConversation(state, args.conversationId);
|
|
2041
|
+
if (!current) {
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
await writeConversation(
|
|
2045
|
+
state,
|
|
2046
|
+
lock,
|
|
2047
|
+
withExecutionUpdate(
|
|
2048
|
+
current,
|
|
2049
|
+
{
|
|
2050
|
+
...current.execution,
|
|
2051
|
+
lastEnqueuedAtMs: nowMs
|
|
2052
|
+
},
|
|
2053
|
+
nowMs
|
|
2054
|
+
)
|
|
2055
|
+
);
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
async function clearConsumedConversationWake(args) {
|
|
2059
|
+
const nowMs = args.nowMs ?? now();
|
|
2060
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2061
|
+
const current = await readConversation(state, args.conversationId);
|
|
2062
|
+
if (!current || hasRunnableWork(current) || current.execution.lastEnqueuedAtMs === void 0) {
|
|
2063
|
+
return false;
|
|
2064
|
+
}
|
|
2065
|
+
await writeConversation(
|
|
2066
|
+
state,
|
|
2067
|
+
lock,
|
|
2068
|
+
withExecutionUpdate(
|
|
2069
|
+
current,
|
|
2070
|
+
{
|
|
2071
|
+
...current.execution,
|
|
2072
|
+
lastEnqueuedAtMs: void 0
|
|
2073
|
+
},
|
|
2074
|
+
nowMs
|
|
2075
|
+
)
|
|
2076
|
+
);
|
|
2077
|
+
return true;
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
async function startConversationWork(args) {
|
|
2081
|
+
const nowMs = args.nowMs ?? now();
|
|
2082
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2083
|
+
const current = await readConversation(state, args.conversationId);
|
|
2084
|
+
if (!current) {
|
|
2085
|
+
return { status: "no_work" };
|
|
2086
|
+
}
|
|
2087
|
+
if (isLeaseActive(current.execution.lease, nowMs)) {
|
|
2088
|
+
return {
|
|
2089
|
+
status: "active",
|
|
2090
|
+
leaseExpiresAtMs: current.execution.lease.expiresAtMs
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
if (!hasRunnableWork(current)) {
|
|
2094
|
+
return { status: "no_work" };
|
|
2095
|
+
}
|
|
2096
|
+
const lease = {
|
|
2097
|
+
token: randomUUID(),
|
|
2098
|
+
acquiredAtMs: nowMs,
|
|
2099
|
+
lastCheckInAtMs: nowMs,
|
|
2100
|
+
expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
|
|
2101
|
+
};
|
|
2102
|
+
await writeConversation(
|
|
2103
|
+
state,
|
|
2104
|
+
lock,
|
|
2105
|
+
withExecutionUpdate(
|
|
2106
|
+
current,
|
|
2107
|
+
{
|
|
2108
|
+
...current.execution,
|
|
2109
|
+
lease,
|
|
2110
|
+
status: "running",
|
|
2111
|
+
runId: current.execution.runId ?? randomUUID(),
|
|
2112
|
+
lastEnqueuedAtMs: void 0
|
|
2113
|
+
},
|
|
2114
|
+
nowMs
|
|
2115
|
+
)
|
|
2116
|
+
);
|
|
2117
|
+
return {
|
|
2118
|
+
status: "acquired",
|
|
2119
|
+
leaseToken: lease.token,
|
|
2120
|
+
leaseExpiresAtMs: lease.expiresAtMs
|
|
2121
|
+
};
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
async function checkInConversationWork(args) {
|
|
2125
|
+
const nowMs = args.nowMs ?? now();
|
|
2126
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2127
|
+
const current = await readConversation(state, args.conversationId);
|
|
2128
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2129
|
+
return false;
|
|
2130
|
+
}
|
|
2131
|
+
await writeConversation(
|
|
2132
|
+
state,
|
|
2133
|
+
lock,
|
|
2134
|
+
withExecutionUpdate(
|
|
2135
|
+
current,
|
|
2136
|
+
{
|
|
2137
|
+
...current.execution,
|
|
2138
|
+
lease: {
|
|
2139
|
+
...current.execution.lease,
|
|
2140
|
+
lastCheckInAtMs: nowMs,
|
|
2141
|
+
expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
|
|
2142
|
+
}
|
|
2143
|
+
},
|
|
2144
|
+
nowMs
|
|
2145
|
+
)
|
|
2146
|
+
);
|
|
2147
|
+
return true;
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
async function drainConversationMailbox(args) {
|
|
2151
|
+
const nowMs = args.nowMs ?? now();
|
|
2152
|
+
const pending = await withConversationMutation(args, async (state) => {
|
|
2153
|
+
const current = await readConversation(state, args.conversationId);
|
|
2154
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2155
|
+
throw new Error(
|
|
2156
|
+
`Conversation lease is not held for ${args.conversationId}`
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
return pendingMessages(current);
|
|
2160
|
+
});
|
|
2161
|
+
if (pending.length === 0) {
|
|
2162
|
+
return [];
|
|
2163
|
+
}
|
|
2164
|
+
const acknowledgedIds = await args.handle(pending);
|
|
2165
|
+
const pendingIds = new Set(
|
|
2166
|
+
pending.map((message) => message.inboundMessageId)
|
|
2167
|
+
);
|
|
2168
|
+
for (const inboundMessageId of acknowledgedIds ?? []) {
|
|
2169
|
+
if (!pendingIds.has(inboundMessageId)) {
|
|
2170
|
+
throw new Error(
|
|
2171
|
+
`Conversation mailbox acknowledgement is not pending for ${args.conversationId}`
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
const drainedIds = new Set(
|
|
2176
|
+
acknowledgedIds ?? pending.map((message) => message.inboundMessageId)
|
|
2177
|
+
);
|
|
2178
|
+
await withConversationMutation(args, async (state, lock) => {
|
|
2179
|
+
const current = await readConversation(state, args.conversationId);
|
|
2180
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2181
|
+
throw new Error(
|
|
2182
|
+
`Conversation lease is not held for ${args.conversationId}`
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
const pendingMessages2 = current.execution.pendingMessages.filter(
|
|
2186
|
+
(message) => !drainedIds.has(message.inboundMessageId)
|
|
2187
|
+
);
|
|
2188
|
+
await writeConversation(
|
|
2189
|
+
state,
|
|
2190
|
+
lock,
|
|
2191
|
+
withExecutionUpdate(
|
|
2192
|
+
current,
|
|
2193
|
+
{
|
|
2194
|
+
...current.execution,
|
|
2195
|
+
status: current.execution.status === "pending" && pendingMessages2.length === 0 ? "running" : current.execution.status,
|
|
2196
|
+
pendingMessages: pendingMessages2
|
|
2197
|
+
},
|
|
2198
|
+
nowMs
|
|
2199
|
+
)
|
|
2200
|
+
);
|
|
2201
|
+
});
|
|
2202
|
+
return pending.filter((message) => drainedIds.has(message.inboundMessageId));
|
|
2203
|
+
}
|
|
2204
|
+
async function ackMessages(args) {
|
|
2205
|
+
const nowMs = args.nowMs ?? now();
|
|
2206
|
+
const inboundMessageIds = new Set(args.inboundMessageIds);
|
|
2207
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2208
|
+
const current = await readConversation(state, args.conversationId);
|
|
2209
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2210
|
+
return false;
|
|
2211
|
+
}
|
|
2212
|
+
if (inboundMessageIds.size === 0) {
|
|
2213
|
+
return true;
|
|
2214
|
+
}
|
|
2215
|
+
const pendingMessages2 = current.execution.pendingMessages.filter(
|
|
2216
|
+
(message) => !inboundMessageIds.has(message.inboundMessageId)
|
|
2217
|
+
);
|
|
2218
|
+
if (pendingMessages2.length === current.execution.pendingMessages.length) {
|
|
2219
|
+
return true;
|
|
2220
|
+
}
|
|
2221
|
+
await writeConversation(
|
|
2222
|
+
state,
|
|
2223
|
+
lock,
|
|
2224
|
+
withExecutionUpdate(
|
|
2225
|
+
current,
|
|
2226
|
+
{
|
|
2227
|
+
...current.execution,
|
|
2228
|
+
pendingMessages: pendingMessages2
|
|
2229
|
+
},
|
|
2230
|
+
nowMs
|
|
2231
|
+
)
|
|
2232
|
+
);
|
|
2233
|
+
return true;
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
async function requestConversationContinuation(args) {
|
|
2237
|
+
const nowMs = args.nowMs ?? now();
|
|
2238
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2239
|
+
const current = await readConversation(state, args.conversationId);
|
|
2240
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2241
|
+
return false;
|
|
2242
|
+
}
|
|
2243
|
+
assertSameConversationDestination({
|
|
2244
|
+
conversationId: args.conversationId,
|
|
2245
|
+
current: current.destination,
|
|
2246
|
+
next: args.destination
|
|
2247
|
+
});
|
|
2248
|
+
await writeConversation(
|
|
2249
|
+
state,
|
|
2250
|
+
lock,
|
|
2251
|
+
withExecutionUpdate(
|
|
2252
|
+
current,
|
|
2253
|
+
{
|
|
2254
|
+
...current.execution,
|
|
2255
|
+
status: "awaiting_resume"
|
|
2256
|
+
},
|
|
2257
|
+
nowMs
|
|
2258
|
+
)
|
|
2259
|
+
);
|
|
2260
|
+
return true;
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
async function releaseConversationWork(args) {
|
|
2264
|
+
const nowMs = args.nowMs ?? now();
|
|
2265
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2266
|
+
const current = await readConversation(state, args.conversationId);
|
|
2267
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2270
|
+
await writeConversation(
|
|
2271
|
+
state,
|
|
2272
|
+
lock,
|
|
2273
|
+
withExecutionUpdate(
|
|
2274
|
+
current,
|
|
2275
|
+
{
|
|
2276
|
+
...current.execution,
|
|
2277
|
+
lease: void 0,
|
|
2278
|
+
status: current.execution.status === "running" ? "pending" : current.execution.status
|
|
2279
|
+
},
|
|
2280
|
+
nowMs
|
|
2281
|
+
)
|
|
2282
|
+
);
|
|
2283
|
+
return true;
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
async function completeConversationWork(args) {
|
|
2287
|
+
const nowMs = args.nowMs ?? now();
|
|
2288
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2289
|
+
const current = await readConversation(state, args.conversationId);
|
|
2290
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2291
|
+
return "lost_lease";
|
|
2292
|
+
}
|
|
2293
|
+
const hasPending = pendingMessages(current).length > 0;
|
|
2294
|
+
const needsRun = current.execution.status === "awaiting_resume";
|
|
2295
|
+
const runnable = needsRun || hasPending;
|
|
2296
|
+
await writeConversation(
|
|
2297
|
+
state,
|
|
2298
|
+
lock,
|
|
2299
|
+
withExecutionUpdate(
|
|
2300
|
+
current,
|
|
2301
|
+
{
|
|
2302
|
+
...current.execution,
|
|
2303
|
+
lease: void 0,
|
|
2304
|
+
status: runnable ? "pending" : "idle",
|
|
2305
|
+
runId: runnable ? current.execution.runId : void 0
|
|
2306
|
+
},
|
|
2307
|
+
nowMs
|
|
2308
|
+
)
|
|
2309
|
+
);
|
|
2310
|
+
return runnable ? "pending" : "completed";
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
async function recordAttemptFailure(args) {
|
|
2314
|
+
const nowMs = args.nowMs ?? now();
|
|
2315
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2316
|
+
const current = await readConversation(state, args.conversationId);
|
|
2317
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2318
|
+
return {
|
|
2319
|
+
status: "lost_lease",
|
|
2320
|
+
pendingCount: 0,
|
|
2321
|
+
deadLetteredMessages: []
|
|
2322
|
+
};
|
|
1233
2323
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
conversationId,
|
|
1246
|
-
turnId,
|
|
1247
|
-
createdAtMs: Date.now(),
|
|
1248
|
-
outcome: assistantMessageDelivered ? "success" : "no_reply"
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
lifecycleTerminalized = true;
|
|
2324
|
+
const pendingIds = new Set(
|
|
2325
|
+
current.execution.pendingMessages.map(
|
|
2326
|
+
(message) => message.inboundMessageId
|
|
2327
|
+
)
|
|
2328
|
+
);
|
|
2329
|
+
if (args.inboundMessageIds.length === 0 || args.inboundMessageIds.some((id) => !pendingIds.has(id))) {
|
|
2330
|
+
return {
|
|
2331
|
+
status: "skipped",
|
|
2332
|
+
pendingCount: current.execution.pendingMessages.length,
|
|
2333
|
+
deadLetteredMessages: []
|
|
2334
|
+
};
|
|
1252
2335
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
try {
|
|
1261
|
-
await scheduleCompletedTasks({
|
|
1262
|
-
conversationId,
|
|
1263
|
-
sessionId: turnId
|
|
1264
|
-
});
|
|
1265
|
-
} catch (error) {
|
|
1266
|
-
logException(
|
|
1267
|
-
error,
|
|
1268
|
-
"plugin_session_completed_task_schedule_failed",
|
|
1269
|
-
logContext,
|
|
1270
|
-
{},
|
|
1271
|
-
"Plugin session.completed task scheduling failed"
|
|
1272
|
-
);
|
|
2336
|
+
const attemptedIds = new Set(args.inboundMessageIds);
|
|
2337
|
+
const deadLetteredMessages = [];
|
|
2338
|
+
const pendingMessages2 = [];
|
|
2339
|
+
for (const message of current.execution.pendingMessages) {
|
|
2340
|
+
if (!attemptedIds.has(message.inboundMessageId)) {
|
|
2341
|
+
pendingMessages2.push(message);
|
|
2342
|
+
continue;
|
|
1273
2343
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
createdAtMs: Date.now(),
|
|
1282
|
-
failureCode: "agent_run_failed"
|
|
1283
|
-
});
|
|
1284
|
-
lifecycleTerminalized = true;
|
|
2344
|
+
const attempted = {
|
|
2345
|
+
...message,
|
|
2346
|
+
attemptCount: (message.attemptCount ?? 0) + 1
|
|
2347
|
+
};
|
|
2348
|
+
if (attempted.attemptCount >= CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS) {
|
|
2349
|
+
deadLetteredMessages.push(attempted);
|
|
2350
|
+
continue;
|
|
1285
2351
|
}
|
|
1286
|
-
|
|
1287
|
-
dispatch,
|
|
1288
|
-
status: "blocked",
|
|
1289
|
-
errorMessage: `Dispatch requires ${error.provider} authorization.`
|
|
1290
|
-
});
|
|
1291
|
-
return;
|
|
2352
|
+
pendingMessages2.push(attempted);
|
|
1292
2353
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
2354
|
+
await writeConversation(
|
|
2355
|
+
state,
|
|
2356
|
+
lock,
|
|
2357
|
+
withExecutionUpdate(
|
|
2358
|
+
current,
|
|
2359
|
+
{
|
|
2360
|
+
...current.execution,
|
|
2361
|
+
pendingMessages: pendingMessages2
|
|
2362
|
+
},
|
|
2363
|
+
nowMs
|
|
2364
|
+
)
|
|
2365
|
+
);
|
|
2366
|
+
return {
|
|
2367
|
+
status: "recorded",
|
|
2368
|
+
pendingCount: pendingMessages2.length,
|
|
2369
|
+
deadLetteredMessages
|
|
2370
|
+
};
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
async function deadLetterAttempt(args) {
|
|
2374
|
+
const nowMs = args.nowMs ?? now();
|
|
2375
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2376
|
+
const current = await readConversation(state, args.conversationId);
|
|
2377
|
+
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
2378
|
+
return "lost_lease";
|
|
1309
2379
|
}
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
2380
|
+
const runnable = pendingMessages(current).length > 0;
|
|
2381
|
+
await writeConversation(
|
|
2382
|
+
state,
|
|
2383
|
+
lock,
|
|
2384
|
+
withExecutionUpdate(
|
|
2385
|
+
current,
|
|
2386
|
+
{
|
|
2387
|
+
...current.execution,
|
|
2388
|
+
lease: void 0,
|
|
2389
|
+
status: runnable ? "pending" : "failed",
|
|
2390
|
+
runId: runnable ? current.execution.runId : void 0
|
|
2391
|
+
},
|
|
2392
|
+
nowMs
|
|
2393
|
+
)
|
|
1319
2394
|
);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
2395
|
+
return runnable ? "pending" : "failed";
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
async function clearExpiredConversationLease(args) {
|
|
2399
|
+
const nowMs = args.nowMs ?? now();
|
|
2400
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2401
|
+
const current = await readConversation(state, args.conversationId);
|
|
2402
|
+
if (!current?.execution.lease || current.execution.lease.expiresAtMs > nowMs) {
|
|
2403
|
+
return false;
|
|
1329
2404
|
}
|
|
1330
|
-
await
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
2405
|
+
await writeConversation(
|
|
2406
|
+
state,
|
|
2407
|
+
lock,
|
|
2408
|
+
withExecutionUpdate(
|
|
2409
|
+
current,
|
|
2410
|
+
{
|
|
2411
|
+
...current.execution,
|
|
2412
|
+
lease: void 0,
|
|
2413
|
+
status: "pending"
|
|
2414
|
+
},
|
|
2415
|
+
nowMs
|
|
2416
|
+
)
|
|
2417
|
+
);
|
|
2418
|
+
return true;
|
|
2419
|
+
});
|
|
1338
2420
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
})
|
|
1358
|
-
);
|
|
1359
|
-
return new Response("Accepted", { status: 202 });
|
|
2421
|
+
async function removeActiveConversation(args) {
|
|
2422
|
+
const state = await getConnectedState(args.state);
|
|
2423
|
+
await removeIndexEntry({
|
|
2424
|
+
state,
|
|
2425
|
+
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2426
|
+
conversationId: args.conversationId
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
async function listActiveConversationIds(args = {}) {
|
|
2430
|
+
const state = await getConnectedState(args.state);
|
|
2431
|
+
const index = await getConversationIndexStore(state);
|
|
2432
|
+
const entries = await index.list({
|
|
2433
|
+
indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
|
|
2434
|
+
limit: args.limit,
|
|
2435
|
+
order: "asc",
|
|
2436
|
+
scoreMax: args.staleBeforeMs
|
|
2437
|
+
});
|
|
2438
|
+
return entries.map((entry) => entry.conversationId);
|
|
1360
2439
|
}
|
|
1361
|
-
|
|
1362
|
-
// src/handlers/heartbeat.ts
|
|
1363
|
-
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1364
2440
|
|
|
1365
2441
|
// src/chat/task-execution/store.ts
|
|
1366
2442
|
function metadataStore(options) {
|
|
@@ -1373,7 +2449,7 @@ function hasRecentEnqueueMarker(conversation, nowMs) {
|
|
|
1373
2449
|
const lastEnqueuedAtMs = conversation.execution.lastEnqueuedAtMs;
|
|
1374
2450
|
return typeof lastEnqueuedAtMs === "number" && lastEnqueuedAtMs + CONVERSATION_WORK_STALE_ENQUEUE_MS > nowMs;
|
|
1375
2451
|
}
|
|
1376
|
-
function
|
|
2452
|
+
function now2() {
|
|
1377
2453
|
return Date.now();
|
|
1378
2454
|
}
|
|
1379
2455
|
async function recordExecutionMetadata(args) {
|
|
@@ -1414,7 +2490,7 @@ function hasRunnableConversationWork(conversation) {
|
|
|
1414
2490
|
return conversation.execution.status !== "idle" || countPendingConversationMessages(conversation) > 0;
|
|
1415
2491
|
}
|
|
1416
2492
|
async function ensureConversationWake(args) {
|
|
1417
|
-
const nowMs = args.nowMs ??
|
|
2493
|
+
const nowMs = args.nowMs ?? now2();
|
|
1418
2494
|
const conversation = await getConversation({
|
|
1419
2495
|
conversationId: args.conversationId,
|
|
1420
2496
|
state: args.state
|
|
@@ -1430,8 +2506,7 @@ async function ensureConversationWake(args) {
|
|
|
1430
2506
|
}
|
|
1431
2507
|
const queueResult = await args.queue.send(
|
|
1432
2508
|
{
|
|
1433
|
-
conversationId: args.conversationId
|
|
1434
|
-
destination: conversation.destination
|
|
2509
|
+
conversationId: args.conversationId
|
|
1435
2510
|
},
|
|
1436
2511
|
{
|
|
1437
2512
|
delayMs: args.delayMs,
|
|
@@ -1447,7 +2522,7 @@ async function ensureConversationWake(args) {
|
|
|
1447
2522
|
return { status: "enqueued", queueMessageId: queueResult?.messageId };
|
|
1448
2523
|
}
|
|
1449
2524
|
async function appendAndEnqueueInboundMessage(args) {
|
|
1450
|
-
const nowMs = args.nowMs ??
|
|
2525
|
+
const nowMs = args.nowMs ?? now2();
|
|
1451
2526
|
const appendResult = await appendInboundMessage({
|
|
1452
2527
|
message: args.message,
|
|
1453
2528
|
nowMs,
|
|
@@ -2108,7 +3183,47 @@ async function GET2(request, waitUntil, options = {}) {
|
|
|
2108
3183
|
import { timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
2109
3184
|
|
|
2110
3185
|
// src/chat/conversations/sql/purge.ts
|
|
2111
|
-
import { and, asc, eq, inArray, isNull, sql } from "drizzle-orm";
|
|
3186
|
+
import { and, asc, eq as eq2, inArray, isNull, sql } from "drizzle-orm";
|
|
3187
|
+
|
|
3188
|
+
// src/chat/conversations/sql/privacy.ts
|
|
3189
|
+
import { eq } from "drizzle-orm";
|
|
3190
|
+
import { alias } from "drizzle-orm/pg-core";
|
|
3191
|
+
var privacyRoot = alias(juniorConversations, "privacy_root");
|
|
3192
|
+
async function readRootCandidate(executor, conversationId) {
|
|
3193
|
+
const rows = await executor.db().select({
|
|
3194
|
+
requestedConversationId: juniorConversations.conversationId,
|
|
3195
|
+
requestedParentConversationId: juniorConversations.parentConversationId,
|
|
3196
|
+
requestedRootConversationId: juniorConversations.rootConversationId,
|
|
3197
|
+
rootConversationId: privacyRoot.conversationId,
|
|
3198
|
+
rootDestinationId: privacyRoot.destinationId,
|
|
3199
|
+
rootParentConversationId: privacyRoot.parentConversationId,
|
|
3200
|
+
rootRootConversationId: privacyRoot.rootConversationId
|
|
3201
|
+
}).from(juniorConversations).leftJoin(
|
|
3202
|
+
privacyRoot,
|
|
3203
|
+
eq(privacyRoot.conversationId, juniorConversations.rootConversationId)
|
|
3204
|
+
).where(eq(juniorConversations.conversationId, conversationId));
|
|
3205
|
+
const row = rows[0];
|
|
3206
|
+
return row?.requestedRootConversationId !== null && (row?.requestedParentConversationId !== null || row?.requestedRootConversationId === row?.requestedConversationId) && row?.rootConversationId === row.requestedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && row.rootDestinationId !== null ? {
|
|
3207
|
+
destinationId: row.rootDestinationId,
|
|
3208
|
+
rootConversationId: row.rootConversationId
|
|
3209
|
+
} : void 0;
|
|
3210
|
+
}
|
|
3211
|
+
async function readCandidateVisibility(executor, candidate) {
|
|
3212
|
+
const destinations = await executor.db().select({ visibility: juniorDestinations.visibility }).from(juniorDestinations).where(eq(juniorDestinations.id, candidate.destinationId)).for("share");
|
|
3213
|
+
return {
|
|
3214
|
+
rootConversationId: candidate.rootConversationId,
|
|
3215
|
+
visibility: destinations[0]?.visibility ?? null
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
async function resolveRootVisibility(executor, conversationId) {
|
|
3219
|
+
const candidate = await readRootCandidate(executor, conversationId);
|
|
3220
|
+
if (!candidate) {
|
|
3221
|
+
return { rootConversationId: conversationId, visibility: null };
|
|
3222
|
+
}
|
|
3223
|
+
return readCandidateVisibility(executor, candidate);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
// src/chat/conversations/sql/purge.ts
|
|
2112
3227
|
async function discoverConversationTree(executor, root) {
|
|
2113
3228
|
const all = /* @__PURE__ */ new Map([
|
|
2114
3229
|
[root.conversationId, root]
|
|
@@ -2177,7 +3292,7 @@ async function selectExpiredRoots(executor, args) {
|
|
|
2177
3292
|
visibility: juniorDestinations.visibility
|
|
2178
3293
|
}).from(juniorConversations).leftJoin(
|
|
2179
3294
|
juniorDestinations,
|
|
2180
|
-
|
|
3295
|
+
eq2(juniorDestinations.id, juniorConversations.destinationId)
|
|
2181
3296
|
).where(
|
|
2182
3297
|
and(
|
|
2183
3298
|
isNull(juniorConversations.parentConversationId),
|
|
@@ -2198,7 +3313,7 @@ async function purgeConversationTree(executor, args) {
|
|
|
2198
3313
|
const initialRoots = await executor.db().select({
|
|
2199
3314
|
conversationId: juniorConversations.conversationId,
|
|
2200
3315
|
parentConversationId: juniorConversations.parentConversationId
|
|
2201
|
-
}).from(juniorConversations).where(
|
|
3316
|
+
}).from(juniorConversations).where(eq2(juniorConversations.conversationId, args.rootConversationId));
|
|
2202
3317
|
const initialRoot = initialRoots[0];
|
|
2203
3318
|
if (!initialRoot || args.retention && initialRoot.parentConversationId !== null) {
|
|
2204
3319
|
return { purged: false, conversations: 0 };
|
|
@@ -2220,14 +3335,14 @@ async function purgeConversationTree(executor, args) {
|
|
|
2220
3335
|
destinationId: juniorConversations.destinationId,
|
|
2221
3336
|
parentConversationId: juniorConversations.parentConversationId
|
|
2222
3337
|
}).from(juniorConversations).where(
|
|
2223
|
-
|
|
3338
|
+
eq2(juniorConversations.conversationId, args.rootConversationId)
|
|
2224
3339
|
).for("update");
|
|
2225
3340
|
const root = roots[0];
|
|
2226
3341
|
if (!root || args.retention && root.parentConversationId !== null) {
|
|
2227
3342
|
return { purged: false, conversations: 0 };
|
|
2228
3343
|
}
|
|
2229
3344
|
const resolvedScrubMetadata = args.scrubMetadataFromRootVisibility ? (await resolveRootVisibility(executor, args.rootConversationId)).visibility !== "public" : args.scrubMetadata;
|
|
2230
|
-
const destinations = root.destinationId ? await executor.db().select({ visibility: juniorDestinations.visibility }).from(juniorDestinations).where(
|
|
3345
|
+
const destinations = root.destinationId ? await executor.db().select({ visibility: juniorDestinations.visibility }).from(juniorDestinations).where(eq2(juniorDestinations.id, root.destinationId)).for("share") : [];
|
|
2231
3346
|
const isPublic = destinations[0]?.visibility === "public";
|
|
2232
3347
|
const ids = tree.map((conversation) => conversation.conversationId);
|
|
2233
3348
|
if (args.retention) {
|
|
@@ -2445,7 +3560,7 @@ var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
|
2445
3560
|
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
2446
3561
|
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
2447
3562
|
function createAssistantStatusScheduler(args) {
|
|
2448
|
-
const
|
|
3563
|
+
const now4 = args.now ?? (() => Date.now());
|
|
2449
3564
|
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
2450
3565
|
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
2451
3566
|
const random = args.random ?? Math.random;
|
|
@@ -2509,7 +3624,7 @@ function createAssistantStatusScheduler(args) {
|
|
|
2509
3624
|
}
|
|
2510
3625
|
currentVisibleStatus = text;
|
|
2511
3626
|
currentLoadingMessages = nextLoadingMessages;
|
|
2512
|
-
lastStatusAt =
|
|
3627
|
+
lastStatusAt = now4();
|
|
2513
3628
|
scheduleRotation();
|
|
2514
3629
|
await enqueueStatusUpdate(async () => {
|
|
2515
3630
|
await args.sendStatus(text, nextLoadingMessages);
|
|
@@ -2596,7 +3711,7 @@ function createAssistantStatusScheduler(args) {
|
|
|
2596
3711
|
}
|
|
2597
3712
|
return;
|
|
2598
3713
|
}
|
|
2599
|
-
const elapsed =
|
|
3714
|
+
const elapsed = now4() - lastStatusAt;
|
|
2600
3715
|
const waitMs = Math.max(
|
|
2601
3716
|
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
2602
3717
|
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
@@ -2994,19 +4109,14 @@ function resolveReplyTimeoutMs(explicitTimeoutMs) {
|
|
|
2994
4109
|
const parsed = Number.parseInt(raw, 10);
|
|
2995
4110
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
2996
4111
|
}
|
|
2997
|
-
async function postSlackMessageBestEffort(channelId, threadTs, text,
|
|
4112
|
+
async function postSlackMessageBestEffort(channelId, threadTs, text, conversationId) {
|
|
2998
4113
|
try {
|
|
2999
|
-
if (
|
|
3000
|
-
await
|
|
4114
|
+
if (conversationId) {
|
|
4115
|
+
await sendSlackReply({
|
|
3001
4116
|
channelId,
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
text,
|
|
3006
|
-
stage: "thread_reply"
|
|
3007
|
-
}
|
|
3008
|
-
],
|
|
3009
|
-
footer
|
|
4117
|
+
conversationId,
|
|
4118
|
+
text,
|
|
4119
|
+
threadTs
|
|
3010
4120
|
});
|
|
3011
4121
|
return;
|
|
3012
4122
|
}
|
|
@@ -3054,10 +4164,10 @@ function getResumeLogContext(args, lockKey) {
|
|
|
3054
4164
|
const actor = routing?.actor;
|
|
3055
4165
|
return {
|
|
3056
4166
|
conversationId: args.conversationId,
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
4167
|
+
messageConversationId: lockKey,
|
|
4168
|
+
userId: isUserActor(actor) ? actor.userId : void 0,
|
|
4169
|
+
userName: isUserActor(actor) ? actor.userName : void 0,
|
|
4170
|
+
destinationName: args.channelId,
|
|
3061
4171
|
assistantUserName: botConfig.userName,
|
|
3062
4172
|
modelId: standardModelId(botConfig)
|
|
3063
4173
|
};
|
|
@@ -3321,18 +4431,18 @@ async function resumeSlackTurn(args) {
|
|
|
3321
4431
|
};
|
|
3322
4432
|
};
|
|
3323
4433
|
const deliverAssistantMessage = async (assistantMessage) => {
|
|
3324
|
-
|
|
3325
|
-
if (posts.length === 0) {
|
|
4434
|
+
if (!assistantMessage.text.trim()) {
|
|
3326
4435
|
return;
|
|
3327
4436
|
}
|
|
3328
4437
|
failureCode = "delivery_failed";
|
|
3329
4438
|
const deliveryState2 = await getDeliveryConversation();
|
|
3330
4439
|
let messageTs;
|
|
3331
4440
|
try {
|
|
3332
|
-
messageTs = await
|
|
4441
|
+
messageTs = await sendSlackReply({
|
|
3333
4442
|
channelId: runArgs.channelId,
|
|
3334
|
-
|
|
3335
|
-
|
|
4443
|
+
conversationId: runArgs.conversationId,
|
|
4444
|
+
text: assistantMessage.text,
|
|
4445
|
+
threadTs: runArgs.threadTs
|
|
3336
4446
|
});
|
|
3337
4447
|
} catch (error) {
|
|
3338
4448
|
if (isRetryableSlackPostError(error)) {
|
|
@@ -3459,16 +4569,10 @@ async function resumeSlackTurn(args) {
|
|
|
3459
4569
|
destination: replyContext.routing.destination,
|
|
3460
4570
|
source: replyContext.routing.source,
|
|
3461
4571
|
actor: resumeActor,
|
|
3462
|
-
surface: "slack"
|
|
3463
|
-
logContext: {
|
|
3464
|
-
threadId: runArgs.conversationId,
|
|
3465
|
-
actorId: isUserActor(replyContext.routing.actor) ? replyContext.routing.actor.userId : void 0,
|
|
3466
|
-
channelId: runArgs.channelId,
|
|
3467
|
-
assistantUserName: botConfig.userName
|
|
3468
|
-
}
|
|
4572
|
+
surface: "slack"
|
|
3469
4573
|
});
|
|
3470
4574
|
}
|
|
3471
|
-
await runArgs.
|
|
4575
|
+
await runArgs.commitResult?.(reply);
|
|
3472
4576
|
if (reply.diagnostics.outcome === "success") {
|
|
3473
4577
|
await turnLifecycle.complete({
|
|
3474
4578
|
conversationId: runArgs.conversationId,
|
|
@@ -3564,9 +4668,6 @@ async function resumeSlackTurn(args) {
|
|
|
3564
4668
|
try {
|
|
3565
4669
|
await deferredPauseHandler();
|
|
3566
4670
|
if (deferredAuthInfo) {
|
|
3567
|
-
const footer = buildSlackReplyFooter({
|
|
3568
|
-
conversationId: runArgs.conversationId
|
|
3569
|
-
});
|
|
3570
4671
|
await postSlackMessageBestEffort(
|
|
3571
4672
|
runArgs.channelId,
|
|
3572
4673
|
runArgs.threadTs,
|
|
@@ -3574,7 +4675,7 @@ async function resumeSlackTurn(args) {
|
|
|
3574
4675
|
deferredAuthInfo.actorId,
|
|
3575
4676
|
deferredAuthInfo.providerDisplayName
|
|
3576
4677
|
),
|
|
3577
|
-
|
|
4678
|
+
runArgs.conversationId
|
|
3578
4679
|
);
|
|
3579
4680
|
}
|
|
3580
4681
|
return true;
|
|
@@ -3977,7 +5078,7 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
3977
5078
|
}
|
|
3978
5079
|
}
|
|
3979
5080
|
},
|
|
3980
|
-
|
|
5081
|
+
commitResult: async (reply) => {
|
|
3981
5082
|
await persistCompletedReplyState(
|
|
3982
5083
|
authSession.channelId,
|
|
3983
5084
|
authSession.threadTs,
|
|
@@ -4575,7 +5676,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
|
|
|
4575
5676
|
}
|
|
4576
5677
|
}
|
|
4577
5678
|
},
|
|
4578
|
-
|
|
5679
|
+
commitResult: async (reply) => {
|
|
4579
5680
|
logInfo(
|
|
4580
5681
|
"oauth_callback_resume_complete",
|
|
4581
5682
|
{},
|
|
@@ -4862,9 +5963,9 @@ function buildJwksUrl(value) {
|
|
|
4862
5963
|
return url;
|
|
4863
5964
|
}
|
|
4864
5965
|
async function getJwks(issuer) {
|
|
4865
|
-
const
|
|
5966
|
+
const now4 = Date.now();
|
|
4866
5967
|
const cached = jwksByIssuer.get(issuer);
|
|
4867
|
-
if (cached && cached.expiresAtMs >
|
|
5968
|
+
if (cached && cached.expiresAtMs > now4) {
|
|
4868
5969
|
return cached.jwks;
|
|
4869
5970
|
}
|
|
4870
5971
|
if (cached) {
|
|
@@ -4889,7 +5990,7 @@ async function getJwks(issuer) {
|
|
|
4889
5990
|
}
|
|
4890
5991
|
jwksByIssuer.set(issuer, {
|
|
4891
5992
|
jwks,
|
|
4892
|
-
expiresAtMs:
|
|
5993
|
+
expiresAtMs: now4 + OIDC_DISCOVERY_CACHE_TTL_MS
|
|
4893
5994
|
});
|
|
4894
5995
|
return jwks;
|
|
4895
5996
|
}
|
|
@@ -5239,7 +6340,7 @@ function rehydrateAttachmentFetchers(message, downloadPrivateSlackFile2 = downlo
|
|
|
5239
6340
|
|
|
5240
6341
|
// src/chat/slack/adapter-context.ts
|
|
5241
6342
|
var initializedAdapters = /* @__PURE__ */ new WeakSet();
|
|
5242
|
-
async function
|
|
6343
|
+
async function getConnectedState2(stateAdapter) {
|
|
5243
6344
|
const state = stateAdapter ?? getStateAdapter();
|
|
5244
6345
|
await state.connect();
|
|
5245
6346
|
return state;
|
|
@@ -5248,7 +6349,7 @@ async function ensureSlackAdapterInitialized(args) {
|
|
|
5248
6349
|
if (initializedAdapters.has(args.adapter)) {
|
|
5249
6350
|
return;
|
|
5250
6351
|
}
|
|
5251
|
-
const state = await
|
|
6352
|
+
const state = await getConnectedState2(args.state);
|
|
5252
6353
|
await args.adapter.initialize({
|
|
5253
6354
|
getState: () => state
|
|
5254
6355
|
});
|
|
@@ -5716,6 +6817,7 @@ function slackSerializedResourceEventMessage(input) {
|
|
|
5716
6817
|
raw: {
|
|
5717
6818
|
channel: input.channelId,
|
|
5718
6819
|
event_type: "resource_event",
|
|
6820
|
+
resource_event_type: input.eventType,
|
|
5719
6821
|
thread_ts: input.threadTs,
|
|
5720
6822
|
type: "message",
|
|
5721
6823
|
user: "UJRNEVENT"
|
|
@@ -5741,6 +6843,7 @@ function createSlackResourceEventInboundMessage(input) {
|
|
|
5741
6843
|
const timestampIso = new Date(input.event.occurredAtMs).toISOString();
|
|
5742
6844
|
const message = slackSerializedResourceEventMessage({
|
|
5743
6845
|
channelId: slack.channelId,
|
|
6846
|
+
eventType: input.event.eventType,
|
|
5744
6847
|
id: messageId,
|
|
5745
6848
|
text: input.text,
|
|
5746
6849
|
threadTs: slack.threadTs,
|
|
@@ -5781,7 +6884,7 @@ function createSlackResourceEventInboundMessage(input) {
|
|
|
5781
6884
|
}
|
|
5782
6885
|
};
|
|
5783
6886
|
}
|
|
5784
|
-
function
|
|
6887
|
+
function getConnectedState3(stateAdapter) {
|
|
5785
6888
|
return stateAdapter ?? getStateAdapter();
|
|
5786
6889
|
}
|
|
5787
6890
|
function parseSlackMetadata(value) {
|
|
@@ -5883,7 +6986,7 @@ function createSlackConversationWorker(options) {
|
|
|
5883
6986
|
return async (context) => {
|
|
5884
6987
|
const adapter = options.getSlackAdapter();
|
|
5885
6988
|
const actorLookup = options.lookupSlackUser ?? lookupSlackUser;
|
|
5886
|
-
const state =
|
|
6989
|
+
const state = getConnectedState3(options.state);
|
|
5887
6990
|
await state.connect();
|
|
5888
6991
|
const records = getPendingRecords({
|
|
5889
6992
|
execution: { pendingMessages: [...context.attempt.messages] }
|
|
@@ -6235,7 +7338,7 @@ async function handleUnlink(event, actorId, provider) {
|
|
|
6235
7338
|
await tokenStore.delete(actorId, provider);
|
|
6236
7339
|
logInfo(
|
|
6237
7340
|
"slash_command_unlink",
|
|
6238
|
-
{
|
|
7341
|
+
{ userId: actorId },
|
|
6239
7342
|
{ "app.credential.provider": provider },
|
|
6240
7343
|
`Unlinked ${formatProviderLabel(provider)} account via ${getCommandName()} slash command`
|
|
6241
7344
|
);
|
|
@@ -6457,7 +7560,7 @@ async function handleSlackEvent(args) {
|
|
|
6457
7560
|
);
|
|
6458
7561
|
} catch (error) {
|
|
6459
7562
|
logException(error, "slack_app_home_publish_failed", {
|
|
6460
|
-
|
|
7563
|
+
userId
|
|
6461
7564
|
});
|
|
6462
7565
|
}
|
|
6463
7566
|
}
|
|
@@ -6568,7 +7671,7 @@ async function handleSlashCommandForm(args) {
|
|
|
6568
7671
|
await withSpan(
|
|
6569
7672
|
"chat.slash_command",
|
|
6570
7673
|
"chat.slash_command",
|
|
6571
|
-
{
|
|
7674
|
+
{ userId },
|
|
6572
7675
|
async () => {
|
|
6573
7676
|
await handleSlashCommand({
|
|
6574
7677
|
adapter: args.adapter,
|
|
@@ -6607,7 +7710,7 @@ async function handleInteractivePayload(args) {
|
|
|
6607
7710
|
await withSpan(
|
|
6608
7711
|
"chat.app_home_disconnect",
|
|
6609
7712
|
"chat.app_home_disconnect",
|
|
6610
|
-
{
|
|
7713
|
+
{ userId },
|
|
6611
7714
|
async () => {
|
|
6612
7715
|
try {
|
|
6613
7716
|
await unlinkProvider(userId, provider, args.userTokenStore);
|
|
@@ -6615,7 +7718,7 @@ async function handleInteractivePayload(args) {
|
|
|
6615
7718
|
logException(
|
|
6616
7719
|
error,
|
|
6617
7720
|
"app_home_disconnect_unlink_failed",
|
|
6618
|
-
{
|
|
7721
|
+
{ userId },
|
|
6619
7722
|
{ "app.credential.provider": provider }
|
|
6620
7723
|
);
|
|
6621
7724
|
}
|
|
@@ -6625,7 +7728,7 @@ async function handleInteractivePayload(args) {
|
|
|
6625
7728
|
logException(
|
|
6626
7729
|
error,
|
|
6627
7730
|
"app_home_disconnect_publish_failed",
|
|
6628
|
-
{
|
|
7731
|
+
{ userId },
|
|
6629
7732
|
{ "app.credential.provider": provider }
|
|
6630
7733
|
);
|
|
6631
7734
|
}
|
|
@@ -6668,7 +7771,7 @@ async function handleSlackForm(args) {
|
|
|
6668
7771
|
})
|
|
6669
7772
|
).catch((error) => {
|
|
6670
7773
|
logException(error, "slash_command_failed", {
|
|
6671
|
-
|
|
7774
|
+
userId: params.get("user_id") ?? void 0
|
|
6672
7775
|
});
|
|
6673
7776
|
})
|
|
6674
7777
|
);
|
|
@@ -6698,7 +7801,7 @@ async function handleSlackForm(args) {
|
|
|
6698
7801
|
})
|
|
6699
7802
|
).catch((error) => {
|
|
6700
7803
|
logException(error, "slack_interactive_payload_failed", {
|
|
6701
|
-
|
|
7804
|
+
userId: payload.user?.id?.trim() || void 0
|
|
6702
7805
|
});
|
|
6703
7806
|
})
|
|
6704
7807
|
);
|
|
@@ -6830,25 +7933,10 @@ import {
|
|
|
6830
7933
|
registerDevConsumer
|
|
6831
7934
|
} from "@vercel/queue";
|
|
6832
7935
|
|
|
6833
|
-
// src/chat/task-execution/queue.ts
|
|
6834
|
-
var ConversationQueueMessageRejectedError = class extends Error {
|
|
6835
|
-
conversationId;
|
|
6836
|
-
reason;
|
|
6837
|
-
constructor(reason, message, options = {}) {
|
|
6838
|
-
super(message);
|
|
6839
|
-
this.name = "ConversationQueueMessageRejectedError";
|
|
6840
|
-
this.reason = reason;
|
|
6841
|
-
this.conversationId = options.conversationId;
|
|
6842
|
-
}
|
|
6843
|
-
};
|
|
6844
|
-
function isConversationQueueMessageRejectedError(error) {
|
|
6845
|
-
return error instanceof ConversationQueueMessageRejectedError;
|
|
6846
|
-
}
|
|
6847
|
-
|
|
6848
7936
|
// src/chat/task-execution/worker.ts
|
|
6849
7937
|
var CONVERSATION_WORK_DEFER_DELAY_MS = 15e3;
|
|
6850
7938
|
var CONVERSATION_WORK_SOFT_YIELD_AFTER_MS = 24e4;
|
|
6851
|
-
function
|
|
7939
|
+
function now3(options) {
|
|
6852
7940
|
return options.nowMs?.() ?? Date.now();
|
|
6853
7941
|
}
|
|
6854
7942
|
function nudgeIdempotencyKey(reason, conversationId, nowMs) {
|
|
@@ -6919,7 +8007,7 @@ function isTerminalFailure(failure) {
|
|
|
6919
8007
|
}
|
|
6920
8008
|
function startLeaseCheckIn(args) {
|
|
6921
8009
|
const timer = setInterval(() => {
|
|
6922
|
-
const nowMs =
|
|
8010
|
+
const nowMs = now3(args.options);
|
|
6923
8011
|
void checkInConversationWork2({
|
|
6924
8012
|
conversationId: args.conversationId,
|
|
6925
8013
|
leaseToken: args.leaseToken,
|
|
@@ -6975,16 +8063,16 @@ async function processConversationWork(message, options) {
|
|
|
6975
8063
|
await clearConsumedConversationWake2({
|
|
6976
8064
|
conversationId,
|
|
6977
8065
|
conversationStore: options.conversationStore,
|
|
6978
|
-
nowMs:
|
|
8066
|
+
nowMs: now3(options),
|
|
6979
8067
|
state: options.state
|
|
6980
8068
|
});
|
|
6981
8069
|
}
|
|
6982
8070
|
return { status: "no_work" };
|
|
6983
8071
|
}
|
|
6984
|
-
if (!initial.destination
|
|
8072
|
+
if (!initial.destination) {
|
|
6985
8073
|
throw new ConversationQueueMessageRejectedError(
|
|
6986
|
-
"
|
|
6987
|
-
`Conversation work
|
|
8074
|
+
"invalid_record",
|
|
8075
|
+
`Conversation work is missing destination context for ${conversationId}`,
|
|
6988
8076
|
{ conversationId }
|
|
6989
8077
|
);
|
|
6990
8078
|
}
|
|
@@ -6992,20 +8080,20 @@ async function processConversationWork(message, options) {
|
|
|
6992
8080
|
const lease = await startConversationWork2({
|
|
6993
8081
|
conversationId,
|
|
6994
8082
|
conversationStore: options.conversationStore,
|
|
6995
|
-
nowMs:
|
|
8083
|
+
nowMs: now3(options),
|
|
6996
8084
|
state: options.state
|
|
6997
8085
|
});
|
|
6998
8086
|
if (lease.status === "no_work") {
|
|
6999
8087
|
await clearConsumedConversationWake2({
|
|
7000
8088
|
conversationId,
|
|
7001
8089
|
conversationStore: options.conversationStore,
|
|
7002
|
-
nowMs:
|
|
8090
|
+
nowMs: now3(options),
|
|
7003
8091
|
state: options.state
|
|
7004
8092
|
});
|
|
7005
8093
|
return { status: "no_work" };
|
|
7006
8094
|
}
|
|
7007
8095
|
if (lease.status === "active") {
|
|
7008
|
-
const nudgeNowMs =
|
|
8096
|
+
const nudgeNowMs = now3(options);
|
|
7009
8097
|
await ensureConversationWake({
|
|
7010
8098
|
conversationId,
|
|
7011
8099
|
conversationStore: options.conversationStore,
|
|
@@ -7026,7 +8114,7 @@ async function processConversationWork(message, options) {
|
|
|
7026
8114
|
);
|
|
7027
8115
|
return { status: "active" };
|
|
7028
8116
|
}
|
|
7029
|
-
const startedAtMs =
|
|
8117
|
+
const startedAtMs = now3(options);
|
|
7030
8118
|
const softYieldDeadlineMs = startedAtMs + (options.softYieldAfterMs ?? CONVERSATION_WORK_SOFT_YIELD_AFTER_MS);
|
|
7031
8119
|
const leasedWork = await getConversationWorkState2({
|
|
7032
8120
|
conversationId,
|
|
@@ -7060,7 +8148,7 @@ async function processConversationWork(message, options) {
|
|
|
7060
8148
|
leaseToken: lease.leaseToken,
|
|
7061
8149
|
conversationStore: options.conversationStore,
|
|
7062
8150
|
handle,
|
|
7063
|
-
nowMs:
|
|
8151
|
+
nowMs: now3(options),
|
|
7064
8152
|
state: options.state
|
|
7065
8153
|
});
|
|
7066
8154
|
const ack = async () => {
|
|
@@ -7069,7 +8157,7 @@ async function processConversationWork(message, options) {
|
|
|
7069
8157
|
inboundMessageIds: attemptMessageIds,
|
|
7070
8158
|
leaseToken: lease.leaseToken,
|
|
7071
8159
|
conversationStore: options.conversationStore,
|
|
7072
|
-
nowMs:
|
|
8160
|
+
nowMs: now3(options),
|
|
7073
8161
|
state: options.state
|
|
7074
8162
|
});
|
|
7075
8163
|
if (!acknowledged) {
|
|
@@ -7092,13 +8180,13 @@ async function processConversationWork(message, options) {
|
|
|
7092
8180
|
},
|
|
7093
8181
|
conversationId,
|
|
7094
8182
|
destination,
|
|
7095
|
-
shouldYield: () => leaseLost ||
|
|
8183
|
+
shouldYield: () => leaseLost || now3(options) >= softYieldDeadlineMs,
|
|
7096
8184
|
checkIn: async () => {
|
|
7097
8185
|
const checkedIn = await checkInConversationWork2({
|
|
7098
8186
|
conversationId,
|
|
7099
8187
|
leaseToken: lease.leaseToken,
|
|
7100
8188
|
conversationStore: options.conversationStore,
|
|
7101
|
-
nowMs:
|
|
8189
|
+
nowMs: now3(options),
|
|
7102
8190
|
state: options.state
|
|
7103
8191
|
});
|
|
7104
8192
|
if (!checkedIn) {
|
|
@@ -7114,7 +8202,7 @@ async function processConversationWork(message, options) {
|
|
|
7114
8202
|
conversationId,
|
|
7115
8203
|
destination,
|
|
7116
8204
|
leaseToken: lease.leaseToken,
|
|
7117
|
-
nowMs:
|
|
8205
|
+
nowMs: now3(options),
|
|
7118
8206
|
options
|
|
7119
8207
|
});
|
|
7120
8208
|
return { status: "lost_lease" };
|
|
@@ -7124,13 +8212,13 @@ async function processConversationWork(message, options) {
|
|
|
7124
8212
|
conversationId,
|
|
7125
8213
|
destination,
|
|
7126
8214
|
leaseToken: lease.leaseToken,
|
|
7127
|
-
nowMs:
|
|
8215
|
+
nowMs: now3(options),
|
|
7128
8216
|
options
|
|
7129
8217
|
});
|
|
7130
8218
|
return { status: "lost_lease" };
|
|
7131
8219
|
}
|
|
7132
8220
|
if (result.status === "yielded") {
|
|
7133
|
-
const yieldNowMs =
|
|
8221
|
+
const yieldNowMs = now3(options);
|
|
7134
8222
|
const continuationMarked = await requestConversationContinuation2({
|
|
7135
8223
|
conversationId,
|
|
7136
8224
|
destination,
|
|
@@ -7165,7 +8253,7 @@ async function processConversationWork(message, options) {
|
|
|
7165
8253
|
"conversation_work_cooperative_yield",
|
|
7166
8254
|
{ conversationId },
|
|
7167
8255
|
{
|
|
7168
|
-
"app.worker.elapsed_ms":
|
|
8256
|
+
"app.worker.elapsed_ms": now3(options) - startedAtMs,
|
|
7169
8257
|
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
7170
8258
|
},
|
|
7171
8259
|
"Conversation work yielded cooperatively"
|
|
@@ -7173,7 +8261,7 @@ async function processConversationWork(message, options) {
|
|
|
7173
8261
|
return { status: "yielded" };
|
|
7174
8262
|
}
|
|
7175
8263
|
if (result.status === "deferred") {
|
|
7176
|
-
const deferredNowMs =
|
|
8264
|
+
const deferredNowMs = now3(options);
|
|
7177
8265
|
const released = await releaseConversationWork2({
|
|
7178
8266
|
conversationId,
|
|
7179
8267
|
leaseToken: lease.leaseToken,
|
|
@@ -7202,7 +8290,7 @@ async function processConversationWork(message, options) {
|
|
|
7202
8290
|
const failure = await recordFailedDeliveryAttempt({
|
|
7203
8291
|
conversationId,
|
|
7204
8292
|
leaseToken: lease.leaseToken,
|
|
7205
|
-
nowMs:
|
|
8293
|
+
nowMs: now3(options),
|
|
7206
8294
|
messageIds: attemptMessageIds,
|
|
7207
8295
|
options
|
|
7208
8296
|
});
|
|
@@ -7211,7 +8299,7 @@ async function processConversationWork(message, options) {
|
|
|
7211
8299
|
conversationId,
|
|
7212
8300
|
leaseToken: lease.leaseToken,
|
|
7213
8301
|
conversationStore: options.conversationStore,
|
|
7214
|
-
nowMs:
|
|
8302
|
+
nowMs: now3(options),
|
|
7215
8303
|
state: options.state
|
|
7216
8304
|
});
|
|
7217
8305
|
return { status: "failed" };
|
|
@@ -7221,14 +8309,14 @@ async function processConversationWork(message, options) {
|
|
|
7221
8309
|
conversationId,
|
|
7222
8310
|
leaseToken: lease.leaseToken,
|
|
7223
8311
|
conversationStore: options.conversationStore,
|
|
7224
|
-
nowMs:
|
|
8312
|
+
nowMs: now3(options),
|
|
7225
8313
|
state: options.state
|
|
7226
8314
|
});
|
|
7227
8315
|
if (completion === "lost_lease") {
|
|
7228
8316
|
return { status: "lost_lease" };
|
|
7229
8317
|
}
|
|
7230
8318
|
if (completion === "pending") {
|
|
7231
|
-
const nudgeNowMs =
|
|
8319
|
+
const nudgeNowMs = now3(options);
|
|
7232
8320
|
const wake = await ensureConversationWake({
|
|
7233
8321
|
conversationId,
|
|
7234
8322
|
conversationStore: options.conversationStore,
|
|
@@ -7247,13 +8335,13 @@ async function processConversationWork(message, options) {
|
|
|
7247
8335
|
"conversation_work_completed",
|
|
7248
8336
|
{ conversationId },
|
|
7249
8337
|
{
|
|
7250
|
-
"app.worker.elapsed_ms":
|
|
8338
|
+
"app.worker.elapsed_ms": now3(options) - startedAtMs
|
|
7251
8339
|
},
|
|
7252
8340
|
"Conversation work completed"
|
|
7253
8341
|
);
|
|
7254
8342
|
return { status: "completed" };
|
|
7255
8343
|
} catch (error) {
|
|
7256
|
-
const errorNowMs =
|
|
8344
|
+
const errorNowMs = now3(options);
|
|
7257
8345
|
let recoveryRecorded = false;
|
|
7258
8346
|
try {
|
|
7259
8347
|
const failure = attemptMessageIds.length > 0 ? await recordFailedDeliveryAttempt({
|
|
@@ -7318,7 +8406,7 @@ async function processConversationWork(message, options) {
|
|
|
7318
8406
|
"conversation_work_failed",
|
|
7319
8407
|
{ conversationId },
|
|
7320
8408
|
{
|
|
7321
|
-
"app.worker.elapsed_ms":
|
|
8409
|
+
"app.worker.elapsed_ms": now3(options) - startedAtMs
|
|
7322
8410
|
},
|
|
7323
8411
|
"Conversation work failed"
|
|
7324
8412
|
);
|
|
@@ -7336,18 +8424,11 @@ async function processConversationWork(message, options) {
|
|
|
7336
8424
|
var CONVERSATION_WORK_VISIBILITY_TIMEOUT_BUFFER_SECONDS = 30;
|
|
7337
8425
|
var CONVERSATION_WORK_DEV_CONSUMER_GROUP = "junior_conversation_work_dev";
|
|
7338
8426
|
function parseConversationQueueMessage(message) {
|
|
7339
|
-
const
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
if (!message || typeof message !== "object" || typeof message.conversationId !== "string" || !message.conversationId.trim() || !destination) {
|
|
7343
|
-
throw new Error(
|
|
7344
|
-
"Conversation queue message is missing destination context"
|
|
7345
|
-
);
|
|
8427
|
+
const parsed = conversationQueueMessageSchema.safeParse(message);
|
|
8428
|
+
if (!parsed.success) {
|
|
8429
|
+
throw new Error("Conversation queue message is malformed");
|
|
7346
8430
|
}
|
|
7347
|
-
return
|
|
7348
|
-
conversationId: message.conversationId,
|
|
7349
|
-
destination
|
|
7350
|
-
};
|
|
8431
|
+
return parsed.data;
|
|
7351
8432
|
}
|
|
7352
8433
|
function resolveConversationWorkVisibilityTimeoutSeconds(functionMaxDurationSeconds = getChatConfig().functionMaxDurationSeconds) {
|
|
7353
8434
|
return functionMaxDurationSeconds + CONVERSATION_WORK_VISIBILITY_TIMEOUT_BUFFER_SECONDS;
|
|
@@ -7898,10 +8979,10 @@ function createAcceptedSteeringDrain(hooks, options) {
|
|
|
7898
8979
|
function buildLogContext(deps, args) {
|
|
7899
8980
|
return {
|
|
7900
8981
|
conversationId: args.threadId ?? args.runId,
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
8982
|
+
messageConversationId: args.threadId,
|
|
8983
|
+
userId: args.actorId,
|
|
8984
|
+
userName: args.actorUserName,
|
|
8985
|
+
destinationName: args.channelId,
|
|
7905
8986
|
runId: args.runId,
|
|
7906
8987
|
assistantUserName: deps.assistantUserName,
|
|
7907
8988
|
modelId: deps.modelId
|
|
@@ -8620,9 +9701,9 @@ function createSlackTurnRuntime(deps) {
|
|
|
8620
9701
|
error,
|
|
8621
9702
|
"assistant_thread_started_handler_failed",
|
|
8622
9703
|
{
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
9704
|
+
messageConversationId: event.threadId,
|
|
9705
|
+
userId: event.userId,
|
|
9706
|
+
destinationName: event.channelId,
|
|
8626
9707
|
assistantUserName: deps.assistantUserName,
|
|
8627
9708
|
modelId: deps.modelId
|
|
8628
9709
|
},
|
|
@@ -8644,9 +9725,9 @@ function createSlackTurnRuntime(deps) {
|
|
|
8644
9725
|
error,
|
|
8645
9726
|
"assistant_context_changed_handler_failed",
|
|
8646
9727
|
{
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
9728
|
+
messageConversationId: event.threadId,
|
|
9729
|
+
userId: event.userId,
|
|
9730
|
+
destinationName: event.channelId,
|
|
8650
9731
|
assistantUserName: deps.assistantUserName,
|
|
8651
9732
|
modelId: deps.modelId
|
|
8652
9733
|
},
|
|
@@ -8670,9 +9751,9 @@ function createSubscribedReplyPolicy(deps) {
|
|
|
8670
9751
|
logWarn(
|
|
8671
9752
|
"subscribed_message_classifier_failed",
|
|
8672
9753
|
{
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
9754
|
+
messageConversationId: input.context.threadId,
|
|
9755
|
+
userId: input.context.actorId,
|
|
9756
|
+
destinationName: input.context.channelId,
|
|
8676
9757
|
runId: input.context.runId,
|
|
8677
9758
|
assistantUserName: botConfig.userName,
|
|
8678
9759
|
modelId: botConfig.fastModelId
|
|
@@ -8881,9 +9962,9 @@ async function resolveUserAttachmentsWithDeps(attachments, context, deps) {
|
|
|
8881
9962
|
logWarn(
|
|
8882
9963
|
"attachment_skipped_size_limit",
|
|
8883
9964
|
{
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
9965
|
+
messageConversationId: context.threadId,
|
|
9966
|
+
userId: context.actorId,
|
|
9967
|
+
destinationName: context.channelId,
|
|
8887
9968
|
runId: context.runId,
|
|
8888
9969
|
assistantUserName: botConfig.userName,
|
|
8889
9970
|
modelId: standardModelId(botConfig)
|
|
@@ -8906,9 +9987,9 @@ async function resolveUserAttachmentsWithDeps(attachments, context, deps) {
|
|
|
8906
9987
|
logWarn(
|
|
8907
9988
|
"image_attachment_processing_failed",
|
|
8908
9989
|
{
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
9990
|
+
messageConversationId: context.threadId,
|
|
9991
|
+
userId: context.actorId,
|
|
9992
|
+
destinationName: context.channelId,
|
|
8912
9993
|
runId: context.runId,
|
|
8913
9994
|
assistantUserName: botConfig.userName,
|
|
8914
9995
|
modelId: botConfig.visionModelId ?? standardModelId(botConfig)
|
|
@@ -8925,9 +10006,9 @@ async function resolveUserAttachmentsWithDeps(attachments, context, deps) {
|
|
|
8925
10006
|
logWarn(
|
|
8926
10007
|
"attachment_resolution_failed",
|
|
8927
10008
|
{
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
10009
|
+
messageConversationId: context.threadId,
|
|
10010
|
+
userId: context.actorId,
|
|
10011
|
+
destinationName: context.channelId,
|
|
8931
10012
|
runId: context.runId,
|
|
8932
10013
|
assistantUserName: botConfig.userName,
|
|
8933
10014
|
modelId: standardModelId(botConfig)
|
|
@@ -8975,9 +10056,9 @@ async function summarizeConversationImage(args, deps) {
|
|
|
8975
10056
|
logWarn(
|
|
8976
10057
|
"conversation_image_vision_failed",
|
|
8977
10058
|
{
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
10059
|
+
messageConversationId: args.context.threadId,
|
|
10060
|
+
userId: args.context.actorId,
|
|
10061
|
+
destinationName: args.context.channelId,
|
|
8981
10062
|
runId: args.context.runId,
|
|
8982
10063
|
assistantUserName: botConfig.userName,
|
|
8983
10064
|
modelId: visionModelId
|
|
@@ -9031,9 +10112,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
9031
10112
|
logWarn(
|
|
9032
10113
|
"conversation_image_replies_fetch_failed",
|
|
9033
10114
|
{
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
10115
|
+
messageConversationId: context.threadId,
|
|
10116
|
+
userId: context.actorId,
|
|
10117
|
+
destinationName: context.channelId,
|
|
9037
10118
|
runId: context.runId,
|
|
9038
10119
|
assistantUserName: botConfig.userName,
|
|
9039
10120
|
modelId: standardModelId(botConfig)
|
|
@@ -9098,9 +10179,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
9098
10179
|
logWarn(
|
|
9099
10180
|
"conversation_image_skipped_size_limit",
|
|
9100
10181
|
{
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
10182
|
+
messageConversationId: context.threadId,
|
|
10183
|
+
userId: context.actorId,
|
|
10184
|
+
destinationName: context.channelId,
|
|
9104
10185
|
runId: context.runId,
|
|
9105
10186
|
assistantUserName: botConfig.userName,
|
|
9106
10187
|
modelId: standardModelId(botConfig)
|
|
@@ -9125,9 +10206,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
9125
10206
|
logWarn(
|
|
9126
10207
|
"conversation_image_download_failed",
|
|
9127
10208
|
{
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
10209
|
+
messageConversationId: context.threadId,
|
|
10210
|
+
userId: context.actorId,
|
|
10211
|
+
destinationName: context.channelId,
|
|
9131
10212
|
runId: context.runId,
|
|
9132
10213
|
assistantUserName: botConfig.userName,
|
|
9133
10214
|
modelId: standardModelId(botConfig)
|
|
@@ -9145,9 +10226,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
9145
10226
|
logWarn(
|
|
9146
10227
|
"conversation_image_skipped_size_limit",
|
|
9147
10228
|
{
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
10229
|
+
messageConversationId: context.threadId,
|
|
10230
|
+
userId: context.actorId,
|
|
10231
|
+
destinationName: context.channelId,
|
|
9151
10232
|
runId: context.runId,
|
|
9152
10233
|
assistantUserName: botConfig.userName,
|
|
9153
10234
|
modelId: standardModelId(botConfig)
|
|
@@ -9188,9 +10269,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
9188
10269
|
logInfo(
|
|
9189
10270
|
"conversation_image_context_hydrated",
|
|
9190
10271
|
{
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
10272
|
+
messageConversationId: context.threadId,
|
|
10273
|
+
userId: context.actorId,
|
|
10274
|
+
destinationName: context.channelId,
|
|
9194
10275
|
runId: context.runId,
|
|
9195
10276
|
assistantUserName: botConfig.userName,
|
|
9196
10277
|
modelId: standardModelId(botConfig)
|
|
@@ -9313,9 +10394,9 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
9313
10394
|
logWarn(
|
|
9314
10395
|
"thread_title_generation_failed",
|
|
9315
10396
|
{
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
10397
|
+
messageConversationId: args.threadId,
|
|
10398
|
+
userId: args.actorId,
|
|
10399
|
+
destinationName: args.channelId,
|
|
9319
10400
|
runId: args.runId,
|
|
9320
10401
|
assistantUserName: args.assistantUserName,
|
|
9321
10402
|
modelId: args.modelId
|
|
@@ -9347,9 +10428,9 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
9347
10428
|
logError(
|
|
9348
10429
|
"thread_title_generation_permission_denied",
|
|
9349
10430
|
{
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
10431
|
+
messageConversationId: args.threadId,
|
|
10432
|
+
userId: args.actorId,
|
|
10433
|
+
destinationName: args.channelId,
|
|
9353
10434
|
runId: args.runId,
|
|
9354
10435
|
assistantUserName: args.assistantUserName,
|
|
9355
10436
|
modelId: args.modelId
|
|
@@ -9361,9 +10442,9 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
9361
10442
|
logWarn(
|
|
9362
10443
|
"thread_title_slack_update_failed",
|
|
9363
10444
|
{
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
10445
|
+
messageConversationId: args.threadId,
|
|
10446
|
+
userId: args.actorId,
|
|
10447
|
+
destinationName: args.channelId,
|
|
9367
10448
|
runId: args.runId,
|
|
9368
10449
|
assistantUserName: args.assistantUserName,
|
|
9369
10450
|
modelId: args.modelId
|
|
@@ -9595,9 +10676,9 @@ function createReplyToThread(deps) {
|
|
|
9595
10676
|
"chat.reply",
|
|
9596
10677
|
{
|
|
9597
10678
|
conversationId,
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
10679
|
+
messageConversationId: threadId,
|
|
10680
|
+
userId: message.author.userId,
|
|
10681
|
+
destinationName: channelId,
|
|
9601
10682
|
runId,
|
|
9602
10683
|
assistantUserName: botConfig.userName,
|
|
9603
10684
|
modelId: standardModelId(botConfig)
|
|
@@ -9653,9 +10734,9 @@ function createReplyToThread(deps) {
|
|
|
9653
10734
|
const turnId = buildDeterministicTurnId(message.id);
|
|
9654
10735
|
const turnTraceContext = {
|
|
9655
10736
|
conversationId,
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
10737
|
+
messageConversationId: threadId,
|
|
10738
|
+
userId: message.author.userId,
|
|
10739
|
+
destinationName: channelId,
|
|
9659
10740
|
runId,
|
|
9660
10741
|
assistantUserName: botConfig.userName,
|
|
9661
10742
|
modelId: standardModelId(botConfig)
|
|
@@ -9676,23 +10757,16 @@ function createReplyToThread(deps) {
|
|
|
9676
10757
|
actor.userId,
|
|
9677
10758
|
providerDisplayName
|
|
9678
10759
|
);
|
|
9679
|
-
const footer = buildSlackReplyFooter({ conversationId });
|
|
9680
10760
|
try {
|
|
10761
|
+
await beforeFirstResponsePost();
|
|
9681
10762
|
if (channelId && threadTs) {
|
|
9682
|
-
await
|
|
9683
|
-
beforePost: beforeFirstResponsePost,
|
|
10763
|
+
await sendSlackReply({
|
|
9684
10764
|
channelId,
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
text,
|
|
9689
|
-
stage: "thread_reply"
|
|
9690
|
-
}
|
|
9691
|
-
],
|
|
9692
|
-
footer
|
|
10765
|
+
conversationId,
|
|
10766
|
+
text,
|
|
10767
|
+
threadTs
|
|
9693
10768
|
});
|
|
9694
10769
|
} else {
|
|
9695
|
-
await beforeFirstResponsePost();
|
|
9696
10770
|
await thread.post(buildSlackOutputMessage(text));
|
|
9697
10771
|
}
|
|
9698
10772
|
} catch (error) {
|
|
@@ -9988,7 +11062,7 @@ function createReplyToThread(deps) {
|
|
|
9988
11062
|
});
|
|
9989
11063
|
}
|
|
9990
11064
|
if (actor?.userName) {
|
|
9991
|
-
setTags({
|
|
11065
|
+
setTags({ userName: actor.userName });
|
|
9992
11066
|
}
|
|
9993
11067
|
const turnAttachments = collectTurnAttachments(
|
|
9994
11068
|
message,
|
|
@@ -10014,32 +11088,6 @@ function createReplyToThread(deps) {
|
|
|
10014
11088
|
const compactingStatus = {
|
|
10015
11089
|
text: "Compacting context"
|
|
10016
11090
|
};
|
|
10017
|
-
const postThreadReply = async (payload, stage) => {
|
|
10018
|
-
await beforeFirstResponsePost();
|
|
10019
|
-
try {
|
|
10020
|
-
return await thread.post(payload);
|
|
10021
|
-
} catch (error) {
|
|
10022
|
-
if (isRetryableSlackPostError(error)) {
|
|
10023
|
-
throw new RetryableDeliveryError(error);
|
|
10024
|
-
}
|
|
10025
|
-
const eventId = logException(
|
|
10026
|
-
error,
|
|
10027
|
-
"slack_thread_post_failed",
|
|
10028
|
-
turnTraceContext,
|
|
10029
|
-
{
|
|
10030
|
-
"app.slack.reply_stage": stage,
|
|
10031
|
-
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
10032
|
-
...getSlackErrorObservabilityAttributes(error)
|
|
10033
|
-
},
|
|
10034
|
-
"Failed to post Slack thread reply"
|
|
10035
|
-
);
|
|
10036
|
-
throw new ConversationTurnBoundaryError({
|
|
10037
|
-
cause: error,
|
|
10038
|
-
...eventId ? { eventId } : {},
|
|
10039
|
-
failureCode: "delivery_failed"
|
|
10040
|
-
});
|
|
10041
|
-
}
|
|
10042
|
-
};
|
|
10043
11091
|
let persistedAtLeastOnce = false;
|
|
10044
11092
|
let shouldPersistFailureState = true;
|
|
10045
11093
|
let runResultHandled = false;
|
|
@@ -10059,18 +11107,45 @@ function createReplyToThread(deps) {
|
|
|
10059
11107
|
turnCompletionNotified = true;
|
|
10060
11108
|
};
|
|
10061
11109
|
const deliverAssistantMessage = async (assistantMessage) => {
|
|
10062
|
-
|
|
10063
|
-
if (posts.length === 0) {
|
|
11110
|
+
if (!assistantMessage.text.trim()) {
|
|
10064
11111
|
return;
|
|
10065
11112
|
}
|
|
10066
11113
|
boundaryFailureCode = "delivery_failed";
|
|
10067
11114
|
let slackTs;
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
11115
|
+
await beforeFirstResponsePost();
|
|
11116
|
+
try {
|
|
11117
|
+
if (channelId && threadTs && thread.adapter.name === "slack") {
|
|
11118
|
+
slackTs = await sendSlackReply({
|
|
11119
|
+
channelId,
|
|
11120
|
+
conversationId,
|
|
11121
|
+
text: assistantMessage.text,
|
|
11122
|
+
threadTs
|
|
11123
|
+
});
|
|
11124
|
+
} else {
|
|
11125
|
+
for (const text of splitSlackReplyText(assistantMessage.text)) {
|
|
11126
|
+
slackTs = (await thread.post(buildSlackOutputMessage(text))).id;
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
} catch (error) {
|
|
11130
|
+
if (isRetryableSlackPostError(error)) {
|
|
11131
|
+
throw new RetryableDeliveryError(error);
|
|
11132
|
+
}
|
|
11133
|
+
const eventId = logException(
|
|
11134
|
+
error,
|
|
11135
|
+
"slack_thread_post_failed",
|
|
11136
|
+
turnTraceContext,
|
|
11137
|
+
{
|
|
11138
|
+
"app.slack.reply_stage": "thread_reply",
|
|
11139
|
+
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
11140
|
+
...getSlackErrorObservabilityAttributes(error)
|
|
11141
|
+
},
|
|
11142
|
+
"Failed to post Slack thread reply"
|
|
10072
11143
|
);
|
|
10073
|
-
|
|
11144
|
+
throw new ConversationTurnBoundaryError({
|
|
11145
|
+
cause: error,
|
|
11146
|
+
...eventId ? { eventId } : {},
|
|
11147
|
+
failureCode: "delivery_failed"
|
|
11148
|
+
});
|
|
10074
11149
|
}
|
|
10075
11150
|
assistantMessageDelivered = true;
|
|
10076
11151
|
const recordedMessageId = recordDeliveredAssistantMessage({
|
|
@@ -10402,9 +11477,9 @@ function createReplyToThread(deps) {
|
|
|
10402
11477
|
}
|
|
10403
11478
|
let reply = outcome.result;
|
|
10404
11479
|
const diagnosticsContext = {
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
11480
|
+
messageConversationId: threadId,
|
|
11481
|
+
userId: message.author.userId,
|
|
11482
|
+
destinationName: channelId,
|
|
10408
11483
|
runId,
|
|
10409
11484
|
assistantUserName: botConfig.userName,
|
|
10410
11485
|
modelId: reply.diagnostics.modelId
|
|
@@ -10454,13 +11529,6 @@ function createReplyToThread(deps) {
|
|
|
10454
11529
|
sliceId: 1,
|
|
10455
11530
|
messages: reply.piMessages,
|
|
10456
11531
|
modelId: reply.diagnostics.modelId,
|
|
10457
|
-
logContext: {
|
|
10458
|
-
threadId,
|
|
10459
|
-
actorId: slackActorId,
|
|
10460
|
-
channelId,
|
|
10461
|
-
runId,
|
|
10462
|
-
assistantUserName: botConfig.userName
|
|
10463
|
-
},
|
|
10464
11532
|
actor: executionActor,
|
|
10465
11533
|
surface: "slack"
|
|
10466
11534
|
});
|
|
@@ -10747,6 +11815,11 @@ async function refreshAssistantThreadContext(event) {
|
|
|
10747
11815
|
|
|
10748
11816
|
// src/chat/runtime/conversation-message.ts
|
|
10749
11817
|
var NON_TEXT_MESSAGE_TEXT = "[non-text message]";
|
|
11818
|
+
function resourceEventType(entry) {
|
|
11819
|
+
if (!entry.raw || typeof entry.raw !== "object") return void 0;
|
|
11820
|
+
const raw = entry.raw;
|
|
11821
|
+
return raw.event_type === "resource_event" && typeof raw.resource_event_type === "string" ? raw.resource_event_type : void 0;
|
|
11822
|
+
}
|
|
10750
11823
|
function resolveMessageText(args) {
|
|
10751
11824
|
const text = normalizeConversationText(args.text);
|
|
10752
11825
|
return text || NON_TEXT_MESSAGE_TEXT;
|
|
@@ -10771,6 +11844,7 @@ function toConversationMessage(args) {
|
|
|
10771
11844
|
},
|
|
10772
11845
|
meta: {
|
|
10773
11846
|
attachmentCount: args.entry.attachments.length,
|
|
11847
|
+
eventType: resourceEventType(args.entry),
|
|
10774
11848
|
explicitMention: args.explicitMention,
|
|
10775
11849
|
imageAttachmentCount: imageAttachmentCount > 0 ? imageAttachmentCount : void 0,
|
|
10776
11850
|
imagesHydrated: !messageHasPotentialImageAttachment,
|
|
@@ -11377,7 +12451,7 @@ async function continueSlackAgentRun(payload, options) {
|
|
|
11377
12451
|
}
|
|
11378
12452
|
}
|
|
11379
12453
|
},
|
|
11380
|
-
|
|
12454
|
+
commitResult: async (reply) => {
|
|
11381
12455
|
await persistCompletedReplyState2({
|
|
11382
12456
|
sessionRecord: activeSessionRecord,
|
|
11383
12457
|
reply
|
|
@@ -11704,6 +12778,9 @@ function renderResourceEventNotificationText(subscription, event) {
|
|
|
11704
12778
|
"Handling:",
|
|
11705
12779
|
"- This is a subscribed conversation update, not a user-authored command.",
|
|
11706
12780
|
"- Use the subscription intent to decide whether this event warrants action or a visible reply. Otherwise, stay silent.",
|
|
12781
|
+
"- Treat the trusted summary as sufficient evidence for the facts it reports. Do not verify or expand those facts with tools.",
|
|
12782
|
+
"- Use tools only when the subscription intent explicitly requires missing details or an action.",
|
|
12783
|
+
"- When replying, state what changed and the useful next step, if any.",
|
|
11707
12784
|
"",
|
|
11708
12785
|
"Subscription:",
|
|
11709
12786
|
`- resource: ${subscription.label}`,
|
|
@@ -12210,6 +13287,7 @@ async function createApp(options) {
|
|
|
12210
13287
|
return app;
|
|
12211
13288
|
}
|
|
12212
13289
|
export {
|
|
13290
|
+
JUNIOR_VERSION,
|
|
12213
13291
|
createApp,
|
|
12214
13292
|
defineJuniorPlugins
|
|
12215
13293
|
};
|