@haven_ai/connect 0.1.1-alpha → 0.1.2-alpha

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
@@ -8,7 +8,7 @@ public signing address, proof signature, and API-key hash. Haven never receives
8
8
  the private key or plaintext API key.
9
9
 
10
10
  ```sh
11
- npx -y @haven_ai/connect@0.1.1-alpha --setup hv_setup_... --api https://api.haven.example --ack-signer --runtime claude-code
11
+ npx -y @haven_ai/connect@0.1.2-alpha --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code
12
12
  ```
13
13
 
14
14
  The connector writes owner-only credential files outside the project by default:
@@ -22,6 +22,6 @@ the locally held signer key and the user-approved on-chain Haven wallet rules.
22
22
  Use `--credentials-dir <path>` to choose a different local credential directory.
23
23
  Do not point it at a project repository, shared folder, or cloud-synced folder.
24
24
 
25
- Use `--ack-signer` with Haven-generated setup prompts. It prepares the local
26
- signer acknowledgement during setup so the configured agent runtime can start
27
- the signer after its required restart.
25
+ Use `--ack-local-tools` with Haven-generated setup prompts. It prepares the
26
+ local Haven tools acknowledgement during setup so Codex and Claude Code can load
27
+ Haven after a normal restart.
package/dist/cli.cjs CHANGED
@@ -8,6 +8,7 @@ var os = require('os');
8
8
  var path = require('path');
9
9
  var child_process = require('child_process');
10
10
  var util = require('util');
11
+ var mcp = require('@haven_ai/mcp');
11
12
  var signer = require('@haven_ai/signer');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -51,10 +52,13 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
