car-runtime 0.5.1 → 0.6.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 +534 -19
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -3,6 +3,30 @@
3
3
  * Most methods that return structured data return a JSON-encoded string; the
4
4
  * caller is expected to `JSON.parse` the result. This keeps the FFI surface
5
5
  * small and avoids coupling the native binding to any specific TS shape.
6
+ *
7
+ * ## Runtime modes (closes #139's cross-process admission gap)
8
+ *
9
+ * `new CarRuntime()` runs in one of two modes, selected at construction:
10
+ *
11
+ * - **`daemon` (default)** — talks to the singleton car-server daemon over
12
+ * WebSocket JSON-RPC. Methods that touch shared resources (inference,
13
+ * embeddings, state, verification) proxy to the daemon — preserving the
14
+ * process-wide admission semaphore so two Node consumers don't double-load
15
+ * models or oversubscribe the GPU.
16
+ * - **`embedded`** — explicit opt-in via `CAR_FFI_MODE=embedded`. Every
17
+ * method runs in-process. Useful for notebooks, offline dev, or single-
18
+ * process deployments where the caller knows they own the machine.
19
+ * Production embedders generally don't want this.
20
+ *
21
+ * Some methods are **embedded-only by design** because their callback
22
+ * surface (`ThreadsafeFunction` for tools, streaming events) doesn't
23
+ * survive a network boundary cleanly. Those throw in `daemon` mode with a
24
+ * clear message: `executeProposal`, `inferStream`. For streaming inference
25
+ * under the singleton-daemon contract, talk to `ws://127.0.0.1:9100/`
26
+ * directly.
27
+ *
28
+ * Daemon URL override: `CAR_DAEMON_URL=ws://...` (default
29
+ * `ws://127.0.0.1:9100`).
6
30
  */
7
31
 
8
32
  /** Persistent runtime instance with state, memory, tools, and policies. */
