// src/mcp-client-manager/mcp-protocol-version.ts
var MCP_PROTOCOL_VERSIONS = [
  "2025-03-26",
  "2025-06-18",
  "2025-11-25",
  "2026-07-28"
];
var STATEFUL_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set([
  "2024-10-07",
  "2024-11-05",
  "2025-03-26",
  "2025-06-18",
  "2025-11-25"
]);
function isKnownProtocolVersion(v) {
  return MCP_PROTOCOL_VERSIONS.includes(v);
}
function isStatelessProtocolVersion(v) {
  return v.length > 0 && !STATEFUL_PROTOCOL_VERSIONS.has(v);
}

// src/host-config/types.ts
var HOST_CONFIG_SCHEMA_VERSION_V2 = 2;
var SEP_1865_PERMISSION_FEATURES = [
  "camera",
  "microphone",
  "geolocation",
  "clipboard-write"
];

// src/host-config/canonicalize.ts
var OPENAI_APPS_CAPABILITY_KEYS = [
  "callTool",
  "sendFollowUpMessage",
  "setWidgetState",
  "requestDisplayMode",
  "notifyIntrinsicHeight",
  "openExternal",
  "setOpenInAppUrl",
  "requestModal",
  "uploadFile",
  "selectFiles",
  "getFileDownloadUrl",
  "requestCheckout",
  "requestClose"
];
var OPENAI_APPS_CAPABILITY_KEY_SET = new Set(
  OPENAI_APPS_CAPABILITY_KEYS
);
var OPENAI_APPS_REQUEST_DISPLAY_MODE_VALUES = [
  "all",
  "fullscreen-only",
  "none"
];
var MCP_APPS_CAPABILITY_KEYS = [
  "availableDisplayModes",
  "toolInputPartial",
  "toolCancelled",
  "hostContextChanged",
  "resourceTeardown",
  "toolInfo",
  "openLinks",
  "serverTools",
  "serverResources",
  "logging",
  "updateModelContext",
  "message",
  "sandboxPermissions",
  "cspFrameDomains",
  "cspBaseUriDomains",
  "resourcePrefersBorder",
  "downloadFile",
  "requestTeardown",
  "widgetDisplayModeRequests"
];
var MCP_APPS_CAPABILITY_KEY_SET = new Set(
  MCP_APPS_CAPABILITY_KEYS
);
var MCP_APPS_DISPLAY_MODE_VALUES = ["inline", "fullscreen", "pip"];
var MCP_APPS_WIDGET_DISPLAY_MODE_REQUEST_VALUES = [
  "accept",
  "user-initiated-only",
  "decline"
];
var MCP_APPS_DISPLAY_MODE_VALUE_SET = new Set(
  MCP_APPS_DISPLAY_MODE_VALUES
);
function sortStringKeys(input) {
  const out = {};
  for (const k of Object.keys(input).sort()) out[k] = input[k];
  return out;
}
function deepSortStringKeys(input) {
  if (Array.isArray(input)) {
    return input.map((v) => deepSortStringKeys(v));
  }
  if (input && typeof input === "object") {
    const out = {};
    const src = input;
    for (const k of Object.keys(src).sort()) {
      out[k] = deepSortStringKeys(src[k]);
    }
    return out;
  }
  return input;
}
function sortUniqueServerIds(ids) {
  return Array.from(new Set(ids ?? [])).sort();
}
function isPlainObject(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}
function canonicalizeCspDomainList(value) {
  if (value === void 0) return void 0;
  if (!Array.isArray(value)) {
    throw new Error(
      "hostConfigV2: mcpProfile CSP domain list must be a string[]"
    );
  }
  const seen = /* @__PURE__ */ new Set();
  for (const entry of value) {
    if (typeof entry !== "string") {
      throw new Error(
        "hostConfigV2: mcpProfile CSP domain list entries must be strings"
      );
    }
    const trimmed = entry.trim();
    if (trimmed === "") continue;
    seen.add(trimmed);
  }
  return Array.from(seen).sort();
}
function canonicalizeCspDomainSet(value) {
  if (value === void 0) return void 0;
  if (!isPlainObject(value)) {
    throw new Error(
      "hostConfigV2: mcpProfile CSP restrictTo must be a plain object"
    );
  }
  const out = {};
  const src = value;
  for (const key of [
    "connectDomains",
    "resourceDomains",
    "frameDomains",
    "baseUriDomains"
  ]) {
    const canonical = canonicalizeCspDomainList(src[key]);
    if (canonical !== void 0) out[key] = canonical;
  }
  for (const key of Object.keys(src).sort()) {
    if (key === "connectDomains" || key === "resourceDomains" || key === "frameDomains" || key === "baseUriDomains") {
      continue;
    }
    out[key] = deepSortStringKeys(src[key]);
  }
  const sorted = {};
  for (const key of Object.keys(out).sort()) {
    sorted[key] = out[key];
  }
  return sorted;
}
function canonicalizeAllowFeatures(value) {
  if (value === void 0) return void 0;
  if (!isPlainObject(value)) {
    throw new Error(
      "hostConfigV2: mcpProfile.apps.sandbox.allowFeatures must be a plain object"
    );
  }
  const dropped = new Set(SEP_1865_PERMISSION_FEATURES);
  const out = {};
  for (const k of Object.keys(value).sort()) {
    if (!/^[a-z][a-z0-9-]*$/.test(k)) {
      throw new Error(
        `hostConfigV2: mcpProfile.apps.sandbox.allowFeatures key "${k}" must be a lowercase kebab-case Permissions Policy token (^[a-z][a-z0-9-]*$)`
      );
    }
    if (dropped.has(k)) continue;
    const v = value[k];
    if (typeof v !== "string") {
      throw new Error(
        `hostConfigV2: mcpProfile.apps.sandbox.allowFeatures.${k} must be a string`
      );
    }
    if (/[;,]/.test(v)) {
      throw new Error(
        `hostConfigV2: mcpProfile.apps.sandbox.allowFeatures.${k} must not contain ';' or ',' (Permissions Policy directive separators)`
      );
    }
    out[k] = v;
  }
  return out;
}
function canonicalizeCspDirectives(value) {
  if (value === void 0) return void 0;
  if (!isPlainObject(value)) {
    throw new Error(
      "hostConfigV2: mcpProfile.apps.sandbox.csp.cspDirectives must be a plain object"
    );
  }
  const out = {};
  for (const k of Object.keys(value).sort()) {
    if (!/^[a-z][a-z0-9-]*$/.test(k)) {
      throw new Error(
        `hostConfigV2: mcpProfile.apps.sandbox.csp.cspDirectives key "${k}" must be a lowercase kebab-case CSP directive name (^[a-z][a-z0-9-]*$)`
      );
    }
    const arr = value[k];
    if (!Array.isArray(arr)) {
      throw new Error(
        `hostConfigV2: mcpProfile.apps.sandbox.csp.cspDirectives.${k} must be a string[]`
      );
    }
    const seen = /* @__PURE__ */ new Set();
    for (const entry of arr) {
      if (typeof entry !== "string") {
        throw new Error(
          `hostConfigV2: mcpProfile.apps.sandbox.csp.cspDirectives.${k} entries must be strings`
        );
      }
      const trimmed = entry.trim();
      if (trimmed === "") continue;
      if (/[;,]/.test(trimmed)) {
        throw new Error(
          `hostConfigV2: mcpProfile.apps.sandbox.csp.cspDirectives.${k} entry "${trimmed}" must not contain ';' or ',' (CSP directive separators \u2014 injection guard)`
        );
      }
      seen.add(trimmed);
    }
    out[k] = Array.from(seen).sort();
  }
  return out;
}
function canonicalizeMcpProfile(input) {
  if (input === void 0) return void 0;
  if (!isPlainObject(input)) {
    throw new Error("hostConfigV2: mcpProfile must be a plain object");
  }
  if (input.profileVersion !== 1) {
    throw new Error("hostConfigV2: mcpProfile.profileVersion must be 1");
  }
  const out = { profileVersion: 1 };
  if (input.mcpProtocolVersion !== void 0) {
    if (!isKnownProtocolVersion(input.mcpProtocolVersion)) {
      throw new Error(
        `hostConfigV2: mcpProfile.mcpProtocolVersion must be one of ${MCP_PROTOCOL_VERSIONS.join(", ")} (got "${String(input.mcpProtocolVersion)}")`
      );
    }
    out.mcpProtocolVersion = input.mcpProtocolVersion;
  }
  if (input.initialize !== void 0) {
    if (!isPlainObject(input.initialize)) {
      throw new Error(
        "hostConfigV2: mcpProfile.initialize must be a plain object"
      );
    }
    const initOut = {};
    if (input.initialize.supportedProtocolVersions !== void 0) {
      const versions = input.initialize.supportedProtocolVersions;
      if (!Array.isArray(versions)) {
        throw new Error(
          "hostConfigV2: mcpProfile.initialize.supportedProtocolVersions must be a string[]"
        );
      }
      if (versions.length === 0) {
        throw new Error(
          "hostConfigV2: mcpProfile.initialize.supportedProtocolVersions must be a non-empty array when set (omit the field to use SDK defaults)"
        );
      }
      for (const v of versions) {
        if (typeof v !== "string") {
          throw new Error(
            "hostConfigV2: mcpProfile.initialize.supportedProtocolVersions entries must be strings"
          );
        }
        if (v.trim() === "") {
          throw new Error(
            "hostConfigV2: mcpProfile.initialize.supportedProtocolVersions entries must be non-empty strings"
          );
        }
      }
      initOut.supportedProtocolVersions = [...versions];
    }
    if (input.initialize.clientInfo !== void 0) {
      const ci = input.initialize.clientInfo;
      if (!isPlainObject(ci)) {
        throw new Error(
          "hostConfigV2: mcpProfile.initialize.clientInfo must be a plain object"
        );
      }
      const name = ci.name;
      const version = ci.version;
      if (typeof name !== "string" || name.trim() === "") {
        throw new Error(
          "hostConfigV2: mcpProfile.initialize.clientInfo.name must be a non-empty string"
        );
      }
      if (typeof version !== "string" || version.trim() === "") {
        throw new Error(
          "hostConfigV2: mcpProfile.initialize.clientInfo.version must be a non-empty string"
        );
      }
      initOut.clientInfo = deepSortStringKeys(ci);
    }
    if (Object.keys(initOut).length > 0) {
      const sortedInit = {};
      for (const k of Object.keys(initOut).sort()) {
        sortedInit[k] = initOut[k];
      }
      out.initialize = sortedInit;
    }
  }
  if (out.mcpProtocolVersion !== void 0 && !isStatelessProtocolVersion(out.mcpProtocolVersion)) {
    const advertised = out.initialize?.supportedProtocolVersions;
    if (advertised === void 0) {
      const initBase = out.initialize ?? {};
      const initWithDerived = {
        ...initBase,
        supportedProtocolVersions: [out.mcpProtocolVersion]
      };
      const sortedInit = {};
      for (const k of Object.keys(initWithDerived).sort()) {
        sortedInit[k] = initWithDerived[k];
      }
      out.initialize = sortedInit;
    } else if (!advertised.includes(out.mcpProtocolVersion)) {
      throw new Error(
        `hostConfigV2: ConflictingProtocolVersionPin \u2014 mcpProtocolVersion "${out.mcpProtocolVersion}" is not in initialize.supportedProtocolVersions [${advertised.join(", ")}]. Either omit one or align them.`
      );
    }
  }
  if (input.apps !== void 0) {
    if (!isPlainObject(input.apps)) {
      throw new Error("hostConfigV2: mcpProfile.apps must be a plain object");
    }
    const appsOut = {};
    if (input.apps.sandbox !== void 0) {
      if (!isPlainObject(input.apps.sandbox)) {
        throw new Error(
          "hostConfigV2: mcpProfile.apps.sandbox must be a plain object"
        );
      }
      const sandboxIn = input.apps.sandbox;
      const sandboxOut = {};
      if (sandboxIn.csp !== void 0) {
        if (!isPlainObject(sandboxIn.csp)) {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.sandbox.csp must be a plain object"
          );
        }
        const cspOut = {};
        if (sandboxIn.csp.mode !== void 0) {
          if (sandboxIn.csp.mode !== "host-default" && sandboxIn.csp.mode !== "declared" && sandboxIn.csp.mode !== "relaxed") {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.sandbox.csp.mode must be 'host-default' | 'declared' | 'relaxed'"
            );
          }
          cspOut.mode = sandboxIn.csp.mode;
        }
        const restrictTo = canonicalizeCspDomainSet(sandboxIn.csp.restrictTo);
        if (restrictTo !== void 0) cspOut.restrictTo = restrictTo;
        const cspDirectives = canonicalizeCspDirectives(
          sandboxIn.csp.cspDirectives
        );
        if (cspDirectives !== void 0)
          cspOut.cspDirectives = cspDirectives;
        if (sandboxIn.csp.extensions !== void 0) {
          if (!isPlainObject(sandboxIn.csp.extensions)) {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.sandbox.csp.extensions must be a plain object"
            );
          }
          cspOut.extensions = deepSortStringKeys(sandboxIn.csp.extensions);
        }
        const sortedCsp = {};
        for (const k of Object.keys(cspOut).sort()) {
          sortedCsp[k] = cspOut[k];
        }
        sandboxOut.csp = sortedCsp;
      }
      if (sandboxIn.permissions !== void 0) {
        if (!isPlainObject(sandboxIn.permissions)) {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.sandbox.permissions must be a plain object"
          );
        }
        const permsIn = sandboxIn.permissions;
        const permsOut = {};
        if (permsIn.mode !== void 0) {
          if (permsIn.mode !== "resource-declared" && permsIn.mode !== "deny-all" && permsIn.mode !== "custom") {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.sandbox.permissions.mode must be 'resource-declared' | 'deny-all' | 'custom'"
            );
          }
          permsOut.mode = permsIn.mode;
        }
        if (permsIn.allow !== void 0) {
          if (!isPlainObject(permsIn.allow)) {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.sandbox.permissions.allow must be a plain object"
            );
          }
          const allowOut = {};
          for (const k of Object.keys(permsIn.allow).sort()) {
            const v = permsIn.allow[k];
            if (typeof v !== "boolean") {
              throw new Error(
                `hostConfigV2: mcpProfile.apps.sandbox.permissions.allow.${k} must be a boolean`
              );
            }
            allowOut[k] = v;
          }
          permsOut.allow = allowOut;
        }
        if (permsIn.extensions !== void 0) {
          if (!isPlainObject(permsIn.extensions)) {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.sandbox.permissions.extensions must be a plain object"
            );
          }
          permsOut.extensions = deepSortStringKeys(permsIn.extensions);
        }
        const sortedPerms = {};
        for (const k of Object.keys(permsOut).sort()) {
          sortedPerms[k] = permsOut[k];
        }
        sandboxOut.permissions = sortedPerms;
      }
      if (sandboxIn.sandboxAttrs !== void 0) {
        const sandboxAttrs = canonicalizeCspDomainList(
          sandboxIn.sandboxAttrs
        );
        if (sandboxAttrs !== void 0) {
          sandboxOut.sandboxAttrs = sandboxAttrs;
        }
      }
      if (sandboxIn.allowFeatures !== void 0) {
        const allowFeatures = canonicalizeAllowFeatures(
          sandboxIn.allowFeatures
        );
        if (allowFeatures !== void 0) {
          sandboxOut.allowFeatures = allowFeatures;
        }
      }
      if (Object.keys(sandboxOut).length > 0) {
        const sortedSandbox = {};
        for (const k of Object.keys(sandboxOut).sort()) {
          sortedSandbox[k] = sandboxOut[k];
        }
        appsOut.sandbox = sortedSandbox;
      }
    }
    if (input.apps.uiInitialize !== void 0) {
      if (!isPlainObject(input.apps.uiInitialize)) {
        throw new Error(
          "hostConfigV2: mcpProfile.apps.uiInitialize must be a plain object"
        );
      }
      const uiInitOut = {};
      if (input.apps.uiInitialize.hostInfo !== void 0) {
        const hi = input.apps.uiInitialize.hostInfo;
        if (!isPlainObject(hi)) {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.uiInitialize.hostInfo must be a plain object"
          );
        }
        const name = hi.name;
        if (typeof name !== "string" || name.trim() === "") {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.uiInitialize.hostInfo.name must be a non-empty string"
          );
        }
        const version = hi.version;
        if (typeof version !== "string" || version.trim() === "") {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.uiInitialize.hostInfo.version must be a non-empty string"
          );
        }
        uiInitOut.hostInfo = deepSortStringKeys(hi);
      }
      if (Object.keys(uiInitOut).length > 0) {
        const sortedUiInit = {};
        for (const k of Object.keys(uiInitOut).sort()) {
          sortedUiInit[k] = uiInitOut[k];
        }
        appsOut.uiInitialize = sortedUiInit;
      }
    }
    if (input.apps.compatRuntime !== void 0) {
      const compatRuntimeIn = input.apps.compatRuntime;
      if (!isPlainObject(compatRuntimeIn)) {
        throw new Error(
          "hostConfigV2: mcpProfile.apps.compatRuntime must be a plain object"
        );
      }
      const compatRuntimeOut = {};
      const compatRecord = compatRuntimeIn;
      const openaiApps = compatRecord.openaiApps;
      if (openaiApps !== void 0) {
        if (typeof openaiApps !== "boolean") {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.compatRuntime.openaiApps must be a boolean"
          );
        }
        compatRuntimeOut.openaiApps = openaiApps;
      }
      const openaiAppsOverridesIn = compatRecord.openaiAppsOverrides;
      if (openaiAppsOverridesIn !== void 0) {
        if (!isPlainObject(openaiAppsOverridesIn)) {
          throw new Error(
            "hostConfigV2: mcpProfile.apps.compatRuntime.openaiAppsOverrides must be a plain object"
          );
        }
        const overridesOut = {};
        for (const [key, value] of Object.entries(openaiAppsOverridesIn)) {
          if (!OPENAI_APPS_CAPABILITY_KEY_SET.has(key)) {
            throw new Error(
              `hostConfigV2: mcpProfile.apps.compatRuntime.openaiAppsOverrides has unknown key "${key}"`
            );
          }
          if (key === "requestDisplayMode") {
            if (typeof value !== "string" || !OPENAI_APPS_REQUEST_DISPLAY_MODE_VALUES.includes(value)) {
              throw new Error(
                'hostConfigV2: mcpProfile.apps.compatRuntime.openaiAppsOverrides.requestDisplayMode must be "all" | "fullscreen-only" | "none"'
              );
            }
            overridesOut.requestDisplayMode = value;
          } else {
            if (typeof value !== "boolean") {
              throw new Error(
                `hostConfigV2: mcpProfile.apps.compatRuntime.openaiAppsOverrides.${key} must be a boolean`
              );
            }
            overridesOut[key] = value;
          }
        }
        if (Object.keys(overridesOut).length > 0) {
          const sortedOverrides = {};
          for (const k of Object.keys(overridesOut).sort()) {
            sortedOverrides[k] = overridesOut[k];
          }
          compatRuntimeOut.openaiAppsOverrides = sortedOverrides;
        }
      }
      if (Object.keys(compatRuntimeOut).length > 0) {
        const sortedCompat = {};
        for (const k of Object.keys(compatRuntimeOut).sort()) {
          sortedCompat[k] = compatRuntimeOut[k];
        }
        appsOut.compatRuntime = sortedCompat;
      }
    }
    if (input.apps.mcpAppsOverrides !== void 0) {
      const mcpAppsOverridesIn = input.apps.mcpAppsOverrides;
      if (!isPlainObject(mcpAppsOverridesIn)) {
        throw new Error(
          "hostConfigV2: mcpProfile.apps.mcpAppsOverrides must be a plain object"
        );
      }
      const mcpAppsOverridesOut = {};
      for (const [key, value] of Object.entries(mcpAppsOverridesIn)) {
        if (!MCP_APPS_CAPABILITY_KEY_SET.has(key)) {
          throw new Error(
            `hostConfigV2: mcpProfile.apps.mcpAppsOverrides has unknown key "${key}"`
          );
        }
        if (key === "availableDisplayModes") {
          if (!Array.isArray(value)) {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.mcpAppsOverrides.availableDisplayModes must be an array"
            );
          }
          if (value.length === 0) {
            throw new Error(
              "hostConfigV2: mcpProfile.apps.mcpAppsOverrides.availableDisplayModes must contain at least one mode"
            );
          }
          const seen = /* @__PURE__ */ new Set();
          for (const entry of value) {
            if (typeof entry !== "string" || !MCP_APPS_DISPLAY_MODE_VALUE_SET.has(entry)) {
              throw new Error(
                'hostConfigV2: mcpProfile.apps.mcpAppsOverrides.availableDisplayModes entries must be "inline" | "fullscreen" | "pip"'
              );
            }
            seen.add(entry);
          }
          mcpAppsOverridesOut.availableDisplayModes = MCP_APPS_DISPLAY_MODE_VALUES.filter(
            (m) => seen.has(m)
          );
        } else if (key === "widgetDisplayModeRequests") {
          if (typeof value !== "string" || !MCP_APPS_WIDGET_DISPLAY_MODE_REQUEST_VALUES.includes(value)) {
            throw new Error(
              'hostConfigV2: mcpProfile.apps.mcpAppsOverrides.widgetDisplayModeRequests must be "accept" | "user-initiated-only" | "decline"'
            );
          }
          mcpAppsOverridesOut.widgetDisplayModeRequests = value;
        } else {
          if (typeof value !== "boolean") {
            throw new Error(
              `hostConfigV2: mcpProfile.apps.mcpAppsOverrides.${key} must be a boolean`
            );
          }
          mcpAppsOverridesOut[key] = value;
        }
      }
      if (Object.keys(mcpAppsOverridesOut).length > 0) {
        const sortedMcpApps = {};
        for (const k of Object.keys(mcpAppsOverridesOut).sort()) {
          sortedMcpApps[k] = mcpAppsOverridesOut[k];
        }
        appsOut.mcpAppsOverrides = sortedMcpApps;
      }
    }
    if (Object.keys(appsOut).length > 0) {
      const sortedApps = {};
      for (const k of Object.keys(appsOut).sort()) {
        sortedApps[k] = appsOut[k];
      }
      out.apps = sortedApps;
    }
  }
  if (input.extensions !== void 0) {
    if (!isPlainObject(input.extensions)) {
      throw new Error(
        "hostConfigV2: mcpProfile.extensions must be a plain object"
      );
    }
    out.extensions = deepSortStringKeys(input.extensions);
  }
  const sorted = { profileVersion: 1 };
  for (const k of Object.keys(out).sort()) {
    if (k === "profileVersion") continue;
    sorted[k] = out[k];
  }
  return sorted;
}
function canonicalizeServerConnectionOverrides(serverIds, optionalServerIds, overrides) {
  if (!overrides || Object.keys(overrides).length === 0) return void 0;
  const allowedIds = /* @__PURE__ */ new Set([...serverIds, ...optionalServerIds]);
  const result = {};
  for (const [serverId, entry] of Object.entries(overrides)) {
    if (!allowedIds.has(serverId)) {
      throw new Error(
        `hostConfigV2: serverConnectionOverrides key "${serverId}" is not in serverIds or optionalServerIds`
      );
    }
    if (!entry) continue;
    const normalizedHeaders = entry.headersOverride && Object.keys(entry.headersOverride).length > 0 ? sortStringKeys(entry.headersOverride) : void 0;
    let mcpProtocolVersionOverride;
    if (entry.mcpProtocolVersionOverride !== void 0) {
      if (!isKnownProtocolVersion(entry.mcpProtocolVersionOverride)) {
        throw new Error(
          `hostConfigV2: serverConnectionOverrides["${serverId}"].mcpProtocolVersionOverride must be one of ${MCP_PROTOCOL_VERSIONS.join(", ")} (got "${String(entry.mcpProtocolVersionOverride)}")`
        );
      }
      mcpProtocolVersionOverride = entry.mcpProtocolVersionOverride;
    }
    if (entry.requestTimeoutOverride !== void 0 && !Number.isFinite(entry.requestTimeoutOverride)) {
      throw new Error(
        `hostConfigV2: serverConnectionOverrides["${serverId}"].requestTimeoutOverride must be finite`
      );
    }
    const hasContent = normalizedHeaders !== void 0 || entry.requestTimeoutOverride !== void 0 || mcpProtocolVersionOverride !== void 0;
    if (hasContent) {
      const entryOut = {
        ...normalizedHeaders !== void 0 ? { headersOverride: normalizedHeaders } : {},
        ...entry.requestTimeoutOverride !== void 0 ? { requestTimeoutOverride: entry.requestTimeoutOverride } : {},
        ...mcpProtocolVersionOverride !== void 0 ? { mcpProtocolVersionOverride } : {}
      };
      result[serverId] = sortStringKeys(entryOut);
    }
  }
  if (Object.keys(result).length === 0) return void 0;
  return sortStringKeys(
    result
  );
}
function canonicalizeHostConfigV2(input) {
  if (!Number.isFinite(input.temperature)) {
    throw new Error("hostConfigV2: temperature must be finite");
  }
  if (!Number.isFinite(input.connectionDefaults.requestTimeout)) {
    throw new Error(
      "hostConfigV2: connectionDefaults.requestTimeout must be finite"
    );
  }
  if (input.hostCapabilitiesOverride !== void 0 && (input.hostCapabilitiesOverride === null || typeof input.hostCapabilitiesOverride !== "object" || Array.isArray(input.hostCapabilitiesOverride))) {
    throw new Error(
      "hostConfigV2: hostCapabilitiesOverride must be a plain object"
    );
  }
  if (input.chatUiOverride !== void 0 && (input.chatUiOverride === null || typeof input.chatUiOverride !== "object" || Array.isArray(input.chatUiOverride))) {
    throw new Error("hostConfigV2: chatUiOverride must be a plain object");
  }
  const serverIds = sortUniqueServerIds(input.serverIds);
  const optionalServerIds = sortUniqueServerIds(input.optionalServerIds);
  return {
    schemaVersion: HOST_CONFIG_SCHEMA_VERSION_V2,
    hostStyle: input.hostStyle,
    modelId: input.modelId,
    systemPrompt: input.systemPrompt,
    temperature: input.temperature,
    requireToolApproval: input.requireToolApproval,
    // Preserve undefined-vs-set: absent hashes byte-identically to a
    // pre-feature row; explicit `false` writes a key and hashes distinctly.
    progressiveToolDiscovery: input.progressiveToolDiscovery,
    respectToolVisibility: input.respectToolVisibility,
    // Normalize undefined → [] and dedupe before sort so canonical/hash output
    // is identical for semantically equivalent server lists.
    serverIds,
    optionalServerIds,
    connectionDefaults: {
      headers: sortStringKeys(input.connectionDefaults.headers),
      requestTimeout: input.connectionDefaults.requestTimeout
    },
    clientCapabilities: sortStringKeys(input.clientCapabilities ?? {}),
    hostContext: sortStringKeys(input.hostContext ?? {}),
    // Preserve undefined (omitted → dedupes with preset) vs {} (explicit empty
    // → hashes distinctly).
    hostCapabilitiesOverride: input.hostCapabilitiesOverride === void 0 ? void 0 : deepSortStringKeys(input.hostCapabilitiesOverride),
    chatUiOverride: input.chatUiOverride === void 0 ? void 0 : deepSortStringKeys(input.chatUiOverride),
    mcpProfile: canonicalizeMcpProfile(input.mcpProfile),
    serverConnectionOverrides: canonicalizeServerConnectionOverrides(
      serverIds,
      optionalServerIds,
      input.serverConnectionOverrides
    )
  };
}

