promptopskit 0.5.0 → 0.7.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 +9 -7
  2. package/SKILL.md +16 -10
  3. package/dist/{chunk-QK3WKE3K.js → chunk-35NN5F6O.js} +2 -2
  4. package/dist/{chunk-D75HKF64.js → chunk-3G3EAV7H.js} +2 -2
  5. package/dist/{chunk-LNGHGGJN.js → chunk-6NOUETAC.js} +2 -2
  6. package/dist/{chunk-KN7H4JRK.js → chunk-F552OBUV.js} +619 -502
  7. package/dist/chunk-F552OBUV.js.map +1 -0
  8. package/dist/{chunk-6BOUQVSX.js → chunk-FICB2ZSK.js} +3 -3
  9. package/dist/{chunk-6VLKZCNS.js → chunk-OWMP5NMV.js} +2 -2
  10. package/dist/{chunk-IN4KT5EU.js → chunk-R65RAHAG.js} +3 -3
  11. package/dist/{chunk-K6ZU3CGV.js → chunk-ULVY473P.js} +306 -17
  12. package/dist/chunk-ULVY473P.js.map +1 -0
  13. package/dist/cli/index.js +446 -103
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/index.cjs +1223 -772
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +9 -5
  18. package/dist/index.d.ts +9 -5
  19. package/dist/index.js +67 -18
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/anthropic.cjs +902 -502
  22. package/dist/providers/anthropic.cjs.map +1 -1
  23. package/dist/providers/anthropic.d.cts +2 -2
  24. package/dist/providers/anthropic.d.ts +2 -2
  25. package/dist/providers/anthropic.js +3 -3
  26. package/dist/providers/gemini.cjs +902 -502
  27. package/dist/providers/gemini.cjs.map +1 -1
  28. package/dist/providers/gemini.d.cts +2 -2
  29. package/dist/providers/gemini.d.ts +2 -2
  30. package/dist/providers/gemini.js +3 -3
  31. package/dist/providers/llmasaservice.cjs +901 -501
  32. package/dist/providers/llmasaservice.cjs.map +1 -1
  33. package/dist/providers/llmasaservice.d.cts +2 -2
  34. package/dist/providers/llmasaservice.d.ts +2 -2
  35. package/dist/providers/llmasaservice.js +4 -4
  36. package/dist/providers/openai-responses.cjs +902 -502
  37. package/dist/providers/openai-responses.cjs.map +1 -1
  38. package/dist/providers/openai-responses.d.cts +2 -2
  39. package/dist/providers/openai-responses.d.ts +2 -2
  40. package/dist/providers/openai-responses.js +3 -3
  41. package/dist/providers/openai.cjs +902 -502
  42. package/dist/providers/openai.cjs.map +1 -1
  43. package/dist/providers/openai.d.cts +2 -2
  44. package/dist/providers/openai.d.ts +2 -2
  45. package/dist/providers/openai.js +3 -3
  46. package/dist/providers/openrouter.cjs +902 -502
  47. package/dist/providers/openrouter.cjs.map +1 -1
  48. package/dist/providers/openrouter.d.cts +2 -2
  49. package/dist/providers/openrouter.d.ts +2 -2
  50. package/dist/providers/openrouter.js +4 -4
  51. package/dist/{schema-D-RI4w-k.d.cts → schema-DBcSns_b.d.cts} +732 -206
  52. package/dist/{schema-D-RI4w-k.d.ts → schema-DBcSns_b.d.ts} +732 -206
  53. package/dist/testing.cjs +54 -5
  54. package/dist/testing.cjs.map +1 -1
  55. package/dist/testing.d.cts +1 -1
  56. package/dist/testing.d.ts +1 -1
  57. package/dist/testing.js +1 -1
  58. package/dist/{types-B3dxBGu6.d.ts → types-8T2D5KB5.d.cts} +20 -2
  59. package/dist/{types-DbTAjlTN.d.cts → types-BlLXT5Qb.d.ts} +20 -2
  60. package/dist/usagetap/index.d.cts +2 -2
  61. package/dist/usagetap/index.d.ts +2 -2
  62. package/package.json +9 -2
  63. package/dist/chunk-K6ZU3CGV.js.map +0 -1
  64. package/dist/chunk-KN7H4JRK.js.map +0 -1
  65. /package/dist/{chunk-QK3WKE3K.js.map → chunk-35NN5F6O.js.map} +0 -0
  66. /package/dist/{chunk-D75HKF64.js.map → chunk-3G3EAV7H.js.map} +0 -0
  67. /package/dist/{chunk-LNGHGGJN.js.map → chunk-6NOUETAC.js.map} +0 -0
  68. /package/dist/{chunk-6BOUQVSX.js.map → chunk-FICB2ZSK.js.map} +0 -0
  69. /package/dist/{chunk-6VLKZCNS.js.map → chunk-OWMP5NMV.js.map} +0 -0
  70. /package/dist/{chunk-IN4KT5EU.js.map → chunk-R65RAHAG.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -37,6 +37,8 @@ __export(src_exports, {
37
37
  PromptAssetOverridesSchema: () => PromptAssetOverridesSchema,
38
38
  PromptAssetSchema: () => PromptAssetSchema,
39
39
  PromptOpsKit: () => PromptOpsKit,
40
+ THETOKENCOMPANY_DEFAULT_BASE_URL: () => THETOKENCOMPANY_DEFAULT_BASE_URL,
41
+ THETOKENCOMPANY_DEFAULT_MODEL: () => THETOKENCOMPANY_DEFAULT_MODEL,
40
42
  anthropicAdapter: () => anthropicAdapter,
41
43
  applyOverrides: () => applyOverrides,
42
44
  applyUsageTapEntitlements: () => applyUsageTapEntitlements,
@@ -72,7 +74,7 @@ __export(src_exports, {
72
74
  withUsageTapCall: () => withUsageTapCall
73
75
  });
74
76
  module.exports = __toCommonJS(src_exports);
75
- var import_node_path4 = require("path");
77
+ var import_node_path5 = require("path");
76
78
 
77
79
  // src/renderer/interpolate.ts
78
80
  var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
@@ -80,12 +82,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
80
82
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
81
83
  function interpolate(template, variables, options = {}) {
82
84
  const { strict = false } = options;
85
+ const optionalVariables = new Set(options.optionalVariables ?? []);
83
86
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
84
87
  result = result.replace(VARIABLE_RE, (match, name) => {
85
88
  if (name in variables) {
86
89
  return variables[name];
87
90
  }
88
- if (strict) {
91
+ if (strict && !optionalVariables.has(name)) {
89
92
  throw new Error(`Missing required variable: "${name}"`);
90
93
  }
91
94
  return match;
@@ -103,159 +106,473 @@ function extractVariables(template) {
103
106
  return [...vars];
104
107
  }
105
108
 
106
- // src/renderer/renderer.ts
107
- function renderSections(asset, options = {}) {
108
- const { variables = {}, strict = false } = options;
109
- const result = {};
110
- if (asset.sections.system_instructions) {
111
- result.system_instructions = interpolate(
112
- asset.sections.system_instructions,
113
- variables,
114
- { strict }
115
- );
116
- }
117
- if (asset.sections.prompt_template) {
118
- result.prompt_template = interpolate(
119
- asset.sections.prompt_template,
120
- variables,
121
- { strict }
122
- );
123
- }
124
- return result;
109
+ // src/context.ts
110
+ var textEncoder = new TextEncoder();
111
+ var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
112
+ var REJECT_SECRETS_FLAGS = "i";
113
+ function getContextInputs(asset) {
114
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
125
115
  }
126
-
127
- // src/overrides/apply-overrides.ts
128
- function applyOverrides(asset, options = {}) {
129
- let result = { ...asset };
130
- if (options.environment && result.environments?.[options.environment]) {
131
- result = mergeOverride(result, result.environments[options.environment]);
116
+ function normalizeContextInput(input) {
117
+ if (typeof input === "string") {
118
+ return { name: input };
132
119
  }
133
- if (options.tier && result.tiers?.[options.tier]) {
134
- result = mergeOverride(result, result.tiers[options.tier]);
120
+ return {
121
+ name: input.name,
122
+ optional: input.optional,
123
+ warnings: input.warnings,
124
+ max_size: input.max_size,
125
+ trim: input.trim,
126
+ allow_regex: normalizeContextRegex(input.allow_regex),
127
+ deny_regex: normalizeContextRegex(input.deny_regex),
128
+ non_empty: normalizeBuiltInValidator(input.non_empty),
129
+ reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
130
+ };
131
+ }
132
+ function areContextInputWarningsEnabled(input) {
133
+ return input.warnings !== false;
134
+ }
135
+ function normalizeContextRegex(value) {
136
+ if (value === void 0) {
137
+ return void 0;
135
138
  }
136
- if (options.runtime) {
137
- result = mergeOverride(result, options.runtime);
139
+ if (typeof value === "string") {
140
+ const literal = parseRegexLiteral(value);
141
+ if (value.startsWith("/") && !literal) {
142
+ return {
143
+ pattern: value,
144
+ flags: "",
145
+ raw: value,
146
+ invalidLiteral: true
147
+ };
148
+ }
149
+ return {
150
+ pattern: literal?.pattern ?? value,
151
+ flags: literal?.flags ?? "",
152
+ raw: value
153
+ };
138
154
  }
139
- return result;
155
+ return {
156
+ pattern: value.pattern,
157
+ flags: value.flags ?? "",
158
+ raw: JSON.stringify(value),
159
+ returnMessage: value.return_message
160
+ };
140
161
  }
141
- function mergeOverride(base, override) {
142
- const result = { ...base };
143
- if (override.model !== void 0) result.model = override.model;
144
- if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
145
- if (override.tools !== void 0) result.tools = override.tools;
146
- if (override.reasoning !== void 0) {
147
- result.reasoning = { ...result.reasoning, ...override.reasoning };
162
+ function normalizeBuiltInValidator(value) {
163
+ if (value === void 0 || value === false) {
164
+ return void 0;
148
165
  }
149
- if (override.sampling !== void 0) {
150
- result.sampling = { ...result.sampling, ...override.sampling };
166
+ if (value === true) {
167
+ return {};
151
168
  }
152
- if (override.response !== void 0) {
153
- result.response = { ...result.response, ...override.response };
169
+ return {
170
+ returnMessage: value.return_message
171
+ };
172
+ }
173
+ function parseRegexLiteral(value) {
174
+ if (!value.startsWith("/")) {
175
+ return void 0;
154
176
  }
155
- if (override.cache !== void 0) {
156
- result.cache = {
157
- ...result.cache,
158
- ...override.cache,
159
- openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
160
- anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
161
- gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
162
- google: mergeRecordBlock(result.cache?.google, override.cache.google)
177
+ for (let index = value.length - 1; index > 0; index -= 1) {
178
+ if (value[index] !== "/") {
179
+ continue;
180
+ }
181
+ let backslashCount = 0;
182
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
183
+ backslashCount += 1;
184
+ }
185
+ if (backslashCount % 2 === 1) {
186
+ continue;
187
+ }
188
+ return {
189
+ pattern: value.slice(1, index),
190
+ flags: value.slice(index + 1)
163
191
  };
164
192
  }
165
- if (override.raw !== void 0) {
166
- result.raw = {
167
- ...result.raw,
168
- ...override.raw,
169
- openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
170
- "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
171
- openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
172
- anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
173
- gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
174
- google: mergeRecordBlock(result.raw?.google, override.raw.google),
175
- openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter),
176
- llmasaservice: mergeRecordBlock(result.raw?.llmasaservice, override.raw.llmasaservice)
177
- };
193
+ return void 0;
194
+ }
195
+ function formatInvalidContextRegexMessage(details) {
196
+ return [
197
+ `Invalid context regex for prompt "${details.promptId}"`,
198
+ `variable "${details.variable}"`,
199
+ `field "${details.field}"`,
200
+ `value ${JSON.stringify(details.raw)}: ${details.reason}`
201
+ ].join(", ");
202
+ }
203
+ function compileContextRegex(regex, details) {
204
+ if (regex.invalidLiteral) {
205
+ throw new Error(
206
+ `POK013: ${formatInvalidContextRegexMessage({
207
+ ...details,
208
+ raw: regex.raw,
209
+ reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
210
+ })}`
211
+ );
178
212
  }
179
- if (override.provider_options !== void 0) {
180
- result.provider_options = {
181
- ...result.provider_options,
182
- ...override.provider_options,
183
- anthropic: {
184
- ...result.provider_options?.anthropic,
185
- ...override.provider_options.anthropic
186
- },
187
- gemini: {
188
- ...result.provider_options?.gemini,
189
- ...override.provider_options.gemini
190
- },
191
- openrouter: {
192
- ...result.provider_options?.openrouter,
193
- ...override.provider_options.openrouter
194
- },
195
- llmasaservice: {
196
- ...result.provider_options?.llmasaservice,
197
- ...override.provider_options.llmasaservice
198
- }
199
- };
213
+ try {
214
+ return new RegExp(regex.pattern, regex.flags);
215
+ } catch (error) {
216
+ const reason = error instanceof Error ? error.message : String(error);
217
+ throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
200
218
  }
201
- return result;
202
219
  }
203
- function mergeRecordBlock(base, override) {
204
- if (override === void 0) return base;
205
- return { ...base, ...override };
220
+ function getRejectSecretsRegex() {
221
+ return {
222
+ pattern: REJECT_SECRETS_PATTERN,
223
+ flags: REJECT_SECRETS_FLAGS,
224
+ raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
225
+ };
206
226
  }
207
-
208
- // src/providers/resolve-asset.ts
209
- function resolveAssetForProvider(asset, runtime = {}) {
210
- if (!runtime.environment && !runtime.tier && !runtime.runtime) {
211
- return asset;
227
+ function isTrimEnabled(mode) {
228
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
229
+ }
230
+ function normalizeTrimMode(mode) {
231
+ if (mode === "start") {
232
+ return "start";
212
233
  }
213
- return applyOverrides(asset, {
214
- environment: runtime.environment,
215
- tier: runtime.tier,
216
- runtime: runtime.runtime
217
- });
234
+ return "end";
218
235
  }
219
-
220
- // src/prompt-resolution.ts
221
- var import_promises3 = require("fs/promises");
222
- var import_node_fs2 = require("fs");
223
- var import_node_path3 = require("path");
224
-
225
- // src/cache.ts
226
- var import_node_fs = require("fs");
227
- var PromptCache = class {
228
- cache = /* @__PURE__ */ new Map();
229
- maxSize;
230
- constructor(maxSize = 100) {
231
- this.maxSize = maxSize;
236
+ function trimToMaxSize(value, maxSize, mode) {
237
+ const measured = measureContextValueSize(value);
238
+ if (measured <= maxSize) {
239
+ return value;
232
240
  }
233
- get(filePath) {
234
- const entry = this.cache.get(filePath);
235
- if (!entry) return void 0;
236
- try {
237
- const stat = (0, import_node_fs.statSync)(filePath);
238
- if (stat.mtimeMs !== entry.mtime) {
239
- this.cache.delete(filePath);
240
- return void 0;
241
+ const characters = Array.from(value);
242
+ const normalizedMode = normalizeTrimMode(mode);
243
+ if (normalizedMode === "start") {
244
+ let collected2 = "";
245
+ let size2 = 0;
246
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
247
+ const next = characters[i];
248
+ const charSize = measureContextValueSize(next);
249
+ if (size2 + charSize > maxSize) {
250
+ break;
241
251
  }
242
- } catch {
243
- this.cache.delete(filePath);
244
- return void 0;
252
+ collected2 = next + collected2;
253
+ size2 += charSize;
245
254
  }
246
- this.cache.delete(filePath);
247
- this.cache.set(filePath, entry);
248
- return entry.value;
255
+ return collected2;
249
256
  }
250
- set(filePath, value) {
251
- try {
252
- const stat = (0, import_node_fs.statSync)(filePath);
253
- if (this.cache.has(filePath)) {
254
- this.cache.delete(filePath);
255
- } else if (this.cache.size >= this.maxSize) {
256
- const oldest = this.cache.keys().next().value;
257
- if (oldest) this.cache.delete(oldest);
258
- }
257
+ let collected = "";
258
+ let size = 0;
259
+ for (const char of characters) {
260
+ const charSize = measureContextValueSize(char);
261
+ if (size + charSize > maxSize) {
262
+ break;
263
+ }
264
+ collected += char;
265
+ size += charSize;
266
+ }
267
+ return collected;
268
+ }
269
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
270
+ const { onContextOverflow } = options;
271
+ const sanitized = { ...variables };
272
+ for (const input of getContextInputs(asset)) {
273
+ const value = sanitized[input.name];
274
+ if (value === void 0) {
275
+ continue;
276
+ }
277
+ let candidate = value;
278
+ if (input.max_size !== void 0) {
279
+ const actualSize = measureContextValueSize(candidate);
280
+ if (actualSize > input.max_size && onContextOverflow) {
281
+ candidate = onContextOverflow({
282
+ promptId: asset.id,
283
+ variable: input.name,
284
+ value: candidate,
285
+ maxSize: input.max_size,
286
+ actualSize
287
+ });
288
+ }
289
+ }
290
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
291
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
292
+ }
293
+ sanitized[input.name] = candidate;
294
+ if (input.allow_regex) {
295
+ const candidate2 = sanitized[input.name];
296
+ const matcher = compileContextRegex(input.allow_regex, {
297
+ promptId: asset.id,
298
+ variable: input.name,
299
+ field: "allow_regex"
300
+ });
301
+ if (!matcher.test(candidate2)) {
302
+ if (input.allow_regex.returnMessage) {
303
+ return {
304
+ variables: sanitized,
305
+ shortCircuit: {
306
+ returnMessage: input.allow_regex.returnMessage,
307
+ code: "POK031",
308
+ variable: input.name,
309
+ field: "allow_regex"
310
+ }
311
+ };
312
+ }
313
+ throw new Error(
314
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
315
+ );
316
+ }
317
+ }
318
+ if (input.deny_regex) {
319
+ const candidate2 = sanitized[input.name];
320
+ const matcher = compileContextRegex(input.deny_regex, {
321
+ promptId: asset.id,
322
+ variable: input.name,
323
+ field: "deny_regex"
324
+ });
325
+ if (matcher.test(candidate2)) {
326
+ if (input.deny_regex.returnMessage) {
327
+ return {
328
+ variables: sanitized,
329
+ shortCircuit: {
330
+ returnMessage: input.deny_regex.returnMessage,
331
+ code: "POK032",
332
+ variable: input.name,
333
+ field: "deny_regex"
334
+ }
335
+ };
336
+ }
337
+ throw new Error(
338
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
339
+ );
340
+ }
341
+ }
342
+ if (input.non_empty && candidate.trim().length === 0) {
343
+ if (input.non_empty.returnMessage) {
344
+ return {
345
+ variables: sanitized,
346
+ shortCircuit: {
347
+ returnMessage: input.non_empty.returnMessage,
348
+ code: "POK033",
349
+ variable: input.name,
350
+ field: "non_empty"
351
+ }
352
+ };
353
+ }
354
+ throw new Error(
355
+ `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
356
+ );
357
+ }
358
+ if (input.reject_secrets) {
359
+ const matcher = compileContextRegex(getRejectSecretsRegex(), {
360
+ promptId: asset.id,
361
+ variable: input.name,
362
+ field: "reject_secrets"
363
+ });
364
+ if (matcher.test(candidate)) {
365
+ if (input.reject_secrets.returnMessage) {
366
+ return {
367
+ variables: sanitized,
368
+ shortCircuit: {
369
+ returnMessage: input.reject_secrets.returnMessage,
370
+ code: "POK034",
371
+ variable: input.name,
372
+ field: "reject_secrets"
373
+ }
374
+ };
375
+ }
376
+ throw new Error(
377
+ `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
378
+ );
379
+ }
380
+ }
381
+ }
382
+ return { variables: sanitized };
383
+ }
384
+ function measureContextValueSize(value) {
385
+ return textEncoder.encode(value).length;
386
+ }
387
+ function collectContextSizeWarnings(asset, variables = {}) {
388
+ const warnings = [];
389
+ for (const input of getContextInputs(asset)) {
390
+ if (!areContextInputWarningsEnabled(input)) {
391
+ continue;
392
+ }
393
+ if (input.max_size === void 0) {
394
+ continue;
395
+ }
396
+ const value = variables[input.name];
397
+ if (value === void 0) {
398
+ continue;
399
+ }
400
+ const actualSize = measureContextValueSize(value);
401
+ if (actualSize > input.max_size) {
402
+ warnings.push({
403
+ variable: input.name,
404
+ maxSize: input.max_size,
405
+ actualSize
406
+ });
407
+ }
408
+ }
409
+ return warnings;
410
+ }
411
+
412
+ // src/renderer/renderer.ts
413
+ function renderSections(asset, options = {}) {
414
+ const { variables = {}, strict = false } = options;
415
+ const optionalVariables = getContextInputs(asset).filter((input) => input.optional === true).map((input) => input.name);
416
+ const result = {};
417
+ if (asset.sections.system_instructions) {
418
+ result.system_instructions = interpolate(
419
+ asset.sections.system_instructions,
420
+ variables,
421
+ { strict, optionalVariables }
422
+ );
423
+ }
424
+ if (asset.sections.prompt_template) {
425
+ result.prompt_template = interpolate(
426
+ asset.sections.prompt_template,
427
+ variables,
428
+ { strict, optionalVariables }
429
+ );
430
+ }
431
+ return result;
432
+ }
433
+
434
+ // src/overrides/apply-overrides.ts
435
+ function applyOverrides(asset, options = {}) {
436
+ let result = { ...asset };
437
+ if (options.environment && result.environments?.[options.environment]) {
438
+ result = mergeOverride(result, result.environments[options.environment]);
439
+ }
440
+ if (options.tier && result.tiers?.[options.tier]) {
441
+ result = mergeOverride(result, result.tiers[options.tier]);
442
+ }
443
+ if (options.runtime) {
444
+ result = mergeOverride(result, options.runtime);
445
+ }
446
+ return result;
447
+ }
448
+ function mergeOverride(base, override) {
449
+ const result = { ...base };
450
+ if (override.model !== void 0) result.model = override.model;
451
+ if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
452
+ if (override.tools !== void 0) result.tools = override.tools;
453
+ if (override.reasoning !== void 0) {
454
+ result.reasoning = { ...result.reasoning, ...override.reasoning };
455
+ }
456
+ if (override.sampling !== void 0) {
457
+ result.sampling = { ...result.sampling, ...override.sampling };
458
+ }
459
+ if (override.response !== void 0) {
460
+ result.response = { ...result.response, ...override.response };
461
+ }
462
+ if (override.compression !== void 0) {
463
+ result.compression = {
464
+ ...result.compression,
465
+ ...override.compression,
466
+ thetokencompany: mergeRecordBlock(
467
+ result.compression?.thetokencompany,
468
+ override.compression.thetokencompany
469
+ )
470
+ };
471
+ }
472
+ if (override.cache !== void 0) {
473
+ result.cache = {
474
+ ...result.cache,
475
+ ...override.cache,
476
+ openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
477
+ anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
478
+ gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
479
+ google: mergeRecordBlock(result.cache?.google, override.cache.google)
480
+ };
481
+ }
482
+ if (override.raw !== void 0) {
483
+ result.raw = {
484
+ ...result.raw,
485
+ ...override.raw,
486
+ openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
487
+ "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
488
+ openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
489
+ anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
490
+ gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
491
+ google: mergeRecordBlock(result.raw?.google, override.raw.google),
492
+ openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter),
493
+ llmasaservice: mergeRecordBlock(result.raw?.llmasaservice, override.raw.llmasaservice)
494
+ };
495
+ }
496
+ if (override.provider_options !== void 0) {
497
+ result.provider_options = {
498
+ ...result.provider_options,
499
+ ...override.provider_options,
500
+ anthropic: {
501
+ ...result.provider_options?.anthropic,
502
+ ...override.provider_options.anthropic
503
+ },
504
+ gemini: {
505
+ ...result.provider_options?.gemini,
506
+ ...override.provider_options.gemini
507
+ },
508
+ openrouter: {
509
+ ...result.provider_options?.openrouter,
510
+ ...override.provider_options.openrouter
511
+ },
512
+ llmasaservice: {
513
+ ...result.provider_options?.llmasaservice,
514
+ ...override.provider_options.llmasaservice
515
+ }
516
+ };
517
+ }
518
+ return result;
519
+ }
520
+ function mergeRecordBlock(base, override) {
521
+ if (override === void 0) return base;
522
+ return { ...base, ...override };
523
+ }
524
+
525
+ // src/providers/resolve-asset.ts
526
+ function resolveAssetForProvider(asset, runtime = {}) {
527
+ if (!runtime.environment && !runtime.tier && !runtime.runtime) {
528
+ return asset;
529
+ }
530
+ return applyOverrides(asset, {
531
+ environment: runtime.environment,
532
+ tier: runtime.tier,
533
+ runtime: runtime.runtime
534
+ });
535
+ }
536
+
537
+ // src/prompt-resolution.ts
538
+ var import_promises4 = require("fs/promises");
539
+ var import_node_fs2 = require("fs");
540
+ var import_node_path4 = require("path");
541
+
542
+ // src/cache.ts
543
+ var import_node_fs = require("fs");
544
+ var PromptCache = class {
545
+ cache = /* @__PURE__ */ new Map();
546
+ maxSize;
547
+ constructor(maxSize = 100) {
548
+ this.maxSize = maxSize;
549
+ }
550
+ get(filePath) {
551
+ const entry = this.cache.get(filePath);
552
+ if (!entry) return void 0;
553
+ try {
554
+ const stat = (0, import_node_fs.statSync)(filePath);
555
+ if (stat.mtimeMs !== entry.mtime) {
556
+ this.cache.delete(filePath);
557
+ return void 0;
558
+ }
559
+ } catch {
560
+ this.cache.delete(filePath);
561
+ return void 0;
562
+ }
563
+ this.cache.delete(filePath);
564
+ this.cache.set(filePath, entry);
565
+ return entry.value;
566
+ }
567
+ set(filePath, value) {
568
+ try {
569
+ const stat = (0, import_node_fs.statSync)(filePath);
570
+ if (this.cache.has(filePath)) {
571
+ this.cache.delete(filePath);
572
+ } else if (this.cache.size >= this.maxSize) {
573
+ const oldest = this.cache.keys().next().value;
574
+ if (oldest) this.cache.delete(oldest);
575
+ }
259
576
  this.cache.set(filePath, { value, mtime: stat.mtimeMs });
260
577
  } catch {
261
578
  }
@@ -269,8 +586,8 @@ var PromptCache = class {
269
586
  };
270
587
 
271
588
  // src/composition/resolve-includes.ts
272
- var import_promises2 = require("fs/promises");
273
- var import_node_path2 = require("path");
589
+ var import_promises3 = require("fs/promises");
590
+ var import_node_path3 = require("path");
274
591
 
275
592
  // src/parser/parser.ts
276
593
  var import_gray_matter = __toESM(require("gray-matter"), 1);
@@ -306,10 +623,26 @@ var ResponseSchema = import_zod.z.object({
306
623
  format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
307
624
  stream: import_zod.z.boolean().optional(),
308
625
  schema: import_zod.z.record(import_zod.z.unknown()).optional(),
626
+ schema_ref: import_zod.z.string().min(1).optional(),
627
+ schema_source: import_zod.z.object({
628
+ mode: import_zod.z.enum(["inline", "schema_ref_json", "schema_ref_zod_module"]).optional(),
629
+ ref: import_zod.z.string().optional(),
630
+ resolved_path: import_zod.z.string().optional(),
631
+ hash: import_zod.z.string().optional()
632
+ }).optional(),
309
633
  schema_name: import_zod.z.string().optional(),
310
634
  schema_description: import_zod.z.string().optional(),
311
635
  schema_strict: import_zod.z.boolean().optional()
312
636
  });
637
+ var ResponseSchemaWithValidation = ResponseSchema.superRefine((value, ctx) => {
638
+ if (value.schema !== void 0 && value.schema_ref !== void 0) {
639
+ ctx.addIssue({
640
+ code: import_zod.z.ZodIssueCode.custom,
641
+ message: "response.schema and response.schema_ref are mutually exclusive",
642
+ path: ["schema_ref"]
643
+ });
644
+ }
645
+ });
313
646
  var AnthropicProviderOptionsSchema = import_zod.z.object({
314
647
  top_k: import_zod.z.number().int().min(0).optional(),
315
648
  tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
@@ -361,6 +694,14 @@ var RawProviderBodySchema = import_zod.z.object({
361
694
  openrouter: import_zod.z.record(import_zod.z.unknown()).optional(),
362
695
  llmasaservice: import_zod.z.record(import_zod.z.unknown()).optional()
363
696
  });
697
+ var TheTokenCompanyCompressionSchema = import_zod.z.object({
698
+ enabled: import_zod.z.boolean().optional(),
699
+ model: import_zod.z.string().min(1).optional(),
700
+ aggressiveness: import_zod.z.number().min(0).max(1).optional()
701
+ });
702
+ var CompressionSchema = import_zod.z.object({
703
+ thetokencompany: TheTokenCompanyCompressionSchema.optional()
704
+ });
364
705
  var OpenAICacheSchema = import_zod.z.object({
365
706
  prompt_cache_key: import_zod.z.string().min(1).optional(),
366
707
  retention: import_zod.z.enum(["in_memory", "24h"]).optional()
@@ -401,6 +742,8 @@ var ContextBuiltInValidatorSchema = import_zod.z.union([
401
742
  ]);
402
743
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
403
744
  name: import_zod.z.string(),
745
+ optional: import_zod.z.boolean().optional(),
746
+ warnings: import_zod.z.boolean().optional(),
404
747
  max_size: import_zod.z.number().int().positive().optional(),
405
748
  trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
406
749
  allow_regex: ContextRegexSchema.optional(),
@@ -430,7 +773,8 @@ var PromptAssetOverridesSchema = import_zod.z.object({
430
773
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
431
774
  reasoning: ReasoningSchema.optional(),
432
775
  sampling: SamplingSchema.optional(),
433
- response: ResponseSchema.optional(),
776
+ response: ResponseSchemaWithValidation.optional(),
777
+ compression: CompressionSchema.optional(),
434
778
  cache: CacheSchema.optional(),
435
779
  raw: RawProviderBodySchema.optional(),
436
780
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -448,7 +792,20 @@ var SectionsSchema = import_zod.z.object({
448
792
  var PromptDefaultsSchema = import_zod.z.object({
449
793
  provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
450
794
  model: import_zod.z.string().optional(),
795
+ fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
796
+ reasoning: ReasoningSchema.optional(),
797
+ sampling: SamplingSchema.optional(),
798
+ response: ResponseSchema.optional(),
799
+ compression: CompressionSchema.optional(),
451
800
  cache: CacheSchema.optional(),
801
+ raw: RawProviderBodySchema.optional(),
802
+ tools: import_zod.z.array(ToolRefSchema).optional(),
803
+ provider_options: ProviderOptionsSchema.optional(),
804
+ mcp: MCPSchema.optional(),
805
+ context: ContextSchema.optional(),
806
+ includes: import_zod.z.array(import_zod.z.string()).optional(),
807
+ environments: import_zod.z.record(PromptAssetOverridesSchema).optional(),
808
+ tiers: import_zod.z.record(PromptAssetOverridesSchema).optional(),
452
809
  metadata: MetadataSchema.optional(),
453
810
  sections: import_zod.z.object({
454
811
  system_instructions: import_zod.z.string().optional()
@@ -463,7 +820,8 @@ var PromptAssetSchema = import_zod.z.object({
463
820
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
464
821
  reasoning: ReasoningSchema.optional(),
465
822
  sampling: SamplingSchema.optional(),
466
- response: ResponseSchema.optional(),
823
+ response: ResponseSchemaWithValidation.optional(),
824
+ compression: CompressionSchema.optional(),
467
825
  cache: CacheSchema.optional(),
468
826
  raw: RawProviderBodySchema.optional(),
469
827
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -517,690 +875,640 @@ function assertRegexFrontMatterQuoting(content, filePath) {
517
875
  if (!patternMatch?.groups) {
518
876
  continue;
519
877
  }
520
- assertNoUnescapedBackslashInDoubleQuotedRegex(
521
- patternMatch.groups.value.trim(),
522
- `${pendingRegex.field}.pattern`,
523
- filePath,
524
- lineNumber
525
- );
526
- }
527
- }
528
- function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
529
- const match = value.match(INLINE_PATTERN_FIELD);
530
- const pattern = match?.groups?.value;
531
- if (!pattern) {
532
- return;
533
- }
534
- assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
535
- }
536
- function extractFrontMatter(content) {
537
- const lines = content.split(/\r?\n/);
538
- if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
539
- return void 0;
540
- }
541
- for (let index = 1; index < lines.length; index += 1) {
542
- if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
543
- return {
544
- lines: lines.slice(1, index),
545
- startLine: 2
546
- };
547
- }
548
- }
549
- return void 0;
550
- }
551
- function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
552
- if (!value.startsWith('"')) {
553
- return;
554
- }
555
- const quoted = readDoubleQuotedScalar(value);
556
- if (!quoted) {
557
- return;
558
- }
559
- if (!hasUnescapedBackslash(quoted)) {
560
- return;
561
- }
562
- const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
563
- throw new Error(
564
- `POK013: Invalid context regex YAML at ${location}, field "${field}": double-quoted regex strings treat backslashes as YAML escapes. Use unquoted /pattern/i literal form, single quotes, or double each backslash.`
565
- );
566
- }
567
- function readDoubleQuotedScalar(value) {
568
- let result = "";
569
- for (let index = 1; index < value.length; index += 1) {
570
- const char = value[index];
571
- if (char === '"') {
572
- return result;
573
- }
574
- result += char;
575
- if (char === "\\" && index + 1 < value.length) {
576
- index += 1;
577
- result += value[index];
578
- }
579
- }
580
- return void 0;
581
- }
582
- function hasUnescapedBackslash(value) {
583
- for (let index = 0; index < value.length; index += 1) {
584
- if (value[index] !== "\\") {
585
- continue;
586
- }
587
- let count = 1;
588
- while (value[index + count] === "\\") {
589
- count += 1;
590
- }
591
- if (count % 2 === 1) {
592
- return true;
593
- }
594
- index += count - 1;
595
- }
596
- return false;
597
- }
598
-
599
- // src/parser/sections.ts
600
- var SECTION_MAP = {
601
- "system instructions": "system_instructions",
602
- "prompt template": "prompt_template",
603
- "notes": "notes"
604
- };
605
- function extractSections(body) {
606
- const lines = body.split(/\r?\n/);
607
- const sections = {};
608
- let currentKey = null;
609
- let currentLines = [];
610
- let foundAnyH1 = false;
611
- for (const line of lines) {
612
- const h1Match = line.match(/^#\s+(.+)$/);
613
- if (h1Match) {
614
- if (currentKey) {
615
- sections[currentKey] = currentLines.join("\n").trim();
616
- }
617
- foundAnyH1 = true;
618
- const heading = h1Match[1].trim().toLowerCase();
619
- currentKey = SECTION_MAP[heading] ?? null;
620
- currentLines = [];
621
- } else {
622
- currentLines.push(line);
623
- }
624
- }
625
- if (currentKey) {
626
- sections[currentKey] = currentLines.join("\n").trim();
627
- }
628
- if (!foundAnyH1) {
629
- const trimmed = body.trim();
630
- if (trimmed) {
631
- sections.prompt_template = trimmed;
632
- }
633
- }
634
- return sections;
635
- }
636
-
637
- // src/parser/parser.ts
638
- function parsePrompt(content, filePath) {
639
- assertRegexFrontMatterQuoting(content, filePath);
640
- const { data: frontMatter, content: body } = (0, import_gray_matter.default)(content);
641
- const sections = extractSections(body);
642
- const raw = {
643
- ...frontMatter,
644
- sections,
645
- source: filePath ? { file_path: filePath } : void 0
646
- };
647
- const asset = PromptAssetSchema.parse(raw);
648
- return {
649
- asset,
650
- raw: {
651
- frontMatter,
652
- body
653
- }
654
- };
655
- }
656
-
657
- // src/parser/loader.ts
658
- var import_promises = require("fs/promises");
659
- var import_node_path = require("path");
660
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
661
- var DEFAULTS_FILE_NAME = "defaults.md";
662
- async function loadPromptFile(filePath, options = {}) {
663
- const content = await (0, import_promises.readFile)(filePath, "utf-8");
664
- const parsed = parsePrompt(content, filePath);
665
- const root = options.defaultsRoot ?? (0, import_node_path.dirname)(filePath);
666
- const defaults = await loadDefaultsForPath(filePath, root);
667
- const asset = applyDefaults(parsed.asset, defaults);
668
- return {
669
- ...parsed,
670
- asset
671
- };
672
- }
673
- async function loadDefaultsForPath(filePath, defaultsRoot) {
674
- const directories = getDirectoriesToCheck(filePath, defaultsRoot);
675
- let merged = {};
676
- for (const dir of directories) {
677
- const defaultsPath = (0, import_node_path.join)(dir, DEFAULTS_FILE_NAME);
678
- try {
679
- const defaultsContent = await (0, import_promises.readFile)(defaultsPath, "utf-8");
680
- const defaults = parseDefaults(defaultsContent);
681
- merged = mergeDefaults(merged, defaults);
682
- } catch (error) {
683
- if (error.code !== "ENOENT") {
684
- throw error;
685
- }
686
- }
687
- }
688
- return merged;
689
- }
690
- function getDirectoriesToCheck(filePath, defaultsRoot) {
691
- const dirs = [];
692
- let current = (0, import_node_path.resolve)((0, import_node_path.dirname)(filePath));
693
- const boundary = defaultsRoot ? (0, import_node_path.resolve)(defaultsRoot) : void 0;
694
- while (true) {
695
- dirs.unshift(current);
696
- if (boundary && current === boundary || current === (0, import_node_path.dirname)(current)) {
697
- break;
698
- }
699
- current = (0, import_node_path.dirname)(current);
700
- }
701
- return dirs;
702
- }
703
- function parseDefaults(content) {
704
- const { data: frontMatter, content: body } = (0, import_gray_matter2.default)(content);
705
- const sections = extractSections(body);
706
- return PromptDefaultsSchema.parse({
707
- ...frontMatter,
708
- sections: {
709
- system_instructions: sections.system_instructions
710
- }
711
- });
712
- }
713
- function mergeDefaults(base, local) {
714
- return {
715
- provider: local.provider ?? base.provider,
716
- model: local.model ?? base.model,
717
- cache: mergeCache(base.cache, local.cache),
718
- metadata: {
719
- ...base.metadata ?? {},
720
- ...local.metadata ?? {}
721
- },
722
- sections: {
723
- ...base.sections ?? {},
724
- ...local.sections ?? {}
725
- }
726
- };
727
- }
728
- function applyDefaults(asset, defaults) {
729
- const cache = mergeCache(defaults.cache, asset.cache);
730
- const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
731
- const hasDefaultSystem = !!defaults.sections?.system_instructions;
732
- const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
733
- const hasDefaultCache = cache !== void 0;
734
- if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
735
- return asset;
736
- }
737
- const mergedMetadata = {
738
- ...defaults.metadata ?? {},
739
- ...asset.metadata ?? {}
740
- };
741
- const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
742
- const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
743
- const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
744
- return {
745
- ...asset,
746
- provider: asset.provider ?? defaults.provider,
747
- model: asset.model ?? defaults.model,
748
- cache,
749
- metadata,
750
- sections
751
- };
752
- }
753
- function mergeCache(base, local) {
754
- const merged = {
755
- ...base ?? {},
756
- ...local ?? {},
757
- openai: {
758
- ...base?.openai ?? {},
759
- ...local?.openai ?? {}
760
- },
761
- anthropic: {
762
- ...base?.anthropic ?? {},
763
- ...local?.anthropic ?? {}
764
- },
765
- gemini: {
766
- ...base?.gemini ?? {},
767
- ...local?.gemini ?? {}
768
- },
769
- google: {
770
- ...base?.google ?? {},
771
- ...local?.google ?? {}
772
- }
773
- };
774
- if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
775
- if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
776
- if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
777
- if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
778
- return Object.keys(merged).length > 0 ? merged : void 0;
878
+ assertNoUnescapedBackslashInDoubleQuotedRegex(
879
+ patternMatch.groups.value.trim(),
880
+ `${pendingRegex.field}.pattern`,
881
+ filePath,
882
+ lineNumber
883
+ );
884
+ }
779
885
  }
780
-
781
- // src/composition/resolve-includes.ts
782
- async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
783
- if (!asset.includes || asset.includes.length === 0) {
784
- return asset;
886
+ function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
887
+ const match = value.match(INLINE_PATTERN_FIELD);
888
+ const pattern = match?.groups?.value;
889
+ if (!pattern) {
890
+ return;
785
891
  }
786
- const baseDir = (0, import_node_path2.dirname)(basePath);
787
- const resolvedPath = (0, import_node_path2.resolve)(basePath);
788
- if (visited.has(resolvedPath)) {
789
- throw new Error(`Circular include detected: ${resolvedPath}`);
892
+ assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
893
+ }
894
+ function extractFrontMatter(content) {
895
+ const lines = content.split(/\r?\n/);
896
+ if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
897
+ return void 0;
790
898
  }
791
- visited.add(resolvedPath);
792
- let mergedSystemInstructions = "";
793
- for (const includePath of asset.includes) {
794
- const fullPath = (0, import_node_path2.resolve)(baseDir, includePath);
795
- if (visited.has(fullPath)) {
796
- throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
797
- }
798
- const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
799
- const { asset: includedAsset } = parsePrompt(content, fullPath);
800
- const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
801
- if (resolved.sections?.system_instructions) {
802
- mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
899
+ for (let index = 1; index < lines.length; index += 1) {
900
+ if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
901
+ return {
902
+ lines: lines.slice(1, index),
903
+ startLine: 2
904
+ };
803
905
  }
804
906
  }
805
- const localSystem = asset.sections?.system_instructions ?? "";
806
- const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
807
- return {
808
- ...asset,
809
- sections: {
810
- ...asset.sections,
811
- system_instructions: combinedSystem
812
- },
813
- // Drop includes from the resolved asset — they've been inlined
814
- includes: void 0
815
- };
907
+ return void 0;
816
908
  }
817
-
818
- // src/prompt-resolution.ts
819
- var DEFAULT_PROMPTS_DIR = "./prompts";
820
- var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
821
- function withPromptResolutionDefaults(config) {
822
- return {
823
- ...config,
824
- sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
825
- compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
826
- };
909
+ function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
910
+ if (!value.startsWith('"')) {
911
+ return;
912
+ }
913
+ const quoted = readDoubleQuotedScalar(value);
914
+ if (!quoted) {
915
+ return;
916
+ }
917
+ if (!hasUnescapedBackslash(quoted)) {
918
+ return;
919
+ }
920
+ const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
921
+ throw new Error(
922
+ `POK013: Invalid context regex YAML at ${location}, field "${field}": double-quoted regex strings treat backslashes as YAML escapes. Use unquoted /pattern/i literal form, single quotes, or double each backslash.`
923
+ );
827
924
  }
828
- var sharedPromptCache = new PromptCache();
829
- async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
830
- const resolvedConfig = withPromptResolutionDefaults(config);
831
- const mode = resolvedConfig.mode ?? "auto";
832
- if (mode !== "source-only" && resolvedConfig.compiledDir) {
833
- const compiledFile = (0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
834
- if ((0, import_node_fs2.existsSync)(compiledFile)) {
835
- if (mode === "auto") {
836
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
837
- if ((0, import_node_fs2.existsSync)(sourceFile)) {
838
- const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
839
- const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
840
- if (sourceMtime > compiledMtime) {
841
- console.warn(
842
- `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
843
- Run "promptopskit compile" or switch to source-only mode.`
844
- );
845
- }
846
- }
847
- }
848
- const content = await (0, import_promises3.readFile)(compiledFile, "utf-8");
849
- return JSON.parse(content);
925
+ function readDoubleQuotedScalar(value) {
926
+ let result = "";
927
+ for (let index = 1; index < value.length; index += 1) {
928
+ const char = value[index];
929
+ if (char === '"') {
930
+ return result;
850
931
  }
851
- if (mode === "compiled-only") {
852
- throw new Error(
853
- `Compiled artifact not found: ${compiledFile}
854
- Run "promptopskit compile" to generate it.`
855
- );
932
+ result += char;
933
+ if (char === "\\" && index + 1 < value.length) {
934
+ index += 1;
935
+ result += value[index];
856
936
  }
857
937
  }
858
- if (mode !== "compiled-only") {
859
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
860
- if (resolvedConfig.cache !== false) {
861
- const cached = promptCache.get(sourceFile);
862
- if (cached) {
863
- return cached;
864
- }
938
+ return void 0;
939
+ }
940
+ function hasUnescapedBackslash(value) {
941
+ for (let index = 0; index < value.length; index += 1) {
942
+ if (value[index] !== "\\") {
943
+ continue;
865
944
  }
866
- if (!(0, import_node_fs2.existsSync)(sourceFile)) {
867
- const paths = [sourceFile];
868
- if (resolvedConfig.compiledDir) {
869
- paths.unshift((0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
870
- }
871
- throw new Error(
872
- `Prompt not found: "${promptPath}"
873
- Searched:
874
- ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
875
- );
945
+ let count = 1;
946
+ while (value[index + count] === "\\") {
947
+ count += 1;
876
948
  }
877
- const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
878
- if (resolvedConfig.cache !== false) {
879
- promptCache.set(sourceFile, asset);
949
+ if (count % 2 === 1) {
950
+ return true;
880
951
  }
881
- return asset;
952
+ index += count - 1;
882
953
  }
883
- throw new Error(`Prompt not found: "${promptPath}"`);
954
+ return false;
884
955
  }
885
- async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
886
- const resolvedConfig = withPromptResolutionDefaults(config);
887
- let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
888
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
889
- if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
890
- asset = await resolveIncludes(asset, sourceFile);
956
+
957
+ // src/parser/sections.ts
958
+ var SECTION_MAP = {
959
+ "system instructions": "system_instructions",
960
+ "prompt template": "prompt_template",
961
+ "notes": "notes"
962
+ };
963
+ function extractSections(body) {
964
+ const lines = body.split(/\r?\n/);
965
+ const sections = {};
966
+ let currentKey = null;
967
+ let currentLines = [];
968
+ let foundAnyH1 = false;
969
+ for (const line of lines) {
970
+ const h1Match = line.match(/^#\s+(.+)$/);
971
+ if (h1Match) {
972
+ if (currentKey) {
973
+ sections[currentKey] = currentLines.join("\n").trim();
974
+ }
975
+ foundAnyH1 = true;
976
+ const heading = h1Match[1].trim().toLowerCase();
977
+ currentKey = SECTION_MAP[heading] ?? null;
978
+ currentLines = [];
979
+ } else {
980
+ currentLines.push(line);
981
+ }
891
982
  }
892
- asset = applyOverrides(asset, {
893
- environment: options.environment,
894
- tier: options.tier,
895
- runtime: options.runtime
896
- });
897
- return asset;
898
- }
899
- function resolveInlinePromptSource(source, options = {}) {
900
- const { asset } = parsePrompt(source);
901
- return applyOverrides(asset, {
902
- environment: options.environment,
903
- tier: options.tier,
904
- runtime: options.runtime
905
- });
983
+ if (currentKey) {
984
+ sections[currentKey] = currentLines.join("\n").trim();
985
+ }
986
+ if (!foundAnyH1) {
987
+ const trimmed = body.trim();
988
+ if (trimmed) {
989
+ sections.prompt_template = trimmed;
990
+ }
991
+ }
992
+ return sections;
906
993
  }
907
994
 
908
- // src/context.ts
909
- var textEncoder = new TextEncoder();
910
- var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
911
- var REJECT_SECRETS_FLAGS = "i";
912
- function getContextInputs(asset) {
913
- return (asset.context?.inputs ?? []).map(normalizeContextInput);
914
- }
915
- function getContextInputNames(asset) {
916
- return getContextInputs(asset).map((input) => input.name);
917
- }
918
- function normalizeContextInput(input) {
919
- if (typeof input === "string") {
920
- return { name: input };
921
- }
995
+ // src/parser/parser.ts
996
+ function parsePrompt(content, filePath) {
997
+ assertRegexFrontMatterQuoting(content, filePath);
998
+ const { data: frontMatter, content: body } = (0, import_gray_matter.default)(content);
999
+ const sections = extractSections(body);
1000
+ const raw = {
1001
+ ...frontMatter,
1002
+ sections,
1003
+ source: filePath ? { file_path: filePath } : void 0
1004
+ };
1005
+ const asset = PromptAssetSchema.parse(raw);
922
1006
  return {
923
- name: input.name,
924
- max_size: input.max_size,
925
- trim: input.trim,
926
- allow_regex: normalizeContextRegex(input.allow_regex),
927
- deny_regex: normalizeContextRegex(input.deny_regex),
928
- non_empty: normalizeBuiltInValidator(input.non_empty),
929
- reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
1007
+ asset,
1008
+ raw: {
1009
+ frontMatter,
1010
+ body
1011
+ }
930
1012
  };
931
1013
  }
932
- function normalizeContextRegex(value) {
933
- if (value === void 0) {
934
- return void 0;
935
- }
936
- if (typeof value === "string") {
937
- const literal = parseRegexLiteral(value);
938
- if (value.startsWith("/") && !literal) {
939
- return {
940
- pattern: value,
941
- flags: "",
942
- raw: value,
943
- invalidLiteral: true
944
- };
1014
+
1015
+ // src/parser/loader.ts
1016
+ var import_promises2 = require("fs/promises");
1017
+ var import_node_path2 = require("path");
1018
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
1019
+
1020
+ // src/parser/response-schema-ref.ts
1021
+ var import_promises = require("fs/promises");
1022
+ var import_node_crypto = require("crypto");
1023
+ var import_node_path = require("path");
1024
+ var import_node_url = require("url");
1025
+ var import_zod2 = require("zod");
1026
+ var import_zod_to_json_schema = require("zod-to-json-schema");
1027
+ var SUPPORTED_SCHEMA_REF_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
1028
+ async function resolveJsonSchemaRef(schemaRef, promptFilePath) {
1029
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
1030
+ let raw;
1031
+ try {
1032
+ raw = await (0, import_promises.readFile)(resolvedPath, "utf-8");
1033
+ } catch (error) {
1034
+ const code = error.code;
1035
+ if (code === "ENOENT") {
1036
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
945
1037
  }
946
- return {
947
- pattern: literal?.pattern ?? value,
948
- flags: literal?.flags ?? "",
949
- raw: value
950
- };
1038
+ throw error;
1039
+ }
1040
+ let parsed;
1041
+ try {
1042
+ parsed = JSON.parse(raw);
1043
+ } catch {
1044
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" is not valid JSON`);
1045
+ }
1046
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1047
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" must resolve to a JSON object schema`);
951
1048
  }
952
1049
  return {
953
- pattern: value.pattern,
954
- flags: value.flags ?? "",
955
- raw: JSON.stringify(value),
956
- returnMessage: value.return_message
1050
+ schema: parsed,
1051
+ resolvedPath,
1052
+ hash: (0, import_node_crypto.createHash)("sha256").update(raw).digest("hex")
957
1053
  };
958
1054
  }
959
- function normalizeBuiltInValidator(value) {
960
- if (value === void 0 || value === false) {
961
- return void 0;
1055
+ async function resolveZodSchemaRef(schemaRef, promptFilePath) {
1056
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
1057
+ let moduleSource;
1058
+ try {
1059
+ moduleSource = await (0, import_promises.readFile)(resolvedPath, "utf-8");
1060
+ } catch (error) {
1061
+ const code = error.code;
1062
+ if (code === "ENOENT") {
1063
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
1064
+ }
1065
+ throw error;
962
1066
  }
963
- if (value === true) {
964
- return {};
1067
+ let imported;
1068
+ try {
1069
+ imported = await import((0, import_node_url.pathToFileURL)(resolvedPath).href);
1070
+ } catch (error) {
1071
+ const message = error instanceof Error ? error.message : String(error);
1072
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" could not be imported as a module (${message})`);
1073
+ }
1074
+ const mod = imported;
1075
+ const candidate = mod.default ?? mod.schema;
1076
+ if (!(candidate instanceof import_zod2.z.ZodType)) {
1077
+ throw new Error('POK051: zod schema modules must export a Zod schema as default export or named export "schema"');
1078
+ }
1079
+ const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(candidate, {
1080
+ target: "jsonSchema7",
1081
+ $refStrategy: "none"
1082
+ });
1083
+ if (!jsonSchema || typeof jsonSchema !== "object" || Array.isArray(jsonSchema)) {
1084
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" did not produce a valid JSON schema object`);
965
1085
  }
966
1086
  return {
967
- returnMessage: value.return_message
1087
+ schema: jsonSchema,
1088
+ resolvedPath,
1089
+ hash: (0, import_node_crypto.createHash)("sha256").update(moduleSource).digest("hex")
968
1090
  };
969
1091
  }
970
- function parseRegexLiteral(value) {
971
- if (!value.startsWith("/")) {
972
- return void 0;
1092
+ async function resolveResponseSchemaRef(asset, promptFilePath) {
1093
+ const schemaRef = asset.response?.schema_ref;
1094
+ if (!schemaRef) return asset;
1095
+ const ext = (0, import_node_path.extname)(schemaRef).toLowerCase();
1096
+ if (!SUPPORTED_SCHEMA_REF_EXTENSIONS.has(ext)) {
1097
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" has unsupported extension "${ext}". Use .json, .js, .mjs, or .cjs`);
973
1098
  }
974
- for (let index = value.length - 1; index > 0; index -= 1) {
975
- if (value[index] !== "/") {
976
- continue;
1099
+ const resolved = ext === ".json" ? await resolveJsonSchemaRef(schemaRef, promptFilePath) : await resolveZodSchemaRef(schemaRef, promptFilePath);
1100
+ return {
1101
+ ...asset,
1102
+ response: {
1103
+ ...asset.response,
1104
+ schema: resolved.schema,
1105
+ schema_ref: void 0,
1106
+ schema_source: {
1107
+ mode: ext === ".json" ? "schema_ref_json" : "schema_ref_zod_module",
1108
+ ref: schemaRef,
1109
+ resolved_path: resolved.resolvedPath,
1110
+ hash: resolved.hash
1111
+ }
977
1112
  }
978
- let backslashCount = 0;
979
- for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
980
- backslashCount += 1;
1113
+ };
1114
+ }
1115
+
1116
+ // src/parser/loader.ts
1117
+ var DEFAULTS_FILE_NAME = "defaults.md";
1118
+ async function loadPromptFile(filePath, options = {}) {
1119
+ const content = await (0, import_promises2.readFile)(filePath, "utf-8");
1120
+ const parsed = parsePrompt(content, filePath);
1121
+ const root = options.defaultsRoot ?? (0, import_node_path2.dirname)(filePath);
1122
+ const defaults = await loadDefaultsForPath(filePath, root);
1123
+ const withDefaults = applyDefaults(parsed.asset, defaults);
1124
+ const resolved = await resolveResponseSchemaRef(withDefaults, filePath);
1125
+ const asset = resolved.response?.schema !== void 0 && !resolved.response?.schema_source ? {
1126
+ ...resolved,
1127
+ response: {
1128
+ ...resolved.response,
1129
+ schema_source: { mode: "inline" }
1130
+ }
1131
+ } : resolved;
1132
+ return {
1133
+ ...parsed,
1134
+ asset
1135
+ };
1136
+ }
1137
+ async function loadDefaultsForPath(filePath, defaultsRoot) {
1138
+ const directories = getDirectoriesToCheck(filePath, defaultsRoot);
1139
+ let merged = {};
1140
+ for (const dir of directories) {
1141
+ const defaultsPath = (0, import_node_path2.join)(dir, DEFAULTS_FILE_NAME);
1142
+ try {
1143
+ const defaultsContent = await (0, import_promises2.readFile)(defaultsPath, "utf-8");
1144
+ const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
1145
+ merged = mergeDefaults(merged, defaults);
1146
+ } catch (error) {
1147
+ if (error.code !== "ENOENT") {
1148
+ throw error;
1149
+ }
981
1150
  }
982
- if (backslashCount % 2 === 1) {
983
- continue;
1151
+ }
1152
+ return merged;
1153
+ }
1154
+ function getDirectoriesToCheck(filePath, defaultsRoot) {
1155
+ const dirs = [];
1156
+ let current = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(filePath));
1157
+ const boundary = defaultsRoot ? (0, import_node_path2.resolve)(defaultsRoot) : void 0;
1158
+ while (true) {
1159
+ dirs.unshift(current);
1160
+ if (boundary && current === boundary || current === (0, import_node_path2.dirname)(current)) {
1161
+ break;
984
1162
  }
985
- return {
986
- pattern: value.slice(1, index),
987
- flags: value.slice(index + 1)
988
- };
1163
+ current = (0, import_node_path2.dirname)(current);
989
1164
  }
990
- return void 0;
1165
+ return dirs;
991
1166
  }
992
- function formatInvalidContextRegexMessage(details) {
993
- return [
994
- `Invalid context regex for prompt "${details.promptId}"`,
995
- `variable "${details.variable}"`,
996
- `field "${details.field}"`,
997
- `value ${JSON.stringify(details.raw)}: ${details.reason}`
998
- ].join(", ");
1167
+ function parseDefaults(content) {
1168
+ const { data: frontMatter, content: body } = (0, import_gray_matter2.default)(content);
1169
+ const sections = extractSections(body);
1170
+ return PromptDefaultsSchema.parse({
1171
+ ...frontMatter,
1172
+ sections: {
1173
+ system_instructions: sections.system_instructions
1174
+ }
1175
+ });
999
1176
  }
1000
- function compileContextRegex(regex, details) {
1001
- if (regex.invalidLiteral) {
1002
- throw new Error(
1003
- `POK013: ${formatInvalidContextRegexMessage({
1004
- ...details,
1005
- raw: regex.raw,
1006
- reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
1007
- })}`
1008
- );
1009
- }
1010
- try {
1011
- return new RegExp(regex.pattern, regex.flags);
1012
- } catch (error) {
1013
- const reason = error instanceof Error ? error.message : String(error);
1014
- throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
1177
+ function mergeDefaults(base, local) {
1178
+ return {
1179
+ provider: local.provider ?? base.provider,
1180
+ model: local.model ?? base.model,
1181
+ fallback_models: local.fallback_models ?? base.fallback_models,
1182
+ reasoning: mergeRecordBlock2(base.reasoning, local.reasoning),
1183
+ sampling: mergeRecordBlock2(base.sampling, local.sampling),
1184
+ response: mergeRecordBlock2(base.response, local.response),
1185
+ compression: mergeCompression(base.compression, local.compression),
1186
+ cache: mergeCache(base.cache, local.cache),
1187
+ raw: mergeRaw(base.raw, local.raw),
1188
+ tools: local.tools ?? base.tools,
1189
+ provider_options: mergeProviderOptions(base.provider_options, local.provider_options),
1190
+ mcp: mergeMcp(base.mcp, local.mcp),
1191
+ context: mergeContext(base.context, local.context),
1192
+ includes: local.includes ?? base.includes,
1193
+ environments: mergeOverrideMap(base.environments, local.environments),
1194
+ tiers: mergeOverrideMap(base.tiers, local.tiers),
1195
+ metadata: {
1196
+ ...base.metadata ?? {},
1197
+ ...local.metadata ?? {}
1198
+ },
1199
+ sections: {
1200
+ ...base.sections ?? {},
1201
+ ...local.sections ?? {}
1202
+ }
1203
+ };
1204
+ }
1205
+ function applyDefaults(asset, defaults) {
1206
+ const compression = mergeCompression(defaults.compression, asset.compression);
1207
+ const cache = mergeCache(defaults.cache, asset.cache);
1208
+ const raw = mergeRaw(defaults.raw, asset.raw);
1209
+ const providerOptions = mergeProviderOptions(defaults.provider_options, asset.provider_options);
1210
+ const reasoning = mergeRecordBlock2(defaults.reasoning, asset.reasoning);
1211
+ const sampling = mergeRecordBlock2(defaults.sampling, asset.sampling);
1212
+ const response = mergeRecordBlock2(defaults.response, asset.response);
1213
+ const mcp = mergeMcp(defaults.mcp, asset.mcp);
1214
+ const context = mergeContext(defaults.context, asset.context);
1215
+ const environments = mergeOverrideMap(defaults.environments, asset.environments);
1216
+ const tiers = mergeOverrideMap(defaults.tiers, asset.tiers);
1217
+ const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
1218
+ const hasDefaultSystem = !!defaults.sections?.system_instructions;
1219
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0 || defaults.fallback_models !== void 0 || defaults.tools !== void 0 || defaults.includes !== void 0;
1220
+ const hasDefaultObjects = compression !== void 0 || cache !== void 0 || raw !== void 0 || providerOptions !== void 0 || reasoning !== void 0 || sampling !== void 0 || response !== void 0 || mcp !== void 0 || context !== void 0 || environments !== void 0 || tiers !== void 0;
1221
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultObjects) {
1222
+ return asset;
1015
1223
  }
1224
+ const mergedMetadata = {
1225
+ ...defaults.metadata ?? {},
1226
+ ...asset.metadata ?? {}
1227
+ };
1228
+ const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
1229
+ const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
1230
+ const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
1231
+ return {
1232
+ ...asset,
1233
+ provider: asset.provider ?? defaults.provider,
1234
+ model: asset.model ?? defaults.model,
1235
+ fallback_models: asset.fallback_models ?? defaults.fallback_models,
1236
+ reasoning,
1237
+ sampling,
1238
+ response,
1239
+ compression,
1240
+ cache,
1241
+ raw,
1242
+ tools: asset.tools ?? defaults.tools,
1243
+ provider_options: providerOptions,
1244
+ mcp,
1245
+ context,
1246
+ includes: asset.includes ?? defaults.includes,
1247
+ environments,
1248
+ tiers,
1249
+ metadata,
1250
+ sections
1251
+ };
1016
1252
  }
1017
- function getRejectSecretsRegex() {
1253
+ function normalizeDefaultIncludes(defaults, defaultsPath, filePath) {
1254
+ if (!defaults.includes) return defaults;
1255
+ const promptDir = (0, import_node_path2.dirname)(filePath);
1256
+ const defaultsDir = (0, import_node_path2.dirname)(defaultsPath);
1257
+ const includes = defaults.includes.map((includePath) => {
1258
+ const relativePath = (0, import_node_path2.relative)(promptDir, (0, import_node_path2.resolve)(defaultsDir, includePath)).replace(/\\/g, "/");
1259
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
1260
+ });
1018
1261
  return {
1019
- pattern: REJECT_SECRETS_PATTERN,
1020
- flags: REJECT_SECRETS_FLAGS,
1021
- raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
1262
+ ...defaults,
1263
+ includes
1022
1264
  };
1023
1265
  }
1024
- function isTrimEnabled(mode) {
1025
- return mode === true || mode === "start" || mode === "end" || mode === "both";
1266
+ function mergeRecordBlock2(base, local) {
1267
+ if (base === void 0) return local;
1268
+ if (local === void 0) return base;
1269
+ return { ...base, ...local };
1026
1270
  }
1027
- function normalizeTrimMode(mode) {
1028
- if (mode === "start") {
1029
- return "start";
1030
- }
1031
- return "end";
1271
+ function mergeMcp(base, local) {
1272
+ return mergeRecordBlock2(base, local);
1032
1273
  }
1033
- function trimToMaxSize(value, maxSize, mode) {
1034
- const measured = measureContextValueSize(value);
1035
- if (measured <= maxSize) {
1036
- return value;
1037
- }
1038
- const characters = Array.from(value);
1039
- const normalizedMode = normalizeTrimMode(mode);
1040
- if (normalizedMode === "start") {
1041
- let collected2 = "";
1042
- let size2 = 0;
1043
- for (let i = characters.length - 1; i >= 0; i -= 1) {
1044
- const next = characters[i];
1045
- const charSize = measureContextValueSize(next);
1046
- if (size2 + charSize > maxSize) {
1047
- break;
1048
- }
1049
- collected2 = next + collected2;
1050
- size2 += charSize;
1051
- }
1052
- return collected2;
1053
- }
1054
- let collected = "";
1055
- let size = 0;
1056
- for (const char of characters) {
1057
- const charSize = measureContextValueSize(char);
1058
- if (size + charSize > maxSize) {
1059
- break;
1274
+ function mergeContext(base, local) {
1275
+ const merged = {
1276
+ ...base ?? {},
1277
+ ...local ?? {},
1278
+ history: mergeRecordBlock2(base?.history, local?.history)
1279
+ };
1280
+ return Object.keys(merged).length > 0 ? merged : void 0;
1281
+ }
1282
+ function mergeCache(base, local) {
1283
+ const merged = {
1284
+ ...base ?? {},
1285
+ ...local ?? {},
1286
+ openai: {
1287
+ ...base?.openai ?? {},
1288
+ ...local?.openai ?? {}
1289
+ },
1290
+ anthropic: {
1291
+ ...base?.anthropic ?? {},
1292
+ ...local?.anthropic ?? {}
1293
+ },
1294
+ gemini: {
1295
+ ...base?.gemini ?? {},
1296
+ ...local?.gemini ?? {}
1297
+ },
1298
+ google: {
1299
+ ...base?.google ?? {},
1300
+ ...local?.google ?? {}
1060
1301
  }
1061
- collected += char;
1062
- size += charSize;
1302
+ };
1303
+ if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
1304
+ if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
1305
+ if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
1306
+ if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
1307
+ return Object.keys(merged).length > 0 ? merged : void 0;
1308
+ }
1309
+ function mergeCompression(base, local) {
1310
+ const merged = {
1311
+ ...base ?? {},
1312
+ ...local ?? {},
1313
+ thetokencompany: mergeRecordBlock2(base?.thetokencompany, local?.thetokencompany)
1314
+ };
1315
+ removeEmptyProviderBlocks(merged);
1316
+ return Object.keys(merged).length > 0 ? merged : void 0;
1317
+ }
1318
+ function mergeRaw(base, local) {
1319
+ const merged = {
1320
+ ...base ?? {},
1321
+ ...local ?? {},
1322
+ openai: mergeRecordBlock2(base?.openai, local?.openai),
1323
+ "openai-responses": mergeRecordBlock2(base?.["openai-responses"], local?.["openai-responses"]),
1324
+ openai_responses: mergeRecordBlock2(base?.openai_responses, local?.openai_responses),
1325
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1326
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1327
+ google: mergeRecordBlock2(base?.google, local?.google),
1328
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1329
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
1330
+ };
1331
+ removeEmptyProviderBlocks(merged);
1332
+ return Object.keys(merged).length > 0 ? merged : void 0;
1333
+ }
1334
+ function mergeProviderOptions(base, local) {
1335
+ const merged = {
1336
+ ...base ?? {},
1337
+ ...local ?? {},
1338
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1339
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1340
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1341
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
1342
+ };
1343
+ removeEmptyProviderBlocks(merged);
1344
+ return Object.keys(merged).length > 0 ? merged : void 0;
1345
+ }
1346
+ function mergeOverrideMap(base, local) {
1347
+ const merged = { ...base ?? {} };
1348
+ for (const [name, localOverride] of Object.entries(local ?? {})) {
1349
+ merged[name] = mergeOverrideConfig(merged[name], localOverride);
1063
1350
  }
1064
- return collected;
1351
+ return Object.keys(merged).length > 0 ? merged : void 0;
1065
1352
  }
1066
- function sanitizeContextVariables(asset, variables = {}, options = {}) {
1067
- const { onContextOverflow } = options;
1068
- const sanitized = { ...variables };
1069
- for (const input of getContextInputs(asset)) {
1070
- const value = sanitized[input.name];
1071
- if (value === void 0) {
1353
+ function mergeOverrideConfig(base, local) {
1354
+ const merged = {
1355
+ ...base ?? {},
1356
+ ...local
1357
+ };
1358
+ const reasoning = mergeRecordBlock2(base?.reasoning, local.reasoning);
1359
+ const sampling = mergeRecordBlock2(base?.sampling, local.sampling);
1360
+ const response = mergeRecordBlock2(base?.response, local.response);
1361
+ const compression = mergeCompression(base?.compression, local.compression);
1362
+ const cache = mergeCache(base?.cache, local.cache);
1363
+ const raw = mergeRaw(base?.raw, local.raw);
1364
+ const providerOptions = mergeProviderOptions(base?.provider_options, local.provider_options);
1365
+ if (reasoning !== void 0) merged.reasoning = reasoning;
1366
+ if (sampling !== void 0) merged.sampling = sampling;
1367
+ if (response !== void 0) merged.response = response;
1368
+ if (compression !== void 0) merged.compression = compression;
1369
+ if (cache !== void 0) merged.cache = cache;
1370
+ if (raw !== void 0) merged.raw = raw;
1371
+ if (providerOptions !== void 0) merged.provider_options = providerOptions;
1372
+ return merged;
1373
+ }
1374
+ function removeEmptyProviderBlocks(value) {
1375
+ for (const key of Object.keys(value)) {
1376
+ const block = value[key];
1377
+ if (block === void 0) {
1378
+ delete value[key];
1072
1379
  continue;
1073
1380
  }
1074
- let candidate = value;
1075
- if (input.max_size !== void 0) {
1076
- const actualSize = measureContextValueSize(candidate);
1077
- if (actualSize > input.max_size && onContextOverflow) {
1078
- candidate = onContextOverflow({
1079
- promptId: asset.id,
1080
- variable: input.name,
1081
- value: candidate,
1082
- maxSize: input.max_size,
1083
- actualSize
1084
- });
1085
- }
1381
+ if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
1382
+ delete value[key];
1383
+ }
1384
+ }
1385
+ }
1386
+
1387
+ // src/composition/resolve-includes.ts
1388
+ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
1389
+ if (!asset.includes || asset.includes.length === 0) {
1390
+ return asset;
1391
+ }
1392
+ const baseDir = (0, import_node_path3.dirname)(basePath);
1393
+ const resolvedPath = (0, import_node_path3.resolve)(basePath);
1394
+ if (visited.has(resolvedPath)) {
1395
+ throw new Error(`Circular include detected: ${resolvedPath}`);
1396
+ }
1397
+ visited.add(resolvedPath);
1398
+ let mergedSystemInstructions = "";
1399
+ for (const includePath of asset.includes) {
1400
+ const fullPath = (0, import_node_path3.resolve)(baseDir, includePath);
1401
+ if (visited.has(fullPath)) {
1402
+ throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
1086
1403
  }
1087
- if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
1088
- candidate = trimToMaxSize(candidate, input.max_size, input.trim);
1404
+ const content = await (0, import_promises3.readFile)(fullPath, "utf-8");
1405
+ const { asset: includedAsset } = parsePrompt(content, fullPath);
1406
+ const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
1407
+ if (resolved.sections?.system_instructions) {
1408
+ mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
1089
1409
  }
1090
- sanitized[input.name] = candidate;
1091
- if (input.allow_regex) {
1092
- const candidate2 = sanitized[input.name];
1093
- const matcher = compileContextRegex(input.allow_regex, {
1094
- promptId: asset.id,
1095
- variable: input.name,
1096
- field: "allow_regex"
1097
- });
1098
- if (!matcher.test(candidate2)) {
1099
- if (input.allow_regex.returnMessage) {
1100
- return {
1101
- variables: sanitized,
1102
- shortCircuit: {
1103
- returnMessage: input.allow_regex.returnMessage,
1104
- code: "POK031",
1105
- variable: input.name,
1106
- field: "allow_regex"
1107
- }
1108
- };
1410
+ }
1411
+ const localSystem = asset.sections?.system_instructions ?? "";
1412
+ const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
1413
+ return {
1414
+ ...asset,
1415
+ sections: {
1416
+ ...asset.sections,
1417
+ system_instructions: combinedSystem
1418
+ },
1419
+ // Drop includes from the resolved asset — they've been inlined
1420
+ includes: void 0
1421
+ };
1422
+ }
1423
+
1424
+ // src/prompt-resolution.ts
1425
+ var DEFAULT_PROMPTS_DIR = "./prompts";
1426
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
1427
+ function withPromptResolutionDefaults(config) {
1428
+ return {
1429
+ ...config,
1430
+ sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
1431
+ compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
1432
+ };
1433
+ }
1434
+ var sharedPromptCache = new PromptCache();
1435
+ async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
1436
+ const resolvedConfig = withPromptResolutionDefaults(config);
1437
+ const mode = resolvedConfig.mode ?? "auto";
1438
+ if (mode !== "source-only" && resolvedConfig.compiledDir) {
1439
+ const compiledFile = (0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
1440
+ if ((0, import_node_fs2.existsSync)(compiledFile)) {
1441
+ if (mode === "auto") {
1442
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1443
+ if ((0, import_node_fs2.existsSync)(sourceFile)) {
1444
+ const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
1445
+ const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
1446
+ if (sourceMtime > compiledMtime) {
1447
+ console.warn(
1448
+ `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
1449
+ Run "promptopskit compile" or switch to source-only mode.`
1450
+ );
1451
+ }
1109
1452
  }
1110
- throw new Error(
1111
- `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
1112
- );
1113
1453
  }
1454
+ const content = await (0, import_promises4.readFile)(compiledFile, "utf-8");
1455
+ return JSON.parse(content);
1114
1456
  }
1115
- if (input.deny_regex) {
1116
- const candidate2 = sanitized[input.name];
1117
- const matcher = compileContextRegex(input.deny_regex, {
1118
- promptId: asset.id,
1119
- variable: input.name,
1120
- field: "deny_regex"
1121
- });
1122
- if (matcher.test(candidate2)) {
1123
- if (input.deny_regex.returnMessage) {
1124
- return {
1125
- variables: sanitized,
1126
- shortCircuit: {
1127
- returnMessage: input.deny_regex.returnMessage,
1128
- code: "POK032",
1129
- variable: input.name,
1130
- field: "deny_regex"
1131
- }
1132
- };
1133
- }
1134
- throw new Error(
1135
- `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
1136
- );
1457
+ if (mode === "compiled-only") {
1458
+ throw new Error(
1459
+ `Compiled artifact not found: ${compiledFile}
1460
+ Run "promptopskit compile" to generate it.`
1461
+ );
1462
+ }
1463
+ }
1464
+ if (mode !== "compiled-only") {
1465
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1466
+ if (resolvedConfig.cache !== false) {
1467
+ const cached = promptCache.get(sourceFile);
1468
+ if (cached) {
1469
+ return cached;
1137
1470
  }
1138
1471
  }
1139
- if (input.non_empty && candidate.trim().length === 0) {
1140
- if (input.non_empty.returnMessage) {
1141
- return {
1142
- variables: sanitized,
1143
- shortCircuit: {
1144
- returnMessage: input.non_empty.returnMessage,
1145
- code: "POK033",
1146
- variable: input.name,
1147
- field: "non_empty"
1148
- }
1149
- };
1472
+ if (!(0, import_node_fs2.existsSync)(sourceFile)) {
1473
+ const paths = [sourceFile];
1474
+ if (resolvedConfig.compiledDir) {
1475
+ paths.unshift((0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
1150
1476
  }
1151
1477
  throw new Error(
1152
- `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
1478
+ `Prompt not found: "${promptPath}"
1479
+ Searched:
1480
+ ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
1153
1481
  );
1154
1482
  }
1155
- if (input.reject_secrets) {
1156
- const matcher = compileContextRegex(getRejectSecretsRegex(), {
1157
- promptId: asset.id,
1158
- variable: input.name,
1159
- field: "reject_secrets"
1160
- });
1161
- if (matcher.test(candidate)) {
1162
- if (input.reject_secrets.returnMessage) {
1163
- return {
1164
- variables: sanitized,
1165
- shortCircuit: {
1166
- returnMessage: input.reject_secrets.returnMessage,
1167
- code: "POK034",
1168
- variable: input.name,
1169
- field: "reject_secrets"
1170
- }
1171
- };
1172
- }
1173
- throw new Error(
1174
- `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
1175
- );
1176
- }
1483
+ const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
1484
+ if (resolvedConfig.cache !== false) {
1485
+ promptCache.set(sourceFile, asset);
1177
1486
  }
1487
+ return asset;
1178
1488
  }
1179
- return { variables: sanitized };
1180
- }
1181
- function measureContextValueSize(value) {
1182
- return textEncoder.encode(value).length;
1489
+ throw new Error(`Prompt not found: "${promptPath}"`);
1183
1490
  }
1184
- function collectContextSizeWarnings(asset, variables = {}) {
1185
- const warnings = [];
1186
- for (const input of getContextInputs(asset)) {
1187
- if (input.max_size === void 0) {
1188
- continue;
1189
- }
1190
- const value = variables[input.name];
1191
- if (value === void 0) {
1192
- continue;
1193
- }
1194
- const actualSize = measureContextValueSize(value);
1195
- if (actualSize > input.max_size) {
1196
- warnings.push({
1197
- variable: input.name,
1198
- maxSize: input.max_size,
1199
- actualSize
1200
- });
1201
- }
1491
+ async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
1492
+ const resolvedConfig = withPromptResolutionDefaults(config);
1493
+ let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
1494
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1495
+ if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
1496
+ asset = await resolveIncludes(asset, sourceFile);
1202
1497
  }
1203
- return warnings;
1498
+ asset = applyOverrides(asset, {
1499
+ environment: options.environment,
1500
+ tier: options.tier,
1501
+ runtime: options.runtime
1502
+ });
1503
+ return asset;
1504
+ }
1505
+ function resolveInlinePromptSource(source, options = {}) {
1506
+ const { asset } = parsePrompt(source);
1507
+ return applyOverrides(asset, {
1508
+ environment: options.environment,
1509
+ tier: options.tier,
1510
+ runtime: options.runtime
1511
+ });
1204
1512
  }
1205
1513
 
1206
1514
  // src/history.ts
@@ -1253,6 +1561,97 @@ function compactHistoryForPrompt(asset, runtime) {
1253
1561
  return [compacted, ...preserved];
1254
1562
  }
1255
1563
 
1564
+ // src/compression.ts
1565
+ var THETOKENCOMPANY_DEFAULT_MODEL = "bear-2";
1566
+ var THETOKENCOMPANY_DEFAULT_BASE_URL = "https://api.thetokencompany.com";
1567
+ async function applyPromptCompressionForRender(asset, runtime) {
1568
+ const config = asset.compression?.thetokencompany;
1569
+ if (config?.enabled !== true || !asset.sections.prompt_template) {
1570
+ return { asset, runtime, compression: [] };
1571
+ }
1572
+ const sections = renderSections(asset, {
1573
+ variables: runtime.variables,
1574
+ strict: runtime.strict
1575
+ });
1576
+ if (!sections.prompt_template) {
1577
+ return { asset, runtime, compression: [] };
1578
+ }
1579
+ const model = config.model ?? THETOKENCOMPANY_DEFAULT_MODEL;
1580
+ const result = await compressWithTheTokenCompany(sections.prompt_template, {
1581
+ apiKey: runtime.theTokenCompany?.apiKey,
1582
+ baseURL: runtime.theTokenCompany?.baseURL,
1583
+ fetch: runtime.theTokenCompany?.fetch,
1584
+ model,
1585
+ aggressiveness: config.aggressiveness
1586
+ });
1587
+ return {
1588
+ asset: {
1589
+ ...asset,
1590
+ sections: {
1591
+ ...asset.sections,
1592
+ ...sections,
1593
+ prompt_template: result.output
1594
+ }
1595
+ },
1596
+ runtime: {
1597
+ ...runtime,
1598
+ variables: {},
1599
+ strict: false
1600
+ },
1601
+ compression: [{
1602
+ provider: "thetokencompany",
1603
+ model,
1604
+ inputTokens: result.input_tokens,
1605
+ outputTokens: result.output_tokens,
1606
+ tokensSaved: result.tokens_saved,
1607
+ compressionRatio: result.compression_ratio
1608
+ }]
1609
+ };
1610
+ }
1611
+ async function compressWithTheTokenCompany(input, options) {
1612
+ const apiKey = options.apiKey ?? getEnv("THETOKENCOMPANY_API_KEY") ?? getEnv("TTC_API_KEY");
1613
+ if (!apiKey) {
1614
+ throw new Error(
1615
+ "TheTokenCompany compression is enabled, but no API key was provided. Pass theTokenCompany.apiKey to renderPrompt() or set THETOKENCOMPANY_API_KEY."
1616
+ );
1617
+ }
1618
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1619
+ if (!fetchImpl) {
1620
+ throw new Error("TheTokenCompany compression requires a runtime with fetch support.");
1621
+ }
1622
+ const baseURL = (options.baseURL ?? THETOKENCOMPANY_DEFAULT_BASE_URL).replace(/\/+$/, "");
1623
+ const compressionSettings = options.aggressiveness === void 0 ? void 0 : { aggressiveness: options.aggressiveness };
1624
+ const response = await fetchImpl(`${baseURL}/v1/compress`, {
1625
+ method: "POST",
1626
+ headers: {
1627
+ Authorization: `Bearer ${apiKey}`,
1628
+ "Content-Type": "application/json"
1629
+ },
1630
+ body: JSON.stringify({
1631
+ model: options.model,
1632
+ input,
1633
+ ...compressionSettings ? { compression_settings: compressionSettings } : {}
1634
+ })
1635
+ });
1636
+ if (!response.ok) {
1637
+ const body = await response.text().catch(() => "");
1638
+ throw new Error(
1639
+ `TheTokenCompany compression failed with HTTP ${response.status}` + (body ? `: ${body}` : ".")
1640
+ );
1641
+ }
1642
+ const data = await response.json();
1643
+ if (typeof data.output !== "string" || typeof data.output_tokens !== "number" || typeof data.input_tokens !== "number" || typeof data.tokens_saved !== "number" || typeof data.compression_ratio !== "number") {
1644
+ throw new Error("TheTokenCompany compression returned an invalid response payload.");
1645
+ }
1646
+ return data;
1647
+ }
1648
+ function getEnv(name) {
1649
+ if (typeof process === "undefined") {
1650
+ return void 0;
1651
+ }
1652
+ return process.env[name];
1653
+ }
1654
+
1256
1655
  // src/providers/prompt-input.ts
1257
1656
  function isPromptLookup(input) {
1258
1657
  return "path" in input && typeof input.path === "string";
@@ -1276,21 +1675,27 @@ function withPromptInputSupport(adapter) {
1276
1675
  };
1277
1676
  const renderPrompt2 = async (input, runtime) => {
1278
1677
  const resolved = await resolveProviderPromptInput(input, runtime);
1279
- const sanitization = sanitizeContextVariables(resolved, runtime.variables, {
1678
+ const resolvedForRender = resolveAssetForProvider(resolved, runtime);
1679
+ const sanitization = sanitizeContextVariables(resolvedForRender, runtime.variables, {
1280
1680
  onContextOverflow: runtime.onContextOverflow
1281
1681
  });
1282
1682
  if (sanitization.shortCircuit) {
1283
1683
  return {
1284
1684
  provider: adapter.name,
1285
- model: resolved.model ?? "",
1685
+ model: resolvedForRender.model ?? "",
1286
1686
  returnMessage: sanitization.shortCircuit.returnMessage
1287
1687
  };
1288
1688
  }
1289
- return adapter.render(resolved, {
1689
+ const prepared = await applyPromptCompressionForRender(resolvedForRender, {
1290
1690
  ...runtime,
1691
+ environment: void 0,
1692
+ tier: void 0,
1693
+ runtime: void 0,
1291
1694
  variables: sanitization.variables,
1292
- history: compactHistoryForPrompt(resolved, runtime)
1695
+ history: compactHistoryForPrompt(resolvedForRender, runtime)
1293
1696
  });
1697
+ const request = adapter.render(prepared.asset, prepared.runtime);
1698
+ return prepared.compression.length > 0 ? { ...request, compression: prepared.compression } : request;
1294
1699
  };
1295
1700
  return {
1296
1701
  ...adapter,
@@ -1982,9 +2387,22 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
1982
2387
  "tiers",
1983
2388
  "metadata",
1984
2389
  "cache",
2390
+ "compression",
1985
2391
  "provider_options",
1986
2392
  "raw"
1987
2393
  ]);
2394
+ function collectSchemaKeywords(value, acc) {
2395
+ if (!value || typeof value !== "object") return;
2396
+ if (Array.isArray(value)) {
2397
+ for (const item of value) collectSchemaKeywords(item, acc);
2398
+ return;
2399
+ }
2400
+ const record = value;
2401
+ for (const [key, child] of Object.entries(record)) {
2402
+ acc.add(key);
2403
+ collectSchemaKeywords(child, acc);
2404
+ }
2405
+ }
1988
2406
  var RISKY_UNBOUNDED_INPUT_NAMES = [
1989
2407
  "message",
1990
2408
  "prompt",
@@ -2036,7 +2454,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2036
2454
  }
2037
2455
  }
2038
2456
  }
2039
- const declaredInputs = new Set(getContextInputNames(asset));
2457
+ const contextInputs = getContextInputs(asset);
2458
+ const declaredInputs = new Set(contextInputs.map((input) => input.name));
2459
+ const declaredInputsByName = new Map(contextInputs.map((input) => [input.name, input]));
2040
2460
  const usedVars = /* @__PURE__ */ new Set();
2041
2461
  if (asset.sections?.system_instructions) {
2042
2462
  for (const v of extractVariables(asset.sections.system_instructions)) {
@@ -2058,7 +2478,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2058
2478
  }
2059
2479
  }
2060
2480
  for (const v of declaredInputs) {
2061
- if (!usedVars.has(v)) {
2481
+ const input = declaredInputsByName.get(v);
2482
+ if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
2062
2483
  warnings.push({
2063
2484
  code: "POK012",
2064
2485
  message: `Variable "${v}" is declared in context.inputs but never used`,
@@ -2074,9 +2495,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2074
2495
  suggestion: "Declare context.inputs to enable input policy validation."
2075
2496
  });
2076
2497
  }
2077
- for (const input of getContextInputs(asset)) {
2498
+ for (const input of contextInputs) {
2078
2499
  const lowerName = input.name.toLowerCase();
2079
- if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
2500
+ const inputWarningsEnabled = areContextInputWarningsEnabled(input);
2501
+ if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
2080
2502
  warnings.push({
2081
2503
  code: "POK040",
2082
2504
  message: `Context input "${input.name}" has no max_size and appears unbounded.`,
@@ -2084,7 +2506,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2084
2506
  suggestion: "Add max_size to constrain prompt payload growth."
2085
2507
  });
2086
2508
  }
2087
- if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
2509
+ if (inputWarningsEnabled && input.optional !== true && input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
2088
2510
  warnings.push({
2089
2511
  code: "POK041",
2090
2512
  message: `Context input "${input.name}" has no input hardening validators.`,
@@ -2092,7 +2514,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2092
2514
  suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
2093
2515
  });
2094
2516
  }
2095
- if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
2517
+ if (inputWarningsEnabled && input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
2096
2518
  warnings.push({
2097
2519
  code: "POK014",
2098
2520
  message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
@@ -2119,6 +2541,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
2119
2541
  }
2120
2542
  }
2121
2543
  }
2544
+ if (asset.response?.schema && asset.provider) {
2545
+ const keywords = /* @__PURE__ */ new Set();
2546
+ collectSchemaKeywords(asset.response.schema, keywords);
2547
+ const openAIUnsupported = ["patternProperties", "unevaluatedProperties", "$dynamicRef"];
2548
+ const geminiUnsupported = ["patternProperties", "$dynamicRef", "dependentSchemas"];
2549
+ const anthropicCaution = ["if", "then", "else"];
2550
+ const checks = [
2551
+ { provider: ["openai", "openai-responses", "openrouter", "llmasaservice"], unsupported: openAIUnsupported, code: "POK052" },
2552
+ { provider: ["gemini", "google"], unsupported: geminiUnsupported, code: "POK053" },
2553
+ { provider: ["anthropic"], unsupported: anthropicCaution, code: "POK054" }
2554
+ ];
2555
+ for (const check of checks) {
2556
+ if (!check.provider.includes(asset.provider)) continue;
2557
+ const hits = check.unsupported.filter((k) => keywords.has(k));
2558
+ if (hits.length > 0) {
2559
+ warnings.push({
2560
+ code: check.code,
2561
+ message: `response.schema includes provider-sensitive JSON Schema keywords for "${asset.provider}": ${hits.join(", ")}`,
2562
+ filePath,
2563
+ suggestion: "Validate schema compatibility with your provider docs or simplify the schema dialect."
2564
+ });
2565
+ }
2566
+ }
2567
+ }
2122
2568
  if (asset.provider) {
2123
2569
  let providerCache;
2124
2570
  let cacheSuggestionField;
@@ -2712,7 +3158,7 @@ var PromptOpsKit = class {
2712
3158
  console.warn(`[promptopskit] Warning: ${warning}`);
2713
3159
  }
2714
3160
  }
2715
- const request = adapter.render(resolved, {
3161
+ const prepared = await applyPromptCompressionForRender(resolved, {
2716
3162
  variables: sanitization.variables,
2717
3163
  history: compactHistoryForPrompt(resolved, {
2718
3164
  history: options.history,
@@ -2721,11 +3167,14 @@ var PromptOpsKit = class {
2721
3167
  onHistoryCompaction: options.onHistoryCompaction,
2722
3168
  toolRegistry: options.toolRegistry,
2723
3169
  strict: options.strict,
2724
- openaiResponses: options.openaiResponses
3170
+ openaiResponses: options.openaiResponses,
3171
+ theTokenCompany: options.theTokenCompany
2725
3172
  });
3173
+ const request = adapter.render(prepared.asset, prepared.runtime);
2726
3174
  return {
2727
3175
  resolved,
2728
- request,
3176
+ request: prepared.compression.length > 0 ? { ...request, compression: prepared.compression } : request,
3177
+ ...prepared.compression.length > 0 ? { compression: prepared.compression } : {},
2729
3178
  warnings: shouldIncludeContextWarningsInResult(contextWarningPolicy) ? [...validation.warnings, ...contextSizeWarnings] : validation.warnings
2730
3179
  };
2731
3180
  }
@@ -2733,7 +3182,7 @@ var PromptOpsKit = class {
2733
3182
  * Validate a prompt file.
2734
3183
  */
2735
3184
  async validatePrompt(promptPath) {
2736
- const sourceFile = (0, import_node_path4.resolve)(this.config.sourceDir, promptPath + ".md");
3185
+ const sourceFile = (0, import_node_path5.resolve)(this.config.sourceDir, promptPath + ".md");
2737
3186
  try {
2738
3187
  const asset = await this.loadPrompt(promptPath);
2739
3188
  return validateAssetWithIncludes(asset, sourceFile);
@@ -2790,6 +3239,8 @@ async function renderPrompt(options) {
2790
3239
  PromptAssetOverridesSchema,
2791
3240
  PromptAssetSchema,
2792
3241
  PromptOpsKit,
3242
+ THETOKENCOMPANY_DEFAULT_BASE_URL,
3243
+ THETOKENCOMPANY_DEFAULT_MODEL,
2793
3244
  anthropicAdapter,
2794
3245
  applyOverrides,
2795
3246
  applyUsageTapEntitlements,