car-runtime 0.46.0 → 0.47.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 +218 -10
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -59,6 +59,14 @@ export interface CoderStartOptions {
59
59
  * hypothesis, the other buys a retry.
60
60
  */
61
61
  transientRetries?: number | undefined | null;
62
+ /**
63
+ * A `coder.discuss` conversation this run was distilled from. Its agreed
64
+ * constraints ride into contract derivation, so a rule stated once in the
65
+ * discussion does not have to be restated in the intent, and the session
66
+ * records the provenance. An unknown id is a clear error — never a silently
67
+ * ungrounded run.
68
+ */
69
+ discussionId?: string | undefined | null;
62
70
  }
63
71
 
64
72
  export class CarRuntime {
@@ -937,9 +945,93 @@ export class CarRuntime {
937
945
  */
938
946
  coderApproveMerge(sessionId: string, approve: boolean): Promise<string>;
939
947
 
940
- /** Cancel a session: stop the loop, abandon, remove the worktree. */
948
+ /**
949
+ * Cancel a session: stop the loop, abandon, remove the worktree. Returns
950
+ * `{state, already_terminal, message}`.
951
+ *
952
+ * An already-finished session **succeeds** rather than rejecting: `state`
953
+ * keeps its pre-existing name and type, `already_terminal` is `true`, and
954
+ * `message` names what already happened. Callers that cancel unconditionally
955
+ * on shutdown depend on that — rejecting would turn a quiet exit into a
956
+ * protocol error whenever the session raced to terminal first.
957
+ */
941
958
  coderCancel(sessionId: string): Promise<string>;
942
959
 
960
+ /**
961
+ * The current session list AND registration for `coder.session_changed` on
962
+ * this connection, atomically (registered under the same lock the list is
963
+ * snapshotted under, so nothing slips through the gap). Notifications are
964
+ * WebSocket-only, same contract as `coder.subscribe`.
965
+ *
966
+ * Each row carries the full summary: the pre-existing
967
+ * `{session_id, state, intent, repo, engine, iterations, updated_at, live,
968
+ * error}` plus `needs_you` (`"contract" | "question" | "approval" | "auth" |
969
+ * null`), `needs_you_label` (the daemon-owned wording, so every client says
970
+ * 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).
975
+ *
976
+ * Pass `renew: true` for the lease-renewal form: it re-registers and answers
977
+ * `{ was_registered }` — `false` means this connection had been shed and
978
+ * should take a full snapshot — and builds NO summaries, so it is cheap
979
+ * enough to call on a timer. The default form is unchanged.
980
+ */
981
+ coderWatch(renew?: boolean | undefined | null): Promise<string>;
982
+
983
+ /** Stop receiving `coder.session_changed` on this connection. */
984
+ coderUnwatch(): Promise<string>;
985
+
986
+ /**
987
+ * Redraft a PROPOSED outcome contract from a plain-English request (e.g.
988
+ * "also verify the Windows path"). Legal only in `contract_proposed`;
989
+ * nothing executes and the session stays at the gate either way. Unlimited
990
+ * rounds.
991
+ *
992
+ * Returns `{state, revised, contract, baseline, baseline_gates_nothing,
993
+ * message}`. **Check `revised` before trusting `contract`**: on a redraft
994
+ * that does not validate, the previous contract comes back byte-identical
995
+ * with `revised: false` and a `message` explaining why, and the daemon emits
996
+ * a `contract_revision_rejected` event.
997
+ */
998
+ coderReviseContract(sessionId: string, request: string): Promise<string>;
999
+
1000
+ /**
1001
+ * Open a repo-grounded, strictly **read-only** discussion — a thinking
1002
+ * surface for working out what a change should be, before a run exists.
1003
+ * Bound at `PermissionTier::ReadOnly` with every write/shell escalation
1004
+ * auto-denied, so it can never touch the repo. Returns
1005
+ * `{discussion_id, repo, repo_summary}`; a non-git path is a clear error.
1006
+ */
1007
+ coderDiscussStart(repo: string): Promise<string>;
1008
+
1009
+ /**
1010
+ * Send one operator message. Returns `{ok, seq}` where `seq` is the first
1011
+ * event this turn emits; the reply streams as `coder.discuss.event`
1012
+ * (WebSocket-only, same contract as `coder.event`).
1013
+ */
1014
+ coderDiscussSend(discussionId: string, text: string): Promise<string>;
1015
+
1016
+ /**
1017
+ * Distill the discussion into `{discussion_id, proposed_intent,
1018
+ * constraints}`. **Starts nothing** — no worktree, no branch, no session.
1019
+ * The caller shows `proposed_intent` (never the transcript) for the operator
1020
+ * to edit, then passes it to `coderStart` with `discussion_id` so the agreed
1021
+ * constraints reach contract derivation. Callable repeatedly.
1022
+ */
1023
+ coderDiscussPromote(discussionId: string): Promise<string>;
1024
+
1025
+ /** Free an in-memory discussion. Discussions do not survive a daemon restart. */
1026
+ coderDiscussClose(discussionId: string): Promise<string>;
1027
+
1028
+ /**
1029
+ * Open discussions: `{discussions: [{discussion_id, repo, created_at,
1030
+ * turns}]}`. Also the capability probe — a daemon predating this surface
1031
+ * answers JSON-RPC `-32601`.
1032
+ */
1033
+ coderDiscussList(): Promise<string>;
1034
+
943
1035
  /**
944
1036
  * Managed projects + in-daemon declarative agents (the non-developer path).
945
1037
  *
@@ -1942,6 +2034,30 @@ export function resumeWorkflow(pausedJson: string, inputJson: string): Promise<s
1942
2034
  * resumable runs after a restart. */
1943
2035
  export function listPausedWorkflows(runsDir: string): string;
1944
2036
 
2037
+ /** Bind (or clear) the memory namespace for subsequent daemon connections
2038
+ * (car-releases#81). Returns the namespace now in effect, or `null` for the
2039
+ * daemon's shared graph.
2040
+ *
2041
+ * Prefer this over setting `CAR_MEMORY_NAMESPACE` when the namespace is
2042
+ * per-project. An env var cannot carry a per-project value: the host learns
2043
+ * which project it is *after* the process starts, and on **bun** a JS-side
2044
+ * `process.env` write never reaches the C `environ` this library reads — the
2045
+ * write is silently ignored and the session falls back to the shared graph.
2046
+ *
2047
+ * Blank or `null` clears the override, falling back to `CAR_MEMORY_NAMESPACE`
2048
+ * and then the shared graph.
2049
+ *
2050
+ * **Takes effect on the next connection.** The namespace is negotiated during
2051
+ * `session.auth`, so an already-established connection keeps the graph it
2052
+ * bound. Call this before your first CAR call, or disconnect afterwards to
2053
+ * force a rebind — otherwise you keep writing to the previous project's
2054
+ * graph. */
2055
+ export function setMemoryNamespace(namespace?: string | null): string | null;
2056
+
2057
+ /** The memory namespace currently in effect — the explicit override if set,
2058
+ * otherwise `CAR_MEMORY_NAMESPACE`, otherwise `null` (shared graph). */
2059
+ export function getMemoryNamespace(): string | null;
2060
+
1945
2061
  /** NLP (F4): identify the dominant language of `text`. Returns
1946
2062
  * `{language, backend}` JSON (Apple NaturalLanguage on macOS, pure-Rust
1947
2063
  * fallback elsewhere). */
@@ -2145,10 +2261,25 @@ export function sendA2AMessage(rt: CarRuntime, paramsJson: string): Promise<stri
2145
2261
  * `{ valid, issues, simulated_state, execution_levels, conflicts,
2146
2262
  * evidence }`. `evidence` is the verifier's declared scope (survey
2147
2263
  * "Code as Agent Harness" §5.2.2): `{ checks: [{ name, ran, verifies,
2148
- * cannot_verify, findings }], assumptions, untested_regions,
2264
+ * cannot_verify, findings, tier }], assumptions, untested_regions,
2149
2265
  * residual_risks, confidence }` — so a `valid: true` verdict can be read
2150
2266
  * with its scope (what was checked, what was not, coverage confidence)
2151
2267
  * rather than as a blanket guarantee.
2268
+ *
2269
+ * Each issue is `{ action_id, severity, message, tier }`. `tier` is the
2270
+ * **evidence tier** — `"decision_procedure" | "heuristic" | "sampled"` —
2271
+ * naming which kind of check produced the finding, so you don't have to
2272
+ * pattern-match the message to tell them apart. All of `verify`'s
2273
+ * findings are `decision_procedure` (set membership, the STRIPS-style
2274
+ * forward walk, write-conflict detection) except the
2275
+ * repeated-identical-call rule, which is `heuristic`: the count is
2276
+ * exact, "runaway loop" is a proxy, and a legitimate 3× poll trips it.
2277
+ * The tier is orthogonal to `severity` (how bad, not how derived), and
2278
+ * `decision_procedure` is not a proof or a soundness claim — it means
2279
+ * the check decides the property it reports over the inputs it was
2280
+ * given, which for the state-dependent checks is a forward model built
2281
+ * only from *declared* effects. Each `CheckRecord` carries the same
2282
+ * `tier` as the findings it contributed.
2152
2283
  */
2153
2284
  export function verify(
2154
2285
  proposalJson: string,
@@ -2652,8 +2783,13 @@ export function cascadeDecide(
2652
2783
  *
2653
2784
  * `statsJson` is aggregate counters (parsed by serde — keys are snake_case):
2654
2785
  * `{ total_facts, structured_facts, total_edges, total_retrievals,
2655
- * helpful_retrievals, conflicts_resolved, outstanding_outdated, facts_created,
2656
- * facts_superseded }` (omitted fields default to 0). Returns the report JSON:
2786
+ * total_proactive_injections, helpful_retrievals, conflicts_resolved,
2787
+ * outstanding_outdated, facts_created, facts_superseded }` (omitted fields
2788
+ * default to 0). `total_retrievals` counts DELIBERATE recalls only;
2789
+ * harness-initiated proactive injections are reported separately as
2790
+ * `total_proactive_injections` and never feed ranking (car#816).
2791
+ * `helpful_retrievals` is always 0 today — `record_fact_helpful` has no
2792
+ * production caller — so read a 0 there as "not wired", not "nothing helped". Returns the report JSON:
2657
2793
  * `{ representation_fidelity, retrieval_precision, update_correctness,
2658
2794
  * long_horizon_stability, overall }` (each 0..1), `bottleneck` (one of
2659
2795
  * `"representation" | "retrieval" | "update_correctness" |
@@ -2849,11 +2985,76 @@ export function evolutionApply(
2849
2985
  // (read_only | sandbox_edit | full_access), gate it against the session's
2850
2986
  // granted standing tier, and record human-in-the-loop approvals as durable,
2851
2987
  // auditable state (a JSONL ledger keyed by a stable action fingerprint).
2852
-
2853
- /**
2854
- * Classify each action in a proposal into its minimum required permission
2855
- * tier. Returns a JSON array of `{ action_id, tool, required_tier }` where
2856
- * `required_tier` is `"read_only" | "sandbox_edit" | "full_access"`.
2988
+ //
2989
+ // Two axes, not one. The tier answers "who may authorize this?" and says
2990
+ // nothing about whether the effect can be undone a `git push`, a production
2991
+ // INSERT, and a charged card are all `full_access` with three different
2992
+ // rollback contracts. Every row below therefore carries a `reversibility`
2993
+ // alongside its `required_tier`.
2994
+ //
2995
+ // The matching Action IR fields (any `proposalJson` this package accepts, and
2996
+ // the full spec in docs/agent-ir-spec.md):
2997
+ //
2998
+ // "reversibility": "reversible" | "compensable" | "irreversible"
2999
+ // Optional. The rollback contract for this action's effects.
3000
+ // `reversible` is undone by restoring the scope it ran in;
3001
+ // `compensable` needs a compensating action run against it;
3002
+ // `irreversible` cannot be undone once it reaches the world.
3003
+ // **Defaults to "irreversible"** when omitted — deliberately, because
3004
+ // the default decides what the runtime believes about an unclassified
3005
+ // action and the two directions fail asymmetrically. Guessing
3006
+ // "reversible" wrongly means silently believing a sent email can be
3007
+ // unsent; guessing "irreversible" wrongly means over-asking for an
3008
+ // approval on something recoverable, which is annoying, visible, and
3009
+ // fixed locally by annotating the action.
3010
+ //
3011
+ // "compensation": { "type": "tool", "tool": string, "parameters"?: object }
3012
+ // | { "type": "action_ref", "action_id": string }
3013
+ // Optional. How to undo the action once it has run — the action-level
3014
+ // analogue of car-workflow's saga CompensationHandler. Meaningful only
3015
+ // with `"reversibility": "compensable"`; omitted from the serialized
3016
+ // form when absent, so older consumers see the payload they saw before.
3017
+ // Declaring one is a *claim*: nothing checks that the named tool is a
3018
+ // true inverse, exactly as nothing checks `expected_effects`.
3019
+ //
3020
+ // Nothing in the runtime enforces on either field yet — they are typed,
3021
+ // classified, and audited. Do not read `"reversible"` as a promise that the
3022
+ // runtime will undo anything for you: rollback restores the state map and
3023
+ // leaves whatever a tool wrote to disk where it is. See
3024
+ // docs/proposals/shepherd-substrate-adoption.md.
3025
+
3026
+ /**
3027
+ * Classify each action in a proposal on both authorization-adjacent axes.
3028
+ * Returns a JSON array of `{ action_id, tool, required_tier, reversibility,
3029
+ * missing_compensation }`. The keys live inside a JSON string, so they stay
3030
+ * snake_case — napi-rs camelCases function and parameter names, not payload
3031
+ * contents.
3032
+ *
3033
+ * - `required_tier` — **who may authorize this**: `"read_only" |
3034
+ * "sandbox_edit" | "full_access"`.
3035
+ * - `reversibility` — **can this be undone**: `"reversible" | "compensable" |
3036
+ * "irreversible"`. Independent of the tier and not derived from it:
3037
+ * `read_secret` is `full_access` and perfectly reversible (a read leaves
3038
+ * nothing to undo), while `send_email` and `deploy_service` are both
3039
+ * `full_access` and differ completely. This is the classifier's answer from
3040
+ * the tool name and flattened parameters — **not** an echo of the action's
3041
+ * declared `reversibility` field, which defaults to `"irreversible"` and so
3042
+ * would tell you only what you sent. It is a keyword heuristic: an
3043
+ * unrecognized tool comes back `"irreversible"`, deliberately.
3044
+ * - `missing_compensation` — the action **declared** `"compensable"` and
3045
+ * supplied no `compensation`, the one incoherent combination the IR cannot
3046
+ * exclude by construction. Keyed off the declared field, so it stays `false`
3047
+ * for proposals that never opted into the axis.
3048
+ *
3049
+ * Severity ascends `"reversible" < "compensable" < "irreversible"`, so the
3050
+ * rollback contract of a whole batch is the worst row — a plan is only as
3051
+ * recoverable as its least recoverable step. (The daemon's `permission.classify`
3052
+ * returns that roll-up precomputed as `declared_rollback_contract`, over the
3053
+ * *declared* fields; this function returns a bare array with nowhere to hang
3054
+ * it, so compute it from the column you care about.)
3055
+ *
3056
+ * Nothing in the runtime gates on the second axis yet — it is typed,
3057
+ * classified, and audited, not enforced.
2857
3058
  */
2858
3059
  export function permissionClassify(proposalJson: string): string;
2859
3060
 
@@ -2861,10 +3062,17 @@ export function permissionClassify(proposalJson: string): string;
2861
3062
  * Evaluate each action against a granted standing tier, consulting the
2862
3063
  * durable approval ledger JSONL at `ledgerPath` when supplied. Returns a
2863
3064
  * JSON array of per-action decisions, each `{ decision, required, granted,
2864
- * action_id, fingerprint, ... }` where `decision` is `"allow" |
3065
+ * action_id, fingerprint, reversibility, ... }` where `decision` is `"allow" |
2865
3066
  * "needs_approval" | "deny"`. A `needs_approval` decision means autonomy is
2866
3067
  * suspended pending a human decision; resolve it with
2867
3068
  * {@link permissionRecordForFingerprint}.
3069
+ *
3070
+ * `reversibility` is orthogonal to `decision` and rides on **every** row, the
3071
+ * `allow`s included: the gate's verdict says whether the action may run, not
3072
+ * whether it could be taken back afterwards, and a caller that only learns the
3073
+ * rollback contract of the actions it was stopped on is missing exactly the
3074
+ * rows an incident review reads first. Same field, labels, and classifier as
3075
+ * the `PermissionDecision` event the engine writes to its audit log.
2868
3076
  */
2869
3077
  export function permissionEvaluate(
2870
3078
  proposalJson: string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "car-runtime",
3
- "version": "0.46.0",
3
+ "version": "0.47.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",