svamp-cli 0.1.29 → 0.1.31

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 (98) hide show
  1. package/dist/cli.mjs +8 -8
  2. package/dist/{commands-CgT3AgJ0.mjs → commands-78RHC879.mjs} +162 -55
  3. package/dist/index.mjs +1 -1
  4. package/dist/{package-DPXkSwHu.mjs → package-BtoCHVOd.mjs} +1 -1
  5. package/dist/{run-BCSNMhiz.mjs → run-dBWhjQRf.mjs} +66 -61
  6. package/package.json +1 -1
  7. package/dist/agent-cli.mjs +0 -453
  8. package/dist/commands-1CYZC6Xh.mjs +0 -481
  9. package/dist/commands-B1DcpgLW.mjs +0 -481
  10. package/dist/commands-BGmdgMAC.mjs +0 -485
  11. package/dist/commands-BOeSil-P.mjs +0 -459
  12. package/dist/commands-BU4GZQuH.mjs +0 -481
  13. package/dist/commands-Ba66PxtQ.mjs +0 -481
  14. package/dist/commands-C0-xqIIc.mjs +0 -481
  15. package/dist/commands-C7Qy5n6d.mjs +0 -481
  16. package/dist/commands-CKTIJoV0.mjs +0 -636
  17. package/dist/commands-CKpC8R9T.mjs +0 -481
  18. package/dist/commands-CNqOjR1y.mjs +0 -481
  19. package/dist/commands-CVKh1tWr.mjs +0 -485
  20. package/dist/commands-CYBblX73.mjs +0 -485
  21. package/dist/commands-CZBYmj16.mjs +0 -485
  22. package/dist/commands-CcWIvCA4.mjs +0 -481
  23. package/dist/commands-Cfwf-cQG.mjs +0 -481
  24. package/dist/commands-CtO1WRt4.mjs +0 -862
  25. package/dist/commands-DCNO2m66.mjs +0 -471
  26. package/dist/commands-DDB3y1L1.mjs +0 -611
  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-CKOQ5lA7.mjs +0 -57
  48. package/dist/package-CS219SXn.mjs +0 -57
  49. package/dist/package-Cd-9ktpd.mjs +0 -60
  50. package/dist/package-CgBD49cA.mjs +0 -57
  51. package/dist/package-CvnNnsm7.mjs +0 -60
  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-CuN6K7pN.mjs +0 -4824
  78. package/dist/run-D0bCTY72.mjs +0 -3816
  79. package/dist/run-D4N6FQON.mjs +0 -4673
  80. package/dist/run-D4dlA0jo.mjs +0 -4813
  81. package/dist/run-DMD0N00A.mjs +0 -4737
  82. package/dist/run-DMW8ibIw.mjs +0 -3958
  83. package/dist/run-DO52unxE.mjs +0 -3950
  84. package/dist/run-DQ5FOQ_c.mjs +0 -4788
  85. package/dist/run-DT7FgL8L.mjs +0 -4339
  86. package/dist/run-DYhBROuo.mjs +0 -3934
  87. package/dist/run-DZmxHj-e.mjs +0 -4774
  88. package/dist/run-DjfPjgOb.mjs +0 -3904
  89. package/dist/run-DlL4JALM.mjs +0 -4719
  90. package/dist/run-Dp2JPkGI.mjs +0 -3913
  91. package/dist/run-Dptna3Je.mjs +0 -3867
  92. package/dist/run-DwK3dfHd.mjs +0 -3875
  93. package/dist/run-M_SMt96j.mjs +0 -3913
  94. package/dist/run-MlpxQUPN.mjs +0 -3869
  95. package/dist/run-PuTIelbv.mjs +0 -3706
  96. package/dist/run-h37iSCUB.mjs +0 -3934
  97. package/dist/run-lpV0oguG.mjs +0 -3897
  98. package/dist/run-oHmTMcv8.mjs +0 -4721
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-BCSNMhiz.mjs';
1
+ import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-dBWhjQRf.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -89,7 +89,7 @@ async function main() {
89
89
  } else if (subcommand === "--help" || subcommand === "-h" || !subcommand) {
90
90
  printHelp();
91
91
  } else if (subcommand === "--version" || subcommand === "-v") {
92
- const pkg = await import('./package-CKOQ5lA7.mjs').catch(() => ({ default: { version: "unknown" } }));
92
+ const pkg = await import('./package-BtoCHVOd.mjs').catch(() => ({ default: { version: "unknown" } }));
93
93
  console.log(`svamp version: ${pkg.default.version}`);
94
94
  } else {
95
95
  console.error(`Unknown command: ${subcommand}`);
@@ -104,7 +104,7 @@ async function handleAgentCommand() {
104
104
  return;
105
105
  }
106
106
  if (agentArgs[0] === "list") {
107
- const { KNOWN_ACP_AGENTS } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.f; });
107
+ const { KNOWN_ACP_AGENTS } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.f; });
108
108
  console.log("Known ACP agents:");
