@haven_ai/connect 0.3.0-alpha.0 → 0.5.0-alpha.1
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 +195 -17
- package/dist/cli.cjs +2494 -1825
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2499 -1830
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2491 -1822
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -5
- package/dist/index.d.ts +63 -5
- package/dist/index.js +2490 -1819
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -87,13 +87,39 @@ every credential directory on the machine and classifies each one:
|
|
|
87
87
|
| `retired` | Tombstoned (see below); key material removed. |
|
|
88
88
|
| `orphaned` | No usable identity and no tombstone. |
|
|
89
89
|
|
|
90
|
-
The exit code is non-zero if **any** wired agent fails **any** check
|
|
90
|
+
The exit code is non-zero if **any** wired agent fails **any** check
|
|
91
|
+
(`level: failed` — see the next section; an advisory does not count) — not
|
|
91
92
|
only the one the report's main section describes. In `--json`, the same
|
|
92
93
|
information is on `agents[]`, each entry carrying `slug`, `agentId`,
|
|
93
94
|
`directory`, `classification` and its own `checks[]`; the flat `checks` array
|
|
94
95
|
is retained and still describes one agent, so a single-agent install reads as
|
|
95
96
|
it always did.
|
|
96
97
|
|
|
98
|
+
### Three verdicts, not two (#3121)
|
|
99
|
+
|
|
100
|
+
Every check carries a `level` — `ok`, `advisory` or `failed` — and the report
|
|
101
|
+
carries the rolled-up `level` over the flat checks and every wired agent's
|
|
102
|
+
checks. Only `failed` reaches the exit code; an advisory is printed with a `!`
|
|
103
|
+
marker, gets its own summary line ("No failures. N advisory finding(s)") and
|
|
104
|
+
exits 0. `ok` on a check and on the report is kept for `--json` consumers and
|
|
105
|
+
means "nothing is broken": it is `true` for `ok` and `advisory`, `false` only
|
|
106
|
+
for `failed`, so `report.ok` is always the exit code's predicate. The doctor
|
|
107
|
+
report stays `version: 1`; `level` is additive.
|
|
108
|
+
|
|
109
|
+
| Level | Meaning | Example |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `ok` | Nothing to say. | The installed signer matches the connector's pin. |
|
|
112
|
+
| `advisory` | Worth reading; nothing is broken. Exit 0. | `signer_runtime`: the install is intact but behind the pinned version ("intact, but outdated" — both versions named, `--repair` offered); `signer_runtime_unused` (#3123): runtime directories nothing references; `mcp_server_name_rebound` (#3122): two local binding records claim one server name. `superseded_agents` on a recognised runtime with no config file the connector can read (Claude Code, `other`): a second live key is reported, and the check says why "wired" cannot be verified from this machine. |
|
|
113
|
+
| `failed` | A real failure with one repair action. Exit 1. | A stale or empty runtime directory; a live key in a directory the runtime's config demonstrably does not use. |
|
|
114
|
+
|
|
115
|
+
What stays blocking is live spend authority: a directory whose stored key
|
|
116
|
+
still authenticates and whose classification is not `wired` fails the run, for
|
|
117
|
+
every non-wired classification, whenever the runtime config could be read. The
|
|
118
|
+
classification itself does not change on Claude Code — only the severity of
|
|
119
|
+
the check that reads it — so `agents[]` reads as before. A runtime string the
|
|
120
|
+
connector does not recognise (`--runtime codex-clii`) is not "no config": it
|
|
121
|
+
fails `runtime_config` naming the allowed values, and demotes nothing.
|
|
122
|
+
|
|
97
123
|
One check is worth calling out: **`identity_match`** compares the agent the
|
|
98
124
|
stored API key actually authenticates as against the `delegate_address` in
|
|
99
125
|
that directory's `signer.json`. A mismatch means the runtime would quote as
|
|
@@ -133,7 +159,9 @@ minutes. Two rules follow:
|
|
|
133
159
|
This replaces the directory's signer wrapper with a diagnostic that logs the
|
|
134
160
|
retirement (agent id, date, reason, restart guidance) to the host's MCP
|
|
135
161
|
stderr log on every probe, and records it in `TOMBSTONE.json` for
|
|
136
|
-
`--doctor
|
|
162
|
+
`--doctor` — with a copy in `~/.haven/tombstones/` (or `.tombstones/` inside
|
|
163
|
+
a custom credentials root), so the retirement stays visible after the
|
|
164
|
+
directory is deleted. It touches no key material and revokes nothing — revoke the
|
|
137
165
|
agent on the Haven agent page yourself. Delete the tombstone only once every
|
|
138
166
|
long-lived host has been restarted.
|
|
139
167
|
|
|
@@ -147,7 +175,12 @@ minutes. Two rules follow:
|
|
|
147
175
|
and a refusal is `{"tombstoned": false, "error": {"code", "next_action"}}`
|
|
148
176
|
with exit 1 — so check the result rather than assuming silence means success
|
|
149
177
|
(#2175). The `message` field is present only for connector-authored refusals;
|
|
150
|
-
an unexpected filesystem error keeps its raw text on stderr alone.
|
|
178
|
+
an unexpected filesystem error keeps its raw text on stderr alone. If only the
|
|
179
|
+
surviving copy fails — the directory IS tombstoned — the run still succeeds
|
|
180
|
+
with `"recordPath": null` and `"mirrorError"` set to the errno code (or
|
|
181
|
+
`mirror_write_failed` when there is none), and `--unwire --json` carries the
|
|
182
|
+
same as `mirror_error`; `--replace` still removes the old directory's key
|
|
183
|
+
files and names the failure in `retirement_mirror_errors` (#3259).
|
|
151
184
|
|
|
152
185
|
### Unwiring an agent (`--unwire`, #2169)
|
|
153
186
|
|
|
@@ -165,13 +198,80 @@ npx @haven_ai/connect@<channel> --unwire --name research [--reason "..."]
|
|
|
165
198
|
touches local files only, so any published connector does the same job.
|
|
166
199
|
|
|
167
200
|
It tombstone-first (so a stale long-lived host still hears `HAVEN-TOMBSTONE`,
|
|
168
|
-
never a masked `ENOENT`),
|
|
201
|
+
never a masked `ENOENT`), releases the directory's local server-name binding
|
|
202
|
+
record (#3122, whatever the key-material decision below), then removes THAT
|
|
203
|
+
agent's hosted + signer pair from
|
|
169
204
|
every runtime config it appears in (Hermes YAML, Codex TOML, the Cursor / VS
|
|
170
205
|
Code / Insiders / Claude Desktop JSON configs), plus the Hermes dotenv API-key
|
|
171
|
-
line — bare `MCP_HAVEN_API_KEY` or named `MCP_HAVEN_<SLUG>_API_KEY`.
|
|
172
|
-
|
|
173
|
-
re-key, the stored API key)
|
|
174
|
-
|
|
206
|
+
line — bare `MCP_HAVEN_API_KEY` or named `MCP_HAVEN_<SLUG>_API_KEY`. Then it
|
|
207
|
+
decides about the target directory's local key material (signer key, any
|
|
208
|
+
abandoned re-key, the stored API key) — and since #3123 it **asks before it
|
|
209
|
+
destroys**.
|
|
210
|
+
|
|
211
|
+
#### Teardown refuses before destroying the recovery credential (#3123)
|
|
212
|
+
|
|
213
|
+
A revoked agent's API key and delegate signature are exactly what the
|
|
214
|
+
sweep-recovery routes still accept — they are the only local means of
|
|
215
|
+
recovering a stranded delegate balance. So after the wiring is removed,
|
|
216
|
+
`--unwire` runs the one read it has (the identity probe, `GET
|
|
217
|
+
/machine-payments/agent` with the stored key — no new network call, no
|
|
218
|
+
backend change) and **refuses to destroy the key material on every answer**:
|
|
219
|
+
|
|
220
|
+
| Probe | What it means | What `--unwire` does |
|
|
221
|
+
| --- | --- | --- |
|
|
222
|
+
| `ok` | The agent is still active: its key still spends. | Refuses; tells you to revoke on the Haven agent page (connect never revokes), then re-run. |
|
|
223
|
+
| `unauthorized` | The key no longer authenticates on normal routes (revoked, archived, paused, pending approval, rotated, or not a key the backend knows — it does not say which). | Refuses; says plainly that a stranded balance **may** exist and the connector **cannot check**; recover first (`haven_sweep_delegate`, or the agent page). |
|
|
224
|
+
| `network_error` / `bad_response` | Could not verify. | Refuses: unknown is not "safe to delete". Retry. |
|
|
225
|
+
| *(no stored API key + URL)* | Nothing the recovery routes would accept. | Proceeds, unprobed — the pre-#3123 shape. |
|
|
226
|
+
|
|
227
|
+
The refusal is exit 1 with the wiring already gone; the key stays in the
|
|
228
|
+
0o600 credential file only (the config and Hermes-env copies are scrubbed
|
|
229
|
+
first, so a refusal leaves the key in the credential file and in any config
|
|
230
|
+
this run could not clean — those are reported `✗` above, never silently),
|
|
231
|
+
and `--doctor` keeps reporting the directory as `superseded` until the key is
|
|
232
|
+
revoked or destroyed — that is the honest state. **`--destroy-key-material`**
|
|
233
|
+
proceeds on every answer, states what it destroyed and that local recovery
|
|
234
|
+
of a stranded balance ends with it. In `--json` the record carries an
|
|
235
|
+
additive `teardown: { status: destroyed | retained | forced, probe, detail,
|
|
236
|
+
remedy? }` and, since #3122, `binding_released` (whether a local
|
|
237
|
+
`mcp-server-binding.json` was removed).
|
|
238
|
+
|
|
239
|
+
The `claude-code` copy of the key (written by `claude mcp add`, into a config
|
|
240
|
+
the connector does not own) is out of scope for `--unwire`, as it always was:
|
|
241
|
+
remove it with `claude mcp remove haven` / `haven-signer` (or the named
|
|
242
|
+
pair) yourself; `--doctor --runtime claude-code` cannot see it either.
|
|
243
|
+
|
|
244
|
+
### Pruning signer-runtime directories (`--prune-signer-runtimes`, #3123)
|
|
245
|
+
|
|
246
|
+
Every pinned version — and, since #2424, every `HAVEN_*_SPEC` override — gets
|
|
247
|
+
its own `~/.haven/signer-runtime/<key>/`, and nothing reclaimed them:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
npx @haven_ai/connect@<channel> --prune-signer-runtimes --dry-run
|
|
251
|
+
npx @haven_ai/connect@<channel> --prune-signer-runtimes
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
It walks the root (so override-keyed `override-<hash>` directories are seen,
|
|
255
|
+
not only manifest versions), **keeps** every directory any credential
|
|
256
|
+
directory names — through its `signer-runtime.json` or through the path its
|
|
257
|
+
wrapper launches — wired, superseded or retired; the prune never decides who
|
|
258
|
+
is live — and the connector's current pin (what
|
|
259
|
+
`--repair` installs), and removes the rest, reporting each with a #3121
|
|
260
|
+
level: kept/removed are `ok`, a dry-run candidate is an `advisory`, a removal
|
|
261
|
+
that failed (a signer process still holding the directory open on a platform
|
|
262
|
+
that refuses the unlink) is `failed` and the only thing that exits 1. On
|
|
263
|
+
POSIX a running signer keeps its open files until it restarts, and since
|
|
264
|
+
only directories no sidecar and no wrapper names go, a configured agent is
|
|
265
|
+
not started against a removed one. The prune trusts those two reference
|
|
266
|
+
sources (the sidecar's `runtime_directory` and the path the wrapper
|
|
267
|
+
launches — so a directory whose sidecar is missing or corrupt but whose
|
|
268
|
+
wrapper is intact keeps its runtime); it reads the default agents root AND
|
|
269
|
+
the parent of an explicit `--credentials-dir`, a union, never either/or. It
|
|
270
|
+
is its own flag — never part of `--repair`, never automatic; `--doctor`
|
|
271
|
+
reports unused directories as an advisory (`signer_runtime_unused`) that
|
|
272
|
+
names this command — names only; sizes come from `--dry-run`, and a kept
|
|
273
|
+
directory is never sized (its row says "not sized"). `--json` emits
|
|
274
|
+
`{ pruned: true, version: 1, root, dry_run, level, removed, reclaimed_bytes, entries[] }`.
|
|
175
275
|
|
|
176
276
|
An **unnamed** pair (`haven` / `haven-signer`) is shared by every unnamed agent
|
|
177
277
|
and is only removed when this directory's wrapper is the one the config
|
|
@@ -181,6 +281,67 @@ revoked on the backend; `connect reports, the user decides` (#1688) survives,
|
|
|
181
281
|
and revocation stays an owner action on the Haven agent page. Restart every
|
|
182
282
|
long-lived host afterwards, as with any retirement.
|
|
183
283
|
|
|
284
|
+
### The wallet warning arrives before the write; names are bound locally (#3122)
|
|
285
|
+
|
|
286
|
+
Setup guards the **name slot** (a taken `--name`, the bare pair already wired
|
|
287
|
+
— #2551 refuses or asks) but never the **wallet**: a machine can carry several
|
|
288
|
+
agents with live keys, and until #3122 the "your previous agent(s) still exist
|
|
289
|
+
with their own keys" heads-up was printed only after the credentials were
|
|
290
|
+
written (that completion heads-up, #1688, is unchanged — this slice ADDS an
|
|
291
|
+
earlier notice). Two things changed, both from local files only (no network call is
|
|
292
|
+
added; the backend is not asked whether any key still authenticates):
|
|
293
|
+
|
|
294
|
+
- **Before the key is minted or anything is written**, setup names every
|
|
295
|
+
other credential directory that still holds a stored key, with the account
|
|
296
|
+
it can spend from — `Heads-up (before anything is written): this machine
|
|
297
|
+
already carries N agent director(y/ies) with stored keys — <id> (spends
|
|
298
|
+
from 0x…)`. It **warns, it does not refuse** (owner decision on #3119): a
|
|
299
|
+
refusal would change behaviour for every non-interactive caller, including
|
|
300
|
+
the dashboard's connect flow. `--json` carries the same list as
|
|
301
|
+
`existing_agents_before_write: [{ agent_id, account_address }]`, always
|
|
302
|
+
present on a completed run (`[]` on a clean machine) — a subset of
|
|
303
|
+
`superseded_agent_ids`, which names every other directory that has an
|
|
304
|
+
`identity.json` at all, key-less and tombstoned ones included.
|
|
305
|
+
"Holds a stored key" is the whole test: a tombstoned directory whose key
|
|
306
|
+
files were never deleted is counted (it can still spend), which is the
|
|
307
|
+
opposite side of the doctor's rule that ignores a retired directory's
|
|
308
|
+
binding record (it launches nothing). The scan covers exactly the credential
|
|
309
|
+
root this run writes into — `~/.haven/agents`, or the root given by
|
|
310
|
+
`--credentials-dir` — and never unions the two (#2551's collision check uses
|
|
311
|
+
the same root; `--doctor` shares the no-union rule but reads its
|
|
312
|
+
`--credentials-dir` as the agent directory itself, so its root is that
|
|
313
|
+
directory's parent; #3123's union was for the one shared `signer-runtime`
|
|
314
|
+
root).
|
|
315
|
+
- **Each setup records what it bound** in a non-secret
|
|
316
|
+
`mcp-server-binding.json` beside `last-connect-outcome.json`: `{ version:
|
|
317
|
+
1, server_name, signer_name, agent_id, api_url, hosted_mcp_url?, bound_at }`
|
|
318
|
+
— no key material, by construction (pinned by test). Per credential
|
|
319
|
+
directory, not machine-wide: two concurrent setups never write the same
|
|
320
|
+
file, and "who else claims this name" is answered by scanning the root,
|
|
321
|
+
which setup and the doctor already do. When a setup is about to bind a
|
|
322
|
+
server name another directory's record holds, it says so before the write —
|
|
323
|
+
previous agent (the NEWEST holder when several records claim the name),
|
|
324
|
+
previous backend URL, when — and flags a **DIFFERENT backend** explicitly, because that is the case the backend cannot see and
|
|
325
|
+
the one that silently repoints a saved session; `--json` carries it as
|
|
326
|
+
`server_name_rebound_from: { server_name, agent_id, api_url, bound_at,
|
|
327
|
+
backend_changed }`. `--unwire` releases the record (its `--json` gains
|
|
328
|
+
`binding_released`), and so does a `--replace` retirement, so a legitimately
|
|
329
|
+
free name does not warn forever; `--tombstone` leaves it, and `--doctor`
|
|
330
|
+
ignores a retired directory's record. `--doctor` reports two records
|
|
331
|
+
claiming one name — excluding tombstoned (retired) directories — as the
|
|
332
|
+
`mcp_server_name_rebound` advisory (oldest → newest, backend change
|
|
333
|
+
flagged). The record is written
|
|
334
|
+
after the credentials, best-effort: a directory with credentials and no
|
|
335
|
+
record (a crash in between, or one retired by hand) is not proof the name
|
|
336
|
+
is free — the absence of a warning is not a guarantee, as with
|
|
337
|
+
`superseded_agent_ids`.
|
|
338
|
+
|
|
339
|
+
**What wins when the local record disagrees with the backend.** The backend's
|
|
340
|
+
`agents.mcp_server_name` is the authority for the same backend; the local
|
|
341
|
+
record is a reporting aid that covers the two cases the backend cannot —
|
|
342
|
+
another backend, or offline — and it never asserts itself as the backend's
|
|
343
|
+
current truth. Every message that reads it says "locally recorded".
|
|
344
|
+
|
|
184
345
|
### Structured output for automation
|
|
185
346
|
|
|
186
347
|
Pass `--json` when a launcher needs a machine-readable completion record. Connect
|
|
@@ -192,9 +353,13 @@ whenever ready and verify later with the read-only `haven_get_agent`
|
|
|
192
353
|
tool. The object includes runtime/topology status,
|
|
193
354
|
probe result, activation and next-action guidance, approval state/expiry (null
|
|
194
355
|
when the backend does not provide an approval expiry) and `approval.url`, the two
|
|
195
|
-
read-only verification tools, `hosted_mcp_url`, `superseded_agent_ids`,
|
|
196
|
-
|
|
197
|
-
with
|
|
356
|
+
read-only verification tools, `hosted_mcp_url`, `superseded_agent_ids`,
|
|
357
|
+
`existing_agents_before_write` (#3122 — the other live-keyed directories named
|
|
358
|
+
before the first write, each with the account it spends from), and — on a run
|
|
359
|
+
that replaced existing wiring — `superseded_agents_retired_locally` with
|
|
360
|
+
`retired_agent_ids`, plus `retirement_mirror_errors` (#3259) only when a retired
|
|
361
|
+
directory's surviving ledger record could not be written; `server_name_rebound_from` (#3122) appears only when the
|
|
362
|
+
run took a server name over from another directory's local binding record. It
|
|
198
363
|
contains no API key, private key, credential
|
|
199
364
|
contents, full credential paths, or full delegate address. The same redacted
|
|
200
365
|
object is available to library callers as `runConnect(...).outcome`; the older
|
|
@@ -361,9 +526,11 @@ already-configured machine behaves as follows (characterized in
|
|
|
361
526
|
managed Codex/Hermes equivalents) and re-points them at the new agent's
|
|
362
527
|
credentials; unrelated MCP servers and configuration are preserved. Once
|
|
363
528
|
the runtime install has actually completed, each superseded directory is
|
|
364
|
-
tombstoned and its local key files removed — the
|
|
365
|
-
|
|
366
|
-
|
|
529
|
+
tombstoned and its local key files removed — the unconditional key-material
|
|
530
|
+
teardown; `--unwire` itself runs that teardown only when its #3123 probe says
|
|
531
|
+
there is nothing to preserve, and `--replace` deliberately does not probe
|
|
532
|
+
(the owner has just chosen to overwrite; see the #3119 follow-up) — so
|
|
533
|
+
`--doctor` reads it as `retired` rather than still spend-capable. If the install ends with an error code the retirement is
|
|
367
534
|
**skipped**, because the old wiring may still be the only working one; the
|
|
368
535
|
outcome's `superseded_agents_retired_locally` says which happened and
|
|
369
536
|
`retired_agent_ids` names exactly the directories it reached. With
|
|
@@ -481,6 +648,7 @@ across the rest is yours.
|
|
|
481
648
|
## Diagnosing a stuck setup: `--doctor` / `--repair` (#1589)
|
|
482
649
|
|
|
483
650
|
```bash
|
|
651
|
+
npx @haven_ai/connect@<channel> --doctor
|
|
484
652
|
npx @haven_ai/connect@<channel> --doctor --runtime codex-desktop
|
|
485
653
|
npx @haven_ai/connect@<channel> --doctor --repair --runtime codex-desktop
|
|
486
654
|
```
|
|
@@ -490,15 +658,25 @@ not indifferent: `signer_runtime` compares the sidecar against the manifest of
|
|
|
490
658
|
the connector **that runs the check**, so a doctor from another channel reports
|
|
491
659
|
a skew that is not there. Use the channel your dashboard hands out.
|
|
492
660
|
|
|
661
|
+
`--runtime` is optional for `--doctor` and required for `--repair` (#3210).
|
|
662
|
+
Without the flag the doctor checks the runtime the setup recorded in the
|
|
663
|
+
agent directory's `last-connect-outcome.json` (#3120), and the `runtime_config`
|
|
664
|
+
verdict says which runtime it resolved and from which file; when no
|
|
665
|
+
record names one, the `runtime_config` check FAILS with "Runtime is unknown —
|
|
666
|
+
the runtime config was NOT checked" and the exit code is 1 — never a pass on a
|
|
667
|
+
config it did not open. `--repair` rewrites that config, so the runtime it
|
|
668
|
+
rewrites is named on the command line rather than inherited from a record on
|
|
669
|
+
disk: a flagless `--repair` is refused before anything runs.
|
|
670
|
+
|
|
493
671
|
`--doctor` is read-only and needs NO setup token: it checks the runtime config,
|
|
494
672
|
the agent credential files, the pinned signer runtime install (and, since
|
|
495
673
|
#2424, whether that install was made under a local runtime-spec override —
|
|
496
674
|
see the last section of this file), the hosted MCP
|
|
497
675
|
(authorized `tools/list`), and starts the local signer for a real stdio
|
|
498
676
|
handshake — reporting its advertised compat versions. Every failing check
|
|
499
|
-
prints one concrete repair action; the exit code is non-zero on any failure
|
|
500
|
-
|
|
501
|
-
printed.
|
|
677
|
+
prints one concrete repair action; the exit code is non-zero on any failure
|
|
678
|
+
and zero on an advisory (see "Three verdicts, not two" above). Add `--json`
|
|
679
|
+
for a machine-readable report. No secret material is ever printed.
|
|
502
680
|
|
|
503
681
|
`--doctor` also probes every OTHER agent credential directory it did not
|
|
504
682
|
select (#1688). A re-run of setup mints a NEW agent and, unless it ran with
|