@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.
- package/dist/provider-bridge-acp.js +1069 -1059
- package/dist/provider-bridge-testing.js +2 -2
- package/package.json +1 -1
|
@@ -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
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
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
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
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
|
|
6834
|
-
const
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
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
|
-
|
|
6842
|
-
|
|
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
|
|
6845
|
-
|
|
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
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
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
|
|
6856
|
-
|
|
6857
|
-
|
|
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
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
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
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
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
|
|
6881
|
-
|
|
6933
|
+
const currentLevel = acpNativeValueToReasoningLevel(
|
|
6934
|
+
thoughtLevelOption?.currentValue
|
|
6935
|
+
);
|
|
6936
|
+
const supportedLevels = supportedReasoningEfforts.map(
|
|
6937
|
+
(effort) => effort.reasoningEffort
|
|
6938
|
+
);
|
|
6882
6939
|
return {
|
|
6883
|
-
|
|
6884
|
-
|
|
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
|
|
6893
|
-
const
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
return {};
|
|
6944
|
+
function buildModelCatalogFromConfigOptions(modelOption, reasoningByModel) {
|
|
6945
|
+
const options = modelOption?.options ?? [];
|
|
6946
|
+
if (options.length === 0) {
|
|
6947
|
+
return [];
|
|
6897
6948
|
}
|
|
6898
|
-
|
|
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
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
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
|
-
|
|
6909
|
-
|
|
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
|
|
6919
|
-
const
|
|
6920
|
-
|
|
6921
|
-
|
|
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
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
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
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
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
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
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
|
-
|
|
6977
|
-
|
|
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
|
|
6980
|
-
|
|
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
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
return
|
|
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
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
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 (
|
|
7061
|
+
if (families.size === 0) {
|
|
7012
7062
|
return null;
|
|
7013
7063
|
}
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
const
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
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
|
-
|
|
7040
|
-
const
|
|
7041
|
-
const
|
|
7042
|
-
|
|
7043
|
-
|
|
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
|
-
|
|
7046
|
-
const
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
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
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
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
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
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
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
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
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
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
|
-
|
|
7155
|
-
});
|
|
7156
|
+
};
|
|
7157
|
+
}
|
|
7156
7158
|
return {
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
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/
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
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
|
|
7262
|
-
|
|
7263
|
-
|
|
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
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
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
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
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
|
|
7294
|
-
|
|
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
|
|
7297
|
-
|
|
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
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
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
|
|
7337
|
-
|
|
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
|
|
7340
|
-
const
|
|
7341
|
-
|
|
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
|
|
7344
|
-
|
|
7345
|
-
|
|
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
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
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
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
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
|
-
|
|
7433
|
-
}
|
|
7434
|
-
default:
|
|
7435
|
-
writeError(
|
|
7436
|
-
message.id,
|
|
7437
|
-
-32601,
|
|
7438
|
-
`Unsupported MCP method: ${message.method}`
|
|
7439
|
-
);
|
|
7273
|
+
};
|
|
7440
7274
|
}
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
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
|
-
|
|
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
|
|
7474
|
-
const
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
}
|
|
7481
|
-
|
|
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
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
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
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
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
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
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
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
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
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
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
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
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
|
-
|
|
7542
|
-
const
|
|
7543
|
-
|
|
7544
|
-
|
|
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
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
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
|
-
|
|
7593
|
-
|
|
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
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
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
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
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
|
-
|
|
7656
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7661
|
-
|
|
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
|
-
|
|
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
|
|
7666
|
-
|
|
7667
|
-
|
|
7655
|
+
function writeJson(message) {
|
|
7656
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
7657
|
+
`);
|
|
7668
7658
|
}
|
|
7669
|
-
function
|
|
7670
|
-
|
|
7671
|
-
(option) => option.category === "thought_level"
|
|
7672
|
-
);
|
|
7659
|
+
function writeResult(id, result) {
|
|
7660
|
+
writeJson({ jsonrpc: "2.0", id, result });
|
|
7673
7661
|
}
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
};
|
|
7695
|
-
|
|
7696
|
-
|
|
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
|
|
7699
|
-
|
|
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
|
|
7709
|
-
const
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
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
|
-
|
|
7725
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7801
|
+
return;
|
|
7730
7802
|
}
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
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
|
|
7761
|
-
const
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
}
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
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
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
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
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
if (
|
|
7817
|
-
|
|
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
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
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
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
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
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
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
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
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
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
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
|
-
|
|
7929
|
-
|
|
7904
|
+
await rename(tempPath, path5);
|
|
7905
|
+
} catch (error) {
|
|
7906
|
+
await rm(tempPath, { force: true });
|
|
7907
|
+
throw error;
|
|
7930
7908
|
}
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
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
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
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
|
-
|
|
7944
|
-
|
|
7929
|
+
} catch (error) {
|
|
7930
|
+
if (errorCode(error) === "ENOENT") {
|
|
7931
|
+
continue;
|
|
7945
7932
|
}
|
|
7946
|
-
|
|
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
|
|
7951
|
-
const
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
const
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
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
|
-
|
|
7961
|
-
|
|
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
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
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
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
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
|