blun-king-cli 9.1.614 → 9.1.615

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:57a7160081cd6a2ce9e3535b31b87f9410c0dafd58da9629b68cb0042a8e57e5
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(),
@@ -10318,7 +10378,8 @@ function tokenToWire(token) {
10318
10378
  expires_at: token.expiresAt,
10319
10379
  scope: token.scope,
10320
10380
  token_type: token.tokenType,
10321
- expires_in: token.expiresIn
10381
+ expires_in: token.expiresIn,
10382
+ ...token.memoryWorkspaceId === void 0 ? {} : { memory_workspace_id: token.memoryWorkspaceId }
10322
10383
  };
10323
10384
  }
10324
10385
  function tokenFromWire(wire) {
@@ -10328,7 +10389,8 @@ function tokenFromWire(wire) {
10328
10389
  expiresAt: typeof wire.expires_at === "number" ? wire.expires_at : 0,
10329
10390
  scope: wire.scope ?? "",
10330
10391
  tokenType: wire.token_type ?? "",
10331
- expiresIn: typeof wire.expires_in === "number" ? wire.expires_in : 0
10392
+ expiresIn: typeof wire.expires_in === "number" ? wire.expires_in : 0,
10393
+ ...typeof wire.memory_workspace_id === "string" && wire.memory_workspace_id.length > 0 ? { memoryWorkspaceId: wire.memory_workspace_id } : {}
10332
10394
  };
10333
10395
  }
10334
10396
  //#endregion
@@ -12719,6 +12781,27 @@ var OAuthManager = class {
12719
12781
  const state = await this.loadState();
12720
12782
  return state.kind === "valid" ? state.token.accessToken : void 0;
12721
12783
  }
12784
+ async getCachedMemoryWorkspaceId() {
12785
+ const state = await this.loadState();
12786
+ return state.kind === "valid" && state.token.expiresAt > this.now() ? state.token.memoryWorkspaceId : void 0;
12787
+ }
12788
+ async cacheMemoryWorkspaceId(workspaceId) {
12789
+ if (workspaceId.length === 0) return false;
12790
+ const release = await this.acquireRefreshLock();
12791
+ try {
12792
+ const state = await this.loadState();
12793
+ if (state.kind !== "valid" || state.token.expiresAt <= this.now()) return false;
12794
+ if (state.token.memoryWorkspaceId === workspaceId) return true;
12795
+ if (state.token.memoryWorkspaceId !== void 0) return false;
12796
+ await this.storage.save(this.config.name, {
12797
+ ...state.token,
12798
+ memoryWorkspaceId: workspaceId
12799
+ });
12800
+ return true;
12801
+ } finally {
12802
+ await release();
12803
+ }
12804
+ }
12722
12805
  async logout() {
12723
12806
  await this.inFlightRefresh?.promise.catch(() => void 0);
12724
12807
  const release = await this.acquireRefreshLock();
@@ -12786,7 +12869,10 @@ var OAuthManager = class {
12786
12869
  if (activeToken.refreshToken.length === 0) throw new OAuthUnauthorizedError(`Token for "${this.config.name}" has no refresh_token; re-login required.`);
12787
12870
  try {
12788
12871
  const refreshed = await this.refreshImpl(this.config, activeToken.refreshToken);
12789
- await this.storage.save(this.config.name, refreshed);
12872
+ await this.storage.save(this.config.name, {
12873
+ ...refreshed,
12874
+ memoryWorkspaceId: activeToken.memoryWorkspaceId
12875
+ });
12790
12876
  this.notifyRefresh({ success: true });
12791
12877
  return refreshed.accessToken;
12792
12878
  } catch (error) {
@@ -14163,6 +14249,16 @@ var BlunOAuthToolkit = class {
14163
14249
  hasToken: await this.managerFor(name, oauthKey, oauthHost).hasToken()
14164
14250
  }] };
14165
14251
  }
