@aiaiai-pt/martha-cli 0.24.0 → 0.26.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 (2) hide show
  1. package/dist/index.js +183 -90
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2158,6 +2158,8 @@ function extractDetail(body) {
2158
2158
  const nested = obj.detail;
2159
2159
  if (typeof nested.message === "string")
2160
2160
  return nested.message;
2161
+ if (typeof nested.code === "string")
2162
+ return nested.code;
2161
2163
  if (nested.code === "mcp_oauth_authorization_required" && typeof nested.authorization_url === "string") {
2162
2164
  return "MCP OAuth authorization required";
2163
2165
  }
@@ -11073,11 +11075,13 @@ ${items.length} ${config.typeNamePlural}`));
11073
11075
  }
11074
11076
 
11075
11077
  // src/commands/chat.ts
11078
+ import { createHash as createHash2 } from "node:crypto";
11076
11079
  import { createInterface as createInterface2 } from "node:readline";
11080
+ init_config();
11077
11081
  init_errors();
11078
11082
 
11079
11083
  // src/version.ts
11080
- var CLI_VERSION = "0.24.0";
11084
+ var CLI_VERSION = "0.26.0";
11081
11085
 
11082
11086
  // src/commands/sessions.ts
11083
11087
  function relativeTime(iso) {
@@ -11351,6 +11355,7 @@ function createApprovalTagStripper() {
11351
11355
 
11352
11356
  // src/lib/local-exec.ts
11353
11357
  import { spawn } from "node:child_process";
11358
+ import { randomBytes as randomBytes2 } from "node:crypto";
11354
11359
  import { closeSync, openSync, promises as fs5 } from "node:fs";
11355
11360
  import * as path4 from "node:path";
11356
11361
  var DEFAULT_TIMEOUT_MS = 20000;
@@ -11373,15 +11378,26 @@ async function runHostCommand(command, toolCallId, opts) {
11373
11378
  return "";
11374
11379
  }
11375
11380
  };
11381
+ const stdin = opts.stdinData != null ? "pipe" : "ignore";
11376
11382
  return await new Promise((resolve, reject) => {
11377
11383
  let settled = false;
11378
- const child = spawn(command, {
11384
+ const child = opts.argv ? spawn(opts.argv[0], opts.argv.slice(1), {
11385
+ shell: false,
11386
+ cwd: opts.cwd,
11387
+ detached: true,
11388
+ stdio: [stdin, fd, fd]
11389
+ }) : spawn(command, {
11379
11390
  shell: true,
11380
11391
  cwd: opts.cwd,
11381
11392
  detached: true,
11382
- stdio: ["ignore", fd, fd]
11393
+ stdio: [stdin, fd, fd]
11383
11394
  });
11384
11395
  closeSync(fd);
11396
+ if (opts.stdinData != null && child.stdin) {
11397
+ child.stdin.on("error", () => {});
11398
+ child.stdin.write(opts.stdinData);
11399
+ child.stdin.end();
11400
+ }
11385
11401
  const timer = setTimeout(async () => {
11386
11402
  if (settled)
11387
11403
  return;
@@ -11392,7 +11408,8 @@ async function runHostCommand(command, toolCallId, opts) {
11392
11408
  stderr: "",
11393
11409
  exit_code: 0,
11394
11410
  backgrounded: true,
11395
- pid: child.pid
11411
+ pid: child.pid,
11412
+ log_path: logPath
11396
11413
  });
11397
11414
  }, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
11398
11415
  child.on("error", (e) => {
@@ -11407,13 +11424,21 @@ async function runHostCommand(command, toolCallId, opts) {
11407
11424
  return;
11408
11425
  settled = true;
11409
11426
  clearTimeout(timer);
11410
- resolve({ stdout: await readLog(), stderr: "", exit_code: code ?? -1 });
11427
+ resolve({
11428
+ stdout: await readLog(),
11429
+ stderr: "",
11430
+ exit_code: code ?? -1,
11431
+ log_path: logPath
11432
+ });
11411
11433
  });
11412
11434
  });
11413
11435
  }
11414
11436
  function safeName(s) {
11415
11437
  return s.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 120) || "task";
11416
11438
  }
11439
+ function mintExecSecret() {
11440
+ return randomBytes2(32).toString("hex");
11441
+ }
11417
11442
 
11418
11443
  class Journal {
11419
11444
  cwd;
@@ -11425,9 +11450,13 @@ class Journal {
11425
11450
  }
11426
11451
  async load() {
11427
11452
  try {
11428
- return JSON.parse(await fs5.readFile(this.file(), "utf8"));
11453
+ const raw = JSON.parse(await fs5.readFile(this.file(), "utf8"));
11454
+ if (raw["entries"] !== undefined) {
11455
+ return raw;
11456
+ }
11457
+ return { entries: raw, secrets: {} };
11429
11458
  } catch {
11430
- return {};
11459
+ return { entries: {}, secrets: {} };
11431
11460
  }
11432
11461
  }
11433
11462
  async save(data) {
@@ -11438,11 +11467,11 @@ class Journal {
11438
11467
  return `${sessionId}:${toolCallId}`;
11439
11468
  }
11440
11469
  async lookup(sessionId, toolCallId) {
11441
- return (await this.load())[Journal.key(sessionId, toolCallId)];
11470
+ return (await this.load()).entries[Journal.key(sessionId, toolCallId)];
11442
11471
  }
11443
11472
  async recordRunning(sessionId, toolCallId, command, now) {
11444
11473
  const data = await this.load();
11445
- data[Journal.key(sessionId, toolCallId)] = {
11474
+ data.entries[Journal.key(sessionId, toolCallId)] = {
11446
11475
  state: "running",
11447
11476
  command,
11448
11477
  started_at: now
@@ -11452,8 +11481,8 @@ class Journal {
11452
11481
  async recordDone(sessionId, toolCallId, result) {
11453
11482
  const data = await this.load();
11454
11483
  const k = Journal.key(sessionId, toolCallId);
11455
- const prev = data[k];
11456
- data[k] = {
11484
+ const prev = data.entries[k];
11485
+ data.entries[k] = {
11457
11486
  state: "done",
11458
11487
  command: prev?.command ?? "",
11459
11488
  started_at: prev?.started_at ?? "",
@@ -11461,6 +11490,14 @@ class Journal {
11461
11490
  };
11462
11491
  await this.save(data);
11463
11492
  }
11493
+ async recordSecret(sessionId, toolCallId, secret) {
11494
+ const data = await this.load();
11495
+ data.secrets[Journal.key(sessionId, toolCallId)] = secret;
11496
+ await this.save(data);
11497
+ }
11498
+ async lookupSecret(sessionId, toolCallId) {
11499
+ return (await this.load()).secrets[Journal.key(sessionId, toolCallId)];
11500
+ }
11464
11501
  }
11465
11502
  function decideFromJournal(entry) {
11466
11503
  if (!entry)
@@ -11816,66 +11853,51 @@ function parseHostFileRequests(data) {
11816
11853
  }
11817
11854
 
11818
11855
  // src/lib/local-agent.ts
11819
- import { spawn as spawn2 } from "node:child_process";
11820
- var DEFAULT_LOCAL_AGENT_CMD = "codex exec";
11856
+ var AGENT_PRESETS = {
11857
+ claude: {
11858
+ name: "claude",
11859
+ command: ["claude", "-p"],
11860
+ promptVia: "argv",
11861
+ description: "Claude Code headless (prompt passed as an argument)."
11862
+ },
11863
+ codex: {
11864
+ name: "codex",
11865
+ command: ["codex", "exec", "--skip-git-repo-check"],
11866
+ promptVia: "stdin",
11867
+ description: "OpenAI Codex CLI (prompt on stdin; runs outside a git repo)."
11868
+ },
11869
+ "cursor-agent": {
11870
+ name: "cursor-agent",
11871
+ command: ["cursor-agent", "--print", "--trust"],
11872
+ promptVia: "argv",
11873
+ description: "Cursor Agent headless (prompt passed as an argument)."
11874
+ }
11875
+ };
11876
+ var DEFAULT_LOCAL_AGENT = "codex";
11821
11877
  var DEFAULT_LOCAL_AGENT_TIMEOUT_MS = 30 * 60 * 1000;
11822
- var MAX_OUTPUT2 = 256 * 1024;
11823
- function buildAgentArgv(agentCmd, task) {
11824
- const parts = agentCmd.trim().split(/\s+/).filter(Boolean);
11825
- if (parts.length === 0)
11826
- throw new Error("MARTHA_LOCAL_AGENT_CMD is empty");
11827
- return [...parts, task];
11828
- }
11829
- async function runLocalAgent(task, opts) {
11830
- const argv = buildAgentArgv(opts.agentCmd, task);
11831
- const cap = opts.maxOutputBytes ?? MAX_OUTPUT2;
11832
- const timeoutMs = opts.timeoutMs ?? DEFAULT_LOCAL_AGENT_TIMEOUT_MS;
11833
- return await new Promise((resolve2, reject) => {
11834
- let out = "";
11835
- let settled = false;
11836
- let child;
11837
- try {
11838
- child = spawn2(argv[0], argv.slice(1), {
11839
- cwd: opts.cwd,
11840
- shell: false,
11841
- stdio: ["ignore", "pipe", "pipe"]
11842
- });
11843
- } catch (e) {
11844
- reject(e);
11845
- return;
11846
- }
11847
- const append = (chunk) => {
11848
- if (out.length < cap)
11849
- out += chunk.toString("utf8");
11878
+ function resolveAgentInvocation(value) {
11879
+ const trimmed = (value || "").trim();
11880
+ if (!trimmed)
11881
+ throw new Error("local agent command is empty");
11882
+ const preset = AGENT_PRESETS[trimmed];
11883
+ if (preset) {
11884
+ return {
11885
+ command: preset.command,
11886
+ promptVia: preset.promptVia,
11887
+ source: preset.name
11850
11888
  };
11851
- child.stdout?.on("data", append);
11852
- child.stderr?.on("data", append);
11853
- const timer = setTimeout(() => {
11854
- if (settled)
11855
- return;
11856
- settled = true;
11857
- child.kill("SIGKILL");
11858
- resolve2({
11859
- text: out.slice(0, cap),
11860
- exit_code: -1,
11861
- timed_out: true
11862
- });
11863
- }, timeoutMs);
11864
- child.on("error", (e) => {
11865
- if (settled)
11866
- return;
11867
- settled = true;
11868
- clearTimeout(timer);
11869
- reject(e);
11870
- });
11871
- child.on("close", (code) => {
11872
- if (settled)
11873
- return;
11874
- settled = true;
11875
- clearTimeout(timer);
11876
- resolve2({ text: out.slice(0, cap), exit_code: code ?? -1 });
11877
- });
11878
- });
11889
+ }
11890
+ const parts = trimmed.split(/\s+/).filter(Boolean);
11891
+ return { command: parts, promptVia: "argv", source: trimmed };
11892
+ }
11893
+ function buildSpawn(invocation, task) {
11894
+ if (invocation.command.length === 0)
11895
+ throw new Error("local agent command is empty");
11896
+ const [bin, ...rest] = invocation.command;
11897
+ if (invocation.promptVia === "stdin") {
11898
+ return { bin, args: rest, stdin: task };
11899
+ }
11900
+ return { bin, args: [...rest, task], stdin: null };
11879
11901
  }
11880
11902
  function parseLocalAgentRequests(data) {
11881
11903
  let tools;
@@ -12032,7 +12054,7 @@ async function resolveHostFile(req, state) {
12032
12054
  return { error: true, message: result.message };
12033
12055
  return result;
12034
12056
  }
12035
- async function resolveLocalAgent(req, state) {
12057
+ async function resolveLocalAgent(sessionId, toolCallId, req, state) {
12036
12058
  if (!state.autoYes) {
12037
12059
  process.stderr.write(source_default.yellow(`
12038
12060
  [local_agent] refused (re-run with --yes to allow): ${req.task.slice(0, 80)}
@@ -12042,28 +12064,87 @@ async function resolveLocalAgent(req, state) {
12042
12064
  message: "Local agent delegation declined; re-run with --yes to allow."
12043
12065
  };
12044
12066
  }
