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
@@ -40,12 +40,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
40
40
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
41
41
  function interpolate(template, variables, options = {}) {
42
42
  const { strict = false } = options;
43
+ const optionalVariables = new Set(options.optionalVariables ?? []);
43
44
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
44
45
  result = result.replace(VARIABLE_RE, (match, name) => {
45
46
  if (name in variables) {
46
47
  return variables[name];
47
48
  }
48
- if (strict) {
49
+ if (strict && !optionalVariables.has(name)) {
49
50
  throw new Error(`Missing required variable: "${name}"`);
50
51
  }
51
52
  return match;
@@ -54,156 +55,438 @@ function interpolate(template, variables, options = {}) {
54
55
  return result;
55
56
  }
56
57
 
57
- // src/renderer/renderer.ts
58
- function renderSections(asset, options = {}) {
59
- const { variables = {}, strict = false } = options;
60
- const result = {};
61
- if (asset.sections.system_instructions) {
62
- result.system_instructions = interpolate(
63
- asset.sections.system_instructions,
64
- variables,
65
- { strict }
66
- );
67
- }
68
- if (asset.sections.prompt_template) {
69
- result.prompt_template = interpolate(
70
- asset.sections.prompt_template,
71
- variables,
72
- { strict }
73
- );
74
- }
75
- return result;
58
+ // src/context.ts
59
+ var textEncoder = new TextEncoder();
60
+ var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
61
+ var REJECT_SECRETS_FLAGS = "i";
62
+ function getContextInputs(asset) {
63
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
76
64
  }
77
-
78
- // src/overrides/apply-overrides.ts
79
- function applyOverrides(asset, options = {}) {
80
- let result = { ...asset };
81
- if (options.environment && result.environments?.[options.environment]) {
82
- result = mergeOverride(result, result.environments[options.environment]);
65
+ function normalizeContextInput(input) {
66
+ if (typeof input === "string") {
67
+ return { name: input };
83
68
  }
84
- if (options.tier && result.tiers?.[options.tier]) {
85
- result = mergeOverride(result, result.tiers[options.tier]);
69
+ return {
70
+ name: input.name,
71
+ optional: input.optional,
72
+ warnings: input.warnings,
73
+ max_size: input.max_size,
74
+ trim: input.trim,
75
+ allow_regex: normalizeContextRegex(input.allow_regex),
76
+ deny_regex: normalizeContextRegex(input.deny_regex),
77
+ non_empty: normalizeBuiltInValidator(input.non_empty),
78
+ reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
79
+ };
80
+ }
81
+ function normalizeContextRegex(value) {
82
+ if (value === void 0) {
83
+ return void 0;
86
84
  }
87
- if (options.runtime) {
88
- result = mergeOverride(result, options.runtime);
85
+ if (typeof value === "string") {
86
+ const literal = parseRegexLiteral(value);
87
+ if (value.startsWith("/") && !literal) {
88
+ return {
89
+ pattern: value,
90
+ flags: "",
91
+ raw: value,
92
+ invalidLiteral: true
93
+ };
94
+ }
95
+ return {
96
+ pattern: literal?.pattern ?? value,
97
+ flags: literal?.flags ?? "",
98
+ raw: value
99
+ };
89
100
  }
90
- return result;
101
+ return {
102
+ pattern: value.pattern,
103
+ flags: value.flags ?? "",
104
+ raw: JSON.stringify(value),
105
+ returnMessage: value.return_message
106
+ };
91
107
  }
92
- function mergeOverride(base, override) {
93
- const result = { ...base };
94
- if (override.model !== void 0) result.model = override.model;
95
- if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
96
- if (override.tools !== void 0) result.tools = override.tools;
97
- if (override.reasoning !== void 0) {
98
- result.reasoning = { ...result.reasoning, ...override.reasoning };
108
+ function normalizeBuiltInValidator(value) {
109
+ if (value === void 0 || value === false) {
110
+ return void 0;
99
111
  }
100
- if (override.sampling !== void 0) {
101
- result.sampling = { ...result.sampling, ...override.sampling };
112
+ if (value === true) {
113
+ return {};
102
114
  }
103
- if (override.response !== void 0) {
104
- result.response = { ...result.response, ...override.response };
115
+ return {
116
+ returnMessage: value.return_message
117
+ };
118
+ }
119
+ function parseRegexLiteral(value) {
120
+ if (!value.startsWith("/")) {
121
+ return void 0;
105
122
  }
106
- if (override.cache !== void 0) {
107
- result.cache = {
108
- ...result.cache,
109
- ...override.cache,
110
- openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
111
- anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
112
- gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
113
- google: mergeRecordBlock(result.cache?.google, override.cache.google)
123
+ for (let index = value.length - 1; index > 0; index -= 1) {
124
+ if (value[index] !== "/") {
125
+ continue;
126
+ }
127
+ let backslashCount = 0;
128
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
129
+ backslashCount += 1;
130
+ }
131
+ if (backslashCount % 2 === 1) {
132
+ continue;
133
+ }
134
+ return {
135
+ pattern: value.slice(1, index),
136
+ flags: value.slice(index + 1)
114
137
  };
115
138
  }
116
- if (override.raw !== void 0) {
117
- result.raw = {
118
- ...result.raw,
119
- ...override.raw,
120
- openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
121
- "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
122
- openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
123
- anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
124
- gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
125
- google: mergeRecordBlock(result.raw?.google, override.raw.google),
126
- openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter)
127
- };
139
+ return void 0;
140
+ }
141
+ function formatInvalidContextRegexMessage(details) {
142
+ return [
143
+ `Invalid context regex for prompt "${details.promptId}"`,
144
+ `variable "${details.variable}"`,
145
+ `field "${details.field}"`,
146
+ `value ${JSON.stringify(details.raw)}: ${details.reason}`
147
+ ].join(", ");
148
+ }
149
+ function compileContextRegex(regex, details) {
150
+ if (regex.invalidLiteral) {
151
+ throw new Error(
152
+ `POK013: ${formatInvalidContextRegexMessage({
153
+ ...details,
154
+ raw: regex.raw,
155
+ reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
156
+ })}`
157
+ );
128
158
  }
129
- if (override.provider_options !== void 0) {
130
- result.provider_options = {
131
- ...result.provider_options,
132
- ...override.provider_options,
133
- anthropic: {
134
- ...result.provider_options?.anthropic,
135
- ...override.provider_options.anthropic
136
- },
137
- gemini: {
138
- ...result.provider_options?.gemini,
139
- ...override.provider_options.gemini
140
- },
141
- openrouter: {
142
- ...result.provider_options?.openrouter,
143
- ...override.provider_options.openrouter
144
- }
145
- };
159
+ try {
160
+ return new RegExp(regex.pattern, regex.flags);
161
+ } catch (error) {
162
+ const reason = error instanceof Error ? error.message : String(error);
163
+ throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
146
164
  }
147
- return result;
148
165
  }
149
- function mergeRecordBlock(base, override) {
150
- if (override === void 0) return base;
151
- return { ...base, ...override };
166
+ function getRejectSecretsRegex() {
167
+ return {
168
+ pattern: REJECT_SECRETS_PATTERN,
169
+ flags: REJECT_SECRETS_FLAGS,
170
+ raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
171
+ };
152
172
  }
153
-
154
- // src/providers/resolve-asset.ts
155
- function resolveAssetForProvider(asset, runtime = {}) {
156
- if (!runtime.environment && !runtime.tier && !runtime.runtime) {
157
- return asset;
173
+ function isTrimEnabled(mode) {
174
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
175
+ }
176
+ function normalizeTrimMode(mode) {
177
+ if (mode === "start") {
178
+ return "start";
158
179
  }
159
- return applyOverrides(asset, {
160
- environment: runtime.environment,
161
- tier: runtime.tier,
162
- runtime: runtime.runtime
163
- });
180
+ return "end";
164
181
  }
165
-
166
- // src/prompt-resolution.ts
167
- var import_promises3 = require("fs/promises");
168
- var import_node_fs2 = require("fs");
169
- var import_node_path3 = require("path");
170
-
171
- // src/cache.ts
172
- var import_node_fs = require("fs");
173
- var PromptCache = class {
174
- cache = /* @__PURE__ */ new Map();
175
- maxSize;
176
- constructor(maxSize = 100) {
177
- this.maxSize = maxSize;
182
+ function trimToMaxSize(value, maxSize, mode) {
183
+ const measured = measureContextValueSize(value);
184
+ if (measured <= maxSize) {
185
+ return value;
178
186
  }
179
- get(filePath) {
180
- const entry = this.cache.get(filePath);
181
- if (!entry) return void 0;
182
- try {
183
- const stat = (0, import_node_fs.statSync)(filePath);
184
- if (stat.mtimeMs !== entry.mtime) {
185
- this.cache.delete(filePath);
186
- return void 0;
187
+ const characters = Array.from(value);
188
+ const normalizedMode = normalizeTrimMode(mode);
189
+ if (normalizedMode === "start") {
190
+ let collected2 = "";
191
+ let size2 = 0;
192
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
193
+ const next = characters[i];
194
+ const charSize = measureContextValueSize(next);
195
+ if (size2 + charSize > maxSize) {
196
+ break;
187
197
  }
188
- } catch {
189
- this.cache.delete(filePath);
190
- return void 0;
198
+ collected2 = next + collected2;
199
+ size2 += charSize;
191
200
  }
192
- this.cache.delete(filePath);
193
- this.cache.set(filePath, entry);
194
- return entry.value;
201
+ return collected2;
195
202
  }
196
- set(filePath, value) {
197
- try {
198
- const stat = (0, import_node_fs.statSync)(filePath);
199
- if (this.cache.has(filePath)) {
200
- this.cache.delete(filePath);
201
- } else if (this.cache.size >= this.maxSize) {
202
- const oldest = this.cache.keys().next().value;
203
- if (oldest) this.cache.delete(oldest);
204
- }
205
- this.cache.set(filePath, { value, mtime: stat.mtimeMs });
206
- } catch {
203
+ let collected = "";
204
+ let size = 0;
205
+ for (const char of characters) {
206
+ const charSize = measureContextValueSize(char);
207
+ if (size + charSize > maxSize) {
208
+ break;
209
+ }
210
+ collected += char;
211
+ size += charSize;
212
+ }
213
+ return collected;
214
+ }
215
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
216
+ const { onContextOverflow } = options;
217
+ const sanitized = { ...variables };
218
+ for (const input of getContextInputs(asset)) {
219
+ const value = sanitized[input.name];
220
+ if (value === void 0) {
221
+ continue;
222
+ }
223
+ let candidate = value;
224
+ if (input.max_size !== void 0) {
225
+ const actualSize = measureContextValueSize(candidate);
226
+ if (actualSize > input.max_size && onContextOverflow) {
227
+ candidate = onContextOverflow({
228
+ promptId: asset.id,
229
+ variable: input.name,
230
+ value: candidate,
231
+ maxSize: input.max_size,
232
+ actualSize
233
+ });
234
+ }
235
+ }
236
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
237
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
238
+ }
239
+ sanitized[input.name] = candidate;
240
+ if (input.allow_regex) {
241
+ const candidate2 = sanitized[input.name];
242
+ const matcher = compileContextRegex(input.allow_regex, {
243
+ promptId: asset.id,
244
+ variable: input.name,
245
+ field: "allow_regex"
246
+ });
247
+ if (!matcher.test(candidate2)) {
248
+ if (input.allow_regex.returnMessage) {
249
+ return {
250
+ variables: sanitized,
251
+ shortCircuit: {
252
+ returnMessage: input.allow_regex.returnMessage,
253
+ code: "POK031",
254
+ variable: input.name,
255
+ field: "allow_regex"
256
+ }
257
+ };
258
+ }
259
+ throw new Error(
260
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
261
+ );
262
+ }
263
+ }
264
+ if (input.deny_regex) {
265
+ const candidate2 = sanitized[input.name];
266
+ const matcher = compileContextRegex(input.deny_regex, {
267
+ promptId: asset.id,
268
+ variable: input.name,
269
+ field: "deny_regex"
270
+ });
271
+ if (matcher.test(candidate2)) {
272
+ if (input.deny_regex.returnMessage) {
273
+ return {
274
+ variables: sanitized,
275
+ shortCircuit: {
276
+ returnMessage: input.deny_regex.returnMessage,
277
+ code: "POK032",
278
+ variable: input.name,
279
+ field: "deny_regex"
280
+ }
281
+ };
282
+ }
283
+ throw new Error(
284
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
285
+ );
286
+ }
287
+ }
288
+ if (input.non_empty && candidate.trim().length === 0) {
289
+ if (input.non_empty.returnMessage) {
290
+ return {
291
+ variables: sanitized,
292
+ shortCircuit: {
293
+ returnMessage: input.non_empty.returnMessage,
294
+ code: "POK033",
295
+ variable: input.name,
296
+ field: "non_empty"
297
+ }
298
+ };
299
+ }
300
+ throw new Error(
301
+ `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
302
+ );
303
+ }
304
+ if (input.reject_secrets) {
305
+ const matcher = compileContextRegex(getRejectSecretsRegex(), {
306
+ promptId: asset.id,
307
+ variable: input.name,
308
+ field: "reject_secrets"
309
+ });
310
+ if (matcher.test(candidate)) {
311
+ if (input.reject_secrets.returnMessage) {
312
+ return {
313
+ variables: sanitized,
314
+ shortCircuit: {
315
+ returnMessage: input.reject_secrets.returnMessage,
316
+ code: "POK034",
317
+ variable: input.name,
318
+ field: "reject_secrets"
319
+ }
320
+ };
321
+ }
322
+ throw new Error(
323
+ `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
324
+ );
325
+ }
326
+ }
327
+ }
328
+ return { variables: sanitized };
329
+ }
330
+ function measureContextValueSize(value) {
331
+ return textEncoder.encode(value).length;
332
+ }
333
+
334
+ // src/renderer/renderer.ts
335
+ function renderSections(asset, options = {}) {
336
+ const { variables = {}, strict = false } = options;
337
+ const optionalVariables = getContextInputs(asset).filter((input) => input.optional === true).map((input) => input.name);
338
+ const result = {};
339
+ if (asset.sections.system_instructions) {
340
+ result.system_instructions = interpolate(
341
+ asset.sections.system_instructions,
342
+ variables,
343
+ { strict, optionalVariables }
344
+ );
345
+ }
346
+ if (asset.sections.prompt_template) {
347
+ result.prompt_template = interpolate(
348
+ asset.sections.prompt_template,
349
+ variables,
350
+ { strict, optionalVariables }
351
+ );
352
+ }
353
+ return result;
354
+ }
355
+
356
+ // src/overrides/apply-overrides.ts
357
+ function applyOverrides(asset, options = {}) {
358
+ let result = { ...asset };
359
+ if (options.environment && result.environments?.[options.environment]) {
360
+ result = mergeOverride(result, result.environments[options.environment]);
361
+ }
362
+ if (options.tier && result.tiers?.[options.tier]) {
363
+ result = mergeOverride(result, result.tiers[options.tier]);
364
+ }
365
+ if (options.runtime) {
366
+ result = mergeOverride(result, options.runtime);
367
+ }
368
+ return result;
369
+ }
370
+ function mergeOverride(base, override) {
371
+ const result = { ...base };
372
+ if (override.model !== void 0) result.model = override.model;
373
+ if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
374
+ if (override.tools !== void 0) result.tools = override.tools;
375
+ if (override.reasoning !== void 0) {
376
+ result.reasoning = { ...result.reasoning, ...override.reasoning };
377
+ }
378
+ if (override.sampling !== void 0) {
379
+ result.sampling = { ...result.sampling, ...override.sampling };
380
+ }
381
+ if (override.response !== void 0) {
382
+ result.response = { ...result.response, ...override.response };
383
+ }
384
+ if (override.cache !== void 0) {
385
+ result.cache = {
386
+ ...result.cache,
387
+ ...override.cache,
388
+ openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
389
+ anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
390
+ gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
391
+ google: mergeRecordBlock(result.cache?.google, override.cache.google)
392
+ };
393
+ }
394
+ if (override.raw !== void 0) {
395
+ result.raw = {
396
+ ...result.raw,
397
+ ...override.raw,
398
+ openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
399
+ "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
400
+ openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
401
+ anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
402
+ gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
403
+ google: mergeRecordBlock(result.raw?.google, override.raw.google),
404
+ openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter),
405
+ llmasaservice: mergeRecordBlock(result.raw?.llmasaservice, override.raw.llmasaservice)
406
+ };
407
+ }
408
+ if (override.provider_options !== void 0) {
409
+ result.provider_options = {
410
+ ...result.provider_options,
411
+ ...override.provider_options,
412
+ anthropic: {
413
+ ...result.provider_options?.anthropic,
414
+ ...override.provider_options.anthropic
415
+ },
416
+ gemini: {
417
+ ...result.provider_options?.gemini,
418
+ ...override.provider_options.gemini
419
+ },
420
+ openrouter: {
421
+ ...result.provider_options?.openrouter,
422
+ ...override.provider_options.openrouter
423
+ },
424
+ llmasaservice: {
425
+ ...result.provider_options?.llmasaservice,
426
+ ...override.provider_options.llmasaservice
427
+ }
428
+ };
429
+ }
430
+ return result;
431
+ }
432
+ function mergeRecordBlock(base, override) {
433
+ if (override === void 0) return base;
434
+ return { ...base, ...override };
435
+ }
436
+
437
+ // src/providers/resolve-asset.ts
438
+ function resolveAssetForProvider(asset, runtime = {}) {
439
+ if (!runtime.environment && !runtime.tier && !runtime.runtime) {
440
+ return asset;
441
+ }
442
+ return applyOverrides(asset, {
443
+ environment: runtime.environment,
444
+ tier: runtime.tier,
445
+ runtime: runtime.runtime
446
+ });
447
+ }
448
+
449
+ // src/prompt-resolution.ts
450
+ var import_promises4 = require("fs/promises");
451
+ var import_node_fs2 = require("fs");
452
+ var import_node_path4 = require("path");
453
+
454
+ // src/cache.ts
455
+ var import_node_fs = require("fs");
456
+ var PromptCache = class {
457
+ cache = /* @__PURE__ */ new Map();
458
+ maxSize;
459
+ constructor(maxSize = 100) {
460
+ this.maxSize = maxSize;
461
+ }
462
+ get(filePath) {
463
+ const entry = this.cache.get(filePath);
464
+ if (!entry) return void 0;
465
+ try {
466
+ const stat = (0, import_node_fs.statSync)(filePath);
467
+ if (stat.mtimeMs !== entry.mtime) {
468
+ this.cache.delete(filePath);
469
+ return void 0;
470
+ }
471
+ } catch {
472
+ this.cache.delete(filePath);
473
+ return void 0;
474
+ }
475
+ this.cache.delete(filePath);
476
+ this.cache.set(filePath, entry);
477
+ return entry.value;
478
+ }
479
+ set(filePath, value) {
480
+ try {
481
+ const stat = (0, import_node_fs.statSync)(filePath);
482
+ if (this.cache.has(filePath)) {
483
+ this.cache.delete(filePath);
484
+ } else if (this.cache.size >= this.maxSize) {
485
+ const oldest = this.cache.keys().next().value;
486
+ if (oldest) this.cache.delete(oldest);
487
+ }
488
+ this.cache.set(filePath, { value, mtime: stat.mtimeMs });
489
+ } catch {
207
490
  }
208
491
  }
209
492
  clear() {
@@ -215,8 +498,8 @@ var PromptCache = class {
215
498
  };
216
499
 
217
500
  // src/composition/resolve-includes.ts
218
- var import_promises2 = require("fs/promises");
219
- var import_node_path2 = require("path");
501
+ var import_promises3 = require("fs/promises");
502
+ var import_node_path3 = require("path");
220
503
 
221
504
  // src/parser/parser.ts
222
505
  var import_gray_matter = __toESM(require("gray-matter"), 1);
@@ -252,10 +535,26 @@ var ResponseSchema = import_zod.z.object({
252
535
  format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
253
536
  stream: import_zod.z.boolean().optional(),
254
537
  schema: import_zod.z.record(import_zod.z.unknown()).optional(),
538
+ schema_ref: import_zod.z.string().min(1).optional(),
539
+ schema_source: import_zod.z.object({
540
+ mode: import_zod.z.enum(["inline", "schema_ref_json", "schema_ref_zod_module"]).optional(),
541
+ ref: import_zod.z.string().optional(),
542
+ resolved_path: import_zod.z.string().optional(),
543
+ hash: import_zod.z.string().optional()
544
+ }).optional(),
255
545
  schema_name: import_zod.z.string().optional(),
256
546
  schema_description: import_zod.z.string().optional(),
257
547
  schema_strict: import_zod.z.boolean().optional()
258
548
  });
549
+ var ResponseSchemaWithValidation = ResponseSchema.superRefine((value, ctx) => {
550
+ if (value.schema !== void 0 && value.schema_ref !== void 0) {
551
+ ctx.addIssue({
552
+ code: import_zod.z.ZodIssueCode.custom,
553
+ message: "response.schema and response.schema_ref are mutually exclusive",
554
+ path: ["schema_ref"]
555
+ });
556
+ }
557
+ });
259
558
  var AnthropicProviderOptionsSchema = import_zod.z.object({
260
559
  top_k: import_zod.z.number().int().min(0).optional(),
261
560
  tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
@@ -276,10 +575,26 @@ var OpenRouterProviderOptionsSchema = import_zod.z.object({
276
575
  plugins: import_zod.z.array(import_zod.z.record(import_zod.z.unknown())).optional(),
277
576
  models: import_zod.z.array(import_zod.z.string()).optional()
278
577
  });
578
+ var LLMAsAServiceCustomerSchema = import_zod.z.object({
579
+ customer_id: import_zod.z.string(),
580
+ customer_name: import_zod.z.string().optional(),
581
+ customer_user_id: import_zod.z.string().optional(),
582
+ customer_user_name: import_zod.z.string().optional(),
583
+ customer_user_email: import_zod.z.string().optional()
584
+ });
585
+ var LLMAsAServiceProviderOptionsSchema = import_zod.z.object({
586
+ base_url: import_zod.z.string().url().optional(),
587
+ project_id: import_zod.z.string().optional(),
588
+ customer: LLMAsAServiceCustomerSchema.optional(),
589
+ conversationId: import_zod.z.string().optional(),
590
+ conversationTitle: import_zod.z.string().optional(),
591
+ projectId: import_zod.z.string().optional()
592
+ });
279
593
  var ProviderOptionsSchema = import_zod.z.object({
280
594
  anthropic: AnthropicProviderOptionsSchema.optional(),
281
595
  gemini: GeminiProviderOptionsSchema.optional(),
282
- openrouter: OpenRouterProviderOptionsSchema.optional()
596
+ openrouter: OpenRouterProviderOptionsSchema.optional(),
597
+ llmasaservice: LLMAsAServiceProviderOptionsSchema.optional()
283
598
  });
284
599
  var RawProviderBodySchema = import_zod.z.object({
285
600
  openai: import_zod.z.record(import_zod.z.unknown()).optional(),
@@ -288,7 +603,8 @@ var RawProviderBodySchema = import_zod.z.object({
288
603
  anthropic: import_zod.z.record(import_zod.z.unknown()).optional(),
289
604
  gemini: import_zod.z.record(import_zod.z.unknown()).optional(),
290
605
  google: import_zod.z.record(import_zod.z.unknown()).optional(),
291
- openrouter: import_zod.z.record(import_zod.z.unknown()).optional()
606
+ openrouter: import_zod.z.record(import_zod.z.unknown()).optional(),
607
+ llmasaservice: import_zod.z.record(import_zod.z.unknown()).optional()
292
608
  });
293
609
  var OpenAICacheSchema = import_zod.z.object({
294
610
  prompt_cache_key: import_zod.z.string().min(1).optional(),
@@ -330,6 +646,8 @@ var ContextBuiltInValidatorSchema = import_zod.z.union([
330
646
  ]);
331
647
  var ContextInputDefinitionObjectSchema = import_zod.z.object({
332
648
  name: import_zod.z.string(),
649
+ optional: import_zod.z.boolean().optional(),
650
+ warnings: import_zod.z.boolean().optional(),
333
651
  max_size: import_zod.z.number().int().positive().optional(),
334
652
  trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
335
653
  allow_regex: ContextRegexSchema.optional(),
@@ -359,7 +677,7 @@ var PromptAssetOverridesSchema = import_zod.z.object({
359
677
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
360
678
  reasoning: ReasoningSchema.optional(),
361
679
  sampling: SamplingSchema.optional(),
362
- response: ResponseSchema.optional(),
680
+ response: ResponseSchemaWithValidation.optional(),
363
681
  cache: CacheSchema.optional(),
364
682
  raw: RawProviderBodySchema.optional(),
365
683
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -375,9 +693,21 @@ var SectionsSchema = import_zod.z.object({
375
693
  notes: import_zod.z.string().optional()
376
694
  });
377
695
  var PromptDefaultsSchema = import_zod.z.object({
378
- provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
696
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
379
697
  model: import_zod.z.string().optional(),
698
+ fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
699
+ reasoning: ReasoningSchema.optional(),
700
+ sampling: SamplingSchema.optional(),
701
+ response: ResponseSchema.optional(),
380
702
  cache: CacheSchema.optional(),
703
+ raw: RawProviderBodySchema.optional(),
704
+ tools: import_zod.z.array(ToolRefSchema).optional(),
705
+ provider_options: ProviderOptionsSchema.optional(),
706
+ mcp: MCPSchema.optional(),
707
+ context: ContextSchema.optional(),
708
+ includes: import_zod.z.array(import_zod.z.string()).optional(),
709
+ environments: import_zod.z.record(PromptAssetOverridesSchema).optional(),
710
+ tiers: import_zod.z.record(PromptAssetOverridesSchema).optional(),
381
711
  metadata: MetadataSchema.optional(),
382
712
  sections: import_zod.z.object({
383
713
  system_instructions: import_zod.z.string().optional()
@@ -387,12 +717,12 @@ var PromptAssetSchema = import_zod.z.object({
387
717
  id: import_zod.z.string(),
388
718
  schema_version: import_zod.z.number().int().positive().default(1),
389
719
  description: import_zod.z.string().optional(),
390
- provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
720
+ provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
391
721
  model: import_zod.z.string().optional(),
392
722
  fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
393
723
  reasoning: ReasoningSchema.optional(),
394
724
  sampling: SamplingSchema.optional(),
395
- response: ResponseSchema.optional(),
725
+ response: ResponseSchemaWithValidation.optional(),
396
726
  cache: CacheSchema.optional(),
397
727
  raw: RawProviderBodySchema.optional(),
398
728
  tools: import_zod.z.array(ToolRefSchema).optional(),
@@ -584,16 +914,122 @@ function parsePrompt(content, filePath) {
584
914
  }
585
915
 
586
916
  // src/parser/loader.ts
917
+ var import_promises2 = require("fs/promises");
918
+ var import_node_path2 = require("path");
919
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
920
+
921
+ // src/parser/response-schema-ref.ts
587
922
  var import_promises = require("fs/promises");
923
+ var import_node_crypto = require("crypto");
588
924
  var import_node_path = require("path");
589
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
925
+ var import_node_url = require("url");
926
+ var import_zod2 = require("zod");
927
+ var import_zod_to_json_schema = require("zod-to-json-schema");
928
+ var SUPPORTED_SCHEMA_REF_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
929
+ async function resolveJsonSchemaRef(schemaRef, promptFilePath) {
930
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
931
+ let raw;
932
+ try {
933
+ raw = await (0, import_promises.readFile)(resolvedPath, "utf-8");
934
+ } catch (error) {
935
+ const code = error.code;
936
+ if (code === "ENOENT") {
937
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
938
+ }
939
+ throw error;
940
+ }
941
+ let parsed;
942
+ try {
943
+ parsed = JSON.parse(raw);
944
+ } catch {
945
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" is not valid JSON`);
946
+ }
947
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
948
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" must resolve to a JSON object schema`);
949
+ }
950
+ return {
951
+ schema: parsed,
952
+ resolvedPath,
953
+ hash: (0, import_node_crypto.createHash)("sha256").update(raw).digest("hex")
954
+ };
955
+ }
956
+ async function resolveZodSchemaRef(schemaRef, promptFilePath) {
957
+ const resolvedPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(promptFilePath), schemaRef);
958
+ let moduleSource;
959
+ try {
960
+ moduleSource = await (0, import_promises.readFile)(resolvedPath, "utf-8");
961
+ } catch (error) {
962
+ const code = error.code;
963
+ if (code === "ENOENT") {
964
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
965
+ }
966
+ throw error;
967
+ }
968
+ let imported;
969
+ try {
970
+ imported = await import((0, import_node_url.pathToFileURL)(resolvedPath).href);
971
+ } catch (error) {
972
+ const message = error instanceof Error ? error.message : String(error);
973
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" could not be imported as a module (${message})`);
974
+ }
975
+ const mod = imported;
976
+ const candidate = mod.default ?? mod.schema;
977
+ if (!(candidate instanceof import_zod2.z.ZodType)) {
978
+ throw new Error('POK051: zod schema modules must export a Zod schema as default export or named export "schema"');
979
+ }
980
+ const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(candidate, {
981
+ target: "jsonSchema7",
982
+ $refStrategy: "none"
983
+ });
984
+ if (!jsonSchema || typeof jsonSchema !== "object" || Array.isArray(jsonSchema)) {
985
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" did not produce a valid JSON schema object`);
986
+ }
987
+ return {
988
+ schema: jsonSchema,
989
+ resolvedPath,
990
+ hash: (0, import_node_crypto.createHash)("sha256").update(moduleSource).digest("hex")
991
+ };
992
+ }
993
+ async function resolveResponseSchemaRef(asset, promptFilePath) {
994
+ const schemaRef = asset.response?.schema_ref;
995
+ if (!schemaRef) return asset;
996
+ const ext = (0, import_node_path.extname)(schemaRef).toLowerCase();
997
+ if (!SUPPORTED_SCHEMA_REF_EXTENSIONS.has(ext)) {
998
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" has unsupported extension "${ext}". Use .json, .js, .mjs, or .cjs`);
999
+ }
1000
+ const resolved = ext === ".json" ? await resolveJsonSchemaRef(schemaRef, promptFilePath) : await resolveZodSchemaRef(schemaRef, promptFilePath);
1001
+ return {
1002
+ ...asset,
1003
+ response: {
1004
+ ...asset.response,
1005
+ schema: resolved.schema,
1006
+ schema_ref: void 0,
1007
+ schema_source: {
1008
+ mode: ext === ".json" ? "schema_ref_json" : "schema_ref_zod_module",
1009
+ ref: schemaRef,
1010
+ resolved_path: resolved.resolvedPath,
1011
+ hash: resolved.hash
1012
+ }
1013
+ }
1014
+ };
1015
+ }
1016
+
1017
+ // src/parser/loader.ts
590
1018
  var DEFAULTS_FILE_NAME = "defaults.md";
591
1019
  async function loadPromptFile(filePath, options = {}) {
592
- const content = await (0, import_promises.readFile)(filePath, "utf-8");
1020
+ const content = await (0, import_promises2.readFile)(filePath, "utf-8");
593
1021
  const parsed = parsePrompt(content, filePath);
594
- const root = options.defaultsRoot ?? (0, import_node_path.dirname)(filePath);
1022
+ const root = options.defaultsRoot ?? (0, import_node_path2.dirname)(filePath);
595
1023
  const defaults = await loadDefaultsForPath(filePath, root);
596
- const asset = applyDefaults(parsed.asset, defaults);
1024
+ const withDefaults = applyDefaults(parsed.asset, defaults);
1025
+ const resolved = await resolveResponseSchemaRef(withDefaults, filePath);
1026
+ const asset = resolved.response?.schema !== void 0 && !resolved.response?.schema_source ? {
1027
+ ...resolved,
1028
+ response: {
1029
+ ...resolved.response,
1030
+ schema_source: { mode: "inline" }
1031
+ }
1032
+ } : resolved;
597
1033
  return {
598
1034
  ...parsed,
599
1035
  asset
@@ -603,10 +1039,10 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
603
1039
  const directories = getDirectoriesToCheck(filePath, defaultsRoot);
604
1040
  let merged = {};
605
1041
  for (const dir of directories) {
606
- const defaultsPath = (0, import_node_path.join)(dir, DEFAULTS_FILE_NAME);
1042
+ const defaultsPath = (0, import_node_path2.join)(dir, DEFAULTS_FILE_NAME);
607
1043
  try {
608
- const defaultsContent = await (0, import_promises.readFile)(defaultsPath, "utf-8");
609
- const defaults = parseDefaults(defaultsContent);
1044
+ const defaultsContent = await (0, import_promises2.readFile)(defaultsPath, "utf-8");
1045
+ const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
610
1046
  merged = mergeDefaults(merged, defaults);
611
1047
  } catch (error) {
612
1048
  if (error.code !== "ENOENT") {
@@ -618,14 +1054,14 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
618
1054
  }
619
1055
  function getDirectoriesToCheck(filePath, defaultsRoot) {
620
1056
  const dirs = [];
621
- let current = (0, import_node_path.resolve)((0, import_node_path.dirname)(filePath));
622
- const boundary = defaultsRoot ? (0, import_node_path.resolve)(defaultsRoot) : void 0;
1057
+ let current = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(filePath));
1058
+ const boundary = defaultsRoot ? (0, import_node_path2.resolve)(defaultsRoot) : void 0;
623
1059
  while (true) {
624
1060
  dirs.unshift(current);
625
- if (boundary && current === boundary || current === (0, import_node_path.dirname)(current)) {
1061
+ if (boundary && current === boundary || current === (0, import_node_path2.dirname)(current)) {
626
1062
  break;
627
1063
  }
628
- current = (0, import_node_path.dirname)(current);
1064
+ current = (0, import_node_path2.dirname)(current);
629
1065
  }
630
1066
  return dirs;
631
1067
  }
@@ -643,7 +1079,19 @@ function mergeDefaults(base, local) {
643
1079
  return {
644
1080
  provider: local.provider ?? base.provider,
645
1081
  model: local.model ?? base.model,
1082
+ fallback_models: local.fallback_models ?? base.fallback_models,
1083
+ reasoning: mergeRecordBlock2(base.reasoning, local.reasoning),
1084
+ sampling: mergeRecordBlock2(base.sampling, local.sampling),
1085
+ response: mergeRecordBlock2(base.response, local.response),
646
1086
  cache: mergeCache(base.cache, local.cache),
1087
+ raw: mergeRaw(base.raw, local.raw),
1088
+ tools: local.tools ?? base.tools,
1089
+ provider_options: mergeProviderOptions(base.provider_options, local.provider_options),
1090
+ mcp: mergeMcp(base.mcp, local.mcp),
1091
+ context: mergeContext(base.context, local.context),
1092
+ includes: local.includes ?? base.includes,
1093
+ environments: mergeOverrideMap(base.environments, local.environments),
1094
+ tiers: mergeOverrideMap(base.tiers, local.tiers),
647
1095
  metadata: {
648
1096
  ...base.metadata ?? {},
649
1097
  ...local.metadata ?? {}
@@ -656,11 +1104,20 @@ function mergeDefaults(base, local) {
656
1104
  }
657
1105
  function applyDefaults(asset, defaults) {
658
1106
  const cache = mergeCache(defaults.cache, asset.cache);
1107
+ const raw = mergeRaw(defaults.raw, asset.raw);
1108
+ const providerOptions = mergeProviderOptions(defaults.provider_options, asset.provider_options);
1109
+ const reasoning = mergeRecordBlock2(defaults.reasoning, asset.reasoning);
1110
+ const sampling = mergeRecordBlock2(defaults.sampling, asset.sampling);
1111
+ const response = mergeRecordBlock2(defaults.response, asset.response);
1112
+ const mcp = mergeMcp(defaults.mcp, asset.mcp);
1113
+ const context = mergeContext(defaults.context, asset.context);
1114
+ const environments = mergeOverrideMap(defaults.environments, asset.environments);
1115
+ const tiers = mergeOverrideMap(defaults.tiers, asset.tiers);
659
1116
  const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
660
1117
  const hasDefaultSystem = !!defaults.sections?.system_instructions;
661
- const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
662
- const hasDefaultCache = cache !== void 0;
663
- if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
1118
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0 || defaults.fallback_models !== void 0 || defaults.tools !== void 0 || defaults.includes !== void 0;
1119
+ 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;
1120
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultObjects) {
664
1121
  return asset;
665
1122
  }
666
1123
  const mergedMetadata = {
@@ -674,11 +1131,52 @@ function applyDefaults(asset, defaults) {
674
1131
  ...asset,
675
1132
  provider: asset.provider ?? defaults.provider,
676
1133
  model: asset.model ?? defaults.model,
1134
+ fallback_models: asset.fallback_models ?? defaults.fallback_models,
1135
+ reasoning,
1136
+ sampling,
1137
+ response,
677
1138
  cache,
1139
+ raw,
1140
+ tools: asset.tools ?? defaults.tools,
1141
+ provider_options: providerOptions,
1142
+ mcp,
1143
+ context,
1144
+ includes: asset.includes ?? defaults.includes,
1145
+ environments,
1146
+ tiers,
678
1147
  metadata,
679
1148
  sections
680
1149
  };
681
1150
  }
1151
+ function normalizeDefaultIncludes(defaults, defaultsPath, filePath) {
1152
+ if (!defaults.includes) return defaults;
1153
+ const promptDir = (0, import_node_path2.dirname)(filePath);
1154
+ const defaultsDir = (0, import_node_path2.dirname)(defaultsPath);
1155
+ const includes = defaults.includes.map((includePath) => {
1156
+ const relativePath = (0, import_node_path2.relative)(promptDir, (0, import_node_path2.resolve)(defaultsDir, includePath)).replace(/\\/g, "/");
1157
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
1158
+ });
1159
+ return {
1160
+ ...defaults,
1161
+ includes
1162
+ };
1163
+ }
1164
+ function mergeRecordBlock2(base, local) {
1165
+ if (base === void 0) return local;
1166
+ if (local === void 0) return base;
1167
+ return { ...base, ...local };
1168
+ }
1169
+ function mergeMcp(base, local) {
1170
+ return mergeRecordBlock2(base, local);
1171
+ }
1172
+ function mergeContext(base, local) {
1173
+ const merged = {
1174
+ ...base ?? {},
1175
+ ...local ?? {},
1176
+ history: mergeRecordBlock2(base?.history, local?.history)
1177
+ };
1178
+ return Object.keys(merged).length > 0 ? merged : void 0;
1179
+ }
682
1180
  function mergeCache(base, local) {
683
1181
  const merged = {
684
1182
  ...base ?? {},
@@ -706,406 +1204,248 @@ function mergeCache(base, local) {
706
1204
  if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
707
1205
  return Object.keys(merged).length > 0 ? merged : void 0;
708
1206
  }
709
-
710
- // src/composition/resolve-includes.ts
711
- async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
712
- if (!asset.includes || asset.includes.length === 0) {
713
- return asset;
714
- }
715
- const baseDir = (0, import_node_path2.dirname)(basePath);
716
- const resolvedPath = (0, import_node_path2.resolve)(basePath);
717
- if (visited.has(resolvedPath)) {
718
- throw new Error(`Circular include detected: ${resolvedPath}`);
719
- }
720
- visited.add(resolvedPath);
721
- let mergedSystemInstructions = "";
722
- for (const includePath of asset.includes) {
723
- const fullPath = (0, import_node_path2.resolve)(baseDir, includePath);
724
- if (visited.has(fullPath)) {
725
- throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
726
- }
727
- const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
728
- const { asset: includedAsset } = parsePrompt(content, fullPath);
729
- const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
730
- if (resolved.sections?.system_instructions) {
731
- mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
732
- }
733
- }
734
- const localSystem = asset.sections?.system_instructions ?? "";
735
- const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
736
- return {
737
- ...asset,
738
- sections: {
739
- ...asset.sections,
740
- system_instructions: combinedSystem
741
- },
742
- // Drop includes from the resolved asset — they've been inlined
743
- includes: void 0
744
- };
745
- }
746
-
747
- // src/prompt-resolution.ts
748
- var DEFAULT_PROMPTS_DIR = "./prompts";
749
- var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
750
- function withPromptResolutionDefaults(config) {
751
- return {
752
- ...config,
753
- sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
754
- compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
755
- };
756
- }
757
- var sharedPromptCache = new PromptCache();
758
- async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
759
- const resolvedConfig = withPromptResolutionDefaults(config);
760
- const mode = resolvedConfig.mode ?? "auto";
761
- if (mode !== "source-only" && resolvedConfig.compiledDir) {
762
- const compiledFile = (0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
763
- if ((0, import_node_fs2.existsSync)(compiledFile)) {
764
- if (mode === "auto") {
765
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
766
- if ((0, import_node_fs2.existsSync)(sourceFile)) {
767
- const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
768
- const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
769
- if (sourceMtime > compiledMtime) {
770
- console.warn(
771
- `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
772
- Run "promptopskit compile" or switch to source-only mode.`
773
- );
774
- }
775
- }
776
- }
777
- const content = await (0, import_promises3.readFile)(compiledFile, "utf-8");
778
- return JSON.parse(content);
779
- }
780
- if (mode === "compiled-only") {
781
- throw new Error(
782
- `Compiled artifact not found: ${compiledFile}
783
- Run "promptopskit compile" to generate it.`
784
- );
785
- }
786
- }
787
- if (mode !== "compiled-only") {
788
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
789
- if (resolvedConfig.cache !== false) {
790
- const cached = promptCache.get(sourceFile);
791
- if (cached) {
792
- return cached;
793
- }
794
- }
795
- if (!(0, import_node_fs2.existsSync)(sourceFile)) {
796
- const paths = [sourceFile];
797
- if (resolvedConfig.compiledDir) {
798
- paths.unshift((0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
799
- }
800
- throw new Error(
801
- `Prompt not found: "${promptPath}"
802
- Searched:
803
- ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
804
- );
805
- }
806
- const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
807
- if (resolvedConfig.cache !== false) {
808
- promptCache.set(sourceFile, asset);
809
- }
810
- return asset;
811
- }
812
- throw new Error(`Prompt not found: "${promptPath}"`);
813
- }
814
- async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
815
- const resolvedConfig = withPromptResolutionDefaults(config);
816
- let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
817
- const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
818
- if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
819
- asset = await resolveIncludes(asset, sourceFile);
820
- }
821
- asset = applyOverrides(asset, {
822
- environment: options.environment,
823
- tier: options.tier,
824
- runtime: options.runtime
825
- });
826
- return asset;
827
- }
828
- function resolveInlinePromptSource(source, options = {}) {
829
- const { asset } = parsePrompt(source);
830
- return applyOverrides(asset, {
831
- environment: options.environment,
832
- tier: options.tier,
833
- runtime: options.runtime
834
- });
835
- }
836
-
837
- // src/context.ts
838
- var textEncoder = new TextEncoder();
839
- var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
840
- var REJECT_SECRETS_FLAGS = "i";
841
- function getContextInputs(asset) {
842
- return (asset.context?.inputs ?? []).map(normalizeContextInput);
843
- }
844
- function normalizeContextInput(input) {
845
- if (typeof input === "string") {
846
- return { name: input };
847
- }
848
- return {
849
- name: input.name,
850
- max_size: input.max_size,
851
- trim: input.trim,
852
- allow_regex: normalizeContextRegex(input.allow_regex),
853
- deny_regex: normalizeContextRegex(input.deny_regex),
854
- non_empty: normalizeBuiltInValidator(input.non_empty),
855
- reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
856
- };
857
- }
858
- function normalizeContextRegex(value) {
859
- if (value === void 0) {
860
- return void 0;
861
- }
862
- if (typeof value === "string") {
863
- const literal = parseRegexLiteral(value);
864
- if (value.startsWith("/") && !literal) {
865
- return {
866
- pattern: value,
867
- flags: "",
868
- raw: value,
869
- invalidLiteral: true
870
- };
871
- }
872
- return {
873
- pattern: literal?.pattern ?? value,
874
- flags: literal?.flags ?? "",
875
- raw: value
876
- };
877
- }
878
- return {
879
- pattern: value.pattern,
880
- flags: value.flags ?? "",
881
- raw: JSON.stringify(value),
882
- returnMessage: value.return_message
883
- };
884
- }
885
- function normalizeBuiltInValidator(value) {
886
- if (value === void 0 || value === false) {
887
- return void 0;
888
- }
889
- if (value === true) {
890
- return {};
891
- }
892
- return {
893
- returnMessage: value.return_message
1207
+ function mergeRaw(base, local) {
1208
+ const merged = {
1209
+ ...base ?? {},
1210
+ ...local ?? {},
1211
+ openai: mergeRecordBlock2(base?.openai, local?.openai),
1212
+ "openai-responses": mergeRecordBlock2(base?.["openai-responses"], local?.["openai-responses"]),
1213
+ openai_responses: mergeRecordBlock2(base?.openai_responses, local?.openai_responses),
1214
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1215
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1216
+ google: mergeRecordBlock2(base?.google, local?.google),
1217
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1218
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
894
1219
  };
1220
+ removeEmptyProviderBlocks(merged);
1221
+ return Object.keys(merged).length > 0 ? merged : void 0;
895
1222
  }
896
- function parseRegexLiteral(value) {
897
- if (!value.startsWith("/")) {
898
- return void 0;
899
- }
900
- for (let index = value.length - 1; index > 0; index -= 1) {
901
- if (value[index] !== "/") {
902
- continue;
903
- }
904
- let backslashCount = 0;
905
- for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
906
- backslashCount += 1;
907
- }
908
- if (backslashCount % 2 === 1) {
909
- continue;
910
- }
911
- return {
912
- pattern: value.slice(1, index),
913
- flags: value.slice(index + 1)
914
- };
915
- }
916
- return void 0;
917
- }
918
- function formatInvalidContextRegexMessage(details) {
919
- return [
920
- `Invalid context regex for prompt "${details.promptId}"`,
921
- `variable "${details.variable}"`,
922
- `field "${details.field}"`,
923
- `value ${JSON.stringify(details.raw)}: ${details.reason}`
924
- ].join(", ");
925
- }
926
- function compileContextRegex(regex, details) {
927
- if (regex.invalidLiteral) {
928
- throw new Error(
929
- `POK013: ${formatInvalidContextRegexMessage({
930
- ...details,
931
- raw: regex.raw,
932
- reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
933
- })}`
934
- );
935
- }
936
- try {
937
- return new RegExp(regex.pattern, regex.flags);
938
- } catch (error) {
939
- const reason = error instanceof Error ? error.message : String(error);
940
- throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
941
- }
942
- }
943
- function getRejectSecretsRegex() {
944
- return {
945
- pattern: REJECT_SECRETS_PATTERN,
946
- flags: REJECT_SECRETS_FLAGS,
947
- raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
1223
+ function mergeProviderOptions(base, local) {
1224
+ const merged = {
1225
+ ...base ?? {},
1226
+ ...local ?? {},
1227
+ anthropic: mergeRecordBlock2(base?.anthropic, local?.anthropic),
1228
+ gemini: mergeRecordBlock2(base?.gemini, local?.gemini),
1229
+ openrouter: mergeRecordBlock2(base?.openrouter, local?.openrouter),
1230
+ llmasaservice: mergeRecordBlock2(base?.llmasaservice, local?.llmasaservice)
948
1231
  };
1232
+ removeEmptyProviderBlocks(merged);
1233
+ return Object.keys(merged).length > 0 ? merged : void 0;
949
1234
  }
950
- function isTrimEnabled(mode) {
951
- return mode === true || mode === "start" || mode === "end" || mode === "both";
952
- }
953
- function normalizeTrimMode(mode) {
954
- if (mode === "start") {
955
- return "start";
956
- }
957
- return "end";
958
- }
959
- function trimToMaxSize(value, maxSize, mode) {
960
- const measured = measureContextValueSize(value);
961
- if (measured <= maxSize) {
962
- return value;
1235
+ function mergeOverrideMap(base, local) {
1236
+ const merged = { ...base ?? {} };
1237
+ for (const [name, localOverride] of Object.entries(local ?? {})) {
1238
+ merged[name] = mergeOverrideConfig(merged[name], localOverride);
963
1239
  }
964
- const characters = Array.from(value);
965
- const normalizedMode = normalizeTrimMode(mode);
966
- if (normalizedMode === "start") {
967
- let collected2 = "";
968
- let size2 = 0;
969
- for (let i = characters.length - 1; i >= 0; i -= 1) {
970
- const next = characters[i];
971
- const charSize = measureContextValueSize(next);
972
- if (size2 + charSize > maxSize) {
973
- break;
974
- }
975
- collected2 = next + collected2;
976
- size2 += charSize;
1240
+ return Object.keys(merged).length > 0 ? merged : void 0;
1241
+ }
1242
+ function mergeOverrideConfig(base, local) {
1243
+ const merged = {
1244
+ ...base ?? {},
1245
+ ...local
1246
+ };
1247
+ const reasoning = mergeRecordBlock2(base?.reasoning, local.reasoning);
1248
+ const sampling = mergeRecordBlock2(base?.sampling, local.sampling);
1249
+ const response = mergeRecordBlock2(base?.response, local.response);
1250
+ const cache = mergeCache(base?.cache, local.cache);
1251
+ const raw = mergeRaw(base?.raw, local.raw);
1252
+ const providerOptions = mergeProviderOptions(base?.provider_options, local.provider_options);
1253
+ if (reasoning !== void 0) merged.reasoning = reasoning;
1254
+ if (sampling !== void 0) merged.sampling = sampling;
1255
+ if (response !== void 0) merged.response = response;
1256
+ if (cache !== void 0) merged.cache = cache;
1257
+ if (raw !== void 0) merged.raw = raw;
1258
+ if (providerOptions !== void 0) merged.provider_options = providerOptions;
1259
+ return merged;
1260
+ }
1261
+ function removeEmptyProviderBlocks(value) {
1262
+ for (const key of Object.keys(value)) {
1263
+ const block = value[key];
1264
+ if (block === void 0) {
1265
+ delete value[key];
1266
+ continue;
977
1267
  }
978
- return collected2;
979
- }
980
- let collected = "";
981
- let size = 0;
982
- for (const char of characters) {
983
- const charSize = measureContextValueSize(char);
984
- if (size + charSize > maxSize) {
985
- break;
1268
+ if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
1269
+ delete value[key];
986
1270
  }
987
- collected += char;
988
- size += charSize;
989
1271
  }
990
- return collected;
991
1272
  }
992
- function sanitizeContextVariables(asset, variables = {}, options = {}) {
993
- const { onContextOverflow } = options;
994
- const sanitized = { ...variables };
995
- for (const input of getContextInputs(asset)) {
996
- const value = sanitized[input.name];
997
- if (value === void 0) {
998
- continue;
999
- }
1000
- let candidate = value;
1001
- if (input.max_size !== void 0) {
1002
- const actualSize = measureContextValueSize(candidate);
1003
- if (actualSize > input.max_size && onContextOverflow) {
1004
- candidate = onContextOverflow({
1005
- promptId: asset.id,
1006
- variable: input.name,
1007
- value: candidate,
1008
- maxSize: input.max_size,
1009
- actualSize
1010
- });
1011
- }
1273
+
1274
+ // src/composition/resolve-includes.ts
1275
+ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
1276
+ if (!asset.includes || asset.includes.length === 0) {
1277
+ return asset;
1278
+ }
1279
+ const baseDir = (0, import_node_path3.dirname)(basePath);
1280
+ const resolvedPath = (0, import_node_path3.resolve)(basePath);
1281
+ if (visited.has(resolvedPath)) {
1282
+ throw new Error(`Circular include detected: ${resolvedPath}`);
1283
+ }
1284
+ visited.add(resolvedPath);
1285
+ let mergedSystemInstructions = "";
1286
+ for (const includePath of asset.includes) {
1287
+ const fullPath = (0, import_node_path3.resolve)(baseDir, includePath);
1288
+ if (visited.has(fullPath)) {
1289
+ throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
1012
1290
  }
1013
- if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
1014
- candidate = trimToMaxSize(candidate, input.max_size, input.trim);
1291
+ const content = await (0, import_promises3.readFile)(fullPath, "utf-8");
1292
+ const { asset: includedAsset } = parsePrompt(content, fullPath);
1293
+ const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
1294
+ if (resolved.sections?.system_instructions) {
1295
+ mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
1015
1296
  }
1016
- sanitized[input.name] = candidate;
1017
- if (input.allow_regex) {
1018
- const candidate2 = sanitized[input.name];
1019
- const matcher = compileContextRegex(input.allow_regex, {
1020
- promptId: asset.id,
1021
- variable: input.name,
1022
- field: "allow_regex"
1023
- });
1024
- if (!matcher.test(candidate2)) {
1025
- if (input.allow_regex.returnMessage) {
1026
- return {
1027
- variables: sanitized,
1028
- shortCircuit: {
1029
- returnMessage: input.allow_regex.returnMessage,
1030
- code: "POK031",
1031
- variable: input.name,
1032
- field: "allow_regex"
1033
- }
1034
- };
1297
+ }
1298
+ const localSystem = asset.sections?.system_instructions ?? "";
1299
+ const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
1300
+ return {
1301
+ ...asset,
1302
+ sections: {
1303
+ ...asset.sections,
1304
+ system_instructions: combinedSystem
1305
+ },
1306
+ // Drop includes from the resolved asset — they've been inlined
1307
+ includes: void 0
1308
+ };
1309
+ }
1310
+
1311
+ // src/prompt-resolution.ts
1312
+ var DEFAULT_PROMPTS_DIR = "./prompts";
1313
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
1314
+ function withPromptResolutionDefaults(config) {
1315
+ return {
1316
+ ...config,
1317
+ sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
1318
+ compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
1319
+ };
1320
+ }
1321
+ var sharedPromptCache = new PromptCache();
1322
+ async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
1323
+ const resolvedConfig = withPromptResolutionDefaults(config);
1324
+ const mode = resolvedConfig.mode ?? "auto";
1325
+ if (mode !== "source-only" && resolvedConfig.compiledDir) {
1326
+ const compiledFile = (0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
1327
+ if ((0, import_node_fs2.existsSync)(compiledFile)) {
1328
+ if (mode === "auto") {
1329
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1330
+ if ((0, import_node_fs2.existsSync)(sourceFile)) {
1331
+ const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
1332
+ const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
1333
+ if (sourceMtime > compiledMtime) {
1334
+ console.warn(
1335
+ `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
1336
+ Run "promptopskit compile" or switch to source-only mode.`
1337
+ );
1338
+ }
1035
1339
  }
1036
- throw new Error(
1037
- `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
1038
- );
1039
1340
  }
1341
+ const content = await (0, import_promises4.readFile)(compiledFile, "utf-8");
1342
+ return JSON.parse(content);
1040
1343
  }
1041
- if (input.deny_regex) {
1042
- const candidate2 = sanitized[input.name];
1043
- const matcher = compileContextRegex(input.deny_regex, {
1044
- promptId: asset.id,
1045
- variable: input.name,
1046
- field: "deny_regex"
1047
- });
1048
- if (matcher.test(candidate2)) {
1049
- if (input.deny_regex.returnMessage) {
1050
- return {
1051
- variables: sanitized,
1052
- shortCircuit: {
1053
- returnMessage: input.deny_regex.returnMessage,
1054
- code: "POK032",
1055
- variable: input.name,
1056
- field: "deny_regex"
1057
- }
1058
- };
1059
- }
1060
- throw new Error(
1061
- `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
1062
- );
1344
+ if (mode === "compiled-only") {
1345
+ throw new Error(
1346
+ `Compiled artifact not found: ${compiledFile}
1347
+ Run "promptopskit compile" to generate it.`
1348
+ );
1349
+ }
1350
+ }
1351
+ if (mode !== "compiled-only") {
1352
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1353
+ if (resolvedConfig.cache !== false) {
1354
+ const cached = promptCache.get(sourceFile);
1355
+ if (cached) {
1356
+ return cached;
1063
1357
  }
1064
1358
  }
1065
- if (input.non_empty && candidate.trim().length === 0) {
1066
- if (input.non_empty.returnMessage) {
1067
- return {
1068
- variables: sanitized,
1069
- shortCircuit: {
1070
- returnMessage: input.non_empty.returnMessage,
1071
- code: "POK033",
1072
- variable: input.name,
1073
- field: "non_empty"
1074
- }
1075
- };
1359
+ if (!(0, import_node_fs2.existsSync)(sourceFile)) {
1360
+ const paths = [sourceFile];
1361
+ if (resolvedConfig.compiledDir) {
1362
+ paths.unshift((0, import_node_path4.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
1076
1363
  }
1077
1364
  throw new Error(
1078
- `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
1365
+ `Prompt not found: "${promptPath}"
1366
+ Searched:
1367
+ ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
1079
1368
  );
1080
1369
  }
1081
- if (input.reject_secrets) {
1082
- const matcher = compileContextRegex(getRejectSecretsRegex(), {
1083
- promptId: asset.id,
1084
- variable: input.name,
1085
- field: "reject_secrets"
1086
- });
1087
- if (matcher.test(candidate)) {
1088
- if (input.reject_secrets.returnMessage) {
1089
- return {
1090
- variables: sanitized,
1091
- shortCircuit: {
1092
- returnMessage: input.reject_secrets.returnMessage,
1093
- code: "POK034",
1094
- variable: input.name,
1095
- field: "reject_secrets"
1096
- }
1097
- };
1098
- }
1099
- throw new Error(
1100
- `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
1101
- );
1102
- }
1370
+ const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
1371
+ if (resolvedConfig.cache !== false) {
1372
+ promptCache.set(sourceFile, asset);
1103
1373
  }
1374
+ return asset;
1104
1375
  }
1105
- return { variables: sanitized };
1376
+ throw new Error(`Prompt not found: "${promptPath}"`);
1106
1377
  }
1107
- function measureContextValueSize(value) {
1108
- return textEncoder.encode(value).length;
1378
+ async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
1379
+ const resolvedConfig = withPromptResolutionDefaults(config);
1380
+ let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
1381
+ const sourceFile = (0, import_node_path4.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
1382
+ if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
1383
+ asset = await resolveIncludes(asset, sourceFile);
1384
+ }
1385
+ asset = applyOverrides(asset, {
1386
+ environment: options.environment,
1387
+ tier: options.tier,
1388
+ runtime: options.runtime
1389
+ });
1390
+ return asset;
1391
+ }
1392
+ function resolveInlinePromptSource(source, options = {}) {
1393
+ const { asset } = parsePrompt(source);
1394
+ return applyOverrides(asset, {
1395
+ environment: options.environment,
1396
+ tier: options.tier,
1397
+ runtime: options.runtime
1398
+ });
1399
+ }
1400
+
1401
+ // src/history.ts
1402
+ function defaultCompactHistory(info) {
1403
+ const content = info.overflow.map((message) => `${message.role}: ${message.content}`).join("\n\n");
1404
+ return {
1405
+ role: "user",
1406
+ content: `Earlier conversation compacted to preserve history:
1407
+
1408
+ ${content}`
1409
+ };
1410
+ }
1411
+ function normalizeCompactionResult(result) {
1412
+ if (typeof result === "string") {
1413
+ return {
1414
+ role: "user",
1415
+ content: result
1416
+ };
1417
+ }
1418
+ return result;
1419
+ }
1420
+ function compactHistoryForPrompt(asset, runtime) {
1421
+ const history = runtime.history;
1422
+ const maxItems = asset.context?.history?.max_items;
1423
+ if (!history || maxItems === void 0 || history.length <= maxItems) {
1424
+ return history;
1425
+ }
1426
+ if (maxItems === 1) {
1427
+ const info2 = {
1428
+ promptId: asset.id,
1429
+ maxItems,
1430
+ overflow: history,
1431
+ preserved: [],
1432
+ history
1433
+ };
1434
+ const compacted2 = runtime.onHistoryCompaction ? normalizeCompactionResult(runtime.onHistoryCompaction(info2)) : defaultCompactHistory(info2);
1435
+ return [compacted2];
1436
+ }
1437
+ const preservedCount = maxItems - 1;
1438
+ const overflow = history.slice(0, history.length - preservedCount);
1439
+ const preserved = history.slice(-preservedCount);
1440
+ const info = {
1441
+ promptId: asset.id,
1442
+ maxItems,
1443
+ overflow,
1444
+ preserved,
1445
+ history
1446
+ };
1447
+ const compacted = runtime.onHistoryCompaction ? normalizeCompactionResult(runtime.onHistoryCompaction(info)) : defaultCompactHistory(info);
1448
+ return [compacted, ...preserved];
1109
1449
  }
1110
1450
 
1111
1451
  // src/providers/prompt-input.ts
@@ -1143,7 +1483,8 @@ function withPromptInputSupport(adapter) {
1143
1483
  }
1144
1484
  return adapter.render(resolved, {
1145
1485
  ...runtime,
1146
- variables: sanitization.variables
1486
+ variables: sanitization.variables,
1487
+ history: compactHistoryForPrompt(resolved, runtime)
1147
1488
  });
1148
1489
  };
1149
1490
  return {
@@ -1197,8 +1538,9 @@ var openaiResponsesAdapter = withPromptInputSupport({
1197
1538
  });
1198
1539
  const responseOptions = runtime.openaiResponses;
1199
1540
  const input = [];
1200
- if (runtime.history) {
1201
- for (const msg of runtime.history) {
1541
+ const history = compactHistoryForPrompt(resolvedAsset, runtime);
1542
+ if (history) {
1543
+ for (const msg of history) {
1202
1544
  input.push({ role: msg.role, content: msg.content });
1203
1545
  }
1204
1546
  }