@haven_ai/connect 0.0.0-dev.202609042048.ad6cc9b → 0.0.0-dev.202609050705.51c3d92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -169,11 +169,11 @@ Pass `--json` when a launcher needs a machine-readable completion record. Connec
169
169
  writes progress and human recovery notes to stderr and exactly one JSON object
170
170
  to stdout, with `schema_version: 1` and `outcome` set to `complete`,
171
171
  `action_required`, or `failed`. Structured runs skip the interactive
172
- budget-approval wait so the record is emitted promptly; approve in the Haven
173
- dashboard whenever ready and verify later with the read-only `haven_get_agent`
172
+ budget-approval wait so the record is emitted promptly; open `approval.url`
173
+ whenever ready and verify later with the read-only `haven_get_agent`
174
174
  tool. The object includes runtime/topology status,
175
175
  probe result, activation and next-action guidance, approval state/expiry (null
176
- when the backend does not provide an approval expiry), the two
176
+ when the backend does not provide an approval expiry) and `approval.url`, the two
177
177
  read-only verification tools, `hosted_mcp_url`, `superseded_agent_ids`, and —
178
178
  on a run that replaced existing wiring — `superseded_agents_retired_locally`
179
179
  with `retired_agent_ids`. It
@@ -182,6 +182,18 @@ contents, full credential paths, or full delegate address. The same redacted
182
182
  object is available to library callers as `runConnect(...).outcome`; the older
183
183
  fields remain for additive compatibility.
184
184
 
