@sentry/junior 0.161.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 (96) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-hooks-O75UJNC6.js → agent-hooks-6IX5CV7G.js} +10 -10
  3. package/dist/api/acp/route.d.ts +13 -0
  4. package/dist/api/locations/detail.d.ts +1 -1
  5. package/dist/api/schema/conversation.d.ts +3 -0
  6. package/dist/api/schema/location.d.ts +1 -0
  7. package/dist/api/schema/person.d.ts +1 -0
  8. package/dist/api.js +204 -403
  9. package/dist/app.d.ts +2 -2
  10. package/dist/app.js +480 -38
  11. package/dist/{catalog-runtime-Z3FHU4ZY.js → catalog-runtime-NEPZNTOM.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/message-search.d.ts +6 -0
  20. package/dist/chat/conversations/turn-lifecycle.d.ts +1 -1
  21. package/dist/chat/experimental.d.ts +1 -1
  22. package/dist/chat/plugins/agent-hooks.d.ts +4 -0
  23. package/dist/chat/repository-instructions.d.ts +1 -1
  24. package/dist/chat/sandbox/paths.d.ts +1 -0
  25. package/dist/chat/sandbox/prepare-workspace.d.ts +11 -0
  26. package/dist/chat/sandbox/ref.d.ts +1 -0
  27. package/dist/chat/sandbox/sandbox.d.ts +4 -0
  28. package/dist/chat/sandbox/session.d.ts +4 -0
  29. package/dist/chat/sandbox/snapshot/profile.d.ts +4 -3
  30. package/dist/chat/sandbox/snapshot/resolve.d.ts +4 -0
  31. package/dist/chat/sandbox/workspace.d.ts +4 -0
  32. package/dist/chat/slack/tools/conversation-message-search.d.ts +9 -0
  33. package/dist/chat/tools/types.d.ts +5 -0
  34. package/dist/chat/workspaces/checkout-path.d.ts +4 -0
  35. package/dist/chat/workspaces/store.d.ts +8 -0
  36. package/dist/chat/workspaces/tools.d.ts +4 -0
  37. package/dist/chat/workspaces/types.d.ts +13 -0
  38. package/dist/{chunk-WKN4ZQTZ.js → chunk-2FBPN2Y4.js} +4 -1
  39. package/dist/{chunk-QHXT7LLF.js → chunk-2HBJQEBE.js} +1 -1
  40. package/dist/{chunk-VXFDVY4C.js → chunk-3OZNFCPR.js} +6 -6
  41. package/dist/{chunk-EAIQBR66.js → chunk-3T7RJVDJ.js} +1 -1
  42. package/dist/{chunk-DGKLYDX7.js → chunk-4P2XNVTO.js} +2 -2
  43. package/dist/{chunk-FLMD25Y5.js → chunk-ATWHPSMF.js} +40 -2
  44. package/dist/{chunk-IRHX2S46.js → chunk-BLZUFHM7.js} +1 -1
  45. package/dist/chunk-BVREIWBD.js +131 -0
  46. package/dist/{chunk-QQ7SY5IZ.js → chunk-EKGJVUVS.js} +9 -6
  47. package/dist/{chunk-ZBCLWBMV.js → chunk-FEIHINKR.js} +5 -5
  48. package/dist/{chunk-MVG5UPLM.js → chunk-GCLHNRMU.js} +1 -1
  49. package/dist/{chunk-YSWHAOPQ.js → chunk-ICJ3KCA2.js} +1 -1
  50. package/dist/{chunk-G35DUPNB.js → chunk-IIT23D3M.js} +4 -4
  51. package/dist/{chunk-OYQEO72I.js → chunk-IX4XI42N.js} +417 -136
  52. package/dist/{chunk-U2XOBFDK.js → chunk-JI5EPI3O.js} +50 -5
  53. package/dist/{chunk-IW7C5C7E.js → chunk-KD3ROG6P.js} +17 -17
  54. package/dist/{chunk-4WAZ6DGX.js → chunk-MWVQ35U6.js} +543 -164
  55. package/dist/{chunk-STZVUVMS.js → chunk-MXFL7LNZ.js} +12 -7
  56. package/dist/{chunk-HSFYTIS2.js → chunk-NL53P7PS.js} +1 -1
  57. package/dist/{chunk-6WTDZKFR.js → chunk-O4IK4O7C.js} +1 -1
  58. package/dist/{chunk-OPIJ6HY3.js → chunk-O5ZZL7IR.js} +9 -9
  59. package/dist/{chunk-CGAIDDLD.js → chunk-RK7KPI6G.js} +1 -1
  60. package/dist/{chunk-V67ORYKQ.js → chunk-RTPJRLOP.js} +9 -9
  61. package/dist/{chunk-6ILVZOF6.js → chunk-SR6WRBJW.js} +4 -4
  62. package/dist/{chunk-RULQD7OX.js → chunk-THS4CKN4.js} +46 -87
  63. package/dist/{chunk-IYFKONIL.js → chunk-TPDNZCZU.js} +1 -1
  64. package/dist/{chunk-DNSIU27S.js → chunk-TPT6EF7K.js} +1 -1
  65. package/dist/{chunk-5IDFPCON.js → chunk-VRS6WDK5.js} +1 -1
  66. package/dist/{chunk-O7JTSJPU.js → chunk-WPGYWDJR.js} +6 -6
  67. package/dist/{chunk-OY2DBUV4.js → chunk-XY2IVIAY.js} +5 -5
  68. package/dist/{chunk-RVENVPNO.js → chunk-YAUQXZVT.js} +2 -2
  69. package/dist/{chunk-WVSQ2SCS.js → chunk-ZDKTZG3G.js} +1 -1
  70. package/dist/{chunk-MNY5M7RB.js → chunk-ZF6IC27P.js} +87 -10
  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-KKOZRE62.js → db-6VBMHVW2.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-5AUZDWXA.js → oauth-callback-server-5NCCUIM2.js} +24 -24
  83. package/dist/{runner-K5H5NDEC.js → runner-6ONEB6TQ.js} +13 -13
  84. package/dist/{skills-OZGPB6G4.js → skills-Y5R36UH4.js} +6 -6
  85. package/dist/spawn-RFMBQ6B3.js +37 -0
  86. package/dist/{validation-TVHCNCYL.js → validation-HLHEQDEK.js} +6 -6
  87. package/dist/version.js +1 -1
  88. package/dist/{work-DXCQMJ5U.js → work-VIEFYTFG.js} +18 -18
  89. package/dist/worker-MRSKPKQZ.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 +8 -5
  94. package/dist/chunk-EM225ITJ.js +0 -56
  95. package/dist/spawn-RCQSC2L5.js +0 -37
  96. package/dist/worker-24HZ5HRI.js +0 -24
