@sentry/junior 0.116.1 → 0.118.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 (93) hide show
  1. package/dist/{agent-hooks-GUAAS3XH.js → agent-hooks-J2FB7XD6.js} +8 -10
  2. package/dist/api/conversations/detail.d.ts +7 -0
  3. package/dist/api/conversations/event-list.d.ts +7 -0
  4. package/dist/api/conversations/events.d.ts +1 -1
  5. package/dist/api/schema/conversation.d.ts +28 -0
  6. package/dist/api/schema/stats.d.ts +22 -0
  7. package/dist/api/schema.d.ts +2 -0
  8. package/dist/api/schema.js +7 -3
  9. package/dist/api/stats.d.ts +16 -0
  10. package/dist/api/user-pages/routes.d.ts +4 -0
  11. package/dist/api.js +192 -18
  12. package/dist/app.js +53 -46
  13. package/dist/{catalog-runtime-WMPC4J4U.js → catalog-runtime-UHHLU53I.js} +5 -5
  14. package/dist/chat/agent/prompt.d.ts +6 -2
  15. package/dist/chat/agent/request.d.ts +2 -1
  16. package/dist/chat/agent/resume.d.ts +2 -0
  17. package/dist/chat/agent/tools.d.ts +1 -0
  18. package/dist/chat/conversations/history.d.ts +22 -1
  19. package/dist/chat/conversations/projection.d.ts +6 -0
  20. package/dist/chat/mcp/client.d.ts +4 -0
  21. package/dist/chat/mcp/errors.d.ts +22 -1
  22. package/dist/chat/oauth-response.d.ts +21 -0
  23. package/dist/chat/plugins/prompt.d.ts +9 -0
  24. package/dist/chat/plugins/types.d.ts +2 -21
  25. package/dist/chat/plugins/user-pages.d.ts +15 -0
  26. package/dist/chat/plugins/viewer-actors.d.ts +6 -0
  27. package/dist/chat/prompt.d.ts +1 -2
  28. package/dist/chat/sandbox/paths.d.ts +2 -0
  29. package/dist/chat/sandbox/sandbox.d.ts +3 -1
  30. package/dist/chat/sandbox/snapshot/install.d.ts +6 -0
  31. package/dist/chat/sandbox/snapshot/profile.d.ts +14 -0
  32. package/dist/chat/sandbox/snapshot/resolve.d.ts +22 -0
  33. package/dist/chat/sandbox/snapshot/span.d.ts +2 -0
  34. package/dist/chat/sandbox/workspace.d.ts +1 -0
  35. package/dist/chat/services/context-compaction-marker.d.ts +1 -0
  36. package/dist/chat/services/turn-session-record.d.ts +2 -0
  37. package/dist/chat/skills.d.ts +1 -2
  38. package/dist/chat/state/turn-session.d.ts +2 -0
  39. package/dist/chat/tools/index.d.ts +4 -1
  40. package/dist/chat/tools/query-conversation-events.d.ts +1 -1
  41. package/dist/chat/tools/sandbox/file-utils.d.ts +11 -0
  42. package/dist/chat/tools/sandbox/find-files.d.ts +2 -1
  43. package/dist/chat/tools/sandbox/grep.d.ts +2 -1
  44. package/dist/chat/tools/sandbox/view-image.d.ts +11 -0
  45. package/dist/chat/tools/types.d.ts +7 -0
  46. package/dist/{chunk-XA3VLFPK.js → chunk-3LGCVRBQ.js} +154 -20
  47. package/dist/{chunk-JPKGW6L6.js → chunk-3MUSSFMP.js} +78 -39
  48. package/dist/{chunk-7CSVG54J.js → chunk-3N3FMCKN.js} +30 -2
  49. package/dist/{chunk-VBMMMVVQ.js → chunk-4LPPH7Y6.js} +405 -446
  50. package/dist/{chunk-2YYPXQNQ.js → chunk-4SOQPUF4.js} +1 -1
  51. package/dist/{chunk-H35BDAUS.js → chunk-6BGUAOYP.js} +8 -8
  52. package/dist/{chunk-GDAXWEIJ.js → chunk-6K2QE2UI.js} +731 -424
  53. package/dist/{chunk-DLLW5ITL.js → chunk-7GADTCNS.js} +30 -9
  54. package/dist/{chunk-TC4ZEM2D.js → chunk-7SM3P4XR.js} +28 -1
  55. package/dist/{chunk-WKU7TY22.js → chunk-FIANEE7Z.js} +3 -1
  56. package/dist/{chunk-RXRQJF7Y.js → chunk-GCGJGSQL.js} +21 -0
  57. package/dist/{chunk-ZYHLNTC2.js → chunk-GT2ZUALJ.js} +584 -25
  58. package/dist/{chunk-KNCELO2M.js → chunk-HN6LFYG3.js} +1 -1
  59. package/dist/{chunk-N3C4XCRE.js → chunk-HWUBBHLK.js} +1 -1
  60. package/dist/{chunk-BFYVIOTS.js → chunk-KC5EE27Y.js} +13 -3
  61. package/dist/chunk-L72GDKSN.js +37 -0
  62. package/dist/{chunk-F3SWGSPG.js → chunk-MLZFK4KP.js} +1 -1
  63. package/dist/{chunk-RYFAVWP3.js → chunk-PJPKHGLG.js} +34 -15
  64. package/dist/{chunk-CY7NXM32.js → chunk-PWRFTDTY.js} +4 -2
  65. package/dist/{chunk-WLT72MRP.js → chunk-QWCF4RAC.js} +1 -1
  66. package/dist/{chunk-CGXLCMWI.js → chunk-S6R23MFB.js} +117 -17
  67. package/dist/{chunk-OY2PQQ4P.js → chunk-UXLYAVYL.js} +1 -1
  68. package/dist/cli/chat.js +23 -24
  69. package/dist/cli/check.js +6 -6
  70. package/dist/cli/main.js +1 -1
  71. package/dist/cli/plugins.js +12 -14
  72. package/dist/cli/{snapshot-warmup.js → snapshot-create.js} +17 -20
  73. package/dist/cli/upgrade.js +7 -7
  74. package/dist/db/schema/stats.d.ts +93 -0
  75. package/dist/db/schema.d.ts +94 -1
  76. package/dist/{db-FH4R5ZQ4.js → db-PMRHFSYL.js} +6 -6
  77. package/dist/handlers/oauth-html.d.ts +4 -2
  78. package/dist/instrumentation.js +2 -2
  79. package/dist/nitro.js +3 -3
  80. package/dist/{oauth-callback-server-C2SSJ3FQ.js → oauth-callback-server-QJUC7S4X.js} +15 -17
  81. package/dist/{plugins-IUJA4VBU.js → plugins-HW4VWW6G.js} +1 -1
  82. package/dist/{runner-NAALFIXM.js → runner-PZMV6JQW.js} +10 -12
  83. package/dist/{skills-UN2JXYYF.js → skills-W4JETYSV.js} +6 -6
  84. package/dist/stats.d.ts +18 -0
  85. package/dist/{validation-EEF4ROHR.js → validation-IJLNHZSU.js} +6 -6
  86. package/dist/version.js +1 -1
  87. package/migrations/0011_sour_golden_guardian.sql +8 -0
  88. package/migrations/meta/0011_snapshot.json +1195 -0
  89. package/migrations/meta/_journal.json +8 -1
  90. package/package.json +5 -5
  91. package/dist/chat/sandbox/runtime-dependency-snapshots.d.ts +0 -20
  92. package/dist/chunk-4ZNGQH7C.js +0 -25
  93. package/dist/chunk-FAEALXEH.js +0 -517
