@byfriends/cli 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/dist/main.mjs +840 -185
- 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
|
-
},
|
|
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
|
-
|
|
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
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
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
|
-
|
|
8522
|
-
|
|
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:
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
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: [
|
|
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
|
-
|
|
8905
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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}: ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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: () =>
|
|
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
|
|
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
|
-
|
|
84066
|
-
|
|
84067
|
-
|
|
84068
|
-
|
|
84069
|
-
|
|
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
|
-
|
|
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 }) => ({
|
|
@@ -88750,11 +89316,19 @@ var Session$1 = class {
|
|
|
88750
89316
|
const { agents } = await this.readMetadata();
|
|
88751
89317
|
this.agents.clear();
|
|
88752
89318
|
let warning;
|
|
89319
|
+
const failedAgents = [];
|
|
88753
89320
|
const resumeTasks = Object.keys(agents).map(async (id) => {
|
|
88754
89321
|
const result = await this.ensureResumeAgentInstantiated(id, agents).resume();
|
|
88755
89322
|
if (result.warning !== void 0 && warning === void 0) warning = result.warning;
|
|
89323
|
+
if (result.error !== void 0) failedAgents.push({
|
|
89324
|
+
id,
|
|
89325
|
+
error: result.error
|
|
89326
|
+
});
|
|
88756
89327
|
});
|
|
88757
89328
|
await Promise.all(resumeTasks);
|
|
89329
|
+
const mainAgentFailure = failedAgents.find(({ id }) => id === "main");
|
|
89330
|
+
if (mainAgentFailure !== void 0) throw mainAgentFailure.error;
|
|
89331
|
+
for (const { id, error } of failedAgents) this.log.warn(`Sub agent "${id}" failed to resume: ${error.message}`);
|
|
88758
89332
|
const resumeWarning = warning;
|
|
88759
89333
|
const main = this.agents.get("main");
|
|
88760
89334
|
const profile = DEFAULT_AGENT_PROFILES["agent"];
|
|
@@ -105949,6 +106523,9 @@ function capabilityToStrings(capability) {
|
|
|
105949
106523
|
if (capability.video_in) caps.push("video_in");
|
|
105950
106524
|
if (capability.audio_in) caps.push("audio_in");
|
|
105951
106525
|
if (capability.thinking) caps.push("thinking");
|
|
106526
|
+
if (capability.thinking_effort) caps.push("thinking_effort");
|
|
106527
|
+
if (capability.thinking_xhigh) caps.push("thinking_xhigh");
|
|
106528
|
+
if (capability.thinking_max) caps.push("thinking_max");
|
|
105952
106529
|
if (capability.tool_use) caps.push("tool_use");
|
|
105953
106530
|
return caps.length > 0 ? caps : void 0;
|
|
105954
106531
|
}
|
|
@@ -106004,6 +106581,40 @@ function applyCatalogProvider(config, options) {
|
|
|
106004
106581
|
config.defaultThinking = options.thinking;
|
|
106005
106582
|
return { defaultModel };
|
|
106006
106583
|
}
|
|
106584
|
+
/**
|
|
106585
|
+
* Tests whether `candidate` is a prefix of `modelId` followed by either
|
|
106586
|
+
* end-of-string or a `-` separator. This matches `gpt-5.5` against
|
|
106587
|
+
* `gpt-5.5-2025-06-01` but not against `gpt-5.5-turbo` (different segment).
|
|
106588
|
+
*/
|
|
106589
|
+
function catalogIdMatchesModelId(candidate, modelId) {
|
|
106590
|
+
if (modelId === candidate) return true;
|
|
106591
|
+
if (modelId.startsWith(candidate) && modelId[candidate.length] === "-") return true;
|
|
106592
|
+
return false;
|
|
106593
|
+
}
|
|
106594
|
+
/**
|
|
106595
|
+
* Searches all providers in the catalog for a model whose ID matches
|
|
106596
|
+
* `modelId` (prefix + separator boundary). Returns the first match.
|
|
106597
|
+
*/
|
|
106598
|
+
function findCatalogModel(catalog, modelId) {
|
|
106599
|
+
for (const entry of Object.values(catalog)) {
|
|
106600
|
+
const models = catalogProviderModels(entry);
|
|
106601
|
+
for (const model of models) if (catalogIdMatchesModelId(model.id, modelId)) return model;
|
|
106602
|
+
}
|
|
106603
|
+
}
|
|
106604
|
+
/**
|
|
106605
|
+
* Merges catalog metadata (priority) with provider-supplied values (fallback).
|
|
106606
|
+
* Catalog provides: capabilities, maxContextSize, maxOutputSize, reasoningKey.
|
|
106607
|
+
* Provider provides: displayName (user chose this provider, keep its naming).
|
|
106608
|
+
*/
|
|
106609
|
+
function enrichWithCatalog(providerModel, catalogModel) {
|
|
106610
|
+
return {
|
|
106611
|
+
maxContextSize: catalogModel.capability.max_context_tokens || providerModel.contextLength,
|
|
106612
|
+
maxOutputSize: catalogModel.maxOutputSize,
|
|
106613
|
+
capabilities: capabilityToStrings(catalogModel.capability),
|
|
106614
|
+
displayName: providerModel.displayName,
|
|
106615
|
+
reasoningKey: catalogModel.reasoningKey
|
|
106616
|
+
};
|
|
106617
|
+
}
|
|
106007
106618
|
//#endregion
|
|
106008
106619
|
//#region ../../packages/oauth/src/utils.ts
|
|
106009
106620
|
function isRecord$2(value) {
|
|
@@ -106363,6 +106974,27 @@ function validateOptions(opts) {
|
|
|
106363
106974
|
};
|
|
106364
106975
|
}
|
|
106365
106976
|
//#endregion
|
|
106977
|
+
//#region src/tui/utils/dead-terminal.ts
|
|
106978
|
+
/**
|
|
106979
|
+
* Detects errors that mean the controlling terminal (stdout/stderr pty) is
|
|
106980
|
+
* effectively gone — for example after the parent shell crashed, the tmux
|
|
106981
|
+
* server vanished, or an SSH connection dropped without delivering SIGHUP.
|
|
106982
|
+
*
|
|
106983
|
+
* Continuing to write to a dead terminal would re-fire the same error on every
|
|
106984
|
+
* render tick and pin a CPU core. Callers should respond by skipping any
|
|
106985
|
+
* cleanup that touches stdout/stderr and exiting immediately.
|
|
106986
|
+
*/
|
|
106987
|
+
const DEAD_TERMINAL_ERROR_CODES = new Set([
|
|
106988
|
+
"EIO",
|
|
106989
|
+
"EPIPE",
|
|
106990
|
+
"ENOTCONN"
|
|
106991
|
+
]);
|
|
106992
|
+
function isDeadTerminalError(error) {
|
|
106993
|
+
if (error === null || typeof error !== "object" || !("code" in error)) return false;
|
|
106994
|
+
const code = error.code;
|
|
106995
|
+
return code !== void 0 && DEAD_TERMINAL_ERROR_CODES.has(code);
|
|
106996
|
+
}
|
|
106997
|
+
//#endregion
|
|
106366
106998
|
//#region src/cli/run-prompt.ts
|
|
106367
106999
|
const PROMPT_UI_MODE = "print";
|
|
106368
107000
|
const PROMPT_MAIN_AGENT_ID = "main";
|
|
@@ -106480,6 +107112,9 @@ function installHeadlessHandlers(session) {
|
|
|
106480
107112
|
}
|
|
106481
107113
|
function installPromptTerminationCleanup(promptProcess, cleanup) {
|
|
106482
107114
|
let terminating = false;
|
|
107115
|
+
const emergencyExit = () => {
|
|
107116
|
+
promptProcess.exit(129);
|
|
107117
|
+
};
|
|
106483
107118
|
const exitAfterCleanup = async (signal) => {
|
|
106484
107119
|
if (terminating) return;
|
|
106485
107120
|
terminating = true;
|
|
@@ -106493,9 +107128,22 @@ function installPromptTerminationCleanup(promptProcess, cleanup) {
|
|
|
106493
107128
|
const onSigterm = () => exitAfterCleanup("SIGTERM");
|
|
106494
107129
|
promptProcess.once("SIGINT", onSigint);
|
|
106495
107130
|
promptProcess.once("SIGTERM", onSigterm);
|
|
107131
|
+
let onSighup;
|
|
107132
|
+
if (process.platform !== "win32") {
|
|
107133
|
+
onSighup = () => emergencyExit();
|
|
107134
|
+
process.prependListener("SIGHUP", onSighup);
|
|
107135
|
+
}
|
|
107136
|
+
const terminalErrorHandler = (error) => {
|
|
107137
|
+
if (isDeadTerminalError(error)) emergencyExit();
|
|
107138
|
+
};
|
|
107139
|
+
process.stdout.on("error", terminalErrorHandler);
|
|
107140
|
+
process.stderr.on("error", terminalErrorHandler);
|
|
106496
107141
|
return () => {
|
|
106497
107142
|
promptProcess.off("SIGINT", onSigint);
|
|
106498
107143
|
promptProcess.off("SIGTERM", onSigterm);
|
|
107144
|
+
if (onSighup !== void 0) process.off("SIGHUP", onSighup);
|
|
107145
|
+
process.stdout.off("error", terminalErrorHandler);
|
|
107146
|
+
process.stderr.off("error", terminalErrorHandler);
|
|
106499
107147
|
};
|
|
106500
107148
|
}
|
|
106501
107149
|
function signalExitCode(signal) {
|
|
@@ -116119,27 +116767,6 @@ function hasDispose(value) {
|
|
|
116119
116767
|
return typeof value === "object" && value !== null && "dispose" in value && typeof value.dispose === "function";
|
|
116120
116768
|
}
|
|
116121
116769
|
//#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
116770
|
//#region src/tui/utils/errors.ts
|
|
116144
116771
|
function isAbortMessage(message) {
|
|
116145
116772
|
return message === "Aborted" || message.endsWith(": Aborted");
|
|
@@ -117558,7 +118185,6 @@ var LoginFlow = class {
|
|
|
117558
118185
|
const baseUrl = await promptTextInput(dialogHost, colors, {
|
|
117559
118186
|
title: "Base URL",
|
|
117560
118187
|
subtitle: "The OpenAI-compatible API endpoint",
|
|
117561
|
-
initialValue: "https://api.openai.com/v1",
|
|
117562
118188
|
placeholder: "https://api.openai.com/v1"
|
|
117563
118189
|
});
|
|
117564
118190
|
if (baseUrl === void 0) return;
|
|
@@ -117585,20 +118211,29 @@ var LoginFlow = class {
|
|
|
117585
118211
|
this.deps.showStatus("No models found at this endpoint. Enter model ID manually.");
|
|
117586
118212
|
return this.handleManualModelEntry(name, baseUrl, apiKey);
|
|
117587
118213
|
}
|
|
118214
|
+
const catalog = await this.fetchCatalogWithFallback();
|
|
118215
|
+
const enriched = {};
|
|
117588
118216
|
const modelDict = {};
|
|
117589
|
-
for (const m of models)
|
|
117590
|
-
|
|
117591
|
-
|
|
117592
|
-
|
|
117593
|
-
|
|
117594
|
-
|
|
117595
|
-
|
|
118217
|
+
for (const m of models) {
|
|
118218
|
+
const aliasKey = `${name}/${m.id}`;
|
|
118219
|
+
const enrichedData = catalog !== void 0 ? this.enrichModelFromCatalog(m, catalog) : void 0;
|
|
118220
|
+
if (enrichedData !== void 0) enriched[aliasKey] = enrichedData;
|
|
118221
|
+
modelDict[aliasKey] = {
|
|
118222
|
+
provider: name,
|
|
118223
|
+
model: m.id,
|
|
118224
|
+
maxContextSize: enrichedData?.maxContextSize ?? m.contextLength,
|
|
118225
|
+
capabilities: enrichedData?.capabilities ?? capabilitiesForModel(m),
|
|
118226
|
+
displayName: m.displayName,
|
|
118227
|
+
reasoningKey: enrichedData?.reasoningKey,
|
|
118228
|
+
maxOutputSize: enrichedData?.maxOutputSize
|
|
118229
|
+
};
|
|
118230
|
+
}
|
|
117596
118231
|
const selection = await promptModelSelector(dialogHost, colors, modelDict);
|
|
117597
118232
|
if (selection === void 0) return;
|
|
117598
118233
|
const selectedId = selection.alias.split("/").slice(1).join("/");
|
|
117599
118234
|
const selectedModel = models.find((m) => m.id === selectedId);
|
|
117600
118235
|
if (selectedModel === void 0) return;
|
|
117601
|
-
await this.applyConfig(name, baseUrl, apiKey, models, selectedModel, selection.thinkingEffort !== "off");
|
|
118236
|
+
await this.applyConfig(name, baseUrl, apiKey, models, selectedModel, selection.thinkingEffort !== "off", enriched);
|
|
117602
118237
|
this.deps.track("login", {
|
|
117603
118238
|
provider: name,
|
|
117604
118239
|
model: selectedModel.id
|
|
@@ -117631,14 +118266,14 @@ var LoginFlow = class {
|
|
|
117631
118266
|
supportsImageIn: false,
|
|
117632
118267
|
supportsVideoIn: false
|
|
117633
118268
|
};
|
|
117634
|
-
await this.applyConfig(name, baseUrl, apiKey, [manualModelInfo], manualModelInfo, false);
|
|
118269
|
+
await this.applyConfig(name, baseUrl, apiKey, [manualModelInfo], manualModelInfo, false, {});
|
|
117635
118270
|
this.deps.track("login", {
|
|
117636
118271
|
provider: name,
|
|
117637
118272
|
model: manualModel
|
|
117638
118273
|
});
|
|
117639
118274
|
this.deps.showStatus(`Connected: ${name} · ${manualModel}`);
|
|
117640
118275
|
}
|
|
117641
|
-
async applyConfig(name, baseUrl, apiKey, models, selectedModel, thinking) {
|
|
118276
|
+
async applyConfig(name, baseUrl, apiKey, models, selectedModel, thinking, enriched) {
|
|
117642
118277
|
const config = await this.deps.getConfig();
|
|
117643
118278
|
this.deps.applyProviderConfig(config, {
|
|
117644
118279
|
name,
|
|
@@ -117648,6 +118283,13 @@ var LoginFlow = class {
|
|
|
117648
118283
|
selectedModel,
|
|
117649
118284
|
thinking
|
|
117650
118285
|
});
|
|
118286
|
+
if (enriched !== void 0) for (const [key, patch] of Object.entries(enriched)) {
|
|
118287
|
+
const existing = config.models?.[key];
|
|
118288
|
+
if (existing !== void 0) config.models[key] = {
|
|
118289
|
+
...existing,
|
|
118290
|
+
...patch
|
|
118291
|
+
};
|
|
118292
|
+
}
|
|
117651
118293
|
await this.deps.setConfig({
|
|
117652
118294
|
providers: config.providers,
|
|
117653
118295
|
models: config.models,
|
|
@@ -117656,6 +118298,18 @@ var LoginFlow = class {
|
|
|
117656
118298
|
});
|
|
117657
118299
|
await this.deps.refreshConfigAfterLogin();
|
|
117658
118300
|
}
|
|
118301
|
+
async fetchCatalogWithFallback() {
|
|
118302
|
+
try {
|
|
118303
|
+
return await fetchCatalog(DEFAULT_CATALOG_URL);
|
|
118304
|
+
} catch {
|
|
118305
|
+
return loadBuiltInCatalog(this.deps.builtInCatalogJson);
|
|
118306
|
+
}
|
|
118307
|
+
}
|
|
118308
|
+
enrichModelFromCatalog(model, catalog) {
|
|
118309
|
+
const catalogModel = findCatalogModel(catalog, model.id);
|
|
118310
|
+
if (catalogModel === void 0) return void 0;
|
|
118311
|
+
return enrichWithCatalog(model, catalogModel);
|
|
118312
|
+
}
|
|
117659
118313
|
};
|
|
117660
118314
|
function capabilitiesForModel(m) {
|
|
117661
118315
|
const caps = [];
|
|
@@ -120747,7 +121401,8 @@ var ByfTui = class {
|
|
|
120747
121401
|
showLoginProgressSpinner: (label) => this.showLoginProgressSpinner(label),
|
|
120748
121402
|
track: (event, props) => {
|
|
120749
121403
|
this.track(event, props);
|
|
120750
|
-
}
|
|
121404
|
+
},
|
|
121405
|
+
builtInCatalogJson: void 0
|
|
120751
121406
|
}).run();
|
|
120752
121407
|
}
|
|
120753
121408
|
async handleLogoutCommand(_args) {
|