@@ -7,7 +7,7 @@ import {
7
7
  PluginCredentialFailureError,
8
8
  agentNameSchema,
9
9
  createPluginAuthOrchestration
10
- } from "./chunk-OY2DBUV4.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-V67ORYKQ.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-VXFDVY4C.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-RULQD7OX.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-ZBCLWBMV.js";
108
+ } from "./chunk-FEIHINKR.js";
111
109
  import {
112
110
  parseSlackThreadId
113
- } from "./chunk-YSWHAOPQ.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-STZVUVMS.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-MNY5M7RB.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-U2XOBFDK.js";
219
+ } from "./chunk-JI5EPI3O.js";
220
220
  import {
221
221
  juniorArtifacts,
222
- juniorIdentities
223
- } from "./chunk-FLMD25Y5.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-IYFKONIL.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-RVENVPNO.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-EAIQBR66.js";
284
+ } from "./chunk-3T7RJVDJ.js";
279
285
  import {
280
286
  discoverSkills,
281
287
  findSkillByName,
282
288
  loadSkillsByName,
283
289
  parseSkillInvocation
284
- } from "./chunk-DGKLYDX7.js";
290
+ } from "./chunk-4P2XNVTO.js";
285
291
  import {
286
292
  pluginCatalogRuntime
287
- } from "./chunk-CGAIDDLD.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-6WTDZKFR.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-DNSIU27S.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,
@@ -10008,8 +10121,12 @@ var CONVERSATIONS_TOOL_SOURCE = {
10008
10121
  };
10009
10122
 
10010
10123
  // src/chat/slack/tools/conversation-message-search.ts
10011
- var DEFAULT_LIMIT = 5;
10124
+ var DEFAULT_LIMIT = 20;
10125
+ var MAX_LIMIT = 50;
10012
10126
  var conversationMessageSearchOutputSchema = juniorToolOutputSchema.extend({
10127
+ after: z39.string().datetime().optional(),
10128
+ annotation: z39.string().min(1).optional(),
10129
+ before: z39.string().datetime().optional(),
10013
10130
  channel_id: z39.string().min(1).optional(),
10014
10131
  count: z39.number().int().nonnegative(),
10015
10132
  query: z39.string().optional(),
@@ -10027,8 +10144,21 @@ var conversationMessageSearchOutputSchema = juniorToolOutputSchema.extend({
10027
10144
  }).strict()
10028
10145
  )
10029
10146
  });
