@byfriends/agent-core 0.2.5 → 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-Bi8MjEPf.d.mts → index-CZA_2ux5.d.mts} +189 -82
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1038 -601
- package/dist/session/store/index.d.mts +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -843,17 +843,17 @@ async function loadAgentsMd(kaos, workDir) {
|
|
|
843
843
|
return true;
|
|
844
844
|
};
|
|
845
845
|
const home = kaos.gethome();
|
|
846
|
-
await collect(joinPath$
|
|
847
|
-
const genericFiles = [joinPath$
|
|
846
|
+
await collect(joinPath$1(kaos, home, ".byf", "AGENTS.md"));
|
|
847
|
+
const genericFiles = [joinPath$1(kaos, home, ".agents")].flatMap((dir) => ["AGENTS.md", "agents.md"].map((name) => joinPath$1(kaos, dir, name)));
|
|
848
848
|
for (const file of genericFiles) if (await collect(file)) break;
|
|
849
849
|
for (const dir of dirs) {
|
|
850
|
-
await collect(joinPath$
|
|
851
|
-
for (const fileName of ["AGENTS.md", "agents.md"]) if (await collect(joinPath$
|
|
850
|
+
await collect(joinPath$1(kaos, dir, ".byf", "AGENTS.md"));
|
|
851
|
+
for (const fileName of ["AGENTS.md", "agents.md"]) if (await collect(joinPath$1(kaos, dir, fileName))) break;
|
|
852
852
|
}
|
|
853
853
|
return renderAgentFiles(discovered);
|
|
854
854
|
}
|
|
855
855
|
async function findProjectRoot$1(kaos, workDir) {
|
|
856
|
-
const path = pathMod$
|
|
856
|
+
const path = pathMod$5(kaos);
|
|
857
857
|
const initial = kaos.normpath(workDir);
|
|
858
858
|
let current = initial;
|
|
859
859
|
while (true) {
|
|
@@ -864,7 +864,7 @@ async function findProjectRoot$1(kaos, workDir) {
|
|
|
864
864
|
}
|
|
865
865
|
}
|
|
866
866
|
function dirsRootToLeaf(kaos, workDir, projectRoot) {
|
|
867
|
-
const path = pathMod$
|
|
867
|
+
const path = pathMod$5(kaos);
|
|
868
868
|
const dirs = [];
|
|
869
869
|
let current = kaos.normpath(workDir);
|
|
870
870
|
while (true) {
|
|
@@ -939,10 +939,10 @@ function byteLength(text) {
|
|
|
939
939
|
function annotationFor(path) {
|
|
940
940
|
return `<!-- From: ${path} -->\n`;
|
|
941
941
|
}
|
|
942
|
-
function joinPath$
|
|
943
|
-
return pathMod$
|
|
942
|
+
function joinPath$1(kaos, ...parts) {
|
|
943
|
+
return pathMod$5(kaos).join(...parts);
|
|
944
944
|
}
|
|
945
|
-
function pathMod$
|
|
945
|
+
function pathMod$5(kaos) {
|
|
946
946
|
return kaos.pathClass() === "win32" ? win32Path : posixPath;
|
|
947
947
|
}
|
|
948
948
|
//#endregion
|
|
@@ -1195,7 +1195,7 @@ const PROFILE_SOURCES = {
|
|
|
1195
1195
|
"profile/default/agent.yaml": agent_default$1,
|
|
1196
1196
|
"profile/default/coder.yaml": coder_default,
|
|
1197
1197
|
"profile/default/explore.yaml": explore_default,
|
|
1198
|
-
"profile/default/system.md": "You are BYF, an AI agent running on the user's computer. Your job is to help\nusers accomplish tasks by taking action — read, write, search, and execute to\nmake real changes on the user's system. Answer questions when asked; otherwise,\nact.\n\nWhen responding, use the same language as the user unless explicitly instructed\notherwise.\n\n{{ ROLE_ADDITIONAL }}\n\n# First Principles\n\nThink from first principles. Strip away assumptions and conventions; every\naction must be traceable to a verifiable fact — the actual file contents,\ncommand output, data, or the user's explicit words. When in doubt, read\nbefore guessing, ask before assuming, verify before claiming.\n\n# Tool Use\n\nUse tools only when the task requires them. If the request can be answered\nwithout reading files, running commands, or searching the web, reply in text\ndirectly. When a request is ambiguous, prefer action — the user can see your\noutput and correct course.\n\nCode that only appears in your text response is NOT saved to the file system\nand will not take effect. To create or modify files, use `Write` or `Edit`.\nTo run commands, use `Bash`.\n\n# Protocol\n\n<system> tags in user or tool messages provide supplementary context. Treat\nthem as background information.\n\n<system-reminder> tags are authoritative directives that override default\nbehavior. They are unrelated to the messages they appear in. Always comply.\n\n# Safety\n\nThe environment is not a sandbox — your actions immediately affect the user's\nsystem.\n\n- Stay within the working directory unless explicitly instructed otherwise.\n- Git operations are destructive and may affect remote repositories. Never\n execute git mutations unless explicitly asked; confirm each time.\n- Avoid installing or deleting anything outside the working directory. If\n necessary, ask for confirmation first.\n\n# Working Environment\n\n## Operating System\n\nYou are running on **{{ BYF_OS }}**. The Bash tool executes commands using **{{ BYF_SHELL }}**.\n{% if BYF_OS == \"Windows\" %}\n\nIMPORTANT: You are on Windows. The Bash tool runs through Git Bash, so use Unix shell syntax inside Bash commands — `/dev/null` not `NUL`, and forward slashes in paths. For file operations, always prefer the built-in tools (Read, Write, Edit, Glob, Grep) over Bash commands — they work reliably across all platforms.\n{% endif %}\n\n## Working Directory\n\nThe current working directory is `{{ BYF_WORK_DIR }}`. This should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, IF SO, YOU MUST use absolute paths for these parameters.\n{% if BYF_ADDITIONAL_DIRS_INFO %}\n\n## Additional Directories\n\nThe following directories have been added to the workspace. You can read, write, search, and glob files in these directories as part of your workspace scope.\n\n{{ BYF_ADDITIONAL_DIRS_INFO }}\n{% endif %}\n\n#
|
|
1198
|
+
"profile/default/system.md": "You are BYF, an AI agent running on the user's computer. Your job is to help\nusers accomplish tasks by taking action — read, write, search, and execute to\nmake real changes on the user's system. Answer questions when asked; otherwise,\nact.\n\nWhen responding, use the same language as the user unless explicitly instructed\notherwise.\n\n{{ ROLE_ADDITIONAL }}\n\n# First Principles\n\nThink from first principles. Strip away assumptions and conventions; every\naction must be traceable to a verifiable fact — the actual file contents,\ncommand output, data, or the user's explicit words. When in doubt, read\nbefore guessing, ask before assuming, verify before claiming.\n\n# Tool Use\n\nUse tools only when the task requires them. If the request can be answered\nwithout reading files, running commands, or searching the web, reply in text\ndirectly. When a request is ambiguous, prefer action — the user can see your\noutput and correct course.\n\nCode that only appears in your text response is NOT saved to the file system\nand will not take effect. To create or modify files, use `Write` or `Edit`.\nTo run commands, use `Bash`.\n\n# Protocol\n\n<system> tags in user or tool messages provide supplementary context. Treat\nthem as background information.\n\n<system-reminder> tags are authoritative directives that override default\nbehavior. They are unrelated to the messages they appear in. Always comply.\n\n# Safety\n\nThe environment is not a sandbox — your actions immediately affect the user's\nsystem.\n\n- Stay within the working directory unless explicitly instructed otherwise.\n- Git operations are destructive and may affect remote repositories. Never\n execute git mutations unless explicitly asked; confirm each time.\n- Avoid installing or deleting anything outside the working directory. If\n necessary, ask for confirmation first.\n\n# Project Information\n\n`AGENTS.md` files contain project-specific context, styles, and conventions for agents. They may exist at different locations in the project — each file governs its directory and all subdirectories beneath it. Deeper files take precedence over parent files.\n\nIf instructions conflict:\n- `<system-reminder>` directives override all other instructions, including user messages.\n- Safety rules are hard constraints and must never be violated, even if a user message or AGENTS.md says otherwise.\n- Beyond those two, user messages > AGENTS.md > default system instructions.\n\n{% if BYF_AGENTS_MD_TOO_LONG %}\n> ⚠️ The merged AGENTS.md content exceeds 4,000 tokens. Consider compressing project instructions to reduce context usage.\n{% endif %}\n\nThe `AGENTS.md` instructions (merged from all applicable directories):\n\n`````````\n{{ BYF_AGENTS_MD }}\n`````````\n\nIf you modified anything mentioned in `AGENTS.md` files, update the corresponding files to keep them up-to-date.\n\n# Working Environment\n\n## Operating System\n\nYou are running on **{{ BYF_OS }}**. The Bash tool executes commands using **{{ BYF_SHELL }}**.\n{% if BYF_OS == \"Windows\" %}\n\nIMPORTANT: You are on Windows. The Bash tool runs through Git Bash, so use Unix shell syntax inside Bash commands — `/dev/null` not `NUL`, and forward slashes in paths. For file operations, always prefer the built-in tools (Read, Write, Edit, Glob, Grep) over Bash commands — they work reliably across all platforms.\n{% endif %}\n\n## Working Directory\n\nThe current working directory is `{{ BYF_WORK_DIR }}`. This should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, IF SO, YOU MUST use absolute paths for these parameters.\n{% if BYF_ADDITIONAL_DIRS_INFO %}\n\n## Additional Directories\n\nThe following directories have been added to the workspace. You can read, write, search, and glob files in these directories as part of your workspace scope.\n\n{{ BYF_ADDITIONAL_DIRS_INFO }}\n{% endif %}\n\n# Skills\n\nSkills are reusable capabilities. When a skill from the listing matches the user's request, you MUST call the `Skill` tool (not free-form text).\n\n{{ BYF_SKILLS }}\n"
|
|
1199
1199
|
};
|
|
1200
1200
|
const DEFAULT_INIT_PROMPT = init_default;
|
|
1201
1201
|
const DEFAULT_AGENT_PROFILES = loadAgentProfilesFromSources([
|
|
@@ -1621,6 +1621,7 @@ var BackgroundProcessManager = class {
|
|
|
1621
1621
|
const kind = opts?.kind;
|
|
1622
1622
|
const taskId = generateTaskId(kind ?? "bash");
|
|
1623
1623
|
const entry = {
|
|
1624
|
+
kind: "process",
|
|
1624
1625
|
taskId,
|
|
1625
1626
|
command,
|
|
1626
1627
|
description,
|
|
@@ -1820,9 +1821,10 @@ var BackgroundProcessManager = class {
|
|
|
1820
1821
|
entry.approvalReason = void 0;
|
|
1821
1822
|
entry.stopRequested = true;
|
|
1822
1823
|
entry.stopReason = stopReason;
|
|
1823
|
-
try {
|
|
1824
|
+
if (entry.kind === "process") try {
|
|
1824
1825
|
await entry.proc.kill("SIGTERM");
|
|
1825
1826
|
} catch {}
|
|
1827
|
+
else entry.abort();
|
|
1826
1828
|
let graceTimer;
|
|
1827
1829
|
const graceful = await Promise.race([entry.lifecyclePromise.then(() => true, () => true), new Promise((resolve) => {
|
|
1828
1830
|
graceTimer = setTimeout(() => {
|
|
@@ -1834,9 +1836,11 @@ var BackgroundProcessManager = class {
|
|
|
1834
1836
|
await entry.persistWriteQueue;
|
|
1835
1837
|
return this.toInfo(entry);
|
|
1836
1838
|
}
|
|
1837
|
-
if (!graceful
|
|
1838
|
-
|
|
1839
|
-
|
|
1839
|
+
if (!graceful) if (entry.kind === "process") {
|
|
1840
|
+
if (entry.proc.exitCode === null) try {
|
|
1841
|
+
await entry.proc.kill("SIGKILL");
|
|
1842
|
+
} catch {}
|
|
1843
|
+
} else entry.abort();
|
|
1840
1844
|
if (TERMINAL_STATUSES.has(entry.status)) {
|
|
1841
1845
|
await entry.persistWriteQueue;
|
|
1842
1846
|
return this.toInfo(entry);
|
|
@@ -1894,32 +1898,15 @@ var BackgroundProcessManager = class {
|
|
|
1894
1898
|
else this.assertCanRegister();
|
|
1895
1899
|
const taskId = generateTaskId("agent");
|
|
1896
1900
|
const entry = {
|
|
1901
|
+
kind: "promise",
|
|
1897
1902
|
taskId,
|
|
1898
1903
|
command: `[agent] ${description}`,
|
|
1899
1904
|
description,
|
|
1900
1905
|
timeoutMs: opts.timeoutMs,
|
|
1906
|
+
completion,
|
|
1907
|
+
abort: () => opts.abort?.(),
|
|
1901
1908
|
agentId: opts.agentId ?? taskId,
|
|
1902
1909
|
subagentType: opts.subagentType ?? "agent",
|
|
1903
|
-
proc: {
|
|
1904
|
-
stdin: {
|
|
1905
|
-
write: () => false,
|
|
1906
|
-
end: () => {}
|
|
1907
|
-
},
|
|
1908
|
-
stdout: {
|
|
1909
|
-
setEncoding: () => {},
|
|
1910
|
-
on: () => {}
|
|
1911
|
-
},
|
|
1912
|
-
stderr: {
|
|
1913
|
-
setEncoding: () => {},
|
|
1914
|
-
on: () => {}
|
|
1915
|
-
},
|
|
1916
|
-
pid: 0,
|
|
1917
|
-
exitCode: null,
|
|
1918
|
-
wait: () => completion.then(() => 0),
|
|
1919
|
-
kill: async () => {
|
|
1920
|
-
opts.abort?.();
|
|
1921
|
-
}
|
|
1922
|
-
},
|
|
1923
1910
|
outputChunks: [],
|
|
1924
1911
|
outputSizeBytes: 0,
|
|
1925
1912
|
status: "running",
|
|
@@ -2095,7 +2082,7 @@ var BackgroundProcessManager = class {
|
|
|
2095
2082
|
if (this.sessionDir !== void 0) await removeTask(this.sessionDir, taskId);
|
|
2096
2083
|
}
|
|
2097
2084
|
/**
|
|
2098
|
-
* Persist the current state of a live
|
|
2085
|
+
* Persist the current state of a live TaskEntry. Called from
|
|
2099
2086
|
* `register()` and the lifecycle finally block. No-op unless attached.
|
|
2100
2087
|
*/
|
|
2101
2088
|
persistLive(entry) {
|
|
@@ -2105,7 +2092,7 @@ var BackgroundProcessManager = class {
|
|
|
2105
2092
|
task_id: entry.taskId,
|
|
2106
2093
|
command: entry.command,
|
|
2107
2094
|
description: entry.description,
|
|
2108
|
-
pid: entry.proc.pid,
|
|
2095
|
+
pid: entry.kind === "process" ? entry.proc.pid : 0,
|
|
2109
2096
|
started_at: entry.startedAt,
|
|
2110
2097
|
ended_at: entry.endedAt,
|
|
2111
2098
|
exit_code: entry.exitCode,
|
|
@@ -2151,7 +2138,7 @@ var BackgroundProcessManager = class {
|
|
|
2151
2138
|
}
|
|
2152
2139
|
observedExitCompletions() {
|
|
2153
2140
|
const completions = [];
|
|
2154
|
-
for (const entry of this.processes.values()) if (!TERMINAL_STATUSES.has(entry.status) && entry.proc.exitCode !== null) completions.push(entry.lifecyclePromise);
|
|
2141
|
+
for (const entry of this.processes.values()) if (entry.kind === "process" && !TERMINAL_STATUSES.has(entry.status) && entry.proc.exitCode !== null) completions.push(entry.lifecyclePromise);
|
|
2155
2142
|
return completions;
|
|
2156
2143
|
}
|
|
2157
2144
|
toInfo(entry) {
|
|
@@ -2160,7 +2147,7 @@ var BackgroundProcessManager = class {
|
|
|
2160
2147
|
command: entry.command,
|
|
2161
2148
|
description: entry.description,
|
|
2162
2149
|
status: entry.status,
|
|
2163
|
-
pid: entry.proc.pid,
|
|
2150
|
+
pid: entry.kind === "process" ? entry.proc.pid : null,
|
|
2164
2151
|
exitCode: entry.exitCode,
|
|
2165
2152
|
startedAt: entry.startedAt,
|
|
2166
2153
|
endedAt: entry.endedAt,
|
|
@@ -2208,7 +2195,7 @@ function infoToPersisted(info) {
|
|
|
2208
2195
|
task_id: info.taskId,
|
|
2209
2196
|
command: info.command,
|
|
2210
2197
|
description: info.description,
|
|
2211
|
-
pid: info.pid,
|
|
2198
|
+
pid: info.pid ?? 0,
|
|
2212
2199
|
started_at: info.startedAt,
|
|
2213
2200
|
ended_at: info.endedAt,
|
|
2214
2201
|
exit_code: info.exitCode,
|
|
@@ -3045,7 +3032,7 @@ function parseSkillText(options) {
|
|
|
3045
3032
|
throw error;
|
|
3046
3033
|
}
|
|
3047
3034
|
const frontmatter = parsed.data ?? {};
|
|
3048
|
-
if (!isRecord$
|
|
3035
|
+
if (!isRecord$2(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
|
|
3049
3036
|
const metadata = normalizeMetadata(frontmatter);
|
|
3050
3037
|
if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
|
|
3051
3038
|
const name = nonEmptyString$2(metadata.name);
|
|
@@ -3158,7 +3145,7 @@ function tokenizeArgs(raw) {
|
|
|
3158
3145
|
function nonEmptyString$2(value) {
|
|
3159
3146
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
3160
3147
|
}
|
|
3161
|
-
function isRecord$
|
|
3148
|
+
function isRecord$2(value) {
|
|
3162
3149
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3163
3150
|
}
|
|
3164
3151
|
//#endregion
|
|
@@ -3605,14 +3592,14 @@ const SENSITIVE_DOT_VARIANT_SUFFIXES = [
|
|
|
3605
3592
|
];
|
|
3606
3593
|
const SENSITIVE_DOT_VARIANT_SUFFIX_SET = new Set(SENSITIVE_DOT_VARIANT_SUFFIXES);
|
|
3607
3594
|
const DEFAULT_PATH_CLASS$1 = nativePath.sep === "\\" ? "win32" : "posix";
|
|
3608
|
-
function pathMod$
|
|
3595
|
+
function pathMod$4(pathClass) {
|
|
3609
3596
|
return pathClass === "win32" ? win32Path : posixPath;
|
|
3610
3597
|
}
|
|
3611
3598
|
function comparable(path, pathClass) {
|
|
3612
3599
|
return pathClass === "win32" ? path.toLowerCase() : path;
|
|
3613
3600
|
}
|
|
3614
3601
|
function isSensitiveFile(path, pathClass = DEFAULT_PATH_CLASS$1) {
|
|
3615
|
-
const mod = pathMod$
|
|
3602
|
+
const mod = pathMod$4(pathClass);
|
|
3616
3603
|
const comparableName = comparable(mod.basename(path), pathClass);
|
|
3617
3604
|
const comparablePath = comparable(path, pathClass);
|
|
3618
3605
|
if (ENV_EXEMPTIONS.has(comparableName)) return false;
|
|
@@ -3666,7 +3653,7 @@ var PathSecurityError = class extends Error {
|
|
|
3666
3653
|
}
|
|
3667
3654
|
};
|
|
3668
3655
|
const DEFAULT_PATH_CLASS = nativePath.sep === "\\" ? "win32" : "posix";
|
|
3669
|
-
function pathMod$
|
|
3656
|
+
function pathMod$3(pathClass) {
|
|
3670
3657
|
return pathClass === "win32" ? win32Path : posixPath;
|
|
3671
3658
|
}
|
|
3672
3659
|
function comparablePath(path, pathClass) {
|
|
@@ -3696,7 +3683,7 @@ function normalizeUserPath(path, pathClass = DEFAULT_PATH_CLASS) {
|
|
|
3696
3683
|
function expandUserPath$1(path, homeDir, pathClass) {
|
|
3697
3684
|
if (homeDir === void 0) return path;
|
|
3698
3685
|
if (path === "~") return homeDir;
|
|
3699
|
-
if (path.startsWith("~/") || pathClass === "win32" && path.startsWith("~\\")) return pathMod$
|
|
3686
|
+
if (path.startsWith("~/") || pathClass === "win32" && path.startsWith("~\\")) return pathMod$3(pathClass).join(homeDir, path.slice(2));
|
|
3700
3687
|
return path;
|
|
3701
3688
|
}
|
|
3702
3689
|
/**
|
|
@@ -3705,7 +3692,7 @@ function expandUserPath$1(path, homeDir, pathClass) {
|
|
|
3705
3692
|
*/
|
|
3706
3693
|
function canonicalizePath(path, cwd, pathClass = DEFAULT_PATH_CLASS) {
|
|
3707
3694
|
if (path === "") throw new PathSecurityError("PATH_INVALID", path, path, "Path cannot be empty");
|
|
3708
|
-
const mod = pathMod$
|
|
3695
|
+
const mod = pathMod$3(pathClass);
|
|
3709
3696
|
const normalizedPath = normalizeUserPath(path, pathClass);
|
|
3710
3697
|
if (pathClass === "win32" && isWin32DriveRelative(normalizedPath)) throw new PathSecurityError("PATH_INVALID", path, normalizedPath, `"${path}" is a drive-relative Windows path. Use an absolute path like C:\\path or a path relative to the working directory.`);
|
|
3711
3698
|
if (!mod.isAbsolute(normalizedPath) && !mod.isAbsolute(cwd)) throw new PathSecurityError("PATH_INVALID", path, normalizedPath, `Cannot resolve "${path}" against non-absolute cwd "${cwd}".`);
|
|
@@ -3717,7 +3704,7 @@ function canonicalizePath(path, cwd, pathClass = DEFAULT_PATH_CLASS) {
|
|
|
3717
3704
|
* on path-component boundaries. Both arguments must already be canonical.
|
|
3718
3705
|
*/
|
|
3719
3706
|
function isWithinDirectory(candidate, base, pathClass = DEFAULT_PATH_CLASS) {
|
|
3720
|
-
const mod = pathMod$
|
|
3707
|
+
const mod = pathMod$3(pathClass);
|
|
3721
3708
|
const comparableCandidate = comparablePath(candidate, pathClass);
|
|
3722
3709
|
const comparableBase = comparablePath(base, pathClass);
|
|
3723
3710
|
if (comparableCandidate === comparableBase) return true;
|
|
@@ -3743,7 +3730,7 @@ function relativeOutsideMessage(path, operation) {
|
|
|
3743
3730
|
}
|
|
3744
3731
|
function resolvePathAccess(path, cwd, config, options) {
|
|
3745
3732
|
const pathClass = options.pathClass ?? DEFAULT_PATH_CLASS;
|
|
3746
|
-
const mod = pathMod$
|
|
3733
|
+
const mod = pathMod$3(pathClass);
|
|
3747
3734
|
const expandedPath = expandUserPath$1(normalizeUserPath(path, pathClass), options.homeDir, pathClass);
|
|
3748
3735
|
const rawIsAbsolute = mod.isAbsolute(expandedPath);
|
|
3749
3736
|
const canonical = canonicalizePath(expandedPath, cwd, pathClass);
|
|
@@ -3898,11 +3885,11 @@ var EditTool = class {
|
|
|
3898
3885
|
}
|
|
3899
3886
|
}
|
|
3900
3887
|
};
|
|
3901
|
-
async function collectEntries
|
|
3888
|
+
async function collectEntries(kaos, dirPath, maxWidth, pathClass) {
|
|
3902
3889
|
const all = [];
|
|
3903
3890
|
try {
|
|
3904
3891
|
for await (const fullPath of kaos.iterdir(dirPath)) {
|
|
3905
|
-
const name = basename$
|
|
3892
|
+
const name = basename$1(fullPath, pathClass);
|
|
3906
3893
|
let isDir = false;
|
|
3907
3894
|
try {
|
|
3908
3895
|
isDir = ((await kaos.stat(fullPath)).stMode & 61440) === 16384;
|
|
@@ -3929,11 +3916,11 @@ async function collectEntries$1(kaos, dirPath, maxWidth, pathClass) {
|
|
|
3929
3916
|
readable: true
|
|
3930
3917
|
};
|
|
3931
3918
|
}
|
|
3932
|
-
function pathMod$
|
|
3919
|
+
function pathMod$2(pathClass) {
|
|
3933
3920
|
return pathClass === "win32" ? win32Path : posixPath;
|
|
3934
3921
|
}
|
|
3935
|
-
function basename$
|
|
3936
|
-
return pathMod$
|
|
3922
|
+
function basename$1(p, pathClass) {
|
|
3923
|
+
return pathMod$2(pathClass).basename(p);
|
|
3937
3924
|
}
|
|
3938
3925
|
/**
|
|
3939
3926
|
* Return a 2-level tree listing of `workDir` suitable for inclusion in a
|
|
@@ -3943,7 +3930,7 @@ function basename$2(p, pathClass) {
|
|
|
3943
3930
|
async function listDirectory(kaos, workDir) {
|
|
3944
3931
|
const lines = [];
|
|
3945
3932
|
const pathClass = kaos.pathClass();
|
|
3946
|
-
const { entries, total, readable } = await collectEntries
|
|
3933
|
+
const { entries, total, readable } = await collectEntries(kaos, workDir, 30, pathClass);
|
|
3947
3934
|
if (!readable) return "[not readable]";
|
|
3948
3935
|
const remaining = total - entries.length;
|
|
3949
3936
|
for (let i = 0; i < entries.length; i++) {
|
|
@@ -3955,7 +3942,7 @@ async function listDirectory(kaos, workDir) {
|
|
|
3955
3942
|
if (isDir) {
|
|
3956
3943
|
lines.push(`${connector}${name}/`);
|
|
3957
3944
|
const childPrefix = isLast ? " " : "│ ";
|
|
3958
|
-
const child = await collectEntries
|
|
3945
|
+
const child = await collectEntries(kaos, joinPath(workDir, name, pathClass), 10, pathClass);
|
|
3959
3946
|
if (!child.readable) {
|
|
3960
3947
|
lines.push(`${childPrefix}└── [not readable]`);
|
|
3961
3948
|
continue;
|
|
@@ -3974,17 +3961,17 @@ async function listDirectory(kaos, workDir) {
|
|
|
3974
3961
|
if (remaining > 0) lines.push(`└── ... and ${String(remaining)} more entries`);
|
|
3975
3962
|
return lines.length > 0 ? lines.join("\n") : "(empty directory)";
|
|
3976
3963
|
}
|
|
3977
|
-
function joinPath
|
|
3978
|
-
return pathMod$
|
|
3964
|
+
function joinPath(parent, child, pathClass) {
|
|
3965
|
+
return pathMod$2(pathClass).join(parent, child);
|
|
3979
3966
|
}
|
|
3980
3967
|
//#endregion
|
|
3981
3968
|
//#region src/tools/builtin/file/glob.md
|
|
3982
|
-
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";
|
|
3983
3970
|
//#endregion
|
|
3984
3971
|
//#region src/tools/builtin/file/glob.ts
|
|
3985
3972
|
const GlobInputSchema = z.object({
|
|
3986
|
-
pattern: z.string().describe("Glob pattern to match files/directories."),
|
|
3987
|
-
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."),
|
|
3988
3975
|
include_dirs: z.boolean().default(true).optional().describe("Whether to include directories in results. Defaults to true. Set false to return only files.")
|
|
3989
3976
|
});
|
|
3990
3977
|
const MAX_MATCHES = 1e3;
|
|
@@ -4026,7 +4013,7 @@ var GlobTool = class {
|
|
|
4026
4013
|
workspace: this.workspace,
|
|
4027
4014
|
operation: "search",
|
|
4028
4015
|
policy: {
|
|
4029
|
-
guardMode: "
|
|
4016
|
+
guardMode: "absolute-outside-allowed",
|
|
4030
4017
|
checkSensitive: false
|
|
4031
4018
|
}
|
|
4032
4019
|
});
|
|
@@ -4060,7 +4047,7 @@ var GlobTool = class {
|
|
|
4060
4047
|
}
|
|
4061
4048
|
return {
|
|
4062
4049
|
isError: true,
|
|
4063
|
-
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}`
|
|
4064
4051
|
};
|
|
4065
4052
|
}
|
|
4066
4053
|
if (containsBraceExpansion(args.pattern)) return {
|
|
@@ -4088,6 +4075,8 @@ var GlobTool = class {
|
|
|
4088
4075
|
try {
|
|
4089
4076
|
const seen = /* @__PURE__ */ new Set();
|
|
4090
4077
|
const entries = [];
|
|
4078
|
+
const pathClass = this.kaos.pathClass();
|
|
4079
|
+
const filteredSensitive = [];
|
|
4091
4080
|
const YIELD_SAFETY_CAP = MAX_MATCHES * 2;
|
|
4092
4081
|
let yielded = 0;
|
|
4093
4082
|
let truncated = false;
|
|
@@ -4103,6 +4092,10 @@ var GlobTool = class {
|
|
|
4103
4092
|
break outer;
|
|
4104
4093
|
}
|
|
4105
4094
|
seen.add(filePath);
|
|
4095
|
+
if (isSensitiveFile(filePath, pathClass)) {
|
|
4096
|
+
filteredSensitive.push(filePath);
|
|
4097
|
+
continue;
|
|
4098
|
+
}
|
|
4106
4099
|
let mtime = 0;
|
|
4107
4100
|
let isDir = false;
|
|
4108
4101
|
try {
|
|
@@ -4118,10 +4111,10 @@ var GlobTool = class {
|
|
|
4118
4111
|
}
|
|
4119
4112
|
entries.sort((a, b) => b.mtime - a.mtime);
|
|
4120
4113
|
const paths = entries.map((e) => e.path);
|
|
4121
|
-
const pathClass = this.kaos.pathClass();
|
|
4122
4114
|
const relBase = searchRoots[0] ?? this.workspace.workspaceDir;
|
|
4123
4115
|
const displayLines = paths.map((p) => relativizeIfUnder$1(p, relBase, pathClass));
|
|
4124
|
-
|
|
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" };
|
|
4125
4118
|
const lines = [];
|
|
4126
4119
|
if (truncated) {
|
|
4127
4120
|
lines.push(`[Truncated at ${String(MAX_MATCHES)} matches — use a more specific pattern]`);
|
|
@@ -4129,6 +4122,10 @@ var GlobTool = class {
|
|
|
4129
4122
|
}
|
|
4130
4123
|
lines.push(...displayLines);
|
|
4131
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
|
+
}
|
|
4132
4129
|
return { output: lines.join("\n") };
|
|
4133
4130
|
} catch (error) {
|
|
4134
4131
|
if (error !== null && typeof error === "object" && "code" in error) {
|
|
@@ -5955,7 +5952,7 @@ var write_default = "Overwrite or append to a file with content exactly as provi
|
|
|
5955
5952
|
const S_IFMT$2 = 61440;
|
|
5956
5953
|
/** File-type bits of a directory. */
|
|
5957
5954
|
const S_IFDIR$1 = 16384;
|
|
5958
|
-
function pathMod$
|
|
5955
|
+
function pathMod$1(pathClass) {
|
|
5959
5956
|
return pathClass === "win32" ? win32Path : posixPath;
|
|
5960
5957
|
}
|
|
5961
5958
|
const WriteInputSchema = z.object({
|
|
@@ -6022,7 +6019,7 @@ var WriteTool = class {
|
|
|
6022
6019
|
* skipped and the write proceeds, surfacing the real I/O error if any.
|
|
6023
6020
|
*/
|
|
6024
6021
|
async checkParentDirectory(safePath) {
|
|
6025
|
-
const parent = pathMod$
|
|
6022
|
+
const parent = pathMod$1(this.kaos.pathClass()).dirname(safePath);
|
|
6026
6023
|
let stat;
|
|
6027
6024
|
try {
|
|
6028
6025
|
stat = await this.kaos.stat(parent);
|
|
@@ -6325,7 +6322,7 @@ function rewriteWindowsNullRedirect$1(command) {
|
|
|
6325
6322
|
}
|
|
6326
6323
|
//#endregion
|
|
6327
6324
|
//#region src/tools/builtin/state/todo-list.md
|
|
6328
|
-
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";
|
|
6329
6326
|
//#endregion
|
|
6330
6327
|
//#region src/tools/builtin/state/todo-list.ts
|
|
6331
6328
|
/**
|
|
@@ -6935,8 +6932,14 @@ function project(history, ephemeralInjections) {
|
|
|
6935
6932
|
if (isBlockedUserPrompt(message)) return false;
|
|
6936
6933
|
return !isTranscriptOnlyHookResult(message) && message.partial !== true && !(message.role === "assistant" && message.content.length === 0 && message.toolCalls.length === 0);
|
|
6937
6934
|
}));
|
|
6938
|
-
|
|
6939
|
-
|
|
6935
|
+
if (!ephemeralInjections?.length) return merged;
|
|
6936
|
+
const afterSystemMsgs = ephemeralInjections.filter((injection) => !injection.position || injection.position === "after_system").map((injection) => renderInjection(injection));
|
|
6937
|
+
const beforeUserMsgs = ephemeralInjections.filter((injection) => injection.position === "before_user").map((injection) => renderInjection(injection));
|
|
6938
|
+
return [
|
|
6939
|
+
...afterSystemMsgs,
|
|
6940
|
+
...merged,
|
|
6941
|
+
...beforeUserMsgs
|
|
6942
|
+
];
|
|
6940
6943
|
}
|
|
6941
6944
|
function isTranscriptOnlyHookResult(message) {
|
|
6942
6945
|
return message.origin?.kind === "hook_result" && TRANSCRIPT_ONLY_HOOK_RESULT_EVENTS.has(message.origin.event ?? "");
|
|
@@ -7926,7 +7929,15 @@ var ContextMemory = class {
|
|
|
7926
7929
|
return this._history;
|
|
7927
7930
|
}
|
|
7928
7931
|
get messages() {
|
|
7929
|
-
return
|
|
7932
|
+
return this.getMessages();
|
|
7933
|
+
}
|
|
7934
|
+
/**
|
|
7935
|
+
* Project history into provider-ready messages, optionally with
|
|
7936
|
+
* ephemeral injections (e.g. timestamp, permission mode) appended
|
|
7937
|
+
* at the `'before_user'` position.
|
|
7938
|
+
*/
|
|
7939
|
+
getMessages(ephemeral) {
|
|
7940
|
+
return project(this.history, ephemeral);
|
|
7930
7941
|
}
|
|
7931
7942
|
applyObservationMasking(config) {
|
|
7932
7943
|
const effectiveConfig = config ?? DEFAULT_MASKING_CONFIG;
|
|
@@ -8210,7 +8221,7 @@ const OptionalStringSchema = z.preprocess((value) => {
|
|
|
8210
8221
|
if (typeof value === "string") return value;
|
|
8211
8222
|
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
8212
8223
|
}, z.string().optional());
|
|
8213
|
-
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({
|
|
8214
8225
|
message: OptionalStringSchema,
|
|
8215
8226
|
permissionDecision: z.unknown().optional(),
|
|
8216
8227
|
permissionDecisionReason: OptionalStringSchema
|
|
@@ -8370,7 +8381,7 @@ function tryKillProcess(child, signal) {
|
|
|
8370
8381
|
} catch {}
|
|
8371
8382
|
}
|
|
8372
8383
|
}
|
|
8373
|
-
function isRecord(value) {
|
|
8384
|
+
function isRecord$1(value) {
|
|
8374
8385
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8375
8386
|
}
|
|
8376
8387
|
function errorMessage(error) {
|
|
@@ -8587,146 +8598,55 @@ var DynamicInjector = class {
|
|
|
8587
8598
|
}
|
|
8588
8599
|
};
|
|
8589
8600
|
//#endregion
|
|
8590
|
-
//#region src/agent/injection/directory-tree.ts
|
|
8591
|
-
const EXCLUDED_DIRS = new Set([
|
|
8592
|
-
"node_modules",
|
|
8593
|
-
".git",
|
|
8594
|
-
"dist",
|
|
8595
|
-
"build",
|
|
8596
|
-
".next",
|
|
8597
|
-
".nuxt",
|
|
8598
|
-
".vite",
|
|
8599
|
-
"target",
|
|
8600
|
-
".turbo",
|
|
8601
|
-
"coverage",
|
|
8602
|
-
".cache",
|
|
8603
|
-
".DS_Store",
|
|
8604
|
-
".idea",
|
|
8605
|
-
".vscode",
|
|
8606
|
-
"venv",
|
|
8607
|
-
".venv"
|
|
8608
|
-
]);
|
|
8609
|
-
const HIDDEN_DIR_WHITELIST = new Set([
|
|
8610
|
-
".github",
|
|
8611
|
-
".byf",
|
|
8612
|
-
".agents",
|
|
8613
|
-
".changeset",
|
|
8614
|
-
".husky"
|
|
8615
|
-
]);
|
|
8616
|
-
var DirectoryTreeInjector = class extends DynamicInjector {
|
|
8617
|
-
injectionVariant = "directory_tree";
|
|
8618
|
-
lastTree;
|
|
8619
|
-
hasInjected = false;
|
|
8620
|
-
capturedTimestamp;
|
|
8621
|
-
async getInjection() {
|
|
8622
|
-
const kaos = this.agent.runtime.kaos;
|
|
8623
|
-
const workDir = this.agent.config.cwd || kaos.getcwd();
|
|
8624
|
-
const tree = await buildTree(kaos, workDir);
|
|
8625
|
-
if (this.hasInjected && tree === this.lastTree) return;
|
|
8626
|
-
this.lastTree = tree;
|
|
8627
|
-
this.hasInjected = true;
|
|
8628
|
-
if (this.capturedTimestamp === void 0) this.capturedTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8629
|
-
return `Current working directory structure (${workDir}):\n${tree}\n\nThe current date and time in ISO format is \`${this.capturedTimestamp}\`. This is only a reference for you when searching the web or checking file modification time, etc. If you need the exact time, use Bash tool with proper command.`;
|
|
8630
|
-
}
|
|
8631
|
-
};
|
|
8632
|
-
async function buildTree(kaos, workDir) {
|
|
8633
|
-
const lines = [];
|
|
8634
|
-
const pathClass = kaos.pathClass();
|
|
8635
|
-
const { entries, total, readable } = await collectEntries(kaos, workDir, 30, pathClass);
|
|
8636
|
-
if (!readable) return "[not readable]";
|
|
8637
|
-
const remaining = total - entries.length;
|
|
8638
|
-
for (let i = 0; i < entries.length; i++) {
|
|
8639
|
-
const entry = entries[i];
|
|
8640
|
-
if (entry === void 0) continue;
|
|
8641
|
-
const { name, isDir } = entry;
|
|
8642
|
-
const isLast = i === entries.length - 1 && remaining === 0;
|
|
8643
|
-
const connector = isLast ? "└── " : "├── ";
|
|
8644
|
-
if (isDir) {
|
|
8645
|
-
lines.push(`${connector}${name}/`);
|
|
8646
|
-
const childPrefix = isLast ? " " : "│ ";
|
|
8647
|
-
const child = await collectEntries(kaos, joinPath(workDir, name, pathClass), 10, pathClass);
|
|
8648
|
-
if (!child.readable) {
|
|
8649
|
-
lines.push(`${childPrefix}└── [not readable]`);
|
|
8650
|
-
continue;
|
|
8651
|
-
}
|
|
8652
|
-
const childRemaining = child.total - child.entries.length;
|
|
8653
|
-
for (let j = 0; j < child.entries.length; j++) {
|
|
8654
|
-
const ce = child.entries[j];
|
|
8655
|
-
if (ce === void 0) continue;
|
|
8656
|
-
const cConnector = j === child.entries.length - 1 && childRemaining === 0 ? "└── " : "├── ";
|
|
8657
|
-
const suffix = ce.isDir ? "/" : "";
|
|
8658
|
-
lines.push(`${childPrefix}${cConnector}${ce.name}${suffix}`);
|
|
8659
|
-
}
|
|
8660
|
-
if (childRemaining > 0) lines.push(`${childPrefix}└── ... and ${String(childRemaining)} more`);
|
|
8661
|
-
} else lines.push(`${connector}${name}`);
|
|
8662
|
-
}
|
|
8663
|
-
if (remaining > 0) lines.push(`└── ... and ${String(remaining)} more entries`);
|
|
8664
|
-
return lines.length > 0 ? lines.join("\n") : "(empty directory)";
|
|
8665
|
-
}
|
|
8666
|
-
async function collectEntries(kaos, dirPath, maxWidth, pathClass) {
|
|
8667
|
-
const all = [];
|
|
8668
|
-
try {
|
|
8669
|
-
for await (const fullPath of kaos.iterdir(dirPath)) {
|
|
8670
|
-
const name = basename$1(fullPath, pathClass);
|
|
8671
|
-
if (shouldExclude(name)) continue;
|
|
8672
|
-
let isDir = false;
|
|
8673
|
-
try {
|
|
8674
|
-
isDir = ((await kaos.stat(fullPath)).stMode & 61440) === 16384;
|
|
8675
|
-
} catch {}
|
|
8676
|
-
all.push({
|
|
8677
|
-
name,
|
|
8678
|
-
isDir
|
|
8679
|
-
});
|
|
8680
|
-
}
|
|
8681
|
-
} catch {
|
|
8682
|
-
return {
|
|
8683
|
-
entries: [],
|
|
8684
|
-
total: 0,
|
|
8685
|
-
readable: false
|
|
8686
|
-
};
|
|
8687
|
-
}
|
|
8688
|
-
all.sort((a, b) => {
|
|
8689
|
-
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
8690
|
-
return a.name.localeCompare(b.name);
|
|
8691
|
-
});
|
|
8692
|
-
return {
|
|
8693
|
-
entries: all.slice(0, maxWidth),
|
|
8694
|
-
total: all.length,
|
|
8695
|
-
readable: true
|
|
8696
|
-
};
|
|
8697
|
-
}
|
|
8698
|
-
function shouldExclude(name) {
|
|
8699
|
-
if (EXCLUDED_DIRS.has(name)) return true;
|
|
8700
|
-
if (name.startsWith(".") && !HIDDEN_DIR_WHITELIST.has(name)) return true;
|
|
8701
|
-
return false;
|
|
8702
|
-
}
|
|
8703
|
-
function pathMod$1(pathClass) {
|
|
8704
|
-
return pathClass === "win32" ? win32Path : posixPath;
|
|
8705
|
-
}
|
|
8706
|
-
function basename$1(p, pathClass) {
|
|
8707
|
-
return pathMod$1(pathClass).basename(p);
|
|
8708
|
-
}
|
|
8709
|
-
function joinPath(parent, child, pathClass) {
|
|
8710
|
-
return pathMod$1(pathClass).join(parent, child);
|
|
8711
|
-
}
|
|
8712
|
-
//#endregion
|
|
8713
8601
|
//#region src/agent/injection/permission-mode.ts
|
|
8714
|
-
const
|
|
8602
|
+
const AUTO_MODE_REMINDER = [
|
|
8715
8603
|
"Auto permission mode is active. Tool approvals will be handled automatically while this mode remains enabled.",
|
|
8716
8604
|
" - Continue normally without pausing for approval prompts.",
|
|
8717
8605
|
" - Do NOT call AskUserQuestion while auto mode is active. Make a reasonable decision and continue without asking the user."
|
|
8718
8606
|
].join("\n");
|
|
8719
|
-
|
|
8607
|
+
/**
|
|
8608
|
+
* Ephemeral injector for permission mode state.
|
|
8609
|
+
*
|
|
8610
|
+
* Emits the current permission mode as an ephemeral injection placed at
|
|
8611
|
+
* the `'before_user'` position. Unlike the previous persistent approach
|
|
8612
|
+
* (which recorded transition events into history), the ephemeral approach
|
|
8613
|
+
* always reflects the current state — surviving compaction and avoiding
|
|
8614
|
+
* history pollution.
|
|
8615
|
+
*
|
|
8616
|
+
* Only auto mode produces an injection; in all other modes the absence
|
|
8617
|
+
* of a reminder signals that normal approval prompts apply.
|
|
8618
|
+
*/
|
|
8720
8619
|
var PermissionModeInjector = class extends DynamicInjector {
|
|
8721
8620
|
injectionVariant = "permission_mode";
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8621
|
+
getInjection() {}
|
|
8622
|
+
getEphemeral() {
|
|
8623
|
+
if (this.agent.permission.mode !== "auto") return [];
|
|
8624
|
+
return [{
|
|
8625
|
+
kind: "system_reminder",
|
|
8626
|
+
content: AUTO_MODE_REMINDER,
|
|
8627
|
+
position: "before_user"
|
|
8628
|
+
}];
|
|
8629
|
+
}
|
|
8630
|
+
};
|
|
8631
|
+
//#endregion
|
|
8632
|
+
//#region src/agent/injection/timestamp.ts
|
|
8633
|
+
/**
|
|
8634
|
+
* Ephemeral injector that provides the current timestamp at request time.
|
|
8635
|
+
*
|
|
8636
|
+
* The timestamp is rendered fresh on every step (not frozen) and placed
|
|
8637
|
+
* at the `'before_user'` position so it never breaks the cached prefix.
|
|
8638
|
+
* This aligns with the prompt-cache best practice of keeping per-request
|
|
8639
|
+
* dynamic content out of the cacheable system-prompt blocks.
|
|
8640
|
+
*/
|
|
8641
|
+
var TimestampInjector = class extends DynamicInjector {
|
|
8642
|
+
injectionVariant = "timestamp";
|
|
8643
|
+
getInjection() {}
|
|
8644
|
+
getEphemeral() {
|
|
8645
|
+
return [{
|
|
8646
|
+
kind: "system_reminder",
|
|
8647
|
+
content: `The current date and time in ISO format is \`${(/* @__PURE__ */ new Date()).toISOString()}\`. This is only a reference for you when searching the web or checking file modification time, etc. If you need the exact time, use Bash tool with proper command.`,
|
|
8648
|
+
position: "before_user"
|
|
8649
|
+
}];
|
|
8730
8650
|
}
|
|
8731
8651
|
};
|
|
8732
8652
|
//#endregion
|
|
@@ -8736,11 +8656,20 @@ var InjectionManager = class {
|
|
|
8736
8656
|
injectors;
|
|
8737
8657
|
constructor(agent) {
|
|
8738
8658
|
this.agent = agent;
|
|
8739
|
-
this.injectors = [new PermissionModeInjector(agent), new
|
|
8659
|
+
this.injectors = [new PermissionModeInjector(agent), new TimestampInjector(agent)];
|
|
8740
8660
|
}
|
|
8741
8661
|
async inject() {
|
|
8742
8662
|
for (const injector of this.injectors) await injector.inject();
|
|
8743
8663
|
}
|
|
8664
|
+
getEphemeralInjections() {
|
|
8665
|
+
return this.injectors.flatMap((injector) => {
|
|
8666
|
+
try {
|
|
8667
|
+
return injector.getEphemeral?.() ?? [];
|
|
8668
|
+
} catch {
|
|
8669
|
+
return [];
|
|
8670
|
+
}
|
|
8671
|
+
});
|
|
8672
|
+
}
|
|
8744
8673
|
onContextClear() {
|
|
8745
8674
|
for (const injector of this.injectors) injector.onContextClear();
|
|
8746
8675
|
}
|
|
@@ -11557,8 +11486,7 @@ var AgentRecords = class {
|
|
|
11557
11486
|
tools: "tools",
|
|
11558
11487
|
usage: "usage",
|
|
11559
11488
|
background: "background",
|
|
11560
|
-
full_compaction: "fullCompaction"
|
|
11561
|
-
plan_mode: "planMode"
|
|
11489
|
+
full_compaction: "fullCompaction"
|
|
11562
11490
|
}[recordType.split(".")[0]] ?? null;
|
|
11563
11491
|
}
|
|
11564
11492
|
async replay() {
|
|
@@ -13603,8 +13531,8 @@ var ToolManager = class {
|
|
|
13603
13531
|
return (input) => withProviderRequestAuth(resolveAuth, (auth) => uploadVideo(input, { auth }));
|
|
13604
13532
|
}
|
|
13605
13533
|
get loopTools() {
|
|
13606
|
-
const builtinNames = [...this.builtinTools.keys()].filter((name) => this.enabledTools.has(name)).
|
|
13607
|
-
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();
|
|
13608
13536
|
const mcpNames = [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name));
|
|
13609
13537
|
return [
|
|
13610
13538
|
...builtinNames.map((name) => this.builtinTools.get(name)),
|
|
@@ -13718,20 +13646,25 @@ const CACHE_BOUNDARY_MARKER = "__CACHE_BOUNDARY__";
|
|
|
13718
13646
|
*
|
|
13719
13647
|
* These headers mark natural breaks in the system prompt where cache boundaries should be placed:
|
|
13720
13648
|
* - "# Project Information" marks the start of project-specific content
|
|
13649
|
+
* - "# Working Environment" marks the start of session-specific environment (OS, working directory)
|
|
13721
13650
|
* - "# Skills" marks the start of session-specific skills listing
|
|
13722
13651
|
*/
|
|
13723
|
-
const IMPLICIT_BOUNDARY_HEADERS = [
|
|
13652
|
+
const IMPLICIT_BOUNDARY_HEADERS = [
|
|
13653
|
+
"# Project Information",
|
|
13654
|
+
"# Working Environment",
|
|
13655
|
+
"# Skills"
|
|
13656
|
+
];
|
|
13724
13657
|
/**
|
|
13725
13658
|
* Block names by position.
|
|
13726
13659
|
*
|
|
13727
13660
|
* - First block (before first marker): 'base'
|
|
13728
13661
|
* - Last block (after last marker): 'sessionContext'
|
|
13729
|
-
* - Intermediate blocks: Sequential names from 'projectInstructions', '
|
|
13662
|
+
* - Intermediate blocks: Sequential names from 'projectInstructions', 'workingEnvironment', etc.
|
|
13730
13663
|
*/
|
|
13731
13664
|
const BLOCK_NAMES = [
|
|
13732
13665
|
"base",
|
|
13733
13666
|
"projectInstructions",
|
|
13734
|
-
"
|
|
13667
|
+
"workingEnvironment",
|
|
13735
13668
|
"sessionContext"
|
|
13736
13669
|
];
|
|
13737
13670
|
/**
|
|
@@ -13775,7 +13708,7 @@ function findImplicitBoundaries(prompt) {
|
|
|
13775
13708
|
const index = prompt.indexOf(header);
|
|
13776
13709
|
if (index !== -1) boundaries.push(index);
|
|
13777
13710
|
}
|
|
13778
|
-
return boundaries.
|
|
13711
|
+
return boundaries.toSorted((a, b) => a - b);
|
|
13779
13712
|
}
|
|
13780
13713
|
/**
|
|
13781
13714
|
* Split prompt by implicit boundaries into blocks.
|
|
@@ -14555,8 +14488,8 @@ var TurnFlow = class {
|
|
|
14555
14488
|
completionBudgetConfig
|
|
14556
14489
|
}),
|
|
14557
14490
|
buildMessages: () => {
|
|
14558
|
-
const
|
|
14559
|
-
return applyCacheStaking(
|
|
14491
|
+
const ephemeral = this.agent.injection.getEphemeralInjections();
|
|
14492
|
+
return applyCacheStaking(this.agent.context.getMessages(ephemeral), { previousTurnMessageCount: this._previousTurnMessageCount });
|
|
14560
14493
|
},
|
|
14561
14494
|
dispatchEvent: this.buildDispatchEvent(turnId),
|
|
14562
14495
|
tools: this.agent.tools.loopTools,
|
|
@@ -15200,15 +15133,6 @@ var Agent = class {
|
|
|
15200
15133
|
getModel: () => {
|
|
15201
15134
|
return this.config.modelAlias ?? "";
|
|
15202
15135
|
},
|
|
15203
|
-
enterPlan: async () => {
|
|
15204
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15205
|
-
},
|
|
15206
|
-
cancelPlan: async () => {
|
|
15207
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15208
|
-
},
|
|
15209
|
-
clearPlan: async () => {
|
|
15210
|
-
throw new ByfError(ErrorCodes.NOT_IMPLEMENTED, "Plan mode has been removed");
|
|
15211
|
-
},
|
|
15212
15136
|
beginCompaction: (payload) => {
|
|
15213
15137
|
this.fullCompaction.begin({
|
|
15214
15138
|
source: "manual",
|
|
@@ -15243,7 +15167,6 @@ var Agent = class {
|
|
|
15243
15167
|
getContext: () => this.context.data(),
|
|
15244
15168
|
getConfig: () => this.config.data(),
|
|
15245
15169
|
getPermission: () => this.permission.data(),
|
|
15246
|
-
getPlan: async () => null,
|
|
15247
15170
|
getUsage: () => this.usage.data(),
|
|
15248
15171
|
getTools: () => this.tools.data(),
|
|
15249
15172
|
getBackground: (payload) => this.background.list(payload.activeOnly ?? false, payload.limit)
|
|
@@ -15376,6 +15299,21 @@ function proxyWithExtraPayload(methods, extraPayload) {
|
|
|
15376
15299
|
}, ...args);
|
|
15377
15300
|
} });
|
|
15378
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
|
+
}
|
|
15379
15317
|
//#endregion
|
|
15380
15318
|
//#region src/config/schema.ts
|
|
15381
15319
|
const ProviderTypeSchema = z.enum([
|
|
@@ -15475,9 +15413,18 @@ const ByfServiceConfigSchema = z.object({
|
|
|
15475
15413
|
oauth: OAuthRefSchema.optional(),
|
|
15476
15414
|
customHeaders: StringRecordSchema.optional()
|
|
15477
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() });
|
|
15478
15425
|
const ServicesConfigSchema = z.object({
|
|
15479
|
-
|
|
15480
|
-
|
|
15426
|
+
webSearch: WebSearchConfigSchema.optional(),
|
|
15427
|
+
fetchUrl: ByfServiceConfigSchema.optional()
|
|
15481
15428
|
});
|
|
15482
15429
|
const McpServerCommonFields = {
|
|
15483
15430
|
enabled: z.boolean().optional(),
|
|
@@ -15543,8 +15490,8 @@ const LoopControlPatchSchema = LoopControlSchema.partial();
|
|
|
15543
15490
|
const BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
|
|
15544
15491
|
const ByfServiceConfigPatchSchema = ByfServiceConfigSchema.partial();
|
|
15545
15492
|
const ServicesConfigPatchSchema = z.object({
|
|
15546
|
-
|
|
15547
|
-
|
|
15493
|
+
webSearch: WebSearchConfigSchema.optional(),
|
|
15494
|
+
fetchUrl: ByfServiceConfigPatchSchema.optional()
|
|
15548
15495
|
});
|
|
15549
15496
|
const ByfConfigPatchSchema = z.object({
|
|
15550
15497
|
providers: z.record(z.string(), ProviderConfigPatchSchema).optional(),
|
|
@@ -15792,10 +15739,15 @@ function transformServiceData(data) {
|
|
|
15792
15739
|
const targetKey = snakeToCamel(key);
|
|
15793
15740
|
if (targetKey === "oauth") out[targetKey] = isPlainObject(value) ? transformPlainObject(value) : value;
|
|
15794
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);
|
|
15795
15744
|
else out[targetKey] = value;
|
|
15796
15745
|
}
|
|
15797
15746
|
return out;
|
|
15798
15747
|
}
|
|
15748
|
+
function identity(v) {
|
|
15749
|
+
return v;
|
|
15750
|
+
}
|
|
15799
15751
|
function transformLoopControlData(data) {
|
|
15800
15752
|
const out = transformPlainObject(data);
|
|
15801
15753
|
if (out["maxStepsPerTurn"] === void 0 && out["maxStepsPerRun"] !== void 0) out["maxStepsPerTurn"] = out["maxStepsPerRun"];
|
|
@@ -15815,11 +15767,11 @@ function configToTomlData(config) {
|
|
|
15815
15767
|
delete out["default_yolo"];
|
|
15816
15768
|
delete out["defaultYolo"];
|
|
15817
15769
|
delete out["defaultPermissionMode"];
|
|
15770
|
+
delete out["default_thinking"];
|
|
15818
15771
|
for (const key of [
|
|
15819
15772
|
"defaultProvider",
|
|
15820
15773
|
"defaultModel",
|
|
15821
15774
|
"yolo",
|
|
15822
|
-
"defaultThinking",
|
|
15823
15775
|
"defaultPermissionMode",
|
|
15824
15776
|
"mergeAllAvailableSkills",
|
|
15825
15777
|
"extraSkillDirs"
|
|
@@ -15888,10 +15840,17 @@ function permissionRuleToToml(rule) {
|
|
|
15888
15840
|
}
|
|
15889
15841
|
function servicesToToml(services, rawServices) {
|
|
15890
15842
|
const out = cloneRecord(rawServices);
|
|
15891
|
-
if (services.
|
|
15892
|
-
|
|
15893
|
-
|
|
15894
|
-
|
|
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"];
|
|
15895
15854
|
return out;
|
|
15896
15855
|
}
|
|
15897
15856
|
function serviceToToml(service) {
|
|
@@ -15949,70 +15908,659 @@ function isFileExistsError(error) {
|
|
|
15949
15908
|
return typeof error === "object" && error !== null && error.code === "EEXIST";
|
|
15950
15909
|
}
|
|
15951
15910
|
//#endregion
|
|
15952
|
-
//#region src/
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
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"
|
|
15960
15940
|
}
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
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
|
+
}];
|
|
15965
15956
|
/**
|
|
15966
|
-
*
|
|
15967
|
-
*
|
|
15968
|
-
*
|
|
15969
|
-
*
|
|
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.
|
|
15970
15961
|
*/
|
|
15971
|
-
|
|
15972
|
-
|
|
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);
|
|
15973
15982
|
return {
|
|
15974
|
-
|
|
15975
|
-
|
|
15983
|
+
modelName,
|
|
15984
|
+
providerName,
|
|
15985
|
+
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
15986
|
+
provider
|
|
15976
15987
|
};
|
|
15977
15988
|
}
|
|
15978
|
-
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();
|
|
15979
15994
|
return {
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
inputSchema: tool.inputSchema
|
|
15995
|
+
...resolved,
|
|
15996
|
+
resolveAuth
|
|
15983
15997
|
};
|
|
15984
15998
|
}
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
if (
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
};
|
|
16008
|
-
}
|
|
16009
|
-
return {
|
|
16010
|
-
content: [],
|
|
16011
|
-
isError: false
|
|
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 };
|
|
16012
16022
|
};
|
|
16013
16023
|
}
|
|
16014
|
-
|
|
16015
|
-
|
|
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
|
+
];
|
|
16065
|
+
/**
|
|
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.
|
|
16072
|
+
*/
|
|
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)
|
|
16100
|
+
};
|
|
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)
|
|
16149
|
+
};
|
|
16150
|
+
default: {
|
|
16151
|
+
const exhaustive = provider.type;
|
|
16152
|
+
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
16153
|
+
}
|
|
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;
|
|
16169
|
+
return {
|
|
16170
|
+
...provider,
|
|
16171
|
+
apiKey: "capability-probe"
|
|
16172
|
+
};
|
|
16173
|
+
}
|
|
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
|
|
16016
16564
|
/**
|
|
16017
16565
|
* Wraps the SDK streamable-HTTP transport as a kosong {@link MCPClient}.
|
|
16018
16566
|
* Static bearer tokens are looked up from `process.env[bearerTokenEnvVar]`.
|
|
@@ -16868,7 +17416,7 @@ var SessionSubagentHost = class {
|
|
|
16868
17416
|
const { id, agent } = await this.session.createAgent({
|
|
16869
17417
|
type: "sub",
|
|
16870
17418
|
generate: parent.rawGenerate
|
|
16871
|
-
}, void 0, this.ownerAgentId);
|
|
17419
|
+
}, void 0, this.ownerAgentId, options.parentToolCallId);
|
|
16872
17420
|
const controller = new AbortController();
|
|
16873
17421
|
const unlinkAbortSignal = linkAbortSignal(options.signal, controller);
|
|
16874
17422
|
this.activeChildren.set(id, {
|
|
@@ -17167,7 +17715,7 @@ var Session = class {
|
|
|
17167
17715
|
})) return;
|
|
17168
17716
|
await Promise.all(Array.from(this.agents.values(), (agent) => agent.background.stopAll("Session closed")));
|
|
17169
17717
|
}
|
|
17170
|
-
async createAgent(config, profile, parentAgentId) {
|
|
17718
|
+
async createAgent(config, profile, parentAgentId, parentToolCallId) {
|
|
17171
17719
|
await this.skillsReady;
|
|
17172
17720
|
const type = config.type ?? "main";
|
|
17173
17721
|
const id = type === "main" ? "main" : this.nextGeneratedAgentId();
|
|
@@ -17179,7 +17727,8 @@ var Session = class {
|
|
|
17179
17727
|
this.metadata.agents[id] = {
|
|
17180
17728
|
homedir,
|
|
17181
17729
|
type,
|
|
17182
|
-
parentAgentId: parentAgentId ?? null
|
|
17730
|
+
parentAgentId: parentAgentId ?? null,
|
|
17731
|
+
parentToolCallId
|
|
17183
17732
|
};
|
|
17184
17733
|
this.writeMetadata();
|
|
17185
17734
|
return {
|
|
@@ -17683,11 +18232,15 @@ function createProxiedFetch(deps) {
|
|
|
17683
18232
|
const proxyUrl = getProxyForUrl(url, envLookup, sysProxy);
|
|
17684
18233
|
const noProxyMatch = noProxy !== void 0 && isNoProxyHost(hostname, noProxy);
|
|
17685
18234
|
const controller = new AbortController();
|
|
17686
|
-
const timeoutId = setTimeout(() =>
|
|
18235
|
+
const timeoutId = setTimeout(() => {
|
|
18236
|
+
controller.abort();
|
|
18237
|
+
}, REQUEST_TIMEOUT_MS);
|
|
17687
18238
|
if (init?.signal) if (init.signal.aborted) {
|
|
17688
18239
|
clearTimeout(timeoutId);
|
|
17689
18240
|
controller.abort();
|
|
17690
|
-
} else init.signal.addEventListener("abort", () =>
|
|
18241
|
+
} else init.signal.addEventListener("abort", () => {
|
|
18242
|
+
controller.abort();
|
|
18243
|
+
}, { once: true });
|
|
17691
18244
|
const mergedInit = {
|
|
17692
18245
|
...init,
|
|
17693
18246
|
signal: controller.signal
|
|
@@ -17699,7 +18252,7 @@ function createProxiedFetch(deps) {
|
|
|
17699
18252
|
return response;
|
|
17700
18253
|
} catch (error) {
|
|
17701
18254
|
clearTimeout(timeoutId);
|
|
17702
|
-
if (isRetryableError(error) && proxyUrl && !noProxyMatch) return
|
|
18255
|
+
if (isRetryableError(error) && proxyUrl && !noProxyMatch) return retryViaProxy(input, init, proxyUrl, innerFetch);
|
|
17703
18256
|
throw error;
|
|
17704
18257
|
}
|
|
17705
18258
|
};
|
|
@@ -17707,11 +18260,15 @@ function createProxiedFetch(deps) {
|
|
|
17707
18260
|
}
|
|
17708
18261
|
async function retryViaProxy(input, init, proxyUrl, innerFetch) {
|
|
17709
18262
|
const controller = new AbortController();
|
|
17710
|
-
const timeoutId = setTimeout(() =>
|
|
18263
|
+
const timeoutId = setTimeout(() => {
|
|
18264
|
+
controller.abort();
|
|
18265
|
+
}, REQUEST_TIMEOUT_MS);
|
|
17711
18266
|
if (init?.signal) if (init.signal.aborted) {
|
|
17712
18267
|
clearTimeout(timeoutId);
|
|
17713
18268
|
controller.abort();
|
|
17714
|
-
} else init.signal.addEventListener("abort", () =>
|
|
18269
|
+
} else init.signal.addEventListener("abort", () => {
|
|
18270
|
+
controller.abort();
|
|
18271
|
+
}, { once: true });
|
|
17715
18272
|
const dispatcher = new ProxyAgent(proxyUrl);
|
|
17716
18273
|
const retryInit = {
|
|
17717
18274
|
...init,
|
|
@@ -17853,90 +18410,196 @@ var RemoteFetchURLProvider = class {
|
|
|
17853
18410
|
if (token.trim().length > 0) return token;
|
|
17854
18411
|
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
17855
18412
|
} catch (error) {
|
|
17856
|
-
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
17857
|
-
throw error;
|
|
18413
|
+
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
18414
|
+
throw error;
|
|
18415
|
+
}
|
|
18416
|
+
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
18417
|
+
throw new Error("Remote fetch service is not configured: missing API key or token provider.");
|
|
18418
|
+
}
|
|
18419
|
+
};
|
|
18420
|
+
//#endregion
|
|
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;
|
|
18449
|
+
baseUrl;
|
|
18450
|
+
fetchImpl;
|
|
18451
|
+
constructor(options) {
|
|
18452
|
+
this.apiKeys = options.apiKeys;
|
|
18453
|
+
this.baseUrl = options.baseUrl ?? "https://api.exa.ai/search";
|
|
18454
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
18455
|
+
}
|
|
18456
|
+
async search(query, options) {
|
|
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
|
|
18464
|
+
};
|
|
18465
|
+
const body = JSON.stringify({
|
|
18466
|
+
query,
|
|
18467
|
+
numResults: limit,
|
|
18468
|
+
contents
|
|
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");
|
|
18502
|
+
}
|
|
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);
|
|
18515
|
+
}
|
|
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
|
+
});
|
|
18545
|
+
} catch (error) {
|
|
18546
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
17858
18547
|
}
|
|
17859
|
-
|
|
17860
|
-
throw new Error("Remote fetch service is not configured: missing API key or token provider.");
|
|
18548
|
+
throw lastError ?? /* @__PURE__ */ new Error("Brave search failed: no API keys configured");
|
|
17861
18549
|
}
|
|
17862
18550
|
};
|
|
18551
|
+
registerProvider("brave", BraveWebSearchProvider);
|
|
17863
18552
|
//#endregion
|
|
17864
|
-
//#region src/tools/providers/
|
|
17865
|
-
var
|
|
17866
|
-
|
|
17867
|
-
apiKey;
|
|
18553
|
+
//#region src/tools/providers/firecrawl.ts
|
|
18554
|
+
var FirecrawlWebSearchProvider = class {
|
|
18555
|
+
apiKeys;
|
|
17868
18556
|
baseUrl;
|
|
17869
|
-
defaultHeaders;
|
|
17870
|
-
customHeaders;
|
|
17871
18557
|
fetchImpl;
|
|
17872
18558
|
constructor(options) {
|
|
17873
|
-
this.
|
|
17874
|
-
this.
|
|
17875
|
-
this.baseUrl = options.baseUrl;
|
|
17876
|
-
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
17877
|
-
this.customHeaders = options.customHeaders ?? {};
|
|
18559
|
+
this.apiKeys = options.apiKeys;
|
|
18560
|
+
this.baseUrl = options.baseUrl ?? "https://api.firecrawl.dev/v2/search";
|
|
17878
18561
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
17879
18562
|
}
|
|
17880
18563
|
async search(query, options) {
|
|
17881
|
-
const
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
18564
|
+
const limit = options?.limit ?? 5;
|
|
18565
|
+
const includeContent = options?.includeContent ?? false;
|
|
18566
|
+
const requestBody = {
|
|
18567
|
+
query,
|
|
18568
|
+
limit
|
|
17886
18569
|
};
|
|
17887
|
-
|
|
17888
|
-
const
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
|
|
17913
|
-
headers: {
|
|
17914
|
-
...this.defaultHeaders,
|
|
17915
|
-
Authorization: `Bearer ${accessToken}`,
|
|
17916
|
-
"Content-Type": "application/json",
|
|
17917
|
-
...this.customHeaders
|
|
17918
|
-
},
|
|
17919
|
-
body: bodyJson
|
|
17920
|
-
});
|
|
17921
|
-
}
|
|
17922
|
-
async resolveApiKey() {
|
|
17923
|
-
if (this.tokenProvider !== void 0) try {
|
|
17924
|
-
return await this.tokenProvider.getAccessToken();
|
|
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
|
+
});
|
|
17925
18596
|
} catch (error) {
|
|
17926
|
-
|
|
17927
|
-
throw error;
|
|
18597
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
17928
18598
|
}
|
|
17929
|
-
|
|
17930
|
-
throw new Error("Remote search service is not configured: missing API key or token provider.");
|
|
18599
|
+
throw lastError ?? /* @__PURE__ */ new Error("Firecrawl search failed: no API keys configured");
|
|
17931
18600
|
}
|
|
17932
18601
|
};
|
|
17933
|
-
|
|
17934
|
-
try {
|
|
17935
|
-
return await response.text();
|
|
17936
|
-
} catch {
|
|
17937
|
-
return "";
|
|
17938
|
-
}
|
|
17939
|
-
}
|
|
18602
|
+
registerProvider("firecrawl", FirecrawlWebSearchProvider);
|
|
17940
18603
|
//#endregion
|
|
17941
18604
|
//#region src/utils/environment.ts
|
|
17942
18605
|
/**
|
|
@@ -18198,15 +18861,6 @@ var SessionAPIImpl = class {
|
|
|
18198
18861
|
getModel({ agentId, ...payload }) {
|
|
18199
18862
|
return this.getAgent(agentId).getModel(payload);
|
|
18200
18863
|
}
|
|
18201
|
-
enterPlan({ agentId, ...payload }) {
|
|
18202
|
-
return this.getAgent(agentId).enterPlan(payload);
|
|
18203
|
-
}
|
|
18204
|
-
cancelPlan({ agentId, ...payload }) {
|
|
18205
|
-
return this.getAgent(agentId).cancelPlan(payload);
|
|
18206
|
-
}
|
|
18207
|
-
clearPlan({ agentId, ...payload }) {
|
|
18208
|
-
return this.getAgent(agentId).clearPlan(payload);
|
|
18209
|
-
}
|
|
18210
18864
|
beginCompaction({ agentId, ...payload }) {
|
|
18211
18865
|
return this.getAgent(agentId).beginCompaction(payload);
|
|
18212
18866
|
}
|
|
@@ -18247,9 +18901,6 @@ var SessionAPIImpl = class {
|
|
|
18247
18901
|
getPermission({ agentId, ...payload }) {
|
|
18248
18902
|
return this.getAgent(agentId).getPermission(payload);
|
|
18249
18903
|
}
|
|
18250
|
-
getPlan({ agentId, ...payload }) {
|
|
18251
|
-
return this.getAgent(agentId).getPlan(payload);
|
|
18252
|
-
}
|
|
18253
18904
|
getUsage({ agentId, ...payload }) {
|
|
18254
18905
|
return this.getAgent(agentId).getUsage(payload);
|
|
18255
18906
|
}
|
|
@@ -19397,207 +20048,6 @@ async function readOptionalFile(path) {
|
|
|
19397
20048
|
}
|
|
19398
20049
|
}
|
|
19399
20050
|
//#endregion
|
|
19400
|
-
//#region src/providers/runtime-provider.ts
|
|
19401
|
-
function resolveRuntimeProvider(input) {
|
|
19402
|
-
const modelName = input.model ?? input.config.defaultModel;
|
|
19403
|
-
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.");
|
|
19404
|
-
const alias = input.config.models?.[modelName];
|
|
19405
|
-
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.`);
|
|
19406
|
-
const resolvedModel = alias.model;
|
|
19407
|
-
const providerName = alias.provider ?? input.config.defaultProvider;
|
|
19408
|
-
const providerConfig = providerName === void 0 ? void 0 : input.config.providers[providerName];
|
|
19409
|
-
if (providerName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a provider in config.toml.`);
|
|
19410
|
-
if (providerConfig === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${modelName}" is not configured.`);
|
|
19411
|
-
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.`);
|
|
19412
|
-
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.`);
|
|
19413
|
-
const provider = toKosongProviderConfig(providerConfig, resolvedModel, input.byfRequestHeaders, alias.maxOutputSize, alias.reasoningKey, input.promptCacheKey);
|
|
19414
|
-
return {
|
|
19415
|
-
modelName,
|
|
19416
|
-
providerName,
|
|
19417
|
-
modelCapabilities: resolveModelCapabilities(alias, provider),
|
|
19418
|
-
provider
|
|
19419
|
-
};
|
|
19420
|
-
}
|
|
19421
|
-
async function resolveRuntimeProviderWithOAuth(input) {
|
|
19422
|
-
const resolved = resolveRuntimeProvider(input);
|
|
19423
|
-
const resolveAuth = createRuntimeProviderAuthResolver(input, resolved);
|
|
19424
|
-
if (resolveAuth === void 0) return resolved;
|
|
19425
|
-
await resolveAuth();
|
|
19426
|
-
return {
|
|
19427
|
-
...resolved,
|
|
19428
|
-
resolveAuth
|
|
19429
|
-
};
|
|
19430
|
-
}
|
|
19431
|
-
function createRuntimeProviderAuthResolver(input, resolved = resolveRuntimeProvider(input)) {
|
|
19432
|
-
const providerName = resolved.providerName;
|
|
19433
|
-
if (providerName === void 0) return void 0;
|
|
19434
|
-
const providerConfig = input.config.providers[providerName];
|
|
19435
|
-
if (providerConfig?.oauth === void 0) return void 0;
|
|
19436
|
-
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.`);
|
|
19437
|
-
const tokenProvider = input.resolveOAuthTokenProvider?.(providerName, providerConfig.oauth);
|
|
19438
|
-
if (tokenProvider === void 0) return async () => {
|
|
19439
|
-
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
19440
|
-
};
|
|
19441
|
-
return async (options) => {
|
|
19442
|
-
let apiKey;
|
|
19443
|
-
try {
|
|
19444
|
-
apiKey = await tokenProvider.getAccessToken(options?.forceRefresh === true ? { force: true } : void 0);
|
|
19445
|
-
} catch (error) {
|
|
19446
|
-
if (!isAuthLoginRequired(error)) (input.log ?? log).warn("oauth token fetch failed", {
|
|
19447
|
-
providerName,
|
|
19448
|
-
error
|
|
19449
|
-
});
|
|
19450
|
-
throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`, { cause: error });
|
|
19451
|
-
}
|
|
19452
|
-
if (apiKey.trim().length === 0) throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
|
|
19453
|
-
return { apiKey };
|
|
19454
|
-
};
|
|
19455
|
-
}
|
|
19456
|
-
function isAuthLoginRequired(error) {
|
|
19457
|
-
return isByfError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED;
|
|
19458
|
-
}
|
|
19459
|
-
function resolveModelCapabilities(alias, provider) {
|
|
19460
|
-
const capabilities = new Set((alias.capabilities ?? []).map((capability) => capability.trim().toLowerCase()));
|
|
19461
|
-
const has = (capability) => capabilities.has(capability);
|
|
19462
|
-
const providerCapability = createProvider(providerForCapabilityProbe(provider)).getCapability?.(provider.model) ?? UNKNOWN_CAPABILITY;
|
|
19463
|
-
return {
|
|
19464
|
-
image_in: has("image_in") || providerCapability.image_in,
|
|
19465
|
-
video_in: has("video_in") || providerCapability.video_in,
|
|
19466
|
-
audio_in: has("audio_in") || providerCapability.audio_in,
|
|
19467
|
-
thinking: has("thinking") || has("always_thinking") || providerCapability.thinking,
|
|
19468
|
-
tool_use: has("tool_use") || providerCapability.tool_use,
|
|
19469
|
-
thinking_effort: has("thinking_effort") || providerCapability.thinking_effort,
|
|
19470
|
-
thinking_xhigh: has("thinking_xhigh") || providerCapability.thinking_xhigh,
|
|
19471
|
-
thinking_max: has("thinking_max") || providerCapability.thinking_max,
|
|
19472
|
-
max_context_tokens: alias.maxContextSize
|
|
19473
|
-
};
|
|
19474
|
-
}
|
|
19475
|
-
function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey) {
|
|
19476
|
-
switch (provider.type) {
|
|
19477
|
-
case "anthropic": return {
|
|
19478
|
-
type: "anthropic",
|
|
19479
|
-
model,
|
|
19480
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "ANTHROPIC_BASE_URL"),
|
|
19481
|
-
apiKey: providerApiKey(provider),
|
|
19482
|
-
...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
|
|
19483
|
-
...defaultHeadersField(provider.customHeaders)
|
|
19484
|
-
};
|
|
19485
|
-
case "openai-completions": {
|
|
19486
|
-
const defaultHeaders = {
|
|
19487
|
-
...byfRequestHeaders,
|
|
19488
|
-
...provider.customHeaders
|
|
19489
|
-
};
|
|
19490
|
-
const generationKwargs = {
|
|
19491
|
-
prompt_cache_key: promptCacheKey,
|
|
19492
|
-
extra_body: provider.extraBody
|
|
19493
|
-
};
|
|
19494
|
-
if (Object.keys(defaultHeaders).length === 0) return {
|
|
19495
|
-
type: "openai-completions",
|
|
19496
|
-
model,
|
|
19497
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
19498
|
-
reasoningKey,
|
|
19499
|
-
thinkingEffortKey: provider.thinkingEffortKey,
|
|
19500
|
-
generationKwargs,
|
|
19501
|
-
apiKey: providerApiKey(provider)
|
|
19502
|
-
};
|
|
19503
|
-
return {
|
|
19504
|
-
type: "openai-completions",
|
|
19505
|
-
model,
|
|
19506
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
|
|
19507
|
-
reasoningKey,
|
|
19508
|
-
thinkingEffortKey: provider.thinkingEffortKey,
|
|
19509
|
-
generationKwargs,
|
|
19510
|
-
defaultHeaders,
|
|
19511
|
-
apiKey: providerApiKey(provider)
|
|
19512
|
-
};
|
|
19513
|
-
}
|
|
19514
|
-
case "google-genai": return {
|
|
19515
|
-
type: "google-genai",
|
|
19516
|
-
model,
|
|
19517
|
-
apiKey: providerApiKey(provider)
|
|
19518
|
-
};
|
|
19519
|
-
case "openai_responses": return {
|
|
19520
|
-
type: "openai_responses",
|
|
19521
|
-
model,
|
|
19522
|
-
baseUrl: providerValue(provider.baseUrl, provider.env, "OPENAI_BASE_URL"),
|
|
19523
|
-
apiKey: providerApiKey(provider),
|
|
19524
|
-
...defaultHeadersField(provider.customHeaders)
|
|
19525
|
-
};
|
|
19526
|
-
case "vertexai": return {
|
|
19527
|
-
type: "vertexai",
|
|
19528
|
-
model,
|
|
19529
|
-
vertexai: hasVertexAIServiceEnv(provider),
|
|
19530
|
-
apiKey: hasVertexAIServiceEnv(provider) ? void 0 : providerApiKey(provider),
|
|
19531
|
-
project: vertexAIProject(provider),
|
|
19532
|
-
location: vertexAILocation(provider)
|
|
19533
|
-
};
|
|
19534
|
-
default: {
|
|
19535
|
-
const exhaustive = provider.type;
|
|
19536
|
-
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
19537
|
-
}
|
|
19538
|
-
}
|
|
19539
|
-
}
|
|
19540
|
-
function defaultHeadersField(headers) {
|
|
19541
|
-
if (headers === void 0 || Object.keys(headers).length === 0) return {};
|
|
19542
|
-
return { defaultHeaders: { ...headers } };
|
|
19543
|
-
}
|
|
19544
|
-
function providerForCapabilityProbe(provider) {
|
|
19545
|
-
if (provider.type === "vertexai") return {
|
|
19546
|
-
...provider,
|
|
19547
|
-
vertexai: false,
|
|
19548
|
-
project: void 0,
|
|
19549
|
-
location: void 0,
|
|
19550
|
-
apiKey: provider.apiKey === void 0 || provider.apiKey.length === 0 ? "capability-probe" : provider.apiKey
|
|
19551
|
-
};
|
|
19552
|
-
if (provider.apiKey !== void 0 && provider.apiKey.length > 0) return provider;
|
|
19553
|
-
return {
|
|
19554
|
-
...provider,
|
|
19555
|
-
apiKey: "capability-probe"
|
|
19556
|
-
};
|
|
19557
|
-
}
|
|
19558
|
-
function providerApiKey(provider) {
|
|
19559
|
-
switch (provider.type) {
|
|
19560
|
-
case "anthropic": return providerValue(provider.apiKey, provider.env, "ANTHROPIC_API_KEY");
|
|
19561
|
-
case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
|
|
19562
|
-
case "openai-completions": return providerValue(provider.apiKey, provider.env, "BYF_API_KEY");
|
|
19563
|
-
case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
|
|
19564
|
-
case "vertexai": return nonEmptyString$1(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
|
|
19565
|
-
default: {
|
|
19566
|
-
const exhaustive = provider.type;
|
|
19567
|
-
throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
19568
|
-
}
|
|
19569
|
-
}
|
|
19570
|
-
}
|
|
19571
|
-
function hasVertexAIServiceEnv(provider) {
|
|
19572
|
-
return vertexAIProject(provider) !== void 0 && vertexAILocation(provider) !== void 0;
|
|
19573
|
-
}
|
|
19574
|
-
function vertexAIProject(provider) {
|
|
19575
|
-
return envValue(provider.env, "GOOGLE_CLOUD_PROJECT");
|
|
19576
|
-
}
|
|
19577
|
-
function vertexAILocation(provider) {
|
|
19578
|
-
return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
|
|
19579
|
-
}
|
|
19580
|
-
function providerValue(configured, env, envKey) {
|
|
19581
|
-
return nonEmptyString$1(configured) ?? envValue(env, envKey);
|
|
19582
|
-
}
|
|
19583
|
-
function envValue(env, key) {
|
|
19584
|
-
return nonEmptyString$1(env?.[key]);
|
|
19585
|
-
}
|
|
19586
|
-
function nonEmptyString$1(value) {
|
|
19587
|
-
const trimmed = value?.trim();
|
|
19588
|
-
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
19589
|
-
}
|
|
19590
|
-
function locationFromVertexAIBaseUrl(baseUrl) {
|
|
19591
|
-
const url = nonEmptyString$1(baseUrl);
|
|
19592
|
-
if (url === void 0) return void 0;
|
|
19593
|
-
try {
|
|
19594
|
-
const host = new URL(url).hostname;
|
|
19595
|
-
return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$1(host.slice(0, -26)) : void 0;
|
|
19596
|
-
} catch {
|
|
19597
|
-
return;
|
|
19598
|
-
}
|
|
19599
|
-
}
|
|
19600
|
-
//#endregion
|
|
19601
20051
|
//#region src/providers/provider-manager.ts
|
|
19602
20052
|
var ProviderManager = class ProviderManager {
|
|
19603
20053
|
options;
|
|
@@ -19929,15 +20379,6 @@ var ByfCore = class {
|
|
|
19929
20379
|
getModel({ sessionId, ...payload }) {
|
|
19930
20380
|
return this.sessionApi(sessionId).getModel(payload);
|
|
19931
20381
|
}
|
|
19932
|
-
enterPlan({ sessionId, ...payload }) {
|
|
19933
|
-
return this.sessionApi(sessionId).enterPlan(payload);
|
|
19934
|
-
}
|
|
19935
|
-
cancelPlan({ sessionId, ...payload }) {
|
|
19936
|
-
return this.sessionApi(sessionId).cancelPlan(payload);
|
|
19937
|
-
}
|
|
19938
|
-
clearPlan({ sessionId, ...payload }) {
|
|
19939
|
-
return this.sessionApi(sessionId).clearPlan(payload);
|
|
19940
|
-
}
|
|
19941
20382
|
beginCompaction({ sessionId, ...payload }) {
|
|
19942
20383
|
return this.sessionApi(sessionId).beginCompaction(payload);
|
|
19943
20384
|
}
|
|
@@ -19977,9 +20418,6 @@ var ByfCore = class {
|
|
|
19977
20418
|
getPermission({ sessionId, ...payload }) {
|
|
19978
20419
|
return this.sessionApi(sessionId).getPermission(payload);
|
|
19979
20420
|
}
|
|
19980
|
-
getPlan({ sessionId, ...payload }) {
|
|
19981
|
-
return this.sessionApi(sessionId).getPlan(payload);
|
|
19982
|
-
}
|
|
19983
20421
|
getUsage({ sessionId, ...payload }) {
|
|
19984
20422
|
return this.sessionApi(sessionId).getUsage(payload);
|
|
19985
20423
|
}
|
|
@@ -20067,8 +20505,8 @@ async function createRuntimeConfig(input) {
|
|
|
20067
20505
|
systemProxy: () => detectSystemProxy()
|
|
20068
20506
|
});
|
|
20069
20507
|
const localFetcher = new LocalFetchURLProvider({ fetchImpl: proxiedFetch });
|
|
20070
|
-
const
|
|
20071
|
-
const
|
|
20508
|
+
const fetchService = input.config.services?.fetchUrl;
|
|
20509
|
+
const webSearchConfig = input.config.services?.webSearch;
|
|
20072
20510
|
return {
|
|
20073
20511
|
kaos: localKaos,
|
|
20074
20512
|
osEnv: await detectEnvironmentFromNode(),
|
|
@@ -20080,12 +20518,11 @@ async function createRuntimeConfig(input) {
|
|
|
20080
20518
|
fetchImpl: proxiedFetch,
|
|
20081
20519
|
...serviceCredentials(fetchService, input.resolveOAuthTokenProvider)
|
|
20082
20520
|
}),
|
|
20083
|
-
webSearcher:
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
fetchImpl: proxiedFetch
|
|
20087
|
-
|
|
20088
|
-
})
|
|
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
|
+
})))
|
|
20089
20526
|
};
|
|
20090
20527
|
}
|
|
20091
20528
|
function serviceCredentials(service, resolveOAuthTokenProvider) {
|
|
@@ -20120,11 +20557,11 @@ async function resumeSessionResult(summary, session, warning) {
|
|
|
20120
20557
|
context,
|
|
20121
20558
|
replay: agent.replayBuilder.buildResult(),
|
|
20122
20559
|
permission,
|
|
20123
|
-
plan: null,
|
|
20124
20560
|
usage,
|
|
20125
20561
|
tools: await api.getTools({ agentId }),
|
|
20126
20562
|
toolStore: agent.tools.storeData(),
|
|
20127
|
-
background: agent.background.list(false)
|
|
20563
|
+
background: agent.background.list(false),
|
|
20564
|
+
parentToolCallId: session.metadata.agents[agentId]?.parentToolCallId
|
|
20128
20565
|
};
|
|
20129
20566
|
}
|
|
20130
20567
|
return {
|
|
@@ -20179,4 +20616,4 @@ function parsePositiveInt(value) {
|
|
|
20179
20616
|
return n;
|
|
20180
20617
|
}
|
|
20181
20618
|
//#endregion
|
|
20182
|
-
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 };
|