@aiaiai-pt/martha-cli 0.18.0 → 0.20.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 +7 -0
- package/dist/index.js +112 -59
- package/package.json +1 -1
- package/skills/martha-cli/SKILL.md +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.20.0] — 2026-06-21
|
|
8
|
+
|
|
9
|
+
### Added — #649 agent→agent delegation surfaces
|
|
10
|
+
- `martha agents add-agent <coordinator> <sub-agent> [--set k=v]` grants a coordinator the ability to delegate to a sub-agent; `martha agents remove-agent <coordinator> <sub-agent>` revokes it; `martha agents agents <coordinator>` lists the granted sub-agents. All support `--json`. Backed by the existing `/api/admin/definitions/agents/{name}/agents` routes (#648). Self-delegation surfaces the API's 400 (`an agent cannot delegate to itself`); an unknown sub-agent returns 404.
|
|
11
|
+
|
|
12
|
+
<!-- NOTE: package.json was already at 0.19.0 on main while this CHANGELOG's latest entry was 0.16.1 — versions 0.17.0–0.19.0 shipped without CHANGELOG sections (pre-existing drift). This 0.20.0 bump is the MINOR bump for #649 off the real 0.19.0 base; the 0.17–0.19 gap is not reconstructed here. -->
|
|
13
|
+
|
|
7
14
|
## [0.16.1] — 2026-06-14
|
|
8
15
|
|
|
9
16
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -1001,7 +1019,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1001
1019
|
this._exitCallback = (err) => {
|
|
1002
1020
|
if (err.code !== "commander.executeSubCommandAsync") {
|
|
1003
1021
|
throw err;
|
|
1004
|
-
}
|
|
1022
|
+
}
|
|
1005
1023
|
};
|
|
1006
1024
|
}
|
|
1007
1025
|
return this;
|
|
@@ -11059,7 +11077,7 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
11059
11077
|
init_errors();
|
|
11060
11078
|
|
|
11061
11079
|
// src/version.ts
|
|
11062
|
-
var CLI_VERSION = "0.
|
|
11080
|
+
var CLI_VERSION = "0.20.0";
|
|
11063
11081
|
|
|
11064
11082
|
// src/commands/sessions.ts
|
|
11065
11083
|
function relativeTime(iso) {
|
|
@@ -13559,27 +13577,6 @@ var workflowsConfig = {
|
|
|
13559
13577
|
|
|
13560
13578
|
// src/commands/agents.ts
|
|
13561
13579
|
init_errors();
|
|
13562
|
-
|
|
13563
|
-
// src/lib/collections.ts
|
|
13564
|
-
init_errors();
|
|
13565
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13566
|
-
async function resolveCollectionIdForGrant(ctx, ref) {
|
|
13567
|
-
if (UUID_RE.test(ref)) {
|
|
13568
|
-
return ref;
|
|
13569
|
-
}
|
|
13570
|
-
const all = await ctx.api.get("/api/admin/collections", {
|
|
13571
|
-
params: { active_only: "false" }
|
|
13572
|
-
});
|
|
13573
|
-
const bySlug = all.find((c) => c.slug === ref);
|
|
13574
|
-
if (bySlug)
|
|
13575
|
-
return bySlug.id;
|
|
13576
|
-
const byName = all.find((c) => c.name === ref);
|
|
13577
|
-
if (byName)
|
|
13578
|
-
return byName.id;
|
|
13579
|
-
throw new CLIError(`Collection '${ref}' not found (tried UUID, slug, and name).`, 3 /* NotFound */, "Run `martha documents collections --tree` to see available collections.");
|
|
13580
|
-
}
|
|
13581
|
-
|
|
13582
|
-
// src/commands/agents.ts
|
|
13583
13580
|
function parseSetFlags(items) {
|
|
13584
13581
|
const result = {};
|
|
13585
13582
|
for (const item of items) {
|
|
@@ -13756,7 +13753,10 @@ var agentsConfig = {
|
|
|
13756
13753
|
console.log(` Updated: ${agent.updated_at}`);
|
|
13757
13754
|
},
|
|
13758
13755
|
extraCommands: (parentCmd, getCtx, isJson) => {
|
|
13759
|
-
parentCmd.command("add-function <agent> <functionName>").description("Add a function to an agent").option("--set <items...>", "Config overrides (key=value)").option("--collection <slug-or-id>", "
|
|
13756
|
+
parentCmd.command("add-function <agent> <functionName>").description("Add a function to an agent").option("--set <items...>", "Config overrides (key=value)").option("--collection <slug-or-id>", "REMOVED (#609) — collection access is managed separately; see error").action(async (agent, functionName, opts) => {
|
|
13757
|
+
if (opts.collection) {
|
|
13758
|
+
throw new CLIError("`--collection` was removed; collection access is managed separately: " + "`martha collections grant <collection> --to agent:<id>`.", 4 /* Validation */);
|
|
13759
|
+
}
|
|
13760
13760
|
const ctx = getCtx();
|
|
13761
13761
|
const body = {
|
|
13762
13762
|
function_name: functionName
|
|
@@ -13764,36 +13764,29 @@ var agentsConfig = {
|
|
|
13764
13764
|
if (opts.set) {
|
|
13765
13765
|
body.config_overrides = parseSetFlags(opts.set);
|
|
13766
13766
|
}
|
|
13767
|
-
if (opts.collection) {
|
|
13768
|
-
body.collection_id = await resolveCollectionIdForGrant(ctx, opts.collection);
|
|
13769
|
-
}
|
|
13770
13767
|
const result = await ctx.api.post(`${API_PATH}/${encodeURIComponent(agent)}/functions`, body);
|
|
13771
13768
|
if (isJson()) {
|
|
13772
13769
|
console.log(JSON.stringify(result, null, 2));
|
|
13773
13770
|
return;
|
|
13774
13771
|
}
|
|
13775
|
-
|
|
13776
|
-
console.log(`Added function '${functionName}' to agent '${agent}'${scopeLabel}`);
|
|
13772
|
+
console.log(`Added function '${functionName}' to agent '${agent}'`);
|
|
13777
13773
|
});
|
|
13778
|
-
parentCmd.command("remove-function <agent> <functionName>").description("Remove a function from an agent").option("--collection <slug-or-id>", "
|
|
13779
|
-
const ctx = getCtx();
|
|
13780
|
-
let endpoint = `${API_PATH}/${encodeURIComponent(agent)}/functions/${encodeURIComponent(functionName)}`;
|
|
13774
|
+
parentCmd.command("remove-function <agent> <functionName>").description("Remove a function from an agent").option("--collection <slug-or-id>", "REMOVED (#609) — collection access is managed separately; see error").action(async (agent, functionName, opts) => {
|
|
13781
13775
|
if (opts.collection) {
|
|
13782
|
-
|
|
13783
|
-
endpoint += `?collection_id=${encodeURIComponent(collId)}`;
|
|
13776
|
+
throw new CLIError("`--collection` was removed; collection access is managed separately: " + "`martha collections revoke <collection> --to agent:<id>`.", 4 /* Validation */);
|
|
13784
13777
|
}
|
|
13778
|
+
const ctx = getCtx();
|
|
13779
|
+
const endpoint = `${API_PATH}/${encodeURIComponent(agent)}/functions/${encodeURIComponent(functionName)}`;
|
|
13785
13780
|
await ctx.api.del(endpoint);
|
|
13786
13781
|
if (isJson()) {
|
|
13787
13782
|
console.log(JSON.stringify({
|
|
13788
13783
|
agent,
|
|
13789
13784
|
function_name: functionName,
|
|
13790
|
-
collection: opts.collection ?? null,
|
|
13791
13785
|
removed: true
|
|
13792
13786
|
}));
|
|
13793
13787
|
return;
|
|
13794
13788
|
}
|
|
13795
|
-
|
|
13796
|
-
console.log(`Removed function '${functionName}' from agent '${agent}'${scopeLabel}`);
|
|
13789
|
+
console.log(`Removed function '${functionName}' from agent '${agent}'`);
|
|
13797
13790
|
});
|
|
13798
13791
|
parentCmd.command("provision-auth <agent>").description("Provision, switch, or rotate agent auth credentials").requiredOption("--method <method>", "Auth method: service-account or api-key").action(async (agent, opts) => {
|
|
13799
13792
|
const ctx = getCtx();
|
|
@@ -13875,6 +13868,57 @@ Usage:
|
|
|
13875
13868
|
].join(" "));
|
|
13876
13869
|
}
|
|
13877
13870
|
});
|
|
13871
|
+
parentCmd.command("add-agent <coordinator> <subAgent>").description("Grant a coordinator agent the ability to delegate to a sub-agent").option("--set <items...>", "Config overrides (key=value)").action(async (coordinator, subAgent, opts) => {
|
|
13872
|
+
const ctx = getCtx();
|
|
13873
|
+
const body = {
|
|
13874
|
+
sub_agent_name: subAgent
|
|
13875
|
+
};
|
|
13876
|
+
if (opts.set) {
|
|
13877
|
+
body.config_overrides = parseSetFlags(opts.set);
|
|
13878
|
+
}
|
|
13879
|
+
const result = await ctx.api.post(`${API_PATH}/${encodeURIComponent(coordinator)}/agents`, body);
|
|
13880
|
+
if (isJson()) {
|
|
13881
|
+
console.log(JSON.stringify(result, null, 2));
|
|
13882
|
+
return;
|
|
13883
|
+
}
|
|
13884
|
+
console.log(`Agent '${coordinator}' may now delegate to '${subAgent}'`);
|
|
13885
|
+
});
|
|
13886
|
+
parentCmd.command("remove-agent <coordinator> <subAgent>").description("Revoke a coordinator's delegation grant to a sub-agent").action(async (coordinator, subAgent) => {
|
|
13887
|
+
const ctx = getCtx();
|
|
13888
|
+
const endpoint = `${API_PATH}/${encodeURIComponent(coordinator)}/agents/${encodeURIComponent(subAgent)}`;
|
|
13889
|
+
await ctx.api.del(endpoint);
|
|
13890
|
+
if (isJson()) {
|
|
13891
|
+
console.log(JSON.stringify({
|
|
13892
|
+
coordinator,
|
|
13893
|
+
sub_agent_name: subAgent,
|
|
13894
|
+
removed: true
|
|
13895
|
+
}));
|
|
13896
|
+
return;
|
|
13897
|
+
}
|
|
13898
|
+
console.log(`Revoked sub-agent '${subAgent}' from coordinator '${coordinator}'`);
|
|
13899
|
+
});
|
|
13900
|
+
parentCmd.command("agents <coordinator>").description("List the sub-agents a coordinator may delegate to").action(async (coordinator) => {
|
|
13901
|
+
const ctx = getCtx();
|
|
13902
|
+
const items = await ctx.api.get(`${API_PATH}/${encodeURIComponent(coordinator)}/agents`);
|
|
13903
|
+
if (isJson()) {
|
|
13904
|
+
console.log(JSON.stringify(items, null, 2));
|
|
13905
|
+
return;
|
|
13906
|
+
}
|
|
13907
|
+
if (items.length === 0) {
|
|
13908
|
+
console.log(source_default.dim("No sub-agents granted."));
|
|
13909
|
+
return;
|
|
13910
|
+
}
|
|
13911
|
+
const nameW = Math.max(8, ...items.map((s) => String(s.name ?? "").length));
|
|
13912
|
+
const header = ["SUB-AGENT", "DESCRIPTION"].map((h, i) => h.padEnd([nameW, 40][i])).join(" ");
|
|
13913
|
+
console.log(source_default.bold(header));
|
|
13914
|
+
console.log(source_default.dim("-".repeat(header.length)));
|
|
13915
|
+
for (const s of items) {
|
|
13916
|
+
console.log([
|
|
13917
|
+
String(s.name ?? "").padEnd(nameW),
|
|
13918
|
+
String(s.description ?? "-")
|
|
13919
|
+
].join(" "));
|
|
13920
|
+
}
|
|
13921
|
+
});
|
|
13878
13922
|
parentCmd.command("self-grant <agent>").description("View or configure an agent's self-provisioning policy (request-then-approve). " + "With no flags, prints the current policy.").option("--enable", "Allow the agent to REQUEST capability grants").option("--disable", "Disallow self-provisioning").option("--scope <scope>", "Scope ceiling: none | read_only | read_write").option("--allow <ref...>", "Add allow-list refs (function:NAME or mcp:integration/name)").option("--remove <ref...>", "Remove allow-list refs").option("--collection-root <id...>", "Add collection-subtree roots the agent may self-grant collection-scoped functions into").option("--remove-collection-root <id...>", "Remove collection roots").option("--max-pending <n>", "Max concurrently-pending requests").action(async (agent, opts) => {
|
|
13879
13923
|
const ctx = getCtx();
|
|
13880
13924
|
const path5 = `${API_PATH}/${encodeURIComponent(agent)}`;
|
|
@@ -17051,7 +17095,10 @@ ${clients.length} client(s)`));
|
|
|
17051
17095
|
}
|
|
17052
17096
|
console.log(`Deleted client '${nameOrId}'`);
|
|
17053
17097
|
});
|
|
17054
|
-
cmd.command("grant <clientNameOrId> <type> <defName>").description("Grant a client access to a function, workflow, or agent").option("--config <items...>", "Config overrides (key=value, functions/agents only)").option("--collection <slug-or-id>", "
|
|
17098
|
+
cmd.command("grant <clientNameOrId> <type> <defName>").description("Grant a client access to a function, workflow, or agent").option("--config <items...>", "Config overrides (key=value, functions/agents only)").option("--collection <slug-or-id>", "REMOVED (#609) — collection access is managed separately; see error").action(async (clientNameOrId, type, defName, opts) => {
|
|
17099
|
+
if (opts.collection) {
|
|
17100
|
+
throw new CLIError("`--collection` was removed; collection access is managed separately: " + "`martha collections grant <collection> --to client:<id>`.", 4 /* Validation */);
|
|
17101
|
+
}
|
|
17055
17102
|
if (!VALID_TYPES.has(type)) {
|
|
17056
17103
|
throw new CLIError(`Invalid type: '${type}'. Must be function, workflow, or agent.`, 4 /* Validation */);
|
|
17057
17104
|
}
|
|
@@ -17079,30 +17126,23 @@ ${clients.length} client(s)`));
|
|
|
17079
17126
|
}
|
|
17080
17127
|
body.config_overrides = parseSetFlags(opts.config);
|
|
17081
17128
|
}
|
|
17082
|
-
if (opts.collection) {
|
|
17083
|
-
if (accessType !== "function") {
|
|
17084
|
-
throw new CLIError("--collection only applies to function grants (#372 PR2).", 4 /* Validation */);
|
|
17085
|
-
}
|
|
17086
|
-
body.collection_id = await resolveCollectionIdForGrant(ctx, opts.collection);
|
|
17087
|
-
}
|
|
17088
17129
|
const result = await ctx.api.post(endpoint, body);
|
|
17089
17130
|
if (isJson()) {
|
|
17090
17131
|
console.log(JSON.stringify(result, null, 2));
|
|
17091
17132
|
return;
|
|
17092
17133
|
}
|
|
17093
|
-
|
|
17094
|
-
console.log(`Granted ${accessType} '${defName}' to client '${clientNameOrId}'${scopeLabel}`);
|
|
17134
|
+
console.log(`Granted ${accessType} '${defName}' to client '${clientNameOrId}'`);
|
|
17095
17135
|
});
|
|
17096
|
-
cmd.command("revoke <clientNameOrId> <type> <defName>").description("Revoke a client's access to a function, workflow, or agent").option("--collection <slug-or-id>", "
|
|
17136
|
+
cmd.command("revoke <clientNameOrId> <type> <defName>").description("Revoke a client's access to a function, workflow, or agent").option("--collection <slug-or-id>", "REMOVED (#609) — collection access is managed separately; see error").action(async (clientNameOrId, type, defName, opts) => {
|
|
17137
|
+
if (opts.collection) {
|
|
17138
|
+
throw new CLIError("`--collection` was removed; collection access is managed separately: " + "`martha collections revoke <collection> --to client:<id>`.", 4 /* Validation */);
|
|
17139
|
+
}
|
|
17097
17140
|
if (!VALID_TYPES.has(type)) {
|
|
17098
17141
|
throw new CLIError(`Invalid type: '${type}'. Must be function, workflow, or agent.`, 4 /* Validation */);
|
|
17099
17142
|
}
|
|
17100
17143
|
const accessType = type;
|
|
17101
17144
|
const ctx = getCtx();
|
|
17102
17145
|
const clientId = await resolveClientId(ctx, clientNameOrId);
|
|
17103
|
-
if (opts.collection && accessType !== "function") {
|
|
17104
|
-
throw new CLIError("--collection only applies to function grants (#372 PR2).", 4 /* Validation */);
|
|
17105
|
-
}
|
|
17106
17146
|
let endpoint;
|
|
17107
17147
|
if (accessType === "agent") {
|
|
17108
17148
|
endpoint = `${DEFS_API}/clients/${clientId}/agents/${encodeURIComponent(defName)}`;
|
|
@@ -17110,10 +17150,6 @@ ${clients.length} client(s)`));
|
|
|
17110
17150
|
const defId = await resolveDefinitionId(ctx, accessType, defName);
|
|
17111
17151
|
const typePlural = `${accessType}s`;
|
|
17112
17152
|
endpoint = `${DEFS_API}/clients/${clientId}/${typePlural}/${encodeURIComponent(defId)}`;
|
|
17113
|
-
if (opts.collection) {
|
|
17114
|
-
const collId = await resolveCollectionIdForGrant(ctx, opts.collection);
|
|
17115
|
-
endpoint += `?collection_id=${encodeURIComponent(collId)}`;
|
|
17116
|
-
}
|
|
17117
17153
|
}
|
|
17118
17154
|
await ctx.api.del(endpoint);
|
|
17119
17155
|
if (isJson()) {
|
|
@@ -17121,13 +17157,11 @@ ${clients.length} client(s)`));
|
|
|
17121
17157
|
client: clientNameOrId,
|
|
17122
17158
|
type: accessType,
|
|
17123
17159
|
name: defName,
|
|
17124
|
-
collection: opts.collection ?? null,
|
|
17125
17160
|
revoked: true
|
|
17126
17161
|
}));
|
|
17127
17162
|
return;
|
|
17128
17163
|
}
|
|
17129
|
-
|
|
17130
|
-
console.log(`Revoked ${accessType} '${defName}' from client '${clientNameOrId}'${scopeLabel}`);
|
|
17164
|
+
console.log(`Revoked ${accessType} '${defName}' from client '${clientNameOrId}'`);
|
|
17131
17165
|
});
|
|
17132
17166
|
const embed = cmd.command("embed").description("Create, configure, and test embeddable chat clients");
|
|
17133
17167
|
function addEmbedConfigOptions(command) {
|
|
@@ -18396,6 +18430,25 @@ function registerPolicyCommands(program2) {
|
|
|
18396
18430
|
// src/commands/authz.ts
|
|
18397
18431
|
init_errors();
|
|
18398
18432
|
|
|
18433
|
+
// src/lib/collections.ts
|
|
18434
|
+
init_errors();
|
|
18435
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18436
|
+
async function resolveCollectionIdForGrant(ctx, ref) {
|
|
18437
|
+
if (UUID_RE.test(ref)) {
|
|
18438
|
+
return ref;
|
|
18439
|
+
}
|
|
18440
|
+
const all = await ctx.api.get("/api/admin/collections", {
|
|
18441
|
+
params: { active_only: "false" }
|
|
18442
|
+
});
|
|
18443
|
+
const bySlug = all.find((c) => c.slug === ref);
|
|
18444
|
+
if (bySlug)
|
|
18445
|
+
return bySlug.id;
|
|
18446
|
+
const byName = all.find((c) => c.name === ref);
|
|
18447
|
+
if (byName)
|
|
18448
|
+
return byName.id;
|
|
18449
|
+
throw new CLIError(`Collection '${ref}' not found (tried UUID, slug, and name).`, 3 /* NotFound */, "Run `martha documents collections --tree` to see available collections.");
|
|
18450
|
+
}
|
|
18451
|
+
|
|
18399
18452
|
// src/lib/authz.ts
|
|
18400
18453
|
init_errors();
|
|
18401
18454
|
var EX_UNAVAILABLE = 69;
|
package/package.json
CHANGED
|
@@ -279,6 +279,16 @@ martha agents functions <agent> # List grant
|
|
|
279
279
|
|
|
280
280
|
`--collection` scopes the grant to one collection per the #372 PR2 cascade. Omit it to grant at the root (tenant-wide); pass it on `remove-function` to revoke just that scope. Server returns 400 if the function is collection-agnostic (e.g. `recall`).
|
|
281
281
|
|
|
282
|
+
**Sub-agent grants (agent→agent delegation):**
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
martha agents add-agent <coordinator> <sub-agent> [--set key=value] # Let <coordinator> delegate to <sub-agent>
|
|
286
|
+
martha agents remove-agent <coordinator> <sub-agent> # Revoke the delegation grant
|
|
287
|
+
martha agents agents <coordinator> # List the sub-agents <coordinator> may delegate to
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
A grant means the coordinator agent *may* delegate to the sub-agent at invoke time (the sub-agent loads as an `_is_agent` delegate tool); the sub-agent's own tool calls still pass the policy gate under its own grants. `--set key=value` records `config_overrides` for the delegation. Self-delegation is rejected with a clear 400 (`an agent cannot delegate to itself`), and granting an unknown sub-agent returns 404. All three commands support `--json`.
|
|
291
|
+
|
|
282
292
|
---
|
|
283
293
|
|
|
284
294
|
## Tasks: queue + executor lifecycle
|