@truefoundry/trueforge-core 0.1.3 → 0.1.4-rc.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 (111) hide show
  1. package/dist/agent-session/TurnResourceResolver.d.ts.map +1 -1
  2. package/dist/agent-session/TurnResourceResolver.js +1 -4
  3. package/dist/agent-session/TurnResourceResolver.js.map +1 -1
  4. package/dist/agent-session/TurnResourceResolver.mjs +1 -4
  5. package/dist/agent-session/TurnResourceResolver.mjs.map +1 -1
  6. package/dist/agent-session/index.d.ts +1 -1
  7. package/dist/agent-session/index.d.ts.map +1 -1
  8. package/dist/agent-session/index.js.map +1 -1
  9. package/dist/agent-session/index.mjs.map +1 -1
  10. package/dist/agent-session/schemas/agentSpec.d.ts +7 -28
  11. package/dist/agent-session/schemas/agentSpec.d.ts.map +1 -1
  12. package/dist/agent-session/schemas/agentSpec.js +15 -43
  13. package/dist/agent-session/schemas/agentSpec.js.map +1 -1
  14. package/dist/agent-session/schemas/agentSpec.mjs +15 -43
  15. package/dist/agent-session/schemas/agentSpec.mjs.map +1 -1
  16. package/dist/agent-session/schemas/session.d.ts +0 -39
  17. package/dist/agent-session/schemas/session.d.ts.map +1 -1
  18. package/dist/core/index.d.ts +4 -1
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/core/index.js +12 -0
  21. package/dist/core/index.js.map +1 -1
  22. package/dist/core/index.mjs +7 -0
  23. package/dist/core/index.mjs.map +1 -1
  24. package/dist/core/runtime/AgentThread.d.ts.map +1 -1
  25. package/dist/core/runtime/AgentThread.js +25 -11
  26. package/dist/core/runtime/AgentThread.js.map +1 -1
  27. package/dist/core/runtime/AgentThread.mjs +25 -11
  28. package/dist/core/runtime/AgentThread.mjs.map +1 -1
  29. package/dist/core/sandbox/Sandbox.d.ts +15 -4
  30. package/dist/core/sandbox/Sandbox.d.ts.map +1 -1
  31. package/dist/core/sandbox/Sandbox.js +200 -80
  32. package/dist/core/sandbox/Sandbox.js.map +1 -1
  33. package/dist/core/sandbox/Sandbox.mjs +201 -81
  34. package/dist/core/sandbox/Sandbox.mjs.map +1 -1
  35. package/dist/core/sandbox/SandboxErrors.d.ts +7 -3
  36. package/dist/core/sandbox/SandboxErrors.d.ts.map +1 -1
  37. package/dist/core/sandbox/SandboxErrors.js +6 -6
  38. package/dist/core/sandbox/SandboxErrors.js.map +1 -1
  39. package/dist/core/sandbox/SandboxErrors.mjs +6 -6
  40. package/dist/core/sandbox/SandboxErrors.mjs.map +1 -1
  41. package/dist/core/sandbox/codeMode/CodeModeTransport.d.ts +22 -0
  42. package/dist/core/sandbox/codeMode/CodeModeTransport.d.ts.map +1 -1
  43. package/dist/core/sandbox/codeMode/CodeModeTransport.js.map +1 -1
  44. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.d.ts +9 -1
  45. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.d.ts.map +1 -1
  46. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.js +12 -0
  47. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.js.map +1 -1
  48. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.mjs +12 -0
  49. package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.mjs.map +1 -1
  50. package/dist/core/sandbox/constants.d.ts +0 -2
  51. package/dist/core/sandbox/constants.d.ts.map +1 -1
  52. package/dist/core/sandbox/constants.js +2 -5
  53. package/dist/core/sandbox/constants.js.map +1 -1
  54. package/dist/core/sandbox/constants.mjs +1 -3
  55. package/dist/core/sandbox/constants.mjs.map +1 -1
  56. package/dist/core/sandbox/provider/DaytonaProvider.d.ts +4 -0
  57. package/dist/core/sandbox/provider/DaytonaProvider.d.ts.map +1 -1
  58. package/dist/core/sandbox/provider/DaytonaProvider.js +27 -5
  59. package/dist/core/sandbox/provider/DaytonaProvider.js.map +1 -1
  60. package/dist/core/sandbox/provider/DaytonaProvider.mjs +27 -4
  61. package/dist/core/sandbox/provider/DaytonaProvider.mjs.map +1 -1
  62. package/dist/core/sandbox/provider/Provider.d.ts +13 -1
  63. package/dist/core/sandbox/provider/Provider.d.ts.map +1 -1
  64. package/dist/core/sandbox/provider/Provider.js.map +1 -1
  65. package/dist/core/sandbox/provider/Provider.mjs.map +1 -1
  66. package/dist/core/sandbox/provider/TFYSandboxProvider.d.ts +17 -2
  67. package/dist/core/sandbox/provider/TFYSandboxProvider.d.ts.map +1 -1
  68. package/dist/core/sandbox/provider/TFYSandboxProvider.js +81 -17
  69. package/dist/core/sandbox/provider/TFYSandboxProvider.js.map +1 -1
  70. package/dist/core/sandbox/provider/TFYSandboxProvider.mjs +82 -13
  71. package/dist/core/sandbox/provider/TFYSandboxProvider.mjs.map +1 -1
  72. package/dist/core/sandbox/provider/execEnv.d.ts +6 -0
  73. package/dist/core/sandbox/provider/execEnv.d.ts.map +1 -0
  74. package/dist/core/sandbox/provider/execEnv.js +55 -0
  75. package/dist/core/sandbox/provider/execEnv.js.map +1 -0
  76. package/dist/core/sandbox/provider/execEnv.mjs +30 -0
  77. package/dist/core/sandbox/provider/execEnv.mjs.map +1 -0
  78. package/dist/core/sandbox/sandboxImage.d.ts +2 -5
  79. package/dist/core/sandbox/sandboxImage.d.ts.map +1 -1
  80. package/dist/core/sandbox/sandboxImage.js +8 -1
  81. package/dist/core/sandbox/sandboxImage.js.map +1 -1
  82. package/dist/core/sandbox/sandboxImage.mjs +6 -1
  83. package/dist/core/sandbox/sandboxImage.mjs.map +1 -1
  84. package/dist/core/sandbox/sandboxRef.d.ts +26 -0
  85. package/dist/core/sandbox/sandboxRef.d.ts.map +1 -0
  86. package/dist/core/sandbox/sandboxRef.js +69 -0
  87. package/dist/core/sandbox/sandboxRef.js.map +1 -0
  88. package/dist/core/sandbox/sandboxRef.mjs +41 -0
  89. package/dist/core/sandbox/sandboxRef.mjs.map +1 -0
  90. package/dist/core/sandbox/skills/ISkillMounter.d.ts +7 -2
  91. package/dist/core/sandbox/skills/ISkillMounter.d.ts.map +1 -1
  92. package/dist/core/sandbox/skills/ISkillMounter.js.map +1 -1
  93. package/dist/core/sandbox/skills/SkillMounter.d.ts +7 -2
  94. package/dist/core/sandbox/skills/SkillMounter.d.ts.map +1 -1
  95. package/dist/core/sandbox/skills/SkillMounter.js +5 -6
  96. package/dist/core/sandbox/skills/SkillMounter.js.map +1 -1
  97. package/dist/core/sandbox/skills/SkillMounter.mjs +5 -6
  98. package/dist/core/sandbox/skills/SkillMounter.mjs.map +1 -1
  99. package/dist/core/sandbox/skills/constants.d.ts +4 -2
  100. package/dist/core/sandbox/skills/constants.d.ts.map +1 -1
  101. package/dist/core/sandbox/skills/constants.js +2 -5
  102. package/dist/core/sandbox/skills/constants.js.map +1 -1
  103. package/dist/core/sandbox/skills/constants.mjs +2 -4
  104. package/dist/core/sandbox/skills/constants.mjs.map +1 -1
  105. package/dist/core/sandbox/skills/index.d.ts +1 -1
  106. package/dist/core/sandbox/skills/index.d.ts.map +1 -1
  107. package/dist/core/sandbox/skills/index.js +0 -2
  108. package/dist/core/sandbox/skills/index.js.map +1 -1
  109. package/dist/core/sandbox/skills/index.mjs +1 -2
  110. package/dist/core/sandbox/skills/index.mjs.map +1 -1
  111. package/package.json +1 -1
