@sentry/junior 0.162.0 → 0.164.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.
Files changed (96) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-hooks-TL2SGC5W.js → agent-hooks-WTBPNRTC.js} +10 -10
  3. package/dist/api/acp/route.d.ts +13 -0
  4. package/dist/api/schema/workspace.d.ts +47 -0
  5. package/dist/api/schema.d.ts +2 -0
  6. package/dist/api/schema.js +15 -3
  7. package/dist/api/workspaces/routes.d.ts +4 -0
  8. package/dist/api.js +321 -408
  9. package/dist/app.d.ts +2 -2
  10. package/dist/app.js +482 -38
  11. package/dist/{catalog-runtime-IGSMIADJ.js → catalog-runtime-CZ2SQINY.js} +5 -5
  12. package/dist/chat/agent/sandbox.d.ts +4 -0
  13. package/dist/chat/api-turns/cancellation.d.ts +27 -0
  14. package/dist/chat/api-turns/routing.d.ts +31 -0
  15. package/dist/chat/api-turns/work.d.ts +12 -29
  16. package/dist/chat/app/conversation-work.d.ts +6 -1
  17. package/dist/chat/app/production.d.ts +2 -2
  18. package/dist/chat/conversations/history.d.ts +3 -0
  19. package/dist/chat/conversations/turn-lifecycle.d.ts +1 -1
  20. package/dist/chat/experimental.d.ts +1 -1
  21. package/dist/chat/plugins/agent-hooks.d.ts +4 -0
  22. package/dist/chat/repository-instructions.d.ts +1 -1
  23. package/dist/chat/sandbox/paths.d.ts +1 -0
  24. package/dist/chat/sandbox/prepare-workspace.d.ts +11 -0
  25. package/dist/chat/sandbox/ref.d.ts +1 -0
  26. package/dist/chat/sandbox/sandbox.d.ts +4 -0
  27. package/dist/chat/sandbox/session.d.ts +4 -0
  28. package/dist/chat/sandbox/snapshot/profile.d.ts +4 -3
  29. package/dist/chat/sandbox/snapshot/resolve.d.ts +4 -0
  30. package/dist/chat/sandbox/workspace.d.ts +4 -0
  31. package/dist/chat/tools/types.d.ts +5 -0
  32. package/dist/chat/workspaces/checkout-path.d.ts +4 -0
  33. package/dist/chat/workspaces/store.d.ts +37 -0
  34. package/dist/chat/workspaces/tools.d.ts +4 -0
  35. package/dist/chat/workspaces/types.d.ts +13 -0
  36. package/dist/chat/workspaces/validation.d.ts +21 -0
  37. package/dist/{chunk-THXMSSFZ.js → chunk-26JYGL2L.js} +1 -1
  38. package/dist/{chunk-OPNJRQDP.js → chunk-2K25MRZK.js} +9 -6
  39. package/dist/{chunk-C3KUKA27.js → chunk-3L23VTY7.js} +17 -17
  40. package/dist/{chunk-JJ3VXFDI.js → chunk-4GTOFUQF.js} +1 -1
  41. package/dist/{chunk-RDV4BQRZ.js → chunk-5FWQ73UZ.js} +1 -1
  42. package/dist/{chunk-BPZQWAZJ.js → chunk-5GLGQHO3.js} +4 -1
  43. package/dist/{chunk-K7IV6UV3.js → chunk-6BXV63YX.js} +5 -5
  44. package/dist/{chunk-4WYGQ47U.js → chunk-6ENAACWN.js} +5 -5
  45. package/dist/{chunk-EHQLTK3X.js → chunk-7WKWMMYQ.js} +323 -127
  46. package/dist/chunk-BVREIWBD.js +131 -0
  47. package/dist/{chunk-DZC3F2UI.js → chunk-BXBZDV3C.js} +1 -1
  48. package/dist/{chunk-HW4BW3PC.js → chunk-CGCNFEAU.js} +4 -4
  49. package/dist/{chunk-JRVURR3H.js → chunk-FKDY7NTC.js} +543 -164
  50. package/dist/{chunk-MNCEGXEH.js → chunk-FX4PX3SA.js} +2 -2
  51. package/dist/{chunk-OBFRMQBY.js → chunk-H36GJVJV.js} +9 -9
  52. package/dist/{chunk-V7QL4QMW.js → chunk-HHW6ZVIZ.js} +12 -7
  53. package/dist/{chunk-O6P7DZ6O.js → chunk-JW23XD4I.js} +6 -6
  54. package/dist/{chunk-DDQ43S5Q.js → chunk-LPLVNMVN.js} +4 -4
  55. package/dist/{chunk-5NEBOHRG.js → chunk-NAGI2XH3.js} +4 -4
  56. package/dist/{chunk-FEWT5KPP.js → chunk-NLTQRYNP.js} +2 -2
  57. package/dist/{chunk-SA6BEEAB.js → chunk-PU45BSBS.js} +108 -74
  58. package/dist/{chunk-7DTPFFQX.js → chunk-RBV4JHCP.js} +1 -1
  59. package/dist/{chunk-6BJFIWEV.js → chunk-RJHZNPDH.js} +46 -87
  60. package/dist/{chunk-FESUWAYU.js → chunk-RULIISEI.js} +1 -1
  61. package/dist/{chunk-MMBMDSRK.js → chunk-RYYEHWIL.js} +1 -1
  62. package/dist/{chunk-Y75ZDQB5.js → chunk-VHSEMJTS.js} +1 -1
  63. package/dist/{chunk-5IDFPCON.js → chunk-VRS6WDK5.js} +1 -1
  64. package/dist/{chunk-EBEHJPFF.js → chunk-VV2DN24T.js} +1 -1
  65. package/dist/{chunk-WHAJ3QVY.js → chunk-WJUKESU2.js} +1 -1
  66. package/dist/{chunk-H2KW7AZC.js → chunk-XAIRI54J.js} +1 -1
  67. package/dist/{chunk-AJ7J2HWY.js → chunk-Y2B6AXZQ.js} +6 -6
  68. package/dist/{chunk-QHCIZLIX.js → chunk-YHCTPDXS.js} +356 -13
  69. package/dist/{chunk-CZ6EHOU5.js → chunk-ZSNABC2E.js} +40 -2
  70. package/dist/{chunk-KQSO4DJ4.js → chunk-ZUTAGCPH.js} +9 -9
  71. package/dist/cli/chat.js +32 -32
  72. package/dist/cli/check.js +6 -6
  73. package/dist/cli/plugins.js +13 -13
  74. package/dist/cli/snapshot-create.js +10 -10
  75. package/dist/cli/upgrade.js +7 -7
  76. package/dist/db/schema/workspaces.d.ts +169 -0
  77. package/dist/db/schema.d.ts +169 -1
  78. package/dist/{db-66XZ4HHQ.js → db-32KRFS6L.js} +7 -7
  79. package/dist/{experimental-HH6OM6UH.js → experimental-XVDOSRBS.js} +1 -1
  80. package/dist/instrumentation.js +4 -4
  81. package/dist/nitro.js +2 -2
  82. package/dist/{oauth-callback-server-FMLKDUUF.js → oauth-callback-server-CGZP2Z6U.js} +24 -24
  83. package/dist/{runner-YZONUMNY.js → runner-S5N4Q2XL.js} +32 -34
  84. package/dist/{skills-3UDJJXSO.js → skills-KYPSHSQR.js} +6 -6
  85. package/dist/spawn-EGOB3CLV.js +37 -0
  86. package/dist/{validation-3HTDBONC.js → validation-SZBE7JNF.js} +6 -6
  87. package/dist/version.js +1 -1
  88. package/dist/{work-O4YTIS54.js → work-GCZKHEZX.js} +18 -18
  89. package/dist/worker-6MY5Q4A2.js +24 -0
  90. package/migrations/0029_dizzy_barracuda.sql +19 -0
  91. package/migrations/meta/0029_snapshot.json +2709 -0
  92. package/migrations/meta/_journal.json +8 -1
  93. package/package.json +7 -4
  94. package/dist/chunk-EM225ITJ.js +0 -56
  95. package/dist/spawn-TBPVA5S2.js +0 -37
  96. package/dist/worker-WHURUEYX.js +0 -24
