car-runtime 0.57.4 → 0.58.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 CHANGED
@@ -138,13 +138,19 @@ main({
138
138
 
139
139
  `runAgent(config, goal)` is also exported for embedding and tests. Pass one
140
140
  long-lived runtime as `config.runtime` to reuse its daemon connection across
141
- runs; otherwise the loop creates and closes a runtime for each call. `serveChat`
141
+ runs; otherwise the loop creates and closes a runtime for each call. The loop
142
+ registers its tools and policies once per daemon session: when that runtime is
143
+ closed and reused, or reconnects after a daemon restart (`connectionEpoch()`
144
+ changes), the next run or `serveChat` chat turn registers them again on the
145
+ new session. `serveChat`
142
146
  uses the same option and returns the runtime it is serving on. Its
143
147
  `AgentOutcome.run_id` is the durable key accepted by `runsGetTrace`; it is
144
148
  `null` when an older daemon has no `runs.*` surface or did not acknowledge the
145
149
  start. `runChatTurn` keeps returning bare text by default; pass
146
150
  `{ includeRunId: true }` as its final options argument to receive
147
- `{ text, run_id }`. `main(config)` provides `--task`, `--json`, and `--serve`.
151
+ `{ text, run_id }`. It registers its tool schemas and `config.policies` on the
152
+ runtime itself, so tool-using turns need no earlier `serveChat` call.
153
+ `main(config)` provides `--task`, `--json`, and `--serve`.
148
154
  The loop adds `finish`, statically verifies every proposal before execution,
149
155
  preserves tool-call/result IDs, honors per-schema `timeoutMs`, traces task and
150
156
  chat runs, and uses request-shaped inference with prompt-cache breakpoints.
package/agent-loop.d.ts CHANGED
@@ -15,6 +15,7 @@ export function runAgent(
15
15
  options?: AgentLoopOptions,
16
16
  ): Promise<AgentOutcome>;
17
17
 
18
+ /** Registers the config's tools and policies on `runtime` before every turn whose `connectionEpoch()` is new. */
18
19
  export function runChatTurn(
19
20
  runtime: CarRuntime,
20
21
  config: AgentLoopConfig,
@@ -27,6 +28,7 @@ export function runChatTurn(
27
28
  options: AgentChatTurnOptions & { includeRunId: true },
28
29
  ): Promise<AgentChatTurnResult>;
29
30
 
31
+ /** Registers the config's tools and policies on `runtime` before every turn whose `connectionEpoch()` is new. */
30
32
  export function runChatTurn(
31
33
  runtime: CarRuntime,
32
34
  config: AgentLoopConfig,
package/agent-loop.js CHANGED
@@ -121,10 +121,13 @@ function makeToolCallback(tools, toolTimeouts = {}, onFinish = null) {
121
121
  // for the entire run;
122
122
  // 2. the LAST tool definition — so identity + every tool schema forms ONE
123
123
  // cached prefix, the largest genuinely stable block this loop has;
124
- // 3. the LAST message — the moving breakpoint. Anthropic serves the longest
125
- // cached prefix it can find, so each turn writes only its own delta and
126
- // READS everything the previous turn wrote. That is what turns quadratic
127
- // re-billing into one write plus N cheap reads.
124
+ // 3. the SECOND-TO-LAST message — the moving breakpoint. Anthropic serves
125
+ // the longest cached prefix it can find, so each turn writes only its own
126
+ // delta and READS everything the previous turn wrote. That is what turns
127
+ // quadratic re-billing into one write plus N cheap reads. Not the last
128
+ // message, because CAR's own assistant loop appends per-turn state to the
129
+ // newest message that the next request re-sends without it; the newest
130
+ // message is billed once at the input rate before it joins the prefix.
128
131
  // The 4th breakpoint is deliberately left unused. It would buy a
129
132
  // `context_stable_prefix` split of the system prompt, which helps only when the
130
133
  // system prompt has a volatile tail; ours has none, so splitting it would
@@ -142,29 +145,48 @@ function makeToolCallback(tools, toolTimeouts = {}, onFinish = null) {
142
145
  // Remember what this package installed so a caller-owned runtime can serve
143
146
  // hundreds of sequential runs without appending the same policy hundreds of
144
147
  // times. Weak keys ensure this bookkeeping never keeps a runtime/socket alive.
148
+ //
149
+ // The bookkeeping is keyed to the connection epoch, not just the object: after
150
+ // `close()`, or a daemon restart the client silently reconnects through, the
151
+ // same runtime talks to a blank daemon session, and remembering the old one's
152
+ // registrations would leave the new session without its tools and policies.
145
153
  const RUNTIME_REGISTRATION = new WeakMap();
154
+ const MAX_SETUP_ATTEMPTS = 3;
155
+
156
+ // A runtime without `connectionEpoch` (a hand-built runtime-shaped object)
157
+ // reports one epoch forever, which is the object-scoped behavior.
158
+ async function connectionEpoch(rt) {
159
+ return typeof rt.connectionEpoch === 'function' ? rt.connectionEpoch() : 0;
160
+ }
146
161
 
147
162
  async function prepareRuntime(rt, toolSchemas, policies) {
148
- let state = RUNTIME_REGISTRATION.get(rt);
149
- if (!state) {
150
- state = { basicsAttempted: false, tools: new Set(), policies: new Set() };
151
- RUNTIME_REGISTRATION.set(rt, state);
152
- }
153
- if (!state.basicsAttempted) {
154
- state.basicsAttempted = true;
155
- try { await rt.registerAgentBasics(); } catch { /* unsupported on this daemon — fine */ }
156
- }
157
- for (const schema of toolSchemas) {
158
- if (state.tools.has(schema.name)) continue;
159
- await rt.registerTool(schema.name);
160
- state.tools.add(schema.name);
161
- }
162
- for (const policy of policies || []) {
163
- const fingerprint = JSON.stringify(policy);
164
- if (state.policies.has(fingerprint)) continue;
165
- await rt.registerPolicy(...policy);
166
- state.policies.add(fingerprint);
163
+ for (let attempt = 0; attempt < MAX_SETUP_ATTEMPTS; attempt++) {
164
+ const epoch = await connectionEpoch(rt);
165
+ let state = RUNTIME_REGISTRATION.get(rt);
166
+ if (!state || state.epoch !== epoch) {
167
+ state = { epoch, basicsAttempted: false, tools: new Set(), policies: new Set() };
168
+ RUNTIME_REGISTRATION.set(rt, state);
169
+ }
170
+ if (!state.basicsAttempted) {
171
+ state.basicsAttempted = true;
172
+ try { await rt.registerAgentBasics(); } catch { /* unsupported on this daemon — fine */ }
173
+ }
174
+ for (const schema of toolSchemas) {
175
+ if (state.tools.has(schema.name)) continue;
176
+ await rt.registerTool(schema.name);
177
+ state.tools.add(schema.name);
178
+ }
179
+ for (const policy of policies || []) {
180
+ const fingerprint = JSON.stringify(policy);
181
+ if (state.policies.has(fingerprint)) continue;
182
+ await rt.registerPolicy(...policy);
183
+ state.policies.add(fingerprint);
184
+ }
185
+ // A reconnect partway through setup put the later registrations on a new
186
+ // session and the earlier ones on a dead one; start that session over.
187
+ if (await connectionEpoch(rt) === epoch) return;
167
188
  }
189
+ throw new Error(`daemon connection reset during agent setup ${MAX_SETUP_ATTEMPTS} times in a row`);
168
190
  }
169
191
 
170
192
  async function closeRuntime(rt) {
@@ -449,6 +471,10 @@ async function runChatTurn(
449
471
  options = {},
450
472
  ) {
451
473
  const { executeProposal } = runtimeApi();
474
+ // A --serve agent outlives daemon restarts and the reconnects they cause, so
475
+ // each turn makes sure the session it lands on has the tools and policies.
476
+ // One epoch read when nothing changed.
477
+ await prepareRuntime(rt, toolSchemas, config.policies);
452
478
  const turnsCap = config.maxTurns ?? 8;
453
479
  const selectedModel = typeof requestedModel === 'string' && requestedModel.trim()
454
480
  ? requestedModel
@@ -2587,7 +2587,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
2587
2587
  "generated": false,
2588
2588
  "name": "fleet_worker_set"
2589
2589
  },
2590
- "role": "operator"
2590
+ "role": "operator_only"
2591
2591
  },
2592
2592
  {
2593
2593
  "javascript": {
@@ -2626,7 +2626,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
2626
2626
  "generated": true,
2627
2627
  "name": "goal_clear"
2628
2628
  },
2629
- "role": "operator"
2629
+ "role": "agent"
2630
2630
  },
2631
2631
  {
2632
2632
  "javascript": {
@@ -2639,7 +2639,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
2639
2639
  "generated": true,
2640
2640
  "name": "goal_set"
2641
2641
  },
2642
- "role": "operator"
2642
+ "role": "agent"
2643
2643
  },
2644
2644
  {
2645
2645
  "javascript": {
@@ -2652,7 +2652,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
2652
2652
  "generated": true,
2653
2653
  "name": "goal_status"
2654
2654
  },
2655
- "role": "operator"
2655
+ "role": "agent"
2656
2656
  },
2657
2657
  {
2658
2658
  "javascript": {
@@ -2665,7 +2665,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
2665
2665
  "generated": true,
2666
2666
  "name": "goal_suggest"
2667
2667
  },
2668
- "role": "operator"
2668
+ "role": "agent"
2669
2669
  },
2670
2670
  {
2671
2671
  "javascript": {
@@ -3055,7 +3055,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3055
3055
  "generated": true,
3056
3056
  "name": "lattice_claim"
3057
3057
  },
3058
- "role": "operator"
3058
+ "role": "owner"
3059
3059
  },
3060
3060
  {
3061
3061
  "javascript": {
@@ -3068,7 +3068,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3068
3068
  "generated": true,
3069
3069
  "name": "lattice_claims"
3070
3070
  },
3071
- "role": "operator"
3071
+ "role": "owner"
3072
3072
  },
3073
3073
  {
3074
3074
  "javascript": {
@@ -3198,7 +3198,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3198
3198
  "generated": true,
3199
3199
  "name": "lattice_find"
3200
3200
  },
3201
- "role": "operator"
3201
+ "role": "owner"
3202
3202
  },
3203
3203
  {
3204
3204
  "javascript": {
@@ -3211,7 +3211,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3211
3211
  "generated": true,
3212
3212
  "name": "lattice_join"
3213
3213
  },
3214
- "role": "operator"
3214
+ "role": "owner"
3215
3215
  },
3216
3216
  {
3217
3217
  "javascript": {
@@ -3315,7 +3315,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3315
3315
  "generated": true,
3316
3316
  "name": "lattice_leave"
3317
3317
  },
3318
- "role": "operator"
3318
+ "role": "owner"
3319
3319
  },
3320
3320
  {
3321
3321
  "javascript": {
@@ -3328,7 +3328,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3328
3328
  "generated": true,
3329
3329
  "name": "lattice_nodes"
3330
3330
  },
3331
- "role": "operator"
3331
+ "role": "owner"
3332
3332
  },
3333
3333
  {
3334
3334
  "javascript": {
@@ -3445,7 +3445,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3445
3445
  "generated": true,
3446
3446
  "name": "lattice_release"
3447
3447
  },
3448
- "role": "operator"
3448
+ "role": "owner"
3449
3449
  },
3450
3450
  {
3451
3451
  "javascript": {
@@ -3484,7 +3484,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
3484
3484
  "generated": true,
3485
3485
  "name": "lattice_wait"
3486
3486
  },
3487
- "role": "operator"
3487
+ "role": "owner"
3488
3488
  },
3489
3489
  {
3490
3490
  "javascript": {
@@ -4576,7 +4576,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4576
4576
  "generated": true,
4577
4577
  "name": "multiplayer_abandon"
4578
4578
  },
4579
- "role": "operator"
4579
+ "role": "operator_only"
4580
4580
  },
4581
4581
  {
4582
4582
  "javascript": {
@@ -4589,7 +4589,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4589
4589
  "generated": true,
4590
4590
  "name": "multiplayer_get"
4591
4591
  },
4592
- "role": "operator"
4592
+ "role": "operator_only"
4593
4593
  },
4594
4594
  {
4595
4595
  "javascript": {
@@ -4602,7 +4602,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4602
4602
  "generated": true,
4603
4603
  "name": "multiplayer_list"
4604
4604
  },
4605
- "role": "operator"
4605
+ "role": "operator_only"
4606
4606
  },
4607
4607
  {
4608
4608
  "javascript": {
@@ -4615,7 +4615,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4615
4615
  "generated": true,
4616
4616
  "name": "multiplayer_merge_check"
4617
4617
  },
4618
- "role": "operator"
4618
+ "role": "operator_only"
4619
4619
  },
4620
4620
  {
4621
4621
  "javascript": {
@@ -4628,7 +4628,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4628
4628
  "generated": true,
4629
4629
  "name": "multiplayer_publish"
4630
4630
  },
4631
- "role": "operator"
4631
+ "role": "operator_only"
4632
4632
  },
4633
4633
  {
4634
4634
  "javascript": {
@@ -4641,7 +4641,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4641
4641
  "generated": true,
4642
4642
  "name": "multiplayer_start_stage"
4643
4643
  },
4644
- "role": "operator"
4644
+ "role": "operator_only"
4645
4645
  },
4646
4646
  {
4647
4647
  "javascript": {
@@ -4654,7 +4654,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4654
4654
  "generated": true,
4655
4655
  "name": "multiplayer_submit_stage"
4656
4656
  },
4657
- "role": "operator"
4657
+ "role": "operator_only"
4658
4658
  },
4659
4659
  {
4660
4660
  "javascript": {
@@ -4734,6 +4734,58 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
4734
4734
  },
4735
4735
  "role": "operator"
4736
4736
  },
4737
+ {
4738
+ "javascript": {
4739
+ "generated": true,
4740
+ "name": "openaiCodexAuthCancel"
4741
+ },
4742
+ "method": "openai_codex.auth_cancel",
4743
+ "owner": "HostClient",
4744
+ "python": {
4745
+ "generated": true,
4746
+ "name": "openai_codex_auth_cancel"
4747
+ },
4748
+ "role": "host"
4749
+ },
4750
+ {
4751
+ "javascript": {
4752
+ "generated": true,
4753
+ "name": "openaiCodexAuthStart"
4754
+ },
4755
+ "method": "openai_codex.auth_start",
4756
+ "owner": "HostClient",
4757
+ "python": {
4758
+ "generated": true,
4759
+ "name": "openai_codex_auth_start"
4760
+ },
4761
+ "role": "host"
4762
+ },
4763
+ {
4764
+ "javascript": {
4765
+ "generated": true,
4766
+ "name": "openaiCodexSignOut"
4767
+ },
4768
+ "method": "openai_codex.sign_out",
4769
+ "owner": "HostClient",
4770
+ "python": {
4771
+ "generated": true,
4772
+ "name": "openai_codex_sign_out"
4773
+ },
4774
+ "role": "host"
4775
+ },
4776
+ {
4777
+ "javascript": {
4778
+ "generated": true,
4779
+ "name": "openaiCodexStatus"
4780
+ },
4781
+ "method": "openai_codex.status",
4782
+ "owner": "HostClient",
4783
+ "python": {
4784
+ "generated": true,
4785
+ "name": "openai_codex_status"
4786
+ },
4787
+ "role": "host"
4788
+ },
4737
4789
  {
4738
4790
  "javascript": {
4739
4791
  "generated": true,
@@ -5642,7 +5694,7 @@ const DAEMON_WRAPPER_ROWS = Object.freeze([
5642
5694
  "generated": true,
5643
5695
  "name": "session_bind_sandbox"
5644
5696
  },
5645
- "role": "agent"
5697
+ "role": "operator_only"
5646
5698
  },
5647
5699
  {
5648
5700
  "javascript": {
package/docs/ASSISTANT.md CHANGED
@@ -102,7 +102,7 @@ than no tool.
102
102
  |---|---|
103
103
  | `http_request` | `url`, `method?`, `headers?`, `body?`, `timeout_secs?` (default 30, max 120) |
104
104
  | `web_search` | `query`, `max_results?` |
105
- | `m365_task` | `task`, `conversation_id?` — delegates to the user's Microsoft 365 Parslee AI Employee (email, calendar, contacts, HubSpot CRM, meetings) over one chat call. Mutating actions (send mail, create an event) come back **drafted for approval**, never executed silently. Advertised only with a Parslee session (`car auth login`) |
105
+ | `m365_task` | `task`, `conversation_id?` — delegates to the user's Microsoft 365 Parslee AI Employee (email, calendar, contacts, HubSpot CRM, meetings) over one chat call. Mutating actions (send mail, create an event) come back **drafted for approval**, never executed silently. When the M365 service itself fails the task, the result has `ok: false`, an `error`, and the code to quote to support in `support_code`. Advertised only with a Parslee session (`car auth login`) |
106
106
 
107
107
  These self-declare `"tier": "full_access"`, so — unlike file/shell — they need
108
108
  approval even *inside* the sandbox unless the session was started with
@@ -125,8 +125,10 @@ The capability a text-only coding agent structurally does not have. Two tiers:
125
125
  local models (fast, always-available-if-installed) and Parslee Studio
126
126
  (slower, needs `car auth login`, materially higher quality on some axes).
127
127
 
128
- **Local (`sandbox_edit` tier — no approval needed once the run has edit
129
- permission), advertised only when the matching local model is installed:**
128
+ **Local (`full_access` tier — approval is required unless the run uses
129
+ `--full-access`), advertised only when the matching local model is installed.**
130
+ These generators run through the host process even during a sandboxed session,
131
+ so their artifact writes cross the sandbox boundary:
130
132
 
131
133
  | Tool | Parameters |
132
134
  |---|---|
@@ -179,7 +181,7 @@ nothing for it.
179
181
  | `browse_wait` | `condition` (`"page_loaded"` or `"url_changed"`), `timeout_ms?` (default 5000) |
180
182
  | `browse_observe` | `include_screenshot?`, `ocr?` — screenshot + accessibility tree + a fused `ui_map`; OCR recovers labels a polished SPA's accessibility tree misses |
181
183
  | `browser_await_answer` | `timeout_seconds?` (default 45) — blocks until a page stops changing after you submit something, so you don't screenshot mid-load |
182
- | `browser_await_signin` | `url?`, `success_url_contains?`, `timeout_seconds?` (default 300, max 1800) — asks the human to complete a sign-in the agent can't (SSO, MFA); the session persists afterward |
184
+ | `browser_await_signin` | `url?`, `success_url_contains?`, `timeout_seconds?` (default 300, max 1800) — asks the human to complete a sign-in the agent can't (SSO, MFA); the session persists afterward. `success_url_contains` is matched against the URL without its query string; omitted, the wait ends when the URL leaves a login/SSO page. A start page that already passes the test (the needle matches it, or with no needle it does not look like a login page) is refused, since it cannot tell signed in from not |
183
185
  | `browser_record_start` | `quality?` (JPEG 1–100, default 80) |
184
186
  | `browser_record_stop` | `output_path?` — writes an MP4 (requires ffmpeg), captures only frames where the page actually changed |
185
187
 
@@ -237,17 +239,20 @@ a human:
237
239
  tool is routed to approval whenever its declared tier **exceeds** the run's
238
240
  standing tier. This is why `web_search`, the browser tools, `run_applescript`
239
241
  / `run_powershell`, `m365_task`, the Studio media tools, `remember`, and
240
- `notify_linked_device` all stop for approval inside the *default sandboxed
241
- run* — they declare `full_access`, and the sandbox's standing tier is only
242
- `SandboxEdit`. The local generators (`generate_image`, `generate_speech`)
243
- declare `sandbox_edit`, so they run without asking inside the sandbox but are
244
- gated on an unelevated local run.
242
+ `notify_linked_device`, and the local generators (`generate_image`,
243
+ `generate_speech`) all stop for approval inside the *default sandboxed run* —
244
+ they declare `full_access`, and the sandbox's standing tier is only
245
+ `SandboxEdit`. The generators write artifacts through the host process rather
246
+ than inside the container, so their writes cross the sandbox boundary.
245
247
 
246
248
  **How the human is asked** depends on the surface:
247
249
 
248
250
  - One-shot / REPL (`car do "<goal>"` or `car do`): a terminal prompt —
249
251
  `⚠ Approve <tool>(<brief>)? [y/N]` — reading a single line from stdin.
250
252
  Anything but `y` is a denial with reason `"declined by user"`.
253
+ A run whose needed action was declined, or could not be approved because
254
+ nobody could be asked, ends as not done and exits 1, even when the final
255
+ answer explains it.
251
256
  - `car do --serve` / `agents.chat`: routed through the chat surface's
252
257
  `approval_pending` → park → resolve flow. A connected host can present that
253
258
  same pending approval as a reviewable control rather than a plain
@@ -272,9 +277,11 @@ supervises, not just one `car do` invocation. The three presets are the only
272
277
  accepted values; anything else is rejected before it reaches the daemon.
273
278
 
274
279
  A per-agent posture cannot widen the environment that was actually bound.
275
- **Approve for me** auto-allows `sandbox_edit` actions only when a sandbox exists.
276
- If Docker is unavailable and CAR falls back to the local host (or the run uses
277
- `--local`), shell commands and file edits still ask before they touch the Mac.
280
+ **Approve for me** auto-allows `sandbox_edit` actions only when a sandbox exists
281
+ or the user explicitly starts the run with `--full-access`. If Docker is
282
+ unavailable and CAR falls back to the local host (or the run uses `--local`),
283
+ shell commands and file edits still ask before they touch the Mac unless that
284
+ explicit full-access flag was supplied.
278
285
 
279
286
  ## Memory: what actually persists
280
287
 
@@ -471,7 +478,12 @@ of `summary`, which a failed run does not carry:
471
478
  }
472
479
  ```
473
480
 
474
- - `status` is `"success"` or `"error"`. Branch on it.
481
+ - `status` is one of `"success"`, `"success_substituted"`, `"max_turns"`,
482
+ `"stalled"`, `"error"` or `"auth_required"`. Branch on it, and match the set
483
+ exhaustively rather than assuming it is closed. `"success_substituted"` is an
484
+ answer from a model other than the one the run named, with `substituted_from`
485
+ and `substitution_reason` naming the model it replaced and why; it exits 0
486
+ like `"success"`.
475
487
  - `error` is a stable machine-readable kind (e.g. `AssistantLoopFailed`);
476
488
  `message` is the human-readable detail and is NOT stable enough to match on.
477
489
  - `receipts` is always present, including on failure, and is how you see what
package/docs/CLI.md CHANGED
@@ -2,22 +2,27 @@
2
2
 
3
3
  > **Generated file — do not hand-edit below the task map.** Produced by
4
4
  > `scripts/gen-cli-docs.sh` from `car --help` / `car help <command>` on car
5
- > 0.57.0-nightly.20261009 (2026-10-09). Every subcommand the installed binary reports is
5
+ > 0.58.0 (2026-10-16). Every subcommand the installed binary reports is
6
6
  > below; a new subcommand cannot ship without appearing here the next time
7
7
  > this script runs. To regenerate: `bash scripts/gen-cli-docs.sh`.
8
8
  >
9
- > 79 top-level commands, 158 nested subcommands
9
+ > 80 top-level commands, 159 nested subcommands
10
10
  > (one level deep) — counted from the live binary at generation time, not
11
11
  > typed by hand.
12
12
 
13
13
  ## Finding your way around
14
14
 
15
- `car` is one binary with 79 subcommands spanning several different jobs:
15
+ `car` is one binary with 80 subcommands spanning several different jobs:
16
16
  running the built-in agent, coding, local model management, OS integrations,
17
17
  and installing other people's agents on your machine. This map groups the
18
18
  commands people actually reach for; the full alphabetical reference with every
19
19
  flag follows below.
20
20
 
21
+ A release-lab build appends its compile-time identity to version output, for
22
+ example `car 0.57.0 (build 20261008-7fdc72e)`. The `car-server`,
23
+ `car-host`, and `car-mcp` binaries report the same ID. Ordinary builds
24
+ without `CAR_BUILD_ID` keep the shorter `name version` form.
25
+
21
26
  ### Run an agent
22
27
 
23
28
  - [`car do`](#car-do) — the general-purpose CAR Assistant. No goal opens an
@@ -205,10 +210,10 @@ commands on a cadence via launchd / cron / schtasks).
205
210
  | [`car init`](#car-init) | Initialize a .car/ project directory for team-shared configuration |
206
211
  | [`car doctor`](#car-doctor) | Diagnose (and optionally repair) a CAR install: corrupt model weights, unparseable `~/.car` state files, version skew, and leftover files from a previous install. Runs entirely against the local filesystem — no daemon required, so it works even when `car-server` won't start |
207
212
  | [`car feedback`](#car-feedback) | Report a problem to Parslee. Captures a redacted diagnostic bundle (your description, the `car doctor` report, bounded log tails, and a version stamp) into the local outbox at `~/.car/feedback-outbox` — nothing is uploaded by this command; queued reports send when CAR can reach Parslee. Works with the daemon down, like `car doctor`. macOS and Windows; Linux needs its own leak-path audit first |
208
- | [`car update`](#car-update) | Update the locally-installed `car` CLI and its sibling `car-server` daemon to the latest release (or `--version <X.Y.Z>`), in place — regardless of how they were installed. Reconciles the drift that otherwise builds up when one channel updates and another doesn't (e.g. CarHost.app auto-updates its bundled daemon via Sparkle but leaves the `/usr/local/bin/car` CLI behind). The npm/PyPI `car-runtime` client packages are separate — this command never touches them; `car doctor` reports which of your agents have drifted, and prints the exact command per environment. Both remedies pin: `npm install car-runtime@<version>` (`npm update` CANNOT cross a 0.x minor — npm reads `^0.41.0` as `>=0.41.0 <0.42.0`, so it is a no-op) and `<venv>/bin/python -m pip install -U car-runtime==<version>` (a bare `-U` can be silently defeated by the consumer's own pin, and the wrong interpreter installs into an environment that does not hold the stale wheel) |
213
+ | [`car update`](#car-update) | Update the locally-installed `car` CLI and its sibling `car-server` daemon to the latest release (or `--version <X.Y.Z>`), in place — regardless of how they were installed. Reconciles the drift that otherwise builds up when one channel updates and another doesn't (e.g. CarHost.app auto-updates its bundled daemon via Sparkle but leaves the `/usr/local/bin/car` CLI behind). The npm/PyPI `car-runtime` client packages are separate — this command never touches them; `car doctor` reports which of your agents have drifted, and prints the exact command per environment. Both remedies pin: `npm install car-runtime@<version>` (`npm update` CANNOT cross a 0.x minor — npm reads `^0.41.0` as `>=0.41.0 <0.42.0`, so it is a no-op) and `<venv>/bin/python -m pip install -U car-runtime==<version>` (a bare `-U` can be silently defeated by the consumer's own pin, and the wrong interpreter installs into an environment that does not hold the stale wheel). On macOS it also creates the `car-mcp` link beside a `car` that links into CarHost.app (or replaces a dangling / wrong-bundle one): Sparkle never re-runs the package postinstall, so an install that predates `car-mcp` lacks it |
209
214
  | [`car purge`](#car-purge) | Remove this CAR install's own state under `~/.car` (config, logs, managed models, binaries) and reap any OS-level schedules CAR installed — for a clean slate before a reinstall. On macOS this also clears CarHost.app's user-level state and resets its privacy (TCC) permissions, so a reinstall really does re-run permission onboarding. NEVER touches the shared HuggingFace model cache (other tools use it); managed models are symlinks into it, so only the links are removed, not the multi-GB blobs. (To uninstall a single contributed agent instead, use `car uninstall <id>`.) |
210
215
  | [`car code`](#car-code) | Open a coding conversation in your repository. Ask questions, plan a change, or describe work to implement; review the checks and edits, then keep iterating in the same conversation |
211
- | [`car board`](#car-board) | Fullscreen supervision board for coder sessions — one screen for every run on this host, whoever started it (`car code`, CarHost, milo, another board). Attach to a run's full history, confirm its outcome contract, answer its questions, approve its diff, or scope work in a repo-grounded discussion first. Closing the board never stops a run |
216
+ | [`car board`](#car-board) | Fullscreen supervision board for coder sessions — one screen for every run on this host, whoever started it (`car code`, CarHost, milo, another board). Attach to a run's full history, confirm its outcome contract, answer its questions, approve its diff, or scope work in a repo-grounded discussion first. It shows and acts on every run, authenticating with CAR's local host key; a board launched by an agent cannot. Closing the board never stops a run |
212
217
  | [`car do`](#car-do) | CAR Assistant: a general-purpose agent that works out of the box |
213
218
  | [`car policy-check-hook`](#car-policy-check-hook) | Evaluate a host's proposed tool call against `.car/policies/` and answer on stdout with that host's hook decision envelope |
214
219
  | [`car project`](#car-project) | Manage CAR-managed projects (a named, git-backed workspace CAR creates for you — no repo to pick) |
@@ -248,6 +253,7 @@ commands on a cadence via launchd / cron / schtasks).
248
253
  | [`car registry`](#car-registry) | Registry tooling for the contributed-agents registry (Parslee-ai/car#182 phase 5): `digest` a manifest, or `validate` a registry directory (the CI gate run by Parslee-ai/car-agent-registry) |
249
254
  | [`car fleet`](#car-fleet) | What every CAR instance you can reach can do — agents, capabilities and models across the fleet — and whether this machine takes coding subtasks farmed out by peers (`car fleet enroll`). Needs a running daemon |
250
255
  | [`car mp`](#car-mp) | Multiplayer development: a work item moves Build → Improve → Polish through different developers before it merges, each stage a coder session on its owner's machine with the engine they choose. Needs a running daemon and a shared git remote |
256
+ | [`car claude`](#car-claude) | Start Claude Code as a CAR-supervised collaboration session. Enrolls a per-session agent bound to the work context `car auth resolve` names for this directory, hands its identity to the CAR plugin's collaboration tools, runs `claude` in this terminal, and removes the agent when it exits. Arguments after `--` go to `claude`. Needs a running daemon |
251
257
  | [`car publish`](#car-publish) | Publish a contributed agent to the registry (Parslee-ai/car#182 phase 5). Reads a local agent's `manifest.toml`, signs it for `--audience public` (ed25519 key at `$CAR_PUBLISH_KEY_PATH`), stages it at the versioned `agents/<namespace>/<name>/<version>/` path, updates `index.json` + the README catalog, re-runs the EXACT `car registry validate` CI gate locally, and opens a PR against the registry repo. Aborts (no PR) on a missing signing key or a validation failure |
252
258
  | [`car help`](#car-help) | Print this message or the help of the given subcommand(s) |
253
259
 
@@ -280,7 +286,7 @@ Options:
280
286
  --json Emit machine-readable JSON
281
287
  --md Emit generated Markdown (the default)
282
288
  --role <ROLE> Print exactly the daemon method names assigned to this caller role [possible
283
- values: agent, owner, operator, host]
289
+ values: agent, owner, operator, operator_only, host]
284
290
  -h, --help Print help
285
291
  ```
286
292
 
@@ -664,7 +670,8 @@ Re-score one delivered attempt without starting a coding session
664
670
  Usage: car coder-ab rescore [OPTIONS] --attempt <ATTEMPT> --corpus <CORPUS>
665
671
 
666
672
  Options:
667
- --attempt <ATTEMPT> Existing `<ledger-dir>/artifacts/<task-id>/<attempt>` directory
673
+ --attempt <ATTEMPT> Existing `<ledger-dir>/artifacts/ledgers/<ledger
674
+ stem>/<task-id>/<attempt>` (or legacy `<ledger-dir>/artifacts/<task-id>/<attempt>`) directory
668
675
  --corpus <CORPUS> Corpus manifest containing the task named by the source ledger row
669
676
  --ledger <LEDGER> Owning ledger. Omit to discover it from the attempt artifact layout
670
677
  -h, --help Print help
@@ -1186,9 +1193,11 @@ Commands:
1186
1193
  both the `everyday` and `local_focused` policies — the same rule `models.list_unified` reports,
1187
1194
  without a machine of each size. Reads a row or an array of rows (as `car models add --dry-run
1188
1195
  --json` prints) from FILE, or stdin. Prints JSON. The verdict is the recommendation estimate
1189
- `models.list_unified` shows, which sizes the KV cache from the parameter count; load-time
1190
- admission uses an installed checkpoint's own KV geometry and the machine's live state, so it can
1191
- differ
1196
+ `models.list_unified` shows, which sizes the KV cache from an installed checkpoint's own KV
1197
+ geometry, as load-time admission does, and from the parameter count for a row not installed;
1198
+ admission also uses the machine's live state, so it can differ. `platform_compatible` is false for
1199
+ a row CAR does not run on Apple Silicon at any size (a GGUF/Candle row, say), and its
1200
+ `smallest_mac_gb` is then null whatever the memory verdict
1192
1201
  register Register a custom model from a JSON schema file
1193
1202
  unregister Unregister a model by ID
1194
1203
  stats Show performance stats for models based on observed outcomes
@@ -1430,8 +1439,11 @@ CAR's memory-fit verdict for catalog rows on stated Apple Silicon machines, unde
1430
1439
  `everyday` and `local_focused` policies — the same rule `models.list_unified` reports, without a
1431
1440
  machine of each size. Reads a row or an array of rows (as `car models add --dry-run --json` prints)
1432
1441
  from FILE, or stdin. Prints JSON. The verdict is the recommendation estimate `models.list_unified`
1433
- shows, which sizes the KV cache from the parameter count; load-time admission uses an installed
1434
- checkpoint's own KV geometry and the machine's live state, so it can differ
1442
+ shows, which sizes the KV cache from an installed checkpoint's own KV geometry, as load-time
1443
+ admission does, and from the parameter count for a row not installed; admission also uses the
1444
+ machine's live state, so it can differ. `platform_compatible` is false for a row CAR does not run on
1445
+ Apple Silicon at any size (a GGUF/Candle row, say), and its `smallest_mac_gb` is then null whatever
1446
+ the memory verdict
1435
1447
 
1436
1448
  Usage: car models fit [OPTIONS] [FILE]
1437
1449
 
@@ -1898,8 +1910,9 @@ Usage: car doctor [OPTIONS]
1898
1910
 
1899
1911
  Options:
1900
1912
  --repair Apply safe repairs: purge corrupt cache files (re-pull afterward), back up
1901
- unparseable state files, refresh the version stamp. Never deletes unrecognized entries or
1902
- anything not provably corrupt
1913
+ unparseable state files, refresh the version stamp, and (on macOS) link a missing, dangling,
1914
+ or wrong-bundle `car-mcp` beside a bundle-linked `car`. Never deletes unrecognized entries or
1915
+ anything not provably corrupt, and never overwrites a regular file
1903
1916
  --deep Deep-verify model weights (recompute sha256 vs the HuggingFace etag) instead of the
1904
1917
  cheap presence/non-empty check. Slower but catches truncated-but-non-empty corruption
1905
1918
  -h, --help Print help
@@ -1937,7 +1950,10 @@ which of your agents have drifted, and prints the exact command per environment.
1937
1950
  `npm install car-runtime@<version>` (`npm update` CANNOT cross a 0.x minor — npm reads `^0.41.0` as
1938
1951
  `>=0.41.0 <0.42.0`, so it is a no-op) and `<venv>/bin/python -m pip install -U
1939
1952
  car-runtime==<version>` (a bare `-U` can be silently defeated by the consumer's own pin, and the
1940
- wrong interpreter installs into an environment that does not hold the stale wheel)
1953
+ wrong interpreter installs into an environment that does not hold the stale wheel). On macOS it also
1954
+ creates the `car-mcp` link beside a `car` that links into CarHost.app (or replaces a dangling /
1955
+ wrong-bundle one): Sparkle never re-runs the package postinstall, so an install that predates
1956
+ `car-mcp` lacks it
1941
1957
 
1942
1958
  Usage: car update [OPTIONS]
1943
1959
 
@@ -2081,7 +2097,8 @@ opens saved conversations. The exit summary prints the resume command.
2081
2097
  Fullscreen supervision board for coder sessions — one screen for every run on this host, whoever
2082
2098
  started it (`car code`, CarHost, milo, another board). Attach to a run's full history, confirm its
2083
2099
  outcome contract, answer its questions, approve its diff, or scope work in a repo-grounded
2084
- discussion first. Closing the board never stops a run
2100
+ discussion first. It shows and acts on every run, authenticating with CAR's local host key; a board
2101
+ launched by an agent cannot. Closing the board never stops a run
2085
2102
 
2086
2103
  Usage: car board [OPTIONS]
2087
2104
 
@@ -2215,6 +2232,18 @@ Options:
2215
2232
 
2216
2233
  [default: 20]
2217
2234
 
2235
+ --org <ORG>
2236
+ Work in this organization (one a signed-in login holds). Omitted, the run binds the
2237
+ repository's declared org, else your default org (`car auth default-org`), else runs as
2238
+ personal work. Its memory, learned repairs, trajectories and receipts stay in that org's
2239
+ private store, and your own-account tools (Mail, Calendar, M365, Studio, automation,
2240
+ iMessage) are left out. Needs the daemon. Not with --serve: a served agent's organization
2241
+ is fixed when it is registered with the daemon
2242
+
2243
+ --personal
2244
+ Run as personal work even where a default org applies. Refused in a repository that
2245
+ declares an org. Not with --serve
2246
+
2218
2247
  -h, --help
2219
2248
  Print help (see a summary with '-h')
2220
2249
  ```
@@ -3093,9 +3122,11 @@ Commands:
3093
3122
  without it the notice is printed and nothing is sent
3094
3123
  connect Connect with another owner's CAR for a shared project: invite, inspect, accept,
3095
3124
  confirm. Both owners consent before anything is shared
3096
- knock Ask a CAR discovered on your network to connect — no invite code. Its owner sees
3097
- your purpose, project and key fingerprint, and accepts or declines. Its identity is unverified:
3098
- compare fingerprints with its owner if it matters who they are
3125
+ knock Ask a CAR to connect — no invite code. Either a CAR discovered on your network
3126
+ (`<machine>`; its identity is unverified, so compare fingerprints with its owner if it matters who
3127
+ they are), or, with `--member <account> --org <org>`, a colleague in a Parslee org through the
3128
+ Parslee relay, which works when neither CAR can reach the other (identity `cert:<org>:<account>`,
3129
+ from their device certificate)
3099
3130
  knocks CARs discovered on your network, and knocks waiting in both directions
3100
3131
  knock-accept Accept a knock, choosing your label for that CAR and what you share
3101
3132
  knock-decline Decline a knock. That CAR may not knock again for a day, or ever with `--block`
@@ -3105,8 +3136,12 @@ Commands:
3105
3136
  knock-block Block a key from knocking, quiet it (knocks recorded, no prompt), or allow it
3106
3137
  again
3107
3138
  connections Your Lattice connections with other owners' CARs
3108
- directory Colleagues in your org whose CARs an org admin attested — the people `car lattice
3109
- connect offer` can reach without a fingerprint exchange
3139
+ directory Colleagues in your orgs: those whose CARs an org admin attested (for `car lattice
3140
+ connect offer`), and those whose devices Parslee certifies, reachable through the Parslee relay
3141
+ (for `car lattice knock --member`)
3142
+ relay-probe Developer check of the Parslee relay: send a sealed probe to a colleague's device,
3143
+ or pull and verify probes sent to this one. Uses this CAR's device certificate for the org; reads
3144
+ no credential
3110
3145
  org Org-attested connections: status, admin attestation and policy, and your standing
3111
3146
  auto-accept rules
3112
3147
  disconnect End a connection now. The other CAR is told; what it already received cannot be
@@ -3231,7 +3266,8 @@ Usage: car lattice connect <COMMAND>
3231
3266
 
3232
3267
  Commands:
3233
3268
  invite Create an invite: purpose, project, a name for the other CAR, and which of your
3234
- nodes it may see and address (repeat --share)
3269
+ nodes it may see and address (repeat --share; a node address, or `agents:<project>` for every
3270
+ agent joined in that project)
3235
3271
  inspect Show what an invite asks for — its purpose and project — before accepting it. The
3236
3272
  inviter's fingerprint is not shown: ask them for it
3237
3273
  accept Accept an invite with your own share list. Active once the inviter confirms your
@@ -3244,6 +3280,8 @@ Commands:
3244
3280
  Only then is the connection active
3245
3281
  share Replace which of your nodes an active connection may see and message (repeat
3246
3282
  --share; none shares nothing). Takes effect here at once; the other CAR is told
3283
+ rename Rename your name for the other CAR (its nodes are `<node>@<name>`). Only yours: the
3284
+ other owner's name for your CAR does not change
3247
3285
  help Print this message or the help of the given subcommand(s)
3248
3286
 
3249
3287
  Options:
@@ -3253,19 +3291,30 @@ Options:
3253
3291
  #### car lattice knock
3254
3292
 
3255
3293
  ```text
3256
- Ask a CAR discovered on your network to connect — no invite code. Its owner sees your purpose,
3257
- project and key fingerprint, and accepts or declines. Its identity is unverified: compare
3258
- fingerprints with its owner if it matters who they are
3294
+ Ask a CAR to connect — no invite code. Either a CAR discovered on your network (`<machine>`; its
3295
+ identity is unverified, so compare fingerprints with its owner if it matters who they are), or, with
3296
+ `--member <account> --org <org>`, a colleague in a Parslee org through the Parslee relay, which
3297
+ works when neither CAR can reach the other (identity `cert:<org>:<account>`, from their device
3298
+ certificate)
3259
3299
 
3260
- Usage: car lattice knock --purpose <PURPOSE> --project <PROJECT> --label <LABEL> <MACHINE>
3300
+ Usage: car lattice knock [OPTIONS] --purpose <PURPOSE> --project <PROJECT> [MACHINE]
3261
3301
 
3262
3302
  Arguments:
3263
- <MACHINE> The discovered CAR: its advertised name (see `car lattice knocks`) or its URL
3303
+ [MACHINE] The discovered CAR: its advertised name (see `car lattice knocks`) or its URL
3264
3304
 
3265
3305
  Options:
3306
+ --member <MEMBER> A colleague's account, as `car lattice directory` lists it: knock through
3307
+ the Parslee relay of `--org`
3308
+ --org <ORG> With `--member`, the org whose Parslee relay carries the offer. Without
3309
+ it, the org whose device certificate this knock presents
3310
+ --device <DEVICE> With `--member`, which of their devices (default: the one with the newest
3311
+ certificate)
3266
3312
  --purpose <PURPOSE>
3267
3313
  --project <PROJECT>
3268
- --label <LABEL> Your name for that CAR once connected (`<node>@<label>`)
3314
+ --label <LABEL> Your name for that CAR once connected (`<node>@<label>`). Never sent to
3315
+ it. Required for a network knock
3316
+ --share <SHARE> With `--member`, a node of yours the colleague may see and address once
3317
+ they accept (repeat)
3269
3318
  -h, --help Print help
3270
3319
  ```
3271
3320
 
@@ -3367,16 +3416,37 @@ Options:
3367
3416
  #### car lattice directory
3368
3417
 
3369
3418
  ```text
3370
- Colleagues in your org whose CARs an org admin attested — the people `car lattice connect offer` can
3371
- reach without a fingerprint exchange
3419
+ Colleagues in your orgs: those whose CARs an org admin attested (for `car lattice connect offer`),
3420
+ and those whose devices Parslee certifies, reachable through the Parslee relay (for `car lattice
3421
+ knock --member`)
3372
3422
 
3373
3423
  Usage: car lattice directory [OPTIONS]
3374
3424
 
3375
3425
  Options:
3376
3426
  --org <ORG> Which org, when this CAR serves several (default: all of them)
3427
+ --refresh Read the org's Parslee relay directory again instead of the cache
3377
3428
  -h, --help Print help
3378
3429
  ```
3379
3430
 
3431
+ #### car lattice relay-probe
3432
+
3433
+ ```text
3434
+ Developer check of the Parslee relay: send a sealed probe to a colleague's device, or pull and
3435
+ verify probes sent to this one. Uses this CAR's device certificate for the org; reads no credential
3436
+
3437
+ Usage: car lattice relay-probe <COMMAND>
3438
+
3439
+ Commands:
3440
+ send Seal `text` to the device with this Ed25519 key (from the org's relay directory) and deposit
3441
+ it with the relay
3442
+ pull Pull this device's relay inbox for the org, verify each probe, print them, and acknowledge
3443
+ them. Other relay mail stays queued
3444
+ help Print this message or the help of the given subcommand(s)
3445
+
3446
+ Options:
3447
+ -h, --help Print help
3448
+ ```
3449
+
3380
3450
  #### car lattice org
3381
3451
 
3382
3452
  ```text
@@ -4449,8 +4519,10 @@ Commands:
4449
4519
  board`
4450
4520
  publish Publish a finished coder session as a stage: an approved diff, or an accepted no-change
4451
4521
  finding. Without --item this publishes a Build and creates the work item
4452
- submit Submit a stage you did outside CAR (for example in Claude Code or Codex). CAR runs the
4453
- item's contract on the commit itself, without credentials, before recording it
4522
+ submit Submit work you did outside CAR (for example in Claude Code or Codex). CAR runs the
4523
+ contract on the commit itself, without credentials, before recording it. With an item this is that
4524
+ item's next stage; without one it is a Build that creates the item from --intent and
4525
+ --contract-file
4454
4526
  check Re-verify an item end to end and, if it holds, create `car/mp/<id>-final` for a pull
4455
4527
  request. Runs the item's contract here
4456
4528
  abandon Retire a work item (e.g. its locked contract was wrong). Nothing is deleted: the record
@@ -4534,19 +4606,27 @@ Options:
4534
4606
  #### car mp submit
4535
4607
 
4536
4608
  ```text
4537
- Submit a stage you did outside CAR (for example in Claude Code or Codex). CAR runs the item's
4538
- contract on the commit itself, without credentials, before recording it
4609
+ Submit work you did outside CAR (for example in Claude Code or Codex). CAR runs the contract on the
4610
+ commit itself, without credentials, before recording it. With an item this is that item's next
4611
+ stage; without one it is a Build that creates the item from --intent and --contract-file
4539
4612
 
4540
- Usage: car mp submit [OPTIONS] <ITEM>
4613
+ Usage: car mp submit [OPTIONS] [ITEM]
4541
4614
 
4542
4615
  Arguments:
4543
- <ITEM>
4544
-
4545
- Options:
4546
- --commit <COMMIT> The stage's work: a commit descending from the item's tip [default: HEAD]
4547
- --repo <REPO> [default: .]
4548
- --remote <REMOTE> [default: origin]
4549
- -h, --help Print help
4616
+ [ITEM]
4617
+
4618
+ Options:
4619
+ --commit <COMMIT> The work: a commit descending from the item's tip, or for a
4620
+ Build from --base [default: HEAD]
4621
+ --intent <INTENT> Build only: what the item is for. Fixed here; the only text
4622
+ that travels to the later stages
4623
+ --contract-file <CONTRACT_FILE> Build only: the outcome contract (JSON, as
4624
+ `coder.confirm_contract` takes it) that becomes the item's locked contract
4625
+ --base <BASE> Build only: the commit the Build starts from, which must be
4626
+ on the remote. Defaults to the remote's default branch
4627
+ --repo <REPO> [default: .]
4628
+ --remote <REMOTE> [default: origin]
4629
+ -h, --help Print help
4550
4630
  ```
4551
4631
 
4552
4632
  #### car mp check
@@ -4585,6 +4665,23 @@ Options:
4585
4665
  -h, --help Print help
4586
4666
  ```
4587
4667
 
4668
+ ### car claude
4669
+
4670
+ ```text
4671
+ Start Claude Code as a CAR-supervised collaboration session. Enrolls a per-session agent bound to
4672
+ the work context `car auth resolve` names for this directory, hands its identity to the CAR plugin's
4673
+ collaboration tools, runs `claude` in this terminal, and removes the agent when it exits. Arguments
4674
+ after `--` go to `claude`. Needs a running daemon
4675
+
4676
+ Usage: car claude [ARGS]...
4677
+
4678
+ Arguments:
4679
+ [ARGS]...
4680
+
4681
+ Options:
4682
+ -h, --help Print help
4683
+ ```
4684
+
4588
4685
  ### car publish
4589
4686
 
4590
4687
  ```text
package/index.d.ts CHANGED
@@ -232,6 +232,14 @@ export class CarRuntime {
232
232
  /** Transport-named alias for `close()`. */
233
233
  disconnect(): Promise<void>;
234
234
 
235
+ /**
236
+ * Identify the daemon session this runtime's calls land on, connecting first
237
+ * if needed. Registered tools, policies and state belong to that session, so
238
+ * a different value than last time (after `close()`, or a daemon restart the
239
+ * client reconnected through) means they must be registered again.
240
+ */
241
+ connectionEpoch(): Promise<number>;
242
+
235
243
  /**
236
244
  * Invoke any daemon JSON-RPC method with a JSON-encoded params value.
237
245
  * `daemonCall` is the call-by-name escape hatch; use the typed wrappers as the primary API.
@@ -240,7 +248,7 @@ export class CarRuntime {
240
248
  */
241
249
  daemonCall(method: string, paramsJson: string): Promise<string>;
242
250
 
243
- /** Host-management-token twin of `daemonCall`; the method allowlist remains enforced. */
251
+ /** Host-management-token twin of `daemonCall`; the method allowlist remains enforced. It admits the owner-gated Coder methods (`coder.respond`, `coder.revise_contract`, `coder.confirm_contract`, `coder.refresh_review`, `coder.approve_merge`, `coder.cancel`), but refuses `coder.confirm_contract` and `coder.approve_merge` on an agent build: building an agent needs the CAR app or `car agent new` in your terminal. */
244
252
  daemonCallHostManagement(method: string, paramsJson: string): Promise<string>;
245
253
 
246
254
  /** List durable permission grants using host-management authority. */
@@ -625,16 +633,16 @@ export class CarRuntime {
625
633
  /** Generated daemon wrapper for `feedback.submit` (operator). */
626
634
  feedbackSubmit(paramsJson: string): Promise<string>;
627
635
 
628
- /** Generated daemon wrapper for `goal.clear` (operator). */
636
+ /** Generated daemon wrapper for `goal.clear` (agent). */
629
637
  goalClear(paramsJson: string): Promise<string>;
630
638
 
631
- /** Generated daemon wrapper for `goal.set` (operator). */
639
+ /** Generated daemon wrapper for `goal.set` (agent). */
632
640
  goalSet(paramsJson: string): Promise<string>;
633
641
 
634
- /** Generated daemon wrapper for `goal.status` (operator). */
642
+ /** Generated daemon wrapper for `goal.status` (agent). */
635
643
  goalStatus(paramsJson: string): Promise<string>;
636
644
 
637
- /** Generated daemon wrapper for `goal.suggest` (operator). */
645
+ /** Generated daemon wrapper for `goal.suggest` (agent). */
638
646
  goalSuggest(paramsJson: string): Promise<string>;
639
647
 
640
648
  /** Generated daemon wrapper for `host.subscribe` (operator). */
@@ -664,28 +672,28 @@ export class CarRuntime {
664
672
  /** Generated daemon wrapper for `inference.runner.fail` (operator). */
665
673
  inferenceRunnerFail(paramsJson: string): Promise<string>;
666
674
 
667
- /** Generated daemon wrapper for `lattice.claim` (operator). */
675
+ /** Generated daemon wrapper for `lattice.claim` (owner). */
668
676
  latticeClaim(paramsJson: string): Promise<string>;
669
677
 
670
- /** Generated daemon wrapper for `lattice.claims` (operator). */
678
+ /** Generated daemon wrapper for `lattice.claims` (owner). */
671
679
  latticeClaims(paramsJson: string): Promise<string>;
672
680
 
673
- /** Generated daemon wrapper for `lattice.find` (operator). */
681
+ /** Generated daemon wrapper for `lattice.find` (owner). */
674
682
  latticeFind(paramsJson: string): Promise<string>;
675
683
 
676
- /** Generated daemon wrapper for `lattice.join` (operator). */
684
+ /** Generated daemon wrapper for `lattice.join` (owner). */
677
685
  latticeJoin(paramsJson: string): Promise<string>;
678
686
 
679
- /** Generated daemon wrapper for `lattice.leave` (operator). */
687
+ /** Generated daemon wrapper for `lattice.leave` (owner). */
680
688
  latticeLeave(paramsJson: string): Promise<string>;
681
689
 
682
- /** Generated daemon wrapper for `lattice.nodes` (operator). */
690
+ /** Generated daemon wrapper for `lattice.nodes` (owner). */
683
691
  latticeNodes(paramsJson: string): Promise<string>;
684
692
 
685
- /** Generated daemon wrapper for `lattice.release` (operator). */
693
+ /** Generated daemon wrapper for `lattice.release` (owner). */
686
694
  latticeRelease(paramsJson: string): Promise<string>;
687
695
 
688
- /** Generated daemon wrapper for `lattice.wait` (operator). */
696
+ /** Generated daemon wrapper for `lattice.wait` (owner). */
689
697
  latticeWait(paramsJson: string): Promise<string>;
690
698
 
691
699
  /** Generated daemon wrapper for `meeting.get` (operator). */
@@ -823,25 +831,25 @@ export class CarRuntime {
823
831
  /** Generated daemon wrapper for `multi.vote` (operator). */
824
832
  multiVote(paramsJson: string): Promise<string>;
825
833
 
826
- /** Generated daemon wrapper for `multiplayer.abandon` (operator). */
834
+ /** Generated daemon wrapper for `multiplayer.abandon` (operator_only). */
827
835
  multiplayerAbandon(paramsJson: string): Promise<string>;
828
836
 
829
- /** Generated daemon wrapper for `multiplayer.get` (operator). */
837
+ /** Generated daemon wrapper for `multiplayer.get` (operator_only). */
830
838
  multiplayerGet(paramsJson: string): Promise<string>;
831
839
 
832
- /** Generated daemon wrapper for `multiplayer.list` (operator). */
840
+ /** Generated daemon wrapper for `multiplayer.list` (operator_only). */
833
841
  multiplayerList(paramsJson: string): Promise<string>;
834
842
 
835
- /** Generated daemon wrapper for `multiplayer.merge_check` (operator). */
843
+ /** Generated daemon wrapper for `multiplayer.merge_check` (operator_only). */
836
844
  multiplayerMergeCheck(paramsJson: string): Promise<string>;
837
845
 
838
- /** Generated daemon wrapper for `multiplayer.publish` (operator). */
846
+ /** Generated daemon wrapper for `multiplayer.publish` (operator_only). */
839
847
  multiplayerPublish(paramsJson: string): Promise<string>;
840
848
 
841
- /** Generated daemon wrapper for `multiplayer.start_stage` (operator). */
849
+ /** Generated daemon wrapper for `multiplayer.start_stage` (operator_only). */
842
850
  multiplayerStartStage(paramsJson: string): Promise<string>;
843
851
 
844
- /** Generated daemon wrapper for `multiplayer.submit_stage` (operator). */
852
+ /** Generated daemon wrapper for `multiplayer.submit_stage` (operator_only). */
845
853
  multiplayerSubmitStage(paramsJson: string): Promise<string>;
846
854
 
847
855
  /** Generated daemon wrapper for `nlp.extract_entities` (operator). */
@@ -967,7 +975,7 @@ export class CarRuntime {
967
975
  /** Generated daemon wrapper for `session.auth` (agent). */
968
976
  sessionAuth(paramsJson: string): Promise<string>;
969
977
 
970
- /** Generated daemon wrapper for `session.bindSandbox` (agent). */
978
+ /** Generated daemon wrapper for `session.bindSandbox` (operator_only). */
971
979
  sessionBindSandbox(paramsJson: string): Promise<string>;
972
980
 
973
981
  /** Generated daemon wrapper for `session.bindSubstrate` (operator). */
@@ -1666,7 +1674,9 @@ export class CarRuntime {
1666
1674
  /**
1667
1675
  * `agents.peers` — visible peers as JSON. Each row distinguishes the
1668
1676
  * kind-level `can_receive` capability from the current `reachable` delivery
1669
- * preflight; the send remains authoritative.
1677
+ * preflight; the send remains authoritative. `{"organization_directory": true}`
1678
+ * adds the colleague directory of the organization this connection is bound
1679
+ * to, from what the CAR already holds.
1670
1680
  */
1671
1681
  agentsPeers(requestJson: string): Promise<string>;
1672
1682
  /** `agents.message` — send text to one peer: `{ to, body, summary? }`. The sender is derived server-side. */
@@ -1769,7 +1779,11 @@ export class CarRuntime {
1769
1779
  * `approved_no_patch` | `rejected_by_gate` | `measurement_failed` |
1770
1780
  * `config_moved_during_measurement` (a graded promotion whose measured base
1771
1781
  * was moved by something else while the replays ran — nothing applied, both
1772
- * values reported, no backoff). When `context_measure` was requested the
1782
+ * values reported, no backoff) | `config_moved_since_diagnosis` (a
1783
+ * human-approved patch whose `conversation_keep_recent` moved after
1784
+ * diagnosis — nothing applied, `diagnosed_under_conversation_keep_recent` and
1785
+ * `current_conversation_keep_recent` reported, no backoff). When
1786
+ * `context_measure` was requested the
1773
1787
  * summary also carries `context_measured: { status: "measured" |
1774
1788
  * "skipped_dry_run", grade_attempts, model, split, split_seed }`.
1775
1789
  *
@@ -2002,9 +2016,11 @@ export class CarRuntime {
2002
2016
  * with reasoning suppressed to produce a direct answer, or
2003
2017
  * `"thinking_truncated"` when even that retry was empty (car-releases#60).
2004
2018
  *
2005
- * `local_last_resort` is present and true ONLY when CAR appended an
2006
- * installed on-device model behind a remote-only chain and that model
2007
- * actually served the turn. An explicitly selected local model is not marked.
2019
+ * `local_last_resort` is present and true ONLY when CAR appended a
2020
+ * model installed through CAR (under CAR's models dir or with an install
2021
+ * receipt; a model only in the shared Hugging Face cache is skipped and
2022
+ * named with `car models adopt`) behind a remote-only chain and that
2023
+ * model actually served the turn. An explicitly selected local model is not marked.
2008
2024
  *
2009
2025
  * `auth_fallback_from` is present ONLY when a candidate earlier in the
2010
2026
  * fallback chain was skipped because its credential was **rejected**
@@ -2164,7 +2180,9 @@ export class CarRuntime {
2164
2180
 
2165
2181
  /**
2166
2182
  * Fetch a URL and extract readable text (keyless; companion to `search`).
2167
- * Returns JSON `{ url, status, content_type, title?, text }`.
2183
+ * Returns JSON `{ url, status, content_type, title?, text }`. Only the
2184
+ * first 8 MiB of the body are read; a longer page's `text` ends in
2185
+ * `…[truncated]…`.
2168
2186
  */
2169
2187
  webFetch(url: string): Promise<string>;
2170
2188
 
@@ -2238,7 +2256,10 @@ export class CarRuntime {
2238
2256
  * weights_ready, downloads_weights,
2239
2257
  * max_output_tokens, public_benchmarks, cost, car_enabled, can_remove,
2240
2258
  * in_use, management_evidence, fit, estimated_peak_mb,
2241
- * platform_compatible, deprecated, family, version }`. `available` means CAR
2259
+ * platform_compatible, deprecated, credential_required,
2260
+ * unavailable_reason, family, version, source_kind }`. `source_kind` is the
2261
+ * row's source `type` tag (`codex_cli`, `remote_api`, `proprietary`, ...),
2262
+ * which keeps two routes to one model apart. `available` means CAR
2242
2263
  * can use the model
2243
2264
  * here — for a local MLX entry with a declared `hf_repo` it is `true`
2244
2265
  * before a byte is fetched, because it lazy-downloads on first use —
@@ -2498,7 +2519,7 @@ export class CarRuntime {
2498
2519
  * JSON may carry `network_approved: true` and
2499
2520
  * `approved_credentials: string[]`. Credential approval is limited to names
2500
2521
  * declared by that exact check; changing its name, command, or declarations
2501
- * drops carried approval.
2522
+ * drops carried approval. Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2502
2523
  */
2503
2524
  coderConfirmContract(
2504
2525
  sessionId: string,
@@ -2556,6 +2577,7 @@ export class CarRuntime {
2556
2577
 
2557
2578
  /**
2558
2579
  * Answer a `user_input_requested` event, or queue native steering with steer=true.
2580
+ * Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2559
2581
  */
2560
2582
  coderRespond(sessionId: string, text: string): Promise<string>;
2561
2583
  coderRespond(sessionId: string, text: string, steer: boolean | null): Promise<string>;
@@ -2570,7 +2592,7 @@ export class CarRuntime {
2570
2592
  * reply is `{state: "reported", branch: null}`. A plain `approve: true` on
2571
2593
  * a finding, or `acceptFinding` on a diff, is refused. Successful delivery
2572
2594
  * replies include `excluded_artifacts` with the exact records used by the
2573
- * committed staging pass.
2595
+ * committed staging pass. Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2574
2596
  */
2575
2597
  coderApproveMerge(
2576
2598
  sessionId: string,
@@ -2596,6 +2618,7 @@ export class CarRuntime {
2596
2618
  * `message` names what already happened. Callers that cancel unconditionally
2597
2619
  * on shutdown depend on that — rejecting would turn a quiet exit into a
2598
2620
  * protocol error whenever the session raced to terminal first.
2621
+ * Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2599
2622
  */
2600
2623
  /** `reason` names a non-operator cause and is recorded as `cancelled: <reason>`; omit it only when a person cancelled. */
2601
2624
  coderCancel(sessionId: string, reason?: string | undefined | null): Promise<string>;
@@ -2642,7 +2665,7 @@ export class CarRuntime {
2642
2665
  * Returns `{state: "running", review_refresh: true}`; the outcome arrives on
2643
2666
  * the event stream. Passing checks return the session to `needs_approval`
2644
2667
  * with a new diff; failing checks fail it and keep the worktree. Delivery
2645
- * still needs a separate `coderApproveMerge`.
2668
+ * still needs a separate `coderApproveMerge`. Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2646
2669
  */
2647
2670
  coderRefreshReview(sessionId: string): Promise<string>;
2648
2671
 
@@ -2656,7 +2679,7 @@ export class CarRuntime {
2656
2679
  * message}`. **Check `revised` before trusting `contract`**: on a redraft
2657
2680
  * that does not validate, the previous contract comes back byte-identical
2658
2681
  * with `revised: false` and a `message` explaining why, and the daemon emits
2659
- * a `contract_revision_rejected` event.
2682
+ * a `contract_revision_rejected` event. Only the agent that started the run or the CAR app may call this; a run a person started also accepts any connection not bound to an agent, and an agent's run also accepts unbound connections when the daemon has no host token. To act on an agent's run with host authority, send the method through the host-management call.
2660
2683
  */
2661
2684
  coderReviseContract(sessionId: string, request: string): Promise<string>;
2662
2685
 
@@ -2734,6 +2757,10 @@ export class CarRuntime {
2734
2757
  /**
2735
2758
  * Discover what the signed-in Parslee account can do — identity, m365
2736
2759
  * product entitlements, and Studio reachability. Read-only. Returns JSON.
2760
+ * Resolves `{authenticated: false, hint, error?}` when signed out; rejects
2761
+ * with the underlying failure when Parslee could not answer the session
2762
+ * check (a rate limit, an outage, or no session answer) or denied this
2763
+ * signed-in account access (HTTP 403).
2737
2764
  */
2738
2765
  parsleeCapabilities(): Promise<string>;
2739
2766
  /**
@@ -3557,6 +3584,18 @@ export class HostClient {
3557
3584
  /** Generated host wrapper for `models.storage_roots`. */
3558
3585
  modelsStorageRoots(paramsJson: string): Promise<string>;
3559
3586
 
3587
+ /** Generated host wrapper for `openai_codex.auth_cancel`. */
3588
+ openaiCodexAuthCancel(paramsJson: string): Promise<string>;
3589
+
3590
+ /** Generated host wrapper for `openai_codex.auth_start`. */
3591
+ openaiCodexAuthStart(paramsJson: string): Promise<string>;
3592
+
3593
+ /** Generated host wrapper for `openai_codex.sign_out`. */
3594
+ openaiCodexSignOut(paramsJson: string): Promise<string>;
3595
+
3596
+ /** Generated host wrapper for `openai_codex.status`. */
3597
+ openaiCodexStatus(paramsJson: string): Promise<string>;
3598
+
3560
3599
  /** Generated host wrapper for `openrouter.auth_cancel`. */
3561
3600
  openrouterAuthCancel(paramsJson: string): Promise<string>;
3562
3601
 
@@ -4005,7 +4044,22 @@ export interface DispatchVoiceTurnResponse {
4005
4044
 
4006
4045
  export type VoiceTurnEvent =
4007
4046
  | { type: 'voice.turn.fast_delta'; turn_id: number; text: string }
4008
- | { type: 'voice.turn.fast_done'; turn_id: number }
4047
+ | {
4048
+ type: 'voice.turn.fast_done';
4049
+ turn_id: number;
4050
+ /** Present (true) only when the on-device last resort answered the fast track. */
4051
+ local_last_resort?: true;
4052
+ /** The model the request was routed to, when the last resort stood in for it. */
4053
+ substituted_from?: string;
4054
+ /** Why `substituted_from` did not serve. */
4055
+ substitution_reason?:
4056
+ | 'credential_rejected'
4057
+ | 'credential_absent'
4058
+ | 'rate_limited'
4059
+ | 'quota_exhausted'
4060
+ | 'timed_out'
4061
+ | 'failed';
4062
+ }
4009
4063
  | {
4010
4064
  type: 'voice.turn.bridge';
4011
4065
  turn_id: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.57.4",
3
+ "version": "0.58.0",
4
4
  "carProtocolVersion": 3,
5
5
  "description": "Common Agent Runtime — a deterministic execution layer for AI agents",
6
6
  "main": "index.js",