promptopskit 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +91 -7
  2. package/SKILL.md +69 -6
  3. package/dist/{chunk-YNIAA4ZJ.js → chunk-F6UJ73Y6.js} +5 -3
  4. package/dist/chunk-F6UJ73Y6.js.map +1 -0
  5. package/dist/{chunk-O6RQZYS4.js → chunk-HLNVKKMS.js} +16 -5
  6. package/dist/chunk-HLNVKKMS.js.map +1 -0
  7. package/dist/{chunk-4LEPCCSA.js → chunk-JBLUPKY4.js} +144 -3
  8. package/dist/chunk-JBLUPKY4.js.map +1 -0
  9. package/dist/{chunk-LMURMZUA.js → chunk-L2V7LDKD.js} +5 -3
  10. package/dist/chunk-L2V7LDKD.js.map +1 -0
  11. package/dist/{chunk-AN3OR7YC.js → chunk-RY5DVTLV.js} +7 -5
  12. package/dist/chunk-RY5DVTLV.js.map +1 -0
  13. package/dist/chunk-UPTCSWK3.js +60 -0
  14. package/dist/chunk-UPTCSWK3.js.map +1 -0
  15. package/dist/{chunk-V7QY7MRM.js → chunk-XNNDGDPI.js} +50 -3
  16. package/dist/chunk-XNNDGDPI.js.map +1 -0
  17. package/dist/cli/index.js +356 -5
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/index.cjs +398 -16
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +4 -4
  22. package/dist/index.d.ts +4 -4
  23. package/dist/index.js +162 -10
  24. package/dist/index.js.map +1 -1
  25. package/dist/providers/anthropic.cjs +200 -5
  26. package/dist/providers/anthropic.cjs.map +1 -1
  27. package/dist/providers/anthropic.d.cts +2 -2
  28. package/dist/providers/anthropic.d.ts +2 -2
  29. package/dist/providers/anthropic.js +3 -3
  30. package/dist/providers/gemini.cjs +191 -5
  31. package/dist/providers/gemini.cjs.map +1 -1
  32. package/dist/providers/gemini.d.cts +2 -2
  33. package/dist/providers/gemini.d.ts +2 -2
  34. package/dist/providers/gemini.js +3 -3
  35. package/dist/providers/openai-responses.cjs +189 -3
  36. package/dist/providers/openai-responses.cjs.map +1 -1
  37. package/dist/providers/openai-responses.d.cts +2 -2
  38. package/dist/providers/openai-responses.d.ts +2 -2
  39. package/dist/providers/openai-responses.js +3 -3
  40. package/dist/providers/openai.cjs +189 -3
  41. package/dist/providers/openai.cjs.map +1 -1
  42. package/dist/providers/openai.d.cts +2 -2
  43. package/dist/providers/openai.d.ts +2 -2
  44. package/dist/providers/openai.js +3 -3
  45. package/dist/providers/openrouter.cjs +224 -6
  46. package/dist/providers/openrouter.cjs.map +1 -1
  47. package/dist/providers/openrouter.d.cts +2 -2
  48. package/dist/providers/openrouter.d.ts +2 -2
  49. package/dist/providers/openrouter.js +4 -4
  50. package/dist/{schema-BKkg93rL.d.cts → schema-B9A1zLRk.d.cts} +482 -2
  51. package/dist/{schema-BKkg93rL.d.ts → schema-B9A1zLRk.d.ts} +482 -2
  52. package/dist/testing.cjs +217 -4
  53. package/dist/testing.cjs.map +1 -1
  54. package/dist/testing.d.cts +59 -2
  55. package/dist/testing.d.ts +59 -2
  56. package/dist/testing.js +70 -2
  57. package/dist/testing.js.map +1 -1
  58. package/dist/{types-CsQ8tON_.d.ts → types-DjGAwJxC.d.ts} +1 -1
  59. package/dist/{types-7cC17GWp.d.cts → types-DpbzzZO2.d.cts} +1 -1
  60. package/dist/usagetap/index.d.cts +2 -2
  61. package/dist/usagetap/index.d.ts +2 -2
  62. package/package.json +2 -2
  63. package/dist/chunk-4LEPCCSA.js.map +0 -1
  64. package/dist/chunk-AN3OR7YC.js.map +0 -1
  65. package/dist/chunk-LMURMZUA.js.map +0 -1
  66. package/dist/chunk-O6RQZYS4.js.map +0 -1
  67. package/dist/chunk-V7QY7MRM.js.map +0 -1
  68. package/dist/chunk-YAD6COQX.js +0 -26
  69. package/dist/chunk-YAD6COQX.js.map +0 -1
  70. package/dist/chunk-YNIAA4ZJ.js.map +0 -1
package/README.md CHANGED
@@ -11,6 +11,31 @@ Your prompts are already in Git. PromptOpsKit makes them manageable. It replaces
11
11
 
12
12
  Provider adapters for OpenAI, Anthropic, Gemini, and OpenRouter produce a ready-to-send **request body only** — no HTTP client, no auth, no headers. Your application owns transport, so PromptOpsKit slots into any stack without opinions about how you call the API.
13
13
 
14
+ ## How is this different from GitHub Models?
15
+
16
+ GitHub Models is a great place to prototype prompts, compare models, and run evaluations inside GitHub.
17
+
18
+ PromptOpsKit is focused on the application runtime layer.
19
+
20
+ It helps you turn prompt behavior into repo-native assets that your application can load, validate, compose, compile, and render into provider-specific request bodies.
21
+
22
+ Use GitHub Models when you want:
23
+
24
+ - a GitHub-hosted prompt editor and playground
25
+ - side-by-side model comparisons
26
+ - structured evaluations inside GitHub
27
+ - `.prompt.yml` files for prompt experiments and evals
28
+
29
+ Use PromptOpsKit when you want:
30
+
31
+ - Markdown prompt assets with YAML metadata
32
+ - production input hardening and validation
33
+ - environment-specific model and parameter overrides
34
+ - reusable prompt composition
35
+ - compiled artifacts for deployment
36
+ - provider-specific request bodies for your own runtime code
37
+ - control over SDKs, auth, retries, routing, observability, and billing
38
+
14
39
  ### Why PromptOpsKit?