109
109
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
110
110
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")}`);
@@ -113,10 +113,10 @@ async function handleAgentCommand() {
113
113
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
114
114
  return;
115
115
  }
116
- const { resolveAcpAgentConfig } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.f; });
117
- const { AcpBackend } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.e; });
118
- const { GeminiTransport } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.G; });
119
- const { DefaultTransport } = await import('./run-BCSNMhiz.mjs').then(function (n) { return n.D; });
116
+ const { resolveAcpAgentConfig } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.f; });
117
+ const { AcpBackend } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.e; });
118
+ const { GeminiTransport } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.G; });
119
+ const { DefaultTransport } = await import('./run-dBWhjQRf.mjs').then(function (n) { return n.D; });
120
120
  let cwd = process.cwd();
121
121
  const filteredArgs = [];
122
122
  for (let i = 0; i < agentArgs.length; i++) {
@@ -255,7 +255,7 @@ async function handleSessionCommand() {
255
255
  printSessionHelp();
256
256
  return;
257
257
  }
258
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait } = await import('./commands-CtO1WRt4.mjs');
258
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait } = await import('./commands-78RHC879.mjs');
259
259
  const parseFlagStr = (flag, shortFlag) => {
260
260
  for (let i = 1; i < sessionArgs.length; i++) {
261
261
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-DZmxHj-e.mjs';
4
+ import { c as connectToHypha } from './run-dBWhjQRf.mjs';
5
5
  import 'os';
6
6
  import 'fs/promises';
7
7
  import 'fs';
@@ -186,7 +186,15 @@ async function connectAndGetMachine(machineId) {
186
186
  }
187
187
  }
188
188
  } else {
189
- selectedService = services[0];
189
+ if (state.machineId) {
190
+ const localMatch = services.find((s) => {
191
+ const id = s.id || s.name || "";
192
+ return id.includes(state.machineId);
193
+ });
194
+ selectedService = localMatch || services[0];
195
+ } else {
196
+ selectedService = services[0];
197
+ }
190
198
  }
191
199
  const svcId = selectedService.id || selectedService.name;
192
200
  machine = await server.getService(svcId);
@@ -199,6 +207,81 @@ async function connectAndGetMachine(machineId) {
199
207
  restoreConsole();
200
208
  return { server, machine };
201
209
  }
