@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,209 @@
|
|
|
1
|
+
import { GuardrailViolationError } from "../errors/guardrail-violation-error.mjs";
|
|
2
|
+
import { extractUserText } from "../middleware/utils/extract-user-text.mjs";
|
|
3
|
+
import { forTool } from "../middleware/helpers/for-tool.mjs";
|
|
4
|
+
import "./errors.mjs";
|
|
5
|
+
|
|
6
|
+
//#region ../@warlock.js/ai/src/guard/guard.ts
|
|
7
|
+
/** Default middleware name when the caller supplies none. */
|
|
8
|
+
const DEFAULT_NAME = "guardrail";
|
|
9
|
+
/**
|
|
10
|
+
* The `ctx.state` key under which a guard records its `flag` verdicts. The
|
|
11
|
+
* value is an append-only array of {@link FlagRecord}, namespaced by the
|
|
12
|
+
* middleware name so two guards on the same agent never collide and a
|
|
13
|
+
* downstream observer (panoptic, the caller) can read the annotations
|
|
14
|
+
* post-run.
|
|
15
|
+
*/
|
|
16
|
+
function flagsKey(name) {
|
|
17
|
+
return `${name}.flags`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Append a `flag` record onto the namespaced `ctx.state` array, creating it
|
|
21
|
+
* on first write. Never throws — recording is best-effort annotation.
|
|
22
|
+
*/
|
|
23
|
+
function recordFlag(ctx, name, record) {
|
|
24
|
+
const key = flagsKey(name);
|
|
25
|
+
const existing = ctx.state.get(key);
|
|
26
|
+
const flags = Array.isArray(existing) ? existing : [];
|
|
27
|
+
flags.push(record);
|
|
28
|
+
ctx.state.set(key, flags);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run a phase's detector array over `text`, in registration order, and fold
|
|
32
|
+
* the verdicts into a single {@link PhaseOutcome}.
|
|
33
|
+
*
|
|
34
|
+
* **Short-circuit.** The first non-`allow`/non-`flag` verdict (a `redact` or
|
|
35
|
+
* `block`) decides the outcome and stops the fold — outer detectors never run
|
|
36
|
+
* after one objects, matching the install-array ordering. `flag` verdicts are
|
|
37
|
+
* recorded into `ctx.state` and the fold continues (allow-but-annotate).
|
|
38
|
+
*
|
|
39
|
+
* **Phase-aware redact downgrade.** A `redact` verdict is only honoured where
|
|
40
|
+
* the seam supports rewrite-and-continue:
|
|
41
|
+
* - `"output"` — `trip.after` may return a replacement `ModelResponse`, so the
|
|
42
|
+
* rewritten text is threaded out.
|
|
43
|
+
* - `"input"` — the core `trip.before` hook can only short-circuit (return a
|
|
44
|
+
* response) or throw; it has **no** rewrite-and-continue seam, so an input
|
|
45
|
+
* `redact` is downgraded to a `block` rather than silently passing the
|
|
46
|
+
* un-redacted prompt through. (Documented on {@link GuardOptions.input}.)
|
|
47
|
+
* - `"tool"` — silently rewriting tool arguments changes the call's
|
|
48
|
+
* side-effects unpredictably, so a tool `redact` is downgraded to a `block`
|
|
49
|
+
* (`tool-arg-redaction-unsupported`) rather than mutating what the tool runs.
|
|
50
|
+
*
|
|
51
|
+
* **Fail-open on detector fault.** A detector's `check()` rejecting is an
|
|
52
|
+
* infrastructure fault, not a content violation — it is recorded as a `flag`
|
|
53
|
+
* (`<detector>.error`) and the fold continues, so a moderation-API outage does
|
|
54
|
+
* not abort every agent run.
|
|
55
|
+
*/
|
|
56
|
+
async function runDetectors(detectors, text, phase, ctx, name) {
|
|
57
|
+
for (const detector of detectors) {
|
|
58
|
+
let verdict;
|
|
59
|
+
try {
|
|
60
|
+
verdict = await detector.check(text, {
|
|
61
|
+
phase,
|
|
62
|
+
ctx
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
recordFlag(ctx, name, {
|
|
66
|
+
detector: detector.name,
|
|
67
|
+
phase,
|
|
68
|
+
reason: `detector "${detector.name}" threw: ${error instanceof Error ? error.message : String(error)}`,
|
|
69
|
+
matches: []
|
|
70
|
+
});
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (verdict.type === "allow") continue;
|
|
74
|
+
if (verdict.type === "flag") {
|
|
75
|
+
recordFlag(ctx, name, {
|
|
76
|
+
detector: detector.name,
|
|
77
|
+
phase,
|
|
78
|
+
reason: verdict.reason,
|
|
79
|
+
matches: verdict.matches
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (verdict.type === "redact") {
|
|
84
|
+
if (phase === "output") return {
|
|
85
|
+
type: "redact",
|
|
86
|
+
text: verdict.text
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
type: "block",
|
|
90
|
+
reason: phase === "tool" ? "tool-arg-redaction-unsupported" : verdict.reason,
|
|
91
|
+
matches: verdict.matches,
|
|
92
|
+
escalate: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
type: "block",
|
|
97
|
+
reason: verdict.reason,
|
|
98
|
+
matches: verdict.matches,
|
|
99
|
+
escalate: verdict.escalate ?? false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { type: "allow" };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Realize a `block` outcome: fire the escalation seam (when the verdict asked
|
|
106
|
+
* for it) and throw the typed {@link GuardrailViolationError} on `result.error`.
|
|
107
|
+
* Never returns — always throws.
|
|
108
|
+
*
|
|
109
|
+
* The core `GuardrailViolationError.phase` is typed `"input" | "output"`; this
|
|
110
|
+
* package widens the surfaced `phase` with `"tool"` (a source-compatible third
|
|
111
|
+
* value), so the construction site asserts the wider value through the options
|
|
112
|
+
* shape the error already accepts at runtime.
|
|
113
|
+
*/
|
|
114
|
+
async function block(outcome, phase, ctx, name, escalation) {
|
|
115
|
+
if (outcome.escalate) await escalation?.onBlock?.({
|
|
116
|
+
phase,
|
|
117
|
+
reason: outcome.reason,
|
|
118
|
+
matches: outcome.matches,
|
|
119
|
+
ctx
|
|
120
|
+
});
|
|
121
|
+
throw new GuardrailViolationError(`guardrail "${name}" rejected ${phase} — ${outcome.reason}`, {
|
|
122
|
+
phase,
|
|
123
|
+
reason: outcome.reason,
|
|
124
|
+
guardrail: name
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the composed **guardrail middleware** (surfaced as
|
|
129
|
+
* `ai.guardrail(options)`) — one {@link AgentMiddleware} that runs the
|
|
130
|
+
* configured detectors at three hook points and maps each
|
|
131
|
+
* {@link GuardrailVerdict} onto the pipeline's throw / return / record
|
|
132
|
+
* mechanics:
|
|
133
|
+
*
|
|
134
|
+
* - **`input`** detectors run at `trip.before` over the outbound prompt
|
|
135
|
+
* (`extractUserText(ctx.messages)`). `block` / `flag` only — the core
|
|
136
|
+
* `trip.before` seam cannot rewrite-and-continue, so a `redact` verdict here
|
|
137
|
+
* is downgraded to a `block`.
|
|
138
|
+
* - **`output`** detectors run at `trip.after` over `response.content`. Full
|
|
139
|
+
* `allow` / `redact` / `block` / `flag` support — a `redact` returns a
|
|
140
|
+
* replacement `ModelResponse` with the rewritten `content`.
|
|
141
|
+
* - **`tool`** detectors run at `tool.before` over `JSON.stringify(toolArgs)`.
|
|
142
|
+
* `block` / `flag`; a `redact` is downgraded to a `block`
|
|
143
|
+
* (`tool-arg-redaction-unsupported`). Scoped to `toolNames` via the core
|
|
144
|
+
* `forTool(toolNames, mw)` helper when set.
|
|
145
|
+
*
|
|
146
|
+
* **Verdict → action.** Detectors run in registration order; the first
|
|
147
|
+
* `redact` / `block` short-circuits the phase. `block` throws a
|
|
148
|
+
* {@link GuardrailViolationError} on `result.error` (never out of the
|
|
149
|
+
* pipeline); `flag` records the match into `ctx.state` under `<name>.flags`
|
|
150
|
+
* and continues; a `{ type: "block", escalate: true }` verdict awaits
|
|
151
|
+
* `escalation.onBlock` before throwing. A detector that *throws* is treated as
|
|
152
|
+
* an infra fault and fails open (recorded as a flag, run continues).
|
|
153
|
+
*
|
|
154
|
+
* @param options - The {@link GuardOptions}: per-phase detector arrays,
|
|
155
|
+
* optional `toolNames` scope, `escalation` seam, and `name` override.
|
|
156
|
+
* @returns One {@link AgentMiddleware} to pass into `ai.agent({ middleware: [...] })`.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* const policy = ai.guardrail({
|
|
160
|
+
* name: "compliance",
|
|
161
|
+
* input: [ai.guardrail.injection({ onMatch: "block" })],
|
|
162
|
+
* output: [ai.guardrail.pii({ onMatch: "redact", mask: "[REDACTED:{label}]" })],
|
|
163
|
+
* tool: [ai.guardrail.pii({ onMatch: "block" })],
|
|
164
|
+
* toolNames: ["send_email"],
|
|
165
|
+
* escalation: { async onBlock(e) { await reviewQueue.enqueue(e); } },
|
|
166
|
+
* });
|
|
167
|
+
*
|
|
168
|
+
* const agent = ai.agent({ model, tools: [sendEmail], middleware: [policy] });
|
|
169
|
+
*/
|
|
170
|
+
function guard(options) {
|
|
171
|
+
const name = options.name ?? DEFAULT_NAME;
|
|
172
|
+
const input = options.input ?? [];
|
|
173
|
+
const output = options.output ?? [];
|
|
174
|
+
const tool = options.tool ?? [];
|
|
175
|
+
const escalation = options.escalation;
|
|
176
|
+
const middleware = {
|
|
177
|
+
name,
|
|
178
|
+
trip: {
|
|
179
|
+
async before(ctx) {
|
|
180
|
+
if (input.length === 0) return;
|
|
181
|
+
const prompt = extractUserText(ctx.messages);
|
|
182
|
+
if (!prompt) return;
|
|
183
|
+
const outcome = await runDetectors(input, prompt, "input", ctx, name);
|
|
184
|
+
if (outcome.type === "block") await block(outcome, "input", ctx, name, escalation);
|
|
185
|
+
},
|
|
186
|
+
async after(ctx, response) {
|
|
187
|
+
if (output.length === 0 || !response.content) return;
|
|
188
|
+
const outcome = await runDetectors(output, response.content, "output", ctx, name);
|
|
189
|
+
if (outcome.type === "block") await block(outcome, "output", ctx, name, escalation);
|
|
190
|
+
if (outcome.type === "redact") return {
|
|
191
|
+
...response,
|
|
192
|
+
content: outcome.text
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
if (tool.length > 0) middleware.tool = { async before(ctx) {
|
|
198
|
+
const args = JSON.stringify(ctx.request.input);
|
|
199
|
+
if (!args) return;
|
|
200
|
+
const outcome = await runDetectors(tool, args, "tool", ctx, name);
|
|
201
|
+
if (outcome.type === "block") await block(outcome, "tool", ctx, name, escalation);
|
|
202
|
+
} };
|
|
203
|
+
if (options.toolNames !== void 0 && middleware.tool) return forTool(options.toolNames, middleware);
|
|
204
|
+
return middleware;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
export { guard };
|
|
209
|
+
//# sourceMappingURL=guard.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/guard/guard.ts"],"sourcesContent":["import { extractUserText } from \"../middleware/utils/extract-user-text\";\nimport { forTool } from \"../middleware/helpers/for-tool\";\nimport type { AgentMiddleware } from \"../contracts/middleware/middleware.contract\";\nimport type {\n MiddlewareToolContext,\n MiddlewareTripContext,\n} from \"../contracts/middleware/middleware-context.type\";\nimport type { ModelResponse } from \"../contracts/model.contract\";\nimport type {\n GuardOptions,\n GuardrailDetector,\n GuardrailEscalation,\n GuardrailMatch,\n GuardrailPhase,\n GuardrailVerdict,\n} from \"./contracts\";\nimport { GuardrailViolationError } from \"./errors\";\n\n/** Default middleware name when the caller supplies none. */\nconst DEFAULT_NAME = \"guardrail\";\n\n/**\n * The `ctx.state` key under which a guard records its `flag` verdicts. The\n * value is an append-only array of {@link FlagRecord}, namespaced by the\n * middleware name so two guards on the same agent never collide and a\n * downstream observer (panoptic, the caller) can read the annotations\n * post-run.\n */\nfunction flagsKey(name: string): string {\n return `${name}.flags`;\n}\n\n/**\n * One flagged match recorded into `ctx.state`. Mirrors the\n * {@link GuardrailVerdict} `flag` shape plus the phase it fired at, so an\n * observer can reconstruct *what* tripped *where* without re-running the\n * detector.\n */\nexport interface FlagRecord {\n /** The detector that produced the flag. */\n readonly detector: string;\n /** Where the detector was running. */\n readonly phase: GuardrailPhase;\n /** The detector's human-readable reason. */\n readonly reason: string;\n /** The matches the detector recorded. */\n readonly matches: readonly GuardrailMatch[];\n}\n\n/**\n * Append a `flag` record onto the namespaced `ctx.state` array, creating it\n * on first write. Never throws — recording is best-effort annotation.\n */\nfunction recordFlag(\n ctx: MiddlewareTripContext,\n name: string,\n record: FlagRecord,\n): void {\n const key = flagsKey(name);\n const existing = ctx.state.get(key);\n const flags = Array.isArray(existing) ? (existing as FlagRecord[]) : [];\n\n flags.push(record);\n ctx.state.set(key, flags);\n}\n\n/**\n * The outcome of folding a phase's detector array — what the hook should do\n * with the inspected text once every detector has had its say.\n *\n * - `allow` — no detector objected; the hook continues untouched.\n * - `redact` — a detector returned rewritten `text`; the hook substitutes it\n * (output / tool phases only — see {@link runDetectors}).\n * - `block` — a detector rejected; the hook throws a\n * {@link GuardrailViolationError} carrying `reason` / `matches` / `escalate`.\n *\n * `flag` verdicts never reach this type — they are recorded into `ctx.state`\n * as a side effect inside {@link runDetectors} and do not short-circuit the\n * fold, so a flagged-but-otherwise-clean run resolves to `allow`.\n */\ntype PhaseOutcome =\n | { readonly type: \"allow\" }\n | { readonly type: \"redact\"; readonly text: string }\n | {\n readonly type: \"block\";\n readonly reason: string;\n readonly matches?: readonly GuardrailMatch[];\n readonly escalate: boolean;\n };\n\n/**\n * Run a phase's detector array over `text`, in registration order, and fold\n * the verdicts into a single {@link PhaseOutcome}.\n *\n * **Short-circuit.** The first non-`allow`/non-`flag` verdict (a `redact` or\n * `block`) decides the outcome and stops the fold — outer detectors never run\n * after one objects, matching the install-array ordering. `flag` verdicts are\n * recorded into `ctx.state` and the fold continues (allow-but-annotate).\n *\n * **Phase-aware redact downgrade.** A `redact` verdict is only honoured where\n * the seam supports rewrite-and-continue:\n * - `\"output\"` — `trip.after` may return a replacement `ModelResponse`, so the\n * rewritten text is threaded out.\n * - `\"input\"` — the core `trip.before` hook can only short-circuit (return a\n * response) or throw; it has **no** rewrite-and-continue seam, so an input\n * `redact` is downgraded to a `block` rather than silently passing the\n * un-redacted prompt through. (Documented on {@link GuardOptions.input}.)\n * - `\"tool\"` — silently rewriting tool arguments changes the call's\n * side-effects unpredictably, so a tool `redact` is downgraded to a `block`\n * (`tool-arg-redaction-unsupported`) rather than mutating what the tool runs.\n *\n * **Fail-open on detector fault.** A detector's `check()` rejecting is an\n * infrastructure fault, not a content violation — it is recorded as a `flag`\n * (`<detector>.error`) and the fold continues, so a moderation-API outage does\n * not abort every agent run.\n */\nasync function runDetectors(\n detectors: readonly GuardrailDetector[],\n text: string,\n phase: GuardrailPhase,\n ctx: MiddlewareTripContext,\n name: string,\n): Promise<PhaseOutcome> {\n for (const detector of detectors) {\n let verdict: GuardrailVerdict;\n\n try {\n verdict = await detector.check(text, { phase, ctx });\n } catch (error) {\n // Infra fault — fail open: record and continue, never abort the run.\n recordFlag(ctx, name, {\n detector: detector.name,\n phase,\n reason: `detector \"${detector.name}\" threw: ${\n error instanceof Error ? error.message : String(error)\n }`,\n matches: [],\n });\n\n continue;\n }\n\n if (verdict.type === \"allow\") {\n continue;\n }\n\n if (verdict.type === \"flag\") {\n recordFlag(ctx, name, {\n detector: detector.name,\n phase,\n reason: verdict.reason,\n matches: verdict.matches,\n });\n\n continue;\n }\n\n if (verdict.type === \"redact\") {\n if (phase === \"output\") {\n return { type: \"redact\", text: verdict.text };\n }\n\n // Input / tool phases have no safe rewrite-and-continue seam — downgrade\n // to a block so the un-redacted text is never threaded through.\n const reason =\n phase === \"tool\"\n ? \"tool-arg-redaction-unsupported\"\n : verdict.reason;\n\n return {\n type: \"block\",\n reason,\n matches: verdict.matches,\n escalate: false,\n };\n }\n\n // verdict.type === \"block\"\n return {\n type: \"block\",\n reason: verdict.reason,\n matches: verdict.matches,\n escalate: verdict.escalate ?? false,\n };\n }\n\n return { type: \"allow\" };\n}\n\n/**\n * Realize a `block` outcome: fire the escalation seam (when the verdict asked\n * for it) and throw the typed {@link GuardrailViolationError} on `result.error`.\n * Never returns — always throws.\n *\n * The core `GuardrailViolationError.phase` is typed `\"input\" | \"output\"`; this\n * package widens the surfaced `phase` with `\"tool\"` (a source-compatible third\n * value), so the construction site asserts the wider value through the options\n * shape the error already accepts at runtime.\n */\nasync function block(\n outcome: Extract<PhaseOutcome, { type: \"block\" }>,\n phase: GuardrailPhase,\n ctx: MiddlewareTripContext,\n name: string,\n escalation: GuardrailEscalation | undefined,\n): Promise<never> {\n if (outcome.escalate) {\n await escalation?.onBlock?.({\n phase,\n reason: outcome.reason,\n matches: outcome.matches,\n ctx,\n });\n }\n\n throw new GuardrailViolationError(\n `guardrail \"${name}\" rejected ${phase} — ${outcome.reason}`,\n {\n // `phase` is widened to include \"tool\"; the error carries it verbatim.\n phase: phase as \"input\" | \"output\",\n reason: outcome.reason,\n guardrail: name,\n },\n );\n}\n\n/**\n * Build the composed **guardrail middleware** (surfaced as\n * `ai.guardrail(options)`) — one {@link AgentMiddleware} that runs the\n * configured detectors at three hook points and maps each\n * {@link GuardrailVerdict} onto the pipeline's throw / return / record\n * mechanics:\n *\n * - **`input`** detectors run at `trip.before` over the outbound prompt\n * (`extractUserText(ctx.messages)`). `block` / `flag` only — the core\n * `trip.before` seam cannot rewrite-and-continue, so a `redact` verdict here\n * is downgraded to a `block`.\n * - **`output`** detectors run at `trip.after` over `response.content`. Full\n * `allow` / `redact` / `block` / `flag` support — a `redact` returns a\n * replacement `ModelResponse` with the rewritten `content`.\n * - **`tool`** detectors run at `tool.before` over `JSON.stringify(toolArgs)`.\n * `block` / `flag`; a `redact` is downgraded to a `block`\n * (`tool-arg-redaction-unsupported`). Scoped to `toolNames` via the core\n * `forTool(toolNames, mw)` helper when set.\n *\n * **Verdict → action.** Detectors run in registration order; the first\n * `redact` / `block` short-circuits the phase. `block` throws a\n * {@link GuardrailViolationError} on `result.error` (never out of the\n * pipeline); `flag` records the match into `ctx.state` under `<name>.flags`\n * and continues; a `{ type: \"block\", escalate: true }` verdict awaits\n * `escalation.onBlock` before throwing. A detector that *throws* is treated as\n * an infra fault and fails open (recorded as a flag, run continues).\n *\n * @param options - The {@link GuardOptions}: per-phase detector arrays,\n * optional `toolNames` scope, `escalation` seam, and `name` override.\n * @returns One {@link AgentMiddleware} to pass into `ai.agent({ middleware: [...] })`.\n *\n * @example\n * const policy = ai.guardrail({\n * name: \"compliance\",\n * input: [ai.guardrail.injection({ onMatch: \"block\" })],\n * output: [ai.guardrail.pii({ onMatch: \"redact\", mask: \"[REDACTED:{label}]\" })],\n * tool: [ai.guardrail.pii({ onMatch: \"block\" })],\n * toolNames: [\"send_email\"],\n * escalation: { async onBlock(e) { await reviewQueue.enqueue(e); } },\n * });\n *\n * const agent = ai.agent({ model, tools: [sendEmail], middleware: [policy] });\n */\nexport function guard(options: GuardOptions): AgentMiddleware {\n const name = options.name ?? DEFAULT_NAME;\n const input = options.input ?? [];\n const output = options.output ?? [];\n const tool = options.tool ?? [];\n const escalation = options.escalation;\n\n const middleware: AgentMiddleware = {\n name,\n trip: {\n async before(ctx: MiddlewareTripContext): Promise<void> {\n if (input.length === 0) {\n return;\n }\n\n const prompt = extractUserText(ctx.messages);\n\n if (!prompt) {\n return;\n }\n\n const outcome = await runDetectors(input, prompt, \"input\", ctx, name);\n\n if (outcome.type === \"block\") {\n await block(outcome, \"input\", ctx, name, escalation);\n }\n\n // `allow` (incl. any recorded flags) and a downgraded-but-impossible\n // input `redact` (already mapped to block above) fall through — the\n // real model call proceeds with the un-mutated prompt.\n },\n async after(\n ctx: MiddlewareTripContext,\n response: ModelResponse,\n ): Promise<void | ModelResponse> {\n if (output.length === 0 || !response.content) {\n return;\n }\n\n const outcome = await runDetectors(\n output,\n response.content,\n \"output\",\n ctx,\n name,\n );\n\n if (outcome.type === \"block\") {\n await block(outcome, \"output\", ctx, name, escalation);\n }\n\n if (outcome.type === \"redact\") {\n // `trip.after` may return a replacement response — thread the\n // rewritten content back so the caller never sees the original.\n return { ...response, content: outcome.text };\n }\n\n return;\n },\n },\n };\n\n // Only declare the `tool` hook map when there are tool detectors — an empty\n // `tool` array would otherwise make `forTool` scoping a no-op cost.\n if (tool.length > 0) {\n middleware.tool = {\n async before(ctx: MiddlewareToolContext): Promise<void> {\n const args = JSON.stringify(ctx.request.input);\n\n if (!args) {\n return;\n }\n\n const outcome = await runDetectors(tool, args, \"tool\", ctx, name);\n\n if (outcome.type === \"block\") {\n await block(outcome, \"tool\", ctx, name, escalation);\n }\n\n // A tool `redact` is downgraded to `block` inside `runDetectors`, so\n // `redact` is unreachable here; `allow`/`flag` fall through and the\n // real tool dispatch proceeds.\n },\n };\n }\n\n // Scope the `tool` hooks to the named tools when requested — `forTool`\n // leaves `trip` hooks untouched, so input/output detectors still fire for\n // every trip regardless of which tool is being dispatched.\n if (options.toolNames !== undefined && middleware.tool) {\n return forTool(options.toolNames, middleware);\n }\n\n return middleware;\n}\n"],"mappings":";;;;;;;AAmBA,MAAM,eAAe;;;;;;;;AASrB,SAAS,SAAS,MAAsB;CACtC,OAAO,GAAG,KAAK;AACjB;;;;;AAuBA,SAAS,WACP,KACA,MACA,QACM;CACN,MAAM,MAAM,SAAS,IAAI;CACzB,MAAM,WAAW,IAAI,MAAM,IAAI,GAAG;CAClC,MAAM,QAAQ,MAAM,QAAQ,QAAQ,IAAK,WAA4B,CAAC;CAEtE,MAAM,KAAK,MAAM;CACjB,IAAI,MAAM,IAAI,KAAK,KAAK;AAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,eAAe,aACb,WACA,MACA,OACA,KACA,MACuB;CACvB,KAAK,MAAM,YAAY,WAAW;EAChC,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,SAAS,MAAM,MAAM;IAAE;IAAO;GAAI,CAAC;EACrD,SAAS,OAAO;GAEd,WAAW,KAAK,MAAM;IACpB,UAAU,SAAS;IACnB;IACA,QAAQ,aAAa,SAAS,KAAK,WACjC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAEvD,SAAS,CAAC;GACZ,CAAC;GAED;EACF;EAEA,IAAI,QAAQ,SAAS,SACnB;EAGF,IAAI,QAAQ,SAAS,QAAQ;GAC3B,WAAW,KAAK,MAAM;IACpB,UAAU,SAAS;IACnB;IACA,QAAQ,QAAQ;IAChB,SAAS,QAAQ;GACnB,CAAC;GAED;EACF;EAEA,IAAI,QAAQ,SAAS,UAAU;GAC7B,IAAI,UAAU,UACZ,OAAO;IAAE,MAAM;IAAU,MAAM,QAAQ;GAAK;GAU9C,OAAO;IACL,MAAM;IACN,QANA,UAAU,SACN,mCACA,QAAQ;IAKZ,SAAS,QAAQ;IACjB,UAAU;GACZ;EACF;EAGA,OAAO;GACL,MAAM;GACN,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,UAAU,QAAQ,YAAY;EAChC;CACF;CAEA,OAAO,EAAE,MAAM,QAAQ;AACzB;;;;;;;;;;;AAYA,eAAe,MACb,SACA,OACA,KACA,MACA,YACgB;CAChB,IAAI,QAAQ,UACV,MAAM,YAAY,UAAU;EAC1B;EACA,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB;CACF,CAAC;CAGH,MAAM,IAAI,wBACR,cAAc,KAAK,aAAa,MAAM,KAAK,QAAQ,UACnD;EAES;EACP,QAAQ,QAAQ;EAChB,WAAW;CACb,CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,MAAM,SAAwC;CAC5D,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAChC,MAAM,SAAS,QAAQ,UAAU,CAAC;CAClC,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,MAAM,aAAa,QAAQ;CAE3B,MAAM,aAA8B;EAClC;EACA,MAAM;GACJ,MAAM,OAAO,KAA2C;IACtD,IAAI,MAAM,WAAW,GACnB;IAGF,MAAM,SAAS,gBAAgB,IAAI,QAAQ;IAE3C,IAAI,CAAC,QACH;IAGF,MAAM,UAAU,MAAM,aAAa,OAAO,QAAQ,SAAS,KAAK,IAAI;IAEpE,IAAI,QAAQ,SAAS,SACnB,MAAM,MAAM,SAAS,SAAS,KAAK,MAAM,UAAU;GAMvD;GACA,MAAM,MACJ,KACA,UAC+B;IAC/B,IAAI,OAAO,WAAW,KAAK,CAAC,SAAS,SACnC;IAGF,MAAM,UAAU,MAAM,aACpB,QACA,SAAS,SACT,UACA,KACA,IACF;IAEA,IAAI,QAAQ,SAAS,SACnB,MAAM,MAAM,SAAS,UAAU,KAAK,MAAM,UAAU;IAGtD,IAAI,QAAQ,SAAS,UAGnB,OAAO;KAAE,GAAG;KAAU,SAAS,QAAQ;IAAK;GAIhD;EACF;CACF;CAIA,IAAI,KAAK,SAAS,GAChB,WAAW,OAAO,EAChB,MAAM,OAAO,KAA2C;EACtD,MAAM,OAAO,KAAK,UAAU,IAAI,QAAQ,KAAK;EAE7C,IAAI,CAAC,MACH;EAGF,MAAM,UAAU,MAAM,aAAa,MAAM,MAAM,QAAQ,KAAK,IAAI;EAEhE,IAAI,QAAQ,SAAS,SACnB,MAAM,MAAM,SAAS,QAAQ,KAAK,MAAM,UAAU;CAMtD,EACF;CAMF,IAAI,QAAQ,cAAc,UAAa,WAAW,MAChD,OAAO,QAAQ,QAAQ,WAAW,UAAU;CAG9C,OAAO;AACT"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AgentMiddleware } from "../contracts/middleware/middleware.contract.mjs";
|
|
2
|
+
import { GuardrailDetector } from "./contracts/guardrail.contract.mjs";
|
|
3
|
+
import { GuardOptions, InjectionDetectorOptions, OpenAiModerationOptions, PiiDetectorOptions, TopicFilterOptions } from "./contracts/guard-options.type.mjs";
|
|
4
|
+
//#region ../@warlock.js/ai/src/guard/guardrail.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The callable `ai.guardrail` surface — the {@link guard} factory with the
|
|
7
|
+
* built-in detector factories attached as methods, so the whole guardrail
|
|
8
|
+
* vocabulary lives under one name:
|
|
9
|
+
*
|
|
10
|
+
* - `ai.guardrail(options)` — build the composed middleware.
|
|
11
|
+
* - `ai.guardrail.pii(o?)` / `.topic(o)` / `.injection(o?)` / `.moderation(o?)`
|
|
12
|
+
* — build a detector to pass into the factory's `input` / `output` / `tool`
|
|
13
|
+
* arrays.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const policy = ai.guardrail({
|
|
17
|
+
* output: [ai.guardrail.pii({ onMatch: "redact" })],
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
interface GuardrailFactory {
|
|
21
|
+
/** Build the composed guardrail {@link AgentMiddleware}. */
|
|
22
|
+
(options: GuardOptions): AgentMiddleware;
|
|
23
|
+
/** Built-in PII detector (regex + dictionary, zero runtime dep). */
|
|
24
|
+
pii(options?: PiiDetectorOptions): GuardrailDetector;
|
|
25
|
+
/** Built-in topic filter (allow / deny string | RegExp lists). */
|
|
26
|
+
topic(options: TopicFilterOptions): GuardrailDetector;
|
|
27
|
+
/** Built-in jailbreak / prompt-injection marker detector. */
|
|
28
|
+
injection(options?: InjectionDetectorOptions): GuardrailDetector;
|
|
29
|
+
/** Optional OpenAI-backed moderation detector (lazy `openai` peer). */
|
|
30
|
+
moderation(options?: OpenAiModerationOptions): GuardrailDetector;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The `ai.guardrail` value: the {@link guard} factory with the detector
|
|
34
|
+
* factories assigned onto it. Built once and shared.
|
|
35
|
+
*/
|
|
36
|
+
declare const guardrail: GuardrailFactory;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { GuardrailFactory, guardrail };
|
|
39
|
+
//# sourceMappingURL=guardrail.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrail.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/guard/guardrail.ts"],"mappings":";;;;;;;;AA2BA;;;;;;;;;;;UAAiB,gBAAA;EAUgC;EAAA,CAR9C,OAAA,EAAS,YAAA,GAAe,eAAA;EAQuC;EANhE,GAAA,CAAI,OAAA,GAAU,kBAAA,GAAqB,iBAAA;EAFlC;EAID,KAAA,CAAM,OAAA,EAAS,kBAAA,GAAqB,iBAAA;EAFpC;EAIA,SAAA,CAAU,OAAA,GAAU,wBAAA,GAA2B,iBAAA;EAJ3C;EAMJ,UAAA,CAAW,OAAA,GAAU,uBAAA,GAA0B,iBAAA;AAAA;;;;;cAOpC,SAAA,EAAW,gBAKtB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { injection } from "./detectors/injection.mjs";
|
|
2
|
+
import { pii } from "./detectors/pii.mjs";
|
|
3
|
+
import { topic } from "./detectors/topic.mjs";
|
|
4
|
+
import { moderation } from "./detectors/moderation.mjs";
|
|
5
|
+
import "./detectors/index.mjs";
|
|
6
|
+
import { guard } from "./guard.mjs";
|
|
7
|
+
|
|
8
|
+
//#region ../@warlock.js/ai/src/guard/guardrail.ts
|
|
9
|
+
/**
|
|
10
|
+
* The `ai.guardrail` value: the {@link guard} factory with the detector
|
|
11
|
+
* factories assigned onto it. Built once and shared.
|
|
12
|
+
*/
|
|
13
|
+
const guardrail = Object.assign(guard, {
|
|
14
|
+
pii,
|
|
15
|
+
topic,
|
|
16
|
+
injection,
|
|
17
|
+
moderation
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { guardrail };
|
|
22
|
+
//# sourceMappingURL=guardrail.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrail.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/guard/guardrail.ts"],"sourcesContent":["import type { AgentMiddleware } from \"../contracts/middleware/middleware.contract\";\nimport type {\n GuardOptions,\n GuardrailDetector,\n InjectionDetectorOptions,\n OpenAiModerationOptions,\n PiiDetectorOptions,\n TopicFilterOptions,\n} from \"./contracts\";\nimport { injection, moderation, pii, topic } from \"./detectors\";\nimport { guard } from \"./guard\";\n\n/**\n * The callable `ai.guardrail` surface — the {@link guard} factory with the\n * built-in detector factories attached as methods, so the whole guardrail\n * vocabulary lives under one name:\n *\n * - `ai.guardrail(options)` — build the composed middleware.\n * - `ai.guardrail.pii(o?)` / `.topic(o)` / `.injection(o?)` / `.moderation(o?)`\n * — build a detector to pass into the factory's `input` / `output` / `tool`\n * arrays.\n *\n * @example\n * const policy = ai.guardrail({\n * output: [ai.guardrail.pii({ onMatch: \"redact\" })],\n * });\n */\nexport interface GuardrailFactory {\n /** Build the composed guardrail {@link AgentMiddleware}. */\n (options: GuardOptions): AgentMiddleware;\n /** Built-in PII detector (regex + dictionary, zero runtime dep). */\n pii(options?: PiiDetectorOptions): GuardrailDetector;\n /** Built-in topic filter (allow / deny string | RegExp lists). */\n topic(options: TopicFilterOptions): GuardrailDetector;\n /** Built-in jailbreak / prompt-injection marker detector. */\n injection(options?: InjectionDetectorOptions): GuardrailDetector;\n /** Optional OpenAI-backed moderation detector (lazy `openai` peer). */\n moderation(options?: OpenAiModerationOptions): GuardrailDetector;\n}\n\n/**\n * The `ai.guardrail` value: the {@link guard} factory with the detector\n * factories assigned onto it. Built once and shared.\n */\nexport const guardrail: GuardrailFactory = Object.assign(guard, {\n pii,\n topic,\n injection,\n moderation,\n});\n\n// `ai.guardrail` is registered natively on the core `ai` object (in `../ai`),\n// now that the guardrail suite ships inside `@warlock.js/ai`.\n"],"mappings":";;;;;;;;;;;;AA4CA,MAAa,YAA8B,OAAO,OAAO,OAAO;CAC9D;CACA;CACA;CACA;AACF,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai/src/human/contracts/approval.type.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* What a human can do to a gated tool call.
|
|
4
|
+
*
|
|
5
|
+
* The discriminator for the {@link ApprovalDecision} union — `type`,
|
|
6
|
+
* never `kind`. Each member of the union is keyed on one of these
|
|
7
|
+
* literals so a reviewer surface can branch exhaustively.
|
|
8
|
+
*
|
|
9
|
+
* - `"approve"` — let the real tool run with the model's args unchanged.
|
|
10
|
+
* - `"reject"` — short-circuit the call; the model sees a typed error
|
|
11
|
+
* (carrying the reviewer's reason) and can self-correct on the next trip.
|
|
12
|
+
* - `"edit"` — run the tool, but with reviewer-replaced args.
|
|
13
|
+
*/
|
|
14
|
+
type ApprovalDecisionType = "approve" | "reject" | "edit";
|
|
15
|
+
/**
|
|
16
|
+
* The read-only run context surfaced to a reviewer alongside an
|
|
17
|
+
* {@link ApprovalRequest}. Derived from the wrapping
|
|
18
|
+
* `MiddlewareToolContext` — the reviewer never mutates it.
|
|
19
|
+
*/
|
|
20
|
+
interface ApprovalRequestContext {
|
|
21
|
+
/** Name of the agent whose tool call is being gated. */
|
|
22
|
+
agentName: string;
|
|
23
|
+
/** Zero-based index of the model round-trip that produced the call. */
|
|
24
|
+
tripIndex: number;
|
|
25
|
+
/** Originating session id, when the run is part of an orchestrator session. */
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The agent's original input prompt for the run that raised this
|
|
29
|
+
* interrupt. Captured so a durable `ai.human.resume(...)` re-run can
|
|
30
|
+
* re-drive the agent with the same prompt without the caller having to
|
|
31
|
+
* thread it through out-of-band.
|
|
32
|
+
*/
|
|
33
|
+
originalInput?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Author-supplied tags from the policy match (e.g. `"destructive"`,
|
|
36
|
+
* `"money"`). Lets a reviewer surface group or prioritize requests
|
|
37
|
+
* without re-deriving the rule that gated the call.
|
|
38
|
+
*/
|
|
39
|
+
tags?: string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The pending tool call a human is asked to rule on.
|
|
43
|
+
*
|
|
44
|
+
* Built by the approval middleware from the `MiddlewareToolContext` the
|
|
45
|
+
* moment the {@link InterruptPolicy} gates a call. In durable mode it is
|
|
46
|
+
* also the payload persisted inside a
|
|
47
|
+
* {@link import("./interrupt-store.contract").PendingInterrupt}, keyed by
|
|
48
|
+
* {@link ApprovalRequest.interruptId}.
|
|
49
|
+
*/
|
|
50
|
+
interface ApprovalRequest {
|
|
51
|
+
/**
|
|
52
|
+
* Stable id for this pending call. Durable mode keys the
|
|
53
|
+
* `InterruptStore` on it; the caller surfaces it to the reviewer and
|
|
54
|
+
* later passes it to `ai.human.resume(interruptId, decision)`.
|
|
55
|
+
*/
|
|
56
|
+
interruptId: string;
|
|
57
|
+
/** Registered name of the tool the model wants to invoke. */
|
|
58
|
+
toolName: string;
|
|
59
|
+
/** Human-facing description from the tool contract, when present. */
|
|
60
|
+
toolDescription?: string;
|
|
61
|
+
/** The exact arguments the model produced for the tool. */
|
|
62
|
+
args: unknown;
|
|
63
|
+
/** Read-only run context the reviewer sees. */
|
|
64
|
+
context: ApprovalRequestContext;
|
|
65
|
+
/** When the request was raised, as an ISO-8601 timestamp. */
|
|
66
|
+
requestedAt: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A human's ruling on an {@link ApprovalRequest}.
|
|
70
|
+
*
|
|
71
|
+
* A discriminated union keyed on `type` (never `kind`):
|
|
72
|
+
* - `approve` — run the real tool unchanged.
|
|
73
|
+
* - `reject` — short-circuit; `reason` is surfaced to the model via the
|
|
74
|
+
* typed error so it can self-correct.
|
|
75
|
+
* - `edit` — run the tool with `args` replaced; `reason` is optional
|
|
76
|
+
* audit context.
|
|
77
|
+
*/
|
|
78
|
+
type ApprovalDecision = {
|
|
79
|
+
type: "approve";
|
|
80
|
+
} | {
|
|
81
|
+
type: "reject";
|
|
82
|
+
reason: string;
|
|
83
|
+
} | {
|
|
84
|
+
type: "edit";
|
|
85
|
+
args: unknown;
|
|
86
|
+
reason?: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Turns an {@link ApprovalRequest} into an {@link ApprovalDecision}.
|
|
90
|
+
*
|
|
91
|
+
* Two modes share this one signature:
|
|
92
|
+
* - **interactive** — resolve the returned promise (or return a decision
|
|
93
|
+
* synchronously) when the operator rules; the middleware `await`s it
|
|
94
|
+
* in-process.
|
|
95
|
+
* - **durable** — persist a
|
|
96
|
+
* {@link import("./interrupt-store.contract").PendingInterrupt} to the
|
|
97
|
+
* configured store, then throw
|
|
98
|
+
* {@link import("../errors").InterruptSuspendedError} to suspend the
|
|
99
|
+
* run; a later `ai.human.resume(...)` call replays the decision.
|
|
100
|
+
*/
|
|
101
|
+
type ApprovalHandler = (request: ApprovalRequest) => Promise<ApprovalDecision> | ApprovalDecision;
|
|
102
|
+
/**
|
|
103
|
+
* The minimal read-only view a {@link InterruptPolicy} predicate sees,
|
|
104
|
+
* derived from the wrapping `MiddlewareToolContext`. Carries only the
|
|
105
|
+
* fields a gating decision can usefully branch on — the model's args,
|
|
106
|
+
* the tool identity, and the surrounding run identity.
|
|
107
|
+
*/
|
|
108
|
+
interface PolicyContext {
|
|
109
|
+
/** Registered name of the tool the model wants to invoke. */
|
|
110
|
+
toolName: string;
|
|
111
|
+
/** Human-facing description from the tool contract, when present. */
|
|
112
|
+
toolDescription?: string;
|
|
113
|
+
/** The exact arguments the model produced for the tool. */
|
|
114
|
+
args: unknown;
|
|
115
|
+
/** Name of the agent whose tool call is being evaluated. */
|
|
116
|
+
agentName: string;
|
|
117
|
+
/** Zero-based index of the model round-trip that produced the call. */
|
|
118
|
+
tripIndex: number;
|
|
119
|
+
/** Originating session id, when part of an orchestrator session. */
|
|
120
|
+
sessionId?: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Decides which tool calls require a human.
|
|
124
|
+
*
|
|
125
|
+
* A discriminated union keyed on `type` (never `kind`):
|
|
126
|
+
* - `allowlist` — gate a call **only** when its tool name is listed.
|
|
127
|
+
* - `denylist` — gate **every** call **except** the listed tool names.
|
|
128
|
+
* - `predicate` — gate a call when the author-supplied `requiresApproval`
|
|
129
|
+
* returns a truthy value; a `string[]` both gates the call and supplies
|
|
130
|
+
* the {@link ApprovalRequestContext.tags}.
|
|
131
|
+
*
|
|
132
|
+
* For the list variants, the optional `tags` callback derives the
|
|
133
|
+
* request tags from the matched tool name.
|
|
134
|
+
*/
|
|
135
|
+
type InterruptPolicy = {
|
|
136
|
+
type: "allowlist"; /** Tool names that require approval. */
|
|
137
|
+
tools: string[]; /** Derive request tags from the matched tool name. */
|
|
138
|
+
tags?: (toolName: string) => string[];
|
|
139
|
+
} | {
|
|
140
|
+
type: "denylist"; /** Tool names that bypass approval; every other tool is gated. */
|
|
141
|
+
tools: string[]; /** Derive request tags from the matched tool name. */
|
|
142
|
+
tags?: (toolName: string) => string[];
|
|
143
|
+
} | {
|
|
144
|
+
type: "predicate";
|
|
145
|
+
/**
|
|
146
|
+
* Return `false` (or an empty result) to skip approval; return
|
|
147
|
+
* `true` or a `string[]` to require it. A `string[]` doubles as the
|
|
148
|
+
* request {@link ApprovalRequestContext.tags}.
|
|
149
|
+
*/
|
|
150
|
+
requiresApproval: (ctx: PolicyContext) => boolean | string[];
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
export { ApprovalDecision, ApprovalDecisionType, ApprovalHandler, ApprovalRequest, ApprovalRequestContext, InterruptPolicy, PolicyContext };
|
|
154
|
+
//# sourceMappingURL=approval.type.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.type.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/human/contracts/approval.type.ts"],"mappings":";;AAYA;;;;AAAgC;AAOhC;;;;;;KAPY,oBAAA;;;;AA8BN;AAYN;UAnCiB,sBAAA;;EAEf,SAAA;EAuCA;EApCA,SAAA;EA0CA;EAvCA,SAAA;EA6CA;;;;AAGW;AAab;EArDE,aAAA;;;;;;EAOA,IAAA;AAAA;;;AAiDuC;AAezC;;;;;;UApDiB,eAAA;EAsDgC;;;;;EAhD/C,WAAA;EAgD+B;EA7C/B,QAAA;EA6C+C;EA1C/C,eAAA;EAkD4B;EA/C5B,IAAA;EA+C4B;EA5C5B,OAAA,EAAS,sBAAsB;EAiD/B;EA9CA,WAAA;AAAA;;;;AA0DS;AAgBX;;;;;;KA7DY,gBAAA;EACN,IAAA;AAAA;EACA,IAAA;EAAgB,MAAA;AAAA;EAChB,IAAA;EAAc,IAAA;EAAe,MAAA;AAAA;;AAgFQ;;;;;;;;;;;;KAjE/B,eAAA,IACV,OAAA,EAAS,eAAA,KACN,OAAA,CAAQ,gBAAA,IAAoB,gBAAA;;;;;;;UAQhB,aAAA;;EAEf,QAAA;;EAGA,eAAA;;EAGA,IAAA;;EAGA,SAAA;;EAGA,SAAA;;EAGA,SAAA;AAAA;;;;;;;;;;;;;;KAgBU,eAAA;EAEN,IAAA;EAEA,KAAA;EAEA,IAAA,IAAQ,QAAA;AAAA;EAGR,IAAA;EAEA,KAAA;EAEA,IAAA,IAAQ,QAAA;AAAA;EAGR,IAAA;;;;;;EAMA,gBAAA,GAAmB,GAAA,EAAK,aAAa;AAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ApprovalHandler, InterruptPolicy } from "./approval.type.mjs";
|
|
2
|
+
import { InterruptStore } from "./interrupt-store.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/human/contracts/human-approval.type.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options for `ai.human.approval(options)` — the `tool.before`
|
|
7
|
+
* approval-gate middleware factory.
|
|
8
|
+
*
|
|
9
|
+
* The middleware evaluates {@link HumanApprovalOptions.policy} against
|
|
10
|
+
* each pending tool call; for gated calls it builds an
|
|
11
|
+
* {@link import("./approval.type").ApprovalRequest} and routes it to
|
|
12
|
+
* {@link HumanApprovalOptions.handler}, then applies the returned
|
|
13
|
+
* {@link import("./approval.type").ApprovalDecision} (approve → continue;
|
|
14
|
+
* reject → short-circuit a typed error; edit → run with replaced args).
|
|
15
|
+
*/
|
|
16
|
+
interface HumanApprovalOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Stable middleware name. Middleware names are validated unique per
|
|
19
|
+
* agent, so two approval middlewares on one agent need distinct names.
|
|
20
|
+
* Defaults to `"human-approval"`.
|
|
21
|
+
*/
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Which tool calls require a human. */
|
|
24
|
+
policy: InterruptPolicy;
|
|
25
|
+
/** How a gated call reaches a human and yields a decision. */
|
|
26
|
+
handler: ApprovalHandler;
|
|
27
|
+
/**
|
|
28
|
+
* Optional durable store. When set **and** the handler throws
|
|
29
|
+
* {@link import("../errors").InterruptSuspendedError}, the pending
|
|
30
|
+
* request is persisted here for out-of-process resume via
|
|
31
|
+
* `ai.human.resume(interruptId, decision)`. Omit for pure interactive
|
|
32
|
+
* (await-in-process) mode.
|
|
33
|
+
*/
|
|
34
|
+
store?: InterruptStore;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { HumanApprovalOptions };
|
|
38
|
+
//# sourceMappingURL=human-approval.type.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-approval.type.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/human/contracts/human-approval.type.ts"],"mappings":";;;;;;AAcA;;;;;;;;;UAAiB,oBAAA;EASP;;;;;EAHR,IAAA;EAesB;EAZtB,MAAA,EAAQ,eAAA;;EAGR,OAAA,EAAS,eAAA;;;;;;;;EAST,KAAA,GAAQ,cAAA;AAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ApprovalDecision, ApprovalDecisionType, ApprovalHandler, ApprovalRequest, ApprovalRequestContext, InterruptPolicy, PolicyContext } from "./approval.type.mjs";
|
|
2
|
+
import { InterruptStore, PendingInterrupt, PendingInterruptStatus, PgClientLike, RedisClientLike } from "./interrupt-store.contract.mjs";
|
|
3
|
+
import { HumanApprovalOptions } from "./human-approval.type.mjs";
|
|
4
|
+
import { ResumeOptions, ResumeResult } from "./resume.type.mjs";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ApprovalRequest } from "./approval.type.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai/src/human/contracts/interrupt-store.contract.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Lifecycle status of a persisted interrupt.
|
|
6
|
+
*
|
|
7
|
+
* `"pending"` until a decision lands; the record is marked
|
|
8
|
+
* `"resolved"` (then deleted) once `ai.human.resume(...)` applies a
|
|
9
|
+
* decision. Discriminator is `status` (a closed string union), so a
|
|
10
|
+
* caller can branch idempotently — a second resume of a `"resolved"`
|
|
11
|
+
* (or already-deleted) interrupt is a no-op, never a double-apply.
|
|
12
|
+
*/
|
|
13
|
+
type PendingInterruptStatus = "pending" | "resolved";
|
|
14
|
+
/**
|
|
15
|
+
* A persisted, awaiting-decision interrupt — the durable counterpart of
|
|
16
|
+
* an in-flight {@link ApprovalRequest}.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the shape the snapshot/checkpoint stores persist: a flat
|
|
19
|
+
* record keyed by a single id (`interruptId`) that the store round-trips
|
|
20
|
+
* verbatim. The `request` is everything a reviewer (in another process,
|
|
21
|
+
* hours later) needs to rule on the call; `status` tracks the lifecycle;
|
|
22
|
+
* `savedAt` is the ISO-8601 write time.
|
|
23
|
+
*/
|
|
24
|
+
interface PendingInterrupt {
|
|
25
|
+
/** Stable id; the store keys the record on it. */
|
|
26
|
+
interruptId: string;
|
|
27
|
+
/** The pending call the human is asked to rule on. */
|
|
28
|
+
request: ApprovalRequest;
|
|
29
|
+
/** `"pending"` until a decision lands; then `"resolved"` and deleted. */
|
|
30
|
+
status: PendingInterruptStatus;
|
|
31
|
+
/** When the record was written, as an ISO-8601 timestamp. */
|
|
32
|
+
savedAt: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Durable store for {@link PendingInterrupt} records — the persistence
|
|
36
|
+
* seam behind durable human-in-the-loop approval.
|
|
37
|
+
*
|
|
38
|
+
* Deliberately shaped like the `@warlock.js/ai`
|
|
39
|
+
* `SnapshotStore` / `CheckpointStore` contracts (`load` / `save` /
|
|
40
|
+
* `delete` / optional `list` / `schema`), so a consumer already running
|
|
41
|
+
* an orchestrator can reuse the **same** `pg.Pool` / redis client for
|
|
42
|
+
* the interrupt table. Keyed by `interruptId`. Schema is never
|
|
43
|
+
* auto-migrated — {@link InterruptStore.schema} returns a DDL string the
|
|
44
|
+
* dev runs through their own migration tooling.
|
|
45
|
+
*
|
|
46
|
+
* Implementations: `ai.human.interrupt.{memory,pg,redis}()`. The memory
|
|
47
|
+
* impl ships first (pure in-process `Map`, zero deps); pg/redis lazily
|
|
48
|
+
* import their client via {@link PgClientLike} / {@link RedisClientLike}
|
|
49
|
+
* so neither driver is a hard dependency.
|
|
50
|
+
*/
|
|
51
|
+
interface InterruptStore {
|
|
52
|
+
/**
|
|
53
|
+
* Persist a pending interrupt, keyed by its own `interruptId`.
|
|
54
|
+
* Overwrites any prior record for the same id (a call has exactly one
|
|
55
|
+
* live interrupt).
|
|
56
|
+
*/
|
|
57
|
+
save(record: PendingInterrupt): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Load the interrupt for an `interruptId`, or `undefined` when none is
|
|
60
|
+
* recorded (never raised, or already resolved + deleted).
|
|
61
|
+
*/
|
|
62
|
+
load(interruptId: string): Promise<PendingInterrupt | undefined>;
|
|
63
|
+
/**
|
|
64
|
+
* Drop the interrupt for an `interruptId`. Idempotent — deleting an
|
|
65
|
+
* absent id is a no-op.
|
|
66
|
+
*/
|
|
67
|
+
delete(interruptId: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* List the interrupt ids the store knows, optionally filtered by a
|
|
70
|
+
* prefix. Optional — stores that can't enumerate (e.g. a key/value
|
|
71
|
+
* driver with no `SCAN`) omit it.
|
|
72
|
+
*/
|
|
73
|
+
list?(prefix?: string): Promise<string[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Return the DDL string for this store's backing table. The dev runs
|
|
76
|
+
* it through their migration tool; the framework never auto-migrates.
|
|
77
|
+
* Stores with no backing table (memory, redis) return an empty string
|
|
78
|
+
* so callers can treat `schema()` uniformly across drivers.
|
|
79
|
+
*/
|
|
80
|
+
schema(): string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Minimal `pg`-compatible client surface the Postgres
|
|
84
|
+
* {@link InterruptStore} depends on. Both `pg.Pool` and `pg.Client`
|
|
85
|
+
* satisfy it — the store only ever calls `query`.
|
|
86
|
+
*
|
|
87
|
+
* `@warlock.js/ai` takes **no** hard dependency on `pg`; the dev
|
|
88
|
+
* installs it (an optional peer) and passes the client in. Structurally
|
|
89
|
+
* identical to the orchestrator stores' `PgClientLike`, so a single pool
|
|
90
|
+
* can back the checkpoint/snapshot stores and the interrupt table alike.
|
|
91
|
+
*/
|
|
92
|
+
interface PgClientLike {
|
|
93
|
+
query(text: string, params?: unknown[]): Promise<{
|
|
94
|
+
rows: unknown[];
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Minimal `redis`-compatible client surface the Redis
|
|
99
|
+
* {@link InterruptStore} depends on.
|
|
100
|
+
*
|
|
101
|
+
* `@warlock.js/ai` takes **no** hard dependency on `redis`; the dev
|
|
102
|
+
* installs it (an optional peer) and passes the connected client in. The
|
|
103
|
+
* store only calls `get` / `set` / `del`.
|
|
104
|
+
*/
|
|
105
|
+
interface RedisClientLike {
|
|
106
|
+
get(key: string): Promise<string | null>;
|
|
107
|
+
set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
|
|
108
|
+
del(key: string): Promise<number>;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { InterruptStore, PendingInterrupt, PendingInterruptStatus, PgClientLike, RedisClientLike };
|
|
112
|
+
//# sourceMappingURL=interrupt-store.contract.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interrupt-store.contract.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai/src/human/contracts/interrupt-store.contract.ts"],"mappings":";;;;;AAWA;;;;AAAkC;AAYlC;;KAZY,sBAAA;;;;;;;;;AAuBH;AAoBT;UA/BiB,gBAAA;;EAEf,WAAA;EAmCgC;EAhChC,OAAA,EAAS,eAAA;EAsCkB;EAnC3B,MAAA,EAAQ,sBAAsB;EAgDN;EA7CxB,OAAA;AAAA;;;;;;;;;;;;;;;;;AAqDM;UAjCS,cAAA;EA8CY;;;;;EAxC3B,IAAA,CAAK,MAAA,EAAQ,gBAAA,GAAmB,OAAA;EAyCS;;;AAAc;EAnCvD,IAAA,CAAK,WAAA,WAAsB,OAAA,CAAQ,gBAAA;EA8CL;;;;EAxC9B,MAAA,CAAO,WAAA,WAAsB,OAAA;EA2CJ;;;;;EApCzB,IAAA,EAAM,MAAA,YAAkB,OAAA;EAmCpB;;;;;;EA3BJ,MAAA;AAAA;AA4ByB;;;;;;;;;;AAAA,UAfV,YAAA;EACf,KAAA,CAAM,IAAA,UAAc,MAAA,eAAqB,OAAO;IAAG,IAAA;EAAA;AAAA;;;;;;;;;UAWpC,eAAA;EACf,GAAA,CAAI,GAAA,WAAc,OAAA;EAClB,GAAA,CAAI,GAAA,UAAa,KAAA,aAAkB,IAAA,cAAkB,OAAA;EACrD,GAAA,CAAI,GAAA,WAAc,OAAA;AAAA"}
|