promptopskit 0.7.1 → 0.8.0

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.
Files changed (70) hide show
  1. package/README.md +39 -4
  2. package/dist/{chunk-OWMP5NMV.js → chunk-5XH2GROP.js} +2 -2
  3. package/dist/{chunk-FICB2ZSK.js → chunk-DI7Q4T7A.js} +3 -3
  4. package/dist/{chunk-ULVY473P.js → chunk-E4GEKWVP.js} +31 -3
  5. package/dist/chunk-E4GEKWVP.js.map +1 -0
  6. package/dist/{chunk-3G3EAV7H.js → chunk-PDVUVZAU.js} +2 -2
  7. package/dist/chunk-RVAFVWR2.js +1760 -0
  8. package/dist/chunk-RVAFVWR2.js.map +1 -0
  9. package/dist/{chunk-35NN5F6O.js → chunk-UQLAWU67.js} +2 -2
  10. package/dist/{chunk-6NOUETAC.js → chunk-Z3DPPJNU.js} +2 -2
  11. package/dist/{chunk-R65RAHAG.js → chunk-ZVCKP4EM.js} +3 -3
  12. package/dist/cli/index.js +86 -5
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +964 -44
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +45 -5
  17. package/dist/index.d.ts +45 -5
  18. package/dist/index.js +18 -10
  19. package/dist/index.js.map +1 -1
  20. package/dist/providers/anthropic.cjs +954 -42
  21. package/dist/providers/anthropic.cjs.map +1 -1
  22. package/dist/providers/anthropic.d.cts +2 -2
  23. package/dist/providers/anthropic.d.ts +2 -2
  24. package/dist/providers/anthropic.js +3 -3
  25. package/dist/providers/gemini.cjs +954 -42
  26. package/dist/providers/gemini.cjs.map +1 -1
  27. package/dist/providers/gemini.d.cts +2 -2
  28. package/dist/providers/gemini.d.ts +2 -2
  29. package/dist/providers/gemini.js +3 -3
  30. package/dist/providers/llmasaservice.cjs +954 -42
  31. package/dist/providers/llmasaservice.cjs.map +1 -1
  32. package/dist/providers/llmasaservice.d.cts +2 -2
  33. package/dist/providers/llmasaservice.d.ts +2 -2
  34. package/dist/providers/llmasaservice.js +4 -4
  35. package/dist/providers/openai-responses.cjs +954 -42
  36. package/dist/providers/openai-responses.cjs.map +1 -1
  37. package/dist/providers/openai-responses.d.cts +2 -2
  38. package/dist/providers/openai-responses.d.ts +2 -2
  39. package/dist/providers/openai-responses.js +3 -3
  40. package/dist/providers/openai.cjs +954 -42
  41. package/dist/providers/openai.cjs.map +1 -1
  42. package/dist/providers/openai.d.cts +2 -2
  43. package/dist/providers/openai.d.ts +2 -2
  44. package/dist/providers/openai.js +3 -3
  45. package/dist/providers/openrouter.cjs +954 -42
  46. package/dist/providers/openrouter.cjs.map +1 -1
  47. package/dist/providers/openrouter.d.cts +2 -2
  48. package/dist/providers/openrouter.d.ts +2 -2
  49. package/dist/providers/openrouter.js +4 -4
  50. package/dist/{schema-DBcSns_b.d.cts → schema-BgVLZ2u5.d.cts} +641 -0
  51. package/dist/{schema-DBcSns_b.d.ts → schema-BgVLZ2u5.d.ts} +641 -0
  52. package/dist/testing.cjs +27 -1
  53. package/dist/testing.cjs.map +1 -1
  54. package/dist/testing.d.cts +1 -1
  55. package/dist/testing.d.ts +1 -1
  56. package/dist/testing.js +1 -1
  57. package/dist/{types-BlLXT5Qb.d.ts → types-2hnZmCkH.d.cts} +16 -3
  58. package/dist/{types-8T2D5KB5.d.cts → types-DgAT4jSR.d.ts} +16 -3
  59. package/dist/usagetap/index.d.cts +2 -2
  60. package/dist/usagetap/index.d.ts +2 -2
  61. package/package.json +1 -1
  62. package/dist/chunk-F552OBUV.js +0 -875
  63. package/dist/chunk-F552OBUV.js.map +0 -1
  64. package/dist/chunk-ULVY473P.js.map +0 -1
  65. /package/dist/{chunk-OWMP5NMV.js.map → chunk-5XH2GROP.js.map} +0 -0
  66. /package/dist/{chunk-FICB2ZSK.js.map → chunk-DI7Q4T7A.js.map} +0 -0
  67. /package/dist/{chunk-3G3EAV7H.js.map → chunk-PDVUVZAU.js.map} +0 -0
  68. /package/dist/{chunk-35NN5F6O.js.map → chunk-UQLAWU67.js.map} +0 -0
  69. /package/dist/{chunk-6NOUETAC.js.map → chunk-Z3DPPJNU.js.map} +0 -0
  70. /package/dist/{chunk-R65RAHAG.js.map → chunk-ZVCKP4EM.js.map} +0 -0
