promptopskit 0.4.2 → 0.6.2

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 (82) hide show
  1. package/README.md +105 -62
  2. package/SKILL.md +376 -16
  3. package/dist/{chunk-7MRMN3SW.js → chunk-55B7QQBK.js} +6 -4
  4. package/dist/chunk-55B7QQBK.js.map +1 -0
  5. package/dist/{chunk-HWEVRBKZ.js → chunk-AGTGIZMR.js} +6 -2
  6. package/dist/chunk-AGTGIZMR.js.map +1 -0
  7. package/dist/chunk-FNCIBZJF.js +130 -0
  8. package/dist/chunk-FNCIBZJF.js.map +1 -0
  9. package/dist/{chunk-XL76NHZR.js → chunk-GLT3YYWK.js} +6 -4
  10. package/dist/chunk-GLT3YYWK.js.map +1 -0
  11. package/dist/{chunk-5AAYDOPU.js → chunk-IPFD5Y3O.js} +4 -3
  12. package/dist/chunk-IPFD5Y3O.js.map +1 -0
  13. package/dist/{chunk-YI4LILPY.js → chunk-JR7252DG.js} +584 -520
  14. package/dist/chunk-JR7252DG.js.map +1 -0
  15. package/dist/{chunk-JKBWC5WJ.js → chunk-UY5OKMVP.js} +302 -21
  16. package/dist/chunk-UY5OKMVP.js.map +1 -0
  17. package/dist/{chunk-MI3M6KDD.js → chunk-VMLGUI4F.js} +6 -4
  18. package/dist/chunk-VMLGUI4F.js.map +1 -0
  19. package/dist/{chunk-Z4IPWEUU.js → chunk-XEZJZ3IG.js} +6 -4
  20. package/dist/chunk-XEZJZ3IG.js.map +1 -0
  21. package/dist/cli/index.js +435 -106
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/index.cjs +1159 -636
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +10 -9
  26. package/dist/index.d.ts +10 -9
  27. package/dist/index.js +79 -17
  28. package/dist/index.js.map +1 -1
  29. package/dist/providers/anthropic.cjs +875 -533
  30. package/dist/providers/anthropic.cjs.map +1 -1
  31. package/dist/providers/anthropic.d.cts +2 -2
  32. package/dist/providers/anthropic.d.ts +2 -2
  33. package/dist/providers/anthropic.js +3 -3
  34. package/dist/providers/gemini.cjs +875 -533
  35. package/dist/providers/gemini.cjs.map +1 -1
  36. package/dist/providers/gemini.d.cts +2 -2
  37. package/dist/providers/gemini.d.ts +2 -2
  38. package/dist/providers/gemini.js +3 -3
  39. package/dist/providers/llmasaservice.cjs +1735 -0
  40. package/dist/providers/llmasaservice.cjs.map +1 -0
  41. package/dist/providers/llmasaservice.d.cts +28 -0
  42. package/dist/providers/llmasaservice.d.ts +28 -0
  43. package/dist/providers/llmasaservice.js +20 -0
  44. package/dist/providers/llmasaservice.js.map +1 -0
  45. package/dist/providers/openai-responses.cjs +875 -533
  46. package/dist/providers/openai-responses.cjs.map +1 -1
  47. package/dist/providers/openai-responses.d.cts +2 -2
  48. package/dist/providers/openai-responses.d.ts +2 -2
  49. package/dist/providers/openai-responses.js +3 -3
  50. package/dist/providers/openai.cjs +875 -533
  51. package/dist/providers/openai.cjs.map +1 -1
  52. package/dist/providers/openai.d.cts +2 -2
  53. package/dist/providers/openai.d.ts +2 -2
  54. package/dist/providers/openai.js +3 -3
  55. package/dist/providers/openrouter.cjs +876 -533
  56. package/dist/providers/openrouter.cjs.map +1 -1
  57. package/dist/providers/openrouter.d.cts +2 -2
  58. package/dist/providers/openrouter.d.ts +2 -2
  59. package/dist/providers/openrouter.js +4 -4
  60. package/dist/{schema-B9A1zLRk.d.cts → schema-CuYsv8JA.d.cts} +853 -7
  61. package/dist/{schema-B9A1zLRk.d.ts → schema-CuYsv8JA.d.ts} +853 -7
  62. package/dist/testing.cjs +64 -9
  63. package/dist/testing.cjs.map +1 -1
  64. package/dist/testing.d.cts +1 -1
  65. package/dist/testing.d.ts +1 -1
  66. package/dist/testing.js +1 -1
  67. package/dist/{types-DjGAwJxC.d.ts → types-DpK0mTHL.d.ts} +18 -6
  68. package/dist/{types-DpbzzZO2.d.cts → types-UA_p9uJy.d.cts} +18 -6
  69. package/dist/usagetap/index.cjs +6 -1
  70. package/dist/usagetap/index.cjs.map +1 -1
  71. package/dist/usagetap/index.d.cts +4 -3
  72. package/dist/usagetap/index.d.ts +4 -3
  73. package/dist/usagetap/index.js +3 -1
  74. package/package.json +19 -2
  75. package/dist/chunk-5AAYDOPU.js.map +0 -1
  76. package/dist/chunk-7MRMN3SW.js.map +0 -1
  77. package/dist/chunk-HWEVRBKZ.js.map +0 -1
  78. package/dist/chunk-JKBWC5WJ.js.map +0 -1
  79. package/dist/chunk-MI3M6KDD.js.map +0 -1
  80. package/dist/chunk-XL76NHZR.js.map +0 -1
  81. package/dist/chunk-YI4LILPY.js.map +0 -1
  82. package/dist/chunk-Z4IPWEUU.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -30,6 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ LLMASASERVICE_BASE_URL: () => LLMASASERVICE_BASE_URL,
34
+ LLMASASERVICE_DEFAULT_MODEL: () => LLMASASERVICE_DEFAULT_MODEL,
35
+ LLMASASERVICE_RESPONSE_HEADER_NAMES: () => LLMASASERVICE_RESPONSE_HEADER_NAMES,
36
+ LLMASASERVICE_SDK_PLACEHOLDER_API_KEY: () => LLMASASERVICE_SDK_PLACEHOLDER_API_KEY,
33
37
  PromptAssetOverridesSchema: () => PromptAssetOverridesSchema,
34
38
  PromptAssetSchema: () => PromptAssetSchema,
35
39
  PromptOpsKit: () => PromptOpsKit,
