@haven_ai/connect 0.1.34-alpha.0 → 0.1.36-alpha.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.
package/README.md CHANGED
@@ -7,10 +7,33 @@ signing key and API key locally, stores them locally, and sends Haven only the
7
7
  public signing address, proof signature, and API-key hash. Haven never receives
8
8
  the private key or plaintext API key.
9
9
 
10
+ ## Are you an AI agent whose user has no Haven account yet?
11
+
12
+ Read **`/for-agents.md`** on the Haven host your user gave you — or
13
+ [the copy in this repository](https://github.com/d-hinders/Haven-AI/blob/dev/packages/frontend/public/for-agents.md)
14
+ if you do not have that host yet.
15
+
16
+ Your user creates the account and the passkey: those are theirs, they need a
17
+ human, and you should never ask for their password. You can do everything else
18
+ — including running the connector command from the setup prompt they paste you,
19
+ and managing the account from the shell with `@haven_ai/cli`.
20
+
10
21
  ```sh
11
- npx -y @haven_ai/connect@alpha --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code
22
+ npx -y @haven_ai/connect@<channel> --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code
12
23
  ```
13
24
 
25
+ `<channel>` is a placeholder, like `hv_setup_...` and the API URL beside it:
26
+ **the Haven dashboard hands you the exact command to run — copy that one.** The
27
+ channel is per-deployment and has been since #2422: the backend's setup response
28
+ names the whole package in `connector_package`, production hands out
29
+ `@haven_ai/connect@alpha`, and a non-production deployment can be configured to
30
+ hand out another, such as `@haven_ai/connect@dev`. This README ships inside every
31
+ channel's tarball and is the npm landing page for all of them, so a literal here
32
+ would be wrong for every reader it did not describe (#2515).
33
+ Read `connector_package` rather than assuming any particular backend's channel. Pinning `@alpha` by hand against such a backend
34
+ installs a signer that skews against it — the signer refuses to sign an
35
+ `x402_expected_context_version` it does not know.
36
+
14
37
  The connector writes owner-only credential files outside the project by default:
15
38
 
16
39
  - `~/.haven/agents/<agent-id>/identity.json` contains the Haven API key.
@@ -80,7 +103,8 @@ already-running host, which is why the restart guidance below matters.
80
103
 
81
104
  ## Retiring an old agent directory
82
105
 
83
- Re-running setup creates a NEW agent and retires nothing. Long-lived MCP hosts
106
+ Re-running setup without `--replace` creates a NEW agent and retires nothing
107
+ (with `--replace`, see [Running setup again](#running-setup-again)). Long-lived MCP hosts
84
108
  (gateways, TUI workers, editors, desktop apps) load their MCP wiring once, at
85
109
  process start — a host started before your latest setup keeps spawning the OLD
86
110
  agent's signer path forever, and when that directory is later removed the spawn
@@ -93,9 +117,12 @@ minutes. Two rules follow:
93
117
  2. **Tombstone a directory before (or instead of) deleting it:**
94
118
 
95
119
  ```
96
- npx @haven_ai/connect@alpha --tombstone ~/.haven/agents/<directory> --reason "superseded" --json
120
+ npx @haven_ai/connect@<channel> --tombstone ~/.haven/agents/<directory> --reason "superseded" --json
97
121
  ```
98
122
 
123
+ `<channel>` is the placeholder defined under the first example; this command
124
+ rewrites local files only, so any published connector does the same job.
125
+
99
126
  This replaces the directory's signer wrapper with a diagnostic that logs the
100
127
  retirement (agent id, date, reason, restart guidance) to the host's MCP
101
128
  stderr log on every probe, and records it in `TOMBSTONE.json` for
@@ -123,10 +150,13 @@ Hermes) the `MCP_HAVEN_API_KEY` dotenv line behind, and the runtime quoted as
123
150
  one agent while signing as another. `--unwire` is the erase half:
124
151
 
125
152
  ```
126
- npx @haven_ai/connect@alpha --unwire ~/.haven/agents/<directory> [--reason "..."]
127
- npx @haven_ai/connect@alpha --unwire --name research [--reason "..."]
153
+ npx @haven_ai/connect@<channel> --unwire ~/.haven/agents/<directory> [--reason "..."]
154
+ npx @haven_ai/connect@<channel> --unwire --name research [--reason "..."]
128
155
  ```
129
156
 
157
+ `<channel>` is the placeholder defined under the first example; `--unwire`
158
+ touches local files only, so any published connector does the same job.
159
+
130
160
  It tombstone-first (so a stale long-lived host still hears `HAVEN-TOMBSTONE`,
131
161
  never a masked `ENOENT`), then removes THAT agent's hosted + signer pair from
132
162
  every runtime config it appears in (Hermes YAML, Codex TOML, the Cursor / VS
@@ -150,17 +180,31 @@ Pass `--json` when a launcher needs a machine-readable completion record. Connec
150
180
  writes progress and human recovery notes to stderr and exactly one JSON object
151
181
  to stdout, with `schema_version: 1` and `outcome` set to `complete`,
152
182
  `action_required`, or `failed`. Structured runs skip the interactive
153
- budget-approval wait so the record is emitted promptly; approve in the Haven
154
- dashboard whenever ready and verify later with the read-only `haven_get_agent`
183
+ budget-approval wait so the record is emitted promptly; open `approval.url`
184
+ whenever ready and verify later with the read-only `haven_get_agent`
155
185
  tool. The object includes runtime/topology status,
156
186
  probe result, activation and next-action guidance, approval state/expiry (null
157
- when the backend does not provide an approval expiry), the two
158
- read-only verification tools, `hosted_mcp_url`, and `superseded_agent_ids`. It
187
+ when the backend does not provide an approval expiry) and `approval.url`, the two
188
+ read-only verification tools, `hosted_mcp_url`, `superseded_agent_ids`, and —
189
+ on a run that replaced existing wiring — `superseded_agents_retired_locally`
190
+ with `retired_agent_ids`. It
159
191
  contains no API key, private key, credential
160
192
  contents, full credential paths, or full delegate address. The same redacted
161
193
  object is available to library callers as `runConnect(...).outcome`; the older
162
194
  fields remain for additive compatibility.
163
195
 
196
+ `approval.url` (#2528) is the absolute link to this setup's budget approval,
197
+ returned by the backend at register. Present only when `approval.required` is
198
+ `true` **and** the backend is new enough to send one — a deployment older than
199
+ #2528 omits the key entirely, which is why a caller must test for it rather
200
+ than assume it. In prose mode the same link replaces "Return to Haven" in the
201
+ printed next steps. It carries no secret (the setup token never appears in it),
202
+ and it is the ONLY approval link a caller should use: the outcome carries no
203
+ setup id, so there is nothing to assemble one from — relay the whole link or
204
+ none. The connector also reports `run_mode` (`json` or `prose`) to the backend
205
+ at register, so Haven can tell an automated setup from a narrated one; it is
206
+ sent, not returned, and appears in no output.
207
+
164
208
  `hosted_mcp_url` is the hosted MCP endpoint this run wired up — **not** the
165
209
  backend URL you passed as `--api`. The hosted MCP server is a separate
166
210
  deployment, so the two differing is intended topology, not an environment
@@ -168,11 +212,40 @@ mismatch. It is non-secret: the same string goes into your own MCP config file,
168
212
  and the API key travels beside it in a header.
169
213
 
170
214
  `superseded_agent_ids` lists the other agent directories on this machine. A
171
- re-run mints a NEW agent and retires nothing, so those older agents still hold
172
- live API and signing keys — revoke them on the Haven agent page if you meant to
173
- replace them. Empty on a clean first run; an empty list is not a guarantee,
174
- since a scan that cannot read the credential root also yields one rather than
175
- failing a completed setup.
215
+ re-run mints a NEW agent, and without `--replace` retires nothing, so those
216
+ older agents still hold live API and signing keys — revoke them on the Haven
217
+ agent page if you meant to replace them. Empty on a clean first run; an empty
218
+ list here is not a guarantee, since a scan that cannot read the credential root
219
+ also yields one rather than failing a completed setup.
220
+
221
+ **The dashboard offers the revoke, never the connector (#2561).** The same ids
222
+ now ride the install-status report, so the Haven dashboard can put a
223
+ one-click revoke next to the setup that displaced them. The connector does not
224
+ and must not do it: `POST /agents/:id/revoke` is owner-authenticated, and an
225
+ agent credential retiring a sibling agent is the "agent editing its own
226
+ authority" the re-key routes refuse. Nothing is revoked automatically — the
227
+ owner clicks, one agent at a time.
228
+
229
+ On the REPORT the field is a tri-state, unlike the `--json` outcome above: a
230
+ list, `[]` when the scan ran and found none, and `null` when it could not run.
231
+ That is the same ambiguity this paragraph warns you about, made machine-
232
+ readable, so a dashboard never tells somebody their machine is clean when
233
+ nobody managed to read it. On a `--replace` run, `retired_agent_ids` names the
234
+ directories that were actually tombstoned and had their local key files removed
235
+ — **the collision set only**, never the whole `superseded_agent_ids` list, which
236
+ also names named agents that coexist with the replaced bare pair and are
237
+ untouched — and `superseded_agents_retired_locally` is `true` when that
238
+ retirement covered every collision entry (`false`: the install ended with an
239
+ error code and the retirement was skipped, or one entry failed). Both fields
240
+ are absent on any other run and say nothing about the backend, where nothing is
241
+ revoked.
242
+
243
+ A `wiring_collision` refusal (a non-interactive bare setup over a live
244
+ previous agent, #2551) carries `error.superseded_agent_ids` and
245
+ `error.suggested_name` — the ids the human needs in order to decide, and a
246
+ valid `--name` the run can be re-issued with — with `next_action:
247
+ relay_wiring_collision_to_user`. It is a relay, not a retry hint: do not append
248
+ `--replace` or `--name` on the agent's own initiative.
176
249
 
177
250
  For a recoverable install, configuration, probe, consent, or manual-runtime
178
251
  condition, inspect `error.code` and `error.next_action`, then follow the safe
@@ -221,12 +294,25 @@ an extra "about to create agent X, proceed?" gate. That is deliberate: the
221
294
  consent already happened when the user minted the one-time setup prompt in the
222
295
  Haven dashboard, which enumerates exactly what the command may do. The setup
223
296
  stays cancellable from the dashboard throughout, and the registered agent
224
- starts `pending_approval` with zero spend authority — nothing can move funds
225
- until the user approves the budget in Haven. A CLI-side confirmation would add
226
- friction without adding a security boundary. (The local-signer tool-exposure
297
+ starts `pending_approval` with zero spending authority — no budgeted spend can
298
+ move until the user approves the budget in Haven. The exact sweep-recovery
299
+ routes remain available only to recover a stranded delegate balance and do not
300
+ grant spending authority. A CLI-side confirmation would add friction without
301
+ adding a security boundary. (The local-signer tool-exposure
227
302
  acknowledgement is a separate, machine-checkable consent about what the local
228
303
  MCP tools expose, not a registration gate.)
229
304
 
305
+ The one question Connect does put before registration is not about the
306
+ agent being created but about the one already here: a **wiring collision**
307
+ ([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)). When the bare
308
+ `haven` / `haven-signer` pair on this machine already belongs to a different
309
+ agent whose directory still holds a live key, proceeding would silently
310
+ re-point that pair — the state `--doctor` reports as a still-spend-capable
311
+ `superseded` directory. That choice (replace it, or install alongside under a
312
+ name) belongs to the user, and it is asked *before* the key is minted or the
313
+ agent registered so that declining leaves no orphaned `pending_approval` agent
314
+ behind. See [Running setup again](#running-setup-again).
315
+
230
316
  ## Running setup again
231
317
 
232
318
  Each setup prompt is one-time and each successful run creates a **new** agent
@@ -235,7 +321,7 @@ already-configured machine behaves as follows (characterized in
235
321
  `storage.test.ts`, `config-writers.test.ts`, `runtime.test.ts`, and
236
322
  `runtime-install.test.ts`, #1544/#1569):
237
323
 
238
- - **Re-running an already-consumed setup command** fails cleanly before any
324
+ - **Re-running an already-consumed connector command** fails cleanly before any
239
325
  credential file or runtime configuration is touched — Haven refuses the
240
326
  consumed setup when Connect resolves it (or, in a rare concurrent-run race,
241
327
  at registration). The key pair minted for the attempt exists only in memory
@@ -244,17 +330,44 @@ already-configured machine behaves as follows (characterized in
244
330
  credentials into its own directory under `~/.haven/agents/<agent-id>/`,
245
331
  alongside the previous agent's directory, which stays byte-identical.
246
332
  Nothing is rotated, revoked, or deleted locally.
247
- - **Runtime MCP entries are replaced, not duplicated**: Connect owns the
248
- `haven` and `haven-signer` entries (and the managed Codex/Hermes
249
- equivalents) and re-points them at the newest agent's credentials.
250
- Unrelated MCP servers and configuration are preserved. Without `--name`, one
251
- runtime is therefore wired to one Haven agent — the newest one. With
333
+ - **A bare re-run over a live previous agent is a decision, not a default
334
+ ([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)).** Before
335
+ minting a key or registering, Connect scans the credential root for a
336
+ bare-pair directory that still holds a usable key — the same reading
337
+ `--doctor` classifies as `wired` or `superseded`; `retired`, `orphaned`,
338
+ `parked` and **named** directories never count. If it finds one:
339
+ - an **interactive terminal** is asked to choose — **replace** (below) or
340
+ **install alongside** under a name Connect proposes from the agent's
341
+ display name (e.g. `payment-agent`), collision-checked against the
342
+ directories already there;
343
+ - a **non-interactive run** (`--json`, CI, an agent tool call, a pipe)
344
+ refuses with `wiring_collision` — nothing written, token still unused —
345
+ naming the superseded agent ids and the two flags that resolve it. The
346
+ refusal is written as a **relay instruction**: an agent running the
347
+ dashboard's command may append only `--json` (and `--runtime` after a
348
+ runtime refusal), so it must hand the choice to its user rather than add
349
+ a flag itself, and re-run only with the flag the user picks.
350
+ - **`--replace`** is the unattended answer "yes, replace". `--name <slug>`
351
+ installs alongside. Passing both is a usage error — they contradict.
352
+ - **Replacing re-points the bare pair, then retires the previous directory
353
+ locally.** Connect owns the `haven` and `haven-signer` entries (and the
354
+ managed Codex/Hermes equivalents) and re-points them at the new agent's
355
+ credentials; unrelated MCP servers and configuration are preserved. Once
356
+ the runtime install has actually completed, each superseded directory is
357
+ tombstoned and its local key files removed — the same teardown `--unwire`
358
+ performs — so `--doctor` reads it as `retired` rather than still
359
+ spend-capable. If the install ends with an error code the retirement is
360
+ **skipped**, because the old wiring may still be the only working one; the
361
+ outcome's `superseded_agents_retired_locally` says which happened and
362
+ `retired_agent_ids` names exactly the directories it reached. With
252
363
  `--name`, each agent owns its own suffixed pair and they coexist; see
253
364
  [Running several agents in one runtime](#running-several-agents-in-one-runtime).
254
- - **The previous agent is not revoked by a re-run.** Its credentials remain on
255
- disk and its authority remains whatever its on-chain rules say. Revoke
256
- agents you no longer use from the Haven dashboard, then delete their
257
- credential directories.
365
+ - **The previous agent is not revoked by a re-run — with or without
366
+ `--replace`.** Local retirement is local: its authority remains whatever
367
+ its on-chain rules and the Haven agent page say. Revoke agents you no
368
+ longer use from the Haven dashboard. Connect never calls revoke — that
369
+ route is owner-authenticated, and an agent credential revoking a sibling
370
+ agent would be an agent editing its own authority.
258
371
  - **A re-run never overwrites an existing credential file.** A write that would
259
372
  collide with an existing `identity.json`/`signer.json`/`agent.json` is
260
373
  refused outright (and a partially failed write rolls itself back), so a
@@ -271,10 +384,14 @@ directory, so several agents coexist in one runtime instead of replacing each
271
384
  other:
272
385
 
273
386
  ```sh
274
- npx -y @haven_ai/connect@alpha --setup hv_setup_... --api https://api.haven.example \
387
+ npx -y @haven_ai/connect@<channel> --setup hv_setup_... --api https://api.haven.example \
275
388
  --name research --runtime claude-code
276
389
  ```
277
390
 
391
+ As above, `<channel>` is a placeholder like the rest of this line: take the
392
+ package from the setup response's `connector_package`
393
+ and add `--name` to the command the dashboard gave you.
394
+
278
395
  | | Without `--name` | With `--name research` |
279
396
  |---|---|---|
280
397
  | MCP entries | `haven`, `haven-signer` | `haven-research`, `haven-signer-research` |
@@ -313,16 +430,24 @@ design. So it runs in two phases with the dashboard between them:
313
430
 
314
431
  ```sh
315
432
  # 1. On this machine: generate the new key, print its public address.
316
- npx -y @haven_ai/connect@alpha --rekey [--name research]
433
+ npx -y @haven_ai/connect@<channel> --rekey [--name research]
317
434
 
318
435
  # 2. In the dashboard: agent → Replace signing key → paste that address.
319
436
  # Sign the steps. It shows a new API key ONCE.
320
437
 
321
438
  # 3. Back here: write the new credentials and rewire this agent's MCP pair.
322
- npx -y @haven_ai/connect@alpha --rekey-finish --api-key sk_agent_... \
439
+ npx -y @haven_ai/connect@<channel> --rekey-finish --api-key sk_agent_... \
323
440
  --runtime claude-code [--name research]
324
441
  ```
325
442
 
443
+ **Phase one prints the exact phase-two command — prefer it over the line above.**
444
+ Since [#2423](https://github.com/d-hinders/Haven-AI/issues/2423) the connector
445
+ builds that command from the npm dist-tag **it** was published under, so a build
446
+ installed from a non-production channel tells you to finish with that same
447
+ channel rather than sending you to production mid-re-key. `@alpha` is what
448
+ production hands out and is right for a production install; it is not right for
449
+ every install, which is why the tool computes it and this page cannot.
450
+
326
451
  Between the two phases nothing has changed: the agent keeps working on its old
327
452
  key until you finish. Phase one refuses up front what the backend would refuse
328
453
  anyway — a legacy-rail account, a revoked agent — so you find out before signing
@@ -349,12 +474,19 @@ across the rest is yours.
349
474
  ## Diagnosing a stuck setup: `--doctor` / `--repair` (#1589)
350
475
 
351
476
  ```bash
352
- npx @haven_ai/connect@alpha --doctor --runtime codex-desktop
353
- npx @haven_ai/connect@alpha --doctor --repair --runtime codex-desktop
477
+ npx @haven_ai/connect@<channel> --doctor --runtime codex-desktop
478
+ npx @haven_ai/connect@<channel> --doctor --repair --runtime codex-desktop
354
479
  ```
355
480
 
481
+ `<channel>` is the placeholder defined under the first example — and here it is
482
+ not indifferent: `signer_runtime` compares the sidecar against the manifest of
483
+ the connector **that runs the check**, so a doctor from another channel reports
484
+ a skew that is not there. Use the channel your dashboard hands out.
485
+
356
486
  `--doctor` is read-only and needs NO setup token: it checks the runtime config,
357
- the agent credential files, the pinned signer runtime install, the hosted MCP
487
+ the agent credential files, the pinned signer runtime install (and, since
488
+ #2424, whether that install was made under a local runtime-spec override —
489
+ see the last section of this file), the hosted MCP
358
490
  (authorized `tools/list`), and starts the local signer for a real stdio
359
491
  handshake — reporting its advertised compat versions. Every failing check
360
492
  prints one concrete repair action; the exit code is non-zero on any failure.
@@ -362,10 +494,14 @@ Add `--json` for a machine-readable report. No secret material is ever
362
494
  printed.
363
495
 
364
496
  `--doctor` also probes every OTHER agent credential directory it did not
365
- select (#1688). A re-run of setup mints a NEW agent and retires nothing, so
366
- a directory from a previous setup can hold an API key that still
367
- authenticates — meaning any host that started before the re-run keeps
368
- spending as the agent you believe you replaced. A superseded directory
497
+ select (#1688). A re-run of setup mints a NEW agent and, unless it ran with
498
+ `--replace` (#2551), retires nothing, so a directory from a previous setup
499
+ can hold an API key that still authenticates — meaning any host that started
500
+ before the re-run keeps spending as the agent you believe you replaced. Since
501
+ #2551 a bare setup no longer reaches that state silently: it asks at a
502
+ terminal and refuses everywhere else, so a `superseded` directory now means
503
+ someone chose it — a `--replace` whose install failed, an older connector, or
504
+ a directory this scan could not classify. A superseded directory
369
505
  whose key is still live is a FAILING check naming the agent id, with the
370
506
  repair spelled out: revoke it on the Haven agent page, then remove the
371
507
  directory. An already-revoked one reports as informational; an unreachable
@@ -381,3 +517,85 @@ which.
381
517
  `--repair` re-runs what setup already owns — reinstall the pinned signer
382
518
  runtime, rewrite the wrapper + sidecar, and re-write the runtime config from
383
519
  the STORED credentials. It never touches keys and never needs a new token.
520
+
521
+ ## Installing an unpublished signer / SDK / MCP build (`HAVEN_SIGNER_SPEC`, #2424)
522
+
523
+ Setup installs the connector's **pinned** siblings — `@haven_ai/signer@<pin>`
524
+ and `@haven_ai/sdk@<pin>` into `~/.haven/signer-runtime/<pin>`, and for
525
+ `--local` also `@haven_ai/mcp@<pin>` into `~/.haven/mcp-runtime/<pin>`. That
526
+ is right for every user and wrong for the one developer iterating on the
527
+ signer or SDK, who otherwise has to publish to find out whether a change works
528
+ end to end. Three environment variables name a different spec:
529
+
530
+ | Variable | Replaces | Read by |
531
+ |---|---|---|
532
+ | `HAVEN_SIGNER_SPEC` | `@haven_ai/signer@<pin>` | the signer runtime (default topology) |
533
+ | `HAVEN_SDK_SPEC` | `@haven_ai/sdk@<pin>` | both runtimes — each installs the SDK |
534
+ | `HAVEN_MCP_SPEC` | `@haven_ai/mcp@<pin>` | the `--local` MCP runtime |
535
+
536
+ A value is anything `npm install` accepts for that package: a checkout
537
+ (`file:/abs/path/to/packages/signer`), a tarball from `npm pack`
538
+ (`/abs/haven_ai-signer-0.0.0.tgz`), or an explicit version
539
+ (`@haven_ai/signer@<version>`). Set it in the shell that runs the setup
540
+ command — the command itself is unchanged:
541
+
542
+ ```bash
543
+ HAVEN_SIGNER_SPEC=file:$PWD/packages/signer npx @haven_ai/connect@<channel> --setup <token> --runtime claude-code
544
+ ```
545
+
546
+ `<channel>` is the placeholder defined under the first example.
547
+
548
+ Environment variables rather than a flag, deliberately: the install runs from
549
+ three entry points (`--setup`, `--doctor --repair`, `--rekey-finish`) and all
550
+ three honour the same variables, so a re-key cannot silently reinstall the
551
+ registry build; and the connector command is minted by the dashboard and pasted
552
+ verbatim, often by an agent, so the override sits beside it instead of being
553
+ spliced into a line the developer did not write.
554
+
555
+ **What an active override changes:**
556
+
557
+ - The runtime directory is `~/.haven/signer-runtime/override-<hash>` (or
558
+ `mcp-runtime/override-<hash>`), keyed by a short hash of the **resolved**
559
+ specs — overridden and pinned alike — so it can never poison the
560
+ version-named directory the normal path reuses, and a pinned-sibling bump
561
+ gets a fresh one.
562
+ - The install is **never reused** from an earlier run: a rebuilt `file:`
563
+ package must not be shadowed by a cache hit.
564
+ - Setup prints `RUNTIME SPEC OVERRIDE ACTIVE …` first, naming each variable
565
+ and the pin it replaced; `--rekey-finish` prints the same line.
566
+ - `signer-runtime.json` / `mcp-runtime.json` record the override under
567
+ `runtime_spec_override`, and their `*_version` fields hold what npm actually
568
+ installed rather than the manifest pins.
569
+ - The wrapper the agent client launches carries a
570
+ `// HAVEN RUNTIME SPEC OVERRIDE (#2424): …` comment.
571
+ - `--doctor` reports a failing `runtime_spec_override` check — "runtime spec
572
+ overridden — not the pinned manifest" — whenever the sidecar says the
573
+ install ran under one **or** a `HAVEN_*_SPEC` variable is set in the shell
574
+ running the doctor (a `--repair` from that shell would install it). The
575
+ override is legitimate; the finding is its record. Under an override the
576
+ `signer_runtime` check compares the directory against the sidecar's own
577
+ record, not the manifest.
578
+
579
+ **What it never changes:** the post-setup handshake probe still requires
580
+ every tool in the manifest's `requiredTools` / `requiredSignerTools`, so a
581
+ local build that dropped a tool fails setup exactly like a bad registry
582
+ version would. A set-but-malformed value — empty, containing whitespace or a
583
+ shell metacharacter — is refused **before** npm runs and before anything is
584
+ written, with a message naming the variable. With no variable set, nothing
585
+ here runs: the install arguments, directory, sidecar and wrapper are
586
+ byte-for-byte what they were before the override existed (pinned by exact
587
+ characterization tests in `signer-runtime.test.ts` and
588
+ `local-mcp-runtime.test.ts`).
589
+
590
+ To return to the pinned manifest: unset the variables and run
591
+ `--doctor --repair --runtime <runtime>` (or re-run setup). The override
592
+ directories live under `~/.haven` like every other runtime directory, so the
593
+ same reset that removes `~/.haven` removes them.
594
+
595
+ That is the loop for a build that has **not** merged. For one that has — a
596
+ `@dev` snapshot of all five packages, published from every package-touching
597
+ push to `dev` — and for the owner steps that make the dev dashboard hand out
598
+ `npx -y @haven_ai/connect@dev …` in the first place, see the repository runbook
599
+ [`docs/operations/package-dev-channel.md`](https://github.com/d-hinders/Haven-AI/blob/dev/docs/operations/package-dev-channel.md).
600
+ The two compose: `@dev` picks the connector, these variables pick what it
601
+ installs.