51
52
  body: JSON.stringify({
52
53
  runtime: input.runtime,
53
54
  connector_version: input.connectorVersion,
55
+ runtime_mcp_mode: input.runtimeMcpMode,
54
56
  hosted_mcp_configured: input.hostedMcpConfigured,
55
57
  local_signer_configured: input.localSignerConfigured,
58
+ local_mcp_configured: input.localMcpConfigured,
56
59
  credential_files_written: input.credentialFilesWritten,
57
60
  signer_acknowledged: input.signerAcknowledged,
61
+ local_mcp_acknowledged: input.localMcpAcknowledged,
58
62
  activation_command_available: input.activationCommandAvailable,
59
63
  probe_result: input.probeResult,
60
64
  restart_required: input.restartRequired,
@@ -137,6 +141,7 @@ async function writeCredentialFiles(input) {
137
141
  signerPath,
138
142
  {
139
143
  delegate_key: input.delegateKey,
144
+ delegate_address: input.delegateAddress,
140
145
  agent_id: input.agentId,
141
146
  safe_address: input.safeAddress,
142
147
  chain_id: input.chainId,
@@ -156,6 +161,7 @@ async function writeCredentialFiles(input) {
156
161
  network: input.network,
157
162
  api_url: input.apiUrl,
158
163
  hosted_mcp_url: input.hostedMcpUrl,
164
+ agent_budget: input.agentBudget,
159
165
  note: "Haven API key identifies the agent only. It cannot spend without the local signer key and on-chain Haven wallet rules."
160
166
  },
161
167
  input.warn
@@ -216,6 +222,8 @@ async function writeRuntimeConfig(input) {
216
222
  return {
217
223
  hostedConfigured: false,
218
224
  signerConfigured: false,
225
+ localMcpConfigured: false,
226
+ runtimeMcpMode: "manual",
219
227
  target: "manual runtime setup",
220
228
  changed: false,
221
229
  restartRequired: true,
@@ -257,17 +265,13 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
257
265
  return `${JSON.stringify(config, null, 2)}
258
266
  `;
259
267
  }
260
- function mergeCodexToml(existingToml, hostedMcpUrl, signerPath) {
268
+ function mergeCodexToml(existingToml, identityPath, signerPath) {
261
269
  let next = removeTomlTable(removeTomlTable(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
262
270
  next = next.trimEnd();
263
271
  const block = [
264
272
  "[mcp_servers.haven]",
265
- `url = ${tomlString(hostedMcpUrl)}`,
266
- 'bearer_token_env_var = "HAVEN_TOKEN"',
267
- "",
268
- "[mcp_servers.haven_signer]",
269
273
  'command = "npx"',
270
- `args = ["-y", ${tomlString(signerPackageName())}, "--credentials", ${tomlString(signerPath)}]`
274
+ `args = ["-y", ${tomlString(localMcpPackageName())}, "--identity", ${tomlString(identityPath)}, "--signer", ${tomlString(signerPath)}]`
271
275
  ].join("\n");
272
276
  return `${next ? `${next}
273
277
 
@@ -287,6 +291,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
287
291
  return {
288
292
  hostedConfigured: true,
289
293
  signerConfigured: true,
294
+ localMcpConfigured: false,
295
+ runtimeMcpMode: "hosted_plus_signer",
290
296
  target: configTargetLabel(input.runtime),
291
297
  changed: existing !== merged,
292
298
  restartRequired: input.runtime === "claude-desktop",
@@ -296,6 +302,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
296
302
  return {
297
303
  hostedConfigured: false,
298
304
  signerConfigured: false,
305
+ localMcpConfigured: false,
306
+ runtimeMcpMode: "hosted_plus_signer",
299
307
  target: configTargetLabel(input.runtime),
300
308
  changed: false,
301
309
  restartRequired: true,
@@ -306,41 +314,29 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
306
314
  }
307
315
  async function writeCodexConfig(input) {
308
316
  const target = codexConfigPath(input.homeDir);
309
- const envTarget = path.join(input.credentialDirectory, "identity.env");
310
- const launchTarget = path.join(input.credentialDirectory, "start-codex.sh");
311
317
  try {
312
318
  const existing = await readOptional(target);
313
- const merged = mergeCodexToml(existing ?? "", input.hostedMcpUrl, input.signerPath);
319
+ const merged = mergeCodexToml(existing ?? "", input.identityPath, input.signerPath);
314
320
  await writeOwnerOnlyText(target, merged);
315
- await writeOwnerOnlyText(envTarget, `export HAVEN_TOKEN=${shellToken(input.apiKey)}
316
- `);
317
- await writeOwnerExecutableText(
318
- launchTarget,
319
- [
320
- "#!/bin/sh",
321
- "set -eu",
322
- `. ${shellToken(envTarget)}`,
323
- 'exec codex "$@"',
324
- ""
325
- ].join("\n")
326
- );
327
321
  return {
328
- hostedConfigured: true,
322
+ hostedConfigured: false,
329
323
  signerConfigured: true,
324
+ localMcpConfigured: true,
325
+ runtimeMcpMode: "local_stdio",
330
326
  target: "Codex CLI config",
331
327
  changed: existing !== merged,
332
328
  restartRequired: true,
333
- activationCommand: shellToken(launchTarget),
334
329
  messages: [
335
- "Updated Haven MCP entries in Codex CLI config.",
336
- "Wrote the hosted MCP token to a private env file.",
337
- `Restart Codex with: ${shellToken(launchTarget)}`
330
+ "Updated local Haven MCP entry in Codex CLI config.",
331
+ "After Haven approval, restart Codex normally so it can load Haven tools."
338
332
  ]
339
333
  };
340
334
  } catch (err) {
341
335
  return {
342
336
  hostedConfigured: false,
343
337
  signerConfigured: false,
338
+ localMcpConfigured: false,
339
+ runtimeMcpMode: "local_stdio",
344
340
  target: "Codex CLI config",
345
341
  changed: false,
346
342
  restartRequired: true,
@@ -362,11 +358,6 @@ async function writeOwnerOnlyText(path$1, value) {
362
358
  await promises.writeFile(path$1, value, { mode: 384 });
363
359
  await promises.chmod(path$1, 384).catch(() => void 0);
364
360
  }
365
- async function writeOwnerExecutableText(path$1, value) {
366
- await promises.mkdir(path.dirname(path$1), { recursive: true, mode: 448 });
367
- await promises.writeFile(path$1, value, { mode: 448 });
368
- await promises.chmod(path$1, 448).catch(() => void 0);
369
- }
370
361
  function parseJsonObject(value) {
371
362
  const parsed = JSON.parse(value);
372
363
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -395,9 +386,6 @@ function removeTomlTable(toml, table) {
395
386
  function tomlString(value) {
396
387
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
397
388
  }
398
- function shellToken(value) {
399
- return `'${value.replace(/'/g, `'\\''`)}'`;
400
- }
401
389
  function cursorConfigPath(homeDir = os.homedir()) {
402
390
  return path.resolve(homeDir, ".cursor", "mcp.json");
403
391
  }
@@ -435,6 +423,85 @@ function configTargetLabel(runtime) {
435
423
  function signerPackageName() {
436
424
  return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
437
425
  }
426
+ function localMcpPackageName() {
427
+ return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
428
+ }
429
+ async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
430
+ try {
431
+ const input = await buildLocalMcpConsentInput(identityPath, signerPath);
432
+ const decision = await mcp.ensureConsent(input, {
433
+ credentialsPath: identityPath,
434
+ writeAck: true,
435
+ out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
436
+ });
437
+ return {
438
+ acknowledged: decision.ok,
439
+ hash: decision.hash,
440
+ reason: decision.reason
441
+ };
442
+ } catch (err) {
443
+ return {
444
+ acknowledged: false,
445
+ error: err instanceof Error ? err.message : String(err)
446
+ };
447
+ }
448
+ }
449
+ async function getLocalMcpConsentStatus(identityPath, signerPath) {
450
+ try {
451
+ const input = await buildLocalMcpConsentInput(identityPath, signerPath);
452
+ const hash = mcp.computeConsentHash(input);
453
+ const stored = await readLocalMcpAckFile(localMcpAckPath(identityPath));
454
+ if (stored === hash) {
455
+ return { acknowledged: true, hash, reason: "ack_file_match" };
456
+ }
457
+ return {
458
+ acknowledged: false,
459
+ hash,
460
+ reason: stored ? "ack_file_mismatch" : "ack_file_missing"
461
+ };
462
+ } catch (err) {
463
+ return {
464
+ acknowledged: false,
465
+ error: err instanceof Error ? err.message : String(err)
466
+ };
467
+ }
468
+ }
469
+ function localMcpAckPath(identityPath) {
470
+ return path.resolve(`${identityPath}.ack.json`);
471
+ }
472
+ async function buildLocalMcpConsentInput(identityPath, signerPath) {
473
+ const credentials = await mcp.loadCredentials({ identityPath, signerPath });
474
+ const unavailableDuringSetup = {
475
+ getAllowances: async () => {
476
+ throw new Error("Haven approval is not complete yet.");
477
+ }
478
+ };
479
+ return mcp.consentInputFromClient(
480
+ unavailableDuringSetup,
481
+ {
482
+ apiKey: credentials.apiKey,
483
+ apiUrl: credentials.apiUrl,
484
+ agentId: credentials.agentId,
485
+ safeAddress: credentials.safeAddress,
486
+ delegateAddress: credentials.delegateAddress,
487
+ chainId: credentials.chainId,
488
+ allowanceSummary: credentials.allowanceSummary
489
+ },
490
+ mcp.registeredToolNames()
491
+ );
492
+ }
493
+ async function readLocalMcpAckFile(path) {
494
+ try {
495
+ const parsed = JSON.parse(await promises.readFile(path, "utf8"));
496
+ return typeof parsed.ack === "string" ? parsed.ack : null;
497
+ } catch {
498
+ return null;
499
+ }
500
+ }
501
+ function writeLogChunk(log, chunk) {
502
+ const message = String(chunk).trimEnd();
503
+ if (message) log(message);
504
+ }
438
505
  async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
439
506
  let response;
440
507
  try {
@@ -589,7 +656,7 @@ async function acknowledgeLocalSignerConsent(signerPath, log) {
589
656
  const decision = await signer.ensureSignerConsent(input, {
590
657
  credentialsPath: signerPath,
591
658
  writeAck: true,
592
- out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
659
+ out: log ? { write: (chunk) => writeLogChunk2(log, chunk) } : void 0
593
660
  });
594
661
  return {
595
662
  acknowledged: decision.ok,
@@ -648,7 +715,7 @@ async function readSignerAckFile(path) {
648
715
  return null;
649
716
  }
650
717
  }
651
- function writeLogChunk(log, chunk) {
718
+ function writeLogChunk2(log, chunk) {
652
719
  const message = String(chunk).trimEnd();
653
720
  if (message) log(message);
654
721
  }
@@ -658,23 +725,28 @@ var execFileAsync = util.promisify(child_process.execFile);
658
725
  async function installRuntime(input, deps = {}) {
659
726
  const runtime = normalizeRuntime(input.runtime, deps.env);
660
727
  const profile = runtimeProfile(runtime, deps.env);
661
- const signerConsentMessages = [];
662
- const signerConsent = await resolveSignerConsent(input, signerConsentMessages);
728
+ const localRuntime = usesLocalMcp(runtime);
729
+ const consentMessages = [];
730
+ const localMcpConsent = localRuntime ? await resolveLocalMcpConsent(input, consentMessages) : void 0;
731
+ const signerConsent = localRuntime ? void 0 : await resolveSignerConsent(input, consentMessages);
663
732
  if (runtime === "other") {
664
733
  const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
665
- const signerReady = signerCredentialReady2 && signerConsent.acknowledged;
734
+ const signerReady = signerCredentialReady2 && signerConsent?.acknowledged;
666
735
  return {
667
736
  runtime,
737
+ runtimeMcpMode: "manual",
668
738
  hostedMcpConfigured: false,
669
739
  localSignerConfigured: false,
740
+ localMcpConfigured: false,
670
741
  probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
671
742
  restartRequired: true,
672
743
  nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
673
744
  errorCode: "manual_runtime_setup_required",
674
745
  configTarget: "manual runtime setup",
675
- signerAcknowledged: signerConsent.acknowledged,
746
+ signerAcknowledged: signerConsent?.acknowledged,
747
+ localMcpAcknowledged: false,
676
748
  messages: [
677
- ...signerConsentMessages,
749
+ ...consentMessages,
678
750
  "Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
679
751
  ]
680
752
  };
@@ -683,6 +755,7 @@ async function installRuntime(input, deps = {}) {
683
755
  runtime,
684
756
  hostedMcpUrl: input.hostedMcpUrl,
685
757
  apiKey: input.apiKey,
758
+ identityPath: input.identityPath,
686
759
  signerPath: input.signerPath,
687
760
  credentialDirectory: input.credentialDirectory,
688
761
  homeDir: deps.homeDir
@@ -692,21 +765,25 @@ async function installRuntime(input, deps = {}) {
692
765
  probeLocalSignerCredential(input.signerPath)
693
766
  ]);
694
767
  const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
695
- const signerOk = configResult.signerConfigured && signerCredentialReady && signerConsent.acknowledged;
768
+ const localMcpOk = configResult.runtimeMcpMode === "local_stdio" && configResult.localMcpConfigured && signerCredentialReady && Boolean(localMcpConsent?.acknowledged);
769
+ const signerOk = configResult.runtimeMcpMode === "local_stdio" ? localMcpOk : configResult.signerConfigured && signerCredentialReady && Boolean(signerConsent?.acknowledged);
696
770
  const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
697
- const errorCode = configResult.errorCode ?? signerConsentErrorCode(signerCredentialReady, signerConsent);
771
+ const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) : signerConsentErrorCode(signerCredentialReady, signerConsent));
698
772
  return {
699
773
  runtime,
774
+ runtimeMcpMode: configResult.runtimeMcpMode,
700
775
  hostedMcpConfigured: hostedOk,
701
776
  localSignerConfigured: signerOk,
702
- probeResult: buildProbeResult(configResult.hostedConfigured, hostedProbe.status, signerOk),
777
+ localMcpConfigured: localMcpOk,
778
+ probeResult: buildProbeResult(configResult.runtimeMcpMode, configResult.hostedConfigured, hostedProbe.status, signerOk, localMcpOk),
703
779
  restartRequired,
704
780
  nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
705
781
  errorCode,
706
782
  configTarget: configResult.target,
707
- signerAcknowledged: signerConsent.acknowledged,
783
+ signerAcknowledged: signerConsent?.acknowledged,
784
+ localMcpAcknowledged: localMcpConsent?.acknowledged,
708
785
  activationCommand: configResult.activationCommand,
709
- messages: [...signerConsentMessages, ...configResult.messages]
786
+ messages: [...consentMessages, ...configResult.messages]
710
787
  };
711
788
  }
712
789
  function runtimeInstallCapabilities(runtime, env = process.env) {
@@ -722,39 +799,36 @@ async function configureClaudeCode(input, deps) {
722
799
  await runCommand("claude", [
723
800
  "mcp",
724
801
  "add",
725
- "--transport",
726
- "http",
727
802
  "haven",
728
- input.hostedMcpUrl,
729
- "--header",
730
- `Authorization: Bearer ${input.apiKey}`
731
- ]);
732
- await runCommand("claude", [
733
- "mcp",
734
- "add",
735
- "haven-signer",
736
803
  "--",
737
804
  "npx",
738
805
  "-y",
739
- signerPackageName2(),
740
- "--credentials",
806
+ localMcpPackageName2(),
807
+ "--identity",
808
+ input.identityPath,
809
+ "--signer",
741
810
  input.signerPath
742
811
  ]);
812
+ await runCommand("claude", ["mcp", "remove", "haven-signer"]).catch(() => void 0);
743
813
  return {
744
- hostedConfigured: true,
814
+ hostedConfigured: false,
745
815
  signerConfigured: true,
816
+ localMcpConfigured: true,
817
+ runtimeMcpMode: "local_stdio",
746
818
  target: "Claude Code MCP config",
747
819
  changed: true,
748
820
  restartRequired: true,
749
821
  messages: [
750
- "Updated Haven MCP entries with Claude Code.",
751
- "Restart Claude Code after Haven approval so it can load the Haven tools."
822
+ "Updated local Haven MCP entry with Claude Code.",
823
+ "After Haven approval, restart Claude Code normally so it can load Haven tools."
752
824
  ]
753
825
  };
754
826
  } catch (err) {
755
827
  return {
756
828
  hostedConfigured: false,
757
829
  signerConfigured: false,
830
+ localMcpConfigured: false,
831
+ runtimeMcpMode: "local_stdio",
758
832
  target: "Claude Code MCP config",
759
833
  changed: false,
760
834
  restartRequired: true,
@@ -769,13 +843,28 @@ async function configureClaudeCode(input, deps) {
769
843
  async function defaultRunCommand(command, args) {
770
844
  await execFileAsync(command, args, { timeout: 1e4 });
771
845
  }
772
- function buildProbeResult(hostedConfigured, hostedStatus, signerReady) {
846
+ function buildProbeResult(mode, hostedConfigured, hostedStatus, signerReady, localMcpReady) {
847
+ if (mode === "local_stdio") {
848
+ return localMcpReady ? "local_stdio_mcp_ready" : "local_stdio_mcp_unavailable";
849
+ }
773
850
  const hostedPart = hostedConfigured ? `hosted_${hostedStatus}` : "hosted_not_configured";
774
851
  const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
775
852
  return `${hostedPart}_${signerPart}`.slice(0, 120);
776
853
  }
854
+ async function resolveLocalMcpConsent(input, messages) {
855
+ if (input.ackLocalTools || input.ackSigner) {
856
+ const status = await acknowledgeLocalMcpConsent(input.identityPath, input.signerPath, (message) => messages.push(message));
857
+ if (status.acknowledged) {
858
+ messages.push("Prepared the local Haven tools acknowledgement.");
859
+ } else {
860
+ messages.push("Local Haven tools acknowledgement still needs attention.");
861
+ }
862
+ return status;
863
+ }
864
+ return getLocalMcpConsentStatus(input.identityPath, input.signerPath);
865
+ }
777
866
  async function resolveSignerConsent(input, messages) {
778
- if (input.ackSigner) {
867
+ if (input.ackSigner || input.ackLocalTools) {
779
868
  const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
780
869
  if (status.acknowledged) {
781
870
  messages.push("Prepared the local Haven signer acknowledgement.");
@@ -788,24 +877,32 @@ async function resolveSignerConsent(input, messages) {
788
877
  }
789
878
  function signerConsentErrorCode(signerCredentialReady, signerConsent) {
790
879
  if (!signerCredentialReady) return "local_signer_credential_unavailable";
791
- if (!signerConsent.acknowledged) return "local_signer_ack_required";
880
+ if (!signerConsent?.acknowledged) return "local_signer_ack_required";
881
+ return void 0;
882
+ }
883
+ function localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) {
884
+ if (!signerCredentialReady) return "local_signer_credential_unavailable";
885
+ if (!localMcpConsent?.acknowledged) return "local_mcp_ack_required";
792
886
  return void 0;
793
887
  }
794
888
  function nextAction(runtime, restartMode, errorCode) {
795
889
  if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
796
890
  if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
797
- if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex_with_haven_env";
891
+ if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex";
798
892
  if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
799
893
  if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
800
894
  if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
801
895
  return "return_to_haven_for_wallet_approval_then_configure_runtime";
802
896
  }
803
- function signerPackageName2() {
804
- return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
897
+ function localMcpPackageName2() {
898
+ return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
899
+ }
900
+ function usesLocalMcp(runtime) {
901
+ return runtime === "codex-cli" || runtime === "claude-code";
805
902
  }
806
903
 
807
904
  // src/runtime.ts
808
- var CONNECTOR_VERSION = "0.1.1-alpha";
905
+ var CONNECTOR_VERSION = "0.1.2";
809
906
  async function runConnect(options, deps = {}) {
810
907
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
811
908
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -851,9 +948,15 @@ async function runConnect(options, deps = {}) {
851
948
  agentId: registration.agent_id,
852
949
  apiKey: localApiKey,
853
950
  delegateKey: localKey.privateKey,
951
+ delegateAddress: localKey.address,
854
952
  safeAddress: setup.haven_wallet.address,
855
953
  chainId: setup.haven_wallet.chain_id,
856
954
  network: setup.haven_wallet.network,
955
+ agentBudget: setup.agent_budget.map((budget) => ({
956
+ token_symbol: budget.token_symbol,
957
+ allowance_amount: budget.allowance_amount,
958
+ reset_period_min: budget.reset_period_min
959
+ })),
857
960
  apiUrl: options.apiBaseUrl,
858
961
  hostedMcpUrl: registration.hosted_mcp_url,
859
962
  warn: log
@@ -868,17 +971,21 @@ async function runConnect(options, deps = {}) {
868
971
  identityPath: credentialPaths.identityPath,
869
972
  credentialDirectory: credentialPaths.directory,
870
973
  environmentLabel: options.environmentLabel ?? "Local workspace",
871
- ackSigner: options.ackSigner
974
+ ackSigner: options.ackSigner,
975
+ ackLocalTools: options.ackLocalTools
872
976
  });
873
977
  printRuntimeInstall(runtimeInstall, log);
874
978
  try {
875
979
  await api.updateInstallStatus(registration.setup_id, localApiKey, {
876
980
  runtime: runtimeInstall.runtime,
877
981
  connectorVersion,
982
+ runtimeMcpMode: runtimeInstall.runtimeMcpMode,
878
983
  hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
879
984
  localSignerConfigured: runtimeInstall.localSignerConfigured,
985
+ localMcpConfigured: runtimeInstall.localMcpConfigured,
880
986
  credentialFilesWritten: true,
881
987
  signerAcknowledged: runtimeInstall.signerAcknowledged,
988
+ localMcpAcknowledged: runtimeInstall.localMcpAcknowledged,
882
989
  activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
883
990
  probeResult: runtimeInstall.probeResult,
884
991
  restartRequired: runtimeInstall.restartRequired,
@@ -891,7 +998,7 @@ async function runConnect(options, deps = {}) {
891
998
  }
892
999
  log("Return to Haven to approve the agent rules.");
893
1000
  if (runtimeInstall.restartRequired) {
894
- log("Restart this agent session after approval if your runtime loads MCP tools at startup.");
1001
+ log("After approval, restart this agent normally so it can load Haven tools.");
895
1002
  }
896
1003
  return {
897
1004
  setupId: registration.setup_id,
@@ -917,10 +1024,12 @@ function secureLogger(log) {
917
1024
  }
918
1025
  function printRuntimeInstall(result, log) {
919
1026
  for (const message of result.messages) log(message);
920
- if (result.hostedMcpConfigured) {
1027
+ if (result.localMcpConfigured) {
1028
+ log("Configured local Haven MCP tools.");
1029
+ } else if (result.hostedMcpConfigured) {
921
1030
  log("Configured hosted Haven MCP identity.");
922
1031
  } else {
923
- log("Hosted Haven MCP identity still needs runtime setup.");
1032
+ log("Haven MCP tools still need runtime setup.");
924
1033
  }
925
1034
  if (result.localSignerConfigured) {
926
1035
  log("Configured local Haven signer.");
@@ -950,8 +1059,11 @@ function parseArgs(argv, env = process.env) {
950
1059
  options.credentialsDir = requireValue(argv, ++i, arg);
951
1060
  } else if (arg === "--environment-label") {
952
1061
  options.environmentLabel = requireValue(argv, ++i, arg);
1062
+ } else if (arg === "--ack-local-tools") {
1063
+ options.ackLocalTools = true;
953
1064
  } else if (arg === "--ack-signer") {
954
1065
  options.ackSigner = true;
1066
+ options.ackLocalTools = true;
955
1067
  } else if (arg === "--version") {
956
1068
  process.stdout.write(`${CONNECTOR_VERSION}
957
1069
  `);
@@ -980,7 +1092,7 @@ function helpText() {
980
1092
  "sends Haven only the public signing address plus a proof signature.",
981
1093
  "",
982
1094
  "Usage:",
983
- " npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-signer --runtime claude-code",
1095
+ " npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code",
984
1096
  "",
985
1097
  "Options:",
986
1098
  " --setup <token> Short-lived setup token from Haven.",
@@ -988,7 +1100,8 @@ function helpText() {
988
1100
  " --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
989
1101
  " --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
990
1102
  " --environment-label <text> Non-sensitive label shown in Haven setup review.",
991
- " --ack-signer Write the one-time local signer acknowledgement during setup.",
1103
+ " --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
1104
+ " --ack-signer Backward-compatible alias for --ack-local-tools.",
992
1105
  " --help Show this help.",
993
1106
  "",
994
1107
  "The connector never prints the private key and never sends it to Haven."