@warlock.js/core 4.6.0 → 4.7.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 (72) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/esm/ai/src/agent/agent-input-builder.mjs +1 -0
  3. package/esm/ai/src/agent/agent-input-builder.mjs.map +1 -1
  4. package/esm/ai/src/errors/index.mjs +1 -0
  5. package/esm/ai/src/errors/prompt-refinement-error.mjs +27 -0
  6. package/esm/ai/src/errors/prompt-refinement-error.mjs.map +1 -0
  7. package/esm/ai/src/index.mjs +2 -0
  8. package/esm/ai/src/prompts/prompts-manager.mjs +1 -1
  9. package/esm/ai/src/prompts/prompts-manager.mjs.map +1 -1
  10. package/esm/ai/src/prompts/prompts-validate.mjs +0 -0
  11. package/esm/ai/src/prompts/prompts-validate.mjs.map +1 -1
  12. package/esm/ai/src/system-prompt/index.mjs +1 -0
  13. package/esm/ai/src/system-prompt/refined-system-prompt.mjs +461 -0
  14. package/esm/ai/src/system-prompt/refined-system-prompt.mjs.map +1 -0
  15. package/esm/ai/src/system-prompt/system-prompt.mjs +19 -0
  16. package/esm/ai/src/system-prompt/system-prompt.mjs.map +1 -1
  17. package/esm/bootstrap.d.mts.map +1 -1
  18. package/esm/bootstrap.mjs +3 -1
  19. package/esm/bootstrap.mjs.map +1 -1
  20. package/esm/config/config-getter.mjs +5 -5
  21. package/esm/config/config-getter.mjs.map +1 -1
  22. package/esm/config/config-loader.mjs +2 -2
  23. package/esm/config/config-loader.mjs.map +1 -1
  24. package/esm/connectors/access-connector.mjs +2 -2
  25. package/esm/connectors/access-connector.mjs.map +1 -1
  26. package/esm/connectors/ai-connector.mjs +2 -2
  27. package/esm/connectors/ai-connector.mjs.map +1 -1
  28. package/esm/connectors/cache-connector.mjs +2 -2
  29. package/esm/connectors/cache-connector.mjs.map +1 -1
  30. package/esm/connectors/database-connector.mjs +2 -2
  31. package/esm/connectors/database-connector.mjs.map +1 -1
  32. package/esm/connectors/herald-connector.mjs +2 -2
  33. package/esm/connectors/herald-connector.mjs.map +1 -1
  34. package/esm/connectors/http-connector.mjs +7 -7
  35. package/esm/connectors/http-connector.mjs.map +1 -1
  36. package/esm/connectors/logger-connector.mjs +2 -2
  37. package/esm/connectors/logger-connector.mjs.map +1 -1
  38. package/esm/connectors/mail-connector.mjs +2 -2
  39. package/esm/connectors/mail-connector.mjs.map +1 -1
  40. package/esm/connectors/notifications-connector.mjs +2 -2
  41. package/esm/connectors/notifications-connector.mjs.map +1 -1
  42. package/esm/connectors/socket-connector.mjs +3 -3
  43. package/esm/connectors/socket-connector.mjs.map +1 -1
  44. package/esm/http/config.mjs +2 -2
  45. package/esm/http/config.mjs.map +1 -1
  46. package/esm/http/createHttpApplication.mjs +2 -2
  47. package/esm/http/createHttpApplication.mjs.map +1 -1
  48. package/esm/http/middleware/idempotency.middleware.mjs +5 -5
  49. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  50. package/esm/http/middleware/inject-request-context.mjs +2 -2
  51. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  52. package/esm/http/middleware/maintenance.middleware.mjs +4 -4
  53. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  54. package/esm/http/plugins.mjs +9 -9
  55. package/esm/http/plugins.mjs.map +1 -1
  56. package/esm/http/response.mjs +5 -5
  57. package/esm/http/response.mjs.map +1 -1
  58. package/esm/http/server.mjs +4 -4
  59. package/esm/http/server.mjs.map +1 -1
  60. package/esm/production/production-builder.mjs +24 -16
  61. package/esm/production/production-builder.mjs.map +1 -1
  62. package/esm/production/resolve-build-config.mjs +1 -1
  63. package/esm/production/resolve-build-config.mjs.map +1 -1
  64. package/esm/utils/paths.mjs +2 -2
  65. package/esm/utils/paths.mjs.map +1 -1
  66. package/esm/validation/validateAll.mjs +2 -2
  67. package/esm/validation/validateAll.mjs.map +1 -1
  68. package/esm/warlock-config/default-configurations.mjs +1 -1
  69. package/esm/warlock-config/default-configurations.mjs.map +1 -1
  70. package/esm/warlock-config/types.d.mts +3 -2
  71. package/esm/warlock-config/types.d.mts.map +1 -1
  72. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `@warlock.js/core` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 4.6.1
8
+
9
+ ### Fixed
10
+
11
+ - a fatal `uncaughtException` at production boot (e.g. a config file that throws) is no longer swallowed into a silent `exit 0` — bootstrap now wires the crash handler to exit non-zero in production so `warlock start` surfaces the failure; the dev server still logs-and-continues for HMR
12
+
7
13
  ## 4.6.0
8
14
 
9
15
  ### Added
@@ -40,6 +40,7 @@ async function buildAgentInputMessages(params) {
40
40
  let promptVersion;
41
41
  if (typeof systemPrompt === "string") systemContent = systemPrompt;
42
42
  else if (systemPrompt) {
43
+ if (typeof systemPrompt.materialize === "function") await systemPrompt.materialize();
43
44
  systemContent = systemPrompt.resolve(placeholders);
44
45
  const meta = systemPrompt.meta();
45
46
  if (meta?.name) {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-input-builder.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/agent/agent-input-builder.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { AgentExecuteOptions } from \"../contracts/agent/agent-options.type\";\nimport type { AttachmentPolicy } from \"../contracts/attachment-policy.type\";\nimport type { Attachment } from \"../contracts/attachment.type\";\nimport type { ContentPart } from \"../contracts/content-part.type\";\nimport type { Message } from \"../contracts/conversation-message.type\";\nimport type { Placeholders } from \"../contracts/placeholders.type\";\nimport { InvalidRequestError } from \"../errors\";\nimport { extractJsonSchema, prepareAttachmentPart } from \"../utils\";\nimport type { AgentConfig } from \"./agent-config.type\";\n\n/**\n * Outcome of `buildAgentInputMessages` — the seeded message list and\n * the JSON Schema cached for every trip's\n * `ModelCallOptions.responseSchema`. `responseSchema` is `undefined`\n * when the caller didn't ask for structured output.\n */\nexport type AgentInputBuildResult = {\n messages: Message[];\n responseSchema?: Record<string, unknown>;\n /**\n * The resolved system-prompt text actually sent as the `role: \"system\"`\n * message (persona + instructions + any auto-appended structured-output\n * instruction). Captured for observability; absent when the agent ran\n * without a system prompt.\n */\n systemPrompt?: string;\n /**\n * Registry name of the `SystemPromptContract` the agent resolved, read from\n * its `meta().name`. Present only when the agent ran against a *named*\n * prompt (one registered in `ai.prompts`); absent for a raw-string prompt,\n * an anonymous contract, or no prompt at all. Lets observers attribute a run\n * to a specific prompt in the registry.\n */\n promptName?: string;\n /**\n * Registry version label of the named prompt the agent resolved, read from\n * its `meta().version` (defaulting to `\"1\"` when the prompt carries a name\n * but no explicit version, mirroring the registry's default). Present only\n * alongside {@link AgentInputBuildResult.promptName}.\n */\n promptVersion?: string;\n};\n\n/**\n * Assemble the seed conversation for an agent execution. Runs exactly\n * once per run — subsequent trips append to the same message list.\n *\n * Responsibilities (previously three methods on `Execution`):\n * 1. Merge factory + per-call placeholders.\n * 2. Resolve the system prompt (string, contract, or absent).\n * 3. When an output schema is supplied:\n * - cache its JSON Schema form for `ModelCallOptions.responseSchema`\n * so native-structured-output providers enforce it at the token\n * level;\n * - fall back to a soft system-prompt instruction for providers\n * that don't advertise `structuredOutput` capability.\n * 4. Append caller-supplied `history` (e.g. session-level prior turns).\n * 5. Shape the user message — plain string in the common case,\n * multipart `ContentPart[]` when `attachments` are present. Image\n * attachments require model vision capability; mismatch throws\n * `InvalidRequestError` here rather than failing opaquely at the\n * provider.\n *\n * Extracted from the `Execution` class to isolate the declarative\n * input-shaping phase from the stateful trip loop.\n */\nexport async function buildAgentInputMessages<TOutput>(params: {\n config: AgentConfig<TOutput>;\n input: string;\n options?: AgentExecuteOptions<TOutput>;\n}): Promise<AgentInputBuildResult> {\n const { config, input, options } = params;\n\n const placeholders: Placeholders = {\n ...config.placeholders,\n ...options?.placeholders,\n };\n\n const systemPrompt = options?.systemPrompt ?? config.systemPrompt;\n let systemContent = \"\";\n let promptName: string | undefined;\n let promptVersion: string | undefined;\n\n if (typeof systemPrompt === \"string\") {\n systemContent = systemPrompt;\n } else if (systemPrompt) {\n systemContent = systemPrompt.resolve(placeholders);\n\n // Capture prompt-version linkage from the contract's metadata: a *named*\n // prompt (one addressable in `ai.prompts`) stamps `promptName@version`\n // onto the run's report so observers can group runs by the exact prompt\n // version that produced them. Anonymous prompts carry no name and are\n // left unlinked.\n const meta = systemPrompt.meta();\n\n if (meta?.name) {\n promptName = meta.name;\n promptVersion = meta.version ?? \"1\";\n }\n }\n\n const { responseSchema, instruction } = resolveStructuredOutput({\n outputSchema: options?.output ?? config.output,\n overrideResponseSchema: options?.responseSchema,\n modelSupportsStructuredOutput: Boolean(config.model.capabilities?.structuredOutput),\n });\n\n if (instruction) {\n systemContent = systemContent ? `${systemContent}\\n\\n${instruction}` : instruction;\n }\n\n const messages: Message[] = [];\n\n if (systemContent) {\n messages.push({ role: \"system\", content: systemContent });\n }\n\n if (options?.history) {\n messages.push(...options.history);\n }\n\n const userContent = await buildUserMessageContent({\n input,\n attachments: options?.attachments,\n attachmentPolicy: options?.attachmentPolicy ?? config.attachmentPolicy,\n modelName: config.model.name,\n modelSupportsVision: Boolean(config.model.capabilities?.vision),\n modelSupportsPdf: Boolean(config.model.capabilities?.pdf),\n modelSupportsAudio: Boolean(config.model.capabilities?.audio),\n });\n\n messages.push({ role: \"user\", content: userContent });\n\n return {\n messages,\n responseSchema,\n systemPrompt: systemContent || undefined,\n promptName,\n promptVersion,\n };\n}\n\n/**\n * Build the user message `content` field. Plain string when no\n * attachments (the hot path) — keeps wire payloads small. Multipart\n * `ContentPart[]` when attachments exist: input text first, resolved\n * parts in declaration order.\n */\nasync function buildUserMessageContent(params: {\n input: string;\n attachments?: Attachment[];\n attachmentPolicy?: AttachmentPolicy;\n modelName: string;\n modelSupportsVision: boolean;\n modelSupportsPdf: boolean;\n modelSupportsAudio: boolean;\n}): Promise<string | ContentPart[]> {\n const {\n input,\n attachments,\n attachmentPolicy,\n modelName,\n modelSupportsVision,\n modelSupportsPdf,\n modelSupportsAudio,\n } = params;\n\n if (!attachments || attachments.length === 0) {\n return input;\n }\n\n const parts: ContentPart[] = await Promise.all(\n attachments.map((attachment) => prepareAttachmentPart(attachment, attachmentPolicy)),\n );\n\n // Capability gate per modality (A2) — reject an attachment the model\n // can't consume here, with a clear message, rather than failing opaquely\n // at the provider.\n assertModality(parts, \"image\", modelSupportsVision, \"vision\", modelName);\n assertModality(parts, \"pdf\", modelSupportsPdf, \"pdf\", modelName);\n assertModality(parts, \"audio\", modelSupportsAudio, \"audio\", modelName);\n\n return [{ type: \"text\", text: input }, ...parts];\n}\n\n/** Throw when a modality is present but the model doesn't declare it. */\nfunction assertModality(\n parts: ContentPart[],\n partType: ContentPart[\"type\"],\n supported: boolean,\n capability: string,\n modelName: string,\n): void {\n if (!supported && parts.some((part) => part.type === partType)) {\n throw new InvalidRequestError(\n `Model \"${modelName}\" does not declare ${capability} capability — ${partType} attachments are not supported`,\n { context: { modelName } },\n );\n }\n}\n\n/**\n * When the caller supplied an `output` schema, resolve two artifacts:\n *\n * - `responseSchema` — extracted JSON Schema to attach on every trip.\n * Adapters that natively support structured output (OpenAI's\n * `response_format: json_schema`) consume it; others ignore it.\n * - `instruction` — a soft fallback appended to the system prompt\n * **only** for models without native structured-output capability.\n * Capable adapters skip it to save tokens and avoid redundancy.\n */\nfunction resolveStructuredOutput(params: {\n outputSchema?: StandardSchemaV1<unknown>;\n overrideResponseSchema?: Record<string, unknown>;\n modelSupportsStructuredOutput: boolean;\n}): {\n responseSchema?: Record<string, unknown>;\n instruction?: string;\n} {\n const { outputSchema, overrideResponseSchema, modelSupportsStructuredOutput } = params;\n\n if (!outputSchema) {\n return {};\n }\n\n const responseSchema = overrideResponseSchema ?? extractJsonSchema(outputSchema);\n\n if (modelSupportsStructuredOutput) {\n return { responseSchema };\n }\n\n const schemaHint = responseSchema\n ? `\\n\\nThe response MUST match this JSON Schema:\\n${JSON.stringify(responseSchema, null, 2)}`\n : \"\";\n\n const instruction = [\n \"You MUST respond with a single valid JSON value only.\",\n \"Do not wrap it in markdown code fences. Do not include prose, commentary, or explanation — JSON only.\",\n schemaHint,\n ]\n .join(\"\")\n .trim();\n\n return { responseSchema, instruction };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,eAAsB,wBAAiC,QAIpB;CACjC,MAAM,EAAE,QAAQ,OAAO,YAAY;CAEnC,MAAM,eAA6B;EACjC,GAAG,OAAO;EACV,GAAG,SAAS;CACd;CAEA,MAAM,eAAe,SAAS,gBAAgB,OAAO;CACrD,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI;CAEJ,IAAI,OAAO,iBAAiB,UAC1B,gBAAgB;MACX,IAAI,cAAc;EACvB,gBAAgB,aAAa,QAAQ,YAAY;EAOjD,MAAM,OAAO,aAAa,KAAK;EAE/B,IAAI,MAAM,MAAM;GACd,aAAa,KAAK;GAClB,gBAAgB,KAAK,WAAW;EAClC;CACF;CAEA,MAAM,EAAE,gBAAgB,gBAAgB,wBAAwB;EAC9D,cAAc,SAAS,UAAU,OAAO;EACxC,wBAAwB,SAAS;EACjC,+BAA+B,QAAQ,OAAO,MAAM,cAAc,gBAAgB;CACpF,CAAC;CAED,IAAI,aACF,gBAAgB,gBAAgB,GAAG,cAAc,MAAM,gBAAgB;CAGzE,MAAM,WAAsB,CAAC;CAE7B,IAAI,eACF,SAAS,KAAK;EAAE,MAAM;EAAU,SAAS;CAAc,CAAC;CAG1D,IAAI,SAAS,SACX,SAAS,KAAK,GAAG,QAAQ,OAAO;CAGlC,MAAM,cAAc,MAAM,wBAAwB;EAChD;EACA,aAAa,SAAS;EACtB,kBAAkB,SAAS,oBAAoB,OAAO;EACtD,WAAW,OAAO,MAAM;EACxB,qBAAqB,QAAQ,OAAO,MAAM,cAAc,MAAM;EAC9D,kBAAkB,QAAQ,OAAO,MAAM,cAAc,GAAG;EACxD,oBAAoB,QAAQ,OAAO,MAAM,cAAc,KAAK;CAC9D,CAAC;CAED,SAAS,KAAK;EAAE,MAAM;EAAQ,SAAS;CAAY,CAAC;CAEpD,OAAO;EACL;EACA;EACA,cAAc,iBAAiB;EAC/B;EACA;CACF;AACF;;;;;;;AAQA,eAAe,wBAAwB,QAQH;CAClC,MAAM,EACJ,OACA,aACA,kBACA,WACA,qBACA,kBACA,uBACE;CAEJ,IAAI,CAAC,eAAe,YAAY,WAAW,GACzC,OAAO;CAGT,MAAM,QAAuB,MAAM,QAAQ,IACzC,YAAY,KAAK,eAAe,sBAAsB,YAAY,gBAAgB,CAAC,CACrF;CAKA,eAAe,OAAO,SAAS,qBAAqB,UAAU,SAAS;CACvE,eAAe,OAAO,OAAO,kBAAkB,OAAO,SAAS;CAC/D,eAAe,OAAO,SAAS,oBAAoB,SAAS,SAAS;CAErE,OAAO,CAAC;EAAE,MAAM;EAAQ,MAAM;CAAM,GAAG,GAAG,KAAK;AACjD;;AAGA,SAAS,eACP,OACA,UACA,WACA,YACA,WACM;CACN,IAAI,CAAC,aAAa,MAAM,MAAM,SAAS,KAAK,SAAS,QAAQ,GAC3D,MAAM,IAAI,oBACR,UAAU,UAAU,qBAAqB,WAAW,gBAAgB,SAAS,iCAC7E,EAAE,SAAS,EAAE,UAAU,EAAE,CAC3B;AAEJ;;;;;;;;;;;AAYA,SAAS,wBAAwB,QAO/B;CACA,MAAM,EAAE,cAAc,wBAAwB,kCAAkC;CAEhF,IAAI,CAAC,cACH,OAAO,CAAC;CAGV,MAAM,iBAAiB,0BAA0B,kBAAkB,YAAY;CAE/E,IAAI,+BACF,OAAO,EAAE,eAAe;CAe1B,OAAO;EAAE;EAAgB,aARL;GAClB;GACA;GANiB,iBACf,kDAAkD,KAAK,UAAU,gBAAgB,MAAM,CAAC,MACxF;EAMJ,CAAC,CACE,KAAK,EAAE,CAAC,CACR,KAEgC;CAAE;AACvC"}
1
+ {"version":3,"file":"agent-input-builder.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/agent/agent-input-builder.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { AgentExecuteOptions } from \"../contracts/agent/agent-options.type\";\nimport type { AttachmentPolicy } from \"../contracts/attachment-policy.type\";\nimport type { Attachment } from \"../contracts/attachment.type\";\nimport type { ContentPart } from \"../contracts/content-part.type\";\nimport type { Message } from \"../contracts/conversation-message.type\";\nimport type { Placeholders } from \"../contracts/placeholders.type\";\nimport { InvalidRequestError } from \"../errors\";\nimport { extractJsonSchema, prepareAttachmentPart } from \"../utils\";\nimport type { AgentConfig } from \"./agent-config.type\";\n\n/**\n * Outcome of `buildAgentInputMessages` — the seeded message list and\n * the JSON Schema cached for every trip's\n * `ModelCallOptions.responseSchema`. `responseSchema` is `undefined`\n * when the caller didn't ask for structured output.\n */\nexport type AgentInputBuildResult = {\n messages: Message[];\n responseSchema?: Record<string, unknown>;\n /**\n * The resolved system-prompt text actually sent as the `role: \"system\"`\n * message (persona + instructions + any auto-appended structured-output\n * instruction). Captured for observability; absent when the agent ran\n * without a system prompt.\n */\n systemPrompt?: string;\n /**\n * Registry name of the `SystemPromptContract` the agent resolved, read from\n * its `meta().name`. Present only when the agent ran against a *named*\n * prompt (one registered in `ai.prompts`); absent for a raw-string prompt,\n * an anonymous contract, or no prompt at all. Lets observers attribute a run\n * to a specific prompt in the registry.\n */\n promptName?: string;\n /**\n * Registry version label of the named prompt the agent resolved, read from\n * its `meta().version` (defaulting to `\"1\"` when the prompt carries a name\n * but no explicit version, mirroring the registry's default). Present only\n * alongside {@link AgentInputBuildResult.promptName}.\n */\n promptVersion?: string;\n};\n\n/**\n * Assemble the seed conversation for an agent execution. Runs exactly\n * once per run — subsequent trips append to the same message list.\n *\n * Responsibilities (previously three methods on `Execution`):\n * 1. Merge factory + per-call placeholders.\n * 2. Resolve the system prompt (string, contract, or absent).\n * 3. When an output schema is supplied:\n * - cache its JSON Schema form for `ModelCallOptions.responseSchema`\n * so native-structured-output providers enforce it at the token\n * level;\n * - fall back to a soft system-prompt instruction for providers\n * that don't advertise `structuredOutput` capability.\n * 4. Append caller-supplied `history` (e.g. session-level prior turns).\n * 5. Shape the user message — plain string in the common case,\n * multipart `ContentPart[]` when `attachments` are present. Image\n * attachments require model vision capability; mismatch throws\n * `InvalidRequestError` here rather than failing opaquely at the\n * provider.\n *\n * Extracted from the `Execution` class to isolate the declarative\n * input-shaping phase from the stateful trip loop.\n */\nexport async function buildAgentInputMessages<TOutput>(params: {\n config: AgentConfig<TOutput>;\n input: string;\n options?: AgentExecuteOptions<TOutput>;\n}): Promise<AgentInputBuildResult> {\n const { config, input, options } = params;\n\n const placeholders: Placeholders = {\n ...config.placeholders,\n ...options?.placeholders,\n };\n\n const systemPrompt = options?.systemPrompt ?? config.systemPrompt;\n let systemContent = \"\";\n let promptName: string | undefined;\n let promptVersion: string | undefined;\n\n if (typeof systemPrompt === \"string\") {\n systemContent = systemPrompt;\n } else if (systemPrompt) {\n // A lazily-compiled prompt (`systemPrompt.refined(...)`) finishes its\n // async work here, before the synchronous `resolve()` below — a no-op for\n // plain builders, and never throws (a failed refinement falls back to the\n // original text).\n if (typeof systemPrompt.materialize === \"function\") {\n await systemPrompt.materialize();\n }\n\n systemContent = systemPrompt.resolve(placeholders);\n\n // Capture prompt-version linkage from the contract's metadata: a *named*\n // prompt (one addressable in `ai.prompts`) stamps `promptName@version`\n // onto the run's report so observers can group runs by the exact prompt\n // version that produced them. Anonymous prompts carry no name and are\n // left unlinked.\n const meta = systemPrompt.meta();\n\n if (meta?.name) {\n promptName = meta.name;\n promptVersion = meta.version ?? \"1\";\n }\n }\n\n const { responseSchema, instruction } = resolveStructuredOutput({\n outputSchema: options?.output ?? config.output,\n overrideResponseSchema: options?.responseSchema,\n modelSupportsStructuredOutput: Boolean(config.model.capabilities?.structuredOutput),\n });\n\n if (instruction) {\n systemContent = systemContent ? `${systemContent}\\n\\n${instruction}` : instruction;\n }\n\n const messages: Message[] = [];\n\n if (systemContent) {\n messages.push({ role: \"system\", content: systemContent });\n }\n\n if (options?.history) {\n messages.push(...options.history);\n }\n\n const userContent = await buildUserMessageContent({\n input,\n attachments: options?.attachments,\n attachmentPolicy: options?.attachmentPolicy ?? config.attachmentPolicy,\n modelName: config.model.name,\n modelSupportsVision: Boolean(config.model.capabilities?.vision),\n modelSupportsPdf: Boolean(config.model.capabilities?.pdf),\n modelSupportsAudio: Boolean(config.model.capabilities?.audio),\n });\n\n messages.push({ role: \"user\", content: userContent });\n\n return {\n messages,\n responseSchema,\n systemPrompt: systemContent || undefined,\n promptName,\n promptVersion,\n };\n}\n\n/**\n * Build the user message `content` field. Plain string when no\n * attachments (the hot path) — keeps wire payloads small. Multipart\n * `ContentPart[]` when attachments exist: input text first, resolved\n * parts in declaration order.\n */\nasync function buildUserMessageContent(params: {\n input: string;\n attachments?: Attachment[];\n attachmentPolicy?: AttachmentPolicy;\n modelName: string;\n modelSupportsVision: boolean;\n modelSupportsPdf: boolean;\n modelSupportsAudio: boolean;\n}): Promise<string | ContentPart[]> {\n const {\n input,\n attachments,\n attachmentPolicy,\n modelName,\n modelSupportsVision,\n modelSupportsPdf,\n modelSupportsAudio,\n } = params;\n\n if (!attachments || attachments.length === 0) {\n return input;\n }\n\n const parts: ContentPart[] = await Promise.all(\n attachments.map((attachment) => prepareAttachmentPart(attachment, attachmentPolicy)),\n );\n\n // Capability gate per modality (A2) — reject an attachment the model\n // can't consume here, with a clear message, rather than failing opaquely\n // at the provider.\n assertModality(parts, \"image\", modelSupportsVision, \"vision\", modelName);\n assertModality(parts, \"pdf\", modelSupportsPdf, \"pdf\", modelName);\n assertModality(parts, \"audio\", modelSupportsAudio, \"audio\", modelName);\n\n return [{ type: \"text\", text: input }, ...parts];\n}\n\n/** Throw when a modality is present but the model doesn't declare it. */\nfunction assertModality(\n parts: ContentPart[],\n partType: ContentPart[\"type\"],\n supported: boolean,\n capability: string,\n modelName: string,\n): void {\n if (!supported && parts.some((part) => part.type === partType)) {\n throw new InvalidRequestError(\n `Model \"${modelName}\" does not declare ${capability} capability — ${partType} attachments are not supported`,\n { context: { modelName } },\n );\n }\n}\n\n/**\n * When the caller supplied an `output` schema, resolve two artifacts:\n *\n * - `responseSchema` — extracted JSON Schema to attach on every trip.\n * Adapters that natively support structured output (OpenAI's\n * `response_format: json_schema`) consume it; others ignore it.\n * - `instruction` — a soft fallback appended to the system prompt\n * **only** for models without native structured-output capability.\n * Capable adapters skip it to save tokens and avoid redundancy.\n */\nfunction resolveStructuredOutput(params: {\n outputSchema?: StandardSchemaV1<unknown>;\n overrideResponseSchema?: Record<string, unknown>;\n modelSupportsStructuredOutput: boolean;\n}): {\n responseSchema?: Record<string, unknown>;\n instruction?: string;\n} {\n const { outputSchema, overrideResponseSchema, modelSupportsStructuredOutput } = params;\n\n if (!outputSchema) {\n return {};\n }\n\n const responseSchema = overrideResponseSchema ?? extractJsonSchema(outputSchema);\n\n if (modelSupportsStructuredOutput) {\n return { responseSchema };\n }\n\n const schemaHint = responseSchema\n ? `\\n\\nThe response MUST match this JSON Schema:\\n${JSON.stringify(responseSchema, null, 2)}`\n : \"\";\n\n const instruction = [\n \"You MUST respond with a single valid JSON value only.\",\n \"Do not wrap it in markdown code fences. Do not include prose, commentary, or explanation — JSON only.\",\n schemaHint,\n ]\n .join(\"\")\n .trim();\n\n return { responseSchema, instruction };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,eAAsB,wBAAiC,QAIpB;CACjC,MAAM,EAAE,QAAQ,OAAO,YAAY;CAEnC,MAAM,eAA6B;EACjC,GAAG,OAAO;EACV,GAAG,SAAS;CACd;CAEA,MAAM,eAAe,SAAS,gBAAgB,OAAO;CACrD,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI;CAEJ,IAAI,OAAO,iBAAiB,UAC1B,gBAAgB;MACX,IAAI,cAAc;EAKvB,IAAI,OAAO,aAAa,gBAAgB,YACtC,MAAM,aAAa,YAAY;EAGjC,gBAAgB,aAAa,QAAQ,YAAY;EAOjD,MAAM,OAAO,aAAa,KAAK;EAE/B,IAAI,MAAM,MAAM;GACd,aAAa,KAAK;GAClB,gBAAgB,KAAK,WAAW;EAClC;CACF;CAEA,MAAM,EAAE,gBAAgB,gBAAgB,wBAAwB;EAC9D,cAAc,SAAS,UAAU,OAAO;EACxC,wBAAwB,SAAS;EACjC,+BAA+B,QAAQ,OAAO,MAAM,cAAc,gBAAgB;CACpF,CAAC;CAED,IAAI,aACF,gBAAgB,gBAAgB,GAAG,cAAc,MAAM,gBAAgB;CAGzE,MAAM,WAAsB,CAAC;CAE7B,IAAI,eACF,SAAS,KAAK;EAAE,MAAM;EAAU,SAAS;CAAc,CAAC;CAG1D,IAAI,SAAS,SACX,SAAS,KAAK,GAAG,QAAQ,OAAO;CAGlC,MAAM,cAAc,MAAM,wBAAwB;EAChD;EACA,aAAa,SAAS;EACtB,kBAAkB,SAAS,oBAAoB,OAAO;EACtD,WAAW,OAAO,MAAM;EACxB,qBAAqB,QAAQ,OAAO,MAAM,cAAc,MAAM;EAC9D,kBAAkB,QAAQ,OAAO,MAAM,cAAc,GAAG;EACxD,oBAAoB,QAAQ,OAAO,MAAM,cAAc,KAAK;CAC9D,CAAC;CAED,SAAS,KAAK;EAAE,MAAM;EAAQ,SAAS;CAAY,CAAC;CAEpD,OAAO;EACL;EACA;EACA,cAAc,iBAAiB;EAC/B;EACA;CACF;AACF;;;;;;;AAQA,eAAe,wBAAwB,QAQH;CAClC,MAAM,EACJ,OACA,aACA,kBACA,WACA,qBACA,kBACA,uBACE;CAEJ,IAAI,CAAC,eAAe,YAAY,WAAW,GACzC,OAAO;CAGT,MAAM,QAAuB,MAAM,QAAQ,IACzC,YAAY,KAAK,eAAe,sBAAsB,YAAY,gBAAgB,CAAC,CACrF;CAKA,eAAe,OAAO,SAAS,qBAAqB,UAAU,SAAS;CACvE,eAAe,OAAO,OAAO,kBAAkB,OAAO,SAAS;CAC/D,eAAe,OAAO,SAAS,oBAAoB,SAAS,SAAS;CAErE,OAAO,CAAC;EAAE,MAAM;EAAQ,MAAM;CAAM,GAAG,GAAG,KAAK;AACjD;;AAGA,SAAS,eACP,OACA,UACA,WACA,YACA,WACM;CACN,IAAI,CAAC,aAAa,MAAM,MAAM,SAAS,KAAK,SAAS,QAAQ,GAC3D,MAAM,IAAI,oBACR,UAAU,UAAU,qBAAqB,WAAW,gBAAgB,SAAS,iCAC7E,EAAE,SAAS,EAAE,UAAU,EAAE,CAC3B;AAEJ;;;;;;;;;;;AAYA,SAAS,wBAAwB,QAO/B;CACA,MAAM,EAAE,cAAc,wBAAwB,kCAAkC;CAEhF,IAAI,CAAC,cACH,OAAO,CAAC;CAGV,MAAM,iBAAiB,0BAA0B,kBAAkB,YAAY;CAE/E,IAAI,+BACF,OAAO,EAAE,eAAe;CAe1B,OAAO;EAAE;EAAgB,aARL;GAClB;GACA;GANiB,iBACf,kDAAkD,KAAK,UAAU,gBAAgB,MAAM,CAAC,MACxF;EAMJ,CAAC,CACE,KAAK,EAAE,CAAC,CACR,KAEgC;CAAE;AACvC"}
@@ -22,6 +22,7 @@ import { PlannerFailedError } from "./planner-failed-error.mjs";
22
22
  import { PlannerCancelledError } from "./planner-cancelled-error.mjs";
23
23
  import { PlannerDriftError } from "./planner-drift-error.mjs";
24
24
  import { PlannerPlanInvalidError } from "./planner-plan-invalid-error.mjs";
25
+ import { PromptRefinementError } from "./prompt-refinement-error.mjs";
25
26
  import "./provider-auth-error.mjs";
26
27
  import "./provider-rate-limit-error.mjs";
27
28
  import "./provider-timeout-error.mjs";
@@ -0,0 +1,27 @@
1
+ import { AIError } from "./ai-error.mjs";
2
+
3
+ //#region ../@warlock.js/ai/src/errors/prompt-refinement-error.ts
4
+ /**
5
+ * An explicit `refine()` / `refinePrompt()` call could not produce an
6
+ * acceptable compiled prompt. Thrown (not degraded) because the explicit
7
+ * compilation surface is used by routes, warmup, and CI — callers there need
8
+ * the failure, not a silently-served original.
9
+ *
10
+ * The LAZY agent path never sees this error: `materialize()` catches it,
11
+ * warns once, and serves the original prompt text — refinement is advisory
12
+ * there, mirroring the Nova-safe judge policy in `ai.prompts.validate`.
13
+ */
14
+ var PromptRefinementError = class extends AIError {
15
+ static {
16
+ this.defaultCategory = "validation";
17
+ }
18
+ constructor(message, options) {
19
+ super("PROMPT_REFINEMENT_FAILED", message, options);
20
+ this.name = "PromptRefinementError";
21
+ this.reason = options.reason;
22
+ }
23
+ };
24
+
25
+ //#endregion
26
+ export { PromptRefinementError };
27
+ //# sourceMappingURL=prompt-refinement-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-refinement-error.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/errors/prompt-refinement-error.ts"],"sourcesContent":["import type { AIErrorOptions } from \"./ai-error\";\nimport { AIError } from \"./ai-error\";\nimport type { ErrorCategory } from \"./error-category.type\";\n\n/**\n * Why a prompt refinement was rejected:\n *\n * - `\"model\"` — the refiner model call itself failed (provider error, no\n * key, timeout); the underlying `AIError` rides on `cause`.\n * - `\"parity\"` — the rewrite broke placeholder parity (added, removed, or\n * renamed a `{{placeholder}}` / changed its `|default`) and one repair\n * attempt didn't fix it; the offending tokens are listed in `context.issues`.\n * - `\"empty\"` — the refiner returned no usable text.\n */\nexport type PromptRefinementFailureReason = \"model\" | \"parity\" | \"empty\";\n\nexport type PromptRefinementErrorOptions = AIErrorOptions & {\n reason: PromptRefinementFailureReason;\n};\n\n/**\n * An explicit `refine()` / `refinePrompt()` call could not produce an\n * acceptable compiled prompt. Thrown (not degraded) because the explicit\n * compilation surface is used by routes, warmup, and CI — callers there need\n * the failure, not a silently-served original.\n *\n * The LAZY agent path never sees this error: `materialize()` catches it,\n * warns once, and serves the original prompt text — refinement is advisory\n * there, mirroring the Nova-safe judge policy in `ai.prompts.validate`.\n */\nexport class PromptRefinementError extends AIError {\n public static readonly defaultCategory: ErrorCategory = \"validation\";\n\n public readonly reason: PromptRefinementFailureReason;\n\n public constructor(message: string, options: PromptRefinementErrorOptions) {\n super(\"PROMPT_REFINEMENT_FAILED\", message, options);\n this.name = \"PromptRefinementError\";\n this.reason = options.reason;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,IAAa,wBAAb,cAA2C,QAAQ;;yBACO;;CAIxD,AAAO,YAAY,SAAiB,SAAuC;EACzE,MAAM,4BAA4B,SAAS,OAAO;EAClD,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ;CACxB;AACF"}
@@ -19,6 +19,7 @@ import { PlannerFailedError } from "./errors/planner-failed-error.mjs";
19
19
  import { PlannerCancelledError } from "./errors/planner-cancelled-error.mjs";
20
20
  import { PlannerDriftError } from "./errors/planner-drift-error.mjs";
21
21
  import { PlannerPlanInvalidError } from "./errors/planner-plan-invalid-error.mjs";
22
+ import { PromptRefinementError } from "./errors/prompt-refinement-error.mjs";
22
23
  import { RoutingError } from "./errors/routing-error.mjs";
23
24
  import { SchemaValidationError } from "./errors/schema-validation-error.mjs";
24
25
  import { StepFailedError } from "./errors/step-failed-error.mjs";
@@ -146,6 +147,7 @@ import "./planner/index.mjs";
146
147
  import { renderPlaceholders } from "./system-prompt/render-placeholders.mjs";
147
148
  import { Instruction, instruction } from "./system-prompt/instruction.mjs";
148
149
  import { Persona, persona } from "./system-prompt/persona.mjs";
150
+ import { RefinedSystemPrompt } from "./system-prompt/refined-system-prompt.mjs";
149
151
  import { SystemPrompt, systemPrompt } from "./system-prompt/system-prompt.mjs";
150
152
  import { defaultPromptsManager, promptKey, prompts } from "./prompts/prompts-manager.mjs";
151
153
  import { chunk } from "./rag/chunk/chunk.mjs";
@@ -151,7 +151,7 @@ var PromptsManager = class {
151
151
  };
152
152
  }
153
153
  const cache = options.judgeCache ?? this.judgeCache;
154
- const judgeOutcome = await judgePromptBodyCached(text, options.judge, cache);
154
+ const judgeOutcome = await judgePromptBodyCached(text, options.judge, cache, options.criteria);
155
155
  const issues = [...unreferenced.map((key) => `Required key "${key}" is never referenced in the prompt.`), ...judgeOutcome.issues];
156
156
  return {
157
157
  ok,
@@ -1 +1 @@
1
- {"version":3,"file":"prompts-manager.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/prompts/prompts-manager.ts"],"sourcesContent":["import type { Placeholders } from \"../contracts/placeholders.type\";\nimport type {\n SystemPromptBlockContract,\n SystemPromptContract,\n SystemPromptMeta,\n} from \"../contracts/system-prompt.contract\";\nimport { InvalidRequestError } from \"../errors\";\nimport { Instruction } from \"../system-prompt/instruction\";\nimport { Persona } from \"../system-prompt/persona\";\nimport { SystemPrompt } from \"../system-prompt/system-prompt\";\nimport type {\n PromptsManagerContract,\n PromptsManagerEntry,\n PromptsManagerRegisterOptions,\n} from \"./prompts-manager.contract\";\nimport type {\n ExportedPromptVersion,\n ExportedRegistry,\n PromptDiff,\n PromptDiffBlock,\n PromptJudgeCacheLike,\n PromptsManagerOptions,\n PromptTemplateVersion,\n PromptValidateTarget,\n PromptValidationResult,\n PromptsValidateOptions,\n} from \"./prompts-manager.type\";\nimport {\n describeContractTarget,\n findMissingPlaceholders,\n findUnreferencedRequired,\n judgePromptBodyCached,\n} from \"./prompts-validate\";\n\n/**\n * Build the `name@version` registry key. Centralized so the duplicate check,\n * `get`, and `composedFrom` provenance all agree on one label shape.\n */\nexport function promptKey(name: string, version: string): string {\n return `${name}@${version}`;\n}\n\n/**\n * Serialize a prompt's observable content — its ordered blocks (discriminator\n * + raw template text) — into a stable signature. Two prompts with the same\n * blocks in the same order share a signature, which is how `register()` tells\n * an idempotent re-registration from a genuine clash. Meta is intentionally\n * excluded: provenance / description should not defeat idempotency.\n */\nfunction contentSignature(contract: SystemPromptContract): string {\n return JSON.stringify(\n contract.blocks.map(block => [block.type, block.text]),\n );\n}\n\n/**\n * Reconstruct a block from its `{ type, text }` snapshot — `persona` blocks\n * become a `Persona`, everything else an `Instruction`. The inverse of the\n * flattening `export()` performs, so an imported registry resolves identically.\n */\nfunction blockFromSnapshot(block: PromptDiffBlock): SystemPromptBlockContract {\n return block.type === \"persona\"\n ? new Persona(block.text)\n : new Instruction(block.text);\n}\n\n/**\n * Narrow a {@link PromptTemplateVersion} body to its ordered block list: a raw\n * string becomes one instruction block; an explicit block list is used verbatim.\n */\nfunction blocksFromTemplate(\n template: string | readonly SystemPromptBlockContract[],\n): SystemPromptBlockContract[] {\n if (typeof template === \"string\") {\n return [new Instruction(template)];\n }\n\n return [...template];\n}\n\n/**\n * Concrete `PromptsManagerContract` — a single registry of named, versioned\n * `SystemPromptContract` builders keyed by `name@version`.\n *\n * **Role.** The store behind `ai.prompts`. It holds one flat\n * `Map<string, PromptsManagerEntry>` keyed by `name@version`, plus a monotonic\n * counter that stamps each entry's `addedAt` so \"latest\" is deterministic\n * (highest `addedAt` for a name) without ever reading the wall clock.\n *\n * **Responsibility.**\n * - Owns: the registry map, the `addedAt` counter, the duplicate /\n * idempotency rule, default version derivation, latest selection, the\n * per-version tag pins, and the validate / diff / export / import surface.\n * - Does NOT own: prompt rendering (delegated to the contract's `resolve()`),\n * block composition, or the LLM-judge mechanics (delegated to the eval\n * `judge` scorer via `prompts-validate`).\n *\n * Users construct via the `prompts()` factory — `new PromptsManager()` is not\n * the public API.\n */\nclass PromptsManager implements PromptsManagerContract {\n /** Flat registry keyed by `name@version`. */\n private readonly entries = new Map<string, PromptsManagerEntry>();\n\n /** First-seen order of names, for a stable `list()`. */\n private readonly names: string[] = [];\n\n /** Per-name tag pins: `name` → (`tag` → `version`). */\n private readonly pins = new Map<string, Map<string, string>>();\n\n /** Optional process-level judge-verdict memo (absent ⇒ judge always runs live). */\n private readonly judgeCache?: PromptJudgeCacheLike;\n\n /** Monotonic insertion counter — the deterministic stand-in for a timestamp. */\n private counter = 0;\n\n public constructor(options: PromptsManagerOptions = {}) {\n this.judgeCache = options.judgeCache;\n }\n\n public register(\n contract: SystemPromptContract,\n options: PromptsManagerRegisterOptions = {},\n ): PromptsManagerContract {\n const meta = contract.meta();\n // An explicit override (from define() / import()) wins over the contract's\n // own meta — it lets those bulk paths register an anonymous contract under\n // a name without the SystemPrompt constructor's default-manager auto-reg.\n const name = options.name ?? meta?.name;\n\n if (!name) {\n throw new InvalidRequestError(\n \"Cannot register a prompt without a name — set meta.name via \" +\n \"systemPrompt(input, { name }) or .meta({ name }).\",\n { context: { meta } },\n );\n }\n\n const version =\n options.version ?? meta?.version ?? this.nextVersion(name);\n const key = promptKey(name, version);\n const existing = this.entries.get(key);\n\n if (existing) {\n // Idempotent re-registration: identical content under the same\n // name@version is a no-op, not an error. Anything else is a clash.\n if (contentSignature(existing.contract) === contentSignature(contract)) {\n return this;\n }\n\n throw new InvalidRequestError(\n `A different prompt is already registered as \"${key}\".`,\n { context: { name, version } },\n );\n }\n\n if (!this.names.includes(name)) {\n this.names.push(name);\n }\n\n this.entries.set(key, {\n name,\n version,\n addedAt: this.counter++,\n contract,\n ...(options.tags ? { tags: options.tags } : {}),\n });\n\n return this;\n }\n\n public create(\n input?: string | ReadonlyArray<SystemPromptBlockContract>,\n meta?: SystemPromptMeta,\n ): SystemPromptContract {\n // Mirror `systemPromptFactory` exactly (no import — `system-prompt.ts`\n // already depends on this module, so importing its factory back here would\n // close an import cycle). A name in `meta` auto-registers into the\n // process-wide default manager via the SystemPrompt constructor.\n if (input === undefined) {\n return new SystemPrompt([], meta);\n }\n\n if (typeof input === \"string\") {\n return new SystemPrompt([new Instruction(input)], meta);\n }\n\n return new SystemPrompt([...input], meta);\n }\n\n public get(name: string, versionOrTag?: string): SystemPromptContract {\n return this.requireEntry(name, versionOrTag).contract;\n }\n\n public has(name: string, versionOrTag?: string): boolean {\n const { baseName, selector } = this.parseSelector(name, versionOrTag);\n\n if (selector !== undefined) {\n return this.resolveSelector(baseName, selector) !== undefined;\n }\n\n return this.latestEntry(baseName) !== undefined;\n }\n\n public list(): string[] {\n return [...this.names];\n }\n\n public versions(name: string): string[] {\n return [...this.entries.values()]\n .filter(entry => entry.name === name)\n .sort((a, b) => a.addedAt - b.addedAt)\n .map(entry => entry.version);\n }\n\n public resolve(\n name: string,\n versionOrTag?: string,\n placeholders?: Placeholders,\n ): string {\n return this.requireEntry(name, versionOrTag).contract.resolve(placeholders);\n }\n\n public define(\n name: string,\n versions: readonly PromptTemplateVersion[],\n ): PromptsManagerContract {\n for (const entry of versions) {\n const blocks = blocksFromTemplate(entry.template);\n // Anonymous contract (no name in meta ⇒ no SystemPrompt constructor\n // auto-registration into the default manager); the name/version are\n // supplied explicitly so define() targets only THIS manager.\n const contract = new SystemPrompt(blocks);\n\n this.register(contract, { name, version: entry.version });\n }\n\n return this;\n }\n\n public tag(\n name: string,\n tag: string,\n version: string,\n ): PromptsManagerContract {\n // Validate the target exists before pinning — a tag to a missing version is\n // an authoring mistake, not a silent dangling pin.\n if (!this.entries.has(promptKey(name, version))) {\n throw new InvalidRequestError(\n `Cannot tag \"${tag}\" — no prompt registered as \"${promptKey(\n name,\n version,\n )}\".`,\n { context: { name, tag, version } },\n );\n }\n\n const nameTags = this.pins.get(name) ?? new Map<string, string>();\n nameTags.set(tag, version);\n this.pins.set(name, nameTags);\n\n return this;\n }\n\n public async validate(\n target: PromptValidateTarget,\n options: PromptsValidateOptions = {},\n ): Promise<PromptValidationResult> {\n const { text, required } = this.describeTarget(target);\n\n const provided = new Set(Object.keys(options.placeholders ?? {}));\n const declared = new Set<string>([\n ...required,\n ...(options.declare ?? []),\n ]);\n\n const missing = findMissingPlaceholders(text, provided, declared);\n\n // A declared-required key that the body never references is itself a\n // defect — surface it as an issue (it does not affect `missing` / `ok`,\n // which track unresolved placeholders).\n const unreferenced = findUnreferencedRequired(text, required);\n\n const ok = missing.length === 0;\n\n if (!options.judge) {\n if (unreferenced.length === 0) {\n return { ok, missing };\n }\n\n return {\n ok,\n missing,\n issues: unreferenced.map(\n key => `Required key \"${key}\" is never referenced in the prompt.`,\n ),\n };\n }\n\n // Per-call cache override wins over the manager-level memo.\n const cache = options.judgeCache ?? this.judgeCache;\n const judgeOutcome = await judgePromptBodyCached(text, options.judge, cache);\n\n const issues = [\n ...unreferenced.map(\n key => `Required key \"${key}\" is never referenced in the prompt.`,\n ),\n ...judgeOutcome.issues,\n ];\n\n return {\n ok,\n missing,\n ...(judgeOutcome.score !== undefined ? { score: judgeOutcome.score } : {}),\n issues,\n };\n }\n\n public diff(name: string, from: string, to: string): PromptDiff {\n const fromBlocks = this.snapshotBlocks(this.requireExact(name, from));\n const toBlocks = this.snapshotBlocks(this.requireExact(name, to));\n\n const added: PromptDiffBlock[] = [];\n const removed: PromptDiffBlock[] = [];\n const changed: { from: PromptDiffBlock; to: PromptDiffBlock }[] = [];\n\n const max = Math.max(fromBlocks.length, toBlocks.length);\n\n for (let index = 0; index < max; index++) {\n const left = fromBlocks[index];\n const right = toBlocks[index];\n\n if (left && !right) {\n removed.push(left);\n continue;\n }\n\n if (!left && right) {\n added.push(right);\n continue;\n }\n\n if (left && right && (left.type !== right.type || left.text !== right.text)) {\n changed.push({ from: left, to: right });\n }\n }\n\n return {\n name,\n from,\n to,\n added,\n removed,\n changed,\n identical:\n added.length === 0 && removed.length === 0 && changed.length === 0,\n };\n }\n\n public export(): ExportedRegistry {\n return {\n prompts: this.names.map(name => ({\n name,\n versions: this.versions(name).map(version =>\n this.exportVersion(name, version),\n ),\n })),\n };\n }\n\n public import(snapshot: ExportedRegistry): PromptsManagerContract {\n for (const exported of snapshot.prompts) {\n for (const version of exported.versions) {\n const blocks = version.blocks.map(blockFromSnapshot);\n // Anonymous (no `name` in meta) so the SystemPrompt constructor does\n // not auto-register into the default manager; description / required\n // ride along for round-trip fidelity. Name/version are explicit so the\n // import lands only on THIS manager.\n const contract = new SystemPrompt(blocks, {\n ...(version.description ? { description: version.description } : {}),\n ...(version.required ? { required: version.required } : {}),\n });\n\n this.register(contract, {\n name: exported.name,\n version: version.version,\n });\n\n for (const tag of version.tags ?? []) {\n this.tag(exported.name, tag, version.version);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Flatten a registered version into its portable `{ version, blocks, tags?,\n * description?, required? }` snapshot for `export()`.\n */\n private exportVersion(name: string, version: string): ExportedPromptVersion {\n const entry = this.requireExact(name, version);\n const meta = entry.contract.meta();\n const tags = this.tagsForVersion(name, version);\n\n return {\n version,\n blocks: this.snapshotBlocks(entry),\n ...(tags.length > 0 ? { tags } : {}),\n ...(meta?.description ? { description: meta.description } : {}),\n ...(meta?.required ? { required: [...meta.required] } : {}),\n };\n }\n\n /** Every tag currently pinned to a specific `name@version`, in pin order. */\n private tagsForVersion(name: string, version: string): string[] {\n const nameTags = this.pins.get(name);\n\n if (!nameTags) {\n return [];\n }\n\n const tags: string[] = [];\n\n for (const [tag, pinnedVersion] of nameTags) {\n if (pinnedVersion === version) {\n tags.push(tag);\n }\n }\n\n return tags;\n }\n\n /** Flatten an entry's blocks to `{ type, text }` snapshots. */\n private snapshotBlocks(entry: PromptsManagerEntry): PromptDiffBlock[] {\n return entry.contract.blocks.map(block => ({\n type: block.type,\n text: block.text,\n }));\n }\n\n /**\n * Resolve the body + declared-required keys for any `validate` target: a\n * registered name (or `name@selector`), a `SystemPromptContract` instance, or\n * a raw string.\n */\n private describeTarget(target: PromptValidateTarget): {\n text: string;\n required: readonly string[];\n } {\n if (typeof target === \"string\") {\n // An inline `name@selector` (or a bare registered name) resolves through\n // the registry; anything else is a raw prompt body validated verbatim.\n const { baseName, selector } = this.parseSelector(target, undefined);\n const entry = selector\n ? this.resolveSelector(baseName, selector)\n : this.latestEntry(baseName);\n\n if (entry) {\n return describeContractTarget(entry.contract);\n }\n\n return { text: target, required: [] };\n }\n\n if (isSystemPromptContract(target)) {\n return describeContractTarget(target);\n }\n\n if (isBlock(target)) {\n return { text: target.text, required: [] };\n }\n\n throw new InvalidRequestError(\n \"validate() target must be a registered name, a SystemPromptContract, \" +\n \"a prompt block, or a raw string.\",\n { context: { target } },\n );\n }\n\n /**\n * The next integer version label for a name — `\"1\"` for the first, then the\n * count of existing versions plus one. String-typed to match the free-form\n * `version` label shape.\n */\n private nextVersion(name: string): string {\n const count = [...this.entries.values()].filter(\n entry => entry.name === name,\n ).length;\n\n return String(count + 1);\n }\n\n /** Pick the highest-`addedAt` entry for a name, or `undefined` when absent. */\n private latestEntry(name: string): PromptsManagerEntry | undefined {\n let latest: PromptsManagerEntry | undefined;\n\n for (const entry of this.entries.values()) {\n if (entry.name !== name) {\n continue;\n }\n\n if (!latest || entry.addedAt > latest.addedAt) {\n latest = entry;\n }\n }\n\n return latest;\n }\n\n /**\n * Split a name argument into its base name + optional selector. The selector\n * comes from the explicit second argument when present, else from an inline\n * `name@selector` in the first argument. A bare name yields no selector.\n */\n private parseSelector(\n name: string,\n versionOrTag: string | undefined,\n ): { baseName: string; selector: string | undefined } {\n if (versionOrTag !== undefined) {\n return { baseName: name, selector: versionOrTag };\n }\n\n const at = name.indexOf(\"@\");\n\n if (at > 0) {\n return { baseName: name.slice(0, at), selector: name.slice(at + 1) };\n }\n\n return { baseName: name, selector: undefined };\n }\n\n /**\n * Resolve a selector (a version label OR a pinned tag) to a concrete entry.\n * Version labels win over tags when both could match — the explicit label is\n * the more specific intent. Returns `undefined` when neither resolves.\n */\n private resolveSelector(\n name: string,\n selector: string,\n ): PromptsManagerEntry | undefined {\n const byVersion = this.entries.get(promptKey(name, selector));\n\n if (byVersion) {\n return byVersion;\n }\n\n const pinnedVersion = this.pins.get(name)?.get(selector);\n\n if (pinnedVersion !== undefined) {\n return this.entries.get(promptKey(name, pinnedVersion));\n }\n\n return undefined;\n }\n\n /**\n * Resolve an entry by name (+ optional version / tag / inline selector),\n * throwing {@link InvalidRequestError} when the name or the requested\n * selector is unknown. The single lookup path `get` / `resolve` share.\n */\n private requireEntry(\n name: string,\n versionOrTag?: string,\n ): PromptsManagerEntry {\n const { baseName, selector } = this.parseSelector(name, versionOrTag);\n\n if (selector !== undefined) {\n const entry = this.resolveSelector(baseName, selector);\n\n if (!entry) {\n throw new InvalidRequestError(\n `No prompt registered as \"${baseName}\" with version/tag \"${selector}\".`,\n { context: { name: baseName, selector } },\n );\n }\n\n return entry;\n }\n\n const latest = this.latestEntry(baseName);\n\n if (!latest) {\n throw new InvalidRequestError(\n `No prompt registered under name \"${baseName}\".`,\n { context: { name: baseName } },\n );\n }\n\n return latest;\n }\n\n /**\n * Resolve a name + EXACT version label to its entry (no tag fallback), for\n * `diff` / `export` where a concrete version is always required. Throws\n * {@link InvalidRequestError} on a miss.\n */\n private requireExact(name: string, version: string): PromptsManagerEntry {\n const entry = this.entries.get(promptKey(name, version));\n\n if (!entry) {\n throw new InvalidRequestError(\n `No prompt registered as \"${promptKey(name, version)}\".`,\n { context: { name, version } },\n );\n }\n\n return entry;\n }\n}\n\n/**\n * Narrow an arbitrary value to a `SystemPromptContract` — true when it exposes\n * the builder surface (`blocks` array + a callable `resolve`) AND a callable\n * `meta`. Robust across duplicate package copies (no `instanceof`).\n */\nfunction isSystemPromptContract(\n value: unknown,\n): value is SystemPromptContract {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray((value as { blocks?: unknown }).blocks) &&\n typeof (value as { resolve?: unknown }).resolve === \"function\" &&\n typeof (value as { meta?: unknown }).meta === \"function\"\n );\n}\n\n/**\n * Narrow an arbitrary value to a single `SystemPromptBlockContract` — true when\n * it carries a string `type` + `text` and a callable `resolve` but is NOT a\n * full prompt (no `blocks` array). Lets `validate` accept a lone block.\n */\nfunction isBlock(value: unknown): value is SystemPromptBlockContract {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\" &&\n typeof (value as { text?: unknown }).text === \"string\" &&\n typeof (value as { resolve?: unknown }).resolve === \"function\"\n );\n}\n\n/**\n * Create a new, isolated prompts manager.\n *\n * **Role.** Public factory for {@link PromptsManagerContract} — keeps\n * user-facing code free of `new` and consistent with the other `ai.*`\n * factories. Each call returns a fresh registry, so parallel test suites and\n * multi-tenant apps never share mutable global prompt state.\n *\n * The process-wide instance that named `systemPrompt(...)` builders\n * auto-register into is `ai.prompts` (see {@link defaultPromptsManager}).\n *\n * @param options - Optional wiring, notably a `judgeCache` that memoizes\n * LLM-judge verdicts (absent ⇒ every judge pass runs live).\n *\n * @example\n * const registry = prompts();\n * registry.register(systemPrompt(\"You are support.\", { name: \"support\" }));\n * registry.resolve(\"support\"); // \"You are support.\"\n *\n * @example\n * // Memoize judge verdicts across validations.\n * const registry = prompts({ judgeCache: new MemoryCacheDriver() });\n */\nexport function prompts(options?: PromptsManagerOptions): PromptsManagerContract {\n return new PromptsManager(options);\n}\n\n/**\n * The process-wide default manager that named prompts auto-register into.\n *\n * Held as a module-level singleton (lazily created on first access) so\n * `system-prompt.ts` can register a named builder without importing the\n * `PromptsManager` class — keeping the auto-registration seam free of a\n * runtime import cycle.\n */\nlet defaultManager: PromptsManagerContract | undefined;\n\n/** Accessor for the process-wide default {@link PromptsManagerContract}. */\nexport function defaultPromptsManager(): PromptsManagerContract {\n if (!defaultManager) {\n defaultManager = new PromptsManager();\n }\n\n return defaultManager;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"prompts-manager.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/prompts/prompts-manager.ts"],"sourcesContent":["import type { Placeholders } from \"../contracts/placeholders.type\";\nimport type {\n SystemPromptBlockContract,\n SystemPromptContract,\n SystemPromptMeta,\n} from \"../contracts/system-prompt.contract\";\nimport { InvalidRequestError } from \"../errors\";\nimport { Instruction } from \"../system-prompt/instruction\";\nimport { Persona } from \"../system-prompt/persona\";\nimport { SystemPrompt } from \"../system-prompt/system-prompt\";\nimport type {\n PromptsManagerContract,\n PromptsManagerEntry,\n PromptsManagerRegisterOptions,\n} from \"./prompts-manager.contract\";\nimport type {\n ExportedPromptVersion,\n ExportedRegistry,\n PromptDiff,\n PromptDiffBlock,\n PromptJudgeCacheLike,\n PromptsManagerOptions,\n PromptTemplateVersion,\n PromptValidateTarget,\n PromptValidationResult,\n PromptsValidateOptions,\n} from \"./prompts-manager.type\";\nimport {\n describeContractTarget,\n findMissingPlaceholders,\n findUnreferencedRequired,\n judgePromptBodyCached,\n} from \"./prompts-validate\";\n\n/**\n * Build the `name@version` registry key. Centralized so the duplicate check,\n * `get`, and `composedFrom` provenance all agree on one label shape.\n */\nexport function promptKey(name: string, version: string): string {\n return `${name}@${version}`;\n}\n\n/**\n * Serialize a prompt's observable content — its ordered blocks (discriminator\n * + raw template text) — into a stable signature. Two prompts with the same\n * blocks in the same order share a signature, which is how `register()` tells\n * an idempotent re-registration from a genuine clash. Meta is intentionally\n * excluded: provenance / description should not defeat idempotency.\n */\nfunction contentSignature(contract: SystemPromptContract): string {\n return JSON.stringify(\n contract.blocks.map(block => [block.type, block.text]),\n );\n}\n\n/**\n * Reconstruct a block from its `{ type, text }` snapshot — `persona` blocks\n * become a `Persona`, everything else an `Instruction`. The inverse of the\n * flattening `export()` performs, so an imported registry resolves identically.\n */\nfunction blockFromSnapshot(block: PromptDiffBlock): SystemPromptBlockContract {\n return block.type === \"persona\"\n ? new Persona(block.text)\n : new Instruction(block.text);\n}\n\n/**\n * Narrow a {@link PromptTemplateVersion} body to its ordered block list: a raw\n * string becomes one instruction block; an explicit block list is used verbatim.\n */\nfunction blocksFromTemplate(\n template: string | readonly SystemPromptBlockContract[],\n): SystemPromptBlockContract[] {\n if (typeof template === \"string\") {\n return [new Instruction(template)];\n }\n\n return [...template];\n}\n\n/**\n * Concrete `PromptsManagerContract` — a single registry of named, versioned\n * `SystemPromptContract` builders keyed by `name@version`.\n *\n * **Role.** The store behind `ai.prompts`. It holds one flat\n * `Map<string, PromptsManagerEntry>` keyed by `name@version`, plus a monotonic\n * counter that stamps each entry's `addedAt` so \"latest\" is deterministic\n * (highest `addedAt` for a name) without ever reading the wall clock.\n *\n * **Responsibility.**\n * - Owns: the registry map, the `addedAt` counter, the duplicate /\n * idempotency rule, default version derivation, latest selection, the\n * per-version tag pins, and the validate / diff / export / import surface.\n * - Does NOT own: prompt rendering (delegated to the contract's `resolve()`),\n * block composition, or the LLM-judge mechanics (delegated to the eval\n * `judge` scorer via `prompts-validate`).\n *\n * Users construct via the `prompts()` factory — `new PromptsManager()` is not\n * the public API.\n */\nclass PromptsManager implements PromptsManagerContract {\n /** Flat registry keyed by `name@version`. */\n private readonly entries = new Map<string, PromptsManagerEntry>();\n\n /** First-seen order of names, for a stable `list()`. */\n private readonly names: string[] = [];\n\n /** Per-name tag pins: `name` → (`tag` → `version`). */\n private readonly pins = new Map<string, Map<string, string>>();\n\n /** Optional process-level judge-verdict memo (absent ⇒ judge always runs live). */\n private readonly judgeCache?: PromptJudgeCacheLike;\n\n /** Monotonic insertion counter — the deterministic stand-in for a timestamp. */\n private counter = 0;\n\n public constructor(options: PromptsManagerOptions = {}) {\n this.judgeCache = options.judgeCache;\n }\n\n public register(\n contract: SystemPromptContract,\n options: PromptsManagerRegisterOptions = {},\n ): PromptsManagerContract {\n const meta = contract.meta();\n // An explicit override (from define() / import()) wins over the contract's\n // own meta — it lets those bulk paths register an anonymous contract under\n // a name without the SystemPrompt constructor's default-manager auto-reg.\n const name = options.name ?? meta?.name;\n\n if (!name) {\n throw new InvalidRequestError(\n \"Cannot register a prompt without a name — set meta.name via \" +\n \"systemPrompt(input, { name }) or .meta({ name }).\",\n { context: { meta } },\n );\n }\n\n const version =\n options.version ?? meta?.version ?? this.nextVersion(name);\n const key = promptKey(name, version);\n const existing = this.entries.get(key);\n\n if (existing) {\n // Idempotent re-registration: identical content under the same\n // name@version is a no-op, not an error. Anything else is a clash.\n if (contentSignature(existing.contract) === contentSignature(contract)) {\n return this;\n }\n\n throw new InvalidRequestError(\n `A different prompt is already registered as \"${key}\".`,\n { context: { name, version } },\n );\n }\n\n if (!this.names.includes(name)) {\n this.names.push(name);\n }\n\n this.entries.set(key, {\n name,\n version,\n addedAt: this.counter++,\n contract,\n ...(options.tags ? { tags: options.tags } : {}),\n });\n\n return this;\n }\n\n public create(\n input?: string | ReadonlyArray<SystemPromptBlockContract>,\n meta?: SystemPromptMeta,\n ): SystemPromptContract {\n // Mirror `systemPromptFactory` exactly (no import — `system-prompt.ts`\n // already depends on this module, so importing its factory back here would\n // close an import cycle). A name in `meta` auto-registers into the\n // process-wide default manager via the SystemPrompt constructor.\n if (input === undefined) {\n return new SystemPrompt([], meta);\n }\n\n if (typeof input === \"string\") {\n return new SystemPrompt([new Instruction(input)], meta);\n }\n\n return new SystemPrompt([...input], meta);\n }\n\n public get(name: string, versionOrTag?: string): SystemPromptContract {\n return this.requireEntry(name, versionOrTag).contract;\n }\n\n public has(name: string, versionOrTag?: string): boolean {\n const { baseName, selector } = this.parseSelector(name, versionOrTag);\n\n if (selector !== undefined) {\n return this.resolveSelector(baseName, selector) !== undefined;\n }\n\n return this.latestEntry(baseName) !== undefined;\n }\n\n public list(): string[] {\n return [...this.names];\n }\n\n public versions(name: string): string[] {\n return [...this.entries.values()]\n .filter(entry => entry.name === name)\n .sort((a, b) => a.addedAt - b.addedAt)\n .map(entry => entry.version);\n }\n\n public resolve(\n name: string,\n versionOrTag?: string,\n placeholders?: Placeholders,\n ): string {\n return this.requireEntry(name, versionOrTag).contract.resolve(placeholders);\n }\n\n public define(\n name: string,\n versions: readonly PromptTemplateVersion[],\n ): PromptsManagerContract {\n for (const entry of versions) {\n const blocks = blocksFromTemplate(entry.template);\n // Anonymous contract (no name in meta ⇒ no SystemPrompt constructor\n // auto-registration into the default manager); the name/version are\n // supplied explicitly so define() targets only THIS manager.\n const contract = new SystemPrompt(blocks);\n\n this.register(contract, { name, version: entry.version });\n }\n\n return this;\n }\n\n public tag(\n name: string,\n tag: string,\n version: string,\n ): PromptsManagerContract {\n // Validate the target exists before pinning — a tag to a missing version is\n // an authoring mistake, not a silent dangling pin.\n if (!this.entries.has(promptKey(name, version))) {\n throw new InvalidRequestError(\n `Cannot tag \"${tag}\" — no prompt registered as \"${promptKey(\n name,\n version,\n )}\".`,\n { context: { name, tag, version } },\n );\n }\n\n const nameTags = this.pins.get(name) ?? new Map<string, string>();\n nameTags.set(tag, version);\n this.pins.set(name, nameTags);\n\n return this;\n }\n\n public async validate(\n target: PromptValidateTarget,\n options: PromptsValidateOptions = {},\n ): Promise<PromptValidationResult> {\n const { text, required } = this.describeTarget(target);\n\n const provided = new Set(Object.keys(options.placeholders ?? {}));\n const declared = new Set<string>([\n ...required,\n ...(options.declare ?? []),\n ]);\n\n const missing = findMissingPlaceholders(text, provided, declared);\n\n // A declared-required key that the body never references is itself a\n // defect — surface it as an issue (it does not affect `missing` / `ok`,\n // which track unresolved placeholders).\n const unreferenced = findUnreferencedRequired(text, required);\n\n const ok = missing.length === 0;\n\n if (!options.judge) {\n if (unreferenced.length === 0) {\n return { ok, missing };\n }\n\n return {\n ok,\n missing,\n issues: unreferenced.map(\n key => `Required key \"${key}\" is never referenced in the prompt.`,\n ),\n };\n }\n\n // Per-call cache override wins over the manager-level memo. `criteria`\n // (when set) replaces the built-in rubric the judge grades against.\n const cache = options.judgeCache ?? this.judgeCache;\n const judgeOutcome = await judgePromptBodyCached(\n text,\n options.judge,\n cache,\n options.criteria,\n );\n\n const issues = [\n ...unreferenced.map(\n key => `Required key \"${key}\" is never referenced in the prompt.`,\n ),\n ...judgeOutcome.issues,\n ];\n\n return {\n ok,\n missing,\n ...(judgeOutcome.score !== undefined ? { score: judgeOutcome.score } : {}),\n issues,\n };\n }\n\n public diff(name: string, from: string, to: string): PromptDiff {\n const fromBlocks = this.snapshotBlocks(this.requireExact(name, from));\n const toBlocks = this.snapshotBlocks(this.requireExact(name, to));\n\n const added: PromptDiffBlock[] = [];\n const removed: PromptDiffBlock[] = [];\n const changed: { from: PromptDiffBlock; to: PromptDiffBlock }[] = [];\n\n const max = Math.max(fromBlocks.length, toBlocks.length);\n\n for (let index = 0; index < max; index++) {\n const left = fromBlocks[index];\n const right = toBlocks[index];\n\n if (left && !right) {\n removed.push(left);\n continue;\n }\n\n if (!left && right) {\n added.push(right);\n continue;\n }\n\n if (left && right && (left.type !== right.type || left.text !== right.text)) {\n changed.push({ from: left, to: right });\n }\n }\n\n return {\n name,\n from,\n to,\n added,\n removed,\n changed,\n identical:\n added.length === 0 && removed.length === 0 && changed.length === 0,\n };\n }\n\n public export(): ExportedRegistry {\n return {\n prompts: this.names.map(name => ({\n name,\n versions: this.versions(name).map(version =>\n this.exportVersion(name, version),\n ),\n })),\n };\n }\n\n public import(snapshot: ExportedRegistry): PromptsManagerContract {\n for (const exported of snapshot.prompts) {\n for (const version of exported.versions) {\n const blocks = version.blocks.map(blockFromSnapshot);\n // Anonymous (no `name` in meta) so the SystemPrompt constructor does\n // not auto-register into the default manager; description / required\n // ride along for round-trip fidelity. Name/version are explicit so the\n // import lands only on THIS manager.\n const contract = new SystemPrompt(blocks, {\n ...(version.description ? { description: version.description } : {}),\n ...(version.required ? { required: version.required } : {}),\n });\n\n this.register(contract, {\n name: exported.name,\n version: version.version,\n });\n\n for (const tag of version.tags ?? []) {\n this.tag(exported.name, tag, version.version);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Flatten a registered version into its portable `{ version, blocks, tags?,\n * description?, required? }` snapshot for `export()`.\n */\n private exportVersion(name: string, version: string): ExportedPromptVersion {\n const entry = this.requireExact(name, version);\n const meta = entry.contract.meta();\n const tags = this.tagsForVersion(name, version);\n\n return {\n version,\n blocks: this.snapshotBlocks(entry),\n ...(tags.length > 0 ? { tags } : {}),\n ...(meta?.description ? { description: meta.description } : {}),\n ...(meta?.required ? { required: [...meta.required] } : {}),\n };\n }\n\n /** Every tag currently pinned to a specific `name@version`, in pin order. */\n private tagsForVersion(name: string, version: string): string[] {\n const nameTags = this.pins.get(name);\n\n if (!nameTags) {\n return [];\n }\n\n const tags: string[] = [];\n\n for (const [tag, pinnedVersion] of nameTags) {\n if (pinnedVersion === version) {\n tags.push(tag);\n }\n }\n\n return tags;\n }\n\n /** Flatten an entry's blocks to `{ type, text }` snapshots. */\n private snapshotBlocks(entry: PromptsManagerEntry): PromptDiffBlock[] {\n return entry.contract.blocks.map(block => ({\n type: block.type,\n text: block.text,\n }));\n }\n\n /**\n * Resolve the body + declared-required keys for any `validate` target: a\n * registered name (or `name@selector`), a `SystemPromptContract` instance, or\n * a raw string.\n */\n private describeTarget(target: PromptValidateTarget): {\n text: string;\n required: readonly string[];\n } {\n if (typeof target === \"string\") {\n // An inline `name@selector` (or a bare registered name) resolves through\n // the registry; anything else is a raw prompt body validated verbatim.\n const { baseName, selector } = this.parseSelector(target, undefined);\n const entry = selector\n ? this.resolveSelector(baseName, selector)\n : this.latestEntry(baseName);\n\n if (entry) {\n return describeContractTarget(entry.contract);\n }\n\n return { text: target, required: [] };\n }\n\n if (isSystemPromptContract(target)) {\n return describeContractTarget(target);\n }\n\n if (isBlock(target)) {\n return { text: target.text, required: [] };\n }\n\n throw new InvalidRequestError(\n \"validate() target must be a registered name, a SystemPromptContract, \" +\n \"a prompt block, or a raw string.\",\n { context: { target } },\n );\n }\n\n /**\n * The next integer version label for a name — `\"1\"` for the first, then the\n * count of existing versions plus one. String-typed to match the free-form\n * `version` label shape.\n */\n private nextVersion(name: string): string {\n const count = [...this.entries.values()].filter(\n entry => entry.name === name,\n ).length;\n\n return String(count + 1);\n }\n\n /** Pick the highest-`addedAt` entry for a name, or `undefined` when absent. */\n private latestEntry(name: string): PromptsManagerEntry | undefined {\n let latest: PromptsManagerEntry | undefined;\n\n for (const entry of this.entries.values()) {\n if (entry.name !== name) {\n continue;\n }\n\n if (!latest || entry.addedAt > latest.addedAt) {\n latest = entry;\n }\n }\n\n return latest;\n }\n\n /**\n * Split a name argument into its base name + optional selector. The selector\n * comes from the explicit second argument when present, else from an inline\n * `name@selector` in the first argument. A bare name yields no selector.\n */\n private parseSelector(\n name: string,\n versionOrTag: string | undefined,\n ): { baseName: string; selector: string | undefined } {\n if (versionOrTag !== undefined) {\n return { baseName: name, selector: versionOrTag };\n }\n\n const at = name.indexOf(\"@\");\n\n if (at > 0) {\n return { baseName: name.slice(0, at), selector: name.slice(at + 1) };\n }\n\n return { baseName: name, selector: undefined };\n }\n\n /**\n * Resolve a selector (a version label OR a pinned tag) to a concrete entry.\n * Version labels win over tags when both could match — the explicit label is\n * the more specific intent. Returns `undefined` when neither resolves.\n */\n private resolveSelector(\n name: string,\n selector: string,\n ): PromptsManagerEntry | undefined {\n const byVersion = this.entries.get(promptKey(name, selector));\n\n if (byVersion) {\n return byVersion;\n }\n\n const pinnedVersion = this.pins.get(name)?.get(selector);\n\n if (pinnedVersion !== undefined) {\n return this.entries.get(promptKey(name, pinnedVersion));\n }\n\n return undefined;\n }\n\n /**\n * Resolve an entry by name (+ optional version / tag / inline selector),\n * throwing {@link InvalidRequestError} when the name or the requested\n * selector is unknown. The single lookup path `get` / `resolve` share.\n */\n private requireEntry(\n name: string,\n versionOrTag?: string,\n ): PromptsManagerEntry {\n const { baseName, selector } = this.parseSelector(name, versionOrTag);\n\n if (selector !== undefined) {\n const entry = this.resolveSelector(baseName, selector);\n\n if (!entry) {\n throw new InvalidRequestError(\n `No prompt registered as \"${baseName}\" with version/tag \"${selector}\".`,\n { context: { name: baseName, selector } },\n );\n }\n\n return entry;\n }\n\n const latest = this.latestEntry(baseName);\n\n if (!latest) {\n throw new InvalidRequestError(\n `No prompt registered under name \"${baseName}\".`,\n { context: { name: baseName } },\n );\n }\n\n return latest;\n }\n\n /**\n * Resolve a name + EXACT version label to its entry (no tag fallback), for\n * `diff` / `export` where a concrete version is always required. Throws\n * {@link InvalidRequestError} on a miss.\n */\n private requireExact(name: string, version: string): PromptsManagerEntry {\n const entry = this.entries.get(promptKey(name, version));\n\n if (!entry) {\n throw new InvalidRequestError(\n `No prompt registered as \"${promptKey(name, version)}\".`,\n { context: { name, version } },\n );\n }\n\n return entry;\n }\n}\n\n/**\n * Narrow an arbitrary value to a `SystemPromptContract` — true when it exposes\n * the builder surface (`blocks` array + a callable `resolve`) AND a callable\n * `meta`. Robust across duplicate package copies (no `instanceof`).\n */\nfunction isSystemPromptContract(\n value: unknown,\n): value is SystemPromptContract {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray((value as { blocks?: unknown }).blocks) &&\n typeof (value as { resolve?: unknown }).resolve === \"function\" &&\n typeof (value as { meta?: unknown }).meta === \"function\"\n );\n}\n\n/**\n * Narrow an arbitrary value to a single `SystemPromptBlockContract` — true when\n * it carries a string `type` + `text` and a callable `resolve` but is NOT a\n * full prompt (no `blocks` array). Lets `validate` accept a lone block.\n */\nfunction isBlock(value: unknown): value is SystemPromptBlockContract {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\" &&\n typeof (value as { text?: unknown }).text === \"string\" &&\n typeof (value as { resolve?: unknown }).resolve === \"function\"\n );\n}\n\n/**\n * Create a new, isolated prompts manager.\n *\n * **Role.** Public factory for {@link PromptsManagerContract} — keeps\n * user-facing code free of `new` and consistent with the other `ai.*`\n * factories. Each call returns a fresh registry, so parallel test suites and\n * multi-tenant apps never share mutable global prompt state.\n *\n * The process-wide instance that named `systemPrompt(...)` builders\n * auto-register into is `ai.prompts` (see {@link defaultPromptsManager}).\n *\n * @param options - Optional wiring, notably a `judgeCache` that memoizes\n * LLM-judge verdicts (absent ⇒ every judge pass runs live).\n *\n * @example\n * const registry = prompts();\n * registry.register(systemPrompt(\"You are support.\", { name: \"support\" }));\n * registry.resolve(\"support\"); // \"You are support.\"\n *\n * @example\n * // Memoize judge verdicts across validations.\n * const registry = prompts({ judgeCache: new MemoryCacheDriver() });\n */\nexport function prompts(options?: PromptsManagerOptions): PromptsManagerContract {\n return new PromptsManager(options);\n}\n\n/**\n * The process-wide default manager that named prompts auto-register into.\n *\n * Held as a module-level singleton (lazily created on first access) so\n * `system-prompt.ts` can register a named builder without importing the\n * `PromptsManager` class — keeping the auto-registration seam free of a\n * runtime import cycle.\n */\nlet defaultManager: PromptsManagerContract | undefined;\n\n/** Accessor for the process-wide default {@link PromptsManagerContract}. */\nexport function defaultPromptsManager(): PromptsManagerContract {\n if (!defaultManager) {\n defaultManager = new PromptsManager();\n }\n\n return defaultManager;\n}\n"],"mappings":";;;;;;;;;;;;AAsCA,SAAgB,UAAU,MAAc,SAAyB;CAC/D,OAAO,GAAG,KAAK,GAAG;AACpB;;;;;;;;AASA,SAAS,iBAAiB,UAAwC;CAChE,OAAO,KAAK,UACV,SAAS,OAAO,KAAI,UAAS,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,CACvD;AACF;;;;;;AAOA,SAAS,kBAAkB,OAAmD;CAC5E,OAAO,MAAM,SAAS,YAClB,IAAI,QAAQ,MAAM,IAAI,IACtB,IAAI,YAAY,MAAM,IAAI;AAChC;;;;;AAMA,SAAS,mBACP,UAC6B;CAC7B,IAAI,OAAO,aAAa,UACtB,OAAO,CAAC,IAAI,YAAY,QAAQ,CAAC;CAGnC,OAAO,CAAC,GAAG,QAAQ;AACrB;;;;;;;;;;;;;;;;;;;;;AAsBA,IAAM,iBAAN,MAAuD;CAgBrD,AAAO,YAAY,UAAiC,CAAC,GAAG;iCAd7B,IAAI,IAAiC;eAG7B,CAAC;8BAGZ,IAAI,IAAiC;iBAM3C;EAGhB,KAAK,aAAa,QAAQ;CAC5B;CAEA,AAAO,SACL,UACA,UAAyC,CAAC,GAClB;EACxB,MAAM,OAAO,SAAS,KAAK;EAI3B,MAAM,OAAO,QAAQ,QAAQ,MAAM;EAEnC,IAAI,CAAC,MACH,MAAM,IAAI,oBACR,iHAEA,EAAE,SAAS,EAAE,KAAK,EAAE,CACtB;EAGF,MAAM,UACJ,QAAQ,WAAW,MAAM,WAAW,KAAK,YAAY,IAAI;EAC3D,MAAM,MAAM,UAAU,MAAM,OAAO;EACnC,MAAM,WAAW,KAAK,QAAQ,IAAI,GAAG;EAErC,IAAI,UAAU;GAGZ,IAAI,iBAAiB,SAAS,QAAQ,MAAM,iBAAiB,QAAQ,GACnE,OAAO;GAGT,MAAM,IAAI,oBACR,gDAAgD,IAAI,KACpD,EAAE,SAAS;IAAE;IAAM;GAAQ,EAAE,CAC/B;EACF;EAEA,IAAI,CAAC,KAAK,MAAM,SAAS,IAAI,GAC3B,KAAK,MAAM,KAAK,IAAI;EAGtB,KAAK,QAAQ,IAAI,KAAK;GACpB;GACA;GACA,SAAS,KAAK;GACd;GACA,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;EAC/C,CAAC;EAED,OAAO;CACT;CAEA,AAAO,OACL,OACA,MACsB;EAKtB,IAAI,UAAU,QACZ,OAAO,IAAI,aAAa,CAAC,GAAG,IAAI;EAGlC,IAAI,OAAO,UAAU,UACnB,OAAO,IAAI,aAAa,CAAC,IAAI,YAAY,KAAK,CAAC,GAAG,IAAI;EAGxD,OAAO,IAAI,aAAa,CAAC,GAAG,KAAK,GAAG,IAAI;CAC1C;CAEA,AAAO,IAAI,MAAc,cAA6C;EACpE,OAAO,KAAK,aAAa,MAAM,YAAY,CAAC,CAAC;CAC/C;CAEA,AAAO,IAAI,MAAc,cAAgC;EACvD,MAAM,EAAE,UAAU,aAAa,KAAK,cAAc,MAAM,YAAY;EAEpE,IAAI,aAAa,QACf,OAAO,KAAK,gBAAgB,UAAU,QAAQ,MAAM;EAGtD,OAAO,KAAK,YAAY,QAAQ,MAAM;CACxC;CAEA,AAAO,OAAiB;EACtB,OAAO,CAAC,GAAG,KAAK,KAAK;CACvB;CAEA,AAAO,SAAS,MAAwB;EACtC,OAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAC9B,QAAO,UAAS,MAAM,SAAS,IAAI,CAAC,CACpC,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CACrC,KAAI,UAAS,MAAM,OAAO;CAC/B;CAEA,AAAO,QACL,MACA,cACA,cACQ;EACR,OAAO,KAAK,aAAa,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,YAAY;CAC5E;CAEA,AAAO,OACL,MACA,UACwB;EACxB,KAAK,MAAM,SAAS,UAAU;GAK5B,MAAM,WAAW,IAAI,aAJN,mBAAmB,MAAM,QAID,CAAC;GAExC,KAAK,SAAS,UAAU;IAAE;IAAM,SAAS,MAAM;GAAQ,CAAC;EAC1D;EAEA,OAAO;CACT;CAEA,AAAO,IACL,MACA,KACA,SACwB;EAGxB,IAAI,CAAC,KAAK,QAAQ,IAAI,UAAU,MAAM,OAAO,CAAC,GAC5C,MAAM,IAAI,oBACR,eAAe,IAAI,+BAA+B,UAChD,MACA,OACF,EAAE,KACF,EAAE,SAAS;GAAE;GAAM;GAAK;EAAQ,EAAE,CACpC;EAGF,MAAM,WAAW,KAAK,KAAK,IAAI,IAAI,qBAAK,IAAI,IAAoB;EAChE,SAAS,IAAI,KAAK,OAAO;EACzB,KAAK,KAAK,IAAI,MAAM,QAAQ;EAE5B,OAAO;CACT;CAEA,MAAa,SACX,QACA,UAAkC,CAAC,GACF;EACjC,MAAM,EAAE,MAAM,aAAa,KAAK,eAAe,MAAM;EAQrD,MAAM,UAAU,wBAAwB,MAAM,IANzB,IAAI,OAAO,KAAK,QAAQ,gBAAgB,CAAC,CAAC,CAMV,GAAG,IALnC,IAAY,CAC/B,GAAG,UACH,GAAI,QAAQ,WAAW,CAAC,CAC1B,CAE+D,CAAC;EAKhE,MAAM,eAAe,yBAAyB,MAAM,QAAQ;EAE5D,MAAM,KAAK,QAAQ,WAAW;EAE9B,IAAI,CAAC,QAAQ,OAAO;GAClB,IAAI,aAAa,WAAW,GAC1B,OAAO;IAAE;IAAI;GAAQ;GAGvB,OAAO;IACL;IACA;IACA,QAAQ,aAAa,KACnB,QAAO,iBAAiB,IAAI,qCAC9B;GACF;EACF;EAIA,MAAM,QAAQ,QAAQ,cAAc,KAAK;EACzC,MAAM,eAAe,MAAM,sBACzB,MACA,QAAQ,OACR,OACA,QAAQ,QACV;EAEA,MAAM,SAAS,CACb,GAAG,aAAa,KACd,QAAO,iBAAiB,IAAI,qCAC9B,GACA,GAAG,aAAa,MAClB;EAEA,OAAO;GACL;GACA;GACA,GAAI,aAAa,UAAU,SAAY,EAAE,OAAO,aAAa,MAAM,IAAI,CAAC;GACxE;EACF;CACF;CAEA,AAAO,KAAK,MAAc,MAAc,IAAwB;EAC9D,MAAM,aAAa,KAAK,eAAe,KAAK,aAAa,MAAM,IAAI,CAAC;EACpE,MAAM,WAAW,KAAK,eAAe,KAAK,aAAa,MAAM,EAAE,CAAC;EAEhE,MAAM,QAA2B,CAAC;EAClC,MAAM,UAA6B,CAAC;EACpC,MAAM,UAA4D,CAAC;EAEnE,MAAM,MAAM,KAAK,IAAI,WAAW,QAAQ,SAAS,MAAM;EAEvD,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,SAAS;GACxC,MAAM,OAAO,WAAW;GACxB,MAAM,QAAQ,SAAS;GAEvB,IAAI,QAAQ,CAAC,OAAO;IAClB,QAAQ,KAAK,IAAI;IACjB;GACF;GAEA,IAAI,CAAC,QAAQ,OAAO;IAClB,MAAM,KAAK,KAAK;IAChB;GACF;GAEA,IAAI,QAAQ,UAAU,KAAK,SAAS,MAAM,QAAQ,KAAK,SAAS,MAAM,OACpE,QAAQ,KAAK;IAAE,MAAM;IAAM,IAAI;GAAM,CAAC;EAE1C;EAEA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA,WACE,MAAM,WAAW,KAAK,QAAQ,WAAW,KAAK,QAAQ,WAAW;EACrE;CACF;CAEA,AAAO,SAA2B;EAChC,OAAO,EACL,SAAS,KAAK,MAAM,KAAI,UAAS;GAC/B;GACA,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,KAAI,YAChC,KAAK,cAAc,MAAM,OAAO,CAClC;EACF,EAAE,EACJ;CACF;CAEA,AAAO,OAAO,UAAoD;EAChE,KAAK,MAAM,YAAY,SAAS,SAC9B,KAAK,MAAM,WAAW,SAAS,UAAU;GAMvC,MAAM,WAAW,IAAI,aALN,QAAQ,OAAO,IAAI,iBAKK,GAAG;IACxC,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;IAClE,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;GAC3D,CAAC;GAED,KAAK,SAAS,UAAU;IACtB,MAAM,SAAS;IACf,SAAS,QAAQ;GACnB,CAAC;GAED,KAAK,MAAM,OAAO,QAAQ,QAAQ,CAAC,GACjC,KAAK,IAAI,SAAS,MAAM,KAAK,QAAQ,OAAO;EAEhD;EAGF,OAAO;CACT;;;;;CAMA,AAAQ,cAAc,MAAc,SAAwC;EAC1E,MAAM,QAAQ,KAAK,aAAa,MAAM,OAAO;EAC7C,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,MAAM,OAAO,KAAK,eAAe,MAAM,OAAO;EAE9C,OAAO;GACL;GACA,QAAQ,KAAK,eAAe,KAAK;GACjC,GAAI,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;GAClC,GAAI,MAAM,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;GAC7D,GAAI,MAAM,WAAW,EAAE,UAAU,CAAC,GAAG,KAAK,QAAQ,EAAE,IAAI,CAAC;EAC3D;CACF;;CAGA,AAAQ,eAAe,MAAc,SAA2B;EAC9D,MAAM,WAAW,KAAK,KAAK,IAAI,IAAI;EAEnC,IAAI,CAAC,UACH,OAAO,CAAC;EAGV,MAAM,OAAiB,CAAC;EAExB,KAAK,MAAM,CAAC,KAAK,kBAAkB,UACjC,IAAI,kBAAkB,SACpB,KAAK,KAAK,GAAG;EAIjB,OAAO;CACT;;CAGA,AAAQ,eAAe,OAA+C;EACpE,OAAO,MAAM,SAAS,OAAO,KAAI,WAAU;GACzC,MAAM,MAAM;GACZ,MAAM,MAAM;EACd,EAAE;CACJ;;;;;;CAOA,AAAQ,eAAe,QAGrB;EACA,IAAI,OAAO,WAAW,UAAU;GAG9B,MAAM,EAAE,UAAU,aAAa,KAAK,cAAc,QAAQ,MAAS;GACnE,MAAM,QAAQ,WACV,KAAK,gBAAgB,UAAU,QAAQ,IACvC,KAAK,YAAY,QAAQ;GAE7B,IAAI,OACF,OAAO,uBAAuB,MAAM,QAAQ;GAG9C,OAAO;IAAE,MAAM;IAAQ,UAAU,CAAC;GAAE;EACtC;EAEA,IAAI,uBAAuB,MAAM,GAC/B,OAAO,uBAAuB,MAAM;EAGtC,IAAI,QAAQ,MAAM,GAChB,OAAO;GAAE,MAAM,OAAO;GAAM,UAAU,CAAC;EAAE;EAG3C,MAAM,IAAI,oBACR,yGAEA,EAAE,SAAS,EAAE,OAAO,EAAE,CACxB;CACF;;;;;;CAOA,AAAQ,YAAY,MAAsB;EACxC,MAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAAC,QACvC,UAAS,MAAM,SAAS,IAC1B,CAAC,CAAC;EAEF,OAAO,OAAO,QAAQ,CAAC;CACzB;;CAGA,AAAQ,YAAY,MAA+C;EACjE,IAAI;EAEJ,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GAAG;GACzC,IAAI,MAAM,SAAS,MACjB;GAGF,IAAI,CAAC,UAAU,MAAM,UAAU,OAAO,SACpC,SAAS;EAEb;EAEA,OAAO;CACT;;;;;;CAOA,AAAQ,cACN,MACA,cACoD;EACpD,IAAI,iBAAiB,QACnB,OAAO;GAAE,UAAU;GAAM,UAAU;EAAa;EAGlD,MAAM,KAAK,KAAK,QAAQ,GAAG;EAE3B,IAAI,KAAK,GACP,OAAO;GAAE,UAAU,KAAK,MAAM,GAAG,EAAE;GAAG,UAAU,KAAK,MAAM,KAAK,CAAC;EAAE;EAGrE,OAAO;GAAE,UAAU;GAAM,UAAU;EAAU;CAC/C;;;;;;CAOA,AAAQ,gBACN,MACA,UACiC;EACjC,MAAM,YAAY,KAAK,QAAQ,IAAI,UAAU,MAAM,QAAQ,CAAC;EAE5D,IAAI,WACF,OAAO;EAGT,MAAM,gBAAgB,KAAK,KAAK,IAAI,IAAI,CAAC,EAAE,IAAI,QAAQ;EAEvD,IAAI,kBAAkB,QACpB,OAAO,KAAK,QAAQ,IAAI,UAAU,MAAM,aAAa,CAAC;CAI1D;;;;;;CAOA,AAAQ,aACN,MACA,cACqB;EACrB,MAAM,EAAE,UAAU,aAAa,KAAK,cAAc,MAAM,YAAY;EAEpE,IAAI,aAAa,QAAW;GAC1B,MAAM,QAAQ,KAAK,gBAAgB,UAAU,QAAQ;GAErD,IAAI,CAAC,OACH,MAAM,IAAI,oBACR,4BAA4B,SAAS,sBAAsB,SAAS,KACpE,EAAE,SAAS;IAAE,MAAM;IAAU;GAAS,EAAE,CAC1C;GAGF,OAAO;EACT;EAEA,MAAM,SAAS,KAAK,YAAY,QAAQ;EAExC,IAAI,CAAC,QACH,MAAM,IAAI,oBACR,oCAAoC,SAAS,KAC7C,EAAE,SAAS,EAAE,MAAM,SAAS,EAAE,CAChC;EAGF,OAAO;CACT;;;;;;CAOA,AAAQ,aAAa,MAAc,SAAsC;EACvE,MAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU,MAAM,OAAO,CAAC;EAEvD,IAAI,CAAC,OACH,MAAM,IAAI,oBACR,4BAA4B,UAAU,MAAM,OAAO,EAAE,KACrD,EAAE,SAAS;GAAE;GAAM;EAAQ,EAAE,CAC/B;EAGF,OAAO;CACT;AACF;;;;;;AAOA,SAAS,uBACP,OAC+B;CAC/B,OACE,OAAO,UAAU,YACjB,UAAU,QACV,MAAM,QAAS,MAA+B,MAAM,KACpD,OAAQ,MAAgC,YAAY,cACpD,OAAQ,MAA6B,SAAS;AAElD;;;;;;AAOA,SAAS,QAAQ,OAAoD;CACnE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS,YAC9C,OAAQ,MAA6B,SAAS,YAC9C,OAAQ,MAAgC,YAAY;AAExD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,QAAQ,SAAyD;CAC/E,OAAO,IAAI,eAAe,OAAO;AACnC;;;;;;;;;AAUA,IAAI;;AAGJ,SAAgB,wBAAgD;CAC9D,IAAI,CAAC,gBACH,iBAAiB,IAAI,eAAe;CAGtC,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"prompts-validate.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/prompts/prompts-validate.ts"],"sourcesContent":["import { agent } from \"../agent/agent\";\nimport type { AgentContract } from \"../contracts/agent/agent.contract\";\nimport type { ModelContract } from \"../contracts/model.contract\";\nimport type { SystemPromptContract } from \"../contracts/system-prompt.contract\";\nimport { judge } from \"../eval/judge-scorer\";\nimport { PROMPT_JUDGE_RUBRIC } from \"../prompt/prompt-validate\";\nimport type { PromptJudgeCacheLike } from \"./prompts-manager.type\";\n\n/**\n * Placeholder matcher — kept in lock-step with the matcher\n * `renderPlaceholders` (`src/system-prompt/render-placeholders.ts`) and the\n * legacy `prompt-validate` lint both use, so the deterministic validator sees\n * the exact same `{{key}}` / `{{a.b}}` / `{{key|default}}` set the renderer\n * substitutes. Global so every occurrence is collected.\n */\nconst PLACEHOLDER_PATTERN = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\n/**\n * One parsed placeholder occurrence — the key path (the part before any\n * `|default`) and whether the occurrence carried an inline default.\n */\ntype ParsedPlaceholder = {\n /** The dot-path key, e.g. `language` or `user.name`. */\n readonly path: string;\n /** Whether THIS occurrence declared an inline `{{key|default}}` fallback. */\n readonly hasDefault: boolean;\n};\n\n/**\n * Collect every distinct placeholder occurrence from a template, in first-seen\n * order. A key is considered to \"have a default\" only when EVERY occurrence of\n * it carries one — a single bare `{{key}}` means the renderer can leave it\n * unresolved, so the key is still required.\n */\nfunction collectPlaceholders(template: string): ParsedPlaceholder[] {\n const byPath = new Map<string, boolean>();\n const order: string[] = [];\n\n for (const match of template.matchAll(PLACEHOLDER_PATTERN)) {\n const [rawPath, rawDefault] = match[1].split(\"|\");\n const path = rawPath.trim();\n\n if (path.length === 0) {\n continue;\n }\n\n const hasDefault = rawDefault !== undefined;\n\n if (!byPath.has(path)) {\n byPath.set(path, hasDefault);\n order.push(path);\n } else {\n // A key only counts as defaulted when ALL of its occurrences default.\n byPath.set(path, (byPath.get(path) ?? false) && hasDefault);\n }\n }\n\n return order.map(path => ({ path, hasDefault: byPath.get(path) ?? false }));\n}\n\n/**\n * Run the deterministic (model-free) half of validation over a resolved prompt\n * body. Reports every `{{key}}` placeholder that has NO inline default and is\n * neither supplied in `provided` nor declared in `declared` (the prompt's\n * `meta.required` plus any caller-declared keys).\n *\n * Pure and synchronous — the only required half of `validate`; the LLM-judge\n * half is optional and layered on top.\n *\n * @param text - The resolved prompt body (placeholders may still be present).\n * @param provided - Placeholder keys the caller has supplied a value for.\n * @param declared - Placeholder keys declared as known/required (e.g. `meta.required`).\n */\nexport function findMissingPlaceholders(\n text: string,\n provided: ReadonlySet<string>,\n declared: ReadonlySet<string>,\n): string[] {\n const missing: string[] = [];\n\n for (const { path, hasDefault } of collectPlaceholders(text)) {\n if (hasDefault) {\n continue;\n }\n\n if (provided.has(path) || declared.has(path)) {\n continue;\n }\n\n missing.push(path);\n }\n\n return missing;\n}\n\n/**\n * A `meta.required` key absent from the template entirely — declared as\n * required but never referenced — is itself a defect worth surfacing. Returns\n * the declared keys that appear nowhere in the body.\n */\nexport function findUnreferencedRequired(\n text: string,\n required: readonly string[],\n): string[] {\n const present = new Set(collectPlaceholders(text).map(p => p.path));\n\n return required.filter(key => !present.has(key));\n}\n\n/**\n * Build the one-shot judge agent the optional LLM-as-judge pass runs. Mirrors\n * the legacy `prompt.ts` judge agent (strict-JSON instruction so the verdict\n * parses even without an output schema), so the two validate paths share one\n * judging contract.\n */\nfunction buildJudgeAgent(model: ModelContract): AgentContract<unknown> {\n return agent({\n name: \"prompt-quality-judge\",\n model,\n systemPrompt:\n \"You are a strict prompt-quality grader. Respond with JSON only: \" +\n '{ \"score\": <0..1>, \"passed\": <true|false>, \"reason\": \"<short explanation>\" }.',\n });\n}\n\n/** Outcome of the optional LLM-as-judge pass over a resolved prompt body. */\nexport type JudgeOutcome = {\n /**\n * The judge score in `[0, 1]`, or `undefined` when the judge degraded\n * (errored, returned no parseable verdict, or threw) — never a misleading\n * `0` masquerading as a real verdict.\n */\n readonly score?: number;\n /** Human-readable issues raised by the judge (its reason, or a degrade note). */\n readonly issues: string[];\n};\n\n/**\n * Run the optional LLM-as-judge pass over a resolved prompt body, REUSING the\n * eval `judge` scorer (the same path `prompt().validate` uses) so there is no\n * second judging implementation.\n *\n * **Nova-safe by contract.** The judge NEVER throws here: the eval scorer\n * already degrades a broken judge to `score: 0` with a failure reason, and any\n * exception that still escapes (model wiring, agent construction) is caught.\n * Both degrade paths surface `score: undefined` plus an issue note — so a flaky\n * judge can never fail an otherwise-valid prompt.\n *\n * @param text - The resolved prompt body under evaluation.\n * @param model - The model that powers the judge agent.\n */\nexport async function judgePromptBody(\n text: string,\n model: ModelContract,\n): Promise<JudgeOutcome> {\n try {\n const judgeAgent = buildJudgeAgent(model);\n const scorer = judge({ agent: judgeAgent, rubric: PROMPT_JUDGE_RUBRIC });\n\n const verdict = await scorer({\n case: { name: \"prompt-quality\", input: \"Grade the system prompt below.\" },\n text,\n // `result` is unused by the judge scorer's prompt builder; a minimal\n // stand-in keeps the structural contract satisfied without a real run.\n result: { text } as never,\n output: undefined,\n });\n\n // The eval scorer signals a degraded judge with score 0 + a diagnostic\n // reason (\"judge failed: …\" / \"judge returned no parseable verdict\"). Treat\n // that as \"no usable score\" rather than a real 0 verdict.\n const degraded =\n verdict.score === 0 &&\n typeof verdict.reason === \"string\" &&\n /^judge (failed|returned no parseable)/.test(verdict.reason);\n\n if (degraded) {\n return {\n issues: [`LLM-judge unavailable: ${verdict.reason}`],\n };\n }\n\n return {\n score: verdict.score,\n issues: verdict.reason ? [verdict.reason] : [],\n };\n } catch (error) {\n // Last-resort guard: never let a judge failure throw out of validate().\n const message = error instanceof Error ? error.message : String(error);\n\n return {\n issues: [`LLM-judge unavailable: ${message}`],\n };\n }\n}\n\n/**\n * Non-cryptographic 53-bit string hash (cyrb53) — deterministic across runs\n * and platforms, with no `node:crypto` dependency (keeps the validate path\n * usable in any runtime). Mirrors the VCR request hash; collision-resistant\n * enough for a per-prompt judge-verdict keyspace. Returned as base-36.\n */\nfunction hashString(input: string): string {\n let h1 = 0xdeadbeef;\n let h2 = 0x41c6ce57;\n\n for (let i = 0; i < input.length; i++) {\n const ch = input.charCodeAt(i);\n\n h1 = Math.imul(h1 ^ ch, 2654435761);\n h2 = Math.imul(h2 ^ ch, 1597334677);\n }\n\n h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);\n h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);\n h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n\n const combined = 4294967296 * (2097151 & h2) + (h1 >>> 0);\n\n return combined.toString(36);\n}\n\n/**\n * Build the judge-verdict cache key for a resolved prompt body + judge model.\n * Combines the model's `provider:name` identity with a content hash of the\n * body, so the same prompt graded by the same judge hits the cache, while any\n * change to either misses it.\n */\nexport function judgeCacheKey(text: string, model: ModelContract): string {\n return `prompts.judge.${model.provider}:${model.name}.${hashString(text)}`;\n}\n\n/**\n * Run the judge pass with an OPTIONAL memo cache in front. On a hit, the stored\n * {@link JudgeOutcome} is returned without a model call; on a miss, the live\n * judge runs and a USABLE verdict (one carrying a `score`) is written back.\n * Degraded outcomes (no score) are NOT cached — a transient judge failure must\n * never poison the memo. A `null`/absent cache degrades to a direct judge call.\n *\n * Cache I/O is itself fault-tolerant: a `get`/`set` that rejects is swallowed\n * so a flaky cache can never break (or fail) validation.\n *\n * @param text - The resolved prompt body under evaluation.\n * @param model - The judge model.\n * @param cache - Optional verdict memo (any `CacheDriver`-like get/set surface).\n */\nexport async function judgePromptBodyCached(\n text: string,\n model: ModelContract,\n cache?: PromptJudgeCacheLike,\n): Promise<JudgeOutcome> {\n if (!cache) {\n return judgePromptBody(text, model);\n }\n\n const key = judgeCacheKey(text, model);\n\n const cached = await readJudgeCache(cache, key);\n\n if (cached) {\n return cached;\n }\n\n const outcome = await judgePromptBody(text, model);\n\n // Only memoize a usable verdict — never a degraded (scoreless) one.\n if (outcome.score !== undefined) {\n await writeJudgeCache(cache, key, outcome);\n }\n\n return outcome;\n}\n\n/** Read a cached verdict, swallowing any cache fault (treated as a miss). */\nasync function readJudgeCache(\n cache: PromptJudgeCacheLike,\n key: string,\n): Promise<JudgeOutcome | undefined> {\n try {\n const value = await cache.get<JudgeOutcome>(key);\n\n return value ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Write a verdict, swallowing any cache fault (best-effort memo). */\nasync function writeJudgeCache(\n cache: PromptJudgeCacheLike,\n key: string,\n outcome: JudgeOutcome,\n): Promise<void> {\n try {\n await cache.set(key, outcome);\n } catch {\n // Best-effort — a failed memo write never affects the validation result.\n }\n}\n\n/**\n * Resolve the body + declared-required keys for a validation target that is a\n * `SystemPromptContract` (named or anonymous). The declared set is the\n * prompt's `meta.required` (when present).\n */\nexport function describeContractTarget(contract: SystemPromptContract): {\n text: string;\n required: readonly string[];\n} {\n const meta = contract.meta();\n\n return {\n text: contract.resolve(),\n required: meta?.required ?? [],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"prompts-validate.mjs","names":[],"sources":["../../../../../../../../../@warlock.js/ai/src/prompts/prompts-validate.ts"],"sourcesContent":["import { agent } from \"../agent/agent\";\nimport type { AgentContract } from \"../contracts/agent/agent.contract\";\nimport type { ModelContract } from \"../contracts/model.contract\";\nimport type { SystemPromptContract } from \"../contracts/system-prompt.contract\";\nimport { judge } from \"../eval/judge-scorer\";\nimport { PROMPT_JUDGE_RUBRIC } from \"../prompt/prompt-validate\";\nimport type { PromptJudgeCacheLike } from \"./prompts-manager.type\";\n\n/**\n * Placeholder matcher — kept in lock-step with the matcher\n * `renderPlaceholders` (`src/system-prompt/render-placeholders.ts`) and the\n * legacy `prompt-validate` lint both use, so the deterministic validator sees\n * the exact same `{{key}}` / `{{a.b}}` / `{{key|default}}` set the renderer\n * substitutes. Global so every occurrence is collected.\n */\nconst PLACEHOLDER_PATTERN = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\n/**\n * One parsed placeholder occurrence — the key path (the part before any\n * `|default`) and whether the occurrence carried an inline default.\n */\ntype ParsedPlaceholder = {\n /** The dot-path key, e.g. `language` or `user.name`. */\n readonly path: string;\n /** Whether THIS occurrence declared an inline `{{key|default}}` fallback. */\n readonly hasDefault: boolean;\n};\n\n/**\n * Collect every distinct placeholder occurrence from a template, in first-seen\n * order. A key is considered to \"have a default\" only when EVERY occurrence of\n * it carries one — a single bare `{{key}}` means the renderer can leave it\n * unresolved, so the key is still required.\n */\nfunction collectPlaceholders(template: string): ParsedPlaceholder[] {\n const byPath = new Map<string, boolean>();\n const order: string[] = [];\n\n for (const match of template.matchAll(PLACEHOLDER_PATTERN)) {\n const [rawPath, rawDefault] = match[1].split(\"|\");\n const path = rawPath.trim();\n\n if (path.length === 0) {\n continue;\n }\n\n const hasDefault = rawDefault !== undefined;\n\n if (!byPath.has(path)) {\n byPath.set(path, hasDefault);\n order.push(path);\n } else {\n // A key only counts as defaulted when ALL of its occurrences default.\n byPath.set(path, (byPath.get(path) ?? false) && hasDefault);\n }\n }\n\n return order.map(path => ({ path, hasDefault: byPath.get(path) ?? false }));\n}\n\n/**\n * Run the deterministic (model-free) half of validation over a resolved prompt\n * body. Reports every `{{key}}` placeholder that has NO inline default and is\n * neither supplied in `provided` nor declared in `declared` (the prompt's\n * `meta.required` plus any caller-declared keys).\n *\n * Pure and synchronous — the only required half of `validate`; the LLM-judge\n * half is optional and layered on top.\n *\n * @param text - The resolved prompt body (placeholders may still be present).\n * @param provided - Placeholder keys the caller has supplied a value for.\n * @param declared - Placeholder keys declared as known/required (e.g. `meta.required`).\n */\nexport function findMissingPlaceholders(\n text: string,\n provided: ReadonlySet<string>,\n declared: ReadonlySet<string>,\n): string[] {\n const missing: string[] = [];\n\n for (const { path, hasDefault } of collectPlaceholders(text)) {\n if (hasDefault) {\n continue;\n }\n\n if (provided.has(path) || declared.has(path)) {\n continue;\n }\n\n missing.push(path);\n }\n\n return missing;\n}\n\n/**\n * A `meta.required` key absent from the template entirely — declared as\n * required but never referenced — is itself a defect worth surfacing. Returns\n * the declared keys that appear nowhere in the body.\n */\nexport function findUnreferencedRequired(\n text: string,\n required: readonly string[],\n): string[] {\n const present = new Set(collectPlaceholders(text).map(p => p.path));\n\n return required.filter(key => !present.has(key));\n}\n\n/**\n * Build the one-shot judge agent the optional LLM-as-judge pass runs. Mirrors\n * the legacy `prompt.ts` judge agent (strict-JSON instruction so the verdict\n * parses even without an output schema), so the two validate paths share one\n * judging contract.\n */\nfunction buildJudgeAgent(model: ModelContract): AgentContract<unknown> {\n return agent({\n name: \"prompt-quality-judge\",\n model,\n systemPrompt:\n \"You are a strict prompt-quality grader. Respond with JSON only: \" +\n '{ \"score\": <0..1>, \"passed\": <true|false>, \"reason\": \"<short explanation>\" }.',\n });\n}\n\n/**\n * Turn caller-supplied `criteria` into the judge rubric that replaces the\n * built-in {@link PROMPT_JUDGE_RUBRIC}. A single string is used verbatim;\n * a list is joined into a numbered rule set the judge must check ALL of.\n * Returns `undefined` for an empty/blank input, so the caller falls back\n * to the default rubric.\n *\n * @example\n * formatCriteria([\"Addresses the user by {{name}}\", \"Under 200 words\"]);\n * // → \"Grade the system prompt against ALL of these criteria …\\n1. …\\n2. …\"\n */\nexport function formatCriteria(\n criteria: string | readonly string[] | undefined,\n): string | undefined {\n if (criteria === undefined) {\n return undefined;\n }\n\n if (typeof criteria === \"string\") {\n const trimmed = criteria.trim();\n\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n const rules = criteria.map(rule => rule.trim()).filter(rule => rule.length > 0);\n\n if (rules.length === 0) {\n return undefined;\n }\n\n return (\n \"Grade the system prompt against ALL of the following criteria — it passes only if it satisfies every one:\\n\" +\n rules.map((rule, index) => `${index + 1}. ${rule}`).join(\"\\n\")\n );\n}\n\n/** Outcome of the optional LLM-as-judge pass over a resolved prompt body. */\nexport type JudgeOutcome = {\n /**\n * The judge score in `[0, 1]`, or `undefined` when the judge degraded\n * (errored, returned no parseable verdict, or threw) — never a misleading\n * `0` masquerading as a real verdict.\n */\n readonly score?: number;\n /** Human-readable issues raised by the judge (its reason, or a degrade note). */\n readonly issues: string[];\n};\n\n/**\n * Run the optional LLM-as-judge pass over a resolved prompt body, REUSING the\n * eval `judge` scorer (the same path `prompt().validate` uses) so there is no\n * second judging implementation.\n *\n * **Nova-safe by contract.** The judge NEVER throws here: the eval scorer\n * already degrades a broken judge to `score: 0` with a failure reason, and any\n * exception that still escapes (model wiring, agent construction) is caught.\n * Both degrade paths surface `score: undefined` plus an issue note — so a flaky\n * judge can never fail an otherwise-valid prompt.\n *\n * @param text - The resolved prompt body under evaluation.\n * @param model - The model that powers the judge agent.\n * @param criteria - Optional caller rules that REPLACE the built-in rubric\n * ({@link formatCriteria}). Omitted ⇒ the default prompt-quality rubric.\n */\nexport async function judgePromptBody(\n text: string,\n model: ModelContract,\n criteria?: string | readonly string[],\n): Promise<JudgeOutcome> {\n try {\n const judgeAgent = buildJudgeAgent(model);\n const scorer = judge({\n agent: judgeAgent,\n rubric: formatCriteria(criteria) ?? PROMPT_JUDGE_RUBRIC,\n });\n\n const verdict = await scorer({\n case: { name: \"prompt-quality\", input: \"Grade the system prompt below.\" },\n text,\n // `result` is unused by the judge scorer's prompt builder; a minimal\n // stand-in keeps the structural contract satisfied without a real run.\n result: { text } as never,\n output: undefined,\n });\n\n // The eval scorer signals a degraded judge with score 0 + a diagnostic\n // reason (\"judge failed: …\" / \"judge returned no parseable verdict\"). Treat\n // that as \"no usable score\" rather than a real 0 verdict.\n const degraded =\n verdict.score === 0 &&\n typeof verdict.reason === \"string\" &&\n /^judge (failed|returned no parseable)/.test(verdict.reason);\n\n if (degraded) {\n return {\n issues: [`LLM-judge unavailable: ${verdict.reason}`],\n };\n }\n\n return {\n score: verdict.score,\n issues: verdict.reason ? [verdict.reason] : [],\n };\n } catch (error) {\n // Last-resort guard: never let a judge failure throw out of validate().\n const message = error instanceof Error ? error.message : String(error);\n\n return {\n issues: [`LLM-judge unavailable: ${message}`],\n };\n }\n}\n\n/**\n * Non-cryptographic 53-bit string hash (cyrb53) — deterministic across runs\n * and platforms, with no `node:crypto` dependency (keeps the validate path\n * usable in any runtime). Mirrors the VCR request hash; collision-resistant\n * enough for a per-prompt judge-verdict keyspace. Returned as base-36.\n */\nfunction hashString(input: string): string {\n let h1 = 0xdeadbeef;\n let h2 = 0x41c6ce57;\n\n for (let i = 0; i < input.length; i++) {\n const ch = input.charCodeAt(i);\n\n h1 = Math.imul(h1 ^ ch, 2654435761);\n h2 = Math.imul(h2 ^ ch, 1597334677);\n }\n\n h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);\n h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);\n h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n\n const combined = 4294967296 * (2097151 & h2) + (h1 >>> 0);\n\n return combined.toString(36);\n}\n\n/**\n * Build the judge-verdict cache key for a resolved prompt body + judge model\n * + the effective rubric. Combines the model's `provider:name` identity with a\n * content hash of the rubric-plus-body, so the same prompt graded by the same\n * judge against the same rules hits the cache — while a change to the prompt,\n * the model, OR the `criteria` misses it (different rules ⇒ different verdict).\n */\nexport function judgeCacheKey(\n text: string,\n model: ModelContract,\n criteria?: string | readonly string[],\n): string {\n const rubric = formatCriteria(criteria) ?? PROMPT_JUDGE_RUBRIC;\n\n return `prompts.judge.${model.provider}:${model.name}.${hashString(`${rubric}\u0000${text}`)}`;\n}\n\n/**\n * Run the judge pass with an OPTIONAL memo cache in front. On a hit, the stored\n * {@link JudgeOutcome} is returned without a model call; on a miss, the live\n * judge runs and a USABLE verdict (one carrying a `score`) is written back.\n * Degraded outcomes (no score) are NOT cached — a transient judge failure must\n * never poison the memo. A `null`/absent cache degrades to a direct judge call.\n *\n * Cache I/O is itself fault-tolerant: a `get`/`set` that rejects is swallowed\n * so a flaky cache can never break (or fail) validation.\n *\n * @param text - The resolved prompt body under evaluation.\n * @param model - The judge model.\n * @param cache - Optional verdict memo (any `CacheDriver`-like get/set surface).\n * @param criteria - Optional caller rules that REPLACE the built-in rubric; also\n * folded into the cache key so a re-validation with different rules re-runs.\n */\nexport async function judgePromptBodyCached(\n text: string,\n model: ModelContract,\n cache?: PromptJudgeCacheLike,\n criteria?: string | readonly string[],\n): Promise<JudgeOutcome> {\n if (!cache) {\n return judgePromptBody(text, model, criteria);\n }\n\n const key = judgeCacheKey(text, model, criteria);\n\n const cached = await readJudgeCache(cache, key);\n\n if (cached) {\n return cached;\n }\n\n const outcome = await judgePromptBody(text, model, criteria);\n\n // Only memoize a usable verdict — never a degraded (scoreless) one.\n if (outcome.score !== undefined) {\n await writeJudgeCache(cache, key, outcome);\n }\n\n return outcome;\n}\n\n/** Read a cached verdict, swallowing any cache fault (treated as a miss). */\nasync function readJudgeCache(\n cache: PromptJudgeCacheLike,\n key: string,\n): Promise<JudgeOutcome | undefined> {\n try {\n const value = await cache.get<JudgeOutcome>(key);\n\n return value ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Write a verdict, swallowing any cache fault (best-effort memo). */\nasync function writeJudgeCache(\n cache: PromptJudgeCacheLike,\n key: string,\n outcome: JudgeOutcome,\n): Promise<void> {\n try {\n await cache.set(key, outcome);\n } catch {\n // Best-effort — a failed memo write never affects the validation result.\n }\n}\n\n/**\n * Resolve the body + declared-required keys for a validation target that is a\n * `SystemPromptContract` (named or anonymous). The declared set is the\n * prompt's `meta.required` (when present).\n */\nexport function describeContractTarget(contract: SystemPromptContract): {\n text: string;\n required: readonly string[];\n} {\n const meta = contract.meta();\n\n return {\n text: contract.resolve(),\n required: meta?.required ?? [],\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { renderPlaceholders } from "./render-placeholders.mjs";
2
2
  import { Instruction, instruction } from "./instruction.mjs";
3
3
  import { Persona, persona } from "./persona.mjs";
4
+ import { RefinedSystemPrompt } from "./refined-system-prompt.mjs";
4
5
  import { SystemPrompt, systemPrompt } from "./system-prompt.mjs";
5
6
 
6
7
  export { };