15
40
 
16
41
  - **Centralized, not scattered** — each prompt is a single Markdown file that captures prompt text, model config, tool bindings, and context rules together.
@@ -44,9 +69,12 @@ This creates:
44
69
  prompts/
45
70
  ├── defaults.md # Folder-level defaults (provider, model, metadata, system instructions)
46
71
  ├── hello.md # Sample prompt with variables
47
- ├── hello.test.yaml # Test sidecar with sample inputs
72
+ ├── hello.test.yaml # Test sidecar with sample inputs and hardcoded responses
48
73
  └── shared/
49
74
  └── tone.md # Shared system instructions (included via composition)
75
+
76
+ tests/
77
+ └── hello.prompt.test.mjs # Executable starter test for the hello prompt
50
78
  ```
51
79
 
52
80
  ### 2. Write a prompt
@@ -69,9 +97,7 @@ context:
69
97
  reject_secrets: true
70
98
  - name: app_context
71
99
  max_size: 2000
72
- allow_regex:
73
- pattern: "^[A-Za-z0-9 _-]+$"
74
- flags: "i"
100
+ allow_regex: /^[A-Za-z0-9 _-]+$/i
75
101
  includes:
76
102
  - ./shared/tone.md
77
103
  ---
@@ -137,8 +163,9 @@ Supported values for `warnings.contextSize` are `auto`, `off`, `result-only`, `c
137
163
  - **Overrides** — Environment and tier-based overrides (base → env → tier → runtime)
138
164
  - **5 provider adapters** — OpenAI (Chat), OpenAI (Responses), Anthropic, Gemini, OpenRouter — body-only output
139
165
  - **Provider-aware input caching controls** — optional `cache` front matter maps to OpenAI prompt cache hints, Anthropic `cache_control`, and Gemini `cachedContent`
166
+ - **Vendor escape hatch** — optional `raw.<provider>` blocks shallow-merge unmodeled request-body fields into the final provider payload
140
167
  - **Validation** — Zod schema validation, Levenshtein-based "did you mean?" for typos, variable usage checks
141
- - **Context hardening** — structured regexes with flags, `/pattern/i` convenience syntax, and built-in `non_empty` / `reject_secrets` validators
168
+ - **Context hardening** — copyable `/pattern/i` regex literals, structured regexes with `return_message`, and built-in `non_empty` / `reject_secrets` validators
142
169
  - **Optional short-circuit messages** — validators can return a structured `returnMessage` instead of throwing when configured
143
170
  - **Context size guardrails** — optional per-input `max_size` metadata with non-blocking render-time warnings
144
171
  - **Warning controls** — top-level config can suppress or emit context size warnings differently in dev and prod