12045
- const agentCmd = process.env.MARTHA_LOCAL_AGENT_CMD || DEFAULT_LOCAL_AGENT_CMD;
12067
+ let invocation;
12068
+ try {
12069
+ invocation = resolveAgentInvocation(state.localAgent);
12070
+ } catch (e) {
12071
+ return { error: true, message: `invalid local agent config: ${String(e)}` };
12072
+ }
12073
+ const decision = decideFromJournal(await state.journal.lookup(sessionId, toolCallId));
12074
+ if (decision.kind === "replay")
12075
+ return decision.result;
12076
+ if (decision.kind === "crashed") {
12077
+ return {
12078
+ error: true,
12079
+ exit_code: -1,
12080
+ message: `A previous local_agent run (${decision.command}) did not complete; ` + `not re-running. Its log survives under .martha-runner/ — read it with host_read.`
12081
+ };
12082
+ }
12046
12083
  const cwd = req.cwd ? `${state.cwd}/${req.cwd}` : state.cwd;
12084
+ const { bin, args, stdin } = buildSpawn(invocation, req.task);
12047
12085
  process.stderr.write(source_default.dim(`
12048
- [local_agent] (${agentCmd}) ${req.task.slice(0, 100)}
12086
+ [local_agent] (${invocation.source}) ${req.task.slice(0, 100)}
12049
12087
  `));
