promptopskit 0.3.5 → 0.3.7

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 +12 -5
  2. package/SKILL.md +8 -1
  3. package/dist/{chunk-7RWTFGMS.js → chunk-5TLHYSP7.js} +2 -2
  4. package/dist/{chunk-M5VKRDIY.js → chunk-6XKV4YVK.js} +256 -3
  5. package/dist/chunk-6XKV4YVK.js.map +1 -0
  6. package/dist/{chunk-ROBYCHAW.js → chunk-DGLLQ3FR.js} +3 -3
  7. package/dist/{chunk-2LK6IILW.js → chunk-IXPIBZXT.js} +14 -2
  8. package/dist/chunk-IXPIBZXT.js.map +1 -0
  9. package/dist/{chunk-4QW4BSGE.js → chunk-KFSP5KN4.js} +2 -2
  10. package/dist/{chunk-2X5HFPSD.js → chunk-QPOHKVY5.js} +2 -2
  11. package/dist/cli/index.js +256 -93
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/index.cjs +295 -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 +44 -50
  18. package/dist/index.js.map +1 -1
  19. package/dist/providers/anthropic.cjs +238 -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 +238 -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 +238 -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 +238 -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-Bgoff-CN.d.cts} +89 -0
  40. package/dist/{schema-Dq0jKest.d.ts → schema-Bgoff-CN.d.ts} +89 -0
  41. package/dist/testing.cjs +13 -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-7U58bgVW.d.cts} +8 -1
  47. package/dist/{types-lLD7m02V.d.ts → types-HnZ46u5Q.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-5TLHYSP7.js.map} +0 -0
  54. /package/dist/{chunk-ROBYCHAW.js.map → chunk-DGLLQ3FR.js.map} +0 -0
  55. /package/dist/{chunk-4QW4BSGE.js.map → chunk-KFSP5KN4.js.map} +0 -0
  56. /package/dist/{chunk-2X5HFPSD.js.map → chunk-QPOHKVY5.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -251,9 +251,21 @@ var ResponseSchema = import_zod.z.object({
251
251
  var HistorySchema = import_zod.z.object({
252
252
  max_items: import_zod.z.number().int().positive().optional()
253
253
  });
254
+ var ContextRegexSchema = import_zod.z.union([
255
+ import_zod.z.string(),
256
+ import_zod.z.object({
257
+ pattern: import_zod.z.string(),
258
+ flags: import_zod.z.string().optional()
259
+ })
260
+ ]);
254
261
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
255
262
  name: import_zod.z.string(),
256
- max_size: import_zod.z.number().int().positive().optional()
263
+ max_size: import_zod.z.number().int().positive().optional(),
264
+ trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
265
+ allow_regex: ContextRegexSchema.optional(),
266
+ deny_regex: ContextRegexSchema.optional(),
267
+ non_empty: import_zod.z.boolean().optional(),
268
+ reject_secrets: import_zod.z.boolean().optional()
257
269
  });
258
270
  var ContextInputDefinitionSchema = import_zod.z.union([
259
271
  import_zod.z.string(),
@@ -596,6 +608,248 @@ function resolveInlinePromptSource(source, options = {}) {
596
608
  });
597
609
  }
598
610
 
611
+ // src/context.ts
612
+ var textEncoder = new TextEncoder();
613
+ var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
614
+ var REJECT_SECRETS_FLAGS = "i";
615
+ function getContextInputs(asset) {
616
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
617
+ }
618
+ function getContextInputNames(asset) {
619
+ return getContextInputs(asset).map((input) => input.name);
620
+ }
621
+ function normalizeContextInput(input) {
622
+ if (typeof input === "string") {
623
+ return { name: input };
624
+ }
625
+ return {
626
+ name: input.name,
627
+ max_size: input.max_size,
628
+ trim: input.trim,
629
+ allow_regex: normalizeContextRegex(input.allow_regex),
630
+ deny_regex: normalizeContextRegex(input.deny_regex),
631
+ non_empty: input.non_empty,
632
+ reject_secrets: input.reject_secrets
633
+ };
634
+ }
635
+ function normalizeContextRegex(value) {
636
+ if (value === void 0) {
637
+ return void 0;
638
+ }
639
+ if (typeof value === "string") {
640
+ const literal = parseRegexLiteral(value);
641
+ if (value.startsWith("/") && !literal) {
642
+ return {
643
+ pattern: value,
644
+ flags: "",
645
+ raw: value,
646
+ invalidLiteral: true
647
+ };
648
+ }
649
+ return {
650
+ pattern: literal?.pattern ?? value,
651
+ flags: literal?.flags ?? "",
652
+ raw: value
653
+ };
654
+ }
655
+ return {
656
+ pattern: value.pattern,
657
+ flags: value.flags ?? "",
658
+ raw: JSON.stringify(value)
659
+ };
660
+ }
661
+ function parseRegexLiteral(value) {
662
+ if (!value.startsWith("/")) {
663
+ return void 0;
664
+ }
665
+ for (let index = value.length - 1; index > 0; index -= 1) {
666
+ if (value[index] !== "/") {
667
+ continue;
668
+ }
669
+ let backslashCount = 0;
670
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
671
+ backslashCount += 1;
672
+ }
673
+ if (backslashCount % 2 === 1) {
674
+ continue;
675
+ }
676
+ return {
677
+ pattern: value.slice(1, index),
678
+ flags: value.slice(index + 1)
679
+ };
680
+ }
681
+ return void 0;
682
+ }
683
+ function formatInvalidContextRegexMessage(details) {
684
+ return [
685
+ `Invalid context regex for prompt "${details.promptId}"`,
686
+ `variable "${details.variable}"`,
687
+ `field "${details.field}"`,
688
+ `value ${JSON.stringify(details.raw)}: ${details.reason}`
689
+ ].join(", ");
690
+ }
691
+ function compileContextRegex(regex, details) {
692
+ if (regex.invalidLiteral) {
693
+ throw new Error(
694
+ `POK013: ${formatInvalidContextRegexMessage({
695
+ ...details,
696
+ raw: regex.raw,
697
+ reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
698
+ })}`
699
+ );
700
+ }
701
+ try {
702
+ return new RegExp(regex.pattern, regex.flags);
703
+ } catch (error) {
704
+ const reason = error instanceof Error ? error.message : String(error);
705
+ throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
706
+ }
707
+ }
708
+ function getRejectSecretsRegex() {
709
+ return {
710
+ pattern: REJECT_SECRETS_PATTERN,
711
+ flags: REJECT_SECRETS_FLAGS,
712
+ raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
713
+ };
714
+ }
715
+ function isTrimEnabled(mode) {
716
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
717
+ }
718
+ function normalizeTrimMode(mode) {
719
+ if (mode === "start") {
720
+ return "start";
721
+ }
722
+ return "end";
723
+ }
724
+ function trimToMaxSize(value, maxSize, mode) {
725
+ const measured = measureContextValueSize(value);
726
+ if (measured <= maxSize) {
727
+ return value;
728
+ }
729
+ const characters = Array.from(value);
730
+ const normalizedMode = normalizeTrimMode(mode);
731
+ if (normalizedMode === "start") {
732
+ let collected2 = "";
733
+ let size2 = 0;
734
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
735
+ const next = characters[i];
736
+ const charSize = measureContextValueSize(next);
737
+ if (size2 + charSize > maxSize) {
738
+ break;
739
+ }
740
+ collected2 = next + collected2;
741
+ size2 += charSize;
742
+ }
743
+ return collected2;
744
+ }
745
+ let collected = "";
746
+ let size = 0;
747
+ for (const char of characters) {
748
+ const charSize = measureContextValueSize(char);
749
+ if (size + charSize > maxSize) {
750
+ break;
751
+ }
752
+ collected += char;
753
+ size += charSize;
754
+ }
755
+ return collected;
756
+ }
757
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
758
+ const { onContextOverflow } = options;
759
+ const sanitized = { ...variables };
760
+ for (const input of getContextInputs(asset)) {
761
+ const value = sanitized[input.name];
762
+ if (value === void 0) {
763
+ continue;
764
+ }
765
+ let candidate = value;
766
+ if (input.max_size !== void 0) {
767
+ const actualSize = measureContextValueSize(candidate);
768
+ if (actualSize > input.max_size && onContextOverflow) {
769
+ candidate = onContextOverflow({
770
+ promptId: asset.id,
771
+ variable: input.name,
772
+ value: candidate,
773
+ maxSize: input.max_size,
774
+ actualSize
775
+ });
776
+ }
777
+ }
778
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
779
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
780
+ }
781
+ sanitized[input.name] = candidate;
782
+ if (input.allow_regex) {
783
+ const candidate2 = sanitized[input.name];
784
+ const matcher = compileContextRegex(input.allow_regex, {
785
+ promptId: asset.id,
786
+ variable: input.name,
787
+ field: "allow_regex"
788
+ });
789
+ if (!matcher.test(candidate2)) {
790
+ throw new Error(
791
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
792
+ );
793
+ }
794
+ }
795
+ if (input.deny_regex) {
796
+ const candidate2 = sanitized[input.name];
797
+ const matcher = compileContextRegex(input.deny_regex, {
798
+ promptId: asset.id,
799
+ variable: input.name,
800
+ field: "deny_regex"
801
+ });
802
+ if (matcher.test(candidate2)) {
803
+ throw new Error(
804
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
805
+ );
806
+ }
807
+ }
808
+ if (input.non_empty && candidate.trim().length === 0) {
809
+ throw new Error(
810
+ `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
811
+ );
812
+ }
813
+ if (input.reject_secrets) {
814
+ const matcher = compileContextRegex(getRejectSecretsRegex(), {
815
+ promptId: asset.id,
816
+ variable: input.name,
817
+ field: "reject_secrets"
818
+ });
819
+ if (matcher.test(candidate)) {
820
+ throw new Error(
821
+ `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
822
+ );
823
+ }
824
+ }
825
+ }
826
+ return sanitized;
827
+ }
828
+ function measureContextValueSize(value) {
829
+ return textEncoder.encode(value).length;
830
+ }
831
+ function collectContextSizeWarnings(asset, variables = {}) {
832
+ const warnings = [];
833
+ for (const input of getContextInputs(asset)) {
834
+ if (input.max_size === void 0) {
835
+ continue;
836
+ }
837
+ const value = variables[input.name];
838
+ if (value === void 0) {
839
+ continue;
840
+ }
841
+ const actualSize = measureContextValueSize(value);
842
+ if (actualSize > input.max_size) {
843
+ warnings.push({
844
+ variable: input.name,
845
+ maxSize: input.max_size,
846
+ actualSize
847
+ });
848
+ }
849
+ }
850
+ return warnings;
851
+ }
852
+
599
853
  // src/providers/prompt-input.ts
