promptopskit 0.3.8 → 0.4.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 (73) hide show
  1. package/README.md +97 -10
  2. package/SKILL.md +69 -6
  3. package/dist/chunk-F6UJ73Y6.js +113 -0
  4. package/dist/chunk-F6UJ73Y6.js.map +1 -0
  5. package/dist/chunk-HLNVKKMS.js +145 -0
  6. package/dist/chunk-HLNVKKMS.js.map +1 -0
  7. package/dist/{chunk-5OYWNRNC.js → chunk-JBLUPKY4.js} +191 -5
  8. package/dist/chunk-JBLUPKY4.js.map +1 -0
  9. package/dist/{chunk-UJU5XERR.js → chunk-L2V7LDKD.js} +25 -4
  10. package/dist/chunk-L2V7LDKD.js.map +1 -0
  11. package/dist/{chunk-E6NPZOJL.js → chunk-RY5DVTLV.js} +29 -4
  12. package/dist/chunk-RY5DVTLV.js.map +1 -0
  13. package/dist/chunk-UPTCSWK3.js +60 -0
  14. package/dist/chunk-UPTCSWK3.js.map +1 -0
  15. package/dist/{chunk-X73VG645.js → chunk-XNNDGDPI.js} +64 -3
  16. package/dist/chunk-XNNDGDPI.js.map +1 -0
  17. package/dist/cli/index.js +423 -7
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/index.cjs +655 -22
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +7 -4
  22. package/dist/index.d.ts +7 -4
  23. package/dist/index.js +172 -11
  24. package/dist/index.js.map +1 -1
  25. package/dist/providers/anthropic.cjs +306 -10
  26. package/dist/providers/anthropic.cjs.map +1 -1
  27. package/dist/providers/anthropic.d.cts +2 -2
  28. package/dist/providers/anthropic.d.ts +2 -2
  29. package/dist/providers/anthropic.js +3 -3
  30. package/dist/providers/gemini.cjs +274 -6
  31. package/dist/providers/gemini.cjs.map +1 -1
  32. package/dist/providers/gemini.d.cts +2 -2
  33. package/dist/providers/gemini.d.ts +2 -2
  34. package/dist/providers/gemini.js +3 -3
  35. package/dist/providers/openai-responses.cjs +1245 -0
  36. package/dist/providers/openai-responses.cjs.map +1 -0
  37. package/dist/providers/openai-responses.d.cts +11 -0
  38. package/dist/providers/openai-responses.d.ts +11 -0
  39. package/dist/providers/openai-responses.js +9 -0
  40. package/dist/providers/openai-responses.js.map +1 -0
  41. package/dist/providers/openai.cjs +270 -6
  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 +305 -9
  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-B9A1zLRk.d.cts +2403 -0
  52. package/dist/schema-B9A1zLRk.d.ts +2403 -0
  53. package/dist/testing.cjs +264 -6
  54. package/dist/testing.cjs.map +1 -1
  55. package/dist/testing.d.cts +59 -2
  56. package/dist/testing.d.ts +59 -2
  57. package/dist/testing.js +70 -2
  58. package/dist/testing.js.map +1 -1
  59. package/dist/{types-fBvo_d4k.d.ts → types-DjGAwJxC.d.ts} +13 -1
  60. package/dist/{types-C6RG0Si0.d.cts → types-DpbzzZO2.d.cts} +13 -1
  61. package/dist/usagetap/index.d.cts +2 -2
  62. package/dist/usagetap/index.d.ts +2 -2
  63. package/package.json +12 -2
  64. package/dist/chunk-5OYWNRNC.js.map +0 -1
  65. package/dist/chunk-BBVVA27J.js +0 -92
  66. package/dist/chunk-BBVVA27J.js.map +0 -1
  67. package/dist/chunk-E2NXJLS6.js +0 -26
  68. package/dist/chunk-E2NXJLS6.js.map +0 -1
  69. package/dist/chunk-E6NPZOJL.js.map +0 -1
  70. package/dist/chunk-UJU5XERR.js.map +0 -1
  71. package/dist/chunk-X73VG645.js.map +0 -1
  72. package/dist/schema-DwzYbZba.d.cts +0 -859
  73. package/dist/schema-DwzYbZba.d.ts +0 -859
