blun-king-cli 9.1.614 → 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,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:0533ca113dc07cc119988be5e76555dec986b6881903f8e2b537f5adf96cc2c5
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);
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";
@@ -14,13 +14,14 @@ import { Blob as Blob$1, File as File$1 } from "node:buffer";
14
14
  import fs, { access, appendFile, chmod, copyFile, cp, link, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
15
15
  import * as nodeOs from "node:os";
16
16
  import os, { arch, homedir, hostname, release, tmpdir, type, userInfo } from "node:os";
17
- import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
17
+ import { execFile, execFileSync, spawn } from "node:child_process";
18
18
  import { Worker } from "node:worker_threads";
19
19
  import * as sysPath from "path";
20
20
  import path$1, { dirname as dirname$1, parse as parse$1 } from "path";
21
21
  import I, { existsSync as existsSync$1, promises as promises$1, stat as stat$1, unwatchFile, watch, watchFile } from "fs";
22
22
  import zi from "assert";
23
23
  import Qr, { EventEmitter } from "events";
24
+ import { StringDecoder } from "node:string_decoder";
24
25
  import { lstat as lstat$1, open as open$1, readdir as readdir$1, realpath as realpath$1, stat as stat$2 } from "fs/promises";
25
26
  import Cs, { PassThrough, Readable, pipeline } from "node:stream";
26
27
  import { type as type$1 } from "os";
@@ -35,7 +36,6 @@ import * as win32Path from "node:path/win32";
35
36
  import { createServer } from "node:http";
36
37
  import { pipeline as pipeline$1 } from "node:stream/promises";
37
38
  import { EventEmitter as EventEmitter$1 } from "node:events";
38
- import { StringDecoder } from "node:string_decoder";
39
39
  import co from "node:assert";
40
40
  import { fileURLToPath, pathToFileURL } from "node:url";
41
41
  import { QuickJSAsyncWASMModule, RELEASE_ASYNC, newQuickJSAsyncWASMModule, shouldInterruptAfterDeadline } from "quickjs-emscripten";
@@ -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
@@ -1953,16 +1959,6 @@ function measureSerializedRequest(body, requestBytes) {
1953
1959
  const payload = parsed;
1954
1960
  const messages = Array.isArray(payload["messages"]) ? payload["messages"] : [];
1955
1961
  const tools = Array.isArray(payload["tools"]) ? payload["tools"] : void 0;
1956
- let systemMessageBytes = 0;
1957
- let conversationMessageBytes = 0;
1958
- let toolResultMessageBytes = 0;
1959
- for (const message of messages) {
1960
- const size = Buffer.byteLength(JSON.stringify(message), "utf8");
1961
- const role = message !== null && typeof message === "object" ? message["role"] : void 0;
1962
- if (role === "system" || role === "developer") systemMessageBytes += size;
1963
- else if (role === "tool") toolResultMessageBytes += size;
1964
- else conversationMessageBytes += size;
1965
- }
1966
1962
  const toolDefinitions = Object.freeze((tools ?? []).map((tool, index) => {
1967
1963
  const entry = tool !== null && typeof tool === "object" ? tool : void 0;
1968
1964
  const fn = entry?.["function"];
@@ -1973,6 +1969,44 @@ function measureSerializedRequest(body, requestBytes) {
1973
1969
  ...typeof name === "string" && name.length > 0 && name.length <= 128 ? { name } : {}
1974
1970
  });
1975
1971
  }));
1972
+ const declaredNames = new Set(toolDefinitions.flatMap((tool) => tool.name === void 0 ? [] : [tool.name]));
1973
+ const callNames = /* @__PURE__ */ new Map();
1974
+ for (const message of messages) {
1975
+ if (message === null || typeof message !== "object" || message["role"] !== "assistant") continue;
1976
+ const calls = message["tool_calls"];
1977
+ if (!Array.isArray(calls)) continue;
1978
+ for (const call of calls) {
1979
+ if (call === null || typeof call !== "object") continue;
1980
+ const entry = call;
1981
+ const fn = entry["function"];
1982
+ const name = fn !== null && typeof fn === "object" ? fn["name"] : void 0;
1983
+ const id = entry["id"];
1984
+ if (typeof id !== "string" || id.length === 0) continue;
1985
+ callNames.set(id, callNames.has(id) ? void 0 : typeof name === "string" && declaredNames.has(name) ? name : void 0);
1986
+ }
1987
+ }
1988
+ let systemMessageBytes = 0;
1989
+ let conversationMessageBytes = 0;
1990
+ let toolResultMessageBytes = 0;
1991
+ const results = /* @__PURE__ */ new Map();
1992
+ for (const message of messages) {
1993
+ const size = Buffer.byteLength(JSON.stringify(message), "utf8");
1994
+ const role = message !== null && typeof message === "object" ? message["role"] : void 0;
1995
+ if (role === "system" || role === "developer") systemMessageBytes += size;
1996
+ else if (role === "tool") {
1997
+ toolResultMessageBytes += size;
1998
+ const id = message["tool_call_id"];
1999
+ const name = typeof id === "string" ? callNames.get(id) : void 0;
2000
+ const key = name ?? "";
2001
+ const previous = results.get(key);
2002
+ results.set(key, {
2003
+ ...name === void 0 ? {} : { name },
2004
+ count: (previous?.count ?? 0) + 1,
2005
+ bytes: (previous?.bytes ?? 0) + size
2006
+ });
2007
+ } else conversationMessageBytes += size;
2008
+ }
2009
+ const toolResultByTool = Object.freeze(Array.from(results.values(), (entry) => Object.freeze(entry)));
1976
2010
  const toolArrayOverheadBytes = tools === void 0 ? 0 : 2 + Math.max(0, tools.length - 1);
1977
2011
  const toolSchemaBytes = toolDefinitions.reduce((sum, tool) => sum + tool.bytes, toolArrayOverheadBytes);
1978
2012
  return Object.freeze({
@@ -1982,6 +2016,7 @@ function measureSerializedRequest(body, requestBytes) {
1982
2016
  systemMessageBytes,
1983
2017
  conversationMessageBytes,
1984
2018
  toolResultMessageBytes,
2019
+ ...toolResultByTool.length === 0 ? {} : { toolResultByTool },
1985
2020
  toolSchemaBytes,
1986
2021
  toolDefinitions,
1987
2022
  toolArrayOverheadBytes,
@@ -2366,6 +2401,9 @@ function extractChatCompletionUsage(usage) {
2366
2401
  const value = usage;
2367
2402
  const promptTokens = typeof value["prompt_tokens"] === "number" ? value["prompt_tokens"] : 0;
2368
2403
  const completionTokens = typeof value["completion_tokens"] === "number" ? value["completion_tokens"] : 0;
2404
+ const completionDetails = value["completion_tokens_details"];
2405
+ const reportedReasoning = completionDetails !== null && typeof completionDetails === "object" ? completionDetails["reasoning_tokens"] : void 0;
2406
+ const outputReasoning = typeof reportedReasoning === "number" && Number.isSafeInteger(reportedReasoning) && reportedReasoning >= 0 && reportedReasoning <= completionTokens ? reportedReasoning : void 0;
2369
2407
  let cachedTokens = 0;
2370
2408
  if (typeof value["cached_tokens"] === "number") cachedTokens = value["cached_tokens"];
2371
2409
  else if (value["prompt_tokens_details"] !== null && typeof value["prompt_tokens_details"] === "object") {
@@ -2375,6 +2413,7 @@ function extractChatCompletionUsage(usage) {
2375
2413
  return {
2376
2414
  inputOther: promptTokens - cachedTokens,
2377
2415
  output: completionTokens,
2416
+ ...outputReasoning === void 0 ? {} : { outputReasoning },
2378
2417
  inputCacheRead: cachedTokens,
2379
2418
  inputCacheCreation: 0
2380
2419
  };
@@ -2648,6 +2687,23 @@ function collectBlunVisionAttachments(messages) {
2648
2687
  }
2649
2688
  return attachments;
2650
2689
  }
2690
+ function estimateBlunSerializedBodyBytes(systemPrompt, tools, history) {
2691
+ const normalizedHistory = normalizeToolCallIdsForProvider([...history], BLUN_TOOL_CALL_ID_POLICY);
2692
+ const messages = systemPrompt ? [{
2693
+ role: "system",
2694
+ content: systemPrompt
2695
+ }] : [];
2696
+ for (const message of normalizedHistory) if (!isEmptyAssistantHistoryMessage(message)) messages.push(convertMessage(message));
2697
+ const attachments = collectBlunVisionAttachments(normalizedHistory);
2698
+ return Buffer.byteLength(JSON.stringify({
2699
+ model: "king",
2700
+ messages,
2701
+ stream: true,
2702
+ ...tools.length > 0 ? { tools: tools.map(convertTool) } : {},
2703
+ ...attachments.length > 0 ? { visionAttachments: attachments } : {},
2704
+ stream_options: { include_usage: true }
2705
+ }), "utf8");
2706
+ }
2651
2707
  function convertMessage(message) {
2652
2708
  let reasoningContent = "";
2653
2709
  const nonThinkParts = [];
@@ -2957,12 +3013,13 @@ var BlunChatProvider = class {
2957
3013
  if (this._stream) createParams["stream_options"] = { include_usage: true };
2958
3014
  try {
2959
3015
  const client = this._createClient(options?.auth);
2960
- const requestOptions = options?.signal !== void 0 || options?.transportMaxRetries !== void 0 || options?.onTransportAttempt !== void 0 ? {
3016
+ const requestOptions = options?.signal !== void 0 || options?.transportMaxRetries !== void 0 || options?.onRequestSent !== void 0 || options?.onSerializedRequest !== void 0 || options?.onTransportAttempt !== void 0 ? {
2961
3017
  ...options.signal !== void 0 ? { signal: options.signal } : {},
2962
3018
  ...options.transportMaxRetries !== void 0 ? { maxRetries: options.transportMaxRetries } : {},
3019
+ ...options.onRequestSent !== void 0 ? { onRequestSent: options.onRequestSent } : {},
3020
+ ...options.onSerializedRequest !== void 0 ? { onSerializedRequest: options.onSerializedRequest } : {},
2963
3021
  ...options.onTransportAttempt !== void 0 ? { onTransportAttempt: options.onTransportAttempt } : {}
2964
3022
  } : void 0;
2965
- options?.onRequestSent?.();
2966
3023
  let response;
2967
3024
  try {
2968
3025
  response = await client.chat.completions.create(createParams, requestOptions);
@@ -2971,7 +3028,6 @@ var BlunChatProvider = class {
2971
3028
  const cap = rejection instanceof APIContextOverflowError ? contextOverflowOutputCap(rejection, createParams["max_completion_tokens"]) : void 0;
2972
3029
  if (cap === void 0) throw rejection;
2973
3030
  options?.signal?.throwIfAborted();
2974
- options?.onRequestSent?.();
2975
3031
  response = await client.chat.completions.create({
2976
3032
  ...createParams,
2977
3033
  max_completion_tokens: cap
@@ -3358,9 +3414,11 @@ function emptyUsage() {
3358
3414
  * Sum two TokenUsage values.
3359
3415
  */
3360
3416
  function addUsage(a, b) {
3417
+ const outputReasoning = a.outputReasoning !== void 0 && b.outputReasoning !== void 0 ? a.outputReasoning + b.outputReasoning : a.output === 0 ? b.outputReasoning : b.output === 0 ? a.outputReasoning : void 0;
3361
3418
  return {
3362
3419
  inputOther: a.inputOther + b.inputOther,
3363
3420
  output: a.output + b.output,
3421
+ ...outputReasoning === void 0 ? {} : { outputReasoning },
3364
3422
  inputCacheRead: a.inputCacheRead + b.inputCacheRead,
3365
3423
  inputCacheCreation: a.inputCacheCreation + b.inputCacheCreation
3366
3424
  };
@@ -9770,6 +9828,8 @@ const StringRecordSchema = record$2(string(), string());
9770
9828
  const ProviderConfigSchema = object({
9771
9829
  type: ProviderTypeSchema,
9772
9830
  apiKey: string().optional(),
9831
+ memoryWorkspaceId: string().regex(/^blun-[0-9a-f]{64}$/u).optional(),
9832
+ memoryApiKeyHash: string().regex(/^[0-9a-f]{64}$/u).optional(),
9773
9833
  baseUrl: string().optional(),
9774
9834
  defaultModel: string().optional(),
9775
9835
  oauth: OAuthRefSchema.optional(),
@@ -10121,7 +10181,17 @@ async function canonicalWriterPath(target) {
10121
10181
  async function readOwner(lockDirectory) {
10122
10182
  const directory = await lstat(lockDirectory);
10123
10183
  if (!directory.isDirectory() || directory.isSymbolicLink()) throw unavailable();
10124
- 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
+ }
10125
10195
  if (names.length === 0) return void 0;
10126
10196
  const name = names[0];
10127
10197
  const match = markerPattern.exec(name);
@@ -10196,7 +10266,10 @@ async function reclaimDeadOwner(lockDirectory) {
10196
10266
  }
10197
10267
  }
10198
10268
  /** A local-host writer claim. It never expires based on age or missed heartbeats. */
10199
- 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;
10200
10273
  let staged;
10201
10274
  let name;
10202
10275
  try {
@@ -10215,7 +10288,8 @@ async function acquireSessionWriter(sessionDirectory) {
10215
10288
  flag: "wx",
10216
10289
  mode: 384
10217
10290
  });
10218
- for (let attempt = 0; attempt < 8; attempt += 1) {
10291
+ let unownedCollisions = 0;
10292
+ for (;;) {
10219
10293
  try {
10220
10294
  await rename(staged, lockDirectory);
10221
10295
  } catch (error) {
@@ -10225,7 +10299,19 @@ async function acquireSessionWriter(sessionDirectory) {
10225
10299
  "EPERM",
10226
10300
  "EACCES"
10227
10301
  ].includes(codeOf(error) ?? "")) throw error;
10228
- 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();
10229
10315
  continue;
10230
10316
  }
10231
10317
  let released = false;
@@ -10248,7 +10334,6 @@ async function acquireSessionWriter(sessionDirectory) {
10248
10334
  }
10249
10335
  };
10250
10336
  }
10251
- throw unavailable();
10252
10337
  } catch (error) {
10253
10338
  if (error instanceof BlunError) throw error;
10254
10339
  throw unavailable(error);
@@ -10318,7 +10403,8 @@ function tokenToWire(token) {
10318
10403
  expires_at: token.expiresAt,
10319
10404
  scope: token.scope,
10320
10405
  token_type: token.tokenType,
10321
- expires_in: token.expiresIn
10406
+ expires_in: token.expiresIn,
10407
+ ...token.memoryWorkspaceId === void 0 ? {} : { memory_workspace_id: token.memoryWorkspaceId }
10322
10408
  };
10323
10409
  }
10324
10410
  function tokenFromWire(wire) {
@@ -10328,7 +10414,8 @@ function tokenFromWire(wire) {
10328
10414
  expiresAt: typeof wire.expires_at === "number" ? wire.expires_at : 0,
10329
10415
  scope: wire.scope ?? "",
10330
10416
  tokenType: wire.token_type ?? "",
10331
- expiresIn: typeof wire.expires_in === "number" ? wire.expires_in : 0
10417
+ expiresIn: typeof wire.expires_in === "number" ? wire.expires_in : 0,
10418
+ ...typeof wire.memory_workspace_id === "string" && wire.memory_workspace_id.length > 0 ? { memoryWorkspaceId: wire.memory_workspace_id } : {}
10332
10419
  };
10333
10420
  }
10334
10421
  //#endregion
@@ -12719,6 +12806,27 @@ var OAuthManager = class {
12719
12806
  const state = await this.loadState();
12720
12807
  return state.kind === "valid" ? state.token.accessToken : void 0;
12721
12808
  }
12809
+ async getCachedMemoryWorkspaceId() {
12810
+ const state = await this.loadState();
12811
+ return state.kind === "valid" && state.token.expiresAt > this.now() ? state.token.memoryWorkspaceId : void 0;
12812
+ }
12813
+ async cacheMemoryWorkspaceId(workspaceId) {
12814
+ if (workspaceId.length === 0) return false;
12815
+ const release = await this.acquireRefreshLock();
12816
+ try {
12817
+ const state = await this.loadState();
12818
+ if (state.kind !== "valid" || state.token.expiresAt <= this.now()) return false;
12819
+ if (state.token.memoryWorkspaceId === workspaceId) return true;
12820
+ if (state.token.memoryWorkspaceId !== void 0) return false;
12821
+ await this.storage.save(this.config.name, {
12822
+ ...state.token,
12823
+ memoryWorkspaceId: workspaceId
12824
+ });
12825
+ return true;
12826
+ } finally {
12827
+ await release();
12828
+ }
12829
+ }
12722
12830
  async logout() {
12723
12831
  await this.inFlightRefresh?.promise.catch(() => void 0);
12724
12832
  const release = await this.acquireRefreshLock();
@@ -12786,7 +12894,10 @@ var OAuthManager = class {
12786
12894
  if (activeToken.refreshToken.length === 0) throw new OAuthUnauthorizedError(`Token for "${this.config.name}" has no refresh_token; re-login required.`);
12787
12895
  try {
12788
12896
  const refreshed = await this.refreshImpl(this.config, activeToken.refreshToken);
12789
- await this.storage.save(this.config.name, refreshed);
12897
+ await this.storage.save(this.config.name, {
12898
+ ...refreshed,
12899
+ memoryWorkspaceId: activeToken.memoryWorkspaceId
12900
+ });
12790
12901
  this.notifyRefresh({ success: true });
12791
12902
  return refreshed.accessToken;
12792
12903
  } catch (error) {
@@ -14163,6 +14274,16 @@ var BlunOAuthToolkit = class {
14163
14274
  hasToken: await this.managerFor(name, oauthKey, oauthHost).hasToken()
14164
14275
  }] };
14165
14276
  }
14277
+ async getCachedMemoryWorkspaceId(oauthRef) {
14278
+ const oauthHost = this.oauthHostFor(oauthRef);
14279
+ const oauthKey = oauthRef?.key ?? this.defaultOAuthKey(void 0, oauthHost);
14280
+ return this.managerFor(BLUN_PROVIDER_NAME$1, oauthKey, oauthHost).getCachedMemoryWorkspaceId();
14281
+ }
14282
+ async cacheMemoryWorkspaceId(workspaceId, oauthRef) {
14283
+ const oauthHost = this.oauthHostFor(oauthRef);
14284
+ const oauthKey = oauthRef?.key ?? this.defaultOAuthKey(void 0, oauthHost);
14285
+ return this.managerFor(BLUN_PROVIDER_NAME$1, oauthKey, oauthHost).cacheMemoryWorkspaceId(workspaceId);
14286
+ }
14166
14287
  async login(providerName, options = {}) {
14167
14288
  const name = providerName ?? "managed:blun";
14168
14289
  const oauthHost = this.oauthHostFor(options.oauthRef, options.oauthHost);
@@ -16230,6 +16351,8 @@ function providerToToml(provider, rawProvider) {
16230
16351
  for (const key of [
16231
16352
  "type",
16232
16353
  "apiKey",
16354
+ "memoryWorkspaceId",
16355
+ "memoryApiKeyHash",
16233
16356
  "baseUrl",
16234
16357
  "defaultModel",
16235
16358
  "oauth",
@@ -16375,16 +16498,11 @@ function configWriterError(error) {
16375
16498
  return error;
16376
16499
  }
16377
16500
  async function acquireConfigWriter(filePath) {
16378
- const deadline = performance.now() + 5e3;
16379
- for (;;) try {
16380
- return await acquireSessionWriter(filePath);
16501
+ try {
16502
+ return await acquireSessionWriter(filePath, { waitForBusyMs: 5e3 });
16381
16503
  } catch (error) {
16382
16504
  if (!(error instanceof BlunError) || error.code !== ErrorCodes.SESSION_WRITER_BUSY) throw error;
16383
- const remaining = deadline - performance.now();
16384
- if (remaining <= 0) throw new BlunError(ErrorCodes.CONFIG_WRITE_BUSY, "Another configuration change is still being saved", { cause: error });
16385
- await new Promise((resolve) => {
16386
- setTimeout(resolve, Math.min(25, remaining));
16387
- });
16505
+ throw new BlunError(ErrorCodes.CONFIG_WRITE_BUSY, "Another configuration change is still being saved", { cause: error });
16388
16506
  }
16389
16507
  }
16390
16508
  /** Keep transforms synchronous; credential validation and discovery belong before this boundary. */
@@ -21678,6 +21796,31 @@ var InstructionText = class InstructionText {
21678
21796
  get content() {
21679
21797
  return this.pieces.map((piece) => piece.text).join("");
21680
21798
  }
21799
+ prefixUtf8(maxBytes) {
21800
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) throw new RangeError("Invalid instruction byte budget");
21801
+ const kept = [];
21802
+ let remaining = maxBytes;
21803
+ let totalBytes = 0;
21804
+ let readBytes = 0;
21805
+ for (const piece of this.pieces) {
21806
+ const encoded = Buffer.from(piece.text, "utf8");
21807
+ totalBytes += encoded.length;
21808
+ if (remaining <= 0) continue;
21809
+ const content = encoded.length <= remaining ? piece.text : new StringDecoder("utf8").write(encoded.subarray(0, remaining));
21810
+ const bytes = Buffer.byteLength(content, "utf8");
21811
+ if (content.length > 0) kept.push({
21812
+ ...piece,
21813
+ text: content
21814
+ });
21815
+ readBytes += bytes;
21816
+ remaining -= bytes;
21817
+ if (bytes < encoded.length) remaining = 0;
21818
+ }
21819
+ return {
21820
+ text: new InstructionText(kept, new Map(this.sources)),
21821
+ omittedBytes: totalBytes - readBytes
21822
+ };
21823
+ }
21681
21824
  replaceFirst(needle, replacement) {
21682
21825
  const start = this.content.indexOf(needle);
21683
21826
  if (start >= 0) this.replaceRange(start, start + needle.length, replacement);
@@ -21735,6 +21878,64 @@ const IMPORT_MAX_DEPTH = 4;
21735
21878
  const IMPORT_LINE_RE = /^@import\s+(.+)$/gm;
21736
21879
  const S_IFMT$4 = 61440;
21737
21880
  const S_IFREG$2 = 32768;
21881
+ function fitAgentsMdContext(context, maxFixedRequestBytes, measureFixedRequestBytes) {
21882
+ if (!Number.isSafeInteger(maxFixedRequestBytes) || maxFixedRequestBytes <= 0) throw new RangeError("Invalid fixed request byte budget");
21883
+ const originalBytes = measureFixedRequestBytes(context);
21884
+ if (originalBytes <= maxFixedRequestBytes) return {
21885
+ context,
21886
+ fixedRequestBytes: originalBytes,
21887
+ omittedBytes: 0,
21888
+ fits: true
21889
+ };
21890
+ const source = context.agentsMdText;
21891
+ if (source === void 0) return {
21892
+ context,
21893
+ fixedRequestBytes: originalBytes,
21894
+ omittedBytes: 0,
21895
+ fits: false
21896
+ };
21897
+ const totalBytes = source.measure().totalBytes;
21898
+ const candidateAt = (maxBytes) => {
21899
+ const { text } = source.prefixUtf8(maxBytes);
21900
+ return {
21901
+ ...context,
21902
+ agentsMd: text.content,
21903
+ agentsMdSources: text.measure(),
21904
+ agentsMdText: text
21905
+ };
21906
+ };
21907
+ let low = 0;
21908
+ let high = totalBytes;
21909
+ let fitted = candidateAt(0);
21910
+ let fittedBytes = measureFixedRequestBytes(fitted);
21911
+ if (fittedBytes > maxFixedRequestBytes) {
21912
+ const omittedBytes = context.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? totalBytes;
21913
+ return {
21914
+ context: fitted,
21915
+ fixedRequestBytes: fittedBytes,
21916
+ omittedBytes,
21917
+ fits: false
21918
+ };
21919
+ }
21920
+ while (low < high) {
21921
+ const middle = Math.ceil((low + high) / 2);
21922
+ const candidate = candidateAt(middle);
21923
+ const bytes = measureFixedRequestBytes(candidate);
21924
+ if (bytes <= maxFixedRequestBytes) {
21925
+ low = middle;
21926
+ fitted = candidate;
21927
+ fittedBytes = bytes;
21928
+ } else high = middle - 1;
21929
+ }
21930
+ const omittedBytes = context.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? totalBytes;
21931
+ const retainedBytes = fitted.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? 0;
21932
+ return {
21933
+ context: fitted,
21934
+ fixedRequestBytes: fittedBytes,
21935
+ omittedBytes: omittedBytes - retainedBytes,
21936
+ fits: true
21937
+ };
21938
+ }
21738
21939
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21739
21940
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
21740
21941
  const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
@@ -21747,7 +21948,8 @@ async function prepareSystemPromptContext(kaos, brandHome, options) {
21747
21948
  agentsMd: agentsMdResult.content,
21748
21949
  additionalDirsInfo,
21749
21950
  agentsMdWarning: agentsMdResult.warning,
21750
- agentsMdSources: agentsMdResult.sources
21951
+ agentsMdSources: agentsMdResult.sources,
21952
+ agentsMdText: agentsMdResult.text
21751
21953
  };
21752
21954
  }
21753
21955
  async function loadAgentsMd(kaos, brandHome) {
@@ -21757,10 +21959,15 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21757
21959
  const discovered = [];
21758
21960
  const emptySources = /* @__PURE__ */ new Map();
21759
21961
  const seen = /* @__PURE__ */ new Set();
21962
+ const notLoadedCandidates = /* @__PURE__ */ new Set();
21760
21963
  const collect = async (path) => {
21761
21964
  const file = await readAgentFile(kaos, path);
21762
- if (file === void 0) return false;
21965
+ if (file === void 0) {
21966
+ notLoadedCandidates.add(kaos.normpath(path));
21967
+ return false;
21968
+ }
21763
21969
  const key = kaos.normpath(file.path);
21970
+ notLoadedCandidates.delete(key);
21764
21971
  if (file.content.length === 0) {
21765
21972
  emptySources.set(key, InstructionText.file(file.path, "", file.sourceTextBytes));
21766
21973
  return false;
@@ -21796,7 +22003,11 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21796
22003
  return {
21797
22004
  content,
21798
22005
  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()
22006
+ text: rendered,
22007
+ sources: {
22008
+ ...rendered.measure(),
22009
+ ...notLoadedCandidates.size === 0 ? {} : { notLoadedCandidates: [...notLoadedCandidates] }
22010
+ }
21800
22011
  };
21801
22012
  }
21802
22013
  async function loadAdditionalDirsInfo(kaos, additionalDirs) {
@@ -28778,17 +28989,20 @@ function createSystemPromptRenderer(merged) {
28778
28989
  const counts = renderPromptContributions(merged.systemPromptTemplate, vars, rendered, true) ?? renderPromptContributions(merged.systemPromptTemplate, vars, rendered);
28779
28990
  const sources = context.agentsMdSources;
28780
28991
  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;
28992
+ 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;
28993
+ const notLoadedCandidates = sourceMatches && sources.notLoadedCandidates !== void 0 ? { notLoadedCandidates: [...sources.notLoadedCandidates] } : {};
28782
28994
  const instructionSources = copies === 0 ? {
28783
28995
  version: 1,
28784
28996
  totalBytes: 0,
28785
28997
  generatedBytes: 0,
28786
- files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : []
28998
+ files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : [],
28999
+ ...notLoadedCandidates
28787
29000
  } : !sourceMatches ? void 0 : {
28788
29001
  version: 1,
28789
29002
  totalBytes: sources.totalBytes * copies,
28790
29003
  generatedBytes: sources.generatedBytes * copies,
28791
- files: sources.files.map((file) => copyInstructionFileBytes(file, copies))
29004
+ files: sources.files.map((file) => copyInstructionFileBytes(file, copies)),
29005
+ ...notLoadedCandidates
28792
29006
  };
28793
29007
  const skillListingSources = counts === void 0 ? void 0 : scaleSkillListingSources(skillSources, vars["BLUN_SKILLS"] ?? "", counts.skillListingBytes);
28794
29008
  context.onContributions(counts === void 0 ? void 0 : {
@@ -29811,7 +30025,7 @@ var FileSystemAgentRecordPersistence = class {
29811
30025
  async *readFrom(offset, recordCount) {
29812
30026
  if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(recordCount) || recordCount < 0) throw new Error("Invalid Wire replay offset");
29813
30027
  await this.flush();
29814
- let line = "";
30028
+ const fragments = [];
29815
30029
  let lineNumber = recordCount;
29816
30030
  let byteLength = offset;
29817
30031
  const stream = createReadStream(this.filePath, {
@@ -29820,29 +30034,36 @@ var FileSystemAgentRecordPersistence = class {
29820
30034
  });
29821
30035
  try {
29822
30036
  for await (const chunk of stream) {
29823
- line += chunk;
29824
- let newlineIndex = line.indexOf("\n");
30037
+ let start = 0;
30038
+ let newlineIndex = chunk.indexOf("\n", start);
29825
30039
  while (newlineIndex !== -1) {
29826
- const rawLine = line.slice(0, newlineIndex);
29827
- 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
+ }
29828
30047
  lineNumber++;
29829
30048
  byteLength += Buffer.byteLength(rawLine, "utf8") + 1;
29830
30049
  const record = parseRecordLine(rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine, lineNumber, this.filePath, false);
29831
30050
  if (record !== void 0) yield record;
29832
- newlineIndex = line.indexOf("\n");
30051
+ start = newlineIndex + 1;
30052
+ newlineIndex = chunk.indexOf("\n", start);
29833
30053
  }
30054
+ if (start < chunk.length) fragments.push(chunk.slice(start));
29834
30055
  }
29835
30056
  } catch (error) {
29836
30057
  if (error.code === "ENOENT") return;
29837
30058
  throw error;
29838
30059
  }
29839
- if (line.length > 0) {
30060
+ if (fragments.length > 0) {
29840
30061
  lineNumber++;
29841
- const record = parseRecordLine(line, lineNumber, this.filePath, true);
30062
+ const record = parseRecordLine(fragments.join(""), lineNumber, this.filePath, true);
29842
30063
  if (record !== void 0) yield record;
29843
30064
  }
29844
30065
  this.persistedRecordCount = lineNumber;
29845
- this.persistedByteLength = line.length === 0 ? byteLength : void 0;
30066
+ this.persistedByteLength = fragments.length === 0 ? byteLength : void 0;
29846
30067
  }
29847
30068
  append(input) {
29848
30069
  this.throwIfError();
@@ -180665,6 +180886,10 @@ var GoalMode = class {
180665
180886
  restoreUpdate(record) {
180666
180887
  const state = this.state;
180667
180888
  if (state === void 0) return;
180889
+ if (record.objective !== void 0) {
180890
+ state.objective = record.objective;
180891
+ this.lifecycleRevision += 1;
180892
+ }
180668
180893
  const status = record.status;
180669
180894
  if (status !== void 0) {
180670
180895
  this.lifecycleRevision += 1;
@@ -180679,7 +180904,17 @@ var GoalMode = class {
180679
180904
  state.wallClockResumedAt = void 0;
180680
180905
  }
180681
180906
  if (record.budgetLimits !== void 0) state.budgetLimits = record.budgetLimits;
180682
- if (status === void 0) return;
180907
+ if (status === void 0) {
180908
+ if (record.objective !== void 0) this.agent.replayBuilder.push({
180909
+ type: "goal_updated",
180910
+ snapshot: this.toSnapshot(state),
180911
+ change: {
180912
+ kind: "edited",
180913
+ actor: record.actor
180914
+ }
180915
+ });
180916
+ return;
180917
+ }
180683
180918
  this.agent.replayBuilder.push({
180684
180919
  type: "goal_updated",
180685
180920
  snapshot: this.toSnapshot(state),
@@ -180723,11 +180958,11 @@ var GoalMode = class {
180723
180958
  if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
180724
180959
  if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH} characters`);
180725
180960
  const verificationScope = normalizeVerificationScope(input.verificationScope);
180961
+ const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180726
180962
  if (this.state !== void 0) {
180727
180963
  if (input.replace !== true) throw new BlunError(ErrorCodes.GOAL_ALREADY_EXISTS, "A goal already exists; use replace to start a new one");
180728
180964
  this.clearInternal("system");
180729
180965
  }
180730
- const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180731
180966
  const state = {
180732
180967
  goalId: randomUUID(),
180733
180968
  objective,
@@ -180751,6 +180986,35 @@ var GoalMode = class {
180751
180986
  this.trackGoalCreated(actor, input.replace === true);
180752
180987
  return this.toSnapshot(state);
180753
180988
  }
180989
+ async completeGoal() {
180990
+ const state = this.requireState();
180991
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot complete a goal in status "${state.status}"`);
180992
+ return this.markComplete({}, "user");
180993
+ }
180994
+ async blockGoal(input = {}) {
180995
+ const state = this.requireState();
180996
+ if (state.status === "blocked") return this.toSnapshot(state);
180997
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot block a goal in status "${state.status}"`);
180998
+ return await this.markBlocked(input, "user");
180999
+ }
181000
+ async editGoal(input, actor = "user") {
181001
+ const state = this.requireState();
181002
+ const objective = input.objective.trim();
181003
+ if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
181004
+ if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH} characters`);
181005
+ if (objective === state.objective) return this.toSnapshot(state);
181006
+ state.objective = objective;
181007
+ this.lifecycleRevision += 1;
181008
+ this.appendGoalUpdate({
181009
+ objective,
181010
+ actor
181011
+ });
181012
+ this.persistState(state, { change: {
181013
+ kind: "edited",
181014
+ actor
181015
+ } });
181016
+ return this.toSnapshot(state);
181017
+ }
180754
181018
  async pauseGoal(input = {}, actor = "user") {
180755
181019
  const state = this.requireState();
180756
181020
  if (state.status === "paused") return this.toSnapshot(state);
@@ -181520,6 +181784,613 @@ var ProviderRetryWait = class {
181520
181784
  }
181521
181785
  }
181522
181786
  };
181787
+ const IMAGE_MIME_BY_SUFFIX = Object.freeze({
181788
+ ".png": "image/png",
181789
+ ".jpg": "image/jpeg",
181790
+ ".jpeg": "image/jpeg",
181791
+ ".gif": "image/gif",
181792
+ ".bmp": "image/bmp",
181793
+ ".tif": "image/tiff",
181794
+ ".tiff": "image/tiff",
181795
+ ".webp": "image/webp",
181796
+ ".ico": "image/x-icon",
181797
+ ".heic": "image/heic",
181798
+ ".heif": "image/heif",
181799
+ ".avif": "image/avif",
181800
+ ".svgz": "image/svg+xml"
181801
+ });
181802
+ const VIDEO_MIME_BY_SUFFIX = Object.freeze({
181803
+ ".mp4": "video/mp4",
181804
+ ".mpg": "video/mpeg",
181805
+ ".mpeg": "video/mpeg",
181806
+ ".mkv": "video/x-matroska",
181807
+ ".avi": "video/x-msvideo",
181808
+ ".mov": "video/quicktime",
181809
+ ".ogv": "video/ogg",
181810
+ ".wmv": "video/x-ms-wmv",
181811
+ ".webm": "video/webm",
181812
+ ".m4v": "video/x-m4v",
181813
+ ".flv": "video/x-flv",
181814
+ ".3gp": "video/3gpp",
181815
+ ".3g2": "video/3gpp2"
181816
+ });
181817
+ const TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
181818
+ const NON_TEXT_SUFFIXES = new Set([
181819
+ ".icns",
181820
+ ".psd",
181821
+ ".ai",
181822
+ ".eps",
181823
+ ".pdf",
181824
+ ".doc",
181825
+ ".docx",
181826
+ ".dot",
181827
+ ".dotx",
181828
+ ".rtf",
181829
+ ".odt",
181830
+ ".xls",
181831
+ ".xlsx",
181832
+ ".xlsm",
181833
+ ".xlt",
181834
+ ".xltx",
181835
+ ".xltm",
181836
+ ".ods",
181837
+ ".ppt",
181838
+ ".pptx",
181839
+ ".pptm",
181840
+ ".pps",
181841
+ ".ppsx",
181842
+ ".odp",
181843
+ ".pages",
181844
+ ".numbers",
181845
+ ".key",
181846
+ ".zip",
181847
+ ".rar",
181848
+ ".7z",
181849
+ ".tar",
181850
+ ".gz",
181851
+ ".tgz",
181852
+ ".bz2",
181853
+ ".xz",
181854
+ ".zst",
181855
+ ".lz",
181856
+ ".lz4",
181857
+ ".br",
181858
+ ".cab",
181859
+ ".ar",
181860
+ ".deb",
181861
+ ".rpm",
181862
+ ".mp3",
181863
+ ".wav",
181864
+ ".flac",
181865
+ ".ogg",
181866
+ ".oga",
181867
+ ".opus",
181868
+ ".aac",
181869
+ ".m4a",
181870
+ ".wma",
181871
+ ".ttf",
181872
+ ".otf",
181873
+ ".woff",
181874
+ ".woff2",
181875
+ ".exe",
181876
+ ".dll",
181877
+ ".so",
181878
+ ".dylib",
181879
+ ".bin",
181880
+ ".apk",
181881
+ ".ipa",
181882
+ ".jar",
181883
+ ".class",
181884
+ ".pyc",
181885
+ ".pyo",
181886
+ ".wasm",
181887
+ ".dmg",
181888
+ ".iso",
181889
+ ".img",
181890
+ ".sqlite",
181891
+ ".sqlite3",
181892
+ ".db",
181893
+ ".db3"
181894
+ ]);
181895
+ const ASF_HEADER = Buffer.from([
181896
+ 48,
181897
+ 38,
181898
+ 178,
181899
+ 117,
181900
+ 142,
181901
+ 102,
181902
+ 207,
181903
+ 17,
181904
+ 166,
181905
+ 217,
181906
+ 0,
181907
+ 170,
181908
+ 0,
181909
+ 98,
181910
+ 206,
181911
+ 108
181912
+ ]);
181913
+ const FTYP_IMAGE_BRANDS = Object.freeze({
181914
+ avif: "image/avif",
181915
+ avis: "image/avif",
181916
+ heic: "image/heic",
181917
+ heif: "image/heif",
181918
+ heix: "image/heif",
181919
+ hevc: "image/heic",
181920
+ mif1: "image/heif",
181921
+ msf1: "image/heif"
181922
+ });
181923
+ const FTYP_VIDEO_BRANDS = Object.freeze({
181924
+ isom: "video/mp4",
181925
+ iso2: "video/mp4",
181926
+ iso5: "video/mp4",
181927
+ mp41: "video/mp4",
181928
+ mp42: "video/mp4",
181929
+ avc1: "video/mp4",
181930
+ mp4v: "video/mp4",
181931
+ m4v: "video/x-m4v",
181932
+ qt: "video/quicktime",
181933
+ "3gp4": "video/3gpp",
181934
+ "3gp5": "video/3gpp",
181935
+ "3gp6": "video/3gpp",
181936
+ "3gp7": "video/3gpp",
181937
+ "3g2": "video/3gpp2"
181938
+ });
181939
+ function toBuffer(data) {
181940
+ return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
181941
+ }
181942
+ function startsWith(buf, prefix) {
181943
+ const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
181944
+ if (buf.length < needle.length) return false;
181945
+ for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
181946
+ return true;
181947
+ }
181948
+ function sniffFtypBrand(header) {
181949
+ if (header.length < 12) return null;
181950
+ if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
181951
+ return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
181952
+ }
181953
+ function sniffMediaFromMagic(data) {
181954
+ const buf = toBuffer(data);
181955
+ const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
181956
+ if (startsWith(header, [
181957
+ 137,
181958
+ 80,
181959
+ 78,
181960
+ 71,
181961
+ 13,
181962
+ 10,
181963
+ 26,
181964
+ 10
181965
+ ])) return {
181966
+ kind: "image",
181967
+ mimeType: "image/png"
181968
+ };
181969
+ if (startsWith(header, [
181970
+ 255,
181971
+ 216,
181972
+ 255
181973
+ ])) return {
181974
+ kind: "image",
181975
+ mimeType: "image/jpeg"
181976
+ };
181977
+ if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
181978
+ kind: "image",
181979
+ mimeType: "image/gif"
181980
+ };
181981
+ if (startsWith(header, Buffer.from("BM"))) return {
181982
+ kind: "image",
181983
+ mimeType: "image/bmp"
181984
+ };
181985
+ if (startsWith(header, [
181986
+ 73,
181987
+ 73,
181988
+ 42,
181989
+ 0
181990
+ ]) || startsWith(header, [
181991
+ 77,
181992
+ 77,
181993
+ 0,
181994
+ 42
181995
+ ])) return {
181996
+ kind: "image",
181997
+ mimeType: "image/tiff"
181998
+ };
181999
+ if (startsWith(header, [
182000
+ 0,
182001
+ 0,
182002
+ 1,
182003
+ 0
182004
+ ])) return {
182005
+ kind: "image",
182006
+ mimeType: "image/x-icon"
182007
+ };
182008
+ if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
182009
+ const chunk = header.subarray(8, 12).toString("latin1");
182010
+ if (chunk === "WEBP") return {
182011
+ kind: "image",
182012
+ mimeType: "image/webp"
182013
+ };
182014
+ if (chunk === "AVI ") return {
182015
+ kind: "video",
182016
+ mimeType: "video/x-msvideo"
182017
+ };
182018
+ }
182019
+ if (startsWith(header, Buffer.from("FLV"))) return {
182020
+ kind: "video",
182021
+ mimeType: "video/x-flv"
182022
+ };
182023
+ if (startsWith(header, ASF_HEADER)) return {
182024
+ kind: "video",
182025
+ mimeType: "video/x-ms-wmv"
182026
+ };
182027
+ if (startsWith(header, [
182028
+ 26,
182029
+ 69,
182030
+ 223,
182031
+ 163
182032
+ ])) {
182033
+ const lowered = header.toString("latin1").toLowerCase();
182034
+ if (lowered.includes("webm")) return {
182035
+ kind: "video",
182036
+ mimeType: "video/webm"
182037
+ };
182038
+ if (lowered.includes("matroska")) return {
182039
+ kind: "video",
182040
+ mimeType: "video/x-matroska"
182041
+ };
182042
+ }
182043
+ const brand = sniffFtypBrand(header);
182044
+ if (brand !== null && brand !== "") {
182045
+ if (brand in FTYP_IMAGE_BRANDS) return {
182046
+ kind: "image",
182047
+ mimeType: FTYP_IMAGE_BRANDS[brand]
182048
+ };
182049
+ if (brand in FTYP_VIDEO_BRANDS) return {
182050
+ kind: "video",
182051
+ mimeType: FTYP_VIDEO_BRANDS[brand]
182052
+ };
182053
+ }
182054
+ return null;
182055
+ }
182056
+ /**
182057
+ * Best-effort pixel-dimension reader for common raster formats.
182058
+ *
182059
+ * Inspects only the fixed region near the start of the file where each
182060
+ * format records its dimensions (the IHDR/DIB header, the RIFF chunk
182061
+ * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
182062
+ * for formats whose dimensions are not locatable from that region, or
182063
+ * when the supplied buffer is too short to cover it.
182064
+ */
182065
+ function sniffImageDimensions(data) {
182066
+ const buf = toBuffer(data);
182067
+ if (startsWith(buf, [
182068
+ 137,
182069
+ 80,
182070
+ 78,
182071
+ 71,
182072
+ 13,
182073
+ 10,
182074
+ 26,
182075
+ 10
182076
+ ]) && buf.length >= 24) return {
182077
+ width: buf.readUInt32BE(16),
182078
+ height: buf.readUInt32BE(20)
182079
+ };
182080
+ if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
182081
+ width: buf.readUInt16LE(6),
182082
+ height: buf.readUInt16LE(8)
182083
+ };
182084
+ if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
182085
+ width: buf.readInt32LE(18),
182086
+ height: Math.abs(buf.readInt32LE(22))
182087
+ };
182088
+ if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
182089
+ const fourCc = buf.subarray(12, 16).toString("latin1");
182090
+ if (fourCc === "VP8 ") return {
182091
+ width: buf.readUInt16LE(26) & 16383,
182092
+ height: buf.readUInt16LE(28) & 16383
182093
+ };
182094
+ if (fourCc === "VP8L" && buf.length >= 25) {
182095
+ const bits = buf.readUInt32LE(21);
182096
+ return {
182097
+ width: (bits & 16383) + 1,
182098
+ height: (bits >> 14 & 16383) + 1
182099
+ };
182100
+ }
182101
+ if (fourCc === "VP8X") return {
182102
+ width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
182103
+ height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
182104
+ };
182105
+ }
182106
+ if (startsWith(buf, [255, 216])) {
182107
+ let offset = 2;
182108
+ while (offset + 9 < buf.length) {
182109
+ if (buf[offset] !== 255) {
182110
+ offset += 1;
182111
+ continue;
182112
+ }
182113
+ const marker = buf[offset + 1];
182114
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
182115
+ height: buf.readUInt16BE(offset + 5),
182116
+ width: buf.readUInt16BE(offset + 7)
182117
+ };
182118
+ if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
182119
+ offset += 2;
182120
+ continue;
182121
+ }
182122
+ const segmentLength = buf.readUInt16BE(offset + 2);
182123
+ if (segmentLength < 2) break;
182124
+ offset += 2 + segmentLength;
182125
+ }
182126
+ }
182127
+ return null;
182128
+ }
182129
+ function getSuffix(path) {
182130
+ const idx = path.lastIndexOf(".");
182131
+ if (idx === -1) return "";
182132
+ if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
182133
+ return path.slice(idx).toLowerCase();
182134
+ }
182135
+ function detectFileType(path, header, type = "text") {
182136
+ const suffix = getSuffix(path);
182137
+ let mediaHint = null;
182138
+ if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
182139
+ kind: "text",
182140
+ mimeType: TEXT_MIME_BY_SUFFIX[suffix]
182141
+ };
182142
+ else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
182143
+ kind: "image",
182144
+ mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
182145
+ };
182146
+ else if (suffix in VIDEO_MIME_BY_SUFFIX) mediaHint = {
182147
+ kind: "video",
182148
+ mimeType: VIDEO_MIME_BY_SUFFIX[suffix]
182149
+ };
182150
+ if (header !== void 0) {
182151
+ const buf = toBuffer(header);
182152
+ const sniffed = sniffMediaFromMagic(buf);
182153
+ if (sniffed) {
182154
+ if (type === "media") return sniffed;
182155
+ if (mediaHint) {
182156
+ if (sniffed.kind !== mediaHint.kind) return {
182157
+ kind: "unknown",
182158
+ mimeType: ""
182159
+ };
182160
+ return mediaHint;
182161
+ }
182162
+ return sniffed;
182163
+ }
182164
+ if (mediaHint?.kind === "image") return {
182165
+ kind: "unknown",
182166
+ mimeType: ""
182167
+ };
182168
+ if (type === "media" && mediaHint?.kind === "video") return mediaHint;
182169
+ if (buf.includes(0)) return {
182170
+ kind: "unknown",
182171
+ mimeType: ""
182172
+ };
182173
+ }
182174
+ if (mediaHint) return mediaHint;
182175
+ if (NON_TEXT_SUFFIXES.has(suffix)) return {
182176
+ kind: "unknown",
182177
+ mimeType: ""
182178
+ };
182179
+ return {
182180
+ kind: "text",
182181
+ mimeType: "text/plain"
182182
+ };
182183
+ }
182184
+ //#endregion
182185
+ //#region ../../packages/agent-core/src/utils/tokens.ts
182186
+ const messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
182187
+ /** Open assistant messages grow while streaming; completed history stays cached. */
182188
+ function invalidateMessageTokenEstimate(message) {
182189
+ messageTokenEstimateCache.delete(message);
182190
+ }
182191
+ /**
182192
+ * Estimate token count from text using a character-based heuristic.
182193
+ * - ASCII (~4 chars per token)
182194
+ * - CJK and other non-ASCII (~1 char per token)
182195
+ * The estimate is transient — the next LLM call returns the real count
182196
+ * and supersedes this value. Used to keep `tokenCountWithPending`
182197
+ * monotonic between LLM round-trips without paying for a tokenizer.
182198
+ */
182199
+ function estimateTokens(text) {
182200
+ let asciiCount = 0;
182201
+ let nonAsciiCount = 0;
182202
+ for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
182203
+ else nonAsciiCount++;
182204
+ return Math.ceil(asciiCount / 4) + nonAsciiCount;
182205
+ }
182206
+ function estimateTokensForMessages(messages) {
182207
+ let total = 0;
182208
+ for (const message of messages) total += estimateTokensForMessage(message);
182209
+ return total;
182210
+ }
182211
+ /** With stopAfter, the result is a lower bound once it exceeds that budget. */
182212
+ function estimateTokensForTools(tools, stopAfter = Infinity) {
182213
+ let total = 0;
182214
+ for (const tool of tools) {
182215
+ total += estimateTokens(tool.name);
182216
+ total += estimateTokens(tool.description);
182217
+ total += estimateTokens(JSON.stringify(tool.parameters));
182218
+ if (total > stopAfter) return total;
182219
+ }
182220
+ return total;
182221
+ }
182222
+ function estimateTokensForMessage(message) {
182223
+ const cached = messageTokenEstimateCache.get(message);
182224
+ if (cached !== void 0) return cached;
182225
+ let total = estimateTokens(message.role);
182226
+ total += estimateTokensForContentParts(message.content);
182227
+ if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
182228
+ total += estimateTokens(call.name);
182229
+ total += estimateTokens(JSON.stringify(call.arguments));
182230
+ }
182231
+ messageTokenEstimateCache.set(message, total);
182232
+ return total;
182233
+ }
182234
+ function estimateTokensForContentParts(parts) {
182235
+ let total = 0;
182236
+ for (const part of parts) total += estimateTokensForContentPart(part);
182237
+ return total;
182238
+ }
182239
+ /**
182240
+ * Conservative transient fallback for media whose pixel dimensions or duration
182241
+ * are unavailable. Inline raster images use their transmitted dimensions below;
182242
+ * audio, video, remote images, and malformed image data keep this safe floor.
182243
+ * The next successful LLM round-trip replaces the estimate with reported usage.
182244
+ */
182245
+ const MEDIA_TOKEN_ESTIMATE = 2e3;
182246
+ /** Enough header data for common JPEG metadata followed by the first SOF marker. */
182247
+ const MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
182248
+ function imageDimensionsFromDataUrl(url) {
182249
+ const commaIndex = url.indexOf(",");
182250
+ if (commaIndex <= 0) return null;
182251
+ const metadata = url.slice(0, commaIndex).toLowerCase();
182252
+ if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
182253
+ const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
182254
+ const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
182255
+ if (encodedHeader.length === 0) return null;
182256
+ try {
182257
+ const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
182258
+ if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
182259
+ return dimensions;
182260
+ } catch {
182261
+ return null;
182262
+ }
182263
+ }
182264
+ function estimateImageTokensFromDimensions(width, height) {
182265
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
182266
+ const patchSize = BigInt(32);
182267
+ const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
182268
+ return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
182269
+ }
182270
+ function estimateImageTokens(url) {
182271
+ const dimensions = imageDimensionsFromDataUrl(url);
182272
+ return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
182273
+ }
182274
+ function estimateTokensForContentPart(part) {
182275
+ switch (part.type) {
182276
+ case "text": return estimateTokens(part.text);
182277
+ case "think": return estimateTokens(part.think);
182278
+ case "image_url": return estimateImageTokens(part.imageUrl.url);
182279
+ case "audio_url":
182280
+ case "video_url": return MEDIA_TOKEN_ESTIMATE;
182281
+ default: return 0;
182282
+ }
182283
+ }
182284
+ const MAX_TOOL_CONTINUITY_SERIALIZED_BYTES = 4096;
182285
+ function saturatingAdd(left, right) {
182286
+ return left + Math.min(right, Number.MAX_SAFE_INTEGER - left);
182287
+ }
182288
+ function renderUnchecked(value) {
182289
+ return [
182290
+ "",
182291
+ "",
182292
+ "<tool-continuity>",
182293
+ "Observed tool-result flags; may be synthetic, not verified job success or permission. omitted counts excluded observations cumulatively, not jobs.",
182294
+ JSON.stringify(value).replaceAll(/[<>&]/g, (character) => {
182295
+ return `\\u${character.codePointAt(0).toString(16).padStart(4, "0")}`;
182296
+ }),
182297
+ "</tool-continuity>"
182298
+ ].join("\n");
182299
+ }
182300
+ function hasExactKeys(value, expected) {
182301
+ const prototype = Object.getPrototypeOf(value);
182302
+ if (prototype !== Object.prototype && prototype !== null) return false;
182303
+ return Reflect.ownKeys(value).length === expected.length && expected.every((key) => {
182304
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
182305
+ return descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value");
182306
+ });
182307
+ }
182308
+ function isDataArray(value) {
182309
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > 8 || Reflect.ownKeys(value).length !== value.length + 1) return false;
182310
+ for (let index = 0; index < value.length; index++) {
182311
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
182312
+ if (descriptor === void 0 || descriptor.enumerable !== true || !Object.hasOwn(descriptor, "value")) return false;
182313
+ }
182314
+ return true;
182315
+ }
182316
+ /** Validate restored metadata without interpreting any summarizer or tool prose. */
182317
+ function isToolContinuity(value) {
182318
+ try {
182319
+ if (value === null || typeof value !== "object" || !hasExactKeys(value, ["receipts", "omitted"])) return false;
182320
+ const candidate = value;
182321
+ if (!Number.isSafeInteger(candidate["omitted"]) || candidate["omitted"] < 0 || !isDataArray(candidate["receipts"]) || candidate["receipts"].length > 8) return false;
182322
+ for (const receipt of candidate["receipts"]) if (receipt === null || typeof receipt !== "object" || !hasExactKeys(receipt, ["toolCallId", "observedIsError"]) || !("toolCallId" in receipt) || !("observedIsError" in receipt) || typeof receipt.toolCallId !== "string" || receipt.toolCallId.length === 0 || Buffer.byteLength(receipt.toolCallId, "utf8") > 256 || receipt.observedIsError !== null && typeof receipt.observedIsError !== "boolean") return false;
182323
+ return Buffer.byteLength(JSON.stringify(renderUnchecked(value)), "utf8") <= MAX_TOOL_CONTINUITY_SERIALIZED_BYTES;
182324
+ } catch {
182325
+ return false;
182326
+ }
182327
+ }
182328
+ function renderToolContinuity(value) {
182329
+ if (!isToolContinuity(value) || value.receipts.length === 0 && value.omitted === 0) return void 0;
182330
+ return renderUnchecked(value);
182331
+ }
182332
+ /** The caller passes only the compactable prefix, excluding the preserved tool suffix. */
182333
+ function collectToolContinuity(messages) {
182334
+ const observed = /* @__PURE__ */ new Map();
182335
+ let ordinal = 0;
182336
+ let inheritedOmitted = 0;
182337
+ let excluded = 0;
182338
+ const remember = (toolCallId, observedIsError) => {
182339
+ if (toolCallId.length === 0 || Buffer.byteLength(toolCallId, "utf8") > 256) {
182340
+ excluded = saturatingAdd(excluded, 1);
182341
+ return;
182342
+ }
182343
+ const key = JSON.stringify([toolCallId, observedIsError]);
182344
+ observed.set(key, {
182345
+ toolCallId,
182346
+ observedIsError,
182347
+ ordinal: ordinal++
182348
+ });
182349
+ };
182350
+ for (const message of messages) if (message.role === "tool" && typeof message.toolCallId === "string") remember(message.toolCallId, typeof message.isError === "boolean" ? message.isError : null);
182351
+ else if (message.role === "user" && message.origin?.kind === "compaction_summary") {
182352
+ const previous = message.origin.toolContinuity;
182353
+ if (!isToolContinuity(previous)) continue;
182354
+ for (const receipt of previous.receipts.toReversed()) remember(receipt.toolCallId, receipt.observedIsError);
182355
+ inheritedOmitted = Math.max(inheritedOmitted, previous.omitted);
182356
+ }
182357
+ const prioritized = [...observed.values()].toSorted((left, right) => Number(right.observedIsError === true) - Number(left.observedIsError === true) || right.ordinal - left.ordinal);
182358
+ const receipts = prioritized.slice(0, 8).map(({ toolCallId, observedIsError }) => ({
182359
+ toolCallId,
182360
+ observedIsError
182361
+ }));
182362
+ let omitted = saturatingAdd(saturatingAdd(inheritedOmitted, excluded), prioritized.length - receipts.length);
182363
+ while (true) {
182364
+ const result = {
182365
+ receipts: [...receipts],
182366
+ omitted
182367
+ };
182368
+ if (renderToolContinuity(result) !== void 0) return result;
182369
+ if (receipts.length === 0) return void 0;
182370
+ receipts.pop();
182371
+ omitted = saturatingAdd(omitted, 1);
182372
+ }
182373
+ }
182374
+ /** Fit complete tuples and their disclaimer; never start a new failure or retry path. */
182375
+ function fitToolContinuity(value, additionalTokenBudget) {
182376
+ if (value === void 0 || !isToolContinuity(value) || !(additionalTokenBudget > 0)) return void 0;
182377
+ const receipts = [...value.receipts];
182378
+ let omitted = value.omitted;
182379
+ while (true) {
182380
+ const continuity = {
182381
+ receipts: [...receipts],
182382
+ omitted
182383
+ };
182384
+ const text = renderToolContinuity(continuity);
182385
+ if (text !== void 0 && estimateTokens(text) <= additionalTokenBudget) return {
182386
+ continuity,
182387
+ text
182388
+ };
182389
+ if (receipts.length === 0) return void 0;
182390
+ receipts.pop();
182391
+ omitted = saturatingAdd(omitted, 1);
182392
+ }
182393
+ }
181523
182394
  //#endregion
181524
182395
  //#region ../../packages/agent-core/src/agent/records/resume-snapshot.ts
181525
182396
  const integer$1 = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
@@ -181614,6 +182485,7 @@ const stateRecordSchema = union([
181614
182485
  verificationScope: _enum(GOAL_VERIFICATION_SCOPES).optional()
181615
182486
  }),
181616
182487
  record$1("goal.update", {
182488
+ objective: text.min(1).max(4e3).regex(/\S/).optional(),
181617
182489
  status: _enum([
181618
182490
  "active",
181619
182491
  "paused",
@@ -181665,7 +182537,7 @@ const originCase = (kind, shape = {}) => object({
181665
182537
  }).passthrough();
181666
182538
  const origin = union([
181667
182539
  originCase("user"),
181668
- originCase("compaction_summary"),
182540
+ originCase("compaction_summary", { toolContinuity: custom$1(isToolContinuity).optional() }),
181669
182541
  originCase("skill_activation", {
181670
182542
  activationId: text,
181671
182543
  skillName: text,
@@ -182016,6 +182888,7 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182016
182888
  if (Buffer.byteLength(bytes) > MAX_CHECKPOINT_BYTES) return false;
182017
182889
  const target = resumeCheckpointPath(wirePath);
182018
182890
  const temporary = `${target}.tmp-${process.pid}-${randomBytes(8).toString("hex")}`;
182891
+ let renamed = false;
182019
182892
  try {
182020
182893
  const output = await open(temporary, "wx", 384);
182021
182894
  try {
@@ -182025,10 +182898,11 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182025
182898
  await output.close();
182026
182899
  }
182027
182900
  await rename(temporary, target);
182901
+ renamed = true;
182028
182902
  await syncDir(dirname$3(target));
182029
182903
  return true;
182030
182904
  } finally {
182031
- await rm(temporary, { force: true }).catch(() => {});
182905
+ if (!renamed) await rm(temporary, { force: true }).catch(() => {});
182032
182906
  }
182033
182907
  }
182034
182908
  //#endregion
@@ -185365,501 +186239,6 @@ var TodoListTool = class {
185365
186239
  })));
185366
186240
  }
185367
186241
  };
185368
- const IMAGE_MIME_BY_SUFFIX = Object.freeze({
185369
- ".png": "image/png",
185370
- ".jpg": "image/jpeg",
185371
- ".jpeg": "image/jpeg",
185372
- ".gif": "image/gif",
185373
- ".bmp": "image/bmp",
185374
- ".tif": "image/tiff",
185375
- ".tiff": "image/tiff",
185376
- ".webp": "image/webp",
185377
- ".ico": "image/x-icon",
185378
- ".heic": "image/heic",
185379
- ".heif": "image/heif",
185380
- ".avif": "image/avif",
185381
- ".svgz": "image/svg+xml"
185382
- });
185383
- const VIDEO_MIME_BY_SUFFIX = Object.freeze({
185384
- ".mp4": "video/mp4",
185385
- ".mpg": "video/mpeg",
185386
- ".mpeg": "video/mpeg",
185387
- ".mkv": "video/x-matroska",
185388
- ".avi": "video/x-msvideo",
185389
- ".mov": "video/quicktime",
185390
- ".ogv": "video/ogg",
185391
- ".wmv": "video/x-ms-wmv",
185392
- ".webm": "video/webm",
185393
- ".m4v": "video/x-m4v",
185394
- ".flv": "video/x-flv",
185395
- ".3gp": "video/3gpp",
185396
- ".3g2": "video/3gpp2"
185397
- });
185398
- const TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
185399
- const NON_TEXT_SUFFIXES = new Set([
185400
- ".icns",
185401
- ".psd",
185402
- ".ai",
185403
- ".eps",
185404
- ".pdf",
185405
- ".doc",
185406
- ".docx",
185407
- ".dot",
185408
- ".dotx",
185409
- ".rtf",
185410
- ".odt",
185411
- ".xls",
185412
- ".xlsx",
185413
- ".xlsm",
185414
- ".xlt",
185415
- ".xltx",
185416
- ".xltm",
185417
- ".ods",
185418
- ".ppt",
185419
- ".pptx",
185420
- ".pptm",
185421
- ".pps",
185422
- ".ppsx",
185423
- ".odp",
185424
- ".pages",
185425
- ".numbers",
185426
- ".key",
185427
- ".zip",
185428
- ".rar",
185429
- ".7z",
185430
- ".tar",
185431
- ".gz",
185432
- ".tgz",
185433
- ".bz2",
185434
- ".xz",
185435
- ".zst",
185436
- ".lz",
185437
- ".lz4",
185438
- ".br",
185439
- ".cab",
185440
- ".ar",
185441
- ".deb",
185442
- ".rpm",
185443
- ".mp3",
185444
- ".wav",
185445
- ".flac",
185446
- ".ogg",
185447
- ".oga",
185448
- ".opus",
185449
- ".aac",
185450
- ".m4a",
185451
- ".wma",
185452
- ".ttf",
185453
- ".otf",
185454
- ".woff",
185455
- ".woff2",
185456
- ".exe",
185457
- ".dll",
185458
- ".so",
185459
- ".dylib",
185460
- ".bin",
185461
- ".apk",
185462
- ".ipa",
185463
- ".jar",
185464
- ".class",
185465
- ".pyc",
185466
- ".pyo",
185467
- ".wasm",
185468
- ".dmg",
185469
- ".iso",
185470
- ".img",
185471
- ".sqlite",
185472
- ".sqlite3",
185473
- ".db",
185474
- ".db3"
185475
- ]);
185476
- const ASF_HEADER = Buffer.from([
185477
- 48,
185478
- 38,
185479
- 178,
185480
- 117,
185481
- 142,
185482
- 102,
185483
- 207,
185484
- 17,
185485
- 166,
185486
- 217,
185487
- 0,
185488
- 170,
185489
- 0,
185490
- 98,
185491
- 206,
185492
- 108
185493
- ]);
185494
- const FTYP_IMAGE_BRANDS = Object.freeze({
185495
- avif: "image/avif",
185496
- avis: "image/avif",
185497
- heic: "image/heic",
185498
- heif: "image/heif",
185499
- heix: "image/heif",
185500
- hevc: "image/heic",
185501
- mif1: "image/heif",
185502
- msf1: "image/heif"
185503
- });
185504
- const FTYP_VIDEO_BRANDS = Object.freeze({
185505
- isom: "video/mp4",
185506
- iso2: "video/mp4",
185507
- iso5: "video/mp4",
185508
- mp41: "video/mp4",
185509
- mp42: "video/mp4",
185510
- avc1: "video/mp4",
185511
- mp4v: "video/mp4",
185512
- m4v: "video/x-m4v",
185513
- qt: "video/quicktime",
185514
- "3gp4": "video/3gpp",
185515
- "3gp5": "video/3gpp",
185516
- "3gp6": "video/3gpp",
185517
- "3gp7": "video/3gpp",
185518
- "3g2": "video/3gpp2"
185519
- });
185520
- function toBuffer(data) {
185521
- return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
185522
- }
185523
- function startsWith(buf, prefix) {
185524
- const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
185525
- if (buf.length < needle.length) return false;
185526
- for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
185527
- return true;
185528
- }
185529
- function sniffFtypBrand(header) {
185530
- if (header.length < 12) return null;
185531
- if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
185532
- return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
185533
- }
185534
- function sniffMediaFromMagic(data) {
185535
- const buf = toBuffer(data);
185536
- const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
185537
- if (startsWith(header, [
185538
- 137,
185539
- 80,
185540
- 78,
185541
- 71,
185542
- 13,
185543
- 10,
185544
- 26,
185545
- 10
185546
- ])) return {
185547
- kind: "image",
185548
- mimeType: "image/png"
185549
- };
185550
- if (startsWith(header, [
185551
- 255,
185552
- 216,
185553
- 255
185554
- ])) return {
185555
- kind: "image",
185556
- mimeType: "image/jpeg"
185557
- };
185558
- if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
185559
- kind: "image",
185560
- mimeType: "image/gif"
185561
- };
185562
- if (startsWith(header, Buffer.from("BM"))) return {
185563
- kind: "image",
185564
- mimeType: "image/bmp"
185565
- };
185566
- if (startsWith(header, [
185567
- 73,
185568
- 73,
185569
- 42,
185570
- 0
185571
- ]) || startsWith(header, [
185572
- 77,
185573
- 77,
185574
- 0,
185575
- 42
185576
- ])) return {
185577
- kind: "image",
185578
- mimeType: "image/tiff"
185579
- };
185580
- if (startsWith(header, [
185581
- 0,
185582
- 0,
185583
- 1,
185584
- 0
185585
- ])) return {
185586
- kind: "image",
185587
- mimeType: "image/x-icon"
185588
- };
185589
- if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
185590
- const chunk = header.subarray(8, 12).toString("latin1");
185591
- if (chunk === "WEBP") return {
185592
- kind: "image",
185593
- mimeType: "image/webp"
185594
- };
185595
- if (chunk === "AVI ") return {
185596
- kind: "video",
185597
- mimeType: "video/x-msvideo"
185598
- };
185599
- }
185600
- if (startsWith(header, Buffer.from("FLV"))) return {
185601
- kind: "video",
185602
- mimeType: "video/x-flv"
185603
- };
185604
- if (startsWith(header, ASF_HEADER)) return {
185605
- kind: "video",
185606
- mimeType: "video/x-ms-wmv"
185607
- };
185608
- if (startsWith(header, [
185609
- 26,
185610
- 69,
185611
- 223,
185612
- 163
185613
- ])) {
185614
- const lowered = header.toString("latin1").toLowerCase();
185615
- if (lowered.includes("webm")) return {
185616
- kind: "video",
185617
- mimeType: "video/webm"
185618
- };
185619
- if (lowered.includes("matroska")) return {
185620
- kind: "video",
185621
- mimeType: "video/x-matroska"
185622
- };
185623
- }
185624
- const brand = sniffFtypBrand(header);
185625
- if (brand !== null && brand !== "") {
185626
- if (brand in FTYP_IMAGE_BRANDS) return {
185627
- kind: "image",
185628
- mimeType: FTYP_IMAGE_BRANDS[brand]
185629
- };
185630
- if (brand in FTYP_VIDEO_BRANDS) return {
185631
- kind: "video",
185632
- mimeType: FTYP_VIDEO_BRANDS[brand]
185633
- };
185634
- }
185635
- return null;
185636
- }
185637
- /**
185638
- * Best-effort pixel-dimension reader for common raster formats.
185639
- *
185640
- * Inspects only the fixed region near the start of the file where each
185641
- * format records its dimensions (the IHDR/DIB header, the RIFF chunk
185642
- * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
185643
- * for formats whose dimensions are not locatable from that region, or
185644
- * when the supplied buffer is too short to cover it.
185645
- */
185646
- function sniffImageDimensions(data) {
185647
- const buf = toBuffer(data);
185648
- if (startsWith(buf, [
185649
- 137,
185650
- 80,
185651
- 78,
185652
- 71,
185653
- 13,
185654
- 10,
185655
- 26,
185656
- 10
185657
- ]) && buf.length >= 24) return {
185658
- width: buf.readUInt32BE(16),
185659
- height: buf.readUInt32BE(20)
185660
- };
185661
- if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
185662
- width: buf.readUInt16LE(6),
185663
- height: buf.readUInt16LE(8)
185664
- };
185665
- if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
185666
- width: buf.readInt32LE(18),
185667
- height: Math.abs(buf.readInt32LE(22))
185668
- };
185669
- if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
185670
- const fourCc = buf.subarray(12, 16).toString("latin1");
185671
- if (fourCc === "VP8 ") return {
185672
- width: buf.readUInt16LE(26) & 16383,
185673
- height: buf.readUInt16LE(28) & 16383
185674
- };
185675
- if (fourCc === "VP8L" && buf.length >= 25) {
185676
- const bits = buf.readUInt32LE(21);
185677
- return {
185678
- width: (bits & 16383) + 1,
185679
- height: (bits >> 14 & 16383) + 1
185680
- };
185681
- }
185682
- if (fourCc === "VP8X") return {
185683
- width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
185684
- height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
185685
- };
185686
- }
185687
- if (startsWith(buf, [255, 216])) {
185688
- let offset = 2;
185689
- while (offset + 9 < buf.length) {
185690
- if (buf[offset] !== 255) {
185691
- offset += 1;
185692
- continue;
185693
- }
185694
- const marker = buf[offset + 1];
185695
- if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
185696
- height: buf.readUInt16BE(offset + 5),
185697
- width: buf.readUInt16BE(offset + 7)
185698
- };
185699
- if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
185700
- offset += 2;
185701
- continue;
185702
- }
185703
- const segmentLength = buf.readUInt16BE(offset + 2);
185704
- if (segmentLength < 2) break;
185705
- offset += 2 + segmentLength;
185706
- }
185707
- }
185708
- return null;
185709
- }
185710
- function getSuffix(path) {
185711
- const idx = path.lastIndexOf(".");
185712
- if (idx === -1) return "";
185713
- if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
185714
- return path.slice(idx).toLowerCase();
185715
- }
185716
- function detectFileType(path, header, type = "text") {
185717
- const suffix = getSuffix(path);
185718
- let mediaHint = null;
185719
- if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
185720
- kind: "text",
185721
- mimeType: TEXT_MIME_BY_SUFFIX[suffix]
185722
- };
185723
- else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
185724
- kind: "image",
185725
- mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
185726
- };
185727
- else if (suffix in VIDEO_MIME_BY_SUFFIX) mediaHint = {
185728
- kind: "video",
185729
- mimeType: VIDEO_MIME_BY_SUFFIX[suffix]
185730
- };
185731
- if (header !== void 0) {
185732
- const buf = toBuffer(header);
185733
- const sniffed = sniffMediaFromMagic(buf);
185734
- if (sniffed) {
185735
- if (type === "media") return sniffed;
185736
- if (mediaHint) {
185737
- if (sniffed.kind !== mediaHint.kind) return {
185738
- kind: "unknown",
185739
- mimeType: ""
185740
- };
185741
- return mediaHint;
185742
- }
185743
- return sniffed;
185744
- }
185745
- if (mediaHint?.kind === "image") return {
185746
- kind: "unknown",
185747
- mimeType: ""
185748
- };
185749
- if (type === "media" && mediaHint?.kind === "video") return mediaHint;
185750
- if (buf.includes(0)) return {
185751
- kind: "unknown",
185752
- mimeType: ""
185753
- };
185754
- }
185755
- if (mediaHint) return mediaHint;
185756
- if (NON_TEXT_SUFFIXES.has(suffix)) return {
185757
- kind: "unknown",
185758
- mimeType: ""
185759
- };
185760
- return {
185761
- kind: "text",
185762
- mimeType: "text/plain"
185763
- };
185764
- }
185765
- //#endregion
185766
- //#region ../../packages/agent-core/src/utils/tokens.ts
185767
- const messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
185768
- /** Open assistant messages grow while streaming; completed history stays cached. */
185769
- function invalidateMessageTokenEstimate(message) {
185770
- messageTokenEstimateCache.delete(message);
185771
- }
185772
- /**
185773
- * Estimate token count from text using a character-based heuristic.
185774
- * - ASCII (~4 chars per token)
185775
- * - CJK and other non-ASCII (~1 char per token)
185776
- * The estimate is transient — the next LLM call returns the real count
185777
- * and supersedes this value. Used to keep `tokenCountWithPending`
185778
- * monotonic between LLM round-trips without paying for a tokenizer.
185779
- */
185780
- function estimateTokens(text) {
185781
- let asciiCount = 0;
185782
- let nonAsciiCount = 0;
185783
- for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
185784
- else nonAsciiCount++;
185785
- return Math.ceil(asciiCount / 4) + nonAsciiCount;
185786
- }
185787
- function estimateTokensForMessages(messages) {
185788
- let total = 0;
185789
- for (const message of messages) total += estimateTokensForMessage(message);
185790
- return total;
185791
- }
185792
- function estimateTokensForTools(tools) {
185793
- let total = 0;
185794
- for (const tool of tools) {
185795
- total += estimateTokens(tool.name);
185796
- total += estimateTokens(tool.description);
185797
- total += estimateTokens(JSON.stringify(tool.parameters));
185798
- }
185799
- return total;
185800
- }
185801
- function estimateTokensForMessage(message) {
185802
- const cached = messageTokenEstimateCache.get(message);
185803
- if (cached !== void 0) return cached;
185804
- let total = estimateTokens(message.role);
185805
- total += estimateTokensForContentParts(message.content);
185806
- if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
185807
- total += estimateTokens(call.name);
185808
- total += estimateTokens(JSON.stringify(call.arguments));
185809
- }
185810
- messageTokenEstimateCache.set(message, total);
185811
- return total;
185812
- }
185813
- function estimateTokensForContentParts(parts) {
185814
- let total = 0;
185815
- for (const part of parts) total += estimateTokensForContentPart(part);
185816
- return total;
185817
- }
185818
- /**
185819
- * Conservative transient fallback for media whose pixel dimensions or duration
185820
- * are unavailable. Inline raster images use their transmitted dimensions below;
185821
- * audio, video, remote images, and malformed image data keep this safe floor.
185822
- * The next successful LLM round-trip replaces the estimate with reported usage.
185823
- */
185824
- const MEDIA_TOKEN_ESTIMATE = 2e3;
185825
- /** Enough header data for common JPEG metadata followed by the first SOF marker. */
185826
- const MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
185827
- function imageDimensionsFromDataUrl(url) {
185828
- const commaIndex = url.indexOf(",");
185829
- if (commaIndex <= 0) return null;
185830
- const metadata = url.slice(0, commaIndex).toLowerCase();
185831
- if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
185832
- const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
185833
- const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
185834
- if (encodedHeader.length === 0) return null;
185835
- try {
185836
- const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
185837
- if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
185838
- return dimensions;
185839
- } catch {
185840
- return null;
185841
- }
185842
- }
185843
- function estimateImageTokensFromDimensions(width, height) {
185844
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
185845
- const patchSize = BigInt(32);
185846
- const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
185847
- return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
185848
- }
185849
- function estimateImageTokens(url) {
185850
- const dimensions = imageDimensionsFromDataUrl(url);
185851
- return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
185852
- }
185853
- function estimateTokensForContentPart(part) {
185854
- switch (part.type) {
185855
- case "text": return estimateTokens(part.text);
185856
- case "think": return estimateTokens(part.think);
185857
- case "image_url": return estimateImageTokens(part.imageUrl.url);
185858
- case "audio_url":
185859
- case "video_url": return MEDIA_TOKEN_ESTIMATE;
185860
- default: return 0;
185861
- }
185862
- }
185863
186242
  //#endregion
185864
186243
  //#region ../../node_modules/.pnpm/bmp-ts@1.0.9/node_modules/bmp-ts/dist/esm/header-types.js
185865
186244
  var HeaderTypes, header_types_default;
@@ -228325,6 +228704,8 @@ async function compressImageForModel(bytes, mimeType, options = {}) {
228325
228704
  if (bytes.length <= byteBudget && (longestEdge > 0 && longestEdge <= maxEdge || longestEdge === 0)) return passthrough();
228326
228705
  if (dims && dims.width * dims.height > MAX_DECODE_PIXELS) return passthrough();
228327
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();
228328
228709
  try {
228329
228710
  const { Jimp } = await Promise.resolve().then(() => (init_esm(), esm_exports$3));
228330
228711
  const image = await Jimp.fromBuffer(Buffer.from(bytes));
@@ -230115,7 +230496,7 @@ function buildCompactionSummaryText(summary) {
230115
230496
  const MAX_SESSION_COMPACTION_FAILURES = 2;
230116
230497
  const DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS = 128 * 1024;
230117
230498
  const COMPACTION_SUMMARY_RESERVE_RATIO = .1;
230118
- const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 75e4;
230499
+ const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 48e4;
230119
230500
  const MAX_HIERARCHICAL_COMPACTION_PASSES = 64;
230120
230501
  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:";
230121
230502
  var CompactionTruncatedError = class extends Error {
@@ -230235,16 +230616,19 @@ var FullCompaction = class {
230235
230616
  const projected = context.additionalMessages === void 0 ? messages : [...messages, ...context.additionalMessages];
230236
230617
  const historyBudget = this.requestHistoryByteBudget(projected);
230237
230618
  const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230619
+ const requestBudget = this.requestBodyByteBudget(projected, context);
230620
+ const requestThresholdReached = requestBudget !== void 0 && requestBudget.usedRequestBytesEstimate >= requestBudget.thresholdRequestBytes;
230238
230621
  if (this.sessionCompactionFailures >= MAX_SESSION_COMPACTION_FAILURES) reason = "session_stopped";
230239
230622
  else if (this.isCompacting) reason = "already_compacting";
230240
- else if (thresholdTokens === void 0 && historyBudget === void 0) reason = "no_budget";
230241
- else if (!historyThresholdReached && (thresholdTokens === void 0 || !Number.isFinite(usedTokens) || usedTokens < thresholdTokens)) reason = "below_threshold";
230623
+ else if (thresholdTokens === void 0 && historyBudget === void 0 && requestBudget === void 0) reason = "no_budget";
230624
+ else if (!historyThresholdReached && !requestThresholdReached && (thresholdTokens === void 0 || !Number.isFinite(usedTokens) || usedTokens < thresholdTokens)) reason = "below_threshold";
230242
230625
  else if (!this.hasShrinkableHistory(projected)) reason = "nothing_to_compact";
230243
230626
  return {
230244
230627
  eligible: reason === "eligible",
230245
230628
  usedTokens,
230246
230629
  thresholdTokens,
230247
230630
  ...historyBudget,
230631
+ ...requestBudget,
230248
230632
  reason
230249
230633
  };
230250
230634
  }
@@ -230266,6 +230650,19 @@ var FullCompaction = class {
230266
230650
  thresholdHistoryBytes: limit
230267
230651
  };
230268
230652
  }
230653
+ requestBodyByteBudget(messages, context) {
230654
+ const limit = this.agent.blunConfig?.loopControl?.maxRequestHistoryBytes;
230655
+ if (!this.agent.config.hasProvider || this.strategy.compactionBudgetTokens === void 0 || limit !== void 0 && (!Number.isSafeInteger(limit) || limit <= 0)) return void 0;
230656
+ const model = this.agent.activeResponderModel ?? this.agent.config.modelAlias;
230657
+ if (model === void 0 || this.agent.config.runtimeModel(model).provider.name !== "blun") return void 0;
230658
+ const systemPrompt = context.systemPrompt ?? this.agent.effectiveSystemPrompt;
230659
+ const tools = context.tools ?? this.agent.tools.loopTools;
230660
+ if (estimateBlunSerializedBodyBytes(systemPrompt, [], []) + 4096 >= BLUN_COMPACTION_REQUEST_BUDGET_BYTES) return void 0;
230661
+ return {
230662
+ usedRequestBytesEstimate: estimateBlunSerializedBodyBytes(systemPrompt, tools, messages) + 4096,
230663
+ thresholdRequestBytes: BLUN_COMPACTION_REQUEST_BUDGET_BYTES
230664
+ };
230665
+ }
230269
230666
  requestProactiveCompaction(context = {}) {
230270
230667
  const status = this.proactiveCompactionStatus(context);
230271
230668
  if (status.eligible && !this.proactiveCompactionRequested) {
@@ -230274,7 +230671,9 @@ var FullCompaction = class {
230274
230671
  used_tokens: status.usedTokens,
230275
230672
  threshold_tokens: status.thresholdTokens,
230276
230673
  used_history_bytes: status.usedHistoryBytes,
230277
- threshold_history_bytes: status.thresholdHistoryBytes
230674
+ threshold_history_bytes: status.thresholdHistoryBytes,
230675
+ used_request_bytes_estimate: status.usedRequestBytesEstimate,
230676
+ threshold_request_bytes: status.thresholdRequestBytes
230278
230677
  });
230279
230678
  }
230280
230679
  return status;
@@ -230403,7 +230802,9 @@ var FullCompaction = class {
230403
230802
  const requestTokens = this.estimateCurrentRequestTokens(requestContext);
230404
230803
  const budget = this.getCompactionBudgetTokens();
230405
230804
  const historyBudget = this.requestHistoryByteBudget(projectedMessages);
230406
- 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);
230805
+ const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230806
+ const requestBudget = this.requestBodyByteBudget(projectedMessages, requestContext);
230807
+ 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);
230407
230808
  if (this.checkAutoCompaction(true, requestTokens, projectedMessages, proactive) && (proactive || this.strategy.shouldBlock(requestTokens))) await this.block(signal);
230408
230809
  signal.throwIfAborted();
230409
230810
  const compacted = this.agent.context.history !== historyBefore;
@@ -230613,7 +231014,7 @@ var FullCompaction = class {
230613
231014
  dropOrphanResults: true
230614
231015
  }), createUserMessage(requestInstruction)], capability));
230615
231016
  };
230616
- const estimateRequestBytes = (messages) => Buffer.byteLength(JSON.stringify(messages), "utf8") + Buffer.byteLength(this.agent.config.systemPrompt, "utf8");
231017
+ const estimateRequestBytes = (messages) => estimateBlunSerializedBodyBytes(this.agent.effectiveSystemPrompt, compactionTools, messages) + 4096;
230617
231018
  while (true) {
230618
231019
  signal.throwIfAborted();
230619
231020
  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: {
@@ -231292,6 +231693,13 @@ const CronRunStatusSchema = _enum([
231292
231693
  ]);
231293
231694
  const counter = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
231294
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();
231295
231703
  const CronRunReceiptSchema = object({
231296
231704
  runId: CronRunIdSchema,
231297
231705
  jobId: CronJobIdSchema,
@@ -231312,6 +231720,7 @@ const CronRunReceiptSchema = object({
231312
231720
  names: array(string().regex(/^[\w.:-]{1,160}$/)).max(64),
231313
231721
  excessNames: counter
231314
231722
  }).strict(),
231723
+ providerUsage: CronProviderUsageSchema.nullable().optional(),
231315
231724
  outputs: array(object({
231316
231725
  rootRef: string().regex(/^(workspace|additional:[0-9]+)$/),
231317
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))
@@ -231347,6 +231756,7 @@ function publicCronRun(run) {
231347
231756
  coalescedCount: receipt.coalescedCount,
231348
231757
  toolCalls: receipt.toolSummary.calls,
231349
231758
  toolErrors: receipt.toolSummary.errors,
231759
+ providerUsage: receipt.providerUsage ?? null,
231350
231760
  outputCount: receipt.outputs.length,
231351
231761
  finalMessageAvailable: run.finalMessage !== null,
231352
231762
  durable: run.durable
@@ -231548,6 +231958,8 @@ var CronRunOutputCollector = class {
231548
231958
  errors = 0;
231549
231959
  excessNames = 0;
231550
231960
  outputs = [];
231961
+ providerUsage;
231962
+ usageComplete = true;
231551
231963
  invalid = false;
231552
231964
  constructor(roots) {
231553
231965
  this.roots = roots;
@@ -231568,6 +231980,7 @@ var CronRunOutputCollector = class {
231568
231980
  }
231569
231981
  if (event.type === "step.retrying") {
231570
231982
  if (event.turnId !== String(this.turnId)) return;
231983
+ this.usageComplete = false;
231571
231984
  if (this.step === void 0 || this.step.id !== event.stepUuid || this.step.step !== event.step || this.step.tools) this.invalid = true;
231572
231985
  else {
231573
231986
  this.step = {
@@ -231636,19 +232049,29 @@ var CronRunOutputCollector = class {
231636
232049
  } else if (event.type === "step.end") {
231637
232050
  const step = this.step;
231638
232051
  if (step === void 0 || event.uuid !== step.id || event.step !== step.step) this.invalid = true;
231639
- else if (event.finishReason === "end_turn" && !step.tools && !this.invalid) {
231640
- const clipped = prefix(step.text);
231641
- const truncated = step.chars > clipped.length;
231642
- const text = clipped + (truncated ? CRON_RUN_TRUNCATION_MARKER : "");
231643
- if (text) this.candidate = {
231644
- stepId: step.id,
231645
- turnId: this.turnId,
231646
- message: {
231647
- text,
231648
- truncated,
231649
- chars: step.chars
231650
- }
231651
- };
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
+ }
231652
232075
  }
231653
232076
  this.step = void 0;
231654
232077
  }
@@ -231663,6 +232086,7 @@ var CronRunOutputCollector = class {
231663
232086
  excessNames: this.excessNames
231664
232087
  },
231665
232088
  outputs: structuredClone(this.outputs),
232089
+ providerUsage: complete && this.usageComplete && this.providerUsage !== void 0 ? structuredClone(this.providerUsage) : null,
231666
232090
  final: complete ? this.candidate : void 0,
231667
232091
  complete
231668
232092
  };
@@ -232821,6 +233245,7 @@ var CronManager = class {
232821
233245
  status,
232822
233246
  endedAt: new Date(this.clocks.wallNow()).toISOString(),
232823
233247
  toolSummary: result.toolSummary,
233248
+ providerUsage: completed ? result.providerUsage : null,
232824
233249
  outputs: result.outputs,
232825
233250
  finalMessageRef: final === void 0 ? null : {
232826
233251
  stepId: final.stepId,
@@ -233008,6 +233433,7 @@ var CronManager = class {
233008
233433
  names: [],
233009
233434
  excessNames: 0
233010
233435
  },
233436
+ providerUsage: null,
233011
233437
  outputs: [],
233012
233438
  finalMessageRef: null,
233013
233439
  unavailableReason
@@ -234063,6 +234489,10 @@ const SYNTHETIC_TOOL_RESULT_TEXT = "Tool result is not available in the current
234063
234489
  function repairToolExchangeAdjacency(messages, options) {
234064
234490
  let lastNonToolIndex = messages.length - 1;
234065
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
+ };
234066
234496
  const out = [];
234067
234497
  const consumed = /* @__PURE__ */ new Set();
234068
234498
  for (let i = 0; i < messages.length; i++) {
@@ -234070,12 +234500,15 @@ function repairToolExchangeAdjacency(messages, options) {
234070
234500
  const message = messages[i];
234071
234501
  if (message.role !== "assistant" || message.toolCalls.length === 0) {
234072
234502
  out.push(message);
234503
+ if (message.role === "tool" && message.toolCallId !== void 0) consumeResult(message.toolCallId);
234073
234504
  continue;
234074
234505
  }
234075
234506
  out.push(message);
234076
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));
234077
234510
  let foreignBetween = false;
234078
- 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++) {
234079
234512
  if (consumed.has(j)) continue;
234080
234513
  const next = messages[j];
234081
234514
  const toolCallId = next.toolCallId;
@@ -234083,11 +234516,25 @@ function repairToolExchangeAdjacency(messages, options) {
234083
234516
  out.push(next);
234084
234517
  consumed.add(j);
234085
234518
  pending.delete(toolCallId);
234519
+ searchable?.delete(toolCallId);
234520
+ consumeResult(toolCallId);
234086
234521
  if (foreignBetween) options?.onAnomaly?.({
234087
234522
  kind: "tool_result_reordered",
234088
234523
  toolCallId
234089
234524
  });
234090
- } 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
+ }
234091
234538
  }
234092
234539
  const isMidHistory = i < lastNonToolIndex;
234093
234540
  if (options?.synthesizeMissing === true || isMidHistory) for (const missingId of pending) {
@@ -234193,8 +234640,9 @@ function makeSyntheticToolResult(toolCallId) {
234193
234640
  }
234194
234641
  function mergeAdjacentUserMessages(history, onAnomaly) {
234195
234642
  const out = [];
234196
- for (const source of history) {
234197
- const message = prepareMessageForProjection(source, onAnomaly);
234643
+ const latestUserIndex = history.findLastIndex((message) => message.role === "user" && message.partial !== true && (message.origin === void 0 || message.origin.kind === "user"));
234644
+ for (const [index, source] of history.entries()) {
234645
+ const message = prepareMessageForProjection(source, onAnomaly, index < latestUserIndex);
234198
234646
  if (message === null) continue;
234199
234647
  const previous = out.at(-1);
234200
234648
  if (canMergeUserMessage(message) && previous !== void 0 && canMergeUserMessage(previous)) {
@@ -234205,7 +234653,7 @@ function mergeAdjacentUserMessages(history, onAnomaly) {
234205
234653
  }
234206
234654
  return out.map(stripContextMetadata);
234207
234655
  }
234208
- function prepareMessageForProjection(message, onAnomaly) {
234656
+ function prepareMessageForProjection(message, onAnomaly, historical = false) {
234209
234657
  if (message.partial === true) return null;
234210
234658
  let content;
234211
234659
  for (const [index, part] of message.content.entries()) {
@@ -234217,6 +234665,19 @@ function prepareMessageForProjection(message, onAnomaly) {
234217
234665
  });
234218
234666
  continue;
234219
234667
  }
234668
+ if (historical && message.role === "user" && part.type === "text") {
234669
+ const text = withoutHistoricalTelegramGroupReminder(part.text);
234670
+ if (text !== part.text) {
234671
+ content ??= message.content.slice(0, index);
234672
+ const shortened = {
234673
+ ...part,
234674
+ text
234675
+ };
234676
+ copyRequestTextOrigin(part, shortened, message.origin);
234677
+ content.push(shortened);
234678
+ continue;
234679
+ }
234680
+ }
234220
234681
  content?.push(part);
234221
234682
  }
234222
234683
  const next = content === void 0 ? message : {
@@ -234226,6 +234687,14 @@ function prepareMessageForProjection(message, onAnomaly) {
234226
234687
  if (next.role === "tool" && next.content.length === 0) throw new BlunError(ErrorCodes.REQUEST_INVALID, "Tool result message content cannot be empty after removing empty text blocks.", { details: { toolCallId: next.toolCallId } });
234227
234688
  return next.content.length === 0 && next.toolCalls.length === 0 ? null : next;
234228
234689
  }
234690
+ function withoutHistoricalTelegramGroupReminder(text) {
234691
+ if (!text.startsWith("<channel source=\"telegram\"") && !text.startsWith("You are connected to a Telegram channel.")) return text;
234692
+ const start = text.lastIndexOf(`</channel>\n[GRUPPENANTWORT:`);
234693
+ if (start < 0) return text;
234694
+ const reminder = text.slice(start + 10 + 1);
234695
+ if (!/^\[GRUPPENANTWORT:[^\r\n]*\]$/.test(reminder)) return text;
234696
+ return text.slice(0, start + 10);
234697
+ }
234229
234698
  function canMergeUserMessage(message) {
234230
234699
  return message.role === "user" && message.origin?.kind === "user";
234231
234700
  }
@@ -234290,6 +234759,8 @@ function checkedRemovalIndexes(indexes, length) {
234290
234759
  }
234291
234760
  var ContextMemory = class {
234292
234761
  agent;
234762
+ mutationGeneration = randomUUID();
234763
+ mutationRevision = 0;
234293
234764
  _history = [];
234294
234765
  _tokenCount = 0;
234295
234766
  tokenCountCoveredMessageCount = 0;
@@ -234301,6 +234772,15 @@ var ContextMemory = class {
234301
234772
  constructor(agent) {
234302
234773
  this.agent = agent;
234303
234774
  }
234775
+ publishMutation(reason) {
234776
+ if (this.agent.records.restoring) return;
234777
+ this.agent.emitEvent({
234778
+ type: "context.changed",
234779
+ generation: this.mutationGeneration,
234780
+ revision: ++this.mutationRevision,
234781
+ reason
234782
+ });
234783
+ }
234304
234784
  get lastAssistantAt() {
234305
234785
  return this._lastAssistantAt;
234306
234786
  }
@@ -234432,17 +234912,29 @@ var ContextMemory = class {
234432
234912
  deferredIndexes: [...deferredIndexes]
234433
234913
  });
234434
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;
234435
234918
  for (let i = this._history.length - 1; i >= 0; i--) {
234436
234919
  const message = this._history[i];
234437
234920
  if (message === void 0 || !historyIndexes.has(i)) continue;
234438
234921
  removedMessages.add(message);
234439
- this._history.splice(i, 1);
234922
+ if (!compactHistory) this._history.splice(i, 1);
234440
234923
  this.agent.injection.onContextMessageRemoved(i);
234441
234924
  if (i < this.tokenCountCoveredMessageCount) {
234442
234925
  this.tokenCountCoveredMessageCount--;
234443
234926
  this._tokenCount -= estimateTokensForMessages([message]);
234444
234927
  }
234445
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
+ }
234446
234938
  this.deferredMessages = this.deferredMessages.filter((message, index) => {
234447
234939
  if (!deferredIndexes.has(index)) return true;
234448
234940
  removedMessages.add(message);
@@ -234454,6 +234946,7 @@ var ContextMemory = class {
234454
234946
  return removedMessages.size;
234455
234947
  }
234456
234948
  clear() {
234949
+ const changed = this._history.length > 0;
234457
234950
  this.agent.records.logRecord({ type: "context.clear" });
234458
234951
  this._history = [];
234459
234952
  this._tokenCount = 0;
@@ -234465,6 +234958,7 @@ var ContextMemory = class {
234465
234958
  this.agent.microCompaction.reset();
234466
234959
  this.agent.injection.onContextClear();
234467
234960
  this.agent.emitStatusUpdated();
234961
+ if (changed) this.publishMutation("clear");
234468
234962
  }
234469
234963
  undo(count) {
234470
234964
  if (count <= 0) return;
@@ -234502,6 +234996,7 @@ var ContextMemory = class {
234502
234996
  this.deferredMessages = [];
234503
234997
  this.agent.microCompaction.reset(this._history.length);
234504
234998
  this.agent.emitStatusUpdated();
234999
+ if (removedMessages.size > 0) this.publishMutation("undo");
234505
235000
  if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
234506
235001
  reason: "undo_limit",
234507
235002
  requestedCount: count,
@@ -234510,6 +235005,7 @@ var ContextMemory = class {
234510
235005
  } });
234511
235006
  }
234512
235007
  applyCompaction(input, options = {}) {
235008
+ if (this.agent.records.restoring !== null && input.toolContinuity !== void 0 && !isToolContinuity(input.toolContinuity)) throw new Error("Invalid compaction tool continuity");
234513
235009
  let preservedToolTailCount = input.preservedToolTailCount;
234514
235010
  if (this.agent.records.restoring === null && this.hasOpenToolExchange()) {
234515
235011
  const start = this._history.findIndex((message) => message.role === "assistant" && message.toolCalls.some((call) => this.pendingToolResultIds.has(call.id)));
@@ -234546,25 +235042,75 @@ var ContextMemory = class {
234546
235042
  elisionMessage,
234547
235043
  ...selection.tail
234548
235044
  ];
234549
- const contextSummary = input.contextSummary ?? input.summary;
234550
- const summaryMessage = {
235045
+ let contextSummary = input.contextSummary ?? input.summary;
235046
+ let toolContinuity = this.agent.records.restoring === null ? void 0 : input.toolContinuity;
235047
+ let summaryMessage = {
234551
235048
  role: "user",
234552
235049
  content: [{
234553
235050
  type: "text",
234554
235051
  text: contextSummary
234555
235052
  }],
234556
235053
  toolCalls: [],
234557
- origin: { kind: "compaction_summary" }
235054
+ origin: {
235055
+ kind: "compaction_summary",
235056
+ ...toolContinuity === void 0 ? {} : { toolContinuity }
235057
+ }
234558
235058
  };
234559
- const replacement = [
235059
+ let replacement = [
234560
235060
  ...keptMessages,
234561
235061
  summaryMessage,
234562
235062
  ...preservedToolTail
234563
235063
  ];
235064
+ const previousHistoryTokens = options.requireReduction && this.agent.records.restoring === null ? estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true })) : void 0;
235065
+ if (this.agent.records.restoring === null) {
235066
+ toolContinuity = void 0;
235067
+ const observations = collectToolContinuity(this._history.slice(0, tailStart));
235068
+ if (observations !== void 0) {
235069
+ const baseProjected = project(replacement, { dropOrphanResults: true });
235070
+ const baseTokens = estimateTokensForMessages(baseProjected);
235071
+ let extraTokens = previousHistoryTokens === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, previousHistoryTokens - baseTokens - 1);
235072
+ if (this.agent.config.hasProvider) {
235073
+ const window = this.agent.fullCompaction.getEffectiveMaxContextTokens();
235074
+ const budget = this.agent.fullCompaction.getCompactionBudgetTokens();
235075
+ const requestLimit = Math.min(window > 0 ? window - 1 : Number.POSITIVE_INFINITY, budget ?? Number.POSITIVE_INFINITY);
235076
+ extraTokens = Math.min(extraTokens, Math.max(0, requestLimit - this.agent.fullCompaction.estimateProjectedRequestTokens({ messages: baseProjected }) - 1));
235077
+ }
235078
+ let fitted = fitToolContinuity(observations, extraTokens);
235079
+ while (fitted !== void 0) {
235080
+ const candidateSummary = {
235081
+ ...summaryMessage,
235082
+ content: [{
235083
+ type: "text",
235084
+ text: contextSummary + fitted.text
235085
+ }],
235086
+ origin: {
235087
+ kind: "compaction_summary",
235088
+ toolContinuity: fitted.continuity
235089
+ }
235090
+ };
235091
+ const candidate = [
235092
+ ...keptMessages,
235093
+ candidateSummary,
235094
+ ...preservedToolTail
235095
+ ];
235096
+ const projected = project(candidate, { dropOrphanResults: true });
235097
+ const status = this.agent.fullCompaction.proactiveCompactionStatus({ messages: projected });
235098
+ const reducing = previousHistoryTokens === void 0 || estimateTokensForMessages(projected) < previousHistoryTokens;
235099
+ const bytesFit = (status.usedHistoryBytes === void 0 || status.thresholdHistoryBytes === void 0 || status.usedHistoryBytes < status.thresholdHistoryBytes) && (status.usedRequestBytesEstimate === void 0 || status.thresholdRequestBytes === void 0 || status.usedRequestBytesEstimate < status.thresholdRequestBytes);
235100
+ if (reducing && bytesFit) {
235101
+ toolContinuity = fitted.continuity;
235102
+ contextSummary += fitted.text;
235103
+ summaryMessage = candidateSummary;
235104
+ replacement = candidate;
235105
+ break;
235106
+ }
235107
+ fitted = fitToolContinuity(observations, estimateTokens(fitted.text) - 1);
235108
+ }
235109
+ }
235110
+ }
234564
235111
  if (options.requireReduction && this.agent.records.restoring === null) {
234565
- const previousHistoryTokens = estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true }));
234566
235112
  const nextHistoryTokens = estimateTokensForMessages(project(replacement, { dropOrphanResults: true }));
234567
- if (nextHistoryTokens >= previousHistoryTokens) throw new BlunError(ErrorCodes.COMPACTION_FAILED, "Automatic compaction did not reduce the history. The conversation history was not changed.", { details: {
235113
+ if (previousHistoryTokens !== void 0 && nextHistoryTokens >= previousHistoryTokens) throw new BlunError(ErrorCodes.COMPACTION_FAILED, "Automatic compaction did not reduce the history. The conversation history was not changed.", { details: {
234568
235114
  reason: "non_reducing_summary",
234569
235115
  contextUnchanged: true,
234570
235116
  previousHistoryTokens,
@@ -234577,6 +235123,7 @@ var ContextMemory = class {
234577
235123
  const result = {
234578
235124
  summary: input.summary,
234579
235125
  contextSummary,
235126
+ ...toolContinuity === void 0 ? {} : { toolContinuity },
234580
235127
  compactedCount: input.compactedCount,
234581
235128
  tokensBefore: input.tokensBefore,
234582
235129
  tokensAfter,
@@ -234593,6 +235140,7 @@ var ContextMemory = class {
234593
235140
  this.agent.replayBuilder.patchLast("compaction", { result: {
234594
235141
  summary: result.summary,
234595
235142
  contextSummary: result.contextSummary,
235143
+ ...result.toolContinuity === void 0 ? {} : { toolContinuity: result.toolContinuity },
234596
235144
  compactedCount: result.compactedCount,
234597
235145
  tokensBefore: result.tokensBefore,
234598
235146
  tokensAfter: result.tokensAfter,
@@ -236200,20 +236748,75 @@ function fingerprintTitle(title) {
236200
236748
  return createHash("sha256").update(title.trim().toLowerCase()).digest("hex");
236201
236749
  }
236202
236750
  async function loadRegister(mistakeDir = defaultMistakeDir()) {
236751
+ const target = join(mistakeDir, REGISTER_FILENAME);
236203
236752
  try {
236204
236753
  await mkdir(mistakeDir, { recursive: true });
236205
- const content = await readFile(join(mistakeDir, REGISTER_FILENAME), "utf-8");
236206
- return JSON.parse(content);
236207
- } catch {
236208
- return {
236754
+ const before = await lstat(target).catch((error) => {
236755
+ if (error.code === "ENOENT") return void 0;
236756
+ throw error;
236757
+ });
236758
+ if (before === void 0) return {
236209
236759
  version: 1,
236210
236760
  entries: []
236211
236761
  };
236762
+ if (!before.isFile() || before.isSymbolicLink()) throw new Error("Mistake register must be a regular file.");
236763
+ const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
236764
+ let content;
236765
+ try {
236766
+ const opened = await handle.stat();
236767
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) throw new Error("Mistake register changed while opening.");
236768
+ content = await handle.readFile("utf8");
236769
+ } finally {
236770
+ await handle.close();
236771
+ }
236772
+ let parsed;
236773
+ try {
236774
+ parsed = JSON.parse(content);
236775
+ } catch {
236776
+ throw new Error("Mistake register has invalid JSON.");
236777
+ }
236778
+ assertRegister(parsed);
236779
+ return parsed;
236780
+ } catch (error) {
236781
+ throw registerError(error, "read");
236212
236782
  }
236213
236783
  }
236214
236784
  async function saveRegister(register, mistakeDir = defaultMistakeDir()) {
236215
- await mkdir(mistakeDir, { recursive: true });
236216
- await writeFile(join(mistakeDir, REGISTER_FILENAME), JSON.stringify(register, null, 2), "utf-8");
236785
+ assertRegister(register);
236786
+ const target = join(mistakeDir, REGISTER_FILENAME);
236787
+ const temporary = `${target}.tmp.${process.pid}.${randomUUID()}`;
236788
+ let created = false;
236789
+ try {
236790
+ await loadRegister(mistakeDir);
236791
+ const handle = await open(temporary, "wx", 384);
236792
+ created = true;
236793
+ try {
236794
+ await handle.writeFile(JSON.stringify(register, null, 2), "utf8");
236795
+ await handle.sync();
236796
+ } finally {
236797
+ await handle.close();
236798
+ }
236799
+ await rename(temporary, target);
236800
+ } catch (error) {
236801
+ throw registerError(error, "write");
236802
+ } finally {
236803
+ if (created) await unlink(temporary).catch(() => {});
236804
+ }
236805
+ }
236806
+ function assertRegister(value) {
236807
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Mistake register has an invalid v1 schema.");
236808
+ const register = value;
236809
+ if (register["version"] !== 1 || !Array.isArray(register["entries"]) || !register["entries"].every((entry) => {
236810
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
236811
+ const fields = entry;
236812
+ return typeof fields["entryIndex"] === "number" && Number.isSafeInteger(fields["entryIndex"]) && fields["entryIndex"] >= 0 && typeof fields["titleFingerprint"] === "string" && fields["titleFingerprint"].trim().length > 0 && typeof fields["title"] === "string" && fields["title"].trim().length > 0 && typeof fields["count"] === "number" && Number.isSafeInteger(fields["count"]) && fields["count"] >= 0 && typeof fields["lastHitAt"] === "string" && fields["lastHitAt"].trim().length > 0 && (fields["origin"] === "program" || fields["origin"] === "hand");
236813
+ })) throw new Error("Mistake register has an invalid v1 schema.");
236814
+ }
236815
+ function registerError(error, operation) {
236816
+ if (error instanceof Error && error.message.startsWith("Mistake register ")) return error;
236817
+ const code = typeof error === "object" && error !== null ? error.code : void 0;
236818
+ const suffix = typeof code === "string" && /^[A-Z0-9_]+$/u.test(code) ? ` (${code})` : "";
236819
+ return /* @__PURE__ */ new Error(`Mistake register ${operation} failed${suffix}.`);
236217
236820
  }
236218
236821
  /**
236219
236822
  * Findet einen Register-Eintrag anhand des Titels (Fingerabdruck).
@@ -238906,6 +239509,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
238906
239509
  const DEFERRED_TOOL_LOADER_NAME = "ToolSearch";
238907
239510
  const MAX_PERSISTENT_DEFERRED_TOOLS = 12;
238908
239511
  const MAX_TOOL_SEARCH_QUERY_CHARS = 1e3;
239512
+ const MAX_TOOL_CATALOG_RESULT_BYTES = 4096;
238909
239513
  const MAX_AUTO_RANKED_TOOLS = 6;
238910
239514
  const deferredToolLoaders = /* @__PURE__ */ new WeakSet();
238911
239515
  const TOOL_RANK_EMBEDDED_CONTENT_RE = /<(attached_documents?|attachment_content|document_content|file_content)\b[^>]*>[\s\S]*?<\/\1>/giu;
@@ -238976,6 +239580,32 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
238976
239580
  "suchen"
238977
239581
  ]);
238978
239582
  const TOOL_SEARCH_RELATED_TERM_GROUPS = Object.freeze([
239583
+ Object.freeze([
239584
+ "persist",
239585
+ "save",
239586
+ "speichere",
239587
+ "speichern",
239588
+ "store"
239589
+ ]),
239590
+ Object.freeze([
239591
+ "erinnerung",
239592
+ "erinnerungen",
239593
+ "fact",
239594
+ "facts",
239595
+ "fakt",
239596
+ "fakten",
239597
+ "memory",
239598
+ "memories",
239599
+ "merk",
239600
+ "merken",
239601
+ "remember"
239602
+ ]),
239603
+ Object.freeze([
239604
+ "note",
239605
+ "notes",
239606
+ "notiz",
239607
+ "notizen"
239608
+ ]),
238979
239609
  Object.freeze([
238980
239610
  "checklist",
238981
239611
  "list",
@@ -239093,10 +239723,18 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239093
239723
  if (VIDEO_MEDIA_TERM_RE.test(text)) selected.add("GenerateVideo");
239094
239724
  return selected;
239095
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
+ }
239096
239734
  function mediaToolNamesForTurnText(value) {
239097
239735
  const text = String(value || "");
239098
239736
  const selected = /* @__PURE__ */ new Set();
239099
- 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);
239100
239738
  const intentTexts = channelBodies.length > 0 ? channelBodies : [text];
239101
239739
  for (const intentText of intentTexts) for (const name of mediaGenerationToolNamesForText(intentText)) selected.add(name);
239102
239740
  if (/\bimage_path\s*=\s*["'][^"']+["']/iu.test(text)) {
@@ -239124,6 +239762,27 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239124
239762
  for (const leaf of leaves) leafCounts.set(leaf, (leafCounts.get(leaf) || 0) + 1);
239125
239763
  return names.map((name, index) => leafCounts.get(leaves[index]) === 1 ? leaves[index] : name).sort((left, right) => left.localeCompare(right));
239126
239764
  }
239765
+ function boundedDeferredToolCatalog(tools, filter) {
239766
+ const needle = filter.toLowerCase();
239767
+ const matched = deferredToolCatalog(tools).filter((name) => name.toLowerCase().includes(needle));
239768
+ const names = [];
239769
+ let bytes = Buffer.byteLength(JSON.stringify({
239770
+ names,
239771
+ total: matched.length,
239772
+ truncated: false
239773
+ }));
239774
+ for (const name of matched) {
239775
+ const nextBytes = Buffer.byteLength(JSON.stringify(name)) + (names.length > 0 ? 1 : 0);
239776
+ if (bytes + nextBytes > MAX_TOOL_CATALOG_RESULT_BYTES) break;
239777
+ names.push(name);
239778
+ bytes += nextBytes;
239779
+ }
239780
+ return JSON.stringify({
239781
+ names,
239782
+ total: matched.length,
239783
+ truncated: names.length < matched.length
239784
+ });
239785
+ }
239127
239786
  function normalizeDeferredToolQuery(value) {
239128
239787
  const query = String(value ?? "").trim();
239129
239788
  if (query.length <= MAX_TOOL_SEARCH_QUERY_CHARS) return query;
@@ -239173,22 +239832,39 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239173
239832
  function relatedSearchTerms(token) {
239174
239833
  return TOOL_SEARCH_RELATED_TERM_GROUPS.find((terms) => terms.includes(token)) || [token];
239175
239834
  }
239835
+ function withoutFencedCode(value) {
239836
+ const visibleLines = [];
239837
+ let fence = null;
239838
+ for (const line of String(value || "").split(/\r?\n/)) {
239839
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})/u)?.[1];
239840
+ if (fence) {
239841
+ if (marker?.[0] === fence[0] && marker.length >= fence.length && /^ {0,3}(?:`{3,}|~{3,})[ \t]*$/u.test(line)) fence = null;
239842
+ continue;
239843
+ }
239844
+ if (marker) {
239845
+ fence = marker;
239846
+ continue;
239847
+ }
239848
+ visibleLines.push(line);
239849
+ }
239850
+ return visibleLines.join("\n");
239851
+ }
239176
239852
  function rankingIntentTexts(value) {
239177
239853
  const text = String(value || "");
239178
- const channelBodies = [...text.matchAll(/(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/giu)].map((match) => match[1]);
239179
- return (channelBodies.length > 0 ? channelBodies : [text]).map((intentText) => normalizeDeferredToolQuery(intentText.replace(TOOL_RANK_EMBEDDED_CONTENT_RE, " ")));
239854
+ const channelBodies = channelMessagesForTurnText(text).map((message) => message.text);
239855
+ return (channelBodies.length > 0 ? channelBodies : [text]).map((intentText) => normalizeDeferredToolQuery(withoutFencedCode(intentText.replace(TOOL_RANK_EMBEDDED_CONTENT_RE, " "))));
239180
239856
  }
239181
239857
  function rankingTokens(value) {
239182
239858
  return [...new Set(String(value || "").normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]{3,}/gu) || [])].filter((token) => !TOOL_RANK_STOP_WORDS.has(token));
239183
239859
  }
239184
- function toolRankingDocument(tool) {
239860
+ function toolRankingDocument(tool, trustedToolNames) {
239185
239861
  const name = String(tool?.name || "");
239186
239862
  const leaf = name.split(/__|:/).at(-1) || name;
239187
239863
  const splitToolName = (value) => String(value || "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ");
239188
239864
  const nameTokens = rankingTokens(`${splitToolName(name)} ${splitToolName(leaf)}`);
239189
239865
  const descriptionTokens = rankingTokens(tool?.description);
239190
239866
  const parameterText = [];
239191
- const properties = tool?.parameters?.properties;
239867
+ const properties = /^mcp__/iu.test(name) && !trustedToolNames?.has(name) ? void 0 : tool?.parameters?.properties;
239192
239868
  if (properties && typeof properties === "object") for (const [parameterName, definition] of Object.entries(properties)) parameterText.push(parameterName, definition?.description || "");
239193
239869
  const exampleText = Array.isArray(tool?.examples) ? tool.examples.map((example) => example?.prompt || "").join(" ") : "";
239194
239870
  return {
@@ -239220,7 +239896,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239220
239896
  const queryTokens = [...new Set(rankingIntentTexts(value).flatMap(rankingTokens))];
239221
239897
  if (queryTokens.length === 0) return /* @__PURE__ */ new Set();
239222
239898
  const ranked = (Array.isArray(tools) ? tools : []).map((tool) => {
239223
- const document = toolRankingDocument(tool);
239899
+ const document = toolRankingDocument(tool, options.trustedToolNames);
239224
239900
  if (!document.name) return null;
239225
239901
  let score = 0;
239226
239902
  let matchedTokens = 0;
@@ -239234,7 +239910,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239234
239910
  if (document.nameTokens.has(token)) exactNameMatches += 1;
239235
239911
  else if (relatedSearchTerms(token).some((term) => document.nameTokens.has(term))) relatedNameMatches += 1;
239236
239912
  }
239237
- return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || relatedNameMatches >= 1 && score >= 5 ? {
239913
+ return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || queryTokens.length === 1 && relatedNameMatches >= 1 && score >= 5 ? {
239238
239914
  name: document.name,
239239
239915
  score,
239240
239916
  matchedTokens,
@@ -239252,17 +239928,15 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239252
239928
  if (!supportChoice.match(/^(?:tool|skill)\s*:\s*(.+)$/iu)?.[1]) return /* @__PURE__ */ new Set();
239253
239929
  return rankedToolNamesForTurnText(tools, supportChoice, { maxTools: 1 });
239254
239930
  }
239255
- function searchRelatedDeferredTools(tools, query, options = {}) {
239931
+ function searchRelatedDeferredTools(tools, query) {
239256
239932
  const normalized = normalizeDeferredToolQuery(query).toLowerCase();
239257
239933
  if (!normalized || normalized.startsWith("select:")) return [];
239258
- const tokens = normalized.split(/\s+/).filter(Boolean);
239259
- const requestedNameTokens = tokens.filter((token) => token.startsWith("+")).map((token) => token.slice(1)).filter(Boolean);
239260
- const relaxRequiredNameTokens = options.relaxRequiredNameTokens === true && requestedNameTokens.length > 0;
239261
- const requiredNameTokens = relaxRequiredNameTokens ? [] : requestedNameTokens;
239262
- const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token));
239934
+ const tokens = normalized.match(/\+?[\p{L}\p{N}_-]+/gu) || [];
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));
239263
239937
  if (searchTokens.length < 2 && !TOOL_SEARCH_SINGLE_TERM_RELATED_FALLBACKS.has(searchTokens[0])) return [];
239264
239938
  const minimumMatchedTokens = searchTokens.length === 1 ? 1 : Math.max(2, Math.ceil(searchTokens.length * .75));
239265
- const ranked = tools.map((tool) => {
239939
+ return tools.map((tool) => {
239266
239940
  const name = String(tool?.name || "").toLowerCase();
239267
239941
  const description = String(tool?.description || "").toLowerCase();
239268
239942
  if (!requiredNameTokens.every((token) => name.includes(token))) return null;
@@ -239293,12 +239967,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239293
239967
  score,
239294
239968
  matchedTokens
239295
239969
  } : null;
239296
- }).filter(Boolean).sort((left, right) => right.matchedTokens - left.matchedTokens || right.score - left.score || String(left.tool.name).localeCompare(String(right.tool.name)));
239297
- if (relaxRequiredNameTokens && ranked.length > 0) {
239298
- const best = ranked[0];
239299
- return ranked.filter((entry) => entry.matchedTokens === best.matchedTokens && entry.score === best.score).slice(0, 5).map((entry) => entry.tool);
239300
- }
239301
- 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);
239302
239971
  }
239303
239972
  function rememberLoadedTool(loadedToolNames, name) {
239304
239973
  loadedToolNames.delete(name);
@@ -239323,13 +239992,12 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239323
239992
  const usedNames = new Set([...selectedTools, ...available.values()].map((tool) => String(tool?.name ?? "")));
239324
239993
  let loaderName = DEFERRED_TOOL_LOADER_NAME;
239325
239994
  for (let suffix = 1; usedNames.has(loaderName); suffix += 1) loaderName = `${DEFERRED_TOOL_LOADER_NAME}_${suffix}`;
239326
- const deferredCount = available.size;
239327
239995
  const loader = {
239328
239996
  name: loaderName,
239329
239997
  description: [
239330
- `Search ${deferredCount} deferred ${deferredCount === 1 ? "tool schema" : "tool schemas"} without loading all definitions.`,
239331
- "Use select:leaf_name for one known tool, plain keywords to search, or +word to require that word in the tool name.",
239332
- `Loaded tools are available in the next step; the ${MAX_PERSISTENT_DEFERRED_TOOLS} most recently selected schemas remain loaded.`
239998
+ "Search deferred tool schemas without loading all definitions.",
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.",
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.`
239333
240001
  ].join("\n"),
239334
240002
  parameters: {
239335
240003
  type: "object",
@@ -239346,17 +240014,18 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239346
240014
  return {
239347
240015
  description: query ? `Searching tool schemas for ${query}` : "Searching tool schemas",
239348
240016
  approvalRule: loaderName,
239349
- execute: async () => {
240017
+ execute: async (context) => {
240018
+ context?.signal?.throwIfAborted();
240019
+ if (query.toLowerCase().startsWith("catalog:")) return {
240020
+ isError: false,
240021
+ output: boundedDeferredToolCatalog([...available.values()], query.slice(8).trim())
240022
+ };
239350
240023
  let matches = searchDeferredTools([...available.values()], query);
239351
240024
  let relatedFallback = false;
239352
240025
  if (matches.length === 0) {
239353
240026
  matches = searchRelatedDeferredTools([...available.values()], query);
239354
240027
  relatedFallback = matches.length > 0;
239355
240028
  }
239356
- if (matches.length === 0) {
239357
- matches = searchRelatedDeferredTools([...available.values()], query, { relaxRequiredNameTokens: true });
239358
- relatedFallback = matches.length > 0;
239359
- }
239360
240029
  if (matches.length === 0) {
239361
240030
  const alreadyLoaded = query.toLowerCase().startsWith("select:") ? searchDeferredTools(selectedTools.filter((tool) => tool?.name !== loaderName), query) : [];
239362
240031
  if (alreadyLoaded.length === 1) return {
@@ -239395,6 +240064,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239395
240064
  return loader;
239396
240065
  }
239397
240066
  module.exports = {
240067
+ channelMessagesForTurnText,
239398
240068
  isDeferredToolLoader: (tool) => deferredToolLoaders.has(tool),
239399
240069
  CORE_TOOL_NAMES,
239400
240070
  DEFERRED_TOOL_LOADER_NAME,
@@ -240697,10 +241367,14 @@ var ReplayBuilder = class {
240697
241367
  return this.records;
240698
241368
  }
240699
241369
  removeMessagesFrom(records, removedMessages) {
240700
- for (let i = records.length - 1; i >= 0; i--) {
240701
- const record = records[i];
240702
- 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++;
240703
241376
  }
241377
+ records.length = retained;
240704
241378
  }
240705
241379
  };
240706
241380
  //#endregion
@@ -241639,6 +242313,7 @@ function withThinkingOnlyRecovery(llm, onTimeout, observeExecution) {
241639
242313
  systemPrompt: llm.systemPrompt,
241640
242314
  modelName: llm.modelName,
241641
242315
  capability: llm.capability,
242316
+ rebuildsMessagesAtRequestBoundary: llm.rebuildsMessagesAtRequestBoundary,
241642
242317
  prepareRequestBoundary: llm.prepareRequestBoundary?.bind(llm),
241643
242318
  isRetryableError: llm.isRetryableError === void 0 ? void 0 : (error) => !recoveryCallbackFailed && !(error instanceof ThinkingOnlyTimeoutError) && (llm.isRetryableError?.(error) ?? false),
241644
242319
  async chat(params) {
@@ -249872,7 +250547,9 @@ async function executeLoopStep(deps) {
249872
250547
  if (beforeStep?.block === true) throw new Error(beforeStep.reason ?? `Step ${String(currentStep)} was blocked`);
249873
250548
  }
249874
250549
  signal.throwIfAborted();
249875
- 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();
249876
250553
  signal.throwIfAborted();
249877
250554
  const stepUuid = randomUUID();
249878
250555
  const stepEvents = createStepEventGate({
@@ -249882,7 +250559,6 @@ async function executeLoopStep(deps) {
249882
250559
  stepUuid,
249883
250560
  onStepStarted
249884
250561
  });
249885
- const prepareRequestBoundary = llm.prepareRequestBoundary?.bind(llm);
249886
250562
  const step = {
249887
250563
  tools,
249888
250564
  hooks,
@@ -249962,7 +250638,7 @@ async function executeLoopStep(deps) {
249962
250638
  turnStep: `${turnId}.${String(currentStep)}`,
249963
250639
  model: llm.modelName
249964
250640
  });
249965
- const strictMessages = await buildMessagesStrict();
250641
+ const strictMessages = deferMessageBuild ? [] : await buildMessagesStrict();
249966
250642
  signal.throwIfAborted();
249967
250643
  let strictParams = {
249968
250644
  ...chatParams,
@@ -250063,7 +250739,8 @@ function logStepTiming(log, turnId, step, response) {
250063
250739
  inputOtherTokens: response.usage.inputOther,
250064
250740
  cacheReadTokens: response.usage.inputCacheRead,
250065
250741
  cacheCreationTokens: response.usage.inputCacheCreation,
250066
- outputTokens: response.usage.output
250742
+ outputTokens: response.usage.output,
250743
+ ...response.usage.outputReasoning === void 0 ? {} : { outputReasoningTokens: response.usage.outputReasoning }
250067
250744
  });
250068
250745
  }
250069
250746
  function deriveStepStopReason(response) {
@@ -251051,6 +251728,7 @@ const MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE = "mcp.oauth.authorization_url";
251051
251728
  const tokenUsageSchema = object({
251052
251729
  inputOther: number$1(),
251053
251730
  output: number$1(),
251731
+ outputReasoning: number$1().optional(),
251054
251732
  inputCacheRead: number$1(),
251055
251733
  inputCacheCreation: number$1()
251056
251734
  });
@@ -251210,7 +251888,11 @@ const goalChangeStatsSchema = object({
251210
251888
  wallClockMs: number$1()
251211
251889
  });
251212
251890
  const goalChangeSchema = object({
251213
- kind: _enum(["lifecycle", "completion"]),
251891
+ kind: _enum([
251892
+ "lifecycle",
251893
+ "completion",
251894
+ "edited"
251895
+ ]),
251214
251896
  status: goalStatusSchema.optional(),
251215
251897
  reason: string().optional(),
251216
251898
  stats: goalChangeStatsSchema.optional(),
@@ -251361,6 +252043,12 @@ const turnEndReasonSchema = _enum([
251361
252043
  "failed",
251362
252044
  "filtered"
251363
252045
  ]);
252046
+ const contextChangedEventSchema = object({
252047
+ type: literal("context.changed"),
252048
+ generation: string().uuid(),
252049
+ revision: number$1().int().positive().max(Number.MAX_SAFE_INTEGER),
252050
+ reason: _enum(["clear", "undo"])
252051
+ });
251364
252052
  const agentStatusUpdatedEventSchema = object({
251365
252053
  type: literal("agent.status.updated"),
251366
252054
  model: string().optional(),
@@ -251680,6 +252368,7 @@ const eventSchema = discriminatedUnion("type", [
251680
252368
  errorEventSchema,
251681
252369
  warningEventSchema,
251682
252370
  agentStatusUpdatedEventSchema,
252371
+ contextChangedEventSchema,
251683
252372
  sessionMetaUpdatedEventSchema,
251684
252373
  sessionCreatedEventSchema,
251685
252374
  workspaceCreatedEventSchema,
@@ -265080,9 +265769,16 @@ var ToolCallDeduplicator = class {
265080
265769
  //#region ../../packages/agent-core/src/agent/turn/tool-result-budget.ts
265081
265770
  const TOOL_RESULT_MAX_CHARS = 5e4;
265082
265771
  const TOOL_RESULT_PREVIEW_CHARS = 2e3;
265772
+ function toolResultMaxChars(maxContextTokens) {
265773
+ if (maxContextTokens === void 0 || !Number.isFinite(maxContextTokens) || maxContextTokens <= 0) return TOOL_RESULT_MAX_CHARS;
265774
+ if (maxContextTokens < 1e5) return 16e3;
265775
+ if (maxContextTokens < 2e5) return 32e3;
265776
+ return TOOL_RESULT_MAX_CHARS;
265777
+ }
265083
265778
  async function budgetToolResultForModel(options) {
265084
265779
  const text = persistableToolResultText(options.result.output);
265085
- if (text === void 0 || text.length <= TOOL_RESULT_MAX_CHARS) return options.result;
265780
+ const maxChars = toolResultMaxChars(options.maxContextTokens);
265781
+ if (text === void 0 || text.length <= maxChars) return options.result;
265086
265782
  if (options.result.truncated === true) return options.result;
265087
265783
  if (options.homedir === void 0) return options.result;
265088
265784
  if (text.includes("\0") || Buffer.from(text, "utf8").toString("utf8") !== text || text.split("\n").some((line) => line.length > 2e3)) return options.result;
@@ -265093,7 +265789,7 @@ async function budgetToolResultForModel(options) {
265093
265789
  toolCallId: options.toolCallId
265094
265790
  }, text);
265095
265791
  if (outputPath === void 0) return options.result;
265096
- const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath);
265792
+ const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath, maxChars);
265097
265793
  return options.result.isError === true ? {
265098
265794
  ...options.result,
265099
265795
  output,
@@ -265125,9 +265821,9 @@ async function saveToolResult(options, text) {
265125
265821
  return;
265126
265822
  }
265127
265823
  }
265128
- function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
265824
+ function renderPersistedToolResult(toolName, toolCallId, text, outputPath, maxChars) {
265129
265825
  const lines = [
265130
- `Tool output exceeded ${String(TOOL_RESULT_MAX_CHARS)} characters; showing a preview only.`,
265826
+ `Tool output exceeded ${String(maxChars)} characters; showing a preview only.`,
265131
265827
  `tool_name: ${toolName}`,
265132
265828
  `tool_call_id: ${toolCallId}`,
265133
265829
  `output_size_chars: ${String(text.length)}`,
@@ -265135,7 +265831,9 @@ function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
265135
265831
  `output_path: ${outputPath}`,
265136
265832
  "next_step: Use Read with output_path to page through the full output."
265137
265833
  ];
265138
- 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);
265139
265837
  return lines.join("\n");
265140
265838
  }
265141
265839
  function safeToolResultFileStem(toolName, toolCallId) {
@@ -266692,6 +267390,48 @@ var HistoricalToolProjection = class {
266692
267390
  }
266693
267391
  };
266694
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
266695
267435
  //#region ../../packages/agent-core/src/agent/response-review-decision.ts
266696
267436
  const STAGES = [
266697
267437
  "setup",
@@ -267000,6 +267740,23 @@ function createReviewedOutputDispatcher(options) {
267000
267740
  }
267001
267741
  //#endregion
267002
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
+ }
267003
267760
  function blunExtractText(content) {
267004
267761
  if (typeof content === "string") return content;
267005
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(" ");
@@ -267019,7 +267776,7 @@ function blunTurnNeedsTools(input, origin) {
267019
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);
267020
267777
  }
267021
267778
  function blunThinkingIntentInput(input) {
267022
- 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;
267023
267780
  return channelText === void 0 ? input : [{
267024
267781
  type: "text",
267025
267782
  text: channelText
@@ -267093,14 +267850,51 @@ function blunToolsForOrigin(tools, origin) {
267093
267850
  * the ordinary permission pipeline.
267094
267851
  */
267095
267852
  function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PURE__ */ new Set(), requiredToolNames = /* @__PURE__ */ new Set(), selected = []) {
267096
- if (estimateTokensForTools(tools) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens) || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
267853
+ const schemaBudget = (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens);
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) {
267097
267879
  selected.splice(0, selected.length, ...tools);
267098
267880
  return selected;
267099
267881
  }
267100
- selected.splice(0, selected.length, ...tools.filter((tool) => BLUN_LEAN_TOOL_NAMES.has(tool.name) || BLUN_LEAN_KEEP_RE.test(tool.name) || BLUN_LANGUAGE_REVIEW_TOOL_NAMES.has(tool.name) || loadedToolNames.has(tool.name) || requiredToolNames.has(tool.name)));
267101
- const names = new Set(selected.map((tool) => tool.name));
267102
- const deferred = tools.filter((tool) => !names.has(tool.name));
267103
- if (deferred.length > 0) selected.push((0, import_turn_tool_performance_policy.createDeferredToolLoader)(selected, deferred, loadedToolNames));
267882
+ const priorLoaderIndex = selected.findIndex(import_turn_tool_performance_policy.isDeferredToolLoader);
267883
+ const justLoaded = new Set(priorLoaderIndex < 0 ? [] : selected.slice(priorLoaderIndex + 1).filter((tool) => loadedToolNames.has(tool.name)).map((tool) => tool.name));
267884
+ const remembered = new Set(loadedToolNames);
267885
+ const choose = () => {
267886
+ selected.splice(0, selected.length, ...tools.filter((tool) => BLUN_LEAN_TOOL_NAMES.has(tool.name) || BLUN_LEAN_KEEP_RE.test(tool.name) || BLUN_LANGUAGE_REVIEW_TOOL_NAMES.has(tool.name) || remembered.has(tool.name) || requiredToolNames.has(tool.name)));
267887
+ const names = new Set(selected.map((tool) => tool.name));
267888
+ const deferred = tools.filter((tool) => !names.has(tool.name));
267889
+ if (deferred.length > 0) selected.push((0, import_turn_tool_performance_policy.createDeferredToolLoader)(selected, deferred, loadedToolNames));
267890
+ };
267891
+ choose();
267892
+ for (const name of loadedToolNames) {
267893
+ if (estimateSelection(selected) <= schemaBudget) break;
267894
+ if (justLoaded.has(name)) continue;
267895
+ if (!remembered.delete(name)) continue;
267896
+ choose();
267897
+ }
267104
267898
  return selected;
267105
267899
  }
267106
267900
  const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
@@ -267460,6 +268254,7 @@ var TurnFlow = class {
267460
268254
  }
267461
268255
  finishResume() {
267462
268256
  if (this.activeTurn === "resuming") this.activeTurn = null;
268257
+ restoreRecentMcpToolNames(this.agent.context.history, this.loadedToolNames);
267463
268258
  this.steerBuffer.length = 0;
267464
268259
  }
267465
268260
  /**
@@ -267590,6 +268385,7 @@ var TurnFlow = class {
267590
268385
  const appendedMessage = this.agent.context.history.at(-1);
267591
268386
  const turnAnchor = appendedMessage !== previousLastMessage && appendedMessage?.role === "user" && appendedMessage.origin === origin ? appendedMessage : void 0;
267592
268387
  const startedAt = Date.now();
268388
+ const preRequestHookStartedAt = performance.now();
267593
268389
  let ended;
267594
268390
  let blockedByUserPromptHook = false;
267595
268391
  let completedStopReason;
@@ -267615,6 +268411,13 @@ var TurnFlow = class {
267615
268411
  this.agent.log.warn("Turn-start observation failed");
267616
268412
  }
267617
268413
  signal.throwIfAborted();
268414
+ if (this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
268415
+ this.agent.log.info("turn pre-request phase", {
268416
+ turnId,
268417
+ phase: "turn-hooks",
268418
+ durationMs: Math.max(0, Math.round(performance.now() - preRequestHookStartedAt))
268419
+ });
268420
+ } catch {}
267618
268421
  const stopReason = await this.runStepLoop(turnId, signal, input, origin, turnAnchor, (response) => {
267619
268422
  completedResponse = response;
267620
268423
  });
@@ -267726,6 +268529,10 @@ var TurnFlow = class {
267726
268529
  signal.throwIfAborted();
267727
268530
  const blockResult = renderUserPromptHookBlockResult(promptHookResults);
267728
268531
  if (blockResult !== void 0) {
268532
+ this.agent.log.warn("turn blocked by UserPromptSubmit hook", {
268533
+ turnId,
268534
+ reason: blockResult.message
268535
+ });
267729
268536
  this.agent.context.appendMessage({
267730
268537
  role: "assistant",
267731
268538
  content: [{
@@ -267784,9 +268591,26 @@ var TurnFlow = class {
267784
268591
  let stopHookContinuationUsed = false;
267785
268592
  let goalOutcomeMessageContinuationUsed = false;
267786
268593
  const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
268594
+ const diagnosePreRequest = this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1";
268595
+ const recordPreRequestPhase = (phase, startedAt) => {
268596
+ if (!diagnosePreRequest) return;
268597
+ try {
268598
+ this.agent.log.info("turn pre-request phase", {
268599
+ turnId,
268600
+ phase,
268601
+ durationMs: Math.max(0, Math.round(performance.now() - startedAt))
268602
+ });
268603
+ } catch {}
268604
+ };
268605
+ let phaseStartedAt = performance.now();
267787
268606
  await this.agent.mcp?.waitForInitialLoad(signal);
268607
+ recordPreRequestPhase("mcp-initial-load", phaseStartedAt);
268608
+ phaseStartedAt = performance.now();
267788
268609
  const personalMemoryRecall = await this.agent.injection.injectPersonalMemoryForTurn(turnId, input, origin, signal);
268610
+ recordPreRequestPhase("personal-memory-recall", phaseStartedAt);
268611
+ phaseStartedAt = performance.now();
267789
268612
  await this.agent.injection.injectGoal();
268613
+ recordPreRequestPhase("goal-injection", phaseStartedAt);
267790
268614
  this.setActiveSteerAcceptance(turnId, true);
267791
268615
  const turnNeedsThinking = blunTurnNeedsTools(blunThinkingIntentInput(input), origin);
267792
268616
  let toolInput = input;
@@ -267797,23 +268621,27 @@ var TurnFlow = class {
267797
268621
  const raw = [...this.agent.context.history];
267798
268622
  const history = personalMemoryRecall === void 0 ? raw : [...raw, personalMemoryRecall];
267799
268623
  const selected = taskToolsNeeded ? history : greetingHistory(history, greetingAnchor);
268624
+ const projectionStartedAt = performance.now();
267800
268625
  const projected = await this.historicalToolProjection.project(selected, turnAnchor, tools, {
267801
268626
  turnId: String(turnId),
267802
268627
  stepNumber: this.currentStep,
267803
268628
  signal,
267804
268629
  llm: turnLLM
267805
268630
  });
268631
+ recordPreRequestPhase("historical-tool-projection", projectionStartedAt);
267806
268632
  const current = this.agent.context.history;
267807
268633
  if (current.length !== raw.length || current.some((message, index) => message !== raw[index])) {
267808
268634
  const latest = personalMemoryRecall === void 0 ? current : [...current, personalMemoryRecall];
267809
268635
  const retained = taskToolsNeeded ? latest : greetingHistory(latest, greetingAnchor);
267810
- partialContext = retained.length < latest.length;
267811
- return retained;
268636
+ const request = projectBaselineSkillForTurn(retained, toolInput, origin, turnAnchor);
268637
+ partialContext = retained.length < latest.length || request !== retained;
268638
+ return request;
267812
268639
  }
267813
- partialContext = selected.length < history.length || projected.changed;
267814
- 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;
267815
268643
  };
267816
- const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off");
268644
+ const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off", turnId);
267817
268645
  const turnTools = this.agent.tools.captureTurnTools();
267818
268646
  while (true) {
267819
268647
  signal.throwIfAborted();
@@ -267823,18 +268651,22 @@ var TurnFlow = class {
267823
268651
  try {
267824
268652
  const selectedTools = [];
267825
268653
  this.refreshSelectedTools = (snapshot) => {
268654
+ const selectionStartedAt = performance.now();
267826
268655
  if (snapshot.messages.length > 0) toolInput = snapshot.messages.flatMap((message) => message.content);
267827
268656
  const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, turnTools()), origin);
267828
268657
  const intent = blunThinkingIntentInput(toolInput);
267829
268658
  taskToolsNeeded ||= blunTurnNeedsTools(intent, origin) || blunTurnHasAttachment(toolInput);
267830
268659
  const required = (0, import_turn_tool_performance_policy.mediaToolNamesForTurnText)(blunExtractText(toolInput));
267831
268660
  if (taskToolsNeeded) {
267832
- blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames, required, selectedTools);
267833
- for (const name of (0, import_turn_tool_performance_policy.rankedToolNamesForTurnText)(eligibleTools, blunExtractText(intent))) {
268661
+ const maxContextTokens = this.agent.fullCompaction.getEffectiveMaxContextTokens();
268662
+ blunSelectTurnTools(eligibleTools, maxContextTokens, this.loadedToolNames, required, selectedTools);
268663
+ const trustedToolNames = new Set(eligibleTools.filter((tool) => this.agent.tools.isUserTool(tool)).map((tool) => tool.name));
268664
+ for (const name of (0, import_turn_tool_performance_policy.rankedToolNamesForTurnText)(eligibleTools, blunExtractText(intent), { trustedToolNames })) {
267834
268665
  const tool = eligibleTools.find((candidate) => candidate.name === name);
267835
- 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);
268666
+ if (tool !== void 0 && !selectedTools.some((candidate) => candidate.name === name) && estimateTokensForTools([...selectedTools, tool]) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens)) selectedTools.push(tool);
267836
268667
  }
267837
268668
  } 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)));
268669
+ recordPreRequestPhase("tool-selection", selectionStartedAt);
267838
268670
  return selectedTools;
267839
268671
  };
267840
268672
  return (await runTurn({
@@ -267847,14 +268679,24 @@ var TurnFlow = class {
267847
268679
  }, completion);
267848
268680
  },
267849
268681
  llm: turnLLM,
267850
- buildMessages: async () => this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true }),
267851
- buildMessagesStrict: async () => this.agent.context.project(await turnHistory(selectedTools), {
267852
- synthesizeMissing: true,
267853
- dropOrphanResults: true,
267854
- dedupeDuplicateToolCalls: true,
267855
- dropLeadingNonUser: true,
267856
- mergeConsecutiveAssistants: true
267857
- }),
268682
+ buildMessages: async () => {
268683
+ const startedAt = performance.now();
268684
+ const messages = this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true });
268685
+ recordPreRequestPhase("message-build", startedAt);
268686
+ return messages;
268687
+ },
268688
+ buildMessagesStrict: async () => {
268689
+ const startedAt = performance.now();
268690
+ const messages = this.agent.context.project(await turnHistory(selectedTools), {
268691
+ synthesizeMissing: true,
268692
+ dropOrphanResults: true,
268693
+ dedupeDuplicateToolCalls: true,
268694
+ dropLeadingNonUser: true,
268695
+ mergeConsecutiveAssistants: true
268696
+ });
268697
+ recordPreRequestPhase("message-build-strict", startedAt);
268698
+ return messages;
268699
+ },
267858
268700
  dispatchEvent: this.buildDispatchEvent(turnId, signal, onFinalResponse, () => partialContext),
267859
268701
  tools: selectedTools,
267860
268702
  log: this.agent.log,
@@ -267871,8 +268713,10 @@ var TurnFlow = class {
267871
268713
  },
267872
268714
  hooks: {
267873
268715
  beforeStep: async ({ signal: stepSignal, llm }) => {
268716
+ const injectionStartedAt = performance.now();
267874
268717
  this.agent.microCompaction.detect();
267875
268718
  await this.agent.injection.inject();
268719
+ recordPreRequestPhase("step-injection", injectionStartedAt);
267876
268720
  stepSignal.throwIfAborted();
267877
268721
  deduper.beginStep();
267878
268722
  },
@@ -267910,6 +268754,17 @@ var TurnFlow = class {
267910
268754
  signal.throwIfAborted();
267911
268755
  if (stopBlock !== void 0) {
267912
268756
  stopHookContinuationUsed = true;
268757
+ this.agent.log.warn("turn continuation blocked by Stop hook", {
268758
+ turnId,
268759
+ reason: stopBlock.reason
268760
+ });
268761
+ this.agent.emitEvent({
268762
+ type: "hook.result",
268763
+ turnId,
268764
+ hookEvent: "Stop",
268765
+ content: stopBlock.reason,
268766
+ blocked: true
268767
+ });
267913
268768
  this.agent.context.appendUserMessage([{
267914
268769
  type: "text",
267915
268770
  text: stopBlock.reason
@@ -267949,6 +268804,7 @@ var TurnFlow = class {
267949
268804
  });
267950
268805
  return budgetToolResultForModel({
267951
268806
  homedir: this.agent.homedir,
268807
+ maxContextTokens: this.agent.fullCompaction.getEffectiveMaxContextTokens(),
267952
268808
  toolName: ctx.toolCall.name,
267953
268809
  toolCallId: ctx.toolCall.id,
267954
268810
  result: finalResult
@@ -268806,10 +269662,10 @@ var BashTool = class {
268806
269662
  },
268807
269663
  approvalRule: literalRulePattern(this.name, args.command),
268808
269664
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
268809
- execute: ({ signal, onUpdate, onForegroundTaskStart }) => this.execution(args, signal, onUpdate, onForegroundTaskStart)
269665
+ execute: ({ signal, onUpdate, onForegroundTaskStart, executionOrigin }) => this.execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin)
268810
269666
  };
268811
269667
  }
268812
- spawn(effectiveCwd, command) {
269668
+ spawn(effectiveCwd, command, executionOrigin) {
268813
269669
  const shellCwd = this.isWindowsBash ? windowsPathToPosixPath(effectiveCwd) : effectiveCwd;
268814
269670
  const shellArgs = [
268815
269671
  this.kaos.osEnv.shellPath,
@@ -268826,9 +269682,11 @@ var BashTool = class {
268826
269682
  ...process.env,
268827
269683
  ...noninteractiveEnv
268828
269684
  };
269685
+ delete mergedEnv["BLUN_SHELL"];
269686
+ if (executionOrigin === "operator_shell") mergedEnv["BLUN_SHELL"] = "operator-local";
268829
269687
  return this.kaos.execWithEnv(shellArgs, mergedEnv);
268830
269688
  }
268831
- async execution(args, signal, onUpdate, onForegroundTaskStart) {
269689
+ async execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin) {
268832
269690
  const validationError = this.validateRunRequest(args, signal);
268833
269691
  if (validationError !== void 0) return validationError;
268834
269692
  const startsInBackground = args.run_in_background === true;
@@ -268840,7 +269698,7 @@ var BashTool = class {
268840
269698
  const builder = new ToolResultBuilder();
268841
269699
  let proc;
268842
269700
  try {
268843
- proc = await this.spawn(effectiveCwd, command);
269701
+ proc = await this.spawn(effectiveCwd, command, executionOrigin);
268844
269702
  } catch (error) {
268845
269703
  return {
268846
269704
  isError: true,
@@ -269204,14 +270062,14 @@ function classifySearchError(error) {
269204
270062
  }
269205
270063
  //#endregion
269206
270064
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.md?raw
269207
- var codebase_search_default = "Find code by **meaning**, not by keywords.\n\nThis tool runs a semantic search over the codebase index. It understands what a file or function *does*, not just what words appear in it. Use it when you are looking for *where* something is handled, *which file* implements a concept, or *how* a particular concern is addressed — even when the exact term never appears in the source.\n\n**When to use this tool:**\n- \"Where is keyboard input handled?\" — finds the handler even if the word \"keyboard\" never appears.\n- \"Which file manages the retry logic for API calls?\"\n- \"How does the app decide which model to route a message to?\"\n- \"Find the code that handles user authentication flow.\"\n- Any question where you want to locate code by its *purpose* or *behavior*.\n\n**When NOT to use this tool — use a different one instead:**\n- Exact string matches, regex patterns, or specific identifiers → `Grep`\n- Finding files by name, extension, or path glob → `Glob`\n- Reading a known file path → `Read`\n\n**Parameters:**\n- `query` (required): A natural-language question, concept, or phrase describing what the code does. Examples: `\"keyboard event handling\"`, `\"retry with exponential backoff\"`, `\"where chat messages are routed to the model\"`.\n- `top` (optional, default 5): Maximum number of results to return.\n\n**Output:**\nEach result includes a file path and a relevance snippet. The index is rebuilt incrementally; if a file was recently added it may not appear until the next index update. Always verify the finding by reading the file before relying on it.\n";
270065
+ var codebase_search_default = "Find code by **meaning**, not by keywords.\n\nThis tool runs a semantic search over the codebase index. It understands what a file or function *does*, not just what words appear in it. Use it when you are looking for *where* something is handled, *which file* implements a concept, or *how* a particular concern is addressed — even when the exact term never appears in the source.\n\n**When to use this tool:**\n- \"Where is keyboard input handled?\" — finds the handler even if the word \"keyboard\" never appears.\n- \"Which file manages the retry logic for API calls?\"\n- \"How does the app decide which model to route a message to?\"\n- \"Find the code that handles user authentication flow.\"\n- Any question where you want to locate code by its *purpose* or *behavior*.\n\n**When NOT to use this tool — use a different one instead:**\n- Exact string matches, regex patterns, or specific identifiers → `Grep`\n- Finding files by name, extension, or path glob → `Glob`\n- Reading a known file path → `Read`\n\n**Parameters:**\n- `query` (required): A natural-language question, concept, or phrase describing what the code does. Examples: `\"keyboard event handling\"`, `\"retry with exponential backoff\"`, `\"where chat messages are routed to the model\"`.\n- `top` (optional, default 5, maximum 100): Maximum number of results to return.\n\n**Output:**\nEach result includes a file path and a relevance score, plus the index model and revision. The index is rebuilt incrementally; if a file was recently added it may not appear until the next index update. Always verify the finding by reading the file before relying on it.\n";
269208
270066
  //#endregion
269209
270067
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.ts
269210
270068
  /**
269211
270069
  * CodebaseSearchTool — semantic code search via the codebase-index.
269212
270070
  *
269213
270071
  * Shells out to `python codebase_index.py query <repo> "<frage>" --top N`
269214
- * through spawnSync. The script lives at
270072
+ * through asynchronous execFile. The script lives at
269215
270073
  * `packages/codebase-index/codebase_index.py` relative to the workspace root.
269216
270074
  *
269217
270075
  * Unlike Grep (keyword/regex) or Glob (file-name patterns), this tool finds
@@ -269219,11 +270077,13 @@ var codebase_search_default = "Find code by **meaning**, not by keywords.\n\nThi
269219
270077
  * to the query, so it can locate a keyboard handler even when the word
269220
270078
  * "keyboard" never appears in the source.
269221
270079
  */
270080
+ const MAX_SEARCH_RESULTS = 100;
269222
270081
  const CodebaseSearchInputSchema = object({
269223
270082
  query: string().min(1, "Query cannot be empty.").describe("A natural-language question or concept describing what the code does. Examples: \"keyboard event handling\", \"retry with exponential backoff\", \"where chat messages are routed to the model\"."),
269224
- top: number$1().int().positive().default(5).describe("Maximum number of results to return. Defaults to 5.")
270083
+ top: number$1().int().positive().max(MAX_SEARCH_RESULTS).default(5).describe(`Maximum number of results to return (1-${String(MAX_SEARCH_RESULTS)}). Defaults to 5.`)
269225
270084
  });
269226
270085
  const SCRIPT_TIMEOUT_MS = 6e4;
270086
+ const SCRIPT_MAX_BUFFER = 1024 * 1024;
269227
270087
  function isScriptFile(path) {
269228
270088
  try {
269229
270089
  return statSync(path).isFile();
@@ -269262,45 +270122,72 @@ var CodebaseSearchTool = class {
269262
270122
  },
269263
270123
  approvalRule: literalRulePattern(this.name, args.query),
269264
270124
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.query),
269265
- execute: () => this.execution(args)
270125
+ execute: async (context) => {
270126
+ context.signal.throwIfAborted();
270127
+ return this.execution(args, context.signal);
270128
+ }
269266
270129
  };
269267
270130
  }
269268
- execution(args) {
270131
+ async execution(args, signal) {
269269
270132
  const builder = new ToolResultBuilder();
269270
- if (this.scriptPath.length === 0) return Promise.resolve(builder.error("Could not locate codebase_index.py. Set CODEBASE_INDEX_SCRIPT to its absolute path."));
270133
+ if (!Number.isSafeInteger(args.top) || args.top < 1 || args.top > MAX_SEARCH_RESULTS) return builder.error(`Codebase search top must be between 1 and ${String(MAX_SEARCH_RESULTS)}.`);
270134
+ if (this.scriptPath.length === 0) return builder.error("Could not locate codebase_index.py. Set CODEBASE_INDEX_SCRIPT to its absolute path.");
269271
270135
  try {
269272
- const result = spawnSync("python", [
269273
- this.scriptPath,
269274
- "query",
269275
- this.cwd,
269276
- args.query,
269277
- "--top",
269278
- String(args.top),
269279
- "--model",
269280
- "auto"
269281
- ], {
269282
- cwd: this.cwd,
269283
- timeout: SCRIPT_TIMEOUT_MS,
269284
- encoding: "utf-8",
269285
- windowsHide: true
270136
+ const result = await new Promise((resolve) => {
270137
+ let closed = false;
270138
+ let output;
270139
+ const finish = () => {
270140
+ if (closed && output !== void 0) resolve(output);
270141
+ };
270142
+ execFile("python", [
270143
+ this.scriptPath,
270144
+ "query",
270145
+ this.cwd,
270146
+ args.query,
270147
+ "--top",
270148
+ String(args.top),
270149
+ "--model",
270150
+ "auto"
270151
+ ], {
270152
+ cwd: this.cwd,
270153
+ timeout: SCRIPT_TIMEOUT_MS,
270154
+ encoding: "utf-8",
270155
+ windowsHide: true,
270156
+ shell: false,
270157
+ maxBuffer: SCRIPT_MAX_BUFFER,
270158
+ signal
270159
+ }, (error, stdout, stderr) => {
270160
+ output = {
270161
+ error,
270162
+ stdout,
270163
+ stderr
270164
+ };
270165
+ finish();
270166
+ }).once("close", () => {
270167
+ closed = true;
270168
+ finish();
270169
+ });
269286
270170
  });
269287
- if (result.error !== void 0) {
270171
+ signal.throwIfAborted();
270172
+ if (result.error !== null && typeof result.error.code !== "number") {
269288
270173
  const msg = result.error instanceof Error ? result.error.message : String(result.error);
269289
- return Promise.resolve(builder.error(`Failed to run codebase_index.py: ${msg}`));
270174
+ builder.write(result.stdout + result.stderr);
270175
+ return builder.error(`Failed to run codebase_index.py: ${msg}`);
269290
270176
  }
269291
270177
  const stdout = result.stdout ?? "";
269292
270178
  const stderr = result.stderr ?? "";
269293
- const exitCode = result.status ?? -1;
270179
+ const exitCode = result.error?.code ?? 0;
269294
270180
  if (exitCode !== 0) {
269295
270181
  const combined = stderr.trim().length > 0 ? `${stdout}${stdout.length > 0 ? "\n" : ""}Error: ${stderr.trim()}` : stdout;
269296
270182
  builder.write(combined.length > 0 ? combined : `codebase_index.py exited with code ${String(exitCode)}`);
269297
- return Promise.resolve(builder.error(`Codebase search failed with exit code ${String(exitCode)}.`));
270183
+ return builder.error(`Codebase search failed with exit code ${String(exitCode)}.`);
269298
270184
  }
269299
270185
  builder.write(stdout.length > 0 ? stdout : "No results found.");
269300
- return Promise.resolve(builder.ok());
270186
+ return builder.ok();
269301
270187
  } catch (error) {
270188
+ signal.throwIfAborted();
269302
270189
  const msg = error instanceof Error ? error.message : String(error);
269303
- return Promise.resolve(builder.error(`Codebase search error: ${msg}`));
270190
+ return builder.error(`Codebase search error: ${msg}`);
269304
270191
  }
269305
270192
  }
269306
270193
  };
@@ -270540,6 +271427,7 @@ var ToolManager = class {
270540
271427
  const result = await execution.execute({
270541
271428
  turnId: "",
270542
271429
  toolCallId: "shell-command",
271430
+ executionOrigin: "operator_shell",
270543
271431
  signal: controller.signal,
270544
271432
  onUpdate: (update) => {
270545
271433
  if (update.kind === "stdout") stdout += update.text ?? "";
@@ -270628,6 +271516,9 @@ var ToolManager = class {
270628
271516
  hasUserTool(name) {
270629
271517
  return this.userTools.has(name);
270630
271518
  }
271519
+ isUserTool(tool) {
271520
+ return this.userTools.get(tool.name) === tool;
271521
+ }
270631
271522
  inheritUserTools(parent) {
270632
271523
  for (const tool of parent.userTools.values()) {
270633
271524
  if (!parent.enabledTools.has(tool.name)) continue;
@@ -271327,15 +272218,17 @@ var FallbackLLM = class {
271327
272218
  onModelChanged;
271328
272219
  prepareRequest;
271329
272220
  onContextOverflow;
272221
+ messagePreparation;
271330
272222
  currentIndex = 0;
271331
272223
  attemptedProviders = /* @__PURE__ */ new Set();
271332
272224
  errorsAfterOutput = /* @__PURE__ */ new WeakSet();
271333
272225
  rebuildBoundaryMessages;
271334
- constructor(candidates, onModelChanged = () => {}, prepareRequest, onContextOverflow) {
272226
+ constructor(candidates, onModelChanged = () => {}, prepareRequest, onContextOverflow, messagePreparation = "eager") {
271335
272227
  this.candidates = candidates;
271336
272228
  this.onModelChanged = onModelChanged;
271337
272229
  this.prepareRequest = prepareRequest;
271338
272230
  this.onContextOverflow = onContextOverflow;
272231
+ this.messagePreparation = messagePreparation;
271339
272232
  if (candidates.length === 0) throw new Error("FallbackLLM requires at least one model candidate.");
271340
272233
  }
271341
272234
  get activeAlias() {
@@ -271350,6 +272243,9 @@ var FallbackLLM = class {
271350
272243
  get capability() {
271351
272244
  return this.current.llm.capability;
271352
272245
  }
272246
+ get rebuildsMessagesAtRequestBoundary() {
272247
+ return this.messagePreparation === "boundary" && this.prepareRequest !== void 0;
272248
+ }
271353
272249
  async prepareRequestBoundary(params, rebuildMessages) {
271354
272250
  params.signal.throwIfAborted();
271355
272251
  this.rebuildBoundaryMessages = rebuildMessages;
@@ -271412,6 +272308,7 @@ var FallbackLLM = class {
271412
272308
  throw new Error("Fallback request preparation requires a fresh request boundary.");
271413
272309
  }
271414
272310
  let requestParams = params;
272311
+ let messagesReady = false;
271415
272312
  const rebuildRequestMessages = async () => {
271416
272313
  const messages = await rebuildMessages();
271417
272314
  const suffix = requestParams.boundaryMessageSuffix;
@@ -271420,7 +272317,14 @@ var FallbackLLM = class {
271420
272317
  while (true) {
271421
272318
  const candidate = this.current;
271422
272319
  try {
271423
- requestParams = await this.prepareRequest?.(candidate, requestParams, rebuildRequestMessages) ?? requestParams;
272320
+ const prepared = await this.prepareRequest?.(candidate, requestParams, rebuildRequestMessages);
272321
+ if (prepared !== void 0) if ("messagesReady" in prepared) {
272322
+ requestParams = prepared.params;
272323
+ messagesReady = true;
272324
+ } else {
272325
+ requestParams = prepared;
272326
+ messagesReady = false;
272327
+ }
271424
272328
  } catch (error) {
271425
272329
  if (!isFallbackCandidateError(error) && !isUnavailableFallbackCandidate(error)) throw error;
271426
272330
  const nextIndex = this.findNextCandidateIndex();
@@ -271430,13 +272334,14 @@ var FallbackLLM = class {
271430
272334
  ...requestParams,
271431
272335
  messages: await rebuildRequestMessages()
271432
272336
  };
272337
+ messagesReady = false;
271433
272338
  requestParams.signal.throwIfAborted();
271434
272339
  this.currentIndex = nextIndex;
271435
272340
  this.onModelChanged(this.current.alias);
271436
272341
  continue;
271437
272342
  }
271438
272343
  requestParams.signal.throwIfAborted();
271439
- const messages = await rebuildRequestMessages();
272344
+ const messages = messagesReady ? requestParams.messages : await rebuildRequestMessages();
271440
272345
  requestParams.signal.throwIfAborted();
271441
272346
  return {
271442
272347
  ...requestParams,
@@ -271538,8 +272443,13 @@ var LlmRequestLogger = class {
271538
272443
  toolsEstimatedTokens = 0;
271539
272444
  requestSequence = 0;
271540
272445
  lastTurnRequest;
271541
- constructor(log) {
272446
+ serializedPrefixKey;
272447
+ requestSignatures = /* @__PURE__ */ new Map();
272448
+ prefixComparisons = /* @__PURE__ */ new Map();
272449
+ lastSerializedRequest;
272450
+ constructor(log, serializedPrefixDiagnostic = process.env["BLUN_DIAG_SERIALIZED_PREFIX"] === "1") {
271542
272451
  this.log = log;
272452
+ this.serializedPrefixKey = serializedPrefixDiagnostic ? randomBytes(32) : void 0;
271543
272453
  }
271544
272454
  logRequest(input) {
271545
272455
  const sequence = ++this.requestSequence;
@@ -271556,6 +272466,10 @@ var LlmRequestLogger = class {
271556
272466
  toolsHash: fingerprint(JSON.stringify(toolSignature(tools)))
271557
272467
  };
271558
272468
  const signature = JSON.stringify(config);
272469
+ if (this.serializedPrefixKey !== void 0) {
272470
+ this.requestSignatures.set(sequence, signature);
272471
+ if (this.requestSignatures.size > 32) this.requestSignatures.delete(this.requestSignatures.keys().next().value);
272472
+ }
271559
272473
  if (signature !== this.lastConfigLogSignature) {
271560
272474
  this.lastConfigLogSignature = signature;
271561
272475
  this.configRevision++;
@@ -271585,6 +272499,20 @@ var LlmRequestLogger = class {
271585
272499
  }
271586
272500
  };
271587
272501
  if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
272502
+ const source = input.systemPromptContributions;
272503
+ if (source?.version === 1 && [
272504
+ source.totalBytes,
272505
+ source.instructionBytes,
272506
+ source.skillListingBytes,
272507
+ source.runtimeMemoryBytes,
272508
+ source.otherBytes
272509
+ ].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 = {
272510
+ totalBytes: source.totalBytes,
272511
+ instructionBytes: source.instructionBytes,
272512
+ skillListingBytes: source.skillListingBytes,
272513
+ runtimeMemoryBytes: source.runtimeMemoryBytes,
272514
+ otherBytes: source.otherBytes
272515
+ };
271588
272516
  this.log.info("llm request", requestFields);
271589
272517
  if (fields?.turnStep !== void 0 && /^\d+\.\d+$/.test(fields.turnStep)) {
271590
272518
  const historyTextOrigins = summarizeRequestTextOrigins(messages);
@@ -271610,10 +272538,51 @@ var LlmRequestLogger = class {
271610
272538
  getLastTurnRequest() {
271611
272539
  return this.lastTurnRequest === void 0 ? void 0 : structuredClone(this.lastTurnRequest);
271612
272540
  }
272541
+ get serializedPrefixDiagnosticEnabled() {
272542
+ return this.serializedPrefixKey !== void 0;
272543
+ }
272544
+ observeSerializedRequest(body, fields, sequence) {
272545
+ const key = this.serializedPrefixKey;
272546
+ if (key === void 0 || !/^\d+\.\d+$/.test(fields?.turnStep ?? "")) return;
272547
+ const signature = this.requestSignatures.get(sequence);
272548
+ if (signature === void 0) return;
272549
+ try {
272550
+ const bytes = Buffer.from(body, "utf8");
272551
+ const granularityBytes = 1024;
272552
+ const chunks = [];
272553
+ for (let start = 0; start + granularityBytes <= bytes.length; start += granularityBytes) chunks.push(createHmac("sha256", key).update(bytes.subarray(start, start + granularityBytes)).digest("hex"));
272554
+ const previous = this.lastSerializedRequest;
272555
+ if (previous !== void 0 && previous.signature === signature) {
272556
+ let common = 0;
272557
+ while (common < previous.chunks.length && common < chunks.length && previous.chunks[common] === chunks[common]) common++;
272558
+ this.prefixComparisons.set(sequence, {
272559
+ version: 1,
272560
+ granularityBytes,
272561
+ previousBytes: previous.bytes,
272562
+ currentBytes: bytes.length,
272563
+ commonPrefixBytesLowerBound: common * granularityBytes,
272564
+ previousSequence: previous.sequence,
272565
+ currentSequence: sequence
272566
+ });
272567
+ if (this.prefixComparisons.size > 32) this.prefixComparisons.delete(this.prefixComparisons.keys().next().value);
272568
+ }
272569
+ this.lastSerializedRequest = {
272570
+ sequence,
272571
+ signature,
272572
+ bytes: bytes.length,
272573
+ chunks
272574
+ };
272575
+ } catch {
272576
+ this.lastSerializedRequest = void 0;
272577
+ }
272578
+ }
271613
272579
  logTransportAttempt(stats, fields, sequence) {
272580
+ const serializedPrefix = sequence === void 0 ? void 0 : this.prefixComparisons.get(sequence);
271614
272581
  this.log.info("llm transport attempt", {
271615
272582
  ...fields,
271616
- ...stats
272583
+ ...stats,
272584
+ ...this.serializedPrefixKey === void 0 || sequence === void 0 ? {} : { requestSequence: sequence },
272585
+ ...serializedPrefix === void 0 ? {} : { serializedPrefix }
271617
272586
  });
271618
272587
  if (sequence === void 0 || this.lastTurnRequest?.sequence !== sequence) return;
271619
272588
  const breakdown = stats.requestBreakdown;
@@ -271635,6 +272604,11 @@ var LlmRequestLogger = class {
271635
272604
  systemMessageBytes: breakdown.systemMessageBytes,
271636
272605
  conversationMessageBytes: breakdown.conversationMessageBytes,
271637
272606
  toolResultMessageBytes: breakdown.toolResultMessageBytes,
272607
+ ...breakdown.toolResultByTool === void 0 ? {} : { toolResultByTool: breakdown.toolResultByTool.map((tool) => ({
272608
+ count: tool.count,
272609
+ bytes: tool.bytes,
272610
+ ...tool.name === void 0 ? {} : { name: tool.name }
272611
+ })) },
271638
272612
  toolSchemaBytes: breakdown.toolSchemaBytes,
271639
272613
  otherBytes: breakdown.otherBytes,
271640
272614
  ...breakdown.toolArrayOverheadBytes === void 0 ? {} : { toolArrayOverheadBytes: breakdown.toolArrayOverheadBytes },
@@ -271696,7 +272670,8 @@ function copyContributions(value) {
271696
272670
  version: source.version,
271697
272671
  totalBytes: source.totalBytes,
271698
272672
  generatedBytes: source.generatedBytes,
271699
- files: source.files.map((file) => copyInstructionFileBytes(file))
272673
+ files: source.files.map((file) => copyInstructionFileBytes(file)),
272674
+ ...source.notLoadedCandidates === void 0 ? {} : { notLoadedCandidates: [...source.notLoadedCandidates] }
271700
272675
  } }
271701
272676
  };
271702
272677
  }
@@ -271792,7 +272767,11 @@ var Agent$1 = class {
271792
272767
  errorMemory;
271793
272768
  replayBuilder;
271794
272769
  additionalDirs;
272770
+ systemPromptStartedAt = /* @__PURE__ */ new Date();
271795
272771
  activeProfile;
272772
+ rawSystemPromptContext;
272773
+ profileSystemPromptText;
272774
+ agentsMdRequestWarningShown = false;
271796
272775
  brandHome;
271797
272776
  _activeResponderModel;
271798
272777
  allowModelFallback = true;
@@ -271879,6 +272858,7 @@ var Agent$1 = class {
271879
272858
  const retryScope = providerRetryScope(modelAlias, provider);
271880
272859
  await this.providerRetryWait.wait(retryScope, generateOptions?.signal);
271881
272860
  const callerTransportAttempt = generateOptions?.onTransportAttempt;
272861
+ const callerSerializedRequest = generateOptions?.onSerializedRequest;
271882
272862
  const ownedGenerateOptions = {
271883
272863
  ...generateOptions,
271884
272864
  transportMaxRetries: 0
@@ -271901,6 +272881,12 @@ var Agent$1 = class {
271901
272881
  callerTransportAttempt?.(stats);
271902
272882
  this.llmRequestLogger.logTransportAttempt(stats, requestLogFields, requestSequence);
271903
272883
  },
272884
+ ...callerSerializedRequest !== void 0 || this.llmRequestLogger.serializedPrefixDiagnosticEnabled ? { onSerializedRequest: (body) => {
272885
+ try {
272886
+ callerSerializedRequest?.(body);
272887
+ } catch {}
272888
+ this.llmRequestLogger.observeSerializedRequest(body, requestLogFields, requestSequence);
272889
+ } } : {},
271904
272890
  onRequestStart: () => {
271905
272891
  requestOptions?.onRequestStart?.();
271906
272892
  requestStarted = true;
@@ -271926,7 +272912,7 @@ var Agent$1 = class {
271926
272912
  get llm() {
271927
272913
  return this.llmForTurn();
271928
272914
  }
271929
- llmForTurn(thinkingEffort) {
272915
+ llmForTurn(thinkingEffort, diagnosticTurnId) {
271930
272916
  const selectedAlias = this.config.model;
271931
272917
  const modelProvider = this.modelProvider;
271932
272918
  if (modelProvider === void 0) throw new BlunError(ErrorCodes.MODEL_NOT_CONFIGURED, "Provider not set");
@@ -271940,7 +272926,18 @@ var Agent$1 = class {
271940
272926
  tools: requestTools,
271941
272927
  messages: await rebuildMessages()
271942
272928
  };
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));
271943
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
+ }
272940
+ const preflightStartedAt = performance.now();
271944
272941
  const compacted = await this.fullCompaction.beforeStep(params.signal, {
271945
272942
  messages: params.messages,
271946
272943
  additionalMessages: steers.messages,
@@ -271949,21 +272946,42 @@ var Agent$1 = class {
271949
272946
  rebuildSystemPrompt: () => systemPrompt = this.effectiveSystemPrompt,
271950
272947
  tools: params.tools
271951
272948
  });
272949
+ if (this.type === "main" && diagnosticTurnId !== void 0 && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
272950
+ this.log.info("turn pre-request phase", {
272951
+ turnId: diagnosticTurnId,
272952
+ phase: "compaction-preflight",
272953
+ durationMs: Math.max(0, Math.round(performance.now() - preflightStartedAt))
272954
+ });
272955
+ } catch {}
271952
272956
  const tools = params.tools.some((tool) => tool.name === "CompactConversation") && !this.fullCompaction.proactiveCompactionStatus({
271953
272957
  messages: compacted ? await rebuildMessages() : params.messages,
271954
272958
  additionalMessages: steers.messages,
271955
272959
  systemPrompt,
271956
272960
  tools: params.tools
271957
272961
  }).eligible ? params.tools.filter((tool) => tool.name !== "CompactConversation") : params.tools;
271958
- this.turn.commitPendingSteers(steers);
271959
- return {
272962
+ const steersCommitted = this.turn.commitPendingSteers(steers);
272963
+ if (compacted || steersCommitted) params = {
271960
272964
  ...params,
271961
- systemPrompt,
271962
- tools
272965
+ messages: await rebuildMessages()
272966
+ };
272967
+ if (this.config.runtimeModel(_candidate.alias).provider.name === "blun") {
272968
+ const requestBytes = estimateBlunSerializedBodyBytes(systemPrompt, tools, params.messages) + 4096;
272969
+ if (requestBytes >= 5e5) throw new BlunError(ErrorCodes.CONTEXT_OVERFLOW, "The BLUN request is too large after compaction. Shorten fixed instructions or tools.", { details: {
272970
+ estimatedRequestBytes: requestBytes,
272971
+ requestBudgetBytes: 5e5
272972
+ } });
272973
+ }
272974
+ return {
272975
+ params: {
272976
+ ...params,
272977
+ systemPrompt,
272978
+ tools
272979
+ },
272980
+ messagesReady: true
271963
272981
  };
271964
272982
  }, (candidate, _params, error) => {
271965
272983
  this.fullCompaction.observeContextOverflow(error, candidate.alias);
271966
- });
272984
+ }, "boundary");
271967
272985
  }
271968
272986
  createKosongLLM(modelAlias, thinkingEffort) {
271969
272987
  const runtimeModel = this.config.runtimeModel(modelAlias);
@@ -272031,8 +273049,14 @@ var Agent$1 = class {
272031
273049
  }
272032
273050
  useProfile(profile, context, brandHome) {
272033
273051
  this.setActiveProfile(profile, brandHome);
272034
- this.updateSystemPromptFromProfile(profile, context);
273052
+ this.rawSystemPromptContext = context;
273053
+ if (context?.agentsMdText === void 0) {
273054
+ this.updateSystemPromptFromProfile(profile, context);
273055
+ this.tools.setActiveTools(profile.tools);
273056
+ return;
273057
+ }
272035
273058
  this.tools.setActiveTools(profile.tools);
273059
+ this.updateSystemPromptFromProfile(profile, this.fitProfileInstructions(profile, context));
272036
273060
  }
272037
273061
  setActiveProfile(profile, brandHome) {
272038
273062
  this.activeProfile = profile;
@@ -272047,12 +273071,74 @@ var Agent$1 = class {
272047
273071
  async refreshSystemPrompt() {
272048
273072
  if (this.activeProfile === void 0) return;
272049
273073
  const context = this.systemPromptContextProvider === void 0 ? await prepareSystemPromptContext(this.kaos, this.brandHome, { additionalDirs: this.additionalDirs }) : await this.systemPromptContextProvider();
272050
- this.updateSystemPromptFromProfile(this.activeProfile, context);
273074
+ this.rawSystemPromptContext = context;
273075
+ this.updateSystemPromptFromProfile(this.activeProfile, this.fitProfileInstructions(this.activeProfile, context));
273076
+ }
273077
+ fitProfileInstructions(profile, context, tools = this.tools.loopTools, notify = false) {
273078
+ if (context?.agentsMdText === void 0 || this.config.modelAlias === void 0) return context;
273079
+ if (this.config.runtimeModel(this.config.modelAlias).provider.name !== "blun") return context;
273080
+ const renderFixedPrompt = (candidate) => {
273081
+ const rendered = profile.systemPrompt({
273082
+ osEnv: this.kaos.osEnv,
273083
+ now: this.systemPromptStartedAt,
273084
+ cwd: this.config.cwd,
273085
+ skills: this.skills?.registry,
273086
+ cwdListing: candidate.cwdListing,
273087
+ agentsMd: candidate.agentsMd,
273088
+ additionalDirsInfo: candidate.additionalDirsInfo
273089
+ });
273090
+ const compact = this.skills?.registry === void 0 ? rendered : compactLegacyModelSkillListing(rendered, this.skills.registry);
273091
+ return this.runtimeSystemPromptAppend.length === 0 ? compact : compact.length === 0 ? this.runtimeSystemPromptAppend : `${compact}\n\n${this.runtimeSystemPromptAppend}`;
273092
+ };
273093
+ const fitted = fitAgentsMdContext(context, 4e5, (candidate) => {
273094
+ return estimateBlunSerializedBodyBytes(renderFixedPrompt(candidate), tools, []) + 4096;
273095
+ });
273096
+ const describeFixedEstimate = () => {
273097
+ const systemPrompt = renderFixedPrompt(fitted.context);
273098
+ const envelopeBytes = estimateBlunSerializedBodyBytes("", [], []);
273099
+ const withoutToolsBytes = estimateBlunSerializedBodyBytes(systemPrompt, [], []);
273100
+ return {
273101
+ estimationStage: notify ? "request" : "profile",
273102
+ toolCount: tools.length,
273103
+ serializedPromptBytes: withoutToolsBytes - envelopeBytes,
273104
+ serializedToolsBytes: fitted.fixedRequestBytes - 4096 - withoutToolsBytes,
273105
+ envelopeBytes,
273106
+ reserveBytes: 4096,
273107
+ instructionBytes: fitted.context.agentsMdSources?.totalBytes ?? 0,
273108
+ runtimeAppendBytes: Buffer.byteLength(this.runtimeSystemPromptAppend, "utf8")
273109
+ };
273110
+ };
273111
+ if (!fitted.fits) this.log.warn("fixed BLUN request exceeds 400000 bytes without AGENTS.md", {
273112
+ fixedRequestBytes: fitted.fixedRequestBytes,
273113
+ ...describeFixedEstimate()
273114
+ });
273115
+ else if (fitted.omittedBytes > 0) {
273116
+ this.log.warn("AGENTS.md content shortened for BLUN request budget", {
273117
+ omittedBytes: fitted.omittedBytes,
273118
+ fixedRequestBytes: fitted.fixedRequestBytes,
273119
+ ...describeFixedEstimate()
273120
+ });
273121
+ if (notify && !this.agentsMdRequestWarningShown) {
273122
+ this.agentsMdRequestWarningShown = true;
273123
+ const retained = new Map(fitted.context.agentsMdSources?.files.map((file) => [file.path, file.bytes]));
273124
+ const files = context.agentsMdSources?.files.flatMap((file) => {
273125
+ const read = retained.get(file.path) ?? 0;
273126
+ return read < file.bytes ? [`${file.path}: ${file.bytes} bytes, ${read} read, ${file.bytes - read} omitted`] : [];
273127
+ }) ?? [];
273128
+ this.emitEvent({
273129
+ type: "warning",
273130
+ code: "agents-md-request-truncated",
273131
+ message: `Instructions shortened for the request size limit: ${files.join("; ")}.`
273132
+ });
273133
+ }
273134
+ }
273135
+ return fitted.context;
272051
273136
  }
272052
273137
  updateSystemPromptFromProfile(profile, context) {
272053
273138
  let contributions;
272054
273139
  const systemPrompt = profile.systemPrompt({
272055
273140
  osEnv: this.kaos.osEnv,
273141
+ now: this.systemPromptStartedAt,
272056
273142
  cwd: this.config.cwd,
272057
273143
  skills: this.skills?.registry,
272058
273144
  cwdListing: context?.cwdListing,
@@ -272067,6 +273153,7 @@ var Agent$1 = class {
272067
273153
  profileName: profile.name,
272068
273154
  systemPrompt
272069
273155
  });
273156
+ this.profileSystemPromptText = systemPrompt;
272070
273157
  this.renderedPromptContributions = contributions === void 0 ? void 0 : {
272071
273158
  text: systemPrompt,
272072
273159
  value: structuredClone(contributions)
@@ -272243,6 +273330,9 @@ var Agent$1 = class {
272243
273330
  },
272244
273331
  startBtw: () => this.subagentHost.startBtw(),
272245
273332
  createGoal: (payload) => this.goal.createGoal(payload),
273333
+ editGoal: (payload) => this.goal.editGoal(payload),
273334
+ blockGoal: (payload) => this.goal.blockGoal(payload),
273335
+ completeGoal: () => this.goal.completeGoal(),
272246
273336
  getGoal: () => this.goal.getGoal(),
272247
273337
  pauseGoal: () => this.goal.pauseGoal(),
272248
273338
  resumeGoal: () => this.goal.resumeGoal(),
@@ -336497,6 +337587,15 @@ var SessionAPIImpl = class {
336497
337587
  async createGoal({ agentId, ...payload }) {
336498
337588
  return (await this.getAgent(agentId)).createGoal(payload);
336499
337589
  }
337590
+ async editGoal({ agentId, ...payload }) {
337591
+ return (await this.getAgent(agentId)).editGoal(payload);
337592
+ }
337593
+ async blockGoal({ agentId, ...payload }) {
337594
+ return (await this.getAgent(agentId)).blockGoal(payload);
337595
+ }
337596
+ async completeGoal({ agentId }) {
337597
+ return (await this.getAgent(agentId)).completeGoal({});
337598
+ }
336500
337599
  async getGoal({ agentId, ...payload }) {
336501
337600
  return (await this.getAgent(agentId, true)).getGoal(payload);
336502
337601
  }
@@ -337741,6 +338840,15 @@ var BlunCore = class {
337741
338840
  getGoal({ sessionId, ...payload }) {
337742
338841
  return Promise.resolve(this.sessionApi(sessionId).getGoal(payload));
337743
338842
  }
338843
+ editGoal({ sessionId, ...payload }) {
338844
+ return Promise.resolve(this.sessionApi(sessionId).editGoal(payload));
338845
+ }
338846
+ blockGoal({ sessionId, ...payload }) {
338847
+ return Promise.resolve(this.sessionApi(sessionId).blockGoal(payload));
338848
+ }
338849
+ completeGoal({ sessionId, ...payload }) {
338850
+ return Promise.resolve(this.sessionApi(sessionId).completeGoal(payload));
338851
+ }
337744
338852
  pauseGoal({ sessionId, ...payload }) {
337745
338853
  return Promise.resolve(this.sessionApi(sessionId).pauseGoal(payload));
337746
338854
  }
@@ -344942,7 +346050,10 @@ function reduceWireRecords(records) {
344942
346050
  text: record.summary
344943
346051
  }],
344944
346052
  toolCalls: [],
344945
- origin: { kind: "compaction_summary" }
346053
+ origin: {
346054
+ kind: "compaction_summary",
346055
+ ...record.toolContinuity === void 0 ? {} : { toolContinuity: record.toolContinuity }
346056
+ }
344946
346057
  },
344947
346058
  time: record.time
344948
346059
  };
@@ -347193,6 +348304,24 @@ var Session = class {
347193
348304
  this.ensureOpen();
347194
348305
  return this.rpc.getGoal({ sessionId: this.id });
347195
348306
  }
348307
+ async editGoal(input) {
348308
+ this.ensureOpen();
348309
+ return this.rpc.editGoal({
348310
+ sessionId: this.id,
348311
+ objective: input.objective
348312
+ });
348313
+ }
348314
+ async blockGoal(input = {}) {
348315
+ this.ensureOpen();
348316
+ return this.rpc.blockGoal({
348317
+ sessionId: this.id,
348318
+ reason: input.reason
348319
+ });
348320
+ }
348321
+ async completeGoal() {
348322
+ this.ensureOpen();
348323
+ return this.rpc.completeGoal({ sessionId: this.id });
348324
+ }
347196
348325
  async pauseGoal() {
347197
348326
  this.ensureOpen();
347198
348327
  return this.rpc.pauseGoal({ sessionId: this.id });
@@ -347752,6 +348881,39 @@ var BlunAuthFacade = class {
347752
348881
  }
347753
348882
  return this.toolkit.status(providerName, this.resolveRuntimeManagedAuth(providerName).oauthRef);
347754
348883
  }
348884
+ async getCachedMemoryWorkspaceId() {
348885
+ const apiKey = nonEmpty(this.resolveManagedAuth(BLUN_PROVIDER_NAME$1).apiKey);
348886
+ if (apiKey !== void 0) {
348887
+ const provider = readConfigFileForUpdate(this.options.configPath).providers[BLUN_PROVIDER_NAME$1];
348888
+ if (provider?.apiKey !== apiKey || provider.memoryApiKeyHash !== memoryApiKeyHash(apiKey)) return;
348889
+ return provider.memoryWorkspaceId;
348890
+ }
348891
+ return this.toolkit.getCachedMemoryWorkspaceId(this.resolveRuntimeManagedAuth(BLUN_PROVIDER_NAME$1).oauthRef);
348892
+ }
348893
+ async cacheMemoryWorkspaceId(workspaceId) {
348894
+ const apiKey = nonEmpty(this.resolveManagedAuth(BLUN_PROVIDER_NAME$1).apiKey);
348895
+ if (apiKey !== void 0) {
348896
+ const hash = memoryApiKeyHash(apiKey);
348897
+ return mutateConfigFile(this.options.configPath, (config) => {
348898
+ const provider = config.providers[BLUN_PROVIDER_NAME$1];
348899
+ if (provider?.apiKey !== apiKey) return {
348900
+ config,
348901
+ result: false
348902
+ };
348903
+ if (provider.memoryApiKeyHash === hash && provider.memoryWorkspaceId !== void 0 && provider.memoryWorkspaceId !== workspaceId) return {
348904
+ config,
348905
+ result: false
348906
+ };
348907
+ provider.memoryApiKeyHash = hash;
348908
+ provider.memoryWorkspaceId = workspaceId;
348909
+ return {
348910
+ config,
348911
+ result: true
348912
+ };
348913
+ });
348914
+ }
348915
+ return this.toolkit.cacheMemoryWorkspaceId(workspaceId, this.resolveRuntimeManagedAuth(BLUN_PROVIDER_NAME$1).oauthRef);
348916
+ }
347755
348917
  async login(providerName = BLUN_PROVIDER_NAME$1, options = {}) {
347756
348918
  const auth = this.resolveManagedAuth(providerName);
347757
348919
  const loginAuth = resolveBlunCodeLoginAuth({
@@ -348057,6 +349219,9 @@ function nonEmpty(value) {
348057
349219
  const normalized = value?.trim();
348058
349220
  return normalized === void 0 || normalized.length === 0 ? void 0 : normalized;
348059
349221
  }
349222
+ function memoryApiKeyHash(apiKey) {
349223
+ return createHash("sha256").update(apiKey).digest("hex");
349224
+ }
348060
349225
  function transitionAggregateError(message, primary, secondary) {
348061
349226
  return new AggregateError([primary, secondary], message, { cause: primary });
348062
349227
  }
@@ -348537,6 +349702,26 @@ var SDKRpcClientBase = class {
348537
349702
  replace: input.replace
348538
349703
  });
348539
349704
  }
349705
+ async editGoal(input) {
349706
+ return (await this.getRpc()).editGoal({
349707
+ sessionId: input.sessionId,
349708
+ agentId: this.interactiveAgentId,
349709
+ objective: input.objective
349710
+ });
349711
+ }
349712
+ async blockGoal(input) {
349713
+ return (await this.getRpc()).blockGoal({
349714
+ sessionId: input.sessionId,
349715
+ agentId: this.interactiveAgentId,
349716
+ reason: input.reason
349717
+ });
349718
+ }
349719
+ async completeGoal(input) {
349720
+ return (await this.getRpc()).completeGoal({
349721
+ sessionId: input.sessionId,
349722
+ agentId: this.interactiveAgentId
349723
+ });
349724
+ }
348540
349725
  async getGoal(input) {
348541
349726
  return (await this.getRpc()).getGoal({
348542
349727
  sessionId: input.sessionId,
@@ -348785,6 +349970,7 @@ var SDKRpcClient = class extends SDKRpcClientBase {
348785
349970
  core;
348786
349971
  ready;
348787
349972
  usesDefaultConfigPath;
349973
+ restoreProfileModel;
348788
349974
  constructor(options = {}) {
348789
349975
  super();
348790
349976
  this.identity = options.identity === void 0 ? void 0 : assertBlunHostIdentity(options.identity);
@@ -348794,9 +349980,11 @@ var SDKRpcClient = class extends SDKRpcClientBase {
348794
349980
  configPath: options.configPath
348795
349981
  });
348796
349982
  this.usesDefaultConfigPath = options.configPath === void 0;
349983
+ const authHomeDir = authHomeForProfile(this.homeDir);
349984
+ this.restoreProfileModel = this.usesDefaultConfigPath && authHomeDir !== this.homeDir;
348797
349985
  this.telemetry = options.telemetry ?? noopTelemetryClient;
348798
349986
  this.auth = new BlunAuthFacade({
348799
- homeDir: authHomeForProfile(this.homeDir),
349987
+ homeDir: authHomeDir,
348800
349988
  configPath: this.configPath,
348801
349989
  identity: this.identity,
348802
349990
  onRefresh: options.onOAuthRefresh
@@ -348816,7 +350004,7 @@ var SDKRpcClient = class extends SDKRpcClientBase {
348816
350004
  }
348817
350005
  async ensureConfigFile() {
348818
350006
  await ensureConfigFile(this.configPath);
348819
- if (this.usesDefaultConfigPath && authHomeForProfile(this.homeDir) !== this.homeDir) await this.auth.restoreMissingProfileModel();
350007
+ if (this.restoreProfileModel) await this.auth.restoreMissingProfileModel();
348820
350008
  }
348821
350009
  async close() {
348822
350010
  try {