@@ -14,29 +14,36 @@ import {
14
14
  } from "../runtime/DeferredTool.mjs";
15
15
  import { extractErrorLogFields } from "../util/errorLogFields.mjs";
16
16
  import { CodeModeDispatcher } from "./codeMode/CodeModeDispatcher.mjs";
17
- import { SANDBOX_FILE_UPLOADS_DIR } from "./constants.mjs";
17
+ import "./codeMode/CodeModeTransport.mjs";
18
18
  import { ensureExecSuccess, shellEscape } from "./provider/Provider.mjs";
19
- import { validateNoPathTraversal, validateSandboxOwnedByTenant } from "./SandboxErrors.mjs";
20
- import { sandboxScripts } from "./sandboxScripts.gen.mjs";
21
- import { SKILLS_DIR } from "./skills/constants.mjs";
19
+ import { SandboxNotAvailableError, validateNoPathTraversal } from "./SandboxErrors.mjs";
20
+ import { formatSandboxId, rawSandboxId } from "./sandboxRef.mjs";
21
+ import { dirname, join, relative } from "path";
22
+ function mcpClientLayout(remotePath) {
23
+ const pythonPath = dirname(remotePath);
24
+ const binDir = join(pythonPath, "bin");
25
+ return { pythonPath, binDir, binLink: join(binDir, "mcp-client") };
26
+ }
22
27
  var SKILL_DOWNLOAD_TIMEOUT_SECONDS = 180;