10147
+ function parseTimestamp(field, value) {
10148
+ if (value == null || value.trim() === "") {
10149
+ return void 0;
10150
+ }
10151
+ const parsed = Date.parse(value);
10152
+ if (!Number.isFinite(parsed)) {
10153
+ throw new ToolInputError(`${field} must be a valid ISO-8601 timestamp`);
10154
+ }
10155
+ return parsed;
10156
+ }
10030
10157
  async function resolveSearchFilters(input) {
10031
10158
  const query = input.query?.trim() || void 0;
10159
+ const annotation = input.annotation?.trim() || void 0;
10160
+ const afterMs = parseTimestamp("after", input.after);
10161
+ const beforeMs = parseTimestamp("before", input.before);
10032
10162
  let channelId;
10033
10163
  if (input.channel_id != null && input.channel_id.trim() !== "") {
10034
10164
  const target = await resolveSlackChannelRef({
@@ -10037,19 +10167,25 @@ async function resolveSearchFilters(input) {
10037
10167
  });
10038
10168
  channelId = target.channelId;
10039
10169
  }
10040
- if (!query && !channelId) {
10170
+ if (afterMs !== void 0 && beforeMs !== void 0 && afterMs >= beforeMs) {
10171
+ throw new ToolInputError("`after` must be earlier than `before`");
10172
+ }
10173
+ if (!query && !channelId && !annotation) {
10041
10174
  throw new ToolInputError(
10042
- "Provide at least one of `query` or `channel_id`."
10175
+ "Provide at least one of `query`, `channel_id`, or `annotation`."
10043
10176
  );
10044
10177
  }
10045
10178
  return {
10179
+ ...afterMs !== void 0 ? { afterMs } : {},
10180
+ ...annotation ? { annotation } : {},
10181
+ ...beforeMs !== void 0 ? { beforeMs } : {},
10046
10182
  ...channelId ? { channelId } : {},
10047
10183
  ...query ? { query } : {}
10048
10184
  };
10049
10185
  }
10050
10186
  function createSlackConversationMessageSearchTool(scope, currentConversationId) {
10051
10187
  return zodTool({
10052
- description: "Search retained user and assistant messages from public conversations in this Slack workspace. Excludes the current conversation. Not live Slack workspace search.",
10188
+ description: "Search retained user and assistant messages from public conversations in this Slack workspace. Filter by message text, channel, time range, or linked annotation such as a resource key. Excludes the current conversation. Not live Slack workspace search.",
10053
10189
  exposure: "deferred",
10054
10190
  source: CONVERSATIONS_TOOL_SOURCE,
10055
10191
  annotations: {
@@ -10059,9 +10195,16 @@ function createSlackConversationMessageSearchTool(scope, currentConversationId)
10059
10195
  readOnlyHint: true
10060
10196
  },
10061
10197
  inputSchema: z39.object({
10198
+ after: z39.string().datetime().nullable().describe("Include conversations active at or after this timestamp.").optional(),
10199
+ annotation: z39.string().trim().min(1).max(256).nullable().describe(
10200
+ "Linked resource key. Matches that key, or nested children that continue with #."
10201
+ ).optional(),
10202
+ before: z39.string().datetime().nullable().describe("Include conversations active before this timestamp.").optional(),
10062
10203
  channel_id: slackChannelRefParam.nullable().optional(),
10063
10204
  query: z39.string().trim().min(1).max(200).nullable().describe("Words or phrase to match in message text.").optional(),
10064
- limit: z39.number().int().min(1).max(10).describe(`Maximum matches. Default ${DEFAULT_LIMIT}; max 10.`).nullable().optional()
10205
+ limit: z39.number().int().min(1).max(MAX_LIMIT).describe(
10206
+ `Maximum conversations. Default ${DEFAULT_LIMIT}; max ${MAX_LIMIT}.`
10207
+ ).nullable().optional()
10065
10208
  }).strict(),
10066
10209
  outputSchema: conversationMessageSearchOutputSchema,
10067
10210
  execute: async (input) => {
@@ -10099,6 +10242,9 @@ function createSlackConversationMessageSearchTool(scope, currentConversationId)
10099
10242
  })
10100
10243
  );
10101
10244
  return {
10245
+ ...filters.afterMs !== void 0 ? { after: new Date(filters.afterMs).toISOString() } : {},
10246
+ ...filters.annotation ? { annotation: filters.annotation } : {},
10247
+ ...filters.beforeMs !== void 0 ? { before: new Date(filters.beforeMs).toISOString() } : {},
10102
10248
  ...filters.query ? { query: filters.query } : {},
10103
10249
  ...filters.channelId ? { channel_id: filters.channelId } : {},
10104
10250
  count: matchesOutput.length,
@@ -12597,7 +12743,7 @@ function createUnpublishImageTool(args) {
12597
12743
  // src/chat/tools/search-conversation-events.ts
12598
12744
  import { z as z58 } from "zod";
12599
12745
  var DEFAULT_LIMIT3 = 25;
12600
- var MAX_LIMIT = 100;
12746
+ var MAX_LIMIT2 = 100;
12601
12747
  var MAX_EVENT_DATA_BYTES = 4e3;
12602
12748
  var MAX_EVENTS_JSON_BYTES = 2e4;
12603
12749
  var MAX_IDENTITY_CHARS = 256;
@@ -12634,8 +12780,8 @@ function createSearchConversationEventsTool(context) {
12634
12780
  ).optional(),
12635
12781
  after_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
12636
12782
  before_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
12637
- limit: z58.number().int().min(1).max(MAX_LIMIT).nullable().describe(
12638
- `Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT}.`
12783
+ limit: z58.number().int().min(1).max(MAX_LIMIT2).nullable().describe(
12784
+ `Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT2}.`
12639
12785
  ).optional(),
12640
12786
  types: z58.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
12641
12787
  }).strict(),
@@ -13521,6 +13667,133 @@ function createWriteFileTool() {
13521
13667
  });
13522
13668
  }
13523
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
+
13524
13797
  // src/chat/tools/index.ts
13525
13798
  function createToolState() {
13526
13799
  const operationResultCache = /* @__PURE__ */ new Map();
@@ -13563,7 +13836,8 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
13563
13836
  }),
13564
13837
  ...createResourceEventTools(context, resourceEventCatalog),
13565
13838
  ...createEventTaskTools(context, resourceEventCatalog),
13566
- ...createScheduledTaskTools(context)
13839
+ ...createScheduledTaskTools(context),
13840
+ ...createWorkspaceTools(context)
13567
13841
  };
