@aiaiai-pt/martha-cli 0.68.0 → 0.68.5
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 +24 -0
- package/dist/index.js +56 -16
- package/package.json +1 -1
- package/skills/martha-cli/SKILL.md +4 -0
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.68.4] — 2026-11-09
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `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).
|
|
10
|
+
|
|
11
|
+
## [0.68.3] — 2026-11-09
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `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.
|
|
16
|
+
|
|
17
|
+
## [0.68.2] — 2026-11-08
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `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.
|
|
22
|
+
|
|
23
|
+
## [0.68.1] — 2026-11-03
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- `martha prompts create -f <file>` accepts `content` again. 0.68.0 refused `content` on every prompt write so that `prompts update` would stop dropping it silently, but the create route requires `content`, so no prompt could be created from a file. The refusal now applies to `update` only.
|
|
28
|
+
|
|
5
29
|
## [0.68.0] — 2026-11-02
|
|
6
30
|
|
|
7
31
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -11149,7 +11149,7 @@ ${items.length} ${config.typeNamePlural}`));
|
|
|
11149
11149
|
throw new CLIError("Missing required field: name", 4 /* Validation */);
|
|
11150
11150
|
}
|
|
11151
11151
|
if (config.normalizeBody)
|
|
11152
|
-
body = config.normalizeBody(body);
|
|
11152
|
+
body = config.normalizeBody(body, "create");
|
|
11153
11153
|
const result = await ctx.api.post(config.apiPath, body);
|
|
11154
11154
|
if (isJson()) {
|
|
11155
11155
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -11164,7 +11164,7 @@ ${items.length} ${config.typeNamePlural}`));
|
|
|
11164
11164
|
throw new CLIError(`File name '${body.name}' does not match argument '${name}'`, 4 /* Validation */);
|
|
11165
11165
|
}
|
|
11166
11166
|
if (config.normalizeBody)
|
|
11167
|
-
body = config.normalizeBody(body);
|
|
11167
|
+
body = config.normalizeBody(body, "update");
|
|
11168
11168
|
const result = await ctx.api.put(`${config.apiPath}/${encodeURIComponent(name)}`, body);
|
|
11169
11169
|
config.verifyUpdateResult?.(body, result);
|
|
11170
11170
|
if (isJson()) {
|
|
@@ -11244,7 +11244,7 @@ init_config();
|
|
|
11244
11244
|
init_errors();
|
|
11245
11245
|
|
|
11246
11246
|
// src/version.ts
|
|
11247
|
-
var CLI_VERSION = "0.68.
|
|
11247
|
+
var CLI_VERSION = "0.68.5";
|
|
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
|
-
|
|
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",
|
|
@@ -16330,6 +16340,30 @@ async function computeOperations(defs, api) {
|
|
|
16330
16340
|
}
|
|
16331
16341
|
return ops;
|
|
16332
16342
|
}
|
|
16343
|
+
var EFFECT_TAG_ALIASES = {
|
|
16344
|
+
write: "mutating",
|
|
16345
|
+
delete: "destructive_write",
|
|
16346
|
+
destructive: "destructive_write",
|
|
16347
|
+
host_exec: "host_execution",
|
|
16348
|
+
readonly: "read_only",
|
|
16349
|
+
read: "read_only",
|
|
16350
|
+
payments: "payment",
|
|
16351
|
+
code_execution: "arbitrary_code",
|
|
16352
|
+
fs: "filesystem"
|
|
16353
|
+
};
|
|
16354
|
+
function canonicalEffectTag(tag) {
|
|
16355
|
+
if (typeof tag !== "string")
|
|
16356
|
+
return tag;
|
|
16357
|
+
const key = tag.trim().toLowerCase().replace(/[- ]/g, "_");
|
|
16358
|
+
return EFFECT_TAG_ALIASES[key] ?? key;
|
|
16359
|
+
}
|
|
16360
|
+
function effectTagsMatch(local, remote) {
|
|
16361
|
+
if (!Array.isArray(local) || !Array.isArray(remote))
|
|
16362
|
+
return localSubsetOf(local, remote);
|
|
16363
|
+
const l = new Set(local.map(canonicalEffectTag));
|
|
16364
|
+
const r = new Set(remote.map(canonicalEffectTag));
|
|
16365
|
+
return l.size === r.size && [...l].every((tag) => r.has(tag));
|
|
16366
|
+
}
|
|
16333
16367
|
function diffFields(local, remote) {
|
|
16334
16368
|
const flatRemote = flattenDefinitionWrapper(remote);
|
|
16335
16369
|
const changed = [];
|
|
@@ -16342,7 +16376,8 @@ function diffFields(local, remote) {
|
|
|
16342
16376
|
continue;
|
|
16343
16377
|
const localVal = local[key];
|
|
16344
16378
|
const remoteVal = flatRemote[key];
|
|
16345
|
-
|
|
16379
|
+
const same = key === "risk_tags" ? effectTagsMatch(localVal, remoteVal) : localSubsetOf(localVal, remoteVal);
|
|
16380
|
+
if (!same) {
|
|
16346
16381
|
changed.push(key);
|
|
16347
16382
|
}
|
|
16348
16383
|
}
|
|
@@ -16614,6 +16649,8 @@ async function applyPrompt(api, op, def) {
|
|
|
16614
16649
|
createBody.description = def.body.description;
|
|
16615
16650
|
if (def.body.scope)
|
|
16616
16651
|
createBody.scope = def.body.scope;
|
|
16652
|
+
if (def.body.scope_entity_id)
|
|
16653
|
+
createBody.scope_entity_id = def.body.scope_entity_id;
|
|
16617
16654
|
const res = await api.post(promptPath, createBody);
|
|
16618
16655
|
const readBack = await api.get(`${promptPath}/${name}`);
|
|
16619
16656
|
const prodContent = readBack.production?.content;
|
|
@@ -16658,7 +16695,8 @@ async function applyPrompt(api, op, def) {
|
|
|
16658
16695
|
}
|
|
16659
16696
|
if (metadataFields.includes("scope")) {
|
|
16660
16697
|
await api.put(`${promptPath}/${name}/scope`, {
|
|
16661
|
-
scope: def.body.scope
|
|
16698
|
+
scope: def.body.scope,
|
|
16699
|
+
...def.body.scope_entity_id ? { scope_entity_id: def.body.scope_entity_id } : {}
|
|
16662
16700
|
});
|
|
16663
16701
|
}
|
|
16664
16702
|
if (Object.keys(metaBody).length > 0) {
|
|
@@ -17817,8 +17855,8 @@ var promptsConfig = {
|
|
|
17817
17855
|
params.is_protected = "true";
|
|
17818
17856
|
return params;
|
|
17819
17857
|
},
|
|
17820
|
-
normalizeBody: (body) => {
|
|
17821
|
-
if ("content" in body) {
|
|
17858
|
+
normalizeBody: (body, action) => {
|
|
17859
|
+
if (action === "update" && "content" in body) {
|
|
17822
17860
|
throw new CLIError("`prompts update` changes metadata only and the server ignores `content`; change the text with " + "`martha prompts version <name> -f <file> --promote`", 4 /* Validation */);
|
|
17823
17861
|
}
|
|
17824
17862
|
return body;
|
|
@@ -28182,11 +28220,13 @@ async function checkHostRunners(ctx) {
|
|
|
28182
28220
|
detail: "none configured"
|
|
28183
28221
|
};
|
|
28184
28222
|
}
|
|
28185
|
-
const attached = names.filter((n) => presence[n]?.present).length;
|
|
28223
|
+
const attached = names.filter((n) => presence[n]?.present === true).length;
|
|
28224
|
+
const unknown = names.filter((n) => presence[n]?.present === null || presence[n]?.available === false).length;
|
|
28225
|
+
const detail = unknown ? `${names.length} configured, ${attached} attached, ${unknown} unknown (registry unreachable)` : `${names.length} configured, ${attached} attached`;
|
|
28186
28226
|
return {
|
|
28187
28227
|
name: "Host runners",
|
|
28188
|
-
status: "pass",
|
|
28189
|
-
detail
|
|
28228
|
+
status: unknown ? "warn" : "pass",
|
|
28229
|
+
detail,
|
|
28190
28230
|
remedy: `per-agent checkup: martha agents doctor <name>`
|
|
28191
28231
|
};
|
|
28192
28232
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -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
|