car-runtime 0.25.0 → 0.27.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 +175 -13
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -49,6 +49,19 @@ export class CarRuntime {
49
49
  */
50
50
  loadMemory(path: string): Promise<number>;
51
51
 
52
+ /**
53
+ * Stream one delta of a chat turn back to the daemon as an
54
+ * `agent.chat.event` notification (the agent-chat surface). Called from an
55
+ * `agent.chat` handler (see `registerChatHandler`) to emit the reply
56
+ * incrementally; the daemon rewrites each event to `agents.chat.event` for
57
+ * the host that issued `agents.chat`, keyed by `sessionId`.
58
+ *
59
+ * `kind` is one of `token` | `tool_call` | `done` | `error`. `delta` carries
60
+ * the text for `token` (and the final text/status for `done`/`error`); omit
61
+ * it for a bare signal.
62
+ */
63
+ chatEvent(sessionId: string, kind: string, delta?: string | undefined | null): Promise<void>;
64
+
52
65
  /**
53
66
  * Persist memory graph to a JSON file (backward-compatible flat format).
54
67
  * Returns the number of records written.
@@ -667,12 +680,30 @@ export class CarRuntime {
667
680
  /** Set update preferences (JSON `UpdatePreferences` shape). Returns stored prefs JSON. */
668
681
  updatePrefsSet(prefsJson: string): Promise<string>;
669
682
 
670
- /** Route a prompt. Returns the routing decision as JSON. */
671
- routeModel(prompt: string): Promise<string>;
683
+ /**
684
+ * Route a prompt. Returns the routing decision as JSON, including
685
+ * `candidates`: the advisory ranking of every scored model
686
+ * (`{ model_id, reliability, score, selected, in_band }`) so callers can see
687
+ * why a model won and what the alternatives cost in reliability terms. Empty
688
+ * on explicit-model and cold-start paths where no ranking occurred.
689
+ */
690
+ routeModel(prompt: string, intentJson?: string | null): Promise<string>;
672
691
 
673
692
  /** Per-model performance profiles. Returns JSON. */
674
693
  modelStats(): Promise<string>;
675
694
 
695
+ /**
696
+ * Persistent outcome scoreboard, folded from the durable outcome ledger.
697
+ * Returns JSON `{ rows: [{ model_id, success_count, fail_count,
698
+ * inconclusive_count, total_input_tokens, total_output_tokens, avg_quality,
699
+ * avg_latency_ms, success_rate, tokens_per_success, usd_per_success }],
700
+ * total_successes, total_failures, total_inconclusive, total_usd,
701
+ * overall_usd_per_success, model_count, receipts }`. Rows are sorted
702
+ * cheapest-correct-outcome first. The cross-session, outcome-denominated view
703
+ * (unlike `modelStats`, the live in-memory profiles).
704
+ */
705
+ outcomeScoreboard(): Promise<string>;
706
+
676
707
  // --- Execution ---
677
708
 
678
709
  /** Count of events in this runtime's execution log. */
@@ -1081,6 +1112,15 @@ export interface IntentHint {
1081
1112
  * then `prefer_quality`, then `prefer_local`.
1082
1113
  */
1083
1114
  prefer_quality?: boolean;
1115
+ /**
1116
+ * The operation is high-stakes — consequential or irreversible (e.g. the
1117
+ * session is authorized for FullAccess actions). Forces the strongest
1118
+ * quality posture regardless of task or any cost/latency preference: never
1119
+ * economize on what you can't take back. Highest precedence — wins over
1120
+ * `prefer_fast`, `prefer_quality`, and `prefer_local`. The daemon sets this
1121
+ * automatically for FullAccess-granted sessions.
1122
+ */
1123
+ high_stakes?: boolean;
1084
1124
  }
1085
1125
 
1086
1126
  // --- Voice streaming (stored-callback pattern) ---
@@ -1155,6 +1195,32 @@ export function registerVoiceEventHandler(
1155
1195
  onEvent: (sessionId: string, eventJson: string) => void,
1156
1196
  ): void;
1157
1197
 
1198
+ /**
1199
+ * Register the JS handler that serves daemon-initiated `agent.chat`
1200
+ * reverse-calls — the agent-chat surface. A supervised agent (running in
1201
+ * `--serve` mode, attached via `session.auth`) calls this once; the daemon
1202
+ * reverse-calls `agent.chat` for every host `agents.chat`, the bridge acks
1203
+ * `{accepted:true}` immediately, and fires this handler.
1204
+ *
1205
+ * `handlerFn(paramsJson)` receives `{"session_id":"...","prompt":"...",
1206
+ * "attachments":[...]?,"context":{...}?}` as a JSON string. Run one
1207
+ * conversational turn — keep a per-`session_id` message thread, run the
1208
+ * agent loop, and stream the reply back via `CarRuntime.chatEvent` — then
1209
+ * return. It is fire-and-forget (the ack already went back), so a rejected
1210
+ * Promise is logged, not surfaced to the host. Process-wide setter,
1211
+ * symmetric to `registerVoiceEventHandler`; pair with
1212
+ * `unregisterChatHandler` to clear.
1213
+ */
1214
+ export function registerChatHandler(
1215
+ handlerFn: (paramsJson: string) => void,
1216
+ ): void;
1217
+
1218
+ /**
1219
+ * Clear the registered `agent.chat` handler. Subsequent reverse-calls are
1220
+ * refused so the daemon learns this agent is no longer conversational.
1221
+ */
1222
+ export function unregisterChatHandler(): void;
1223
+
1158
1224
  /**
1159
1225
  * Register the JS `tools.execute` handler for `submitProposal`
1160
1226
  * (Parslee-ai/car-releases#38). When the daemon dispatches a
@@ -1401,6 +1467,13 @@ export function resumeWorkflow(pausedJson: string, inputJson: string): Promise<s
1401
1467
  /** Static analysis on a workflow definition. Returns verification report JSON. */
1402
1468
  export function verifyWorkflow(workflowJson: string): string;
1403
1469
 
1470
+ /**
1471
+ * Build the external-item automation recipe (poll → dedup → per-item agent →
1472
+ * deliver) from an `AutomationSpec` JSON into a runnable workflow JSON. Hand the
1473
+ * result to `runWorkflow`, typically on a schedule. Stateless.
1474
+ */
1475
+ export function buildAutomationWorkflow(specJson: string): string;
1476
+
1404
1477
  export interface StartMeetingResponse {
1405
1478
  id: string;
1406
1479
  title: string;
@@ -1486,12 +1559,19 @@ export function reapStaleAgents(
1486
1559
  // --- car-a2a server lifecycle ---
1487
1560
  //
1488
1561
  // Expose CAR as an Agent2Agent (A2A) v1.0 peer programmatically,
1489
- // without shelling out to `car-server --a2a-bind`. Process-global
1490
- // state holds the bound listener and join handle so a later
1491
- // `stopA2aServer` / `a2aServerStatus` call reaches the right server.
1562
+ // without shelling out to `car-server --a2a-bind`. These proxy to the
1563
+ // daemon the `CarRuntime` is connected to, so each takes the runtime as
1564
+ // its first argument.
1565
+ //
1566
+ // NOTE ON NAMING: napi-rs (heck) splits at digit→letter boundaries, so
1567
+ // the Rust `start_a2a_server` camelCases to `startA2AServer` (the `a2a`
1568
+ // segment becomes `A2A`), NOT `startA2aServer`. The exported names are
1569
+ // `startA2AServer` / `stopA2AServer` / `a2AServerStatus` /
1570
+ // `sendA2AMessage` / `a2ADispatch`. (Earlier versions of this file
1571
+ // declared `…A2a…`, which did not exist at runtime — car-releases#65.)
1492
1572
 
1493
1573
  /**
1494
- * Start an A2A listener.
1574
+ * Start an A2A listener on the daemon `rt` is connected to.
1495
1575
  *
1496
1576
  * `paramsJson` shape:
1497
1577
  * ```jsonc
@@ -1510,31 +1590,49 @@ export function reapStaleAgents(
1510
1590
  * the calling `CarRuntime`'s session runtime instead of spawning a
1511
1591
  * fresh one. Tools registered on the session via
1512
1592
  * `registerToolSchema` then appear on the Agent Card's `skills`
1513
- * list, and A2A peer `message/send` calls for those tools route
1514
- * back to the handler installed via `registerToolHandler`. This is
1593
+ * list, and A2A peer `message/send` calls that carry an explicit
1594
+ * tool invocation (a `data` part `{ "tool": "...", "parameters": {...} }`)
1595
+ * route back to the handler installed via `registerToolHandler`. This is
1515
1596
  * the canonical path for host-language agents to project themselves
1516
1597
  * over A2A. Default `false` preserves the legacy fresh-Runtime
1517
1598
  * behaviour (only `register_agent_basics` tools, dispatch in Rust).
1518
1599
  *
1600
+ * A purely *conversational* `message/send` (free text, no tool `data`
1601
+ * part) is routed to the host's `agent.chat` handler — the daemon
1602
+ * reverse-calls `agent.chat` on this session, aggregates the streamed
1603
+ * reply, and returns it as the A2A agent message (car-releases#65). So
1604
+ * register an `agent.chat` handler to serve conversational turns, and/or
1605
+ * a `registerToolHandler` for explicit tool `data` parts.
1606
+ *
1519
1607
  * Returns `'{"bound":"127.0.0.1:8731"}'` on success. Errors if a
1520
1608
  * server is already running, the bind fails, `share_session_runtime`
1521
1609
  * is set but no session runtime is available (e.g. invoked from a
1522
1610
  * non-WS path), or `paramsJson` is malformed.
1523
1611
  */
1524
- export function startA2aServer(paramsJson: string): Promise<string>;
1612
+ export function startA2AServer(rt: CarRuntime, paramsJson: string): Promise<string>;
1525
1613
 
1526
1614
  /**
1527
1615
  * Stop the running A2A listener. Returns `'{"stopped":true}'` on
1528
1616
  * success. Errors if no server is running.
1529
1617
  */
1530
- export function stopA2aServer(): string;
1618
+ export function stopA2AServer(rt: CarRuntime): Promise<string>;
1531
1619
 
1532
1620
  /**
1533
1621
  * Report whether the A2A listener is up. Always returns a JSON
1534
1622
  * object — `{"running":true,"bound":"...","uptime_secs":N}` when
1535
1623
  * running, `{"running":false}` otherwise.
1536
1624
  */
1537
- export function a2aServerStatus(): string;
1625
+ export function a2AServerStatus(rt: CarRuntime): Promise<string>;
1626
+
1627
+ /**
1628
+ * Send a message to a remote A2A peer at `endpoint`.
1629
+ *
1630
+ * `paramsJson` shape: `{ endpoint, message, blocking?, ingest_a2ui?,
1631
+ * route_auth?, allow_untrusted_endpoint? }`. The daemon enforces a
1632
+ * loopback-or-explicit-allow rule on `endpoint` — non-loopback URLs
1633
+ * require `allow_untrusted_endpoint: true`.
1634
+ */
1635
+ export function sendA2AMessage(rt: CarRuntime, paramsJson: string): Promise<string>;
1538
1636
 
1539
1637
  // --- Verification (stateless) ---
1540
1638
 
@@ -1575,6 +1673,13 @@ export function optimize(proposalJson: string): string;
1575
1673
 
1576
1674
  export function equivalent(proposal1Json: string, proposal2Json: string): boolean;
1577
1675
 
1676
+ /**
1677
+ * Wire protocol version this binding speaks to the `car-server` daemon,
1678
+ * exchanged via the `server.handshake` RPC. Bumped only on a
1679
+ * backward-incompatible JSON-RPC change — independent of the package semver.
1680
+ */
1681
+ export function protocolVersion(): number;
1682
+
1578
1683
  /**
1579
1684
  * Check a proposal for transactional conflicts against the current shared
1580
1685
  * state (survey "Code as Agent Harness" §4.3/§5.2.4 — the shared
@@ -1844,6 +1949,34 @@ export function createTask(
1844
1949
  systemPrompt?: string | null,
1845
1950
  ): string;
1846
1951
 
1952
+ /**
1953
+ * Preview the durable OS-level schedule (launchd plist + crontab line) a task
1954
+ * would install, without installing it. `program` + `argsJson` (a JSON string
1955
+ * array) are the command the OS runs to execute the task once. Returns
1956
+ * `{ label, launchdPlist, launchdError, crontabLine, crontabError }`.
1957
+ */
1958
+ export function renderOsSchedule(taskJson: string, program: string, argsJson: string): string;
1959
+
1960
+ /**
1961
+ * Install a durable OS-level schedule (launchd on macOS, crontab on Linux) so
1962
+ * the task fires even when the CAR daemon is down. Idempotent. Returns the
1963
+ * installed-schedule JSON.
1964
+ */
1965
+ export function installOsSchedule(taskJson: string, program: string, argsJson: string): string;
1966
+
1967
+ /** Remove a task's OS-level schedule (full label or bare task id). Returns `{ label, removed }`. */
1968
+ export function uninstallOsSchedule(labelOrId: string): string;
1969
+
1970
+ /** List labels of all CAR-managed OS-level schedules on this host (JSON string array). */
1971
+ export function listOsSchedules(): string;
1972
+
1973
+ /**
1974
+ * Reap orphaned OS-level schedules — uninstall every CAR-managed launchd/cron
1975
+ * entry whose task is gone from `~/.car/tasks/` or whose trigger is no longer
1976
+ * schedulable. Returns the reconcile report `{ removed, kept, errors }`.
1977
+ */
1978
+ export function reconcileOsSchedules(): string;
1979
+
1847
1980
  /** Run a task once using the registered agent runner. */
1848
1981
  export function runTask(taskJson: string): Promise<string>;
1849
1982
 
@@ -1972,7 +2105,7 @@ export function visionOcr(argsJson: string): Promise<string>;
1972
2105
  * both in one process gives you two task stores (task ids are
1973
2106
  * unique per dispatcher).
1974
2107
  */
1975
- export function a2aDispatch(method: string, paramsJson: string): Promise<string>;
2108
+ export function a2ADispatch(rt: CarRuntime, method: string, paramsJson: string): Promise<string>;
1976
2109
 
1977
2110
  // --- Lifecycle-managed agents (car_registry::supervisor) ---
1978
2111
 
@@ -2077,6 +2210,20 @@ export function agentsStop(id: string, signal?: string | null): Promise<string>;
2077
2210
  */
2078
2211
  export function agentsRestart(id: string): Promise<string>;
2079
2212
 
2213
+ /**
2214
+ * Block until a managed agent reaches one of `targetsJson` (a JSON string array
2215
+ * of statuses like `["running"]` or `["stopped","errored"]`; default
2216
+ * `["running"]`) or `timeoutSecs` (default 30) elapses, polling every `pollMs`
2217
+ * (default 200). Returns the matching `ManagedAgent` JSON; rejects on timeout or
2218
+ * unknown id.
2219
+ */
2220
+ export function agentsWait(
2221
+ id: string,
2222
+ targetsJson?: string | null,
2223
+ timeoutSecs?: number | null,
2224
+ pollMs?: number | null,
2225
+ ): Promise<string>;
2226
+
2080
2227
  /**
2081
2228
  * Read a window of an agent's logs under
2082
2229
  * `~/.car/logs/<id>.{stdout,stderr}.log`.
@@ -2193,12 +2340,26 @@ export function agentsHealthExternal(
2193
2340
  * "allowed_tools"?: string[], // tool allowlist; [] denies all
2194
2341
  * "max_turns"?: number, // turn cap
2195
2342
  * "timeout_secs"?: number, // hard deadline (default 300s)
2196
- * "mcp_endpoint"?: string // MCP server URL passed via
2343
+ * "mcp_endpoint"?: string, // MCP server URL passed via
2197
2344
  * // --mcp-config; daemon callers
2198
2345
  * // auto-fill from car-server's
2199
2346
  * // bound /mcp URL. "" opts out.
2347
+ * "attachments"?: [ // images attached to the prompt
2348
+ * { "path": string, // abs path on the daemon's fs
2349
+ * "media_type"?: string } // advisory; the runner derives
2350
+ * ] // the real type from content
2200
2351
  * }
2201
2352
  *
2353
+ * `attachments` are image files the runner hands to the CLI in its
2354
+ * native form: Claude Code reads + inlines a base64 image block on
2355
+ * stdin, Codex passes each via `--image`, Gemini references it with
2356
+ * `@path`. Paths must be readable by the daemon process. The runner
2357
+ * caps reads at 32 MB and (on the read/stage paths) verifies the bytes
2358
+ * are a real image by magic signature, deriving `media_type` from
2359
+ * content — non-image / oversized / unreadable paths are skipped, so a
2360
+ * file that isn't an image is never inlined. Adapters whose CLI lacks
2361
+ * image input ignore them.
2362
+ *
2202
2363
  * Returns JSON `InvokeResult`:
2203
2364
  *
2204
2365
  * {
@@ -2208,6 +2369,7 @@ export function agentsHealthExternal(
2208
2369
  * "tool_calls": number, // tool_use blocks observed
2209
2370
  * "duration_ms": number,
2210
2371
  * "total_cost_usd"?: number, // would-be API cost (subscription users don't pay)
2372
+ * "dropped_attachments"?: number, // images dropped (unreadable/oversized/not an image); omitted when 0
2211
2373
  * "is_error": boolean,
2212
2374
  * "error"?: string
2213
2375
  * }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.25.0",
3
+ "version": "0.27.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",