@byfriends/cli 0.1.2 → 0.2.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.
Files changed (3) hide show
  1. package/LICENSE +28 -0
  2. package/dist/main.mjs +849 -188
  3. package/package.json +23 -23
package/dist/main.mjs CHANGED
@@ -8041,6 +8041,9 @@ Anthropic.Completions = Completions$2;
8041
8041
  Anthropic.Messages = Messages$2;
8042
8042
  Anthropic.Models = Models$2;
8043
8043
  Anthropic.Beta = Beta$1;
8044
+ //#endregion
8045
+ //#region ../../packages/kosong/src/capability.ts
8046
+ const UNKNOWN_CAPABILITY_MARKER = Symbol.for("byf.kosong.UNKNOWN_CAPABILITY");
8044
8047
  /**
8045
8048
  * Shared read-only default returned when a provider has not catalogued a
8046
8049
  * given model. Frozen so accidental mutation at one call site cannot leak
@@ -8056,9 +8059,32 @@ const UNKNOWN_CAPABILITY = Object.freeze(Object.defineProperty({
8056
8059
  thinking_xhigh: false,
8057
8060
  thinking_max: false,
8058
8061
  max_context_tokens: 0
8059
- }, Symbol.for("byf.kosong.UNKNOWN_CAPABILITY"), { value: true }));
8062
+ }, UNKNOWN_CAPABILITY_MARKER, { value: true }));
8063
+ function isUnknownCapability(capability) {
8064
+ if (capability === UNKNOWN_CAPABILITY) return true;
8065
+ if (capability[UNKNOWN_CAPABILITY_MARKER] === true) return true;
8066
+ return !capability.image_in && !capability.video_in && !capability.audio_in && !capability.thinking && !capability.tool_use && !capability.thinking_effort && !capability.thinking_xhigh && !capability.thinking_max && capability.max_context_tokens === 0;
8067
+ }
8060
8068
  //#endregion
8061
8069
  //#region ../../packages/kosong/src/providers/capability-registry.ts
8070
+ const CACHE_CAPABILITY = Object.freeze({
8071
+ strategy: "explicit-block",
8072
+ maxCacheableBlocks: 4,
8073
+ supportedScopes: [
8074
+ "global",
8075
+ "project",
8076
+ "session",
8077
+ "none"
8078
+ ]
8079
+ });
8080
+ const OPENAI_CACHE_CAPABILITY = Object.freeze({
8081
+ strategy: "prompt-cache-key",
8082
+ supportedScopes: ["global"]
8083
+ });
8084
+ const NO_CACHE_CAPABILITY = Object.freeze({
8085
+ strategy: "none",
8086
+ supportedScopes: []
8087
+ });
8062
8088
  const OPENAI_RESPONSES_DEVELOPER_ROLE_MODELS = new Set([
8063
8089
  "gpt-4.1",
8064
8090
  "gpt-4.1-mini",
@@ -8105,7 +8131,8 @@ const OPENAI_REASONING_CAPABILITY = Object.freeze({
8105
8131
  thinking_effort: true,
8106
8132
  thinking_xhigh: false,
8107
8133
  thinking_max: false,
8108
- max_context_tokens: 0
8134
+ max_context_tokens: 0,
8135
+ cache: OPENAI_CACHE_CAPABILITY
8109
8136
  });
8110
8137
  const OPENAI_REASONING_XHIGH_CAPABILITY = Object.freeze({
8111
8138
  image_in: false,
@@ -8116,7 +8143,8 @@ const OPENAI_REASONING_XHIGH_CAPABILITY = Object.freeze({
8116
8143
  thinking_effort: true,
8117
8144
  thinking_xhigh: true,
8118
8145
  thinking_max: false,
8119
- max_context_tokens: 0
8146
+ max_context_tokens: 0,
8147
+ cache: OPENAI_CACHE_CAPABILITY
8120
8148
  });
8121
8149
  const OPENAI_VISION_TOOL_CAPABILITY = Object.freeze({
8122
8150
  image_in: true,
@@ -8127,7 +8155,8 @@ const OPENAI_VISION_TOOL_CAPABILITY = Object.freeze({
8127
8155
  thinking_effort: false,
8128
8156
  thinking_xhigh: false,
8129
8157
  thinking_max: false,
8130
- max_context_tokens: 0
8158
+ max_context_tokens: 0,
8159
+ cache: OPENAI_CACHE_CAPABILITY
8131
8160
  });
8132
8161
  const OPENAI_TEXT_TOOL_CAPABILITY = Object.freeze({
8133
8162
  image_in: false,
@@ -8138,7 +8167,8 @@ const OPENAI_TEXT_TOOL_CAPABILITY = Object.freeze({
8138
8167
  thinking_effort: false,
8139
8168
  thinking_xhigh: false,
8140
8169
  thinking_max: false,
8141
- max_context_tokens: 0
8170
+ max_context_tokens: 0,
8171
+ cache: OPENAI_CACHE_CAPABILITY
8142
8172
  });
8143
8173
  const ANTHROPIC_VISION_TOOL_CAPABILITY = Object.freeze({
8144
8174
  image_in: true,
@@ -8149,7 +8179,8 @@ const ANTHROPIC_VISION_TOOL_CAPABILITY = Object.freeze({
8149
8179
  thinking_effort: false,
8150
8180
  thinking_xhigh: false,
8151
8181
  thinking_max: false,
8152
- max_context_tokens: 0
8182
+ max_context_tokens: 0,
8183
+ cache: CACHE_CAPABILITY
8153
8184
  });
8154
8185
  const ANTHROPIC_THINKING_VISION_TOOL_CAPABILITY = Object.freeze({
8155
8186
  image_in: true,
@@ -8160,7 +8191,8 @@ const ANTHROPIC_THINKING_VISION_TOOL_CAPABILITY = Object.freeze({
8160
8191
  thinking_effort: true,
8161
8192
  thinking_xhigh: false,
8162
8193
  thinking_max: true,
8163
- max_context_tokens: 0
8194
+ max_context_tokens: 0,
8195
+ cache: CACHE_CAPABILITY
8164
8196
  });
8165
8197
  const ANTHROPIC_THINKING_XHIGH_VISION_TOOL_CAPABILITY = Object.freeze({
8166
8198
  image_in: true,
@@ -8171,7 +8203,8 @@ const ANTHROPIC_THINKING_XHIGH_VISION_TOOL_CAPABILITY = Object.freeze({
8171
8203
  thinking_effort: true,
8172
8204
  thinking_xhigh: true,
8173
8205
  thinking_max: true,
8174
- max_context_tokens: 0
8206
+ max_context_tokens: 0,
8207
+ cache: CACHE_CAPABILITY
8175
8208
  });
8176
8209
  const GEMINI_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
8177
8210
  image_in: true,
@@ -8182,7 +8215,8 @@ const GEMINI_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
8182
8215
  thinking_effort: false,
8183
8216
  thinking_xhigh: false,
8184
8217
  thinking_max: false,
8185
- max_context_tokens: 0
8218
+ max_context_tokens: 0,
8219
+ cache: NO_CACHE_CAPABILITY
8186
8220
  });
8187
8221
  const GEMINI_THINKING_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
8188
8222
  image_in: true,
@@ -8193,7 +8227,8 @@ const GEMINI_THINKING_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
8193
8227
  thinking_effort: false,
8194
8228
  thinking_xhigh: false,
8195
8229
  thinking_max: false,
8196
- max_context_tokens: 0
8230
+ max_context_tokens: 0,
8231
+ cache: NO_CACHE_CAPABILITY
8197
8232
  });
8198
8233
  const OPENAI_LEGACY_CAPABILITY_CATALOG = [
8199
8234
  {
@@ -8262,7 +8297,12 @@ function capabilityFromCatalog(modelName, catalog) {
8262
8297
  return UNKNOWN_CAPABILITY;
8263
8298
  }
8264
8299
  function getOpenAILegacyModelCapability(modelName) {
8265
- return capabilityFromCatalog(modelName, OPENAI_LEGACY_CAPABILITY_CATALOG);
8300
+ const capability = capabilityFromCatalog(modelName, OPENAI_LEGACY_CAPABILITY_CATALOG);
8301
+ if (!capability.cache) return {
8302
+ ...capability,
8303
+ cache: OPENAI_CACHE_CAPABILITY
8304
+ };
8305
+ return capability;
8266
8306
  }
8267
8307
  function getOpenAIResponsesModelCapability(modelName) {
8268
8308
  return capabilityFromCatalog(modelName, OPENAI_RESPONSES_CAPABILITY_CATALOG);
@@ -8277,6 +8317,23 @@ function getGoogleGenAIModelCapability(modelName) {
8277
8317
  if (normalized.startsWith("gemini-2.5-") || normalized.includes("thinking")) return GEMINI_THINKING_MULTIMODAL_TOOL_CAPABILITY;
8278
8318
  return GEMINI_MULTIMODAL_TOOL_CAPABILITY;
8279
8319
  }
8320
+ /**
8321
+ * Tries all provider-specific capability registries and returns the first
8322
+ * non-UNKNOWN match. Used when the provider context is unknown (e.g., catalog
8323
+ * enrichment) to fill in accurate thinking capability flags.
8324
+ */
8325
+ function resolveCapabilityFromRegistry(modelName) {
8326
+ const registries = [
8327
+ getAnthropicModelCapability,
8328
+ getOpenAIResponsesModelCapability,
8329
+ getOpenAILegacyModelCapability,
8330
+ getGoogleGenAIModelCapability
8331
+ ];
8332
+ for (const fn of registries) {
8333
+ const cap = fn(modelName);
8334
+ if (!isUnknownCapability(cap)) return cap;
8335
+ }
8336
+ }
8280
8337
  function usesOpenAIResponsesDeveloperRole(modelName) {
8281
8338
  const normalized = normalizeModelName(modelName);
8282
8339
  if (OPENAI_RESPONSES_DEVELOPER_ROLE_MODELS.has(normalized)) return true;
@@ -8511,31 +8568,34 @@ function clampEffort(effort, model) {
8511
8568
  return effort;
8512
8569
  }
8513
8570
  const CACHE_CONTROL = { type: "ephemeral" };
8514
- function splitSystemPrompt(systemPrompt, breakpoints) {
8515
- if (!breakpoints || breakpoints.length === 0) return systemPrompt ? [{
8516
- type: "text",
8517
- text: systemPrompt,
8518
- cache_control: CACHE_CONTROL
8519
- }] : [];
8571
+ /**
8572
+ * Content block types that support cache_control injection.
8573
+ */
8574
+ const CACHEABLE_TYPES = new Set([
8575
+ "text",
8576
+ "image",
8577
+ "document",
8578
+ "search_result",
8579
+ "tool_use",
8580
+ "tool_result",
8581
+ "server_tool_use",
8582
+ "web_search_tool_result"
8583
+ ]);
8584
+ /**
8585
+ * Convert a PromptPlan to Anthropic TextBlockParam[] with cache control.
8586
+ *
8587
+ * Injects cache_control on blocks with cacheScope other than 'none'.
8588
+ */
8589
+ function promptPlanToSystemBlocks(promptPlan) {
8520
8590
  const blocks = [];
8521
- let remaining = systemPrompt;
8522
- for (const marker of breakpoints) {
8523
- const idx = remaining.indexOf(marker);
8524
- if (idx === -1) continue;
8525
- const before = remaining.slice(0, idx).trim();
8526
- if (before) blocks.push({
8591
+ for (const block of promptPlan.blocks) {
8592
+ const textBlock = {
8527
8593
  type: "text",
8528
- text: before,
8529
- cache_control: CACHE_CONTROL
8530
- });
8531
- remaining = remaining.slice(idx + marker.length);
8594
+ text: block.text
8595
+ };
8596
+ if (block.cacheScope !== "none") textBlock.cache_control = CACHE_CONTROL;
8597
+ blocks.push(textBlock);
8532
8598
  }
8533
- const final = remaining.trim();
8534
- if (final) blocks.push({
8535
- type: "text",
8536
- text: final,
8537
- cache_control: CACHE_CONTROL
8538
- });
8539
8599
  return blocks;
8540
8600
  }
8541
8601
  /**
@@ -8617,9 +8677,11 @@ function convertMessage$2(message) {
8617
8677
  };
8618
8678
  if (role === "tool") {
8619
8679
  if (message.toolCallId === void 0) throw new ChatProviderError("Tool message missing `toolCallId`.");
8680
+ const block = toolResultToBlock(message.toolCallId, message.content);
8681
+ if (message.cacheHint?.isLastTurnEnd || message.cacheHint?.isSuddenLargeContext) block.cache_control = CACHE_CONTROL;
8620
8682
  return {
8621
8683
  role: "user",
8622
- content: [toolResultToBlock(message.toolCallId, message.content)]
8684
+ content: [block]
8623
8685
  };
8624
8686
  }
8625
8687
  const blocks = [];
@@ -8653,6 +8715,10 @@ function convertMessage$2(message) {
8653
8715
  input: toolInput
8654
8716
  });
8655
8717
  }
8718
+ if (message.cacheHint?.isLastTurnEnd || message.cacheHint?.isSuddenLargeContext) {
8719
+ const lastBlock = blocks.at(-1);
8720
+ if (lastBlock !== void 0 && CACHEABLE_TYPES.has(lastBlock.type)) lastBlock.cache_control = CACHE_CONTROL;
8721
+ }
8656
8722
  return {
8657
8723
  role,
8658
8724
  content: blocks
@@ -8901,8 +8967,16 @@ var AnthropicChatProvider = class {
8901
8967
  return getAnthropicModelCapability(model ?? this._model);
8902
8968
  }
8903
8969
  async generate(systemPrompt, tools, history, options) {
8904
- const systemBlocks = splitSystemPrompt(systemPrompt, options?.cacheBreakpoints);
8905
- const system = systemBlocks.length > 0 ? systemBlocks : void 0;
8970
+ let system;
8971
+ if (options?.promptPlan) {
8972
+ const systemBlocks = promptPlanToSystemBlocks(options.promptPlan);
8973
+ system = systemBlocks.length > 0 ? systemBlocks : void 0;
8974
+ } else if (systemPrompt) system = [{
8975
+ type: "text",
8976
+ text: systemPrompt,
8977
+ cache_control: CACHE_CONTROL
8978
+ }];
8979
+ else system = void 0;
8906
8980
  const messages = [];
8907
8981
  for (const msg of history) {
8908
8982
  const converted = convertMessage$2(msg);
@@ -45335,6 +45409,22 @@ function isEffectivelyEmptyContent(parts) {
45335
45409
  }
45336
45410
  return true;
45337
45411
  }
45412
+ /**
45413
+ * Derive a stable SHA256 hash from cacheable blocks in a PromptPlan.
45414
+ *
45415
+ * Only blocks with cacheScope 'global' are included in the hash, as OpenAI
45416
+ * only supports caching the prefix (global scope).
45417
+ *
45418
+ * @param promptPlan - The prompt plan containing cacheable blocks.
45419
+ * @returns A hexadecimal SHA256 hash string.
45420
+ */
45421
+ function deriveCacheKeyFromPromptPlan$1(promptPlan) {
45422
+ if (!promptPlan || promptPlan.blocks.length === 0) return createHash("sha256").digest("hex");
45423
+ const cacheableTexts = [];
45424
+ for (const block of promptPlan.blocks) if (block.cacheScope === "global") cacheableTexts.push(block.text);
45425
+ const concatenated = cacheableTexts.join("");
45426
+ return createHash("sha256").update(concatenated).digest("hex");
45427
+ }
45338
45428
  function convertMessage$1(message, reasoningKey, toolMessageConversion) {
45339
45429
  let reasoningContent = "";
45340
45430
  const nonThinkParts = [];
@@ -45569,6 +45659,10 @@ var OpenAICompletionsChatProvider = class {
45569
45659
  };
45570
45660
  if (tools.length > 0) createParams["tools"] = tools.map((t) => convertTool$1(t));
45571
45661
  if (this._stream) createParams["stream_options"] = { include_usage: true };
45662
+ if (options?.promptPlan) {
45663
+ const cacheKey = deriveCacheKeyFromPromptPlan$1(options.promptPlan);
45664
+ if (cacheKey) createParams["prompt_cache_key"] = cacheKey;
45665
+ }
45572
45666
  try {
45573
45667
  return new OpenAICompletionsStreamedMessage(await this._createClient(options?.auth).chat.completions.create(createParams, options?.signal ? { signal: options.signal } : void 0), this._stream, this._reasoningKey);
45574
45668
  } catch (error) {
@@ -45688,6 +45782,19 @@ function normalizeResponsesFinishReason(status, incompleteReason) {
45688
45782
  rawFinishReason: null
45689
45783
  };
45690
45784
  }
45785
+ /**
45786
+ * Derive a stable cache key from cacheable blocks in a PromptPlan.
45787
+ *
45788
+ * Only blocks with cacheScope 'global' are included in the hash, as OpenAI
45789
+ * only supports caching the prefix (global scope).
45790
+ */
45791
+ function deriveCacheKeyFromPromptPlan(promptPlan) {
45792
+ if (!promptPlan || promptPlan.blocks.length === 0) return void 0;
45793
+ const cacheableTexts = [];
45794
+ for (const block of promptPlan.blocks) if (block.cacheScope === "global") cacheableTexts.push(block.text);
45795
+ if (cacheableTexts.length === 0) return void 0;
45796
+ return createHash("sha256").update(cacheableTexts.join("")).digest("hex");
45797
+ }
45691
45798
  function asRawObject(value) {
45692
45799
  if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
45693
45800
  return value;
@@ -46217,6 +46324,10 @@ var OpenAIResponsesChatProvider = class {
46217
46324
  stream: this._stream,
46218
46325
  ...kwargs
46219
46326
  };
46327
+ if (options?.promptPlan) {
46328
+ const cacheKey = deriveCacheKeyFromPromptPlan(options.promptPlan);
46329
+ if (cacheKey) createParams["prompt_cache_key"] = cacheKey;
46330
+ }
46220
46331
  if (!("responses" in client) || typeof client.responses?.create !== "function") throw new Error("OpenAI SDK version does not support Responses API. Upgrade to >=4.x with responses support.");
46221
46332
  return new OpenAIResponsesStreamedMessage(await client.responses.create(createParams, options?.signal ? { signal: options.signal } : void 0), this._stream);
46222
46333
  } catch (error) {
@@ -46335,22 +46446,29 @@ function catalogModelToCapability(model) {
46335
46446
  if (!isUsableChatModel(model)) return void 0;
46336
46447
  const inputs = model.modalities?.input ?? [];
46337
46448
  const output = model.limit?.output;
46449
+ const base = {
46450
+ image_in: inputs.includes("image"),
46451
+ video_in: inputs.includes("video"),
46452
+ audio_in: inputs.includes("audio"),
46453
+ thinking: Boolean(model.reasoning),
46454
+ tool_use: model.tool_call ?? true,
46455
+ thinking_effort: false,
46456
+ thinking_xhigh: false,
46457
+ thinking_max: false,
46458
+ max_context_tokens: context
46459
+ };
46460
+ const registry = resolveCapabilityFromRegistry(model.id);
46461
+ const capability = registry !== void 0 ? {
46462
+ ...base,
46463
+ ...registry,
46464
+ max_context_tokens: context
46465
+ } : base;
46338
46466
  return {
46339
46467
  id: model.id,
46340
46468
  name: typeof model.name === "string" && model.name.length > 0 ? model.name : void 0,
46341
46469
  maxOutputSize: typeof output === "number" && output > 0 ? output : void 0,
46342
46470
  reasoningKey: catalogReasoningKey(model.interleaved),
46343
- capability: {
46344
- image_in: inputs.includes("image"),
46345
- video_in: inputs.includes("video"),
46346
- audio_in: inputs.includes("audio"),
46347
- thinking: Boolean(model.reasoning),
46348
- tool_use: model.tool_call ?? true,
46349
- thinking_effort: false,
46350
- thinking_xhigh: false,
46351
- thinking_max: false,
46352
- max_context_tokens: context
46353
- }
46471
+ capability
46354
46472
  };
46355
46473
  }
46356
46474
  function catalogReasoningKey(interleaved) {
@@ -46535,6 +46653,17 @@ function addUsage(a, b) {
46535
46653
  inputCacheCreation: a.inputCacheCreation + b.inputCacheCreation
46536
46654
  };
46537
46655
  }
46656
+ /**
46657
+ * Compute the cache hit rate as a branded number between 0 and 1.
46658
+ *
46659
+ * Returns `undefined` when no input tokens were processed (inputTotal === 0),
46660
+ * so callers can distinguish "no data" from "zero hits".
46661
+ */
46662
+ function cacheHitRate(usage) {
46663
+ const total = inputTotal(usage);
46664
+ if (total === 0) return void 0;
46665
+ return usage.inputCacheRead / total;
46666
+ }
46538
46667
  //#endregion
46539
46668
  //#region ../../packages/kosong/src/index.ts
46540
46669
  /**
@@ -46695,7 +46824,7 @@ function redactCtx(ctx) {
46695
46824
  };
46696
46825
  return walk(ctx, 0);
46697
46826
  }
46698
- function truncate$2(value, max) {
46827
+ function truncate$1(value, max) {
46699
46828
  return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$3;
46700
46829
  }
46701
46830
  function serializeValue(raw) {
@@ -46719,7 +46848,7 @@ function quote(value) {
46719
46848
  return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n")}"`;
46720
46849
  }
46721
46850
  function formatPair(key, raw) {
46722
- const limited = truncate$2(serializeValue(raw), CTX_VALUE_MAX_CHARS);
46851
+ const limited = truncate$1(serializeValue(raw), CTX_VALUE_MAX_CHARS);
46723
46852
  return `${SAFE_KEY_RE.test(key) ? key : quote(key)}=${/[\s="\\]/.test(limited) || limited.length === 0 ? quote(limited) : limited}`;
46724
46853
  }
46725
46854
  function clipBytes(text, maxBytes) {
@@ -46743,7 +46872,7 @@ function indentStack(stack) {
46743
46872
  function formatEntry(entry, options = {}) {
46744
46873
  const ctx = entry.ctx ? redactCtx(entry.ctx) : void 0;
46745
46874
  const omitContextKeys = new Set(options.omitContextKeys ?? []);
46746
- const msg = truncate$2(entry.msg, 200);
46875
+ const msg = truncate$1(entry.msg, 200);
46747
46876
  const pairs = [];
46748
46877
  if (ctx) for (const [k, v] of Object.entries(ctx)) {
46749
46878
  if (omitContextKeys.has(k)) continue;
@@ -54524,13 +54653,11 @@ function createSystemPromptRenderer(merged) {
54524
54653
  }
54525
54654
  function buildTemplateVars(context, promptVars) {
54526
54655
  const skills = typeof context.skills === "string" ? context.skills : context.skills?.getModelSkillListing() ?? "";
54527
- const now = context.now instanceof Date ? context.now.toISOString() : context.now ?? (/* @__PURE__ */ new Date()).toISOString();
54528
54656
  const agentsMd = context.agentsMd ?? "";
54529
54657
  return {
54530
54658
  ...promptVars,
54531
54659
  BYF_OS: context.osEnv.osKind,
54532
54660
  BYF_SHELL: `${context.osEnv.shellName} (\`${context.osEnv.shellPath}\`)`,
54533
- BYF_NOW: now,
54534
54661
  BYF_WORK_DIR: context.cwd,
54535
54662
  BYF_AGENTS_MD: agentsMd,
54536
54663
  BYF_AGENTS_MD_TOO_LONG: estimateTokens(agentsMd) > 4e3 ? "true" : "",
@@ -54611,10 +54738,10 @@ function normalizeSourcePath(path) {
54611
54738
  var agent_default$1 = "name: agent\ndescription: Default BYF agent\n\nsystemPromptPath: ./system.md\npromptVars:\n roleAdditional: ''\n\ntools:\n - Read\n - Write\n - Edit\n - Grep\n - Glob\n - Bash\n - TaskList\n - TaskOutput\n - TaskStop\n - ReadMediaFile\n - TodoList\n - Skill\n - WebSearch\n - Agent\n - FetchURL\n - AskUserQuestion\n - mcp__*\n\nsubagents:\n coder:\n description: Good at general software engineering tasks.\n explore:\n description: Fast codebase exploration with prompt-enforced read-only behavior.\n";
54612
54739
  //#endregion
54613
54740
  //#region ../../packages/agent-core/src/profile/default/coder.yaml
54614
- var coder_default = "extends: agent\nname: coder\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\nwhenToUse: |\n Use this agent for non-trivial software engineering work that may require reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - Write\n - Edit\n - WebSearch\n - FetchURL\n - mcp__*\n";
54741
+ var coder_default = "extends: agent\nname: coder\npromptVars:\n roleAdditional: |\n You are operating as a subagent instance. The main BYF agent spawned you to handle a specific task. All user messages come from the main agent it cannot see your context and will only see your final result when you finish. Do not address the end user directly. If something is unclear, explain the ambiguity in your summary to the parent agent.\nwhenToUse: |\n Use this agent for non-trivial software engineering work that may require reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - Write\n - Edit\n - WebSearch\n - FetchURL\n - mcp__*\n";
54615
54742
  //#endregion
54616
54743
  //#region ../../packages/agent-core/src/profile/default/explore.yaml
54617
- var explore_default = "extends: agent\nname: explore\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n You are a codebase exploration specialist. Your role is EXCLUSIVELY to search, read, and analyze existing code and resources. You do NOT have access to file editing tools.\n\n Your strengths:\n - Rapidly finding files using glob patterns\n - Searching code and text with powerful regex patterns\n - Reading and analyzing file contents\n - Running read-only shell commands (git log, git diff, ls, find, etc.)\n\n Guidelines:\n - Use Glob for broad file pattern matching. Patterns MUST contain a literal anchor (extension or subdirectory); pure wildcards like `*` or `**/*` are rejected by the tool.\n - Use Grep for searching file contents with regex\n - Use Read when you know the specific file path\n - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find)\n - NEVER use Bash for any file creation or modification commands\n - Adapt your search depth based on the thoroughness level specified by the caller\n - Wherever possible, spawn multiple parallel tool calls for grepping and reading files to maximize speed\n\n If the prompt includes a <git-context> block, use it to orient yourself about the repository state before starting your investigation.\n\n You are meant to be a fast agent. Complete the search request efficiently and report your findings clearly in a structured format.\nwhenToUse: |\n Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. \"src/**/*.yaml\"), search code for keywords (e.g. \"database connection\"), or answer questions about the codebase (e.g. \"how does the auth module work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"thorough\" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. Prefer launching multiple explore agents concurrently when investigating independent questions.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - WebSearch\n - FetchURL\n";
54744
+ var explore_default = "extends: agent\nname: explore\npromptVars:\n roleAdditional: |\n You are operating as a subagent instance. The main BYF agent spawned you to handle a specific task. All user messages come from the main agent it cannot see your context and will only see your final result when you finish. Do not address the end user directly. If something is unclear, explain the ambiguity in your summary to the parent agent.\n\n You are a codebase exploration specialist. Your role is EXCLUSIVELY to search, read, and analyze existing code and resources. You do NOT have access to file editing tools.\n\n Your strengths:\n - Rapidly finding files using glob patterns\n - Searching code and text with powerful regex patterns\n - Reading and analyzing file contents\n - Running read-only shell commands (git log, git diff, ls, find, etc.)\n\n Guidelines:\n - Use Glob for broad file pattern matching. Patterns MUST contain a literal anchor (extension or subdirectory); pure wildcards like `*` or `**/*` are rejected by the tool.\n - Use Grep for searching file contents with regex\n - Use Read when you know the specific file path\n - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find)\n - NEVER use Bash for any file creation or modification commands\n - Adapt your search depth based on the thoroughness level specified by the caller\n - Wherever possible, spawn multiple parallel tool calls for grepping and reading files to maximize speed\n\n If the prompt includes a <git-context> block, use it to orient yourself about the repository state before starting your investigation.\n\n You are meant to be a fast agent. Complete the search request efficiently and report your findings clearly in a structured format.\nwhenToUse: |\n Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. \"src/**/*.yaml\"), search code for keywords (e.g. \"database connection\"), or answer questions about the codebase (e.g. \"how does the auth module work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"thorough\" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. Prefer launching multiple explore agents concurrently when investigating independent questions.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - WebSearch\n - FetchURL\n";
54618
54745
  //#endregion
54619
54746
  //#region ../../packages/agent-core/src/profile/default/init.md
54620
54747
  var init_default = "You are a software engineering expert with many years of programming experience. Please explore the current project directory to understand the project's architecture and main details.\n\nTask requirements:\n1. Analyze the project structure and identify key configuration files (such as pyproject.toml, package.json, Cargo.toml, etc.).\n2. Understand the project's technology stack, build process and runtime architecture.\n3. Identify how the code is organized and main module divisions.\n4. Discover project-specific development conventions, testing strategies, and deployment processes.\n\nAfter the exploration, you should do a thorough summary of your findings and overwrite it into `AGENTS.md` file in the project root. You need to refer to what is already in the file when you do so.\n\nFor your information, `AGENTS.md` is a file intended to be read by AI coding agents. Expect the reader of this file know nothing about the project.\n\nYou should compose this file according to the actual project content. Do not make any assumptions or generalizations. Ensure the information is accurate and useful. You must use the natural language that is mainly used in the project's comments and documentation.\n\nPopular sections that people usually write in `AGENTS.md` are:\n\n- Project overview\n- Build and test commands\n- Code style guidelines\n- Testing instructions\n- Security considerations\n";
@@ -54624,7 +54751,7 @@ const PROFILE_SOURCES = {
54624
54751
  "profile/default/agent.yaml": agent_default$1,
54625
54752
  "profile/default/coder.yaml": coder_default,
54626
54753
  "profile/default/explore.yaml": explore_default,
54627
- "profile/default/system.md": "You are BYF, an interactive general AI agent running on a user's computer.\n\nYour primary goal is to help users with software engineering tasks by taking action — use the tools available to you to make real changes on the user's system. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.\n\n{{ ROLE_ADDITIONAL }}\n\n# Prompt and Tool Use\n\nThe user's messages may contain questions and/or task descriptions in natural language, code snippets, logs, file paths, or other forms of information. Read them, understand them and do what the user requested. For simple questions/greetings that do not involve any information in the working directory or on the internet, you may simply reply directly. For anything else, default to taking action with tools. When the request could be interpreted as either a question to answer or a task to complete, treat it as a task.\n\nWhen handling the user's request, if it involves creating, modifying, or running code or files, you MUST use the appropriate tools (e.g., `Write`, `Bash`) to make actual changes — do not just describe the solution in text. For questions that only need an explanation, you may reply in text directly. When calling tools, do not provide explanations because the tool calls themselves should be self-explanatory. You MUST follow the description of each tool and its parameters when calling tools.\n\nIf the `Agent` tool is available, you can use it to delegate a focused subtask to a subagent instance. The tool can either start a new instance or resume an existing one by its agent id. Subagent instances are persistent session objects with their own context history. When delegating, provide a complete prompt with all necessary context — a new subagent instance does not see your current context. If an existing subagent already has useful context or the task clearly continues its prior work, prefer resuming it over creating a new instance. Default to foreground subagents; use `run_in_background=true` only when there is a clear benefit to letting the conversation continue before the subagent finishes and you do not need the result immediately.\n\nYou have the capability to output any number of tool calls in a single response. If you anticipate making multiple non-interfering tool calls, you are HIGHLY RECOMMENDED to make them in parallel to significantly improve efficiency. This is very important to your performance.\n\nThe results of the tool calls will be returned to you in a tool message. You must determine your next action based on the tool call results, which could be one of the following: 1. Continue working on the task, 2. Inform the user that the task is completed or has failed, or 3. Ask the user for more information.\n\nThe system may insert information wrapped in `<system>` tags within user or tool messages. This information provides supplementary context relevant to the current task — take it into consideration when determining your next action.\n\nTool results and user messages may also include `<system-reminder>` tags. Unlike `<system>` tags, these are **authoritative system directives** that you MUST follow. They bear no direct relation to the specific tool results or user messages in which they appear. Always read them carefully and comply with their instructions — they may override or constrain your normal behavior.\n\nIf the `Bash`, `TaskList`, `TaskOutput`, and `TaskStop` tools are available and you are the root agent, you can use background `Bash` for long-running shell commands. Launch it via `Bash` with `run_in_background=true` and a short `description`. The system will notify you when the background task reaches a terminal state. Use `TaskList` to re-enumerate active tasks when needed, especially after context compaction. Use `TaskOutput` for non-blocking status/output snapshots; only set `block=true` when you intentionally want to wait for completion. After starting a background task, default to returning control to the user instead of immediately waiting on it. Use `TaskStop` only when you need to cancel the task. For human users in the interactive shell, the only task-management slash command is `/tasks`. Do not tell users to run `/task`, `/tasks list`, `/tasks output`, `/tasks stop`, or any other invented slash subcommands. If you are a subagent or these tools are not available, do not assume you can create or control background tasks.\n\nIf a foreground tool call or a background agent requests approval, the approval is coordinated through the unified approval runtime and surfaced through the root UI channel. Do not assume approvals are local to a single subagent turn.\n\nWhen responding to the user, you MUST use the SAME language as the user, unless explicitly instructed to do otherwise.\n\n# Tool Efficiency Guidelines\n\nThe following common command categories are usually available in Bash. Availability depends on the host, so when in doubt run `which <command>` first to confirm a command exists before relying on it.\n- Navigation and inspection: `ls`, `pwd`, `cd`, `stat`, `file`, `du`, `df`, `tree`\n- File and directory management: `cp`, `mv`, `rm`, `mkdir`, `touch`, `ln`, `chmod`, `chown`\n- Text and data processing: `wc`, `sort`, `uniq`, `cut`, `tr`, `diff`, `xargs`\n- Archives and compression: `tar`, `gzip`, `gunzip`, `zip`, `unzip`\n- Networking and transfer: `curl`, `wget`, `ping`, `ssh`, `scp`\n- Version control: `git`\n- Process and system: `ps`, `kill`, `top`, `env`, `date`, `uname`, `whoami`\n- Language and package toolchains: `node`, `npm`, `pnpm`, `yarn`, `python`, `pip` (use whichever the project actually relies on)\n\nWhen using Bash:\n- For multiple related commands, use `&&` to chain them in a single call, e.g. `cd /path && ls -la`\n- Use `;` to run commands sequentially regardless of success/failure\n- Use `||` for conditional execution (run second command only if first fails)\n- Use pipe operations (`|`) and redirections (`>`, `>>`) to chain input and output between commands\n- Always quote file paths containing spaces with double quotes (e.g., cd \"/path with spaces/\")\n- Compose multi-step logic in a single call with `if` / `case` / `for` / `while` control flows.\n\nWhen using Grep, ALWAYS use the Grep tool instead of running `grep` or `rg` from a shell — direct shell calls bypass workspace policy, output limits, and sensitive-file filtering. Use Grep only when the task is to search for unknown content or locations.\n\nWhen reading files:\n- If the user provides a concrete file path to a text file, call Read directly. Do not `Glob`, `ls`, or otherwise pre-check known text file paths; missing or invalid file paths return errors you can handle.\n- Do not use Read for directories; use `ls` via Bash for a known directory, or Glob when you need files/directories matching a pattern.\n\nWhen editing files:\n- Use Edit for targeted changes to existing files; use Write only for new files or complete overwrites.\n- To modify a file, always use Edit; do not run a Shell `sed` command for edits.\n\n# First Principles\n\nThink from first principles. Start from real requirements, code facts, and verification results; if the goal is unclear, discuss it with the user first. Treat code, not documentation, as the source of truth.\n\n# General Guidelines for Coding\n\nWhen building something from scratch, you should:\n\n- Understand the user's requirements.\n- Ask the user for clarification if there is anything unclear.\n- Design the architecture and make a plan for the implementation.\n- Write the code in a modular and maintainable way.\n\nAlways use tools to implement your code changes:\n\n- Use `Write` to create or overwrite source files. Code that only appears in your text response is NOT saved to the file system and will not take effect.\n- Use `Bash` to run and test your code after writing it.\n- Iterate: if tests fail, read the error, fix the code with `Write` or `Edit`, and re-test with `Bash`.\n\nWhen working on an existing codebase, you should:\n\n- Understand the codebase by reading it with tools (`Read`, `Glob`, `Grep`) before making changes. Identify the ultimate goal and the most important criteria to achieve the goal.\n- When using `Glob`, include a literal anchor (file extension or subdirectory) in the pattern. Pure wildcards like `*` or `**/*` are rejected by the tool.\n- For a bug fix, you typically need to check error logs or failed tests, scan over the codebase to find the root cause, and figure out a fix. If user mentioned any failed tests, you should make sure they pass after the changes.\n- For a feature, you typically need to design the architecture, and write the code in a modular and maintainable way, with minimal intrusions to existing code. Add new tests if the project already has tests.\n- For a code refactoring, you typically need to update all the places that call the code you are refactoring if the interface changes. DO NOT change any existing logic especially in tests, focus only on fixing any errors caused by the interface changes.\n- Make MINIMAL changes to achieve the goal. This is very important to your performance.\n- Follow the coding style of existing code in the project.\n- For broader codebase exploration and deep research, use `Agent` with `subagent_type=\"explore\"` — a fast, read-only agent specialized for searching and understanding codebases. Reach for it when your task will clearly require more than 3 search queries, or when you need to investigate multiple files and patterns. Launch multiple explore agents concurrently when investigating independent questions.\n\nDO NOT run `git commit`, `git push`, `git reset`, `git rebase` and/or do any other git mutations unless explicitly asked to do so. Ask for confirmation each time when you need to do git mutations, even if the user has confirmed in earlier conversations.\n\n# General Guidelines for Research and Data Processing\n\n- Understand the user's requirements thoroughly, ask for clarification before you start if needed.\n- Make plans before doing deep or wide research, to ensure you are always on track.\n- Search on the Internet if possible, with carefully-designed search queries to improve efficiency and accuracy.\n- Use proper tools or shell commands or Python packages to process or generate images, videos, PDFs, docs, spreadsheets, presentations, or other multimedia files. Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.\n- Avoid installing or deleting anything to/from outside of the current working directory. If you have to do so, ask the user for confirmation.\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\nThe operating environment is not in a sandbox. Any actions you do will immediately affect the user's system. So you MUST be extremely cautious. Unless being explicitly instructed to do so, you should never access (read/write/execute) files outside of the working directory.\n\n## Date and Time\n\nThe current date and time in ISO format is `{{ BYF_NOW }}`. 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.\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__CACHE_BOUNDARY__\n\n# Project Information\n\nMarkdown files named `AGENTS.md` usually contain the background, structure, coding styles, user preferences and other relevant information about the project. You should use this information to understand the project and the user's preferences. `AGENTS.md` files may exist at different locations in the project, but typically there is one in the project root.\n\n> Why `AGENTS.md`?\n>\n> `README.md` files are for humans: quick starts, project descriptions, and contribution guidelines. `AGENTS.md` complements this by containing the extra, sometimes detailed context coding agents need: build steps, tests, and conventions that might clutter a README or aren’t relevant to human contributors.\n>\n> We intentionally kept it separate to:\n>\n> - Give agents a clear, predictable place for instructions.\n> - Keep `README`s concise and focused on human contributors.\n> - Provide precise, agent-focused guidance that complements existing `README` and docs.\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\n`AGENTS.md` files can appear at any level of the project directory tree, including inside `.byf/` directories. Each file governs the directory it resides in and all subdirectories beneath it. When multiple `AGENTS.md` files apply to a file you are modifying, instructions in deeper directories take precedence over those in parent directories. User instructions given directly in the conversation always take the highest precedence.\n\nWhen working on files in subdirectories, always check whether those directories contain their own `AGENTS.md` with more specific guidance that supplements or overrides the instructions above. You may also check `README`/`README.md` files for more information about the project.\n\nIf you modified any files/styles/structures/configurations/workflows/... mentioned in `AGENTS.md` files, you MUST update the corresponding `AGENTS.md` files to keep them up-to-date.\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\n# Ultimate Reminders\n\nAt any time, you should be HELPFUL, CONCISE, and ACCURATE. Be thorough in your actions — test what you build, verify what you change — not in your explanations.\n\n- Never give the user more than what they want.\n- Try your best to avoid any hallucination. Do fact checking before providing any factual information.\n- Do not give up too early.\n- ALWAYS, keep it stupidly simple. Do not overcomplicate things.\n"
54754
+ "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# 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# 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"
54628
54755
  };
54629
54756
  const DEFAULT_INIT_PROMPT = init_default;
54630
54757
  const DEFAULT_AGENT_PROFILES = loadAgentProfilesFromSources([
@@ -58257,7 +58384,6 @@ function isWithin(child, parent) {
58257
58384
  }
58258
58385
  //#endregion
58259
58386
  //#region ../../packages/agent-core/src/skill/registry.ts
58260
- const LISTING_DESC_MAX = 100;
58261
58387
  var SkillRegistry = class {
58262
58388
  byName = /* @__PURE__ */ new Map();
58263
58389
  roots = [];
@@ -58359,10 +58485,7 @@ function formatFullSkill(skill) {
58359
58485
  ];
58360
58486
  }
58361
58487
  function formatModelSkill(skill) {
58362
- return [`- ${skill.name}: ${truncate$1(skill.description, LISTING_DESC_MAX)}`];
58363
- }
58364
- function truncate$1(value, max) {
58365
- return value.length > max ? value.slice(0, max) : value;
58488
+ return [`- ${skill.name}: ${skill.description}`];
58366
58489
  }
58367
58490
  //#endregion
58368
58491
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/skill-tool.md
@@ -65765,9 +65888,6 @@ function notificationKey(origin) {
65765
65888
  }
65766
65889
  //#endregion
65767
65890
  //#region ../../node_modules/.pnpm/@antfu+utils@9.3.0/node_modules/@antfu/utils/dist/index.mjs
65768
- function uniq(array) {
65769
- return Array.from(new Set(array));
65770
- }
65771
65891
  function notNullish(v) {
65772
65892
  return v != null;
65773
65893
  }
@@ -66612,6 +66732,19 @@ var FullCompaction = class {
66612
66732
  computeCompactableCount(history) {
66613
66733
  return sliceCompleteMessages(history, this.strategy.computeCompactCount(history, this.maxContextSize));
66614
66734
  }
66735
+ restoreRecord(record) {
66736
+ switch (record.type) {
66737
+ case "full_compaction.begin":
66738
+ this.begin(record);
66739
+ break;
66740
+ case "full_compaction.cancel":
66741
+ this.cancel();
66742
+ break;
66743
+ case "full_compaction.complete":
66744
+ this.complete(record);
66745
+ break;
66746
+ }
66747
+ }
66615
66748
  };
66616
66749
  function extractCompactionSummary(response) {
66617
66750
  const summary = typeof response.message.content === "string" ? response.message.content : response.message.content.map((part) => part.type === "text" ? part.text : "").join("");
@@ -66753,6 +66886,13 @@ var ConfigState = class {
66753
66886
  return;
66754
66887
  }
66755
66888
  }
66889
+ restoreRecord(record) {
66890
+ switch (record.type) {
66891
+ case "config.update":
66892
+ this.update(record);
66893
+ break;
66894
+ }
66895
+ }
66756
66896
  };
66757
66897
  //#endregion
66758
66898
  //#region ../../packages/agent-core/src/agent/context/output-offloading.ts
@@ -67123,6 +67263,83 @@ var ContextMemory = class {
67123
67263
  });
67124
67264
  }
67125
67265
  }
67266
+ restoreRecord(record) {
67267
+ switch (record.type) {
67268
+ case "context.append_message":
67269
+ this.appendMessage(record.message);
67270
+ break;
67271
+ case "context.clear":
67272
+ this.restoreClear();
67273
+ break;
67274
+ case "context.apply_compaction":
67275
+ this.restoreApplyCompaction(record);
67276
+ break;
67277
+ case "context.mark_last_user_prompt_blocked":
67278
+ this.restoreMarkLastUserPromptBlocked(record);
67279
+ break;
67280
+ case "context.append_loop_event":
67281
+ this.restoreAppendLoopEvent(record);
67282
+ break;
67283
+ case "context.observation_masking":
67284
+ this.restoreObservationMasking();
67285
+ break;
67286
+ }
67287
+ }
67288
+ restoreClear() {
67289
+ this._history = [];
67290
+ this._tokenCount = 0;
67291
+ this.tokenCountCoveredMessageCount = 0;
67292
+ this.openSteps.clear();
67293
+ this.pendingToolResultIds.clear();
67294
+ this.deferredMessages = [];
67295
+ this.toolCallInfo.clear();
67296
+ this.agent.injection.onContextClear();
67297
+ this.agent.emitStatusUpdated();
67298
+ }
67299
+ restoreApplyCompaction(record) {
67300
+ const compactedCount = record.compactedCount;
67301
+ const summary = record.summary;
67302
+ const tokensAfter = record.tokensAfter;
67303
+ this._history = [{
67304
+ role: "assistant",
67305
+ content: [{
67306
+ type: "text",
67307
+ text: summary
67308
+ }],
67309
+ toolCalls: [],
67310
+ origin: { kind: "compaction_summary" }
67311
+ }, ...this._history.slice(compactedCount)];
67312
+ this.openSteps.clear();
67313
+ this.flushDeferredMessagesIfToolExchangeClosed();
67314
+ this._tokenCount = tokensAfter;
67315
+ this.tokenCountCoveredMessageCount = this._history.length;
67316
+ this.agent.injection.onContextCompacted(compactedCount);
67317
+ this.agent.emitStatusUpdated();
67318
+ }
67319
+ restoreMarkLastUserPromptBlocked(record) {
67320
+ const hookEvent = record.hookEvent;
67321
+ for (let i = this._history.length - 1; i >= 0; i--) {
67322
+ const message = this._history[i];
67323
+ if (message?.role !== "user" || message.origin?.kind !== "user") continue;
67324
+ this._history[i] = {
67325
+ ...message,
67326
+ origin: {
67327
+ ...message.origin,
67328
+ blockedByHook: hookEvent
67329
+ }
67330
+ };
67331
+ return;
67332
+ }
67333
+ }
67334
+ async restoreAppendLoopEvent(record) {
67335
+ await this.appendLoopEvent(record.event);
67336
+ }
67337
+ restoreObservationMasking() {
67338
+ const maxContextSize = this.agent.config.modelCapabilities.max_context_tokens;
67339
+ const { history } = applyObservationMasking(this._history, maxContextSize, this.toolCallInfo);
67340
+ this._history = history;
67341
+ this.agent.emitStatusUpdated();
67342
+ }
67126
67343
  };
67127
67344
  function toolResultOutputForModel(result) {
67128
67345
  const output = result.output;
@@ -67563,13 +67780,16 @@ var DirectoryTreeInjector = class extends DynamicInjector {
67563
67780
  injectionVariant = "directory_tree";
67564
67781
  lastTree;
67565
67782
  hasInjected = false;
67783
+ capturedTimestamp;
67566
67784
  async getInjection() {
67567
67785
  const kaos = this.agent.runtime.kaos;
67568
- const tree = await buildTree(kaos, this.agent.config.cwd || kaos.getcwd());
67786
+ const workDir = this.agent.config.cwd || kaos.getcwd();
67787
+ const tree = await buildTree(kaos, workDir);
67569
67788
  if (this.hasInjected && tree === this.lastTree) return;
67570
67789
  this.lastTree = tree;
67571
67790
  this.hasInjected = true;
67572
- return tree;
67791
+ if (this.capturedTimestamp === void 0) this.capturedTimestamp = (/* @__PURE__ */ new Date()).toISOString();
67792
+ 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.`;
67573
67793
  }
67574
67794
  };
67575
67795
  async function buildTree(kaos, workDir) {
@@ -70181,16 +70401,20 @@ var PermissionManager = class {
70181
70401
  block: true,
70182
70402
  reason: this.formatMessage(name, matchedRule?.reason)
70183
70403
  };
70184
- const policyResult = await this.evaluatePolicies(context, matchedRule);
70185
- if (policyResult !== void 0) return this.permissionPolicyResultToPrepare(policyResult, context);
70186
70404
  if (mode === "auto") {
70405
+ const policyResult = await this.evaluatePolicies(context, matchedRule);
70406
+ if (policyResult !== void 0) return this.permissionPolicyResultToPrepare(policyResult, context);
70187
70407
  if (this.wouldAskInManualMode(name, args)) this.trackToolApproved(name, "afk");
70188
70408
  return;
70189
70409
  }
70190
70410
  if (mode === "yolo") {
70411
+ const policyResult = await this.evaluatePolicies(context, matchedRule);
70412
+ if (policyResult !== void 0) return this.permissionPolicyResultToPrepare(policyResult, context);
70191
70413
  if (this.wouldAskInManualMode(name, args)) this.trackToolApproved(name, "yolo");
70192
70414
  return;
70193
70415
  }
70416
+ const policyResult = await this.evaluatePolicies(context, matchedRule);
70417
+ if (policyResult !== void 0) return this.permissionPolicyResultToPrepare(policyResult, context);
70194
70418
  if (decision === "allow") {
70195
70419
  if (matchedRule?.scope === "session-runtime") this.trackToolApproved(name, "auto_session", "session");
70196
70420
  return;
@@ -70309,6 +70533,16 @@ var PermissionManager = class {
70309
70533
  if (scope !== void 0) properties["scope"] = scope;
70310
70534
  this.agent.telemetry.track("tool_approved", properties);
70311
70535
  }
70536
+ restoreRecord(record) {
70537
+ switch (record.type) {
70538
+ case "permission.set_mode":
70539
+ this.setMode(record.mode);
70540
+ break;
70541
+ case "permission.record_approval_result":
70542
+ this.recordApprovalResult(record);
70543
+ break;
70544
+ }
70545
+ }
70312
70546
  };
70313
70547
  function approvalTelemetryMode(mode) {
70314
70548
  return mode === "auto" ? "afk" : mode;
@@ -70500,80 +70734,12 @@ function parseRecordLine(line, lineNumber, filePath, allowTruncated) {
70500
70734
  }
70501
70735
  //#endregion
70502
70736
  //#region ../../packages/agent-core/src/agent/records/index.ts
70503
- async function restoreAgentRecord(agent, input) {
70504
- switch (input.type) {
70505
- case "metadata": return;
70506
- case "turn.prompt":
70507
- agent.turn.restorePrompt();
70508
- return;
70509
- case "turn.steer":
70510
- agent.turn.restoreSteer(input.input, input.origin);
70511
- return;
70512
- case "turn.cancel":
70513
- agent.turn.cancel(input.turnId);
70514
- return;
70515
- case "background.stop": return;
70516
- case "config.update":
70517
- agent.config.update(input);
70518
- return;
70519
- case "permission.set_mode":
70520
- agent.permission.setMode(input.mode);
70521
- return;
70522
- case "permission.record_approval_result":
70523
- agent.permission.recordApprovalResult(input);
70524
- return;
70525
- case "usage.record":
70526
- agent.usage.record(input.model, input.usage, "session");
70527
- return;
70528
- case "full_compaction.begin":
70529
- agent.fullCompaction.begin(input);
70530
- return;
70531
- case "full_compaction.cancel":
70532
- agent.fullCompaction.cancel();
70533
- return;
70534
- case "full_compaction.complete":
70535
- agent.fullCompaction.complete(input);
70536
- return;
70537
- case "plan_mode.enter":
70538
- case "plan_mode.cancel":
70539
- case "plan_mode.exit": return;
70540
- case "context.append_message":
70541
- agent.context.appendMessage(input.message);
70542
- return;
70543
- case "context.mark_last_user_prompt_blocked":
70544
- agent.context.markLastUserPromptBlocked(input.hookEvent);
70545
- return;
70546
- case "context.append_loop_event":
70547
- await agent.context.appendLoopEvent(input.event);
70548
- return;
70549
- case "context.clear":
70550
- agent.context.clear();
70551
- return;
70552
- case "context.apply_compaction":
70553
- agent.context.applyCompaction(input);
70554
- return;
70555
- case "context.observation_masking":
70556
- agent.context.applyObservationMasking();
70557
- return;
70558
- case "tools.register_user_tool":
70559
- agent.tools.registerUserTool(input);
70560
- return;
70561
- case "tools.unregister_user_tool":
70562
- agent.tools.unregisterUserTool(input.name);
70563
- return;
70564
- case "tools.set_active_tools":
70565
- agent.tools.setActiveTools(input.names);
70566
- return;
70567
- case "tools.update_store":
70568
- agent.tools.updateStore(input.key, input.value);
70569
- return;
70570
- }
70571
- }
70572
70737
  var AgentRecords = class {
70573
70738
  agent;
70574
70739
  persistence;
70575
70740
  _restoring = false;
70576
70741
  metadataInitialized = false;
70742
+ handlers = {};
70577
70743
  constructor(agent, persistence) {
70578
70744
  this.agent = agent;
70579
70745
  this.persistence = persistence;
@@ -70581,6 +70747,9 @@ var AgentRecords = class {
70581
70747
  get restoring() {
70582
70748
  return this._restoring;
70583
70749
  }
70750
+ registerHandlers(handlers) {
70751
+ this.handlers = { ...handlers };
70752
+ }
70584
70753
  logRecord(record) {
70585
70754
  if (this._restoring) return;
70586
70755
  const stamped = record.time !== void 0 ? record : {
@@ -70601,11 +70770,30 @@ var AgentRecords = class {
70601
70770
  restore(record) {
70602
70771
  this._restoring = true;
70603
70772
  try {
70604
- restoreAgentRecord(this.agent, record);
70773
+ this.routeToHandler(record);
70605
70774
  } finally {
70606
70775
  this._restoring = false;
70607
70776
  }
70608
70777
  }
70778
+ routeToHandler(record) {
70779
+ const handlerKey = this.getHandlerKey(record.type);
70780
+ if (handlerKey === null || this.handlers[handlerKey] === void 0) return;
70781
+ this.handlers[handlerKey].restoreRecord(record);
70782
+ }
70783
+ getHandlerKey(recordType) {
70784
+ if (recordType === "metadata") return null;
70785
+ return {
70786
+ context: "context",
70787
+ config: "config",
70788
+ turn: "turn",
70789
+ permission: "permission",
70790
+ tools: "tools",
70791
+ usage: "usage",
70792
+ background: "background",
70793
+ full_compaction: "fullCompaction",
70794
+ plan_mode: "planMode"
70795
+ }[recordType.split(".")[0]] ?? null;
70796
+ }
70609
70797
  async replay() {
70610
70798
  if (!this.persistence) throw new Error("No persistence provided for AgentRecords");
70611
70799
  let migrations = [];
@@ -82867,11 +83055,87 @@ var ToolManager = class {
82867
83055
  return (input) => withProviderRequestAuth(resolveAuth, (auth) => uploadVideo(input, { auth }));
82868
83056
  }
82869
83057
  get loopTools() {
83058
+ const builtinNames = [...this.builtinTools.keys()].filter((name) => this.enabledTools.has(name)).sort();
83059
+ const userNames = [...this.userTools.keys()].filter((name) => this.enabledTools.has(name)).sort();
82870
83060
  const mcpNames = [...this.mcpTools.keys()].filter((name) => this.isMcpToolEnabled(name));
82871
- return uniq([...this.enabledTools, ...mcpNames]).toSorted((a, b) => a.localeCompare(b)).map((name) => this.userTools.get(name) ?? this.mcpTools.get(name)?.tool ?? this.builtinTools.get(name)).filter((tool) => !!tool);
83061
+ return [
83062
+ ...builtinNames.map((name) => this.builtinTools.get(name)),
83063
+ ...userNames.map((name) => this.userTools.get(name)),
83064
+ ...mcpNames.map((name) => this.mcpTools.get(name)?.tool)
83065
+ ].filter((tool) => !!tool);
83066
+ }
83067
+ restoreRecord(record) {
83068
+ switch (record.type) {
83069
+ case "tools.register_user_tool":
83070
+ this.registerUserTool(record);
83071
+ break;
83072
+ case "tools.unregister_user_tool":
83073
+ this.unregisterUserTool(record.name);
83074
+ break;
83075
+ case "tools.set_active_tools":
83076
+ this.setActiveTools(record.names);
83077
+ break;
83078
+ case "tools.update_store":
83079
+ this.updateStore(record.key, record.value);
83080
+ break;
83081
+ }
82872
83082
  }
82873
83083
  };
82874
83084
  //#endregion
83085
+ //#region ../../packages/agent-core/src/agent/cache-staking/index.ts
83086
+ const DEFAULT_SIZE_THRESHOLD = 2e3;
83087
+ /**
83088
+ * Apply cache staking hints to a message array based on turn boundaries.
83089
+ *
83090
+ * - **Stake 3**: Tags the last assistant message of the previous turn with
83091
+ * `cacheHint.isLastTurnEnd = true`.
83092
+ * - **Stake 4** (conditional): Tags the largest content block in the current
83093
+ * turn that exceeds `sizeThreshold` with `cacheHint.isSuddenLargeContext = true`.
83094
+ *
83095
+ * Returns a new array with shallow-copied messages (original messages are
83096
+ * not mutated).
83097
+ */
83098
+ function applyCacheStaking(messages, context) {
83099
+ const { previousTurnMessageCount, sizeThreshold = DEFAULT_SIZE_THRESHOLD } = context;
83100
+ if (previousTurnMessageCount <= 0 || messages.length === 0) return messages;
83101
+ const result = messages.map((msg) => ({ ...msg }));
83102
+ const lastTurnIndex = previousTurnMessageCount - 1;
83103
+ if (lastTurnIndex < result.length) {
83104
+ const lastTurnMsg = result[lastTurnIndex];
83105
+ if (lastTurnMsg.role === "assistant") {
83106
+ const existingHint = lastTurnMsg.cacheHint ?? {};
83107
+ result[lastTurnIndex] = {
83108
+ ...lastTurnMsg,
83109
+ cacheHint: {
83110
+ ...existingHint,
83111
+ isLastTurnEnd: true
83112
+ }
83113
+ };
83114
+ }
83115
+ }
83116
+ let largestIndex = -1;
83117
+ let largestSize = 0;
83118
+ for (let i = previousTurnMessageCount; i < result.length; i++) {
83119
+ const contentLength = result[i].content.filter((p) => p.type === "text").reduce((sum, p) => sum + p.text.length, 0);
83120
+ if (contentLength >= sizeThreshold && contentLength > largestSize) {
83121
+ largestSize = contentLength;
83122
+ largestIndex = i;
83123
+ }
83124
+ }
83125
+ if (largestIndex >= 0) {
83126
+ const target = result[largestIndex];
83127
+ const existingHint = target.cacheHint ?? {};
83128
+ result[largestIndex] = {
83129
+ ...target,
83130
+ cacheHint: {
83131
+ ...existingHint,
83132
+ isSuddenLargeContext: true
83133
+ }
83134
+ };
83135
+ }
83136
+ return result;
83137
+ }
83138
+ //#endregion
82875
83139
  //#region ../../packages/agent-core/src/agent/turn/canonical-args.ts
82876
83140
  /**
82877
83141
  * JSON canonicalization used by tool-call telemetry and dedup.
@@ -82893,6 +83157,231 @@ function isPlainRecord(value) {
82893
83157
  return proto === Object.prototype || proto === null;
82894
83158
  }
82895
83159
  //#endregion
83160
+ //#region ../../packages/agent-core/src/prompt-plan/builder.ts
83161
+ /**
83162
+ * Cache boundary marker used to split the system prompt into cacheable blocks.
83163
+ *
83164
+ * @deprecated This marker is deprecated in favor of implicit boundaries based on section headers.
83165
+ * It is still supported for backward compatibility.
83166
+ */
83167
+ const CACHE_BOUNDARY_MARKER = "__CACHE_BOUNDARY__";
83168
+ /**
83169
+ * Section headers that define implicit cache boundaries.
83170
+ *
83171
+ * These headers mark natural breaks in the system prompt where cache boundaries should be placed:
83172
+ * - "# Project Information" marks the start of project-specific content
83173
+ * - "# Skills" marks the start of session-specific skills listing
83174
+ */
83175
+ const IMPLICIT_BOUNDARY_HEADERS = ["# Project Information", "# Skills"];
83176
+ /**
83177
+ * Block names by position.
83178
+ *
83179
+ * - First block (before first marker): 'base'
83180
+ * - Last block (after last marker): 'sessionContext'
83181
+ * - Intermediate blocks: Sequential names from 'projectInstructions', 'skillsListing', etc.
83182
+ */
83183
+ const BLOCK_NAMES = [
83184
+ "base",
83185
+ "projectInstructions",
83186
+ "skillsListing",
83187
+ "sessionContext"
83188
+ ];
83189
+ /**
83190
+ * Get the cache scope for a block by its position.
83191
+ *
83192
+ * @param position - The block position (0-indexed)
83193
+ * @param totalBlocks - Total number of blocks
83194
+ * @returns The default cache scope for this position
83195
+ */
83196
+ function getDefaultScopeForPosition(position, totalBlocks) {
83197
+ if (position === 0) return "global";
83198
+ if (position === totalBlocks - 1) return "session";
83199
+ if (position === 1) return "project";
83200
+ return "session";
83201
+ }
83202
+ /**
83203
+ * Filter cache scope based on provider's supported scopes.
83204
+ *
83205
+ * @param scope - The desired cache scope
83206
+ * @param capability - The provider's cache capability
83207
+ * @returns The filtered scope (or 'none' if not supported)
83208
+ */
83209
+ function filterScopeByCapability(scope, capability) {
83210
+ if (capability.strategy === "none") return "none";
83211
+ if (capability.supportedScopes === void 0) return scope;
83212
+ if (capability.supportedScopes.includes(scope)) return scope;
83213
+ return "none";
83214
+ }
83215
+ /**
83216
+ * Find implicit boundary positions in the system prompt.
83217
+ *
83218
+ * Searches for section headers that mark natural cache boundaries.
83219
+ * Returns sorted indices of where each boundary header starts.
83220
+ *
83221
+ * @param prompt - The system prompt to search
83222
+ * @returns Array of character positions where implicit boundaries occur
83223
+ */
83224
+ function findImplicitBoundaries(prompt) {
83225
+ const boundaries = [];
83226
+ for (const header of IMPLICIT_BOUNDARY_HEADERS) {
83227
+ const index = prompt.indexOf(header);
83228
+ if (index !== -1) boundaries.push(index);
83229
+ }
83230
+ return boundaries.sort((a, b) => a - b);
83231
+ }
83232
+ /**
83233
+ * Split prompt by implicit boundaries into blocks.
83234
+ *
83235
+ * Creates blocks based on the position of section headers that mark natural boundaries.
83236
+ * The block before the first header is the base block.
83237
+ * Blocks between headers are intermediate blocks.
83238
+ * The block after the last header is the session context block.
83239
+ *
83240
+ * @param prompt - The system prompt to split
83241
+ * @param boundaryPositions - Sorted array of boundary positions
83242
+ * @returns Array of text blocks
83243
+ */
83244
+ function splitByImplicitBoundaries(prompt, boundaryPositions) {
83245
+ if (boundaryPositions.length === 0) return [prompt];
83246
+ const blocks = [];
83247
+ let previousPosition = 0;
83248
+ for (const position of boundaryPositions) {
83249
+ blocks.push(prompt.slice(previousPosition, position));
83250
+ previousPosition = position;
83251
+ }
83252
+ blocks.push(prompt.slice(previousPosition));
83253
+ return blocks;
83254
+ }
83255
+ /**
83256
+ * Detect if a prompt contains implicit cache boundaries.
83257
+ *
83258
+ * A prompt has implicit boundaries if it contains any of the known boundary headers.
83259
+ *
83260
+ * @param prompt - The system prompt to check
83261
+ * @returns true if implicit boundaries are detected
83262
+ */
83263
+ function hasImplicitBoundaries(prompt) {
83264
+ return IMPLICIT_BOUNDARY_HEADERS.some((header) => prompt.includes(header));
83265
+ }
83266
+ /**
83267
+ * Build a prompt plan from a rendered system prompt and provider cache capability.
83268
+ *
83269
+ * This function parses cache boundary markers from the system prompt and creates
83270
+ * a structured plan with named blocks, each with an appropriate cache scope.
83271
+ *
83272
+ * @param renderedSystemPrompt - The fully rendered system prompt (may contain `__CACHE_BOUNDARY__` markers)
83273
+ * @param providerCacheCapability - The provider's cache capability (for scope filtering)
83274
+ * @returns A prompt plan with cacheable blocks
83275
+ *
83276
+ * @example
83277
+ * ```ts
83278
+ * const prompt = `Base instructions
83279
+ * __CACHE_BOUNDARY__
83280
+ * Session context`;
83281
+ *
83282
+ * const capability = {
83283
+ * strategy: 'explicit-block',
83284
+ * supportedScopes: ['global', 'session'],
83285
+ * };
83286
+ *
83287
+ * const plan = buildPromptPlan(prompt, capability);
83288
+ * // {
83289
+ * // blocks: [
83290
+ * // { name: 'base', text: 'Base instructions\n', cacheScope: 'global' },
83291
+ * // { name: 'sessionContext', text: 'Session context', cacheScope: 'session' },
83292
+ * // ],
83293
+ * // }
83294
+ * ```
83295
+ */
83296
+ /**
83297
+ * Normalize block text by handling newlines around cache boundaries.
83298
+ *
83299
+ * After splitting by `__CACHE_BOUNDARY__`, the pattern is typically:
83300
+ * - `[text]\n__CACHE_BOUNDARY__\n[next text]`
83301
+ *
83302
+ * Rules:
83303
+ * - First block: Keep as-is (preserves any trailing newline before marker)
83304
+ * - Middle blocks: Keep the newline that was between markers
83305
+ * - Last block: Trim leading newline (the one immediately after the last marker)
83306
+ *
83307
+ * @param text - The raw block text after splitting
83308
+ * @param index - The block index
83309
+ * @param totalBlocks - Total number of blocks
83310
+ * @returns Normalized block text
83311
+ */
83312
+ function normalizeBlockText(text, index, totalBlocks) {
83313
+ if (index === 0) return text;
83314
+ if (index === totalBlocks - 1) {
83315
+ if (text.startsWith("\n")) return text.slice(1);
83316
+ return text;
83317
+ }
83318
+ return text;
83319
+ }
83320
+ function buildPromptPlan(renderedSystemPrompt, providerCacheCapability) {
83321
+ const parts = renderedSystemPrompt.split(CACHE_BOUNDARY_MARKER);
83322
+ if (parts.length > 1) return createPlanFromParts(parts, providerCacheCapability);
83323
+ if (hasImplicitBoundaries(renderedSystemPrompt)) return createPlanFromParts(splitByImplicitBoundaries(renderedSystemPrompt, findImplicitBoundaries(renderedSystemPrompt)), providerCacheCapability);
83324
+ return { blocks: [{
83325
+ name: "base",
83326
+ text: renderedSystemPrompt,
83327
+ cacheScope: "none"
83328
+ }] };
83329
+ }
83330
+ /**
83331
+ * Create a prompt plan from pre-split parts.
83332
+ *
83333
+ * This is shared logic for both explicit and implicit boundaries.
83334
+ *
83335
+ * @param parts - The pre-split text parts
83336
+ * @param providerCacheCapability - The provider's cache capability
83337
+ * @returns A prompt plan with cacheable blocks
83338
+ */
83339
+ function createPlanFromParts(parts, providerCacheCapability) {
83340
+ const blocks = parts.map((part, index) => {
83341
+ const totalBlocks = parts.length;
83342
+ const filteredScope = filterScopeByCapability(getDefaultScopeForPosition(index, totalBlocks), providerCacheCapability);
83343
+ return {
83344
+ name: getBlockNameForPart(part, index, totalBlocks),
83345
+ text: normalizeBlockText(part, index, totalBlocks),
83346
+ cacheScope: filteredScope
83347
+ };
83348
+ });
83349
+ const maxCacheable = providerCacheCapability.maxCacheableBlocks;
83350
+ if (maxCacheable !== void 0 && maxCacheable > 0) {
83351
+ let cacheableCount = 0;
83352
+ for (const block of blocks) if (block.cacheScope !== "none") {
83353
+ cacheableCount++;
83354
+ if (cacheableCount > maxCacheable) block.cacheScope = "none";
83355
+ }
83356
+ }
83357
+ return { blocks };
83358
+ }
83359
+ /**
83360
+ * Get the block name for a given part, considering its content.
83361
+ *
83362
+ * This is a smarter version of getBlockName that looks at the content
83363
+ * to determine the appropriate name for the last block.
83364
+ *
83365
+ * @param part - The text content of this block
83366
+ * @param position - The block position (0-indexed)
83367
+ * @param totalBlocks - Total number of blocks
83368
+ * @returns The block name
83369
+ */
83370
+ function getBlockNameForPart(part, position, totalBlocks) {
83371
+ if (position === 0) return "base";
83372
+ if (position === totalBlocks - 1) {
83373
+ if (part.includes("# Skills")) return "sessionContext";
83374
+ if (part.includes("# Project Information")) return "projectInstructions";
83375
+ return "sessionContext";
83376
+ }
83377
+ const nameIndex = position - 1 + 1;
83378
+ if (nameIndex < BLOCK_NAMES.length && nameIndex < BLOCK_NAMES.length - 1) {
83379
+ const name = BLOCK_NAMES[nameIndex];
83380
+ if (name !== void 0) return name;
83381
+ }
83382
+ return "sessionContext";
83383
+ }
83384
+ //#endregion
82896
83385
  //#region ../../packages/agent-core/src/agent/turn/kosong-llm.ts
82897
83386
  /**
82898
83387
  * Kosong-backed implementation of the loop `LLM` interface.
@@ -82939,7 +83428,7 @@ var KosongLLM = class {
82939
83428
  systemPrompt: this.systemPrompt,
82940
83429
  tools: params.tools
82941
83430
  });
82942
- const result = await this.generate(effectiveProvider, this.systemPrompt, [...params.tools], [...params.messages], callbacks, generateOptions(params));
83431
+ const result = await this.generate(effectiveProvider, this.systemPrompt, [...params.tools], [...params.messages], callbacks, generateOptions(params, this.systemPrompt, effectiveProvider));
82943
83432
  if (params.onTextPart !== void 0 || params.onThinkPart !== void 0) {
82944
83433
  for (const part of result.message.content) if (part.type === "text" && params.onTextPart !== void 0) await params.onTextPart(part);
82945
83434
  else if (part.type === "think" && params.onThinkPart !== void 0) await params.onThinkPart(part);
@@ -82965,10 +83454,11 @@ var KosongLLM = class {
82965
83454
  ].includes(error.statusCode);
82966
83455
  }
82967
83456
  };
82968
- function generateOptions(params) {
83457
+ function generateOptions(params, systemPrompt, provider) {
83458
+ const promptPlan = buildPromptPlan(systemPrompt, getProviderCacheCapability(provider));
82969
83459
  const options = {
82970
83460
  signal: params.signal,
82971
- cacheBreakpoints: ["__CACHE_BOUNDARY__"]
83461
+ promptPlan
82972
83462
  };
82973
83463
  if (params.requestLogContext !== void 0) return {
82974
83464
  ...options,
@@ -82976,6 +83466,18 @@ function generateOptions(params) {
82976
83466
  };
82977
83467
  return options;
82978
83468
  }
83469
+ /**
83470
+ * Get the cache capability from a provider.
83471
+ *
83472
+ * Safely handles providers that don't implement getCapability or don't have cache.
83473
+ * Returns a default capability with 'none' strategy for non-caching providers.
83474
+ */
83475
+ function getProviderCacheCapability(provider) {
83476
+ if (typeof provider.getCapability !== "function") return { strategy: "none" };
83477
+ const capability = provider.getCapability();
83478
+ if (capability?.cache !== void 0) return capability.cache;
83479
+ return { strategy: "none" };
83480
+ }
82979
83481
  function buildKosongCallbacks(params) {
82980
83482
  const toolCallIdentities = /* @__PURE__ */ new Map();
82981
83483
  const pendingIndexedToolCallDeltas = /* @__PURE__ */ new Map();
@@ -83222,6 +83724,7 @@ var TurnFlow = class {
83222
83724
  agent;
83223
83725
  steerBuffer = [];
83224
83726
  turnId = -1;
83727
+ _previousTurnMessageCount = 0;
83225
83728
  activeTurn = null;
83226
83729
  toolCallStartedAt = /* @__PURE__ */ new Map();
83227
83730
  toolCallDupType = /* @__PURE__ */ new Map();
@@ -83266,6 +83769,7 @@ var TurnFlow = class {
83266
83769
  return null;
83267
83770
  }
83268
83771
  this.turnId += 1;
83772
+ this._previousTurnMessageCount = this.agent.context.messages.length;
83269
83773
  this.currentStep = 0;
83270
83774
  this.stepToolCallKeys.clear();
83271
83775
  this.toolCallDupType.clear();
@@ -83502,7 +84006,10 @@ var TurnFlow = class {
83502
84006
  generate: this.agent.generate,
83503
84007
  completionBudgetConfig
83504
84008
  }),
83505
- buildMessages: () => this.agent.context.messages,
84009
+ buildMessages: () => {
84010
+ const messages = this.agent.context.messages;
84011
+ return applyCacheStaking(messages, { previousTurnMessageCount: this._previousTurnMessageCount });
84012
+ },
83506
84013
  dispatchEvent: this.buildDispatchEvent(turnId),
83507
84014
  tools: this.agent.tools.loopTools,
83508
84015
  log: this.agent.log,
@@ -83700,6 +84207,19 @@ var TurnFlow = class {
83700
84207
  const failure = this.stepFailureByTurn.get(turnId);
83701
84208
  return failure?.reason === "error" && failure.activeStep !== void 0;
83702
84209
  }
84210
+ restoreRecord(record) {
84211
+ switch (record.type) {
84212
+ case "turn.prompt":
84213
+ if (this.activeTurn === "resuming") this.activeTurn = null;
84214
+ this.restorePrompt();
84215
+ break;
84216
+ case "turn.steer":
84217
+ if (this.activeTurn === "resuming") this.activeTurn = null;
84218
+ this.restoreSteer(record.input, record.origin);
84219
+ break;
84220
+ case "turn.cancel": break;
84221
+ }
84222
+ }
83703
84223
  };
83704
84224
  function mapLoopEvent(event, turnId) {
83705
84225
  switch (event.type) {
@@ -83936,10 +84456,12 @@ var UsageRecorder = class {
83936
84456
  const byModel = this.byModelSnapshot();
83937
84457
  const hasByModel = Object.keys(byModel).length > 0;
83938
84458
  const currentTurn = this.currentTurn;
84459
+ const total = hasByModel ? totalUsage(byModel) : void 0;
83939
84460
  return {
83940
84461
  byModel: hasByModel ? byModel : void 0,
83941
- total: hasByModel ? totalUsage(byModel) : void 0,
83942
- currentTurn: currentTurn === void 0 ? void 0 : copyUsage(currentTurn)
84462
+ total,
84463
+ currentTurn: currentTurn === void 0 ? void 0 : copyUsage(currentTurn),
84464
+ cacheHitRate: total !== void 0 ? cacheHitRate(total) : void 0
83943
84465
  };
83944
84466
  }
83945
84467
  status() {
@@ -83950,6 +84472,13 @@ var UsageRecorder = class {
83950
84472
  byModelSnapshot() {
83951
84473
  return Object.fromEntries(Object.entries(this.byModel).map(([model, usage]) => [model, copyUsage(usage)]));
83952
84474
  }
84475
+ restoreRecord(record) {
84476
+ switch (record.type) {
84477
+ case "usage.record":
84478
+ this.record(record.model, record.usage, "session");
84479
+ break;
84480
+ }
84481
+ }
83953
84482
  };
83954
84483
  function totalUsage(byModel) {
83955
84484
  let total;
@@ -84012,6 +84541,15 @@ var Agent = class {
84012
84541
  sessionDir: config.backgroundSessionDir
84013
84542
  });
84014
84543
  this.replayBuilder = new ReplayBuilder(this);
84544
+ this.records.registerHandlers({
84545
+ context: this.context,
84546
+ config: this.config,
84547
+ usage: this.usage,
84548
+ turn: this.turn,
84549
+ permission: this.permission,
84550
+ tools: this.tools,
84551
+ fullCompaction: this.fullCompaction
84552
+ });
84015
84553
  }
84016
84554
  get generate() {
84017
84555
  return async (provider, systemPrompt, tools, history, callbacks, options) => {
@@ -84032,7 +84570,7 @@ var Agent = class {
84032
84570
  }
84033
84571
  logLlmRequest(provider, systemPrompt, tools, history, options) {
84034
84572
  const context = buildLlmRequestContext(options);
84035
- const configMetadata = buildLlmConfigMetadata(provider, this.config.modelAlias, systemPrompt, tools);
84573
+ const configMetadata = buildLlmConfigMetadata(provider, this.config.modelAlias, systemPrompt, tools, options);
84036
84574
  this.logLlmConfigIfChanged(context, configMetadata, buildLlmConfigSignature(configMetadata, systemPrompt, tools));
84037
84575
  this.log.info("llm request", {
84038
84576
  ...context,
@@ -84062,11 +84600,15 @@ var Agent = class {
84062
84600
  this.tools.setActiveTools(profile.tools);
84063
84601
  }
84064
84602
  async resume() {
84065
- const result = await this.records.replay();
84066
- await this.background.loadFromDisk();
84067
- await this.background.reconcile();
84068
- this.turn.finishResume();
84069
- return result;
84603
+ try {
84604
+ const result = await this.records.replay();
84605
+ await this.background.loadFromDisk();
84606
+ await this.background.reconcile();
84607
+ this.turn.finishResume();
84608
+ return result;
84609
+ } catch (error) {
84610
+ return { error: error instanceof Error ? error : new Error(String(error)) };
84611
+ }
84070
84612
  }
84071
84613
  get rpcMethods() {
84072
84614
  return {
@@ -84222,8 +84764,8 @@ function buildLlmRequestMetadata(systemPrompt, tools, history) {
84222
84764
  if (partialMessageCount > 0) metadata.partialMessageCount = partialMessageCount;
84223
84765
  return metadata;
84224
84766
  }
84225
- function buildLlmConfigMetadata(provider, modelAlias, systemPrompt, tools) {
84226
- return {
84767
+ function buildLlmConfigMetadata(provider, modelAlias, systemPrompt, tools, options) {
84768
+ const metadata = {
84227
84769
  provider: provider.name,
84228
84770
  model: provider.modelName,
84229
84771
  modelAlias,
@@ -84231,6 +84773,30 @@ function buildLlmConfigMetadata(provider, modelAlias, systemPrompt, tools) {
84231
84773
  systemPromptChars: systemPrompt.length,
84232
84774
  toolCount: tools.length
84233
84775
  };
84776
+ const providerCacheStrategy = getProviderCacheStrategy(provider);
84777
+ if (providerCacheStrategy !== void 0) metadata.providerCacheStrategy = providerCacheStrategy;
84778
+ const promptPlan = options?.promptPlan;
84779
+ if (promptPlan !== void 0 && promptPlan.blocks.length > 0) metadata.cacheBlockHashes = extractCacheBlockHashes(promptPlan);
84780
+ return metadata;
84781
+ }
84782
+ /**
84783
+ * Get the cache strategy from a provider's capability.
84784
+ *
84785
+ * Safely handles providers that don't implement getCapability or don't have cache.
84786
+ */
84787
+ function getProviderCacheStrategy(provider) {
84788
+ if (typeof provider.getCapability !== "function") return;
84789
+ return provider.getCapability()?.cache?.strategy;
84790
+ }
84791
+ /**
84792
+ * Extract cache block hashes from a PromptPlan.
84793
+ *
84794
+ * Returns a Record mapping block names to their SHA256 hashes.
84795
+ */
84796
+ function extractCacheBlockHashes(promptPlan) {
84797
+ const hashes = {};
84798
+ for (const block of promptPlan.blocks) hashes[block.name] = fingerprint(block.text);
84799
+ return hashes;
84234
84800
  }
84235
84801
  function buildLlmConfigSignature(metadata, systemPrompt, tools) {
84236
84802
  const toolsForSignature = tools.map(({ name, description, parameters }) => ({
@@ -84276,6 +84842,7 @@ const OAuthRefSchema = z.object({
84276
84842
  key: z.string().min(1)
84277
84843
  });
84278
84844
  const StringRecordSchema = z.record(z.string(), z.string());
84845
+ const ExtraBodySchema = z.record(z.string(), z.unknown());
84279
84846
  const ProviderConfigSchema = z.object({
84280
84847
  type: ProviderTypeSchema,
84281
84848
  apiKey: z.string().optional(),
@@ -84284,7 +84851,8 @@ const ProviderConfigSchema = z.object({
84284
84851
  thinkingEffortKey: z.string().optional(),
84285
84852
  oauth: OAuthRefSchema.optional(),
84286
84853
  env: StringRecordSchema.optional(),
84287
- customHeaders: StringRecordSchema.optional()
84854
+ customHeaders: StringRecordSchema.optional(),
84855
+ extraBody: ExtraBodySchema.optional()
84288
84856
  });
84289
84857
  const ModelAliasSchema = z.object({
84290
84858
  provider: z.string(),
@@ -88750,11 +89318,19 @@ var Session$1 = class {
88750
89318
  const { agents } = await this.readMetadata();
88751
89319
  this.agents.clear();
88752
89320
  let warning;
89321
+ const failedAgents = [];
88753
89322
  const resumeTasks = Object.keys(agents).map(async (id) => {
88754
89323
  const result = await this.ensureResumeAgentInstantiated(id, agents).resume();
88755
89324
  if (result.warning !== void 0 && warning === void 0) warning = result.warning;
89325
+ if (result.error !== void 0) failedAgents.push({
89326
+ id,
89327
+ error: result.error
89328
+ });
88756
89329
  });
88757
89330
  await Promise.all(resumeTasks);
89331
+ const mainAgentFailure = failedAgents.find(({ id }) => id === "main");
89332
+ if (mainAgentFailure !== void 0) throw mainAgentFailure.error;
89333
+ for (const { id, error } of failedAgents) this.log.warn(`Sub agent "${id}" failed to resume: ${error.message}`);
88758
89334
  const resumeWarning = warning;
88759
89335
  const main = this.agents.get("main");
88760
89336
  const profile = DEFAULT_AGENT_PROFILES["agent"];
@@ -104097,13 +104673,17 @@ function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSiz
104097
104673
  ...byfRequestHeaders,
104098
104674
  ...provider.customHeaders
104099
104675
  };
104676
+ const generationKwargs = {
104677
+ prompt_cache_key: promptCacheKey,
104678
+ extra_body: provider.extraBody
104679
+ };
104100
104680
  if (Object.keys(defaultHeaders).length === 0) return {
104101
104681
  type: "openai-completions",
104102
104682
  model,
104103
104683
  baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
104104
104684
  reasoningKey,
104105
104685
  thinkingEffortKey: provider.thinkingEffortKey,
104106
- generationKwargs: { prompt_cache_key: promptCacheKey },
104686
+ generationKwargs,
104107
104687
  apiKey: providerApiKey(provider)
104108
104688
  };
104109
104689
  return {
@@ -104112,7 +104692,7 @@ function toKosongProviderConfig(provider, model, byfRequestHeaders, maxOutputSiz
104112
104692
  baseUrl: providerValue(provider.baseUrl, provider.env, "BYF_BASE_URL"),
104113
104693
  reasoningKey,
104114
104694
  thinkingEffortKey: provider.thinkingEffortKey,
104115
- generationKwargs: { prompt_cache_key: promptCacheKey },
104695
+ generationKwargs,
104116
104696
  defaultHeaders,
104117
104697
  apiKey: providerApiKey(provider)
104118
104698
  };
@@ -105949,6 +106529,9 @@ function capabilityToStrings(capability) {
105949
106529
  if (capability.video_in) caps.push("video_in");
105950
106530
  if (capability.audio_in) caps.push("audio_in");
105951
106531
  if (capability.thinking) caps.push("thinking");
106532
+ if (capability.thinking_effort) caps.push("thinking_effort");
106533
+ if (capability.thinking_xhigh) caps.push("thinking_xhigh");
106534
+ if (capability.thinking_max) caps.push("thinking_max");
105952
106535
  if (capability.tool_use) caps.push("tool_use");
105953
106536
  return caps.length > 0 ? caps : void 0;
105954
106537
  }
@@ -106004,6 +106587,40 @@ function applyCatalogProvider(config, options) {
106004
106587
  config.defaultThinking = options.thinking;
106005
106588
  return { defaultModel };
106006
106589
  }
106590
+ /**
106591
+ * Tests whether `candidate` is a prefix of `modelId` followed by either
106592
+ * end-of-string or a `-` separator. This matches `gpt-5.5` against
106593
+ * `gpt-5.5-2025-06-01` but not against `gpt-5.5-turbo` (different segment).
106594
+ */
106595
+ function catalogIdMatchesModelId(candidate, modelId) {
106596
+ if (modelId === candidate) return true;
106597
+ if (modelId.startsWith(candidate) && modelId[candidate.length] === "-") return true;
106598
+ return false;
106599
+ }
106600
+ /**
106601
+ * Searches all providers in the catalog for a model whose ID matches
106602
+ * `modelId` (prefix + separator boundary). Returns the first match.
106603
+ */
106604
+ function findCatalogModel(catalog, modelId) {
106605
+ for (const entry of Object.values(catalog)) {
106606
+ const models = catalogProviderModels(entry);
106607
+ for (const model of models) if (catalogIdMatchesModelId(model.id, modelId)) return model;
106608
+ }
106609
+ }
106610
+ /**
106611
+ * Merges catalog metadata (priority) with provider-supplied values (fallback).
106612
+ * Catalog provides: capabilities, maxContextSize, maxOutputSize, reasoningKey.
106613
+ * Provider provides: displayName (user chose this provider, keep its naming).
106614
+ */
106615
+ function enrichWithCatalog(providerModel, catalogModel) {
106616
+ return {
106617
+ maxContextSize: catalogModel.capability.max_context_tokens || providerModel.contextLength,
106618
+ maxOutputSize: catalogModel.maxOutputSize,
106619
+ capabilities: capabilityToStrings(catalogModel.capability),
106620
+ displayName: providerModel.displayName,
106621
+ reasoningKey: catalogModel.reasoningKey
106622
+ };
106623
+ }
106007
106624
  //#endregion
106008
106625
  //#region ../../packages/oauth/src/utils.ts
106009
106626
  function isRecord$2(value) {
@@ -106363,6 +106980,27 @@ function validateOptions(opts) {
106363
106980
  };
106364
106981
  }
106365
106982
  //#endregion
106983
+ //#region src/tui/utils/dead-terminal.ts
106984
+ /**
106985
+ * Detects errors that mean the controlling terminal (stdout/stderr pty) is
106986
+ * effectively gone — for example after the parent shell crashed, the tmux
106987
+ * server vanished, or an SSH connection dropped without delivering SIGHUP.
106988
+ *
106989
+ * Continuing to write to a dead terminal would re-fire the same error on every
106990
+ * render tick and pin a CPU core. Callers should respond by skipping any
106991
+ * cleanup that touches stdout/stderr and exiting immediately.
106992
+ */
106993
+ const DEAD_TERMINAL_ERROR_CODES = new Set([
106994
+ "EIO",
106995
+ "EPIPE",
106996
+ "ENOTCONN"
106997
+ ]);
106998
+ function isDeadTerminalError(error) {
106999
+ if (error === null || typeof error !== "object" || !("code" in error)) return false;
107000
+ const code = error.code;
107001
+ return code !== void 0 && DEAD_TERMINAL_ERROR_CODES.has(code);
107002
+ }
107003
+ //#endregion
106366
107004
  //#region src/cli/run-prompt.ts
106367
107005
  const PROMPT_UI_MODE = "print";
106368
107006
  const PROMPT_MAIN_AGENT_ID = "main";
@@ -106480,6 +107118,9 @@ function installHeadlessHandlers(session) {
106480
107118
  }
106481
107119
  function installPromptTerminationCleanup(promptProcess, cleanup) {
106482
107120
  let terminating = false;
107121
+ const emergencyExit = () => {
107122
+ promptProcess.exit(129);
107123
+ };
106483
107124
  const exitAfterCleanup = async (signal) => {
106484
107125
  if (terminating) return;
106485
107126
  terminating = true;
@@ -106493,9 +107134,22 @@ function installPromptTerminationCleanup(promptProcess, cleanup) {
106493
107134
  const onSigterm = () => exitAfterCleanup("SIGTERM");
106494
107135
  promptProcess.once("SIGINT", onSigint);
106495
107136
  promptProcess.once("SIGTERM", onSigterm);
107137
+ let onSighup;
107138
+ if (process.platform !== "win32") {
107139
+ onSighup = () => emergencyExit();
107140
+ process.prependListener("SIGHUP", onSighup);
107141
+ }
107142
+ const terminalErrorHandler = (error) => {
107143
+ if (isDeadTerminalError(error)) emergencyExit();
107144
+ };
107145
+ process.stdout.on("error", terminalErrorHandler);
107146
+ process.stderr.on("error", terminalErrorHandler);
106496
107147
  return () => {
106497
107148
  promptProcess.off("SIGINT", onSigint);
106498
107149
  promptProcess.off("SIGTERM", onSigterm);
107150
+ if (onSighup !== void 0) process.off("SIGHUP", onSighup);
107151
+ process.stdout.off("error", terminalErrorHandler);
107152
+ process.stderr.off("error", terminalErrorHandler);
106499
107153
  };
106500
107154
  }
106501
107155
  function signalExitCode(signal) {
@@ -116119,27 +116773,6 @@ function hasDispose(value) {
116119
116773
  return typeof value === "object" && value !== null && "dispose" in value && typeof value.dispose === "function";
116120
116774
  }
116121
116775
  //#endregion
116122
- //#region src/tui/utils/dead-terminal.ts
116123
- /**
116124
- * Detects errors that mean the controlling terminal (stdout/stderr pty) is
116125
- * effectively gone — for example after the parent shell crashed, the tmux
116126
- * server vanished, or an SSH connection dropped without delivering SIGHUP.
116127
- *
116128
- * Continuing to write to a dead terminal would re-fire the same error on every
116129
- * render tick and pin a CPU core. Callers should respond by skipping any
116130
- * cleanup that touches stdout/stderr and exiting immediately.
116131
- */
116132
- const DEAD_TERMINAL_ERROR_CODES = new Set([
116133
- "EIO",
116134
- "EPIPE",
116135
- "ENOTCONN"
116136
- ]);
116137
- function isDeadTerminalError(error) {
116138
- if (error === null || typeof error !== "object" || !("code" in error)) return false;
116139
- const code = error.code;
116140
- return code !== void 0 && DEAD_TERMINAL_ERROR_CODES.has(code);
116141
- }
116142
- //#endregion
116143
116776
  //#region src/tui/utils/errors.ts
116144
116777
  function isAbortMessage(message) {
116145
116778
  return message === "Aborted" || message.endsWith(": Aborted");
@@ -117558,7 +118191,6 @@ var LoginFlow = class {
117558
118191
  const baseUrl = await promptTextInput(dialogHost, colors, {
117559
118192
  title: "Base URL",
117560
118193
  subtitle: "The OpenAI-compatible API endpoint",
117561
- initialValue: "https://api.openai.com/v1",
117562
118194
  placeholder: "https://api.openai.com/v1"
117563
118195
  });
117564
118196
  if (baseUrl === void 0) return;
@@ -117585,20 +118217,29 @@ var LoginFlow = class {
117585
118217
  this.deps.showStatus("No models found at this endpoint. Enter model ID manually.");
117586
118218
  return this.handleManualModelEntry(name, baseUrl, apiKey);
117587
118219
  }
118220
+ const catalog = await this.fetchCatalogWithFallback();
118221
+ const enriched = {};
117588
118222
  const modelDict = {};
117589
- for (const m of models) modelDict[`${name}/${m.id}`] = {
117590
- provider: name,
117591
- model: m.id,
117592
- maxContextSize: m.contextLength,
117593
- capabilities: capabilitiesForModel(m),
117594
- displayName: m.displayName
117595
- };
118223
+ for (const m of models) {
118224
+ const aliasKey = `${name}/${m.id}`;
118225
+ const enrichedData = catalog !== void 0 ? this.enrichModelFromCatalog(m, catalog) : void 0;
118226
+ if (enrichedData !== void 0) enriched[aliasKey] = enrichedData;
118227
+ modelDict[aliasKey] = {
118228
+ provider: name,
118229
+ model: m.id,
118230
+ maxContextSize: enrichedData?.maxContextSize ?? m.contextLength,
118231
+ capabilities: enrichedData?.capabilities ?? capabilitiesForModel(m),
118232
+ displayName: m.displayName,
118233
+ reasoningKey: enrichedData?.reasoningKey,
118234
+ maxOutputSize: enrichedData?.maxOutputSize
118235
+ };
118236
+ }
117596
118237
  const selection = await promptModelSelector(dialogHost, colors, modelDict);
117597
118238
  if (selection === void 0) return;
117598
118239
  const selectedId = selection.alias.split("/").slice(1).join("/");
117599
118240
  const selectedModel = models.find((m) => m.id === selectedId);
117600
118241
  if (selectedModel === void 0) return;
117601
- await this.applyConfig(name, baseUrl, apiKey, models, selectedModel, selection.thinkingEffort !== "off");
118242
+ await this.applyConfig(name, baseUrl, apiKey, models, selectedModel, selection.thinkingEffort !== "off", enriched);
117602
118243
  this.deps.track("login", {
117603
118244
  provider: name,
117604
118245
  model: selectedModel.id
@@ -117631,14 +118272,14 @@ var LoginFlow = class {
117631
118272
  supportsImageIn: false,
117632
118273
  supportsVideoIn: false
117633
118274
  };
117634
- await this.applyConfig(name, baseUrl, apiKey, [manualModelInfo], manualModelInfo, false);
118275
+ await this.applyConfig(name, baseUrl, apiKey, [manualModelInfo], manualModelInfo, false, {});
117635
118276
  this.deps.track("login", {
117636
118277
  provider: name,
117637
118278
  model: manualModel
117638
118279
  });
117639
118280
  this.deps.showStatus(`Connected: ${name} · ${manualModel}`);
117640
118281
  }
117641
- async applyConfig(name, baseUrl, apiKey, models, selectedModel, thinking) {
118282
+ async applyConfig(name, baseUrl, apiKey, models, selectedModel, thinking, enriched) {
117642
118283
  const config = await this.deps.getConfig();
117643
118284
  this.deps.applyProviderConfig(config, {
117644
118285
  name,
@@ -117648,6 +118289,13 @@ var LoginFlow = class {
117648
118289
  selectedModel,
117649
118290
  thinking
117650
118291
  });
118292
+ if (enriched !== void 0) for (const [key, patch] of Object.entries(enriched)) {
118293
+ const existing = config.models?.[key];
118294
+ if (existing !== void 0) config.models[key] = {
118295
+ ...existing,
118296
+ ...patch
118297
+ };
118298
+ }
117651
118299
  await this.deps.setConfig({
117652
118300
  providers: config.providers,
117653
118301
  models: config.models,
@@ -117656,6 +118304,18 @@ var LoginFlow = class {
117656
118304
  });
117657
118305
  await this.deps.refreshConfigAfterLogin();
117658
118306
  }
118307
+ async fetchCatalogWithFallback() {
118308
+ try {
118309
+ return await fetchCatalog(DEFAULT_CATALOG_URL);
118310
+ } catch {
118311
+ return loadBuiltInCatalog(this.deps.builtInCatalogJson);
118312
+ }
118313
+ }
118314
+ enrichModelFromCatalog(model, catalog) {
118315
+ const catalogModel = findCatalogModel(catalog, model.id);
118316
+ if (catalogModel === void 0) return void 0;
118317
+ return enrichWithCatalog(model, catalogModel);
118318
+ }
117659
118319
  };
117660
118320
  function capabilitiesForModel(m) {
117661
118321
  const caps = [];
@@ -120747,7 +121407,8 @@ var ByfTui = class {
120747
121407
  showLoginProgressSpinner: (label) => this.showLoginProgressSpinner(label),
120748
121408
  track: (event, props) => {
120749
121409
  this.track(event, props);
120750
- }
121410
+ },
121411
+ builtInCatalogJson: void 0
120751
121412
  }).run();
120752
121413
  }
120753
121414
  async handleLogoutCommand(_args) {