svamp-cli 0.1.29 → 0.1.30

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 (95) hide show
  1. package/dist/cli.mjs +7 -7
  2. package/dist/{commands-CgT3AgJ0.mjs → commands-B2xQb9u7.mjs} +1 -1
  3. package/dist/{commands-CKTIJoV0.mjs → commands-C5pW2VmI.mjs} +286 -60
  4. package/dist/{commands-DDB3y1L1.mjs → commands-TZkNivgV.mjs} +295 -44
  5. package/dist/index.mjs +1 -1
  6. package/dist/{run-DZmxHj-e.mjs → run-CtCTd6if.mjs} +234 -50
  7. package/dist/{run-CuN6K7pN.mjs → run-Cxdc5Zmw.mjs} +316 -164
  8. package/dist/{run-DMD0N00A.mjs → run-dBWhjQRf.mjs} +316 -76
  9. package/package.json +1 -1
  10. package/dist/agent-cli.mjs +0 -453
  11. package/dist/commands-1CYZC6Xh.mjs +0 -481
  12. package/dist/commands-B1DcpgLW.mjs +0 -481
  13. package/dist/commands-BGmdgMAC.mjs +0 -485
  14. package/dist/commands-BOeSil-P.mjs +0 -459
  15. package/dist/commands-BU4GZQuH.mjs +0 -481
  16. package/dist/commands-Ba66PxtQ.mjs +0 -481
  17. package/dist/commands-C0-xqIIc.mjs +0 -481
  18. package/dist/commands-C7Qy5n6d.mjs +0 -481
  19. package/dist/commands-CKpC8R9T.mjs +0 -481
  20. package/dist/commands-CNqOjR1y.mjs +0 -481
  21. package/dist/commands-CVKh1tWr.mjs +0 -485
  22. package/dist/commands-CYBblX73.mjs +0 -485
  23. package/dist/commands-CZBYmj16.mjs +0 -485
  24. package/dist/commands-CcWIvCA4.mjs +0 -481
  25. package/dist/commands-Cfwf-cQG.mjs +0 -481
  26. package/dist/commands-DCNO2m66.mjs +0 -471
  27. package/dist/commands-DRIFvhmC.mjs +0 -481
  28. package/dist/commands-DXmw2dzy.mjs +0 -481
  29. package/dist/commands-DkSvlKFF.mjs +0 -485
  30. package/dist/commands-DnDd4Sew.mjs +0 -481
  31. package/dist/commands-DnpnAFQW.mjs +0 -485
  32. package/dist/commands-Do-TVYFm.mjs +0 -481
  33. package/dist/commands-GEXri0yz.mjs +0 -484
  34. package/dist/commands-Kzm0_XNH.mjs +0 -481
  35. package/dist/commands-MQvNbIid.mjs +0 -481
  36. package/dist/commands-_uCC3U1U.mjs +0 -481
  37. package/dist/commands-y2WG29W9.mjs +0 -485
  38. package/dist/hyphaClient-DLkclazm.mjs +0 -39
  39. package/dist/package-ASJ9pMHk.mjs +0 -60
  40. package/dist/package-B2FOzHaM.mjs +0 -57
  41. package/dist/package-Bk_PFVA0.mjs +0 -57
  42. package/dist/package-Bnij-ZtR.mjs +0 -57
  43. package/dist/package-BtRbHfjz.mjs +0 -57
  44. package/dist/package-C5B0twb8.mjs +0 -57
  45. package/dist/package-CC5d8_0L.mjs +0 -57
  46. package/dist/package-CCJ045H0.mjs +0 -60
  47. package/dist/package-CS219SXn.mjs +0 -57
  48. package/dist/package-Cd-9ktpd.mjs +0 -60
  49. package/dist/package-CgBD49cA.mjs +0 -57
  50. package/dist/package-CvnNnsm7.mjs +0 -60
  51. package/dist/package-DPXkSwHu.mjs +0 -57
  52. package/dist/package-DpqWz9Cr.mjs +0 -60
  53. package/dist/package-JqEt5Ib4.mjs +0 -57
  54. package/dist/package-k18Su1iE.mjs +0 -58
  55. package/dist/package-nzkXV1aM.mjs +0 -57
  56. package/dist/package-pNo6GC3a.mjs +0 -60
  57. package/dist/package-pZp14zKI.mjs +0 -57
  58. package/dist/run-4fyJcaRE.mjs +0 -3856
  59. package/dist/run-B6oqR83K.mjs +0 -4631
  60. package/dist/run-BI32lPRK.mjs +0 -3870
  61. package/dist/run-BQHneHfW.mjs +0 -3834
  62. package/dist/run-Bb4fyIWZ.mjs +0 -3812
  63. package/dist/run-BglwnB-A.mjs +0 -3889
  64. package/dist/run-BjVWuitO.mjs +0 -3919
  65. package/dist/run-BzUE-JUT.mjs +0 -3708
  66. package/dist/run-BzqS97Sx.mjs +0 -3666
  67. package/dist/run-C6snRxyh.mjs +0 -3826
  68. package/dist/run-C8CI8Ujj.mjs +0 -3693
  69. package/dist/run-CL-FS4Yc.mjs +0 -3933
  70. package/dist/run-CS1Z4GcM.mjs +0 -3786
  71. package/dist/run-CT7uizQo.mjs +0 -4492
  72. package/dist/run-CUIj4xbE.mjs +0 -4880
  73. package/dist/run-CW26vPqj.mjs +0 -3919
  74. package/dist/run-CkTufc0D.mjs +0 -3875
  75. package/dist/run-Cmostc0S.mjs +0 -3902
  76. package/dist/run-Cp3kKdzm.mjs +0 -3865
  77. package/dist/run-D0bCTY72.mjs +0 -3816
  78. package/dist/run-D4N6FQON.mjs +0 -4673
  79. package/dist/run-D4dlA0jo.mjs +0 -4813
  80. package/dist/run-DMW8ibIw.mjs +0 -3958
  81. package/dist/run-DO52unxE.mjs +0 -3950
  82. package/dist/run-DQ5FOQ_c.mjs +0 -4788
  83. package/dist/run-DT7FgL8L.mjs +0 -4339
  84. package/dist/run-DYhBROuo.mjs +0 -3934
  85. package/dist/run-DjfPjgOb.mjs +0 -3904
  86. package/dist/run-DlL4JALM.mjs +0 -4719
  87. package/dist/run-Dp2JPkGI.mjs +0 -3913
  88. package/dist/run-Dptna3Je.mjs +0 -3867
  89. package/dist/run-DwK3dfHd.mjs +0 -3875
  90. package/dist/run-M_SMt96j.mjs +0 -3913
  91. package/dist/run-MlpxQUPN.mjs +0 -3869
  92. package/dist/run-PuTIelbv.mjs +0 -3706
  93. package/dist/run-h37iSCUB.mjs +0 -3934
  94. package/dist/run-lpV0oguG.mjs +0 -3897
  95. package/dist/run-oHmTMcv8.mjs +0 -4721