@@ -37,6 +41,7 @@ __export(src_exports, {
37
41
  applyOverrides: () => applyOverrides,
38
42
  applyUsageTapEntitlements: () => applyUsageTapEntitlements,
39
43
  beginUsageTapCall: () => beginUsageTapCall,
44
+ createLLMAsAServiceOpenAIConfig: () => createLLMAsAServiceOpenAIConfig,
40
45
  createPromptOpsKit: () => createPromptOpsKit,
41
46
  createUsageTapClient: () => createUsageTapClient,
42
47
  defaultUsageTapErrorMapper: () => defaultUsageTapErrorMapper,
@@ -49,6 +54,7 @@ __export(src_exports, {
49
54
  geminiAdapter: () => geminiAdapter,
50
55
  getAdapter: () => getAdapter,
51
56
  interpolate: () => interpolate,
57
+ llmasaserviceAdapter: () => llmasaserviceAdapter,
52
58
  loadPromptFile: () => loadPromptFile,
53
59
  openaiAdapter: () => openaiAdapter,
54
60
  openaiResponsesAdapter: () => openaiResponsesAdapter,
@@ -58,6 +64,7 @@ __export(src_exports, {
58
64
  resolveIncludes: () => resolveIncludes,
59
65
  runAnthropicWithUsageTap: () => runAnthropicWithUsageTap,
60
66
  runGeminiWithUsageTap: () => runGeminiWithUsageTap,
67
+ runLLMAsAServiceWithUsageTap: () => runLLMAsAServiceWithUsageTap,
61
68
  runOpenAIWithUsageTap: () => runOpenAIWithUsageTap,
62
69
  runOpenRouterWithUsageTap: () => runOpenRouterWithUsageTap,
63
70
  validateAsset: () => validateAsset,
@@ -65,7 +72,7 @@ __export(src_exports, {
65
72
  withUsageTapCall: () => withUsageTapCall
66
73
  });
67
74
  module.exports = __toCommonJS(src_exports);
68
- var import_node_path4 = require("path");
75
+ var import_node_path5 = require("path");
69
76
 
70
77
  // src/renderer/interpolate.ts
71
78
  var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
@@ -73,12 +80,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
73
80
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
74
81
  function interpolate(template, variables, options = {}) {
75
82
  const { strict = false } = options;
83
+ const optionalVariables = new Set(options.optionalVariables ?? []);
76
84
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
77
85
  result = result.replace(VARIABLE_RE, (match, name) => {
78
86
  if (name in variables) {
79
87
  return variables[name];
80
88
  }
81
- if (strict) {
89
+ if (strict && !optionalVariables.has(name)) {
82
90
  throw new Error(`Missing required variable: "${name}"`);
83
91
  }
84
92
  return match;
@@ -96,160 +104,469 @@ function extractVariables(template) {
96
104
  return [...vars];
97
105
  }
98
106
 
99
- // src/renderer/renderer.ts
100
- function renderSections(asset, options = {}) {
101
- const { variables = {}, strict = false } = options;
102
- const result = {};
103
- if (asset.sections.system_instructions) {
104
- result.system_instructions = interpolate(
105
- asset.sections.system_instructions,
106
- variables,
107
- { strict }
108
- );
109
- }
110
- if (asset.sections.prompt_template) {
111
- result.prompt_template = interpolate(
112
- asset.sections.prompt_template,
113
- variables,
114
- { strict }
115
- );
116
- }
117
- return result;
107
+ // src/context.ts
108
+ var textEncoder = new TextEncoder();
109
+ var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
110
+ var REJECT_SECRETS_FLAGS = "i";
111
+ function getContextInputs(asset) {
112
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
118
113
  }
119
-
120
- // src/overrides/apply-overrides.ts
121
- function applyOverrides(asset, options = {}) {
122
- let result = { ...asset };
123
- if (options.environment && result.environments?.[options.environment]) {
124
- result = mergeOverride(result, result.environments[options.environment]);
114
+ function normalizeContextInput(input) {
115
+ if (typeof input === "string") {
116
+ return { name: input };
125
117
  }
126
- if (options.tier && result.tiers?.[options.tier]) {
127
- result = mergeOverride(result, result.tiers[options.tier]);
118
+ return {
119
+ name: input.name,
120
+ optional: input.optional,
121
+ warnings: input.warnings,
122
+ max_size: input.max_size,
123
+ trim: input.trim,
124
+ allow_regex: normalizeContextRegex(input.allow_regex),
125
+ deny_regex: normalizeContextRegex(input.deny_regex),
126
+ non_empty: normalizeBuiltInValidator(input.non_empty),
127
+ reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
128
+ };
129
+ }
130
+ function areContextInputWarningsEnabled(input) {
131
+ return input.warnings !== false;
132
+ }
133
+ function normalizeContextRegex(value) {
134
+ if (value === void 0) {
135
+ return void 0;
128
136
  }
129
- if (options.runtime) {
130
- result = mergeOverride(result, options.runtime);
137
+ if (typeof value === "string") {
138
+ const literal = parseRegexLiteral(value);
139
+ if (value.startsWith("/") && !literal) {
140
+ return {
141
+ pattern: value,
142
+ flags: "",
143
+ raw: value,
144
+ invalidLiteral: true
145
+ };
146
+ }
147
+ return {
148
+ pattern: literal?.pattern ?? value,
149
+ flags: literal?.flags ?? "",
150
+ raw: value
151
+ };
131
152
  }
132
- return result;
153
+ return {
154
+ pattern: value.pattern,
155
+ flags: value.flags ?? "",
156
+ raw: JSON.stringify(value),
157
+ returnMessage: value.return_message
158
+ };
133
159
  }
134
- function mergeOverride(base, override) {
135
- const result = { ...base };
136
- if (override.model !== void 0) result.model = override.model;
137
- if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
138
- if (override.tools !== void 0) result.tools = override.tools;
139
- if (override.reasoning !== void 0) {
140
- result.reasoning = { ...result.reasoning, ...override.reasoning };
160
+ function normalizeBuiltInValidator(value) {
161
+ if (value === void 0 || value === false) {
162
+ return void 0;
141
163
  }
142
- if (override.sampling !== void 0) {
143
- result.sampling = { ...result.sampling, ...override.sampling };
164
+ if (value === true) {
165
+ return {};
144
166
  }
145
- if (override.response !== void 0) {
146
- result.response = { ...result.response, ...override.response };
167
+ return {
168
+ returnMessage: value.return_message
169
+ };
170
+ }
171
+ function parseRegexLiteral(value) {
172
+ if (!value.startsWith("/")) {
173
+ return void 0;
147
174
  }
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)
175
+ for (let index = value.length - 1; index > 0; index -= 1) {
176
+ if (value[index] !== "/") {
177
+ continue;
178
+ }
179
+ let backslashCount = 0;
180
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
181
+ backslashCount += 1;
182
+ }
183
+ if (backslashCount % 2 === 1) {
184
+ continue;
185
+ }
186
+ return {
187
+ pattern: value.slice(1, index),
188
+ flags: value.slice(index + 1)
156
189
  };
157
190
  }
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
- };
191
+ return void 0;
192
+ }
193
+ function formatInvalidContextRegexMessage(details) {
194
+ return [
195
+ `Invalid context regex for prompt "${details.promptId}"`,
196
+ `variable "${details.variable}"`,
197
+ `field "${details.field}"`,
198
+ `value ${JSON.stringify(details.raw)}: ${details.reason}`
199
+ ].join(", ");
200
+ }
201
+ function compileContextRegex(regex, details) {
202
+ if (regex.invalidLiteral) {
203
+ throw new Error(
204
+ `POK013: ${formatInvalidContextRegexMessage({
205
+ ...details,
206
+ raw: regex.raw,
207
+ reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
208
+ })}`
209
+ );
170
210
  }
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
- };
211
+ try {
212
+ return new RegExp(regex.pattern, regex.flags);
213
+ } catch (error) {
214
+ const reason = error instanceof Error ? error.message : String(error);
215
+ throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
188
216
  }
189
- return result;
190
217
  }
191
- function mergeRecordBlock(base, override) {
192
- if (override === void 0) return base;
193
- return { ...base, ...override };
218
+ function getRejectSecretsRegex() {
219
+ return {
220
+ pattern: REJECT_SECRETS_PATTERN,
221
+ flags: REJECT_SECRETS_FLAGS,
222
+ raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
223
+ };
194
224
  }
195
-
196
- // src/providers/resolve-asset.ts
197
- function resolveAssetForProvider(asset, runtime = {}) {
198
- if (!runtime.environment && !runtime.tier && !runtime.runtime) {
199
- return asset;
200
- }
201
- return applyOverrides(asset, {
202
- environment: runtime.environment,
203
- tier: runtime.tier,
204
- runtime: runtime.runtime
205
- });
225
+ function isTrimEnabled(mode) {
226
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
206
227
  }
207
-
208
- // src/prompt-resolution.ts
209
- var import_promises3 = require("fs/promises");
210
- var import_node_fs2 = require("fs");
211
- var import_node_path3 = require("path");
212
-
213
- // src/cache.ts
214
- var import_node_fs = require("fs");
215
- var PromptCache = class {
216
- cache = /* @__PURE__ */ new Map();
217
- maxSize;
218
- constructor(maxSize = 100) {
219
- this.maxSize = maxSize;
228
+ function normalizeTrimMode(mode) {
229
+ if (mode === "start") {
230
+ return "start";
220
231
  }
221
- get(filePath) {
222
- const entry = this.cache.get(filePath);
223
- if (!entry) return void 0;
224
- try {
225
- const stat = (0, import_node_fs.statSync)(filePath);
226
- if (stat.mtimeMs !== entry.mtime) {
227
- this.cache.delete(filePath);
228
- return void 0;
229
- }
230
- } catch {
231
- this.cache.delete(filePath);
232
- return void 0;
233
- }
234
- this.cache.delete(filePath);
235
- this.cache.set(filePath, entry);
236
- return entry.value;
232
+ return "end";
233
+ }
234
+ function trimToMaxSize(value, maxSize, mode) {
235
+ const measured = measureContextValueSize(value);
236
+ if (measured <= maxSize) {
237
+ return value;
237
238
  }
238
- set(filePath, value) {
239
- try {
240
- const stat = (0, import_node_fs.statSync)(filePath);
241
- if (this.cache.has(filePath)) {
242
- this.cache.delete(filePath);
243
- } else if (this.cache.size >= this.maxSize) {
244
- const oldest = this.cache.keys().next().value;
245
- if (oldest) this.cache.delete(oldest);
239
+ const characters = Array.from(value);
240
+ const normalizedMode = normalizeTrimMode(mode);
241
+ if (normalizedMode === "start") {
242
+ let collected2 = "";
243
+ let size2 = 0;
244
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
245
+ const next = characters[i];
246
+ const charSize = measureContextValueSize(next);
247
+ if (size2 + charSize > maxSize) {
248
+ break;
246
249
  }
247
- this.cache.set(filePath, { value, mtime: stat.mtimeMs });
248
- } catch {
250
+ collected2 = next + collected2;
251
+ size2 += charSize;
249
252
  }
253
+ return collected2;
250
254
  }
251
- clear() {
252
- this.cache.clear();
255
+ let collected = "";
256
+ let size = 0;
257
+ for (const char of characters) {
258
+ const charSize = measureContextValueSize(char);
259
+ if (size + charSize > maxSize) {
260
+ break;
261
+ }
262
+ collected += char;
263
+ size += charSize;
264
+ }
265
+ return collected;
266
+ }
267
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
268
+ const { onContextOverflow } = options;
269
+ const sanitized = { ...variables };
270
+ for (const input of getContextInputs(asset)) {
271
+ const value = sanitized[input.name];
272
+ if (value === void 0) {
273
+ continue;
274
+ }
275
+ let candidate = value;
276
+ if (input.max_size !== void 0) {
277
+ const actualSize = measureContextValueSize(candidate);
278
+ if (actualSize > input.max_size && onContextOverflow) {
279
+ candidate = onContextOverflow({
280
+ promptId: asset.id,
281
+ variable: input.name,
282
+ value: candidate,
283
+ maxSize: input.max_size,
284
+ actualSize
285
+ });
286
+ }
287
+ }
288
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
289
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
290
+ }
291
+ sanitized[input.name] = candidate;
292
+ if (input.allow_regex) {
293
+ const candidate2 = sanitized[input.name];
294
+ const matcher = compileContextRegex(input.allow_regex, {
295
+ promptId: asset.id,
296
+ variable: input.name,
297
+ field: "allow_regex"
298
+ });
299
+ if (!matcher.test(candidate2)) {
300
+ if (input.allow_regex.returnMessage) {
301
+ return {
302
+ variables: sanitized,
303
+ shortCircuit: {
304
+ returnMessage: input.allow_regex.returnMessage,
305
+ code: "POK031",
306
+ variable: input.name,
307
+ field: "allow_regex"
308
+ }
309
+ };
310
+ }
311
+ throw new Error(
312
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
313
+ );
314
+ }
315
+ }
316
+ if (input.deny_regex) {
317
+ const candidate2 = sanitized[input.name];
318
+ const matcher = compileContextRegex(input.deny_regex, {
319
+ promptId: asset.id,
320
+ variable: input.name,
321
+ field: "deny_regex"
322
+ });
323
+ if (matcher.test(candidate2)) {
324
+ if (input.deny_regex.returnMessage) {
325
+ return {
326
+ variables: sanitized,
327
+ shortCircuit: {
328
+ returnMessage: input.deny_regex.returnMessage,
329
+ code: "POK032",
330
+ variable: input.name,
331
+ field: "deny_regex"
332
+ }
333
+ };
334
+ }
335
+ throw new Error(
336
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
337
+ );
338
+ }
339
+ }
340
+ if (input.non_empty && candidate.trim().length === 0) {
341
+ if (input.non_empty.returnMessage) {
342
+ return {
343
+ variables: sanitized,
344
+ shortCircuit: {
345
+ returnMessage: input.non_empty.returnMessage,
346
+ code: "POK033",
347
+ variable: input.name,
348
+ field: "non_empty"
349
+ }
350
+ };
351
+ }
352
+ throw new Error(
353
+ `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
354
+ );
355
+ }
356
+ if (input.reject_secrets) {
357
+ const matcher = compileContextRegex(getRejectSecretsRegex(), {
358
+ promptId: asset.id,
359
+ variable: input.name,
360
+ field: "reject_secrets"
361
+ });
362
+ if (matcher.test(candidate)) {
363
+ if (input.reject_secrets.returnMessage) {
364
+ return {
365
+ variables: sanitized,
366
+ shortCircuit: {
367
+ returnMessage: input.reject_secrets.returnMessage,
368
+ code: "POK034",
369
+ variable: input.name,
370
+ field: "reject_secrets"
371
+ }
372
+ };
373
+ }
374
+ throw new Error(
375
+ `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
376
+ );
377
+ }
378
+ }
379
+ }
380
+ return { variables: sanitized };
381
+ }
382
+ function measureContextValueSize(value) {
383
+ return textEncoder.encode(value).length;
384
+ }
385
+ function collectContextSizeWarnings(asset, variables = {}) {
386
+ const warnings = [];
387
+ for (const input of getContextInputs(asset)) {
388
+ if (!areContextInputWarningsEnabled(input)) {
389
+ continue;
390
+ }
391
+ if (input.max_size === void 0) {
392
+ continue;
393
+ }
394
+ const value = variables[input.name];
395
+ if (value === void 0) {
396
+ continue;
397
+ }
398
+ const actualSize = measureContextValueSize(value);
399
+ if (actualSize > input.max_size) {
400
+ warnings.push({
401
+ variable: input.name,
402
+ maxSize: input.max_size,
403
+ actualSize
404
+ });
405
+ }
406
+ }
407
+ return warnings;
408
+ }
409
+
410
+ // src/renderer/renderer.ts
411
+ function renderSections(asset, options = {}) {
412
+ const { variables = {}, strict = false } = options;
413
+ const optionalVariables = getContextInputs(asset).filter((input) => input.optional === true).map((input) => input.name);
414
+ const result = {};
415
+ if (asset.sections.system_instructions) {
416
+ result.system_instructions = interpolate(
417
+ asset.sections.system_instructions,
418
+ variables,
419
+ { strict, optionalVariables }
420
+ );
421
+ }
422
+ if (asset.sections.prompt_template) {
423
+ result.prompt_template = interpolate(
424
+ asset.sections.prompt_template,
425
+ variables,
426
+ { strict, optionalVariables }
427
+ );
428
+ }
429
+ return result;
430
+ }
431
+
432
+ // src/overrides/apply-overrides.ts
433
+ function applyOverrides(asset, options = {}) {
434
+ let result = { ...asset };
435
+ if (options.environment && result.environments?.[options.environment]) {
436
+ result = mergeOverride(result, result.environments[options.environment]);
437
+ }
438
+ if (options.tier && result.tiers?.[options.tier]) {
439
+ result = mergeOverride(result, result.tiers[options.tier]);
440
+ }
441
+ if (options.runtime) {
442
+ result = mergeOverride(result, options.runtime);
443
+ }
444
+ return result;
445
+ }
446
+ function mergeOverride(base, override) {
447
+ const result = { ...base };
448
+ if (override.model !== void 0) result.model = override.model;
449
+ if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
450
+ if (override.tools !== void 0) result.tools = override.tools;
451
+ if (override.reasoning !== void 0) {
452
+ result.reasoning = { ...result.reasoning, ...override.reasoning };
453
+ }
454
+ if (override.sampling !== void 0) {
455
+ result.sampling = { ...result.sampling, ...override.sampling };
456
+ }
457
+ if (override.response !== void 0) {
458
+ result.response = { ...result.response, ...override.response };
459
+ }
460
+ if (override.cache !== void 0) {
461
+ result.cache = {
462
+ ...result.cache,
463
+ ...override.cache,
464
+ openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
465
+ anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
466
+ gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
467
+ google: mergeRecordBlock(result.cache?.google, override.cache.google)
468
+ };
469
+ }
470
+ if (override.raw !== void 0) {
471
+ result.raw = {
472
+ ...result.raw,
473
+ ...override.raw,
474
+ openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
475
+ "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
476
+ openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
477
+ anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
478
+ gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
479
+ google: mergeRecordBlock(result.raw?.google, override.raw.google),
480
+ openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter),
481
+ llmasaservice: mergeRecordBlock(result.raw?.llmasaservice, override.raw.llmasaservice)
482
+ };
483
+ }
484
+ if (override.provider_options !== void 0) {
485
+ result.provider_options = {
486
+ ...result.provider_options,
487
+ ...override.provider_options,
488
+ anthropic: {
489
+ ...result.provider_options?.anthropic,
490
+ ...override.provider_options.anthropic
491
+ },
492
+ gemini: {
493
+ ...result.provider_options?.gemini,
494
+ ...override.provider_options.gemini
495
+ },
496
+ openrouter: {
497
+ ...result.provider_options?.openrouter,
498
+ ...override.provider_options.openrouter
499
+ },
500
+ llmasaservice: {
501
+ ...result.provider_options?.llmasaservice,
502
+ ...override.provider_options.llmasaservice
503
+ }
504
+ };
505
+ }
506
+ return result;
507
+ }
508
+ function mergeRecordBlock(base, override) {
509
+ if (override === void 0) return base;
510
+ return { ...base, ...override };
511
+ }
512
+
513
+ // src/providers/resolve-asset.ts
514
+ function resolveAssetForProvider(asset, runtime = {}) {
515
+ if (!runtime.environment && !runtime.tier && !runtime.runtime) {
516
+ return asset;
517
+ }
518
+ return applyOverrides(asset, {
519
+ environment: runtime.environment,
520
+ tier: runtime.tier,
521
+ runtime: runtime.runtime
522
+ });
523
+ }
524
+
525
+ // src/prompt-resolution.ts
526
+ var import_promises4 = require("fs/promises");
527
+ var import_node_fs2 = require("fs");
528
+ var import_node_path4 = require("path");
529
+
530
+ // src/cache.ts
531
+ var import_node_fs = require("fs");
532
+ var PromptCache = class {
533
+ cache = /* @__PURE__ */ new Map();
534
+ maxSize;
535
+ constructor(maxSize = 100) {
536
+ this.maxSize = maxSize;
537
+ }
538
+ get(filePath) {
539
+ const entry = this.cache.get(filePath);
540
+ if (!entry) return void 0;
541
+ try {
542
+ const stat = (0, import_node_fs.statSync)(filePath);
543
+ if (stat.mtimeMs !== entry.mtime) {
544
+ this.cache.delete(filePath);
545
+ return void 0;
546
+ }
547
+ } catch {
548
+ this.cache.delete(filePath);
549
+ return void 0;
550
+ }
551
+ this.cache.delete(filePath);
552
+ this.cache.set(filePath, entry);
553
+ return entry.value;
554
+ }
555
+ set(filePath, value) {
556
+ try {
557
+ const stat = (0, import_node_fs.statSync)(filePath);
558
+ if (this.cache.has(filePath)) {
559
+ this.cache.delete(filePath);
560
+ } else if (this.cache.size >= this.maxSize) {
561
+ const oldest = this.cache.keys().next().value;
562
+ if (oldest) this.cache.delete(oldest);
563
+ }
564
+ this.cache.set(filePath, { value, mtime: stat.mtimeMs });
565
+ } catch {
566
+ }
567
+ }
568
+ clear() {
569
+ this.cache.clear();
253
570
  }
254
571
  get size() {
255
572
  return this.cache.size;
@@ -257,8 +574,8 @@ var PromptCache = class {
257
574
  };
258
575
 
259
576
  // src/composition/resolve-includes.ts
260
- var import_promises2 = require("fs/promises");
261
- var import_node_path2 = require("path");
577
+ var import_promises3 = require("fs/promises");
578
+ var import_node_path3 = require("path");
262
579
 
263
580
  // src/parser/parser.ts
264
581
  var import_gray_matter = __toESM(require("gray-matter"), 1);
@@ -294,10 +611,26 @@ var ResponseSchema = import_zod.z.object({
294
611
  format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
295
612
  stream: import_zod.z.boolean().optional(),
296
613
  schema: import_zod.z.record(import_zod.z.unknown()).optional(),
614
+ schema_ref: import_zod.z.string().min(1).optional(),
615
+ schema_source: import_zod.z.object({
616
+ mode: import_zod.z.enum(["inline", "schema_ref_json", "schema_ref_zod_module"]).optional(),
617
+ ref: import_zod.z.string().optional(),
618
+ resolved_path: import_zod.z.string().optional(),
619
+ hash: import_zod.z.string().optional()
620
+ }).optional(),
297
621
  schema_name: import_zod.z.string().optional(),
298
622
  schema_description: import_zod.z.string().optional(),
299
623
  schema_strict: import_zod.z.boolean().optional()
300
624
  });
625
+ var ResponseSchemaWithValidation = ResponseSchema.superRefine((value, ctx) => {
626
+ if (value.schema !== void 0 && value.schema_ref !== void 0) {
627
+ ctx.addIssue({
628
+ code: import_zod.z.ZodIssueCode.custom,
629
+ message: "response.schema and response.schema_ref are mutually exclusive",
630
+ path: ["schema_ref"]
631
+ });
632
+ }
633
+ });
301
634
  var AnthropicProviderOptionsSchema = import_zod.z.object({
302
635
  top_k: import_zod.z.number().int().min(0).optional(),
303
636
  tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
@@ -318,10 +651,26 @@ var OpenRouterProviderOptionsSchema = import_zod.z.object({
318
651
  plugins: import_zod.z.array(import_zod.z.record(import_zod.z.unknown())).optional(),
319
652
  models: import_zod.z.array(import_zod.z.string()).optional()
320
653
  });
654
+ var LLMAsAServiceCustomerSchema = import_zod.z.object({
655
+ customer_id: import_zod.z.string(),
656
+ customer_name: import_zod.z.string().optional(),
657
+ customer_user_id: import_zod.z.string().optional(),
658
+ customer_user_name: import_zod.z.string().optional(),
659
+ customer_user_email: import_zod.z.string().optional()
660
+ });
661
+ var LLMAsAServiceProviderOptionsSchema = import_zod.z.object({
662
+ base_url: import_zod.z.string().url().optional(),
663
+ project_id: import_zod.z.string().optional(),
664
+ customer: LLMAsAServiceCustomerSchema.optional(),
665
+ conversationId: import_zod.z.string().optional(),
666
+ conversationTitle: import_zod.z.string().optional(),
667
+ projectId: import_zod.z.string().optional()
668
+ });
321
669
  var ProviderOptionsSchema = import_zod.z.object({
322
670
  anthropic: AnthropicProviderOptionsSchema.optional(),
323
671
  gemini: GeminiProviderOptionsSchema.optional(),
324
- openrouter: OpenRouterProviderOptionsSchema.optional()
672
+ openrouter: OpenRouterProviderOptionsSchema.optional(),
673
+ llmasaservice: LLMAsAServiceProviderOptionsSchema.optional()
325
674
  });
326
675
  var RawProviderBodySchema = import_zod.z.object({
327
676
  openai: import_zod.z.record(import_zod.z.unknown()).optional(),
@@ -330,7 +679,8 @@ var RawProviderBodySchema = import_zod.z.object({
330
679
  anthropic: import_zod.z.record(import_zod.z.unknown()).optional(),
331
680
  gemini: import_zod.z.record(import_zod.z.unknown()).optional(),
332
681
  google: import_zod.z.record(import_zod.z.unknown()).optional(),
333
- openrouter: import_zod.z.record(import_zod.z.unknown()).optional()
682
+ openrouter: import_zod.z.record(import_zod.z.unknown()).optional(),
683
+ llmasaservice: import_zod.z.record(import_zod.z.unknown()).optional()
334
684
  });
335
685
  var OpenAICacheSchema = import_zod.z.object({
336
686
  prompt_cache_key: import_zod.z.string().min(1).optional(),
@@ -372,6 +722,8 @@ var ContextBuiltInValidatorSchema = import_zod.z.union([
372
722
  ]);
373
723
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
374
724
  name: import_zod.z.string(),
725
+ optional: import_zod.z.boolean().optional(),
726
+ warnings: import_zod.z.boolean().optional(),
375
727
  max_size: import_zod.z.number().int().positive().optional(),
376
728
  trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
377
729
  allow_regex: ContextRegexSchema.optional(),
@@ -401,7 +753,7 @@ var PromptAssetOverridesSchema = import_zod.z.object({
401
753
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
402
754
  reasoning: ReasoningSchema.optional(),
403
755
  sampling: SamplingSchema.optional(),
404
- response: ResponseSchema.optional(),
756
+ response: ResponseSchemaWithValidation.optional(),
405
757
  cache: CacheSchema.optional(),
406
758
  raw: RawProviderBodySchema.optional(),
407
759
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -417,9 +769,21 @@ var SectionsSchema = import_zod.z.object({
417
769
  notes: import_zod.z.string().optional()
418
770
  });
419
771
  var PromptDefaultsSchema = import_zod.z.object({
420
- provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
772
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
421
773
  model: import_zod.z.string().optional(),
774
+ fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
775
+ reasoning: ReasoningSchema.optional(),
776
+ sampling: SamplingSchema.optional(),
777
+ response: ResponseSchema.optional(),
422
778
  cache: CacheSchema.optional(),
779
+ raw: RawProviderBodySchema.optional(),
780
+ tools: import_zod.z.array(ToolRefSchema).optional(),
781
+ provider_options: ProviderOptionsSchema.optional(),
782
+ mcp: MCPSchema.optional(),
783
+ context: ContextSchema.optional(),
784
+ includes: import_zod.z.array(import_zod.z.string()).optional(),
785
+ environments: import_zod.z.record(PromptAssetOverridesSchema).optional(),
786
+ tiers: import_zod.z.record(PromptAssetOverridesSchema).optional(),
423
787
  metadata: MetadataSchema.optional(),
424
788
  sections: import_zod.z.object({
425
789
  system_instructions: import_zod.z.string().optional()
@@ -429,12 +793,12 @@ var PromptAssetSchema = import_zod.z.object({
429
793
  id: import_zod.z.string(),
430
794
  schema_version: import_zod.z.number().int().positive().default(1),
431
795
  description: import_zod.z.string().optional(),
432
- provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
796
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
433
797
  model: import_zod.z.string().optional(),
434
798
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
435
799
  reasoning: ReasoningSchema.optional(),
436
800
  sampling: SamplingSchema.optional(),
437
- response: ResponseSchema.optional(),
801
+ response: ResponseSchemaWithValidation.optional(),
438
802
  cache: CacheSchema.optional(),
439
803
  raw: RawProviderBodySchema.optional(),
440
804
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -626,16 +990,122 @@ function parsePrompt(content, filePath) {
626
990
  }
627
991
 
628
992
  // src/parser/loader.ts
993
+ var import_promises2 = require("fs/promises");
994
+ var import_node_path2 = require("path");
995
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
996
+
997
+ // src/parser/response-schema-ref.ts
629
998
  var import_promises = require("fs/promises");
999
+ var import_node_crypto = require("crypto");
630
1000
  var import_node_path = require("path");
631
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
1001
+ var import_node_url = require("url");
1002
+ var import_zod2 = require("zod");
1003
+ var import_zod_to_json_schema = require("zod-to-json-schema");
1004
+ var SUPPORTED_SCHEMA_REF_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
1005
+ async function resolveJsonSchemaRef(schemaRef, promptFilePath) {
1006
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
1007
+ let raw;
1008
+ try {
1009
+ raw = await (0, import_promises.readFile)(resolvedPath, "utf-8");
1010
+ } catch (error) {
1011
+ const code = error.code;
1012
+ if (code === "ENOENT") {
1013
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
1014
+ }
1015
+ throw error;
1016
+ }
1017
+ let parsed;
1018
+ try {
1019
+ parsed = JSON.parse(raw);
1020
+ } catch {
1021
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" is not valid JSON`);
1022
+ }
1023
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1024
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" must resolve to a JSON object schema`);
1025
+ }
1026
+ return {
1027
+ schema: parsed,
1028
+ resolvedPath,
1029
+ hash: (0, import_node_crypto.createHash)("sha256").update(raw).digest("hex")
1030
+ };
1031
+ }
1032
+ async function resolveZodSchemaRef(schemaRef, promptFilePath) {
1033
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
1034
+ let moduleSource;
1035
+ try {
1036
+ moduleSource = await (0, import_promises.readFile)(resolvedPath, "utf-8");
1037
+ } catch (error) {
1038
+ const code = error.code;
1039
+ if (code === "ENOENT") {
1040
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
1041
+ }
1042
+ throw error;
1043
+ }
1044
+ let imported;
1045
+ try {
1046
+ imported = await import((0, import_node_url.pathToFileURL)(resolvedPath).href);
1047
+ } catch (error) {
1048
+ const message = error instanceof Error ? error.message : String(error);
1049
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" could not be imported as a module (${message})`);
1050
+ }
1051
+ const mod = imported;
1052
+ const candidate = mod.default ?? mod.schema;
1053
+ if (!(candidate instanceof import_zod2.z.ZodType)) {
1054
+ throw new Error('POK051: zod schema modules must export a Zod schema as default export or named export "schema"');
1055
+ }
1056
+ const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(candidate, {
1057
+ target: "jsonSchema7",
1058
+ $refStrategy: "none"
1059
+ });
1060
+ if (!jsonSchema || typeof jsonSchema !== "object" || Array.isArray(jsonSchema)) {
1061
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" did not produce a valid JSON schema object`);
1062
+ }
1063
+ return {
1064
+ schema: jsonSchema,
1065
+ resolvedPath,
1066
+ hash: (0, import_node_crypto.createHash)("sha256").update(moduleSource).digest("hex")
1067
+ };
1068
+ }
1069
+ async function resolveResponseSchemaRef(asset, promptFilePath) {
1070
+ const schemaRef = asset.response?.schema_ref;
1071
+ if (!schemaRef) return asset;
1072
+ const ext = (0, import_node_path.extname)(schemaRef).toLowerCase();
1073
+ if (!SUPPORTED_SCHEMA_REF_EXTENSIONS.has(ext)) {
1074
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" has unsupported extension "${ext}". Use .json, .js, .mjs, or .cjs`);
1075
+ }
1076
+ const resolved = ext === ".json" ? await resolveJsonSchemaRef(schemaRef, promptFilePath) : await resolveZodSchemaRef(schemaRef, promptFilePath);
1077
+ return {
1078
+ ...asset,
1079
+ response: {
1080
+ ...asset.response,
1081
+ schema: resolved.schema,
1082
+ schema_ref: void 0,
1083
+ schema_source: {
1084
+ mode: ext === ".json" ? "schema_ref_json" : "schema_ref_zod_module",
1085
+ ref: schemaRef,
1086
+ resolved_path: resolved.resolvedPath,
1087
+ hash: resolved.hash
1088
+ }
1089
+ }
1090
+ };
1091
+ }
1092
+
1093
+ // src/parser/loader.ts
632
1094
  var DEFAULTS_FILE_NAME = "defaults.md";
633
1095
  async function loadPromptFile(filePath, options = {}) {
634
- const content = await (0, import_promises.readFile)(filePath, "utf-8");
1096
+ const content = await (0, import_promises2.readFile)(filePath, "utf-8");
635
1097
  const parsed = parsePrompt(content, filePath);
636
- const root = options.defaultsRoot ?? (0, import_node_path.dirname)(filePath);
1098
+ const root = options.defaultsRoot ?? (0, import_node_path2.dirname)(filePath);
637
1099
  const defaults = await loadDefaultsForPath(filePath, root);
638
- const asset = applyDefaults(parsed.asset, defaults);
1100
+ const withDefaults = applyDefaults(parsed.asset, defaults);
1101
+ const resolved = await resolveResponseSchemaRef(withDefaults, filePath);
1102
+ const asset = resolved.response?.schema !== void 0 && !resolved.response?.schema_source ? {
1103
+ ...resolved,
1104
+ response: {
1105
+ ...resolved.response,
1106
+ schema_source: { mode: "inline" }
1107
+ }
1108
+ } : resolved;
639
1109
  return {
640
1110
  ...parsed,
641
1111
  asset
@@ -645,10 +1115,10 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
645
1115
  const directories = getDirectoriesToCheck(filePath, defaultsRoot);
646
1116
  let merged = {};
647
1117
  for (const dir of directories) {
648
- const defaultsPath = (0, import_node_path.join)(dir, DEFAULTS_FILE_NAME);
1118
+ const defaultsPath = (0, import_node_path2.join)(dir, DEFAULTS_FILE_NAME);
649
1119
  try {
650
- const defaultsContent = await (0, import_promises.readFile)(defaultsPath, "utf-8");
651
- const defaults = parseDefaults(defaultsContent);
1120
+ const defaultsContent = await (0, import_promises2.readFile)(defaultsPath, "utf-8");
1121
+ const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
652
1122
  merged = mergeDefaults(merged, defaults);
653
1123
  } catch (error) {
654
1124
  if (error.code !== "ENOENT") {
@@ -660,14 +1130,14 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
660
1130
  }
661
1131
  function getDirectoriesToCheck(filePath, defaultsRoot) {
662
1132
  const dirs = [];
663
- let current = (0, import_node_path.resolve)((0, import_node_path.dirname)(filePath));
664
- const boundary = defaultsRoot ? (0, import_node_path.resolve)(defaultsRoot) : void 0;
1133
+ let current = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(filePath));
1134
+ const boundary = defaultsRoot ? (0, import_node_path2.resolve)(defaultsRoot) : void 0;
665
1135
  while (true) {
666
1136
  dirs.unshift(current);
667
- if (boundary && current === boundary || current === (0, import_node_path.dirname)(current)) {
1137
+ if (boundary && current === boundary || current === (0, import_node_path2.dirname)(current)) {
668
1138
  break;
669
1139
  }
670
- current = (0, import_node_path.dirname)(current);
1140
+ current = (0, import_node_path2.dirname)(current);
671
1141
  }
672
1142
  return dirs;
673
1143
  }
@@ -677,501 +1147,381 @@ function parseDefaults(content) {
677
1147
  return PromptDefaultsSchema.parse({
678
1148
  ...frontMatter,
679
1149
  sections: {
680
- system_instructions: sections.system_instructions
681
- }
682
- });
683
- }
684
- function mergeDefaults(base, local) {
685
- return {
686
- provider: local.provider ?? base.provider,
687
- model: local.model ?? base.model,
688
- cache: mergeCache(base.cache, local.cache),
689
- metadata: {
690
- ...base.metadata ?? {},
691
- ...local.metadata ?? {}
692
- },
693
- sections: {
694
- ...base.sections ?? {},
695
- ...local.sections ?? {}
696
- }
697
- };
698
- }
699
- function applyDefaults(asset, defaults) {
700
- const cache = mergeCache(defaults.cache, asset.cache);
701
- const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
702
- const hasDefaultSystem = !!defaults.sections?.system_instructions;
703
- const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
704
- const hasDefaultCache = cache !== void 0;
705
- if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
706
- return asset;
707
- }
708
- const mergedMetadata = {
709
- ...defaults.metadata ?? {},
710
- ...asset.metadata ?? {}
711
- };
712
- const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
713
- const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
714
- const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
715
- return {
716
- ...asset,
717
- provider: asset.provider ?? defaults.provider,
718
- model: asset.model ?? defaults.model,
719
- cache,
720
- metadata,
721
- sections
722
- };
723
- }
724
- function mergeCache(base, local) {
725
- const merged = {
726
- ...base ?? {},
727
- ...local ?? {},
728
- openai: {
729
- ...base?.openai ?? {},
730
- ...local?.openai ?? {}
731
- },
732
- anthropic: {
733
- ...base?.anthropic ?? {},
734
- ...local?.anthropic ?? {}
735
- },
736
- gemini: {
737
- ...base?.gemini ?? {},
738
- ...local?.gemini ?? {}
739
- },
740
- google: {
741
- ...base?.google ?? {},
742
- ...local?.google ?? {}
743
- }
744
- };
745
- if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
746
- if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
747
- if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
748
- if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
749
- return Object.keys(merged).length > 0 ? merged : void 0;
750
- }
751
-
752
- // src/composition/resolve-includes.ts
753
- async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
754
- if (!asset.includes || asset.includes.length === 0) {
755
- return asset;
756
- }
757
- const baseDir = (0, import_node_path2.dirname)(basePath);
758
- const resolvedPath = (0, import_node_path2.resolve)(basePath);
759
- if (visited.has(resolvedPath)) {
760
- throw new Error(`Circular include detected: ${resolvedPath}`);
761
- }
762
- visited.add(resolvedPath);
763
- let mergedSystemInstructions = "";
764
- for (const includePath of asset.includes) {
765
- const fullPath = (0, import_node_path2.resolve)(baseDir, includePath);
766
- if (visited.has(fullPath)) {
767
- throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
768
- }
769
- const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
770
- const { asset: includedAsset } = parsePrompt(content, fullPath);
771
- const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
772
- if (resolved.sections?.system_instructions) {
773
- mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
774
- }
775
- }
776
- const localSystem = asset.sections?.system_instructions ?? "";
777
- const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
778
- return {
779
- ...asset,
780
- sections: {
781
- ...asset.sections,
782
- system_instructions: combinedSystem
783
- },
784
- // Drop includes from the resolved asset — they've been inlined
785
- includes: void 0
786
- };
787
- }
788
-
789
- // src/prompt-resolution.ts
790
- var DEFAULT_PROMPTS_DIR = "./prompts";
791
- var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
792
- function withPromptResolutionDefaults(config) {
793
- return {
794
- ...config,
795
- sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
796
- compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
797
- };
798
- }
799
- var sharedPromptCache = new PromptCache();
800
- async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
801
- const resolvedConfig = withPromptResolutionDefaults(config);
802
- const mode = resolvedConfig.mode ?? "auto";
803
- if (mode !== "source-only" && resolvedConfig.compiledDir) {
804
- const compiledFile = (0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
805
- if ((0, import_node_fs2.existsSync)(compiledFile)) {
806
- if (mode === "auto") {
807
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
808
- if ((0, import_node_fs2.existsSync)(sourceFile)) {
809
- const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
810
- const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
811
- if (sourceMtime > compiledMtime) {
812
- console.warn(
813
- `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
814
- Run "promptopskit compile" or switch to source-only mode.`
815
- );
816
- }
817
- }
818
- }
819
- const content = await (0, import_promises3.readFile)(compiledFile, "utf-8");
820
- return JSON.parse(content);
821
- }
822
- if (mode === "compiled-only") {
823
- throw new Error(
824
- `Compiled artifact not found: ${compiledFile}
825
- Run "promptopskit compile" to generate it.`
826
- );
827
- }
828
- }
829
- if (mode !== "compiled-only") {
830
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
831
- if (resolvedConfig.cache !== false) {
832
- const cached = promptCache.get(sourceFile);
833
- if (cached) {
834
- return cached;
835
- }
836
- }
837
- if (!(0, import_node_fs2.existsSync)(sourceFile)) {
838
- const paths = [sourceFile];
839
- if (resolvedConfig.compiledDir) {
840
- paths.unshift((0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
841
- }
842
- throw new Error(
843
- `Prompt not found: "${promptPath}"
844
- Searched:
845
- ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
846
- );
847
- }
848
- const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
849
- if (resolvedConfig.cache !== false) {
850
- promptCache.set(sourceFile, asset);
851
- }
852
- return asset;
853
- }
854
- throw new Error(`Prompt not found: "${promptPath}"`);
855
- }
856
- async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
857
- const resolvedConfig = withPromptResolutionDefaults(config);
858
- let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
859
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
860
- if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
861
- asset = await resolveIncludes(asset, sourceFile);
862
- }
863
- asset = applyOverrides(asset, {
864
- environment: options.environment,
865
- tier: options.tier,
866
- runtime: options.runtime
867
- });
868
- return asset;
869
- }
870
- function resolveInlinePromptSource(source, options = {}) {
871
- const { asset } = parsePrompt(source);
872
- return applyOverrides(asset, {
873
- environment: options.environment,
874
- tier: options.tier,
875
- runtime: options.runtime
876
- });
877
- }
878
-
879
- // src/context.ts
880
- var textEncoder = new TextEncoder();
881
- var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
882
- var REJECT_SECRETS_FLAGS = "i";
883
- function getContextInputs(asset) {
884
- return (asset.context?.inputs ?? []).map(normalizeContextInput);
885
- }
886
- function getContextInputNames(asset) {
887
- return getContextInputs(asset).map((input) => input.name);
1150
+ system_instructions: sections.system_instructions
1151
+ }
1152
+ });
888
1153
  }
889
- function normalizeContextInput(input) {
890
- if (typeof input === "string") {
891
- return { name: input };
892
- }
1154
+ function mergeDefaults(base, local) {
893
1155
  return {
894
- name: input.name,
895
- max_size: input.max_size,
896
- trim: input.trim,
897
- allow_regex: normalizeContextRegex(input.allow_regex),
898
- deny_regex: normalizeContextRegex(input.deny_regex),
899
- non_empty: normalizeBuiltInValidator(input.non_empty),
900
- reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
1156
+ provider: local.provider ?? base.provider,
1157
+ model: local.model ?? base.model,
1158
+ fallback_models: local.fallback_models ?? base.fallback_models,
1159
+ reasoning: mergeRecordBlock2(base.reasoning, local.reasoning),
1160
+ sampling: mergeRecordBlock2(base.sampling, local.sampling),
1161
+ response: mergeRecordBlock2(base.response, local.response),
1162
+ cache: mergeCache(base.cache, local.cache),
1163
+ raw: mergeRaw(base.raw, local.raw),
1164
+ tools: local.tools ?? base.tools,
1165
+ provider_options: mergeProviderOptions(base.provider_options, local.provider_options),
1166
+ mcp: mergeMcp(base.mcp, local.mcp),
1167
+ context: mergeContext(base.context, local.context),
1168
+ includes: local.includes ?? base.includes,
1169
+ environments: mergeOverrideMap(base.environments, local.environments),
1170
+ tiers: mergeOverrideMap(base.tiers, local.tiers),
1171
+ metadata: {
1172
+ ...base.metadata ?? {},
1173
+ ...local.metadata ?? {}
1174
+ },
1175
+ sections: {
1176
+ ...base.sections ?? {},
1177
+ ...local.sections ?? {}
1178
+ }
901
1179
  };
902
1180
  }
903
- function normalizeContextRegex(value) {
904
- if (value === void 0) {
905
- return void 0;
906
- }
907
- if (typeof value === "string") {
908
- const literal = parseRegexLiteral(value);
909
- if (value.startsWith("/") && !literal) {
910
- return {
911
- pattern: value,
912
- flags: "",
913
- raw: value,
914
- invalidLiteral: true
915
- };
916
- }
917
- return {
918
- pattern: literal?.pattern ?? value,
919
- flags: literal?.flags ?? "",
920
- raw: value
921
- };
1181
+ function applyDefaults(asset, defaults) {
1182
+ const cache = mergeCache(defaults.cache, asset.cache);
1183
+ const raw = mergeRaw(defaults.raw, asset.raw);
1184
+ const providerOptions = mergeProviderOptions(defaults.provider_options, asset.provider_options);
1185
+ const reasoning = mergeRecordBlock2(defaults.reasoning, asset.reasoning);
1186
+ const sampling = mergeRecordBlock2(defaults.sampling, asset.sampling);
1187
+ const response = mergeRecordBlock2(defaults.response, asset.response);
1188
+ const mcp = mergeMcp(defaults.mcp, asset.mcp);
1189
+ const context = mergeContext(defaults.context, asset.context);
1190
+ const environments = mergeOverrideMap(defaults.environments, asset.environments);
1191
+ const tiers = mergeOverrideMap(defaults.tiers, asset.tiers);
1192
+ const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
1193
+ const hasDefaultSystem = !!defaults.sections?.system_instructions;
1194
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0 || defaults.fallback_models !== void 0 || defaults.tools !== void 0 || defaults.includes !== void 0;
1195
+ const hasDefaultObjects = 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;
1196
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultObjects) {
1197
+ return asset;
922
1198
  }
1199
+ const mergedMetadata = {
1200
+ ...defaults.metadata ?? {},
1201
+ ...asset.metadata ?? {}
1202
+ };
1203
+ const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
1204
+ const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
1205
+ const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
923
1206
  return {
924
- pattern: value.pattern,
925
- flags: value.flags ?? "",
926
- raw: JSON.stringify(value),
927
- returnMessage: value.return_message
1207
+ ...asset,
1208
+ provider: asset.provider ?? defaults.provider,
1209
+ model: asset.model ?? defaults.model,
1210
+ fallback_models: asset.fallback_models ?? defaults.fallback_models,
1211
+ reasoning,
1212
+ sampling,
1213
+ response,
1214
+ cache,
1215
+ raw,
1216
+ tools: asset.tools ?? defaults.tools,
1217
+ provider_options: providerOptions,
1218
+ mcp,
1219
+ context,
1220
+ includes: asset.includes ?? defaults.includes,
1221
+ environments,
1222
+ tiers,
1223
+ metadata,
1224
+ sections
928
1225
  };
929
1226
  }
930
- function normalizeBuiltInValidator(value) {
931
- if (value === void 0 || value === false) {
932
- return void 0;
933
- }
934
- if (value === true) {
935
- return {};
936
- }
1227
+ function normalizeDefaultIncludes(defaults, defaultsPath, filePath) {
1228
+ if (!defaults.includes) return defaults;
1229
+ const promptDir = (0, import_node_path2.dirname)(filePath);
1230
+ const defaultsDir = (0, import_node_path2.dirname)(defaultsPath);
1231
+ const includes = defaults.includes.map((includePath) => {
1232
+ const relativePath = (0, import_node_path2.relative)(promptDir, (0, import_node_path2.resolve)(defaultsDir, includePath)).replace(/\\/g, "/");
1233
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
1234
+ });
937
1235
  return {
938
- returnMessage: value.return_message
1236
+ ...defaults,
1237
+ includes
939
1238
  };
940
1239
  }
941
- function parseRegexLiteral(value) {
942
- if (!value.startsWith("/")) {
943
- return void 0;
944
- }
945
- for (let index = value.length - 1; index > 0; index -= 1) {
946
- if (value[index] !== "/") {
947
- continue;
948
- }
949
- let backslashCount = 0;
950
- for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
951
- backslashCount += 1;
952
- }
953
- if (backslashCount % 2 === 1) {
954
- continue;
955
- }
956
- return {
957
- pattern: value.slice(1, index),
958
- flags: value.slice(index + 1)
959
- };
960
- }
961
- return void 0;
1240
+ function mergeRecordBlock2(base, local) {
1241
+ if (base === void 0) return local;
1242
+ if (local === void 0) return base;
1243
+ return { ...base, ...local };
962
1244
  }
963
- function formatInvalidContextRegexMessage(details) {
964
- return [
965
- `Invalid context regex for prompt "${details.promptId}"`,
966
- `variable "${details.variable}"`,
967
- `field "${details.field}"`,
968
- `value ${JSON.stringify(details.raw)}: ${details.reason}`
969
- ].join(", ");
1245
+ function mergeMcp(base, local) {
1246
+ return mergeRecordBlock2(base, local);
970
1247
  }
971
- function compileContextRegex(regex, details) {
972
- if (regex.invalidLiteral) {
973
- throw new Error(
974
- `POK013: ${formatInvalidContextRegexMessage({
975
- ...details,
976
- raw: regex.raw,
977
- reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
978
- })}`
979
- );
980
- }
981
- try {
982
- return new RegExp(regex.pattern, regex.flags);
983
- } catch (error) {
984
- const reason = error instanceof Error ? error.message : String(error);
985
- throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
986
- }
1248
+ function mergeContext(base, local) {
1249
+ const merged = {
1250
+ ...base ?? {},
1251
+ ...local ?? {},
1252
+ history: mergeRecordBlock2(base?.history, local?.history)
1253
+ };
1254
+ return Object.keys(merged).length > 0 ? merged : void 0;
987
1255
  }
988
- function getRejectSecretsRegex() {
989
- return {
990
- pattern: REJECT_SECRETS_PATTERN,
991
- flags: REJECT_SECRETS_FLAGS,
992
- raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
1256
+ function mergeCache(base, local) {
1257
+ const merged = {
1258
+ ...base ?? {},
1259
+ ...local ?? {},
1260
+ openai: {
1261
+ ...base?.openai ?? {},
1262
+ ...local?.openai ?? {}
1263
+ },
1264
+ anthropic: {
1265
+ ...base?.anthropic ?? {},
1266
+ ...local?.anthropic ?? {}
1267
+ },
1268
+ gemini: {
1269
+ ...base?.gemini ?? {},
1270
+ ...local?.gemini ?? {}
1271
+ },
1272
+ google: {
1273
+ ...base?.google ?? {},
1274
+ ...local?.google ?? {}
1275
+ }
993
1276
  };
1277
+ if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
1278
+ if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
1279
+ if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
1280
+ if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
1281
+ return Object.keys(merged).length > 0 ? merged : void 0;
994
1282
  }
995
- function isTrimEnabled(mode) {
996
- return mode === true || mode === "start" || mode === "end" || mode === "both";
1283
+ function mergeRaw(base, local) {
1284
+ const merged = {
1285
+ ...base ?? {},
1286
+ ...local ?? {},
1287
+ openai: mergeRecordBlock2(base?.openai, local?.openai),
1288
+ "openai-responses": mergeRecordBlock2(base?.["openai-responses"], local?.["openai-responses"]),
1289
+ openai_responses: mergeRecordBlock2(base?.openai_responses, local?.openai_responses),
1290
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1291
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1292
+ google: mergeRecordBlock2(base?.google, local?.google),
1293
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1294
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
1295
+ };
1296
+ removeEmptyProviderBlocks(merged);
1297
+ return Object.keys(merged).length > 0 ? merged : void 0;
997
1298
  }
998
- function normalizeTrimMode(mode) {
999
- if (mode === "start") {
1000
- return "start";
1001
- }
1002
- return "end";
1299
+ function mergeProviderOptions(base, local) {
1300
+ const merged = {
1301
+ ...base ?? {},
1302
+ ...local ?? {},
1303
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1304
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1305
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1306
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
1307
+ };
1308
+ removeEmptyProviderBlocks(merged);
1309
+ return Object.keys(merged).length > 0 ? merged : void 0;
1003
1310
  }
1004
- function trimToMaxSize(value, maxSize, mode) {
1005
- const measured = measureContextValueSize(value);
1006
- if (measured <= maxSize) {
1007
- return value;
1008
- }
1009
- const characters = Array.from(value);
1010
- const normalizedMode = normalizeTrimMode(mode);
1011
- if (normalizedMode === "start") {
1012
- let collected2 = "";
1013
- let size2 = 0;
1014
- for (let i = characters.length - 1; i >= 0; i -= 1) {
1015
- const next = characters[i];
1016
- const charSize = measureContextValueSize(next);
1017
- if (size2 + charSize > maxSize) {
1018
- break;
1019
- }
1020
- collected2 = next + collected2;
1021
- size2 += charSize;
1022
- }
1023
- return collected2;
1311
+ function mergeOverrideMap(base, local) {
1312
+ const merged = { ...base ?? {} };
1313
+ for (const [name, localOverride] of Object.entries(local ?? {})) {
1314
+ merged[name] = mergeOverrideConfig(merged[name], localOverride);
1024
1315
  }
1025
- let collected = "";
1026
- let size = 0;
1027
- for (const char of characters) {
1028
- const charSize = measureContextValueSize(char);
1029
- if (size + charSize > maxSize) {
1030
- break;
1031
- }
1032
- collected += char;
1033
- size += charSize;
1034
- }
1035
- return collected;
1316
+ return Object.keys(merged).length > 0 ? merged : void 0;
1036
1317
  }
1037
- function sanitizeContextVariables(asset, variables = {}, options = {}) {
1038
- const { onContextOverflow } = options;
1039
- const sanitized = { ...variables };
1040
- for (const input of getContextInputs(asset)) {
1041
- const value = sanitized[input.name];
1042
- if (value === void 0) {
1318
+ function mergeOverrideConfig(base, local) {
1319
+ const merged = {
1320
+ ...base ?? {},
1321
+ ...local
1322
+ };
1323
+ const reasoning = mergeRecordBlock2(base?.reasoning, local.reasoning);
1324
+ const sampling = mergeRecordBlock2(base?.sampling, local.sampling);
1325
+ const response = mergeRecordBlock2(base?.response, local.response);
1326
+ const cache = mergeCache(base?.cache, local.cache);
1327
+ const raw = mergeRaw(base?.raw, local.raw);
1328
+ const providerOptions = mergeProviderOptions(base?.provider_options, local.provider_options);
1329
+ if (reasoning !== void 0) merged.reasoning = reasoning;
1330
+ if (sampling !== void 0) merged.sampling = sampling;
1331
+ if (response !== void 0) merged.response = response;
1332
+ if (cache !== void 0) merged.cache = cache;
1333
+ if (raw !== void 0) merged.raw = raw;
1334
+ if (providerOptions !== void 0) merged.provider_options = providerOptions;
1335
+ return merged;
1336
+ }
1337
+ function removeEmptyProviderBlocks(value) {
1338
+ for (const key of Object.keys(value)) {
1339
+ const block = value[key];
1340
+ if (block === void 0) {
1341
+ delete value[key];
1043
1342
  continue;
1044
1343
  }
1045
- let candidate = value;
1046
- if (input.max_size !== void 0) {
1047
- const actualSize = measureContextValueSize(candidate);
1048
- if (actualSize > input.max_size && onContextOverflow) {
1049
- candidate = onContextOverflow({
1050
- promptId: asset.id,
1051
- variable: input.name,
1052
- value: candidate,
1053
- maxSize: input.max_size,
1054
- actualSize
1055
- });
1056
- }
1057
- }
1058
- if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
1059
- candidate = trimToMaxSize(candidate, input.max_size, input.trim);
1344
+ if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
1345
+ delete value[key];
1060
1346
  }
1061
- sanitized[input.name] = candidate;
1062
- if (input.allow_regex) {
1063
- const candidate2 = sanitized[input.name];
1064
- const matcher = compileContextRegex(input.allow_regex, {
1065
- promptId: asset.id,
1066
- variable: input.name,
1067
- field: "allow_regex"
1068
- });
1069
- if (!matcher.test(candidate2)) {
1070
- if (input.allow_regex.returnMessage) {
1071
- return {
1072
- variables: sanitized,
1073
- shortCircuit: {
1074
- returnMessage: input.allow_regex.returnMessage,
1075
- code: "POK031",
1076
- variable: input.name,
1077
- field: "allow_regex"
1078
- }
1079
- };
1347
+ }
1348
+ }
1349
+
1350
+ // src/composition/resolve-includes.ts
1351
+ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
1352
+ if (!asset.includes || asset.includes.length === 0) {
1353
+ return asset;
1354
+ }
1355
+ const baseDir = (0, import_node_path3.dirname)(basePath);
1356
+ const resolvedPath = (0, import_node_path3.resolve)(basePath);
1357
+ if (visited.has(resolvedPath)) {
1358
+ throw new Error(`Circular include detected: ${resolvedPath}`);
1359
+ }
1360
+ visited.add(resolvedPath);
1361
+ let mergedSystemInstructions = "";
1362
+ for (const includePath of asset.includes) {
1363
+ const fullPath = (0, import_node_path3.resolve)(baseDir, includePath);
1364
+ if (visited.has(fullPath)) {
1365
+ throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
1366
+ }
1367
+ const content = await (0, import_promises3.readFile)(fullPath, "utf-8");
1368
+ const { asset: includedAsset } = parsePrompt(content, fullPath);
1369
+ const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
1370
+ if (resolved.sections?.system_instructions) {
1371
+ mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
1372
+ }
1373
+ }
1374
+ const localSystem = asset.sections?.system_instructions ?? "";
1375
+ const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
1376
+ return {
1377
+ ...asset,
1378
+ sections: {
1379
+ ...asset.sections,
1380
+ system_instructions: combinedSystem
1381
+ },
1382
+ // Drop includes from the resolved asset — they've been inlined
1383
+ includes: void 0
1384
+ };
1385
+ }
1386
+
1387
+ // src/prompt-resolution.ts
1388
+ var DEFAULT_PROMPTS_DIR = "./prompts";
1389
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
1390
+ function withPromptResolutionDefaults(config) {
1391
+ return {
1392
+ ...config,
1393
+ sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
1394
+ compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
1395
+ };
1396
+ }
1397
+ var sharedPromptCache = new PromptCache();
1398
+ async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
1399
+ const resolvedConfig = withPromptResolutionDefaults(config);
1400
+ const mode = resolvedConfig.mode ?? "auto";
1401
+ if (mode !== "source-only" && resolvedConfig.compiledDir) {
1402
+ const compiledFile = (0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
1403
+ if ((0, import_node_fs2.existsSync)(compiledFile)) {
1404
+ if (mode === "auto") {
1405
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1406
+ if ((0, import_node_fs2.existsSync)(sourceFile)) {
1407
+ const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
1408
+ const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
1409
+ if (sourceMtime > compiledMtime) {
1410
+ console.warn(
1411
+ `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
1412
+ Run "promptopskit compile" or switch to source-only mode.`
1413
+ );
1414
+ }
1080
1415
  }
1081
- throw new Error(
1082
- `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
1083
- );
1084
1416
  }
1417
+ const content = await (0, import_promises4.readFile)(compiledFile, "utf-8");
1418
+ return JSON.parse(content);
1085
1419
  }
1086
- if (input.deny_regex) {
1087
- const candidate2 = sanitized[input.name];
1088
- const matcher = compileContextRegex(input.deny_regex, {
1089
- promptId: asset.id,
1090
- variable: input.name,
1091
- field: "deny_regex"
1092
- });
1093
- if (matcher.test(candidate2)) {
1094
- if (input.deny_regex.returnMessage) {
1095
- return {
1096
- variables: sanitized,
1097
- shortCircuit: {
1098
- returnMessage: input.deny_regex.returnMessage,
1099
- code: "POK032",
1100
- variable: input.name,
1101
- field: "deny_regex"
1102
- }
1103
- };
1104
- }
1105
- throw new Error(
1106
- `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
1107
- );
1420
+ if (mode === "compiled-only") {
1421
+ throw new Error(
1422
+ `Compiled artifact not found: ${compiledFile}
1423
+ Run "promptopskit compile" to generate it.`
1424
+ );
1425
+ }
1426
+ }
1427
+ if (mode !== "compiled-only") {
1428
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1429
+ if (resolvedConfig.cache !== false) {
1430
+ const cached = promptCache.get(sourceFile);
1431
+ if (cached) {
1432
+ return cached;
1108
1433
  }
1109
1434
  }
1110
- if (input.non_empty && candidate.trim().length === 0) {
1111
- if (input.non_empty.returnMessage) {
1112
- return {
1113
- variables: sanitized,
1114
- shortCircuit: {
1115
- returnMessage: input.non_empty.returnMessage,
1116
- code: "POK033",
1117
- variable: input.name,
1118
- field: "non_empty"
1119
- }
1120
- };
1435
+ if (!(0, import_node_fs2.existsSync)(sourceFile)) {
1436
+ const paths = [sourceFile];
1437
+ if (resolvedConfig.compiledDir) {
1438
+ paths.unshift((0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
1121
1439
  }
1122
1440
  throw new Error(
1123
- `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
1441
+ `Prompt not found: "${promptPath}"
1442
+ Searched:
1443
+ ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
1124
1444
  );
1125
1445
  }
1126
- if (input.reject_secrets) {
1127
- const matcher = compileContextRegex(getRejectSecretsRegex(), {
1128
- promptId: asset.id,
1129
- variable: input.name,
1130
- field: "reject_secrets"
1131
- });
1132
- if (matcher.test(candidate)) {
1133
- if (input.reject_secrets.returnMessage) {
1134
- return {
1135
- variables: sanitized,
1136
- shortCircuit: {
1137
- returnMessage: input.reject_secrets.returnMessage,
1138
- code: "POK034",
1139
- variable: input.name,
1140
- field: "reject_secrets"
1141
- }
1142
- };
1143
- }
1144
- throw new Error(
1145
- `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
1146
- );
1147
- }
1446
+ const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
1447
+ if (resolvedConfig.cache !== false) {
1448
+ promptCache.set(sourceFile, asset);
1148
1449
  }
1450
+ return asset;
1149
1451
  }
1150
- return { variables: sanitized };
1452
+ throw new Error(`Prompt not found: "${promptPath}"`);
1151
1453
  }
1152
- function measureContextValueSize(value) {
1153
- return textEncoder.encode(value).length;
1454
+ async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
1455
+ const resolvedConfig = withPromptResolutionDefaults(config);
1456
+ let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
1457
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1458
+ if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
1459
+ asset = await resolveIncludes(asset, sourceFile);
1460
+ }
1461
+ asset = applyOverrides(asset, {
1462
+ environment: options.environment,
1463
+ tier: options.tier,
1464
+ runtime: options.runtime
1465
+ });
1466
+ return asset;
1154
1467
  }
1155
- function collectContextSizeWarnings(asset, variables = {}) {
1156
- const warnings = [];
1157
- for (const input of getContextInputs(asset)) {
1158
- if (input.max_size === void 0) {
1159
- continue;
1160
- }
1161
- const value = variables[input.name];
1162
- if (value === void 0) {
1163
- continue;
1164
- }
1165
- const actualSize = measureContextValueSize(value);
1166
- if (actualSize > input.max_size) {
1167
- warnings.push({
1168
- variable: input.name,
1169
- maxSize: input.max_size,
1170
- actualSize
1171
- });
1172
- }
1468
+ function resolveInlinePromptSource(source, options = {}) {
1469
+ const { asset } = parsePrompt(source);
1470
+ return applyOverrides(asset, {
1471
+ environment: options.environment,
1472
+ tier: options.tier,
1473
+ runtime: options.runtime
1474
+ });
1475
+ }
1476
+
1477
+ // src/history.ts
1478
+ function defaultCompactHistory(info) {
1479
+ const content = info.overflow.map((message) => `${message.role}: ${message.content}`).join("\n\n");
1480
+ return {
1481
+ role: "user",
1482
+ content: `Earlier conversation compacted to preserve history:
1483
+
1484
+ ${content}`
1485
+ };
1486
+ }
1487
+ function normalizeCompactionResult(result) {
1488
+ if (typeof result === "string") {
1489
+ return {
1490
+ role: "user",
1491
+ content: result
1492
+ };
1173
1493
  }
1174
- return warnings;
1494
+ return result;
1495
+ }
1496
+ function compactHistoryForPrompt(asset, runtime) {
1497
+ const history = runtime.history;
1498
+ const maxItems = asset.context?.history?.max_items;
1499
+ if (!history || maxItems === void 0 || history.length <= maxItems) {
1500
+ return history;
1501
+ }
1502
+ if (maxItems === 1) {
1503
+ const info2 = {
1504
+ promptId: asset.id,
1505
+ maxItems,
1506
+ overflow: history,
1507
+ preserved: [],
1508
+ history
1509
+ };
1510
+ const compacted2 = runtime.onHistoryCompaction ? normalizeCompactionResult(runtime.onHistoryCompaction(info2)) : defaultCompactHistory(info2);
1511
+ return [compacted2];
1512
+ }
1513
+ const preservedCount = maxItems - 1;
1514
+ const overflow = history.slice(0, history.length - preservedCount);
1515
+ const preserved = history.slice(-preservedCount);
1516
+ const info = {
1517
+ promptId: asset.id,
1518
+ maxItems,
1519
+ overflow,
1520
+ preserved,
1521
+ history
1522
+ };
1523
+ const compacted = runtime.onHistoryCompaction ? normalizeCompactionResult(runtime.onHistoryCompaction(info)) : defaultCompactHistory(info);
1524
+ return [compacted, ...preserved];
1175
1525
  }
1176
1526
 
1177
1527
  // src/providers/prompt-input.ts
@@ -1209,7 +1559,8 @@ function withPromptInputSupport(adapter) {
1209
1559
  }
1210
1560
  return adapter.render(resolved, {
1211
1561
  ...runtime,
1212
- variables: sanitization.variables
1562
+ variables: sanitization.variables,
1563
+ history: compactHistoryForPrompt(resolved, runtime)
1213
1564
  });
1214
1565
  };
1215
1566
  return {
@@ -1262,8 +1613,9 @@ var openaiAdapter = withPromptInputSupport({
1262
1613
  if (sections.system_instructions) {
1263
1614
  messages.push({ role: "system", content: sections.system_instructions });
1264
1615
  }
1265
- if (runtime.history) {
1266
- for (const msg of runtime.history) {
1616
+ const history = compactHistoryForPrompt(resolvedAsset, runtime);
1617
+ if (history) {
1618
+ for (const msg of history) {
1267
1619
  messages.push({ role: msg.role, content: msg.content });
1268
1620
  }
1269
1621
  }
@@ -1360,8 +1712,9 @@ var openaiResponsesAdapter = withPromptInputSupport({
1360
1712
  });
1361
1713
  const responseOptions = runtime.openaiResponses;
1362
1714
  const input = [];
1363
- if (runtime.history) {
1364
- for (const msg of runtime.history) {
1715
+ const history = compactHistoryForPrompt(resolvedAsset, runtime);
1716
+ if (history) {
1717
+ for (const msg of history) {
1365
1718
  input.push({ role: msg.role, content: msg.content });
1366
1719
  }
1367
1720
  }
@@ -1474,8 +1827,9 @@ var anthropicAdapter = withPromptInputSupport({
1474
1827
  ...anthropicCacheConfig.ttl ? { ttl: anthropicCacheConfig.ttl } : {}
1475
1828
  } : void 0;
1476
1829
  const cacheMode = anthropicCacheConfig?.mode ?? "automatic";
1477
- if (runtime.history) {
1478
- for (const msg of runtime.history) {
1830
+ const history = compactHistoryForPrompt(resolvedAsset, runtime);
1831
+ if (history) {
1832
+ for (const msg of history) {
1479
1833
  messages.push({ role: msg.role, content: msg.content });
1480
1834
  }
1481
1835
  }
@@ -1600,8 +1954,9 @@ var geminiAdapter = withPromptInputSupport({
1600
1954
  strict: runtime.strict
1601
1955
  });
1602
1956
  const contents = [];
1603
- if (runtime.history) {
1604
- for (const msg of runtime.history) {
1957
+ const history = compactHistoryForPrompt(resolvedAsset, runtime);
1958
+ if (history) {
1959
+ for (const msg of history) {
1605
1960
  contents.push({
1606
1961
  role: msg.role === "assistant" ? "model" : "user",
1607
1962
  parts: [{ text: msg.content }]
@@ -1703,6 +2058,7 @@ var openrouterAdapter = withPromptInputSupport({
1703
2058
  {
1704
2059
  variables: runtime.variables,
1705
2060
  history: runtime.history,
2061
+ onHistoryCompaction: runtime.onHistoryCompaction,
1706
2062
  toolRegistry: runtime.toolRegistry,
1707
2063
  strict: runtime.strict
1708
2064
  }
@@ -1724,6 +2080,118 @@ var openrouterAdapter = withPromptInputSupport({
1724
2080
  }
1725
2081
  });
1726
2082
 
2083
+ // src/providers/llmasaservice.ts
2084
+ var LLMASASERVICE_BASE_URL = "https://gateway.llmasaservice.io";
2085
+ var LLMASASERVICE_SDK_PLACEHOLDER_API_KEY = "not-used-by-llm-gateway";
2086
+ var LLMASASERVICE_DEFAULT_MODEL = "group:standard";
2087
+ var LLMASASERVICE_RESPONSE_HEADER_NAMES = [
2088
+ "x-request-id",
2089
+ "x-llm-model-id",
2090
+ "x-llm-model-group"
2091
+ ];
2092
+ function createLLMAsAServiceOpenAIConfig(options = {}) {
2093
+ return {
2094
+ baseURL: options.baseURL ?? LLMASASERVICE_BASE_URL,
2095
+ apiKey: LLMASASERVICE_SDK_PLACEHOLDER_API_KEY,
2096
+ defaultHeaders: {
2097
+ "x-project-id": options.projectId ?? ""
2098
+ }
2099
+ };
2100
+ }
2101
+ function isGPT5ModelSelector(model) {
2102
+ if (!model) return false;
2103
+ return model.split(/[|,]/).map((candidate) => candidate.trim()).some((candidate) => candidate.startsWith("gpt-5") || candidate.startsWith("openai:gpt-5"));
2104
+ }
2105
+ function defaultModel() {
2106
+ return LLMASASERVICE_DEFAULT_MODEL;
2107
+ }
2108
+ function isRecord(value) {
2109
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2110
+ }
2111
+ function rawCustomerId(asset) {
2112
+ const customer = isRecord(asset.raw?.llmasaservice?.customer) ? asset.raw.llmasaservice.customer : void 0;
2113
+ return customer?.customer_id;
2114
+ }
2115
+ function isGatewayRenderValidation(runtime) {
2116
+ return runtime?.runtime !== void 0;
2117
+ }
2118
+ var llmasaserviceAdapter = withPromptInputSupport({
2119
+ name: "llmasaservice",
2120
+ validate(asset, runtime) {
2121
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
2122
+ const validation = openaiAdapter.validate(
2123
+ {
2124
+ ...resolvedAsset,
2125
+ model: resolvedAsset.model ?? defaultModel()
2126
+ },
2127
+ runtime
2128
+ );
2129
+ const warnings = validation.warnings.filter(
2130
+ (warning) => warning !== "OpenAI adapter requires a model to be specified."
2131
+ );
2132
+ const errors = [...validation.errors];
2133
+ const gatewayOptions = resolvedAsset.provider_options?.llmasaservice;
2134
+ const missingProjectId = !gatewayOptions?.project_id;
2135
+ const missingCustomerId = !gatewayOptions?.customer?.customer_id && typeof rawCustomerId(resolvedAsset) !== "string";
2136
+ const validateRenderMetadata = isGatewayRenderValidation(runtime);
2137
+ if (missingProjectId && validateRenderMetadata) {
2138
+ errors.push(
2139
+ "LLMAsAService adapter requires provider_options.llmasaservice.project_id for x-project-id routing."
2140
+ );
2141
+ }
2142
+ if (missingCustomerId && validateRenderMetadata) {
2143
+ errors.push(
2144
+ "LLMAsAService adapter requires customer.customer_id in provider_options.llmasaservice.customer or raw.llmasaservice.customer."
2145
+ );
2146
+ }
2147
+ if ((missingProjectId || missingCustomerId) && !validateRenderMetadata) {
2148
+ warnings.push(
2149
+ "LLMAsAService project_id and customer.customer_id must be supplied before rendering, usually through runtime provider_options."
2150
+ );
2151
+ }
2152
+ return {
2153
+ valid: errors.length === 0,
2154
+ errors,
2155
+ warnings
2156
+ };
2157
+ },
2158
+ render(asset, runtime) {
2159
+ const resolvedAsset = resolveAssetForProvider(asset, runtime);
2160
+ const options = resolvedAsset.provider_options?.llmasaservice;
2161
+ const model = resolvedAsset.model ?? defaultModel();
2162
+ const result = openaiAdapter.render(
2163
+ { ...resolvedAsset, model, raw: void 0 },
2164
+ {
2165
+ variables: runtime.variables,
2166
+ history: runtime.history,
2167
+ onHistoryCompaction: runtime.onHistoryCompaction,
2168
+ toolRegistry: runtime.toolRegistry,
2169
+ strict: runtime.strict
2170
+ }
2171
+ );
2172
+ let body = { ...result.body };
2173
+ if (isGPT5ModelSelector(model) && body.max_tokens !== void 0 && body.max_completion_tokens === void 0) {
2174
+ body.max_completion_tokens = body.max_tokens;
2175
+ delete body.max_tokens;
2176
+ }
2177
+ if (options?.customer !== void 0) body.customer = options.customer;
2178
+ if (options?.conversationId !== void 0) body.conversationId = options.conversationId;
2179
+ if (options?.conversationTitle !== void 0) body.conversationTitle = options.conversationTitle;
2180
+ if (options?.projectId !== void 0) body.projectId = options.projectId;
2181
+ body = applyRawProviderBody(body, resolvedAsset, "llmasaservice");
2182
+ const projectId = options?.project_id ?? "";
2183
+ return {
2184
+ body,
2185
+ provider: "llmasaservice",
2186
+ model,
2187
+ baseURL: options?.base_url ?? LLMASASERVICE_BASE_URL,
2188
+ headers: {
2189
+ "x-project-id": projectId
2190
+ }
2191
+ };
2192
+ }
2193
+ });
2194
+
1727
2195
  // src/providers/index.ts
1728
2196
  var adapters = {
1729
2197
  openai: openaiAdapter,
@@ -1731,7 +2199,8 @@ var adapters = {
1731
2199
  anthropic: anthropicAdapter,
1732
2200
  google: geminiAdapter,
1733
2201
  gemini: geminiAdapter,
1734
- openrouter: openrouterAdapter
2202
+ openrouter: openrouterAdapter,
2203
+ llmasaservice: llmasaserviceAdapter
1735
2204
  };
1736
2205
  function getAdapter(provider) {
1737
2206
  const adapter = adapters[provider];
@@ -1787,6 +2256,18 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
1787
2256
  "provider_options",
1788
2257
  "raw"
1789
2258
  ]);
2259
+ function collectSchemaKeywords(value, acc) {
2260
+ if (!value || typeof value !== "object") return;
2261
+ if (Array.isArray(value)) {
2262
+ for (const item of value) collectSchemaKeywords(item, acc);
2263
+ return;
2264
+ }
2265
+ const record = value;
2266
+ for (const [key, child] of Object.entries(record)) {
2267
+ acc.add(key);
2268
+ collectSchemaKeywords(child, acc);
2269
+ }
2270
+ }
1790
2271
  var RISKY_UNBOUNDED_INPUT_NAMES = [
1791
2272
  "message",
1792
2273
  "prompt",
@@ -1838,7 +2319,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1838
2319
  }
1839
2320
  }
1840
2321
  }
1841
- const declaredInputs = new Set(getContextInputNames(asset));
2322
+ const contextInputs = getContextInputs(asset);
2323
+ const declaredInputs = new Set(contextInputs.map((input) => input.name));
2324
+ const declaredInputsByName = new Map(contextInputs.map((input) => [input.name, input]));
1842
2325
  const usedVars = /* @__PURE__ */ new Set();
1843
2326
  if (asset.sections?.system_instructions) {
1844
2327
  for (const v of extractVariables(asset.sections.system_instructions)) {
@@ -1860,7 +2343,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1860
2343
  }
1861
2344
  }
1862
2345
  for (const v of declaredInputs) {
1863
- if (!usedVars.has(v)) {
2346
+ const input = declaredInputsByName.get(v);
2347
+ if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
1864
2348
  warnings.push({
1865
2349
  code: "POK012",
1866
2350
  message: `Variable "${v}" is declared in context.inputs but never used`,
@@ -1876,9 +2360,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1876
2360
  suggestion: "Declare context.inputs to enable input policy validation."
1877
2361
  });
1878
2362
  }
1879
- for (const input of getContextInputs(asset)) {
2363
+ for (const input of contextInputs) {
1880
2364
  const lowerName = input.name.toLowerCase();
1881
- if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
2365
+ const inputWarningsEnabled = areContextInputWarningsEnabled(input);
2366
+ if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
1882
2367
  warnings.push({
1883
2368
  code: "POK040",
1884
2369
  message: `Context input "${input.name}" has no max_size and appears unbounded.`,
@@ -1886,7 +2371,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1886
2371
  suggestion: "Add max_size to constrain prompt payload growth."
1887
2372
  });
1888
2373
  }
1889
- if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
2374
+ 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) {
1890
2375
  warnings.push({
1891
2376
  code: "POK041",
1892
2377
  message: `Context input "${input.name}" has no input hardening validators.`,
@@ -1894,7 +2379,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1894
2379
  suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
1895
2380
  });
1896
2381
  }
1897
- if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
2382
+ if (inputWarningsEnabled && input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1898
2383
  warnings.push({
1899
2384
  code: "POK014",
1900
2385
  message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
@@ -1921,6 +2406,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1921
2406
  }
1922
2407
  }
1923
2408
  }
2409
+ if (asset.response?.schema && asset.provider) {
2410
+ const keywords = /* @__PURE__ */ new Set();
2411
+ collectSchemaKeywords(asset.response.schema, keywords);
2412
+ const openAIUnsupported = ["patternProperties", "unevaluatedProperties", "$dynamicRef"];
2413
+ const geminiUnsupported = ["patternProperties", "$dynamicRef", "dependentSchemas"];
2414
+ const anthropicCaution = ["if", "then", "else"];
2415
+ const checks = [
2416
+ { provider: ["openai", "openai-responses", "openrouter", "llmasaservice"], unsupported: openAIUnsupported, code: "POK052" },
2417
+ { provider: ["gemini", "google"], unsupported: geminiUnsupported, code: "POK053" },
2418
+ { provider: ["anthropic"], unsupported: anthropicCaution, code: "POK054" }
2419
+ ];
2420
+ for (const check of checks) {
2421
+ if (!check.provider.includes(asset.provider)) continue;
2422
+ const hits = check.unsupported.filter((k) => keywords.has(k));
2423
+ if (hits.length > 0) {
2424
+ warnings.push({
2425
+ code: check.code,
2426
+ message: `response.schema includes provider-sensitive JSON Schema keywords for "${asset.provider}": ${hits.join(", ")}`,
2427
+ filePath,
2428
+ suggestion: "Validate schema compatibility with your provider docs or simplify the schema dialect."
2429
+ });
2430
+ }
2431
+ }
2432
+ }
1924
2433
  if (asset.provider) {
1925
2434
  let providerCache;
1926
2435
  let cacheSuggestionField;
@@ -1967,7 +2476,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1967
2476
  });
1968
2477
  }
1969
2478
  for (const [envName, overrides] of Object.entries(asset.environments ?? {})) {
1970
- if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
2479
+ if (asset.cache && (!isRecord2(overrides) || !overrides.cache)) {
1971
2480
  warnings.push({
1972
2481
  code: "POK045",
1973
2482
  message: `Environment "${envName}" does not override cache while prompt-level cache is defined.`,
@@ -1977,7 +2486,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1977
2486
  }
1978
2487
  }
1979
2488
  for (const [tierName, overrides] of Object.entries(asset.tiers ?? {})) {
1980
- if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
2489
+ if (asset.cache && (!isRecord2(overrides) || !overrides.cache)) {
1981
2490
  warnings.push({
1982
2491
  code: "POK045",
1983
2492
  message: `Tier "${tierName}" does not override cache while prompt-level cache is defined.`,
@@ -1987,7 +2496,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
1987
2496
  }
1988
2497
  }
1989
2498
  for (const tool of asset.tools ?? []) {
1990
- if (isRecord(tool)) {
2499
+ if (isRecord2(tool)) {
1991
2500
  if (!tool.description) {
1992
2501
  warnings.push({
1993
2502
  code: "POK047",
@@ -2042,7 +2551,7 @@ function findClosestMatch(input, candidates) {
2042
2551
  }
2043
2552
  return best;
2044
2553
  }
2045
- function isRecord(value) {
2554
+ function isRecord2(value) {
2046
2555
  return typeof value === "object" && value !== null && !Array.isArray(value);
2047
2556
  }
2048
2557
 
@@ -2321,7 +2830,7 @@ function applyUsageTapEntitlements(request, begin, options = {}) {
2321
2830
  if (!isObject2(nextRequest.body)) {
2322
2831
  return nextRequest;
2323
2832
  }
2324
- if (nextRequest.provider === "openai" || nextRequest.provider === "openrouter") {
2833
+ if (nextRequest.provider === "openai" || nextRequest.provider === "openrouter" || nextRequest.provider === "llmasaservice") {
2325
2834
  capOpenAIReasoning(nextRequest.body, allowed);
2326
2835
  if (isRecordArray(nextRequest.body.tools)) {
2327
2836
  nextRequest.body.tools = filterOpenAITools(nextRequest.body.tools, allowed, options);
@@ -2410,6 +2919,9 @@ function runOpenAIWithUsageTap(client, options) {
2410
2919
  function runOpenRouterWithUsageTap(client, options) {
2411
2920
  return runProviderWithUsageTap(client, options, extractOpenAIUsage);
2412
2921
  }
2922
+ function runLLMAsAServiceWithUsageTap(client, options) {
2923
+ return runProviderWithUsageTap(client, options, extractOpenAIUsage);
2924
+ }
2413
2925
  function runAnthropicWithUsageTap(client, options) {
2414
2926
  return runProviderWithUsageTap(client, options, extractAnthropicUsage);
2415
2927
  }
@@ -2513,7 +3025,11 @@ var PromptOpsKit = class {
2513
3025
  }
2514
3026
  const request = adapter.render(resolved, {
2515
3027
  variables: sanitization.variables,
2516
- history: options.history,
3028
+ history: compactHistoryForPrompt(resolved, {
3029
+ history: options.history,
3030
+ onHistoryCompaction: options.onHistoryCompaction
3031
+ }),
3032
+ onHistoryCompaction: options.onHistoryCompaction,
2517
3033
  toolRegistry: options.toolRegistry,
2518
3034
  strict: options.strict,
2519
3035
  openaiResponses: options.openaiResponses
@@ -2528,7 +3044,7 @@ var PromptOpsKit = class {
2528
3044
  * Validate a prompt file.
2529
3045
  */
2530
3046
  async validatePrompt(promptPath) {
2531
- const sourceFile = (0, import_node_path4.resolve)(this.config.sourceDir, promptPath + ".md");
3047
+ const sourceFile = (0, import_node_path5.resolve)(this.config.sourceDir, promptPath + ".md");
2532
3048
  try {
2533
3049
  const asset = await this.loadPrompt(promptPath);
2534
3050
  return validateAssetWithIncludes(asset, sourceFile);
@@ -2578,6 +3094,10 @@ async function renderPrompt(options) {
2578
3094
  }
2579
3095
  // Annotate the CommonJS export names for ESM import in node:
2580
3096
  0 && (module.exports = {
3097
+ LLMASASERVICE_BASE_URL,
3098
+ LLMASASERVICE_DEFAULT_MODEL,
3099
+ LLMASASERVICE_RESPONSE_HEADER_NAMES,
3100
+ LLMASASERVICE_SDK_PLACEHOLDER_API_KEY,
2581
3101
  PromptAssetOverridesSchema,
2582
3102
  PromptAssetSchema,
2583
3103
  PromptOpsKit,
@@ -2585,6 +3105,7 @@ async function renderPrompt(options) {
2585
3105
  applyOverrides,
2586
3106
  applyUsageTapEntitlements,
2587
3107
  beginUsageTapCall,
3108
+ createLLMAsAServiceOpenAIConfig,
2588
3109
  createPromptOpsKit,
2589
3110
  createUsageTapClient,
2590
3111
  defaultUsageTapErrorMapper,
@@ -2597,6 +3118,7 @@ async function renderPrompt(options) {
2597
3118
  geminiAdapter,
2598
3119
  getAdapter,
2599
3120
  interpolate,
3121
+ llmasaserviceAdapter,
2600
3122
  loadPromptFile,
2601
3123
  openaiAdapter,
2602
3124
  openaiResponsesAdapter,
@@ -2606,6 +3128,7 @@ async function renderPrompt(options) {
2606
3128
  resolveIncludes,
2607
3129
  runAnthropicWithUsageTap,
2608
3130
  runGeminiWithUsageTap,
3131
+ runLLMAsAServiceWithUsageTap,
2609
3132
  runOpenAIWithUsageTap,
2610
3133
  runOpenRouterWithUsageTap,
2611
3134
  validateAsset,