car-runtime 0.47.0 → 0.48.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.
Files changed (2) hide show
  1. package/index.d.ts +183 -13
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -151,6 +151,28 @@ export class CarRuntime {
151
151
  /** Register a tool by name. */
152
152
  registerTool(name: string): Promise<void>;
153
153
 
154
+ /**
155
+ * The tools currently registered on this runtime, as a JSON array of full
156
+ * `ToolSchema` objects sorted by name.
157
+ *
158
+ * Counterpart to `registerTool` / `registerToolSchema`, which had none: a
159
+ * caller could add tools but never ask what was actually in effect, so a
160
+ * governed or read-only deployment could not prove "only these tools are
161
+ * callable". Sorted, so two calls with no registration in between are
162
+ * byte-identical and can be diffed.
163
+ */
164
+ listTools(): Promise<string>;
165
+
166
+ /**
167
+ * Remove a tool by name. Resolves to how many were removed — `0` means
168
+ * nothing matched, which is not an error, so cleanup can call this
169
+ * unconditionally.
170
+ *
171
+ * Drops the tool from both the runtime's registry and its schema map, so
172
+ * the model stops seeing it and the validator stops accepting it.
173
+ */
174
+ unregisterTool(name: string): Promise<number>;
175
+
154
176
  /** Register CAR's built-in agent utility tools. */
155
177
  registerAgentBasics(): Promise<void>;
156
178
 
@@ -399,6 +421,26 @@ export class CarRuntime {
399
421
  * dispatches the chosen components.
400
422
  */
401
423
  planEvolutionLive(requestJson: string): Promise<string>;
424
+ /**
425
+ * `supervision.subscribe` — register this connection as a supervisor of the
426
+ * admission gate: `{ filter?: { tools?, sessions?, min_reversibility? } }`.
427
+ *
428
+ * Intents arrive as `supervision.intent` NOTIFICATIONS on the same socket.
429
+ * A caller that cannot read notifications should poll `supervisionPending`
430
+ * instead — subscribing without consuming them blocks every supervised
431
+ * proposal until it fails closed.
432
+ */
433
+ supervisionSubscribe(requestJson: string): Promise<string>;
434
+ /**
435
+ * `supervision.unsubscribe` — stop supervising: `{}`. Intents already parked
436
+ * run out their timeout and fail closed rather than being released, so a
437
+ * supervisor cannot turn a pending deny into an allow by disconnecting.
438
+ */
439
+ supervisionUnsubscribe(requestJson: string): Promise<string>;
440
+ /** `supervision.pending` — every intent currently parked on a verdict: `{}`. */
441
+ supervisionPending(requestJson: string): Promise<string>;
442
+ /** `supervision.decide` — `{ intent_id, decision: { kind: "allow" | "deny" | "escalate", reason? } }`. */
443
+ supervisionDecide(requestJson: string): Promise<string>;
402
444
  /** `sync.status` — roster, journal frontier, stable frontier, state hash (B6). */
403
445
  syncStatus(requestJson: string): Promise<string>;
404
446
  /** `sync.append` — record an op on any surface: `{ surface, payload, scope? }` (B6). */
@@ -472,6 +514,27 @@ export class CarRuntime {
472
514
  */
473
515
  enforceSkillDeploymentLive(requestJson: string): Promise<string>;
474
516
 
517
+ /**
518
+ * Read the standing permission tier granted to this connection's daemon
519
+ * session (`read_only` | `sandbox_edit` | `full_access`) — the tier every
520
+ * {@link submitProposal} on this connection is judged against
521
+ * (Parslee-ai/car#890).
522
+ */
523
+ permissionGetTier(): Promise<string>;
524
+
525
+ /**
526
+ * Set this connection's standing permission tier and return the tier as the
527
+ * daemon now holds it. `tier` is `read_only` | `sandbox_edit` |
528
+ * `full_access`.
529
+ *
530
+ * Lets a binding client govern its own session — most usefully by tightening
531
+ * it: dropping to `read_only` makes the runtime escalate any write this
532
+ * client proposes to a human instead of running it. Raising the tier is
533
+ * host-gated whenever the daemon runs under a host token, so an agent
534
+ * connection cannot self-elevate.
535
+ */
536
+ permissionSetTier(tier: string): Promise<string>;
537
+
475
538
  /**
476
539
  * Ingest a skill through the deployment gate on the daemon (arXiv 2602.12430
477
540
  * "Agent Skills" — the loader integration). `requestJson` carries the skill
@@ -485,6 +548,21 @@ export class CarRuntime {
485
548
  */
486
549
  ingestSkillGoverned(requestJson: string): Promise<string>;
487
550
 
551
+ /**
552
+ * Adopt an installed skill pack on the daemon through the skill-trust
553
+ * deployment gate (arXiv 2602.12430 "Agent Skills" — the pack-adoption
554
+ * call-site). `requestJson` carries `pack` (an `ApprovedSkillPack`),
555
+ * `requested_tier?` (default `read_only`), and either `manifest?` — the signed
556
+ * bundle, whose signature trust is derived against the operator's
557
+ * `.car/config.toml` `trusted_skill_signers` keyring — or `provenance?`
558
+ * (caller-assembled), plus optional `scanned?`/`vulnerabilities?`/`source?`.
559
+ * Governance is unconditional: a denied skill never enters the graph. Returns
560
+ * `{ loaded, pending, refused, requested_tier, provenance, trusted_signers }`;
561
+ * a pending deny is resolved via `permission.approve`/`permission.reject` by
562
+ * the returned `fingerprint`, then re-adopted.
563
+ */
564
+ adoptSkillPack(requestJson: string): Promise<string>;
565
+
488
566
  /**
489
567
  * Save a learned skill with trigger context. Returns the node
490
568
  * index.
@@ -597,6 +675,13 @@ export class CarRuntime {
597
675
  * with reasoning suppressed to produce a direct answer, or
598
676
  * `"thinking_truncated"` when even that retry was empty (car-releases#60).
599
677
  *
678
+ * `auth_fallback_from` is present ONLY when a candidate earlier in the
679
+ * fallback chain was skipped because its credential was **rejected**
680
+ * (not merely absent) and a later model then answered. It names that
681
+ * dead lane, so a caller can tell the user their sign-in lapsed instead
682
+ * of silently serving a different model (Parslee-ai/car#888). Absent on
683
+ * the common path.
684
+ *
600
685
  * **Note:** intent is not exposed on the tracked path until the
601
686
  * positional argument list is converted to an options object —
602
687
  * this method already takes 9 positional parameters and adding
@@ -782,8 +867,24 @@ export class CarRuntime {
782
867
  /**
783
868
  * Unified registry (local + remote). Returns JSON array of
784
869
  * `{ id, name, provider, capabilities, param_count, size_mb,
785
- * context_length, available, is_local, max_output_tokens,
786
- * public_benchmarks, cost }`. `max_output_tokens` is the registry-declared
870
+ * context_length, available, is_local, weights_ready, downloads_weights,
871
+ * max_output_tokens, public_benchmarks, cost }`. `available` means CAR
872
+ * can use the model
873
+ * here — for a local MLX entry with a declared `hf_repo` it is `true`
874
+ * before a byte is fetched, because it lazy-downloads on first use —
875
+ * whereas `weights_ready` means the weights are already on disk (remote
876
+ * models, having none to install, report `true`). Older daemons omit
877
+ * `weights_ready`; it defaults to `false` rather than failing.
878
+ * `downloads_weights` is `true` only for entries whose weights CAR fetches
879
+ * before use (GGUF, MLX, whisper.cpp); when it is `false` — OS-provided
880
+ * models such as `windows/speech-synthesis:os` and
881
+ * `apple/foundation:default`, server-backed local models such as
882
+ * `vllm-mlx/*` and Ollama, and every remote entry — there is nothing to
883
+ * install, so `weights_ready` is meaningless and the CLI renders
884
+ * `INSTALLED` as `-`. Do not substitute `is_local`: those first four are
885
+ * all local and all download nothing. Older daemons omit
886
+ * `downloads_weights`; it defaults to `false` rather than failing.
887
+ * `max_output_tokens` is the registry-declared
787
888
  * per-model output ceiling (`null` when the entry omits it; callers
788
889
  * then fall back to a fraction of `context_length`).
789
890
  * `public_benchmarks` is `[{ name, score, harness?, source_url?,
@@ -968,10 +1069,10 @@ export class CarRuntime {
968
1069
  * error}` plus `needs_you` (`"contract" | "question" | "approval" | "auth" |
969
1070
  * null`), `needs_you_label` (the daemon-owned wording, so every client says
970
1071
  * the same thing), `question_prompt`, `auth_message`, `auth_wait_secs`,
971
- * `failure_kind` (`"budget_exhausted" | "auth_required" | "error"` when
972
- * failed), `worktree` (only when it still exists on disk), `project`,
973
- * `result_branch`, `model`, `discussion_id`, and `next_seq` (live only —
974
- * the `coder.subscribe` cursor).
1072
+ * `failure_kind` (`"budget_exhausted" | "auth_required" | "infrastructure" |
1073
+ * "error"` when failed), `worktree` (only when it still exists on disk),
1074
+ * `project`, `result_branch`, `model`, `discussion_id`, and `next_seq` (live
1075
+ * only — the `coder.subscribe` cursor).
975
1076
  *
976
1077
  * Pass `renew: true` for the lease-renewal form: it re-registers and answers
977
1078
  * `{ was_registered }` — `false` means this connection had been shed and
@@ -1528,10 +1629,25 @@ export class CarRuntime {
1528
1629
  /**
1529
1630
  * Execute a proposal through a CarRuntime with a JS tool callback.
1530
1631
  * The callback receives
1531
- * `{"tool":"name","params":{...},"action_id":"<id>"}` as a JSON
1532
- * string and must return a JSON string. `action_id` is the
1632
+ * `{"tool":"name","params":{...},"action_id":"<id>","request_id":"<id>","timeout_ms":<ms|null>,"session_id":"<id>|null","attempt":<n>}`
1633
+ *
1634
+ * `attempt` is the engine's retry counter, 1-based — which retry you are
1635
+ * serving. (Correlate a specific in-flight call by `request_id` instead.) It
1636
+ * was hardcoded to 1 on the wire and dropped here before car#928.
1637
+ *
1638
+ * `session_id` is the daemon-stamped execution session (car#904) — the
1639
+ * attribution key for which mission a callback belongs to. Null when the
1640
+ * caller has no session. Prefer it over reconstructing attribution from
1641
+ * `action_id`, which is client-authored and not unique across concurrent or
1642
+ * retried attempts.
1643
+ * as a JSON string and must return a JSON string. `action_id` is the
1533
1644
  * originating `Action.id` from the proposal — useful for routing
1534
1645
  * when the same callback closes over multiple in-flight calls.
1646
+ * `request_id` is the daemon's callback-routing id, which a
1647
+ * `tools.cancel` notification repeats so the host can abort the right
1648
+ * in-flight call. `timeout_ms` is the action's declared budget in
1649
+ * milliseconds when the action declared one (`null` otherwise); the
1650
+ * host's tool runner may use it to bound its own work.
1535
1651
  *
1536
1652
  * `sessionId`, when provided, scopes per-action policy validation to
1537
1653
  * the named session opened via `CarRuntime.openSession()`. Global
@@ -1749,6 +1865,13 @@ export function registerVoiceEventHandler(
1749
1865
  * Promise is logged, not surfaced to the host. Process-wide setter,
1750
1866
  * symmetric to `registerVoiceEventHandler`; pair with
1751
1867
  * `unregisterChatHandler` to clear.
1868
+ *
1869
+ * The handler may call any runtime method and should run the turn inline.
1870
+ * NAPI dispatches through a non-blocking `ThreadsafeFunction` and `chatEvent`
1871
+ * is async, so this side never had the reentrancy hazard that made the same
1872
+ * surface unusable from Python before Parslee-ai/car#905 — noted here because
1873
+ * the two bindings' handlers now carry the same contract for the same reason,
1874
+ * arrived at differently.
1752
1875
  */
1753
1876
  export function registerChatHandler(
1754
1877
  handlerFn: (paramsJson: string) => void,
@@ -1767,11 +1890,27 @@ export function unregisterChatHandler(): void;
1767
1890
  * this handler.
1768
1891
  *
1769
1892
  * `handlerFn(callJson)` receives
1770
- * `{"tool":"name","params":{...},"action_id":"<id>"}` as a JSON
1771
- * string and MUST return a Promise resolving to the tool's
1893
+ * `{"tool":"name","params":{...},"action_id":"<id>","request_id":"<id>","timeout_ms":<ms|null>,"session_id":"<id>|null","attempt":<n>}`
1894
+ *
1895
+ * `attempt` is the engine's retry counter, 1-based — which retry you are
1896
+ * serving. (Correlate a specific in-flight call by `request_id` instead.) It
1897
+ * was hardcoded to 1 on the wire and dropped here before car#928.
1898
+ *
1899
+ * `session_id` is the daemon-stamped execution session (car#904) — the
1900
+ * attribution key for which mission a callback belongs to. Null when the
1901
+ * caller has no session. Prefer it over reconstructing attribution from
1902
+ * `action_id`, which is client-authored and not unique across concurrent or
1903
+ * retried attempts.
1904
+ * as a JSON string and MUST return a Promise resolving to the tool's
1772
1905
  * JSON-encoded result. Throwing rejects the daemon-side action
1773
1906
  * with a -32000 JSON-RPC error.
1774
1907
  *
1908
+ * `request_id` is the daemon's callback-routing id, repeated by the
1909
+ * `tools.cancel` notification so the host can abort the right
1910
+ * in-flight call. `timeout_ms` is the action's declared budget in
1911
+ * milliseconds when the action declared one (`null` otherwise); the
1912
+ * host's tool runner may use it to bound its own work.
1913
+ *
1775
1914
  * `action_id` carries the originating `Action.id` from the
1776
1915
  * proposal so process-wide handlers can route concurrent
1777
1916
  * callbacks back to the right per-call closure. Empty string when
@@ -2878,6 +3017,16 @@ export function simulateWithPredictions(
2878
3017
  * `state_consistency` (changes, snapshots, rollbacks), `safety`
2879
3018
  * (permission escalations/denials/approvals), and `replayability` — to
2880
3019
  * complement task-success accuracy when comparing harness variants.
3020
+ *
3021
+ * The optional `task_pass_rate` field — and its two companions,
3022
+ * `task_pass_denominator` (how many tasks that rate is over) and
3023
+ * `tasks_unrunnable` (how many the runner could not measure) — are **always
3024
+ * absent** from this result: end-task success is not in the event stream (the
3025
+ * log records what ran, not whether the task was satisfied), and inventing any
3026
+ * of the three here would hand the regression gate a fabricated number. Only a
3027
+ * runner holding the task suite and its grading criteria can supply them —
3028
+ * `car-bench-harness --metrics-out` does. Absent means *not measured*, never
3029
+ * zero.
2881
3030
  */
2882
3031
  export function harnessMetrics(eventsJsonl: string): string;
2883
3032
 
@@ -2953,9 +3102,30 @@ export function evolutionDiagnose(
2953
3102
  * `HarnessMutation`; `baselineJson`/`candidateJson` are `HarnessMetrics`
2954
3103
  * measured before/after applying it on held-out telemetry. Returns the
2955
3104
  * `PromotionDecision` JSON `{ decision: "promote" | "needs_approval" |
2956
- * "reject", reason }` — a mutation is promoted only if its target improved
2957
- * without regressing guarded metrics; safety-affecting mutations route to
2958
- * `needs_approval` even when they pass.
3105
+ * "reject" | "incomparable", reason }` — a mutation is promoted only if its
3106
+ * target improved without regressing guarded metrics; safety-affecting
3107
+ * mutations route to `needs_approval` even when they pass.
3108
+ *
3109
+ * Reliability is guarded twice, because the two available measures are
3110
+ * different quantities. `task_pass_rate` is end-task success and is checked
3111
+ * first, but only when BOTH documents carry it (absent on either side = not
3112
+ * measured, so the guard does not fire rather than defaulting to 0.0 or 1.0).
3113
+ * `trajectory_efficiency.success_rate` is tool-attempt success and is always
3114
+ * checked. A candidate that cuts tokens by abandoning hard tasks earlier holds
3115
+ * a perfect attempt-level rate while solving fewer tasks — only the first guard
3116
+ * sees that.
3117
+ *
3118
+ * Before either guard, the two pass rates must be over the SAME task set.
3119
+ * `HarnessMetrics` carries two optional companions to `task_pass_rate`:
3120
+ * `task_pass_denominator` (how many tasks the rate is over) and
3121
+ * `tasks_unrunnable` (how many the runner could not measure). When both
3122
+ * documents carry a denominator and they differ, the result is
3123
+ * `incomparable` — no verdict, nothing applied. That is not pedantry: a
3124
+ * harness that loses a capability also loses the ability to *measure* the
3125
+ * tasks needing it, so those tasks leave the denominator and the surviving
3126
+ * rate goes up. Both fields are optional and absent from documents written
3127
+ * before they existed, in which case the check is skipped rather than
3128
+ * failing.
2959
3129
  */
2960
3130
  export function evolutionEvaluate(
2961
3131
  mutationJson: string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "description": "Common Agent Runtime — a deterministic execution layer for AI agents",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",