@enderfga/claw-orchestrator 7.12.1 → 7.14.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/README.md +14 -11
- package/configs/engines/README.md +20 -0
- package/configs/engines/dsh.json +17 -0
- package/configs/engines/kimi-code.json +17 -0
- package/dist/bin/cli.js +123 -34
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/acp-server.js +4 -2
- package/dist/src/acp-server.js.map +1 -1
- package/dist/src/autoloop/dispatcher.d.ts +19 -0
- package/dist/src/autoloop/dispatcher.js +57 -0
- package/dist/src/autoloop/dispatcher.js.map +1 -1
- package/dist/src/autoloop/runner.d.ts +102 -2
- package/dist/src/autoloop/runner.js +367 -44
- package/dist/src/autoloop/runner.js.map +1 -1
- package/dist/src/autoloop/types.d.ts +17 -0
- package/dist/src/autoloop/types.js.map +1 -1
- package/dist/src/dashboard/index.html +79 -73
- package/dist/src/embedded-server.js +35 -7
- package/dist/src/embedded-server.js.map +1 -1
- package/dist/src/engine-presets.d.ts +7 -3
- package/dist/src/engine-presets.js +39 -6
- package/dist/src/engine-presets.js.map +1 -1
- package/dist/src/engine-spawn.d.ts +8 -1
- package/dist/src/engine-spawn.js +13 -3
- package/dist/src/engine-spawn.js.map +1 -1
- package/dist/src/index.js +78 -71
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/agent-step.d.ts +3 -0
- package/dist/src/kernel/agent-step.js.map +1 -1
- package/dist/src/kernel/engine.d.ts +5 -2
- package/dist/src/kernel/engine.js +11 -12
- package/dist/src/kernel/engine.js.map +1 -1
- package/dist/src/kernel/nodes/autoloop.d.ts +32 -2
- package/dist/src/kernel/nodes/autoloop.js +122 -15
- package/dist/src/kernel/nodes/autoloop.js.map +1 -1
- package/dist/src/kernel/nodes/goal.d.ts +94 -0
- package/dist/src/kernel/nodes/goal.js +386 -0
- package/dist/src/kernel/nodes/goal.js.map +1 -0
- package/dist/src/kernel/nodes/index.d.ts +2 -1
- package/dist/src/kernel/nodes/index.js +3 -1
- package/dist/src/kernel/nodes/index.js.map +1 -1
- package/dist/src/kernel/templates/index.d.ts +27 -3
- package/dist/src/kernel/templates/index.js +78 -1
- package/dist/src/kernel/templates/index.js.map +1 -1
- package/dist/src/kernel/types.d.ts +41 -2
- package/dist/src/kernel/types.js.map +1 -1
- package/dist/src/persistent-acp-session.d.ts +76 -0
- package/dist/src/persistent-acp-session.js +409 -0
- package/dist/src/persistent-acp-session.js.map +1 -0
- package/dist/src/persistent-agy-session.js +1 -0
- package/dist/src/persistent-agy-session.js.map +1 -1
- package/dist/src/persistent-codex-app-session.d.ts +55 -26
- package/dist/src/persistent-codex-app-session.js +109 -24
- package/dist/src/persistent-codex-app-session.js.map +1 -1
- package/dist/src/persistent-codex-session.js +1 -0
- package/dist/src/persistent-codex-session.js.map +1 -1
- package/dist/src/persistent-cursor-session.js +1 -0
- package/dist/src/persistent-cursor-session.js.map +1 -1
- package/dist/src/persistent-custom-session.js +2 -0
- package/dist/src/persistent-custom-session.js.map +1 -1
- package/dist/src/persistent-gemini-session.js +1 -0
- package/dist/src/persistent-gemini-session.js.map +1 -1
- package/dist/src/persistent-grok-session.js +1 -0
- package/dist/src/persistent-grok-session.js.map +1 -1
- package/dist/src/persistent-opencode-session.js +1 -0
- package/dist/src/persistent-opencode-session.js.map +1 -1
- package/dist/src/persistent-session.js +1 -0
- package/dist/src/persistent-session.js.map +1 -1
- package/dist/src/sanitize.d.ts +10 -0
- package/dist/src/sanitize.js +26 -0
- package/dist/src/sanitize.js.map +1 -1
- package/dist/src/session-manager.d.ts +25 -20
- package/dist/src/session-manager.js +92 -43
- package/dist/src/session-manager.js.map +1 -1
- package/dist/src/types.d.ts +35 -1
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/skills/SKILL.md +8 -5
- package/skills/references/acp.md +4 -0
- package/skills/references/autoloop.md +27 -2
- package/skills/references/claude-cli-tracking.md +3 -1
- package/skills/references/cli.md +79 -39
- package/skills/references/dashboard.md +5 -1
- package/skills/references/multi-engine.md +39 -10
- package/skills/references/tools.md +77 -65
- package/skills/references/workflow.md +87 -13
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ clawo runs --since 1h # every turn on every engine: model, tokens, cost, verdi
|
|
|
49
49
|
| Switch engines mid-task | `session_handoff` replays a live conversation onto another engine in the same workspace. → [sessions](./skills/references/sessions.md) |
|
|
50
50
|
| Know what it cost | A durable per-turn ledger across engines (`clawo runs`) and a `maxBudgetUsd` cap the runtime enforces on every engine. → [observability](./skills/references/observability.md) |
|
|
51
51
|
| Runs that survive a crash | Durable workflows: every transition is checkpointed; resume, retry, timeout, cancel and steer come from the kernel. → [workflow](./skills/references/workflow.md) |
|
|
52
|
+
| Leave Codex on a goal for hours | `clawo goal` runs a Codex `/goal` on one thread until it finishes, reattaches it after a crash, resumes it after usage limits within a bound, optionally inside your own sandbox, then judges it with your checks. → [workflow](./skills/references/workflow.md#goal) |
|
|
52
53
|
| Call it from your own tools | An MCP server (`clawo-mcp`), an ACP agent (`clawo acp`) for Zed, JetBrains and other ACP editors, an OpenAI-compatible endpoint, a TypeScript SDK, and the CLI. → [integrations](#integrations) |
|
|
53
54
|
|
|
54
55
|
**How it relates to other tools.** claude-squad, Conductor and Vibe Kanban are interfaces for running agents side by side in worktrees; Claude Code subagents and Codex's parallel attempts stay within one vendor. Claw Orchestrator is a headless runtime: several vendors behind one API, durable workflows, and a verification step the agents cannot edit. They can be used together.
|
|
@@ -71,6 +72,7 @@ The server writes an access token to `~/.openclaw/server-token`; the CLI reads i
|
|
|
71
72
|
```bash
|
|
72
73
|
clawo solve "Fix the failing tests" --engine claude --check "npm test" --wait
|
|
73
74
|
clawo fanout "How would you speed up this test suite?" --engines claude,codex,grok --synthesize --wait
|
|
75
|
+
clawo goal --objective-file GOAL.md --check "npm run bench:check" --check-timeout 120 --wait
|
|
74
76
|
clawo workflow list
|
|
75
77
|
```
|
|
76
78
|
|
|
@@ -99,7 +101,7 @@ const result = await manager.sendMessage('fix-tests', 'Fix the failing tests');
|
|
|
99
101
|
| Capability | What it does | Reference |
|
|
100
102
|
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
101
103
|
| **Persistent Sessions** | Long-lived coding agents kept alive across requests, with full context, tool, model, and worktree control. | [`sessions.md`](./skills/references/sessions.md) |
|
|
102
|
-
| **Multi-Engine Runtime** | One interface over Claude Code, Codex, Antigravity (agy), Grok Build, OpenCode, and arbitrary custom CLIs.
|
|
104
|
+
| **Multi-Engine Runtime** | One interface over Claude Code, Codex, Antigravity (agy), Grok Build, OpenCode, any Agent Client Protocol agent (Kimi Code, dsh), and arbitrary custom CLIs. | [`multi-engine.md`](./skills/references/multi-engine.md) |
|
|
103
105
|
| **Session Handoff** | Move a live conversation to another engine or model — a stuck Claude session into Codex, an expensive model into a cheaper one. The new session picks up where the old one stopped, in the same workspace; the old one keeps running. | [`sessions.md`](./skills/references/sessions.md) |
|
|
104
106
|
| **Multi-Agent Council** | Parallel agents in isolated git worktrees, voting on consensus until they agree. | [`council.md`](./skills/references/council.md) |
|
|
105
107
|
| **Fan-out** | Run one task across N engine/model agents in parallel and collect their answers, with an optional synthesis pass — the cross-engine best-of-N / diverse-perspective primitive (no rounds or worktrees). | [`tools.md`](./skills/references/tools.md) |
|
|
@@ -165,16 +167,17 @@ block, and the cancellation and permission limitations are in
|
|
|
165
167
|
|
|
166
168
|
## Engine Compatibility
|
|
167
169
|
|
|
168
|
-
| Engine | CLI | Tested Version
|
|
169
|
-
| ----------- | ---------- |
|
|
170
|
-
| Claude Code | `claude` | 2.1.
|
|
171
|
-
| Codex | `codex` | 0.
|
|
172
|
-
| Antigravity | `agy` | 1.4.
|
|
173
|
-
| Grok Build | `grok` | 1.0.
|
|
174
|
-
| OpenCode | `opencode` | 1.19.
|
|
175
|
-
|
|
|
176
|
-
|
|
177
|
-
|
|
170
|
+
| Engine | CLI | Tested Version |
|
|
171
|
+
| ----------- | ---------- | ------------------------------- |
|
|
172
|
+
| Claude Code | `claude` | 2.1.310 |
|
|
173
|
+
| Codex | `codex` | 0.164.0 |
|
|
174
|
+
| Antigravity | `agy` | 1.4.6 |
|
|
175
|
+
| Grok Build | `grok` | 1.0.70 |
|
|
176
|
+
| OpenCode | `opencode` | 1.19.4 |
|
|
177
|
+
| ACP agent | any | Kimi Code 2.3.1, dsh 0.2.1-rc.2 |
|
|
178
|
+
| Custom CLI | any | — |
|
|
179
|
+
|
|
180
|
+
An agent that serves the Agent Client Protocol on stdio runs as `engine: 'acp'` — see [`multi-engine.md`](./skills/references/multi-engine.md#acp-agents-engine-acp). Any other coding CLI that runs as a subprocess can be wired up as a custom engine — see [`multi-engine.md`](./skills/references/multi-engine.md#custom-engine-engine-custom).
|
|
178
181
|
|
|
179
182
|
---
|
|
180
183
|
|
|
@@ -47,3 +47,23 @@ Contribution steps, the smoke script, and the full field reference are in
|
|
|
47
47
|
|
|
48
48
|
`engine.bin` must be a command name, not an absolute path — one machine's layout
|
|
49
49
|
is not a preset. Use `binEnv` so people who installed it elsewhere can point at it.
|
|
50
|
+
|
|
51
|
+
## ACP agents
|
|
52
|
+
|
|
53
|
+
A preset can name an agent that serves the Agent Client Protocol on stdio instead
|
|
54
|
+
of a `CustomEngineConfig`: it carries `acp` in place of `engine`, and is started
|
|
55
|
+
with `engine: "acp"` (`clawo session-start <name> -e acp --acp-agent <id>`). No
|
|
56
|
+
protocol mapping is needed, since every ACP agent speaks the same protocol.
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"id": "kimi-code",
|
|
61
|
+
"tier": "community",
|
|
62
|
+
"description": "…",
|
|
63
|
+
"provenance": { "maintainer": "…", "verifiedAgainst": "2.3.1", "verifiedOn": "2026-10-24" },
|
|
64
|
+
"acp": { "name": "kimi-code", "command": "kimi", "commandEnv": "KIMI_BIN", "args": ["acp"] }
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`acp.command` follows the same rule as `engine.bin`: a command name, with
|
|
69
|
+
`commandEnv` for an install elsewhere.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dsh",
|
|
3
|
+
"tier": "community",
|
|
4
|
+
"description": "DeepSeek Harness (`dsh`), served over the Agent Client Protocol (`dsh --profile acp`). Start with engine 'acp'.",
|
|
5
|
+
"provenance": {
|
|
6
|
+
"maintainer": "Enderfga",
|
|
7
|
+
"verifiedAgainst": "0.2.1-rc.2",
|
|
8
|
+
"verifiedOn": "2026-10-24",
|
|
9
|
+
"sourceUrl": "https://www.npmjs.com/package/@deepseek-ai/dsh"
|
|
10
|
+
},
|
|
11
|
+
"acp": {
|
|
12
|
+
"name": "dsh",
|
|
13
|
+
"command": "dsh",
|
|
14
|
+
"commandEnv": "DSH_BIN",
|
|
15
|
+
"args": ["--profile", "acp"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "kimi-code",
|
|
3
|
+
"tier": "community",
|
|
4
|
+
"description": "Moonshot AI's Kimi Code CLI, served over the Agent Client Protocol (`kimi acp`). Start with engine 'acp'.",
|
|
5
|
+
"provenance": {
|
|
6
|
+
"maintainer": "Enderfga",
|
|
7
|
+
"verifiedAgainst": "2.3.1",
|
|
8
|
+
"verifiedOn": "2026-10-24",
|
|
9
|
+
"sourceUrl": "https://www.kimi.com/code/docs/en/kimi-code-cli/reference/kimi-acp"
|
|
10
|
+
},
|
|
11
|
+
"acp": {
|
|
12
|
+
"name": "kimi-code",
|
|
13
|
+
"command": "kimi",
|
|
14
|
+
"commandEnv": "KIMI_BIN",
|
|
15
|
+
"args": ["acp"]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/bin/cli.js
CHANGED
|
@@ -67,6 +67,15 @@ async function api(path, method = 'GET', body) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
// ─── CLI ─────────────────────────────────────────────────────────────────────
|
|
70
|
+
/**
|
|
71
|
+
* Print an API failure the same way every command does, and exit non-zero so a
|
|
72
|
+
* script can tell: a command that could not reach the server used to exit 0.
|
|
73
|
+
*/
|
|
74
|
+
function fail(r) {
|
|
75
|
+
const error = r && typeof r === 'object' && 'error' in r ? r.error : r;
|
|
76
|
+
console.error(`Failed: ${String(error)}`);
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
}
|
|
70
79
|
const program = new Command();
|
|
71
80
|
program.name('clawo').description('Claw Orchestrator CLI').version(getCliVersion());
|
|
72
81
|
// Serve (standalone mode — no OpenClaw needed)
|
|
@@ -146,9 +155,9 @@ program
|
|
|
146
155
|
// Session commands
|
|
147
156
|
program
|
|
148
157
|
.command('session-start [name]')
|
|
149
|
-
.description('Start a persistent coding session (Claude Code, Codex, Antigravity, Grok Build, OpenCode, or a custom engine preset)')
|
|
158
|
+
.description('Start a persistent coding session (Claude Code, Codex, Antigravity, Grok Build, OpenCode, an ACP agent, or a custom engine preset)')
|
|
150
159
|
.option('-d, --cwd <dir>', 'Working directory')
|
|
151
|
-
.option('-e, --engine <engine>', 'Engine: claude (default), codex, codex-app, gemini, agy, cursor, grok, opencode, or custom')
|
|
160
|
+
.option('-e, --engine <engine>', 'Engine: claude (default), codex, codex-app, gemini, agy, cursor, grok, opencode, acp, or custom')
|
|
152
161
|
.option('-m, --model <model>', 'Model to use')
|
|
153
162
|
.option('--permission-mode <mode>', 'Permission mode', 'acceptEdits')
|
|
154
163
|
.option('--effort <level>', 'Effort level')
|
|
@@ -174,6 +183,7 @@ program
|
|
|
174
183
|
.option('--base-url <url>', 'Custom API endpoint (for proxy)')
|
|
175
184
|
.option('--add-dir <dirs>', 'Comma-separated additional working directories')
|
|
176
185
|
.option('--custom-engine <preset>', 'With -e custom: the id of a bundled engine preset (see `clawo engines`). Inline configs are not accepted here — that is what presets are for')
|
|
186
|
+
.option('--acp-agent <preset>', 'With -e acp: the id of a bundled ACP agent preset (see `clawo engines`)')
|
|
177
187
|
.action(async (name, opts) => {
|
|
178
188
|
const body = { name: name || `session-${Date.now()}` };
|
|
179
189
|
if (opts.cwd)
|
|
@@ -185,6 +195,8 @@ program
|
|
|
185
195
|
// refuses those — see `clawo engines` for what is available.
|
|
186
196
|
if (opts.customEngine)
|
|
187
197
|
body.customEngine = opts.customEngine;
|
|
198
|
+
if (opts.acpAgent)
|
|
199
|
+
body.acpAgent = opts.acpAgent;
|
|
188
200
|
if (opts.model)
|
|
189
201
|
body.model = opts.model;
|
|
190
202
|
if (opts.permissionMode)
|
|
@@ -271,19 +283,26 @@ program
|
|
|
271
283
|
if (result.ok) {
|
|
272
284
|
console.log(`Session '${body.name}' started!`);
|
|
273
285
|
if (result.claudeSessionId)
|
|
274
|
-
console.log(`
|
|
286
|
+
console.log(`Engine session ID: ${result.claudeSessionId}`);
|
|
275
287
|
}
|
|
276
288
|
else
|
|
277
|
-
|
|
289
|
+
fail(result.error);
|
|
278
290
|
});
|
|
279
291
|
program
|
|
280
|
-
.command('session-send <name>
|
|
292
|
+
.command('session-send <name> [message]')
|
|
281
293
|
.description('Send a message to a session')
|
|
294
|
+
.option('--message-file <file>', 'Read the message from a file (for long or dash-leading text)')
|
|
282
295
|
.option('--effort <level>', 'Effort level')
|
|
283
296
|
.option('--plan', 'Plan mode')
|
|
284
297
|
.option('-t, --timeout <ms>', 'Timeout', '300000')
|
|
285
298
|
.option('-s, --stream', 'Collect streaming chunks and include in output')
|
|
286
|
-
.
|
|
299
|
+
.option('--json', 'Print the whole result as JSON')
|
|
300
|
+
.action(async (name, messageArg, opts) => {
|
|
301
|
+
const message = opts.messageFile ? fs.readFileSync(opts.messageFile, 'utf-8') : messageArg;
|
|
302
|
+
if (!message) {
|
|
303
|
+
fail('give a message, or --message-file');
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
287
306
|
const result = await api('/session/send', 'POST', {
|
|
288
307
|
name,
|
|
289
308
|
message,
|
|
@@ -292,14 +311,19 @@ program
|
|
|
292
311
|
timeout: parseInt(opts.timeout),
|
|
293
312
|
stream: opts.stream || undefined,
|
|
294
313
|
});
|
|
295
|
-
if (
|
|
314
|
+
if (opts.json) {
|
|
315
|
+
console.log(JSON.stringify(result));
|
|
316
|
+
if (!result.ok)
|
|
317
|
+
process.exitCode = 1;
|
|
318
|
+
}
|
|
319
|
+
else if (result.ok) {
|
|
296
320
|
console.log(result.output);
|
|
297
321
|
if (opts.stream && Array.isArray(result.chunks) && result.chunks.length > 0) {
|
|
298
322
|
console.log(`\n[${result.chunks.length} streaming chunks collected]`);
|
|
299
323
|
}
|
|
300
324
|
}
|
|
301
325
|
else
|
|
302
|
-
|
|
326
|
+
fail(result.error);
|
|
303
327
|
});
|
|
304
328
|
program
|
|
305
329
|
.command('session-stop <name>')
|
|
@@ -309,7 +333,7 @@ program
|
|
|
309
333
|
if (r.ok)
|
|
310
334
|
console.log(`Session '${name}' stopped.`);
|
|
311
335
|
else
|
|
312
|
-
|
|
336
|
+
fail(r.error);
|
|
313
337
|
});
|
|
314
338
|
program
|
|
315
339
|
.command('session-list')
|
|
@@ -317,7 +341,7 @@ program
|
|
|
317
341
|
.action(async () => {
|
|
318
342
|
const r = await api('/session/list');
|
|
319
343
|
if (!r.ok) {
|
|
320
|
-
|
|
344
|
+
fail(r.error);
|
|
321
345
|
return;
|
|
322
346
|
}
|
|
323
347
|
const sessions = r.sessions;
|
|
@@ -331,10 +355,17 @@ program
|
|
|
331
355
|
program
|
|
332
356
|
.command('session-status <name>')
|
|
333
357
|
.description('Get session status')
|
|
334
|
-
.
|
|
358
|
+
.option('--json', 'Print the whole status as JSON')
|
|
359
|
+
.action(async (name, opts) => {
|
|
335
360
|
const r = await api('/session/status', 'POST', { name });
|
|
361
|
+
if (opts.json) {
|
|
362
|
+
console.log(JSON.stringify(r));
|
|
363
|
+
if (!r.ok)
|
|
364
|
+
process.exitCode = 1;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
336
367
|
if (!r.ok) {
|
|
337
|
-
|
|
368
|
+
fail(r.error);
|
|
338
369
|
return;
|
|
339
370
|
}
|
|
340
371
|
const s = r.stats;
|
|
@@ -343,10 +374,6 @@ program
|
|
|
343
374
|
console.log(` Tokens: ${s.tokensIn} in / ${s.tokensOut} out`);
|
|
344
375
|
console.log(` Uptime: ${s.uptime}s`);
|
|
345
376
|
});
|
|
346
|
-
/** Print an API failure the same way every command does. */
|
|
347
|
-
function fail(r) {
|
|
348
|
-
console.error(`Failed: ${r.error}`);
|
|
349
|
-
}
|
|
350
377
|
program
|
|
351
378
|
.command('engines')
|
|
352
379
|
.description('List the community engine presets bundled with this package')
|
|
@@ -372,7 +399,9 @@ program
|
|
|
372
399
|
console.log(` verified by ${p.provenance.maintainer} against ${p.provenance.verifiedAgainst} on ${p.provenance.verifiedOn}`);
|
|
373
400
|
if (p.provenance.smokeUrl)
|
|
374
401
|
console.log(` smoke: ${p.provenance.smokeUrl}`);
|
|
375
|
-
console.log(
|
|
402
|
+
console.log(p.acp
|
|
403
|
+
? ` use: clawo session-start my-session -e acp --acp-agent ${p.id}`
|
|
404
|
+
: ` use: clawo session-start my-session -e custom --custom-engine ${p.id}`);
|
|
376
405
|
console.log('');
|
|
377
406
|
}
|
|
378
407
|
});
|
|
@@ -401,7 +430,7 @@ program
|
|
|
401
430
|
params.set('verified', 'false');
|
|
402
431
|
const r = await api(`/runs?${params.toString()}`);
|
|
403
432
|
if (!r.ok) {
|
|
404
|
-
|
|
433
|
+
fail(r.error);
|
|
405
434
|
return;
|
|
406
435
|
}
|
|
407
436
|
if (opts.json) {
|
|
@@ -418,7 +447,7 @@ program
|
|
|
418
447
|
.action(async (name, pattern, opts) => {
|
|
419
448
|
const r = await api('/session/grep', 'POST', { name, pattern, limit: parseInt(opts.limit) });
|
|
420
449
|
if (!r.ok) {
|
|
421
|
-
|
|
450
|
+
fail(r.error);
|
|
422
451
|
return;
|
|
423
452
|
}
|
|
424
453
|
console.log(`Found ${r.count} match(es)`);
|
|
@@ -434,7 +463,7 @@ program
|
|
|
434
463
|
if (r.ok)
|
|
435
464
|
console.log('Compacted.');
|
|
436
465
|
else
|
|
437
|
-
|
|
466
|
+
fail(r.error);
|
|
438
467
|
});
|
|
439
468
|
// Agent management
|
|
440
469
|
program
|
|
@@ -445,7 +474,7 @@ program
|
|
|
445
474
|
const q = opts.cwd ? `?cwd=${encodeURIComponent(opts.cwd)}` : '';
|
|
446
475
|
const r = await api(`/agents${q}`);
|
|
447
476
|
if (!r.ok) {
|
|
448
|
-
|
|
477
|
+
fail(r.error);
|
|
449
478
|
return;
|
|
450
479
|
}
|
|
451
480
|
const agents = r.agents;
|
|
@@ -472,7 +501,7 @@ program
|
|
|
472
501
|
if (r.ok)
|
|
473
502
|
console.log(`Agent '${name}' created at: ${r.path}`);
|
|
474
503
|
else
|
|
475
|
-
|
|
504
|
+
fail(r.error);
|
|
476
505
|
});
|
|
477
506
|
// Skills
|
|
478
507
|
program
|
|
@@ -483,7 +512,7 @@ program
|
|
|
483
512
|
const q = opts.cwd ? `?cwd=${encodeURIComponent(opts.cwd)}` : '';
|
|
484
513
|
const r = await api(`/skills${q}`);
|
|
485
514
|
if (!r.ok) {
|
|
486
|
-
|
|
515
|
+
fail(r.error);
|
|
487
516
|
return;
|
|
488
517
|
}
|
|
489
518
|
const skills = r.skills;
|
|
@@ -512,7 +541,7 @@ program
|
|
|
512
541
|
if (r.ok)
|
|
513
542
|
console.log(`Skill '${name}' created at: ${r.path}`);
|
|
514
543
|
else
|
|
515
|
-
|
|
544
|
+
fail(r.error);
|
|
516
545
|
});
|
|
517
546
|
// Rules
|
|
518
547
|
program
|
|
@@ -523,7 +552,7 @@ program
|
|
|
523
552
|
const q = opts.cwd ? `?cwd=${encodeURIComponent(opts.cwd)}` : '';
|
|
524
553
|
const r = await api(`/rules${q}`);
|
|
525
554
|
if (!r.ok) {
|
|
526
|
-
|
|
555
|
+
fail(r.error);
|
|
527
556
|
return;
|
|
528
557
|
}
|
|
529
558
|
const rules = r.rules;
|
|
@@ -562,7 +591,7 @@ program
|
|
|
562
591
|
if (r.ok)
|
|
563
592
|
console.log(`Rule '${name}' created at: ${r.path}`);
|
|
564
593
|
else
|
|
565
|
-
|
|
594
|
+
fail(r.error);
|
|
566
595
|
});
|
|
567
596
|
// Agent teams
|
|
568
597
|
program
|
|
@@ -573,7 +602,7 @@ program
|
|
|
573
602
|
if (r.ok)
|
|
574
603
|
console.log(r.response || 'No team info');
|
|
575
604
|
else
|
|
576
|
-
|
|
605
|
+
fail(r.error);
|
|
577
606
|
});
|
|
578
607
|
program
|
|
579
608
|
.command('session-team-send <name> <teammate> <message>')
|
|
@@ -583,7 +612,7 @@ program
|
|
|
583
612
|
if (r.ok)
|
|
584
613
|
console.log(r.output || 'Sent');
|
|
585
614
|
else
|
|
586
|
-
|
|
615
|
+
fail(r.error);
|
|
587
616
|
});
|
|
588
617
|
// ─── workflow / verify ──────────────────────────────────────────────────────
|
|
589
618
|
program
|
|
@@ -635,7 +664,15 @@ program
|
|
|
635
664
|
if (run.error)
|
|
636
665
|
console.log(`error: ${String(run.error)}`);
|
|
637
666
|
for (const node of Object.values(run.nodes)) {
|
|
638
|
-
console.log(` ${String(node.id).padEnd(18)} ${String(node.kind).padEnd(11)} ${node.state}`);
|
|
667
|
+
console.log(` ${String(node.id).padEnd(18)} ${String(node.kind).padEnd(11)} ${String(node.state)}`);
|
|
668
|
+
// A goal runs for hours; its checkpoint is the only progress there is.
|
|
669
|
+
const g = node.kind === 'goal' ? node.data : undefined;
|
|
670
|
+
if (g) {
|
|
671
|
+
const err = g.lastError;
|
|
672
|
+
console.log(` goal ${String(g.status)} ${String(g.tokensUsed)} tokens ${String(g.timeUsedSeconds)}s ` +
|
|
673
|
+
`resumes ${String(g.resumes)} thread ${String(g.threadId)}` +
|
|
674
|
+
(err ? `\n last error: ${err.code ?? err.message}` : ''));
|
|
675
|
+
}
|
|
639
676
|
}
|
|
640
677
|
return;
|
|
641
678
|
}
|
|
@@ -716,19 +753,19 @@ function collect(value, previous) {
|
|
|
716
753
|
* `--check "node --test"` → a command check. Split on whitespace and run as
|
|
717
754
|
* argv, never through a shell; anything that needs quoting goes in `--contract`.
|
|
718
755
|
*/
|
|
719
|
-
function buildContract(checks, contractFile) {
|
|
756
|
+
function buildContract(checks, contractFile, timeoutMs) {
|
|
720
757
|
if (contractFile) {
|
|
721
758
|
const raw = JSON.parse(fs.readFileSync(contractFile, 'utf-8'));
|
|
722
759
|
if (!checks.length)
|
|
723
760
|
return raw;
|
|
724
|
-
return { ...raw, checks: [...(raw.checks ?? []), ...checksFromFlags(checks)] };
|
|
761
|
+
return { ...raw, checks: [...(raw.checks ?? []), ...checksFromFlags(checks, timeoutMs)] };
|
|
725
762
|
}
|
|
726
|
-
return checks.length ? { checks: checksFromFlags(checks) } : undefined;
|
|
763
|
+
return checks.length ? { checks: checksFromFlags(checks, timeoutMs) } : undefined;
|
|
727
764
|
}
|
|
728
|
-
function checksFromFlags(checks) {
|
|
765
|
+
function checksFromFlags(checks, timeoutMs) {
|
|
729
766
|
return checks.map((c, i) => {
|
|
730
767
|
const [cmd, ...args] = c.trim().split(/\s+/);
|
|
731
|
-
return { id: `check-${i + 1}`, type: 'command', cmd, args };
|
|
768
|
+
return { id: `check-${i + 1}`, type: 'command', cmd, args, ...(timeoutMs ? { timeoutMs } : {}) };
|
|
732
769
|
});
|
|
733
770
|
}
|
|
734
771
|
const RUN_DONE = new Set(['completed', 'failed', 'cancelled']);
|
|
@@ -946,5 +983,57 @@ program
|
|
|
946
983
|
if (node?.output)
|
|
947
984
|
console.log(`\n${node.output}`);
|
|
948
985
|
});
|
|
986
|
+
program
|
|
987
|
+
.command('goal')
|
|
988
|
+
.description('Run a Codex /goal on one codex-app thread, then have the runtime check it: goal → verify → repair')
|
|
989
|
+
.argument('[objective]', 'What Codex should achieve (or use --objective-file)')
|
|
990
|
+
.option('--objective-file <file>', 'Read the objective from a file')
|
|
991
|
+
.option('-m, --model <model>', "Codex model (default: codex's own)")
|
|
992
|
+
.option('--effort <level>', 'Reasoning effort')
|
|
993
|
+
.option('--sandbox <mode>', "Codex's own sandbox: read-only | workspace-write | danger-full-access")
|
|
994
|
+
.option('--launcher <arg>', 'Run codex under this command, one argv element per flag, e.g. --launcher ./sandbox.sh --launcher task --launcher "0 1"', collect, [])
|
|
995
|
+
.option('--thread <id>', 'Start on this existing codex thread: its goal is replaced, its history kept')
|
|
996
|
+
.option('--token-budget <n>', 'Tokens the goal may spend before it stops as budgetLimited')
|
|
997
|
+
.option('--goal-timeout <minutes>', 'Wall-clock budget for one goal pass (default: none)')
|
|
998
|
+
.option('-c, --check <command>', 'Acceptance check the runtime runs after the goal (repeatable)', collect, [])
|
|
999
|
+
.option('--check-timeout <minutes>', 'Timeout for each --check (default 10)')
|
|
1000
|
+
.option('--contract <file>', 'Acceptance contract JSON file (merged with --check)')
|
|
1001
|
+
.option('--max-repairs <n>', 'Times a red verdict sends the goal back to the same thread', '1')
|
|
1002
|
+
.option('--cwd <dir>', 'Project directory', process.cwd())
|
|
1003
|
+
.option('-w, --wait', 'Follow the run until it ends; exit 1 unless it completes')
|
|
1004
|
+
.option('--json', 'Machine-readable output')
|
|
1005
|
+
.action(async (objectiveArg, opts) => {
|
|
1006
|
+
const { goalWorkflow } = await import('../src/kernel/templates/index.js');
|
|
1007
|
+
const { GOAL_OBJECTIVE_MAX_CHARS } = await import('../src/kernel/nodes/goal.js');
|
|
1008
|
+
const objective = opts.objectiveFile ? fs.readFileSync(opts.objectiveFile, 'utf-8').trim() : objectiveArg?.trim();
|
|
1009
|
+
if (!objective) {
|
|
1010
|
+
console.error('Error: give an objective, or --objective-file');
|
|
1011
|
+
process.exitCode = 1;
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
if ([...objective].length > GOAL_OBJECTIVE_MAX_CHARS) {
|
|
1015
|
+
console.error(`Error: the objective is ${[...objective].length} characters; Codex accepts at most ${GOAL_OBJECTIVE_MAX_CHARS}. ` +
|
|
1016
|
+
'Put the full brief in a file in the working directory and name that file in the objective.');
|
|
1017
|
+
process.exitCode = 1;
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const minutes = (v) => (v ? Math.max(1, parseFloat(v)) * 60_000 : undefined);
|
|
1021
|
+
const cwd = path.resolve(opts.cwd);
|
|
1022
|
+
const contract = buildContract(opts.check, opts.contract, minutes(opts.checkTimeout));
|
|
1023
|
+
const spec = goalWorkflow({
|
|
1024
|
+
task: objective,
|
|
1025
|
+
cwd,
|
|
1026
|
+
contract: contract,
|
|
1027
|
+
model: opts.model,
|
|
1028
|
+
effort: opts.effort,
|
|
1029
|
+
sandboxMode: opts.sandbox,
|
|
1030
|
+
launcher: opts.launcher.length ? opts.launcher : undefined,
|
|
1031
|
+
threadId: opts.thread,
|
|
1032
|
+
tokenBudget: opts.tokenBudget ? parseInt(opts.tokenBudget, 10) : undefined,
|
|
1033
|
+
goalTimeoutMs: minutes(opts.goalTimeout),
|
|
1034
|
+
maxRepairs: Math.max(0, parseInt(opts.maxRepairs, 10) || 0),
|
|
1035
|
+
});
|
|
1036
|
+
await startWorkflow(spec, cwd, contract, opts);
|
|
1037
|
+
});
|
|
949
1038
|
program.parse();
|
|
950
1039
|
//# sourceMappingURL=cli.js.map
|