@byfriends/sdk 0.3.1 → 0.3.2

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.mjs CHANGED
@@ -3,23 +3,23 @@ import { dirname as __cjsShimDirname } from 'node:path';
3
3
  const __filename = __cjsShimFileURLToPath(import.meta.url);
4
4
  const __dirname = __cjsShimDirname(__filename);
5
5
  import { createRequire } from "node:module";
6
- import * as fs$8 from "node:fs";
7
- import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, renameSync, statSync, unlinkSync, writeSync } from "node:fs";
8
- import jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
9
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
10
7
  import * as path$8 from "node:path";
11
8
  import path, { basename, dirname, isAbsolute, join, normalize, posix, relative, resolve, win32 } from "node:path";
12
- import I, { createWriteStream as createWriteStream$1 } from "fs";
9
+ import I, { createWriteStream } from "fs";
13
10
  import Kr, { EventEmitter } from "events";
14
11
  import * as path$1$1 from "path";
15
12
  import $s, { dirname as dirname$1, parse } from "path";
16
13
  import { Buffer as Buffer$1 } from "buffer";
17
14
  import * as fs$9 from "fs/promises";
18
- import { writeFile as writeFile$1 } from "fs/promises";
15
+ import { writeFile } from "fs/promises";
19
16
  import Ns, { PassThrough, Readable } from "node:stream";
20
17
  import { finished, pipeline as pipeline$1 } from "node:stream/promises";
21
18
  import * as ks from "zlib";
22
19
  import qr from "zlib";
20
+ import * as fs$8 from "node:fs";
21
+ import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, renameSync, statSync, unlinkSync, writeSync } from "node:fs";
22
+ import jn, { access, appendFile, chmod, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, stat, unlink, writeFile as writeFile$1 } from "node:fs/promises";
23
23
  import { z } from "zod";
24
24
  import * as posixPath from "node:path/posix";
25
25
  import * as win32Path from "node:path/win32";
@@ -10288,7 +10288,7 @@ var require_gaxios = /* @__PURE__ */ __commonJSMin(((exports) => {
10288
10288
  }
10289
10289
  static async #getFetch() {
10290
10290
  const hasWindow = typeof window !== "undefined" && !!window;
10291
- this.#fetch ||= hasWindow ? window.fetch : (await import("./src-u7agTn5O.mjs")).default;
10291
+ this.#fetch ||= hasWindow ? window.fetch : (await import("./src-Dedv0Wfs.mjs")).default;
10292
10292
  return this.#fetch;
10293
10293
  }
10294
10294
  /**
@@ -36078,11 +36078,11 @@ var NodeDownloader = class {
36078
36078
  if (params.downloadPath) {
36079
36079
  const response = await downloadFile(params, apiClient);
36080
36080
  if (response instanceof HttpResponse) {
36081
- const writer = createWriteStream$1(params.downloadPath);
36081
+ const writer = createWriteStream(params.downloadPath);
36082
36082
  Readable.fromWeb(response.responseInternal.body).pipe(writer);
36083
36083
  await finished(writer);
36084
36084
  } else try {
36085
- await writeFile$1(params.downloadPath, response, { encoding: "base64" });
36085
+ await writeFile(params.downloadPath, response, { encoding: "base64" });
36086
36086
  } catch (error) {
36087
36087
  throw new Error(`Failed to write file to ${params.downloadPath}: ${error}`);
36088
36088
  }
@@ -38110,7 +38110,7 @@ function normalizeOpenAICompatToolSchema(schema) {
38110
38110
  }
38111
38111
  function ensureOpenAICompatPropertyTypes(schema) {
38112
38112
  const normalized = cloneJsonValue(schema);
38113
- if (!isRecord$6(normalized)) throw new Error("JSON Schema root must normalize to an object.");
38113
+ if (!isRecord$5(normalized)) throw new Error("JSON Schema root must normalize to an object.");
38114
38114
  recurseSchema(normalized);
38115
38115
  return normalized;
38116
38116
  }
@@ -38171,7 +38171,7 @@ function resolveLocalJsonPointer(root, ref) {
38171
38171
  let current = root;
38172
38172
  for (const rawPart of ref.slice(2).split("/")) {
38173
38173
  const part = unescapeJsonPointerPart(rawPart);
38174
- if (isRecord$6(current)) {
38174
+ if (isRecord$5(current)) {
38175
38175
  if (!hasOwn$1(current, part)) return { found: false };
38176
38176
  current = current[part];
38177
38177
  } else if (Array.isArray(current)) {
@@ -38193,20 +38193,20 @@ function parseJsonPointerArrayIndex(part) {
38193
38193
  return Number(part);
38194
38194
  }
38195
38195
  function recurseSchema(node) {
38196
- if (!isRecord$6(node)) return;
38196
+ if (!isRecord$5(node)) return;
38197
38197
  visitChildSchemas(node, normalizeProperty);
38198
38198
  }
38199
38199
  function visitChildSchemas(node, visit) {
38200
38200
  for (const { key, kind } of CHILD_SCHEMA_SLOTS) {
38201
38201
  const value = node[key];
38202
38202
  if (kind === "single") {
38203
- if (isRecord$6(value)) visit(value);
38203
+ if (isRecord$5(value)) visit(value);
38204
38204
  } else if (kind === "array") {
38205
38205
  if (Array.isArray(value)) for (const item of value) visit(item);
38206
38206
  } else if (kind === "map") {
38207
- if (isRecord$6(value)) for (const item of Object.values(value)) visit(item);
38207
+ if (isRecord$5(value)) for (const item of Object.values(value)) visit(item);
38208
38208
  } else if (kind === "schema-or-array") {
38209
- if (isRecord$6(value)) visit(value);
38209
+ if (isRecord$5(value)) visit(value);
38210
38210
  else if (Array.isArray(value)) for (const item of value) visit(item);
38211
38211
  }
38212
38212
  }
@@ -38218,7 +38218,7 @@ function childSchemaKeysForParentType(parentType) {
38218
38218
  });
38219
38219
  }
38220
38220
  function normalizeProperty(node) {
38221
- if (!isRecord$6(node)) return;
38221
+ if (!isRecord$5(node)) return;
38222
38222
  if (!hasOwn$1(node, "type") && !hasAnyKey(node, TYPE_COMPLETION_SKIP_KEYS)) {
38223
38223
  const enumValues = node["enum"];
38224
38224
  if (Array.isArray(enumValues) && enumValues.length > 0) node["type"] = inferTypeFromValues(enumValues);
@@ -38282,14 +38282,14 @@ function hasAnyKey(obj, keys) {
38282
38282
  }
38283
38283
  function cloneJsonValue(value) {
38284
38284
  if (Array.isArray(value)) return value.map((item) => cloneJsonValue(item));
38285
- if (isRecord$6(value)) {
38285
+ if (isRecord$5(value)) {
38286
38286
  const cloned = {};
38287
38287
  for (const [key, child] of Object.entries(value)) cloned[key] = cloneJsonValue(child);
38288
38288
  return cloned;
38289
38289
  }
38290
38290
  return value;
38291
38291
  }
38292
- function isRecord$6(value) {
38292
+ function isRecord$5(value) {
38293
38293
  return typeof value === "object" && value !== null && !Array.isArray(value);
38294
38294
  }
38295
38295
  function hasOwn$1(obj, key) {
@@ -54783,7 +54783,7 @@ function isAbortError(err) {
54783
54783
  if (err instanceof Error) return err.name === "AbortError";
54784
54784
  return false;
54785
54785
  }
54786
- function errorMessage$3(err) {
54786
+ function errorMessage$2(err) {
54787
54787
  if (err instanceof Error) return err.message;
54788
54788
  return String(err);
54789
54789
  }
@@ -58027,7 +58027,7 @@ function parseSkillText(options) {
58027
58027
  throw error;
58028
58028
  }
58029
58029
  const frontmatter = parsed.data ?? {};
58030
- if (!isRecord$5(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
58030
+ if (!isRecord$4(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
58031
58031
  const metadata = normalizeMetadata(frontmatter);
58032
58032
  if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
58033
58033
  const name = nonEmptyString$3(metadata.name);
@@ -58140,7 +58140,7 @@ function tokenizeArgs(raw) {
58140
58140
  function nonEmptyString$3(value) {
58141
58141
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
58142
58142
  }
58143
- function isRecord$5(value) {
58143
+ function isRecord$4(value) {
58144
58144
  return typeof value === "object" && value !== null && !Array.isArray(value);
58145
58145
  }
58146
58146
  //#endregion
@@ -58148,14 +58148,36 @@ function isRecord$5(value) {
58148
58148
  var mcp_config_default = "---\nname: mcp-config\ndescription: Configure MCP servers and handle MCP OAuth login.\n---\n\n# Interactive MCP server configuration\n\nThe user invoked this skill through `/mcp-config` or `/skill:mcp-config`.\nEither they want to log into an MCP server that asked for OAuth, or they\nwant to edit the `mcp.json` that lists MCP servers. The work is small and\nlocal — handle it on this turn yourself, no agents or planning todos.\n\nPick the flow from the user's message and your tool list:\n\n- An `mcp__<server>__authenticate` tool is in your list, the user says\n \"log in\" / \"auth\" / \"sign in\", they invoke `/mcp-config login\n <server>`, or they quote a `needs-auth` status → **Login**.\n- Add / edit / remove / list of an `mcp.json` entry → **Config edit**.\n- Bare `/mcp-config` with no `authenticate` tool in your list →\n **Config edit**. If there were a pending login, the authenticate tool\n would be in your list.\n\n## Login\n\nEach MCP server in `needs-auth` exposes one `mcp__<server>__authenticate`\ntool. Call it for the server the user means — its own description owns\nthe OAuth UX (printing the URL, blocking on the callback, reconnecting on\nsuccess). Surface its output verbatim, including the authorization URL\nunchanged; the URL contains state and PKCE parameters that break if\nedited.\n\nIf the user named a server that has no authenticate tool, say so in one\nsentence and stop — do **not** fall into config edit. They're trying to\nlog in to a server that isn't currently waiting for login; quietly\nrewriting `mcp.json` would be the wrong fix. If multiple authenticate\ntools exist and the user didn't name one, ask which.\n\n## Config edit\n\nConfig lives in two files; on key collision the project file overrides\nthe user-global one:\n\n- User-global: `~/.byf/mcp.json` (or `$BYF_HOME/mcp.json` if\n set). Use for servers you want everywhere.\n- Project-local: `<cwd>/.byf/mcp.json`. Mention once that stdio\n entries spawn commands at session start, so this should only live in\n trusted repos.\n\nBoth files wrap their entries the same way:\n\n```json\n{ \"mcpServers\": { \"<name>\": { /* entry */ } } }\n```\n\nA minimal stdio entry needs `command` (+ optional `args`, `env`, `cwd`).\nA minimal http entry needs `url`; add `bearerTokenEnvVar: \"ENV_NAME\"` for\nservers that authenticate with a static bearer token from the\nenvironment. Servers that use OAuth take no token field — the login flow\nabove handles them. `transport` is inferred from `command` vs `url`, so\nomit it. For less common fields (`enabled`, `startupTimeoutMs`,\n`toolTimeoutMs`, `enabledTools`, `disabledTools`, `headers`) the source of\ntruth is `McpServerStdioConfigSchema` / `McpServerHttpConfigSchema` in\n`packages/agent-core/src/config/schema.ts`.\n\nIf the user only wants to **see** what's configured, read both files,\nshow a merged view, and stop — no scope prompt, no write.\n\nFor changes, the flow is:\n\n1. **Pick a scope.** Infer it from the user's words when you can\n (project / repo / this checkout / cwd → project; global / everywhere /\n all projects → user-global). When the request is genuinely scope-less,\n use one `AskUserQuestion` to ask user-global vs project-local, defaulting\n to user-global. Use plain text for every other question — `AskUserQuestion`\n is a poor fit for free-form input. If the user dismisses the scope\n question, stop; you can't safely guess where they wanted the change.\n2. **Read and announce.** Read the target file (a missing or empty file\n is fine; you'll create `{ \"mcpServers\": {} }`). If JSON parsing fails,\n surface the error verbatim and stop — silently overwriting a broken\n file could destroy work. Then show the user the target path, what's\n currently in it, and the entry you're about to write or delete. This\n is for transparency, not a confirmation gate — the Edit/Write\n permission prompt is the real gate, and your message is what gives\n the user context when that prompt appears. In yolo / afk modes there\n is no prompt, which is those modes' explicit contract.\n3. **Write and tell them how to reload MCP servers.** Preserve unrelated\n entries and the `mcpServers` wrapper. MCP servers load at session\n start, so tell the user to start a new session (for example `/new`) or\n restart BYF for the change to take effect.\n\n## Secrets\n\nDon't store secrets (tokens, keys, passwords) as literals in\n`mcp.json` — it's a plain config file on disk. http servers should use\n`bearerTokenEnvVar` to reference an env var instead; if a stdio entry\nmust inline one in `env`, warn the user before writing.\n";