@@ -61,19 +85,40 @@ export class CarRuntime {
61
85
 
62
86
  // --- Memory / Facts (graph-backed) ---
63
87
 
64
- /** Add a fact. `kind` is typically "pattern" or "constraint". */
65
- addFact(subject: string, body: string, kind: string, confidence?: number | null): number;
88
+ /**
89
+ * Add a fact. `kind` is typically "pattern" or "constraint".
90
+ *
91
+ * In Daemon mode, rejects with the daemon-unreachable error
92
+ * instead of silently returning 0 (#146).
93
+ */
94
+ addFact(
95
+ subject: string,
96
+ body: string,
97
+ kind: string,
98
+ confidence?: number | null,
99
+ ): Promise<number>;
66
100
 
67
101
  /** Query facts via graph spreading activation. Returns a JSON array. */
68
102
  queryFacts(query: string, k?: number | null): string;
69
103
 
70
- factCount(): number;
104
+ /**
105
+ * Total valid fact count.
106
+ *
107
+ * In Daemon mode, hits the daemon's per-session memgine — the
108
+ * embedded fallback memgine stays empty by design and would
109
+ * silently return 0 (#146). Rejects with the daemon-unreachable
110
+ * error instead.
111
+ */
112
+ factCount(): Promise<number>;
71
113
 
72
114
  /**
73
115
  * Build the full 4-layer context for a query.
74
116
  * When `modelContextWindow` is provided, dynamically sizes the budget.
117
+ *
118
+ * In Daemon mode, rejects with the daemon-unreachable error
119
+ * instead of silently returning "" (#146).
75
120
  */
76
- buildContext(query: string, modelContextWindow?: number | null): string;
121
+ buildContext(query: string, modelContextWindow?: number | null): Promise<string>;
77
122
 
78
123
  /**
79
124
  * Build context in Fast mode for latency-sensitive paths.
@@ -86,7 +131,14 @@ export class CarRuntime {
86
131
 
87
132
  // --- Skills ---
88
133
 
89
- /** Save a learned skill with trigger context. Returns the node index. */
134
+ /**
135
+ * Save a learned skill with trigger context. Returns the node
136
+ * index.
137
+ *
138
+ * In Daemon mode, rejects with the daemon-unreachable error
139
+ * (or a parse error if the response is malformed) instead of
140
+ * silently returning 0 (#146).
141
+ */
90
142
  ingestSkill(
91
143
  name: string,
92
144
  code: string,
@@ -96,7 +148,7 @@ export class CarRuntime {
96
148
  taskKeywords: string[],
97
149
  description: string,
98
150
  supersedesSkill?: string | null,
99
- ): number;
151
+ ): Promise<number>;
100
152
 
101
153
  /** Find best matching skill for context. Returns JSON or `"null"`. */
102
154
  findSkill(
@@ -129,12 +181,33 @@ export class CarRuntime {
129
181
 
130
182
  // --- Inference ---
131
183
 
132
- /** Generate text. Returns JSON: `{"text":"..."}`. */
133
- infer(prompt: string, model?: string | null, maxTokens?: number | null): Promise<string>;
184
+ /**
185
+ * Generate text. Returns JSON: `{"text":"..."}`.
186
+ *
187
+ * `intentJson` is an optional serialized {@link IntentHint} —
188
+ * caller-facing routing hints (task, prefer_local, require). Omit to
189
+ * preserve the existing adaptive vs. pinned-model behavior.
190
+ */
191
+ infer(
192
+ prompt: string,
193
+ model?: string | null,
194
+ maxTokens?: number | null,
195
+ intentJson?: string | null,
196
+ ): Promise<string>;
134
197
 
135
198
  /**
136
- * Generate with full tracking: text, tool_calls, usage, model_used, latency_ms.
137
- * Returns JSON.
199
+ * Generate with full tracking. Returns JSON with `text`, `tool_calls`,
200
+ * `usage`, `model_used`, `latency_ms`, `time_to_first_token_ms`,
201
+ * `trace_id`. `time_to_first_token_ms` is wall-clock to the first
202
+ * sampled token (populated by local Candle/MLX paths; `null` for
203
+ * non-streaming remote calls).
204
+ *
205
+ * **Note:** intent is not exposed on the tracked path until the
206
+ * positional argument list is converted to an options object —
207
+ * this method already takes 8 positional parameters and adding a
208
+ * 9th would push call sites past readability. For new code, use
209
+ * {@link inferTrackedWithRequest} which takes a JSON-stringified
210
+ * `GenerateRequest` and exposes every field including `intent`.
138
211
  */
139
212
  inferTracked(
140
213
  prompt: string,
@@ -147,11 +220,24 @@ export class CarRuntime {
147
220
  parallelToolCalls?: boolean | null,
148
221
  ): Promise<string>;
149
222
 
150
- /** Generate text grounded with memory context from this runtime's memgine. */
223
+ /**
224
+ * Generate with full tracking, options-object form.
225
+ * `requestJson` is a `JSON.stringify`d `GenerateRequest` (every
226
+ * field optional except `prompt`). Exposes every field on the
227
+ * Rust struct including `intent`. Same pattern as
228
+ * {@link verifyProposal}. Closes #107.
229
+ */
230
+ inferTrackedWithRequest(requestJson: string): Promise<string>;
231
+
232
+ /**
233
+ * Generate text grounded with memory context from this runtime's
234
+ * memgine. `intentJson` works the same as on {@link infer}.
235
+ */
151
236
  inferWithContext(
152
237
  prompt: string,
153
238
  model?: string | null,
154
239
  maxTokens?: number | null,
240
+ intentJson?: string | null,
155
241
  ): Promise<string>;
156
242
 
157
243
  /** Embed texts. Returns JSON array of float arrays. */
@@ -172,6 +258,20 @@ export class CarRuntime {
172
258
  /** Classify text against labels. Returns JSON array of `{label, score}`. */
173
259
  classify(text: string, labels: string[], model?: string | null): Promise<string>;
174
260
 
261
+ /**
262
+ * Encode `text` via the named local model's tokenizer. Returns a JSON
263
+ * array of u32 token IDs, raw (no chat-template wrapping, no BOS).
264
+ * Pair with `detokenize` for byte-identical round-trip. Remote models
265
+ * are not supported — call rejects with an error there.
266
+ */
267
+ tokenize(model: string, text: string): Promise<string>;
268
+
269
+ /**
270
+ * Decode token IDs back to text via the named local model's tokenizer.
271
+ * Inverse of `tokenize` for the round-trip property.
272
+ */
273
+ detokenize(model: string, tokens: number[]): Promise<string>;
274
+
175
275
  // --- Speech ---
176
276
 
177
277
  /** Prepare the managed speech runtime and return its root path. */
@@ -215,7 +315,14 @@ export class CarRuntime {
215
315
  /** Remove a downloaded model. */
216
316
  removeModel(name: string): void;
217
317
 
218
- /** Unified registry (local + remote). Returns JSON array. */
318
+ /**
319
+ * Unified registry (local + remote). Returns JSON array of
320
+ * `{ id, name, provider, capabilities, param_count, size_mb,
321
+ * context_length, available, is_local, public_benchmarks }`.
322
+ * `public_benchmarks` is `[{ name, score, harness?, source_url?,
323
+ * measured_at? }]` with score on a 0.0–1.0 scale; ships empty in
324
+ * the built-in catalog and is populated via curated registry data.
325
+ */
219
326
  listModelsUnified(): string;
220
327
 
221
328
  /** Register a model schema (persists to `~/.car/models.json`). */
@@ -248,12 +355,167 @@ export class CarRuntime {
248
355
  * Supported ops: navigate, observe, click, type, scroll, keypress, wait.
249
356
  * Returns JSON: `{steps:[{op,status,data,error,duration_ms}]}`. Execution
250
357
  * short-circuits on first error.
358
+ *
359
+ * `headed`: when `true`, launches a visible Chromium window
360
+ * instead of headless mode — for interactive flows like
361
+ * first-time auth (LinkedIn / OAuth / SSO / 2FA / captcha).
362
+ * Honoured only on the *first* call that launches the session;
363
+ * subsequent calls reuse the existing browser regardless. To
364
+ * switch modes, call `browserClose()` first.
365
+ *
366
+ * `extraArgs`: extra Chromium command-line flags appended
367
+ * verbatim to argv at launch (#112). Use cases: the Google
368
+ * Meet bot needing `--use-fake-ui-for-media-stream`,
369
+ * `--autoplay-policy=no-user-gesture-required`, and the
370
+ * container-friendly `--no-sandbox` /
371
+ * `--disable-dev-shm-usage` / `--disable-setuid-sandbox`. Like
372
+ * `headed`, honoured only on the launch call.
251
373
  */
252
374
  browserRun(
253
375
  scriptJson: string,
254
376
  width?: number | null,
255
377
  height?: number | null,
378
+ headed?: boolean | null,
379
+ extraArgs?: string[] | null,
256
380
  ): Promise<string>;
381
+
382
+ /** Close any persistent browser session attached to this runtime. */
383
+ browserClose(): Promise<void>;
384
+
385
+ /**
386
+ * Register a tool with a full JSON-serialized `ToolSchema`.
387
+ * `verifyProposal` validates `Action.parameters` against the schema's
388
+ * `parameters` field — catching type mismatches like `{path: 42}` for a
389
+ * tool wanting `{path: string}` before dispatch. The schema also carries
390
+ * idempotency, cache TTL, and rate-limit hints that the engine wires up
391
+ * automatically.
392
+ *
393
+ * Tools registered via the schemaless `registerTool(name)` bypass type
394
+ * validation; this is the opt-in upgrade path.
395
+ *
396
+ * `schemaJson` matches:
397
+ * ```json
398
+ * {
399
+ * "name": "read_file",
400
+ * "description": "...",
401
+ * "parameters": {"type":"object","properties":{"path":{"type":"string"}},"required":["path"]},
402
+ * "returns": null,
403
+ * "idempotent": true,
404
+ * "cache_ttl_secs": 60,
405
+ * "rate_limit": {"max_calls": 100, "interval_secs": 60}
406
+ * }
407
+ * ```
408
+ */
409
+ registerToolSchema(schemaJson: string): Promise<void>;
410
+
411
+ // -------------------------------------------------------------------------
412
+ // OS-native secret store (Keychain / Credential Manager / Secret Service)
413
+ // -------------------------------------------------------------------------
414
+
415
+ /** Returns JSON `{available: boolean, reason?: string}`. */
416
+ secretAvailable(): string;
417
+
418
+ /**
419
+ * Store a secret. Returns JSON `{ok: true}` on success. Throws if the
420
+ * backend is unavailable. `service` defaults to the runtime-wide bundle id.
421
+ */
422
+ secretPut(key: string, value: string, service?: string | null): string;
423
+
424
+ /**
425
+ * Retrieve a secret. Returns JSON `{value: string}`. Throws `not_found`
426
+ * if the secret does not exist.
427
+ */
428
+ secretGet(key: string, service?: string | null): string;
429
+
430
+ /** Delete a secret (idempotent). Returns JSON `{ok: true}`. */
431
+ secretDelete(key: string, service?: string | null): string;
432
+
433
+ /** Returns JSON `{exists: boolean, key, service}` without exposing the value. */
434
+ secretStatus(key: string, service?: string | null): string;
435
+
436
+ // -------------------------------------------------------------------------
437
+ // OS permission preflight
438
+ // -------------------------------------------------------------------------
439
+
440
+ /** Returns JSON `{domains: [...]}` listing every permission domain CAR knows. */
441
+ permissionDomains(): string;
442
+
443
+ /** Returns JSON `{status: "granted"|"denied"|"prompt"|"unsupported", ...}`. */
444
+ permissionStatus(domain: string, targetBundleId?: string | null): string;
445
+
446
+ /** Triggers the OS permission prompt; returns the resulting status. */
447
+ permissionRequest(domain: string, targetBundleId?: string | null): string;
448
+
449
+ /** Returns JSON describing what the permission unlocks and how to revoke it. */
450
+ permissionExplain(domain: string, targetBundleId?: string | null): string;
451
+
452
+ // -------------------------------------------------------------------------
453
+ // Native account discovery (system Settings → Internet Accounts on macOS)
454
+ // -------------------------------------------------------------------------
455
+
456
+ /** Returns JSON array of accounts known to the OS. */
457
+ accountsList(): string;
458
+
459
+ /** Open the OS's native account-management UI for an account or the root pane. */
460
+ accountsOpen(accountId?: string | null): string;
461
+
462
+ // -------------------------------------------------------------------------
463
+ // Calendar / Contacts / Mail integrations (delegated to OS providers)
464
+ // -------------------------------------------------------------------------
465
+
466
+ /** Returns JSON array of calendars discovered through the OS provider. */
467
+ calendarList(): string;
468
+
469
+ /**
470
+ * Returns JSON array of events in the [start, end] window. Times are
471
+ * RFC3339; `calendarIdsCsv` is an optional comma-separated filter.
472
+ */
473
+ calendarEvents(
474
+ startRfc3339: string,
475
+ endRfc3339: string,
476
+ calendarIdsCsv?: string | null,
477
+ ): string;
478
+
479
+ /** Returns JSON array of contact containers (sources). */
480
+ contactsContainers(): string;
481
+
482
+ /** Returns JSON array of contacts matching `query`. */
483
+ contactsFind(
484
+ query: string,
485
+ limit?: number | null,
486
+ containerIdsCsv?: string | null,
487
+ ): string;
488
+
489
+ /** Returns JSON array of mail accounts known to the OS provider. */
490
+ mailAccounts(): string;
491
+
492
+ /**
493
+ * Returns JSON inbox snapshot. `accountIdsCsv` is an optional
494
+ * comma-separated filter; omit to query all known accounts.
495
+ */
496
+ mailInbox(accountIdsCsv?: string | null): string;
497
+
498
+ /**
499
+ * Send mail. `sendRequestJson` is `{to, subject, body, ...}` per the
500
+ * provider contract. Returns JSON `{ok, message_id?}`.
501
+ */
502
+ mailSend(sendRequestJson: string): string;
503
+
504
+ // -------------------------------------------------------------------------
505
+ // Wearable / activity (HealthKit + Fitbit/Garmin/Oura/etc.)
506
+ // -------------------------------------------------------------------------
507
+
508
+ /** Returns JSON `{available, reason?, providers?}`. */
509
+ healthStatus(): string;
510
+
511
+ /** Times are RFC3339. Returns JSON array of sleep sessions. */
512
+ healthSleep(startRfc3339: string, endRfc3339: string): string;
513
+
514
+ /** Times are RFC3339. Returns JSON array of workouts. */
515
+ healthWorkouts(startRfc3339: string, endRfc3339: string): string;
516
+
517
+ /** Dates are YYYY-MM-DD. Returns JSON array of daily activity summaries. */
518
+ healthActivity(startYmd: string, endYmd: string): string;
257
519
  }
258
520
 
259
521
  // ---------------------------------------------------------------------------
@@ -278,19 +540,90 @@ export function executeProposal(
278
540
  * `{"type":"tool_start","name":"...","index":N}`
279
541
  * `{"type":"tool_delta","index":N,"data":"..."}`
280
542
  * `{"type":"done","text":"...","tool_calls":[...]}`
543
+ *
544
+ * **Breaking change in 0.5.x:** the prior 9-positional signature
545
+ * has been replaced with an options-object form. Migration: build
546
+ * the request object and `JSON.stringify` it as `requestJson`. The
547
+ * options object exposes every `GenerateRequest` field including
548
+ * `intent` (closes #107). The replacement (rather than addition of
549
+ * a sibling) is forced by napi-rs's 3-standalone-TSF cap — see the
550
+ * project rules for the rationale.
281
551
  */
282
552
  export function inferStream(
283
553
  rt: CarRuntime,
284
- prompt: string,
285
- model: string | null | undefined,
286
- maxTokens: number | null | undefined,
287
- context: string | null | undefined,
288
- toolsJson: string | null | undefined,
554
+ requestJson: string,
289
555
  onEvent: (eventJson: string) => void,
290
- toolChoice?: string | null,
291
- parallelToolCalls?: boolean | null,
292
556
  ): Promise<string>;
293
557
 
558
+ // --- Caller-facing routing intent (parslee-ai/car-releases#18) ---
559
+
560
+ /**
561
+ * Coarse-grained task hint the adaptive router maps to its internal
562
+ * `InferenceTask`. A closed set so adding a new task type is a
563
+ * deliberate, FFI-visible change rather than a silent fallback.
564
+ */
565
+ export type TaskHint =
566
+ | 'chat'
567
+ | 'classify'
568
+ | 'summarize'
569
+ | 'reasoning'
570
+ | 'code'
571
+ | 'extract';
572
+
573
+ /**
574
+ * Hard model-capability filters the router enforces in addition to
575
+ * any prompt-derived requirements. Mirrors `ModelCapability` in the
576
+ * registry; values must be one of these snake_case strings.
577
+ */
578
+ export type ModelCapabilityRequirement =
579
+ | 'generate'
580
+ | 'embed'
581
+ | 'rerank'
582
+ | 'classify'
583
+ | 'code'
584
+ | 'reasoning'
585
+ | 'summarize'
586
+ | 'tool_use'
587
+ | 'multi_tool_call'
588
+ | 'vision'
589
+ | 'video_understanding'
590
+ | 'audio_understanding'
591
+ | 'grounding'
592
+ | 'speech_to_text'
593
+ | 'text_to_speech'
594
+ | 'image_generation'
595
+ | 'video_generation';
596
+
597
+ /**
598
+ * Caller-facing routing intent — express requirements, not model IDs.
599
+ *
600
+ * All fields are optional. An IntentHint with no fields set is
601
+ * equivalent to omitting the hint entirely (adaptive routing as today).
602
+ *
603
+ * @example
604
+ * await rt.infer(
605
+ * prompt,
606
+ * null,
607
+ * null,
608
+ * JSON.stringify({ task: 'chat', prefer_local: true } satisfies IntentHint),
609
+ * );
610
+ */
611
+ export interface IntentHint {
612
+ /** What the caller is doing. Maps to `InferenceTask` server-side. */
613
+ task?: TaskHint;
614
+ /**
615
+ * Hard filter — every required capability must be present on the
616
+ * candidate before scoring runs.
617
+ */
618
+ require?: ModelCapabilityRequirement[];
619
+ /**
620
+ * Bias the score profile toward local on-device models. Maps to a
621
+ * dedicated `RoutingWorkload::LocalPreferred` weight profile —
622
+ * quality-aware with a strong local_bonus so the hint wins ties.
623
+ */
624
+ prefer_local?: boolean;
625
+ }
626
+
294
627
  // --- Voice streaming (stored-callback pattern) ---
295
628
 
296
629
  export type AudioSourceSpec =
@@ -363,6 +696,20 @@ export function transcribeStreamPush(
363
696
 
364
697
  export function listVoiceSessions(rt: CarRuntime): string;
365
698
 
699
+ /**
700
+ * Voice providers (STT + TTS) compiled into this build.
701
+ *
702
+ * Returns a JSON-encoded array of objects with shape:
703
+ * `{ id: string, kind: "stt" | "tts", available: boolean, description: string }`.
704
+ *
705
+ * `available` reflects build-time presence (cfg-target, build features) —
706
+ * runtime readiness (API key set, permission granted, model downloaded)
707
+ * surfaces via per-provider error paths when you actually use them.
708
+ *
709
+ * Stateless; safe to call before constructing a `CarRuntime`.
710
+ */
711
+ export function listVoiceProviders(): string;
712
+
366
713
  // --- Meeting capture ---
367
714
 
368
715
  export interface StartMeetingRequest {
@@ -454,6 +801,103 @@ export function getMeeting(
454
801
  rootOverride?: string | null,
455
802
  ): string;
456
803
 
804
+ // --- Agent registry (file-based, no daemon) ---
805
+
806
+ /**
807
+ * Register or replace an agent's entry in the user-default registry
808
+ * (`~/.car/registry/`) or at the path supplied as `registryPath`.
809
+ *
810
+ * `entryJson` is a JSON-serialised `AgentEntry`:
811
+ * ```json
812
+ * {
813
+ * "name": "trader-paper",
814
+ * "dashboard_url": "http://127.0.0.1:8731",
815
+ * "status": "running",
816
+ * "display_name": "Trader (paper)",
817
+ * "port": 8731,
818
+ * "pid": 12345
819
+ * }
820
+ * ```
821
+ * `name` and `dashboard_url` are required; the rest are optional.
822
+ * Returns `"null"` on success.
823
+ */
824
+ export function registerAgent(
825
+ entryJson: string,
826
+ registryPath?: string | null,
827
+ ): string;
828
+
829
+ /**
830
+ * Bump `last_heartbeat_at` for the named agent. Returns
831
+ * `'{"refreshed": true}'` if the agent was registered, or
832
+ * `'{"refreshed": false}'` if the caller should re-register.
833
+ */
834
+ export function agentHeartbeat(
835
+ name: string,
836
+ registryPath?: string | null,
837
+ ): string;
838
+
839
+ /** Remove an agent's entry. Idempotent. */
840
+ export function unregisterAgent(
841
+ name: string,
842
+ registryPath?: string | null,
843
+ ): string;
844
+
845
+ /**
846
+ * List all currently-registered agents. Returns a JSON array of
847
+ * `AgentEntry` objects, sorted by name.
848
+ */
849
+ export function listAgents(registryPath?: string | null): string;
850
+
851
+ /**
852
+ * Reap entries whose last heartbeat is older than `maxAgeSecs`.
853
+ * Returns a JSON array of names that were reaped.
854
+ */
855
+ export function reapStaleAgents(
856
+ maxAgeSecs: number,
857
+ registryPath?: string | null,
858
+ ): string;
859
+
860
+ // --- car-a2a server lifecycle ---
861
+ //
862
+ // Expose CAR as an Agent2Agent (A2A) v1.0 peer programmatically,
863
+ // without shelling out to `car-server --a2a-bind`. Process-global
864
+ // state holds the bound listener and join handle so a later
865
+ // `stopA2aServer` / `a2aServerStatus` call reaches the right server.
866
+
867
+ /**
868
+ * Start an A2A listener.
869
+ *
870
+ * `paramsJson` shape:
871
+ * ```jsonc
872
+ * {
873
+ * "bind": "127.0.0.1:8731", // required
874
+ * "public_url": "https://...", // optional; defaults to http://<bound>
875
+ * "agent_name": "...", // optional
876
+ * "agent_description": "...", // optional
877
+ * "organization": "...", // optional
878
+ * "organization_url": "..." // optional
879
+ * }
880
+ * ```
881
+ *
882
+ * Returns `'{"bound":"127.0.0.1:8731"}'` on success. Errors if a
883
+ * server is already running, the bind fails, or `paramsJson` is
884
+ * malformed.
885
+ */
886
+ export function startA2aServer(paramsJson: string): Promise<string>;
887
+
888
+ /**
889
+ * Stop the running A2A listener. Returns `'{"stopped":true}'` on
890
+ * success. Errors if no server is running.
891
+ */
892
+ export function stopA2aServer(): string;
893
+
894
+ /**
895
+ * Report whether the A2A listener is up. Always returns a JSON
896
+ * object — `{"running":true,"bound":"...","uptime_secs":N}` when
897
+ * running, `{"running":false}` otherwise.
898
+ */
899
+ export function a2aServerStatus(): string;
900
+
457
901
  // --- Verification (stateless) ---
458
902
 
459
903
  export function verify(
@@ -552,3 +996,74 @@ export function rankProposals(
552
996
  tools?: string[] | null,
553
997
  costWeight?: number | null,
554
998
  ): string;
999
+
1000
+ // --- macOS automation (car-automation) ---
1001
+ //
1002
+ // AppleScript / JXA / Shortcuts bridges. Subprocess-backed so the
1003
+ // surface is uniform across binding layers. On non-macOS hosts each
1004
+ // call rejects with a PlatformUnsupported error.
1005
+
1006
+ /**
1007
+ * Run an AppleScript or JXA snippet via `osascript`.
1008
+ *
1009
+ * `argsJson` shape:
1010
+ * ```jsonc
1011
+ * {
1012
+ * "script": "return 1 + 2",
1013
+ * "language": "applescript" | "javascript", // optional, default applescript
1014
+ * "args": ["positional", "args"], // optional
1015
+ * "timeout_ms": 5000 // optional
1016
+ * }
1017
+ * ```
1018
+ *
1019
+ * Returns JSON `{stdout, stderr, exit_code}`. Rejects with the
1020
+ * subprocess stderr on non-zero exit.
1021
+ */
1022
+ export function runApplescript(argsJson: string): Promise<string>;
1023
+
1024
+ /**
1025
+ * Enumerate Shortcuts (user-authored *and* AppShortcuts donated by
1026
+ * apps via the App Intents framework). Returns an array of
1027
+ * `{name, identifier?, tool_slug, tool_description, parameters_schema}`,
1028
+ * suitable for registering each as a runtime tool.
1029
+ *
1030
+ * `argsJson` shape: `{ folder?: string, with_identifiers?: boolean }`.
1031
+ */
1032
+ export function listShortcuts(argsJson: string): Promise<string>;
1033
+
1034
+ /**
1035
+ * Invoke a Shortcut by name or UUID. `argsJson` shape:
1036
+ * ```jsonc
1037
+ * {
1038
+ * "name_or_id": "Shazam shortcut" | "43E9-...",
1039
+ * "input": "optional text input", // optional
1040
+ * "output_type": "public.plain-text", // optional UTI
1041
+ * "timeout_ms": 30000 // optional
1042
+ * }
1043
+ * ```
1044
+ * Returns JSON `{stdout, stderr, exit_code}`.
1045
+ */
1046
+ export function runShortcut(argsJson: string): Promise<string>;
1047
+
1048
+ // --- Vision OCR (car-vision) ---
1049
+
1050
+ /**
1051
+ * Apple Vision-framework on-device text recognition.
1052
+ *
1053
+ * `argsJson` shape:
1054
+ * ```jsonc
1055
+ * {
1056
+ * "image_path": "/path/to/screen.png",
1057
+ * "fast_path": false, // optional; true = .fast vs .accurate
1058
+ * "languages": ["en-US"], // optional BCP-47 hints
1059
+ * "language_correction": true, // optional, default true
1060
+ * "minimum_text_height": 0.0 // optional, normalized; floors tiny noise
1061
+ * }
1062
+ * ```
1063
+ *
1064
+ * Returns JSON `{available, observations}`. `available` is `false`
1065
+ * when the Vision shim wasn't built into this binary (non-macOS or
1066
+ * skipped Swift compile); in that case `observations` is an empty
1067
+ * array rather than an error.
1068
+ */
1069
+ export function visionOcr(argsJson: string): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",