@haven_ai/connect 0.1.0-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/dist/index.cjs CHANGED
@@ -7,6 +7,8 @@ var os = require('os');
7
7
  var path = require('path');
8
8
  var child_process = require('child_process');
9
9
  var util = require('util');
10
+ var mcp = require('@haven_ai/mcp');
11
+ var signer = require('@haven_ai/signer');
10
12
 
11
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
14
 
@@ -49,9 +51,14 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
49
51
  body: JSON.stringify({
50
52
  runtime: input.runtime,
51
53
  connector_version: input.connectorVersion,
54
+ runtime_mcp_mode: input.runtimeMcpMode,
52
55
  hosted_mcp_configured: input.hostedMcpConfigured,
53
56
  local_signer_configured: input.localSignerConfigured,
57
+ local_mcp_configured: input.localMcpConfigured,
54
58
  credential_files_written: input.credentialFilesWritten,
59
+ signer_acknowledged: input.signerAcknowledged,
60
+ local_mcp_acknowledged: input.localMcpAcknowledged,
61
+ activation_command_available: input.activationCommandAvailable,
55
62
  probe_result: input.probeResult,
56
63
  restart_required: input.restartRequired,
57
64
  next_user_action: input.nextUserAction,
@@ -133,6 +140,7 @@ async function writeCredentialFiles(input) {
133
140
  signerPath,
134
141
  {
135
142
  delegate_key: input.delegateKey,
143
+ delegate_address: input.delegateAddress,
136
144
  agent_id: input.agentId,
137
145
  safe_address: input.safeAddress,
138
146
  chain_id: input.chainId,
@@ -152,6 +160,7 @@ async function writeCredentialFiles(input) {
152
160
  network: input.network,
153
161
  api_url: input.apiUrl,
154
162
  hosted_mcp_url: input.hostedMcpUrl,
163
+ agent_budget: input.agentBudget,
155
164
  note: "Haven API key identifies the agent only. It cannot spend without the local signer key and on-chain Haven wallet rules."
156
165
  },
157
166
  input.warn
@@ -212,6 +221,8 @@ async function writeRuntimeConfig(input) {
212
221
  return {
213
222
  hostedConfigured: false,
214
223
  signerConfigured: false,
224
+ localMcpConfigured: false,
225
+ runtimeMcpMode: "manual",
215
226
  target: "manual runtime setup",
216
227
  changed: false,
217
228
  restartRequired: true,
@@ -236,7 +247,7 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
236
247
  function buildSignerServer(signerPath, runtime) {
237
248
  const server = {
238
249
  command: "npx",
239
- args: ["-y", "@haven_ai/signer", "--credentials", signerPath]
250
+ args: ["-y", signerPackageName(), "--credentials", signerPath]
240
251
  };
241
252
  if (runtime === "vscode") return { type: "stdio", ...server };
242
253
  return server;
@@ -253,17 +264,13 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
253
264
  return `${JSON.stringify(config, null, 2)}
254
265
  `;
255
266
  }
256
- function mergeCodexToml(existingToml, hostedMcpUrl, signerPath) {
267
+ function mergeCodexToml(existingToml, identityPath, signerPath) {
257
268
  let next = removeTomlTable(removeTomlTable(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
258
269
  next = next.trimEnd();
259
270
  const block = [
260
271
  "[mcp_servers.haven]",
261
- `url = ${tomlString(hostedMcpUrl)}`,
262
- 'bearer_token_env_var = "HAVEN_TOKEN"',
263
- "",
264
- "[mcp_servers.haven_signer]",
265
272
  'command = "npx"',
266
- `args = ["-y", "@haven_ai/signer", "--credentials", ${tomlString(signerPath)}]`
273
+ `args = ["-y", ${tomlString(localMcpPackageName())}, "--identity", ${tomlString(identityPath)}, "--signer", ${tomlString(signerPath)}]`
267
274
  ].join("\n");
268
275
  return `${next ? `${next}
269
276
 
@@ -283,6 +290,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
283
290
  return {
284
291
  hostedConfigured: true,
285
292
  signerConfigured: true,
293
+ localMcpConfigured: false,
294
+ runtimeMcpMode: "hosted_plus_signer",
286
295
  target: configTargetLabel(input.runtime),
287
296
  changed: existing !== merged,
288
297
  restartRequired: input.runtime === "claude-desktop",
@@ -292,6 +301,8 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
292
301
  return {
293
302
  hostedConfigured: false,
294
303
  signerConfigured: false,
304
+ localMcpConfigured: false,
305
+ runtimeMcpMode: "hosted_plus_signer",
295
306
  target: configTargetLabel(input.runtime),
296
307
  changed: false,
297
308
  restartRequired: true,
@@ -302,29 +313,29 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
302
313
  }
303
314
  async function writeCodexConfig(input) {
304
315
  const target = codexConfigPath(input.homeDir);
305
- const envTarget = path.join(input.credentialDirectory, "identity.env");
306
316
  try {
307
317
  const existing = await readOptional(target);
308
- const merged = mergeCodexToml(existing ?? "", input.hostedMcpUrl, input.signerPath);
318
+ const merged = mergeCodexToml(existing ?? "", input.identityPath, input.signerPath);
309
319
  await writeOwnerOnlyText(target, merged);
310
- await writeOwnerOnlyText(envTarget, `HAVEN_TOKEN=${shellToken(input.apiKey)}
311
- `);
312
320
  return {
313
321
  hostedConfigured: false,
314
322
  signerConfigured: true,
323
+ localMcpConfigured: true,
324
+ runtimeMcpMode: "local_stdio",
315
325
  target: "Codex CLI config",
316
326
  changed: existing !== merged,
317
327
  restartRequired: true,
318
328
  messages: [
319
- "Updated Haven MCP entries in Codex CLI config.",
320
- "Wrote the hosted MCP token to a private env file. Launch Codex with that env file before using Haven tools."
321
- ],
322
- errorCode: "codex_env_activation_required"
329
+ "Updated local Haven MCP entry in Codex CLI config.",
330
+ "After Haven approval, restart Codex normally so it can load Haven tools."
331
+ ]
323
332
  };
324
333
  } catch (err) {
325
334
  return {
326
335
  hostedConfigured: false,
327
336
  signerConfigured: false,
337
+ localMcpConfigured: false,
338
+ runtimeMcpMode: "local_stdio",
328
339
  target: "Codex CLI config",
329
340
  changed: false,
330
341
  restartRequired: true,
@@ -374,9 +385,6 @@ function removeTomlTable(toml, table) {
374
385
  function tomlString(value) {
375
386
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
376
387
  }
377
- function shellToken(value) {
378
- return `'${value.replace(/'/g, `'\\''`)}'`;
379
- }
380
388
  function cursorConfigPath(homeDir = os.homedir()) {
381
389
  return path.resolve(homeDir, ".cursor", "mcp.json");
382
390
  }
@@ -411,6 +419,88 @@ function configTargetLabel(runtime) {
411
419
  return "runtime MCP config";
412
420
  }
413
421
  }
422
+ function signerPackageName() {
423
+ return `@haven_ai/signer@${signer.SIGNER_VERSION}`;
424
+ }
425
+ function localMcpPackageName() {
426
+ return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
427
+ }
428
+ async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
429
+ try {
430
+ const input = await buildLocalMcpConsentInput(identityPath, signerPath);
431
+ const decision = await mcp.ensureConsent(input, {
432
+ credentialsPath: identityPath,
433
+ writeAck: true,
434
+ out: log ? { write: (chunk) => writeLogChunk(log, chunk) } : void 0
435
+ });
436
+ return {
437
+ acknowledged: decision.ok,
438
+ hash: decision.hash,
439
+ reason: decision.reason
440
+ };
441
+ } catch (err) {
442
+ return {
443
+ acknowledged: false,
444
+ error: err instanceof Error ? err.message : String(err)
445
+ };
446
+ }
447
+ }
448
+ async function getLocalMcpConsentStatus(identityPath, signerPath) {
449
+ try {
450
+ const input = await buildLocalMcpConsentInput(identityPath, signerPath);
451
+ const hash = mcp.computeConsentHash(input);
452
+ const stored = await readLocalMcpAckFile(localMcpAckPath(identityPath));
453
+ if (stored === hash) {
454
+ return { acknowledged: true, hash, reason: "ack_file_match" };
455
+ }
456
+ return {
457
+ acknowledged: false,
458
+ hash,
459
+ reason: stored ? "ack_file_mismatch" : "ack_file_missing"
460
+ };
461
+ } catch (err) {
462
+ return {
463
+ acknowledged: false,
464
+ error: err instanceof Error ? err.message : String(err)
465
+ };
466
+ }
467
+ }
468
+ function localMcpAckPath(identityPath) {
469
+ return path.resolve(`${identityPath}.ack.json`);
470
+ }
471
+ async function buildLocalMcpConsentInput(identityPath, signerPath) {
472
+ const credentials = await mcp.loadCredentials({ identityPath, signerPath });
473
+ const unavailableDuringSetup = {
474
+ getAllowances: async () => {
475
+ throw new Error("Haven approval is not complete yet.");
476
+ }
477
+ };
478
+ return mcp.consentInputFromClient(
479
+ unavailableDuringSetup,
480
+ {
481
+ apiKey: credentials.apiKey,
482
+ apiUrl: credentials.apiUrl,
483
+ agentId: credentials.agentId,
484
+ safeAddress: credentials.safeAddress,
485
+ delegateAddress: credentials.delegateAddress,
486
+ chainId: credentials.chainId,
487
+ allowanceSummary: credentials.allowanceSummary
488
+ },
489
+ mcp.registeredToolNames()
490
+ );
491
+ }
492
+ async function readLocalMcpAckFile(path) {
493
+ try {
494
+ const parsed = JSON.parse(await promises.readFile(path, "utf8"));
495
+ return typeof parsed.ack === "string" ? parsed.ack : null;
496
+ } catch {
497
+ return null;
498
+ }
499
+ }
500
+ function writeLogChunk(log, chunk) {
501
+ const message = String(chunk).trimEnd();
502
+ if (message) log(message);
503
+ }
414
504
  async function probeHostedMcpTools(apiKey, hostedMcpUrl, fetchImpl = fetch) {
415
505
  let response;
416
506
  try {
@@ -559,51 +649,140 @@ function detectRuntime(env) {
559
649
  if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
560
650
  return null;
561
651
  }
652
+ async function acknowledgeLocalSignerConsent(signerPath, log) {
653
+ try {
654
+ const input = await buildSignerConsentInput(signerPath);
655
+ const decision = await signer.ensureSignerConsent(input, {
656
+ credentialsPath: signerPath,
657
+ writeAck: true,
658
+ out: log ? { write: (chunk) => writeLogChunk2(log, chunk) } : void 0
659
+ });
660
+ return {
661
+ acknowledged: decision.ok,
662
+ hash: decision.hash,
663
+ reason: decision.reason
664
+ };
665
+ } catch (err) {
666
+ return {
667
+ acknowledged: false,
668
+ error: err instanceof Error ? err.message : String(err)
669
+ };
670
+ }
671
+ }
672
+ async function getLocalSignerConsentStatus(signerPath) {
673
+ try {
674
+ const input = await buildSignerConsentInput(signerPath);
675
+ const hash = signer.computeSignerConsentHash(input);
676
+ const stored = await readSignerAckFile(signerAckPath(signerPath));
677
+ if (stored === hash) {
678
+ return { acknowledged: true, hash, reason: "ack_file_match" };
679
+ }
680
+ return {
681
+ acknowledged: false,
682
+ hash,
683
+ reason: stored ? "ack_file_mismatch" : "ack_file_missing"
684
+ };
685
+ } catch (err) {
686
+ return {
687
+ acknowledged: false,
688
+ error: err instanceof Error ? err.message : String(err)
689
+ };
690
+ }
691
+ }
692
+ function signerAckPath(signerPath) {
693
+ return path.resolve(`${signerPath}.signer-ack.json`);
694
+ }
695
+ async function buildSignerConsentInput(signerPath) {
696
+ const credentials = await signer.loadSignerCredentials(signerPath);
697
+ const signer$1 = signer.createEdgeSigner(credentials.delegateKey, {
698
+ x402BindingSigner: credentials.x402BindingSigner
699
+ });
700
+ return {
701
+ delegateAddress: signer$1.delegateAddress,
702
+ safeAddress: credentials.safeAddress,
703
+ agentId: credentials.agentId,
704
+ chainId: credentials.chainId,
705
+ network: credentials.network,
706
+ toolNames: Object.keys(signer.toolSchemas)
707
+ };
708
+ }
709
+ async function readSignerAckFile(path) {
710
+ try {
711
+ const parsed = JSON.parse(await promises.readFile(path, "utf8"));
712
+ return typeof parsed.ack === "string" ? parsed.ack : null;
713
+ } catch {
714
+ return null;
715
+ }
716
+ }
717
+ function writeLogChunk2(log, chunk) {
718
+ const message = String(chunk).trimEnd();
719
+ if (message) log(message);
720
+ }
562
721
 
563
722
  // src/runtime-install.ts
564
723
  var execFileAsync = util.promisify(child_process.execFile);
565
724
  async function installRuntime(input, deps = {}) {
566
725
  const runtime = normalizeRuntime(input.runtime, deps.env);
567
726
  const profile = runtimeProfile(runtime, deps.env);
727
+ const localRuntime = usesLocalMcp(runtime);
728
+ const consentMessages = [];
729
+ const localMcpConsent = localRuntime ? await resolveLocalMcpConsent(input, consentMessages) : void 0;
730
+ const signerConsent = localRuntime ? void 0 : await resolveSignerConsent(input, consentMessages);
568
731
  if (runtime === "other") {
569
- const signerReady2 = await probeLocalSignerCredential(input.signerPath);
732
+ const signerCredentialReady2 = await probeLocalSignerCredential(input.signerPath);
733
+ const signerReady = signerCredentialReady2 && signerConsent?.acknowledged;
570
734
  return {
571
735
  runtime,
736
+ runtimeMcpMode: "manual",
572
737
  hostedMcpConfigured: false,
573
738
  localSignerConfigured: false,
574
- probeResult: signerReady2 ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
739
+ localMcpConfigured: false,
740
+ probeResult: signerReady ? "manual_runtime_setup_required_local_signer_ready" : "manual_runtime_setup_required_local_signer_unavailable",
575
741
  restartRequired: true,
576
742
  nextUserAction: "return_to_haven_for_wallet_approval_then_configure_runtime",
577
743
  errorCode: "manual_runtime_setup_required",
578
744
  configTarget: "manual runtime setup",
579
- messages: ["Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."]
745
+ signerAcknowledged: signerConsent?.acknowledged,
746
+ localMcpAcknowledged: false,
747
+ messages: [
748
+ ...consentMessages,
749
+ "Runtime was not recognized. Keep the local credentials and add Haven MCP entries manually after wallet approval."
750
+ ]
580
751
  };
581
752
  }
582
753
  const configResult = runtime === "claude-code" ? await configureClaudeCode(input, deps) : await writeRuntimeConfig({
583
754
  runtime,
584
755
  hostedMcpUrl: input.hostedMcpUrl,
585
756
  apiKey: input.apiKey,
757
+ identityPath: input.identityPath,
586
758
  signerPath: input.signerPath,
587
759
  credentialDirectory: input.credentialDirectory,
588
760
  homeDir: deps.homeDir
589
761
  });
590
- const [hostedProbe, signerReady] = await Promise.all([
762
+ const [hostedProbe, signerCredentialReady] = await Promise.all([
591
763
  configResult.hostedConfigured ? probeHostedMcpTools(input.apiKey, input.hostedMcpUrl, deps.fetch) : Promise.resolve({ status: "bad_response" }),
592
764
  probeLocalSignerCredential(input.signerPath)
593
765
  ]);
594
766
  const hostedOk = configResult.hostedConfigured && hostedProbe.status !== "unauthorized";
595
- const signerOk = configResult.signerConfigured && signerReady;
767
+ const localMcpOk = configResult.runtimeMcpMode === "local_stdio" && configResult.localMcpConfigured && signerCredentialReady && Boolean(localMcpConsent?.acknowledged);
768
+ const signerOk = configResult.runtimeMcpMode === "local_stdio" ? localMcpOk : configResult.signerConfigured && signerCredentialReady && Boolean(signerConsent?.acknowledged);
596
769
  const restartRequired = configResult.restartRequired || restartRequiredForRuntime(runtime, deps.env);
770
+ const errorCode = configResult.errorCode ?? (configResult.runtimeMcpMode === "local_stdio" ? localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) : signerConsentErrorCode(signerCredentialReady, signerConsent));
597
771
  return {
598
772
  runtime,
773
+ runtimeMcpMode: configResult.runtimeMcpMode,
599
774
  hostedMcpConfigured: hostedOk,
600
775
  localSignerConfigured: signerOk,
601
- probeResult: buildProbeResult(configResult.hostedConfigured, hostedProbe.status, signerOk),
776
+ localMcpConfigured: localMcpOk,
777
+ probeResult: buildProbeResult(configResult.runtimeMcpMode, configResult.hostedConfigured, hostedProbe.status, signerOk, localMcpOk),
602
778
  restartRequired,
603
- nextUserAction: nextAction(profile.restartMode, configResult.errorCode),
604
- errorCode: configResult.errorCode,
779
+ nextUserAction: nextAction(runtime, profile.restartMode, errorCode),
780
+ errorCode,
605
781
  configTarget: configResult.target,
606
- messages: configResult.messages
782
+ signerAcknowledged: signerConsent?.acknowledged,
783
+ localMcpAcknowledged: localMcpConsent?.acknowledged,
784
+ activationCommand: configResult.activationCommand,
785
+ messages: [...consentMessages, ...configResult.messages]
607
786
  };
608
787
  }
609
788
  function runtimeInstallCapabilities(runtime, env = process.env) {
@@ -619,39 +798,43 @@ async function configureClaudeCode(input, deps) {
619
798
  await runCommand("claude", [
620
799
  "mcp",
621
800
  "add",
622
- "--transport",
623
- "http",
624
801
  "haven",
625
- input.hostedMcpUrl,
626
- "--header",
627
- `Authorization: Bearer ${input.apiKey}`
628
- ]);
629
- await runCommand("claude", [
630
- "mcp",
631
- "add",
632
- "haven-signer",
802
+ "--",
633
803
  "npx",
634
804
  "-y",
635
- "@haven_ai/signer",
636
- "--credentials",
805
+ localMcpPackageName2(),
806
+ "--identity",
807
+ input.identityPath,
808
+ "--signer",
637
809
  input.signerPath
638
810
  ]);
811
+ await runCommand("claude", ["mcp", "remove", "haven-signer"]).catch(() => void 0);
639
812
  return {
640
- hostedConfigured: true,
813
+ hostedConfigured: false,
641
814
  signerConfigured: true,
815
+ localMcpConfigured: true,
816
+ runtimeMcpMode: "local_stdio",
642
817
  target: "Claude Code MCP config",
643
818
  changed: true,
644
819
  restartRequired: true,
645
- messages: ["Updated Haven MCP entries with Claude Code."]
820
+ messages: [
821
+ "Updated local Haven MCP entry with Claude Code.",
822
+ "After Haven approval, restart Claude Code normally so it can load Haven tools."
823
+ ]
646
824
  };
647
825
  } catch (err) {
648
826
  return {
649
827
  hostedConfigured: false,
650
828
  signerConfigured: false,
829
+ localMcpConfigured: false,
830
+ runtimeMcpMode: "local_stdio",
651
831
  target: "Claude Code MCP config",
652
832
  changed: false,
653
833
  restartRequired: true,
654
- messages: [`Could not update Claude Code MCP config: ${err instanceof Error ? err.message : String(err)}`],
834
+ messages: [
835
+ `Could not update Claude Code MCP config: ${err instanceof Error ? err.message : String(err)}`,
836
+ "Install Claude Code or rerun the Haven setup command inside a Claude Code terminal."
837
+ ],
655
838
  errorCode: "claude_code_config_failed"
656
839
  };
657
840
  }
@@ -659,21 +842,66 @@ async function configureClaudeCode(input, deps) {
659
842
  async function defaultRunCommand(command, args) {
660
843
  await execFileAsync(command, args, { timeout: 1e4 });
661
844
  }
662
- function buildProbeResult(hostedConfigured, hostedStatus, signerReady) {
845
+ function buildProbeResult(mode, hostedConfigured, hostedStatus, signerReady, localMcpReady) {
846
+ if (mode === "local_stdio") {
847
+ return localMcpReady ? "local_stdio_mcp_ready" : "local_stdio_mcp_unavailable";
848
+ }
663
849
  const hostedPart = hostedConfigured ? `hosted_${hostedStatus}` : "hosted_not_configured";
664
850
  const signerPart = signerReady ? "local_signer_ready" : "local_signer_unavailable";
665
851
  return `${hostedPart}_${signerPart}`.slice(0, 120);
666
852
  }
667
- function nextAction(restartMode, errorCode) {
853
+ async function resolveLocalMcpConsent(input, messages) {
854
+ if (input.ackLocalTools || input.ackSigner) {
855
+ const status = await acknowledgeLocalMcpConsent(input.identityPath, input.signerPath, (message) => messages.push(message));
856
+ if (status.acknowledged) {
857
+ messages.push("Prepared the local Haven tools acknowledgement.");
858
+ } else {
859
+ messages.push("Local Haven tools acknowledgement still needs attention.");
860
+ }
861
+ return status;
862
+ }
863
+ return getLocalMcpConsentStatus(input.identityPath, input.signerPath);
864
+ }
865
+ async function resolveSignerConsent(input, messages) {
866
+ if (input.ackSigner || input.ackLocalTools) {
867
+ const status = await acknowledgeLocalSignerConsent(input.signerPath, (message) => messages.push(message));
868
+ if (status.acknowledged) {
869
+ messages.push("Prepared the local Haven signer acknowledgement.");
870
+ } else {
871
+ messages.push("Local Haven signer acknowledgement still needs attention.");
872
+ }
873
+ return status;
874
+ }
875
+ return getLocalSignerConsentStatus(input.signerPath);
876
+ }
877
+ function signerConsentErrorCode(signerCredentialReady, signerConsent) {
878
+ if (!signerCredentialReady) return "local_signer_credential_unavailable";
879
+ if (!signerConsent?.acknowledged) return "local_signer_ack_required";
880
+ return void 0;
881
+ }
882
+ function localMcpConsentErrorCode(signerCredentialReady, localMcpConsent) {
883
+ if (!signerCredentialReady) return "local_signer_credential_unavailable";
884
+ if (!localMcpConsent?.acknowledged) return "local_mcp_ack_required";
885
+ return void 0;
886
+ }
887
+ function nextAction(runtime, restartMode, errorCode) {
668
888
  if (errorCode) return "return_to_haven_for_wallet_approval_then_finish_runtime_setup";
669
889
  if (restartMode === "hot-reload") return "return_to_haven_for_wallet_approval";
890
+ if (runtime === "codex-cli") return "return_to_haven_for_wallet_approval_then_restart_codex";
891
+ if (runtime === "claude-code") return "return_to_haven_for_wallet_approval_then_restart_claude_code";
670
892
  if (restartMode === "restart-app") return "return_to_haven_for_wallet_approval_then_restart_app";
671
893
  if (restartMode === "restart-session") return "return_to_haven_for_wallet_approval_then_restart_agent_session";
672
894
  return "return_to_haven_for_wallet_approval_then_configure_runtime";
673
895
  }
896
+ function localMcpPackageName2() {
897
+ return `@haven_ai/mcp@${mcp.MCP_VERSION}`;
898
+ }
899
+ function usesLocalMcp(runtime) {
900
+ return runtime === "codex-cli" || runtime === "claude-code";
901
+ }
674
902
 
675
903
  // src/runtime.ts
676
- var CONNECTOR_VERSION = "0.1.0";
904
+ var CONNECTOR_VERSION = "0.1.2";
677
905
  async function runConnect(options, deps = {}) {
678
906
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
679
907
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -719,9 +947,15 @@ async function runConnect(options, deps = {}) {
719
947
  agentId: registration.agent_id,
720
948
  apiKey: localApiKey,
721
949
  delegateKey: localKey.privateKey,
950
+ delegateAddress: localKey.address,
722
951
  safeAddress: setup.haven_wallet.address,
723
952
  chainId: setup.haven_wallet.chain_id,
724
953
  network: setup.haven_wallet.network,
954
+ agentBudget: setup.agent_budget.map((budget) => ({
955
+ token_symbol: budget.token_symbol,
956
+ allowance_amount: budget.allowance_amount,
957
+ reset_period_min: budget.reset_period_min
958
+ })),
725
959
  apiUrl: options.apiBaseUrl,
726
960
  hostedMcpUrl: registration.hosted_mcp_url,
727
961
  warn: log
@@ -735,16 +969,23 @@ async function runConnect(options, deps = {}) {
735
969
  signerPath: credentialPaths.signerPath,
736
970
  identityPath: credentialPaths.identityPath,
737
971
  credentialDirectory: credentialPaths.directory,
738
- environmentLabel: options.environmentLabel ?? "Local workspace"
972
+ environmentLabel: options.environmentLabel ?? "Local workspace",
973
+ ackSigner: options.ackSigner,
974
+ ackLocalTools: options.ackLocalTools
739
975
  });
740
976
  printRuntimeInstall(runtimeInstall, log);
741
977
  try {
742
978
  await api.updateInstallStatus(registration.setup_id, localApiKey, {
743
979
  runtime: runtimeInstall.runtime,
744
980
  connectorVersion,
981
+ runtimeMcpMode: runtimeInstall.runtimeMcpMode,
745
982
  hostedMcpConfigured: runtimeInstall.hostedMcpConfigured,
746
983
  localSignerConfigured: runtimeInstall.localSignerConfigured,
984
+ localMcpConfigured: runtimeInstall.localMcpConfigured,
747
985
  credentialFilesWritten: true,
986
+ signerAcknowledged: runtimeInstall.signerAcknowledged,
987
+ localMcpAcknowledged: runtimeInstall.localMcpAcknowledged,
988
+ activationCommandAvailable: Boolean(runtimeInstall.activationCommand),
748
989
  probeResult: runtimeInstall.probeResult,
749
990
  restartRequired: runtimeInstall.restartRequired,
750
991
  nextUserAction: runtimeInstall.nextUserAction,
@@ -756,7 +997,7 @@ async function runConnect(options, deps = {}) {
756
997
  }
757
998
  log("Return to Haven to approve the agent rules.");
758
999
  if (runtimeInstall.restartRequired) {
759
- log("Restart this agent session after approval if your runtime loads MCP tools at startup.");
1000
+ log("After approval, restart this agent normally so it can load Haven tools.");
760
1001
  }
761
1002
  return {
762
1003
  setupId: registration.setup_id,
@@ -782,10 +1023,12 @@ function secureLogger(log) {
782
1023
  }
783
1024
  function printRuntimeInstall(result, log) {
784
1025
  for (const message of result.messages) log(message);
785
- if (result.hostedMcpConfigured) {
1026
+ if (result.localMcpConfigured) {
1027
+ log("Configured local Haven MCP tools.");
1028
+ } else if (result.hostedMcpConfigured) {
786
1029
  log("Configured hosted Haven MCP identity.");
787
1030
  } else {
788
- log("Hosted Haven MCP identity still needs runtime setup.");
1031
+ log("Haven MCP tools still need runtime setup.");
789
1032
  }
790
1033
  if (result.localSignerConfigured) {
791
1034
  log("Configured local Haven signer.");
@@ -815,6 +1058,11 @@ function parseArgs(argv, env = process.env) {
815
1058
  options.credentialsDir = requireValue(argv, ++i, arg);
816
1059
  } else if (arg === "--environment-label") {
817
1060
  options.environmentLabel = requireValue(argv, ++i, arg);
1061
+ } else if (arg === "--ack-local-tools") {
1062
+ options.ackLocalTools = true;
1063
+ } else if (arg === "--ack-signer") {
1064
+ options.ackSigner = true;
1065
+ options.ackLocalTools = true;
818
1066
  } else if (arg === "--version") {
819
1067
  process.stdout.write(`${CONNECTOR_VERSION}
820
1068
  `);
@@ -843,7 +1091,7 @@ function helpText() {
843
1091
  "sends Haven only the public signing address plus a proof signature.",
844
1092
  "",
845
1093
  "Usage:",
846
- " npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --runtime claude-code",
1094
+ " npx -y @haven_ai/connect --setup hv_setup_... --api https://api.haven.example --ack-local-tools --runtime claude-code",
847
1095
  "",
848
1096
  "Options:",
849
1097
  " --setup <token> Short-lived setup token from Haven.",
@@ -851,6 +1099,8 @@ function helpText() {
851
1099
  " --runtime <name> Agent runtime hint, such as claude-code, codex-cli, cursor, vscode, or claude-desktop.",
852
1100
  " --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
853
1101
  " --environment-label <text> Non-sensitive label shown in Haven setup review.",
1102
+ " --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
1103
+ " --ack-signer Backward-compatible alias for --ack-local-tools.",
854
1104
  " --help Show this help.",
855
1105
  "",
856
1106
  "The connector never prints the private key and never sends it to Haven."