@sentry/junior 0.177.0 → 0.178.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-IYHCN7CO.js → agent-hooks-PBT4NNBV.js} +12 -12
- package/dist/api/conversations/archive.d.ts +5 -2
- package/dist/api/conversations/list.d.ts +0 -1
- package/dist/api/conversations/membership.d.ts +14 -0
- package/dist/api/conversations/projection.d.ts +2 -1
- package/dist/api/locations/detail.d.ts +1 -1
- package/dist/api/people/shared.d.ts +2 -2
- package/dist/api/schema/conversation.d.ts +4 -4
- package/dist/api/schema/location.d.ts +1 -1
- package/dist/api/schema/person.d.ts +1 -1
- package/dist/api/schema.js +1 -1
- package/dist/api.js +245 -166
- package/dist/app.js +35 -35
- package/dist/{catalog-runtime-2TD4CXSB.js → catalog-runtime-M7QGKPFZ.js} +5 -5
- package/dist/chat/agent/sandbox.d.ts +2 -0
- package/dist/chat/conversations/sql/participants.d.ts +1 -0
- package/dist/chat/conversations/store.d.ts +0 -1
- package/dist/chat/sandbox/errors.d.ts +4 -0
- package/dist/chat/sandbox/noninteractive-command.d.ts +1 -0
- package/dist/chat/sandbox/prepare-workspace.d.ts +27 -3
- package/dist/chat/sandbox/sandbox.d.ts +2 -0
- package/dist/chat/sandbox/session.d.ts +2 -0
- package/dist/chat/sandbox/snapshot/builder-sandbox.d.ts +5 -0
- package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
- package/dist/chat/sandbox/snapshot/span.d.ts +3 -0
- package/dist/chat/sandbox/snapshot/store.d.ts +20 -17
- package/dist/chat/sandbox/snapshot/waiting-error.d.ts +15 -0
- package/dist/chat/sandbox/snapshot/workspace.d.ts +17 -0
- package/dist/chat/services/workspace-snapshot-wait.d.ts +27 -0
- package/dist/chat/workspaces/types.d.ts +1 -0
- package/dist/{chunk-3FWXQZJJ.js → chunk-22T3HB44.js} +1 -1
- package/dist/{chunk-DLTP6TUJ.js → chunk-3ZZEPLLB.js} +2 -2
- package/dist/{chunk-7ZFESKJ2.js → chunk-4K6VNP3S.js} +9 -9
- package/dist/{chunk-N4YEVOEC.js → chunk-534BO42N.js} +45 -276
- package/dist/{chunk-6NZRKJWU.js → chunk-6OYVH66J.js} +1 -1
- package/dist/{chunk-J2EWL6GJ.js → chunk-ABLODWPO.js} +1 -1
- package/dist/{chunk-ORMJY4RG.js → chunk-AGJ4OQ2V.js} +1 -1
- package/dist/{chunk-Q362TECJ.js → chunk-AQHSXA3J.js} +331 -2
- package/dist/{chunk-4SVAFAI2.js → chunk-AVRWMQ4U.js} +1 -1
- package/dist/{chunk-I7PKGMKI.js → chunk-BFLMRTPD.js} +6 -6
- package/dist/{chunk-ZCYABD7N.js → chunk-BHHWCNXI.js} +5 -5
- package/dist/{chunk-LFZODSAY.js → chunk-BUGO42R6.js} +4 -4
- package/dist/{chunk-7B6NW4P7.js → chunk-D3UJTX4U.js} +1 -1
- package/dist/{chunk-2IMQLZHS.js → chunk-FQ6BMP5P.js} +767 -81
- package/dist/{chunk-4TWDRU64.js → chunk-G3CQEUHI.js} +5 -5
- package/dist/{chunk-VGDOEKO2.js → chunk-H6ZXBFGX.js} +1 -1
- package/dist/{chunk-SNPQGTHJ.js → chunk-IQRPDKPJ.js} +1 -1
- package/dist/{chunk-CAWBLVBY.js → chunk-JVXZ3WDH.js} +6 -6
- package/dist/{chunk-7PZDAXIK.js → chunk-KEQAA42D.js} +1 -1
- package/dist/{chunk-UB5YB2ED.js → chunk-LAOP4EPL.js} +6 -6
- package/dist/{chunk-WYO2HXY5.js → chunk-OGRD5PFM.js} +1 -1
- package/dist/{chunk-AFOFVEXP.js → chunk-PRDKO3QB.js} +124 -13
- package/dist/{chunk-QLLOX7JY.js → chunk-QCCLDJHB.js} +8 -8
- package/dist/{chunk-FVVYHML7.js → chunk-RE4XWXBV.js} +6 -6
- package/dist/{chunk-4TRHPYTL.js → chunk-RGV6KHDM.js} +4 -4
- package/dist/{chunk-DGGIKWYQ.js → chunk-RGXOLPWO.js} +1 -1
- package/dist/{chunk-ELEAIRTG.js → chunk-TKB25A3L.js} +2 -2
- package/dist/{chunk-6KGMMVHF.js → chunk-U3UDPGZG.js} +2 -2
- package/dist/{chunk-7CVYXUOJ.js → chunk-UGWBJAGN.js} +1 -1
- package/dist/{chunk-LVL5NG5G.js → chunk-UMG7M32G.js} +19 -19
- package/dist/{chunk-LPMLAJYR.js → chunk-XCNS3CXE.js} +11 -17
- package/dist/{chunk-PA7QDS7P.js → chunk-XWUJWHFD.js} +204 -100
- package/dist/{chunk-WCQUKX74.js → chunk-ZCHHVTMF.js} +14 -5
- package/dist/cli/chat.js +31 -30
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +13 -13
- package/dist/cli/snapshot-create.js +10 -10
- package/dist/cli/upgrade.js +7 -7
- package/dist/db/schema/conversation-participants.d.ts +17 -0
- package/dist/db/schema/conversations.d.ts +629 -1
- package/dist/db/schema/snapshots.d.ts +1 -1
- package/dist/db/schema.d.ts +18 -1
- package/dist/{db-A3CB76US.js → db-QVYOWATH.js} +7 -7
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-64QMI3IB.js → oauth-callback-server-PLPAFXO6.js} +23 -23
- package/dist/{runner-EIAE2BGD.js → runner-AGNEIOZS.js} +15 -15
- package/dist/{skills-ZADWFKDG.js → skills-U5KQ66EM.js} +6 -6
- package/dist/spawn-ZRYYZP2B.js +37 -0
- package/dist/{validation-4I6AF4CV.js → validation-QE4U45KS.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-BS6BZ66U.js → work-ATAXAHJJ.js} +18 -18
- package/dist/worker-LIL5AOER.js +24 -0
- package/migrations/0033_workspace_snapshot_active_build.sql +1 -0
- package/migrations/0034_strong_randall.sql +31 -0
- package/migrations/meta/0033_snapshot.json +2914 -0
- package/migrations/meta/0034_snapshot.json +3038 -0
- package/migrations/meta/_journal.json +15 -1
- package/package.json +5 -5
- package/dist/spawn-DVUOUFFC.js +0 -37
- package/dist/worker-RS5PWTCE.js +0 -24
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
PluginCredentialFailureError,
|
|
8
8
|
agentNameSchema,
|
|
9
9
|
createPluginAuthOrchestration
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BHHWCNXI.js";
|
|
11
11
|
import {
|
|
12
12
|
authorizationId
|
|
13
13
|
} from "./chunk-DIB4GX3G.js";
|
|
@@ -32,7 +32,10 @@ import {
|
|
|
32
32
|
searchSlackUsers,
|
|
33
33
|
selectPluginGrant,
|
|
34
34
|
toMcpProviderError
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-QCCLDJHB.js";
|
|
36
|
+
import {
|
|
37
|
+
getTurnRequestDeadline
|
|
38
|
+
} from "./chunk-6OYVH66J.js";
|
|
36
39
|
import {
|
|
37
40
|
RetryableDeliveryError,
|
|
38
41
|
actorFromRun,
|
|
@@ -48,7 +51,7 @@ import {
|
|
|
48
51
|
getSlackMessagePermalink,
|
|
49
52
|
resolveBaseUrl,
|
|
50
53
|
uploadFilesToConversation
|
|
51
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-JVXZ3WDH.js";
|
|
52
55
|
import {
|
|
53
56
|
parseSandboxEgressAuthRequiredSignal,
|
|
54
57
|
parseSandboxEgressPermissionDeniedSignal,
|
|
@@ -87,10 +90,10 @@ import {
|
|
|
87
90
|
scheduledTaskAttributes,
|
|
88
91
|
storeAttachments,
|
|
89
92
|
writableEventTask
|
|
90
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-RGV6KHDM.js";
|
|
91
94
|
import {
|
|
92
95
|
parseSlackThreadId
|
|
93
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-22T3HB44.js";
|
|
94
97
|
import {
|
|
95
98
|
AGENTS_REMOVAL_NOTICE,
|
|
96
99
|
AGENTS_REPLACEMENT_NOTICE,
|
|
@@ -160,23 +163,18 @@ import {
|
|
|
160
163
|
stripRuntimeTurnContext,
|
|
161
164
|
trimTrailingAssistantMessages,
|
|
162
165
|
truncateText
|
|
163
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-BFLMRTPD.js";
|
|
164
167
|
import {
|
|
165
168
|
bindSandboxFileSystem,
|
|
166
169
|
createSandboxSession,
|
|
170
|
+
dependencies,
|
|
167
171
|
getSandboxResources,
|
|
168
|
-
getVercelSandboxCredentials,
|
|
169
|
-
isAlreadyExistsError,
|
|
170
172
|
isMissingError,
|
|
171
|
-
|
|
172
|
-
isSandboxMissingError,
|
|
173
|
-
isSandboxUnavailableError,
|
|
174
|
-
isSnapshottingError,
|
|
173
|
+
postinstall,
|
|
175
174
|
resolve,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
} from "./chunk-N4YEVOEC.js";
|
|
175
|
+
setSnapshotSpanAttributes,
|
|
176
|
+
stopSession
|
|
177
|
+
} from "./chunk-534BO42N.js";
|
|
180
178
|
import {
|
|
181
179
|
privateTraceResultAttributes
|
|
182
180
|
} from "./chunk-UIE3R5XU.js";
|
|
@@ -196,6 +194,7 @@ import {
|
|
|
196
194
|
createResourceEventSubscription,
|
|
197
195
|
createWorkspace,
|
|
198
196
|
deleteWorkspace,
|
|
197
|
+
deleteWorkspaceSnapshotBuilders,
|
|
199
198
|
downloadPrivateSlackFile,
|
|
200
199
|
getDashboardTaskLink,
|
|
201
200
|
getFilePermalink,
|
|
@@ -207,19 +206,23 @@ import {
|
|
|
207
206
|
getSlackToolContext,
|
|
208
207
|
getWorkspace,
|
|
209
208
|
getWorkspaceByName,
|
|
209
|
+
getWorkspaceSnapshotBuilder,
|
|
210
|
+
invalidateReadySnapshot,
|
|
210
211
|
isConversationScopedChannel,
|
|
211
212
|
listResourceEventSubscriptions,
|
|
212
213
|
listWorkspaces,
|
|
214
|
+
loadSnapshotsForProfile,
|
|
213
215
|
normalizeSlackConversationId,
|
|
214
216
|
readActorIdentity,
|
|
215
|
-
recordResolvedWorkspaceSnapshot,
|
|
216
217
|
requireResourceWatchConversation,
|
|
217
218
|
resolveChannelCapabilities,
|
|
219
|
+
setWorkspaceSnapshot,
|
|
220
|
+
setWorkspaceSnapshotBuild,
|
|
218
221
|
tryWorkspaceRepoCheckoutPath,
|
|
219
222
|
updateWorkspace,
|
|
220
223
|
withSlackRetries,
|
|
221
224
|
workspaceRepoCheckoutPath
|
|
222
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-XWUJWHFD.js";
|
|
223
226
|
import {
|
|
224
227
|
KNOWN_CONVERSATION_EVENT_TYPES,
|
|
225
228
|
contextProvenance,
|
|
@@ -231,29 +234,47 @@ import {
|
|
|
231
234
|
instructionActors,
|
|
232
235
|
instructionProvenanceFor,
|
|
233
236
|
sameActorIdentity
|
|
234
|
-
} from "./chunk-
|
|
237
|
+
} from "./chunk-XCNS3CXE.js";
|
|
235
238
|
import {
|
|
236
239
|
juniorArtifacts,
|
|
237
240
|
juniorIdentities
|
|
238
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-ZCHHVTMF.js";
|
|
239
242
|
import {
|
|
240
243
|
SANDBOX_DATA_ROOT,
|
|
241
244
|
SANDBOX_REPOS_ROOT,
|
|
242
245
|
SANDBOX_SKILLS_ROOT,
|
|
243
246
|
SANDBOX_WORKSPACE_ROOT,
|
|
247
|
+
WorkspaceSnapshotWaitingError,
|
|
244
248
|
buildCommandScript,
|
|
245
249
|
buildNonInteractiveShellScript,
|
|
250
|
+
create,
|
|
246
251
|
ensureDockerDaemon,
|
|
252
|
+
getVercelSandboxCredentials,
|
|
247
253
|
hash,
|
|
254
|
+
isAbortError,
|
|
255
|
+
isAlreadyExistsError,
|
|
256
|
+
isSandboxApiTransientError,
|
|
257
|
+
isSandboxCommandStreamInterruptedError,
|
|
258
|
+
isSandboxMissingError,
|
|
259
|
+
isSandboxUnavailableError,
|
|
260
|
+
isSnapshottingError,
|
|
261
|
+
isStale,
|
|
262
|
+
isWorkspaceSnapshotWaitingError,
|
|
263
|
+
isWorkspaceSnapshotWaitingResult,
|
|
248
264
|
runNonInteractiveCommand,
|
|
249
265
|
sandboxSkillDir,
|
|
250
266
|
sandboxSkillFile,
|
|
251
267
|
sandboxSkillPathResolution,
|
|
252
|
-
sleep
|
|
253
|
-
|
|
268
|
+
sleep,
|
|
269
|
+
throwSandboxOperationError,
|
|
270
|
+
workspaceNameFromWaitingResult,
|
|
271
|
+
wrapSandboxSetupError
|
|
272
|
+
} from "./chunk-AQHSXA3J.js";
|
|
254
273
|
import {
|
|
255
|
-
|
|
256
|
-
|
|
274
|
+
fenceLock,
|
|
275
|
+
getStateAdapter,
|
|
276
|
+
withLock
|
|
277
|
+
} from "./chunk-H6ZXBFGX.js";
|
|
257
278
|
import {
|
|
258
279
|
ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
|
|
259
280
|
COMPACTION_SUMMARY_PREFIX,
|
|
@@ -286,7 +307,7 @@ import {
|
|
|
286
307
|
resolveGatewayCredential,
|
|
287
308
|
resolveGatewayModel,
|
|
288
309
|
unwrapCurrentInstruction
|
|
289
|
-
} from "./chunk-
|
|
310
|
+
} from "./chunk-3ZZEPLLB.js";
|
|
290
311
|
import {
|
|
291
312
|
canExposeConversationPayload,
|
|
292
313
|
resolveConversationPrivacy,
|
|
@@ -296,16 +317,16 @@ import {
|
|
|
296
317
|
toGenAiMessagesTraceAttributes,
|
|
297
318
|
toGenAiPayloadMetadata,
|
|
298
319
|
toGenAiPayloadTraceAttributes
|
|
299
|
-
} from "./chunk-
|
|
320
|
+
} from "./chunk-KEQAA42D.js";
|
|
300
321
|
import {
|
|
301
322
|
discoverSkills,
|
|
302
323
|
findSkillByName,
|
|
303
324
|
loadSkillsByName,
|
|
304
325
|
parseSkillInvocation
|
|
305
|
-
} from "./chunk-
|
|
326
|
+
} from "./chunk-U3UDPGZG.js";
|
|
306
327
|
import {
|
|
307
328
|
pluginCatalogRuntime
|
|
308
|
-
} from "./chunk-
|
|
329
|
+
} from "./chunk-ABLODWPO.js";
|
|
309
330
|
import {
|
|
310
331
|
CredentialUnavailableError,
|
|
311
332
|
OAuthProviderError,
|
|
@@ -313,7 +334,7 @@ import {
|
|
|
313
334
|
getOAuthProviderErrorAttributes,
|
|
314
335
|
resolveAuthTokenPlaceholder,
|
|
315
336
|
resolvePluginCommandEnv
|
|
316
|
-
} from "./chunk-
|
|
337
|
+
} from "./chunk-D3UJTX4U.js";
|
|
317
338
|
import {
|
|
318
339
|
credentialUserSubjectId
|
|
319
340
|
} from "./chunk-CGA4ERJ5.js";
|
|
@@ -343,7 +364,7 @@ import {
|
|
|
343
364
|
summarizeMessageText,
|
|
344
365
|
withLogContext,
|
|
345
366
|
withSpan
|
|
346
|
-
} from "./chunk-
|
|
367
|
+
} from "./chunk-UGWBJAGN.js";
|
|
347
368
|
import {
|
|
348
369
|
startInactiveSpan,
|
|
349
370
|
withActiveSpan
|
|
@@ -987,6 +1008,97 @@ function nextEmptyOutputContinuation(args) {
|
|
|
987
1008
|
return isContinuablePiBoundary(messages) ? { kind: "retry", messages } : { kind: "exhausted" };
|
|
988
1009
|
}
|
|
989
1010
|
|
|
1011
|
+
// src/chat/services/workspace-snapshot-wait.ts
|
|
1012
|
+
import { randomUUID } from "crypto";
|
|
1013
|
+
var SWITCH_WORKSPACE_TOOL = "switchWorkspace";
|
|
1014
|
+
function pendingWorkspaceSnapshotWait(messages) {
|
|
1015
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
1016
|
+
const message = messages[index];
|
|
1017
|
+
if (!isToolResultMessage(message)) break;
|
|
1018
|
+
if (message.toolName !== SWITCH_WORKSPACE_TOOL) continue;
|
|
1019
|
+
const name = workspaceNameFromWaitingResult(message.details);
|
|
1020
|
+
return name ? { name } : null;
|
|
1021
|
+
}
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
async function continueWorkspaceSnapshotWait(args) {
|
|
1025
|
+
if (!isContinuablePiBoundary(args.messages)) return { kind: "none" };
|
|
1026
|
+
const pending = pendingWorkspaceSnapshotWait(args.messages);
|
|
1027
|
+
if (!pending) return { kind: "none" };
|
|
1028
|
+
const tool = args.tools.find((entry) => entry.name === SWITCH_WORKSPACE_TOOL);
|
|
1029
|
+
if (!tool) return { kind: "none" };
|
|
1030
|
+
const toolCallId = randomUUID();
|
|
1031
|
+
const assistantMessage = {
|
|
1032
|
+
role: "assistant",
|
|
1033
|
+
api: "openai-responses",
|
|
1034
|
+
provider: "openai",
|
|
1035
|
+
model: "host-continue",
|
|
1036
|
+
content: [
|
|
1037
|
+
{
|
|
1038
|
+
type: "toolCall",
|
|
1039
|
+
id: toolCallId,
|
|
1040
|
+
name: SWITCH_WORKSPACE_TOOL,
|
|
1041
|
+
arguments: { name: pending.name }
|
|
1042
|
+
}
|
|
1043
|
+
],
|
|
1044
|
+
usage: {
|
|
1045
|
+
input: 0,
|
|
1046
|
+
output: 0,
|
|
1047
|
+
cacheRead: 0,
|
|
1048
|
+
cacheWrite: 0,
|
|
1049
|
+
totalTokens: 0,
|
|
1050
|
+
cost: {
|
|
1051
|
+
input: 0,
|
|
1052
|
+
output: 0,
|
|
1053
|
+
cacheRead: 0,
|
|
1054
|
+
cacheWrite: 0,
|
|
1055
|
+
total: 0
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
timestamp: Date.now(),
|
|
1059
|
+
stopReason: "toolUse"
|
|
1060
|
+
};
|
|
1061
|
+
let result;
|
|
1062
|
+
try {
|
|
1063
|
+
result = await tool.execute(
|
|
1064
|
+
toolCallId,
|
|
1065
|
+
{ name: pending.name },
|
|
1066
|
+
args.signal
|
|
1067
|
+
);
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1070
|
+
const toolResultMessage2 = {
|
|
1071
|
+
role: "toolResult",
|
|
1072
|
+
toolCallId,
|
|
1073
|
+
toolName: SWITCH_WORKSPACE_TOOL,
|
|
1074
|
+
content: [{ type: "text", text: message }],
|
|
1075
|
+
details: { error: message },
|
|
1076
|
+
isError: true,
|
|
1077
|
+
timestamp: Date.now()
|
|
1078
|
+
};
|
|
1079
|
+
return {
|
|
1080
|
+
kind: "failed",
|
|
1081
|
+
error,
|
|
1082
|
+
messages: [...args.messages, assistantMessage, toolResultMessage2]
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
const toolResultMessage = {
|
|
1086
|
+
role: "toolResult",
|
|
1087
|
+
toolCallId,
|
|
1088
|
+
toolName: SWITCH_WORKSPACE_TOOL,
|
|
1089
|
+
content: result.content,
|
|
1090
|
+
details: result.details,
|
|
1091
|
+
isError: false,
|
|
1092
|
+
timestamp: Date.now()
|
|
1093
|
+
};
|
|
1094
|
+
const messages = [
|
|
1095
|
+
...args.messages,
|
|
1096
|
+
assistantMessage,
|
|
1097
|
+
toolResultMessage
|
|
1098
|
+
];
|
|
1099
|
+
return isWorkspaceSnapshotWaitingResult(result.details) ? { kind: "waiting", messages } : { kind: "ready", messages };
|
|
1100
|
+
}
|
|
1101
|
+
|
|
990
1102
|
// src/chat/agent/retry-usage.ts
|
|
991
1103
|
function getDiscardedRetryUsage(messages, retryMessages) {
|
|
992
1104
|
const keepsExactPrefix = retryMessages.length <= messages.length && retryMessages.every((message, index) => message === messages[index]);
|
|
@@ -2671,7 +2783,7 @@ function formatSandboxCommandResult(params) {
|
|
|
2671
2783
|
import fs4 from "fs/promises";
|
|
2672
2784
|
|
|
2673
2785
|
// src/chat/sandbox/egress/session.ts
|
|
2674
|
-
import { createHmac, randomUUID, timingSafeEqual } from "crypto";
|
|
2786
|
+
import { createHmac, randomUUID as randomUUID2, timingSafeEqual } from "crypto";
|
|
2675
2787
|
var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
|
|
2676
2788
|
var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
|
|
2677
2789
|
var SANDBOX_EGRESS_HMAC_CONTEXT = "junior.sandbox_egress.v1";
|
|
@@ -2743,7 +2855,7 @@ function createSandboxEgressCredentialToken(input) {
|
|
|
2743
2855
|
credentials: input.credentials,
|
|
2744
2856
|
egressId: input.egressId,
|
|
2745
2857
|
expiresAtMs: now + ttlMs2,
|
|
2746
|
-
contextId:
|
|
2858
|
+
contextId: randomUUID2()
|
|
2747
2859
|
};
|
|
2748
2860
|
const payload = `${SANDBOX_EGRESS_TOKEN_VERSION}.${base64Url(
|
|
2749
2861
|
JSON.stringify(context)
|
|
@@ -2960,11 +3072,11 @@ async function resolveSandboxCommandEnvironment() {
|
|
|
2960
3072
|
}
|
|
2961
3073
|
|
|
2962
3074
|
// src/chat/sandbox/session.ts
|
|
2963
|
-
import { randomUUID as
|
|
2964
|
-
import { Sandbox } from "@vercel/sandbox";
|
|
3075
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3076
|
+
import { Sandbox as Sandbox2 } from "@vercel/sandbox";
|
|
2965
3077
|
|
|
2966
3078
|
// src/chat/sandbox/prepare-workspace.ts
|
|
2967
|
-
async function
|
|
3079
|
+
async function prepareWorkspaceRepositories(params) {
|
|
2968
3080
|
const { sandbox, workspace, signal } = params;
|
|
2969
3081
|
signal?.throwIfAborted();
|
|
2970
3082
|
await params.applyNetworkPolicy(sandbox);
|
|
@@ -2972,15 +3084,54 @@ async function prepareWorkspaceSnapshot(params) {
|
|
|
2972
3084
|
if (params.removeCredentialRoute) {
|
|
2973
3085
|
await sandbox.update({ networkPolicy: "allow-all" });
|
|
2974
3086
|
}
|
|
2975
|
-
|
|
2976
|
-
|
|
3087
|
+
}
|
|
3088
|
+
function workspaceSetupCommand(workspace) {
|
|
3089
|
+
return {
|
|
2977
3090
|
cmd: "bash",
|
|
2978
3091
|
args: ["-euo", "pipefail", "-c", workspace.setupScript],
|
|
2979
3092
|
cwd: SANDBOX_WORKSPACE_ROOT,
|
|
2980
3093
|
env: {
|
|
2981
3094
|
JUNIOR_REPOS_ROOT: SANDBOX_REPOS_ROOT,
|
|
2982
3095
|
JUNIOR_WORKSPACE_ROOT: SANDBOX_WORKSPACE_ROOT
|
|
2983
|
-
}
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
function workspaceSnapshotSetupCommand(workspace, buildId) {
|
|
3100
|
+
const stateRoot = `/tmp/junior-snapshot-build-${buildId}`;
|
|
3101
|
+
const wrapper = [
|
|
3102
|
+
`mkdir -p "${stateRoot}"`,
|
|
3103
|
+
`exec 9>"${stateRoot}/lock"`,
|
|
3104
|
+
"flock 9",
|
|
3105
|
+
`if test -f "${stateRoot}/exit"; then exit "$(cat "${stateRoot}/exit")"; fi`,
|
|
3106
|
+
"set +e",
|
|
3107
|
+
'bash -euo pipefail -c "$1"',
|
|
3108
|
+
"status=$?",
|
|
3109
|
+
`printf '%s\\n' "$status" > "${stateRoot}/exit"`,
|
|
3110
|
+
'exit "$status"'
|
|
3111
|
+
].join("\n");
|
|
3112
|
+
return {
|
|
3113
|
+
cmd: "bash",
|
|
3114
|
+
args: [
|
|
3115
|
+
"-euo",
|
|
3116
|
+
"pipefail",
|
|
3117
|
+
"-c",
|
|
3118
|
+
wrapper,
|
|
3119
|
+
"junior-setup",
|
|
3120
|
+
workspace.setupScript
|
|
3121
|
+
],
|
|
3122
|
+
cwd: SANDBOX_WORKSPACE_ROOT,
|
|
3123
|
+
env: {
|
|
3124
|
+
JUNIOR_REPOS_ROOT: SANDBOX_REPOS_ROOT,
|
|
3125
|
+
JUNIOR_WORKSPACE_ROOT: SANDBOX_WORKSPACE_ROOT
|
|
3126
|
+
}
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
async function prepareWorkspaceSnapshot(params) {
|
|
3130
|
+
const { sandbox, workspace, signal } = params;
|
|
3131
|
+
await prepareWorkspaceRepositories(params);
|
|
3132
|
+
if (!workspace.setupScript.trim()) return;
|
|
3133
|
+
const result = await sandbox.runCommand({
|
|
3134
|
+
...workspaceSetupCommand(workspace),
|
|
2984
3135
|
signal
|
|
2985
3136
|
});
|
|
2986
3137
|
if (result.exitCode !== 0) {
|
|
@@ -2990,6 +3141,486 @@ async function prepareWorkspaceSnapshot(params) {
|
|
|
2990
3141
|
}
|
|
2991
3142
|
}
|
|
2992
3143
|
|
|
3144
|
+
// src/chat/sandbox/snapshot/workspace.ts
|
|
3145
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3146
|
+
import { Sandbox } from "@vercel/sandbox";
|
|
3147
|
+
var BUILD_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
3148
|
+
var BUILD_TIMEOUT_ERROR = "Workspace snapshot build timed out after 1 hour";
|
|
3149
|
+
var BUILD_LOCK_TTL_MS = 2 * 60 * 1e3;
|
|
3150
|
+
var BUILD_LOCK_PREFIX = "junior:workspace_snapshot_start";
|
|
3151
|
+
var WAIT_POLL_MS = 5e3;
|
|
3152
|
+
var TRANSIENT_API_RETRY_MS = 2e3;
|
|
3153
|
+
var BUILD_COMMAND_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
3154
|
+
var BUILD_COMMAND_TIMEOUT_ERROR = "Workspace snapshot build phase timed out after 10 minutes";
|
|
3155
|
+
var COMMAND_STATUS_POLL_TIMEOUT_MS = 1e3;
|
|
3156
|
+
var WAIT_YIELD_BUFFER_MS = 4e4;
|
|
3157
|
+
function isCancelOrTransient(error) {
|
|
3158
|
+
return isAbortError(error) || isSandboxApiTransientError(error);
|
|
3159
|
+
}
|
|
3160
|
+
function buildLockKey(profileHash) {
|
|
3161
|
+
return `${BUILD_LOCK_PREFIX}:${profileHash}`;
|
|
3162
|
+
}
|
|
3163
|
+
function builderName() {
|
|
3164
|
+
return `junior-ws-${randomUUID3()}`;
|
|
3165
|
+
}
|
|
3166
|
+
async function snapshotFromSql(workspaceId, value, staleSnapshotId) {
|
|
3167
|
+
const { ready } = await loadSnapshotsForProfile(
|
|
3168
|
+
getDb(),
|
|
3169
|
+
workspaceId,
|
|
3170
|
+
value.hash
|
|
3171
|
+
);
|
|
3172
|
+
if (!ready) return null;
|
|
3173
|
+
if (ready.id === staleSnapshotId || isStale(value, ready.generatedAt.getTime())) {
|
|
3174
|
+
const ownerName = await invalidateReadySnapshot({
|
|
3175
|
+
workspaceId,
|
|
3176
|
+
profileHash: value.hash,
|
|
3177
|
+
snapshotId: ready.id
|
|
3178
|
+
});
|
|
3179
|
+
try {
|
|
3180
|
+
await deleteBuilder(ownerName);
|
|
3181
|
+
} catch (error) {
|
|
3182
|
+
logException(error, "sandbox.workspace_snapshot.builder.delete_failed", {
|
|
3183
|
+
"app.workspace.id": workspaceId
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
return null;
|
|
3187
|
+
}
|
|
3188
|
+
return {
|
|
3189
|
+
snapshotId: ready.id,
|
|
3190
|
+
profileHash: ready.profileHash,
|
|
3191
|
+
dependencyCount: value.dependencyCount,
|
|
3192
|
+
cacheHit: true,
|
|
3193
|
+
resolveOutcome: "cache_hit",
|
|
3194
|
+
createdAtMs: ready.generatedAt.getTime(),
|
|
3195
|
+
buildDurationMs: ready.buildDurationMs
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
function activeBuild(build) {
|
|
3199
|
+
if (!build || build.status !== "building" || !build.sandboxName) return null;
|
|
3200
|
+
return build;
|
|
3201
|
+
}
|
|
3202
|
+
async function deleteBuilder(sandboxName) {
|
|
3203
|
+
if (!sandboxName) return;
|
|
3204
|
+
await deleteWorkspaceSnapshotBuilders([sandboxName]);
|
|
3205
|
+
}
|
|
3206
|
+
function workspaceDeletedError(workspace) {
|
|
3207
|
+
return new Error(
|
|
3208
|
+
`Workspace ${workspace.name} was deleted while its snapshot was building`
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
function requireBuildWrite(written, workspace) {
|
|
3212
|
+
if (!written) {
|
|
3213
|
+
throw new Error(
|
|
3214
|
+
`Workspace ${workspace.name} snapshot build lost SQL ownership`
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
async function markFailed(workspace, build, error, beforeWrite) {
|
|
3219
|
+
try {
|
|
3220
|
+
await beforeWrite();
|
|
3221
|
+
const written = await setWorkspaceSnapshotBuild(workspace.id, {
|
|
3222
|
+
...build,
|
|
3223
|
+
status: "failed",
|
|
3224
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3225
|
+
});
|
|
3226
|
+
requireBuildWrite(written, workspace);
|
|
3227
|
+
} catch (persistError) {
|
|
3228
|
+
logException(
|
|
3229
|
+
persistError,
|
|
3230
|
+
"sandbox.workspace_snapshot.failure_persist_failed",
|
|
3231
|
+
{ "app.workspace.id": workspace.id }
|
|
3232
|
+
);
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
try {
|
|
3236
|
+
await deleteBuilder(build.sandboxName);
|
|
3237
|
+
} catch (cleanupError) {
|
|
3238
|
+
logException(
|
|
3239
|
+
cleanupError,
|
|
3240
|
+
"sandbox.workspace_snapshot.builder.delete_failed",
|
|
3241
|
+
{ "app.workspace.id": workspace.id }
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
async function requireRemainingBuildTime(workspace, build, beforeWrite) {
|
|
3246
|
+
const remainingMs = build.startedAt.getTime() + BUILD_TIMEOUT_MS - Date.now();
|
|
3247
|
+
if (remainingMs > 0) return remainingMs;
|
|
3248
|
+
await markFailed(workspace, build, BUILD_TIMEOUT_ERROR, beforeWrite);
|
|
3249
|
+
throw new Error(BUILD_TIMEOUT_ERROR);
|
|
3250
|
+
}
|
|
3251
|
+
async function finishBuild(workspace, value, signal, beforeWrite) {
|
|
3252
|
+
const { build: loaded } = await loadSnapshotsForProfile(
|
|
3253
|
+
getDb(),
|
|
3254
|
+
workspace.id,
|
|
3255
|
+
value.hash
|
|
3256
|
+
);
|
|
3257
|
+
const build = activeBuild(loaded);
|
|
3258
|
+
if (!build?.commandId || !build.sandboxName) return null;
|
|
3259
|
+
try {
|
|
3260
|
+
await requireRemainingBuildTime(workspace, build, beforeWrite);
|
|
3261
|
+
const sandbox = await getWorkspaceSnapshotBuilder(
|
|
3262
|
+
build.sandboxName,
|
|
3263
|
+
signal
|
|
3264
|
+
);
|
|
3265
|
+
const command = await sandbox.getCommand(build.commandId, { signal });
|
|
3266
|
+
const pollSignal = AbortSignal.timeout(COMMAND_STATUS_POLL_TIMEOUT_MS);
|
|
3267
|
+
const waitSignal = signal ? AbortSignal.any([signal, pollSignal]) : pollSignal;
|
|
3268
|
+
let finished;
|
|
3269
|
+
try {
|
|
3270
|
+
finished = await command.wait({ signal: waitSignal });
|
|
3271
|
+
} catch (error) {
|
|
3272
|
+
if (pollSignal.aborted && !signal?.aborted) return null;
|
|
3273
|
+
throw error;
|
|
3274
|
+
}
|
|
3275
|
+
if (finished.exitCode !== 0) {
|
|
3276
|
+
const error = (await finished.stderr({ signal })).trim() || `exit ${finished.exitCode}`;
|
|
3277
|
+
throw new Error(
|
|
3278
|
+
`Workspace ${workspace.name} snapshot setup failed: ${error}`
|
|
3279
|
+
);
|
|
3280
|
+
}
|
|
3281
|
+
await requireRemainingBuildTime(workspace, build, beforeWrite);
|
|
3282
|
+
const snapshot = await sandbox.snapshot({ signal });
|
|
3283
|
+
await requireRemainingBuildTime(workspace, build, beforeWrite);
|
|
3284
|
+
const createdAtMs = Date.now();
|
|
3285
|
+
const buildDurationMs = Math.max(
|
|
3286
|
+
0,
|
|
3287
|
+
createdAtMs - build.startedAt.getTime()
|
|
3288
|
+
);
|
|
3289
|
+
await beforeWrite();
|
|
3290
|
+
const result = await setWorkspaceSnapshot(
|
|
3291
|
+
workspace.id,
|
|
3292
|
+
{
|
|
3293
|
+
id: snapshot.snapshotId,
|
|
3294
|
+
generatedAt: new Date(createdAtMs),
|
|
3295
|
+
buildDurationMs,
|
|
3296
|
+
profileHash: value.hash
|
|
3297
|
+
},
|
|
3298
|
+
{ buildId: build.id }
|
|
3299
|
+
);
|
|
3300
|
+
requireBuildWrite(result.written, workspace);
|
|
3301
|
+
try {
|
|
3302
|
+
await deleteWorkspaceSnapshotBuilders(result.replacedBuilderNames);
|
|
3303
|
+
} catch (error) {
|
|
3304
|
+
logException(error, "sandbox.workspace_snapshot.builder.delete_failed", {
|
|
3305
|
+
"app.workspace.id": workspace.id,
|
|
3306
|
+
"app.sandbox.snapshot.builder_count": result.replacedBuilderNames.length
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
return {
|
|
3310
|
+
snapshotId: snapshot.snapshotId,
|
|
3311
|
+
profileHash: value.hash,
|
|
3312
|
+
dependencyCount: value.dependencyCount,
|
|
3313
|
+
cacheHit: false,
|
|
3314
|
+
resolveOutcome: "rebuilt",
|
|
3315
|
+
rebuildReason: "cache_miss",
|
|
3316
|
+
createdAtMs,
|
|
3317
|
+
buildDurationMs
|
|
3318
|
+
};
|
|
3319
|
+
} catch (error) {
|
|
3320
|
+
if (error instanceof Error && error.message === BUILD_TIMEOUT_ERROR) {
|
|
3321
|
+
throw error;
|
|
3322
|
+
}
|
|
3323
|
+
if (isCancelOrTransient(error)) throw error;
|
|
3324
|
+
await markFailed(workspace, build, error, beforeWrite);
|
|
3325
|
+
throw error;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
async function startBuild(params) {
|
|
3329
|
+
const { workspace, value, signal } = params;
|
|
3330
|
+
const credentials = getVercelSandboxCredentials();
|
|
3331
|
+
const resources = getSandboxResources();
|
|
3332
|
+
const name = builderName();
|
|
3333
|
+
const buildId = randomUUID3();
|
|
3334
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
3335
|
+
try {
|
|
3336
|
+
await Sandbox.create({
|
|
3337
|
+
name,
|
|
3338
|
+
persistent: true,
|
|
3339
|
+
timeout: BUILD_TIMEOUT_MS,
|
|
3340
|
+
runtime: params.runtime,
|
|
3341
|
+
signal,
|
|
3342
|
+
...credentials ?? {},
|
|
3343
|
+
...resources ? { resources } : {}
|
|
3344
|
+
});
|
|
3345
|
+
await params.beforeWrite();
|
|
3346
|
+
const written = await setWorkspaceSnapshotBuild(
|
|
3347
|
+
workspace.id,
|
|
3348
|
+
{
|
|
3349
|
+
id: buildId,
|
|
3350
|
+
status: "building",
|
|
3351
|
+
phase: "created",
|
|
3352
|
+
profileHash: value.hash,
|
|
3353
|
+
startedAt,
|
|
3354
|
+
sandboxName: name,
|
|
3355
|
+
commandId: null,
|
|
3356
|
+
error: null
|
|
3357
|
+
},
|
|
3358
|
+
{ insertIfMissing: true, expectedRecipe: workspace }
|
|
3359
|
+
);
|
|
3360
|
+
if (!written) {
|
|
3361
|
+
try {
|
|
3362
|
+
await deleteBuilder(name);
|
|
3363
|
+
} catch (cleanupError) {
|
|
3364
|
+
logException(
|
|
3365
|
+
cleanupError,
|
|
3366
|
+
"sandbox.workspace_snapshot.builder.delete_failed",
|
|
3367
|
+
{ "app.workspace.id": workspace.id }
|
|
3368
|
+
);
|
|
3369
|
+
}
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
} catch (error) {
|
|
3373
|
+
try {
|
|
3374
|
+
if (!isCancelOrTransient(error)) {
|
|
3375
|
+
try {
|
|
3376
|
+
await params.beforeWrite();
|
|
3377
|
+
await setWorkspaceSnapshotBuild(
|
|
3378
|
+
workspace.id,
|
|
3379
|
+
{
|
|
3380
|
+
id: buildId,
|
|
3381
|
+
status: "failed",
|
|
3382
|
+
phase: "created",
|
|
3383
|
+
profileHash: value.hash,
|
|
3384
|
+
startedAt,
|
|
3385
|
+
sandboxName: name,
|
|
3386
|
+
commandId: null,
|
|
3387
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3388
|
+
},
|
|
3389
|
+
{ insertIfMissing: true, expectedRecipe: workspace }
|
|
3390
|
+
);
|
|
3391
|
+
} catch {
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
} finally {
|
|
3395
|
+
try {
|
|
3396
|
+
await deleteBuilder(name);
|
|
3397
|
+
} catch (cleanupError) {
|
|
3398
|
+
logException(
|
|
3399
|
+
cleanupError,
|
|
3400
|
+
"sandbox.workspace_snapshot.builder.delete_failed",
|
|
3401
|
+
{ "app.workspace.id": workspace.id }
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
throw error;
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
async function continueBuild(params) {
|
|
3409
|
+
const { build: loaded } = await loadSnapshotsForProfile(
|
|
3410
|
+
getDb(),
|
|
3411
|
+
params.workspace.id,
|
|
3412
|
+
params.value.hash
|
|
3413
|
+
);
|
|
3414
|
+
const build = activeBuild(loaded);
|
|
3415
|
+
if (!build?.sandboxName || build.commandId) return;
|
|
3416
|
+
const remainingMs = await requireRemainingBuildTime(
|
|
3417
|
+
params.workspace,
|
|
3418
|
+
build,
|
|
3419
|
+
params.beforeWrite
|
|
3420
|
+
);
|
|
3421
|
+
const commandTimeoutMs = Math.min(remainingMs, BUILD_COMMAND_TIMEOUT_MS);
|
|
3422
|
+
const phaseTimeoutSignal = AbortSignal.timeout(commandTimeoutMs);
|
|
3423
|
+
const phaseSignal = params.signal ? AbortSignal.any([params.signal, phaseTimeoutSignal]) : phaseTimeoutSignal;
|
|
3424
|
+
try {
|
|
3425
|
+
const sandbox = await getWorkspaceSnapshotBuilder(
|
|
3426
|
+
build.sandboxName,
|
|
3427
|
+
phaseSignal
|
|
3428
|
+
);
|
|
3429
|
+
const session = createSandboxSession(sandbox);
|
|
3430
|
+
if (build.phase === "created") {
|
|
3431
|
+
await dependencies(
|
|
3432
|
+
session,
|
|
3433
|
+
params.value.dependencies,
|
|
3434
|
+
phaseSignal,
|
|
3435
|
+
commandTimeoutMs
|
|
3436
|
+
);
|
|
3437
|
+
await postinstall(
|
|
3438
|
+
session,
|
|
3439
|
+
params.value.postinstall,
|
|
3440
|
+
phaseSignal,
|
|
3441
|
+
commandTimeoutMs
|
|
3442
|
+
);
|
|
3443
|
+
await requireRemainingBuildTime(
|
|
3444
|
+
params.workspace,
|
|
3445
|
+
build,
|
|
3446
|
+
params.beforeWrite
|
|
3447
|
+
);
|
|
3448
|
+
await params.beforeWrite();
|
|
3449
|
+
const written2 = await setWorkspaceSnapshotBuild(params.workspace.id, {
|
|
3450
|
+
...build,
|
|
3451
|
+
phase: "dependencies_installed"
|
|
3452
|
+
});
|
|
3453
|
+
requireBuildWrite(written2, params.workspace);
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
if (build.phase === "dependencies_installed") {
|
|
3457
|
+
await prepareWorkspaceRepositories({
|
|
3458
|
+
sandbox: session,
|
|
3459
|
+
workspace: params.workspace,
|
|
3460
|
+
signal: phaseSignal,
|
|
3461
|
+
applyNetworkPolicy: params.applyNetworkPolicy,
|
|
3462
|
+
prepareRepositories: params.prepareRepositories,
|
|
3463
|
+
removeCredentialRoute: params.removeCredentialRoute
|
|
3464
|
+
});
|
|
3465
|
+
await requireRemainingBuildTime(
|
|
3466
|
+
params.workspace,
|
|
3467
|
+
build,
|
|
3468
|
+
params.beforeWrite
|
|
3469
|
+
);
|
|
3470
|
+
await params.beforeWrite();
|
|
3471
|
+
const written2 = await setWorkspaceSnapshotBuild(params.workspace.id, {
|
|
3472
|
+
...build,
|
|
3473
|
+
phase: "repositories_prepared"
|
|
3474
|
+
});
|
|
3475
|
+
requireBuildWrite(written2, params.workspace);
|
|
3476
|
+
return;
|
|
3477
|
+
}
|
|
3478
|
+
await params.beforeWrite();
|
|
3479
|
+
const command = await sandbox.runCommand({
|
|
3480
|
+
...workspaceSnapshotSetupCommand(params.workspace, build.id),
|
|
3481
|
+
detached: true,
|
|
3482
|
+
timeoutMs: remainingMs,
|
|
3483
|
+
signal: phaseSignal
|
|
3484
|
+
});
|
|
3485
|
+
await params.beforeWrite();
|
|
3486
|
+
const written = await setWorkspaceSnapshotBuild(params.workspace.id, {
|
|
3487
|
+
...build,
|
|
3488
|
+
commandId: command.cmdId,
|
|
3489
|
+
error: null
|
|
3490
|
+
});
|
|
3491
|
+
requireBuildWrite(written, params.workspace);
|
|
3492
|
+
} catch (error) {
|
|
3493
|
+
if (phaseTimeoutSignal.aborted && !params.signal?.aborted) {
|
|
3494
|
+
const timeoutError = new Error(BUILD_COMMAND_TIMEOUT_ERROR, {
|
|
3495
|
+
cause: error
|
|
3496
|
+
});
|
|
3497
|
+
await markFailed(
|
|
3498
|
+
params.workspace,
|
|
3499
|
+
build,
|
|
3500
|
+
timeoutError,
|
|
3501
|
+
params.beforeWrite
|
|
3502
|
+
);
|
|
3503
|
+
throw timeoutError;
|
|
3504
|
+
}
|
|
3505
|
+
if (error instanceof Error && error.message === BUILD_TIMEOUT_ERROR) {
|
|
3506
|
+
throw error;
|
|
3507
|
+
}
|
|
3508
|
+
if (isCancelOrTransient(error)) throw error;
|
|
3509
|
+
await markFailed(params.workspace, build, error, params.beforeWrite);
|
|
3510
|
+
throw error;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
async function advanceWorkspaceSnapshot(params) {
|
|
3514
|
+
const value = create(params.runtime, params.workspace);
|
|
3515
|
+
if (!value) {
|
|
3516
|
+
throw new Error(
|
|
3517
|
+
`Workspace ${params.workspace.name} has no snapshot profile`
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
const sqlCached = await snapshotFromSql(
|
|
3521
|
+
params.workspace.id,
|
|
3522
|
+
value,
|
|
3523
|
+
params.staleSnapshotId
|
|
3524
|
+
);
|
|
3525
|
+
if (sqlCached) return sqlCached;
|
|
3526
|
+
const state = getStateAdapter();
|
|
3527
|
+
await state.connect();
|
|
3528
|
+
const locked = await withLock(
|
|
3529
|
+
state,
|
|
3530
|
+
buildLockKey(value.hash),
|
|
3531
|
+
async (lock) => {
|
|
3532
|
+
const workspace = await getWorkspace(getDb(), params.workspace.id);
|
|
3533
|
+
if (!workspace) throw workspaceDeletedError(params.workspace);
|
|
3534
|
+
const lockedValue = create(params.runtime, workspace) ?? value;
|
|
3535
|
+
if (lockedValue.hash !== value.hash) return null;
|
|
3536
|
+
const beforeWrite = async () => {
|
|
3537
|
+
await fenceLock(state, lock, BUILD_LOCK_TTL_MS);
|
|
3538
|
+
};
|
|
3539
|
+
const sqlInsideLock = await snapshotFromSql(
|
|
3540
|
+
workspace.id,
|
|
3541
|
+
lockedValue,
|
|
3542
|
+
params.staleSnapshotId
|
|
3543
|
+
);
|
|
3544
|
+
if (sqlInsideLock) return sqlInsideLock;
|
|
3545
|
+
const finished = await finishBuild(
|
|
3546
|
+
workspace,
|
|
3547
|
+
lockedValue,
|
|
3548
|
+
params.signal,
|
|
3549
|
+
beforeWrite
|
|
3550
|
+
);
|
|
3551
|
+
if (finished) return finished;
|
|
3552
|
+
const { build: loaded } = await loadSnapshotsForProfile(
|
|
3553
|
+
getDb(),
|
|
3554
|
+
workspace.id,
|
|
3555
|
+
lockedValue.hash
|
|
3556
|
+
);
|
|
3557
|
+
const build = activeBuild(loaded);
|
|
3558
|
+
if (build && !build.commandId) {
|
|
3559
|
+
await continueBuild({
|
|
3560
|
+
...params,
|
|
3561
|
+
workspace,
|
|
3562
|
+
value: lockedValue,
|
|
3563
|
+
beforeWrite
|
|
3564
|
+
});
|
|
3565
|
+
} else if (!build) {
|
|
3566
|
+
if (loaded?.sandboxName) {
|
|
3567
|
+
try {
|
|
3568
|
+
await deleteBuilder(loaded.sandboxName);
|
|
3569
|
+
} catch (cleanupError) {
|
|
3570
|
+
logException(
|
|
3571
|
+
cleanupError,
|
|
3572
|
+
"sandbox.workspace_snapshot.builder.delete_failed",
|
|
3573
|
+
{ "app.workspace.id": workspace.id }
|
|
3574
|
+
);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
await startBuild({
|
|
3578
|
+
workspace,
|
|
3579
|
+
value: lockedValue,
|
|
3580
|
+
runtime: params.runtime,
|
|
3581
|
+
signal: params.signal,
|
|
3582
|
+
beforeWrite
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
return null;
|
|
3586
|
+
},
|
|
3587
|
+
{ ttlMs: BUILD_LOCK_TTL_MS, keepAlive: true }
|
|
3588
|
+
);
|
|
3589
|
+
return locked.acquired ? locked.value ?? null : null;
|
|
3590
|
+
}
|
|
3591
|
+
function shouldYieldWait(shouldYield) {
|
|
3592
|
+
if (shouldYield?.()) return true;
|
|
3593
|
+
const deadlineAtMs = getTurnRequestDeadline()?.deadlineAtMs;
|
|
3594
|
+
return deadlineAtMs !== void 0 && Date.now() >= deadlineAtMs - WAIT_YIELD_BUFFER_MS;
|
|
3595
|
+
}
|
|
3596
|
+
async function resolveWorkspaceSnapshot(params) {
|
|
3597
|
+
for (; ; ) {
|
|
3598
|
+
params.signal?.throwIfAborted();
|
|
3599
|
+
const workspace = await getWorkspace(getDb(), params.workspace.id);
|
|
3600
|
+
if (!workspace) throw workspaceDeletedError(params.workspace);
|
|
3601
|
+
const slice = { ...params, workspace };
|
|
3602
|
+
if (shouldYieldWait(slice.shouldYield)) {
|
|
3603
|
+
throw new WorkspaceSnapshotWaitingError(workspace.name);
|
|
3604
|
+
}
|
|
3605
|
+
let snapshot;
|
|
3606
|
+
try {
|
|
3607
|
+
snapshot = await advanceWorkspaceSnapshot(slice);
|
|
3608
|
+
} catch (error) {
|
|
3609
|
+
if (!isSandboxApiTransientError(error)) throw error;
|
|
3610
|
+
if (shouldYieldWait(slice.shouldYield)) {
|
|
3611
|
+
throw new WorkspaceSnapshotWaitingError(workspace.name);
|
|
3612
|
+
}
|
|
3613
|
+
await sleep(TRANSIENT_API_RETRY_MS, params.signal);
|
|
3614
|
+
continue;
|
|
3615
|
+
}
|
|
3616
|
+
if (shouldYieldWait(slice.shouldYield)) {
|
|
3617
|
+
throw new WorkspaceSnapshotWaitingError(workspace.name);
|
|
3618
|
+
}
|
|
3619
|
+
if (snapshot) return snapshot;
|
|
3620
|
+
await sleep(WAIT_POLL_MS, params.signal);
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
|
|
2993
3624
|
// src/chat/sandbox/skill-sync.ts
|
|
2994
3625
|
import fs3 from "fs/promises";
|
|
2995
3626
|
import path3 from "path";
|
|
@@ -3247,7 +3878,7 @@ function createSandboxRuntime(options) {
|
|
|
3247
3878
|
const adaptSandbox = (vercelSandbox) => createSandboxSession(vercelSandbox, {
|
|
3248
3879
|
onUnavailable: invalidateSession
|
|
3249
3880
|
});
|
|
3250
|
-
const createSandboxName = () => `${SANDBOX_NAME_PREFIX}${
|
|
3881
|
+
const createSandboxName = () => `${SANDBOX_NAME_PREFIX}${randomUUID4()}`;
|
|
3251
3882
|
const preflightNetworkPolicy = (sandboxName) => options.createNetworkPolicy?.(sandboxName);
|
|
3252
3883
|
const persistSandboxRef = async (nextRef) => {
|
|
3253
3884
|
if (reportedSandboxRef?.id === nextRef.id && reportedSandboxRef.profileHash === nextRef.profileHash && reportedSandboxRef.workspaceId === nextRef.workspaceId) {
|
|
@@ -3339,7 +3970,7 @@ function createSandboxRuntime(options) {
|
|
|
3339
3970
|
const networkPolicy = preflightNetworkPolicy(sandboxName);
|
|
3340
3971
|
try {
|
|
3341
3972
|
return adaptSandbox(
|
|
3342
|
-
await
|
|
3973
|
+
await Sandbox2.create({
|
|
3343
3974
|
timeout: timeoutMs,
|
|
3344
3975
|
...networkPolicy ? { name: sandboxName, persistent: false, networkPolicy } : {},
|
|
3345
3976
|
source: {
|
|
@@ -3360,20 +3991,6 @@ function createSandboxRuntime(options) {
|
|
|
3360
3991
|
}
|
|
3361
3992
|
throw new Error(`Failed to boot sandbox from snapshot ${snapshotId}`);
|
|
3362
3993
|
};
|
|
3363
|
-
const setSnapshotAttributes = (snapshot) => {
|
|
3364
|
-
setSpanAttributes({
|
|
3365
|
-
"app.sandbox.source": snapshot.snapshotId ? "snapshot" : "created",
|
|
3366
|
-
"app.sandbox.snapshot.cache_hit": snapshot.cacheHit,
|
|
3367
|
-
"app.sandbox.snapshot.resolve_outcome": snapshot.resolveOutcome,
|
|
3368
|
-
...snapshot.profileHash ? {
|
|
3369
|
-
"app.sandbox.snapshot.profile_hash": snapshot.profileHash
|
|
3370
|
-
} : {},
|
|
3371
|
-
"app.sandbox.snapshot.dependency_count": snapshot.dependencyCount,
|
|
3372
|
-
...snapshot.rebuildReason ? {
|
|
3373
|
-
"app.sandbox.snapshot.rebuild_reason": snapshot.rebuildReason
|
|
3374
|
-
} : {}
|
|
3375
|
-
});
|
|
3376
|
-
};
|
|
3377
3994
|
const createSandboxFromResolvedSnapshot = async (params) => {
|
|
3378
3995
|
const { runtime, snapshot, sandboxCredentials, sandboxName, signal } = params;
|
|
3379
3996
|
signal?.throwIfAborted();
|
|
@@ -3381,7 +3998,7 @@ function createSandboxRuntime(options) {
|
|
|
3381
3998
|
const networkPolicy = preflightNetworkPolicy(sandboxName);
|
|
3382
3999
|
const resources = getSandboxResources();
|
|
3383
4000
|
const session = adaptSandbox(
|
|
3384
|
-
await
|
|
4001
|
+
await Sandbox2.create({
|
|
3385
4002
|
timeout: timeoutMs,
|
|
3386
4003
|
runtime,
|
|
3387
4004
|
...networkPolicy ? { name: sandboxName, persistent: false, networkPolicy } : {},
|
|
@@ -3403,13 +4020,21 @@ function createSandboxRuntime(options) {
|
|
|
3403
4020
|
} catch (error) {
|
|
3404
4021
|
if (!isMissingError(error)) throw error;
|
|
3405
4022
|
setSpanAttributes({ "app.sandbox.snapshot.rebuild_after_missing": true });
|
|
3406
|
-
const rebuilt = await
|
|
4023
|
+
const rebuilt = params.workspace ? await resolveWorkspaceSnapshot({
|
|
4024
|
+
workspace: params.workspace,
|
|
4025
|
+
runtime,
|
|
4026
|
+
staleSnapshotId: snapshot.snapshotId,
|
|
4027
|
+
signal,
|
|
4028
|
+
shouldYield: options.shouldYield,
|
|
4029
|
+
applyNetworkPolicy,
|
|
4030
|
+
prepareRepositories: options.onWorkspacePrepare,
|
|
4031
|
+
removeCredentialRoute: Boolean(options.createNetworkPolicy)
|
|
4032
|
+
}) : await resolve({
|
|
3407
4033
|
runtime,
|
|
3408
4034
|
timeoutMs,
|
|
3409
4035
|
forceRebuild: true,
|
|
3410
4036
|
staleSnapshotId: snapshot.snapshotId,
|
|
3411
4037
|
signal,
|
|
3412
|
-
workspace: params.workspace,
|
|
3413
4038
|
prepareWorkspace: params.prepareWorkspace
|
|
3414
4039
|
});
|
|
3415
4040
|
if (!rebuilt.snapshotId) throw error;
|
|
@@ -3446,15 +4071,22 @@ function createSandboxRuntime(options) {
|
|
|
3446
4071
|
"app.sandbox.runtime": runtime
|
|
3447
4072
|
},
|
|
3448
4073
|
async () => {
|
|
3449
|
-
const snapshot = await
|
|
4074
|
+
const snapshot = workspace ? await resolveWorkspaceSnapshot({
|
|
4075
|
+
workspace,
|
|
4076
|
+
runtime,
|
|
4077
|
+
signal,
|
|
4078
|
+
shouldYield: options.shouldYield,
|
|
4079
|
+
applyNetworkPolicy,
|
|
4080
|
+
prepareRepositories: options.onWorkspacePrepare,
|
|
4081
|
+
removeCredentialRoute: Boolean(options.createNetworkPolicy)
|
|
4082
|
+
}) : await resolve({
|
|
3450
4083
|
runtime,
|
|
3451
4084
|
timeoutMs,
|
|
3452
4085
|
signal,
|
|
3453
|
-
workspace,
|
|
3454
4086
|
prepareWorkspace
|
|
3455
4087
|
});
|
|
3456
4088
|
signal?.throwIfAborted();
|
|
3457
|
-
|
|
4089
|
+
setSnapshotSpanAttributes(snapshot);
|
|
3458
4090
|
const created = await createSandboxFromResolvedSnapshot({
|
|
3459
4091
|
runtime,
|
|
3460
4092
|
snapshot,
|
|
@@ -3464,12 +4096,6 @@ function createSandboxRuntime(options) {
|
|
|
3464
4096
|
workspace,
|
|
3465
4097
|
prepareWorkspace
|
|
3466
4098
|
});
|
|
3467
|
-
if (workspace) {
|
|
3468
|
-
await recordResolvedWorkspaceSnapshot(
|
|
3469
|
-
workspace.id,
|
|
3470
|
-
created.snapshot
|
|
3471
|
-
);
|
|
3472
|
-
}
|
|
3473
4099
|
return created.session;
|
|
3474
4100
|
}
|
|
3475
4101
|
);
|
|
@@ -3488,6 +4114,9 @@ function createSandboxRuntime(options) {
|
|
|
3488
4114
|
let networkPolicyKey;
|
|
3489
4115
|
try {
|
|
3490
4116
|
networkPolicyKey = await applyNetworkPolicy(createdSandbox);
|
|
4117
|
+
if (workspace) {
|
|
4118
|
+
await options.onWorkspacePrepare?.(createdSandbox, workspace, signal);
|
|
4119
|
+
}
|
|
3491
4120
|
await prepareSandbox(createdSandbox);
|
|
3492
4121
|
} catch (error) {
|
|
3493
4122
|
if (workspace) await stopSession(createdSandbox);
|
|
@@ -3551,7 +4180,7 @@ function createSandboxRuntime(options) {
|
|
|
3551
4180
|
"app.sandbox.source": "id_hint"
|
|
3552
4181
|
},
|
|
3553
4182
|
async () => adaptSandbox(
|
|
3554
|
-
await
|
|
4183
|
+
await Sandbox2.get({
|
|
3555
4184
|
name: ref.id,
|
|
3556
4185
|
resume: true,
|
|
3557
4186
|
...sandboxCredentials ?? {},
|
|
@@ -4636,6 +5265,11 @@ async function grepFilesWithRipgrep(params) {
|
|
|
4636
5265
|
});
|
|
4637
5266
|
if (result.exitCode !== 0 && result.exitCode !== 1) {
|
|
4638
5267
|
const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
|
|
5268
|
+
if (/error parsing glob/i.test(detail)) {
|
|
5269
|
+
throw new ToolInputError(`Invalid glob: ${params.glob ?? ""}`, {
|
|
5270
|
+
cause: new Error(detail)
|
|
5271
|
+
});
|
|
5272
|
+
}
|
|
4639
5273
|
if (/regex parse error|error parsing regex/i.test(detail)) {
|
|
4640
5274
|
throw new ToolInputError(`Invalid regex pattern: ${params.pattern}`, {
|
|
4641
5275
|
cause: new Error(detail)
|
|
@@ -5090,6 +5724,7 @@ function createSandbox(options) {
|
|
|
5090
5724
|
skills: options.skills,
|
|
5091
5725
|
referenceFiles: options.referenceFiles,
|
|
5092
5726
|
timeoutMs: options.timeoutMs,
|
|
5727
|
+
shouldYield: options.shouldYield,
|
|
5093
5728
|
traceContext,
|
|
5094
5729
|
commandEnv: credentialEgress ? resolveSandboxCommandEnvironment : void 0,
|
|
5095
5730
|
createNetworkPolicy: credentialEgress || hasTracePropagationDomains ? (sessionId, traceHeaders) => buildSandboxEgressNetworkPolicy({
|
|
@@ -5543,9 +6178,9 @@ function createSandbox(options) {
|
|
|
5543
6178
|
return void 0;
|
|
5544
6179
|
}
|
|
5545
6180
|
const { fs: fs5 } = await runtime.tools();
|
|
5546
|
-
const workspaceDirectories = activeWorkspace?.repos.map((repo) => tryWorkspaceRepoCheckoutPath(repo.repo)).filter(
|
|
5547
|
-
(checkoutPath) =>
|
|
5548
|
-
) ?? [];
|
|
6181
|
+
const workspaceDirectories = activeWorkspace?.repos.map((repo) => tryWorkspaceRepoCheckoutPath(repo.repo)).filter(
|
|
6182
|
+
(checkoutPath) => Boolean(checkoutPath)
|
|
6183
|
+
).map((checkoutPath) => `${SANDBOX_WORKSPACE_ROOT}/${checkoutPath}`) ?? [];
|
|
5549
6184
|
const directories = workspaceDirectories.length > 0 ? workspaceDirectories : await listRepositoryDirectories(fs5);
|
|
5550
6185
|
return await resolveRepositoryInstructionsForDirectories({
|
|
5551
6186
|
directories,
|
|
@@ -5985,6 +6620,7 @@ function createAgentSandbox(options) {
|
|
|
5985
6620
|
tracePropagation: options.tracePropagation,
|
|
5986
6621
|
egressSignals: options.egressSignals,
|
|
5987
6622
|
credentialEgress: options.credentialEgress,
|
|
6623
|
+
shouldYield: options.shouldYield,
|
|
5988
6624
|
prepare: options.prepareSandbox,
|
|
5989
6625
|
prepareWorkspace: options.prepareWorkspace,
|
|
5990
6626
|
onSandboxRefChanged: async (sandboxRef) => {
|
|
@@ -11997,7 +12633,7 @@ function getTimeZoneOffsetMinutes(timestampMs, timezone) {
|
|
|
11997
12633
|
import { z as z55 } from "zod";
|
|
11998
12634
|
|
|
11999
12635
|
// src/chat/artifacts/store.ts
|
|
12000
|
-
import { createHash as createHash4, randomUUID as
|
|
12636
|
+
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
12001
12637
|
import { and as and2, eq as eq2, isNull } from "drizzle-orm";
|
|
12002
12638
|
var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
12003
12639
|
var IMAGE_TYPES = {
|
|
@@ -12090,7 +12726,7 @@ async function publishImage(args) {
|
|
|
12090
12726
|
eq2(juniorArtifacts.sha256, sha256)
|
|
12091
12727
|
)
|
|
12092
12728
|
);
|
|
12093
|
-
const id = existing?.id ??
|
|
12729
|
+
const id = existing?.id ?? randomUUID5();
|
|
12094
12730
|
const filename = `${id}.${imageType.extension}`;
|
|
12095
12731
|
const storageKey = artifactKey(filename);
|
|
12096
12732
|
await args.storage.put({
|
|
@@ -13443,7 +14079,18 @@ function createWorkspaceTools(context) {
|
|
|
13443
14079
|
const workspace = await getWorkspaceByName(getDb(), name);
|
|
13444
14080
|
if (!workspace)
|
|
13445
14081
|
throw new ToolInputError(`Workspace not found: ${name}`);
|
|
13446
|
-
|
|
14082
|
+
try {
|
|
14083
|
+
await context.workspaces.switch(workspace, options.signal);
|
|
14084
|
+
} catch (error) {
|
|
14085
|
+
if (isWorkspaceSnapshotWaitingError(error)) {
|
|
14086
|
+
return {
|
|
14087
|
+
workspace: view(workspace),
|
|
14088
|
+
waiting: "workspace_snapshot",
|
|
14089
|
+
timed_out: true
|
|
14090
|
+
};
|
|
14091
|
+
}
|
|
14092
|
+
throw error;
|
|
14093
|
+
}
|
|
13447
14094
|
await tryRecordWorkspaceSwitchStat(workspace);
|
|
13448
14095
|
return { workspace: view(workspace) };
|
|
13449
14096
|
}
|
|
@@ -14963,7 +15610,7 @@ function createMcpAuthOrchestration(input) {
|
|
|
14963
15610
|
}
|
|
14964
15611
|
|
|
14965
15612
|
// src/chat/egress/plugin.ts
|
|
14966
|
-
import { randomUUID as
|
|
15613
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
14967
15614
|
|
|
14968
15615
|
// src/chat/sandbox/egress/credentials.ts
|
|
14969
15616
|
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
@@ -15717,8 +16364,8 @@ async function executeCredentialedEgressRequest(input) {
|
|
|
15717
16364
|
function credentialContextForPluginEgress(credentialContext) {
|
|
15718
16365
|
return {
|
|
15719
16366
|
credentials: credentialContext,
|
|
15720
|
-
contextId:
|
|
15721
|
-
egressId: `plugin-egress:${
|
|
16367
|
+
contextId: randomUUID6(),
|
|
16368
|
+
egressId: `plugin-egress:${randomUUID6()}`,
|
|
15722
16369
|
expiresAtMs: Date.now() + 30 * 60 * 1e3
|
|
15723
16370
|
};
|
|
15724
16371
|
}
|
|
@@ -16151,6 +16798,7 @@ async function wireAgentTools(args) {
|
|
|
16151
16798
|
actor: args.currentActor,
|
|
16152
16799
|
locationConfiguration: args.run.environment?.locationConfiguration,
|
|
16153
16800
|
configurationValues: args.configurationValues,
|
|
16801
|
+
shouldYield: args.durability.shouldYield,
|
|
16154
16802
|
getActiveSkill: () => args.skillSandbox.getActiveSkill(),
|
|
16155
16803
|
prepareSandbox: pluginHooks.prepareSandbox,
|
|
16156
16804
|
prepareWorkspace: async (sandbox, recipe, signal) => await pluginHooks.prepareWorkspace(sandbox, recipe.repos, signal),
|
|
@@ -17470,7 +18118,10 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
17470
18118
|
history: run.history
|
|
17471
18119
|
});
|
|
17472
18120
|
const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...input.piMessages ?? []];
|
|
17473
|
-
const handoffEnabled = !isAgentRunFeatureDisabled(
|
|
18121
|
+
const handoffEnabled = !isAgentRunFeatureDisabled(
|
|
18122
|
+
policy.disabledFeatures,
|
|
18123
|
+
"handoff"
|
|
18124
|
+
);
|
|
17474
18125
|
const storedTurnRoute = await loadTurnRoute({ conversationId, turnId });
|
|
17475
18126
|
if (storedTurnRoute) {
|
|
17476
18127
|
const resumedAfterHandoff = handoffEnabled && activeModelProfile !== STANDARD_MODEL_PROFILE && activeModelProfile !== storedTurnRoute.modelProfile;
|
|
@@ -17943,10 +18594,14 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
17943
18594
|
const containsHandoff = toolCalls2.some(
|
|
17944
18595
|
(call) => call.name === HANDOFF_TOOL_NAME
|
|
17945
18596
|
);
|
|
17946
|
-
|
|
18597
|
+
const containsWorkspaceSwitch = toolCalls2.some(
|
|
18598
|
+
(call) => call.name === "switchWorkspace"
|
|
18599
|
+
);
|
|
18600
|
+
const exclusiveTool = containsHandoff ? HANDOFF_TOOL_NAME : containsWorkspaceSwitch ? "switchWorkspace" : void 0;
|
|
18601
|
+
if (exclusiveTool && toolCalls2.length !== 1) {
|
|
17947
18602
|
return {
|
|
17948
18603
|
block: true,
|
|
17949
|
-
reason:
|
|
18604
|
+
reason: `${exclusiveTool} must be the only tool call in its assistant message; reissue it alone`
|
|
17950
18605
|
};
|
|
17951
18606
|
}
|
|
17952
18607
|
return void 0;
|
|
@@ -17962,15 +18617,46 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
17962
18617
|
},
|
|
17963
18618
|
prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
|
|
17964
18619
|
try {
|
|
18620
|
+
let continuedSnapshotWait = false;
|
|
18621
|
+
for (; ; ) {
|
|
18622
|
+
const snapshotWait = await continueWorkspaceSnapshotWait({
|
|
18623
|
+
messages: currentAgentMessages(),
|
|
18624
|
+
tools: toolsForActiveProfile(),
|
|
18625
|
+
signal: hookSignal
|
|
18626
|
+
});
|
|
18627
|
+
if (snapshotWait.kind === "none") break;
|
|
18628
|
+
if (snapshotWait.kind === "failed") {
|
|
18629
|
+
agent.state.messages = snapshotWait.messages;
|
|
18630
|
+
await runResume.persistSafeBoundary(snapshotWait.messages);
|
|
18631
|
+
throw snapshotWait.error;
|
|
18632
|
+
}
|
|
18633
|
+
continuedSnapshotWait = true;
|
|
18634
|
+
agent.state.messages = snapshotWait.messages;
|
|
18635
|
+
await runResume.persistSafeBoundary(snapshotWait.messages);
|
|
18636
|
+
if (snapshotWait.kind === "ready") break;
|
|
18637
|
+
const yieldError2 = runResume.prepareYieldIfDue(
|
|
18638
|
+
snapshotWait.messages
|
|
18639
|
+
);
|
|
18640
|
+
if (yieldError2) throw yieldError2;
|
|
18641
|
+
await sleep(5e3, hookSignal);
|
|
18642
|
+
}
|
|
17965
18643
|
const handoffUpdate = applyPendingHandoff();
|
|
18644
|
+
const snapshotWaitUpdate = continuedSnapshotWait ? {
|
|
18645
|
+
context: {
|
|
18646
|
+
...nextTurn.context,
|
|
18647
|
+
messages: currentAgentMessages(),
|
|
18648
|
+
tools: agent.state.tools
|
|
18649
|
+
}
|
|
18650
|
+
} : void 0;
|
|
17966
18651
|
const pendingMessages = await drainSteeringMessages();
|
|
17967
18652
|
const capacityUpdate = await applyActiveContextCompaction(
|
|
17968
|
-
handoffUpdate ? currentAgentMessages() : nextTurn.context.messages,
|
|
18653
|
+
handoffUpdate || continuedSnapshotWait ? currentAgentMessages() : nextTurn.context.messages,
|
|
17969
18654
|
hookSignal,
|
|
17970
18655
|
pendingMessages,
|
|
17971
18656
|
true
|
|
17972
18657
|
);
|
|
17973
|
-
const
|
|
18658
|
+
const handoffOrWaitUpdate = handoffUpdate ? { ...snapshotWaitUpdate, ...handoffUpdate } : snapshotWaitUpdate;
|
|
18659
|
+
const combinedUpdate = capacityUpdate ? { ...handoffOrWaitUpdate, ...capacityUpdate } : handoffOrWaitUpdate;
|
|
17974
18660
|
const repositoryInstructionsUpdate = await repositoryInstructionsContext.applyUpdate(
|
|
17975
18661
|
combinedUpdate,
|
|
17976
18662
|
nextTurn.context
|