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