14252
+ async getCachedMemoryWorkspaceId(oauthRef) {
14253
+ const oauthHost = this.oauthHostFor(oauthRef);
14254
+ const oauthKey = oauthRef?.key ?? this.defaultOAuthKey(void 0, oauthHost);
14255
+ return this.managerFor(BLUN_PROVIDER_NAME$1, oauthKey, oauthHost).getCachedMemoryWorkspaceId();
14256
+ }
14257
+ async cacheMemoryWorkspaceId(workspaceId, oauthRef) {
14258
+ const oauthHost = this.oauthHostFor(oauthRef);
14259
+ const oauthKey = oauthRef?.key ?? this.defaultOAuthKey(void 0, oauthHost);
14260
+ return this.managerFor(BLUN_PROVIDER_NAME$1, oauthKey, oauthHost).cacheMemoryWorkspaceId(workspaceId);
14261
+ }
14166
14262
  async login(providerName, options = {}) {
14167
14263
  const name = providerName ?? "managed:blun";
14168
14264
  const oauthHost = this.oauthHostFor(options.oauthRef, options.oauthHost);
@@ -16230,6 +16326,8 @@ function providerToToml(provider, rawProvider) {
16230
16326
  for (const key of [
16231
16327
  "type",
16232
16328
  "apiKey",
16329
+ "memoryWorkspaceId",
16330
+ "memoryApiKeyHash",
16233
16331
  "baseUrl",
16234
16332
  "defaultModel",
16235
16333
  "oauth",
@@ -21678,6 +21776,31 @@ var InstructionText = class InstructionText {
21678
21776
  get content() {
21679
21777
  return this.pieces.map((piece) => piece.text).join("");
21680
21778
  }
21779
+ prefixUtf8(maxBytes) {
21780
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) throw new RangeError("Invalid instruction byte budget");
21781
+ const kept = [];
21782
+ let remaining = maxBytes;
21783
+ let totalBytes = 0;
21784
+ let readBytes = 0;
21785
+ for (const piece of this.pieces) {
21786
+ const encoded = Buffer.from(piece.text, "utf8");
21787
+ totalBytes += encoded.length;
21788
+ if (remaining <= 0) continue;
21789
+ const content = encoded.length <= remaining ? piece.text : new StringDecoder("utf8").write(encoded.subarray(0, remaining));
21790
+ const bytes = Buffer.byteLength(content, "utf8");
21791
+ if (content.length > 0) kept.push({
21792
+ ...piece,
21793
+ text: content
21794
+ });
21795
+ readBytes += bytes;
21796
+ remaining -= bytes;
21797
+ if (bytes < encoded.length) remaining = 0;
21798
+ }
21799
+ return {
21800
+ text: new InstructionText(kept, new Map(this.sources)),
21801
+ omittedBytes: totalBytes - readBytes
21802
+ };
21803
+ }
21681
21804
  replaceFirst(needle, replacement) {
21682
21805
  const start = this.content.indexOf(needle);
21683
21806
  if (start >= 0) this.replaceRange(start, start + needle.length, replacement);
@@ -21735,6 +21858,64 @@ const IMPORT_MAX_DEPTH = 4;
21735
21858
  const IMPORT_LINE_RE = /^@import\s+(.+)$/gm;
21736
21859
  const S_IFMT$4 = 61440;
21737
21860
  const S_IFREG$2 = 32768;
21861
+ function fitAgentsMdContext(context, maxFixedRequestBytes, measureFixedRequestBytes) {
21862
+ if (!Number.isSafeInteger(maxFixedRequestBytes) || maxFixedRequestBytes <= 0) throw new RangeError("Invalid fixed request byte budget");
21863
+ const originalBytes = measureFixedRequestBytes(context);
21864
+ if (originalBytes <= maxFixedRequestBytes) return {
21865
+ context,
21866
+ fixedRequestBytes: originalBytes,
21867
+ omittedBytes: 0,
21868
+ fits: true
21869
+ };
21870
+ const source = context.agentsMdText;
21871
+ if (source === void 0) return {
21872
+ context,
21873
+ fixedRequestBytes: originalBytes,
21874
+ omittedBytes: 0,
21875
+ fits: false
21876
+ };
21877
+ const totalBytes = source.measure().totalBytes;
21878
+ const candidateAt = (maxBytes) => {
21879
+ const { text } = source.prefixUtf8(maxBytes);
21880
+ return {
21881
+ ...context,
21882
+ agentsMd: text.content,
21883
+ agentsMdSources: text.measure(),
21884
+ agentsMdText: text
21885
+ };
21886
+ };
21887
+ let low = 0;
21888
+ let high = totalBytes;
21889
+ let fitted = candidateAt(0);
21890
+ let fittedBytes = measureFixedRequestBytes(fitted);
21891
+ if (fittedBytes > maxFixedRequestBytes) {
21892
+ const omittedBytes = context.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? totalBytes;
21893
+ return {
21894
+ context: fitted,
21895
+ fixedRequestBytes: fittedBytes,
21896
+ omittedBytes,
21897
+ fits: false
21898
+ };
21899
+ }
21900
+ while (low < high) {
21901
+ const middle = Math.ceil((low + high) / 2);
21902
+ const candidate = candidateAt(middle);
21903
+ const bytes = measureFixedRequestBytes(candidate);
21904
+ if (bytes <= maxFixedRequestBytes) {
21905
+ low = middle;
21906
+ fitted = candidate;
21907
+ fittedBytes = bytes;
21908
+ } else high = middle - 1;
21909
+ }
21910
+ const omittedBytes = context.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? totalBytes;
21911
+ const retainedBytes = fitted.agentsMdSources?.files.reduce((sum, file) => sum + file.bytes, 0) ?? 0;
21912
+ return {
21913
+ context: fitted,
21914
+ fixedRequestBytes: fittedBytes,
21915
+ omittedBytes: omittedBytes - retainedBytes,
21916
+ fits: true
21917
+ };
21918
+ }
21738
21919
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21739
21920
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
21740
21921
  const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
@@ -21747,7 +21928,8 @@ async function prepareSystemPromptContext(kaos, brandHome, options) {
21747
21928
  agentsMd: agentsMdResult.content,
21748
21929
  additionalDirsInfo,
21749
21930
  agentsMdWarning: agentsMdResult.warning,
21750
- agentsMdSources: agentsMdResult.sources
21931
+ agentsMdSources: agentsMdResult.sources,
21932
+ agentsMdText: agentsMdResult.text
21751
21933
  };
21752
21934
  }
21753
21935
  async function loadAgentsMd(kaos, brandHome) {
@@ -21757,10 +21939,15 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21757
21939
  const discovered = [];
21758
21940
  const emptySources = /* @__PURE__ */ new Map();
21759
21941
  const seen = /* @__PURE__ */ new Set();
21942
+ const notLoadedCandidates = /* @__PURE__ */ new Set();
21760
21943
  const collect = async (path) => {
21761
21944
  const file = await readAgentFile(kaos, path);
21762
- if (file === void 0) return false;
21945
+ if (file === void 0) {
21946
+ notLoadedCandidates.add(kaos.normpath(path));
21947
+ return false;
21948
+ }
21763
21949
  const key = kaos.normpath(file.path);
21950
+ notLoadedCandidates.delete(key);
21764
21951
  if (file.content.length === 0) {
21765
21952
  emptySources.set(key, InstructionText.file(file.path, "", file.sourceTextBytes));
21766
21953
  return false;
@@ -21796,7 +21983,11 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21796
21983
  return {
21797
21984
  content,
21798
21985
  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()
21986
+ text: rendered,
21987
+ sources: {
21988
+ ...rendered.measure(),
21989
+ ...notLoadedCandidates.size === 0 ? {} : { notLoadedCandidates: [...notLoadedCandidates] }
21990
+ }
21800
21991
  };
21801
21992
  }
21802
21993
  async function loadAdditionalDirsInfo(kaos, additionalDirs) {
@@ -28778,17 +28969,20 @@ function createSystemPromptRenderer(merged) {
28778
28969
  const counts = renderPromptContributions(merged.systemPromptTemplate, vars, rendered, true) ?? renderPromptContributions(merged.systemPromptTemplate, vars, rendered);
28779
28970
  const sources = context.agentsMdSources;
28780
28971
  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;
28972
+ 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;
28973
+ const notLoadedCandidates = sourceMatches && sources.notLoadedCandidates !== void 0 ? { notLoadedCandidates: [...sources.notLoadedCandidates] } : {};
28782
28974
  const instructionSources = copies === 0 ? {
28783
28975
  version: 1,
28784
28976
  totalBytes: 0,
28785
28977
  generatedBytes: 0,
28786
- files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : []
28978
+ files: sourceMatches ? sources.files.map((file) => copyInstructionFileBytes(file, 0)) : [],
28979
+ ...notLoadedCandidates
28787
28980
  } : !sourceMatches ? void 0 : {
28788
28981
  version: 1,
28789
28982
  totalBytes: sources.totalBytes * copies,
28790
28983
  generatedBytes: sources.generatedBytes * copies,
28791
- files: sources.files.map((file) => copyInstructionFileBytes(file, copies))
28984
+ files: sources.files.map((file) => copyInstructionFileBytes(file, copies)),
28985
+ ...notLoadedCandidates
28792
28986
  };
28793
28987
  const skillListingSources = counts === void 0 ? void 0 : scaleSkillListingSources(skillSources, vars["BLUN_SKILLS"] ?? "", counts.skillListingBytes);
28794
28988
  context.onContributions(counts === void 0 ? void 0 : {
@@ -180665,6 +180859,10 @@ var GoalMode = class {
180665
180859
  restoreUpdate(record) {
180666
180860
  const state = this.state;
180667
180861
  if (state === void 0) return;
180862
+ if (record.objective !== void 0) {
180863
+ state.objective = record.objective;
180864
+ this.lifecycleRevision += 1;
180865
+ }
180668
180866
  const status = record.status;
180669
180867
  if (status !== void 0) {
180670
180868
  this.lifecycleRevision += 1;
@@ -180679,7 +180877,17 @@ var GoalMode = class {
180679
180877
  state.wallClockResumedAt = void 0;
180680
180878
  }
180681
180879
  if (record.budgetLimits !== void 0) state.budgetLimits = record.budgetLimits;
180682
- if (status === void 0) return;
180880
+ if (status === void 0) {
180881
+ if (record.objective !== void 0) this.agent.replayBuilder.push({
180882
+ type: "goal_updated",
180883
+ snapshot: this.toSnapshot(state),
180884
+ change: {
180885
+ kind: "edited",
180886
+ actor: record.actor
180887
+ }
180888
+ });
180889
+ return;
180890
+ }
180683
180891
  this.agent.replayBuilder.push({
180684
180892
  type: "goal_updated",
180685
180893
  snapshot: this.toSnapshot(state),
@@ -180723,11 +180931,11 @@ var GoalMode = class {
180723
180931
  if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
180724
180932
  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
180933
  const verificationScope = normalizeVerificationScope(input.verificationScope);
180934
+ const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180726
180935
  if (this.state !== void 0) {
180727
180936
  if (input.replace !== true) throw new BlunError(ErrorCodes.GOAL_ALREADY_EXISTS, "A goal already exists; use replace to start a new one");
180728
180937
  this.clearInternal("system");
180729
180938
  }
180730
- const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180731
180939
  const state = {
180732
180940
  goalId: randomUUID(),
180733
180941
  objective,
@@ -180751,6 +180959,35 @@ var GoalMode = class {
180751
180959
  this.trackGoalCreated(actor, input.replace === true);
180752
180960
  return this.toSnapshot(state);
180753
180961
  }
180962
+ async completeGoal() {
180963
+ const state = this.requireState();
180964
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot complete a goal in status "${state.status}"`);
180965
+ return this.markComplete({}, "user");
180966
+ }
180967
+ async blockGoal(input = {}) {
180968
+ const state = this.requireState();
180969
+ if (state.status === "blocked") return this.toSnapshot(state);
180970
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot block a goal in status "${state.status}"`);
180971
+ return await this.markBlocked(input, "user");
180972
+ }
180973
+ async editGoal(input, actor = "user") {
180974
+ const state = this.requireState();
180975
+ const objective = input.objective.trim();
180976
+ if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
180977
+ if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH} characters`);
180978
+ if (objective === state.objective) return this.toSnapshot(state);
180979
+ state.objective = objective;
180980
+ this.lifecycleRevision += 1;
180981
+ this.appendGoalUpdate({
180982
+ objective,
180983
+ actor
180984
+ });
180985
+ this.persistState(state, { change: {
180986
+ kind: "edited",
180987
+ actor
180988
+ } });
180989
+ return this.toSnapshot(state);
180990
+ }
180754
180991
  async pauseGoal(input = {}, actor = "user") {
180755
180992
  const state = this.requireState();
180756
180993
  if (state.status === "paused") return this.toSnapshot(state);
@@ -181520,6 +181757,613 @@ var ProviderRetryWait = class {
181520
181757
  }
181521
181758
  }
181522
181759
  };
181760
+ const IMAGE_MIME_BY_SUFFIX = Object.freeze({
181761
+ ".png": "image/png",
181762
+ ".jpg": "image/jpeg",
181763
+ ".jpeg": "image/jpeg",
181764
+ ".gif": "image/gif",
181765
+ ".bmp": "image/bmp",
181766
+ ".tif": "image/tiff",
181767
+ ".tiff": "image/tiff",
181768
+ ".webp": "image/webp",
181769
+ ".ico": "image/x-icon",
181770
+ ".heic": "image/heic",
181771
+ ".heif": "image/heif",
181772
+ ".avif": "image/avif",
181773
+ ".svgz": "image/svg+xml"
181774
+ });
181775
+ const VIDEO_MIME_BY_SUFFIX = Object.freeze({
181776
+ ".mp4": "video/mp4",
181777
+ ".mpg": "video/mpeg",
181778
+ ".mpeg": "video/mpeg",
181779
+ ".mkv": "video/x-matroska",
181780
+ ".avi": "video/x-msvideo",
181781
+ ".mov": "video/quicktime",
181782
+ ".ogv": "video/ogg",
181783
+ ".wmv": "video/x-ms-wmv",
181784
+ ".webm": "video/webm",
181785
+ ".m4v": "video/x-m4v",
181786
+ ".flv": "video/x-flv",
181787
+ ".3gp": "video/3gpp",
181788
+ ".3g2": "video/3gpp2"
181789
+ });
181790
+ const TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
181791
+ const NON_TEXT_SUFFIXES = new Set([
181792
+ ".icns",
181793
+ ".psd",
181794
+ ".ai",
181795
+ ".eps",
181796
+ ".pdf",
181797
+ ".doc",
181798
+ ".docx",
181799
+ ".dot",
181800
+ ".dotx",
181801
+ ".rtf",
181802
+ ".odt",
181803
+ ".xls",
181804
+ ".xlsx",
181805
+ ".xlsm",
181806
+ ".xlt",
181807
+ ".xltx",
181808
+ ".xltm",
181809
+ ".ods",
181810
+ ".ppt",
181811
+ ".pptx",
181812
+ ".pptm",
181813
+ ".pps",
181814
+ ".ppsx",
181815
+ ".odp",
181816
+ ".pages",
181817
+ ".numbers",
181818
+ ".key",
181819
+ ".zip",
181820
+ ".rar",
181821
+ ".7z",
181822
+ ".tar",
181823
+ ".gz",
181824
+ ".tgz",
181825
+ ".bz2",
181826
+ ".xz",
181827
+ ".zst",
181828
+ ".lz",
181829
+ ".lz4",
181830
+ ".br",
181831
+ ".cab",
181832
+ ".ar",
181833
+ ".deb",
181834
+ ".rpm",
181835
+ ".mp3",
181836
+ ".wav",
181837
+ ".flac",
181838
+ ".ogg",
181839
+ ".oga",
181840
+ ".opus",
181841
+ ".aac",
181842
+ ".m4a",
181843
+ ".wma",
181844
+ ".ttf",
181845
+ ".otf",
181846
+ ".woff",
181847
+ ".woff2",
181848
+ ".exe",
181849
+ ".dll",
181850
+ ".so",
181851
+ ".dylib",
181852
+ ".bin",
181853
+ ".apk",
181854
+ ".ipa",
181855
+ ".jar",
181856
+ ".class",
181857
+ ".pyc",
181858
+ ".pyo",
181859
+ ".wasm",
181860
+ ".dmg",
181861
+ ".iso",
181862
+ ".img",
181863
+ ".sqlite",
181864
+ ".sqlite3",
181865
+ ".db",
181866
+ ".db3"
181867
+ ]);
181868
+ const ASF_HEADER = Buffer.from([
181869
+ 48,
181870
+ 38,
181871
+ 178,
181872
+ 117,
181873
+ 142,
181874
+ 102,
181875
+ 207,
181876
+ 17,
181877
+ 166,
181878
+ 217,
181879
+ 0,
181880
+ 170,
181881
+ 0,
181882
+ 98,
181883
+ 206,
181884
+ 108
181885
+ ]);
181886
+ const FTYP_IMAGE_BRANDS = Object.freeze({
181887
+ avif: "image/avif",
181888
+ avis: "image/avif",
181889
+ heic: "image/heic",
181890
+ heif: "image/heif",
181891
+ heix: "image/heif",
181892
+ hevc: "image/heic",
181893
+ mif1: "image/heif",
181894
+ msf1: "image/heif"
181895
+ });
181896
+ const FTYP_VIDEO_BRANDS = Object.freeze({
181897
+ isom: "video/mp4",
181898
+ iso2: "video/mp4",
181899
+ iso5: "video/mp4",
181900
+ mp41: "video/mp4",
181901
+ mp42: "video/mp4",
181902
+ avc1: "video/mp4",
181903
+ mp4v: "video/mp4",
181904
+ m4v: "video/x-m4v",
181905
+ qt: "video/quicktime",
181906
+ "3gp4": "video/3gpp",
181907
+ "3gp5": "video/3gpp",
181908
+ "3gp6": "video/3gpp",
181909
+ "3gp7": "video/3gpp",
181910
+ "3g2": "video/3gpp2"
181911
+ });
181912
+ function toBuffer(data) {
181913
+ return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
181914
+ }
181915
+ function startsWith(buf, prefix) {
181916
+ const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
181917
+ if (buf.length < needle.length) return false;
181918
+ for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
181919
+ return true;
181920
+ }
181921
+ function sniffFtypBrand(header) {
181922
+ if (header.length < 12) return null;
181923
+ if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
181924
+ return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
181925
+ }
181926
+ function sniffMediaFromMagic(data) {
181927
+ const buf = toBuffer(data);
181928
+ const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
181929
+ if (startsWith(header, [
181930
+ 137,
181931
+ 80,
181932
+ 78,
181933
+ 71,
181934
+ 13,
181935
+ 10,
181936
+ 26,
181937
+ 10
181938
+ ])) return {
181939
+ kind: "image",
181940
+ mimeType: "image/png"
181941
+ };
181942
+ if (startsWith(header, [
181943
+ 255,
181944
+ 216,
181945
+ 255
181946
+ ])) return {
181947
+ kind: "image",
181948
+ mimeType: "image/jpeg"
181949
+ };
181950
+ if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
181951
+ kind: "image",
181952
+ mimeType: "image/gif"
181953
+ };
181954
+ if (startsWith(header, Buffer.from("BM"))) return {
181955
+ kind: "image",
181956
+ mimeType: "image/bmp"
181957
+ };
181958
+ if (startsWith(header, [
181959
+ 73,
181960
+ 73,
181961
+ 42,
181962
+ 0
181963
+ ]) || startsWith(header, [
181964
+ 77,
181965
+ 77,
181966
+ 0,
181967
+ 42
181968
+ ])) return {
181969
+ kind: "image",
181970
+ mimeType: "image/tiff"
181971
+ };
181972
+ if (startsWith(header, [
181973
+ 0,
181974
+ 0,
181975
+ 1,
181976
+ 0
181977
+ ])) return {
181978
+ kind: "image",
181979
+ mimeType: "image/x-icon"
181980
+ };
181981
+ if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
181982
+ const chunk = header.subarray(8, 12).toString("latin1");
181983
+ if (chunk === "WEBP") return {
181984
+ kind: "image",
181985
+ mimeType: "image/webp"
181986
+ };
181987
+ if (chunk === "AVI ") return {
181988
+ kind: "video",
181989
+ mimeType: "video/x-msvideo"
181990
+ };
181991
+ }
181992
+ if (startsWith(header, Buffer.from("FLV"))) return {
181993
+ kind: "video",
181994
+ mimeType: "video/x-flv"
181995
+ };
181996
+ if (startsWith(header, ASF_HEADER)) return {
181997
+ kind: "video",
181998
+ mimeType: "video/x-ms-wmv"
181999
+ };
182000
+ if (startsWith(header, [
182001
+ 26,
182002
+ 69,
182003
+ 223,
182004
+ 163
182005
+ ])) {
182006
+ const lowered = header.toString("latin1").toLowerCase();
182007
+ if (lowered.includes("webm")) return {
182008
+ kind: "video",
182009
+ mimeType: "video/webm"
182010
+ };
182011
+ if (lowered.includes("matroska")) return {
182012
+ kind: "video",
182013
+ mimeType: "video/x-matroska"
182014
+ };
182015
+ }
182016
+ const brand = sniffFtypBrand(header);
182017
+ if (brand !== null && brand !== "") {
182018
+ if (brand in FTYP_IMAGE_BRANDS) return {
182019
+ kind: "image",
182020
+ mimeType: FTYP_IMAGE_BRANDS[brand]
182021
+ };
182022
+ if (brand in FTYP_VIDEO_BRANDS) return {
182023
+ kind: "video",
182024
+ mimeType: FTYP_VIDEO_BRANDS[brand]
182025
+ };
182026
+ }
182027
+ return null;
182028
+ }
182029
+ /**
182030
+ * Best-effort pixel-dimension reader for common raster formats.
182031
+ *
182032
+ * Inspects only the fixed region near the start of the file where each
182033
+ * format records its dimensions (the IHDR/DIB header, the RIFF chunk
182034
+ * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
182035
+ * for formats whose dimensions are not locatable from that region, or
182036
+ * when the supplied buffer is too short to cover it.
182037
+ */
182038
+ function sniffImageDimensions(data) {
182039
+ const buf = toBuffer(data);
182040
+ if (startsWith(buf, [
182041
+ 137,
182042
+ 80,
182043
+ 78,
182044
+ 71,
182045
+ 13,
182046
+ 10,
182047
+ 26,
182048
+ 10
182049
+ ]) && buf.length >= 24) return {
182050
+ width: buf.readUInt32BE(16),
182051
+ height: buf.readUInt32BE(20)
182052
+ };
182053
+ if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
182054
+ width: buf.readUInt16LE(6),
182055
+ height: buf.readUInt16LE(8)
182056
+ };
182057
+ if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
182058
+ width: buf.readInt32LE(18),
182059
+ height: Math.abs(buf.readInt32LE(22))
182060
+ };
182061
+ if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
182062
+ const fourCc = buf.subarray(12, 16).toString("latin1");
182063
+ if (fourCc === "VP8 ") return {
182064
+ width: buf.readUInt16LE(26) & 16383,
182065
+ height: buf.readUInt16LE(28) & 16383
182066
+ };
182067
+ if (fourCc === "VP8L" && buf.length >= 25) {
182068
+ const bits = buf.readUInt32LE(21);
182069
+ return {
182070
+ width: (bits & 16383) + 1,
182071
+ height: (bits >> 14 & 16383) + 1
182072
+ };
182073
+ }
182074
+ if (fourCc === "VP8X") return {
182075
+ width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
182076
+ height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
182077
+ };
182078
+ }
182079
+ if (startsWith(buf, [255, 216])) {
182080
+ let offset = 2;
182081
+ while (offset + 9 < buf.length) {
182082
+ if (buf[offset] !== 255) {
182083
+ offset += 1;
182084
+ continue;
182085
+ }
182086
+ const marker = buf[offset + 1];
182087
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
182088
+ height: buf.readUInt16BE(offset + 5),
182089
+ width: buf.readUInt16BE(offset + 7)
182090
+ };
182091
+ if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
182092
+ offset += 2;
182093
+ continue;
182094
+ }
182095
+ const segmentLength = buf.readUInt16BE(offset + 2);
182096
+ if (segmentLength < 2) break;
182097
+ offset += 2 + segmentLength;
182098
+ }
182099
+ }
182100
+ return null;
182101
+ }
182102
+ function getSuffix(path) {
182103
+ const idx = path.lastIndexOf(".");
182104
+ if (idx === -1) return "";
182105
+ if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
182106
+ return path.slice(idx).toLowerCase();
182107
+ }
182108
+ function detectFileType(path, header, type = "text") {
182109
+ const suffix = getSuffix(path);
182110
+ let mediaHint = null;
182111
+ if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
182112
+ kind: "text",
182113
+ mimeType: TEXT_MIME_BY_SUFFIX[suffix]
182114
+ };
182115
+ else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
182116
+ kind: "image",
182117
+ mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
182118
+ };
182119
+ else if (suffix in VIDEO_MIME_BY_SUFFIX) mediaHint = {
182120
+ kind: "video",
182121
+ mimeType: VIDEO_MIME_BY_SUFFIX[suffix]
182122
+ };
182123
+ if (header !== void 0) {
182124
+ const buf = toBuffer(header);
182125
+ const sniffed = sniffMediaFromMagic(buf);
182126
+ if (sniffed) {
182127
+ if (type === "media") return sniffed;
182128
+ if (mediaHint) {
182129
+ if (sniffed.kind !== mediaHint.kind) return {
182130
+ kind: "unknown",
182131
+ mimeType: ""
182132
+ };
182133
+ return mediaHint;
182134
+ }
182135
+ return sniffed;
182136
+ }
182137
+ if (mediaHint?.kind === "image") return {
182138
+ kind: "unknown",
182139
+ mimeType: ""
182140
+ };
182141
+ if (type === "media" && mediaHint?.kind === "video") return mediaHint;
182142
+ if (buf.includes(0)) return {
182143
+ kind: "unknown",
182144
+ mimeType: ""
182145
+ };
182146
+ }
182147
+ if (mediaHint) return mediaHint;
182148
+ if (NON_TEXT_SUFFIXES.has(suffix)) return {
182149
+ kind: "unknown",
182150
+ mimeType: ""
182151
+ };
182152
+ return {
182153
+ kind: "text",
182154
+ mimeType: "text/plain"
182155
+ };
182156
+ }
182157
+ //#endregion
182158
+ //#region ../../packages/agent-core/src/utils/tokens.ts
182159
+ const messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
182160
+ /** Open assistant messages grow while streaming; completed history stays cached. */
182161
+ function invalidateMessageTokenEstimate(message) {
182162
+ messageTokenEstimateCache.delete(message);
182163
+ }
182164
+ /**
182165
+ * Estimate token count from text using a character-based heuristic.
182166
+ * - ASCII (~4 chars per token)
182167
+ * - CJK and other non-ASCII (~1 char per token)
182168
+ * The estimate is transient — the next LLM call returns the real count
182169
+ * and supersedes this value. Used to keep `tokenCountWithPending`
182170
+ * monotonic between LLM round-trips without paying for a tokenizer.
182171
+ */
182172
+ function estimateTokens(text) {
182173
+ let asciiCount = 0;
182174
+ let nonAsciiCount = 0;
182175
+ for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
182176
+ else nonAsciiCount++;
182177
+ return Math.ceil(asciiCount / 4) + nonAsciiCount;
182178
+ }
182179
+ function estimateTokensForMessages(messages) {
182180
+ let total = 0;
182181
+ for (const message of messages) total += estimateTokensForMessage(message);
182182
+ return total;
182183
+ }
182184
+ /** With stopAfter, the result is a lower bound once it exceeds that budget. */
182185
+ function estimateTokensForTools(tools, stopAfter = Infinity) {
182186
+ let total = 0;
182187
+ for (const tool of tools) {
182188
+ total += estimateTokens(tool.name);
182189
+ total += estimateTokens(tool.description);
182190
+ total += estimateTokens(JSON.stringify(tool.parameters));
182191
+ if (total > stopAfter) return total;
182192
+ }
182193
+ return total;
182194
+ }
182195
+ function estimateTokensForMessage(message) {
182196
+ const cached = messageTokenEstimateCache.get(message);
182197
+ if (cached !== void 0) return cached;
182198
+ let total = estimateTokens(message.role);
182199
+ total += estimateTokensForContentParts(message.content);
182200
+ if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
182201
+ total += estimateTokens(call.name);
182202
+ total += estimateTokens(JSON.stringify(call.arguments));
182203
+ }
182204
+ messageTokenEstimateCache.set(message, total);
182205
+ return total;
182206
+ }
182207
+ function estimateTokensForContentParts(parts) {
182208
+ let total = 0;
182209
+ for (const part of parts) total += estimateTokensForContentPart(part);
182210
+ return total;
182211
+ }
182212
+ /**
182213
+ * Conservative transient fallback for media whose pixel dimensions or duration
182214
+ * are unavailable. Inline raster images use their transmitted dimensions below;
182215
+ * audio, video, remote images, and malformed image data keep this safe floor.
182216
+ * The next successful LLM round-trip replaces the estimate with reported usage.
182217
+ */
182218
+ const MEDIA_TOKEN_ESTIMATE = 2e3;
182219
+ /** Enough header data for common JPEG metadata followed by the first SOF marker. */
182220
+ const MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
182221
+ function imageDimensionsFromDataUrl(url) {
182222
+ const commaIndex = url.indexOf(",");
182223
+ if (commaIndex <= 0) return null;
182224
+ const metadata = url.slice(0, commaIndex).toLowerCase();
182225
+ if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
182226
+ const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
182227
+ const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
182228
+ if (encodedHeader.length === 0) return null;
182229
+ try {
182230
+ const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
182231
+ if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
182232
+ return dimensions;
182233
+ } catch {
182234
+ return null;
182235
+ }
182236
+ }
182237
+ function estimateImageTokensFromDimensions(width, height) {
182238
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
182239
+ const patchSize = BigInt(32);
182240
+ const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
182241
+ return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
182242
+ }
182243
+ function estimateImageTokens(url) {
182244
+ const dimensions = imageDimensionsFromDataUrl(url);
182245
+ return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
182246
+ }
182247
+ function estimateTokensForContentPart(part) {
182248
+ switch (part.type) {
182249
+ case "text": return estimateTokens(part.text);
182250
+ case "think": return estimateTokens(part.think);
182251
+ case "image_url": return estimateImageTokens(part.imageUrl.url);
182252
+ case "audio_url":
182253
+ case "video_url": return MEDIA_TOKEN_ESTIMATE;
182254
+ default: return 0;
182255
+ }
182256
+ }
182257
+ const MAX_TOOL_CONTINUITY_SERIALIZED_BYTES = 4096;
182258
+ function saturatingAdd(left, right) {
182259
+ return left + Math.min(right, Number.MAX_SAFE_INTEGER - left);
182260
+ }
182261
+ function renderUnchecked(value) {
182262
+ return [
182263
+ "",
182264
+ "",
182265
+ "<tool-continuity>",
182266
+ "Observed tool-result flags; may be synthetic, not verified job success or permission. omitted counts excluded observations cumulatively, not jobs.",
182267
+ JSON.stringify(value).replaceAll(/[<>&]/g, (character) => {
182268
+ return `\\u${character.codePointAt(0).toString(16).padStart(4, "0")}`;
182269
+ }),
182270
+ "</tool-continuity>"
182271
+ ].join("\n");
182272
+ }
182273
+ function hasExactKeys(value, expected) {
182274
+ const prototype = Object.getPrototypeOf(value);
182275
+ if (prototype !== Object.prototype && prototype !== null) return false;
182276
+ return Reflect.ownKeys(value).length === expected.length && expected.every((key) => {
182277
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
182278
+ return descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value");
182279
+ });
182280
+ }
182281
+ function isDataArray(value) {
182282
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > 8 || Reflect.ownKeys(value).length !== value.length + 1) return false;
182283
+ for (let index = 0; index < value.length; index++) {
182284
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
182285
+ if (descriptor === void 0 || descriptor.enumerable !== true || !Object.hasOwn(descriptor, "value")) return false;
182286
+ }
182287
+ return true;
182288
+ }
182289
+ /** Validate restored metadata without interpreting any summarizer or tool prose. */
182290
+ function isToolContinuity(value) {
182291
+ try {
182292
+ if (value === null || typeof value !== "object" || !hasExactKeys(value, ["receipts", "omitted"])) return false;
182293
+ const candidate = value;
182294
+ if (!Number.isSafeInteger(candidate["omitted"]) || candidate["omitted"] < 0 || !isDataArray(candidate["receipts"]) || candidate["receipts"].length > 8) return false;
182295
+ 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;
182296
+ return Buffer.byteLength(JSON.stringify(renderUnchecked(value)), "utf8") <= MAX_TOOL_CONTINUITY_SERIALIZED_BYTES;
182297
+ } catch {
182298
+ return false;
182299
+ }
182300
+ }
182301
+ function renderToolContinuity(value) {
182302
+ if (!isToolContinuity(value) || value.receipts.length === 0 && value.omitted === 0) return void 0;
182303
+ return renderUnchecked(value);
182304
+ }
182305
+ /** The caller passes only the compactable prefix, excluding the preserved tool suffix. */
182306
+ function collectToolContinuity(messages) {
182307
+ const observed = /* @__PURE__ */ new Map();
182308
+ let ordinal = 0;
182309
+ let inheritedOmitted = 0;
182310
+ let excluded = 0;
182311
+ const remember = (toolCallId, observedIsError) => {
182312
+ if (toolCallId.length === 0 || Buffer.byteLength(toolCallId, "utf8") > 256) {
182313
+ excluded = saturatingAdd(excluded, 1);
182314
+ return;
182315
+ }
182316
+ const key = JSON.stringify([toolCallId, observedIsError]);
182317
+ observed.set(key, {
182318
+ toolCallId,
182319
+ observedIsError,
182320
+ ordinal: ordinal++
182321
+ });
182322
+ };
182323
+ for (const message of messages) if (message.role === "tool" && typeof message.toolCallId === "string") remember(message.toolCallId, typeof message.isError === "boolean" ? message.isError : null);
182324
+ else if (message.role === "user" && message.origin?.kind === "compaction_summary") {
182325
+ const previous = message.origin.toolContinuity;
182326
+ if (!isToolContinuity(previous)) continue;
182327
+ for (const receipt of previous.receipts.toReversed()) remember(receipt.toolCallId, receipt.observedIsError);
182328
+ inheritedOmitted = Math.max(inheritedOmitted, previous.omitted);
182329
+ }
182330
+ const prioritized = [...observed.values()].toSorted((left, right) => Number(right.observedIsError === true) - Number(left.observedIsError === true) || right.ordinal - left.ordinal);
182331
+ const receipts = prioritized.slice(0, 8).map(({ toolCallId, observedIsError }) => ({
182332
+ toolCallId,
182333
+ observedIsError
182334
+ }));
182335
+ let omitted = saturatingAdd(saturatingAdd(inheritedOmitted, excluded), prioritized.length - receipts.length);
182336
+ while (true) {
182337
+ const result = {
182338
+ receipts: [...receipts],
182339
+ omitted
182340
+ };
182341
+ if (renderToolContinuity(result) !== void 0) return result;
182342
+ if (receipts.length === 0) return void 0;
182343
+ receipts.pop();
182344
+ omitted = saturatingAdd(omitted, 1);
182345
+ }
182346
+ }
182347
+ /** Fit complete tuples and their disclaimer; never start a new failure or retry path. */
182348
+ function fitToolContinuity(value, additionalTokenBudget) {
182349
+ if (value === void 0 || !isToolContinuity(value) || !(additionalTokenBudget > 0)) return void 0;
182350
+ const receipts = [...value.receipts];
182351
+ let omitted = value.omitted;
182352
+ while (true) {
182353
+ const continuity = {
182354
+ receipts: [...receipts],
182355
+ omitted
182356
+ };
182357
+ const text = renderToolContinuity(continuity);
182358
+ if (text !== void 0 && estimateTokens(text) <= additionalTokenBudget) return {
182359
+ continuity,
182360
+ text
182361
+ };
182362
+ if (receipts.length === 0) return void 0;
182363
+ receipts.pop();
182364
+ omitted = saturatingAdd(omitted, 1);
182365
+ }
182366
+ }
181523
182367
  //#endregion
181524
182368
  //#region ../../packages/agent-core/src/agent/records/resume-snapshot.ts
181525
182369
  const integer$1 = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
@@ -181614,6 +182458,7 @@ const stateRecordSchema = union([
181614
182458
  verificationScope: _enum(GOAL_VERIFICATION_SCOPES).optional()
181615
182459
  }),
181616
182460
  record$1("goal.update", {
182461
+ objective: text.min(1).max(4e3).regex(/\S/).optional(),
181617
182462
  status: _enum([
181618
182463
  "active",
181619
182464
  "paused",
@@ -181665,7 +182510,7 @@ const originCase = (kind, shape = {}) => object({
181665
182510
  }).passthrough();
181666
182511
  const origin = union([
181667
182512
  originCase("user"),
181668
- originCase("compaction_summary"),
182513
+ originCase("compaction_summary", { toolContinuity: custom$1(isToolContinuity).optional() }),
181669
182514
  originCase("skill_activation", {
181670
182515
  activationId: text,
181671
182516
  skillName: text,
@@ -182016,6 +182861,7 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182016
182861
  if (Buffer.byteLength(bytes) > MAX_CHECKPOINT_BYTES) return false;
182017
182862
  const target = resumeCheckpointPath(wirePath);
182018
182863
  const temporary = `${target}.tmp-${process.pid}-${randomBytes(8).toString("hex")}`;
182864
+ let renamed = false;
182019
182865
  try {
182020
182866
  const output = await open(temporary, "wx", 384);
182021
182867
  try {
@@ -182025,10 +182871,11 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182025
182871
  await output.close();
182026
182872
  }
182027
182873
  await rename(temporary, target);
182874
+ renamed = true;
182028
182875
  await syncDir(dirname$3(target));
182029
182876
  return true;
182030
182877
  } finally {
182031
- await rm(temporary, { force: true }).catch(() => {});
182878
+ if (!renamed) await rm(temporary, { force: true }).catch(() => {});
182032
182879
  }
182033
182880
  }
182034
182881
  //#endregion
@@ -185365,501 +186212,6 @@ var TodoListTool = class {
185365
186212
  })));
185366
186213
  }
185367
186214
  };
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
186215
  //#endregion
185864
186216
  //#region ../../node_modules/.pnpm/bmp-ts@1.0.9/node_modules/bmp-ts/dist/esm/header-types.js
185865
186217
  var HeaderTypes, header_types_default;
@@ -230115,7 +230467,7 @@ function buildCompactionSummaryText(summary) {
230115
230467
  const MAX_SESSION_COMPACTION_FAILURES = 2;
230116
230468
  const DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS = 128 * 1024;
230117
230469
  const COMPACTION_SUMMARY_RESERVE_RATIO = .1;
230118
- const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 75e4;
230470
+ const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 48e4;
230119
230471
  const MAX_HIERARCHICAL_COMPACTION_PASSES = 64;
230120
230472
  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
230473
  var CompactionTruncatedError = class extends Error {
@@ -230235,16 +230587,19 @@ var FullCompaction = class {
230235
230587
  const projected = context.additionalMessages === void 0 ? messages : [...messages, ...context.additionalMessages];
230236
230588
  const historyBudget = this.requestHistoryByteBudget(projected);
230237
230589
  const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230590
+ const requestBudget = this.requestBodyByteBudget(projected, context);
230591
+ const requestThresholdReached = requestBudget !== void 0 && requestBudget.usedRequestBytesEstimate >= requestBudget.thresholdRequestBytes;
230238
230592
  if (this.sessionCompactionFailures >= MAX_SESSION_COMPACTION_FAILURES) reason = "session_stopped";
230239
230593
  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";
230594
+ else if (thresholdTokens === void 0 && historyBudget === void 0 && requestBudget === void 0) reason = "no_budget";
230595
+ else if (!historyThresholdReached && !requestThresholdReached && (thresholdTokens === void 0 || !Number.isFinite(usedTokens) || usedTokens < thresholdTokens)) reason = "below_threshold";
230242
230596
  else if (!this.hasShrinkableHistory(projected)) reason = "nothing_to_compact";
230243
230597
  return {
230244
230598
  eligible: reason === "eligible",
230245
230599
  usedTokens,
230246
230600
  thresholdTokens,
230247
230601
  ...historyBudget,
230602
+ ...requestBudget,
230248
230603
  reason
230249
230604
  };
230250
230605
  }
@@ -230266,6 +230621,19 @@ var FullCompaction = class {
230266
230621
  thresholdHistoryBytes: limit
230267
230622
  };
230268
230623
  }
230624
+ requestBodyByteBudget(messages, context) {
230625
+ const limit = this.agent.blunConfig?.loopControl?.maxRequestHistoryBytes;
230626
+ if (!this.agent.config.hasProvider || this.strategy.compactionBudgetTokens === void 0 || limit !== void 0 && (!Number.isSafeInteger(limit) || limit <= 0)) return void 0;
230627
+ const model = this.agent.activeResponderModel ?? this.agent.config.modelAlias;
230628
+ if (model === void 0 || this.agent.config.runtimeModel(model).provider.name !== "blun") return void 0;
230629
+ const systemPrompt = context.systemPrompt ?? this.agent.effectiveSystemPrompt;
230630
+ const tools = context.tools ?? this.agent.tools.loopTools;
230631
+ if (estimateBlunSerializedBodyBytes(systemPrompt, [], []) + 4096 >= BLUN_COMPACTION_REQUEST_BUDGET_BYTES) return void 0;
230632
+ return {
230633
+ usedRequestBytesEstimate: estimateBlunSerializedBodyBytes(systemPrompt, tools, messages) + 4096,
230634
+ thresholdRequestBytes: BLUN_COMPACTION_REQUEST_BUDGET_BYTES
230635
+ };
230636
+ }
230269
230637
  requestProactiveCompaction(context = {}) {
230270
230638
  const status = this.proactiveCompactionStatus(context);
230271
230639
  if (status.eligible && !this.proactiveCompactionRequested) {
@@ -230274,7 +230642,9 @@ var FullCompaction = class {
230274
230642
  used_tokens: status.usedTokens,
230275
230643
  threshold_tokens: status.thresholdTokens,
230276
230644
  used_history_bytes: status.usedHistoryBytes,
230277
- threshold_history_bytes: status.thresholdHistoryBytes
230645
+ threshold_history_bytes: status.thresholdHistoryBytes,
230646
+ used_request_bytes_estimate: status.usedRequestBytesEstimate,
230647
+ threshold_request_bytes: status.thresholdRequestBytes
230278
230648
  });
230279
230649
  }
230280
230650
  return status;
@@ -230403,7 +230773,9 @@ var FullCompaction = class {
230403
230773
  const requestTokens = this.estimateCurrentRequestTokens(requestContext);
230404
230774
  const budget = this.getCompactionBudgetTokens();
230405
230775
  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);
230776
+ const historyThresholdReached = historyBudget !== void 0 && historyBudget.usedHistoryBytes >= historyBudget.thresholdHistoryBytes;
230777
+ const requestBudget = this.requestBodyByteBudget(projectedMessages, requestContext);
230778
+ 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
230779
  if (this.checkAutoCompaction(true, requestTokens, projectedMessages, proactive) && (proactive || this.strategy.shouldBlock(requestTokens))) await this.block(signal);
230408
230780
  signal.throwIfAborted();
230409
230781
  const compacted = this.agent.context.history !== historyBefore;
@@ -230613,7 +230985,7 @@ var FullCompaction = class {
230613
230985
  dropOrphanResults: true
230614
230986
  }), createUserMessage(requestInstruction)], capability));
230615
230987
  };
230616
- const estimateRequestBytes = (messages) => Buffer.byteLength(JSON.stringify(messages), "utf8") + Buffer.byteLength(this.agent.config.systemPrompt, "utf8");
230988
+ const estimateRequestBytes = (messages) => estimateBlunSerializedBodyBytes(this.agent.effectiveSystemPrompt, compactionTools, messages) + 4096;
230617
230989
  while (true) {
230618
230990
  signal.throwIfAborted();
230619
230991
  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: {
@@ -234193,8 +234565,9 @@ function makeSyntheticToolResult(toolCallId) {
234193
234565
  }
234194
234566
  function mergeAdjacentUserMessages(history, onAnomaly) {
234195
234567
  const out = [];
234196
- for (const source of history) {
234197
- const message = prepareMessageForProjection(source, onAnomaly);
234568
+ const latestUserIndex = history.findLastIndex((message) => message.role === "user" && message.partial !== true && (message.origin === void 0 || message.origin.kind === "user"));
234569
+ for (const [index, source] of history.entries()) {
234570
+ const message = prepareMessageForProjection(source, onAnomaly, index < latestUserIndex);
234198
234571
  if (message === null) continue;
234199
234572
  const previous = out.at(-1);
234200
234573
  if (canMergeUserMessage(message) && previous !== void 0 && canMergeUserMessage(previous)) {
@@ -234205,7 +234578,7 @@ function mergeAdjacentUserMessages(history, onAnomaly) {
234205
234578
  }
234206
234579
  return out.map(stripContextMetadata);
234207
234580
  }
234208
- function prepareMessageForProjection(message, onAnomaly) {
234581
+ function prepareMessageForProjection(message, onAnomaly, historical = false) {
234209
234582
  if (message.partial === true) return null;
234210
234583
  let content;
234211
234584
  for (const [index, part] of message.content.entries()) {
@@ -234217,6 +234590,19 @@ function prepareMessageForProjection(message, onAnomaly) {
234217
234590
  });
234218
234591
  continue;
234219
234592
  }
234593
+ if (historical && message.role === "user" && part.type === "text") {
234594
+ const text = withoutHistoricalTelegramGroupReminder(part.text);
234595
+ if (text !== part.text) {
234596
+ content ??= message.content.slice(0, index);
234597
+ const shortened = {
234598
+ ...part,
234599
+ text
234600
+ };
234601
+ copyRequestTextOrigin(part, shortened, message.origin);
234602
+ content.push(shortened);
234603
+ continue;
234604
+ }
234605
+ }
234220
234606
  content?.push(part);
234221
234607
  }
234222
234608
  const next = content === void 0 ? message : {
@@ -234226,6 +234612,14 @@ function prepareMessageForProjection(message, onAnomaly) {
234226
234612
  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
234613
  return next.content.length === 0 && next.toolCalls.length === 0 ? null : next;
234228
234614
  }
234615
+ function withoutHistoricalTelegramGroupReminder(text) {
234616
+ if (!text.startsWith("<channel source=\"telegram\"") && !text.startsWith("You are connected to a Telegram channel.")) return text;
234617
+ const start = text.lastIndexOf(`</channel>\n[GRUPPENANTWORT:`);
234618
+ if (start < 0) return text;
234619
+ const reminder = text.slice(start + 10 + 1);
234620
+ if (!/^\[GRUPPENANTWORT:[^\r\n]*\]$/.test(reminder)) return text;
234621
+ return text.slice(0, start + 10);
234622
+ }
234229
234623
  function canMergeUserMessage(message) {
234230
234624
  return message.role === "user" && message.origin?.kind === "user";
234231
234625
  }
@@ -234290,6 +234684,8 @@ function checkedRemovalIndexes(indexes, length) {
234290
234684
  }
234291
234685
  var ContextMemory = class {
234292
234686
  agent;
234687
+ mutationGeneration = randomUUID();
234688
+ mutationRevision = 0;
234293
234689
  _history = [];
234294
234690
  _tokenCount = 0;
234295
234691
  tokenCountCoveredMessageCount = 0;
@@ -234301,6 +234697,15 @@ var ContextMemory = class {
234301
234697
  constructor(agent) {
234302
234698
  this.agent = agent;
234303
234699
  }
234700
+ publishMutation(reason) {
234701
+ if (this.agent.records.restoring) return;
234702
+ this.agent.emitEvent({
234703
+ type: "context.changed",
234704
+ generation: this.mutationGeneration,
234705
+ revision: ++this.mutationRevision,
234706
+ reason
234707
+ });
234708
+ }
234304
234709
  get lastAssistantAt() {
234305
234710
  return this._lastAssistantAt;
234306
234711
  }
@@ -234454,6 +234859,7 @@ var ContextMemory = class {
234454
234859
  return removedMessages.size;
234455
234860
  }
234456
234861
  clear() {
234862
+ const changed = this._history.length > 0;
234457
234863
  this.agent.records.logRecord({ type: "context.clear" });
234458
234864
  this._history = [];
234459
234865
  this._tokenCount = 0;
@@ -234465,6 +234871,7 @@ var ContextMemory = class {
234465
234871
  this.agent.microCompaction.reset();
234466
234872
  this.agent.injection.onContextClear();
234467
234873
  this.agent.emitStatusUpdated();
234874
+ if (changed) this.publishMutation("clear");
234468
234875
  }
234469
234876
  undo(count) {
234470
234877
  if (count <= 0) return;
@@ -234502,6 +234909,7 @@ var ContextMemory = class {
234502
234909
  this.deferredMessages = [];
234503
234910
  this.agent.microCompaction.reset(this._history.length);
234504
234911
  this.agent.emitStatusUpdated();
234912
+ if (removedMessages.size > 0) this.publishMutation("undo");
234505
234913
  if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
234506
234914
  reason: "undo_limit",
234507
234915
  requestedCount: count,
@@ -234510,6 +234918,7 @@ var ContextMemory = class {
234510
234918
  } });
234511
234919
  }
234512
234920
  applyCompaction(input, options = {}) {
234921
+ if (this.agent.records.restoring !== null && input.toolContinuity !== void 0 && !isToolContinuity(input.toolContinuity)) throw new Error("Invalid compaction tool continuity");
234513
234922
  let preservedToolTailCount = input.preservedToolTailCount;
234514
234923
  if (this.agent.records.restoring === null && this.hasOpenToolExchange()) {
234515
234924
  const start = this._history.findIndex((message) => message.role === "assistant" && message.toolCalls.some((call) => this.pendingToolResultIds.has(call.id)));
@@ -234546,25 +234955,75 @@ var ContextMemory = class {
234546
234955
  elisionMessage,
234547
234956
  ...selection.tail
234548
234957
  ];
234549
- const contextSummary = input.contextSummary ?? input.summary;
234550
- const summaryMessage = {
234958
+ let contextSummary = input.contextSummary ?? input.summary;
234959
+ let toolContinuity = this.agent.records.restoring === null ? void 0 : input.toolContinuity;
234960
+ let summaryMessage = {
234551
234961
  role: "user",
234552
234962
  content: [{
234553
234963
  type: "text",
234554
234964
  text: contextSummary
234555
234965
  }],
234556
234966
  toolCalls: [],
234557
- origin: { kind: "compaction_summary" }
234967
+ origin: {
234968
+ kind: "compaction_summary",
234969
+ ...toolContinuity === void 0 ? {} : { toolContinuity }
234970
+ }
234558
234971
  };
234559
- const replacement = [
234972
+ let replacement = [
234560
234973
  ...keptMessages,
234561
234974
  summaryMessage,
234562
234975
  ...preservedToolTail
234563
234976
  ];
234977
+ const previousHistoryTokens = options.requireReduction && this.agent.records.restoring === null ? estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true })) : void 0;
234978
+ if (this.agent.records.restoring === null) {
234979
+ toolContinuity = void 0;
234980
+ const observations = collectToolContinuity(this._history.slice(0, tailStart));
234981
+ if (observations !== void 0) {
234982
+ const baseProjected = project(replacement, { dropOrphanResults: true });
234983
+ const baseTokens = estimateTokensForMessages(baseProjected);
234984
+ let extraTokens = previousHistoryTokens === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, previousHistoryTokens - baseTokens - 1);
234985
+ if (this.agent.config.hasProvider) {
234986
+ const window = this.agent.fullCompaction.getEffectiveMaxContextTokens();
234987
+ const budget = this.agent.fullCompaction.getCompactionBudgetTokens();
234988
+ const requestLimit = Math.min(window > 0 ? window - 1 : Number.POSITIVE_INFINITY, budget ?? Number.POSITIVE_INFINITY);
234989
+ extraTokens = Math.min(extraTokens, Math.max(0, requestLimit - this.agent.fullCompaction.estimateProjectedRequestTokens({ messages: baseProjected }) - 1));
234990
+ }
234991
+ let fitted = fitToolContinuity(observations, extraTokens);
234992
+ while (fitted !== void 0) {
234993
+ const candidateSummary = {
234994
+ ...summaryMessage,
234995
+ content: [{
234996
+ type: "text",
234997
+ text: contextSummary + fitted.text
234998
+ }],
234999
+ origin: {
235000
+ kind: "compaction_summary",
235001
+ toolContinuity: fitted.continuity
235002
+ }
235003
+ };
235004
+ const candidate = [
235005
+ ...keptMessages,
235006
+ candidateSummary,
235007
+ ...preservedToolTail
235008
+ ];
235009
+ const projected = project(candidate, { dropOrphanResults: true });
235010
+ const status = this.agent.fullCompaction.proactiveCompactionStatus({ messages: projected });
235011
+ const reducing = previousHistoryTokens === void 0 || estimateTokensForMessages(projected) < previousHistoryTokens;
235012
+ 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);
235013
+ if (reducing && bytesFit) {
235014
+ toolContinuity = fitted.continuity;
235015
+ contextSummary += fitted.text;
235016
+ summaryMessage = candidateSummary;
235017
+ replacement = candidate;
235018
+ break;
235019
+ }
235020
+ fitted = fitToolContinuity(observations, estimateTokens(fitted.text) - 1);
235021
+ }
235022
+ }
235023
+ }
234564
235024
  if (options.requireReduction && this.agent.records.restoring === null) {
234565
- const previousHistoryTokens = estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true }));
234566
235025
  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: {
235026
+ 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
235027
  reason: "non_reducing_summary",
234569
235028
  contextUnchanged: true,
234570
235029
  previousHistoryTokens,
@@ -234577,6 +235036,7 @@ var ContextMemory = class {
234577
235036
  const result = {
234578
235037
  summary: input.summary,
234579
235038
  contextSummary,
235039
+ ...toolContinuity === void 0 ? {} : { toolContinuity },
234580
235040
  compactedCount: input.compactedCount,
234581
235041
  tokensBefore: input.tokensBefore,
234582
235042
  tokensAfter,
@@ -234593,6 +235053,7 @@ var ContextMemory = class {
234593
235053
  this.agent.replayBuilder.patchLast("compaction", { result: {
234594
235054
  summary: result.summary,
234595
235055
  contextSummary: result.contextSummary,
235056
+ ...result.toolContinuity === void 0 ? {} : { toolContinuity: result.toolContinuity },
234596
235057
  compactedCount: result.compactedCount,
234597
235058
  tokensBefore: result.tokensBefore,
234598
235059
  tokensAfter: result.tokensAfter,
@@ -236200,20 +236661,75 @@ function fingerprintTitle(title) {
236200
236661
  return createHash("sha256").update(title.trim().toLowerCase()).digest("hex");
236201
236662
  }
236202
236663
  async function loadRegister(mistakeDir = defaultMistakeDir()) {
236664
+ const target = join(mistakeDir, REGISTER_FILENAME);
236203
236665
  try {
236204
236666
  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 {
236667
+ const before = await lstat(target).catch((error) => {
236668
+ if (error.code === "ENOENT") return void 0;
236669
+ throw error;
236670
+ });
236671
+ if (before === void 0) return {
236209
236672
  version: 1,
236210
236673
  entries: []
236211
236674
  };
236675
+ if (!before.isFile() || before.isSymbolicLink()) throw new Error("Mistake register must be a regular file.");
236676
+ const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
236677
+ let content;
236678
+ try {
236679
+ const opened = await handle.stat();
236680
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) throw new Error("Mistake register changed while opening.");
236681
+ content = await handle.readFile("utf8");
236682
+ } finally {
236683
+ await handle.close();
236684
+ }
236685
+ let parsed;
236686
+ try {
236687
+ parsed = JSON.parse(content);
236688
+ } catch {
236689
+ throw new Error("Mistake register has invalid JSON.");
236690
+ }
236691
+ assertRegister(parsed);
236692
+ return parsed;
236693
+ } catch (error) {
236694
+ throw registerError(error, "read");
236212
236695
  }
236213
236696
  }
236214
236697
  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");
236698
+ assertRegister(register);
236699
+ const target = join(mistakeDir, REGISTER_FILENAME);
236700
+ const temporary = `${target}.tmp.${process.pid}.${randomUUID()}`;
236701
+ let created = false;
236702
+ try {
236703
+ await loadRegister(mistakeDir);
236704
+ const handle = await open(temporary, "wx", 384);
236705
+ created = true;
236706
+ try {
236707
+ await handle.writeFile(JSON.stringify(register, null, 2), "utf8");
236708
+ await handle.sync();
236709
+ } finally {
236710
+ await handle.close();
236711
+ }
236712
+ await rename(temporary, target);
236713
+ } catch (error) {
236714
+ throw registerError(error, "write");
236715
+ } finally {
236716
+ if (created) await unlink(temporary).catch(() => {});
236717
+ }
236718
+ }
236719
+ function assertRegister(value) {
236720
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Mistake register has an invalid v1 schema.");
236721
+ const register = value;
236722
+ if (register["version"] !== 1 || !Array.isArray(register["entries"]) || !register["entries"].every((entry) => {
236723
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
236724
+ const fields = entry;
236725
+ 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");
236726
+ })) throw new Error("Mistake register has an invalid v1 schema.");
236727
+ }
236728
+ function registerError(error, operation) {
236729
+ if (error instanceof Error && error.message.startsWith("Mistake register ")) return error;
236730
+ const code = typeof error === "object" && error !== null ? error.code : void 0;
236731
+ const suffix = typeof code === "string" && /^[A-Z0-9_]+$/u.test(code) ? ` (${code})` : "";
236732
+ return /* @__PURE__ */ new Error(`Mistake register ${operation} failed${suffix}.`);
236217
236733
  }
236218
236734
  /**
236219
236735
  * Findet einen Register-Eintrag anhand des Titels (Fingerabdruck).
@@ -238906,6 +239422,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
238906
239422
  const DEFERRED_TOOL_LOADER_NAME = "ToolSearch";
238907
239423
  const MAX_PERSISTENT_DEFERRED_TOOLS = 12;
238908
239424
  const MAX_TOOL_SEARCH_QUERY_CHARS = 1e3;
239425
+ const MAX_TOOL_CATALOG_RESULT_BYTES = 4096;
238909
239426
  const MAX_AUTO_RANKED_TOOLS = 6;
238910
239427
  const deferredToolLoaders = /* @__PURE__ */ new WeakSet();
238911
239428
  const TOOL_RANK_EMBEDDED_CONTENT_RE = /<(attached_documents?|attachment_content|document_content|file_content)\b[^>]*>[\s\S]*?<\/\1>/giu;
@@ -238976,6 +239493,32 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
238976
239493
  "suchen"
238977
239494
  ]);
238978
239495
  const TOOL_SEARCH_RELATED_TERM_GROUPS = Object.freeze([
239496
+ Object.freeze([
239497
+ "persist",
239498
+ "save",
239499
+ "speichere",
239500
+ "speichern",
239501
+ "store"
239502
+ ]),
239503
+ Object.freeze([
239504
+ "erinnerung",
239505
+ "erinnerungen",
239506
+ "fact",
239507
+ "facts",
239508
+ "fakt",
239509
+ "fakten",
239510
+ "memory",
239511
+ "memories",
239512
+ "merk",
239513
+ "merken",
239514
+ "remember"
239515
+ ]),
239516
+ Object.freeze([
239517
+ "note",
239518
+ "notes",
239519
+ "notiz",
239520
+ "notizen"
239521
+ ]),
238979
239522
  Object.freeze([
238980
239523
  "checklist",
238981
239524
  "list",
@@ -239124,6 +239667,27 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239124
239667
  for (const leaf of leaves) leafCounts.set(leaf, (leafCounts.get(leaf) || 0) + 1);
239125
239668
  return names.map((name, index) => leafCounts.get(leaves[index]) === 1 ? leaves[index] : name).sort((left, right) => left.localeCompare(right));
239126
239669
  }
239670
+ function boundedDeferredToolCatalog(tools, filter) {
239671
+ const needle = filter.toLowerCase();
239672
+ const matched = deferredToolCatalog(tools).filter((name) => name.toLowerCase().includes(needle));
239673
+ const names = [];
239674
+ let bytes = Buffer.byteLength(JSON.stringify({
239675
+ names,
239676
+ total: matched.length,
239677
+ truncated: false
239678
+ }));
239679
+ for (const name of matched) {
239680
+ const nextBytes = Buffer.byteLength(JSON.stringify(name)) + (names.length > 0 ? 1 : 0);
239681
+ if (bytes + nextBytes > MAX_TOOL_CATALOG_RESULT_BYTES) break;
239682
+ names.push(name);
239683
+ bytes += nextBytes;
239684
+ }
239685
+ return JSON.stringify({
239686
+ names,
239687
+ total: matched.length,
239688
+ truncated: names.length < matched.length
239689
+ });
239690
+ }
239127
239691
  function normalizeDeferredToolQuery(value) {
239128
239692
  const query = String(value ?? "").trim();
239129
239693
  if (query.length <= MAX_TOOL_SEARCH_QUERY_CHARS) return query;
@@ -239173,22 +239737,39 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239173
239737
  function relatedSearchTerms(token) {
239174
239738
  return TOOL_SEARCH_RELATED_TERM_GROUPS.find((terms) => terms.includes(token)) || [token];
239175
239739
  }
239740
+ function withoutFencedCode(value) {
239741
+ const visibleLines = [];
239742
+ let fence = null;
239743
+ for (const line of String(value || "").split(/\r?\n/)) {
239744
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})/u)?.[1];
239745
+ if (fence) {
239746
+ if (marker?.[0] === fence[0] && marker.length >= fence.length && /^ {0,3}(?:`{3,}|~{3,})[ \t]*$/u.test(line)) fence = null;
239747
+ continue;
239748
+ }
239749
+ if (marker) {
239750
+ fence = marker;
239751
+ continue;
239752
+ }
239753
+ visibleLines.push(line);
239754
+ }
239755
+ return visibleLines.join("\n");
239756
+ }
239176
239757
  function rankingIntentTexts(value) {
239177
239758
  const text = String(value || "");
239178
239759
  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, " ")));
239760
+ return (channelBodies.length > 0 ? channelBodies : [text]).map((intentText) => normalizeDeferredToolQuery(withoutFencedCode(intentText.replace(TOOL_RANK_EMBEDDED_CONTENT_RE, " "))));
239180
239761
  }
239181
239762
  function rankingTokens(value) {
239182
239763
  return [...new Set(String(value || "").normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]{3,}/gu) || [])].filter((token) => !TOOL_RANK_STOP_WORDS.has(token));
239183
239764
  }
239184
- function toolRankingDocument(tool) {
239765
+ function toolRankingDocument(tool, trustedToolNames) {
239185
239766
  const name = String(tool?.name || "");
239186
239767
  const leaf = name.split(/__|:/).at(-1) || name;
239187
239768
  const splitToolName = (value) => String(value || "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ");
239188
239769
  const nameTokens = rankingTokens(`${splitToolName(name)} ${splitToolName(leaf)}`);
239189
239770
  const descriptionTokens = rankingTokens(tool?.description);
239190
239771
  const parameterText = [];
239191
- const properties = tool?.parameters?.properties;
239772
+ const properties = /^mcp__/iu.test(name) && !trustedToolNames?.has(name) ? void 0 : tool?.parameters?.properties;
239192
239773
  if (properties && typeof properties === "object") for (const [parameterName, definition] of Object.entries(properties)) parameterText.push(parameterName, definition?.description || "");
239193
239774
  const exampleText = Array.isArray(tool?.examples) ? tool.examples.map((example) => example?.prompt || "").join(" ") : "";
239194
239775
  return {
@@ -239220,7 +239801,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239220
239801
  const queryTokens = [...new Set(rankingIntentTexts(value).flatMap(rankingTokens))];
239221
239802
  if (queryTokens.length === 0) return /* @__PURE__ */ new Set();
239222
239803
  const ranked = (Array.isArray(tools) ? tools : []).map((tool) => {
239223
- const document = toolRankingDocument(tool);
239804
+ const document = toolRankingDocument(tool, options.trustedToolNames);
239224
239805
  if (!document.name) return null;
239225
239806
  let score = 0;
239226
239807
  let matchedTokens = 0;
@@ -239234,7 +239815,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239234
239815
  if (document.nameTokens.has(token)) exactNameMatches += 1;
239235
239816
  else if (relatedSearchTerms(token).some((term) => document.nameTokens.has(term))) relatedNameMatches += 1;
239236
239817
  }
239237
- return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || relatedNameMatches >= 1 && score >= 5 ? {
239818
+ return matchedTokens >= 2 || exactNameMatches >= 1 && score >= 10 || queryTokens.length === 1 && relatedNameMatches >= 1 && score >= 5 ? {
239238
239819
  name: document.name,
239239
239820
  score,
239240
239821
  matchedTokens,
@@ -239255,11 +239836,11 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239255
239836
  function searchRelatedDeferredTools(tools, query, options = {}) {
239256
239837
  const normalized = normalizeDeferredToolQuery(query).toLowerCase();
239257
239838
  if (!normalized || normalized.startsWith("select:")) return [];
239258
- const tokens = normalized.split(/\s+/).filter(Boolean);
239839
+ const tokens = normalized.match(/\+?[\p{L}\p{N}_-]+/gu) || [];
239259
239840
  const requestedNameTokens = tokens.filter((token) => token.startsWith("+")).map((token) => token.slice(1)).filter(Boolean);
239260
239841
  const relaxRequiredNameTokens = options.relaxRequiredNameTokens === true && requestedNameTokens.length > 0;
239261
239842
  const requiredNameTokens = relaxRequiredNameTokens ? [] : requestedNameTokens;
239262
- const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token));
239843
+ const searchTokens = [...tokens.filter((token) => !token.startsWith("+")), ...relaxRequiredNameTokens ? requestedNameTokens : []].filter((token) => !TOOL_SEARCH_INTENT_WORDS.has(token) && !TOOL_RANK_STOP_WORDS.has(token));
239263
239844
  if (searchTokens.length < 2 && !TOOL_SEARCH_SINGLE_TERM_RELATED_FALLBACKS.has(searchTokens[0])) return [];
239264
239845
  const minimumMatchedTokens = searchTokens.length === 1 ? 1 : Math.max(2, Math.ceil(searchTokens.length * .75));
239265
239846
  const ranked = tools.map((tool) => {
@@ -239328,8 +239909,8 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239328
239909
  name: loaderName,
239329
239910
  description: [
239330
239911
  `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.`
239912
+ "Use select:leaf_name for one known tool, plain keywords to search, +word to require a name word, or catalog:fragment to list matching names.",
239913
+ `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
239914
  ].join("\n"),
239334
239915
  parameters: {
239335
239916
  type: "object",
@@ -239346,7 +239927,12 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239346
239927
  return {
239347
239928
  description: query ? `Searching tool schemas for ${query}` : "Searching tool schemas",
239348
239929
  approvalRule: loaderName,
239349
- execute: async () => {
239930
+ execute: async (context) => {
239931
+ context?.signal?.throwIfAborted();
239932
+ if (query.toLowerCase().startsWith("catalog:")) return {
239933
+ isError: false,
239934
+ output: boundedDeferredToolCatalog([...available.values()], query.slice(8).trim())
239935
+ };
239350
239936
  let matches = searchDeferredTools([...available.values()], query);
239351
239937
  let relatedFallback = false;
239352
239938
  if (matches.length === 0) {
@@ -250063,7 +250649,8 @@ function logStepTiming(log, turnId, step, response) {
250063
250649
  inputOtherTokens: response.usage.inputOther,
250064
250650
  cacheReadTokens: response.usage.inputCacheRead,
250065
250651
  cacheCreationTokens: response.usage.inputCacheCreation,
250066
- outputTokens: response.usage.output
250652
+ outputTokens: response.usage.output,
250653
+ ...response.usage.outputReasoning === void 0 ? {} : { outputReasoningTokens: response.usage.outputReasoning }
250067
250654
  });
250068
250655
  }
250069
250656
  function deriveStepStopReason(response) {
@@ -251051,6 +251638,7 @@ const MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE = "mcp.oauth.authorization_url";
251051
251638
  const tokenUsageSchema = object({
251052
251639
  inputOther: number$1(),
251053
251640
  output: number$1(),
251641
+ outputReasoning: number$1().optional(),
251054
251642
  inputCacheRead: number$1(),
251055
251643
  inputCacheCreation: number$1()
251056
251644
  });
@@ -251210,7 +251798,11 @@ const goalChangeStatsSchema = object({
251210
251798
  wallClockMs: number$1()
251211
251799
  });
251212
251800
  const goalChangeSchema = object({
251213
- kind: _enum(["lifecycle", "completion"]),
251801
+ kind: _enum([
251802
+ "lifecycle",
251803
+ "completion",
251804
+ "edited"
251805
+ ]),
251214
251806
  status: goalStatusSchema.optional(),
251215
251807
  reason: string().optional(),
251216
251808
  stats: goalChangeStatsSchema.optional(),
@@ -251361,6 +251953,12 @@ const turnEndReasonSchema = _enum([
251361
251953
  "failed",
251362
251954
  "filtered"
251363
251955
  ]);
251956
+ const contextChangedEventSchema = object({
251957
+ type: literal("context.changed"),
251958
+ generation: string().uuid(),
251959
+ revision: number$1().int().positive().max(Number.MAX_SAFE_INTEGER),
251960
+ reason: _enum(["clear", "undo"])
251961
+ });
251364
251962
  const agentStatusUpdatedEventSchema = object({
251365
251963
  type: literal("agent.status.updated"),
251366
251964
  model: string().optional(),
@@ -251680,6 +252278,7 @@ const eventSchema = discriminatedUnion("type", [
251680
252278
  errorEventSchema,
251681
252279
  warningEventSchema,
251682
252280
  agentStatusUpdatedEventSchema,
252281
+ contextChangedEventSchema,
251683
252282
  sessionMetaUpdatedEventSchema,
251684
252283
  sessionCreatedEventSchema,
251685
252284
  workspaceCreatedEventSchema,
@@ -265080,9 +265679,16 @@ var ToolCallDeduplicator = class {
265080
265679
  //#region ../../packages/agent-core/src/agent/turn/tool-result-budget.ts
265081
265680
  const TOOL_RESULT_MAX_CHARS = 5e4;
265082
265681
  const TOOL_RESULT_PREVIEW_CHARS = 2e3;
265682
+ function toolResultMaxChars(maxContextTokens) {
265683
+ if (maxContextTokens === void 0 || !Number.isFinite(maxContextTokens) || maxContextTokens <= 0) return TOOL_RESULT_MAX_CHARS;
265684
+ if (maxContextTokens < 1e5) return 16e3;
265685
+ if (maxContextTokens < 2e5) return 32e3;
265686
+ return TOOL_RESULT_MAX_CHARS;
265687
+ }
265083
265688
  async function budgetToolResultForModel(options) {
265084
265689
  const text = persistableToolResultText(options.result.output);
265085
- if (text === void 0 || text.length <= TOOL_RESULT_MAX_CHARS) return options.result;
265690
+ const maxChars = toolResultMaxChars(options.maxContextTokens);
265691
+ if (text === void 0 || text.length <= maxChars) return options.result;
265086
265692
  if (options.result.truncated === true) return options.result;
265087
265693
  if (options.homedir === void 0) return options.result;
265088
265694
  if (text.includes("\0") || Buffer.from(text, "utf8").toString("utf8") !== text || text.split("\n").some((line) => line.length > 2e3)) return options.result;
@@ -265093,7 +265699,7 @@ async function budgetToolResultForModel(options) {
265093
265699
  toolCallId: options.toolCallId
265094
265700
  }, text);
265095
265701
  if (outputPath === void 0) return options.result;
265096
- const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath);
265702
+ const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath, maxChars);
265097
265703
  return options.result.isError === true ? {
265098
265704
  ...options.result,
265099
265705
  output,
@@ -265125,9 +265731,9 @@ async function saveToolResult(options, text) {
265125
265731
  return;
265126
265732
  }
265127
265733
  }
265128
- function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
265734
+ function renderPersistedToolResult(toolName, toolCallId, text, outputPath, maxChars) {
265129
265735
  const lines = [
265130
- `Tool output exceeded ${String(TOOL_RESULT_MAX_CHARS)} characters; showing a preview only.`,
265736
+ `Tool output exceeded ${String(maxChars)} characters; showing a preview only.`,
265131
265737
  `tool_name: ${toolName}`,
265132
265738
  `tool_call_id: ${toolCallId}`,
265133
265739
  `output_size_chars: ${String(text.length)}`,
@@ -267093,14 +267699,27 @@ function blunToolsForOrigin(tools, origin) {
267093
267699
  * the ordinary permission pipeline.
267094
267700
  */
267095
267701
  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)) {
267702
+ const schemaBudget = (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens);
267703
+ if (estimateTokensForTools(tools, schemaBudget) <= schemaBudget || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
267097
267704
  selected.splice(0, selected.length, ...tools);
267098
267705
  return selected;
267099
267706
  }
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));
267707
+ const priorLoaderIndex = selected.findIndex(import_turn_tool_performance_policy.isDeferredToolLoader);
267708
+ const justLoaded = new Set(priorLoaderIndex < 0 ? [] : selected.slice(priorLoaderIndex + 1).filter((tool) => loadedToolNames.has(tool.name)).map((tool) => tool.name));
267709
+ const remembered = new Set(loadedToolNames);
267710
+ const choose = () => {
267711
+ 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)));
267712
+ const names = new Set(selected.map((tool) => tool.name));
267713
+ const deferred = tools.filter((tool) => !names.has(tool.name));
267714
+ if (deferred.length > 0) selected.push((0, import_turn_tool_performance_policy.createDeferredToolLoader)(selected, deferred, loadedToolNames));
267715
+ };
267716
+ choose();
267717
+ for (const name of loadedToolNames) {
267718
+ if (estimateTokensForTools(selected, schemaBudget) <= schemaBudget) break;
267719
+ if (justLoaded.has(name)) continue;
267720
+ if (!remembered.delete(name)) continue;
267721
+ choose();
267722
+ }
267104
267723
  return selected;
267105
267724
  }
267106
267725
  const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
@@ -267590,6 +268209,7 @@ var TurnFlow = class {
267590
268209
  const appendedMessage = this.agent.context.history.at(-1);
267591
268210
  const turnAnchor = appendedMessage !== previousLastMessage && appendedMessage?.role === "user" && appendedMessage.origin === origin ? appendedMessage : void 0;
267592
268211
  const startedAt = Date.now();
268212
+ const preRequestHookStartedAt = performance.now();
267593
268213
  let ended;
267594
268214
  let blockedByUserPromptHook = false;
267595
268215
  let completedStopReason;
@@ -267615,6 +268235,13 @@ var TurnFlow = class {
267615
268235
  this.agent.log.warn("Turn-start observation failed");
267616
268236
  }
267617
268237
  signal.throwIfAborted();
268238
+ if (this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
268239
+ this.agent.log.info("turn pre-request phase", {
268240
+ turnId,
268241
+ phase: "turn-hooks",
268242
+ durationMs: Math.max(0, Math.round(performance.now() - preRequestHookStartedAt))
268243
+ });
268244
+ } catch {}
267618
268245
  const stopReason = await this.runStepLoop(turnId, signal, input, origin, turnAnchor, (response) => {
267619
268246
  completedResponse = response;
267620
268247
  });
@@ -267726,6 +268353,10 @@ var TurnFlow = class {
267726
268353
  signal.throwIfAborted();
267727
268354
  const blockResult = renderUserPromptHookBlockResult(promptHookResults);
267728
268355
  if (blockResult !== void 0) {
268356
+ this.agent.log.warn("turn blocked by UserPromptSubmit hook", {
268357
+ turnId,
268358
+ reason: blockResult.message
268359
+ });
267729
268360
  this.agent.context.appendMessage({
267730
268361
  role: "assistant",
267731
268362
  content: [{
@@ -267784,9 +268415,26 @@ var TurnFlow = class {
267784
268415
  let stopHookContinuationUsed = false;
267785
268416
  let goalOutcomeMessageContinuationUsed = false;
267786
268417
  const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
268418
+ const diagnosePreRequest = this.agent.type === "main" && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1";
268419
+ const recordPreRequestPhase = (phase, startedAt) => {
268420
+ if (!diagnosePreRequest) return;
268421
+ try {
268422
+ this.agent.log.info("turn pre-request phase", {
268423
+ turnId,
268424
+ phase,
268425
+ durationMs: Math.max(0, Math.round(performance.now() - startedAt))
268426
+ });
268427
+ } catch {}
268428
+ };
268429
+ let phaseStartedAt = performance.now();
267787
268430
  await this.agent.mcp?.waitForInitialLoad(signal);
268431
+ recordPreRequestPhase("mcp-initial-load", phaseStartedAt);
268432
+ phaseStartedAt = performance.now();
267788
268433
  const personalMemoryRecall = await this.agent.injection.injectPersonalMemoryForTurn(turnId, input, origin, signal);
268434
+ recordPreRequestPhase("personal-memory-recall", phaseStartedAt);
268435
+ phaseStartedAt = performance.now();
267789
268436
  await this.agent.injection.injectGoal();
268437
+ recordPreRequestPhase("goal-injection", phaseStartedAt);
267790
268438
  this.setActiveSteerAcceptance(turnId, true);
267791
268439
  const turnNeedsThinking = blunTurnNeedsTools(blunThinkingIntentInput(input), origin);
267792
268440
  let toolInput = input;
@@ -267797,12 +268445,14 @@ var TurnFlow = class {
267797
268445
  const raw = [...this.agent.context.history];
267798
268446
  const history = personalMemoryRecall === void 0 ? raw : [...raw, personalMemoryRecall];
267799
268447
  const selected = taskToolsNeeded ? history : greetingHistory(history, greetingAnchor);
268448
+ const projectionStartedAt = performance.now();
267800
268449
  const projected = await this.historicalToolProjection.project(selected, turnAnchor, tools, {
267801
268450
  turnId: String(turnId),
267802
268451
  stepNumber: this.currentStep,
267803
268452
  signal,
267804
268453
  llm: turnLLM
267805
268454
  });
268455
+ recordPreRequestPhase("historical-tool-projection", projectionStartedAt);
267806
268456
  const current = this.agent.context.history;
267807
268457
  if (current.length !== raw.length || current.some((message, index) => message !== raw[index])) {
267808
268458
  const latest = personalMemoryRecall === void 0 ? current : [...current, personalMemoryRecall];
@@ -267813,7 +268463,7 @@ var TurnFlow = class {
267813
268463
  partialContext = selected.length < history.length || projected.changed;
267814
268464
  return projected.history;
267815
268465
  };
267816
- const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off");
268466
+ const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off", turnId);
267817
268467
  const turnTools = this.agent.tools.captureTurnTools();
267818
268468
  while (true) {
267819
268469
  signal.throwIfAborted();
@@ -267823,18 +268473,22 @@ var TurnFlow = class {
267823
268473
  try {
267824
268474
  const selectedTools = [];
267825
268475
  this.refreshSelectedTools = (snapshot) => {
268476
+ const selectionStartedAt = performance.now();
267826
268477
  if (snapshot.messages.length > 0) toolInput = snapshot.messages.flatMap((message) => message.content);
267827
268478
  const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, turnTools()), origin);
267828
268479
  const intent = blunThinkingIntentInput(toolInput);
267829
268480
  taskToolsNeeded ||= blunTurnNeedsTools(intent, origin) || blunTurnHasAttachment(toolInput);
267830
268481
  const required = (0, import_turn_tool_performance_policy.mediaToolNamesForTurnText)(blunExtractText(toolInput));
267831
268482
  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))) {
268483
+ const maxContextTokens = this.agent.fullCompaction.getEffectiveMaxContextTokens();
268484
+ blunSelectTurnTools(eligibleTools, maxContextTokens, this.loadedToolNames, required, selectedTools);
268485
+ const trustedToolNames = new Set(eligibleTools.filter((tool) => this.agent.tools.isUserTool(tool)).map((tool) => tool.name));
268486
+ for (const name of (0, import_turn_tool_performance_policy.rankedToolNamesForTurnText)(eligibleTools, blunExtractText(intent), { trustedToolNames })) {
267834
268487
  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);
268488
+ 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
268489
  }
267837
268490
  } 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)));
268491
+ recordPreRequestPhase("tool-selection", selectionStartedAt);
267838
268492
  return selectedTools;
267839
268493
  };
267840
268494
  return (await runTurn({
@@ -267847,14 +268501,24 @@ var TurnFlow = class {
267847
268501
  }, completion);
267848
268502
  },
267849
268503
  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
- }),
268504
+ buildMessages: async () => {
268505
+ const startedAt = performance.now();
268506
+ const messages = this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true });
268507
+ recordPreRequestPhase("message-build", startedAt);
268508
+ return messages;
268509
+ },
268510
+ buildMessagesStrict: async () => {
268511
+ const startedAt = performance.now();
268512
+ const messages = this.agent.context.project(await turnHistory(selectedTools), {
268513
+ synthesizeMissing: true,
268514
+ dropOrphanResults: true,
268515
+ dedupeDuplicateToolCalls: true,
268516
+ dropLeadingNonUser: true,
268517
+ mergeConsecutiveAssistants: true
268518
+ });
268519
+ recordPreRequestPhase("message-build-strict", startedAt);
268520
+ return messages;
268521
+ },
267858
268522
  dispatchEvent: this.buildDispatchEvent(turnId, signal, onFinalResponse, () => partialContext),
267859
268523
  tools: selectedTools,
267860
268524
  log: this.agent.log,
@@ -267871,8 +268535,10 @@ var TurnFlow = class {
267871
268535
  },
267872
268536
  hooks: {
267873
268537
  beforeStep: async ({ signal: stepSignal, llm }) => {
268538
+ const injectionStartedAt = performance.now();
267874
268539
  this.agent.microCompaction.detect();
267875
268540
  await this.agent.injection.inject();
268541
+ recordPreRequestPhase("step-injection", injectionStartedAt);
267876
268542
  stepSignal.throwIfAborted();
267877
268543
  deduper.beginStep();
267878
268544
  },
@@ -267910,6 +268576,17 @@ var TurnFlow = class {
267910
268576
  signal.throwIfAborted();
267911
268577
  if (stopBlock !== void 0) {
267912
268578
  stopHookContinuationUsed = true;
268579
+ this.agent.log.warn("turn continuation blocked by Stop hook", {
268580
+ turnId,
268581
+ reason: stopBlock.reason
268582
+ });
268583
+ this.agent.emitEvent({
268584
+ type: "hook.result",
268585
+ turnId,
268586
+ hookEvent: "Stop",
268587
+ content: stopBlock.reason,
268588
+ blocked: true
268589
+ });
267913
268590
  this.agent.context.appendUserMessage([{
267914
268591
  type: "text",
267915
268592
  text: stopBlock.reason
@@ -267949,6 +268626,7 @@ var TurnFlow = class {
267949
268626
  });
267950
268627
  return budgetToolResultForModel({
267951
268628
  homedir: this.agent.homedir,
268629
+ maxContextTokens: this.agent.fullCompaction.getEffectiveMaxContextTokens(),
267952
268630
  toolName: ctx.toolCall.name,
267953
268631
  toolCallId: ctx.toolCall.id,
267954
268632
  result: finalResult
@@ -268806,10 +269484,10 @@ var BashTool = class {
268806
269484
  },
268807
269485
  approvalRule: literalRulePattern(this.name, args.command),
268808
269486
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
268809
- execute: ({ signal, onUpdate, onForegroundTaskStart }) => this.execution(args, signal, onUpdate, onForegroundTaskStart)
269487
+ execute: ({ signal, onUpdate, onForegroundTaskStart, executionOrigin }) => this.execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin)
268810
269488
  };
268811
269489
  }
268812
- spawn(effectiveCwd, command) {
269490
+ spawn(effectiveCwd, command, executionOrigin) {
268813
269491
  const shellCwd = this.isWindowsBash ? windowsPathToPosixPath(effectiveCwd) : effectiveCwd;
268814
269492
  const shellArgs = [
268815
269493
  this.kaos.osEnv.shellPath,
@@ -268826,9 +269504,11 @@ var BashTool = class {
268826
269504
  ...process.env,
268827
269505
  ...noninteractiveEnv
268828
269506
  };
269507
+ delete mergedEnv["BLUN_SHELL"];
269508
+ if (executionOrigin === "operator_shell") mergedEnv["BLUN_SHELL"] = "operator-local";
268829
269509
  return this.kaos.execWithEnv(shellArgs, mergedEnv);
268830
269510
  }
268831
- async execution(args, signal, onUpdate, onForegroundTaskStart) {
269511
+ async execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin) {
268832
269512
  const validationError = this.validateRunRequest(args, signal);
268833
269513
  if (validationError !== void 0) return validationError;
268834
269514
  const startsInBackground = args.run_in_background === true;
@@ -268840,7 +269520,7 @@ var BashTool = class {
268840
269520
  const builder = new ToolResultBuilder();
268841
269521
  let proc;
268842
269522
  try {
268843
- proc = await this.spawn(effectiveCwd, command);
269523
+ proc = await this.spawn(effectiveCwd, command, executionOrigin);
268844
269524
  } catch (error) {
268845
269525
  return {
268846
269526
  isError: true,
@@ -269204,14 +269884,14 @@ function classifySearchError(error) {
269204
269884
  }
269205
269885
  //#endregion
269206
269886
  //#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";
269887
+ 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
269888
  //#endregion
269209
269889
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.ts
269210
269890
  /**
269211
269891
  * CodebaseSearchTool — semantic code search via the codebase-index.
269212
269892
  *
269213
269893
  * Shells out to `python codebase_index.py query <repo> "<frage>" --top N`
269214
- * through spawnSync. The script lives at
269894
+ * through asynchronous execFile. The script lives at
269215
269895
  * `packages/codebase-index/codebase_index.py` relative to the workspace root.
269216
269896
  *
269217
269897
  * Unlike Grep (keyword/regex) or Glob (file-name patterns), this tool finds
@@ -269219,11 +269899,13 @@ var codebase_search_default = "Find code by **meaning**, not by keywords.\n\nThi
269219
269899
  * to the query, so it can locate a keyboard handler even when the word
269220
269900
  * "keyboard" never appears in the source.
269221
269901
  */
269902
+ const MAX_SEARCH_RESULTS = 100;
269222
269903
  const CodebaseSearchInputSchema = object({
269223
269904
  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.")
269905
+ 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
269906
  });
269226
269907
  const SCRIPT_TIMEOUT_MS = 6e4;
269908
+ const SCRIPT_MAX_BUFFER = 1024 * 1024;
269227
269909
  function isScriptFile(path) {
269228
269910
  try {
269229
269911
  return statSync(path).isFile();
@@ -269262,45 +269944,72 @@ var CodebaseSearchTool = class {
269262
269944
  },
269263
269945
  approvalRule: literalRulePattern(this.name, args.query),
269264
269946
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.query),
269265
- execute: () => this.execution(args)
269947
+ execute: async (context) => {
269948
+ context.signal.throwIfAborted();
269949
+ return this.execution(args, context.signal);
269950
+ }
269266
269951
  };
269267
269952
  }
269268
- execution(args) {
269953
+ async execution(args, signal) {
269269
269954
  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."));
269955
+ 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)}.`);
269956
+ if (this.scriptPath.length === 0) return builder.error("Could not locate codebase_index.py. Set CODEBASE_INDEX_SCRIPT to its absolute path.");
269271
269957
  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
269958
+ const result = await new Promise((resolve) => {
269959
+ let closed = false;
269960
+ let output;
269961
+ const finish = () => {
269962
+ if (closed && output !== void 0) resolve(output);
269963
+ };
269964
+ execFile("python", [
269965
+ this.scriptPath,
269966
+ "query",
269967
+ this.cwd,
269968
+ args.query,
269969
+ "--top",
269970
+ String(args.top),
269971
+ "--model",
269972
+ "auto"
269973
+ ], {
269974
+ cwd: this.cwd,
269975
+ timeout: SCRIPT_TIMEOUT_MS,
269976
+ encoding: "utf-8",
269977
+ windowsHide: true,
269978
+ shell: false,
269979
+ maxBuffer: SCRIPT_MAX_BUFFER,
269980
+ signal
269981
+ }, (error, stdout, stderr) => {
269982
+ output = {
269983
+ error,
269984
+ stdout,
269985
+ stderr
269986
+ };
269987
+ finish();
269988
+ }).once("close", () => {
269989
+ closed = true;
269990
+ finish();
269991
+ });
269286
269992
  });
269287
- if (result.error !== void 0) {
269993
+ signal.throwIfAborted();
269994
+ if (result.error !== null && typeof result.error.code !== "number") {
269288
269995
  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}`));
269996
+ builder.write(result.stdout + result.stderr);
269997
+ return builder.error(`Failed to run codebase_index.py: ${msg}`);
269290
269998
  }
269291
269999
  const stdout = result.stdout ?? "";
269292
270000
  const stderr = result.stderr ?? "";
269293
- const exitCode = result.status ?? -1;
270001
+ const exitCode = result.error?.code ?? 0;
269294
270002
  if (exitCode !== 0) {
269295
270003
  const combined = stderr.trim().length > 0 ? `${stdout}${stdout.length > 0 ? "\n" : ""}Error: ${stderr.trim()}` : stdout;
269296
270004
  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)}.`));
270005
+ return builder.error(`Codebase search failed with exit code ${String(exitCode)}.`);
269298
270006
  }
269299
270007
  builder.write(stdout.length > 0 ? stdout : "No results found.");
269300
- return Promise.resolve(builder.ok());
270008
+ return builder.ok();
269301
270009
  } catch (error) {
270010
+ signal.throwIfAborted();
269302
270011
  const msg = error instanceof Error ? error.message : String(error);
269303
- return Promise.resolve(builder.error(`Codebase search error: ${msg}`));
270012
+ return builder.error(`Codebase search error: ${msg}`);
269304
270013
  }
269305
270014
  }
269306
270015
  };
@@ -270540,6 +271249,7 @@ var ToolManager = class {
270540
271249
  const result = await execution.execute({
270541
271250
  turnId: "",
270542
271251
  toolCallId: "shell-command",
271252
+ executionOrigin: "operator_shell",
270543
271253
  signal: controller.signal,
270544
271254
  onUpdate: (update) => {
270545
271255
  if (update.kind === "stdout") stdout += update.text ?? "";
@@ -270628,6 +271338,9 @@ var ToolManager = class {
270628
271338
  hasUserTool(name) {
270629
271339
  return this.userTools.has(name);
270630
271340
  }
271341
+ isUserTool(tool) {
271342
+ return this.userTools.get(tool.name) === tool;
271343
+ }
270631
271344
  inheritUserTools(parent) {
270632
271345
  for (const tool of parent.userTools.values()) {
270633
271346
  if (!parent.enabledTools.has(tool.name)) continue;
@@ -271412,6 +272125,7 @@ var FallbackLLM = class {
271412
272125
  throw new Error("Fallback request preparation requires a fresh request boundary.");
271413
272126
  }
271414
272127
  let requestParams = params;
272128
+ let messagesReady = false;
271415
272129
  const rebuildRequestMessages = async () => {
271416
272130
  const messages = await rebuildMessages();
271417
272131
  const suffix = requestParams.boundaryMessageSuffix;
@@ -271420,7 +272134,14 @@ var FallbackLLM = class {
271420
272134
  while (true) {
271421
272135
  const candidate = this.current;
271422
272136
  try {
271423
- requestParams = await this.prepareRequest?.(candidate, requestParams, rebuildRequestMessages) ?? requestParams;
272137
+ const prepared = await this.prepareRequest?.(candidate, requestParams, rebuildRequestMessages);
272138
+ if (prepared !== void 0) if ("messagesReady" in prepared) {
272139
+ requestParams = prepared.params;
272140
+ messagesReady = true;
272141
+ } else {
272142
+ requestParams = prepared;
272143
+ messagesReady = false;
272144
+ }
271424
272145
  } catch (error) {
271425
272146
  if (!isFallbackCandidateError(error) && !isUnavailableFallbackCandidate(error)) throw error;
271426
272147
  const nextIndex = this.findNextCandidateIndex();
@@ -271430,13 +272151,14 @@ var FallbackLLM = class {
271430
272151
  ...requestParams,
271431
272152
  messages: await rebuildRequestMessages()
271432
272153
  };
272154
+ messagesReady = false;
271433
272155
  requestParams.signal.throwIfAborted();
271434
272156
  this.currentIndex = nextIndex;
271435
272157
  this.onModelChanged(this.current.alias);
271436
272158
  continue;
271437
272159
  }
271438
272160
  requestParams.signal.throwIfAborted();
271439
- const messages = await rebuildRequestMessages();
272161
+ const messages = messagesReady ? requestParams.messages : await rebuildRequestMessages();
271440
272162
  requestParams.signal.throwIfAborted();
271441
272163
  return {
271442
272164
  ...requestParams,
@@ -271538,8 +272260,13 @@ var LlmRequestLogger = class {
271538
272260
  toolsEstimatedTokens = 0;
271539
272261
  requestSequence = 0;
271540
272262
  lastTurnRequest;
271541
- constructor(log) {
272263
+ serializedPrefixKey;
272264
+ requestSignatures = /* @__PURE__ */ new Map();
272265
+ prefixComparisons = /* @__PURE__ */ new Map();
272266
+ lastSerializedRequest;
272267
+ constructor(log, serializedPrefixDiagnostic = process.env["BLUN_DIAG_SERIALIZED_PREFIX"] === "1") {
271542
272268
  this.log = log;
272269
+ this.serializedPrefixKey = serializedPrefixDiagnostic ? randomBytes(32) : void 0;
271543
272270
  }
271544
272271
  logRequest(input) {
271545
272272
  const sequence = ++this.requestSequence;
@@ -271556,6 +272283,10 @@ var LlmRequestLogger = class {
271556
272283
  toolsHash: fingerprint(JSON.stringify(toolSignature(tools)))
271557
272284
  };
271558
272285
  const signature = JSON.stringify(config);
272286
+ if (this.serializedPrefixKey !== void 0) {
272287
+ this.requestSignatures.set(sequence, signature);
272288
+ if (this.requestSignatures.size > 32) this.requestSignatures.delete(this.requestSignatures.keys().next().value);
272289
+ }
271559
272290
  if (signature !== this.lastConfigLogSignature) {
271560
272291
  this.lastConfigLogSignature = signature;
271561
272292
  this.configRevision++;
@@ -271585,6 +272316,20 @@ var LlmRequestLogger = class {
271585
272316
  }
271586
272317
  };
271587
272318
  if (partialMessageCount > 0) requestFields.partialMessageCount = partialMessageCount;
272319
+ const source = input.systemPromptContributions;
272320
+ if (source?.version === 1 && [
272321
+ source.totalBytes,
272322
+ source.instructionBytes,
272323
+ source.skillListingBytes,
272324
+ source.runtimeMemoryBytes,
272325
+ source.otherBytes
272326
+ ].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 = {
272327
+ totalBytes: source.totalBytes,
272328
+ instructionBytes: source.instructionBytes,
272329
+ skillListingBytes: source.skillListingBytes,
272330
+ runtimeMemoryBytes: source.runtimeMemoryBytes,
272331
+ otherBytes: source.otherBytes
272332
+ };
271588
272333
  this.log.info("llm request", requestFields);
271589
272334
  if (fields?.turnStep !== void 0 && /^\d+\.\d+$/.test(fields.turnStep)) {
271590
272335
  const historyTextOrigins = summarizeRequestTextOrigins(messages);
@@ -271610,10 +272355,51 @@ var LlmRequestLogger = class {
271610
272355
  getLastTurnRequest() {
271611
272356
  return this.lastTurnRequest === void 0 ? void 0 : structuredClone(this.lastTurnRequest);
271612
272357
  }
272358
+ get serializedPrefixDiagnosticEnabled() {
272359
+ return this.serializedPrefixKey !== void 0;
272360
+ }
272361
+ observeSerializedRequest(body, fields, sequence) {
272362
+ const key = this.serializedPrefixKey;
272363
+ if (key === void 0 || !/^\d+\.\d+$/.test(fields?.turnStep ?? "")) return;
272364
+ const signature = this.requestSignatures.get(sequence);
272365
+ if (signature === void 0) return;
272366
+ try {
272367
+ const bytes = Buffer.from(body, "utf8");
272368
+ const granularityBytes = 1024;
272369
+ const chunks = [];
272370
+ for (let start = 0; start + granularityBytes <= bytes.length; start += granularityBytes) chunks.push(createHmac("sha256", key).update(bytes.subarray(start, start + granularityBytes)).digest("hex"));
272371
+ const previous = this.lastSerializedRequest;
272372
+ if (previous !== void 0 && previous.signature === signature) {
272373
+ let common = 0;
272374
+ while (common < previous.chunks.length && common < chunks.length && previous.chunks[common] === chunks[common]) common++;
272375
+ this.prefixComparisons.set(sequence, {
272376
+ version: 1,
272377
+ granularityBytes,
272378
+ previousBytes: previous.bytes,
272379
+ currentBytes: bytes.length,
272380
+ commonPrefixBytesLowerBound: common * granularityBytes,
272381
+ previousSequence: previous.sequence,
272382
+ currentSequence: sequence
272383
+ });
272384
+ if (this.prefixComparisons.size > 32) this.prefixComparisons.delete(this.prefixComparisons.keys().next().value);
272385
+ }
272386
+ this.lastSerializedRequest = {
272387
+ sequence,
272388
+ signature,
272389
+ bytes: bytes.length,
272390
+ chunks
272391
+ };
272392
+ } catch {
272393
+ this.lastSerializedRequest = void 0;
272394
+ }
272395
+ }
271613
272396
  logTransportAttempt(stats, fields, sequence) {
272397
+ const serializedPrefix = sequence === void 0 ? void 0 : this.prefixComparisons.get(sequence);
271614
272398
  this.log.info("llm transport attempt", {
271615
272399
  ...fields,
271616
- ...stats
272400
+ ...stats,
272401
+ ...this.serializedPrefixKey === void 0 || sequence === void 0 ? {} : { requestSequence: sequence },
272402
+ ...serializedPrefix === void 0 ? {} : { serializedPrefix }
271617
272403
  });
271618
272404
  if (sequence === void 0 || this.lastTurnRequest?.sequence !== sequence) return;
271619
272405
  const breakdown = stats.requestBreakdown;
@@ -271635,6 +272421,11 @@ var LlmRequestLogger = class {
271635
272421
  systemMessageBytes: breakdown.systemMessageBytes,
271636
272422
  conversationMessageBytes: breakdown.conversationMessageBytes,
271637
272423
  toolResultMessageBytes: breakdown.toolResultMessageBytes,
272424
+ ...breakdown.toolResultByTool === void 0 ? {} : { toolResultByTool: breakdown.toolResultByTool.map((tool) => ({
272425
+ count: tool.count,
272426
+ bytes: tool.bytes,
272427
+ ...tool.name === void 0 ? {} : { name: tool.name }
272428
+ })) },
271638
272429
  toolSchemaBytes: breakdown.toolSchemaBytes,
271639
272430
  otherBytes: breakdown.otherBytes,
271640
272431
  ...breakdown.toolArrayOverheadBytes === void 0 ? {} : { toolArrayOverheadBytes: breakdown.toolArrayOverheadBytes },
@@ -271696,7 +272487,8 @@ function copyContributions(value) {
271696
272487
  version: source.version,
271697
272488
  totalBytes: source.totalBytes,
271698
272489
  generatedBytes: source.generatedBytes,
271699
- files: source.files.map((file) => copyInstructionFileBytes(file))
272490
+ files: source.files.map((file) => copyInstructionFileBytes(file)),
272491
+ ...source.notLoadedCandidates === void 0 ? {} : { notLoadedCandidates: [...source.notLoadedCandidates] }
271700
272492
  } }
271701
272493
  };
271702
272494
  }
@@ -271792,7 +272584,11 @@ var Agent$1 = class {
271792
272584
  errorMemory;
271793
272585
  replayBuilder;
271794
272586
  additionalDirs;
272587
+ systemPromptStartedAt = /* @__PURE__ */ new Date();
271795
272588
  activeProfile;
272589
+ rawSystemPromptContext;
272590
+ profileSystemPromptText;
272591
+ agentsMdRequestWarningShown = false;
271796
272592
  brandHome;
271797
272593
  _activeResponderModel;
271798
272594
  allowModelFallback = true;
@@ -271879,6 +272675,7 @@ var Agent$1 = class {
271879
272675
  const retryScope = providerRetryScope(modelAlias, provider);
271880
272676
  await this.providerRetryWait.wait(retryScope, generateOptions?.signal);
271881
272677
  const callerTransportAttempt = generateOptions?.onTransportAttempt;
272678
+ const callerSerializedRequest = generateOptions?.onSerializedRequest;
271882
272679
  const ownedGenerateOptions = {
271883
272680
  ...generateOptions,
271884
272681
  transportMaxRetries: 0
@@ -271901,6 +272698,12 @@ var Agent$1 = class {
271901
272698
  callerTransportAttempt?.(stats);
271902
272699
  this.llmRequestLogger.logTransportAttempt(stats, requestLogFields, requestSequence);
271903
272700
  },
272701
+ ...callerSerializedRequest !== void 0 || this.llmRequestLogger.serializedPrefixDiagnosticEnabled ? { onSerializedRequest: (body) => {
272702
+ try {
272703
+ callerSerializedRequest?.(body);
272704
+ } catch {}
272705
+ this.llmRequestLogger.observeSerializedRequest(body, requestLogFields, requestSequence);
272706
+ } } : {},
271904
272707
  onRequestStart: () => {
271905
272708
  requestOptions?.onRequestStart?.();
271906
272709
  requestStarted = true;
@@ -271926,7 +272729,7 @@ var Agent$1 = class {
271926
272729
  get llm() {
271927
272730
  return this.llmForTurn();
271928
272731
  }
271929
- llmForTurn(thinkingEffort) {
272732
+ llmForTurn(thinkingEffort, diagnosticTurnId) {
271930
272733
  const selectedAlias = this.config.model;
271931
272734
  const modelProvider = this.modelProvider;
271932
272735
  if (modelProvider === void 0) throw new BlunError(ErrorCodes.MODEL_NOT_CONFIGURED, "Provider not set");
@@ -271940,7 +272743,9 @@ var Agent$1 = class {
271940
272743
  tools: requestTools,
271941
272744
  messages: await rebuildMessages()
271942
272745
  };
272746
+ 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
272747
  let systemPrompt = this.effectiveSystemPrompt;
272748
+ const preflightStartedAt = performance.now();
271944
272749
  const compacted = await this.fullCompaction.beforeStep(params.signal, {
271945
272750
  messages: params.messages,
271946
272751
  additionalMessages: steers.messages,
@@ -271949,17 +272754,38 @@ var Agent$1 = class {
271949
272754
  rebuildSystemPrompt: () => systemPrompt = this.effectiveSystemPrompt,
271950
272755
  tools: params.tools
271951
272756
  });
272757
+ if (this.type === "main" && diagnosticTurnId !== void 0 && process.env["BLUN_DIAG_PRE_REQUEST_PHASES"] === "1") try {
272758
+ this.log.info("turn pre-request phase", {
272759
+ turnId: diagnosticTurnId,
272760
+ phase: "compaction-preflight",
272761
+ durationMs: Math.max(0, Math.round(performance.now() - preflightStartedAt))
272762
+ });
272763
+ } catch {}
271952
272764
  const tools = params.tools.some((tool) => tool.name === "CompactConversation") && !this.fullCompaction.proactiveCompactionStatus({
271953
272765
  messages: compacted ? await rebuildMessages() : params.messages,
271954
272766
  additionalMessages: steers.messages,
271955
272767
  systemPrompt,
271956
272768
  tools: params.tools
271957
272769
  }).eligible ? params.tools.filter((tool) => tool.name !== "CompactConversation") : params.tools;
271958
- this.turn.commitPendingSteers(steers);
271959
- return {
272770
+ const steersCommitted = this.turn.commitPendingSteers(steers);
272771
+ if (compacted || steersCommitted) params = {
271960
272772
  ...params,
271961
- systemPrompt,
271962
- tools
272773
+ messages: await rebuildMessages()
272774
+ };
272775
+ if (this.config.runtimeModel(_candidate.alias).provider.name === "blun") {
272776
+ const requestBytes = estimateBlunSerializedBodyBytes(systemPrompt, tools, params.messages) + 4096;
272777
+ if (requestBytes >= 5e5) throw new BlunError(ErrorCodes.CONTEXT_OVERFLOW, "The BLUN request is too large after compaction. Shorten fixed instructions or tools.", { details: {
272778
+ estimatedRequestBytes: requestBytes,
272779
+ requestBudgetBytes: 5e5
272780
+ } });
272781
+ }
272782
+ return {
272783
+ params: {
272784
+ ...params,
272785
+ systemPrompt,
272786
+ tools
272787
+ },
272788
+ messagesReady: true
271963
272789
  };
271964
272790
  }, (candidate, _params, error) => {
271965
272791
  this.fullCompaction.observeContextOverflow(error, candidate.alias);
@@ -272031,8 +272857,14 @@ var Agent$1 = class {
272031
272857
  }
272032
272858
  useProfile(profile, context, brandHome) {
272033
272859
  this.setActiveProfile(profile, brandHome);
272034
- this.updateSystemPromptFromProfile(profile, context);
272860
+ this.rawSystemPromptContext = context;
272861
+ if (context?.agentsMdText === void 0) {
272862
+ this.updateSystemPromptFromProfile(profile, context);
272863
+ this.tools.setActiveTools(profile.tools);
272864
+ return;
272865
+ }
272035
272866
  this.tools.setActiveTools(profile.tools);
272867
+ this.updateSystemPromptFromProfile(profile, this.fitProfileInstructions(profile, context));
272036
272868
  }
272037
272869
  setActiveProfile(profile, brandHome) {
272038
272870
  this.activeProfile = profile;
@@ -272047,12 +272879,74 @@ var Agent$1 = class {
272047
272879
  async refreshSystemPrompt() {
272048
272880
  if (this.activeProfile === void 0) return;
272049
272881
  const context = this.systemPromptContextProvider === void 0 ? await prepareSystemPromptContext(this.kaos, this.brandHome, { additionalDirs: this.additionalDirs }) : await this.systemPromptContextProvider();
272050
- this.updateSystemPromptFromProfile(this.activeProfile, context);
272882
+ this.rawSystemPromptContext = context;
272883
+ this.updateSystemPromptFromProfile(this.activeProfile, this.fitProfileInstructions(this.activeProfile, context));
272884
+ }
272885
+ fitProfileInstructions(profile, context, tools = this.tools.loopTools, notify = false) {
272886
+ if (context?.agentsMdText === void 0 || this.config.modelAlias === void 0) return context;
272887
+ if (this.config.runtimeModel(this.config.modelAlias).provider.name !== "blun") return context;
272888
+ const renderFixedPrompt = (candidate) => {
272889
+ const rendered = profile.systemPrompt({
272890
+ osEnv: this.kaos.osEnv,
272891
+ now: this.systemPromptStartedAt,
272892
+ cwd: this.config.cwd,
272893
+ skills: this.skills?.registry,
272894
+ cwdListing: candidate.cwdListing,
272895
+ agentsMd: candidate.agentsMd,
272896
+ additionalDirsInfo: candidate.additionalDirsInfo
272897
+ });
272898
+ const compact = this.skills?.registry === void 0 ? rendered : compactLegacyModelSkillListing(rendered, this.skills.registry);
272899
+ return this.runtimeSystemPromptAppend.length === 0 ? compact : compact.length === 0 ? this.runtimeSystemPromptAppend : `${compact}\n\n${this.runtimeSystemPromptAppend}`;
272900
+ };
272901
+ const fitted = fitAgentsMdContext(context, 4e5, (candidate) => {
272902
+ return estimateBlunSerializedBodyBytes(renderFixedPrompt(candidate), tools, []) + 4096;
272903
+ });
272904
+ const describeFixedEstimate = () => {
272905
+ const systemPrompt = renderFixedPrompt(fitted.context);
272906
+ const envelopeBytes = estimateBlunSerializedBodyBytes("", [], []);
272907
+ const withoutToolsBytes = estimateBlunSerializedBodyBytes(systemPrompt, [], []);
272908
+ return {
272909
+ estimationStage: notify ? "request" : "profile",
272910
+ toolCount: tools.length,
272911
+ serializedPromptBytes: withoutToolsBytes - envelopeBytes,
272912
+ serializedToolsBytes: fitted.fixedRequestBytes - 4096 - withoutToolsBytes,
272913
+ envelopeBytes,
272914
+ reserveBytes: 4096,
272915
+ instructionBytes: fitted.context.agentsMdSources?.totalBytes ?? 0,
272916
+ runtimeAppendBytes: Buffer.byteLength(this.runtimeSystemPromptAppend, "utf8")
272917
+ };
272918
+ };
272919
+ if (!fitted.fits) this.log.warn("fixed BLUN request exceeds 400000 bytes without AGENTS.md", {
272920
+ fixedRequestBytes: fitted.fixedRequestBytes,
272921
+ ...describeFixedEstimate()
272922
+ });
272923
+ else if (fitted.omittedBytes > 0) {
272924
+ this.log.warn("AGENTS.md content shortened for BLUN request budget", {
272925
+ omittedBytes: fitted.omittedBytes,
272926
+ fixedRequestBytes: fitted.fixedRequestBytes,
272927
+ ...describeFixedEstimate()
272928
+ });
272929
+ if (notify && !this.agentsMdRequestWarningShown) {
272930
+ this.agentsMdRequestWarningShown = true;
272931
+ const retained = new Map(fitted.context.agentsMdSources?.files.map((file) => [file.path, file.bytes]));
272932
+ const files = context.agentsMdSources?.files.flatMap((file) => {
272933
+ const read = retained.get(file.path) ?? 0;
272934
+ return read < file.bytes ? [`${file.path}: ${file.bytes} bytes, ${read} read, ${file.bytes - read} omitted`] : [];
272935
+ }) ?? [];
272936
+ this.emitEvent({
272937
+ type: "warning",
272938
+ code: "agents-md-request-truncated",
272939
+ message: `Instructions shortened for the request size limit: ${files.join("; ")}.`
272940
+ });
272941
+ }
272942
+ }
272943
+ return fitted.context;
272051
272944
  }
272052
272945
  updateSystemPromptFromProfile(profile, context) {
272053
272946
  let contributions;
272054
272947
  const systemPrompt = profile.systemPrompt({
272055
272948
  osEnv: this.kaos.osEnv,
272949
+ now: this.systemPromptStartedAt,
272056
272950
  cwd: this.config.cwd,
272057
272951
  skills: this.skills?.registry,
272058
272952
  cwdListing: context?.cwdListing,
@@ -272067,6 +272961,7 @@ var Agent$1 = class {
272067
272961
  profileName: profile.name,
272068
272962
  systemPrompt
272069
272963
  });
272964
+ this.profileSystemPromptText = systemPrompt;
272070
272965
  this.renderedPromptContributions = contributions === void 0 ? void 0 : {
272071
272966
  text: systemPrompt,
272072
272967
  value: structuredClone(contributions)
@@ -272243,6 +273138,9 @@ var Agent$1 = class {
272243
273138
  },
272244
273139
  startBtw: () => this.subagentHost.startBtw(),
272245
273140
  createGoal: (payload) => this.goal.createGoal(payload),
273141
+ editGoal: (payload) => this.goal.editGoal(payload),
273142
+ blockGoal: (payload) => this.goal.blockGoal(payload),
273143
+ completeGoal: () => this.goal.completeGoal(),
272246
273144
  getGoal: () => this.goal.getGoal(),
272247
273145
  pauseGoal: () => this.goal.pauseGoal(),
272248
273146
  resumeGoal: () => this.goal.resumeGoal(),
@@ -336497,6 +337395,15 @@ var SessionAPIImpl = class {
336497
337395
  async createGoal({ agentId, ...payload }) {
336498
337396
  return (await this.getAgent(agentId)).createGoal(payload);
336499
337397
  }
337398
+ async editGoal({ agentId, ...payload }) {
337399
+ return (await this.getAgent(agentId)).editGoal(payload);
337400
+ }
337401
+ async blockGoal({ agentId, ...payload }) {
337402
+ return (await this.getAgent(agentId)).blockGoal(payload);
337403
+ }
337404
+ async completeGoal({ agentId }) {
337405
+ return (await this.getAgent(agentId)).completeGoal({});
337406
+ }
336500
337407
  async getGoal({ agentId, ...payload }) {
336501
337408
  return (await this.getAgent(agentId, true)).getGoal(payload);
336502
337409
  }
@@ -337741,6 +338648,15 @@ var BlunCore = class {
337741
338648
  getGoal({ sessionId, ...payload }) {
337742
338649
  return Promise.resolve(this.sessionApi(sessionId).getGoal(payload));
337743
338650
  }
338651
+ editGoal({ sessionId, ...payload }) {
338652
+ return Promise.resolve(this.sessionApi(sessionId).editGoal(payload));
338653
+ }
338654
+ blockGoal({ sessionId, ...payload }) {
338655
+ return Promise.resolve(this.sessionApi(sessionId).blockGoal(payload));
338656
+ }
338657
+ completeGoal({ sessionId, ...payload }) {
338658
+ return Promise.resolve(this.sessionApi(sessionId).completeGoal(payload));
338659
+ }
337744
338660
  pauseGoal({ sessionId, ...payload }) {
337745
338661
  return Promise.resolve(this.sessionApi(sessionId).pauseGoal(payload));
337746
338662
  }
@@ -344942,7 +345858,10 @@ function reduceWireRecords(records) {
344942
345858
  text: record.summary
344943
345859
  }],
344944
345860
  toolCalls: [],
344945
- origin: { kind: "compaction_summary" }
345861
+ origin: {
345862
+ kind: "compaction_summary",
345863
+ ...record.toolContinuity === void 0 ? {} : { toolContinuity: record.toolContinuity }
345864
+ }
344946
345865
  },
344947
345866
  time: record.time
344948
345867
  };
@@ -347193,6 +348112,24 @@ var Session = class {
347193
348112
  this.ensureOpen();
347194
348113
  return this.rpc.getGoal({ sessionId: this.id });
347195
348114
  }
348115
+ async editGoal(input) {
348116
+ this.ensureOpen();
348117
+ return this.rpc.editGoal({
348118
+ sessionId: this.id,
348119
+ objective: input.objective
348120
+ });
348121
+ }
348122
+ async blockGoal(input = {}) {
348123
+ this.ensureOpen();
348124
+ return this.rpc.blockGoal({
348125
+ sessionId: this.id,
348126
+ reason: input.reason
348127
+ });
348128
+ }
348129
+ async completeGoal() {
348130
+ this.ensureOpen();
348131
+ return this.rpc.completeGoal({ sessionId: this.id });
348132
+ }
347196
348133
  async pauseGoal() {
347197
348134
  this.ensureOpen();
347198
348135
  return this.rpc.pauseGoal({ sessionId: this.id });
@@ -347752,6 +348689,39 @@ var BlunAuthFacade = class {
347752
348689
  }
347753
348690
  return this.toolkit.status(providerName, this.resolveRuntimeManagedAuth(providerName).oauthRef);
347754
348691
  }
348692
+ async getCachedMemoryWorkspaceId() {
348693
+ const apiKey = nonEmpty(this.resolveManagedAuth(BLUN_PROVIDER_NAME$1).apiKey);
348694
+ if (apiKey !== void 0) {
348695
+ const provider = readConfigFileForUpdate(this.options.configPath).providers[BLUN_PROVIDER_NAME$1];
348696
+ if (provider?.apiKey !== apiKey || provider.memoryApiKeyHash !== memoryApiKeyHash(apiKey)) return;
348697
+ return provider.memoryWorkspaceId;
348698
+ }
348699
+ return this.toolkit.getCachedMemoryWorkspaceId(this.resolveRuntimeManagedAuth(BLUN_PROVIDER_NAME$1).oauthRef);
348700
+ }
348701
+ async cacheMemoryWorkspaceId(workspaceId) {
348702
+ const apiKey = nonEmpty(this.resolveManagedAuth(BLUN_PROVIDER_NAME$1).apiKey);
348703
+ if (apiKey !== void 0) {
348704
+ const hash = memoryApiKeyHash(apiKey);
348705
+ return mutateConfigFile(this.options.configPath, (config) => {
348706
+ const provider = config.providers[BLUN_PROVIDER_NAME$1];
348707
+ if (provider?.apiKey !== apiKey) return {
348708
+ config,
348709
+ result: false
348710
+ };
348711
+ if (provider.memoryApiKeyHash === hash && provider.memoryWorkspaceId !== void 0 && provider.memoryWorkspaceId !== workspaceId) return {
348712
+ config,
348713
+ result: false
348714
+ };
348715
+ provider.memoryApiKeyHash = hash;
348716
+ provider.memoryWorkspaceId = workspaceId;
348717
+ return {
348718
+ config,
348719
+ result: true
348720
+ };
348721
+ });
348722
+ }
348723
+ return this.toolkit.cacheMemoryWorkspaceId(workspaceId, this.resolveRuntimeManagedAuth(BLUN_PROVIDER_NAME$1).oauthRef);
348724
+ }
347755
348725
  async login(providerName = BLUN_PROVIDER_NAME$1, options = {}) {
347756
348726
  const auth = this.resolveManagedAuth(providerName);
347757
348727
  const loginAuth = resolveBlunCodeLoginAuth({
@@ -348057,6 +349027,9 @@ function nonEmpty(value) {
348057
349027
  const normalized = value?.trim();
348058
349028
  return normalized === void 0 || normalized.length === 0 ? void 0 : normalized;
348059
349029
  }
349030
+ function memoryApiKeyHash(apiKey) {
349031
+ return createHash("sha256").update(apiKey).digest("hex");
349032
+ }
348060
349033
  function transitionAggregateError(message, primary, secondary) {
348061
349034
  return new AggregateError([primary, secondary], message, { cause: primary });
348062
349035
  }
@@ -348537,6 +349510,26 @@ var SDKRpcClientBase = class {
348537
349510
  replace: input.replace
348538
349511
  });
348539
349512
  }
349513
+ async editGoal(input) {
349514
+ return (await this.getRpc()).editGoal({
349515
+ sessionId: input.sessionId,
349516
+ agentId: this.interactiveAgentId,
349517
+ objective: input.objective
349518
+ });
349519
+ }
349520
+ async blockGoal(input) {
349521
+ return (await this.getRpc()).blockGoal({
349522
+ sessionId: input.sessionId,
349523
+ agentId: this.interactiveAgentId,
349524
+ reason: input.reason
349525
+ });
349526
+ }
349527
+ async completeGoal(input) {
349528
+ return (await this.getRpc()).completeGoal({
349529
+ sessionId: input.sessionId,
349530
+ agentId: this.interactiveAgentId
349531
+ });
349532
+ }
348540
349533
  async getGoal(input) {
348541
349534
  return (await this.getRpc()).getGoal({
348542
349535
  sessionId: input.sessionId,