@@ -7,7 +7,7 @@ import {
7
7
  PluginCredentialFailureError,
8
8
  agentNameSchema,
9
9
  createPluginAuthOrchestration
10
- } from "./chunk-K7IV6UV3.js";
10
+ } from "./chunk-6BXV63YX.js";
11
11
  import {
12
12
  authorizationId
13
13
  } from "./chunk-DIB4GX3G.js";
@@ -32,7 +32,7 @@ import {
32
32
  searchSlackUsers,
33
33
  selectPluginGrant,
34
34
  toMcpProviderError
35
- } from "./chunk-OBFRMQBY.js";
35
+ } from "./chunk-H36GJVJV.js";
36
36
  import {
37
37
  RetryableDeliveryError,
38
38
  actorFromRun,
@@ -48,7 +48,7 @@ import {
48
48
  getSlackMessagePermalink,
49
49
  resolveBaseUrl,
50
50
  uploadFilesToConversation
51
- } from "./chunk-O6P7DZ6O.js";
51
+ } from "./chunk-JW23XD4I.js";
52
52
  import {
53
53
  parseSandboxEgressAuthRequiredSignal,
54
54
  parseSandboxEgressPermissionDeniedSignal,
@@ -57,8 +57,6 @@ import {
57
57
  } from "./chunk-7MA4HV4W.js";
58
58
  import {
59
59
  bindSandboxFileSystem,
60
- buildCommandScript,
61
- buildNonInteractiveShellScript,
62
60
  createSandboxSession,
63
61
  ensureDockerDaemon,
64
62
  getSandboxResources,
@@ -71,10 +69,10 @@ import {
71
69
  isSandboxUnavailableError,
72
70
  isSnapshottingError,
73
71
  resolve,
74
- runNonInteractiveCommand,
72
+ stopSession,
75
73
  throwSandboxOperationError,
76
74
  wrapSandboxSetupError
77
- } from "./chunk-6BJFIWEV.js";
75
+ } from "./chunk-RJHZNPDH.js";
78
76
  import {
79
77
  SCHEDULED_TASK_SYSTEM_ACTOR,
80
78
  compactEventTask,
@@ -107,10 +105,10 @@ import {
107
105
  scheduledTaskAttributes,
108
106
  storeAttachments,
109
107
  writableEventTask
110
- } from "./chunk-4WYGQ47U.js";
108
+ } from "./chunk-6ENAACWN.js";
111
109
  import {
112
110
  parseSlackThreadId
113
- } from "./chunk-WHAJ3QVY.js";
111
+ } from "./chunk-WJUKESU2.js";
114
112
  import {
115
113
  AGENTS_REMOVAL_NOTICE,
116
114
  AGENTS_REPLACEMENT_NOTICE,
@@ -181,7 +179,7 @@ import {
181
179
  stripRuntimeTurnContext,
182
180
  trimTrailingAssistantMessages,
183
181
  truncateText
184
- } from "./chunk-V7QL4QMW.js";
182
+ } from "./chunk-HHW6ZVIZ.js";
185
183
  import {
186
184
  JUNIOR_THREAD_STATE_TTL_MS
187
185
  } from "./chunk-3Y47VMHV.js";
@@ -199,12 +197,17 @@ import {
199
197
  getPluginUserPromptContributions,
200
198
  getSlackClient,
201
199
  getSlackToolContext,
200
+ getWorkspace,
201
+ getWorkspaceByName,
202
202
  isConversationScopedChannel,
203
+ listWorkspaces,
203
204
  normalizeSlackConversationId,
204
205
  readActorIdentity,
205
206
  resolveChannelCapabilities,
206
- withSlackRetries
207
- } from "./chunk-QHCIZLIX.js";
207
+ tryWorkspaceRepoCheckoutPath,
208
+ withSlackRetries,
209
+ workspaceRepoCheckoutPath
210
+ } from "./chunk-YHCTPDXS.js";
208
211
  import {
209
212
  KNOWN_CONVERSATION_EVENT_TYPES,
210
213
  contextProvenance,
@@ -216,23 +219,27 @@ import {
216
219
  instructionActors,
217
220
  instructionProvenanceFor,
218
221
  sameActorIdentity
219
- } from "./chunk-DDQ43S5Q.js";
222
+ } from "./chunk-LPLVNMVN.js";
220
223
  import {
221
224
  juniorArtifacts,
222
225
  juniorIdentities
223
- } from "./chunk-CZ6EHOU5.js";
226
+ } from "./chunk-ZSNABC2E.js";
224
227
  import {
225
228
  SANDBOX_DATA_ROOT,
229
+ SANDBOX_REPOS_ROOT,
226
230
  SANDBOX_SKILLS_ROOT,
227
231
  SANDBOX_WORKSPACE_ROOT,
232
+ buildCommandScript,
233
+ buildNonInteractiveShellScript,
234
+ runNonInteractiveCommand,
228
235
  sandboxSkillDir,
229
236
  sandboxSkillFile,
230
237
  sandboxSkillPathResolution,
231
238
  sleep
232
- } from "./chunk-EM225ITJ.js";
239
+ } from "./chunk-BVREIWBD.js";
233
240
  import {
234
241
  getStateAdapter
235
- } from "./chunk-DZC3F2UI.js";
242
+ } from "./chunk-BXBZDV3C.js";
236
243
  import {
237
244
  ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
238
245
  COMPACTION_SUMMARY_PREFIX,
@@ -265,7 +272,7 @@ import {
265
272
  resolveGatewayCredential,
266
273
  resolveGatewayModel,
267
274
  unwrapCurrentInstruction
268
- } from "./chunk-MNCEGXEH.js";
275
+ } from "./chunk-FX4PX3SA.js";
269
276
  import {
270
277
  canExposeConversationPayload,
271
278
  resolveConversationPrivacy,
@@ -275,16 +282,16 @@ import {
275
282
  toGenAiMessagesTraceAttributes,
276
283
  toGenAiPayloadMetadata,
277
284
  toGenAiPayloadTraceAttributes
278
- } from "./chunk-7DTPFFQX.js";
285
+ } from "./chunk-RBV4JHCP.js";
279
286
  import {
280
287
  discoverSkills,
281
288
  findSkillByName,
282
289
  loadSkillsByName,
283
290
  parseSkillInvocation
284
- } from "./chunk-FEWT5KPP.js";
291
+ } from "./chunk-NLTQRYNP.js";
285
292
  import {
286
293
  pluginCatalogRuntime
287
- } from "./chunk-FESUWAYU.js";
294
+ } from "./chunk-RULIISEI.js";
288
295
  import {
289
296
  CredentialUnavailableError,
290
297
  OAuthProviderError,
@@ -292,7 +299,7 @@ import {
292
299
  getOAuthProviderErrorAttributes,
293
300
  resolveAuthTokenPlaceholder,
294
301
  resolvePluginCommandEnv
295
- } from "./chunk-EBEHJPFF.js";
302
+ } from "./chunk-VV2DN24T.js";
296
303
  import {
297
304
  credentialUserSubjectId
298
305
  } from "./chunk-BDUENQD3.js";
