@aiaiai-pt/martha-cli 0.45.0 → 0.48.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 (3) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/index.js +51 -224
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.48.0] — 2026-07-13
8
+
9
+ ### Changed — BREAKING: `martha runner` means ONE thing (#714 runner-journey unification)
10
+ - Bare `martha runner` now attaches this machine as a **cloud agent's hands** (the
11
+ previous `--tools` mode): it holds the per-agent channel open and executes
12
+ `tool.invoke` host-tool calls the agent's Martha-hosted reasoning loop parks on
13
+ (chat gates and assigned-task parks, #910). `--tools` is kept as an accepted
14
+ no-op so printed `setup-host-runner` commands stay valid.
15
+ - The startup line now states the model plainly ("this machine is the agent's
16
+ hands … the agent reasons in Martha's cloud loop") — the old "reasoning stays
17
+ Martha-hosted regardless" line was false for the removed task mode.
18
+
19
+ ### Removed — the goal-as-`bash -c` task-executor mode
20
+ - `--tasks` (and the pre-#822 `--channel` alias), `--once`, `--team`, `--agent`,
21
+ and `--poll-interval` are **removed**. They ran a claimed task's goal as a raw
22
+ `bash -c` — the runner impersonating an external agent. Invoking any of them
23
+ now fails with migration guidance. **Nothing is stranded:** a self-hosted
24
+ agent is your own program driving the task REST — `martha tasks
25
+ poll/claim/heartbeat/complete` are unchanged.
26
+
7
27
  ## [0.30.0] — 2026-07-03
8
28
 
9
29
  ### Added — #761 runner heartbeat + poll-based cancel
package/dist/index.js CHANGED
@@ -11119,7 +11119,7 @@ init_config();
11119
11119
  init_errors();
11120
11120
 
11121
11121
  // src/version.ts
11122
- var CLI_VERSION = "0.45.0";
11122
+ var CLI_VERSION = "0.48.0";
11123
11123
 
11124
11124
  // src/commands/sessions.ts
11125
11125
  function relativeTime(iso) {
@@ -12249,6 +12249,7 @@ function parseLocalAgentRequests(data) {
12249
12249
 
12250
12250
  // src/lib/local-tool-runner.ts
12251
12251
  import { createHash as createHash2 } from "node:crypto";
12252
+ init_errors();
12252
12253
 
12253
12254
  // src/lib/local-exec.ts
12254
12255
  import { spawn } from "node:child_process";
@@ -12336,68 +12337,6 @@ async function runHostCommand(command, toolCallId, opts) {
12336
12337
  function safeName(s) {
12337
12338
  return s.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 120) || "task";
12338
12339
  }
12339
- async function startHostCommand(command, toolCallId, opts) {
12340
- const dir = runnerDir(opts.cwd);
12341
- await fs5.mkdir(dir, { recursive: true });
12342
- const logPath = path4.join(dir, `${safeName(toolCallId)}.log`);
12343
- await fs5.writeFile(logPath, "");
12344
- const fd = openSync(logPath, "a");
12345
- const cap = opts.maxOutputBytes ?? MAX_OUTPUT;
12346
- const readLog = async () => {
12347
- try {
12348
- const buf = await fs5.readFile(logPath);
12349
- return buf.subarray(-cap).toString("utf8");
12350
- } catch {
12351
- return "";
12352
- }
12353
- };
12354
- const child = spawn(command, {
12355
- shell: true,
12356
- cwd: opts.cwd,
12357
- detached: true,
12358
- stdio: ["ignore", fd, fd]
12359
- });
12360
- closeSync(fd);
12361
- let exited = false;
12362
- const done = new Promise((resolve, reject) => {
12363
- let settled = false;
12364
- child.on("error", (e) => {
12365
- if (settled)
12366
- return;
12367
- settled = true;
12368
- exited = true;
12369
- reject(e);
12370
- });
12371
- child.on("close", async (code) => {
12372
- if (settled)
12373
- return;
12374
- settled = true;
12375
- exited = true;
12376
- resolve({
12377
- stdout: await readLog(),
12378
- stderr: "",
12379
- exit_code: code ?? -1,
12380
- log_path: logPath
12381
- });
12382
- });
12383
- });
12384
- const kill = (graceMs = 5000) => {
12385
- if (exited || !child.pid)
12386
- return;
12387
- try {
12388
- process.kill(-child.pid, "SIGTERM");
12389
- } catch {}
12390
- const escalate = setTimeout(() => {
12391
- if (exited || !child.pid)
12392
- return;
12393
- try {
12394
- process.kill(-child.pid, "SIGKILL");
12395
- } catch {}
12396
- }, graceMs);
12397
- escalate.unref?.();
12398
- };
12399
- return { pid: child.pid, done, kill };
12400
- }
12401
12340
  function mintExecSecret() {
12402
12341
  return randomBytes2(32).toString("hex");
12403
12342
  }
@@ -13393,7 +13332,7 @@ async function resolveLocalAgent(sessionId, toolCallId, req, state) {
13393
13332
  }
13394
13333
  async function claimGate(sessionId, toolCallId, state, transport) {
13395
13334
  if (state.claimed.has(toolCallId))
13396
- return;
13335
+ return true;
13397
13336
  const secret = state.secrets.get(toolCallId) ?? mintExecSecret();
13398
13337
  const secretHash = createHash2("sha256").update(secret).digest("hex");
13399
13338
  state.secrets.set(toolCallId, secret);
@@ -13401,9 +13340,16 @@ async function claimGate(sessionId, toolCallId, state, transport) {
13401
13340
  try {
13402
13341
  await transport.claim(toolCallId, secretHash);
13403
13342
  state.claimed.add(toolCallId);
13343
+ return true;
13404
13344
  } catch (e) {
13345
+ if (e instanceof MarthaAPIError && e.status === 409) {
13346
+ process.stderr.write(source_default.yellow(`[local-exec] gate ${toolCallId} is not pending (already resolved or timed out) — skipping execution
13347
+ `));
13348
+ return false;
13349
+ }
13405
13350
  process.stderr.write(source_default.yellow(`[local-exec] failed to claim gate for ${toolCallId}: ${String(e)}
13406
13351
  `));
13352
+ return true;
13407
13353
  }
13408
13354
  }
13409
13355
  async function postToolResult(toolCallId, result, label, transport, execSecret) {
@@ -13420,7 +13366,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13420
13366
  if (state.handled.has(id))
13421
13367
  continue;
13422
13368
  state.handled.add(id);
13423
- await claimGate(sessionId, id, state, transport);
13369
+ if (!await claimGate(sessionId, id, state, transport))
13370
+ continue;
13424
13371
  const result = await resolveHostExec(sessionId, id, req, state);
13425
13372
  await postToolResult(id, result, "host_exec", transport, state.secrets.get(id));
13426
13373
  }
@@ -13429,7 +13376,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13429
13376
  if (state.handled.has(id))
13430
13377
  continue;
13431
13378
  state.handled.add(id);
13432
- await claimGate(sessionId, id, state, transport);
13379
+ if (!await claimGate(sessionId, id, state, transport))
13380
+ continue;
13433
13381
  const result = await resolveHostOutput(req, state);
13434
13382
  await postToolResult(id, result, "host_output", transport, state.secrets.get(id));
13435
13383
  }
@@ -13438,7 +13386,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13438
13386
  if (state.handled.has(id))
13439
13387
  continue;
13440
13388
  state.handled.add(id);
13441
- await claimGate(sessionId, id, state, transport);
13389
+ if (!await claimGate(sessionId, id, state, transport))
13390
+ continue;
13442
13391
  const result = await resolveHostKill(req, state);
13443
13392
  await postToolResult(id, result, "host_kill", transport, state.secrets.get(id));
13444
13393
  }
@@ -13447,7 +13396,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13447
13396
  if (state.handled.has(id))
13448
13397
  continue;
13449
13398
  state.handled.add(id);
13450
- await claimGate(sessionId, id, state, transport);
13399
+ if (!await claimGate(sessionId, id, state, transport))
13400
+ continue;
13451
13401
  const result = await resolveHostScreenshot(ctx, sessionId, req, state);
13452
13402
  await postToolResult(id, result, "host_screenshot", transport, state.secrets.get(id));
13453
13403
  }
@@ -13456,7 +13406,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13456
13406
  if (state.handled.has(id))
13457
13407
  continue;
13458
13408
  state.handled.add(id);
13459
- await claimGate(sessionId, id, state, transport);
13409
+ if (!await claimGate(sessionId, id, state, transport))
13410
+ continue;
13460
13411
  const result = await resolveHostBrowser(ctx, sessionId, req, state);
13461
13412
  await postToolResult(id, result, "host_browser", transport, state.secrets.get(id));
13462
13413
  }
@@ -13465,7 +13416,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13465
13416
  if (state.handled.has(id))
13466
13417
  continue;
13467
13418
  state.handled.add(id);
13468
- await claimGate(sessionId, id, state, transport);
13419
+ if (!await claimGate(sessionId, id, state, transport))
13420
+ continue;
13469
13421
  const result = await resolveHostFile(req, state);
13470
13422
  await postToolResult(id, result, req.action, transport, state.secrets.get(id));
13471
13423
  }
@@ -13474,7 +13426,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
13474
13426
  if (state.handled.has(id))
13475
13427
  continue;
13476
13428
  state.handled.add(id);
13477
- await claimGate(sessionId, id, state, transport);
13429
+ if (!await claimGate(sessionId, id, state, transport))
13430
+ continue;
13478
13431
  const result = await resolveLocalAgent(sessionId, id, req, state);
13479
13432
  await postToolResult(id, result, "local_agent", transport, state.secrets.get(id));
13480
13433
  }
