veryfront 0.1.600 → 0.1.602

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 (37) hide show
  1. package/esm/cli/commands/init/deno-config-generator.d.ts +10 -0
  2. package/esm/cli/commands/init/deno-config-generator.d.ts.map +1 -0
  3. package/esm/cli/commands/init/deno-config-generator.js +26 -0
  4. package/esm/cli/commands/init/handler.d.ts.map +1 -1
  5. package/esm/cli/commands/init/handler.js +8 -0
  6. package/esm/cli/commands/init/init-command.d.ts.map +1 -1
  7. package/esm/cli/commands/init/init-command.js +14 -3
  8. package/esm/cli/commands/init/interactive-wizard.d.ts +3 -2
  9. package/esm/cli/commands/init/interactive-wizard.d.ts.map +1 -1
  10. package/esm/cli/commands/init/interactive-wizard.js +37 -2
  11. package/esm/cli/commands/init/runtime.d.ts +8 -0
  12. package/esm/cli/commands/init/runtime.d.ts.map +1 -0
  13. package/esm/cli/commands/init/runtime.js +14 -0
  14. package/esm/cli/commands/init/types.d.ts +3 -0
  15. package/esm/cli/commands/init/types.d.ts.map +1 -1
  16. package/esm/deno.js +1 -1
  17. package/esm/src/agent/hosted/durable-child-fork-execution.d.ts.map +1 -1
  18. package/esm/src/agent/hosted/durable-child-fork-execution.js +3 -0
  19. package/esm/src/agent/types.d.ts +11 -0
  20. package/esm/src/agent/types.d.ts.map +1 -1
  21. package/esm/src/discovery/import-rewriter.d.ts.map +1 -1
  22. package/esm/src/discovery/import-rewriter.js +216 -30
  23. package/esm/src/discovery/transpiler.d.ts.map +1 -1
  24. package/esm/src/discovery/transpiler.js +8 -0
  25. package/esm/src/internal-agents/run-stream.d.ts +8 -0
  26. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  27. package/esm/src/internal-agents/run-stream.js +86 -3
  28. package/esm/src/sandbox/lazy-sandbox.d.ts +14 -0
  29. package/esm/src/sandbox/lazy-sandbox.d.ts.map +1 -1
  30. package/esm/src/sandbox/lazy-sandbox.js +46 -4
  31. package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
  32. package/esm/src/server/handlers/request/agent-stream.handler.js +12 -5
  33. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
  34. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +158 -18
  35. package/esm/src/utils/version-constant.d.ts +1 -1
  36. package/esm/src/utils/version-constant.js +1 -1
  37. package/package.json +1 -1
@@ -60,6 +60,51 @@ function rewriteDenoCompiledVeryfrontImports(code) {
60
60
  }
61
61
  return transformed;
62
62
  }
63
+ /**
64
+ * True for bare npm imports that need an `npm:` prefix for Deno to resolve
65
+ * them when the discovery module is loaded from a temp directory. Excludes
66
+ * relative paths, absolute URLs, Node built-ins, and the veryfront package
67
+ * (handled by the dedicated rewrites above).
68
+ */
69
+ function isUnprefixedNpmSpecifier(specifier) {
70
+ if (!specifier)
71
+ return false;
72
+ if (specifier.startsWith(".") || specifier.startsWith("/"))
73
+ return false;
74
+ if (specifier.startsWith("npm:") || specifier.startsWith("jsr:"))
75
+ return false;
76
+ if (specifier.startsWith("node:"))
77
+ return false;
78
+ if (specifier.startsWith("file:") ||
79
+ specifier.startsWith("http:") ||
80
+ specifier.startsWith("https:"))
81
+ return false;
82
+ if (specifier === "veryfront" || specifier.startsWith("veryfront/"))
83
+ return false;
84
+ return true;
85
+ }
86
+ // `import type` / `export type` lines reference types only; they get erased
87
+ // by TypeScript and must not trigger filesystem resolution.
88
+ const TYPE_ONLY_STATIC_RE = /(?:^|[\s;{}])(?:import|export)\s+type\b/;
89
+ function rewriteBareNpmImportsForDeno(code) {
90
+ return code
91
+ // `import x from "spec"`, `export { x } from "spec"`, `export * from "spec"`.
92
+ // The leading-context capture lets us skip `import type` / `export type`.
93
+ .replace(/(^|[\s;{}])((?:import|export)\b[^"']*?\bfrom\s+)["']([^"']+)["']/g, (match, lead, head, specifier) => {
94
+ if (TYPE_ONLY_STATIC_RE.test(lead + head))
95
+ return match;
96
+ return isUnprefixedNpmSpecifier(specifier) ? `${lead}${head}"npm:${specifier}"` : match;
97
+ })
98
+ .replace(/import\s*\(\s*["']([^"']+)["']\s*\)/g, (match, specifier) => {
99
+ return isUnprefixedNpmSpecifier(specifier) ? `import("npm:${specifier}")` : match;
100
+ })
101
+ // Side-effect-only imports: `import "reflect-metadata";`, `import "dotenv/config";`.
102
+ .replace(/(^|[\n;{}])(\s*)import\s+["']([^"']+)["'](\s*;?)/g, (match, lead, indent, specifier, tail) => {
103
+ return isUnprefixedNpmSpecifier(specifier)
104
+ ? `${lead}${indent}import "npm:${specifier}"${tail}`
105
+ : match;
106
+ });
107
+ }
63
108
  /**
64
109
  * Rewrite imports for Deno runtime
65
110
  * - Converts npm package imports to npm: specifier format
@@ -67,14 +112,7 @@ function rewriteDenoCompiledVeryfrontImports(code) {
67
112
  * - For compiled binaries, rewrites veryfront imports to use globals
68
113
  */
69
114
  export function rewriteForDeno(code, fileDir, options = {}) {
70
- const npmReplacements = [
71
- [/from\s+["']zod["']/g, 'from "npm:zod"'],
72
- [/import\s*\(\s*["']zod["']\s*\)/g, 'import("npm:zod")'],
73
- ];
74
115
  let transformed = code;
75
- for (const [pattern, replacement] of npmReplacements) {
76
- transformed = transformed.replace(pattern, replacement);
77
- }
78
116
  // Handle relative imports
79
117
  transformed = transformed.replace(/from\s+["'](\.\.\/[^"']+)["']/g, (_match, relativePath) => `from "file://${pathHelper.resolve(fileDir, relativePath)}"`);
80
118
  // For compiled binaries, rewrite veryfront imports to use globals
@@ -85,8 +123,89 @@ export function rewriteForDeno(code, fileDir, options = {}) {
85
123
  else {
86
124
  transformed = rewriteDenoPublicVeryfrontImports(transformed, options.resolveSpecifier ?? ((specifier) => globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).resolve(specifier)));
87
125
  }
126
+ // Prefix any remaining bare npm specifiers with `npm:` so Deno can resolve
127
+ // them from the temp directory the discovery module is loaded from. Covers
128
+ // `zod` plus arbitrary npm packages a tool/agent depends on, after the
129
+ // discovery bundler externalizes them via `packages: "external"`.
130
+ transformed = rewriteBareNpmImportsForDeno(transformed);
88
131
  return transformed;
89
132
  }
133
+ // Memoizes resolved bare-specifier → file:// URL lookups across all
134
+ // `rewriteDiscoveryImports` calls. Keyed by `${projectDir}::${specifier}`.
135
+ // Most projects re-resolve the same handful of packages (react, zod,
136
+ // pdf-parse, …) across every discovered tool/agent file — without this
137
+ // cache, each discovery pass re-reads the same package.json files.
138
+ const resolvedSpecifierCache = new Map();
139
+ // Split `react/jsx-runtime` → { name: "react", subpath: "./jsx-runtime" } and
140
+ // `@scope/pkg/sub/path` → { name: "@scope/pkg", subpath: "./sub/path" }.
141
+ function splitPackageSubpath(specifier) {
142
+ const parts = specifier.split("/");
143
+ const segments = specifier.startsWith("@") ? parts.slice(0, 2) : parts.slice(0, 1);
144
+ const name = segments.join("/");
145
+ const rest = parts.slice(segments.length).join("/");
146
+ return { name, subpath: rest ? `./${rest}` : "." };
147
+ }
148
+ // Pick the relative file path from a `package.json#exports` entry, which can
149
+ // be a string, a conditional object (`{ import, default, ... }`), or an
150
+ // array of those.
151
+ function pickExportEntry(entry) {
152
+ if (typeof entry === "string")
153
+ return entry;
154
+ if (Array.isArray(entry)) {
155
+ for (const e of entry) {
156
+ const v = pickExportEntry(e);
157
+ if (v)
158
+ return v;
159
+ }
160
+ return null;
161
+ }
162
+ if (entry && typeof entry === "object") {
163
+ const obj = entry;
164
+ const candidate = obj.import ?? obj.node ?? obj.default;
165
+ return candidate ? pickExportEntry(candidate) : null;
166
+ }
167
+ return null;
168
+ }
169
+ // Resolve a subpath (`.` or `./foo/bar`) against a `package.json#exports`
170
+ // map. Honors literal keys first, then matches `./*`-style glob patterns
171
+ // where the trailing `*` is substituted with the captured remainder.
172
+ // Returns the resolved relative path (e.g. `./debounce.js`) or null when
173
+ // no entry matches.
174
+ function resolveExportPath(exports, subpath) {
175
+ if (!exports || typeof exports !== "object")
176
+ return null;
177
+ const map = exports;
178
+ // Literal key (covers "." and exact subpaths like "./jsx-runtime").
179
+ if (subpath in map)
180
+ return pickExportEntry(map[subpath]);
181
+ // Glob keys like "./*", "./feature/*", "./lib/*.js". Pick the longest
182
+ // matching prefix so more specific patterns win over `./*`.
183
+ let bestKey = null;
184
+ let bestPrefixLen = -1;
185
+ for (const key of Object.keys(map)) {
186
+ const star = key.indexOf("*");
187
+ if (star === -1)
188
+ continue;
189
+ const prefix = key.slice(0, star);
190
+ const suffix = key.slice(star + 1);
191
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
192
+ continue;
193
+ if (subpath.length < prefix.length + suffix.length)
194
+ continue;
195
+ if (prefix.length > bestPrefixLen) {
196
+ bestKey = key;
197
+ bestPrefixLen = prefix.length;
198
+ }
199
+ }
200
+ if (!bestKey)
201
+ return null;
202
+ const star = bestKey.indexOf("*");
203
+ const captured = subpath.slice(bestKey.slice(0, star).length, subpath.length - bestKey.slice(star + 1).length);
204
+ const template = pickExportEntry(map[bestKey]);
205
+ if (!template)
206
+ return null;
207
+ return template.replaceAll("*", captured);
208
+ }
90
209
  /**
91
210
  * Rewrite imports for Node.js runtime
92
211
  * - Resolves relative imports to file:// URLs
@@ -99,20 +218,47 @@ export async function rewriteDiscoveryImports(code, projectDir, fs, fileDir) {
99
218
  const { pathToFileURL } = await import("node:url");
100
219
  // Handle relative imports
101
220
  transformed = transformed.replace(/from\s+["'](\.\.\/[^"']+)["']/g, (_match, relativePath) => `from "${pathToFileURL(pathHelper.resolve(fileDir, relativePath)).href}"`);
102
- // Resolve npm package to file URL
103
- const resolvePackageToFileUrl = async (packageName) => {
221
+ // Resolve a bare specifier (optionally with subpath) to a file:// URL,
222
+ // honoring the package's `exports` map for subpath imports such as
223
+ // `react/jsx-runtime` or `lodash-es/debounce`. Successful resolutions
224
+ // are memoized per (projectDir, specifier); failures are NOT cached
225
+ // so a subsequent `npm install` of the missing dep is picked up
226
+ // without a process restart.
227
+ const resolvePackageToFileUrl = async (specifier) => {
228
+ const cacheKey = `${projectDir}::${specifier}`;
229
+ const cached = resolvedSpecifierCache.get(cacheKey);
230
+ if (cached !== undefined)
231
+ return cached;
232
+ const { name: packageName, subpath } = splitPackageSubpath(specifier);
104
233
  let searchDir = projectDir;
105
234
  for (let i = 0; i < 10; i++) {
106
235
  const packagePath = pathHelper.join(searchDir, "node_modules", packageName);
107
236
  const packageJsonPath = pathHelper.join(packagePath, "package.json");
108
237
  try {
109
238
  const pkgJson = JSON.parse(await fs.readTextFile(packageJsonPath));
110
- const dotExport = pkgJson.exports?.["."];
111
- const entryPoint = (typeof dotExport === "string" ? dotExport : dotExport?.import ?? dotExport?.default) ??
112
- pkgJson.module ??
113
- pkgJson.main ??
114
- "index.js";
115
- return pathToFileURL(pathHelper.join(packagePath, entryPoint)).href;
239
+ const exportPath = resolveExportPath(pkgJson.exports, subpath);
240
+ const entryPoint = exportPath ??
241
+ (subpath === "."
242
+ ? (pkgJson.module ?? pkgJson.main ?? "index.js")
243
+ // No exports entry matched: fall back to joining the subpath
244
+ // onto the package dir (e.g. `dotenv/config.js`).
245
+ : subpath.replace(/^\.\//, ""));
246
+ // Defense in depth: refuse resolved paths that escape the package
247
+ // directory. A malicious package shipping `exports: { ".": "../foo" }`
248
+ // would otherwise yield a `file://` URL outside `node_modules/<pkg>`
249
+ // that the discovery loader would still `import()`. `path.resolve`
250
+ // (unlike `path.join`) normalizes `..` segments, so the prefix
251
+ // check correctly catches escape attempts.
252
+ const normalized = pathHelper.resolve(packagePath, entryPoint);
253
+ const packagePathPrefix = packagePath.endsWith(pathHelper.SEPARATOR)
254
+ ? packagePath
255
+ : packagePath + pathHelper.SEPARATOR;
256
+ if (normalized !== packagePath && !normalized.startsWith(packagePathPrefix)) {
257
+ return null;
258
+ }
259
+ const resolved = pathToFileURL(normalized).href;
260
+ resolvedSpecifierCache.set(cacheKey, resolved);
261
+ return resolved;
116
262
  }
117
263
  catch (_) {
118
264
  /* expected: package.json not found at this level, walk up */
@@ -122,26 +268,66 @@ export async function rewriteDiscoveryImports(code, projectDir, fs, fileDir) {
122
268
  searchDir = parent;
123
269
  }
124
270
  }
271
+ // Intentionally do NOT cache nulls — a missing-then-installed package
272
+ // should be resolvable on the next pass without a process restart.
125
273
  return null;
126
274
  };
127
- // Rewrite package imports
128
- const rewritePackageImports = async (input, pkg) => {
129
- const escapedPkg = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
130
- const staticImportRegex = new RegExp(`from\\s*["']${escapedPkg}["']`, "g");
275
+ const rewritePackageImports = (input, pkg, resolvedUrl) => {
276
+ const escapedPkg = escapeRegExp(pkg);
277
+ const staticImportRegex = new RegExp(`(^|[\\s;{}])((?:import|export)\\b[^"']*?\\bfrom\\s+)["']${escapedPkg}["']`, "g");
131
278
  const dynamicImportRegex = new RegExp(`import\\s*\\(\\s*["']${escapedPkg}["']\\s*\\)`, "g");
132
- if (!staticImportRegex.test(input) && !dynamicImportRegex.test(input))
133
- return input;
134
- const resolvedUrl = await resolvePackageToFileUrl(pkg);
135
- if (!resolvedUrl)
136
- return input;
279
+ const sideEffectRegex = new RegExp(`(^|[\\n;{}])(\\s*)import\\s+["']${escapedPkg}["'](\\s*;?)`, "g");
137
280
  return input
138
- .replace(staticImportRegex, `from "${resolvedUrl}"`)
139
- .replace(dynamicImportRegex, `import("${resolvedUrl}")`);
281
+ .replace(staticImportRegex, (match, lead, head) => {
282
+ if (TYPE_ONLY_STATIC_RE.test(lead + head))
283
+ return match;
284
+ return `${lead}${head}"${resolvedUrl}"`;
285
+ })
286
+ .replace(dynamicImportRegex, `import("${resolvedUrl}")`)
287
+ .replace(sideEffectRegex, (_m, lead, indent, tail) => `${lead}${indent}import "${resolvedUrl}"${tail}`);
288
+ };
289
+ // Collect every bare specifier the transformed source still imports,
290
+ // then resolve each to a file:// URL in the project's node_modules.
291
+ // With `packages: "external"` in the discovery bundler, npm packages a
292
+ // tool/agent depends on (e.g. `pdf-parse`, `mammoth`, `react/jsx-runtime`)
293
+ // survive as bare imports here and need explicit resolution before the
294
+ // temp module is loaded from outside the project's resolution root.
295
+ const collectBareSpecifiers = (input) => {
296
+ const specifiers = new Set();
297
+ // Matches `import x from "spec"`, `export { x } from "spec"`,
298
+ // `export * from "spec"`. The leading-context capture lets us skip
299
+ // `import type` / `export type` lines, which TypeScript erases at
300
+ // emit time and must not trigger filesystem resolution.
301
+ for (const match of input.matchAll(/(?:^|[\s;{}])((?:import|export)\b[^"']*?\bfrom\s+)["']([^"']+)["']/g)) {
302
+ const head = match[1] ?? "";
303
+ const s = match[2];
304
+ if (TYPE_ONLY_STATIC_RE.test(head))
305
+ continue;
306
+ if (s && isUnprefixedNpmSpecifier(s))
307
+ specifiers.add(s);
308
+ }
309
+ for (const match of input.matchAll(/import\s*\(\s*["']([^"']+)["']\s*\)/g)) {
310
+ const s = match[1];
311
+ if (s && isUnprefixedNpmSpecifier(s))
312
+ specifiers.add(s);
313
+ }
314
+ // Side-effect imports: `import "reflect-metadata";`, `import "dotenv/config";`.
315
+ for (const match of input.matchAll(/(?:^|[\n;{}])\s*import\s+["']([^"']+)["']\s*;?/g)) {
316
+ const s = match[1];
317
+ if (s && isUnprefixedNpmSpecifier(s))
318
+ specifiers.add(s);
319
+ }
320
+ return [...specifiers];
140
321
  };
141
- // Rewrite external package imports
142
- const externalPackages = ["zod"];
143
- for (const pkg of externalPackages) {
144
- transformed = await rewritePackageImports(transformed, pkg);
322
+ // Resolve every bare specifier in parallel — each lookup is independent
323
+ // node_modules I/O. Apply the resulting rewrites sequentially against
324
+ // the same source string.
325
+ const specifiers = collectBareSpecifiers(transformed);
326
+ const resolvedPairs = await Promise.all(specifiers.map(async (pkg) => [pkg, await resolvePackageToFileUrl(pkg)]));
327
+ for (const [pkg, resolvedUrl] of resolvedPairs) {
328
+ if (!resolvedUrl)
329
+ continue;
330
+ transformed = rewritePackageImports(transformed, pkg, resolvedUrl);
145
331
  }
146
332
  const resolveRuntimeSpecifierToFileUrl = (specifier) => {
147
333
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"transpiler.d.ts","sourceRoot":"","sources":["../../../src/src/discovery/transpiler.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAmIvD;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAwFlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
1
+ {"version":3,"file":"transpiler.d.ts","sourceRoot":"","sources":["../../../src/src/discovery/transpiler.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAmIvD;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAgGlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -160,6 +160,14 @@ export async function importModule(file, context) {
160
160
  jsxImportSource: "react",
161
161
  resolveExtensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"],
162
162
  plugins,
163
+ // Externalize all bare-specifier imports so npm packages a tool/agent file
164
+ // depends on (e.g. `pdf-parse`, `mammoth`) are not pulled into the
165
+ // discovery bundle. Discovery only needs the module's exports; the
166
+ // implementation runs server-side at request time and can resolve npm
167
+ // packages natively via the project's node_modules / import map.
168
+ // Without this, esbuild under platform: "neutral" tries to bundle CJS
169
+ // npm packages and fails on their Node built-in references (fs, http, ...).
170
+ packages: "external",
163
171
  external: [
164
172
  "zod",
165
173
  "node:*",
@@ -1,8 +1,16 @@
1
1
  import { type Agent, type AgentMessage as Message, type AgentResponse } from "../agent/index.js";
2
+ import type { AgentServiceSandboxToolsOptions, AgentServiceSandboxToolsResult } from "../sandbox/index.js";
2
3
  import { type AgentRunSessionManager } from "./session-manager.js";
3
4
  import type { RuntimeRunAgentInput } from "./schema.js";
4
5
  export interface RuntimeAgentStreamExecutionDeps {
5
6
  sessionManager: AgentRunSessionManager;
7
+ projectAgentSandbox?: {
8
+ apiUrl?: string;
9
+ authToken?: string;
10
+ projectId?: string | null;
11
+ };
12
+ createBashTool?: AgentServiceSandboxToolsOptions["createBashTool"];
13
+ createAgentServiceSandboxTools?: (input: AgentServiceSandboxToolsOptions) => Promise<AgentServiceSandboxToolsResult>;
6
14
  createRuntime?: (agent: Agent, mergedTools: Agent["config"]["tools"]) => {
7
15
  stream: (messages: Message[], context?: Record<string, unknown>, callbacks?: {
8
16
  onFinish?: (response: AgentResponse) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAa3B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAkBxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAkJD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAwPnB"}
1
+ {"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAkB7B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAmBxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,mBAAmB,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC;IACF,cAAc,CAAC,EAAE,+BAA+B,CAAC,gBAAgB,CAAC,CAAC;IACnE,8BAA8B,CAAC,EAAE,CAC/B,KAAK,EAAE,+BAA+B,KACnC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAmOD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAwQnB"}
@@ -1,5 +1,8 @@
1
1
  import { AgentRuntime, } from "../agent/index.js";
2
2
  import { normalizeAgUiRuntimeMessages } from "../agent/ag-ui/runtime-support.js";
3
+ import { createAgentServiceSandboxTools } from "../sandbox/index.js";
4
+ import { tryResolve } from "../extensions/contracts.js";
5
+ import { SandboxShellToolsProviderName, } from "../extensions/sandbox/index.js";
3
6
  import { SKILL_TOOL_IDS } from "../skill/types.js";
4
7
  import { toolRegistry } from "../tool/index.js";
5
8
  import { defineSchema, lazySchema } from "../schemas/index.js";
@@ -9,6 +12,7 @@ import { serverLogger } from "../utils/index.js";
9
12
  const getAnyObjectSchema = defineSchema((v) => v.record(v.string(), v.unknown()));
10
13
  const anyObjectSchema = lazySchema(getAnyObjectSchema);
11
14
  const logger = serverLogger.component("internal-agent-run-stream");
15
+ const PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME = "bash";
12
16
  function getAgentAllowedRemoteToolNames(agent) {
13
17
  const raw = agent.config.allowedRemoteTools;
14
18
  return Array.isArray(raw) && raw.every((toolName) => typeof toolName === "string") ? raw : [];
@@ -37,7 +41,7 @@ function createInjectedStudioTool(runId, toolName, description, parameters, sess
37
41
  },
38
42
  };
39
43
  }
40
- function buildMergedTools(agent, input, sessionManager, availableForwardedToolNames) {
44
+ function buildMergedTools(agent, input, sessionManager, availableForwardedToolNames, availableLocalTools) {
41
45
  const injectedTools = Object.fromEntries(input.tools.map((tool) => [
42
46
  tool.name,
43
47
  createInjectedStudioTool(input.runId, tool.name, tool.description, tool.inputSchema ?? tool.parameters, sessionManager),
@@ -63,9 +67,10 @@ function buildMergedTools(agent, input, sessionManager, availableForwardedToolNa
63
67
  for (const [toolName, entry] of Object.entries(agent.config.tools)) {
64
68
  if (entry === true) {
65
69
  if (toolRegistry.get(toolName) ||
70
+ availableLocalTools?.[toolName] ||
66
71
  availableForwardedToolNames?.includes(toolName) ||
67
72
  sourceAllowedRemoteToolNames.includes(toolName)) {
68
- merged[toolName] = true;
73
+ merged[toolName] = availableLocalTools?.[toolName] ?? true;
69
74
  }
70
75
  continue;
71
76
  }
@@ -76,6 +81,69 @@ function buildMergedTools(agent, input, sessionManager, availableForwardedToolNa
76
81
  const filtered = { ...merged, ...injectedTools };
77
82
  return Object.keys(filtered).length > 0 ? filtered : undefined;
78
83
  }
84
+ async function loadDefaultCreateBashTool() {
85
+ const provider = tryResolve(SandboxShellToolsProviderName);
86
+ if (provider)
87
+ return provider;
88
+ const { createBashSandboxShellToolsProvider } = await import("../../extensions/ext-sandbox-shell-tools/src/index.js");
89
+ return createBashSandboxShellToolsProvider;
90
+ }
91
+ function getStringProperty(value, keys) {
92
+ for (const key of keys) {
93
+ const candidate = value[key];
94
+ if (typeof candidate === "string" && candidate.trim()) {
95
+ return candidate.trim();
96
+ }
97
+ }
98
+ return undefined;
99
+ }
100
+ function getAgentSandboxConfig(agent) {
101
+ const config = agent.config;
102
+ if (!isRecord(config.sandbox)) {
103
+ return {};
104
+ }
105
+ return {
106
+ sandboxId: getStringProperty(config.sandbox, ["id", "sandboxId", "sessionId"]),
107
+ projectId: getStringProperty(config.sandbox, ["projectId"]),
108
+ };
109
+ }
110
+ function shouldExposeSandboxBash(agent) {
111
+ const tools = agent.config.tools;
112
+ return isRecord(tools) && tools[PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME] === true;
113
+ }
114
+ async function buildProjectAgentSandboxTools(input) {
115
+ if (!shouldExposeSandboxBash(input.agent)) {
116
+ return {};
117
+ }
118
+ const sandboxConfig = getAgentSandboxConfig(input.agent);
119
+ const createBashTool = input.deps.createBashTool ?? await loadDefaultCreateBashTool();
120
+ const createSandboxTools = input.deps.createAgentServiceSandboxTools ??
121
+ createAgentServiceSandboxTools;
122
+ const sandboxResult = await createSandboxTools({
123
+ createBashTool,
124
+ ...(input.deps.projectAgentSandbox?.apiUrl
125
+ ? { apiUrl: input.deps.projectAgentSandbox.apiUrl }
126
+ : {}),
127
+ ...(input.deps.projectAgentSandbox?.authToken
128
+ ? { authToken: input.deps.projectAgentSandbox.authToken }
129
+ : {}),
130
+ ...(sandboxConfig.sandboxId
131
+ ? { sandboxId: sandboxConfig.sandboxId, deleteOnClose: false }
132
+ : {}),
133
+ getProjectId: () => sandboxConfig.projectId ?? input.deps.projectAgentSandbox?.projectId,
134
+ });
135
+ const bash = sandboxResult.tools[PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME];
136
+ if (!bash) {
137
+ await sandboxResult.closeSandbox();
138
+ return {};
139
+ }
140
+ return {
141
+ tools: {
142
+ [PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME]: bash,
143
+ },
144
+ closeSandbox: sandboxResult.closeSandbox,
145
+ };
146
+ }
79
147
  function isRecord(value) {
80
148
  return typeof value === "object" && value !== null && !Array.isArray(value);
81
149
  }
@@ -126,7 +194,8 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
126
194
  const allowedRemoteToolNames = getAllowedRemoteToolNames(input.forwardedProps);
127
195
  const forwardedIntegrationToolDefs = getForwardedIntegrationToolDefinitions(input.forwardedProps);
128
196
  const availableForwardedToolNames = forwardedIntegrationToolDefs?.map((tool) => tool.name);
129
- const mergedTools = buildMergedTools(agent, input, deps.sessionManager, availableForwardedToolNames);
197
+ const sandboxTools = await buildProjectAgentSandboxTools({ agent, deps });
198
+ const mergedTools = buildMergedTools(agent, input, deps.sessionManager, availableForwardedToolNames, sandboxTools.tools);
130
199
  const runtimeAgent = {
131
200
  ...agent,
132
201
  config: {
@@ -167,6 +236,13 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
167
236
  }
168
237
  catch (error) {
169
238
  deps.sessionManager.failRun(input.runId);
239
+ await sandboxTools.closeSandbox?.().catch((cleanupError) => {
240
+ logger.warn("Internal agent runtime sandbox cleanup failed after setup error", {
241
+ runId: input.runId,
242
+ agentId: agent.id,
243
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
244
+ });
245
+ });
170
246
  logger.error("Internal agent runtime stream setup failed", {
171
247
  runId: input.runId,
172
248
  threadId: input.threadId,
@@ -303,6 +379,13 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
303
379
  if (clientAttached) {
304
380
  controller.close();
305
381
  }
382
+ await sandboxTools.closeSandbox?.().catch((cleanupError) => {
383
+ logger.warn("Internal agent runtime sandbox cleanup failed", {
384
+ runId: input.runId,
385
+ agentId: agent.id,
386
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
387
+ });
388
+ });
306
389
  logger.debug("Internal agent runtime stream response closed", {
307
390
  runId: input.runId,
308
391
  threadId: input.threadId,
@@ -1,6 +1,15 @@
1
1
  import { type BackgroundCommand, type BackgroundCommandOutput, type ExecOptions, type ExecResult, type ExecStreamEvent, type SandboxOptions } from "./types.js";
2
2
  /** Options accepted by lazy sandbox. */
3
3
  export interface LazySandboxOptions extends SandboxOptions {
4
+ /**
5
+ * Optional existing sandbox session to attach to instead of creating a new one.
6
+ * Attached sessions are detached, not deleted, on close unless deleteOnClose is true.
7
+ */
8
+ sandboxId?: string;
9
+ /** Optional known endpoint for sandboxId; avoids the initial control-plane lookup. */
10
+ sandboxEndpoint?: string;
11
+ /** Delete the sandbox when closing. Defaults to true for created sessions and false for sandboxId. */
12
+ deleteOnClose?: boolean;
4
13
  getProjectId?: () => string | null | undefined;
5
14
  startupTimeoutMs?: number;
6
15
  pollIntervalMs?: number;
@@ -23,6 +32,9 @@ export declare function resolveDefaultSandboxRuntimeEndpoint(input: {
23
32
  export declare class LazySandbox {
24
33
  private readonly apiUrl;
25
34
  private readonly authToken;
35
+ private readonly sandboxId;
36
+ private readonly sandboxEndpoint;
37
+ private readonly deleteOnClose;
26
38
  private readonly getProjectId;
27
39
  private readonly startupTimeoutMs;
28
40
  private readonly pollIntervalMs;
@@ -63,6 +75,8 @@ export declare class LazySandbox {
63
75
  get isActive(): boolean;
64
76
  private bootstrapSession;
65
77
  private resolveReadyEndpoint;
78
+ private attachExistingSession;
79
+ private getSession;
66
80
  private waitForReadySession;
67
81
  private touchSession;
68
82
  private startHeartbeatLoop;
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAE5B,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAEpB,wCAAwC;AACxC,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sGAAsG;IACtG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;CACrF;AA4BD,iDAAiD;AACjD,wBAAgB,oCAAoC,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAmBxF;AAED,4EAA4E;AAC5E,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAK/B;IAEd,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAA6B;gBAElE,OAAO,GAAE,kBAAuB;IAoBtC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBvB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAsB1E,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,eAAe,CAAC;IA0CvF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBvC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1E,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqB1F,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkBnE,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAyB/E,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAkBtD,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmBtE,SAAS,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,CAEtB;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;YAEa,gBAAgB;YAsChB,oBAAoB;YAQpB,qBAAqB;YAkBrB,UAAU;YAcV,mBAAmB;YA4BnB,YAAY;IAc1B,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,iBAAiB;YAMX,aAAa;IAO3B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,kBAAkB;YAKZ,gCAAgC;IAU9C,OAAO,CAAC,8BAA8B;YAmBxB,SAAS;YAkCT,cAAc;YAId,YAAY;IAI1B,OAAO,CAAC,qBAAqB;YAIf,gCAAgC;IAU9C,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,WAAW;CAMpB"}
@@ -40,6 +40,9 @@ export function resolveDefaultSandboxRuntimeEndpoint(input) {
40
40
  export class LazySandbox {
41
41
  apiUrl;
42
42
  authToken;
43
+ sandboxId;
44
+ sandboxEndpoint;
45
+ deleteOnClose;
43
46
  getProjectId;
44
47
  startupTimeoutMs;
45
48
  pollIntervalMs;
@@ -62,6 +65,9 @@ export class LazySandbox {
62
65
  constructor(options = {}) {
63
66
  this.apiUrl = resolveSandboxApiUrl(options);
64
67
  this.authToken = resolveSandboxAuthToken(options);
68
+ this.sandboxId = options.sandboxId?.trim() || undefined;
69
+ this.sandboxEndpoint = options.sandboxEndpoint?.trim() || undefined;
70
+ this.deleteOnClose = options.deleteOnClose ?? !this.sandboxId;
65
71
  this.getProjectId = options.getProjectId ?? (() => options.projectId);
66
72
  this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
67
73
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -313,7 +319,9 @@ export class LazySandbox {
313
319
  this.resetSessionState();
314
320
  return;
315
321
  }
316
- await this.deleteSession(currentSessionId);
322
+ if (this.deleteOnClose) {
323
+ await this.deleteSession(currentSessionId);
324
+ }
317
325
  this.resetSessionState(currentSessionId);
318
326
  })(),
319
327
  };
@@ -337,6 +345,10 @@ export class LazySandbox {
337
345
  return this.endpoint !== null;
338
346
  }
339
347
  async bootstrapSession() {
348
+ if (this.sandboxId) {
349
+ await this.attachExistingSession(this.sandboxId);
350
+ return;
351
+ }
340
352
  const projectId = this.resolveProjectId();
341
353
  const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions`, {
342
354
  method: "POST",
@@ -359,7 +371,7 @@ export class LazySandbox {
359
371
  }
360
372
  catch (error) {
361
373
  const currentSessionId = this.sessionId;
362
- if (currentSessionId) {
374
+ if (currentSessionId && this.deleteOnClose) {
363
375
  await this.deleteSession(currentSessionId);
364
376
  }
365
377
  this.resetSessionState(currentSessionId ?? undefined);
@@ -372,6 +384,34 @@ export class LazySandbox {
372
384
  }
373
385
  return (await this.waitForReadySession(session.id)).endpoint;
374
386
  }
387
+ async attachExistingSession(sessionId) {
388
+ const projectId = this.resolveProjectId();
389
+ this.sessionId = sessionId;
390
+ this.sessionProjectId = projectId;
391
+ try {
392
+ const session = this.sandboxEndpoint
393
+ ? { id: sessionId, endpoint: this.sandboxEndpoint, status: "running" }
394
+ : await this.getSession(sessionId);
395
+ this.endpoint = await this.resolveReadyEndpoint(session);
396
+ await this.heartbeat(true);
397
+ this.startHeartbeatLoop();
398
+ }
399
+ catch (error) {
400
+ this.resetSessionState(sessionId);
401
+ throw error;
402
+ }
403
+ }
404
+ async getSession(sessionId) {
405
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
406
+ headers: this.authHeaders(),
407
+ });
408
+ if (!res.ok) {
409
+ throw REQUEST_ERROR.create({
410
+ detail: `Failed to get sandbox: ${res.status} ${await res.text()}`,
411
+ });
412
+ }
413
+ return await res.json();
414
+ }
375
415
  async waitForReadySession(sessionId) {
376
416
  const start = Date.now();
377
417
  while (Date.now() - start < this.startupTimeoutMs) {
@@ -396,7 +436,7 @@ export class LazySandbox {
396
436
  }
397
437
  async touchSession() {
398
438
  const projectId = this.resolveProjectId();
399
- if (this.endpoint && this.sessionProjectId !== projectId) {
439
+ if (!this.sandboxId && this.endpoint && this.sessionProjectId !== projectId) {
400
440
  const currentSessionId = this.sessionId;
401
441
  if (currentSessionId) {
402
442
  await this.deleteSession(currentSessionId);
@@ -514,7 +554,9 @@ export class LazySandbox {
514
554
  const sessionId = this.sessionId;
515
555
  if (!sessionId)
516
556
  return;
517
- await this.deleteSession(sessionId);
557
+ if (this.deleteOnClose) {
558
+ await this.deleteSession(sessionId);
559
+ }
518
560
  this.resetSessionState(sessionId);
519
561
  }
520
562
  resolveRuntimeEndpoint() {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AAwBnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAUnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;CACpE;AAyND,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAwBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAwJxE"}
1
+ {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AAwBnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAUnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;CACpE;AAyND,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAwBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA+JxE"}