promptopskit 0.3.5 → 0.3.6

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 (56) hide show
  1. package/README.md +2 -1
  2. package/SKILL.md +4 -1
  3. package/dist/{chunk-2LK6IILW.js → chunk-6FLNJVE7.js} +5 -2
  4. package/dist/chunk-6FLNJVE7.js.map +1 -0
  5. package/dist/{chunk-7RWTFGMS.js → chunk-J32I6DSG.js} +2 -2
  6. package/dist/{chunk-2X5HFPSD.js → chunk-MN3RQ7DZ.js} +2 -2
  7. package/dist/{chunk-4QW4BSGE.js → chunk-MYXDJMWV.js} +2 -2
  8. package/dist/{chunk-M5VKRDIY.js → chunk-SHYKSLVR.js} +146 -3
  9. package/dist/chunk-SHYKSLVR.js.map +1 -0
  10. package/dist/{chunk-ROBYCHAW.js → chunk-SOY2CEJM.js} +3 -3
  11. package/dist/cli/index.js +32 -2
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/index.cjs +173 -46
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +7 -5
  16. package/dist/index.d.ts +7 -5
  17. package/dist/index.js +39 -50
  18. package/dist/index.js.map +1 -1
  19. package/dist/providers/anthropic.cjs +120 -2
  20. package/dist/providers/anthropic.cjs.map +1 -1
  21. package/dist/providers/anthropic.d.cts +2 -2
  22. package/dist/providers/anthropic.d.ts +2 -2
  23. package/dist/providers/anthropic.js +3 -3
  24. package/dist/providers/gemini.cjs +120 -2
  25. package/dist/providers/gemini.cjs.map +1 -1
  26. package/dist/providers/gemini.d.cts +2 -2
  27. package/dist/providers/gemini.d.ts +2 -2
  28. package/dist/providers/gemini.js +3 -3
  29. package/dist/providers/openai.cjs +120 -2
  30. package/dist/providers/openai.cjs.map +1 -1
  31. package/dist/providers/openai.d.cts +2 -2
  32. package/dist/providers/openai.d.ts +2 -2
  33. package/dist/providers/openai.js +3 -3
  34. package/dist/providers/openrouter.cjs +120 -2
  35. package/dist/providers/openrouter.cjs.map +1 -1
  36. package/dist/providers/openrouter.d.cts +2 -2
  37. package/dist/providers/openrouter.d.ts +2 -2
  38. package/dist/providers/openrouter.js +4 -4
  39. package/dist/{schema-Dq0jKest.d.cts → schema-D145q3Dw.d.cts} +63 -42
  40. package/dist/{schema-Dq0jKest.d.ts → schema-D145q3Dw.d.ts} +63 -42
  41. package/dist/testing.cjs +4 -1
  42. package/dist/testing.cjs.map +1 -1
  43. package/dist/testing.d.cts +1 -1
  44. package/dist/testing.d.ts +1 -1
  45. package/dist/testing.js +1 -1
  46. package/dist/{types-ClXTFaX-.d.cts → types-B3sWHzIo.d.cts} +8 -1
  47. package/dist/{types-lLD7m02V.d.ts → types-CXlVWckk.d.ts} +8 -1
  48. package/dist/usagetap/index.d.cts +2 -2
  49. package/dist/usagetap/index.d.ts +2 -2
  50. package/package.json +1 -1
  51. package/dist/chunk-2LK6IILW.js.map +0 -1
  52. package/dist/chunk-M5VKRDIY.js.map +0 -1
  53. /package/dist/{chunk-7RWTFGMS.js.map → chunk-J32I6DSG.js.map} +0 -0
  54. /package/dist/{chunk-2X5HFPSD.js.map → chunk-MN3RQ7DZ.js.map} +0 -0
  55. /package/dist/{chunk-4QW4BSGE.js.map → chunk-MYXDJMWV.js.map} +0 -0
  56. /package/dist/{chunk-ROBYCHAW.js.map → chunk-SOY2CEJM.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -253,7 +253,10 @@ var HistorySchema = import_zod.z.object({
253
253
  });
254
254
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
255
255
  name: import_zod.z.string(),
256
- max_size: import_zod.z.number().int().positive().optional()
256
+ max_size: import_zod.z.number().int().positive().optional(),
257
+ trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
258
+ allow_regex: import_zod.z.string().optional(),
259
+ deny_regex: import_zod.z.string().optional()
257
260
  });
