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