@get-bb/plugin-sdk 0.4.18 → 0.4.20

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.
@@ -6802,1180 +6802,1190 @@ function resolveAcpPermissionDecision(args) {
6802
6802
 
6803
6803
  // ../provider-bridge-acp/src/session-params.ts
6804
6804
  import path4 from "node:path";
6805
- function sanitizeAcpSkillDescription(description) {
6806
- const sanitized = description.replace(/[\r\n]+/gu, " ").replace(/\s+/gu, " ").replace(/[<>]/gu, "").trim();
6807
- return sanitized.length > 0 ? sanitized : "(description unavailable)";
6808
- }
6809
- function buildAcpSkillsInstructions(skillRoots) {
6810
- if (!skillRoots || skillRoots.length === 0) {
6811
- return void 0;
6805
+
6806
+ // ../provider-bridge-acp/src/bridge/model-catalog.ts
6807
+ var ACP_NATIVE_REASONING_EFFORTS = [
6808
+ {
6809
+ reasoningEffort: "medium",
6810
+ description: "Reasoning effort is managed by the connected ACP agent."
6812
6811
  }
6813
- const skillLines = skillRoots.flatMap((skillRoot) => {
6814
- return skillRoot.skills.map((skill) => {
6815
- const skillFilePath = path4.join(
6816
- skillRoot.skillDirectoryRootPath,
6817
- skill.name,
6818
- "SKILL.md"
6819
- );
6820
- return `- ${skill.name}: ${sanitizeAcpSkillDescription(skill.description)} (SKILL.md: ${skillFilePath})`;
6821
- });
6822
- });
6823
- if (skillLines.length === 0) {
6824
- return void 0;
6812
+ ];
6813
+ var MODEL_LINE_PATTERN = /^(\S+) - (.+)$/;
6814
+ var BARE_PROVIDER_MODEL_LINE_PATTERN = /^\S+\/\S+$/;
6815
+ var BULLETED_MODEL_LINE_PATTERN = /^[*-]\s+(\S+)(?:\s+\([^)]*\))?$/u;
6816
+ var EFFORT_TOKENS = [
6817
+ ["extra-high", "xhigh"],
6818
+ ["medium", "medium"],
6819
+ ["xhigh", "xhigh"],
6820
+ ["high", "high"],
6821
+ ["low", "low"],
6822
+ ["max", "max"],
6823
+ ["none", "none"]
6824
+ ];
6825
+ var FAST_TAIL = "-fast";
6826
+ var THINKING_TOKEN = "thinking";
6827
+ function parseAgentModelLines(stdout) {
6828
+ const models = [];
6829
+ for (const line of stdout.split("\n")) {
6830
+ const trimmed = line.trim();
6831
+ const match = MODEL_LINE_PATTERN.exec(trimmed);
6832
+ if (!match) {
6833
+ const bulletMatch = BULLETED_MODEL_LINE_PATTERN.exec(trimmed);
6834
+ if (bulletMatch) {
6835
+ const [, id2] = bulletMatch;
6836
+ models.push({ id: id2, displayName: id2 });
6837
+ continue;
6838
+ }
6839
+ if (BARE_PROVIDER_MODEL_LINE_PATTERN.test(trimmed)) {
6840
+ models.push({ id: trimmed, displayName: trimmed });
6841
+ }
6842
+ continue;
6843
+ }
6844
+ const [, id, displayName] = match;
6845
+ models.push({ id, displayName });
6825
6846
  }
6826
- return [
6827
- "bb skills are reusable instruction folders. When the current task matches a listed skill description, read that skill's SKILL.md at the absolute path before proceeding; you may read supporting files in the same skill directory that SKILL.md references. If a listed path does not exist, the list is stale and should be ignored.",
6828
- "",
6829
- "Available bb skills:",
6830
- ...skillLines
6831
- ].join("\n");
6847
+ return models;
6832
6848
  }
6833
- function buildAcpSessionInstructions(options) {
6834
- const baseInstructions = options.instructions?.trim();
6835
- const skillsInstructions = buildAcpSkillsInstructions(options.skillRoots);
6836
- const instructions = [baseInstructions, skillsInstructions].filter(
6837
- (value) => value !== void 0 && value.length > 0
6849
+ function findAcpModelConfigOption(configOptions) {
6850
+ const options = configOptions ?? [];
6851
+ return options.find((option) => option.category === "model") ?? options.find((option) => option.id === "model");
6852
+ }
6853
+ function findAcpThoughtLevelConfigOption(configOptions) {
6854
+ return (configOptions ?? []).find(
6855
+ (option) => option.category === "thought_level"
6838
6856
  );
6839
- return instructions.length > 0 ? instructions.join("\n\n") : void 0;
6840
6857
  }
6841
- function launchEnvVars(launchSpec) {
6842
- return Object.keys(launchSpec.env).length > 0 ? { envVars: launchSpec.env } : {};
6858
+ var ACP_NATIVE_REASONING_LEVEL_BY_VALUE = {
6859
+ none: "none",
6860
+ minimal: "low",
6861
+ low: "low",
6862
+ medium: "medium",
6863
+ high: "high",
6864
+ xhigh: "xhigh",
6865
+ ultracode: "ultracode",
6866
+ max: "max",
6867
+ ultra: "ultra"
6868
+ };
6869
+ var ACP_NATIVE_REASONING_VALUE_CANDIDATES_BY_LEVEL = {
6870
+ none: ["none"],
6871
+ low: ["low", "minimal"],
6872
+ medium: ["medium"],
6873
+ high: ["high"],
6874
+ xhigh: ["xhigh"],
6875
+ ultracode: ["ultracode", "xhigh"],
6876
+ max: ["max", "xhigh"],
6877
+ ultra: ["ultra", "max"]
6878
+ };
6879
+ function acpNativeValueToReasoningLevel(value) {
6880
+ return value === void 0 ? void 0 : ACP_NATIVE_REASONING_LEVEL_BY_VALUE[value];
6843
6881
  }
6844
- function buildAcpModelListCommand(launchSpec) {
6845
- if (!launchSpec.modelCli || launchSpec.modelCli.listArgs.length === 0) {
6882
+ function acpNativeReasoningLevelToValue(level, thoughtLevelOption) {
6883
+ const candidateValues = ACP_NATIVE_REASONING_VALUE_CANDIDATES_BY_LEVEL[level];
6884
+ if (candidateValues === void 0) {
6846
6885
  return void 0;
6847
6886
  }
6848
- return {
6849
- command: launchSpec.command,
6850
- args: [...launchSpec.modelCli.listArgs],
6851
- ...launchSpec.cwd !== void 0 ? { cwd: launchSpec.cwd } : {},
6852
- ...launchEnvVars(launchSpec)
6853
- };
6887
+ const values = new Set(
6888
+ (thoughtLevelOption.options ?? []).map((o) => o.value)
6889
+ );
6890
+ return candidateValues.find((value) => values.has(value));
6854
6891
  }
6855
- function buildAcpModelDiscoveryAgentCommand(launchSpec) {
6856
- if (buildAcpModelListCommand(launchSpec) !== void 0) {
6857
- return void 0;
6892
+ function buildAcpNativeReasoningSupport(thoughtLevelOption) {
6893
+ const options = thoughtLevelOption?.options ?? [];
6894
+ const seen = /* @__PURE__ */ new Set();
6895
+ const matchedValueByLevel = /* @__PURE__ */ new Map();
6896
+ const supportedReasoningEfforts = [];
6897
+ for (const option of options) {
6898
+ const level = acpNativeValueToReasoningLevel(option.value);
6899
+ if (level === void 0) {
6900
+ continue;
6901
+ }
6902
+ if (seen.has(level)) {
6903
+ const previousValue = matchedValueByLevel.get(level);
6904
+ if (previousValue !== level && option.value === level) {
6905
+ const effort = supportedReasoningEfforts.find(
6906
+ (candidate) => candidate.reasoningEffort === level
6907
+ );
6908
+ if (effort) {
6909
+ effort.description = option.name ?? option.value;
6910
+ }
6911
+ matchedValueByLevel.set(level, option.value);
6912
+ }
6913
+ continue;
6914
+ }
6915
+ seen.add(level);
6916
+ matchedValueByLevel.set(level, option.value);
6917
+ supportedReasoningEfforts.push({
6918
+ reasoningEffort: level,
6919
+ description: option.name ?? option.value
6920
+ });
6858
6921
  }
6859
- return {
6860
- command: launchSpec.command,
6861
- args: [...launchSpec.args],
6862
- ...launchSpec.cwd !== void 0 ? { cwd: launchSpec.cwd } : {},
6863
- ...launchEnvVars(launchSpec)
6864
- };
6865
- }
6866
- function buildAcpModelListParams(launchSpec, options) {
6867
- const primaryModels = [
6868
- ...options.primaryModels ?? launchSpec?.modelCli?.primaryModels ?? []
6869
- ];
6870
- const reasoningProbePriorityModelIds = [
6871
- ...options.reasoningProbePriorityModelIds
6872
- ];
6873
- if (launchSpec === null) {
6922
+ supportedReasoningEfforts.sort(
6923
+ (a, b) => reasoningLevelValues.indexOf(a.reasoningEffort) - reasoningLevelValues.indexOf(b.reasoningEffort)
6924
+ );
6925
+ if (supportedReasoningEfforts.length === 0) {
6874
6926
  return {
6875
- primaryModels,
6876
- reasoningProbePriorityModelIds,
6877
- parameterizedModelPicker: options.parameterizedModelPicker
6927
+ // An omitted option preserves the legacy agent-managed fallback. A
6928
+ // declared option is authoritative, even when bb cannot map its values.
6929
+ supportedReasoningEfforts: thoughtLevelOption === void 0 ? ACP_NATIVE_REASONING_EFFORTS : [],
6930
+ defaultReasoningEffort: "medium"
6878
6931
  };
6879
6932
  }
6880
- const listCommand = buildAcpModelListCommand(launchSpec);
6881
- const agent = buildAcpModelDiscoveryAgentCommand(launchSpec);
6933
+ const currentLevel = acpNativeValueToReasoningLevel(
6934
+ thoughtLevelOption?.currentValue
6935
+ );
6936
+ const supportedLevels = supportedReasoningEfforts.map(
6937
+ (effort) => effort.reasoningEffort
6938
+ );
6882
6939
  return {
6883
- ...listCommand !== void 0 ? { listCommand } : {},
6884
- ...agent !== void 0 ? { agent } : {},
6885
- primaryModels,
6886
- reasoningProbePriorityModelIds,
6887
- parameterizedModelPicker: options.parameterizedModelPicker,
6888
- ...launchSpec.reasoningCli !== void 0 ? { reasoningCli: launchSpec.reasoningCli } : {},
6889
- ...launchSpec.nativeReasoning !== void 0 ? { nativeReasoning: launchSpec.nativeReasoning } : {}
6940
+ supportedReasoningEfforts,
6941
+ defaultReasoningEffort: currentLevel !== void 0 && supportedLevels.includes(currentLevel) ? currentLevel : supportedReasoningEfforts[0].reasoningEffort
6890
6942
  };
6891
6943
  }
6892
- function buildAcpModelSelectionParam(launchSpec, options, parameterizedModelPicker) {
6893
- const model = options.model;
6894
- const listCommand = buildAcpModelListCommand(launchSpec);
6895
- if (!model || model === ACP_DEFAULT_MODEL_ID) {
6896
- return {};
6944
+ function buildModelCatalogFromConfigOptions(modelOption, reasoningByModel) {
6945
+ const options = modelOption?.options ?? [];
6946
+ if (options.length === 0) {
6947
+ return [];
6897
6948
  }
6898
- if (parameterizedModelPicker || !listCommand || !launchSpec.modelCli?.selectFlag) {
6949
+ const currentValue = modelOption?.currentValue;
6950
+ const models = options.map((option, index) => {
6951
+ const isDefault = currentValue !== void 0 ? option.value === currentValue : index === 0;
6952
+ const reasoning = reasoningByModel?.get(option.value) ?? {
6953
+ supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
6954
+ defaultReasoningEffort: "medium"
6955
+ };
6899
6956
  return {
6900
- modelSelection: {
6901
- modelId: model,
6902
- ...options.reasoningLevel !== void 0 ? { reasoningLevel: options.reasoningLevel } : {},
6903
- ...parameterizedModelPicker && options.serviceTier !== void 0 ? { serviceTier: options.serviceTier } : {}
6904
- }
6957
+ id: option.value,
6958
+ model: option.value,
6959
+ displayName: option.name ?? option.value,
6960
+ description: "",
6961
+ supportedReasoningEfforts: reasoning.supportedReasoningEfforts,
6962
+ defaultReasoningEffort: reasoning.defaultReasoningEffort,
6963
+ isDefault
6905
6964
  };
6906
- }
6907
- return {
6908
- modelSelection: {
6909
- listCommand,
6910
- selectFlag: launchSpec.modelCli.selectFlag,
6911
- model,
6912
- ...options.reasoningLevel !== void 0 ? { reasoningLevel: options.reasoningLevel } : {},
6913
- // Only "fast" changes launch resolution; "default" is the normal id.
6914
- ...options.serviceTier === "fast" ? { serviceTier: options.serviceTier } : {}
6915
- }
6916
- };
6965
+ });
6966
+ return models.some((model) => model.isDefault) ? models : models.map(
6967
+ (model, index) => index === 0 ? { ...model, isDefault: true } : model
6968
+ );
6917
6969
  }
6918
- function buildAcpSessionParams(args) {
6919
- const { options, launchSpec } = args;
6920
- const instructions = buildAcpSessionInstructions(options);
6921
- const cwd = launchSpec.cwd ?? args.cwd;
6922
- const envVars = {
6923
- ...launchSpec.env,
6924
- ...options.envVars ?? {}
6925
- };
6926
- if (options.permissionMode === "auto") {
6927
- throw new Error(
6928
- `Provider "${args.providerLabel}" does not support permission mode "auto".`
6929
- );
6970
+ function buildModelCatalogFromSessionModels(sessionModels) {
6971
+ const availableModels = sessionModels?.availableModels ?? [];
6972
+ if (availableModels.length === 0) {
6973
+ return [];
6930
6974
  }
6931
- return {
6932
- threadId: args.threadId,
6933
- cwd,
6934
- agent: {
6935
- command: launchSpec.command,
6936
- args: [...launchSpec.args]
6937
- },
6938
- ...args.dialectId === void 0 ? {} : { dialectId: args.dialectId },
6939
- ...buildAcpModelSelectionParam(
6940
- launchSpec,
6941
- options,
6942
- args.parameterizedModelPicker
6943
- ),
6944
- parameterizedModelPicker: args.parameterizedModelPicker,
6945
- ...launchSpec.reasoningCli !== void 0 ? { reasoningCli: launchSpec.reasoningCli } : {},
6946
- ...launchSpec.nativeReasoning !== void 0 ? { nativeReasoning: launchSpec.nativeReasoning } : {},
6947
- ...launchSpec.permissionCli !== void 0 ? { permissionCli: launchSpec.permissionCli } : {},
6948
- ...launchSpec.reasoningCli !== void 0 && options.reasoningLevel !== void 0 ? { launchReasoningLevel: options.reasoningLevel } : {},
6949
- permissionMode: options.permissionMode,
6950
- workspaceWriteRoots: [cwd, ...args.additionalWorkspaceWriteRoots],
6951
- ...Object.keys(envVars).length > 0 ? { envVars } : {},
6952
- ...instructions ? { instructions } : {},
6953
- ...args.dynamicTools && args.dynamicTools.length > 0 ? { dynamicTools: args.dynamicTools } : {}
6954
- };
6975
+ const currentModelId = sessionModels?.currentModelId;
6976
+ const models = availableModels.map((model, index) => {
6977
+ const isDefault = currentModelId !== void 0 ? model.modelId === currentModelId : index === 0;
6978
+ return {
6979
+ id: model.modelId,
6980
+ model: model.modelId,
6981
+ displayName: model.name ?? model.modelId,
6982
+ description: model.description ?? "",
6983
+ supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
6984
+ defaultReasoningEffort: "medium",
6985
+ isDefault
6986
+ };
6987
+ });
6988
+ return models.some((model) => model.isDefault) ? models : models.map(
6989
+ (model, index) => index === 0 ? { ...model, isDefault: true } : model
6990
+ );
6955
6991
  }
6956
-
6957
- // ../provider-bridge-acp/src/bridge/agent-connection.ts
6958
- import { spawn } from "node:child_process";
6959
- import { createInterface } from "node:readline";
6960
- var STDERR_TAIL_MAX_CHUNKS = 40;
6961
- var CLOSED_STDIN_ERROR_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED"]);
6962
- var AcpAgentExitedError = class extends Error {
6963
- constructor(message) {
6964
- super(message);
6965
- this.name = "AcpAgentExitedError";
6992
+ function splitVariant(id) {
6993
+ let rest = id;
6994
+ let fast = false;
6995
+ if (rest.endsWith(FAST_TAIL)) {
6996
+ fast = true;
6997
+ rest = rest.slice(0, -FAST_TAIL.length);
6966
6998
  }
6967
- };
6968
- var AcpAgentResponseError = class extends Error {
6969
- code;
6970
- constructor(message, code) {
6971
- super(message);
6972
- this.name = "AcpAgentResponseError";
6973
- this.code = code;
6999
+ let thinking = false;
7000
+ if (rest.endsWith(`-${THINKING_TOKEN}`)) {
7001
+ thinking = true;
7002
+ rest = rest.slice(0, -(THINKING_TOKEN.length + 1));
7003
+ } else if (rest.includes(`-${THINKING_TOKEN}-`)) {
7004
+ thinking = true;
7005
+ rest = rest.replace(`-${THINKING_TOKEN}-`, "-");
6974
7006
  }
6975
- };
6976
- function isClosedAgentStdinError(error) {
6977
- return "code" in error && typeof error.code === "string" && CLOSED_STDIN_ERROR_CODES.has(error.code);
7007
+ for (const [token, effort] of EFFORT_TOKENS) {
7008
+ if (rest.endsWith(`-${token}`)) {
7009
+ return {
7010
+ familyKey: rest.slice(0, -(token.length + 1)),
7011
+ effort,
7012
+ effortToken: token,
7013
+ fast,
7014
+ thinking
7015
+ };
7016
+ }
7017
+ }
7018
+ return {
7019
+ familyKey: rest,
7020
+ effort: "medium",
7021
+ effortToken: void 0,
7022
+ fast,
7023
+ thinking
7024
+ };
6978
7025
  }
6979
- function formatAgentError(error) {
6980
- const message = error.message ?? `ACP agent returned error code ${error.code ?? "unknown"}`;
6981
- const details = formatAgentErrorData(error.data);
6982
- return details === void 0 ? message : `${message}: ${details}`;
7026
+ function agentModelFamilyId(id) {
7027
+ return splitVariant(id).familyKey;
6983
7028
  }
6984
- function formatAgentErrorData(data) {
6985
- if (data === void 0 || data === null) {
6986
- return void 0;
6987
- }
6988
- if (typeof data === "string") {
6989
- return data.trim() === "" ? void 0 : data;
6990
- }
6991
- if (typeof data === "object" && "details" in data && typeof data.details === "string" && data.details.trim() !== "") {
6992
- return data.details;
6993
- }
6994
- try {
6995
- return JSON.stringify(data);
6996
- } catch {
6997
- return void 0;
7029
+ var EFFORT_DISPLAY_WORDS = {
7030
+ "extra-high": "Extra High",
7031
+ medium: "Medium",
7032
+ xhigh: "Extra High",
7033
+ high: "High",
7034
+ low: "Low",
7035
+ max: "Max",
7036
+ ultra: "Ultra",
7037
+ none: "None"
7038
+ };
7039
+ function familyDisplayName(displayName, effortToken) {
7040
+ const word = effortToken ? EFFORT_DISPLAY_WORDS[effortToken] : void 0;
7041
+ if (!word) {
7042
+ return cleanDisplayName(displayName);
6998
7043
  }
7044
+ return cleanDisplayName(
7045
+ displayName.replace(new RegExp(`(^|\\s)${word}(?=\\s|$)`), "$1")
7046
+ );
6999
7047
  }
7000
- function parseAgentLine(line) {
7001
- const trimmed = line.trim();
7002
- if (!trimmed) {
7003
- return null;
7004
- }
7005
- let parsed;
7006
- try {
7007
- parsed = JSON.parse(trimmed);
7008
- } catch {
7009
- return null;
7048
+ function cleanDisplayName(name) {
7049
+ return name.replace(/\s*\((?:NO ZDR|default|current)\)/gi, "").replace(/(^|\s)(?:1M|Thinking)(?=\s|$)/g, "$1").replace(/\s{2,}/g, " ").trim();
7050
+ }
7051
+ function buildAgentModelCatalog(rawModels) {
7052
+ const families = /* @__PURE__ */ new Map();
7053
+ for (const raw of rawModels) {
7054
+ const { familyKey, effort, effortToken, fast, thinking } = splitVariant(
7055
+ raw.id
7056
+ );
7057
+ const members = families.get(familyKey) ?? [];
7058
+ members.push({ ...raw, effort, effortToken, fast, thinking });
7059
+ families.set(familyKey, members);
7010
7060
  }
7011
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
7061
+ if (families.size === 0) {
7012
7062
  return null;
7013
7063
  }
7014
- return parsed;
7015
- }
7016
- function createAcpAgentConnection(options) {
7017
- const child = spawn(options.command, options.args, {
7018
- cwd: options.cwd,
7019
- env: options.env,
7020
- stdio: ["pipe", "pipe", "pipe"]
7021
- });
7022
- experimental_recordProviderChildIo(child, {
7023
- threadId: options.recordThreadId
7024
- });
7025
- const pending = /* @__PURE__ */ new Map();
7026
- const stderrChunks = [];
7027
- let nextRequestId = 1;
7028
- let exited = false;
7029
- function rejectAllPending(error) {
7030
- for (const [, request] of pending) {
7031
- request.reject(error);
7032
- }
7033
- pending.clear();
7034
- }
7035
- function closeForAgentStdin(error) {
7036
- if (exited) {
7037
- return;
7064
+ const models = [];
7065
+ const variantsByFamilyId = /* @__PURE__ */ new Map();
7066
+ const defaultEffortByFamilyId = /* @__PURE__ */ new Map();
7067
+ for (const members of families.values()) {
7068
+ const hasThinking = members.some((m) => m.thinking);
7069
+ const leveled = members.map((member) => ({
7070
+ member,
7071
+ level: member.thinking ? member.effort : hasThinking ? "none" : member.effort
7072
+ }));
7073
+ const byLevel = /* @__PURE__ */ new Map();
7074
+ const repEffortByCell = /* @__PURE__ */ new Map();
7075
+ for (const { member, level } of leveled) {
7076
+ const slot = member.fast ? "fast" : "normal";
7077
+ const tier = byLevel.get(level) ?? {};
7078
+ const cellKey = `${level}:${slot}`;
7079
+ const upgradesNoneRep = level === "none" && member.effort === "medium" && repEffortByCell.get(cellKey) !== "medium";
7080
+ if (tier[slot] === void 0 || upgradesNoneRep) {
7081
+ tier[slot] = member.id;
7082
+ repEffortByCell.set(cellKey, member.effort);
7083
+ byLevel.set(level, tier);
7084
+ }
7038
7085
  }
7039
- exited = true;
7040
- const code = "code" in error && typeof error.code === "string" ? ` (${error.code})` : "";
7041
- const detail = `stdin closed${code}: ${error.message}`;
7042
- rejectAllPending(
7043
- new AcpAgentExitedError(`ACP agent "${options.command}" ${detail}`)
7086
+ const nonFast = leveled.filter((entry) => !entry.member.fast);
7087
+ const pool = nonFast.length > 0 ? nonFast : leveled;
7088
+ const defaultEntry = pool.find((entry) => entry.level === "medium") ?? pool.find((entry) => entry.level !== "none") ?? pool[0];
7089
+ const defaultVariant = defaultEntry.member;
7090
+ const levelsInLadderOrder = [...byLevel.keys()].sort(
7091
+ (a, b) => reasoningLevelValues.indexOf(a) - reasoningLevelValues.indexOf(b)
7044
7092
  );
7045
- child.kill("SIGKILL");
7046
- const stderrTail = [...stderrChunks, detail].join("\n");
7047
- options.onExit({ code: null, signal: null, stderrTail });
7048
- }
7049
- function writeLine(message) {
7050
- const stdin = child.stdin;
7051
- if (!stdin || stdin.destroyed || !stdin.writable) {
7052
- closeForAgentStdin(new Error("stdin is not writable"));
7053
- return;
7054
- }
7055
- stdin.write(JSON.stringify(message) + "\n");
7056
- }
7057
- child.stdin?.on("error", (error) => {
7058
- if (!isClosedAgentStdinError(error)) {
7059
- throw error;
7093
+ const nameByLevel = /* @__PURE__ */ new Map();
7094
+ for (const { member, level } of leveled) {
7095
+ if (!nameByLevel.has(level)) {
7096
+ nameByLevel.set(level, member.displayName);
7097
+ }
7060
7098
  }
7061
- closeForAgentStdin(error);
7062
- });
7063
- if (child.stdout) {
7064
- const stdoutLines = createInterface({
7065
- input: child.stdout,
7066
- terminal: false
7099
+ models.push({
7100
+ id: defaultVariant.id,
7101
+ model: defaultVariant.id,
7102
+ displayName: familyDisplayName(
7103
+ defaultVariant.displayName,
7104
+ defaultVariant.effortToken
7105
+ ),
7106
+ description: "",
7107
+ supportedReasoningEfforts: levelsInLadderOrder.map((level) => ({
7108
+ reasoningEffort: level,
7109
+ description: nameByLevel.get(level) ?? ""
7110
+ })),
7111
+ defaultReasoningEffort: defaultEntry.level,
7112
+ // The agent lists its default model first.
7113
+ isDefault: models.length === 0
7067
7114
  });
7068
- stdoutLines.on("line", (line) => {
7069
- const message = parseAgentLine(line);
7070
- if (!message) {
7071
- return;
7115
+ variantsByFamilyId.set(defaultVariant.id, byLevel);
7116
+ defaultEffortByFamilyId.set(defaultVariant.id, defaultEntry.level);
7117
+ }
7118
+ return {
7119
+ models,
7120
+ resolveVariant({ model, reasoningLevel, serviceTier }) {
7121
+ const byLevel = variantsByFamilyId.get(model);
7122
+ if (!byLevel) {
7123
+ return void 0;
7072
7124
  }
7073
- const id = message.id;
7074
- if ((typeof id === "string" || typeof id === "number") && message.method === void 0) {
7075
- const numericId = typeof id === "number" ? id : Number(id);
7076
- const request = pending.get(numericId);
7077
- if (!request) {
7078
- return;
7079
- }
7080
- pending.delete(numericId);
7081
- if (message.error) {
7082
- request.reject(
7083
- new AcpAgentResponseError(
7084
- formatAgentError(message.error),
7085
- message.error.code
7086
- )
7087
- );
7088
- } else {
7089
- request.resolve(message.result);
7090
- }
7091
- return;
7092
- }
7093
- if (typeof message.method !== "string") {
7094
- return;
7095
- }
7096
- if (typeof id === "string" || typeof id === "number") {
7097
- let settled = false;
7098
- options.onRequest(message.method, message.params, {
7099
- result(value) {
7100
- if (settled) return;
7101
- settled = true;
7102
- writeLine({ jsonrpc: "2.0", id, result: value ?? null });
7103
- },
7104
- error(code, errorMessage) {
7105
- if (settled) return;
7106
- settled = true;
7107
- writeLine({
7108
- jsonrpc: "2.0",
7109
- id,
7110
- error: { code, message: errorMessage }
7111
- });
7112
- }
7113
- });
7114
- return;
7125
+ const level = reasoningLevel ?? defaultEffortByFamilyId.get(model);
7126
+ const tier = level === void 0 ? void 0 : byLevel.get(level);
7127
+ if (!tier) {
7128
+ return void 0;
7115
7129
  }
7116
- options.onNotification(message.method, message.params);
7117
- });
7118
- }
7119
- if (child.stderr) {
7120
- const stderrLines = createInterface({
7121
- input: child.stderr,
7122
- terminal: false
7123
- });
7124
- stderrLines.on("line", (line) => {
7125
- stderrChunks.push(line);
7126
- if (stderrChunks.length > STDERR_TAIL_MAX_CHUNKS) {
7127
- stderrChunks.shift();
7130
+ if (serviceTier === "fast" && tier.fast !== void 0) {
7131
+ return tier.fast;
7128
7132
  }
7129
- });
7130
- }
7131
- child.on("error", (error) => {
7132
- if (exited) {
7133
- return;
7133
+ return tier.normal ?? tier.fast;
7134
7134
  }
7135
- exited = true;
7136
- rejectAllPending(
7137
- new AcpAgentExitedError(
7138
- `Failed to launch ACP agent "${options.command}": ${error.message}`
7139
- )
7140
- );
7141
- options.onExit({ code: null, signal: null, stderrTail: error.message });
7135
+ };
7136
+ }
7137
+ function splitPrimaryModels(catalogModels, primaryModels) {
7138
+ const primaryIds = new Set(primaryModels);
7139
+ const modelsById = new Map(catalogModels.map((model) => [model.id, model]));
7140
+ const models = primaryModels.flatMap((id) => {
7141
+ const model = modelsById.get(id);
7142
+ return model ? [model] : [];
7142
7143
  });
7143
- child.on("exit", (code, signal) => {
7144
- if (exited) {
7145
- return;
7146
- }
7147
- exited = true;
7148
- const stderrTail = stderrChunks.join("\n");
7149
- rejectAllPending(
7150
- new AcpAgentExitedError(
7151
- `ACP agent "${options.command}" exited (code ${code ?? "null"}, signal ${signal ?? "null"})${stderrTail ? `: ${stderrTail}` : ""}`
7144
+ if (models.length === 0) {
7145
+ return { models: [...catalogModels], selectedOnlyModels: [] };
7146
+ }
7147
+ const selectedOnlyModels = catalogModels.filter(
7148
+ (model) => !primaryIds.has(model.id)
7149
+ );
7150
+ if (models.some((model) => model.isDefault)) {
7151
+ return {
7152
+ models,
7153
+ selectedOnlyModels: selectedOnlyModels.map(
7154
+ (model) => model.isDefault ? { ...model, isDefault: false } : model
7152
7155
  )
7153
- );
7154
- options.onExit({ code, signal, stderrTail });
7155
- });
7156
+ };
7157
+ }
7156
7158
  return {
7157
- get exited() {
7158
- return exited;
7159
- },
7160
- request({ method, params, resultSchema }) {
7161
- if (exited) {
7162
- return Promise.reject(
7163
- new AcpAgentExitedError(
7164
- `ACP agent "${options.command}" is not running`
7165
- )
7166
- );
7167
- }
7168
- const id = nextRequestId;
7169
- nextRequestId += 1;
7170
- return new Promise((resolve4, reject) => {
7171
- pending.set(id, {
7172
- resolve: (value) => {
7173
- const parsed = resultSchema.safeParse(value);
7174
- if (parsed.success) {
7175
- resolve4(parsed.data);
7176
- } else {
7177
- reject(
7178
- new Error(
7179
- `ACP agent returned an unexpected ${method} result: ${parsed.error.message}`
7180
- )
7181
- );
7182
- }
7183
- },
7184
- reject
7185
- });
7186
- writeLine({ jsonrpc: "2.0", id, method, params });
7187
- });
7188
- },
7189
- notify(method, params) {
7190
- if (exited) {
7191
- return;
7192
- }
7193
- writeLine({ jsonrpc: "2.0", method, params });
7194
- },
7195
- kill() {
7196
- if (exited) {
7197
- return;
7198
- }
7199
- child.kill("SIGTERM");
7200
- }
7159
+ models: models.map(
7160
+ (model, index) => index === 0 ? { ...model, isDefault: true } : model
7161
+ ),
7162
+ selectedOnlyModels: selectedOnlyModels.map(
7163
+ (model) => model.isDefault ? { ...model, isDefault: false } : model
7164
+ )
7201
7165
  };
7202
7166
  }
7203
7167
 
7204
- // ../provider-bridge-acp/src/bridge/cursor-mcp-approval.ts
7205
- import { execFile as execFile3 } from "node:child_process";
7206
- import { createHash, randomBytes } from "node:crypto";
7207
- import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
7208
- import { homedir } from "node:os";
7209
- import { basename as basename2, dirname, join as join2, resolve as resolve2 } from "node:path";
7210
-
7211
- // ../provider-bridge-acp/src/bridge/tool-proxy-mcp.ts
7212
- import { createConnection } from "node:net";
7213
- import { createInterface as createInterface2 } from "node:readline";
7214
- import { z as z38 } from "zod";
7215
- var ACP_BRIDGE_MCP_SERVER_NAME = "bb-bridge";
7216
- var ENV_HOST = "BB_ACP_DYNAMIC_TOOL_HOST";
7217
- var ENV_PORT = "BB_ACP_DYNAMIC_TOOL_PORT";
7218
- var ENV_TOKEN = "BB_ACP_DYNAMIC_TOOL_TOKEN";
7219
- var ENV_THREAD_ID = "BB_ACP_DYNAMIC_TOOL_THREAD_ID";
7220
- var ENV_TOOLS = "BB_ACP_DYNAMIC_TOOLS";
7221
- var ENV_PROGRESS_INTERVAL_MS = "BB_ACP_DYNAMIC_TOOL_PROGRESS_INTERVAL_MS";
7222
- var bridgeToolCallResponseSchema = z38.union([
7223
- z38.object({
7224
- ok: z38.literal(true),
7225
- content: z38.string(),
7226
- contentBlocks: z38.array(
7227
- z38.discriminatedUnion("type", [
7228
- z38.object({ type: z38.literal("text"), text: z38.string() }),
7229
- z38.object({
7230
- type: z38.literal("image"),
7231
- data: z38.string(),
7232
- mimeType: z38.string()
7233
- })
7234
- ])
7235
- ).optional(),
7236
- // The initialized response and older text-only responses omit images.
7237
- // Parsing them as an empty list keeps the re-executed packaged artifact
7238
- // compatible with that legacy socket shape.
7239
- images: z38.array(z38.object({ data: z38.string(), mimeType: z38.string() })).default([]),
7240
- isError: z38.boolean().optional()
7241
- }),
7242
- z38.object({ ok: z38.literal(false), error: z38.string() })
7243
- ]);
7244
- var nextMcpToolCallId = 0;
7245
- var TOOL_CALL_PROGRESS_INTERVAL_MS = 15e3;
7246
- function buildAcpMcpServerConfig(args) {
7247
- return {
7248
- name: ACP_BRIDGE_MCP_SERVER_NAME,
7249
- command: args.command,
7250
- args: args.bridgeArgs,
7251
- env: [
7252
- ...args.runtimeEnv,
7253
- { name: ENV_HOST, value: args.host },
7254
- { name: ENV_PORT, value: String(args.port) },
7255
- { name: ENV_TOKEN, value: args.token },
7256
- { name: ENV_THREAD_ID, value: args.threadId },
7257
- { name: ENV_TOOLS, value: JSON.stringify(args.dynamicTools) }
7258
- ]
7259
- };
7168
+ // ../provider-bridge-acp/src/session-params.ts
7169
+ function sanitizeAcpSkillDescription(description) {
7170
+ const sanitized = description.replace(/[\r\n]+/gu, " ").replace(/\s+/gu, " ").replace(/[<>]/gu, "").trim();
7171
+ return sanitized.length > 0 ? sanitized : "(description unavailable)";
7260
7172
  }
7261
- function readEnvironment() {
7262
- const port = Number(process.env[ENV_PORT]);
7263
- if (!Number.isInteger(port) || port <= 0) {
7264
- throw new Error(`${ENV_PORT} must be a positive integer`);
7173
+ function buildAcpSkillsInstructions(skillRoots) {
7174
+ if (!skillRoots || skillRoots.length === 0) {
7175
+ return void 0;
7265
7176
  }
7266
- const host = process.env[ENV_HOST];
7267
- const token = process.env[ENV_TOKEN];
7268
- const threadId = process.env[ENV_THREAD_ID];
7269
- const toolsJson = process.env[ENV_TOOLS];
7270
- if (!host || !token || !threadId || !toolsJson) {
7271
- throw new Error("Missing ACP dynamic tool MCP server environment");
7177
+ const skillLines = skillRoots.flatMap((skillRoot) => {
7178
+ return skillRoot.skills.map((skill) => {
7179
+ const skillFilePath = path4.join(
7180
+ skillRoot.skillDirectoryRootPath,
7181
+ skill.name,
7182
+ "SKILL.md"
7183
+ );
7184
+ return `- ${skill.name}: ${sanitizeAcpSkillDescription(skill.description)} (SKILL.md: ${skillFilePath})`;
7185
+ });
7186
+ });
7187
+ if (skillLines.length === 0) {
7188
+ return void 0;
7272
7189
  }
7273
- const parsedTools = JSON.parse(toolsJson);
7274
- const tools = dynamicToolSchema.array().parse(parsedTools);
7275
- const rawProgressInterval = process.env[ENV_PROGRESS_INTERVAL_MS];
7276
- const progressIntervalMs = rawProgressInterval !== void 0 && Number(rawProgressInterval) > 0 ? Number(rawProgressInterval) : void 0;
7277
- return {
7278
- host,
7279
- port,
7280
- progressIntervalMs,
7281
- threadId,
7282
- token,
7283
- tools
7284
- };
7285
- }
7286
- function writeJson(message) {
7287
- process.stdout.write(`${JSON.stringify(message)}
7288
- `);
7289
- }
7290
- function writeResult(id, result) {
7291
- writeJson({ jsonrpc: "2.0", id, result });
7190
+ return [
7191
+ "bb skills are reusable instruction folders. When the current task matches a listed skill description, read that skill's SKILL.md at the absolute path before proceeding; you may read supporting files in the same skill directory that SKILL.md references. If a listed path does not exist, the list is stale and should be ignored.",
7192
+ "",
7193
+ "Available bb skills:",
7194
+ ...skillLines
7195
+ ].join("\n");
7292
7196
  }
7293
- function writeError(id, code, message) {
7294
- writeJson({ jsonrpc: "2.0", id, error: { code, message } });
7197
+ function buildAcpSessionInstructions(options) {
7198
+ const baseInstructions = options.instructions?.trim();
7199
+ const skillsInstructions = buildAcpSkillsInstructions(options.skillRoots);
7200
+ const instructions = [baseInstructions, skillsInstructions].filter(
7201
+ (value) => value !== void 0 && value.length > 0
7202
+ );
7203
+ return instructions.length > 0 ? instructions.join("\n\n") : void 0;
7295
7204
  }
7296
- function mcpToolCallId(toolName) {
7297
- nextMcpToolCallId += 1;
7298
- return `acp-mcp-${toolName}-${Date.now()}-${nextMcpToolCallId}`;
7205
+ function launchEnvVars(launchSpec) {
7206
+ return Object.keys(launchSpec.env).length > 0 ? { envVars: launchSpec.env } : {};
7299
7207
  }
7300
- function callBridge(env, request) {
7301
- return new Promise((resolve4, reject) => {
7302
- const socket = createConnection({ host: env.host, port: env.port });
7303
- let buffer = "";
7304
- socket.setEncoding("utf8");
7305
- socket.on("connect", () => {
7306
- const payload = {
7307
- ...request,
7308
- threadId: env.threadId,
7309
- token: env.token
7310
- };
7311
- socket.write(`${JSON.stringify(payload)}
7312
- `);
7313
- });
7314
- socket.on("data", (chunk) => {
7315
- buffer += chunk;
7316
- const newlineIndex = buffer.indexOf("\n");
7317
- if (newlineIndex === -1) {
7318
- return;
7319
- }
7320
- const line = buffer.slice(0, newlineIndex);
7321
- socket.end();
7322
- try {
7323
- resolve4(bridgeToolCallResponseSchema.parse(JSON.parse(line)));
7324
- } catch (error) {
7325
- reject(error);
7326
- }
7327
- });
7328
- socket.on("error", reject);
7329
- socket.on("end", () => {
7330
- if (!buffer.includes("\n")) {
7331
- reject(new Error("ACP dynamic tool bridge closed without a response"));
7332
- }
7333
- });
7334
- });
7208
+ function buildAcpModelListCommand(launchSpec) {
7209
+ if (!launchSpec.modelCli || launchSpec.modelCli.listArgs.length === 0) {
7210
+ return void 0;
7211
+ }
7212
+ return {
7213
+ command: launchSpec.command,
7214
+ args: [...launchSpec.modelCli.listArgs],
7215
+ ...launchSpec.cwd !== void 0 ? { cwd: launchSpec.cwd } : {},
7216
+ ...launchEnvVars(launchSpec)
7217
+ };
7335
7218
  }
7336
- function objectParams(params) {
7337
- return params && typeof params === "object" && !Array.isArray(params) ? params : {};
7219
+ function buildAcpModelDiscoveryAgentCommand(launchSpec) {
7220
+ if (buildAcpModelListCommand(launchSpec) !== void 0) {
7221
+ return void 0;
7222
+ }
7223
+ return {
7224
+ command: launchSpec.command,
7225
+ args: [...launchSpec.args],
7226
+ ...launchSpec.cwd !== void 0 ? { cwd: launchSpec.cwd } : {},
7227
+ ...launchEnvVars(launchSpec)
7228
+ };
7338
7229
  }
7339
- function readProgressToken(params) {
7340
- const meta = objectParams(objectParams(params)._meta).progressToken;
7341
- return typeof meta === "string" || typeof meta === "number" ? meta : null;
7230
+ function buildAcpModelListParams(launchSpec, options) {
7231
+ const primaryModels = [
7232
+ ...options.primaryModels ?? launchSpec?.modelCli?.primaryModels ?? []
7233
+ ];
7234
+ const reasoningProbePriorityModelIds = [
7235
+ ...options.reasoningProbePriorityModelIds
7236
+ ];
7237
+ if (launchSpec === null) {
7238
+ return {
7239
+ primaryModels,
7240
+ reasoningProbePriorityModelIds,
7241
+ parameterizedModelPicker: options.parameterizedModelPicker
7242
+ };
7243
+ }
7244
+ const listCommand = buildAcpModelListCommand(launchSpec);
7245
+ const agent = buildAcpModelDiscoveryAgentCommand(launchSpec);
7246
+ return {
7247
+ ...listCommand !== void 0 ? { listCommand } : {},
7248
+ ...agent !== void 0 ? { agent } : {},
7249
+ primaryModels,
7250
+ reasoningProbePriorityModelIds,
7251
+ parameterizedModelPicker: options.parameterizedModelPicker,
7252
+ ...launchSpec.reasoningCli !== void 0 ? { reasoningCli: launchSpec.reasoningCli } : {},
7253
+ ...launchSpec.nativeReasoning !== void 0 ? { nativeReasoning: launchSpec.nativeReasoning } : {}
7254
+ };
7342
7255
  }
7343
- function startProgressHeartbeat(args) {
7344
- let progress = 0;
7345
- const timer = setInterval(() => {
7346
- progress += 1;
7347
- writeJson({
7348
- jsonrpc: "2.0",
7349
- method: "notifications/progress",
7350
- params: { progressToken: args.progressToken, progress }
7351
- });
7352
- }, args.intervalMs ?? TOOL_CALL_PROGRESS_INTERVAL_MS);
7353
- return () => clearInterval(timer);
7256
+ function cursorParameterizedModelId(model) {
7257
+ const familyId = model === "auto" ? "default" : agentModelFamilyId(model);
7258
+ return familyId.startsWith("cursor-") ? familyId.slice("cursor-".length) : familyId;
7354
7259
  }
7355
- async function handleRequest(env, message) {
7356
- if (message.id === void 0 || message.method === void 0) {
7357
- return;
7260
+ function buildAcpModelSelectionParam(launchSpec, options, parameterizedModelPicker, dialectId) {
7261
+ const model = options.model;
7262
+ const listCommand = buildAcpModelListCommand(launchSpec);
7263
+ if (!model || model === ACP_DEFAULT_MODEL_ID) {
7264
+ return {};
7358
7265
  }
7359
- switch (message.method) {
7360
- case "initialize":
7361
- writeResult(message.id, {
7362
- protocolVersion: typeof objectParams(message.params).protocolVersion === "string" ? objectParams(message.params).protocolVersion : "2024-11-05",
7363
- capabilities: { tools: {} },
7364
- serverInfo: { name: ACP_BRIDGE_MCP_SERVER_NAME, version: "1.0.0" }
7365
- });
7366
- void callBridge(env, {
7367
- kind: "initialized",
7368
- toolCount: env.tools.length
7369
- }).catch((error) => {
7370
- process.stderr.write(
7371
- `bb-bridge MCP: failed to report initialize: ${error instanceof Error ? error.message : String(error)}
7372
- `
7373
- );
7374
- });
7375
- return;
7376
- case "tools/list":
7377
- writeResult(message.id, {
7378
- tools: env.tools.map((tool) => ({
7379
- name: tool.name,
7380
- description: tool.description,
7381
- inputSchema: tool.inputSchema
7382
- }))
7383
- });
7384
- return;
7385
- case "tools/call": {
7386
- const params = objectParams(message.params);
7387
- const name = typeof params.name === "string" ? params.name : "";
7388
- const tool = env.tools.find((candidate) => candidate.name === name);
7389
- if (!tool) {
7390
- writeError(message.id, -32602, `Unknown tool: ${name}`);
7391
- return;
7392
- }
7393
- const rawArguments = params.arguments;
7394
- const toolArguments = rawArguments && typeof rawArguments === "object" && !Array.isArray(rawArguments) ? rawArguments : {};
7395
- const progressToken = readProgressToken(message.params);
7396
- const stopHeartbeat = progressToken === null ? () => {
7397
- } : startProgressHeartbeat({
7398
- intervalMs: env.progressIntervalMs,
7399
- progressToken
7400
- });
7401
- try {
7402
- const result = await callBridge(env, {
7403
- kind: "toolCall",
7404
- arguments: toolArguments,
7405
- callId: mcpToolCallId(tool.name),
7406
- tool: tool.name
7407
- });
7408
- stopHeartbeat();
7409
- if (!result.ok) {
7410
- writeResult(message.id, {
7411
- content: [{ type: "text", text: result.error }],
7412
- isError: true
7413
- });
7414
- return;
7415
- }
7416
- writeResult(message.id, {
7417
- content: buildBridgeToolCallContent(result),
7418
- ...result.isError ? { isError: true } : {}
7419
- });
7420
- } catch (error) {
7421
- stopHeartbeat();
7422
- writeResult(message.id, {
7423
- content: [
7424
- {
7425
- type: "text",
7426
- text: error instanceof Error ? error.message : String(error)
7427
- }
7428
- ],
7429
- isError: true
7430
- });
7266
+ if (parameterizedModelPicker || !listCommand || !launchSpec.modelCli?.selectFlag) {
7267
+ return {
7268
+ modelSelection: {
7269
+ modelId: parameterizedModelPicker && dialectId === "cursor" ? cursorParameterizedModelId(model) : model,
7270
+ ...options.reasoningLevel !== void 0 ? { reasoningLevel: options.reasoningLevel } : {},
7271
+ ...parameterizedModelPicker && options.serviceTier !== void 0 ? { serviceTier: options.serviceTier } : {}
7431
7272
  }
7432
- return;
7433
- }
7434
- default:
7435
- writeError(
7436
- message.id,
7437
- -32601,
7438
- `Unsupported MCP method: ${message.method}`
7439
- );
7273
+ };
7440
7274
  }
7441
- }
7442
- function runAcpDynamicToolMcpServer() {
7443
- const env = readEnvironment();
7444
- const rl = createInterface2({ input: process.stdin, terminal: false });
7445
- rl.on("line", (line) => {
7446
- const trimmed = line.trim();
7447
- if (!trimmed) {
7448
- return;
7449
- }
7450
- let message;
7451
- try {
7452
- message = JSON.parse(trimmed);
7453
- } catch {
7454
- return;
7275
+ return {
7276
+ modelSelection: {
7277
+ listCommand,
7278
+ selectFlag: launchSpec.modelCli.selectFlag,
7279
+ model,
7280
+ ...options.reasoningLevel !== void 0 ? { reasoningLevel: options.reasoningLevel } : {},
7281
+ // Only "fast" changes launch resolution; "default" is the normal id.
7282
+ ...options.serviceTier === "fast" ? { serviceTier: options.serviceTier } : {}
7455
7283
  }
7456
- void handleRequest(env, message);
7457
- });
7458
- }
7459
-
7460
- // ../provider-bridge-acp/src/bridge/cursor-mcp-approval.ts
7461
- var CURSOR_MCP_APPROVAL_FILE = "mcp-approvals.json";
7462
- var CURSOR_APPROVAL_LOCK_STALE_MS = 3e4;
7463
- var CURSOR_APPROVAL_LOCK_TIMEOUT_MS = 5e3;
7464
- function errorCode(error) {
7465
- return error instanceof Error && "code" in error ? error.code : void 0;
7466
- }
7467
- function cursorAgentCommand(command) {
7468
- return basename2(command).toLowerCase().replace(/\.(?:bat|cmd|exe)$/u, "") === "cursor-agent";
7469
- }
7470
- function cursorProjectSlug(projectRoot) {
7471
- return projectRoot.replace(/[^a-zA-Z0-9]/gu, "-").replace(/-+/gu, "-").replace(/^-+|-+$/gu, "");
7284
+ };
7472
7285
  }
7473
- function cursorDataDirectory(env) {
7474
- const configured = env.CURSOR_DATA_DIR?.trim();
7475
- if (configured) {
7476
- return configured;
7477
- }
7478
- const home = env.HOME?.trim() || env.USERPROFILE?.trim() || homedir();
7479
- return join2(home, ".cursor");
7480
- }
7481
- function cursorMcpServerConfig(config) {
7286
+ function buildAcpSessionParams(args) {
7287
+ const { options, launchSpec } = args;
7288
+ const instructions = buildAcpSessionInstructions(options);
7289
+ const cwd = launchSpec.cwd ?? args.cwd;
7290
+ const envVars = {
7291
+ ...launchSpec.env,
7292
+ ...options.envVars ?? {}
7293
+ };
7294
+ if (options.permissionMode === "auto") {
7295
+ throw new Error(
7296
+ `Provider "${args.providerLabel}" does not support permission mode "auto".`
7297
+ );
7298
+ }
7482
7299
  return {
7483
- command: config.command,
7484
- args: config.args,
7485
- env: Object.fromEntries(config.env.map(({ name, value }) => [name, value]))
7300
+ threadId: args.threadId,
7301
+ cwd,
7302
+ agent: {
7303
+ command: launchSpec.command,
7304
+ args: [...launchSpec.args]
7305
+ },
7306
+ ...args.dialectId === void 0 ? {} : { dialectId: args.dialectId },
7307
+ ...buildAcpModelSelectionParam(
7308
+ launchSpec,
7309
+ options,
7310
+ args.parameterizedModelPicker,
7311
+ args.dialectId
7312
+ ),
7313
+ parameterizedModelPicker: args.parameterizedModelPicker,
7314
+ ...launchSpec.reasoningCli !== void 0 ? { reasoningCli: launchSpec.reasoningCli } : {},
7315
+ ...launchSpec.nativeReasoning !== void 0 ? { nativeReasoning: launchSpec.nativeReasoning } : {},
7316
+ ...launchSpec.permissionCli !== void 0 ? { permissionCli: launchSpec.permissionCli } : {},
7317
+ ...launchSpec.reasoningCli !== void 0 && options.reasoningLevel !== void 0 ? { launchReasoningLevel: options.reasoningLevel } : {},
7318
+ permissionMode: options.permissionMode,
7319
+ workspaceWriteRoots: [cwd, ...args.additionalWorkspaceWriteRoots],
7320
+ ...Object.keys(envVars).length > 0 ? { envVars } : {},
7321
+ ...instructions ? { instructions } : {},
7322
+ ...args.dynamicTools && args.dynamicTools.length > 0 ? { dynamicTools: args.dynamicTools } : {}
7486
7323
  };
7487
7324
  }
7488
- function buildCursorMcpApprovalIdentifier(args) {
7489
- const fingerprint = createHash("sha256").update(
7490
- JSON.stringify({
7491
- path: args.projectRoot,
7492
- server: cursorMcpServerConfig(args.config)
7493
- })
7494
- ).digest("hex").slice(0, 16);
7495
- return `${args.config.name}-${fingerprint}`;
7325
+
7326
+ // ../provider-bridge-acp/src/bridge/agent-connection.ts
7327
+ import { spawn } from "node:child_process";
7328
+ import { createInterface } from "node:readline";
7329
+ var STDERR_TAIL_MAX_CHUNKS = 40;
7330
+ var CLOSED_STDIN_ERROR_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED"]);
7331
+ var AcpAgentExitedError = class extends Error {
7332
+ constructor(message) {
7333
+ super(message);
7334
+ this.name = "AcpAgentExitedError";
7335
+ }
7336
+ };
7337
+ var AcpAgentResponseError = class extends Error {
7338
+ code;
7339
+ constructor(message, code) {
7340
+ super(message);
7341
+ this.name = "AcpAgentResponseError";
7342
+ this.code = code;
7343
+ }
7344
+ };
7345
+ function isClosedAgentStdinError(error) {
7346
+ return "code" in error && typeof error.code === "string" && CLOSED_STDIN_ERROR_CODES.has(error.code);
7496
7347
  }
7497
- async function resolveCursorProjectRoot(args) {
7498
- return new Promise((resolveRoot) => {
7499
- execFile3(
7500
- "git",
7501
- ["rev-parse", "--show-toplevel"],
7502
- { cwd: args.cwd, env: args.env, windowsHide: true },
7503
- (error, stdout) => {
7504
- const root = stdout.trim();
7505
- resolveRoot(error === null && root !== "" ? root : resolve2(args.cwd));
7506
- }
7507
- );
7508
- });
7348
+ function formatAgentError(error) {
7349
+ const message = error.message ?? `ACP agent returned error code ${error.code ?? "unknown"}`;
7350
+ const details = formatAgentErrorData(error.data);
7351
+ return details === void 0 ? message : `${message}: ${details}`;
7509
7352
  }
7510
- async function readApprovals(path5) {
7511
- let text;
7512
- try {
7513
- text = await readFile(path5, "utf8");
7514
- } catch (error) {
7515
- if (errorCode(error) === "ENOENT") {
7516
- return [];
7517
- }
7518
- throw error;
7353
+ function formatAgentErrorData(data) {
7354
+ if (data === void 0 || data === null) {
7355
+ return void 0;
7519
7356
  }
7520
- const value = JSON.parse(text);
7521
- if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
7522
- throw new Error(`Cursor MCP approval file is not a string array: ${path5}`);
7357
+ if (typeof data === "string") {
7358
+ return data.trim() === "" ? void 0 : data;
7359
+ }
7360
+ if (typeof data === "object" && "details" in data && typeof data.details === "string" && data.details.trim() !== "") {
7361
+ return data.details;
7523
7362
  }
7524
- return value;
7525
- }
7526
- async function writeApprovals(path5, approvals) {
7527
- await mkdir(dirname(path5), { recursive: true });
7528
- const tempPath = `${path5}.bb-${process.pid}-${randomBytes(6).toString("hex")}.tmp`;
7529
7363
  try {
7530
- await writeFile(tempPath, `${JSON.stringify(approvals, null, 2)}
7531
- `, {
7532
- encoding: "utf8",
7533
- mode: 384
7534
- });
7535
- await rename(tempPath, path5);
7536
- } catch (error) {
7537
- await rm(tempPath, { force: true });
7538
- throw error;
7364
+ return JSON.stringify(data);
7365
+ } catch {
7366
+ return void 0;
7539
7367
  }
7540
7368
  }
7541
- async function acquireApprovalLock(path5) {
7542
- const lockPath = `${path5}.bb-lock`;
7543
- const deadline = Date.now() + CURSOR_APPROVAL_LOCK_TIMEOUT_MS;
7544
- await mkdir(dirname(path5), { recursive: true });
7545
- for (; ; ) {
7546
- try {
7547
- await mkdir(lockPath, { mode: 448 });
7548
- return () => rm(lockPath, { recursive: true, force: true });
7549
- } catch (error) {
7550
- if (errorCode(error) !== "EEXIST") {
7551
- throw error;
7552
- }
7553
- }
7554
- try {
7555
- const lockStat = await stat(lockPath);
7556
- if (Date.now() - lockStat.mtimeMs > CURSOR_APPROVAL_LOCK_STALE_MS) {
7557
- await rm(lockPath, { recursive: true, force: true });
7558
- continue;
7559
- }
7560
- } catch (error) {
7561
- if (errorCode(error) === "ENOENT") {
7562
- continue;
7563
- }
7564
- throw error;
7565
- }
7566
- if (Date.now() >= deadline) {
7567
- throw new Error(`Timed out updating Cursor MCP approvals: ${path5}`);
7568
- }
7569
- await new Promise((resolveDelay) => setTimeout(resolveDelay, 25));
7369
+ function parseAgentLine(line) {
7370
+ const trimmed = line.trim();
7371
+ if (!trimmed) {
7372
+ return null;
7570
7373
  }
7571
- }
7572
- async function mutateApprovals(path5, mutate) {
7573
- const releaseLock = await acquireApprovalLock(path5);
7374
+ let parsed;
7574
7375
  try {
7575
- const approvals = await readApprovals(path5);
7576
- const nextApprovals = mutate(approvals);
7577
- if (approvals.length !== nextApprovals.length || approvals.some((approval, index) => approval !== nextApprovals[index])) {
7578
- await writeApprovals(path5, nextApprovals);
7579
- }
7580
- } finally {
7581
- await releaseLock();
7376
+ parsed = JSON.parse(trimmed);
7377
+ } catch {
7378
+ return null;
7582
7379
  }
7583
- }
7584
- async function approveCursorSessionMcpServer(args) {
7585
- if (!cursorAgentCommand(args.agentCommand) || args.config.name !== ACP_BRIDGE_MCP_SERVER_NAME) {
7586
- return void 0;
7380
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
7381
+ return null;
7587
7382
  }
7588
- const projectRoot = await resolveCursorProjectRoot({
7589
- cwd: args.cwd,
7590
- env: args.env
7383
+ return parsed;
7384
+ }
7385
+ function createAcpAgentConnection(options) {
7386
+ const child = spawn(options.command, options.args, {
7387
+ cwd: options.cwd,
7388
+ env: options.env,
7389
+ stdio: ["pipe", "pipe", "pipe"]
7591
7390
  });
7592
- const path5 = join2(
7593
- cursorDataDirectory(args.env),
7594
- "projects",
7595
- cursorProjectSlug(projectRoot),
7596
- CURSOR_MCP_APPROVAL_FILE
7597
- );
7598
- const approval = buildCursorMcpApprovalIdentifier({
7599
- config: args.config,
7600
- projectRoot
7391
+ experimental_recordProviderChildIo(child, {
7392
+ threadId: options.recordThreadId
7601
7393
  });
7602
- let installedByBb = false;
7603
- await mutateApprovals(path5, (approvals) => {
7604
- if (approvals.includes(approval)) {
7605
- return approvals;
7394
+ const pending = /* @__PURE__ */ new Map();
7395
+ const stderrChunks = [];
7396
+ let nextRequestId = 1;
7397
+ let exited = false;
7398
+ function rejectAllPending(error) {
7399
+ for (const [, request] of pending) {
7400
+ request.reject(error);
7606
7401
  }
7607
- installedByBb = true;
7608
- return [...approvals, approval];
7609
- });
7610
- return { approval, installedByBb, path: path5 };
7611
- }
7612
- async function revokeCursorSessionMcpServer(approval) {
7613
- if (!approval.installedByBb) {
7614
- return;
7402
+ pending.clear();
7615
7403
  }
7616
- await mutateApprovals(
7617
- approval.path,
7618
- (approvals) => approvals.filter((candidate) => candidate !== approval.approval)
7619
- );
7620
- }
7621
-
7622
- // ../provider-bridge-acp/src/bridge/model-catalog.ts
7623
- var ACP_NATIVE_REASONING_EFFORTS = [
7624
- {
7625
- reasoningEffort: "medium",
7626
- description: "Reasoning effort is managed by the connected ACP agent."
7404
+ function closeForAgentStdin(error) {
7405
+ if (exited) {
7406
+ return;
7407
+ }
7408
+ exited = true;
7409
+ const code = "code" in error && typeof error.code === "string" ? ` (${error.code})` : "";
7410
+ const detail = `stdin closed${code}: ${error.message}`;
7411
+ rejectAllPending(
7412
+ new AcpAgentExitedError(`ACP agent "${options.command}" ${detail}`)
7413
+ );
7414
+ child.kill("SIGKILL");
7415
+ const stderrTail = [...stderrChunks, detail].join("\n");
7416
+ options.onExit({ code: null, signal: null, stderrTail });
7627
7417
  }
7628
- ];
7629
- var MODEL_LINE_PATTERN = /^(\S+) - (.+)$/;
7630
- var BARE_PROVIDER_MODEL_LINE_PATTERN = /^\S+\/\S+$/;
7631
- var BULLETED_MODEL_LINE_PATTERN = /^[*-]\s+(\S+)(?:\s+\([^)]*\))?$/u;
7632
- var EFFORT_TOKENS = [
7633
- ["extra-high", "xhigh"],
7634
- ["medium", "medium"],
7635
- ["xhigh", "xhigh"],
7636
- ["high", "high"],
7637
- ["low", "low"],
7638
- ["max", "max"],
7639
- ["none", "none"]
7640
- ];
7641
- var FAST_TAIL = "-fast";
7642
- var THINKING_TOKEN = "thinking";
7643
- function parseAgentModelLines(stdout) {
7644
- const models = [];
7645
- for (const line of stdout.split("\n")) {
7646
- const trimmed = line.trim();
7647
- const match = MODEL_LINE_PATTERN.exec(trimmed);
7648
- if (!match) {
7649
- const bulletMatch = BULLETED_MODEL_LINE_PATTERN.exec(trimmed);
7650
- if (bulletMatch) {
7651
- const [, id2] = bulletMatch;
7652
- models.push({ id: id2, displayName: id2 });
7653
- continue;
7418
+ function writeLine(message) {
7419
+ const stdin = child.stdin;
7420
+ if (!stdin || stdin.destroyed || !stdin.writable) {
7421
+ closeForAgentStdin(new Error("stdin is not writable"));
7422
+ return;
7423
+ }
7424
+ stdin.write(JSON.stringify(message) + "\n");
7425
+ }
7426
+ child.stdin?.on("error", (error) => {
7427
+ if (!isClosedAgentStdinError(error)) {
7428
+ throw error;
7429
+ }
7430
+ closeForAgentStdin(error);
7431
+ });
7432
+ if (child.stdout) {
7433
+ const stdoutLines = createInterface({
7434
+ input: child.stdout,
7435
+ terminal: false
7436
+ });
7437
+ stdoutLines.on("line", (line) => {
7438
+ const message = parseAgentLine(line);
7439
+ if (!message) {
7440
+ return;
7654
7441
  }
7655
- if (BARE_PROVIDER_MODEL_LINE_PATTERN.test(trimmed)) {
7656
- models.push({ id: trimmed, displayName: trimmed });
7442
+ const id = message.id;
7443
+ if ((typeof id === "string" || typeof id === "number") && message.method === void 0) {
7444
+ const numericId = typeof id === "number" ? id : Number(id);
7445
+ const request = pending.get(numericId);
7446
+ if (!request) {
7447
+ return;
7448
+ }
7449
+ pending.delete(numericId);
7450
+ if (message.error) {
7451
+ request.reject(
7452
+ new AcpAgentResponseError(
7453
+ formatAgentError(message.error),
7454
+ message.error.code
7455
+ )
7456
+ );
7457
+ } else {
7458
+ request.resolve(message.result);
7459
+ }
7460
+ return;
7657
7461
  }
7658
- continue;
7462
+ if (typeof message.method !== "string") {
7463
+ return;
7464
+ }
7465
+ if (typeof id === "string" || typeof id === "number") {
7466
+ let settled = false;
7467
+ options.onRequest(message.method, message.params, {
7468
+ result(value) {
7469
+ if (settled) return;
7470
+ settled = true;
7471
+ writeLine({ jsonrpc: "2.0", id, result: value ?? null });
7472
+ },
7473
+ error(code, errorMessage) {
7474
+ if (settled) return;
7475
+ settled = true;
7476
+ writeLine({
7477
+ jsonrpc: "2.0",
7478
+ id,
7479
+ error: { code, message: errorMessage }
7480
+ });
7481
+ }
7482
+ });
7483
+ return;
7484
+ }
7485
+ options.onNotification(message.method, message.params);
7486
+ });
7487
+ }
7488
+ if (child.stderr) {
7489
+ const stderrLines = createInterface({
7490
+ input: child.stderr,
7491
+ terminal: false
7492
+ });
7493
+ stderrLines.on("line", (line) => {
7494
+ stderrChunks.push(line);
7495
+ if (stderrChunks.length > STDERR_TAIL_MAX_CHUNKS) {
7496
+ stderrChunks.shift();
7497
+ }
7498
+ });
7499
+ }
7500
+ child.on("error", (error) => {
7501
+ if (exited) {
7502
+ return;
7659
7503
  }
7660
- const [, id, displayName] = match;
7661
- models.push({ id, displayName });
7504
+ exited = true;
7505
+ rejectAllPending(
7506
+ new AcpAgentExitedError(
7507
+ `Failed to launch ACP agent "${options.command}": ${error.message}`
7508
+ )
7509
+ );
7510
+ options.onExit({ code: null, signal: null, stderrTail: error.message });
7511
+ });
7512
+ child.on("exit", (code, signal) => {
7513
+ if (exited) {
7514
+ return;
7515
+ }
7516
+ exited = true;
7517
+ const stderrTail = stderrChunks.join("\n");
7518
+ rejectAllPending(
7519
+ new AcpAgentExitedError(
7520
+ `ACP agent "${options.command}" exited (code ${code ?? "null"}, signal ${signal ?? "null"})${stderrTail ? `: ${stderrTail}` : ""}`
7521
+ )
7522
+ );
7523
+ options.onExit({ code, signal, stderrTail });
7524
+ });
7525
+ return {
7526
+ get exited() {
7527
+ return exited;
7528
+ },
7529
+ request({ method, params, resultSchema }) {
7530
+ if (exited) {
7531
+ return Promise.reject(
7532
+ new AcpAgentExitedError(
7533
+ `ACP agent "${options.command}" is not running`
7534
+ )
7535
+ );
7536
+ }
7537
+ const id = nextRequestId;
7538
+ nextRequestId += 1;
7539
+ return new Promise((resolve4, reject) => {
7540
+ pending.set(id, {
7541
+ resolve: (value) => {
7542
+ const parsed = resultSchema.safeParse(value);
7543
+ if (parsed.success) {
7544
+ resolve4(parsed.data);
7545
+ } else {
7546
+ reject(
7547
+ new Error(
7548
+ `ACP agent returned an unexpected ${method} result: ${parsed.error.message}`
7549
+ )
7550
+ );
7551
+ }
7552
+ },
7553
+ reject
7554
+ });
7555
+ writeLine({ jsonrpc: "2.0", id, method, params });
7556
+ });
7557
+ },
7558
+ notify(method, params) {
7559
+ if (exited) {
7560
+ return;
7561
+ }
7562
+ writeLine({ jsonrpc: "2.0", method, params });
7563
+ },
7564
+ kill() {
7565
+ if (exited) {
7566
+ return;
7567
+ }
7568
+ child.kill("SIGTERM");
7569
+ }
7570
+ };
7571
+ }
7572
+
7573
+ // ../provider-bridge-acp/src/bridge/cursor-mcp-approval.ts
7574
+ import { execFile as execFile3 } from "node:child_process";
7575
+ import { createHash, randomBytes } from "node:crypto";
7576
+ import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
7577
+ import { homedir } from "node:os";
7578
+ import { basename as basename2, dirname, join as join2, resolve as resolve2 } from "node:path";
7579
+
7580
+ // ../provider-bridge-acp/src/bridge/tool-proxy-mcp.ts
7581
+ import { createConnection } from "node:net";
7582
+ import { createInterface as createInterface2 } from "node:readline";
7583
+ import { z as z38 } from "zod";
7584
+ var ACP_BRIDGE_MCP_SERVER_NAME = "bb-bridge";
7585
+ var ENV_HOST = "BB_ACP_DYNAMIC_TOOL_HOST";
7586
+ var ENV_PORT = "BB_ACP_DYNAMIC_TOOL_PORT";
7587
+ var ENV_TOKEN = "BB_ACP_DYNAMIC_TOOL_TOKEN";
7588
+ var ENV_THREAD_ID = "BB_ACP_DYNAMIC_TOOL_THREAD_ID";
7589
+ var ENV_TOOLS = "BB_ACP_DYNAMIC_TOOLS";
7590
+ var ENV_PROGRESS_INTERVAL_MS = "BB_ACP_DYNAMIC_TOOL_PROGRESS_INTERVAL_MS";
7591
+ var bridgeToolCallResponseSchema = z38.union([
7592
+ z38.object({
7593
+ ok: z38.literal(true),
7594
+ content: z38.string(),
7595
+ contentBlocks: z38.array(
7596
+ z38.discriminatedUnion("type", [
7597
+ z38.object({ type: z38.literal("text"), text: z38.string() }),
7598
+ z38.object({
7599
+ type: z38.literal("image"),
7600
+ data: z38.string(),
7601
+ mimeType: z38.string()
7602
+ })
7603
+ ])
7604
+ ).optional(),
7605
+ // The initialized response and older text-only responses omit images.
7606
+ // Parsing them as an empty list keeps the re-executed packaged artifact
7607
+ // compatible with that legacy socket shape.
7608
+ images: z38.array(z38.object({ data: z38.string(), mimeType: z38.string() })).default([]),
7609
+ isError: z38.boolean().optional()
7610
+ }),
7611
+ z38.object({ ok: z38.literal(false), error: z38.string() })
7612
+ ]);
7613
+ var nextMcpToolCallId = 0;
7614
+ var TOOL_CALL_PROGRESS_INTERVAL_MS = 15e3;
7615
+ function buildAcpMcpServerConfig(args) {
7616
+ return {
7617
+ name: ACP_BRIDGE_MCP_SERVER_NAME,
7618
+ command: args.command,
7619
+ args: args.bridgeArgs,
7620
+ env: [
7621
+ ...args.runtimeEnv,
7622
+ { name: ENV_HOST, value: args.host },
7623
+ { name: ENV_PORT, value: String(args.port) },
7624
+ { name: ENV_TOKEN, value: args.token },
7625
+ { name: ENV_THREAD_ID, value: args.threadId },
7626
+ { name: ENV_TOOLS, value: JSON.stringify(args.dynamicTools) }
7627
+ ]
7628
+ };
7629
+ }
7630
+ function readEnvironment() {
7631
+ const port = Number(process.env[ENV_PORT]);
7632
+ if (!Number.isInteger(port) || port <= 0) {
7633
+ throw new Error(`${ENV_PORT} must be a positive integer`);
7662
7634
  }
7663
- return models;
7635
+ const host = process.env[ENV_HOST];
7636
+ const token = process.env[ENV_TOKEN];
7637
+ const threadId = process.env[ENV_THREAD_ID];
7638
+ const toolsJson = process.env[ENV_TOOLS];
7639
+ if (!host || !token || !threadId || !toolsJson) {
7640
+ throw new Error("Missing ACP dynamic tool MCP server environment");
7641
+ }
7642
+ const parsedTools = JSON.parse(toolsJson);
7643
+ const tools = dynamicToolSchema.array().parse(parsedTools);
7644
+ const rawProgressInterval = process.env[ENV_PROGRESS_INTERVAL_MS];
7645
+ const progressIntervalMs = rawProgressInterval !== void 0 && Number(rawProgressInterval) > 0 ? Number(rawProgressInterval) : void 0;
7646
+ return {
7647
+ host,
7648
+ port,
7649
+ progressIntervalMs,
7650
+ threadId,
7651
+ token,
7652
+ tools
7653
+ };
7664
7654
  }
7665
- function findAcpModelConfigOption(configOptions) {
7666
- const options = configOptions ?? [];
7667
- return options.find((option) => option.category === "model") ?? options.find((option) => option.id === "model");
7655
+ function writeJson(message) {
7656
+ process.stdout.write(`${JSON.stringify(message)}
7657
+ `);
7668
7658
  }
7669
- function findAcpThoughtLevelConfigOption(configOptions) {
7670
- return (configOptions ?? []).find(
7671
- (option) => option.category === "thought_level"
7672
- );
7659
+ function writeResult(id, result) {
7660
+ writeJson({ jsonrpc: "2.0", id, result });
7673
7661
  }
7674
- var ACP_NATIVE_REASONING_LEVEL_BY_VALUE = {
7675
- none: "none",
7676
- minimal: "low",
7677
- low: "low",
7678
- medium: "medium",
7679
- high: "high",
7680
- xhigh: "xhigh",
7681
- ultracode: "ultracode",
7682
- max: "max",
7683
- ultra: "ultra"
7684
- };
7685
- var ACP_NATIVE_REASONING_VALUE_CANDIDATES_BY_LEVEL = {
7686
- none: ["none"],
7687
- low: ["low", "minimal"],
7688
- medium: ["medium"],
7689
- high: ["high"],
7690
- xhigh: ["xhigh"],
7691
- ultracode: ["ultracode", "xhigh"],
7692
- max: ["max", "xhigh"],
7693
- ultra: ["ultra", "max"]
7694
- };
7695
- function acpNativeValueToReasoningLevel(value) {
7696
- return value === void 0 ? void 0 : ACP_NATIVE_REASONING_LEVEL_BY_VALUE[value];
7662
+ function writeError(id, code, message) {
7663
+ writeJson({ jsonrpc: "2.0", id, error: { code, message } });
7664
+ }
7665
+ function mcpToolCallId(toolName) {
7666
+ nextMcpToolCallId += 1;
7667
+ return `acp-mcp-${toolName}-${Date.now()}-${nextMcpToolCallId}`;
7668
+ }
7669
+ function callBridge(env, request) {
7670
+ return new Promise((resolve4, reject) => {
7671
+ const socket = createConnection({ host: env.host, port: env.port });
7672
+ let buffer = "";
7673
+ socket.setEncoding("utf8");
7674
+ socket.on("connect", () => {
7675
+ const payload = {
7676
+ ...request,
7677
+ threadId: env.threadId,
7678
+ token: env.token
7679
+ };
7680
+ socket.write(`${JSON.stringify(payload)}
7681
+ `);
7682
+ });
7683
+ socket.on("data", (chunk) => {
7684
+ buffer += chunk;
7685
+ const newlineIndex = buffer.indexOf("\n");
7686
+ if (newlineIndex === -1) {
7687
+ return;
7688
+ }
7689
+ const line = buffer.slice(0, newlineIndex);
7690
+ socket.end();
7691
+ try {
7692
+ resolve4(bridgeToolCallResponseSchema.parse(JSON.parse(line)));
7693
+ } catch (error) {
7694
+ reject(error);
7695
+ }
7696
+ });
7697
+ socket.on("error", reject);
7698
+ socket.on("end", () => {
7699
+ if (!buffer.includes("\n")) {
7700
+ reject(new Error("ACP dynamic tool bridge closed without a response"));
7701
+ }
7702
+ });
7703
+ });
7697
7704
  }
7698
- function acpNativeReasoningLevelToValue(level, thoughtLevelOption) {
7699
- const candidateValues = ACP_NATIVE_REASONING_VALUE_CANDIDATES_BY_LEVEL[level];
7700
- if (candidateValues === void 0) {
7701
- return void 0;
7702
- }
7703
- const values = new Set(
7704
- (thoughtLevelOption.options ?? []).map((o) => o.value)
7705
- );
7706
- return candidateValues.find((value) => values.has(value));
7705
+ function objectParams(params) {
7706
+ return params && typeof params === "object" && !Array.isArray(params) ? params : {};
7707
7707
  }
7708
- function buildAcpNativeReasoningSupport(thoughtLevelOption) {
7709
- const options = thoughtLevelOption?.options ?? [];
7710
- const seen = /* @__PURE__ */ new Set();
7711
- const matchedValueByLevel = /* @__PURE__ */ new Map();
7712
- const supportedReasoningEfforts = [];
7713
- for (const option of options) {
7714
- const level = acpNativeValueToReasoningLevel(option.value);
7715
- if (level === void 0) {
7716
- continue;
7717
- }
7718
- if (seen.has(level)) {
7719
- const previousValue = matchedValueByLevel.get(level);
7720
- if (previousValue !== level && option.value === level) {
7721
- const effort = supportedReasoningEfforts.find(
7722
- (candidate) => candidate.reasoningEffort === level
7708
+ function readProgressToken(params) {
7709
+ const meta = objectParams(objectParams(params)._meta).progressToken;
7710
+ return typeof meta === "string" || typeof meta === "number" ? meta : null;
7711
+ }
7712
+ function startProgressHeartbeat(args) {
7713
+ let progress = 0;
7714
+ const timer = setInterval(() => {
7715
+ progress += 1;
7716
+ writeJson({
7717
+ jsonrpc: "2.0",
7718
+ method: "notifications/progress",
7719
+ params: { progressToken: args.progressToken, progress }
7720
+ });
7721
+ }, args.intervalMs ?? TOOL_CALL_PROGRESS_INTERVAL_MS);
7722
+ return () => clearInterval(timer);
7723
+ }
7724
+ async function handleRequest(env, message) {
7725
+ if (message.id === void 0 || message.method === void 0) {
7726
+ return;
7727
+ }
7728
+ switch (message.method) {
7729
+ case "initialize":
7730
+ writeResult(message.id, {
7731
+ protocolVersion: typeof objectParams(message.params).protocolVersion === "string" ? objectParams(message.params).protocolVersion : "2024-11-05",
7732
+ capabilities: { tools: {} },
7733
+ serverInfo: { name: ACP_BRIDGE_MCP_SERVER_NAME, version: "1.0.0" }
7734
+ });
7735
+ void callBridge(env, {
7736
+ kind: "initialized",
7737
+ toolCount: env.tools.length
7738
+ }).catch((error) => {
7739
+ process.stderr.write(
7740
+ `bb-bridge MCP: failed to report initialize: ${error instanceof Error ? error.message : String(error)}
7741
+ `
7723
7742
  );
7724
- if (effort) {
7725
- effort.description = option.name ?? option.value;
7743
+ });
7744
+ return;
7745
+ case "tools/list":
7746
+ writeResult(message.id, {
7747
+ tools: env.tools.map((tool) => ({
7748
+ name: tool.name,
7749
+ description: tool.description,
7750
+ inputSchema: tool.inputSchema
7751
+ }))
7752
+ });
7753
+ return;
7754
+ case "tools/call": {
7755
+ const params = objectParams(message.params);
7756
+ const name = typeof params.name === "string" ? params.name : "";
7757
+ const tool = env.tools.find((candidate) => candidate.name === name);
7758
+ if (!tool) {
7759
+ writeError(message.id, -32602, `Unknown tool: ${name}`);
7760
+ return;
7761
+ }
7762
+ const rawArguments = params.arguments;
7763
+ const toolArguments = rawArguments && typeof rawArguments === "object" && !Array.isArray(rawArguments) ? rawArguments : {};
7764
+ const progressToken = readProgressToken(message.params);
7765
+ const stopHeartbeat = progressToken === null ? () => {
7766
+ } : startProgressHeartbeat({
7767
+ intervalMs: env.progressIntervalMs,
7768
+ progressToken
7769
+ });
7770
+ try {
7771
+ const result = await callBridge(env, {
7772
+ kind: "toolCall",
7773
+ arguments: toolArguments,
7774
+ callId: mcpToolCallId(tool.name),
7775
+ tool: tool.name
7776
+ });
7777
+ stopHeartbeat();
7778
+ if (!result.ok) {
7779
+ writeResult(message.id, {
7780
+ content: [{ type: "text", text: result.error }],
7781
+ isError: true
7782
+ });
7783
+ return;
7726
7784
  }
7727
- matchedValueByLevel.set(level, option.value);
7785
+ writeResult(message.id, {
7786
+ content: buildBridgeToolCallContent(result),
7787
+ ...result.isError ? { isError: true } : {}
7788
+ });
7789
+ } catch (error) {
7790
+ stopHeartbeat();
7791
+ writeResult(message.id, {
7792
+ content: [
7793
+ {
7794
+ type: "text",
7795
+ text: error instanceof Error ? error.message : String(error)
7796
+ }
7797
+ ],
7798
+ isError: true
7799
+ });
7728
7800
  }
7729
- continue;
7801
+ return;
7730
7802
  }
7731
- seen.add(level);
7732
- matchedValueByLevel.set(level, option.value);
7733
- supportedReasoningEfforts.push({
7734
- reasoningEffort: level,
7735
- description: option.name ?? option.value
7736
- });
7737
- }
7738
- supportedReasoningEfforts.sort(
7739
- (a, b) => reasoningLevelValues.indexOf(a.reasoningEffort) - reasoningLevelValues.indexOf(b.reasoningEffort)
7740
- );
7741
- if (supportedReasoningEfforts.length === 0) {
7742
- return {
7743
- // An omitted option preserves the legacy agent-managed fallback. A
7744
- // declared option is authoritative, even when bb cannot map its values.
7745
- supportedReasoningEfforts: thoughtLevelOption === void 0 ? ACP_NATIVE_REASONING_EFFORTS : [],
7746
- defaultReasoningEffort: "medium"
7747
- };
7803
+ default:
7804
+ writeError(
7805
+ message.id,
7806
+ -32601,
7807
+ `Unsupported MCP method: ${message.method}`
7808
+ );
7748
7809
  }
7749
- const currentLevel = acpNativeValueToReasoningLevel(
7750
- thoughtLevelOption?.currentValue
7751
- );
7752
- const supportedLevels = supportedReasoningEfforts.map(
7753
- (effort) => effort.reasoningEffort
7754
- );
7755
- return {
7756
- supportedReasoningEfforts,
7757
- defaultReasoningEffort: currentLevel !== void 0 && supportedLevels.includes(currentLevel) ? currentLevel : supportedReasoningEfforts[0].reasoningEffort
7758
- };
7759
7810
  }
7760
- function buildModelCatalogFromConfigOptions(modelOption, reasoningByModel) {
7761
- const options = modelOption?.options ?? [];
7762
- if (options.length === 0) {
7763
- return [];
7764
- }
7765
- const currentValue = modelOption?.currentValue;
7766
- const models = options.map((option, index) => {
7767
- const isDefault = currentValue !== void 0 ? option.value === currentValue : index === 0;
7768
- const reasoning = reasoningByModel?.get(option.value) ?? {
7769
- supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
7770
- defaultReasoningEffort: "medium"
7771
- };
7772
- return {
7773
- id: option.value,
7774
- model: option.value,
7775
- displayName: option.name ?? option.value,
7776
- description: "",
7777
- supportedReasoningEfforts: reasoning.supportedReasoningEfforts,
7778
- defaultReasoningEffort: reasoning.defaultReasoningEffort,
7779
- isDefault
7780
- };
7811
+ function runAcpDynamicToolMcpServer() {
7812
+ const env = readEnvironment();
7813
+ const rl = createInterface2({ input: process.stdin, terminal: false });
7814
+ rl.on("line", (line) => {
7815
+ const trimmed = line.trim();
7816
+ if (!trimmed) {
7817
+ return;
7818
+ }
7819
+ let message;
7820
+ try {
7821
+ message = JSON.parse(trimmed);
7822
+ } catch {
7823
+ return;
7824
+ }
7825
+ void handleRequest(env, message);
7781
7826
  });
7782
- return models.some((model) => model.isDefault) ? models : models.map(
7783
- (model, index) => index === 0 ? { ...model, isDefault: true } : model
7784
- );
7785
7827
  }
7786
- function buildModelCatalogFromSessionModels(sessionModels) {
7787
- const availableModels = sessionModels?.availableModels ?? [];
7788
- if (availableModels.length === 0) {
7789
- return [];
7790
- }
7791
- const currentModelId = sessionModels?.currentModelId;
7792
- const models = availableModels.map((model, index) => {
7793
- const isDefault = currentModelId !== void 0 ? model.modelId === currentModelId : index === 0;
7794
- return {
7795
- id: model.modelId,
7796
- model: model.modelId,
7797
- displayName: model.name ?? model.modelId,
7798
- description: model.description ?? "",
7799
- supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
7800
- defaultReasoningEffort: "medium",
7801
- isDefault
7802
- };
7803
- });
7804
- return models.some((model) => model.isDefault) ? models : models.map(
7805
- (model, index) => index === 0 ? { ...model, isDefault: true } : model
7806
- );
7828
+
7829
+ // ../provider-bridge-acp/src/bridge/cursor-mcp-approval.ts
7830
+ var CURSOR_MCP_APPROVAL_FILE = "mcp-approvals.json";
7831
+ var CURSOR_APPROVAL_LOCK_STALE_MS = 3e4;
7832
+ var CURSOR_APPROVAL_LOCK_TIMEOUT_MS = 5e3;
7833
+ function errorCode(error) {
7834
+ return error instanceof Error && "code" in error ? error.code : void 0;
7807
7835
  }
7808
- function splitVariant(id) {
7809
- let rest = id;
7810
- let fast = false;
7811
- if (rest.endsWith(FAST_TAIL)) {
7812
- fast = true;
7813
- rest = rest.slice(0, -FAST_TAIL.length);
7814
- }
7815
- let thinking = false;
7816
- if (rest.endsWith(`-${THINKING_TOKEN}`)) {
7817
- thinking = true;
7818
- rest = rest.slice(0, -(THINKING_TOKEN.length + 1));
7819
- } else if (rest.includes(`-${THINKING_TOKEN}-`)) {
7820
- thinking = true;
7821
- rest = rest.replace(`-${THINKING_TOKEN}-`, "-");
7822
- }
7823
- for (const [token, effort] of EFFORT_TOKENS) {
7824
- if (rest.endsWith(`-${token}`)) {
7825
- return {
7826
- familyKey: rest.slice(0, -(token.length + 1)),
7827
- effort,
7828
- effortToken: token,
7829
- fast,
7830
- thinking
7831
- };
7832
- }
7836
+ function cursorAgentCommand(command) {
7837
+ return basename2(command).toLowerCase().replace(/\.(?:bat|cmd|exe)$/u, "") === "cursor-agent";
7838
+ }
7839
+ function cursorProjectSlug(projectRoot) {
7840
+ return projectRoot.replace(/[^a-zA-Z0-9]/gu, "-").replace(/-+/gu, "-").replace(/^-+|-+$/gu, "");
7841
+ }
7842
+ function cursorDataDirectory(env) {
7843
+ const configured = env.CURSOR_DATA_DIR?.trim();
7844
+ if (configured) {
7845
+ return configured;
7833
7846
  }
7847
+ const home = env.HOME?.trim() || env.USERPROFILE?.trim() || homedir();
7848
+ return join2(home, ".cursor");
7849
+ }
7850
+ function cursorMcpServerConfig(config) {
7834
7851
  return {
7835
- familyKey: rest,
7836
- effort: "medium",
7837
- effortToken: void 0,
7838
- fast,
7839
- thinking
7852
+ command: config.command,
7853
+ args: config.args,
7854
+ env: Object.fromEntries(config.env.map(({ name, value }) => [name, value]))
7840
7855
  };
7841
7856
  }
7842
- var EFFORT_DISPLAY_WORDS = {
7843
- "extra-high": "Extra High",
7844
- medium: "Medium",
7845
- xhigh: "Extra High",
7846
- high: "High",
7847
- low: "Low",
7848
- max: "Max",
7849
- ultra: "Ultra",
7850
- none: "None"
7851
- };
7852
- function familyDisplayName(displayName, effortToken) {
7853
- const word = effortToken ? EFFORT_DISPLAY_WORDS[effortToken] : void 0;
7854
- if (!word) {
7855
- return cleanDisplayName(displayName);
7856
- }
7857
- return cleanDisplayName(
7858
- displayName.replace(new RegExp(`(^|\\s)${word}(?=\\s|$)`), "$1")
7859
- );
7860
- }
7861
- function cleanDisplayName(name) {
7862
- return name.replace(/\s*\((?:NO ZDR|default|current)\)/gi, "").replace(/(^|\s)(?:1M|Thinking)(?=\s|$)/g, "$1").replace(/\s{2,}/g, " ").trim();
7857
+ function buildCursorMcpApprovalIdentifier(args) {
7858
+ const fingerprint = createHash("sha256").update(
7859
+ JSON.stringify({
7860
+ path: args.projectRoot,
7861
+ server: cursorMcpServerConfig(args.config)
7862
+ })
7863
+ ).digest("hex").slice(0, 16);
7864
+ return `${args.config.name}-${fingerprint}`;
7863
7865
  }
7864
- function buildAgentModelCatalog(rawModels) {
7865
- const families = /* @__PURE__ */ new Map();
7866
- for (const raw of rawModels) {
7867
- const { familyKey, effort, effortToken, fast, thinking } = splitVariant(
7868
- raw.id
7869
- );
7870
- const members = families.get(familyKey) ?? [];
7871
- members.push({ ...raw, effort, effortToken, fast, thinking });
7872
- families.set(familyKey, members);
7873
- }
7874
- if (families.size === 0) {
7875
- return null;
7876
- }
7877
- const models = [];
7878
- const variantsByFamilyId = /* @__PURE__ */ new Map();
7879
- const defaultEffortByFamilyId = /* @__PURE__ */ new Map();
7880
- for (const members of families.values()) {
7881
- const hasThinking = members.some((m) => m.thinking);
7882
- const leveled = members.map((member) => ({
7883
- member,
7884
- level: member.thinking ? member.effort : hasThinking ? "none" : member.effort
7885
- }));
7886
- const byLevel = /* @__PURE__ */ new Map();
7887
- const repEffortByCell = /* @__PURE__ */ new Map();
7888
- for (const { member, level } of leveled) {
7889
- const slot = member.fast ? "fast" : "normal";
7890
- const tier = byLevel.get(level) ?? {};
7891
- const cellKey = `${level}:${slot}`;
7892
- const upgradesNoneRep = level === "none" && member.effort === "medium" && repEffortByCell.get(cellKey) !== "medium";
7893
- if (tier[slot] === void 0 || upgradesNoneRep) {
7894
- tier[slot] = member.id;
7895
- repEffortByCell.set(cellKey, member.effort);
7896
- byLevel.set(level, tier);
7866
+ async function resolveCursorProjectRoot(args) {
7867
+ return new Promise((resolveRoot) => {
7868
+ execFile3(
7869
+ "git",
7870
+ ["rev-parse", "--show-toplevel"],
7871
+ { cwd: args.cwd, env: args.env, windowsHide: true },
7872
+ (error, stdout) => {
7873
+ const root = stdout.trim();
7874
+ resolveRoot(error === null && root !== "" ? root : resolve2(args.cwd));
7897
7875
  }
7898
- }
7899
- const nonFast = leveled.filter((entry) => !entry.member.fast);
7900
- const pool = nonFast.length > 0 ? nonFast : leveled;
7901
- const defaultEntry = pool.find((entry) => entry.level === "medium") ?? pool.find((entry) => entry.level !== "none") ?? pool[0];
7902
- const defaultVariant = defaultEntry.member;
7903
- const levelsInLadderOrder = [...byLevel.keys()].sort(
7904
- (a, b) => reasoningLevelValues.indexOf(a) - reasoningLevelValues.indexOf(b)
7905
7876
  );
7906
- const nameByLevel = /* @__PURE__ */ new Map();
7907
- for (const { member, level } of leveled) {
7908
- if (!nameByLevel.has(level)) {
7909
- nameByLevel.set(level, member.displayName);
7910
- }
7877
+ });
7878
+ }
7879
+ async function readApprovals(path5) {
7880
+ let text;
7881
+ try {
7882
+ text = await readFile(path5, "utf8");
7883
+ } catch (error) {
7884
+ if (errorCode(error) === "ENOENT") {
7885
+ return [];
7911
7886
  }
7912
- models.push({
7913
- id: defaultVariant.id,
7914
- model: defaultVariant.id,
7915
- displayName: familyDisplayName(
7916
- defaultVariant.displayName,
7917
- defaultVariant.effortToken
7918
- ),
7919
- description: "",
7920
- supportedReasoningEfforts: levelsInLadderOrder.map((level) => ({
7921
- reasoningEffort: level,
7922
- description: nameByLevel.get(level) ?? ""
7923
- })),
7924
- defaultReasoningEffort: defaultEntry.level,
7925
- // The agent lists its default model first.
7926
- isDefault: models.length === 0
7887
+ throw error;
7888
+ }
7889
+ const value = JSON.parse(text);
7890
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
7891
+ throw new Error(`Cursor MCP approval file is not a string array: ${path5}`);
7892
+ }
7893
+ return value;
7894
+ }
7895
+ async function writeApprovals(path5, approvals) {
7896
+ await mkdir(dirname(path5), { recursive: true });
7897
+ const tempPath = `${path5}.bb-${process.pid}-${randomBytes(6).toString("hex")}.tmp`;
7898
+ try {
7899
+ await writeFile(tempPath, `${JSON.stringify(approvals, null, 2)}
7900
+ `, {
7901
+ encoding: "utf8",
7902
+ mode: 384
7927
7903
  });
7928
- variantsByFamilyId.set(defaultVariant.id, byLevel);
7929
- defaultEffortByFamilyId.set(defaultVariant.id, defaultEntry.level);
7904
+ await rename(tempPath, path5);
7905
+ } catch (error) {
7906
+ await rm(tempPath, { force: true });
7907
+ throw error;
7930
7908
  }
7931
- return {
7932
- models,
7933
- resolveVariant({ model, reasoningLevel, serviceTier }) {
7934
- const byLevel = variantsByFamilyId.get(model);
7935
- if (!byLevel) {
7936
- return void 0;
7909
+ }
7910
+ async function acquireApprovalLock(path5) {
7911
+ const lockPath = `${path5}.bb-lock`;
7912
+ const deadline = Date.now() + CURSOR_APPROVAL_LOCK_TIMEOUT_MS;
7913
+ await mkdir(dirname(path5), { recursive: true });
7914
+ for (; ; ) {
7915
+ try {
7916
+ await mkdir(lockPath, { mode: 448 });
7917
+ return () => rm(lockPath, { recursive: true, force: true });
7918
+ } catch (error) {
7919
+ if (errorCode(error) !== "EEXIST") {
7920
+ throw error;
7937
7921
  }
7938
- const level = reasoningLevel ?? defaultEffortByFamilyId.get(model);
7939
- const tier = level === void 0 ? void 0 : byLevel.get(level);
7940
- if (!tier) {
7941
- return void 0;
7922
+ }
7923
+ try {
7924
+ const lockStat = await stat(lockPath);
7925
+ if (Date.now() - lockStat.mtimeMs > CURSOR_APPROVAL_LOCK_STALE_MS) {
7926
+ await rm(lockPath, { recursive: true, force: true });
7927
+ continue;
7942
7928
  }
7943
- if (serviceTier === "fast" && tier.fast !== void 0) {
7944
- return tier.fast;
7929
+ } catch (error) {
7930
+ if (errorCode(error) === "ENOENT") {
7931
+ continue;
7945
7932
  }
7946
- return tier.normal ?? tier.fast;
7933
+ throw error;
7947
7934
  }
7948
- };
7935
+ if (Date.now() >= deadline) {
7936
+ throw new Error(`Timed out updating Cursor MCP approvals: ${path5}`);
7937
+ }
7938
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 25));
7939
+ }
7949
7940
  }
7950
- function splitPrimaryModels(catalogModels, primaryModels) {
7951
- const primaryIds = new Set(primaryModels);
7952
- const modelsById = new Map(catalogModels.map((model) => [model.id, model]));
7953
- const models = primaryModels.flatMap((id) => {
7954
- const model = modelsById.get(id);
7955
- return model ? [model] : [];
7956
- });
7957
- if (models.length === 0) {
7958
- return { models: [...catalogModels], selectedOnlyModels: [] };
7941
+ async function mutateApprovals(path5, mutate) {
7942
+ const releaseLock = await acquireApprovalLock(path5);
7943
+ try {
7944
+ const approvals = await readApprovals(path5);
7945
+ const nextApprovals = mutate(approvals);
7946
+ if (approvals.length !== nextApprovals.length || approvals.some((approval, index) => approval !== nextApprovals[index])) {
7947
+ await writeApprovals(path5, nextApprovals);
7948
+ }
7949
+ } finally {
7950
+ await releaseLock();
7959
7951
  }
7960
- const selectedOnlyModels = catalogModels.filter(
7961
- (model) => !primaryIds.has(model.id)
7952
+ }
7953
+ async function approveCursorSessionMcpServer(args) {
7954
+ if (!cursorAgentCommand(args.agentCommand) || args.config.name !== ACP_BRIDGE_MCP_SERVER_NAME) {
7955
+ return void 0;
7956
+ }
7957
+ const projectRoot = await resolveCursorProjectRoot({
7958
+ cwd: args.cwd,
7959
+ env: args.env
7960
+ });
7961
+ const path5 = join2(
7962
+ cursorDataDirectory(args.env),
7963
+ "projects",
7964
+ cursorProjectSlug(projectRoot),
7965
+ CURSOR_MCP_APPROVAL_FILE
7962
7966
  );
7963
- if (models.some((model) => model.isDefault)) {
7964
- return {
7965
- models,
7966
- selectedOnlyModels: selectedOnlyModels.map(
7967
- (model) => model.isDefault ? { ...model, isDefault: false } : model
7968
- )
7969
- };
7967
+ const approval = buildCursorMcpApprovalIdentifier({
7968
+ config: args.config,
7969
+ projectRoot
7970
+ });
7971
+ let installedByBb = false;
7972
+ await mutateApprovals(path5, (approvals) => {
7973
+ if (approvals.includes(approval)) {
7974
+ return approvals;
7975
+ }
7976
+ installedByBb = true;
7977
+ return [...approvals, approval];
7978
+ });
7979
+ return { approval, installedByBb, path: path5 };
7980
+ }
7981
+ async function revokeCursorSessionMcpServer(approval) {
7982
+ if (!approval.installedByBb) {
7983
+ return;
7970
7984
  }
7971
- return {
7972
- models: models.map(
7973
- (model, index) => index === 0 ? { ...model, isDefault: true } : model
7974
- ),
7975
- selectedOnlyModels: selectedOnlyModels.map(
7976
- (model) => model.isDefault ? { ...model, isDefault: false } : model
7977
- )
7978
- };
7985
+ await mutateApprovals(
7986
+ approval.path,
7987
+ (approvals) => approvals.filter((candidate) => candidate !== approval.approval)
7988
+ );
7979
7989
  }
7980
7990
 
7981
7991
  // ../provider-bridge-acp/src/bridge/bridge.ts