blun-king-cli 9.1.610 → 9.1.612

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/worker-host.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:8c6e16ea727bc28d31e8a784ec7ab7cc656e35f70f996bb1e64df97776875360
2
+ // BLUN_BUILD_INPUT_SHA256:f9f2527561c5e5ec1afc53eef27313667c4d323a444532439e73a66865340f69
3
3
  import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
4
4
  import { dirname as __cjsShimDirname } from 'node:path';
5
5
  const __filename = __cjsShimFileURLToPath(import.meta.url);
6
6
  const __dirname = __cjsShimDirname(__filename);
7
7
  import { createRequire } from "node:module";
8
- import crypto$1, { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
8
+ import crypto$1, { createHash, createHmac, randomBytes, randomInt, randomUUID } from "node:crypto";
9
9
  import * as fs$12 from "node:fs";
10
10
  import Kt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, opendirSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
11
11
  import * as path$11 from "node:path";
@@ -1888,6 +1888,9 @@ var FetchTransport = class {
1888
1888
  const maxRetries = normalizeMaxRetries(requestOptions?.maxRetries);
1889
1889
  const requestBytes = exactBodyBytes(init.body);
1890
1890
  const requestBreakdown = pathname === "/chat/completions" && requestOptions?.onTransportAttempt !== void 0 && typeof init.body === "string" && requestBytes !== void 0 ? measureSerializedRequest(init.body, requestBytes) : void 0;
1891
+ if (pathname === "/chat/completions" && typeof init.body === "string") try {
1892
+ requestOptions?.onSerializedRequest?.(init.body);
1893
+ } catch {}
1891
1894
  let attempt = 0;
1892
1895
  while (true) {
1893
1896
  const headers = new Headers(init.headers);
@@ -1896,6 +1899,9 @@ var FetchTransport = class {
1896
1899
  const startedAt = performance.now();
1897
1900
  const startedAtMs = Date.now();
1898
1901
  try {
1902
+ try {
1903
+ requestOptions?.onRequestSent?.();
1904
+ } catch {}
1899
1905
  const response = await this.fetch(`${this.baseUrl}${pathname}`, {
1900
1906
  ...init,
1901
1907
  headers
@@ -2648,6 +2654,13 @@ function collectBlunVisionAttachments(messages) {
2648
2654
  }
2649
2655
  return attachments;
2650
2656
  }
2657
+ function estimateBlunRequestExtraBytes(tools, messages) {
2658
+ const attachments = collectBlunVisionAttachments(messages);
2659
+ return {
2660
+ toolsBytes: tools.length === 0 ? 0 : Buffer.byteLength(JSON.stringify({ tools: tools.map(convertTool) }), "utf8"),
2661
+ visionBytes: attachments.length === 0 ? 0 : Buffer.byteLength(JSON.stringify({ visionAttachments: attachments }), "utf8")
2662
+ };
2663
+ }
2651
2664
  function convertMessage(message) {
2652
2665
  let reasoningContent = "";
2653
2666
  const nonThinkParts = [];
@@ -2957,12 +2970,13 @@ var BlunChatProvider = class {
2957
2970
  if (this._stream) createParams["stream_options"] = { include_usage: true };
2958
2971
  try {
2959
2972
  const client = this._createClient(options?.auth);
2960
- const requestOptions = options?.signal !== void 0 || options?.transportMaxRetries !== void 0 || options?.onTransportAttempt !== void 0 ? {
2973
+ const requestOptions = options?.signal !== void 0 || options?.transportMaxRetries !== void 0 || options?.onRequestSent !== void 0 || options?.onSerializedRequest !== void 0 || options?.onTransportAttempt !== void 0 ? {
2961
2974
  ...options.signal !== void 0 ? { signal: options.signal } : {},
2962
2975
  ...options.transportMaxRetries !== void 0 ? { maxRetries: options.transportMaxRetries } : {},
2976
+ ...options.onRequestSent !== void 0 ? { onRequestSent: options.onRequestSent } : {},
2977
+ ...options.onSerializedRequest !== void 0 ? { onSerializedRequest: options.onSerializedRequest } : {},
2963
2978
  ...options.onTransportAttempt !== void 0 ? { onTransportAttempt: options.onTransportAttempt } : {}
2964
2979
  } : void 0;
2965
- options?.onRequestSent?.();
2966
2980
  let response;
2967
2981
  try {
2968
2982
  response = await client.chat.completions.create(createParams, requestOptions);
@@ -2971,7 +2985,6 @@ var BlunChatProvider = class {
2971
2985
  const cap = rejection instanceof APIContextOverflowError ? contextOverflowOutputCap(rejection, createParams["max_completion_tokens"]) : void 0;
2972
2986
  if (cap === void 0) throw rejection;
2973
2987
  options?.signal?.throwIfAborted();
2974
- options?.onRequestSent?.();
2975
2988
  response = await client.chat.completions.create({
2976
2989
  ...createParams,
2977
2990
  max_completion_tokens: cap
@@ -21757,10 +21770,15 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21757
21770
  const discovered = [];
21758
21771
  const emptySources = /* @__PURE__ */ new Map();
21759
21772
  const seen = /* @__PURE__ */ new Set();
21773
+ const notLoadedCandidates = /* @__PURE__ */ new Set();
21760
21774
  const collect = async (path) => {
21761
21775
  const file = await readAgentFile(kaos, path);
21762
- if (file === void 0) return false;
21776
+ if (file === void 0) {
21777
+ notLoadedCandidates.add(kaos.normpath(path));
21778
+ return false;
21779
+ }
21763
21780
  const key = kaos.normpath(file.path);
21781
+ notLoadedCandidates.delete(key);
21764
21782
  if (file.content.length === 0) {
21765
21783
  emptySources.set(key, InstructionText.file(file.path, "", file.sourceTextBytes));
21766
21784
  return false;
@@ -21796,7 +21814,10 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21796
21814
  return {
21797
21815
  content,
21798
21816
  warning: totalBytes > AGENTS_MD_RECOMMENDED_MAX_BYTES ? `AGENTS.md total ${formatKB(totalBytes)} KB exceeds the recommended ${formatKB(AGENTS_MD_RECOMMENDED_MAX_BYTES)} KB. Large instruction files increase cost and may impact performance; consider trimming.` : void 0,
21799
- sources: rendered.measure()
21817
+ sources: {
21818
+ ...rendered.measure(),
21819
+ ...notLoadedCandidates.size === 0 ? {} : { notLoadedCandidates: [...notLoadedCandidates] }
21820
+ }
21800
21821
  };
21801
21822
  }
21802
21823
  async function loadAdditionalDirsInfo(kaos, additionalDirs) {
@@ -28778,17 +28799,20 @@ function createSystemPromptRenderer(merged) {
28778
28799
  const counts = renderPromptContributions(merged.systemPromptTemplate, vars, rendered, true) ?? renderPromptContributions(merged.systemPromptTemplate, vars, rendered);
28779
28800
  const sources = context.agentsMdSources;
28780
28801
  const copies = counts?.instructionCopies ?? 0;
28781
- const sourceMatches = sources !== void 0 && sources.version === 1 && Number.isSafeInteger(sources.generatedBytes) && sources.generatedBytes >= 0 && sources.files.every((file) => typeof file.path === "string" && Number.isSafeInteger(file.bytes) && file.bytes >= 0) && sources.totalBytes === Buffer.byteLength(vars["BLUN_AGENTS_MD"] ?? "", "utf8") && sources.generatedBytes + sources.files.reduce((sum, file) => sum + file.bytes, 0) === sources.totalBytes;
28802
+ const sourceMatches = sources !== void 0 && sources.version === 1 && Number.isSafeInteger(sources.generatedBytes) && sources.generatedBytes >= 0 && sources.files.every((file) => typeof file.path === "string" && Number.isSafeInteger(file.bytes) && file.bytes >= 0) && (sources.notLoadedCandidates?.every((path) => typeof path === "string") ?? true) && sources.totalBytes === Buffer.byteLength(vars["BLUN_AGENTS_MD"] ?? "", "utf8") && sources.generatedBytes + sources.files.reduce((sum, file) => sum + file.bytes, 0) === sources.totalBytes;
28803
+ const notLoadedCandidates = sourceMatches && sources.notLoadedCandidates !== void 0 ? { notLoadedCandidates: [...sources.notLoadedCandidates] } : {};
28782
28804
  const instructionSources = copies === 0 ? {
28783
28805
  version: 1,
28784
28806
  totalBytes: 0,
28785
28807
  generatedBytes: 0,
28786
- files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : []
28808
+ files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : [],
28809
+ ...notLoadedCandidates
28787
28810
  } : !sourceMatches ? void 0 : {
28788
28811
  version: 1,
28789
28812
  totalBytes: sources.totalBytes * copies,
28790
28813
  generatedBytes: sources.generatedBytes * copies,
28791
- files: sources.files.map((file) => copyInstructionFileBytes(file, copies))
28814
+ files: sources.files.map((file) => copyInstructionFileBytes(file, copies)),
28815
+ ...notLoadedCandidates
28792
28816
  };
28793
28817
  const skillListingSources = counts === void 0 ? void 0 : scaleSkillListingSources(skillSources, vars["BLUN_SKILLS"] ?? "", counts.skillListingBytes);
28794
28818
  context.onContributions(counts === void 0 ? void 0 : {
@@ -230115,6 +230139,7 @@ function buildCompactionSummaryText(summary) {
230115
230139
  const MAX_SESSION_COMPACTION_FAILURES = 2;
230116
230140
  const DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS = 128 * 1024;
230117
230141
  const COMPACTION_SUMMARY_RESERVE_RATIO = .1;
230142
+ const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 75e4;
230118
230143
  const MAX_HIERARCHICAL_COMPACTION_PASSES = 64;
230119
230144
  const HIERARCHICAL_COMPACTION_PREFIX = "This is a complete summary of an earlier chronological segment. Preserve it as source material when merging it with the following conversation:";
230120
230145
  var CompactionTruncatedError = class extends Error {
@@ -230234,16 +230259,19 @@ var FullCompaction = class {
230234
230259
  const projected = context.additionalMessages === void 0 ? messages : [...messages, ...context.additionalMessages];
230235
230260
  const historyBudget = this.requestHistoryByteBudget(projected);
230236
230261
  const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230262
+ const requestBudget = this.requestBodyByteBudget(projected, context);
230263
+ const requestThresholdReached = requestBudget !== void 0 && requestBudget.usedRequestBytesEstimate >= requestBudget.thresholdRequestBytes;
230237
230264
  if (this.sessionCompactionFailures >= MAX_SESSION_COMPACTION_FAILURES) reason = "session_stopped";
230238
230265
  else if (this.isCompacting) reason = "already_compacting";
230239
- else if (thresholdTokens === void 0 && historyBudget === void 0) reason = "no_budget";
230240
- else if (!historyThresholdReached && (thresholdTokens === void 0 || !Number.isFinite(usedTokens) || usedTokens < thresholdTokens)) reason = "below_threshold";
230266
+ else if (thresholdTokens === void 0 && historyBudget === void 0 && requestBudget === void 0) reason = "no_budget";
230267
+ else if (!historyThresholdReached && !requestThresholdReached && (thresholdTokens === void 0 || !Number.isFinite(usedTokens) || usedTokens < thresholdTokens)) reason = "below_threshold";
230241
230268
  else if (!this.hasShrinkableHistory(projected)) reason = "nothing_to_compact";
230242
230269
  return {
230243
230270
  eligible: reason === "eligible",
230244
230271
  usedTokens,
230245
230272
  thresholdTokens,
230246
230273
  ...historyBudget,
230274
+ ...requestBudget,
230247
230275
  reason
230248
230276
  };
230249
230277
  }
@@ -230265,6 +230293,35 @@ var FullCompaction = class {
230265
230293
  thresholdHistoryBytes: limit
230266
230294
  };
230267
230295
  }
230296
+ requestBodyByteBudget(messages, context) {
230297
+ const limit = this.agent.blunConfig?.loopControl?.maxRequestHistoryBytes;
230298
+ if (!this.agent.config.hasProvider || this.strategy.compactionBudgetTokens === void 0 || limit === void 0 || !Number.isSafeInteger(limit) || limit <= 0) return void 0;
230299
+ const model = this.agent.activeResponderModel ?? this.agent.config.modelAlias;
230300
+ if (model === void 0 || this.agent.config.runtimeModel(model).provider.name !== "blun") return void 0;
230301
+ const systemPrompt = context.systemPrompt ?? this.agent.effectiveSystemPrompt;
230302
+ const tools = (context.tools ?? this.agent.tools.loopTools).map((tool) => ({
230303
+ name: tool.name,
230304
+ description: tool.description,
230305
+ parameters: tool.parameters
230306
+ }));
230307
+ let extraBytes;
230308
+ try {
230309
+ extraBytes = estimateBlunRequestExtraBytes(tools, messages);
230310
+ } catch {
230311
+ return;
230312
+ }
230313
+ if (Buffer.byteLength(JSON.stringify({
230314
+ systemPrompt,
230315
+ messages: []
230316
+ }), "utf8") + extraBytes.toolsBytes >= BLUN_COMPACTION_REQUEST_BUDGET_BYTES) return void 0;
230317
+ return {
230318
+ usedRequestBytesEstimate: Buffer.byteLength(JSON.stringify({
230319
+ systemPrompt,
230320
+ messages
230321
+ }), "utf8") + extraBytes.toolsBytes + extraBytes.visionBytes,
230322
+ thresholdRequestBytes: BLUN_COMPACTION_REQUEST_BUDGET_BYTES
230323
+ };
230324
+ }
230268
230325
  requestProactiveCompaction(context = {}) {
230269
230326
  const status = this.proactiveCompactionStatus(context);
230270
230327
  if (status.eligible && !this.proactiveCompactionRequested) {
@@ -230273,7 +230330,9 @@ var FullCompaction = class {
230273
230330
  used_tokens: status.usedTokens,
230274
230331
  threshold_tokens: status.thresholdTokens,
230275
230332
  used_history_bytes: status.usedHistoryBytes,
230276
- threshold_history_bytes: status.thresholdHistoryBytes
230333
+ threshold_history_bytes: status.thresholdHistoryBytes,
230334
+ used_request_bytes_estimate: status.usedRequestBytesEstimate,
230335
+ threshold_request_bytes: status.thresholdRequestBytes
230277
230336
  });
230278
230337
  }
230279
230338
  return status;
@@ -230402,7 +230461,9 @@ var FullCompaction = class {
230402
230461
  const requestTokens = this.estimateCurrentRequestTokens(requestContext);
230403
230462
  const budget = this.getCompactionBudgetTokens();
230404
230463
  const historyBudget = this.requestHistoryByteBudget(projectedMessages);
230405
- const proactive = (historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes || !this.isCompacting && budget !== void 0 && Number.isFinite(requestTokens) && requestTokens >= Math.max(1, Math.floor(budget * .9))) && this.hasShrinkableHistory(projectedMessages);
230464
+ const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230465
+ const requestBudget = this.requestBodyByteBudget(projectedMessages, requestContext);
230466
+ const proactive = (requestBudget !== void 0 && requestBudget.usedRequestBytesEstimate >= requestBudget.thresholdRequestBytes || historyThresholdReached || !this.isCompacting && budget !== void 0 && Number.isFinite(requestTokens) && requestTokens >= Math.max(1, Math.floor(budget * .9))) && this.hasShrinkableHistory(projectedMessages);
230406
230467
  if (this.checkAutoCompaction(true, requestTokens, projectedMessages, proactive) && (proactive || this.strategy.shouldBlock(requestTokens))) await this.block(signal);
230407
230468
  signal.throwIfAborted();
230408
230469
  const compacted = this.agent.context.history !== historyBefore;
@@ -230604,6 +230665,7 @@ var FullCompaction = class {
230604
230665
  let charsReceived = 0;
230605
230666
  let lastProgressEmitAt = 0;
230606
230667
  let initialCompactionRequestTokens;
230668
+ let initialCompactionRequestBytes;
230607
230669
  const compactionTools = [];
230608
230670
  const buildRequestMessages = (history, requestInstruction) => {
230609
230671
  return stripMediaForCompaction(downgradeUnsupportedMedia([...this.agent.context.project(history, {
@@ -230611,6 +230673,7 @@ var FullCompaction = class {
230611
230673
  dropOrphanResults: true
230612
230674
  }), createUserMessage(requestInstruction)], capability));
230613
230675
  };
230676
+ const estimateRequestBytes = (messages) => Buffer.byteLength(JSON.stringify(messages), "utf8") + Buffer.byteLength(this.agent.config.systemPrompt, "utf8");
230614
230677
  while (true) {
230615
230678
  signal.throwIfAborted();
230616
230679
  if (hierarchicalPassCount >= MAX_HIERARCHICAL_COMPACTION_PASSES) throw new BlunError(ErrorCodes.COMPACTION_FAILED, `Compaction stopped after ${String(MAX_HIERARCHICAL_COMPACTION_PASSES)} staged requests without reaching a final summary. The conversation history was not changed.`, { details: {
@@ -230620,30 +230683,39 @@ var FullCompaction = class {
230620
230683
  const compactionRequestLimit = this.getEffectiveMaxContextTokens(model);
230621
230684
  const safeCompactionRequestLimit = compactionRequestLimit > 0 ? Math.max(1, Math.floor(compactionRequestLimit * (1 - COMPACTION_SUMMARY_RESERVE_RATIO))) : compactionRequestLimit;
230622
230685
  let messages = buildRequestMessages(historyForModel, instruction);
230686
+ const requestByteBudget = provider.name === "blun" ? BLUN_COMPACTION_REQUEST_BUDGET_BYTES : void 0;
230623
230687
  let estimatedCompactionRequestTokens = this.estimateRequestTokens(messages, this.agent.effectiveSystemPrompt, compactionTools);
230688
+ let estimatedCompactionRequestBytes = requestByteBudget === void 0 ? void 0 : estimateRequestBytes(messages);
230624
230689
  initialCompactionRequestTokens ??= estimatedCompactionRequestTokens;
230690
+ initialCompactionRequestBytes ??= estimatedCompactionRequestBytes;
230625
230691
  let hierarchicalChunkEnd;
230626
- if (safeCompactionRequestLimit > 0 && estimatedCompactionRequestTokens >= safeCompactionRequestLimit) {
230692
+ const tokenBudgetExceeded = safeCompactionRequestLimit > 0 && estimatedCompactionRequestTokens >= safeCompactionRequestLimit;
230693
+ const byteBudgetExceeded = requestByteBudget !== void 0 && estimatedCompactionRequestBytes !== void 0 && estimatedCompactionRequestBytes >= requestByteBudget;
230694
+ if (tokenBudgetExceeded || byteBudgetExceeded) {
230627
230695
  if (historyForModel.length > 1) {
230628
230696
  const chunkInstruction = `${instruction}\n\nThis request contains only the oldest chronological segment. Summarize every fact needed by a later merge pass. Do not assume later messages are visible.`;
230629
- const chunk = selectHierarchicalCompactionChunk(historyForModel, safeCompactionRequestLimit, safeCompactionRequestLimit, (candidate) => {
230697
+ const chunk = selectHierarchicalCompactionChunk(historyForModel, safeCompactionRequestLimit, safeCompactionRequestLimit, requestByteBudget, (candidate) => {
230630
230698
  const candidateMessages = buildRequestMessages(candidate, chunkInstruction);
230631
230699
  return {
230632
230700
  messages: candidateMessages,
230633
- estimatedTokens: this.estimateRequestTokens(candidateMessages, this.agent.effectiveSystemPrompt, compactionTools)
230701
+ estimatedTokens: this.estimateRequestTokens(candidateMessages, this.agent.effectiveSystemPrompt, compactionTools),
230702
+ estimatedBytes: requestByteBudget === void 0 ? void 0 : estimateRequestBytes(candidateMessages)
230634
230703
  };
230635
230704
  });
230636
230705
  if (chunk !== void 0) {
230637
230706
  hierarchicalChunkEnd = chunk.end;
230638
230707
  messages = chunk.messages;
230639
230708
  estimatedCompactionRequestTokens = chunk.estimatedTokens;
230709
+ estimatedCompactionRequestBytes = chunk.estimatedBytes;
230640
230710
  }
230641
230711
  }
230642
- if (hierarchicalChunkEnd === void 0) throw new BlunError(ErrorCodes.CONTEXT_OVERFLOW, `Compaction stopped before upload: the conversation (${String(tokensBefore)} tokens) could not be divided into a request below the active model window (${String(compactionRequestLimit)} tokens).`, { details: {
230712
+ if (hierarchicalChunkEnd === void 0) throw new BlunError(ErrorCodes.CONTEXT_OVERFLOW, byteBudgetExceeded ? "Compaction stopped before upload: the conversation could not be divided into a request below the active byte limit." : `Compaction stopped before upload: the conversation (${String(tokensBefore)} tokens) could not be divided into a request below the active model window (${String(compactionRequestLimit)} tokens).`, { details: {
230643
230713
  contextBrakeBlocked: true,
230644
230714
  estimatedRequestTokens: estimatedCompactionRequestTokens,
230645
230715
  maxContextTokens: compactionRequestLimit,
230646
230716
  requestBudgetTokens: safeCompactionRequestLimit,
230717
+ estimatedRequestBytes: estimatedCompactionRequestBytes,
230718
+ requestBudgetBytes: requestByteBudget,
230647
230719
  summaryReserveTokens: compactionRequestLimit - safeCompactionRequestLimit,
230648
230720
  contextUnchanged: true
230649
230721
  } });
@@ -230652,7 +230724,7 @@ var FullCompaction = class {
230652
230724
  attemptCount += 1;
230653
230725
  charsReceived = 0;
230654
230726
  const stage = hierarchicalPassCount + 1;
230655
- const estimatedStageCount = estimateCompactionStageCount(initialCompactionRequestTokens, safeCompactionRequestLimit, stage);
230727
+ const estimatedStageCount = estimateCompactionStageCount(initialCompactionRequestTokens, safeCompactionRequestLimit, stage, initialCompactionRequestBytes, requestByteBudget);
230656
230728
  const estimatedProgressPercent = estimateCompactionProgressPercent(stage, estimatedStageCount);
230657
230729
  const windowUsagePercent = estimateCompactionWindowUsagePercent(estimatedCompactionRequestTokens, compactionRequestLimit);
230658
230730
  this.agent.log.info("compaction stage request", {
@@ -230661,6 +230733,8 @@ var FullCompaction = class {
230661
230733
  estimatedStageCount,
230662
230734
  activeContextTokens: this.estimateProjectedRequestTokens(),
230663
230735
  estimatedInputTokens: estimatedCompactionRequestTokens,
230736
+ estimatedInputBytes: estimatedCompactionRequestBytes,
230737
+ requestBudgetBytes: requestByteBudget,
230664
230738
  windowUsagePercent,
230665
230739
  safeInputLimitTokens: safeCompactionRequestLimit,
230666
230740
  maxContextTokens: compactionRequestLimit
@@ -230732,13 +230806,17 @@ var FullCompaction = class {
230732
230806
  if (hierarchicalChunkEnd !== void 0) {
230733
230807
  hierarchicalPassCount += 1;
230734
230808
  const previousTokens = estimateTokensForMessages(historyForModel);
230809
+ const previousBytes = requestByteBudget === void 0 ? void 0 : Buffer.byteLength(JSON.stringify(historyForModel), "utf8");
230735
230810
  const nextHistory = [createUserMessage(`${HIERARCHICAL_COMPACTION_PREFIX}\n${extractedSummary}`), ...historyForModel.slice(hierarchicalChunkEnd)];
230736
230811
  const nextTokens = estimateTokensForMessages(nextHistory);
230737
- if (nextTokens >= previousTokens) throw new BlunError(ErrorCodes.COMPACTION_FAILED, "A staged compaction response did not reduce the pending history. The conversation history was not changed.", { details: {
230812
+ const nextBytes = requestByteBudget === void 0 ? void 0 : Buffer.byteLength(JSON.stringify(nextHistory), "utf8");
230813
+ if (nextTokens >= previousTokens || previousBytes !== void 0 && nextBytes !== void 0 && nextBytes >= previousBytes) throw new BlunError(ErrorCodes.COMPACTION_FAILED, "A staged compaction response did not reduce the pending history. The conversation history was not changed.", { details: {
230738
230814
  contextUnchanged: true,
230739
230815
  hierarchicalPassCount,
230740
230816
  previousTokens,
230741
- nextTokens
230817
+ nextTokens,
230818
+ previousBytes,
230819
+ nextBytes
230742
230820
  } });
230743
230821
  historyForModel = nextHistory;
230744
230822
  retryCount = 0;
@@ -230934,9 +231012,10 @@ function isModelFallbackStatus(error) {
230934
231012
  function compactionTimingKey(provider) {
230935
231013
  return `${provider.name}\u0000${provider.modelName}`;
230936
231014
  }
230937
- function estimateCompactionStageCount(initialInputTokens, safeRequestLimitTokens, currentStage) {
230938
- if (safeRequestLimitTokens <= 0) return currentStage;
230939
- return Math.max(currentStage, Math.ceil(initialInputTokens / safeRequestLimitTokens));
231015
+ function estimateCompactionStageCount(initialInputTokens, safeRequestLimitTokens, currentStage, initialInputBytes, safeRequestLimitBytes) {
231016
+ const tokenStages = safeRequestLimitTokens > 0 ? Math.ceil(initialInputTokens / safeRequestLimitTokens) : 0;
231017
+ const byteStages = initialInputBytes !== void 0 && safeRequestLimitBytes !== void 0 && safeRequestLimitBytes > 0 ? Math.ceil(initialInputBytes / safeRequestLimitBytes) : 0;
231018
+ return Math.max(currentStage, tokenStages, byteStages);
230940
231019
  }
230941
231020
  function estimateCompactionProgressPercent(stage, estimatedStageCount) {
230942
231021
  if (estimatedStageCount <= 0) return 0;
@@ -230952,7 +231031,7 @@ const COMPACTION_OVERFLOW_SHRINK_RATIOS = [
230952
231031
  .5,
230953
231032
  .35
230954
231033
  ];
230955
- function selectHierarchicalCompactionChunk(history, targetRequestTokens, hardRequestLimit, build) {
231034
+ function selectHierarchicalCompactionChunk(history, targetRequestTokens, hardRequestLimit, requestByteBudget, build) {
230956
231035
  const safeEnds = [];
230957
231036
  for (let end = 1; end < history.length; end++) if (history[end]?.role !== "tool") safeEnds.push(end);
230958
231037
  if (safeEnds.length === 0) return void 0;
@@ -230964,7 +231043,7 @@ function selectHierarchicalCompactionChunk(history, targetRequestTokens, hardReq
230964
231043
  const middle = Math.floor((low + high) / 2);
230965
231044
  const end = safeEnds[middle];
230966
231045
  const request = build(history.slice(0, end));
230967
- if (request.estimatedTokens < limit) {
231046
+ if (request.estimatedTokens < limit && (requestByteBudget === void 0 || request.estimatedBytes !== void 0 && request.estimatedBytes < requestByteBudget)) {
230968
231047
  best = {
230969
231048
  end,
230970
231049
  ...request
@@ -236771,8 +236850,14 @@ var PersonalMemoryRecallInjector = class {
236771
236850
  * to read, write, list, or change any personal-memory state.
236772
236851
  */
236773
236852
  filterToolsForTurn(turnId, input, origin, tools) {
236774
- if (this.toolsAllowedForTurn(turnId, input, origin)) return tools.filter((tool) => !isPersonalMemoryRecallToolName(tool.name) && (!isPersonalMemoryToolName(tool.name) || this.isTrustedHostTool(tool.name)));
236775
- return tools.filter((tool) => !isPersonalMemoryToolName(tool.name));
236853
+ const brokerAllowed = this.toolsAllowedForTurn(turnId, input, origin);
236854
+ const localSearchAllowed = origin.kind === "user" && this.agent.type === "main" && !isTelegramGroupInput(input) && this.agent.localWorkHistorySearchEligibility?.() === true;
236855
+ return tools.filter((tool) => {
236856
+ if (!isPersonalMemoryToolName(tool.name)) return true;
236857
+ if (isPersonalMemoryRecallToolName(tool.name)) return false;
236858
+ if (tool.name === "mcp__personal-memory__memory_search" && localSearchAllowed) return true;
236859
+ return brokerAllowed && this.isTrustedHostTool(tool.name);
236860
+ });
236776
236861
  }
236777
236862
  isTrustedHostTool(name) {
236778
236863
  const isHostTool = this.agent.isPersonalMemoryHostTool;
@@ -238951,6 +239036,32 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
238951
239036
  "suchen"
238952
239037
  ]);
238953
239038
  const TOOL_SEARCH_RELATED_TERM_GROUPS = Object.freeze([
239039
+ Object.freeze([
239040
+ "persist",
239041
+ "save",
239042
+ "speichere",
239043
+ "speichern",
239044
+ "store"
239045
+ ]),
239046
+ Object.freeze([
239047
+ "erinnerung",
239048
+ "erinnerungen",
239049
+ "fact",
239050
+ "facts",
239051
+ "fakt",
239052
+ "fakten",
239053
+ "memory",
239054
+ "memories",
239055
+ "merk",
239056
+ "merken",
239057
+ "remember"
239058
+ ]),
239059
+ Object.freeze([
239060
+ "note",
239061
+ "notes",
239062
+ "notiz",
239063
+ "notizen"
239064
+ ]),
238954
239065
  Object.freeze([
238955
239066
  "checklist",
238956
239067
  "list",
@@ -239209,7 +239320,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239209
239320
  if (document.nameTokens.has(token)) exactNameMatches += 1;
239210
239321
  else if (relatedSearchTerms(token).some((term) => document.nameTokens.has(term))) relatedNameMatches += 1;
239211
239322
  }
239212
- return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || relatedNameMatches >= 1 && score >= 5 ? {
239323
+ return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || queryTokens.length === 1 && relatedNameMatches >= 1 && score >= 5 ? {
239213
239324
  name: document.name,
239214
239325
  score,
239215
239326
  matchedTokens,
@@ -239230,11 +239341,11 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239230
239341
  function searchRelatedDeferredTools(tools, query, options = {}) {
239231
239342
  const normalized = normalizeDeferredToolQuery(query).toLowerCase();
239232
239343
  if (!normalized || normalized.startsWith("select:")) return [];
239233
- const tokens = normalized.split(/\s+/).filter(Boolean);
239344
+ const tokens = normalized.match(/\+?[\p{L}\p{N}_-]+/gu) || [];
239234
239345
  const requestedNameTokens = tokens.filter((token) => token.startsWith("+")).map((token) => token.slice(1)).filter(Boolean);
239235
239346
  const relaxRequiredNameTokens = options.relaxRequiredNameTokens === true && requestedNameTokens.length > 0;
239236
239347
  const requiredNameTokens = relaxRequiredNameTokens ? [] : requestedNameTokens;
239237
- const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token));
239348
+ const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token) && !TOOL_RANK_STOP_WORDS.has(token));
239238
239349
  if (searchTokens.length < 2 && !TOOL_SEARCH_SINGLE_TERM_RELATED_FALLBACKS.has(searchTokens[0])) return [];
239239
239350
  const minimumMatchedTokens = searchTokens.length === 1 ? 1 : Math.max(2, Math.ceil(searchTokens.length * .75));
239240
239351
  const ranked = tools.map((tool) => {
@@ -239295,9 +239406,12 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239295
239406
  const name = String(tool?.name || "").trim();
239296
239407
  if (name && !available.has(name)) available.set(name, tool);
239297
239408
  }
239409
+ const usedNames = new Set([...selectedTools, ...available.values()].map((tool) => String(tool?.name ?? "")));
239410
+ let loaderName = DEFERRED_TOOL_LOADER_NAME;
239411
+ for (let suffix = 1; usedNames.has(loaderName); suffix += 1) loaderName = `${DEFERRED_TOOL_LOADER_NAME}_${suffix}`;
239298
239412
  const deferredCount = available.size;
239299
239413
  const loader = {
239300
- name: DEFERRED_TOOL_LOADER_NAME,
239414
+ name: loaderName,
239301
239415
  description: [
239302
239416
  `Search ${deferredCount} deferred ${deferredCount === 1 ? "tool schema" : "tool schemas"} without loading all definitions.`,
239303
239417
  "Use select:leaf_name for one known tool, plain keywords to search, or +word to require that word in the tool name.",
@@ -239317,7 +239431,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239317
239431
  const query = normalizeDeferredToolQuery(args?.query);
239318
239432
  return {
239319
239433
  description: query ? `Searching tool schemas for ${query}` : "Searching tool schemas",
239320
- approvalRule: DEFERRED_TOOL_LOADER_NAME,
239434
+ approvalRule: loaderName,
239321
239435
  execute: async () => {
239322
239436
  let matches = searchDeferredTools([...available.values()], query);
239323
239437
  let relatedFallback = false;
@@ -239330,7 +239444,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239330
239444
  relatedFallback = matches.length > 0;
239331
239445
  }
239332
239446
  if (matches.length === 0) {
239333
- const alreadyLoaded = query.toLowerCase().startsWith("select:") ? searchDeferredTools(selectedTools.filter((tool) => tool?.name !== DEFERRED_TOOL_LOADER_NAME), query) : [];
239447
+ const alreadyLoaded = query.toLowerCase().startsWith("select:") ? searchDeferredTools(selectedTools.filter((tool) => tool?.name !== loaderName), query) : [];
239334
239448
  if (alreadyLoaded.length === 1) return {
239335
239449
  isError: false,
239336
239450
  output: `Tool schema already loaded: ${alreadyLoaded[0].name}. Invoke it now.`
@@ -239340,7 +239454,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239340
239454
  output: [
239341
239455
  `No eligible deferred tool is available for: ${query || "[empty query]"}.`,
239342
239456
  "Use a loaded alternative or report this integration as unavailable.",
239343
- "ToolSearch remains available for a different tool needed later."
239457
+ `${loaderName} remains available for a different tool needed later.`
239344
239458
  ].join(" ")
239345
239459
  };
239346
239460
  }
@@ -250032,6 +250146,9 @@ function logStepTiming(log, turnId, step, response) {
250032
250146
  streamDurationMs: timing.streamDurationMs,
250033
250147
  ...timing.serverDecodeMs !== void 0 ? { serverDecodeMs: timing.serverDecodeMs } : {},
250034
250148
  ...timing.clientConsumeMs !== void 0 ? { clientConsumeMs: timing.clientConsumeMs } : {},
250149
+ inputOtherTokens: response.usage.inputOther,
250150
+ cacheReadTokens: response.usage.inputCacheRead,
250151
+ cacheCreationTokens: response.usage.inputCacheCreation,
250035
250152
  outputTokens: response.usage.output
250036
250153
  });
250037
250154
  }
@@ -266492,7 +266609,7 @@ var HistoricalToolProjection = class {
266492
266609
  for (const { index, message, outputPath, text, reference, shellOutput } of candidates) try {
266493
266610
  context.signal.throwIfAborted();
266494
266611
  if (!await this.canRead(read, outputPath, context)) continue;
266495
- const save = this.pending.then(() => this.persist(outputPath, text, context.signal));
266612
+ const save = this.pending.then(() => this.persist(outputPath, text, context.signal, true));
266496
266613
  this.pending = save.catch(() => void 0);
266497
266614
  await save;
266498
266615
  const stat = await this.agent.kaos.stat(outputPath);
@@ -266584,7 +266701,7 @@ var HistoricalToolProjection = class {
266584
266701
  revision: revision(after)
266585
266702
  });
266586
266703
  }
266587
- async persist(path, text, signal) {
266704
+ async persist(path, text, signal, finalVerificationFollows = false) {
266588
266705
  signal.throwIfAborted();
266589
266706
  await checkedAncestors(dirname(dirname(dirname(path))));
266590
266707
  signal.throwIfAborted();
@@ -266593,7 +266710,7 @@ var HistoricalToolProjection = class {
266593
266710
  await this.ensureDirectory(dirname(path));
266594
266711
  signal.throwIfAborted();
266595
266712
  if (this.files.has(path)) {
266596
- await this.verifyFile(path, text);
266713
+ if (!finalVerificationFollows) await this.verifyFile(path, text);
266597
266714
  return;
266598
266715
  }
266599
266716
  try {
@@ -267062,7 +267179,7 @@ function blunToolsForOrigin(tools, origin) {
267062
267179
  * the ordinary permission pipeline.
267063
267180
  */
267064
267181
  function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PURE__ */ new Set(), requiredToolNames = /* @__PURE__ */ new Set(), selected = []) {
267065
- if (estimateTokensForTools(tools) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens) || tools.some((tool) => tool.name === "ToolSearch")) {
267182
+ if (estimateTokensForTools(tools) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens) || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
267066
267183
  selected.splice(0, selected.length, ...tools);
267067
267184
  return selected;
267068
267185
  }
@@ -267559,6 +267676,7 @@ var TurnFlow = class {
267559
267676
  const appendedMessage = this.agent.context.history.at(-1);
267560
267677
  const turnAnchor = appendedMessage !== previousLastMessage && appendedMessage?.role === "user" && appendedMessage.origin === origin ? appendedMessage : void 0;
267561
267678
  const startedAt = Date.now();
267679
+ const preRequestHookStartedAt = performance.now();
267562
267680
  let ended;
267563
267681
  let blockedByUserPromptHook = false;
267564
267682
  let completedStopReason;
@@ -267584,6 +267702,13 @@ var TurnFlow = class {
267584
267702
  this.agent.log.warn("Turn-start observation failed");
267585
267703
  }
267586
267704
  signal.throwIfAborted();
267705
+ if (this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
267706
+ this.agent.log.info("turn pre-request phase", {
267707
+ turnId,
267708
+ phase: "turn-hooks",
267709
+ durationMs: Math.max(0, Math.round(performance.now() - preRequestHookStartedAt))
267710
+ });
267711
+ } catch {}
267587
267712
  const stopReason = await this.runStepLoop(turnId, signal, input, origin, turnAnchor, (response) => {
267588
267713
  completedResponse = response;
267589
267714
  });
@@ -267753,9 +267878,26 @@ var TurnFlow = class {
267753
267878
  let stopHookContinuationUsed = false;
267754
267879
  let goalOutcomeMessageContinuationUsed = false;
267755
267880
  const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
267881
+ const diagnosePreRequest = this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1";
267882
+ const recordPreRequestPhase = (phase, startedAt) => {
267883
+ if (!diagnosePreRequest) return;
267884
+ try {
267885
+ this.agent.log.info("turn pre-request phase", {
267886
+ turnId,
267887
+ phase,
267888
+ durationMs: Math.max(0, Math.round(performance.now() - startedAt))
267889
+ });
267890
+ } catch {}
267891
+ };
267892
+ let phaseStartedAt = performance.now();
267756
267893
  await this.agent.mcp?.waitForInitialLoad(signal);
267894
+ recordPreRequestPhase("mcp-initial-load", phaseStartedAt);
267895
+ phaseStartedAt = performance.now();
267757
267896
  const personalMemoryRecall = await this.agent.injection.injectPersonalMemoryForTurn(turnId, input, origin, signal);
267897
+ recordPreRequestPhase("personal-memory-recall", phaseStartedAt);
267898
+ phaseStartedAt = performance.now();
267758
267899
  await this.agent.injection.injectGoal();
267900
+ recordPreRequestPhase("goal-injection", phaseStartedAt);
267759
267901
  this.setActiveSteerAcceptance(turnId, true);
267760
267902
  const turnNeedsThinking = blunTurnNeedsTools(blunThinkingIntentInput(input), origin);
267761
267903
  let toolInput = input;
@@ -267766,12 +267908,14 @@ var TurnFlow = class {
267766
267908
  const raw = [...this.agent.context.history];
267767
267909
  const history = personalMemoryRecall === void 0 ? raw : [...raw, personalMemoryRecall];
267768
267910
  const selected = taskToolsNeeded ? history : greetingHistory(history, greetingAnchor);
267911
+ const projectionStartedAt = performance.now();
267769
267912
  const projected = await this.historicalToolProjection.project(selected, turnAnchor, tools, {
267770
267913
  turnId: String(turnId),
267771
267914
  stepNumber: this.currentStep,
267772
267915
  signal,
267773
267916
  llm: turnLLM
267774
267917
  });
267918
+ recordPreRequestPhase("historical-tool-projection", projectionStartedAt);
267775
267919
  const current = this.agent.context.history;
267776
267920
  if (current.length !== raw.length || current.some((message, index) => message !== raw[index])) {
267777
267921
  const latest = personalMemoryRecall === void 0 ? current : [...current, personalMemoryRecall];
@@ -267782,7 +267926,7 @@ var TurnFlow = class {
267782
267926
  partialContext = selected.length < history.length || projected.changed;
267783
267927
  return projected.history;
267784
267928
  };
267785
- const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off");
267929
+ const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off", turnId);
267786
267930
  const turnTools = this.agent.tools.captureTurnTools();
267787
267931
  while (true) {
267788
267932
  signal.throwIfAborted();
@@ -267792,6 +267936,7 @@ var TurnFlow = class {
267792
267936
  try {
267793
267937
  const selectedTools = [];
267794
267938
  this.refreshSelectedTools = (snapshot) => {
267939
+ const selectionStartedAt = performance.now();
267795
267940
  if (snapshot.messages.length > 0) toolInput = snapshot.messages.flatMap((message) => message.content);
267796
267941
  const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, turnTools()), origin);
267797
267942
  const intent = blunThinkingIntentInput(toolInput);
@@ -267804,6 +267949,7 @@ var TurnFlow = class {
267804
267949
  if (tool !== void 0 && !selectedTools.some((candidate) => candidate.name === name) && estimateTokensForTools([...selectedTools, tool]) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(this.agent.config.modelCapabilities?.max_context_tokens)) selectedTools.push(tool);
267805
267950
  }
267806
267951
  } else selectedTools.splice(0, selectedTools.length, ...eligibleTools.filter((tool) => BLUN_LANGUAGE_REVIEW_TOOL_NAMES.has(tool.name) || BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name)));
267952
+ recordPreRequestPhase("tool-selection", selectionStartedAt);
267807
267953
  return selectedTools;
267808
267954
  };
267809
267955
  return (await runTurn({
@@ -267816,14 +267962,24 @@ var TurnFlow = class {
267816
267962
  }, completion);
267817
267963
  },
267818
267964
  llm: turnLLM,
267819
- buildMessages: async () => this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true }),
267820
- buildMessagesStrict: async () => this.agent.context.project(await turnHistory(selectedTools), {
267821
- synthesizeMissing: true,
267822
- dropOrphanResults: true,
267823
- dedupeDuplicateToolCalls: true,
267824
- dropLeadingNonUser: true,
267825
- mergeConsecutiveAssistants: true
267826
- }),
267965
+ buildMessages: async () => {
267966
+ const startedAt = performance.now();
267967
+ const messages = this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true });
267968
+ recordPreRequestPhase("message-build", startedAt);
267969
+ return messages;
267970
+ },
267971
+ buildMessagesStrict: async () => {
267972
+ const startedAt = performance.now();
267973
+ const messages = this.agent.context.project(await turnHistory(selectedTools), {
267974
+ synthesizeMissing: true,
267975
+ dropOrphanResults: true,
267976
+ dedupeDuplicateToolCalls: true,
267977
+ dropLeadingNonUser: true,
267978
+ mergeConsecutiveAssistants: true
267979
+ });
267980
+ recordPreRequestPhase("message-build-strict", startedAt);
267981
+ return messages;
267982
+ },
267827
267983
  dispatchEvent: this.buildDispatchEvent(turnId, signal, onFinalResponse, () => partialContext),
267828
267984
  tools: selectedTools,
267829
267985
  log: this.agent.log,
@@ -267840,8 +267996,10 @@ var TurnFlow = class {
267840
267996
  },
267841
267997
  hooks: {
267842
267998
  beforeStep: async ({ signal: stepSignal, llm }) => {
267999
+ const injectionStartedAt = performance.now();
267843
268000
  this.agent.microCompaction.detect();
267844
268001
  await this.agent.injection.inject();
268002
+ recordPreRequestPhase("step-injection", injectionStartedAt);
267845
268003
  stepSignal.throwIfAborted();
267846
268004
  deduper.beginStep();
267847
268005
  },
@@ -271507,8 +271665,13 @@ var LlmRequestLogger = class {
271507
271665
  toolsEstimatedTokens = 0;
271508
271666
  requestSequence = 0;
271509
271667
  lastTurnRequest;
271510
- constructor(log) {
271668
+ serializedPrefixKey;
271669
+ requestSignatures = /* @__PURE__ */ new Map();
271670
+ prefixComparisons = /* @__PURE__ */ new Map();
271671
+ lastSerializedRequest;
271672
+ constructor(log, serializedPrefixDiagnostic = process.env["BLUN_DIAG_SERIALIZED_PREFIX"] === "1") {
271511
271673
  this.log = log;
271674
+ this.serializedPrefixKey = serializedPrefixDiagnostic ? randomBytes(32) : void 0;
271512
271675
  }
271513
271676
  logRequest(input) {
271514
271677
  const sequence = ++this.requestSequence;
@@ -271525,6 +271688,10 @@ var LlmRequestLogger = class {
271525
271688
  toolsHash: fingerprint(JSON.stringify(toolSignature(tools)))
271526
271689
  };
271527
271690
  const signature = JSON.stringify(config);
271691
+ if (this.serializedPrefixKey !== void 0) {
271692
+ this.requestSignatures.set(sequence, signature);
271693
+ if (this.requestSignatures.size > 32) this.requestSignatures.delete(this.requestSignatures.keys().next().value);
271694
+ }
271528
271695
  if (signature !== this.lastConfigLogSignature) {
271529
271696
  this.lastConfigLogSignature = signature;
271530
271697
  this.configRevision++;
@@ -271554,6 +271721,20 @@ var LlmRequestLogger = class {
271554
271721
  }
271555
271722
  };
271556
271723
  if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
271724
+ const source = input.systemPromptContributions;
271725
+ if (source?.version === 1 && [
271726
+ source.totalBytes,
271727
+ source.instructionBytes,
271728
+ source.skillListingBytes,
271729
+ source.runtimeMemoryBytes,
271730
+ source.otherBytes
271731
+ ].every((value) => Number.isSafeInteger(value) && value >= 0) && source.instructionBytes + source.skillListingBytes + source.runtimeMemoryBytes + source.otherBytes === source.totalBytes && source.totalBytes === Buffer.byteLength(systemPrompt, "utf8")) requestFields.systemPromptSourceBytes = {
271732
+ totalBytes: source.totalBytes,
271733
+ instructionBytes: source.instructionBytes,
271734
+ skillListingBytes: source.skillListingBytes,
271735
+ runtimeMemoryBytes: source.runtimeMemoryBytes,
271736
+ otherBytes: source.otherBytes
271737
+ };
271557
271738
  this.log.info("llm request", requestFields);
271558
271739
  if (fields?.turnStep !== void 0 && /^\d+\.\d+$/.test(fields.turnStep)) {
271559
271740
  const historyTextOrigins = summarizeRequestTextOrigins(messages);
@@ -271579,10 +271760,51 @@ var LlmRequestLogger = class {
271579
271760
  getLastTurnRequest() {
271580
271761
  return this.lastTurnRequest === void 0 ? void 0 : structuredClone(this.lastTurnRequest);
271581
271762
  }
271763
+ get serializedPrefixDiagnosticEnabled() {
271764
+ return this.serializedPrefixKey !== void 0;
271765
+ }
271766
+ observeSerializedRequest(body, fields, sequence) {
271767
+ const key = this.serializedPrefixKey;
271768
+ if (key === void 0 || !/^\d+\.\d+$/.test(fields?.turnStep ?? "")) return;
271769
+ const signature = this.requestSignatures.get(sequence);
271770
+ if (signature === void 0) return;
271771
+ try {
271772
+ const bytes = Buffer.from(body, "utf8");
271773
+ const granularityBytes = 1024;
271774
+ const chunks = [];
271775
+ for (let start = 0; start + granularityBytes <= bytes.length; start += granularityBytes) chunks.push(createHmac("sha256", key).update(bytes.subarray(start, start + granularityBytes)).digest("hex"));
271776
+ const previous = this.lastSerializedRequest;
271777
+ if (previous !== void 0 && previous.signature === signature) {
271778
+ let common = 0;
271779
+ while (common < previous.chunks.length && common < chunks.length && previous.chunks[common] === chunks[common]) common++;
271780
+ this.prefixComparisons.set(sequence, {
271781
+ version: 1,
271782
+ granularityBytes,
271783
+ previousBytes: previous.bytes,
271784
+ currentBytes: bytes.length,
271785
+ commonPrefixBytesLowerBound: common * granularityBytes,
271786
+ previousSequence: previous.sequence,
271787
+ currentSequence: sequence
271788
+ });
271789
+ if (this.prefixComparisons.size > 32) this.prefixComparisons.delete(this.prefixComparisons.keys().next().value);
271790
+ }
271791
+ this.lastSerializedRequest = {
271792
+ sequence,
271793
+ signature,
271794
+ bytes: bytes.length,
271795
+ chunks
271796
+ };
271797
+ } catch {
271798
+ this.lastSerializedRequest = void 0;
271799
+ }
271800
+ }
271582
271801
  logTransportAttempt(stats, fields, sequence) {
271802
+ const serializedPrefix = sequence === void 0 ? void 0 : this.prefixComparisons.get(sequence);
271583
271803
  this.log.info("llm transport attempt", {
271584
271804
  ...fields,
271585
- ...stats
271805
+ ...stats,
271806
+ ...this.serializedPrefixKey === void 0 || sequence === void 0 ? {} : { requestSequence: sequence },
271807
+ ...serializedPrefix === void 0 ? {} : { serializedPrefix }
271586
271808
  });
271587
271809
  if (sequence === void 0 || this.lastTurnRequest?.sequence !== sequence) return;
271588
271810
  const breakdown = stats.requestBreakdown;
@@ -271665,7 +271887,8 @@ function copyContributions(value) {
271665
271887
  version: source.version,
271666
271888
  totalBytes: source.totalBytes,
271667
271889
  generatedBytes: source.generatedBytes,
271668
- files: source.files.map((file) => copyInstructionFileBytes(file))
271890
+ files: source.files.map((file) => copyInstructionFileBytes(file)),
271891
+ ...source.notLoadedCandidates === void 0 ? {} : { notLoadedCandidates: [...source.notLoadedCandidates] }
271669
271892
  } }
271670
271893
  };
271671
271894
  }
@@ -271761,6 +271984,7 @@ var Agent$1 = class {
271761
271984
  errorMemory;
271762
271985
  replayBuilder;
271763
271986
  additionalDirs;
271987
+ systemPromptStartedAt = /* @__PURE__ */ new Date();
271764
271988
  activeProfile;
271765
271989
  brandHome;
271766
271990
  _activeResponderModel;
@@ -271831,6 +272055,7 @@ var Agent$1 = class {
271831
272055
  }
271832
272056
  personalMemoryRecallEligibility = () => this.type === "main" && this.personalMemoryHostCapabilityEnabled;
271833
272057
  isPersonalMemoryHostTool = (name) => this.personalMemoryHostCapabilityEnabled && this.tools.hasUserTool(name);
272058
+ localWorkHistorySearchEligibility = () => this.type === "main" && this.tools.hasUserTool("mcp__personal-memory__memory_search");
271834
272059
  setPersonalMemoryHostCapability(enabled) {
271835
272060
  this.personalMemoryHostCapabilityEnabled = enabled === true;
271836
272061
  }
@@ -271847,6 +272072,7 @@ var Agent$1 = class {
271847
272072
  const retryScope = providerRetryScope(modelAlias, provider);
271848
272073
  await this.providerRetryWait.wait(retryScope, generateOptions?.signal);
271849
272074
  const callerTransportAttempt = generateOptions?.onTransportAttempt;
272075
+ const callerSerializedRequest = generateOptions?.onSerializedRequest;
271850
272076
  const ownedGenerateOptions = {
271851
272077
  ...generateOptions,
271852
272078
  transportMaxRetries: 0
@@ -271869,6 +272095,12 @@ var Agent$1 = class {
271869
272095
  callerTransportAttempt?.(stats);
271870
272096
  this.llmRequestLogger.logTransportAttempt(stats, requestLogFields, requestSequence);
271871
272097
  },
272098
+ ...callerSerializedRequest !== void 0 || this.llmRequestLogger.serializedPrefixDiagnosticEnabled ? { onSerializedRequest: (body) => {
272099
+ try {
272100
+ callerSerializedRequest?.(body);
272101
+ } catch {}
272102
+ this.llmRequestLogger.observeSerializedRequest(body, requestLogFields, requestSequence);
272103
+ } } : {},
271872
272104
  onRequestStart: () => {
271873
272105
  requestOptions?.onRequestStart?.();
271874
272106
  requestStarted = true;
@@ -271894,7 +272126,7 @@ var Agent$1 = class {
271894
272126
  get llm() {
271895
272127
  return this.llmForTurn();
271896
272128
  }
271897
- llmForTurn(thinkingEffort) {
272129
+ llmForTurn(thinkingEffort, diagnosticTurnId) {
271898
272130
  const selectedAlias = this.config.model;
271899
272131
  const modelProvider = this.modelProvider;
271900
272132
  if (modelProvider === void 0) throw new BlunError(ErrorCodes.MODEL_NOT_CONFIGURED, "Provider not set");
@@ -271909,6 +272141,7 @@ var Agent$1 = class {
271909
272141
  messages: await rebuildMessages()
271910
272142
  };
271911
272143
  let systemPrompt = this.effectiveSystemPrompt;
272144
+ const preflightStartedAt = performance.now();
271912
272145
  const compacted = await this.fullCompaction.beforeStep(params.signal, {
271913
272146
  messages: params.messages,
271914
272147
  additionalMessages: steers.messages,
@@ -271917,6 +272150,13 @@ var Agent$1 = class {
271917
272150
  rebuildSystemPrompt: () => systemPrompt = this.effectiveSystemPrompt,
271918
272151
  tools: params.tools
271919
272152
  });
272153
+ if (this.type === "main" && diagnosticTurnId !== void 0 && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
272154
+ this.log.info("turn pre-request phase", {
272155
+ turnId: diagnosticTurnId,
272156
+ phase: "compaction-preflight",
272157
+ durationMs: Math.max(0, Math.round(performance.now() - preflightStartedAt))
272158
+ });
272159
+ } catch {}
271920
272160
  const tools = params.tools.some((tool) => tool.name === "CompactConversation") && !this.fullCompaction.proactiveCompactionStatus({
271921
272161
  messages: compacted ? await rebuildMessages() : params.messages,
271922
272162
  additionalMessages: steers.messages,
@@ -272021,6 +272261,7 @@ var Agent$1 = class {
272021
272261
  let contributions;
272022
272262
  const systemPrompt = profile.systemPrompt({
272023
272263
  osEnv: this.kaos.osEnv,
272264
+ now: this.systemPromptStartedAt,
272024
272265
  cwd: this.config.cwd,
272025
272266
  skills: this.skills?.registry,
272026
272267
  cwdListing: context?.cwdListing,