promptopskit 0.2.5 → 0.3.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 (39) hide show
  1. package/README.md +86 -2
  2. package/SKILL.md +33 -6
  3. package/dist/{chunk-CX6KVBW4.js → chunk-2LK6IILW.js} +10 -2
  4. package/dist/chunk-2LK6IILW.js.map +1 -0
  5. package/dist/chunk-HWEVRBKZ.js +387 -0
  6. package/dist/chunk-HWEVRBKZ.js.map +1 -0
  7. package/dist/cli/index.js +50 -22
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/index.cjs +483 -6
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +9 -4
  12. package/dist/index.d.ts +9 -4
  13. package/dist/index.js +105 -5
  14. package/dist/index.js.map +1 -1
  15. package/dist/providers/anthropic.d.cts +2 -2
  16. package/dist/providers/anthropic.d.ts +2 -2
  17. package/dist/providers/gemini.d.cts +2 -2
  18. package/dist/providers/gemini.d.ts +2 -2
  19. package/dist/providers/openai.d.cts +2 -2
  20. package/dist/providers/openai.d.ts +2 -2
  21. package/dist/providers/openrouter.d.cts +2 -2
  22. package/dist/providers/openrouter.d.ts +2 -2
  23. package/dist/{schema-C6smABrt.d.cts → schema-DIOA4OiO.d.cts} +53 -32
  24. package/dist/{schema-C6smABrt.d.ts → schema-DIOA4OiO.d.ts} +53 -32
  25. package/dist/testing.cjs +9 -1
  26. package/dist/testing.cjs.map +1 -1
  27. package/dist/testing.d.cts +1 -1
  28. package/dist/testing.d.ts +1 -1
  29. package/dist/testing.js +1 -1
  30. package/dist/{types-S_c-ZEfK.d.cts → types-1zDDD-Ij.d.cts} +1 -1
  31. package/dist/{types-D7lW5IYT.d.ts → types-CQhlfAnR.d.ts} +1 -1
  32. package/dist/usagetap/index.cjs +425 -0
  33. package/dist/usagetap/index.cjs.map +1 -0
  34. package/dist/usagetap/index.d.cts +147 -0
  35. package/dist/usagetap/index.d.ts +147 -0
  36. package/dist/usagetap/index.js +31 -0
  37. package/dist/usagetap/index.js.map +1 -0
  38. package/package.json +11 -1
  39. package/dist/chunk-CX6KVBW4.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # PromptOpsKit
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/promptopskit.svg)](https://www.npmjs.com/package/promptopskit)
4
+ [![CI](https://github.com/PredictabilityAtScale/promptopskit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/PredictabilityAtScale/promptopskit/actions/workflows/ci.yml)
4
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
6
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org)
6
7
 
@@ -63,7 +64,8 @@ sampling:
63
64
  context:
64
65
  inputs:
65
66
  - user_message
66
- - app_context
67
+ - name: app_context
68
+ max_size: 2000
67
69
  includes:
68
70
  - ./shared/tone.md
69
71
  ---
@@ -104,6 +106,19 @@ const response = await fetch('https://api.openai.com/v1/chat/completions', {
104
106
  });
105
107
  ```
106
108
 
109
+ You can control context size warning behavior at the kit level:
110
+
111
+ ```typescript
112
+ const kit = createPromptOpsKit({
113
+ sourceDir: './prompts',
114
+ warnings: {
115
+ contextSize: process.env.NODE_ENV === 'production' ? 'off' : 'console-and-result',
116
+ },
117
+ });
118
+ ```
119
+
120
+ Supported values for `warnings.contextSize` are `auto`, `off`, `result-only`, `console`, and `console-and-result`.
121
+
107
122
  ## Features
108
123
 
109
124
  - **Prompts as Markdown** — YAML front matter for settings, H1 headings for sections (`# System instructions`, `# Prompt template`, `# Notes`)
@@ -113,6 +128,8 @@ const response = await fetch('https://api.openai.com/v1/chat/completions', {
113
128
  - **Overrides** — Environment and tier-based overrides (base → env → tier → runtime)
114
129
  - **4 provider adapters** — OpenAI, Anthropic, Gemini, OpenRouter — body-only output
115
130
  - **Validation** — Zod schema validation, Levenshtein-based "did you mean?" for typos, variable usage checks
131
+ - **Context size guardrails** — optional per-input `max_size` metadata with non-blocking render-time warnings
132
+ - **Warning controls** — top-level config can suppress or emit context size warnings differently in dev and prod
116
133
  - **Caching** — LRU cache with mtime-based invalidation
117
134
  - **CLI** — init, validate, compile, render, inspect, skill
118
135
  - **Compiled artifacts** — Pre-compile `.md` → JSON or ESM for production
@@ -165,6 +182,71 @@ import { geminiAdapter } from 'promptopskit/gemini';
165
182
  import { openrouterAdapter } from 'promptopskit/openrouter';
166
183
  ```
167
184
 
185
+ ## Optional UsageTap Tracking
186
+
187
+ PromptOpsKit can also help you track provider calls with UsageTap.com while keeping the core render API body-only.
188
+
189
+ ```typescript
190
+ import { createPromptOpsKit } from 'promptopskit';
191
+ import { createUsageTapClient, runOpenAIWithUsageTap } from 'promptopskit/usagetap';
192
+
193
+ const kit = createPromptOpsKit({ sourceDir: './prompts' });
194
+ const usageTap = createUsageTapClient({ apiKey: process.env.USAGETAP_API_KEY! });
195
+
196
+ const { request } = await kit.renderPrompt({
197
+ path: 'support/reply',
198
+ provider: 'openai',
199
+ variables: {
200
+ user_message: 'How do I reset my password?',
201
+ app_context: 'Account settings page',
202
+ },
203
+ });
204
+
205
+ const tracked = await runOpenAIWithUsageTap(usageTap, {
206
+ begin: {
207
+ customerId: 'user_123',
208
+ feature: 'chat.send',
209
+ requested: { standard: true, premium: true, search: true },
210
+ idempotencyKey: 'chat-send-user-123-req-456',
211
+ },
212
+ request,
213
+ entitlementMode: 'apply',
214
+ modelTiers: {
215
+ standard: 'gpt-5.4-mini',
216
+ premium: 'gpt-5.4',
217
+ },
218
+ toolEntitlements: {
219
+ image_tool: 'image',
220
+ web_lookup: 'search',
221
+ },
222
+ invoke: async (requestUsed) => {
223
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
224
+ method: 'POST',
225
+ headers: {
226
+ 'Content-Type': 'application/json',
227
+ Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
228
+ },
229
+ body: JSON.stringify(requestUsed.body),
230
+ });
231
+
232
+ return response.json();
233
+ },
234
+ });
235
+
236
+ // tracked.response -> vendor JSON response
237
+ // tracked.begin -> UsageTap call_begin payload
238
+ // tracked.end -> UsageTap call_end payload
239
+ // tracked.requestUsed -> effective request after optional entitlement changes
240
+ // tracked.effectiveUsage -> usage sent to UsageTap
241
+ ```
242
+
243
+ Notes:
244
+ - `entitlementMode` defaults to `'off'`. Set it to `'apply'` only when you want UsageTap allowances to mutate a cloned provider request.
245
+ - `runOpenRouterWithUsageTap`, `runAnthropicWithUsageTap`, and `runGeminiWithUsageTap` follow the same pattern.
246
+ - `extractOpenAIUsage`, `extractAnthropicUsage`, and `extractGeminiUsage` are public if you want to manage UsageTap lifecycle yourself.
247
+
248
+ For explicit lifecycle control, use `beginUsageTapCall`, `endUsageTapCall`, or `withUsageTapCall` from `promptopskit/usagetap`. Full documentation: [docs/usagetap.md](./docs/usagetap.md).
249
+
168
250
  ## Overrides
169
251
 
170
252
  Define environment and tier overrides in front matter. Precedence: **base → environment → tier → runtime**. Scalars and arrays are replaced, not merged.
@@ -333,6 +415,7 @@ Creates a `PromptOpsKit` instance.
333
415
  | `compiledDir` | `string` | — | Path to compiled artifacts |
334
416
  | `mode` | `'auto' \| 'compiled-only' \| 'source-only'` | `'auto'` | Resolution strategy |
335
417
  | `cache` | `boolean` | `true` | Enable LRU cache with mtime invalidation |
418
+ | `warnings.contextSize` | `'auto' \| 'off' \| 'result-only' \| 'console' \| 'console-and-result'` | `'auto'` | Control whether render-time context size warnings are returned, logged, both, or suppressed |
336
419
 
337
420
  ### `kit.renderPrompt(options)`
338
421
 
@@ -347,6 +430,7 @@ Renders a prompt for a specific provider. Returns `{ resolved, request, warnings
347
430
  | `environment` | `string` | Environment override name |
348
431
  | `tier` | `string` | Tier override name |
349
432
  | `history` | `Array<{ role, content }>` | Conversation history |
433
+ | `toolRegistry` | `Record<string, unknown>` | Tool definitions for resolving string tool references |
350
434
  | `strict` | `boolean` | Fail on missing variables |
351
435
 
352
436
  ### `kit.loadPrompt(path)` / `kit.resolvePrompt(path, options)` / `kit.validatePrompt(path)`
@@ -375,7 +459,7 @@ Prompt files use YAML front matter with these fields:
375
459
  | `response` | `object` | `{ format, stream }` |
376
460
  | `tools` | `array` | Tool references (string names or inline definitions) |
377
461
  | `mcp` | `object` | MCP server references |
378
- | `context` | `object` | `{ inputs, history }` — declare expected variables |
462
+ | `context` | `object` | `{ inputs, history }` — declare expected variables, with optional per-input `max_size` budgets |
379
463
  | `includes` | `string[]` | Paths to included prompt files |
380
464
  | `environments` | `object` | Named environment overrides |
381
465
  | `tiers` | `object` | Named tier overrides |
package/SKILL.md CHANGED
@@ -29,7 +29,8 @@ provider: openai
29
29
  model: gpt-5.4
30
30
  context:
31
31
  inputs:
32
- - name
32
+ - name: name
33
+ max_size: 2000
33
34
  ---
34
35
 
35
36
  # System instructions
@@ -65,7 +66,7 @@ the fields required by that specific file:
65
66
  | `response` | object | no | `{ format: text|json|markdown, stream: boolean }` |
66
67
  | `tools` | array | no | Tool names (strings) or inline definitions with `{ name, description, input_schema }` |
67
68
  | `mcp` | object | no | `{ servers: [string | { name, config }] }` |
68
- | `context.inputs` | string[] | no | Declared variable names used in templates |
69
+ | `context.inputs` | `Array<string | { name, max_size? }>` | no | Declared variable names used in templates, with optional size budgets |
69
70
  | `context.history` | object | no | `{ max_items: number }` |
70
71
  | `includes` | string[] | no | Relative paths to other prompt files to include |
71
72
  | `environments` | object | no | Per-environment overrides (see Overrides) |
@@ -98,10 +99,23 @@ Rules:
98
99
  - Declare all variables in `context.inputs` — validation warns on undeclared usage
99
100
  - Before finishing a new prompt file, scan the body for every `{{ variable }}` and
100
101
  ensure each exact variable name appears in `context.inputs`
102
+ - Use object-form inputs with `max_size` when a variable is likely to grow large and should trigger early warnings
101
103
  - Escape literal braces with `\{{` and `\}}`
102
104
  - In strict mode, missing variables throw an error
103
105
  - In permissive mode, unresolved placeholders are left intact
104
106
 
107
+ Example with a size budget:
108
+
109
+ ```yaml
110
+ context:
111
+ inputs:
112
+ - user_message
113
+ - name: account_summary
114
+ max_size: 4096
115
+ ```
116
+
117
+ If a rendered value exceeds `max_size`, `renderPrompt()` emits a non-blocking `POK030` warning.
118
+
105
119
  Example: this is the minimal valid shape for a prompt that references
106
120
  `{{ pull_request }}` even when provider/model are inherited from defaults:
107
121
 
@@ -238,19 +252,32 @@ import { createPromptOpsKit } from 'promptopskit';
238
252
  const kit = createPromptOpsKit({ sourceDir: './prompts' });
239
253
 
240
254
  // Load → resolve includes → apply overrides → render
241
- const request = await kit.renderPrompt('greeting', {
255
+ const result = await kit.renderPrompt({
256
+ path: 'greeting',
257
+ provider: 'openai',
242
258
  variables: { name: 'Alice' },
243
259
  environment: 'production',
244
260
  });
245
261
 
246
- // request.body is ready for the provider's API
262
+ // result.request.body is ready for the provider's API
247
263
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
248
264
  method: 'POST',
249
265
  headers: {
250
266
  'Content-Type': 'application/json',
251
267
  Authorization: `Bearer ${apiKey}`,
252
268
  },
253
- body: JSON.stringify(request.body),
269
+ body: JSON.stringify(result.request.body),
270
+ });
271
+ ```
272
+
273
+ You can control render-time context size warnings at the top level:
274
+
275
+ ```typescript
276
+ const kit = createPromptOpsKit({
277
+ sourceDir: './prompts',
278
+ warnings: {
279
+ contextSize: process.env.NODE_ENV === 'production' ? 'off' : 'console-and-result',
280
+ },
254
281
  });
255
282
  ```
256
283
 
@@ -307,7 +334,7 @@ const asset = parsePrompt(source);
307
334
  const result = validateAsset(asset);
308
335
 
309
336
  if (!result.valid) {
310
- console.error(result.errors); // Error codes: POK001-POK021
337
+ console.error(result.errors); // Validation error codes: POK001-POK021
311
338
  }
312
339
  ```
313
340
 
@@ -32,8 +32,16 @@ var ResponseSchema = z.object({
32
32
  var HistorySchema = z.object({
33
33
  max_items: z.number().int().positive().optional()
34
34
  });
35
+ var ContextInputDefinitionObjectSchema = z.object({
36
+ name: z.string(),
37
+ max_size: z.number().int().positive().optional()
38
+ });
39
+ var ContextInputDefinitionSchema = z.union([
40
+ z.string(),
41
+ ContextInputDefinitionObjectSchema
42
+ ]);
35
43
  var ContextSchema = z.object({
36
- inputs: z.array(z.string()).optional(),
44
+ inputs: z.array(ContextInputDefinitionSchema).optional(),
37
45
  history: HistorySchema.optional()
38
46
  });
39
47
  var MetadataSchema = z.object({
@@ -250,4 +258,4 @@ export {
250
258
  parsePrompt,
251
259
  loadPromptFile
252
260
  };
253
- //# sourceMappingURL=chunk-CX6KVBW4.js.map
261
+ //# sourceMappingURL=chunk-2LK6IILW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema/schema.ts","../src/parser/sections.ts","../src/parser/parser.ts","../src/parser/loader.ts"],"sourcesContent":["import { z } from 'zod';\n\n// --- Tool definitions ---\n\nexport const InlineToolDefSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n input_schema: z.record(z.unknown()).optional(),\n});\n\nexport type InlineToolDef = z.infer<typeof InlineToolDefSchema>;\n\nexport const ToolRefSchema = z.union([z.string(), InlineToolDefSchema]);\n\n// --- MCP ---\n\nexport const MCPServerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n }),\n]);\n\nexport type MCPServerRef = z.infer<typeof MCPServerRefSchema>;\n\n// --- Reasoning ---\n\nexport const ReasoningSchema = z.object({\n effort: z.enum(['low', 'medium', 'high']).optional(),\n budget_tokens: z.number().int().positive().optional(),\n});\n\n// --- Sampling ---\n\nexport const SamplingSchema = z.object({\n temperature: z.number().min(0).max(2).optional(),\n top_p: z.number().min(0).max(1).optional(),\n frequency_penalty: z.number().optional(),\n presence_penalty: z.number().optional(),\n stop: z.array(z.string()).optional(),\n max_output_tokens: z.number().int().positive().optional(),\n});\n\n// --- Response ---\n\nexport const ResponseSchema = z.object({\n format: z.enum(['text', 'json', 'markdown']).optional(),\n stream: z.boolean().optional(),\n});\n\n// --- Context ---\n\nexport const HistorySchema = z.object({\n max_items: z.number().int().positive().optional(),\n});\n\nexport const ContextInputDefinitionObjectSchema = z.object({\n name: z.string(),\n max_size: z.number().int().positive().optional(),\n});\n\nexport const ContextInputDefinitionSchema = z.union([\n z.string(),\n ContextInputDefinitionObjectSchema,\n]);\n\nexport type ContextInputDefinition = z.infer<typeof ContextInputDefinitionSchema>;\n\nexport const ContextSchema = z.object({\n inputs: z.array(ContextInputDefinitionSchema).optional(),\n history: HistorySchema.optional(),\n});\n\n// --- Metadata ---\n\nexport const MetadataSchema = z.object({\n owner: z.string().optional(),\n tags: z.array(z.string()).optional(),\n review_required: z.boolean().optional(),\n stable: z.boolean().optional(),\n});\n\n// --- MCP block ---\n\nexport const MCPSchema = z.object({\n servers: z.array(MCPServerRefSchema).optional(),\n});\n\n// --- Overrides (subset allowed in environments/tiers) ---\n\nexport const PromptAssetOverridesSchema = z.object({\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n tools: z.array(ToolRefSchema).optional(),\n});\n\nexport type PromptAssetOverrides = z.infer<typeof PromptAssetOverridesSchema>;\n\n// --- Source tracking ---\n\nexport const SourceSchema = z.object({\n file_path: z.string().optional(),\n checksum: z.string().optional(),\n});\n\n// --- Sections (populated by parser) ---\n\nexport const SectionsSchema = z.object({\n system_instructions: z.string().optional(),\n prompt_template: z.string().optional(),\n notes: z.string().optional(),\n});\n\n// --- Defaults files (folder-level inheritance) ---\n\nexport const PromptDefaultsSchema = z.object({\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n metadata: MetadataSchema.optional(),\n sections: z.object({\n system_instructions: z.string().optional(),\n }).optional(),\n});\n\nexport type PromptDefaults = z.infer<typeof PromptDefaultsSchema>;\n\n// --- Top-level PromptAsset ---\n\nexport const PromptAssetSchema = z.object({\n id: z.string(),\n schema_version: z.number().int().positive().default(1),\n description: z.string().optional(),\n\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n\n tools: z.array(ToolRefSchema).optional(),\n mcp: MCPSchema.optional(),\n\n context: ContextSchema.optional(),\n\n includes: z.array(z.string()).optional(),\n\n environments: z.record(PromptAssetOverridesSchema).optional(),\n tiers: z.record(PromptAssetOverridesSchema).optional(),\n\n metadata: MetadataSchema.optional(),\n\n // Populated by parser, not authored in YAML\n sections: SectionsSchema.optional(),\n source: SourceSchema.optional(),\n});\n\nexport type PromptAsset = z.infer<typeof PromptAssetSchema>;\n\n// --- Resolved asset (after includes, overrides applied) ---\n\nexport interface ResolvedPromptAsset extends PromptAsset {\n sections: {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n };\n source: {\n file_path?: string;\n checksum?: string;\n };\n}\n","export interface Sections {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n}\n\nconst SECTION_MAP: Record<string, keyof Sections> = {\n 'system instructions': 'system_instructions',\n 'prompt template': 'prompt_template',\n 'notes': 'notes',\n};\n\n/**\n * Extract named sections from the markdown body using H1 headings.\n * Case-insensitive. H2+ within a section is treated as content.\n * If no H1 headings are found, the entire body is treated as prompt_template.\n */\nexport function extractSections(body: string): Sections {\n const lines = body.split(/\\r?\\n/);\n const sections: Sections = {};\n\n let currentKey: keyof Sections | null = null;\n let currentLines: string[] = [];\n let foundAnyH1 = false;\n\n for (const line of lines) {\n const h1Match = line.match(/^#\\s+(.+)$/);\n if (h1Match) {\n // Flush previous section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n foundAnyH1 = true;\n const heading = h1Match[1].trim().toLowerCase();\n currentKey = SECTION_MAP[heading] ?? null;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n // Flush last section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n // If no H1 headings found, treat entire body as prompt_template\n if (!foundAnyH1) {\n const trimmed = body.trim();\n if (trimmed) {\n sections.prompt_template = trimmed;\n }\n }\n\n return sections;\n}\n","import matter from 'gray-matter';\nimport { PromptAssetSchema } from '../schema/index.js';\nimport type { PromptAsset } from '../schema/index.js';\nimport { extractSections } from './sections.js';\n\nexport interface ParseResult {\n asset: PromptAsset;\n raw: {\n frontMatter: Record<string, unknown>;\n body: string;\n };\n}\n\n/**\n * Parse a prompt markdown string (YAML front matter + markdown body)\n * into a validated PromptAsset.\n */\nexport function parsePrompt(content: string, filePath?: string): ParseResult {\n const { data: frontMatter, content: body } = matter(content);\n\n const sections = extractSections(body);\n\n const raw = {\n ...frontMatter,\n sections,\n source: filePath ? { file_path: filePath } : undefined,\n };\n\n const asset = PromptAssetSchema.parse(raw);\n\n return {\n asset,\n raw: {\n frontMatter: frontMatter as Record<string, unknown>,\n body,\n },\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport matter from 'gray-matter';\nimport { parsePrompt } from './parser.js';\nimport type { ParseResult } from './parser.js';\nimport { extractSections } from './sections.js';\nimport { PromptDefaultsSchema } from '../schema/index.js';\nimport type { PromptDefaults } from '../schema/index.js';\n\nconst DEFAULTS_FILE_NAME = 'defaults.md';\n\nexport interface LoadPromptOptions {\n /**\n * Optional boundary directory for defaults discovery.\n * If provided, defaults are loaded from this directory down to the prompt directory.\n */\n defaultsRoot?: string;\n}\n\n/**\n * Load and parse a prompt file from disk.\n */\nexport async function loadPromptFile(filePath: string, options: LoadPromptOptions = {}): Promise<ParseResult> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = parsePrompt(content, filePath);\n // Default the boundary to the file's own directory so traversal never\n // walks above the prompt tree when no explicit root is provided.\n const root = options.defaultsRoot ?? dirname(filePath);\n const defaults = await loadDefaultsForPath(filePath, root);\n const asset = applyDefaults(parsed.asset, defaults);\n\n return {\n ...parsed,\n asset,\n };\n}\n\nasync function loadDefaultsForPath(filePath: string, defaultsRoot?: string): Promise<PromptDefaults> {\n const directories = getDirectoriesToCheck(filePath, defaultsRoot);\n let merged: PromptDefaults = {};\n\n for (const dir of directories) {\n const defaultsPath = join(dir, DEFAULTS_FILE_NAME);\n try {\n const defaultsContent = await readFile(defaultsPath, 'utf-8');\n const defaults = parseDefaults(defaultsContent);\n merged = mergeDefaults(merged, defaults);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n return merged;\n}\n\nfunction getDirectoriesToCheck(filePath: string, defaultsRoot?: string): string[] {\n const dirs: string[] = [];\n let current = resolve(dirname(filePath));\n const boundary = defaultsRoot ? resolve(defaultsRoot) : undefined;\n\n while (true) {\n dirs.unshift(current);\n if ((boundary && current === boundary) || current === dirname(current)) {\n break;\n }\n current = dirname(current);\n }\n\n return dirs;\n}\n\nfunction parseDefaults(content: string): PromptDefaults {\n const { data: frontMatter, content: body } = matter(content);\n const sections = extractSections(body);\n\n return PromptDefaultsSchema.parse({\n ...frontMatter,\n sections: {\n system_instructions: sections.system_instructions,\n },\n });\n}\n\nfunction mergeDefaults(base: PromptDefaults, local: PromptDefaults): PromptDefaults {\n return {\n provider: local.provider ?? base.provider,\n model: local.model ?? base.model,\n metadata: {\n ...(base.metadata ?? {}),\n ...(local.metadata ?? {}),\n },\n sections: {\n ...(base.sections ?? {}),\n ...(local.sections ?? {}),\n },\n };\n}\n\nfunction applyDefaults(asset: ParseResult['asset'], defaults: PromptDefaults): ParseResult['asset'] {\n const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;\n const hasDefaultSystem = !!defaults.sections?.system_instructions;\n const hasDefaultScalars = defaults.provider !== undefined\n || defaults.model !== undefined;\n\n // Short-circuit: nothing to merge\n if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {\n return asset;\n }\n\n const mergedMetadata = {\n ...(defaults.metadata ?? {}),\n ...(asset.metadata ?? {}),\n };\n const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : undefined;\n\n const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;\n\n const sections = asset.sections\n ? { ...asset.sections, system_instructions: systemInstructions }\n : systemInstructions\n ? { system_instructions: systemInstructions }\n : undefined;\n\n return {\n ...asset,\n provider: asset.provider ?? defaults.provider,\n model: asset.model ?? defaults.model,\n metadata,\n sections,\n };\n}\n"],"mappings":"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","names":["matter","matter"]}
@@ -0,0 +1,387 @@
1
+ // src/usagetap/client.ts
2
+ var DEFAULT_BASE_URL = "https://api.usagetap.com";
3
+ var ACCEPT_HEADER = "application/vnd.usagetap.v1+json";
4
+ function createUsageTapHttpError(status, body) {
5
+ const error = new Error(`UsageTap request failed with status ${status}`);
6
+ error.statusCode = status;
7
+ error.body = body;
8
+ return error;
9
+ }
10
+ function createUsageTapClient(config) {
11
+ const fetchImpl = config.fetch ?? globalThis.fetch;
12
+ if (!fetchImpl) {
13
+ throw new Error("Fetch API is not available. Provide config.fetch when creating the UsageTap client.");
14
+ }
15
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
16
+ async function request(path, body, init = {}) {
17
+ const response = await fetchImpl(`${baseUrl}${path}`, {
18
+ method: "POST",
19
+ headers: {
20
+ Authorization: `Bearer ${config.apiKey}`,
21
+ Accept: ACCEPT_HEADER,
22
+ "Content-Type": "application/json"
23
+ },
24
+ body: JSON.stringify(body),
25
+ signal: init.signal
26
+ });
27
+ const text = await response.text();
28
+ const parsed = text ? JSON.parse(text) : {};
29
+ if (!response.ok) {
30
+ throw createUsageTapHttpError(response.status, parsed);
31
+ }
32
+ return parsed;
33
+ }
34
+ return {
35
+ request,
36
+ beginCall(begin, init) {
37
+ return request("/call_begin", begin, init);
38
+ },
39
+ endCall(end, init) {
40
+ return request("/call_end", end, init);
41
+ }
42
+ };
43
+ }
44
+
45
+ // src/usagetap/lifecycle.ts
46
+ function isObject(value) {
47
+ return typeof value === "object" && value !== null;
48
+ }
49
+ function isInvokeResult(value) {
50
+ return isObject(value) && "result" in value && "usage" in value;
51
+ }
52
+ function getErrorStatusCode(error) {
53
+ if (!isObject(error)) {
54
+ return void 0;
55
+ }
56
+ if (typeof error.statusCode === "number") {
57
+ return error.statusCode;
58
+ }
59
+ if (typeof error.status === "number") {
60
+ return error.status;
61
+ }
62
+ if (isObject(error.response) && typeof error.response.status === "number") {
63
+ return error.response.status;
64
+ }
65
+ return void 0;
66
+ }
67
+ function attachEndErrorCause(thrownError, endError) {
68
+ if (thrownError instanceof Error) {
69
+ if (thrownError.cause === void 0) {
70
+ Object.defineProperty(thrownError, "cause", {
71
+ value: endError,
72
+ configurable: true,
73
+ writable: true
74
+ });
75
+ }
76
+ throw thrownError;
77
+ }
78
+ throw new AggregateError([thrownError, endError], "Vendor call failed and UsageTap call_end also failed.");
79
+ }
80
+ function defaultUsageTapErrorMapper(error) {
81
+ return {
82
+ code: "VENDOR_ERROR",
83
+ message: String(error)
84
+ };
85
+ }
86
+ function beginUsageTapCall(client, begin, init) {
87
+ return client.beginCall(begin, init);
88
+ }
89
+ function endUsageTapCall(client, end, init) {
90
+ return client.endCall(end, init);
91
+ }
92
+ async function withUsageTapCall(client, options) {
93
+ const begin = await beginUsageTapCall(client, options.begin, { signal: options.signal });
94
+ const onError = options.onError ?? defaultUsageTapErrorMapper;
95
+ let result;
96
+ let usage;
97
+ let thrownError;
98
+ const setUsage = (nextUsage) => {
99
+ usage = { ...usage, ...nextUsage };
100
+ };
101
+ try {
102
+ const invoked = await options.invoke({
103
+ begin,
104
+ setUsage,
105
+ signal: options.signal
106
+ });
107
+ if (isInvokeResult(invoked)) {
108
+ result = invoked.result;
109
+ setUsage(invoked.usage);
110
+ } else {
111
+ result = invoked;
112
+ }
113
+ } catch (error) {
114
+ thrownError = error;
115
+ }
116
+ const effectiveUsage = {
117
+ ...usage
118
+ };
119
+ if (thrownError) {
120
+ effectiveUsage.error = effectiveUsage.error ?? onError(thrownError);
121
+ effectiveUsage.responseStatusCode = effectiveUsage.responseStatusCode ?? getErrorStatusCode(thrownError);
122
+ } else {
123
+ effectiveUsage.responseStatusCode = effectiveUsage.responseStatusCode ?? 200;
124
+ }
125
+ let end;
126
+ try {
127
+ end = await endUsageTapCall(client, {
128
+ callId: begin.data.callId,
129
+ ...effectiveUsage
130
+ }, { signal: options.signal });
131
+ } catch (endError) {
132
+ if (thrownError) {
133
+ attachEndErrorCause(thrownError, endError);
134
+ }
135
+ throw endError;
136
+ }
137
+ if (thrownError) {
138
+ throw thrownError;
139
+ }
140
+ return {
141
+ result,
142
+ begin,
143
+ end,
144
+ effectiveUsage
145
+ };
146
+ }
147
+
148
+ // src/usagetap/providers.ts
149
+ function cloneRequest(request) {
150
+ return structuredClone(request);
151
+ }
152
+ function isObject2(value) {
153
+ return typeof value === "object" && value !== null;
154
+ }
155
+ function isRecordArray(value) {
156
+ return Array.isArray(value) && value.every((item) => isObject2(item));
157
+ }
158
+ function isInvokeResult2(value) {
159
+ return isObject2(value) && "result" in value && "usage" in value;
160
+ }
161
+ function capabilityAllowed(allowed, capability) {
162
+ return allowed[capability] !== false;
163
+ }
164
+ function reasoningLevelOrder(level) {
165
+ switch (level) {
166
+ case "HIGH":
167
+ return 3;
168
+ case "MEDIUM":
169
+ return 2;
170
+ case "LOW":
171
+ return 1;
172
+ default:
173
+ return 0;
174
+ }
175
+ }
176
+ function capOpenAIReasoning(body, allowed) {
177
+ if (typeof body.reasoning_effort !== "string") {
178
+ return;
179
+ }
180
+ const current = String(body.reasoning_effort).toUpperCase();
181
+ const allowedLevel = allowed.reasoningLevel ?? "HIGH";
182
+ if (allowedLevel === "NONE") {
183
+ delete body.reasoning_effort;
184
+ return;
185
+ }
186
+ if (reasoningLevelOrder(current) > reasoningLevelOrder(allowedLevel)) {
187
+ body.reasoning_effort = allowedLevel.toLowerCase();
188
+ }
189
+ }
190
+ function capGeminiReasoning(body, allowed) {
191
+ if (allowed.reasoningLevel === "NONE") {
192
+ delete body.thinkingConfig;
193
+ return;
194
+ }
195
+ const budgets = {
196
+ LOW: 1024,
197
+ MEDIUM: 4096,
198
+ HIGH: 8192
199
+ };
200
+ const allowedBudget = allowed.reasoningLevel ? budgets[allowed.reasoningLevel] : void 0;
201
+ if (allowedBudget === void 0) {
202
+ return;
203
+ }
204
+ if (!isObject2(body.thinkingConfig)) {
205
+ body.thinkingConfig = { thinkingBudget: allowedBudget };
206
+ return;
207
+ }
208
+ const thinkingConfig = body.thinkingConfig;
209
+ if (typeof thinkingConfig.thinkingBudget === "number") {
210
+ thinkingConfig.thinkingBudget = Math.min(thinkingConfig.thinkingBudget, allowedBudget);
211
+ return;
212
+ }
213
+ thinkingConfig.thinkingBudget = allowedBudget;
214
+ }
215
+ function applyModelTier(request, allowed, options) {
216
+ const nextModel = allowed.premium && options.modelTiers?.premium ? options.modelTiers.premium : allowed.standard && options.modelTiers?.standard ? options.modelTiers.standard : void 0;
217
+ if (!nextModel) {
218
+ return;
219
+ }
220
+ request.model = nextModel;
221
+ if (isObject2(request.body) && "model" in request.body) {
222
+ request.body.model = nextModel;
223
+ }
224
+ }
225
+ function filterOpenAITools(tools, allowed, options) {
226
+ return tools.filter((tool) => {
227
+ if (tool.type === "web_search" && allowed.search === false) {
228
+ return false;
229
+ }
230
+ const functionDef = isObject2(tool.function) ? tool.function : void 0;
231
+ const name = typeof functionDef?.name === "string" ? functionDef.name : typeof tool.name === "string" ? tool.name : typeof tool.type === "string" ? tool.type : void 0;
232
+ if (!name) {
233
+ return true;
234
+ }
235
+ const capability = options.toolEntitlements?.[name];
236
+ return capability ? capabilityAllowed(allowed, capability) : true;
237
+ });
238
+ }
239
+ function filterAnthropicTools(tools, allowed, options) {
240
+ return tools.filter((tool) => {
241
+ const name = typeof tool.name === "string" ? tool.name : void 0;
242
+ if (!name) {
243
+ return true;
244
+ }
245
+ const capability = options.toolEntitlements?.[name];
246
+ return capability ? capabilityAllowed(allowed, capability) : true;
247
+ });
248
+ }
249
+ function filterGeminiTools(tools, allowed, options) {
250
+ return tools.map((tool) => {
251
+ const declarations = Array.isArray(tool.functionDeclarations) ? tool.functionDeclarations.filter((declaration) => {
252
+ if (!isObject2(declaration) || typeof declaration.name !== "string") {
253
+ return true;
254
+ }
255
+ const capability = options.toolEntitlements?.[declaration.name];
256
+ return capability ? capabilityAllowed(allowed, capability) : true;
257
+ }) : tool.functionDeclarations;
258
+ return {
259
+ ...tool,
260
+ functionDeclarations: declarations
261
+ };
262
+ }).filter((tool) => {
263
+ if (!Array.isArray(tool.functionDeclarations)) {
264
+ return true;
265
+ }
266
+ return tool.functionDeclarations.length > 0;
267
+ });
268
+ }
269
+ function applyUsageTapEntitlements(request, begin, options = {}) {
270
+ const nextRequest = cloneRequest(request);
271
+ const allowed = begin.data.allowed ?? {};
272
+ applyModelTier(nextRequest, allowed, options);
273
+ if (!isObject2(nextRequest.body)) {
274
+ return nextRequest;
275
+ }
276
+ if (nextRequest.provider === "openai" || nextRequest.provider === "openrouter") {
277
+ capOpenAIReasoning(nextRequest.body, allowed);
278
+ if (isRecordArray(nextRequest.body.tools)) {
279
+ nextRequest.body.tools = filterOpenAITools(nextRequest.body.tools, allowed, options);
280
+ }
281
+ return nextRequest;
282
+ }
283
+ if (nextRequest.provider === "gemini") {
284
+ capGeminiReasoning(nextRequest.body, allowed);
285
+ if (isRecordArray(nextRequest.body.tools)) {
286
+ nextRequest.body.tools = filterGeminiTools(nextRequest.body.tools, allowed, options);
287
+ }
288
+ return nextRequest;
289
+ }
290
+ if (nextRequest.provider === "anthropic" && isRecordArray(nextRequest.body.tools)) {
291
+ nextRequest.body.tools = filterAnthropicTools(nextRequest.body.tools, allowed, options);
292
+ }
293
+ return nextRequest;
294
+ }
295
+ function extractOpenAIUsage(response, meta = {}) {
296
+ const usage = isObject2(response) && isObject2(response.usage) ? response.usage : {};
297
+ const promptDetails = isObject2(usage.prompt_tokens_details) ? usage.prompt_tokens_details : {};
298
+ const completionDetails = isObject2(usage.completion_tokens_details) ? usage.completion_tokens_details : {};
299
+ return {
300
+ modelUsed: meta.modelUsed,
301
+ inputTokens: typeof usage.prompt_tokens === "number" ? usage.prompt_tokens : 0,
302
+ responseTokens: typeof usage.completion_tokens === "number" ? usage.completion_tokens : 0,
303
+ cachedInputTokens: typeof promptDetails.cached_tokens === "number" ? promptDetails.cached_tokens : void 0,
304
+ reasoningTokens: typeof completionDetails.reasoning_tokens === "number" ? completionDetails.reasoning_tokens : void 0,
305
+ responseStatusCode: meta.responseStatusCode ?? 200
306
+ };
307
+ }
308
+ function extractAnthropicUsage(response, meta = {}) {
309
+ const usage = isObject2(response) && isObject2(response.usage) ? response.usage : {};
310
+ return {
311
+ modelUsed: meta.modelUsed,
312
+ inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
313
+ responseTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
314
+ cachedInputTokens: typeof usage.cache_read_input_tokens === "number" ? usage.cache_read_input_tokens : void 0,
315
+ responseStatusCode: meta.responseStatusCode ?? 200
316
+ };
317
+ }
318
+ function extractGeminiUsage(response, meta = {}) {
319
+ const usage = isObject2(response) && isObject2(response.usageMetadata) ? response.usageMetadata : {};
320
+ return {
321
+ modelUsed: meta.modelUsed,
322
+ inputTokens: typeof usage.promptTokenCount === "number" ? usage.promptTokenCount : 0,
323
+ responseTokens: typeof usage.candidatesTokenCount === "number" ? usage.candidatesTokenCount : 0,
324
+ cachedInputTokens: typeof usage.cachedContentTokenCount === "number" ? usage.cachedContentTokenCount : void 0,
325
+ reasoningTokens: typeof usage.thoughtsTokenCount === "number" ? usage.thoughtsTokenCount : void 0,
326
+ responseStatusCode: meta.responseStatusCode ?? 200
327
+ };
328
+ }
329
+ async function runProviderWithUsageTap(client, options, defaultExtractor) {
330
+ let requestUsed = cloneRequest(options.request);
331
+ const lifecycle = await withUsageTapCall(client, {
332
+ begin: options.begin,
333
+ signal: options.signal,
334
+ onError: options.onError ?? defaultUsageTapErrorMapper,
335
+ invoke: async ({ begin, setUsage }) => {
336
+ requestUsed = options.entitlementMode === "apply" ? applyUsageTapEntitlements(options.request, begin, options) : cloneRequest(options.request);
337
+ const invoked = await options.invoke(requestUsed);
338
+ if (isInvokeResult2(invoked)) {
339
+ return invoked;
340
+ }
341
+ const response = invoked;
342
+ const usage = (options.extractUsage ?? defaultExtractor)(response, {
343
+ modelUsed: requestUsed.model,
344
+ responseStatusCode: 200
345
+ });
346
+ setUsage(usage);
347
+ return response;
348
+ }
349
+ });
350
+ return {
351
+ response: lifecycle.result,
352
+ begin: lifecycle.begin,
353
+ end: lifecycle.end,
354
+ requestUsed,
355
+ effectiveUsage: lifecycle.effectiveUsage,
356
+ allowed: lifecycle.begin.data.allowed ?? {}
357
+ };
358
+ }
359
+ function runOpenAIWithUsageTap(client, options) {
360
+ return runProviderWithUsageTap(client, options, extractOpenAIUsage);
361
+ }
362
+ function runOpenRouterWithUsageTap(client, options) {
363
+ return runProviderWithUsageTap(client, options, extractOpenAIUsage);
364
+ }
365
+ function runAnthropicWithUsageTap(client, options) {
366
+ return runProviderWithUsageTap(client, options, extractAnthropicUsage);
367
+ }
368
+ function runGeminiWithUsageTap(client, options) {
369
+ return runProviderWithUsageTap(client, options, extractGeminiUsage);
370
+ }
371
+
372
+ export {
373
+ createUsageTapClient,
374
+ defaultUsageTapErrorMapper,
375
+ beginUsageTapCall,
376
+ endUsageTapCall,
377
+ withUsageTapCall,
378
+ applyUsageTapEntitlements,
379
+ extractOpenAIUsage,
380
+ extractAnthropicUsage,
381
+ extractGeminiUsage,
382
+ runOpenAIWithUsageTap,
383
+ runOpenRouterWithUsageTap,
384
+ runAnthropicWithUsageTap,
385
+ runGeminiWithUsageTap
386
+ };
387
+ //# sourceMappingURL=chunk-HWEVRBKZ.js.map