@haven_ai/connect 0.1.31-alpha.0 → 0.1.32-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/dist/index.d.ts CHANGED
@@ -190,6 +190,37 @@ interface PreflightCredentialStorageInput {
190
190
  declare function preflightCredentialStorage(input?: PreflightCredentialStorageInput): Promise<string>;
191
191
  declare function writeCredentialFiles(input: WriteCredentialInput): Promise<StoredCredentialPaths>;
192
192
  declare function defaultAgentDirectory(agentId: string, baseDir?: string): string;
193
+ /**
194
+ * Where the terminal `ConnectOutcome` is parked so a caller that lost the
195
+ * stream can still read the verdict.
196
+ *
197
+ * The field failure this exists for (2026-08-28, connector 0.1.31-alpha.0):
198
+ * setup SUCCEEDED, but the agent driving it stopped watching during the cold
199
+ * signer install, so the connector's final `--json` object — the only place
200
+ * the restart instruction and the read-only verification sequence live — was
201
+ * emitted into a stream nobody was reading. The agent reverse-engineered
202
+ * completion from `codex mcp list` instead.
203
+ */
204
+ declare const CONNECT_OUTCOME_FILENAME = "last-connect-outcome.json";
205
+ /**
206
+ * Persist the terminal outcome next to the credentials it describes.
207
+ *
208
+ * Non-secret by construction: the content is exactly the object already
209
+ * emitted on stdout, whose secret-freedom is pinned by its own tests. This
210
+ * function never reads or embeds credential-file contents — it serializes what
211
+ * it is handed and nothing else. Written 0o600 anyway, because it lives inside
212
+ * a 0o700 credential directory and there is no reason to widen it.
213
+ *
214
+ * Overwrites, unlike every other write in this file: "the last outcome" is a
215
+ * single slot, not a credential, so `assertDoesNotExist` would be wrong here —
216
+ * a stale verdict left in place is exactly the failure the file exists to
217
+ * prevent.
218
+ *
219
+ * Throws on failure. Best-effort is the CALLER's contract (`runConnect` wraps
220
+ * both call sites), so that a test can inject a writer that fails and still
221
+ * assert the run completes.
222
+ */
223
+ declare function writeConnectOutcomeRecord(directory: string, outcome: unknown, warn?: (message: string) => void): Promise<string>;
193
224
 
194
225
  type RuntimeId = 'claude-code' | 'codex-desktop' | 'codex-cli' | 'cursor' | 'vscode' | 'vscode-insiders' | 'claude-desktop' | 'hermes' | 'other';
195
226
  type RestartMode = 'restart-session' | 'restart-app' | 'hot-reload' | 'manual';
@@ -447,7 +478,102 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
447
478
  restartRequired: boolean;
448
479
  };
449
480
 