58149
58149
  //#endregion
58150
58150
  //#region ../agent-core/src/skill/builtin/mcp-config.ts
58151
- const PSEUDO_PATH = "builtin://mcp-config";
58152
- const parsed = parseSkillText({
58151
+ const PSEUDO_PATH$1 = "builtin://mcp-config";
58152
+ const parsed$1 = parseSkillText({
58153
58153
  skillMdPath: "/builtin/skills/mcp-config.md",
58154
58154
  skillDirName: "mcp-config",
58155
58155
  source: "builtin",
58156
58156
  text: mcp_config_default
58157
58157
  });
58158
58158
  const MCP_CONFIG_SKILL = {
58159
+ ...parsed$1,
58160
+ path: PSEUDO_PATH$1,
58161
+ dir: PSEUDO_PATH$1,
58162
+ metadata: {
58163
+ ...parsed$1.metadata,
58164
+ type: parsed$1.metadata.type ?? "inline",
58165
+ disableModelInvocation: true
58166
+ }
58167
+ };
58168
+ //#endregion
58169
+ //#region ../agent-core/src/skill/builtin/update-config.md
58170
+ var update_config_default = "---\nname: update-config\ndescription: Audit and fix ~/.byf/config.toml — remove deprecated fields, migrate legacy settings, and flag semantic conflicts.\n---\n\n# Audit and fix config.toml\n\nThe user invoked this skill via `/update-config` or `/skill:update-config`.\nThe goal is to bring `~/.byf/config.toml` in line with the current BYF schema:\nremove deprecated fields, migrate legacy settings, and point out semantic\nconflicts that a deterministic linter cannot catch. The work is small and\nlocal — handle it on this turn yourself, no agents or planning todos.\n\n## Read the config\n\n1. **Resolve the path.** Default: `~/.byf/config.toml` (or\n `$BYF_HOME/config.toml` if that env var is set). If the user passed a path\n as an argument, use that instead.\n2. **Read the file.** If it does not exist, say so and stop — there is nothing\n to update. If the TOML fails to parse, surface the parse error verbatim and\n **stop** — do not overwrite a broken file, that could destroy the user's\n work.\n3. **Never echo secrets.** `config.toml` stores `api_key` secret values, and\n `oauth` references (a `{ storage, key }` handle that points at a\n file/keyring entry — not the token itself). When you describe what you\n found or plan to change, state only that a credential is present or\n absent — never quote the `api_key` value itself, not even partially.\n\n## What to check for\n\nField-level validity (which keys exist, what types and enums are allowed) is\n**not** something to reproduce here. The single source of truth is\n`ByfConfigSchema` in `packages/agent-core/src/config/schema.ts` — read it when\nyou need to confirm whether a field is recognized and what values it accepts.\n\nThe checks below fall into three groups. Report findings from all three before\nmaking any change.\n\n### 1. Deprecated, renamed, and migrated fields\n\nThese are historical fields that the schema no longer accepts or has replaced.\nDelete them, except where the table says to rename or migrate:\n\n| Field | Action |\n|---|---|\n| `default_yolo` / `defaultYolo` (top-level) | Remove. Use `yolo` instead. |\n| `services.byf_search` | Remove. (Legacy service, superseded by `[services.web_search]`.) |\n| `services.byf_fetch` | Remove. Use `[services.fetch_url]` instead. |\n| `loop_control.max_steps_per_run` | **Rename** to `max_steps_per_turn`, preserving the value. First check whether `max_steps_per_turn` is already present: if so, the old key is a stale duplicate — just delete it; if not, write `max_steps_per_turn` with the old value, then delete `max_steps_per_run`. (The runtime auto-copies the value on read, but this skill edits the file directly and skips that roundtrip — so you must perform the rename yourself to avoid losing the limit.) |\n| `default_thinking` (top-level boolean) | Migrate — see below. |\n\n**`default_thinking` migration** (matches the runtime precedence in\n`byf-tui.ts`: a `[thinking]` block wins over `default_thinking`):\n\n- If `[thinking]` already has `mode` or `effort` set, `default_thinking` was\n never effective — just remove it.\n- Otherwise migrate by value: `true` → write `[thinking]` with\n `mode = \"on\"` and `effort = \"high\"`; `false` → `[thinking]` with\n `mode = \"off\"`. Then remove `default_thinking`.\n\n> **Raw-passthrough blind spot.** Fields stripped by the schema (like\n> `byf_search`, `byf_fetch`) survive inside `config.raw` and get written back\n> on every read→write roundtrip, so they linger in the file even though they\n> have no effect. Deleting them here is the only way to clear them — that is\n> the core value of this skill.\n\n### 2. Semantic conflicts (the part a linter cannot enumerate)\n\nLook for cross-field problems that require understanding intent:\n\n- A provider with **both** `api_key` and `oauth` configured — these are\n mutually exclusive; ask which one the user meant.\n- `thinking.mode = \"off\"` **and** a non-empty `thinking.effort` — the effort\n is ignored; suggest removing it or flipping the mode.\n- `models.<alias>.capabilities` containing a value not in the valid set. The\n valid set is the single source of truth: read `CAPABILITY_DEFINITIONS` (or\n the derived `VALID_CAPABILITIES`) in\n `packages/agent-core/src/providers/runtime-provider.ts`. Comparison is\n case-insensitive at runtime, so flag only genuine mismatches.\n- A model alias, `default_provider`, or `default_model` that points at a\n provider or model that does not exist — a dangling reference. Report it; do\n **not** delete it, the user may be mid-edit.\n\n### 3. Housekeeping\n\n- Top-level keys that are not in `ByfConfigSchema` (and not a deprecated key\n above) — likely typos or leftovers from an old version. Report them; only\n remove with user confirmation. Note that legitimate unknown keys (e.g.\n `theme`, `notifications`) are preserved by design through `config.raw`, so\n do not touch keys the user clearly added on purpose.\n- **Nested keys inside containers** (`[providers.<name>]`, `[models.<alias>]`,\n `[services]`, `[background]`, `[loop_control]`, `[thinking]`,\n `[permission]`). A key that the corresponding section of `ByfConfigSchema`\n does not recognize is silently dropped by the parser and has no effect — a\n common cause is a typo (e.g. `max_context_tokns` instead of\n `max_context_size`). Cross-check each sub-key against the schema and flag\n any mismatch; fix only with user confirmation. Note that `[permission]`\n still accepts the legacy `deny`/`allow`/`ask` array shorthand, so do not\n report those as unknown.\n\n## Make changes\n\nFor each change, show the user **what is currently there → what you plan to\nwrite** before editing. Then apply edits with Write/Edit. The Edit/Write\npermission prompt is the real safety gate — your summary is what gives the\nuser context when that prompt appears. There is no automatic backup; if the\nuser wants one, suggest they copy the file first.\n\nAfter editing, tell the user to start a new session (e.g. `/new`) or restart\nBYF for the config change to take effect.\n";
58171
+ //#endregion
58172
+ //#region ../agent-core/src/skill/builtin/update-config.ts
58173
+ const PSEUDO_PATH = "builtin://update-config";
58174
+ const parsed = parseSkillText({
58175
+ skillMdPath: "/builtin/skills/update-config.md",
58176
+ skillDirName: "update-config",
58177
+ source: "builtin",
58178
+ text: update_config_default
58179
+ });
58180
+ const UPDATE_CONFIG_SKILL = {
58159
58181
  ...parsed,
58160
58182
  path: PSEUDO_PATH,
58161
58183
  dir: PSEUDO_PATH,
@@ -58169,6 +58191,7 @@ const MCP_CONFIG_SKILL = {
58169
58191
  //#region ../agent-core/src/skill/builtin/index.ts
58170
58192
  function registerBuiltinSkills(registry) {
58171
58193
  registry.registerBuiltinSkill(MCP_CONFIG_SKILL);
58194
+ registry.registerBuiltinSkill(UPDATE_CONFIG_SKILL);
58172
58195
  }
58173
58196
  //#endregion
58174
58197
  //#region ../agent-core/src/skill/scanner.ts
@@ -63468,7 +63491,7 @@ async function downloadAndInstallRg(shareDir) {
63468
63491
  clearTimeout(timeoutHandle);
63469
63492
  }
63470
63493
  if (!resp.ok || resp.body === null) throw new Error(`Failed to download ripgrep: HTTP ${String(resp.status)} ${resp.statusText}`);
63471
- const write = createWriteStream(archivePath);
63494
+ const write = createWriteStream$1(archivePath);
63472
63495
  await pipeline$1(Readable.fromWeb(resp.body), write);
63473
63496
  await verifyArchiveChecksum(archivePath, archiveName, expectedSha256);
63474
63497
  if (isWindows) await extractRgFromZip(archivePath, destination);
@@ -63529,7 +63552,7 @@ async function extractRgFromZip(archivePath, destination) {
63529
63552
  zipfile.close();
63530
63553
  return;
63531
63554
  }
63532
- const out = createWriteStream(destination);
63555
+ const out = createWriteStream$1(destination);
63533
63556
  (async () => {
63534
63557
  try {
63535
63558
  await pipeline$1(stream, out);
@@ -67347,7 +67370,7 @@ const OptionalStringSchema = z.preprocess((value) => {
67347
67370
  if (typeof value === "string") return value;
67348
67371
  if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
67349
67372
  }, z.string().optional());
67350
- const HookSpecificOutputSchema = z.preprocess((value) => isRecord$4(value) ? value : void 0, z.looseObject({
67373
+ const HookSpecificOutputSchema = z.preprocess((value) => isRecord$3(value) ? value : void 0, z.looseObject({
67351
67374
  message: OptionalStringSchema,
67352
67375
  permissionDecision: z.unknown().optional(),
67353
67376
  permissionDecisionReason: OptionalStringSchema
@@ -67366,7 +67389,7 @@ async function runHook(command, input, options) {
67366
67389
  detached: process.platform !== "win32"
67367
67390
  });
67368
67391
  } catch (error) {
67369
- return allowResult({ stderr: errorMessage$2(error) });
67392
+ return allowResult({ stderr: errorMessage$1(error) });
67370
67393
  }
67371
67394
  return new Promise((resolve) => {
67372
67395
  let stdout = "";
@@ -67414,7 +67437,7 @@ async function runHook(command, input, options) {
67414
67437
  child.on("error", (error) => {
67415
67438
  settle(allowResult({
67416
67439
  stdout,
67417
- stderr: stderr + errorMessage$2(error)
67440
+ stderr: stderr + errorMessage$1(error)
67418
67441
  }));
67419
67442
  });
67420
67443
  child.on("close", (code) => {
@@ -67507,10 +67530,10 @@ function tryKillProcess(child, signal) {
67507
67530
  } catch {}
67508
67531
  }
67509
67532
  }
67510
- function isRecord$4(value) {
67533
+ function isRecord$3(value) {
67511
67534
  return typeof value === "object" && value !== null && !Array.isArray(value);
67512
67535
  }
67513
- function errorMessage$2(error) {
67536
+ function errorMessage$1(error) {
67514
67537
  return error instanceof Error ? error.message : String(error);
67515
67538
  }
67516
67539
  //#endregion
@@ -70175,6 +70198,7 @@ const YoloOutsideWorkspacePermissionPolicy = {
70175
70198
  evaluate({ agent, mode, toolCallContext }) {
70176
70199
  if (mode !== "yolo") return void 0;
70177
70200
  const toolName = toolCallContext.toolCall.name;
70201
+ if (isDefaultAutoAllowTool(toolName)) return void 0;
70178
70202
  const toolAccess = FILE_ACCESS_TOOLS[toolName];
70179
70203
  if (toolAccess === void 0) return void 0;
70180
70204
  const [operation, displayOperation] = toolAccess;
@@ -70442,8 +70466,8 @@ function approvalTelemetryMode(mode) {
70442
70466
  //#endregion
70443
70467
  //#region ../agent-core/src/agent/records/migration/v1.1.ts
70444
70468
  function isLegacyToolCall(v) {
70445
- if (!isRecord$3(v)) return false;
70446
- return v["type"] === "function" && typeof v["id"] === "string" && isRecord$3(v["function"]);
70469
+ if (!isRecord$2(v)) return false;
70470
+ return v["type"] === "function" && typeof v["id"] === "string" && isRecord$2(v["function"]);
70447
70471
  }
70448
70472
  function migrateToolCall(v) {
70449
70473
  const { function: fn, ...rest } = v;
@@ -70453,7 +70477,7 @@ function migrateToolCall(v) {
70453
70477
  arguments: fn.arguments
70454
70478
  };
70455
70479
  }
70456
- function isRecord$3(value) {
70480
+ function isRecord$2(value) {
70457
70481
  return typeof value === "object" && value !== null && !Array.isArray(value);
70458
70482
  }
70459
70483
  const MIGRATIONS = [{
@@ -78265,7 +78289,7 @@ function parseToolCallArguments(raw) {
78265
78289
  } catch (error) {
78266
78290
  return {
78267
78291
  success: false,
78268
- error: errorMessage$3(error)
78292
+ error: errorMessage$2(error)
78269
78293
  };
78270
78294
  }
78271
78295
  }
@@ -78317,7 +78341,7 @@ async function prepareToolCall(step, call) {
78317
78341
  toolCallId: call.toolCall.id,
78318
78342
  error
78319
78343
  });
78320
- const output = error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$3(error)}`;
78344
+ const output = error instanceof PathSecurityError ? error.message : `Tool "${call.toolName}" failed to resolve execution: ${errorMessage$2(error)}`;
78321
78345
  await dispatchToolCall(step, call, effectiveArgs);
78322
78346
  return { task: makeResolvedToolCallTask(makeErrorToolResult(call, effectiveArgs, output)) };
78323
78347
  }
@@ -78377,7 +78401,7 @@ async function runPrepareToolExecutionHook(step, call) {
78377
78401
  return {
78378
78402
  kind: "hookFailed",
78379
78403
  args,
78380
- output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$3(error)}`
78404
+ output: `prepareToolExecution hook failed for "${call.toolName}": ${errorMessage$2(error)}`
78381
78405
  };
78382
78406
  }
78383
78407
  const effectiveArgs = hookResult?.updatedArgs ?? args;
@@ -78421,7 +78445,7 @@ async function runRunnableToolCall(step, call, effectiveArgs, metadata, executio
78421
78445
  toolCallId: toolCall.id,
78422
78446
  error
78423
78447
  });
78424
- return makeErrorToolResult(call, effectiveArgs, aborted ? `Tool "${toolName}" was aborted` : `Tool "${toolName}" failed: ${errorMessage$3(error)}`);
78448
+ return makeErrorToolResult(call, effectiveArgs, aborted ? `Tool "${toolName}" was aborted` : `Tool "${toolName}" failed: ${errorMessage$2(error)}`);
78425
78449
  }
78426
78450
  return makeToolResult(call, effectiveArgs, toolResult);
78427
78451
  }
@@ -78453,7 +78477,7 @@ async function finalizePendingToolResult(step, pendingResult) {
78453
78477
  toolCallId: pendingResult.toolCall.id,
78454
78478
  error
78455
78479
  });
78456
- const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$3(error)}`;
78480
+ const output = aborted ? `Tool "${pendingResult.toolName}" aborted during finalizeToolResult hook.` : `finalizeToolResult hook failed for "${pendingResult.toolName}": ${errorMessage$2(error)}`;
78457
78481
  return {
78458
78482
  ...pendingResult,
78459
78483
  stopTurn: pendingResult.stopTurn,
@@ -78815,7 +78839,7 @@ async function runTurn(input) {
78815
78839
  usage
78816
78840
  };
78817
78841
  }
78818
- dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$3(error)));
78842
+ dispatchEvent(makeInterruptedEvent(isMaxStepsExceededError(error) ? "max_steps" : "error", steps, activeStep, errorMessage$2(error)));
78819
78843
  throw error;
78820
78844
  }
78821
78845
  return {
@@ -85325,595 +85349,6 @@ function isFileExistsError(error) {
85325
85349
  return typeof error === "object" && error !== null && error.code === "EEXIST";
85326
85350
  }
85327
85351
  //#endregion
85328
- //#region ../agent-core/src/config/update-rules.ts
85329
- const REMOVED_RULES = [
85330
- {
85331
- path: "default_yolo",
85332
- pathParts: ["default_yolo"],
85333
- kind: "removed",
85334
- detail: "Top-level field default_yolo is removed. Use yolo instead.",
85335
- deprecatedSince: "pre-0.1.0"
85336
- },
85337
- {
85338
- path: "defaultYolo",
85339
- pathParts: ["defaultYolo"],
85340
- kind: "removed",
85341
- detail: "Top-level field defaultYolo is removed. Use yolo instead.",
85342
- deprecatedSince: "pre-0.1.0"
85343
- },
85344
- {
85345
- path: "services.byf_search",
85346
- pathParts: ["services", "byf_search"],
85347
- kind: "removed",
85348
- detail: "Deprecated service byf_search is removed.",
85349
- deprecatedSince: "pre-0.1.0"
85350
- },
85351
- {
85352
- path: "services.byf_fetch",
85353
- pathParts: ["services", "byf_fetch"],
85354
- kind: "removed",
85355
- detail: "Deprecated service byf_fetch is removed. Use services.fetch_url instead.",
85356
- deprecatedSince: "pre-0.1.0"
85357
- }
85358
- ];
85359
- const RENAMED_RULES = [{
85360
- path: "loop_control.max_steps_per_run",
85361
- pathParts: ["loop_control", "max_steps_per_run"],
85362
- kind: "renamed",
85363
- detail: "Renamed to max_steps_per_turn.",
85364
- deprecatedSince: "pre-0.1.0"
85365
- }];
85366
- const MIGRATED_RULES = [{
85367
- path: "default_thinking",
85368
- pathParts: ["default_thinking"],
85369
- kind: "migrated",
85370
- detail: "Migrate default_thinking to [thinking] block.",
85371
- deprecatedSince: "pre-0.1.0"
85372
- }];
85373
- /**
85374
- * Combined list of all deprecated-field rules.
85375
- *
85376
- * Order within the list does not affect correctness (the CLI groups by kind
85377
- * at display time), but a stable order helps test expectations.
85378
- */
85379
- const DEPRECATED_FIELD_RULES = [
85380
- ...REMOVED_RULES,
85381
- ...RENAMED_RULES,
85382
- ...MIGRATED_RULES
85383
- ];
85384
- //#endregion
85385
- //#region ../agent-core/src/providers/runtime-provider.ts
85386
- function resolveRuntimeProvider(input) {
85387
- const modelName = input.model ?? input.config.defaultModel;
85388
- 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.");
85389
- const alias = input.config.models?.[modelName];
85390
- 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.`);
85391
- const resolvedModel = alias.model;
85392
- const providerName = alias.provider ?? input.config.defaultProvider;
85393
- const providerConfig = providerName === void 0 ? void 0 : input.config.providers[providerName];
85394
- if (providerName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a provider in config.toml.`);
85395
- if (providerConfig === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${modelName}" is not configured.`);
85396
- 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.`);
85397
- 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.`);
85398
- const provider = toKosongProviderConfig(providerConfig, resolvedModel, input.byfRequestHeaders, alias.maxOutputSize, alias.reasoningKey, input.promptCacheKey);
85399
- return {
85400
- modelName,
85401
- providerName,
85402
- modelCapabilities: resolveModelCapabilities(alias, provider),
85403
- provider
85404
- };
85405
- }
85406
- async function resolveRuntimeProviderWithOAuth(input) {
85407
- const resolved = resolveRuntimeProvider(input);
85408
- const resolveAuth = createRuntimeProviderAuthResolver(input, resolved);
85409
- if (resolveAuth === void 0) return resolved;
85410
- await resolveAuth();
85411
- return {
85412
- ...resolved,
85413
- resolveAuth
85414
- };
85415
- }
85416
- function createRuntimeProviderAuthResolver(input, resolved = resolveRuntimeProvider(input)) {
85417
- const providerName = resolved.providerName;
85418
- if (providerName === void 0) return void 0;
85419
- const providerConfig = input.config.providers[providerName];
85420
- if (providerConfig?.oauth === void 0) return void 0;
85421
- 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.`);
85422
- const tokenProvider = input.resolveOAuthTokenProvider?.(providerName, providerConfig.oauth);
85423
- if (tokenProvider === void 0) return async () => {
85424
- throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
85425
- };
85426
- return async (options) => {
85427
- let apiKey;
85428
- try {
85429
- apiKey = await tokenProvider.getAccessToken(options?.forceRefresh === true ? { force: true } : void 0);
85430
- } catch (error) {
85431
- if (!isAuthLoginRequired(error)) (input.log ?? log).warn("oauth token fetch failed", {
85432
- providerName,
85433
- error
85434
- });
85435
- throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`, { cause: error });
85436
- }
85437
- if (apiKey.trim().length === 0) throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
85438
- return { apiKey };
85439
- };
85440
- }
85441
- function isAuthLoginRequired(error) {
85442
- return isByfError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED;
85443
- }
85444
- const CAPABILITY_DEFINITIONS = [
85445
- {
85446
- name: "image_in",
85447
- returnKey: "image_in"
85448
- },
85449
- {
85450
- name: "video_in",
85451
- returnKey: "video_in"
85452
- },
85453
- {
85454
- name: "audio_in",
85455
- returnKey: "audio_in"
85456
- },
85457
- {
85458
- name: "thinking",
85459
- returnKey: "thinking"
85460
- },
85461
- {
85462
- name: "always_thinking",
85463
- returnKey: "thinking"
85464
- },
85465
- {
85466
- name: "tool_use",
85467
- returnKey: "tool_use"
85468
- },
85469
- {
85470
- name: "thinking_effort",
85471
- returnKey: "thinking_effort"
85472
- },
85473
- {
85474
- name: "thinking_xhigh",
85475
- returnKey: "thinking_xhigh"
85476
- },
85477
- {
85478
- name: "thinking_max",
85479
- returnKey: "thinking_max"
85480
- }
85481
- ];
85482
- /**
85483
- * The list of every valid capability name that can appear in a model
85484
- * alias's `capabilities` array.
85485
- *
85486
- * Derives directly from {@link CAPABILITY_DEFINITIONS} so that adding a
85487
- * new capability in one place automatically keeps both the validation
85488
- * gate (`update-config`) and the runtime resolver in sync.
85489
- */
85490
- const VALID_CAPABILITIES = CAPABILITY_DEFINITIONS.map((d) => d.name);
85491
- function resolveModelCapabilities(alias, provider) {
85492
- const capabilities = new Set((alias.capabilities ?? []).map((capability) => capability.trim().toLowerCase()));
85493
- const has = (capability) => capabilities.has(capability);
85494
- const providerCapability = createProvider$1(providerForCapabilityProbe(provider)).getCapability?.(provider.model) ?? UNKNOWN_CAPABILITY;
85495
- const returnKeyToNames = /* @__PURE__ */ new Map();
85496
- for (const def of CAPABILITY_DEFINITIONS) {
85497
- const names = returnKeyToNames.get(def.returnKey) ?? [];
85498
- names.push(def.name);
85499
- returnKeyToNames.set(def.returnKey, names);
85500
- }
85501
- const resolved = {};
85502
- for (const [returnKey, names] of returnKeyToNames) resolved[returnKey] = names.some((n) => has(n)) || Boolean(providerCapability[returnKey]);
85503
- return {
85504
- ...resolved,
85505
- max_context_tokens: alias.maxContextSize
85506
- };
85507
- }
85508
- function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey) {
85509
- switch (provider.type) {
85510
- case "anthropic": return {
85511
- type: "anthropic",
85512
- model,
85513
- baseUrl: providerValue(provider.baseUrl, provider.env, "ANTHROPIC_BASE_URL"),
85514
- apiKey: providerApiKey(provider),
85515
- ...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
85516
- ...defaultHeadersField(provider.customHeaders)
85517
- };
85518
- case "openai-completions": {
85519
- const defaultHeaders = {
85520
- ...byfRequestHeaders,
85521
- ...provider.customHeaders
85522
- };
85523
- const generationKwargs = {
85524
- prompt_cache_key: promptCacheKey,
85525
- extra_body: provider.extraBody
85526
- };
85527
- if (Object.keys(defaultHeaders).length === 0) return {
85528
- type: "openai-completions",
85529
- model,
85530
- baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
85531
- reasoningKey,
85532
- thinkingEffortKey: provider.thinkingEffortKey,
85533
- generationKwargs,
85534
- apiKey: providerApiKey(provider)
85535
- };
85536
- return {
85537
- type: "openai-completions",
85538
- model,
85539
- baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
85540
- reasoningKey,
85541
- thinkingEffortKey: provider.thinkingEffortKey,
85542
- generationKwargs,
85543
- defaultHeaders,
85544
- apiKey: providerApiKey(provider)
85545
- };
85546
- }
85547
- case "google-genai": return {
85548
- type: "google-genai",
85549
- model,
85550
- apiKey: providerApiKey(provider)
85551
- };
85552
- case "openai_responses": return {
85553
- type: "openai_responses",
85554
- model,
85555
- baseUrl: providerValue(provider.baseUrl, provider.env, "OPENAI_BASE_URL"),
85556
- apiKey: providerApiKey(provider),
85557
- ...defaultHeadersField(provider.customHeaders)
85558
- };
85559
- case "vertexai": return {
85560
- type: "vertexai",
85561
- model,
85562
- vertexai: hasVertexAIServiceEnv(provider),
85563
- apiKey: hasVertexAIServiceEnv(provider) ? void 0 : providerApiKey(provider),
85564
- project: vertexAIProject(provider),
85565
- location: vertexAILocation(provider)
85566
- };
85567
- default: {
85568
- const exhaustive = provider.type;
85569
- throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
85570
- }
85571
- }
85572
- }
85573
- function defaultHeadersField(headers) {
85574
- if (headers === void 0 || Object.keys(headers).length === 0) return {};
85575
- return { defaultHeaders: { ...headers } };
85576
- }
85577
- function providerForCapabilityProbe(provider) {
85578
- if (provider.type === "vertexai") return {
85579
- ...provider,
85580
- vertexai: false,
85581
- project: void 0,
85582
- location: void 0,
85583
- apiKey: provider.apiKey === void 0 || provider.apiKey.length === 0 ? "capability-probe" : provider.apiKey
85584
- };
85585
- if (provider.apiKey !== void 0 && provider.apiKey.length > 0) return provider;
85586
- return {
85587
- ...provider,
85588
- apiKey: "capability-probe"
85589
- };
85590
- }
85591
- function providerApiKey(provider) {
85592
- switch (provider.type) {
85593
- case "anthropic": return providerValue(provider.apiKey, provider.env, "ANTHROPIC_API_KEY");
85594
- case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
85595
- case "openai-completions": return providerValue(provider.apiKey, provider.env, "BYF_API_KEY");
85596
- case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
85597
- case "vertexai": return nonEmptyString$2(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
85598
- default: {
85599
- const exhaustive = provider.type;
85600
- throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
85601
- }
85602
- }
85603
- }
85604
- function hasVertexAIServiceEnv(provider) {
85605
- return vertexAIProject(provider) !== void 0 && vertexAILocation(provider) !== void 0;
85606
- }
85607
- function vertexAIProject(provider) {
85608
- return envValue(provider.env, "GOOGLE_CLOUD_PROJECT");
85609
- }
85610
- function vertexAILocation(provider) {
85611
- return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
85612
- }
85613
- function providerValue(configured, env, envKey) {
85614
- return nonEmptyString$2(configured) ?? envValue(env, envKey);
85615
- }
85616
- function envValue(env, key) {
85617
- return nonEmptyString$2(env?.[key]);
85618
- }
85619
- function nonEmptyString$2(value) {
85620
- const trimmed = value?.trim();
85621
- return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
85622
- }
85623
- function locationFromVertexAIBaseUrl(baseUrl) {
85624
- const url = nonEmptyString$2(baseUrl);
85625
- if (url === void 0) return void 0;
85626
- try {
85627
- const host = new URL(url).hostname;
85628
- return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$2(host.slice(0, -26)) : void 0;
85629
- } catch {
85630
- return;
85631
- }
85632
- }
85633
- //#endregion
85634
- //#region ../agent-core/src/config/update.ts
85635
- /**
85636
- * Scan a parsed config (including `config.raw`) and return all Findings
85637
- * for deprecated / renamed / migrated / dangling / unknown / invalid-value
85638
- * fields.
85639
- *
85640
- * This is a **pure** function — no file I/O, no side effects.
85641
- *
85642
- * Detection is based on `config.raw` (the clone of the original TOML data),
85643
- * not on the parsed camelCase schema. This matches the PRD's observation
85644
- * that `raw` is both the protection layer (preserving unknown fields) and
85645
- * the blind spot (retaining stale keys through read→write cycles).
85646
- */
85647
- function analyzeConfig(config) {
85648
- const raw = config.raw;
85649
- const findings = [];
85650
- if (isRecord$2(raw)) {
85651
- for (const rule of DEPRECATED_FIELD_RULES) if (pathExistsInRaw(raw, rule.pathParts)) findings.push({
85652
- kind: rule.kind,
85653
- path: rule.path,
85654
- detail: rule.detail,
85655
- deprecatedSince: rule.deprecatedSince
85656
- });
85657
- const defaultThinkingFinding = findings.find((f) => f.path === "default_thinking");
85658
- if (defaultThinkingFinding) {
85659
- const thinking = config.thinking;
85660
- if (thinking && (thinking.mode !== void 0 || thinking.effort !== void 0)) {
85661
- defaultThinkingFinding.kind = "removed";
85662
- defaultThinkingFinding.detail = "Already superseded by [thinking] block.";
85663
- }
85664
- }
85665
- const UNKNOWN_SKIP_PATHS = new Set(DEPRECATED_FIELD_RULES.map((r) => r.pathParts.join(".")));
85666
- const byfShapeKeys = /* @__PURE__ */ new Set();
85667
- for (const key of Object.keys(ByfConfigSchema.shape)) {
85668
- byfShapeKeys.add(key);
85669
- byfShapeKeys.add(camelToSnakeStatic(key));
85670
- }
85671
- for (const rawKey of Object.keys(raw)) {
85672
- if (rawKey === "raw") continue;
85673
- if (UNKNOWN_SKIP_PATHS.has(rawKey)) continue;
85674
- const camelKey = snakeToCamelStatic(rawKey);
85675
- if (!byfShapeKeys.has(camelKey) && !byfShapeKeys.has(rawKey)) findings.push({
85676
- kind: "unknown",
85677
- path: rawKey,
85678
- 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.`
85679
- });
85680
- }
85681
- const nestedFindings = scanNestedUnknowns(raw, UNKNOWN_SKIP_PATHS);
85682
- findings.push(...nestedFindings);
85683
- }
85684
- if (config.models) {
85685
- const validCapsLower = new Set(VALID_CAPABILITIES.map((c) => c.toLowerCase()));
85686
- for (const [alias, modelConfig] of Object.entries(config.models)) if (modelConfig.capabilities) for (let i = 0; i < modelConfig.capabilities.length; i++) {
85687
- const cap = modelConfig.capabilities[i];
85688
- if (cap === void 0) continue;
85689
- if (!validCapsLower.has(cap.toLowerCase())) findings.push({
85690
- kind: "invalid-value",
85691
- path: `models.${alias}.capabilities[${i}]`,
85692
- detail: `"${cap}" is not a valid capability. Valid values: ${VALID_CAPABILITIES.join(", ")}.`
85693
- });
85694
- }
85695
- }
85696
- const providerKeys = Object.keys(config.providers ?? {});
85697
- if (config.models) {
85698
- for (const [alias, modelConfig] of Object.entries(config.models)) if (!providerKeys.includes(modelConfig.provider)) findings.push({
85699
- kind: "dangling",
85700
- path: `models.${alias}.provider`,
85701
- detail: `Model alias "${alias}" references provider "${modelConfig.provider}", which does not exist in [providers].`
85702
- });
85703
- }
85704
- if (config.defaultProvider !== void 0 && !providerKeys.includes(config.defaultProvider)) findings.push({
85705
- kind: "dangling",
85706
- path: "default_provider",
85707
- detail: `Default provider "${config.defaultProvider}" does not exist in [providers].`
85708
- });
85709
- const modelKeys = Object.keys(config.models ?? {});
85710
- if (config.defaultModel !== void 0 && !modelKeys.includes(config.defaultModel)) findings.push({
85711
- kind: "dangling",
85712
- path: "default_model",
85713
- detail: `Default model "${config.defaultModel}" does not exist in [models].`
85714
- });
85715
- return findings;
85716
- }
85717
- /**
85718
- * Apply automatic fixes to a `ByfConfig` by deleting every path registered in
85719
- * `DEPRECATED_FIELD_RULES` from `config.raw`.
85720
- *
85721
- * The `_findings` parameter is intentionally **not consulted** — deletion is
85722
- * driven exclusively by the whitelist in `DEPRECATED_FIELD_RULES`. This ensures
85723
- * that a call to `applyFixes` always produces a clean config regardless of
85724
- * what analysis step previously ran.
85725
- *
85726
- * This is a **pure** function — it returns a new config object without
85727
- * mutating the input.
85728
- */
85729
- function applyFixes(config, findings) {
85730
- const newRaw = rawShallowClone(config.raw);
85731
- for (const rule of DEPRECATED_FIELD_RULES) deletePath(newRaw, rule.pathParts);
85732
- let newConfig = {
85733
- ...config,
85734
- raw: newRaw
85735
- };
85736
- if (findings.find((f) => f.kind === "migrated" && f.path === "default_thinking")) {
85737
- const rawValue = config.raw?.["default_thinking"];
85738
- const isTruthy = rawValue === true || rawValue === "true" || rawValue === 1;
85739
- newConfig = {
85740
- ...newConfig,
85741
- thinking: isTruthy ? {
85742
- mode: "on",
85743
- effort: "high"
85744
- } : { mode: "off" }
85745
- };
85746
- }
85747
- return newConfig;
85748
- }
85749
- /** Convert snake_case to camelCase (static helper for unknown detection). */
85750
- function snakeToCamelStatic(str) {
85751
- return str.replaceAll(/_([a-z])/g, (_, ch) => ch.toUpperCase());
85752
- }
85753
- /** Convert camelCase to snake_case (static helper for unknown detection). */
85754
- function camelToSnakeStatic(str) {
85755
- return str.replaceAll(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
85756
- }
85757
- /** True when `value` is a non-null, non-array object. */
85758
- function isRecord$2(value) {
85759
- return typeof value === "object" && value !== null && !Array.isArray(value);
85760
- }
85761
- /**
85762
- * Walk `root` along `pathParts` checking that **every** segment exists.
85763
- *
85764
- * Returns `true` iff all parts exist as own keys (or inherited keys — TOML
85765
- * parse results are plain objects so the distinction doesn't matter here).
85766
- */
85767
- function pathExistsInRaw(root, pathParts) {
85768
- let current = root;
85769
- for (const part of pathParts) {
85770
- if (!isRecord$2(current) || !(part in current)) return false;
85771
- current = current[part];
85772
- }
85773
- return true;
85774
- }
85775
- /**
85776
- * Delete a leaf (or entire sub-tree) from `root` following `pathParts`.
85777
- *
85778
- * If the parent after deletion becomes empty it is cleaned up as well
85779
- * (recursive upward), so that a service table cleared of all deprecated
85780
- * keys does not leave behind an empty `{}`.
85781
- */
85782
- function deletePath(root, pathParts) {
85783
- if (pathParts.length === 0) return;
85784
- const parentParts = pathParts.slice(0, -1);
85785
- const leafKey = pathParts.at(-1);
85786
- let current;
85787
- if (parentParts.length === 0) current = root;
85788
- else current = traverseTo(root, parentParts);
85789
- if (current === void 0) return;
85790
- const keyExisted = leafKey in current;
85791
- delete current[leafKey];
85792
- if (keyExisted && parentParts.length > 0 && Object.keys(current).length === 0) deletePath(root, parentParts);
85793
- }
85794
- /**
85795
- * Walk `root` along `pathParts` returning the penultimate record, or
85796
- * `undefined` if any segment is missing.
85797
- */
85798
- function traverseTo(root, pathParts) {
85799
- let current = root;
85800
- for (const part of pathParts) {
85801
- if (!isRecord$2(current) || !(part in current)) return void 0;
85802
- current = current[part];
85803
- }
85804
- return isRecord$2(current) ? current : void 0;
85805
- }
85806
- /**
85807
- * Shallow-clone `raw`: each nested record is also shallow-cloned so that
85808
- * mutations in `applyFixes` do not affect the original object.
85809
- */
85810
- function rawShallowClone(raw) {
85811
- if (!isRecord$2(raw)) return {};
85812
- const clone = {};
85813
- for (const [key, value] of Object.entries(raw)) clone[key] = isRecord$2(value) ? { ...value } : value;
85814
- return clone;
85815
- }
85816
- /**
85817
- * Build a set of all valid keys (camelCase + snake_case) from a zod
85818
- * object schema's `.shape`.
85819
- */
85820
- function getShapeKeySet(schema) {
85821
- const keys = /* @__PURE__ */ new Set();
85822
- for (const key of Object.keys(schema.shape)) {
85823
- keys.add(key);
85824
- keys.add(camelToSnakeStatic(key));
85825
- }
85826
- return keys;
85827
- }
85828
- /**
85829
- * Scan known container keys in `raw` for sub-keys that don't match the
85830
- * corresponding schema shape. Unknown paths that overlap with
85831
- * `skipPaths` (e.g. already reported deprecated fields) are skipped.
85832
- *
85833
- * Detects e.g. `models.gpt4.max_context_tokns` (typo) or
85834
- * `providers.anthropic.api_kei` (typo).
85835
- */
85836
- function scanNestedUnknowns(raw, skipPaths) {
85837
- const findings = [];
85838
- const containers = [
85839
- {
85840
- rawKey: "models",
85841
- isRecord: true,
85842
- schema: ModelAliasSchema
85843
- },
85844
- {
85845
- rawKey: "providers",
85846
- isRecord: true,
85847
- schema: ProviderConfigSchema
85848
- },
85849
- {
85850
- rawKey: "services",
85851
- isRecord: false,
85852
- schema: ServicesConfigSchema
85853
- },
85854
- {
85855
- rawKey: "background",
85856
- isRecord: false,
85857
- schema: BackgroundConfigSchema
85858
- },
85859
- {
85860
- rawKey: "loop_control",
85861
- isRecord: false,
85862
- schema: LoopControlSchema
85863
- },
85864
- {
85865
- rawKey: "thinking",
85866
- isRecord: false,
85867
- schema: ThinkingConfigSchema
85868
- },
85869
- {
85870
- rawKey: "permission",
85871
- isRecord: false,
85872
- schema: PermissionConfigSchema,
85873
- legacyKeys: [
85874
- "deny",
85875
- "allow",
85876
- "ask"
85877
- ]
85878
- }
85879
- ];
85880
- const schemaKeySets = /* @__PURE__ */ new Map();
85881
- containers.forEach((entry, index) => {
85882
- const base = getShapeKeySet(entry.schema);
85883
- if (entry.legacyKeys) for (const k of entry.legacyKeys) base.add(k);
85884
- schemaKeySets.set(index, base);
85885
- });
85886
- for (const [entryIndex, entry] of containers.entries()) {
85887
- if (!(entry.rawKey in raw)) continue;
85888
- const rawValue = raw[entry.rawKey];
85889
- if (!isRecord$2(rawValue)) continue;
85890
- if (entry.isRecord) for (const [itemKey, itemValue] of Object.entries(rawValue)) {
85891
- if (!isRecord$2(itemValue)) continue;
85892
- const validKeys = schemaKeySets.get(entryIndex);
85893
- for (const subKey of Object.keys(itemValue)) if (!validKeys.has(subKey)) {
85894
- const path = `${entry.rawKey}.${itemKey}.${subKey}`;
85895
- if (!skipPaths.has(path)) findings.push({
85896
- kind: "unknown",
85897
- path,
85898
- detail: `Field "${subKey}" is not recognized in ${entry.rawKey}.${itemKey}. This may be a typo or a field from a previous version.`
85899
- });
85900
- }
85901
- }
85902
- else {
85903
- const validKeys = schemaKeySets.get(entryIndex);
85904
- for (const subKey of Object.keys(rawValue)) if (!validKeys.has(subKey)) {
85905
- const path = `${entry.rawKey}.${subKey}`;
85906
- if (!skipPaths.has(path)) findings.push({
85907
- kind: "unknown",
85908
- path,
85909
- detail: `Field "${subKey}" is not recognized in [${entry.rawKey}]. This may be a typo or a field from a previous version.`
85910
- });
85911
- }
85912
- }
85913
- }
85914
- return findings;
85915
- }
85916
- //#endregion
85917
85352
  //#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
85918
85353
  function isZ4Schema(s) {
85919
85354
  return !!s._zod;
@@ -90650,14 +90085,14 @@ var LocalKaos = class {
90650
90085
  }
90651
90086
  }
90652
90087
  async writeBytes(path, data) {
90653
- await writeFile(this._resolvePath(path), data);
90088
+ await writeFile$1(this._resolvePath(path), data);
90654
90089
  return data.length;
90655
90090
  }
90656
90091
  async writeText(path, data, options) {
90657
90092
  const resolved = this._resolvePath(path);
90658
90093
  const encoding = options?.encoding ?? "utf-8";
90659
90094
  if ((options?.mode ?? "w") === "a") await appendFile(resolved, data, encoding);
90660
- else await writeFile(resolved, data, encoding);
90095
+ else await writeFile$1(resolved, data, encoding);
90661
90096
  return data.length;
90662
90097
  }
90663
90098
  async mkdir(path, options) {
@@ -126724,7 +126159,7 @@ async function writeExportZip(args) {
126724
126159
  entries.push(extra.target);
126725
126160
  } catch {}
126726
126161
  zip.end();
126727
- await pipeline$1(zip.outputStream, createWriteStream(args.outputPath));
126162
+ await pipeline$1(zip.outputStream, createWriteStream$1(args.outputPath));
126728
126163
  return entries;
126729
126164
  }
126730
126165
  //#endregion
@@ -126783,6 +126218,247 @@ async function readOptionalFile(path) {
126783
126218
  }
126784
126219
  }
126785
126220
  //#endregion
126221
+ //#region ../agent-core/src/providers/runtime-provider.ts
126222
+ function resolveRuntimeProvider(input) {
126223
+ const modelName = input.model ?? input.config.defaultModel;
126224
+ 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.");
126225
+ const alias = input.config.models?.[modelName];
126226
+ 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.`);
126227
+ const resolvedModel = alias.model;
126228
+ const providerName = alias.provider ?? input.config.defaultProvider;
126229
+ const providerConfig = providerName === void 0 ? void 0 : input.config.providers[providerName];
126230
+ if (providerName === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Model "${modelName}" must define a provider in config.toml.`);
126231
+ if (providerConfig === void 0) throw new ByfError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${modelName}" is not configured.`);
126232
+ 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.`);
126233
+ 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.`);
126234
+ const provider = toKosongProviderConfig(providerConfig, resolvedModel, input.byfRequestHeaders, alias.maxOutputSize, alias.reasoningKey, input.promptCacheKey);
126235
+ return {
126236
+ modelName,
126237
+ providerName,
126238
+ modelCapabilities: resolveModelCapabilities(alias, provider),
126239
+ provider
126240
+ };
126241
+ }
126242
+ async function resolveRuntimeProviderWithOAuth(input) {
126243
+ const resolved = resolveRuntimeProvider(input);
126244
+ const resolveAuth = createRuntimeProviderAuthResolver(input, resolved);
126245
+ if (resolveAuth === void 0) return resolved;
126246
+ await resolveAuth();
126247
+ return {
126248
+ ...resolved,
126249
+ resolveAuth
126250
+ };
126251
+ }
126252
+ function createRuntimeProviderAuthResolver(input, resolved = resolveRuntimeProvider(input)) {
126253
+ const providerName = resolved.providerName;
126254
+ if (providerName === void 0) return void 0;
126255
+ const providerConfig = input.config.providers[providerName];
126256
+ if (providerConfig?.oauth === void 0) return void 0;
126257
+ 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.`);
126258
+ const tokenProvider = input.resolveOAuthTokenProvider?.(providerName, providerConfig.oauth);
126259
+ if (tokenProvider === void 0) return async () => {
126260
+ throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
126261
+ };
126262
+ return async (options) => {
126263
+ let apiKey;
126264
+ try {
126265
+ apiKey = await tokenProvider.getAccessToken(options?.forceRefresh === true ? { force: true } : void 0);
126266
+ } catch (error) {
126267
+ if (!isAuthLoginRequired(error)) (input.log ?? log).warn("oauth token fetch failed", {
126268
+ providerName,
126269
+ error
126270
+ });
126271
+ throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`, { cause: error });
126272
+ }
126273
+ if (apiKey.trim().length === 0) throw new ByfError(ErrorCodes.AUTH_LOGIN_REQUIRED, `OAuth provider "${providerName}" requires login before it can be used.`);
126274
+ return { apiKey };
126275
+ };
126276
+ }
126277
+ function isAuthLoginRequired(error) {
126278
+ return isByfError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED;
126279
+ }
126280
+ const CAPABILITY_DEFINITIONS = [
126281
+ {
126282
+ name: "image_in",
126283
+ returnKey: "image_in"
126284
+ },
126285
+ {
126286
+ name: "video_in",
126287
+ returnKey: "video_in"
126288
+ },
126289
+ {
126290
+ name: "audio_in",
126291
+ returnKey: "audio_in"
126292
+ },
126293
+ {
126294
+ name: "thinking",
126295
+ returnKey: "thinking"
126296
+ },
126297
+ {
126298
+ name: "always_thinking",
126299
+ returnKey: "thinking"
126300
+ },
126301
+ {
126302
+ name: "tool_use",
126303
+ returnKey: "tool_use"
126304
+ },
126305
+ {
126306
+ name: "thinking_effort",
126307
+ returnKey: "thinking_effort"
126308
+ },
126309
+ {
126310
+ name: "thinking_xhigh",
126311
+ returnKey: "thinking_xhigh"
126312
+ },
126313
+ {
126314
+ name: "thinking_max",
126315
+ returnKey: "thinking_max"
126316
+ }
126317
+ ];
126318
+ CAPABILITY_DEFINITIONS.map((d) => d.name);
126319
+ function resolveModelCapabilities(alias, provider) {
126320
+ const capabilities = new Set((alias.capabilities ?? []).map((capability) => capability.trim().toLowerCase()));
126321
+ const has = (capability) => capabilities.has(capability);
126322
+ const providerCapability = createProvider$1(providerForCapabilityProbe(provider)).getCapability?.(provider.model) ?? UNKNOWN_CAPABILITY;
126323
+ const returnKeyToNames = /* @__PURE__ */ new Map();
126324
+ for (const def of CAPABILITY_DEFINITIONS) {
126325
+ const names = returnKeyToNames.get(def.returnKey) ?? [];
126326
+ names.push(def.name);
126327
+ returnKeyToNames.set(def.returnKey, names);
126328
+ }
126329
+ const resolved = {};
126330
+ for (const [returnKey, names] of returnKeyToNames) resolved[returnKey] = names.some((n) => has(n)) || Boolean(providerCapability[returnKey]);
126331
+ return {
126332
+ ...resolved,
126333
+ max_context_tokens: alias.maxContextSize
126334
+ };
126335
+ }
126336
+ function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSize, reasoningKey, promptCacheKey) {
126337
+ switch (provider.type) {
126338
+ case "anthropic": return {
126339
+ type: "anthropic",
126340
+ model,
126341
+ baseUrl: providerValue(provider.baseUrl, provider.env, "ANTHROPIC_BASE_URL"),
126342
+ apiKey: providerApiKey(provider),
126343
+ ...maxOutputSize !== void 0 ? { defaultMaxTokens: maxOutputSize } : {},
126344
+ ...defaultHeadersField(provider.customHeaders)
126345
+ };
126346
+ case "openai-completions": {
126347
+ const defaultHeaders = {
126348
+ ...byfRequestHeaders,
126349
+ ...provider.customHeaders
126350
+ };
126351
+ const generationKwargs = {
126352
+ prompt_cache_key: promptCacheKey,
126353
+ extra_body: provider.extraBody
126354
+ };
126355
+ if (Object.keys(defaultHeaders).length === 0) return {
126356
+ type: "openai-completions",
126357
+ model,
126358
+ baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
126359
+ reasoningKey,
126360
+ thinkingEffortKey: provider.thinkingEffortKey,
126361
+ generationKwargs,
126362
+ apiKey: providerApiKey(provider)
126363
+ };
126364
+ return {
126365
+ type: "openai-completions",
126366
+ model,
126367
+ baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
126368
+ reasoningKey,
126369
+ thinkingEffortKey: provider.thinkingEffortKey,
126370
+ generationKwargs,
126371
+ defaultHeaders,
126372
+ apiKey: providerApiKey(provider)
126373
+ };
126374
+ }
126375
+ case "google-genai": return {
126376
+ type: "google-genai",
126377
+ model,
126378
+ apiKey: providerApiKey(provider)
126379
+ };
126380
+ case "openai_responses": return {
126381
+ type: "openai_responses",
126382
+ model,
126383
+ baseUrl: providerValue(provider.baseUrl, provider.env, "OPENAI_BASE_URL"),
126384
+ apiKey: providerApiKey(provider),
126385
+ ...defaultHeadersField(provider.customHeaders)
126386
+ };
126387
+ case "vertexai": return {
126388
+ type: "vertexai",
126389
+ model,
126390
+ vertexai: hasVertexAIServiceEnv(provider),
126391
+ apiKey: hasVertexAIServiceEnv(provider) ? void 0 : providerApiKey(provider),
126392
+ project: vertexAIProject(provider),
126393
+ location: vertexAILocation(provider)
126394
+ };
126395
+ default: {
126396
+ const exhaustive = provider.type;
126397
+ throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
126398
+ }
126399
+ }
126400
+ }
126401
+ function defaultHeadersField(headers) {
126402
+ if (headers === void 0 || Object.keys(headers).length === 0) return {};
126403
+ return { defaultHeaders: { ...headers } };
126404
+ }
126405
+ function providerForCapabilityProbe(provider) {
126406
+ if (provider.type === "vertexai") return {
126407
+ ...provider,
126408
+ vertexai: false,
126409
+ project: void 0,
126410
+ location: void 0,
126411
+ apiKey: provider.apiKey === void 0 || provider.apiKey.length === 0 ? "capability-probe" : provider.apiKey
126412
+ };
126413
+ if (provider.apiKey !== void 0 && provider.apiKey.length > 0) return provider;
126414
+ return {
126415
+ ...provider,
126416
+ apiKey: "capability-probe"
126417
+ };
126418
+ }
126419
+ function providerApiKey(provider) {
126420
+ switch (provider.type) {
126421
+ case "anthropic": return providerValue(provider.apiKey, provider.env, "ANTHROPIC_API_KEY");
126422
+ case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
126423
+ case "openai-completions": return providerValue(provider.apiKey, provider.env, "BYF_API_KEY");
126424
+ case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
126425
+ case "vertexai": return nonEmptyString$2(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
126426
+ default: {
126427
+ const exhaustive = provider.type;
126428
+ throw new ByfError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
126429
+ }
126430
+ }
126431
+ }
126432
+ function hasVertexAIServiceEnv(provider) {
126433
+ return vertexAIProject(provider) !== void 0 && vertexAILocation(provider) !== void 0;
126434
+ }
126435
+ function vertexAIProject(provider) {
126436
+ return envValue(provider.env, "GOOGLE_CLOUD_PROJECT");
126437
+ }
126438
+ function vertexAILocation(provider) {
126439
+ return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
126440
+ }
126441
+ function providerValue(configured, env, envKey) {
126442
+ return nonEmptyString$2(configured) ?? envValue(env, envKey);
126443
+ }
126444
+ function envValue(env, key) {
126445
+ return nonEmptyString$2(env?.[key]);
126446
+ }
126447
+ function nonEmptyString$2(value) {
126448
+ const trimmed = value?.trim();
126449
+ return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
126450
+ }
126451
+ function locationFromVertexAIBaseUrl(baseUrl) {
126452
+ const url = nonEmptyString$2(baseUrl);
126453
+ if (url === void 0) return void 0;
126454
+ try {
126455
+ const host = new URL(url).hostname;
126456
+ return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString$2(host.slice(0, -26)) : void 0;
126457
+ } catch {
126458
+ return;
126459
+ }
126460
+ }
126461
+ //#endregion
126786
126462
  //#region ../agent-core/src/providers/provider-manager.ts
126787
126463
  var ProviderManager = class ProviderManager {
126788
126464
  options;
@@ -127043,7 +126719,7 @@ var SessionStore = class {
127043
126719
  title: normalized,
127044
126720
  isCustomTitle: true
127045
126721
  };
127046
- await writeFile(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
126722
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
127047
126723
  }
127048
126724
  async list(options) {
127049
126725
  const workDir = normalizeWorkDir(options.workDir);
@@ -127098,7 +126774,7 @@ var SessionStore = class {
127098
126774
  agents: rewriteAgentHomedirs(parsed["agents"], sourceDir, targetDir),
127099
126775
  custom: Object.assign({}, isRecord$1(parsed["custom"]) ? parsed["custom"] : {}, input.metadata)
127100
126776
  };
127101
- await writeFile(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
126777
+ await writeFile$1(statePath, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
127102
126778
  }
127103
126779
  async summaryFromDir(id, sessionDir, workDir) {
127104
126780
  const dirStat = await stat(sessionDir);
@@ -127995,7 +127671,7 @@ var SDKRpcClient = class {
127995
127671
  type: "error",
127996
127672
  sessionId: request.sessionId,
127997
127673
  agentId: request.agentId,
127998
- ...makeErrorPayload(ErrorCodes.SESSION_APPROVAL_HANDLER_ERROR, errorMessage$1(error))
127674
+ ...makeErrorPayload(ErrorCodes.SESSION_APPROVAL_HANDLER_ERROR, errorMessage(error))
127999
127675
  });
128000
127676
  return {
128001
127677
  decision: "cancelled",
@@ -128013,7 +127689,7 @@ var SDKRpcClient = class {
128013
127689
  type: "error",
128014
127690
  sessionId: request.sessionId,
128015
127691
  agentId: request.agentId,
128016
- ...makeErrorPayload(ErrorCodes.SESSION_QUESTION_HANDLER_ERROR, errorMessage$1(error))
127692
+ ...makeErrorPayload(ErrorCodes.SESSION_QUESTION_HANDLER_ERROR, errorMessage(error))
128017
127693
  });
128018
127694
  return null;
128019
127695
  }
@@ -128048,7 +127724,7 @@ var ClientAPI = class {
128048
127724
  return this.client.toolCall(request);
128049
127725
  }
128050
127726
  };
128051
- function errorMessage$1(error) {
127727
+ function errorMessage(error) {
128052
127728
  return error instanceof Error ? error.message : String(error);
128053
127729
  }
128054
127730
  //#endregion
@@ -128448,38 +128124,6 @@ var ByfHarness = class {
128448
128124
  async removeProvider(providerId) {
128449
128125
  return this.rpc.removeProvider(providerId);
128450
128126
  }
128451
- async updateConfig(input = {}) {
128452
- const configPath = input.configPath ?? this.configPath;
128453
- if (!existsSync(configPath)) return {
128454
- findings: [],
128455
- fixed: false
128456
- };
128457
- const config = readConfigFile(configPath);
128458
- const findings = analyzeConfig(config);
128459
- if (!input.fix || findings.length === 0) return {
128460
- findings,
128461
- fixed: false
128462
- };
128463
- const backupPath = `${configPath}.bak.${(/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-")}`;
128464
- await copyFile(configPath, backupPath);
128465
- await chmod(backupPath, 384);
128466
- try {
128467
- await writeConfigFile(configPath, applyFixes(config, findings));
128468
- readConfigFile(configPath);
128469
- return {
128470
- findings,
128471
- fixed: true,
128472
- backupPath
128473
- };
128474
- } catch (error) {
128475
- try {
128476
- await copyFile(backupPath, configPath);
128477
- } catch (rollbackError) {
128478
- throw new Error(`[update-config] Rollback failed: could not restore backup at ${backupPath}. Original error: ${errorMessage(error)}. Rollback error: ${errorMessage(rollbackError)}.`, { cause: rollbackError });
128479
- }
128480
- throw error;
128481
- }
128482
- }
128483
128127
  async shellExec(command, options = {}) {
128484
128128
  const normalizedCommand = command.trim();
128485
128129
  if (normalizedCommand.length === 0) throw new ByfError(ErrorCodes.REQUEST_INVALID, "Shell command cannot be empty.");
@@ -128510,9 +128154,6 @@ function normalizeSessionId(value) {
128510
128154
  if (normalized.length === 0) throw new ByfError(ErrorCodes.SESSION_ID_EMPTY, "Session id cannot be empty.");
128511
128155
  return normalized;
128512
128156
  }
128513
- function errorMessage(error) {
128514
- return error instanceof Error ? error.message : String(error);
128515
- }
128516
128157
  //#endregion
128517
128158
  //#region src/catalog.ts
128518
128159
  const DEFAULT_CATALOG_URL = "https://models.dev/api.json";