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