@@ -35,16 +35,17 @@ __export(openai_exports, {
35
35
  module.exports = __toCommonJS(openai_exports);
36
36
 
37
37
  // src/renderer/interpolate.ts
38
- var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
38
+ var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*\|\s*(compress|toon|compact|code))?\s*\}\}/g;
39
39
  var ESCAPED_OPEN = /\\\{\\\{/g;
40
40
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
41
41
  function interpolate(template, variables, options = {}) {
42
42
  const { strict = false } = options;
43
43
  const optionalVariables = new Set(options.optionalVariables ?? []);
44
44
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
45
- result = result.replace(VARIABLE_RE, (match, name) => {
45
+ result = result.replace(VARIABLE_RE, (match, name, modifier) => {
46
46
  if (name in variables) {
47
- return variables[name];
47
+ const value = variables[name];
48
+ return options.transformVariable ? options.transformVariable({ name, value, modifier, match }) : value;
48
49
  }
49
50
  if (strict && !optionalVariables.has(name)) {
50
51
  throw new Error(`Missing required variable: "${name}"`);
@@ -72,12 +73,56 @@ function normalizeContextInput(input) {
72
73
  warnings: input.warnings,
73
74
  max_size: input.max_size,
74
75
  trim: input.trim,
76
+ compression: normalizeContextInputCompression(input.compression),
75
77
  allow_regex: normalizeContextRegex(input.allow_regex),
76
78
  deny_regex: normalizeContextRegex(input.deny_regex),
77
79
  non_empty: normalizeBuiltInValidator(input.non_empty),
78
80
  reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
79
81
  };
80
82
  }
83
+ function normalizeContextInputCompression(value) {
84
+ if (value === void 0) {
85
+ return void 0;
86
+ }
87
+ if (value === "heuristic") {
88
+ return { heuristic: { enabled: true } };
89
+ }
90
+ if (value === "code") {
91
+ return { code: { enabled: true } };
92
+ }
93
+ if (value.heuristic === void 0) {
94
+ return normalizeCodeCompression(value.code);
95
+ }
96
+ const normalized = {
97
+ heuristic: {
98
+ enabled: value.heuristic.enabled ?? true,
99
+ min_tokens: value.heuristic.min_tokens,
100
+ max_sentences: value.heuristic.max_sentences,
101
+ target_reduction: value.heuristic.target_reduction,
102
+ query: value.heuristic.query,
103
+ query_variable: value.heuristic.query_variable,
104
+ json_to_toon: value.heuristic.json_to_toon
105
+ }
106
+ };
107
+ const code = normalizeCodeCompression(value.code);
108
+ if (code?.code) {
109
+ normalized.code = code.code;
110
+ }
111
+ return normalized;
112
+ }
113
+ function normalizeCodeCompression(value) {
114
+ if (value === void 0) {
115
+ return void 0;
116
+ }
117
+ return {
118
+ code: {
119
+ enabled: value.enabled ?? true,
120
+ remove_comments: value.remove_comments,
121
+ trim_indentation: value.trim_indentation,
122
+ collapse_blank_lines: value.collapse_blank_lines
123
+ }
124
+ };
125
+ }
81
126
  function normalizeContextRegex(value) {
82
127
  if (value === void 0) {
83
128
  return void 0;
@@ -331,27 +376,703 @@ function measureContextValueSize(value) {
331
376
  return textEncoder.encode(value).length;
332
377
  }
333
378
 
379
+ // src/toon-encoding.ts
380
+ var DEFAULT_INDENT = 2;
381
+ var DEFAULT_DELIMITER = ",";
382
+ function tryJsonToToon(text, options = {}) {
383
+ const trimmed = text.trim();
384
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
385
+ return void 0;
386
+ }
387
+ let parsed;
388
+ try {
389
+ parsed = JSON.parse(trimmed);
390
+ } catch {
391
+ return void 0;
392
+ }
393
+ const value = normalizeJsonValue(parsed);
394
+ if (value === void 0) {
395
+ return void 0;
396
+ }
397
+ return {
398
+ output: encodeToon(value, options),
399
+ value
400
+ };
401
+ }
402
+ function encodeToon(value, options = {}) {
403
+ const resolved = {
404
+ indent: options.indent ?? DEFAULT_INDENT,
405
+ delimiter: options.delimiter ?? DEFAULT_DELIMITER
406
+ };
407
+ return encodeValue(value, void 0, 0, resolved).join("\n");
408
+ }
409
+ function normalizeJsonValue(value) {
410
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
411
+ return value;
412
+ }
413
+ if (typeof value === "number") {
414
+ return Number.isFinite(value) ? value : null;
415
+ }
416
+ if (Array.isArray(value)) {
417
+ return value.map((item) => normalizeJsonValue(item) ?? null);
418
+ }
419
+ if (isPlainObject(value)) {
420
+ const normalized = {};
421
+ for (const [key, child] of Object.entries(value)) {
422
+ normalized[key] = normalizeJsonValue(child) ?? null;
423
+ }
424
+ return normalized;
425
+ }
426
+ return void 0;
427
+ }
428
+ function encodeValue(value, key, depth, options) {
429
+ if (isPrimitive(value)) {
430
+ return [line(depth, key === void 0 ? encodePrimitive(value, options.delimiter) : `${encodeKey(key)}: ${encodePrimitive(value, options.delimiter)}`, options.indent)];
431
+ }
432
+ if (Array.isArray(value)) {
433
+ return encodeArray(value, key, depth, options);
434
+ }
435
+ return encodeObject(value, key, depth, options);
436
+ }
437
+ function encodeObject(value, key, depth, options) {
438
+ const entries = Object.entries(value);
439
+ const lines = [];
440
+ if (key !== void 0) {
441
+ lines.push(line(depth, `${encodeKey(key)}:`, options.indent));
442
+ }
443
+ const childDepth = key === void 0 ? depth : depth + 1;
444
+ for (const [childKey, childValue] of entries) {
445
+ lines.push(...encodeValue(childValue, childKey, childDepth, options));
446
+ }
447
+ return lines;
448
+ }
449
+ function encodeArray(value, key, depth, options) {
450
+ const prefix = key === void 0 ? "" : encodeKey(key);
451
+ if (value.length === 0) {
452
+ return [line(depth, key === void 0 ? "[]" : `${prefix}: []`, options.indent)];
453
+ }
454
+ if (value.every(isPrimitive)) {
455
+ const joined = value.map((item) => encodePrimitive(item, options.delimiter)).join(options.delimiter);
456
+ return [line(depth, `${prefix}[${value.length}]: ${joined}`, options.indent)];
457
+ }
458
+ const tabularFields = getTabularFields(value);
459
+ if (tabularFields) {
460
+ const lines2 = [
461
+ line(depth, `${prefix}[${value.length}]{${tabularFields.map(encodeKey).join(options.delimiter)}}:`, options.indent)
462
+ ];
463
+ for (const row of value) {
464
+ lines2.push(line(depth + 1, tabularFields.map((field) => encodePrimitive(row[field], options.delimiter)).join(options.delimiter), options.indent));
465
+ }
466
+ return lines2;
467
+ }
468
+ const lines = [line(depth, `${prefix}[${value.length}]:`, options.indent)];
469
+ for (const item of value) {
470
+ lines.push(...encodeListItem(item, depth + 1, options));
471
+ }
472
+ return lines;
473
+ }
474
+ function encodeListItem(value, depth, options) {
475
+ if (isPrimitive(value)) {
476
+ return [line(depth, `- ${encodePrimitive(value, options.delimiter)}`, options.indent)];
477
+ }
478
+ if (Array.isArray(value)) {
479
+ const encoded = encodeArray(value, void 0, depth, options);
480
+ return encoded.map((item, index) => {
481
+ if (index === 0) {
482
+ return line(depth, `- ${item.trimStart()}`, options.indent);
483
+ }
484
+ return item;
485
+ });
486
+ }
487
+ const entries = Object.entries(value);
488
+ if (entries.length === 0) {
489
+ return [line(depth, "-", options.indent)];
490
+ }
491
+ const [firstKey, firstValue] = entries[0];
492
+ const rest = Object.fromEntries(entries.slice(1));
493
+ const firstLines = encodeValue(firstValue, firstKey, depth, options);
494
+ const lines = [line(depth, `- ${firstLines[0].trimStart()}`, options.indent)];
495
+ for (const continuation of firstLines.slice(1)) {
496
+ lines.push(continuation);
497
+ }
498
+ if (Object.keys(rest).length > 0) {
499
+ lines.push(...encodeObject(rest, void 0, depth + 1, options));
500
+ }
501
+ return lines;
502
+ }
503
+ function getTabularFields(value) {
504
+ if (value.length === 0 || !value.every(isObjectRecord)) {
505
+ return void 0;
506
+ }
507
+ const first = value[0];
508
+ const fields = Object.keys(first);
509
+ if (fields.length === 0) {
510
+ return void 0;
511
+ }
512
+ for (const row of value) {
513
+ const keys = Object.keys(row);
514
+ if (keys.length !== fields.length) {
515
+ return void 0;
516
+ }
517
+ for (const field of fields) {
518
+ if (!(field in row) || !isPrimitive(row[field])) {
519
+ return void 0;
520
+ }
521
+ }
522
+ }
523
+ return fields;
524
+ }
525
+ function encodePrimitive(value, delimiter) {
526
+ if (value === null) {
527
+ return "null";
528
+ }
529
+ if (typeof value === "number" || typeof value === "boolean") {
530
+ return String(value);
531
+ }
532
+ return encodeString(value, delimiter);
533
+ }
534
+ function encodeString(value, delimiter) {
535
+ if (isSafeUnquotedString(value, delimiter)) {
536
+ return value;
537
+ }
538
+ return JSON.stringify(value);
539
+ }
540
+ function encodeKey(key) {
541
+ if (/^[A-Z_][\w.]*$/i.test(key)) {
542
+ return key;
543
+ }
544
+ return JSON.stringify(key);
545
+ }
546
+ function isSafeUnquotedString(value, delimiter) {
547
+ if (value.length === 0 || value !== value.trim()) {
548
+ return false;
549
+ }
550
+ if (/^(?:true|false|null)$/i.test(value) || /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value) || /^0\d+$/.test(value)) {
551
+ return false;
552
+ }
553
+ if (value.includes(":") || value.includes('"') || value.includes("\\") || value.includes(delimiter)) {
554
+ return false;
555
+ }
556
+ if (/[[\]{}]|[\u0000-\u001F]/.test(value) || value.startsWith("-")) {
557
+ return false;
558
+ }
559
+ return true;
560
+ }
561
+ function isPrimitive(value) {
562
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
563
+ }
564
+ function isObjectRecord(value) {
565
+ return value !== null && typeof value === "object" && !Array.isArray(value);
566
+ }
567
+ function isPlainObject(value) {
568
+ if (value === null || typeof value !== "object") {
569
+ return false;
570
+ }
571
+ const prototype = Object.getPrototypeOf(value);
572
+ return prototype === null || prototype === Object.prototype;
573
+ }
574
+ function line(depth, content, indent) {
575
+ return `${" ".repeat(depth * indent)}${content}`;
576
+ }
577
+
578
+ // src/token-compression.ts
579
+ var TOKEN_REGEX = /[\p{L}\p{N}]+|[^\s]/gu;
580
+ var TOKEN_NORMALIZE_REGEX = /[^\p{L}\p{N}]/gu;
581
+ var DEDUPE_NORMALIZE_REGEX = /[^\p{L}\p{N}\s]/gu;
582
+ var BOILERPLATE_REGEX = /copyright|all rights reserved|disclaimer|confidential/i;
583
+ var MAX_SEGMENT_TOKENS = 120;
584
+ var STOP_WORDS = /* @__PURE__ */ new Set([
585
+ "a",
586
+ "an",
587
+ "and",
588
+ "are",
589
+ "as",
590
+ "at",
591
+ "be",
592
+ "by",
593
+ "for",
594
+ "from",
595
+ "how",
596
+ "in",
597
+ "is",
598
+ "it",
599
+ "of",
600
+ "on",
601
+ "or",
602
+ "that",
603
+ "the",
604
+ "this",
605
+ "to",
606
+ "was",
607
+ "we",
608
+ "with",
609
+ "you",
610
+ "your",
611
+ "our"
612
+ ]);
613
+ var DEFAULT_OPTIONS = {
614
+ min_tokens: 80,
615
+ max_sentences: 10,
616
+ target_reduction: 0.45
617
+ };
618
+ function tokenizeForHeuristicCompression(text) {
619
+ if (!text) {
620
+ return [];
621
+ }
622
+ return text.match(TOKEN_REGEX) ?? [];
623
+ }
624
+ function estimateHeuristicTokens(text) {
625
+ return tokenizeForHeuristicCompression(text).length;
626
+ }
627
+ function compressHeuristicText(input, options = {}) {
628
+ const inputText = input ?? "";
629
+ const inputTokens = estimateHeuristicTokens(inputText);
630
+ if (options.json_to_toon === true) {
631
+ const toonResult = tryJsonToToon(inputText);
632
+ if (toonResult) {
633
+ const toonOutputTokens = estimateHeuristicTokens(toonResult.output);
634
+ if (toonOutputTokens <= inputTokens) {
635
+ return {
636
+ ...toCompressionOutput(toonResult.output, inputTokens),
637
+ outputFormat: "toon"
638
+ };
639
+ }
640
+ return {
641
+ ...toCompressionOutput(inputText, inputTokens),
642
+ warnings: ["JSON-to-TOON skipped because TOON was not smaller than the original JSON."]
643
+ };
644
+ }
645
+ return {
646
+ ...toCompressionOutput(inputText, inputTokens),
647
+ warnings: ["JSON-to-TOON skipped because the input is not a complete valid JSON object or array."]
648
+ };
649
+ }
650
+ const analysis = preprocessContext(inputText);
651
+ if (inputTokens === 0) {
652
+ return toCompressionOutput(inputText, inputTokens);
653
+ }
654
+ const minTokens = options.min_tokens ?? DEFAULT_OPTIONS.min_tokens;
655
+ if (inputTokens <= minTokens) {
656
+ return toCompressionOutput(inputText, inputTokens);
657
+ }
658
+ const targetReduction = options.target_reduction ?? DEFAULT_OPTIONS.target_reduction;
659
+ const targetTokens = Math.max(1, Math.max(minTokens, Math.floor(inputTokens * (1 - targetReduction))));
660
+ const maxSentences = options.max_sentences ?? DEFAULT_OPTIONS.max_sentences;
661
+ const query = options.query ?? "";
662
+ const terms = queryTerms(query);
663
+ const scoredSentences = analysis.candidates.map((candidate) => ({
664
+ sentence: candidate.sentence,
665
+ index: candidate.index,
666
+ ...scoreSentenceCandidate(candidate, terms),
667
+ tokens: candidate.tokenCount
668
+ }));
669
+ const selected = [];
670
+ const selectedIndices = /* @__PURE__ */ new Set();
671
+ const coveredTerms = /* @__PURE__ */ new Set();
672
+ let selectedTokens = 0;
673
+ while (selected.length < maxSentences) {
674
+ let bestCandidate;
675
+ let bestAdjustedScore = Number.NEGATIVE_INFINITY;
676
+ for (const candidate of scoredSentences) {
677
+ if (selectedIndices.has(candidate.index)) {
678
+ continue;
679
+ }
680
+ const underBudget = selectedTokens + candidate.tokens <= targetTokens;
681
+ const forceTopCandidate = selected.length === 0;
682
+ const forceRelevantSecond = selected.length === 1 && candidate.overlapCount > 0;
683
+ if (!underBudget && !forceTopCandidate && !forceRelevantSecond) {
684
+ continue;
685
+ }
686
+ let uncoveredOverlapCount = 0;
687
+ for (const term of candidate.overlapTerms) {
688
+ if (!coveredTerms.has(term)) {
689
+ uncoveredOverlapCount += 1;
690
+ }
691
+ }
692
+ const adjustedScore = candidate.score + uncoveredOverlapCount * 1.2;
693
+ if (adjustedScore > bestAdjustedScore || adjustedScore === bestAdjustedScore && bestCandidate && isBetterTieBreak(candidate, bestCandidate)) {
694
+ bestAdjustedScore = adjustedScore;
695
+ bestCandidate = candidate;
696
+ }
697
+ }
698
+ if (!bestCandidate) {
699
+ break;
700
+ }
701
+ selected.push(bestCandidate);
702
+ selectedIndices.add(bestCandidate.index);
703
+ selectedTokens += bestCandidate.tokens;
704
+ for (const term of bestCandidate.overlapTerms) {
705
+ coveredTerms.add(term);
706
+ }
707
+ }
708
+ selected.sort((left, right) => left.index - right.index);
709
+ let output = selected.map((item) => item.sentence).join(" ");
710
+ if (!output) {
711
+ output = tokenizeForHeuristicCompression(inputText).slice(0, targetTokens).join(" ");
712
+ } else {
713
+ const outputTokens = tokenizeForHeuristicCompression(output);
714
+ if (outputTokens.length > targetTokens) {
715
+ output = outputTokens.slice(0, targetTokens).join(" ");
716
+ }
717
+ }
718
+ return toCompressionOutput(output, inputTokens);
719
+ }
720
+ function toCompressionOutput(output, inputTokens) {
721
+ const outputTokens = estimateHeuristicTokens(output);
722
+ const tokensSaved = Math.max(0, inputTokens - outputTokens);
723
+ return {
724
+ output,
725
+ inputTokens,
726
+ outputTokens,
727
+ tokensSaved,
728
+ compressionRatio: outputTokens === 0 ? 0 : inputTokens / outputTokens
729
+ };
730
+ }
731
+ function isBetterTieBreak(candidate, bestCandidate) {
732
+ if (candidate.overlapCount !== bestCandidate.overlapCount) {
733
+ return candidate.overlapCount > bestCandidate.overlapCount;
734
+ }
735
+ if (candidate.tokens !== bestCandidate.tokens) {
736
+ return candidate.tokens < bestCandidate.tokens;
737
+ }
738
+ return candidate.index < bestCandidate.index;
739
+ }
740
+ function normalizeForDeduping(sentence) {
741
+ return sentence.toLowerCase().replace(DEDUPE_NORMALIZE_REGEX, " ").replace(/\s+/g, " ").trim();
742
+ }
743
+ function splitIntoSentences(text) {
744
+ return text.split(/(?<=[.!?])\s+|\n+/g).map((sentence) => sentence.trim()).filter(Boolean);
745
+ }
746
+ function chunkTokens(tokens, chunkSize) {
747
+ const chunks = [];
748
+ for (let index = 0; index < tokens.length; index += chunkSize) {
749
+ chunks.push(tokens.slice(index, index + chunkSize));
750
+ }
751
+ return chunks;
752
+ }
753
+ function splitOversizedSentence(sentence, maxTokens = MAX_SEGMENT_TOKENS) {
754
+ const rawTokens = tokenizeForHeuristicCompression(sentence);
755
+ if (rawTokens.length <= maxTokens) {
756
+ return [sentence];
757
+ }
758
+ const commaSplit = sentence.split(/(?<=[,;:])\s+|\s+-\s+/g).map((segment) => segment.trim()).filter(Boolean);
759
+ if (commaSplit.length > 1) {
760
+ return commaSplit.flatMap((segment) => {
761
+ if (estimateHeuristicTokens(segment) <= maxTokens) {
762
+ return [segment];
763
+ }
764
+ return chunkTokens(tokenizeForHeuristicCompression(segment), maxTokens).map((chunk) => chunk.join(" "));
765
+ });
766
+ }
767
+ return chunkTokens(rawTokens, maxTokens).map((chunk) => chunk.join(" "));
768
+ }
769
+ function normalizeToken(token) {
770
+ return token.replace(TOKEN_NORMALIZE_REGEX, "").trim();
771
+ }
772
+ function tokenizeNormalized(text) {
773
+ return tokenizeForHeuristicCompression(text.toLowerCase()).map(normalizeToken).filter(Boolean);
774
+ }
775
+ function queryTerms(query) {
776
+ return new Set(tokenizeNormalized(query).filter((term) => term.length >= 3 && !STOP_WORDS.has(term)));
777
+ }
778
+ function preprocessContext(context) {
779
+ const originalTokens = estimateHeuristicTokens(context);
780
+ const dedupeSet = /* @__PURE__ */ new Set();
781
+ const candidates = [];
782
+ for (const sentence of splitIntoSentences(context)) {
783
+ for (const segment of splitOversizedSentence(sentence)) {
784
+ const normalized = normalizeForDeduping(segment);
785
+ if (!normalized || dedupeSet.has(normalized)) {
786
+ continue;
787
+ }
788
+ dedupeSet.add(normalized);
789
+ const rawTokens = tokenizeForHeuristicCompression(segment);
790
+ const normalizedTerms = rawTokens.map((token) => normalizeToken(token.toLowerCase())).filter(Boolean);
791
+ const lowered = segment.toLowerCase();
792
+ candidates.push({
793
+ sentence: segment,
794
+ index: candidates.length,
795
+ tokenCount: rawTokens.length,
796
+ uniqueTerms: new Set(normalizedTerms),
797
+ isBoilerplate: BOILERPLATE_REGEX.test(lowered)
798
+ });
799
+ }
800
+ }
801
+ return { originalTokens, candidates };
802
+ }
803
+ function scoreSentenceCandidate(candidate, terms) {
804
+ if (candidate.tokenCount === 0) {
805
+ return {
806
+ score: 0,
807
+ overlapCount: 0,
808
+ overlapTerms: []
809
+ };
810
+ }
811
+ const overlapTerms = [];
812
+ let overlap = 0;
813
+ for (const term of terms) {
814
+ if (candidate.uniqueTerms.has(term)) {
815
+ overlap += 1;
816
+ overlapTerms.push(term);
817
+ }
818
+ }
819
+ const overlapScore = overlap * 2;
820
+ const densityScore = terms.size > 0 ? overlap / terms.size : 0;
821
+ const lengthScore = Math.min(candidate.tokenCount, 40) / 40;
822
+ const boilerplatePenalty = candidate.isBoilerplate ? 0.5 : 0;
823
+ return {
824
+ score: overlapScore + densityScore + lengthScore - boilerplatePenalty,
825
+ overlapCount: overlap,
826
+ overlapTerms
827
+ };
828
+ }
829
+
830
+ // src/code-compaction.ts
831
+ function compactCode(input, options = {}) {
832
+ const inputText = input ?? "";
833
+ const removeComments = options.remove_comments ?? true;
834
+ const trimIndentation = options.trim_indentation ?? true;
835
+ const collapseBlankLines = options.collapse_blank_lines ?? true;
836
+ let output = inputText.replace(/\r\n?/g, "\n");
837
+ if (removeComments) {
838
+ output = stripCodeComments(output);
839
+ }
840
+ output = output.split("\n").map((line2) => line2.replace(/[ \t]+$/g, "")).join("\n");
841
+ if (trimIndentation) {
842
+ output = stripCommonIndent(output);
843
+ }
844
+ if (collapseBlankLines) {
845
+ output = output.split("\n").filter((line2) => line2.trim().length > 0).join("\n");
846
+ }
847
+ output = output.trim();
848
+ const inputTokens = estimateHeuristicTokens(inputText);
849
+ const outputTokens = estimateHeuristicTokens(output);
850
+ return {
851
+ output,
852
+ inputTokens,
853
+ outputTokens,
854
+ tokensSaved: Math.max(0, inputTokens - outputTokens),
855
+ compressionRatio: outputTokens === 0 ? 0 : inputTokens / outputTokens
856
+ };
857
+ }
858
+ function stripCommonIndent(input) {
859
+ const lines = input.split("\n");
860
+ const nonEmptyLines = lines.filter((line2) => line2.trim().length > 0);
861
+ if (nonEmptyLines.length === 0) {
862
+ return "";
863
+ }
864
+ const commonIndent = Math.min(
865
+ ...nonEmptyLines.map((line2) => {
866
+ const match = line2.match(/^[ \t]*/);
867
+ return match?.[0].length ?? 0;
868
+ })
869
+ );
870
+ if (commonIndent === 0) {
871
+ return input;
872
+ }
873
+ return lines.map((line2) => line2.slice(commonIndent)).join("\n");
874
+ }
875
+ function stripCodeComments(input) {
876
+ let output = "";
877
+ let index = 0;
878
+ let quote;
879
+ let escaped = false;
880
+ let atLineStart = true;
881
+ while (index < input.length) {
882
+ const char = input[index];
883
+ const next = input[index + 1];
884
+ if (quote) {
885
+ output += char;
886
+ if (escaped) {
887
+ escaped = false;
888
+ } else if (char === "\\") {
889
+ escaped = true;
890
+ } else if (char === quote) {
891
+ quote = void 0;
892
+ }
893
+ atLineStart = char === "\n";
894
+ index += 1;
895
+ continue;
896
+ }
897
+ if (char === '"' || char === "'" || char === "`") {
898
+ quote = char;
899
+ output += char;
900
+ atLineStart = false;
901
+ index += 1;
902
+ continue;
903
+ }
904
+ if (atLineStart && char === "#" && next === "!") {
905
+ while (index < input.length && input[index] !== "\n") {
906
+ output += input[index];
907
+ index += 1;
908
+ }
909
+ continue;
910
+ }
911
+ if (char === "/" && next === "/") {
912
+ index += 2;
913
+ while (index < input.length && input[index] !== "\n") {
914
+ index += 1;
915
+ }
916
+ continue;
917
+ }
918
+ if (char === "/" && next === "*") {
919
+ index += 2;
920
+ while (index < input.length && !(input[index] === "*" && input[index + 1] === "/")) {
921
+ if (input[index] === "\n") {
922
+ output += "\n";
923
+ }
924
+ index += 1;
925
+ }
926
+ index += input[index] === "*" ? 2 : 0;
927
+ continue;
928
+ }
929
+ if (char === "#" && (next === " " || next === " ")) {
930
+ index += 1;
931
+ while (index < input.length && input[index] !== "\n") {
932
+ index += 1;
933
+ }
934
+ continue;
935
+ }
936
+ output += char;
937
+ atLineStart = char === "\n" || atLineStart && (char === " " || char === " ");
938
+ index += 1;
939
+ }
940
+ return output;
941
+ }
942
+
334
943
  // src/renderer/renderer.ts
335
944
  function renderSections(asset, options = {}) {
336
945
  const { variables = {}, strict = false } = options;
337
- const optionalVariables = getContextInputs(asset).filter((input) => input.optional === true).map((input) => input.name);
946
+ const contextInputs = getContextInputs(asset);
947
+ const optionalVariables = contextInputs.filter((input) => input.optional === true).map((input) => input.name);
948
+ const contextInputsByName = new Map(contextInputs.map((input) => [input.name, input]));
949
+ const compressionCache = /* @__PURE__ */ new Map();
950
+ const createTransformVariable = (template) => {
951
+ return ({ name, value, modifier, match }) => {
952
+ const input = contextInputsByName.get(name);
953
+ const configuredHeuristic = input?.compression?.heuristic;
954
+ const configuredCode = input?.compression?.code;
955
+ const shouldCompactCode = modifier === "compact" || modifier === "code" || configuredCode?.enabled === true;
956
+ const shouldCompress = modifier === "compress" || configuredHeuristic?.enabled === true;
957
+ if (modifier === "toon") {
958
+ const result3 = toToonPlaceholder(value);
959
+ if (!result3) {
960
+ options.compression?.onCompressionWarning?.(
961
+ `POK031: JSON-to-TOON skipped for placeholder "${name}" because the value is not a complete valid JSON object or array.`
962
+ );
963
+ return value;
964
+ }
965
+ if (result3.tokensSaved > 0 || result3.output !== value) {
966
+ options.compression?.onHeuristicCompression?.({
967
+ scope: "placeholder",
968
+ variable: name,
969
+ ...result3
970
+ });
971
+ }
972
+ return result3.output;
973
+ }
974
+ if (shouldCompactCode) {
975
+ const result3 = compactCode(value, toCodeCompactionOptions(configuredCode));
976
+ if (result3.tokensSaved > 0 || result3.output !== value) {
977
+ options.compression?.onCodeCompaction?.({
978
+ scope: "placeholder",
979
+ variable: name,
980
+ ...result3
981
+ });
982
+ }
983
+ return result3.output;
984
+ }
985
+ if (!shouldCompress) {
986
+ return value;
987
+ }
988
+ const query = resolveHeuristicQuery(configuredHeuristic, variables, template, match);
989
+ const compressionOptions = {
990
+ min_tokens: configuredHeuristic?.min_tokens,
991
+ max_sentences: configuredHeuristic?.max_sentences,
992
+ target_reduction: configuredHeuristic?.target_reduction,
993
+ query,
994
+ json_to_toon: configuredHeuristic?.json_to_toon
995
+ };
996
+ const cacheKey = JSON.stringify([name, value, compressionOptions]);
997
+ const cached = compressionCache.get(cacheKey);
998
+ if (cached !== void 0) {
999
+ return cached;
1000
+ }
1001
+ const result2 = compressHeuristicText(value, compressionOptions);
1002
+ compressionCache.set(cacheKey, result2.output);
1003
+ reportHeuristicCompressionWarnings(options, result2, `placeholder "${name}"`);
1004
+ if (result2.tokensSaved > 0 || result2.output !== value) {
1005
+ options.compression?.onHeuristicCompression?.({
1006
+ scope: "placeholder",
1007
+ variable: name,
1008
+ ...result2
1009
+ });
1010
+ }
1011
+ return result2.output;
1012
+ };
1013
+ };
338
1014
  const result = {};
339
1015
  if (asset.sections.system_instructions) {
340
1016
  result.system_instructions = interpolate(
341
1017
  asset.sections.system_instructions,
342
1018
  variables,
343
- { strict, optionalVariables }
1019
+ {
1020
+ strict,
1021
+ optionalVariables,
1022
+ transformVariable: createTransformVariable(asset.sections.system_instructions)
1023
+ }
344
1024
  );
345
1025
  }
346
1026
  if (asset.sections.prompt_template) {
347
1027
  result.prompt_template = interpolate(
348
1028
  asset.sections.prompt_template,
349
1029
  variables,
350
- { strict, optionalVariables }
1030
+ {
1031
+ strict,
1032
+ optionalVariables,
1033
+ transformVariable: createTransformVariable(asset.sections.prompt_template)
1034
+ }
351
1035
  );
352
1036
  }
353
1037
  return result;
354
1038
  }
1039
+ function resolveHeuristicQuery(options, variables, template, match) {
1040
+ if (options?.query !== void 0) {
1041
+ return options.query;
1042
+ }
1043
+ if (options?.query_variable && variables[options.query_variable] !== void 0) {
1044
+ return variables[options.query_variable];
1045
+ }
1046
+ return template.replace(match, " ");
1047
+ }
1048
+ function toToonPlaceholder(value) {
1049
+ const toon = tryJsonToToon(value);
1050
+ if (!toon) {
1051
+ return void 0;
1052
+ }
1053
+ const inputTokens = estimateHeuristicTokens(value);
1054
+ const outputTokens = estimateHeuristicTokens(toon.output);
1055
+ return {
1056
+ output: toon.output,
1057
+ inputTokens,
1058
+ outputTokens,
1059
+ tokensSaved: Math.max(0, inputTokens - outputTokens),
1060
+ compressionRatio: outputTokens === 0 ? 0 : inputTokens / outputTokens,
1061
+ outputFormat: "toon"
1062
+ };
1063
+ }
1064
+ function toCodeCompactionOptions(options) {
1065
+ return {
1066
+ remove_comments: options?.remove_comments,
1067
+ trim_indentation: options?.trim_indentation,
1068
+ collapse_blank_lines: options?.collapse_blank_lines
1069
+ };
1070
+ }
1071
+ function reportHeuristicCompressionWarnings(options, result, scope) {
1072
+ for (const warning of result.warnings ?? []) {
1073
+ options.compression?.onCompressionWarning?.(`POK031: ${warning} Scope: ${scope}.`);
1074
+ }
1075
+ }
355
1076
 
356
1077
  // src/overrides/apply-overrides.ts
357
1078
  function applyOverrides(asset, options = {}) {
@@ -388,6 +1109,14 @@ function mergeOverride(base, override) {
388
1109
  thetokencompany: mergeRecordBlock(
389
1110
  result.compression?.thetokencompany,
390
1111
  override.compression.thetokencompany
1112
+ ),
1113
+ heuristic: mergeRecordBlock(
1114
+ result.compression?.heuristic,
1115
+ override.compression.heuristic
1116
+ ),
1117
+ code: mergeRecordBlock(
1118
+ result.compression?.code,
1119
+ override.compression.code
391
1120
  )
392
1121
  };
393
1122
  }
@@ -621,8 +1350,25 @@ var TheTokenCompanyCompressionSchema = import_zod.z.object({
621
1350
  model: import_zod.z.string().min(1).optional(),
622
1351
  aggressiveness: import_zod.z.number().min(0).max(1).optional()
623
1352
  });
1353
+ var HeuristicCompressionSchema = import_zod.z.object({
1354
+ enabled: import_zod.z.boolean().optional(),
1355
+ min_tokens: import_zod.z.number().int().positive().optional(),
1356
+ max_sentences: import_zod.z.number().int().positive().optional(),
1357
+ target_reduction: import_zod.z.number().min(0).max(1).optional(),
1358
+ query: import_zod.z.string().optional(),
1359
+ query_variable: import_zod.z.string().min(1).optional(),
1360
+ json_to_toon: import_zod.z.boolean().optional()
1361
+ });
1362
+ var CodeCompactionSchema = import_zod.z.object({
1363
+ enabled: import_zod.z.boolean().optional(),
1364
+ remove_comments: import_zod.z.boolean().optional(),
1365
+ trim_indentation: import_zod.z.boolean().optional(),
1366
+ collapse_blank_lines: import_zod.z.boolean().optional()
1367
+ });
624
1368
  var CompressionSchema = import_zod.z.object({
625
- thetokencompany: TheTokenCompanyCompressionSchema.optional()
1369
+ thetokencompany: TheTokenCompanyCompressionSchema.optional(),
1370
+ heuristic: HeuristicCompressionSchema.optional(),
1371
+ code: CodeCompactionSchema.optional()
626
1372
  });
627
1373
  var OpenAICacheSchema = import_zod.z.object({
628
1374
  prompt_cache_key: import_zod.z.string().min(1).optional(),
@@ -662,12 +1408,21 @@ var ContextBuiltInValidatorSchema = import_zod.z.union([
662
1408
  return_message: import_zod.z.string().optional()
663
1409
  })
664
1410
  ]);
1411
+ var ContextInputCompressionSchema = import_zod.z.union([
1412
+ import_zod.z.literal("heuristic"),
1413
+ import_zod.z.literal("code"),
1414
+ import_zod.z.object({
1415
+ heuristic: HeuristicCompressionSchema.optional(),
1416
+ code: CodeCompactionSchema.optional()
1417
+ })
1418
+ ]);
665
1419
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
666
1420
  name: import_zod.z.string(),
667
1421
  optional: import_zod.z.boolean().optional(),
668
1422
  warnings: import_zod.z.boolean().optional(),
669
1423
  max_size: import_zod.z.number().int().positive().optional(),
670
1424
  trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
1425
+ compression: ContextInputCompressionSchema.optional(),
671
1426
  allow_regex: ContextRegexSchema.optional(),
672
1427
  deny_regex: ContextRegexSchema.optional(),
673
1428
  non_empty: ContextBuiltInValidatorSchema.optional(),
@@ -770,9 +1525,9 @@ function assertRegexFrontMatterQuoting(content, filePath) {
770
1525
  return;
771
1526
  }
772
1527
  let pendingRegex;
773
- for (const [index, line] of frontMatter.lines.entries()) {
1528
+ for (const [index, line2] of frontMatter.lines.entries()) {
774
1529
  const lineNumber = frontMatter.startLine + index;
775
- const regexMatch = line.match(REGEX_FIELD);
1530
+ const regexMatch = line2.match(REGEX_FIELD);
776
1531
  if (regexMatch?.groups) {
777
1532
  const field = regexMatch.groups.field;
778
1533
  const indent2 = regexMatch.groups.indent.length;
@@ -785,15 +1540,15 @@ function assertRegexFrontMatterQuoting(content, filePath) {
785
1540
  if (!pendingRegex) {
786
1541
  continue;
787
1542
  }
788
- if (line.trim() === "" || line.trimStart().startsWith("#")) {
1543
+ if (line2.trim() === "" || line2.trimStart().startsWith("#")) {
789
1544
  continue;
790
1545
  }
791
- const indent = line.match(/^\s*/)?.[0].length ?? 0;
1546
+ const indent = line2.match(/^\s*/)?.[0].length ?? 0;
792
1547
  if (indent <= pendingRegex.indent) {
793
1548
  pendingRegex = void 0;
794
1549
  continue;
795
1550
  }
796
- const patternMatch = line.match(PATTERN_FIELD);
1551
+ const patternMatch = line2.match(PATTERN_FIELD);
797
1552
  if (!patternMatch?.groups) {
798
1553
  continue;
799
1554
  }
@@ -888,8 +1643,8 @@ function extractSections(body) {
888
1643
  let currentKey = null;
889
1644
  let currentLines = [];
890
1645
  let foundAnyH1 = false;
891
- for (const line of lines) {
892
- const h1Match = line.match(/^#\s+(.+)$/);
1646
+ for (const line2 of lines) {
1647
+ const h1Match = line2.match(/^#\s+(.+)$/);
893
1648
  if (h1Match) {
894
1649
  if (currentKey) {
895
1650
  sections[currentKey] = currentLines.join("\n").trim();
@@ -899,7 +1654,7 @@ function extractSections(body) {
899
1654
  currentKey = SECTION_MAP[heading] ?? null;
900
1655
  currentLines = [];
901
1656
  } else {
902
- currentLines.push(line);
1657
+ currentLines.push(line2);
903
1658
  }
904
1659
  }
905
1660
  if (currentKey) {
@@ -1232,7 +1987,9 @@ function mergeCompression(base, local) {
1232
1987
  const merged = {
1233
1988
  ...base ?? {},
1234
1989
  ...local ?? {},
1235
- thetokencompany: mergeRecordBlock2(base?.thetokencompany, local?.thetokencompany)
1990
+ thetokencompany: mergeRecordBlock2(base?.thetokencompany, local?.thetokencompany),
1991
+ heuristic: mergeRecordBlock2(base?.heuristic, local?.heuristic),
1992
+ code: mergeRecordBlock2(base?.code, local?.code)
1236
1993
  };
1237
1994
  removeEmptyProviderBlocks(merged);
1238
1995
  return Object.keys(merged).length > 0 ? merged : void 0;
@@ -1486,33 +2243,152 @@ function compactHistoryForPrompt(asset, runtime) {
1486
2243
  // src/compression.ts
1487
2244
  var THETOKENCOMPANY_DEFAULT_MODEL = "bear-2";
1488
2245
  var THETOKENCOMPANY_DEFAULT_BASE_URL = "https://api.thetokencompany.com";
2246
+ var HEURISTIC_COMPRESSION_MODEL = "local-heuristic-v1";
2247
+ var CODE_COMPACTION_MODEL = "local-code-compactor-v1";
2248
+ function summarizePromptCompression(compression = []) {
2249
+ const totals = compression.reduce(
2250
+ (accumulator, result) => {
2251
+ accumulator.inputTokens += result.inputTokens;
2252
+ accumulator.outputTokens += result.outputTokens;
2253
+ accumulator.tokensSaved += result.tokensSaved;
2254
+ return accumulator;
2255
+ },
2256
+ {
2257
+ inputTokens: 0,
2258
+ outputTokens: 0,
2259
+ tokensSaved: 0
2260
+ }
2261
+ );
2262
+ return {
2263
+ steps: compression.length,
2264
+ inputTokens: totals.inputTokens,
2265
+ outputTokens: totals.outputTokens,
2266
+ tokensSaved: totals.tokensSaved,
2267
+ reductionRatio: totals.inputTokens === 0 ? 0 : totals.tokensSaved / totals.inputTokens
2268
+ };
2269
+ }
1489
2270
  async function applyPromptCompressionForRender(asset, runtime) {
1490
- const config = asset.compression?.thetokencompany;
1491
- if (config?.enabled !== true || !asset.sections.prompt_template) {
1492
- return { asset, runtime, compression: [] };
1493
- }
2271
+ const theTokenCompanyConfig = asset.compression?.thetokencompany;
2272
+ const heuristicConfig = asset.compression?.heuristic;
2273
+ const codeConfig = asset.compression?.code;
2274
+ const hasTheTokenCompanyCompression = theTokenCompanyConfig?.enabled === true && Boolean(asset.sections.prompt_template);
2275
+ const hasHeuristicPromptCompression = heuristicConfig?.enabled === true && Boolean(asset.sections.prompt_template);
2276
+ const hasCodePromptCompaction = codeConfig?.enabled === true && Boolean(asset.sections.prompt_template);
2277
+ const hasPlaceholderCompression = usesPlaceholderCompression(asset);
2278
+ if (!hasTheTokenCompanyCompression && !hasHeuristicPromptCompression && !hasCodePromptCompaction && !hasPlaceholderCompression) {
2279
+ return { asset, runtime, compression: [], warnings: [] };
2280
+ }
2281
+ const compression = [];
2282
+ const warnings = [];
1494
2283
  const sections = renderSections(asset, {
1495
2284
  variables: runtime.variables,
1496
- strict: runtime.strict
1497
- });
1498
- if (!sections.prompt_template) {
1499
- return { asset, runtime, compression: [] };
1500
- }
1501
- const model = config.model ?? THETOKENCOMPANY_DEFAULT_MODEL;
1502
- const result = await compressWithTheTokenCompany(sections.prompt_template, {
1503
- apiKey: runtime.theTokenCompany?.apiKey,
1504
- baseURL: runtime.theTokenCompany?.baseURL,
1505
- fetch: runtime.theTokenCompany?.fetch,
1506
- model,
1507
- aggressiveness: config.aggressiveness
2285
+ strict: runtime.strict,
2286
+ compression: {
2287
+ onHeuristicCompression: (event) => {
2288
+ compression.push({
2289
+ provider: "heuristic",
2290
+ model: HEURISTIC_COMPRESSION_MODEL,
2291
+ inputTokens: event.inputTokens,
2292
+ outputTokens: event.outputTokens,
2293
+ tokensSaved: event.tokensSaved,
2294
+ compressionRatio: event.compressionRatio,
2295
+ scope: event.scope,
2296
+ variable: event.variable,
2297
+ outputFormat: event.outputFormat
2298
+ });
2299
+ },
2300
+ onCodeCompaction: (event) => {
2301
+ compression.push({
2302
+ provider: "code",
2303
+ model: CODE_COMPACTION_MODEL,
2304
+ inputTokens: event.inputTokens,
2305
+ outputTokens: event.outputTokens,
2306
+ tokensSaved: event.tokensSaved,
2307
+ compressionRatio: event.compressionRatio,
2308
+ scope: event.scope,
2309
+ variable: event.variable,
2310
+ outputFormat: "code"
2311
+ });
2312
+ },
2313
+ onCompressionWarning: (warning) => {
2314
+ warnings.push(warning);
2315
+ }
2316
+ }
1508
2317
  });
2318
+ let promptTemplate = sections.prompt_template;
2319
+ if (hasCodePromptCompaction && promptTemplate) {
2320
+ if (hasHeuristicPromptCompression) {
2321
+ warnings.push(
2322
+ "POK032: Local heuristic prompt compression skipped because compression.code is enabled for the prompt template."
2323
+ );
2324
+ }
2325
+ const result = compactCode(promptTemplate, toCodeCompactionOptions2(codeConfig));
2326
+ promptTemplate = result.output;
2327
+ if (result.tokensSaved > 0 || result.output !== sections.prompt_template) {
2328
+ compression.push({
2329
+ provider: "code",
2330
+ model: CODE_COMPACTION_MODEL,
2331
+ inputTokens: result.inputTokens,
2332
+ outputTokens: result.outputTokens,
2333
+ tokensSaved: result.tokensSaved,
2334
+ compressionRatio: result.compressionRatio,
2335
+ scope: "prompt_template",
2336
+ outputFormat: "code"
2337
+ });
2338
+ }
2339
+ } else if (hasHeuristicPromptCompression && promptTemplate) {
2340
+ const result = compressHeuristicText(promptTemplate, {
2341
+ min_tokens: heuristicConfig?.min_tokens,
2342
+ max_sentences: heuristicConfig?.max_sentences,
2343
+ target_reduction: heuristicConfig?.target_reduction,
2344
+ query: resolveHeuristicPromptQuery(heuristicConfig, runtime.variables, sections.system_instructions),
2345
+ json_to_toon: heuristicConfig?.json_to_toon
2346
+ });
2347
+ promptTemplate = result.output;
2348
+ reportHeuristicCompressionWarnings2(warnings, result.warnings, "prompt template");
2349
+ if (result.tokensSaved > 0 || result.output !== sections.prompt_template) {
2350
+ compression.push({
2351
+ provider: "heuristic",
2352
+ model: HEURISTIC_COMPRESSION_MODEL,
2353
+ inputTokens: result.inputTokens,
2354
+ outputTokens: result.outputTokens,
2355
+ tokensSaved: result.tokensSaved,
2356
+ compressionRatio: result.compressionRatio,
2357
+ scope: "prompt_template",
2358
+ outputFormat: result.outputFormat
2359
+ });
2360
+ }
2361
+ }
2362
+ if (hasTheTokenCompanyCompression && hasCodePromptCompaction && promptTemplate) {
2363
+ warnings.push(
2364
+ "POK033: TheTokenCompany compression skipped because compression.code is enabled; code is compacted locally and not text-compressed."
2365
+ );
2366
+ } else if (hasTheTokenCompanyCompression && promptTemplate) {
2367
+ const model = theTokenCompanyConfig?.model ?? THETOKENCOMPANY_DEFAULT_MODEL;
2368
+ const result = await compressWithTheTokenCompany(promptTemplate, {
2369
+ apiKey: runtime.theTokenCompany?.apiKey,
2370
+ baseURL: runtime.theTokenCompany?.baseURL,
2371
+ fetch: runtime.theTokenCompany?.fetch,
2372
+ model,
2373
+ aggressiveness: theTokenCompanyConfig?.aggressiveness
2374
+ });
2375
+ promptTemplate = result.output;
2376
+ compression.push({
2377
+ provider: "thetokencompany",
2378
+ model,
2379
+ inputTokens: result.input_tokens,
2380
+ outputTokens: result.output_tokens,
2381
+ tokensSaved: result.tokens_saved,
2382
+ compressionRatio: result.compression_ratio
2383
+ });
2384
+ }
1509
2385
  return {
1510
2386
  asset: {
1511
2387
  ...asset,
1512
2388
  sections: {
1513
2389
  ...asset.sections,
1514
2390
  ...sections,
1515
- prompt_template: result.output
2391
+ ...promptTemplate !== void 0 ? { prompt_template: promptTemplate } : {}
1516
2392
  }
1517
2393
  },
1518
2394
  runtime: {
@@ -1520,16 +2396,45 @@ async function applyPromptCompressionForRender(asset, runtime) {
1520
2396
  variables: {},
1521
2397
  strict: false
1522
2398
  },
1523
- compression: [{
1524
- provider: "thetokencompany",
1525
- model,
1526
- inputTokens: result.input_tokens,
1527
- outputTokens: result.output_tokens,
1528
- tokensSaved: result.tokens_saved,
1529
- compressionRatio: result.compression_ratio
1530
- }]
2399
+ compression,
2400
+ warnings
2401
+ };
2402
+ }
2403
+ function usesPlaceholderCompression(asset) {
2404
+ const hasContextInputCompression = getContextInputs(asset).some(
2405
+ (input) => input.compression?.heuristic?.enabled === true || input.compression?.code?.enabled === true
2406
+ );
2407
+ if (hasContextInputCompression) {
2408
+ return true;
2409
+ }
2410
+ return Boolean(
2411
+ asset.sections.system_instructions && hasCompressionModifier(asset.sections.system_instructions) || asset.sections.prompt_template && hasCompressionModifier(asset.sections.prompt_template)
2412
+ );
2413
+ }
2414
+ function hasCompressionModifier(template) {
2415
+ return /\{\{\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\|\s*(?:compress|toon|compact|code)\s*\}\}/.test(template);
2416
+ }
2417
+ function resolveHeuristicPromptQuery(options, variables, systemInstructions) {
2418
+ if (options?.query !== void 0) {
2419
+ return options.query;
2420
+ }
2421
+ if (options?.query_variable && variables?.[options.query_variable] !== void 0) {
2422
+ return variables[options.query_variable];
2423
+ }
2424
+ return systemInstructions;
2425
+ }
2426
+ function toCodeCompactionOptions2(options) {
2427
+ return {
2428
+ remove_comments: options?.remove_comments,
2429
+ trim_indentation: options?.trim_indentation,
2430
+ collapse_blank_lines: options?.collapse_blank_lines
1531
2431
  };
1532
2432
  }
2433
+ function reportHeuristicCompressionWarnings2(warnings, compressionWarnings, scope) {
2434
+ for (const warning of compressionWarnings ?? []) {
2435
+ warnings.push(`POK031: ${warning} Scope: ${scope}.`);
2436
+ }
2437
+ }
1533
2438
  async function compressWithTheTokenCompany(input, options) {
1534
2439
  const apiKey = options.apiKey ?? getEnv("THETOKENCOMPANY_API_KEY") ?? getEnv("TTC_API_KEY");
1535
2440
  if (!apiKey) {
@@ -1617,7 +2522,14 @@ function withPromptInputSupport(adapter) {
1617
2522
  history: compactHistoryForPrompt(resolvedForRender, runtime)
1618
2523
  });
1619
2524
  const request = adapter.render(prepared.asset, prepared.runtime);
1620
- return prepared.compression.length > 0 ? { ...request, compression: prepared.compression } : request;
2525
+ return prepared.compression.length > 0 || prepared.warnings.length > 0 ? {
2526
+ ...request,
2527
+ ...prepared.compression.length > 0 ? {
2528
+ compression: prepared.compression,
2529
+ compressionSummary: summarizePromptCompression(prepared.compression)
2530
+ } : {},
2531
+ ...prepared.warnings.length > 0 ? { warnings: prepared.warnings } : {}
2532
+ } : request;
1621
2533
  };
1622
2534
  return {
1623
2535
  ...adapter,