23
28
  var NATS_REQUEST_TIMEOUT_BUFFER_SECONDS = 30;
24
29
  function buildWriteAndRunScriptCommand(params) {
25
30
  const b64 = Buffer.from(params.scriptContent, "utf-8").toString("base64");
31
+ const scriptPath = shellEscape(params.scriptPath);
26
32
  return [
27
- `mkdir -p "$(dirname ${params.scriptPath})"`,
28
- `rm -f ${params.scriptPath}`,
29
- `echo ${shellEscape(b64)} | base64 -d > ${params.scriptPath}`,
30
- `chmod a-w ${params.scriptPath}`,
31
- `python3 ${params.scriptPath}`
33
+ `mkdir -p "$(dirname ${scriptPath})"`,
34
+ `rm -f ${scriptPath}`,
35
+ `echo ${shellEscape(b64)} | base64 -d > ${scriptPath}`,
36
+ `chmod a-w ${scriptPath}`,
37
+ `python3 ${scriptPath}`
32
38
  ].join(" && ");
33
39
  }
34
40
  function buildSyncGitCredentialsCommand(credentialsContent, credentialsPath) {
41
+ const path = shellEscape(credentialsPath);
35
42
  if (credentialsContent === null) {
36
- return `rm -f ${credentialsPath}`;
43
+ return `rm -f ${path}`;
37
44
  }
38
45
  const b64 = Buffer.from(credentialsContent, "utf-8").toString("base64");
39
- return `mkdir -p "$(dirname ${credentialsPath})" && echo '${b64}' | base64 -d > ${credentialsPath} && chmod 600 ${credentialsPath}`;
46
+ return `mkdir -p "$(dirname ${path})" && echo '${b64}' | base64 -d > ${path} && chmod 600 ${path}`;
40
47
  }
41
48
  function buildGitCredentialHelperEnv(credentialsPath) {
42
49
  return {
@@ -50,9 +57,6 @@ var SANDBOX_MCP_REMINDER_TAG = "sandbox-mcp-code-mode";
50
57
  var SANDBOX_FILE_OUTPUT_TAG = "sandbox-file-output";
51
58
  var SANDBOX_SCHEMA_INFER_TAG = "sandbox-schema-infer";
52
59
  var SANDBOX_FILE_UPLOADS_TAG = "sandbox-file-uploads";
53
- var MCP_CLIENT_DIR = "/opt/tfy/mcp-client";
54
- var MCP_CLIENT_PATH = `${MCP_CLIENT_DIR}/mcp_client.py`;
55
- var MCP_CLIENT_BIN_SYMLINK = "/usr/local/bin/mcp-client";
56
60
  function injectTraceContextEnv() {
57
61
  const carrier = {};
58
62
  propagation.inject(context.active(), carrier);
@@ -68,9 +72,13 @@ function injectTraceContextEnv() {
68
72
  function injectMCPClientEnv(params) {
69
73
  const codeModeEnv = params.codeModeEnv ?? {};
70
74
  const hasCodeModeTransport = Object.keys(codeModeEnv).length > 0;
75
+ const install = params.mcpClientInstall;
76
+ const layout = install === void 0 ? void 0 : mcpClientLayout(install.remotePath);
71
77
  return {
72
78
  ...params.env ?? {},
73
- PYTHONPATH: MCP_CLIENT_DIR,
79
+ ...layout !== void 0 && {
80
+ PYTHONPATH: layout.pythonPath
81
+ },
74
82
  ...Object.keys(params.mcpServers).length > 0 && {
75
83
  TFY_MCP_SERVERS: Buffer.from(JSON.stringify(params.mcpServers)).toString("base64")
76
84
  },
@@ -102,14 +110,12 @@ function createSandboxLargeToolResponseGuidance() {
102
110
  return `For use cases where the Agent needs a subset of fields from the MCP response or needs deterministic processing, write code with the provided MCP client and use <${SANDBOX_MCP_REMINDER_TAG}>.`;
103
111
  }
104
112
  var SANDBOX_MCP_SERVER_ID = "sandbox";
105
- var Sandbox = class _Sandbox extends LocalToolMCP {
113
+ var Sandbox = class extends LocalToolMCP {
106
114
  name = SANDBOX_MCP_SERVER_ID;
107
115
  displayName = "Sandbox";
108
116
  description = "Persistent sandbox environment for code execution";
109
- static FILE_UPLOADS_DIR = SANDBOX_FILE_UPLOADS_DIR;
110
117
  provider;
111
118
  existingSandboxId;
112
- tenantName;
113
119
  existingSandboxInfo;
114
120
  // Cached promise to prevent concurrent sub-agents from creating duplicate sandboxes.
115
121
  sandboxCreationPromise;
@@ -119,12 +125,13 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
119
125
  requestTimeoutSeconds;
120
126
  skillMounter;
121
127
  cachedSkillsSection;
122
- mcpClientScriptBase64;
123
128
  logger;
124
129
  // Pre-resolved credential-store file content (null = clear / no git auth).
125
130
  resolvedGitCredentialsContent;
126
131
  codeModeDispatcher;
127
132
  codeModeTransport;
133
+ /** Cached from transport.getClientInstall after sandbox init (when Code Mode is configured). */
134
+ mcpClientInstall;
128
135
  tools = [
129
136
  defineTool({
130
137
  name: SANDBOX_EXEC_TOOL_NAME,
@@ -137,19 +144,24 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
137
144
  super({ tracing: options.tracing });
138
145
  this.provider = options.provider;
139
146
  this.existingSandboxId = options.existingSandboxId;
140
- this.tenantName = options.tenantName;
141
147
  this.skillMounter = options.skillMounter;
142
148
  this.fileDownloadEnabled = options.fileDownloadEnabled ?? false;
143
149
  const mcpBoundTimeoutMs = options.mcpRequestTimeoutMs + options.mcpConnectTimeoutMs;
144
150
  this.requestTimeoutSeconds = Math.ceil(mcpBoundTimeoutMs / 1e3) + NATS_REQUEST_TIMEOUT_BUFFER_SECONDS;
145
- this.mcpClientScriptBase64 = Buffer.from(sandboxScripts.mcpClient, "utf-8").toString("base64");
146
151
  this.logger = options.logger.child({ module: "Sandbox" });
147
152
  this.resolvedGitCredentialsContent = options.resolvedGitCredentialsContent ?? null;
148
153
  if (this.existingSandboxId) {
149
- validateSandboxOwnedByTenant(this.existingSandboxId, this.tenantName);
150
154
  this.existingSandboxInfo = { sandbox_id: this.existingSandboxId };
151
155
  }
152
156
  }
157
+ /** Provider-facing id (unwraps `v1:type:raw`; legacy ids pass through). */
158
+ providerSandboxId(sessionId) {
159
+ return rawSandboxId(sessionId);
160
+ }
161
+ /** Raw id for prompt layout paths before create (empty when this turn has no existing sandbox). */
162
+ promptSandboxId() {
163
+ return this.existingSandboxId === void 0 ? "" : rawSandboxId(this.existingSandboxId);
164
+ }
153
165
  buildMcpServersEnvelope() {
154
166
  const out = {};
155
167
  for (const server of this.codeExecToolSets) {
@@ -179,7 +191,7 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
179
191
  renderSkillsSection() {
180
192
  if (this.cachedSkillsSection === void 0) {
181
193
  const skills = new InstructionBuilder("skills");
182
- this.skillMounter?.instruction(skills);
194
+ this.skillMounter?.instruction(skills, { skillsDir: this.provider.getSkillsDir(this.promptSandboxId()) });
183
195
  this.cachedSkillsSection = skills.build();
184
196
  }
185
197
  return this.cachedSkillsSection;
@@ -203,7 +215,7 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
203
215
  buildFileUploadsSection(builder) {
204
216
  builder.addSection(
205
217
  SANDBOX_FILE_UPLOADS_TAG,
206
- `User-uploaded files are placed in ${_Sandbox.FILE_UPLOADS_DIR}/. The Agent must not modify files in this directory. Always create a copy at a different location and work on the copy.`
218
+ `User-uploaded files are placed in ${this.provider.getFileUploadsDir(this.promptSandboxId())}/. The Agent must not modify files in this directory. Always create a copy at a different location and work on the copy.`
207
219
  );
208
220
  }
209
221
  buildSchemaSection(builder) {
@@ -325,86 +337,168 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
325
337
  }
326
338
  return this.existingSandboxInfo;
327
339
  }
340
+ clearExistingSandbox() {
341
+ this.existingSandboxInfo = void 0;
342
+ this.sandboxCreationPromise = void 0;
343
+ this.sandboxInitPromise = void 0;
344
+ this.mcpClientInstall = void 0;
345
+ }
328
346
  async ensureSandboxCreated() {
329
347
  if (this.existingSandboxInfo) {
330
348
  return { sandboxInfo: this.existingSandboxInfo, sandboxCreated: void 0 };
331
349
  }
332
- this.sandboxCreationPromise ??= this.provider.createSandbox().then(({ sandboxId }) => {
333
- validateSandboxOwnedByTenant(sandboxId, this.tenantName);
334
- return { sandbox_id: sandboxId };
335
- }).catch((e) => {
350
+ this.sandboxCreationPromise ??= this.provider.createSandbox().then(({ sandboxId }) => ({
351
+ sandbox_id: formatSandboxId({ providerType: this.provider.type, rawId: sandboxId })
352
+ })).catch((e) => {
336
353
  this.sandboxCreationPromise = void 0;
337
354
  throw e;
338
355
  });
339
356
  const sandboxInfo = await this.sandboxCreationPromise;
340
- const prior = this.existingSandboxInfo;
341
- const isNew = prior === void 0;
357
+ const raced = this.peekExistingSandboxInfo();
358
+ if (raced !== void 0) {
359
+ return { sandboxInfo: raced, sandboxCreated: void 0 };
360
+ }
342
361
  this.existingSandboxInfo = sandboxInfo;
343
- return { sandboxInfo, sandboxCreated: isNew ? sandboxInfo : void 0 };
362
+ return { sandboxInfo, sandboxCreated: sandboxInfo };
344
363
  }
345
- async handleExec(input) {
346
- const { sandboxInfo, sandboxCreated } = await this.ensureSandboxCreated();
347
- const sandboxCreatedFlag = Boolean(sandboxCreated);
364
+ peekExistingSandboxInfo() {
365
+ return this.existingSandboxInfo;
366
+ }
367
+ /** Same-type missing remote/local root: drop the stale id and create a new fancy id. */
368
+ async recreateAfterUnavailable() {
369
+ this.clearExistingSandbox();
370
+ const created = await this.ensureSandboxCreated();
371
+ if (created.sandboxCreated === void 0) {
372
+ throw new Error("Sandbox recreate did not create a new sandbox");
373
+ }
374
+ return { sandboxInfo: created.sandboxInfo, sandboxCreated: created.sandboxCreated };
375
+ }
376
+ /**
377
+ * Create or reattach, then init. A missing same-type sandbox throws
378
+ * `SandboxNotAvailableError` from the provider; reattach then recreates.
379
+ */
380
+ async ensureReadySandbox() {
381
+ const first = await this.ensureSandboxCreated();
348
382
  try {
349
383
  await this.ensureSandboxInitialized();
384
+ return first;
385
+ } catch (error) {
386
+ if (!(error instanceof SandboxNotAvailableError) || first.sandboxCreated !== void 0) {
387
+ throw error;
388
+ }
389
+ const recreated = await this.recreateAfterUnavailable();
390
+ await this.ensureSandboxInitialized();
391
+ return recreated;
392
+ }
393
+ }
394
+ async handleExec(input) {
395
+ let sandboxInfo;
396
+ let sandboxCreated;
397
+ try {
398
+ ({ sandboxInfo, sandboxCreated } = await this.ensureReadySandbox());
350
399
  } catch (e) {
351
400
  this.logger.error("Sandbox initialization failed", extractErrorLogFields(e));
352
401
  const message = e instanceof Error ? e.message : "Sandbox initialization failed";
402
+ const fallback = this.existingSandboxInfo;
353
403
  return toolResultResponse({
354
404
  text: `Sandbox initialization failed: ${message}`,
355
405
  isError: true,
356
- overrides: { sandboxCreated: sandboxCreatedFlag, sandboxInfo }
406
+ overrides: {
407
+ sandboxCreated: Boolean(fallback),
408
+ ...fallback !== void 0 ? { sandboxInfo: fallback } : {}
409
+ }
357
410
  });
358
411
  }
359
- const codeModeEnv = await this.ensureCodeModeStarted(sandboxInfo.sandbox_id);
412
+ const sandboxCreatedFlag = Boolean(sandboxCreated);
413
+ const rawId = this.providerSandboxId(sandboxInfo.sandbox_id);
414
+ const codeModeEnv = await this.ensureCodeModeStarted(rawId);
360
415
  const mcpClientEnv = injectMCPClientEnv({
361
416
  env: input.env,
362
417
  mcpServers: this.buildMcpServersEnvelope(),
363
- codeModeEnv
418
+ codeModeEnv,
419
+ mcpClientInstall: this.mcpClientInstall
364
420
  });
365
- const gitAuthEnv = this.resolvedGitCredentialsContent !== null ? buildGitCredentialHelperEnv(this.provider.getGitCredentialsPath(sandboxInfo.sandbox_id)) : {};
421
+ const gitAuthEnv = this.resolvedGitCredentialsContent !== null ? buildGitCredentialHelperEnv(this.provider.getGitCredentialsPath(rawId)) : {};
366
422
  const env = { ...mcpClientEnv, ...gitAuthEnv };
367
- const result = await this.provider.exec({
368
- sandboxId: sandboxInfo.sandbox_id,
369
- command: input.command,
370
- cwd: input.cwd,
371
- env
372
- });
373
- return {
374
- result: {
375
- content: [{ type: "text", text: JSON.stringify(result) }],
376
- isError: !result.success
377
- },
378
- wasInitialized: void 0,
379
- sandboxCreated: sandboxCreatedFlag,
380
- sandboxInfo
381
- };
423
+ try {
424
+ const result = await this.provider.exec({
425
+ sandboxId: rawId,
426
+ command: input.command,
427
+ cwd: input.cwd,
428
+ env
429
+ });
430
+ return {
431
+ result: {
432
+ content: [{ type: "text", text: JSON.stringify(result) }],
433
+ isError: !result.success
434
+ },
435
+ wasInitialized: void 0,
436
+ sandboxCreated: sandboxCreatedFlag,
437
+ sandboxInfo
438
+ };
439
+ } catch (error) {
440
+ if (!(error instanceof SandboxNotAvailableError) || sandboxCreated !== void 0) {
441
+ throw error;
442
+ }
443
+ const recreated = await this.recreateAfterUnavailable();
444
+ await this.ensureSandboxInitialized();
445
+ const recreatedRawId = this.providerSandboxId(recreated.sandboxInfo.sandbox_id);
446
+ const retryCodeModeEnv = await this.ensureCodeModeStarted(recreatedRawId);
447
+ const retryEnv = {
448
+ ...injectMCPClientEnv({
449
+ env: input.env,
450
+ mcpServers: this.buildMcpServersEnvelope(),
451
+ codeModeEnv: retryCodeModeEnv,
452
+ mcpClientInstall: this.mcpClientInstall
453
+ }),
454
+ ...this.resolvedGitCredentialsContent !== null ? buildGitCredentialHelperEnv(this.provider.getGitCredentialsPath(recreatedRawId)) : {}
455
+ };
456
+ const retryResult = await this.provider.exec({
457
+ sandboxId: recreatedRawId,
458
+ command: input.command,
459
+ cwd: input.cwd,
460
+ env: retryEnv
461
+ });
462
+ return {
463
+ result: {
464
+ content: [{ type: "text", text: JSON.stringify(retryResult) }],
465
+ isError: !retryResult.success
466
+ },
467
+ wasInitialized: void 0,
468
+ sandboxCreated: true,
469
+ sandboxInfo: recreated.sandboxInfo
470
+ };
471
+ }
382
472
  }
383
473
  async getToolResultDumpDir() {
384
- const { sandboxCreated } = await this.ensureSandboxCreated();
385
- const dir = this.provider.getToolResultDumpDir(this.requiredSandboxInfo.sandbox_id).replace(/\/+$/, "");
474
+ const { sandboxCreated, sandboxInfo } = await this.ensureSandboxCreated();
475
+ const dir = this.provider.getToolResultDumpDir(this.providerSandboxId(sandboxInfo.sandbox_id)).replace(/\/+$/, "");
386
476
  return { dir, sandboxCreated };
387
477
  }
388
478
  async uploadFile(params) {
389
- const { sandboxCreated } = await this.ensureSandboxCreated();
390
- await this.ensureSandboxInitialized();
391
- const { sandbox_id: sandboxId } = this.requiredSandboxInfo;
479
+ const { sandboxCreated, sandboxInfo } = await this.ensureReadySandbox();
392
480
  const fileName = params.fileName.replace(/^\/+/, "");
393
481
  const targetDir = params.targetDir.replace(/\/+$/, "");
394
482
  const filePath = `${targetDir}/${fileName}`;
395
- await this.provider.uploadFile({ sandboxId, remotePath: filePath, content: params.content });
483
+ await this.provider.uploadFile({
484
+ sandboxId: this.providerSandboxId(sandboxInfo.sandbox_id),
485
+ remotePath: filePath,
486
+ content: params.content
487
+ });
396
488
  return { filePath, sandboxCreated };
397
489
  }
398
490
  async uploadUserFile(input) {
399
491
  validateNoPathTraversal(input.fileName);
492
+ const { sandboxCreated, sandboxInfo } = await this.ensureReadySandbox();
400
493
  const result = await this.uploadFile({
401
- targetDir: _Sandbox.FILE_UPLOADS_DIR,
494
+ targetDir: this.provider.getFileUploadsDir(this.providerSandboxId(sandboxInfo.sandbox_id)),
402
495
  fileName: input.fileName,
403
496
  content: input.content
404
497
  });
498
+ const created = sandboxCreated ?? result.sandboxCreated;
405
499
  return {
406
500
  filePath: result.filePath,
407
- ...result.sandboxCreated && { sandboxCreated: result.sandboxCreated }
501
+ ...created && { sandboxCreated: created }
408
502
  };
409
503
  }
410
504
  async writeArtifact(input) {
@@ -433,7 +527,7 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
433
527
  await this.sandboxInitPromise;
434
528
  }
435
529
  async writeGitCredentials() {
436
- const sandboxId = this.requiredSandboxInfo.sandbox_id;
530
+ const sandboxId = this.providerSandboxId(this.requiredSandboxInfo.sandbox_id);
437
531
  const credentialsPath = this.provider.getGitCredentialsPath(sandboxId);
438
532
  const result = await this.provider.exec({
439
533
  sandboxId,
@@ -442,30 +536,56 @@ var Sandbox = class _Sandbox extends LocalToolMCP {
442
536
  ensureExecSuccess(result);
443
537
  }
444
538
  async initSandboxEnvironment() {
445
- const toolResultDumpDir = this.provider.getToolResultDumpDir(this.requiredSandboxInfo.sandbox_id);
539
+ const sandboxId = this.providerSandboxId(this.requiredSandboxInfo.sandbox_id);
540
+ const fileUploadsDir = this.provider.getFileUploadsDir(sandboxId);
541
+ const skillsDir = this.provider.getSkillsDir(sandboxId);
542
+ const toolResultDumpDir = this.provider.getToolResultDumpDir(sandboxId);
446
543
  this.logger.info("Uploading MCP client script and preparing skills directory in sandbox");
447
- const initSteps = [
448
- `mkdir -p ${MCP_CLIENT_DIR} ${_Sandbox.FILE_UPLOADS_DIR} ${toolResultDumpDir} ${SKILLS_DIR}`,
449
- `rm -f ${MCP_CLIENT_PATH}`,
450
- `echo '${this.mcpClientScriptBase64}' | base64 -d > ${MCP_CLIENT_PATH}`,
451
- // Make executable + symlink onto PATH so `mcp-client` runs by name.
452
- `chmod 0555 ${MCP_CLIENT_PATH}`,
453
- `ln -sf ${MCP_CLIENT_PATH} ${MCP_CLIENT_BIN_SYMLINK}`
454
- ];
455
- const skillInit = this.skillMounter?.getSandboxInit();
544
+ this.mcpClientInstall = this.codeModeTransport?.getClientInstall({ sandboxId });
545
+ const install = this.mcpClientInstall;
546
+ const dirs = [shellEscape(fileUploadsDir), shellEscape(toolResultDumpDir), shellEscape(skillsDir)];
547
+ if (install !== void 0) {
548
+ const { pythonPath, binDir } = mcpClientLayout(install.remotePath);
549
+ dirs.push(shellEscape(pythonPath));
550
+ if (install.pathBinSymlink === void 0) {
551
+ dirs.push(shellEscape(binDir));
552
+ }
553
+ }
554
+ ensureExecSuccess(await this.provider.exec({ sandboxId, command: `mkdir -p ${dirs.join(" ")}` }));
555
+ const initSteps = [];
556
+ if (install !== void 0) {
557
+ await this.provider.uploadFile({
558
+ sandboxId,
559
+ remotePath: install.remotePath,
560
+ content: Buffer.from(install.content, "utf-8")
561
+ });
562
+ initSteps.push(`chmod 0555 ${shellEscape(install.remotePath)}`);
563
+ if (install.pathBinSymlink !== void 0) {
564
+ initSteps.push(`ln -sf ${shellEscape(install.remotePath)} ${shellEscape(install.pathBinSymlink)}`);
565
+ } else {
566
+ const { binDir, binLink } = mcpClientLayout(install.remotePath);
567
+ initSteps.push(`ln -sf ${shellEscape(relative(binDir, install.remotePath))} ${shellEscape(binLink)}`);
568
+ }
569
+ }
570
+ const skillInit = this.skillMounter?.getSandboxInit({
571
+ skillsDir,
572
+ gitDownloaderPath: this.provider.getGitDownloaderPath(sandboxId)
573
+ });
456
574
  if (skillInit) {
457
575
  initSteps.push(skillInit.command);
458
576
  }
459
- const script = initSteps.join(" && ");
460
- const result = await this.provider.exec({
461
- sandboxId: this.requiredSandboxInfo.sandbox_id,
462
- command: script,
463
- env: skillInit?.env,
464
- timeoutSeconds: skillInit?.timeoutSeconds
465
- });
466
- ensureExecSuccess(result);
577
+ if (initSteps.length > 0) {
578
+ ensureExecSuccess(
579
+ await this.provider.exec({
580
+ sandboxId,
581
+ command: initSteps.join(" && "),
582
+ env: skillInit?.env,
583
+ timeoutSeconds: skillInit?.timeoutSeconds
584
+ })
585
+ );
586
+ }
467
587
  this.logger.info(
468
- `Sandbox initialized: MCP client at ${MCP_CLIENT_PATH} (symlinked to ${MCP_CLIENT_BIN_SYMLINK}); skills dir ${SKILLS_DIR}`
588
+ install === void 0 ? `Sandbox initialized: skills dir ${skillsDir}` : `Sandbox initialized: MCP client at ${install.remotePath}; skills dir ${skillsDir}`
469
589
  );
470
590
  await this.writeGitCredentials();
471
591
  }