// src/host-config/host.ts
var DEFAULT_TEMPERATURE = 0.7;
var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
function hostMcpToProfile(mcp) {
  const profile = { profileVersion: 1 };
  if (mcp.protocolVersion !== void 0) {
    profile.mcpProtocolVersion = mcp.protocolVersion;
  }
  if (mcp.initialize !== void 0) profile.initialize = mcp.initialize;
  if (mcp.apps !== void 0) profile.apps = mcp.apps;
  if (mcp.extensions !== void 0) profile.extensions = mcp.extensions;
  return profile;
}
function isEmptyHostMcp(mcp) {
  if (mcp === void 0) return true;
  return mcp.protocolVersion === void 0 && mcp.initialize === void 0 && mcp.apps === void 0 && mcp.extensions === void 0;
}
function serverOverrideToInternal(override) {
  const out = {};
  if (override.headers !== void 0) out.headersOverride = override.headers;
  if (override.requestTimeout !== void 0) {
    out.requestTimeoutOverride = override.requestTimeout;
  }
  if (override.protocolVersion !== void 0) {
    out.mcpProtocolVersionOverride = override.protocolVersion;
  }
  return out;
}
function serverOverridesToInternal(overrides) {
  if (Object.keys(overrides).length === 0) return void 0;
  const out = {};
  for (const [id, override] of Object.entries(overrides)) {
    out[id] = serverOverrideToInternal(override);
  }
  return out;
}
function profileToHostMcp(profile) {
  const mcp = {};
  if (profile.mcpProtocolVersion !== void 0) {
    mcp.protocolVersion = profile.mcpProtocolVersion;
  }
  if (profile.initialize !== void 0) mcp.initialize = profile.initialize;
  if (profile.apps !== void 0) mcp.apps = profile.apps;
  if (profile.extensions !== void 0) mcp.extensions = profile.extensions;
  return mcp;
}
function serverOverridesToPublic(overrides) {
  const out = {};
  for (const [id, ov] of Object.entries(overrides)) {
    const pub = {};
    if (ov.headersOverride !== void 0) pub.headers = ov.headersOverride;
    if (ov.requestTimeoutOverride !== void 0) {
      pub.requestTimeout = ov.requestTimeoutOverride;
    }
    if (ov.mcpProtocolVersionOverride !== void 0) {
      pub.protocolVersion = ov.mcpProtocolVersionOverride;
    }
    out[id] = pub;
  }
  return out;
}
function canonicalToPublic(c) {
  const out = {
    style: c.hostStyle,
    model: c.modelId,
    systemPrompt: c.systemPrompt,
    temperature: c.temperature,
    requireToolApproval: c.requireToolApproval,
    servers: c.serverIds,
    optionalServers: c.optionalServerIds,
    connectionDefaults: c.connectionDefaults,
    clientCapabilities: c.clientCapabilities,
    hostContext: c.hostContext
  };
  if (c.progressiveToolDiscovery !== void 0) {
    out.progressiveToolDiscovery = c.progressiveToolDiscovery;
  }
  if (c.respectToolVisibility !== void 0) {
    out.respectToolVisibility = c.respectToolVisibility;
  }
  if (c.hostCapabilitiesOverride !== void 0) {
    out.hostCapabilitiesOverride = c.hostCapabilitiesOverride;
  }
  if (c.chatUiOverride !== void 0) out.chatUiOverride = c.chatUiOverride;
  if (c.mcpProfile !== void 0) out.mcp = profileToHostMcp(c.mcpProfile);
  if (c.serverConnectionOverrides !== void 0) {
    out.serverOverrides = serverOverridesToPublic(c.serverConnectionOverrides);
  }
  return out;
}
function pushUnique(list, id) {
  if (!list.includes(id)) list.push(id);
}
function removeFrom(list, id) {
  const i = list.indexOf(id);
  if (i >= 0) list.splice(i, 1);
}
function dedup(arr) {
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const v of arr) {
    if (!seen.has(v)) {
      seen.add(v);
      out.push(v);
    }
  }
  return out;
}
var Host = class {
  /**
   * Host style id (e.g. "mcpjam", "claude", "chatgpt"). **Required** at
   * `toJSON()` time — there is no SDK default, so an external agent author
   * who forgets to set it gets a loud error rather than silently inheriting
   * MCPJam product chrome on a Claude-style flow.
   *
   * Note: `style` is a **product knob**, not part of SEP-1865 / the MCP base
   * spec. It selects which host-style preset (chrome, default capabilities,
   * compat-runtime shims) the inspector applies.
   */
  style;
  /**
   * LLM model id, e.g. `"anthropic/claude-sonnet-4-6"`. **Required** at
   * `toJSON()` time — no SDK default.
   */
  model;
  systemPrompt;
  temperature;
  requireToolApproval;
  /** Opt into progressive MCP tool discovery (search/load meta-tools). */
  progressiveToolDiscovery;
  /**
   * SEP-1865 `_meta.ui.visibility` filtering. `undefined` → spec default
   * (filter); explicit `false` → show every tool (for hosts that don't
   * implement visibility).
   */
  respectToolVisibility;
  /** Required servers. Mutable — `addServer`/`removeServer` are sugar. */
  servers;
  /** Optional (auto-connect-if-available) servers. */
  optionalServers;
  connectionDefaults;
  /**
   * MCP `ClientCapabilities` the inspector advertises in `initialize`.
   * Untyped (`Record<string, unknown>`) so future spec additions and host
   * extensions (SEP-1724) can be added without an SDK release. The MCP Apps
   * extension lives at `clientCapabilities.extensions["io.modelcontextprotocol/ui"]`.
   */
  clientCapabilities;
  /** SEP-1865 `HostContext` (theme, displayMode, …) advertised via `ui/initialize`. */
  hostContext;
  /**
   * Override the SEP-1865 MCP-Apps `hostCapabilities` blob the inspector
   * advertises in `ui/initialize`.
   *
   * Semantics: `undefined` = "use the host-style preset" (the inspector
   * picks a sensible default per style). `{}` = "advertise nothing"
   * (distinct from `undefined`; hashes distinctly). Use this to *cap* what
   * a host advertises — e.g. force `serverTools: undefined` to block widget
   * → server tool proxying for a hardened host.
   */
  hostCapabilitiesOverride;
  /** Override the chat-UI surface (logo, fonts, …). `undefined` vs `{}` semantics match `hostCapabilitiesOverride`. */
  chatUiOverride;
  /**
   * The host's MCP settings.
   *
   * Spec-aligned shape (`protocolVersion`, `initialize`, `apps`, `extensions`)
   * is **mutable in place** — assign or mutate any leaf directly:
   *
   * ```ts
   * host.mcp.protocolVersion = "2025-11-25";
   * host.mcp.initialize = { clientInfo: { name: "my-app", version: "1.0" } };
   * host.mcp.apps = { sandbox: { csp: { mode: "declared" } } };
   * ```
   *
   * The SEP-1865 sandbox knobs at `mcp.apps.sandbox.csp` / `.permissions` are
   * **host enforcement caps**, not capability grants to the widget. The
   * widget *declares* what it needs via its resource metadata; the host MAY
   * further restrict but MUST NOT loosen (`restrictTo` intersects the
   * declared set; `mode: "declared"` honors the declared set as-is).
   *
   * A "freshly constructed, untouched" `host.mcp` (all fields undefined)
   * collapses to no `mcpProfile` in canonical, so it hashes identically to
   * `host.mcp = undefined`.
   */
  mcp;
  /** Per-server connection overrides keyed by server id. */
  serverOverrides;
  constructor(init = {}) {
    const cfg = structuredClone(init);
    this.style = cfg.style ?? "";
    this.model = cfg.model ?? "";
    this.systemPrompt = cfg.systemPrompt ?? "";
    this.temperature = cfg.temperature ?? DEFAULT_TEMPERATURE;
    this.requireToolApproval = cfg.requireToolApproval ?? false;
    this.progressiveToolDiscovery = cfg.progressiveToolDiscovery;
    this.respectToolVisibility = cfg.respectToolVisibility;
    this.servers = cfg.servers ? dedup(cfg.servers) : [];
    this.optionalServers = cfg.optionalServers ? dedup(cfg.optionalServers) : [];
    this.connectionDefaults = {
      headers: cfg.connectionDefaults?.headers ?? {},
      requestTimeout: cfg.connectionDefaults?.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS
    };
    this.clientCapabilities = cfg.clientCapabilities ?? {};
    this.hostContext = cfg.hostContext ?? {};
    this.hostCapabilitiesOverride = cfg.hostCapabilitiesOverride;
    this.chatUiOverride = cfg.chatUiOverride;
    this.mcp = cfg.mcp ?? {};
    this.serverOverrides = cfg.serverOverrides ?? {};
  }
  // ── Setters kept as fluent-chain conveniences. Direct property assignment
  //    is equivalent (`host.style = "..."`); both are supported. ────────────
  setStyle(style) {
    this.style = style;
    return this;
  }
  setModel(model) {
    this.model = model;
    return this;
  }
  setSystemPrompt(systemPrompt) {
    this.systemPrompt = systemPrompt;
    return this;
  }
  setTemperature(temperature) {
    this.temperature = temperature;
    return this;
  }
  setRequireToolApproval(require2 = true) {
    this.requireToolApproval = require2;
    return this;
  }
  setProgressiveToolDiscovery(enabled) {
    this.progressiveToolDiscovery = enabled;
    return this;
  }
  setRespectToolVisibility(respect) {
    this.respectToolVisibility = respect;
    return this;
  }
  // ── Server list mutations (deduped). ──────────────────────────────────────
  /** Append a required server; no-op if `id` is already in the list. */
  addServer(id) {
    pushUnique(this.servers, id);
    return this;
  }
  /** Remove a required server; no-op if absent. */
  removeServer(id) {
    removeFrom(this.servers, id);
    return this;
  }
  /** Append an optional (auto-connect-if-available) server; deduped. */
  addOptionalServer(id) {
    pushUnique(this.optionalServers, id);
    return this;
  }
  removeOptionalServer(id) {
    removeFrom(this.optionalServers, id);
    return this;
  }
  // ── Per-server connection overrides. ──────────────────────────────────────
  /**
   * Replace the per-server override for `id`. Passing an empty `{}` is
   * preserved here but stripped from the canonical output (an override with
   * no fields adds no information).
   */
  setServerOverride(id, override) {
    this.serverOverrides[id] = structuredClone(override);
    return this;
  }
  removeServerOverride(id) {
    delete this.serverOverrides[id];
    return this;
  }
  // ── MCP block. ────────────────────────────────────────────────────────────
  /**
   * Reset `mcp` to an empty object — equivalent to "use SDK defaults / no
   * host-level MCP profile." Collapses to no `mcpProfile` in canonical.
   */
  clearMcp() {
    this.mcp = {};
    return this;
  }
  // ── Output. ───────────────────────────────────────────────────────────────
  /**
   * Throw a clear error if required fields (`style`, `model`) are still empty.
   * Called from `toJSON()` so the failure lands at the moment of use, not
   * deep inside the canonicalizer with a less obvious message.
   */
  requireConfigured() {
    if (!this.style) {
      throw new Error(
        'Host requires a `style` (e.g. "mcpjam", "claude", "chatgpt"). Pass it to the constructor (`new Host({ style: "..." })`) or assign `host.style = "..."`.'
      );
    }
    if (!this.model) {
      throw new Error(
        'Host requires a `model` (e.g. "anthropic/claude-sonnet-4-6"). Pass it to the constructor (`new Host({ model: "..." })`) or assign `host.model = "..."`.'
      );
    }
  }
  /**
   * Build the internal canonicalizer input from the current public-shape
   * properties. Snapshotted (`structuredClone`) so the canonicalizer sees a
   * stable copy even if the caller is mid-mutation.
   */
  toInternalInput() {
    const snap = structuredClone({
      style: this.style,
      model: this.model,
      systemPrompt: this.systemPrompt,
      temperature: this.temperature,
      requireToolApproval: this.requireToolApproval,
      progressiveToolDiscovery: this.progressiveToolDiscovery,
      respectToolVisibility: this.respectToolVisibility,
      servers: this.servers,
      optionalServers: this.optionalServers,
      connectionDefaults: this.connectionDefaults,
      clientCapabilities: this.clientCapabilities,
      hostContext: this.hostContext,
      hostCapabilitiesOverride: this.hostCapabilitiesOverride,
      chatUiOverride: this.chatUiOverride,
      mcp: this.mcp,
      serverOverrides: this.serverOverrides
    });
    const input = {
      hostStyle: snap.style,
      modelId: snap.model,
      systemPrompt: snap.systemPrompt,
      temperature: snap.temperature,
      requireToolApproval: snap.requireToolApproval,
      serverIds: snap.servers,
      optionalServerIds: snap.optionalServers,
      connectionDefaults: snap.connectionDefaults,
      clientCapabilities: snap.clientCapabilities,
      hostContext: snap.hostContext
    };
    if (snap.progressiveToolDiscovery !== void 0) {
      input.progressiveToolDiscovery = snap.progressiveToolDiscovery;
    }
    if (snap.respectToolVisibility !== void 0) {
      input.respectToolVisibility = snap.respectToolVisibility;
    }
    if (snap.hostCapabilitiesOverride !== void 0) {
      input.hostCapabilitiesOverride = snap.hostCapabilitiesOverride;
    }
    if (snap.chatUiOverride !== void 0) {
      input.chatUiOverride = snap.chatUiOverride;
    }
    if (!isEmptyHostMcp(snap.mcp)) {
      input.mcpProfile = hostMcpToProfile(snap.mcp);
    }
    const overrides = serverOverridesToInternal(snap.serverOverrides);
    if (overrides !== void 0) input.serverConnectionOverrides = overrides;
    return input;
  }
  /**
   * Serialize to the normalized public `HostJson` shape (clean MCP
   * vocabulary — `mcp`, `servers`, `style`; no `mcpProfile`/`schemaVersion`).
   * Normalized and round-trippable: `new Host(host.toJSON())` reproduces an
   * equivalent host. Throws if the configuration is invalid (e.g.
   * `style`/`model` not set, a non-finite temperature, or a malformed MCP
   * profile).
   */
  toJSON() {
    this.requireConfigured();
    return canonicalToPublic(canonicalizeHostConfigV2(this.toInternalInput()));
  }
};

export { Host };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map