210
+ async function connectAndGetAllMachines() {
211
+ loadDotEnv();
212
+ const state = readDaemonState();
213
+ if (!state || !isDaemonAlive(state)) {
214
+ console.error('Daemon is not running. Start it with "svamp daemon start".');
215
+ process.exit(1);
216
+ }
217
+ const serverUrl = process.env.HYPHA_SERVER_URL || state.hyphaServerUrl;
218
+ const token = process.env.HYPHA_TOKEN;
219
+ if (!serverUrl) {
220
+ console.error('No Hypha server URL. Run "svamp login <url>" first.');
221
+ process.exit(1);
222
+ }
223
+ const origLog = console.log;
224
+ const origWarn = console.warn;
225
+ const origInfo = console.info;
226
+ const origError = console.error;
227
+ const stdoutWrite = process.stdout.write.bind(process.stdout);
228
+ const stderrWrite = process.stderr.write.bind(process.stderr);
229
+ const isHyphaLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection") || chunk.includes("Timeout registering service") || chunk.includes("Failed to subscribe to client_disconnected") || chunk.includes("Timeout subscribing to client_disconnected"));
230
+ console.log = () => {
231
+ };
232
+ console.warn = () => {
233
+ };
234
+ console.info = () => {
235
+ };
236
+ console.error = (...args) => {
237
+ if (!args.some((a) => isHyphaLog(a))) origError(...args);
238
+ };
239
+ process.stdout.write = (chunk, ...args) => {
240
+ if (isHyphaLog(chunk)) return true;
241
+ return stdoutWrite(chunk, ...args);
242
+ };
243
+ process.stderr.write = (chunk, ...args) => {
244
+ if (isHyphaLog(chunk)) return true;
245
+ return stderrWrite(chunk, ...args);
246
+ };
247
+ const restoreConsole = () => {
248
+ console.log = origLog;
249
+ console.warn = origWarn;
250
+ console.info = origInfo;
251
+ console.error = origError;
252
+ };
253
+ let server;
254
+ try {
255
+ server = await connectToHypha({ serverUrl, token, name: "svamp-session-cli" });
256
+ } catch (err) {
257
+ restoreConsole();
258
+ console.error(`Failed to connect to Hypha: ${err.message}`);
259
+ process.exit(1);
260
+ }
261
+ const machines = [];
262
+ try {
263
+ const services = await server.listServices({ type: "svamp-machine" });
264
+ for (const svc of services) {
265
+ try {
266
+ const svcId = svc.id || svc.name;
267
+ machines.push(await server.getService(svcId));
268
+ } catch {
269
+ }
270
+ }
271
+ } catch (err) {
272
+ restoreConsole();
273
+ console.error(`Failed to discover machine services: ${err.message}`);
274
+ await server.disconnect();
275
+ process.exit(1);
276
+ }
277
+ restoreConsole();
278
+ if (machines.length === 0) {
279
+ console.error("No machine service found. Is the daemon registered on Hypha?");
280
+ await server.disconnect();
281
+ process.exit(1);
282
+ }
283
+ return { server, machines };
284
+ }
202
285
  async function sessionMachines() {
203
286
  loadDotEnv();
204
287
  const state = readDaemonState();
@@ -497,66 +580,90 @@ async function waitForBusyThenIdle(server, sessionId, timeoutMs = 3e5, busyTimeo
497
580
  throw new Error("Timeout waiting for agent to become idle");
498
581
  }
499
582
  async function sessionList(machineId, opts) {
500
- const { server, machine } = await connectAndGetMachine(machineId);
501
- try {
502
- const sessions = await machine.listSessions();
503
- const filtered = opts?.active ? sessions.filter((s) => s.active) : sessions;
504
- if (filtered.length === 0) {
505
- if (opts?.json) {
506
- console.log(formatJson([]));
507
- } else {
508
- console.log("No active sessions.");
509
- }
510
- return;
583
+ if (machineId) {
584
+ const { server, machine } = await connectAndGetMachine(machineId);
585
+ try {
586
+ await listSessionsFromMachines(server, [machine], opts);
587
+ } finally {
588
+ await server.disconnect();
511
589
  }
512
- const enriched = [];
513
- for (const s of filtered) {
514
- let flavor = "claude";
515
- let name = "";
516
- let path = s.directory || "";
517
- let host = "";
518
- if (s.metadata) {
519
- flavor = s.metadata.flavor || "claude";
520
- name = s.metadata.name || "";
521
- }
522
- if (s.active) {
523
- try {
524
- const svc = await server.getService(`svamp-session-${s.sessionId}`);
525
- const { metadata } = await svc.getMetadata();
526
- flavor = metadata?.flavor || flavor;
527
- name = metadata?.name || name;
528
- path = metadata?.path || path;
529
- host = metadata?.host || "";
530
- } catch {
531
- }
590
+ } else {
591
+ const { server, machines } = await connectAndGetAllMachines();
592
+ try {
593
+ await listSessionsFromMachines(server, machines, opts);
594
+ } finally {
595
+ await server.disconnect();
596
+ }
597
+ }
598
+ }
599
+ async function listSessionsFromMachines(server, machines, opts) {
600
+ const allSessions = [];
601
+ for (const machine of machines) {
602
+ try {
603
+ const info = await machine.getMachineInfo();
604
+ const sessions = await machine.listSessions();
605
+ for (const s of sessions) {
606
+ s.machineHost = info.metadata?.displayName || info.metadata?.host || info.machineId;
532
607
  }
533
- enriched.push({ ...s, flavor, name, path, host });
608
+ allSessions.push(...sessions);
609
+ } catch {
534
610
  }
611
+ }
612
+ const filtered = opts?.active ? allSessions.filter((s) => s.active) : allSessions;
613
+ if (filtered.length === 0) {
535
614
  if (opts?.json) {
536
- console.log(formatJson(enriched.map((s) => ({
537
- sessionId: s.sessionId,
538
- agent: s.flavor,
539
- name: s.name,
540
- path: s.path,
541
- host: s.host,
542
- active: s.active,
543
- directory: s.directory
544
- }))));
615
+ console.log(formatJson([]));
545
616
  } else {
546
- const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"DIRECTORY".padEnd(35)}`;
547
- console.log(header);
548
- console.log("-".repeat(header.length));
549
- for (const s of enriched) {
550
- const id = s.sessionId.slice(0, 8);
551
- const agent = (s.flavor || "claude").padEnd(10);
552
- const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
553
- const name = truncate(s.name || "-", 25).padEnd(25);
554
- const dir = truncate(s.directory || "-", 33).padEnd(35);
555
- console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${dir}`);
617
+ console.log("No active sessions.");
618
+ }
619
+ return;
620
+ }
621
+ const enriched = [];
622
+ for (const s of filtered) {
623
+ let flavor = "claude";
624
+ let name = "";
625
+ let path = s.directory || "";
626
+ let host = s.machineHost || "";
627
+ if (s.metadata) {
628
+ flavor = s.metadata.flavor || "claude";
629
+ name = s.metadata.name || "";
630
+ }
631
+ if (s.active) {
632
+ try {
633
+ const svc = await server.getService(`svamp-session-${s.sessionId}`);
634
+ const { metadata } = await svc.getMetadata();
635
+ flavor = metadata?.flavor || flavor;
636
+ name = metadata?.name || name;
637
+ path = metadata?.path || path;
638
+ host = metadata?.host || host;
639
+ } catch {
556
640
  }
557
641
  }
558
- } finally {
559
- await server.disconnect();
642
+ enriched.push({ ...s, flavor, name, path, host });
643
+ }
644
+ if (opts?.json) {
645
+ console.log(formatJson(enriched.map((s) => ({
646
+ sessionId: s.sessionId,
647
+ agent: s.flavor,
648
+ name: s.name,
649
+ path: s.path,
650
+ host: s.host,
651
+ active: s.active,
652
+ directory: s.directory
653
+ }))));
654
+ } else {
655
+ const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"MACHINE".padEnd(18)} ${"DIRECTORY".padEnd(35)}`;
656
+ console.log(header);
657
+ console.log("-".repeat(header.length));
658
+ for (const s of enriched) {
659
+ const id = s.sessionId.slice(0, 8);
660
+ const agent = (s.flavor || "claude").padEnd(10);
661
+ const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
662
+ const name = truncate(s.name || "-", 25).padEnd(25);
663
+ const machine = truncate(s.host || "-", 16).padEnd(18);
664
+ const dir = truncate(s.directory || "-", 33).padEnd(35);
665
+ console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${machine} ${dir}`);
666
+ }
560
667
  }
561
668
  }
