@sentry/junior 0.110.0 → 0.112.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-ELT4ODFF.js → agent-hooks-6MVTPKP4.js} +8 -8
- package/dist/api/people/profile.d.ts +1 -0
- package/dist/api/schema/person.d.ts +2 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +13 -12
- package/dist/app.js +551 -401
- package/dist/build/virtual-config.d.ts +2 -0
- package/dist/{catalog-runtime-4GI22PXK.js → catalog-runtime-UEO25P2J.js} +5 -5
- package/dist/chat/agent/request.d.ts +3 -6
- package/dist/chat/agent/sandbox.d.ts +36 -11
- package/dist/chat/agent/tools.d.ts +5 -8
- package/dist/chat/config.d.ts +8 -1
- package/dist/chat/pi/sdk.d.ts +1 -1
- package/dist/chat/plugins/agent-hooks.d.ts +2 -2
- package/dist/chat/runtime/agent-continue-runner.d.ts +7 -3
- package/dist/chat/runtime/agent-run-outcome.d.ts +1 -0
- package/dist/chat/runtime/slack-runtime.d.ts +1 -2
- package/dist/chat/runtime/thread-state.d.ts +3 -7
- package/dist/chat/runtime/turn-preparation.d.ts +2 -2
- package/dist/chat/sandbox/command-result.d.ts +91 -0
- package/dist/chat/sandbox/errors.d.ts +3 -1
- package/dist/chat/sandbox/ref.d.ts +5 -0
- package/dist/chat/sandbox/sandbox.d.ts +19 -46
- package/dist/chat/sandbox/session.d.ts +16 -20
- package/dist/chat/sandbox/skill-sync.d.ts +2 -2
- package/dist/chat/sandbox/workspace.d.ts +14 -12
- package/dist/chat/services/auth-pause-response.d.ts +1 -1
- package/dist/chat/services/auth-pause.d.ts +2 -1
- package/dist/chat/services/mcp-auth-orchestration.d.ts +1 -1
- package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
- package/dist/chat/services/turn-result.d.ts +3 -4
- package/dist/chat/task-execution/slack-work.d.ts +3 -1
- package/dist/chat/task-execution/state.d.ts +75 -22
- package/dist/chat/task-execution/store.d.ts +34 -3
- package/dist/chat/task-execution/worker.d.ts +0 -1
- package/dist/chat/tool-support/normalize-result.d.ts +2 -2
- package/dist/chat/tool-support/pi-tool-adapter.d.ts +2 -2
- package/dist/chat/tool-support/structured-result.d.ts +1 -2
- package/dist/chat/tools/resource-events.d.ts +3 -129
- package/dist/chat/tools/sandbox/file-uploads.d.ts +1 -1
- package/dist/chat/{runtime → tools/sandbox}/generated-artifacts.d.ts +3 -3
- package/dist/chat/tools/types.d.ts +1 -1
- package/dist/chat/tools/web/fetch-content.d.ts +5 -0
- package/dist/chat/tools/web/image-generate.d.ts +2 -1
- package/dist/chat/tools/web/search.d.ts +1 -1
- package/dist/{chunk-3S7YYHBI.js → chunk-2FJ6WEYV.js} +1 -1
- package/dist/{chunk-Y5FK3CNG.js → chunk-4EJO4HIC.js} +1 -1
- package/dist/{chunk-W6RJZG7F.js → chunk-66IP6WED.js} +306 -31
- package/dist/{chunk-W75ZPAGG.js → chunk-AU2JVUDF.js} +1 -1
- package/dist/{chunk-3UBQI73J.js → chunk-AWNDKI5K.js} +7 -8
- package/dist/{chunk-IYKPU7H6.js → chunk-CSS7JHEM.js} +1 -1
- package/dist/{chunk-SBOBTS27.js → chunk-GJG67YFS.js} +2 -2
- package/dist/{chunk-RHHRKSPI.js → chunk-GR4IB6OQ.js} +8729 -9024
- package/dist/{chunk-ZKIOWEFL.js → chunk-IV2O4HQJ.js} +1 -1
- package/dist/{chunk-FPK7XY3F.js → chunk-JE23YR3X.js} +1 -0
- package/dist/{chunk-L5XQPJ7T.js → chunk-NDQL4ESR.js} +2 -2
- package/dist/{chunk-WNOVRQ67.js → chunk-PK4ETCUQ.js} +46 -18
- package/dist/{chunk-4QU44P7Q.js → chunk-QIALGHRN.js} +2 -2
- package/dist/{chunk-C5XAFTHY.js → chunk-SKEMI4IF.js} +16 -13
- package/dist/{chunk-Y3H2DN6F.js → chunk-TZU3R5OC.js} +1 -1
- package/dist/{chunk-2T7TIEXI.js → chunk-VBSVNOLA.js} +11 -15
- package/dist/{chunk-GK47NBDE.js → chunk-X3SP5RDW.js} +1 -1
- package/dist/{chunk-3TV7OEHL.js → chunk-ZHP4KNF7.js} +1 -1
- package/dist/cli/chat.js +19 -19
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +11 -11
- package/dist/cli/snapshot-warmup.js +8 -8
- package/dist/cli/upgrade.js +5 -5
- package/dist/{db-Y6W6CWWW.js → db-P5H5RVQV.js} +5 -5
- package/dist/instrumentation.js +2 -2
- package/dist/nitro.js +14 -8
- package/dist/{runner-BBFV5GLR.js → runner-B4ZDUO3R.js} +19 -28
- package/dist/{skills-AR4AF32L.js → skills-GWTOZ3XV.js} +6 -6
- package/dist/{validation-FBIEPTC2.js → validation-QFIC6XH6.js} +6 -6
- package/dist/version.js +1 -1
- package/package.json +5 -5
package/dist/app.js
CHANGED
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
processPluginTask,
|
|
7
7
|
requireTurnFailureEventId,
|
|
8
8
|
scheduleSessionCompletedPluginTasks
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AWNDKI5K.js";
|
|
10
10
|
import {
|
|
11
11
|
GET,
|
|
12
12
|
buildSentryConversationUrl,
|
|
13
13
|
conversationVisibilityFromSlackChannelType,
|
|
14
14
|
resolveSlackChannelTypeFromMessage,
|
|
15
15
|
resolveSlackConversationContext
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-2FJ6WEYV.js";
|
|
17
17
|
import "./chunk-2ZTTXTSZ.js";
|
|
18
18
|
import {
|
|
19
19
|
ConversationQueueMessageRejectedError,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
validatePluginEgressCredentialHooks,
|
|
33
33
|
validatePluginRegistrations
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-4EJO4HIC.js";
|
|
35
35
|
import {
|
|
36
36
|
defineJuniorPlugins,
|
|
37
37
|
pluginCatalogConfigFromEnv,
|
|
@@ -84,7 +84,7 @@ import {
|
|
|
84
84
|
splitSlackReplyText,
|
|
85
85
|
startOAuthFlow,
|
|
86
86
|
truncateStatusText
|
|
87
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-GR4IB6OQ.js";
|
|
88
88
|
import {
|
|
89
89
|
ConversationTurnBoundaryError,
|
|
90
90
|
CooperativeTurnYieldError,
|
|
@@ -143,8 +143,8 @@ import {
|
|
|
143
143
|
turnHasReply,
|
|
144
144
|
updateConversationStats,
|
|
145
145
|
upsertConversationMessage
|
|
146
|
-
} from "./chunk-
|
|
147
|
-
import "./chunk-
|
|
146
|
+
} from "./chunk-SKEMI4IF.js";
|
|
147
|
+
import "./chunk-66IP6WED.js";
|
|
148
148
|
import "./chunk-MU6HHZEN.js";
|
|
149
149
|
import "./chunk-PDO5BLNM.js";
|
|
150
150
|
import "./chunk-UIE3R5XU.js";
|
|
@@ -163,12 +163,12 @@ import {
|
|
|
163
163
|
validatePlugins,
|
|
164
164
|
verifyScheduledTaskCredentialSubject,
|
|
165
165
|
verifySlackDirectCredentialSubject
|
|
166
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-VBSVNOLA.js";
|
|
167
167
|
import {
|
|
168
168
|
acquireActiveLock,
|
|
169
169
|
getDefaultRedisStateAdapterFor,
|
|
170
170
|
getStateAdapter
|
|
171
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-X3SP5RDW.js";
|
|
172
172
|
import {
|
|
173
173
|
SlackActionError,
|
|
174
174
|
downloadPrivateSlackFile,
|
|
@@ -179,7 +179,7 @@ import {
|
|
|
179
179
|
normalizeSlackConversationId,
|
|
180
180
|
runWithSlackInstallationToken,
|
|
181
181
|
runWithWorkspaceTeamId
|
|
182
|
-
} from "./chunk-
|
|
182
|
+
} from "./chunk-NDQL4ESR.js";
|
|
183
183
|
import {
|
|
184
184
|
contextProvenance,
|
|
185
185
|
createSlackDestination,
|
|
@@ -193,7 +193,7 @@ import {
|
|
|
193
193
|
requireSlackDestination,
|
|
194
194
|
sameDestination,
|
|
195
195
|
withConversationEventLock
|
|
196
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-QIALGHRN.js";
|
|
197
197
|
import {
|
|
198
198
|
juniorConversationEvents,
|
|
199
199
|
juniorConversations
|
|
@@ -206,6 +206,7 @@ import {
|
|
|
206
206
|
botConfig,
|
|
207
207
|
completeObject,
|
|
208
208
|
completeText,
|
|
209
|
+
configureFunctionMaxDurationSeconds,
|
|
209
210
|
escapeXml,
|
|
210
211
|
getChatConfig,
|
|
211
212
|
getRuntimeMetadata,
|
|
@@ -221,7 +222,7 @@ import {
|
|
|
221
222
|
standardModelId,
|
|
222
223
|
stripRuntimeTurnContext,
|
|
223
224
|
trimTrailingAssistantMessages
|
|
224
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-PK4ETCUQ.js";
|
|
225
226
|
import {
|
|
226
227
|
juniorDestinations
|
|
227
228
|
} from "./chunk-VFUK3X5B.js";
|
|
@@ -233,16 +234,16 @@ import {
|
|
|
233
234
|
} from "./chunk-6Y5L4BOM.js";
|
|
234
235
|
import {
|
|
235
236
|
discoverSkills
|
|
236
|
-
} from "./chunk-
|
|
237
|
+
} from "./chunk-GJG67YFS.js";
|
|
237
238
|
import {
|
|
238
239
|
pluginCatalogRuntime
|
|
239
|
-
} from "./chunk-
|
|
240
|
+
} from "./chunk-ZHP4KNF7.js";
|
|
240
241
|
import {
|
|
241
242
|
buildOAuthTokenRequest,
|
|
242
243
|
credentialSubjectSchema,
|
|
243
244
|
hasRequiredOAuthScope,
|
|
244
245
|
parseOAuthTokenResponse
|
|
245
|
-
} from "./chunk-
|
|
246
|
+
} from "./chunk-TZU3R5OC.js";
|
|
246
247
|
import {
|
|
247
248
|
createActor,
|
|
248
249
|
createSlackActor,
|
|
@@ -267,7 +268,7 @@ import {
|
|
|
267
268
|
setTags,
|
|
268
269
|
withContext,
|
|
269
270
|
withSpan
|
|
270
|
-
} from "./chunk-
|
|
271
|
+
} from "./chunk-CSS7JHEM.js";
|
|
271
272
|
import {
|
|
272
273
|
isRecord,
|
|
273
274
|
parseSlackChannelId,
|
|
@@ -284,10 +285,10 @@ import {
|
|
|
284
285
|
} from "./chunk-3V7OFJZC.js";
|
|
285
286
|
import {
|
|
286
287
|
JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
|
|
287
|
-
} from "./chunk-
|
|
288
|
+
} from "./chunk-AU2JVUDF.js";
|
|
288
289
|
import {
|
|
289
290
|
JUNIOR_VERSION
|
|
290
|
-
} from "./chunk-
|
|
291
|
+
} from "./chunk-IV2O4HQJ.js";
|
|
291
292
|
import "./chunk-MLKGABMK.js";
|
|
292
293
|
|
|
293
294
|
// src/app.ts
|
|
@@ -823,8 +824,7 @@ async function persistRuntimePatch(args) {
|
|
|
823
824
|
await persistThreadStateById(args.threadId, {
|
|
824
825
|
artifacts: args.artifacts,
|
|
825
826
|
conversation: args.conversation,
|
|
826
|
-
|
|
827
|
-
sandboxDependencyProfileHash: args.sandboxDependencyProfileHash
|
|
827
|
+
sandboxRef: args.sandboxRef
|
|
828
828
|
});
|
|
829
829
|
}
|
|
830
830
|
async function markDispatch(args) {
|
|
@@ -946,8 +946,7 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
946
946
|
return;
|
|
947
947
|
}
|
|
948
948
|
let artifacts = coerceThreadArtifactsState(persisted);
|
|
949
|
-
let
|
|
950
|
-
let sandboxDependencyProfileHash = typeof persisted.app_sandbox_dependency_profile_hash === "string" ? persisted.app_sandbox_dependency_profile_hash : void 0;
|
|
949
|
+
let sandboxRef = getPersistedSandboxState(persisted);
|
|
951
950
|
const channelConfiguration = getChannelConfigurationServiceById(
|
|
952
951
|
dispatch.destination.channelId
|
|
953
952
|
);
|
|
@@ -1052,24 +1051,19 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
1052
1051
|
},
|
|
1053
1052
|
state: {
|
|
1054
1053
|
artifactState: artifacts,
|
|
1055
|
-
|
|
1056
|
-
sandboxId,
|
|
1057
|
-
sandboxDependencyProfileHash
|
|
1058
|
-
}
|
|
1054
|
+
sandboxRef
|
|
1059
1055
|
},
|
|
1060
1056
|
delivery: {
|
|
1061
1057
|
onAssistantMessage: deliverAssistantMessage
|
|
1062
1058
|
},
|
|
1063
1059
|
durability: {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
sandboxDependencyProfileHash = sandbox.sandboxDependencyProfileHash;
|
|
1060
|
+
onSandboxRefChanged: async (nextSandboxRef) => {
|
|
1061
|
+
sandboxRef = nextSandboxRef;
|
|
1067
1062
|
await persistRuntimePatch({
|
|
1068
1063
|
threadId: conversationId,
|
|
1069
1064
|
conversation,
|
|
1070
1065
|
artifacts,
|
|
1071
|
-
|
|
1072
|
-
sandboxDependencyProfileHash
|
|
1066
|
+
sandboxRef
|
|
1073
1067
|
});
|
|
1074
1068
|
},
|
|
1075
1069
|
onArtifactStateUpdated: async (nextArtifacts2) => {
|
|
@@ -1078,8 +1072,7 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
1078
1072
|
threadId: conversationId,
|
|
1079
1073
|
conversation,
|
|
1080
1074
|
artifacts,
|
|
1081
|
-
|
|
1082
|
-
sandboxDependencyProfileHash
|
|
1075
|
+
sandboxRef
|
|
1083
1076
|
});
|
|
1084
1077
|
}
|
|
1085
1078
|
}
|
|
@@ -1141,8 +1134,7 @@ async function runAgentDispatchSlice(callback, deps) {
|
|
|
1141
1134
|
threadId: conversationId,
|
|
1142
1135
|
conversation,
|
|
1143
1136
|
artifacts: nextArtifacts,
|
|
1144
|
-
|
|
1145
|
-
sandboxDependencyProfileHash: reply.sandboxDependencyProfileHash ?? sandboxDependencyProfileHash
|
|
1137
|
+
sandboxRef: reply.sandboxRef ?? sandboxRef
|
|
1146
1138
|
});
|
|
1147
1139
|
});
|
|
1148
1140
|
statePersisted = true;
|
|
@@ -1313,8 +1305,10 @@ import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
|
1313
1305
|
|
|
1314
1306
|
// src/chat/task-execution/state.ts
|
|
1315
1307
|
import { randomUUID } from "crypto";
|
|
1308
|
+
import { destinationSchema as destinationSchema2 } from "@sentry/junior-plugin-api";
|
|
1309
|
+
import { z as z2 } from "zod";
|
|
1316
1310
|
var CONVERSATION_PREFIX = "junior:conversation";
|
|
1317
|
-
var CONVERSATION_SCHEMA_VERSION =
|
|
1311
|
+
var CONVERSATION_SCHEMA_VERSION = 2;
|
|
1318
1312
|
var CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH = 1e4;
|
|
1319
1313
|
var CONVERSATION_INDEX_LOCK_TTL_MS = 1e4;
|
|
1320
1314
|
var CONVERSATION_INDEX_LOCK_WAIT_MS = 2e3;
|
|
@@ -1345,6 +1339,36 @@ var CONVERSATION_WORK_LEASE_TTL_MS = 9e4;
|
|
|
1345
1339
|
var CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15e3;
|
|
1346
1340
|
var CONVERSATION_WORK_STALE_ENQUEUE_MS = 6e4;
|
|
1347
1341
|
var CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
|
|
1342
|
+
var inboundMessageSourceSchema = z2.enum([
|
|
1343
|
+
"api",
|
|
1344
|
+
"internal",
|
|
1345
|
+
"local",
|
|
1346
|
+
"plugin",
|
|
1347
|
+
"resource_event",
|
|
1348
|
+
"scheduler",
|
|
1349
|
+
"slack"
|
|
1350
|
+
]);
|
|
1351
|
+
var agentInputSchema = z2.object({
|
|
1352
|
+
attachments: z2.array(z2.unknown()).optional(),
|
|
1353
|
+
authorId: z2.string().min(1).optional(),
|
|
1354
|
+
metadata: z2.record(z2.string(), z2.unknown()).optional(),
|
|
1355
|
+
text: z2.string()
|
|
1356
|
+
}).strict().refine((input) => input.text.trim() || input.attachments?.length, {
|
|
1357
|
+
message: "agent input requires text or attachments"
|
|
1358
|
+
});
|
|
1359
|
+
var inboundMessageDeliverySchema = z2.enum(["defer", "interrupt"]);
|
|
1360
|
+
var inboundMessageSchema = z2.object({
|
|
1361
|
+
attemptCount: z2.number().finite().optional(),
|
|
1362
|
+
conversationId: z2.string().refine((value) => value.trim().length > 0),
|
|
1363
|
+
createdAtMs: z2.number().finite(),
|
|
1364
|
+
delivery: inboundMessageDeliverySchema,
|
|
1365
|
+
destination: destinationSchema2,
|
|
1366
|
+
inboundMessageId: z2.string().refine((value) => value.trim().length > 0),
|
|
1367
|
+
injectedAtMs: z2.number().finite().optional(),
|
|
1368
|
+
input: agentInputSchema,
|
|
1369
|
+
receivedAtMs: z2.number().finite(),
|
|
1370
|
+
source: inboundMessageSourceSchema
|
|
1371
|
+
}).strict();
|
|
1348
1372
|
function conversationKey(conversationId) {
|
|
1349
1373
|
return `${CONVERSATION_PREFIX}:${conversationId}`;
|
|
1350
1374
|
}
|
|
@@ -1364,10 +1388,16 @@ function inputHasAttachments(input) {
|
|
|
1364
1388
|
return Array.isArray(input.attachments) && input.attachments.length > 0;
|
|
1365
1389
|
}
|
|
1366
1390
|
function upgradedPendingMessage(stored, duplicate) {
|
|
1367
|
-
|
|
1391
|
+
const promotesInterrupt = stored.delivery === "defer" && duplicate.delivery === "interrupt";
|
|
1392
|
+
const delivery = promotesInterrupt ? "interrupt" : stored.delivery;
|
|
1393
|
+
const input = promotesInterrupt ? {
|
|
1394
|
+
...duplicate.input,
|
|
1395
|
+
...inputHasAttachments(duplicate.input) || !inputHasAttachments(stored.input) ? {} : { attachments: stored.input.attachments }
|
|
1396
|
+
} : inputHasAttachments(duplicate.input) && !inputHasAttachments(stored.input) ? duplicate.input : stored.input;
|
|
1397
|
+
if (input === stored.input && delivery === stored.delivery) {
|
|
1368
1398
|
return stored;
|
|
1369
1399
|
}
|
|
1370
|
-
return { ...stored,
|
|
1400
|
+
return { ...stored, delivery, input };
|
|
1371
1401
|
}
|
|
1372
1402
|
function compareIndexDescending(left, right) {
|
|
1373
1403
|
return right.score - left.score || right.conversationId.localeCompare(left.conversationId);
|
|
@@ -1379,10 +1409,8 @@ function uniqueStrings(values) {
|
|
|
1379
1409
|
return [...new Set(values)];
|
|
1380
1410
|
}
|
|
1381
1411
|
function normalizeSource(value) {
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
return void 0;
|
|
1412
|
+
const parsed = inboundMessageSourceSchema.safeParse(value);
|
|
1413
|
+
return parsed.success ? parsed.data : void 0;
|
|
1386
1414
|
}
|
|
1387
1415
|
function normalizeExecutionStatus(value) {
|
|
1388
1416
|
if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
|
|
@@ -1390,53 +1418,13 @@ function normalizeExecutionStatus(value) {
|
|
|
1390
1418
|
}
|
|
1391
1419
|
return void 0;
|
|
1392
1420
|
}
|
|
1393
|
-
function
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
-
};
|
|
1421
|
+
function normalizeMessage(value, schemaVersion) {
|
|
1422
|
+
const candidate = schemaVersion === 1 && isRecord(value) && value.delivery === void 0 ? {
|
|
1423
|
+
...value,
|
|
1424
|
+
delivery: "defer"
|
|
1425
|
+
} : value;
|
|
1426
|
+
const parsed = inboundMessageSchema.safeParse(candidate);
|
|
1427
|
+
return parsed.success ? parsed.data : void 0;
|
|
1440
1428
|
}
|
|
1441
1429
|
function isFinalAttempt(message) {
|
|
1442
1430
|
return (message.attemptCount ?? 0) >= CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS - 1;
|
|
@@ -1462,7 +1450,7 @@ function normalizeLease(value) {
|
|
|
1462
1450
|
expiresAtMs
|
|
1463
1451
|
};
|
|
1464
1452
|
}
|
|
1465
|
-
function normalizeExecution(conversationId, value) {
|
|
1453
|
+
function normalizeExecution(conversationId, value, schemaVersion) {
|
|
1466
1454
|
if (!isRecord(value)) {
|
|
1467
1455
|
return void 0;
|
|
1468
1456
|
}
|
|
@@ -1470,7 +1458,20 @@ function normalizeExecution(conversationId, value) {
|
|
|
1470
1458
|
if (!status) {
|
|
1471
1459
|
return void 0;
|
|
1472
1460
|
}
|
|
1473
|
-
const pendingMessages2 =
|
|
1461
|
+
const pendingMessages2 = [];
|
|
1462
|
+
if (Array.isArray(value.pendingMessages)) {
|
|
1463
|
+
for (const rawMessage of value.pendingMessages) {
|
|
1464
|
+
const message = normalizeMessage(rawMessage, schemaVersion);
|
|
1465
|
+
if (!message || message.conversationId !== conversationId) {
|
|
1466
|
+
return void 0;
|
|
1467
|
+
}
|
|
1468
|
+
if (message.injectedAtMs !== void 0) {
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
pendingMessages2.push(message);
|
|
1472
|
+
}
|
|
1473
|
+
pendingMessages2.sort(compareMessages);
|
|
1474
|
+
}
|
|
1474
1475
|
const inboundMessageIds = Array.isArray(value.inboundMessageIds) ? uniqueStrings(
|
|
1475
1476
|
value.inboundMessageIds.map((id) => typeof id === "string" ? id : void 0).filter((id) => Boolean(id))
|
|
1476
1477
|
) : [];
|
|
@@ -1492,14 +1493,19 @@ function normalizeExecution(conversationId, value) {
|
|
|
1492
1493
|
};
|
|
1493
1494
|
}
|
|
1494
1495
|
function normalizeConversation(conversationId, value) {
|
|
1495
|
-
if (!isRecord(value) || value.schemaVersion !== CONVERSATION_SCHEMA_VERSION) {
|
|
1496
|
+
if (!isRecord(value) || value.schemaVersion !== 1 && value.schemaVersion !== CONVERSATION_SCHEMA_VERSION) {
|
|
1496
1497
|
return void 0;
|
|
1497
1498
|
}
|
|
1499
|
+
const schemaVersion = value.schemaVersion;
|
|
1498
1500
|
const storedConversationId = toOptionalString(value.conversationId);
|
|
1499
1501
|
const createdAtMs = toOptionalNumber(value.createdAtMs);
|
|
1500
1502
|
const lastActivityAtMs = toOptionalNumber(value.lastActivityAtMs);
|
|
1501
1503
|
const updatedAtMs = toOptionalNumber(value.updatedAtMs);
|
|
1502
|
-
const execution = normalizeExecution(
|
|
1504
|
+
const execution = normalizeExecution(
|
|
1505
|
+
conversationId,
|
|
1506
|
+
value.execution,
|
|
1507
|
+
schemaVersion
|
|
1508
|
+
);
|
|
1503
1509
|
const destination = value.destination === void 0 ? void 0 : parseDestination(value.destination);
|
|
1504
1510
|
if (storedConversationId !== conversationId || typeof createdAtMs !== "number" || typeof lastActivityAtMs !== "number" || typeof updatedAtMs !== "number" || !execution || value.destination !== void 0 && !destination) {
|
|
1505
1511
|
return void 0;
|
|
@@ -2107,7 +2113,7 @@ async function startConversationWork(args) {
|
|
|
2107
2113
|
{
|
|
2108
2114
|
...current.execution,
|
|
2109
2115
|
lease,
|
|
2110
|
-
status: "running",
|
|
2116
|
+
status: current.execution.status === "awaiting_resume" ? "awaiting_resume" : "running",
|
|
2111
2117
|
runId: current.execution.runId ?? randomUUID(),
|
|
2112
2118
|
lastEnqueuedAtMs: void 0
|
|
2113
2119
|
},
|
|
@@ -2159,22 +2165,25 @@ async function drainConversationMailbox(args) {
|
|
|
2159
2165
|
return pendingMessages(current);
|
|
2160
2166
|
});
|
|
2161
2167
|
if (pending.length === 0) {
|
|
2162
|
-
return [];
|
|
2168
|
+
return { changed: false, messages: [] };
|
|
2163
2169
|
}
|
|
2164
|
-
const
|
|
2170
|
+
const result = await args.handle(pending);
|
|
2165
2171
|
const pendingIds = new Set(
|
|
2166
2172
|
pending.map((message) => message.inboundMessageId)
|
|
2167
2173
|
);
|
|
2168
|
-
|
|
2174
|
+
const acknowledgedIds = new Set(
|
|
2175
|
+
result ?? pending.map((message) => message.inboundMessageId)
|
|
2176
|
+
);
|
|
2177
|
+
for (const inboundMessageId of acknowledgedIds) {
|
|
2169
2178
|
if (!pendingIds.has(inboundMessageId)) {
|
|
2170
2179
|
throw new Error(
|
|
2171
|
-
`Conversation mailbox
|
|
2180
|
+
`Conversation mailbox drain result is not pending for ${args.conversationId}`
|
|
2172
2181
|
);
|
|
2173
2182
|
}
|
|
2174
2183
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2184
|
+
if (acknowledgedIds.size === 0) {
|
|
2185
|
+
return { changed: false, messages: [] };
|
|
2186
|
+
}
|
|
2178
2187
|
await withConversationMutation(args, async (state, lock) => {
|
|
2179
2188
|
const current = await readConversation(state, args.conversationId);
|
|
2180
2189
|
if (!current || current.execution.lease?.token !== args.leaseToken) {
|
|
@@ -2183,7 +2192,7 @@ async function drainConversationMailbox(args) {
|
|
|
2183
2192
|
);
|
|
2184
2193
|
}
|
|
2185
2194
|
const pendingMessages2 = current.execution.pendingMessages.filter(
|
|
2186
|
-
(message) => !
|
|
2195
|
+
(message) => !acknowledgedIds.has(message.inboundMessageId)
|
|
2187
2196
|
);
|
|
2188
2197
|
await writeConversation(
|
|
2189
2198
|
state,
|
|
@@ -2199,7 +2208,12 @@ async function drainConversationMailbox(args) {
|
|
|
2199
2208
|
)
|
|
2200
2209
|
);
|
|
2201
2210
|
});
|
|
2202
|
-
return
|
|
2211
|
+
return {
|
|
2212
|
+
changed: true,
|
|
2213
|
+
messages: pending.filter(
|
|
2214
|
+
(message) => acknowledgedIds.has(message.inboundMessageId)
|
|
2215
|
+
)
|
|
2216
|
+
};
|
|
2203
2217
|
}
|
|
2204
2218
|
async function ackMessages(args) {
|
|
2205
2219
|
const nowMs = args.nowMs ?? now();
|
|
@@ -2260,6 +2274,28 @@ async function requestConversationContinuation(args) {
|
|
|
2260
2274
|
return true;
|
|
2261
2275
|
});
|
|
2262
2276
|
}
|
|
2277
|
+
async function beginConversationResume(args) {
|
|
2278
|
+
const nowMs = args.nowMs ?? now();
|
|
2279
|
+
return await withConversationMutation(args, async (state, lock) => {
|
|
2280
|
+
const current = await readConversation(state, args.conversationId);
|
|
2281
|
+
if (!current || current.execution.lease?.token !== args.leaseToken || current.execution.status !== "awaiting_resume") {
|
|
2282
|
+
return false;
|
|
2283
|
+
}
|
|
2284
|
+
await writeConversation(
|
|
2285
|
+
state,
|
|
2286
|
+
lock,
|
|
2287
|
+
withExecutionUpdate(
|
|
2288
|
+
current,
|
|
2289
|
+
{
|
|
2290
|
+
...current.execution,
|
|
2291
|
+
status: "running"
|
|
2292
|
+
},
|
|
2293
|
+
nowMs
|
|
2294
|
+
)
|
|
2295
|
+
);
|
|
2296
|
+
return true;
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2263
2299
|
async function releaseConversationWork(args) {
|
|
2264
2300
|
const nowMs = args.nowMs ?? now();
|
|
2265
2301
|
return await withConversationMutation(args, async (state, lock) => {
|
|
@@ -2301,7 +2337,7 @@ async function completeConversationWork(args) {
|
|
|
2301
2337
|
{
|
|
2302
2338
|
...current.execution,
|
|
2303
2339
|
lease: void 0,
|
|
2304
|
-
status:
|
|
2340
|
+
status: needsRun ? "awaiting_resume" : hasPending ? "pending" : "idle",
|
|
2305
2341
|
runId: runnable ? current.execution.runId : void 0
|
|
2306
2342
|
},
|
|
2307
2343
|
nowMs
|
|
@@ -2410,7 +2446,7 @@ async function clearExpiredConversationLease(args) {
|
|
|
2410
2446
|
{
|
|
2411
2447
|
...current.execution,
|
|
2412
2448
|
lease: void 0,
|
|
2413
|
-
status: "
|
|
2449
|
+
status: "awaiting_resume"
|
|
2414
2450
|
},
|
|
2415
2451
|
nowMs
|
|
2416
2452
|
)
|
|
@@ -2501,6 +2537,9 @@ async function ensureConversationWake(args) {
|
|
|
2501
2537
|
if (!conversation.destination) {
|
|
2502
2538
|
return { status: "no_work" };
|
|
2503
2539
|
}
|
|
2540
|
+
if (args.replaceExistingWake !== true && conversation.execution.lease && conversation.execution.lease.expiresAtMs > nowMs) {
|
|
2541
|
+
return { status: "lease_active" };
|
|
2542
|
+
}
|
|
2504
2543
|
if (args.replaceExistingWake !== true && hasRecentEnqueueMarker(conversation, nowMs)) {
|
|
2505
2544
|
return { status: "already_enqueued" };
|
|
2506
2545
|
}
|
|
@@ -2612,10 +2651,10 @@ async function checkInConversationWork2(args) {
|
|
|
2612
2651
|
}
|
|
2613
2652
|
async function drainConversationMailbox2(args) {
|
|
2614
2653
|
const result = await drainConversationMailbox(args);
|
|
2615
|
-
if (result.
|
|
2654
|
+
if (result.changed) {
|
|
2616
2655
|
await recordExecutionMetadata(args);
|
|
2617
2656
|
}
|
|
2618
|
-
return result;
|
|
2657
|
+
return result.messages;
|
|
2619
2658
|
}
|
|
2620
2659
|
async function ackMessages2(args) {
|
|
2621
2660
|
const result = await ackMessages(args);
|
|
@@ -2627,6 +2666,13 @@ async function requestConversationContinuation2(args) {
|
|
|
2627
2666
|
await recordExecutionMetadata(args);
|
|
2628
2667
|
return result;
|
|
2629
2668
|
}
|
|
2669
|
+
async function beginConversationResume2(args) {
|
|
2670
|
+
const result = await beginConversationResume(args);
|
|
2671
|
+
if (result) {
|
|
2672
|
+
await recordExecutionMetadata(args);
|
|
2673
|
+
}
|
|
2674
|
+
return result;
|
|
2675
|
+
}
|
|
2630
2676
|
async function releaseConversationWork2(args) {
|
|
2631
2677
|
const result = await releaseConversationWork(args);
|
|
2632
2678
|
await recordExecutionMetadata(args);
|
|
@@ -4065,9 +4111,26 @@ async function startSlackProcessingReactionForMessage(args) {
|
|
|
4065
4111
|
}
|
|
4066
4112
|
|
|
4067
4113
|
// src/chat/services/auth-pause-response.ts
|
|
4068
|
-
|
|
4114
|
+
var MAX_AUTH_REQUEST_LENGTH = 200;
|
|
4115
|
+
function formatAuthRequest(requestText) {
|
|
4116
|
+
const normalized = requestText.replace(/\s+/g, " ").trim();
|
|
4117
|
+
if (!normalized) {
|
|
4118
|
+
return void 0;
|
|
4119
|
+
}
|
|
4120
|
+
const bounded = normalized.length > MAX_AUTH_REQUEST_LENGTH ? `${normalized.slice(0, MAX_AUTH_REQUEST_LENGTH - 1).trimEnd()}\u2026` : normalized;
|
|
4121
|
+
return bounded.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
4122
|
+
}
|
|
4123
|
+
function buildAuthPauseResponse(slackUserId, providerDisplayName, requestText) {
|
|
4069
4124
|
const mention = slackUserId ? `<@${slackUserId}> ` : "";
|
|
4070
|
-
|
|
4125
|
+
const request = requestText ? formatAuthRequest(requestText) : void 0;
|
|
4126
|
+
if (!request) {
|
|
4127
|
+
return `${mention}I'll need you to authorize ${providerDisplayName}. I sent you a link.`;
|
|
4128
|
+
}
|
|
4129
|
+
return `${mention}I need access to ${providerDisplayName} to continue.
|
|
4130
|
+
|
|
4131
|
+
*Why:* ${request}
|
|
4132
|
+
|
|
4133
|
+
I sent you a link.`;
|
|
4071
4134
|
}
|
|
4072
4135
|
|
|
4073
4136
|
// src/chat/runtime/request-deadline.ts
|
|
@@ -4313,12 +4376,11 @@ function createResumeReplyContext(args, statusSession, delivery) {
|
|
|
4313
4376
|
delivery,
|
|
4314
4377
|
durability: {
|
|
4315
4378
|
...replyContext.durability,
|
|
4316
|
-
|
|
4379
|
+
onSandboxRefChanged: async (sandboxRef) => {
|
|
4317
4380
|
await persistThreadStateById(threadId, {
|
|
4318
|
-
|
|
4319
|
-
sandboxDependencyProfileHash: sandbox.sandboxDependencyProfileHash
|
|
4381
|
+
sandboxRef
|
|
4320
4382
|
});
|
|
4321
|
-
await replyContext.durability?.
|
|
4383
|
+
await replyContext.durability?.onSandboxRefChanged?.(sandboxRef);
|
|
4322
4384
|
},
|
|
4323
4385
|
onArtifactStateUpdated: async (artifacts) => {
|
|
4324
4386
|
await persistThreadStateById(threadId, { artifacts });
|
|
@@ -4519,7 +4581,8 @@ async function resumeSlackTurn(args) {
|
|
|
4519
4581
|
if (outcome.status === "awaiting_auth" && onAuthPause) {
|
|
4520
4582
|
deferredAuthInfo = {
|
|
4521
4583
|
providerDisplayName: outcome.providerDisplayName,
|
|
4522
|
-
actorId: isUserActor(resumeActor) ? resumeActor.userId : void 0
|
|
4584
|
+
actorId: isUserActor(resumeActor) ? resumeActor.userId : void 0,
|
|
4585
|
+
...outcome.requestText ? { requestText: outcome.requestText } : {}
|
|
4523
4586
|
};
|
|
4524
4587
|
deferredPauseHandler = async () => {
|
|
4525
4588
|
await onAuthPause({
|
|
@@ -4673,7 +4736,8 @@ async function resumeSlackTurn(args) {
|
|
|
4673
4736
|
runArgs.threadTs,
|
|
4674
4737
|
buildAuthPauseResponse(
|
|
4675
4738
|
deferredAuthInfo.actorId,
|
|
4676
|
-
deferredAuthInfo.providerDisplayName
|
|
4739
|
+
deferredAuthInfo.providerDisplayName,
|
|
4740
|
+
deferredAuthInfo.requestText
|
|
4677
4741
|
),
|
|
4678
4742
|
runArgs.conversationId
|
|
4679
4743
|
);
|
|
@@ -5067,7 +5131,7 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
5067
5131
|
state: {
|
|
5068
5132
|
artifactState: lockedArtifacts,
|
|
5069
5133
|
pendingAuth: lockedPendingAuth,
|
|
5070
|
-
|
|
5134
|
+
sandboxRef: getPersistedSandboxState(lockedState)
|
|
5071
5135
|
},
|
|
5072
5136
|
durability: {
|
|
5073
5137
|
recordPendingAuth: async (nextPendingAuth) => {
|
|
@@ -5665,7 +5729,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
|
|
|
5665
5729
|
state: {
|
|
5666
5730
|
artifactState: lockedArtifacts,
|
|
5667
5731
|
pendingAuth: lockedPendingAuth,
|
|
5668
|
-
|
|
5732
|
+
sandboxRef: getPersistedSandboxState(lockedState)
|
|
5669
5733
|
},
|
|
5670
5734
|
durability: {
|
|
5671
5735
|
recordPendingAuth: async (nextPendingAuth) => {
|
|
@@ -6302,7 +6366,7 @@ import {
|
|
|
6302
6366
|
Message,
|
|
6303
6367
|
ThreadImpl
|
|
6304
6368
|
} from "chat";
|
|
6305
|
-
import { z as
|
|
6369
|
+
import { z as z4 } from "zod";
|
|
6306
6370
|
|
|
6307
6371
|
// src/chat/ingress/message-router.ts
|
|
6308
6372
|
function normalizeIncomingSlackThreadId(threadId, message) {
|
|
@@ -6473,14 +6537,14 @@ async function ensureSlackMessageActorIdentity(message, teamId, lookupSlackUser2
|
|
|
6473
6537
|
}
|
|
6474
6538
|
|
|
6475
6539
|
// src/chat/slack/user.ts
|
|
6476
|
-
import { z as
|
|
6477
|
-
var slackUserInfoSchema =
|
|
6478
|
-
ok:
|
|
6479
|
-
user:
|
|
6480
|
-
name:
|
|
6481
|
-
profile:
|
|
6482
|
-
email:
|
|
6483
|
-
real_name:
|
|
6540
|
+
import { z as z3 } from "zod";
|
|
6541
|
+
var slackUserInfoSchema = z3.object({
|
|
6542
|
+
ok: z3.literal(true),
|
|
6543
|
+
user: z3.object({
|
|
6544
|
+
name: z3.string().nullish(),
|
|
6545
|
+
profile: z3.object({
|
|
6546
|
+
email: z3.string().nullish(),
|
|
6547
|
+
real_name: z3.string().nullish()
|
|
6484
6548
|
}).optional()
|
|
6485
6549
|
})
|
|
6486
6550
|
});
|
|
@@ -6568,14 +6632,14 @@ async function lookupSlackUser(teamId, userId) {
|
|
|
6568
6632
|
}
|
|
6569
6633
|
|
|
6570
6634
|
// src/chat/task-execution/slack-work.ts
|
|
6571
|
-
var slackConversationRouteSchema =
|
|
6572
|
-
var serializedDateSchema =
|
|
6573
|
-
var mdastPointSchema =
|
|
6574
|
-
column:
|
|
6575
|
-
line:
|
|
6576
|
-
offset:
|
|
6635
|
+
var slackConversationRouteSchema = z4.enum(["mention", "subscribed"]);
|
|
6636
|
+
var serializedDateSchema = z4.iso.datetime();
|
|
6637
|
+
var mdastPointSchema = z4.object({
|
|
6638
|
+
column: z4.number().int().positive(),
|
|
6639
|
+
line: z4.number().int().positive(),
|
|
6640
|
+
offset: z4.number().int().nonnegative().optional()
|
|
6577
6641
|
}).strict();
|
|
6578
|
-
var mdastPositionSchema =
|
|
6642
|
+
var mdastPositionSchema = z4.object({
|
|
6579
6643
|
end: mdastPointSchema,
|
|
6580
6644
|
start: mdastPointSchema
|
|
6581
6645
|
}).strict();
|
|
@@ -6687,7 +6751,7 @@ function isFormattedContentNode(value) {
|
|
|
6687
6751
|
return false;
|
|
6688
6752
|
}
|
|
6689
6753
|
}
|
|
6690
|
-
var formattedContentSchema =
|
|
6754
|
+
var formattedContentSchema = z4.custom(
|
|
6691
6755
|
(value) => {
|
|
6692
6756
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
6693
6757
|
return false;
|
|
@@ -6697,79 +6761,79 @@ var formattedContentSchema = z3.custom(
|
|
|
6697
6761
|
},
|
|
6698
6762
|
"must be a valid formatted-content root"
|
|
6699
6763
|
);
|
|
6700
|
-
var serializedMessageSchema =
|
|
6701
|
-
_type:
|
|
6702
|
-
attachments:
|
|
6703
|
-
|
|
6704
|
-
type:
|
|
6705
|
-
url:
|
|
6706
|
-
name:
|
|
6707
|
-
mimeType:
|
|
6708
|
-
size:
|
|
6709
|
-
width:
|
|
6710
|
-
height:
|
|
6711
|
-
fetchMetadata:
|
|
6764
|
+
var serializedMessageSchema = z4.object({
|
|
6765
|
+
_type: z4.literal("chat:Message"),
|
|
6766
|
+
attachments: z4.array(
|
|
6767
|
+
z4.object({
|
|
6768
|
+
type: z4.enum(["image", "file", "video", "audio"]),
|
|
6769
|
+
url: z4.string().optional(),
|
|
6770
|
+
name: z4.string().optional(),
|
|
6771
|
+
mimeType: z4.string().optional(),
|
|
6772
|
+
size: z4.number().finite().optional(),
|
|
6773
|
+
width: z4.number().finite().optional(),
|
|
6774
|
+
height: z4.number().finite().optional(),
|
|
6775
|
+
fetchMetadata: z4.record(z4.string(), z4.string()).optional()
|
|
6712
6776
|
}).strict()
|
|
6713
6777
|
),
|
|
6714
|
-
author:
|
|
6715
|
-
userId:
|
|
6716
|
-
userName:
|
|
6717
|
-
fullName:
|
|
6718
|
-
isBot:
|
|
6719
|
-
isMe:
|
|
6778
|
+
author: z4.object({
|
|
6779
|
+
userId: z4.string(),
|
|
6780
|
+
userName: z4.string(),
|
|
6781
|
+
fullName: z4.string(),
|
|
6782
|
+
isBot: z4.union([z4.boolean(), z4.literal("unknown")]),
|
|
6783
|
+
isMe: z4.boolean()
|
|
6720
6784
|
}).strict(),
|
|
6721
6785
|
formatted: formattedContentSchema,
|
|
6722
|
-
id:
|
|
6723
|
-
isMention:
|
|
6724
|
-
links:
|
|
6725
|
-
|
|
6726
|
-
url:
|
|
6727
|
-
title:
|
|
6728
|
-
description:
|
|
6729
|
-
imageUrl:
|
|
6730
|
-
siteName:
|
|
6786
|
+
id: z4.string().min(1),
|
|
6787
|
+
isMention: z4.boolean().optional(),
|
|
6788
|
+
links: z4.array(
|
|
6789
|
+
z4.object({
|
|
6790
|
+
url: z4.string(),
|
|
6791
|
+
title: z4.string().optional(),
|
|
6792
|
+
description: z4.string().optional(),
|
|
6793
|
+
imageUrl: z4.string().optional(),
|
|
6794
|
+
siteName: z4.string().optional()
|
|
6731
6795
|
}).strict()
|
|
6732
6796
|
).optional(),
|
|
6733
|
-
metadata:
|
|
6797
|
+
metadata: z4.object({
|
|
6734
6798
|
dateSent: serializedDateSchema,
|
|
6735
|
-
edited:
|
|
6799
|
+
edited: z4.boolean(),
|
|
6736
6800
|
editedAt: serializedDateSchema.optional()
|
|
6737
6801
|
}).strict(),
|
|
6738
|
-
raw:
|
|
6739
|
-
text:
|
|
6740
|
-
threadId:
|
|
6802
|
+
raw: z4.unknown(),
|
|
6803
|
+
text: z4.string(),
|
|
6804
|
+
threadId: z4.string().min(1)
|
|
6741
6805
|
}).strict();
|
|
6742
|
-
var serializedThreadSchema =
|
|
6743
|
-
_type:
|
|
6744
|
-
adapterName:
|
|
6745
|
-
channelId:
|
|
6746
|
-
channelVisibility:
|
|
6806
|
+
var serializedThreadSchema = z4.object({
|
|
6807
|
+
_type: z4.literal("chat:Thread"),
|
|
6808
|
+
adapterName: z4.literal("slack"),
|
|
6809
|
+
channelId: z4.string().min(1),
|
|
6810
|
+
channelVisibility: z4.enum(["private", "workspace", "external", "unknown"]).optional(),
|
|
6747
6811
|
currentMessage: serializedMessageSchema.optional(),
|
|
6748
|
-
id:
|
|
6749
|
-
isDM:
|
|
6812
|
+
id: z4.string().min(1),
|
|
6813
|
+
isDM: z4.boolean()
|
|
6750
6814
|
}).strict();
|
|
6751
|
-
var slackInstallationSchema =
|
|
6752
|
-
enterpriseId:
|
|
6753
|
-
isEnterpriseInstall:
|
|
6754
|
-
teamId:
|
|
6815
|
+
var slackInstallationSchema = z4.object({
|
|
6816
|
+
enterpriseId: z4.string().optional(),
|
|
6817
|
+
isEnterpriseInstall: z4.boolean().optional(),
|
|
6818
|
+
teamId: z4.string().optional()
|
|
6755
6819
|
}).strict();
|
|
6756
|
-
var slackConversationMessageMetadataBaseSchema =
|
|
6820
|
+
var slackConversationMessageMetadataBaseSchema = z4.object({
|
|
6757
6821
|
installation: slackInstallationSchema.optional(),
|
|
6758
6822
|
message: serializedMessageSchema,
|
|
6759
|
-
platform:
|
|
6823
|
+
platform: z4.literal("slack"),
|
|
6760
6824
|
route: slackConversationRouteSchema,
|
|
6761
6825
|
thread: serializedThreadSchema
|
|
6762
6826
|
});
|
|
6763
|
-
var slackConversationMessageMetadataSchema =
|
|
6827
|
+
var slackConversationMessageMetadataSchema = z4.union([
|
|
6764
6828
|
slackConversationMessageMetadataBaseSchema.strict(),
|
|
6765
6829
|
slackConversationMessageMetadataBaseSchema.extend({
|
|
6766
|
-
kind:
|
|
6767
|
-
resourceEvent:
|
|
6768
|
-
eventKey:
|
|
6769
|
-
eventType:
|
|
6770
|
-
provider:
|
|
6771
|
-
resourceRef:
|
|
6772
|
-
subscriptionId:
|
|
6830
|
+
kind: z4.literal("resource_event"),
|
|
6831
|
+
resourceEvent: z4.object({
|
|
6832
|
+
eventKey: z4.string(),
|
|
6833
|
+
eventType: z4.string(),
|
|
6834
|
+
provider: z4.string(),
|
|
6835
|
+
resourceRef: z4.string(),
|
|
6836
|
+
subscriptionId: z4.string()
|
|
6773
6837
|
}).strict()
|
|
6774
6838
|
}).strict()
|
|
6775
6839
|
]);
|
|
@@ -6859,6 +6923,7 @@ function createSlackResourceEventInboundMessage(input) {
|
|
|
6859
6923
|
createdAtMs: input.event.occurredAtMs,
|
|
6860
6924
|
destination,
|
|
6861
6925
|
inboundMessageId: `resource-event:${input.subscription.id}:${input.event.eventKey}`,
|
|
6926
|
+
delivery: "defer",
|
|
6862
6927
|
source: "resource_event",
|
|
6863
6928
|
receivedAtMs: Date.now(),
|
|
6864
6929
|
input: {
|
|
@@ -6903,10 +6968,6 @@ function routeForRecords(records) {
|
|
|
6903
6968
|
return metadata.route === "mention";
|
|
6904
6969
|
}) ? "mention" : "subscribed";
|
|
6905
6970
|
}
|
|
6906
|
-
function isSlackAssistantThreadUserMessage(message) {
|
|
6907
|
-
const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
|
|
6908
|
-
return raw?.channel_type === "im" && typeof raw.thread_ts === "string" && raw.thread_ts.trim().length > 0;
|
|
6909
|
-
}
|
|
6910
6971
|
function isResourceEventNotificationMessage(message) {
|
|
6911
6972
|
const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
|
|
6912
6973
|
return raw?.event_type === "resource_event";
|
|
@@ -7001,7 +7062,9 @@ function createSlackConversationWorker(options) {
|
|
|
7001
7062
|
installation: { teamId: destination.teamId },
|
|
7002
7063
|
state,
|
|
7003
7064
|
task: async () => {
|
|
7004
|
-
await options.resumeAwaitingContinuation(context.conversationId
|
|
7065
|
+
await options.resumeAwaitingContinuation(context.conversationId, {
|
|
7066
|
+
shouldYield: context.shouldYield
|
|
7067
|
+
});
|
|
7005
7068
|
}
|
|
7006
7069
|
});
|
|
7007
7070
|
return { status: "completed" };
|
|
@@ -7069,21 +7132,12 @@ function createSlackConversationWorker(options) {
|
|
|
7069
7132
|
};
|
|
7070
7133
|
const drainSteeringMessages = async (accept) => {
|
|
7071
7134
|
await context.attempt.drain(async (pendingRecords) => {
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
if (!metadata) {
|
|
7075
|
-
throw new Error(
|
|
7076
|
-
"Conversation mailbox record is not Slack metadata"
|
|
7077
|
-
);
|
|
7078
|
-
}
|
|
7079
|
-
const message = restoreMessage({ adapter, record });
|
|
7080
|
-
return {
|
|
7081
|
-
activeRequest: metadata.route === "mention" || isSlackAssistantThreadUserMessage(message),
|
|
7135
|
+
return await accept(
|
|
7136
|
+
pendingRecords.map((record) => ({
|
|
7082
7137
|
inboundMessageId: record.inboundMessageId,
|
|
7083
|
-
message
|
|
7084
|
-
}
|
|
7085
|
-
|
|
7086
|
-
return await accept(messages2);
|
|
7138
|
+
message: restoreMessage({ adapter, record })
|
|
7139
|
+
}))
|
|
7140
|
+
);
|
|
7087
7141
|
});
|
|
7088
7142
|
};
|
|
7089
7143
|
try {
|
|
@@ -7096,16 +7150,20 @@ function createSlackConversationWorker(options) {
|
|
|
7096
7150
|
isFinalAttempt: context.attempt.isFinalAttempt,
|
|
7097
7151
|
shouldYield: context.shouldYield
|
|
7098
7152
|
});
|
|
7099
|
-
|
|
7153
|
+
} else {
|
|
7154
|
+
await options.runtime.handleSubscribedMessage(
|
|
7155
|
+
thread,
|
|
7156
|
+
latestMessage,
|
|
7157
|
+
{
|
|
7158
|
+
destination: context.destination,
|
|
7159
|
+
messageContext,
|
|
7160
|
+
drainSteeringMessages,
|
|
7161
|
+
ack,
|
|
7162
|
+
isFinalAttempt: context.attempt.isFinalAttempt,
|
|
7163
|
+
shouldYield: context.shouldYield
|
|
7164
|
+
}
|
|
7165
|
+
);
|
|
7100
7166
|
}
|
|
7101
|
-
await options.runtime.handleSubscribedMessage(thread, latestMessage, {
|
|
7102
|
-
destination: context.destination,
|
|
7103
|
-
messageContext,
|
|
7104
|
-
drainSteeringMessages,
|
|
7105
|
-
ack,
|
|
7106
|
-
isFinalAttempt: context.attempt.isFinalAttempt,
|
|
7107
|
-
shouldYield: context.shouldYield
|
|
7108
|
-
});
|
|
7109
7167
|
} catch (error) {
|
|
7110
7168
|
if (isTurnInputDeferredError(error)) {
|
|
7111
7169
|
return { status: "deferred" };
|
|
@@ -7144,6 +7202,7 @@ function buildSlackInboundMessage(args) {
|
|
|
7144
7202
|
args.conversationId,
|
|
7145
7203
|
args.message.id
|
|
7146
7204
|
].join(":"),
|
|
7205
|
+
delivery: args.message.isMention ? "interrupt" : "defer",
|
|
7147
7206
|
source: "slack",
|
|
7148
7207
|
createdAtMs: args.message.metadata.dateSent.getTime(),
|
|
7149
7208
|
receivedAtMs: args.receivedAtMs,
|
|
@@ -7935,15 +7994,24 @@ import {
|
|
|
7935
7994
|
|
|
7936
7995
|
// src/chat/task-execution/worker.ts
|
|
7937
7996
|
var CONVERSATION_WORK_DEFER_DELAY_MS = 15e3;
|
|
7938
|
-
var CONVERSATION_WORK_SOFT_YIELD_AFTER_MS = 24e4;
|
|
7939
7997
|
function now3(options) {
|
|
7940
7998
|
return options.nowMs?.() ?? Date.now();
|
|
7941
7999
|
}
|
|
8000
|
+
function selectAttemptMessages(work) {
|
|
8001
|
+
const messages = work.messages;
|
|
8002
|
+
const interrupts = messages.filter(
|
|
8003
|
+
(message) => message.delivery === "interrupt"
|
|
8004
|
+
);
|
|
8005
|
+
if (interrupts.length > 0) {
|
|
8006
|
+
return interrupts;
|
|
8007
|
+
}
|
|
8008
|
+
return work.execution.status === "awaiting_resume" ? [] : messages;
|
|
8009
|
+
}
|
|
7942
8010
|
function nudgeIdempotencyKey(reason, conversationId, nowMs) {
|
|
7943
8011
|
return `${reason}:${conversationId}:${nowMs}`;
|
|
7944
8012
|
}
|
|
7945
8013
|
async function requestLostLeaseRecovery(args) {
|
|
7946
|
-
const
|
|
8014
|
+
const resumeRequested = await requestConversationContinuation2({
|
|
7947
8015
|
conversationId: args.conversationId,
|
|
7948
8016
|
destination: args.destination,
|
|
7949
8017
|
leaseToken: args.leaseToken,
|
|
@@ -7951,7 +8019,7 @@ async function requestLostLeaseRecovery(args) {
|
|
|
7951
8019
|
nowMs: args.nowMs,
|
|
7952
8020
|
state: args.options.state
|
|
7953
8021
|
});
|
|
7954
|
-
if (!
|
|
8022
|
+
if (!resumeRequested) {
|
|
7955
8023
|
return;
|
|
7956
8024
|
}
|
|
7957
8025
|
const released = await releaseConversationWork2({
|
|
@@ -8115,15 +8183,8 @@ async function processConversationWork(message, options) {
|
|
|
8115
8183
|
return { status: "active" };
|
|
8116
8184
|
}
|
|
8117
8185
|
const startedAtMs = now3(options);
|
|
8118
|
-
const softYieldDeadlineMs = startedAtMs + (options.softYieldAfterMs ??
|
|
8119
|
-
|
|
8120
|
-
conversationId,
|
|
8121
|
-
state: options.state
|
|
8122
|
-
});
|
|
8123
|
-
const attemptMessages = leasedWork?.messages ?? initial.messages;
|
|
8124
|
-
const attemptMessageIds = attemptMessages.map(
|
|
8125
|
-
(message2) => message2.inboundMessageId
|
|
8126
|
-
);
|
|
8186
|
+
const softYieldDeadlineMs = startedAtMs + (options.softYieldAfterMs ?? getChatConfig().conversationWorkSoftYieldAfterMs);
|
|
8187
|
+
let attemptMessageIds = [];
|
|
8127
8188
|
let leaseLost = false;
|
|
8128
8189
|
const markLeaseLost = () => {
|
|
8129
8190
|
leaseLost = true;
|
|
@@ -8143,166 +8204,246 @@ async function processConversationWork(message, options) {
|
|
|
8143
8204
|
},
|
|
8144
8205
|
"Conversation work lease acquired"
|
|
8145
8206
|
);
|
|
8146
|
-
const
|
|
8207
|
+
const drain = (handle) => drainConversationMailbox2({
|
|
8147
8208
|
conversationId,
|
|
8148
8209
|
leaseToken: lease.leaseToken,
|
|
8149
8210
|
conversationStore: options.conversationStore,
|
|
8150
|
-
handle
|
|
8211
|
+
handle: async (messages) => {
|
|
8212
|
+
const candidates = messages.filter(
|
|
8213
|
+
(message2) => message2.delivery === "interrupt"
|
|
8214
|
+
);
|
|
8215
|
+
if (candidates.length === 0) {
|
|
8216
|
+
return [];
|
|
8217
|
+
}
|
|
8218
|
+
return await handle(candidates) ?? candidates.map((message2) => message2.inboundMessageId);
|
|
8219
|
+
},
|
|
8151
8220
|
nowMs: now3(options),
|
|
8152
8221
|
state: options.state
|
|
8153
8222
|
});
|
|
8154
|
-
const
|
|
8155
|
-
|
|
8223
|
+
const shouldYield = () => leaseLost || now3(options) >= softYieldDeadlineMs;
|
|
8224
|
+
const checkIn = async () => {
|
|
8225
|
+
const checkedIn = await checkInConversationWork2({
|
|
8156
8226
|
conversationId,
|
|
8157
|
-
inboundMessageIds: attemptMessageIds,
|
|
8158
8227
|
leaseToken: lease.leaseToken,
|
|
8159
8228
|
conversationStore: options.conversationStore,
|
|
8160
8229
|
nowMs: now3(options),
|
|
8161
8230
|
state: options.state
|
|
8162
8231
|
});
|
|
8163
|
-
if (!
|
|
8232
|
+
if (!checkedIn) {
|
|
8164
8233
|
markLeaseLost();
|
|
8165
|
-
throw new Error(
|
|
8166
|
-
`Conversation work lease lost before inbox ack for ${conversationId}`
|
|
8167
|
-
);
|
|
8168
8234
|
}
|
|
8235
|
+
return checkedIn;
|
|
8169
8236
|
};
|
|
8170
|
-
const
|
|
8171
|
-
|
|
8172
|
-
|
|
8237
|
+
const yieldWork = async () => {
|
|
8238
|
+
const yieldNowMs = now3(options);
|
|
8239
|
+
await ensureConversationWake({
|
|
8173
8240
|
conversationId,
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
}
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
state: options.state
|
|
8191
|
-
});
|
|
8192
|
-
if (!checkedIn) {
|
|
8193
|
-
markLeaseLost();
|
|
8194
|
-
}
|
|
8195
|
-
return checkedIn;
|
|
8241
|
+
conversationStore: options.conversationStore,
|
|
8242
|
+
idempotencyKey: nudgeIdempotencyKey("yield", conversationId, yieldNowMs),
|
|
8243
|
+
nowMs: yieldNowMs,
|
|
8244
|
+
queue: options.queue,
|
|
8245
|
+
replaceExistingWake: true,
|
|
8246
|
+
state: options.state
|
|
8247
|
+
});
|
|
8248
|
+
const released = await releaseConversationWork2({
|
|
8249
|
+
conversationId,
|
|
8250
|
+
leaseToken: lease.leaseToken,
|
|
8251
|
+
conversationStore: options.conversationStore,
|
|
8252
|
+
nowMs: yieldNowMs,
|
|
8253
|
+
state: options.state
|
|
8254
|
+
});
|
|
8255
|
+
if (!released) {
|
|
8256
|
+
return { status: "lost_lease" };
|
|
8196
8257
|
}
|
|
8258
|
+
logInfo(
|
|
8259
|
+
"conversation_work_cooperative_yield",
|
|
8260
|
+
{ conversationId },
|
|
8261
|
+
{
|
|
8262
|
+
"app.worker.elapsed_ms": now3(options) - startedAtMs,
|
|
8263
|
+
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
8264
|
+
},
|
|
8265
|
+
"Conversation work yielded cooperatively"
|
|
8266
|
+
);
|
|
8267
|
+
return { status: "yielded" };
|
|
8197
8268
|
};
|
|
8198
8269
|
try {
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
destination,
|
|
8204
|
-
leaseToken: lease.leaseToken,
|
|
8205
|
-
nowMs: now3(options),
|
|
8206
|
-
options
|
|
8207
|
-
});
|
|
8208
|
-
return { status: "lost_lease" };
|
|
8209
|
-
}
|
|
8210
|
-
if (leaseLost) {
|
|
8211
|
-
await requestLostLeaseRecovery({
|
|
8270
|
+
let hasRun = false;
|
|
8271
|
+
while (true) {
|
|
8272
|
+
attemptMessageIds = [];
|
|
8273
|
+
const leasedWork = await getConversationWorkState2({
|
|
8212
8274
|
conversationId,
|
|
8213
|
-
destination,
|
|
8214
|
-
leaseToken: lease.leaseToken,
|
|
8215
|
-
nowMs: now3(options),
|
|
8216
|
-
options
|
|
8217
|
-
});
|
|
8218
|
-
return { status: "lost_lease" };
|
|
8219
|
-
}
|
|
8220
|
-
if (result.status === "yielded") {
|
|
8221
|
-
const yieldNowMs = now3(options);
|
|
8222
|
-
const continuationMarked = await requestConversationContinuation2({
|
|
8223
|
-
conversationId,
|
|
8224
|
-
destination,
|
|
8225
|
-
leaseToken: lease.leaseToken,
|
|
8226
|
-
conversationStore: options.conversationStore,
|
|
8227
|
-
nowMs: yieldNowMs,
|
|
8228
8275
|
state: options.state
|
|
8229
8276
|
});
|
|
8230
|
-
if (!
|
|
8277
|
+
if (!leasedWork || leasedWork.lease?.leaseToken !== lease.leaseToken || leaseLost) {
|
|
8278
|
+
markLeaseLost();
|
|
8279
|
+
await requestLostLeaseRecovery({
|
|
8280
|
+
conversationId,
|
|
8281
|
+
destination,
|
|
8282
|
+
leaseToken: lease.leaseToken,
|
|
8283
|
+
nowMs: now3(options),
|
|
8284
|
+
options
|
|
8285
|
+
});
|
|
8231
8286
|
return { status: "lost_lease" };
|
|
8232
8287
|
}
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8288
|
+
const resumePending = leasedWork.execution.status === "awaiting_resume";
|
|
8289
|
+
const attemptMessages = selectAttemptMessages(leasedWork);
|
|
8290
|
+
if (hasRun && shouldYield()) {
|
|
8291
|
+
if (resumePending) {
|
|
8292
|
+
return await yieldWork();
|
|
8293
|
+
}
|
|
8294
|
+
break;
|
|
8295
|
+
}
|
|
8296
|
+
if (resumePending && attemptMessages.length === 0) {
|
|
8297
|
+
const resumeStarted = await beginConversationResume2({
|
|
8238
8298
|
conversationId,
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
},
|
|
8259
|
-
"Conversation work yielded cooperatively"
|
|
8299
|
+
leaseToken: lease.leaseToken,
|
|
8300
|
+
conversationStore: options.conversationStore,
|
|
8301
|
+
nowMs: now3(options),
|
|
8302
|
+
state: options.state
|
|
8303
|
+
});
|
|
8304
|
+
if (!resumeStarted) {
|
|
8305
|
+
markLeaseLost();
|
|
8306
|
+
await requestLostLeaseRecovery({
|
|
8307
|
+
conversationId,
|
|
8308
|
+
destination,
|
|
8309
|
+
leaseToken: lease.leaseToken,
|
|
8310
|
+
nowMs: now3(options),
|
|
8311
|
+
options
|
|
8312
|
+
});
|
|
8313
|
+
return { status: "lost_lease" };
|
|
8314
|
+
}
|
|
8315
|
+
}
|
|
8316
|
+
attemptMessageIds = attemptMessages.map(
|
|
8317
|
+
(message2) => message2.inboundMessageId
|
|
8260
8318
|
);
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8319
|
+
const ack = async () => {
|
|
8320
|
+
const acknowledged = await ackMessages2({
|
|
8321
|
+
conversationId,
|
|
8322
|
+
inboundMessageIds: attemptMessageIds,
|
|
8323
|
+
leaseToken: lease.leaseToken,
|
|
8324
|
+
conversationStore: options.conversationStore,
|
|
8325
|
+
nowMs: now3(options),
|
|
8326
|
+
state: options.state
|
|
8327
|
+
});
|
|
8328
|
+
if (!acknowledged) {
|
|
8329
|
+
markLeaseLost();
|
|
8330
|
+
throw new Error(
|
|
8331
|
+
`Conversation work lease lost before inbox ack for ${conversationId}`
|
|
8332
|
+
);
|
|
8333
|
+
}
|
|
8334
|
+
};
|
|
8335
|
+
const workerContext = {
|
|
8336
|
+
attempt: {
|
|
8337
|
+
ack,
|
|
8338
|
+
conversationId,
|
|
8339
|
+
destination,
|
|
8340
|
+
drain,
|
|
8341
|
+
isFinalAttempt: attemptMessages.some(
|
|
8342
|
+
(message2) => isFinalAttempt(message2)
|
|
8343
|
+
),
|
|
8344
|
+
messages: attemptMessages
|
|
8345
|
+
},
|
|
8266
8346
|
conversationId,
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8347
|
+
destination,
|
|
8348
|
+
shouldYield,
|
|
8349
|
+
checkIn
|
|
8350
|
+
};
|
|
8351
|
+
const result = await options.run(workerContext);
|
|
8352
|
+
hasRun = true;
|
|
8353
|
+
if (result.status === "lost_lease") {
|
|
8354
|
+
await requestLostLeaseRecovery({
|
|
8355
|
+
conversationId,
|
|
8356
|
+
destination,
|
|
8357
|
+
leaseToken: lease.leaseToken,
|
|
8358
|
+
nowMs: now3(options),
|
|
8359
|
+
options
|
|
8360
|
+
});
|
|
8273
8361
|
return { status: "lost_lease" };
|
|
8274
8362
|
}
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
conversationStore: options.conversationStore,
|
|
8278
|
-
idempotencyKey: nudgeIdempotencyKey(
|
|
8279
|
-
"deferred",
|
|
8363
|
+
if (leaseLost) {
|
|
8364
|
+
await requestLostLeaseRecovery({
|
|
8280
8365
|
conversationId,
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
const failure = await recordFailedDeliveryAttempt({
|
|
8291
|
-
conversationId,
|
|
8292
|
-
leaseToken: lease.leaseToken,
|
|
8293
|
-
nowMs: now3(options),
|
|
8294
|
-
messageIds: attemptMessageIds,
|
|
8295
|
-
options
|
|
8296
|
-
});
|
|
8297
|
-
if (isTerminalFailure(failure)) {
|
|
8298
|
-
await deadLetterAttempt2({
|
|
8366
|
+
destination,
|
|
8367
|
+
leaseToken: lease.leaseToken,
|
|
8368
|
+
nowMs: now3(options),
|
|
8369
|
+
options
|
|
8370
|
+
});
|
|
8371
|
+
return { status: "lost_lease" };
|
|
8372
|
+
}
|
|
8373
|
+
if (result.status === "yielded") {
|
|
8374
|
+
const resumeRequested = await requestConversationContinuation2({
|
|
8299
8375
|
conversationId,
|
|
8376
|
+
destination,
|
|
8300
8377
|
leaseToken: lease.leaseToken,
|
|
8301
8378
|
conversationStore: options.conversationStore,
|
|
8302
8379
|
nowMs: now3(options),
|
|
8303
8380
|
state: options.state
|
|
8304
8381
|
});
|
|
8305
|
-
|
|
8382
|
+
if (!resumeRequested) {
|
|
8383
|
+
return { status: "lost_lease" };
|
|
8384
|
+
}
|
|
8385
|
+
return await yieldWork();
|
|
8386
|
+
}
|
|
8387
|
+
if (result.status === "deferred") {
|
|
8388
|
+
const deferredNowMs = now3(options);
|
|
8389
|
+
const released = await releaseConversationWork2({
|
|
8390
|
+
conversationId,
|
|
8391
|
+
leaseToken: lease.leaseToken,
|
|
8392
|
+
conversationStore: options.conversationStore,
|
|
8393
|
+
nowMs: deferredNowMs,
|
|
8394
|
+
state: options.state
|
|
8395
|
+
});
|
|
8396
|
+
if (!released) {
|
|
8397
|
+
return { status: "lost_lease" };
|
|
8398
|
+
}
|
|
8399
|
+
const wake = await ensureConversationWake({
|
|
8400
|
+
conversationId,
|
|
8401
|
+
conversationStore: options.conversationStore,
|
|
8402
|
+
idempotencyKey: nudgeIdempotencyKey(
|
|
8403
|
+
"deferred",
|
|
8404
|
+
conversationId,
|
|
8405
|
+
deferredNowMs
|
|
8406
|
+
),
|
|
8407
|
+
nowMs: deferredNowMs,
|
|
8408
|
+
queue: options.queue,
|
|
8409
|
+
state: options.state
|
|
8410
|
+
});
|
|
8411
|
+
return wake.status === "enqueued" ? { status: "pending_requeued" } : { status: "completed" };
|
|
8412
|
+
}
|
|
8413
|
+
if (attemptMessageIds.length > 0) {
|
|
8414
|
+
const failure = await recordFailedDeliveryAttempt({
|
|
8415
|
+
conversationId,
|
|
8416
|
+
leaseToken: lease.leaseToken,
|
|
8417
|
+
nowMs: now3(options),
|
|
8418
|
+
messageIds: attemptMessageIds,
|
|
8419
|
+
options
|
|
8420
|
+
});
|
|
8421
|
+
if (isTerminalFailure(failure)) {
|
|
8422
|
+
await deadLetterAttempt2({
|
|
8423
|
+
conversationId,
|
|
8424
|
+
leaseToken: lease.leaseToken,
|
|
8425
|
+
conversationStore: options.conversationStore,
|
|
8426
|
+
nowMs: now3(options),
|
|
8427
|
+
state: options.state
|
|
8428
|
+
});
|
|
8429
|
+
return { status: "failed" };
|
|
8430
|
+
}
|
|
8431
|
+
if (failure.status === "lost_lease") {
|
|
8432
|
+
return { status: "lost_lease" };
|
|
8433
|
+
}
|
|
8434
|
+
if (failure.status === "recorded") {
|
|
8435
|
+
break;
|
|
8436
|
+
}
|
|
8437
|
+
}
|
|
8438
|
+
const next = await getConversationWorkState2({
|
|
8439
|
+
conversationId,
|
|
8440
|
+
state: options.state
|
|
8441
|
+
});
|
|
8442
|
+
if (!next || next.lease?.leaseToken !== lease.leaseToken) {
|
|
8443
|
+
return { status: "lost_lease" };
|
|
8444
|
+
}
|
|
8445
|
+
if (next.execution.status !== "awaiting_resume" && countPendingConversationMessages(next) === 0) {
|
|
8446
|
+
break;
|
|
8306
8447
|
}
|
|
8307
8448
|
}
|
|
8308
8449
|
const completion = await completeConversationWork2({
|
|
@@ -8360,7 +8501,7 @@ async function processConversationWork(message, options) {
|
|
|
8360
8501
|
state: options.state
|
|
8361
8502
|
});
|
|
8362
8503
|
} else {
|
|
8363
|
-
const
|
|
8504
|
+
const resumeRequested = await requestConversationContinuation2({
|
|
8364
8505
|
conversationId,
|
|
8365
8506
|
destination,
|
|
8366
8507
|
leaseToken: lease.leaseToken,
|
|
@@ -8368,7 +8509,7 @@ async function processConversationWork(message, options) {
|
|
|
8368
8509
|
nowMs: errorNowMs,
|
|
8369
8510
|
state: options.state
|
|
8370
8511
|
});
|
|
8371
|
-
if (
|
|
8512
|
+
if (resumeRequested) {
|
|
8372
8513
|
await ensureConversationWake({
|
|
8373
8514
|
conversationId,
|
|
8374
8515
|
conversationStore: options.conversationStore,
|
|
@@ -8379,6 +8520,7 @@ async function processConversationWork(message, options) {
|
|
|
8379
8520
|
),
|
|
8380
8521
|
nowMs: errorNowMs,
|
|
8381
8522
|
queue: options.queue,
|
|
8523
|
+
replaceExistingWake: true,
|
|
8382
8524
|
state: options.state
|
|
8383
8525
|
});
|
|
8384
8526
|
}
|
|
@@ -8580,14 +8722,14 @@ function registerVercelPluginTaskDevConsumer() {
|
|
|
8580
8722
|
}
|
|
8581
8723
|
|
|
8582
8724
|
// src/chat/services/subscribed-decision.ts
|
|
8583
|
-
import { z as
|
|
8584
|
-
var replyDecisionSchema =
|
|
8585
|
-
should_reply:
|
|
8586
|
-
should_unsubscribe:
|
|
8725
|
+
import { z as z5 } from "zod";
|
|
8726
|
+
var replyDecisionSchema = z5.object({
|
|
8727
|
+
should_reply: z5.boolean().describe("Whether Junior should respond to this thread message."),
|
|
8728
|
+
should_unsubscribe: z5.boolean().describe(
|
|
8587
8729
|
"Whether Junior should unsubscribe from this thread because the user clearly asked it to stop participating."
|
|
8588
8730
|
),
|
|
8589
|
-
confidence:
|
|
8590
|
-
reason:
|
|
8731
|
+
confidence: z5.number().min(0).max(1).describe("Classifier confidence from 0 to 1."),
|
|
8732
|
+
reason: z5.string().describe("Short reason for the decision; use an empty string if none.")
|
|
8591
8733
|
}).strict();
|
|
8592
8734
|
var ROUTER_CONFIDENCE_THRESHOLD = 0.8;
|
|
8593
8735
|
var ROUTER_CLASSIFIER_MAX_TOKENS = 240;
|
|
@@ -8961,12 +9103,14 @@ function createAcceptedSteeringDrain(hooks, options) {
|
|
|
8961
9103
|
await hooks.drainSteeringMessages(async (messages) => {
|
|
8962
9104
|
const selection = await options.selectMessages(messages, context);
|
|
8963
9105
|
await options.onSkipped?.(selection.skipped);
|
|
8964
|
-
const interrupted = selection.accepted.
|
|
9106
|
+
const interrupted = selection.accepted.map(
|
|
9107
|
+
(accepted) => accepted.message
|
|
9108
|
+
);
|
|
8965
9109
|
await accept(getQueuedMessagesFromSlackMessages(interrupted, options));
|
|
8966
9110
|
interruptedMessages = interrupted;
|
|
8967
9111
|
await options.onAcceptedForProcessing?.(interrupted);
|
|
8968
9112
|
return [
|
|
8969
|
-
...selection.accepted.
|
|
9113
|
+
...selection.accepted.map((accepted) => accepted.inboundMessageId),
|
|
8970
9114
|
...selection.skipped.map((skipped) => skipped.inboundMessageId)
|
|
8971
9115
|
];
|
|
8972
9116
|
});
|
|
@@ -9100,19 +9244,17 @@ function createSlackTurnRuntime(deps) {
|
|
|
9100
9244
|
rawText: appendSlackLegacyAttachmentText(message.text, message.raw),
|
|
9101
9245
|
userText: appendSlackLegacyAttachmentText(strippedUserText, message.raw)
|
|
9102
9246
|
};
|
|
9103
|
-
const isActiveRequest = candidate.activeRequest || Boolean(message.isMention);
|
|
9104
9247
|
const decision = await deps.decideSubscribedReply({
|
|
9105
9248
|
rawText: text.rawText,
|
|
9106
9249
|
text: text.userText,
|
|
9107
9250
|
conversationContext,
|
|
9108
9251
|
hasAttachments: message.attachments.length > 0 || legacyAttachmentText !== "",
|
|
9109
|
-
isExplicitMention:
|
|
9252
|
+
isExplicitMention: true,
|
|
9110
9253
|
context
|
|
9111
9254
|
});
|
|
9112
9255
|
return {
|
|
9113
9256
|
context,
|
|
9114
9257
|
decision,
|
|
9115
|
-
mode: isActiveRequest ? "interrupt" : "defer",
|
|
9116
9258
|
text
|
|
9117
9259
|
};
|
|
9118
9260
|
};
|
|
@@ -9166,7 +9308,6 @@ function createSlackTurnRuntime(deps) {
|
|
|
9166
9308
|
context: decision.context,
|
|
9167
9309
|
decision: decision.decision,
|
|
9168
9310
|
inboundMessageId: candidate.inboundMessageId,
|
|
9169
|
-
mode: decision.mode,
|
|
9170
9311
|
message: candidate.message,
|
|
9171
9312
|
text: decision.text
|
|
9172
9313
|
});
|
|
@@ -9188,8 +9329,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
9188
9329
|
return {
|
|
9189
9330
|
accepted: selected.filter((message) => message.decision.shouldReply).map((message) => ({
|
|
9190
9331
|
inboundMessageId: message.inboundMessageId,
|
|
9191
|
-
message: message.message
|
|
9192
|
-
mode: message.mode
|
|
9332
|
+
message: message.message
|
|
9193
9333
|
})),
|
|
9194
9334
|
skipped: selected.filter((message) => !message.decision.shouldReply)
|
|
9195
9335
|
};
|
|
@@ -10354,11 +10494,11 @@ function getSlackMessageTs(message) {
|
|
|
10354
10494
|
}
|
|
10355
10495
|
|
|
10356
10496
|
// src/chat/slack/action-token.ts
|
|
10357
|
-
import { z as
|
|
10358
|
-
var slackActionTokenSchema =
|
|
10359
|
-
var slackMessageEnvelopeSchema =
|
|
10360
|
-
raw:
|
|
10361
|
-
action_token:
|
|
10497
|
+
import { z as z6 } from "zod";
|
|
10498
|
+
var slackActionTokenSchema = z6.string().trim().min(1).brand();
|
|
10499
|
+
var slackMessageEnvelopeSchema = z6.object({
|
|
10500
|
+
raw: z6.object({
|
|
10501
|
+
action_token: z6.unknown().optional()
|
|
10362
10502
|
}).optional()
|
|
10363
10503
|
});
|
|
10364
10504
|
function readSlackActionToken(message) {
|
|
@@ -10749,13 +10889,14 @@ function createReplyToThread(deps) {
|
|
|
10749
10889
|
beforeFirstResponsePostCalled = true;
|
|
10750
10890
|
await options.beforeFirstResponsePost?.();
|
|
10751
10891
|
};
|
|
10752
|
-
const postAuthPauseNotice = async (providerDisplayName) => {
|
|
10892
|
+
const postAuthPauseNotice = async (providerDisplayName, requestText) => {
|
|
10753
10893
|
if (!actor) {
|
|
10754
10894
|
throw new Error("Slack auth pause notice requires an actor");
|
|
10755
10895
|
}
|
|
10756
10896
|
const text = buildAuthPauseResponse(
|
|
10757
10897
|
actor.userId,
|
|
10758
|
-
providerDisplayName
|
|
10898
|
+
providerDisplayName,
|
|
10899
|
+
requestText
|
|
10759
10900
|
);
|
|
10760
10901
|
try {
|
|
10761
10902
|
await beforeFirstResponsePost();
|
|
@@ -11353,10 +11494,7 @@ function createReplyToThread(deps) {
|
|
|
11353
11494
|
state: {
|
|
11354
11495
|
artifactState: preparedState.artifacts,
|
|
11355
11496
|
pendingAuth: preparedState.conversation.processing.pendingAuth,
|
|
11356
|
-
|
|
11357
|
-
sandboxId: preparedState.sandboxId,
|
|
11358
|
-
sandboxDependencyProfileHash: preparedState.sandboxDependencyProfileHash
|
|
11359
|
-
}
|
|
11497
|
+
sandboxRef: preparedState.sandboxRef
|
|
11360
11498
|
},
|
|
11361
11499
|
observers: {
|
|
11362
11500
|
onStatus: (nextStatus) => status.update(nextStatus),
|
|
@@ -11369,10 +11507,9 @@ function createReplyToThread(deps) {
|
|
|
11369
11507
|
onInputCommitted: options.ack,
|
|
11370
11508
|
drainSteeringMessages,
|
|
11371
11509
|
shouldYield: options.shouldYield,
|
|
11372
|
-
|
|
11510
|
+
onSandboxRefChanged: async (sandboxRef) => {
|
|
11373
11511
|
await persistThreadState(thread, {
|
|
11374
|
-
|
|
11375
|
-
sandboxDependencyProfileHash: sandbox.sandboxDependencyProfileHash
|
|
11512
|
+
sandboxRef
|
|
11376
11513
|
});
|
|
11377
11514
|
},
|
|
11378
11515
|
onArtifactStateUpdated: async (artifacts) => {
|
|
@@ -11428,7 +11565,10 @@ function createReplyToThread(deps) {
|
|
|
11428
11565
|
shouldPersistFailureState = false;
|
|
11429
11566
|
return;
|
|
11430
11567
|
}
|
|
11431
|
-
await postAuthPauseNotice(
|
|
11568
|
+
await postAuthPauseNotice(
|
|
11569
|
+
outcome.providerDisplayName,
|
|
11570
|
+
outcome.requestText
|
|
11571
|
+
);
|
|
11432
11572
|
completeAuthPauseTurn({
|
|
11433
11573
|
conversation: preparedState.conversation,
|
|
11434
11574
|
sessionId: turnId
|
|
@@ -11932,12 +12072,7 @@ async function seedConversationBackfill(thread, conversation, currentTurn) {
|
|
|
11932
12072
|
function createPrepareTurnState(deps) {
|
|
11933
12073
|
return async function prepareTurnState(args) {
|
|
11934
12074
|
const existingState = await args.thread.state;
|
|
11935
|
-
const
|
|
11936
|
-
existingState.app_sandbox_id
|
|
11937
|
-
) : void 0;
|
|
11938
|
-
const existingSandboxDependencyProfileHash = existingState ? toOptionalString(
|
|
11939
|
-
existingState.app_sandbox_dependency_profile_hash
|
|
11940
|
-
) : void 0;
|
|
12075
|
+
const sandboxRef = getPersistedSandboxState(existingState ?? {});
|
|
11941
12076
|
const artifacts = coerceThreadArtifactsState(existingState);
|
|
11942
12077
|
const conversation = coerceThreadConversationState(existingState);
|
|
11943
12078
|
const conversationId = args.context.threadId ?? args.context.runId;
|
|
@@ -12006,8 +12141,7 @@ function createPrepareTurnState(deps) {
|
|
|
12006
12141
|
configuration,
|
|
12007
12142
|
channelConfiguration,
|
|
12008
12143
|
conversation,
|
|
12009
|
-
|
|
12010
|
-
sandboxDependencyProfileHash: existingSandboxDependencyProfileHash,
|
|
12144
|
+
sandboxRef,
|
|
12011
12145
|
conversationContext,
|
|
12012
12146
|
userMessageAlreadyReplied,
|
|
12013
12147
|
userMessageId
|
|
@@ -12315,7 +12449,7 @@ async function failUnresumableContinuation(args) {
|
|
|
12315
12449
|
errorMessage: args.errorMessage
|
|
12316
12450
|
});
|
|
12317
12451
|
}
|
|
12318
|
-
async function continueSlackAgentRun(payload, options) {
|
|
12452
|
+
async function continueSlackAgentRun(payload, options, runOptions = {}) {
|
|
12319
12453
|
const thread = parseSlackThreadId(payload.conversationId);
|
|
12320
12454
|
if (!thread) {
|
|
12321
12455
|
throw new Error(
|
|
@@ -12368,7 +12502,7 @@ async function continueSlackAgentRun(payload, options) {
|
|
|
12368
12502
|
const conversationContext = buildConversationContext(conversation, {
|
|
12369
12503
|
excludeMessageId: userMessage.id
|
|
12370
12504
|
});
|
|
12371
|
-
const
|
|
12505
|
+
const sandboxRef = getPersistedSandboxState(currentState);
|
|
12372
12506
|
const destination = requireSlackDestination(
|
|
12373
12507
|
payload.destination,
|
|
12374
12508
|
"Slack continuation"
|
|
@@ -12440,9 +12574,10 @@ async function continueSlackAgentRun(payload, options) {
|
|
|
12440
12574
|
state: {
|
|
12441
12575
|
artifactState: artifacts,
|
|
12442
12576
|
pendingAuth: conversation.processing.pendingAuth,
|
|
12443
|
-
|
|
12577
|
+
sandboxRef
|
|
12444
12578
|
},
|
|
12445
12579
|
durability: {
|
|
12580
|
+
shouldYield: runOptions.shouldYield,
|
|
12446
12581
|
recordPendingAuth: async (nextPendingAuth) => {
|
|
12447
12582
|
conversation.processing.pendingAuth = nextPendingAuth;
|
|
12448
12583
|
await persistThreadStateById(payload.conversationId, {
|
|
@@ -12605,7 +12740,11 @@ async function recoverStrandedRunningSession(args) {
|
|
|
12605
12740
|
});
|
|
12606
12741
|
return false;
|
|
12607
12742
|
}
|
|
12608
|
-
if (await continueSlackAgentRunWithLockRetry(
|
|
12743
|
+
if (await continueSlackAgentRunWithLockRetry(
|
|
12744
|
+
request,
|
|
12745
|
+
args.options,
|
|
12746
|
+
args.runOptions
|
|
12747
|
+
)) {
|
|
12609
12748
|
return true;
|
|
12610
12749
|
}
|
|
12611
12750
|
await failUnresumableContinuation({
|
|
@@ -12616,13 +12755,14 @@ async function recoverStrandedRunningSession(args) {
|
|
|
12616
12755
|
});
|
|
12617
12756
|
return false;
|
|
12618
12757
|
}
|
|
12619
|
-
async function resumeAwaitingSlackContinuation(conversationId, options) {
|
|
12758
|
+
async function resumeAwaitingSlackContinuation(conversationId, options, runOptions = {}) {
|
|
12620
12759
|
const summaries = await listAgentTurnSessionSummariesForConversation(conversationId);
|
|
12621
12760
|
const newest = summaries[0];
|
|
12622
12761
|
if (newest?.state === "running") {
|
|
12623
12762
|
return await recoverStrandedRunningSession({
|
|
12624
12763
|
conversationId,
|
|
12625
12764
|
options,
|
|
12765
|
+
runOptions,
|
|
12626
12766
|
summary: newest
|
|
12627
12767
|
});
|
|
12628
12768
|
}
|
|
@@ -12642,7 +12782,7 @@ async function resumeAwaitingSlackContinuation(conversationId, options) {
|
|
|
12642
12782
|
});
|
|
12643
12783
|
continue;
|
|
12644
12784
|
}
|
|
12645
|
-
if (await continueSlackAgentRunWithLockRetry(request, options)) {
|
|
12785
|
+
if (await continueSlackAgentRunWithLockRetry(request, options, runOptions)) {
|
|
12646
12786
|
return true;
|
|
12647
12787
|
}
|
|
12648
12788
|
await failUnresumableContinuation({
|
|
@@ -12654,14 +12794,14 @@ async function resumeAwaitingSlackContinuation(conversationId, options) {
|
|
|
12654
12794
|
}
|
|
12655
12795
|
return false;
|
|
12656
12796
|
}
|
|
12657
|
-
async function continueSlackAgentRunWithLockRetry(payload, options) {
|
|
12797
|
+
async function continueSlackAgentRunWithLockRetry(payload, options, runOptions = {}) {
|
|
12658
12798
|
const scheduleAgentContinue2 = options.scheduleAgentContinue ?? scheduleAgentContinue;
|
|
12659
12799
|
for (const [attempt, delayMs] of [
|
|
12660
12800
|
...AGENT_CONTINUE_LOCK_RETRY_DELAYS_MS,
|
|
12661
12801
|
void 0
|
|
12662
12802
|
].entries()) {
|
|
12663
12803
|
try {
|
|
12664
|
-
return await continueSlackAgentRun(payload, options);
|
|
12804
|
+
return await continueSlackAgentRun(payload, options, runOptions);
|
|
12665
12805
|
} catch (error) {
|
|
12666
12806
|
if (!(error instanceof ResumeTurnBusyError)) {
|
|
12667
12807
|
throw error;
|
|
@@ -12756,10 +12896,14 @@ function createProductionConversationWorkOptions(options) {
|
|
|
12756
12896
|
run: createSlackConversationWorker({
|
|
12757
12897
|
getSlackAdapter: getProductionSlackAdapter,
|
|
12758
12898
|
conversationStore,
|
|
12759
|
-
resumeAwaitingContinuation: async (conversationId) => await resumeAwaitingSlackContinuation(
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12899
|
+
resumeAwaitingContinuation: async (conversationId, runOptions) => await resumeAwaitingSlackContinuation(
|
|
12900
|
+
conversationId,
|
|
12901
|
+
{
|
|
12902
|
+
agentRunner,
|
|
12903
|
+
scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks
|
|
12904
|
+
},
|
|
12905
|
+
runOptions
|
|
12906
|
+
),
|
|
12763
12907
|
runtime
|
|
12764
12908
|
})
|
|
12765
12909
|
};
|
|
@@ -12889,6 +13033,7 @@ async function resolveVirtualConfig() {
|
|
|
12889
13033
|
const mod = await import("#junior/config");
|
|
12890
13034
|
return {
|
|
12891
13035
|
createDashboardApp: mod.createDashboardApp,
|
|
13036
|
+
functionMaxDurationSeconds: mod.functionMaxDurationSeconds,
|
|
12892
13037
|
dashboard: mod.dashboard,
|
|
12893
13038
|
pluginSet: mod.pluginSet,
|
|
12894
13039
|
plugins: mod.plugins,
|
|
@@ -13142,6 +13287,11 @@ function mountRoutes(app, routes) {
|
|
|
13142
13287
|
}
|
|
13143
13288
|
async function createApp(options) {
|
|
13144
13289
|
const virtualConfig = await resolveVirtualConfig();
|
|
13290
|
+
if (virtualConfig?.functionMaxDurationSeconds !== void 0) {
|
|
13291
|
+
configureFunctionMaxDurationSeconds(
|
|
13292
|
+
virtualConfig.functionMaxDurationSeconds
|
|
13293
|
+
);
|
|
13294
|
+
}
|
|
13145
13295
|
const dashboard = options?.dashboard ?? virtualConfig?.dashboard;
|
|
13146
13296
|
const configuredPlugins = options?.plugins ?? virtualConfig?.pluginSet;
|
|
13147
13297
|
const plugins = pluginRuntimeRegistrationsFromPluginSet(configuredPlugins);
|