@@ -225,6 +252,48 @@ const request = openaiAdapter.render(prompt, {
225
252
 
226
253
  In browser or client-side code, keep provider credentials on the server. Use the rendered request body with your own server endpoint, server action, or edge function rather than calling a provider directly from the client.
227
254
 
255
+ ### Provider-specific fields and raw passthrough
256
+
257
+ Use normalized fields first (`sampling`, `response`, `cache`, `tools`) so prompts stay portable. `response.schema` is the neutral JSON Schema path; adapters emit it as OpenAI/OpenRouter `response_format`, OpenAI Responses `text.format`, Anthropic `output_config.format`, and Gemini `generationConfig.responseJsonSchema`.
258
+
259
+ Use `provider_options` when PromptOpsKit has a known provider-specific mapping, such as Anthropic `top_k`, Gemini's native `response_schema`, or OpenRouter routing fields.
260
+
261
+ ```yaml
262
+ response:
263
+ format: json
264
+ schema_name: support_reply
265
+ schema_description: Structured support reply
266
+ schema:
267
+ type: object
268
+ properties:
269
+ answer:
270
+ type: string
271
+ provider_options:
272
+ openrouter:
273
+ provider:
274
+ order: ["anthropic", "openai"]
275
+ transforms: ["middle-out"]
276
+ ```
277
+
278
+ When a provider adds a body field PromptOpsKit does not model yet, use `raw`:
279
+
280
+ ```yaml
281
+ raw:
282
+ openai:
283
+ service_tier: flex
284
+ anthropic:
285
+ service_tier: auto
286
+ gemini:
287
+ safetySettings:
288
+ - category: HARM_CATEGORY_DANGEROUS_CONTENT
289
+ threshold: BLOCK_ONLY_HIGH
290
+ openrouter:
291
+ usage:
292
+ include: true
293
+ ```
294
+
295
+ Each adapter reads only its matching raw block and shallow-merges it into the generated request body after normalized mappings. This is intentionally an escape hatch; prefer first-class fields when they exist.
296
+
228
297
  On the server, adapters also provide async prompt-aware helpers so you can use the default `./prompts` and `./.generated-prompts/json` directories without creating a `PromptOpsKit` instance:
229
298
 
230
299
  ```typescript
@@ -474,11 +543,21 @@ Hello {{ name }}!`,
474
543
  ## Testing Helpers
475
544
 
476
545
  ```typescript
477
- import { createMockAsset, createMockResolvedAsset, parseTestPrompt } from 'promptopskit/testing';
546
+ import {
547
+ createHardcodedPromptResponder,
548
+ createMockAsset,
549
+ createMockResolvedAsset,
550
+ loadPromptTestSidecar,
551
+ parseTestPrompt,
552
+ } from 'promptopskit/testing';
478
553
 
479
554
  const asset = createMockAsset({ model: 'gpt-5.4' });
480
555
  const resolved = createMockResolvedAsset();
481
556
  const parsed = parseTestPrompt('---\nid: test\nschema_version: 1\n---\n\nHello');
557
+
558
+ const sidecar = await loadPromptTestSidecar('./prompts/hello.test.yaml');
559
+ const respond = createHardcodedPromptResponder(sidecar);
560
+ const response = respond('basic-greeting');
482
561
  ```
483
562
 
484
563
  ## API Reference
@@ -536,8 +615,11 @@ Prompt files use YAML front matter with these fields:
536
615
  | `fallback_models` | `string[]` | Fallback model list |
537
616
  | `reasoning` | `object` | `{ effort, budget_tokens }` |
538
617
  | `sampling` | `object` | `{ temperature, top_p, frequency_penalty, presence_penalty, stop, max_output_tokens }` |
539
- | `response` | `object` | `{ format, stream }` |
618
+ | `response` | `object` | `{ format, stream, schema, schema_name, schema_description, schema_strict }` |
619
+ | `cache` | `object` | Provider-specific cache controls (`openai`, `anthropic`, `gemini`/`google`) |
540
620
  | `tools` | `array` | Tool references (string names or inline definitions) |
621
+ | `provider_options` | `object` | Provider-specific non-portable options (`anthropic`, `gemini`, `openrouter`) |
622
+ | `raw` | `object` | Provider-scoped request-body passthrough (`openai`, `openai-responses`, `anthropic`, `gemini`/`google`, `openrouter`) |
541
623
  | `mcp` | `object` | MCP server references |
542
624
  | `context` | `object` | `{ inputs, history }` — declare expected variables, with optional per-input `max_size`, `trim`, structured or literal `allow_regex`/`deny_regex`, and built-in `non_empty` / `reject_secrets` validators |
543
625
  | `includes` | `string[]` | Paths to included prompt files |
@@ -545,6 +627,8 @@ Prompt files use YAML front matter with these fields:
545
627
  | `tiers` | `object` | Named tier overrides |
546
628
  | `metadata` | `object` | `{ owner, tags, review_required, stable }` |
547
629
 
630
+ For `allow_regex` and `deny_regex`, prefer unquoted `/pattern/i` literal form so regex escapes such as `\s` and `\b` stay copyable from tools like regex101. If you use structured `pattern:` form, use single-quoted YAML strings or double each backslash in double-quoted strings.
631
+
548
632
  ## Website
549
633
 
550
634
  The `website/` directory contains a standalone marketing website for PromptOpsKit.
package/SKILL.md CHANGED
@@ -8,7 +8,7 @@ description: Guidance for creating and editing promptopskit prompt files, defaul
8
8
  This project uses **promptopskit** to manage LLM prompts as code.
9
9
  Prompts live in markdown files with YAML front matter, are validated against
10
10
  a schema, and render into provider-specific request bodies (OpenAI, Anthropic,
11
- Gemini, OpenRouter). Follow these instructions when creating or editing prompts.
11
+ Gemini, OpenRouter, and OpenAI Responses). Follow these instructions when creating or editing prompts.
12
12
 
13
13
  ---
14
14
 
@@ -58,13 +58,16 @@ the fields required by that specific file:
58
58
  | `id` | string | **yes** | Unique identifier for the prompt |
59
59
  | `schema_version` | number | yes | Always `1` |
60
60
  | `description` | string | no | Human-readable description |
61
- | `provider` | enum | no | `openai`, `anthropic`, `google`, `gemini`, `openrouter`, or `any` |
61
+ | `provider` | enum | no | `openai`, `openai-responses`, `anthropic`, `google`, `gemini`, `openrouter`, or `any` |
62
62
  | `model` | string | no | Model identifier (e.g. `gpt-5.4`, `claude-sonnet-4-20250514`) |
63
63
  | `fallback_models` | string[] | no | Ordered fallback model list |
64
64
  | `reasoning` | object | no | `{ effort: low|medium|high, budget_tokens: number }` |
65
65
  | `sampling` | object | no | `{ temperature, top_p, frequency_penalty, presence_penalty, stop, max_output_tokens }` |
66
- | `response` | object | no | `{ format: text|json|markdown, stream: boolean }` |
66
+ | `response` | object | no | `{ format: text|json|markdown, stream: boolean, schema?: object, schema_name?: string, schema_description?: string, schema_strict?: boolean }` |
67
+ | `cache` | object | no | Provider-specific cache controls (`openai`, `anthropic`, `gemini`/`google`) |
67
68
  | `tools` | array | no | Tool names (strings) or inline definitions with `{ name, description, input_schema }` |
69
+ | `provider_options` | object | no | Provider-specific advanced options (`anthropic`, `gemini`, `openrouter`) |
70
+ | `raw` | object | no | Provider-scoped request-body passthrough for unmodeled vendor fields |
68
71
  | `mcp` | object | no | `{ servers: [string | { name, config }] }` |
69
72
  | `context.inputs` | `Array<string | { name, max_size?, trim?, allow_regex?, deny_regex?, non_empty?, reject_secrets? }>` | no | Declared variable names used in templates, with optional size budgets and runtime hardening controls |
70
73
  | `context.history` | object | no | `{ max_items: number }` |
@@ -102,7 +105,9 @@ Rules:
102
105
  - Use object-form inputs with `max_size` when a variable is likely to grow large and should trigger early warnings
103
106
  - Use `trim` to enforce byte budgets before interpolation when `max_size` is set
104
107
  - Use `allow_regex` for allowlist checks and `deny_regex` for blocklist checks on risky inputs
105
- - Prefer structured regexes like `{ pattern, flags }`; `/pattern/i` strings are also accepted and normalized internally
108
+ - Prefer unquoted `/pattern/i` literals for regex validators so backslash escapes such as `\s` and `\b` stay copyable from regex tools
109
+ - Use structured regexes like `{ pattern, flags, return_message }` when the validator needs a fallback message or separate flags
110
+ - In structured `pattern:` YAML, use single quotes for patterns with backslashes or double each backslash in double-quoted strings
106
111
  - Use `non_empty: true` for required user text and `reject_secrets: true` for common secret redaction checks
107
112
  - When the caller should receive a structured fallback message instead of an exception, use object form with `return_message` on `allow_regex`, `deny_regex`, `non_empty`, or `reject_secrets`
108
113
  - Escape literal braces with `\{{` and `\}}`
@@ -124,7 +129,7 @@ At render time, callers can also pass `onContextOverflow` to transform oversized
124
129
 
125
130
  If a validator declares `return_message`, `renderPrompt()` returns that message in a structured result and omits the provider request instead of throwing for that validation failure. Invalid regex definitions still fail during `validate` and `compile` as `POK013` prompt-authoring errors.
126
131
 
127
- Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile`, not just at render time. When regex compilation fails, the error includes the prompt id, variable name, field name, and raw configured value.
132
+ Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile`, not just at render time. When regex compilation fails, the error includes the prompt id, variable name, field name, and raw configured value. Double-quoted YAML regex strings with raw backslashes fail as `POK013`; use `/pattern/i`, single-quoted `pattern: '...'`, or doubled backslashes.
128
133
 
129
134
  Example: this is the minimal valid shape for a prompt that references
130
135
  `{{ pull_request }}` even when provider/model are inherited from defaults:
@@ -182,6 +187,7 @@ prompts/
182
187
  Supported default fields:
183
188
  - `provider` (front matter) — default provider for the folder
184
189
  - `model` (front matter) — default model for the folder
190
+ - `cache` (front matter) — default provider-specific cache hints
185
191
  - `metadata` (front matter) — merged with prompt-local metadata
186
192
  - `# System instructions` (body section) — used when the prompt has none
187
193
 
@@ -229,12 +235,69 @@ tiers:
229
235
  ```
230
236
 
231
237
  Overridable fields: `model`, `fallback_models`, `reasoning`, `sampling`,
232
- `response`, `tools`.
238
+ `response`, `cache`, `raw`, `tools`, `provider_options`.
233
239
 
234
240
  Override application order: **base → environment → tier → runtime**.
235
241
 
236
242
  ---
237
243
 
244
+ ## Provider-specific fields
245
+
246
+ Prefer portable fields first:
247
+
248
+ - Use `sampling` for common sampling controls
249
+ - Use `response.schema`, `response.schema_name`, `response.schema_description`, and `response.schema_strict` for structured output when possible
250
+ - Use `cache` for provider cache hints
251
+ - Use `tools` for tool definitions
252
+
253
+ Treat `response.schema` as the provider-neutral JSON Schema contract. The adapters emit it through provider-specific request fields: OpenAI/OpenRouter `response_format`, OpenAI Responses `text.format`, Anthropic `output_config.format`, and Gemini `generationConfig.responseJsonSchema`.
254
+
255
+ Use `provider_options` for known non-portable mappings:
256
+
257
+ ```yaml
258
+ provider_options:
259
+ anthropic:
260
+ top_k: 40
261
+ tool_choice:
262
+ type: auto
263
+ output_config:
264
+ format:
265
+ type: json_schema
266
+ schema:
267
+ type: object
268
+ gemini:
269
+ # Use only when Gemini's native schema dialect is required.
270
+ response_schema:
271
+ type: object
272
+ response_json_schema:
273
+ type: object
274
+ openrouter:
275
+ provider:
276
+ order: ["anthropic", "openai"]
277
+ transforms: ["middle-out"]
278
+ ```
279
+
280
+ Use `raw` only when a vendor request-body field is important and PromptOpsKit does not model it yet:
281
+
282
+ ```yaml
283
+ raw:
284
+ openai:
285
+ service_tier: flex
286
+ anthropic:
287
+ service_tier: auto
288
+ gemini:
289
+ safetySettings:
290
+ - category: HARM_CATEGORY_DANGEROUS_CONTENT
291
+ threshold: BLOCK_ONLY_HIGH
292
+ openrouter:
293
+ usage:
294
+ include: true
295
+ ```
296
+
297
+ Raw blocks are provider-scoped (`openai`, `openai-responses`/`openai_responses`, `anthropic`, `gemini`/`google`, `openrouter`) and are shallow-merged into the final request body after normalized fields. When adding `raw`, include a short note in `# Notes` explaining why a first-class field is not being used.
298
+
299
+ ---
300
+
238
301
  ## Test sidecars
239
302
 
240
303
  Create a `.test.yaml` file alongside a prompt to define test cases:
@@ -1,8 +1,9 @@
1
1
  import {
2
+ applyRawProviderBody,
2
3
  renderSections,
3
4
  resolveAssetForProvider,
4
5
  withPromptInputSupport
5
- } from "./chunk-V7QY7MRM.js";
6
+ } from "./chunk-XNNDGDPI.js";
6
7
 