12088
+ await state.journal.recordRunning(sessionId, toolCallId, `local_agent:${invocation.source}`, new Date().toISOString());
12089
+ let result;
12050
12090
  try {
12051
- const result = await runLocalAgent(req.task, { cwd, agentCmd });
12052
- return {
12053
- text: result.text || "(local agent produced no output)",
12054
- exit_code: result.exit_code,
12055
- ...result.timed_out ? { timed_out: true } : {}
12056
- };
12091
+ result = await runHostCommand(`local_agent:${invocation.source}`, toolCallId, {
12092
+ cwd,
12093
+ argv: [bin, ...args],
12094
+ stdinData: stdin ?? undefined,
12095
+ timeoutMs: DEFAULT_LOCAL_AGENT_TIMEOUT_MS
12096
+ });
12057
12097
  } catch (e) {
12098
+ const failed = {
12099
+ stdout: "",
12100
+ stderr: String(e),
12101
+ exit_code: -1
12102
+ };
12103
+ await state.journal.recordDone(sessionId, toolCallId, failed);
12058
12104
  return {
12059
12105
  error: true,
12060
- message: `local agent failed to start (${agentCmd}): ${String(e)}. ` + `Set MARTHA_LOCAL_AGENT_CMD to an installed agent command.`
12106
+ message: `local agent failed to start (${invocation.source}): ${String(e)}. ` + `Pick an installed agent with --local-agent <claude|codex|cursor-agent> ` + `or set one via 'martha config set local_agent <name>'.`
12107
+ };
12108
+ }
12109
+ await state.journal.recordDone(sessionId, toolCallId, result);
12110
+ if (result.backgrounded) {
12111
+ const mins = Math.round(DEFAULT_LOCAL_AGENT_TIMEOUT_MS / 60000);
12112
+ return {
12113
+ text: result.stdout || "(no output captured yet)",
12114
+ backgrounded: true,
12115
+ pid: result.pid,
12116
+ log_path: result.log_path,
12117
+ message: `The local agent is still running after ${mins} min; it was left running ` + `in the background (it survives this session). Read its live log and final ` + `result with host_read on ${result.log_path}.`
12061
12118
  };
12062
12119
  }