@@ -1,31 +1,136 @@
1
1
  import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
- getStateAdapter
4
- } from "./chunk-7CSVG54J.js";
5
- import {
3
+ getStateAdapter,
6
4
  sleep
7
- } from "./chunk-4ZNGQH7C.js";
5
+ } from "./chunk-3N3FMCKN.js";
8
6
  import {
9
7
  toOptionalTrimmed
10
- } from "./chunk-CY7NXM32.js";
8
+ } from "./chunk-PWRFTDTY.js";
11
9
  import {
12
10
  pluginCatalogRuntime
13
- } from "./chunk-OY2PQQ4P.js";
11
+ } from "./chunk-UXLYAVYL.js";
14
12
  import {
15
13
  setSpanAttributes,
16
14
  setSpanStatus,
17
15
  withSpan
18
- } from "./chunk-F3SWGSPG.js";
16
+ } from "./chunk-MLZFK4KP.js";
17
+
18
+ // src/chat/sandbox/snapshot/profile.ts
19
+ import { createHash } from "crypto";
19
20
 
20
21
  // src/chat/sandbox/runtime-dependencies.ts
21
22
  var GLOBAL_RUNTIME_DEPENDENCIES = [
22
23
  { type: "system", package: "docker" },
24
+ { type: "system", package: "spal-release" },
23
25
  { type: "system", package: "ripgrep" }
24
26
  ];
25
27
 
26
- // src/chat/sandbox/runtime-dependency-snapshots.ts
27
- import { createHash } from "crypto";
28
+ // src/chat/sandbox/snapshot/profile.ts
29
+ var VERSION = 1;
30
+ var DEFAULT_FLOATING_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
31
+ function isExactNpmVersion(version) {
32
+ return /^\d+\.\d+\.\d+(?:[-+][a-z0-9.]+)?$/i.test(version.trim());
33
+ }
34
+ function isFloating(dep) {
35
+ return dep.type === "npm" && !isExactNpmVersion(dep.version);
36
+ }
37
+ function mergeDependencies(dependencies2) {
38
+ const seen = /* @__PURE__ */ new Set();
39
+ const npmVersions = /* @__PURE__ */ new Map();
40
+ const merged = [];
41
+ for (const dependency of dependencies2) {
42
+ const key = dependency.type === "npm" ? `${dependency.type}:${dependency.package}:${dependency.version}` : "package" in dependency ? `${dependency.type}:package:${dependency.package}` : `${dependency.type}:url:${dependency.url}:${dependency.sha256}`;
43
+ if (seen.has(key)) {
44
+ continue;
45
+ }
46
+ seen.add(key);
47
+ if (dependency.type === "npm") {
48
+ const version = npmVersions.get(dependency.package);
49
+ if (version !== void 0 && version !== dependency.version) {
50
+ throw new Error(
51
+ `Conflicting runtime dependency versions for ${dependency.package}: ${version} and ${dependency.version}`
52
+ );
53
+ }
54
+ npmVersions.set(dependency.package, dependency.version);
55
+ }
56
+ merged.push(dependency);
57
+ }
58
+ return merged;
59
+ }
60
+ function floatingMaxAgeMs() {
61
+ const raw = process.env.SANDBOX_SNAPSHOT_FLOATING_MAX_AGE_MS;
62
+ if (!raw?.trim()) {
63
+ return DEFAULT_FLOATING_MAX_AGE_MS;
64
+ }
65
+ const parsed = Number.parseInt(raw, 10);
66
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_FLOATING_MAX_AGE_MS;
67
+ }
68
+ function create(runtime) {
69
+ const dependencies2 = mergeDependencies([
70
+ ...GLOBAL_RUNTIME_DEPENDENCIES,
71
+ ...pluginCatalogRuntime.getRuntimeDependencies()
72
+ ]);
73
+ const postinstall2 = pluginCatalogRuntime.getRuntimePostinstall();
74
+ if (dependencies2.length === 0 && postinstall2.length === 0) {
75
+ return null;
76
+ }
77
+ const rebuildEpoch = process.env.SANDBOX_SNAPSHOT_REBUILD_EPOCH?.trim() ?? "";
78
+ const floating = dependencies2.some((dependency) => isFloating(dependency)) || postinstall2.length > 0;
79
+ const hash2 = createHash("sha256").update(
80
+ JSON.stringify({
81
+ version: VERSION,
82
+ runtime,
83
+ rebuildEpoch,
84
+ dependencies: dependencies2,
85
+ postinstall: postinstall2
86
+ })
87
+ ).digest("hex");
88
+ return {
89
+ hash: hash2,
90
+ dependencyCount: dependencies2.length,
91
+ floating,
92
+ dependencies: dependencies2,
93
+ postinstall: postinstall2
94
+ };
95
+ }
96
+ function hash(runtime) {
97
+ return create(runtime)?.hash;
98
+ }
99
+ function isStale(profile, createdAtMs) {
100
+ if (!profile.floating) {
101
+ return false;
102
+ }
103
+ const maxAgeMs = floatingMaxAgeMs();
104
+ return maxAgeMs === 0 || Date.now() - createdAtMs > maxAgeMs;
105
+ }
106
+
107
+ // src/chat/sandbox/snapshot/resolve.ts
28
108
  import { Sandbox } from "@vercel/sandbox";
109
+ import { z } from "zod";
110
+
111
+ // src/chat/sandbox/credentials.ts
112
+ function getVercelSandboxCredentials() {
113
+ const token = toOptionalTrimmed(process.env.VERCEL_TOKEN);
114
+ const teamId = toOptionalTrimmed(process.env.VERCEL_TEAM_ID);
115
+ const projectId = toOptionalTrimmed(process.env.VERCEL_PROJECT_ID);
116
+ if (token && teamId && projectId) {
117
+ return { token, teamId, projectId };
118
+ }
119
+ return void 0;
120
+ }
121
+
122
+ // src/chat/sandbox/resources.ts
123
+ function getSandboxResources() {
124
+ const value = process.env.SANDBOX_VCPUS?.trim();
125
+ if (!value || !/^\d+$/.test(value)) {
126
+ return void 0;
127
+ }
128
+ const vcpus = Number(value);
129
+ if (!Number.isSafeInteger(vcpus) || vcpus <= 0) {
130
+ return void 0;
131
+ }
132
+ return { vcpus };
133
+ }
29
134
 
30
135
  // src/chat/sandbox/noninteractive-command.ts
31
136
  var NON_INTERACTIVE_ENV = {
@@ -96,15 +201,215 @@ async function runNonInteractiveCommand(sandbox, input) {
96
201
  return await sandbox.runCommand(command);
97
202
  }
98
203
 
99
- // src/chat/sandbox/credentials.ts
100
- function getVercelSandboxCredentials() {
101
- const token = toOptionalTrimmed(process.env.VERCEL_TOKEN);
102
- const teamId = toOptionalTrimmed(process.env.VERCEL_TEAM_ID);
103
- const projectId = toOptionalTrimmed(process.env.VERCEL_PROJECT_ID);
104
- if (token && teamId && projectId) {
105
- return { token, teamId, projectId };
204
+ // src/chat/sandbox/snapshot/span.ts
205
+ async function trace(name, op, attributes, callback) {
206
+ return await withSpan(name, op, {}, callback, attributes);
207
+ }
208
+
209
+ // src/chat/sandbox/snapshot/install.ts
210
+ async function runOrThrow(sandbox, params, label) {
211
+ const result = await runNonInteractiveCommand(sandbox, params);
212
+ if (result.exitCode === 0) {
213
+ return;
106
214
  }
107
- return void 0;
215
+ const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
216
+ throw new Error(`${label} failed: ${detail}`);
217
+ }
218
+ async function tryRun(sandbox, params) {
219
+ const result = await runNonInteractiveCommand(sandbox, params);
220
+ return result.exitCode === 0;
221
+ }
222
+ async function installGh(sandbox) {
223
+ const installed = await tryRun(sandbox, {
224
+ cmd: "dnf",
225
+ args: ["install", "-y", "gh"],
226
+ sudo: true
227
+ });
228
+ if (installed) {
229
+ return;
230
+ }
231
+ const repoAdded = await tryRun(sandbox, {
232
+ cmd: "dnf",
233
+ args: [
234
+ "config-manager",
235
+ "addrepo",
236
+ "--from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo"
237
+ ],
238
+ sudo: true
239
+ });
240
+ if (!repoAdded) {
241
+ await runOrThrow(
242
+ sandbox,
243
+ {
244
+ cmd: "dnf",
245
+ args: ["install", "-y", "dnf-command(config-manager)"],
246
+ sudo: true
247
+ },
248
+ "dnf install dnf-command(config-manager)"
249
+ );
250
+ await runOrThrow(
251
+ sandbox,
252
+ {
253
+ cmd: "dnf",
254
+ args: [
255
+ "config-manager",
256
+ "--add-repo",
257
+ "https://cli.github.com/packages/rpm/gh-cli.repo"
258
+ ],
259
+ sudo: true
260
+ },
261
+ "dnf config-manager --add-repo gh-cli.repo"
262
+ );
263
+ }
264
+ await runOrThrow(
265
+ sandbox,
266
+ {
267
+ cmd: "dnf",
268
+ args: ["install", "-y", "gh", "--repo", "gh-cli"],
269
+ sudo: true
270
+ },
271
+ "dnf install gh --repo gh-cli"
272
+ );
273
+ }
274
+ function filePath(url, sha256) {
275
+ let basename = "package.rpm";
276
+ try {
277
+ const candidate = new URL(url).pathname.split("/").filter(Boolean).at(-1);
278
+ if (candidate) {
279
+ basename = candidate;
280
+ }
281
+ } catch {
282
+ }
283
+ const sanitized = basename.replace(/[^a-zA-Z0-9._-]/g, "_");
284
+ return `/tmp/junior-runtime-${sha256.slice(0, 12)}-${sanitized}`;
285
+ }
286
+ async function installUrl(sandbox, dependency) {
287
+ const rpmPath = filePath(dependency.url, dependency.sha256);
288
+ await runOrThrow(
289
+ sandbox,
290
+ {
291
+ cmd: "curl",
292
+ args: ["-fsSL", dependency.url, "-o", rpmPath]
293
+ },
294
+ `curl download ${dependency.url}`
295
+ );
296
+ const checksum = await runNonInteractiveCommand(sandbox, {
297
+ cmd: "sha256sum",
298
+ args: [rpmPath]
299
+ });
300
+ const expected = dependency.sha256;
301
+ const actual = checksum.stdout.trim().split(/\s+/)[0]?.toLowerCase();
302
+ if (checksum.exitCode !== 0) {
303
+ const detail = checksum.stderr.trim() || checksum.stdout.trim() || "command failed";
304
+ throw new Error(`sha256sum failed: ${detail}`);
305
+ }
306
+ if (!actual) {
307
+ throw new Error("sha256sum produced empty output");
308
+ }
309
+ if (actual !== expected) {
310
+ throw new Error(
311
+ `checksum mismatch for ${dependency.url}: expected ${expected}, got ${actual}`
312
+ );
313
+ }
314
+ await runOrThrow(
315
+ sandbox,
316
+ {
317
+ cmd: "dnf",
318
+ args: ["install", "-y", rpmPath],
319
+ sudo: true
320
+ },
321
+ `dnf install ${dependency.url}`
322
+ );
323
+ }
324
+ async function dependencies(sandbox, values) {
325
+ const system = values.filter(
326
+ (dependency) => dependency.type === "system"
327
+ );
328
+ const npm = values.filter(
329
+ (dependency) => dependency.type === "npm"
330
+ ).map((dependency) => `${dependency.package}@${dependency.version}`);
331
+ if (system.length > 0) {
332
+ await trace(
333
+ "sandbox.snapshot.install_system",
334
+ "sandbox.snapshot.install.system",
335
+ {
336
+ "app.sandbox.snapshot.install.system_count": system.length
337
+ },
338
+ async () => {
339
+ for (const dependency of system) {
340
+ if ("url" in dependency) {
341
+ await installUrl(sandbox, dependency);
342
+ } else if (dependency.package === "gh") {
343
+ await installGh(sandbox);
344
+ } else {
345
+ await runOrThrow(
346
+ sandbox,
347
+ {
348
+ cmd: "dnf",
349
+ args: ["install", "-y", dependency.package],
350
+ sudo: true
351
+ },
352
+ `dnf install ${dependency.package}`
353
+ );
354
+ }
355
+ }
356
+ }
357
+ );
358
+ }
359
+ if (npm.length > 0) {
360
+ await trace(
361
+ "sandbox.snapshot.install_npm",
362
+ "sandbox.snapshot.install.npm",
363
+ {
364
+ "app.sandbox.snapshot.install.npm_count": npm.length
365
+ },
366
+ async () => {
367
+ await runOrThrow(
368
+ sandbox,
369
+ {
370
+ cmd: "npm",
371
+ args: [
372
+ "install",
373
+ "--global",
374
+ "--prefix",
375
+ `${SANDBOX_WORKSPACE_ROOT}/.junior`,
376
+ ...npm
377
+ ]
378
+ },
379
+ "npm install"
380
+ );
381
+ }
382
+ );
383
+ }
384
+ }
385
+ async function postinstall(sandbox, commands) {
386
+ if (commands.length === 0) {
387
+ return;
388
+ }
389
+ await trace(
390
+ "sandbox.snapshot.runtime_postinstall",
391
+ "sandbox.snapshot.runtime_postinstall",
392
+ {
393
+ "app.sandbox.snapshot.runtime_postinstall.count": commands.length
394
+ },
395
+ async () => {
396
+ for (const command of commands) {
397
+ const result = await runNonInteractiveCommand(sandbox, {
398
+ cmd: command.cmd,
399
+ args: command.args,
400
+ login: true,
401
+ pathPrefix: `${SANDBOX_WORKSPACE_ROOT}/.junior/bin:$PATH`,
402
+ ...command.sudo !== void 0 ? { sudo: command.sudo } : {}
403
+ });
404
+ if (result.exitCode !== 0) {
405
+ const detail = result.stderr.trim() || result.stdout.trim() || "command failed";
406
+ throw new Error(
407
+ `runtime-postinstall ${command.cmd} failed: ${detail}`
408
+ );
409
+ }
410
+ }
411
+ }
412
+ );
108
413
  }
109
414
 
110
415
  // src/chat/sandbox/workspace.ts
@@ -350,21 +655,21 @@ function bindSandboxFileSystem(fs, signal) {
350
655
  return fs;
351
656
  }
352
657
  return {
353
- readFile(filePath, options) {
658
+ readFile(filePath2, options) {
354
659
  signal.throwIfAborted();
355
- return fs.readFile(filePath, { ...options, signal });
660
+ return fs.readFile(filePath2, { ...options, signal });
356
661
  },
357
- writeFile(filePath, content, options) {
662
+ writeFile(filePath2, content, options) {
358
663
  signal.throwIfAborted();
359
- return fs.writeFile(filePath, content, { ...options, signal });
664
+ return fs.writeFile(filePath2, content, { ...options, signal });
360
665
  },
361
- readdir(filePath) {
666
+ readdir(filePath2) {
362
667
  signal.throwIfAborted();
363
- return fs.readdir(filePath, { signal });
668
+ return fs.readdir(filePath2, { signal });
364
669
  },
365
- stat(filePath) {
670
+ stat(filePath2) {
366
671
  signal.throwIfAborted();
367
- return fs.stat(filePath, { signal });
672
+ return fs.stat(filePath2, { signal });
368
673
  }
369
674
  };
370
675
  }
@@ -382,12 +687,12 @@ function createSandboxSession(sandbox, options) {
382
687
  }
383
688
  };
384
689
  const fs = {
385
- readFile: async (filePath, readOptions) => await run(async () => await fileSystem.readFile(filePath, readOptions)),
386
- writeFile: async (filePath, content, writeOptions) => await run(
387
- async () => await fileSystem.writeFile(filePath, content, writeOptions)
690
+ readFile: async (filePath2, readOptions) => await run(async () => await fileSystem.readFile(filePath2, readOptions)),
691
+ writeFile: async (filePath2, content, writeOptions) => await run(
692
+ async () => await fileSystem.writeFile(filePath2, content, writeOptions)
388
693
  ),
389
- readdir: async (filePath, readOptions) => await run(async () => await fileSystem.readdir(filePath, readOptions)),
390
- stat: async (filePath, statOptions) => await run(async () => await fileSystem.stat(filePath, statOptions))
694
+ readdir: async (filePath2, readOptions) => await run(async () => await fileSystem.readdir(filePath2, readOptions)),
695
+ stat: async (filePath2, statOptions) => await run(async () => await fileSystem.stat(filePath2, statOptions))
391
696
  };
392
697
  return {
393
698
  sandboxId: sandbox.name,
@@ -432,109 +737,43 @@ function createSandboxSession(sandbox, options) {
432
737
  };
433
738
  }
434
739
 
435
- // src/chat/sandbox/resources.ts
436
- function getSandboxResources() {
437
- const value = process.env.SANDBOX_VCPUS?.trim();
438
- if (!value || !/^\d+$/.test(value)) {
439
- return void 0;
440
- }
441
- const vcpus = Number(value);
442
- if (!Number.isSafeInteger(vcpus) || vcpus <= 0) {
443
- return void 0;
444
- }
445
- return { vcpus };
446
- }
447
-
448
- // src/chat/sandbox/runtime-dependency-snapshots.ts
740
+ // src/chat/sandbox/snapshot/resolve.ts
449
741
  var SNAPSHOT_CACHE_PREFIX = "junior:sandbox_snapshot_profile";
450
742
  var SNAPSHOT_LOCK_PREFIX = "junior:sandbox_snapshot_lock";
451
- var SNAPSHOT_PROFILE_VERSION = 2;
452
743
  var SNAPSHOT_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
453
- var SNAPSHOT_BUILD_LOCK_TTL_MS = 10 * 60 * 1e3;
454
- var SNAPSHOT_WAIT_FOR_LOCK_MS = SNAPSHOT_BUILD_LOCK_TTL_MS + 30 * 1e3;
455
- var DEFAULT_FLOATING_DEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
456
- var RIPGREP_VERSION = "15.1.0";
457
- var RIPGREP_ARCHIVE_SHA256 = "1c9297be4a084eea7ecaedf93eb03d058d6faae29bbc57ecdaf5063921491599";
458
- var RIPGREP_ARCHIVE_URL = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`;
744
+ var SNAPSHOT_BUILD_LOCK_BUFFER_MS = 30 * 1e3;
745
+ var SNAPSHOT_WAIT_FOR_LOCK_BUFFER_MS = 30 * 1e3;
746
+ var cachedSnapshotSchema = z.object({
747
+ profileHash: z.string(),
748
+ snapshotId: z.string(),
749
+ runtime: z.string(),
750
+ createdAtMs: z.number(),
751
+ dependencyCount: z.number()
752
+ }).strict();
459
753
  function profileCacheKey(profileHash) {
460
754
  return `${SNAPSHOT_CACHE_PREFIX}:${profileHash}`;
461
755
  }
462
756
  function profileLockKey(profileHash) {
463
757
  return `${SNAPSHOT_LOCK_PREFIX}:${profileHash}`;
464
758
  }
465
- function isExactNpmVersion(version) {
466
- return /^\d+\.\d+\.\d+(?:[-+][a-z0-9.]+)?$/i.test(version.trim());
467
- }
468
- function hasFloatingSelector(dep) {
469
- return dep.type === "npm" && !isExactNpmVersion(dep.version);
470
- }
471
- function parseFloatingDepMaxAgeMs() {
472
- const raw = process.env.SANDBOX_SNAPSHOT_FLOATING_MAX_AGE_MS;
473
- if (!raw?.trim()) {
474
- return DEFAULT_FLOATING_DEP_MAX_AGE_MS;
475
- }
476
- const parsed = Number.parseInt(raw, 10);
477
- if (!Number.isFinite(parsed) || parsed < 0) {
478
- return DEFAULT_FLOATING_DEP_MAX_AGE_MS;
479
- }
480
- return parsed;
481
- }
482
- function buildDependencyProfile(runtime) {
483
- const dependencies = [
484
- ...GLOBAL_RUNTIME_DEPENDENCIES,
485
- ...pluginCatalogRuntime.getRuntimeDependencies()
486
- ];
487
- const postinstall = pluginCatalogRuntime.getRuntimePostinstall();
488
- if (dependencies.length === 0 && postinstall.length === 0) {
759
+ async function getCachedSnapshot(profileHash) {
760
+ const state = getStateAdapter();
761
+ await state.connect();
762
+ const raw = await state.get(profileCacheKey(profileHash));
763
+ if (typeof raw !== "string") {
489
764
  return null;
490
765
  }
491
- const rebuildEpoch = process.env.SANDBOX_SNAPSHOT_REBUILD_EPOCH?.trim() ?? "";
492
- const hasFloatingVersions = dependencies.some((dep) => hasFloatingSelector(dep)) || postinstall.length > 0;
493
- const hashInput = JSON.stringify({
494
- version: SNAPSHOT_PROFILE_VERSION,
495
- runtime,
496
- rebuildEpoch,
497
- dependencies,
498
- postinstall
499
- });
500
- const profileHash = createHash("sha256").update(hashInput).digest("hex");
501
- return {
502
- profileHash,
503
- dependencyCount: dependencies.length,
504
- hasFloatingVersions,
505
- dependencies,
506
- postinstall
507
- };
508
- }
509
- function getRuntimeDependencyProfileHash(runtime) {
510
- return buildDependencyProfile(runtime)?.profileHash;
511
- }
512
- function shouldRebuildCachedSnapshot(profile, cached) {
513
- if (!profile.hasFloatingVersions) {
514
- return false;
515
- }
516
- const maxAgeMs = parseFloatingDepMaxAgeMs();
517
- if (maxAgeMs === 0) {
518
- return true;
519
- }
520
- return Date.now() - cached.createdAtMs > maxAgeMs;
521
- }
522
- async function getCachedSnapshot(profileHash) {
766
+ let value;
523
767
  try {
524
- const state = getStateAdapter();
525
- await state.connect();
526
- const raw = await state.get(profileCacheKey(profileHash));
527
- if (typeof raw !== "string") {
528
- return null;
529
- }
530
- const parsed = JSON.parse(raw);
531
- if (!parsed || typeof parsed !== "object" || typeof parsed.profileHash !== "string" || typeof parsed.snapshotId !== "string" || typeof parsed.runtime !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.dependencyCount !== "number") {
532
- return null;
533
- }
534
- return parsed;
768
+ value = JSON.parse(raw);
535
769
  } catch {
536
- return null;
770
+ throw new Error(`Invalid cached sandbox snapshot for ${profileHash}`);
537
771
  }
772
+ const parsed = cachedSnapshotSchema.safeParse(value);
773
+ if (!parsed.success || parsed.data.profileHash !== profileHash) {
774
+ throw new Error(`Invalid cached sandbox snapshot for ${profileHash}`);
775
+ }
776
+ return parsed.data;
538
777
  }
539
778
  async function setCachedSnapshot(entry) {
540
779
  const state = getStateAdapter();
@@ -545,280 +784,13 @@ async function setCachedSnapshot(entry) {
545
784
  SNAPSHOT_CACHE_TTL_MS
546
785
  );
547
786
  }
548
- async function withSnapshotSpan(name, op, attributes, callback) {
549
- return await withSpan(name, op, {}, callback, attributes);
550
- }
551
- async function runOrThrow(sandbox, params, label) {
552
- const result = await runNonInteractiveCommand(sandbox, params);
553
- if (result.exitCode === 0) {
554
- return;
555
- }
556
- const stderr = result.stderr.trim();
557
- const stdout = result.stdout.trim();
558
- const detail = stderr || stdout || "command failed";
559
- throw new Error(`${label} failed: ${detail}`);
560
- }
561
- async function tryRun(sandbox, params) {
562
- const result = await runNonInteractiveCommand(sandbox, params);
563
- if (result.exitCode === 0) {
564
- return { ok: true };
565
- }
566
- const stderr = result.stderr.trim();
567
- const stdout = result.stdout.trim();
568
- return { ok: false, detail: stderr || stdout || "command failed" };
569
- }
570
- async function installGhCliViaDnf(sandbox) {
571
- const direct = await tryRun(sandbox, {
572
- cmd: "dnf",
573
- args: ["install", "-y", "gh"],
574
- sudo: true
575
- });
576
- if (direct.ok) {
577
- return;
578
- }
579
- const dnf5Repo = await tryRun(sandbox, {
580
- cmd: "dnf",
581
- args: [
582
- "config-manager",
583
- "addrepo",
584
- "--from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo"
585
- ],
586
- sudo: true
587
- });
588
- if (!dnf5Repo.ok) {
589
- await runOrThrow(
590
- sandbox,
591
- {
592
- cmd: "dnf",
593
- args: ["install", "-y", "dnf-command(config-manager)"],
594
- sudo: true
595
- },
596
- "dnf install dnf-command(config-manager)"
597
- );
598
- await runOrThrow(
599
- sandbox,
600
- {
601
- cmd: "dnf",
602
- args: [
603
- "config-manager",
604
- "--add-repo",
605
- "https://cli.github.com/packages/rpm/gh-cli.repo"
606
- ],
607
- sudo: true
608
- },
609
- "dnf config-manager --add-repo gh-cli.repo"
610
- );
611
- }
612
- await runOrThrow(
613
- sandbox,
614
- {
615
- cmd: "dnf",
616
- args: ["install", "-y", "gh", "--repo", "gh-cli"],
617
- sudo: true
618
- },
619
- "dnf install gh --repo gh-cli"
620
- );
621
- }
622
- function runtimeDependencyFilePath(url, sha256) {
623
- let urlBasename = "package.rpm";
624
- try {
625
- const pathname = new URL(url).pathname;
626
- const segments = pathname.split("/").filter(Boolean);
627
- const candidate = segments[segments.length - 1];
628
- if (candidate) {
629
- urlBasename = candidate;
630
- }
631
- } catch {
632
- }
633
- const sanitizedBasename = urlBasename.replace(/[^a-zA-Z0-9._-]/g, "_");
634
- return `/tmp/junior-runtime-${sha256.slice(0, 12)}-${sanitizedBasename}`;
635
- }
636
- async function downloadVerifiedRuntimeDependency(sandbox, url, sha256) {
637
- const filePath = runtimeDependencyFilePath(url, sha256);
638
- await runOrThrow(
639
- sandbox,
640
- {
641
- cmd: "curl",
642
- args: ["-fsSL", url, "-o", filePath]
643
- },
644
- `curl download ${url}`
645
- );
646
- const checksumResult = await runNonInteractiveCommand(sandbox, {
647
- cmd: "sha256sum",
648
- args: [filePath]
649
- });
650
- const checksumStdout = checksumResult.stdout.trim();
651
- const checksumStderr = checksumResult.stderr.trim();
652
- if (checksumResult.exitCode !== 0) {
653
- throw new Error(
654
- `sha256sum failed: ${checksumStderr || checksumStdout || "command failed"}`
655
- );
656
- }
657
- const actualChecksum = checksumStdout.split(/\s+/)[0]?.toLowerCase();
658
- if (!actualChecksum) {
659
- throw new Error("sha256sum produced empty output");
660
- }
661
- if (actualChecksum !== sha256) {
662
- throw new Error(
663
- `checksum mismatch for ${url}: expected ${sha256}, got ${actualChecksum}`
664
- );
665
- }
666
- return filePath;
667
- }
668
- async function installRipgrep(sandbox) {
669
- const archivePath = await downloadVerifiedRuntimeDependency(
670
- sandbox,
671
- RIPGREP_ARCHIVE_URL,
672
- RIPGREP_ARCHIVE_SHA256
673
- );
674
- const extractedDir = `/tmp/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl`;
675
- await runOrThrow(
676
- sandbox,
677
- {
678
- cmd: "tar",
679
- args: ["-xzf", archivePath, "-C", "/tmp"]
680
- },
681
- "extract ripgrep"
682
- );
683
- await runOrThrow(
684
- sandbox,
685
- {
686
- cmd: "mkdir",
687
- args: ["-p", `${SANDBOX_WORKSPACE_ROOT}/.junior/bin`]
688
- },
689
- "create sandbox bin directory"
690
- );
691
- await runOrThrow(
692
- sandbox,
693
- {
694
- cmd: "install",
695
- args: [
696
- "-m",
697
- "0755",
698
- `${extractedDir}/rg`,
699
- `${SANDBOX_WORKSPACE_ROOT}/.junior/bin/rg`
700
- ]
701
- },
702
- "install ripgrep"
703
- );
704
- }
705
- async function installRuntimeDependencies(sandbox, deps) {
706
- const systemDeps = deps.filter(
707
- (dep) => dep.type === "system"
708
- );
709
- const npmPackages = deps.filter(
710
- (dep) => dep.type === "npm"
711
- ).map((dep) => `${dep.package}@${dep.version}`);
712
- if (systemDeps.length > 0) {
713
- await withSnapshotSpan(
714
- "sandbox.snapshot.install_system",
715
- "sandbox.snapshot.install.system",
716
- {
717
- "app.sandbox.snapshot.install.system_count": systemDeps.length
718
- },
719
- async () => {
720
- for (const dep of systemDeps) {
721
- if ("url" in dep) {
722
- const rpmPath = await downloadVerifiedRuntimeDependency(
723
- sandbox,
724
- dep.url,
725
- dep.sha256
726
- );
727
- await runOrThrow(
728
- sandbox,
729
- {
730
- cmd: "dnf",
731
- args: ["install", "-y", rpmPath],
732
- sudo: true
733
- },
734
- `dnf install ${dep.url}`
735
- );
736
- continue;
737
- }
738
- if (dep.package === "ripgrep") {
739
- await installRipgrep(sandbox);
740
- continue;
741
- }
742
- if (dep.package === "gh") {
743
- await installGhCliViaDnf(sandbox);
744
- continue;
745
- }
746
- await runOrThrow(
747
- sandbox,
748
- {
749
- cmd: "dnf",
750
- args: ["install", "-y", dep.package],
751
- sudo: true
752
- },
753
- `dnf install ${dep.package}`
754
- );
755
- }
756
- }
757
- );
758
- }
759
- if (npmPackages.length > 0) {
760
- await withSnapshotSpan(
761
- "sandbox.snapshot.install_npm",
762
- "sandbox.snapshot.install.npm",
763
- {
764
- "app.sandbox.snapshot.install.npm_count": npmPackages.length
765
- },
766
- async () => {
767
- await runOrThrow(
768
- sandbox,
769
- {
770
- cmd: "npm",
771
- args: [
772
- "install",
773
- "--global",
774
- "--prefix",
775
- `${SANDBOX_WORKSPACE_ROOT}/.junior`,
776
- ...npmPackages
777
- ]
778
- },
779
- "npm install"
780
- );
781
- }
782
- );
783
- }
784
- }
785
- async function runRuntimePostinstall(sandbox, commands) {
786
- if (commands.length === 0) {
787
- return;
788
- }
789
- await withSnapshotSpan(
790
- "sandbox.snapshot.runtime_postinstall",
791
- "sandbox.snapshot.runtime_postinstall",
792
- {
793
- "app.sandbox.snapshot.runtime_postinstall.count": commands.length
794
- },
795
- async () => {
796
- for (const command of commands) {
797
- const result = await runNonInteractiveCommand(sandbox, {
798
- cmd: command.cmd,
799
- args: command.args,
800
- login: true,
801
- pathPrefix: `${SANDBOX_WORKSPACE_ROOT}/.junior/bin:$PATH`,
802
- ...command.sudo !== void 0 ? { sudo: command.sudo } : {}
803
- });
804
- if (result.exitCode === 0) {
805
- continue;
806
- }
807
- const stderr = result.stderr.trim();
808
- const stdout = result.stdout.trim();
809
- const detail = stderr || stdout || "command failed";
810
- throw new Error(`runtime-postinstall ${command.cmd} failed: ${detail}`);
811
- }
812
- }
813
- );
814
- }
815
- async function createDependencySnapshot(profile, runtime, timeoutMs) {
816
- return await withSnapshotSpan(
787
+ async function build(value, runtime, timeoutMs) {
788
+ return await trace(
817
789
  "sandbox.snapshot.build",
818
790
  "sandbox.snapshot.build",
819
791
  {
820
792
  "app.sandbox.runtime": runtime,
821
- "app.sandbox.snapshot.dependency_count": profile.dependencyCount
793
+ "app.sandbox.snapshot.dependency_count": value.dependencyCount
822
794
  },
823
795
  async () => {
824
796
  const sandboxCredentials = getVercelSandboxCredentials();
@@ -832,13 +804,13 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
832
804
  })
833
805
  );
834
806
  try {
835
- await installRuntimeDependencies(sandbox, profile.dependencies);
836
- await runRuntimePostinstall(sandbox, profile.postinstall);
837
- return await withSnapshotSpan(
807
+ await dependencies(sandbox, value.dependencies);
808
+ await postinstall(sandbox, value.postinstall);
809
+ return await trace(
838
810
  "sandbox.snapshot.capture",
839
811
  "sandbox.snapshot.capture",
840
812
  {
841
- "app.sandbox.snapshot.dependency_count": profile.dependencyCount
813
+ "app.sandbox.snapshot.dependency_count": value.dependencyCount
842
814
  },
843
815
  async () => {
844
816
  const snapshot = await sandbox.snapshot();
@@ -854,40 +826,35 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
854
826
  }
855
827
  );
856
828
  }
857
- async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks) {
829
+ async function withBuildLock(profileHash, timeoutMs, callback, canUseCachedSnapshot, onWaitingForLock) {
858
830
  const state = getStateAdapter();
859
831
  await state.connect();
860
832
  const lockKey = profileLockKey(profileHash);
861
- const tryAcquireLock = async () => await state.acquireLock(lockKey, SNAPSHOT_BUILD_LOCK_TTL_MS);
833
+ const lockTtlMs = timeoutMs + SNAPSHOT_BUILD_LOCK_BUFFER_MS;
834
+ const tryAcquireLock = async () => await state.acquireLock(lockKey, lockTtlMs);
862
835
  let lock = await tryAcquireLock();
863
836
  if (lock) {
864
837
  try {
865
- const result = await callback();
866
- return {
867
- snapshotId: result.snapshotId,
868
- source: result.source,
869
- waitedForLock: false
870
- };
838
+ return await callback();
871
839
  } finally {
872
840
  await state.releaseLock(lock);
873
841
  }
874
842
  }
875
- return await withSnapshotSpan(
843
+ return await trace(
876
844
  "sandbox.snapshot.lock_wait",
877
845
  "sandbox.snapshot.lock_wait",
878
846
  {
879
847
  "app.sandbox.snapshot.profile_hash": profileHash
880
848
  },
881
849
  async () => {
882
- await hooks?.onWaitingForLock?.();
883
- const waitUntil = Date.now() + SNAPSHOT_WAIT_FOR_LOCK_MS;
850
+ await onWaitingForLock?.();
851
+ const waitUntil = Date.now() + lockTtlMs + SNAPSHOT_WAIT_FOR_LOCK_BUFFER_MS;
884
852
  while (Date.now() < waitUntil) {
885
853
  const cached2 = await getCachedSnapshot(profileHash);
886
854
  if (cached2?.snapshotId && canUseCachedSnapshot(cached2)) {
887
855
  return {
888
856
  snapshotId: cached2.snapshotId,
889
- source: "wait_cache",
890
- waitedForLock: true
857
+ source: "cache_hit_after_lock_wait"
891
858
  };
892
859
  }
893
860
  lock = await tryAcquireLock();
@@ -896,8 +863,7 @@ async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks)
896
863
  const result = await callback();
897
864
  return {
898
865
  snapshotId: result.snapshotId,
899
- source: result.source,
900
- waitedForLock: true
866
+ source: result.source === "built" ? "built" : "cache_hit_after_lock_wait"
901
867
  };
902
868
  } finally {
903
869
  await state.releaseLock(lock);
@@ -909,22 +875,18 @@ async function withBuildLock(profileHash, callback, canUseCachedSnapshot, hooks)
909
875
  if (cached?.snapshotId && canUseCachedSnapshot(cached)) {
910
876
  return {
911
877
  snapshotId: cached.snapshotId,
912
- source: "wait_cache",
913
- waitedForLock: true
878
+ source: "cache_hit_after_lock_wait"
914
879
  };
915
880
  }
916
881
  throw new Error("Timed out waiting for snapshot build lock");
917
882
  }
918
883
  );
919
884
  }
920
- function toResolveOutcome(forceRebuild, source, waitedForLock) {
885
+ function toResolveOutcome(forceRebuild, source) {
921
886
  if (source === "built") {
922
887
  return forceRebuild ? "forced_rebuild" : "rebuilt";
923
888
  }
924
- if (waitedForLock || source === "wait_cache") {
925
- return "cache_hit_after_lock_wait";
926
- }
927
- return "cache_hit";
889
+ return source;
928
890
  }
929
891
  function getRebuildReason(params) {
930
892
  if (params.forceRebuild) {
@@ -938,8 +900,8 @@ function getRebuildReason(params) {
938
900
  }
939
901
  return void 0;
940
902
  }
941
- async function resolveRuntimeDependencySnapshot(params) {
942
- return await withSnapshotSpan(
903
+ async function resolve(params) {
904
+ return await trace(
943
905
  "sandbox.snapshot.resolve",
944
906
  "sandbox.snapshot.resolve",
945
907
  {
@@ -948,25 +910,24 @@ async function resolveRuntimeDependencySnapshot(params) {
948
910
  },
949
911
  async () => {
950
912
  await params.onProgress?.("resolve_start");
951
- const resolveStartedAtMs = Date.now();
952
- const profile = buildDependencyProfile(params.runtime);
953
- if (!profile) {
913
+ const currentProfile = create(params.runtime);
914
+ if (!currentProfile) {
954
915
  return {
955
916
  dependencyCount: 0,
956
917
  cacheHit: false,
957
918
  resolveOutcome: "no_profile"
958
919
  };
959
920
  }
960
- const cached = await getCachedSnapshot(profile.profileHash);
921
+ const cached = await getCachedSnapshot(currentProfile.hash);
961
922
  const cachedNeedsRebuild = Boolean(
962
- cached?.snapshotId && shouldRebuildCachedSnapshot(profile, cached)
923
+ cached?.snapshotId && isStale(currentProfile, cached.createdAtMs)
963
924
  );
964
925
  if (!params.forceRebuild && cached?.snapshotId && !cachedNeedsRebuild) {
965
926
  await params.onProgress?.("cache_hit");
966
927
  return {
967
928
  snapshotId: cached.snapshotId,
968
- profileHash: profile.profileHash,
969
- dependencyCount: profile.dependencyCount,
929
+ profileHash: currentProfile.hash,
930
+ dependencyCount: currentProfile.dependencyCount,
970
931
  cacheHit: true,
971
932
  resolveOutcome: "cache_hit"
972
933
  };
@@ -982,60 +943,58 @@ async function resolveRuntimeDependencySnapshot(params) {
982
943
  if (params.staleSnapshotId) {
983
944
  return candidate.snapshotId !== params.staleSnapshotId;
984
945
  }
985
- return candidate.createdAtMs > resolveStartedAtMs;
946
+ return candidate.snapshotId !== cached?.snapshotId;
986
947
  }
987
- return !shouldRebuildCachedSnapshot(profile, candidate);
948
+ return !isStale(currentProfile, candidate.createdAtMs);
988
949
  };
989
950
  const lockResult = await withBuildLock(
990
- profile.profileHash,
951
+ currentProfile.hash,
952
+ params.timeoutMs,
991
953
  async () => {
992
- const latest = await getCachedSnapshot(profile.profileHash);
954
+ const latest = await getCachedSnapshot(currentProfile.hash);
993
955
  if (latest?.snapshotId && canUseCachedSnapshot(latest)) {
994
956
  await params.onProgress?.("cache_hit");
995
957
  return {
996
958
  snapshotId: latest.snapshotId,
997
- source: "callback_cache"
959
+ source: "cache_hit"
998
960
  };
999
961
  }
1000
962
  await params.onProgress?.("building_snapshot");
1001
- const nextSnapshotId = await createDependencySnapshot(
1002
- profile,
963
+ const nextSnapshotId = await build(
964
+ currentProfile,
1003
965
  params.runtime,
1004
966
  params.timeoutMs
1005
967
  );
1006
968
  await setCachedSnapshot({
1007
- profileHash: profile.profileHash,
969
+ profileHash: currentProfile.hash,
1008
970
  snapshotId: nextSnapshotId,
1009
971
  runtime: params.runtime,
1010
972
  createdAtMs: Date.now(),
1011
- dependencyCount: profile.dependencyCount
973
+ dependencyCount: currentProfile.dependencyCount
1012
974
  });
1013
975
  await params.onProgress?.("build_complete");
1014
976
  return { snapshotId: nextSnapshotId, source: "built" };
1015
977
  },
1016
978
  canUseCachedSnapshot,
1017
- {
1018
- onWaitingForLock: async () => {
1019
- await params.onProgress?.("waiting_for_lock");
1020
- }
979
+ async () => {
980
+ await params.onProgress?.("waiting_for_lock");
1021
981
  }
1022
982
  );
1023
983
  return {
1024
984
  snapshotId: lockResult.snapshotId,
1025
- profileHash: profile.profileHash,
1026
- dependencyCount: profile.dependencyCount,
985
+ profileHash: currentProfile.hash,
986
+ dependencyCount: currentProfile.dependencyCount,
1027
987
  cacheHit: lockResult.source !== "built",
1028
988
  resolveOutcome: toResolveOutcome(
1029
989
  Boolean(params.forceRebuild),
1030
- lockResult.source,
1031
- lockResult.waitedForLock
990
+ lockResult.source
1032
991
  ),
1033
992
  ...rebuildReason ? { rebuildReason } : {}
1034
993
  };
1035
994
  }
1036
995
  );
1037
996
  }
1038
- function isSnapshotMissingError(error) {
997
+ function isMissingError(error) {
1039
998
  const searchable = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
1040
999
  return searchable.includes("snapshot") && (searchable.includes("not found") || searchable.includes("unknown") || searchable.includes("404"));
1041
1000
  }
@@ -1052,10 +1011,10 @@ export {
1052
1011
  buildNonInteractiveShellScript,
1053
1012
  runNonInteractiveCommand,
1054
1013
  getSandboxResources,
1055
- GLOBAL_RUNTIME_DEPENDENCIES,
1014
+ create,
1015
+ hash,
1056
1016
  bindSandboxFileSystem,
1057
1017
  createSandboxSession,
1058
- getRuntimeDependencyProfileHash,
1059
- resolveRuntimeDependencySnapshot,
1060
- isSnapshotMissingError
1018
+ resolve,
1019
+ isMissingError
1061
1020
  };