@haven_ai/connect 0.0.0-dev.202610071649.8f72409 → 0.0.0-dev.202610071831.a279c0e

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
@@ -385,8 +385,8 @@ and the API key travels beside it in a header.
385
385
 
386
386
  `superseded_agent_ids` lists the other agent directories on this machine. A
387
387
  re-run mints a NEW agent, and without `--replace` retires nothing, so those
388
- older agents still hold live API and signing keys — revoke them on the Haven
389
- agent page if you meant to replace them. Empty on a clean first run; an empty
388
+ older agents still hold live API and signing keys — use **Remove agent…** on the
389
+ Haven agent page for each if you meant to replace them. Empty on a clean first run; an empty
390
390
  list here is not a guarantee, since a scan that cannot read the credential root
391
391
  also yields one rather than failing a completed setup.
392
392
 
@@ -499,12 +499,24 @@ already-configured machine behaves as follows (characterized in
499
499
  consumed setup when Connect resolves it (or, in a rare concurrent-run race,
500
500
  at registration). The key pair minted for the attempt exists only in memory
501
501
  and is discarded. Start a fresh connection from the Haven dashboard instead.
502
- - **Running a fresh setup on a configured machine** writes the new agent's
503
- credentials into its own directory under `~/.haven/agents/<agent-id>/`,
502
+ - **Running a fresh setup on a configured machine writes the new agent's
503
+ credentials into its own directory under `~/.haven/agents/<slug-or-id>/`,
504
504
  alongside the previous agent's directory, which stays byte-identical.
505
- Nothing is rotated, revoked, or deleted locally.
505
+ Nothing is rotated, revoked, or deleted locally.**
506
+ - **A default setup names its pair from the agent's display name
507
+ ([#3737](https://github.com/d-hinders/Haven-AI/issues/3737)) and never
508
+ collides.** Without `--name`, `--bare` or `--replace`, the connector derives
509
+ the slug itself (`proposeServerSlug`: slugified display name, `agent`
510
+ fallback, numeric suffix when taken) and wires `haven-<slug>` /
511
+ `haven-signer-<slug>` — de-collided against **every** directory under the
512
+ credential root (retired and key-removed ones included, so a slug freed by
513
+ `--unwire` is never reused), and marked `-dev` on a non-production backend
514
+ (Base Sepolia or any chain that is not Base mainnet 8453; an explicit
515
+ `--name` is used verbatim and never altered). Nothing is displaced, so
516
+ nothing is asked. `--bare` opts back into the unnamed pair.
506
517
  - **A bare re-run over a live previous agent is a decision, not a default
507
- ([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)).** Before
518
+ ([#2551](https://github.com/d-hinders/Haven-AI/issues/2551)).** Only a run
519
+ that targets the bare pair — `--bare`, or `--replace` — can collide; before
508
520
  minting a key or registering, Connect scans the credential root for a
509
521
  bare-pair directory that still holds a usable key — the same reading
510
522
  `--doctor` classifies as `wired` or `superseded`; `retired`, `orphaned`,
@@ -521,7 +533,9 @@ already-configured machine behaves as follows (characterized in
521
533
  flag itself. The setup prompt then permits one re-run with the flag the
522
534
  user picks (#3689), on top of `--json` and a `--runtime` retry.
523
535
  - **`--replace`** is the unattended answer "yes, replace". `--name <slug>`
524
- installs alongside. Passing both is a usage error — they contradict.
536
+ installs alongside. `--bare` is the human opt-in to the unnamed pair and
537
+ refuses with `--name`. Passing `--replace --name` is a usage error — they
538
+ contradict.
525
539
  - **Replacing re-points the bare pair, then retires the previous directory
526
540
  locally.** Connect owns the `haven` and `haven-signer` entries (and the
527
541
  managed Codex/Hermes equivalents) and re-points them at the new agent's
@@ -539,8 +553,9 @@ already-configured machine behaves as follows (characterized in
539
553
  [Running several agents in one runtime](#running-several-agents-in-one-runtime).
540
554
  - **The previous agent is not revoked by a re-run — with or without
541
555
  `--replace`.** Local retirement is local: its authority remains whatever
542
- its on-chain rules and the Haven agent page say. Revoke agents you no
543
- longer use from the Haven dashboard. Connect never calls revoke — that
556
+ its on-chain rules and the Haven agent page say. Use **Remove agent…** on
557
+ the Haven agent page for agents you no longer use (or the superseded-agents
558
+ revoke on the setup screen). Connect never calls revoke — that
544
559
  route is owner-authenticated, and an agent credential revoking a sibling
545
560
  agent would be an agent editing its own authority.
546
561
  - **A re-run never overwrites an existing credential file.** A write that would
@@ -567,15 +582,17 @@ As above, `<channel>` is a placeholder like the rest of this line: take the
567
582
  package from the setup response's `connector_package`
568
583
  and add `--name` to the command the dashboard gave you.
569
584
 
570
- | | Without `--name` | With `--name research` |
571
- |---|---|---|
572
- | MCP entries | `haven`, `haven-signer` | `haven-research`, `haven-signer-research` |
573
- | Credentials | `~/.haven/agents/<agent-id>/` | `~/.haven/agents/research/` |
585
+ | | Default (no `--name`) | `--name research` | `--bare` |
586
+ |---|---|---|---|
587
+ | MCP entries | `haven-<slug>`, `haven-signer-<slug>` (slug from the agent's display name, `-dev`-marked on a non-production backend) | `haven-research`, `haven-signer-research` | `haven`, `haven-signer` |
588
+ | Credentials | `~/.haven/agents/<slug>/` | `~/.haven/agents/research/` | `~/.haven/agents/<agent-id>/` |
574
589
 
575
590
  A writer only ever touches the pair it owns, so adding a named agent cannot
576
- disturb the bare pair or another named one. Omitting `--name` is byte-identical
577
- to how the connector behaved before named pairs existed, so nothing already
578
- wired needs changing.
591
+ disturb the bare pair or another named one. **`--bare` wires the bare pair
592
+ byte-identically to how the connector behaved before named pairs existed**, so
593
+ nothing already wired needs changing — it is the one flag (besides
594
+ `--replace`) that reaches the unnamed pair, and it cannot be combined with
595
+ `--name`.
579
596
 
580
597
  The slug is **1–32 lowercase letters, digits and single hyphens**, validated
581
598
  before anything is written, and **immutable once wired** — it is the server name
@@ -615,6 +632,14 @@ npx -y @haven_ai/connect@<channel> --rekey-finish --api-key sk_agent_... \
615
632
  --runtime claude-code [--name research]
616
633
  ```
617
634
 
635
+ **No `--name` needed when exactly one live agent is on the machine.** Since
636
+ [#3737](https://github.com/d-hinders/Haven-AI/issues/3737), `--rekey` without
637
+ `--name` selects the sole agent directory that still holds a stored API key —
638
+ tombstoned and key-removed directories left by `--unwire` do not count — and
639
+ the printed phase-two command carries that agent's exact `--name` when it has
640
+ one. With **several** live agents it refuses and lists them, so you must pass
641
+ `--name <slug>`; a bare agent needs no `--name`, exactly as before.
642
+
618
643
  **Phase one prints the exact phase-two command — prefer it over the line above.**
619
644
  Since [#2423](https://github.com/d-hinders/Haven-AI/issues/2423) the connector
620
645
  builds that command from the npm dist-tag **it** was published under, so a build
@@ -689,8 +714,8 @@ terminal and refuses everywhere else, so a `superseded` directory now means
689
714
  someone chose it — a `--replace` whose install failed, an older connector, or
690
715
  a directory this scan could not classify. A superseded directory
691
716
  whose key is still live is a FAILING check naming the agent id, with the
692
- repair spelled out: revoke it on the Haven agent page, then remove the
693
- directory. An already-revoked one reports as informational; an unreachable
717
+ repair spelled out: use **Remove agent…** on the Haven agent page, then remove
718
+ the directory. An already-revoked one reports as informational; an unreachable
694
719
  probe is a note, never a verdict. Connect never revokes or deletes
695
720
  credentials itself — it reports, you decide. The setup completion output
696
721
  names superseded agents the moment they are created, for the same reason.
package/dist/cli.cjs CHANGED
@@ -325,6 +325,19 @@ async function readStoredCredentials(serverName, agentIdOrSlug, baseDir) {
325
325
  `No Haven credentials at ${directory}. Nothing to re-key \u2014 connect this agent first, or pass the --name you wired it under.`
326
326
  );
327
327
  }
328
+ let discoveredSlug;
329
+ if (!key) {
330
+ const binding = await readJsonFile(path.join(directory, "mcp-server-binding.json"));
331
+ const hostedName = binding && typeof binding === "object" ? asString(binding.server_name) : void 0;
332
+ if (hostedName && hostedName.startsWith("haven-")) {
333
+ const slug = hostedName.slice("haven-".length);
334
+ try {
335
+ assertValidServerSlug(slug);
336
+ discoveredSlug = slug;
337
+ } catch {
338
+ }
339
+ }
340
+ }
328
341
  const agent = await readJsonFile(path.join(directory, "agent.json")) ?? {};
329
342
  const signer = await readJsonFile(path.join(directory, "signer.json")) ?? {};
330
343
  const agentId = asString(identity.agent_id);
@@ -340,6 +353,7 @@ async function readStoredCredentials(serverName, agentIdOrSlug, baseDir) {
340
353
  directory,
341
354
  agentId,
342
355
  apiKey,
356
+ ...discoveredSlug ? { discoveredSlug } : {},
343
357
  apiUrl,
344
358
  hostedMcpUrl,
345
359
  delegateAddress: asString(agent.delegate_address) ?? asString(signer.delegate_address),
@@ -368,7 +382,9 @@ async function discoverSoleAgentDirectory(baseDir) {
368
382
  const candidates = [];
369
383
  for (const entry of entries) {
370
384
  const directory = path.join(root, entry);
371
- if (!await readJsonFile(path.join(directory, "identity.json"))) continue;
385
+ const identity = await readJsonFile(path.join(directory, "identity.json"));
386
+ if (!identity) continue;
387
+ if (!asString(identity.api_key)) continue;
372
388
  if (await readJsonFile(path.join(directory, "TOMBSTONE.json"))) continue;
373
389
  candidates.push(directory);
374
390
  }
@@ -598,6 +614,7 @@ async function listMcpServerBindings(baseDir, excludeDirectory) {
598
614
  var REKEY_PENDING_FILENAME, REKEY_PENDING_TTL_MS, CONNECT_OUTCOME_FILENAME, MCP_SERVER_BINDING_FILENAME;
599
615
  var init_storage = __esm({
600
616
  "src/storage.ts"() {
617
+ init_server_names();
601
618
  REKEY_PENDING_FILENAME = "rekey-pending.json";
602
619
  REKEY_PENDING_TTL_MS = 24 * 60 * 60 * 1e3;
603
620
  CONNECT_OUTCOME_FILENAME = "last-connect-outcome.json";
@@ -620,9 +637,9 @@ var init_runtime_manifest = __esm({
620
637
  mcpPackage: "@haven_ai/mcp",
621
638
  mcpVersion: mcp.MCP_VERSION,
622
639
  sdkPackage: "@haven_ai/sdk",
623
- sdkVersion: "0.0.0-dev.202610071649.8f72409",
640
+ sdkVersion: "0.0.0-dev.202610071831.a279c0e",
624
641
  signerPackage: "@haven_ai/signer",
625
- signerVersion: "0.0.0-dev.202610071649.8f72409",
642
+ signerVersion: "0.0.0-dev.202610071831.a279c0e",
626
643
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
627
644
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
628
645
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -1035,7 +1052,9 @@ async function writeHermesConfig(input, deps) {
1035
1052
  restartRequired: true,
1036
1053
  messages: [
1037
1054
  `Updated Haven MCP entries in ${target}; stored the hosted MCP identity in ${envTarget}.`,
1038
- "Restart Hermes (start a new session; gateway users: /restart), then verify with `hermes mcp list`, `hermes mcp test haven`, and `hermes mcp test haven-signer`.",
1055
+ // #3737: name the pair actually written — a bare run prints exactly the
1056
+ // historical sentence; a named run names its own pair.
1057
+ `Restart Hermes (start a new session; gateway users: /restart), then verify with \`hermes mcp list\`, \`hermes mcp test ${names.hosted}\`, and \`hermes mcp test ${names.signer}\`.`,
1039
1058
  "If several long-lived Hermes processes are running (a gateway plus TUI workers), restart EVERY one: each loads its MCP wiring at startup, so a process started before this setup keeps using its old snapshot.",
1040
1059
  "If no mcp_* tools appear after restart, ensure the MCP SDK is installed in Hermes: pip install mcp"
1041
1060
  ]
@@ -3392,17 +3411,45 @@ async function detectWiringCollision(input) {
3392
3411
  suggestedServerName: proposeServerSlug(input.agentName, taken)
3393
3412
  };
3394
3413
  }
3395
- function proposeServerSlug(agentName, taken) {
3396
- let base = agentName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 32).replace(/-+$/g, "");
3414
+ function proposeServerSlug(agentName, taken, maxBaseLength = 32) {
3415
+ let base = agentName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, maxBaseLength).replace(/-+$/g, "");
3397
3416
  if (!slugIsValid(base)) base = "agent";
3398
3417
  if (!taken.has(base)) return base;
3399
3418
  for (let n = 2; n < 1e3; n += 1) {
3400
3419
  const suffix = `-${n}`;
3401
- const candidate = `${base.slice(0, 32 - suffix.length).replace(/-+$/g, "")}${suffix}`;
3420
+ const candidate = `${base.slice(0, maxBaseLength - suffix.length).replace(/-+$/g, "")}${suffix}`;
3402
3421
  if (slugIsValid(candidate) && !taken.has(candidate)) return candidate;
3403
3422
  }
3404
3423
  throw new Error(`Could not propose an unused server name for ${JSON.stringify(agentName)}.`);
3405
3424
  }
3425
+ function deriveServerSlug(agentName, taken, markNonProduction) {
3426
+ const mark = "-dev";
3427
+ if (!markNonProduction) return proposeServerSlug(agentName, taken);
3428
+ const base = proposeServerSlug(agentName, taken, 32 - mark.length);
3429
+ if (base.endsWith(mark)) return base;
3430
+ const marked = `${base}${mark}`;
3431
+ if (!taken.has(marked)) return marked;
3432
+ return proposeServerSlug(marked, taken);
3433
+ }
3434
+ async function listTakenDirectoryNames(credentialsDir) {
3435
+ const root = defaultCredentialRoot(credentialsDir);
3436
+ let entries = [];
3437
+ try {
3438
+ entries = await promises.readdir(root);
3439
+ } catch {
3440
+ return /* @__PURE__ */ new Set();
3441
+ }
3442
+ const taken = /* @__PURE__ */ new Set();
3443
+ for (const entry of entries) {
3444
+ try {
3445
+ if (!(await promises.stat(path.join(root, entry))).isDirectory()) continue;
3446
+ } catch {
3447
+ continue;
3448
+ }
3449
+ taken.add(entry);
3450
+ }
3451
+ return taken;
3452
+ }
3406
3453
  function slugIsValid(slug) {
3407
3454
  try {
3408
3455
  assertValidServerSlug(slug);
@@ -3418,7 +3465,7 @@ async function promptWiringCollisionResolution(collision, agentName, io) {
3418
3465
  io.write(`Setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring.
3419
3466
  `);
3420
3467
  io.write(" r) replace \u2014 re-point haven / haven-signer at the new agent and retire the previous directory locally\n");
3421
- io.write(" (tombstoned, local key files removed; you still revoke it on the Haven agent page)\n");
3468
+ io.write(" (tombstoned, local key files removed; you still use Remove agent\u2026 on the Haven agent page)\n");
3422
3469
  io.write(` a) alongside \u2014 install as a named agent (suggested: ${collision.suggestedServerName}) with its own
3423
3470
  `);
3424
3471
  io.write(` haven-<name> / haven-signer-<name> pair, leaving the current wiring untouched
@@ -3802,7 +3849,16 @@ async function executeConnect(options, deps, trace) {
3802
3849
  log("Checked local credential storage \u2014 all clear.");
3803
3850
  let serverName = options.serverName;
3804
3851
  let replacing;
3805
- if (!serverName) {
3852
+ if (!serverName && options.deriveServerName) {
3853
+ const taken = await listTakenDirectoryNames(options.credentialsDir);
3854
+ const nonProduction = setup.haven_wallet.chain_id !== PRODUCTION_CHAIN_ID;
3855
+ serverName = deriveServerSlug(setup.agent.name, taken, nonProduction);
3856
+ await assertServerSlugAvailable(serverName, options.credentialsDir);
3857
+ const derivedNames = serverNamesFor(serverName);
3858
+ log(
3859
+ `Naming this agent's MCP pair from its display name: ${derivedNames.hosted} / ${derivedNames.signer}. Nothing was displaced; to wire the bare haven / haven-signer pair instead, re-run with --bare, or choose the name yourself with --name <slug>.`
3860
+ );
3861
+ } else if (!serverName) {
3806
3862
  const collision = await detectWiringCollision({
3807
3863
  credentialsDir: options.credentialsDir,
3808
3864
  agentName: setup.agent.name
@@ -4028,7 +4084,7 @@ async function executeConnect(options, deps, trace) {
4028
4084
  if (supersededAgentsRetiredLocally === true) {
4029
4085
  log("");
4030
4086
  log(
4031
- `Replaced: previous agent(s) ${supersededIds(replacing)} are retired on this machine but NOT revoked \u2014 revoke them on the Haven agent page, then restart EVERY long-lived host (gateways, TUI workers, editors): each holds the MCP wiring snapshot from its own start time, and the tombstone speaks only when a stale host next probes the old path.`
4087
+ `Replaced: previous agent(s) ${supersededIds(replacing)} are retired on this machine but NOT revoked \u2014 use Remove agent\u2026 on the Haven agent page for each (it ends their live budgets), then restart EVERY long-lived host (gateways, TUI workers, editors): each holds the MCP wiring snapshot from its own start time, and the tombstone speaks only when a stale host next probes the old path.`
4032
4088
  );
4033
4089
  } else if (supersededAgentIds.length > 0) {
4034
4090
  log("");
@@ -4036,7 +4092,7 @@ async function executeConnect(options, deps, trace) {
4036
4092
  `Heads-up: this setup created a NEW agent. Your previous agent(s) \u2014 ${supersededAgentIds.join(", ")} \u2014 still exist with their own keys, and any host that was already running keeps acting as them.`
4037
4093
  );
4038
4094
  log(
4039
- `If you meant to replace them: revoke them on the Haven agent page, then restart EVERY long-lived host (gateways, TUI workers, editors) \u2014 each holds the MCP wiring snapshot from its own start time, so after repeated setups each can be stuck on a DIFFERENT old agent. Then remove their directories under ~/.haven/agents (or ${RERUN_HINT} --tombstone <dir> to leave a diagnostic in their place). Run ${RERUN_HINT} --doctor to check whether their keys are still live.`
4095
+ `If you meant to replace them: use Remove agent\u2026 on the Haven agent page for each (it ends their live budgets), then restart EVERY long-lived host (gateways, TUI workers, editors) \u2014 each holds the MCP wiring snapshot from its own start time, so after repeated setups each can be stuck on a DIFFERENT old agent. Then remove their directories under ~/.haven/agents (or ${RERUN_HINT} --tombstone <dir> to leave a diagnostic in their place). Run ${RERUN_HINT} --doctor to check whether their keys are still live.`
4040
4096
  );
4041
4097
  }
4042
4098
  } catch {
@@ -4077,6 +4133,9 @@ async function executeConnect(options, deps, trace) {
4077
4133
  runtimeInstall,
4078
4134
  delegateAddress: registration.delegate_address,
4079
4135
  hostedMcpUrl: registration.hosted_mcp_url,
4136
+ // #3737: the resolved name — derived, explicit, or the bare pair — rides
4137
+ // the machine-readable outcome alongside the prose completion text.
4138
+ serverName,
4080
4139
  supersededAgentIds,
4081
4140
  supersededAgentsRetiredLocally,
4082
4141
  ...replacing ? { retiredAgentIds } : {},
@@ -4151,6 +4210,9 @@ function completionOutcome(input) {
4151
4210
  // #3122: always emitted on a completed run (empty list included), for the
4152
4211
  // same reason as superseded_agent_ids above.
4153
4212
  existing_agents_before_write: input.existingAgentsBeforeWrite ?? [],
4213
+ // #3737: the resolved hosted server name — always present on a completed
4214
+ // run, bare pair included, so a caller never has to parse the prose.
4215
+ server_name: serverNamesFor(input.serverName).hosted,
4154
4216
  ...input.serverNameReboundFrom ? { server_name_rebound_from: input.serverNameReboundFrom } : {},
4155
4217
  ...input.setupChallengeExpiresAt ? { setup_challenge_expires_at: input.setupChallengeExpiresAt } : {},
4156
4218
  ...runtimeInstall.errorCode ? { error: { code: runtimeInstall.errorCode, next_action: nextAction2 } } : {}
@@ -4201,7 +4263,7 @@ async function resolveWiringCollision(collision, agentName, options, deps) {
4201
4263
  }
4202
4264
  throw new ConnectError(
4203
4265
  "wiring_collision",
4204
- `This machine is already wired to a Haven agent with a live key (${supersededIds(collision)}), and setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring. Nothing was written and the Haven setup token is still unused. If you are an AI agent running this command: do NOT add a flag yourself \u2014 relay this to your user and stop. Your user decides: REPLACE the existing wiring (re-run the same command with --replace added, which re-points the pair at the new agent and retires the previous directory locally \u2014 they still revoke the old agent on the Haven agent page), or install ALONGSIDE it (re-run with --name <slug> added, e.g. --name ${collision.suggestedServerName}, which gives the new agent its own haven-<slug> / haven-signer-<slug> pair). Re-run only with the flag your user chooses.`,
4266
+ `This machine is already wired to a Haven agent with a live key (${supersededIds(collision)}), and setting up "${agentName}" on the bare haven / haven-signer pair would replace that wiring. Nothing was written and the Haven setup token is still unused. If you are an AI agent running this command: do NOT add a flag yourself \u2014 relay this to your user and stop. Your user decides: REPLACE the existing wiring (re-run the same command with --replace added, which re-points the pair at the new agent and retires the previous directory locally \u2014 they still use Remove agent\u2026 on the Haven agent page to end the old agent\u2019s budgets), or install ALONGSIDE it (re-run with --name <slug> added, e.g. --name ${collision.suggestedServerName}, which gives the new agent its own haven-<slug> / haven-signer-<slug> pair). Re-run only with the flag your user chooses.`,
4205
4267
  "relay_wiring_collision_to_user",
4206
4268
  { supersededAgentIds: collision.superseded.map((e) => e.agentId), suggestedServerName: collision.suggestedServerName }
4207
4269
  );
@@ -4482,7 +4544,7 @@ async function listOtherAgentIds(baseDir, currentDirectory) {
4482
4544
  function printNextSteps(result, log, approval, approvalUrl) {
4483
4545
  for (const line of completionHandoffLines(result, approval, approvalUrl)) log(line);
4484
4546
  }
4485
- var CONNECTOR_VERSION, CONNECTOR_CLIENT_IDENTITY, CONNECT_OUTCOME_SCHEMA_VERSION, failureOutcomesByError, RERUN_HINT;
4547
+ var CONNECTOR_VERSION, PRODUCTION_CHAIN_ID, CONNECTOR_CLIENT_IDENTITY, CONNECT_OUTCOME_SCHEMA_VERSION, failureOutcomesByError, RERUN_HINT;
4486
4548
  var init_runtime = __esm({
4487
4549
  "src/runtime.ts"() {
4488
4550
  init_api();
@@ -4499,7 +4561,8 @@ var init_runtime = __esm({
4499
4561
  init_tombstone();
4500
4562
  init_local_mcp_runtime();
4501
4563
  init_runtime_manifest();
4502
- CONNECTOR_VERSION = "0.0.0-dev.202610071649.8f72409";
4564
+ CONNECTOR_VERSION = "0.0.0-dev.202610071831.a279c0e";
4565
+ PRODUCTION_CHAIN_ID = 8453;
4503
4566
  CONNECTOR_CLIENT_IDENTITY = `@haven_ai/connect/${CONNECTOR_VERSION}`;
4504
4567
  CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4505
4568
  failureOutcomesByError = /* @__PURE__ */ new WeakMap();
@@ -4709,9 +4772,10 @@ async function startRekey(options, deps = {}) {
4709
4772
  started_at: startedAt,
4710
4773
  expires_at: expiresAt
4711
4774
  });
4775
+ const finishName = options.serverName ?? stored.discoveredSlug;
4712
4776
  const finishCommand = [
4713
4777
  sdk.connectorRerunCommand("--rekey-finish"),
4714
- options.serverName ? `--name ${options.serverName}` : void 0,
4778
+ finishName ? `--name ${finishName}` : void 0,
4715
4779
  "--api-key <the key the dashboard showed you>",
4716
4780
  options.runtime ? `--runtime ${options.runtime}` : "--runtime <your runtime>"
4717
4781
  ].filter(Boolean).join(" ");
@@ -5543,9 +5607,9 @@ async function runDoctor(input, deps = {}) {
5543
5607
  level: supersededLevel,
5544
5608
  detail: supersededLevel === "failed" ? `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}. A host started before your latest setup keeps authenticating (and spending) as the old agent.` : supersededLevel === "advisory" ? `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}. Runtime '${input2.runtime}' has no config file the connector can read, so which of these agents are wired cannot be verified from this machine: a live key here may be an agent you use deliberately, or one a host started before your latest setup is still spending as.` : `${otherEntries.length} other credential dir(s) found \u2014 ${parts.join("; ")}.`,
5545
5609
  ...supersededLevel === "failed" ? {
5546
- repair: `Revoke ${supersededLive.join(", ")} on the Haven agent page, then remove the old director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
5610
+ repair: `Use Remove agent\u2026 on the Haven agent page for ${supersededLive.join(", ")} (it ends each one\u2019s live budgets), then remove the old director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
5547
5611
  } : supersededLevel === "advisory" ? {
5548
- repair: `Check ${supersededLive.join(", ")} on the Haven agent page: revoke the ones you no longer use, then remove their director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
5612
+ repair: `Check ${supersededLive.join(", ")} on the Haven agent page: use Remove agent\u2026 on the ones you no longer use, then remove their director(y/ies) under ~/.haven/agents. Connect never revokes or deletes for you.`
5549
5613
  } : {}
5550
5614
  });
5551
5615
  }
@@ -5770,6 +5834,7 @@ function parseArgs(argv, env = process.env) {
5770
5834
  let dryRun = false;
5771
5835
  let unwireDir;
5772
5836
  let replace = false;
5837
+ let bare = false;
5773
5838
  for (let i = 0; i < argv.length; i += 1) {
5774
5839
  const arg = argv[i];
5775
5840
  if (arg === "--help" || arg === "-h") {
@@ -5817,6 +5882,8 @@ function parseArgs(argv, env = process.env) {
5817
5882
  options.credentialsDir = requireValue(argv, ++i, arg);
5818
5883
  } else if (arg === "--replace") {
5819
5884
  replace = true;
5885
+ } else if (arg === "--bare") {
5886
+ bare = true;
5820
5887
  } else if (arg === "--name") {
5821
5888
  options.serverName = requireValue(argv, ++i, arg);
5822
5889
  assertValidServerSlug(options.serverName);
@@ -5842,16 +5909,23 @@ function parseArgs(argv, env = process.env) {
5842
5909
  if (help) {
5843
5910
  return { options, help, json, doctor, repair, tombstone, rekey };
5844
5911
  }
5845
- if (replace) {
5912
+ if (replace || bare) {
5846
5913
  if (rekeyPhase || tombstoneDir || unwire || doctor || repair || pruneSignerRuntimes2) {
5847
- throw new Error("--replace belongs to a --setup run: it says what to do when the bare haven / haven-signer pair is already wired to another agent.");
5914
+ throw new Error(`${replace ? "--replace" : "--bare"} belongs to a --setup run: it says what to do when the bare haven / haven-signer pair is already wired to another agent.`);
5848
5915
  }
5849
- if (options.serverName) {
5916
+ if (bare && options.serverName) {
5850
5917
  throw new Error(
5851
- "--replace and --name contradict each other: --replace overwrites the bare haven / haven-signer wiring, --name installs alongside it under its own pair. Pass one of them."
5918
+ "--bare and --name contradict each other: --bare wires the bare haven / haven-signer pair, --name installs a named haven-<slug> pair. Pass one of them."
5852
5919
  );
5853
5920
  }
5854
- options.replaceExistingWiring = true;
5921
+ if (replace) {
5922
+ if (options.serverName) {
5923
+ throw new Error(
5924
+ "--replace and --name contradict each other: --replace overwrites the bare haven / haven-signer wiring, --name installs alongside it under its own pair. Pass one of them."
5925
+ );
5926
+ }
5927
+ options.replaceExistingWiring = true;
5928
+ }
5855
5929
  }
5856
5930
  if (destroyKeyMaterial && !unwire) {
5857
5931
  throw new Error("--destroy-key-material only applies to --unwire.");
@@ -5925,6 +5999,9 @@ function parseArgs(argv, env = process.env) {
5925
5999
  if (!options.apiBaseUrl) {
5926
6000
  throw new Error("Missing --api <Haven API URL>.");
5927
6001
  }
6002
+ if (!options.serverName && !bare && !replace) {
6003
+ options.deriveServerName = true;
6004
+ }
5928
6005
  options.apiBaseUrl = options.apiBaseUrl.replace(/\/+$/, "");
5929
6006
  return { options, help, json, doctor, repair, tombstone, rekey };
5930
6007
  }
@@ -5952,13 +6029,19 @@ function helpText() {
5952
6029
  " --replace When this machine is already wired to a different Haven agent on the bare",
5953
6030
  " haven / haven-signer pair, re-point that pair at the new agent and retire the",
5954
6031
  " previous agent directory locally (tombstoned, local key files removed).",
5955
- " Nothing is revoked \u2014 revoke the old agent on the Haven agent page. Without",
5956
- " this flag a non-interactive run REFUSES such a collision (wiring_collision)",
5957
- " and an interactive terminal is asked; --name installs alongside instead.",
6032
+ " Nothing is revoked \u2014 use Remove agent\u2026 on the Haven agent page. Implies",
6033
+ " the bare pair. Without it (or --bare) a non-interactive run REFUSES such a",
6034
+ " collision (wiring_collision) and an interactive terminal is asked.",
6035
+ " --bare Wire the bare haven / haven-signer pair. Without --name a setup now names",
6036
+ " its pair from the agent's display name (haven-<slug> /",
6037
+ " haven-signer-<slug>, de-collided against this machine, ending -dev on a",
6038
+ " non-production backend), so adding an agent never has to displace anyone;",
6039
+ " --bare is the explicit opt-in to the unnamed pair. --name overrides it.",
5958
6040
  " --name <slug> Wiring slug for a NAMED agent: writes haven-<slug> / haven-signer-<slug>",
5959
6041
  " MCP entries and stores credentials at ~/.haven/agents/<slug>/, so several",
5960
6042
  " agents can run side by side in one runtime. 1-32 lowercase letters, digits,",
5961
- " single hyphens; immutable once wired. Omit for the bare haven / haven-signer pair.",
6043
+ " single hyphens; immutable once wired. Without it the pair is named from the",
6044
+ " agent's display name; --bare wires the bare haven / haven-signer pair.",
5962
6045
  " --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
5963
6046
  " --ack-signer Backward-compatible alias for --ack-local-tools.",
5964
6047
  " --local Advanced: install the fully-local Haven MCP (no hosted dependency).",