@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.
- package/CHANGELOG.md +52 -48
- package/cjs/index.cjs +221 -14144
- package/cjs/src-C02yzsLs.cjs +22991 -0
- package/cjs/src-C02yzsLs.cjs.map +1 -0
- package/cjs/src-DFibP2FQ.cjs +30 -0
- package/esm/agent/agent-config.type.d.mts +90 -1
- package/esm/agent/agent-config.type.d.mts.map +1 -1
- package/esm/agent/agent-input-builder.mjs +27 -6
- package/esm/agent/agent-input-builder.mjs.map +1 -1
- package/esm/agent/agent.d.mts +40 -1
- package/esm/agent/agent.d.mts.map +1 -1
- package/esm/agent/agent.mjs +203 -30
- package/esm/agent/agent.mjs.map +1 -1
- package/esm/agent/index.d.mts +2 -1
- package/esm/agent/index.mjs +1 -0
- package/esm/agent/judge-config.type.d.mts +33 -0
- package/esm/agent/judge-config.type.d.mts.map +1 -0
- package/esm/agent/judge-config.type.mjs +13 -0
- package/esm/agent/judge-config.type.mjs.map +1 -0
- package/esm/ai-openai/src/embedder.mjs +4 -0
- package/esm/ai-openai/src/index.mjs +4 -0
- package/esm/ai-openai/src/model.mjs +5 -0
- package/esm/ai-openai/src/sdk.mjs +6 -0
- package/esm/ai-openai/src/utils/index.mjs +4 -0
- package/esm/ai-openai/src/utils/to-openai-tools.mjs +3 -0
- package/esm/ai-openai/src/utils/wrap-openai-error.mjs +4 -0
- package/esm/ai.d.mts +119 -53
- package/esm/ai.d.mts.map +1 -1
- package/esm/ai.mjs +43 -8
- package/esm/ai.mjs.map +1 -1
- package/esm/batch/batch.d.mts.map +1 -1
- package/esm/batch/batch.mjs +21 -1
- package/esm/batch/batch.mjs.map +1 -1
- package/esm/batch/batch.type.d.mts +11 -4
- package/esm/batch/batch.type.d.mts.map +1 -1
- package/esm/config.d.mts +39 -3
- package/esm/config.d.mts.map +1 -1
- package/esm/config.mjs +26 -2
- package/esm/config.mjs.map +1 -1
- package/esm/contracts/agent/agent-options.type.d.mts +11 -3
- package/esm/contracts/agent/agent-options.type.d.mts.map +1 -1
- package/esm/contracts/agent/eval.type.d.mts +43 -2
- package/esm/contracts/agent/eval.type.d.mts.map +1 -1
- package/esm/contracts/attachment-policy.type.d.mts +51 -0
- package/esm/contracts/attachment-policy.type.d.mts.map +1 -0
- package/esm/contracts/attachment.type.d.mts +16 -7
- package/esm/contracts/attachment.type.d.mts.map +1 -1
- package/esm/contracts/content-part.type.d.mts +14 -5
- package/esm/contracts/content-part.type.d.mts.map +1 -1
- package/esm/contracts/events/supervisor-events.type.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +6 -4
- package/esm/contracts/model.contract.d.mts +11 -8
- package/esm/contracts/model.contract.d.mts.map +1 -1
- package/esm/contracts/orchestrator/index.d.mts +1 -0
- package/esm/contracts/orchestrator/orchestrator-config.type.d.mts +31 -2
- package/esm/contracts/orchestrator/orchestrator-config.type.d.mts.map +1 -1
- package/esm/contracts/orchestrator/session-lock.contract.d.mts +47 -0
- package/esm/contracts/orchestrator/session-lock.contract.d.mts.map +1 -0
- package/esm/contracts/planner/index.d.mts +1 -1
- package/esm/contracts/planner/planner-config.type.d.mts +31 -0
- package/esm/contracts/planner/planner-config.type.d.mts.map +1 -1
- package/esm/contracts/planner/planner-execute-options.type.d.mts +48 -1
- package/esm/contracts/planner/planner-execute-options.type.d.mts.map +1 -1
- package/esm/contracts/planner/planner-result.type.d.mts +19 -5
- package/esm/contracts/planner/planner-result.type.d.mts.map +1 -1
- package/esm/contracts/planner/planner.contract.d.mts +1 -1
- package/esm/contracts/result/agent-result.type.d.mts +6 -4
- package/esm/contracts/result/agent-result.type.d.mts.map +1 -1
- package/esm/contracts/result/base-report.type.d.mts +21 -3
- package/esm/contracts/result/base-report.type.d.mts.map +1 -1
- package/esm/contracts/result/base-report.type.mjs.map +1 -1
- package/esm/contracts/result/execution-report.type.d.mts +53 -1
- package/esm/contracts/result/execution-report.type.d.mts.map +1 -1
- package/esm/contracts/result/orchestrator-result.type.d.mts +15 -11
- package/esm/contracts/result/orchestrator-result.type.d.mts.map +1 -1
- package/esm/contracts/result/supervisor-result.type.d.mts +13 -1
- package/esm/contracts/result/supervisor-result.type.d.mts.map +1 -1
- package/esm/contracts/result/tool-call.type.d.mts +2 -2
- package/esm/contracts/result/tool-call.type.d.mts.map +1 -1
- package/esm/contracts/result/workflow-result.type.d.mts +1 -0
- package/esm/contracts/result/workflow-result.type.d.mts.map +1 -1
- package/esm/contracts/supervisor/supervisor-config.type.d.mts +29 -0
- package/esm/contracts/supervisor/supervisor-config.type.d.mts.map +1 -1
- package/esm/contracts/system-prompt.contract.d.mts +103 -1
- package/esm/contracts/system-prompt.contract.d.mts.map +1 -1
- package/esm/contracts/team/index.d.mts +1 -0
- package/esm/contracts/team/team-config.type.d.mts +127 -0
- package/esm/contracts/team/team-config.type.d.mts.map +1 -0
- package/esm/contracts/tool.contract.d.mts +4 -2
- package/esm/contracts/tool.contract.d.mts.map +1 -1
- package/esm/contracts/workflow/step.contract.d.mts +30 -5
- package/esm/contracts/workflow/step.contract.d.mts.map +1 -1
- package/esm/contracts/workflow/workflow.contract.d.mts +16 -0
- package/esm/contracts/workflow/workflow.contract.d.mts.map +1 -1
- package/esm/errors/error-code.type.d.mts +1 -1
- package/esm/errors/index.d.mts +1 -0
- package/esm/errors/index.mjs +1 -0
- package/esm/errors/outbound-policy-error.d.mts +27 -0
- package/esm/errors/outbound-policy-error.d.mts.map +1 -0
- package/esm/errors/outbound-policy-error.mjs +32 -0
- package/esm/errors/outbound-policy-error.mjs.map +1 -0
- package/esm/eval/dataset.d.mts +28 -0
- package/esm/eval/dataset.d.mts.map +1 -0
- package/esm/eval/dataset.mjs +112 -0
- package/esm/eval/dataset.mjs.map +1 -0
- package/esm/eval/dataset.type.d.mts +53 -0
- package/esm/eval/dataset.type.d.mts.map +1 -0
- package/esm/eval/eval-runner.d.mts.map +1 -1
- package/esm/eval/eval-runner.mjs +16 -2
- package/esm/eval/eval-runner.mjs.map +1 -1
- package/esm/eval/index.d.mts +20 -1
- package/esm/eval/index.d.mts.map +1 -1
- package/esm/eval/index.mjs +20 -2
- package/esm/eval/index.mjs.map +1 -1
- package/esm/eval/regression.d.mts +30 -0
- package/esm/eval/regression.d.mts.map +1 -0
- package/esm/eval/regression.mjs +51 -0
- package/esm/eval/regression.mjs.map +1 -0
- package/esm/eval/report-json.d.mts +30 -0
- package/esm/eval/report-json.d.mts.map +1 -0
- package/esm/eval/report-json.mjs +33 -0
- package/esm/eval/report-json.mjs.map +1 -0
- package/esm/eval/report-junit.d.mts +22 -0
- package/esm/eval/report-junit.d.mts.map +1 -0
- package/esm/eval/report-junit.mjs +60 -0
- package/esm/eval/report-junit.mjs.map +1 -0
- package/esm/guard/contracts/guard-options.type.d.mts +165 -0
- package/esm/guard/contracts/guard-options.type.d.mts.map +1 -0
- package/esm/guard/contracts/guardrail.contract.d.mts +78 -0
- package/esm/guard/contracts/guardrail.contract.d.mts.map +1 -0
- package/esm/guard/contracts/index.d.mts +4 -0
- package/esm/guard/contracts/openai-client.contract.d.mts +51 -0
- package/esm/guard/contracts/openai-client.contract.d.mts.map +1 -0
- package/esm/guard/contracts/verdict.type.d.mts +76 -0
- package/esm/guard/contracts/verdict.type.d.mts.map +1 -0
- package/esm/guard/detectors/index.d.mts +4 -0
- package/esm/guard/detectors/index.mjs +6 -0
- package/esm/guard/detectors/injection.d.mts +34 -0
- package/esm/guard/detectors/injection.d.mts.map +1 -0
- package/esm/guard/detectors/injection.mjs +254 -0
- package/esm/guard/detectors/injection.mjs.map +1 -0
- package/esm/guard/detectors/moderation.d.mts +32 -0
- package/esm/guard/detectors/moderation.d.mts.map +1 -0
- package/esm/guard/detectors/moderation.mjs +134 -0
- package/esm/guard/detectors/moderation.mjs.map +1 -0
- package/esm/guard/detectors/pii.d.mts +41 -0
- package/esm/guard/detectors/pii.d.mts.map +1 -0
- package/esm/guard/detectors/pii.mjs +199 -0
- package/esm/guard/detectors/pii.mjs.map +1 -0
- package/esm/guard/detectors/topic.d.mts +29 -0
- package/esm/guard/detectors/topic.d.mts.map +1 -0
- package/esm/guard/detectors/topic.mjs +99 -0
- package/esm/guard/detectors/topic.mjs.map +1 -0
- package/esm/guard/errors.d.mts +12 -0
- package/esm/guard/errors.d.mts.map +1 -0
- package/esm/guard/errors.mjs +18 -0
- package/esm/guard/errors.mjs.map +1 -0
- package/esm/guard/guard.d.mts +67 -0
- package/esm/guard/guard.d.mts.map +1 -0
- package/esm/guard/guard.mjs +209 -0
- package/esm/guard/guard.mjs.map +1 -0
- package/esm/guard/guardrail.d.mts +39 -0
- package/esm/guard/guardrail.d.mts.map +1 -0
- package/esm/guard/guardrail.mjs +22 -0
- package/esm/guard/guardrail.mjs.map +1 -0
- package/esm/human/contracts/approval.type.d.mts +154 -0
- package/esm/human/contracts/approval.type.d.mts.map +1 -0
- package/esm/human/contracts/human-approval.type.d.mts +38 -0
- package/esm/human/contracts/human-approval.type.d.mts.map +1 -0
- package/esm/human/contracts/index.d.mts +4 -0
- package/esm/human/contracts/interrupt-store.contract.d.mts +112 -0
- package/esm/human/contracts/interrupt-store.contract.d.mts.map +1 -0
- package/esm/human/contracts/resume.type.d.mts +77 -0
- package/esm/human/contracts/resume.type.d.mts.map +1 -0
- package/esm/human/errors.d.mts +84 -0
- package/esm/human/errors.d.mts.map +1 -0
- package/esm/human/errors.mjs +60 -0
- package/esm/human/errors.mjs.map +1 -0
- package/esm/human/human-approval.d.mts +57 -0
- package/esm/human/human-approval.d.mts.map +1 -0
- package/esm/human/human-approval.mjs +170 -0
- package/esm/human/human-approval.mjs.map +1 -0
- package/esm/human/policy.d.mts +55 -0
- package/esm/human/policy.d.mts.map +1 -0
- package/esm/human/policy.mjs +67 -0
- package/esm/human/policy.mjs.map +1 -0
- package/esm/human/register.mjs +37 -0
- package/esm/human/register.mjs.map +1 -0
- package/esm/human/resume-seed.mjs +53 -0
- package/esm/human/resume-seed.mjs.map +1 -0
- package/esm/human/resume.d.mts +54 -0
- package/esm/human/resume.d.mts.map +1 -0
- package/esm/human/resume.mjs +121 -0
- package/esm/human/resume.mjs.map +1 -0
- package/esm/human/stores/index.mjs +5 -0
- package/esm/human/stores/memory.d.mts +30 -0
- package/esm/human/stores/memory.d.mts.map +1 -0
- package/esm/human/stores/memory.mjs +91 -0
- package/esm/human/stores/memory.mjs.map +1 -0
- package/esm/human/stores/pg.d.mts +59 -0
- package/esm/human/stores/pg.d.mts.map +1 -0
- package/esm/human/stores/pg.mjs +220 -0
- package/esm/human/stores/pg.mjs.map +1 -0
- package/esm/human/stores/redis.d.mts +56 -0
- package/esm/human/stores/redis.d.mts.map +1 -0
- package/esm/human/stores/redis.mjs +201 -0
- package/esm/human/stores/redis.mjs.map +1 -0
- package/esm/index.d.mts +113 -22
- package/esm/index.mjs +82 -11
- package/esm/middleware/builtins/budget.mjs +6 -2
- package/esm/middleware/builtins/budget.mjs.map +1 -1
- package/esm/middleware/utils/extract-user-text.d.mts +8 -1
- package/esm/middleware/utils/extract-user-text.d.mts.map +1 -1
- package/esm/middleware/utils/extract-user-text.mjs +8 -1
- package/esm/middleware/utils/extract-user-text.mjs.map +1 -1
- package/esm/object-stream/index.d.mts +2 -0
- package/esm/object-stream/index.mjs +4 -0
- package/esm/object-stream/parse-partial-json.d.mts +22 -0
- package/esm/object-stream/parse-partial-json.d.mts.map +1 -0
- package/esm/object-stream/parse-partial-json.mjs +78 -0
- package/esm/object-stream/parse-partial-json.mjs.map +1 -0
- package/esm/object-stream/stream-object.d.mts +68 -0
- package/esm/object-stream/stream-object.d.mts.map +1 -0
- package/esm/object-stream/stream-object.mjs +104 -0
- package/esm/object-stream/stream-object.mjs.map +1 -0
- package/esm/observe/index.mjs +4 -0
- package/esm/observe/observer-registry.d.mts +30 -0
- package/esm/observe/observer-registry.d.mts.map +1 -0
- package/esm/observe/observer-registry.mjs +51 -0
- package/esm/observe/observer-registry.mjs.map +1 -0
- package/esm/observe/observer.contract.d.mts +40 -0
- package/esm/observe/observer.contract.d.mts.map +1 -0
- package/esm/observe/resolve-observers.d.mts +40 -0
- package/esm/observe/resolve-observers.d.mts.map +1 -0
- package/esm/observe/resolve-observers.mjs +73 -0
- package/esm/observe/resolve-observers.mjs.map +1 -0
- package/esm/orchestrator/execution.d.mts.map +1 -1
- package/esm/orchestrator/execution.mjs +5 -2
- package/esm/orchestrator/execution.mjs.map +1 -1
- package/esm/orchestrator/index.d.mts +1 -0
- package/esm/orchestrator/index.mjs +1 -0
- package/esm/orchestrator/orchestrator.d.mts.map +1 -1
- package/esm/orchestrator/orchestrator.mjs +39 -6
- package/esm/orchestrator/orchestrator.mjs.map +1 -1
- package/esm/orchestrator/session-lock.d.mts +25 -0
- package/esm/orchestrator/session-lock.d.mts.map +1 -0
- package/esm/orchestrator/session-lock.mjs +83 -0
- package/esm/orchestrator/session-lock.mjs.map +1 -0
- package/esm/planner/dag-scheduler.mjs +97 -0
- package/esm/planner/dag-scheduler.mjs.map +1 -0
- package/esm/planner/plan-prompt.d.mts +1 -1
- package/esm/planner/plan-prompt.d.mts.map +1 -1
- package/esm/planner/plan-prompt.mjs +2 -1
- package/esm/planner/plan-prompt.mjs.map +1 -1
- package/esm/planner/plan-schema.mjs +17 -14
- package/esm/planner/plan-schema.mjs.map +1 -1
- package/esm/planner/planner-run.d.mts.map +1 -1
- package/esm/planner/planner-run.mjs +300 -29
- package/esm/planner/planner-run.mjs.map +1 -1
- package/esm/planner/planner.mjs +1 -1
- package/esm/planner/planner.mjs.map +1 -1
- package/esm/prompt/errors.d.mts +57 -0
- package/esm/prompt/errors.d.mts.map +1 -0
- package/esm/prompt/errors.mjs +73 -0
- package/esm/prompt/errors.mjs.map +1 -0
- package/esm/prompt/index.d.mts +3 -0
- package/esm/prompt/index.mjs +4 -0
- package/esm/prompt/prompt-langfuse-sync.mjs +104 -0
- package/esm/prompt/prompt-langfuse-sync.mjs.map +1 -0
- package/esm/prompt/prompt-langfuse-sync.type.d.mts +32 -0
- package/esm/prompt/prompt-langfuse-sync.type.d.mts.map +1 -0
- package/esm/prompt/prompt-validate.mjs +170 -0
- package/esm/prompt/prompt-validate.mjs.map +1 -0
- package/esm/prompt/prompt.d.mts +54 -0
- package/esm/prompt/prompt.d.mts.map +1 -0
- package/esm/prompt/prompt.mjs +218 -0
- package/esm/prompt/prompt.mjs.map +1 -0
- package/esm/prompt/prompt.type.d.mts +174 -0
- package/esm/prompt/prompt.type.d.mts.map +1 -0
- package/esm/prompts/index.d.mts +3 -0
- package/esm/prompts/index.mjs +3 -0
- package/esm/prompts/prompts-manager.contract.d.mts +154 -0
- package/esm/prompts/prompts-manager.contract.d.mts.map +1 -0
- package/esm/prompts/prompts-manager.d.mts +38 -0
- package/esm/prompts/prompts-manager.d.mts.map +1 -0
- package/esm/prompts/prompts-manager.mjs +410 -0
- package/esm/prompts/prompts-manager.mjs.map +1 -0
- package/esm/prompts/prompts-manager.type.d.mts +172 -0
- package/esm/prompts/prompts-manager.type.d.mts.map +1 -0
- package/esm/prompts/prompts-validate.mjs +200 -0
- package/esm/prompts/prompts-validate.mjs.map +1 -0
- package/esm/rag/as-tool.mjs +48 -0
- package/esm/rag/as-tool.mjs.map +1 -0
- package/esm/rag/chunk/chunk.d.mts +24 -0
- package/esm/rag/chunk/chunk.d.mts.map +1 -0
- package/esm/rag/chunk/chunk.mjs +44 -0
- package/esm/rag/chunk/chunk.mjs.map +1 -0
- package/esm/rag/chunk/fixed.mjs +32 -0
- package/esm/rag/chunk/fixed.mjs.map +1 -0
- package/esm/rag/chunk/markdown.mjs +75 -0
- package/esm/rag/chunk/markdown.mjs.map +1 -0
- package/esm/rag/chunk/recursive.mjs +132 -0
- package/esm/rag/chunk/recursive.mjs.map +1 -0
- package/esm/rag/chunk/sentence.mjs +73 -0
- package/esm/rag/chunk/sentence.mjs.map +1 -0
- package/esm/rag/contracts/chunk-options.type.d.mts +35 -0
- package/esm/rag/contracts/chunk-options.type.d.mts.map +1 -0
- package/esm/rag/contracts/citation.type.d.mts +35 -0
- package/esm/rag/contracts/citation.type.d.mts.map +1 -0
- package/esm/rag/contracts/index.d.mts +4 -0
- package/esm/rag/contracts/rag-config.type.d.mts +68 -0
- package/esm/rag/contracts/rag-config.type.d.mts.map +1 -0
- package/esm/rag/contracts/rag-document.type.d.mts +21 -0
- package/esm/rag/contracts/rag-document.type.d.mts.map +1 -0
- package/esm/rag/hybrid/bm25.d.mts +23 -0
- package/esm/rag/hybrid/bm25.d.mts.map +1 -0
- package/esm/rag/hybrid/bm25.mjs +51 -0
- package/esm/rag/hybrid/bm25.mjs.map +1 -0
- package/esm/rag/hybrid/hybrid-rank.d.mts +33 -0
- package/esm/rag/hybrid/hybrid-rank.d.mts.map +1 -0
- package/esm/rag/hybrid/hybrid-rank.mjs +29 -0
- package/esm/rag/hybrid/hybrid-rank.mjs.map +1 -0
- package/esm/rag/hybrid/rrf.d.mts +25 -0
- package/esm/rag/hybrid/rrf.d.mts.map +1 -0
- package/esm/rag/hybrid/rrf.mjs +30 -0
- package/esm/rag/hybrid/rrf.mjs.map +1 -0
- package/esm/rag/index.d.mts +15 -0
- package/esm/rag/index.mjs +11 -0
- package/esm/rag/rag.d.mts +38 -0
- package/esm/rag/rag.d.mts.map +1 -0
- package/esm/rag/rag.mjs +126 -0
- package/esm/rag/rag.mjs.map +1 -0
- package/esm/rag/rerank/keyword-reranker.d.mts +32 -0
- package/esm/rag/rerank/keyword-reranker.d.mts.map +1 -0
- package/esm/rag/rerank/keyword-reranker.mjs +58 -0
- package/esm/rag/rerank/keyword-reranker.mjs.map +1 -0
- package/esm/rag/rerank/llm-reranker.d.mts +36 -0
- package/esm/rag/rerank/llm-reranker.d.mts.map +1 -0
- package/esm/rag/rerank/llm-reranker.mjs +85 -0
- package/esm/rag/rerank/llm-reranker.mjs.map +1 -0
- package/esm/rag/rerank/reranker.contract.d.mts +28 -0
- package/esm/rag/rerank/reranker.contract.d.mts.map +1 -0
- package/esm/rag/retrieve.mjs +68 -0
- package/esm/rag/retrieve.mjs.map +1 -0
- package/esm/rag/store/cache-vector-store.d.mts +27 -0
- package/esm/rag/store/cache-vector-store.d.mts.map +1 -0
- package/esm/rag/store/cache-vector-store.mjs +48 -0
- package/esm/rag/store/cache-vector-store.mjs.map +1 -0
- package/esm/rag/store/vector-store.contract.d.mts +38 -0
- package/esm/rag/store/vector-store.contract.d.mts.map +1 -0
- package/esm/rag/transforms/multi-query.d.mts +27 -0
- package/esm/rag/transforms/multi-query.d.mts.map +1 -0
- package/esm/rag/transforms/multi-query.mjs +41 -0
- package/esm/rag/transforms/multi-query.mjs.map +1 -0
- package/esm/security/index.mjs +5 -0
- package/esm/security/outbound-policy.d.mts +46 -0
- package/esm/security/outbound-policy.d.mts.map +1 -0
- package/esm/security/outbound-policy.mjs +187 -0
- package/esm/security/outbound-policy.mjs.map +1 -0
- package/esm/security/outbound-policy.type.d.mts +74 -0
- package/esm/security/outbound-policy.type.d.mts.map +1 -0
- package/esm/security/private-ip.d.mts +15 -0
- package/esm/security/private-ip.d.mts.map +1 -0
- package/esm/security/private-ip.mjs +48 -0
- package/esm/security/private-ip.mjs.map +1 -0
- package/esm/security/redact.d.mts +59 -0
- package/esm/security/redact.d.mts.map +1 -0
- package/esm/security/redact.mjs +122 -0
- package/esm/security/redact.mjs.map +1 -0
- package/esm/serve/serve.d.mts +50 -0
- package/esm/serve/serve.d.mts.map +1 -0
- package/esm/serve/serve.mjs +90 -0
- package/esm/serve/serve.mjs.map +1 -0
- package/esm/serve/sse.d.mts +20 -0
- package/esm/serve/sse.d.mts.map +1 -0
- package/esm/serve/sse.mjs +25 -0
- package/esm/serve/sse.mjs.map +1 -0
- package/esm/serve/stream-to-sse.d.mts +29 -0
- package/esm/serve/stream-to-sse.d.mts.map +1 -0
- package/esm/serve/stream-to-sse.mjs +37 -0
- package/esm/serve/stream-to-sse.mjs.map +1 -0
- package/esm/skills/catalog.d.mts +49 -0
- package/esm/skills/catalog.d.mts.map +1 -0
- package/esm/skills/catalog.mjs +140 -0
- package/esm/skills/catalog.mjs.map +1 -0
- package/esm/skills/contracts/skill-record.type.d.mts +37 -0
- package/esm/skills/contracts/skill-record.type.d.mts.map +1 -0
- package/esm/skills/contracts/skills-config.type.d.mts +108 -0
- package/esm/skills/contracts/skills-config.type.d.mts.map +1 -0
- package/esm/skills/contracts/skills-store.contract.d.mts +28 -0
- package/esm/skills/contracts/skills-store.contract.d.mts.map +1 -0
- package/esm/skills/contracts/skills.contract.d.mts +43 -0
- package/esm/skills/contracts/skills.contract.d.mts.map +1 -0
- package/esm/skills/index.d.mts +16 -0
- package/esm/skills/index.mjs +14 -0
- package/esm/skills/load-skill-tool.d.mts +38 -0
- package/esm/skills/load-skill-tool.d.mts.map +1 -0
- package/esm/skills/load-skill-tool.mjs +65 -0
- package/esm/skills/load-skill-tool.mjs.map +1 -0
- package/esm/skills/review-gate.d.mts +33 -0
- package/esm/skills/review-gate.d.mts.map +1 -0
- package/esm/skills/review-gate.mjs +60 -0
- package/esm/skills/review-gate.mjs.map +1 -0
- package/esm/skills/save-skill-tool.d.mts +39 -0
- package/esm/skills/save-skill-tool.d.mts.map +1 -0
- package/esm/skills/save-skill-tool.mjs +65 -0
- package/esm/skills/save-skill-tool.mjs.map +1 -0
- package/esm/skills/skills.d.mts +33 -0
- package/esm/skills/skills.d.mts.map +1 -0
- package/esm/skills/skills.mjs +109 -0
- package/esm/skills/skills.mjs.map +1 -0
- package/esm/skills/sources/directory-source.d.mts +19 -0
- package/esm/skills/sources/directory-source.d.mts.map +1 -0
- package/esm/skills/sources/directory-source.mjs +108 -0
- package/esm/skills/sources/directory-source.mjs.map +1 -0
- package/esm/skills/sources/index.d.mts +18 -0
- package/esm/skills/sources/index.d.mts.map +1 -0
- package/esm/skills/sources/index.mjs +27 -0
- package/esm/skills/sources/index.mjs.map +1 -0
- package/esm/skills/sources/parse-frontmatter.d.mts +27 -0
- package/esm/skills/sources/parse-frontmatter.d.mts.map +1 -0
- package/esm/skills/sources/parse-frontmatter.mjs +46 -0
- package/esm/skills/sources/parse-frontmatter.mjs.map +1 -0
- package/esm/skills/sources/store-source.d.mts +14 -0
- package/esm/skills/sources/store-source.d.mts.map +1 -0
- package/esm/skills/sources/store-source.mjs +15 -0
- package/esm/skills/sources/store-source.mjs.map +1 -0
- package/esm/skills/sources/url-source.d.mts +29 -0
- package/esm/skills/sources/url-source.d.mts.map +1 -0
- package/esm/skills/sources/url-source.mjs +117 -0
- package/esm/skills/sources/url-source.mjs.map +1 -0
- package/esm/skills/store/mock-skills-store.d.mts +57 -0
- package/esm/skills/store/mock-skills-store.d.mts.map +1 -0
- package/esm/skills/store/mock-skills-store.mjs +100 -0
- package/esm/skills/store/mock-skills-store.mjs.map +1 -0
- package/esm/skills/store/procedural-skill-store.d.mts +30 -0
- package/esm/skills/store/procedural-skill-store.d.mts.map +1 -0
- package/esm/skills/store/procedural-skill-store.mjs +125 -0
- package/esm/skills/store/procedural-skill-store.mjs.map +1 -0
- package/esm/supervisor/as-tool.mjs +2 -2
- package/esm/supervisor/as-tool.mjs.map +1 -1
- package/esm/supervisor/execution.d.mts.map +1 -1
- package/esm/supervisor/execution.mjs +31 -28
- package/esm/supervisor/execution.mjs.map +1 -1
- package/esm/supervisor/supervisor.d.mts.map +1 -1
- package/esm/supervisor/supervisor.mjs +8 -3
- package/esm/supervisor/supervisor.mjs.map +1 -1
- package/esm/system-prompt/index.d.mts +4 -0
- package/esm/system-prompt/system-prompt.d.mts +68 -4
- package/esm/system-prompt/system-prompt.d.mts.map +1 -1
- package/esm/system-prompt/system-prompt.mjs +89 -5
- package/esm/system-prompt/system-prompt.mjs.map +1 -1
- package/esm/team/gates.mjs +48 -0
- package/esm/team/gates.mjs.map +1 -0
- package/esm/team/index.d.mts +1 -0
- package/esm/team/index.mjs +3 -0
- package/esm/team/team.d.mts +42 -0
- package/esm/team/team.d.mts.map +1 -0
- package/esm/team/team.mjs +94 -0
- package/esm/team/team.mjs.map +1 -0
- package/esm/tool/executable-as-tool.d.mts.map +1 -1
- package/esm/tool/executable-as-tool.mjs +2 -2
- package/esm/tool/executable-as-tool.mjs.map +1 -1
- package/esm/tool/tool.d.mts.map +1 -1
- package/esm/tool/tool.mjs +2 -2
- package/esm/tool/tool.mjs.map +1 -1
- package/esm/utils/compute-cost.d.mts +17 -1
- package/esm/utils/compute-cost.d.mts.map +1 -1
- package/esm/utils/compute-cost.mjs +26 -1
- package/esm/utils/compute-cost.mjs.map +1 -1
- package/esm/utils/extract-json-lenient.d.mts +42 -0
- package/esm/utils/extract-json-lenient.d.mts.map +1 -0
- package/esm/utils/extract-json-lenient.mjs +97 -0
- package/esm/utils/extract-json-lenient.mjs.map +1 -0
- package/esm/utils/index.d.mts +4 -2
- package/esm/utils/index.mjs +3 -1
- package/esm/utils/json-schema.d.mts +1 -1
- package/esm/utils/prepare-attachment-part.d.mts +10 -1
- package/esm/utils/prepare-attachment-part.d.mts.map +1 -1
- package/esm/utils/prepare-attachment-part.mjs +103 -11
- package/esm/utils/prepare-attachment-part.mjs.map +1 -1
- package/esm/utils/resolve-attachment.d.mts +4 -3
- package/esm/utils/resolve-attachment.d.mts.map +1 -1
- package/esm/utils/resolve-attachment.mjs +4 -3
- package/esm/utils/resolve-attachment.mjs.map +1 -1
- package/esm/utils/run-context.d.mts +94 -0
- package/esm/utils/run-context.d.mts.map +1 -0
- package/esm/utils/run-context.mjs +98 -0
- package/esm/utils/run-context.mjs.map +1 -0
- package/esm/vcr/cassette-io.mjs +57 -0
- package/esm/vcr/cassette-io.mjs.map +1 -0
- package/esm/vcr/errors.d.mts +42 -0
- package/esm/vcr/errors.d.mts.map +1 -0
- package/esm/vcr/errors.mjs +37 -0
- package/esm/vcr/errors.mjs.map +1 -0
- package/esm/vcr/hash-request.d.mts +28 -0
- package/esm/vcr/hash-request.d.mts.map +1 -0
- package/esm/vcr/hash-request.mjs +118 -0
- package/esm/vcr/hash-request.mjs.map +1 -0
- package/esm/vcr/index.d.mts +4 -0
- package/esm/vcr/index.mjs +5 -0
- package/esm/vcr/vcr.d.mts +32 -0
- package/esm/vcr/vcr.d.mts.map +1 -0
- package/esm/vcr/vcr.mjs +248 -0
- package/esm/vcr/vcr.mjs.map +1 -0
- package/esm/vcr/vcr.type.d.mts +118 -0
- package/esm/vcr/vcr.type.d.mts.map +1 -0
- package/esm/workflow/as-tool.mjs +2 -2
- package/esm/workflow/as-tool.mjs.map +1 -1
- package/esm/workflow/engine.mjs +1 -0
- package/esm/workflow/engine.mjs.map +1 -1
- package/esm/workflow/step-runner.mjs +19 -20
- package/esm/workflow/step-runner.mjs.map +1 -1
- package/esm/workflow/workflow.d.mts.map +1 -1
- package/esm/workflow/workflow.mjs +8 -3
- package/esm/workflow/workflow.mjs.map +1 -1
- package/llms-full.txt +1683 -68
- package/llms.txt +16 -4
- package/package.json +7 -3
- package/skills/README.md +40 -4
- package/skills/ai-dx-helpers/SKILL.md +2 -2
- package/skills/approve-tool-calls/SKILL.md +134 -0
- package/skills/attach-ai-middleware/SKILL.md +1 -1
- package/skills/detect-and-redact-pii/SKILL.md +104 -0
- package/skills/durable-resume/SKILL.md +128 -0
- package/skills/escalate-block-to-human/SKILL.md +85 -0
- package/skills/eval-datasets-and-ci/SKILL.md +117 -0
- package/skills/guard-input-output/SKILL.md +117 -0
- package/skills/manage-prompts/SKILL.md +186 -0
- package/skills/observe-ai-flows/SKILL.md +94 -0
- package/skills/record-replay-llm/SKILL.md +92 -0
- package/skills/run-ai-agent/SKILL.md +26 -1
- package/skills/run-ai-rag/SKILL.md +139 -0
- package/skills/run-ai-team/SKILL.md +107 -0
- package/skills/run-orchestrator/SKILL.md +2 -0
- package/skills/run-planner/SKILL.md +73 -8
- package/skills/run-supervisor/SKILL.md +20 -1
- package/skills/use-runtime-skills/SKILL.md +106 -0
- package/skills/write-system-prompt/SKILL.md +30 -1
- package/cjs/index.cjs.map +0 -1
- package/esm/contracts/result/index.d.mts +0 -15
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AIError } from "../errors/ai-error.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/vcr/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when a `vcr(model, { mode: "replay" })` call finds no cassette
|
|
6
|
+
* entry matching the request hash.
|
|
7
|
+
*
|
|
8
|
+
* **The whole point of deterministic tests.** VCR in `replay` mode never
|
|
9
|
+
* falls back to a live provider call on a miss — that would silently
|
|
10
|
+
* re-introduce network/non-determinism into a test that asked for the
|
|
11
|
+
* opposite. Instead it throws this error so the run fails loud, telling
|
|
12
|
+
* the developer to re-record the cassette (run once in `record`/`auto`).
|
|
13
|
+
*
|
|
14
|
+
* Extends {@link AIError} directly (not `ProviderError`) — a cassette miss
|
|
15
|
+
* is a harness/config failure, not a provider failure.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* try {
|
|
19
|
+
* await vcrModel.complete(messages);
|
|
20
|
+
* } catch (error) {
|
|
21
|
+
* if (error instanceof VcrCassetteMissError) {
|
|
22
|
+
* console.error("Re-record the cassette:", error.path);
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
var VcrCassetteMissError = class extends AIError {
|
|
27
|
+
constructor(message, options) {
|
|
28
|
+
super("VCR_CASSETTE_MISS", message, options);
|
|
29
|
+
this.name = "VcrCassetteMissError";
|
|
30
|
+
this.requestHash = options?.requestHash;
|
|
31
|
+
this.path = options?.path;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { VcrCassetteMissError };
|
|
37
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/errors.ts"],"sourcesContent":["import { AIError } from \"../errors/ai-error\";\nimport type { AIErrorOptions } from \"../errors/ai-error\";\n\n/**\n * Payload for {@link VcrCassetteMissError}. Carries the looked-up request\n * hash and the cassette path so a failing CI run names exactly which call\n * was not recorded.\n */\nexport type VcrCassetteMissErrorOptions = AIErrorOptions & {\n /** The normalized request hash that found no matching cassette entry. */\n requestHash?: string;\n /** Cassette file path the lookup ran against. */\n path?: string;\n};\n\n/**\n * Thrown when a `vcr(model, { mode: \"replay\" })` call finds no cassette\n * entry matching the request hash.\n *\n * **The whole point of deterministic tests.** VCR in `replay` mode never\n * falls back to a live provider call on a miss — that would silently\n * re-introduce network/non-determinism into a test that asked for the\n * opposite. Instead it throws this error so the run fails loud, telling\n * the developer to re-record the cassette (run once in `record`/`auto`).\n *\n * Extends {@link AIError} directly (not `ProviderError`) — a cassette miss\n * is a harness/config failure, not a provider failure.\n *\n * @example\n * try {\n * await vcrModel.complete(messages);\n * } catch (error) {\n * if (error instanceof VcrCassetteMissError) {\n * console.error(\"Re-record the cassette:\", error.path);\n * }\n * }\n */\nexport class VcrCassetteMissError extends AIError {\n public readonly requestHash?: string;\n public readonly path?: string;\n\n public constructor(message: string, options?: VcrCassetteMissErrorOptions) {\n super(\"VCR_CASSETTE_MISS\", message, options);\n this.name = \"VcrCassetteMissError\";\n this.requestHash = options?.requestHash;\n this.path = options?.path;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,uBAAb,cAA0C,QAAQ;CAIhD,AAAO,YAAY,SAAiB,SAAuC;EACzE,MAAM,qBAAqB,SAAS,OAAO;EAC3C,KAAK,OAAO;EACZ,KAAK,cAAc,SAAS;EAC5B,KAAK,OAAO,SAAS;CACvB;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Message } from "../contracts/conversation-message.type.mjs";
|
|
2
|
+
import { ModelCallOptions } from "../contracts/model.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/vcr/hash-request.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Default `ModelCallOptions` fields folded into the request hash. These are
|
|
7
|
+
* the inputs that materially change the model's output; everything else
|
|
8
|
+
* (notably `signal` and unknown provider keys) is excluded so an otherwise
|
|
9
|
+
* identical logical call still matches its recording.
|
|
10
|
+
*/
|
|
11
|
+
declare const DEFAULT_HASH_OPTIONS: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Compute the stable VCR request hash for a model call.
|
|
14
|
+
*
|
|
15
|
+
* The hash covers the full `messages` array plus the picked, normalized
|
|
16
|
+
* `options` subset (see {@link DEFAULT_HASH_OPTIONS}). Inputs are
|
|
17
|
+
* canonicalized (recursive key sort) before serialization so property order
|
|
18
|
+
* never affects the result. `signal` and unknown provider keys are excluded.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const a = hashRequest(messages, { temperature: 0.2 });
|
|
22
|
+
* const b = hashRequest(messages, { temperature: 0.2, signal });
|
|
23
|
+
* // a === b — signal is excluded.
|
|
24
|
+
*/
|
|
25
|
+
declare function hashRequest(messages: Message[], options?: ModelCallOptions, hashOptions?: readonly string[]): string;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { DEFAULT_HASH_OPTIONS, hashRequest };
|
|
28
|
+
//# sourceMappingURL=hash-request.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-request.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/hash-request.ts"],"mappings":";;;;;;AAUA;;;;cAAa,oBAAA;AAiJb;;;;;;;;;;AAGuD;;;AAHvD,iBAAgB,WAAA,CACd,QAAA,EAAU,OAAA,IACV,OAAA,GAAU,gBAAgB,EAC1B,WAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai/src/vcr/hash-request.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default `ModelCallOptions` fields folded into the request hash. These are
|
|
4
|
+
* the inputs that materially change the model's output; everything else
|
|
5
|
+
* (notably `signal` and unknown provider keys) is excluded so an otherwise
|
|
6
|
+
* identical logical call still matches its recording.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_HASH_OPTIONS = [
|
|
9
|
+
"temperature",
|
|
10
|
+
"maxTokens",
|
|
11
|
+
"responseSchema",
|
|
12
|
+
"tools",
|
|
13
|
+
"reasoning"
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Reduce a tool to the parts the model actually conditions on: its name,
|
|
17
|
+
* description, and the *shape* of its input schema. Two tools that differ
|
|
18
|
+
* only by object identity (a fresh schema instance per import) hash
|
|
19
|
+
* identically; a real contract change (renamed field, new description)
|
|
20
|
+
* invalidates the recording.
|
|
21
|
+
*
|
|
22
|
+
* The input schema is fingerprinted structurally — a Standard Schema is an
|
|
23
|
+
* opaque object, so we serialize its enumerable own keys rather than
|
|
24
|
+
* attempting to read its internals.
|
|
25
|
+
*/
|
|
26
|
+
function fingerprintTool(tool) {
|
|
27
|
+
return {
|
|
28
|
+
name: tool.name,
|
|
29
|
+
description: tool.description,
|
|
30
|
+
input: tool.input ? schemaShape(tool.input) : void 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Produce a stable, JSON-safe fingerprint of an arbitrary schema object.
|
|
35
|
+
* Records only the structural skeleton (own enumerable keys, recursively)
|
|
36
|
+
* so harmless instance differences don't perturb the hash while a genuine
|
|
37
|
+
* structural change does.
|
|
38
|
+
*/
|
|
39
|
+
function schemaShape(value, depth = 0) {
|
|
40
|
+
if (depth > 6 || value === null || typeof value !== "object") return typeof value;
|
|
41
|
+
if (Array.isArray(value)) return value.map((item) => schemaShape(item, depth + 1));
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const key of Object.keys(value).sort()) out[key] = schemaShape(value[key], depth + 1);
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Pick the hashable subset of `options`, normalizing `tools` into their
|
|
48
|
+
* name+description+schema-shape fingerprint. `signal` and any field not in
|
|
49
|
+
* `hashOptions` are dropped.
|
|
50
|
+
*/
|
|
51
|
+
function pickOptions(options, hashOptions) {
|
|
52
|
+
if (!options) return {};
|
|
53
|
+
const picked = {};
|
|
54
|
+
for (const key of hashOptions) {
|
|
55
|
+
const value = options[key];
|
|
56
|
+
if (value === void 0) continue;
|
|
57
|
+
if (key === "tools" && Array.isArray(value)) {
|
|
58
|
+
picked[key] = value.map(fingerprintTool);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
picked[key] = value;
|
|
62
|
+
}
|
|
63
|
+
return picked;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Recursively sort object keys so two logically-equal payloads serialize to
|
|
67
|
+
* byte-identical JSON regardless of property insertion order.
|
|
68
|
+
*/
|
|
69
|
+
function canonicalize(value) {
|
|
70
|
+
if (value === null || typeof value !== "object") return value;
|
|
71
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
72
|
+
const out = {};
|
|
73
|
+
for (const key of Object.keys(value).sort()) out[key] = canonicalize(value[key]);
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Non-cryptographic 53-bit string hash (FNV-style, cyrb53). Deterministic
|
|
78
|
+
* across runs and platforms; collision-resistant enough for a per-cassette
|
|
79
|
+
* keyspace. Returned as a base-36 string.
|
|
80
|
+
*/
|
|
81
|
+
function hashString(input) {
|
|
82
|
+
let h1 = 3735928559;
|
|
83
|
+
let h2 = 1103547991;
|
|
84
|
+
for (let i = 0; i < input.length; i++) {
|
|
85
|
+
const ch = input.charCodeAt(i);
|
|
86
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
87
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
88
|
+
}
|
|
89
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
90
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
91
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
92
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
93
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compute the stable VCR request hash for a model call.
|
|
97
|
+
*
|
|
98
|
+
* The hash covers the full `messages` array plus the picked, normalized
|
|
99
|
+
* `options` subset (see {@link DEFAULT_HASH_OPTIONS}). Inputs are
|
|
100
|
+
* canonicalized (recursive key sort) before serialization so property order
|
|
101
|
+
* never affects the result. `signal` and unknown provider keys are excluded.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* const a = hashRequest(messages, { temperature: 0.2 });
|
|
105
|
+
* const b = hashRequest(messages, { temperature: 0.2, signal });
|
|
106
|
+
* // a === b — signal is excluded.
|
|
107
|
+
*/
|
|
108
|
+
function hashRequest(messages, options, hashOptions = DEFAULT_HASH_OPTIONS) {
|
|
109
|
+
const payload = canonicalize({
|
|
110
|
+
messages,
|
|
111
|
+
options: pickOptions(options, hashOptions)
|
|
112
|
+
});
|
|
113
|
+
return hashString(JSON.stringify(payload));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
export { DEFAULT_HASH_OPTIONS, hashRequest };
|
|
118
|
+
//# sourceMappingURL=hash-request.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-request.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/hash-request.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type { ModelCallOptions } from \"../contracts/model.contract\";\nimport type { ToolConfig } from \"../contracts/tool.contract\";\n\n/**\n * Default `ModelCallOptions` fields folded into the request hash. These are\n * the inputs that materially change the model's output; everything else\n * (notably `signal` and unknown provider keys) is excluded so an otherwise\n * identical logical call still matches its recording.\n */\nexport const DEFAULT_HASH_OPTIONS: readonly string[] = [\n \"temperature\",\n \"maxTokens\",\n \"responseSchema\",\n \"tools\",\n \"reasoning\",\n];\n\n/**\n * Reduce a tool to the parts the model actually conditions on: its name,\n * description, and the *shape* of its input schema. Two tools that differ\n * only by object identity (a fresh schema instance per import) hash\n * identically; a real contract change (renamed field, new description)\n * invalidates the recording.\n *\n * The input schema is fingerprinted structurally — a Standard Schema is an\n * opaque object, so we serialize its enumerable own keys rather than\n * attempting to read its internals.\n */\nfunction fingerprintTool(tool: ToolConfig<unknown, unknown>): unknown {\n return {\n name: tool.name,\n description: tool.description,\n input: tool.input ? schemaShape(tool.input) : undefined,\n };\n}\n\n/**\n * Produce a stable, JSON-safe fingerprint of an arbitrary schema object.\n * Records only the structural skeleton (own enumerable keys, recursively)\n * so harmless instance differences don't perturb the hash while a genuine\n * structural change does.\n */\nfunction schemaShape(value: unknown, depth = 0): unknown {\n if (depth > 6 || value === null || typeof value !== \"object\") {\n return typeof value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => schemaShape(item, depth + 1));\n }\n\n const out: Record<string, unknown> = {};\n\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = schemaShape((value as Record<string, unknown>)[key], depth + 1);\n }\n\n return out;\n}\n\n/**\n * Pick the hashable subset of `options`, normalizing `tools` into their\n * name+description+schema-shape fingerprint. `signal` and any field not in\n * `hashOptions` are dropped.\n */\nfunction pickOptions(\n options: ModelCallOptions | undefined,\n hashOptions: readonly string[],\n): Record<string, unknown> {\n if (!options) {\n return {};\n }\n\n const picked: Record<string, unknown> = {};\n\n for (const key of hashOptions) {\n const value = options[key];\n\n if (value === undefined) {\n continue;\n }\n\n if (key === \"tools\" && Array.isArray(value)) {\n picked[key] = (value as ToolConfig<unknown, unknown>[]).map(fingerprintTool);\n continue;\n }\n\n picked[key] = value;\n }\n\n return picked;\n}\n\n/**\n * Recursively sort object keys so two logically-equal payloads serialize to\n * byte-identical JSON regardless of property insertion order.\n */\nfunction canonicalize(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(canonicalize);\n }\n\n const out: Record<string, unknown> = {};\n\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = canonicalize((value as Record<string, unknown>)[key]);\n }\n\n return out;\n}\n\n/**\n * Non-cryptographic 53-bit string hash (FNV-style, cyrb53). Deterministic\n * across runs and platforms; collision-resistant enough for a per-cassette\n * keyspace. Returned as a base-36 string.\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 * Compute the stable VCR request hash for a model call.\n *\n * The hash covers the full `messages` array plus the picked, normalized\n * `options` subset (see {@link DEFAULT_HASH_OPTIONS}). Inputs are\n * canonicalized (recursive key sort) before serialization so property order\n * never affects the result. `signal` and unknown provider keys are excluded.\n *\n * @example\n * const a = hashRequest(messages, { temperature: 0.2 });\n * const b = hashRequest(messages, { temperature: 0.2, signal });\n * // a === b — signal is excluded.\n */\nexport function hashRequest(\n messages: Message[],\n options?: ModelCallOptions,\n hashOptions: readonly string[] = DEFAULT_HASH_OPTIONS,\n): string {\n const payload = canonicalize({\n messages,\n options: pickOptions(options, hashOptions),\n });\n\n return hashString(JSON.stringify(payload));\n}\n"],"mappings":";;;;;;;AAUA,MAAa,uBAA0C;CACrD;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;AAaA,SAAS,gBAAgB,MAA6C;CACpE,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,OAAO,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI;CAChD;AACF;;;;;;;AAQA,SAAS,YAAY,OAAgB,QAAQ,GAAY;CACvD,IAAI,QAAQ,KAAK,UAAU,QAAQ,OAAO,UAAU,UAClD,OAAO,OAAO;CAGhB,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,KAAK,SAAS,YAAY,MAAM,QAAQ,CAAC,CAAC;CAGzD,MAAM,MAA+B,CAAC;CAEtC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,CAAC,CAAC,KAAK,GACnE,IAAI,OAAO,YAAa,MAAkC,MAAM,QAAQ,CAAC;CAG3E,OAAO;AACT;;;;;;AAOA,SAAS,YACP,SACA,aACyB;CACzB,IAAI,CAAC,SACH,OAAO,CAAC;CAGV,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,OAAO,aAAa;EAC7B,MAAM,QAAQ,QAAQ;EAEtB,IAAI,UAAU,QACZ;EAGF,IAAI,QAAQ,WAAW,MAAM,QAAQ,KAAK,GAAG;GAC3C,OAAO,OAAQ,MAAyC,IAAI,eAAe;GAC3E;EACF;EAEA,OAAO,OAAO;CAChB;CAEA,OAAO;AACT;;;;;AAMA,SAAS,aAAa,OAAyB;CAC7C,IAAI,UAAU,QAAQ,OAAO,UAAU,UACrC,OAAO;CAGT,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,IAAI,YAAY;CAG/B,MAAM,MAA+B,CAAC;CAEtC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,CAAC,CAAC,KAAK,GACnE,IAAI,OAAO,aAAc,MAAkC,IAAI;CAGjE,OAAO;AACT;;;;;;AAOA,SAAS,WAAW,OAAuB;CACzC,IAAI,KAAK;CACT,IAAI,KAAK;CAET,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,KAAK,MAAM,WAAW,CAAC;EAE7B,KAAK,KAAK,KAAK,KAAK,IAAI,UAAU;EAClC,KAAK,KAAK,KAAK,KAAK,IAAI,UAAU;CACpC;CAEA,KAAK,KAAK,KAAK,KAAM,OAAO,IAAK,UAAU;CAC3C,MAAM,KAAK,KAAK,KAAM,OAAO,IAAK,UAAU;CAC5C,KAAK,KAAK,KAAK,KAAM,OAAO,IAAK,UAAU;CAC3C,MAAM,KAAK,KAAK,KAAM,OAAO,IAAK,UAAU;CAI5C,QAFiB,cAAc,UAAU,OAAO,OAAO,GAExC,CAAC,SAAS,EAAE;AAC7B;;;;;;;;;;;;;;AAeA,SAAgB,YACd,UACA,SACA,cAAiC,sBACzB;CACR,MAAM,UAAU,aAAa;EAC3B;EACA,SAAS,YAAY,SAAS,WAAW;CAC3C,CAAC;CAED,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC;AAC3C"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Cassette, CassetteEntry, VcrMode, VcrModel, VcrOptions } from "./vcr.type.mjs";
|
|
2
|
+
import { vcr } from "./vcr.mjs";
|
|
3
|
+
import { VcrCassetteMissError, VcrCassetteMissErrorOptions } from "./errors.mjs";
|
|
4
|
+
import { DEFAULT_HASH_OPTIONS, hashRequest } from "./hash-request.mjs";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ModelContract } from "../contracts/model.contract.mjs";
|
|
2
|
+
import { VcrModel, VcrOptions } from "./vcr.type.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/vcr/vcr.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Wrap any `ModelContract` in a record/replay decorator backed by a JSON
|
|
7
|
+
* cassette on disk.
|
|
8
|
+
*
|
|
9
|
+
* **What it does.** Intercepts only `complete()`/`stream()` — the single
|
|
10
|
+
* seam every agent trip funnels through — and delegates `name`, `provider`,
|
|
11
|
+
* `capabilities`, and `pricing` to the inner model untouched. On a call it
|
|
12
|
+
* computes a stable hash over `{ messages, picked options }` and, depending
|
|
13
|
+
* on `mode`:
|
|
14
|
+
*
|
|
15
|
+
* - **`record`** — always calls the inner model and appends a cassette entry.
|
|
16
|
+
* - **`replay`** — returns the matching entry (or re-yields its chunks /
|
|
17
|
+
* re-throws its error); a miss throws `VcrCassetteMissError`, never a live
|
|
18
|
+
* call.
|
|
19
|
+
* - **`auto`** (default) — replays a hit, records a miss.
|
|
20
|
+
*
|
|
21
|
+
* Composes *below* `fallbackModel` and works with any adapter because it
|
|
22
|
+
* depends only on `ModelContract`. Call `save()` to flush new entries.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const model = vcr(liveModel, { path: "./cassettes/support.json" });
|
|
26
|
+
* const response = await model.complete(messages);
|
|
27
|
+
* await model.save(); // first run records; later runs replay deterministically.
|
|
28
|
+
*/
|
|
29
|
+
declare function vcr(model: ModelContract, options: VcrOptions): VcrModel;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { vcr };
|
|
32
|
+
//# sourceMappingURL=vcr.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vcr.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/vcr.ts"],"mappings":";;;;;;AA8UA;;;;;;;;;;;;;;AAAwE;;;;;;;;iBAAxD,GAAA,CAAI,KAAA,EAAO,aAAA,EAAe,OAAA,EAAS,UAAA,GAAa,QAAA"}
|
package/esm/vcr/vcr.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { redact } from "../security/redact.mjs";
|
|
2
|
+
import { emptyCassette, loadCassette, saveCassette } from "./cassette-io.mjs";
|
|
3
|
+
import { VcrCassetteMissError } from "./errors.mjs";
|
|
4
|
+
import { DEFAULT_HASH_OPTIONS, hashRequest } from "./hash-request.mjs";
|
|
5
|
+
|
|
6
|
+
//#region ../@warlock.js/ai/src/vcr/vcr.ts
|
|
7
|
+
/**
|
|
8
|
+
* Internal decorator that wraps an inner `ModelContract`, intercepting only
|
|
9
|
+
* `complete()`/`stream()` and delegating every identity getter to the inner
|
|
10
|
+
* model. Drives the record/replay state machine over a single in-memory
|
|
11
|
+
* {@link Cassette}.
|
|
12
|
+
*
|
|
13
|
+
* **Why a class.** It holds mutable per-instance state (the loaded cassette,
|
|
14
|
+
* the dirty flag, the load promise) behind a stable `ModelContract` surface;
|
|
15
|
+
* the public API is the `vcr()` factory, never `new`.
|
|
16
|
+
*/
|
|
17
|
+
var Vcr = class {
|
|
18
|
+
constructor(inner, options) {
|
|
19
|
+
this.inner = inner;
|
|
20
|
+
this.dirty = false;
|
|
21
|
+
this.warnedVerbatim = false;
|
|
22
|
+
this.path = options.path;
|
|
23
|
+
this.mode = options.mode ?? "auto";
|
|
24
|
+
this.hashOptions = options.hashOptions ?? DEFAULT_HASH_OPTIONS;
|
|
25
|
+
this.recordRequest = options.recordRequest ?? "verbatim";
|
|
26
|
+
this.redactRequestHook = options.redactRequest;
|
|
27
|
+
this.redactResponseHook = options.redactResponse;
|
|
28
|
+
this.redactErrorHook = options.redactError;
|
|
29
|
+
this.loadedCassette = emptyCassette(inner.name, inner.provider);
|
|
30
|
+
}
|
|
31
|
+
/** Inner model identifier — delegated verbatim. */
|
|
32
|
+
get name() {
|
|
33
|
+
return this.inner.name;
|
|
34
|
+
}
|
|
35
|
+
/** Inner provider — delegated verbatim. */
|
|
36
|
+
get provider() {
|
|
37
|
+
return this.inner.provider;
|
|
38
|
+
}
|
|
39
|
+
/** Inner capability flags — delegated verbatim. */
|
|
40
|
+
get capabilities() {
|
|
41
|
+
return this.inner.capabilities;
|
|
42
|
+
}
|
|
43
|
+
/** Inner pricing — delegated verbatim so cost accounting is unchanged. */
|
|
44
|
+
get pricing() {
|
|
45
|
+
return this.inner.pricing;
|
|
46
|
+
}
|
|
47
|
+
/** Loaded/recorded cassette, exposed for assertions. */
|
|
48
|
+
get cassette() {
|
|
49
|
+
return this.loadedCassette;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Load the on-disk cassette exactly once. Pure `record` mode skips the
|
|
53
|
+
* read — it always writes fresh — but the in-memory cassette still starts
|
|
54
|
+
* empty so a record run never accidentally replays a stale entry.
|
|
55
|
+
*/
|
|
56
|
+
async ensureLoaded() {
|
|
57
|
+
if (this.loadPromise) return this.loadPromise;
|
|
58
|
+
this.loadPromise = this.mode === "record" ? Promise.resolve() : (async () => {
|
|
59
|
+
this.loadedCassette = await loadCassette(this.path, this.inner.name, this.inner.provider);
|
|
60
|
+
})();
|
|
61
|
+
return this.loadPromise;
|
|
62
|
+
}
|
|
63
|
+
/** Find a recorded entry whose hash matches the current request. */
|
|
64
|
+
findEntry(hash) {
|
|
65
|
+
return this.loadedCassette.entries.find((entry) => entry.requestHash === hash);
|
|
66
|
+
}
|
|
67
|
+
/** Re-throw a recorded error by reconstructing a plain `Error`. */
|
|
68
|
+
throwRecordedError(entry) {
|
|
69
|
+
const error = new Error(entry.error?.message ?? "Recorded error");
|
|
70
|
+
error.name = entry.error?.name ?? "Error";
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Non-streaming call. In `replay` a miss throws; in `auto`/`record` a miss
|
|
75
|
+
* calls the inner model and records the outcome (response or error).
|
|
76
|
+
*/
|
|
77
|
+
async complete(messages, options) {
|
|
78
|
+
await this.ensureLoaded();
|
|
79
|
+
const hash = hashRequest(messages, options, this.hashOptions);
|
|
80
|
+
if (this.mode !== "record") {
|
|
81
|
+
const entry = this.findEntry(hash);
|
|
82
|
+
if (entry) {
|
|
83
|
+
if (entry.error) this.throwRecordedError(entry);
|
|
84
|
+
if (entry.response) return entry.response;
|
|
85
|
+
}
|
|
86
|
+
if (this.mode === "replay") throw new VcrCassetteMissError(`No cassette entry for this request (model "${this.inner.name}", hash ${hash}).`, {
|
|
87
|
+
requestHash: hash,
|
|
88
|
+
path: this.path
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const response = await this.inner.complete(messages, options);
|
|
93
|
+
this.record({
|
|
94
|
+
requestHash: hash,
|
|
95
|
+
request: {
|
|
96
|
+
messages,
|
|
97
|
+
options
|
|
98
|
+
},
|
|
99
|
+
response
|
|
100
|
+
});
|
|
101
|
+
return response;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
this.record({
|
|
104
|
+
requestHash: hash,
|
|
105
|
+
request: {
|
|
106
|
+
messages,
|
|
107
|
+
options
|
|
108
|
+
},
|
|
109
|
+
error: {
|
|
110
|
+
name: error.name,
|
|
111
|
+
message: error.message
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Streaming call. On replay the stored `chunks` are re-yielded in order
|
|
119
|
+
* (reproducing the `delta`/`tool-call`/`done` sequence) or the stored
|
|
120
|
+
* error is re-thrown. On record the inner stream is buffered into
|
|
121
|
+
* `chunks[]` while being re-emitted, then recorded once exhausted.
|
|
122
|
+
*/
|
|
123
|
+
async *stream(messages, options) {
|
|
124
|
+
await this.ensureLoaded();
|
|
125
|
+
const hash = hashRequest(messages, options, this.hashOptions);
|
|
126
|
+
if (this.mode !== "record") {
|
|
127
|
+
const entry = this.findEntry(hash);
|
|
128
|
+
if (entry) {
|
|
129
|
+
if (entry.error) this.throwRecordedError(entry);
|
|
130
|
+
if (entry.chunks) {
|
|
131
|
+
for (const chunk of entry.chunks) yield chunk;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (this.mode === "replay") throw new VcrCassetteMissError(`No cassette entry for this request (model "${this.inner.name}", hash ${hash}).`, {
|
|
136
|
+
requestHash: hash,
|
|
137
|
+
path: this.path
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const chunks = [];
|
|
141
|
+
try {
|
|
142
|
+
for await (const chunk of this.inner.stream(messages, options)) {
|
|
143
|
+
chunks.push(chunk);
|
|
144
|
+
yield chunk;
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
this.record({
|
|
148
|
+
requestHash: hash,
|
|
149
|
+
request: {
|
|
150
|
+
messages,
|
|
151
|
+
options
|
|
152
|
+
},
|
|
153
|
+
error: {
|
|
154
|
+
name: error.name,
|
|
155
|
+
message: error.message
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
this.record({
|
|
161
|
+
requestHash: hash,
|
|
162
|
+
request: {
|
|
163
|
+
messages,
|
|
164
|
+
options
|
|
165
|
+
},
|
|
166
|
+
chunks
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Append an entry to the in-memory cassette and mark it dirty, applying
|
|
171
|
+
* the configured request/response/error redaction first (S2). Pure
|
|
172
|
+
* `replay` never reaches this path, so no replay run is ever dirtied.
|
|
173
|
+
*/
|
|
174
|
+
record(entry) {
|
|
175
|
+
this.loadedCassette.entries.push(this.applyRedaction(entry));
|
|
176
|
+
this.dirty = true;
|
|
177
|
+
this.maybeWarnVerbatim();
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Apply the persisted-body privacy controls to an entry before it is
|
|
181
|
+
* stored. The request body follows `recordRequest`; response/error
|
|
182
|
+
* redactors are applied only when supplied. Replay matching is by the
|
|
183
|
+
* recomputed hash (kept verbatim), so none of this affects replay.
|
|
184
|
+
*/
|
|
185
|
+
applyRedaction(entry) {
|
|
186
|
+
const out = {
|
|
187
|
+
requestHash: entry.requestHash,
|
|
188
|
+
request: entry.request
|
|
189
|
+
};
|
|
190
|
+
if (this.recordRequest === "hash-only") out.request = { messages: [] };
|
|
191
|
+
else if (this.recordRequest === "redacted") out.request = this.redactRequestHook ? this.redactRequestHook(entry.request) : redact(entry.request);
|
|
192
|
+
if (entry.response) out.response = this.redactResponseHook ? this.redactResponseHook(entry.response) : entry.response;
|
|
193
|
+
if (entry.chunks) out.chunks = entry.chunks;
|
|
194
|
+
if (entry.error) out.error = this.redactErrorHook ? this.redactErrorHook(entry.error) : entry.error;
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Warn once (outside tests) when the cassette is recording verbatim
|
|
199
|
+
* request bodies — they may carry prompts, tool args, and PII, so the
|
|
200
|
+
* file is not safe to commit until sanitized.
|
|
201
|
+
*/
|
|
202
|
+
maybeWarnVerbatim() {
|
|
203
|
+
if (this.warnedVerbatim || this.recordRequest !== "verbatim") return;
|
|
204
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") return;
|
|
205
|
+
this.warnedVerbatim = true;
|
|
206
|
+
console.warn(`[warlock-ai] VCR is recording verbatim request bodies to "${this.path}" — prompts, tool args, and any PII are stored unredacted. Sanitize before committing, or set recordRequest: "redacted" | "hash-only".`);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Flush newly recorded entries to `path`. No-op when nothing was recorded
|
|
210
|
+
* (pure replay, or a record/auto run that only ever hit cached entries).
|
|
211
|
+
*/
|
|
212
|
+
async save() {
|
|
213
|
+
if (!this.dirty) return;
|
|
214
|
+
await saveCassette(this.path, this.loadedCassette);
|
|
215
|
+
this.dirty = false;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Wrap any `ModelContract` in a record/replay decorator backed by a JSON
|
|
220
|
+
* cassette on disk.
|
|
221
|
+
*
|
|
222
|
+
* **What it does.** Intercepts only `complete()`/`stream()` — the single
|
|
223
|
+
* seam every agent trip funnels through — and delegates `name`, `provider`,
|
|
224
|
+
* `capabilities`, and `pricing` to the inner model untouched. On a call it
|
|
225
|
+
* computes a stable hash over `{ messages, picked options }` and, depending
|
|
226
|
+
* on `mode`:
|
|
227
|
+
*
|
|
228
|
+
* - **`record`** — always calls the inner model and appends a cassette entry.
|
|
229
|
+
* - **`replay`** — returns the matching entry (or re-yields its chunks /
|
|
230
|
+
* re-throws its error); a miss throws `VcrCassetteMissError`, never a live
|
|
231
|
+
* call.
|
|
232
|
+
* - **`auto`** (default) — replays a hit, records a miss.
|
|
233
|
+
*
|
|
234
|
+
* Composes *below* `fallbackModel` and works with any adapter because it
|
|
235
|
+
* depends only on `ModelContract`. Call `save()` to flush new entries.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* const model = vcr(liveModel, { path: "./cassettes/support.json" });
|
|
239
|
+
* const response = await model.complete(messages);
|
|
240
|
+
* await model.save(); // first run records; later runs replay deterministically.
|
|
241
|
+
*/
|
|
242
|
+
function vcr(model, options) {
|
|
243
|
+
return new Vcr(model, options);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
export { vcr };
|
|
248
|
+
//# sourceMappingURL=vcr.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vcr.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/vcr.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type {\n ModelCallOptions,\n ModelCapabilities,\n ModelContract,\n ModelResponse,\n ModelStreamChunk,\n} from \"../contracts/model.contract\";\nimport type { ModelPricing } from \"../contracts/result/model-pricing.type\";\nimport { redact } from \"../security/redact\";\nimport { emptyCassette, loadCassette, saveCassette } from \"./cassette-io\";\nimport { VcrCassetteMissError } from \"./errors\";\nimport { DEFAULT_HASH_OPTIONS, hashRequest } from \"./hash-request\";\nimport type { Cassette, CassetteEntry, VcrMode, VcrModel, VcrOptions } from \"./vcr.type\";\n\n/**\n * Internal decorator that wraps an inner `ModelContract`, intercepting only\n * `complete()`/`stream()` and delegating every identity getter to the inner\n * model. Drives the record/replay state machine over a single in-memory\n * {@link Cassette}.\n *\n * **Why a class.** It holds mutable per-instance state (the loaded cassette,\n * the dirty flag, the load promise) behind a stable `ModelContract` surface;\n * the public API is the `vcr()` factory, never `new`.\n */\nclass Vcr implements VcrModel {\n private readonly mode: VcrMode;\n private readonly path: string;\n private readonly hashOptions: readonly string[];\n\n /** Loaded + newly recorded entries. Mutated in place as we record. */\n private loadedCassette: Cassette;\n\n /** Set when an entry is recorded so `save()` knows there's work to flush. */\n private dirty = false;\n\n /** One-shot lazy load of the on-disk cassette, shared across calls. */\n private loadPromise: Promise<void> | undefined;\n\n /** Persisted-body privacy controls (S2). */\n private readonly recordRequest: NonNullable<VcrOptions[\"recordRequest\"]>;\n private readonly redactRequestHook: VcrOptions[\"redactRequest\"];\n private readonly redactResponseHook: VcrOptions[\"redactResponse\"];\n private readonly redactErrorHook: VcrOptions[\"redactError\"];\n\n /** Verbatim-recording warning fires at most once per instance. */\n private warnedVerbatim = false;\n\n public constructor(\n private readonly inner: ModelContract,\n options: VcrOptions,\n ) {\n this.path = options.path;\n this.mode = options.mode ?? \"auto\";\n this.hashOptions = options.hashOptions ?? DEFAULT_HASH_OPTIONS;\n this.recordRequest = options.recordRequest ?? \"verbatim\";\n this.redactRequestHook = options.redactRequest;\n this.redactResponseHook = options.redactResponse;\n this.redactErrorHook = options.redactError;\n this.loadedCassette = emptyCassette(inner.name, inner.provider);\n }\n\n /** Inner model identifier — delegated verbatim. */\n public get name(): string {\n return this.inner.name;\n }\n\n /** Inner provider — delegated verbatim. */\n public get provider(): string {\n return this.inner.provider;\n }\n\n /** Inner capability flags — delegated verbatim. */\n public get capabilities(): ModelCapabilities | undefined {\n return this.inner.capabilities;\n }\n\n /** Inner pricing — delegated verbatim so cost accounting is unchanged. */\n public get pricing(): ModelPricing | undefined {\n return this.inner.pricing;\n }\n\n /** Loaded/recorded cassette, exposed for assertions. */\n public get cassette(): Cassette {\n return this.loadedCassette;\n }\n\n /**\n * Load the on-disk cassette exactly once. Pure `record` mode skips the\n * read — it always writes fresh — but the in-memory cassette still starts\n * empty so a record run never accidentally replays a stale entry.\n */\n private async ensureLoaded(): Promise<void> {\n if (this.loadPromise) {\n return this.loadPromise;\n }\n\n this.loadPromise =\n this.mode === \"record\"\n ? Promise.resolve()\n : (async () => {\n this.loadedCassette = await loadCassette(\n this.path,\n this.inner.name,\n this.inner.provider,\n );\n })();\n\n return this.loadPromise;\n }\n\n /** Find a recorded entry whose hash matches the current request. */\n private findEntry(hash: string): CassetteEntry | undefined {\n return this.loadedCassette.entries.find((entry) => entry.requestHash === hash);\n }\n\n /** Re-throw a recorded error by reconstructing a plain `Error`. */\n private throwRecordedError(entry: CassetteEntry): never {\n const error = new Error(entry.error?.message ?? \"Recorded error\");\n\n error.name = entry.error?.name ?? \"Error\";\n\n throw error;\n }\n\n /**\n * Non-streaming call. In `replay` a miss throws; in `auto`/`record` a miss\n * calls the inner model and records the outcome (response or error).\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n await this.ensureLoaded();\n\n const hash = hashRequest(messages, options, this.hashOptions);\n\n if (this.mode !== \"record\") {\n const entry = this.findEntry(hash);\n\n if (entry) {\n if (entry.error) {\n this.throwRecordedError(entry);\n }\n\n if (entry.response) {\n return entry.response;\n }\n }\n\n if (this.mode === \"replay\") {\n throw new VcrCassetteMissError(\n `No cassette entry for this request (model \"${this.inner.name}\", hash ${hash}).`,\n { requestHash: hash, path: this.path },\n );\n }\n }\n\n try {\n const response = await this.inner.complete(messages, options);\n\n this.record({ requestHash: hash, request: { messages, options }, response });\n\n return response;\n } catch (error) {\n this.record({\n requestHash: hash,\n request: { messages, options },\n error: { name: (error as Error).name, message: (error as Error).message },\n });\n\n throw error;\n }\n }\n\n /**\n * Streaming call. On replay the stored `chunks` are re-yielded in order\n * (reproducing the `delta`/`tool-call`/`done` sequence) or the stored\n * error is re-thrown. On record the inner stream is buffered into\n * `chunks[]` while being re-emitted, then recorded once exhausted.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n await this.ensureLoaded();\n\n const hash = hashRequest(messages, options, this.hashOptions);\n\n if (this.mode !== \"record\") {\n const entry = this.findEntry(hash);\n\n if (entry) {\n if (entry.error) {\n this.throwRecordedError(entry);\n }\n\n if (entry.chunks) {\n for (const chunk of entry.chunks) {\n yield chunk;\n }\n\n return;\n }\n }\n\n if (this.mode === \"replay\") {\n throw new VcrCassetteMissError(\n `No cassette entry for this request (model \"${this.inner.name}\", hash ${hash}).`,\n { requestHash: hash, path: this.path },\n );\n }\n }\n\n const chunks: ModelStreamChunk[] = [];\n\n try {\n for await (const chunk of this.inner.stream(messages, options)) {\n chunks.push(chunk);\n\n yield chunk;\n }\n } catch (error) {\n this.record({\n requestHash: hash,\n request: { messages, options },\n error: { name: (error as Error).name, message: (error as Error).message },\n });\n\n throw error;\n }\n\n this.record({ requestHash: hash, request: { messages, options }, chunks });\n }\n\n /**\n * Append an entry to the in-memory cassette and mark it dirty, applying\n * the configured request/response/error redaction first (S2). Pure\n * `replay` never reaches this path, so no replay run is ever dirtied.\n */\n private record(entry: CassetteEntry): void {\n this.loadedCassette.entries.push(this.applyRedaction(entry));\n this.dirty = true;\n this.maybeWarnVerbatim();\n }\n\n /**\n * Apply the persisted-body privacy controls to an entry before it is\n * stored. The request body follows `recordRequest`; response/error\n * redactors are applied only when supplied. Replay matching is by the\n * recomputed hash (kept verbatim), so none of this affects replay.\n */\n private applyRedaction(entry: CassetteEntry): CassetteEntry {\n const out: CassetteEntry = {\n requestHash: entry.requestHash,\n request: entry.request,\n };\n\n if (this.recordRequest === \"hash-only\") {\n out.request = { messages: [] };\n } else if (this.recordRequest === \"redacted\") {\n out.request = this.redactRequestHook\n ? this.redactRequestHook(entry.request)\n : redact(entry.request);\n }\n\n if (entry.response) {\n out.response = this.redactResponseHook\n ? this.redactResponseHook(entry.response)\n : entry.response;\n }\n if (entry.chunks) {\n out.chunks = entry.chunks;\n }\n if (entry.error) {\n out.error = this.redactErrorHook\n ? this.redactErrorHook(entry.error)\n : entry.error;\n }\n\n return out;\n }\n\n /**\n * Warn once (outside tests) when the cassette is recording verbatim\n * request bodies — they may carry prompts, tool args, and PII, so the\n * file is not safe to commit until sanitized.\n */\n private maybeWarnVerbatim(): void {\n if (this.warnedVerbatim || this.recordRequest !== \"verbatim\") return;\n if (process.env.VITEST || process.env.NODE_ENV === \"test\") return;\n\n this.warnedVerbatim = true;\n console.warn(\n `[warlock-ai] VCR is recording verbatim request bodies to \"${this.path}\" — prompts, tool args, and any PII are stored unredacted. ` +\n 'Sanitize before committing, or set recordRequest: \"redacted\" | \"hash-only\".',\n );\n }\n\n /**\n * Flush newly recorded entries to `path`. No-op when nothing was recorded\n * (pure replay, or a record/auto run that only ever hit cached entries).\n */\n public async save(): Promise<void> {\n if (!this.dirty) {\n return;\n }\n\n await saveCassette(this.path, this.loadedCassette);\n this.dirty = false;\n }\n}\n\n/**\n * Wrap any `ModelContract` in a record/replay decorator backed by a JSON\n * cassette on disk.\n *\n * **What it does.** Intercepts only `complete()`/`stream()` — the single\n * seam every agent trip funnels through — and delegates `name`, `provider`,\n * `capabilities`, and `pricing` to the inner model untouched. On a call it\n * computes a stable hash over `{ messages, picked options }` and, depending\n * on `mode`:\n *\n * - **`record`** — always calls the inner model and appends a cassette entry.\n * - **`replay`** — returns the matching entry (or re-yields its chunks /\n * re-throws its error); a miss throws `VcrCassetteMissError`, never a live\n * call.\n * - **`auto`** (default) — replays a hit, records a miss.\n *\n * Composes *below* `fallbackModel` and works with any adapter because it\n * depends only on `ModelContract`. Call `save()` to flush new entries.\n *\n * @example\n * const model = vcr(liveModel, { path: \"./cassettes/support.json\" });\n * const response = await model.complete(messages);\n * await model.save(); // first run records; later runs replay deterministically.\n */\nexport function vcr(model: ModelContract, options: VcrOptions): VcrModel {\n return new Vcr(model, options);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Message } from "../contracts/conversation-message.type.mjs";
|
|
2
|
+
import { ModelCallOptions, ModelContract, ModelResponse, ModelStreamChunk } from "../contracts/model.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/vcr/vcr.type.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Record/replay mode for a {@link VcrModel}.
|
|
7
|
+
*
|
|
8
|
+
* - **`record`** — always call the inner model and append the result to
|
|
9
|
+
* the cassette. Never replays. Use to (re)capture a fresh cassette.
|
|
10
|
+
* - **`replay`** — never call the inner model. A cassette hit returns the
|
|
11
|
+
* stored response/chunks/error; a miss throws `VcrCassetteMissError`
|
|
12
|
+
* (never a silent live call). Use in CI for deterministic, offline tests.
|
|
13
|
+
* - **`auto`** (default) — replay on a cassette hit, record on a miss.
|
|
14
|
+
* The friendliest mode for local dev: records once, replays thereafter.
|
|
15
|
+
*/
|
|
16
|
+
type VcrMode = "record" | "replay" | "auto";
|
|
17
|
+
/**
|
|
18
|
+
* A single recorded request → response pair on a {@link Cassette}.
|
|
19
|
+
*
|
|
20
|
+
* Exactly one of `response` / `chunks` / `error` is populated, mirroring
|
|
21
|
+
* the three outcomes of a model call: a non-streaming reply, a streamed
|
|
22
|
+
* chunk sequence, or a thrown provider error.
|
|
23
|
+
*/
|
|
24
|
+
type CassetteEntry = {
|
|
25
|
+
/** Stable hash of the normalized request (messages + hashed options). */requestHash: string;
|
|
26
|
+
/**
|
|
27
|
+
* What was sent — stored verbatim for human readability and so the
|
|
28
|
+
* cassette can be re-hashed if the hashing format ever changes.
|
|
29
|
+
*/
|
|
30
|
+
request: {
|
|
31
|
+
messages: Message[];
|
|
32
|
+
options?: ModelCallOptions;
|
|
33
|
+
}; /** A non-streaming reply, recorded from `complete()`. */
|
|
34
|
+
response?: ModelResponse; /** A streaming reply, captured from `stream()` as the ordered chunk list. */
|
|
35
|
+
chunks?: ModelStreamChunk[]; /** A thrown provider error, replayed by re-throwing a reconstructed `Error`. */
|
|
36
|
+
error?: {
|
|
37
|
+
name: string;
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* On-disk cassette format: the inner model identity plus every recorded
|
|
43
|
+
* {@link CassetteEntry}. Serialized as JSON to {@link VcrOptions.path}.
|
|
44
|
+
*/
|
|
45
|
+
type Cassette = {
|
|
46
|
+
/** Cassette schema version. Bump only on a breaking format change. */version: 1; /** Inner `model.name` at record time — informational. */
|
|
47
|
+
model: string; /** Inner `model.provider` at record time — informational. */
|
|
48
|
+
provider: string; /** Recorded request → response pairs, in capture order. */
|
|
49
|
+
entries: CassetteEntry[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Options for {@link vcr}.
|
|
53
|
+
*/
|
|
54
|
+
type VcrOptions = {
|
|
55
|
+
/** Cassette file path (JSON). Read on construct, written on `save()`. */path: string; /** Record/replay behavior. Default `"auto"`. */
|
|
56
|
+
mode?: VcrMode;
|
|
57
|
+
/**
|
|
58
|
+
* Fields of `ModelCallOptions` to include in the request hash. Default
|
|
59
|
+
* `["temperature","maxTokens","responseSchema","tools","reasoning"]`.
|
|
60
|
+
* `signal` and unknown provider keys are always excluded so an
|
|
61
|
+
* otherwise-identical logical call still matches. `tools` are hashed by
|
|
62
|
+
* name + description + input-schema shape, not object identity.
|
|
63
|
+
*/
|
|
64
|
+
hashOptions?: string[];
|
|
65
|
+
/**
|
|
66
|
+
* How the request BODY is persisted (S2). Replay matching is by the
|
|
67
|
+
* recomputed hash, never the stored body — so privacy modes don't break
|
|
68
|
+
* replay. Default `"verbatim"`.
|
|
69
|
+
*
|
|
70
|
+
* - `"verbatim"` — store the full request (prompts, tool args, options)
|
|
71
|
+
* for human-readable diffs and re-hashing. Records a loud one-time
|
|
72
|
+
* warning (outside tests) since the body may carry PII / secrets.
|
|
73
|
+
* - `"redacted"` — run the request through {@link redactRequest}, or —
|
|
74
|
+
* without one — the key-based `redact()` (strips structured secrets
|
|
75
|
+
* like API keys in options; free-text prompts need a custom hook).
|
|
76
|
+
* - `"hash-only"` — drop the request body entirely; keep only the hash
|
|
77
|
+
* and the outcome. Maximum privacy; loses human readability and the
|
|
78
|
+
* ability to re-hash.
|
|
79
|
+
*/
|
|
80
|
+
recordRequest?: "verbatim" | "redacted" | "hash-only";
|
|
81
|
+
/**
|
|
82
|
+
* Custom request redactor for `recordRequest: "redacted"`. Receives the
|
|
83
|
+
* `{ messages, options }` about to be stored; return a sanitized copy.
|
|
84
|
+
* Overrides the default key-based `redact()`.
|
|
85
|
+
*/
|
|
86
|
+
redactRequest?: (request: {
|
|
87
|
+
messages: Message[];
|
|
88
|
+
options?: ModelCallOptions;
|
|
89
|
+
}) => {
|
|
90
|
+
messages: Message[];
|
|
91
|
+
options?: ModelCallOptions;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Optional redactor for the recorded RESPONSE. Off by default — redacting
|
|
95
|
+
* the response changes what replay returns, so opt in only when a secret
|
|
96
|
+
* leaks into model output (e.g. an echoed token).
|
|
97
|
+
*/
|
|
98
|
+
redactResponse?: (response: ModelResponse) => ModelResponse; /** Optional redactor for a recorded error's `{ name, message }`. */
|
|
99
|
+
redactError?: (error: {
|
|
100
|
+
name: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}) => {
|
|
103
|
+
name: string;
|
|
104
|
+
message: string;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A `ModelContract` decorator returned by {@link vcr}. Delegates
|
|
109
|
+
* `name`/`provider`/`capabilities`/`pricing` to the inner model and
|
|
110
|
+
* intercepts only `complete()`/`stream()`.
|
|
111
|
+
*/
|
|
112
|
+
type VcrModel = ModelContract & {
|
|
113
|
+
/** Flush newly recorded entries to `path`. No-op in pure replay. */save(): Promise<void>; /** Recorded/loaded entries, exposed for assertions. */
|
|
114
|
+
readonly cassette: Cassette;
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { Cassette, CassetteEntry, VcrMode, VcrModel, VcrOptions };
|
|
118
|
+
//# sourceMappingURL=vcr.type.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vcr.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/vcr/vcr.type.ts"],"mappings":";;;;;;AAmBA;;;;AAAmB;AASnB;;;;KATY,OAAA;;;;;;;;KASA,aAAA;EASR,yEAPF,WAAA;EAUA;;;;EALA,OAAA;IACE,QAAA,EAAU,OAAA;IACV,OAAA,GAAU,gBAAA;EAAA,GASH;EANT,QAAA,GAAW,aAAA,EAcO;EAZlB,MAAA,GAAS,gBAAA,IAoBa;EAlBtB,KAAA;IACE,IAAA;IACA,OAAA;EAAA;AAAA;;AAgBoB;AAMxB;;KAdY,QAAA;EAkBH,sEAhBP,OAAA,KAgDY;EA9CZ,KAAA,UA+CuC;EA7CvC,QAAA,UAmD8C;EAjD9C,OAAA,EAAS,aAAa;AAAA;;;;KAMZ,UAAA;EA4BV,yEA1BA,IAAA,UAiCE;EA/BF,IAAA,GAAO,OAAA;EAgCL;;;;;;;EAxBF,WAAA;EA+B4B;;;;;;;;;;AAInB;AASX;;;;EA5BE,aAAA;EAgCmB;;;;;EA1BnB,aAAA,IAAiB,OAAA;IACf,QAAA,EAAU,OAAA;IACV,OAAA,GAAU,gBAAA;EAAA;IACJ,QAAA,EAAU,OAAA;IAAW,OAAA,GAAU,gBAAA;EAAA;;;;;;EAMvC,cAAA,IAAkB,QAAA,EAAU,aAAA,KAAkB,aAAA;EAE9C,WAAA,IAAe,KAAA;IAAS,IAAA;IAAc,OAAA;EAAA;IACpC,IAAA;IACA,OAAA;EAAA;AAAA;;;;;;KASQ,QAAA,GAAW,aAAA;sEAErB,IAAA,IAAQ,OAAA;WAEC,QAAA,EAAU,QAAA;AAAA"}
|