562
669
  async function sessionSpawn(agent, directory, machineId, opts) {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-BCSNMhiz.mjs';
1
+ export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-dBWhjQRf.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.1.28";
2
+ var version = "0.1.31";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -183,6 +183,11 @@ async function registerMachineService(server, machineId, metadata, daemonState,
183
183
  });
184
184
  return result;
185
185
  },
186
+ // Restart agent process in a session (machine-level fallback)
187
+ restartSession: async (sessionId, context) => {
188
+ authorizeRequest(context, currentMetadata.sharing, "interact");
189
+ return await handlers.restartSession(sessionId);
190
+ },
186
191
  // Stop the daemon
187
192
  stopDaemon: async (context) => {
188
193
  authorizeRequest(context, currentMetadata.sharing, "admin");
@@ -425,6 +430,8 @@ async function registerSessionService(server, sessionId, initialMetadata, initia
425
430
  data.uuid = randomUUID();
426
431
  }
427
432
  wrappedContent = { role: "agent", content: { type: "output", data } };
433
+ } else if (role === "event") {
434
+ wrappedContent = { role: "agent", content: { type: "event", data: content } };
428
435
  } else if (role === "session") {
429
436
  wrappedContent = { role: "session", content: { type: "session", data: content } };
430
437
  } else {
@@ -2262,15 +2269,15 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
2262
2269
  setMetadata((m) => ({ ...m, lifecycleState: "running" }));
2263
2270
  } else if (msg.status === "error") {
2264
2271
  flushText();
2265
- sessionService.pushMessage({
2266
- type: "assistant",
2267
- content: [{ type: "text", text: `Error: ${msg.detail || "Unknown error"}` }]
2268
- }, "agent");
2269
- sessionService.sendKeepAlive(false);
2272
+ sessionService.pushMessage(
2273
+ { type: "message", message: `Agent process exited unexpectedly: ${msg.detail || "Unknown error"}` },
2274
+ "event"
2275
+ );
2276
+ sessionService.sendSessionEnd();
2270
2277
  setMetadata((m) => ({ ...m, lifecycleState: "error" }));
2271
2278
  } else if (msg.status === "stopped") {
2272
2279
  flushText();
2273
- sessionService.sendKeepAlive(false);
2280
+ sessionService.sendSessionEnd();
2274
2281
  setMetadata((m) => ({ ...m, lifecycleState: "stopped" }));
2275
2282
  }
