@byfriends/agent-core 0.3.0 → 0.3.1
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/{index-BfZezIU5.d.mts → index-CZA_2ux5.d.mts} +158 -79
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +915 -404
- package/dist/session/store/index.d.mts +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3032,7 +3032,7 @@ function parseSkillText(options) {
|
|
|
3032
3032
|
throw error;
|
|
3033
3033
|
}
|
|
3034
3034
|
const frontmatter = parsed.data ?? {};
|
|
3035
|
-
if (!isRecord$
|
|
3035
|
+
if (!isRecord$2(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
|
|
3036
3036
|
const metadata = normalizeMetadata(frontmatter);
|
|
3037
3037
|
if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
|
|
3038
3038
|
const name = nonEmptyString$2(metadata.name);
|
|
@@ -3145,7 +3145,7 @@ function tokenizeArgs(raw) {
|
|
|
3145
3145
|
function nonEmptyString$2(value) {
|
|
3146
3146
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
3147
3147
|
}
|
|
3148
|
-
function isRecord$
|
|
3148
|
+
function isRecord$2(value) {
|
|
3149
3149
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3150
3150
|
}
|
|
3151
3151
|
//#endregion
|
|
@@ -3966,12 +3966,12 @@ function joinPath(parent, child, pathClass) {
|
|
|
3966
3966
|
}
|
|
3967
3967
|
//#endregion
|
|
3968
3968
|
//#region src/tools/builtin/file/glob.md
|
|
3969
|
-
var glob_default = "Find files (and optionally directories) by glob pattern, sorted by modification time (most recent first).\n\
|
|
3969
|
+
var glob_default = "Find files (and optionally directories) by glob pattern, sorted by modification time (most recent first).\n\nREJECTED patterns (no literal anchor — will be rejected):\n- **Pure wildcards**: `**`, `**/*`, `*/*` — no literal anchor bounds the result. Add an extension or subdirectory to give the walk a concrete target.\n- **`**/` prefix**: Anything starting with `**/` (e.g. `**/*.py`, `**/main/*.ts`). The leading `**/` has no literal anchor in front of it. Anchor it with a top-level subdirectory like `src/**/*.ts`.\n- **Brace expansion**: `*.{ts,tsx}` is not supported. Split it into separate calls: `*.ts` and `*.tsx`.\n\nGood patterns:\n- `*.ts` — files in the current directory matching an extension\n- `src/**/*.ts` — recursive with a subdirectory anchor and extension\n- `test_*.py` — files whose name starts with a literal prefix\n\nLarge-directory warning — avoid recursing into dependency/build output even with an anchor:\n- `node_modules/**/*.js`, `.venv/**/*.py`, `__pycache__/**`, `target/**` match technically but typically produce thousands of results that truncate at the match cap. Prefer specific subpaths like `node_modules/react/src/**/*.js`.\n\nWhen you need to search the entire project, first use Glob to explore the top-level directory structure, then use an anchored pattern like `src/**/*.ts` or `packages/**/*.ts` to narrow the search.\n";
|
|
3970
3970
|
//#endregion
|
|
3971
3971
|
//#region src/tools/builtin/file/glob.ts
|
|
3972
3972
|
const GlobInputSchema = z.object({
|
|
3973
|
-
pattern: z.string().describe("Glob pattern to match files/directories."),
|
|
3974
|
-
path: z.string().optional().describe("Absolute path to the directory to search in. Defaults to the current working directory."),
|
|
3973
|
+
pattern: z.string().describe("Glob pattern to match files/directories. IMPORTANT: pattern MUST contain a literal anchor like a file extension (.ts) or a subdirectory name (src/). Patterns starting with **/ (e.g. **/*.py, **/main/*.ts), pure wildcards (**, **/*, */*, *), and brace expansion (*.{ts,tsx}) are REJECTED. Example: src/**/*.ts searches all .ts files under src/."),
|
|
3974
|
+
path: z.string().optional().describe("Absolute path to the directory to search in. Defaults to the current working directory. Explicit absolute paths outside the workspace are allowed (e.g. to search a dependency installed elsewhere); relative paths are resolved against the working directory and rejected if they escape it."),
|
|
3975
3975
|
include_dirs: z.boolean().default(true).optional().describe("Whether to include directories in results. Defaults to true. Set false to return only files.")
|
|
3976
3976
|
});
|
|
3977
3977
|
const MAX_MATCHES = 1e3;
|
|
@@ -4013,7 +4013,7 @@ var GlobTool = class {
|
|
|
4013
4013
|
workspace: this.workspace,
|
|
4014
4014
|
operation: "search",
|
|
4015
4015
|
policy: {
|
|
4016
|
-
guardMode: "
|
|
4016
|
+
guardMode: "absolute-outside-allowed",
|
|
4017
4017
|
checkSensitive: false
|
|
4018
4018
|
}
|
|
4019
4019
|
});
|
|
@@ -4047,7 +4047,7 @@ var GlobTool = class {
|
|
|
4047
4047
|
}
|
|
4048
4048
|
return {
|
|
4049
4049
|
isError: true,
|
|
4050
|
-
output: `Pattern "${args.pattern}" is a pure wildcard (only \`*\`, \`?\`, \`**\`, \`/\`) and would enumerate every file under the search root — with no literal anchor to bound the result set, this typically exhausts your context on large trees. Add an extension ("${args.pattern === "**" || args.pattern === "**/*" ? "**/*.ts" : "**/*.md"}") or a subdirectory ("src/**/*.ts") to constrain the walk.\n\
|
|
4050
|
+
output: `Pattern "${args.pattern}" is a pure wildcard (only \`*\`, \`?\`, \`**\`, \`/\`) and would enumerate every file under the search root — with no literal anchor to bound the result set, this typically exhausts your context on large trees. Add an extension ("${args.pattern === "**" || args.pattern === "**/*" ? "**/*.ts" : "**/*.md"}") or a subdirectory ("src/**/*.ts") to constrain the walk.\n\nWorkspace roots:\n${rootList}\n\nTop of ${this.workspace.workspaceDir}:\n${tree}`
|
|
4051
4051
|
};
|
|
4052
4052
|
}
|
|
4053
4053
|
if (containsBraceExpansion(args.pattern)) return {
|
|
@@ -4075,6 +4075,8 @@ var GlobTool = class {
|
|
|
4075
4075
|
try {
|
|
4076
4076
|
const seen = /* @__PURE__ */ new Set();
|
|
4077
4077
|
const entries = [];
|
|
4078
|
+
const pathClass = this.kaos.pathClass();
|
|
4079
|
+
const filteredSensitive = [];
|
|
4078
4080
|
const YIELD_SAFETY_CAP = MAX_MATCHES * 2;
|
|
4079
4081
|
let yielded = 0;
|
|
4080
4082
|
let truncated = false;
|
|
@@ -4090,6 +4092,10 @@ var GlobTool = class {
|
|
|
4090
4092
|
break outer;
|
|
4091
4093
|
}
|
|
4092
4094
|
seen.add(filePath);
|
|
4095
|
+
if (isSensitiveFile(filePath, pathClass)) {
|
|
4096
|
+
filteredSensitive.push(filePath);
|
|
4097
|
+
continue;
|
|
4098
|
+
}
|
|
4093
4099
|
let mtime = 0;
|
|
4094
4100
|
let isDir = false;
|
|
4095
4101
|
try {
|
|
@@ -4105,10 +4111,10 @@ var GlobTool = class {
|
|
|
4105
4111
|
}
|
|
4106
4112
|
entries.sort((a, b) => b.mtime - a.mtime);
|
|
4107
4113
|
const paths = entries.map((e) => e.path);
|
|
4108
|
-
const pathClass = this.kaos.pathClass();
|
|
4109
4114
|
const relBase = searchRoots[0] ?? this.workspace.workspaceDir;
|
|
4110
4115
|
const displayLines = paths.map((p) => relativizeIfUnder$1(p, relBase, pathClass));
|
|
4111
|
-
|
|
4116
|
+
const filteredSome = filteredSensitive.length > 0;
|
|
4117
|
+
if (entries.length === 0 && !truncated) return { output: filteredSome ? `No non-sensitive matches found (filtered ${String(filteredSensitive.length)} sensitive file(s))` : "No matches found" };
|
|
4112
4118
|
const lines = [];
|
|
4113
4119
|
if (truncated) {
|
|
4114
4120
|
lines.push(`[Truncated at ${String(MAX_MATCHES)} matches — use a more specific pattern]`);
|
|
@@ -4116,6 +4122,10 @@ var GlobTool = class {
|
|
|
4116
4122
|
}
|
|
4117
4123
|
lines.push(...displayLines);
|
|
4118
4124
|
if (!truncated && entries.length === 1e3) lines.push(`Found ${String(entries.length)} matches`);
|
|
4125
|
+
if (filteredSome) {
|
|
4126
|
+
const displayedFiltered = filteredSensitive.map((p) => relativizeIfUnder$1(p, relBase, pathClass));
|
|
4127
|
+
lines.push(`Filtered ${String(filteredSensitive.length)} sensitive file(s): ${displayedFiltered.join(", ")}`);
|
|
4128
|
+
}
|
|
4119
4129
|
return { output: lines.join("\n") };
|
|
4120
4130
|
} catch (error) {
|
|
4121
4131
|
if (error !== null && typeof error === "object" && "code" in error) {
|
|
@@ -6312,7 +6322,7 @@ function rewriteWindowsNullRedirect$1(command) {
|
|
|
6312
6322
|
}
|
|
6313
6323
|
//#endregion
|
|
6314
6324
|
//#region src/tools/builtin/state/todo-list.md
|
|
6315
|
-
var todo_list_default = "Use this tool to maintain a structured TODO list as you work through a multi-step task.\n\nUse for multi-step tasks, tracking investigation progress, or planning a sequence of edits. Do not use for single-shot answers or trivial requests.\n\n**
|
|
6325
|
+
var todo_list_default = "Use this tool to maintain a structured TODO list as you work through a multi-step task.\n\nUse for multi-step tasks, tracking investigation progress, or planning a sequence of edits. Do not use for single-shot answers or trivial requests.\n\n**Update discipline:**\n- Update status immediately when you start or complete a subtask: mark it `in_progress` when you begin working on it, and `done` when finished.\n- Do not skip the `in_progress` state — the user should always see what you are currently working on.\n- Avoid redundant calls: do not re-call this tool when nothing meaningful has changed since the last call.\n- When unsure of the current state, call query mode first (omit `todos`) to check the list before deciding what to update.\n- If no available tool can move any task forward, tell the user where you are stuck instead of repeatedly re-ordering the same todos.\n\n**How to use:**\n- Call with `todos: [...]` to replace the full list. Statuses: pending / in_progress / done.\n- Call with no arguments to query the current list.\n- Call with `todos: []` to clear the list.\n- Keep titles short and actionable.\n- Update statuses as you make progress — mark one item in_progress at a time.\n";
|
|
6316
6326
|
//#endregion
|
|
6317
6327
|
//#region src/tools/builtin/state/todo-list.ts
|
|
6318
6328
|
/**
|
|
@@ -8211,7 +8221,7 @@ const OptionalStringSchema = z.preprocess((value) => {
|
|
|
8211
8221
|
if (typeof value === "string") return value;
|
|
8212
8222
|
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
8213
8223
|
}, z.string().optional());
|
|
8214
|
-
const HookSpecificOutputSchema = z.preprocess((value) => isRecord(value) ? value : void 0, z.looseObject({
|
|
8224
|
+
const HookSpecificOutputSchema = z.preprocess((value) => isRecord$1(value) ? value : void 0, z.looseObject({
|
|
8215
8225
|
message: OptionalStringSchema,
|
|
8216
8226
|
permissionDecision: z.unknown().optional(),
|
|
8217
8227
|
permissionDecisionReason: OptionalStringSchema
|
|
@@ -8371,7 +8381,7 @@ function tryKillProcess(child, signal) {
|
|
|
8371
8381
|
} catch {}
|
|
8372
8382
|
}
|
|
8373
8383
|
}
|
|
8374
|
-
function isRecord(value) {
|
|
8384
|
+
function isRecord$1(value) {
|
|
8375
8385
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8376
8386
|
}
|
|
8377
8387
|
function errorMessage(error) {
|
|
@@ -11476,8 +11486,7 @@ var AgentRecords = class {
|
|
|
11476
11486
|
tools: "tools",
|
|
11477
11487
|
usage: "usage",
|
|
11478
11488
|
background: "background",
|
|
11479
|
-
full_compaction: "fullCompaction"
|
|
11480
|
-
plan_mode: "planMode"
|
|
11489
|
+
full_compaction: "fullCompaction"
|
|
11481
11490
|
}[recordType.split(".")[0]] ?? null;
|
|
11482
11491
|
}
|
|
11483
11492
|
async replay() {
|
|
@@ -13522,8 +13531,8 @@ var ToolManager = class {
|
|
|
13522
13531
|
return (input) => withProviderRequestAuth(resolveAuth, (auth) => uploadVideo(input, { auth }));
|
|
13523
13532
|
}
|
|
13524
13533
|
get loopTools() {
|
|
13525
|
-
const builtinNames = [...this.builtinTools.keys()].filter((name) => this.enabledTools.has(name)).
|
|
13526
|
-
const userNames = [...this.userTools.keys()].filter((name) => this.enabledTools.has(name)).
|
|
13534
|
+
const builtinNames = [...this.builtinTools.keys()].filter((name) => this.enabledTools.has(name)).toSorted();
|
|
13535
|
+
const userNames = [...this.userTools.keys()].filter((name) => this.enabledTools.has(name)).toSorted();
|
|
13527
13536
|
const mcpNames = [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name));
|
|
13528
13537
|
return [
|
|
13529
13538
|
...builtinNames.map((name) => this.builtinTools.get(name)),
|
|
@@ -13699,7 +13708,7 @@ function findImplicitBoundaries(prompt) {
|
|
|
13699
13708
|
const index = prompt.indexOf(header);
|
|
13700
13709
|
if (index !== -1) boundaries.push(index);
|
|
13701
13710
|
}
|
|
13702
|
-
return boundaries.
|
|
13711
|
+
return boundaries.toSorted((a, b) => a - b);
|
|
13703
13712
|
}
|
|
13704
13713
|
/**
|
|
13705
13714
|
* Split prompt by implicit boundaries into blocks.
|
|
@@ -15124,15 +15133,6 @@ var Agent = class {
|
|
|
15124
15133
|
getModel: () => {
|
|
15125
15134
|
return this.config.modelAlias ?? "";
|
|
15126
15135
|
},
|
|
15127
|
-
enterPlan: async () => {
|
|
15128
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15129
|
-
},
|
|
15130
|
-
cancelPlan: async () => {
|
|
15131
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15132
|
-
},
|
|
15133
|
-
clearPlan: async () => {
|
|
15134
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15135
|
-
},
|
|
15136
15136
|
beginCompaction: (payload) => {
|
|
15137
15137
|
this.fullCompaction.begin({
|
|
15138
15138
|
source: "manual",
|
|
@@ -15167,7 +15167,6 @@ var Agent = class {
|
|
|
15167
15167
|
getContext: () => this.context.data(),
|
|
15168
15168
|
getConfig: () => this.config.data(),
|
|
15169
15169
|
getPermission: () => this.permission.data(),
|
|
15170
|
-
getPlan: async () => null,
|
|
15171
15170
|
getUsage: () => this.usage.data(),
|
|
15172
15171
|
getTools: () => this.tools.data(),
|
|
15173
15172
|
getBackground: (payload) => this.background.list(payload.activeOnly ?? false, payload.limit)
|
|
@@ -15300,6 +15299,21 @@ function proxyWithExtraPayload(methods, extraPayload) {
|
|
|
15300
15299
|
}, ...args);
|
|
15301
15300
|
} });
|
|
15302
15301
|
}
|
|
15302
|
+
/** The Zod `z.enum` literal inferred from registry keys. */
|
|
15303
|
+
const PROVIDER_TYPE_VALUES = Object.keys({
|
|
15304
|
+
exa: { defaultBaseUrl: "https://api.exa.ai/search" },
|
|
15305
|
+
brave: { defaultBaseUrl: "https://api.search.brave.com/res/v1/web/search" },
|
|
15306
|
+
firecrawl: { defaultBaseUrl: "https://api.firecrawl.dev/v2/search" }
|
|
15307
|
+
});
|
|
15308
|
+
const providerClassMap = /* @__PURE__ */ new Map();
|
|
15309
|
+
function registerProvider(type, cls) {
|
|
15310
|
+
providerClassMap.set(type, cls);
|
|
15311
|
+
}
|
|
15312
|
+
function createProvider$1(type, options) {
|
|
15313
|
+
const cls = providerClassMap.get(type);
|
|
15314
|
+
if (cls === void 0) throw new Error(`WebSearch provider type "${type}" is not registered. Did you import the provider module?`);
|
|
15315
|
+
return new cls(options);
|
|
15316
|
+
}
|
|
15303
15317
|
//#endregion
|
|
15304
15318
|
//#region src/config/schema.ts
|
|
15305
15319
|
const ProviderTypeSchema = z.enum([
|
|
@@ -15399,9 +15413,18 @@ const ByfServiceConfigSchema = z.object({
|
|
|
15399
15413
|
oauth: OAuthRefSchema.optional(),
|
|
15400
15414
|
customHeaders: StringRecordSchema.optional()
|
|
15401
15415
|
});
|
|
15416
|
+
/** Provider type enum derived from webSearchProviderRegistry keys. */
|
|
15417
|
+
const WebSearchProviderTypeSchema = z.enum(PROVIDER_TYPE_VALUES);
|
|
15418
|
+
const WebSearchProviderConfigSchema = z.object({
|
|
15419
|
+
type: WebSearchProviderTypeSchema,
|
|
15420
|
+
apiKeys: z.array(z.string().min(1)).nonempty(),
|
|
15421
|
+
baseUrl: z.string().optional(),
|
|
15422
|
+
priority: z.number().int().positive()
|
|
15423
|
+
});
|
|
15424
|
+
const WebSearchConfigSchema = z.object({ providers: z.array(WebSearchProviderConfigSchema).nonempty() });
|
|
15402
15425
|
const ServicesConfigSchema = z.object({
|
|
15403
|
-
|
|
15404
|
-
|
|
15426
|
+
webSearch: WebSearchConfigSchema.optional(),
|
|
15427
|
+
fetchUrl: ByfServiceConfigSchema.optional()
|
|
15405
15428
|
});
|
|
15406
15429
|
const McpServerCommonFields = {
|
|
15407
15430
|
enabled: z.boolean().optional(),
|
|
@@ -15467,8 +15490,8 @@ const LoopControlPatchSchema = LoopControlSchema.partial();
|
|
|
15467
15490
|
const BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
|
|
15468
15491
|
const ByfServiceConfigPatchSchema = ByfServiceConfigSchema.partial();
|
|
15469
15492
|
const ServicesConfigPatchSchema = z.object({
|
|
15470
|
-
|
|
15471
|
-
|
|
15493
|
+
webSearch: WebSearchConfigSchema.optional(),
|
|
15494
|
+
fetchUrl: ByfServiceConfigPatchSchema.optional()
|
|
15472
15495
|
});
|
|
15473
15496
|
const ByfConfigPatchSchema = z.object({
|
|
15474
15497
|
providers: z.record(z.string(), ProviderConfigPatchSchema).optional(),
|
|
@@ -15716,10 +15739,15 @@ function transformServiceData(data) {
|
|
|
15716
15739
|
const targetKey = snakeToCamel(key);
|
|
15717
15740
|
if (targetKey === "oauth") out[targetKey] = isPlainObject(value) ? transformPlainObject(value) : value;
|
|
15718
15741
|
else if (targetKey === "customHeaders") out[targetKey] = cloneObjectValue(value);
|
|
15742
|
+
else if (Array.isArray(value)) out[targetKey] = value.map((item) => isPlainObject(item) ? transformRecord(item, identity, snakeToCamel) : item);
|
|
15743
|
+
else if (isPlainObject(value)) out[targetKey] = transformPlainObject(value);
|
|
15719
15744
|
else out[targetKey] = value;
|
|
15720
15745
|
}
|
|
15721
15746
|
return out;
|
|
15722
15747
|
}
|
|
15748
|
+
function identity(v) {
|
|
15749
|
+
return v;
|
|
15750
|
+
}
|
|
15723
15751
|
function transformLoopControlData(data) {
|
|
15724
15752
|
const out = transformPlainObject(data);
|
|
15725
15753
|
if (out["maxStepsPerTurn"] === void 0 && out["maxStepsPerRun"] !== void 0) out["maxStepsPerTurn"] = out["maxStepsPerRun"];
|
|
@@ -15739,11 +15767,11 @@ function configToTomlData(config) {
|
|
|
15739
15767
|
delete out["default_yolo"];
|
|
15740
15768
|
delete out["defaultYolo"];
|
|
15741
15769
|
delete out["defaultPermissionMode"];
|
|
15770
|
+
delete out["default_thinking"];
|
|
15742
15771
|
for (const key of [
|
|
15743
15772
|
"defaultProvider",
|
|
15744
15773
|
"defaultModel",
|
|
15745
15774
|
"yolo",
|
|
15746
|
-
"defaultThinking",
|
|
15747
15775
|
"defaultPermissionMode",
|
|
15748
15776
|
"mergeAllAvailableSkills",
|
|
15749
15777
|
"extraSkillDirs"
|
|
@@ -15812,10 +15840,17 @@ function permissionRuleToToml(rule) {
|
|
|
15812
15840
|
}
|
|
15813
15841
|
function servicesToToml(services, rawServices) {
|
|
15814
15842
|
const out = cloneRecord(rawServices);
|
|
15815
|
-
if (services.
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15843
|
+
if (services.webSearch !== void 0 && services.webSearch.providers.length > 0) {
|
|
15844
|
+
const providersToml = services.webSearch.providers.map((p) => {
|
|
15845
|
+
const providerOut = {};
|
|
15846
|
+
for (const [key, value] of Object.entries(p)) setDefined(providerOut, camelToSnake(key), value);
|
|
15847
|
+
return providerOut;
|
|
15848
|
+
});
|
|
15849
|
+
out["web_search"] = out["web_search"] ?? {};
|
|
15850
|
+
out["web_search"]["providers"] = providersToml;
|
|
15851
|
+
} else delete out["web_search"];
|
|
15852
|
+
if (services.fetchUrl !== void 0) out["fetch_url"] = serviceToToml(services.fetchUrl);
|
|
15853
|
+
else delete out["fetch_url"];
|
|
15819
15854
|
return out;
|
|
15820
15855
|
}
|
|
15821
15856
|
function serviceToToml(service) {
|
|
@@ -15873,90 +15908,679 @@ function isFileExistsError(error) {
|
|
|
15873
15908
|
return typeof error === "object" && error !== null && error.code === "EEXIST";
|
|
15874
15909
|
}
|
|
15875
15910
|
//#endregion
|
|
15876
|
-
//#region src/
|
|
15877
|
-
|
|
15878
|
-
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15911
|
+
//#region src/config/update-rules.ts
|
|
15912
|
+
const REMOVED_RULES = [
|
|
15913
|
+
{
|
|
15914
|
+
path: "default_yolo",
|
|
15915
|
+
pathParts: ["default_yolo"],
|
|
15916
|
+
kind: "removed",
|
|
15917
|
+
detail: "Top-level field default_yolo is removed. Use yolo instead.",
|
|
15918
|
+
deprecatedSince: "pre-0.1.0"
|
|
15919
|
+
},
|
|
15920
|
+
{
|
|
15921
|
+
path: "defaultYolo",
|
|
15922
|
+
pathParts: ["defaultYolo"],
|
|
15923
|
+
kind: "removed",
|
|
15924
|
+
detail: "Top-level field defaultYolo is removed. Use yolo instead.",
|
|
15925
|
+
deprecatedSince: "pre-0.1.0"
|
|
15926
|
+
},
|
|
15927
|
+
{
|
|
15928
|
+
path: "services.byf_search",
|
|
15929
|
+
pathParts: ["services", "byf_search"],
|
|
15930
|
+
kind: "removed",
|
|
15931
|
+
detail: "Deprecated service byf_search is removed.",
|
|
15932
|
+
deprecatedSince: "pre-0.1.0"
|
|
15933
|
+
},
|
|
15934
|
+
{
|
|
15935
|
+
path: "services.byf_fetch",
|
|
15936
|
+
pathParts: ["services", "byf_fetch"],
|
|
15937
|
+
kind: "removed",
|
|
15938
|
+
detail: "Deprecated service byf_fetch is removed. Use services.fetch_url instead.",
|
|
15939
|
+
deprecatedSince: "pre-0.1.0"
|
|
15884
15940
|
}
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15888
|
-
|
|
15941
|
+
];
|
|
15942
|
+
const RENAMED_RULES = [{
|
|
15943
|
+
path: "loop_control.max_steps_per_run",
|
|
15944
|
+
pathParts: ["loop_control", "max_steps_per_run"],
|
|
15945
|
+
kind: "renamed",
|
|
15946
|
+
detail: "Renamed to max_steps_per_turn.",
|
|
15947
|
+
deprecatedSince: "pre-0.1.0"
|
|
15948
|
+
}];
|
|
15949
|
+
const MIGRATED_RULES = [{
|
|
15950
|
+
path: "default_thinking",
|
|
15951
|
+
pathParts: ["default_thinking"],
|
|
15952
|
+
kind: "migrated",
|
|
15953
|
+
detail: "Migrate default_thinking to [thinking] block.",
|
|
15954
|
+
deprecatedSince: "pre-0.1.0"
|
|
15955
|
+
}];
|
|
15889
15956
|
/**
|
|
15890
|
-
*
|
|
15891
|
-
*
|
|
15892
|
-
*
|
|
15893
|
-
*
|
|
15957
|
+
* Combined list of all deprecated-field rules.
|
|
15958
|
+
*
|
|
15959
|
+
* Order within the list does not affect correctness (the CLI groups by kind
|
|
15960
|
+
* at display time), but a stable order helps test expectations.
|
|
15894
15961
|
*/
|
|
15895
|
-
|
|
15896
|
-
|
|
15962
|
+
const DEPRECATED_FIELD_RULES = [
|
|
15963
|
+
...REMOVED_RULES,
|
|
15964
|
+
...RENAMED_RULES,
|
|
15965
|
+
...MIGRATED_RULES
|
|
15966
|
+
];
|
|
15967
|
+
//#endregion
|
|
15968
|
+
//#region src/providers/runtime-provider.ts
|
|
15969
|
+
function resolveRuntimeProvider(input) {
|
|
15970
|
+
const modelName = input.model ?? input.config.defaultModel;
|
|
15971
|
+
if (modelName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, "No model is selected. Set default_model in config.toml or pass a configured model alias.");
|
|
15972
|
+
const alias = input.config.models?.[modelName];
|
|
15973
|
+
if (alias === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" is not configured in config.toml. Add a [models."${modelName}"] entry with max_context_size.`);
|
|
15974
|
+
const resolvedModel = alias.model;
|
|
15975
|
+
const providerName = alias.provider ?? input.config.defaultProvider;
|
|
15976
|
+
const providerConfig = providerName === void 0 ? void 0 : input.config.providers[providerName];
|
|
15977
|
+
if (providerName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a provider in config.toml.`);
|
|
15978
|
+
if (providerConfig === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${modelName}" is not configured.`);
|
|
15979
|
+
if (!Number.isInteger(alias.maxContextSize) || alias.maxContextSize <= 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a positive max_context_size in config.toml.`);
|
|
15980
|
+
if (input.validateCredentials !== false && providerConfig.type !== "vertexai" && providerConfig.oauth === void 0 && providerApiKey(providerConfig) === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" has no credentials configured. Set apiKey, oauth, or a provider env API key in config.toml.`);
|
|
15981
|
+
const provider = toKosongProviderConfig(providerConfig, resolvedModel, input.byfRequestHeaders, alias.maxOutputSize, alias.reasoningKey, input.promptCacheKey);
|
|
15897
15982
|
return {
|
|
15898
|
-
|
|
15899
|
-
|
|
15983
|
+
modelName,
|
|
15984
|
+
providerName,
|
|
15985
|
+
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
15986
|
+
provider
|
|
15900
15987
|
};
|
|
15901
15988
|
}
|
|
15902
|
-
function
|
|
15989
|
+
async function resolveRuntimeProviderWithOAuth(input) {
|
|
15990
|
+
const resolved = resolveRuntimeProvider(input);
|
|
15991
|
+
const resolveAuth = createRuntimeProviderAuthResolver(input, resolved);
|
|
15992
|
+
if (resolveAuth === void 0) return resolved;
|
|
15993
|
+
await resolveAuth();
|
|
15903
15994
|
return {
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15995
|
+
...resolved,
|
|
15996
|
+
resolveAuth
|
|
15997
|
+
};
|
|
15998
|
+
}
|
|
15999
|
+
function createRuntimeProviderAuthResolver(input, resolved = resolveRuntimeProvider(input)) {
|
|
16000
|
+
const providerName = resolved.providerName;
|
|
16001
|
+
if (providerName === void 0) return void 0;
|
|
16002
|
+
const providerConfig = input.config.providers[providerName];
|
|
16003
|
+
if (providerConfig?.oauth === void 0) return void 0;
|
|
16004
|
+
if (providerApiKey(providerConfig) !== void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" has both apiKey and oauth set in config.toml — they are mutually exclusive. Remove one.`);
|
|
16005
|
+
const tokenProvider = input.resolveOAuthTokenProvider?.(providerName, providerConfig.oauth);
|
|
16006
|
+
if (tokenProvider === void 0) return async () => {
|
|
16007
|
+
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
16008
|
+
};
|
|
16009
|
+
return async (options) => {
|
|
16010
|
+
let apiKey;
|
|
16011
|
+
try {
|
|
16012
|
+
apiKey = await tokenProvider.getAccessToken(options?.forceRefresh === true ? { force: true } : void 0);
|
|
16013
|
+
} catch (error) {
|
|
16014
|
+
if (!isAuthLoginRequired(error)) (input.log ?? log).warn("oauth token fetch failed", {
|
|
16015
|
+
providerName,
|
|
16016
|
+
error
|
|
16017
|
+
});
|
|
16018
|
+
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`, { cause: error });
|
|
16019
|
+
}
|
|
16020
|
+
if (apiKey.trim().length === 0) throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
16021
|
+
return { apiKey };
|
|
15907
16022
|
};
|
|
15908
16023
|
}
|
|
16024
|
+
function isAuthLoginRequired(error) {
|
|
16025
|
+
return isByfError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED;
|
|
16026
|
+
}
|
|
16027
|
+
const CAPABILITY_DEFINITIONS = [
|
|
16028
|
+
{
|
|
16029
|
+
name: "image_in",
|
|
16030
|
+
returnKey: "image_in"
|
|
16031
|
+
},
|
|
16032
|
+
{
|
|
16033
|
+
name: "video_in",
|
|
16034
|
+
returnKey: "video_in"
|
|
16035
|
+
},
|
|
16036
|
+
{
|
|
16037
|
+
name: "audio_in",
|
|
16038
|
+
returnKey: "audio_in"
|
|
16039
|
+
},
|
|
16040
|
+
{
|
|
16041
|
+
name: "thinking",
|
|
16042
|
+
returnKey: "thinking"
|
|
16043
|
+
},
|
|
16044
|
+
{
|
|
16045
|
+
name: "always_thinking",
|
|
16046
|
+
returnKey: "thinking"
|
|
16047
|
+
},
|
|
16048
|
+
{
|
|
16049
|
+
name: "tool_use",
|
|
16050
|
+
returnKey: "tool_use"
|
|
16051
|
+
},
|
|
16052
|
+
{
|
|
16053
|
+
name: "thinking_effort",
|
|
16054
|
+
returnKey: "thinking_effort"
|
|
16055
|
+
},
|
|
16056
|
+
{
|
|
16057
|
+
name: "thinking_xhigh",
|
|
16058
|
+
returnKey: "thinking_xhigh"
|
|
16059
|
+
},
|
|
16060
|
+
{
|
|
16061
|
+
name: "thinking_max",
|
|
16062
|
+
returnKey: "thinking_max"
|
|
16063
|
+
}
|
|
16064
|
+
];
|
|
15909
16065
|
/**
|
|
15910
|
-
*
|
|
15911
|
-
*
|
|
15912
|
-
*
|
|
15913
|
-
*
|
|
16066
|
+
* The list of every valid capability name that can appear in a model
|
|
16067
|
+
* alias's `capabilities` array.
|
|
16068
|
+
*
|
|
16069
|
+
* Derives directly from {@link CAPABILITY_DEFINITIONS} so that adding a
|
|
16070
|
+
* new capability in one place automatically keeps both the validation
|
|
16071
|
+
* gate (`update-config`) and the runtime resolver in sync.
|
|
15914
16072
|
*/
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
16073
|
+
const VALID_CAPABILITIES = CAPABILITY_DEFINITIONS.map((d) => d.name);
|
|
16074
|
+
function resolveModelCapabilities(alias, provider) {
|
|
16075
|
+
const capabilities = new Set((alias.capabilities ?? []).map((capability) => capability.trim().toLowerCase()));
|
|
16076
|
+
const has = (capability) => capabilities.has(capability);
|
|
16077
|
+
const providerCapability = createProvider(providerForCapabilityProbe(provider)).getCapability?.(provider.model) ?? UNKNOWN_CAPABILITY;
|
|
16078
|
+
const returnKeyToNames = /* @__PURE__ */ new Map();
|
|
16079
|
+
for (const def of CAPABILITY_DEFINITIONS) {
|
|
16080
|
+
const names = returnKeyToNames.get(def.returnKey) ?? [];
|
|
16081
|
+
names.push(def.name);
|
|
16082
|
+
returnKeyToNames.set(def.returnKey, names);
|
|
16083
|
+
}
|
|
16084
|
+
const resolved = {};
|
|
16085
|
+
for (const [returnKey, names] of returnKeyToNames) resolved[returnKey] = names.some((n) => has(n)) || Boolean(providerCapability[returnKey]);
|
|
16086
|
+
return {
|
|
16087
|
+
...resolved,
|
|
16088
|
+
max_context_tokens: alias.maxContextSize
|
|
16089
|
+
};
|
|
16090
|
+
}
|
|
16091
|
+
function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey) {
|
|
16092
|
+
switch (provider.type) {
|
|
16093
|
+
case "anthropic": return {
|
|
16094
|
+
type: "anthropic",
|
|
16095
|
+
model,
|
|
16096
|
+
baseUrl: providerValue(provider.baseUrl, provider.env, "ANTHROPIC_BASE_URL"),
|
|
16097
|
+
apiKey: providerApiKey(provider),
|
|
16098
|
+
...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
|
|
16099
|
+
...defaultHeadersField(provider.customHeaders)
|
|
15921
16100
|
};
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
|
|
16101
|
+
case "openai-completions": {
|
|
16102
|
+
const defaultHeaders = {
|
|
16103
|
+
...byfRequestHeaders,
|
|
16104
|
+
...provider.customHeaders
|
|
16105
|
+
};
|
|
16106
|
+
const generationKwargs = {
|
|
16107
|
+
prompt_cache_key: promptCacheKey,
|
|
16108
|
+
extra_body: provider.extraBody
|
|
16109
|
+
};
|
|
16110
|
+
if (Object.keys(defaultHeaders).length === 0) return {
|
|
16111
|
+
type: "openai-completions",
|
|
16112
|
+
model,
|
|
16113
|
+
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
16114
|
+
reasoningKey,
|
|
16115
|
+
thinkingEffortKey: provider.thinkingEffortKey,
|
|
16116
|
+
generationKwargs,
|
|
16117
|
+
apiKey: providerApiKey(provider)
|
|
16118
|
+
};
|
|
16119
|
+
return {
|
|
16120
|
+
type: "openai-completions",
|
|
16121
|
+
model,
|
|
16122
|
+
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
16123
|
+
reasoningKey,
|
|
16124
|
+
thinkingEffortKey: provider.thinkingEffortKey,
|
|
16125
|
+
generationKwargs,
|
|
16126
|
+
defaultHeaders,
|
|
16127
|
+
apiKey: providerApiKey(provider)
|
|
16128
|
+
};
|
|
16129
|
+
}
|
|
16130
|
+
case "google-genai": return {
|
|
16131
|
+
type: "google-genai",
|
|
16132
|
+
model,
|
|
16133
|
+
apiKey: providerApiKey(provider)
|
|
16134
|
+
};
|
|
16135
|
+
case "openai_responses": return {
|
|
16136
|
+
type: "openai_responses",
|
|
16137
|
+
model,
|
|
16138
|
+
baseUrl: providerValue(provider.baseUrl, provider.env, "OPENAI_BASE_URL"),
|
|
16139
|
+
apiKey: providerApiKey(provider),
|
|
16140
|
+
...defaultHeadersField(provider.customHeaders)
|
|
16141
|
+
};
|
|
16142
|
+
case "vertexai": return {
|
|
16143
|
+
type: "vertexai",
|
|
16144
|
+
model,
|
|
16145
|
+
vertexai: hasVertexAIServiceEnv(provider),
|
|
16146
|
+
apiKey: hasVertexAIServiceEnv(provider) ? void 0 : providerApiKey(provider),
|
|
16147
|
+
project: vertexAIProject(provider),
|
|
16148
|
+
location: vertexAILocation(provider)
|
|
15931
16149
|
};
|
|
16150
|
+
default: {
|
|
16151
|
+
const exhaustive = provider.type;
|
|
16152
|
+
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
16153
|
+
}
|
|
15932
16154
|
}
|
|
16155
|
+
}
|
|
16156
|
+
function defaultHeadersField(headers) {
|
|
16157
|
+
if (headers === void 0 || Object.keys(headers).length === 0) return {};
|
|
16158
|
+
return { defaultHeaders: { ...headers } };
|
|
16159
|
+
}
|
|
16160
|
+
function providerForCapabilityProbe(provider) {
|
|
16161
|
+
if (provider.type === "vertexai") return {
|
|
16162
|
+
...provider,
|
|
16163
|
+
vertexai: false,
|
|
16164
|
+
project: void 0,
|
|
16165
|
+
location: void 0,
|
|
16166
|
+
apiKey: provider.apiKey === void 0 || provider.apiKey.length === 0 ? "capability-probe" : provider.apiKey
|
|
16167
|
+
};
|
|
16168
|
+
if (provider.apiKey !== void 0 && provider.apiKey.length > 0) return provider;
|
|
15933
16169
|
return {
|
|
15934
|
-
|
|
15935
|
-
|
|
16170
|
+
...provider,
|
|
16171
|
+
apiKey: "capability-probe"
|
|
15936
16172
|
};
|
|
15937
16173
|
}
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
15942
|
-
|
|
15943
|
-
|
|
15944
|
-
|
|
15945
|
-
|
|
15946
|
-
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
16174
|
+
function providerApiKey(provider) {
|
|
16175
|
+
switch (provider.type) {
|
|
16176
|
+
case "anthropic": return providerValue(provider.apiKey, provider.env, "ANTHROPIC_API_KEY");
|
|
16177
|
+
case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
|
|
16178
|
+
case "openai-completions": return providerValue(provider.apiKey, provider.env, "BYF_API_KEY");
|
|
16179
|
+
case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
|
|
16180
|
+
case "vertexai": return nonEmptyString$1(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
|
|
16181
|
+
default: {
|
|
16182
|
+
const exhaustive = provider.type;
|
|
16183
|
+
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
16184
|
+
}
|
|
16185
|
+
}
|
|
16186
|
+
}
|
|
16187
|
+
function hasVertexAIServiceEnv(provider) {
|
|
16188
|
+
return vertexAIProject(provider) !== void 0 && vertexAILocation(provider) !== void 0;
|
|
16189
|
+
}
|
|
16190
|
+
function vertexAIProject(provider) {
|
|
16191
|
+
return envValue(provider.env, "GOOGLE_CLOUD_PROJECT");
|
|
16192
|
+
}
|
|
16193
|
+
function vertexAILocation(provider) {
|
|
16194
|
+
return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
|
|
16195
|
+
}
|
|
16196
|
+
function providerValue(configured, env, envKey) {
|
|
16197
|
+
return nonEmptyString$1(configured) ?? envValue(env, envKey);
|
|
16198
|
+
}
|
|
16199
|
+
function envValue(env, key) {
|
|
16200
|
+
return nonEmptyString$1(env?.[key]);
|
|
16201
|
+
}
|
|
16202
|
+
function nonEmptyString$1(value) {
|
|
16203
|
+
const trimmed = value?.trim();
|
|
16204
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
16205
|
+
}
|
|
16206
|
+
function locationFromVertexAIBaseUrl(baseUrl) {
|
|
16207
|
+
const url = nonEmptyString$1(baseUrl);
|
|
16208
|
+
if (url === void 0) return void 0;
|
|
16209
|
+
try {
|
|
16210
|
+
const host = new URL(url).hostname;
|
|
16211
|
+
return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$1(host.slice(0, -26)) : void 0;
|
|
16212
|
+
} catch {
|
|
16213
|
+
return;
|
|
16214
|
+
}
|
|
16215
|
+
}
|
|
16216
|
+
//#endregion
|
|
16217
|
+
//#region src/config/update.ts
|
|
16218
|
+
/**
|
|
16219
|
+
* Scan a parsed config (including `config.raw`) and return all Findings
|
|
16220
|
+
* for deprecated / renamed / migrated / dangling / unknown / invalid-value
|
|
16221
|
+
* fields.
|
|
16222
|
+
*
|
|
16223
|
+
* This is a **pure** function — no file I/O, no side effects.
|
|
16224
|
+
*
|
|
16225
|
+
* Detection is based on `config.raw` (the clone of the original TOML data),
|
|
16226
|
+
* not on the parsed camelCase schema. This matches the PRD's observation
|
|
16227
|
+
* that `raw` is both the protection layer (preserving unknown fields) and
|
|
16228
|
+
* the blind spot (retaining stale keys through read→write cycles).
|
|
16229
|
+
*/
|
|
16230
|
+
function analyzeConfig(config) {
|
|
16231
|
+
const raw = config.raw;
|
|
16232
|
+
const findings = [];
|
|
16233
|
+
if (isRecord(raw)) {
|
|
16234
|
+
for (const rule of DEPRECATED_FIELD_RULES) if (pathExistsInRaw(raw, rule.pathParts)) findings.push({
|
|
16235
|
+
kind: rule.kind,
|
|
16236
|
+
path: rule.path,
|
|
16237
|
+
detail: rule.detail,
|
|
16238
|
+
deprecatedSince: rule.deprecatedSince
|
|
16239
|
+
});
|
|
16240
|
+
const defaultThinkingFinding = findings.find((f) => f.path === "default_thinking");
|
|
16241
|
+
if (defaultThinkingFinding) {
|
|
16242
|
+
const thinking = config.thinking;
|
|
16243
|
+
if (thinking && (thinking.mode !== void 0 || thinking.effort !== void 0)) {
|
|
16244
|
+
defaultThinkingFinding.kind = "removed";
|
|
16245
|
+
defaultThinkingFinding.detail = "Already superseded by [thinking] block.";
|
|
16246
|
+
}
|
|
16247
|
+
}
|
|
16248
|
+
const UNKNOWN_SKIP_PATHS = new Set(DEPRECATED_FIELD_RULES.map((r) => r.pathParts.join(".")));
|
|
16249
|
+
const byfShapeKeys = /* @__PURE__ */ new Set();
|
|
16250
|
+
for (const key of Object.keys(ByfConfigSchema.shape)) {
|
|
16251
|
+
byfShapeKeys.add(key);
|
|
16252
|
+
byfShapeKeys.add(camelToSnakeStatic(key));
|
|
16253
|
+
}
|
|
16254
|
+
for (const rawKey of Object.keys(raw)) {
|
|
16255
|
+
if (rawKey === "raw") continue;
|
|
16256
|
+
if (UNKNOWN_SKIP_PATHS.has(rawKey)) continue;
|
|
16257
|
+
const camelKey = snakeToCamelStatic(rawKey);
|
|
16258
|
+
if (!byfShapeKeys.has(camelKey) && !byfShapeKeys.has(rawKey)) findings.push({
|
|
16259
|
+
kind: "unknown",
|
|
16260
|
+
path: rawKey,
|
|
16261
|
+
detail: `Field "${rawKey}" is not recognized by the current schema. Its value has been ignored. This may be a typo or a field from a previous version.`
|
|
16262
|
+
});
|
|
16263
|
+
}
|
|
16264
|
+
const nestedFindings = scanNestedUnknowns(raw, UNKNOWN_SKIP_PATHS);
|
|
16265
|
+
findings.push(...nestedFindings);
|
|
16266
|
+
}
|
|
16267
|
+
if (config.models) {
|
|
16268
|
+
const validCapsLower = new Set(VALID_CAPABILITIES.map((c) => c.toLowerCase()));
|
|
16269
|
+
for (const [alias, modelConfig] of Object.entries(config.models)) if (modelConfig.capabilities) for (let i = 0; i < modelConfig.capabilities.length; i++) {
|
|
16270
|
+
const cap = modelConfig.capabilities[i];
|
|
16271
|
+
if (cap === void 0) continue;
|
|
16272
|
+
if (!validCapsLower.has(cap.toLowerCase())) findings.push({
|
|
16273
|
+
kind: "invalid-value",
|
|
16274
|
+
path: `models.${alias}.capabilities[${i}]`,
|
|
16275
|
+
detail: `"${cap}" is not a valid capability. Valid values: ${VALID_CAPABILITIES.join(", ")}.`
|
|
16276
|
+
});
|
|
16277
|
+
}
|
|
16278
|
+
}
|
|
16279
|
+
const providerKeys = Object.keys(config.providers ?? {});
|
|
16280
|
+
if (config.models) {
|
|
16281
|
+
for (const [alias, modelConfig] of Object.entries(config.models)) if (!providerKeys.includes(modelConfig.provider)) findings.push({
|
|
16282
|
+
kind: "dangling",
|
|
16283
|
+
path: `models.${alias}.provider`,
|
|
16284
|
+
detail: `Model alias "${alias}" references provider "${modelConfig.provider}", which does not exist in [providers].`
|
|
16285
|
+
});
|
|
16286
|
+
}
|
|
16287
|
+
if (config.defaultProvider !== void 0 && !providerKeys.includes(config.defaultProvider)) findings.push({
|
|
16288
|
+
kind: "dangling",
|
|
16289
|
+
path: "default_provider",
|
|
16290
|
+
detail: `Default provider "${config.defaultProvider}" does not exist in [providers].`
|
|
16291
|
+
});
|
|
16292
|
+
const modelKeys = Object.keys(config.models ?? {});
|
|
16293
|
+
if (config.defaultModel !== void 0 && !modelKeys.includes(config.defaultModel)) findings.push({
|
|
16294
|
+
kind: "dangling",
|
|
16295
|
+
path: "default_model",
|
|
16296
|
+
detail: `Default model "${config.defaultModel}" does not exist in [models].`
|
|
16297
|
+
});
|
|
16298
|
+
return findings;
|
|
16299
|
+
}
|
|
16300
|
+
/**
|
|
16301
|
+
* Apply automatic fixes to a `ByfConfig` by deleting every path registered in
|
|
16302
|
+
* `DEPRECATED_FIELD_RULES` from `config.raw`.
|
|
16303
|
+
*
|
|
16304
|
+
* The `_findings` parameter is intentionally **not consulted** — deletion is
|
|
16305
|
+
* driven exclusively by the whitelist in `DEPRECATED_FIELD_RULES`. This ensures
|
|
16306
|
+
* that a call to `applyFixes` always produces a clean config regardless of
|
|
16307
|
+
* what analysis step previously ran.
|
|
16308
|
+
*
|
|
16309
|
+
* This is a **pure** function — it returns a new config object without
|
|
16310
|
+
* mutating the input.
|
|
16311
|
+
*/
|
|
16312
|
+
function applyFixes(config, findings) {
|
|
16313
|
+
const newRaw = rawShallowClone(config.raw);
|
|
16314
|
+
for (const rule of DEPRECATED_FIELD_RULES) deletePath(newRaw, rule.pathParts);
|
|
16315
|
+
let newConfig = {
|
|
16316
|
+
...config,
|
|
16317
|
+
raw: newRaw
|
|
16318
|
+
};
|
|
16319
|
+
if (findings.find((f) => f.kind === "migrated" && f.path === "default_thinking")) {
|
|
16320
|
+
const rawValue = config.raw?.["default_thinking"];
|
|
16321
|
+
const isTruthy = rawValue === true || rawValue === "true" || rawValue === 1;
|
|
16322
|
+
newConfig = {
|
|
16323
|
+
...newConfig,
|
|
16324
|
+
thinking: isTruthy ? {
|
|
16325
|
+
mode: "on",
|
|
16326
|
+
effort: "high"
|
|
16327
|
+
} : { mode: "off" }
|
|
16328
|
+
};
|
|
16329
|
+
}
|
|
16330
|
+
return newConfig;
|
|
16331
|
+
}
|
|
16332
|
+
/** Convert snake_case to camelCase (static helper for unknown detection). */
|
|
16333
|
+
function snakeToCamelStatic(str) {
|
|
16334
|
+
return str.replaceAll(/_([a-z])/g, (_, ch) => ch.toUpperCase());
|
|
16335
|
+
}
|
|
16336
|
+
/** Convert camelCase to snake_case (static helper for unknown detection). */
|
|
16337
|
+
function camelToSnakeStatic(str) {
|
|
16338
|
+
return str.replaceAll(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
|
|
16339
|
+
}
|
|
16340
|
+
/** True when `value` is a non-null, non-array object. */
|
|
16341
|
+
function isRecord(value) {
|
|
16342
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16343
|
+
}
|
|
16344
|
+
/**
|
|
16345
|
+
* Walk `root` along `pathParts` checking that **every** segment exists.
|
|
16346
|
+
*
|
|
16347
|
+
* Returns `true` iff all parts exist as own keys (or inherited keys — TOML
|
|
16348
|
+
* parse results are plain objects so the distinction doesn't matter here).
|
|
16349
|
+
*/
|
|
16350
|
+
function pathExistsInRaw(root, pathParts) {
|
|
16351
|
+
let current = root;
|
|
16352
|
+
for (const part of pathParts) {
|
|
16353
|
+
if (!isRecord(current) || !(part in current)) return false;
|
|
16354
|
+
current = current[part];
|
|
16355
|
+
}
|
|
16356
|
+
return true;
|
|
16357
|
+
}
|
|
16358
|
+
/**
|
|
16359
|
+
* Delete a leaf (or entire sub-tree) from `root` following `pathParts`.
|
|
16360
|
+
*
|
|
16361
|
+
* If the parent after deletion becomes empty it is cleaned up as well
|
|
16362
|
+
* (recursive upward), so that a service table cleared of all deprecated
|
|
16363
|
+
* keys does not leave behind an empty `{}`.
|
|
16364
|
+
*/
|
|
16365
|
+
function deletePath(root, pathParts) {
|
|
16366
|
+
if (pathParts.length === 0) return;
|
|
16367
|
+
const parentParts = pathParts.slice(0, -1);
|
|
16368
|
+
const leafKey = pathParts.at(-1);
|
|
16369
|
+
let current;
|
|
16370
|
+
if (parentParts.length === 0) current = root;
|
|
16371
|
+
else current = traverseTo(root, parentParts);
|
|
16372
|
+
if (current === void 0) return;
|
|
16373
|
+
const keyExisted = leafKey in current;
|
|
16374
|
+
delete current[leafKey];
|
|
16375
|
+
if (keyExisted && parentParts.length > 0 && Object.keys(current).length === 0) deletePath(root, parentParts);
|
|
16376
|
+
}
|
|
16377
|
+
/**
|
|
16378
|
+
* Walk `root` along `pathParts` returning the penultimate record, or
|
|
16379
|
+
* `undefined` if any segment is missing.
|
|
16380
|
+
*/
|
|
16381
|
+
function traverseTo(root, pathParts) {
|
|
16382
|
+
let current = root;
|
|
16383
|
+
for (const part of pathParts) {
|
|
16384
|
+
if (!isRecord(current) || !(part in current)) return void 0;
|
|
16385
|
+
current = current[part];
|
|
16386
|
+
}
|
|
16387
|
+
return isRecord(current) ? current : void 0;
|
|
16388
|
+
}
|
|
16389
|
+
/**
|
|
16390
|
+
* Shallow-clone `raw`: each nested record is also shallow-cloned so that
|
|
16391
|
+
* mutations in `applyFixes` do not affect the original object.
|
|
16392
|
+
*/
|
|
16393
|
+
function rawShallowClone(raw) {
|
|
16394
|
+
if (!isRecord(raw)) return {};
|
|
16395
|
+
const clone = {};
|
|
16396
|
+
for (const [key, value] of Object.entries(raw)) clone[key] = isRecord(value) ? { ...value } : value;
|
|
16397
|
+
return clone;
|
|
16398
|
+
}
|
|
16399
|
+
/**
|
|
16400
|
+
* Build a set of all valid keys (camelCase + snake_case) from a zod
|
|
16401
|
+
* object schema's `.shape`.
|
|
16402
|
+
*/
|
|
16403
|
+
function getShapeKeySet(schema) {
|
|
16404
|
+
const keys = /* @__PURE__ */ new Set();
|
|
16405
|
+
for (const key of Object.keys(schema.shape)) {
|
|
16406
|
+
keys.add(key);
|
|
16407
|
+
keys.add(camelToSnakeStatic(key));
|
|
16408
|
+
}
|
|
16409
|
+
return keys;
|
|
16410
|
+
}
|
|
16411
|
+
/**
|
|
16412
|
+
* Scan known container keys in `raw` for sub-keys that don't match the
|
|
16413
|
+
* corresponding schema shape. Unknown paths that overlap with
|
|
16414
|
+
* `skipPaths` (e.g. already reported deprecated fields) are skipped.
|
|
16415
|
+
*
|
|
16416
|
+
* Detects e.g. `models.gpt4.max_context_tokns` (typo) or
|
|
16417
|
+
* `providers.anthropic.api_kei` (typo).
|
|
16418
|
+
*/
|
|
16419
|
+
function scanNestedUnknowns(raw, skipPaths) {
|
|
16420
|
+
const findings = [];
|
|
16421
|
+
const containers = [
|
|
16422
|
+
{
|
|
16423
|
+
rawKey: "models",
|
|
16424
|
+
isRecord: true,
|
|
16425
|
+
schema: ModelAliasSchema
|
|
16426
|
+
},
|
|
16427
|
+
{
|
|
16428
|
+
rawKey: "providers",
|
|
16429
|
+
isRecord: true,
|
|
16430
|
+
schema: ProviderConfigSchema
|
|
16431
|
+
},
|
|
16432
|
+
{
|
|
16433
|
+
rawKey: "services",
|
|
16434
|
+
isRecord: false,
|
|
16435
|
+
schema: ServicesConfigSchema
|
|
16436
|
+
},
|
|
16437
|
+
{
|
|
16438
|
+
rawKey: "background",
|
|
16439
|
+
isRecord: false,
|
|
16440
|
+
schema: BackgroundConfigSchema
|
|
16441
|
+
},
|
|
16442
|
+
{
|
|
16443
|
+
rawKey: "loop_control",
|
|
16444
|
+
isRecord: false,
|
|
16445
|
+
schema: LoopControlSchema
|
|
16446
|
+
},
|
|
16447
|
+
{
|
|
16448
|
+
rawKey: "thinking",
|
|
16449
|
+
isRecord: false,
|
|
16450
|
+
schema: ThinkingConfigSchema
|
|
16451
|
+
},
|
|
16452
|
+
{
|
|
16453
|
+
rawKey: "permission",
|
|
16454
|
+
isRecord: false,
|
|
16455
|
+
schema: PermissionConfigSchema,
|
|
16456
|
+
legacyKeys: [
|
|
16457
|
+
"deny",
|
|
16458
|
+
"allow",
|
|
16459
|
+
"ask"
|
|
16460
|
+
]
|
|
16461
|
+
}
|
|
16462
|
+
];
|
|
16463
|
+
const schemaKeySets = /* @__PURE__ */ new Map();
|
|
16464
|
+
containers.forEach((entry, index) => {
|
|
16465
|
+
const base = getShapeKeySet(entry.schema);
|
|
16466
|
+
if (entry.legacyKeys) for (const k of entry.legacyKeys) base.add(k);
|
|
16467
|
+
schemaKeySets.set(index, base);
|
|
16468
|
+
});
|
|
16469
|
+
for (const [entryIndex, entry] of containers.entries()) {
|
|
16470
|
+
if (!(entry.rawKey in raw)) continue;
|
|
16471
|
+
const rawValue = raw[entry.rawKey];
|
|
16472
|
+
if (!isRecord(rawValue)) continue;
|
|
16473
|
+
if (entry.isRecord) for (const [itemKey, itemValue] of Object.entries(rawValue)) {
|
|
16474
|
+
if (!isRecord(itemValue)) continue;
|
|
16475
|
+
const validKeys = schemaKeySets.get(entryIndex);
|
|
16476
|
+
for (const subKey of Object.keys(itemValue)) if (!validKeys.has(subKey)) {
|
|
16477
|
+
const path = `${entry.rawKey}.${itemKey}.${subKey}`;
|
|
16478
|
+
if (!skipPaths.has(path)) findings.push({
|
|
16479
|
+
kind: "unknown",
|
|
16480
|
+
path,
|
|
16481
|
+
detail: `Field "${subKey}" is not recognized in ${entry.rawKey}.${itemKey}. This may be a typo or a field from a previous version.`
|
|
16482
|
+
});
|
|
16483
|
+
}
|
|
16484
|
+
}
|
|
16485
|
+
else {
|
|
16486
|
+
const validKeys = schemaKeySets.get(entryIndex);
|
|
16487
|
+
for (const subKey of Object.keys(rawValue)) if (!validKeys.has(subKey)) {
|
|
16488
|
+
const path = `${entry.rawKey}.${subKey}`;
|
|
16489
|
+
if (!skipPaths.has(path)) findings.push({
|
|
16490
|
+
kind: "unknown",
|
|
16491
|
+
path,
|
|
16492
|
+
detail: `Field "${subKey}" is not recognized in [${entry.rawKey}]. This may be a typo or a field from a previous version.`
|
|
16493
|
+
});
|
|
16494
|
+
}
|
|
16495
|
+
}
|
|
16496
|
+
}
|
|
16497
|
+
return findings;
|
|
16498
|
+
}
|
|
16499
|
+
//#endregion
|
|
16500
|
+
//#region src/version.ts
|
|
16501
|
+
function getCoreVersion() {
|
|
16502
|
+
try {
|
|
16503
|
+
const raw = readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf-8");
|
|
16504
|
+
const pkg = JSON.parse(raw);
|
|
16505
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
16506
|
+
} catch {
|
|
16507
|
+
return "0.0.0";
|
|
16508
|
+
}
|
|
16509
|
+
}
|
|
16510
|
+
//#endregion
|
|
16511
|
+
//#region src/mcp/client-shared.ts
|
|
16512
|
+
const BYF_MCP_CLIENT_VERSION = getCoreVersion();
|
|
16513
|
+
/**
|
|
16514
|
+
* Build the `RequestOptions` object accepted by the MCP SDK's `callTool`,
|
|
16515
|
+
* including either the configured tool-call timeout, an in-flight abort
|
|
16516
|
+
* signal, both, or neither. Returns `undefined` when nothing needs to be
|
|
16517
|
+
* passed so the SDK falls back to its defaults.
|
|
16518
|
+
*/
|
|
16519
|
+
function buildRequestOptions(toolCallTimeoutMs, signal) {
|
|
16520
|
+
if (toolCallTimeoutMs === void 0 && signal === void 0) return void 0;
|
|
16521
|
+
return {
|
|
16522
|
+
timeout: toolCallTimeoutMs,
|
|
16523
|
+
signal
|
|
16524
|
+
};
|
|
16525
|
+
}
|
|
16526
|
+
function toMcpToolDefinition(tool) {
|
|
16527
|
+
return {
|
|
16528
|
+
name: tool.name,
|
|
16529
|
+
description: tool.description ?? "",
|
|
16530
|
+
inputSchema: tool.inputSchema
|
|
16531
|
+
};
|
|
16532
|
+
}
|
|
16533
|
+
/**
|
|
16534
|
+
* Normalise the SDK's `callTool` return into kosong's {@link MCPToolResult}.
|
|
16535
|
+
* The SDK can return either the modern `{ content, isError }` shape or a
|
|
16536
|
+
* legacy `{ toolResult }` shape; we collapse the legacy shape to a single
|
|
16537
|
+
* text content block.
|
|
16538
|
+
*/
|
|
16539
|
+
function toMcpToolResult(result) {
|
|
16540
|
+
if (typeof result === "object" && result !== null && "content" in result) {
|
|
16541
|
+
const typed = result;
|
|
16542
|
+
if (Array.isArray(typed.content)) return {
|
|
16543
|
+
content: typed.content,
|
|
16544
|
+
isError: typed.isError === true
|
|
16545
|
+
};
|
|
16546
|
+
}
|
|
16547
|
+
if (typeof result === "object" && result !== null && "toolResult" in result) {
|
|
16548
|
+
const legacy = result.toolResult;
|
|
16549
|
+
return {
|
|
16550
|
+
content: [{
|
|
16551
|
+
type: "text",
|
|
16552
|
+
text: typeof legacy === "string" ? legacy : JSON.stringify(legacy)
|
|
16553
|
+
}],
|
|
16554
|
+
isError: false
|
|
16555
|
+
};
|
|
16556
|
+
}
|
|
16557
|
+
return {
|
|
16558
|
+
content: [],
|
|
16559
|
+
isError: false
|
|
16560
|
+
};
|
|
16561
|
+
}
|
|
16562
|
+
//#endregion
|
|
16563
|
+
//#region src/mcp/client-http.ts
|
|
16564
|
+
/**
|
|
16565
|
+
* Wraps the SDK streamable-HTTP transport as a kosong {@link MCPClient}.
|
|
16566
|
+
* Static bearer tokens are looked up from `process.env[bearerTokenEnvVar]`.
|
|
16567
|
+
* OAuth providers are attached separately by the connection manager.
|
|
16568
|
+
*/
|
|
16569
|
+
var HttpMcpClient = class {
|
|
16570
|
+
client;
|
|
16571
|
+
transport;
|
|
16572
|
+
toolCallTimeoutMs;
|
|
16573
|
+
started = false;
|
|
16574
|
+
closed = false;
|
|
16575
|
+
ready = false;
|
|
16576
|
+
hooksInstalled = false;
|
|
16577
|
+
unexpectedCloseListener;
|
|
16578
|
+
lastTransportError;
|
|
16579
|
+
pendingUnexpectedClose;
|
|
16580
|
+
unexpectedCloseFired = false;
|
|
16581
|
+
constructor(config, options = {}) {
|
|
16582
|
+
const headers = buildMcpHttpHeaders(config, options.envLookup ?? ((name) => process.env[name]));
|
|
16583
|
+
this.transport = new StreamableHTTPClientTransport(new URL(config.url), {
|
|
15960
16584
|
requestInit: headers !== void 0 ? { headers } : void 0,
|
|
15961
16585
|
fetch: options.fetch,
|
|
15962
16586
|
authProvider: options.oauthProvider
|
|
@@ -17608,11 +18232,15 @@ function createProxiedFetch(deps) {
|
|
|
17608
18232
|
const proxyUrl = getProxyForUrl(url, envLookup, sysProxy);
|
|
17609
18233
|
const noProxyMatch = noProxy !== void 0 && isNoProxyHost(hostname, noProxy);
|
|
17610
18234
|
const controller = new AbortController();
|
|
17611
|
-
const timeoutId = setTimeout(() =>
|
|
18235
|
+
const timeoutId = setTimeout(() => {
|
|
18236
|
+
controller.abort();
|
|
18237
|
+
}, REQUEST_TIMEOUT_MS);
|
|
17612
18238
|
if (init?.signal) if (init.signal.aborted) {
|
|
17613
18239
|
clearTimeout(timeoutId);
|
|
17614
18240
|
controller.abort();
|
|
17615
|
-
} else init.signal.addEventListener("abort", () =>
|
|
18241
|
+
} else init.signal.addEventListener("abort", () => {
|
|
18242
|
+
controller.abort();
|
|
18243
|
+
}, { once: true });
|
|
17616
18244
|
const mergedInit = {
|
|
17617
18245
|
...init,
|
|
17618
18246
|
signal: controller.signal
|
|
@@ -17624,7 +18252,7 @@ function createProxiedFetch(deps) {
|
|
|
17624
18252
|
return response;
|
|
17625
18253
|
} catch (error) {
|
|
17626
18254
|
clearTimeout(timeoutId);
|
|
17627
|
-
if (isRetryableError(error) && proxyUrl && !noProxyMatch) return
|
|
18255
|
+
if (isRetryableError(error) && proxyUrl && !noProxyMatch) return retryViaProxy(input, init, proxyUrl, innerFetch);
|
|
17628
18256
|
throw error;
|
|
17629
18257
|
}
|
|
17630
18258
|
};
|
|
@@ -17632,11 +18260,15 @@ function createProxiedFetch(deps) {
|
|
|
17632
18260
|
}
|
|
17633
18261
|
async function retryViaProxy(input, init, proxyUrl, innerFetch) {
|
|
17634
18262
|
const controller = new AbortController();
|
|
17635
|
-
const timeoutId = setTimeout(() =>
|
|
18263
|
+
const timeoutId = setTimeout(() => {
|
|
18264
|
+
controller.abort();
|
|
18265
|
+
}, REQUEST_TIMEOUT_MS);
|
|
17636
18266
|
if (init?.signal) if (init.signal.aborted) {
|
|
17637
18267
|
clearTimeout(timeoutId);
|
|
17638
18268
|
controller.abort();
|
|
17639
|
-
} else init.signal.addEventListener("abort", () =>
|
|
18269
|
+
} else init.signal.addEventListener("abort", () => {
|
|
18270
|
+
controller.abort();
|
|
18271
|
+
}, { once: true });
|
|
17640
18272
|
const dispatcher = new ProxyAgent(proxyUrl);
|
|
17641
18273
|
const retryInit = {
|
|
17642
18274
|
...init,
|
|
@@ -17786,82 +18418,188 @@ var RemoteFetchURLProvider = class {
|
|
|
17786
18418
|
}
|
|
17787
18419
|
};
|
|
17788
18420
|
//#endregion
|
|
17789
|
-
//#region src/tools/providers/
|
|
17790
|
-
var
|
|
17791
|
-
|
|
17792
|
-
|
|
18421
|
+
//#region src/tools/providers/router.ts
|
|
18422
|
+
var AllProvidersFailedError = class extends Error {
|
|
18423
|
+
lastError;
|
|
18424
|
+
constructor(lastError) {
|
|
18425
|
+
super(`All search providers failed. Last error: ${lastError ?? "unknown"}`);
|
|
18426
|
+
this.lastError = lastError;
|
|
18427
|
+
this.name = "AllProvidersFailedError";
|
|
18428
|
+
}
|
|
18429
|
+
};
|
|
18430
|
+
var PriorityRouter = class {
|
|
18431
|
+
providers;
|
|
18432
|
+
constructor(providers) {
|
|
18433
|
+
this.providers = providers;
|
|
18434
|
+
}
|
|
18435
|
+
async search(query, options) {
|
|
18436
|
+
let lastError;
|
|
18437
|
+
for (const provider of this.providers) try {
|
|
18438
|
+
return await provider.search(query, options);
|
|
18439
|
+
} catch (error) {
|
|
18440
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
18441
|
+
}
|
|
18442
|
+
throw new AllProvidersFailedError(lastError);
|
|
18443
|
+
}
|
|
18444
|
+
};
|
|
18445
|
+
//#endregion
|
|
18446
|
+
//#region src/tools/providers/exa.ts
|
|
18447
|
+
var ExaWebSearchProvider = class {
|
|
18448
|
+
apiKeys;
|
|
17793
18449
|
baseUrl;
|
|
17794
|
-
defaultHeaders;
|
|
17795
|
-
customHeaders;
|
|
17796
18450
|
fetchImpl;
|
|
17797
18451
|
constructor(options) {
|
|
17798
|
-
this.
|
|
17799
|
-
this.
|
|
17800
|
-
this.baseUrl = options.baseUrl;
|
|
17801
|
-
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
17802
|
-
this.customHeaders = options.customHeaders ?? {};
|
|
18452
|
+
this.apiKeys = options.apiKeys;
|
|
18453
|
+
this.baseUrl = options.baseUrl ?? "https://api.exa.ai/search";
|
|
17803
18454
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
17804
18455
|
}
|
|
17805
18456
|
async search(query, options) {
|
|
17806
|
-
const
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
18457
|
+
const limit = options?.limit ?? 5;
|
|
18458
|
+
const includeContent = options?.includeContent ?? false;
|
|
18459
|
+
const contents = {};
|
|
18460
|
+
if (includeContent) contents.text = { maxCharacters: 1e4 };
|
|
18461
|
+
else contents.highlights = {
|
|
18462
|
+
query,
|
|
18463
|
+
maxCharacters: 300
|
|
17811
18464
|
};
|
|
17812
|
-
const
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
throw new Error(`Remote search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
|
|
17817
|
-
}
|
|
17818
|
-
if (response.status !== 200) {
|
|
17819
|
-
const detail = await safeReadText(response);
|
|
17820
|
-
throw new Error(`Remote search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
17821
|
-
}
|
|
17822
|
-
const json = await response.json();
|
|
17823
|
-
return (Array.isArray(json.search_results) ? json.search_results : []).map((r) => {
|
|
17824
|
-
const out = {
|
|
17825
|
-
title: r.title ?? "",
|
|
17826
|
-
url: r.url ?? "",
|
|
17827
|
-
snippet: r.snippet ?? ""
|
|
17828
|
-
};
|
|
17829
|
-
if (typeof r.date === "string" && r.date.length > 0) out.date = r.date;
|
|
17830
|
-
if (typeof r.content === "string" && r.content.length > 0) out.content = r.content;
|
|
17831
|
-
return out;
|
|
18465
|
+
const body = JSON.stringify({
|
|
18466
|
+
query,
|
|
18467
|
+
numResults: limit,
|
|
18468
|
+
contents
|
|
17832
18469
|
});
|
|
18470
|
+
let lastError;
|
|
18471
|
+
for (const apiKey of this.apiKeys) try {
|
|
18472
|
+
const response = await this.fetchImpl(this.baseUrl, {
|
|
18473
|
+
method: "POST",
|
|
18474
|
+
headers: {
|
|
18475
|
+
Authorization: `Bearer ${apiKey}`,
|
|
18476
|
+
"Content-Type": "application/json"
|
|
18477
|
+
},
|
|
18478
|
+
body
|
|
18479
|
+
});
|
|
18480
|
+
if (!response.ok) {
|
|
18481
|
+
const detail = (await response.text().catch(() => "")).slice(0, 200);
|
|
18482
|
+
throw new Error(`Exa search failed: HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
18483
|
+
}
|
|
18484
|
+
const json = await response.json();
|
|
18485
|
+
return (Array.isArray(json.results) ? json.results : []).map((r) => {
|
|
18486
|
+
const out = {
|
|
18487
|
+
title: r.title ?? "",
|
|
18488
|
+
url: r.url ?? "",
|
|
18489
|
+
snippet: ""
|
|
18490
|
+
};
|
|
18491
|
+
if (includeContent && typeof r.text === "string") {
|
|
18492
|
+
out.snippet = r.text.slice(0, 300);
|
|
18493
|
+
if (r.text.length > 0) out.content = r.text;
|
|
18494
|
+
} else if (Array.isArray(r.highlights) && r.highlights.length > 0) out.snippet = r.highlights[0].slice(0, 300);
|
|
18495
|
+
if (typeof r.publishedDate === "string" && r.publishedDate.length > 0) out.date = r.publishedDate;
|
|
18496
|
+
return out;
|
|
18497
|
+
});
|
|
18498
|
+
} catch (error) {
|
|
18499
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
18500
|
+
}
|
|
18501
|
+
throw lastError ?? /* @__PURE__ */ new Error("Exa search failed: no API keys configured");
|
|
17833
18502
|
}
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
17841
|
-
|
|
17842
|
-
|
|
17843
|
-
|
|
17844
|
-
|
|
17845
|
-
|
|
18503
|
+
};
|
|
18504
|
+
registerProvider("exa", ExaWebSearchProvider);
|
|
18505
|
+
//#endregion
|
|
18506
|
+
//#region src/tools/providers/brave.ts
|
|
18507
|
+
var BraveWebSearchProvider = class {
|
|
18508
|
+
apiKeys;
|
|
18509
|
+
baseUrl;
|
|
18510
|
+
fetchImpl;
|
|
18511
|
+
constructor(options) {
|
|
18512
|
+
this.apiKeys = options.apiKeys;
|
|
18513
|
+
this.baseUrl = options.baseUrl ?? "https://api.search.brave.com/res/v1/web/search";
|
|
18514
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
17846
18515
|
}
|
|
17847
|
-
async
|
|
17848
|
-
|
|
17849
|
-
|
|
18516
|
+
async search(query, options) {
|
|
18517
|
+
const limit = options?.limit ?? 5;
|
|
18518
|
+
const url = new URL(this.baseUrl);
|
|
18519
|
+
url.searchParams.set("q", query);
|
|
18520
|
+
url.searchParams.set("count", String(limit));
|
|
18521
|
+
let lastError;
|
|
18522
|
+
for (const apiKey of this.apiKeys) try {
|
|
18523
|
+
const response = await this.fetchImpl(url.toString(), {
|
|
18524
|
+
method: "GET",
|
|
18525
|
+
headers: {
|
|
18526
|
+
"Accept": "application/json",
|
|
18527
|
+
"Accept-Encoding": "gzip",
|
|
18528
|
+
"X-Subscription-Token": apiKey
|
|
18529
|
+
}
|
|
18530
|
+
});
|
|
18531
|
+
if (!response.ok) {
|
|
18532
|
+
const detail = (await response.text().catch(() => "")).slice(0, 200);
|
|
18533
|
+
throw new Error(`Brave search failed: HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
18534
|
+
}
|
|
18535
|
+
const json = await response.json();
|
|
18536
|
+
return (Array.isArray(json.web?.results) ? json.web.results : []).map((r) => {
|
|
18537
|
+
const out = {
|
|
18538
|
+
title: r.title ?? "",
|
|
18539
|
+
url: r.url ?? "",
|
|
18540
|
+
snippet: r.description ?? ""
|
|
18541
|
+
};
|
|
18542
|
+
if (typeof r.age === "string" && r.age.length > 0) out.date = r.age;
|
|
18543
|
+
return out;
|
|
18544
|
+
});
|
|
17850
18545
|
} catch (error) {
|
|
17851
|
-
|
|
17852
|
-
throw error;
|
|
18546
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
17853
18547
|
}
|
|
17854
|
-
|
|
17855
|
-
throw new Error("Remote search service is not configured: missing API key or token provider.");
|
|
18548
|
+
throw lastError ?? /* @__PURE__ */ new Error("Brave search failed: no API keys configured");
|
|
17856
18549
|
}
|
|
17857
18550
|
};
|
|
17858
|
-
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
18551
|
+
registerProvider("brave", BraveWebSearchProvider);
|
|
18552
|
+
//#endregion
|
|
18553
|
+
//#region src/tools/providers/firecrawl.ts
|
|
18554
|
+
var FirecrawlWebSearchProvider = class {
|
|
18555
|
+
apiKeys;
|
|
18556
|
+
baseUrl;
|
|
18557
|
+
fetchImpl;
|
|
18558
|
+
constructor(options) {
|
|
18559
|
+
this.apiKeys = options.apiKeys;
|
|
18560
|
+
this.baseUrl = options.baseUrl ?? "https://api.firecrawl.dev/v2/search";
|
|
18561
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
17863
18562
|
}
|
|
17864
|
-
|
|
18563
|
+
async search(query, options) {
|
|
18564
|
+
const limit = options?.limit ?? 5;
|
|
18565
|
+
const includeContent = options?.includeContent ?? false;
|
|
18566
|
+
const requestBody = {
|
|
18567
|
+
query,
|
|
18568
|
+
limit
|
|
18569
|
+
};
|
|
18570
|
+
if (includeContent) requestBody.scrapeOptions = { formats: ["markdown"] };
|
|
18571
|
+
const body = JSON.stringify(requestBody);
|
|
18572
|
+
let lastError;
|
|
18573
|
+
for (const apiKey of this.apiKeys) try {
|
|
18574
|
+
const response = await this.fetchImpl(this.baseUrl, {
|
|
18575
|
+
method: "POST",
|
|
18576
|
+
headers: {
|
|
18577
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
18578
|
+
"Content-Type": "application/json"
|
|
18579
|
+
},
|
|
18580
|
+
body
|
|
18581
|
+
});
|
|
18582
|
+
if (!response.ok) {
|
|
18583
|
+
const detail = (await response.text().catch(() => "")).slice(0, 200);
|
|
18584
|
+
throw new Error(`Firecrawl search failed: HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
18585
|
+
}
|
|
18586
|
+
const json = await response.json();
|
|
18587
|
+
return (Array.isArray(json.data?.web) ? json.data.web : []).map((r) => {
|
|
18588
|
+
const out = {
|
|
18589
|
+
title: r.title ?? "",
|
|
18590
|
+
url: r.url ?? "",
|
|
18591
|
+
snippet: r.description ?? ""
|
|
18592
|
+
};
|
|
18593
|
+
if (includeContent && typeof r.markdown === "string" && r.markdown.length > 0) out.content = r.markdown;
|
|
18594
|
+
return out;
|
|
18595
|
+
});
|
|
18596
|
+
} catch (error) {
|
|
18597
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
18598
|
+
}
|
|
18599
|
+
throw lastError ?? /* @__PURE__ */ new Error("Firecrawl search failed: no API keys configured");
|
|
18600
|
+
}
|
|
18601
|
+
};
|
|
18602
|
+
registerProvider("firecrawl", FirecrawlWebSearchProvider);
|
|
17865
18603
|
//#endregion
|
|
17866
18604
|
//#region src/utils/environment.ts
|
|
17867
18605
|
/**
|
|
@@ -18123,15 +18861,6 @@ var SessionAPIImpl = class {
|
|
|
18123
18861
|
getModel({ agentId, ...payload }) {
|
|
18124
18862
|
return this.getAgent(agentId).getModel(payload);
|
|
18125
18863
|
}
|
|
18126
|
-
enterPlan({ agentId, ...payload }) {
|
|
18127
|
-
return this.getAgent(agentId).enterPlan(payload);
|
|
18128
|
-
}
|
|
18129
|
-
cancelPlan({ agentId, ...payload }) {
|
|
18130
|
-
return this.getAgent(agentId).cancelPlan(payload);
|
|
18131
|
-
}
|
|
18132
|
-
clearPlan({ agentId, ...payload }) {
|
|
18133
|
-
return this.getAgent(agentId).clearPlan(payload);
|
|
18134
|
-
}
|
|
18135
18864
|
beginCompaction({ agentId, ...payload }) {
|
|
18136
18865
|
return this.getAgent(agentId).beginCompaction(payload);
|
|
18137
18866
|
}
|
|
@@ -18172,9 +18901,6 @@ var SessionAPIImpl = class {
|
|
|
18172
18901
|
getPermission({ agentId, ...payload }) {
|
|
18173
18902
|
return this.getAgent(agentId).getPermission(payload);
|
|
18174
18903
|
}
|
|
18175
|
-
getPlan({ agentId, ...payload }) {
|
|
18176
|
-
return this.getAgent(agentId).getPlan(payload);
|
|
18177
|
-
}
|
|
18178
18904
|
getUsage({ agentId, ...payload }) {
|
|
18179
18905
|
return this.getAgent(agentId).getUsage(payload);
|
|
18180
18906
|
}
|
|
@@ -19322,207 +20048,6 @@ async function readOptionalFile(path) {
|
|
|
19322
20048
|
}
|
|
19323
20049
|
}
|
|
19324
20050
|
//#endregion
|
|
19325
|
-
//#region src/providers/runtime-provider.ts
|
|
19326
|
-
function resolveRuntimeProvider(input) {
|
|
19327
|
-
const modelName = input.model ?? input.config.defaultModel;
|
|
19328
|
-
if (modelName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, "No model is selected. Set default_model in config.toml or pass a configured model alias.");
|
|
19329
|
-
const alias = input.config.models?.[modelName];
|
|
19330
|
-
if (alias === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" is not configured in config.toml. Add a [models."${modelName}"] entry with max_context_size.`);
|
|
19331
|
-
const resolvedModel = alias.model;
|
|
19332
|
-
const providerName = alias.provider ?? input.config.defaultProvider;
|
|
19333
|
-
const providerConfig = providerName === void 0 ? void 0 : input.config.providers[providerName];
|
|
19334
|
-
if (providerName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a provider in config.toml.`);
|
|
19335
|
-
if (providerConfig === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${modelName}" is not configured.`);
|
|
19336
|
-
if (!Number.isInteger(alias.maxContextSize) || alias.maxContextSize <= 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a positive max_context_size in config.toml.`);
|
|
19337
|
-
if (input.validateCredentials !== false && providerConfig.type !== "vertexai" && providerConfig.oauth === void 0 && providerApiKey(providerConfig) === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" has no credentials configured. Set apiKey, oauth, or a provider env API key in config.toml.`);
|
|
19338
|
-
const provider = toKosongProviderConfig(providerConfig, resolvedModel, input.byfRequestHeaders, alias.maxOutputSize, alias.reasoningKey, input.promptCacheKey);
|
|
19339
|
-
return {
|
|
19340
|
-
modelName,
|
|
19341
|
-
providerName,
|
|
19342
|
-
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
19343
|
-
provider
|
|
19344
|
-
};
|
|
19345
|
-
}
|
|
19346
|
-
async function resolveRuntimeProviderWithOAuth(input) {
|
|
19347
|
-
const resolved = resolveRuntimeProvider(input);
|
|
19348
|
-
const resolveAuth = createRuntimeProviderAuthResolver(input, resolved);
|
|
19349
|
-
if (resolveAuth === void 0) return resolved;
|
|
19350
|
-
await resolveAuth();
|
|
19351
|
-
return {
|
|
19352
|
-
...resolved,
|
|
19353
|
-
resolveAuth
|
|
19354
|
-
};
|
|
19355
|
-
}
|
|
19356
|
-
function createRuntimeProviderAuthResolver(input, resolved = resolveRuntimeProvider(input)) {
|
|
19357
|
-
const providerName = resolved.providerName;
|
|
19358
|
-
if (providerName === void 0) return void 0;
|
|
19359
|
-
const providerConfig = input.config.providers[providerName];
|
|
19360
|
-
if (providerConfig?.oauth === void 0) return void 0;
|
|
19361
|
-
if (providerApiKey(providerConfig) !== void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" has both apiKey and oauth set in config.toml — they are mutually exclusive. Remove one.`);
|
|
19362
|
-
const tokenProvider = input.resolveOAuthTokenProvider?.(providerName, providerConfig.oauth);
|
|
19363
|
-
if (tokenProvider === void 0) return async () => {
|
|
19364
|
-
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
19365
|
-
};
|
|
19366
|
-
return async (options) => {
|
|
19367
|
-
let apiKey;
|
|
19368
|
-
try {
|
|
19369
|
-
apiKey = await tokenProvider.getAccessToken(options?.forceRefresh === true ? { force: true } : void 0);
|
|
19370
|
-
} catch (error) {
|
|
19371
|
-
if (!isAuthLoginRequired(error)) (input.log ?? log).warn("oauth token fetch failed", {
|
|
19372
|
-
providerName,
|
|
19373
|
-
error
|
|
19374
|
-
});
|
|
19375
|
-
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`, { cause: error });
|
|
19376
|
-
}
|
|
19377
|
-
if (apiKey.trim().length === 0) throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
19378
|
-
return { apiKey };
|
|
19379
|
-
};
|
|
19380
|
-
}
|
|
19381
|
-
function isAuthLoginRequired(error) {
|
|
19382
|
-
return isByfError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED;
|
|
19383
|
-
}
|
|
19384
|
-
function resolveModelCapabilities(alias, provider) {
|
|
19385
|
-
const capabilities = new Set((alias.capabilities ?? []).map((capability) => capability.trim().toLowerCase()));
|
|
19386
|
-
const has = (capability) => capabilities.has(capability);
|
|
19387
|
-
const providerCapability = createProvider(providerForCapabilityProbe(provider)).getCapability?.(provider.model) ?? UNKNOWN_CAPABILITY;
|
|
19388
|
-
return {
|
|
19389
|
-
image_in: has("image_in") || providerCapability.image_in,
|
|
19390
|
-
video_in: has("video_in") || providerCapability.video_in,
|
|
19391
|
-
audio_in: has("audio_in") || providerCapability.audio_in,
|
|
19392
|
-
thinking: has("thinking") || has("always_thinking") || providerCapability.thinking,
|
|
19393
|
-
tool_use: has("tool_use") || providerCapability.tool_use,
|
|
19394
|
-
thinking_effort: has("thinking_effort") || providerCapability.thinking_effort,
|
|
19395
|
-
thinking_xhigh: has("thinking_xhigh") || providerCapability.thinking_xhigh,
|
|
19396
|
-
thinking_max: has("thinking_max") || providerCapability.thinking_max,
|
|
19397
|
-
max_context_tokens: alias.maxContextSize
|
|
19398
|
-
};
|
|
19399
|
-
}
|
|
19400
|
-
function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey) {
|
|
19401
|
-
switch (provider.type) {
|
|
19402
|
-
case "anthropic": return {
|
|
19403
|
-
type: "anthropic",
|
|
19404
|
-
model,
|
|
19405
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "ANTHROPIC_BASE_URL"),
|
|
19406
|
-
apiKey: providerApiKey(provider),
|
|
19407
|
-
...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
|
|
19408
|
-
...defaultHeadersField(provider.customHeaders)
|
|
19409
|
-
};
|
|
19410
|
-
case "openai-completions": {
|
|
19411
|
-
const defaultHeaders = {
|
|
19412
|
-
...byfRequestHeaders,
|
|
19413
|
-
...provider.customHeaders
|
|
19414
|
-
};
|
|
19415
|
-
const generationKwargs = {
|
|
19416
|
-
prompt_cache_key: promptCacheKey,
|
|
19417
|
-
extra_body: provider.extraBody
|
|
19418
|
-
};
|
|
19419
|
-
if (Object.keys(defaultHeaders).length === 0) return {
|
|
19420
|
-
type: "openai-completions",
|
|
19421
|
-
model,
|
|
19422
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
19423
|
-
reasoningKey,
|
|
19424
|
-
thinkingEffortKey: provider.thinkingEffortKey,
|
|
19425
|
-
generationKwargs,
|
|
19426
|
-
apiKey: providerApiKey(provider)
|
|
19427
|
-
};
|
|
19428
|
-
return {
|
|
19429
|
-
type: "openai-completions",
|
|
19430
|
-
model,
|
|
19431
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
19432
|
-
reasoningKey,
|
|
19433
|
-
thinkingEffortKey: provider.thinkingEffortKey,
|
|
19434
|
-
generationKwargs,
|
|
19435
|
-
defaultHeaders,
|
|
19436
|
-
apiKey: providerApiKey(provider)
|
|
19437
|
-
};
|
|
19438
|
-
}
|
|
19439
|
-
case "google-genai": return {
|
|
19440
|
-
type: "google-genai",
|
|
19441
|
-
model,
|
|
19442
|
-
apiKey: providerApiKey(provider)
|
|
19443
|
-
};
|
|
19444
|
-
case "openai_responses": return {
|
|
19445
|
-
type: "openai_responses",
|
|
19446
|
-
model,
|
|
19447
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "OPENAI_BASE_URL"),
|
|
19448
|
-
apiKey: providerApiKey(provider),
|
|
19449
|
-
...defaultHeadersField(provider.customHeaders)
|
|
19450
|
-
};
|
|
19451
|
-
case "vertexai": return {
|
|
19452
|
-
type: "vertexai",
|
|
19453
|
-
model,
|
|
19454
|
-
vertexai: hasVertexAIServiceEnv(provider),
|
|
19455
|
-
apiKey: hasVertexAIServiceEnv(provider) ? void 0 : providerApiKey(provider),
|
|
19456
|
-
project: vertexAIProject(provider),
|
|
19457
|
-
location: vertexAILocation(provider)
|
|
19458
|
-
};
|
|
19459
|
-
default: {
|
|
19460
|
-
const exhaustive = provider.type;
|
|
19461
|
-
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
19462
|
-
}
|
|
19463
|
-
}
|
|
19464
|
-
}
|
|
19465
|
-
function defaultHeadersField(headers) {
|
|
19466
|
-
if (headers === void 0 || Object.keys(headers).length === 0) return {};
|
|
19467
|
-
return { defaultHeaders: { ...headers } };
|
|
19468
|
-
}
|
|
19469
|
-
function providerForCapabilityProbe(provider) {
|
|
19470
|
-
if (provider.type === "vertexai") return {
|
|
19471
|
-
...provider,
|
|
19472
|
-
vertexai: false,
|
|
19473
|
-
project: void 0,
|
|
19474
|
-
location: void 0,
|
|
19475
|
-
apiKey: provider.apiKey === void 0 || provider.apiKey.length === 0 ? "capability-probe" : provider.apiKey
|
|
19476
|
-
};
|
|
19477
|
-
if (provider.apiKey !== void 0 && provider.apiKey.length > 0) return provider;
|
|
19478
|
-
return {
|
|
19479
|
-
...provider,
|
|
19480
|
-
apiKey: "capability-probe"
|
|
19481
|
-
};
|
|
19482
|
-
}
|
|
19483
|
-
function providerApiKey(provider) {
|
|
19484
|
-
switch (provider.type) {
|
|
19485
|
-
case "anthropic": return providerValue(provider.apiKey, provider.env, "ANTHROPIC_API_KEY");
|
|
19486
|
-
case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
|
|
19487
|
-
case "openai-completions": return providerValue(provider.apiKey, provider.env, "BYF_API_KEY");
|
|
19488
|
-
case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
|
|
19489
|
-
case "vertexai": return nonEmptyString$1(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
|
|
19490
|
-
default: {
|
|
19491
|
-
const exhaustive = provider.type;
|
|
19492
|
-
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
19493
|
-
}
|
|
19494
|
-
}
|
|
19495
|
-
}
|
|
19496
|
-
function hasVertexAIServiceEnv(provider) {
|
|
19497
|
-
return vertexAIProject(provider) !== void 0 && vertexAILocation(provider) !== void 0;
|
|
19498
|
-
}
|
|
19499
|
-
function vertexAIProject(provider) {
|
|
19500
|
-
return envValue(provider.env, "GOOGLE_CLOUD_PROJECT");
|
|
19501
|
-
}
|
|
19502
|
-
function vertexAILocation(provider) {
|
|
19503
|
-
return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
|
|
19504
|
-
}
|
|
19505
|
-
function providerValue(configured, env, envKey) {
|
|
19506
|
-
return nonEmptyString$1(configured) ?? envValue(env, envKey);
|
|
19507
|
-
}
|
|
19508
|
-
function envValue(env, key) {
|
|
19509
|
-
return nonEmptyString$1(env?.[key]);
|
|
19510
|
-
}
|
|
19511
|
-
function nonEmptyString$1(value) {
|
|
19512
|
-
const trimmed = value?.trim();
|
|
19513
|
-
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
19514
|
-
}
|
|
19515
|
-
function locationFromVertexAIBaseUrl(baseUrl) {
|
|
19516
|
-
const url = nonEmptyString$1(baseUrl);
|
|
19517
|
-
if (url === void 0) return void 0;
|
|
19518
|
-
try {
|
|
19519
|
-
const host = new URL(url).hostname;
|
|
19520
|
-
return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$1(host.slice(0, -26)) : void 0;
|
|
19521
|
-
} catch {
|
|
19522
|
-
return;
|
|
19523
|
-
}
|
|
19524
|
-
}
|
|
19525
|
-
//#endregion
|
|
19526
20051
|
//#region src/providers/provider-manager.ts
|
|
19527
20052
|
var ProviderManager = class ProviderManager {
|
|
19528
20053
|
options;
|
|
@@ -19854,15 +20379,6 @@ var ByfCore = class {
|
|
|
19854
20379
|
getModel({ sessionId, ...payload }) {
|
|
19855
20380
|
return this.sessionApi(sessionId).getModel(payload);
|
|
19856
20381
|
}
|
|
19857
|
-
enterPlan({ sessionId, ...payload }) {
|
|
19858
|
-
return this.sessionApi(sessionId).enterPlan(payload);
|
|
19859
|
-
}
|
|
19860
|
-
cancelPlan({ sessionId, ...payload }) {
|
|
19861
|
-
return this.sessionApi(sessionId).cancelPlan(payload);
|
|
19862
|
-
}
|
|
19863
|
-
clearPlan({ sessionId, ...payload }) {
|
|
19864
|
-
return this.sessionApi(sessionId).clearPlan(payload);
|
|
19865
|
-
}
|
|
19866
20382
|
beginCompaction({ sessionId, ...payload }) {
|
|
19867
20383
|
return this.sessionApi(sessionId).beginCompaction(payload);
|
|
19868
20384
|
}
|
|
@@ -19902,9 +20418,6 @@ var ByfCore = class {
|
|
|
19902
20418
|
getPermission({ sessionId, ...payload }) {
|
|
19903
20419
|
return this.sessionApi(sessionId).getPermission(payload);
|
|
19904
20420
|
}
|
|
19905
|
-
getPlan({ sessionId, ...payload }) {
|
|
19906
|
-
return this.sessionApi(sessionId).getPlan(payload);
|
|
19907
|
-
}
|
|
19908
20421
|
getUsage({ sessionId, ...payload }) {
|
|
19909
20422
|
return this.sessionApi(sessionId).getUsage(payload);
|
|
19910
20423
|
}
|
|
@@ -19992,8 +20505,8 @@ async function createRuntimeConfig(input) {
|
|
|
19992
20505
|
systemProxy: () => detectSystemProxy()
|
|
19993
20506
|
});
|
|
19994
20507
|
const localFetcher = new LocalFetchURLProvider({ fetchImpl: proxiedFetch });
|
|
19995
|
-
const
|
|
19996
|
-
const
|
|
20508
|
+
const fetchService = input.config.services?.fetchUrl;
|
|
20509
|
+
const webSearchConfig = input.config.services?.webSearch;
|
|
19997
20510
|
return {
|
|
19998
20511
|
kaos: localKaos,
|
|
19999
20512
|
osEnv: await detectEnvironmentFromNode(),
|
|
@@ -20005,12 +20518,11 @@ async function createRuntimeConfig(input) {
|
|
|
20005
20518
|
fetchImpl: proxiedFetch,
|
|
20006
20519
|
...serviceCredentials(fetchService, input.resolveOAuthTokenProvider)
|
|
20007
20520
|
}),
|
|
20008
|
-
webSearcher:
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
fetchImpl: proxiedFetch
|
|
20012
|
-
|
|
20013
|
-
})
|
|
20521
|
+
webSearcher: webSearchConfig === void 0 ? void 0 : new PriorityRouter([...webSearchConfig.providers].toSorted((a, b) => a.priority - b.priority).map((p) => createProvider$1(p.type, {
|
|
20522
|
+
apiKeys: p.apiKeys,
|
|
20523
|
+
baseUrl: p.baseUrl,
|
|
20524
|
+
fetchImpl: proxiedFetch
|
|
20525
|
+
})))
|
|
20014
20526
|
};
|
|
20015
20527
|
}
|
|
20016
20528
|
function serviceCredentials(service, resolveOAuthTokenProvider) {
|
|
@@ -20045,7 +20557,6 @@ async function resumeSessionResult(summary, session, warning) {
|
|
|
20045
20557
|
context,
|
|
20046
20558
|
replay: agent.replayBuilder.buildResult(),
|
|
20047
20559
|
permission,
|
|
20048
|
-
plan: null,
|
|
20049
20560
|
usage,
|
|
20050
20561
|
tools: await api.getTools({ agentId }),
|
|
20051
20562
|
toolStore: agent.tools.storeData(),
|
|
@@ -20105,4 +20616,4 @@ function parsePositiveInt(value) {
|
|
|
20105
20616
|
return n;
|
|
20106
20617
|
}
|
|
20107
20618
|
//#endregion
|
|
20108
|
-
export { AGENT_WIRE_PROTOCOL_VERSION, Agent, BYF_ERROR_INFO, BackgroundConfigSchema, ByfConfigPatchSchema, ByfConfigSchema, ByfCore, ByfError, ByfServiceConfigSchema, ErrorCodes, HookDefSchema, LoopControlSchema, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE, McpServerConfigSchema, McpServerHttpConfigSchema, McpServerStdioConfigSchema, ModelAliasSchema, OAuthRefSchema, PermissionConfigSchema, PermissionModeSchema, PermissionRuleDecisionSchema, PermissionRuleSchema, PermissionRuleScopeSchema, ProviderConfigSchema, ProviderTypeSchema, ServicesConfigSchema, Session, SessionSubagentHost, ThinkingConfigSchema, USER_PROMPT_ORIGIN, WIRE_PROTOCOL_VERSION, buildExportManifest, buildPromptPlan, collectFilesRecursive, configToTomlData, createRPC, ensureByfHome, ensureConfigFile, exportSessionDirectory, flushDiagnosticLogs, formatConfigValidationError, fromByfErrorPayload, getDefaultConfig, getRootLogger, isByfError, log, makeErrorPayload, mergeConfigPatch, normalizeTimestampMs, parseBooleanEnv, parseConfigString, proxyWithExtraPayload, readConfigFile, redact, resolveByfHome, resolveConfigPath, resolveConfigValue, resolveGlobalLogPath, resolveLoggingConfig, scanSessionWire, toByfErrorPayload, transformTomlData, validateConfig, writeConfigFile, writeExportZip };
|
|
20619
|
+
export { AGENT_WIRE_PROTOCOL_VERSION, Agent, BYF_ERROR_INFO, BackgroundConfigSchema, ByfConfigPatchSchema, ByfConfigSchema, ByfCore, ByfError, ByfServiceConfigSchema, DEPRECATED_FIELD_RULES, ErrorCodes, HookDefSchema, LoopControlSchema, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE, McpServerConfigSchema, McpServerHttpConfigSchema, McpServerStdioConfigSchema, ModelAliasSchema, OAuthRefSchema, PermissionConfigSchema, PermissionModeSchema, PermissionRuleDecisionSchema, PermissionRuleSchema, PermissionRuleScopeSchema, ProviderConfigSchema, ProviderTypeSchema, ServicesConfigSchema, Session, SessionSubagentHost, ThinkingConfigSchema, USER_PROMPT_ORIGIN, WIRE_PROTOCOL_VERSION, WebSearchConfigSchema, WebSearchProviderConfigSchema, analyzeConfig, applyFixes, buildExportManifest, buildPromptPlan, collectFilesRecursive, configToTomlData, createRPC, ensureByfHome, ensureConfigFile, exportSessionDirectory, flushDiagnosticLogs, formatConfigValidationError, fromByfErrorPayload, getDefaultConfig, getRootLogger, isByfError, log, makeErrorPayload, mergeConfigPatch, normalizeTimestampMs, parseBooleanEnv, parseConfigString, proxyWithExtraPayload, readConfigFile, redact, resolveByfHome, resolveConfigPath, resolveConfigValue, resolveGlobalLogPath, resolveLoggingConfig, scanSessionWire, toByfErrorPayload, transformTomlData, validateConfig, writeConfigFile, writeExportZip };
|