@warlock.js/ai 4.4.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 -55
- package/cjs/index.cjs +221 -14147
- 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 +8 -0
- 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/supervisor-result.type.d.mts +14 -2
- package/esm/contracts/result/supervisor-result.type.d.mts.map +1 -1
- package/esm/contracts/result/workflow-result.type.d.mts +2 -1
- 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/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,134 @@
|
|
|
1
|
+
import { OPENAI_INSTALL_INSTRUCTIONS } from "../errors.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/guard/detectors/moderation.ts
|
|
4
|
+
const DETECTOR_NAME = "moderation.openai";
|
|
5
|
+
const DEFAULT_MODEL = "omni-moderation-latest";
|
|
6
|
+
let OpenAiSdk;
|
|
7
|
+
let isModuleExists;
|
|
8
|
+
let loadingPromise;
|
|
9
|
+
/**
|
|
10
|
+
* Settle the lazy import of `openai` once, concurrency-safe. Only needed
|
|
11
|
+
* when the caller did not pass a ready `client`. A bare `catch` flips the
|
|
12
|
+
* flag to `false`; the curated {@link OPENAI_INSTALL_INSTRUCTIONS} surfaces
|
|
13
|
+
* at first `check()`, never a raw module-resolution stack trace. Mirrors
|
|
14
|
+
* ai-panoptic's `loadLangfuse`.
|
|
15
|
+
*/
|
|
16
|
+
function loadOpenAi() {
|
|
17
|
+
if (isModuleExists !== void 0) return Promise.resolve();
|
|
18
|
+
if (loadingPromise) return loadingPromise;
|
|
19
|
+
loadingPromise = (async () => {
|
|
20
|
+
try {
|
|
21
|
+
OpenAiSdk = await import("openai");
|
|
22
|
+
isModuleExists = true;
|
|
23
|
+
} catch {
|
|
24
|
+
isModuleExists = false;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
return loadingPromise;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The optional OpenAI-backed moderation detector — the internal class behind
|
|
31
|
+
* the {@link moderation} factory. Sends the inspected text to OpenAI's
|
|
32
|
+
* moderation endpoint and maps the flagged categories to a verdict: any
|
|
33
|
+
* category in `blockOn` → `block`; any other flagged category → `flag`;
|
|
34
|
+
* nothing flagged → `allow`.
|
|
35
|
+
*
|
|
36
|
+
* The `openai` SDK is resolved lazily on the FIRST `check()` (not at
|
|
37
|
+
* construction) so importing `@warlock.js/ai` never forces the peer to
|
|
38
|
+
* be installed. When a `client` is supplied it is used verbatim and the SDK
|
|
39
|
+
* is never imported.
|
|
40
|
+
*/
|
|
41
|
+
var OpenAiModerationDetector = class {
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.name = DETECTOR_NAME;
|
|
44
|
+
this.client = options.client;
|
|
45
|
+
this.apiKey = options.apiKey;
|
|
46
|
+
this.model = options.model ?? DEFAULT_MODEL;
|
|
47
|
+
this.blockOn = new Set(options.blockOn ?? []);
|
|
48
|
+
if (!this.client) loadOpenAi();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Moderate `text` and fold the response into a verdict. `allow` when the
|
|
52
|
+
* model flags nothing; `block` when any flagged category is in `blockOn`;
|
|
53
|
+
* otherwise `flag` listing every flagged category. Resolving the client
|
|
54
|
+
* throws the curated install string when the `openai` peer is absent.
|
|
55
|
+
*/
|
|
56
|
+
async check(text) {
|
|
57
|
+
const result = (await (await this.resolveClient()).moderations.create({
|
|
58
|
+
model: this.model,
|
|
59
|
+
input: text
|
|
60
|
+
})).results[0];
|
|
61
|
+
if (result === void 0 || !result.flagged) return { type: "allow" };
|
|
62
|
+
return this.toVerdict(result);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Return the supplied client, or construct one lazily from the resolved
|
|
66
|
+
* SDK. Throws {@link OPENAI_INSTALL_INSTRUCTIONS} (a plain `Error` — a
|
|
67
|
+
* missing optional peer is an infrastructure fault, not a content
|
|
68
|
+
* violation) when `openai` could not be imported.
|
|
69
|
+
*/
|
|
70
|
+
async resolveClient() {
|
|
71
|
+
if (this.client) return this.client;
|
|
72
|
+
await loadOpenAi();
|
|
73
|
+
if (!isModuleExists) throw new Error(OPENAI_INSTALL_INSTRUCTIONS);
|
|
74
|
+
this.client = new OpenAiSdk.default({ apiKey: this.apiKey });
|
|
75
|
+
return this.client;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fold a flagged moderation result into a `block` or `flag` verdict. Every
|
|
79
|
+
* `true` category becomes a {@link GuardrailMatch} (`moderation.<category>`);
|
|
80
|
+
* the verdict is `block` when any flagged category is in `blockOn`,
|
|
81
|
+
* otherwise `flag`.
|
|
82
|
+
*/
|
|
83
|
+
toVerdict(result) {
|
|
84
|
+
const flagged = Object.entries(result.categories).filter(([, tripped]) => tripped).map(([category]) => category);
|
|
85
|
+
const matches = flagged.map((category) => ({
|
|
86
|
+
rule: `moderation.${category}`,
|
|
87
|
+
label: category
|
|
88
|
+
}));
|
|
89
|
+
const shouldBlock = flagged.some((category) => this.blockOn.has(category));
|
|
90
|
+
const list = flagged.join(", ");
|
|
91
|
+
if (shouldBlock) return {
|
|
92
|
+
type: "block",
|
|
93
|
+
reason: `OpenAI moderation flagged blocked category(ies): ${list}.`,
|
|
94
|
+
matches
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
type: "flag",
|
|
98
|
+
reason: `OpenAI moderation flagged category(ies): ${list}.`,
|
|
99
|
+
matches
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Build the optional `moderation` detector (surfaced as
|
|
105
|
+
* `ai.guardrail.moderation(options?)`), backed by OpenAI's moderation
|
|
106
|
+
* endpoint. The `openai` SDK is an **optional lazy peer**: importing
|
|
107
|
+
* `@warlock.js/ai` never forces it to resolve, and the detector throws
|
|
108
|
+
* a curated install string ({@link OPENAI_INSTALL_INSTRUCTIONS}) on first
|
|
109
|
+
* `check()` when the peer is absent — mirroring ai-panoptic's lazy Langfuse
|
|
110
|
+
* exporter.
|
|
111
|
+
*
|
|
112
|
+
* On a moderation hit, every flagged category becomes a
|
|
113
|
+
* {@link GuardrailMatch}; the verdict is `block` when any flagged category is
|
|
114
|
+
* listed in `blockOn`, otherwise `flag`. A clean result is `allow`.
|
|
115
|
+
*
|
|
116
|
+
* @param options - `apiKey` (defaults to `OPENAI_API_KEY`), `model`
|
|
117
|
+
* (defaults to `"omni-moderation-latest"`), `blockOn` (categories that
|
|
118
|
+
* escalate to `block`), or a pre-built `client` to bypass the lazy import.
|
|
119
|
+
* @returns A {@link GuardrailDetector} for the guard's `input` / `output` / `tool` arrays.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* const guard = ai.guardrail({
|
|
123
|
+
* output: [
|
|
124
|
+
* ai.guardrail.moderation({ blockOn: ["violence", "sexual/minors"] }),
|
|
125
|
+
* ],
|
|
126
|
+
* });
|
|
127
|
+
*/
|
|
128
|
+
function moderation(options) {
|
|
129
|
+
return new OpenAiModerationDetector(options);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { moderation };
|
|
134
|
+
//# sourceMappingURL=moderation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderation.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/guard/detectors/moderation.ts"],"sourcesContent":["import type {\n GuardrailDetector,\n GuardrailMatch,\n GuardrailVerdict,\n OpenAiClientLike,\n OpenAiModerationOptions,\n OpenAiModerationResult,\n} from \"../contracts\";\nimport { OPENAI_INSTALL_INSTRUCTIONS } from \"../errors\";\n\nconst DETECTOR_NAME = \"moderation.openai\";\n\nconst DEFAULT_MODEL = \"omni-moderation-latest\";\n\n// ============================================================\n// Lazily-loaded openai SDK (OPTIONAL peer)\n// ============================================================\n\nlet OpenAiSdk: typeof import(\"openai\");\nlet isModuleExists: boolean | undefined;\nlet loadingPromise: Promise<void> | undefined;\n\n/**\n * Settle the lazy import of `openai` once, concurrency-safe. Only needed\n * when the caller did not pass a ready `client`. A bare `catch` flips the\n * flag to `false`; the curated {@link OPENAI_INSTALL_INSTRUCTIONS} surfaces\n * at first `check()`, never a raw module-resolution stack trace. Mirrors\n * ai-panoptic's `loadLangfuse`.\n */\nfunction loadOpenAi(): Promise<void> {\n if (isModuleExists !== undefined) {\n return Promise.resolve();\n }\n\n if (loadingPromise) {\n return loadingPromise;\n }\n\n loadingPromise = (async () => {\n try {\n OpenAiSdk = await import(\"openai\");\n isModuleExists = true;\n } catch {\n isModuleExists = false;\n }\n })();\n\n return loadingPromise;\n}\n\n/**\n * The optional OpenAI-backed moderation detector — the internal class behind\n * the {@link moderation} factory. Sends the inspected text to OpenAI's\n * moderation endpoint and maps the flagged categories to a verdict: any\n * category in `blockOn` → `block`; any other flagged category → `flag`;\n * nothing flagged → `allow`.\n *\n * The `openai` SDK is resolved lazily on the FIRST `check()` (not at\n * construction) so importing `@warlock.js/ai` never forces the peer to\n * be installed. When a `client` is supplied it is used verbatim and the SDK\n * is never imported.\n */\nclass OpenAiModerationDetector implements GuardrailDetector {\n public readonly name = DETECTOR_NAME;\n\n /** A pre-built client, or `undefined` until the lazy SDK constructs one. */\n private client: OpenAiClientLike | undefined;\n\n private readonly apiKey: string | undefined;\n\n private readonly model: string;\n\n /** Categories that escalate to `block`; empty means \"flag on any\". */\n private readonly blockOn: ReadonlySet<string>;\n\n public constructor(options: OpenAiModerationOptions = {}) {\n this.client = options.client;\n this.apiKey = options.apiKey;\n this.model = options.model ?? DEFAULT_MODEL;\n this.blockOn = new Set(options.blockOn ?? []);\n\n // Kick off the lazy import eagerly when no client was supplied, so the\n // first `check()` does not pay the resolution latency. Errors are\n // swallowed by `loadOpenAi`; the curated install string surfaces at use.\n if (!this.client) {\n loadOpenAi();\n }\n }\n\n /**\n * Moderate `text` and fold the response into a verdict. `allow` when the\n * model flags nothing; `block` when any flagged category is in `blockOn`;\n * otherwise `flag` listing every flagged category. Resolving the client\n * throws the curated install string when the `openai` peer is absent.\n */\n public async check(text: string): Promise<GuardrailVerdict> {\n const client = await this.resolveClient();\n\n const response = await client.moderations.create({\n model: this.model,\n input: text,\n });\n\n const result = response.results[0];\n\n if (result === undefined || !result.flagged) {\n return { type: \"allow\" };\n }\n\n return this.toVerdict(result);\n }\n\n /**\n * Return the supplied client, or construct one lazily from the resolved\n * SDK. Throws {@link OPENAI_INSTALL_INSTRUCTIONS} (a plain `Error` — a\n * missing optional peer is an infrastructure fault, not a content\n * violation) when `openai` could not be imported.\n */\n private async resolveClient(): Promise<OpenAiClientLike> {\n if (this.client) {\n return this.client;\n }\n\n await loadOpenAi();\n\n if (!isModuleExists) {\n throw new Error(OPENAI_INSTALL_INSTRUCTIONS);\n }\n\n this.client = new OpenAiSdk.default({\n apiKey: this.apiKey,\n }) as unknown as OpenAiClientLike;\n\n return this.client;\n }\n\n /**\n * Fold a flagged moderation result into a `block` or `flag` verdict. Every\n * `true` category becomes a {@link GuardrailMatch} (`moderation.<category>`);\n * the verdict is `block` when any flagged category is in `blockOn`,\n * otherwise `flag`.\n */\n private toVerdict(result: OpenAiModerationResult): GuardrailVerdict {\n const flagged = Object.entries(result.categories)\n .filter(([, tripped]) => tripped)\n .map(([category]) => category);\n\n const matches: GuardrailMatch[] = flagged.map((category) => ({\n rule: `moderation.${category}`,\n label: category,\n }));\n\n const shouldBlock = flagged.some((category) => this.blockOn.has(category));\n const list = flagged.join(\", \");\n\n if (shouldBlock) {\n return {\n type: \"block\",\n reason: `OpenAI moderation flagged blocked category(ies): ${list}.`,\n matches,\n };\n }\n\n return {\n type: \"flag\",\n reason: `OpenAI moderation flagged category(ies): ${list}.`,\n matches,\n };\n }\n}\n\n/**\n * Build the optional `moderation` detector (surfaced as\n * `ai.guardrail.moderation(options?)`), backed by OpenAI's moderation\n * endpoint. The `openai` SDK is an **optional lazy peer**: importing\n * `@warlock.js/ai` never forces it to resolve, and the detector throws\n * a curated install string ({@link OPENAI_INSTALL_INSTRUCTIONS}) on first\n * `check()` when the peer is absent — mirroring ai-panoptic's lazy Langfuse\n * exporter.\n *\n * On a moderation hit, every flagged category becomes a\n * {@link GuardrailMatch}; the verdict is `block` when any flagged category is\n * listed in `blockOn`, otherwise `flag`. A clean result is `allow`.\n *\n * @param options - `apiKey` (defaults to `OPENAI_API_KEY`), `model`\n * (defaults to `\"omni-moderation-latest\"`), `blockOn` (categories that\n * escalate to `block`), or a pre-built `client` to bypass the lazy import.\n * @returns A {@link GuardrailDetector} for the guard's `input` / `output` / `tool` arrays.\n *\n * @example\n * const guard = ai.guardrail({\n * output: [\n * ai.guardrail.moderation({ blockOn: [\"violence\", \"sexual/minors\"] }),\n * ],\n * });\n */\nexport function moderation(\n options?: OpenAiModerationOptions,\n): GuardrailDetector {\n return new OpenAiModerationDetector(options);\n}\n"],"mappings":";;;AAUA,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AAMtB,IAAI;AACJ,IAAI;AACJ,IAAI;;;;;;;;AASJ,SAAS,aAA4B;CACnC,IAAI,mBAAmB,QACrB,OAAO,QAAQ,QAAQ;CAGzB,IAAI,gBACF,OAAO;CAGT,kBAAkB,YAAY;EAC5B,IAAI;GACF,YAAY,MAAM,OAAO;GACzB,iBAAiB;EACnB,QAAQ;GACN,iBAAiB;EACnB;CACF,EAAC,CAAE;CAEH,OAAO;AACT;;;;;;;;;;;;;AAcA,IAAM,2BAAN,MAA4D;CAa1D,AAAO,YAAY,UAAmC,CAAC,GAAG;cAZnC;EAarB,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,QAAQ;EACtB,KAAK,QAAQ,QAAQ,SAAS;EAC9B,KAAK,UAAU,IAAI,IAAI,QAAQ,WAAW,CAAC,CAAC;EAK5C,IAAI,CAAC,KAAK,QACR,WAAW;CAEf;;;;;;;CAQA,MAAa,MAAM,MAAyC;EAQ1D,MAAM,UAAS,OALQ,MAFF,KAAK,cAAc,EAEX,CAAC,YAAY,OAAO;GAC/C,OAAO,KAAK;GACZ,OAAO;EACT,CAAC,EAEsB,CAAC,QAAQ;EAEhC,IAAI,WAAW,UAAa,CAAC,OAAO,SAClC,OAAO,EAAE,MAAM,QAAQ;EAGzB,OAAO,KAAK,UAAU,MAAM;CAC9B;;;;;;;CAQA,MAAc,gBAA2C;EACvD,IAAI,KAAK,QACP,OAAO,KAAK;EAGd,MAAM,WAAW;EAEjB,IAAI,CAAC,gBACH,MAAM,IAAI,MAAM,2BAA2B;EAG7C,KAAK,SAAS,IAAI,UAAU,QAAQ,EAClC,QAAQ,KAAK,OACf,CAAC;EAED,OAAO,KAAK;CACd;;;;;;;CAQA,AAAQ,UAAU,QAAkD;EAClE,MAAM,UAAU,OAAO,QAAQ,OAAO,UAAU,CAAC,CAC9C,QAAQ,GAAG,aAAa,OAAO,CAAC,CAChC,KAAK,CAAC,cAAc,QAAQ;EAE/B,MAAM,UAA4B,QAAQ,KAAK,cAAc;GAC3D,MAAM,cAAc;GACpB,OAAO;EACT,EAAE;EAEF,MAAM,cAAc,QAAQ,MAAM,aAAa,KAAK,QAAQ,IAAI,QAAQ,CAAC;EACzE,MAAM,OAAO,QAAQ,KAAK,IAAI;EAE9B,IAAI,aACF,OAAO;GACL,MAAM;GACN,QAAQ,oDAAoD,KAAK;GACjE;EACF;EAGF,OAAO;GACL,MAAM;GACN,QAAQ,4CAA4C,KAAK;GACzD;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,WACd,SACmB;CACnB,OAAO,IAAI,yBAAyB,OAAO;AAC7C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SyncGuardrailDetector } from "../contracts/guardrail.contract.mjs";
|
|
2
|
+
import { PiiDetectorOptions } from "../contracts/guard-options.type.mjs";
|
|
3
|
+
//#region ../@warlock.js/ai/src/guard/detectors/pii.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the built-in **PII detector** (`ai.guardrail.pii`) — a zero-runtime-
|
|
6
|
+
* dependency {@link GuardrailDetector} that scans text for personally
|
|
7
|
+
* identifiable information via a curated set of linear regexes plus an
|
|
8
|
+
* optional exact-string dictionary.
|
|
9
|
+
*
|
|
10
|
+
* Categories (`detect`, default: all): `ssn`, `email`, `phone`,
|
|
11
|
+
* `credit-card`, `ipv4`. `dictionary` adds extra exact terms matched
|
|
12
|
+
* case-insensitively as literal strings (regex metacharacters escaped).
|
|
13
|
+
*
|
|
14
|
+
* On a hit the verdict follows `onMatch` (default `"redact"`):
|
|
15
|
+
*
|
|
16
|
+
* - **`redact`** — every matched span is replaced by the `mask` template
|
|
17
|
+
* (`{label}` → the matched category, default `"[REDACTED]"`) and the
|
|
18
|
+
* rewritten text is returned for the factory to substitute. Output and
|
|
19
|
+
* tool phases honour the rewrite; on the input phase the factory treats a
|
|
20
|
+
* `redact` verdict as a `block` (the core `trip.before` hook can only
|
|
21
|
+
* short-circuit, not rewrite-and-continue — see {@link PiiDetectorOptions}).
|
|
22
|
+
* - **`block`** — a hard stop carrying the matches.
|
|
23
|
+
* - **`flag`** — the content passes but the matches are recorded.
|
|
24
|
+
*
|
|
25
|
+
* Clean text returns `{ type: "allow" }`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ai.guardrail({ output: [ai.guardrail.pii()] }); // redact, default mask
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ai.guardrail.pii({
|
|
32
|
+
* detect: ["ssn", "credit-card"],
|
|
33
|
+
* onMatch: "redact",
|
|
34
|
+
* mask: "[PII:{label}]",
|
|
35
|
+
* dictionary: ["Project Aurora"],
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
declare function pii(options?: PiiDetectorOptions): SyncGuardrailDetector;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { pii };
|
|
41
|
+
//# sourceMappingURL=pii.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pii.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/guard/detectors/pii.ts"],"mappings":";;;;;;;AAqOA;;;;;;;;AAA4E;;;;;;;;;;;;;;;;;;;;;;iBAA5D,GAAA,CAAI,OAAA,GAAS,kBAAA,GAA0B,qBAAqB"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai/src/guard/detectors/pii.ts
|
|
2
|
+
/** Detector name, used as the namespace prefix on every {@link GuardrailMatch.rule}. */
|
|
3
|
+
const DETECTOR_NAME = "pii";
|
|
4
|
+
/** Placeholder substituted for a matched span when the caller supplies no `mask`. */
|
|
5
|
+
const DEFAULT_MASK = "[REDACTED]";
|
|
6
|
+
/**
|
|
7
|
+
* The built-in PII category regexes. Each is linear (anchored alternations,
|
|
8
|
+
* no nested quantifiers) so it is safe against catastrophic backtracking on
|
|
9
|
+
* adversarial input. All carry the global flag so a single pass over the
|
|
10
|
+
* text yields every occurrence; `lastIndex` is reset per use so a shared
|
|
11
|
+
* instance never leaks state across calls.
|
|
12
|
+
*
|
|
13
|
+
* - `ssn` — US Social Security number, `123-45-6789` / `123 45 6789`.
|
|
14
|
+
* - `email` — a pragmatic address shape, not full RFC 5322.
|
|
15
|
+
* - `phone` — North-American style, optional `+1`, separators, parens.
|
|
16
|
+
* - `credit-card` — 13–16 digit runs, optional space / hyphen grouping.
|
|
17
|
+
* - `ipv4` — four dotted octets (loosely; out-of-range octets still match).
|
|
18
|
+
*/
|
|
19
|
+
const CATEGORY_PATTERNS = {
|
|
20
|
+
ssn: /\b\d{3}[-\s]\d{2}[-\s]\d{4}\b/g,
|
|
21
|
+
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
22
|
+
phone: /(?:\+?1[-.\s]?)?(?:\(\d{3}\)|\d{3})[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
23
|
+
"credit-card": /\b(?:\d[ -]?){13,16}\b/g,
|
|
24
|
+
ipv4: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g
|
|
25
|
+
};
|
|
26
|
+
/** Every built-in category, in a stable scan order. */
|
|
27
|
+
const ALL_CATEGORIES = [
|
|
28
|
+
"ssn",
|
|
29
|
+
"email",
|
|
30
|
+
"phone",
|
|
31
|
+
"credit-card",
|
|
32
|
+
"ipv4"
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Escape a string for safe interpolation into a `RegExp` source, so an
|
|
36
|
+
* extra dictionary term containing regex metacharacters (`.`, `+`, `(`, …)
|
|
37
|
+
* matches literally rather than as a pattern.
|
|
38
|
+
*/
|
|
39
|
+
function escapeRegExp(term) {
|
|
40
|
+
return term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the `{label}` mask for a hit. The template's `{label}` token is
|
|
44
|
+
* substituted with the hit's category; a template without the token is used
|
|
45
|
+
* verbatim. Falls back to {@link DEFAULT_MASK} when no template is given.
|
|
46
|
+
*/
|
|
47
|
+
function applyMask(template, label) {
|
|
48
|
+
if (template === void 0) return DEFAULT_MASK;
|
|
49
|
+
return template.replace(/\{label\}/g, label);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Collect every built-in-category hit in `text` for the requested
|
|
53
|
+
* categories, in document order per category.
|
|
54
|
+
*/
|
|
55
|
+
function scanCategories(text, categories) {
|
|
56
|
+
const hits = [];
|
|
57
|
+
for (const category of categories) {
|
|
58
|
+
const pattern = CATEGORY_PATTERNS[category];
|
|
59
|
+
pattern.lastIndex = 0;
|
|
60
|
+
let match = pattern.exec(text);
|
|
61
|
+
while (match !== null) {
|
|
62
|
+
hits.push({
|
|
63
|
+
label: category,
|
|
64
|
+
start: match.index,
|
|
65
|
+
end: match.index + match[0].length - 1
|
|
66
|
+
});
|
|
67
|
+
if (match[0].length === 0) pattern.lastIndex += 1;
|
|
68
|
+
match = pattern.exec(text);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return hits;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Collect every occurrence of each extra dictionary term in `text`,
|
|
75
|
+
* case-insensitively, as `"dictionary"`-labelled hits.
|
|
76
|
+
*/
|
|
77
|
+
function scanDictionary(text, dictionary) {
|
|
78
|
+
const hits = [];
|
|
79
|
+
for (const term of dictionary) {
|
|
80
|
+
if (term.length === 0) continue;
|
|
81
|
+
const pattern = new RegExp(escapeRegExp(term), "gi");
|
|
82
|
+
let match = pattern.exec(text);
|
|
83
|
+
while (match !== null) {
|
|
84
|
+
hits.push({
|
|
85
|
+
label: "dictionary",
|
|
86
|
+
start: match.index,
|
|
87
|
+
end: match.index + match[0].length - 1
|
|
88
|
+
});
|
|
89
|
+
match = pattern.exec(text);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return hits;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sort hits by start offset, then drop any hit fully contained in (or
|
|
96
|
+
* duplicating) an already-kept span. Different category regexes can overlap
|
|
97
|
+
* on the same characters (e.g. a credit-card run inside a phone-shaped
|
|
98
|
+
* span); keeping the earliest, widest span makes redaction deterministic
|
|
99
|
+
* and avoids masking a sub-span twice.
|
|
100
|
+
*/
|
|
101
|
+
function dedupeHits(hits) {
|
|
102
|
+
const sorted = [...hits].sort((a, b) => {
|
|
103
|
+
if (a.start !== b.start) return a.start - b.start;
|
|
104
|
+
return b.end - a.end;
|
|
105
|
+
});
|
|
106
|
+
const kept = [];
|
|
107
|
+
for (const hit of sorted) if (!kept.some((existing) => hit.start <= existing.end && hit.end >= existing.start)) kept.push(hit);
|
|
108
|
+
return kept;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Rewrite `text`, replacing every kept hit's span with its mask. Applied
|
|
112
|
+
* right-to-left so earlier offsets stay valid as later spans are spliced.
|
|
113
|
+
*/
|
|
114
|
+
function redactText(text, hits, mask) {
|
|
115
|
+
const ordered = [...hits].sort((a, b) => b.start - a.start);
|
|
116
|
+
let result = text;
|
|
117
|
+
for (const hit of ordered) {
|
|
118
|
+
const replacement = applyMask(mask, hit.label);
|
|
119
|
+
result = result.slice(0, hit.start) + replacement + result.slice(hit.end + 1);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/** Fold a {@link RawHit} into the public {@link GuardrailMatch} shape. */
|
|
124
|
+
function toMatch(hit) {
|
|
125
|
+
return {
|
|
126
|
+
rule: `${DETECTOR_NAME}.${hit.label}`,
|
|
127
|
+
span: [hit.start, hit.end],
|
|
128
|
+
label: hit.label
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build the built-in **PII detector** (`ai.guardrail.pii`) — a zero-runtime-
|
|
133
|
+
* dependency {@link GuardrailDetector} that scans text for personally
|
|
134
|
+
* identifiable information via a curated set of linear regexes plus an
|
|
135
|
+
* optional exact-string dictionary.
|
|
136
|
+
*
|
|
137
|
+
* Categories (`detect`, default: all): `ssn`, `email`, `phone`,
|
|
138
|
+
* `credit-card`, `ipv4`. `dictionary` adds extra exact terms matched
|
|
139
|
+
* case-insensitively as literal strings (regex metacharacters escaped).
|
|
140
|
+
*
|
|
141
|
+
* On a hit the verdict follows `onMatch` (default `"redact"`):
|
|
142
|
+
*
|
|
143
|
+
* - **`redact`** — every matched span is replaced by the `mask` template
|
|
144
|
+
* (`{label}` → the matched category, default `"[REDACTED]"`) and the
|
|
145
|
+
* rewritten text is returned for the factory to substitute. Output and
|
|
146
|
+
* tool phases honour the rewrite; on the input phase the factory treats a
|
|
147
|
+
* `redact` verdict as a `block` (the core `trip.before` hook can only
|
|
148
|
+
* short-circuit, not rewrite-and-continue — see {@link PiiDetectorOptions}).
|
|
149
|
+
* - **`block`** — a hard stop carrying the matches.
|
|
150
|
+
* - **`flag`** — the content passes but the matches are recorded.
|
|
151
|
+
*
|
|
152
|
+
* Clean text returns `{ type: "allow" }`.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ai.guardrail({ output: [ai.guardrail.pii()] }); // redact, default mask
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ai.guardrail.pii({
|
|
159
|
+
* detect: ["ssn", "credit-card"],
|
|
160
|
+
* onMatch: "redact",
|
|
161
|
+
* mask: "[PII:{label}]",
|
|
162
|
+
* dictionary: ["Project Aurora"],
|
|
163
|
+
* });
|
|
164
|
+
*/
|
|
165
|
+
function pii(options = {}) {
|
|
166
|
+
const categories = options.detect ?? ALL_CATEGORIES;
|
|
167
|
+
const onMatch = options.onMatch ?? "redact";
|
|
168
|
+
const dictionary = options.dictionary ?? [];
|
|
169
|
+
return {
|
|
170
|
+
name: DETECTOR_NAME,
|
|
171
|
+
check(text) {
|
|
172
|
+
const rawHits = [...scanCategories(text, categories), ...scanDictionary(text, dictionary)];
|
|
173
|
+
if (rawHits.length === 0) return { type: "allow" };
|
|
174
|
+
const hits = dedupeHits(rawHits);
|
|
175
|
+
const matches = hits.map(toMatch);
|
|
176
|
+
const labels = [...new Set(hits.map((hit) => hit.label))].join(", ");
|
|
177
|
+
if (onMatch === "block") return {
|
|
178
|
+
type: "block",
|
|
179
|
+
reason: `PII detected: ${labels}.`,
|
|
180
|
+
matches
|
|
181
|
+
};
|
|
182
|
+
if (onMatch === "flag") return {
|
|
183
|
+
type: "flag",
|
|
184
|
+
reason: `PII detected: ${labels}.`,
|
|
185
|
+
matches
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
type: "redact",
|
|
189
|
+
text: redactText(text, hits, options.mask),
|
|
190
|
+
reason: `Redacted PII: ${labels}.`,
|
|
191
|
+
matches
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { pii };
|
|
199
|
+
//# sourceMappingURL=pii.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pii.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/guard/detectors/pii.ts"],"sourcesContent":["import type {\n GuardrailMatch,\n GuardrailVerdict,\n PiiCategory,\n PiiDetectorOptions,\n SyncGuardrailDetector,\n} from \"../contracts\";\n\n/** Detector name, used as the namespace prefix on every {@link GuardrailMatch.rule}. */\nconst DETECTOR_NAME = \"pii\";\n\n/** Placeholder substituted for a matched span when the caller supplies no `mask`. */\nconst DEFAULT_MASK = \"[REDACTED]\";\n\n/**\n * The built-in PII category regexes. Each is linear (anchored alternations,\n * no nested quantifiers) so it is safe against catastrophic backtracking on\n * adversarial input. All carry the global flag so a single pass over the\n * text yields every occurrence; `lastIndex` is reset per use so a shared\n * instance never leaks state across calls.\n *\n * - `ssn` — US Social Security number, `123-45-6789` / `123 45 6789`.\n * - `email` — a pragmatic address shape, not full RFC 5322.\n * - `phone` — North-American style, optional `+1`, separators, parens.\n * - `credit-card` — 13–16 digit runs, optional space / hyphen grouping.\n * - `ipv4` — four dotted octets (loosely; out-of-range octets still match).\n */\nconst CATEGORY_PATTERNS: Record<PiiCategory, RegExp> = {\n ssn: /\\b\\d{3}[-\\s]\\d{2}[-\\s]\\d{4}\\b/g,\n email: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n phone: /(?:\\+?1[-.\\s]?)?(?:\\(\\d{3}\\)|\\d{3})[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g,\n \"credit-card\": /\\b(?:\\d[ -]?){13,16}\\b/g,\n ipv4: /\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/g,\n};\n\n/** Every built-in category, in a stable scan order. */\nconst ALL_CATEGORIES: readonly PiiCategory[] = [\n \"ssn\",\n \"email\",\n \"phone\",\n \"credit-card\",\n \"ipv4\",\n];\n\n/**\n * A raw hit located inside the inspected text, before it is folded into a\n * {@link GuardrailMatch}. `label` is the category (built-in) or\n * `\"dictionary\"` (an extra term); `start` / `end` are inclusive offsets.\n */\ninterface RawHit {\n readonly label: string;\n readonly start: number;\n readonly end: number;\n}\n\n/**\n * Escape a string for safe interpolation into a `RegExp` source, so an\n * extra dictionary term containing regex metacharacters (`.`, `+`, `(`, …)\n * matches literally rather than as a pattern.\n */\nfunction escapeRegExp(term: string): string {\n return term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Build the `{label}` mask for a hit. The template's `{label}` token is\n * substituted with the hit's category; a template without the token is used\n * verbatim. Falls back to {@link DEFAULT_MASK} when no template is given.\n */\nfunction applyMask(template: string | undefined, label: string): string {\n if (template === undefined) {\n return DEFAULT_MASK;\n }\n\n return template.replace(/\\{label\\}/g, label);\n}\n\n/**\n * Collect every built-in-category hit in `text` for the requested\n * categories, in document order per category.\n */\nfunction scanCategories(text: string, categories: readonly PiiCategory[]): RawHit[] {\n const hits: RawHit[] = [];\n\n for (const category of categories) {\n const pattern = CATEGORY_PATTERNS[category];\n pattern.lastIndex = 0;\n\n let match = pattern.exec(text);\n\n while (match !== null) {\n hits.push({\n label: category,\n start: match.index,\n end: match.index + match[0].length - 1,\n });\n\n // Guard the zero-length-match case so `exec` can never spin forever.\n if (match[0].length === 0) {\n pattern.lastIndex += 1;\n }\n\n match = pattern.exec(text);\n }\n }\n\n return hits;\n}\n\n/**\n * Collect every occurrence of each extra dictionary term in `text`,\n * case-insensitively, as `\"dictionary\"`-labelled hits.\n */\nfunction scanDictionary(text: string, dictionary: readonly string[]): RawHit[] {\n const hits: RawHit[] = [];\n\n for (const term of dictionary) {\n if (term.length === 0) {\n continue;\n }\n\n const pattern = new RegExp(escapeRegExp(term), \"gi\");\n let match = pattern.exec(text);\n\n while (match !== null) {\n hits.push({\n label: \"dictionary\",\n start: match.index,\n end: match.index + match[0].length - 1,\n });\n\n match = pattern.exec(text);\n }\n }\n\n return hits;\n}\n\n/**\n * Sort hits by start offset, then drop any hit fully contained in (or\n * duplicating) an already-kept span. Different category regexes can overlap\n * on the same characters (e.g. a credit-card run inside a phone-shaped\n * span); keeping the earliest, widest span makes redaction deterministic\n * and avoids masking a sub-span twice.\n */\nfunction dedupeHits(hits: RawHit[]): RawHit[] {\n const sorted = [...hits].sort((a, b) => {\n if (a.start !== b.start) {\n return a.start - b.start;\n }\n\n // Same start: keep the wider span first so the narrower one is absorbed.\n return b.end - a.end;\n });\n\n const kept: RawHit[] = [];\n\n for (const hit of sorted) {\n const overlaps = kept.some(\n existing => hit.start <= existing.end && hit.end >= existing.start,\n );\n\n if (!overlaps) {\n kept.push(hit);\n }\n }\n\n return kept;\n}\n\n/**\n * Rewrite `text`, replacing every kept hit's span with its mask. Applied\n * right-to-left so earlier offsets stay valid as later spans are spliced.\n */\nfunction redactText(text: string, hits: RawHit[], mask: string | undefined): string {\n const ordered = [...hits].sort((a, b) => b.start - a.start);\n let result = text;\n\n for (const hit of ordered) {\n const replacement = applyMask(mask, hit.label);\n result = result.slice(0, hit.start) + replacement + result.slice(hit.end + 1);\n }\n\n return result;\n}\n\n/** Fold a {@link RawHit} into the public {@link GuardrailMatch} shape. */\nfunction toMatch(hit: RawHit): GuardrailMatch {\n return {\n rule: `${DETECTOR_NAME}.${hit.label}`,\n span: [hit.start, hit.end],\n label: hit.label,\n };\n}\n\n/**\n * Build the built-in **PII detector** (`ai.guardrail.pii`) — a zero-runtime-\n * dependency {@link GuardrailDetector} that scans text for personally\n * identifiable information via a curated set of linear regexes plus an\n * optional exact-string dictionary.\n *\n * Categories (`detect`, default: all): `ssn`, `email`, `phone`,\n * `credit-card`, `ipv4`. `dictionary` adds extra exact terms matched\n * case-insensitively as literal strings (regex metacharacters escaped).\n *\n * On a hit the verdict follows `onMatch` (default `\"redact\"`):\n *\n * - **`redact`** — every matched span is replaced by the `mask` template\n * (`{label}` → the matched category, default `\"[REDACTED]\"`) and the\n * rewritten text is returned for the factory to substitute. Output and\n * tool phases honour the rewrite; on the input phase the factory treats a\n * `redact` verdict as a `block` (the core `trip.before` hook can only\n * short-circuit, not rewrite-and-continue — see {@link PiiDetectorOptions}).\n * - **`block`** — a hard stop carrying the matches.\n * - **`flag`** — the content passes but the matches are recorded.\n *\n * Clean text returns `{ type: \"allow\" }`.\n *\n * @example\n * ai.guardrail({ output: [ai.guardrail.pii()] }); // redact, default mask\n *\n * @example\n * ai.guardrail.pii({\n * detect: [\"ssn\", \"credit-card\"],\n * onMatch: \"redact\",\n * mask: \"[PII:{label}]\",\n * dictionary: [\"Project Aurora\"],\n * });\n */\nexport function pii(options: PiiDetectorOptions = {}): SyncGuardrailDetector {\n const categories = options.detect ?? ALL_CATEGORIES;\n const onMatch = options.onMatch ?? \"redact\";\n const dictionary = options.dictionary ?? [];\n\n return {\n name: DETECTOR_NAME,\n check(text: string): GuardrailVerdict {\n const rawHits = [\n ...scanCategories(text, categories),\n ...scanDictionary(text, dictionary),\n ];\n\n if (rawHits.length === 0) {\n return { type: \"allow\" };\n }\n\n const hits = dedupeHits(rawHits);\n const matches = hits.map(toMatch);\n const labels = [...new Set(hits.map(hit => hit.label))].join(\", \");\n\n if (onMatch === \"block\") {\n return {\n type: \"block\",\n reason: `PII detected: ${labels}.`,\n matches,\n };\n }\n\n if (onMatch === \"flag\") {\n return {\n type: \"flag\",\n reason: `PII detected: ${labels}.`,\n matches,\n };\n }\n\n return {\n type: \"redact\",\n text: redactText(text, hits, options.mask),\n reason: `Redacted PII: ${labels}.`,\n matches,\n };\n },\n };\n}\n"],"mappings":";;AASA,MAAM,gBAAgB;;AAGtB,MAAM,eAAe;;;;;;;;;;;;;;AAerB,MAAM,oBAAiD;CACrD,KAAK;CACL,OAAO;CACP,OAAO;CACP,eAAe;CACf,MAAM;AACR;;AAGA,MAAM,iBAAyC;CAC7C;CACA;CACA;CACA;CACA;AACF;;;;;;AAkBA,SAAS,aAAa,MAAsB;CAC1C,OAAO,KAAK,QAAQ,uBAAuB,MAAM;AACnD;;;;;;AAOA,SAAS,UAAU,UAA8B,OAAuB;CACtE,IAAI,aAAa,QACf,OAAO;CAGT,OAAO,SAAS,QAAQ,cAAc,KAAK;AAC7C;;;;;AAMA,SAAS,eAAe,MAAc,YAA8C;CAClF,MAAM,OAAiB,CAAC;CAExB,KAAK,MAAM,YAAY,YAAY;EACjC,MAAM,UAAU,kBAAkB;EAClC,QAAQ,YAAY;EAEpB,IAAI,QAAQ,QAAQ,KAAK,IAAI;EAE7B,OAAO,UAAU,MAAM;GACrB,KAAK,KAAK;IACR,OAAO;IACP,OAAO,MAAM;IACb,KAAK,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS;GACvC,CAAC;GAGD,IAAI,MAAM,EAAE,CAAC,WAAW,GACtB,QAAQ,aAAa;GAGvB,QAAQ,QAAQ,KAAK,IAAI;EAC3B;CACF;CAEA,OAAO;AACT;;;;;AAMA,SAAS,eAAe,MAAc,YAAyC;CAC7E,MAAM,OAAiB,CAAC;CAExB,KAAK,MAAM,QAAQ,YAAY;EAC7B,IAAI,KAAK,WAAW,GAClB;EAGF,MAAM,UAAU,IAAI,OAAO,aAAa,IAAI,GAAG,IAAI;EACnD,IAAI,QAAQ,QAAQ,KAAK,IAAI;EAE7B,OAAO,UAAU,MAAM;GACrB,KAAK,KAAK;IACR,OAAO;IACP,OAAO,MAAM;IACb,KAAK,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS;GACvC,CAAC;GAED,QAAQ,QAAQ,KAAK,IAAI;EAC3B;CACF;CAEA,OAAO;AACT;;;;;;;;AASA,SAAS,WAAW,MAA0B;CAC5C,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;EACtC,IAAI,EAAE,UAAU,EAAE,OAChB,OAAO,EAAE,QAAQ,EAAE;EAIrB,OAAO,EAAE,MAAM,EAAE;CACnB,CAAC;CAED,MAAM,OAAiB,CAAC;CAExB,KAAK,MAAM,OAAO,QAKhB,IAAI,CAJa,KAAK,MACpB,aAAY,IAAI,SAAS,SAAS,OAAO,IAAI,OAAO,SAAS,KAGnD,GACV,KAAK,KAAK,GAAG;CAIjB,OAAO;AACT;;;;;AAMA,SAAS,WAAW,MAAc,MAAgB,MAAkC;CAClF,MAAM,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CAC1D,IAAI,SAAS;CAEb,KAAK,MAAM,OAAO,SAAS;EACzB,MAAM,cAAc,UAAU,MAAM,IAAI,KAAK;EAC7C,SAAS,OAAO,MAAM,GAAG,IAAI,KAAK,IAAI,cAAc,OAAO,MAAM,IAAI,MAAM,CAAC;CAC9E;CAEA,OAAO;AACT;;AAGA,SAAS,QAAQ,KAA6B;CAC5C,OAAO;EACL,MAAM,GAAG,cAAc,GAAG,IAAI;EAC9B,MAAM,CAAC,IAAI,OAAO,IAAI,GAAG;EACzB,OAAO,IAAI;CACb;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,IAAI,UAA8B,CAAC,GAA0B;CAC3E,MAAM,aAAa,QAAQ,UAAU;CACrC,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,aAAa,QAAQ,cAAc,CAAC;CAE1C,OAAO;EACL,MAAM;EACN,MAAM,MAAgC;GACpC,MAAM,UAAU,CACd,GAAG,eAAe,MAAM,UAAU,GAClC,GAAG,eAAe,MAAM,UAAU,CACpC;GAEA,IAAI,QAAQ,WAAW,GACrB,OAAO,EAAE,MAAM,QAAQ;GAGzB,MAAM,OAAO,WAAW,OAAO;GAC/B,MAAM,UAAU,KAAK,IAAI,OAAO;GAChC,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,KAAI,QAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;GAEjE,IAAI,YAAY,SACd,OAAO;IACL,MAAM;IACN,QAAQ,iBAAiB,OAAO;IAChC;GACF;GAGF,IAAI,YAAY,QACd,OAAO;IACL,MAAM;IACN,QAAQ,iBAAiB,OAAO;IAChC;GACF;GAGF,OAAO;IACL,MAAM;IACN,MAAM,WAAW,MAAM,MAAM,QAAQ,IAAI;IACzC,QAAQ,iBAAiB,OAAO;IAChC;GACF;EACF;CACF;AACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SyncGuardrailDetector } from "../contracts/guardrail.contract.mjs";
|
|
2
|
+
import { TopicFilterOptions } from "../contracts/guard-options.type.mjs";
|
|
3
|
+
//#region ../@warlock.js/ai/src/guard/detectors/topic.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the built-in **topic filter** (`ai.guardrail.topic`) — a
|
|
6
|
+
* zero-runtime-dependency {@link GuardrailDetector} that gates text against a
|
|
7
|
+
* deny list, an allow list, or both.
|
|
8
|
+
*
|
|
9
|
+
* - **`deny`** — any term that appears triggers `onMatch`. A `string`
|
|
10
|
+
* matches case-insensitively as a substring; a `RegExp` is tested as-is.
|
|
11
|
+
* The deny list is checked first; the first hit decides the verdict.
|
|
12
|
+
* - **`allow`** — when set, text matching **none** of the allow terms
|
|
13
|
+
* triggers `onMatch` (an allow-list miss). Text matching at least one
|
|
14
|
+
* allow term passes the allow gate.
|
|
15
|
+
*
|
|
16
|
+
* `onMatch` is `"block"` (default) or `"flag"`. With neither list supplied
|
|
17
|
+
* the detector is a no-op that always allows.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ai.guardrail.topic({ deny: ["medical advice", /diagnos\w+/i] });
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Stay on-topic: anything not about billing is flagged.
|
|
24
|
+
* ai.guardrail.topic({ allow: ["billing", "invoice", "refund"], onMatch: "flag" });
|
|
25
|
+
*/
|
|
26
|
+
declare function topic(options: TopicFilterOptions): SyncGuardrailDetector;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { topic };
|
|
29
|
+
//# sourceMappingURL=topic.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topic.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/guard/detectors/topic.ts"],"mappings":";;;;;;;AAsEA;;;;;;;;AAAyE;;;;;;;;;;iBAAzD,KAAA,CAAM,OAAA,EAAS,kBAAA,GAAqB,qBAAqB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai/src/guard/detectors/topic.ts
|
|
2
|
+
/** Detector name, used as the namespace prefix on every {@link GuardrailMatch.rule}. */
|
|
3
|
+
const DETECTOR_NAME = "topic";
|
|
4
|
+
/**
|
|
5
|
+
* Locate the first occurrence of `term` in `text`. A `string` matches
|
|
6
|
+
* case-insensitively as a substring; a `RegExp` is tested as-is (its own
|
|
7
|
+
* flags decide case-sensitivity). Returns the inclusive `[start, end]`
|
|
8
|
+
* span on a hit, or `undefined` when the term is absent.
|
|
9
|
+
*/
|
|
10
|
+
function locate(text, term) {
|
|
11
|
+
if (typeof term === "string") {
|
|
12
|
+
if (term.length === 0) return;
|
|
13
|
+
const index = text.toLowerCase().indexOf(term.toLowerCase());
|
|
14
|
+
if (index === -1) return;
|
|
15
|
+
return [index, index + term.length - 1];
|
|
16
|
+
}
|
|
17
|
+
const match = new RegExp(term.source, term.flags.replace(/[gy]/g, "")).exec(text);
|
|
18
|
+
if (match === null) return;
|
|
19
|
+
return [match.index, match.index + match[0].length - 1];
|
|
20
|
+
}
|
|
21
|
+
/** A human-readable label for a deny/allow term, used in the match rule + reason. */
|
|
22
|
+
function describeTerm(term) {
|
|
23
|
+
return typeof term === "string" ? term : term.source;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the built-in **topic filter** (`ai.guardrail.topic`) — a
|
|
27
|
+
* zero-runtime-dependency {@link GuardrailDetector} that gates text against a
|
|
28
|
+
* deny list, an allow list, or both.
|
|
29
|
+
*
|
|
30
|
+
* - **`deny`** — any term that appears triggers `onMatch`. A `string`
|
|
31
|
+
* matches case-insensitively as a substring; a `RegExp` is tested as-is.
|
|
32
|
+
* The deny list is checked first; the first hit decides the verdict.
|
|
33
|
+
* - **`allow`** — when set, text matching **none** of the allow terms
|
|
34
|
+
* triggers `onMatch` (an allow-list miss). Text matching at least one
|
|
35
|
+
* allow term passes the allow gate.
|
|
36
|
+
*
|
|
37
|
+
* `onMatch` is `"block"` (default) or `"flag"`. With neither list supplied
|
|
38
|
+
* the detector is a no-op that always allows.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ai.guardrail.topic({ deny: ["medical advice", /diagnos\w+/i] });
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Stay on-topic: anything not about billing is flagged.
|
|
45
|
+
* ai.guardrail.topic({ allow: ["billing", "invoice", "refund"], onMatch: "flag" });
|
|
46
|
+
*/
|
|
47
|
+
function topic(options) {
|
|
48
|
+
const deny = options.deny ?? [];
|
|
49
|
+
const allow = options.allow ?? [];
|
|
50
|
+
const onMatch = options.onMatch ?? "block";
|
|
51
|
+
return {
|
|
52
|
+
name: DETECTOR_NAME,
|
|
53
|
+
check(text) {
|
|
54
|
+
for (const term of deny) {
|
|
55
|
+
const span = locate(text, term);
|
|
56
|
+
if (span !== void 0) {
|
|
57
|
+
const label = describeTerm(term);
|
|
58
|
+
const match = {
|
|
59
|
+
rule: `${DETECTOR_NAME}.deny.${label}`,
|
|
60
|
+
span,
|
|
61
|
+
label
|
|
62
|
+
};
|
|
63
|
+
return verdict(onMatch, options.reason ?? `Denied topic matched: ${label}.`, [match]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (allow.length > 0) {
|
|
67
|
+
if (!allow.some((term) => locate(text, term) !== void 0)) {
|
|
68
|
+
const match = {
|
|
69
|
+
rule: `${DETECTOR_NAME}.allow.miss`,
|
|
70
|
+
label: "allow-miss"
|
|
71
|
+
};
|
|
72
|
+
return verdict(onMatch, options.reason ?? "Text matched none of the allowed topics.", [match]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { type: "allow" };
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fold the resolved action into a `block` or `flag` verdict. Topic never
|
|
81
|
+
* redacts — it cannot meaningfully rewrite a whole-text policy miss — so the
|
|
82
|
+
* action is constrained to `"block" | "flag"` at the type level.
|
|
83
|
+
*/
|
|
84
|
+
function verdict(action, reason, matches) {
|
|
85
|
+
if (action === "block") return {
|
|
86
|
+
type: "block",
|
|
87
|
+
reason,
|
|
88
|
+
matches
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
type: "flag",
|
|
92
|
+
reason,
|
|
93
|
+
matches
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
export { topic };
|
|
99
|
+
//# sourceMappingURL=topic.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topic.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/guard/detectors/topic.ts"],"sourcesContent":["import type {\n GuardrailMatch,\n GuardrailVerdict,\n SyncGuardrailDetector,\n TopicFilterOptions,\n} from \"../contracts\";\n\n/** Detector name, used as the namespace prefix on every {@link GuardrailMatch.rule}. */\nconst DETECTOR_NAME = \"topic\";\n\n/**\n * Locate the first occurrence of `term` in `text`. A `string` matches\n * case-insensitively as a substring; a `RegExp` is tested as-is (its own\n * flags decide case-sensitivity). Returns the inclusive `[start, end]`\n * span on a hit, or `undefined` when the term is absent.\n */\nfunction locate(text: string, term: string | RegExp): readonly [number, number] | undefined {\n if (typeof term === \"string\") {\n if (term.length === 0) {\n return undefined;\n }\n\n const index = text.toLowerCase().indexOf(term.toLowerCase());\n\n if (index === -1) {\n return undefined;\n }\n\n return [index, index + term.length - 1];\n }\n\n // RegExp: run a non-global copy so a caller-supplied `/g` term cannot leak\n // `lastIndex` between calls and so `.exec` reports a deterministic first hit.\n const probe = new RegExp(term.source, term.flags.replace(/[gy]/g, \"\"));\n const match = probe.exec(text);\n\n if (match === null) {\n return undefined;\n }\n\n return [match.index, match.index + match[0].length - 1];\n}\n\n/** A human-readable label for a deny/allow term, used in the match rule + reason. */\nfunction describeTerm(term: string | RegExp): string {\n return typeof term === \"string\" ? term : term.source;\n}\n\n/**\n * Build the built-in **topic filter** (`ai.guardrail.topic`) — a\n * zero-runtime-dependency {@link GuardrailDetector} that gates text against a\n * deny list, an allow list, or both.\n *\n * - **`deny`** — any term that appears triggers `onMatch`. A `string`\n * matches case-insensitively as a substring; a `RegExp` is tested as-is.\n * The deny list is checked first; the first hit decides the verdict.\n * - **`allow`** — when set, text matching **none** of the allow terms\n * triggers `onMatch` (an allow-list miss). Text matching at least one\n * allow term passes the allow gate.\n *\n * `onMatch` is `\"block\"` (default) or `\"flag\"`. With neither list supplied\n * the detector is a no-op that always allows.\n *\n * @example\n * ai.guardrail.topic({ deny: [\"medical advice\", /diagnos\\w+/i] });\n *\n * @example\n * // Stay on-topic: anything not about billing is flagged.\n * ai.guardrail.topic({ allow: [\"billing\", \"invoice\", \"refund\"], onMatch: \"flag\" });\n */\nexport function topic(options: TopicFilterOptions): SyncGuardrailDetector {\n const deny = options.deny ?? [];\n const allow = options.allow ?? [];\n const onMatch = options.onMatch ?? \"block\";\n\n return {\n name: DETECTOR_NAME,\n check(text: string): GuardrailVerdict {\n // Deny list: the first present term decides the verdict.\n for (const term of deny) {\n const span = locate(text, term);\n\n if (span !== undefined) {\n const label = describeTerm(term);\n const match: GuardrailMatch = {\n rule: `${DETECTOR_NAME}.deny.${label}`,\n span,\n label,\n };\n const reason = options.reason ?? `Denied topic matched: ${label}.`;\n\n return verdict(onMatch, reason, [match]);\n }\n }\n\n // Allow list: matching NONE of the terms is a miss → trigger onMatch.\n if (allow.length > 0) {\n const matchedAny = allow.some(term => locate(text, term) !== undefined);\n\n if (!matchedAny) {\n const match: GuardrailMatch = {\n rule: `${DETECTOR_NAME}.allow.miss`,\n label: \"allow-miss\",\n };\n const reason =\n options.reason ?? \"Text matched none of the allowed topics.\";\n\n return verdict(onMatch, reason, [match]);\n }\n }\n\n return { type: \"allow\" };\n },\n };\n}\n\n/**\n * Fold the resolved action into a `block` or `flag` verdict. Topic never\n * redacts — it cannot meaningfully rewrite a whole-text policy miss — so the\n * action is constrained to `\"block\" | \"flag\"` at the type level.\n */\nfunction verdict(\n action: \"block\" | \"flag\",\n reason: string,\n matches: readonly GuardrailMatch[],\n): GuardrailVerdict {\n if (action === \"block\") {\n return { type: \"block\", reason, matches };\n }\n\n return { type: \"flag\", reason, matches };\n}\n"],"mappings":";;AAQA,MAAM,gBAAgB;;;;;;;AAQtB,SAAS,OAAO,MAAc,MAA8D;CAC1F,IAAI,OAAO,SAAS,UAAU;EAC5B,IAAI,KAAK,WAAW,GAClB;EAGF,MAAM,QAAQ,KAAK,YAAY,CAAC,CAAC,QAAQ,KAAK,YAAY,CAAC;EAE3D,IAAI,UAAU,IACZ;EAGF,OAAO,CAAC,OAAO,QAAQ,KAAK,SAAS,CAAC;CACxC;CAKA,MAAM,QAAQ,IADI,OAAO,KAAK,QAAQ,KAAK,MAAM,QAAQ,SAAS,EAAE,CAClD,CAAC,CAAC,KAAK,IAAI;CAE7B,IAAI,UAAU,MACZ;CAGF,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS,CAAC;AACxD;;AAGA,SAAS,aAAa,MAA+B;CACnD,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK;AAChD;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,MAAM,SAAoD;CACxE,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAChC,MAAM,UAAU,QAAQ,WAAW;CAEnC,OAAO;EACL,MAAM;EACN,MAAM,MAAgC;GAEpC,KAAK,MAAM,QAAQ,MAAM;IACvB,MAAM,OAAO,OAAO,MAAM,IAAI;IAE9B,IAAI,SAAS,QAAW;KACtB,MAAM,QAAQ,aAAa,IAAI;KAC/B,MAAM,QAAwB;MAC5B,MAAM,GAAG,cAAc,QAAQ;MAC/B;MACA;KACF;KAGA,OAAO,QAAQ,SAFA,QAAQ,UAAU,yBAAyB,MAAM,IAEhC,CAAC,KAAK,CAAC;IACzC;GACF;GAGA,IAAI,MAAM,SAAS,GAGjB;QAAI,CAFe,MAAM,MAAK,SAAQ,OAAO,MAAM,IAAI,MAAM,MAE/C,GAAG;KACf,MAAM,QAAwB;MAC5B,MAAM,GAAG,cAAc;MACvB,OAAO;KACT;KAIA,OAAO,QAAQ,SAFb,QAAQ,UAAU,4CAEY,CAAC,KAAK,CAAC;IACzC;;GAGF,OAAO,EAAE,MAAM,QAAQ;EACzB;CACF;AACF;;;;;;AAOA,SAAS,QACP,QACA,QACA,SACkB;CAClB,IAAI,WAAW,SACb,OAAO;EAAE,MAAM;EAAS;EAAQ;CAAQ;CAG1C,OAAO;EAAE,MAAM;EAAQ;EAAQ;CAAQ;AACzC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GuardrailViolationError, GuardrailViolationErrorOptions } from "../errors/guardrail-violation-error.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/guard/errors.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Curated install string thrown by the optional `moderation` detector on
|
|
6
|
+
* first `check()` when the `openai` peer is absent. Mirrors ai-panoptic's
|
|
7
|
+
* `LANGFUSE_INSTALL_INSTRUCTIONS`.
|
|
8
|
+
*/
|
|
9
|
+
declare const OPENAI_INSTALL_INSTRUCTIONS: string;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { OPENAI_INSTALL_INSTRUCTIONS };
|
|
12
|
+
//# sourceMappingURL=errors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/guard/errors.ts"],"mappings":";;;;;;;;cAuBa,2BAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GuardrailViolationError } from "../errors/guardrail-violation-error.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/guard/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Curated install string thrown by the optional `moderation` detector on
|
|
6
|
+
* first `check()` when the `openai` peer is absent. Mirrors ai-panoptic's
|
|
7
|
+
* `LANGFUSE_INSTALL_INSTRUCTIONS`.
|
|
8
|
+
*/
|
|
9
|
+
const OPENAI_INSTALL_INSTRUCTIONS = `
|
|
10
|
+
The @warlock.js/ai moderation detector requires the optional "openai" peer.
|
|
11
|
+
Install it with:
|
|
12
|
+
|
|
13
|
+
npm install openai
|
|
14
|
+
`.trim();
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { OPENAI_INSTALL_INSTRUCTIONS };
|
|
18
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/guard/errors.ts"],"sourcesContent":["/**\n * Error surface for `@warlock.js/ai`.\n *\n * **No new error class.** A `block` verdict reuses the existing\n * `@warlock.js/ai` {@link GuardrailViolationError} verbatim — its category\n * (`\"guardrail\"`) and `phase` field already model exactly what a guard\n * needs, and `phase` is widened by this package to include `\"tool\"` (a\n * source-compatible third value). Re-exported here so the future `guard()`\n * factory has one import site for the typed abort it throws.\n *\n * The optional `moderation` detector's missing-peer failure is an\n * *infrastructure* fault, not a content violation, so it throws a plain\n * `Error` carrying {@link OPENAI_INSTALL_INSTRUCTIONS} (the langfuse-style\n * lazy-import pattern) rather than an `AIError`.\n */\nexport { GuardrailViolationError } from \"../errors/guardrail-violation-error\";\nexport type { GuardrailViolationErrorOptions } from \"../errors/guardrail-violation-error\";\n\n/**\n * Curated install string thrown by the optional `moderation` detector on\n * first `check()` when the `openai` peer is absent. Mirrors ai-panoptic's\n * `LANGFUSE_INSTALL_INSTRUCTIONS`.\n */\nexport const OPENAI_INSTALL_INSTRUCTIONS = `\nThe @warlock.js/ai moderation detector requires the optional \"openai\" peer.\nInstall it with:\n\n npm install openai\n`.trim();\n"],"mappings":";;;;;;;;AAuBA,MAAa,8BAA8B;;;;;EAKzC,KAAK"}
|