258
261
  var ContextInputDefinitionSchema = import_zod.z.union([
259
262
  import_zod.z.string(),
@@ -596,6 +599,139 @@ function resolveInlinePromptSource(source, options = {}) {
596
599
  });
597
600
  }
598
601
 
602
+ // src/context.ts
603
+ var textEncoder = new TextEncoder();
604
+ function getContextInputs(asset) {
605
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
606
+ }
607
+ function getContextInputNames(asset) {
608
+ return getContextInputs(asset).map((input) => input.name);
609
+ }
610
+ function normalizeContextInput(input) {
611
+ if (typeof input === "string") {
612
+ return { name: input };
613
+ }
614
+ return {
615
+ name: input.name,
616
+ max_size: input.max_size,
617
+ trim: input.trim,
618
+ allow_regex: input.allow_regex,
619
+ deny_regex: input.deny_regex
620
+ };
621
+ }
622
+ function isTrimEnabled(mode) {
623
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
624
+ }
625
+ function normalizeTrimMode(mode) {
626
+ if (mode === "start") {
627
+ return "start";
628
+ }
629
+ return "end";
630
+ }
631
+ function trimToMaxSize(value, maxSize, mode) {
632
+ const measured = measureContextValueSize(value);
633
+ if (measured <= maxSize) {
634
+ return value;
635
+ }
636
+ const characters = Array.from(value);
637
+ const normalizedMode = normalizeTrimMode(mode);
638
+ if (normalizedMode === "start") {
639
+ let collected2 = "";
640
+ let size2 = 0;
641
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
642
+ const next = characters[i];
643
+ const charSize = measureContextValueSize(next);
644
+ if (size2 + charSize > maxSize) {
645
+ break;
646
+ }
647
+ collected2 = next + collected2;
648
+ size2 += charSize;
649
+ }
650
+ return collected2;
651
+ }
652
+ let collected = "";
653
+ let size = 0;
654
+ for (const char of characters) {
655
+ const charSize = measureContextValueSize(char);
656
+ if (size + charSize > maxSize) {
657
+ break;
658
+ }
659
+ collected += char;
660
+ size += charSize;
661
+ }
662
+ return collected;
663
+ }
664
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
665
+ const { onContextOverflow } = options;
666
+ const sanitized = { ...variables };
667
+ for (const input of getContextInputs(asset)) {
668
+ const value = sanitized[input.name];
669
+ if (value === void 0) {
670
+ continue;
671
+ }
672
+ let candidate = value;
673
+ if (input.max_size !== void 0) {
674
+ const actualSize = measureContextValueSize(candidate);
675
+ if (actualSize > input.max_size && onContextOverflow) {
676
+ candidate = onContextOverflow({
677
+ promptId: asset.id,
678
+ variable: input.name,
679
+ value: candidate,
680
+ maxSize: input.max_size,
681
+ actualSize
682
+ });
683
+ }
684
+ }
685
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
686
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
687
+ }
688
+ sanitized[input.name] = candidate;
689
+ if (input.allow_regex) {
690
+ const candidate2 = sanitized[input.name];
691
+ const matcher = new RegExp(input.allow_regex);
692
+ if (!matcher.test(candidate2)) {
693
+ throw new Error(
694
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
695
+ );
696
+ }
697
+ }
698
+ if (input.deny_regex) {
699
+ const candidate2 = sanitized[input.name];
700
+ const matcher = new RegExp(input.deny_regex);
701
+ if (matcher.test(candidate2)) {
702
+ throw new Error(
703
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
704
+ );
705
+ }
706
+ }
707
+ }
708
+ return sanitized;
709
+ }
710
+ function measureContextValueSize(value) {
711
+ return textEncoder.encode(value).length;
712
+ }
713
+ function collectContextSizeWarnings(asset, variables = {}) {
714
+ const warnings = [];
715
+ for (const input of getContextInputs(asset)) {
716
+ if (input.max_size === void 0) {
717
+ continue;
718
+ }
719
+ const value = variables[input.name];
720
+ if (value === void 0) {
721
+ continue;
722
+ }
723
+ const actualSize = measureContextValueSize(value);
724
+ if (actualSize > input.max_size) {
725
+ warnings.push({
726
+ variable: input.name,
727
+ maxSize: input.max_size,
728
+ actualSize
729
+ });
730
+ }
731
+ }
732
+ return warnings;
733
+ }
734
+
599
735
  // src/providers/prompt-input.ts
