blun-king-cli 9.1.614 → 9.1.615

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/blun.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:8cd22e70289533171a13424a0ac5703315b0efc0c63f381d08987a054e6e2f00
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);
@@ -181492,6 +181492,10 @@ var init_goal$1 = __esmMin((() => {
181492
181492
  restoreUpdate(record) {
181493
181493
  const state = this.state;
181494
181494
  if (state === void 0) return;
181495
+ if (record.objective !== void 0) {
181496
+ state.objective = record.objective;
181497
+ this.lifecycleRevision += 1;
181498
+ }
181495
181499
  const status = record.status;
181496
181500
  if (status !== void 0) {
181497
181501
  this.lifecycleRevision += 1;
@@ -181506,7 +181510,17 @@ var init_goal$1 = __esmMin((() => {
181506
181510
  state.wallClockResumedAt = void 0;
181507
181511
  }
181508
181512
  if (record.budgetLimits !== void 0) state.budgetLimits = record.budgetLimits;
181509
- if (status === void 0) return;
181513
+ if (status === void 0) {
181514
+ if (record.objective !== void 0) this.agent.replayBuilder.push({
181515
+ type: "goal_updated",
181516
+ snapshot: this.toSnapshot(state),
181517
+ change: {
181518
+ kind: "edited",
181519
+ actor: record.actor
181520
+ }
181521
+ });
181522
+ return;
181523
+ }
181510
181524
  this.agent.replayBuilder.push({
181511
181525
  type: "goal_updated",
181512
181526
  snapshot: this.toSnapshot(state),
@@ -181550,11 +181564,11 @@ var init_goal$1 = __esmMin((() => {
181550
181564
  if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
181551
181565
  if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH$3) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH$3} characters`);
181552
181566
  const verificationScope = normalizeVerificationScope(input.verificationScope);
181567
+ const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
181553
181568
  if (this.state !== void 0) {
181554
181569
  if (input.replace !== true) throw new BlunError(ErrorCodes.GOAL_ALREADY_EXISTS, "A goal already exists; use replace to start a new one");
181555
181570
  this.clearInternal("system");
181556
181571
  }
181557
- const completionCriterion = normalizeCompletionCriterion(input.completionCriterion);
181558
181572
  const state = {
181559
181573
  goalId: randomUUID(),
181560
181574
  objective,
@@ -181578,6 +181592,35 @@ var init_goal$1 = __esmMin((() => {
181578
181592
  this.trackGoalCreated(actor, input.replace === true);
181579
181593
  return this.toSnapshot(state);
181580
181594
  }
181595
+ async completeGoal() {
181596
+ const state = this.requireState();
181597
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot complete a goal in status "${state.status}"`);
181598
+ return this.markComplete({}, "user");
181599
+ }
181600
+ async blockGoal(input = {}) {
181601
+ const state = this.requireState();
181602
+ if (state.status === "blocked") return this.toSnapshot(state);
181603
+ if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot block a goal in status "${state.status}"`);
181604
+ return await this.markBlocked(input, "user");
181605
+ }
181606
+ async editGoal(input, actor = "user") {
181607
+ const state = this.requireState();
181608
+ const objective = input.objective.trim();
181609
+ if (objective.length === 0) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_EMPTY, "Goal objective cannot be empty");
181610
+ if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH$3) throw new BlunError(ErrorCodes.GOAL_OBJECTIVE_TOO_LONG, `Goal objective cannot exceed ${MAX_GOAL_OBJECTIVE_LENGTH$3} characters`);
181611
+ if (objective === state.objective) return this.toSnapshot(state);
181612
+ state.objective = objective;
181613
+ this.lifecycleRevision += 1;
181614
+ this.appendGoalUpdate({
181615
+ objective,
181616
+ actor
181617
+ });
181618
+ this.persistState(state, { change: {
181619
+ kind: "edited",
181620
+ actor
181621
+ } });
181622
+ return this.toSnapshot(state);
181623
+ }
181581
181624
  async pauseGoal(input = {}, actor = "user") {
181582
181625
  const state = this.requireState();
181583
181626
  if (state.status === "paused") return this.toSnapshot(state);
@@ -182320,6 +182363,621 @@ var init_provider_retry_wait = __esmMin((() => {
182320
182363
  };
182321
182364
  }));
182322
182365
  //#endregion
182366
+ //#region ../../packages/agent-core/src/tools/support/file-type.ts
182367
+ function toBuffer$2(data) {
182368
+ return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
182369
+ }
182370
+ function startsWith(buf, prefix) {
182371
+ const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
182372
+ if (buf.length < needle.length) return false;
182373
+ for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
182374
+ return true;
182375
+ }
182376
+ function sniffFtypBrand(header) {
182377
+ if (header.length < 12) return null;
182378
+ if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
182379
+ return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
182380
+ }
182381
+ function sniffMediaFromMagic(data) {
182382
+ const buf = toBuffer$2(data);
182383
+ const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
182384
+ if (startsWith(header, [
182385
+ 137,
182386
+ 80,
182387
+ 78,
182388
+ 71,
182389
+ 13,
182390
+ 10,
182391
+ 26,
182392
+ 10
182393
+ ])) return {
182394
+ kind: "image",
182395
+ mimeType: "image/png"
182396
+ };
182397
+ if (startsWith(header, [
182398
+ 255,
182399
+ 216,
182400
+ 255
182401
+ ])) return {
182402
+ kind: "image",
182403
+ mimeType: "image/jpeg"
182404
+ };
182405
+ if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
182406
+ kind: "image",
182407
+ mimeType: "image/gif"
182408
+ };
182409
+ if (startsWith(header, Buffer.from("BM"))) return {
182410
+ kind: "image",
182411
+ mimeType: "image/bmp"
182412
+ };
182413
+ if (startsWith(header, [
182414
+ 73,
182415
+ 73,
182416
+ 42,
182417
+ 0
182418
+ ]) || startsWith(header, [
182419
+ 77,
182420
+ 77,
182421
+ 0,
182422
+ 42
182423
+ ])) return {
182424
+ kind: "image",
182425
+ mimeType: "image/tiff"
182426
+ };
182427
+ if (startsWith(header, [
182428
+ 0,
182429
+ 0,
182430
+ 1,
182431
+ 0
182432
+ ])) return {
182433
+ kind: "image",
182434
+ mimeType: "image/x-icon"
182435
+ };
182436
+ if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
182437
+ const chunk = header.subarray(8, 12).toString("latin1");
182438
+ if (chunk === "WEBP") return {
182439
+ kind: "image",
182440
+ mimeType: "image/webp"
182441
+ };
182442
+ if (chunk === "AVI ") return {
182443
+ kind: "video",
182444
+ mimeType: "video/x-msvideo"
182445
+ };
182446
+ }
182447
+ if (startsWith(header, Buffer.from("FLV"))) return {
182448
+ kind: "video",
182449
+ mimeType: "video/x-flv"
182450
+ };
182451
+ if (startsWith(header, ASF_HEADER)) return {
182452
+ kind: "video",
182453
+ mimeType: "video/x-ms-wmv"
182454
+ };
182455
+ if (startsWith(header, [
182456
+ 26,
182457
+ 69,
182458
+ 223,
182459
+ 163
182460
+ ])) {
182461
+ const lowered = header.toString("latin1").toLowerCase();
182462
+ if (lowered.includes("webm")) return {
182463
+ kind: "video",
182464
+ mimeType: "video/webm"
182465
+ };
182466
+ if (lowered.includes("matroska")) return {
182467
+ kind: "video",
182468
+ mimeType: "video/x-matroska"
182469
+ };
182470
+ }
182471
+ const brand = sniffFtypBrand(header);
182472
+ if (brand !== null && brand !== "") {
182473
+ if (brand in FTYP_IMAGE_BRANDS) return {
182474
+ kind: "image",
182475
+ mimeType: FTYP_IMAGE_BRANDS[brand]
182476
+ };
182477
+ if (brand in FTYP_VIDEO_BRANDS) return {
182478
+ kind: "video",
182479
+ mimeType: FTYP_VIDEO_BRANDS[brand]
182480
+ };
182481
+ }
182482
+ return null;
182483
+ }
182484
+ /**
182485
+ * Best-effort pixel-dimension reader for common raster formats.
182486
+ *
182487
+ * Inspects only the fixed region near the start of the file where each
182488
+ * format records its dimensions (the IHDR/DIB header, the RIFF chunk
182489
+ * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
182490
+ * for formats whose dimensions are not locatable from that region, or
182491
+ * when the supplied buffer is too short to cover it.
182492
+ */
182493
+ function sniffImageDimensions(data) {
182494
+ const buf = toBuffer$2(data);
182495
+ if (startsWith(buf, [
182496
+ 137,
182497
+ 80,
182498
+ 78,
182499
+ 71,
182500
+ 13,
182501
+ 10,
182502
+ 26,
182503
+ 10
182504
+ ]) && buf.length >= 24) return {
182505
+ width: buf.readUInt32BE(16),
182506
+ height: buf.readUInt32BE(20)
182507
+ };
182508
+ if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
182509
+ width: buf.readUInt16LE(6),
182510
+ height: buf.readUInt16LE(8)
182511
+ };
182512
+ if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
182513
+ width: buf.readInt32LE(18),
182514
+ height: Math.abs(buf.readInt32LE(22))
182515
+ };
182516
+ if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
182517
+ const fourCc = buf.subarray(12, 16).toString("latin1");
182518
+ if (fourCc === "VP8 ") return {
182519
+ width: buf.readUInt16LE(26) & 16383,
182520
+ height: buf.readUInt16LE(28) & 16383
182521
+ };
182522
+ if (fourCc === "VP8L" && buf.length >= 25) {
182523
+ const bits = buf.readUInt32LE(21);
182524
+ return {
182525
+ width: (bits & 16383) + 1,
182526
+ height: (bits >> 14 & 16383) + 1
182527
+ };
182528
+ }
182529
+ if (fourCc === "VP8X") return {
182530
+ width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
182531
+ height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
182532
+ };
182533
+ }
182534
+ if (startsWith(buf, [255, 216])) {
182535
+ let offset = 2;
182536
+ while (offset + 9 < buf.length) {
182537
+ if (buf[offset] !== 255) {
182538
+ offset += 1;
182539
+ continue;
182540
+ }
182541
+ const marker = buf[offset + 1];
182542
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
182543
+ height: buf.readUInt16BE(offset + 5),
182544
+ width: buf.readUInt16BE(offset + 7)
182545
+ };
182546
+ if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
182547
+ offset += 2;
182548
+ continue;
182549
+ }
182550
+ const segmentLength = buf.readUInt16BE(offset + 2);
182551
+ if (segmentLength < 2) break;
182552
+ offset += 2 + segmentLength;
182553
+ }
182554
+ }
182555
+ return null;
182556
+ }
182557
+ function getSuffix(path) {
182558
+ const idx = path.lastIndexOf(".");
182559
+ if (idx === -1) return "";
182560
+ if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
182561
+ return path.slice(idx).toLowerCase();
182562
+ }
182563
+ function detectFileType(path, header, type = "text") {
182564
+ const suffix = getSuffix(path);
182565
+ let mediaHint = null;
182566
+ if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
182567
+ kind: "text",
182568
+ mimeType: TEXT_MIME_BY_SUFFIX[suffix]
182569
+ };
182570
+ else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
182571
+ kind: "image",
182572
+ mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
182573
+ };
182574
+ else if (suffix in VIDEO_MIME_BY_SUFFIX$1) mediaHint = {
182575
+ kind: "video",
182576
+ mimeType: VIDEO_MIME_BY_SUFFIX$1[suffix]
182577
+ };
182578
+ if (header !== void 0) {
182579
+ const buf = toBuffer$2(header);
182580
+ const sniffed = sniffMediaFromMagic(buf);
182581
+ if (sniffed) {
182582
+ if (type === "media") return sniffed;
182583
+ if (mediaHint) {
182584
+ if (sniffed.kind !== mediaHint.kind) return {
182585
+ kind: "unknown",
182586
+ mimeType: ""
182587
+ };
182588
+ return mediaHint;
182589
+ }
182590
+ return sniffed;
182591
+ }
182592
+ if (mediaHint?.kind === "image") return {
182593
+ kind: "unknown",
182594
+ mimeType: ""
182595
+ };
182596
+ if (type === "media" && mediaHint?.kind === "video") return mediaHint;
182597
+ if (buf.includes(0)) return {
182598
+ kind: "unknown",
182599
+ mimeType: ""
182600
+ };
182601
+ }
182602
+ if (mediaHint) return mediaHint;
182603
+ if (NON_TEXT_SUFFIXES.has(suffix)) return {
182604
+ kind: "unknown",
182605
+ mimeType: ""
182606
+ };
182607
+ return {
182608
+ kind: "text",
182609
+ mimeType: "text/plain"
182610
+ };
182611
+ }
182612
+ var IMAGE_MIME_BY_SUFFIX, VIDEO_MIME_BY_SUFFIX$1, TEXT_MIME_BY_SUFFIX, NON_TEXT_SUFFIXES, ASF_HEADER, FTYP_IMAGE_BRANDS, FTYP_VIDEO_BRANDS;
182613
+ var init_file_type$1 = __esmMin((() => {
182614
+ IMAGE_MIME_BY_SUFFIX = Object.freeze({
182615
+ ".png": "image/png",
182616
+ ".jpg": "image/jpeg",
182617
+ ".jpeg": "image/jpeg",
182618
+ ".gif": "image/gif",
182619
+ ".bmp": "image/bmp",
182620
+ ".tif": "image/tiff",
182621
+ ".tiff": "image/tiff",
182622
+ ".webp": "image/webp",
182623
+ ".ico": "image/x-icon",
182624
+ ".heic": "image/heic",
182625
+ ".heif": "image/heif",
182626
+ ".avif": "image/avif",
182627
+ ".svgz": "image/svg+xml"
182628
+ });
182629
+ VIDEO_MIME_BY_SUFFIX$1 = Object.freeze({
182630
+ ".mp4": "video/mp4",
182631
+ ".mpg": "video/mpeg",
182632
+ ".mpeg": "video/mpeg",
182633
+ ".mkv": "video/x-matroska",
182634
+ ".avi": "video/x-msvideo",
182635
+ ".mov": "video/quicktime",
182636
+ ".ogv": "video/ogg",
182637
+ ".wmv": "video/x-ms-wmv",
182638
+ ".webm": "video/webm",
182639
+ ".m4v": "video/x-m4v",
182640
+ ".flv": "video/x-flv",
182641
+ ".3gp": "video/3gpp",
182642
+ ".3g2": "video/3gpp2"
182643
+ });
182644
+ TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
182645
+ NON_TEXT_SUFFIXES = new Set([
182646
+ ".icns",
182647
+ ".psd",
182648
+ ".ai",
182649
+ ".eps",
182650
+ ".pdf",
182651
+ ".doc",
182652
+ ".docx",
182653
+ ".dot",
182654
+ ".dotx",
182655
+ ".rtf",
182656
+ ".odt",
182657
+ ".xls",
182658
+ ".xlsx",
182659
+ ".xlsm",
182660
+ ".xlt",
182661
+ ".xltx",
182662
+ ".xltm",
182663
+ ".ods",
182664
+ ".ppt",
182665
+ ".pptx",
182666
+ ".pptm",
182667
+ ".pps",
182668
+ ".ppsx",
182669
+ ".odp",
182670
+ ".pages",
182671
+ ".numbers",
182672
+ ".key",
182673
+ ".zip",
182674
+ ".rar",
182675
+ ".7z",
182676
+ ".tar",
182677
+ ".gz",
182678
+ ".tgz",
182679
+ ".bz2",
182680
+ ".xz",
182681
+ ".zst",
182682
+ ".lz",
182683
+ ".lz4",
182684
+ ".br",
182685
+ ".cab",
182686
+ ".ar",
182687
+ ".deb",
182688
+ ".rpm",
182689
+ ".mp3",
182690
+ ".wav",
182691
+ ".flac",
182692
+ ".ogg",
182693
+ ".oga",
182694
+ ".opus",
182695
+ ".aac",
182696
+ ".m4a",
182697
+ ".wma",
182698
+ ".ttf",
182699
+ ".otf",
182700
+ ".woff",
182701
+ ".woff2",
182702
+ ".exe",
182703
+ ".dll",
182704
+ ".so",
182705
+ ".dylib",
182706
+ ".bin",
182707
+ ".apk",
182708
+ ".ipa",
182709
+ ".jar",
182710
+ ".class",
182711
+ ".pyc",
182712
+ ".pyo",
182713
+ ".wasm",
182714
+ ".dmg",
182715
+ ".iso",
182716
+ ".img",
182717
+ ".sqlite",
182718
+ ".sqlite3",
182719
+ ".db",
182720
+ ".db3"
182721
+ ]);
182722
+ ASF_HEADER = Buffer.from([
182723
+ 48,
182724
+ 38,
182725
+ 178,
182726
+ 117,
182727
+ 142,
182728
+ 102,
182729
+ 207,
182730
+ 17,
182731
+ 166,
182732
+ 217,
182733
+ 0,
182734
+ 170,
182735
+ 0,
182736
+ 98,
182737
+ 206,
182738
+ 108
182739
+ ]);
182740
+ FTYP_IMAGE_BRANDS = Object.freeze({
182741
+ avif: "image/avif",
182742
+ avis: "image/avif",
182743
+ heic: "image/heic",
182744
+ heif: "image/heif",
182745
+ heix: "image/heif",
182746
+ hevc: "image/heic",
182747
+ mif1: "image/heif",
182748
+ msf1: "image/heif"
182749
+ });
182750
+ FTYP_VIDEO_BRANDS = Object.freeze({
182751
+ isom: "video/mp4",
182752
+ iso2: "video/mp4",
182753
+ iso5: "video/mp4",
182754
+ mp41: "video/mp4",
182755
+ mp42: "video/mp4",
182756
+ avc1: "video/mp4",
182757
+ mp4v: "video/mp4",
182758
+ m4v: "video/x-m4v",
182759
+ qt: "video/quicktime",
182760
+ "3gp4": "video/3gpp",
182761
+ "3gp5": "video/3gpp",
182762
+ "3gp6": "video/3gpp",
182763
+ "3gp7": "video/3gpp",
182764
+ "3g2": "video/3gpp2"
182765
+ });
182766
+ }));
182767
+ //#endregion
182768
+ //#region ../../packages/agent-core/src/utils/tokens.ts
182769
+ /** Open assistant messages grow while streaming; completed history stays cached. */
182770
+ function invalidateMessageTokenEstimate(message) {
182771
+ messageTokenEstimateCache.delete(message);
182772
+ }
182773
+ /**
182774
+ * Estimate token count from text using a character-based heuristic.
182775
+ * - ASCII (~4 chars per token)
182776
+ * - CJK and other non-ASCII (~1 char per token)
182777
+ * The estimate is transient — the next LLM call returns the real count
182778
+ * and supersedes this value. Used to keep `tokenCountWithPending`
182779
+ * monotonic between LLM round-trips without paying for a tokenizer.
182780
+ */
182781
+ function estimateTokens(text) {
182782
+ let asciiCount = 0;
182783
+ let nonAsciiCount = 0;
182784
+ for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
182785
+ else nonAsciiCount++;
182786
+ return Math.ceil(asciiCount / 4) + nonAsciiCount;
182787
+ }
182788
+ function estimateTokensForMessages(messages) {
182789
+ let total = 0;
182790
+ for (const message of messages) total += estimateTokensForMessage(message);
182791
+ return total;
182792
+ }
182793
+ /** With stopAfter, the result is a lower bound once it exceeds that budget. */
182794
+ function estimateTokensForTools(tools, stopAfter = Infinity) {
182795
+ let total = 0;
182796
+ for (const tool of tools) {
182797
+ total += estimateTokens(tool.name);
182798
+ total += estimateTokens(tool.description);
182799
+ total += estimateTokens(JSON.stringify(tool.parameters));
182800
+ if (total > stopAfter) return total;
182801
+ }
182802
+ return total;
182803
+ }
182804
+ function estimateTokensForMessage(message) {
182805
+ const cached = messageTokenEstimateCache.get(message);
182806
+ if (cached !== void 0) return cached;
182807
+ let total = estimateTokens(message.role);
182808
+ total += estimateTokensForContentParts(message.content);
182809
+ if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
182810
+ total += estimateTokens(call.name);
182811
+ total += estimateTokens(JSON.stringify(call.arguments));
182812
+ }
182813
+ messageTokenEstimateCache.set(message, total);
182814
+ return total;
182815
+ }
182816
+ function estimateTokensForContentParts(parts) {
182817
+ let total = 0;
182818
+ for (const part of parts) total += estimateTokensForContentPart(part);
182819
+ return total;
182820
+ }
182821
+ function imageDimensionsFromDataUrl(url) {
182822
+ const commaIndex = url.indexOf(",");
182823
+ if (commaIndex <= 0) return null;
182824
+ const metadata = url.slice(0, commaIndex).toLowerCase();
182825
+ if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
182826
+ const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
182827
+ const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
182828
+ if (encodedHeader.length === 0) return null;
182829
+ try {
182830
+ const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
182831
+ if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
182832
+ return dimensions;
182833
+ } catch {
182834
+ return null;
182835
+ }
182836
+ }
182837
+ function estimateImageTokensFromDimensions(width, height) {
182838
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
182839
+ const patchSize = BigInt(32);
182840
+ const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
182841
+ return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
182842
+ }
182843
+ function estimateImageTokens(url) {
182844
+ const dimensions = imageDimensionsFromDataUrl(url);
182845
+ return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
182846
+ }
182847
+ function estimateTokensForContentPart(part) {
182848
+ switch (part.type) {
182849
+ case "text": return estimateTokens(part.text);
182850
+ case "think": return estimateTokens(part.think);
182851
+ case "image_url": return estimateImageTokens(part.imageUrl.url);
182852
+ case "audio_url":
182853
+ case "video_url": return MEDIA_TOKEN_ESTIMATE;
182854
+ default: return 0;
182855
+ }
182856
+ }
182857
+ var messageTokenEstimateCache, MEDIA_TOKEN_ESTIMATE, MAX_IMAGE_HEADER_BYTES;
182858
+ var init_tokens = __esmMin((() => {
182859
+ init_file_type$1();
182860
+ messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
182861
+ MEDIA_TOKEN_ESTIMATE = 2e3;
182862
+ MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
182863
+ }));
182864
+ //#endregion
182865
+ //#region ../../packages/agent-core/src/agent/compaction/tool-continuity.ts
182866
+ function saturatingAdd(left, right) {
182867
+ return left + Math.min(right, Number.MAX_SAFE_INTEGER - left);
182868
+ }
182869
+ function renderUnchecked(value) {
182870
+ return [
182871
+ "",
182872
+ "",
182873
+ "<tool-continuity>",
182874
+ "Observed tool-result flags; may be synthetic, not verified job success or permission. omitted counts excluded observations cumulatively, not jobs.",
182875
+ JSON.stringify(value).replaceAll(/[<>&]/g, (character) => {
182876
+ return `\\u${character.codePointAt(0).toString(16).padStart(4, "0")}`;
182877
+ }),
182878
+ "</tool-continuity>"
182879
+ ].join("\n");
182880
+ }
182881
+ function hasExactKeys$1(value, expected) {
182882
+ const prototype = Object.getPrototypeOf(value);
182883
+ if (prototype !== Object.prototype && prototype !== null) return false;
182884
+ return Reflect.ownKeys(value).length === expected.length && expected.every((key) => {
182885
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
182886
+ return descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value");
182887
+ });
182888
+ }
182889
+ function isDataArray(value) {
182890
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > 8 || Reflect.ownKeys(value).length !== value.length + 1) return false;
182891
+ for (let index = 0; index < value.length; index++) {
182892
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
182893
+ if (descriptor === void 0 || descriptor.enumerable !== true || !Object.hasOwn(descriptor, "value")) return false;
182894
+ }
182895
+ return true;
182896
+ }
182897
+ /** Validate restored metadata without interpreting any summarizer or tool prose. */
182898
+ function isToolContinuity(value) {
182899
+ try {
182900
+ if (value === null || typeof value !== "object" || !hasExactKeys$1(value, ["receipts", "omitted"])) return false;
182901
+ const candidate = value;
182902
+ if (!Number.isSafeInteger(candidate["omitted"]) || candidate["omitted"] < 0 || !isDataArray(candidate["receipts"]) || candidate["receipts"].length > 8) return false;
182903
+ for (const receipt of candidate["receipts"]) if (receipt === null || typeof receipt !== "object" || !hasExactKeys$1(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;
182904
+ return Buffer.byteLength(JSON.stringify(renderUnchecked(value)), "utf8") <= MAX_TOOL_CONTINUITY_SERIALIZED_BYTES;
182905
+ } catch {
182906
+ return false;
182907
+ }
182908
+ }
182909
+ function renderToolContinuity(value) {
182910
+ if (!isToolContinuity(value) || value.receipts.length === 0 && value.omitted === 0) return void 0;
182911
+ return renderUnchecked(value);
182912
+ }
182913
+ /** The caller passes only the compactable prefix, excluding the preserved tool suffix. */
182914
+ function collectToolContinuity(messages) {
182915
+ const observed = /* @__PURE__ */ new Map();
182916
+ let ordinal = 0;
182917
+ let inheritedOmitted = 0;
182918
+ let excluded = 0;
182919
+ const remember = (toolCallId, observedIsError) => {
182920
+ if (toolCallId.length === 0 || Buffer.byteLength(toolCallId, "utf8") > 256) {
182921
+ excluded = saturatingAdd(excluded, 1);
182922
+ return;
182923
+ }
182924
+ const key = JSON.stringify([toolCallId, observedIsError]);
182925
+ observed.set(key, {
182926
+ toolCallId,
182927
+ observedIsError,
182928
+ ordinal: ordinal++
182929
+ });
182930
+ };
182931
+ for (const message of messages) if (message.role === "tool" && typeof message.toolCallId === "string") remember(message.toolCallId, typeof message.isError === "boolean" ? message.isError : null);
182932
+ else if (message.role === "user" && message.origin?.kind === "compaction_summary") {
182933
+ const previous = message.origin.toolContinuity;
182934
+ if (!isToolContinuity(previous)) continue;
182935
+ for (const receipt of previous.receipts.toReversed()) remember(receipt.toolCallId, receipt.observedIsError);
182936
+ inheritedOmitted = Math.max(inheritedOmitted, previous.omitted);
182937
+ }
182938
+ const prioritized = [...observed.values()].toSorted((left, right) => Number(right.observedIsError === true) - Number(left.observedIsError === true) || right.ordinal - left.ordinal);
182939
+ const receipts = prioritized.slice(0, 8).map(({ toolCallId, observedIsError }) => ({
182940
+ toolCallId,
182941
+ observedIsError
182942
+ }));
182943
+ let omitted = saturatingAdd(saturatingAdd(inheritedOmitted, excluded), prioritized.length - receipts.length);
182944
+ while (true) {
182945
+ const result = {
182946
+ receipts: [...receipts],
182947
+ omitted
182948
+ };
182949
+ if (renderToolContinuity(result) !== void 0) return result;
182950
+ if (receipts.length === 0) return void 0;
182951
+ receipts.pop();
182952
+ omitted = saturatingAdd(omitted, 1);
182953
+ }
182954
+ }
182955
+ /** Fit complete tuples and their disclaimer; never start a new failure or retry path. */
182956
+ function fitToolContinuity(value, additionalTokenBudget) {
182957
+ if (value === void 0 || !isToolContinuity(value) || !(additionalTokenBudget > 0)) return void 0;
182958
+ const receipts = [...value.receipts];
182959
+ let omitted = value.omitted;
182960
+ while (true) {
182961
+ const continuity = {
182962
+ receipts: [...receipts],
182963
+ omitted
182964
+ };
182965
+ const text = renderToolContinuity(continuity);
182966
+ if (text !== void 0 && estimateTokens(text) <= additionalTokenBudget) return {
182967
+ continuity,
182968
+ text
182969
+ };
182970
+ if (receipts.length === 0) return void 0;
182971
+ receipts.pop();
182972
+ omitted = saturatingAdd(omitted, 1);
182973
+ }
182974
+ }
182975
+ var MAX_TOOL_CONTINUITY_SERIALIZED_BYTES;
182976
+ var init_tool_continuity = __esmMin((() => {
182977
+ init_tokens();
182978
+ MAX_TOOL_CONTINUITY_SERIALIZED_BYTES = 4096;
182979
+ }));
182980
+ //#endregion
182323
182981
  //#region ../../packages/agent-core/src/agent/records/resume-snapshot.ts
182324
182982
  function resumeRecordKind(type) {
182325
182983
  if ([
@@ -182408,6 +183066,7 @@ var init_resume_snapshot = __esmMin((() => {
182408
183066
  init_schema();
182409
183067
  init_goal$1();
182410
183068
  init_provider_retry_wait();
183069
+ init_tool_continuity();
182411
183070
  init_migration();
182412
183071
  integer$1 = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
182413
183072
  finite = number$1().finite().nonnegative();
@@ -182501,6 +183160,7 @@ var init_resume_snapshot = __esmMin((() => {
182501
183160
  verificationScope: _enum(GOAL_VERIFICATION_SCOPES).optional()
182502
183161
  }),
182503
183162
  record$1("goal.update", {
183163
+ objective: text.min(1).max(4e3).regex(/\S/).optional(),
182504
183164
  status: _enum([
182505
183165
  "active",
182506
183166
  "paused",
@@ -182552,7 +183212,7 @@ var init_resume_snapshot = __esmMin((() => {
182552
183212
  }).passthrough();
182553
183213
  origin = union([
182554
183214
  originCase("user"),
182555
- originCase("compaction_summary"),
183215
+ originCase("compaction_summary", { toolContinuity: custom$1(isToolContinuity).optional() }),
182556
183216
  originCase("skill_activation", {
182557
183217
  activationId: text,
182558
183218
  skillName: text,
@@ -182789,6 +183449,7 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182789
183449
  if (Buffer.byteLength(bytes) > MAX_CHECKPOINT_BYTES) return false;
182790
183450
  const target = resumeCheckpointPath(wirePath);
182791
183451
  const temporary = `${target}.tmp-${process.pid}-${randomBytes(8).toString("hex")}`;
183452
+ let renamed = false;
182792
183453
  try {
182793
183454
  const output = await open(temporary, "wx", 384);
182794
183455
  try {
@@ -182798,10 +183459,11 @@ async function writeResumeCheckpoint(wirePath, position, snapshot) {
182798
183459
  await output.close();
182799
183460
  }
182800
183461
  await rename(temporary, target);
183462
+ renamed = true;
182801
183463
  await syncDir(dirname$3(target));
182802
183464
  return true;
182803
183465
  } finally {
182804
- await rm(temporary, { force: true }).catch(() => {});
183466
+ if (!renamed) await rm(temporary, { force: true }).catch(() => {});
182805
183467
  }
182806
183468
  }
182807
183469
  var MAX_RESUME_SNAPSHOT_BYTES, MAX_CHECKPOINT_BYTES, SAMPLE_BYTES, integer, digest$1, exactInteger, samplesSchema, identitySchema, envelopeSchema$2, resumeCheckpointPath, sha256$6, milliseconds;
@@ -186215,503 +186877,6 @@ var init_todo_list = __esmMin((() => {
186215
186877
  };
186216
186878
  }));
186217
186879
  //#endregion
186218
- //#region ../../packages/agent-core/src/tools/support/file-type.ts
186219
- function toBuffer$2(data) {
186220
- return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
186221
- }
186222
- function startsWith(buf, prefix) {
186223
- const needle = Buffer.isBuffer(prefix) ? prefix : Buffer.from(prefix);
186224
- if (buf.length < needle.length) return false;
186225
- for (let i = 0; i < needle.length; i += 1) if (buf[i] !== needle[i]) return false;
186226
- return true;
186227
- }
186228
- function sniffFtypBrand(header) {
186229
- if (header.length < 12) return null;
186230
- if (header.subarray(4, 8).toString("latin1") !== "ftyp") return null;
186231
- return header.subarray(8, 12).toString("latin1").toLowerCase().replaceAll(/[\s\u0000]+$/g, "").trim();
186232
- }
186233
- function sniffMediaFromMagic(data) {
186234
- const buf = toBuffer$2(data);
186235
- const header = buf.length > 512 ? buf.subarray(0, 512) : buf;
186236
- if (startsWith(header, [
186237
- 137,
186238
- 80,
186239
- 78,
186240
- 71,
186241
- 13,
186242
- 10,
186243
- 26,
186244
- 10
186245
- ])) return {
186246
- kind: "image",
186247
- mimeType: "image/png"
186248
- };
186249
- if (startsWith(header, [
186250
- 255,
186251
- 216,
186252
- 255
186253
- ])) return {
186254
- kind: "image",
186255
- mimeType: "image/jpeg"
186256
- };
186257
- if (startsWith(header, Buffer.from("GIF87a")) || startsWith(header, Buffer.from("GIF89a"))) return {
186258
- kind: "image",
186259
- mimeType: "image/gif"
186260
- };
186261
- if (startsWith(header, Buffer.from("BM"))) return {
186262
- kind: "image",
186263
- mimeType: "image/bmp"
186264
- };
186265
- if (startsWith(header, [
186266
- 73,
186267
- 73,
186268
- 42,
186269
- 0
186270
- ]) || startsWith(header, [
186271
- 77,
186272
- 77,
186273
- 0,
186274
- 42
186275
- ])) return {
186276
- kind: "image",
186277
- mimeType: "image/tiff"
186278
- };
186279
- if (startsWith(header, [
186280
- 0,
186281
- 0,
186282
- 1,
186283
- 0
186284
- ])) return {
186285
- kind: "image",
186286
- mimeType: "image/x-icon"
186287
- };
186288
- if (startsWith(header, Buffer.from("RIFF")) && header.length >= 12) {
186289
- const chunk = header.subarray(8, 12).toString("latin1");
186290
- if (chunk === "WEBP") return {
186291
- kind: "image",
186292
- mimeType: "image/webp"
186293
- };
186294
- if (chunk === "AVI ") return {
186295
- kind: "video",
186296
- mimeType: "video/x-msvideo"
186297
- };
186298
- }
186299
- if (startsWith(header, Buffer.from("FLV"))) return {
186300
- kind: "video",
186301
- mimeType: "video/x-flv"
186302
- };
186303
- if (startsWith(header, ASF_HEADER)) return {
186304
- kind: "video",
186305
- mimeType: "video/x-ms-wmv"
186306
- };
186307
- if (startsWith(header, [
186308
- 26,
186309
- 69,
186310
- 223,
186311
- 163
186312
- ])) {
186313
- const lowered = header.toString("latin1").toLowerCase();
186314
- if (lowered.includes("webm")) return {
186315
- kind: "video",
186316
- mimeType: "video/webm"
186317
- };
186318
- if (lowered.includes("matroska")) return {
186319
- kind: "video",
186320
- mimeType: "video/x-matroska"
186321
- };
186322
- }
186323
- const brand = sniffFtypBrand(header);
186324
- if (brand !== null && brand !== "") {
186325
- if (brand in FTYP_IMAGE_BRANDS) return {
186326
- kind: "image",
186327
- mimeType: FTYP_IMAGE_BRANDS[brand]
186328
- };
186329
- if (brand in FTYP_VIDEO_BRANDS) return {
186330
- kind: "video",
186331
- mimeType: FTYP_VIDEO_BRANDS[brand]
186332
- };
186333
- }
186334
- return null;
186335
- }
186336
- /**
186337
- * Best-effort pixel-dimension reader for common raster formats.
186338
- *
186339
- * Inspects only the fixed region near the start of the file where each
186340
- * format records its dimensions (the IHDR/DIB header, the RIFF chunk
186341
- * after the `WEBP` tag, or the first JPEG SOFn segment). Returns `null`
186342
- * for formats whose dimensions are not locatable from that region, or
186343
- * when the supplied buffer is too short to cover it.
186344
- */
186345
- function sniffImageDimensions(data) {
186346
- const buf = toBuffer$2(data);
186347
- if (startsWith(buf, [
186348
- 137,
186349
- 80,
186350
- 78,
186351
- 71,
186352
- 13,
186353
- 10,
186354
- 26,
186355
- 10
186356
- ]) && buf.length >= 24) return {
186357
- width: buf.readUInt32BE(16),
186358
- height: buf.readUInt32BE(20)
186359
- };
186360
- if ((startsWith(buf, Buffer.from("GIF87a")) || startsWith(buf, Buffer.from("GIF89a"))) && buf.length >= 10) return {
186361
- width: buf.readUInt16LE(6),
186362
- height: buf.readUInt16LE(8)
186363
- };
186364
- if (startsWith(buf, Buffer.from("BM")) && buf.length >= 26) return {
186365
- width: buf.readInt32LE(18),
186366
- height: Math.abs(buf.readInt32LE(22))
186367
- };
186368
- if (startsWith(buf, Buffer.from("RIFF")) && buf.length >= 30) {
186369
- const fourCc = buf.subarray(12, 16).toString("latin1");
186370
- if (fourCc === "VP8 ") return {
186371
- width: buf.readUInt16LE(26) & 16383,
186372
- height: buf.readUInt16LE(28) & 16383
186373
- };
186374
- if (fourCc === "VP8L" && buf.length >= 25) {
186375
- const bits = buf.readUInt32LE(21);
186376
- return {
186377
- width: (bits & 16383) + 1,
186378
- height: (bits >> 14 & 16383) + 1
186379
- };
186380
- }
186381
- if (fourCc === "VP8X") return {
186382
- width: 1 + (buf[24] | buf[25] << 8 | buf[26] << 16),
186383
- height: 1 + (buf[27] | buf[28] << 8 | buf[29] << 16)
186384
- };
186385
- }
186386
- if (startsWith(buf, [255, 216])) {
186387
- let offset = 2;
186388
- while (offset + 9 < buf.length) {
186389
- if (buf[offset] !== 255) {
186390
- offset += 1;
186391
- continue;
186392
- }
186393
- const marker = buf[offset + 1];
186394
- if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
186395
- height: buf.readUInt16BE(offset + 5),
186396
- width: buf.readUInt16BE(offset + 7)
186397
- };
186398
- if (marker === 216 || marker === 217 || marker >= 208 && marker <= 215) {
186399
- offset += 2;
186400
- continue;
186401
- }
186402
- const segmentLength = buf.readUInt16BE(offset + 2);
186403
- if (segmentLength < 2) break;
186404
- offset += 2 + segmentLength;
186405
- }
186406
- }
186407
- return null;
186408
- }
186409
- function getSuffix(path) {
186410
- const idx = path.lastIndexOf(".");
186411
- if (idx === -1) return "";
186412
- if (idx <= Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1) return "";
186413
- return path.slice(idx).toLowerCase();
186414
- }
186415
- function detectFileType(path, header, type = "text") {
186416
- const suffix = getSuffix(path);
186417
- let mediaHint = null;
186418
- if (suffix in TEXT_MIME_BY_SUFFIX) mediaHint = {
186419
- kind: "text",
186420
- mimeType: TEXT_MIME_BY_SUFFIX[suffix]
186421
- };
186422
- else if (suffix in IMAGE_MIME_BY_SUFFIX) mediaHint = {
186423
- kind: "image",
186424
- mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
186425
- };
186426
- else if (suffix in VIDEO_MIME_BY_SUFFIX$1) mediaHint = {
186427
- kind: "video",
186428
- mimeType: VIDEO_MIME_BY_SUFFIX$1[suffix]
186429
- };
186430
- if (header !== void 0) {
186431
- const buf = toBuffer$2(header);
186432
- const sniffed = sniffMediaFromMagic(buf);
186433
- if (sniffed) {
186434
- if (type === "media") return sniffed;
186435
- if (mediaHint) {
186436
- if (sniffed.kind !== mediaHint.kind) return {
186437
- kind: "unknown",
186438
- mimeType: ""
186439
- };
186440
- return mediaHint;
186441
- }
186442
- return sniffed;
186443
- }
186444
- if (mediaHint?.kind === "image") return {
186445
- kind: "unknown",
186446
- mimeType: ""
186447
- };
186448
- if (type === "media" && mediaHint?.kind === "video") return mediaHint;
186449
- if (buf.includes(0)) return {
186450
- kind: "unknown",
186451
- mimeType: ""
186452
- };
186453
- }
186454
- if (mediaHint) return mediaHint;
186455
- if (NON_TEXT_SUFFIXES.has(suffix)) return {
186456
- kind: "unknown",
186457
- mimeType: ""
186458
- };
186459
- return {
186460
- kind: "text",
186461
- mimeType: "text/plain"
186462
- };
186463
- }
186464
- var IMAGE_MIME_BY_SUFFIX, VIDEO_MIME_BY_SUFFIX$1, TEXT_MIME_BY_SUFFIX, NON_TEXT_SUFFIXES, ASF_HEADER, FTYP_IMAGE_BRANDS, FTYP_VIDEO_BRANDS;
186465
- var init_file_type$1 = __esmMin((() => {
186466
- IMAGE_MIME_BY_SUFFIX = Object.freeze({
186467
- ".png": "image/png",
186468
- ".jpg": "image/jpeg",
186469
- ".jpeg": "image/jpeg",
186470
- ".gif": "image/gif",
186471
- ".bmp": "image/bmp",
186472
- ".tif": "image/tiff",
186473
- ".tiff": "image/tiff",
186474
- ".webp": "image/webp",
186475
- ".ico": "image/x-icon",
186476
- ".heic": "image/heic",
186477
- ".heif": "image/heif",
186478
- ".avif": "image/avif",
186479
- ".svgz": "image/svg+xml"
186480
- });
186481
- VIDEO_MIME_BY_SUFFIX$1 = Object.freeze({
186482
- ".mp4": "video/mp4",
186483
- ".mpg": "video/mpeg",
186484
- ".mpeg": "video/mpeg",
186485
- ".mkv": "video/x-matroska",
186486
- ".avi": "video/x-msvideo",
186487
- ".mov": "video/quicktime",
186488
- ".ogv": "video/ogg",
186489
- ".wmv": "video/x-ms-wmv",
186490
- ".webm": "video/webm",
186491
- ".m4v": "video/x-m4v",
186492
- ".flv": "video/x-flv",
186493
- ".3gp": "video/3gpp",
186494
- ".3g2": "video/3gpp2"
186495
- });
186496
- TEXT_MIME_BY_SUFFIX = Object.freeze({ ".svg": "image/svg+xml" });
186497
- NON_TEXT_SUFFIXES = new Set([
186498
- ".icns",
186499
- ".psd",
186500
- ".ai",
186501
- ".eps",
186502
- ".pdf",
186503
- ".doc",
186504
- ".docx",
186505
- ".dot",
186506
- ".dotx",
186507
- ".rtf",
186508
- ".odt",
186509
- ".xls",
186510
- ".xlsx",
186511
- ".xlsm",
186512
- ".xlt",
186513
- ".xltx",
186514
- ".xltm",
186515
- ".ods",
186516
- ".ppt",
186517
- ".pptx",
186518
- ".pptm",
186519
- ".pps",
186520
- ".ppsx",
186521
- ".odp",
186522
- ".pages",
186523
- ".numbers",
186524
- ".key",
186525
- ".zip",
186526
- ".rar",
186527
- ".7z",
186528
- ".tar",
186529
- ".gz",
186530
- ".tgz",
186531
- ".bz2",
186532
- ".xz",
186533
- ".zst",
186534
- ".lz",
186535
- ".lz4",
186536
- ".br",
186537
- ".cab",
186538
- ".ar",
186539
- ".deb",
186540
- ".rpm",
186541
- ".mp3",
186542
- ".wav",
186543
- ".flac",
186544
- ".ogg",
186545
- ".oga",
186546
- ".opus",
186547
- ".aac",
186548
- ".m4a",
186549
- ".wma",
186550
- ".ttf",
186551
- ".otf",
186552
- ".woff",
186553
- ".woff2",
186554
- ".exe",
186555
- ".dll",
186556
- ".so",
186557
- ".dylib",
186558
- ".bin",
186559
- ".apk",
186560
- ".ipa",
186561
- ".jar",
186562
- ".class",
186563
- ".pyc",
186564
- ".pyo",
186565
- ".wasm",
186566
- ".dmg",
186567
- ".iso",
186568
- ".img",
186569
- ".sqlite",
186570
- ".sqlite3",
186571
- ".db",
186572
- ".db3"
186573
- ]);
186574
- ASF_HEADER = Buffer.from([
186575
- 48,
186576
- 38,
186577
- 178,
186578
- 117,
186579
- 142,
186580
- 102,
186581
- 207,
186582
- 17,
186583
- 166,
186584
- 217,
186585
- 0,
186586
- 170,
186587
- 0,
186588
- 98,
186589
- 206,
186590
- 108
186591
- ]);
186592
- FTYP_IMAGE_BRANDS = Object.freeze({
186593
- avif: "image/avif",
186594
- avis: "image/avif",
186595
- heic: "image/heic",
186596
- heif: "image/heif",
186597
- heix: "image/heif",
186598
- hevc: "image/heic",
186599
- mif1: "image/heif",
186600
- msf1: "image/heif"
186601
- });
186602
- FTYP_VIDEO_BRANDS = Object.freeze({
186603
- isom: "video/mp4",
186604
- iso2: "video/mp4",
186605
- iso5: "video/mp4",
186606
- mp41: "video/mp4",
186607
- mp42: "video/mp4",
186608
- avc1: "video/mp4",
186609
- mp4v: "video/mp4",
186610
- m4v: "video/x-m4v",
186611
- qt: "video/quicktime",
186612
- "3gp4": "video/3gpp",
186613
- "3gp5": "video/3gpp",
186614
- "3gp6": "video/3gpp",
186615
- "3gp7": "video/3gpp",
186616
- "3g2": "video/3gpp2"
186617
- });
186618
- }));
186619
- //#endregion
186620
- //#region ../../packages/agent-core/src/utils/tokens.ts
186621
- /** Open assistant messages grow while streaming; completed history stays cached. */
186622
- function invalidateMessageTokenEstimate(message) {
186623
- messageTokenEstimateCache.delete(message);
186624
- }
186625
- /**
186626
- * Estimate token count from text using a character-based heuristic.
186627
- * - ASCII (~4 chars per token)
186628
- * - CJK and other non-ASCII (~1 char per token)
186629
- * The estimate is transient — the next LLM call returns the real count
186630
- * and supersedes this value. Used to keep `tokenCountWithPending`
186631
- * monotonic between LLM round-trips without paying for a tokenizer.
186632
- */
186633
- function estimateTokens(text) {
186634
- let asciiCount = 0;
186635
- let nonAsciiCount = 0;
186636
- for (const char of text) if (char.codePointAt(0) <= 127) asciiCount++;
186637
- else nonAsciiCount++;
186638
- return Math.ceil(asciiCount / 4) + nonAsciiCount;
186639
- }
186640
- function estimateTokensForMessages(messages) {
186641
- let total = 0;
186642
- for (const message of messages) total += estimateTokensForMessage(message);
186643
- return total;
186644
- }
186645
- function estimateTokensForTools(tools) {
186646
- let total = 0;
186647
- for (const tool of tools) {
186648
- total += estimateTokens(tool.name);
186649
- total += estimateTokens(tool.description);
186650
- total += estimateTokens(JSON.stringify(tool.parameters));
186651
- }
186652
- return total;
186653
- }
186654
- function estimateTokensForMessage(message) {
186655
- const cached = messageTokenEstimateCache.get(message);
186656
- if (cached !== void 0) return cached;
186657
- let total = estimateTokens(message.role);
186658
- total += estimateTokensForContentParts(message.content);
186659
- if (message.toolCalls !== void 0) for (const call of message.toolCalls) {
186660
- total += estimateTokens(call.name);
186661
- total += estimateTokens(JSON.stringify(call.arguments));
186662
- }
186663
- messageTokenEstimateCache.set(message, total);
186664
- return total;
186665
- }
186666
- function estimateTokensForContentParts(parts) {
186667
- let total = 0;
186668
- for (const part of parts) total += estimateTokensForContentPart(part);
186669
- return total;
186670
- }
186671
- function imageDimensionsFromDataUrl(url) {
186672
- const commaIndex = url.indexOf(",");
186673
- if (commaIndex <= 0) return null;
186674
- const metadata = url.slice(0, commaIndex).toLowerCase();
186675
- if (!metadata.startsWith("data:image/") || !metadata.split(";").includes("base64")) return null;
186676
- const maxBase64Chars = Math.ceil(MAX_IMAGE_HEADER_BYTES / 3) * 4;
186677
- const encodedHeader = url.slice(commaIndex + 1, commaIndex + 1 + maxBase64Chars);
186678
- if (encodedHeader.length === 0) return null;
186679
- try {
186680
- const dimensions = sniffImageDimensions(Buffer.from(encodedHeader, "base64"));
186681
- if (dimensions === null || !Number.isSafeInteger(dimensions.width) || !Number.isSafeInteger(dimensions.height) || dimensions.width <= 0 || dimensions.height <= 0) return null;
186682
- return dimensions;
186683
- } catch {
186684
- return null;
186685
- }
186686
- }
186687
- function estimateImageTokensFromDimensions(width, height) {
186688
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return MEDIA_TOKEN_ESTIMATE;
186689
- const patchSize = BigInt(32);
186690
- const tokens = (BigInt(width) + patchSize - 1n) / patchSize * ((BigInt(height) + patchSize - 1n) / patchSize);
186691
- return tokens > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(tokens);
186692
- }
186693
- function estimateImageTokens(url) {
186694
- const dimensions = imageDimensionsFromDataUrl(url);
186695
- return dimensions === null ? MEDIA_TOKEN_ESTIMATE : estimateImageTokensFromDimensions(dimensions.width, dimensions.height);
186696
- }
186697
- function estimateTokensForContentPart(part) {
186698
- switch (part.type) {
186699
- case "text": return estimateTokens(part.text);
186700
- case "think": return estimateTokens(part.think);
186701
- case "image_url": return estimateImageTokens(part.imageUrl.url);
186702
- case "audio_url":
186703
- case "video_url": return MEDIA_TOKEN_ESTIMATE;
186704
- default: return 0;
186705
- }
186706
- }
186707
- var messageTokenEstimateCache, MEDIA_TOKEN_ESTIMATE, MAX_IMAGE_HEADER_BYTES;
186708
- var init_tokens = __esmMin((() => {
186709
- init_file_type$1();
186710
- messageTokenEstimateCache = /* @__PURE__ */ new WeakMap();
186711
- MEDIA_TOKEN_ESTIMATE = 2e3;
186712
- MAX_IMAGE_HEADER_BYTES = 1024 * 1024;
186713
- }));
186714
- //#endregion
186715
186880
  //#region ../../node_modules/.pnpm/bmp-ts@1.0.9/node_modules/bmp-ts/dist/esm/header-types.js
186716
186881
  var HeaderTypes, header_types_default;
186717
186882
  var init_header_types = __esmMin((() => {
@@ -235103,8 +235268,9 @@ function makeSyntheticToolResult(toolCallId) {
235103
235268
  }
235104
235269
  function mergeAdjacentUserMessages(history, onAnomaly) {
235105
235270
  const out = [];
235106
- for (const source of history) {
235107
- const message = prepareMessageForProjection(source, onAnomaly);
235271
+ const latestUserIndex = history.findLastIndex((message) => message.role === "user" && message.partial !== true && (message.origin === void 0 || message.origin.kind === "user"));
235272
+ for (const [index, source] of history.entries()) {
235273
+ const message = prepareMessageForProjection(source, onAnomaly, index < latestUserIndex);
235108
235274
  if (message === null) continue;
235109
235275
  const previous = out.at(-1);
235110
235276
  if (canMergeUserMessage(message) && previous !== void 0 && canMergeUserMessage(previous)) {
@@ -235115,7 +235281,7 @@ function mergeAdjacentUserMessages(history, onAnomaly) {
235115
235281
  }
235116
235282
  return out.map(stripContextMetadata);
235117
235283
  }
235118
- function prepareMessageForProjection(message, onAnomaly) {
235284
+ function prepareMessageForProjection(message, onAnomaly, historical = false) {
235119
235285
  if (message.partial === true) return null;
235120
235286
  let content;
235121
235287
  for (const [index, part] of message.content.entries()) {
@@ -235127,6 +235293,19 @@ function prepareMessageForProjection(message, onAnomaly) {
235127
235293
  });
235128
235294
  continue;
235129
235295
  }
235296
+ if (historical && message.role === "user" && part.type === "text") {
235297
+ const text = withoutHistoricalTelegramGroupReminder(part.text);
235298
+ if (text !== part.text) {
235299
+ content ??= message.content.slice(0, index);
235300
+ const shortened = {
235301
+ ...part,
235302
+ text
235303
+ };
235304
+ copyRequestTextOrigin(part, shortened, message.origin);
235305
+ content.push(shortened);
235306
+ continue;
235307
+ }
235308
+ }
235130
235309
  content?.push(part);
235131
235310
  }
235132
235311
  const next = content === void 0 ? message : {
@@ -235136,6 +235315,14 @@ function prepareMessageForProjection(message, onAnomaly) {
235136
235315
  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 } });
235137
235316
  return next.content.length === 0 && next.toolCalls.length === 0 ? null : next;
235138
235317
  }
235318
+ function withoutHistoricalTelegramGroupReminder(text) {
235319
+ if (!text.startsWith("<channel source=\"telegram\"") && !text.startsWith("You are connected to a Telegram channel.")) return text;
235320
+ const start = text.lastIndexOf(`</channel>\n[GRUPPENANTWORT:`);
235321
+ if (start < 0) return text;
235322
+ const reminder = text.slice(start + 10 + 1);
235323
+ if (!/^\[GRUPPENANTWORT:[^\r\n]*\]$/.test(reminder)) return text;
235324
+ return text.slice(0, start + 10);
235325
+ }
235139
235326
  function canMergeUserMessage(message) {
235140
235327
  return message.role === "user" && message.origin?.kind === "user";
235141
235328
  }
@@ -235267,6 +235454,7 @@ var init_context$2 = __esmMin((() => {
235267
235454
  init_tokens();
235268
235455
  init_xml_escape();
235269
235456
  init_compaction();
235457
+ init_tool_continuity();
235270
235458
  init_projector();
235271
235459
  init_types$6();
235272
235460
  init_types$6();
@@ -235277,6 +235465,8 @@ var init_context$2 = __esmMin((() => {
235277
235465
  TOOL_INTERRUPTED_ON_RESUME_OUTPUT$2 = "Tool execution was interrupted before its result was recorded. Do not assume the tool completed successfully.";
235278
235466
  ContextMemory = class {
235279
235467
  agent;
235468
+ mutationGeneration = randomUUID();
235469
+ mutationRevision = 0;
235280
235470
  _history = [];
235281
235471
  _tokenCount = 0;
235282
235472
  tokenCountCoveredMessageCount = 0;
@@ -235288,6 +235478,15 @@ var init_context$2 = __esmMin((() => {
235288
235478
  constructor(agent) {
235289
235479
  this.agent = agent;
235290
235480
  }
235481
+ publishMutation(reason) {
235482
+ if (this.agent.records.restoring) return;
235483
+ this.agent.emitEvent({
235484
+ type: "context.changed",
235485
+ generation: this.mutationGeneration,
235486
+ revision: ++this.mutationRevision,
235487
+ reason
235488
+ });
235489
+ }
235291
235490
  get lastAssistantAt() {
235292
235491
  return this._lastAssistantAt;
235293
235492
  }
@@ -235441,6 +235640,7 @@ var init_context$2 = __esmMin((() => {
235441
235640
  return removedMessages.size;
235442
235641
  }
235443
235642
  clear() {
235643
+ const changed = this._history.length > 0;
235444
235644
  this.agent.records.logRecord({ type: "context.clear" });
235445
235645
  this._history = [];
235446
235646
  this._tokenCount = 0;
@@ -235452,6 +235652,7 @@ var init_context$2 = __esmMin((() => {
235452
235652
  this.agent.microCompaction.reset();
235453
235653
  this.agent.injection.onContextClear();
235454
235654
  this.agent.emitStatusUpdated();
235655
+ if (changed) this.publishMutation("clear");
235455
235656
  }
235456
235657
  undo(count) {
235457
235658
  if (count <= 0) return;
@@ -235489,6 +235690,7 @@ var init_context$2 = __esmMin((() => {
235489
235690
  this.deferredMessages = [];
235490
235691
  this.agent.microCompaction.reset(this._history.length);
235491
235692
  this.agent.emitStatusUpdated();
235693
+ if (removedMessages.size > 0) this.publishMutation("undo");
235492
235694
  if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
235493
235695
  reason: "undo_limit",
235494
235696
  requestedCount: count,
@@ -235497,6 +235699,7 @@ var init_context$2 = __esmMin((() => {
235497
235699
  } });
235498
235700
  }
235499
235701
  applyCompaction(input, options = {}) {
235702
+ if (this.agent.records.restoring !== null && input.toolContinuity !== void 0 && !isToolContinuity(input.toolContinuity)) throw new Error("Invalid compaction tool continuity");
235500
235703
  let preservedToolTailCount = input.preservedToolTailCount;
235501
235704
  if (this.agent.records.restoring === null && this.hasOpenToolExchange()) {
235502
235705
  const start = this._history.findIndex((message) => message.role === "assistant" && message.toolCalls.some((call) => this.pendingToolResultIds.has(call.id)));
@@ -235533,25 +235736,75 @@ var init_context$2 = __esmMin((() => {
235533
235736
  elisionMessage,
235534
235737
  ...selection.tail
235535
235738
  ];
235536
- const contextSummary = input.contextSummary ?? input.summary;
235537
- const summaryMessage = {
235739
+ let contextSummary = input.contextSummary ?? input.summary;
235740
+ let toolContinuity = this.agent.records.restoring === null ? void 0 : input.toolContinuity;
235741
+ let summaryMessage = {
235538
235742
  role: "user",
235539
235743
  content: [{
235540
235744
  type: "text",
235541
235745
  text: contextSummary
235542
235746
  }],
235543
235747
  toolCalls: [],
235544
- origin: { kind: "compaction_summary" }
235748
+ origin: {
235749
+ kind: "compaction_summary",
235750
+ ...toolContinuity === void 0 ? {} : { toolContinuity }
235751
+ }
235545
235752
  };
235546
- const replacement = [
235753
+ let replacement = [
235547
235754
  ...keptMessages,
235548
235755
  summaryMessage,
235549
235756
  ...preservedToolTail
235550
235757
  ];
235758
+ const previousHistoryTokens = options.requireReduction && this.agent.records.restoring === null ? estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true })) : void 0;
235759
+ if (this.agent.records.restoring === null) {
235760
+ toolContinuity = void 0;
235761
+ const observations = collectToolContinuity(this._history.slice(0, tailStart));
235762
+ if (observations !== void 0) {
235763
+ const baseProjected = project(replacement, { dropOrphanResults: true });
235764
+ const baseTokens = estimateTokensForMessages(baseProjected);
235765
+ let extraTokens = previousHistoryTokens === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, previousHistoryTokens - baseTokens - 1);
235766
+ if (this.agent.config.hasProvider) {
235767
+ const window = this.agent.fullCompaction.getEffectiveMaxContextTokens();
235768
+ const budget = this.agent.fullCompaction.getCompactionBudgetTokens();
235769
+ const requestLimit = Math.min(window > 0 ? window - 1 : Number.POSITIVE_INFINITY, budget ?? Number.POSITIVE_INFINITY);
235770
+ extraTokens = Math.min(extraTokens, Math.max(0, requestLimit - this.agent.fullCompaction.estimateProjectedRequestTokens({ messages: baseProjected }) - 1));
235771
+ }
235772
+ let fitted = fitToolContinuity(observations, extraTokens);
235773
+ while (fitted !== void 0) {
235774
+ const candidateSummary = {
235775
+ ...summaryMessage,
235776
+ content: [{
235777
+ type: "text",
235778
+ text: contextSummary + fitted.text
235779
+ }],
235780
+ origin: {
235781
+ kind: "compaction_summary",
235782
+ toolContinuity: fitted.continuity
235783
+ }
235784
+ };
235785
+ const candidate = [
235786
+ ...keptMessages,
235787
+ candidateSummary,
235788
+ ...preservedToolTail
235789
+ ];
235790
+ const projected = project(candidate, { dropOrphanResults: true });
235791
+ const status = this.agent.fullCompaction.proactiveCompactionStatus({ messages: projected });
235792
+ const reducing = previousHistoryTokens === void 0 || estimateTokensForMessages(projected) < previousHistoryTokens;
235793
+ 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);
235794
+ if (reducing && bytesFit) {
235795
+ toolContinuity = fitted.continuity;
235796
+ contextSummary += fitted.text;
235797
+ summaryMessage = candidateSummary;
235798
+ replacement = candidate;
235799
+ break;
235800
+ }
235801
+ fitted = fitToolContinuity(observations, estimateTokens(fitted.text) - 1);
235802
+ }
235803
+ }
235804
+ }
235551
235805
  if (options.requireReduction && this.agent.records.restoring === null) {
235552
- const previousHistoryTokens = estimateTokensForMessages(project(this.agent.microCompaction.compact(this._history), { dropOrphanResults: true }));
235553
235806
  const nextHistoryTokens = estimateTokensForMessages(project(replacement, { dropOrphanResults: true }));
235554
- if (nextHistoryTokens >= previousHistoryTokens) throw new BlunError(ErrorCodes.COMPACTION_FAILED, "Automatic compaction did not reduce the history. The conversation history was not changed.", { details: {
235807
+ 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: {
235555
235808
  reason: "non_reducing_summary",
235556
235809
  contextUnchanged: true,
235557
235810
  previousHistoryTokens,
@@ -235564,6 +235817,7 @@ var init_context$2 = __esmMin((() => {
235564
235817
  const result = {
235565
235818
  summary: input.summary,
235566
235819
  contextSummary,
235820
+ ...toolContinuity === void 0 ? {} : { toolContinuity },
235567
235821
  compactedCount: input.compactedCount,
235568
235822
  tokensBefore: input.tokensBefore,
235569
235823
  tokensAfter,
@@ -235580,6 +235834,7 @@ var init_context$2 = __esmMin((() => {
235580
235834
  this.agent.replayBuilder.patchLast("compaction", { result: {
235581
235835
  summary: result.summary,
235582
235836
  contextSummary: result.contextSummary,
235837
+ ...result.toolContinuity === void 0 ? {} : { toolContinuity: result.toolContinuity },
235583
235838
  compactedCount: result.compactedCount,
235584
235839
  tokensBefore: result.tokensBefore,
235585
235840
  tokensAfter: result.tokensAfter,
@@ -237134,20 +237389,75 @@ function fingerprintTitle(title) {
237134
237389
  return createHash("sha256").update(title.trim().toLowerCase()).digest("hex");
237135
237390
  }
237136
237391
  async function loadRegister(mistakeDir = defaultMistakeDir()) {
237392
+ const target = join(mistakeDir, REGISTER_FILENAME);
237137
237393
  try {
237138
237394
  await mkdir(mistakeDir, { recursive: true });
237139
- const content = await readFile(join(mistakeDir, REGISTER_FILENAME), "utf-8");
237140
- return JSON.parse(content);
237141
- } catch {
237142
- return {
237395
+ const before = await lstat(target).catch((error) => {
237396
+ if (error.code === "ENOENT") return void 0;
237397
+ throw error;
237398
+ });
237399
+ if (before === void 0) return {
237143
237400
  version: 1,
237144
237401
  entries: []
237145
237402
  };
237403
+ if (!before.isFile() || before.isSymbolicLink()) throw new Error("Mistake register must be a regular file.");
237404
+ const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
237405
+ let content;
237406
+ try {
237407
+ const opened = await handle.stat();
237408
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) throw new Error("Mistake register changed while opening.");
237409
+ content = await handle.readFile("utf8");
237410
+ } finally {
237411
+ await handle.close();
237412
+ }
237413
+ let parsed;
237414
+ try {
237415
+ parsed = JSON.parse(content);
237416
+ } catch {
237417
+ throw new Error("Mistake register has invalid JSON.");
237418
+ }
237419
+ assertRegister(parsed);
237420
+ return parsed;
237421
+ } catch (error) {
237422
+ throw registerError(error, "read");
237146
237423
  }
237147
237424
  }
237148
237425
  async function saveRegister(register, mistakeDir = defaultMistakeDir()) {
237149
- await mkdir(mistakeDir, { recursive: true });
237150
- await writeFile(join(mistakeDir, REGISTER_FILENAME), JSON.stringify(register, null, 2), "utf-8");
237426
+ assertRegister(register);
237427
+ const target = join(mistakeDir, REGISTER_FILENAME);
237428
+ const temporary = `${target}.tmp.${process.pid}.${randomUUID()}`;
237429
+ let created = false;
237430
+ try {
237431
+ await loadRegister(mistakeDir);
237432
+ const handle = await open(temporary, "wx", 384);
237433
+ created = true;
237434
+ try {
237435
+ await handle.writeFile(JSON.stringify(register, null, 2), "utf8");
237436
+ await handle.sync();
237437
+ } finally {
237438
+ await handle.close();
237439
+ }
237440
+ await rename(temporary, target);
237441
+ } catch (error) {
237442
+ throw registerError(error, "write");
237443
+ } finally {
237444
+ if (created) await unlink(temporary).catch(() => {});
237445
+ }
237446
+ }
237447
+ function assertRegister(value) {
237448
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Mistake register has an invalid v1 schema.");
237449
+ const register = value;
237450
+ if (register["version"] !== 1 || !Array.isArray(register["entries"]) || !register["entries"].every((entry) => {
237451
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
237452
+ const fields = entry;
237453
+ 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");
237454
+ })) throw new Error("Mistake register has an invalid v1 schema.");
237455
+ }
237456
+ function registerError(error, operation) {
237457
+ if (error instanceof Error && error.message.startsWith("Mistake register ")) return error;
237458
+ const code = typeof error === "object" && error !== null ? error.code : void 0;
237459
+ const suffix = typeof code === "string" && /^[A-Z0-9_]+$/u.test(code) ? ` (${code})` : "";
237460
+ return /* @__PURE__ */ new Error(`Mistake register ${operation} failed${suffix}.`);
237151
237461
  }
237152
237462
  /**
237153
237463
  * Findet einen Register-Eintrag anhand des Titels (Fingerabdruck).
@@ -239937,6 +240247,7 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
239937
240247
  const DEFERRED_TOOL_LOADER_NAME = "ToolSearch";
239938
240248
  const MAX_PERSISTENT_DEFERRED_TOOLS = 12;
239939
240249
  const MAX_TOOL_SEARCH_QUERY_CHARS = 1e3;
240250
+ const MAX_TOOL_CATALOG_RESULT_BYTES = 4096;
239940
240251
  const MAX_AUTO_RANKED_TOOLS = 6;
239941
240252
  const deferredToolLoaders = /* @__PURE__ */ new WeakSet();
239942
240253
  const TOOL_RANK_EMBEDDED_CONTENT_RE = /<(attached_documents?|attachment_content|document_content|file_content)\b[^>]*>[\s\S]*?<\/\1>/giu;
@@ -240181,6 +240492,27 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
240181
240492
  for (const leaf of leaves) leafCounts.set(leaf, (leafCounts.get(leaf) || 0) + 1);
240182
240493
  return names.map((name, index) => leafCounts.get(leaves[index]) === 1 ? leaves[index] : name).sort((left, right) => left.localeCompare(right));
240183
240494
  }
240495
+ function boundedDeferredToolCatalog(tools, filter) {
240496
+ const needle = filter.toLowerCase();
240497
+ const matched = deferredToolCatalog(tools).filter((name) => name.toLowerCase().includes(needle));
240498
+ const names = [];
240499
+ let bytes = Buffer.byteLength(JSON.stringify({
240500
+ names,
240501
+ total: matched.length,
240502
+ truncated: false
240503
+ }));
240504
+ for (const name of matched) {
240505
+ const nextBytes = Buffer.byteLength(JSON.stringify(name)) + (names.length > 0 ? 1 : 0);
240506
+ if (bytes + nextBytes > MAX_TOOL_CATALOG_RESULT_BYTES) break;
240507
+ names.push(name);
240508
+ bytes += nextBytes;
240509
+ }
240510
+ return JSON.stringify({
240511
+ names,
240512
+ total: matched.length,
240513
+ truncated: names.length < matched.length
240514
+ });
240515
+ }
240184
240516
  function normalizeDeferredToolQuery(value) {
240185
240517
  const query = String(value ?? "").trim();
240186
240518
  if (query.length <= MAX_TOOL_SEARCH_QUERY_CHARS) return query;
@@ -240255,14 +240587,14 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
240255
240587
  function rankingTokens(value) {
240256
240588
  return [...new Set(String(value || "").normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]{3,}/gu) || [])].filter((token) => !TOOL_RANK_STOP_WORDS.has(token));
240257
240589
  }
240258
- function toolRankingDocument(tool) {
240590
+ function toolRankingDocument(tool, trustedToolNames) {
240259
240591
  const name = String(tool?.name || "");
240260
240592
  const leaf = name.split(/__|:/).at(-1) || name;
240261
240593
  const splitToolName = (value) => String(value || "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ");
240262
240594
  const nameTokens = rankingTokens(`${splitToolName(name)} ${splitToolName(leaf)}`);
240263
240595
  const descriptionTokens = rankingTokens(tool?.description);
240264
240596
  const parameterText = [];
240265
- const properties = tool?.parameters?.properties;
240597
+ const properties = /^mcp__/iu.test(name) && !trustedToolNames?.has(name) ? void 0 : tool?.parameters?.properties;
240266
240598
  if (properties && typeof properties === "object") for (const [parameterName, definition] of Object.entries(properties)) parameterText.push(parameterName, definition?.description || "");
240267
240599
  const exampleText = Array.isArray(tool?.examples) ? tool.examples.map((example) => example?.prompt || "").join(" ") : "";
240268
240600
  return {
@@ -240294,7 +240626,7 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
240294
240626
  const queryTokens = [...new Set(rankingIntentTexts(value).flatMap(rankingTokens))];
240295
240627
  if (queryTokens.length === 0) return /* @__PURE__ */ new Set();
240296
240628
  const ranked = (Array.isArray(tools) ? tools : []).map((tool) => {
240297
- const document = toolRankingDocument(tool);
240629
+ const document = toolRankingDocument(tool, options.trustedToolNames);
240298
240630
  if (!document.name) return null;
240299
240631
  let score = 0;
240300
240632
  let matchedTokens = 0;
@@ -240402,8 +240734,8 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
240402
240734
  name: loaderName,
240403
240735
  description: [
240404
240736
  `Search ${deferredCount} deferred ${deferredCount === 1 ? "tool schema" : "tool schemas"} without loading all definitions.`,
240405
- "Use select:leaf_name for one known tool, plain keywords to search, or +word to require that word in the tool name.",
240406
- `Loaded tools are available in the next step; the ${MAX_PERSISTENT_DEFERRED_TOOLS} most recently selected schemas remain loaded.`
240737
+ "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.",
240738
+ `Loaded tools are available in the next step; up to ${MAX_PERSISTENT_DEFERRED_TOOLS} recently selected schemas remain loaded when the context budget allows.`
240407
240739
  ].join("\n"),
240408
240740
  parameters: {
240409
240741
  type: "object",
@@ -240420,7 +240752,12 @@ var require_turn_tool_performance_policy = /* @__PURE__ */ __commonJSMin(((expor
240420
240752
  return {
240421
240753
  description: query ? `Searching tool schemas for ${query}` : "Searching tool schemas",
240422
240754
  approvalRule: loaderName,
240423
- execute: async () => {
240755
+ execute: async (context) => {
240756
+ context?.signal?.throwIfAborted();
240757
+ if (query.toLowerCase().startsWith("catalog:")) return {
240758
+ isError: false,
240759
+ output: boundedDeferredToolCatalog([...available.values()], query.slice(8).trim())
240760
+ };
240424
240761
  let matches = searchDeferredTools([...available.values()], query);
240425
240762
  let relatedFallback = false;
240426
240763
  if (matches.length === 0) {
@@ -252407,7 +252744,7 @@ var init_modelCatalog$2 = __esmMin((() => {
252407
252744
  function isVolatileEventType(type) {
252408
252745
  return volatileEventTypeSet.has(type);
252409
252746
  }
252410
- var MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE, tokenUsageSchema, finishReasonSchema, usageStatusSchema, permissionModeSchema, skillSourceSchema, userPromptOriginSchema, skillActivationOriginSchema, pluginCommandOriginSchema, injectionOriginSchema, shellCommandOriginSchema, compactionSummaryOriginSchema, systemTriggerOriginSchema, agentCoreBackgroundTaskStatusSchema, backgroundTaskOriginSchema, cronJobOriginSchema, cronMissedOriginSchema, hookResultOriginSchema, retryOriginSchema, promptOriginSchema, goalStatusSchema, goalActorSchema, goalBudgetReportSchema, goalSnapshotSchema, goalChangeStatsSchema, goalChangeKindSchema, goalChangeSchema, blunErrorCodeSchema, blunErrorPayloadSchema, backgroundTaskInfoBaseSchema, processBackgroundTaskInfoSchema, agentBackgroundTaskInfoSchema, questionBackgroundTaskInfoSchema, backgroundTaskInfoSchema, compactionResultSchema, toolUpdateSchema, turnEndReasonSchema, agentStatusUpdatedEventSchema, sessionMetaUpdatedEventSchema, sessionCreatedEventSchema, workspaceCreatedEventSchema, workspaceUpdatedEventSchema, workspaceDeletedEventSchema, sessionStatusChangedEventSchema, modelCatalogChangedEventSchema, goalUpdatedEventSchema, skillActivatedEventSchema, pluginCommandActivatedEventSchema, errorEventSchema, warningEventSchema, turnStartedEventSchema, turnEndedEventSchema, turnStepStartedEventSchema, turnStepCompletedEventSchema, turnStepRetryingEventSchema, turnStepInterruptedEventSchema, assistantDeltaEventSchema, hookResultEventSchema, thinkingDeltaEventSchema, toolCallDeltaEventSchema, toolCallStartedEventSchema, toolProgressEventSchema, shellOutputEventSchema, shellStartedEventSchema, toolResultEventSchema, subagentSpawnedEventSchema, subagentStartedEventSchema, subagentSuspendedEventSchema, subagentCompletedEventSchema, subagentFailedEventSchema, compactionStartedEventSchema, compactionBlockedEventSchema, compactionCancelledEventSchema, compactionProgressEventSchema, compactionCompletedEventSchema, backgroundTaskStartedEventSchema, backgroundTaskTerminatedEventSchema, cronFiredEventSchema, cronRunUpdatedEventSchema, promptSubmittedEventSchema, toolListUpdatedReasonSchema, toolListUpdatedEventSchema, mcpServerStatusPayloadSchema, mcpServerStatusEventSchema, mediaDroppedEventSchema, agentEventSchema, eventSchema, VOLATILE_EVENT_TYPES, volatileEventTypeSet;
252747
+ var MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE, tokenUsageSchema, finishReasonSchema, usageStatusSchema, permissionModeSchema, skillSourceSchema, userPromptOriginSchema, skillActivationOriginSchema, pluginCommandOriginSchema, injectionOriginSchema, shellCommandOriginSchema, compactionSummaryOriginSchema, systemTriggerOriginSchema, agentCoreBackgroundTaskStatusSchema, backgroundTaskOriginSchema, cronJobOriginSchema, cronMissedOriginSchema, hookResultOriginSchema, retryOriginSchema, promptOriginSchema, goalStatusSchema, goalActorSchema, goalBudgetReportSchema, goalSnapshotSchema, goalChangeStatsSchema, goalChangeKindSchema, goalChangeSchema, blunErrorCodeSchema, blunErrorPayloadSchema, backgroundTaskInfoBaseSchema, processBackgroundTaskInfoSchema, agentBackgroundTaskInfoSchema, questionBackgroundTaskInfoSchema, backgroundTaskInfoSchema, compactionResultSchema, toolUpdateSchema, turnEndReasonSchema, contextChangedEventSchema, agentStatusUpdatedEventSchema, sessionMetaUpdatedEventSchema, sessionCreatedEventSchema, workspaceCreatedEventSchema, workspaceUpdatedEventSchema, workspaceDeletedEventSchema, sessionStatusChangedEventSchema, modelCatalogChangedEventSchema, goalUpdatedEventSchema, skillActivatedEventSchema, pluginCommandActivatedEventSchema, errorEventSchema, warningEventSchema, turnStartedEventSchema, turnEndedEventSchema, turnStepStartedEventSchema, turnStepCompletedEventSchema, turnStepRetryingEventSchema, turnStepInterruptedEventSchema, assistantDeltaEventSchema, hookResultEventSchema, thinkingDeltaEventSchema, toolCallDeltaEventSchema, toolCallStartedEventSchema, toolProgressEventSchema, shellOutputEventSchema, shellStartedEventSchema, toolResultEventSchema, subagentSpawnedEventSchema, subagentStartedEventSchema, subagentSuspendedEventSchema, subagentCompletedEventSchema, subagentFailedEventSchema, compactionStartedEventSchema, compactionBlockedEventSchema, compactionCancelledEventSchema, compactionProgressEventSchema, compactionCompletedEventSchema, backgroundTaskStartedEventSchema, backgroundTaskTerminatedEventSchema, cronFiredEventSchema, cronRunUpdatedEventSchema, promptSubmittedEventSchema, toolListUpdatedReasonSchema, toolListUpdatedEventSchema, mcpServerStatusPayloadSchema, mcpServerStatusEventSchema, mediaDroppedEventSchema, agentEventSchema, eventSchema, VOLATILE_EVENT_TYPES, volatileEventTypeSet;
252411
252748
  var init_events$1 = __esmMin((() => {
252412
252749
  init_zod$1();
252413
252750
  init_display();
@@ -252583,7 +252920,11 @@ var init_events$1 = __esmMin((() => {
252583
252920
  tokensUsed: number$1(),
252584
252921
  wallClockMs: number$1()
252585
252922
  });
252586
- goalChangeKindSchema = _enum(["lifecycle", "completion"]);
252923
+ goalChangeKindSchema = _enum([
252924
+ "lifecycle",
252925
+ "completion",
252926
+ "edited"
252927
+ ]);
252587
252928
  goalChangeSchema = object$1({
252588
252929
  kind: goalChangeKindSchema,
252589
252930
  status: goalStatusSchema.optional(),
@@ -252740,6 +253081,12 @@ var init_events$1 = __esmMin((() => {
252740
253081
  "failed",
252741
253082
  "filtered"
252742
253083
  ]);
253084
+ contextChangedEventSchema = object$1({
253085
+ type: literal("context.changed"),
253086
+ generation: string().uuid(),
253087
+ revision: number$1().int().positive().max(Number.MAX_SAFE_INTEGER),
253088
+ reason: _enum(["clear", "undo"])
253089
+ });
252743
253090
  agentStatusUpdatedEventSchema = object$1({
252744
253091
  type: literal("agent.status.updated"),
252745
253092
  model: string().optional(),
@@ -253076,6 +253423,7 @@ var init_events$1 = __esmMin((() => {
253076
253423
  errorEventSchema,
253077
253424
  warningEventSchema,
253078
253425
  agentStatusUpdatedEventSchema,
253426
+ contextChangedEventSchema,
253079
253427
  sessionMetaUpdatedEventSchema,
253080
253428
  sessionCreatedEventSchema,
253081
253429
  workspaceCreatedEventSchema,
@@ -266315,9 +266663,16 @@ var init_tool_dedup = __esmMin((() => {
266315
266663
  }));
266316
266664
  //#endregion
266317
266665
  //#region ../../packages/agent-core/src/agent/turn/tool-result-budget.ts
266666
+ function toolResultMaxChars(maxContextTokens) {
266667
+ if (maxContextTokens === void 0 || !Number.isFinite(maxContextTokens) || maxContextTokens <= 0) return TOOL_RESULT_MAX_CHARS;
266668
+ if (maxContextTokens < 1e5) return 16e3;
266669
+ if (maxContextTokens < 2e5) return 32e3;
266670
+ return TOOL_RESULT_MAX_CHARS;
266671
+ }
266318
266672
  async function budgetToolResultForModel(options) {
266319
266673
  const text = persistableToolResultText(options.result.output);
266320
- if (text === void 0 || text.length <= TOOL_RESULT_MAX_CHARS) return options.result;
266674
+ const maxChars = toolResultMaxChars(options.maxContextTokens);
266675
+ if (text === void 0 || text.length <= maxChars) return options.result;
266321
266676
  if (options.result.truncated === true) return options.result;
266322
266677
  if (options.homedir === void 0) return options.result;
266323
266678
  if (text.includes("\0") || Buffer.from(text, "utf8").toString("utf8") !== text || text.split("\n").some((line) => line.length > 2e3)) return options.result;
@@ -266328,7 +266683,7 @@ async function budgetToolResultForModel(options) {
266328
266683
  toolCallId: options.toolCallId
266329
266684
  }, text);
266330
266685
  if (outputPath === void 0) return options.result;
266331
- const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath);
266686
+ const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath, maxChars);
266332
266687
  return options.result.isError === true ? {
266333
266688
  ...options.result,
266334
266689
  output,
@@ -266360,9 +266715,9 @@ async function saveToolResult(options, text) {
266360
266715
  return;
266361
266716
  }
266362
266717
  }
266363
- function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
266718
+ function renderPersistedToolResult(toolName, toolCallId, text, outputPath, maxChars) {
266364
266719
  const lines = [
266365
- `Tool output exceeded ${String(TOOL_RESULT_MAX_CHARS)} characters; showing a preview only.`,
266720
+ `Tool output exceeded ${String(maxChars)} characters; showing a preview only.`,
266366
266721
  `tool_name: ${toolName}`,
266367
266722
  `tool_call_id: ${toolCallId}`,
266368
266723
  `output_size_chars: ${String(text.length)}`,
@@ -268320,14 +268675,27 @@ function blunToolsForOrigin(tools, origin) {
268320
268675
  * the ordinary permission pipeline.
268321
268676
  */
268322
268677
  function blunSelectTurnTools(tools, maxContextTokens, loadedToolNames = /* @__PURE__ */ new Set(), requiredToolNames = /* @__PURE__ */ new Set(), selected = []) {
268323
- if (estimateTokensForTools(tools) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens) || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
268678
+ const schemaBudget = (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens);
268679
+ if (estimateTokensForTools(tools, schemaBudget) <= schemaBudget || tools.some(import_turn_tool_performance_policy.isDeferredToolLoader)) {
268324
268680
  selected.splice(0, selected.length, ...tools);
268325
268681
  return selected;
268326
268682
  }
268327
- 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)));
268328
- const names = new Set(selected.map((tool) => tool.name));
268329
- const deferred = tools.filter((tool) => !names.has(tool.name));
268330
- if (deferred.length > 0) selected.push((0, import_turn_tool_performance_policy.createDeferredToolLoader)(selected, deferred, loadedToolNames));
268683
+ const priorLoaderIndex = selected.findIndex(import_turn_tool_performance_policy.isDeferredToolLoader);
268684
+ const justLoaded = new Set(priorLoaderIndex < 0 ? [] : selected.slice(priorLoaderIndex + 1).filter((tool) => loadedToolNames.has(tool.name)).map((tool) => tool.name));
268685
+ const remembered = new Set(loadedToolNames);
268686
+ const choose = () => {
268687
+ 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)));
268688
+ const names = new Set(selected.map((tool) => tool.name));
268689
+ const deferred = tools.filter((tool) => !names.has(tool.name));
268690
+ if (deferred.length > 0) selected.push((0, import_turn_tool_performance_policy.createDeferredToolLoader)(selected, deferred, loadedToolNames));
268691
+ };
268692
+ choose();
268693
+ for (const name of loadedToolNames) {
268694
+ if (estimateTokensForTools(selected, schemaBudget) <= schemaBudget) break;
268695
+ if (justLoaded.has(name)) continue;
268696
+ if (!remembered.delete(name)) continue;
268697
+ choose();
268698
+ }
268331
268699
  return selected;
268332
268700
  }
268333
268701
  function goalContinuationOrigin(origin) {
@@ -269356,10 +269724,12 @@ var init_turn = __esmMin((() => {
269356
269724
  taskToolsNeeded ||= blunTurnNeedsTools(intent, origin) || blunTurnHasAttachment(toolInput);
269357
269725
  const required = (0, import_turn_tool_performance_policy.mediaToolNamesForTurnText)(blunExtractText(toolInput));
269358
269726
  if (taskToolsNeeded) {
269359
- blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames, required, selectedTools);
269360
- for (const name of (0, import_turn_tool_performance_policy.rankedToolNamesForTurnText)(eligibleTools, blunExtractText(intent))) {
269727
+ const maxContextTokens = this.agent.fullCompaction.getEffectiveMaxContextTokens();
269728
+ blunSelectTurnTools(eligibleTools, maxContextTokens, this.loadedToolNames, required, selectedTools);
269729
+ const trustedToolNames = new Set(eligibleTools.filter((tool) => this.agent.tools.isUserTool(tool)).map((tool) => tool.name));
269730
+ for (const name of (0, import_turn_tool_performance_policy.rankedToolNamesForTurnText)(eligibleTools, blunExtractText(intent), { trustedToolNames })) {
269361
269731
  const tool = eligibleTools.find((candidate) => candidate.name === name);
269362
- 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);
269732
+ if (tool !== void 0 && !selectedTools.some((candidate) => candidate.name === name) && estimateTokensForTools([...selectedTools, tool]) <= (0, import_turn_tool_performance_policy.toolSchemaBudgetTokens)(maxContextTokens)) selectedTools.push(tool);
269363
269733
  }
269364
269734
  } 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)));
269365
269735
  recordPreRequestPhase("tool-selection", selectionStartedAt);
@@ -269500,6 +269870,7 @@ var init_turn = __esmMin((() => {
269500
269870
  });
269501
269871
  return budgetToolResultForModel({
269502
269872
  homedir: this.agent.homedir,
269873
+ maxContextTokens: this.agent.fullCompaction.getEffectiveMaxContextTokens(),
269503
269874
  toolName: ctx.toolCall.name,
269504
269875
  toolCallId: ctx.toolCall.id,
269505
269876
  result: finalResult
@@ -270210,10 +270581,10 @@ var init_bash = __esmMin((() => {
270210
270581
  },
270211
270582
  approvalRule: literalRulePattern(this.name, args.command),
270212
270583
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
270213
- execute: ({ signal, onUpdate, onForegroundTaskStart }) => this.execution(args, signal, onUpdate, onForegroundTaskStart)
270584
+ execute: ({ signal, onUpdate, onForegroundTaskStart, executionOrigin }) => this.execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin)
270214
270585
  };
270215
270586
  }
270216
- spawn(effectiveCwd, command) {
270587
+ spawn(effectiveCwd, command, executionOrigin) {
270217
270588
  const shellCwd = this.isWindowsBash ? windowsPathToPosixPath(effectiveCwd) : effectiveCwd;
270218
270589
  const shellArgs = [
270219
270590
  this.kaos.osEnv.shellPath,
@@ -270230,9 +270601,11 @@ var init_bash = __esmMin((() => {
270230
270601
  ...process.env,
270231
270602
  ...noninteractiveEnv
270232
270603
  };
270604
+ delete mergedEnv["BLUN_SHELL"];
270605
+ if (executionOrigin === "operator_shell") mergedEnv["BLUN_SHELL"] = "operator-local";
270233
270606
  return this.kaos.execWithEnv(shellArgs, mergedEnv);
270234
270607
  }
270235
- async execution(args, signal, onUpdate, onForegroundTaskStart) {
270608
+ async execution(args, signal, onUpdate, onForegroundTaskStart, executionOrigin) {
270236
270609
  const validationError = this.validateRunRequest(args, signal);
270237
270610
  if (validationError !== void 0) return validationError;
270238
270611
  const startsInBackground = args.run_in_background === true;
@@ -270244,7 +270617,7 @@ var init_bash = __esmMin((() => {
270244
270617
  const builder = new ToolResultBuilder();
270245
270618
  let proc;
270246
270619
  try {
270247
- proc = await this.spawn(effectiveCwd, command);
270620
+ proc = await this.spawn(effectiveCwd, command, executionOrigin);
270248
270621
  } catch (error) {
270249
270622
  return {
270250
270623
  isError: true,
@@ -270579,7 +270952,7 @@ var init_web_search = __esmMin((() => {
270579
270952
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.md?raw
270580
270953
  var codebase_search_default;
270581
270954
  var init_codebase_search$1 = __esmMin((() => {
270582
- 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";
270955
+ 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";
270583
270956
  }));
270584
270957
  //#endregion
270585
270958
  //#region ../../packages/agent-core/src/tools/builtin/search/codebase-search.ts
@@ -270587,7 +270960,7 @@ var init_codebase_search$1 = __esmMin((() => {
270587
270960
  * CodebaseSearchTool — semantic code search via the codebase-index.
270588
270961
  *
270589
270962
  * Shells out to `python codebase_index.py query <repo> "<frage>" --top N`
270590
- * through spawnSync. The script lives at
270963
+ * through asynchronous execFile. The script lives at
270591
270964
  * `packages/codebase-index/codebase_index.py` relative to the workspace root.
270592
270965
  *
270593
270966
  * Unlike Grep (keyword/regex) or Glob (file-name patterns), this tool finds
@@ -270611,18 +270984,20 @@ function resolveScriptPath() {
270611
270984
  }
270612
270985
  return "";
270613
270986
  }
270614
- var CodebaseSearchInputSchema, SCRIPT_TIMEOUT_MS, CodebaseSearchTool;
270987
+ var MAX_SEARCH_RESULTS, CodebaseSearchInputSchema, SCRIPT_TIMEOUT_MS, SCRIPT_MAX_BUFFER, CodebaseSearchTool;
270615
270988
  var init_codebase_search = __esmMin((() => {
270616
270989
  init_zod$1();
270617
270990
  init_rule_match();
270618
270991
  init_input_schema();
270619
270992
  init_result_builder();
270620
270993
  init_codebase_search$1();
270994
+ MAX_SEARCH_RESULTS = 100;
270621
270995
  CodebaseSearchInputSchema = object$1({
270622
270996
  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\"."),
270623
- top: number$1().int().positive().default(5).describe("Maximum number of results to return. Defaults to 5.")
270997
+ 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.`)
270624
270998
  });
270625
270999
  SCRIPT_TIMEOUT_MS = 6e4;
271000
+ SCRIPT_MAX_BUFFER = 1024 * 1024;
270626
271001
  CodebaseSearchTool = class {
270627
271002
  cwd;
270628
271003
  name = "CodebaseSearch";
@@ -270645,45 +271020,72 @@ var init_codebase_search = __esmMin((() => {
270645
271020
  },
270646
271021
  approvalRule: literalRulePattern(this.name, args.query),
270647
271022
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.query),
270648
- execute: () => this.execution(args)
271023
+ execute: async (context) => {
271024
+ context.signal.throwIfAborted();
271025
+ return this.execution(args, context.signal);
271026
+ }
270649
271027
  };
270650
271028
  }
270651
- execution(args) {
271029
+ async execution(args, signal) {
270652
271030
  const builder = new ToolResultBuilder();
270653
- if (this.scriptPath.length === 0) return Promise.resolve(builder.error("Could not locate codebase_index.py. Set CODEBASE_INDEX_SCRIPT to its absolute path."));
271031
+ 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)}.`);
271032
+ if (this.scriptPath.length === 0) return builder.error("Could not locate codebase_index.py. Set CODEBASE_INDEX_SCRIPT to its absolute path.");
270654
271033
  try {
270655
- const result = spawnSync("python", [
270656
- this.scriptPath,
270657
- "query",
270658
- this.cwd,
270659
- args.query,
270660
- "--top",
270661
- String(args.top),
270662
- "--model",
270663
- "auto"
270664
- ], {
270665
- cwd: this.cwd,
270666
- timeout: SCRIPT_TIMEOUT_MS,
270667
- encoding: "utf-8",
270668
- windowsHide: true
271034
+ const result = await new Promise((resolve) => {
271035
+ let closed = false;
271036
+ let output;
271037
+ const finish = () => {
271038
+ if (closed && output !== void 0) resolve(output);
271039
+ };
271040
+ execFile("python", [
271041
+ this.scriptPath,
271042
+ "query",
271043
+ this.cwd,
271044
+ args.query,
271045
+ "--top",
271046
+ String(args.top),
271047
+ "--model",
271048
+ "auto"
271049
+ ], {
271050
+ cwd: this.cwd,
271051
+ timeout: SCRIPT_TIMEOUT_MS,
271052
+ encoding: "utf-8",
271053
+ windowsHide: true,
271054
+ shell: false,
271055
+ maxBuffer: SCRIPT_MAX_BUFFER,
271056
+ signal
271057
+ }, (error, stdout, stderr) => {
271058
+ output = {
271059
+ error,
271060
+ stdout,
271061
+ stderr
271062
+ };
271063
+ finish();
271064
+ }).once("close", () => {
271065
+ closed = true;
271066
+ finish();
271067
+ });
270669
271068
  });
270670
- if (result.error !== void 0) {
271069
+ signal.throwIfAborted();
271070
+ if (result.error !== null && typeof result.error.code !== "number") {
270671
271071
  const msg = result.error instanceof Error ? result.error.message : String(result.error);
270672
- return Promise.resolve(builder.error(`Failed to run codebase_index.py: ${msg}`));
271072
+ builder.write(result.stdout + result.stderr);
271073
+ return builder.error(`Failed to run codebase_index.py: ${msg}`);
270673
271074
  }
270674
271075
  const stdout = result.stdout ?? "";
270675
271076
  const stderr = result.stderr ?? "";
270676
- const exitCode = result.status ?? -1;
271077
+ const exitCode = result.error?.code ?? 0;
270677
271078
  if (exitCode !== 0) {
270678
271079
  const combined = stderr.trim().length > 0 ? `${stdout}${stdout.length > 0 ? "\n" : ""}Error: ${stderr.trim()}` : stdout;
270679
271080
  builder.write(combined.length > 0 ? combined : `codebase_index.py exited with code ${String(exitCode)}`);
270680
- return Promise.resolve(builder.error(`Codebase search failed with exit code ${String(exitCode)}.`));
271081
+ return builder.error(`Codebase search failed with exit code ${String(exitCode)}.`);
270681
271082
  }
270682
271083
  builder.write(stdout.length > 0 ? stdout : "No results found.");
270683
- return Promise.resolve(builder.ok());
271084
+ return builder.ok();
270684
271085
  } catch (error) {
271086
+ signal.throwIfAborted();
270685
271087
  const msg = error instanceof Error ? error.message : String(error);
270686
- return Promise.resolve(builder.error(`Codebase search error: ${msg}`));
271088
+ return builder.error(`Codebase search error: ${msg}`);
270687
271089
  }
270688
271090
  }
270689
271091
  };
@@ -272017,6 +272419,7 @@ var init_tool$1 = __esmMin((() => {
272017
272419
  const result = await execution.execute({
272018
272420
  turnId: "",
272019
272421
  toolCallId: "shell-command",
272422
+ executionOrigin: "operator_shell",
272020
272423
  signal: controller.signal,
272021
272424
  onUpdate: (update) => {
272022
272425
  if (update.kind === "stdout") stdout += update.text ?? "";
@@ -272105,6 +272508,9 @@ var init_tool$1 = __esmMin((() => {
272105
272508
  hasUserTool(name) {
272106
272509
  return this.userTools.has(name);
272107
272510
  }
272511
+ isUserTool(tool) {
272512
+ return this.userTools.get(tool.name) === tool;
272513
+ }
272108
272514
  inheritUserTools(parent) {
272109
272515
  for (const tool of parent.userTools.values()) {
272110
272516
  if (!parent.enabledTools.has(tool.name)) continue;
@@ -273718,7 +274124,7 @@ var init_agent = __esmMin((() => {
273718
274124
  fitProfileInstructions(profile, context, tools = this.tools.loopTools, notify = false) {
273719
274125
  if (context?.agentsMdText === void 0 || this.config.modelAlias === void 0) return context;
273720
274126
  if (this.config.runtimeModel(this.config.modelAlias).provider.name !== "blun") return context;
273721
- const fitted = fitAgentsMdContext(context, 4e5, (candidate) => {
274127
+ const renderFixedPrompt = (candidate) => {
273722
274128
  const rendered = profile.systemPrompt({
273723
274129
  osEnv: this.kaos.osEnv,
273724
274130
  now: this.systemPromptStartedAt,
@@ -273729,13 +274135,35 @@ var init_agent = __esmMin((() => {
273729
274135
  additionalDirsInfo: candidate.additionalDirsInfo
273730
274136
  });
273731
274137
  const compact = this.skills?.registry === void 0 ? rendered : compactLegacyModelSkillListing(rendered, this.skills.registry);
273732
- return estimateBlunSerializedBodyBytes(this.runtimeSystemPromptAppend.length === 0 ? compact : compact.length === 0 ? this.runtimeSystemPromptAppend : `${compact}\n\n${this.runtimeSystemPromptAppend}`, tools, []) + 4096;
274138
+ return this.runtimeSystemPromptAppend.length === 0 ? compact : compact.length === 0 ? this.runtimeSystemPromptAppend : `${compact}\n\n${this.runtimeSystemPromptAppend}`;
274139
+ };
274140
+ const fitted = fitAgentsMdContext(context, 4e5, (candidate) => {
274141
+ return estimateBlunSerializedBodyBytes(renderFixedPrompt(candidate), tools, []) + 4096;
274142
+ });
274143
+ const describeFixedEstimate = () => {
274144
+ const systemPrompt = renderFixedPrompt(fitted.context);
274145
+ const envelopeBytes = estimateBlunSerializedBodyBytes("", [], []);
274146
+ const withoutToolsBytes = estimateBlunSerializedBodyBytes(systemPrompt, [], []);
274147
+ return {
274148
+ estimationStage: notify ? "request" : "profile",
274149
+ toolCount: tools.length,
274150
+ serializedPromptBytes: withoutToolsBytes - envelopeBytes,
274151
+ serializedToolsBytes: fitted.fixedRequestBytes - 4096 - withoutToolsBytes,
274152
+ envelopeBytes,
274153
+ reserveBytes: 4096,
274154
+ instructionBytes: fitted.context.agentsMdSources?.totalBytes ?? 0,
274155
+ runtimeAppendBytes: Buffer.byteLength(this.runtimeSystemPromptAppend, "utf8")
274156
+ };
274157
+ };
274158
+ if (!fitted.fits) this.log.warn("fixed BLUN request exceeds 400000 bytes without AGENTS.md", {
274159
+ fixedRequestBytes: fitted.fixedRequestBytes,
274160
+ ...describeFixedEstimate()
273733
274161
  });
273734
- if (!fitted.fits) this.log.warn("fixed BLUN request exceeds 400000 bytes without AGENTS.md", { fixedRequestBytes: fitted.fixedRequestBytes });
273735
274162
  else if (fitted.omittedBytes > 0) {
273736
274163
  this.log.warn("AGENTS.md content shortened for BLUN request budget", {
273737
274164
  omittedBytes: fitted.omittedBytes,
273738
- fixedRequestBytes: fitted.fixedRequestBytes
274165
+ fixedRequestBytes: fitted.fixedRequestBytes,
274166
+ ...describeFixedEstimate()
273739
274167
  });
273740
274168
  if (notify && !this.agentsMdRequestWarningShown) {
273741
274169
  this.agentsMdRequestWarningShown = true;
@@ -273949,6 +274377,9 @@ var init_agent = __esmMin((() => {
273949
274377
  },
273950
274378
  startBtw: () => this.subagentHost.startBtw(),
273951
274379
  createGoal: (payload) => this.goal.createGoal(payload),
274380
+ editGoal: (payload) => this.goal.editGoal(payload),
274381
+ blockGoal: (payload) => this.goal.blockGoal(payload),
274382
+ completeGoal: () => this.goal.completeGoal(),
273952
274383
  getGoal: () => this.goal.getGoal(),
273953
274384
  pauseGoal: () => this.goal.pauseGoal(),
273954
274385
  resumeGoal: () => this.goal.resumeGoal(),
@@ -339272,6 +339703,15 @@ var init_rpc$1 = __esmMin((() => {
339272
339703
  async createGoal({ agentId, ...payload }) {
339273
339704
  return (await this.getAgent(agentId)).createGoal(payload);
339274
339705
  }
339706
+ async editGoal({ agentId, ...payload }) {
339707
+ return (await this.getAgent(agentId)).editGoal(payload);
339708
+ }
339709
+ async blockGoal({ agentId, ...payload }) {
339710
+ return (await this.getAgent(agentId)).blockGoal(payload);
339711
+ }
339712
+ async completeGoal({ agentId }) {
339713
+ return (await this.getAgent(agentId)).completeGoal({});
339714
+ }
339275
339715
  async getGoal({ agentId, ...payload }) {
339276
339716
  return (await this.getAgent(agentId, true)).getGoal(payload);
339277
339717
  }
@@ -340706,6 +341146,15 @@ var init_core_impl = __esmMin((() => {
340706
341146
  getGoal({ sessionId, ...payload }) {
340707
341147
  return Promise.resolve(this.sessionApi(sessionId).getGoal(payload));
340708
341148
  }
341149
+ editGoal({ sessionId, ...payload }) {
341150
+ return Promise.resolve(this.sessionApi(sessionId).editGoal(payload));
341151
+ }
341152
+ blockGoal({ sessionId, ...payload }) {
341153
+ return Promise.resolve(this.sessionApi(sessionId).blockGoal(payload));
341154
+ }
341155
+ completeGoal({ sessionId, ...payload }) {
341156
+ return Promise.resolve(this.sessionApi(sessionId).completeGoal(payload));
341157
+ }
340709
341158
  pauseGoal({ sessionId, ...payload }) {
340710
341159
  return Promise.resolve(this.sessionApi(sessionId).pauseGoal(payload));
340711
341160
  }
@@ -348319,7 +348768,10 @@ function reduceWireRecords(records) {
348319
348768
  text: record.summary
348320
348769
  }],
348321
348770
  toolCalls: [],
348322
- origin: { kind: "compaction_summary" }
348771
+ origin: {
348772
+ kind: "compaction_summary",
348773
+ ...record.toolContinuity === void 0 ? {} : { toolContinuity: record.toolContinuity }
348774
+ }
348323
348775
  },
348324
348776
  time: record.time
348325
348777
  };
@@ -350593,6 +351045,24 @@ var Session = class {
350593
351045
  this.ensureOpen();
350594
351046
  return this.rpc.getGoal({ sessionId: this.id });
350595
351047
  }
351048
+ async editGoal(input) {
351049
+ this.ensureOpen();
351050
+ return this.rpc.editGoal({
351051
+ sessionId: this.id,
351052
+ objective: input.objective
351053
+ });
351054
+ }
351055
+ async blockGoal(input = {}) {
351056
+ this.ensureOpen();
351057
+ return this.rpc.blockGoal({
351058
+ sessionId: this.id,
351059
+ reason: input.reason
351060
+ });
351061
+ }
351062
+ async completeGoal() {
351063
+ this.ensureOpen();
351064
+ return this.rpc.completeGoal({ sessionId: this.id });
351065
+ }
350596
351066
  async pauseGoal() {
350597
351067
  this.ensureOpen();
350598
351068
  return this.rpc.pauseGoal({ sessionId: this.id });
@@ -351982,6 +352452,26 @@ var SDKRpcClientBase = class {
351982
352452
  replace: input.replace
351983
352453
  });
351984
352454
  }
352455
+ async editGoal(input) {
352456
+ return (await this.getRpc()).editGoal({
352457
+ sessionId: input.sessionId,
352458
+ agentId: this.interactiveAgentId,
352459
+ objective: input.objective
352460
+ });
352461
+ }
352462
+ async blockGoal(input) {
352463
+ return (await this.getRpc()).blockGoal({
352464
+ sessionId: input.sessionId,
352465
+ agentId: this.interactiveAgentId,
352466
+ reason: input.reason
352467
+ });
352468
+ }
352469
+ async completeGoal(input) {
352470
+ return (await this.getRpc()).completeGoal({
352471
+ sessionId: input.sessionId,
352472
+ agentId: this.interactiveAgentId
352473
+ });
352474
+ }
351985
352475
  async getGoal(input) {
351986
352476
  return (await this.getRpc()).getGoal({
351987
352477
  sessionId: input.sessionId,
@@ -426659,7 +427149,7 @@ async function searchLocalToolEvents(homeDir, sessions, query, limit = 8) {
426659
427149
  skippedRecords++;
426660
427150
  continue;
426661
427151
  }
426662
- const searchable = `${event.tool} ${event.filePath ?? ""}`.normalize("NFKC").toLocaleLowerCase();
427152
+ const searchable = `${event.tool} ${event.status} ${event.filePath ?? ""}`.normalize("NFKC").toLocaleLowerCase();
426663
427153
  if (terms.every((term) => searchable.includes(term))) {
426664
427154
  hits.push(event);
426665
427155
  hits.sort((a, b) => b.at - a.at || a.sessionId.localeCompare(b.sessionId));
@@ -439574,7 +440064,7 @@ function buildUsageReportLines(options) {
439574
440064
  const errorStyle = (text) => currentTheme.fg("error", text);
439575
440065
  const severityColor = (sev) => sev === "danger" ? "error" : sev === "warn" ? "warning" : "success";
439576
440066
  const lines = [accent(uiText("usage.session.title")), ...buildSessionUsageSection(options.sessionUsage, options.sessionUsageError, value, muted, errorStyle)];
439577
- if (options.maxContextTokens > 0) {
440067
+ if (Number.isFinite(options.maxContextTokens) && options.maxContextTokens > 0) {
439578
440068
  const ratio = safeUsageRatio(options.contextTokens / options.maxContextTokens);
439579
440069
  const bar = renderProgressBar(ratio, 20);
439580
440070
  const pct = `${(ratio * 100).toFixed(1)}%`;
@@ -439825,7 +440315,7 @@ function buildStatusReportLines(options) {
439825
440315
  const { ratio, tokens, maxTokens } = contextValues(options);
439826
440316
  lines.push("");
439827
440317
  lines.push(accent(uiText("usage.context.title")));
439828
- if (maxTokens > 0) {
440318
+ if (Number.isFinite(maxTokens) && maxTokens > 0) {
439829
440319
  const safeRatio = safeUsageRatio(ratio);
439830
440320
  const bar = renderProgressBar(safeRatio, 20);
439831
440321
  const barColoured = currentTheme.fg(severityToken(ratioSeverity(safeRatio)), bar);
@@ -440776,11 +441266,18 @@ registerUiCatalogFragment({
440776
441266
  //#endregion
440777
441267
  //#region src/tui/utils/event-payload.ts
440778
441268
  function appendStreamingArgsPreview(current, next) {
440779
- const existing = (current ?? "").slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
441269
+ let existing = (current ?? "").slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
441270
+ const boundary = existing.charCodeAt(existing.length - 1);
441271
+ if (existing.length === 65536 && boundary >= 55296 && boundary <= 56319) existing = existing.slice(0, -1);
440780
441272
  if (next === null || next === void 0 || next.length === 0) return existing;
440781
441273
  const remaining = STREAMING_ARGS_PREVIEW_MAX_CHARS - existing.length;
440782
441274
  if (remaining <= 0) return existing;
440783
- return `${existing}${next.slice(0, remaining)}`;
441275
+ const first = next.charCodeAt(0);
441276
+ const previous = existing.charCodeAt(existing.length - 1);
441277
+ const continuation = first >= 56320 && first <= 57343 && !(previous >= 55296 && previous <= 56319) ? next.slice(1) : next;
441278
+ const preview = `${existing}${continuation.slice(0, remaining)}`;
441279
+ const last = preview.charCodeAt(preview.length - 1);
441280
+ return preview.length === 65536 && last >= 55296 && last <= 56319 ? preview.slice(0, -1) : preview;
440784
441281
  }
440785
441282
  function unescapeJsonString$1(s) {
440786
441283
  return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
@@ -502691,6 +503188,7 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
502691
503188
  const tokens = trimmed.split(/\s+/);
502692
503189
  const action = tokens[0]?.toLowerCase();
502693
503190
  if (action === "next") return "always";
503191
+ if (action === "edit" || action === "block") return "always";
502694
503192
  if (tokens.length === 1 && (action === "status" || action === "pause" || action === "paused" || action === "cancel" || action === "clear" || action === "stop")) return "always";
502695
503193
  return "idle-only";
502696
503194
  }
@@ -509515,7 +510013,9 @@ registerUiCatalogFragment({
509515
510013
  //#region src/tui/commands/goal.ts
509516
510014
  const MAX_GOAL_OBJECTIVE_LENGTH = 4e3;
509517
510015
  const RESUME_GOAL_INPUT = "Resume the active goal.";
510016
+ const pendingGoalEdits = /* @__PURE__ */ new WeakMap();
509518
510017
  const CONTROL_SUBCOMMANDS = new Map([
510018
+ ["complete", "complete"],
509519
510019
  ["pause", "pause"],
509520
510020
  ["paused", "pause"],
509521
510021
  ["resume", "resume"],
@@ -509537,6 +510037,26 @@ function parseGoalCommand(rawArgs) {
509537
510037
  if (args.length === 0 || args.toLowerCase() === "status") return { kind: "status" };
509538
510038
  const tokens = args.split(/\s+/);
509539
510039
  const first = tokens[0]?.toLowerCase();
510040
+ if (first === "block") return {
510041
+ kind: "block",
510042
+ reason: tokens.slice(1).join(" ").trim() || void 0
510043
+ };
510044
+ if (first === "edit") {
510045
+ const objective = tokens.slice(1).join(" ").trim();
510046
+ if (objective.length === 0) return {
510047
+ kind: "error",
510048
+ severity: "hint",
510049
+ message: uiText("goal.error.objectiveRequired")
510050
+ };
510051
+ if (objective.length > MAX_GOAL_OBJECTIVE_LENGTH) return {
510052
+ kind: "error",
510053
+ message: uiText("goal.error.objectiveTooLong", { max: MAX_GOAL_OBJECTIVE_LENGTH })
510054
+ };
510055
+ return {
510056
+ kind: "edit",
510057
+ objective
510058
+ };
510059
+ }
509540
510060
  if (first === "next") return parseNextGoalCommand(tokens);
509541
510061
  const control = first === void 0 ? void 0 : CONTROL_SUBCOMMANDS.get(first);
509542
510062
  if (control !== void 0 && tokens.length === 1) return { kind: control };
@@ -509574,6 +510094,15 @@ async function handleGoalCommand(host, args) {
509574
510094
  case "status":
509575
510095
  await showGoalStatus(host);
509576
510096
  return;
510097
+ case "edit":
510098
+ await editGoal(host, parsed.objective);
510099
+ return;
510100
+ case "block":
510101
+ await blockGoal(host, parsed.reason);
510102
+ return;
510103
+ case "complete":
510104
+ await completeGoal(host);
510105
+ return;
509577
510106
  case "pause":
509578
510107
  await pauseGoal(host);
509579
510108
  return;
@@ -509819,12 +510348,78 @@ async function startGoal(host, parsed, options) {
509819
510348
  else host.sendNormalUserInput(parsed.objective);
509820
510349
  return true;
509821
510350
  }
510351
+ async function completeGoal(host) {
510352
+ if (isBusy(host)) {
510353
+ host.showStatus(slashBusyMessage("goal", host.state.appState.isCompacting ? "compacting" : "streaming"));
510354
+ return;
510355
+ }
510356
+ const session = host.requireSession();
510357
+ pendingGoalEdits.delete(host);
510358
+ try {
510359
+ const result = await session.completeGoal();
510360
+ if (host.session !== session || result !== null) return;
510361
+ const { goal } = await session.getGoal();
510362
+ if (host.session !== session) return;
510363
+ host.setAppState({ goal });
510364
+ if (goal === null) host.showStatus(uiText("goal.status.noneSet"));
510365
+ else renderGoalStatus(host, goal);
510366
+ } catch (error) {
510367
+ if (host.session !== session) return;
510368
+ if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
510369
+ host.showStatus(uiText("goal.status.noneSet"));
510370
+ return;
510371
+ }
510372
+ host.showError(formatErrorMessage$2(error));
510373
+ }
510374
+ }
510375
+ async function blockGoal(host, reason) {
510376
+ const session = host.requireSession();
510377
+ pendingGoalEdits.delete(host);
510378
+ try {
510379
+ const goal = await session.blockGoal({ reason });
510380
+ if (host.session !== session) return;
510381
+ if (isStreaming$1(host)) await session.cancel();
510382
+ if (host.session !== session) return;
510383
+ host.setAppState({ goal });
510384
+ renderGoalStatus(host, goal);
510385
+ } catch (error) {
510386
+ if (host.session !== session) return;
510387
+ if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
510388
+ host.showStatus(uiText("goal.status.noneSet"));
510389
+ return;
510390
+ }
510391
+ host.showError(formatErrorMessage$2(error));
510392
+ }
510393
+ }
510394
+ async function editGoal(host, objective) {
510395
+ const session = host.requireSession();
510396
+ const request = Symbol("goal-edit");
510397
+ pendingGoalEdits.set(host, request);
510398
+ try {
510399
+ const goal = await session.editGoal({ objective });
510400
+ if (host.session !== session || pendingGoalEdits.get(host) !== request) return;
510401
+ host.setAppState({ goal });
510402
+ renderGoalStatus(host, goal);
510403
+ } catch (error) {
510404
+ if (host.session !== session || pendingGoalEdits.get(host) !== request) return;
510405
+ if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
510406
+ host.showStatus(uiText("goal.status.noneSet"));
510407
+ return;
510408
+ }
510409
+ host.showError(formatErrorMessage$2(error));
510410
+ } finally {
510411
+ if (pendingGoalEdits.get(host) === request) pendingGoalEdits.delete(host);
510412
+ }
510413
+ }
509822
510414
  async function pauseGoal(host) {
509823
510415
  const session = host.requireSession();
510416
+ pendingGoalEdits.delete(host);
509824
510417
  try {
509825
510418
  await session.pauseGoal();
510419
+ if (host.session !== session) return;
509826
510420
  if (isStreaming$1(host)) await session.cancel();
509827
510421
  } catch (error) {
510422
+ if (host.session !== session) return;
509828
510423
  if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
509829
510424
  host.showStatus(uiText("goal.status.noneToPause"));
509830
510425
  return;
@@ -509832,6 +510427,7 @@ async function pauseGoal(host) {
509832
510427
  host.showError(formatErrorMessage$2(error));
509833
510428
  return;
509834
510429
  }
510430
+ if (host.session !== session) return;
509835
510431
  host.track("goal_pause");
509836
510432
  host.showStatus(uiText("goal.status.paused"));
509837
510433
  }
@@ -509840,9 +510436,12 @@ async function resumeGoal(host) {
509840
510436
  host.showError(uiText("goal.error.modelNotSet"));
509841
510437
  return;
509842
510438
  }
510439
+ const session = host.requireSession();
510440
+ pendingGoalEdits.delete(host);
509843
510441
  try {
509844
- await host.requireSession().resumeGoal();
510442
+ await session.resumeGoal();
509845
510443
  } catch (error) {
510444
+ if (host.session !== session) return;
509846
510445
  if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
509847
510446
  host.showStatus(uiText("goal.status.noneToResume"));
509848
510447
  return;
@@ -509850,15 +510449,19 @@ async function resumeGoal(host) {
509850
510449
  host.showError(formatErrorMessage$2(error));
509851
510450
  return;
509852
510451
  }
510452
+ if (host.session !== session) return;
509853
510453
  host.track("goal_resume");
509854
510454
  host.sendNormalUserInput(RESUME_GOAL_INPUT);
509855
510455
  }
509856
510456
  async function cancelGoal(host) {
509857
510457
  const session = host.requireSession();
510458
+ pendingGoalEdits.delete(host);
509858
510459
  try {
509859
510460
  await session.cancelGoal();
510461
+ if (host.session !== session) return;
509860
510462
  if (isStreaming$1(host)) await session.cancel();
509861
510463
  } catch (error) {
510464
+ if (host.session !== session) return;
509862
510465
  if (isBlunError(error) && error.code === ErrorCodes.GOAL_NOT_FOUND) {
509863
510466
  host.showStatus(uiText("goal.status.noneToCancel"));
509864
510467
  return;
@@ -509866,6 +510469,7 @@ async function cancelGoal(host) {
509866
510469
  host.showError(formatErrorMessage$2(error));
509867
510470
  return;
509868
510471
  }
510472
+ if (host.session !== session) return;
509869
510473
  host.track("goal_cancel");
509870
510474
  host.showNotice(uiText("goal.status.cancelled"));
509871
510475
  }
@@ -509876,6 +510480,9 @@ async function showGoalStatus(host) {
509876
510480
  host.showStatus(uiText("goal.status.noneSet"));
509877
510481
  return;
509878
510482
  }
510483
+ renderGoalStatus(host, goal);
510484
+ }
510485
+ function renderGoalStatus(host, goal) {
509879
510486
  if (host.getChannelCommandContext?.() !== void 0) {
509880
510487
  host.showStatus(`${goalPanelTitle(goal).trim()}\n${goal.objective}`);
509881
510488
  return;
@@ -521188,6 +521795,196 @@ function numberField(record, key) {
521188
521795
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
521189
521796
  }
521190
521797
  //#endregion
521798
+ //#region src/tui/components/media/image-thumbnail.ts
521799
+ /**
521800
+ * Transcript-side rendering of a pasted image.
521801
+ *
521802
+ * On terminals that speak the Kitty graphics protocol or iTerm2 inline
521803
+ * image protocol (detected by pi-tui's `getCapabilities()`), we show
521804
+ * the actual image. Everywhere else we fall back to a one-line text
521805
+ * marker matching the placeholder the user sees in the input box —
521806
+ * this keeps the transcript readable on Terminal.app / Linux default
521807
+ * terminals / `script` recordings without extra chrome.
521808
+ *
521809
+ * Height is capped at ~12 rows so a single screenshot can't monopolize
521810
+ * the viewport; pi-tui handles proportional scaling internally.
521811
+ */
521812
+ const MAX_IMAGE_ROWS = 12;
521813
+ const MAX_IMAGE_WIDTH = 40;
521814
+ var ImageThumbnail = class extends Container {
521815
+ attachment;
521816
+ image;
521817
+ lastBuiltProtocol;
521818
+ constructor(attachment) {
521819
+ super();
521820
+ this.attachment = attachment;
521821
+ this.buildChildren();
521822
+ }
521823
+ buildChildren() {
521824
+ this.clear();
521825
+ const caps = getCapabilities();
521826
+ const supportsInline = caps.images === "kitty" || caps.images === "iterm2";
521827
+ this.lastBuiltProtocol = caps.images;
521828
+ if (!supportsInline) {
521829
+ this.addChild(new Text(currentTheme.fg("accent", this.attachment.placeholder), 0, 0));
521830
+ return;
521831
+ }
521832
+ const theme = { fallbackColor: (s) => currentTheme.fg("textDim", s) };
521833
+ this.image ??= new Image(Buffer.from(this.attachment.bytes).toString("base64"), this.attachment.mime, theme, {
521834
+ maxHeightCells: MAX_IMAGE_ROWS,
521835
+ maxWidthCells: MAX_IMAGE_WIDTH,
521836
+ filename: this.attachment.placeholder
521837
+ }, {
521838
+ widthPx: this.attachment.width,
521839
+ heightPx: this.attachment.height
521840
+ });
521841
+ this.image.invalidate();
521842
+ this.addChild(this.image);
521843
+ }
521844
+ render(width) {
521845
+ const safeWidth = Math.max(0, width);
521846
+ if (safeWidth === 0) return [""];
521847
+ if (safeWidth < MAX_IMAGE_WIDTH + 2) return new Text(currentTheme.fg("accent", this.attachment.placeholder), 0, 0).render(safeWidth);
521848
+ const caps = getCapabilities();
521849
+ if (this.lastBuiltProtocol !== caps.images) this.buildChildren();
521850
+ return super.render(safeWidth);
521851
+ }
521852
+ invalidate() {
521853
+ this.buildChildren();
521854
+ super.invalidate();
521855
+ }
521856
+ };
521857
+ //#endregion
521858
+ //#region src/utils/image/image-mime.ts
521859
+ function parseImageMeta(bytes) {
521860
+ if (isPng(bytes)) return parsePng(bytes);
521861
+ if (isJpeg(bytes)) return parseJpeg(bytes);
521862
+ if (isGif(bytes)) return parseGif(bytes);
521863
+ if (isWebp(bytes)) return parseWebp(bytes);
521864
+ return null;
521865
+ }
521866
+ function isPng(b) {
521867
+ return b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71 && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10;
521868
+ }
521869
+ function parsePng(b) {
521870
+ if (b.length < 24) return null;
521871
+ const width = readUInt32BE(b, 16);
521872
+ const height = readUInt32BE(b, 20);
521873
+ if (width <= 0 || height <= 0) return null;
521874
+ return {
521875
+ mime: "image/png",
521876
+ width,
521877
+ height
521878
+ };
521879
+ }
521880
+ function isJpeg(b) {
521881
+ return b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255;
521882
+ }
521883
+ function parseJpeg(b) {
521884
+ let i = 2;
521885
+ while (i < b.length) {
521886
+ if (b[i] !== 255) {
521887
+ i += 1;
521888
+ continue;
521889
+ }
521890
+ while (i < b.length && b[i] === 255) i += 1;
521891
+ if (i >= b.length) return null;
521892
+ const marker = b[i];
521893
+ i += 1;
521894
+ if (marker === 216 || marker === 217) continue;
521895
+ if (i + 1 >= b.length) return null;
521896
+ const segLen = readUInt16BE(b, i);
521897
+ if (isSofMarker(marker)) {
521898
+ if (i + 7 >= b.length) return null;
521899
+ const height = readUInt16BE(b, i + 3);
521900
+ const width = readUInt16BE(b, i + 5);
521901
+ if (width <= 0 || height <= 0) return null;
521902
+ return {
521903
+ mime: "image/jpeg",
521904
+ width,
521905
+ height
521906
+ };
521907
+ }
521908
+ i += segLen;
521909
+ }
521910
+ return null;
521911
+ }
521912
+ function isSofMarker(marker) {
521913
+ if (marker < 192 || marker > 207) return false;
521914
+ return marker !== 196 && marker !== 200 && marker !== 204;
521915
+ }
521916
+ function isGif(b) {
521917
+ return b.length >= 6 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56 && (b[4] === 55 || b[4] === 57) && b[5] === 97;
521918
+ }
521919
+ function parseGif(b) {
521920
+ if (b.length < 10) return null;
521921
+ const width = readUInt16LE(b, 6);
521922
+ const height = readUInt16LE(b, 8);
521923
+ if (width <= 0 || height <= 0) return null;
521924
+ return {
521925
+ mime: "image/gif",
521926
+ width,
521927
+ height
521928
+ };
521929
+ }
521930
+ function isWebp(b) {
521931
+ return b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80;
521932
+ }
521933
+ function parseWebp(b) {
521934
+ if (b.length < 30) return null;
521935
+ const chunk = String.fromCodePoint(b[12], b[13], b[14], b[15]);
521936
+ if (chunk === "VP8 ") {
521937
+ const widthRaw = readUInt16LE(b, 26);
521938
+ const heightRaw = readUInt16LE(b, 28);
521939
+ const width = widthRaw & 16383;
521940
+ const height = heightRaw & 16383;
521941
+ if (width <= 0 || height <= 0) return null;
521942
+ return {
521943
+ mime: "image/webp",
521944
+ width,
521945
+ height
521946
+ };
521947
+ }
521948
+ if (chunk === "VP8L") {
521949
+ if (b[20] !== 47) return null;
521950
+ const b1 = b[21];
521951
+ const b2 = b[22];
521952
+ const b3 = b[23];
521953
+ const b4 = b[24];
521954
+ const width = 1 + ((b2 & 63) << 8 | b1);
521955
+ const height = 1 + ((b4 & 15) << 10 | b3 << 2 | (b2 & 192) >> 6);
521956
+ if (width <= 0 || height <= 0) return null;
521957
+ return {
521958
+ mime: "image/webp",
521959
+ width,
521960
+ height
521961
+ };
521962
+ }
521963
+ if (chunk === "VP8X") {
521964
+ const width = 1 + readUInt24LE(b, 24);
521965
+ const height = 1 + readUInt24LE(b, 27);
521966
+ if (width <= 0 || height <= 0) return null;
521967
+ return {
521968
+ mime: "image/webp",
521969
+ width,
521970
+ height
521971
+ };
521972
+ }
521973
+ return null;
521974
+ }
521975
+ function readUInt16BE(b, off) {
521976
+ return b[off] << 8 | b[off + 1];
521977
+ }
521978
+ function readUInt16LE(b, off) {
521979
+ return b[off] | b[off + 1] << 8;
521980
+ }
521981
+ function readUInt24LE(b, off) {
521982
+ return b[off] | b[off + 1] << 8 | b[off + 2] << 16;
521983
+ }
521984
+ function readUInt32BE(b, off) {
521985
+ return Math.trunc(b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3]);
521986
+ }
521987
+ //#endregion
521191
521988
  //#region src/tui/components/messages/tool-renderers/media.copy.ts
521192
521989
  registerUiCatalogFragment({
521193
521990
  en: { "mediaChip.uploaded": "uploaded" },
@@ -521202,6 +521999,51 @@ registerUiCatalogFragment({
521202
521999
  const PATH_TAG_RE = /^<(image|video)\s+path="([^"]+)">$/;
521203
522000
  const ORIGINAL_SIZE_RE = /original size\s+(\d+x\d+px)/;
521204
522001
  const DATA_URL_RE = /^data:([^;]+);base64,(.*)$/s;
522002
+ const MAX_PREVIEW_BYTES = 8 * 1024 * 1024;
522003
+ const MAX_PREVIEW_IMAGES = 4;
522004
+ function imagePreviews(output) {
522005
+ const protocol = getCapabilities().images;
522006
+ if (protocol !== "kitty" && protocol !== "iterm2") return [];
522007
+ let parts;
522008
+ try {
522009
+ parts = JSON.parse(output);
522010
+ } catch {
522011
+ return [];
522012
+ }
522013
+ if (!Array.isArray(parts)) return [];
522014
+ const previews = [];
522015
+ let remainingBytes = MAX_PREVIEW_BYTES;
522016
+ for (const raw of parts) {
522017
+ if (previews.length >= MAX_PREVIEW_IMAGES || remainingBytes <= 0) break;
522018
+ if (typeof raw !== "object" || raw === null) continue;
522019
+ const part = raw;
522020
+ if (part["type"] !== "image_url") continue;
522021
+ const holder = part["imageUrl"];
522022
+ if (typeof holder !== "object" || holder === null) continue;
522023
+ const url = holder["url"];
522024
+ if (typeof url !== "string") continue;
522025
+ const data = DATA_URL_RE.exec(url);
522026
+ if (data === null || data[2] === void 0) continue;
522027
+ const encoded = data[2];
522028
+ if (bytesFromBase64(encoded) > remainingBytes || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) continue;
522029
+ const bytes = Buffer.from(encoded, "base64");
522030
+ if (bytes.length > remainingBytes) continue;
522031
+ const meta = parseImageMeta(bytes);
522032
+ if (meta === null || meta.mime !== data[1]) continue;
522033
+ const attachment = {
522034
+ id: 0,
522035
+ kind: "image",
522036
+ bytes,
522037
+ mime: meta.mime,
522038
+ width: meta.width,
522039
+ height: meta.height,
522040
+ placeholder: `${meta.mime} (${meta.width}x${meta.height})`
522041
+ };
522042
+ previews.push(new ImageThumbnail(attachment));
522043
+ remainingBytes -= bytes.length;
522044
+ }
522045
+ return previews;
522046
+ }
521205
522047
  function bytesFromBase64(b64) {
521206
522048
  const len = b64.length;
521207
522049
  if (len === 0) return 0;
@@ -521285,7 +522127,6 @@ const readMediaChip = (_toolCall, result) => {
521285
522127
  return `${summary.kind} (${meta.join(", ")})`;
521286
522128
  };
521287
522129
  const readMediaSummary = (toolCall, result, ctx) => {
521288
- if (result.is_error) return renderTruncated(toolCall, result, ctx);
521289
522130
  const summary = parseReadMediaOutput(result.output);
521290
522131
  if (summary === null) return renderTruncated(toolCall, result, ctx);
521291
522132
  if (!ctx.expanded) return [];
@@ -521297,6 +522138,22 @@ const readMediaSummary = (toolCall, result, ctx) => {
521297
522138
  if (meta.length > 0) tail.push(meta.join(", "));
521298
522139
  if (summary.url !== void 0) tail.push(summary.url);
521299
522140
  out.push(new Text(` ${dim(tail.join(" · "))}`, 0, 0));
522141
+ if (!result.is_error && summary.kind === "image") out.push(...imagePreviews(result.output));
522142
+ return out;
522143
+ };
522144
+ const genericMediaResult = (toolCall, result, ctx) => {
522145
+ if (!result.output.includes("\"image_url\"") && !result.output.includes("\"video_url\"")) return renderTruncated(toolCall, result, ctx);
522146
+ const summary = parseReadMediaOutput(result.output);
522147
+ if (summary === null) return renderTruncated(toolCall, result, ctx);
522148
+ const textParts = JSON.parse(result.output).filter((part) => typeof part === "object" && part !== null && part["type"] === "text" && typeof part["text"] === "string").map((part) => part.text);
522149
+ const meta = metaSegments(summary);
522150
+ const label = [summary.kind, ...meta].join(" · ");
522151
+ const out = [new Text(` ${chalk.dim(label)}`, 0, 0)];
522152
+ if (textParts.length > 0) out.push(...renderTruncated(toolCall, {
522153
+ ...result,
522154
+ output: textParts.join("\n")
522155
+ }, ctx));
522156
+ if (ctx.expanded && !result.is_error) out.push(...imagePreviews(result.output));
521300
522157
  return out;
521301
522158
  };
521302
522159
  //#endregion
@@ -521463,8 +522320,8 @@ const globSummary = withGlance(globGlance);
521463
522320
  *
521464
522321
  * Each tool name maps to a `ResultRenderer` that turns the tool's
521465
522322
  * `ToolResultBlockData` into renderable Components. Tools without an
521466
- * explicit entry fall through to `renderTruncated` (the original
521467
- * 3-line + ctrl+o behavior).
522323
+ * explicit entry fall through to the generic renderer, which keeps the
522324
+ * original 3-line + ctrl+o behavior for text results.
521468
522325
  *
521469
522326
  * Keep this dispatch flat — tool names live next to the renderer they
521470
522327
  * choose, so adding a new tool means appending one case.
@@ -521476,7 +522333,7 @@ const globSummary = withGlance(globGlance);
521476
522333
  * the main agent previews it.
521477
522334
  */
521478
522335
  function isGenericToolResult(toolName) {
521479
- return pickResultRenderer(toolName) === renderTruncated;
522336
+ return pickResultRenderer(toolName) === genericMediaResult;
521480
522337
  }
521481
522338
  function pickResultRenderer(toolName) {
521482
522339
  switch (toolName) {
@@ -521494,7 +522351,7 @@ function pickResultRenderer(toolName) {
521494
522351
  case "GetGoal":
521495
522352
  case "SetGoalBudget":
521496
522353
  case "UpdateGoal": return goalSummary;
521497
- default: return renderTruncated;
522354
+ default: return genericMediaResult;
521498
522355
  }
521499
522356
  }
521500
522357
  //#endregion
@@ -523449,66 +524306,6 @@ function formatActivityLine(phase, toolName, args, workspaceDir) {
523449
524306
  return keyArg ? `${verb} ${toolName} (${keyArg})` : `${verb} ${toolName}`;
523450
524307
  }
523451
524308
  //#endregion
523452
- //#region src/tui/components/media/image-thumbnail.ts
523453
- /**
523454
- * Transcript-side rendering of a pasted image.
523455
- *
523456
- * On terminals that speak the Kitty graphics protocol or iTerm2 inline
523457
- * image protocol (detected by pi-tui's `getCapabilities()`), we show
523458
- * the actual image. Everywhere else we fall back to a one-line text
523459
- * marker matching the placeholder the user sees in the input box —
523460
- * this keeps the transcript readable on Terminal.app / Linux default
523461
- * terminals / `script` recordings without extra chrome.
523462
- *
523463
- * Height is capped at ~12 rows so a single screenshot can't monopolize
523464
- * the viewport; pi-tui handles proportional scaling internally.
523465
- */
523466
- const MAX_IMAGE_ROWS = 12;
523467
- const MAX_IMAGE_WIDTH = 40;
523468
- var ImageThumbnail = class extends Container {
523469
- attachment;
523470
- image;
523471
- lastBuiltProtocol;
523472
- constructor(attachment) {
523473
- super();
523474
- this.attachment = attachment;
523475
- this.buildChildren();
523476
- }
523477
- buildChildren() {
523478
- this.clear();
523479
- const caps = getCapabilities();
523480
- const supportsInline = caps.images === "kitty" || caps.images === "iterm2";
523481
- this.lastBuiltProtocol = caps.images;
523482
- if (!supportsInline) {
523483
- this.addChild(new Text(currentTheme.fg("accent", this.attachment.placeholder), 0, 0));
523484
- return;
523485
- }
523486
- const theme = { fallbackColor: (s) => currentTheme.fg("textDim", s) };
523487
- this.image ??= new Image(Buffer.from(this.attachment.bytes).toString("base64"), this.attachment.mime, theme, {
523488
- maxHeightCells: MAX_IMAGE_ROWS,
523489
- maxWidthCells: MAX_IMAGE_WIDTH,
523490
- filename: this.attachment.placeholder
523491
- }, {
523492
- widthPx: this.attachment.width,
523493
- heightPx: this.attachment.height
523494
- });
523495
- this.image.invalidate();
523496
- this.addChild(this.image);
523497
- }
523498
- render(width) {
523499
- const safeWidth = Math.max(0, width);
523500
- if (safeWidth === 0) return [""];
523501
- if (safeWidth < MAX_IMAGE_WIDTH + 2) return new Text(currentTheme.fg("accent", this.attachment.placeholder), 0, 0).render(safeWidth);
523502
- const caps = getCapabilities();
523503
- if (this.lastBuiltProtocol !== caps.images) this.buildChildren();
523504
- return super.render(safeWidth);
523505
- }
523506
- invalidate() {
523507
- this.buildChildren();
523508
- super.invalidate();
523509
- }
523510
- };
523511
- //#endregion
523512
524309
  //#region src/tui/components/messages/user-message.ts
523513
524310
  /**
523514
524311
  * Renders a user message in the transcript.
@@ -523613,6 +524410,14 @@ async function handleUndoCommand(host, args = "") {
523613
524410
  await undoByCount(host, count);
523614
524411
  }
523615
524412
  async function undoByCount(host, count) {
524413
+ const finish = host.beginLocalContextMutation?.();
524414
+ try {
524415
+ return await undoByCountOwned(host, count);
524416
+ } finally {
524417
+ finish?.();
524418
+ }
524419
+ }
524420
+ async function undoByCountOwned(host, count) {
523616
524421
  const session = host.session;
523617
524422
  if (session === void 0) {
523618
524423
  host.showError(uiText("undo.error.noActiveSession"));
@@ -532588,136 +533393,6 @@ var ClipboardImageHintController = class {
532588
533393
  }
532589
533394
  };
532590
533395
  //#endregion
532591
- //#region src/utils/image/image-mime.ts
532592
- function parseImageMeta(bytes) {
532593
- if (isPng(bytes)) return parsePng(bytes);
532594
- if (isJpeg(bytes)) return parseJpeg(bytes);
532595
- if (isGif(bytes)) return parseGif(bytes);
532596
- if (isWebp(bytes)) return parseWebp(bytes);
532597
- return null;
532598
- }
532599
- function isPng(b) {
532600
- return b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71 && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10;
532601
- }
532602
- function parsePng(b) {
532603
- if (b.length < 24) return null;
532604
- const width = readUInt32BE(b, 16);
532605
- const height = readUInt32BE(b, 20);
532606
- if (width <= 0 || height <= 0) return null;
532607
- return {
532608
- mime: "image/png",
532609
- width,
532610
- height
532611
- };
532612
- }
532613
- function isJpeg(b) {
532614
- return b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255;
532615
- }
532616
- function parseJpeg(b) {
532617
- let i = 2;
532618
- while (i < b.length) {
532619
- if (b[i] !== 255) {
532620
- i += 1;
532621
- continue;
532622
- }
532623
- while (i < b.length && b[i] === 255) i += 1;
532624
- if (i >= b.length) return null;
532625
- const marker = b[i];
532626
- i += 1;
532627
- if (marker === 216 || marker === 217) continue;
532628
- if (i + 1 >= b.length) return null;
532629
- const segLen = readUInt16BE(b, i);
532630
- if (isSofMarker(marker)) {
532631
- if (i + 7 >= b.length) return null;
532632
- const height = readUInt16BE(b, i + 3);
532633
- const width = readUInt16BE(b, i + 5);
532634
- if (width <= 0 || height <= 0) return null;
532635
- return {
532636
- mime: "image/jpeg",
532637
- width,
532638
- height
532639
- };
532640
- }
532641
- i += segLen;
532642
- }
532643
- return null;
532644
- }
532645
- function isSofMarker(marker) {
532646
- if (marker < 192 || marker > 207) return false;
532647
- return marker !== 196 && marker !== 200 && marker !== 204;
532648
- }
532649
- function isGif(b) {
532650
- return b.length >= 6 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56 && (b[4] === 55 || b[4] === 57) && b[5] === 97;
532651
- }
532652
- function parseGif(b) {
532653
- if (b.length < 10) return null;
532654
- const width = readUInt16LE(b, 6);
532655
- const height = readUInt16LE(b, 8);
532656
- if (width <= 0 || height <= 0) return null;
532657
- return {
532658
- mime: "image/gif",
532659
- width,
532660
- height
532661
- };
532662
- }
532663
- function isWebp(b) {
532664
- return b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80;
532665
- }
532666
- function parseWebp(b) {
532667
- if (b.length < 30) return null;
532668
- const chunk = String.fromCodePoint(b[12], b[13], b[14], b[15]);
532669
- if (chunk === "VP8 ") {
532670
- const widthRaw = readUInt16LE(b, 26);
532671
- const heightRaw = readUInt16LE(b, 28);
532672
- const width = widthRaw & 16383;
532673
- const height = heightRaw & 16383;
532674
- if (width <= 0 || height <= 0) return null;
532675
- return {
532676
- mime: "image/webp",
532677
- width,
532678
- height
532679
- };
532680
- }
532681
- if (chunk === "VP8L") {
532682
- if (b[20] !== 47) return null;
532683
- const b1 = b[21];
532684
- const b2 = b[22];
532685
- const b3 = b[23];
532686
- const b4 = b[24];
532687
- const width = 1 + ((b2 & 63) << 8 | b1);
532688
- const height = 1 + ((b4 & 15) << 10 | b3 << 2 | (b2 & 192) >> 6);
532689
- if (width <= 0 || height <= 0) return null;
532690
- return {
532691
- mime: "image/webp",
532692
- width,
532693
- height
532694
- };
532695
- }
532696
- if (chunk === "VP8X") {
532697
- const width = 1 + readUInt24LE(b, 24);
532698
- const height = 1 + readUInt24LE(b, 27);
532699
- if (width <= 0 || height <= 0) return null;
532700
- return {
532701
- mime: "image/webp",
532702
- width,
532703
- height
532704
- };
532705
- }
532706
- return null;
532707
- }
532708
- function readUInt16BE(b, off) {
532709
- return b[off] << 8 | b[off + 1];
532710
- }
532711
- function readUInt16LE(b, off) {
532712
- return b[off] | b[off + 1] << 8;
532713
- }
532714
- function readUInt24LE(b, off) {
532715
- return b[off] | b[off + 1] << 8 | b[off + 2] << 16;
532716
- }
532717
- function readUInt32BE(b, off) {
532718
- return Math.trunc(b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3]);
532719
- }
532720
- //#endregion
532721
533396
  //#region src/utils/clipboard/clipboard-image.ts
532722
533397
  /**
532723
533398
  * Read media from the system clipboard with graceful platform fallbacks.
@@ -535643,7 +536318,7 @@ function isTelegramBridgeFraming(value) {
535643
536318
  let remaining = value.trim();
535644
536319
  let foundFrame = false;
535645
536320
  while (remaining.length > 0) {
535646
- const frame = /^(?:\[(?:REPLY-PFLICHT|SEMANTISCHE ADRESSIERUNG|NUR MITLESEN|SPRACH-GATE)\b[\s\S]*?\]|<vision-observation\s+source="telegram"\b[^>]*>[\s\S]*?<\/vision-observation>)/i.exec(remaining);
536321
+ const frame = /^(?:\[GRUPPENANTWORT:[^[\]\r\n]*(?:\[\[BLUN_TELEGRAM_SILENT\]\][^[\]\r\n]*)*\]|\[(?:REPLY-PFLICHT|SEMANTISCHE ADRESSIERUNG|NUR MITLESEN|SPRACH-GATE)\b[\s\S]*?\]|<vision-observation\s+source="telegram"\b[^>]*>[\s\S]*?<\/vision-observation>)/i.exec(remaining);
535647
536322
  if (frame === null) return false;
535648
536323
  foundFrame = true;
535649
536324
  remaining = remaining.slice(frame[0].length).trim();
@@ -536854,10 +537529,71 @@ var SessionEventHandler = class {
536854
537529
  pendingCompactionRetry;
536855
537530
  compactionRetryPanel;
536856
537531
  localToolEvents;
537532
+ contextRefreshEpoch = 0;
537533
+ contextRefreshPending = false;
537534
+ contextRefreshRunning = false;
537535
+ contextRefreshTurnActive = false;
537536
+ contextRefreshFailed = false;
537537
+ localContextMutations = 0;
537538
+ contextRevisions = /* @__PURE__ */ new Map();
537539
+ beginLocalContextMutation() {
537540
+ const epoch = this.contextRefreshEpoch;
537541
+ this.localContextMutations++;
537542
+ let finished = false;
537543
+ return () => {
537544
+ if (finished) return;
537545
+ finished = true;
537546
+ if (epoch !== this.contextRefreshEpoch) return;
537547
+ this.localContextMutations--;
537548
+ this.drainContextRefresh();
537549
+ };
537550
+ }
537551
+ handleContextChanged(event) {
537552
+ if (event.agentId !== "main") return;
537553
+ const previous = this.contextRevisions.get(event.generation) ?? 0;
537554
+ if (event.revision < previous || event.revision === previous && !this.contextRefreshFailed) return;
537555
+ this.contextRevisions.delete(event.generation);
537556
+ this.contextRevisions.set(event.generation, event.revision);
537557
+ if (this.contextRevisions.size > 64) {
537558
+ const oldest = this.contextRevisions.keys().next().value;
537559
+ if (oldest !== void 0) this.contextRevisions.delete(oldest);
537560
+ }
537561
+ this.contextRefreshPending = true;
537562
+ this.drainContextRefresh();
537563
+ }
537564
+ drainContextRefresh() {
537565
+ const session = this.host.session;
537566
+ const refresh = this.host.refreshContextFromReplay;
537567
+ if (!this.contextRefreshPending || this.contextRefreshRunning || this.contextRefreshTurnActive || this.localContextMutations > 0 || session === void 0 || refresh === void 0) return;
537568
+ const epoch = this.contextRefreshEpoch;
537569
+ const isCurrent = () => epoch === this.contextRefreshEpoch && !this.host.aborted && this.host.session === session && this.host.state.appState.sessionId === session.id && this.localContextMutations === 0 && !this.contextRefreshPending && !this.contextRefreshTurnActive;
537570
+ this.contextRefreshPending = false;
537571
+ this.contextRefreshRunning = true;
537572
+ this.contextRefreshFailed = false;
537573
+ refresh.call(this.host, session, isCurrent).then((result) => {
537574
+ if (result === false && isCurrent()) this.contextRefreshFailed = true;
537575
+ }).catch((error) => {
537576
+ if (isCurrent()) {
537577
+ this.contextRefreshFailed = true;
537578
+ this.host.showError(formatErrorMessage$2(error));
537579
+ }
537580
+ }).finally(() => {
537581
+ if (epoch !== this.contextRefreshEpoch) return;
537582
+ this.contextRefreshRunning = false;
537583
+ this.drainContextRefresh();
537584
+ });
537585
+ }
536857
537586
  setLocalToolEventRecordingEnabled(enabled) {
536858
537587
  this.localToolEvents?.setEnabled(enabled);
536859
537588
  }
536860
537589
  resetRuntimeState() {
537590
+ this.contextRefreshEpoch++;
537591
+ this.contextRefreshPending = false;
537592
+ this.contextRefreshRunning = false;
537593
+ this.contextRefreshTurnActive = false;
537594
+ this.contextRefreshFailed = false;
537595
+ this.localContextMutations = 0;
537596
+ this.contextRevisions.clear();
536861
537597
  this.backgroundTasks.clear();
536862
537598
  this.backgroundTaskTranscriptedTerminal.clear();
536863
537599
  this.subAgentEventHandler.resetRuntimeState();
@@ -536979,10 +537715,14 @@ var SessionEventHandler = class {
536979
537715
  if ("turnId" in event && event.turnId !== void 0) this.host.streamingUI.setTurnId(String(event.turnId));
536980
537716
  switch (event.type) {
536981
537717
  case "turn.started":
537718
+ this.contextRefreshTurnActive = true;
537719
+ if (this.contextRefreshRunning) this.contextRefreshPending = true;
536982
537720
  this.handleTurnBegin(event);
536983
537721
  break;
536984
537722
  case "turn.ended":
537723
+ this.contextRefreshTurnActive = false;
536985
537724
  this.handleTurnEnd(event, sendQueued);
537725
+ this.drainContextRefresh();
536986
537726
  break;
536987
537727
  case "turn.step.started":
536988
537728
  this.handleStepBegin(event);
@@ -537028,6 +537768,9 @@ var SessionEventHandler = class {
537028
537768
  case "agent.status.updated":
537029
537769
  this.handleStatusUpdate(event);
537030
537770
  break;
537771
+ case "context.changed":
537772
+ this.handleContextChanged(event);
537773
+ break;
537031
537774
  case "session.meta.updated":
537032
537775
  this.handleSessionMetaChanged(event);
537033
537776
  break;
@@ -537423,7 +538166,7 @@ var SessionEventHandler = class {
537423
538166
  if (change.kind === "completion" && event.snapshot !== null) {
537424
538167
  this.pendingModelBlockedFallback = void 0;
537425
538168
  this.goalCompletionAwaitingClear = true;
537426
- this.goalCompletionTurnEnded = false;
538169
+ this.goalCompletionTurnEnded = change.actor === "user" && state.appState.streamingPhase === "idle";
537427
538170
  this.host.appendTranscriptEntry({
537428
538171
  id: nextTranscriptId(),
537429
538172
  kind: "assistant",
@@ -537495,7 +538238,7 @@ var SessionEventHandler = class {
537495
538238
  this.scheduleQueuedGoalPromotion();
537496
538239
  }
537497
538240
  isReadyForQueuedGoalPromotion(session) {
537498
- return (session === void 0 || this.host.session === session) && !this.host.aborted && this.host.state.appState.streamingPhase === "idle" && this.host.state.queuedMessages.length === 0;
538241
+ return (session === void 0 || this.host.session === session) && !this.host.aborted && this.host.state.appState.streamingPhase === "idle" && !this.host.state.appState.isCompacting && this.host.state.queuedMessages.length === 0;
537499
538242
  }
537500
538243
  async promoteNextQueuedGoal() {
537501
538244
  const { host } = this;
@@ -538100,6 +538843,14 @@ var SessionReplayRenderer = class {
538100
538843
  constructor(host) {
538101
538844
  this.host = host;
538102
538845
  }
538846
+ async refreshFromReplay(session, isCurrent, clearTranscript, historyLimit = 200) {
538847
+ if (!isCurrent()) return false;
538848
+ await session.refreshResumeState({ historyLimit });
538849
+ if (!isCurrent()) return false;
538850
+ if (session.getResumeState()?.agents["main"] === void 0) return this.hydrateFromReplay(session);
538851
+ clearTranscript();
538852
+ return this.hydrateFromReplay(session);
538853
+ }
538103
538854
  /** Records-only projection for a detached page; never hydrates the live snapshot. */
538104
538855
  renderHistoricalPage(agent) {
538105
538856
  this.renderRecords(agent);
@@ -539408,7 +540159,8 @@ function contextMetrics(state) {
539408
540159
  const responder = state.availableModels[responderAlias];
539409
540160
  const responderMax = responder === void 0 ? void 0 : effectiveModelAlias(responder).maxContextSize;
539410
540161
  const runtimeMax = Number.isFinite(state.maxContextTokens) ? Math.max(0, state.maxContextTokens) : 0;
539411
- const maxTokens = runtimeMax > 0 ? runtimeMax : responderMax ?? 0;
540162
+ const hardMaxTokens = runtimeMax > 0 ? runtimeMax : responderMax ?? 0;
540163
+ const maxTokens = Number.isFinite(hardMaxTokens) && hardMaxTokens > 0 ? hardMaxTokens : 0;
539412
540164
  return {
539413
540165
  tokens,
539414
540166
  maxTokens,
@@ -543080,9 +543832,12 @@ function isGroupNoiseReply(text) {
543080
543832
  function isGroupSuppressed(text, _contextOnly) {
543081
543833
  return isGroupNoiseReply(text);
543082
543834
  }
543835
+ function isSilentTurnMarker(text) {
543836
+ return text.trim() === "[[BLUN_TELEGRAM_SILENT]]";
543837
+ }
543083
543838
  async function sendReplyFallback(chatId, text, contextOnly = false, requireDelivery = false, progress, threadId) {
543084
543839
  if (threadId !== void 0 && !validChannelThreadId(threadId)) return false;
543085
- if (text.trim() === "[[BLUN_TELEGRAM_SILENT]]") return !requireDelivery;
543840
+ if (isSilentTurnMarker(text)) return !requireDelivery;
543086
543841
  const topic = threadId === void 0 ? {} : { message_thread_id: Number(threadId) };
543087
543842
  if (isGroupChat(chatId) && isGroupSuppressed(text, contextOnly)) {
543088
543843
  try {
@@ -545770,7 +546525,7 @@ var BlunTUI = class {
545770
546525
  })) return;
545771
546526
  if (outboxGrewForChat(guard.outboxMarker, guard.chatId, guard.threadId)) return;
545772
546527
  const finalText = this.state.transcriptEntries.slice(guard.transcriptStart).filter((entry) => entry.kind === "assistant" && entry.content.trim().length > 0).map((entry) => entry.content.trim()).at(-1) ?? "";
545773
- if (finalText.length === 0) return;
546528
+ if (finalText.length === 0 || isSilentTurnMarker(finalText)) return;
545774
546529
  (guard.threadId === void 0 ? sendReplyFallback(guard.chatId, finalText, guard.contextOnly) : sendReplyFallback(guard.chatId, finalText, guard.contextOnly, false, void 0, guard.threadId)).then((sent) => {
545775
546530
  if (sent) this.showStatus(uiText("blunTui.telegram.fallbackDelivered"));
545776
546531
  });
@@ -546719,6 +547474,14 @@ var BlunTUI = class {
546719
547474
  mergeSteps: (state) => this.mergeAllTurnSteps(state)
546720
547475
  });
546721
547476
  }
547477
+ beginLocalContextMutation() {
547478
+ return this.sessionEventHandler.beginLocalContextMutation();
547479
+ }
547480
+ async refreshContextFromReplay(session, isCurrent) {
547481
+ return this.sessionReplay.refreshFromReplay(session, isCurrent, () => {
547482
+ this.clearTranscriptAndRedraw();
547483
+ }, this.historyLimit);
547484
+ }
546722
547485
  createTranscriptComponent(entry, state = this.state) {
546723
547486
  if (entry.compactionData !== void 0) {
546724
547487
  const data = entry.compactionData;