450
- declare const CONNECTOR_VERSION = "0.1.31-alpha.0";
481
+ /**
482
+ * Installed-client scan + interactive pick (#1719).
483
+ *
484
+ * The rung that answers "which runtime am I configuring?" for a HUMAN in a
485
+ * plain terminal, where there is no agent shell to detect. Two properties hold
486
+ * it up, and both are load-bearing:
487
+ *
488
+ * 1. **Only clients the connector can actually write.** A row that cannot be
489
+ * configured is not a choice, it is a dead end wearing a choice's clothes.
490
+ * 2. **The scan populates the choices; it never selects.** Finding exactly one
491
+ * installed app tells you what EXISTS, not where the user wants their agent
492
+ * to run — and the cost of being wrong is an API key and a delegate key
493
+ * written into an app the user does not use. `scanInstalledClients` returns
494
+ * candidates and nothing else; only an answer typed at the prompt resolves
495
+ * a runtime.
496
+ */
497
+ interface InstalledClientCandidate {
498
+ runtime: RuntimeId;
499
+ label: string;
500
+ /** What made this a candidate — shown at the prompt so the pick is informed. */
501
+ detail: string;
502
+ /**
503
+ * The config file Haven would write for this client, when it owns one.
504
+ * `null` for Claude Code, which is configured through its own CLI. Set
505
+ * regardless of which evidence found the client — `evidence` is what says
506
+ * whether that file exists today.
507
+ */
508
+ configPath: string | null;
509
+ evidence: 'config-file' | 'client-directory';
510
+ }
511
+ interface ScanInstalledClientsOptions {
512
+ homeDir?: string;
513
+ /** Workspace root, for the project-local `.vscode/` marker. */
514
+ cwd?: string;
515
+ env?: NodeJS.ProcessEnv;
516
+ /** Injectable so the scan is testable without a populated home directory. */
517
+ exists?: (path: string) => Promise<boolean>;
518
+ }
519
+ declare function scanInstalledClients(options?: ScanInstalledClientsOptions): Promise<InstalledClientCandidate[]>;
520
+ /**
521
+ * The scan's findings as DATA, for the `--json` refusal (#2174).
522
+ *
523
+ * The interactive prompt is deliberately omitted under `--json`, which threw
524
+ * this signal away exactly where it was most useful: an agent retrying a
525
+ * `runtime_undetermined` refusal picked from a nine-value menu on
526
+ * self-knowledge alone, while the connector already knew which client configs
527
+ * exist on the machine.
528
+ *
529
+ * Property 2 above is preserved verbatim and is the reason this returns a
530
+ * HINT rather than a runtime: the caller still has to refuse. Finding exactly
531
+ * one installed app tells you what exists, not where the user wants their
532
+ * agent to run, and the cost of being wrong is an API key and a delegate key
533
+ * written into an app they do not use.
534
+ */
535
+ interface InstalledClientHint {
536
+ /** Runtime ids the scan found, likeliest first. */
537
+ installedClients: readonly RuntimeId[];
538
+ /**
539
+ * The top hit, and only when it is unambiguously top — see
540
+ * `installedClientHint`. A value an agent may echo back as `--runtime`;
541
+ * never a selection the connector makes for it.
542
+ */
543
+ suggestedRuntime?: RuntimeId;
544
+ }
545
+ /**
546
+ * A suggestion is offered only when one candidate is CLEARLY first: a lone
547
+ * candidate, or a single live MCP config file among bare client directories —
548
+ * the one tier difference the scan treats as evidence. Candidates within a
549
+ * tier are separated only by `SCAN_ORDER`, a fixed preference rather than a
550
+ * fact about this machine, so suggesting the winner of that tiebreak would
551
+ * dress an arbitrary choice as a finding.
552
+ *
553
+ * The suggestion is derived from the WHOLE array rather than from the first
554
+ * two entries, so it does not depend on the caller having sorted anything.
555
+ * This is exported, and an unsorted list reaching a positional rule would
556
+ * yield a quietly wrong suggestion — never a selection, since only the caller
557
+ * can act on it, but wrong is still wrong. `installedClients` preserves the
558
+ * order it was given, which for `scanInstalledClients` is likeliest-first.
559
+ */
560
+ declare function installedClientHint(candidates: readonly InstalledClientCandidate[]): InstalledClientHint;
561
+ interface PromptIo {
562
+ write: (text: string) => void;
563
+ /** Resolves the typed line, or `null` on EOF / Ctrl-C. */
564
+ question: (query: string) => Promise<string | null>;
565
+ }
566
+ declare function promptForInstalledClient(candidates: readonly InstalledClientCandidate[], io?: PromptIo): Promise<RuntimeId>;
567
+ /**
568
+ * The whole rung as one thunk: scan, refuse if nothing writable is installed,
569
+ * otherwise prompt. This is what `resolveRuntimeSelection` calls, which is why
570
+ * the registry needs no knowledge of the filesystem or of readline.
571
+ */
572
+ declare function resolveRuntimeByInstalledClientPrompt(options?: ScanInstalledClientsOptions & {
573
+ io?: PromptIo;
574
+ }): Promise<RuntimeId>;
575
+
576
+ declare const CONNECTOR_VERSION = "0.1.32-alpha.0";
451
577
  interface ConnectOptions {
452
578
  setupToken: string;
453
579
  apiBaseUrl: string;
@@ -515,6 +641,29 @@ interface ConnectOutcome {
515
641
  };
516
642
  delegate_address?: string;
517
643
  setup_challenge_expires_at?: string;
644
+ /**
645
+ * #2173, additive within schema_version 1. Both are present on a completed
646
+ * run and absent on `failedConnectOutcome`, which by construction has
647
+ * neither a registration nor a credential scan behind it.
648
+ *
649
+ * `hosted_mcp_url` is the endpoint Connect wired this run up to — written
650
+ * into the runtime's MCP config, or, on a manual runtime that Connect cannot
651
+ * configure, printed as the endpoint to enter by hand. It is deliberately
652
+ * NOT the `--api` backend URL: the hosted MCP
653
+ * server is a separate deployment, and an automation caller comparing the
654
+ * two used to read that intentional topology as an environment mismatch. It
655
+ * is non-secret — the same string already sits in the user's own config file
656
+ * — and carries no credential; the API key travels beside it in a header.
657
+ *
658
+ * `superseded_agent_ids` is the #1688 heads-up made structural: previously
659
+ * it was prose on stderr, so a `--json` caller could not see that the run it
660
+ * just completed left older agents alive with their own keys. Empty on a
661
+ * clean first run. An empty list is NOT proof of a clean machine — a scan
662
+ * that cannot read the credential root also yields an empty list, and the
663
+ * connector prefers that to failing a completed setup.
664
+ */
665
+ hosted_mcp_url?: string;
666
+ superseded_agent_ids?: readonly string[];
518
667
  /**
519
668
  * #2091, additive within schema_version 1: `message` is the redacted human
520
669
  * refusal (automation used to get code + next_action and nothing to act
@@ -528,6 +677,21 @@ interface ConnectOutcome {
528
677
  next_action: string;
529
678
  message?: string;
530
679
  allowed_runtimes?: readonly string[];
680
+ /**
681
+ * #2174, additive within schema_version 1. What the installed-client scan
682
+ * found on THIS machine, narrowing a `runtime_undetermined` retry from the
683
+ * nine-value `allowed_runtimes` menu to what is actually here.
684
+ *
685
+ * A hint an agent may echo back as `--runtime`, never a selection: the
686
+ * outcome stays `failed` with `rerun_connect_with_explicit_runtime`, and
687
+ * the connector never proceeds on it (#1719's populates-never-selects
688
+ * invariant). Absent when the scan found nothing OR could not run —
689
+ * both are honestly "no finding"; neither is a claim the machine is bare.
690
+ * `suggested_runtime` appears only when one candidate is unambiguously
691
+ * top, so it is never the winner of an arbitrary tiebreak.
692
+ */
693
+ installed_clients?: readonly string[];
694
+ suggested_runtime?: string;
531
695
  };
