@haven_ai/connect 0.1.15-alpha.0 → 0.1.17-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -136,8 +136,10 @@ async function writeCredentialFiles(input) {
136
136
  await restrictPermissions(directory, 448, input.warn);
137
137
  const identityPath = path.join(directory, "identity.json");
138
138
  const signerPath = path.join(directory, "signer.json");
139
+ const agentPath = path.join(directory, "agent.json");
139
140
  await assertDoesNotExist(identityPath);
140
141
  await assertDoesNotExist(signerPath);
142
+ await assertDoesNotExist(agentPath);
141
143
  await writeOwnerOnlyJson(
142
144
  signerPath,
143
145
  {
@@ -172,7 +174,26 @@ async function writeCredentialFiles(input) {
172
174
  await promises.rm(signerPath, { force: true }).catch(() => void 0);
173
175
  throw err;
174
176
  }
175
- return { directory, identityPath, signerPath };
177
+ try {
178
+ await writeOwnerOnlyJson(
179
+ agentPath,
180
+ {
181
+ agent_id: input.agentId,
182
+ safe_address: input.safeAddress,
183
+ chain_id: input.chainId,
184
+ network: input.network,
185
+ agent_budget: input.agentBudget,
186
+ note: "Non-secret orientation for the agent: identity + configured budget. Contains no API key or signing key. For the live remaining budget, call haven_get_allowances."
187
+ },
188
+ input.warn
189
+ );
190
+ } catch (err) {
191
+ await promises.rm(signerPath, { force: true }).catch(() => void 0);
192
+ await promises.rm(identityPath, { force: true }).catch(() => void 0);
193
+ await promises.rm(agentPath, { force: true }).catch(() => void 0);
194
+ throw err;
195
+ }
196
+ return { directory, identityPath, signerPath, agentPath };
176
197
  }
177
198
  function defaultAgentDirectory(agentId, baseDir = path.join(os.homedir(), ".haven", "agents")) {
178
199
  return path.resolve(defaultCredentialRoot(baseDir), safePathPart(agentId));
@@ -214,9 +235,9 @@ var MCP_RUNTIME_MANIFEST = {
214
235
  mcpPackage: "@haven_ai/mcp",
215
236
  mcpVersion: mcp.MCP_VERSION,
216
237
  sdkPackage: "@haven_ai/sdk",
217
- sdkVersion: "0.1.15-alpha.0",
238
+ sdkVersion: "0.1.17-alpha.0",
218
239
  signerPackage: "@haven_ai/signer",
219
- signerVersion: "0.1.15-alpha.0",
240
+ signerVersion: "0.1.17-alpha.0",
220
241
  minimumNodeVersion: "20.0.0",
221
242
  supportedClients: ["codex-cli", "codex-desktop", "claude-code"],
222
243
  requiredTools: mcp.registeredToolNames()
@@ -231,117 +252,6 @@ function signerPackageSpec() {
231
252
  return `${MCP_RUNTIME_MANIFEST.signerPackage}@${MCP_RUNTIME_MANIFEST.signerVersion}`;
232
253
  }
233
254
 
234
- // src/runtime-registry.ts
235
- var RUNTIME_PROFILES = {
236
- "claude-code": {
237
- id: "claude-code",
238
- label: "Claude Code",
239
- restartMode: "restart-session",
240
- canWriteRuntimeConfig: true
241
- },
242
- "codex-cli": {
243
- id: "codex-cli",
244
- label: "Codex CLI",
245
- restartMode: "restart-session",
246
- canWriteRuntimeConfig: true
247
- },
248
- "codex-desktop": {
249
- id: "codex-desktop",
250
- label: "Codex Desktop",
251
- restartMode: "restart-session",
252
- canWriteRuntimeConfig: true
253
- },
254
- cursor: {
255
- id: "cursor",
256
- label: "Cursor",
257
- restartMode: "hot-reload",
258
- canWriteRuntimeConfig: true
259
- },
260
- vscode: {
261
- id: "vscode",
262
- label: "VS Code",
263
- restartMode: "hot-reload",
264
- canWriteRuntimeConfig: true
265
- },
266
- "vscode-insiders": {
267
- id: "vscode-insiders",
268
- label: "VS Code Insiders",
269
- restartMode: "hot-reload",
270
- canWriteRuntimeConfig: true
271
- },
272
- "claude-desktop": {
273
- id: "claude-desktop",
274
- label: "Claude Desktop",
275
- restartMode: "restart-app",
276
- canWriteRuntimeConfig: true
277
- },
278
- other: {
279
- id: "other",
280
- label: "Other agent runtime",
281
- restartMode: "manual",
282
- canWriteRuntimeConfig: false
283
- }
284
- };
285
- var RUNTIME_ALIASES = {
286
- claude: "claude-code",
287
- "claude-code": "claude-code",
288
- claudecode: "claude-code",
289
- "claude_code": "claude-code",
290
- codex: "codex-cli",
291
- "codex-cli": "codex-cli",
292
- codexcli: "codex-cli",
293
- "codex_cli": "codex-cli",
294
- "codex-desktop": "codex-desktop",
295
- "codex_desktop": "codex-desktop",
296
- codexdesktop: "codex-desktop",
297
- "codex-app": "codex-desktop",
298
- "codex_app": "codex-desktop",
299
- codexapp: "codex-desktop",
300
- cursor: "cursor",
301
- vscode: "vscode",
302
- "vs-code": "vscode",
303
- "vs_code": "vscode",
304
- code: "vscode",
305
- "vscode-insiders": "vscode-insiders",
306
- "vscode_insiders": "vscode-insiders",
307
- vscodeinsiders: "vscode-insiders",
308
- "vs-code-insiders": "vscode-insiders",
309
- "code-insiders": "vscode-insiders",
310
- insiders: "vscode-insiders",
311
- "claude-desktop": "claude-desktop",
312
- "claude_desktop": "claude-desktop",
313
- claudesktop: "claude-desktop",
314
- desktop: "claude-desktop",
315
- other: "other",
316
- manual: "other"
317
- };
318
- function runtimeProfile(runtime, env = process.env) {
319
- return RUNTIME_PROFILES[normalizeRuntime(runtime, env)];
320
- }
321
- function normalizeRuntime(runtime, env = process.env) {
322
- const explicit = normalizeRuntimeName(runtime);
323
- if (explicit) return explicit;
324
- return detectRuntime(env) ?? "other";
325
- }
326
- function restartRequiredForRuntime(runtime, env = process.env) {
327
- const mode = runtimeProfile(runtime, env).restartMode;
328
- return mode === "restart-session" || mode === "restart-app";
329
- }
330
- function runtimeRequiresHardRestart(runtime) {
331
- return runtime === "claude-desktop" || runtime === "codex-desktop";
332
- }
333
- function normalizeRuntimeName(runtime) {
334
- const key = runtime?.trim().toLowerCase();
335
- if (!key) return null;
336
- return RUNTIME_ALIASES[key.replace(/\s+/g, "-")] ?? null;
337
- }
338
- function detectRuntime(env) {
339
- if (env.CLAUDECODE || env.CLAUDE_CODE || env.CLAUDECODE_CWD) return "claude-code";
340
- if (env.CODEX_SANDBOX || env.CODEX_HOME || env.CODEX_CWD) return "codex-cli";
341
- if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
342
- return null;
343
- }
344
-
345
255
  // src/config-writers.ts
346
256
  var InvalidCodexTomlError = class extends Error {
347
257
  constructor(message) {
@@ -488,7 +398,6 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
488
398
  async function writeCodexConfig(input) {
489
399
  const target = codexConfigPath(input.homeDir);
490
400
  const local = input.mode === "local";
491
- const restartMessage = runtimeRequiresHardRestart(input.runtime) ? "After Haven approval, restart Codex Desktop so it can load Haven tools." : "After Haven approval, Haven tools should appear in your next Codex message. If they don't, restart Codex to load them.";
492
401
  try {
493
402
  const existing = await readOptional(target);
494
403
  if (local) {
@@ -506,11 +415,7 @@ async function writeCodexConfig(input) {
506
415
  changed: existing !== merged2,
507
416
  restartRequired: true,
508
417
  messages: [
509
- `Updated local Haven MCP entry in ${configTargetLabel(input.runtime)}.`,
510
- // Codex Desktop loads MCP servers at app launch; Codex CLI typically
511
- // picks them up in the next session. Branch the copy so desktop users
512
- // get the unambiguous instruction.
513
- restartMessage
418
+ `Updated local Haven MCP entry in ${configTargetLabel(input.runtime)}.`
514
419
  ]
515
420
  };
516
421
  }
@@ -525,8 +430,7 @@ async function writeCodexConfig(input) {
525
430
  changed: existing !== merged,
526
431
  restartRequired: true,
527
432
  messages: [
528
- `Updated Haven MCP entries in ${configTargetLabel(input.runtime)}.`,
529
- restartMessage
433
+ `Updated Haven MCP entries in ${configTargetLabel(input.runtime)}.`
530
434
  ]
531
435
  };
532
436
  } catch (err) {
@@ -1079,23 +983,29 @@ function parseNodeVersion(value) {
1079
983
  ];
1080
984
  }
1081
985
  async function installRuntimePackages(runtimeDirectory, npmCacheDirectory, runCommand) {
1082
- const args = [
986
+ const baseArgs = [
1083
987
  "install",
1084
988
  "--prefix",
1085
989
  runtimeDirectory,
1086
- "--cache",
1087
- npmCacheDirectory,
1088
990
  "--no-audit",
1089
991
  "--no-fund",
1090
992
  "--omit=dev",
993
+ "--prefer-offline",
1091
994
  mcpPackageSpec(),
1092
995
  sdkPackageSpec()
1093
996
  ];
1094
- try {
997
+ const run = async (args) => {
1095
998
  if (runCommand) await runCommand("npm", args);
1096
999
  else await execFileAsync("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1097
- } catch (err) {
1098
- throw new Error(`Could not install local Haven MCP runtime ${mcpPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`);
1000
+ };
1001
+ try {
1002
+ await run(baseArgs);
1003
+ } catch {
1004
+ try {
1005
+ await run([...baseArgs, "--cache", npmCacheDirectory]);
1006
+ } catch (err) {
1007
+ throw new Error(`Could not install local Haven MCP runtime ${mcpPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`);
1008
+ }
1099
1009
  }
1100
1010
  }
1101
1011
  async function installedRuntimeMatches(runtimeDirectory, cliPath) {
@@ -1199,25 +1109,31 @@ async function prepareSignerRuntime(input, deps = {}) {
1199
1109
  };
1200
1110
  }
1201
1111
  async function installRuntimePackages2(runtimeDirectory, npmCacheDirectory, runCommand) {
1202
- const args = [
1112
+ const baseArgs = [
1203
1113
  "install",
1204
1114
  "--prefix",
1205
1115
  runtimeDirectory,
1206
- "--cache",
1207
- npmCacheDirectory,
1208
1116
  "--no-audit",
1209
1117
  "--no-fund",
1210
1118
  "--omit=dev",
1119
+ "--prefer-offline",
1211
1120
  signerPackageSpec(),
1212
1121
  sdkPackageSpec()
1213
1122
  ];
1214
- try {
1123
+ const run = async (args) => {
1215
1124
  if (runCommand) await runCommand("npm", args);
1216
1125
  else await execFileAsync2("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1217
- } catch (err) {
1218
- throw new Error(
1219
- `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1220
- );
1126
+ };
1127
+ try {
1128
+ await run(baseArgs);
1129
+ } catch {
1130
+ try {
1131
+ await run([...baseArgs, "--cache", npmCacheDirectory]);
1132
+ } catch (err) {
1133
+ throw new Error(
1134
+ `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1135
+ );
1136
+ }
1221
1137
  }
1222
1138
  }
1223
1139
  async function installedRuntimeMatches2(runtimeDirectory, cliPath) {
@@ -1280,6 +1196,117 @@ async function assertFileExists2(path, label) {
1280
1196
  throw new Error(`Missing ${label}: ${path}`);
1281
1197
  }
1282
1198
  }
1199
+
1200
+ // src/runtime-registry.ts
1201
+ var RUNTIME_PROFILES = {
1202
+ "claude-code": {
1203
+ id: "claude-code",
1204
+ label: "Claude Code",
1205
+ restartMode: "restart-session",
1206
+ canWriteRuntimeConfig: true
1207
+ },
1208
+ "codex-cli": {
1209
+ id: "codex-cli",
1210
+ label: "Codex CLI",
1211
+ restartMode: "restart-session",
1212
+ canWriteRuntimeConfig: true
1213
+ },
1214
+ "codex-desktop": {
1215
+ id: "codex-desktop",
1216
+ label: "Codex Desktop",
1217
+ restartMode: "restart-session",
1218
+ canWriteRuntimeConfig: true
1219
+ },
1220
+ cursor: {
1221
+ id: "cursor",
1222
+ label: "Cursor",
1223
+ restartMode: "hot-reload",
1224
+ canWriteRuntimeConfig: true
1225
+ },
1226
+ vscode: {
1227
+ id: "vscode",
1228
+ label: "VS Code",
1229
+ restartMode: "hot-reload",
1230
+ canWriteRuntimeConfig: true
1231
+ },
1232
+ "vscode-insiders": {
1233
+ id: "vscode-insiders",
1234
+ label: "VS Code Insiders",
1235
+ restartMode: "hot-reload",
1236
+ canWriteRuntimeConfig: true
1237
+ },
1238
+ "claude-desktop": {
1239
+ id: "claude-desktop",
1240
+ label: "Claude Desktop",
1241
+ restartMode: "restart-app",
1242
+ canWriteRuntimeConfig: true
1243
+ },
1244
+ other: {
1245
+ id: "other",
1246
+ label: "Other agent runtime",
1247
+ restartMode: "manual",
1248
+ canWriteRuntimeConfig: false
1249
+ }
1250
+ };
1251
+ var RUNTIME_ALIASES = {
1252
+ claude: "claude-code",
1253
+ "claude-code": "claude-code",
1254
+ claudecode: "claude-code",
1255
+ "claude_code": "claude-code",
1256
+ codex: "codex-cli",
1257
+ "codex-cli": "codex-cli",
1258
+ codexcli: "codex-cli",
1259
+ "codex_cli": "codex-cli",
1260
+ "codex-desktop": "codex-desktop",
1261
+ "codex_desktop": "codex-desktop",
1262
+ codexdesktop: "codex-desktop",
1263
+ "codex-app": "codex-desktop",
1264
+ "codex_app": "codex-desktop",
1265
+ codexapp: "codex-desktop",
1266
+ cursor: "cursor",
1267
+ vscode: "vscode",
1268
+ "vs-code": "vscode",
1269
+ "vs_code": "vscode",
1270
+ code: "vscode",
1271
+ "vscode-insiders": "vscode-insiders",
1272
+ "vscode_insiders": "vscode-insiders",
1273
+ vscodeinsiders: "vscode-insiders",
1274
+ "vs-code-insiders": "vscode-insiders",
1275
+ "code-insiders": "vscode-insiders",
1276
+ insiders: "vscode-insiders",
1277
+ "claude-desktop": "claude-desktop",
1278
+ "claude_desktop": "claude-desktop",
1279
+ claudesktop: "claude-desktop",
1280
+ desktop: "claude-desktop",
1281
+ other: "other",
1282
+ manual: "other"
1283
+ };
1284
+ function runtimeProfile(runtime, env = process.env) {
1285
+ return RUNTIME_PROFILES[normalizeRuntime(runtime, env)];
1286
+ }
1287
+ function normalizeRuntime(runtime, env = process.env) {
1288
+ const explicit = normalizeRuntimeName(runtime);
1289
+ if (explicit) return explicit;
1290
+ return detectRuntime(env) ?? "other";
1291
+ }
1292
+ function restartRequiredForRuntime(runtime, env = process.env) {
1293
+ const mode = runtimeProfile(runtime, env).restartMode;
1294
+ return mode === "restart-session" || mode === "restart-app";
1295
+ }
1296
+ function runtimeRequiresHardRestart(runtime) {
1297
+ return runtime === "claude-desktop" || runtime === "codex-desktop";
1298
+ }
1299
+ function normalizeRuntimeName(runtime) {
1300
+ const key = runtime?.trim().toLowerCase();
1301
+ if (!key) return null;
1302
+ return RUNTIME_ALIASES[key.replace(/\s+/g, "-")] ?? null;
1303
+ }
1304
+ function detectRuntime(env) {
1305
+ if (env.CLAUDECODE || env.CLAUDE_CODE || env.CLAUDECODE_CWD) return "claude-code";
1306
+ if (env.CODEX_SANDBOX || env.CODEX_HOME || env.CODEX_CWD) return "codex-cli";
1307
+ if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
1308
+ return null;
1309
+ }
1283
1310
  async function acknowledgeLocalSignerConsent(signerPath, log) {
1284
1311
  try {
1285
1312
  const input = await buildSignerConsentInput(signerPath);
@@ -1355,6 +1382,7 @@ var execFileAsync3 = util.promisify(child_process.execFile);
1355
1382
  async function installRuntime(input, deps = {}) {
1356
1383
  const runtime = normalizeRuntime(input.runtime, deps.env);
1357
1384
  const profile = runtimeProfile(runtime, deps.env);
1385
+ const progress = deps.onProgress ?? (() => void 0);
1358
1386
  const localRuntime = input.localMcp === true && supportsLocalMcp(runtime);
1359
1387
  const consentMessages = [];
1360
1388
  const localMcpConsent = localRuntime ? await resolveLocalMcpConsent(input, consentMessages) : void 0;
@@ -1414,6 +1442,7 @@ async function installRuntime(input, deps = {}) {
1414
1442
  }
1415
1443
  let signerCommand;
1416
1444
  if (!localRuntime) {
1445
+ progress("Getting the signer ready\u2026");
1417
1446
  try {
1418
1447
  const signerRuntime = await prepareSignerForRuntime(input, deps);
1419
1448
  signerCommand = { command: signerRuntime.command, args: signerRuntime.args };
@@ -1425,6 +1454,7 @@ async function installRuntime(input, deps = {}) {
1425
1454
  }
1426
1455
  }
1427
1456
  const signerRuntimePrepared = localRuntime ? void 0 : signerCommand !== void 0;
1457
+ progress("Setting up your Haven tools\u2026");
1428
1458
  const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input, signerCommand) : await writeRuntimeConfig({
1429
1459
  runtime,
1430
1460
  hostedMcpUrl: input.hostedMcpUrl,
@@ -1437,6 +1467,7 @@ async function installRuntime(input, deps = {}) {
1437
1467
  homeDir: deps.homeDir,
1438
1468
  mode: localRuntime ? "local" : "hosted"
1439
1469
  });
1470
+ progress("Almost there \u2014 just confirming everything connects\u2026");
1440
1471
  const localProbePromise = configResult.runtimeMcpMode === "local_stdio" && localRuntimeInstall ? runLocalMcpProbe(localRuntimeInstall, deps) : Promise.resolve(void 0);
1441
1472
  const [hostedProbe, signerCredentialReady, localMcpProbe] = await Promise.all([
1442
1473
  configResult.hostedConfigured ? probeHostedMcpTools(input.apiKey, input.hostedMcpUrl, deps.fetch) : Promise.resolve({ status: "bad_response" }),
@@ -1501,8 +1532,7 @@ async function configureClaudeCode(deps, localMcpCommand) {
1501
1532
  restartRequired: true,
1502
1533
  messages: [
1503
1534
  "Updated local Haven MCP entry with Claude Code.",
1504
- ...verified ? ["Verified Claude Code MCP entry."] : [],
1505
- "After Haven approval, Haven tools should appear in your next Claude Code message. If they don't, restart the session to load them."
1535
+ ...verified ? ["Verified Claude Code MCP entry."] : []
1506
1536
  ]
1507
1537
  };
1508
1538
  } catch (err) {
@@ -1551,8 +1581,7 @@ async function configureClaudeCodeHosted(deps, input, signerCommand) {
1551
1581
  restartRequired: true,
1552
1582
  messages: [
1553
1583
  "Updated hosted Haven MCP and local signer entries with Claude Code.",
1554
- ...verified ? ["Verified Claude Code MCP entry."] : [],
1555
- "After Haven approval, Haven tools should appear in your next Claude Code message. If they don't, restart the session to load them."
1584
+ ...verified ? ["Verified Claude Code MCP entry."] : []
1556
1585
  ]
1557
1586
  };
1558
1587
  } catch (err) {
@@ -1680,7 +1709,7 @@ function localRuntimePrepareErrorCode(err) {
1680
1709
  }
1681
1710
 
1682
1711
  // src/runtime.ts
1683
- var CONNECTOR_VERSION = "0.1.15-alpha.0";
1712
+ var CONNECTOR_VERSION = "0.1.17-alpha.0";
1684
1713
  async function runConnect(options, deps = {}) {
1685
1714
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
1686
1715
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -1700,6 +1729,7 @@ async function runConnect(options, deps = {}) {
1700
1729
  );
1701
1730
  }
1702
1731
  }
1732
+ log("Warming up your connection to Haven\u2026");
1703
1733
  const setup = await api.resolveSetup({
1704
1734
  setupToken: options.setupToken,
1705
1735
  connectorVersion,
@@ -1707,12 +1737,12 @@ async function runConnect(options, deps = {}) {
1707
1737
  });
1708
1738
  printSetupSummary(setup, log);
1709
1739
  await preflightStorage({ baseDir: options.credentialsDir, warn: log });
1710
- log("Checked local credential storage.");
1740
+ log("Checked local credential storage \u2014 all clear.");
1711
1741
  const localKey = generateKey();
1712
1742
  const localApiKey = generateLocalApiKey();
1713
- log("Generated local signing key.");
1714
- log("Generated local Haven API key.");
1743
+ log("Minting a fresh signing key and API key \u2014 both stay on this machine.");
1715
1744
  const proofSignature = await localKey.signChallenge(setup.challenge.message);
1745
+ log("Introducing your agent to Haven\u2026");
1716
1746
  const registration = await api.registerSetup({
1717
1747
  setupToken: options.setupToken,
1718
1748
  connectorVersion,
@@ -1729,6 +1759,7 @@ async function runConnect(options, deps = {}) {
1729
1759
  installCapabilities
1730
1760
  });
1731
1761
  log(`Registered signing address with Haven: ${shortAddress(registration.delegate_address)}.`);
1762
+ log("Tucking your credentials away safely on disk\u2026");
1732
1763
  const credentialPaths = await writeCredentials({
1733
1764
  baseDir: options.credentialsDir,
1734
1765
  agentId: registration.agent_id,
@@ -1750,6 +1781,7 @@ async function runConnect(options, deps = {}) {
1750
1781
  });
1751
1782
  log(`Stored Haven identity credential locally: ${credentialPaths.identityPath}`);
1752
1783
  log(`Stored local signer credential locally: ${credentialPaths.signerPath}`);
1784
+ log(`Stored non-secret agent orientation locally: ${credentialPaths.agentPath}`);
1753
1785
  if (setup.x402_binding_signer) {
1754
1786
  log("Configured x402 binding signer for the local signer.");
1755
1787
  } else {
@@ -1768,8 +1800,14 @@ async function runConnect(options, deps = {}) {
1768
1800
  ackSigner: options.ackSigner,
1769
1801
  ackLocalTools: options.ackLocalTools,
1770
1802
  localMcp: options.localMcp
1771
- });
1803
+ }, { onProgress: log });
1772
1804
  printRuntimeInstall(runtimeInstall, log);
1805
+ if (runtimeInstall.errorCode) {
1806
+ log("Haven setup needs a couple more steps on this machine \u2014 see the notes above.");
1807
+ } else {
1808
+ log("Haven setup on this machine is complete.");
1809
+ }
1810
+ printNextSteps(runtimeInstall, log);
1773
1811
  try {
1774
1812
  await api.updateInstallStatus(registration.setup_id, localApiKey, {
1775
1813
  runtime: runtimeInstall.runtime,
@@ -1792,14 +1830,6 @@ async function runConnect(options, deps = {}) {
1792
1830
  } catch (err) {
1793
1831
  log(`Could not report install status to Haven: ${err instanceof Error ? err.message : String(err)}`);
1794
1832
  }
1795
- log("Return to Haven to approve the agent rules.");
1796
- if (runtimeInstall.restartRequired) {
1797
- if (runtimeRequiresHardRestart(runtimeInstall.runtime)) {
1798
- log("After approval, restart this agent so it can load Haven tools.");
1799
- } else {
1800
- log("After approval, Haven tools should appear in your next message. If they don't, restart this agent to load them.");
1801
- }
1802
- }
1803
1833
  return {
1804
1834
  setupId: registration.setup_id,
1805
1835
  agentId: registration.agent_id,
@@ -1837,6 +1867,18 @@ function printRuntimeInstall(result, log) {
1837
1867
  log("Local Haven signer still needs runtime setup.");
1838
1868
  }
1839
1869
  }
1870
+ function printNextSteps(result, log) {
1871
+ log("Next: approve the agent rules in Haven. No Haven tools appear until you approve \u2014 restart or not.");
1872
+ if (result.restartRequired) {
1873
+ if (runtimeRequiresHardRestart(result.runtime)) {
1874
+ log("After you approve, restart this agent so it can load Haven tools.");
1875
+ } else {
1876
+ log("After you approve, restart this agent \u2014 your current session won't load the Haven tools until you do.");
1877
+ }
1878
+ } else {
1879
+ log("After you approve, the Haven tools should appear in this runtime shortly.");
1880
+ }
1881
+ }
1840
1882
 
1841
1883
  // src/args.ts
1842
1884
  function parseArgs(argv, env = process.env) {