12120
+ return {
12121
+ text: result.stdout || "(local agent produced no output)",
12122
+ exit_code: result.exit_code,
12123
+ log_path: result.log_path
12124
+ };
12063
12125
  }
12064
- async function _postToolResult(ctx, sessionId, toolCallId, result, label) {
12126
+ async function claimGate(ctx, sessionId, toolCallId, state) {
12127
+ if (state.claimed.has(toolCallId))
12128
+ return;
12129
+ const secret = state.secrets.get(toolCallId) ?? mintExecSecret();
12130
+ const secretHash = createHash2("sha256").update(secret).digest("hex");
12131
+ state.secrets.set(toolCallId, secret);
12132
+ await state.journal.recordSecret(sessionId, toolCallId, secret);
12065
12133
  try {
12066
- await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/tool-result`, { tool_call_id: toolCallId, result });
12134
+ await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/tool-claim`, { tool_call_id: toolCallId, secret_hash: secretHash });
12135
+ state.claimed.add(toolCallId);
12136
+ } catch (e) {
12137
+ process.stderr.write(source_default.yellow(`[local-exec] failed to claim gate for ${toolCallId}: ${String(e)}
12138
+ `));
12139
+ }
12140
+ }
12141
+ async function _postToolResult(ctx, sessionId, toolCallId, result, label, execSecret) {
12142
+ try {
12143
+ await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/tool-result`, {
12144
+ tool_call_id: toolCallId,
12145
+ result,
12146
+ ...execSecret !== undefined ? { exec_secret: execSecret } : {}
12147
+ });
12067
12148
  } catch (e) {
12068
12149
  process.stderr.write(source_default.red(`[${label}] failed to POST result: ${String(e)}
12069
12150
  `));
@@ -12075,40 +12156,45 @@ async function handleHostExecFrame(ctx, sessionId, data, state) {
12075
12156
  if (state.handled.has(id))
12076
12157
  continue;
12077
12158
  state.handled.add(id);
12159
+ await claimGate(ctx, sessionId, id, state);
12078
12160
  const result = await resolveHostExec(sessionId, id, req.command, state);
12079
- await _postToolResult(ctx, sessionId, id, result, "host_exec");
12161
+ await _postToolResult(ctx, sessionId, id, result, "host_exec", state.secrets.get(id));
12080
12162
  }
12081
12163
  for (const req of parseHostScreenshotRequests(data)) {
12082
12164
  const id = req.toolCallId;
12083
12165
  if (state.handled.has(id))
12084
12166
  continue;
12085
12167
  state.handled.add(id);
12168
+ await claimGate(ctx, sessionId, id, state);
12086
12169
  const result = await resolveHostScreenshot(ctx, sessionId, req, state);
12087
- await _postToolResult(ctx, sessionId, id, result, "host_screenshot");
12170
+ await _postToolResult(ctx, sessionId, id, result, "host_screenshot", state.secrets.get(id));
12088
12171
  }
12089
12172
  for (const req of parseHostBrowserRequests(data)) {
12090
12173
  const id = req.toolCallId;
12091
12174
  if (state.handled.has(id))
12092
12175
  continue;
12093
12176
  state.handled.add(id);
12177
+ await claimGate(ctx, sessionId, id, state);
12094
12178
  const result = await resolveHostBrowser(ctx, sessionId, req, state);
12095
- await _postToolResult(ctx, sessionId, id, result, "host_browser");
12179
+ await _postToolResult(ctx, sessionId, id, result, "host_browser", state.secrets.get(id));
12096
12180
  }
12097
12181
  for (const req of parseHostFileRequests(data)) {
12098
12182
  const id = req.toolCallId;
12099
12183
  if (state.handled.has(id))
12100
12184
  continue;
12101
12185
  state.handled.add(id);
12186
+ await claimGate(ctx, sessionId, id, state);
12102
12187
  const result = await resolveHostFile(req, state);
12103
- await _postToolResult(ctx, sessionId, id, result, req.action);
12188
+ await _postToolResult(ctx, sessionId, id, result, req.action, state.secrets.get(id));
12104
12189
  }
12105
12190
  for (const req of parseLocalAgentRequests(data)) {
12106
12191
  const id = req.toolCallId;
12107
12192
  if (state.handled.has(id))
12108
12193
  continue;
12109
12194
  state.handled.add(id);
12110
- const result = await resolveLocalAgent(req, state);
12111
- await _postToolResult(ctx, sessionId, id, result, "local_agent");
12195
+ await claimGate(ctx, sessionId, id, state);
12196
+ const result = await resolveLocalAgent(sessionId, id, req, state);
12197
+ await _postToolResult(ctx, sessionId, id, result, "local_agent", state.secrets.get(id));
12112
12198
  }
12113
12199
  }
12114
12200
  function parseSlashCommand(input) {
@@ -12164,8 +12250,11 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
12164
12250
  const localExecState = opts.localExec ? {
12165
12251
  cwd: opts.localExec.cwd,
12166
12252
  autoYes: opts.localExec.autoYes,
12253
+ localAgent: opts.localExec.agent,
12167
12254
  handled: new Set,
12168
- journal: new Journal(opts.localExec.cwd)
12255
+ journal: new Journal(opts.localExec.cwd),
12256
+ secrets: new Map,
12257
+ claimed: new Set
12169
12258
  } : null;
12170
12259
  const reqBody = { content: message };
12171
12260
  if (opts.localExec)
@@ -12685,7 +12774,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName,
12685
12774
  }
12686
12775
  }
12687
12776
  function registerChatCommand(program2) {
12688
- program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--local-exec", "Allow the agent to run host tools on THIS machine (#568). Requires the server to have LOCAL_EXEC_ENABLED=1.").option("--yes", "Auto-approve host tool execution (required with --local-exec for non-interactive runs).").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
12777
+ program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--local-exec", "Allow the agent to run host tools on THIS machine (#568). Requires the server to have LOCAL_EXEC_ENABLED=1.").option("--yes", "Auto-approve host tool execution (required with --local-exec for non-interactive runs).").option("--local-agent <name>", "Which local coding agent `local_agent` delegates to: a preset (claude | codex | cursor-agent) or a raw command. Overrides MARTHA_LOCAL_AGENT_CMD and `config local_agent` for this run.").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
12689
12778
  const ctx = createContext({
12690
12779
  profileOverride: program2.opts().profile,
12691
12780
  verbose: program2.opts().verbose
@@ -12720,7 +12809,8 @@ function registerChatCommand(program2) {
12720
12809
  }
12721
12810
  const showTools = !!opts.showTools;
12722
12811
  const timeoutMs = opts.timeout ? parseTimeoutSeconds(opts.timeout) * 1000 : undefined;
12723
- const localExec = opts.localExec ? { cwd: process.cwd(), autoYes: !!opts.yes } : undefined;
12812
+ const localAgentChoice = opts.localAgent || process.env.MARTHA_LOCAL_AGENT_CMD || loadConfig().settings.local_agent || DEFAULT_LOCAL_AGENT;
12813
+ const localExec = opts.localExec ? { cwd: process.cwd(), autoYes: !!opts.yes, agent: localAgentChoice } : undefined;
12724
12814
  if (opts.message) {
12725
12815
  await runOneShot(ctx, sessionId, opts.message, isJson, clientId, agentName, showTools, timeoutMs, localExec);
12726
12816
  } else {
@@ -12841,6 +12931,8 @@ Global Settings
12841
12931
  console.log(` Editor: ${settings.editor}`);
12842
12932
  if (settings.pager)
12843
12933
  console.log(` Pager: ${settings.pager}`);
12934
+ if (settings.local_agent)
12935
+ console.log(` Local agent: ${settings.local_agent}`);
12844
12936
  if (settings.chat) {
12845
12937
  if (settings.chat.show_tool_calls)
12846
12938
  console.log(` Tool calls: ${settings.chat.show_tool_calls}`);
@@ -12879,6 +12971,7 @@ var SETTINGS_KEYS = {
12879
12971
  color: (s) => s.color,
12880
12972
  editor: (s) => s.editor,
12881
12973
  pager: (s) => s.pager,
12974
+ local_agent: (s) => s.local_agent,
12882
12975
  "chat.show_tool_calls": (s) => s.chat?.show_tool_calls,
12883
12976
  "chat.markdown": (s) => s.chat?.markdown,
12884
12977
  "chat.history_file": (s) => s.chat?.history_file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {