@warlock.js/ai 4.3.0 → 4.5.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 (541) hide show
  1. package/CHANGELOG.md +52 -48
  2. package/cjs/index.cjs +221 -14144
  3. package/cjs/src-C02yzsLs.cjs +22991 -0
  4. package/cjs/src-C02yzsLs.cjs.map +1 -0
  5. package/cjs/src-DFibP2FQ.cjs +30 -0
  6. package/esm/agent/agent-config.type.d.mts +90 -1
  7. package/esm/agent/agent-config.type.d.mts.map +1 -1
  8. package/esm/agent/agent-input-builder.mjs +27 -6
  9. package/esm/agent/agent-input-builder.mjs.map +1 -1
  10. package/esm/agent/agent.d.mts +40 -1
  11. package/esm/agent/agent.d.mts.map +1 -1
  12. package/esm/agent/agent.mjs +203 -30
  13. package/esm/agent/agent.mjs.map +1 -1
  14. package/esm/agent/index.d.mts +2 -1
  15. package/esm/agent/index.mjs +1 -0
  16. package/esm/agent/judge-config.type.d.mts +33 -0
  17. package/esm/agent/judge-config.type.d.mts.map +1 -0
  18. package/esm/agent/judge-config.type.mjs +13 -0
  19. package/esm/agent/judge-config.type.mjs.map +1 -0
  20. package/esm/ai-openai/src/embedder.mjs +4 -0
  21. package/esm/ai-openai/src/index.mjs +4 -0
  22. package/esm/ai-openai/src/model.mjs +5 -0
  23. package/esm/ai-openai/src/sdk.mjs +6 -0
  24. package/esm/ai-openai/src/utils/index.mjs +4 -0
  25. package/esm/ai-openai/src/utils/to-openai-tools.mjs +3 -0
  26. package/esm/ai-openai/src/utils/wrap-openai-error.mjs +4 -0
  27. package/esm/ai.d.mts +119 -53
  28. package/esm/ai.d.mts.map +1 -1
  29. package/esm/ai.mjs +43 -8
  30. package/esm/ai.mjs.map +1 -1
  31. package/esm/batch/batch.d.mts.map +1 -1
  32. package/esm/batch/batch.mjs +21 -1
  33. package/esm/batch/batch.mjs.map +1 -1
  34. package/esm/batch/batch.type.d.mts +11 -4
  35. package/esm/batch/batch.type.d.mts.map +1 -1
  36. package/esm/config.d.mts +39 -3
  37. package/esm/config.d.mts.map +1 -1
  38. package/esm/config.mjs +26 -2
  39. package/esm/config.mjs.map +1 -1
  40. package/esm/contracts/agent/agent-options.type.d.mts +11 -3
  41. package/esm/contracts/agent/agent-options.type.d.mts.map +1 -1
  42. package/esm/contracts/agent/eval.type.d.mts +43 -2
  43. package/esm/contracts/agent/eval.type.d.mts.map +1 -1
  44. package/esm/contracts/attachment-policy.type.d.mts +51 -0
  45. package/esm/contracts/attachment-policy.type.d.mts.map +1 -0
  46. package/esm/contracts/attachment.type.d.mts +16 -7
  47. package/esm/contracts/attachment.type.d.mts.map +1 -1
  48. package/esm/contracts/content-part.type.d.mts +14 -5
  49. package/esm/contracts/content-part.type.d.mts.map +1 -1
  50. package/esm/contracts/events/supervisor-events.type.d.mts.map +1 -1
  51. package/esm/contracts/index.d.mts +6 -4
  52. package/esm/contracts/model.contract.d.mts +11 -8
  53. package/esm/contracts/model.contract.d.mts.map +1 -1
  54. package/esm/contracts/orchestrator/index.d.mts +1 -0
  55. package/esm/contracts/orchestrator/orchestrator-config.type.d.mts +31 -2
  56. package/esm/contracts/orchestrator/orchestrator-config.type.d.mts.map +1 -1
  57. package/esm/contracts/orchestrator/session-lock.contract.d.mts +47 -0
  58. package/esm/contracts/orchestrator/session-lock.contract.d.mts.map +1 -0
  59. package/esm/contracts/planner/index.d.mts +1 -1
  60. package/esm/contracts/planner/planner-config.type.d.mts +31 -0
  61. package/esm/contracts/planner/planner-config.type.d.mts.map +1 -1
  62. package/esm/contracts/planner/planner-execute-options.type.d.mts +48 -1
  63. package/esm/contracts/planner/planner-execute-options.type.d.mts.map +1 -1
  64. package/esm/contracts/planner/planner-result.type.d.mts +19 -5
  65. package/esm/contracts/planner/planner-result.type.d.mts.map +1 -1
  66. package/esm/contracts/planner/planner.contract.d.mts +1 -1
  67. package/esm/contracts/result/agent-result.type.d.mts +6 -4
  68. package/esm/contracts/result/agent-result.type.d.mts.map +1 -1
  69. package/esm/contracts/result/base-report.type.d.mts +21 -3
  70. package/esm/contracts/result/base-report.type.d.mts.map +1 -1
  71. package/esm/contracts/result/base-report.type.mjs.map +1 -1
  72. package/esm/contracts/result/execution-report.type.d.mts +53 -1
  73. package/esm/contracts/result/execution-report.type.d.mts.map +1 -1
  74. package/esm/contracts/result/orchestrator-result.type.d.mts +15 -11
  75. package/esm/contracts/result/orchestrator-result.type.d.mts.map +1 -1
  76. package/esm/contracts/result/supervisor-result.type.d.mts +13 -1
  77. package/esm/contracts/result/supervisor-result.type.d.mts.map +1 -1
  78. package/esm/contracts/result/tool-call.type.d.mts +2 -2
  79. package/esm/contracts/result/tool-call.type.d.mts.map +1 -1
  80. package/esm/contracts/result/workflow-result.type.d.mts +1 -0
  81. package/esm/contracts/result/workflow-result.type.d.mts.map +1 -1
  82. package/esm/contracts/supervisor/supervisor-config.type.d.mts +29 -0
  83. package/esm/contracts/supervisor/supervisor-config.type.d.mts.map +1 -1
  84. package/esm/contracts/system-prompt.contract.d.mts +103 -1
  85. package/esm/contracts/system-prompt.contract.d.mts.map +1 -1
  86. package/esm/contracts/team/index.d.mts +1 -0
  87. package/esm/contracts/team/team-config.type.d.mts +127 -0
  88. package/esm/contracts/team/team-config.type.d.mts.map +1 -0
  89. package/esm/contracts/tool.contract.d.mts +4 -2
  90. package/esm/contracts/tool.contract.d.mts.map +1 -1
  91. package/esm/contracts/workflow/step.contract.d.mts +30 -5
  92. package/esm/contracts/workflow/step.contract.d.mts.map +1 -1
  93. package/esm/contracts/workflow/workflow.contract.d.mts +16 -0
  94. package/esm/contracts/workflow/workflow.contract.d.mts.map +1 -1
  95. package/esm/errors/error-code.type.d.mts +1 -1
  96. package/esm/errors/index.d.mts +1 -0
  97. package/esm/errors/index.mjs +1 -0
  98. package/esm/errors/outbound-policy-error.d.mts +27 -0
  99. package/esm/errors/outbound-policy-error.d.mts.map +1 -0
  100. package/esm/errors/outbound-policy-error.mjs +32 -0
  101. package/esm/errors/outbound-policy-error.mjs.map +1 -0
  102. package/esm/eval/dataset.d.mts +28 -0
  103. package/esm/eval/dataset.d.mts.map +1 -0
  104. package/esm/eval/dataset.mjs +112 -0
  105. package/esm/eval/dataset.mjs.map +1 -0
  106. package/esm/eval/dataset.type.d.mts +53 -0
  107. package/esm/eval/dataset.type.d.mts.map +1 -0
  108. package/esm/eval/eval-runner.d.mts.map +1 -1
  109. package/esm/eval/eval-runner.mjs +16 -2
  110. package/esm/eval/eval-runner.mjs.map +1 -1
  111. package/esm/eval/index.d.mts +20 -1
  112. package/esm/eval/index.d.mts.map +1 -1
  113. package/esm/eval/index.mjs +20 -2
  114. package/esm/eval/index.mjs.map +1 -1
  115. package/esm/eval/regression.d.mts +30 -0
  116. package/esm/eval/regression.d.mts.map +1 -0
  117. package/esm/eval/regression.mjs +51 -0
  118. package/esm/eval/regression.mjs.map +1 -0
  119. package/esm/eval/report-json.d.mts +30 -0
  120. package/esm/eval/report-json.d.mts.map +1 -0
  121. package/esm/eval/report-json.mjs +33 -0
  122. package/esm/eval/report-json.mjs.map +1 -0
  123. package/esm/eval/report-junit.d.mts +22 -0
  124. package/esm/eval/report-junit.d.mts.map +1 -0
  125. package/esm/eval/report-junit.mjs +60 -0
  126. package/esm/eval/report-junit.mjs.map +1 -0
  127. package/esm/guard/contracts/guard-options.type.d.mts +165 -0
  128. package/esm/guard/contracts/guard-options.type.d.mts.map +1 -0
  129. package/esm/guard/contracts/guardrail.contract.d.mts +78 -0
  130. package/esm/guard/contracts/guardrail.contract.d.mts.map +1 -0
  131. package/esm/guard/contracts/index.d.mts +4 -0
  132. package/esm/guard/contracts/openai-client.contract.d.mts +51 -0
  133. package/esm/guard/contracts/openai-client.contract.d.mts.map +1 -0
  134. package/esm/guard/contracts/verdict.type.d.mts +76 -0
  135. package/esm/guard/contracts/verdict.type.d.mts.map +1 -0
  136. package/esm/guard/detectors/index.d.mts +4 -0
  137. package/esm/guard/detectors/index.mjs +6 -0
  138. package/esm/guard/detectors/injection.d.mts +34 -0
  139. package/esm/guard/detectors/injection.d.mts.map +1 -0
  140. package/esm/guard/detectors/injection.mjs +254 -0
  141. package/esm/guard/detectors/injection.mjs.map +1 -0
  142. package/esm/guard/detectors/moderation.d.mts +32 -0
  143. package/esm/guard/detectors/moderation.d.mts.map +1 -0
  144. package/esm/guard/detectors/moderation.mjs +134 -0
  145. package/esm/guard/detectors/moderation.mjs.map +1 -0
  146. package/esm/guard/detectors/pii.d.mts +41 -0
  147. package/esm/guard/detectors/pii.d.mts.map +1 -0
  148. package/esm/guard/detectors/pii.mjs +199 -0
  149. package/esm/guard/detectors/pii.mjs.map +1 -0
  150. package/esm/guard/detectors/topic.d.mts +29 -0
  151. package/esm/guard/detectors/topic.d.mts.map +1 -0
  152. package/esm/guard/detectors/topic.mjs +99 -0
  153. package/esm/guard/detectors/topic.mjs.map +1 -0
  154. package/esm/guard/errors.d.mts +12 -0
  155. package/esm/guard/errors.d.mts.map +1 -0
  156. package/esm/guard/errors.mjs +18 -0
  157. package/esm/guard/errors.mjs.map +1 -0
  158. package/esm/guard/guard.d.mts +67 -0
  159. package/esm/guard/guard.d.mts.map +1 -0
  160. package/esm/guard/guard.mjs +209 -0
  161. package/esm/guard/guard.mjs.map +1 -0
  162. package/esm/guard/guardrail.d.mts +39 -0
  163. package/esm/guard/guardrail.d.mts.map +1 -0
  164. package/esm/guard/guardrail.mjs +22 -0
  165. package/esm/guard/guardrail.mjs.map +1 -0
  166. package/esm/human/contracts/approval.type.d.mts +154 -0
  167. package/esm/human/contracts/approval.type.d.mts.map +1 -0
  168. package/esm/human/contracts/human-approval.type.d.mts +38 -0
  169. package/esm/human/contracts/human-approval.type.d.mts.map +1 -0
  170. package/esm/human/contracts/index.d.mts +4 -0
  171. package/esm/human/contracts/interrupt-store.contract.d.mts +112 -0
  172. package/esm/human/contracts/interrupt-store.contract.d.mts.map +1 -0
  173. package/esm/human/contracts/resume.type.d.mts +77 -0
  174. package/esm/human/contracts/resume.type.d.mts.map +1 -0
  175. package/esm/human/errors.d.mts +84 -0
  176. package/esm/human/errors.d.mts.map +1 -0
  177. package/esm/human/errors.mjs +60 -0
  178. package/esm/human/errors.mjs.map +1 -0
  179. package/esm/human/human-approval.d.mts +57 -0
  180. package/esm/human/human-approval.d.mts.map +1 -0
  181. package/esm/human/human-approval.mjs +170 -0
  182. package/esm/human/human-approval.mjs.map +1 -0
  183. package/esm/human/policy.d.mts +55 -0
  184. package/esm/human/policy.d.mts.map +1 -0
  185. package/esm/human/policy.mjs +67 -0
  186. package/esm/human/policy.mjs.map +1 -0
  187. package/esm/human/register.mjs +37 -0
  188. package/esm/human/register.mjs.map +1 -0
  189. package/esm/human/resume-seed.mjs +53 -0
  190. package/esm/human/resume-seed.mjs.map +1 -0
  191. package/esm/human/resume.d.mts +54 -0
  192. package/esm/human/resume.d.mts.map +1 -0
  193. package/esm/human/resume.mjs +121 -0
  194. package/esm/human/resume.mjs.map +1 -0
  195. package/esm/human/stores/index.mjs +5 -0
  196. package/esm/human/stores/memory.d.mts +30 -0
  197. package/esm/human/stores/memory.d.mts.map +1 -0
  198. package/esm/human/stores/memory.mjs +91 -0
  199. package/esm/human/stores/memory.mjs.map +1 -0
  200. package/esm/human/stores/pg.d.mts +59 -0
  201. package/esm/human/stores/pg.d.mts.map +1 -0
  202. package/esm/human/stores/pg.mjs +220 -0
  203. package/esm/human/stores/pg.mjs.map +1 -0
  204. package/esm/human/stores/redis.d.mts +56 -0
  205. package/esm/human/stores/redis.d.mts.map +1 -0
  206. package/esm/human/stores/redis.mjs +201 -0
  207. package/esm/human/stores/redis.mjs.map +1 -0
  208. package/esm/index.d.mts +113 -22
  209. package/esm/index.mjs +82 -11
  210. package/esm/middleware/builtins/budget.mjs +6 -2
  211. package/esm/middleware/builtins/budget.mjs.map +1 -1
  212. package/esm/middleware/utils/extract-user-text.d.mts +8 -1
  213. package/esm/middleware/utils/extract-user-text.d.mts.map +1 -1
  214. package/esm/middleware/utils/extract-user-text.mjs +8 -1
  215. package/esm/middleware/utils/extract-user-text.mjs.map +1 -1
  216. package/esm/object-stream/index.d.mts +2 -0
  217. package/esm/object-stream/index.mjs +4 -0
  218. package/esm/object-stream/parse-partial-json.d.mts +22 -0
  219. package/esm/object-stream/parse-partial-json.d.mts.map +1 -0
  220. package/esm/object-stream/parse-partial-json.mjs +78 -0
  221. package/esm/object-stream/parse-partial-json.mjs.map +1 -0
  222. package/esm/object-stream/stream-object.d.mts +68 -0
  223. package/esm/object-stream/stream-object.d.mts.map +1 -0
  224. package/esm/object-stream/stream-object.mjs +104 -0
  225. package/esm/object-stream/stream-object.mjs.map +1 -0
  226. package/esm/observe/index.mjs +4 -0
  227. package/esm/observe/observer-registry.d.mts +30 -0
  228. package/esm/observe/observer-registry.d.mts.map +1 -0
  229. package/esm/observe/observer-registry.mjs +51 -0
  230. package/esm/observe/observer-registry.mjs.map +1 -0
  231. package/esm/observe/observer.contract.d.mts +40 -0
  232. package/esm/observe/observer.contract.d.mts.map +1 -0
  233. package/esm/observe/resolve-observers.d.mts +40 -0
  234. package/esm/observe/resolve-observers.d.mts.map +1 -0
  235. package/esm/observe/resolve-observers.mjs +73 -0
  236. package/esm/observe/resolve-observers.mjs.map +1 -0
  237. package/esm/orchestrator/execution.d.mts.map +1 -1
  238. package/esm/orchestrator/execution.mjs +5 -2
  239. package/esm/orchestrator/execution.mjs.map +1 -1
  240. package/esm/orchestrator/index.d.mts +1 -0
  241. package/esm/orchestrator/index.mjs +1 -0
  242. package/esm/orchestrator/orchestrator.d.mts.map +1 -1
  243. package/esm/orchestrator/orchestrator.mjs +39 -6
  244. package/esm/orchestrator/orchestrator.mjs.map +1 -1
  245. package/esm/orchestrator/session-lock.d.mts +25 -0
  246. package/esm/orchestrator/session-lock.d.mts.map +1 -0
  247. package/esm/orchestrator/session-lock.mjs +83 -0
  248. package/esm/orchestrator/session-lock.mjs.map +1 -0
  249. package/esm/planner/dag-scheduler.mjs +97 -0
  250. package/esm/planner/dag-scheduler.mjs.map +1 -0
  251. package/esm/planner/plan-prompt.d.mts +1 -1
  252. package/esm/planner/plan-prompt.d.mts.map +1 -1
  253. package/esm/planner/plan-prompt.mjs +2 -1
  254. package/esm/planner/plan-prompt.mjs.map +1 -1
  255. package/esm/planner/plan-schema.mjs +17 -14
  256. package/esm/planner/plan-schema.mjs.map +1 -1
  257. package/esm/planner/planner-run.d.mts.map +1 -1
  258. package/esm/planner/planner-run.mjs +300 -29
  259. package/esm/planner/planner-run.mjs.map +1 -1
  260. package/esm/planner/planner.mjs +1 -1
  261. package/esm/planner/planner.mjs.map +1 -1
  262. package/esm/prompt/errors.d.mts +57 -0
  263. package/esm/prompt/errors.d.mts.map +1 -0
  264. package/esm/prompt/errors.mjs +73 -0
  265. package/esm/prompt/errors.mjs.map +1 -0
  266. package/esm/prompt/index.d.mts +3 -0
  267. package/esm/prompt/index.mjs +4 -0
  268. package/esm/prompt/prompt-langfuse-sync.mjs +104 -0
  269. package/esm/prompt/prompt-langfuse-sync.mjs.map +1 -0
  270. package/esm/prompt/prompt-langfuse-sync.type.d.mts +32 -0
  271. package/esm/prompt/prompt-langfuse-sync.type.d.mts.map +1 -0
  272. package/esm/prompt/prompt-validate.mjs +170 -0
  273. package/esm/prompt/prompt-validate.mjs.map +1 -0
  274. package/esm/prompt/prompt.d.mts +54 -0
  275. package/esm/prompt/prompt.d.mts.map +1 -0
  276. package/esm/prompt/prompt.mjs +218 -0
  277. package/esm/prompt/prompt.mjs.map +1 -0
  278. package/esm/prompt/prompt.type.d.mts +174 -0
  279. package/esm/prompt/prompt.type.d.mts.map +1 -0
  280. package/esm/prompts/index.d.mts +3 -0
  281. package/esm/prompts/index.mjs +3 -0
  282. package/esm/prompts/prompts-manager.contract.d.mts +154 -0
  283. package/esm/prompts/prompts-manager.contract.d.mts.map +1 -0
  284. package/esm/prompts/prompts-manager.d.mts +38 -0
  285. package/esm/prompts/prompts-manager.d.mts.map +1 -0
  286. package/esm/prompts/prompts-manager.mjs +410 -0
  287. package/esm/prompts/prompts-manager.mjs.map +1 -0
  288. package/esm/prompts/prompts-manager.type.d.mts +172 -0
  289. package/esm/prompts/prompts-manager.type.d.mts.map +1 -0
  290. package/esm/prompts/prompts-validate.mjs +200 -0
  291. package/esm/prompts/prompts-validate.mjs.map +1 -0
  292. package/esm/rag/as-tool.mjs +48 -0
  293. package/esm/rag/as-tool.mjs.map +1 -0
  294. package/esm/rag/chunk/chunk.d.mts +24 -0
  295. package/esm/rag/chunk/chunk.d.mts.map +1 -0
  296. package/esm/rag/chunk/chunk.mjs +44 -0
  297. package/esm/rag/chunk/chunk.mjs.map +1 -0
  298. package/esm/rag/chunk/fixed.mjs +32 -0
  299. package/esm/rag/chunk/fixed.mjs.map +1 -0
  300. package/esm/rag/chunk/markdown.mjs +75 -0
  301. package/esm/rag/chunk/markdown.mjs.map +1 -0
  302. package/esm/rag/chunk/recursive.mjs +132 -0
  303. package/esm/rag/chunk/recursive.mjs.map +1 -0
  304. package/esm/rag/chunk/sentence.mjs +73 -0
  305. package/esm/rag/chunk/sentence.mjs.map +1 -0
  306. package/esm/rag/contracts/chunk-options.type.d.mts +35 -0
  307. package/esm/rag/contracts/chunk-options.type.d.mts.map +1 -0
  308. package/esm/rag/contracts/citation.type.d.mts +35 -0
  309. package/esm/rag/contracts/citation.type.d.mts.map +1 -0
  310. package/esm/rag/contracts/index.d.mts +4 -0
  311. package/esm/rag/contracts/rag-config.type.d.mts +68 -0
  312. package/esm/rag/contracts/rag-config.type.d.mts.map +1 -0
  313. package/esm/rag/contracts/rag-document.type.d.mts +21 -0
  314. package/esm/rag/contracts/rag-document.type.d.mts.map +1 -0
  315. package/esm/rag/hybrid/bm25.d.mts +23 -0
  316. package/esm/rag/hybrid/bm25.d.mts.map +1 -0
  317. package/esm/rag/hybrid/bm25.mjs +51 -0
  318. package/esm/rag/hybrid/bm25.mjs.map +1 -0
  319. package/esm/rag/hybrid/hybrid-rank.d.mts +33 -0
  320. package/esm/rag/hybrid/hybrid-rank.d.mts.map +1 -0
  321. package/esm/rag/hybrid/hybrid-rank.mjs +29 -0
  322. package/esm/rag/hybrid/hybrid-rank.mjs.map +1 -0
  323. package/esm/rag/hybrid/rrf.d.mts +25 -0
  324. package/esm/rag/hybrid/rrf.d.mts.map +1 -0
  325. package/esm/rag/hybrid/rrf.mjs +30 -0
  326. package/esm/rag/hybrid/rrf.mjs.map +1 -0
  327. package/esm/rag/index.d.mts +15 -0
  328. package/esm/rag/index.mjs +11 -0
  329. package/esm/rag/rag.d.mts +38 -0
  330. package/esm/rag/rag.d.mts.map +1 -0
  331. package/esm/rag/rag.mjs +126 -0
  332. package/esm/rag/rag.mjs.map +1 -0
  333. package/esm/rag/rerank/keyword-reranker.d.mts +32 -0
  334. package/esm/rag/rerank/keyword-reranker.d.mts.map +1 -0
  335. package/esm/rag/rerank/keyword-reranker.mjs +58 -0
  336. package/esm/rag/rerank/keyword-reranker.mjs.map +1 -0
  337. package/esm/rag/rerank/llm-reranker.d.mts +36 -0
  338. package/esm/rag/rerank/llm-reranker.d.mts.map +1 -0
  339. package/esm/rag/rerank/llm-reranker.mjs +85 -0
  340. package/esm/rag/rerank/llm-reranker.mjs.map +1 -0
  341. package/esm/rag/rerank/reranker.contract.d.mts +28 -0
  342. package/esm/rag/rerank/reranker.contract.d.mts.map +1 -0
  343. package/esm/rag/retrieve.mjs +68 -0
  344. package/esm/rag/retrieve.mjs.map +1 -0
  345. package/esm/rag/store/cache-vector-store.d.mts +27 -0
  346. package/esm/rag/store/cache-vector-store.d.mts.map +1 -0
  347. package/esm/rag/store/cache-vector-store.mjs +48 -0
  348. package/esm/rag/store/cache-vector-store.mjs.map +1 -0
  349. package/esm/rag/store/vector-store.contract.d.mts +38 -0
  350. package/esm/rag/store/vector-store.contract.d.mts.map +1 -0
  351. package/esm/rag/transforms/multi-query.d.mts +27 -0
  352. package/esm/rag/transforms/multi-query.d.mts.map +1 -0
  353. package/esm/rag/transforms/multi-query.mjs +41 -0
  354. package/esm/rag/transforms/multi-query.mjs.map +1 -0
  355. package/esm/security/index.mjs +5 -0
  356. package/esm/security/outbound-policy.d.mts +46 -0
  357. package/esm/security/outbound-policy.d.mts.map +1 -0
  358. package/esm/security/outbound-policy.mjs +187 -0
  359. package/esm/security/outbound-policy.mjs.map +1 -0
  360. package/esm/security/outbound-policy.type.d.mts +74 -0
  361. package/esm/security/outbound-policy.type.d.mts.map +1 -0
  362. package/esm/security/private-ip.d.mts +15 -0
  363. package/esm/security/private-ip.d.mts.map +1 -0
  364. package/esm/security/private-ip.mjs +48 -0
  365. package/esm/security/private-ip.mjs.map +1 -0
  366. package/esm/security/redact.d.mts +59 -0
  367. package/esm/security/redact.d.mts.map +1 -0
  368. package/esm/security/redact.mjs +122 -0
  369. package/esm/security/redact.mjs.map +1 -0
  370. package/esm/serve/serve.d.mts +50 -0
  371. package/esm/serve/serve.d.mts.map +1 -0
  372. package/esm/serve/serve.mjs +90 -0
  373. package/esm/serve/serve.mjs.map +1 -0
  374. package/esm/serve/sse.d.mts +20 -0
  375. package/esm/serve/sse.d.mts.map +1 -0
  376. package/esm/serve/sse.mjs +25 -0
  377. package/esm/serve/sse.mjs.map +1 -0
  378. package/esm/serve/stream-to-sse.d.mts +29 -0
  379. package/esm/serve/stream-to-sse.d.mts.map +1 -0
  380. package/esm/serve/stream-to-sse.mjs +37 -0
  381. package/esm/serve/stream-to-sse.mjs.map +1 -0
  382. package/esm/skills/catalog.d.mts +49 -0
  383. package/esm/skills/catalog.d.mts.map +1 -0
  384. package/esm/skills/catalog.mjs +140 -0
  385. package/esm/skills/catalog.mjs.map +1 -0
  386. package/esm/skills/contracts/skill-record.type.d.mts +37 -0
  387. package/esm/skills/contracts/skill-record.type.d.mts.map +1 -0
  388. package/esm/skills/contracts/skills-config.type.d.mts +108 -0
  389. package/esm/skills/contracts/skills-config.type.d.mts.map +1 -0
  390. package/esm/skills/contracts/skills-store.contract.d.mts +28 -0
  391. package/esm/skills/contracts/skills-store.contract.d.mts.map +1 -0
  392. package/esm/skills/contracts/skills.contract.d.mts +43 -0
  393. package/esm/skills/contracts/skills.contract.d.mts.map +1 -0
  394. package/esm/skills/index.d.mts +16 -0
  395. package/esm/skills/index.mjs +14 -0
  396. package/esm/skills/load-skill-tool.d.mts +38 -0
  397. package/esm/skills/load-skill-tool.d.mts.map +1 -0
  398. package/esm/skills/load-skill-tool.mjs +65 -0
  399. package/esm/skills/load-skill-tool.mjs.map +1 -0
  400. package/esm/skills/review-gate.d.mts +33 -0
  401. package/esm/skills/review-gate.d.mts.map +1 -0
  402. package/esm/skills/review-gate.mjs +60 -0
  403. package/esm/skills/review-gate.mjs.map +1 -0
  404. package/esm/skills/save-skill-tool.d.mts +39 -0
  405. package/esm/skills/save-skill-tool.d.mts.map +1 -0
  406. package/esm/skills/save-skill-tool.mjs +65 -0
  407. package/esm/skills/save-skill-tool.mjs.map +1 -0
  408. package/esm/skills/skills.d.mts +33 -0
  409. package/esm/skills/skills.d.mts.map +1 -0
  410. package/esm/skills/skills.mjs +109 -0
  411. package/esm/skills/skills.mjs.map +1 -0
  412. package/esm/skills/sources/directory-source.d.mts +19 -0
  413. package/esm/skills/sources/directory-source.d.mts.map +1 -0
  414. package/esm/skills/sources/directory-source.mjs +108 -0
  415. package/esm/skills/sources/directory-source.mjs.map +1 -0
  416. package/esm/skills/sources/index.d.mts +18 -0
  417. package/esm/skills/sources/index.d.mts.map +1 -0
  418. package/esm/skills/sources/index.mjs +27 -0
  419. package/esm/skills/sources/index.mjs.map +1 -0
  420. package/esm/skills/sources/parse-frontmatter.d.mts +27 -0
  421. package/esm/skills/sources/parse-frontmatter.d.mts.map +1 -0
  422. package/esm/skills/sources/parse-frontmatter.mjs +46 -0
  423. package/esm/skills/sources/parse-frontmatter.mjs.map +1 -0
  424. package/esm/skills/sources/store-source.d.mts +14 -0
  425. package/esm/skills/sources/store-source.d.mts.map +1 -0
  426. package/esm/skills/sources/store-source.mjs +15 -0
  427. package/esm/skills/sources/store-source.mjs.map +1 -0
  428. package/esm/skills/sources/url-source.d.mts +29 -0
  429. package/esm/skills/sources/url-source.d.mts.map +1 -0
  430. package/esm/skills/sources/url-source.mjs +117 -0
  431. package/esm/skills/sources/url-source.mjs.map +1 -0
  432. package/esm/skills/store/mock-skills-store.d.mts +57 -0
  433. package/esm/skills/store/mock-skills-store.d.mts.map +1 -0
  434. package/esm/skills/store/mock-skills-store.mjs +100 -0
  435. package/esm/skills/store/mock-skills-store.mjs.map +1 -0
  436. package/esm/skills/store/procedural-skill-store.d.mts +30 -0
  437. package/esm/skills/store/procedural-skill-store.d.mts.map +1 -0
  438. package/esm/skills/store/procedural-skill-store.mjs +125 -0
  439. package/esm/skills/store/procedural-skill-store.mjs.map +1 -0
  440. package/esm/supervisor/as-tool.mjs +2 -2
  441. package/esm/supervisor/as-tool.mjs.map +1 -1
  442. package/esm/supervisor/execution.d.mts.map +1 -1
  443. package/esm/supervisor/execution.mjs +31 -28
  444. package/esm/supervisor/execution.mjs.map +1 -1
  445. package/esm/supervisor/supervisor.d.mts.map +1 -1
  446. package/esm/supervisor/supervisor.mjs +8 -3
  447. package/esm/supervisor/supervisor.mjs.map +1 -1
  448. package/esm/system-prompt/index.d.mts +4 -0
  449. package/esm/system-prompt/system-prompt.d.mts +68 -4
  450. package/esm/system-prompt/system-prompt.d.mts.map +1 -1
  451. package/esm/system-prompt/system-prompt.mjs +89 -5
  452. package/esm/system-prompt/system-prompt.mjs.map +1 -1
  453. package/esm/team/gates.mjs +48 -0
  454. package/esm/team/gates.mjs.map +1 -0
  455. package/esm/team/index.d.mts +1 -0
  456. package/esm/team/index.mjs +3 -0
  457. package/esm/team/team.d.mts +42 -0
  458. package/esm/team/team.d.mts.map +1 -0
  459. package/esm/team/team.mjs +94 -0
  460. package/esm/team/team.mjs.map +1 -0
  461. package/esm/tool/executable-as-tool.d.mts.map +1 -1
  462. package/esm/tool/executable-as-tool.mjs +2 -2
  463. package/esm/tool/executable-as-tool.mjs.map +1 -1
  464. package/esm/tool/tool.d.mts.map +1 -1
  465. package/esm/tool/tool.mjs +2 -2
  466. package/esm/tool/tool.mjs.map +1 -1
  467. package/esm/utils/compute-cost.d.mts +17 -1
  468. package/esm/utils/compute-cost.d.mts.map +1 -1
  469. package/esm/utils/compute-cost.mjs +26 -1
  470. package/esm/utils/compute-cost.mjs.map +1 -1
  471. package/esm/utils/extract-json-lenient.d.mts +42 -0
  472. package/esm/utils/extract-json-lenient.d.mts.map +1 -0
  473. package/esm/utils/extract-json-lenient.mjs +97 -0
  474. package/esm/utils/extract-json-lenient.mjs.map +1 -0
  475. package/esm/utils/index.d.mts +4 -2
  476. package/esm/utils/index.mjs +3 -1
  477. package/esm/utils/json-schema.d.mts +1 -1
  478. package/esm/utils/prepare-attachment-part.d.mts +10 -1
  479. package/esm/utils/prepare-attachment-part.d.mts.map +1 -1
  480. package/esm/utils/prepare-attachment-part.mjs +103 -11
  481. package/esm/utils/prepare-attachment-part.mjs.map +1 -1
  482. package/esm/utils/resolve-attachment.d.mts +4 -3
  483. package/esm/utils/resolve-attachment.d.mts.map +1 -1
  484. package/esm/utils/resolve-attachment.mjs +4 -3
  485. package/esm/utils/resolve-attachment.mjs.map +1 -1
  486. package/esm/utils/run-context.d.mts +94 -0
  487. package/esm/utils/run-context.d.mts.map +1 -0
  488. package/esm/utils/run-context.mjs +98 -0
  489. package/esm/utils/run-context.mjs.map +1 -0
  490. package/esm/vcr/cassette-io.mjs +57 -0
  491. package/esm/vcr/cassette-io.mjs.map +1 -0
  492. package/esm/vcr/errors.d.mts +42 -0
  493. package/esm/vcr/errors.d.mts.map +1 -0
  494. package/esm/vcr/errors.mjs +37 -0
  495. package/esm/vcr/errors.mjs.map +1 -0
  496. package/esm/vcr/hash-request.d.mts +28 -0
  497. package/esm/vcr/hash-request.d.mts.map +1 -0
  498. package/esm/vcr/hash-request.mjs +118 -0
  499. package/esm/vcr/hash-request.mjs.map +1 -0
  500. package/esm/vcr/index.d.mts +4 -0
  501. package/esm/vcr/index.mjs +5 -0
  502. package/esm/vcr/vcr.d.mts +32 -0
  503. package/esm/vcr/vcr.d.mts.map +1 -0
  504. package/esm/vcr/vcr.mjs +248 -0
  505. package/esm/vcr/vcr.mjs.map +1 -0
  506. package/esm/vcr/vcr.type.d.mts +118 -0
  507. package/esm/vcr/vcr.type.d.mts.map +1 -0
  508. package/esm/workflow/as-tool.mjs +2 -2
  509. package/esm/workflow/as-tool.mjs.map +1 -1
  510. package/esm/workflow/engine.mjs +1 -0
  511. package/esm/workflow/engine.mjs.map +1 -1
  512. package/esm/workflow/step-runner.mjs +19 -20
  513. package/esm/workflow/step-runner.mjs.map +1 -1
  514. package/esm/workflow/workflow.d.mts.map +1 -1
  515. package/esm/workflow/workflow.mjs +8 -3
  516. package/esm/workflow/workflow.mjs.map +1 -1
  517. package/llms-full.txt +1683 -68
  518. package/llms.txt +16 -4
  519. package/package.json +7 -3
  520. package/skills/README.md +40 -4
  521. package/skills/ai-dx-helpers/SKILL.md +2 -2
  522. package/skills/approve-tool-calls/SKILL.md +134 -0
  523. package/skills/attach-ai-middleware/SKILL.md +1 -1
  524. package/skills/detect-and-redact-pii/SKILL.md +104 -0
  525. package/skills/durable-resume/SKILL.md +128 -0
  526. package/skills/escalate-block-to-human/SKILL.md +85 -0
  527. package/skills/eval-datasets-and-ci/SKILL.md +117 -0
  528. package/skills/guard-input-output/SKILL.md +117 -0
  529. package/skills/manage-prompts/SKILL.md +186 -0
  530. package/skills/observe-ai-flows/SKILL.md +94 -0
  531. package/skills/record-replay-llm/SKILL.md +92 -0
  532. package/skills/run-ai-agent/SKILL.md +26 -1
  533. package/skills/run-ai-rag/SKILL.md +139 -0
  534. package/skills/run-ai-team/SKILL.md +107 -0
  535. package/skills/run-orchestrator/SKILL.md +2 -0
  536. package/skills/run-planner/SKILL.md +73 -8
  537. package/skills/run-supervisor/SKILL.md +20 -1
  538. package/skills/use-runtime-skills/SKILL.md +106 -0
  539. package/skills/write-system-prompt/SKILL.md +30 -1
  540. package/cjs/index.cjs.map +0 -1
  541. package/esm/contracts/result/index.d.mts +0 -15
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: durable-resume
3
+ description: 'Persist a gated tool call and resume it from another process hours later — ships in @warlock.js/ai core: `ai.human.resume(interruptId, decision, options)`, the `InterruptStore` (`ai.human.interrupt.{memory,pg,redis}()`), `PendingInterrupt`, and the `InterruptSuspendedError` suspend sentinel. Triggers: `ai.human.resume`, `resume(interruptId, decision)`, `InterruptStore`, `ai.human.interrupt.memory`, `ai.human.interrupt.pg`, `ai.human.interrupt.redis`, `interruptMemory`, `interruptPg`, `interruptRedis`, `PendingInterrupt`, `InterruptSuspendedError`, `ResumeOptions`, `ResumeResult`, `PgClientLike`, `RedisClientLike`; ''approve hours later from a webhook'', ''persist the approval request and resume in another process'', ''durable human-in-the-loop'', ''store the interrupt in Postgres/Redis'', ''re-run the agent turn once the human approves''. Typical import `import { ai, InterruptSuspendedError } from "@warlock.js/ai"`. Skip: the in-process await gate and the policy/decision shapes — `@warlock.js/ai/approve-tool-calls/SKILL.md`.'
4
+ ---
5
+
6
+ # Durable resume — persist the interrupt, approve from another process
7
+
8
+ Interactive approval `await`s the operator in-process. **Durable** approval is for when the reviewer rules out-of-band — a Slack button, a webhook, hours later, in a different process. The flow: the handler **persists** the request to an `InterruptStore` and **throws** `InterruptSuspendedError` to suspend the run; the caller surfaces the `interruptId`; later, `ai.human.resume(interruptId, decision, { store })` applies the ruling.
9
+
10
+ > **v1 durable resume re-runs the turn** with the decision pre-seeded — it does **not** rehydrate an in-flight supervisor mid-call (that's the deferred v2 lift). Re-running is idempotent because the prompt and the seeded decision fully determine the gated call's outcome.
11
+
12
+ ## Process A — suspend and surface the id
13
+
14
+ ```ts
15
+ import { ai, InterruptSuspendedError } from "@warlock.js/ai";
16
+
17
+ const store = ai.human.interrupt.memory(); // swap for pg / redis in production
18
+
19
+ const agent = ai.agent({
20
+ model,
21
+ tools: [deleteAccount],
22
+ middleware: [
23
+ ai.human.approval({
24
+ policy: { type: "predicate", requiresApproval: (c) => c.toolName === "deleteAccount" },
25
+ store,
26
+ handler: async (req) => {
27
+ // 1. persist the pending interrupt
28
+ await store.save({
29
+ interruptId: req.interruptId,
30
+ request: req,
31
+ status: "pending",
32
+ savedAt: new Date().toISOString(),
33
+ });
34
+ // 2. notify the reviewer out-of-band
35
+ await slack.postApproval(req);
36
+ // 3. suspend the run — the middleware recognizes its OWN sentinel
37
+ throw new InterruptSuspendedError("Awaiting human approval", {
38
+ interruptId: req.interruptId,
39
+ });
40
+ },
41
+ }),
42
+ ],
43
+ });
44
+
45
+ const result = await agent.execute("Delete account #88");
46
+
47
+ // execute() never throws — the suspend rides result.error:
48
+ if (result.error instanceof InterruptSuspendedError) {
49
+ return { status: "awaiting-approval", interruptId: result.error.interruptId };
50
+ }
51
+ ```
52
+
53
+ The middleware catches the **sentinel** (`instanceof InterruptSuspendedError`) and short-circuits a failed `ToolInvokeResult` carrying it, so `error.interruptId` is on `result.error`. Hand that id to the reviewer.
54
+
55
+ ## Process B — resume hours later
56
+
57
+ ```ts
58
+ import { ai } from "@warlock.js/ai";
59
+
60
+ // Re-run the turn with the decision pre-seeded:
61
+ const outcome = await ai.human.resume(
62
+ interruptId,
63
+ { type: "edit", args: { confirm: true } },
64
+ { store, agent },
65
+ );
66
+
67
+ if (outcome.type === "applied" && outcome.result) {
68
+ console.log(outcome.result.text); // the re-run completed with the ruling applied
69
+ }
70
+ ```
71
+
72
+ `ai.human.resume(interruptId, decision, options)` loads the `PendingInterrupt`, validates the decision shape, deletes the record, and — when an `agent` is supplied — re-executes the original prompt with the decision **pre-seeded** so the gated tool call resolves to the ruling instead of pausing again. The prompt comes from `request.context.originalInput`; pass `options.input` to override (e.g. to append the reviewer's note), and `options.executeOptions` to forward history / output schema / signal to the re-run.
73
+
74
+ ### Two resume shapes
75
+
76
+ | Shape | Pass | Behavior |
77
+ |---|---|---|
78
+ | **re-run** | `{ store, agent }` | Loads, deletes, re-executes the turn; `ResultResult.result` carries the `AgentResult`. |
79
+ | **apply-only** | `{ store }` (no `agent`) | Loads, validates, deletes; returns `{ type: "applied", decision }` for a caller-owned re-drive (custom transport). No turn re-run. |
80
+
81
+ ### Idempotent by construction
82
+
83
+ ```ts
84
+ type ResumeResult =
85
+ | { type: "applied"; interruptId: string; decision: ApprovalDecision; result?: AgentResult }
86
+ | { type: "already-resolved"; interruptId: string };
87
+ ```
88
+
89
+ A second resume of an already-resolved (deleted) or never-raised interrupt returns `{ type: "already-resolved" }` — it never double-applies the decision or re-runs the turn. The record is deleted **before** the re-run, so even a re-run that itself raises a fresh interrupt can't collide with the one being resolved. A malformed decision (`reject` with no `reason`, `edit` with no `args`, an unknown `type`) throws a `TypeError` loudly rather than silently mis-driving the re-run.
90
+
91
+ ## The `InterruptStore`
92
+
93
+ `ai.human.interrupt.{memory,pg,redis}()` build the store. The contract mirrors `@warlock.js/ai`'s `CheckpointStore` / `SnapshotStore` — `save` / `load` / `delete` / optional `list(prefix?)` / `schema()` — so a consumer already running an orchestrator can reuse the **same** pool for the interrupt table.
94
+
95
+ | Factory | Backing | Deps |
96
+ |---|---|---|
97
+ | `ai.human.interrupt.memory()` | process-local `Map` | none — zero runtime deps |
98
+ | `ai.human.interrupt.pg(options)` | one Postgres row per interrupt, keyed by `interrupt_id` | lazily imports the optional `pg` peer |
99
+ | `ai.human.interrupt.redis(options)` | one namespaced JSON value + a self-maintained id index | lazily imports the optional `redis` peer |
100
+
101
+ ```ts
102
+ // Memory — dev / tests / single-process:
103
+ const store = ai.human.interrupt.memory();
104
+
105
+ // Postgres — pass a live pool (core never imports pg in that case):
106
+ import { Pool } from "pg";
107
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
108
+ const store = ai.human.interrupt.pg({ client: pool });
109
+ // Once, via your migration tool — the framework never auto-migrates:
110
+ // await pool.query(store.schema());
111
+
112
+ // …or let the store build its own pool (lazily import("pg")):
113
+ const store = ai.human.interrupt.pg({ connectionString: process.env.DATABASE_URL });
114
+
115
+ // Redis — pass a connected client, or a url:
116
+ const store = ai.human.interrupt.redis({ url: process.env.REDIS_URL });
117
+ ```
118
+
119
+ ### Optional peers are lazy
120
+
121
+ `pg` and `redis` are **optional** peer dependencies — neither is a hard dependency. The driver is imported only inside the store that needs it, and only when you pass a `connectionString` / `url` (passing a live `client` imports nothing). If the driver is absent, a **curated install string** surfaces on first use, never a raw module-resolution stack trace at import — so a memory-only consumer always loads cleanly. `PgClientLike` / `RedisClientLike` are structural interfaces, so any compatible pool/client satisfies them.
122
+
123
+ `schema()` returns the reference DDL for the Postgres store (run it through your migration tool once) and an empty string for memory / redis, so callers treat `schema()` uniformly across drivers.
124
+
125
+ ## See also
126
+
127
+ - [`@warlock.js/ai/approve-tool-calls/SKILL.md`](@warlock.js/ai/approve-tool-calls/SKILL.md) — the gate itself: the interrupt policy, the approve / reject / edit decision union, and the interactive (in-process await) handler.
128
+ - `@warlock.js/ai` — the `CheckpointStore` / `SnapshotStore` the `InterruptStore` mirrors, and the `ai.agent(...)` re-run target.
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: escalate-block-to-human
3
+ description: 'Route a hard guardrail block to a human-review surface with @warlock.js/ai-guard — the `escalation.onBlock` seam and an `escalate: true` verdict. Triggers: `escalation`, `onBlock`, `GuardrailEscalation`, `GuardrailBlockEvent`, `escalate: true`, `{ type: "block", escalate: true }`, ''escalate a block to a human'', ''human review queue for guardrail'', ''page an operator on a guardrail block'', ''human-in-the-loop guardrail'', ''compose a block with a review surface'', ''custom detector that escalates''; typical import `import "@warlock.js/ai-guard"` then `ai.guardrail({ escalation: { onBlock } })`. Skip: composing the guard / phases / verdict model — `@warlock.js/ai-guard/guard-input-output/SKILL.md`; PII/moderation detectors — `@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md`; durable suspend/resume human-step machinery (deferred) — not in this package.'
4
+ ---
5
+
6
+ # Escalate a block to a human
7
+
8
+ A `block` verdict can carry `escalate: true`. When it does, the guard `await`s your `escalation.onBlock(...)` handler **before** throwing the `GuardrailViolationError` — the seam to a human-review queue, an operator page, or any out-of-band approval surface.
9
+
10
+ ```ts
11
+ import { ai } from "@warlock.js/ai";
12
+ import "@warlock.js/ai-guard";
13
+
14
+ const policy = ai.guardrail({
15
+ output: [ai.guardrail.moderation({ blockOn: ["self-harm"] })],
16
+ escalation: {
17
+ async onBlock(event) {
18
+ await reviewQueue.enqueue({
19
+ phase: event.phase, // "input" | "output" | "tool"
20
+ reason: event.reason, // the detector's human-readable reason
21
+ });
22
+ },
23
+ },
24
+ });
25
+
26
+ const agent = ai.agent({ model, middleware: [policy] });
27
+ ```
28
+
29
+ ## When `onBlock` fires
30
+
31
+ `onBlock` fires **only** for a verdict of `{ type: "block", escalate: true }` — not for an ordinary `block`, and never for `allow` / `redact` / `flag`. It is **awaited before** the `GuardrailViolationError` is thrown, so your handler runs to completion (enqueue succeeds, the page is sent) before the error surfaces on `result.error`. The run still aborts: escalation is a *signal*, not a recovery — `execute()` returns with `result.error` populated as usual.
32
+
33
+ ## The `GuardrailBlockEvent` payload
34
+
35
+ `onBlock(event)` receives:
36
+
37
+ | Field | Type | Meaning |
38
+ |---|---|---|
39
+ | `phase` | `"input" \| "output" \| "tool"` | where the block fired |
40
+ | `reason` | `string` | the detector's human-readable reason |
41
+ | `matches` | `readonly GuardrailMatch[] \| undefined` | what tripped the rule (rule id, span, label), when reported |
42
+ | `ctx` | `MiddlewareTripContext` | the live trip context — `state`, `messages`, `agent`, `model`, `signal` |
43
+
44
+ `ctx` lets the handler enrich the review item with run context (session id from `ctx.state`, the offending messages, etc.).
45
+
46
+ ## Producing an escalating verdict
47
+
48
+ The built-in detectors return ordinary `block` verdicts (no `escalate`). To escalate, author a tiny custom `GuardrailDetector` that sets `escalate: true` on its `block`:
49
+
50
+ ```ts
51
+ import type { GuardrailDetector } from "@warlock.js/ai-guard";
52
+
53
+ const wirePolicy: GuardrailDetector = {
54
+ name: "wire-transfer",
55
+ check(text) {
56
+ if (/wire \$?\d{5,}/i.test(text)) {
57
+ return {
58
+ type: "block",
59
+ reason: "large wire transfer requires human approval",
60
+ escalate: true, // <- routes through escalation.onBlock
61
+ matches: [{ rule: "wire-transfer.large", label: "wire" }],
62
+ };
63
+ }
64
+
65
+ return { type: "allow" };
66
+ },
67
+ };
68
+
69
+ const policy = ai.guardrail({
70
+ tool: [wirePolicy],
71
+ toolNames: ["initiate_transfer"],
72
+ escalation: { async onBlock(e) { await approvals.request(e); } },
73
+ });
74
+ ```
75
+
76
+ A `check()` may be sync or async (async = call an external service); the guard awaits either.
77
+
78
+ ## A plain callback by design
79
+
80
+ `escalation.onBlock` is a **plain callback** — `ai-guard` takes **no** dependency on the deferred durable human-step machinery (suspend/resume). The callback is the decoupling seam: inside it you wire your own review queue, and (where your stack supports it) a `workflow.resume(...)` loop. This package only emits the *signal*; it does not own durable suspension. When the typed human-step handoff ships, `onBlock` upgrades to it without a breaking change here.
81
+
82
+ ## See also
83
+
84
+ - [`@warlock.js/ai-guard/guard-input-output/SKILL.md`](@warlock.js/ai-guard/guard-input-output/SKILL.md) — composing the guard, the phases, the verdict model, and how a `block` surfaces on `result.error`.
85
+ - [`@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md`](@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md) — the `pii` detector and the optional `moderation` peer that commonly drives an escalation.
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: eval-datasets-and-ci
3
+ description: 'Datasets + regression-gated eval CI with ai.dataset({...}) feeding agent.eval({cases,baseline,tolerance}). Covers the immutable filterable/shardable dataset (cases / fromFile JSONL), DatasetEntry tags, EvalReport.regression (regressed/added/removed/passed) against a baseline, and the ai.eval reporters toJUnit / toJSON / fromJSON for CI artifacts + committed baselines. Triggers: `ai.dataset`, `DatasetContract`, `DatasetEntry`, `DatasetOptions`, `dataset.filter`, `dataset.shard`, `fromFile`, `agent.eval`, `EvalOptions`, `EvalReport`, `EvalCaseResult`, `EvalRegression`, `baseline`, `tolerance`, `ai.eval.toJUnit`, `ai.eval.toJSON`, `ai.eval.fromJSON`, `diff`, JSONL; ''eval dataset from a JSONL file'', ''shard an eval suite across CI jobs'', ''fail CI on an eval regression'', ''emit a JUnit report'', ''snapshot an eval baseline''; typical import `import { ai } from "@warlock.js/ai"`. Skip: the scorers + LLM-as-judge + Vitest matchers themselves — `@warlock.js/ai/ai-dx-helpers/SKILL.md` (registerAiMatchers / ai.eval.exact|contains|predicate|judge); record/replay of model calls for deterministic tests — `@warlock.js/ai/record-replay-llm/SKILL.md`; competing libs `promptfoo`, `braintrust`.'
4
+ ---
5
+
6
+ # `ai.dataset()` + `agent.eval()` regression CI
7
+
8
+ Turn a corpus of cases into a regression-gated CI signal. `ai.dataset(...)` wraps cases into an immutable, filterable, shardable collection; `agent.eval({ cases, baseline, tolerance })` runs them, scores them, and diffs against a prior report; the `ai.eval.*` reporters serialize the result for CI ingestion and tomorrow's baseline.
9
+
10
+ > This skill is the **dataset + CI** layer. The scorers, LLM-as-judge config, and Vitest matchers live in [`@warlock.js/ai/ai-dx-helpers/SKILL.md`](@warlock.js/ai/ai-dx-helpers/SKILL.md); `agent.eval`'s core scoring loop is in [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md).
11
+
12
+ ## `ai.dataset()` — immutable, filterable, shardable
13
+
14
+ ```ts
15
+ import { ai } from "@warlock.js/ai";
16
+
17
+ const ds = ai.dataset({
18
+ name: "support",
19
+ cases: [{ name: "greeting", input: "hi", expected: "Hello" }], // inline entries
20
+ fromFile: "./eval/support.jsonl", // JSONL read ONCE, synchronously, at construction
21
+ });
22
+
23
+ ds.name; // "support"
24
+ ds.cases; // DatasetEntry[] (inline first, then file entries appended)
25
+ ```
26
+
27
+ - A `DatasetEntry` is an `EvalCase` plus optional `tags?: string[]` for filtering / sharding.
28
+ - **`fromFile`** reads a JSONL file (one JSON object per line; blank lines skipped) synchronously at construction — mirroring `SystemPrompt.fromFile`. A malformed line throws an **`InvalidRequestError` naming the 1-based line number**; a missing/unreadable path throws too. `cases` and `fromFile` combine (file entries append after inline).
29
+
30
+ ### `filter` / `shard` — derive new datasets
31
+
32
+ ```ts
33
+ const smoke = ds.filter((entry) => entry.tags?.includes("smoke"));
34
+ const shard = ds.shard(0, 4); // first of four parallel CI shards
35
+ ```
36
+
37
+ Both return a **fresh dataset sharing nothing mutable**. `shard(index, total)` is deterministic round-robin by position: every entry lands in exactly one shard, so the union of all `total` shards reproduces the full list with no gaps or overlaps. `shard` validates its args (positive integer `total`, `index` in `[0, total)`) and throws `InvalidRequestError` otherwise.
38
+
39
+ ## `agent.eval({ cases })` — accepts a dataset directly
40
+
41
+ ```ts
42
+ const report = await myAgent.eval({
43
+ cases: ds, // a DatasetContract OR a raw EvalCase[]
44
+ scorers: [ai.eval.contains()],
45
+ });
46
+
47
+ expect(report.passed).toBe(true); // true only when EVERY case passed
48
+ ```
49
+
50
+ The runner reads `.cases` off a dataset. Full `EvalReport`: `{ agentName, total, passedCount, failedCount, passRate, meanScore, passed, cases, duration, regression? }`. Each `EvalCaseResult` carries the case, the full `AgentResult`, every scorer's `scores`, the mean `score`, `passed`, and `duration`.
51
+
52
+ ## Regression gating — `baseline` + `tolerance`
53
+
54
+ ```ts
55
+ import { readFile, writeFile } from "node:fs/promises";
56
+
57
+ const baseline = ai.eval.fromJSON(await readFile("./eval/baseline.json", "utf8"));
58
+
59
+ const report = await myAgent.eval({
60
+ cases: ds,
61
+ scorers: [ai.eval.exact()],
62
+ baseline, // a prior EvalReport to diff against
63
+ tolerance: 0.05, // max allowed per-case score DROP before it regresses. default 0 (any drop)
64
+ });
65
+
66
+ if (report.regression && !report.regression.passed) {
67
+ console.error("Regressed:", report.regression.regressed); // [{ name, before, after }]
68
+ process.exit(1);
69
+ }
70
+ ```
71
+
72
+ When `baseline` is set the report carries a `regression` block (`EvalRegression`), joining cases by `name`:
73
+
74
+ - **`regressed`** — `[{ name, before, after }]` for cases whose new score fell more than `tolerance` below baseline.
75
+ - **`added`** / **`removed`** — case names present in only one report. Adding or dropping a case **never fails the gate by itself**.
76
+ - **`passed`** — `true` when `regressed` is empty.
77
+
78
+ The pure `diff(report, baseline, tolerance)` function (exported as `diff`) is the same logic, decoupled from the runner — depends only on the two reports and the tolerance, mutates neither.
79
+
80
+ ## CI reporters — `ai.eval.toJUnit` / `toJSON` / `fromJSON`
81
+
82
+ Pure functions over a finished `EvalReport`:
83
+
84
+ ```ts
85
+ // JUnit-XML artifact for CI ingestion — one <testsuite> (the agent), one <testcase> per case,
86
+ // a <failure> on each non-passing case (joined scorer reasons), times in SECONDS.
87
+ await writeFile("./report.junit.xml", ai.eval.toJUnit(report));
88
+
89
+ // Round-trippable snapshot — today's report becomes tomorrow's baseline.
90
+ await writeFile("./eval/baseline.json", ai.eval.toJSON(report));
91
+ const restored = ai.eval.fromJSON(await readFile("./eval/baseline.json", "utf8"));
92
+ ```
93
+
94
+ `toJSON`/`fromJSON` preserve `result` payloads, per-case `scores`, timings, and any attached `regression` block, so a parsed report drives regression diffing exactly as the in-memory one. `toJUnit` hand-emits XML (no `xml` dependency) and entity-escapes every dynamic value.
95
+
96
+ ## Typical CI shard job
97
+
98
+ ```ts
99
+ const shard = ai.dataset({ name: "support", fromFile: "./eval/support.jsonl" })
100
+ .shard(Number(process.env.SHARD_INDEX), Number(process.env.SHARD_TOTAL));
101
+
102
+ const report = await agent.eval({
103
+ cases: shard,
104
+ scorers: [ai.eval.contains()],
105
+ baseline: ai.eval.fromJSON(await readFile("./eval/baseline.json", "utf8")),
106
+ tolerance: 0.05,
107
+ });
108
+
109
+ await writeFile(`./out/report-${process.env.SHARD_INDEX}.junit.xml`, ai.eval.toJUnit(report));
110
+ if (report.regression && !report.regression.passed) process.exit(1);
111
+ ```
112
+
113
+ ## See also
114
+
115
+ - [`@warlock.js/ai/ai-dx-helpers/SKILL.md`](@warlock.js/ai/ai-dx-helpers/SKILL.md) — `ai.eval.{exact,contains,predicate,judge}` scorers + Vitest matchers
116
+ - [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md) — `agent.eval` scoring loop, `EvalCase` / `EvalJudge`
117
+ - [`@warlock.js/ai/record-replay-llm/SKILL.md`](@warlock.js/ai/record-replay-llm/SKILL.md) — `ai.vcr` for deterministic, offline eval runs
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: guard-input-output
3
+ description: 'Build the composed guardrail middleware with @warlock.js/ai-guard and wire it into an agent — `ai.guardrail({ input, output, tool, toolNames, escalation })`. Triggers: `ai.guardrail`, `guard`, `GuardOptions`, `GuardrailVerdict`, `GuardrailDetector`, `GuardrailPhase`, `GuardrailMatch`, `GuardrailViolationError`, `ai.guardrail.topic`, `ai.guardrail.injection`, `topicFilter`, `injectionDetector`, `toolNames`, `forTool`; ''add a guardrail to my agent'', ''block prompt injection'', ''filter banned topics'', ''guard agent input and output'', ''stop the model leaking data into a tool call'', ''scope a detector to one tool''; typical import `import "@warlock.js/ai-guard"` (registers `ai.guardrail`) or `import { guard } from "@warlock.js/ai-guard"`. Skip: PII detection/redaction specifically — `@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md`; routing a block to a human — `@warlock.js/ai-guard/escalate-block-to-human/SKILL.md`; the core middleware pipeline / hook contract — `@warlock.js/ai/run-ai-agent/SKILL.md`.'
4
+ ---
5
+
6
+ # Guard agent input, output, and tool args
7
+
8
+ `ai.guardrail(...)` is a **middleware factory**. It produces one `AgentMiddleware` that runs your detectors at three hook points and maps each verdict onto the agent pipeline's existing throw / return / record mechanics. Importing the package registers the verb (and its attached detector factories) on the shared `ai` namespace:
9
+
10
+ ```ts
11
+ import { ai } from "@warlock.js/ai";
12
+ import "@warlock.js/ai-guard"; // registers ai.guardrail + ai.guardrail.pii/.topic/.injection/.moderation
13
+
14
+ const policy = ai.guardrail({
15
+ name: "compliance",
16
+ input: [ai.guardrail.injection({ onMatch: "block" })],
17
+ output: [ai.guardrail.topic({ deny: [/medical advice/i, "diagnosis"], onMatch: "block" })],
18
+ });
19
+
20
+ const agent = ai.agent({ model, middleware: [policy] });
21
+ ```
22
+
23
+ A named-export form is available for callers who prefer not to rely on the augmented namespace:
24
+
25
+ ```ts
26
+ import { guard, topic, injection } from "@warlock.js/ai-guard";
27
+ const policy = guard({ input: [injection({ onMatch: "block" })] });
28
+ ```
29
+
30
+ ## The three phases
31
+
32
+ | Phase | Hook | Inspected text | Set with |
33
+ |---|---|---|---|
34
+ | **input** | `trip.before` | the outbound prompt (`extractUserText(ctx.messages)`) | `input: [...]` |
35
+ | **output** | `trip.after` | `response.content` | `output: [...]` |
36
+ | **tool** | `tool.before` | `JSON.stringify(toolArgs)` | `tool: [...]` |
37
+
38
+ Each phase array runs its detectors in **registration order**; the first non-`allow` verdict decides the action for that phase (short-circuit). A phase you don't configure is inert — a guard with no detectors is a no-op middleware.
39
+
40
+ ## The verdict model
41
+
42
+ A detector inspects text and returns a `GuardrailVerdict`, discriminated by `type` (never `kind`):
43
+
44
+ | `type` | Effect |
45
+ |---|---|
46
+ | `allow` | Pass to the next detector. |
47
+ | `redact` | Rewrite the inspected text and continue — **output phase only** (see limitation below). |
48
+ | `block` | Short-circuit with the existing `GuardrailViolationError`. |
49
+ | `flag` | Pass, but append a `FlagRecord` into `ctx.state` under `<name>.flags` for a downstream observer (panoptic, the caller). |
50
+
51
+ `agent.execute()` **never throws** — a `block` surfaces on `result.error` as a `GuardrailViolationError`, exactly like every other `AIError`. Branch on it after the run:
52
+
53
+ ```ts
54
+ const result = await agent.execute(userInput);
55
+
56
+ if (result.error instanceof ai.errors.GuardrailViolationError) {
57
+ // result.error.phase is "input" | "output" | "tool"
58
+ // result.error.reason / result.error.guardrail carry the detail
59
+ }
60
+ ```
61
+
62
+ ## Built-in detectors
63
+
64
+ Three zero-dependency detectors ship (a fourth, `moderation`, is an optional `openai` peer — see [`detect-and-redact-pii/SKILL.md`](@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md)):
65
+
66
+ - **`ai.guardrail.injection(options?)`** — jailbreak / prompt-injection marker phrases. Extra `markers` (string | RegExp); `onMatch` defaults to `"flag"`, callers commonly use `"block"` on input.
67
+ - **`ai.guardrail.topic(options)`** — `deny` (string substring | RegExp) and/or `allow` (allow-list miss triggers `onMatch`). `onMatch` is `"block" | "flag"`, default `"block"`.
68
+ - **`ai.guardrail.pii(options?)`** — PII regex + dictionary (its own skill).
69
+
70
+ ```ts
71
+ const policy = ai.guardrail({
72
+ input: [
73
+ ai.guardrail.injection({ onMatch: "block", markers: ["ignore previous instructions"] }),
74
+ ai.guardrail.topic({ deny: ["competitor-name"], onMatch: "block" }),
75
+ ],
76
+ });
77
+ ```
78
+
79
+ ## Scope tool detectors to specific tools
80
+
81
+ `tool` detectors fire on **every** tool call by default. Set `toolNames` to scope them — the whole middleware is wrapped with the core `forTool(toolNames, mw)` helper so the `tool` hooks fire only for those names; `input` / `output` (`trip`) hooks are unaffected:
82
+
83
+ ```ts
84
+ const policy = ai.guardrail({
85
+ tool: [ai.guardrail.pii({ onMatch: "block" })], // stop PII reaching the tool
86
+ toolNames: ["send_email", "post_webhook"], // ...only for these tools
87
+ });
88
+
89
+ const agent = ai.agent({ model, tools: [sendEmail, postWebhook, lookup], middleware: [policy] });
90
+ // `lookup` runs unguarded; `send_email` / `post_webhook` block on PII in their args.
91
+ ```
92
+
93
+ A `block` from `tool.before` aborts that tool dispatch and surfaces on `result.error` with `phase: "tool"` — the agent run itself still never crashes.
94
+
95
+ ## Install order
96
+
97
+ A guard is a normal `AgentMiddleware`; registration order is execution order (`before` top-down, `after` bottom-up). The canonical order is `[cache, budget, guardrail, observability]`. A `semanticCache` that short-circuits `trip.before` runs *before* the guard — a cached response then skips the **output** detectors, so place the guard before the cache if you don't trust cached contents.
98
+
99
+ ## Input-redaction limitation (v1)
100
+
101
+ The core `trip.before` hook can only **short-circuit** (return a `ModelResponse`); it cannot rewrite the outbound prompt and continue. So:
102
+
103
+ - **Input detectors are `block` / `flag` only.** A `redact` verdict on an input detector is treated as a `block` rather than silently passing an un-redacted prompt.
104
+ - **Output redaction works** — `trip.after` returns a replacement `ModelResponse` with the rewritten `content`.
105
+ - **Tool-arg `redact` is also withheld** — it downgrades to a `block` (`reason: "tool-arg-redaction-unsupported"`), because silently rewriting tool arguments changes the call's side-effects unpredictably.
106
+
107
+ Lifting the input limitation needs a small, non-breaking core affordance and is deferred.
108
+
109
+ ## Failure isolation
110
+
111
+ A detector's `check()` **rejecting** is an infrastructure fault, not a content violation — it is recorded as a `flag` (`<detector>.error`) into `ctx.state` and the fold **continues** (fail-open). A moderation-API outage degrades to missing annotation, never a failed agent run.
112
+
113
+ ## See also
114
+
115
+ - [`@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md`](@warlock.js/ai-guard/detect-and-redact-pii/SKILL.md) — the `pii` detector (detect/redact/block), the `mask` template, and the optional `moderation` peer.
116
+ - [`@warlock.js/ai-guard/escalate-block-to-human/SKILL.md`](@warlock.js/ai-guard/escalate-block-to-human/SKILL.md) — routing a `block` to a human-review surface via `escalation.onBlock`.
117
+ - [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md) — running the agent, the middleware pipeline, and the `GuardrailViolationError` on `result.error`.
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: manage-prompts
3
+ description: 'Unified prompt registry — ai.prompts: one process-wide store of named, versioned systemPrompt(...) builders keyed by name@version. Register by giving a prompt a meta.name (auto-registers), resolve by get(name) / resolve(name, versionOrTag, placeholders) / the inline name@selector form, bulk-register with define(name, versions), pin tags with tag(name, tag, version), compare with diff(name, from, to), round-trip with export() / import(snapshot), and quality-check with a unified validate(target, options) (deterministic missing-placeholder check + optional Nova-safe LLM-as-judge with verdict caching). Compose registered prompts into new ones with systemPrompt().merge(name, { fromVersion }) — provenance recorded in meta.composedFrom. ai.prompt is now a thin FACADE over ai.prompts (BREAKING vs the old standalone registry). Triggers: `ai.prompts`, `ai.prompt`, `PromptsManagerContract`, `PromptsManagerEntry`, `SystemPromptContract`, `SystemPromptMeta`, `SystemPromptMergeOptions`, `PromptsValidateOptions`, `PromptValidationResult`, `PromptValidateTarget`, `PromptTemplateVersion`, `PromptDiff`, `ExportedRegistry`, `defaultPromptsManager`, `prompts()`, `promptKey`, `meta`, `name`, `version`, `composedFrom`, `fromVersion`, `register`, `create`, `get`, `has`, `list`, `versions`, `resolve`, `define`, `tag`, `validate`, `diff`, `export`, `import`, `merge`, `judge`, `judgeCache`; ''register a prompt by name'', ''resolve a prompt by name@version or tag'', ''pin a production tag to a prompt version'', ''diff two prompt versions'', ''export / import the prompt registry'', ''validate a prompt for missing placeholders'', ''merge a registered prompt into another''; typical import `import { ai } from "@warlock.js/ai"`. Skip: composing a single prompt from persona + instruction blocks (the builder itself) — `@warlock.js/ai/write-system-prompt/SKILL.md`; runtime loadable skill bodies — `@warlock.js/ai/use-runtime-skills/SKILL.md`; eval scoring of agent outputs — `@warlock.js/ai/eval-datasets-and-ci/SKILL.md`; competing libs `langfuse` (direct), `promptfoo`.'
4
+ ---
5
+
6
+ # `ai.prompts` — the unified prompt registry
7
+
8
+ `ai.prompts` is ONE process-wide registry of named, versioned `systemPrompt(...)` builders keyed by `name@version`. A `systemPrompt(input, { name })` (or any `.meta({ name })` rename) auto-registers here; `ai.prompts.get(name)` / `.resolve(name)` read them back; `systemPrompt().merge(name)` folds a registered prompt into a new one. There is exactly **one storage shape** behind the whole prompt surface — a `SystemPromptContract` keyed by `name@version` — and `ai.prompt(...)` is now a thin facade over it (see the migration note below).
9
+
10
+ ```ts
11
+ import { ai } from "@warlock.js/ai";
12
+
13
+ // Register: any named systemPrompt auto-registers in ai.prompts.
14
+ ai.systemPrompt("You are support for {{product}}.", { name: "support" });
15
+
16
+ // Resolve back — latest version, or a version / pinned tag.
17
+ ai.prompts.get("support"); // → the SystemPromptContract
18
+ ai.prompts.resolve("support", undefined, { product: "Warlock" }); // → final string
19
+ ```
20
+
21
+ `ai.prompts` is the process-wide default (`defaultPromptsManager()`). For an **isolated** registry (parallel test suites, multi-tenant apps) call the `prompts()` factory — same `PromptsManagerContract`, its own store, no global side effects.
22
+
23
+ ## Identity — `SystemPromptMeta` (`meta.name` / `version` / `description` / `required` / `composedFrom`)
24
+
25
+ A prompt's identity rides on its `meta`. Read it with the no-argument accessor; update it immutably with the one-argument form:
26
+
27
+ ```ts
28
+ const base = ai.systemPrompt("You are support.", {
29
+ name: "support",
30
+ version: "1",
31
+ description: "Tier-1 support persona.",
32
+ required: ["product"],
33
+ });
34
+
35
+ base.meta(); // → { name: "support", version: "1", description, required }
36
+ const v2 = base.meta({ version: "2" }); // new builder, shallow-merged meta; original untouched
37
+ ```
38
+
39
+ - **`name`** — when present, the prompt auto-registers in `ai.prompts` under `name@version`. Anonymous prompts (no `name`) are never registered.
40
+ - **`version`** — free-form label (`"1"`, `"2025-draft"`). Defaults to the **next integer** for that name when omitted.
41
+ - **`description`** — human-readable purpose (carried through `export`).
42
+ - **`required`** — placeholder keys callers must supply; `validate()` reads them.
43
+ - **`composedFrom`** — deterministic source labels a prompt was merged from (e.g. `["base@2", "global@1"]`). No random suffixes — the same merge always yields the same labels.
44
+
45
+ ## Register / resolve — `register` / `get` / `resolve` / `has` / `list` / `versions`
46
+
47
+ ```ts
48
+ const registry = ai.prompts; // or prompts() for an isolated one
49
+
50
+ registry.register(ai.systemPrompt("You are support.", { name: "support" }));
51
+ registry.versions("support"); // ["1"] — version derived as next integer
52
+
53
+ registry.get("support"); // latest SystemPromptContract
54
+ registry.get("support@1"); // inline name@selector
55
+ registry.resolve("support", "1", { product: "Warlock" }); // pick version + render in one call
56
+
57
+ registry.has("support"); // boolean
58
+ registry.list(); // every registered name, first-seen order
59
+ ```
60
+
61
+ - **Version selection** — `get(name)` / `resolve(name)` return the **latest** by insertion order; pass a version label, a pinned tag, or fold it into the first arg as `name@selector` (`get("support@1")`, `resolve("support@production")`).
62
+ - **Duplicates** — re-registering the same `name@version` throws `InvalidRequestError` **unless** the content is byte-identical (idempotent re-registration is a no-op).
63
+ - **Unknown name / version / tag** → `InvalidRequestError`.
64
+ - `register()` throws if the prompt has no `meta.name`.
65
+
66
+ ## `create()` — build + register in one entry point
67
+
68
+ `ai.prompts.create(input?, meta?)` is a documented alias of `ai.systemPrompt(...)` — identical input forms (no arg → empty builder; a string → one instruction; an array of blocks → verbatim). Pass `meta.name` to auto-register, so authoring and lookup read side-by-side:
69
+
70
+ ```ts
71
+ ai.prompts.create("You are support for {{product}}.", { name: "support" });
72
+ ai.prompts.resolve("support", undefined, { product: "Warlock" });
73
+ ```
74
+
75
+ ## `define()` — bulk-register many versions
76
+
77
+ ```ts
78
+ ai.prompts.define("agent", [
79
+ { version: "1", template: "You are v1." },
80
+ { version: "2", template: [ai.persona("You are Alex."), ai.instruction("Be concise.")] },
81
+ ]);
82
+ ```
83
+
84
+ A `PromptTemplateVersion`'s `template` is a raw string (wrapped into one instruction block) or an explicit ordered block list (verbatim). Versions register **oldest-first** in array order; the same duplicate / idempotency rule applies per `name@version`. Returns the manager for chaining.
85
+
86
+ ## `tag()` — pin a moving label to a version
87
+
88
+ ```ts
89
+ ai.prompts.tag("agent", "production", "2"); // pin "production" → version 2
90
+
91
+ ai.prompts.get("agent", "production"); // resolves through the tag
92
+ ai.prompts.resolve("agent", "production");
93
+ ai.prompts.get("agent@production"); // inline form
94
+ ```
95
+
96
+ Re-pinning an existing tag moves it. An unknown name / version throws `InvalidRequestError`. Tags survive `export` / `import`.
97
+
98
+ ## `validate()` — unified deterministic + optional LLM-judge
99
+
100
+ ```ts
101
+ const report = await ai.prompts.validate("support", {
102
+ placeholders: { product: "Warlock" }, // values you intend to supply
103
+ declare: ["language"], // extra keys to treat as known
104
+ judge: judgeModel, // optional — turns on the LLM-as-judge pass
105
+ });
106
+
107
+ report.ok; // true iff no required placeholder is missing (DETERMINISTIC verdict alone)
108
+ report.missing; // placeholder keys referenced with no default, unsupplied, undeclared
109
+ report.score; // 0..1 — present ONLY when a judge ran and produced a usable verdict
110
+ report.issues; // advisory judge reasons / a degrade note — present only when a judge was supplied
111
+ ```
112
+
113
+ - **Always** runs the deterministic check: every `{{key}}` with no inline default that is neither supplied (`placeholders`), declared (`declare`), nor in the prompt's `meta.required` lands in `missing`; `ok` is `true` iff `missing` is empty.
114
+ - **`judge`** adds a **Nova-safe** LLM-as-judge quality pass — it **never throws** and degrades to an `issues` note (leaving `score` undefined) on failure, so a flaky judge can **never flip `ok`**.
115
+ - **`target`** is a registered name (or `name@selector`), a `SystemPromptContract` instance, or a raw prompt string.
116
+ - **`judgeCache`** (per-call or via the `prompts({ judgeCache })` factory option) memoizes judge verdicts by a content hash of the resolved body + the judge model id — a structural `{ get, set }` subset of `@warlock.js/cache`'s `CacheDriver`, so the cache package stays a strictly **optional** peer.
117
+
118
+ `systemPrompt().validate(options?)` is the per-builder sugar — `ai.prompts.validate(this, options)` under the hood, same result shape.
119
+
120
+ ## `diff()` — block-level version diff
121
+
122
+ ```ts
123
+ const diff = ai.prompts.diff("agent", "1", "2");
124
+
125
+ diff.identical; // true when both versions have identical blocks in identical order
126
+ diff.added; // blocks in `to` not at the same position in `from`
127
+ diff.removed; // blocks in `from` not at the same position in `to`
128
+ diff.changed; // [{ from, to }] — same position, type/text changed
129
+ ```
130
+
131
+ Blocks are matched **positionally**. Unknown name / version → `InvalidRequestError`.
132
+
133
+ ## `export()` / `import()` — portable JSON round-trip
134
+
135
+ ```ts
136
+ const snapshot = ai.prompts.export(); // ExportedRegistry — every name, version, pinned tag, description/required
137
+ otherRegistry.import(snapshot); // rehydrate (same duplicate / idempotency rule; tags restored)
138
+ ```
139
+
140
+ Each version flattens to `{ type, text }` blocks so the registry round-trips without live builder instances — commit a snapshot, ship it, restore it elsewhere.
141
+
142
+ ## Compose registered prompts — `systemPrompt().merge(name, { fromVersion })`
143
+
144
+ `merge` folds another prompt's blocks into a new builder (persona **replaces**, instructions **append**) and records `meta.composedFrom`:
145
+
146
+ ```ts
147
+ ai.systemPrompt("Always answer in {{language|English}}.", { name: "global", version: "1" });
148
+
149
+ const supportPrompt = ai.systemPrompt("You are support for {{product}}.")
150
+ .merge("global", { fromVersion: "1" }); // fold the registered prompt by name
151
+
152
+ supportPrompt.meta()?.composedFrom; // ["…", "global@1"] — deterministic provenance
153
+ ```
154
+
155
+ `merge` accepts three source forms: a pre-built block, another `SystemPromptContract`, or a **registered name** resolved from `ai.prompts` (latest version unless `options.fromVersion` selects another — an unknown name / version throws `InvalidRequestError`).
156
+
157
+ ## `ai.prompt(...)` — now a thin facade (⚠ breaking vs the old registry)
158
+
159
+ `ai.prompt` has **two** call forms, both backed by the unified manager — there is no longer a separate prompt store:
160
+
161
+ ```ts
162
+ // (a) Resolve a globally-registered prompt from ai.prompts by name.
163
+ ai.systemPrompt("You are support.", { name: "support" });
164
+ const sp = ai.prompt("support"); // → SystemPromptContract (latest)
165
+ const v1 = ai.prompt("support", "1"); // → a specific version / pinned tag
166
+
167
+ // (b) Build an ISOLATED legacy-shaped registry (PromptRegistryContract).
168
+ const reg = ai.prompt({
169
+ prompts: [{ name: "summarizer", versions: [{ version: "1", template: "Summarize: {{text}}" }] }],
170
+ });
171
+ const resolved = reg.resolve("summarizer", { placeholders: { text } });
172
+ resolved.toSystemPrompt(); // drop-in for ai.agent({ systemPrompt })
173
+ ```
174
+
175
+ **⚠ Migration.** Before unification, `ai.prompt(...)` only built a standalone, self-contained registry with its **own private** storage. It now:
176
+
177
+ 1. Adds the **string overload** `ai.prompt(name, versionOrTag?)` → resolves from the shared `ai.prompts` manager. (New capability — `ai.prompt("x")` used to be a type error.)
178
+ 2. Backs the **options form** (`ai.prompt({ ... })` → `PromptRegistryContract`) by an internal `PromptsManagerContract`, so its storage shape and validation primitives are now the unified ones. The legacy method surface (`register` / `add` / `versions` / `resolve` / `validate` / `sync` + the `{ score, notes }` report shape) is **unchanged**, and each `ai.prompt({ ... })` call still returns its **own isolated** registry — no shared global state.
179
+
180
+ If you only ever called `ai.prompt({ ... })` and used the returned registry, **no code change is needed**. The new behavior is additive: prefer `ai.prompts` (the unified manager) for new code; reach for `ai.prompt({ ... })` only when you want the legacy `ResolvedPrompt` / `toSystemPrompt()` ergonomics or the optional Langfuse sync. The legacy facade's reference — `register` / `add` / `resolve(name, { version, placeholders })` / `validate` (`{ score, notes }`) / `sync()` (lazy `langfuse` peer) — is documented inline in `src/prompt/prompt.ts`.
181
+
182
+ ## See also
183
+
184
+ - [`@warlock.js/ai/write-system-prompt/SKILL.md`](@warlock.js/ai/write-system-prompt/SKILL.md) — the `systemPrompt()` / `persona()` / `instruction()` builder, `.meta()`, and `merge()` this registry stores and composes
185
+ - [`@warlock.js/ai/eval-datasets-and-ci/SKILL.md`](@warlock.js/ai/eval-datasets-and-ci/SKILL.md) — the eval `judge` scorer `validate()`'s LLM pass reuses
186
+ - [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md) — wiring a resolved prompt into an agent, plus the judge-safe agent preset (`ai.agent.judge`)