@@ -17718,9 +17671,6 @@ ${items.length} task(s) available`));
17718
17671
  // src/commands/runner.ts
17719
17672
  import os3 from "node:os";
17720
17673
  init_errors();
17721
- function sleep5(ms) {
17722
- return new Promise((r) => setTimeout(r, ms));
17723
- }
17724
17674
  function hostLabel() {
17725
17675
  try {
17726
17676
  return os3.hostname() || undefined;
@@ -17734,92 +17684,24 @@ function sleepUnref(ms) {
17734
17684
  t.unref?.();
17735
17685
  });
17736
17686
  }
17737
- var DEFAULT_LEASE_S = 300;
17738
17687
  var ACK_INTERVAL_MS = 5000;
17739
- function heartbeatIntervalMs(leaseTimeoutS) {
17740
- const lease = typeof leaseTimeoutS === "number" && leaseTimeoutS > 0 ? leaseTimeoutS : DEFAULT_LEASE_S;
17741
- return Math.min(lease / 3, 60) * 1000;
17742
- }
17743
- async function runOnce(ctx, opts, handled) {
17744
- const params = { limit: String(opts.pollLimit ?? 5) };
17745
- if (opts.team)
17746
- params.team_id = opts.team;
17747
- const tasks = await ctx.api.get("/api/tasks/poll", { params });
17748
- const candidates = Array.isArray(tasks) ? tasks : [];
17749
- const matchesAgent = (t) => !opts.agent || t.assigned_agent_id === opts.agent || t.agent_definition_id === opts.agent;
17750
- const task = candidates.find((t) => (t.status ?? "open") === "open" && !handled?.has(t.id) && matchesAgent(t));
17751
- if (!task)
17752
- return { ran: false };
17753
- if (!opts.allowHostExec) {
17754
- process.stderr.write(source_default.yellow(`
17755
- [runner] refused task ${task.id} — re-run with --allow-host-exec to allow host execution
17756
- `));
17757
- return { ran: false, taskId: task.id, status: "refused" };
17758
- }
17759
- handled?.add(task.id);
17760
- const claimed = await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/claim`, {});
17761
- const goal = claimed.goal ?? task.goal ?? "";
17762
- const intervalMs = heartbeatIntervalMs(claimed.lease_timeout_s ?? DEFAULT_LEASE_S);
17763
- process.stderr.write(source_default.dim(`
17764
- [runner] claimed ${task.id} — running: ${goal} (heartbeat every ${Math.round(intervalMs / 1000)}s)
17765
- `));
17766
- let result;
17767
- let cancelled = false;
17768
- try {
17769
- const handle = await startHostCommand(goal, `task-${task.id}`, {
17770
- cwd: opts.cwd
17771
- });
17772
- const done = handle.done.catch((e) => ({
17773
- stdout: "",
17774
- stderr: String(e),
17775
- exit_code: -1
17776
- }));
17777
- for (;; ) {
17778
- const raced = await Promise.race([
17779
- done,
17780
- sleepUnref(intervalMs).then(() => null)
17781
- ]);
17782
- if (raced !== null) {
17783
- result = raced;
17784
- break;
17785
- }
17786
- let reply;
17787
- try {
17788
- reply = await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/heartbeat`, {});
17789
- } catch (e) {
17790
- process.stderr.write(source_default.yellow(`[runner] heartbeat failed for ${task.id} (job unaffected): ${String(e)}
17791
- `));
17792
- continue;
17793
- }
17794
- if (reply && reply.signal === "cancel") {
17795
- process.stderr.write(source_default.yellow(`[runner] cancel signal for ${task.id} — killing process tree
17796
- `));
17797
- handle.kill();
17798
- result = await done;
17799
- cancelled = true;
17800
- break;
17801
- }
17802
- }
17803
- } catch (e) {
17804
- result = { stdout: "", stderr: String(e), exit_code: -1 };
17805
- }
17806
- const exit_code = result.exit_code;
17807
- const stdout = result.stdout || result.stderr || "";
17808
- const status = cancelled ? "cancelled" : exit_code === 0 ? "completed" : "failed";
17809
- await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/complete`, {
17810
- outcome: cancelled ? { stdout, exit_code, cancelled: true } : { stdout, exit_code },
17811
- status
17812
- });
17813
- process.stderr.write(source_default.dim(`[runner] completed ${task.id} — ${status} (exit ${exit_code})
17814
- `));
17815
- return { ran: true, taskId: task.id, status, exitCode: exit_code };
17816
- }
17817
17688
  function startupEchoLine(who, opts) {
17818
17689
  const identity2 = who ? who.kind === "agent" ? `agent ${who.agent_name ?? "?"} (${(who.agent_id ?? "").slice(0, 8)}…)` : `${who.kind} ${who.sub ?? ""}`.trim() : "identity unknown (whoami unavailable)";
17819
- const scope = opts.team ? `team:${opts.team}` : "direct";
17820
17690
  const exec = opts.allowHostExec ? "host exec allowed" : "host exec refused (pass --allow-host-exec)";
17821
- const modePart = opts.mode ? `, mode ${opts.mode}` : "";
17822
- return `[runner] attached as ${identity2}${modePart}, scope ${scope}, cwd ${opts.cwd}, ${exec}${opts.once ? " — once" : ""}`;
17691
+ return `[runner] attached as ${identity2} this machine is the agent's hands (host executor; the agent reasons in Martha's cloud loop), cwd ${opts.cwd}, ${exec}`;
17692
+ }
17693
+ function rejectRemovedTaskModeFlags(opts) {
17694
+ const used = [
17695
+ opts.tasks && "--tasks",
17696
+ opts.channel && "--channel",
17697
+ opts.once && "--once",
17698
+ opts.team && "--team",
17699
+ opts.agent && "--agent",
17700
+ opts.pollInterval && "--poll-interval"
17701
+ ].filter(Boolean);
17702
+ if (used.length === 0)
17703
+ return;
17704
+ throw new CLIError(`${used.join(", ")} ${used.length > 1 ? "were" : "was"} removed: ` + "`martha runner` now means ONE thing — attach this machine as a cloud " + "agent's hands (host executor). It no longer polls or runs tasks. A " + "self-hosted agent is your own program driving the task REST " + "(`martha tasks poll/claim/heartbeat/complete`) and the peer channel.", 4 /* Validation */);
17823
17705
  }
17824
17706
  function shouldFlushAck(pendingAckId, lastAckedId, lastAckAt, now, force, intervalMs) {
17825
17707
  if (!pendingAckId || pendingAckId === lastAckedId)
@@ -17879,7 +17761,7 @@ async function runChannelLoop(ctx, state, abort) {
17879
17761
  if (abort.signal.aborted)
17880
17762
  break;
17881
17763
  if (err instanceof MarthaAPIError && err.status === 404) {
17882
- process.stderr.write(source_default.dim(`[runner] channel unavailable (server flag off) — task polling continues
17764
+ process.stderr.write(source_default.dim(`[runner] channel unavailable (server CHANNEL_ENABLED off, or old server) — nothing to attach to; exiting
17883
17765
  `));
17884
17766
  return;
17885
17767
  }
@@ -17945,59 +17827,25 @@ async function runChannelLoop(ctx, state, abort) {
17945
17827
  }
17946
17828
  }
17947
17829
  function registerRunnerCommand(program2) {
17948
- program2.command("runner").description("Attach this machine as an agent's host executor (#822). Two modes: " + "--tools executes host-tool calls pushed over the channel (a Martha-hosted " + "agent's host runner); --tasks claims and runs tasks (a self-hosted agent's " + "executor). Identity comes from the API token.").option("--tools", "Host-runner mode: execute host-tool calls pushed over the per-agent channel. " + "Never polls task queues. Requires --allow-host-exec.").option("--tasks", "Task-executor mode: poll/claim/run tasks assigned to this agent.").option("--agent <id>", "Deprecated: identity comes from the API token; applied as a client-side filter only").option("--team <name>", "Poll this team's queue (name or UUID) instead of direct-only (task mode)").option("--once", "Claim + run a single task, then exit (task mode)").option("--poll-interval <ms>", "Poll interval when idle (ms)", "3000").option("--cwd <dir>", "Scoped workspace for host execution", process.cwd()).option("--allow-host-exec", "Consent to run host commands (required to execute)").option("--yes", "Deprecated alias for --allow-host-exec").option("--channel", "Deprecated alias for --tools alongside task polling (pre-#822 behaviour).").option("--local-agent <name>", "Local coding agent for local_agent delegation over the channel", DEFAULT_LOCAL_AGENT).action(async (opts) => {
17830
+ program2.command("runner").description("Attach this machine as a cloud agent's hands (#714): hold the per-agent " + "channel open and execute host-tool calls the agent's Martha-hosted " + "reasoning loop parks on (chat gates and assigned-task parks). Identity " + "comes from the API token (the agent's runner key). The runner never " + "reasons and never runs tasks self-hosted agents drive the task REST " + "(`martha tasks poll/claim/complete`) with their own code.").option("--tools", "Accepted no-op: hands mode is the only mode (kept so printed " + "setup-host-runner commands stay valid).").option("--cwd <dir>", "Scoped workspace for host execution", process.cwd()).option("--allow-host-exec", "Consent to run host commands (required to execute)").option("--yes", "Deprecated alias for --allow-host-exec").option("--local-agent <name>", "Local coding agent for local_agent delegation over the channel", DEFAULT_LOCAL_AGENT).option("--tasks", "REMOVED (#714): the runner no longer runs tasks").option("--channel", "REMOVED (#714): deprecated pre-#822 alias").option("--once", "REMOVED (#714): belonged to the task-executor mode").option("--team <name>", "REMOVED (#714): belonged to the task-executor mode").option("--agent <id>", "REMOVED (#714): identity comes from the API token").option("--poll-interval <ms>", "REMOVED (#714): the runner no longer polls").action(async (opts) => {
17831
+ rejectRemovedTaskModeFlags(opts);
17949
17832
  const ctx = createContext({
17950
17833
  profileOverride: program2.opts().profile,
17951
17834
  verbose: program2.opts().verbose,
17952
17835
  apiUrlOverride: program2.opts().apiUrl
17953
17836
  });
17954
- const pollInterval = parseInt(opts.pollInterval, 10);
17955
- if (isNaN(pollInterval) || pollInterval < 250) {
17956
- throw new CLIError("--poll-interval must be >= 250 (ms)", 4 /* Validation */);
17957
- }
17958
- if (opts.agent) {
17959
- process.stderr.write(source_default.yellow(`[runner] --agent is deprecated — identity comes from the API token; the value is applied as a client-side filter only
17960
- `));
17961
- }
17962
- const runOpts = {
17963
- agent: opts.agent,
17964
- team: opts.team,
17965
- cwd: opts.cwd,
17966
- allowHostExec: !!(opts.allowHostExec || opts.yes)
17967
- };
17968
- let runTasks;
17969
- let runChannel;
17970
- if (opts.tools || opts.tasks) {
17971
- runTasks = !!opts.tasks;
17972
- runChannel = !!opts.tools;
17973
- } else if (opts.channel) {
17974
- runTasks = true;
17975
- runChannel = true;
17976
- } else {
17977
- runTasks = true;
17978
- runChannel = false;
17979
- }
17980
- const mode = runTasks && runChannel ? "tools+tasks" : runChannel ? "tools" : "tasks";
17837
+ const allowHostExec = !!(opts.allowHostExec || opts.yes);
17981
17838
  let who;
17982
17839
  try {
17983
17840
  who = await ctx.api.get("/api/tasks/whoami");
17984
17841
  } catch {}
17985
- process.stderr.write(source_default.dim(`${startupEchoLine(who, { team: opts.team, cwd: opts.cwd, allowHostExec: runOpts.allowHostExec, once: !!opts.once, mode })}
17842
+ process.stderr.write(source_default.dim(`${startupEchoLine(who, { cwd: opts.cwd, allowHostExec })}
17986
17843
  `));
17987
- if (runChannel && !runOpts.allowHostExec) {
17988
- throw new CLIError("--tools executes host-tool calls; re-run with --allow-host-exec to consent", 4 /* Validation */);
17844
+ if (!allowHostExec) {
17845
+ throw new CLIError("the runner executes host-tool calls; re-run with --allow-host-exec to consent", 4 /* Validation */);
17989
17846
  }
17990
- if (opts.once) {
17991
- if (!runTasks) {
17992
- throw new CLIError("--once runs a single TASK; it has no meaning in --tools (host-runner) mode", 4 /* Validation */);
17993
- }
17994
- await runOnce(ctx, runOpts);
17995
- return;
17996
- }
17997
- let stop = false;
17998
17847
  const abort = new AbortController;
17999
17848
  const onSignal = () => {
18000
- stop = true;
18001
17849
  abort.abort();
18002
17850
  process.stderr.write(`
18003
17851
  [runner] stopping…
@@ -18005,34 +17853,13 @@ function registerRunnerCommand(program2) {
18005
17853
  };
18006
17854
  process.on("SIGINT", onSignal);
18007
17855
  process.on("SIGTERM", onSignal);
18008
- const pollLoop = async () => {
18009
- const handled = new Set;
18010
- while (!stop) {
18011
- let r;
18012
- try {
18013
- r = await runOnce(ctx, runOpts, handled);
18014
- } catch (e) {
18015
- process.stderr.write(source_default.red(`[runner] error: ${String(e)}
18016
- `));
18017
- r = { ran: false };
18018
- }
18019
- if (!r.ran)
18020
- await sleep5(pollInterval);
18021
- }
18022
- };
18023
- const loops = [];
18024
- if (runTasks)
18025
- loops.push(pollLoop());
18026
- if (runChannel) {
18027
- const state = createLocalExecState({
18028
- cwd: opts.cwd,
18029
- autoYes: runOpts.allowHostExec,
18030
- localAgent: opts.localAgent ?? DEFAULT_LOCAL_AGENT
18031
- });
18032
- loops.push(runChannelLoop(ctx, state, abort));
18033
- }
17856
+ const state = createLocalExecState({
17857
+ cwd: opts.cwd,
17858
+ autoYes: allowHostExec,
17859
+ localAgent: opts.localAgent ?? DEFAULT_LOCAL_AGENT
17860
+ });
18034
17861
  try {
18035
- await Promise.all(loops);
17862
+ await runChannelLoop(ctx, state, abort);
18036
17863
  } finally {
18037
17864
  process.off("SIGINT", onSignal);
18038
17865
  process.off("SIGTERM", onSignal);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.45.0",
3
+ "version": "0.48.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {