@aiaiai-pt/martha-cli 0.68.1 → 0.69.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project adheres to semver — `0.x` releases may include breaking changes between minor versions.
4
4
 
5
+ ## [0.69.0] — 2026-11-13
6
+
7
+ ### Added
8
+
9
+ - `martha definitions apply` preserves the server-supported agent `base_pack_opt_out` flag, allowing narrowly granted specialists such as delegated customer support without restoring broad default tools. This release retains the current published CLI capabilities.
10
+
11
+ ## [0.68.4] — 2026-11-09
12
+
13
+ ### Fixed
14
+
15
+ - `martha definitions apply` compares a function's `risk_tags` as the server stores them: a legacy spelling (`write`, `delete`, …) matches the canonical tag it maps to, and order no longer matters. It used to report a change on every apply once the server stored the canonical form (MARTHA-602).
16
+
17
+ ## [0.68.3] — 2026-11-09
18
+
19
+ ### Fixed
20
+
21
+ - `martha functions update` and `martha definitions apply` now check the function's `tool` block (the per-tool call timeout, #1833): an update whose `tool` the server did not persist fails instead of reporting success.
22
+
23
+ ## [0.68.2] — 2026-11-08
24
+
25
+ ### Fixed
26
+
27
+ - `martha agents doctor` and `martha agents runner-presence` report runner presence as unknown when the presence registry can't be read (the server sends `present: null, available: false`) or the presence call fails. They used to say "no runner connected", which is wrong during an outage: the runner may be attached.
28
+
5
29
  ## [0.68.1] — 2026-11-03
6
30
 
7
31
  ### Fixed
package/dist/index.js CHANGED
@@ -11244,7 +11244,7 @@ init_config();
11244
11244
  init_errors();
11245
11245
 
11246
11246
  // src/version.ts
11247
- var CLI_VERSION = "0.68.1";
11247
+ var CLI_VERSION = "0.69.0";
11248
11248
 
11249
11249
  // src/commands/sessions.ts
11250
11250
  function relativeTime(iso) {
@@ -11633,18 +11633,25 @@ async function diagnoseHostRunner(ctx, name) {
11633
11633
  fix: takeover ? undefined : setupFix
11634
11634
  });
11635
11635
  let attached = false;
11636
+ let presenceUnknown = false;
11636
11637
  let attachedHost;
11637
11638
  try {
11638
11639
  const pres = await ctx.api.get(`${API_PATH}/runner-presence`);
11639
11640
  const p = pres.presence?.[name];
11640
- attached = !!p?.present;
11641
+ if (p?.present === null || p?.available === false) {
11642
+ presenceUnknown = true;
11643
+ } else {
11644
+ attached = !!p?.present;
11645
+ }
11641
11646
  attachedHost = p?.connections?.[0]?.host;
11642
- } catch {}
11647
+ } catch {
11648
+ presenceUnknown = true;
11649
+ }
11643
11650
  checks.push({
11644
- ok: attached,
11651
+ ok: presenceUnknown ? null : attached,
11645
11652
  label: "a runner is attached now",
11646
- detail: attached ? `host ${attachedHost ?? "?"}` : "no runner connected",
11647
- fix: attached ? undefined : `on the target machine: martha runner --tools --allow-host-exec (and the server needs CHANNEL_ENABLED=1)`
11653
+ detail: presenceUnknown ? "unknown (presence registry unreachable)" : attached ? `host ${attachedHost ?? "?"}` : "no runner connected",
11654
+ fix: presenceUnknown ? "check Redis connectivity on the Martha server" : attached ? undefined : `on the target machine: martha runner --tools --allow-host-exec (and the server needs CHANNEL_ENABLED=1)`
11648
11655
  });
11649
11656
  const isHostRunner = hasKey || hostTools.length > 0;
11650
11657
  const drivableFromChat = marthaHosted && hasKey && hostTools.length > 0 && grantingClients.length > 0 && takeover;
@@ -12008,6 +12015,8 @@ Attach it as this agent's host executor:
12008
12015
  if (p.present && conns.length) {
12009
12016
  const hosts = conns.map((c) => c.host || c.connection_id?.slice(0, 8) || "?").join(", ");
12010
12017
  console.log(`${source_default.green("● connected")} ${name} ${source_default.dim(`(${hosts})`)}`);
12018
+ } else if (p.present === null || p.available === false) {
12019
+ console.log(`${source_default.yellow("? unknown")} ${name}`);
12011
12020
  } else {
12012
12021
  console.log(`${source_default.dim("○ disconnected")} ${name}`);
12013
12022
  }
@@ -15902,7 +15911,8 @@ var MUTABLE_FIELDS = {
15902
15911
  "risk_tags",
15903
15912
  "risk_level",
15904
15913
  "verification_safe",
15905
- "idempotent"
15914
+ "idempotent",
15915
+ "tool"
15906
15916
  ]),
15907
15917
  Workflow: new Set([
15908
15918
  "description",
@@ -16146,6 +16156,7 @@ var AGENT_WRITE_FIELDS = new Set([
16146
16156
  "llm_config",
16147
16157
  "loop_config",
16148
16158
  "runs_as_chat",
16159
+ "base_pack_opt_out",
16149
16160
  "agent_metadata",
16150
16161
  "auth_method",
16151
16162
  "webhook_url",
@@ -16330,6 +16341,30 @@ async function computeOperations(defs, api) {
16330
16341
  }
16331
16342
  return ops;
16332
16343
  }
16344
+ var EFFECT_TAG_ALIASES = {
16345
+ write: "mutating",
16346
+ delete: "destructive_write",
16347
+ destructive: "destructive_write",
16348
+ host_exec: "host_execution",
16349
+ readonly: "read_only",
16350
+ read: "read_only",
16351
+ payments: "payment",
16352
+ code_execution: "arbitrary_code",
16353
+ fs: "filesystem"
16354
+ };
16355
+ function canonicalEffectTag(tag) {
16356
+ if (typeof tag !== "string")
16357
+ return tag;
16358
+ const key = tag.trim().toLowerCase().replace(/[- ]/g, "_");
16359
+ return EFFECT_TAG_ALIASES[key] ?? key;
16360
+ }
16361
+ function effectTagsMatch(local, remote) {
16362
+ if (!Array.isArray(local) || !Array.isArray(remote))
16363
+ return localSubsetOf(local, remote);
16364
+ const l = new Set(local.map(canonicalEffectTag));
16365
+ const r = new Set(remote.map(canonicalEffectTag));
16366
+ return l.size === r.size && [...l].every((tag) => r.has(tag));
16367
+ }
16333
16368
  function diffFields(local, remote) {
16334
16369
  const flatRemote = flattenDefinitionWrapper(remote);
16335
16370
  const changed = [];
@@ -16342,7 +16377,8 @@ function diffFields(local, remote) {
16342
16377
  continue;
16343
16378
  const localVal = local[key];
16344
16379
  const remoteVal = flatRemote[key];
16345
- if (!localSubsetOf(localVal, remoteVal)) {
16380
+ const same = key === "risk_tags" ? effectTagsMatch(localVal, remoteVal) : localSubsetOf(localVal, remoteVal);
16381
+ if (!same) {
16346
16382
  changed.push(key);
16347
16383
  }
16348
16384
  }
@@ -16614,6 +16650,8 @@ async function applyPrompt(api, op, def) {
16614
16650
  createBody.description = def.body.description;
16615
16651
  if (def.body.scope)
16616
16652
  createBody.scope = def.body.scope;
16653
+ if (def.body.scope_entity_id)
16654
+ createBody.scope_entity_id = def.body.scope_entity_id;
16617
16655
  const res = await api.post(promptPath, createBody);
16618
16656
  const readBack = await api.get(`${promptPath}/${name}`);
16619
16657
  const prodContent = readBack.production?.content;
@@ -16658,7 +16696,8 @@ async function applyPrompt(api, op, def) {
16658
16696
  }
16659
16697
  if (metadataFields.includes("scope")) {
16660
16698
  await api.put(`${promptPath}/${name}/scope`, {
16661
- scope: def.body.scope
16699
+ scope: def.body.scope,
16700
+ ...def.body.scope_entity_id ? { scope_entity_id: def.body.scope_entity_id } : {}
16662
16701
  });
16663
16702
  }
16664
16703
  if (Object.keys(metaBody).length > 0) {
@@ -28182,11 +28221,13 @@ async function checkHostRunners(ctx) {
28182
28221
  detail: "none configured"
28183
28222
  };
28184
28223
  }
28185
- const attached = names.filter((n) => presence[n]?.present).length;
28224
+ const attached = names.filter((n) => presence[n]?.present === true).length;
28225
+ const unknown = names.filter((n) => presence[n]?.present === null || presence[n]?.available === false).length;
28226
+ const detail = unknown ? `${names.length} configured, ${attached} attached, ${unknown} unknown (registry unreachable)` : `${names.length} configured, ${attached} attached`;
28186
28227
  return {
28187
28228
  name: "Host runners",
28188
- status: "pass",
28189
- detail: `${names.length} configured, ${attached} attached`,
28229
+ status: unknown ? "warn" : "pass",
28230
+ detail,
28190
28231
  remedy: `per-agent checkup: martha agents doctor <name>`
28191
28232
  };
28192
28233
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.68.1",
3
+ "version": "0.69.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {
@@ -164,6 +164,10 @@ martha functions export [--format yaml|json] [--output FILE]
164
164
 
165
165
  Function `auth` field is the most error-prone part — see the [Function authentication patterns](#function-authentication-patterns) section below.
166
166
 
167
+ **Call timeout (`tool` block, #1833 I5).** To let one call of a slow function run longer than the default, put `tool: { timeout: 60 }` (seconds, 1-900) at the top level of the `functions create|update -f` body. The server stores it in `definition.tool`. In chat the call's activity then gets `timeout` + 15 s (never under 30 s) instead of a fixed 30 s, and the handler bounds the call by `timeout`, with a 401 credential retry sharing the same deadline. A chat session keeps the bound its tool list had when it started, and calls are capped to fit it, so start a new session after raising `timeout`. Out-of-range values or unknown keys return **422**. A function with a `handler` (platform function) may not declare more than its handler's registry `timeout` (30 s unless the decorator sets another): create/update return **422** naming that bound, and a `timeout` on an unknown handler is a 422 too (MARTHA-626; only the `__system__` seed may exceed it, and older tenant rows above it are clamped at dispatch). `tool: {}` clears it. Platform functions get it from their decorator at seed time. `client_timeout_seconds` (1-86400) is stored but not read yet. `definitions apply` does not round-trip the block yet.
168
+
169
+ **Effect tags (`risk_tags`) are a closed vocabulary.** Allowed: `read_only`, `network`, `browser`, `untrusted_content`, `external_io`, `external_write`, `mutating`, `tenant_write`, `filesystem`, `host_execution`, `infra`, `destructive_write`, `payment`, `arbitrary_code`. Legacy spellings are mapped on write (`write`→`mutating`, `delete`→`destructive_write`, `host_exec`→`host_execution`, …), so a re-`get` can show a different tag than the file. Any other tag fails `create` / `update` / `definitions apply` with HTTP 422 `unknown_effect_tag`; the body's `suggestion` is the closest valid tag. Fix the file, don't retry. The live list is `martha policy show --json` → `risk_tag_vocabulary`. Full table: `docs/reference/effect-tags.md`.
170
+
167
171
  ### Workflows CRUD + Execution
168
172
 
169
173
  ```bash