@sentry/junior 0.162.0 → 0.163.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 (90) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-hooks-TL2SGC5W.js → agent-hooks-6IX5CV7G.js} +10 -10
  3. package/dist/api/acp/route.d.ts +13 -0
  4. package/dist/api.js +204 -403
  5. package/dist/app.d.ts +2 -2
  6. package/dist/app.js +480 -38
  7. package/dist/{catalog-runtime-IGSMIADJ.js → catalog-runtime-NEPZNTOM.js} +5 -5
  8. package/dist/chat/agent/sandbox.d.ts +4 -0
  9. package/dist/chat/api-turns/cancellation.d.ts +27 -0
  10. package/dist/chat/api-turns/routing.d.ts +31 -0
  11. package/dist/chat/api-turns/work.d.ts +12 -29
  12. package/dist/chat/app/conversation-work.d.ts +6 -1
  13. package/dist/chat/app/production.d.ts +2 -2
  14. package/dist/chat/conversations/history.d.ts +3 -0
  15. package/dist/chat/conversations/turn-lifecycle.d.ts +1 -1
  16. package/dist/chat/experimental.d.ts +1 -1
  17. package/dist/chat/plugins/agent-hooks.d.ts +4 -0
  18. package/dist/chat/repository-instructions.d.ts +1 -1
  19. package/dist/chat/sandbox/paths.d.ts +1 -0
  20. package/dist/chat/sandbox/prepare-workspace.d.ts +11 -0
  21. package/dist/chat/sandbox/ref.d.ts +1 -0
  22. package/dist/chat/sandbox/sandbox.d.ts +4 -0
  23. package/dist/chat/sandbox/session.d.ts +4 -0
  24. package/dist/chat/sandbox/snapshot/profile.d.ts +4 -3
  25. package/dist/chat/sandbox/snapshot/resolve.d.ts +4 -0
  26. package/dist/chat/sandbox/workspace.d.ts +4 -0
  27. package/dist/chat/tools/types.d.ts +5 -0
  28. package/dist/chat/workspaces/checkout-path.d.ts +4 -0
  29. package/dist/chat/workspaces/store.d.ts +8 -0
  30. package/dist/chat/workspaces/tools.d.ts +4 -0
  31. package/dist/chat/workspaces/types.d.ts +13 -0
  32. package/dist/{chunk-BPZQWAZJ.js → chunk-2FBPN2Y4.js} +4 -1
  33. package/dist/{chunk-H2KW7AZC.js → chunk-2HBJQEBE.js} +1 -1
  34. package/dist/{chunk-O6P7DZ6O.js → chunk-3OZNFCPR.js} +6 -6
  35. package/dist/{chunk-7DTPFFQX.js → chunk-3T7RJVDJ.js} +1 -1
  36. package/dist/{chunk-FEWT5KPP.js → chunk-4P2XNVTO.js} +2 -2
  37. package/dist/{chunk-CZ6EHOU5.js → chunk-ATWHPSMF.js} +40 -2
  38. package/dist/{chunk-MMBMDSRK.js → chunk-BLZUFHM7.js} +1 -1
  39. package/dist/chunk-BVREIWBD.js +131 -0
  40. package/dist/{chunk-OPNJRQDP.js → chunk-EKGJVUVS.js} +9 -6
  41. package/dist/{chunk-4WYGQ47U.js → chunk-FEIHINKR.js} +5 -5
  42. package/dist/{chunk-JJ3VXFDI.js → chunk-GCLHNRMU.js} +1 -1
  43. package/dist/{chunk-WHAJ3QVY.js → chunk-ICJ3KCA2.js} +1 -1
  44. package/dist/{chunk-5NEBOHRG.js → chunk-IIT23D3M.js} +4 -4
  45. package/dist/{chunk-EHQLTK3X.js → chunk-IX4XI42N.js} +376 -128
  46. package/dist/{chunk-DDQ43S5Q.js → chunk-JI5EPI3O.js} +4 -4
  47. package/dist/{chunk-C3KUKA27.js → chunk-KD3ROG6P.js} +17 -17
  48. package/dist/{chunk-JRVURR3H.js → chunk-MWVQ35U6.js} +543 -164
  49. package/dist/{chunk-V7QL4QMW.js → chunk-MXFL7LNZ.js} +12 -7
  50. package/dist/{chunk-RDV4BQRZ.js → chunk-NL53P7PS.js} +1 -1
  51. package/dist/{chunk-EBEHJPFF.js → chunk-O4IK4O7C.js} +1 -1
  52. package/dist/{chunk-KQSO4DJ4.js → chunk-O5ZZL7IR.js} +9 -9
  53. package/dist/{chunk-FESUWAYU.js → chunk-RK7KPI6G.js} +1 -1
  54. package/dist/{chunk-OBFRMQBY.js → chunk-RTPJRLOP.js} +9 -9
  55. package/dist/{chunk-HW4BW3PC.js → chunk-SR6WRBJW.js} +4 -4
  56. package/dist/{chunk-6BJFIWEV.js → chunk-THS4CKN4.js} +46 -87
  57. package/dist/{chunk-DZC3F2UI.js → chunk-TPDNZCZU.js} +1 -1
  58. package/dist/{chunk-Y75ZDQB5.js → chunk-TPT6EF7K.js} +1 -1
  59. package/dist/{chunk-5IDFPCON.js → chunk-VRS6WDK5.js} +1 -1
  60. package/dist/{chunk-AJ7J2HWY.js → chunk-WPGYWDJR.js} +6 -6
  61. package/dist/{chunk-K7IV6UV3.js → chunk-XY2IVIAY.js} +5 -5
  62. package/dist/{chunk-MNCEGXEH.js → chunk-YAUQXZVT.js} +2 -2
  63. package/dist/{chunk-THXMSSFZ.js → chunk-ZDKTZG3G.js} +1 -1
  64. package/dist/{chunk-QHCIZLIX.js → chunk-ZF6IC27P.js} +87 -10
  65. package/dist/cli/chat.js +32 -32
  66. package/dist/cli/check.js +6 -6
  67. package/dist/cli/plugins.js +13 -13
  68. package/dist/cli/snapshot-create.js +10 -10
  69. package/dist/cli/upgrade.js +7 -7
  70. package/dist/db/schema/workspaces.d.ts +169 -0
  71. package/dist/db/schema.d.ts +169 -1
  72. package/dist/{db-66XZ4HHQ.js → db-6VBMHVW2.js} +7 -7
  73. package/dist/{experimental-HH6OM6UH.js → experimental-XVDOSRBS.js} +1 -1
  74. package/dist/instrumentation.js +4 -4
  75. package/dist/nitro.js +2 -2
  76. package/dist/{oauth-callback-server-FMLKDUUF.js → oauth-callback-server-5NCCUIM2.js} +24 -24
  77. package/dist/{runner-YZONUMNY.js → runner-6ONEB6TQ.js} +13 -13
  78. package/dist/{skills-3UDJJXSO.js → skills-Y5R36UH4.js} +6 -6
  79. package/dist/spawn-RFMBQ6B3.js +37 -0
  80. package/dist/{validation-3HTDBONC.js → validation-HLHEQDEK.js} +6 -6
  81. package/dist/version.js +1 -1
  82. package/dist/{work-O4YTIS54.js → work-VIEFYTFG.js} +18 -18
  83. package/dist/worker-MRSKPKQZ.js +24 -0
  84. package/migrations/0029_dizzy_barracuda.sql +19 -0
  85. package/migrations/meta/0029_snapshot.json +2709 -0
  86. package/migrations/meta/_journal.json +8 -1
  87. package/package.json +8 -5
  88. package/dist/chunk-EM225ITJ.js +0 -56
  89. package/dist/spawn-TBPVA5S2.js +0 -37
  90. 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-XY2IVIAY.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-RTPJRLOP.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-3OZNFCPR.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-THS4CKN4.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-FEIHINKR.js";
111
109
  import {
112
110
  parseSlackThreadId
113
- } from "./chunk-WHAJ3QVY.js";
111
+ } from "./chunk-ICJ3KCA2.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-MXFL7LNZ.js";
185
183
  import {
186
184
  JUNIOR_THREAD_STATE_TTL_MS
187
185
  } from "./chunk-3Y47VMHV.js";
@@ -203,8 +201,10 @@ import {
203
201
  normalizeSlackConversationId,
204
202
  readActorIdentity,
205
203
  resolveChannelCapabilities,
206
- withSlackRetries
207
- } from "./chunk-QHCIZLIX.js";
204
+ tryWorkspaceRepoCheckoutPath,
205
+ withSlackRetries,
206
+ workspaceRepoCheckoutPath
207
+ } from "./chunk-ZF6IC27P.js";
208
208
  import {
209
209
  KNOWN_CONVERSATION_EVENT_TYPES,
210
210
  contextProvenance,
@@ -216,23 +216,29 @@ import {
216
216
  instructionActors,
217
217
  instructionProvenanceFor,
218
218
  sameActorIdentity
219
- } from "./chunk-DDQ43S5Q.js";
219
+ } from "./chunk-JI5EPI3O.js";
220
220
  import {
221
221
  juniorArtifacts,
222
- juniorIdentities
223
- } from "./chunk-CZ6EHOU5.js";
222
+ juniorIdentities,
223
+ juniorWorkspaceRepos,
224
+ juniorWorkspaces
225
+ } from "./chunk-ATWHPSMF.js";
224
226
  import {
225
227
  SANDBOX_DATA_ROOT,
228
+ SANDBOX_REPOS_ROOT,
226
229
  SANDBOX_SKILLS_ROOT,
227
230
  SANDBOX_WORKSPACE_ROOT,
231
+ buildCommandScript,
232
+ buildNonInteractiveShellScript,
233
+ runNonInteractiveCommand,
228
234
  sandboxSkillDir,
229
235
  sandboxSkillFile,
230
236
  sandboxSkillPathResolution,
231
237
  sleep
232
- } from "./chunk-EM225ITJ.js";
238
+ } from "./chunk-BVREIWBD.js";
233
239
  import {
234
240
  getStateAdapter
235
- } from "./chunk-DZC3F2UI.js";
241
+ } from "./chunk-TPDNZCZU.js";
236
242
  import {
237
243
  ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
238
244
  COMPACTION_SUMMARY_PREFIX,
@@ -265,7 +271,7 @@ import {
265
271
  resolveGatewayCredential,
266
272
  resolveGatewayModel,
267
273
  unwrapCurrentInstruction
268
- } from "./chunk-MNCEGXEH.js";
274
+ } from "./chunk-YAUQXZVT.js";
269
275
  import {
270
276
  canExposeConversationPayload,
271
277
  resolveConversationPrivacy,
@@ -275,16 +281,16 @@ import {
275
281
  toGenAiMessagesTraceAttributes,
276
282
  toGenAiPayloadMetadata,
277
283
  toGenAiPayloadTraceAttributes
278
- } from "./chunk-7DTPFFQX.js";
284
+ } from "./chunk-3T7RJVDJ.js";
279
285
  import {
280
286
  discoverSkills,
281
287
  findSkillByName,
282
288
  loadSkillsByName,
283
289
  parseSkillInvocation
284
- } from "./chunk-FEWT5KPP.js";
290
+ } from "./chunk-4P2XNVTO.js";
285
291
  import {
286
292
  pluginCatalogRuntime
287
- } from "./chunk-FESUWAYU.js";
293
+ } from "./chunk-RK7KPI6G.js";
288
294
  import {
289
295
  CredentialUnavailableError,
290
296
  OAuthProviderError,
@@ -292,7 +298,7 @@ import {
292
298
  getOAuthProviderErrorAttributes,
293
299
  resolveAuthTokenPlaceholder,
294
300
  resolvePluginCommandEnv
295
- } from "./chunk-EBEHJPFF.js";
301
+ } from "./chunk-O4IK4O7C.js";
296
302
  import {
297
303
  credentialUserSubjectId
298
304
  } from "./chunk-BDUENQD3.js";
@@ -320,7 +326,7 @@ import {
320
326
  summarizeMessageText,
321
327
  withLogContext,
322
328
  withSpan
323
- } from "./chunk-Y75ZDQB5.js";
329
+ } from "./chunk-TPT6EF7K.js";
324
330
  import {
325
331
  startInactiveSpan,
326
332
  withActiveSpan
@@ -2935,6 +2941,33 @@ async function resolveSandboxCommandEnvironment() {
2935
2941
  import { randomUUID as randomUUID2 } from "crypto";
2936
2942
  import { Sandbox } from "@vercel/sandbox";
2937
2943
 
2944
+ // src/chat/sandbox/prepare-workspace.ts
2945
+ async function prepareWorkspaceSnapshot(params) {
2946
+ const { sandbox, workspace, signal } = params;
2947
+ signal?.throwIfAborted();
2948
+ await params.applyNetworkPolicy(sandbox);
2949
+ await params.prepareRepositories?.(sandbox, workspace, signal);
2950
+ if (params.removeCredentialRoute) {
2951
+ await sandbox.update({ networkPolicy: "allow-all" });
2952
+ }
2953
+ if (!workspace.setupScript.trim()) return;
2954
+ const result = await sandbox.runCommand({
2955
+ cmd: "bash",
2956
+ args: ["-euo", "pipefail", "-c", workspace.setupScript],
2957
+ cwd: SANDBOX_WORKSPACE_ROOT,
2958
+ env: {
2959
+ JUNIOR_REPOS_ROOT: SANDBOX_REPOS_ROOT,
2960
+ JUNIOR_WORKSPACE_ROOT: SANDBOX_WORKSPACE_ROOT
2961
+ },
2962
+ signal
2963
+ });
2964
+ if (result.exitCode !== 0) {
2965
+ throw new Error(
2966
+ `Workspace setup failed: ${result.stderr.trim() || `exit ${result.exitCode}`}`
2967
+ );
2968
+ }
2969
+ }
2970
+
2938
2971
  // src/chat/sandbox/skill-sync.ts
2939
2972
  import fs3 from "fs/promises";
2940
2973
  import path3 from "path";
@@ -3130,9 +3163,7 @@ var SANDBOX_NAME_PREFIX = "junior-";
3130
3163
  var MAX_KEEPALIVE_INTERVAL_MS = 3e4;
3131
3164
  var MIN_KEEPALIVE_INTERVAL_MS = 1e3;
3132
3165
  function sandboxFetchOptions(signal) {
3133
- if (!signal) {
3134
- return {};
3135
- }
3166
+ if (!signal) return {};
3136
3167
  return {
3137
3168
  fetch: (input, init) => {
3138
3169
  const requestSignal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
@@ -3144,14 +3175,11 @@ function sandboxFetchOptions(signal) {
3144
3175
  };
3145
3176
  }
3146
3177
  function truncateOutput(output, maxLength) {
3147
- if (output.length <= maxLength) {
3148
- return { value: output, truncated: false };
3149
- }
3150
- const truncatedLength = output.length - maxLength;
3178
+ if (output.length <= maxLength) return { value: output, truncated: false };
3151
3179
  return {
3152
3180
  value: `${output.slice(0, maxLength)}
3153
3181
 
3154
- [output truncated: ${truncatedLength} characters removed]`,
3182
+ [output truncated: ${output.length - maxLength} characters removed]`,
3155
3183
  truncated: true
3156
3184
  };
3157
3185
  }
@@ -3183,13 +3211,12 @@ function createSandboxRuntime(options) {
3183
3211
  let closed = false;
3184
3212
  const timeoutMs = options.timeoutMs ?? 1e3 * 60 * 30;
3185
3213
  const traceContext = options.traceContext ?? {};
3186
- const dependencyProfileHash = hash(SANDBOX_RUNTIME);
3214
+ let activeWorkspace = options.workspace;
3215
+ let dependencyProfileHash = hash(SANDBOX_RUNTIME, activeWorkspace);
3187
3216
  const resolveCommandEnv = options.commandEnv ?? (async () => ({}));
3188
3217
  const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
3189
3218
  const invalidateSession = (sessionId) => {
3190
- if (sessionId && activeSandbox && activeSandbox.session.sessionId !== sessionId) {
3191
- return;
3192
- }
3219
+ if (sessionId && activeSandbox?.session.sessionId !== sessionId) return;
3193
3220
  activeSandbox = null;
3194
3221
  if (keepAliveTimer) {
3195
3222
  clearTimeout(keepAliveTimer);
@@ -3200,21 +3227,21 @@ function createSandboxRuntime(options) {
3200
3227
  onUnavailable: invalidateSession
3201
3228
  });
3202
3229
  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) {
3230
+ const preflightNetworkPolicy = (sandboxName) => options.createNetworkPolicy?.(sandboxName);
3231
+ const persistSandboxRef = async (nextRef) => {
3232
+ if (reportedSandboxRef?.id === nextRef.id && reportedSandboxRef.profileHash === nextRef.profileHash && reportedSandboxRef.workspaceId === nextRef.workspaceId) {
3233
+ sandboxRef = nextRef;
3213
3234
  return;
3214
3235
  }
3215
3236
  await options.onSandboxRefChanged?.(nextRef);
3237
+ sandboxRef = nextRef;
3216
3238
  reportedSandboxRef = nextRef;
3217
3239
  };
3240
+ const sandboxReference = (session, workspace, hash2) => ({
3241
+ id: session.sandboxId,
3242
+ ...hash2 ? { profileHash: hash2 } : {},
3243
+ ...workspace?.id ? { workspaceId: workspace.id } : {}
3244
+ });
3218
3245
  const rememberSandbox = (nextSandbox, networkPolicyKey) => {
3219
3246
  activeSandbox = { session: nextSandbox, networkPolicyKey };
3220
3247
  return nextSandbox;
@@ -3327,7 +3354,15 @@ function createSandboxRuntime(options) {
3327
3354
  });
3328
3355
  };
3329
3356
  const createSandboxFromResolvedSnapshot = async (params) => {
3330
- const { runtime, snapshot, sandboxCredentials, sandboxName, signal } = params;
3357
+ const {
3358
+ runtime,
3359
+ snapshot,
3360
+ sandboxCredentials,
3361
+ sandboxName,
3362
+ signal,
3363
+ workspace,
3364
+ prepareWorkspace
3365
+ } = params;
3331
3366
  signal?.throwIfAborted();
3332
3367
  if (!snapshot.snapshotId) {
3333
3368
  const networkPolicy = preflightNetworkPolicy(sandboxName);
@@ -3362,7 +3397,9 @@ function createSandboxRuntime(options) {
3362
3397
  timeoutMs,
3363
3398
  forceRebuild: true,
3364
3399
  staleSnapshotId: snapshot.snapshotId,
3365
- signal
3400
+ signal,
3401
+ workspace,
3402
+ prepareWorkspace
3366
3403
  });
3367
3404
  if (!rebuiltSnapshot.snapshotId) {
3368
3405
  throw error;
@@ -3376,10 +3413,18 @@ function createSandboxRuntime(options) {
3376
3413
  );
3377
3414
  }
3378
3415
  };
3379
- const createFreshSandbox = async (signal) => {
3416
+ const createSandboxCandidate = async (workspace, hash2, signal) => {
3380
3417
  const runtime = SANDBOX_RUNTIME;
3381
3418
  const sandboxCredentials = getVercelSandboxCredentials();
3382
3419
  const sandboxName = createSandboxName();
3420
+ const prepareWorkspace = workspace ? async (sandbox) => await prepareWorkspaceSnapshot({
3421
+ sandbox,
3422
+ workspace,
3423
+ signal,
3424
+ applyNetworkPolicy,
3425
+ prepareRepositories: options.onWorkspacePrepare,
3426
+ removeCredentialRoute: Boolean(options.createNetworkPolicy)
3427
+ }) : void 0;
3383
3428
  let createdSandbox;
3384
3429
  try {
3385
3430
  createdSandbox = await withSandboxSpan(
@@ -3394,7 +3439,9 @@ function createSandboxRuntime(options) {
3394
3439
  const snapshot = await resolve({
3395
3440
  runtime,
3396
3441
  timeoutMs,
3397
- signal
3442
+ signal,
3443
+ workspace,
3444
+ prepareWorkspace
3398
3445
  });
3399
3446
  signal?.throwIfAborted();
3400
3447
  setSnapshotAttributes(snapshot);
@@ -3403,47 +3450,74 @@ function createSandboxRuntime(options) {
3403
3450
  snapshot,
3404
3451
  sandboxCredentials,
3405
3452
  sandboxName,
3406
- signal
3453
+ signal,
3454
+ workspace,
3455
+ prepareWorkspace
3407
3456
  });
3408
3457
  }
3409
3458
  );
3410
3459
  } catch (error) {
3411
3460
  return failSetup(error);
3412
3461
  }
3413
- await reportSandboxRef(createdSandbox);
3462
+ const ref = sandboxReference(createdSandbox, workspace, hash2);
3463
+ if (!workspace) {
3464
+ try {
3465
+ await persistSandboxRef(ref);
3466
+ } catch (error) {
3467
+ await stopSession(createdSandbox);
3468
+ throw error;
3469
+ }
3470
+ }
3414
3471
  let networkPolicyKey;
3415
3472
  try {
3416
3473
  networkPolicyKey = await applyNetworkPolicy(createdSandbox);
3417
3474
  await prepareSandbox(createdSandbox);
3418
3475
  } catch (error) {
3476
+ if (workspace) await stopSession(createdSandbox);
3419
3477
  return failSetup(error);
3420
3478
  }
3421
- return rememberSandbox(createdSandbox, networkPolicyKey);
3479
+ return {
3480
+ session: createdSandbox,
3481
+ networkPolicyKey,
3482
+ profileHash: hash2,
3483
+ ref,
3484
+ workspace
3485
+ };
3486
+ };
3487
+ const createFreshSandbox = async (signal) => {
3488
+ const candidate = await createSandboxCandidate(
3489
+ activeWorkspace,
3490
+ dependencyProfileHash,
3491
+ signal
3492
+ );
3493
+ try {
3494
+ await persistSandboxRef(candidate.ref);
3495
+ return rememberSandbox(candidate.session, candidate.networkPolicyKey);
3496
+ } catch (error) {
3497
+ await stopSession(candidate.session);
3498
+ throw error;
3499
+ }
3422
3500
  };
3423
3501
  const discardHintIfProfileChanged = () => {
3424
3502
  if (activeSandbox || !sandboxRef || dependencyProfileHash === sandboxRef.profileHash) {
3425
3503
  return;
3426
3504
  }
3505
+ const attrs = {
3506
+ ...sandboxRef.profileHash ? { "app.sandbox.previous_profile_hash": sandboxRef.profileHash } : {},
3507
+ ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3508
+ };
3427
3509
  setSpanAttributes({
3428
3510
  "app.sandbox.reused": false,
3429
3511
  "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 } : {}
3512
+ ...attrs
3434
3513
  });
3435
3514
  logInfo("sandbox.hint.discarded", {
3436
3515
  "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 } : {}
3516
+ ...attrs
3441
3517
  });
3442
3518
  sandboxRef = void 0;
3443
3519
  };
3444
- const tryReuseCachedSandbox = async () => {
3445
- return activeSandbox?.session ?? null;
3446
- };
3520
+ const tryReuseCachedSandbox = () => activeSandbox?.session ?? null;
3447
3521
  const tryRestoreHintedSandbox = async (signal) => {
3448
3522
  const ref = sandboxRef;
3449
3523
  if (!ref) {
@@ -3481,9 +3555,9 @@ function createSandboxRuntime(options) {
3481
3555
  }
3482
3556
  let networkPolicyKey;
3483
3557
  try {
3484
- await reportSandboxRef(hintedSandbox);
3485
3558
  networkPolicyKey = await applyNetworkPolicy(hintedSandbox);
3486
3559
  await prepareSandbox(hintedSandbox);
3560
+ await persistSandboxRef({ ...ref, id: hintedSandbox.sandboxId });
3487
3561
  return rememberSandbox(hintedSandbox, networkPolicyKey);
3488
3562
  } catch (error) {
3489
3563
  if (isSandboxUnavailableError(error)) {
@@ -3589,33 +3663,25 @@ function createSandboxRuntime(options) {
3589
3663
  stderrTruncated: stderr.truncated
3590
3664
  };
3591
3665
  };
3592
- const extendKeepAlive = async (activeSandbox2) => {
3666
+ const extendKeepAlive = async (session) => {
3593
3667
  const keepAliveMs = parseKeepAliveMs();
3594
- if (keepAliveMs === 0) {
3595
- return;
3596
- }
3668
+ if (keepAliveMs === 0) return;
3597
3669
  try {
3598
3670
  await withSandboxSpan(
3599
3671
  "sandbox.keepalive.extend",
3600
3672
  "sandbox.keepalive",
3601
- {
3602
- "app.sandbox.keepalive_ms": keepAliveMs
3603
- },
3673
+ { "app.sandbox.keepalive_ms": keepAliveMs },
3604
3674
  async () => {
3605
- await activeSandbox2.extendTimeout(keepAliveMs);
3675
+ await session.extendTimeout(keepAliveMs);
3606
3676
  }
3607
3677
  );
3608
3678
  } catch (error) {
3609
- if (isSandboxUnavailableError(error)) {
3610
- throw error;
3611
- }
3679
+ if (isSandboxUnavailableError(error)) throw error;
3612
3680
  }
3613
3681
  };
3614
3682
  const startKeepAlive = (session) => {
3615
3683
  const keepAliveMs = parseKeepAliveMs();
3616
- if (keepAliveMs === 0 || closed || keepAliveTimer) {
3617
- return;
3618
- }
3684
+ if (keepAliveMs === 0 || closed || keepAliveTimer) return;
3619
3685
  const intervalMs = Math.max(
3620
3686
  MIN_KEEPALIVE_INTERVAL_MS,
3621
3687
  Math.min(MAX_KEEPALIVE_INTERVAL_MS, Math.floor(keepAliveMs / 2))
@@ -3623,18 +3689,14 @@ function createSandboxRuntime(options) {
3623
3689
  const schedule = () => {
3624
3690
  keepAliveTimer = setTimeout(async () => {
3625
3691
  keepAliveTimer = void 0;
3626
- if (closed || activeSandbox?.session !== session) {
3627
- return;
3628
- }
3692
+ if (closed || activeSandbox?.session !== session) return;
3629
3693
  try {
3630
3694
  await extendKeepAlive(session);
3631
3695
  } catch {
3632
3696
  invalidateSession(session.sessionId);
3633
3697
  return;
3634
3698
  }
3635
- if (closed || activeSandbox?.session !== session) {
3636
- return;
3637
- }
3699
+ if (closed || activeSandbox?.session !== session) return;
3638
3700
  schedule();
3639
3701
  }, intervalMs);
3640
3702
  keepAliveTimer.unref?.();
@@ -3714,18 +3776,57 @@ function createSandboxRuntime(options) {
3714
3776
  };
3715
3777
  };
3716
3778
  const ensureReadySandbox = async (signal) => {
3717
- const activeSandbox2 = await getOrAcquireSandbox(signal);
3779
+ const session = await getOrAcquireSandbox(signal);
3718
3780
  signal?.throwIfAborted();
3719
- await probeSession(activeSandbox2);
3781
+ await probeSession(session);
3720
3782
  signal?.throwIfAborted();
3721
- await extendKeepAlive(activeSandbox2);
3722
- startKeepAlive(activeSandbox2);
3723
- return activeSandbox2;
3783
+ await extendKeepAlive(session);
3784
+ startKeepAlive(session);
3785
+ return session;
3724
3786
  };
3725
3787
  return {
3726
3788
  sandboxRef() {
3727
3789
  return sandboxRef ? { ...sandboxRef } : void 0;
3728
3790
  },
3791
+ async switchWorkspace(workspace, signal) {
3792
+ signal?.throwIfAborted();
3793
+ const nextProfileHash = hash(SANDBOX_RUNTIME, workspace);
3794
+ if (activeWorkspace?.id === workspace.id && dependencyProfileHash === nextProfileHash) {
3795
+ return;
3796
+ }
3797
+ if (acquiringSandbox) {
3798
+ await getOrAcquireSandbox(signal);
3799
+ }
3800
+ const previous = activeSandbox;
3801
+ let candidate;
3802
+ try {
3803
+ candidate = await createSandboxCandidate(
3804
+ workspace,
3805
+ nextProfileHash,
3806
+ signal
3807
+ );
3808
+ await probeSession(candidate.session);
3809
+ signal?.throwIfAborted();
3810
+ await extendKeepAlive(candidate.session);
3811
+ signal?.throwIfAborted();
3812
+ await persistSandboxRef(candidate.ref);
3813
+ } catch (error) {
3814
+ await stopSession(candidate?.session);
3815
+ throw error;
3816
+ }
3817
+ if (keepAliveTimer) {
3818
+ clearTimeout(keepAliveTimer);
3819
+ keepAliveTimer = void 0;
3820
+ }
3821
+ activeWorkspace = candidate.workspace;
3822
+ dependencyProfileHash = candidate.profileHash;
3823
+ activeSandbox = {
3824
+ session: candidate.session,
3825
+ networkPolicyKey: candidate.networkPolicyKey
3826
+ };
3827
+ startKeepAlive(candidate.session);
3828
+ await stopSession(previous?.session);
3829
+ },
3729
3830
  async acquire(signal) {
3730
3831
  return await getOrAcquireSandbox(signal);
3731
3832
  },
@@ -4965,8 +5066,10 @@ function createSandbox(options) {
4965
5066
  ...permissionDenied ? { permissionDenied } : {}
4966
5067
  };
4967
5068
  };
5069
+ let activeWorkspace = options.workspace;
4968
5070
  const runtime = createSandboxRuntime({
4969
5071
  sandboxRef: options.sandboxRef,
5072
+ workspace: options.workspace,
4970
5073
  skills: options.skills,
4971
5074
  referenceFiles: options.referenceFiles,
4972
5075
  timeoutMs: options.timeoutMs,
@@ -4980,6 +5083,7 @@ function createSandbox(options) {
4980
5083
  traceHeaders
4981
5084
  }) : void 0,
4982
5085
  onSandboxPrepare: options.prepare,
5086
+ onWorkspacePrepare: options.prepareWorkspace,
4983
5087
  onSandboxRefChanged: options.onSandboxRefChanged
4984
5088
  });
4985
5089
  const createToolCallContext = (signal, setToolCallSpanAttributes) => {
@@ -5422,7 +5526,9 @@ function createSandbox(options) {
5422
5526
  return void 0;
5423
5527
  }
5424
5528
  const { fs: fs5 } = await runtime.tools();
5425
- const selected = await findSingleRepositoryDirectory(fs5);
5529
+ const primary = activeWorkspace?.repos.find((repo) => repo.isPrimary);
5530
+ const primaryPath = primary ? tryWorkspaceRepoCheckoutPath(primary.repo) : void 0;
5531
+ const selected = primaryPath ? `${SANDBOX_WORKSPACE_ROOT}/${primaryPath}` : await findSingleRepositoryDirectory(fs5);
5426
5532
  if (!selected) return void 0;
5427
5533
  return await resolveRepositoryInstructions({
5428
5534
  cwd: selected,
@@ -5430,6 +5536,10 @@ function createSandbox(options) {
5430
5536
  });
5431
5537
  },
5432
5538
  workspace,
5539
+ async switchWorkspace(recipe, signal) {
5540
+ await runtime.switchWorkspace(recipe, signal);
5541
+ activeWorkspace = recipe;
5542
+ },
5433
5543
  tools: {
5434
5544
  supports(toolName) {
5435
5545
  return SANDBOX_TOOL_NAMES.has(toolName);
@@ -5851,6 +5961,7 @@ function bashCommand(input) {
5851
5961
  function createAgentSandbox(options) {
5852
5962
  const sandbox = createSandbox({
5853
5963
  sandboxRef: options.sandboxRef,
5964
+ workspace: options.workspace,
5854
5965
  skills: options.skills,
5855
5966
  referenceFiles: listReferenceFiles(),
5856
5967
  traceContext: options.traceContext,
@@ -5858,6 +5969,7 @@ function createAgentSandbox(options) {
5858
5969
  egressSignals: options.egressSignals,
5859
5970
  credentialEgress: options.credentialEgress,
5860
5971
  prepare: options.prepareSandbox,
5972
+ prepareWorkspace: options.prepareWorkspace,
5861
5973
  onSandboxRefChanged: async (sandboxRef) => {
5862
5974
  options.onSandboxRefChanged(sandboxRef);
5863
5975
  await options.persistSandboxRef?.(sandboxRef);
@@ -5867,6 +5979,7 @@ function createAgentSandbox(options) {
5867
5979
  captureRepositoryInstructions: sandbox.captureRepositoryInstructions,
5868
5980
  workspace: sandbox.workspace,
5869
5981
  sandboxRef: sandbox.sandboxRef,
5982
+ switchWorkspace: sandbox.switchWorkspace,
5870
5983
  close: sandbox.close,
5871
5984
  tools: {
5872
5985
  supports: sandbox.tools.supports,
@@ -13554,6 +13667,133 @@ function createWriteFileTool() {
13554
13667
  });
13555
13668
  }
13556
13669
 
13670
+ // src/chat/workspaces/tools.ts
13671
+ import { z as z63 } from "zod";
13672
+
13673
+ // src/chat/workspaces/store.ts
13674
+ import { asc, eq as eq3 } from "drizzle-orm";
13675
+ function workspaceFromRows(row, repos) {
13676
+ return {
13677
+ id: row.id,
13678
+ name: row.name,
13679
+ setupScript: row.setupScript,
13680
+ repos: repos.map((repo) => ({
13681
+ provider: repo.provider,
13682
+ repo: repo.repo,
13683
+ isPrimary: repo.isPrimary
13684
+ }))
13685
+ };
13686
+ }
13687
+ async function listWorkspaces(db) {
13688
+ const [workspaces, repos] = await Promise.all([
13689
+ db.select().from(juniorWorkspaces).orderBy(asc(juniorWorkspaces.name)),
13690
+ db.select().from(juniorWorkspaceRepos).orderBy(
13691
+ asc(juniorWorkspaceRepos.workspaceId),
13692
+ asc(juniorWorkspaceRepos.provider),
13693
+ asc(juniorWorkspaceRepos.repo)
13694
+ )
13695
+ ]);
13696
+ return workspaces.map(
13697
+ (workspace) => workspaceFromRows(
13698
+ workspace,
13699
+ repos.filter((repo) => repo.workspaceId === workspace.id)
13700
+ )
13701
+ );
13702
+ }
13703
+ async function getWorkspaceByName(db, name) {
13704
+ const rows = await db.select().from(juniorWorkspaces).where(eq3(juniorWorkspaces.name, name)).limit(1);
13705
+ const workspace = rows[0];
13706
+ if (!workspace) return void 0;
13707
+ const repos = await db.select().from(juniorWorkspaceRepos).where(eq3(juniorWorkspaceRepos.workspaceId, workspace.id)).orderBy(
13708
+ asc(juniorWorkspaceRepos.provider),
13709
+ asc(juniorWorkspaceRepos.repo)
13710
+ );
13711
+ return workspaceFromRows(workspace, repos);
13712
+ }
13713
+ async function getWorkspace(db, id) {
13714
+ const rows = await db.select().from(juniorWorkspaces).where(eq3(juniorWorkspaces.id, id)).limit(1);
13715
+ const workspace = rows[0];
13716
+ if (!workspace) return void 0;
13717
+ const repos = await db.select().from(juniorWorkspaceRepos).where(eq3(juniorWorkspaceRepos.workspaceId, workspace.id)).orderBy(
13718
+ asc(juniorWorkspaceRepos.provider),
13719
+ asc(juniorWorkspaceRepos.repo)
13720
+ );
13721
+ return workspaceFromRows(workspace, repos);
13722
+ }
13723
+
13724
+ // src/chat/workspaces/tools.ts
13725
+ var repoSchema = z63.object({
13726
+ provider: z63.string(),
13727
+ repo: z63.string(),
13728
+ checkout_path: z63.string(),
13729
+ is_primary: z63.boolean()
13730
+ });
13731
+ var workspaceSchema = z63.object({
13732
+ id: z63.string(),
13733
+ name: z63.string(),
13734
+ repos: z63.array(repoSchema)
13735
+ });
13736
+ function view(workspace) {
13737
+ return {
13738
+ id: workspace.id,
13739
+ name: workspace.name,
13740
+ repos: workspace.repos.map((repo) => ({
13741
+ provider: repo.provider,
13742
+ repo: repo.repo,
13743
+ checkout_path: workspaceRepoCheckoutPath(repo.repo),
13744
+ is_primary: repo.isPrimary
13745
+ }))
13746
+ };
13747
+ }
13748
+ function createWorkspaceTools(context) {
13749
+ if (!context.workspaces) return {};
13750
+ return {
13751
+ listWorkspaces: zodTool({
13752
+ annotations: {
13753
+ destructiveHint: false,
13754
+ idempotentHint: true,
13755
+ openWorldHint: false,
13756
+ readOnlyHint: true
13757
+ },
13758
+ description: "List named repository workspaces that can replace the current sandbox.",
13759
+ inputSchema: z63.object({}).strict(),
13760
+ outputSchema: juniorToolOutputSchema.extend({
13761
+ active_workspace_id: z63.string().nullable(),
13762
+ workspaces: z63.array(workspaceSchema)
13763
+ }),
13764
+ async execute() {
13765
+ return {
13766
+ active_workspace_id: context.workspaces.activeWorkspaceId() ?? null,
13767
+ workspaces: (await listWorkspaces(getDb())).map(view)
13768
+ };
13769
+ }
13770
+ }),
13771
+ switchWorkspace: zodTool({
13772
+ executionMode: "sequential",
13773
+ annotations: {
13774
+ destructiveHint: true,
13775
+ idempotentHint: true,
13776
+ openWorldHint: true,
13777
+ readOnlyHint: false
13778
+ },
13779
+ description: "Replace the current sandbox with a named preconfigured repository workspace. Files in the prior sandbox do not carry over.",
13780
+ inputSchema: z63.object({
13781
+ name: z63.string().trim().min(1).describe("Exact workspace name.")
13782
+ }).strict(),
13783
+ outputSchema: juniorToolOutputSchema.extend({
13784
+ workspace: workspaceSchema
13785
+ }),
13786
+ async execute({ name }, options) {
13787
+ const workspace = await getWorkspaceByName(getDb(), name);
13788
+ if (!workspace)
13789
+ throw new ToolInputError(`Workspace not found: ${name}`);
13790
+ await context.workspaces.switch(workspace, options.signal);
13791
+ return { workspace: view(workspace) };
13792
+ }
13793
+ })
13794
+ };
13795
+ }
13796
+
13557
13797
  // src/chat/tools/index.ts
13558
13798
  function createToolState() {
13559
13799
  const operationResultCache = /* @__PURE__ */ new Map();
@@ -13596,7 +13836,8 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
13596
13836
  }),
13597
13837
  ...createResourceEventTools(context, resourceEventCatalog),
13598
13838
  ...createEventTaskTools(context, resourceEventCatalog),
13599
- ...createScheduledTaskTools(context)
13839
+ ...createScheduledTaskTools(context),
13840
+ ...createWorkspaceTools(context)
13600
13841
  };
13601
13842
  if (context.conversationId) {
13602
13843
  tools.searchConversationEvents = createSearchConversationEventsTool(context);
@@ -14278,7 +14519,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
14278
14519
  }
14279
14520
 
14280
14521
  // src/chat/tools/execute-tool.ts
14281
- import { z as z63 } from "zod";
14522
+ import { z as z64 } from "zod";
14282
14523
  var EXECUTE_TOOL_NAME = "executeTool";
14283
14524
  function createExecuteToolTool() {
14284
14525
  return zodTool({
@@ -14290,11 +14531,11 @@ function createExecuteToolTool() {
14290
14531
  },
14291
14532
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
14292
14533
  executionMode: "sequential",
14293
- inputSchema: z63.object({
14294
- tool_name: z63.string().min(1).describe(
14534
+ inputSchema: z64.object({
14535
+ tool_name: z64.string().min(1).describe(
14295
14536
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
14296
14537
  ),
14297
- arguments: z63.record(z63.string(), z63.unknown()).describe(
14538
+ arguments: z64.record(z64.string(), z64.unknown()).describe(
14298
14539
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
14299
14540
  ).optional()
14300
14541
  }).strict(),
@@ -14334,7 +14575,7 @@ function planToolExposure(tools) {
14334
14575
  }
14335
14576
 
14336
14577
  // src/chat/tools/search-tools.ts
14337
- import { z as z64 } from "zod";
14578
+ import { z as z65 } from "zod";
14338
14579
  var SEARCH_TOOLS_NAME = "searchTools";
14339
14580
  var DEFAULT_MAX_RESULTS2 = 5;
14340
14581
  var MAX_RESULTS3 = 20;
@@ -14379,29 +14620,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
14379
14620
  "you",
14380
14621
  "your"
14381
14622
  ]);
14382
- var searchToolsSourceSchema = z64.object({
14383
- id: z64.string(),
14384
- description: z64.string()
14623
+ var searchToolsSourceSchema = z65.object({
14624
+ id: z65.string(),
14625
+ description: z65.string()
14385
14626
  }).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())
14627
+ var searchToolsToolSchema = z65.object({
14628
+ tool_name: z65.string(),
14629
+ description: z65.string(),
14630
+ exposure: z65.enum(["direct", "deferred", "modelOnly", "hidden"]),
14631
+ source: z65.string().optional(),
14632
+ input_schema: z65.unknown(),
14633
+ call_notes: z65.array(z65.string()),
14634
+ annotations: z65.record(z65.string(), z65.unknown())
14394
14635
  }).strict();
14395
14636
  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)
14637
+ query: z65.string().nullable(),
14638
+ source: z65.string().nullable(),
14639
+ sources: z65.array(searchToolsSourceSchema),
14640
+ total_catalog_tools: z65.number().int().nonnegative(),
14641
+ total_eligible_tools: z65.number().int().nonnegative(),
14642
+ total_matches: z65.number().int().nonnegative(),
14643
+ returned_tools: z65.number().int().nonnegative(),
14644
+ execution_tool: z65.literal("executeTool"),
14645
+ tools: z65.array(searchToolsToolSchema)
14405
14646
  }).strict();
14406
14647
  function terms(value) {
14407
14648
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -14612,14 +14853,14 @@ function createSearchToolsTool(catalogTools) {
14612
14853
  openWorldHint: false,
14613
14854
  readOnlyHint: true
14614
14855
  },
14615
- inputSchema: z64.object({
14616
- query: z64.string().nullable().describe(
14856
+ inputSchema: z65.object({
14857
+ query: z65.string().nullable().describe(
14617
14858
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
14618
14859
  ).optional(),
14619
- source: z64.string().nullable().describe(
14860
+ source: z65.string().nullable().describe(
14620
14861
  "Optional source id to search within, such as a plugin source returned in sources."
14621
14862
  ).optional(),
14622
- max_results: z64.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
14863
+ max_results: z65.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
14623
14864
  }).strict(),
14624
14865
  outputSchema: searchToolsOutputSchema,
14625
14866
  privateTraceResult: (result) => ({
@@ -15878,7 +16119,7 @@ function createPluginEgress(deps) {
15878
16119
  }
15879
16120
 
15880
16121
  // src/chat/services/guardian-action-review.ts
15881
- import { z as z65 } from "zod";
16122
+ import { z as z66 } from "zod";
15882
16123
 
15883
16124
  // src/chat/services/guardian-action-policy.ts
15884
16125
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -15946,17 +16187,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
15946
16187
  - 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
16188
 
15948
16189
  // 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"])
16190
+ var guardianDecisionSchema = z66.object({
16191
+ decision: z66.enum(["allow", "ask", "deny"]),
16192
+ reason: z66.string().trim().min(1).max(500),
16193
+ riskLevel: z66.enum(["low", "medium", "high", "critical"]),
16194
+ userAuthorization: z66.enum(["high", "medium", "low", "unknown"])
15954
16195
  }).strict();
15955
16196
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
15956
16197
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
15957
16198
  var MAX_PROPOSAL_CHARS = 192e3;
15958
16199
  function guardianPrompt(proposal) {
15959
- const proposalJson = JSON.stringify(z65.json().parse(proposal), null, 2);
16200
+ const proposalJson = JSON.stringify(z66.json().parse(proposal), null, 2);
15960
16201
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
15961
16202
  throw new Error(
15962
16203
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -16241,8 +16482,10 @@ async function wireAgentTools(args) {
16241
16482
  actor: args.currentActor,
16242
16483
  actors: args.currentActors
16243
16484
  });
16485
+ const workspace = args.state.sandboxRef?.workspaceId ? await getWorkspace(getDb(), args.state.sandboxRef.workspaceId) : void 0;
16244
16486
  const agentSandbox = createAgentSandbox({
16245
16487
  sandboxRef: args.state.sandboxRef,
16488
+ workspace,
16246
16489
  skills: args.availableSkills,
16247
16490
  traceContext: args.spanContext,
16248
16491
  tracePropagation: args.run.environment?.sandboxTracePropagation,
@@ -16253,6 +16496,7 @@ async function wireAgentTools(args) {
16253
16496
  configurationValues: args.configurationValues,
16254
16497
  getActiveSkill: () => args.skillSandbox.getActiveSkill(),
16255
16498
  prepareSandbox: pluginHooks.prepareSandbox,
16499
+ prepareWorkspace: async (sandbox, recipe, signal) => await pluginHooks.prepareWorkspace(sandbox, recipe.repos, signal),
16256
16500
  onSandboxRefChanged: args.onSandboxRefChanged,
16257
16501
  persistSandboxRef: args.durability.onSandboxRefChanged
16258
16502
  });
@@ -16356,7 +16600,11 @@ async function wireAgentTools(args) {
16356
16600
  const toolRuntimeContext = {
16357
16601
  ...commonToolRuntimeContext,
16358
16602
  ...toolRoute,
16359
- attachmentStorage: args.run.environment?.attachmentStorage
16603
+ attachmentStorage: args.run.environment?.attachmentStorage,
16604
+ workspaces: {
16605
+ activeWorkspaceId: () => agentSandbox.sandboxRef()?.workspaceId,
16606
+ switch: agentSandbox.switchWorkspace
16607
+ }
16360
16608
  };
16361
16609
  const actionReview = createToolActionReview({
16362
16610
  context: {