2276
2283
  break;
@@ -3800,17 +3807,11 @@ async function startDaemon() {
3800
3807
  logger.log(`[Session ${sessionId}] Claude PID: ${child.pid}, stdin: ${!!child.stdin}, stdout: ${!!child.stdout}, stderr: ${!!child.stderr}`);
3801
3808
  child.on("error", (err) => {
3802
3809
  logger.log(`[Session ${sessionId}] Claude process error: ${err.message}`);
3803
- sessionService.pushMessage({
3804
- type: "assistant",
3805
- content: [{
3806
- type: "text",
3807
- text: `Error: Failed to start Claude Code CLI: ${err.message}
3808
-
3809
- Please ensure Claude Code CLI is installed on this machine. You can install it with:
3810
- \`npm install -g @anthropic-ai/claude-code\``
3811
- }]
3812
- }, "agent");
3813
- sessionService.sendKeepAlive(false);
3810
+ sessionService.pushMessage(
3811
+ { type: "message", message: `Agent process exited unexpectedly: ${err.message}. Please ensure Claude Code CLI is installed.` },
3812
+ "event"
3813
+ );
3814
+ sessionService.sendSessionEnd();
3814
3815
  });
3815
3816
  let stdoutBuffer = "";
3816
3817
  let lastErrorMessagePushed = false;
@@ -4014,20 +4015,15 @@ Please ensure Claude Code CLI is installed on this machine. You can install it w
4014
4015
  }
4015
4016
  pendingPermissions.clear();
4016
4017
  if (code !== 0 && code !== null && !lastErrorMessagePushed) {
4017
- sessionService.pushMessage({
4018
- type: "assistant",
4019
- content: [{
4020
- type: "text",
4021
- text: `Error: Claude process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}.
4022
-
4023
- This may indicate that Claude Code CLI is not properly installed or configured.`
4024
- }]
4025
- }, "agent");
4018
+ sessionService.pushMessage(
4019
+ { type: "message", message: `Agent process exited unexpectedly (code ${code}${signal ? `, signal: ${signal}` : ""})` },
4020
+ "event"
4021
+ );
4026
4022
  }
4027
4023
  lastErrorMessagePushed = false;
4028
4024
  sessionMetadata = { ...sessionMetadata, lifecycleState: claudeResumeId ? "idle" : "stopped" };
4029
4025
  sessionService.updateMetadata(sessionMetadata);
4030
- sessionService.sendKeepAlive(false);
4026
+ sessionService.sendSessionEnd();
4031
4027
  if (claudeResumeId && !trackedSession.stopped) {
4032
4028
  saveSession({
4033
4029
  sessionId,
@@ -4051,6 +4047,30 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4051
4047
  }
4052
4048
  return child;
4053
4049
  };
4050
+ const restartClaudeHandler = async () => {
4051
+ logger.log(`[Session ${sessionId}] Restart Claude requested`);
4052
+ try {
4053
+ if (claudeProcess && claudeProcess.exitCode === null) {
4054
+ isKillingClaude = true;
4055
+ sessionMetadata = { ...sessionMetadata, lifecycleState: "restarting" };
4056
+ sessionService.updateMetadata(sessionMetadata);
4057
+ await killAndWaitForExit2(claudeProcess);
4058
+ isKillingClaude = false;
4059
+ }
4060
+ if (claudeResumeId) {
4061
+ spawnClaude(void 0, { permissionMode: currentPermissionMode });
4062
+ logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
4063
+ return { success: true, message: "Claude process restarted successfully." };
4064
+ } else {
4065
+ logger.log(`[Session ${sessionId}] No resume ID \u2014 cannot restart`);
4066
+ return { success: false, message: "No session to resume. Send a message to start a new session." };
4067
+ }
4068
+ } catch (err) {
4069
+ isKillingClaude = false;
4070
+ logger.log(`[Session ${sessionId}] Restart failed: ${err.message}`);
4071
+ return { success: false, message: `Restart failed: ${err.message}` };
4072
+ }
4073
+ };
4054
4074
  const sessionService = await registerSessionService(
4055
4075
  server,
4056
4076
  sessionId,
@@ -4148,30 +4168,7 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4148
4168
  spawnClaude(void 0, { permissionMode: mode });
4149
4169
  }
4150
4170
  },