185
+ `approval.url` (#2528) is the absolute link to this setup's budget approval,
186
+ returned by the backend at register. Present only when `approval.required` is
187
+ `true` **and** the backend is new enough to send one — a deployment older than
188
+ #2528 omits the key entirely, which is why a caller must test for it rather
189
+ than assume it. In prose mode the same link replaces "Return to Haven" in the
190
+ printed next steps. It carries no secret (the setup token never appears in it),
191
+ and it is the ONLY approval link a caller should use: the outcome carries no
192
+ setup id, so there is nothing to assemble one from — relay the whole link or
193
+ none. The connector also reports `run_mode` (`json` or `prose`) to the backend
194
+ at register, so Haven can tell an automated setup from a narrated one; it is
195
+ sent, not returned, and appears in no output.
196
+
185
197
  `hosted_mcp_url` is the hosted MCP endpoint this run wired up — **not** the
186
198
  backend URL you passed as `--api`. The hosted MCP server is a separate
187
199
  deployment, so the two differing is intended topology, not an environment
package/dist/cli.cjs CHANGED
@@ -55,6 +55,7 @@ function createConnectApiClient(baseUrl, fetchImpl = fetch) {
55
55
  runtime: input.runtime,
56
56
  connector_version: input.connectorVersion,
57
57
  mcp_server_name: input.mcpServerName,
58
+ run_mode: input.runMode,
58
59
  connector_context: input.connectorContext,
59
60
  install_capabilities: input.installCapabilities && {
60
61
  can_write_runtime_config: input.installCapabilities.canWriteRuntimeConfig,
@@ -535,9 +536,9 @@ var init_runtime_manifest = __esm({
535
536
  mcpPackage: "@haven_ai/mcp",
536
537
  mcpVersion: mcp.MCP_VERSION,
537
538
  sdkPackage: "@haven_ai/sdk",
538
- sdkVersion: "0.0.0-dev.202609042048.ad6cc9b",
539
+ sdkVersion: "0.0.0-dev.202609050705.51c3d92",
539
540
  signerPackage: "@haven_ai/signer",
540
- signerVersion: "0.0.0-dev.202609042048.ad6cc9b",
541
+ signerVersion: "0.0.0-dev.202609050705.51c3d92",
541
542
  // Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
542
543
  // while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
543
544
  // so the guard that was supposed to enforce the floor waved Node v23 through
@@ -4388,7 +4389,7 @@ async function pathExists2(path) {
4388
4389
  init_unwire();
4389
4390
  init_local_mcp_runtime();
4390
4391
  init_runtime_manifest();
4391
- var CONNECTOR_VERSION = "0.0.0-dev.202609042048.ad6cc9b";
4392
+ var CONNECTOR_VERSION = "0.0.0-dev.202609050705.51c3d92";
4392
4393
  var CONNECT_OUTCOME_SCHEMA_VERSION = 1;
4393
4394
  var failureOutcomesByError = /* @__PURE__ */ new WeakMap();
4394
4395
  function failureOutcomeFor(runtimeHint, error) {
@@ -4536,6 +4537,9 @@ async function executeConnect(options, deps, trace) {
4536
4537
  // slug — `serverNamesFor` is the one place the naming rule lives, and
4537
4538
  // the hosted name is what a user pastes into an MCP config.
4538
4539
  mcpServerName: serverNamesFor(serverName).hosted,
4540
+ // #2528: 'prose' is the default because it is what a caller who says
4541
+ // nothing is doing — the library entry point is not a --json run.
4542
+ runMode: options.runMode ?? "prose",
4539
4543
  connectorContext: {
4540
4544
  environment_label: options.environmentLabel ?? "Local workspace",
4541
4545
  config_target: installCapabilities.canWriteRuntimeConfig ? "agent runtime MCP config" : "local credential files"
@@ -4623,7 +4627,7 @@ async function executeConnect(options, deps, trace) {
4623
4627
  environmentLabel: options.environmentLabel ?? "Local workspace"
4624
4628
  });
4625
4629
  if (!early.errorCode || early.errorCode === "manual_runtime_setup_required") {
4626
- log(approveBudgetCta(setup));
4630
+ log(approveBudgetCta(setup, registration.approval_url));
4627
4631
  }
4628
4632
  } catch {
4629
4633
  }
@@ -4709,7 +4713,7 @@ async function executeConnect(options, deps, trace) {
4709
4713
  if (narrateApprovalWait && !runtimeInstall.errorCode) {
4710
4714
  approval = await waitForBudgetApproval(api, registration.setup_id, localApiKey, log, options.approvalWait);
4711
4715
  }
4712
- printNextSteps(runtimeInstall, log, approval);
4716
+ printNextSteps(runtimeInstall, log, approval, registration.approval_url);
4713
4717
  const outcome = completionOutcome({
4714
4718
  runtimeInstall,
4715
4719
  delegateAddress: registration.delegate_address,
@@ -4718,7 +4722,8 @@ async function executeConnect(options, deps, trace) {
4718
4722
  supersededAgentsRetiredLocally,
4719
4723
  ...replacing ? { retiredAgentIds } : {},
4720
4724
  setupChallengeExpiresAt: setup.challenge.expires_at,
4721
- approvalRequired: registration.agent_status === "pending_approval"
4725
+ approvalRequired: registration.agent_status === "pending_approval",
4726
+ approvalUrl: registration.approval_url
4722
4727
  });
4723
4728
  if (await recordConnectOutcome(deps, credentialPaths.directory, outcome)) {
4724
4729
  log(`Saved this run's outcome to ${CONNECT_OUTCOME_FILENAME} in the agent's credential directory.`);
@@ -4751,7 +4756,16 @@ function completionOutcome(input) {
4751
4756
  instruction: manualSetup ? "Finish the manual MCP setup using the secret-free references shown in normal Connect output, then start a fresh session." : runtimeProfile(runtimeInstall.runtime).activationInstruction
4752
4757
  },
4753
4758
  next_action: nextAction2,
4754
- approval: { required: input.approvalRequired, expires_at: null },
4759
+ approval: {
4760
+ required: input.approvalRequired,
4761
+ expires_at: null,
4762
+ // Only when there is something to approve AND the backend supplied a
4763
+ // link. Never synthesised here: the connector does not know the
4764
+ // dashboard's origin, and a guessed URL is worse than none — it is the
4765
+ // "do not invent one" rule the agent runbook states, applied to the
4766
+ // tool rather than the agent.
4767
+ ...input.approvalRequired && input.approvalUrl ? { url: input.approvalUrl } : {}
4768
+ },
4755
4769
  verification: {
4756
4770
  tools: ["haven_get_agent", "haven_get_allowances"],
4757
4771
  instruction: runtimeVerificationInstruction(runtimeInstall.runtime)
@@ -4872,12 +4886,13 @@ function printSetupSummary(setup, log) {
4872
4886
  }
4873
4887
  log(`Setup challenge expires at ${setup.challenge.expires_at}. If it expires, return to Haven for a fresh setup and rerun Connect \u2014 do not reuse or paste credentials.`);
4874
4888
  }
4875
- function approveBudgetCta(setup) {
4889
+ function approveBudgetCta(setup, approvalUrl) {
4890
+ const where = approvalUrl ? `at ${approvalUrl}` : "in the Haven dashboard";
4876
4891
  const budgetPhrase = setup.agent_budget.length > 0 ? setup.agent_budget.map((budget) => `up to ${describeSetupBudget(budget)}`).join(", ") : void 0;
4877
4892
  if (budgetPhrase) {
4878
- return `\u2192 Action needed: approve this agent's budget \u2014 ${budgetPhrase} from ${setup.haven_wallet.name} on ${setup.haven_wallet.network} \u2014 in the Haven dashboard. The approval button is live now; setup continues here in the meantime.`;
4893
+ return `\u2192 Action needed: approve this agent's budget \u2014 ${budgetPhrase} from ${setup.haven_wallet.name} on ${setup.haven_wallet.network} \u2014 ${where}. The approval button is live now; setup continues here in the meantime.`;
4879
4894
  }
4880
- return "\u2192 Action needed: approve this agent's budget in the Haven dashboard \u2014 the approval button is live now. Setup continues here in the meantime.";
4895
+ return `\u2192 Action needed: approve this agent's budget ${where} \u2014 the approval button is live now. Setup continues here in the meantime.`;
4881
4896
  }
4882
4897
  function describeSetupBudget(budget) {
4883
4898
  return describeApprovedBudget({
@@ -4999,11 +5014,12 @@ async function waitForBudgetApproval(api, setupId, apiKey, log, options = {}) {
4999
5014
  );
5000
5015
  return "pending";
5001
5016
  }
5002
- function completionHandoffLines(result, approval) {
5017
+ function completionHandoffLines(result, approval, approvalUrl) {
5018
+ const approveStep = approvalUrl ? `Approve the budget at ${approvalUrl}. Approval \u2014 not restarting \u2014 unlocks Haven tools.` : "Return to Haven and approve the budget. Approval \u2014 not restarting \u2014 unlocks Haven tools.";
5003
5019
  if (result.errorCode === "manual_runtime_setup_required") {
5004
5020
  return [
5005
5021
  "Next steps:",
5006
- "1. Return to Haven and approve the budget. Approval \u2014 not restarting \u2014 unlocks Haven tools.",
5022
+ `1. ${approveStep}`,
5007
5023
  "2. Finish the manual MCP setup using the secret-free file references printed above, then start a fresh session in your runtime.",
5008
5024
  `3. ${runtimeVerificationInstruction(result.runtime)}`
5009
5025
  ];
@@ -5029,7 +5045,7 @@ function completionHandoffLines(result, approval) {
5029
5045
  }
5030
5046
  return [
5031
5047
  "Next steps:",
5032
- "1. Return to Haven and approve the budget. Approval \u2014 not restarting \u2014 unlocks Haven tools.",
5048
+ `1. ${approveStep}`,
5033
5049
  `2. ${activation}`,
5034
5050
  `3. ${runtimeVerificationInstruction(result.runtime)}`
5035
5051
  ];
@@ -5070,8 +5086,8 @@ async function listOtherAgentIds(baseDir, currentDirectory) {
5070
5086
  }
5071
5087
  return ids;
5072
5088
  }
5073
- function printNextSteps(result, log, approval) {
5074
- for (const line of completionHandoffLines(result, approval)) log(line);
5089
+ function printNextSteps(result, log, approval, approvalUrl) {
5090
+ for (const line of completionHandoffLines(result, approval, approvalUrl)) log(line);
5075
5091
  }
5076
5092
 
5077
5093
  // src/args.ts
@@ -5583,7 +5599,13 @@ async function runCli(argv, io = {
5583
5599
  // configure. --json is the automation contract — it must fail with a
5584
5600
  // machine-readable code, never block on stdin. runConnect additionally
5585
5601
  // requires a real TTY before it prompts.
5586
- interactive: !parsed.json
5602
+ interactive: !parsed.json,
5603
+ // #2528: reported to the backend at register, so the funnel can tell a
5604
+ // machine-readable run from a narrated one. Read from the SAME
5605
+ // `parsed.json` the three flags above use, rather than inferred later
5606
+ // from `waitForApproval` — that flag is already false for a prose run
5607
+ // with no TTY (#2484), so inferring would mislabel real prose runs.
5608
+ runMode: parsed.json ? "json" : "prose"
5587
5609
  },
5588
5610
  {
5589
5611
  log: (message) => (parsed.json ? io.stderr : io.stdout)(`${message}