package/dist/index.cjs CHANGED
@@ -51,6 +51,7 @@ __export(src_exports, {
51
51
  interpolate: () => interpolate,
52
52
  loadPromptFile: () => loadPromptFile,
53
53
  openaiAdapter: () => openaiAdapter,
54
+ openaiResponsesAdapter: () => openaiResponsesAdapter,
54
55
  openrouterAdapter: () => openrouterAdapter,
55
56
  parsePrompt: () => parsePrompt,
56
57
  renderPrompt: () => renderPrompt,
@@ -144,8 +145,53 @@ function mergeOverride(base, override) {
144
145
  if (override.response !== void 0) {
145
146
  result.response = { ...result.response, ...override.response };
146
147
  }
148
+ if (override.cache !== void 0) {
149
+ result.cache = {
150
+ ...result.cache,
151
+ ...override.cache,
152
+ openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
153
+ anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
154
+ gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
155
+ google: mergeRecordBlock(result.cache?.google, override.cache.google)
156
+ };
157
+ }
158
+ if (override.raw !== void 0) {
159
+ result.raw = {
160
+ ...result.raw,
161
+ ...override.raw,
162
+ openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
163
+ "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
164
+ openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
165
+ anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
166
+ gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
167
+ google: mergeRecordBlock(result.raw?.google, override.raw.google),
168
+ openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter)
169
+ };
170
+ }
171
+ if (override.provider_options !== void 0) {
172
+ result.provider_options = {
173
+ ...result.provider_options,
174
+ ...override.provider_options,
175
+ anthropic: {
176
+ ...result.provider_options?.anthropic,
177
+ ...override.provider_options.anthropic
178
+ },
179
+ gemini: {
180
+ ...result.provider_options?.gemini,
181
+ ...override.provider_options.gemini
182
+ },
183
+ openrouter: {
184
+ ...result.provider_options?.openrouter,
185
+ ...override.provider_options.openrouter
186
+ }
187
+ };
188
+ }
147
189
  return result;
148
190
  }
191
+ function mergeRecordBlock(base, override) {
192
+ if (override === void 0) return base;
193
+ return { ...base, ...override };
194
+ }
149
195
 
150
196
  // src/providers/resolve-asset.ts
151
197
  function resolveAssetForProvider(asset, runtime = {}) {
@@ -246,7 +292,66 @@ var SamplingSchema = import_zod.z.object({
246
292
  });
247
293
  var ResponseSchema = import_zod.z.object({
248
294
  format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
249
- stream: import_zod.z.boolean().optional()
295
+ stream: import_zod.z.boolean().optional(),
296
+ schema: import_zod.z.record(import_zod.z.unknown()).optional(),
297
+ schema_name: import_zod.z.string().optional(),
298
+ schema_description: import_zod.z.string().optional(),
299
+ schema_strict: import_zod.z.boolean().optional()
300
+ });
301
+ var AnthropicProviderOptionsSchema = import_zod.z.object({
302
+ top_k: import_zod.z.number().int().min(0).optional(),
303
+ tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
304
+ output_config: import_zod.z.record(import_zod.z.unknown()).optional()
305
+ });
306
+ var GeminiProviderOptionsSchema = import_zod.z.object({
307
+ candidate_count: import_zod.z.number().int().positive().optional(),
308
+ top_k: import_zod.z.number().int().min(0).optional(),
309
+ seed: import_zod.z.number().int().optional(),
310
+ response_schema: import_zod.z.record(import_zod.z.unknown()).optional(),
311
+ response_json_schema: import_zod.z.record(import_zod.z.unknown()).optional(),
312
+ response_modalities: import_zod.z.array(import_zod.z.string()).optional(),
313
+ thinking_budget_tokens: import_zod.z.number().int().positive().optional()
314
+ });
315
+ var OpenRouterProviderOptionsSchema = import_zod.z.object({
316
+ provider: import_zod.z.record(import_zod.z.unknown()).optional(),
317
+ transforms: import_zod.z.array(import_zod.z.string()).optional(),
318
+ plugins: import_zod.z.array(import_zod.z.record(import_zod.z.unknown())).optional(),
319
+ models: import_zod.z.array(import_zod.z.string()).optional()
320
+ });
321
+ var ProviderOptionsSchema = import_zod.z.object({
322
+ anthropic: AnthropicProviderOptionsSchema.optional(),
323
+ gemini: GeminiProviderOptionsSchema.optional(),
324
+ openrouter: OpenRouterProviderOptionsSchema.optional()
325
+ });
326
+ var RawProviderBodySchema = import_zod.z.object({
327
+ openai: import_zod.z.record(import_zod.z.unknown()).optional(),
328
+ "openai-responses": import_zod.z.record(import_zod.z.unknown()).optional(),
329
+ openai_responses: import_zod.z.record(import_zod.z.unknown()).optional(),
330
+ anthropic: import_zod.z.record(import_zod.z.unknown()).optional(),
331
+ gemini: import_zod.z.record(import_zod.z.unknown()).optional(),
332
+ google: import_zod.z.record(import_zod.z.unknown()).optional(),
333
+ openrouter: import_zod.z.record(import_zod.z.unknown()).optional()
334
+ });
335
+ var OpenAICacheSchema = import_zod.z.object({
336
+ prompt_cache_key: import_zod.z.string().min(1).optional(),
337
+ retention: import_zod.z.enum(["in_memory", "24h"]).optional()
338
+ });
339
+ var AnthropicCacheSchema = import_zod.z.object({
340
+ mode: import_zod.z.enum(["automatic", "explicit"]).optional(),
341
+ type: import_zod.z.literal("ephemeral").optional(),
342
+ ttl: import_zod.z.enum(["5m", "1h"]).optional(),
343
+ cache_system_instructions: import_zod.z.boolean().optional(),
344
+ cache_tools: import_zod.z.boolean().optional(),
345
+ cache_prompt_template: import_zod.z.boolean().optional()
346
+ });
347
+ var GeminiCacheSchema = import_zod.z.object({
348
+ cached_content: import_zod.z.string().min(1).optional()
349
+ });
350
+ var CacheSchema = import_zod.z.object({
351
+ openai: OpenAICacheSchema.optional(),
352
+ anthropic: AnthropicCacheSchema.optional(),
353
+ gemini: GeminiCacheSchema.optional(),
354
+ google: GeminiCacheSchema.optional()
250
355
  });
251
356
  var HistorySchema = import_zod.z.object({
252
357
  max_items: import_zod.z.number().int().positive().optional()
@@ -297,7 +402,10 @@ var PromptAssetOverridesSchema = import_zod.z.object({
297
402
  reasoning: ReasoningSchema.optional(),
298
403
  sampling: SamplingSchema.optional(),
299
404
  response: ResponseSchema.optional(),
300
- tools: import_zod.z.array(ToolRefSchema).optional()
405
+ cache: CacheSchema.optional(),
406
+ raw: RawProviderBodySchema.optional(),
407
+ tools: import_zod.z.array(ToolRefSchema).optional(),
408
+ provider_options: ProviderOptionsSchema.optional()
301
409
  });
302
410
  var SourceSchema = import_zod.z.object({
303
411
  file_path: import_zod.z.string().optional(),
@@ -309,8 +417,9 @@ var SectionsSchema = import_zod.z.object({
309
417
  notes: import_zod.z.string().optional()
310
418
  });
311
419
  var PromptDefaultsSchema = import_zod.z.object({
312
- provider: import_zod.z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
420
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
313
421
  model: import_zod.z.string().optional(),
422
+ cache: CacheSchema.optional(),
314
423
  metadata: MetadataSchema.optional(),
315
424
  sections: import_zod.z.object({
316
425
  system_instructions: import_zod.z.string().optional()
@@ -320,13 +429,16 @@ var PromptAssetSchema = import_zod.z.object({
320
429
  id: import_zod.z.string(),
321
430
  schema_version: import_zod.z.number().int().positive().default(1),
322
431
  description: import_zod.z.string().optional(),
323
- provider: import_zod.z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
432
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
324
433
  model: import_zod.z.string().optional(),
325
434
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
326
435
  reasoning: ReasoningSchema.optional(),
327
436
  sampling: SamplingSchema.optional(),
328
437
  response: ResponseSchema.optional(),
438
+ cache: CacheSchema.optional(),
439
+ raw: RawProviderBodySchema.optional(),
329
440
  tools: import_zod.z.array(ToolRefSchema).optional(),
441
+ provider_options: ProviderOptionsSchema.optional(),
330
442
  mcp: MCPSchema.optional(),
331
443
  context: ContextSchema.optional(),
332
444
  includes: import_zod.z.array(import_zod.z.string()).optional(),
@@ -338,6 +450,123 @@ var PromptAssetSchema = import_zod.z.object({
338
450
  source: SourceSchema.optional()
339
451
  });
340
452
 
453
+ // src/parser/frontmatter-guard.ts
454
+ var FRONT_MATTER_DELIMITER = /^---[ \t]*$/;
455
+ var REGEX_FIELD = /^(?<indent>\s*)(?<field>allow_regex|deny_regex)\s*:\s*(?<value>.*)$/;
456
+ var PATTERN_FIELD = /^(?<indent>\s*)pattern\s*:\s*(?<value>.*)$/;
457
+ var INLINE_PATTERN_FIELD = /(?:^|[{,]\s*)pattern\s*:\s*(?<value>"(?:\\.|[^"\\])*")/;
458
+ function assertRegexFrontMatterQuoting(content, filePath) {
459
+ const frontMatter = extractFrontMatter(content);
460
+ if (!frontMatter) {
461
+ return;
462
+ }
463
+ let pendingRegex;
464
+ for (const [index, line] of frontMatter.lines.entries()) {
465
+ const lineNumber = frontMatter.startLine + index;
466
+ const regexMatch = line.match(REGEX_FIELD);
467
+ if (regexMatch?.groups) {
468
+ const field = regexMatch.groups.field;
469
+ const indent2 = regexMatch.groups.indent.length;
470
+ const value = regexMatch.groups.value.trim();
471
+ pendingRegex = value === "" ? { field, indent: indent2 } : void 0;
472
+ assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber);
473
+ assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber);
474
+ continue;
475
+ }
476
+ if (!pendingRegex) {
477
+ continue;
478
+ }
479
+ if (line.trim() === "" || line.trimStart().startsWith("#")) {
480
+ continue;
481
+ }
482
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
483
+ if (indent <= pendingRegex.indent) {
484
+ pendingRegex = void 0;
485
+ continue;
486
+ }
487
+ const patternMatch = line.match(PATTERN_FIELD);
488
+ if (!patternMatch?.groups) {
489
+ continue;
490
+ }
491
+ assertNoUnescapedBackslashInDoubleQuotedRegex(
492
+ patternMatch.groups.value.trim(),
493
+ `${pendingRegex.field}.pattern`,
494
+ filePath,
495
+ lineNumber
496
+ );
497
+ }
498
+ }
499
+ function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
500
+ const match = value.match(INLINE_PATTERN_FIELD);
501
+ const pattern = match?.groups?.value;
502
+ if (!pattern) {
503
+ return;
504
+ }
505
+ assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
506
+ }
507
+ function extractFrontMatter(content) {
508
+ const lines = content.split(/\r?\n/);
509
+ if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
510
+ return void 0;
511
+ }
512
+ for (let index = 1; index < lines.length; index += 1) {
513
+ if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
514
+ return {
515
+ lines: lines.slice(1, index),
516
+ startLine: 2
517
+ };
518
+ }
519
+ }
520
+ return void 0;
521
+ }
522
+ function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
523
+ if (!value.startsWith('"')) {
524
+ return;
525
+ }
526
+ const quoted = readDoubleQuotedScalar(value);
527
+ if (!quoted) {
528
+ return;
529
+ }
530
+ if (!hasUnescapedBackslash(quoted)) {
531
+ return;
532
+ }
533
+ const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
534
+ throw new Error(
535
+ `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.`
536
+ );
537
+ }
538
+ function readDoubleQuotedScalar(value) {
539
+ let result = "";
540
+ for (let index = 1; index < value.length; index += 1) {
541
+ const char = value[index];
542
+ if (char === '"') {
543
+ return result;
544
+ }
545
+ result += char;
546
+ if (char === "\\" && index + 1 < value.length) {
547
+ index += 1;
548
+ result += value[index];
549
+ }
550
+ }
551
+ return void 0;
552
+ }
553
+ function hasUnescapedBackslash(value) {
554
+ for (let index = 0; index < value.length; index += 1) {
555
+ if (value[index] !== "\\") {
556
+ continue;
557
+ }
558
+ let count = 1;
559
+ while (value[index + count] === "\\") {
560
+ count += 1;
561
+ }
562
+ if (count % 2 === 1) {
563
+ return true;
564
+ }
565
+ index += count - 1;
566
+ }
567
+ return false;
568
+ }
569
+
341
570
  // src/parser/sections.ts
342
571
  var SECTION_MAP = {
343
572
  "system instructions": "system_instructions",
@@ -378,6 +607,7 @@ function extractSections(body) {
378
607
 
379
608
  // src/parser/parser.ts
380
609
  function parsePrompt(content, filePath) {
610
+ assertRegexFrontMatterQuoting(content, filePath);
381
611
  const { data: frontMatter, content: body } = (0, import_gray_matter.default)(content);
382
612
  const sections = extractSections(body);
383
613
  const raw = {
@@ -958,6 +1188,21 @@ function withPromptInputSupport(adapter) {
958
1188
  };
959
1189
  }
960
1190
 
1191
+ // src/providers/raw.ts
1192
+ function applyRawProviderBody(body, asset, provider) {
1193
+ const raw = getRawProviderBody(asset, provider);
1194
+ return raw ? { ...body, ...raw } : body;
1195
+ }
1196
+ function getRawProviderBody(asset, provider) {
1197
+ if (provider === "openai-responses") {
1198
+ return asset.raw?.["openai-responses"] ?? asset.raw?.openai_responses;
1199
+ }
1200
+ if (provider === "gemini") {
1201
+ return asset.raw?.gemini ?? asset.raw?.google;
1202
+ }
1203
+ return asset.raw?.[provider];
1204
+ }
1205
+
961
1206
  // src/providers/openai.ts
962
1207
  var openaiAdapter = withPromptInputSupport({
963
1208
  name: "openai",
@@ -971,6 +1216,9 @@ var openaiAdapter = withPromptInputSupport({
971
1216
  if (resolvedAsset.reasoning?.budget_tokens !== void 0) {
972
1217
  warnings.push("OpenAI uses reasoning_effort, not budget_tokens. budget_tokens will be ignored.");
973
1218
  }
1219
+ if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
1220
+ warnings.push("OpenAI response.schema requires response.format: json. schema will still be applied as JSON schema output.");
1221
+ }
974
1222
  return { valid: errors.length === 0, errors, warnings };
975
1223
  },
976
1224
  render(asset, runtime) {
@@ -995,6 +1243,7 @@ var openaiAdapter = withPromptInputSupport({
995
1243
  model: resolvedAsset.model,
996
1244
  messages
997
1245
  };
1246
+ const openaiCacheConfig = resolvedAsset.cache?.openai;
998
1247
  if (resolvedAsset.sampling?.temperature !== void 0) body.temperature = resolvedAsset.sampling.temperature;
999
1248
  if (resolvedAsset.sampling?.top_p !== void 0) body.top_p = resolvedAsset.sampling.top_p;
1000
1249
  if (resolvedAsset.sampling?.frequency_penalty !== void 0) body.frequency_penalty = resolvedAsset.sampling.frequency_penalty;
@@ -1004,12 +1253,28 @@ var openaiAdapter = withPromptInputSupport({
1004
1253
  if (resolvedAsset.reasoning?.effort) {
1005
1254
  body.reasoning_effort = resolvedAsset.reasoning.effort;
1006
1255
  }
1007
- if (resolvedAsset.response?.format === "json") {
1256
+ if (resolvedAsset.response?.schema) {
1257
+ body.response_format = {
1258
+ type: "json_schema",
1259
+ json_schema: {
1260
+ name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,
1261
+ ...resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {},
1262
+ schema: resolvedAsset.response.schema,
1263
+ strict: resolvedAsset.response.schema_strict ?? true
1264
+ }
1265
+ };
1266
+ } else if (resolvedAsset.response?.format === "json") {
1008
1267
  body.response_format = { type: "json_object" };
1009
1268
  }
1010
1269
  if (resolvedAsset.response?.stream !== void 0) {
1011
1270
  body.stream = resolvedAsset.response.stream;
1012
1271
  }
1272
+ if (openaiCacheConfig?.prompt_cache_key) {
1273
+ body.prompt_cache_key = openaiCacheConfig.prompt_cache_key;
1274
+ }
1275
+ if (openaiCacheConfig?.retention) {
1276
+ body.prompt_cache_retention = openaiCacheConfig.retention;
1277
+ }
1013
1278
  if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
1014
1279
  body.tools = resolvedAsset.tools.map((tool) => {
1015
1280
  if (typeof tool === "string") {
@@ -1028,13 +1293,115 @@ var openaiAdapter = withPromptInputSupport({
1028
1293
  });
1029
1294
  }
1030
1295
  return {
1031
- body,
1296
+ body: applyRawProviderBody(body, resolvedAsset, "openai"),
1032
1297
  provider: "openai",
1033
1298
  model: resolvedAsset.model ?? "unknown"
1034
1299
  };
1035
1300
  }
1036
1301
  });
1037
1302
 
1303
+ // src/providers/openai-responses.ts
1304
+ var openaiResponsesAdapter = withPromptInputSupport({
1305
+ name: "openai-responses",
1306
+ validate(asset, runtime) {
1307
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
1308
+ const errors = [];
1309
+ const warnings = [];
1310
+ if (!resolvedAsset.model) {
1311
+ errors.push("OpenAI Responses adapter requires a model to be specified.");
1312
+ }
1313
+ if (resolvedAsset.reasoning?.budget_tokens !== void 0) {
1314
+ warnings.push("OpenAI Responses uses reasoning.effort, not budget_tokens. budget_tokens will be ignored.");
1315
+ }
1316
+ if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
1317
+ warnings.push("OpenAI Responses response.schema requires response.format: json. schema will still be applied as JSON schema output.");
1318
+ }
1319
+ if (runtime?.openaiResponses?.conversation !== void 0 && runtime?.openaiResponses?.previous_response_id !== void 0) {
1320
+ errors.push('OpenAI Responses options "conversation" and "previous_response_id" cannot both be set.');
1321
+ }
1322
+ return { valid: errors.length === 0, errors, warnings };
1323
+ },
1324
+ render(asset, runtime) {
1325
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
1326
+ const sections = renderSections(resolvedAsset, {
1327
+ variables: runtime.variables,
1328
+ strict: runtime.strict
1329
+ });
1330
+ const responseOptions = runtime.openaiResponses;
1331
+ const input = [];
1332
+ if (runtime.history) {
1333
+ for (const msg of runtime.history) {
1334
+ input.push({ role: msg.role, content: msg.content });
1335
+ }
1336
+ }
1337
+ if (sections.prompt_template) {
1338
+ input.push({ role: "user", content: sections.prompt_template });
1339
+ }
1340
+ const body = {
1341
+ model: resolvedAsset.model,
1342
+ input
1343
+ };
1344
+ if (responseOptions?.instructions !== void 0) {
1345
+ body.instructions = responseOptions.instructions;
1346
+ } else if (sections.system_instructions) {
1347
+ body.instructions = sections.system_instructions;
1348
+ }
1349
+ if (resolvedAsset.sampling?.temperature !== void 0) body.temperature = resolvedAsset.sampling.temperature;
1350
+ if (resolvedAsset.sampling?.top_p !== void 0) body.top_p = resolvedAsset.sampling.top_p;
1351
+ if (resolvedAsset.sampling?.frequency_penalty !== void 0) body.frequency_penalty = resolvedAsset.sampling.frequency_penalty;
1352
+ if (resolvedAsset.sampling?.presence_penalty !== void 0) body.presence_penalty = resolvedAsset.sampling.presence_penalty;
1353
+ if (resolvedAsset.sampling?.stop !== void 0) body.stop = resolvedAsset.sampling.stop;
1354
+ if (resolvedAsset.sampling?.max_output_tokens !== void 0) body.max_output_tokens = resolvedAsset.sampling.max_output_tokens;
1355
+ if (resolvedAsset.reasoning?.effort) {
1356
+ body.reasoning = { effort: resolvedAsset.reasoning.effort };
1357
+ }
1358
+ if (resolvedAsset.response?.schema) {
1359
+ body.text = {
1360
+ format: {
1361
+ type: "json_schema",
1362
+ name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,
1363
+ ...resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {},
1364
+ schema: resolvedAsset.response.schema,
1365
+ strict: resolvedAsset.response.schema_strict ?? true
1366
+ }
1367
+ };
1368
+ } else if (resolvedAsset.response?.format === "json") {
1369
+ body.text = { format: { type: "json_object" } };
1370
+ }
1371
+ if (resolvedAsset.response?.stream !== void 0) {
1372
+ body.stream = resolvedAsset.response.stream;
1373
+ }
1374
+ if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
1375
+ body.tools = resolvedAsset.tools.map((tool) => {
1376
+ if (typeof tool === "string") {
1377
+ const def = runtime.toolRegistry?.[tool];
1378
+ if (def) return def;
1379
+ return { type: "function", name: tool };
1380
+ }
1381
+ return {
1382
+ type: "function",
1383
+ name: tool.name,
1384
+ description: tool.description,
1385
+ parameters: tool.input_schema
1386
+ };
1387
+ });
1388
+ }
1389
+ if (responseOptions?.previous_response_id !== void 0) body.previous_response_id = responseOptions.previous_response_id;
1390
+ if (responseOptions?.conversation !== void 0) body.conversation = responseOptions.conversation;
1391
+ if (responseOptions?.parallel_tool_calls !== void 0) body.parallel_tool_calls = responseOptions.parallel_tool_calls;
1392
+ if (responseOptions?.max_tool_calls !== void 0) body.max_tool_calls = responseOptions.max_tool_calls;
1393
+ if (responseOptions?.store !== void 0) body.store = responseOptions.store;
1394
+ if (responseOptions?.metadata !== void 0) body.metadata = responseOptions.metadata;
1395
+ if (responseOptions?.include !== void 0) body.include = responseOptions.include;
1396
+ if (responseOptions?.background !== void 0) body.background = responseOptions.background;
1397
+ return {
1398
+ body: applyRawProviderBody(body, resolvedAsset, "openai-responses"),
1399
+ provider: "openai-responses",
1400
+ model: resolvedAsset.model ?? "unknown"
1401
+ };
1402
+ }
1403
+ });
1404
+
1038
1405
  // src/providers/anthropic.ts
1039
1406
  var anthropicAdapter = withPromptInputSupport({
1040
1407
  name: "anthropic",
@@ -1054,6 +1421,12 @@ var anthropicAdapter = withPromptInputSupport({
1054
1421
  if (resolvedAsset.reasoning?.effort !== void 0) {
1055
1422
  warnings.push("Anthropic uses budget_tokens for thinking, not effort. effort will be mapped approximately.");
1056
1423
  }
1424
+ if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
1425
+ warnings.push("Anthropic response.schema is mapped to output_config.format and should usually be paired with response.format: json.");
1426
+ }
1427
+ if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0 && resolvedAsset.provider_options.anthropic.top_k < 0) {
1428
+ errors.push("Anthropic provider_options.top_k must be >= 0.");
1429
+ }
1057
1430
  return { valid: errors.length === 0, errors, warnings };
1058
1431
  },
1059
1432
  render(asset, runtime) {
@@ -1063,20 +1436,38 @@ var anthropicAdapter = withPromptInputSupport({
1063
1436
  strict: runtime.strict
1064
1437
  });
1065
1438
  const messages = [];
1439
+ const anthropicCacheConfig = resolvedAsset.cache?.anthropic;
1440
+ const cacheType = anthropicCacheConfig?.type ?? "ephemeral";
1441
+ const cacheControl = anthropicCacheConfig ? {
1442
+ type: cacheType,
1443
+ ...anthropicCacheConfig.ttl ? { ttl: anthropicCacheConfig.ttl } : {}
1444
+ } : void 0;
1445
+ const cacheMode = anthropicCacheConfig?.mode ?? "automatic";
1066
1446
  if (runtime.history) {
1067
1447
  for (const msg of runtime.history) {
1068
1448
  messages.push({ role: msg.role, content: msg.content });
1069
1449
  }
1070
1450
  }
1071
1451
  if (sections.prompt_template) {
1072
- messages.push({ role: "user", content: sections.prompt_template });
1452
+ if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_prompt_template) {
1453
+ messages.push({
1454
+ role: "user",
1455
+ content: [{ type: "text", text: sections.prompt_template, cache_control: cacheControl }]
1456
+ });
1457
+ } else {
1458
+ messages.push({ role: "user", content: sections.prompt_template });
1459
+ }
1073
1460
  }
1074
1461
  const body = {
1075
1462
  model: resolvedAsset.model,
1076
1463
  messages
1077
1464
  };
1078
1465
  if (sections.system_instructions) {
1079
- body.system = sections.system_instructions;
1466
+ if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_system_instructions !== false) {
1467
+ body.system = [{ type: "text", text: sections.system_instructions, cache_control: cacheControl }];
1468
+ } else {
1469
+ body.system = sections.system_instructions;
1470
+ }
1080
1471
  }
1081
1472
  if (resolvedAsset.sampling?.temperature !== void 0) body.temperature = resolvedAsset.sampling.temperature;
1082
1473
  if (resolvedAsset.sampling?.top_p !== void 0) body.top_p = resolvedAsset.sampling.top_p;
@@ -1092,25 +1483,53 @@ var anthropicAdapter = withPromptInputSupport({
1092
1483
  budget_tokens: resolvedAsset.reasoning.budget_tokens
1093
1484
  };
1094
1485
  }
1486
+ if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0) {
1487
+ body.top_k = resolvedAsset.provider_options.anthropic.top_k;
1488
+ }
1489
+ if (resolvedAsset.provider_options?.anthropic?.output_config !== void 0) {
1490
+ body.output_config = resolvedAsset.provider_options.anthropic.output_config;
1491
+ } else if (resolvedAsset.response?.schema !== void 0) {
1492
+ body.output_config = {
1493
+ format: {
1494
+ type: "json_schema",
1495
+ schema: resolvedAsset.response.schema
1496
+ }
1497
+ };
1498
+ }
1095
1499
  if (resolvedAsset.response?.stream !== void 0) {
1096
1500
  body.stream = resolvedAsset.response.stream;
1097
1501
  }
1502
+ if (cacheControl && cacheMode === "automatic") {
1503
+ body.cache_control = cacheControl;
1504
+ }
1098
1505
  if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
1099
1506
  body.tools = resolvedAsset.tools.map((tool) => {
1100
1507
  if (typeof tool === "string") {
1101
1508
  const def = runtime.toolRegistry?.[tool];
1102
- if (def) return def;
1103
- return { name: tool };
1509
+ if (def) {
1510
+ if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools) {
1511
+ return { ...def, cache_control: cacheControl };
1512
+ }
1513
+ return def;
1514
+ }
1515
+ return {
1516
+ name: tool,
1517
+ ...cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools ? { cache_control: cacheControl } : {}
1518
+ };
1104
1519
  }
1105
1520
  return {
1106
1521
  name: tool.name,
1107
1522
  description: tool.description,
1108
- input_schema: tool.input_schema ?? { type: "object", properties: {} }
1523
+ input_schema: tool.input_schema ?? { type: "object", properties: {} },
1524
+ ...cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools ? { cache_control: cacheControl } : {}
1109
1525
  };
1110
1526
  });
1111
1527
  }
1528
+ if (resolvedAsset.provider_options?.anthropic?.tool_choice !== void 0) {
1529
+ body.tool_choice = resolvedAsset.provider_options.anthropic.tool_choice;
1530
+ }
1112
1531
  return {
1113
- body,
1532
+ body: applyRawProviderBody(body, resolvedAsset, "anthropic"),
1114
1533
  provider: "anthropic",
1115
1534
  model: resolvedAsset.model ?? "unknown"
1116
1535
  };
@@ -1124,6 +1543,8 @@ var geminiAdapter = withPromptInputSupport({
1124
1543
  const resolvedAsset = resolveAssetForProvider(asset, runtime);
1125
1544
  const errors = [];
1126
1545
  const warnings = [];
1546
+ const geminiCache = resolvedAsset.cache?.gemini?.cached_content;
1547
+ const googleCache = resolvedAsset.cache?.google?.cached_content;
1127
1548
  if (!resolvedAsset.model) {
1128
1549
  errors.push("Gemini adapter requires a model to be specified.");
1129
1550
  }
@@ -1133,6 +1554,12 @@ var geminiAdapter = withPromptInputSupport({
1133
1554
  if (resolvedAsset.sampling?.presence_penalty !== void 0) {
1134
1555
  warnings.push("Gemini does not support presence_penalty. It will be ignored.");
1135
1556
  }
1557
+ if (geminiCache && googleCache && geminiCache !== googleCache) {
1558
+ warnings.push("Both cache.gemini.cached_content and cache.google.cached_content are set. Gemini uses cache.gemini.cached_content.");
1559
+ }
1560
+ if (resolvedAsset.response?.stream !== void 0) {
1561
+ warnings.push("Gemini streaming is endpoint-based (streamGenerateContent), not body-based. response.stream will be ignored.");
1562
+ }
1136
1563
  return { valid: errors.length === 0, errors, warnings };
1137
1564
  },
1138
1565
  render(asset, runtime) {
@@ -1159,20 +1586,33 @@ var geminiAdapter = withPromptInputSupport({
1159
1586
  const body = {
1160
1587
  contents
1161
1588
  };
1589
+ const geminiCacheConfig = resolvedAsset.cache?.gemini ?? resolvedAsset.cache?.google;
1162
1590
  if (sections.system_instructions) {
1163
1591
  body.systemInstruction = {
1164
1592
  parts: [{ text: sections.system_instructions }]
1165
1593
  };
1166
1594
  }
1167
1595
  const generationConfig = {};
1596
+ const geminiOptions = resolvedAsset.provider_options?.gemini;
1168
1597
  if (resolvedAsset.sampling?.temperature !== void 0) generationConfig.temperature = resolvedAsset.sampling.temperature;
1169
1598
  if (resolvedAsset.sampling?.top_p !== void 0) generationConfig.topP = resolvedAsset.sampling.top_p;
1170
1599
  if (resolvedAsset.sampling?.max_output_tokens !== void 0) generationConfig.maxOutputTokens = resolvedAsset.sampling.max_output_tokens;
1171
1600
  if (resolvedAsset.sampling?.stop !== void 0) generationConfig.stopSequences = resolvedAsset.sampling.stop;
1601
+ if (geminiOptions?.candidate_count !== void 0) generationConfig.candidateCount = geminiOptions.candidate_count;
1602
+ if (geminiOptions?.top_k !== void 0) generationConfig.topK = geminiOptions.top_k;
1603
+ if (geminiOptions?.seed !== void 0) generationConfig.seed = geminiOptions.seed;
1604
+ if (geminiOptions?.response_modalities !== void 0) generationConfig.responseModalities = geminiOptions.response_modalities;
1605
+ if (resolvedAsset.response?.schema !== void 0) generationConfig.responseJsonSchema = resolvedAsset.response.schema;
1606
+ if (geminiOptions?.response_schema !== void 0) generationConfig.responseSchema = geminiOptions.response_schema;
1607
+ if (geminiOptions?.response_json_schema !== void 0) generationConfig.responseJsonSchema = geminiOptions.response_json_schema;
1172
1608
  if (resolvedAsset.response?.format === "json") {
1173
1609
  generationConfig.responseMimeType = "application/json";
1174
1610
  }
1175
- if (resolvedAsset.reasoning?.effort) {
1611
+ if (geminiOptions?.thinking_budget_tokens !== void 0) {
1612
+ body.thinkingConfig = {
1613
+ thinkingBudget: geminiOptions.thinking_budget_tokens
1614
+ };
1615
+ } else if (resolvedAsset.reasoning?.effort) {
1176
1616
  body.thinkingConfig = {
1177
1617
  thinkingBudget: resolvedAsset.reasoning.effort === "high" ? 8192 : resolvedAsset.reasoning.effort === "medium" ? 4096 : 1024
1178
1618
  };
@@ -1180,6 +1620,9 @@ var geminiAdapter = withPromptInputSupport({
1180
1620
  if (Object.keys(generationConfig).length > 0) {
1181
1621
  body.generationConfig = generationConfig;
1182
1622
  }
1623
+ if (geminiCacheConfig?.cached_content) {
1624
+ body.cachedContent = geminiCacheConfig.cached_content;
1625
+ }
1183
1626
  if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
1184
1627
  const functionDeclarations = resolvedAsset.tools.map((tool) => {
1185
1628
  if (typeof tool === "string") {
@@ -1196,7 +1639,7 @@ var geminiAdapter = withPromptInputSupport({
1196
1639
  body.tools = [{ functionDeclarations }];
1197
1640
  }
1198
1641
  return {
1199
- body,
1642
+ body: applyRawProviderBody(body, resolvedAsset, "gemini"),
1200
1643
  provider: "gemini",
1201
1644
  model: resolvedAsset.model ?? "unknown"
1202
1645
  };
@@ -1207,13 +1650,45 @@ var geminiAdapter = withPromptInputSupport({
1207
1650
  var openrouterAdapter = withPromptInputSupport({
1208
1651
  name: "openrouter",
1209
1652
  validate(asset, runtime) {
1210
- return openaiAdapter.validate(asset, runtime);
1653
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
1654
+ const validation = openaiAdapter.validate(asset, runtime);
1655
+ const hasOpenRouterModels = (resolvedAsset.provider_options?.openrouter?.models?.length ?? 0) > 0;
1656
+ if (!hasOpenRouterModels) {
1657
+ return validation;
1658
+ }
1659
+ const modelRequiredError = "OpenAI adapter requires a model to be specified.";
1660
+ const errors = validation.errors.filter((error) => error !== modelRequiredError);
1661
+ const warnings = validation.warnings.filter((warning) => warning !== modelRequiredError);
1662
+ return {
1663
+ valid: errors.length === 0,
1664
+ errors,
1665
+ warnings
1666
+ };
1211
1667
  },
1212
1668
  render(asset, runtime) {
1213
- const result = openaiAdapter.render(asset, runtime);
1669
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
1670
+ const result = openaiAdapter.render(
1671
+ { ...resolvedAsset, raw: void 0 },
1672
+ {
1673
+ variables: runtime.variables,
1674
+ history: runtime.history,
1675
+ toolRegistry: runtime.toolRegistry,
1676
+ strict: runtime.strict
1677
+ }
1678
+ );
1679
+ const openrouterOptions = resolvedAsset.provider_options?.openrouter;
1680
+ let body = { ...result.body };
1681
+ if (openrouterOptions?.provider !== void 0) body.provider = openrouterOptions.provider;
1682
+ if (openrouterOptions?.transforms !== void 0) body.transforms = openrouterOptions.transforms;
1683
+ if (openrouterOptions?.plugins !== void 0) body.plugins = openrouterOptions.plugins;
1684
+ if (openrouterOptions?.models !== void 0) body.models = openrouterOptions.models;
1685
+ if (body.model === void 0) delete body.model;
1686
+ body = applyRawProviderBody(body, resolvedAsset, "openrouter");
1214
1687
  return {
1215
1688
  ...result,
1216
- provider: "openrouter"
1689
+ body,
1690
+ provider: "openrouter",
1691
+ model: resolvedAsset.model ?? openrouterOptions?.models?.[0] ?? "unknown"
1217
1692
  };
1218
1693
  }
1219
1694
  });
@@ -1221,6 +1696,7 @@ var openrouterAdapter = withPromptInputSupport({
1221
1696
  // src/providers/index.ts
1222
1697
  var adapters = {
1223
1698
  openai: openaiAdapter,
1699
+ "openai-responses": openaiResponsesAdapter,
1224
1700
  anthropic: anthropicAdapter,
1225
1701
  google: geminiAdapter,
1226
1702
  gemini: geminiAdapter,
@@ -1275,8 +1751,22 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
1275
1751
  "includes",
1276
1752
  "environments",
1277
1753
  "tiers",
1278
- "metadata"
1754
+ "metadata",
1755
+ "cache",
1756
+ "provider_options",
1757
+ "raw"
1279
1758
  ]);
1759
+ var RISKY_UNBOUNDED_INPUT_NAMES = [
1760
+ "message",
1761
+ "prompt",
1762
+ "history",
1763
+ "transcript",
1764
+ "document",
1765
+ "content",
1766
+ "input",
1767
+ "body",
1768
+ "context"
1769
+ ];
1280
1770
  function validateAsset(asset, frontMatterKeys, filePath) {
1281
1771
  const errors = [];
1282
1772
  const warnings = [];
@@ -1347,7 +1837,32 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1347
1837
  });
1348
1838
  }
1349
1839
  }
1840
+ if (usedVars.size > 0 && (!asset.context?.inputs || asset.context.inputs.length === 0)) {
1841
+ warnings.push({
1842
+ code: "POK046",
1843
+ message: `Template uses ${usedVars.size === 1 ? "a variable" : "variables"} but context.inputs is not declared.`,
1844
+ filePath,
1845
+ suggestion: "Declare context.inputs to enable input policy validation."
1846
+ });
1847
+ }
1350
1848
  for (const input of getContextInputs(asset)) {
1849
+ const lowerName = input.name.toLowerCase();
1850
+ if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
1851
+ warnings.push({
1852
+ code: "POK040",
1853
+ message: `Context input "${input.name}" has no max_size and appears unbounded.`,
1854
+ filePath,
1855
+ suggestion: "Add max_size to constrain prompt payload growth."
1856
+ });
1857
+ }
1858
+ if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
1859
+ warnings.push({
1860
+ code: "POK041",
1861
+ message: `Context input "${input.name}" has no input hardening validators.`,
1862
+ filePath,
1863
+ suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
1864
+ });
1865
+ }
1351
1866
  if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1352
1867
  warnings.push({
1353
1868
  code: "POK014",
@@ -1375,6 +1890,91 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1375
1890
  }
1376
1891
  }
1377
1892
  }
1893
+ if (asset.provider) {
1894
+ let providerCache;
1895
+ let cacheSuggestionField;
1896
+ switch (asset.provider) {
1897
+ case "openai":
1898
+ providerCache = asset.cache?.openai;
1899
+ cacheSuggestionField = "cache.openai";
1900
+ break;
1901
+ case "anthropic":
1902
+ providerCache = asset.cache?.anthropic;
1903
+ cacheSuggestionField = "cache.anthropic";
1904
+ break;
1905
+ case "gemini":
1906
+ case "google":
1907
+ providerCache = asset.cache?.gemini ?? asset.cache?.google;
1908
+ cacheSuggestionField = "cache.gemini";
1909
+ break;
1910
+ default:
1911
+ break;
1912
+ }
1913
+ if (cacheSuggestionField && providerCache === void 0) {
1914
+ warnings.push({
1915
+ code: "POK042",
1916
+ message: `Provider "${asset.provider}" has no provider-specific cache settings.`,
1917
+ filePath,
1918
+ suggestion: `Consider configuring ${cacheSuggestionField} for better cache-hit behavior.`
1919
+ });
1920
+ }
1921
+ if (!asset.model) {
1922
+ warnings.push({
1923
+ code: "POK044",
1924
+ message: `Provider "${asset.provider}" is configured without a model.`,
1925
+ filePath,
1926
+ suggestion: "Set model in prompt or defaults to avoid adapter-time errors."
1927
+ });
1928
+ }
1929
+ }
1930
+ if (asset.cache?.gemini?.cached_content && asset.cache.google?.cached_content && asset.cache.gemini.cached_content !== asset.cache.google.cached_content) {
1931
+ warnings.push({
1932
+ code: "POK043",
1933
+ message: "cache.gemini.cached_content and cache.google.cached_content are both set to different values.",
1934
+ filePath,
1935
+ suggestion: "Use one canonical value; Gemini prefers cache.gemini.cached_content."
1936
+ });
1937
+ }
1938
+ for (const [envName, overrides] of Object.entries(asset.environments ?? {})) {
1939
+ if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
1940
+ warnings.push({
1941
+ code: "POK045",
1942
+ message: `Environment "${envName}" does not override cache while prompt-level cache is defined.`,
1943
+ filePath,
1944
+ suggestion: "Confirm cache strategy is intentionally shared across environments."
1945
+ });
1946
+ }
1947
+ }
1948
+ for (const [tierName, overrides] of Object.entries(asset.tiers ?? {})) {
1949
+ if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
1950
+ warnings.push({
1951
+ code: "POK045",
1952
+ message: `Tier "${tierName}" does not override cache while prompt-level cache is defined.`,
1953
+ filePath,
1954
+ suggestion: "Confirm cache strategy is intentionally shared across tiers."
1955
+ });
1956
+ }
1957
+ }
1958
+ for (const tool of asset.tools ?? []) {
1959
+ if (isRecord(tool)) {
1960
+ if (!tool.description) {
1961
+ warnings.push({
1962
+ code: "POK047",
1963
+ message: `Inline tool "${tool.name}" is missing a description.`,
1964
+ filePath,
1965
+ suggestion: "Add description to improve model tool-selection quality."
1966
+ });
1967
+ }
1968
+ if (!tool.input_schema) {
1969
+ warnings.push({
1970
+ code: "POK047",
1971
+ message: `Inline tool "${tool.name}" is missing input_schema.`,
1972
+ filePath,
1973
+ suggestion: "Add input_schema so tool inputs are strongly typed."
1974
+ });
1975
+ }
1976
+ }
1977
+ }
1378
1978
  return {
1379
1979
  valid: errors.length === 0,
1380
1980
  errors,
@@ -1411,6 +2011,9 @@ function findClosestMatch(input, candidates) {
1411
2011
  }
1412
2012
  return best;
1413
2013
  }
2014
+ function isRecord(value) {
2015
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2016
+ }
1414
2017
 
1415
2018
  // src/usagetap/client.ts
1416
2019
  var DEFAULT_BASE_URL = "https://api.usagetap.com";
@@ -1849,7 +2452,9 @@ var PromptOpsKit = class {
1849
2452
  throw new Error('Either "path" or "source" must be provided to renderPrompt()');
1850
2453
  }
1851
2454
  const adapter = getAdapter(options.provider);
1852
- const validation = adapter.validate(resolved);
2455
+ const validation = adapter.validate(resolved, {
2456
+ openaiResponses: options.openaiResponses
2457
+ });
1853
2458
  if (!validation.valid) {
1854
2459
  throw new Error(
1855
2460
  `Provider validation failed for "${options.provider}":
@@ -1879,7 +2484,8 @@ var PromptOpsKit = class {
1879
2484
  variables: sanitization.variables,
1880
2485
  history: options.history,
1881
2486
  toolRegistry: options.toolRegistry,
1882
- strict: options.strict
2487
+ strict: options.strict,
2488
+ openaiResponses: options.openaiResponses
1883
2489
  });
1884
2490
  return {
1885
2491
  resolved,
@@ -1891,9 +2497,21 @@ var PromptOpsKit = class {
1891
2497
  * Validate a prompt file.
1892
2498
  */
1893
2499
  async validatePrompt(promptPath) {
1894
- const asset = await this.loadPrompt(promptPath);
1895
2500
  const sourceFile = (0, import_node_path4.resolve)(this.config.sourceDir, promptPath + ".md");
1896
- return validateAssetWithIncludes(asset, sourceFile);
2501
+ try {
2502
+ const asset = await this.loadPrompt(promptPath);
2503
+ return validateAssetWithIncludes(asset, sourceFile);
2504
+ } catch (error) {
2505
+ const validationError = toPromptValidationError(error, sourceFile);
2506
+ if (validationError) {
2507
+ return {
2508
+ valid: false,
2509
+ errors: [validationError],
2510
+ warnings: []
2511
+ };
2512
+ }
2513
+ throw error;
2514
+ }
1897
2515
  }
1898
2516
  /**
1899
2517
  * Clear the internal cache.
@@ -1905,6 +2523,20 @@ var PromptOpsKit = class {
1905
2523
  function createPromptOpsKit(config = {}) {
1906
2524
  return new PromptOpsKit(config);
1907
2525
  }
2526
+ function toPromptValidationError(error, filePath) {
2527
+ if (!(error instanceof Error)) {
2528
+ return void 0;
2529
+ }
2530
+ const match = error.message.match(/^(POK\d+):\s*(.*)$/);
2531
+ if (!match) {
2532
+ return void 0;
2533
+ }
2534
+ return {
2535
+ code: match[1],
2536
+ message: match[2],
2537
+ filePath
2538
+ };
2539
+ }
1908
2540
  async function renderPrompt(options) {
1909
2541
  const kit = createPromptOpsKit({
1910
2542
  sourceDir: options.sourceDir ?? DEFAULT_PROMPTS_DIR,
@@ -1936,6 +2568,7 @@ async function renderPrompt(options) {
1936
2568
  interpolate,
1937
2569
  loadPromptFile,
1938
2570
  openaiAdapter,
2571
+ openaiResponsesAdapter,
1939
2572
  openrouterAdapter,
1940
2573
  parsePrompt,
1941
2574
  renderPrompt,