600
736
  function isPromptLookup(input) {
601
737
  return "path" in input && typeof input.path === "string";
@@ -619,7 +755,13 @@ function withPromptInputSupport(adapter) {
619
755
  };
620
756
  const renderPrompt2 = async (input, runtime) => {
621
757
  const resolved = await resolveProviderPromptInput(input, runtime);
622
- return adapter.render(resolved, runtime);
758
+ const variables = sanitizeContextVariables(resolved, runtime.variables, {
759
+ onContextOverflow: runtime.onContextOverflow
760
+ });
761
+ return adapter.render(resolved, {
762
+ ...runtime,
763
+ variables
764
+ });
623
765
  };
624
766
  return {
625
767
  ...adapter,
@@ -906,48 +1048,6 @@ function getAdapter(provider) {
906
1048
  return adapter;
907
1049
  }
908
1050
 
909
- // src/context.ts
910
- var textEncoder = new TextEncoder();
911
- function getContextInputs(asset) {
912
- return (asset.context?.inputs ?? []).map(normalizeContextInput);
913
- }
914
- function getContextInputNames(asset) {
915
- return getContextInputs(asset).map((input) => input.name);
916
- }
917
- function normalizeContextInput(input) {
918
- if (typeof input === "string") {
919
- return { name: input };
920
- }
921
- return {
922
- name: input.name,
923
- max_size: input.max_size
924
- };
925
- }
926
- function measureContextValueSize(value) {
927
- return textEncoder.encode(value).length;
928
- }
929
- function collectContextSizeWarnings(asset, variables = {}) {
930
- const warnings = [];
931
- for (const input of getContextInputs(asset)) {
932
- if (input.max_size === void 0) {
933
- continue;
934
- }
935
- const value = variables[input.name];
936
- if (value === void 0) {
937
- continue;
938
- }
939
- const actualSize = measureContextValueSize(value);
940
- if (actualSize > input.max_size) {
941
- warnings.push({
942
- variable: input.name,
943
- maxSize: input.max_size,
944
- actualSize
945
- });
946
- }
947
- }
948
- return warnings;
949
- }
950
-
951
1051
  // src/validation/levenshtein.ts
952
1052
  function levenshtein(a, b) {
953
1053
  const m = a.length;
@@ -1059,6 +1159,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1059
1159
  });
1060
1160
  }
1061
1161
  }
1162
+ for (const input of getContextInputs(asset)) {
1163
+ if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1164
+ warnings.push({
1165
+ code: "POK014",
1166
+ message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
1167
+ filePath
1168
+ });
1169
+ }
1170
+ const checks = [];
1171
+ if (input.allow_regex) checks.push({ pattern: input.allow_regex, kind: "allow_regex" });
1172
+ if (input.deny_regex) checks.push({ pattern: input.deny_regex, kind: "deny_regex" });
1173
+ for (const check of checks) {
1174
+ try {
1175
+ new RegExp(check.pattern);
1176
+ } catch (error) {
1177
+ const reason = error instanceof Error ? error.message : String(error);
1178
+ errors.push({
1179
+ code: "POK013",
1180
+ message: `Invalid context ${check.kind} for "${input.name}": ${reason}`,
1181
+ filePath
1182
+ });
1183
+ }
1184
+ }
1185
+ }
1062
1186
  return {
1063
1187
  valid: errors.length === 0,
1064
1188
  errors,
@@ -1540,7 +1664,10 @@ var PromptOpsKit = class {
1540
1664
  ` + validation.errors.map((e) => ` - ${e}`).join("\n")
1541
1665
  );
1542
1666
  }
1543
- const contextSizeWarnings = collectContextSizeWarnings(resolved, options.variables).map(
1667
+ const sanitizedVariables = sanitizeContextVariables(resolved, options.variables, {
1668
+ onContextOverflow: options.onContextOverflow
1669
+ });
1670
+ const contextSizeWarnings = collectContextSizeWarnings(resolved, sanitizedVariables).map(
1544
1671
  (warning) => formatContextSizeWarning(resolved, warning)
1545
1672
  );
1546
1673
  const contextWarningPolicy = this.config.warnings?.contextSize;
@@ -1550,7 +1677,7 @@ var PromptOpsKit = class {
1550
1677
  }
1551
1678
  }
1552
1679
  const request = adapter.render(resolved, {
1553
- variables: options.variables,
1680
+ variables: sanitizedVariables,
1554
1681
  history: options.history,
1555
1682
  toolRegistry: options.toolRegistry,
1556
1683
  strict: options.strict