7
8
  // src/providers/openai-responses.ts
8
9
  var openaiResponsesAdapter = withPromptInputSupport({
@@ -64,6 +65,7 @@ var openaiResponsesAdapter = withPromptInputSupport({
64
65
  format: {
65
66
  type: "json_schema",
66
67
  name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,
68
+ ...resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {},
67
69
  schema: resolvedAsset.response.schema,
68
70
  strict: resolvedAsset.response.schema_strict ?? true
69
71
  }
@@ -98,7 +100,7 @@ var openaiResponsesAdapter = withPromptInputSupport({
98
100
  if (responseOptions?.include !== void 0) body.include = responseOptions.include;
99
101
  if (responseOptions?.background !== void 0) body.background = responseOptions.background;
100
102
  return {
101
- body,
103
+ body: applyRawProviderBody(body, resolvedAsset, "openai-responses"),
102
104
  provider: "openai-responses",
103
105
  model: resolvedAsset.model ?? "unknown"
104
106
  };
@@ -108,4 +110,4 @@ var openaiResponsesAdapter = withPromptInputSupport({
108
110
  export {
109
111
  openaiResponsesAdapter
110
112
  };
111
- //# sourceMappingURL=chunk-YNIAA4ZJ.js.map
113
+ //# sourceMappingURL=chunk-F6UJ73Y6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/openai-responses.ts"],"sourcesContent":["import type { ResolvedPromptAsset } from '../schema/index.js';\nimport type {\n ProviderAdapter,\n ProviderRequest,\n ValidationResult,\n RuntimeRenderOptions,\n} from './types.js';\nimport { renderSections } from '../renderer/index.js';\nimport { resolveAssetForProvider } from './resolve-asset.js';\nimport { withPromptInputSupport } from './prompt-input.js';\nimport { applyRawProviderBody } from './raw.js';\n\n/**\n * OpenAI Responses provider adapter.\n * Produces request bodies compatible with the OpenAI Responses API.\n */\nexport const openaiResponsesAdapter: ProviderAdapter = withPromptInputSupport({\n name: 'openai-responses',\n\n validate(asset: ResolvedPromptAsset, runtime?: RuntimeRenderOptions): ValidationResult {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!resolvedAsset.model) {\n errors.push('OpenAI Responses adapter requires a model to be specified.');\n }\n\n if (resolvedAsset.reasoning?.budget_tokens !== undefined) {\n warnings.push('OpenAI Responses uses reasoning.effort, not budget_tokens. budget_tokens will be ignored.');\n }\n\n if (resolvedAsset.response?.schema !== undefined && resolvedAsset.response?.format !== 'json') {\n warnings.push('OpenAI Responses response.schema requires response.format: json. schema will still be applied as JSON schema output.');\n }\n\n if (runtime?.openaiResponses?.conversation !== undefined && runtime?.openaiResponses?.previous_response_id !== undefined) {\n errors.push('OpenAI Responses options \"conversation\" and \"previous_response_id\" cannot both be set.');\n }\n\n return { valid: errors.length === 0, errors, warnings };\n },\n\n render(asset: ResolvedPromptAsset, runtime: RuntimeRenderOptions): ProviderRequest {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const sections = renderSections(resolvedAsset, {\n variables: runtime.variables,\n strict: runtime.strict,\n });\n const responseOptions = runtime.openaiResponses;\n\n const input: Array<Record<string, unknown>> = [];\n\n if (runtime.history) {\n for (const msg of runtime.history) {\n input.push({ role: msg.role, content: msg.content });\n }\n }\n\n if (sections.prompt_template) {\n input.push({ role: 'user', content: sections.prompt_template });\n }\n\n const body: Record<string, unknown> = {\n model: resolvedAsset.model,\n input,\n };\n\n if (responseOptions?.instructions !== undefined) {\n body.instructions = responseOptions.instructions;\n } else if (sections.system_instructions) {\n body.instructions = sections.system_instructions;\n }\n\n // Sampling params\n if (resolvedAsset.sampling?.temperature !== undefined) body.temperature = resolvedAsset.sampling.temperature;\n if (resolvedAsset.sampling?.top_p !== undefined) body.top_p = resolvedAsset.sampling.top_p;\n if (resolvedAsset.sampling?.frequency_penalty !== undefined) body.frequency_penalty = resolvedAsset.sampling.frequency_penalty;\n if (resolvedAsset.sampling?.presence_penalty !== undefined) body.presence_penalty = resolvedAsset.sampling.presence_penalty;\n if (resolvedAsset.sampling?.stop !== undefined) body.stop = resolvedAsset.sampling.stop;\n if (resolvedAsset.sampling?.max_output_tokens !== undefined) body.max_output_tokens = resolvedAsset.sampling.max_output_tokens;\n\n // Reasoning\n if (resolvedAsset.reasoning?.effort) {\n body.reasoning = { effort: resolvedAsset.reasoning.effort };\n }\n\n // Structured output / response format\n if (resolvedAsset.response?.schema) {\n body.text = {\n format: {\n type: 'json_schema',\n name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,\n ...(resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {}),\n schema: resolvedAsset.response.schema,\n strict: resolvedAsset.response.schema_strict ?? true,\n },\n };\n } else if (resolvedAsset.response?.format === 'json') {\n body.text = { format: { type: 'json_object' } };\n }\n\n // Streaming\n if (resolvedAsset.response?.stream !== undefined) {\n body.stream = resolvedAsset.response.stream;\n }\n\n // Tools\n if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {\n body.tools = resolvedAsset.tools.map((tool) => {\n if (typeof tool === 'string') {\n const def = runtime.toolRegistry?.[tool];\n if (def) return def;\n return { type: 'function', name: tool };\n }\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n };\n });\n }\n\n if (responseOptions?.previous_response_id !== undefined) body.previous_response_id = responseOptions.previous_response_id;\n if (responseOptions?.conversation !== undefined) body.conversation = responseOptions.conversation;\n if (responseOptions?.parallel_tool_calls !== undefined) body.parallel_tool_calls = responseOptions.parallel_tool_calls;\n if (responseOptions?.max_tool_calls !== undefined) body.max_tool_calls = responseOptions.max_tool_calls;\n if (responseOptions?.store !== undefined) body.store = responseOptions.store;\n if (responseOptions?.metadata !== undefined) body.metadata = responseOptions.metadata;\n if (responseOptions?.include !== undefined) body.include = responseOptions.include;\n if (responseOptions?.background !== undefined) body.background = responseOptions.background;\n\n return {\n body: applyRawProviderBody(body, resolvedAsset, 'openai-responses'),\n provider: 'openai-responses',\n model: resolvedAsset.model ?? 'unknown',\n };\n },\n});\n"],"mappings":"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","names":[]}
@@ -1,8 +1,9 @@
1
1
  import {
2
+ applyRawProviderBody,
2
3
  renderSections,
3
4
  resolveAssetForProvider,
4
5
  withPromptInputSupport
5
- } from "./chunk-V7QY7MRM.js";
6
+ } from "./chunk-XNNDGDPI.js";
6
7
 
7
8
  // src/providers/anthropic.ts
8
9
  var anthropicAdapter = withPromptInputSupport({
@@ -23,8 +24,8 @@ var anthropicAdapter = withPromptInputSupport({
23
24
  if (resolvedAsset.reasoning?.effort !== void 0) {
24
25
  warnings.push("Anthropic uses budget_tokens for thinking, not effort. effort will be mapped approximately.");
25
26
  }
26
- if (resolvedAsset.response?.schema !== void 0) {
27
- warnings.push("Anthropic does not support response.schema structured output in this adapter. It will be ignored.");
27
+ if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
28
+ warnings.push("Anthropic response.schema is mapped to output_config.format and should usually be paired with response.format: json.");
28
29
  }
29
30
  if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0 && resolvedAsset.provider_options.anthropic.top_k < 0) {
30
31
  errors.push("Anthropic provider_options.top_k must be >= 0.");
@@ -88,6 +89,16 @@ var anthropicAdapter = withPromptInputSupport({
88
89
  if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0) {
89
90
  body.top_k = resolvedAsset.provider_options.anthropic.top_k;
90
91
  }
92
+ if (resolvedAsset.provider_options?.anthropic?.output_config !== void 0) {
93
+ body.output_config = resolvedAsset.provider_options.anthropic.output_config;
94
+ } else if (resolvedAsset.response?.schema !== void 0) {
95
+ body.output_config = {
96
+ format: {
97
+ type: "json_schema",
98
+ schema: resolvedAsset.response.schema
99
+ }
100
+ };
101
+ }
91
102
  if (resolvedAsset.response?.stream !== void 0) {
92
103
  body.stream = resolvedAsset.response.stream;
93
104
  }
@@ -121,7 +132,7 @@ var anthropicAdapter = withPromptInputSupport({
121
132
  body.tool_choice = resolvedAsset.provider_options.anthropic.tool_choice;
122
133
  }
123
134
  return {
124
- body,
135
+ body: applyRawProviderBody(body, resolvedAsset, "anthropic"),
125
136
  provider: "anthropic",
126
137
  model: resolvedAsset.model ?? "unknown"
127
138
  };
@@ -131,4 +142,4 @@ var anthropicAdapter = withPromptInputSupport({
131
142
  export {
132
143
  anthropicAdapter
133
144
  };
134
- //# sourceMappingURL=chunk-O6RQZYS4.js.map
145
+ //# sourceMappingURL=chunk-HLNVKKMS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/anthropic.ts"],"sourcesContent":["import type { ResolvedPromptAsset } from '../schema/index.js';\nimport type {\n ProviderAdapter,\n ProviderRequest,\n ValidationResult,\n RuntimeRenderOptions,\n} from './types.js';\nimport { renderSections } from '../renderer/index.js';\nimport { resolveAssetForProvider } from './resolve-asset.js';\nimport { withPromptInputSupport } from './prompt-input.js';\nimport { applyRawProviderBody } from './raw.js';\n\n/**\n * Anthropic provider adapter.\n * Produces request bodies compatible with the Anthropic Messages API.\n */\nexport const anthropicAdapter: ProviderAdapter = withPromptInputSupport({\n name: 'anthropic',\n\n validate(asset: ResolvedPromptAsset, runtime?: RuntimeRenderOptions): ValidationResult {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!resolvedAsset.model) {\n errors.push('Anthropic adapter requires a model to be specified.');\n }\n\n if (resolvedAsset.sampling?.frequency_penalty !== undefined) {\n warnings.push('Anthropic does not support frequency_penalty. It will be ignored.');\n }\n if (resolvedAsset.sampling?.presence_penalty !== undefined) {\n warnings.push('Anthropic does not support presence_penalty. It will be ignored.');\n }\n if (resolvedAsset.reasoning?.effort !== undefined) {\n warnings.push('Anthropic uses budget_tokens for thinking, not effort. effort will be mapped approximately.');\n }\n if (resolvedAsset.response?.schema !== undefined && resolvedAsset.response?.format !== 'json') {\n warnings.push('Anthropic response.schema is mapped to output_config.format and should usually be paired with response.format: json.');\n }\n\n if (resolvedAsset.provider_options?.anthropic?.top_k !== undefined && resolvedAsset.provider_options.anthropic.top_k < 0) {\n errors.push('Anthropic provider_options.top_k must be >= 0.');\n }\n\n return { valid: errors.length === 0, errors, warnings };\n },\n\n render(asset: ResolvedPromptAsset, runtime: RuntimeRenderOptions): ProviderRequest {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const sections = renderSections(resolvedAsset, {\n variables: runtime.variables,\n strict: runtime.strict,\n });\n\n const messages: Array<Record<string, unknown>> = [];\n const anthropicCacheConfig = resolvedAsset.cache?.anthropic;\n const cacheType = anthropicCacheConfig?.type ?? 'ephemeral';\n const cacheControl = anthropicCacheConfig\n ? {\n type: cacheType,\n ...(anthropicCacheConfig.ttl ? { ttl: anthropicCacheConfig.ttl } : {}),\n }\n : undefined;\n const cacheMode = anthropicCacheConfig?.mode ?? 'automatic';\n\n // History\n if (runtime.history) {\n for (const msg of runtime.history) {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n\n // User message (prompt template)\n if (sections.prompt_template) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_prompt_template) {\n messages.push({\n role: 'user',\n content: [{ type: 'text', text: sections.prompt_template, cache_control: cacheControl }],\n });\n } else {\n messages.push({ role: 'user', content: sections.prompt_template });\n }\n }\n\n const body: Record<string, unknown> = {\n model: resolvedAsset.model,\n messages,\n };\n\n // System goes as top-level field in Anthropic\n if (sections.system_instructions) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_system_instructions !== false) {\n body.system = [{ type: 'text', text: sections.system_instructions, cache_control: cacheControl }];\n } else {\n body.system = sections.system_instructions;\n }\n }\n\n // Sampling params\n if (resolvedAsset.sampling?.temperature !== undefined) body.temperature = resolvedAsset.sampling.temperature;\n if (resolvedAsset.sampling?.top_p !== undefined) body.top_p = resolvedAsset.sampling.top_p;\n if (resolvedAsset.sampling?.stop !== undefined) body.stop_sequences = resolvedAsset.sampling.stop;\n if (resolvedAsset.sampling?.max_output_tokens !== undefined) {\n body.max_tokens = resolvedAsset.sampling.max_output_tokens;\n } else {\n // Anthropic requires max_tokens\n body.max_tokens = 4096;\n }\n\n // Thinking/reasoning\n if (resolvedAsset.reasoning?.budget_tokens) {\n body.thinking = {\n type: 'enabled',\n budget_tokens: resolvedAsset.reasoning.budget_tokens,\n };\n }\n\n // Provider-specific options\n if (resolvedAsset.provider_options?.anthropic?.top_k !== undefined) {\n body.top_k = resolvedAsset.provider_options.anthropic.top_k;\n }\n\n if (resolvedAsset.provider_options?.anthropic?.output_config !== undefined) {\n body.output_config = resolvedAsset.provider_options.anthropic.output_config;\n } else if (resolvedAsset.response?.schema !== undefined) {\n body.output_config = {\n format: {\n type: 'json_schema',\n schema: resolvedAsset.response.schema,\n },\n };\n }\n\n // Streaming\n if (resolvedAsset.response?.stream !== undefined) {\n body.stream = resolvedAsset.response.stream;\n }\n\n if (cacheControl && cacheMode === 'automatic') {\n body.cache_control = cacheControl;\n }\n\n // Tools\n if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {\n body.tools = resolvedAsset.tools.map((tool) => {\n if (typeof tool === 'string') {\n const def = runtime.toolRegistry?.[tool];\n if (def) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools) {\n return { ...(def as Record<string, unknown>), cache_control: cacheControl };\n }\n return def;\n }\n return {\n name: tool,\n ...(cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools\n ? { cache_control: cacheControl }\n : {}),\n };\n }\n return {\n name: tool.name,\n description: tool.description,\n input_schema: tool.input_schema ?? { type: 'object', properties: {} },\n ...(cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools\n ? { cache_control: cacheControl }\n : {}),\n };\n });\n }\n\n if (resolvedAsset.provider_options?.anthropic?.tool_choice !== undefined) {\n body.tool_choice = resolvedAsset.provider_options.anthropic.tool_choice;\n }\n\n return {\n body: applyRawProviderBody(body, resolvedAsset, 'anthropic'),\n provider: 'anthropic',\n model: resolvedAsset.model ?? 'unknown',\n };\n },\n});\n"],"mappings":"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","names":[]}
@@ -30,23 +30,42 @@ var ResponseSchema = z.object({
30
30
  stream: z.boolean().optional(),
31
31
  schema: z.record(z.unknown()).optional(),
32
32
  schema_name: z.string().optional(),
33
+ schema_description: z.string().optional(),
33
34
  schema_strict: z.boolean().optional()
34
35
  });
35
36
  var AnthropicProviderOptionsSchema = z.object({
36
37
  top_k: z.number().int().min(0).optional(),
37
- tool_choice: z.record(z.unknown()).optional()
38
+ tool_choice: z.record(z.unknown()).optional(),
39
+ output_config: z.record(z.unknown()).optional()
38
40
  });
39
41
  var GeminiProviderOptionsSchema = z.object({
40
42
  candidate_count: z.number().int().positive().optional(),
41
43
  top_k: z.number().int().min(0).optional(),
42
44
  seed: z.number().int().optional(),
43
45
  response_schema: z.record(z.unknown()).optional(),
46
+ response_json_schema: z.record(z.unknown()).optional(),
44
47
  response_modalities: z.array(z.string()).optional(),
45
48
  thinking_budget_tokens: z.number().int().positive().optional()
46
49
  });
50
+ var OpenRouterProviderOptionsSchema = z.object({
51
+ provider: z.record(z.unknown()).optional(),
52
+ transforms: z.array(z.string()).optional(),
53
+ plugins: z.array(z.record(z.unknown())).optional(),
54
+ models: z.array(z.string()).optional()
55
+ });
47
56
  var ProviderOptionsSchema = z.object({
48
57
  anthropic: AnthropicProviderOptionsSchema.optional(),
49
- gemini: GeminiProviderOptionsSchema.optional()
58
+ gemini: GeminiProviderOptionsSchema.optional(),
59
+ openrouter: OpenRouterProviderOptionsSchema.optional()
60
+ });
61
+ var RawProviderBodySchema = z.object({
62
+ openai: z.record(z.unknown()).optional(),
63
+ "openai-responses": z.record(z.unknown()).optional(),
64
+ openai_responses: z.record(z.unknown()).optional(),
65
+ anthropic: z.record(z.unknown()).optional(),
66
+ gemini: z.record(z.unknown()).optional(),
67
+ google: z.record(z.unknown()).optional(),
68
+ openrouter: z.record(z.unknown()).optional()
50
69
  });
51
70
  var OpenAICacheSchema = z.object({
52
71
  prompt_cache_key: z.string().min(1).optional(),
@@ -119,6 +138,7 @@ var PromptAssetOverridesSchema = z.object({
119
138
  sampling: SamplingSchema.optional(),
120
139
  response: ResponseSchema.optional(),
121
140
  cache: CacheSchema.optional(),
141
+ raw: RawProviderBodySchema.optional(),
122
142
  tools: z.array(ToolRefSchema).optional(),
123
143
  provider_options: ProviderOptionsSchema.optional()
124
144
  });
@@ -151,6 +171,7 @@ var PromptAssetSchema = z.object({
151
171
  sampling: SamplingSchema.optional(),
152
172
  response: ResponseSchema.optional(),
153
173
  cache: CacheSchema.optional(),
174
+ raw: RawProviderBodySchema.optional(),
154
175
  tools: z.array(ToolRefSchema).optional(),
155
176
  provider_options: ProviderOptionsSchema.optional(),
156
177
  mcp: MCPSchema.optional(),
@@ -204,7 +225,127 @@ function extractSections(body) {
204
225
 
205
226
  // src/parser/parser.ts
206
227
  import matter from "gray-matter";
228
+
229
+ // src/parser/frontmatter-guard.ts
230
+ var FRONT_MATTER_DELIMITER = /^---[ \t]*$/;
231
+ var REGEX_FIELD = /^(?<indent>\s*)(?<field>allow_regex|deny_regex)\s*:\s*(?<value>.*)$/;
232
+ var PATTERN_FIELD = /^(?<indent>\s*)pattern\s*:\s*(?<value>.*)$/;
233
+ var INLINE_PATTERN_FIELD = /(?:^|[{,]\s*)pattern\s*:\s*(?<value>"(?:\\.|[^"\\])*")/;
234
+ function assertRegexFrontMatterQuoting(content, filePath) {
235
+ const frontMatter = extractFrontMatter(content);
236
+ if (!frontMatter) {
237
+ return;
238
+ }
239
+ let pendingRegex;
240
+ for (const [index, line] of frontMatter.lines.entries()) {
241
+ const lineNumber = frontMatter.startLine + index;
242
+ const regexMatch = line.match(REGEX_FIELD);
243
+ if (regexMatch?.groups) {
244
+ const field = regexMatch.groups.field;
245
+ const indent2 = regexMatch.groups.indent.length;
246
+ const value = regexMatch.groups.value.trim();
247
+ pendingRegex = value === "" ? { field, indent: indent2 } : void 0;
248
+ assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber);
249
+ assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber);
250
+ continue;
251
+ }
252
+ if (!pendingRegex) {
253
+ continue;
254
+ }
255
+ if (line.trim() === "" || line.trimStart().startsWith("#")) {
256
+ continue;
257
+ }
258
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
259
+ if (indent <= pendingRegex.indent) {
260
+ pendingRegex = void 0;
261
+ continue;
262
+ }
263
+ const patternMatch = line.match(PATTERN_FIELD);
264
+ if (!patternMatch?.groups) {
265
+ continue;
266
+ }
267
+ assertNoUnescapedBackslashInDoubleQuotedRegex(
268
+ patternMatch.groups.value.trim(),
269
+ `${pendingRegex.field}.pattern`,
270
+ filePath,
271
+ lineNumber
272
+ );
273
+ }
274
+ }
275
+ function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
276
+ const match = value.match(INLINE_PATTERN_FIELD);
277
+ const pattern = match?.groups?.value;
278
+ if (!pattern) {
279
+ return;
280
+ }
281
+ assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
282
+ }
283
+ function extractFrontMatter(content) {
284
+ const lines = content.split(/\r?\n/);
285
+ if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
286
+ return void 0;
287
+ }
288
+ for (let index = 1; index < lines.length; index += 1) {
289
+ if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
290
+ return {
291
+ lines: lines.slice(1, index),
292
+ startLine: 2
293
+ };
294
+ }
295
+ }
296
+ return void 0;
297
+ }
298
+ function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
299
+ if (!value.startsWith('"')) {
300
+ return;
301
+ }
302
+ const quoted = readDoubleQuotedScalar(value);
303
+ if (!quoted) {
304
+ return;
305
+ }
306
+ if (!hasUnescapedBackslash(quoted)) {
307
+ return;
308
+ }
309
+ const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
310
+ throw new Error(
311
+ `POK013: Invalid context regex YAML at ${location}, field "${field}": double-quoted regex strings treat backslashes as YAML escapes. Use unquoted /pattern/i literal form, single quotes, or double each backslash.`
312
+ );
313
+ }
314
+ function readDoubleQuotedScalar(value) {
315
+ let result = "";
316
+ for (let index = 1; index < value.length; index += 1) {
317
+ const char = value[index];
318
+ if (char === '"') {
319
+ return result;
320
+ }
321
+ result += char;
322
+ if (char === "\\" && index + 1 < value.length) {
323
+ index += 1;
324
+ result += value[index];
325
+ }
326
+ }
327
+ return void 0;
328
+ }
329
+ function hasUnescapedBackslash(value) {
330
+ for (let index = 0; index < value.length; index += 1) {
331
+ if (value[index] !== "\\") {
332
+ continue;
333
+ }
334
+ let count = 1;
335
+ while (value[index + count] === "\\") {
336
+ count += 1;
337
+ }
338
+ if (count % 2 === 1) {
339
+ return true;
340
+ }
341
+ index += count - 1;
342
+ }
343
+ return false;
344
+ }
345
+
346
+ // src/parser/parser.ts
207
347
  function parsePrompt(content, filePath) {
348
+ assertRegexFrontMatterQuoting(content, filePath);
208
349
  const { data: frontMatter, content: body } = matter(content);
209
350
  const sections = extractSections(body);
210
351
  const raw = {
@@ -322,4 +463,4 @@ export {
322
463
  parsePrompt,
323
464
  loadPromptFile
324
465
  };
325
- //# sourceMappingURL=chunk-4LEPCCSA.js.map
466
+ //# sourceMappingURL=chunk-JBLUPKY4.js.map