@haven_ai/connect 0.2.1-alpha.0 → 0.4.0-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 +174 -15
- package/dist/cli.cjs +660 -89
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +661 -90
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +660 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -8
- package/dist/index.d.ts +46 -8
- package/dist/index.js +661 -90
- 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
|
|
@@ -165,13 +191,80 @@ npx @haven_ai/connect@<channel> --unwire --name research [--reason "..."]
|
|
|
165
191
|
touches local files only, so any published connector does the same job.
|
|
166
192
|
|
|
167
193
|
It tombstone-first (so a stale long-lived host still hears `HAVEN-TOMBSTONE`,
|
|
168
|
-
never a masked `ENOENT`),
|
|
194
|
+
never a masked `ENOENT`), releases the directory's local server-name binding
|
|
195
|
+
record (#3122, whatever the key-material decision below), then removes THAT
|
|
196
|
+
agent's hosted + signer pair from
|
|
169
197
|
every runtime config it appears in (Hermes YAML, Codex TOML, the Cursor / VS
|
|
170
198
|
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
|
-
|
|
199
|
+
line — bare `MCP_HAVEN_API_KEY` or named `MCP_HAVEN_<SLUG>_API_KEY`. Then it
|
|
200
|
+
decides about the target directory's local key material (signer key, any
|
|
201
|
+
abandoned re-key, the stored API key) — and since #3123 it **asks before it
|
|
202
|
+
destroys**.
|
|
203
|
+
|
|
204
|
+
#### Teardown refuses before destroying the recovery credential (#3123)
|
|
205
|
+
|
|
206
|
+
A revoked agent's API key and delegate signature are exactly what the
|
|
207
|
+
sweep-recovery routes still accept — they are the only local means of
|
|
208
|
+
recovering a stranded delegate balance. So after the wiring is removed,
|
|
209
|
+
`--unwire` runs the one read it has (the identity probe, `GET
|
|
210
|
+
/machine-payments/agent` with the stored key — no new network call, no
|
|
211
|
+
backend change) and **refuses to destroy the key material on every answer**:
|
|
212
|
+
|
|
213
|
+
| Probe | What it means | What `--unwire` does |
|
|
214
|
+
| --- | --- | --- |
|
|
215
|
+
| `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. |
|
|
216
|
+
| `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). |
|
|
217
|
+
| `network_error` / `bad_response` | Could not verify. | Refuses: unknown is not "safe to delete". Retry. |
|
|
218
|
+
| *(no stored API key + URL)* | Nothing the recovery routes would accept. | Proceeds, unprobed — the pre-#3123 shape. |
|
|
219
|
+
|
|
220
|
+
The refusal is exit 1 with the wiring already gone; the key stays in the
|
|
221
|
+
0o600 credential file only (the config and Hermes-env copies are scrubbed
|
|
222
|
+
first, so a refusal leaves the key in the credential file and in any config
|
|
223
|
+
this run could not clean — those are reported `✗` above, never silently),
|
|
224
|
+
and `--doctor` keeps reporting the directory as `superseded` until the key is
|
|
225
|
+
revoked or destroyed — that is the honest state. **`--destroy-key-material`**
|
|
226
|
+
proceeds on every answer, states what it destroyed and that local recovery
|
|
227
|
+
of a stranded balance ends with it. In `--json` the record carries an
|
|
228
|
+
additive `teardown: { status: destroyed | retained | forced, probe, detail,
|
|
229
|
+
remedy? }` and, since #3122, `binding_released` (whether a local
|
|
230
|
+
`mcp-server-binding.json` was removed).
|
|
231
|
+
|
|
232
|
+
The `claude-code` copy of the key (written by `claude mcp add`, into a config
|
|
233
|
+
the connector does not own) is out of scope for `--unwire`, as it always was:
|
|
234
|
+
remove it with `claude mcp remove haven` / `haven-signer` (or the named
|
|
235
|
+
pair) yourself; `--doctor --runtime claude-code` cannot see it either.
|
|
236
|
+
|
|
237
|
+
### Pruning signer-runtime directories (`--prune-signer-runtimes`, #3123)
|
|
238
|
+
|
|
239
|
+
Every pinned version — and, since #2424, every `HAVEN_*_SPEC` override — gets
|
|
240
|
+
its own `~/.haven/signer-runtime/<key>/`, and nothing reclaimed them:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
npx @haven_ai/connect@<channel> --prune-signer-runtimes --dry-run
|
|
244
|
+
npx @haven_ai/connect@<channel> --prune-signer-runtimes
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
It walks the root (so override-keyed `override-<hash>` directories are seen,
|
|
248
|
+
not only manifest versions), **keeps** every directory any credential
|
|
249
|
+
directory names — through its `signer-runtime.json` or through the path its
|
|
250
|
+
wrapper launches — wired, superseded or retired; the prune never decides who
|
|
251
|
+
is live — and the connector's current pin (what
|
|
252
|
+
`--repair` installs), and removes the rest, reporting each with a #3121
|
|
253
|
+
level: kept/removed are `ok`, a dry-run candidate is an `advisory`, a removal
|
|
254
|
+
that failed (a signer process still holding the directory open on a platform
|
|
255
|
+
that refuses the unlink) is `failed` and the only thing that exits 1. On
|
|
256
|
+
POSIX a running signer keeps its open files until it restarts, and since
|
|
257
|
+
only directories no sidecar and no wrapper names go, a configured agent is
|
|
258
|
+
not started against a removed one. The prune trusts those two reference
|
|
259
|
+
sources (the sidecar's `runtime_directory` and the path the wrapper
|
|
260
|
+
launches — so a directory whose sidecar is missing or corrupt but whose
|
|
261
|
+
wrapper is intact keeps its runtime); it reads the default agents root AND
|
|
262
|
+
the parent of an explicit `--credentials-dir`, a union, never either/or. It
|
|
263
|
+
is its own flag — never part of `--repair`, never automatic; `--doctor`
|
|
264
|
+
reports unused directories as an advisory (`signer_runtime_unused`) that
|
|
265
|
+
names this command — names only; sizes come from `--dry-run`, and a kept
|
|
266
|
+
directory is never sized (its row says "not sized"). `--json` emits
|
|
267
|
+
`{ pruned: true, version: 1, root, dry_run, level, removed, reclaimed_bytes, entries[] }`.
|
|
175
268
|
|
|
176
269
|
An **unnamed** pair (`haven` / `haven-signer`) is shared by every unnamed agent
|
|
177
270
|
and is only removed when this directory's wrapper is the one the config
|
|
@@ -181,6 +274,67 @@ revoked on the backend; `connect reports, the user decides` (#1688) survives,
|
|
|
181
274
|
and revocation stays an owner action on the Haven agent page. Restart every
|
|
182
275
|
long-lived host afterwards, as with any retirement.
|
|
183
276
|
|
|
277
|
+
### The wallet warning arrives before the write; names are bound locally (#3122)
|
|
278
|
+
|
|
279
|
+
Setup guards the **name slot** (a taken `--name`, the bare pair already wired
|
|
280
|
+
— #2551 refuses or asks) but never the **wallet**: a machine can carry several
|
|
281
|
+
agents with live keys, and until #3122 the "your previous agent(s) still exist
|
|
282
|
+
with their own keys" heads-up was printed only after the credentials were
|
|
283
|
+
written (that completion heads-up, #1688, is unchanged — this slice ADDS an
|
|
284
|
+
earlier notice). Two things changed, both from local files only (no network call is
|
|
285
|
+
added; the backend is not asked whether any key still authenticates):
|
|
286
|
+
|
|
287
|
+
- **Before the key is minted or anything is written**, setup names every
|
|
288
|
+
other credential directory that still holds a stored key, with the account
|
|
289
|
+
it can spend from — `Heads-up (before anything is written): this machine
|
|
290
|
+
already carries N agent director(y/ies) with stored keys — <id> (spends
|
|
291
|
+
from 0x…)`. It **warns, it does not refuse** (owner decision on #3119): a
|
|
292
|
+
refusal would change behaviour for every non-interactive caller, including
|
|
293
|
+
the dashboard's connect flow. `--json` carries the same list as
|
|
294
|
+
`existing_agents_before_write: [{ agent_id, account_address }]`, always
|
|
295
|
+
present on a completed run (`[]` on a clean machine) — a subset of
|
|
296
|
+
`superseded_agent_ids`, which names every other directory that has an
|
|
297
|
+
`identity.json` at all, key-less and tombstoned ones included.
|
|
298
|
+
"Holds a stored key" is the whole test: a tombstoned directory whose key
|
|
299
|
+
files were never deleted is counted (it can still spend), which is the
|
|
300
|
+
opposite side of the doctor's rule that ignores a retired directory's
|
|
301
|
+
binding record (it launches nothing). The scan covers exactly the credential
|
|
302
|
+
root this run writes into — `~/.haven/agents`, or the root given by
|
|
303
|
+
`--credentials-dir` — and never unions the two (#2551's collision check uses
|
|
304
|
+
the same root; `--doctor` shares the no-union rule but reads its
|
|
305
|
+
`--credentials-dir` as the agent directory itself, so its root is that
|
|
306
|
+
directory's parent; #3123's union was for the one shared `signer-runtime`
|
|
307
|
+
root).
|
|
308
|
+
- **Each setup records what it bound** in a non-secret
|
|
309
|
+
`mcp-server-binding.json` beside `last-connect-outcome.json`: `{ version:
|
|
310
|
+
1, server_name, signer_name, agent_id, api_url, hosted_mcp_url?, bound_at }`
|
|
311
|
+
— no key material, by construction (pinned by test). Per credential
|
|
312
|
+
directory, not machine-wide: two concurrent setups never write the same
|
|
313
|
+
file, and "who else claims this name" is answered by scanning the root,
|
|
314
|
+
which setup and the doctor already do. When a setup is about to bind a
|
|
315
|
+
server name another directory's record holds, it says so before the write —
|
|
316
|
+
previous agent (the NEWEST holder when several records claim the name),
|
|
317
|
+
previous backend URL, when — and flags a **DIFFERENT backend** explicitly, because that is the case the backend cannot see and
|
|
318
|
+
the one that silently repoints a saved session; `--json` carries it as
|
|
319
|
+
`server_name_rebound_from: { server_name, agent_id, api_url, bound_at,
|
|
320
|
+
backend_changed }`. `--unwire` releases the record (its `--json` gains
|
|
321
|
+
`binding_released`), and so does a `--replace` retirement, so a legitimately
|
|
322
|
+
free name does not warn forever; `--tombstone` leaves it, and `--doctor`
|
|
323
|
+
ignores a retired directory's record. `--doctor` reports two records
|
|
324
|
+
claiming one name — excluding tombstoned (retired) directories — as the
|
|
325
|
+
`mcp_server_name_rebound` advisory (oldest → newest, backend change
|
|
326
|
+
flagged). The record is written
|
|
327
|
+
after the credentials, best-effort: a directory with credentials and no
|
|
328
|
+
record (a crash in between, or one retired by hand) is not proof the name
|
|
329
|
+
is free — the absence of a warning is not a guarantee, as with
|
|
330
|
+
`superseded_agent_ids`.
|
|
331
|
+
|
|
332
|
+
**What wins when the local record disagrees with the backend.** The backend's
|
|
333
|
+
`agents.mcp_server_name` is the authority for the same backend; the local
|
|
334
|
+
record is a reporting aid that covers the two cases the backend cannot —
|
|
335
|
+
another backend, or offline — and it never asserts itself as the backend's
|
|
336
|
+
current truth. Every message that reads it says "locally recorded".
|
|
337
|
+
|
|
184
338
|
### Structured output for automation
|
|
185
339
|
|
|
186
340
|
Pass `--json` when a launcher needs a machine-readable completion record. Connect
|
|
@@ -192,9 +346,12 @@ whenever ready and verify later with the read-only `haven_get_agent`
|
|
|
192
346
|
tool. The object includes runtime/topology status,
|
|
193
347
|
probe result, activation and next-action guidance, approval state/expiry (null
|
|
194
348
|
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
|
|
349
|
+
read-only verification tools, `hosted_mcp_url`, `superseded_agent_ids`,
|
|
350
|
+
`existing_agents_before_write` (#3122 — the other live-keyed directories named
|
|
351
|
+
before the first write, each with the account it spends from), and — on a run
|
|
352
|
+
that replaced existing wiring — `superseded_agents_retired_locally` with
|
|
353
|
+
`retired_agent_ids`; `server_name_rebound_from` (#3122) appears only when the
|
|
354
|
+
run took a server name over from another directory's local binding record. It
|
|
198
355
|
contains no API key, private key, credential
|
|
199
356
|
contents, full credential paths, or full delegate address. The same redacted
|
|
200
357
|
object is available to library callers as `runConnect(...).outcome`; the older
|
|
@@ -361,9 +518,11 @@ already-configured machine behaves as follows (characterized in
|
|
|
361
518
|
managed Codex/Hermes equivalents) and re-points them at the new agent's
|
|
362
519
|
credentials; unrelated MCP servers and configuration are preserved. Once
|
|
363
520
|
the runtime install has actually completed, each superseded directory is
|
|
364
|
-
tombstoned and its local key files removed — the
|
|
365
|
-
|
|
366
|
-
|
|
521
|
+
tombstoned and its local key files removed — the unconditional key-material
|
|
522
|
+
teardown; `--unwire` itself runs that teardown only when its #3123 probe says
|
|
523
|
+
there is nothing to preserve, and `--replace` deliberately does not probe
|
|
524
|
+
(the owner has just chosen to overwrite; see the #3119 follow-up) — so
|
|
525
|
+
`--doctor` reads it as `retired` rather than still spend-capable. If the install ends with an error code the retirement is
|
|
367
526
|
**skipped**, because the old wiring may still be the only working one; the
|
|
368
527
|
outcome's `superseded_agents_retired_locally` says which happened and
|
|
369
528
|
`retired_agent_ids` names exactly the directories it reached. With
|
|
@@ -496,9 +655,9 @@ the agent credential files, the pinned signer runtime install (and, since
|
|
|
496
655
|
see the last section of this file), the hosted MCP
|
|
497
656
|
(authorized `tools/list`), and starts the local signer for a real stdio
|
|
498
657
|
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.
|
|
658
|
+
prints one concrete repair action; the exit code is non-zero on any failure
|
|
659
|
+
and zero on an advisory (see "Three verdicts, not two" above). Add `--json`
|
|
660
|
+
for a machine-readable report. No secret material is ever printed.
|
|
502
661
|
|
|
503
662
|
`--doctor` also probes every OTHER agent credential directory it did not
|
|
504
663
|
select (#1688). A re-run of setup mints a NEW agent and, unless it ran with
|