external-subagent 0.2.0 → 0.3.1
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/README.md +16 -9
- package/cli/commands/agents.mjs +11 -5
- package/cli/config/schema.mjs +2 -1
- package/cli/constants.mjs +1 -1
- package/cli/install/service-macos.mjs +6 -1
- package/cli/rpc.mjs +17 -3
- package/npm/native/darwin-arm64/external-subagent-mcp +0 -0
- package/npm/native/darwin-arm64/external-subagentd +0 -0
- package/npm/native/darwin-arm64/payload.json +5 -5
- package/package.json +3 -2
- package/plugins/codex/external-subagent/skills/external-subagent/SKILL.md +19 -10
- package/schema/external-subagent-public-api.json +17 -9
- package/schema/public-reasoning-source.json +6 -0
package/README.md
CHANGED
|
@@ -8,10 +8,10 @@ independent axes:
|
|
|
8
8
|
- **Hosts** — Codex (`install-plugin codex` or `install-mcp`), ZCode
|
|
9
9
|
(`install-plugin zcode`), and any custom local MCP client that connects
|
|
10
10
|
directly to the facade without registration.
|
|
11
|
-
- **Subagents** — ZCode, DeepSeek Harness (DSH),
|
|
12
|
-
through an internal adapter and subject to
|
|
13
|
-
listed in [docs/product.md](docs/product.md)
|
|
14
|
-
[docs/mcp-api.md](docs/mcp-api.md).
|
|
11
|
+
- **Subagents** — ZCode, DeepSeek Harness (DSH), Codex, and Google
|
|
12
|
+
Antigravity (`agy`), each routed through an internal adapter and subject to
|
|
13
|
+
the per-subagent limitations listed in [docs/product.md](docs/product.md)
|
|
14
|
+
and [docs/mcp-api.md](docs/mcp-api.md).
|
|
15
15
|
|
|
16
16
|
## Status
|
|
17
17
|
|
|
@@ -26,9 +26,9 @@ artifact and explicitly initializing to a launchd-resident service
|
|
|
26
26
|
active-task upgrade and standalone initialization are implemented and
|
|
27
27
|
reviewed.
|
|
28
28
|
|
|
29
|
-
All
|
|
30
|
-
`external-subagent agents enable <zcode|dsh|codex>`: a successful local probe
|
|
31
|
-
writes the configuration (no write on failure), and `dsh`/`codex` need a
|
|
29
|
+
All four subagents are **disabled by default** and are enabled per name with
|
|
30
|
+
`external-subagent agents enable <zcode|dsh|codex|agy>`: a successful local probe
|
|
31
|
+
writes the configuration (no write on failure), and `dsh`/`codex`/`agy` need a
|
|
32
32
|
daemon restart to take effect. ZCode admits all four permission modes
|
|
33
33
|
(build/edit/plan/yolo) and rejects an explicit spawn `model`; DSH admits
|
|
34
34
|
`build` and strict `plan`, with model selection as explicit spawn model,
|
|
@@ -40,9 +40,16 @@ legacy build composition and `plan` still requires an empty manifest); Codex
|
|
|
40
40
|
admits all four modes
|
|
41
41
|
(build/edit → workspace-write, plan → read-only, yolo → danger-full-access,
|
|
42
42
|
all pinning `approvalPolicy=never`) and rejects a non-empty `write_manifest`
|
|
43
|
-
with `codex_write_manifest_unsupported
|
|
43
|
+
with `codex_write_manifest_unsupported`; Antigravity (`agy`) spawns only when
|
|
44
|
+
its `enabled + spawn_supported` gate and an absolute `AGY_RUNTIME_PATH`
|
|
45
|
+
executable admit it, runs with `build` (`--mode accept-edits`) or `yolo`
|
|
46
|
+
(`--dangerously-skip-permissions`), selects a catalog-validated bare model
|
|
47
|
+
slug, admits effort from `low|medium|high|max`, and rejects a non-empty
|
|
48
|
+
`write_manifest` with `agy_write_manifest_unsupported` (it has no
|
|
49
|
+
permission-respond interaction — tools are soft-denied and denied actions
|
|
50
|
+
surface on failure diagnostics). `observe` works on all four: ZCode
|
|
44
51
|
and DSH expose the public reasoning tail (at most 200 characters) and Codex
|
|
45
|
-
|
|
52
|
+
and agy report `reasoning: null`.
|
|
46
53
|
|
|
47
54
|
The daemon socket environment variable is now the neutral
|
|
48
55
|
`EXTERNAL_SUBAGENT_SOCKET`, with no fallback to the retired name. An existing
|
package/cli/commands/agents.mjs
CHANGED
|
@@ -65,7 +65,7 @@ export function parseSubagentsArgs(args) {
|
|
|
65
65
|
const [operation, ...rest] = args;
|
|
66
66
|
if (!['list', 'status', 'probe', 'models', 'enable'].includes(operation)) throw new CliError('INVALID_ARGUMENT', `unsupported subagents operation: ${operation}`, 2);
|
|
67
67
|
if (operation === 'enable') {
|
|
68
|
-
if (rest.length !== 1 || !SUBAGENT_IDS.includes(rest[0])) throw new CliError('INVALID_ARGUMENT', 'usage: agents enable <zcode|dsh|codex>', 2);
|
|
68
|
+
if (rest.length !== 1 || !SUBAGENT_IDS.includes(rest[0])) throw new CliError('INVALID_ARGUMENT', 'usage: agents enable <zcode|dsh|codex|agy>', 2);
|
|
69
69
|
return { operation, subagent: rest[0] };
|
|
70
70
|
}
|
|
71
71
|
if (operation === 'probe') return parseProbeArgs(rest);
|
|
@@ -82,7 +82,7 @@ export async function subagentsCommand(paths, input = {}, options = {}) {
|
|
|
82
82
|
if (operation === 'enable') {
|
|
83
83
|
const agent = input.subagent;
|
|
84
84
|
if (!SUBAGENT_IDS.includes(agent) || ['through', 'workspace', 'home'].some((key) => input[key] !== undefined)) {
|
|
85
|
-
throw new CliError('INVALID_ARGUMENT', 'usage: agents enable <zcode|dsh|codex>', 2);
|
|
85
|
+
throw new CliError('INVALID_ARGUMENT', 'usage: agents enable <zcode|dsh|codex|agy>', 2);
|
|
86
86
|
}
|
|
87
87
|
if (typeof options.callDaemon !== 'function' || typeof options.socket !== 'string') throw new CliError('INTERNAL_ERROR', 'agents enable requires a daemon connection');
|
|
88
88
|
const entry = config.subagents[agent];
|
|
@@ -102,9 +102,15 @@ export async function subagentsCommand(paths, input = {}, options = {}) {
|
|
|
102
102
|
if (typeof local.runtime_path !== 'string' || !path.isAbsolute(local.runtime_path) || typeof local.version !== 'string' || !local.version.trim()) {
|
|
103
103
|
throw new CliError('agent_probe_failed', 'Probe did not return an absolute runtime_path and observed version', 2);
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
if (agent === 'agy') {
|
|
106
|
+
// agy has no home override: the daemon resolves the executable from
|
|
107
|
+
// AGY_RUNTIME_PATH, so the patch carries runtime_path + version only.
|
|
108
|
+
Object.assign(patch, { runtime_path: local.runtime_path, version: local.version });
|
|
109
|
+
} else {
|
|
110
|
+
const home = local.scope?.home;
|
|
111
|
+
if (typeof home !== 'string' || !path.isAbsolute(home)) throw new CliError('agent_probe_failed', `Cannot enable ${agent}: configure subagents.${agent}.home or export ${agent.toUpperCase()}_HOME before starting the daemon`, 2);
|
|
112
|
+
Object.assign(patch, { runtime_path: local.runtime_path, home, profile: agent === 'dsh' ? 'acp' : (entry.profile ?? null), version: local.version });
|
|
113
|
+
}
|
|
108
114
|
}
|
|
109
115
|
const updated = updateConfig(paths.config, (latest) => {
|
|
110
116
|
if (Object.entries(patch).every(([key, value]) => latest.subagents[agent][key] === value)) return latest;
|
package/cli/config/schema.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CliError } from '../errors.mjs';
|
|
2
2
|
|
|
3
3
|
export const CONFIG_SCHEMA_VERSION = 2;
|
|
4
|
-
export const SUBAGENT_IDS = Object.freeze(['zcode', 'dsh', 'codex']);
|
|
4
|
+
export const SUBAGENT_IDS = Object.freeze(['zcode', 'dsh', 'codex', 'agy']);
|
|
5
5
|
const CONFIG_FIELDS = new Set(['schema_version', 'revision', 'default_subagent', 'subagents']);
|
|
6
6
|
const AGENT_FIELDS = new Set(['enabled', 'spawn_supported', 'default_model', 'runtime_path', 'home', 'profile', 'version']);
|
|
7
7
|
|
|
@@ -24,6 +24,7 @@ export function defaultConfig() {
|
|
|
24
24
|
zcode: { enabled: false, spawn_supported: false, default_model: null },
|
|
25
25
|
dsh: { enabled: false, spawn_supported: false, default_model: null, runtime_path: null, home: null, profile: null, version: null },
|
|
26
26
|
codex: { enabled: false, spawn_supported: false, default_model: null, runtime_path: null, home: null, profile: null, version: null },
|
|
27
|
+
agy: { enabled: false, spawn_supported: false, default_model: null, runtime_path: null, home: null, profile: null, version: null },
|
|
27
28
|
},
|
|
28
29
|
};
|
|
29
30
|
}
|
package/cli/constants.mjs
CHANGED
|
@@ -8,7 +8,7 @@ const IS_DEBUG_VARIANT = process.env.EXTERNAL_SUBAGENT_VARIANT === 'debug';
|
|
|
8
8
|
|
|
9
9
|
export const PRODUCT_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debug' : 'external-subagent';
|
|
10
10
|
export const PRODUCT_ID = IS_DEBUG_VARIANT ? 'external_subagent_debug' : 'external_subagent';
|
|
11
|
-
export const VERSION = '0.
|
|
11
|
+
export const VERSION = '0.3.1';
|
|
12
12
|
export const LAUNCH_AGENT_LABEL = IS_DEBUG_VARIANT ? 'com.external-subagent-debug.daemon' : 'com.external-subagent.daemon';
|
|
13
13
|
export const DAEMON_BIN_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debugd' : 'external-subagentd';
|
|
14
14
|
export const MCP_BIN_NAME = IS_DEBUG_VARIANT ? 'external-subagent-debug-mcp' : 'external-subagent-mcp';
|
|
@@ -21,7 +21,7 @@ function escapeXml(value) {
|
|
|
21
21
|
// Presence is diagnostic only; enabling is an explicit probe + config operation.
|
|
22
22
|
export function runtimeObservations(paths, options = {}) {
|
|
23
23
|
const config = readConfig(paths.config);
|
|
24
|
-
return Object.fromEntries(['zcode', 'dsh', 'codex'].map((agent) => {
|
|
24
|
+
return Object.fromEntries(['zcode', 'dsh', 'codex', 'agy'].map((agent) => {
|
|
25
25
|
const runtime = agent === 'zcode' ? (options.zcodeRuntime ?? ZCODE_RUNTIME) : config.subagents[agent].runtime_path;
|
|
26
26
|
return [agent, { path: runtime, present: Boolean(runtime && fs.existsSync(runtime)), enabled: config.subagents[agent].enabled }];
|
|
27
27
|
}));
|
|
@@ -35,6 +35,7 @@ export function launchAgentPlist(paths, options = {}) {
|
|
|
35
35
|
const config = readConfig(paths.config);
|
|
36
36
|
const { runtime_path: dshRuntime, home: dshHome, profile: dshProfile, version: dshVersion } = config.subagents.dsh;
|
|
37
37
|
const { runtime_path: codexRuntime, home: codexHome } = config.subagents.codex;
|
|
38
|
+
const { runtime_path: agyRuntime } = config.subagents.agy;
|
|
38
39
|
const configRevision = config.revision;
|
|
39
40
|
// AUD-005/D1: the standalone service never depends on an unrelated runtime.
|
|
40
41
|
// The pinned ZCode runtime is forwarded only when that installation exists;
|
|
@@ -70,6 +71,10 @@ export function launchAgentPlist(paths, options = {}) {
|
|
|
70
71
|
// fails Codex admission closed instead of falling back to ~/.codex.
|
|
71
72
|
...(codexRuntime ? [`<key>CODEX_RUNTIME_PATH</key><string>${escapeXml(codexRuntime)}</string>`] : []),
|
|
72
73
|
...(codexHome ? [`<key>CODEX_HOME</key><string>${escapeXml(codexHome)}</string>`] : []),
|
|
74
|
+
// agy has no home override: only its persisted executable is forwarded, so
|
|
75
|
+
// the service-side daemon resolves the same AGY_RUNTIME_PATH the
|
|
76
|
+
// interactive one does.
|
|
77
|
+
...(agyRuntime ? [`<key>AGY_RUNTIME_PATH</key><string>${escapeXml(agyRuntime)}</string>`] : []),
|
|
73
78
|
].join('');
|
|
74
79
|
return Buffer.from(`<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n<key>Label</key><string>${LAUNCH_AGENT_LABEL}</string>\n<key>ProgramArguments</key><array>${programArguments.join('')}</array>\n<key>EnvironmentVariables</key><dict>${dshEnvironment}</dict>\n<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>\n<key>StandardOutPath</key><string>${escapeXml(path.join(paths.logs, 'daemon.log'))}</string>\n<key>StandardErrorPath</key><string>${escapeXml(path.join(paths.logs, 'daemon-error.log'))}</string>\n</dict></plist>\n`);
|
|
75
80
|
}
|
package/cli/rpc.mjs
CHANGED
|
@@ -7,6 +7,7 @@ export const MAX_FRAME_BYTES = 512 * 1024;
|
|
|
7
7
|
export const MAX_RESPONSE_FRAME_BYTES = 2 * 1024 * 1024;
|
|
8
8
|
export const MAX_RESULT_CHUNK_BYTES = 256 * 1024;
|
|
9
9
|
export const AGENT_PROBE_TRANSPORT_TIMEOUT_MS = 200_000;
|
|
10
|
+
export const SPAWN_TRANSPORT_TIMEOUT_MS = 150_000;
|
|
10
11
|
export const MIN_TASK_ID = 10_000_000;
|
|
11
12
|
export const MAX_TASK_ID = 99_999_999;
|
|
12
13
|
|
|
@@ -38,6 +39,7 @@ export function waitTransportTimeoutMs(waitTime = 290) {
|
|
|
38
39
|
export function daemonTransportTimeoutMs(command, params = {}) {
|
|
39
40
|
if (command === 'wait') return waitTransportTimeoutMs(params.wait_time);
|
|
40
41
|
if (command === 'agent-probe' || command === 'agent-models') return AGENT_PROBE_TRANSPORT_TIMEOUT_MS;
|
|
42
|
+
if (command === 'create' || command === 'spawn') return SPAWN_TRANSPORT_TIMEOUT_MS;
|
|
41
43
|
return 6000;
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -136,8 +138,10 @@ function methodFor(command, input) {
|
|
|
136
138
|
} };
|
|
137
139
|
}
|
|
138
140
|
case 'send': {
|
|
139
|
-
rejectUnknownFields('send', input, new Set(['agent_id', 'message_id', 'content']));
|
|
141
|
+
rejectUnknownFields('send', input, new Set(['agent_id', 'message_id', 'mode', 'content']));
|
|
142
|
+
if (!['queue', 'steer'].includes(input.mode)) throw new CliError('VALIDATION', 'send mode must be queue or steer', 2);
|
|
140
143
|
return { method: 'task_message', params: {
|
|
144
|
+
mode: input.mode,
|
|
141
145
|
agent_id: daemonTaskId(input.agent_id),
|
|
142
146
|
...(input.message_id ? { message_id: input.message_id } : {}),
|
|
143
147
|
content: input.content,
|
|
@@ -174,6 +178,16 @@ function publicTask(task) {
|
|
|
174
178
|
};
|
|
175
179
|
}
|
|
176
180
|
|
|
181
|
+
// The wait response carries only the task lifecycle header: admission
|
|
182
|
+
// provenance never changes after spawn, so wait does not repeat it.
|
|
183
|
+
function publicTaskHeader(task) {
|
|
184
|
+
return {
|
|
185
|
+
agent_id: publicTaskId(task.agent_id),
|
|
186
|
+
status: task.status,
|
|
187
|
+
session_id: task.session_id ?? null,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
177
191
|
function publicResult(result) {
|
|
178
192
|
if (result == null) return null;
|
|
179
193
|
return {
|
|
@@ -195,10 +209,10 @@ export function projectDaemonResult(command, result) {
|
|
|
195
209
|
case 'agent-probe': return { evidence: result.evidence, status: result.status };
|
|
196
210
|
case 'agent-models': return result.catalog ?? result;
|
|
197
211
|
case 'create': case 'spawn':
|
|
198
|
-
return { agent_id: publicTaskId(result.task.agent_id), status: result.task.status };
|
|
212
|
+
return { agent_id: publicTaskId(result.task.agent_id), status: result.task.status, session_id: result.task.session_id ?? null };
|
|
199
213
|
case 'wait': {
|
|
200
214
|
const { result: taskResult, task, activity, kind: _kind, ...rest } = result;
|
|
201
|
-
return { ...rest, task:
|
|
215
|
+
return { ...rest, task: publicTaskHeader(task), activity, result: publicResult(taskResult) };
|
|
202
216
|
}
|
|
203
217
|
case 'list': return { tasks: result.tasks.map(publicTask), next_cursor: result.next_cursor ?? null };
|
|
204
218
|
case 'send': return { message_id: result.message_id, disposition: result.disposition };
|
|
Binary file
|
|
Binary file
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"product": "external-subagent",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"platform": "darwin-arm64",
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"name": "external-subagentd",
|
|
9
|
-
"bytes":
|
|
9
|
+
"bytes": 9911616,
|
|
10
10
|
"mode": "755",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "d3f6d45a32d7675ba68fa059e70cca4c85f1ed781441b53dddfa7585751ea339"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "external-subagent-mcp",
|
|
15
|
-
"bytes":
|
|
15
|
+
"bytes": 935760,
|
|
16
16
|
"mode": "755",
|
|
17
|
-
"sha256": "
|
|
17
|
+
"sha256": "504256978a7cf0cc16b13c8f53d75bca3c53bf65d1286a3c9bc22dc923cd0476"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "external-subagent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Manage bounded ZCode, DeepSeek Harness (DSH), and
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Manage bounded ZCode, DeepSeek Harness (DSH), Codex, and Google Antigravity (agy) subagent tasks through a local MCP service for any local host (Codex, ZCode, or a custom MCP client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"postpack": "node scripts/release/check-native-tarball.mjs",
|
|
53
53
|
"prepublishOnly": "node scripts/release/check-native-tarball.mjs --staged",
|
|
54
54
|
"test:install": "node --test tests/install/*.test.mjs",
|
|
55
|
+
"test:affected": "node scripts/test/affected.mjs",
|
|
55
56
|
"check": "node --check bin/external-subagent.mjs && node --check bin/external-subagent-mcp.mjs && node --check bin/external-subagent-debug.mjs && node --check bin/external-subagent-debug-mcp.mjs && node --check cli/main.mjs"
|
|
56
57
|
},
|
|
57
58
|
"engines": {
|
|
@@ -7,7 +7,7 @@ description: Manage bounded repository-scoped tasks through external-subagent.
|
|
|
7
7
|
|
|
8
8
|
Use the `external_subagent_*` tools to inspect status, spawn a task with an explicit
|
|
9
9
|
`subagent`, wait for progress, retrieve the complete result, and close the task.
|
|
10
|
-
Execution targets are `zcode`, `dsh`, and `
|
|
10
|
+
Execution targets are `zcode`, `dsh`, `codex`, and `agy`; each name currently has one
|
|
11
11
|
runtime instance and no instance selector is supported. Omitting `subagent` uses
|
|
12
12
|
`default_subagent` or returns `subagent_required`; unknown targets return
|
|
13
13
|
`subagent_unknown`, and unknown spawn fields are rejected before dispatch.
|
|
@@ -22,10 +22,11 @@ evidence are operator diagnostics — read them with the CLI `diagnose` command
|
|
|
22
22
|
instead of the MCP status tool.
|
|
23
23
|
|
|
24
24
|
Spawn accepts an optional `effort` token that steers the task's reasoning
|
|
25
|
-
effort. Omit it to keep each agent's current default (codex
|
|
26
|
-
wire default; zcode and dsh send no effort field or call at all).
|
|
27
|
-
admits only the closed set
|
|
25
|
+
effort. Omit it to keep each agent's current default (codex and agy keep their
|
|
26
|
+
existing wire default; zcode and dsh send no effort field or call at all).
|
|
27
|
+
`codex` admits only the closed set
|
|
28
28
|
`low | medium | high | xhigh | max` — `minimal` and `ultra` are rejected;
|
|
29
|
+
`agy` admits only the measured closed set `low | medium | high | max`;
|
|
29
30
|
`zcode` and `dsh` accept any bounded token (1..24 bytes of `[a-z0-9_]`) and
|
|
30
31
|
pass it through to the runtime, because the supported set is only known at
|
|
31
32
|
runtime. A malformed or unsupported token is rejected before dispatch, so no
|
|
@@ -40,9 +41,9 @@ Codex home instances, and offers plugin or direct MCP bindings; built-in
|
|
|
40
41
|
dirs with a single per-user instance. This is separate
|
|
41
42
|
from `subagent.codex`. Protocol implementations are adapters.
|
|
42
43
|
|
|
43
|
-
All
|
|
44
|
+
All four subagents (`zcode`, `dsh`, `codex`, `agy`) are disabled by default; enable one
|
|
44
45
|
with `external-subagent agents enable <name>`, which runs a local probe and writes
|
|
45
|
-
the configuration only on success (a daemon restart is required for `dsh`/`codex`).
|
|
46
|
+
the configuration only on success (a daemon restart is required for `dsh`/`codex`/`agy`).
|
|
46
47
|
Actual per-subagent limitations (from the accepted code):
|
|
47
48
|
|
|
48
49
|
- `zcode` — disabled until explicitly enabled; all four permission modes
|
|
@@ -56,7 +57,15 @@ Actual per-subagent limitations (from the accepted code):
|
|
|
56
57
|
and `yolo` to sandbox=danger-full-access, all pinning approvalPolicy=never.
|
|
57
58
|
A non-empty spawn `write_manifest` is rejected before task creation with
|
|
58
59
|
`codex_write_manifest_unsupported`.
|
|
59
|
-
- `
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
- `agy` (Google Antigravity) — disabled until explicitly enabled with an
|
|
61
|
+
absolute executable `AGY_RUNTIME_PATH`; only `build` (`--mode accept-edits`)
|
|
62
|
+
and `yolo` (`--dangerously-skip-permissions`) are admitted (plan/edit are
|
|
63
|
+
rejected with `agy_permission_mode_unsupported`); `model` is a bare slug
|
|
64
|
+
validated against the `agy models` catalog; a non-empty spawn
|
|
65
|
+
`write_manifest` is rejected before task creation with
|
|
66
|
+
`agy_write_manifest_unsupported`; there is no permission-respond interaction
|
|
67
|
+
(tools are soft-denied).
|
|
68
|
+
- `observe` — available for all four subagents; `zcode` and `dsh` return the
|
|
69
|
+
public reasoning tail (at most 200 characters), while `codex` and `agy` do not
|
|
70
|
+
collect reasoning and return `reasoning: null`. Tool history and coverage are
|
|
71
|
+
reported truthfully per adapter capability.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"type": "object",
|
|
16
16
|
"required": ["subagent", "configured", "enabled", "spawn_supported", "permission_modes", "model_selection", "effort_selection", "local", "auth", "hi"],
|
|
17
17
|
"properties": {
|
|
18
|
-
"subagent": { "enum": ["zcode", "dsh", "codex"] },
|
|
18
|
+
"subagent": { "enum": ["zcode", "dsh", "codex", "agy"] },
|
|
19
19
|
"configured": { "type": "boolean" },
|
|
20
20
|
"enabled": { "type": "boolean" },
|
|
21
21
|
"spawn_supported": { "type": "boolean" },
|
|
@@ -60,21 +60,29 @@
|
|
|
60
60
|
},
|
|
61
61
|
"spawn": {
|
|
62
62
|
"type": "object",
|
|
63
|
+
"description": "Start one durable subagent in an absolute repository workspace, returning after establishing the new session.",
|
|
63
64
|
"required": ["repository", "prompt"],
|
|
64
65
|
"properties": {
|
|
65
66
|
"subagent": { "type": "string", "minLength": 1, "description": "Subagent route key; omission uses configured default_subagent or returns subagent_required. Null is invalid; dsh production spawn requires its enabled + spawn_supported + pinned-runtime configuration." },
|
|
66
67
|
"repository": { "type": "string", "minLength": 1 },
|
|
67
|
-
"permission_mode": { "$ref": "#/properties/permission_mode", "description": "codex supports build/edit (workspace-write), plan (read-only), and yolo (danger-full-access), always with approvalPolicy=never." },
|
|
68
|
+
"permission_mode": { "$ref": "#/properties/permission_mode", "description": "codex supports build/edit (workspace-write), plan (read-only), and yolo (danger-full-access), always with approvalPolicy=never; agy supports only build (--mode accept-edits) and yolo (--dangerously-skip-permissions)." },
|
|
68
69
|
"prompt": { "type": "string", "minLength": 1 },
|
|
69
|
-
"model": { "type": "string", "minLength": 1, "description": "Task model. dsh requires provider:model, split at the first colon (the model side may contain further colons; a missing colon or an empty side is rejected before any task). codex uses its own model id; zcode uses provider/model (a bare token keeps its backward-compatible zai/<token> reading). Omission falls back to the configured default_model for dsh and
|
|
70
|
-
"effort": { "type": "string", "minLength": 1, "maxLength": 24, "pattern": "^[a-z0-9_]+$", "description": "Reasoning-effort token: 1..24 bytes of [a-z0-9_]. codex admits only low, medium, high or xhigh; zcode and dsh pass the bounded token through. Omission keeps the runtime default." },
|
|
71
|
-
"write_manifest": { "type": "array", "items": { "type": "string", "minLength": 1 }, "description": "codex requires an empty or omitted write_manifest; non-empty input returns codex_write_manifest_unsupported before task creation. dsh admits a non-empty manifest in build through the guarded manifest-build composition (workspace-write sandbox, only tool-fs writable, out-of-manifest paths rejected as FS_WRITE_MANIFEST_DENIED), at most 256 entries; an explicit [\".\"] keeps the existing build composition. plan requires an empty manifest for every subagent." }
|
|
70
|
+
"model": { "type": "string", "minLength": 1, "description": "Task model. dsh requires provider:model, split at the first colon (the model side may contain further colons; a missing colon or an empty side is rejected before any task). codex uses its own model id; zcode uses provider/model (a bare token keeps its backward-compatible zai/<token> reading). agy uses a bare model slug with no ':' or '/' separator or whitespace; admission validates only that shape and the daemon does not verify catalog membership, so an unknown slug fails loudly at session start when the agy CLI rejects it. Omission falls back to the configured default_model for dsh, zcode and agy, then to the provider-native model; codex requires an explicit model or agents.codex.default_model and never falls back to a provider default." },
|
|
71
|
+
"effort": { "type": "string", "minLength": 1, "maxLength": 24, "pattern": "^[a-z0-9_]+$", "description": "Reasoning-effort token: 1..24 bytes of [a-z0-9_]. codex admits only low, medium, high or xhigh; agy admits only low, medium, high or max; zcode and dsh pass the bounded token through. Omission keeps the runtime default." },
|
|
72
|
+
"write_manifest": { "type": "array", "items": { "type": "string", "minLength": 1 }, "description": "codex requires an empty or omitted write_manifest; non-empty input returns codex_write_manifest_unsupported before task creation. agy likewise requires an empty or omitted write_manifest; non-empty input returns agy_write_manifest_unsupported before task creation. dsh admits a non-empty manifest in build through the guarded manifest-build composition (workspace-write sandbox, only tool-fs writable, out-of-manifest paths rejected as FS_WRITE_MANIFEST_DENIED), at most 256 entries; an explicit [\".\"] keeps the existing build composition. plan requires an empty manifest for every subagent." }
|
|
72
73
|
},
|
|
73
74
|
"allOf": [
|
|
74
75
|
{ "if": { "required": ["subagent"], "properties": { "subagent": { "const": "codex" } } },
|
|
75
76
|
"then": { "properties": { "write_manifest": { "maxItems": 0 } } } },
|
|
76
77
|
{ "if": { "required": ["subagent"], "properties": { "subagent": { "const": "dsh" } } },
|
|
77
78
|
"then": { "properties": { "write_manifest": { "maxItems": 256 } } } },
|
|
79
|
+
{ "if": { "required": ["subagent"], "properties": { "subagent": { "const": "agy" } } },
|
|
80
|
+
"then": { "properties": {
|
|
81
|
+
"permission_mode": { "enum": ["build", "yolo"] },
|
|
82
|
+
"model": { "type": "string", "minLength": 1, "maxLength": 512, "pattern": "^[^:/\\s]+$" },
|
|
83
|
+
"effort": { "enum": ["low", "medium", "high", "max"] },
|
|
84
|
+
"write_manifest": { "maxItems": 0 }
|
|
85
|
+
} } },
|
|
78
86
|
{ "if": { "required": ["permission_mode"], "properties": { "permission_mode": { "const": "plan" } } },
|
|
79
87
|
"then": { "properties": { "write_manifest": { "maxItems": 0 } } } }
|
|
80
88
|
],
|
|
@@ -95,7 +103,7 @@
|
|
|
95
103
|
},
|
|
96
104
|
"wait": {
|
|
97
105
|
"type": "object",
|
|
98
|
-
"description": "Wait
|
|
106
|
+
"description": "Wait up to 290 seconds by default. Returns early only when an actionable pending request exists (permission requests need allow/deny; user-input requests carry the full embedded question and need answer with non-empty content) or the terminal result is available; ordinary progress, message receipts, already-responded and unsupported requests never wake it, and its timeout still returns timed_out=true. When the final result is embedded with complete=true no further result call is needed; a partial page directs you to external_subagent_result with next_offset. A failed terminal result also carries failure_message: usually the daemon's bounded single-line JSON failure detail, but an early fail_claim row may be plain text, so treat it as an opaque bounded string and attempt JSON parsing defensively (null when the task has no persisted detail or the outcome is not a failure). Read failure_message before falling back to daemon logs or sqlite. The pending_requests projection stays capped at 100 records while the wake decision scans the full pending set, and the request that woke the wait is always part of the returned projection so its request_id can be answered directly. activity carries the text tail incrementally: each wait returns only the text appended since the previous wait (an empty tail means nothing new), every byte is delivered by wait at most once, and latest_text_truncated flags the case where undelivered bytes rolled out of the bounded window so this response degraded to the whole window (on the terminal response, bytes repeating the embedded result page verbatim are stripped so they are not shipped twice), a 200-char verified reasoning tail, tool calls started in the last 60 seconds, and telemetry status; activity also exposes last_tool_calls as up to three [tool, arg] string pairs in chronological order across turns (tool up to 64 characters, arg up to 512 UTF-8 bytes), last_activity_age_ms (null before any activity), and model_request_active; additionally in_flight_tool_count counts tracked in-flight tool calls (scheduled or started without a matching result, error or permission resolution; 0 when there is no snapshot or the adapter has no per-call lifecycle tracking, such as codex) and oldest_in_flight_tool_age_ms is the age since the oldest tracked in-flight call first appeared (null when none is tracked); use observe only when these suggest a meaningless loop.",
|
|
99
107
|
"required": ["agent_id"],
|
|
100
108
|
"properties": {
|
|
101
109
|
"agent_id": { "type": "integer", "minimum": 10000000, "maximum": 99999999 },
|
|
@@ -105,7 +113,7 @@
|
|
|
105
113
|
"additionalProperties": false
|
|
106
114
|
},
|
|
107
115
|
"observe": {
|
|
108
|
-
"description": "Read captured task observations for zcode, dsh or
|
|
116
|
+
"description": "Read captured task observations for zcode, dsh, codex or agy. zcode and dsh expose at most 200 Unicode characters of public reasoning; codex and agy reasoning is null. Coverage reports capture gaps and unsupported history truthfully.",
|
|
109
117
|
"type": "object",
|
|
110
118
|
"required": ["agent_id"],
|
|
111
119
|
"properties": {
|
|
@@ -175,11 +183,11 @@
|
|
|
175
183
|
"additionalProperties": false,
|
|
176
184
|
"properties": {
|
|
177
185
|
"external_subagent_status": { "input": [], "output": ["mcp_version", "components", "capabilities", "subagents"] },
|
|
178
|
-
"external_subagent_spawn": { "input": ["subagent", "repository", "permission_mode", "prompt", "model", "effort", "write_manifest"], "output": ["agent_id", "status"], "idempotent": false },
|
|
186
|
+
"external_subagent_spawn": { "input": ["subagent", "repository", "permission_mode", "prompt", "model", "effort", "write_manifest"], "output": ["agent_id", "status", "session_id"], "idempotent": false },
|
|
179
187
|
"external_subagent_wait": { "input": ["agent_id", "wait_time", "message_id"], "output": ["task", "pending_requests", "result_available", "activity", "result", "instruction", "timed_out", "message_receipt"] },
|
|
180
188
|
"external_subagent_observe": { "input": ["agent_id"], "output": ["tools", "reasoning", "coverage"], "idempotent": true },
|
|
181
189
|
"external_subagent_list": { "input": ["subagent", "repository", "phase", "outcome", "cursor", "limit"], "output": ["tasks", "next_cursor"] },
|
|
182
|
-
"external_subagent_send": { "input": ["agent_id", "message_id", "content"], "output": ["message_id", "disposition"] },
|
|
190
|
+
"external_subagent_send": { "input": ["agent_id", "message_id", "mode", "content"], "required": ["agent_id", "mode", "content"], "mode": ["queue", "steer"], "output": ["message_id", "disposition"] },
|
|
183
191
|
"external_subagent_respond": { "input": ["agent_id", "request_id", "decision", "content"], "output": ["disposition", "requested_decision", "effective_decision", "policy_overrode", "policy_reason_code"] },
|
|
184
192
|
"external_subagent_cancel": { "input": ["agent_id"], "output": ["task"] },
|
|
185
193
|
"external_subagent_result": { "input": ["agent_id", "offset", "limit"], "output": ["task", "result"] },
|