@@ -10,12 +10,12 @@ import { existsSync, readFileSync, mkdirSync, appendFileSync, writeFileSync } fr
10
10
  import { join } from 'node:path';
11
11
  import { spawn, execSync, execFile } from 'node:child_process';
12
12
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
13
+ import { homedir, tmpdir } from 'node:os';
13
14
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
14
15
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
15
16
  import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
16
17
  import { z } from 'zod';
17
18
  import { mkdir, access, mkdtemp, rm, writeFile } from 'node:fs/promises';
18
- import { homedir } from 'node:os';
19
19
  import { promisify } from 'node:util';
20
20
 
21
21
  let connectToServerFn = null;
@@ -153,6 +153,12 @@ async function registerMachineService(server, machineId, metadata, daemonState,
153
153
  // Spawn a new session
154
154
  spawnSession: async (options, context) => {
155
155
  authorizeRequest(context, currentMetadata.sharing, "interact");
156
+ if (options.sharing?.enabled && !options.sharing.owner && context?.user?.email) {
157
+ options = {
158
+ ...options,
159
+ sharing: { ...options.sharing, owner: context.user.email }
160
+ };
161
+ }
156
162
  const result = await handlers.spawnSession({
157
163
  ...options,
158
164
  machineId
@@ -419,6 +425,8 @@ async function registerSessionService(server, sessionId, initialMetadata, initia
419
425
  data.uuid = randomUUID();
420
426
  }
421
427
  wrappedContent = { role: "agent", content: { type: "output", data } };
428
+ } else if (role === "event") {
429
+ wrappedContent = { role: "agent", content: { type: "event", data: content } };
422
430
  } else if (role === "session") {
423
431
  wrappedContent = { role: "session", content: { type: "session", data: content } };
424
432
  } else {
@@ -661,6 +669,9 @@ async function registerSessionService(server, sessionId, initialMetadata, initia
661
669
  if (metadata.sharing && context?.user?.email && context.user.email !== metadata.sharing.owner) {
662
670
  throw new Error("Only the session owner can update sharing settings");
663
671
  }
672
+ if (newSharing.enabled && !newSharing.owner && context?.user?.email) {
673
+ newSharing = { ...newSharing, owner: context.user.email };
674
+ }
664
675
  metadata = { ...metadata, sharing: newSharing };
665
676
  metadataVersion++;
666
677
  notifyListeners({
@@ -1188,6 +1199,106 @@ var DefaultTransport$1 = /*#__PURE__*/Object.freeze({
1188
1199
  DefaultTransport: DefaultTransport
1189
1200
  });
1190
1201
 
1202
+ const SVAMP_TOOLS_DIR$1 = join(homedir(), ".svamp", "tools");
1203
+ const SVAMP_TOOLS_BIN$1 = join(SVAMP_TOOLS_DIR$1, "node_modules", ".bin");
1204
+ const SVAMP_TOOLS_RG_BIN$1 = join(SVAMP_TOOLS_DIR$1, "node_modules", "@vscode", "ripgrep", "bin");
1205
+ function wrapWithIsolation(originalCommand, originalArgs, config) {
1206
+ switch (config.method) {
1207
+ case "srt":
1208
+ return wrapWithSrt(originalCommand, originalArgs, config);
1209
+ case "bwrap":
1210
+ return wrapWithBwrap(originalCommand, originalArgs, config);
1211
+ case "docker":
1212
+ return wrapWithContainer("docker", originalCommand, originalArgs, config);
1213
+ case "podman":
1214
+ return wrapWithContainer("podman", originalCommand, originalArgs, config);
1215
+ }
1216
+ }
1217
+ function wrapWithSrt(command, args, config) {
1218
+ const settings = {
1219
+ filesystem: {
1220
+ denyRead: config.srtConfig?.filesystem?.denyRead ?? [],
1221
+ allowWrite: config.srtConfig?.filesystem?.allowWrite ?? [config.workspacePath],
1222
+ denyWrite: config.srtConfig?.filesystem?.denyWrite ?? []
1223
+ },
1224
+ network: {
1225
+ allowedDomains: config.srtConfig?.network?.allowedDomains ?? [],
1226
+ deniedDomains: config.srtConfig?.network?.deniedDomains ?? []
1227
+ }
1228
+ };
1229
+ const settingsPath = join(tmpdir(), `srt-settings-${process.pid}-${Date.now()}.json`);
1230
+ writeFileSync(settingsPath, JSON.stringify(settings));
1231
+ const pathParts = [SVAMP_TOOLS_BIN$1, SVAMP_TOOLS_RG_BIN$1];
1232
+ if (process.env.PATH) pathParts.push(process.env.PATH);
1233
+ return {
1234
+ command: config.binaryPath,
1235
+ args: ["--settings", settingsPath, command, ...args],
1236
+ env: { PATH: pathParts.join(":") },
1237
+ cleanupFiles: [settingsPath]
1238
+ };
1239
+ }
1240
+ function wrapWithBwrap(command, args, config) {
1241
+ const bwrapArgs = [
1242
+ // Mount root filesystem read-only
1243
+ "--ro-bind",
1244
+ "/",
1245
+ "/",
1246
+ // Mount workspace read-write
1247
+ "--bind",
1248
+ config.workspacePath,
1249
+ config.workspacePath,
1250
+ // Mount /tmp read-write (many tools need it)
1251
+ "--bind",
1252
+ "/tmp",
1253
+ "/tmp",
1254
+ // Mount /dev read-write
1255
+ "--dev",
1256
+ "/dev",
1257
+ // Mount /proc
1258
+ "--proc",
1259
+ "/proc",
1260
+ // Unshare network — no network access inside sandbox
1261
+ "--unshare-net",
1262
+ // Unshare PID namespace
1263
+ "--unshare-pid",
1264
+ // Die when parent dies
1265
+ "--die-with-parent",
1266
+ // The actual command
1267
+ "--",
1268
+ command,
1269
+ ...args
1270
+ ];
1271
+ return {
1272
+ command: config.binaryPath,
1273
+ args: bwrapArgs
1274
+ };
1275
+ }
1276
+ function wrapWithContainer(runtime, command, args, config) {
1277
+ const image = config.containerConfig?.image || "node:lts-slim";
1278
+ const networkMode = config.containerConfig?.networkMode || "none";
1279
+ const containerArgs = [
1280
+ "run",
1281
+ "--rm",
1282
+ "-i",
1283
+ // interactive (for stdin/stdout piping)
1284
+ `--network=${networkMode}`,
1285
+ "-v",
1286
+ `${config.workspacePath}:${config.workspacePath}`,
1287
+ "-w",
1288
+ config.workspacePath
1289
+ ];
1290
+ if (config.containerConfig?.extraMounts) {
1291
+ for (const mount of config.containerConfig.extraMounts) {
1292
+ containerArgs.push("-v", mount);
1293
+ }
1294
+ }
1295
+ containerArgs.push(image, command, ...args);
1296
+ return {
1297
+ command: config.binaryPath,
1298
+ args: containerArgs
1299
+ };
1300
+ }
1301
+
1191
1302
  const DEFAULT_IDLE_TIMEOUT_MS = 500;
1192
1303
  const DEFAULT_TOOL_CALL_TIMEOUT_MS = 12e4;
1193
1304
  function parseArgsFromContent(content) {
@@ -1506,21 +1617,41 @@ class AcpBackend {
1506
1617
  let startupStatusErrorEmitted = false;
1507
1618
  try {
1508
1619
  const args = this.options.args || [];
1620
+ let spawnCommand = this.options.command;
1621
+ let spawnArgs = args;
1622
+ let isoEnv = {};
1623
+ let isoCleanupFiles = [];
1624
+ if (this.options.isolationConfig) {
1625
+ const wrapped = wrapWithIsolation(spawnCommand, spawnArgs, this.options.isolationConfig);
1626
+ spawnCommand = wrapped.command;
1627
+ spawnArgs = wrapped.args;
1628
+ if (wrapped.env) isoEnv = wrapped.env;
1629
+ if (wrapped.cleanupFiles) isoCleanupFiles = wrapped.cleanupFiles;
1630
+ this.log(`[ACP] Isolation: ${this.options.isolationConfig.method}`);
1631
+ }
1632
+ const spawnEnv = { ...process.env, ...this.options.env, ...isoEnv };
1509
1633
  if (process.platform === "win32") {
1510
- const fullCommand = [this.options.command, ...args].join(" ");
1634
+ const fullCommand = [spawnCommand, ...spawnArgs].join(" ");
1511
1635
  this.process = spawn("cmd.exe", ["/c", fullCommand], {
1512
1636
  cwd: this.options.cwd,
1513
- env: { ...process.env, ...this.options.env },
1637
+ env: spawnEnv,
1514
1638
  stdio: ["pipe", "pipe", "pipe"],
1515
1639
  windowsHide: true
1516
1640
  });
1517
1641
  } else {
1518
- this.process = spawn(this.options.command, args, {
1642
+ this.process = spawn(spawnCommand, spawnArgs, {
1519
1643
  cwd: this.options.cwd,
1520
- env: { ...process.env, ...this.options.env },
1644
+ env: spawnEnv,
1521
1645
  stdio: ["pipe", "pipe", "pipe"]
1522
1646
  });
1523
1647
  }
1648
+ if (isoCleanupFiles.length > 0) {
1649
+ this.process.on("exit", async () => {
1650
+ const { rm } = await import('node:fs/promises');
1651
+ for (const f of isoCleanupFiles) rm(f, { force: true }).catch(() => {
1652
+ });
1653
+ });
1654
+ }
1524
1655
  if (!this.process.stdin || !this.process.stdout || !this.process.stderr) {
1525
1656
  throw new Error("Failed to create stdio pipes");
1526
1657
  }
@@ -2133,15 +2264,15 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
2133
2264
  setMetadata((m) => ({ ...m, lifecycleState: "running" }));
2134
2265
  } else if (msg.status === "error") {
2135
2266
  flushText();
2136
- sessionService.pushMessage({
2137
- type: "assistant",
2138
- content: [{ type: "text", text: `Error: ${msg.detail || "Unknown error"}` }]
2139
- }, "agent");
2140
- sessionService.sendKeepAlive(false);
2267
+ sessionService.pushMessage(
2268
+ { type: "message", message: `Agent process exited unexpectedly: ${msg.detail || "Unknown error"}` },
2269
+ "event"
2270
+ );
2271
+ sessionService.sendSessionEnd();
2141
2272
  setMetadata((m) => ({ ...m, lifecycleState: "error" }));
2142
2273
  } else if (msg.status === "stopped") {
2143
2274
  flushText();
2144
- sessionService.sendKeepAlive(false);
2275
+ sessionService.sendSessionEnd();
2145
2276
  setMetadata((m) => ({ ...m, lifecycleState: "stopped" }));
2146
2277
  }
2147
2278
  break;
@@ -2296,6 +2427,8 @@ class CodexMcpBackend {
2296
2427
  connected = false;
2297
2428
  // Pending elicitation approvals
2298
2429
  pendingApprovals = /* @__PURE__ */ new Map();
2430
+ // Temp files from isolation wrapping (cleaned up on disconnect)
2431
+ _isolationCleanupFiles = [];
2299
2432
  constructor(options) {
2300
2433
  this.options = options;
2301
2434
  this.log = options.log || (() => {
@@ -2438,9 +2571,21 @@ class CodexMcpBackend {
2438
2571
  } else if (!existingRustLog.includes("codex_core::rollout::list=")) {
2439
2572
  env.RUST_LOG = `${existingRustLog},${rolloutFilter}`;
2440
2573
  }
2574
+ let transportCommand = "codex";
2575
+ let transportArgs = [mcpCommand];
2576
+ if (this.options.isolationConfig) {
2577
+ const wrapped = wrapWithIsolation(transportCommand, transportArgs, this.options.isolationConfig);
2578
+ transportCommand = wrapped.command;
2579
+ transportArgs = wrapped.args;
2580
+ if (wrapped.env) Object.assign(env, wrapped.env);
2581
+ if (wrapped.cleanupFiles) {
2582
+ this._isolationCleanupFiles = wrapped.cleanupFiles;
2583
+ }
2584
+ this.log(`[Codex] Isolation: ${this.options.isolationConfig.method}`);
2585
+ }
2441
2586
  this.transport = new StdioClientTransport({
2442
- command: "codex",
2443
- args: [mcpCommand],
2587
+ command: transportCommand,
2588
+ args: transportArgs,
2444
2589
  env
2445
2590
  });
2446
2591
  this.registerPermissionHandlers();
@@ -2477,6 +2622,12 @@ class CodexMcpBackend {
2477
2622
  }
2478
2623
  this.transport = null;
2479
2624
  this.connected = false;
2625
+ if (this._isolationCleanupFiles.length > 0) {
2626
+ const { rm } = await import('node:fs/promises');
2627
+ for (const f of this._isolationCleanupFiles) rm(f, { force: true }).catch(() => {
2628
+ });
2629
+ this._isolationCleanupFiles = [];
2630
+ }
2480
2631
  }
2481
2632
  // ── Permission handling ────────────────────────────────────────────
2482
2633
  registerPermissionHandlers() {
@@ -3536,8 +3687,19 @@ async function startDaemon() {
3536
3687
  proc.kill(signal);
3537
3688
  }
3538
3689
  });
3690
+ }, buildIsolationConfig2 = function(dir) {
3691
+ if (!sessionMetadata.sharing?.enabled) return null;
3692
+ const method = isolationCapabilities.preferred;
3693
+ if (!method) return null;
3694
+ const detail = isolationCapabilities.details[method];
3695
+ if (!detail.found || detail.verified === false) return null;
3696
+ return {
3697
+ method,
3698
+ binaryPath: detail.path || method,
3699
+ workspacePath: dir
3700
+ };
3539
3701
  };
3540
- var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2, killAndWaitForExit = killAndWaitForExit2;
3702
+ var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2, killAndWaitForExit = killAndWaitForExit2, buildIsolationConfig = buildIsolationConfig2;
3541
3703
  let sessionMetadata = {
3542
3704
  path: directory,
3543
3705
  host: os.hostname(),
@@ -3590,6 +3752,7 @@ async function startDaemon() {
3590
3752
  "auto-approve-all": "bypassPermissions"
3591
3753
  };
3592
3754
  const toClaudePermissionMode = (mode) => CLAUDE_PERMISSION_MODE_MAP[mode] || mode;
3755
+ let isolationCleanupFiles = [];
3593
3756
  const spawnClaude = (initialMessage, meta) => {
3594
3757
  const rawPermissionMode = meta?.permissionMode || agentConfig.default_permission_mode || currentPermissionMode;
3595
3758
  const permissionMode = toClaudePermissionMode(rawPermissionMode);
@@ -3610,10 +3773,26 @@ async function startDaemon() {
3610
3773
  if (model) args.push("--model", model);
3611
3774
  if (appendSystemPrompt) args.push("--append-system-prompt", appendSystemPrompt);
3612
3775
  if (claudeResumeId) args.push("--resume", claudeResumeId);
3613
- logger.log(`[Session ${sessionId}] Spawning Claude: claude ${args.join(" ")} (cwd: ${directory})`);
3614
- const spawnEnv = { ...process.env };
3776
+ let spawnCommand = "claude";
3777
+ let spawnArgs = args;
3778
+ let extraEnv = {};
3779
+ isolationCleanupFiles = [];
3780
+ const isoConfig = buildIsolationConfig2(directory);
3781
+ if (isoConfig) {
3782
+ const wrapped = wrapWithIsolation(spawnCommand, spawnArgs, isoConfig);
3783
+ spawnCommand = wrapped.command;
3784
+ spawnArgs = wrapped.args;
3785
+ if (wrapped.env) extraEnv = wrapped.env;
3786
+ if (wrapped.cleanupFiles) isolationCleanupFiles = wrapped.cleanupFiles;
3787
+ sessionMetadata = { ...sessionMetadata, isolationMethod: isoConfig.method };
3788
+ logger.log(`[Session ${sessionId}] Isolation: ${isoConfig.method} (binary: ${isoConfig.binaryPath})`);
3789
+ } else {
3790
+ sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
3791
+ }
3792
+ logger.log(`[Session ${sessionId}] Spawning Claude: ${spawnCommand} ${spawnArgs.join(" ")} (cwd: ${directory})`);
3793
+ const spawnEnv = { ...process.env, ...extraEnv };
3615
3794
  delete spawnEnv.CLAUDECODE;
3616
- const child = spawn$1("claude", args, {
3795
+ const child = spawn$1(spawnCommand, spawnArgs, {
3617
3796
  cwd: directory,
3618
3797
  stdio: ["pipe", "pipe", "pipe"],
3619
3798
  env: spawnEnv,
@@ -3623,17 +3802,11 @@ async function startDaemon() {
3623
3802
  logger.log(`[Session ${sessionId}] Claude PID: ${child.pid}, stdin: ${!!child.stdin}, stdout: ${!!child.stdout}, stderr: ${!!child.stderr}`);
3624
3803
  child.on("error", (err) => {
3625
3804
  logger.log(`[Session ${sessionId}] Claude process error: ${err.message}`);
3626
- sessionService.pushMessage({
3627
- type: "assistant",
3628
- content: [{
3629
- type: "text",
3630
- text: `Error: Failed to start Claude Code CLI: ${err.message}
3631
-
3632
- Please ensure Claude Code CLI is installed on this machine. You can install it with:
3633
- \`npm install -g @anthropic-ai/claude-code\``
3634
- }]
3635
- }, "agent");
3636
- sessionService.sendKeepAlive(false);
3805
+ sessionService.pushMessage(
3806
+ { type: "message", message: `Agent process exited unexpectedly: ${err.message}. Please ensure Claude Code CLI is installed.` },
3807
+ "event"
3808
+ );
3809
+ sessionService.sendSessionEnd();
3637
3810
  });
3638
3811
  let stdoutBuffer = "";
3639
3812
  let lastErrorMessagePushed = false;
@@ -3827,25 +4000,25 @@ Please ensure Claude Code CLI is installed on this machine. You can install it w
3827
4000
  child.on("exit", (code, signal) => {
3828
4001
  logger.log(`[Session ${sessionId}] Claude exited: code=${code}, signal=${signal}`);
3829
4002
  claudeProcess = null;
4003
+ for (const f of isolationCleanupFiles) {
4004
+ fs.rm(f, { force: true }).catch(() => {
4005
+ });
4006
+ }
4007
+ isolationCleanupFiles = [];
3830
4008
  for (const [id, pending] of pendingPermissions) {
3831
4009
  pending.resolve({ behavior: "deny", message: "Claude process exited" });
3832
4010
  }
3833
4011
  pendingPermissions.clear();
3834
4012
  if (code !== 0 && code !== null && !lastErrorMessagePushed) {
3835
- sessionService.pushMessage({
3836
- type: "assistant",
3837
- content: [{
3838
- type: "text",
3839
- text: `Error: Claude process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}.
3840
-
3841
- This may indicate that Claude Code CLI is not properly installed or configured.`
3842
- }]
3843
- }, "agent");
4013
+ sessionService.pushMessage(
4014
+ { type: "message", message: `Agent process exited unexpectedly (code ${code}${signal ? `, signal: ${signal}` : ""})` },
4015
+ "event"
4016
+ );
3844
4017
  }
3845
4018
  lastErrorMessagePushed = false;
3846
4019
  sessionMetadata = { ...sessionMetadata, lifecycleState: claudeResumeId ? "idle" : "stopped" };
3847
4020
  sessionService.updateMetadata(sessionMetadata);
3848
- sessionService.sendKeepAlive(false);
4021
+ sessionService.sendSessionEnd();
3849
4022
  if (claudeResumeId && !trackedSession.stopped) {
3850
4023
  saveSession({
3851
4024
  sessionId,
@@ -4337,12 +4510,27 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4337
4510
  logger
4338
4511
  );
4339
4512
  const permissionHandler = new HyphaPermissionHandler(shouldAutoAllow2, logger.log);
4513
+ let agentIsoConfig;
4514
+ if (sessionMetadata.sharing?.enabled && isolationCapabilities.preferred) {
4515
+ const method = isolationCapabilities.preferred;
4516
+ const detail = isolationCapabilities.details[method];
4517
+ if (detail.found && detail.verified !== false) {
4518
+ agentIsoConfig = {
4519
+ method,
4520
+ binaryPath: detail.path || method,
4521
+ workspacePath: directory
4522
+ };
4523
+ sessionMetadata = { ...sessionMetadata, isolationMethod: method };
4524
+ logger.log(`[Agent Session ${sessionId}] Isolation: ${method}`);
4525
+ }
4526
+ }
4340
4527
  let agentBackend;
4341
4528
  if (KNOWN_MCP_AGENTS[agentName]) {
4342
4529
  agentBackend = new CodexMcpBackend({
4343
4530
  cwd: directory,
4344
4531
  env: options.environmentVariables,
4345
- log: logger.log
4532
+ log: logger.log,
4533
+ isolationConfig: agentIsoConfig
4346
4534
  });
4347
4535
  } else {
4348
4536
  const transportHandler = agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(agentName);
@@ -4355,7 +4543,8 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4355
4543
  env: options.environmentVariables,
4356
4544
  permissionHandler,
4357
4545
  transportHandler,
4358
- log: logger.log
4546
+ log: logger.log,
4547
+ isolationConfig: agentIsoConfig
4359
4548
  });
4360
4549
  }
4361
4550
  bridgeAcpToSession(
@@ -4387,16 +4576,11 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4387
4576
  logger.log(`[Agent Session ${sessionId}] ${agentName} backend started, waiting for first message`);
4388
4577
  }).catch((err) => {
4389
4578
  logger.error(`[Agent Session ${sessionId}] Failed to start ${agentName}:`, err);
4390
- sessionService.pushMessage({
4391
- type: "assistant",
4392
- content: [{
4393
- type: "text",
4394
- text: `Error: Failed to start ${agentName} agent: ${err.message}
4395
-
4396
- Please ensure the ${agentName} CLI is installed.`
4397
- }]
4398
- }, "agent");
4399
- sessionService.sendKeepAlive(false);
4579
+ sessionService.pushMessage(
4580
+ { type: "message", message: `Agent process exited unexpectedly: ${err.message}. Please ensure the ${agentName} CLI is installed.` },
4581
+ "event"
4582
+ );
4583
+ sessionService.sendSessionEnd();
4400
4584
  });
4401
4585
  return {
4402
4586
  type: "success",