car-runtime 0.48.0 → 0.49.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 +137 -6
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -474,15 +474,97 @@ export class CarRuntime {
474
474
  * Run one evolution cycle over the daemon's **live** signals — the
475
475
  * self-evolution governor's real executor (arXiv 2507.21046). `requestJson`
476
476
  * is `{ policy?, dry_run?, harness_baseline_metrics?,
477
- * harness_candidate_metrics? }`; the daemon plans over all five live
477
+ * harness_candidate_metrics?, harness_measure?, context_measure? }`; the daemon plans over all five live
478
478
  * components (Memory/Skills/Context from the engine, Harness from the event
479
479
  * log, Tools from connector health) and dispatches each `EvolveNow`
480
480
  * component: Memory → consolidate (sized by decide_maintenance), Skills →
481
481
  * evolve_skills over event-log failure traces, Harness → the HITL-gated
482
482
  * harness_evolution loop (pending approvals resolve via
483
- * `permission.approve`/`reject` by fingerprint); Context/Tools record
484
- * `not_executable`. Returns the cycle record JSON
485
- * `{ plan, steps, evolved, pending_approvals? }`.
483
+ * `permission.approve`/`reject` by fingerprint), Context → the
484
+ * `context_evolution` loop, which resolves each mutation either through the
485
+ * opt-in pre-activation grader (`context_measure`) or, for whatever that did
486
+ * not decide, the diagnose→approve→apply→measure→revert human path. Returns
487
+ * the cycle record JSON
488
+ * `{ plan, steps, evolved, out_of_scope, pending_approvals?, measurement? }`,
489
+ * where each step is `{ component, ran, applied, out_of_scope, outcome }`.
490
+ *
491
+ * **Context.** Diagnoses off the engine's own live conversation-layer
492
+ * saturation and lowers `MemgineConfig.conversation_keep_recent` (halved,
493
+ * floored at 2) so compaction summarizes more of the older turns. Every
494
+ * mutation is HITL-gated on the same shared durable `ApprovalLedger` as
495
+ * harness ones, under its own fingerprint namespace
496
+ * `context:<component>:<patch-digest>`, resolved by the same
497
+ * `permission.approve`/`reject`. There **is** a pre-activation regression
498
+ * gate, opt-in via `context_measure` (see below) — this doc comment used to
499
+ * say there was none, because the bench replayed a runtime with no memgine
500
+ * attached and never offered a `recall` tool; bench tasks may now declare a
501
+ * `memory:` fixture and are then replayed with a real memgine and the shipped
502
+ * `recall` tool, so the assembled context moves with the knob. A graded
503
+ * mutation promotes (`applied` with `governance: "promoted"`) or is rejected
504
+ * (`rejected_by_gate`) with no operator in the loop. On the human-approved
505
+ * path — and whenever no grade ran — the daemon measures the MARGIN after the
506
+ * apply: compact under the unchanged value for a baseline
507
+ * (`conversation_tokens_baseline`), apply, compact again, and revert unless
508
+ * the tokens fell below that baseline (`rolled_back`, not counted as
509
+ * applied; `rollback_failed` with `rollback_error` if even the revert did
510
+ * not take). Comparing against the baseline rather than the uncompacted
511
+ * layer is what stops the change being credited with savings compaction
512
+ * would have produced anyway. So context is **not** unattended out of the
513
+ * box; it becomes unattended for a given change only once that fingerprint
514
+ * has been approved — and since the ledger is daemon-wide and the
515
+ * fingerprint names the change, that approval covers the same change on
516
+ * every engine this daemon evolves. On the unattended cadence a falsified
517
+ * mutation then backs off exponentially per fingerprint (`in_backoff`)
518
+ * instead of being re-applied and re-reverted every tick. The step's
519
+ * `outcome` is a JSON string `{ mechanism: "context_evolution", mutations,
520
+ * applied, pending, details }`, each detail carrying `mutation`,
521
+ * `component`, `fingerprint`, `rationale` and one of `pending_approval` |
522
+ * `applied` | `rolled_back` | `rollback_failed` | `apply_failed` |
523
+ * `would_apply` | `in_backoff` | `rejected_by_operator` |
524
+ * `approved_no_patch` | `rejected_by_gate` | `measurement_failed` |
525
+ * `config_moved_during_measurement` (a graded promotion whose measured base
526
+ * was moved by something else while the replays ran — nothing applied, both
527
+ * values reported, no backoff). When `context_measure` was requested the
528
+ * summary also carries `context_measured: { status: "measured" |
529
+ * "skipped_dry_run", grade_attempts, model, split, split_seed }`.
530
+ *
531
+ * **Tools** is recorded as `out_of_scope` — a decision, not a failure.
532
+ * Connector remediation means re-running a connector's OAuth or credential
533
+ * exchange, an access change this loop holds no authority to perform;
534
+ * reconnect/re-auth stay operator actions via `connectors.*`. Such a step is
535
+ * `ran: true, applied: false, out_of_scope: true` with the reason in
536
+ * `outcome`, and the component appears in the top-level `out_of_scope`
537
+ * array (always present, empty when none). `ran: false` therefore means one
538
+ * thing only: the mechanism was invoked and errored.
539
+ *
540
+ * `harness_measure` `{ model, split?, held_in_fraction?, split_seed?,
541
+ * max_turns?, tasks_dir? }` opts into **in-daemon measurement**: the daemon
542
+ * replays the held-out split itself (once for the baseline under the live
543
+ * `HarnessConfig`, once per measurable mutation under that config plus the
544
+ * mutation's patch) and feeds the regression gate, so a cycle can promote or
545
+ * reject unattended. It is mutually exclusive with the two supplied-metrics
546
+ * params (sending both errors, naming both); `dry_run` measures nothing and
547
+ * reports `measurement.status = "skipped_dry_run"`; a build with no
548
+ * in-process evaluator installed errors rather than degrading to HITL;
549
+ * safety-affecting and patchless mutations are never measured; a failed
550
+ * replay reports `measurement_failed` and fabricates no metrics.
551
+ *
552
+ * `context_measure` takes the SAME request shape and opts into the **Context
553
+ * pillar's** pre-activation grader: two replays over the same split, one
554
+ * under the engine's live `MemgineConfig` and one under it plus the
555
+ * mutation's patch, graded on TASK outcomes by the same gate. The two params
556
+ * are not mutually exclusive with each other (different pillars, two
557
+ * independent measurements). `dry_run` performs no replay; a build with no
558
+ * evaluator installed errors; a patchless mutation is never measured; the
559
+ * unattended cadence never requests a grade at all, so an idle timer cannot
560
+ * start spending benchmark replays.
561
+ * `measurement` is TOP-LEVEL on the response (not only inside the harness
562
+ * step) and present whenever `harness_measure` was requested, in every
563
+ * shape it can end in — `measured` / `skipped_dry_run` /
564
+ * `measurement_failed` with the error. A replay is a paid side effect and
565
+ * the plan may legitimately never dispatch Harness, so a side effect
566
+ * reported only from that step is one a caller can be billed for and never
567
+ * see.
486
568
  */
487
569
  runEvolutionCycleLive(requestJson: string): Promise<string>;
488
570
 
@@ -1558,11 +1640,60 @@ export class CarRuntime {
1558
1640
  mailAccounts(): string;
1559
1641
 
1560
1642
  /**
1561
- * Returns JSON inbox snapshot. `accountIdsCsv` is an optional
1562
- * comma-separated filter; omit to query all known accounts.
1643
+ * Returns JSON inbox snapshot
1644
+ * `{ available, backend, reason?, summaries: InboxSummary[] }` — per-account
1645
+ * unread/total counts, not message rows. Use `mailMessages` for rows.
1646
+ * `accountIdsCsv` is an optional comma-separated filter; omit to query all
1647
+ * known accounts.
1563
1648
  */
1564
1649
  mailInbox(accountIdsCsv?: string | null): string;
1565
1650
 
1651
+ /**
1652
+ * Enumerate every mailbox (folder) of the given accounts, nested ones
1653
+ * included on BOTH backends. Returns
1654
+ * `{ available, backend, reason?, mailboxes: Mailbox[] }` where `Mailbox`
1655
+ * is `{ account_id, name, full_name, unread, total }`.
1656
+ *
1657
+ * `full_name` is the selector to pass back as `MessageQuery.mailbox` — the
1658
+ * slash-joined path on macOS, the folder id on Microsoft Graph. Graph's
1659
+ * `/me/mailFolders` is root-only, so nested folders come from a bounded
1660
+ * `childFolders` walk (depth 8, at most 64 requests); a tree deeper or
1661
+ * wider than that is truncated.
1662
+ *
1663
+ * An `accountIdsCsv` that matches no account returns `available: false`
1664
+ * with a reason, not an empty list.
1665
+ */
1666
+ mailMailboxes(accountIdsCsv?: string | null): string;
1667
+
1668
+ /**
1669
+ * Read message rows, newest first. `queryJson` is a `MessageQuery`:
1670
+ * `{account_ids?: string[], mailbox?: string | null, limit?: number,
1671
+ * since?: string, include_body?: boolean}`. Every field defaults, and
1672
+ * `mailbox: null` means INBOX — so `"{}"` reproduces the pre-existing
1673
+ * INBOX-only read.
1674
+ *
1675
+ * "Newest first" is GLOBAL, not per account: rows from every matched
1676
+ * account are merged into one date-ordered list before `limit` applies, so
1677
+ * `limit: 1` across two accounts returns the newer message rather than
1678
+ * whichever account the backend listed first.
1679
+ *
1680
+ * Returns `{ available, backend, reason?, messages: MessageSummary[] }`;
1681
+ * each row carries a stable opaque `id` accepted by `mailMessageBody`, and
1682
+ * a `mailbox` holding the mailbox as the backend RESOLVED it (a query for
1683
+ * `"travel"` comes back stamped `"Travel/2026"`), so rows match
1684
+ * `mailMailboxes` output. An unresolvable mailbox or an unmatched
1685
+ * `account_ids` returns `available: false` with a reason, never an empty
1686
+ * list.
1687
+ */
1688
+ mailMessages(queryJson: string): string;
1689
+
1690
+ /**
1691
+ * Fetch one message body by the `id` from a `mailMessages` row. Returns
1692
+ * `{ available, backend, reason?, id, content_type, body, truncated }`;
1693
+ * bodies are cut at 100,000 characters with `truncated: true`.
1694
+ */
1695
+ mailMessageBody(messageId: string): string;
1696
+
1566
1697
  /**
1567
1698
  * Send mail. `sendRequestJson` is `{to, subject, body, ...}` per the
1568
1699
  * provider contract. Returns JSON `{ok, message_id?}`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.48.0",
3
+ "version": "0.49.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",