@@ -320,7 +327,7 @@ import {
320
327
  summarizeMessageText,
321
328
  withLogContext,
322
329
  withSpan
323
- } from "./chunk-Y75ZDQB5.js";
330
+ } from "./chunk-VHSEMJTS.js";
324
331
  import {
325
332
  startInactiveSpan,
326
333
  withActiveSpan
@@ -2935,6 +2942,33 @@ async function resolveSandboxCommandEnvironment() {
2935
2942
  import { randomUUID as randomUUID2 } from "crypto";
2936
2943
  import { Sandbox } from "@vercel/sandbox";
2937
2944
 
2945
+ // src/chat/sandbox/prepare-workspace.ts
2946
+ async function prepareWorkspaceSnapshot(params) {
2947
+ const { sandbox, workspace, signal } = params;
2948
+ signal?.throwIfAborted();
2949
+ await params.applyNetworkPolicy(sandbox);
2950
+ await params.prepareRepositories?.(sandbox, workspace, signal);
2951
+ if (params.removeCredentialRoute) {
2952
+ await sandbox.update({ networkPolicy: "allow-all" });
2953
+ }
2954
+ if (!workspace.setupScript.trim()) return;
2955
+ const result = await sandbox.runCommand({
2956
+ cmd: "bash",
2957
+ args: ["-euo", "pipefail", "-c", workspace.setupScript],
2958
+ cwd: SANDBOX_WORKSPACE_ROOT,
2959
+ env: {
2960
+ JUNIOR_REPOS_ROOT: SANDBOX_REPOS_ROOT,
2961
+ JUNIOR_WORKSPACE_ROOT: SANDBOX_WORKSPACE_ROOT
2962
+ },
2963
+ signal
2964
+ });
2965
+ if (result.exitCode !== 0) {
2966
+ throw new Error(
2967
+ `Workspace setup failed: ${result.stderr.trim() || `exit ${result.exitCode}`}`
2968
+ );
2969
+ }
2970
+ }
2971
+
2938
2972
  // src/chat/sandbox/skill-sync.ts
2939
2973
  import fs3 from "fs/promises";
2940
2974
  import path3 from "path";
@@ -3130,9 +3164,7 @@ var SANDBOX_NAME_PREFIX = "junior-";
3130
3164
  var MAX_KEEPALIVE_INTERVAL_MS = 3e4;
3131
3165
  var MIN_KEEPALIVE_INTERVAL_MS = 1e3;
3132
3166
  function sandboxFetchOptions(signal) {
3133
- if (!signal) {
3134
- return {};
3135
- }
3167
+ if (!signal) return {};
3136
3168
  return {
3137
3169
  fetch: (input, init) => {
3138
3170
  const requestSignal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
@@ -3144,14 +3176,11 @@ function sandboxFetchOptions(signal) {
3144
3176
  };
3145
3177
  }
3146
3178
  function truncateOutput(output, maxLength) {
3147
- if (output.length <= maxLength) {
3148
- return { value: output, truncated: false };
3149
- }
3150
- const truncatedLength = output.length - maxLength;
3179
+ if (output.length <= maxLength) return { value: output, truncated: false };
3151
3180
  return {
3152
3181
  value: `${output.slice(0, maxLength)}
3153
3182
 
3154
- [output truncated: ${truncatedLength} characters removed]`,
3183
+ [output truncated: ${output.length - maxLength} characters removed]`,
3155
3184
  truncated: true
3156
3185
  };
3157
3186
  }
@@ -3183,13 +3212,12 @@ function createSandboxRuntime(options) {
3183
3212
  let closed = false;
3184
3213
  const timeoutMs = options.timeoutMs ?? 1e3 * 60 * 30;
3185
3214
  const traceContext = options.traceContext ?? {};
3186
- const dependencyProfileHash = hash(SANDBOX_RUNTIME);
3215
+ let activeWorkspace = options.workspace;
3216
+ let dependencyProfileHash = hash(SANDBOX_RUNTIME, activeWorkspace);
3187
3217
  const resolveCommandEnv = options.commandEnv ?? (async () => ({}));
3188
3218
  const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
3189
3219
  const invalidateSession = (sessionId) => {
3190
- if (sessionId && activeSandbox && activeSandbox.session.sessionId !== sessionId) {
3191
- return;
3192
- }
3220
+ if (sessionId && activeSandbox?.session.sessionId !== sessionId) return;
3193
3221
  activeSandbox = null;
3194
3222
  if (keepAliveTimer) {
3195
3223
  clearTimeout(keepAliveTimer);
@@ -3200,21 +3228,21 @@ function createSandboxRuntime(options) {
3200
3228
  onUnavailable: invalidateSession
3201
3229
  });
3202
3230
  const createSandboxName = () => `${SANDBOX_NAME_PREFIX}${randomUUID2()}`;
3203
- const preflightNetworkPolicy = (sandboxName) => {
3204
- return options.createNetworkPolicy?.(sandboxName);
3205
- };
3206
- const reportSandboxRef = async (nextSandbox) => {
3207
- const nextRef = {
3208
- id: nextSandbox.sandboxId,
3209
- ...dependencyProfileHash ? { profileHash: dependencyProfileHash } : {}
3210
- };
3211
- sandboxRef = nextRef;
3212
- if (reportedSandboxRef?.id === nextRef.id && reportedSandboxRef.profileHash === nextRef.profileHash) {
3231
+ const preflightNetworkPolicy = (sandboxName) => options.createNetworkPolicy?.(sandboxName);
3232
+ const persistSandboxRef = async (nextRef) => {
3233
+ if (reportedSandboxRef?.id === nextRef.id && reportedSandboxRef.profileHash === nextRef.profileHash && reportedSandboxRef.workspaceId === nextRef.workspaceId) {
3234
+ sandboxRef = nextRef;
3213
3235
  return;
3214
3236
  }
3215
3237
  await options.onSandboxRefChanged?.(nextRef);
3238
+ sandboxRef = nextRef;
3216
3239
  reportedSandboxRef = nextRef;
3217
3240
  };
3241
+ const sandboxReference = (session, workspace, hash2) => ({
3242
+ id: session.sandboxId,
3243
+ ...hash2 ? { profileHash: hash2 } : {},
3244
+ ...workspace?.id ? { workspaceId: workspace.id } : {}
3245
+ });
3218
3246
  const rememberSandbox = (nextSandbox, networkPolicyKey) => {
3219
3247
  activeSandbox = { session: nextSandbox, networkPolicyKey };
3220
3248
  return nextSandbox;
@@ -3327,7 +3355,15 @@ function createSandboxRuntime(options) {
3327
3355
  });
3328
3356
  };
3329
3357
  const createSandboxFromResolvedSnapshot = async (params) => {
3330
- const { runtime, snapshot, sandboxCredentials, sandboxName, signal } = params;
3358
+ const {
3359
+ runtime,
3360
+ snapshot,
3361
+ sandboxCredentials,
3362
+ sandboxName,
3363
+ signal,
3364
+ workspace,
3365
+ prepareWorkspace
3366
+ } = params;
3331
3367
  signal?.throwIfAborted();
3332
3368
  if (!snapshot.snapshotId) {
3333
3369
  const networkPolicy = preflightNetworkPolicy(sandboxName);
@@ -3362,7 +3398,9 @@ function createSandboxRuntime(options) {
3362
3398
  timeoutMs,
3363
3399
  forceRebuild: true,
3364
3400
  staleSnapshotId: snapshot.snapshotId,
3365
- signal
3401
+ signal,
3402
+ workspace,
3403
+ prepareWorkspace
3366
3404
  });
3367
3405
  if (!rebuiltSnapshot.snapshotId) {
3368
3406
  throw error;
@@ -3376,10 +3414,18 @@ function createSandboxRuntime(options) {
3376
3414
  );
3377
3415
  }
3378
3416
  };
3379
- const createFreshSandbox = async (signal) => {
3417
+ const createSandboxCandidate = async (workspace, hash2, signal) => {
3380
3418
  const runtime = SANDBOX_RUNTIME;
3381
3419
  const sandboxCredentials = getVercelSandboxCredentials();
3382
3420
  const sandboxName = createSandboxName();
3421
+ const prepareWorkspace = workspace ? async (sandbox) => await prepareWorkspaceSnapshot({
3422
+ sandbox,
3423
+ workspace,
3424
+ signal,
3425
+ applyNetworkPolicy,
3426
+ prepareRepositories: options.onWorkspacePrepare,
3427
+ removeCredentialRoute: Boolean(options.createNetworkPolicy)
3428
+ }) : void 0;
3383
3429
  let createdSandbox;
3384
3430
  try {
3385
3431
  createdSandbox = await withSandboxSpan(
@@ -3394,7 +3440,9 @@ function createSandboxRuntime(options) {
3394
3440
  const snapshot = await resolve({
3395
3441
  runtime,
3396
3442
  timeoutMs,
3397
- signal
3443
+ signal,
3444
+ workspace,
3445
+ prepareWorkspace
3398
3446
  });
3399
3447
  signal?.throwIfAborted();
3400
3448
  setSnapshotAttributes(snapshot);
@@ -3403,47 +3451,74 @@ function createSandboxRuntime(options) {
3403
3451
  snapshot,
3404
3452
  sandboxCredentials,
3405
3453
  sandboxName,
3406
- signal
3454
+ signal,
3455
+ workspace,
3456
+ prepareWorkspace
3407
3457
  });
3408
3458
  }
3409
3459
  );
3410
3460
  } catch (error) {
3411
3461
  return failSetup(error);
3412
3462
  }
3413
- await reportSandboxRef(createdSandbox);
3463
+ const ref = sandboxReference(createdSandbox, workspace, hash2);
3464
+ if (!workspace) {
3465
+ try {
3466
+ await persistSandboxRef(ref);
3467
+ } catch (error) {
3468
+ await stopSession(createdSandbox);
3469
+ throw error;
3470
+ }
3471
+ }
3414
3472
  let networkPolicyKey;
3415
3473
  try {
3416
3474
  networkPolicyKey = await applyNetworkPolicy(createdSandbox);
3417
3475
  await prepareSandbox(createdSandbox);
3418
3476
  } catch (error) {
3477
+ if (workspace) await stopSession(createdSandbox);
3419
3478
  return failSetup(error);
3420
3479
  }
3421
- return rememberSandbox(createdSandbox, networkPolicyKey);
3480
+ return {
3481
+ session: createdSandbox,
3482
+ networkPolicyKey,
3483
+ profileHash: hash2,
3484
+ ref,
3485
+ workspace
3486
+ };
3487
+ };
3488
+ const createFreshSandbox = async (signal) => {
3489
+ const candidate = await createSandboxCandidate(
3490
+ activeWorkspace,
3491
+ dependencyProfileHash,
3492
+ signal
3493
+ );
3494
+ try {
3495
+ await persistSandboxRef(candidate.ref);
3496
+ return rememberSandbox(candidate.session, candidate.networkPolicyKey);
3497
+ } catch (error) {
3498
+ await stopSession(candidate.session);
3499
+ throw error;
3500
+ }
3422
3501
  };
3423
3502
  const discardHintIfProfileChanged = () => {
3424
3503
  if (activeSandbox || !sandboxRef || dependencyProfileHash === sandboxRef.profileHash) {
3425
3504
  return;
3426
3505
  }
3506
+ const attrs = {
3507
+ ...sandboxRef.profileHash ? { "app.sandbox.previous_profile_hash": sandboxRef.profileHash } : {},
3508
+ ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3509
+ };
3427
3510
  setSpanAttributes({
3428
3511
  "app.sandbox.reused": false,
3429
3512
  "app.sandbox.recreate.reason": "dependency_profile_mismatch",
3430
- ...sandboxRef.profileHash ? {
3431
- "app.sandbox.previous_profile_hash": sandboxRef.profileHash
3432
- } : {},
3433
- ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3513
+ ...attrs
3434
3514
  });
3435
3515
  logInfo("sandbox.hint.discarded", {
3436
3516
  "app.decision.reason": "dependency_profile_mismatch",
3437
- ...sandboxRef.profileHash ? {
3438
- "app.sandbox.previous_profile_hash": sandboxRef.profileHash
3439
- } : {},
3440
- ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3517
+ ...attrs
3441
3518
  });
3442
3519
  sandboxRef = void 0;
3443
3520
  };
3444
- const tryReuseCachedSandbox = async () => {
3445
- return activeSandbox?.session ?? null;
3446
- };
3521
+ const tryReuseCachedSandbox = () => activeSandbox?.session ?? null;
3447
3522
  const tryRestoreHintedSandbox = async (signal) => {
3448
3523
  const ref = sandboxRef;
3449
3524
  if (!ref) {
@@ -3481,9 +3556,9 @@ function createSandboxRuntime(options) {
3481
3556
  }
3482
3557
  let networkPolicyKey;
3483
3558
  try {
3484
- await reportSandboxRef(hintedSandbox);
3485
3559
  networkPolicyKey = await applyNetworkPolicy(hintedSandbox);
3486
3560
  await prepareSandbox(hintedSandbox);
3561
+ await persistSandboxRef({ ...ref, id: hintedSandbox.sandboxId });
3487
3562
  return rememberSandbox(hintedSandbox, networkPolicyKey);
3488
3563
  } catch (error) {
3489
3564
  if (isSandboxUnavailableError(error)) {
@@ -3589,33 +3664,25 @@ function createSandboxRuntime(options) {
3589
3664
  stderrTruncated: stderr.truncated
3590
3665
  };
3591
3666
  };
3592
- const extendKeepAlive = async (activeSandbox2) => {
3667
+ const extendKeepAlive = async (session) => {
3593
3668
  const keepAliveMs = parseKeepAliveMs();
3594
- if (keepAliveMs === 0) {
3595
- return;
3596
- }
3669
+ if (keepAliveMs === 0) return;
3597
3670
  try {
3598
3671
  await withSandboxSpan(
3599
3672
  "sandbox.keepalive.extend",
3600
3673
  "sandbox.keepalive",
3601
- {
3602
- "app.sandbox.keepalive_ms": keepAliveMs
3603
- },
3674
+ { "app.sandbox.keepalive_ms": keepAliveMs },
3604
3675
  async () => {
3605
- await activeSandbox2.extendTimeout(keepAliveMs);
3676
+ await session.extendTimeout(keepAliveMs);
3606
3677
  }
3607
3678
  );
3608
3679
  } catch (error) {
3609
- if (isSandboxUnavailableError(error)) {
3610
- throw error;
3611
- }
3680
+ if (isSandboxUnavailableError(error)) throw error;
3612
3681
  }
3613
3682
  };
3614
3683
  const startKeepAlive = (session) => {
3615
3684
  const keepAliveMs = parseKeepAliveMs();
3616
- if (keepAliveMs === 0 || closed || keepAliveTimer) {
3617
- return;
3618
- }
3685
+ if (keepAliveMs === 0 || closed || keepAliveTimer) return;
3619
3686
  const intervalMs = Math.max(
3620
3687
  MIN_KEEPALIVE_INTERVAL_MS,
3621
3688
  Math.min(MAX_KEEPALIVE_INTERVAL_MS, Math.floor(keepAliveMs / 2))
@@ -3623,18 +3690,14 @@ function createSandboxRuntime(options) {
3623
3690
  const schedule = () => {
3624
3691
  keepAliveTimer = setTimeout(async () => {
3625
3692
  keepAliveTimer = void 0;
3626
- if (closed || activeSandbox?.session !== session) {
3627
- return;
3628
- }
3693
+ if (closed || activeSandbox?.session !== session) return;
3629
3694
  try {
3630
3695
  await extendKeepAlive(session);
3631
3696
  } catch {
3632
3697
  invalidateSession(session.sessionId);
3633
3698
  return;
3634
3699
  }
3635
- if (closed || activeSandbox?.session !== session) {
3636
- return;
3637
- }
3700
+ if (closed || activeSandbox?.session !== session) return;
3638
3701
  schedule();
3639
3702
  }, intervalMs);
3640
3703
  keepAliveTimer.unref?.();
@@ -3714,18 +3777,57 @@ function createSandboxRuntime(options) {
3714
3777
  };
3715
3778
  };
3716
3779
  const ensureReadySandbox = async (signal) => {
3717
- const activeSandbox2 = await getOrAcquireSandbox(signal);
3780
+ const session = await getOrAcquireSandbox(signal);
3718
3781
  signal?.throwIfAborted();
3719
- await probeSession(activeSandbox2);
3782
+ await probeSession(session);
3720
3783
  signal?.throwIfAborted();
3721
- await extendKeepAlive(activeSandbox2);
3722
- startKeepAlive(activeSandbox2);
3723
- return activeSandbox2;
3784
+ await extendKeepAlive(session);
3785
+ startKeepAlive(session);
3786
+ return session;
3724
3787
  };
3725
3788
  return {
3726
3789
  sandboxRef() {
3727
3790
  return sandboxRef ? { ...sandboxRef } : void 0;
3728
3791
  },
3792
+ async switchWorkspace(workspace, signal) {
3793
+ signal?.throwIfAborted();
3794
+ const nextProfileHash = hash(SANDBOX_RUNTIME, workspace);
3795
+ if (activeWorkspace?.id === workspace.id && dependencyProfileHash === nextProfileHash) {
3796
+ return;
3797
+ }
3798
+ if (acquiringSandbox) {
3799
+ await getOrAcquireSandbox(signal);
3800
+ }
3801
+ const previous = activeSandbox;
3802
+ let candidate;
3803
+ try {
3804
+ candidate = await createSandboxCandidate(
3805
+ workspace,
3806
+ nextProfileHash,
3807
+ signal
3808
+ );
3809
+ await probeSession(candidate.session);
3810
+ signal?.throwIfAborted();
3811
+ await extendKeepAlive(candidate.session);
3812
+ signal?.throwIfAborted();
3813
+ await persistSandboxRef(candidate.ref);
3814
+ } catch (error) {
3815
+ await stopSession(candidate?.session);
3816
+ throw error;
3817
+ }
3818
+ if (keepAliveTimer) {
3819
+ clearTimeout(keepAliveTimer);
3820
+ keepAliveTimer = void 0;
3821
+ }
3822
+ activeWorkspace = candidate.workspace;
3823
+ dependencyProfileHash = candidate.profileHash;
3824
+ activeSandbox = {
3825
+ session: candidate.session,
3826
+ networkPolicyKey: candidate.networkPolicyKey
3827
+ };
3828
+ startKeepAlive(candidate.session);
3829
+ await stopSession(previous?.session);
3830
+ },
3729
3831
  async acquire(signal) {
3730
3832
  return await getOrAcquireSandbox(signal);
3731
3833
  },
@@ -4965,8 +5067,10 @@ function createSandbox(options) {
4965
5067
  ...permissionDenied ? { permissionDenied } : {}
4966
5068
  };
4967
5069
  };
5070
+ let activeWorkspace = options.workspace;
4968
5071
  const runtime = createSandboxRuntime({
4969
5072
  sandboxRef: options.sandboxRef,
5073
+ workspace: options.workspace,
4970
5074
  skills: options.skills,
4971
5075
  referenceFiles: options.referenceFiles,
4972
5076
  timeoutMs: options.timeoutMs,
@@ -4980,6 +5084,7 @@ function createSandbox(options) {
4980
5084
  traceHeaders
4981
5085
  }) : void 0,
4982
5086
  onSandboxPrepare: options.prepare,
5087
+ onWorkspacePrepare: options.prepareWorkspace,
4983
5088
  onSandboxRefChanged: options.onSandboxRefChanged
4984
5089
  });
4985
5090
  const createToolCallContext = (signal, setToolCallSpanAttributes) => {
@@ -5422,7 +5527,9 @@ function createSandbox(options) {
5422
5527
  return void 0;
5423
5528
  }
5424
5529
  const { fs: fs5 } = await runtime.tools();
5425
- const selected = await findSingleRepositoryDirectory(fs5);
5530
+ const primary = activeWorkspace?.repos.find((repo) => repo.isPrimary);
5531
+ const primaryPath = primary ? tryWorkspaceRepoCheckoutPath(primary.repo) : void 0;
5532
+ const selected = primaryPath ? `${SANDBOX_WORKSPACE_ROOT}/${primaryPath}` : await findSingleRepositoryDirectory(fs5);
5426
5533
  if (!selected) return void 0;
5427
5534
  return await resolveRepositoryInstructions({
5428
5535
  cwd: selected,
@@ -5430,6 +5537,10 @@ function createSandbox(options) {
5430
5537
  });
5431
5538
  },
5432
5539
  workspace,
5540
+ async switchWorkspace(recipe, signal) {
5541
+ await runtime.switchWorkspace(recipe, signal);
5542
+ activeWorkspace = recipe;
5543
+ },
5433
5544
  tools: {
5434
5545
  supports(toolName) {
5435
5546
  return SANDBOX_TOOL_NAMES.has(toolName);
@@ -5851,6 +5962,7 @@ function bashCommand(input) {
5851
5962
  function createAgentSandbox(options) {
5852
5963
  const sandbox = createSandbox({
5853
5964
  sandboxRef: options.sandboxRef,
5965
+ workspace: options.workspace,
5854
5966
  skills: options.skills,
5855
5967
  referenceFiles: listReferenceFiles(),
5856
5968
  traceContext: options.traceContext,
@@ -5858,6 +5970,7 @@ function createAgentSandbox(options) {
5858
5970
  egressSignals: options.egressSignals,
5859
5971
  credentialEgress: options.credentialEgress,
5860
5972
  prepare: options.prepareSandbox,
5973
+ prepareWorkspace: options.prepareWorkspace,
5861
5974
  onSandboxRefChanged: async (sandboxRef) => {
5862
5975
  options.onSandboxRefChanged(sandboxRef);
5863
5976
  await options.persistSandboxRef?.(sandboxRef);
@@ -5867,6 +5980,7 @@ function createAgentSandbox(options) {
5867
5980
  captureRepositoryInstructions: sandbox.captureRepositoryInstructions,
5868
5981
  workspace: sandbox.workspace,
5869
5982
  sandboxRef: sandbox.sandboxRef,
5983
+ switchWorkspace: sandbox.switchWorkspace,
5870
5984
  close: sandbox.close,
5871
5985
  tools: {
5872
5986
  supports: sandbox.tools.supports,
@@ -13554,6 +13668,80 @@ function createWriteFileTool() {
13554
13668
  });
13555
13669
  }
13556
13670
 
13671
+ // src/chat/workspaces/tools.ts
13672
+ import { z as z63 } from "zod";
13673
+ var repoSchema = z63.object({
13674
+ provider: z63.string(),
13675
+ repo: z63.string(),
13676
+ checkout_path: z63.string(),
13677
+ is_primary: z63.boolean()
13678
+ });
13679
+ var workspaceSchema = z63.object({
13680
+ id: z63.string(),
13681
+ name: z63.string(),
13682
+ repos: z63.array(repoSchema)
13683
+ });
13684
+ function view(workspace) {
13685
+ return {
13686
+ id: workspace.id,
13687
+ name: workspace.name,
13688
+ repos: workspace.repos.map((repo) => ({
13689
+ provider: repo.provider,
13690
+ repo: repo.repo,
13691
+ checkout_path: workspaceRepoCheckoutPath(repo.repo),
13692
+ is_primary: repo.isPrimary
13693
+ }))
13694
+ };
13695
+ }
13696
+ function createWorkspaceTools(context) {
13697
+ if (!context.workspaces) return {};
13698
+ return {
13699
+ listWorkspaces: zodTool({
13700
+ annotations: {
13701
+ destructiveHint: false,
13702
+ idempotentHint: true,
13703
+ openWorldHint: false,
13704
+ readOnlyHint: true
13705
+ },
13706
+ description: "List named repository workspaces that can replace the current sandbox.",
13707
+ inputSchema: z63.object({}).strict(),
13708
+ outputSchema: juniorToolOutputSchema.extend({
13709
+ active_workspace_id: z63.string().nullable(),
13710
+ workspaces: z63.array(workspaceSchema)
13711
+ }),
13712
+ async execute() {
13713
+ return {
13714
+ active_workspace_id: context.workspaces.activeWorkspaceId() ?? null,
13715
+ workspaces: (await listWorkspaces(getDb())).map(view)
13716
+ };
13717
+ }
13718
+ }),
13719
+ switchWorkspace: zodTool({
13720
+ executionMode: "sequential",
13721
+ annotations: {
13722
+ destructiveHint: true,
13723
+ idempotentHint: true,
13724
+ openWorldHint: true,
13725
+ readOnlyHint: false
13726
+ },
13727
+ description: "Replace the current sandbox with a named preconfigured repository workspace. Files in the prior sandbox do not carry over.",
13728
+ inputSchema: z63.object({
13729
+ name: z63.string().trim().min(1).describe("Exact workspace name.")
13730
+ }).strict(),
13731
+ outputSchema: juniorToolOutputSchema.extend({
13732
+ workspace: workspaceSchema
13733
+ }),
13734
+ async execute({ name }, options) {
13735
+ const workspace = await getWorkspaceByName(getDb(), name);
13736
+ if (!workspace)
13737
+ throw new ToolInputError(`Workspace not found: ${name}`);
13738
+ await context.workspaces.switch(workspace, options.signal);
13739
+ return { workspace: view(workspace) };
13740
+ }
13741
+ })
13742
+ };
13743
+ }
13744
+
13557
13745
  // src/chat/tools/index.ts
13558
13746
  function createToolState() {
13559
13747
  const operationResultCache = /* @__PURE__ */ new Map();
@@ -13596,7 +13784,8 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
13596
13784
  }),
13597
13785
  ...createResourceEventTools(context, resourceEventCatalog),
13598
13786
  ...createEventTaskTools(context, resourceEventCatalog),
13599
- ...createScheduledTaskTools(context)
13787
+ ...createScheduledTaskTools(context),
13788
+ ...createWorkspaceTools(context)
13600
13789
  };
13601
13790
  if (context.conversationId) {
13602
13791
  tools.searchConversationEvents = createSearchConversationEventsTool(context);
@@ -14278,7 +14467,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
14278
14467
  }
14279
14468
 
14280
14469
  // src/chat/tools/execute-tool.ts
14281
- import { z as z63 } from "zod";
14470
+ import { z as z64 } from "zod";
14282
14471
  var EXECUTE_TOOL_NAME = "executeTool";
14283
14472
  function createExecuteToolTool() {
14284
14473
  return zodTool({
@@ -14290,11 +14479,11 @@ function createExecuteToolTool() {
14290
14479
  },
14291
14480
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
14292
14481
  executionMode: "sequential",
14293
- inputSchema: z63.object({
14294
- tool_name: z63.string().min(1).describe(
14482
+ inputSchema: z64.object({
14483
+ tool_name: z64.string().min(1).describe(
14295
14484
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
14296
14485
  ),
14297
- arguments: z63.record(z63.string(), z63.unknown()).describe(
14486
+ arguments: z64.record(z64.string(), z64.unknown()).describe(
14298
14487
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
14299
14488
  ).optional()
14300
14489
  }).strict(),
@@ -14334,7 +14523,7 @@ function planToolExposure(tools) {
14334
14523
  }
14335
14524
 
14336
14525
  // src/chat/tools/search-tools.ts
14337
- import { z as z64 } from "zod";
14526
+ import { z as z65 } from "zod";
14338
14527
  var SEARCH_TOOLS_NAME = "searchTools";
14339
14528
  var DEFAULT_MAX_RESULTS2 = 5;
14340
14529
  var MAX_RESULTS3 = 20;
@@ -14379,29 +14568,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
14379
14568
  "you",
14380
14569
  "your"
14381
14570
  ]);
14382
- var searchToolsSourceSchema = z64.object({
14383
- id: z64.string(),
14384
- description: z64.string()
14571
+ var searchToolsSourceSchema = z65.object({
14572
+ id: z65.string(),
14573
+ description: z65.string()
14385
14574
  }).strict();
14386
- var searchToolsToolSchema = z64.object({
14387
- tool_name: z64.string(),
14388
- description: z64.string(),
14389
- exposure: z64.enum(["direct", "deferred", "modelOnly", "hidden"]),
14390
- source: z64.string().optional(),
14391
- input_schema: z64.unknown(),
14392
- call_notes: z64.array(z64.string()),
14393
- annotations: z64.record(z64.string(), z64.unknown())
14575
+ var searchToolsToolSchema = z65.object({
14576
+ tool_name: z65.string(),
14577
+ description: z65.string(),
14578
+ exposure: z65.enum(["direct", "deferred", "modelOnly", "hidden"]),
14579
+ source: z65.string().optional(),
14580
+ input_schema: z65.unknown(),
14581
+ call_notes: z65.array(z65.string()),
14582
+ annotations: z65.record(z65.string(), z65.unknown())
14394
14583
  }).strict();
14395
14584
  var searchToolsOutputSchema = juniorToolOutputSchema.extend({
14396
- query: z64.string().nullable(),
14397
- source: z64.string().nullable(),
14398
- sources: z64.array(searchToolsSourceSchema),
14399
- total_catalog_tools: z64.number().int().nonnegative(),
14400
- total_eligible_tools: z64.number().int().nonnegative(),
14401
- total_matches: z64.number().int().nonnegative(),
14402
- returned_tools: z64.number().int().nonnegative(),
14403
- execution_tool: z64.literal("executeTool"),
14404
- tools: z64.array(searchToolsToolSchema)
14585
+ query: z65.string().nullable(),
14586
+ source: z65.string().nullable(),
14587
+ sources: z65.array(searchToolsSourceSchema),
14588
+ total_catalog_tools: z65.number().int().nonnegative(),
14589
+ total_eligible_tools: z65.number().int().nonnegative(),
14590
+ total_matches: z65.number().int().nonnegative(),
14591
+ returned_tools: z65.number().int().nonnegative(),
14592
+ execution_tool: z65.literal("executeTool"),
14593
+ tools: z65.array(searchToolsToolSchema)
14405
14594
  }).strict();
14406
14595
  function terms(value) {
14407
14596
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -14612,14 +14801,14 @@ function createSearchToolsTool(catalogTools) {
14612
14801
  openWorldHint: false,
14613
14802
  readOnlyHint: true
14614
14803
  },
14615
- inputSchema: z64.object({
14616
- query: z64.string().nullable().describe(
14804
+ inputSchema: z65.object({
14805
+ query: z65.string().nullable().describe(
14617
14806
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
14618
14807
  ).optional(),
14619
- source: z64.string().nullable().describe(
14808
+ source: z65.string().nullable().describe(
14620
14809
  "Optional source id to search within, such as a plugin source returned in sources."
14621
14810
  ).optional(),
14622
- max_results: z64.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
14811
+ max_results: z65.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
14623
14812
  }).strict(),
14624
14813
  outputSchema: searchToolsOutputSchema,
14625
14814
  privateTraceResult: (result) => ({
@@ -15878,7 +16067,7 @@ function createPluginEgress(deps) {
15878
16067
  }
15879
16068
 
15880
16069
  // src/chat/services/guardian-action-review.ts
15881
- import { z as z65 } from "zod";
16070
+ import { z as z66 } from "zod";
15882
16071
 
15883
16072
  // src/chat/services/guardian-action-policy.ts
15884
16073
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -15946,17 +16135,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
15946
16135
  - Keep the reason to one concise, specific sentence grounded in the main risk or authorization fact. For ask, name the exact action, target, and material side effects the user must confirm.`;
15947
16136
 
15948
16137
  // src/chat/services/guardian-action-review.ts
15949
- var guardianDecisionSchema = z65.object({
15950
- decision: z65.enum(["allow", "ask", "deny"]),
15951
- reason: z65.string().trim().min(1).max(500),
15952
- riskLevel: z65.enum(["low", "medium", "high", "critical"]),
15953
- userAuthorization: z65.enum(["high", "medium", "low", "unknown"])
16138
+ var guardianDecisionSchema = z66.object({
16139
+ decision: z66.enum(["allow", "ask", "deny"]),
16140
+ reason: z66.string().trim().min(1).max(500),
16141
+ riskLevel: z66.enum(["low", "medium", "high", "critical"]),
16142
+ userAuthorization: z66.enum(["high", "medium", "low", "unknown"])
15954
16143
  }).strict();
15955
16144
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
15956
16145
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
15957
16146
  var MAX_PROPOSAL_CHARS = 192e3;
15958
16147
  function guardianPrompt(proposal) {
15959
- const proposalJson = JSON.stringify(z65.json().parse(proposal), null, 2);
16148
+ const proposalJson = JSON.stringify(z66.json().parse(proposal), null, 2);
15960
16149
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
15961
16150
  throw new Error(
15962
16151
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -16241,8 +16430,10 @@ async function wireAgentTools(args) {
16241
16430
  actor: args.currentActor,
16242
16431
  actors: args.currentActors
16243
16432
  });
16433
+ const workspace = args.state.sandboxRef?.workspaceId ? await getWorkspace(getDb(), args.state.sandboxRef.workspaceId) : void 0;
16244
16434
  const agentSandbox = createAgentSandbox({
16245
16435
  sandboxRef: args.state.sandboxRef,
16436
+ workspace,
16246
16437
  skills: args.availableSkills,
16247
16438
  traceContext: args.spanContext,
16248
16439
  tracePropagation: args.run.environment?.sandboxTracePropagation,
@@ -16253,6 +16444,7 @@ async function wireAgentTools(args) {
16253
16444
  configurationValues: args.configurationValues,
16254
16445
  getActiveSkill: () => args.skillSandbox.getActiveSkill(),
16255
16446
  prepareSandbox: pluginHooks.prepareSandbox,
16447
+ prepareWorkspace: async (sandbox, recipe, signal) => await pluginHooks.prepareWorkspace(sandbox, recipe.repos, signal),
16256
16448
  onSandboxRefChanged: args.onSandboxRefChanged,
16257
16449
  persistSandboxRef: args.durability.onSandboxRefChanged
16258
16450
  });
@@ -16356,7 +16548,11 @@ async function wireAgentTools(args) {
16356
16548
  const toolRuntimeContext = {
16357
16549
  ...commonToolRuntimeContext,
16358
16550
  ...toolRoute,
16359
- attachmentStorage: args.run.environment?.attachmentStorage
16551
+ attachmentStorage: args.run.environment?.attachmentStorage,
16552
+ workspaces: {
16553
+ activeWorkspaceId: () => agentSandbox.sandboxRef()?.workspaceId,
16554
+ switch: agentSandbox.switchWorkspace
16555
+ }
16360
16556
  };
16361
16557
  const actionReview = createToolActionReview({
16362
16558
  context: {