13568
13842
  if (context.conversationId) {
13569
13843
  tools.searchConversationEvents = createSearchConversationEventsTool(context);
@@ -14245,7 +14519,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
14245
14519
  }
14246
14520
 
14247
14521
  // src/chat/tools/execute-tool.ts
14248
- import { z as z63 } from "zod";
14522
+ import { z as z64 } from "zod";
14249
14523
  var EXECUTE_TOOL_NAME = "executeTool";
14250
14524
  function createExecuteToolTool() {
14251
14525
  return zodTool({
@@ -14257,11 +14531,11 @@ function createExecuteToolTool() {
14257
14531
  },
14258
14532
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
14259
14533
  executionMode: "sequential",
14260
- inputSchema: z63.object({
14261
- tool_name: z63.string().min(1).describe(
14534
+ inputSchema: z64.object({
14535
+ tool_name: z64.string().min(1).describe(
14262
14536
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
14263
14537
  ),
14264
- arguments: z63.record(z63.string(), z63.unknown()).describe(
14538
+ arguments: z64.record(z64.string(), z64.unknown()).describe(
14265
14539
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
14266
14540
  ).optional()
14267
14541
  }).strict(),
@@ -14301,7 +14575,7 @@ function planToolExposure(tools) {
14301
14575
  }
14302
14576
 
14303
14577
  // src/chat/tools/search-tools.ts
14304
- import { z as z64 } from "zod";
14578
+ import { z as z65 } from "zod";
14305
14579
  var SEARCH_TOOLS_NAME = "searchTools";
14306
14580
  var DEFAULT_MAX_RESULTS2 = 5;
14307
14581
  var MAX_RESULTS3 = 20;
@@ -14346,29 +14620,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
14346
14620
  "you",
14347
14621
  "your"
14348
14622
  ]);
14349
- var searchToolsSourceSchema = z64.object({
14350
- id: z64.string(),
14351
- description: z64.string()
14623
+ var searchToolsSourceSchema = z65.object({
14624
+ id: z65.string(),
14625
+ description: z65.string()
14352
14626
  }).strict();
14353
- var searchToolsToolSchema = z64.object({
14354
- tool_name: z64.string(),
14355
- description: z64.string(),
14356
- exposure: z64.enum(["direct", "deferred", "modelOnly", "hidden"]),
14357
- source: z64.string().optional(),
14358
- input_schema: z64.unknown(),
14359
- call_notes: z64.array(z64.string()),
14360
- 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())
14361
14635
  }).strict();
14362
14636
  var searchToolsOutputSchema = juniorToolOutputSchema.extend({
14363
- query: z64.string().nullable(),
14364
- source: z64.string().nullable(),
14365
- sources: z64.array(searchToolsSourceSchema),
14366
- total_catalog_tools: z64.number().int().nonnegative(),
14367
- total_eligible_tools: z64.number().int().nonnegative(),
14368
- total_matches: z64.number().int().nonnegative(),
14369
- returned_tools: z64.number().int().nonnegative(),
14370
- execution_tool: z64.literal("executeTool"),
14371
- 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)
14372
14646
  }).strict();
14373
14647
  function terms(value) {
14374
14648
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -14579,14 +14853,14 @@ function createSearchToolsTool(catalogTools) {
14579
14853
  openWorldHint: false,
14580
14854
  readOnlyHint: true
14581
14855
  },
14582
- inputSchema: z64.object({
14583
- query: z64.string().nullable().describe(
14856
+ inputSchema: z65.object({
14857
+ query: z65.string().nullable().describe(
14584
14858
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
14585
14859
  ).optional(),
14586
- source: z64.string().nullable().describe(
14860
+ source: z65.string().nullable().describe(
14587
14861
  "Optional source id to search within, such as a plugin source returned in sources."
14588
14862
  ).optional(),
14589
- 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()
14590
14864
  }).strict(),
14591
14865
  outputSchema: searchToolsOutputSchema,
14592
14866
  privateTraceResult: (result) => ({
@@ -15845,7 +16119,7 @@ function createPluginEgress(deps) {
15845
16119
  }
15846
16120
 
15847
16121
  // src/chat/services/guardian-action-review.ts
15848
- import { z as z65 } from "zod";
16122
+ import { z as z66 } from "zod";
15849
16123
 
15850
16124
  // src/chat/services/guardian-action-policy.ts
15851
16125
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -15913,17 +16187,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
15913
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.`;
15914
16188
 
15915
16189
  // src/chat/services/guardian-action-review.ts
15916
- var guardianDecisionSchema = z65.object({
15917
- decision: z65.enum(["allow", "ask", "deny"]),
15918
- reason: z65.string().trim().min(1).max(500),
15919
- riskLevel: z65.enum(["low", "medium", "high", "critical"]),
15920
- 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"])
15921
16195
  }).strict();
15922
16196
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
15923
16197
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
15924
16198
  var MAX_PROPOSAL_CHARS = 192e3;
15925
16199
  function guardianPrompt(proposal) {
15926
- const proposalJson = JSON.stringify(z65.json().parse(proposal), null, 2);
16200
+ const proposalJson = JSON.stringify(z66.json().parse(proposal), null, 2);
15927
16201
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
15928
16202
  throw new Error(
15929
16203
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -16208,8 +16482,10 @@ async function wireAgentTools(args) {
16208
16482
  actor: args.currentActor,
16209
16483
  actors: args.currentActors
16210
16484
  });
16485
+ const workspace = args.state.sandboxRef?.workspaceId ? await getWorkspace(getDb(), args.state.sandboxRef.workspaceId) : void 0;
16211
16486
  const agentSandbox = createAgentSandbox({
16212
16487
  sandboxRef: args.state.sandboxRef,
16488
+ workspace,
16213
16489
  skills: args.availableSkills,
16214
16490
  traceContext: args.spanContext,
16215
16491
  tracePropagation: args.run.environment?.sandboxTracePropagation,
@@ -16220,6 +16496,7 @@ async function wireAgentTools(args) {
16220
16496
  configurationValues: args.configurationValues,
16221
16497
  getActiveSkill: () => args.skillSandbox.getActiveSkill(),
16222
16498
  prepareSandbox: pluginHooks.prepareSandbox,
16499
+ prepareWorkspace: async (sandbox, recipe, signal) => await pluginHooks.prepareWorkspace(sandbox, recipe.repos, signal),
16223
16500
  onSandboxRefChanged: args.onSandboxRefChanged,
16224
16501
  persistSandboxRef: args.durability.onSandboxRefChanged
16225
16502
  });
@@ -16323,7 +16600,11 @@ async function wireAgentTools(args) {
16323
16600
  const toolRuntimeContext = {
16324
16601
  ...commonToolRuntimeContext,
16325
16602
  ...toolRoute,
16326
- attachmentStorage: args.run.environment?.attachmentStorage
16603
+ attachmentStorage: args.run.environment?.attachmentStorage,
16604
+ workspaces: {
16605
+ activeWorkspaceId: () => agentSandbox.sandboxRef()?.workspaceId,
16606
+ switch: agentSandbox.switchWorkspace
16607
+ }
16327
16608
  };
16328
16609
  const actionReview = createToolActionReview({
16329
16610
  context: {