promptopskit 0.5.0 → 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.
- package/README.md +9 -7
- package/SKILL.md +16 -10
- package/dist/{chunk-6VLKZCNS.js → chunk-55B7QQBK.js} +2 -2
- package/dist/{chunk-6BOUQVSX.js → chunk-FNCIBZJF.js} +3 -3
- package/dist/{chunk-D75HKF64.js → chunk-GLT3YYWK.js} +2 -2
- package/dist/{chunk-IN4KT5EU.js → chunk-IPFD5Y3O.js} +3 -3
- package/dist/{chunk-KN7H4JRK.js → chunk-JR7252DG.js} +393 -386
- package/dist/chunk-JR7252DG.js.map +1 -0
- package/dist/{chunk-K6ZU3CGV.js → chunk-UY5OKMVP.js} +281 -17
- package/dist/chunk-UY5OKMVP.js.map +1 -0
- package/dist/{chunk-QK3WKE3K.js → chunk-VMLGUI4F.js} +2 -2
- package/dist/{chunk-LNGHGGJN.js → chunk-XEZJZ3IG.js} +2 -2
- package/dist/cli/index.js +410 -103
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +971 -660
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +55 -15
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +675 -407
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +2 -2
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +3 -3
- package/dist/providers/gemini.cjs +675 -407
- package/dist/providers/gemini.cjs.map +1 -1
- package/dist/providers/gemini.d.cts +2 -2
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +3 -3
- package/dist/providers/llmasaservice.cjs +675 -407
- package/dist/providers/llmasaservice.cjs.map +1 -1
- package/dist/providers/llmasaservice.d.cts +2 -2
- package/dist/providers/llmasaservice.d.ts +2 -2
- package/dist/providers/llmasaservice.js +4 -4
- package/dist/providers/openai-responses.cjs +675 -407
- package/dist/providers/openai-responses.cjs.map +1 -1
- package/dist/providers/openai-responses.d.cts +2 -2
- package/dist/providers/openai-responses.d.ts +2 -2
- package/dist/providers/openai-responses.js +3 -3
- package/dist/providers/openai.cjs +675 -407
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.cjs +675 -407
- package/dist/providers/openrouter.cjs.map +1 -1
- package/dist/providers/openrouter.d.cts +2 -2
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/openrouter.js +4 -4
- package/dist/{schema-D-RI4w-k.d.cts → schema-CuYsv8JA.d.cts} +540 -206
- package/dist/{schema-D-RI4w-k.d.ts → schema-CuYsv8JA.d.ts} +540 -206
- package/dist/testing.cjs +43 -5
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-B3dxBGu6.d.ts → types-DpK0mTHL.d.ts} +1 -1
- package/dist/{types-DbTAjlTN.d.cts → types-UA_p9uJy.d.cts} +1 -1
- package/dist/usagetap/index.d.cts +2 -2
- package/dist/usagetap/index.d.ts +2 -2
- package/package.json +9 -2
- package/dist/chunk-K6ZU3CGV.js.map +0 -1
- package/dist/chunk-KN7H4JRK.js.map +0 -1
- /package/dist/{chunk-6VLKZCNS.js.map → chunk-55B7QQBK.js.map} +0 -0
- /package/dist/{chunk-6BOUQVSX.js.map → chunk-FNCIBZJF.js.map} +0 -0
- /package/dist/{chunk-D75HKF64.js.map → chunk-GLT3YYWK.js.map} +0 -0
- /package/dist/{chunk-IN4KT5EU.js.map → chunk-IPFD5Y3O.js.map} +0 -0
- /package/dist/{chunk-QK3WKE3K.js.map → chunk-VMLGUI4F.js.map} +0 -0
- /package/dist/{chunk-LNGHGGJN.js.map → chunk-XEZJZ3IG.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -72,7 +72,7 @@ __export(src_exports, {
|
|
|
72
72
|
withUsageTapCall: () => withUsageTapCall
|
|
73
73
|
});
|
|
74
74
|
module.exports = __toCommonJS(src_exports);
|
|
75
|
-
var
|
|
75
|
+
var import_node_path5 = require("path");
|
|
76
76
|
|
|
77
77
|
// src/renderer/interpolate.ts
|
|
78
78
|
var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
@@ -80,12 +80,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
|
|
|
80
80
|
var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
|
|
81
81
|
function interpolate(template, variables, options = {}) {
|
|
82
82
|
const { strict = false } = options;
|
|
83
|
+
const optionalVariables = new Set(options.optionalVariables ?? []);
|
|
83
84
|
let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
|
|
84
85
|
result = result.replace(VARIABLE_RE, (match, name) => {
|
|
85
86
|
if (name in variables) {
|
|
86
87
|
return variables[name];
|
|
87
88
|
}
|
|
88
|
-
if (strict) {
|
|
89
|
+
if (strict && !optionalVariables.has(name)) {
|
|
89
90
|
throw new Error(`Missing required variable: "${name}"`);
|
|
90
91
|
}
|
|
91
92
|
return match;
|
|
@@ -103,159 +104,463 @@ function extractVariables(template) {
|
|
|
103
104
|
return [...vars];
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
// src/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
asset.sections.system_instructions,
|
|
113
|
-
variables,
|
|
114
|
-
{ strict }
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
if (asset.sections.prompt_template) {
|
|
118
|
-
result.prompt_template = interpolate(
|
|
119
|
-
asset.sections.prompt_template,
|
|
120
|
-
variables,
|
|
121
|
-
{ strict }
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
return result;
|
|
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);
|
|
125
113
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
let result = { ...asset };
|
|
130
|
-
if (options.environment && result.environments?.[options.environment]) {
|
|
131
|
-
result = mergeOverride(result, result.environments[options.environment]);
|
|
114
|
+
function normalizeContextInput(input) {
|
|
115
|
+
if (typeof input === "string") {
|
|
116
|
+
return { name: input };
|
|
132
117
|
}
|
|
133
|
-
|
|
134
|
-
|
|
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;
|
|
135
136
|
}
|
|
136
|
-
if (
|
|
137
|
-
|
|
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
|
+
};
|
|
138
152
|
}
|
|
139
|
-
return
|
|
153
|
+
return {
|
|
154
|
+
pattern: value.pattern,
|
|
155
|
+
flags: value.flags ?? "",
|
|
156
|
+
raw: JSON.stringify(value),
|
|
157
|
+
returnMessage: value.return_message
|
|
158
|
+
};
|
|
140
159
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
|
|
145
|
-
if (override.tools !== void 0) result.tools = override.tools;
|
|
146
|
-
if (override.reasoning !== void 0) {
|
|
147
|
-
result.reasoning = { ...result.reasoning, ...override.reasoning };
|
|
160
|
+
function normalizeBuiltInValidator(value) {
|
|
161
|
+
if (value === void 0 || value === false) {
|
|
162
|
+
return void 0;
|
|
148
163
|
}
|
|
149
|
-
if (
|
|
150
|
-
|
|
164
|
+
if (value === true) {
|
|
165
|
+
return {};
|
|
151
166
|
}
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
return {
|
|
168
|
+
returnMessage: value.return_message
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function parseRegexLiteral(value) {
|
|
172
|
+
if (!value.startsWith("/")) {
|
|
173
|
+
return void 0;
|
|
154
174
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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)
|
|
163
189
|
};
|
|
164
190
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
+
);
|
|
178
210
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
...result.provider_options?.anthropic,
|
|
185
|
-
...override.provider_options.anthropic
|
|
186
|
-
},
|
|
187
|
-
gemini: {
|
|
188
|
-
...result.provider_options?.gemini,
|
|
189
|
-
...override.provider_options.gemini
|
|
190
|
-
},
|
|
191
|
-
openrouter: {
|
|
192
|
-
...result.provider_options?.openrouter,
|
|
193
|
-
...override.provider_options.openrouter
|
|
194
|
-
},
|
|
195
|
-
llmasaservice: {
|
|
196
|
-
...result.provider_options?.llmasaservice,
|
|
197
|
-
...override.provider_options.llmasaservice
|
|
198
|
-
}
|
|
199
|
-
};
|
|
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 })}`);
|
|
200
216
|
}
|
|
201
|
-
return result;
|
|
202
217
|
}
|
|
203
|
-
function
|
|
204
|
-
|
|
205
|
-
|
|
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
|
+
};
|
|
206
224
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
225
|
+
function isTrimEnabled(mode) {
|
|
226
|
+
return mode === true || mode === "start" || mode === "end" || mode === "both";
|
|
227
|
+
}
|
|
228
|
+
function normalizeTrimMode(mode) {
|
|
229
|
+
if (mode === "start") {
|
|
230
|
+
return "start";
|
|
212
231
|
}
|
|
213
|
-
return
|
|
214
|
-
environment: runtime.environment,
|
|
215
|
-
tier: runtime.tier,
|
|
216
|
-
runtime: runtime.runtime
|
|
217
|
-
});
|
|
232
|
+
return "end";
|
|
218
233
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
var import_node_path3 = require("path");
|
|
224
|
-
|
|
225
|
-
// src/cache.ts
|
|
226
|
-
var import_node_fs = require("fs");
|
|
227
|
-
var PromptCache = class {
|
|
228
|
-
cache = /* @__PURE__ */ new Map();
|
|
229
|
-
maxSize;
|
|
230
|
-
constructor(maxSize = 100) {
|
|
231
|
-
this.maxSize = maxSize;
|
|
234
|
+
function trimToMaxSize(value, maxSize, mode) {
|
|
235
|
+
const measured = measureContextValueSize(value);
|
|
236
|
+
if (measured <= maxSize) {
|
|
237
|
+
return value;
|
|
232
238
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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;
|
|
241
249
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return void 0;
|
|
250
|
+
collected2 = next + collected2;
|
|
251
|
+
size2 += charSize;
|
|
245
252
|
}
|
|
246
|
-
|
|
247
|
-
this.cache.set(filePath, entry);
|
|
248
|
-
return entry.value;
|
|
253
|
+
return collected2;
|
|
249
254
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
+
}
|
|
259
564
|
this.cache.set(filePath, { value, mtime: stat.mtimeMs });
|
|
260
565
|
} catch {
|
|
261
566
|
}
|
|
@@ -269,8 +574,8 @@ var PromptCache = class {
|
|
|
269
574
|
};
|
|
270
575
|
|
|
271
576
|
// src/composition/resolve-includes.ts
|
|
272
|
-
var
|
|
273
|
-
var
|
|
577
|
+
var import_promises3 = require("fs/promises");
|
|
578
|
+
var import_node_path3 = require("path");
|
|
274
579
|
|
|
275
580
|
// src/parser/parser.ts
|
|
276
581
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
@@ -306,10 +611,26 @@ var ResponseSchema = import_zod.z.object({
|
|
|
306
611
|
format: import_zod.z.enum(["text", "json", "markdown"]).optional(),
|
|
307
612
|
stream: import_zod.z.boolean().optional(),
|
|
308
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(),
|
|
309
621
|
schema_name: import_zod.z.string().optional(),
|
|
310
622
|
schema_description: import_zod.z.string().optional(),
|
|
311
623
|
schema_strict: import_zod.z.boolean().optional()
|
|
312
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
|
+
});
|
|
313
634
|
var AnthropicProviderOptionsSchema = import_zod.z.object({
|
|
314
635
|
top_k: import_zod.z.number().int().min(0).optional(),
|
|
315
636
|
tool_choice: import_zod.z.record(import_zod.z.unknown()).optional(),
|
|
@@ -401,6 +722,8 @@ var ContextBuiltInValidatorSchema = import_zod.z.union([
|
|
|
401
722
|
]);
|
|
402
723
|
var ContextInputDefinitionObjectSchema = import_zod.z.object({
|
|
403
724
|
name: import_zod.z.string(),
|
|
725
|
+
optional: import_zod.z.boolean().optional(),
|
|
726
|
+
warnings: import_zod.z.boolean().optional(),
|
|
404
727
|
max_size: import_zod.z.number().int().positive().optional(),
|
|
405
728
|
trim: import_zod.z.union([import_zod.z.boolean(), import_zod.z.enum(["start", "end", "both"])]).optional(),
|
|
406
729
|
allow_regex: ContextRegexSchema.optional(),
|
|
@@ -430,7 +753,7 @@ var PromptAssetOverridesSchema = import_zod.z.object({
|
|
|
430
753
|
fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
|
|
431
754
|
reasoning: ReasoningSchema.optional(),
|
|
432
755
|
sampling: SamplingSchema.optional(),
|
|
433
|
-
response:
|
|
756
|
+
response: ResponseSchemaWithValidation.optional(),
|
|
434
757
|
cache: CacheSchema.optional(),
|
|
435
758
|
raw: RawProviderBodySchema.optional(),
|
|
436
759
|
tools: import_zod.z.array(ToolRefSchema).optional(),
|
|
@@ -448,7 +771,19 @@ var SectionsSchema = import_zod.z.object({
|
|
|
448
771
|
var PromptDefaultsSchema = import_zod.z.object({
|
|
449
772
|
provider: import_zod.z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
|
|
450
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(),
|
|
451
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(),
|
|
452
787
|
metadata: MetadataSchema.optional(),
|
|
453
788
|
sections: import_zod.z.object({
|
|
454
789
|
system_instructions: import_zod.z.string().optional()
|
|
@@ -463,7 +798,7 @@ var PromptAssetSchema = import_zod.z.object({
|
|
|
463
798
|
fallback_models: import_zod.z.array(import_zod.z.string()).optional(),
|
|
464
799
|
reasoning: ReasoningSchema.optional(),
|
|
465
800
|
sampling: SamplingSchema.optional(),
|
|
466
|
-
response:
|
|
801
|
+
response: ResponseSchemaWithValidation.optional(),
|
|
467
802
|
cache: CacheSchema.optional(),
|
|
468
803
|
raw: RawProviderBodySchema.optional(),
|
|
469
804
|
tools: import_zod.z.array(ToolRefSchema).optional(),
|
|
@@ -640,567 +975,503 @@ function parsePrompt(content, filePath) {
|
|
|
640
975
|
const { data: frontMatter, content: body } = (0, import_gray_matter.default)(content);
|
|
641
976
|
const sections = extractSections(body);
|
|
642
977
|
const raw = {
|
|
643
|
-
...frontMatter,
|
|
644
|
-
sections,
|
|
645
|
-
source: filePath ? { file_path: filePath } : void 0
|
|
646
|
-
};
|
|
647
|
-
const asset = PromptAssetSchema.parse(raw);
|
|
648
|
-
return {
|
|
649
|
-
asset,
|
|
650
|
-
raw: {
|
|
651
|
-
frontMatter,
|
|
652
|
-
body
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// src/parser/loader.ts
|
|
658
|
-
var import_promises = require("fs/promises");
|
|
659
|
-
var import_node_path = require("path");
|
|
660
|
-
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
661
|
-
var DEFAULTS_FILE_NAME = "defaults.md";
|
|
662
|
-
async function loadPromptFile(filePath, options = {}) {
|
|
663
|
-
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
664
|
-
const parsed = parsePrompt(content, filePath);
|
|
665
|
-
const root = options.defaultsRoot ?? (0, import_node_path.dirname)(filePath);
|
|
666
|
-
const defaults = await loadDefaultsForPath(filePath, root);
|
|
667
|
-
const asset = applyDefaults(parsed.asset, defaults);
|
|
668
|
-
return {
|
|
669
|
-
...parsed,
|
|
670
|
-
asset
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
async function loadDefaultsForPath(filePath, defaultsRoot) {
|
|
674
|
-
const directories = getDirectoriesToCheck(filePath, defaultsRoot);
|
|
675
|
-
let merged = {};
|
|
676
|
-
for (const dir of directories) {
|
|
677
|
-
const defaultsPath = (0, import_node_path.join)(dir, DEFAULTS_FILE_NAME);
|
|
678
|
-
try {
|
|
679
|
-
const defaultsContent = await (0, import_promises.readFile)(defaultsPath, "utf-8");
|
|
680
|
-
const defaults = parseDefaults(defaultsContent);
|
|
681
|
-
merged = mergeDefaults(merged, defaults);
|
|
682
|
-
} catch (error) {
|
|
683
|
-
if (error.code !== "ENOENT") {
|
|
684
|
-
throw error;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return merged;
|
|
689
|
-
}
|
|
690
|
-
function getDirectoriesToCheck(filePath, defaultsRoot) {
|
|
691
|
-
const dirs = [];
|
|
692
|
-
let current = (0, import_node_path.resolve)((0, import_node_path.dirname)(filePath));
|
|
693
|
-
const boundary = defaultsRoot ? (0, import_node_path.resolve)(defaultsRoot) : void 0;
|
|
694
|
-
while (true) {
|
|
695
|
-
dirs.unshift(current);
|
|
696
|
-
if (boundary && current === boundary || current === (0, import_node_path.dirname)(current)) {
|
|
697
|
-
break;
|
|
698
|
-
}
|
|
699
|
-
current = (0, import_node_path.dirname)(current);
|
|
700
|
-
}
|
|
701
|
-
return dirs;
|
|
702
|
-
}
|
|
703
|
-
function parseDefaults(content) {
|
|
704
|
-
const { data: frontMatter, content: body } = (0, import_gray_matter2.default)(content);
|
|
705
|
-
const sections = extractSections(body);
|
|
706
|
-
return PromptDefaultsSchema.parse({
|
|
707
|
-
...frontMatter,
|
|
708
|
-
sections: {
|
|
709
|
-
system_instructions: sections.system_instructions
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
function mergeDefaults(base, local) {
|
|
714
|
-
return {
|
|
715
|
-
provider: local.provider ?? base.provider,
|
|
716
|
-
model: local.model ?? base.model,
|
|
717
|
-
cache: mergeCache(base.cache, local.cache),
|
|
718
|
-
metadata: {
|
|
719
|
-
...base.metadata ?? {},
|
|
720
|
-
...local.metadata ?? {}
|
|
721
|
-
},
|
|
722
|
-
sections: {
|
|
723
|
-
...base.sections ?? {},
|
|
724
|
-
...local.sections ?? {}
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
function applyDefaults(asset, defaults) {
|
|
729
|
-
const cache = mergeCache(defaults.cache, asset.cache);
|
|
730
|
-
const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
|
|
731
|
-
const hasDefaultSystem = !!defaults.sections?.system_instructions;
|
|
732
|
-
const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
|
|
733
|
-
const hasDefaultCache = cache !== void 0;
|
|
734
|
-
if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
|
|
735
|
-
return asset;
|
|
736
|
-
}
|
|
737
|
-
const mergedMetadata = {
|
|
738
|
-
...defaults.metadata ?? {},
|
|
739
|
-
...asset.metadata ?? {}
|
|
740
|
-
};
|
|
741
|
-
const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
|
|
742
|
-
const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
|
|
743
|
-
const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
|
|
744
|
-
return {
|
|
745
|
-
...asset,
|
|
746
|
-
provider: asset.provider ?? defaults.provider,
|
|
747
|
-
model: asset.model ?? defaults.model,
|
|
748
|
-
cache,
|
|
749
|
-
metadata,
|
|
750
|
-
sections
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
function mergeCache(base, local) {
|
|
754
|
-
const merged = {
|
|
755
|
-
...base ?? {},
|
|
756
|
-
...local ?? {},
|
|
757
|
-
openai: {
|
|
758
|
-
...base?.openai ?? {},
|
|
759
|
-
...local?.openai ?? {}
|
|
760
|
-
},
|
|
761
|
-
anthropic: {
|
|
762
|
-
...base?.anthropic ?? {},
|
|
763
|
-
...local?.anthropic ?? {}
|
|
764
|
-
},
|
|
765
|
-
gemini: {
|
|
766
|
-
...base?.gemini ?? {},
|
|
767
|
-
...local?.gemini ?? {}
|
|
768
|
-
},
|
|
769
|
-
google: {
|
|
770
|
-
...base?.google ?? {},
|
|
771
|
-
...local?.google ?? {}
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
|
|
775
|
-
if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
|
|
776
|
-
if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
|
|
777
|
-
if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
|
|
778
|
-
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// src/composition/resolve-includes.ts
|
|
782
|
-
async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
|
|
783
|
-
if (!asset.includes || asset.includes.length === 0) {
|
|
784
|
-
return asset;
|
|
785
|
-
}
|
|
786
|
-
const baseDir = (0, import_node_path2.dirname)(basePath);
|
|
787
|
-
const resolvedPath = (0, import_node_path2.resolve)(basePath);
|
|
788
|
-
if (visited.has(resolvedPath)) {
|
|
789
|
-
throw new Error(`Circular include detected: ${resolvedPath}`);
|
|
790
|
-
}
|
|
791
|
-
visited.add(resolvedPath);
|
|
792
|
-
let mergedSystemInstructions = "";
|
|
793
|
-
for (const includePath of asset.includes) {
|
|
794
|
-
const fullPath = (0, import_node_path2.resolve)(baseDir, includePath);
|
|
795
|
-
if (visited.has(fullPath)) {
|
|
796
|
-
throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
|
|
797
|
-
}
|
|
798
|
-
const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
|
|
799
|
-
const { asset: includedAsset } = parsePrompt(content, fullPath);
|
|
800
|
-
const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
|
|
801
|
-
if (resolved.sections?.system_instructions) {
|
|
802
|
-
mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
const localSystem = asset.sections?.system_instructions ?? "";
|
|
806
|
-
const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
|
|
807
|
-
return {
|
|
808
|
-
...asset,
|
|
809
|
-
sections: {
|
|
810
|
-
...asset.sections,
|
|
811
|
-
system_instructions: combinedSystem
|
|
812
|
-
},
|
|
813
|
-
// Drop includes from the resolved asset — they've been inlined
|
|
814
|
-
includes: void 0
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// src/prompt-resolution.ts
|
|
819
|
-
var DEFAULT_PROMPTS_DIR = "./prompts";
|
|
820
|
-
var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
|
|
821
|
-
function withPromptResolutionDefaults(config) {
|
|
822
|
-
return {
|
|
823
|
-
...config,
|
|
824
|
-
sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
|
|
825
|
-
compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
var sharedPromptCache = new PromptCache();
|
|
829
|
-
async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
|
|
830
|
-
const resolvedConfig = withPromptResolutionDefaults(config);
|
|
831
|
-
const mode = resolvedConfig.mode ?? "auto";
|
|
832
|
-
if (mode !== "source-only" && resolvedConfig.compiledDir) {
|
|
833
|
-
const compiledFile = (0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json");
|
|
834
|
-
if ((0, import_node_fs2.existsSync)(compiledFile)) {
|
|
835
|
-
if (mode === "auto") {
|
|
836
|
-
const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
|
|
837
|
-
if ((0, import_node_fs2.existsSync)(sourceFile)) {
|
|
838
|
-
const compiledMtime = (0, import_node_fs2.statSync)(compiledFile).mtimeMs;
|
|
839
|
-
const sourceMtime = (0, import_node_fs2.statSync)(sourceFile).mtimeMs;
|
|
840
|
-
if (sourceMtime > compiledMtime) {
|
|
841
|
-
console.warn(
|
|
842
|
-
`[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
|
|
843
|
-
Run "promptopskit compile" or switch to source-only mode.`
|
|
844
|
-
);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
const content = await (0, import_promises3.readFile)(compiledFile, "utf-8");
|
|
849
|
-
return JSON.parse(content);
|
|
850
|
-
}
|
|
851
|
-
if (mode === "compiled-only") {
|
|
852
|
-
throw new Error(
|
|
853
|
-
`Compiled artifact not found: ${compiledFile}
|
|
854
|
-
Run "promptopskit compile" to generate it.`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
if (mode !== "compiled-only") {
|
|
859
|
-
const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
|
|
860
|
-
if (resolvedConfig.cache !== false) {
|
|
861
|
-
const cached = promptCache.get(sourceFile);
|
|
862
|
-
if (cached) {
|
|
863
|
-
return cached;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
if (!(0, import_node_fs2.existsSync)(sourceFile)) {
|
|
867
|
-
const paths = [sourceFile];
|
|
868
|
-
if (resolvedConfig.compiledDir) {
|
|
869
|
-
paths.unshift((0, import_node_path3.resolve)(resolvedConfig.compiledDir, promptPath + ".json"));
|
|
870
|
-
}
|
|
871
|
-
throw new Error(
|
|
872
|
-
`Prompt not found: "${promptPath}"
|
|
873
|
-
Searched:
|
|
874
|
-
${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
|
|
878
|
-
if (resolvedConfig.cache !== false) {
|
|
879
|
-
promptCache.set(sourceFile, asset);
|
|
880
|
-
}
|
|
881
|
-
return asset;
|
|
882
|
-
}
|
|
883
|
-
throw new Error(`Prompt not found: "${promptPath}"`);
|
|
884
|
-
}
|
|
885
|
-
async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
|
|
886
|
-
const resolvedConfig = withPromptResolutionDefaults(config);
|
|
887
|
-
let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
|
|
888
|
-
const sourceFile = (0, import_node_path3.resolve)(resolvedConfig.sourceDir, promptPath + ".md");
|
|
889
|
-
if (asset.includes && asset.includes.length > 0 && (0, import_node_fs2.existsSync)(sourceFile)) {
|
|
890
|
-
asset = await resolveIncludes(asset, sourceFile);
|
|
891
|
-
}
|
|
892
|
-
asset = applyOverrides(asset, {
|
|
893
|
-
environment: options.environment,
|
|
894
|
-
tier: options.tier,
|
|
895
|
-
runtime: options.runtime
|
|
896
|
-
});
|
|
897
|
-
return asset;
|
|
898
|
-
}
|
|
899
|
-
function resolveInlinePromptSource(source, options = {}) {
|
|
900
|
-
const { asset } = parsePrompt(source);
|
|
901
|
-
return applyOverrides(asset, {
|
|
902
|
-
environment: options.environment,
|
|
903
|
-
tier: options.tier,
|
|
904
|
-
runtime: options.runtime
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// src/context.ts
|
|
909
|
-
var textEncoder = new TextEncoder();
|
|
910
|
-
var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
|
|
911
|
-
var REJECT_SECRETS_FLAGS = "i";
|
|
912
|
-
function getContextInputs(asset) {
|
|
913
|
-
return (asset.context?.inputs ?? []).map(normalizeContextInput);
|
|
914
|
-
}
|
|
915
|
-
function getContextInputNames(asset) {
|
|
916
|
-
return getContextInputs(asset).map((input) => input.name);
|
|
917
|
-
}
|
|
918
|
-
function normalizeContextInput(input) {
|
|
919
|
-
if (typeof input === "string") {
|
|
920
|
-
return { name: input };
|
|
921
|
-
}
|
|
978
|
+
...frontMatter,
|
|
979
|
+
sections,
|
|
980
|
+
source: filePath ? { file_path: filePath } : void 0
|
|
981
|
+
};
|
|
982
|
+
const asset = PromptAssetSchema.parse(raw);
|
|
922
983
|
return {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
non_empty: normalizeBuiltInValidator(input.non_empty),
|
|
929
|
-
reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
|
|
984
|
+
asset,
|
|
985
|
+
raw: {
|
|
986
|
+
frontMatter,
|
|
987
|
+
body
|
|
988
|
+
}
|
|
930
989
|
};
|
|
931
990
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
991
|
+
|
|
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
|
|
998
|
+
var import_promises = require("fs/promises");
|
|
999
|
+
var import_node_crypto = require("crypto");
|
|
1000
|
+
var import_node_path = require("path");
|
|
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})`);
|
|
945
1014
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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`);
|
|
951
1025
|
}
|
|
952
1026
|
return {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
returnMessage: value.return_message
|
|
1027
|
+
schema: parsed,
|
|
1028
|
+
resolvedPath,
|
|
1029
|
+
hash: (0, import_node_crypto.createHash)("sha256").update(raw).digest("hex")
|
|
957
1030
|
};
|
|
958
1031
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
961
|
-
|
|
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;
|
|
962
1043
|
}
|
|
963
|
-
|
|
964
|
-
|
|
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`);
|
|
965
1062
|
}
|
|
966
1063
|
return {
|
|
967
|
-
|
|
1064
|
+
schema: jsonSchema,
|
|
1065
|
+
resolvedPath,
|
|
1066
|
+
hash: (0, import_node_crypto.createHash)("sha256").update(moduleSource).digest("hex")
|
|
968
1067
|
};
|
|
969
1068
|
}
|
|
970
|
-
function
|
|
971
|
-
|
|
972
|
-
|
|
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`);
|
|
973
1075
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
+
}
|
|
977
1089
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// src/parser/loader.ts
|
|
1094
|
+
var DEFAULTS_FILE_NAME = "defaults.md";
|
|
1095
|
+
async function loadPromptFile(filePath, options = {}) {
|
|
1096
|
+
const content = await (0, import_promises2.readFile)(filePath, "utf-8");
|
|
1097
|
+
const parsed = parsePrompt(content, filePath);
|
|
1098
|
+
const root = options.defaultsRoot ?? (0, import_node_path2.dirname)(filePath);
|
|
1099
|
+
const defaults = await loadDefaultsForPath(filePath, root);
|
|
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;
|
|
1109
|
+
return {
|
|
1110
|
+
...parsed,
|
|
1111
|
+
asset
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
async function loadDefaultsForPath(filePath, defaultsRoot) {
|
|
1115
|
+
const directories = getDirectoriesToCheck(filePath, defaultsRoot);
|
|
1116
|
+
let merged = {};
|
|
1117
|
+
for (const dir of directories) {
|
|
1118
|
+
const defaultsPath = (0, import_node_path2.join)(dir, DEFAULTS_FILE_NAME);
|
|
1119
|
+
try {
|
|
1120
|
+
const defaultsContent = await (0, import_promises2.readFile)(defaultsPath, "utf-8");
|
|
1121
|
+
const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
|
|
1122
|
+
merged = mergeDefaults(merged, defaults);
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
if (error.code !== "ENOENT") {
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
981
1127
|
}
|
|
982
|
-
|
|
983
|
-
|
|
1128
|
+
}
|
|
1129
|
+
return merged;
|
|
1130
|
+
}
|
|
1131
|
+
function getDirectoriesToCheck(filePath, defaultsRoot) {
|
|
1132
|
+
const dirs = [];
|
|
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;
|
|
1135
|
+
while (true) {
|
|
1136
|
+
dirs.unshift(current);
|
|
1137
|
+
if (boundary && current === boundary || current === (0, import_node_path2.dirname)(current)) {
|
|
1138
|
+
break;
|
|
984
1139
|
}
|
|
985
|
-
|
|
986
|
-
pattern: value.slice(1, index),
|
|
987
|
-
flags: value.slice(index + 1)
|
|
988
|
-
};
|
|
1140
|
+
current = (0, import_node_path2.dirname)(current);
|
|
989
1141
|
}
|
|
990
|
-
return
|
|
1142
|
+
return dirs;
|
|
991
1143
|
}
|
|
992
|
-
function
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1144
|
+
function parseDefaults(content) {
|
|
1145
|
+
const { data: frontMatter, content: body } = (0, import_gray_matter2.default)(content);
|
|
1146
|
+
const sections = extractSections(body);
|
|
1147
|
+
return PromptDefaultsSchema.parse({
|
|
1148
|
+
...frontMatter,
|
|
1149
|
+
sections: {
|
|
1150
|
+
system_instructions: sections.system_instructions
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
999
1153
|
}
|
|
1000
|
-
function
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
)
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1154
|
+
function mergeDefaults(base, local) {
|
|
1155
|
+
return {
|
|
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
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
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;
|
|
1015
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;
|
|
1206
|
+
return {
|
|
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
|
|
1225
|
+
};
|
|
1016
1226
|
}
|
|
1017
|
-
function
|
|
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
|
+
});
|
|
1018
1235
|
return {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
|
|
1236
|
+
...defaults,
|
|
1237
|
+
includes
|
|
1022
1238
|
};
|
|
1023
1239
|
}
|
|
1024
|
-
function
|
|
1025
|
-
|
|
1240
|
+
function mergeRecordBlock2(base, local) {
|
|
1241
|
+
if (base === void 0) return local;
|
|
1242
|
+
if (local === void 0) return base;
|
|
1243
|
+
return { ...base, ...local };
|
|
1026
1244
|
}
|
|
1027
|
-
function
|
|
1028
|
-
|
|
1029
|
-
return "start";
|
|
1030
|
-
}
|
|
1031
|
-
return "end";
|
|
1245
|
+
function mergeMcp(base, local) {
|
|
1246
|
+
return mergeRecordBlock2(base, local);
|
|
1032
1247
|
}
|
|
1033
|
-
function
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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;
|
|
1255
|
+
}
|
|
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 ?? {}
|
|
1060
1275
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
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;
|
|
1282
|
+
}
|
|
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;
|
|
1298
|
+
}
|
|
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;
|
|
1310
|
+
}
|
|
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);
|
|
1063
1315
|
}
|
|
1064
|
-
return
|
|
1316
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
1065
1317
|
}
|
|
1066
|
-
function
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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];
|
|
1072
1342
|
continue;
|
|
1073
1343
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
const actualSize = measureContextValueSize(candidate);
|
|
1077
|
-
if (actualSize > input.max_size && onContextOverflow) {
|
|
1078
|
-
candidate = onContextOverflow({
|
|
1079
|
-
promptId: asset.id,
|
|
1080
|
-
variable: input.name,
|
|
1081
|
-
value: candidate,
|
|
1082
|
-
maxSize: input.max_size,
|
|
1083
|
-
actualSize
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1344
|
+
if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
|
|
1345
|
+
delete value[key];
|
|
1086
1346
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
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})`);
|
|
1089
1366
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
+
}
|
|
1109
1415
|
}
|
|
1110
|
-
throw new Error(
|
|
1111
|
-
`POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
|
|
1112
|
-
);
|
|
1113
1416
|
}
|
|
1417
|
+
const content = await (0, import_promises4.readFile)(compiledFile, "utf-8");
|
|
1418
|
+
return JSON.parse(content);
|
|
1114
1419
|
}
|
|
1115
|
-
if (
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
code: "POK032",
|
|
1129
|
-
variable: input.name,
|
|
1130
|
-
field: "deny_regex"
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
throw new Error(
|
|
1135
|
-
`POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
|
|
1136
|
-
);
|
|
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;
|
|
1137
1433
|
}
|
|
1138
1434
|
}
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
shortCircuit: {
|
|
1144
|
-
returnMessage: input.non_empty.returnMessage,
|
|
1145
|
-
code: "POK033",
|
|
1146
|
-
variable: input.name,
|
|
1147
|
-
field: "non_empty"
|
|
1148
|
-
}
|
|
1149
|
-
};
|
|
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"));
|
|
1150
1439
|
}
|
|
1151
1440
|
throw new Error(
|
|
1152
|
-
`
|
|
1441
|
+
`Prompt not found: "${promptPath}"
|
|
1442
|
+
Searched:
|
|
1443
|
+
${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
|
|
1153
1444
|
);
|
|
1154
1445
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
variable: input.name,
|
|
1159
|
-
field: "reject_secrets"
|
|
1160
|
-
});
|
|
1161
|
-
if (matcher.test(candidate)) {
|
|
1162
|
-
if (input.reject_secrets.returnMessage) {
|
|
1163
|
-
return {
|
|
1164
|
-
variables: sanitized,
|
|
1165
|
-
shortCircuit: {
|
|
1166
|
-
returnMessage: input.reject_secrets.returnMessage,
|
|
1167
|
-
code: "POK034",
|
|
1168
|
-
variable: input.name,
|
|
1169
|
-
field: "reject_secrets"
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
|
-
throw new Error(
|
|
1174
|
-
`POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1446
|
+
const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
|
|
1447
|
+
if (resolvedConfig.cache !== false) {
|
|
1448
|
+
promptCache.set(sourceFile, asset);
|
|
1177
1449
|
}
|
|
1450
|
+
return asset;
|
|
1178
1451
|
}
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1181
|
-
function measureContextValueSize(value) {
|
|
1182
|
-
return textEncoder.encode(value).length;
|
|
1452
|
+
throw new Error(`Prompt not found: "${promptPath}"`);
|
|
1183
1453
|
}
|
|
1184
|
-
function
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
const value = variables[input.name];
|
|
1191
|
-
if (value === void 0) {
|
|
1192
|
-
continue;
|
|
1193
|
-
}
|
|
1194
|
-
const actualSize = measureContextValueSize(value);
|
|
1195
|
-
if (actualSize > input.max_size) {
|
|
1196
|
-
warnings.push({
|
|
1197
|
-
variable: input.name,
|
|
1198
|
-
maxSize: input.max_size,
|
|
1199
|
-
actualSize
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
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);
|
|
1202
1460
|
}
|
|
1203
|
-
|
|
1461
|
+
asset = applyOverrides(asset, {
|
|
1462
|
+
environment: options.environment,
|
|
1463
|
+
tier: options.tier,
|
|
1464
|
+
runtime: options.runtime
|
|
1465
|
+
});
|
|
1466
|
+
return asset;
|
|
1467
|
+
}
|
|
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
|
+
});
|
|
1204
1475
|
}
|
|
1205
1476
|
|
|
1206
1477
|
// src/history.ts
|
|
@@ -1985,6 +2256,18 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
|
|
|
1985
2256
|
"provider_options",
|
|
1986
2257
|
"raw"
|
|
1987
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
|
+
}
|
|
1988
2271
|
var RISKY_UNBOUNDED_INPUT_NAMES = [
|
|
1989
2272
|
"message",
|
|
1990
2273
|
"prompt",
|
|
@@ -2036,7 +2319,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2036
2319
|
}
|
|
2037
2320
|
}
|
|
2038
2321
|
}
|
|
2039
|
-
const
|
|
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]));
|
|
2040
2325
|
const usedVars = /* @__PURE__ */ new Set();
|
|
2041
2326
|
if (asset.sections?.system_instructions) {
|
|
2042
2327
|
for (const v of extractVariables(asset.sections.system_instructions)) {
|
|
@@ -2058,7 +2343,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2058
2343
|
}
|
|
2059
2344
|
}
|
|
2060
2345
|
for (const v of declaredInputs) {
|
|
2061
|
-
|
|
2346
|
+
const input = declaredInputsByName.get(v);
|
|
2347
|
+
if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
|
|
2062
2348
|
warnings.push({
|
|
2063
2349
|
code: "POK012",
|
|
2064
2350
|
message: `Variable "${v}" is declared in context.inputs but never used`,
|
|
@@ -2074,9 +2360,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2074
2360
|
suggestion: "Declare context.inputs to enable input policy validation."
|
|
2075
2361
|
});
|
|
2076
2362
|
}
|
|
2077
|
-
for (const input of
|
|
2363
|
+
for (const input of contextInputs) {
|
|
2078
2364
|
const lowerName = input.name.toLowerCase();
|
|
2079
|
-
|
|
2365
|
+
const inputWarningsEnabled = areContextInputWarningsEnabled(input);
|
|
2366
|
+
if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
|
|
2080
2367
|
warnings.push({
|
|
2081
2368
|
code: "POK040",
|
|
2082
2369
|
message: `Context input "${input.name}" has no max_size and appears unbounded.`,
|
|
@@ -2084,7 +2371,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2084
2371
|
suggestion: "Add max_size to constrain prompt payload growth."
|
|
2085
2372
|
});
|
|
2086
2373
|
}
|
|
2087
|
-
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) {
|
|
2088
2375
|
warnings.push({
|
|
2089
2376
|
code: "POK041",
|
|
2090
2377
|
message: `Context input "${input.name}" has no input hardening validators.`,
|
|
@@ -2092,7 +2379,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2092
2379
|
suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
|
|
2093
2380
|
});
|
|
2094
2381
|
}
|
|
2095
|
-
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) {
|
|
2096
2383
|
warnings.push({
|
|
2097
2384
|
code: "POK014",
|
|
2098
2385
|
message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
|
|
@@ -2119,6 +2406,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
2119
2406
|
}
|
|
2120
2407
|
}
|
|
2121
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
|
+
}
|
|
2122
2433
|
if (asset.provider) {
|
|
2123
2434
|
let providerCache;
|
|
2124
2435
|
let cacheSuggestionField;
|
|
@@ -2733,7 +3044,7 @@ var PromptOpsKit = class {
|
|
|
2733
3044
|
* Validate a prompt file.
|
|
2734
3045
|
*/
|
|
2735
3046
|
async validatePrompt(promptPath) {
|
|
2736
|
-
const sourceFile = (0,
|
|
3047
|
+
const sourceFile = (0, import_node_path5.resolve)(this.config.sourceDir, promptPath + ".md");
|
|
2737
3048
|
try {
|
|
2738
3049
|
const asset = await this.loadPrompt(promptPath);
|
|
2739
3050
|
return validateAssetWithIncludes(asset, sourceFile);
|