4151
- onRestartClaude: async () => {
4152
- logger.log(`[Session ${sessionId}] Restart Claude requested`);
4153
- try {
4154
- if (claudeProcess && claudeProcess.exitCode === null) {
4155
- isKillingClaude = true;
4156
- sessionMetadata = { ...sessionMetadata, lifecycleState: "restarting" };
4157
- sessionService.updateMetadata(sessionMetadata);
4158
- await killAndWaitForExit2(claudeProcess);
4159
- isKillingClaude = false;
4160
- }
4161
- if (claudeResumeId) {
4162
- spawnClaude(void 0, { permissionMode: currentPermissionMode });
4163
- logger.log(`[Session ${sessionId}] Claude respawned with --resume ${claudeResumeId}`);
4164
- return { success: true, message: "Claude process restarted successfully." };
4165
- } else {
4166
- logger.log(`[Session ${sessionId}] No resume ID \u2014 cannot restart`);
4167
- return { success: false, message: "No session to resume. Send a message to start a new session." };
4168
- }
4169
- } catch (err) {
4170
- isKillingClaude = false;
4171
- logger.log(`[Session ${sessionId}] Restart failed: ${err.message}`);
4172
- return { success: false, message: `Restart failed: ${err.message}` };
4173
- }
4174
- },
4171
+ onRestartClaude: restartClaudeHandler,
4175
4172
  onKillSession: () => {
4176
4173
  logger.log(`[Session ${sessionId}] Kill session requested`);
4177
4174
  stopSession(sessionId);
@@ -4284,6 +4281,7 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4284
4281
  checkSvampConfig,
4285
4282
  directory,
4286
4283
  resumeSessionId: claudeResumeId,
4284
+ restartAgent: restartClaudeHandler,
4287
4285
  get childProcess() {
4288
4286
  return claudeProcess || void 0;
4289
4287
  }
@@ -4585,16 +4583,11 @@ This may indicate that Claude Code CLI is not properly installed or configured.`
4585
4583
  logger.log(`[Agent Session ${sessionId}] ${agentName} backend started, waiting for first message`);
4586
4584
  }).catch((err) => {
4587
4585
  logger.error(`[Agent Session ${sessionId}] Failed to start ${agentName}:`, err);
4588
- sessionService.pushMessage({
4589
- type: "assistant",
4590
- content: [{
4591
- type: "text",
4592
- text: `Error: Failed to start ${agentName} agent: ${err.message}
4593
-
4594
- Please ensure the ${agentName} CLI is installed.`
4595
- }]
4596
- }, "agent");
4597
- sessionService.sendKeepAlive(false);
4586
+ sessionService.pushMessage(
4587
+ { type: "message", message: `Agent process exited unexpectedly: ${err.message}. Please ensure the ${agentName} CLI is installed.` },
4588
+ "event"
4589
+ );
4590
+ sessionService.sendSessionEnd();
4598
4591
  });
4599
4592
  return {
4600
4593
  type: "success",
@@ -4631,6 +4624,17 @@ Please ensure the ${agentName} CLI is installed.`
4631
4624
  logger.log(`Session ${sessionId} not found`);
4632
4625
  return false;
4633
4626
  };
4627
+ const restartSession = async (sessionId) => {
4628
+ for (const session of pidToTrackedSession.values()) {
4629
+ if (session.svampSessionId === sessionId && !session.stopped) {
4630
+ if (session.restartAgent) {
4631
+ return await session.restartAgent();
4632
+ }
4633
+ return { success: false, message: "This session does not support restart." };
4634
+ }
4635
+ }
4636
+ return { success: false, message: `Session ${sessionId} not found or already stopped.` };
4637
+ };
4634
4638
  let isolationCapabilities;
4635
4639
  try {
4636
4640
  isolationCapabilities = await detectIsolationCapabilities();
@@ -4663,6 +4667,7 @@ Please ensure the ${agentName} CLI is installed.`
4663
4667
  {
4664
4668
  spawnSession,
4665
4669
  stopSession,
4670
+ restartSession,
4666
4671
  requestShutdown: () => requestShutdown("hypha-app"),
4667
4672
  getTrackedSessions: getCurrentChildren
4668
4673
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",