532
696
  }
533
697
  interface ConnectDeps {
@@ -548,6 +712,19 @@ interface ConnectDeps {
548
712
  isTty?: boolean;
549
713
  /** Overridable so the #1719 installed-client prompt is testable without readline. */
550
714
  promptRuntime?: () => Promise<RuntimeId>;
715
+ /**
716
+ * Overridable so the #2173 recovery-record write is testable without a real
717
+ * credential directory — and so a FAILING writer can be injected to prove
718
+ * that a broken record write never fails a completed setup.
719
+ */
720
+ writeOutcomeRecord?: typeof writeConnectOutcomeRecord;
721
+ /**
722
+ * Overridable so the #2174 refusal hint is testable without depending on
723
+ * which agent clients happen to be installed on the machine running the
724
+ * suite — and so a THROWING scan can be injected to prove the refusal
725
+ * degrades to its un-hinted shape.
726
+ */
727
+ scanInstalledClients?: typeof scanInstalledClients;
551
728
  }
552
729
  interface ConnectResult {
553
730
  setupId: string;
@@ -557,10 +734,19 @@ interface ConnectResult {
557
734
  /** Additive, secret-free completion contract shared by library callers and --json. */
558
735
  outcome: ConnectOutcome;
559
736
  }
737
+ /**
738
+ * The failure record for an error thrown by `runConnect` — the run's own, when
739
+ * it built one, and a freshly derived record otherwise (a rejection that never
740
+ * entered the run at all, such as an argument-parse refusal, or an injected
741
+ * one in a test).
742
+ */
743
+ declare function failureOutcomeFor(runtimeHint: string | undefined, error: unknown): ConnectOutcome;
560
744
  declare function runConnect(options: ConnectOptions, deps?: ConnectDeps): Promise<ConnectResult>;
561
745
  declare function completionOutcome(input: {
562
746
  runtimeInstall: RuntimeInstallResult;
563
747
  delegateAddress: string;
748
+ hostedMcpUrl?: string;
749
+ supersededAgentIds?: readonly string[];
564
750
  setupChallengeExpiresAt?: string;
565
751
  approvalRequired: boolean;
566
752
  }): ConnectOutcome;
@@ -615,6 +801,17 @@ interface ParsedCli {
615
801
  reason?: string;
616
802
  replacedBy?: string;
617
803
  };
804
+ /**
805
+ * #2169: unwire one agent — tombstone it, then remove its MCP pair and
806
+ * Hermes dotenv key from every runtime config. No token required; refuses
807
+ * rather than guess when a bare pair is owned by a different agent.
808
+ */
809
+ unwire?: {
810
+ reason?: string;
811
+ replacedBy?: string;
812
+ };
813
+ /** Optional positional value of --unwire <dir> (else --name / --credentials-dir resolve it). */
814
+ unwireDir?: string;
618
815
  /**
619
816
  * #1700: replace an agent's signing key on this machine. Two phases, because
620
817
  * the dashboard sits between them — `start` generates the key and prints its
@@ -659,6 +856,18 @@ interface ConnectErrorDetails {
659
856
  * from automation.
660
857
  */
661
858
  allowedRuntimes?: readonly string[];
859
+ /**
860
+ * What the #1719 installed-client scan found on this machine (#2174), when
861
+ * the refusal is `runtime_undetermined`. `installedClients` is ordered
862
+ * likeliest-first; `suggestedRuntime` appears only when one candidate is
863
+ * unambiguously top.
864
+ *
865
+ * A HINT, never a decision. The scan populates choices and never selects —
866
+ * carrying it here does not let the connector proceed on it, and the retry
867
+ * stays the agent's explicit `--runtime <name>`.
868
+ */
869
+ installedClients?: readonly string[];
870
+ suggestedRuntime?: string;
662
871
  }
663
872
  declare class ConnectError extends Error {
664
873
  readonly code: string;
@@ -668,67 +877,13 @@ declare class ConnectError extends Error {
668
877
  }
669
878
  declare function isConnectError(err: unknown): err is ConnectError;
670
879
 
671
- /**
672
- * Installed-client scan + interactive pick (#1719).
673
- *
674
- * The rung that answers "which runtime am I configuring?" for a HUMAN in a
675
- * plain terminal, where there is no agent shell to detect. Two properties hold
676
- * it up, and both are load-bearing:
677
- *
678
- * 1. **Only clients the connector can actually write.** A row that cannot be
679
- * configured is not a choice, it is a dead end wearing a choice's clothes.
680
- * 2. **The scan populates the choices; it never selects.** Finding exactly one
681
- * installed app tells you what EXISTS, not where the user wants their agent
682
- * to run — and the cost of being wrong is an API key and a delegate key
683
- * written into an app the user does not use. `scanInstalledClients` returns
684
- * candidates and nothing else; only an answer typed at the prompt resolves
685
- * a runtime.
686
- */
687
- interface InstalledClientCandidate {
688
- runtime: RuntimeId;
689
- label: string;
690
- /** What made this a candidate — shown at the prompt so the pick is informed. */
691
- detail: string;
692
- /**
693
- * The config file Haven would write for this client, when it owns one.
694
- * `null` for Claude Code, which is configured through its own CLI. Set
695
- * regardless of which evidence found the client — `evidence` is what says
696
- * whether that file exists today.
697
- */
698
- configPath: string | null;
699
- evidence: 'config-file' | 'client-directory';
700
- }
701
- interface ScanInstalledClientsOptions {
702
- homeDir?: string;
703
- /** Workspace root, for the project-local `.vscode/` marker. */
704
- cwd?: string;
705
- env?: NodeJS.ProcessEnv;
706
- /** Injectable so the scan is testable without a populated home directory. */
707
- exists?: (path: string) => Promise<boolean>;
708
- }
709
- declare function scanInstalledClients(options?: ScanInstalledClientsOptions): Promise<InstalledClientCandidate[]>;
710
- interface PromptIo {
711
- write: (text: string) => void;
712
- /** Resolves the typed line, or `null` on EOF / Ctrl-C. */
713
- question: (query: string) => Promise<string | null>;
714
- }
715
- declare function promptForInstalledClient(candidates: readonly InstalledClientCandidate[], io?: PromptIo): Promise<RuntimeId>;
716
- /**
717
- * The whole rung as one thunk: scan, refuse if nothing writable is installed,
718
- * otherwise prompt. This is what `resolveRuntimeSelection` calls, which is why
719
- * the registry needs no knowledge of the filesystem or of readline.
720
- */
721
- declare function resolveRuntimeByInstalledClientPrompt(options?: ScanInstalledClientsOptions & {
722
- io?: PromptIo;
723
- }): Promise<RuntimeId>;
724
-
725
880
  declare const MCP_RUNTIME_MANIFEST: {
726
881
  readonly mcpPackage: "@haven_ai/mcp";
727
- readonly mcpVersion: "0.1.31-alpha.0";
882
+ readonly mcpVersion: "0.1.32-alpha.0";
728
883
  readonly sdkPackage: "@haven_ai/sdk";
729
- readonly sdkVersion: "0.1.31-alpha.0";
884
+ readonly sdkVersion: "0.1.32-alpha.0";
730
885
  readonly signerPackage: "@haven_ai/signer";
731
- readonly signerVersion: "0.1.31-alpha.0";
886
+ readonly signerVersion: "0.1.32-alpha.0";
732
887
  readonly minimumNodeVersion: "22.0.0";
733
888
  readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
734
889
  readonly requiredTools: readonly string[];
@@ -744,4 +899,4 @@ declare function mcpPackageSpec(): string;
744
899
  declare function sdkPackageSpec(): string;
745
900
  declare function signerPackageSpec(): string;
746
901
 
747
- export { CONNECTOR_VERSION, CONNECT_OUTCOME_SCHEMA_VERSION, type ConnectApiClient, type ConnectDeps, ConnectError, type ConnectOptions, type ConnectOutcome, type ConnectOutcomeStatus, type ConnectResult, type InstalledClientCandidate, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type ParsedCli, type PrepareSignerRuntimeInput, type PreparedSignerRuntime, type PromptIo, RUNTIME_FLAG_VALUES, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type RuntimeResolutionOptions, type RuntimeSelection, type ScanInstalledClientsOptions, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, completionOutcome, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, failedConnectOutcome, generateDelegateKey, helpText, installRuntime, isConnectError, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, promptForInstalledClient, redactSecrets, resolveRuntimeByInstalledClientPrompt, resolveRuntimeSelection, runConnect, runtimeInstallCapabilities, runtimeProfile, scanInstalledClients, sdkPackageSpec, shortAddress, signerPackageSpec, writeCredentialFiles };
902
+ export { CONNECTOR_VERSION, CONNECT_OUTCOME_FILENAME, CONNECT_OUTCOME_SCHEMA_VERSION, type ConnectApiClient, type ConnectDeps, ConnectError, type ConnectOptions, type ConnectOutcome, type ConnectOutcomeStatus, type ConnectResult, type InstalledClientCandidate, type InstalledClientHint, type LocalDelegateKey, MCP_RUNTIME_MANIFEST, type ParsedCli, type PrepareSignerRuntimeInput, type PreparedSignerRuntime, type PromptIo, RUNTIME_FLAG_VALUES, type RegisterSetupInput, type RegisterSetupResponse, type ResolveSetupInput, type ResolvedSetup, type RuntimeId, type RuntimeInstallInput, type RuntimeInstallResult, type RuntimeProfile, type RuntimeResolutionOptions, type RuntimeSelection, type ScanInstalledClientsOptions, type StoredCredentialPaths, type UpdateInstallStatusInput, type WriteCredentialInput, completionOutcome, createConnectApiClient, defaultAgentDirectory, delegateKeyFromPrivateKey, failedConnectOutcome, failureOutcomeFor, generateDelegateKey, helpText, installRuntime, installedClientHint, isConnectError, mcpPackageSpec, normalizeRuntime, parseArgs, prepareSignerRuntime, promptForInstalledClient, redactSecrets, resolveRuntimeByInstalledClientPrompt, resolveRuntimeSelection, runConnect, runtimeInstallCapabilities, runtimeProfile, scanInstalledClients, sdkPackageSpec, shortAddress, signerPackageSpec, writeConnectOutcomeRecord, writeCredentialFiles };