@enderfga/claw-orchestrator 5.1.0 → 6.0.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 +26 -26
- package/dist/bin/cli.js +107 -1
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/acp-server.d.ts +5 -5
- package/dist/src/acp-server.js +3 -3
- package/dist/src/acp-server.js.map +1 -1
- package/dist/src/autoloop/dispatcher.d.ts +22 -0
- package/dist/src/autoloop/dispatcher.js +71 -13
- package/dist/src/autoloop/dispatcher.js.map +1 -1
- package/dist/src/autoloop/messages.d.ts +10 -0
- package/dist/src/autoloop/messages.js.map +1 -1
- package/dist/src/autoloop/runner.js +6 -0
- package/dist/src/autoloop/runner.js.map +1 -1
- package/dist/src/constants.d.ts +0 -6
- package/dist/src/constants.js +0 -6
- package/dist/src/constants.js.map +1 -1
- package/dist/src/council.d.ts +15 -0
- package/dist/src/council.js +48 -35
- package/dist/src/council.js.map +1 -1
- package/dist/src/dashboard/index.html +191 -6
- package/dist/src/embedded-server.js +132 -9
- package/dist/src/embedded-server.js.map +1 -1
- package/dist/src/fanout.d.ts +30 -1
- package/dist/src/fanout.js +32 -3
- package/dist/src/fanout.js.map +1 -1
- package/dist/src/index.js +359 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/agent-step.d.ts +59 -0
- package/dist/src/kernel/agent-step.js +100 -0
- package/dist/src/kernel/agent-step.js.map +1 -0
- package/dist/src/kernel/conditions.d.ts +11 -0
- package/dist/src/kernel/conditions.js +24 -0
- package/dist/src/kernel/conditions.js.map +1 -0
- package/dist/src/kernel/engine.d.ts +319 -0
- package/dist/src/kernel/engine.js +1047 -0
- package/dist/src/kernel/engine.js.map +1 -0
- package/dist/src/kernel/exec.d.ts +43 -0
- package/dist/src/kernel/exec.js +112 -0
- package/dist/src/kernel/exec.js.map +1 -0
- package/dist/src/kernel/file-lock.d.ts +50 -0
- package/dist/src/kernel/file-lock.js +135 -0
- package/dist/src/kernel/file-lock.js.map +1 -0
- package/dist/src/kernel/nodes/agent.d.ts +4 -0
- package/dist/src/kernel/nodes/agent.js +35 -0
- package/dist/src/kernel/nodes/agent.js.map +1 -0
- package/dist/src/kernel/nodes/autoloop.d.ts +78 -0
- package/dist/src/kernel/nodes/autoloop.js +75 -0
- package/dist/src/kernel/nodes/autoloop.js.map +1 -0
- package/dist/src/kernel/nodes/council.d.ts +12 -0
- package/dist/src/kernel/nodes/council.js +88 -0
- package/dist/src/kernel/nodes/council.js.map +1 -0
- package/dist/src/kernel/nodes/fanout.d.ts +11 -0
- package/dist/src/kernel/nodes/fanout.js +63 -0
- package/dist/src/kernel/nodes/fanout.js.map +1 -0
- package/dist/src/kernel/nodes/human-gate.d.ts +4 -0
- package/dist/src/kernel/nodes/human-gate.js +7 -0
- package/dist/src/kernel/nodes/human-gate.js.map +1 -0
- package/dist/src/kernel/nodes/index.d.ts +12 -0
- package/dist/src/kernel/nodes/index.js +21 -0
- package/dist/src/kernel/nodes/index.js.map +1 -0
- package/dist/src/kernel/nodes/router.d.ts +4 -0
- package/dist/src/kernel/nodes/router.js +12 -0
- package/dist/src/kernel/nodes/router.js.map +1 -0
- package/dist/src/kernel/nodes/subflow.d.ts +13 -0
- package/dist/src/kernel/nodes/subflow.js +38 -0
- package/dist/src/kernel/nodes/subflow.js.map +1 -0
- package/dist/src/kernel/nodes/ultraapp.d.ts +60 -0
- package/dist/src/kernel/nodes/ultraapp.js +62 -0
- package/dist/src/kernel/nodes/ultraapp.js.map +1 -0
- package/dist/src/kernel/nodes/verifier.d.ts +14 -0
- package/dist/src/kernel/nodes/verifier.js +84 -0
- package/dist/src/kernel/nodes/verifier.js.map +1 -0
- package/dist/src/kernel/projections.d.ts +42 -0
- package/dist/src/kernel/projections.js +133 -0
- package/dist/src/kernel/projections.js.map +1 -0
- package/dist/src/kernel/repo.d.ts +13 -0
- package/dist/src/kernel/repo.js +64 -0
- package/dist/src/kernel/repo.js.map +1 -0
- package/dist/src/kernel/secrets.d.ts +25 -0
- package/dist/src/kernel/secrets.js +48 -0
- package/dist/src/kernel/secrets.js.map +1 -0
- package/dist/src/kernel/store.d.ts +225 -0
- package/dist/src/kernel/store.js +838 -0
- package/dist/src/kernel/store.js.map +1 -0
- package/dist/src/kernel/templates/index.d.ts +140 -0
- package/dist/src/kernel/templates/index.js +266 -0
- package/dist/src/kernel/templates/index.js.map +1 -0
- package/dist/src/kernel/types.d.ts +326 -0
- package/dist/src/kernel/types.js +19 -0
- package/dist/src/kernel/types.js.map +1 -0
- package/dist/src/run-ledger.d.ts +57 -3
- package/dist/src/run-ledger.js +45 -2
- package/dist/src/run-ledger.js.map +1 -1
- package/dist/src/session-manager.d.ts +176 -129
- package/dist/src/session-manager.js +652 -603
- package/dist/src/session-manager.js.map +1 -1
- package/dist/src/types.d.ts +33 -3
- package/dist/src/ultraapp/build.d.ts +117 -3
- package/dist/src/ultraapp/build.js +319 -3
- package/dist/src/ultraapp/build.js.map +1 -1
- package/dist/src/ultraapp/contract.d.ts +52 -0
- package/dist/src/ultraapp/contract.js +83 -0
- package/dist/src/ultraapp/contract.js.map +1 -0
- package/dist/src/ultraapp/conventions.js +9 -2
- package/dist/src/ultraapp/conventions.js.map +1 -1
- package/dist/src/ultraapp/fix-on-failure.d.ts +21 -2
- package/dist/src/ultraapp/fix-on-failure.js +46 -62
- package/dist/src/ultraapp/fix-on-failure.js.map +1 -1
- package/dist/src/ultraapp/manager.d.ts +107 -2
- package/dist/src/ultraapp/manager.js +305 -86
- package/dist/src/ultraapp/manager.js.map +1 -1
- package/dist/src/verify/baseline.d.ts +73 -0
- package/dist/src/verify/baseline.js +186 -0
- package/dist/src/verify/baseline.js.map +1 -0
- package/dist/src/verify/contract.d.ts +116 -0
- package/dist/src/verify/contract.js +142 -0
- package/dist/src/verify/contract.js.map +1 -0
- package/dist/src/verify/evidence.d.ts +61 -0
- package/dist/src/verify/evidence.js +133 -0
- package/dist/src/verify/evidence.js.map +1 -0
- package/dist/src/verify/runner.d.ts +63 -0
- package/dist/src/verify/runner.js +317 -0
- package/dist/src/verify/runner.js.map +1 -0
- package/openclaw.plugin.json +8 -0
- package/package.json +2 -2
- package/skills/SKILL.md +120 -79
- package/skills/references/acp.md +17 -17
- package/skills/references/autoloop.md +139 -65
- package/skills/references/claude-cli-tracking.md +4 -4
- package/skills/references/cli.md +101 -59
- package/skills/references/council.md +109 -37
- package/skills/references/dashboard.md +34 -6
- package/skills/references/getting-started.md +13 -13
- package/skills/references/inbox.md +4 -4
- package/skills/references/mcp.md +39 -34
- package/skills/references/multi-engine.md +51 -47
- package/skills/references/observability.md +88 -28
- package/skills/references/openai-compat.md +39 -39
- package/skills/references/sessions.md +43 -25
- package/skills/references/tools.md +402 -309
- package/skills/references/ultra.md +45 -45
- package/skills/references/ultraapp.md +126 -50
- package/skills/references/verification.md +187 -0
- package/skills/references/workflow.md +362 -0
- package/dist/src/ultraapp/fix-on-failure-session.d.ts +0 -23
- package/dist/src/ultraapp/fix-on-failure-session.js +0 -51
- package/dist/src/ultraapp/fix-on-failure-session.js.map +0 -1
|
@@ -39,6 +39,7 @@ Default engine. Long-running subprocess with streaming JSON I/O. Tested with Cla
|
|
|
39
39
|
- Fork subagent (`forkSubagent`), tool search (`enableToolSearch`), OpenTelemetry logging toggles (`otelLogUserPrompts`, `otelLogRawApiBodies`), `xhigh` effort tier (Opus 4.7), and `stats.pluginErrors` capture — see [CLI 2.1.121 options in SKILL.md](../SKILL.md) and [tools.md](./tools.md)
|
|
40
40
|
|
|
41
41
|
> **Behavior changes from upstream Claude CLI 2.1.121** (worth knowing if you set permission rules):
|
|
42
|
+
>
|
|
42
43
|
> - `--agent` / `--print` now enforce agent frontmatter `permissionMode`, `tools`, `disallowedTools` (was advisory). Affects `council` agent personas.
|
|
43
44
|
> - `Bash(find:*)` permission rule no longer auto-approves `find -exec` or `find -delete`. Add explicit rules if you depend on these.
|
|
44
45
|
> - `--dangerously-skip-permissions` also skips prompts for `.claude/skills/` directory. Treat with care.
|
|
@@ -47,7 +48,7 @@ Default engine. Long-running subprocess with streaming JSON I/O. Tested with Cla
|
|
|
47
48
|
```typescript
|
|
48
49
|
await manager.startSession({
|
|
49
50
|
name: 'claude-task',
|
|
50
|
-
engine: 'claude',
|
|
51
|
+
engine: 'claude', // default, can omit
|
|
51
52
|
model: 'opus',
|
|
52
53
|
cwd: '/project',
|
|
53
54
|
});
|
|
@@ -140,6 +141,7 @@ in print mode. Verified against `agy` **1.1.13**.
|
|
|
140
141
|
`gemini-3.1-pro has no "medium" effort (available: low, high)`. The adapter
|
|
141
142
|
passes the requested effort through rather than substituting a tier the caller
|
|
142
143
|
did not ask for; run `agy models` to see the tiers a slug actually exposes.
|
|
144
|
+
|
|
143
145
|
- Permission modes: `bypassPermissions` → `--dangerously-skip-permissions`,
|
|
144
146
|
`default` → `--sandbox` (terminal-restricted), and
|
|
145
147
|
`sandboxMode: 'read-only'` → `--mode plan` (takes precedence). Other modes
|
|
@@ -325,12 +327,12 @@ is not the answer on every engine. See "Stats & Monitoring" in `sessions.md`.
|
|
|
325
327
|
|
|
326
328
|
Team tools (`team_list`, `team_send`) operate on the same virtual-team layer for **every** engine: the "team" is the set of all active sessions managed by SessionManager.
|
|
327
329
|
|
|
328
|
-
| Engine
|
|
329
|
-
|
|
330
|
-
| Claude
|
|
331
|
-
| Codex
|
|
330
|
+
| Engine | `team_list` | `team_send` |
|
|
331
|
+
| ----------- | ------------------------------------------ | ------------------------------ |
|
|
332
|
+
| Claude | Lists other active SessionManager sessions | Routes via cross-session inbox |
|
|
333
|
+
| Codex | Lists other active SessionManager sessions | Routes via cross-session inbox |
|
|
332
334
|
| Antigravity | Lists other active SessionManager sessions | Routes via cross-session inbox |
|
|
333
|
-
| Cursor
|
|
335
|
+
| Cursor | Lists other active SessionManager sessions | Routes via cross-session inbox |
|
|
334
336
|
|
|
335
337
|
Messages are delivered via the inbox system — idle sessions receive immediately, busy sessions queue for later delivery.
|
|
336
338
|
|
|
@@ -349,12 +351,13 @@ If OpenClaw gateway is running, everything is automatic:
|
|
|
349
351
|
await manager.startSession({
|
|
350
352
|
name: 'task',
|
|
351
353
|
engine: 'claude',
|
|
352
|
-
model: 'openclaw',
|
|
354
|
+
model: 'openclaw', // gateway routes to your configured model
|
|
353
355
|
cwd: '/project',
|
|
354
356
|
});
|
|
355
357
|
```
|
|
356
358
|
|
|
357
359
|
What happens behind the scenes:
|
|
360
|
+
|
|
358
361
|
1. Plugin reads `~/.openclaw/openclaw.json` for gateway port + auth
|
|
359
362
|
2. Starts a local proxy server (random port, auto-managed)
|
|
360
363
|
3. Claude Code CLI sends Anthropic-format requests → proxy converts to OpenAI → gateway → any model
|
|
@@ -363,12 +366,12 @@ What happens behind the scenes:
|
|
|
363
366
|
|
|
364
367
|
Override with environment variables if needed:
|
|
365
368
|
|
|
366
|
-
| Variable
|
|
367
|
-
|
|
368
|
-
| `GATEWAY_URL`
|
|
369
|
-
| `GATEWAY_KEY`
|
|
370
|
-
| `GEMINI_API_KEY` | -
|
|
371
|
-
| `OPENAI_API_KEY` | -
|
|
369
|
+
| Variable | Default | Description |
|
|
370
|
+
| ---------------- | -------------------------------- | --------------------------------------------------- |
|
|
371
|
+
| `GATEWAY_URL` | Auto-detected from openclaw.json | Gateway endpoint (e.g. `http://127.0.0.1:18789/v1`) |
|
|
372
|
+
| `GATEWAY_KEY` | Auto-detected from openclaw.json | Gateway auth password/token |
|
|
373
|
+
| `GEMINI_API_KEY` | - | Direct Gemini API access (bypasses gateway) |
|
|
374
|
+
| `OPENAI_API_KEY` | - | Direct OpenAI API access (bypasses gateway) |
|
|
372
375
|
|
|
373
376
|
### Architecture
|
|
374
377
|
|
|
@@ -384,46 +387,47 @@ Claude Code CLI (Anthropic format)
|
|
|
384
387
|
Integrate **any** coding agent CLI without writing engine-specific code. You provide a `CustomEngineConfig` that maps your CLI's flags to OpenClaw session concepts.
|
|
385
388
|
|
|
386
389
|
Two protocol modes:
|
|
390
|
+
|
|
387
391
|
- **Persistent** (`persistent: true`) — long-running subprocess with stream-json I/O over stdin/stdout (like Claude Code)
|
|
388
392
|
- **One-shot** (`persistent: false`, default) — new process spawned per `send()` (like Codex/Antigravity)
|
|
389
393
|
|
|
390
394
|
### CustomEngineConfig
|
|
391
395
|
|
|
392
|
-
| Field
|
|
393
|
-
|
|
394
|
-
| `name`
|
|
395
|
-
| `bin`
|
|
396
|
-
| `binEnv`
|
|
397
|
-
| `persistent`
|
|
398
|
-
| `args`
|
|
399
|
-
| `permissionModes`
|
|
400
|
-
| `pricing`
|
|
401
|
-
| `contextWindow`
|
|
402
|
-
| `env`
|
|
403
|
-
| `sanitizePatterns` | string[] |
|
|
396
|
+
| Field | Type | Required | Description |
|
|
397
|
+
| ------------------ | -------- | -------- | ------------------------------------------------------------ |
|
|
398
|
+
| `name` | string | yes | Display name (used in logs, session IDs) |
|
|
399
|
+
| `bin` | string | yes | Binary path or command name |
|
|
400
|
+
| `binEnv` | string | | Env var name that overrides `bin` at runtime |
|
|
401
|
+
| `persistent` | boolean | | `true` = persistent subprocess, `false` = one-shot (default) |
|
|
402
|
+
| `args` | object | yes | CLI flag mappings (see below) |
|
|
403
|
+
| `permissionModes` | object | | Maps OpenClaw mode names to CLI-specific values |
|
|
404
|
+
| `pricing` | object | | `{ input, output, cached? }` per 1M tokens |
|
|
405
|
+
| `contextWindow` | number | | Context window size (default: 200,000) |
|
|
406
|
+
| `env` | object | | Extra environment variables for the CLI process |
|
|
407
|
+
| `sanitizePatterns` | string[] | | Regex patterns to redact from stderr |
|
|
404
408
|
|
|
405
409
|
### args field
|
|
406
410
|
|
|
407
|
-
| Key
|
|
408
|
-
|
|
409
|
-
| `print`
|
|
410
|
-
| `outputFormat`
|
|
411
|
-
| `outputFormatValue`
|
|
412
|
-
| `inputFormat`
|
|
413
|
-
| `inputFormatValue`
|
|
414
|
-
| `skipPermissions`
|
|
415
|
-
| `permissionMode`
|
|
416
|
-
| `model`
|
|
417
|
-
| `systemPrompt`
|
|
418
|
-
| `appendSystemPrompt`
|
|
419
|
-
| `maxTurns`
|
|
420
|
-
| `resume`
|
|
421
|
-
| `verbose`
|
|
422
|
-
| `replayUserMessages`
|
|
411
|
+
| Key | Example | Description |
|
|
412
|
+
| ------------------------ | ------------------------------ | ------------------------------------------ |
|
|
413
|
+
| `print` | `"-p"` | Non-interactive/print mode flag |
|
|
414
|
+
| `outputFormat` | `"--output-format"` | Output format flag |
|
|
415
|
+
| `outputFormatValue` | `"stream-json"` | Value for stream-json output |
|
|
416
|
+
| `inputFormat` | `"--input-format"` | Input format flag (persistent only) |
|
|
417
|
+
| `inputFormatValue` | `"stream-json"` | Value for stream-json input |
|
|
418
|
+
| `skipPermissions` | `"-y"` | Skip all permissions flag |
|
|
419
|
+
| `permissionMode` | `"--permission-mode"` | Permission mode flag |
|
|
420
|
+
| `model` | `"--model"` | Model selection flag |
|
|
421
|
+
| `systemPrompt` | `"--system-prompt"` | System prompt override flag |
|
|
422
|
+
| `appendSystemPrompt` | `"--append-system-prompt"` | Append system prompt flag |
|
|
423
|
+
| `maxTurns` | `"--max-turns"` | Max agent turns flag |
|
|
424
|
+
| `resume` | `"--resume"` | Session resume flag (persistent only) |
|
|
425
|
+
| `verbose` | `"--verbose"` | Verbose output flag |
|
|
426
|
+
| `replayUserMessages` | `"--replay-user-messages"` | Replay user messages (persistent only) |
|
|
423
427
|
| `includePartialMessages` | `"--include-partial-messages"` | Include partial messages (persistent only) |
|
|
424
|
-
| `effort`
|
|
425
|
-
| `workspace`
|
|
426
|
-
| `extra`
|
|
428
|
+
| `effort` | `"--effort"` | Effort level flag |
|
|
429
|
+
| `workspace` | `"--workspace"` | Workspace/cwd flag (one-shot only) |
|
|
430
|
+
| `extra` | `["--trust"]` | Additional static arguments |
|
|
427
431
|
|
|
428
432
|
### Example: Persistent mode (Claude Code-compatible CLI)
|
|
429
433
|
|
|
@@ -471,7 +475,7 @@ await manager.startSession({
|
|
|
471
475
|
customEngine: {
|
|
472
476
|
name: 'simple-agent',
|
|
473
477
|
bin: '/usr/local/bin/simple-agent',
|
|
474
|
-
persistent: false,
|
|
478
|
+
persistent: false, // default
|
|
475
479
|
args: {
|
|
476
480
|
print: '-p',
|
|
477
481
|
outputFormat: '--output-format',
|
|
@@ -505,11 +509,11 @@ await manager.startSession({
|
|
|
505
509
|
dangerouslySkipPermissions: true,
|
|
506
510
|
customEngine: {
|
|
507
511
|
name: 'antigravity',
|
|
508
|
-
bin: 'agy',
|
|
512
|
+
bin: 'agy', // install: curl -fsSL https://antigravity.google/cli/install.sh | bash
|
|
509
513
|
binEnv: 'AGY_BIN',
|
|
510
514
|
persistent: false,
|
|
511
515
|
args: {
|
|
512
|
-
print: '-p',
|
|
516
|
+
print: '-p', // single-prompt headless mode
|
|
513
517
|
skipPermissions: '--dangerously-skip-permissions',
|
|
514
518
|
workspace: '--add-dir',
|
|
515
519
|
// NOTE: agy 1.0.2 has NO --output-format flag — output is plain text only.
|
|
@@ -30,22 +30,22 @@ every engine passes through.
|
|
|
30
30
|
|
|
31
31
|
### Row schema
|
|
32
32
|
|
|
33
|
-
| Field
|
|
34
|
-
|
|
35
|
-
| `ts`
|
|
36
|
-
| `session`
|
|
37
|
-
| `engine`
|
|
38
|
-
| `model`
|
|
39
|
-
| `cwd`
|
|
40
|
-
| `turn`
|
|
41
|
-
| `tokensIn` / `tokensOut` / `cachedTokens` | **Per-turn deltas**, not session totals
|
|
42
|
-
| `costUsd`
|
|
43
|
-
| `tokensEstimated`
|
|
44
|
-
| `durationMs`
|
|
45
|
-
| `toolCalls` / `toolErrors`
|
|
46
|
-
| `ok`
|
|
47
|
-
| `error`
|
|
48
|
-
| `parent`
|
|
33
|
+
| Field | Meaning |
|
|
34
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
35
|
+
| `ts` | ISO timestamp of turn completion |
|
|
36
|
+
| `session` | SessionManager session name |
|
|
37
|
+
| `engine` | `claude` / `codex` / `codex-app` / `grok` / `opencode` / `agy` / `custom` |
|
|
38
|
+
| `model` | Configured model, or the engine's own reported model when none was set |
|
|
39
|
+
| `cwd` | Working directory the turn ran in |
|
|
40
|
+
| `turn` | 1-based turn index within the session |
|
|
41
|
+
| `tokensIn` / `tokensOut` / `cachedTokens` | **Per-turn deltas**, not session totals |
|
|
42
|
+
| `costUsd` | Per-turn delta in USD |
|
|
43
|
+
| `tokensEstimated` | `true` when the counts came from `estimateTokens()` (see below) |
|
|
44
|
+
| `durationMs` | Wall-clock for the turn |
|
|
45
|
+
| `toolCalls` / `toolErrors` | Per-turn deltas |
|
|
46
|
+
| `ok` | `false` for a turn that threw, or that the session's own `turnsSucceeded` counter did not count (see `sessions.md`). Falls back to "nothing was thrown" when the counter cannot be read |
|
|
47
|
+
| `error` | Failure text, truncated to 500 chars. Absent when the turn resolved but the engine did not count it as succeeded (an interrupted or non-SUCCESS turn), so a failed row does not always carry one |
|
|
48
|
+
| `parent` | council id / fanout id / autoloop run id, when the turn belongs to one |
|
|
49
49
|
|
|
50
50
|
Deltas rather than totals means summing a query window gives that window's spend
|
|
51
51
|
without double-counting.
|
|
@@ -98,11 +98,11 @@ Notes:
|
|
|
98
98
|
- The check is "has the cap been reached", not "would this turn exceed it" — a
|
|
99
99
|
turn's cost is unknown until it finishes, so the last allowed turn can overshoot.
|
|
100
100
|
Size the cap accordingly.
|
|
101
|
-
- A cap of `0` or a negative number means
|
|
101
|
+
- A cap of `0` or a negative number means _unset_, not _refuse everything_.
|
|
102
102
|
- Claude Code still receives `--max-budget-usd` as well: an in-CLI stop happens
|
|
103
103
|
earlier and therefore costs less than an after-the-fact refusal.
|
|
104
104
|
- `session_list` / `GET /session/list` expose `costUsd`, `budgetUsd` and
|
|
105
|
-
`budgetExhausted` so a stalled session shows
|
|
105
|
+
`budgetExhausted` so a stalled session shows _why_ it stopped taking turns.
|
|
106
106
|
|
|
107
107
|
## Accuracy: which engines report real usage
|
|
108
108
|
|
|
@@ -111,16 +111,16 @@ Where the engine reports usage, those counts are the engine's own. Where it does
|
|
|
111
111
|
not, the wrapper falls back to `estimateTokens()` (characters ÷ 4) and the row is
|
|
112
112
|
flagged `tokensEstimated: true`; the CLI marks those costs with a trailing `~`.
|
|
113
113
|
|
|
114
|
-
| Engine
|
|
115
|
-
|
|
116
|
-
| `claude`
|
|
117
|
-
| `codex`
|
|
118
|
-
| `codex-app`
|
|
119
|
-
| `grok`
|
|
120
|
-
| `cursor` (legacy) | Engine-reported when the stream carries `usage`, else estimated
|
|
121
|
-
| `opencode`
|
|
122
|
-
| `agy`
|
|
123
|
-
| `custom`
|
|
114
|
+
| Engine | Token counts |
|
|
115
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
+
| `claude` | Engine-reported |
|
|
117
|
+
| `codex` | Engine-reported |
|
|
118
|
+
| `codex-app` | Engine-reported |
|
|
119
|
+
| `grok` | Engine-reported — and so is the **cost**: this engine reports `total_cost_usd`, which the wrapper passes through instead of pricing tokens from the registry, so registry drift cannot affect a grok row |
|
|
120
|
+
| `cursor` (legacy) | Engine-reported when the stream carries `usage`, else estimated |
|
|
121
|
+
| `opencode` | Engine-reported when the run JSON carries `tokens`, else estimated |
|
|
122
|
+
| `agy` | Engine-reported when the result event carries usage, else estimated |
|
|
123
|
+
| `custom` | Depends on the CLI; estimated when it emits no usage |
|
|
124
124
|
|
|
125
125
|
So on an estimating engine the cap is best-effort. It will stop a runaway session;
|
|
126
126
|
it is not an accounting guarantee, and it is not a substitute for the spend limits
|
|
@@ -130,3 +130,63 @@ Cost figures are also only as good as the pricing table: a model missing from
|
|
|
130
130
|
`models.ts` prices at its family default, and subscription plans (Claude Max,
|
|
131
131
|
ChatGPT Pro) bill nothing per token while the ledger still reports the API-rate
|
|
132
132
|
equivalent. Read `costUsd` as "what this would cost at API rates".
|
|
133
|
+
|
|
134
|
+
## `ok` vs `verified` (6.0.0)
|
|
135
|
+
|
|
136
|
+
A row now carries two different judgements, and conflating them is the mistake
|
|
137
|
+
this section exists to prevent.
|
|
138
|
+
|
|
139
|
+
- **`ok`** — the engine's own terminal verdict for that turn. Codex fails a turn
|
|
140
|
+
that emits `turn.failed` while exiting 0; gemini succeeds on exit 53. It is a
|
|
141
|
+
careful signal, but it is the engine talking about itself.
|
|
142
|
+
- **`verified`** — an acceptance contract ran against the work and every required
|
|
143
|
+
check passed. That is the runtime's own measurement.
|
|
144
|
+
|
|
145
|
+
Three states, not two:
|
|
146
|
+
|
|
147
|
+
| `verified` | Means | CLI column |
|
|
148
|
+
| ---------- | --------------------------------------------------- | ---------- |
|
|
149
|
+
| `true` | A contract ran and passed | `yes` |
|
|
150
|
+
| `false` | A contract ran and a required check failed | `NO` |
|
|
151
|
+
| absent | **No contract was declared. Nothing checked this.** | `—` |
|
|
152
|
+
|
|
153
|
+
Absent is not false. An unchecked run is not a failed one, and reading it as
|
|
154
|
+
either would make the ledger useless for the thing it is for.
|
|
155
|
+
|
|
156
|
+
### Where the verdict comes from
|
|
157
|
+
|
|
158
|
+
`verified` is **not written at turn time**, deliberately. The turns that produce
|
|
159
|
+
the work all finish before the verifier that judges it, so stamping a verdict on
|
|
160
|
+
them as they are written would be inventing one. It is joined in at read time
|
|
161
|
+
from the run record via the row's `parent`, by `annotateVerdicts()`.
|
|
162
|
+
|
|
163
|
+
Two consequences worth knowing:
|
|
164
|
+
|
|
165
|
+
- A raw `runs/*.jsonl` line usually has no `verified` field. Read through
|
|
166
|
+
`clawo runs` / `GET /runs` / `getRunLedger()` to get the join.
|
|
167
|
+
- Filtering on `--verified` happens _after_ the join. Pushing the filter into the
|
|
168
|
+
ledger read would match on a field no row carries yet and return nothing.
|
|
169
|
+
|
|
170
|
+
### Other new row fields
|
|
171
|
+
|
|
172
|
+
| Field | Source |
|
|
173
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
174
|
+
| `evidenceId`, `contractId` | Joined from the run record |
|
|
175
|
+
| `nodeKind` | The kernel node the turn belonged to (`agent`, `council`, `verifier`, …) |
|
|
176
|
+
| `repoLang` | Detected from a manifest (`package.json`, `pyproject.toml`, `go.mod`, …). Never guessed — a wrong label would corrupt the comparison the field exists to enable |
|
|
177
|
+
| `taskKind` | Caller-declared only. Never inferred from the prompt |
|
|
178
|
+
|
|
179
|
+
All are optional and absent on rows written before 6.0.0. The reader already
|
|
180
|
+
skips unknown and missing keys, so old shards stay readable; nothing backfills
|
|
181
|
+
them, because we cannot know retroactively.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
clawo runs --since 7d --verified # only turns whose contract passed
|
|
185
|
+
clawo runs --since 7d --refuted # only turns whose contract failed
|
|
186
|
+
clawo runs --parent wf-abc123 # every turn of one workflow run
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Related
|
|
190
|
+
|
|
191
|
+
- [`verification.md`](./verification.md) — what a contract is and how a verdict is produced
|
|
192
|
+
- [`workflow.md`](./workflow.md) — where run records live
|
|
@@ -11,13 +11,13 @@ Both modes share the same wire protocol; the difference is how a "new conversati
|
|
|
11
11
|
|
|
12
12
|
## Endpoint
|
|
13
13
|
|
|
14
|
-
|
|
|
15
|
-
|
|
16
|
-
| **URL**
|
|
17
|
-
| **Models endpoint**
|
|
18
|
-
| **Inspection endpoint** | `GET /v1/sessions` (lists active openai-compat sessions with caching stats)
|
|
19
|
-
| **Auth**
|
|
20
|
-
| **Wire format**
|
|
14
|
+
| | |
|
|
15
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| **URL** | `http://127.0.0.1:18796/v1/chat/completions` |
|
|
17
|
+
| **Models endpoint** | `GET /v1/models` |
|
|
18
|
+
| **Inspection endpoint** | `GET /v1/sessions` (lists active openai-compat sessions with caching stats) |
|
|
19
|
+
| **Auth** | Bearer token via `Authorization: Bearer $OPENCLAW_SERVER_TOKEN` (set the env var to enable; otherwise no auth and the server is loopback-only) |
|
|
20
|
+
| **Wire format** | OpenAI Chat Completions, both streaming (SSE) and non-streaming |
|
|
21
21
|
|
|
22
22
|
## Session keying
|
|
23
23
|
|
|
@@ -56,20 +56,20 @@ Without this flag, those frontends would silently continue the previous CLI sess
|
|
|
56
56
|
|
|
57
57
|
The env var is read on every request, so ops can flip it via `launchctl setenv` (or equivalent) without restarting the server.
|
|
58
58
|
|
|
59
|
-
| Mode
|
|
60
|
-
|
|
61
|
-
| **Default**
|
|
59
|
+
| Mode | Best for | New-conversation signals |
|
|
60
|
+
| ----------------- | ----------------------------------------------------------- | --------------------------------------------------- |
|
|
61
|
+
| **Default** | OpenClaw main agent, cron jobs, subagents, scripted clients | `X-Session-Reset: 1` only |
|
|
62
62
|
| **`HEURISTIC=1`** | ChatGPT-Next-Web, Open WebUI, LobeChat, data labeling tools | `X-Session-Reset: 1` **and** `[system, user]` shape |
|
|
63
63
|
|
|
64
64
|
## Status webhook
|
|
65
65
|
|
|
66
66
|
When `OPENAI_COMPAT_STATUS_URL` is set (full HTTP URL), each chat completion sends best-effort `POST` requests with `Content-Type: application/json` and body:
|
|
67
67
|
|
|
68
|
-
| Field
|
|
69
|
-
|
|
70
|
-
| `state`
|
|
71
|
-
| `activity` | string
|
|
72
|
-
| `tool`
|
|
68
|
+
| Field | Type | Meaning |
|
|
69
|
+
| ---------- | -------------- | ----------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `state` | string | `thinking` (turn started), `working` (a tool is running), or `idle` (turn finished or stream closed). |
|
|
71
|
+
| `activity` | string | Short human-readable line, e.g. `Processing request...`, `Reading: foo.ts`, `Running: npm test...`. |
|
|
72
|
+
| `tool` | string \| null | Tool name when `state === working`, otherwise `null`. |
|
|
73
73
|
|
|
74
74
|
Failures are ignored (no retries). Use this from a small local HTTP handler that forwards status into your webchat UI.
|
|
75
75
|
|
|
@@ -78,17 +78,17 @@ Failures are ignored (no retries). Use this from a small local HTTP handler that
|
|
|
78
78
|
When the request carries `tools`, the schemas have to reach the CLI somehow. Which
|
|
79
79
|
mechanism is used depends on whether the engine keeps the conversation itself.
|
|
80
80
|
|
|
81
|
-
| Engine
|
|
82
|
-
|
|
83
|
-
| `claude`
|
|
84
|
-
| `codex`, `codex-app`, `agy`, `opencode`, `grok` | Full schema block prepended to the message
|
|
85
|
-
| `gemini`, one-shot `custom`
|
|
81
|
+
| Engine | Turn 1 | Later turns |
|
|
82
|
+
| ----------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
83
|
+
| `claude` | Schemas go into the session system prompt (`--system-prompt`) | Nothing injected — the system prompt persists |
|
|
84
|
+
| `codex`, `codex-app`, `agy`, `opencode`, `grok` | Full schema block prepended to the message | A short reminder of the calling convention, no schemas — but only once the conversation id has been captured; until then the full block is sent again |
|
|
85
|
+
| `gemini`, one-shot `custom` | Full schema block prepended to the message | Full schema block again — these have no resume surface, so nothing persists between sends |
|
|
86
86
|
|
|
87
87
|
The middle row is the one worth understanding. Those engines resume a conversation by id, so
|
|
88
88
|
everything injected stays in the transcript. Re-sending the full block each turn
|
|
89
89
|
grows the prompt without bound — a 54-tool block runs to roughly 17k tokens, so a
|
|
90
90
|
handful of turns is enough to overflow the context window mid-loop and fail the
|
|
91
|
-
run outright. Sending
|
|
91
|
+
run outright. Sending _nothing_ on resume turns is not the answer either: the
|
|
92
92
|
block also carries the "emit a tool call, do not carry out the work yourself"
|
|
93
93
|
framing, and without it the CLI starts doing the work directly.
|
|
94
94
|
|
|
@@ -116,16 +116,16 @@ does change mid-conversation.
|
|
|
116
116
|
|
|
117
117
|
## Environment variables
|
|
118
118
|
|
|
119
|
-
| Variable
|
|
120
|
-
|
|
121
|
-
| `OPENCLAW_SERVER_TOKEN`
|
|
122
|
-
| `OPENCLAW_RATE_LIMIT`
|
|
123
|
-
| `OPENCLAW_CORS_ORIGINS`
|
|
124
|
-
| `OPENAI_COMPAT_NEW_CONVO_HEURISTIC` | (unset)
|
|
125
|
-
| `OPENAI_COMPAT_TOOLS_PER_MESSAGE`
|
|
126
|
-
| `OPENAI_COMPAT_STATUS_URL`
|
|
127
|
-
| `OPENCLAW_SERVE_MAX_SESSIONS`
|
|
128
|
-
| `OPENCLAW_SERVE_TTL_MINUTES`
|
|
119
|
+
| Variable | Default | Purpose |
|
|
120
|
+
| ----------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `OPENCLAW_SERVER_TOKEN` | (unset) | Bearer token for HTTP auth. Set to enable; written to `~/.openclaw/server-token` for the CLI. |
|
|
122
|
+
| `OPENCLAW_RATE_LIMIT` | `300` | Max requests per IP per 60-second sliding window. |
|
|
123
|
+
| `OPENCLAW_CORS_ORIGINS` | (loopback only) | Set to `*` to allow all origins (the `/v1/*` paths already do this). |
|
|
124
|
+
| `OPENAI_COMPAT_NEW_CONVO_HEURISTIC` | (unset) | Set to `1` to enable webchat mode (see above). |
|
|
125
|
+
| `OPENAI_COMPAT_TOOLS_PER_MESSAGE` | (unset) | Set to `1` to re-send the full tool schemas on every turn (see [Tool definitions](#tool-definitions-and-where-they-live)). Needed only when the tool set changes mid-conversation; costs per-turn prompt growth. |
|
|
126
|
+
| `OPENAI_COMPAT_STATUS_URL` | (unset) | If set, the bridge POSTs JSON status updates to this URL (fire-and-forget, 2s timeout). See [Status webhook](#status-webhook). |
|
|
127
|
+
| `OPENCLAW_SERVE_MAX_SESSIONS` | `32` | Max concurrent OpenAI-compat sessions in serve mode. Bumped from the in-plugin default of 5 because each distinct caller now gets its own `sys-<hash>` session. |
|
|
128
|
+
| `OPENCLAW_SERVE_TTL_MINUTES` | `60` | Idle TTL for OpenAI-compat sessions in serve mode. Idle sessions are reaped by a 60s background loop; persisted disk registry is kept for 7 days so a returning caller is auto-resumed. |
|
|
129
129
|
|
|
130
130
|
## Inspection: `GET /v1/sessions`
|
|
131
131
|
|
|
@@ -235,14 +235,14 @@ Errors use the OpenAI error envelope:
|
|
|
235
235
|
{ "error": { "message": "...", "type": "invalid_request_error" } }
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
| Status | When
|
|
239
|
-
|
|
240
|
-
| 400
|
|
241
|
-
| 401
|
|
242
|
-
| 415
|
|
243
|
-
| 429
|
|
244
|
-
| 503
|
|
245
|
-
| 500
|
|
238
|
+
| Status | When |
|
|
239
|
+
| ------ | ---------------------------------------------------------------------- |
|
|
240
|
+
| 400 | `messages` empty/missing, no user message, invalid `max_tokens` |
|
|
241
|
+
| 401 | Missing or wrong bearer token (when auth enabled) |
|
|
242
|
+
| 415 | POST without `Content-Type: application/json` |
|
|
243
|
+
| 429 | Rate limited (`OPENCLAW_RATE_LIMIT` exceeded) |
|
|
244
|
+
| 503 | Failed to start a new session (model unavailable, CLI crashed at boot) |
|
|
245
|
+
| 500 | Mid-turn failure |
|
|
246
246
|
|
|
247
247
|
## Related
|
|
248
248
|
|
|
@@ -16,7 +16,7 @@ start() → send() → send() → ... → stop()
|
|
|
16
16
|
const info = await manager.startSession({
|
|
17
17
|
name: 'my-task',
|
|
18
18
|
cwd: '/path/to/project',
|
|
19
|
-
model: 'opus',
|
|
19
|
+
model: 'opus', // alias or full name
|
|
20
20
|
permissionMode: 'acceptEdits',
|
|
21
21
|
effort: 'high',
|
|
22
22
|
allowedTools: ['Bash', 'Read', 'Edit', 'Write'],
|
|
@@ -27,24 +27,24 @@ const info = await manager.startSession({
|
|
|
27
27
|
|
|
28
28
|
Key options:
|
|
29
29
|
|
|
30
|
-
| Option
|
|
31
|
-
|
|
32
|
-
| `engine`
|
|
33
|
-
| `model`
|
|
34
|
-
| `permissionMode`
|
|
35
|
-
| `effort`
|
|
36
|
-
| `bare`
|
|
37
|
-
| `worktree`
|
|
38
|
-
| `appendSystemPrompt` | Append custom instructions to the system prompt
|
|
30
|
+
| Option | Description |
|
|
31
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `engine` | `'claude'` (default), `'codex'`, `'codex-app'`, `'agy'`, `'grok'`, `'opencode'`, or `'custom'` — see [Multi-Engine](./multi-engine.md) |
|
|
33
|
+
| `model` | Model alias (`fable`, `opus`, `sonnet`, `haiku`, `agy-pro`) or full name |
|
|
34
|
+
| `permissionMode` | `acceptEdits`, `bypassPermissions`, `plan`, `auto`, `manual`, `dontAsk` (`default` = legacy alias for `manual`) |
|
|
35
|
+
| `effort` | `low`, `medium`, `high`, `max`, `auto` |
|
|
36
|
+
| `bare` | Skip hooks, LSP, auto-memory, CLAUDE.md |
|
|
37
|
+
| `worktree` | Run in isolated git worktree |
|
|
38
|
+
| `appendSystemPrompt` | Append custom instructions to the system prompt |
|
|
39
39
|
|
|
40
40
|
### Sending Messages
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
43
|
const result = await manager.sendMessage('my-task', 'Fix the auth bug', {
|
|
44
|
-
effort: 'high',
|
|
45
|
-
plan: true,
|
|
46
|
-
timeout: 600_000,
|
|
47
|
-
onChunk: (text) => process.stdout.write(text),
|
|
44
|
+
effort: 'high', // override effort for this message
|
|
45
|
+
plan: true, // enter plan mode
|
|
46
|
+
timeout: 600_000, // 10 min timeout
|
|
47
|
+
onChunk: (text) => process.stdout.write(text), // streaming
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
console.log(result.output);
|
|
@@ -100,8 +100,8 @@ Mid-turn and thread control via Codex 0.137 v2 RPCs, surfaced as tools: `codex_i
|
|
|
100
100
|
Switch models mid-conversation. The session restarts with `--resume` to preserve history:
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
|
-
await manager.switchModel('my-task', 'haiku');
|
|
104
|
-
await manager.switchModel('my-task', 'opus');
|
|
103
|
+
await manager.switchModel('my-task', 'haiku'); // fast model for simple tasks
|
|
104
|
+
await manager.switchModel('my-task', 'opus'); // back to powerful model
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
### Tool Management
|
|
@@ -111,11 +111,11 @@ Add/remove tool permissions at runtime:
|
|
|
111
111
|
```typescript
|
|
112
112
|
await manager.updateTools('my-task', {
|
|
113
113
|
allowedTools: ['Bash', 'Read'],
|
|
114
|
-
merge: true,
|
|
114
|
+
merge: true, // add to existing list
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
await manager.updateTools('my-task', {
|
|
118
|
-
removeTools: ['Bash'],
|
|
118
|
+
removeTools: ['Bash'], // revoke Bash access
|
|
119
119
|
});
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -152,7 +152,7 @@ against `turns`.
|
|
|
152
152
|
|
|
153
153
|
Which outcome counts as a success is the engine's own verdict, not the exit
|
|
154
154
|
code's: `codex` fails a turn that emits `turn.failed` while exiting 0, `agy`
|
|
155
|
-
requires a `SUCCESS` status
|
|
155
|
+
requires a `SUCCESS` status _and_ a zero exit (it can report success and then die
|
|
156
156
|
in cleanup), `gemini` succeeds on exit 53 because its turn limit resolves,
|
|
157
157
|
`codex-app` requires `status: 'completed'` so a turn cancelled through
|
|
158
158
|
`interrupt()` does not count, and `opencode` refuses a turn on purpose when
|
|
@@ -207,15 +207,15 @@ Session stats are returned by `getStats()` and surfaced through `coding_session_
|
|
|
207
207
|
|
|
208
208
|
Fields added in plugin v2.13.0 (Claude CLI 2.1.111):
|
|
209
209
|
|
|
210
|
-
| Field
|
|
211
|
-
|
|
212
|
-
| `retries`
|
|
213
|
-
| `lastRetryError` | string \| undefined | Error message from the most recent retry (if any)
|
|
210
|
+
| Field | Type | Description |
|
|
211
|
+
| ---------------- | ------------------- | -------------------------------------------------- |
|
|
212
|
+
| `retries` | number | Total API retries that occurred during the session |
|
|
213
|
+
| `lastRetryError` | string \| undefined | Error message from the most recent retry (if any) |
|
|
214
214
|
|
|
215
215
|
Fields added in plugin v2.14.0 (Claude CLI 2.1.121):
|
|
216
216
|
|
|
217
|
-
| Field
|
|
218
|
-
|
|
217
|
+
| Field | Type | Description |
|
|
218
|
+
| -------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
219
219
|
| `pluginErrors` | `Array<{plugin, reason}>` \| undefined | Plugins that failed to load due to unmet dependencies, captured from the `system/init` event. `undefined` when no plugin errors occurred. |
|
|
220
220
|
|
|
221
221
|
### `system/api_retry` events
|
|
@@ -238,3 +238,21 @@ All session engine classes expose an optional `pid` readonly property, providing
|
|
|
238
238
|
const session = manager.getSession('my-session');
|
|
239
239
|
console.log(session.pid); // e.g., 12345 or undefined
|
|
240
240
|
```
|
|
241
|
+
|
|
242
|
+
## Verifying what a session did (6.0.0)
|
|
243
|
+
|
|
244
|
+
A plain session leaves no verdict — it ran, and nothing checked the result. To
|
|
245
|
+
check it, hand `verify_run` a contract and the directory:
|
|
246
|
+
|
|
247
|
+
```jsonc
|
|
248
|
+
verify_run({
|
|
249
|
+
cwd: "/repo",
|
|
250
|
+
contract: { checks: [{ type: "command", cmd: "npm", args: ["test"] }] }
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
For work that should be checked as part of running it, use a workflow instead —
|
|
255
|
+
see [`workflow.md`](./workflow.md).
|
|
256
|
+
|
|
257
|
+
`SendOptions` also gained `nodeKind` and `taskKind`, both stamped onto the run
|
|
258
|
+
ledger row. `taskKind` is caller-declared and never inferred from the prompt.
|