blun-king-cli 9.1.615 → 9.1.616

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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:57a7160081cd6a2ce9e3535b31b87f9410c0dafd58da9629b68cb0042a8e57e5
2
+ // BLUN_BUILD_INPUT_SHA256:8034cba54fd011bf9ce255483d60f6d12f8cdb9862b405ccf424ae40ac14b7c5
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);
@@ -10181,7 +10181,17 @@ async function canonicalWriterPath(target) {
10181
10181
  async function readOwner(lockDirectory) {
10182
10182
  const directory = await lstat(lockDirectory);
10183
10183
  if (!directory.isDirectory() || directory.isSymbolicLink()) throw unavailable();
10184
- const names = await readdir(lockDirectory);
10184
+ let names;
10185
+ try {
10186
+ names = await readdir(lockDirectory);
10187
+ } catch (error) {
10188
+ if (process.platform === "win32" && codeOf(error) === "EPERM") try {
10189
+ await lstat(lockDirectory);
10190
+ } catch (inspectionError) {
10191
+ if (codeOf(inspectionError) === "ENOENT") throw inspectionError;
10192
+ }
10193
+ throw error;
10194
+ }
10185
10195
  if (names.length === 0) return void 0;
10186
10196
  const name = names[0];
10187
10197
  const match = markerPattern.exec(name);
@@ -10256,7 +10266,10 @@ async function reclaimDeadOwner(lockDirectory) {
10256
10266
  }
10257
10267
  }
10258
10268
  /** A local-host writer claim. It never expires based on age or missed heartbeats. */
10259
- async function acquireSessionWriter(sessionDirectory) {
10269
+ async function acquireSessionWriter(sessionDirectory, options = {}) {
10270
+ const waitForBusyMs = options.waitForBusyMs ?? 0;
10271
+ if (!Number.isFinite(waitForBusyMs) || waitForBusyMs < 0 || waitForBusyMs > 5e3) throw new RangeError("Writer contention budget must be between 0 and 5000 milliseconds");
10272
+ const deadline = performance.now() + waitForBusyMs;
10260
10273
  let staged;
10261
10274
  let name;
10262
10275
  try {
@@ -10275,7 +10288,8 @@ async function acquireSessionWriter(sessionDirectory) {
10275
10288
  flag: "wx",
10276
10289
  mode: 384
10277
10290
  });
10278
- for (let attempt = 0; attempt < 8; attempt += 1) {
10291
+ let unownedCollisions = 0;
10292
+ for (;;) {
10279
10293
  try {
10280
10294
  await rename(staged, lockDirectory);
10281
10295
  } catch (error) {
@@ -10285,7 +10299,19 @@ async function acquireSessionWriter(sessionDirectory) {
10285
10299
  "EPERM",
10286
10300
  "EACCES"
10287
10301
  ].includes(codeOf(error) ?? "")) throw error;
10288
- await reclaimDeadOwner(lockDirectory);
10302
+ try {
10303
+ await reclaimDeadOwner(lockDirectory);
10304
+ } catch (ownershipError) {
10305
+ if (!(ownershipError instanceof BlunError) || ownershipError.code !== ErrorCodes.SESSION_WRITER_BUSY) throw ownershipError;
10306
+ const remaining = deadline - performance.now();
10307
+ if (waitForBusyMs === 0 || remaining <= 0) throw ownershipError;
10308
+ await new Promise((resolve) => {
10309
+ setTimeout(resolve, Math.min(25, remaining));
10310
+ });
10311
+ unownedCollisions = 0;
10312
+ continue;
10313
+ }
10314
+ if (++unownedCollisions >= 8) throw unavailable();
10289
10315
  continue;
10290
10316
  }
10291
10317
  let released = false;
@@ -10308,7 +10334,6 @@ async function acquireSessionWriter(sessionDirectory) {
10308
10334
  }
10309
10335
  };
10310
10336
  }
10311
- throw unavailable();
10312
10337
  } catch (error) {
10313
10338
  if (error instanceof BlunError) throw error;
10314
10339
  throw unavailable(error);
@@ -16473,16 +16498,11 @@ function configWriterError(error) {
16473
16498
  return error;
16474
16499
  }
16475
16500
  async function acquireConfigWriter(filePath) {
16476
- const deadline = performance.now() + 5e3;
16477
- for (;;) try {
16478
- return await acquireSessionWriter(filePath);
16501
+ try {
16502
+ return await acquireSessionWriter(filePath, { waitForBusyMs: 5e3 });
16479
16503
  } catch (error) {
16480
16504
  if (!(error instanceof BlunError) || error.code !== ErrorCodes.SESSION_WRITER_BUSY) throw error;
16481
- const remaining = deadline - performance.now();
16482
- if (remaining <= 0) throw new BlunError(ErrorCodes.CONFIG_WRITE_BUSY, "Another configuration change is still being saved", { cause: error });
16483
- await new Promise((resolve) => {
16484
- setTimeout(resolve, Math.min(25, remaining));
16485
- });
16505
+ throw new BlunError(ErrorCodes.CONFIG_WRITE_BUSY, "Another configuration change is still being saved", { cause: error });
16486
16506
  }
16487
16507
  }
16488
16508
  /** Keep transforms synchronous; credential validation and discovery belong before this boundary. */
@@ -30005,7 +30025,7 @@ var FileSystemAgentRecordPersistence = class {
30005
30025
  async *readFrom(offset, recordCount) {
30006
30026
  if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(recordCount) || recordCount < 0) throw new Error("Invalid Wire replay offset");
30007
30027
  await this.flush();
30008
- let line = "";
30028
+ const fragments = [];
30009
30029
  let lineNumber = recordCount;
30010
30030
  let byteLength = offset;
30011
30031
  const stream = createReadStream(this.filePath, {
@@ -30014,29 +30034,36 @@ var FileSystemAgentRecordPersistence = class {
30014
30034
  });
30015
30035
  try {
30016
30036
  for await (const chunk of stream) {
30017
- line += chunk;
30018
- let newlineIndex = line.indexOf("\n");
30037
+ let start = 0;
30038
+ let newlineIndex = chunk.indexOf("\n", start);
30019
30039
  while (newlineIndex !== -1) {
30020
- const rawLine = line.slice(0, newlineIndex);
30021
- line = line.slice(newlineIndex + 1);
30040
+ const part = chunk.slice(start, newlineIndex);
30041
+ let rawLine = part;
30042
+ if (fragments.length > 0) {
30043
+ fragments.push(part);
30044
+ rawLine = fragments.join("");
30045
+ fragments.length = 0;
30046
+ }
30022
30047
  lineNumber++;
30023
30048
  byteLength += Buffer.byteLength(rawLine, "utf8") + 1;
30024
30049
  const record = parseRecordLine(rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine, lineNumber, this.filePath, false);
30025
30050
  if (record !== void 0) yield record;
30026
- newlineIndex = line.indexOf("\n");
30051
+ start = newlineIndex + 1;
30052
+ newlineIndex = chunk.indexOf("\n", start);
30027
30053
  }
30054
+ if (start < chunk.length) fragments.push(chunk.slice(start));
30028
30055
  }
30029
30056
  } catch (error) {
30030
30057
  if (error.code === "ENOENT") return;
30031
30058
  throw error;
30032
30059
  }
30033
- if (line.length > 0) {
30060
+ if (fragments.length > 0) {
30034
30061
  lineNumber++;
30035
- const record = parseRecordLine(line, lineNumber, this.filePath, true);
30062
+ const record = parseRecordLine(fragments.join(""), lineNumber, this.filePath, true);
30036
30063
  if (record !== void 0) yield record;
30037
30064
  }
30038
30065
  this.persistedRecordCount = lineNumber;
30039
- this.persistedByteLength = line.length === 0 ? byteLength : void 0;
30066
+ this.persistedByteLength = fragments.length === 0 ? byteLength : void 0;
30040
30067
  }
30041
30068
  append(input) {
30042
30069
  this.throwIfError();
@@ -228677,6 +228704,8 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
228677
228704
  if (bytes.length <= byteBudget && (longestEdge > 0 && longestEdge <= maxEdge || longestEdge === 0)) return passthrough();
228678
228705
  if (dims && dims.width * dims.height > MAX_DECODE_PIXELS) return passthrough();
228679
228706
  if (bytes.length > maxDecodeBytes) return passthrough();
228707
+ const detected = detectFileType("", bytes.subarray(0, 512), "media");
228708
+ if (!RECODABLE_MIME.has(detected.mimeType)) return passthrough();
228680
228709
  try {
228681
228710
  const { Jimp } = await Promise.resolve().then(() => (init_esm(), esm_exports$3));
228682
228711
  const image = await Jimp.fromBuffer(Buffer.from(bytes));
@@ -231664,6 +231693,13 @@ const CronRunStatusSchema = _enum([
231664
231693
  ]);
231665
231694
  const counter = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
231666
231695
  const timestamp$1 = string().datetime();
231696
+ const CronProviderUsageSchema = object({
231697
+ inputOther: counter,
231698
+ inputCacheRead: counter,
231699
+ inputCacheCreation: counter,
231700
+ output: counter,
231701
+ outputReasoning: counter.optional()
231702
+ }).strict();
231667
231703
  const CronRunReceiptSchema = object({
231668
231704
  runId: CronRunIdSchema,
231669
231705
  jobId: CronJobIdSchema,
@@ -231684,6 +231720,7 @@ const CronRunReceiptSchema = object({
231684
231720
  names: array(string().regex(/^[\w.:-]{1,160}$/)).max(64),
231685
231721
  excessNames: counter
231686
231722
  }).strict(),
231723
+ providerUsage: CronProviderUsageSchema.nullable().optional(),
231687
231724
  outputs: array(object({
231688
231725
  rootRef: string().regex(/^(workspace|additional:[0-9]+)$/),
231689
231726
  path: string().min(1).max(1024).refine((value) => Buffer.byteLength(value) <= 1024 && !value.startsWith("/") && !value.includes("\\") && !value.includes(":") && !value.split("/").some((part) => part === ".." || part === ".") && !/[\u0000-\u001F\u007F]/.test(value))
@@ -231719,6 +231756,7 @@ function publicCronRun(run) {
231719
231756
  coalescedCount: receipt.coalescedCount,
231720
231757
  toolCalls: receipt.toolSummary.calls,
231721
231758
  toolErrors: receipt.toolSummary.errors,
231759
+ providerUsage: receipt.providerUsage ?? null,
231722
231760
  outputCount: receipt.outputs.length,
231723
231761
  finalMessageAvailable: run.finalMessage !== null,
231724
231762
  durable: run.durable
@@ -231920,6 +231958,8 @@ var CronRunOutputCollector = class {
231920
231958
  errors = 0;
231921
231959
  excessNames = 0;
231922
231960
  outputs = [];
231961
+ providerUsage;
231962
+ usageComplete = true;
231923
231963
  invalid = false;
231924
231964
  constructor(roots) {
231925
231965
  this.roots = roots;
@@ -231940,6 +231980,7 @@ var CronRunOutputCollector = class {
231940
231980
  }
231941
231981
  if (event.type === "step.retrying") {
231942
231982
  if (event.turnId !== String(this.turnId)) return;
231983
+ this.usageComplete = false;
231943
231984
  if (this.step === void 0 || this.step.id !== event.stepUuid || this.step.step !== event.step || this.step.tools) this.invalid = true;
231944
231985
  else {
231945
231986
  this.step = {
@@ -232008,19 +232049,29 @@ var CronRunOutputCollector = class {
232008
232049
  } else if (event.type === "step.end") {
232009
232050
  const step = this.step;
232010
232051
  if (step === void 0 || event.uuid !== step.id || event.step !== step.step) this.invalid = true;
232011
- else if (event.finishReason === "end_turn" && !step.tools && !this.invalid) {
232012
- const clipped = prefix(step.text);
232013
- const truncated = step.chars > clipped.length;
232014
- const text = clipped + (truncated ? CRON_RUN_TRUNCATION_MARKER : "");
232015
- if (text) this.candidate = {
232016
- stepId: step.id,
232017
- turnId: this.turnId,
232018
- message: {
232019
- text,
232020
- truncated,
232021
- chars: step.chars
232022
- }
232023
- };
232052
+ else {
232053
+ const usage = event.usage === void 0 ? void 0 : CronProviderUsageSchema.safeParse(event.usage);
232054
+ if (usage?.success !== true) this.usageComplete = false;
232055
+ else {
232056
+ const total = this.providerUsage === void 0 ? usage.data : addUsage(this.providerUsage, usage.data);
232057
+ const checked = CronProviderUsageSchema.safeParse(total);
232058
+ if (checked.success) this.providerUsage = checked.data;
232059
+ else this.usageComplete = false;
232060
+ }
232061
+ if (event.finishReason === "end_turn" && !step.tools && !this.invalid) {
232062
+ const clipped = prefix(step.text);
232063
+ const truncated = step.chars > clipped.length;
232064
+ const text = clipped + (truncated ? CRON_RUN_TRUNCATION_MARKER : "");
232065
+ if (text) this.candidate = {
232066
+ stepId: step.id,
232067
+ turnId: this.turnId,
232068
+ message: {
232069
+ text,
232070
+ truncated,
232071
+ chars: step.chars
232072
+ }
232073
+ };
232074
+ }
232024
232075
  }
232025
232076
  this.step = void 0;
232026
232077
  }
@@ -232035,6 +232086,7 @@ var CronRunOutputCollector = class {
232035
232086
  excessNames: this.excessNames
232036
232087
  },
232037
232088
  outputs: structuredClone(this.outputs),
232089
+ providerUsage: complete && this.usageComplete && this.providerUsage !== void 0 ? structuredClone(this.providerUsage) : null,
232038
232090
  final: complete ? this.candidate : void 0,
232039
232091
  complete
232040
232092
  };
@@ -233193,6 +233245,7 @@ var CronManager = class {
233193
233245
  status,
233194
233246
  endedAt: new Date(this.clocks.wallNow()).toISOString(),
233195
233247
  toolSummary: result.toolSummary,
233248
+ providerUsage: completed ? result.providerUsage : null,
233196
233249
  outputs: result.outputs,
233197
233250
  finalMessageRef: final === void 0 ? null : {
233198
233251
  stepId: final.stepId,
@@ -233380,6 +233433,7 @@ var CronManager = class {
233380
233433
  names: [],
233381
233434
  excessNames: 0
233382
233435
  },
233436
+ providerUsage: null,
233383
233437
  outputs: [],
233384
233438
  finalMessageRef: null,
233385
233439
  unavailableReason
@@ -234435,6 +234489,10 @@ const SYNTHETIC_TOOL_RESULT_TEXT = "Tool result is not available in the current
234435
234489
  function repairToolExchangeAdjacency(messages, options) {
234436
234490
  let lastNonToolIndex = messages.length - 1;
234437
234491
  while (lastNonToolIndex >= 0 && messages[lastNonToolIndex]?.role === "tool") lastNonToolIndex -= 1;
234492
+ let remainingResults;
234493
+ const consumeResult = (toolCallId) => {
234494
+ if (remainingResults !== void 0) remainingResults.set(toolCallId, (remainingResults.get(toolCallId) ?? 0) - 1);
234495
+ };
234438
234496
  const out = [];
234439
234497
  const consumed = /* @__PURE__ */ new Set();
234440
234498
  for (let i = 0; i < messages.length; i++) {
@@ -234442,12 +234500,15 @@ function repairToolExchangeAdjacency(messages, options) {
234442
234500
  const message = messages[i];
234443
234501
  if (message.role !== "assistant" || message.toolCalls.length === 0) {
234444
234502
  out.push(message);
234503
+ if (message.role === "tool" && message.toolCallId !== void 0) consumeResult(message.toolCallId);
234445
234504
  continue;
234446
234505
  }
234447
234506
  out.push(message);
234448
234507
  const pending = new Set(message.toolCalls.map((toolCall) => toolCall.id));
234508
+ const counts = remainingResults;
234509
+ let searchable = counts === void 0 ? void 0 : new Set([...pending].filter((id) => (counts.get(id) ?? 0) > 0));
234449
234510
  let foreignBetween = false;
234450
- for (let j = i + 1; j < messages.length && pending.size > 0; j++) {
234511
+ for (let j = i + 1; j < messages.length && (searchable ?? pending).size > 0; j++) {
234451
234512
  if (consumed.has(j)) continue;
234452
234513
  const next = messages[j];
234453
234514
  const toolCallId = next.toolCallId;
@@ -234455,11 +234516,25 @@ function repairToolExchangeAdjacency(messages, options) {
234455
234516
  out.push(next);
234456
234517
  consumed.add(j);
234457
234518
  pending.delete(toolCallId);
234519
+ searchable?.delete(toolCallId);
234520
+ consumeResult(toolCallId);
234458
234521
  if (foreignBetween) options?.onAnomaly?.({
234459
234522
  kind: "tool_result_reordered",
234460
234523
  toolCallId
234461
234524
  });
234462
- } else foreignBetween = true;
234525
+ } else {
234526
+ foreignBetween = true;
234527
+ if (remainingResults === void 0) {
234528
+ remainingResults = /* @__PURE__ */ new Map();
234529
+ for (let index = j; index < messages.length; index += 1) {
234530
+ if (consumed.has(index)) continue;
234531
+ const result = messages[index];
234532
+ if (result.role === "tool" && result.toolCallId !== void 0) remainingResults.set(result.toolCallId, (remainingResults.get(result.toolCallId) ?? 0) + 1);
234533
+ }
234534
+ }
234535
+ const counts = remainingResults;
234536
+ searchable ??= new Set([...pending].filter((id) => (counts.get(id) ?? 0) > 0));
234537
+ }
234463
234538
  }
234464
234539
  const isMidHistory = i < lastNonToolIndex;
234465
234540
  if (options?.synthesizeMissing === true || isMidHistory) for (const missingId of pending) {
@@ -234837,17 +234912,29 @@ var ContextMemory = class {
234837
234912
  deferredIndexes: [...deferredIndexes]
234838
234913
  });
234839
234914
  const removedMessages = /* @__PURE__ */ new Set();
234915
+ let shiftedSlots = -historyIndexes.size * (historyIndexes.size - 1) / 2;
234916
+ for (const index of historyIndexes) shiftedSlots += this._history.length - 1 - index;
234917
+ const compactHistory = shiftedSlots > this._history.length * 64;
234840
234918
  for (let i = this._history.length - 1; i >= 0; i--) {
234841
234919
  const message = this._history[i];
234842
234920
  if (message === void 0 || !historyIndexes.has(i)) continue;
234843
234921
  removedMessages.add(message);
234844
- this._history.splice(i, 1);
234922
+ if (!compactHistory) this._history.splice(i, 1);
234845
234923
  this.agent.injection.onContextMessageRemoved(i);
234846
234924
  if (i < this.tokenCountCoveredMessageCount) {
234847
234925
  this.tokenCountCoveredMessageCount--;
234848
234926
  this._tokenCount -= estimateTokensForMessages([message]);
234849
234927
  }
234850
234928
  }
234929
+ if (compactHistory) {
234930
+ let retained = 0;
234931
+ for (let index = 0; index < this._history.length; index++) {
234932
+ if (historyIndexes.has(index)) continue;
234933
+ if (retained !== index) this._history[retained] = this._history[index];
234934
+ retained++;
234935
+ }
234936
+ this._history.length = retained;
234937
+ }
234851
234938
  this.deferredMessages = this.deferredMessages.filter((message, index) => {
234852
234939
  if (!deferredIndexes.has(index)) return true;
234853
234940
  removedMessages.add(message);
@@ -239636,10 +239723,18 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239636
239723
  if (VIDEO_MEDIA_TERM_RE.test(text)) selected.add("GenerateVideo");
239637
239724
  return selected;
239638
239725
  }
239726
+ function channelMessagesForTurnText(value) {
239727
+ return [...String(value || "").matchAll(/(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/giu)].map((match) => ({
239728
+ text: match[1],
239729
+ start: match.index,
239730
+ end: match.index + match[0].length,
239731
+ telegram: /^<channel\s+source=["']telegram["'](?:\s|>)/iu.test(match[0].trimStart())
239732
+ }));
239733
+ }
239639
239734
  function mediaToolNamesForTurnText(value) {
239640
239735
  const text = String(value || "");
239641
239736
  const selected = /* @__PURE__ */ new Set();
239642
- const channelBodies = [...text.matchAll(/(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/giu)].map((match) => match[1]);
239737
+ const channelBodies = channelMessagesForTurnText(text).map((message) => message.text);
239643
239738
  const intentTexts = channelBodies.length > 0 ? channelBodies : [text];
239644
239739
  for (const intentText of intentTexts) for (const name of mediaGenerationToolNamesForText(intentText)) selected.add(name);
239645
239740
  if (/\bimage_path\s*=\s*["'][^"']+["']/iu.test(text)) {
@@ -239756,7 +239851,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239756
239851
  }
239757
239852
  function rankingIntentTexts(value) {
239758
239853
  const text = String(value || "");
239759
- const channelBodies = [...text.matchAll(/(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/giu)].map((match) => match[1]);
239854
+ const channelBodies = channelMessagesForTurnText(text).map((message) => message.text);
239760
239855
  return (channelBodies.length > 0 ? channelBodies : [text]).map((intentText) => normalizeDeferredToolQuery(withoutFencedCode(intentText.replace(TOOL_RANK_EMBEDDED_CONTENT_RE, " "))));
239761
239856
  }
239762
239857
  function rankingTokens(value) {
@@ -239833,17 +239928,15 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239833
239928
  if (!supportChoice.match(/^(?:tool|skill)\s*:\s*(.+)$/iu)?.[1]) return /* @__PURE__ */ new Set();
239834
239929
  return rankedToolNamesForTurnText(tools, supportChoice, { maxTools: 1 });
239835
239930
  }
239836
- function searchRelatedDeferredTools(tools, query, options = {}) {
239931
+ function searchRelatedDeferredTools(tools, query) {
239837
239932
  const normalized = normalizeDeferredToolQuery(query).toLowerCase();
239838
239933
  if (!normalized || normalized.startsWith("select:")) return [];
239839
239934
  const tokens = normalized.match(/\+?[\p{L}\p{N}_-]+/gu) || [];
239840
- const requestedNameTokens = tokens.filter((token) => token.startsWith("+")).map((token) => token.slice(1)).filter(Boolean);
239841
- const relaxRequiredNameTokens = options.relaxRequiredNameTokens === true && requestedNameTokens.length > 0;
239842
- const requiredNameTokens = relaxRequiredNameTokens ? [] : requestedNameTokens;
239843
- const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token) && !TOOL_RANK_STOP_WORDS.has(token));
239935
+ const requiredNameTokens = tokens.filter((token) => token.startsWith("+")).map((token) => token.slice(1)).filter(Boolean);
239936
+ const searchTokens = tokens.filter((token) => !token.startsWith("+")).filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token) && !TOOL_RANK_STOP_WORDS.has(token));
239844
239937
  if (searchTokens.length < 2 && !TOOL_SEARCH_SINGLE_TERM_RELATED_FALLBACKS.has(searchTokens[0])) return [];
239845
239938
  const minimumMatchedTokens = searchTokens.length === 1 ? 1 : Math.max(2, Math.ceil(searchTokens.length * .75));
239846
- const ranked = tools.map((tool) => {
239939
+ return tools.map((tool) => {
239847
239940
  const name = String(tool?.name || "").toLowerCase();
239848
239941
  const description = String(tool?.description || "").toLowerCase();
239849
239942
  if (!requiredNameTokens.every((token) => name.includes(token))) return null;
@@ -239874,12 +239967,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239874
239967
  score,
239875
239968
  matchedTokens
239876
239969
  } : null;
239877
- }).filter(Boolean).sort((left, right) => right.matchedTokens - left.matchedTokens || right.score - left.score || String(left.tool.name).localeCompare(String(right.tool.name)));
239878
- if (relaxRequiredNameTokens && ranked.length > 0) {
239879
- const best = ranked[0];
239880
- return ranked.filter((entry) => entry.matchedTokens === best.matchedTokens && entry.score === best.score).slice(0, 5).map((entry) => entry.tool);
239881
- }
239882
- return ranked.slice(0, 5).map((entry) => entry.tool);
239970
+ }).filter(Boolean).sort((left, right) => right.matchedTokens - left.matchedTokens || right.score - left.score || String(left.tool.name).localeCompare(String(right.tool.name))).slice(0, 5).map((entry) => entry.tool);
239883
239971
  }
239884
239972
  function rememberLoadedTool(loadedToolNames, name) {
239885
239973
  loadedToolNames.delete(name);
@@ -239904,11 +239992,10 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239904
239992
  const usedNames = new Set([...selectedTools, ...available.values()].map((tool) => String(tool?.name ?? "")));
239905
239993
  let loaderName = DEFERRED_TOOL_LOADER_NAME;
239906
239994
  for (let suffix = 1; usedNames.has(loaderName); suffix += 1) loaderName = `${DEFERRED_TOOL_LOADER_NAME}_${suffix}`;
239907
- const deferredCount = available.size;
239908
239995
  const loader = {
239909
239996
  name: loaderName,
239910
239997
  description: [
239911
- `Search ${deferredCount} deferred ${deferredCount === 1 ? "tool schema" : "tool schemas"} without loading all definitions.`,
239998
+ "Search deferred tool schemas without loading all definitions.",
239912
239999
  "Use select:leaf_name for one known tool, plain keywords to search, +word to require a name word, or catalog:fragment to list matching names.",
239913
240000
  `Loaded tools are available in the next step; up to ${MAX_PERSISTENT_DEFERRED_TOOLS} recently selected schemas remain loaded when the context budget allows.`
239914
240001
  ].join("\n"),
@@ -239939,10 +240026,6 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239939
240026
  matches = searchRelatedDeferredTools([...available.values()], query);
239940
240027
  relatedFallback = matches.length > 0;
239941
240028
  }
239942
- if (matches.length === 0) {
239943
- matches = searchRelatedDeferredTools([...available.values()], query, { relaxRequiredNameTokens: true });
239944
- relatedFallback = matches.length > 0;
239945
- }
239946
240029
  if (matches.length === 0) {
239947
240030
  const alreadyLoaded = query.toLowerCase().startsWith("select:") ? searchDeferredTools(selectedTools.filter((tool) => tool?.name !== loaderName), query) : [];
239948
240031
  if (alreadyLoaded.length === 1) return {
@@ -239981,6 +240064,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239981
240064
  return loader;
239982
240065
  }
239983
240066
  module.exports = {
240067
+ channelMessagesForTurnText,
239984
240068
  isDeferredToolLoader: (tool) => deferredToolLoaders.has(tool),
239985
240069
  CORE_TOOL_NAMES,
239986
240070
  DEFERRED_TOOL_LOADER_NAME,
@@ -241283,10 +241367,14 @@ var ReplayBuilder = class {
241283
241367
  return this.records;
241284
241368
  }
241285
241369
  removeMessagesFrom(records, removedMessages) {
241286
- for (let i = records.length - 1; i >= 0; i--) {
241287
- const record = records[i];
241288
- if (record.type === "message" && removedMessages.has(record.message)) records.splice(i, 1);
241370
+ let retained = 0;
241371
+ for (let index = 0; index < records.length; index++) {
241372
+ const record = records[index];
241373
+ if (record.type === "message" && removedMessages.has(record.message)) continue;
241374
+ if (retained !== index) records[retained] = record;
241375
+ retained++;
241289
241376
  }
241377
+ records.length = retained;
241290
241378
  }
241291
241379
  };
241292
241380
  //#endregion
@@ -242225,6 +242313,7 @@ function withThinkingOnlyRecovery(llm, onTimeout, observeExecution) {
242225
242313
  systemPrompt: llm.systemPrompt,
242226
242314
  modelName: llm.modelName,
242227
242315
  capability: llm.capability,
242316
+ rebuildsMessagesAtRequestBoundary: llm.rebuildsMessagesAtRequestBoundary,
242228
242317
  prepareRequestBoundary: llm.prepareRequestBoundary?.bind(llm),
242229
242318
  isRetryableError: llm.isRetryableError === void 0 ? void 0 : (error) => !recoveryCallbackFailed && !(error instanceof ThinkingOnlyTimeoutError) && (llm.isRetryableError?.(error) ?? false),
242230
242319
  async chat(params) {
@@ -250458,7 +250547,9 @@ async function executeLoopStep(deps) {
250458
250547
  if (beforeStep?.block === true) throw new Error(beforeStep.reason ?? `Step ${String(currentStep)} was blocked`);
250459
250548
  }
250460
250549
  signal.throwIfAborted();
250461
- const messages = await buildMessages();
250550
+ const prepareRequestBoundary = llm.prepareRequestBoundary?.bind(llm);
250551
+ const deferMessageBuild = prepareRequestBoundary !== void 0 && llm.rebuildsMessagesAtRequestBoundary === true;
250552
+ const messages = deferMessageBuild ? [] : await buildMessages();
250462
250553
  signal.throwIfAborted();
250463
250554
  const stepUuid = randomUUID();
250464
250555
  const stepEvents = createStepEventGate({
@@ -250468,7 +250559,6 @@ async function executeLoopStep(deps) {
250468
250559
  stepUuid,
250469
250560
  onStepStarted
250470
250561
  });
250471
- const prepareRequestBoundary = llm.prepareRequestBoundary?.bind(llm);
250472
250562
  const step = {
250473
250563
  tools,
250474
250564
  hooks,
@@ -250548,7 +250638,7 @@ async function executeLoopStep(deps) {
250548
250638
  turnStep: `${turnId}.${String(currentStep)}`,
250549
250639
  model: llm.modelName
250550
250640
  });
250551
- const strictMessages = await buildMessagesStrict();
250641
+ const strictMessages = deferMessageBuild ? [] : await buildMessagesStrict();
250552
250642
  signal.throwIfAborted();
250553
250643
  let strictParams = {
250554
250644
  ...chatParams,
@@ -265741,7 +265831,9 @@ function renderPersistedToolResult(toolName, toolCallId, text, outputPath, maxCh
265741
265831
  `output_path: ${outputPath}`,
265742
265832
  "next_step: Use Read with output_path to page through the full output."
265743
265833
  ];
265744
- lines.push("", "[preview]", text.slice(0, TOOL_RESULT_PREVIEW_CHARS));
265834
+ let preview = text.slice(0, TOOL_RESULT_PREVIEW_CHARS);
265835
+ if (/[\uD800-\uDBFF]$/.test(preview)) preview = preview.slice(0, -1);
265836
+ lines.push("", "[preview]", preview);
265745
265837
  return lines.join("\n");
265746
265838
  }
265747
265839
  function safeToolResultFileStem(toolName, toolCallId) {
@@ -267298,6 +267390,48 @@ var HistoricalToolProjection = class {
267298
267390
  }
267299
267391
  };
267300
267392
  //#endregion
267393
+ //#region ../../packages/agent-core/src/agent/turn/baseline-skill-projection.ts
267394
+ const BASELINE_ORIGIN = "baseline_skill:blun-design-dna";
267395
+ const NONVISUAL_TASK = /\b(?:backend|back-end|serverseitig|server-side|datenbank|database|sql|api[- ](?:endpoint|route)|memory broker)\b/i;
267396
+ const VISUAL_TASK = /\b(?:ui|ux|frontend|front-end|website|webseite|web|page|screen|screenshot|bild|image|design|layout|css|html|react|vue|svelte|komponente|component|button|formular|form|style|visual|visuell|interaction|interaktion|interface|oberfl[aä]che|dashboard|landing|mobile|responsive|accessibility|barrierefreiheit|tui|terminal|konsole|console|browser|diagram|chart|graph)\b/i;
267397
+ const SHORT_REFERENCE = [
267398
+ "<system-reminder>",
267399
+ "The BLUN Design DNA baseline is retained in the session. Its visual rules are dormant for this explicitly non-visual backend or data task. Preserve security, privacy, accessibility, permissions, and verification. If the work turns visual or interactive, use the complete baseline before continuing.",
267400
+ "</system-reminder>"
267401
+ ].join("\n");
267402
+ function shortNonVisualText(text) {
267403
+ return text.length <= 500 && !/[<>]/.test(text) && !/https?:\/\//i.test(text) && NONVISUAL_TASK.test(text) && !VISUAL_TASK.test(text);
267404
+ }
267405
+ function clearlyNonVisual(input, origin) {
267406
+ if (origin.kind !== "user" || input.length !== 1 || input[0]?.type !== "text") return false;
267407
+ const text = input[0].text;
267408
+ if (text.length > 16e3 || /\b(?:image_path|attachment_file_id)\s*=|Telegram-Anhang:/i.test(text)) return false;
267409
+ const messages = (0, import_turn_tool_performance_policy.channelMessagesForTurnText)(text);
267410
+ if (messages.length === 0) return shortNonVisualText(text);
267411
+ if (messages.some((message) => !message.telegram || !shortNonVisualText(message.text)) || messages.reduce((size, message) => size + message.text.length, 0) > 500) return false;
267412
+ let cursor = 0;
267413
+ let remainder = "";
267414
+ for (const message of messages) {
267415
+ remainder += text.slice(cursor, message.start);
267416
+ cursor = message.end;
267417
+ }
267418
+ remainder += text.slice(cursor);
267419
+ return !VISUAL_TASK.test(remainder) && remainder.split(/\r?\n/).every((line) => line.trim().length === 0 || /^\[(?:GRUPPENANTWORT|REPLY-PFLICHT|SEMANTISCHE ADRESSIERUNG|NUR MITLESEN)\b[^\r\n]*\]$/.test(line.trim()));
267420
+ }
267421
+ /** Request-only view; the context, records, and resume state keep the full skill. */
267422
+ function projectBaselineSkillForTurn(history, input, origin, turnAnchor) {
267423
+ if (!clearlyNonVisual(input, origin)) return history;
267424
+ const anchorIndex = turnAnchor === void 0 ? -1 : history.indexOf(turnAnchor);
267425
+ if (anchorIndex >= 0 && history.slice(anchorIndex + 1).some((message) => message.role === "tool" && message.content.some((part) => part.type !== "text" || VISUAL_TASK.test(part.text)))) return history;
267426
+ return history.map((message) => message.origin?.kind === "injection" && message.origin.variant === BASELINE_ORIGIN ? {
267427
+ ...message,
267428
+ content: [{
267429
+ type: "text",
267430
+ text: SHORT_REFERENCE
267431
+ }]
267432
+ } : message);
267433
+ }
267434
+ //#endregion
267301
267435
  //#region ../../packages/agent-core/src/agent/response-review-decision.ts
267302
267436
  const STAGES = [
267303
267437
  "setup",
@@ -267606,6 +267740,23 @@ function createReviewedOutputDispatcher(options) {
267606
267740
  }
267607
267741
  //#endregion
267608
267742
  //#region ../../packages/agent-core/src/agent/turn/index.ts
267743
+ function restoreRecentMcpToolNames(history, loadedNames) {
267744
+ const successfulResults = /* @__PURE__ */ new Set();
267745
+ const recentNames = [];
267746
+ const start = Math.max(0, history.length - 256);
267747
+ for (let index = history.length - 1; index >= start && recentNames.length < import_turn_tool_performance_policy.MAX_PERSISTENT_DEFERRED_TOOLS; index--) {
267748
+ const message = history[index];
267749
+ if (message.role === "tool" && message.isError !== true && message.toolCallId !== void 0) {
267750
+ if (successfulResults.size < 64) successfulResults.add(message.toolCallId);
267751
+ } else if (message.role === "assistant") for (let callIndex = message.toolCalls.length - 1; callIndex >= 0; callIndex--) {
267752
+ const call = message.toolCalls[callIndex];
267753
+ if (!successfulResults.delete(call.id) || !/^mcp__[\w.:-]{1,160}$/.test(call.name) || recentNames.includes(call.name)) continue;
267754
+ recentNames.push(call.name);
267755
+ if (recentNames.length >= import_turn_tool_performance_policy.MAX_PERSISTENT_DEFERRED_TOOLS) break;
267756
+ }
267757
+ }
267758
+ for (const name of recentNames.reverse()) loadedNames.add(name);
267759
+ }
267609
267760
  function blunExtractText(content) {
267610
267761
  if (typeof content === "string") return content;
267611
267762
  if (Array.isArray(content)) return content.map((c) => typeof c === "string" ? c : c && typeof c === "object" && "text" in c && typeof c.text === "string" ? c.text : "").join(" ");
@@ -267625,7 +267776,7 @@ function blunTurnNeedsTools(input, origin) {
267625
267776
  return !/^(?:hi|hallo|hey|hello|hoi|moin|servus|yo|danke|thanks|thx|bye|tschau|ciao)[.!?]*$/.test(text) && !/^(?:wer bist du|who are you|wie geht(?: es dir)?|how are you|na)[.!?]*$/.test(text);
267626
267777
  }
267627
267778
  function blunThinkingIntentInput(input) {
267628
- const channelText = [...blunExtractText(input).matchAll(/(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/gi)].at(-1)?.[1];
267779
+ const channelText = (0, import_turn_tool_performance_policy.channelMessagesForTurnText)(blunExtractText(input)).at(-1)?.text;
267629
267780
  return channelText === void 0 ? input : [{
267630
267781
  type: "text",
267631
267782
  text: channelText
@@ -267700,7 +267851,31 @@ function blunToolsForOrigin(tools, origin) {
267700
267851
  */
267701
267852
  function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PURE__ */ new Set(), requiredToolNames = /* @__PURE__ */ new Set(), selected = []) {
267702
267853
  const schemaBudget = (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens);
267703
- if (estimateTokensForTools(tools, schemaBudget) <= schemaBudget || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
267854
+ const estimates = /* @__PURE__ */ new Map();
267855
+ const estimateSummary = (tool) => {
267856
+ let estimate = estimates.get(tool);
267857
+ if (estimate === void 0) {
267858
+ estimate = { summary: estimateTokens(tool.name) + estimateTokens(tool.description) };
267859
+ estimates.set(tool, estimate);
267860
+ }
267861
+ return estimate;
267862
+ };
267863
+ const estimateSelection = (candidates) => {
267864
+ let total = 0;
267865
+ for (const candidate of candidates) {
267866
+ const estimate = estimateSummary(candidate);
267867
+ if (estimate.complete === void 0) estimate.complete = estimate.summary + estimateTokens(JSON.stringify(candidate.parameters));
267868
+ total += estimate.complete;
267869
+ if (total > schemaBudget) break;
267870
+ }
267871
+ return total;
267872
+ };
267873
+ let summaryTokens = 0;
267874
+ for (const tool of tools) {
267875
+ summaryTokens += estimateSummary(tool).summary;
267876
+ if (summaryTokens > schemaBudget) break;
267877
+ }
267878
+ if (tools.some(import_turn_tool_performance_policy.isDeferredToolLoader) || summaryTokens <= schemaBudget && estimateSelection(tools) <= schemaBudget) {
267704
267879
  selected.splice(0, selected.length, ...tools);
267705
267880
  return selected;
267706
267881
  }
@@ -267715,7 +267890,7 @@ function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PU
267715
267890
  };
267716
267891
  choose();
267717
267892
  for (const name of loadedToolNames) {
267718
- if (estimateTokensForTools(selected, schemaBudget) <= schemaBudget) break;
267893
+ if (estimateSelection(selected) <= schemaBudget) break;
267719
267894
  if (justLoaded.has(name)) continue;
267720
267895
  if (!remembered.delete(name)) continue;
267721
267896
  choose();
@@ -268079,6 +268254,7 @@ var TurnFlow = class {
268079
268254
  }
268080
268255
  finishResume() {
268081
268256
  if (this.activeTurn === "resuming") this.activeTurn = null;
268257
+ restoreRecentMcpToolNames(this.agent.context.history, this.loadedToolNames);
268082
268258
  this.steerBuffer.length = 0;
268083
268259
  }
268084
268260
  /**
@@ -268457,11 +268633,13 @@ var TurnFlow = class {
268457
268633
  if (current.length !== raw.length || current.some((message, index) => message !== raw[index])) {
268458
268634
  const latest = personalMemoryRecall === void 0 ? current : [...current, personalMemoryRecall];
268459
268635
  const retained = taskToolsNeeded ? latest : greetingHistory(latest, greetingAnchor);
268460
- partialContext = retained.length < latest.length;
268461
- return retained;
268636
+ const request = projectBaselineSkillForTurn(retained, toolInput, origin, turnAnchor);
268637
+ partialContext = retained.length < latest.length || request !== retained;
268638
+ return request;
268462
268639
  }
268463
- partialContext = selected.length < history.length || projected.changed;
268464
- return projected.history;
268640
+ const request = projectBaselineSkillForTurn(projected.history, toolInput, origin, turnAnchor);
268641
+ partialContext = selected.length < history.length || projected.changed || request !== projected.history;
268642
+ return request;
268465
268643
  };
268466
268644
  const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off", turnId);
268467
268645
  const turnTools = this.agent.tools.captureTurnTools();
@@ -272040,15 +272218,17 @@ var FallbackLLM = class {
272040
272218
  onModelChanged;
272041
272219
  prepareRequest;
272042
272220
  onContextOverflow;
272221
+ messagePreparation;
272043
272222
  currentIndex = 0;
272044
272223
  attemptedProviders = /* @__PURE__ */ new Set();
272045
272224
  errorsAfterOutput = /* @__PURE__ */ new WeakSet();
272046
272225
  rebuildBoundaryMessages;
272047
- constructor(candidates, onModelChanged = () => {}, prepareRequest, onContextOverflow) {
272226
+ constructor(candidates, onModelChanged = () => {}, prepareRequest, onContextOverflow, messagePreparation = "eager") {
272048
272227
  this.candidates = candidates;
272049
272228
  this.onModelChanged = onModelChanged;
272050
272229
  this.prepareRequest = prepareRequest;
272051
272230
  this.onContextOverflow = onContextOverflow;
272231
+ this.messagePreparation = messagePreparation;
272052
272232
  if (candidates.length === 0) throw new Error("FallbackLLM requires at least one model candidate.");
272053
272233
  }
272054
272234
  get activeAlias() {
@@ -272063,6 +272243,9 @@ var FallbackLLM = class {
272063
272243
  get capability() {
272064
272244
  return this.current.llm.capability;
272065
272245
  }
272246
+ get rebuildsMessagesAtRequestBoundary() {
272247
+ return this.messagePreparation === "boundary" && this.prepareRequest !== void 0;
272248
+ }
272066
272249
  async prepareRequestBoundary(params, rebuildMessages) {
272067
272250
  params.signal.throwIfAborted();
272068
272251
  this.rebuildBoundaryMessages = rebuildMessages;
@@ -272745,6 +272928,15 @@ var Agent$1 = class {
272745
272928
  };
272746
272929
  if (this.activeProfile !== void 0 && this.rawSystemPromptContext !== void 0 && this.config.systemPrompt === this.profileSystemPromptText) this.updateSystemPromptFromProfile(this.activeProfile, this.fitProfileInstructions(this.activeProfile, this.rawSystemPromptContext, requestTools, true));
272747
272930
  let systemPrompt = this.effectiveSystemPrompt;
272931
+ if (this.config.runtimeModel(_candidate.alias).provider.name === "blun") {
272932
+ const fixedTools = requestTools.filter((tool) => tool.name !== "CompactConversation");
272933
+ const fixedRequestBytes = estimateBlunSerializedBodyBytes(systemPrompt, fixedTools, []) + 4096;
272934
+ if (fixedRequestBytes >= 5e5) throw new BlunError(ErrorCodes.CONTEXT_OVERFLOW, "Fixed instructions or tools exceed the BLUN request limit; compaction cannot reduce them.", { details: {
272935
+ contextBrakeBlocked: true,
272936
+ estimatedRequestBytes: fixedRequestBytes,
272937
+ requestBudgetBytes: 5e5
272938
+ } });
272939
+ }
272748
272940
  const preflightStartedAt = performance.now();
272749
272941
  const compacted = await this.fullCompaction.beforeStep(params.signal, {
272750
272942
  messages: params.messages,
@@ -272789,7 +272981,7 @@ var Agent$1 = class {
272789
272981
  };
272790
272982
  }, (candidate, _params, error) => {
272791
272983
  this.fullCompaction.observeContextOverflow(error, candidate.alias);
272792
- });
272984
+ }, "boundary");
272793
272985
  }
272794
272986
  createKosongLLM(modelAlias, thinkingEffort) {
272795
272987
  const runtimeModel = this.config.runtimeModel(modelAlias);
@@ -349778,6 +349970,7 @@ var SDKRpcClient = class extends SDKRpcClientBase {
349778
349970
  core;
349779
349971
  ready;
349780
349972
  usesDefaultConfigPath;
349973
+ restoreProfileModel;
349781
349974
  constructor(options = {}) {
349782
349975
  super();
349783
349976
  this.identity = options.identity === void 0 ? void 0 : assertBlunHostIdentity(options.identity);
@@ -349787,9 +349980,11 @@ var SDKRpcClient = class extends SDKRpcClientBase {
349787
349980
  configPath: options.configPath
349788
349981
  });
349789
349982
  this.usesDefaultConfigPath = options.configPath === void 0;
349983
+ const authHomeDir = authHomeForProfile(this.homeDir);
349984
+ this.restoreProfileModel = this.usesDefaultConfigPath && authHomeDir !== this.homeDir;
349790
349985
  this.telemetry = options.telemetry ?? noopTelemetryClient;
349791
349986
  this.auth = new BlunAuthFacade({
349792
- homeDir: authHomeForProfile(this.homeDir),
349987
+ homeDir: authHomeDir,
349793
349988
  configPath: this.configPath,
349794
349989
  identity: this.identity,
349795
349990
  onRefresh: options.onOAuthRefresh
@@ -349809,7 +350004,7 @@ var SDKRpcClient = class extends SDKRpcClientBase {
349809
350004
  }
349810
350005
  async ensureConfigFile() {
349811
350006
  await ensureConfigFile(this.configPath);
349812
- if (this.usesDefaultConfigPath && authHomeForProfile(this.homeDir) !== this.homeDir) await this.auth.restoreMissingProfileModel();
350007
+ if (this.restoreProfileModel) await this.auth.restoreMissingProfileModel();
349813
350008
  }
349814
350009
  async close() {
349815
350010
  try {