600
854
  function isPromptLookup(input) {
601
855
  return "path" in input && typeof input.path === "string";
@@ -619,7 +873,13 @@ function withPromptInputSupport(adapter) {
619
873
  };
620
874
  const renderPrompt2 = async (input, runtime) => {
621
875
  const resolved = await resolveProviderPromptInput(input, runtime);
622
- return adapter.render(resolved, runtime);
876
+ const variables = sanitizeContextVariables(resolved, runtime.variables, {
877
+ onContextOverflow: runtime.onContextOverflow
878
+ });
879
+ return adapter.render(resolved, {
880
+ ...runtime,
881
+ variables
882
+ });
623
883
  };
624
884
  return {
625
885
  ...adapter,
@@ -906,48 +1166,6 @@ function getAdapter(provider) {
906
1166
  return adapter;
907
1167
  }
908
1168
 
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
1169
  // src/validation/levenshtein.ts
952
1170
  function levenshtein(a, b) {
953
1171
  const m = a.length;
@@ -1059,6 +1277,34 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1059
1277
  });
1060
1278
  }
1061
1279
  }
1280
+ for (const input of getContextInputs(asset)) {
1281
+ if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1282
+ warnings.push({
1283
+ code: "POK014",
1284
+ message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
1285
+ filePath
1286
+ });
1287
+ }
1288
+ const checks = [];
1289
+ if (input.allow_regex) checks.push({ regex: input.allow_regex, kind: "allow_regex" });
1290
+ if (input.deny_regex) checks.push({ regex: input.deny_regex, kind: "deny_regex" });
1291
+ for (const check of checks) {
1292
+ try {
1293
+ compileContextRegex(check.regex, {
1294
+ promptId: asset.id,
1295
+ variable: input.name,
1296
+ field: check.kind
1297
+ });
1298
+ } catch (error) {
1299
+ const reason = error instanceof Error ? error.message.replace(/^POK013:\s*/, "") : String(error);
1300
+ errors.push({
1301
+ code: "POK013",
1302
+ message: reason,
1303
+ filePath
1304
+ });
1305
+ }
1306
+ }
1307
+ }
1062
1308
  return {
1063
1309
  valid: errors.length === 0,
1064
1310
  errors,
@@ -1540,7 +1786,10 @@ var PromptOpsKit = class {
1540
1786
  ` + validation.errors.map((e) => ` - ${e}`).join("\n")
1541
1787
  );
1542
1788
  }
1543
- const contextSizeWarnings = collectContextSizeWarnings(resolved, options.variables).map(
1789
+ const sanitizedVariables = sanitizeContextVariables(resolved, options.variables, {
1790
+ onContextOverflow: options.onContextOverflow
1791
+ });
1792
+ const contextSizeWarnings = collectContextSizeWarnings(resolved, sanitizedVariables).map(
1544
1793
  (warning) => formatContextSizeWarning(resolved, warning)
1545
1794
  );
1546
1795
  const contextWarningPolicy = this.config.warnings?.contextSize;
@@ -1550,7 +1799,7 @@ var PromptOpsKit = class {
1550
1799
  }
1551
1800
  }
1552
1801
  const request = adapter.render(resolved, {
1553
- variables: options.variables,
1802
+ variables: sanitizedVariables,
1554
1803
  history: options.history,
1555
1804
  toolRegistry: options.toolRegistry,
1556
1805
  strict: options.strict