blun-king-cli 9.1.613 → 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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:5c7277a864ffcc0a6909494ad5ada6956f900fe23735200c5aca26b1e7d78834
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);
@@ -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";
@@ -2687,12 +2687,22 @@ function collectBlunVisionAttachments(messages) {
2687
2687
  }
2688
2688
  return attachments;
2689
2689
  }
2690
- function estimateBlunRequestExtraBytes(tools, messages) {
2691
- const attachments = collectBlunVisionAttachments(messages);
2692
- return {
2693
- toolsBytes: tools.length === 0 ? 0 : Buffer.byteLength(JSON.stringify({ tools: tools.map(convertTool) }), "utf8"),
2694
- visionBytes: attachments.length === 0 ? 0 : Buffer.byteLength(JSON.stringify({ visionAttachments: attachments }), "utf8")
2695
- };
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");
2696
2706
  }
2697
2707
  function convertMessage(message) {
2698
2708
  let reasoningContent = "";
@@ -21766,6 +21776,31 @@ var InstructionText = class InstructionText {
21766
21776
  get content() {
21767
21777
  return this.pieces.map((piece) => piece.text).join("");
21768
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
+ }
21769
21804
  replaceFirst(needle, replacement) {
21770
21805
  const start = this.content.indexOf(needle);
21771
21806
  if (start >= 0) this.replaceRange(start, start + needle.length, replacement);
@@ -21823,6 +21858,64 @@ const IMPORT_MAX_DEPTH = 4;
21823
21858
  const IMPORT_LINE_RE = /^@import\s+(.+)$/gm;
21824
21859
  const S_IFMT$4 = 61440;
21825
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
+ }
21826
21919
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21827
21920
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
21828
21921
  const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
@@ -21835,7 +21928,8 @@ async function prepareSystemPromptContext(kaos, brandHome, options) {
21835
21928
  agentsMd: agentsMdResult.content,
21836
21929
  additionalDirsInfo,
21837
21930
  agentsMdWarning: agentsMdResult.warning,
21838
- agentsMdSources: agentsMdResult.sources
21931
+ agentsMdSources: agentsMdResult.sources,
21932
+ agentsMdText: agentsMdResult.text
21839
21933
  };
21840
21934
  }
21841
21935
  async function loadAgentsMd(kaos, brandHome) {
@@ -21889,6 +21983,7 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
21889
21983
  return {
21890
21984
  content,
21891
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,
21986
+ text: rendered,
21892
21987
  sources: {
21893
21988
  ...rendered.measure(),
21894
21989
  ...notLoadedCandidates.size === 0 ? {} : { notLoadedCandidates: [...notLoadedCandidates] }
@@ -180764,6 +180859,10 @@ var GoalMode = class {
180764
180859
  restoreUpdate(record) {
180765
180860
  const state = this.state;
180766
180861
  if (state === void 0) return;
180862
+ if (record.objective !== void 0) {
180863
+ state.objective = record.objective;
180864
+ this.lifecycleRevision += 1;
180865
+ }
180767
180866
  const status = record.status;
180768
180867
  if (status !== void 0) {
180769
180868
  this.lifecycleRevision += 1;
@@ -180778,7 +180877,17 @@ var GoalMode = class {
180778
180877
  state.wallClockResumedAt = void 0;
180779
180878
  }
180780
180879
  if (record.budgetLimits !== void 0) state.budgetLimits = record.budgetLimits;
180781
- 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
+ }
180782
180891
  this.agent.replayBuilder.push({
180783
180892
  type: "goal_updated",
180784
180893
  snapshot: this.toSnapshot(state),
@@ -180822,11 +180931,11 @@ var GoalMode = class {
180822
180931
  if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
180823
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`);
180824
180933
  const verificationScope = normalizeVerificationScope(input.verificationScope);
180934
+ const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180825
180935
  if (this.state !== void 0) {
180826
180936
  if (input.replace !== true) throw new BlunError(ErrorCodes.GOAL_ALREADY_EXISTS, "A goal already exists; use replace to start a new one");
180827
180937
  this.clearInternal("system");
180828
180938
  }
180829
- const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
180830
180939
  const state = {
180831
180940
  goalId: randomUUID(),
180832
180941
  objective,
@@ -180850,6 +180959,35 @@ var GoalMode = class {
180850
180959
  this.trackGoalCreated(actor, input.replace === true);
180851
180960
  return this.toSnapshot(state);
180852
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
+ }
180853
180991
  async pauseGoal(input = {}, actor = "user") {
180854
180992
  const state = this.requireState();
180855
180993
  if (state.status === "paused") return this.toSnapshot(state);
@@ -181619,6 +181757,613 @@ var ProviderRetryWait = class {
181619
181757
  }
181620
181758
  }
181621
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
+ }
181622
182367
  //#endregion
181623
182368
  //#region ../../packages/agent-core/src/agent/records/resume-snapshot.ts
181624
182369
  const integer$1 = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
@@ -181713,6 +182458,7 @@ const stateRecordSchema = union([
181713
182458
  verificationScope: _enum(GOAL_VERIFICATION_SCOPES).optional()
181714
182459
  }),
181715
182460
  record$1("goal.update", {
182461
+ objective: text.min(1).max(4e3).regex(/\S/).optional(),
181716
182462
  status: _enum([
181717
182463
  "active",
181718
182464
  "paused",
@@ -181764,7 +182510,7 @@ const originCase = (kind, shape = {}) => object({
181764
182510
  }).passthrough();
181765
182511
  const origin = union([
181766
182512
  originCase("user"),
181767
- originCase("compaction_summary"),
182513
+ originCase("compaction_summary", { toolContinuity: custom$1(isToolContinuity).optional() }),
181768
182514
  originCase("skill_activation", {
181769
182515
  activationId: text,
181770
182516
  skillName: text,
@@ -182115,6 +182861,7 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182115
182861
  if (Buffer.byteLength(bytes) > MAX_CHECKPOINT_BYTES) return false;
182116
182862
  const target = resumeCheckpointPath(wirePath);
182117
182863
  const temporary = `${target}.tmp-${process.pid}-${randomBytes(8).toString("hex")}`;
182864
+ let renamed = false;
182118
182865
  try {
182119
182866
  const output = await open(temporary, "wx", 384);
182120
182867
  try {
@@ -182124,10 +182871,11 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182124
182871
  await output.close();
182125
182872
  }
182126
182873
  await rename(temporary, target);
182874
+ renamed = true;
182127
182875
  await syncDir(dirname$3(target));
182128
182876
  return true;
182129
182877
  } finally {
182130
- await rm(temporary, { force: true }).catch(() => {});
182878
+ if (!renamed) await rm(temporary, { force: true }).catch(() => {});
182131
182879
  }
182132
182880
  }
182133
182881
  //#endregion
@@ -185464,501 +186212,6 @@ var TodoListTool = class {
185464
186212
  })));
185465
186213
  }
185466
186214
  };
185467
- const IMAGE_MIME_BY_SUFFIX = Object.freeze({
185468
- ".png": "image/png",
185469
- ".jpg": "image/jpeg",
185470
- ".jpeg": "image/jpeg",
185471
- ".gif": "image/gif",
185472
- ".bmp": "image/bmp",
185473
- ".tif": "image/tiff",
185474
- ".tiff": "image/tiff",
185475
- ".webp": "image/webp",
185476
- ".ico": "image/x-icon",
185477
- ".heic": "image/heic",
185478
- ".heif": "image/heif",
185479
- ".avif": "image/avif",
185480
- ".svgz": "image/svg+xml"
185481
- });
185482
- const VIDEO_MIME_BY_SUFFIX = Object.freeze({
185483
- ".mp4": "video/mp4",
185484
- ".mpg": "video/mpeg",
185485
- ".mpeg": "video/mpeg",
185486
- ".mkv": "video/x-matroska",
185487
- ".avi": "video/x-msvideo",
185488
- ".mov": "video/quicktime",
185489
- ".ogv": "video/ogg",
185490
- ".wmv": "video/x-ms-wmv",
185491
- ".webm": "video/webm",
185492
- ".m4v": "video/x-m4v",
185493
- ".flv": "video/x-flv",
185494
- ".3gp": "video/3gpp",
185495
- ".3g2": "video/3gpp2"
185496
- });
185497
- const TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
185498
- const NON_TEXT_SUFFIXES = new Set([
185499
- ".icns",
185500
- ".psd",
185501
- ".ai",
185502
- ".eps",
185503
- ".pdf",
185504
- ".doc",
185505
- ".docx",
185506
- ".dot",
185507
- ".dotx",
185508
- ".rtf",
185509
- ".odt",
185510
- ".xls",
185511
- ".xlsx",
185512
- ".xlsm",
185513
- ".xlt",
185514
- ".xltx",
185515
- ".xltm",
185516
- ".ods",
185517
- ".ppt",
185518
- ".pptx",
185519
- ".pptm",
185520
- ".pps",
185521
- ".ppsx",
185522
- ".odp",
185523
- ".pages",
185524
- ".numbers",
185525
- ".key",
185526
- ".zip",
185527
- ".rar",
185528
- ".7z",
185529
- ".tar",
185530
- ".gz",
185531
- ".tgz",
185532
- ".bz2",
185533
- ".xz",
185534
- ".zst",
185535
- ".lz",
185536
- ".lz4",
185537
- ".br",
185538
- ".cab",
185539
- ".ar",
185540
- ".deb",
185541
- ".rpm",
185542
- ".mp3",
185543
- ".wav",
185544
- ".flac",
185545
- ".ogg",
185546
- ".oga",
185547
- ".opus",
185548
- ".aac",
185549
- ".m4a",
185550
- ".wma",
185551
- ".ttf",
185552
- ".otf",
185553
- ".woff",
185554
- ".woff2",
185555
- ".exe",
185556
- ".dll",
185557
- ".so",
185558
- ".dylib",
185559
- ".bin",
185560
- ".apk",
185561
- ".ipa",
185562
- ".jar",
185563
- ".class",
185564
- ".pyc",
185565
- ".pyo",
185566
- ".wasm",
185567
- ".dmg",
185568
- ".iso",
185569
- ".img",
185570
- ".sqlite",
185571
- ".sqlite3",
185572
- ".db",
185573
- ".db3"
185574
- ]);
185575
- const ASF_HEADER = Buffer.from([
185576
- 48,
185577
- 38,
185578
- 178,
185579
- 117,
185580
- 142,
185581
- 102,
185582
- 207,
185583
- 17,
185584
- 166,
185585
- 217,
185586
- 0,
185587
- 170,
185588
- 0,
185589
- 98,
185590
- 206,
185591
- 108
185592
- ]);
185593
- const FTYP_IMAGE_BRANDS = Object.freeze({
185594
- avif: "image/avif",
185595
- avis: "image/avif",
185596
- heic: "image/heic",
185597
- heif: "image/heif",
185598
- heix: "image/heif",
185599
- hevc: "image/heic",
185600
- mif1: "image/heif",
185601
- msf1: "image/heif"
185602
- });
185603
- const FTYP_VIDEO_BRANDS = Object.freeze({
185604
- isom: "video/mp4",
185605
- iso2: "video/mp4",
185606
- iso5: "video/mp4",
185607
- mp41: "video/mp4",
185608
- mp42: "video/mp4",
185609
- avc1: "video/mp4",
185610
- mp4v: "video/mp4",
185611
- m4v: "video/x-m4v",
185612
- qt: "video/quicktime",
185613
- "3gp4": "video/3gpp",
185614
- "3gp5": "video/3gpp",
185615
- "3gp6": "video/3gpp",
185616
- "3gp7": "video/3gpp",
185617
- "3g2": "video/3gpp2"
185618
- });
185619
- function toBuffer(data) {
185620
- return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
185621
- }
185622
- function startsWith(buf, prefix) {
185623
- const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
185624
- if (buf.length < needle.length) return false;
185625
- for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
185626
- return true;
185627
- }
185628
- function sniffFtypBrand(header) {
185629
- if (header.length < 12) return null;
185630
- if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
185631
- return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
185632
- }
185633
- function sniffMediaFromMagic(data) {
185634
- const buf = toBuffer(data);
185635
- const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
185636
- if (startsWith(header, [
185637
- 137,
185638
- 80,
185639
- 78,
185640
- 71,
185641
- 13,
185642
- 10,
185643
- 26,
185644
- 10
185645
- ])) return {
185646
- kind: "image",
185647
- mimeType: "image/png"
185648
- };
185649
- if (startsWith(header, [
185650
- 255,
185651
- 216,
185652
- 255
185653
- ])) return {
185654
- kind: "image",
185655
- mimeType: "image/jpeg"
185656
- };
185657
- if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
185658
- kind: "image",
185659
- mimeType: "image/gif"
185660
- };
185661
- if (startsWith(header, Buffer.from("BM"))) return {
185662
- kind: "image",
185663
- mimeType: "image/bmp"
185664
- };
185665
- if (startsWith(header, [
185666
- 73,
185667
- 73,
185668
- 42,
185669
- 0
185670
- ]) || startsWith(header, [
185671
- 77,
185672
- 77,
185673
- 0,
185674
- 42
185675
- ])) return {
185676
- kind: "image",
185677
- mimeType: "image/tiff"
185678
- };
185679
- if (startsWith(header, [
185680
- 0,
185681
- 0,
185682
- 1,
185683
- 0
185684
- ])) return {
185685
- kind: "image",
185686
- mimeType: "image/x-icon"
185687
- };
185688
- if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
185689
- const chunk = header.subarray(8, 12).toString("latin1");
185690
- if (chunk === "WEBP") return {
185691
- kind: "image",
185692
- mimeType: "image/webp"
185693
- };
185694
- if (chunk === "AVI ") return {
185695
- kind: "video",
185696
- mimeType: "video/x-msvideo"
185697
- };
185698
- }
185699
- if (startsWith(header, Buffer.from("FLV"))) return {
185700
- kind: "video",
185701
- mimeType: "video/x-flv"
185702
- };
185703
- if (startsWith(header, ASF_HEADER)) return {
185704
- kind: "video",
185705
- mimeType: "video/x-ms-wmv"
185706
- };
185707
- if (startsWith(header, [
185708
- 26,
185709
- 69,
185710
- 223,
185711
- 163
185712
- ])) {
185713
- const lowered = header.toString("latin1").toLowerCase();
185714
- if (lowered.includes("webm")) return {
185715
- kind: "video",
185716
- mimeType: "video/webm"
185717
- };
185718
- if (lowered.includes("matroska")) return {
185719
- kind: "video",
185720
- mimeType: "video/x-matroska"
185721
- };
185722
- }
185723
- const brand = sniffFtypBrand(header);
185724
- if (brand !== null && brand !== "") {
185725
- if (brand in FTYP_IMAGE_BRANDS) return {
185726
- kind: "image",
185727
- mimeType: FTYP_IMAGE_BRANDS[brand]
185728
- };
185729
- if (brand in FTYP_VIDEO_BRANDS) return {
185730
- kind: "video",
185731
- mimeType: FTYP_VIDEO_BRANDS[brand]
185732
- };
185733
- }
185734
- return null;
185735
- }
185736
- /**
185737
- * Best-effort pixel-dimension reader for common raster formats.
185738
- *
185739
- * Inspects only the fixed region near the start of the file where each
185740
- * format records its dimensions (the IHDR/DIB header, the RIFF chunk
185741
- * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
185742
- * for formats whose dimensions are not locatable from that region, or
185743
- * when the supplied buffer is too short to cover it.
185744
- */
185745
- function sniffImageDimensions(data) {
185746
- const buf = toBuffer(data);
185747
- if (startsWith(buf, [
185748
- 137,
185749
- 80,
185750
- 78,
185751
- 71,
185752
- 13,
185753
- 10,
185754
- 26,
185755
- 10
185756
- ]) && buf.length >= 24) return {
185757
- width: buf.readUInt32BE(16),
185758
- height: buf.readUInt32BE(20)
185759
- };
185760
- if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
185761
- width: buf.readUInt16LE(6),
185762
- height: buf.readUInt16LE(8)
185763
- };
185764
- if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
185765
- width: buf.readInt32LE(18),
185766
- height: Math.abs(buf.readInt32LE(22))
185767
- };
185768
- if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
185769
- const fourCc = buf.subarray(12, 16).toString("latin1");
185770
- if (fourCc === "VP8 ") return {
185771
- width: buf.readUInt16LE(26) & 16383,
185772
- height: buf.readUInt16LE(28) & 16383
185773
- };
185774
- if (fourCc === "VP8L" && buf.length >= 25) {
185775
- const bits = buf.readUInt32LE(21);
185776
- return {
185777
- width: (bits & 16383) + 1,
185778
- height: (bits >> 14 & 16383) + 1
185779
- };
185780
- }
185781
- if (fourCc === "VP8X") return {
185782
- width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
185783
- height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
185784
- };
185785
- }
185786
- if (startsWith(buf, [255, 216])) {
185787
- let offset = 2;
185788
- while (offset + 9 < buf.length) {
185789
- if (buf[offset] !== 255) {
185790
- offset += 1;
185791
- continue;
185792
- }
185793
- const marker = buf[offset + 1];
185794
- if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
185795
- height: buf.readUInt16BE(offset + 5),
185796
- width: buf.readUInt16BE(offset + 7)
185797
- };
185798
- if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
185799
- offset += 2;
185800
- continue;
185801
- }
185802
- const segmentLength = buf.readUInt16BE(offset + 2);
185803
- if (segmentLength < 2) break;
185804
- offset += 2 + segmentLength;
185805
- }
185806
- }
185807
- return null;
185808
- }
185809
- function getSuffix(path) {
185810
- const idx = path.lastIndexOf(".");
185811
- if (idx === -1) return "";
185812
- if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
185813
- return path.slice(idx).toLowerCase();
185814
- }
185815
- function detectFileType(path, header, type = "text") {
185816
- const suffix = getSuffix(path);
185817
- let mediaHint = null;
185818
- if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
185819
- kind: "text",
185820
- mimeType: TEXT_MIME_BY_SUFFIX[suffix]
185821
- };
185822
- else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
185823
- kind: "image",
185824
- mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
185825
- };
185826
- else if (suffix in VIDEO_MIME_BY_SUFFIX) mediaHint = {
185827
- kind: "video",
185828
- mimeType: VIDEO_MIME_BY_SUFFIX[suffix]
185829
- };
185830
- if (header !== void 0) {
185831
- const buf = toBuffer(header);
185832
- const sniffed = sniffMediaFromMagic(buf);
185833
- if (sniffed) {
185834
- if (type === "media") return sniffed;
185835
- if (mediaHint) {
185836
- if (sniffed.kind !== mediaHint.kind) return {
185837
- kind: "unknown",
185838
- mimeType: ""
185839
- };
185840
- return mediaHint;
185841
- }
185842
- return sniffed;
185843
- }
185844
- if (mediaHint?.kind === "image") return {
185845
- kind: "unknown",
185846
- mimeType: ""
185847
- };
185848
- if (type === "media" && mediaHint?.kind === "video") return mediaHint;
185849
- if (buf.includes(0)) return {
185850
- kind: "unknown",
185851
- mimeType: ""
185852
- };
185853
- }
185854
- if (mediaHint) return mediaHint;
185855
- if (NON_TEXT_SUFFIXES.has(suffix)) return {
185856
- kind: "unknown",
185857
- mimeType: ""
185858
- };
185859
- return {
185860
- kind: "text",
185861
- mimeType: "text/plain"
185862
- };
185863
- }
185864
- //#endregion
185865
- //#region ../../packages/agent-core/src/utils/tokens.ts
185866
- const messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
185867
- /** Open assistant messages grow while streaming; completed history stays cached. */
185868
- function invalidateMessageTokenEstimate(message) {
185869
- messageTokenEstimateCache.delete(message);
185870
- }
185871
- /**
185872
- * Estimate token count from text using a character-based heuristic.
185873
- * - ASCII (~4 chars per token)
185874
- * - CJK and other non-ASCII (~1 char per token)
185875
- * The estimate is transient — the next LLM call returns the real count
185876
- * and supersedes this value. Used to keep `tokenCountWithPending`
185877
- * monotonic between LLM round-trips without paying for a tokenizer.
185878
- */
185879
- function estimateTokens(text) {
185880
- let asciiCount = 0;
185881
- let nonAsciiCount = 0;
185882
- for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
185883
- else nonAsciiCount++;
185884
- return Math.ceil(asciiCount / 4) + nonAsciiCount;
185885
- }
185886
- function estimateTokensForMessages(messages) {
185887
- let total = 0;
185888
- for (const message of messages) total += estimateTokensForMessage(message);
185889
- return total;
185890
- }
185891
- function estimateTokensForTools(tools) {
185892
- let total = 0;
185893
- for (const tool of tools) {
185894
- total += estimateTokens(tool.name);
185895
- total += estimateTokens(tool.description);
185896
- total += estimateTokens(JSON.stringify(tool.parameters));
185897
- }
185898
- return total;
185899
- }
185900
- function estimateTokensForMessage(message) {
185901
- const cached = messageTokenEstimateCache.get(message);
185902
- if (cached !== void 0) return cached;
185903
- let total = estimateTokens(message.role);
185904
- total += estimateTokensForContentParts(message.content);
185905
- if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
185906
- total += estimateTokens(call.name);
185907
- total += estimateTokens(JSON.stringify(call.arguments));
185908
- }
185909
- messageTokenEstimateCache.set(message, total);
185910
- return total;
185911
- }
185912
- function estimateTokensForContentParts(parts) {
185913
- let total = 0;
185914
- for (const part of parts) total += estimateTokensForContentPart(part);
185915
- return total;
185916
- }
185917
- /**
185918
- * Conservative transient fallback for media whose pixel dimensions or duration
185919
- * are unavailable. Inline raster images use their transmitted dimensions below;
185920
- * audio, video, remote images, and malformed image data keep this safe floor.
185921
- * The next successful LLM round-trip replaces the estimate with reported usage.
185922
- */
185923
- const MEDIA_TOKEN_ESTIMATE = 2e3;
185924
- /** Enough header data for common JPEG metadata followed by the first SOF marker. */
185925
- const MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
185926
- function imageDimensionsFromDataUrl(url) {
185927
- const commaIndex = url.indexOf(",");
185928
- if (commaIndex <= 0) return null;
185929
- const metadata = url.slice(0, commaIndex).toLowerCase();
185930
- if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
185931
- const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
185932
- const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
185933
- if (encodedHeader.length === 0) return null;
185934
- try {
185935
- const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
185936
- if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
185937
- return dimensions;
185938
- } catch {
185939
- return null;
185940
- }
185941
- }
185942
- function estimateImageTokensFromDimensions(width, height) {
185943
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
185944
- const patchSize = BigInt(32);
185945
- const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
185946
- return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
185947
- }
185948
- function estimateImageTokens(url) {
185949
- const dimensions = imageDimensionsFromDataUrl(url);
185950
- return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
185951
- }
185952
- function estimateTokensForContentPart(part) {
185953
- switch (part.type) {
185954
- case "text": return estimateTokens(part.text);
185955
- case "think": return estimateTokens(part.think);
185956
- case "image_url": return estimateImageTokens(part.imageUrl.url);
185957
- case "audio_url":
185958
- case "video_url": return MEDIA_TOKEN_ESTIMATE;
185959
- default: return 0;
185960
- }
185961
- }
185962
186215
  //#endregion
185963
186216
  //#region ../../node_modules/.pnpm/bmp-ts@1.0.9/node_modules/bmp-ts/dist/esm/header-types.js
185964
186217
  var HeaderTypes, header_types_default;
@@ -230214,7 +230467,7 @@ function buildCompactionSummaryText(summary) {
230214
230467
  const MAX_SESSION_COMPACTION_FAILURES = 2;
230215
230468
  const DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS = 128 * 1024;
230216
230469
  const COMPACTION_SUMMARY_RESERVE_RATIO = .1;
230217
- const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 75e4;
230470
+ const BLUN_COMPACTION_REQUEST_BUDGET_BYTES = 48e4;
230218
230471
  const MAX_HIERARCHICAL_COMPACTION_PASSES = 64;
230219
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:";
230220
230473
  var CompactionTruncatedError = class extends Error {
@@ -230374,26 +230627,10 @@ var FullCompaction = class {
230374
230627
  const model = this.agent.activeResponderModel ?? this.agent.config.modelAlias;
230375
230628
  if (model === void 0 || this.agent.config.runtimeModel(model).provider.name !== "blun") return void 0;
230376
230629
  const systemPrompt = context.systemPrompt ?? this.agent.effectiveSystemPrompt;
230377
- const tools = (context.tools ?? this.agent.tools.loopTools).map((tool) => ({
230378
- name: tool.name,
230379
- description: tool.description,
230380
- parameters: tool.parameters
230381
- }));
230382
- let extraBytes;
230383
- try {
230384
- extraBytes = estimateBlunRequestExtraBytes(tools, messages);
230385
- } catch {
230386
- return;
230387
- }
230388
- if (Buffer.byteLength(JSON.stringify({
230389
- systemPrompt,
230390
- messages: []
230391
- }), "utf8") >= BLUN_COMPACTION_REQUEST_BUDGET_BYTES) return void 0;
230630
+ const tools = context.tools ?? this.agent.tools.loopTools;
230631
+ if (estimateBlunSerializedBodyBytes(systemPrompt, [], []) + 4096 >= BLUN_COMPACTION_REQUEST_BUDGET_BYTES) return void 0;
230392
230632
  return {
230393
- usedRequestBytesEstimate: Buffer.byteLength(JSON.stringify({
230394
- systemPrompt,
230395
- messages
230396
- }), "utf8") + extraBytes.toolsBytes + extraBytes.visionBytes,
230633
+ usedRequestBytesEstimate: estimateBlunSerializedBodyBytes(systemPrompt, tools, messages) + 4096,
230397
230634
  thresholdRequestBytes: BLUN_COMPACTION_REQUEST_BUDGET_BYTES
230398
230635
  };
230399
230636
  }
@@ -230748,10 +230985,7 @@ var FullCompaction = class {
230748
230985
  dropOrphanResults: true
230749
230986
  }), createUserMessage(requestInstruction)], capability));
230750
230987
  };
230751
- const estimateRequestBytes = (messages) => Buffer.byteLength(JSON.stringify({
230752
- systemPrompt: this.agent.effectiveSystemPrompt,
230753
- messages
230754
- }), "utf8");
230988
+ const estimateRequestBytes = (messages) => estimateBlunSerializedBodyBytes(this.agent.effectiveSystemPrompt, compactionTools, messages) + 4096;
230755
230989
  while (true) {
230756
230990
  signal.throwIfAborted();
230757
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: {
@@ -234331,8 +234565,9 @@ function makeSyntheticToolResult(toolCallId) {
234331
234565
  }
234332
234566
  function mergeAdjacentUserMessages(history, onAnomaly) {
234333
234567
  const out = [];
234334
- for (const source of history) {
234335
- 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);
234336
234571
  if (message === null) continue;
234337
234572
  const previous = out.at(-1);
234338
234573
  if (canMergeUserMessage(message) && previous !== void 0 && canMergeUserMessage(previous)) {
@@ -234343,7 +234578,7 @@ function mergeAdjacentUserMessages(history, onAnomaly) {
234343
234578
  }
234344
234579
  return out.map(stripContextMetadata);
234345
234580
  }
234346
- function prepareMessageForProjection(message, onAnomaly) {
234581
+ function prepareMessageForProjection(message, onAnomaly, historical = false) {
234347
234582
  if (message.partial === true) return null;
234348
234583
  let content;
234349
234584
  for (const [index, part] of message.content.entries()) {
@@ -234355,6 +234590,19 @@ function prepareMessageForProjection(message, onAnomaly) {
234355
234590
  });
234356
234591
  continue;
234357
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
+ }
234358
234606
  content?.push(part);
234359
234607
  }
234360
234608
  const next = content === void 0 ? message : {
@@ -234364,6 +234612,14 @@ function prepareMessageForProjection(message, onAnomaly) {
234364
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 } });
234365
234613
  return next.content.length === 0 && next.toolCalls.length === 0 ? null : next;
234366
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
+ }
234367
234623
  function canMergeUserMessage(message) {
234368
234624
  return message.role === "user" && message.origin?.kind === "user";
234369
234625
  }
@@ -234428,6 +234684,8 @@ function checkedRemovalIndexes(indexes, length) {
234428
234684
  }
234429
234685
  var ContextMemory = class {
234430
234686
  agent;
234687
+ mutationGeneration = randomUUID();
234688
+ mutationRevision = 0;
234431
234689
  _history = [];
234432
234690
  _tokenCount = 0;
234433
234691
  tokenCountCoveredMessageCount = 0;
@@ -234439,6 +234697,15 @@ var ContextMemory = class {
234439
234697
  constructor(agent) {
234440
234698
  this.agent = agent;
234441
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
+ }
234442
234709
  get lastAssistantAt() {
234443
234710
  return this._lastAssistantAt;
234444
234711
  }
@@ -234592,6 +234859,7 @@ var ContextMemory = class {
234592
234859
  return removedMessages.size;
234593
234860
  }
234594
234861
  clear() {
234862
+ const changed = this._history.length > 0;
234595
234863
  this.agent.records.logRecord({ type: "context.clear" });
234596
234864
  this._history = [];
234597
234865
  this._tokenCount = 0;
@@ -234603,6 +234871,7 @@ var ContextMemory = class {
234603
234871
  this.agent.microCompaction.reset();
234604
234872
  this.agent.injection.onContextClear();
234605
234873
  this.agent.emitStatusUpdated();
234874
+ if (changed) this.publishMutation("clear");
234606
234875
  }
234607
234876
  undo(count) {
234608
234877
  if (count <= 0) return;
@@ -234640,6 +234909,7 @@ var ContextMemory = class {
234640
234909
  this.deferredMessages = [];
234641
234910
  this.agent.microCompaction.reset(this._history.length);
234642
234911
  this.agent.emitStatusUpdated();
234912
+ if (removedMessages.size > 0) this.publishMutation("undo");
234643
234913
  if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
234644
234914
  reason: "undo_limit",
234645
234915
  requestedCount: count,
@@ -234648,6 +234918,7 @@ var ContextMemory = class {
234648
234918
  } });
234649
234919
  }
234650
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");
234651
234922
  let preservedToolTailCount = input.preservedToolTailCount;
234652
234923
  if (this.agent.records.restoring === null && this.hasOpenToolExchange()) {
234653
234924
  const start = this._history.findIndex((message) => message.role === "assistant" && message.toolCalls.some((call) => this.pendingToolResultIds.has(call.id)));
@@ -234684,25 +234955,75 @@ var ContextMemory = class {
234684
234955
  elisionMessage,
234685
234956
  ...selection.tail
234686
234957
  ];
234687
- const contextSummary = input.contextSummary ?? input.summary;
234688
- const summaryMessage = {
234958
+ let contextSummary = input.contextSummary ?? input.summary;
234959
+ let toolContinuity = this.agent.records.restoring === null ? void 0 : input.toolContinuity;
234960
+ let summaryMessage = {
234689
234961
  role: "user",
234690
234962
  content: [{
234691
234963
  type: "text",
234692
234964
  text: contextSummary
234693
234965
  }],
234694
234966
  toolCalls: [],
234695
- origin: { kind: "compaction_summary" }
234967
+ origin: {
234968
+ kind: "compaction_summary",
234969
+ ...toolContinuity === void 0 ? {} : { toolContinuity }
234970
+ }
234696
234971
  };
234697
- const replacement = [
234972
+ let replacement = [
234698
234973
  ...keptMessages,
234699
234974
  summaryMessage,
234700
234975
  ...preservedToolTail
234701
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
+ }
234702
235024
  if (options.requireReduction && this.agent.records.restoring === null) {
234703
- const previousHistoryTokens = estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true }));
234704
235025
  const nextHistoryTokens = estimateTokensForMessages(project(replacement, { dropOrphanResults: true }));
234705
- 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: {
234706
235027
  reason: "non_reducing_summary",
234707
235028
  contextUnchanged: true,
234708
235029
  previousHistoryTokens,
@@ -234715,6 +235036,7 @@ var ContextMemory = class {
234715
235036
  const result = {
234716
235037
  summary: input.summary,
234717
235038
  contextSummary,
235039
+ ...toolContinuity === void 0 ? {} : { toolContinuity },
234718
235040
  compactedCount: input.compactedCount,
234719
235041
  tokensBefore: input.tokensBefore,
234720
235042
  tokensAfter,
@@ -234731,6 +235053,7 @@ var ContextMemory = class {
234731
235053
  this.agent.replayBuilder.patchLast("compaction", { result: {
234732
235054
  summary: result.summary,
234733
235055
  contextSummary: result.contextSummary,
235056
+ ...result.toolContinuity === void 0 ? {} : { toolContinuity: result.toolContinuity },
234734
235057
  compactedCount: result.compactedCount,
234735
235058
  tokensBefore: result.tokensBefore,
234736
235059
  tokensAfter: result.tokensAfter,
@@ -236338,20 +236661,75 @@ function fingerprintTitle(title) {
236338
236661
  return createHash("sha256").update(title.trim().toLowerCase()).digest("hex");
236339
236662
  }
236340
236663
  async function loadRegister(mistakeDir = defaultMistakeDir()) {
236664
+ const target = join(mistakeDir, REGISTER_FILENAME);
236341
236665
  try {
236342
236666
  await mkdir(mistakeDir, { recursive: true });
236343
- const content = await readFile(join(mistakeDir, REGISTER_FILENAME), "utf-8");
236344
- return JSON.parse(content);
236345
- } catch {
236346
- 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 {
236347
236672
  version: 1,
236348
236673
  entries: []
236349
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");
236350
236695
  }
236351
236696
  }
236352
236697
  async function saveRegister(register, mistakeDir = defaultMistakeDir()) {
236353
- await mkdir(mistakeDir, { recursive: true });
236354
- 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}.`);
236355
236733
  }
236356
236734
  /**
236357
236735
  * Findet einen Register-Eintrag anhand des Titels (Fingerabdruck).
@@ -239044,6 +239422,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239044
239422
  const DEFERRED_TOOL_LOADER_NAME = "ToolSearch";
239045
239423
  const MAX_PERSISTENT_DEFERRED_TOOLS = 12;
239046
239424
  const MAX_TOOL_SEARCH_QUERY_CHARS = 1e3;
239425
+ const MAX_TOOL_CATALOG_RESULT_BYTES = 4096;
239047
239426
  const MAX_AUTO_RANKED_TOOLS = 6;
239048
239427
  const deferredToolLoaders = /* @__PURE__ */ new WeakSet();
239049
239428
  const TOOL_RANK_EMBEDDED_CONTENT_RE = /<(attached_documents?|attachment_content|document_content|file_content)\b[^>]*>[\s\S]*?<\/\1>/giu;
@@ -239288,6 +239667,27 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239288
239667
  for (const leaf of leaves) leafCounts.set(leaf, (leafCounts.get(leaf) || 0) + 1);
239289
239668
  return names.map((name, index) => leafCounts.get(leaves[index]) === 1 ? leaves[index] : name).sort((left, right) => left.localeCompare(right));
239290
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
+ }
239291
239691
  function normalizeDeferredToolQuery(value) {
239292
239692
  const query = String(value ?? "").trim();
239293
239693
  if (query.length <= MAX_TOOL_SEARCH_QUERY_CHARS) return query;
@@ -239362,14 +239762,14 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239362
239762
  function rankingTokens(value) {
239363
239763
  return [...new Set(String(value || "").normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]{3,}/gu) || [])].filter((token) => !TOOL_RANK_STOP_WORDS.has(token));
239364
239764
  }
239365
- function toolRankingDocument(tool) {
239765
+ function toolRankingDocument(tool, trustedToolNames) {
239366
239766
  const name = String(tool?.name || "");
239367
239767
  const leaf = name.split(/__|:/).at(-1) || name;
239368
239768
  const splitToolName = (value) => String(value || "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ");
239369
239769
  const nameTokens = rankingTokens(`${splitToolName(name)} ${splitToolName(leaf)}`);
239370
239770
  const descriptionTokens = rankingTokens(tool?.description);
239371
239771
  const parameterText = [];
239372
- const properties = tool?.parameters?.properties;
239772
+ const properties = /^mcp__/iu.test(name) && !trustedToolNames?.has(name) ? void 0 : tool?.parameters?.properties;
239373
239773
  if (properties && typeof properties === "object") for (const [parameterName, definition] of Object.entries(properties)) parameterText.push(parameterName, definition?.description || "");
239374
239774
  const exampleText = Array.isArray(tool?.examples) ? tool.examples.map((example) => example?.prompt || "").join(" ") : "";
239375
239775
  return {
@@ -239401,7 +239801,7 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239401
239801
  const queryTokens = [...new Set(rankingIntentTexts(value).flatMap(rankingTokens))];
239402
239802
  if (queryTokens.length === 0) return /* @__PURE__ */ new Set();
239403
239803
  const ranked = (Array.isArray(tools) ? tools : []).map((tool) => {
239404
- const document = toolRankingDocument(tool);
239804
+ const document = toolRankingDocument(tool, options.trustedToolNames);
239405
239805
  if (!document.name) return null;
239406
239806
  let score = 0;
239407
239807
  let matchedTokens = 0;
@@ -239509,8 +239909,8 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239509
239909
  name: loaderName,
239510
239910
  description: [
239511
239911
  `Search ${deferredCount} deferred ${deferredCount === 1 ? "tool schema" : "tool schemas"} without loading all definitions.`,
239512
- "Use select:leaf_name for one known tool, plain keywords to search, or +word to require that word in the tool name.",
239513
- `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.`
239514
239914
  ].join("\n"),
239515
239915
  parameters: {
239516
239916
  type: "object",
@@ -239527,7 +239927,12 @@ var import_turn_tool_performance_policy = (/* @__PURE__ */ __commonJSMin(((expor
239527
239927
  return {
239528
239928
  description: query ? `Searching tool schemas for ${query}` : "Searching tool schemas",
239529
239929
  approvalRule: loaderName,
239530
- 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
+ };
239531
239936
  let matches = searchDeferredTools([...available.values()], query);
239532
239937
  let relatedFallback = false;
239533
239938
  if (matches.length === 0) {
@@ -251393,7 +251798,11 @@ const goalChangeStatsSchema = object({
251393
251798
  wallClockMs: number$1()
251394
251799
  });
251395
251800
  const goalChangeSchema = object({
251396
- kind: _enum(["lifecycle", "completion"]),
251801
+ kind: _enum([
251802
+ "lifecycle",
251803
+ "completion",
251804
+ "edited"
251805
+ ]),
251397
251806
  status: goalStatusSchema.optional(),
251398
251807
  reason: string().optional(),
251399
251808
  stats: goalChangeStatsSchema.optional(),
@@ -251544,6 +251953,12 @@ const turnEndReasonSchema = _enum([
251544
251953
  "failed",
251545
251954
  "filtered"
251546
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
+ });
251547
251962
  const agentStatusUpdatedEventSchema = object({
251548
251963
  type: literal("agent.status.updated"),
251549
251964
  model: string().optional(),
@@ -251863,6 +252278,7 @@ const eventSchema = discriminatedUnion("type", [
251863
252278
  errorEventSchema,
251864
252279
  warningEventSchema,
251865
252280
  agentStatusUpdatedEventSchema,
252281
+ contextChangedEventSchema,
251866
252282
  sessionMetaUpdatedEventSchema,
251867
252283
  sessionCreatedEventSchema,
251868
252284
  workspaceCreatedEventSchema,
@@ -265263,9 +265679,16 @@ var ToolCallDeduplicator = class {
265263
265679
  //#region ../../packages/agent-core/src/agent/turn/tool-result-budget.ts
265264
265680
  const TOOL_RESULT_MAX_CHARS = 5e4;
265265
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
+ }
265266
265688
  async function budgetToolResultForModel(options) {
265267
265689
  const text = persistableToolResultText(options.result.output);
265268
- 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;
265269
265692
  if (options.result.truncated === true) return options.result;
265270
265693
  if (options.homedir === void 0) return options.result;
265271
265694
  if (text.includes("\0") || Buffer.from(text, "utf8").toString("utf8") !== text || text.split("\n").some((line) => line.length > 2e3)) return options.result;
@@ -265276,7 +265699,7 @@ async function budgetToolResultForModel(options) {
265276
265699
  toolCallId: options.toolCallId
265277
265700
  }, text);
265278
265701
  if (outputPath === void 0) return options.result;
265279
- const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath);
265702
+ const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath, maxChars);
265280
265703
  return options.result.isError === true ? {
265281
265704
  ...options.result,
265282
265705
  output,
@@ -265308,9 +265731,9 @@ async function saveToolResult(options, text) {
265308
265731
  return;
265309
265732
  }
265310
265733
  }
265311
- function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
265734
+ function renderPersistedToolResult(toolName, toolCallId, text, outputPath, maxChars) {
265312
265735
  const lines = [
265313
- `Tool output exceeded ${String(TOOL_RESULT_MAX_CHARS)} characters; showing a preview only.`,
265736
+ `Tool output exceeded ${String(maxChars)} characters; showing a preview only.`,
265314
265737
  `tool_name: ${toolName}`,
265315
265738
  `tool_call_id: ${toolCallId}`,
265316
265739
  `output_size_chars: ${String(text.length)}`,
@@ -267276,14 +267699,27 @@ function blunToolsForOrigin(tools, origin) {
267276
267699
  * the ordinary permission pipeline.
267277
267700
  */
267278
267701
  function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PURE__ */ new Set(), requiredToolNames = /* @__PURE__ */ new Set(), selected = []) {
267279
- 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)) {
267280
267704
  selected.splice(0, selected.length, ...tools);
267281
267705
  return selected;
267282
267706
  }
267283
- 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)));
267284
- const names = new Set(selected.map((tool) => tool.name));
267285
- const deferred = tools.filter((tool) => !names.has(tool.name));
267286
- 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
+ }
267287
267723
  return selected;
267288
267724
  }
267289
267725
  const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
@@ -268044,10 +268480,12 @@ var TurnFlow = class {
268044
268480
  taskToolsNeeded ||= blunTurnNeedsTools(intent, origin) || blunTurnHasAttachment(toolInput);
268045
268481
  const required = (0, import_turn_tool_performance_policy.mediaToolNamesForTurnText)(blunExtractText(toolInput));
268046
268482
  if (taskToolsNeeded) {
268047
- blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames, required, selectedTools);
268048
- 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 })) {
268049
268487
  const tool = eligibleTools.find((candidate) => candidate.name === name);
268050
- 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);
268051
268489
  }
268052
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)));
268053
268491
  recordPreRequestPhase("tool-selection", selectionStartedAt);
@@ -268188,6 +268626,7 @@ var TurnFlow = class {
268188
268626
  });
268189
268627
  return budgetToolResultForModel({
268190
268628
  homedir: this.agent.homedir,
268629
+ maxContextTokens: this.agent.fullCompaction.getEffectiveMaxContextTokens(),
268191
268630
  toolName: ctx.toolCall.name,
268192
268631
  toolCallId: ctx.toolCall.id,
268193
268632
  result: finalResult
@@ -269045,10 +269484,10 @@ var BashTool = class {
269045
269484
  },
269046
269485
  approvalRule: literalRulePattern(this.name, args.command),
269047
269486
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
269048
- execute: ({ signal, onUpdate, onForegroundTaskStart }) => this.execution(args, signal, onUpdate, onForegroundTaskStart)
269487
+ execute: ({ signal, onUpdate, onForegroundTaskStart, executionOrigin }) => this.execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin)
269049
269488
  };
269050
269489
  }
269051
- spawn(effectiveCwd, command) {
269490
+ spawn(effectiveCwd, command, executionOrigin) {
269052
269491
  const shellCwd = this.isWindowsBash ? windowsPathToPosixPath(effectiveCwd) : effectiveCwd;
269053
269492
  const shellArgs = [
269054
269493
  this.kaos.osEnv.shellPath,
@@ -269065,9 +269504,11 @@ var BashTool = class {
269065
269504
  ...process.env,
269066
269505
  ...noninteractiveEnv
269067
269506
  };
269507
+ delete mergedEnv["BLUN_SHELL"];
269508
+ if (executionOrigin === "operator_shell") mergedEnv["BLUN_SHELL"] = "operator-local";
269068
269509
  return this.kaos.execWithEnv(shellArgs, mergedEnv);
269069
269510
  }
269070
- async execution(args, signal, onUpdate, onForegroundTaskStart) {
269511
+ async execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin) {
269071
269512
  const validationError = this.validateRunRequest(args, signal);
269072
269513
  if (validationError !== void 0) return validationError;
269073
269514
  const startsInBackground = args.run_in_background === true;
@@ -269079,7 +269520,7 @@ var BashTool = class {
269079
269520
  const builder = new ToolResultBuilder();
269080
269521
  let proc;
269081
269522
  try {
269082
- proc = await this.spawn(effectiveCwd, command);
269523
+ proc = await this.spawn(effectiveCwd, command, executionOrigin);
269083
269524
  } catch (error) {
269084
269525
  return {
269085
269526
  isError: true,
@@ -269443,14 +269884,14 @@ function classifySearchError(error) {
269443
269884
  }
269444
269885
  //#endregion
269445
269886
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.md?raw
269446
- 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";
269447
269888
  //#endregion
269448
269889
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.ts
269449
269890
  /**
269450
269891
  * CodebaseSearchTool — semantic code search via the codebase-index.
269451
269892
  *
269452
269893
  * Shells out to `python codebase_index.py query <repo> "<frage>" --top N`
269453
- * through spawnSync. The script lives at
269894
+ * through asynchronous execFile. The script lives at
269454
269895
  * `packages/codebase-index/codebase_index.py` relative to the workspace root.
269455
269896
  *
269456
269897
  * Unlike Grep (keyword/regex) or Glob (file-name patterns), this tool finds
@@ -269458,11 +269899,13 @@ var codebase_search_default = "Find code by **meaning**, not by keywords.\n\nThi
269458
269899
  * to the query, so it can locate a keyboard handler even when the word
269459
269900
  * "keyboard" never appears in the source.
269460
269901
  */
269902
+ const MAX_SEARCH_RESULTS = 100;
269461
269903
  const CodebaseSearchInputSchema = object({
269462
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\"."),
269463
- 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.`)
269464
269906
  });
269465
269907
  const SCRIPT_TIMEOUT_MS = 6e4;
269908
+ const SCRIPT_MAX_BUFFER = 1024 * 1024;
269466
269909
  function isScriptFile(path) {
269467
269910
  try {
269468
269911
  return statSync(path).isFile();
@@ -269501,45 +269944,72 @@ var CodebaseSearchTool = class {
269501
269944
  },
269502
269945
  approvalRule: literalRulePattern(this.name, args.query),
269503
269946
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.query),
269504
- execute: () => this.execution(args)
269947
+ execute: async (context) => {
269948
+ context.signal.throwIfAborted();
269949
+ return this.execution(args, context.signal);
269950
+ }
269505
269951
  };
269506
269952
  }
269507
- execution(args) {
269953
+ async execution(args, signal) {
269508
269954
  const builder = new ToolResultBuilder();
269509
- 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.");
269510
269957
  try {
269511
- const result = spawnSync("python", [
269512
- this.scriptPath,
269513
- "query",
269514
- this.cwd,
269515
- args.query,
269516
- "--top",
269517
- String(args.top),
269518
- "--model",
269519
- "auto"
269520
- ], {
269521
- cwd: this.cwd,
269522
- timeout: SCRIPT_TIMEOUT_MS,
269523
- encoding: "utf-8",
269524
- 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
+ });
269525
269992
  });
269526
- if (result.error !== void 0) {
269993
+ signal.throwIfAborted();
269994
+ if (result.error !== null && typeof result.error.code !== "number") {
269527
269995
  const msg = result.error instanceof Error ? result.error.message : String(result.error);
269528
- 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}`);
269529
269998
  }
269530
269999
  const stdout = result.stdout ?? "";
269531
270000
  const stderr = result.stderr ?? "";
269532
- const exitCode = result.status ?? -1;
270001
+ const exitCode = result.error?.code ?? 0;
269533
270002
  if (exitCode !== 0) {
269534
270003
  const combined = stderr.trim().length > 0 ? `${stdout}${stdout.length > 0 ? "\n" : ""}Error: ${stderr.trim()}` : stdout;
269535
270004
  builder.write(combined.length > 0 ? combined : `codebase_index.py exited with code ${String(exitCode)}`);
269536
- 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)}.`);
269537
270006
  }
269538
270007
  builder.write(stdout.length > 0 ? stdout : "No results found.");
269539
- return Promise.resolve(builder.ok());
270008
+ return builder.ok();
269540
270009
  } catch (error) {
270010
+ signal.throwIfAborted();
269541
270011
  const msg = error instanceof Error ? error.message : String(error);
269542
- return Promise.resolve(builder.error(`Codebase search error: ${msg}`));
270012
+ return builder.error(`Codebase search error: ${msg}`);
269543
270013
  }
269544
270014
  }
269545
270015
  };
@@ -270779,6 +271249,7 @@ var ToolManager = class {
270779
271249
  const result = await execution.execute({
270780
271250
  turnId: "",
270781
271251
  toolCallId: "shell-command",
271252
+ executionOrigin: "operator_shell",
270782
271253
  signal: controller.signal,
270783
271254
  onUpdate: (update) => {
270784
271255
  if (update.kind === "stdout") stdout += update.text ?? "";
@@ -270867,6 +271338,9 @@ var ToolManager = class {
270867
271338
  hasUserTool(name) {
270868
271339
  return this.userTools.has(name);
270869
271340
  }
271341
+ isUserTool(tool) {
271342
+ return this.userTools.get(tool.name) === tool;
271343
+ }
270870
271344
  inheritUserTools(parent) {
270871
271345
  for (const tool of parent.userTools.values()) {
270872
271346
  if (!parent.enabledTools.has(tool.name)) continue;
@@ -272112,6 +272586,9 @@ var Agent$1 = class {
272112
272586
  additionalDirs;
272113
272587
  systemPromptStartedAt = /* @__PURE__ */ new Date();
272114
272588
  activeProfile;
272589
+ rawSystemPromptContext;
272590
+ profileSystemPromptText;
272591
+ agentsMdRequestWarningShown = false;
272115
272592
  brandHome;
272116
272593
  _activeResponderModel;
272117
272594
  allowModelFallback = true;
@@ -272266,6 +272743,7 @@ var Agent$1 = class {
272266
272743
  tools: requestTools,
272267
272744
  messages: await rebuildMessages()
272268
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));
272269
272747
  let systemPrompt = this.effectiveSystemPrompt;
272270
272748
  const preflightStartedAt = performance.now();
272271
272749
  const compacted = await this.fullCompaction.beforeStep(params.signal, {
@@ -272294,6 +272772,13 @@ var Agent$1 = class {
272294
272772
  ...params,
272295
272773
  messages: await rebuildMessages()
272296
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
+ }
272297
272782
  return {
272298
272783
  params: {
272299
272784
  ...params,
@@ -272372,8 +272857,14 @@ var Agent$1 = class {
272372
272857
  }
272373
272858
  useProfile(profile, context, brandHome) {
272374
272859
  this.setActiveProfile(profile, brandHome);
272375
- 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
+ }
272376
272866
  this.tools.setActiveTools(profile.tools);
272867
+ this.updateSystemPromptFromProfile(profile, this.fitProfileInstructions(profile, context));
272377
272868
  }
272378
272869
  setActiveProfile(profile, brandHome) {
272379
272870
  this.activeProfile = profile;
@@ -272388,7 +272879,68 @@ var Agent$1 = class {
272388
272879
  async refreshSystemPrompt() {
272389
272880
  if (this.activeProfile === void 0) return;
272390
272881
  const context = this.systemPromptContextProvider === void 0 ? await prepareSystemPromptContext(this.kaos, this.brandHome, { additionalDirs: this.additionalDirs }) : await this.systemPromptContextProvider();
272391
- 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;
272392
272944
  }
272393
272945
  updateSystemPromptFromProfile(profile, context) {
272394
272946
  let contributions;
@@ -272409,6 +272961,7 @@ var Agent$1 = class {
272409
272961
  profileName: profile.name,
272410
272962
  systemPrompt
272411
272963
  });
272964
+ this.profileSystemPromptText = systemPrompt;
272412
272965
  this.renderedPromptContributions = contributions === void 0 ? void 0 : {
272413
272966
  text: systemPrompt,
272414
272967
  value: structuredClone(contributions)
@@ -272585,6 +273138,9 @@ var Agent$1 = class {
272585
273138
  },
272586
273139
  startBtw: () => this.subagentHost.startBtw(),
272587
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(),
272588
273144
  getGoal: () => this.goal.getGoal(),
272589
273145
  pauseGoal: () => this.goal.pauseGoal(),
272590
273146
  resumeGoal: () => this.goal.resumeGoal(),
@@ -336839,6 +337395,15 @@ var SessionAPIImpl = class {
336839
337395
  async createGoal({ agentId, ...payload }) {
336840
337396
  return (await this.getAgent(agentId)).createGoal(payload);
336841
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
+ }
336842
337407
  async getGoal({ agentId, ...payload }) {
336843
337408
  return (await this.getAgent(agentId, true)).getGoal(payload);
336844
337409
  }
@@ -338083,6 +338648,15 @@ var BlunCore = class {
338083
338648
  getGoal({ sessionId, ...payload }) {
338084
338649
  return Promise.resolve(this.sessionApi(sessionId).getGoal(payload));
338085
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
+ }
338086
338660
  pauseGoal({ sessionId, ...payload }) {
338087
338661
  return Promise.resolve(this.sessionApi(sessionId).pauseGoal(payload));
338088
338662
  }
@@ -345284,7 +345858,10 @@ function reduceWireRecords(records) {
345284
345858
  text: record.summary
345285
345859
  }],
345286
345860
  toolCalls: [],
345287
- origin: { kind: "compaction_summary" }
345861
+ origin: {
345862
+ kind: "compaction_summary",
345863
+ ...record.toolContinuity === void 0 ? {} : { toolContinuity: record.toolContinuity }
345864
+ }
345288
345865
  },
345289
345866
  time: record.time
345290
345867
  };
@@ -347535,6 +348112,24 @@ var Session = class {
347535
348112
  this.ensureOpen();
347536
348113
  return this.rpc.getGoal({ sessionId: this.id });
347537
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
+ }
347538
348133
  async pauseGoal() {
347539
348134
  this.ensureOpen();
347540
348135
  return this.rpc.pauseGoal({ sessionId: this.id });
@@ -348915,6 +349510,26 @@ var SDKRpcClientBase = class {
348915
349510
  replace: input.replace
348916
349511
  });
348917
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
+ }
348918
349533
  async getGoal(input) {
348919
349534
  return (await this.getRpc()).getGoal({
348920
349535
  sessionId: input.sessionId,