@creatornader/agent-bridge 0.6.4 → 0.7.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/cli.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-UKFRCP6X.js";
18
18
  import {
19
19
  createClientRuntime
20
- } from "./chunk-DBQ5PNQW.js";
20
+ } from "./chunk-FDDLNAOY.js";
21
21
  import {
22
22
  BridgeHttpError,
23
23
  HttpBridgeStore
@@ -27,7 +27,7 @@ import {
27
27
  SyncingBridgeStore,
28
28
  edgeScopeKey,
29
29
  inspectEdgeScopeReadOnly
30
- } from "./chunk-HIYXPNBE.js";
30
+ } from "./chunk-J6GHXVHC.js";
31
31
  import {
32
32
  NativeDrBundleError,
33
33
  NativeDrBundleReader,
@@ -42,8 +42,8 @@ import {
42
42
  verifyNativeDrBundle,
43
43
  verifyPostgresNativeDrArtifacts,
44
44
  writeNativeDrBundle
45
- } from "./chunk-H2UAY6AV.js";
46
- import "./chunk-NSQE6YRP.js";
45
+ } from "./chunk-SHJGICQ5.js";
46
+ import "./chunk-SBVZVXGK.js";
47
47
  import {
48
48
  PrivatePathError,
49
49
  createPrivateDirectoryAccessCache,
@@ -1584,6 +1584,31 @@ var defaultExecutor = (command, args, context) => spawnSync2(command, args, {
1584
1584
  cwd: context?.cwd,
1585
1585
  env: context?.env
1586
1586
  });
1587
+ function validateLoopbackMcpUrl(value, field = "--mcp-url") {
1588
+ const text4 = strictText(value, field, 2048);
1589
+ let parsed;
1590
+ try {
1591
+ parsed = new URL(text4);
1592
+ } catch {
1593
+ throw new Error(`${field} must be a loopback HTTP MCP URL`);
1594
+ }
1595
+ if (parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.search || parsed.hash || !["127.0.0.1", "localhost", "[::1]"].includes(parsed.hostname) || !parsed.port || parsed.pathname !== "/mcp") {
1596
+ throw new Error(`${field} must be a loopback HTTP MCP URL ending in /mcp`);
1597
+ }
1598
+ return parsed.toString();
1599
+ }
1600
+ function managedClientTarget(metadata) {
1601
+ return metadata.version === 2 ? metadata.target : {
1602
+ version: 1,
1603
+ transport: "stdio",
1604
+ command: metadata.launch.command,
1605
+ args: [...metadata.launch.args],
1606
+ scope: metadata.launch.scope
1607
+ };
1608
+ }
1609
+ function targetLaunchProjection(target) {
1610
+ return target.transport === "stdio" ? { command: target.command, args: [...target.args], scope: target.scope } : { command: target.url, args: [], scope: target.scope };
1611
+ }
1587
1612
  function safeComponent2(value) {
1588
1613
  const readable = value.replace(/[^A-Za-z0-9._-]+/g, "_").slice(0, 48) || "client";
1589
1614
  const suffix = createHash3("sha256").update(value).digest("hex").slice(0, 12);
@@ -1632,8 +1657,8 @@ function expectedManagedClientMetadata(runtime2, identity2, options, env) {
1632
1657
  locator: clientRegistrationLocator(runtime2, scope, options, env)
1633
1658
  };
1634
1659
  }
1635
- function exactObject(actual, expected) {
1636
- return isDeepStrictEqual(actual, expected);
1660
+ function exactManagedTarget(actual, expected) {
1661
+ return actual != null && actual.schema === expected.schema && actual.runtime === expected.runtime && actual.identity === expected.identity && actual.instance === expected.instance && actual.backendConfigPath === expected.backendConfigPath && isDeepStrictEqual(actual.locator, expected.locator);
1637
1662
  }
1638
1663
  var REGISTRATION_ENV_KEYS = [
1639
1664
  "AGENT_BRIDGE_AGENT",
@@ -1685,6 +1710,10 @@ function safeEnvironment(value) {
1685
1710
  return environment;
1686
1711
  }
1687
1712
  function exactRegistration(expected, command, args, environment) {
1713
+ const target = managedClientTarget(expected);
1714
+ if (target.transport === "streamable-http") {
1715
+ return command === target.url && args.length === 0 && Object.keys(environment).length === 0;
1716
+ }
1688
1717
  return command === expected.launch.command && isDeepStrictEqual(args, expected.launch.args) && isDeepStrictEqual(environment, {
1689
1718
  AGENT_BRIDGE_AGENT: expected.identity,
1690
1719
  AGENT_BRIDGE_INSTANCE: expected.instance,
@@ -1776,14 +1805,29 @@ ${result.stderr}`.trim();
1776
1805
  "tool_timeout_sec",
1777
1806
  "status"
1778
1807
  ]);
1779
- const transportFields = /* @__PURE__ */ new Set(["type", "command", "args", "env", "env_vars", "cwd"]);
1808
+ const target = managedClientTarget(expected);
1809
+ const transportFields = transport?.type === "streamable_http" ? /* @__PURE__ */ new Set(["type", "url", "bearer_token_env_var", "http_headers", "env_http_headers"]) : /* @__PURE__ */ new Set(["type", "command", "args", "env", "env_vars", "cwd"]);
1780
1810
  const hasNonNullSetting = (field) => field in server && server[field] !== null;
1781
- if (!server || typeof server !== "object" || Array.isArray(server) || server.name !== "agent-bridge" || server.enabled !== true || !transport || typeof transport !== "object" || Array.isArray(transport) || transport.type !== "stdio") {
1782
- throw new Error("Codex MCP registration is not a supported stdio contract");
1811
+ if (!server || typeof server !== "object" || Array.isArray(server) || server.name !== "agent-bridge" || server.enabled !== true || !transport || typeof transport !== "object" || Array.isArray(transport)) {
1812
+ throw new Error("Codex MCP registration is not a supported contract");
1783
1813
  }
1784
1814
  if (Object.keys(server).some((field) => !serverFields.has(field)) || Object.keys(transport).some((field) => !transportFields.has(field)) || hasNonNullSetting("enabled_tools") || hasNonNullSetting("disabled_tools")) {
1785
1815
  throw new Error("Codex MCP registration contains an unsupported execution setting");
1786
1816
  }
1817
+ if (transport.type === "streamable_http") {
1818
+ if (transport.type !== "streamable_http" || transport.bearer_token_env_var !== null || transport.http_headers !== null || transport.env_http_headers !== null) {
1819
+ throw new Error("Codex MCP registration contains an unsupported HTTP setting");
1820
+ }
1821
+ const url = validateLoopbackMcpUrl(transport.url, "Codex MCP URL");
1822
+ return {
1823
+ state: exactRegistration(expected, url, [], {}) ? "exact" : "inexact",
1824
+ target: registrationTarget(expected),
1825
+ observed: { state: "present", command: url, args: [], env: {} }
1826
+ };
1827
+ }
1828
+ if (transport.type !== "stdio") {
1829
+ throw new Error("Codex MCP registration is not a supported stdio contract");
1830
+ }
1787
1831
  if ("cwd" in transport && transport.cwd !== null || "env_vars" in transport && (!Array.isArray(transport.env_vars) || transport.env_vars.length !== 0)) {
1788
1832
  throw new Error("Codex MCP registration contains an unsupported execution setting");
1789
1833
  }
@@ -1822,6 +1866,28 @@ ${result.stderr}`.trim();
1822
1866
  if (lines[0] !== "agent-bridge:" || lines[1] !== scope) {
1823
1867
  throw new Error("Claude Code MCP registration scope is not the recorded scope");
1824
1868
  }
1869
+ const target = managedClientTarget(expected);
1870
+ const httpTypeIndex = lines.indexOf(" Type: http", 2);
1871
+ if (httpTypeIndex >= 0) {
1872
+ const urlLine = lines[httpTypeIndex + 1];
1873
+ if (!urlLine?.startsWith(" URL: ")) {
1874
+ throw new Error("Claude Code MCP registration cannot be represented safely");
1875
+ }
1876
+ const url = validateLoopbackMcpUrl(urlLine.slice(" URL: ".length), "Claude Code MCP URL");
1877
+ let index2 = httpTypeIndex + 2;
1878
+ while (lines[index2] === "") index2 += 1;
1879
+ if (lines[index2] !== `To remove this server, run: claude mcp remove agent-bridge -s ${expected.launch.scope}`) {
1880
+ throw new Error("Claude Code MCP registration cannot be represented safely");
1881
+ }
1882
+ if (lines.slice(index2 + 1).some((line) => line !== "")) {
1883
+ throw new Error("Claude Code MCP registration cannot be represented safely");
1884
+ }
1885
+ return {
1886
+ state: target.transport === "streamable-http" && target.url === url ? "exact" : "inexact",
1887
+ target: registrationTarget(expected),
1888
+ observed: { state: "present", command: url, args: [], env: {} }
1889
+ };
1890
+ }
1825
1891
  const typeIndex = lines.indexOf(" Type: stdio", 2);
1826
1892
  if (typeIndex < 0 || lines[typeIndex + 1] !== ` Command: ${expected.launch.command}` || lines[typeIndex + 2] !== " Args:" || lines[typeIndex + 3] !== " Environment:") {
1827
1893
  if (typeIndex < 0 || lines[typeIndex + 2] !== " Args:" || lines[typeIndex + 3] !== " Environment:") {
@@ -1987,7 +2053,9 @@ function strictLocator(runtime2, value) {
1987
2053
  function strictManagedMetadata(value, runtime2, instance) {
1988
2054
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("managed metadata is invalid");
1989
2055
  const metadata = value;
1990
- if (Object.keys(metadata).sort().join(",") !== "backendConfigPath,identity,instance,launch,locator,runtime,schema,version" || metadata.schema !== "agent-bridge.client-management" || metadata.version !== 1 || metadata.runtime !== runtime2) throw new Error("managed metadata is invalid");
2056
+ const version = metadata.version;
2057
+ const expectedKeys = version === 2 ? "backendConfigPath,identity,instance,launch,locator,runtime,schema,target,version" : "backendConfigPath,identity,instance,launch,locator,runtime,schema,version";
2058
+ if (Object.keys(metadata).sort().join(",") !== expectedKeys || metadata.schema !== "agent-bridge.client-management" || version !== 1 && version !== 2 || metadata.runtime !== runtime2) throw new Error("managed metadata is invalid");
1991
2059
  const identity2 = strictText(metadata.identity, "identity", 128);
1992
2060
  const recordedInstance = strictText(metadata.instance, "instance", 128);
1993
2061
  if (recordedInstance !== instance) throw new Error("managed metadata instance does not match its locator");
@@ -2008,7 +2076,7 @@ function strictManagedMetadata(value, runtime2, instance) {
2008
2076
  }
2009
2077
  const locator = strictLocator(runtime2, metadata.locator);
2010
2078
  const scope = launch.scope;
2011
- if (runtime2 !== "claude-code" && scope !== null || runtime2 === "claude-code" && scope !== locator.scope || (runtime2 === "codex" || runtime2 === "claude-code") && launch.args.length !== 0 || runtime2 === "claude-desktop" && !isAbsolute2(strictText(launch.command, "launch command", 4096)) || (runtime2 === "codex" || runtime2 === "claude-code") && (() => {
2079
+ if (runtime2 !== "claude-code" && scope !== null || runtime2 === "claude-code" && scope !== locator.scope || (runtime2 === "codex" || runtime2 === "claude-code") && launch.args.length !== 0 || runtime2 === "claude-desktop" && !isAbsolute2(strictText(launch.command, "launch command", 4096)) || (runtime2 === "codex" || runtime2 === "claude-code") && !(version === 2 && metadata.target?.transport === "streamable-http") && (() => {
2012
2080
  try {
2013
2081
  safeNativeExecutableContract(launch.command, "launch command");
2014
2082
  return false;
@@ -2018,14 +2086,69 @@ function strictManagedMetadata(value, runtime2, instance) {
2018
2086
  })()) {
2019
2087
  throw new Error("managed metadata launch is invalid");
2020
2088
  }
2089
+ let target;
2090
+ if (version === 2) {
2091
+ if (!metadata.target || typeof metadata.target !== "object" || Array.isArray(metadata.target)) {
2092
+ throw new Error("managed metadata target is invalid");
2093
+ }
2094
+ const candidate = metadata.target;
2095
+ const targetScope = candidate.scope;
2096
+ if (candidate.version !== 1 || ![null, "local", "user", "project"].includes(targetScope)) {
2097
+ throw new Error("managed metadata target is invalid");
2098
+ }
2099
+ if (candidate.transport === "streamable-http") {
2100
+ if (Object.keys(candidate).sort().join(",") !== "scope,transport,url,version" || runtime2 === "claude-desktop" || targetScope !== scope) {
2101
+ throw new Error("managed metadata target is invalid");
2102
+ }
2103
+ target = {
2104
+ version: 1,
2105
+ transport: "streamable-http",
2106
+ url: validateLoopbackMcpUrl(candidate.url, "managed metadata target URL"),
2107
+ scope: targetScope
2108
+ };
2109
+ } else if (candidate.transport === "stdio") {
2110
+ if (Object.keys(candidate).sort().join(",") !== "args,command,scope,transport,version" || !Array.isArray(candidate.args) || candidate.args.length > 16 || candidate.args.some((arg) => {
2111
+ try {
2112
+ strictText(arg, "target argument", 1024);
2113
+ return false;
2114
+ } catch {
2115
+ return true;
2116
+ }
2117
+ })) {
2118
+ throw new Error("managed metadata target is invalid");
2119
+ }
2120
+ target = {
2121
+ version: 1,
2122
+ transport: "stdio",
2123
+ command: strictText(candidate.command, "target command", 4096),
2124
+ args: [...candidate.args],
2125
+ scope: targetScope
2126
+ };
2127
+ if (runtime2 === "claude-desktop") {
2128
+ if (!isAbsolute2(target.command)) throw new Error("managed metadata target is invalid");
2129
+ } else {
2130
+ safeNativeExecutableContract(target.command, "managed metadata target command");
2131
+ }
2132
+ } else {
2133
+ throw new Error("managed metadata target is invalid");
2134
+ }
2135
+ if (!isDeepStrictEqual(targetLaunchProjection(target), {
2136
+ command: strictText(launch.command, "launch command", 4096),
2137
+ args: [...launch.args],
2138
+ scope
2139
+ })) {
2140
+ throw new Error("managed metadata target does not match its launch projection");
2141
+ }
2142
+ }
2021
2143
  return {
2022
2144
  schema: "agent-bridge.client-management",
2023
- version: 1,
2145
+ version,
2024
2146
  runtime: runtime2,
2025
2147
  identity: identity2,
2026
2148
  instance: recordedInstance,
2027
2149
  backendConfigPath,
2028
2150
  launch: { command: strictText(launch.command, "launch command", 4096), args: [...launch.args], scope },
2151
+ ...target ? { target } : {},
2029
2152
  locator
2030
2153
  };
2031
2154
  }
@@ -2134,8 +2257,8 @@ function inspectClient(runtime2, identity2, options, execute = defaultExecutor)
2134
2257
  const normalizedIdentity = identity2.trim();
2135
2258
  const expected = expectedManagedClientMetadata(runtime2, normalizedIdentity, options, env);
2136
2259
  const path = managedClientMetadataPath(runtime2, expected.instance, env);
2137
- const registration = inspectManagedRegistration(expected, execute, env);
2138
2260
  const metadata = readMetadata(path, runtime2, expected.instance);
2261
+ const registration = inspectManagedRegistration(metadata ?? expected, execute, env);
2139
2262
  const backend = backendState(expected.backendConfigPath);
2140
2263
  let state;
2141
2264
  let reason;
@@ -2145,7 +2268,7 @@ function inspectClient(runtime2, identity2, options, execute = defaultExecutor)
2145
2268
  } else if (registration === "exact" && backend === "private-file" && metadata === void 0) {
2146
2269
  state = "unmanaged";
2147
2270
  reason = "exact registration has no managed metadata";
2148
- } else if (registration === "exact" && backend === "private-file" && exactObject(metadata, expected)) {
2271
+ } else if (registration === "exact" && backend === "private-file" && exactManagedTarget(metadata, expected)) {
2149
2272
  state = "managed";
2150
2273
  reason = "registration and managed metadata are exact";
2151
2274
  } else {
@@ -2496,7 +2619,9 @@ function validateOperationLocator(runtime2, value) {
2496
2619
  function validateOperationMetadata(value, runtime2, instance) {
2497
2620
  if (!value || typeof value !== "object" || Array.isArray(value)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2498
2621
  const metadata = value;
2499
- if (Object.keys(metadata).sort().join(",") !== "backendConfigPath,identity,instance,launch,locator,runtime,schema,version" || metadata.schema !== "agent-bridge.client-management" || metadata.version !== 1 || !RUNTIMES.includes(metadata.runtime) || runtime2 !== void 0 && metadata.runtime !== runtime2) {
2622
+ const metadataVersion = metadata.version;
2623
+ const metadataKeys = metadataVersion === 2 ? "backendConfigPath,identity,instance,launch,locator,runtime,schema,target,version" : "backendConfigPath,identity,instance,launch,locator,runtime,schema,version";
2624
+ if (Object.keys(metadata).sort().join(",") !== metadataKeys || metadata.schema !== "agent-bridge.client-management" || metadataVersion !== 1 && metadataVersion !== 2 || !RUNTIMES.includes(metadata.runtime) || runtime2 !== void 0 && metadata.runtime !== runtime2) {
2500
2625
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2501
2626
  }
2502
2627
  const recordedRuntime = metadata.runtime;
@@ -2521,19 +2646,52 @@ function validateOperationMetadata(value, runtime2, instance) {
2521
2646
  const locator = validateOperationLocator(recordedRuntime, metadata.locator);
2522
2647
  const command = safeText(launch.command, "launch command", 4096);
2523
2648
  const scope = launch.scope;
2649
+ const httpTarget = metadataVersion === 2 && metadata.target?.transport === "streamable-http";
2524
2650
  if (recordedRuntime !== "claude-code" && scope !== null || recordedRuntime === "claude-code" && scope !== locator.scope || (recordedRuntime === "codex" || recordedRuntime === "claude-code") && launch.args.length !== 0) {
2525
2651
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2526
2652
  }
2527
- if (recordedRuntime === "codex" || recordedRuntime === "claude-code") safeNativeLaunchCommand(command);
2653
+ if ((recordedRuntime === "codex" || recordedRuntime === "claude-code") && !httpTarget) {
2654
+ safeNativeLaunchCommand(command);
2655
+ }
2528
2656
  if (recordedRuntime === "claude-desktop" && !isAbsolute3(command)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2657
+ let target;
2658
+ if (metadataVersion === 2) {
2659
+ if (!metadata.target || typeof metadata.target !== "object" || Array.isArray(metadata.target)) {
2660
+ fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2661
+ }
2662
+ const candidate = metadata.target;
2663
+ if (candidate.version !== 1 || candidate.scope !== scope) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2664
+ if (candidate.transport === "streamable-http") {
2665
+ if (recordedRuntime === "claude-desktop" || Object.keys(candidate).sort().join(",") !== "scope,transport,url,version") {
2666
+ fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2667
+ }
2668
+ const url = validateLoopbackMcpUrl(candidate.url, "managed operation target URL");
2669
+ if (command !== url || launch.args.length !== 0) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2670
+ target = { version: 1, transport: "streamable-http", url, scope };
2671
+ } else if (candidate.transport === "stdio") {
2672
+ if (Object.keys(candidate).sort().join(",") !== "args,command,scope,transport,version" || candidate.command !== command || !sameCanonicalValue(candidate.args, launch.args)) {
2673
+ fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2674
+ }
2675
+ target = {
2676
+ version: 1,
2677
+ transport: "stdio",
2678
+ command,
2679
+ args: [...launch.args],
2680
+ scope
2681
+ };
2682
+ } else {
2683
+ fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2684
+ }
2685
+ }
2529
2686
  return {
2530
2687
  schema: "agent-bridge.client-management",
2531
- version: 1,
2688
+ version: metadataVersion,
2532
2689
  runtime: recordedRuntime,
2533
2690
  identity: identity2,
2534
2691
  instance: recordedInstance,
2535
2692
  backendConfigPath,
2536
2693
  launch: { command, args: [...launch.args], scope },
2694
+ ...target ? { target } : {},
2537
2695
  locator
2538
2696
  };
2539
2697
  }
@@ -2549,6 +2707,7 @@ function canonicalDigest(value) {
2549
2707
  return createHash4("sha256").update(canonical(value)).digest("hex");
2550
2708
  }
2551
2709
  function exactRegistrationProof(metadata) {
2710
+ const http = metadata.version === 2 && metadata.target?.transport === "streamable-http";
2552
2711
  return {
2553
2712
  state: "exact",
2554
2713
  target: {
@@ -2563,7 +2722,7 @@ function exactRegistrationProof(metadata) {
2563
2722
  state: "present",
2564
2723
  command: metadata.launch.command,
2565
2724
  args: [...metadata.launch.args],
2566
- env: {
2725
+ env: http ? {} : {
2567
2726
  AGENT_BRIDGE_AGENT: metadata.identity,
2568
2727
  AGENT_BRIDGE_INSTANCE: metadata.instance,
2569
2728
  AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
@@ -2572,9 +2731,23 @@ function exactRegistrationProof(metadata) {
2572
2731
  };
2573
2732
  }
2574
2733
  function forwardMetadataFromUpdate(prior, launch) {
2734
+ const target = prior.runtime !== "claude-desktop" && launch.command.startsWith("http://") ? {
2735
+ version: 1,
2736
+ transport: "streamable-http",
2737
+ url: validateLoopbackMcpUrl(launch.command, "managed operation target URL"),
2738
+ scope: launch.scope
2739
+ } : {
2740
+ version: 1,
2741
+ transport: "stdio",
2742
+ command: launch.command,
2743
+ args: [...launch.args],
2744
+ scope: launch.scope
2745
+ };
2575
2746
  return {
2576
2747
  ...prior,
2577
- launch: { command: launch.command, args: [...launch.args], scope: launch.scope }
2748
+ version: 2,
2749
+ launch: { command: launch.command, args: [...launch.args], scope: launch.scope },
2750
+ target
2578
2751
  };
2579
2752
  }
2580
2753
  function validateRegistrationProof(value, metadata) {
@@ -2589,12 +2762,13 @@ function validateRegistrationProof(value, metadata) {
2589
2762
  }
2590
2763
  const targetMetadata = validateOperationMetadata({
2591
2764
  schema: "agent-bridge.client-management",
2592
- version: 1,
2765
+ version: metadata.version,
2593
2766
  runtime: target.runtime,
2594
2767
  identity: target.identity,
2595
2768
  instance: target.instance,
2596
2769
  backendConfigPath: target.backendConfigPath,
2597
2770
  launch: target.launch,
2771
+ ...metadata.version === 2 ? { target: metadata.target } : {},
2598
2772
  locator: target.locator
2599
2773
  }, metadata.runtime, metadata.instance);
2600
2774
  if (!sameCanonicalValue(targetMetadata, metadata)) fail("CORRUPT_OPERATION", "operation manifest is corrupt");
@@ -2603,7 +2777,8 @@ function validateRegistrationProof(value, metadata) {
2603
2777
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2604
2778
  }
2605
2779
  const env = observed.env;
2606
- if (Object.keys(env).sort().join(",") !== "AGENT_BRIDGE_AGENT,AGENT_BRIDGE_CONFIG,AGENT_BRIDGE_INSTANCE" || env.AGENT_BRIDGE_AGENT !== metadata.identity || env.AGENT_BRIDGE_CONFIG !== metadata.backendConfigPath || env.AGENT_BRIDGE_INSTANCE !== metadata.instance) {
2780
+ const http = metadata.version === 2 && metadata.target?.transport === "streamable-http";
2781
+ if (http ? Object.keys(env).length !== 0 : Object.keys(env).sort().join(",") !== "AGENT_BRIDGE_AGENT,AGENT_BRIDGE_CONFIG,AGENT_BRIDGE_INSTANCE" || env.AGENT_BRIDGE_AGENT !== metadata.identity || env.AGENT_BRIDGE_CONFIG !== metadata.backendConfigPath || env.AGENT_BRIDGE_INSTANCE !== metadata.instance) {
2607
2782
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2608
2783
  }
2609
2784
  return {
@@ -2620,7 +2795,7 @@ function validateRegistrationProof(value, metadata) {
2620
2795
  state: "present",
2621
2796
  command: metadata.launch.command,
2622
2797
  args: [...metadata.launch.args],
2623
- env: {
2798
+ env: http ? {} : {
2624
2799
  AGENT_BRIDGE_AGENT: metadata.identity,
2625
2800
  AGENT_BRIDGE_CONFIG: metadata.backendConfigPath,
2626
2801
  AGENT_BRIDGE_INSTANCE: metadata.instance
@@ -2720,7 +2895,10 @@ function validateRequest2(value, runtime2, version = CLIENT_OPERATION_VERSION, i
2720
2895
  if ((runtime2 === "codex" || runtime2 === "claude-code") && (launch.args.length !== 0 || (runtime2 === "claude-code" ? !["local", "user", "project"].includes(launch.scope) : launch.scope !== null))) {
2721
2896
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2722
2897
  }
2723
- if (runtime2 === "codex" || runtime2 === "claude-code") safeNativeLaunchCommand(command);
2898
+ if (runtime2 === "codex" || runtime2 === "claude-code") {
2899
+ if (command.startsWith("http://")) validateLoopbackMcpUrl(command, "managed operation target URL");
2900
+ else safeNativeLaunchCommand(command);
2901
+ }
2724
2902
  if (runtime2 === "claude-desktop" && (launch.scope !== null || !isAbsolute3(command))) {
2725
2903
  fail("CORRUPT_OPERATION", "operation manifest is corrupt");
2726
2904
  }
@@ -3826,6 +4004,13 @@ function assertIdentity(metadata, assertion) {
3826
4004
  function registrationProof(observation) {
3827
4005
  return canonical2({ role: "registration", observation });
3828
4006
  }
4007
+ function registrationEnvironment(metadata) {
4008
+ return managedClientTarget(metadata).transport === "streamable-http" ? {} : {
4009
+ AGENT_BRIDGE_AGENT: metadata.identity,
4010
+ AGENT_BRIDGE_INSTANCE: metadata.instance,
4011
+ AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
4012
+ };
4013
+ }
3829
4014
  function exactRegistrationObservation(metadata) {
3830
4015
  return {
3831
4016
  state: "exact",
@@ -3841,11 +4026,7 @@ function exactRegistrationObservation(metadata) {
3841
4026
  state: "present",
3842
4027
  command: metadata.launch.command,
3843
4028
  args: [...metadata.launch.args],
3844
- env: {
3845
- AGENT_BRIDGE_AGENT: metadata.identity,
3846
- AGENT_BRIDGE_INSTANCE: metadata.instance,
3847
- AGENT_BRIDGE_CONFIG: metadata.backendConfigPath
3848
- }
4029
+ env: registrationEnvironment(metadata)
3849
4030
  }
3850
4031
  };
3851
4032
  }
@@ -3887,24 +4068,88 @@ function operationLaunch(metadata) {
3887
4068
  envKeys: fixedEnvKeys()
3888
4069
  };
3889
4070
  }
3890
- function updateMetadata(metadata, launch) {
4071
+ function updateMetadata(metadata, launch, target) {
3891
4072
  return {
3892
4073
  ...metadata,
3893
- launch: { command: launch.command, args: [...launch.args], scope: launch.scope }
4074
+ version: 2,
4075
+ launch: { command: launch.command, args: [...launch.args], scope: launch.scope },
4076
+ target
3894
4077
  };
3895
4078
  }
3896
- function resolveUpdateLaunch(metadata, command, env) {
4079
+ function safeProxyArgs(args, endpoint) {
4080
+ if (args.length === 0 || args.length > 16 || args.some((arg) => !arg || arg.length > 1024 || arg.trim() !== arg || /[\u0000-\u001f\u007f]/.test(arg)) || !args.includes(endpoint)) {
4081
+ throw new Error("--proxy-args-json must include the exact --mcp-url value");
4082
+ }
4083
+ if (args.some((arg, index) => {
4084
+ const previous = index > 0 ? args[index - 1] : "";
4085
+ const sensitive = /^--?(?:token|key|secret|password|authorization|credential)(?:=.*)?$/i;
4086
+ return sensitive.test(arg) || sensitive.test(previous) || /\bBearer\s+\S+/i.test(arg) || /^https?:\/\/\S*[?#]\S+/i.test(arg);
4087
+ })) {
4088
+ throw new Error("--proxy-args-json cannot contain credential-like values");
4089
+ }
4090
+ return [...args];
4091
+ }
4092
+ function resolveUpdateTarget(metadata, options, env) {
4093
+ if (options.mcpUrl !== void 0) {
4094
+ const url = validateLoopbackMcpUrl(options.mcpUrl);
4095
+ if (metadata.runtime !== "claude-desktop") {
4096
+ if (options.command !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
4097
+ throw new Error("--command, --proxy-command, and --proxy-args-json are not valid for a native HTTP target");
4098
+ }
4099
+ const scope2 = metadata.runtime === "claude-code" ? metadata.launch.scope : null;
4100
+ return {
4101
+ launch: { command: url, args: [], scope: scope2, envKeys: fixedEnvKeys() },
4102
+ target: { version: 1, transport: "streamable-http", url, scope: scope2 }
4103
+ };
4104
+ }
4105
+ if (options.command !== void 0) throw new Error("--command cannot be combined with --mcp-url");
4106
+ if (!options.proxyCommand) throw new Error("--proxy-command is required for a Desktop HTTP target");
4107
+ const command = newNativeExecutableContract(options.proxyCommand, "--proxy-command");
4108
+ const args = safeProxyArgs(options.proxyArgs ?? [], url);
4109
+ return {
4110
+ launch: { command, args, scope: null, envKeys: fixedEnvKeys() },
4111
+ target: { version: 1, transport: "stdio", command, args, scope: null }
4112
+ };
4113
+ }
4114
+ if (options.proxyCommand !== void 0 || options.proxyArgs?.length) {
4115
+ throw new Error("--proxy-command and --proxy-args-json require --mcp-url");
4116
+ }
3897
4117
  if (metadata.runtime === "claude-desktop") {
3898
- const launch = resolveDesktopLaunchContract(command, env);
3899
- return { command: launch.command, args: [...launch.args], scope: null, envKeys: fixedEnvKeys() };
4118
+ const launch = resolveDesktopLaunchContract(options.command, env);
4119
+ return {
4120
+ launch: { command: launch.command, args: [...launch.args], scope: null, envKeys: fixedEnvKeys() },
4121
+ target: {
4122
+ version: 1,
4123
+ transport: "stdio",
4124
+ command: launch.command,
4125
+ args: [...launch.args],
4126
+ scope: null
4127
+ }
4128
+ };
3900
4129
  }
3901
- const requested = command === void 0 ? "agent-bridge-mcp" : command;
4130
+ const requested = options.command === void 0 ? "agent-bridge-mcp" : options.command;
3902
4131
  newNativeExecutableContract(requested, "--command");
4132
+ const scope = metadata.runtime === "claude-code" ? metadata.launch.scope : null;
3903
4133
  return {
3904
- command: requested,
3905
- args: [],
3906
- scope: metadata.runtime === "claude-code" ? metadata.launch.scope : null,
3907
- envKeys: fixedEnvKeys()
4134
+ launch: { command: requested, args: [], scope, envKeys: fixedEnvKeys() },
4135
+ target: { version: 1, transport: "stdio", command: requested, args: [], scope }
4136
+ };
4137
+ }
4138
+ function targetFromRecordedLaunch(metadata, launch) {
4139
+ if (metadata.runtime !== "claude-desktop" && launch.command.startsWith("http://")) {
4140
+ return {
4141
+ version: 1,
4142
+ transport: "streamable-http",
4143
+ url: validateLoopbackMcpUrl(launch.command, "recorded MCP URL"),
4144
+ scope: launch.scope
4145
+ };
4146
+ }
4147
+ return {
4148
+ version: 1,
4149
+ transport: "stdio",
4150
+ command: launch.command,
4151
+ args: [...launch.args],
4152
+ scope: launch.scope
3908
4153
  };
3909
4154
  }
3910
4155
  function backendPolicy(path) {
@@ -4122,7 +4367,17 @@ function nativeContext(metadata, env) {
4122
4367
  function nativeExecute(metadata, action, execute, env) {
4123
4368
  const context = nativeContext(metadata, env);
4124
4369
  const executable = metadata.runtime === "codex" ? "codex" : "claude";
4125
- const args = action === "remove" ? metadata.runtime === "codex" ? ["mcp", "remove", "agent-bridge"] : ["mcp", "remove", "agent-bridge", "-s", metadata.launch.scope] : metadata.runtime === "codex" ? [
4370
+ const target = managedClientTarget(metadata);
4371
+ const args = action === "remove" ? metadata.runtime === "codex" ? ["mcp", "remove", "agent-bridge"] : ["mcp", "remove", "agent-bridge", "-s", metadata.launch.scope] : target.transport === "streamable-http" ? metadata.runtime === "codex" ? ["mcp", "add", "agent-bridge", "--url", target.url] : [
4372
+ "mcp",
4373
+ "add",
4374
+ "--scope",
4375
+ metadata.launch.scope,
4376
+ "--transport",
4377
+ "http",
4378
+ "agent-bridge",
4379
+ target.url
4380
+ ] : metadata.runtime === "codex" ? [
4126
4381
  "mcp",
4127
4382
  "add",
4128
4383
  "agent-bridge",
@@ -4348,11 +4603,7 @@ function buildPlan(metadata, target, execute, env) {
4348
4603
  state: "present",
4349
4604
  command: target.launch.command,
4350
4605
  args: [...target.launch.args],
4351
- env: {
4352
- AGENT_BRIDGE_AGENT: target.identity,
4353
- AGENT_BRIDGE_INSTANCE: target.instance,
4354
- AGENT_BRIDGE_CONFIG: target.backendConfigPath
4355
- }
4606
+ env: registrationEnvironment(target)
4356
4607
  }
4357
4608
  };
4358
4609
  if (target.runtime === "claude-desktop") {
@@ -4389,7 +4640,7 @@ function buildPlan(metadata, target, execute, env) {
4389
4640
  after: registrationProof(exactRegistration2)
4390
4641
  });
4391
4642
  }
4392
- if (!isDeepStrictEqual2(metadata.launch, target.launch)) {
4643
+ if (metadata.version !== target.version || !isDeepStrictEqual2(metadata.launch, target.launch) || !isDeepStrictEqual2(metadata.target, target.target)) {
4393
4644
  steps.push({
4394
4645
  kind: "metadata",
4395
4646
  target: "metadata",
@@ -4592,6 +4843,13 @@ function assertForwardRollbackMetadataState(forward, contract) {
4592
4843
  throw new Error("managed client no longer matches the forward update metadata state");
4593
4844
  }
4594
4845
  }
4846
+ function loadForwardRollbackMetadata(runtime2, instance, env) {
4847
+ try {
4848
+ return loadManagedClientMetadata(runtime2, instance, env);
4849
+ } catch {
4850
+ throw new Error("managed client no longer matches the forward update metadata state");
4851
+ }
4852
+ }
4595
4853
  function assertForwardRollbackRegistrationState(forward, contract, execute, env) {
4596
4854
  const registration = observeManagedRegistration(forward, execute, env);
4597
4855
  if (digest(registrationProof(registration)) !== contract.forwardRegistrationSha256) {
@@ -4729,7 +4987,7 @@ function switchManagedClientMetadata(before, after, env = process.env) {
4729
4987
  throw new Error("managed metadata switch was not verified");
4730
4988
  }
4731
4989
  }
4732
- function resumeAction(metadata, manifest, command, env, action) {
4990
+ function resumeAction(metadata, manifest, options, env, action) {
4733
4991
  if (manifest.runtime !== metadata.runtime || manifest.instance !== metadata.instance) {
4734
4992
  throw new Error("--resume does not match this client action, runtime, or instance");
4735
4993
  }
@@ -4740,14 +4998,18 @@ function resumeAction(metadata, manifest, command, env, action) {
4740
4998
  if (manifest.version !== 3 || !manifest.request || manifest.request.kind !== "repair" || !("identity" in manifest.request) || manifest.request.identity !== metadata.identity) {
4741
4999
  throw new Error("--resume does not match this client action, runtime, or instance");
4742
5000
  }
4743
- if (command !== void 0) throw new Error("--command is not valid for clients repair");
5001
+ if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
5002
+ throw new Error("target options are not valid for clients repair");
5003
+ }
4744
5004
  return metadata;
4745
5005
  }
4746
5006
  if (action === "uninstall") {
4747
5007
  if (manifest.version !== 3 || !manifest.request || manifest.request.kind !== "uninstall" || !("identity" in manifest.request) || manifest.request.identity !== metadata.identity) {
4748
5008
  throw new Error("--resume does not match this client action, runtime, or instance");
4749
5009
  }
4750
- if (command !== void 0) throw new Error("--command is not valid for clients uninstall");
5010
+ if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
5011
+ throw new Error("target options are not valid for clients uninstall");
5012
+ }
4751
5013
  return metadata;
4752
5014
  }
4753
5015
  if (!manifest.request || manifest.request.kind !== "update" || !("identity" in manifest.request) || !("launch" in manifest.request) || manifest.request.identity !== metadata.identity) {
@@ -4761,13 +5023,13 @@ function resumeAction(metadata, manifest, command, env, action) {
4761
5023
  throw new Error("recorded Desktop update launch is invalid");
4762
5024
  }
4763
5025
  }
4764
- if (command !== void 0) {
4765
- const supplied = resolveUpdateLaunch(metadata, command, env);
4766
- if (!isDeepStrictEqual2(supplied, recorded)) {
4767
- throw new Error("--command does not match the recorded update request");
5026
+ if (options.command !== void 0 || options.mcpUrl !== void 0 || options.proxyCommand !== void 0 || options.proxyArgs?.length) {
5027
+ const supplied = resolveUpdateTarget(metadata, options, env);
5028
+ if (!isDeepStrictEqual2(supplied.launch, recorded)) {
5029
+ throw new Error("target options do not match the recorded update request");
4768
5030
  }
4769
5031
  }
4770
- return updateMetadata(metadata, recorded);
5032
+ return updateMetadata(metadata, recorded, targetFromRecordedLaunch(metadata, recorded));
4771
5033
  }
4772
5034
  function planResult(action, applied, metadata, steps, env, operationId) {
4773
5035
  return {
@@ -4796,7 +5058,10 @@ function maintainManagedClient(options) {
4796
5058
  const requestedAction = options.action;
4797
5059
  if (requestedAction === "uninstall") assertUninstallPathIsolation(metadata, env);
4798
5060
  const manifest = options.resume ? readClientOperation(options.resume, env) : void 0;
4799
- const target = manifest ? resumeAction(metadata, manifest, options.command, env, requestedAction) : requestedAction === "update" ? updateMetadata(metadata, resolveUpdateLaunch(metadata, options.command, env)) : metadata;
5061
+ const target = manifest ? resumeAction(metadata, manifest, options, env, requestedAction) : requestedAction === "update" ? (() => {
5062
+ const resolved = resolveUpdateTarget(metadata, options, env);
5063
+ return updateMetadata(metadata, resolved.launch, resolved.target);
5064
+ })() : metadata;
4800
5065
  const steps = options.resume && options.apply ? [] : requestedAction === "uninstall" ? buildUninstallPlan(metadata, execute, env) : buildPlan(metadata, target, execute, env);
4801
5066
  if (!options.apply) {
4802
5067
  if (options.resume || options.recoverLock) throw new Error("--resume and --recover-lock require --apply");
@@ -5012,7 +5277,7 @@ function runManagedClientRollback(options, resumeOperationId) {
5012
5277
  throw new Error("rollback request no longer matches its committed source");
5013
5278
  }
5014
5279
  const metadataStepApplied = rollbackMetadataReachedAfterState(reverse, prior, env);
5015
- const forward = metadataStepApplied ? prior : loadManagedClientMetadata(source.runtime, source.instance, env);
5280
+ const forward = metadataStepApplied ? prior : loadForwardRollbackMetadata(source.runtime, source.instance, env);
5016
5281
  const registrationHasProgress = rollbackRegistrationHasProgress(reverse);
5017
5282
  if (!metadataStepApplied) {
5018
5283
  assertForwardRollbackMetadataState(forward, contract);
@@ -5049,7 +5314,7 @@ function runManagedClientRollback(options, resumeOperationId) {
5049
5314
  throw new Error("rollback source changed while acquiring the client lock");
5050
5315
  }
5051
5316
  const metadataApplied = rollbackMetadataReachedAfterState(current, prior, env);
5052
- const currentForward = metadataApplied ? prior : loadManagedClientMetadata(source.runtime, source.instance, env);
5317
+ const currentForward = metadataApplied ? prior : loadForwardRollbackMetadata(source.runtime, source.instance, env);
5053
5318
  const currentRegistrationHasProgress = rollbackRegistrationHasProgress(current);
5054
5319
  if (!metadataApplied) {
5055
5320
  assertForwardRollbackMetadataState(currentForward, contract);
@@ -8598,6 +8863,9 @@ var SUPPORTED_OPTIONS = /* @__PURE__ */ new Set([
8598
8863
  "scope",
8599
8864
  "backend-config",
8600
8865
  "config-path",
8866
+ "mcp-url",
8867
+ "proxy-command",
8868
+ "proxy-args-json",
8601
8869
  "recipient",
8602
8870
  "retry-base-ms",
8603
8871
  "retry-jitter",
@@ -8770,7 +9038,7 @@ Commands:
8770
9038
  clients inspect <codex|claude-code|claude-desktop> --identity <name> --instance <key> --backend-config <path>
8771
9039
  clients adopt <codex|claude-code|claude-desktop> --identity <name> --instance <key> --backend-config <path> [--apply]
8772
9040
  clients repair <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--apply] [--resume <uuid>] [--recover-lock]
8773
- clients update <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--command <command>] [--apply] [--resume <uuid>] [--recover-lock]
9041
+ clients update <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--command <command> | --mcp-url <loopback-url>] [--proxy-command <command> --proxy-args-json <json-array>] [--apply] [--resume <uuid>] [--recover-lock]
8774
9042
  clients uninstall <codex|claude-code|claude-desktop> --identity <name> --instance <key> [--apply] [--resume <uuid>] [--recover-lock]
8775
9043
  clients rollback <source-operation-id> --identity <name> [--apply] [--recover-lock]
8776
9044
  clients migrate stage <codex|claude-code|claude-desktop> --identity <name> --instance <key> --enrollment-file <path> [--apply] [--recover-lock]
@@ -9086,12 +9354,15 @@ async function runCli(argv = process.argv.slice(2)) {
9086
9354
  "identity",
9087
9355
  "instance",
9088
9356
  "command",
9357
+ "mcp-url",
9358
+ "proxy-command",
9359
+ "proxy-args-json",
9089
9360
  "apply",
9090
9361
  "resume",
9091
9362
  "recover-lock"
9092
9363
  ]), `clients ${action}`);
9093
- if (action !== "update" && options.command !== void 0) {
9094
- throw new Error(`--command is not valid for clients ${action}`);
9364
+ if (action !== "update" && (options.command !== void 0 || options["mcp-url"] !== void 0 || options["proxy-command"] !== void 0 || options["proxy-args-json"] !== void 0)) {
9365
+ throw new Error(`target options are not valid for clients ${action}`);
9095
9366
  }
9096
9367
  if (!boolean(options, "apply") && (options.resume !== void 0 || options["recover-lock"] !== void 0)) {
9097
9368
  throw new Error("--resume and --recover-lock require --apply");
@@ -9102,6 +9373,16 @@ async function runCli(argv = process.argv.slice(2)) {
9102
9373
  instance: one(options, "instance") ?? "",
9103
9374
  command: one(options, "command"),
9104
9375
  apply: boolean(options, "apply"),
9376
+ mcpUrl: one(options, "mcp-url"),
9377
+ proxyCommand: one(options, "proxy-command"),
9378
+ proxyArgs: (() => {
9379
+ const value = json(options, "proxy-args-json");
9380
+ if (value === void 0) return [];
9381
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
9382
+ throw new Error("--proxy-args-json must be a JSON array of strings");
9383
+ }
9384
+ return value;
9385
+ })(),
9105
9386
  resume: one(options, "resume"),
9106
9387
  recoverLock: boolean(options, "recover-lock"),
9107
9388
  env: process.env