@theoremjs/agents 0.3.0 → 0.3.2
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/README.md +277 -995
- package/esm/mod.d.ts +34 -20
- package/esm/mod.js +19 -12
- package/esm/src/cli/commands/bench.js +36 -14
- package/esm/src/cli/commands/detect-compile.d.ts +17 -0
- package/esm/src/cli/commands/detect-compile.js +22 -0
- package/esm/src/cli/commands/eval.d.ts +3 -3
- package/esm/src/cli/commands/fuzz-canary.js +55 -16
- package/esm/src/cli/commands/profile.js +4 -4
- package/esm/src/cli/commands/run.d.ts +2 -2
- package/esm/src/cli/commands/run.js +1 -1
- package/esm/src/cli/commands/test.d.ts +4 -3
- package/esm/src/cli/commands/test.js +2 -2
- package/esm/src/cli/index.js +9 -9
- package/esm/src/cli/matrix/fixtures.js +25 -30
- package/esm/src/evals/attachments.d.ts +1 -0
- package/esm/src/evals/attachments.js +4 -2
- package/esm/src/evals/breakdown.d.ts +2 -0
- package/esm/src/evals/breakdown.js +1 -0
- package/esm/src/evals/graders/answer.d.ts +3 -0
- package/esm/src/evals/graders/answer.js +1 -0
- package/esm/src/evals/graders/code.d.ts +7 -31
- package/esm/src/evals/graders/code.js +4 -25
- package/esm/src/evals/graders/judge.d.ts +4 -0
- package/esm/src/evals/graders/judge.js +7 -5
- package/esm/src/evals/graders/latency.d.ts +1 -0
- package/esm/src/evals/graders/latency.js +3 -2
- package/esm/src/evals/graders/live.d.ts +3 -0
- package/esm/src/evals/graders/live.js +2 -0
- package/esm/src/evals/graders/media.js +1 -2
- package/esm/src/evals/graders/shared.js +1 -1
- package/esm/src/evals/graders/transcript.d.ts +2 -0
- package/esm/src/evals/graders/transcript.js +5 -3
- package/esm/src/evals/mod.d.ts +1 -9
- package/esm/src/evals/mod.js +1 -9
- package/esm/src/evals/record.d.ts +6 -0
- package/esm/src/evals/record.js +4 -2
- package/esm/src/evals/rubrics/mod.d.ts +1 -1
- package/esm/src/evals/rubrics/mod.js +1 -1
- package/esm/src/evals/rubrics/mustache.js +1 -1
- package/esm/src/evals/rubrics/types.js +1 -1
- package/esm/src/evals/run.d.ts +9 -52
- package/esm/src/evals/run.js +12 -46
- package/esm/src/evals/suite.d.ts +12 -3
- package/esm/src/evals/suite.js +9 -4
- package/esm/src/evals/summary.d.ts +3 -0
- package/esm/src/evals/summary.js +1 -0
- package/esm/src/evals/trial.d.ts +3 -16
- package/esm/src/evals/trial.js +7 -30
- package/esm/src/evals/types.d.ts +30 -121
- package/esm/src/evals/types.js +3 -16
- package/esm/src/evals/verdict.d.ts +5 -0
- package/esm/src/evals/verdict.js +4 -1
- package/esm/src/guardrails/boundaries.d.ts +41 -0
- package/esm/src/guardrails/boundaries.js +103 -0
- package/esm/src/guardrails/canary-gate.d.ts +1 -0
- package/esm/src/guardrails/canary.d.ts +49 -15
- package/esm/src/guardrails/canary.js +103 -30
- package/esm/src/guardrails/compile-egress.d.ts +6 -5
- package/esm/src/guardrails/compile-egress.js +6 -5
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +3 -1
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +4 -2
- package/esm/src/guardrails/corpus/fuzz-inbound.js +14 -17
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +2 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +4 -2
- package/esm/src/guardrails/corpus/live-attacks.d.ts +2 -0
- package/esm/src/guardrails/corpus/live-attacks.js +6 -1
- package/esm/src/guardrails/corpus/mod.d.ts +1 -1
- package/esm/src/guardrails/corpus/secrets.d.ts +3 -3
- package/esm/src/guardrails/corpus/secrets.js +2 -2
- package/esm/src/guardrails/corpus/strings.d.ts +1 -1
- package/esm/src/guardrails/corpus/strings.js +1 -1
- package/esm/src/guardrails/corpus/types.d.ts +5 -1
- package/esm/src/guardrails/credential-rules.d.ts +36 -0
- package/esm/src/guardrails/credential-rules.js +3001 -0
- package/esm/src/guardrails/credential-scan.d.ts +45 -0
- package/esm/src/guardrails/credential-scan.js +172 -0
- package/esm/src/guardrails/destinations.d.ts +45 -0
- package/esm/src/guardrails/destinations.js +234 -0
- package/esm/src/guardrails/detect-at.d.ts +107 -0
- package/esm/src/guardrails/detect-at.js +434 -0
- package/esm/src/guardrails/detect-reply.d.ts +61 -0
- package/esm/src/guardrails/detect-reply.js +95 -0
- package/esm/src/guardrails/detect-stream.d.ts +33 -0
- package/esm/src/guardrails/detect-stream.js +77 -0
- package/esm/src/guardrails/detectors.d.ts +216 -0
- package/esm/src/guardrails/detectors.js +503 -0
- package/esm/src/guardrails/egress-automata.d.ts +4 -6
- package/esm/src/guardrails/egress-automata.js +24095 -4572
- package/esm/src/guardrails/egress-compiler.d.ts +38 -16
- package/esm/src/guardrails/egress-compiler.js +254 -60
- package/esm/src/guardrails/egress-patterns.d.ts +4 -2
- package/esm/src/guardrails/egress-patterns.js +6 -2
- package/esm/src/guardrails/egress-stream.d.ts +54 -36
- package/esm/src/guardrails/egress-stream.js +253 -124
- package/esm/src/guardrails/egress-urls.d.ts +6 -3
- package/esm/src/guardrails/egress-urls.js +14 -22
- package/esm/src/guardrails/egress.d.ts +26 -86
- package/esm/src/guardrails/egress.js +59 -309
- package/esm/src/guardrails/error.d.ts +13 -8
- package/esm/src/guardrails/error.js +25 -18
- package/esm/src/guardrails/event-schemas.d.ts +46 -3
- package/esm/src/guardrails/event-schemas.js +12 -5
- package/esm/src/guardrails/events.d.ts +5 -1
- package/esm/src/guardrails/events.js +4 -1
- package/esm/src/guardrails/hints.d.ts +14 -0
- package/esm/src/guardrails/hints.js +57 -0
- package/esm/src/guardrails/hits.d.ts +7 -1
- package/esm/src/guardrails/hits.js +12 -1
- package/esm/src/guardrails/host-patterns.d.ts +85 -0
- package/esm/src/guardrails/host-patterns.js +173 -0
- package/esm/src/guardrails/injection-patterns.d.ts +6 -1
- package/esm/src/guardrails/injection-patterns.js +33 -1
- package/esm/src/guardrails/injection-reversed.d.ts +9 -0
- package/esm/src/guardrails/injection-reversed.js +49 -0
- package/esm/src/guardrails/injection.d.ts +17 -1
- package/esm/src/guardrails/injection.js +259 -36
- package/esm/src/guardrails/labels.d.ts +40 -0
- package/esm/src/guardrails/labels.js +881 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +9 -8
- package/esm/src/guardrails/live-outbound-gate.js +100 -107
- package/esm/src/guardrails/mod.d.ts +30 -22
- package/esm/src/guardrails/mod.js +19 -14
- package/esm/src/guardrails/network.d.ts +4 -0
- package/esm/src/guardrails/network.js +21 -19
- package/esm/src/guardrails/normalize.d.ts +15 -1
- package/esm/src/guardrails/normalize.js +34 -1
- package/esm/src/guardrails/policy.d.ts +2 -17
- package/esm/src/guardrails/policy.js +12 -41
- package/esm/src/guardrails/progressive-yield.d.ts +65 -24
- package/esm/src/guardrails/progressive-yield.js +191 -147
- package/esm/src/guardrails/prompt-echo.d.ts +4 -2
- package/esm/src/guardrails/prompt-echo.js +87 -6
- package/esm/src/guardrails/quota.d.ts +4 -1
- package/esm/src/guardrails/quota.js +7 -2
- package/esm/src/guardrails/rules.d.ts +20 -29
- package/esm/src/guardrails/rules.js +23 -30
- package/esm/src/guardrails/sanitize.d.ts +32 -15
- package/esm/src/guardrails/sanitize.js +100 -124
- package/esm/src/guardrails/sensitive-groups.d.ts +21 -0
- package/esm/src/guardrails/sensitive-groups.js +12 -0
- package/esm/src/guardrails/sensitive.d.ts +16 -23
- package/esm/src/guardrails/sensitive.js +168 -47
- package/esm/src/guardrails/serialize.d.ts +1 -0
- package/esm/src/guardrails/testing.d.ts +4 -2
- package/esm/src/guardrails/testing.js +4 -2
- package/esm/src/guardrails/theorem-error.d.ts +5 -2
- package/esm/src/guardrails/theorem-error.js +6 -3
- package/esm/src/guardrails/thought-guard.d.ts +34 -13
- package/esm/src/guardrails/thought-guard.js +264 -64
- package/esm/src/guardrails/tool-directives.d.ts +29 -10
- package/esm/src/guardrails/tool-directives.js +133 -47
- package/esm/src/guardrails/tool-leak.d.ts +26 -0
- package/esm/src/guardrails/tool-leak.js +45 -0
- package/esm/src/guardrails/tool-result.d.ts +45 -14
- package/esm/src/guardrails/tool-result.js +94 -70
- package/esm/src/guardrails/types.d.ts +79 -71
- package/esm/src/guardrails/types.js +11 -7
- package/esm/src/host/client-turn.d.ts +29 -2
- package/esm/src/host/client-turn.js +46 -4
- package/esm/src/host/mint-trace.d.ts +1 -0
- package/esm/src/host/mint-trace.js +3 -2
- package/esm/src/host/mod.d.ts +2 -2
- package/esm/src/host/mod.js +1 -1
- package/esm/src/host/readStreamingJsonStringField.js +2 -43
- package/esm/src/host/reply.d.ts +6 -0
- package/esm/src/host/reply.js +8 -1
- package/esm/src/interface/blocks.d.ts +3 -0
- package/esm/src/interface/blocks.js +84 -14
- package/esm/src/interface/composer-actions.d.ts +4 -0
- package/esm/src/interface/composer-actions.js +5 -3
- package/esm/src/interface/draft.d.ts +6 -0
- package/esm/src/interface/draft.js +22 -11
- package/esm/src/interface/from-profile.d.ts +13 -1
- package/esm/src/interface/from-profile.js +66 -31
- package/esm/src/interface/history.d.ts +9 -3
- package/esm/src/interface/history.js +33 -24
- package/esm/src/interface/inputs.d.ts +1 -0
- package/esm/src/interface/inputs.js +3 -0
- package/esm/src/interface/mod.d.ts +4 -2
- package/esm/src/interface/mod.js +3 -2
- package/esm/src/interface/models.d.ts +7 -0
- package/esm/src/interface/models.js +5 -0
- package/esm/src/interface/pending.d.ts +9 -0
- package/esm/src/interface/pending.js +9 -3
- package/esm/src/interface/profile-interface.js +54 -40
- package/esm/src/interface/session.d.ts +12 -5
- package/esm/src/interface/session.js +17 -10
- package/esm/src/interface/stop.d.ts +13 -0
- package/esm/src/interface/stop.js +28 -0
- package/esm/src/interface/tool-calls.d.ts +14 -2
- package/esm/src/interface/tool-calls.js +64 -10
- package/esm/src/interface/tool-invoke.d.ts +2 -0
- package/esm/src/interface/tool-invoke.js +3 -1
- package/esm/src/interface/tool-media.d.ts +2 -0
- package/esm/src/interface/tool-media.js +2 -1
- package/esm/src/interface/types.d.ts +105 -40
- package/esm/src/interface/types.js +1 -0
- package/esm/src/kernel/api-catalog.d.ts +85 -0
- package/esm/src/kernel/api-catalog.js +229 -0
- package/esm/src/kernel/auth/crypto.d.ts +7 -2
- package/esm/src/kernel/auth/crypto.js +12 -10
- package/esm/src/kernel/auth/oauth.d.ts +2 -1
- package/esm/src/kernel/auth/oauth.js +23 -22
- package/esm/src/kernel/auth/typed-secret.js +2 -2
- package/esm/src/kernel/auth/types.d.ts +13 -0
- package/esm/src/kernel/checkpoint.d.ts +43 -0
- package/esm/src/kernel/checkpoint.js +58 -0
- package/esm/src/kernel/default-scope.d.ts +43 -5
- package/esm/src/kernel/default-scope.js +54 -5
- package/esm/src/kernel/engine/call-trace.d.ts +19 -0
- package/esm/src/kernel/engine/call-trace.js +29 -0
- package/esm/src/kernel/engine/compaction.d.ts +6 -5
- package/esm/src/kernel/engine/compaction.js +12 -11
- package/esm/src/kernel/engine/decision.d.ts +10 -8
- package/esm/src/kernel/engine/decision.js +174 -146
- package/esm/src/kernel/engine/embedding.d.ts +8 -0
- package/esm/src/kernel/engine/embedding.js +118 -0
- package/esm/src/kernel/engine/live-inbound.d.ts +14 -4
- package/esm/src/kernel/engine/live-inbound.js +36 -11
- package/esm/src/kernel/engine/live-ingress.d.ts +6 -0
- package/esm/src/kernel/engine/live-ingress.js +6 -1
- package/esm/src/kernel/engine/repair.js +12 -12
- package/esm/src/kernel/engine/runner/gates.d.ts +2 -2
- package/esm/src/kernel/engine/runner/gates.js +287 -101
- package/esm/src/kernel/engine/runner/mod.d.ts +19 -5
- package/esm/src/kernel/engine/runner/mod.js +107 -56
- package/esm/src/kernel/engine/runner/nested.d.ts +32 -0
- package/esm/src/kernel/engine/runner/nested.js +134 -0
- package/esm/src/kernel/engine/runner/schema-validation.d.ts +24 -3
- package/esm/src/kernel/engine/runner/schema-validation.js +78 -4
- package/esm/src/kernel/engine/runner/stages.d.ts +1 -1
- package/esm/src/kernel/engine/runner/stages.js +1 -8
- package/esm/src/kernel/engine/runner/state.d.ts +33 -12
- package/esm/src/kernel/engine/runner/state.js +11 -1
- package/esm/src/kernel/engine/runner/step-order.d.ts +27 -0
- package/esm/src/kernel/engine/runner/step-order.js +165 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +2 -2
- package/esm/src/kernel/engine/runner/steps.js +329 -169
- package/esm/src/kernel/engine/runner/stream.d.ts +42 -11
- package/esm/src/kernel/engine/runner/stream.js +197 -46
- package/esm/src/kernel/engine/runner/usage.d.ts +5 -5
- package/esm/src/kernel/engine/runner/usage.js +12 -14
- package/esm/src/kernel/engine/session/mod.d.ts +8 -6
- package/esm/src/kernel/engine/session/mod.js +360 -129
- package/esm/src/kernel/engine/session/session-trace.d.ts +10 -10
- package/esm/src/kernel/engine/session/session-trace.js +41 -30
- package/esm/src/kernel/engine/token-estimate.d.ts +10 -64
- package/esm/src/kernel/engine/token-estimate.js +14 -194
- package/esm/src/kernel/engine/tool-trace.d.ts +3 -3
- package/esm/src/kernel/engine/tool-trace.js +2 -1
- package/esm/src/kernel/engine/turn-trace.d.ts +8 -7
- package/esm/src/kernel/engine/turn-trace.js +28 -36
- package/esm/src/kernel/engine/usage.d.ts +6 -0
- package/esm/src/kernel/engine/usage.js +17 -0
- package/esm/src/kernel/interaction-parts.d.ts +3 -0
- package/esm/src/kernel/interaction-parts.js +18 -0
- package/esm/src/kernel/json.d.ts +10 -0
- package/esm/src/kernel/json.js +3 -0
- package/esm/src/kernel/mod.d.ts +38 -11
- package/esm/src/kernel/mod.js +22 -7
- package/esm/src/kernel/profile-graph.d.ts +11 -6
- package/esm/src/kernel/profile-graph.js +14 -7
- package/esm/src/kernel/profile-presence.d.ts +10 -1
- package/esm/src/kernel/profile-presence.js +90 -75
- package/esm/src/kernel/profile-scope.d.ts +7 -3
- package/esm/src/kernel/profile-scope.js +67 -55
- package/esm/src/kernel/protocols/binding.d.ts +65 -0
- package/esm/src/kernel/protocols/binding.js +125 -0
- package/esm/src/kernel/protocols/limits.d.ts +15 -0
- package/esm/src/kernel/protocols/limits.js +28 -0
- package/esm/src/kernel/protocols/mod.d.ts +5 -0
- package/esm/src/kernel/protocols/mod.js +5 -0
- package/esm/src/kernel/protocols/prices.d.ts +36 -0
- package/esm/src/kernel/protocols/prices.js +37 -0
- package/esm/src/kernel/protocols/protocol.d.ts +253 -0
- package/esm/src/kernel/protocols/protocol.js +253 -0
- package/esm/src/kernel/protocols/provider.d.ts +108 -0
- package/esm/src/kernel/protocols/provider.js +143 -0
- package/esm/src/kernel/protocols/runtime.d.ts +55 -0
- package/esm/src/kernel/protocols/runtime.js +534 -0
- package/esm/src/kernel/provider-budget.d.ts +32 -0
- package/esm/src/kernel/provider-budget.js +262 -0
- package/esm/src/kernel/provider-dispatch.d.ts +13 -0
- package/esm/src/kernel/provider-dispatch.js +36 -0
- package/esm/src/kernel/provider-live.d.ts +7 -0
- package/esm/src/kernel/provider-live.js +189 -0
- package/esm/src/kernel/provider-runtime.d.ts +57 -0
- package/esm/src/kernel/provider-runtime.js +524 -0
- package/esm/src/kernel/registry/attachments.d.ts +15 -4
- package/esm/src/kernel/registry/attachments.js +31 -22
- package/esm/src/kernel/registry/catalog.d.ts +6 -0
- package/esm/src/kernel/registry/catalog.js +9 -2
- package/esm/src/kernel/registry/embedding-rules.d.ts +28 -0
- package/esm/src/kernel/registry/embedding-rules.js +66 -0
- package/esm/src/kernel/registry/ingress.d.ts +6 -2
- package/esm/src/kernel/registry/ingress.js +55 -10
- package/esm/src/kernel/registry/kernel-registry.d.ts +3 -0
- package/esm/src/kernel/registry/kernel-registry.js +9 -2
- package/esm/src/kernel/registry/profile-outputs.js +4 -1
- package/esm/src/kernel/registry/profiles.d.ts +38 -14
- package/esm/src/kernel/registry/profiles.js +280 -205
- package/esm/src/kernel/registry/provider-request.d.ts +3 -3
- package/esm/src/kernel/registry/provider-request.js +5 -13
- package/esm/src/kernel/registry/resolve.d.ts +4 -1
- package/esm/src/kernel/registry/resolve.js +59 -58
- package/esm/src/kernel/registry/schemas.d.ts +3 -0
- package/esm/src/kernel/registry/schemas.js +3 -1
- package/esm/src/kernel/registry/slot-fill.d.ts +7 -0
- package/esm/src/kernel/registry/slot-fill.js +22 -0
- package/esm/src/kernel/registry/system-prompt.js +7 -8
- package/esm/src/kernel/registry/vault.d.ts +14 -13
- package/esm/src/kernel/registry/vault.js +1 -14
- package/esm/src/kernel/schema.d.ts +100 -46
- package/esm/src/kernel/schema.js +499 -257
- package/esm/src/kernel/scope.d.ts +12 -5
- package/esm/src/kernel/scope.js +23 -3
- package/esm/src/kernel/stages.d.ts +38 -3
- package/esm/src/kernel/stages.js +82 -39
- package/esm/src/kernel/stop.d.ts +29 -6
- package/esm/src/kernel/stop.js +16 -4
- package/esm/src/kernel/system-parts.js +6 -6
- package/esm/src/kernel/tools/activity-label.d.ts +12 -6
- package/esm/src/kernel/tools/activity-label.js +258 -32
- package/esm/src/kernel/tools/agent.d.ts +66 -0
- package/esm/src/kernel/tools/agent.js +212 -0
- package/esm/src/kernel/tools/coordinate.d.ts +4 -0
- package/esm/src/kernel/tools/coordinate.js +165 -0
- package/esm/src/kernel/tools/events.d.ts +21 -6
- package/esm/src/kernel/tools/events.js +53 -18
- package/esm/src/kernel/tools/execute.d.ts +47 -11
- package/esm/src/kernel/tools/execute.js +508 -136
- package/esm/src/kernel/tools/gate-answer.d.ts +12 -2
- package/esm/src/kernel/tools/gate-answer.js +17 -6
- package/esm/src/kernel/tools/handler-events.d.ts +45 -0
- package/esm/src/kernel/tools/handler-events.js +106 -0
- package/esm/src/kernel/tools/harness.d.ts +1 -0
- package/esm/src/kernel/tools/harness.js +2 -1
- package/esm/src/kernel/tools/http-limit.d.ts +27 -0
- package/esm/src/kernel/tools/http-limit.js +85 -0
- package/esm/src/kernel/tools/invoke.d.ts +27 -5
- package/esm/src/kernel/tools/invoke.js +223 -70
- package/esm/src/kernel/tools/mcp-sessions.d.ts +4 -1
- package/esm/src/kernel/tools/mcp-sessions.js +13 -8
- package/esm/src/kernel/tools/mod.d.ts +4 -3
- package/esm/src/kernel/tools/mod.js +3 -2
- package/esm/src/kernel/tools/model-text.d.ts +10 -5
- package/esm/src/kernel/tools/model-text.js +18 -12
- package/esm/src/kernel/tools/operation.d.ts +76 -0
- package/esm/src/kernel/tools/operation.js +100 -0
- package/esm/src/kernel/tools/page-answer.d.ts +8 -0
- package/esm/src/kernel/tools/page-answer.js +19 -0
- package/esm/src/kernel/tools/permission.d.ts +11 -2
- package/esm/src/kernel/tools/permission.js +12 -12
- package/esm/src/kernel/tools/project.d.ts +4 -1
- package/esm/src/kernel/tools/project.js +32 -2
- package/esm/src/kernel/tools/registry.d.ts +9 -2
- package/esm/src/kernel/tools/registry.js +69 -10
- package/esm/src/kernel/tools/remote.d.ts +40 -6
- package/esm/src/kernel/tools/remote.js +219 -92
- package/esm/src/kernel/tools/resolve.d.ts +3 -0
- package/esm/src/kernel/tools/resolve.js +12 -4
- package/esm/src/kernel/tools/run.d.ts +28 -0
- package/esm/src/kernel/tools/run.js +86 -0
- package/esm/src/kernel/tools/schema.d.ts +3 -3
- package/esm/src/kernel/tools/schema.js +21 -100
- package/esm/src/kernel/tools/signed-in-fetch.d.ts +2 -1
- package/esm/src/kernel/tools/signed-in-fetch.js +3 -3
- package/esm/src/kernel/tools/stage-run.d.ts +27 -4
- package/esm/src/kernel/tools/stage-run.js +75 -17
- package/esm/src/kernel/tools/types.d.ts +235 -25
- package/esm/src/kernel/tools/unchecked-output.d.ts +14 -0
- package/esm/src/kernel/tools/unchecked-output.js +18 -0
- package/esm/src/kernel/turn-events.d.ts +208 -31
- package/esm/src/kernel/turn-events.js +127 -9
- package/esm/src/kernel/types.d.ts +302 -161
- package/esm/src/kernel/util/base64.d.ts +2 -0
- package/esm/src/kernel/util/base64.js +2 -0
- package/esm/src/kernel/util/partial-json.d.ts +23 -0
- package/esm/src/kernel/util/partial-json.js +128 -0
- package/esm/src/kernel/util/record.d.ts +1 -0
- package/esm/src/kernel/util/record.js +1 -0
- package/esm/src/observability/destinations.d.ts +5 -0
- package/esm/src/observability/destinations.js +7 -2
- package/esm/src/observability/jsonl.d.ts +2 -2
- package/esm/src/observability/jsonl.js +15 -22
- package/esm/src/observability/mod.d.ts +3 -3
- package/esm/src/observability/mod.js +2 -2
- package/esm/src/observability/openinference.d.ts +1 -1
- package/esm/src/observability/openinference.js +3 -3
- package/esm/src/observability/otlp.d.ts +3 -0
- package/esm/src/observability/otlp.js +3 -3
- package/esm/src/observability/phoenix.d.ts +1 -0
- package/esm/src/observability/policy.d.ts +3 -0
- package/esm/src/observability/policy.js +5 -1
- package/esm/src/observability/resolve-policy.js +11 -5
- package/esm/src/observability/spans.d.ts +8 -2
- package/esm/src/observability/spans.js +21 -5
- package/esm/src/observability/trace-catalog.d.ts +19 -2
- package/esm/src/observability/trace-catalog.js +130 -88
- package/esm/src/observability/trace-record.js +22 -17
- package/esm/src/observability/trace-schema.d.ts +3 -0
- package/esm/src/observability/trace-schema.js +1 -1
- package/esm/src/observability/trace-sink.d.ts +1 -0
- package/esm/src/observability/trace-sink.js +1 -1
- package/esm/src/observability/trace-span.d.ts +7 -1
- package/esm/src/observability/trace-span.js +3 -1
- package/esm/src/observability/trace.d.ts +2 -0
- package/esm/src/observability/trace.js +4 -2
- package/esm/src/observability/types.d.ts +35 -11
- package/esm/src/observability/types.js +1 -1
- package/esm/src/presets/google/speech-voices.d.ts +1 -0
- package/esm/src/presets/google-limits.d.ts +1 -0
- package/esm/src/presets/google.d.ts +27 -12
- package/esm/src/presets/google.js +34 -25
- package/esm/src/presets/local.d.ts +26 -0
- package/esm/src/presets/local.js +30 -0
- package/esm/src/presets/mod.d.ts +5 -4
- package/esm/src/presets/mod.js +4 -4
- package/esm/src/presets/openrouter.d.ts +14 -1
- package/esm/src/presets/openrouter.js +19 -1
- package/esm/src/providers/embedding-limits.d.ts +7 -0
- package/esm/src/providers/embedding-limits.js +12 -0
- package/esm/src/providers/gemini/embedding-protocol.d.ts +8 -0
- package/esm/src/providers/gemini/embedding-protocol.js +125 -0
- package/esm/src/providers/gemini/interactions-protocol.d.ts +8 -0
- package/esm/src/providers/gemini/interactions-protocol.js +102 -0
- package/esm/src/providers/gemini/live-protocol.d.ts +3 -0
- package/esm/src/providers/gemini/live-protocol.js +191 -0
- package/esm/src/providers/gemini/prices.d.ts +3 -0
- package/esm/src/providers/gemini/prices.js +63 -0
- package/esm/src/providers/gemini/provider.d.ts +7 -0
- package/esm/src/providers/gemini/provider.js +39 -0
- package/esm/src/providers/google/api-error.d.ts +2 -0
- package/esm/src/providers/google/api-error.js +26 -18
- package/esm/src/providers/google/builtins.d.ts +14 -0
- package/esm/src/providers/google/builtins.js +57 -0
- package/esm/src/providers/google/grounding.d.ts +4 -4
- package/esm/src/providers/google/interactions/framing.d.ts +20 -8
- package/esm/src/providers/google/interactions/framing.js +85 -44
- package/esm/src/providers/google/interactions/steps.d.ts +5 -5
- package/esm/src/providers/google/interactions/steps.js +4 -4
- package/esm/src/providers/google/interactions/stream.d.ts +10 -11
- package/esm/src/providers/google/interactions/stream.js +33 -100
- package/esm/src/providers/google/live/framing.d.ts +15 -5
- package/esm/src/providers/google/live/framing.js +25 -25
- package/esm/src/providers/google/live/stream.d.ts +6 -44
- package/esm/src/providers/google/live/stream.js +4 -210
- package/esm/src/providers/google/urls.d.ts +1 -3
- package/esm/src/providers/google/urls.js +1 -3
- package/esm/src/providers/google/usage.js +2 -2
- package/esm/src/providers/live.d.ts +6 -0
- package/esm/src/providers/live.js +6 -0
- package/esm/src/providers/local/embedding-protocol.d.ts +7 -0
- package/esm/src/providers/local/embedding-protocol.js +60 -0
- package/esm/src/providers/local/provider.d.ts +7 -0
- package/esm/src/providers/local/provider.js +28 -0
- package/esm/src/providers/mod.d.ts +28 -7
- package/esm/src/providers/mod.js +19 -3
- package/esm/src/providers/open-ai/protocol.d.ts +3 -0
- package/esm/src/providers/open-ai/protocol.js +189 -0
- package/esm/src/providers/openrouter/embedding-protocol.d.ts +6 -0
- package/esm/src/providers/openrouter/embedding-protocol.js +86 -0
- package/esm/src/providers/openrouter/image.d.ts +0 -9
- package/esm/src/providers/openrouter/image.js +0 -153
- package/esm/src/providers/openrouter/images-protocol.d.ts +3 -0
- package/esm/src/providers/openrouter/images-protocol.js +82 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +2 -2
- package/esm/src/providers/openrouter/openai/compat.js +6 -6
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +2 -2
- package/esm/src/providers/openrouter/openai/reasoning-state.d.ts +2 -0
- package/esm/src/providers/openrouter/openai/reasoning-state.js +31 -0
- package/esm/src/providers/openrouter/protocol.d.ts +8 -0
- package/esm/src/providers/openrouter/protocol.js +171 -0
- package/esm/src/providers/openrouter/provider.d.ts +10 -0
- package/esm/src/providers/openrouter/provider.js +50 -0
- package/esm/src/providers/shared/carried-tool-files.d.ts +7 -0
- package/esm/src/providers/shared/carried-tool-files.js +43 -0
- package/esm/src/providers/shared/pcm.js +2 -2
- package/esm/src/providers/shared/protocol-events.d.ts +8 -0
- package/esm/src/providers/shared/protocol-events.js +20 -0
- package/esm/src/providers/shared/response-identity.d.ts +2 -2
- package/esm/src/providers/shared/retry.d.ts +18 -0
- package/esm/src/providers/shared/retry.js +89 -0
- package/esm/src/providers/shared/sse.d.ts +0 -4
- package/esm/src/providers/shared/sse.js +50 -36
- package/esm/src/providers/shared/tool-args.d.ts +6 -7
- package/esm/src/providers/shared/tool-args.js +10 -6
- package/esm/src/providers/shared/tool-draft.d.ts +16 -0
- package/esm/src/providers/shared/tool-draft.js +14 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +2 -2
- package/esm/src/providers/shared/upstream-tape.js +1 -1
- package/esm/src/providers/system-one/protocol.d.ts +8 -0
- package/esm/src/providers/system-one/protocol.js +93 -0
- package/esm/src/providers/types.d.ts +48 -16
- package/esm/src/providers/types.js +2 -1
- package/esm/src/providers/typesafe/provider.d.ts +7 -0
- package/esm/src/providers/typesafe/provider.js +24 -0
- package/esm/src/surface/formats.d.ts +19 -1
- package/esm/src/surface/formats.js +68 -29
- package/esm/src/surface/mod.d.ts +4 -2
- package/esm/src/surface/mod.js +4 -2
- package/esm/src/surface/runtime.d.ts +4 -0
- package/esm/src/surface/runtime.js +2 -0
- package/esm/src/surface/tools.d.ts +2 -8
- package/esm/src/surface/tools.js +4 -22
- package/esm/src/surface/types.d.ts +6 -0
- package/package.json +5 -13
- package/esm/src/cli/commands/egress-compile.d.ts +0 -17
- package/esm/src/cli/commands/egress-compile.js +0 -25
- package/esm/src/guardrails/egress-policy.d.ts +0 -27
- package/esm/src/guardrails/egress-policy.js +0 -76
- package/esm/src/guardrails/egress-rules.d.ts +0 -54
- package/esm/src/guardrails/egress-rules.js +0 -49
- package/esm/src/guardrails/lexicon.d.ts +0 -30
- package/esm/src/guardrails/lexicon.js +0 -478
- package/esm/src/kernel/engine/media-probe/audio.d.ts +0 -7
- package/esm/src/kernel/engine/media-probe/audio.js +0 -342
- package/esm/src/kernel/engine/media-probe/bytes.d.ts +0 -6
- package/esm/src/kernel/engine/media-probe/bytes.js +0 -26
- package/esm/src/kernel/engine/media-probe/image.d.ts +0 -5
- package/esm/src/kernel/engine/media-probe/image.js +0 -171
- package/esm/src/kernel/engine/media-probe/iso-bmff.d.ts +0 -19
- package/esm/src/kernel/engine/media-probe/iso-bmff.js +0 -48
- package/esm/src/kernel/engine/media-probe/matroska.d.ts +0 -35
- package/esm/src/kernel/engine/media-probe/matroska.js +0 -226
- package/esm/src/kernel/engine/media-probe/mp4.d.ts +0 -22
- package/esm/src/kernel/engine/media-probe/mp4.js +0 -279
- package/esm/src/kernel/engine/media-probe/opus.d.ts +0 -4
- package/esm/src/kernel/engine/media-probe/opus.js +0 -23
- package/esm/src/kernel/engine/media-probe/pdf.d.ts +0 -5
- package/esm/src/kernel/engine/media-probe/pdf.js +0 -117
- package/esm/src/kernel/engine/media-probe/video.d.ts +0 -15
- package/esm/src/kernel/engine/media-probe/video.js +0 -49
- package/esm/src/presets/typesafe.d.ts +0 -2
- package/esm/src/presets/typesafe.js +0 -2
- package/esm/src/providers/create-provider.d.ts +0 -21
- package/esm/src/providers/create-provider.js +0 -90
- package/esm/src/providers/decision/usage.d.ts +0 -3
- package/esm/src/providers/decision/usage.js +0 -25
- package/esm/src/providers/google/interactions/mod.d.ts +0 -2
- package/esm/src/providers/google/interactions/mod.js +0 -2
- package/esm/src/providers/google/keys.d.ts +0 -20
- package/esm/src/providers/google/keys.js +0 -87
- package/esm/src/providers/google/live/mod.d.ts +0 -7
- package/esm/src/providers/google/live/mod.js +0 -7
- package/esm/src/providers/google/live/openapi-schema.d.ts +0 -1
- package/esm/src/providers/google/live/openapi-schema.js +0 -42
- package/esm/src/providers/google/live/session.d.ts +0 -14
- package/esm/src/providers/google/live/session.js +0 -150
- package/esm/src/providers/local/local.d.ts +0 -12
- package/esm/src/providers/local/local.js +0 -186
- package/esm/src/providers/local/mod.d.ts +0 -8
- package/esm/src/providers/local/mod.js +0 -8
- package/esm/src/providers/openrouter/cache-control.d.ts +0 -5
- package/esm/src/providers/openrouter/cache-control.js +0 -4
- package/esm/src/providers/openrouter/chat.d.ts +0 -64
- package/esm/src/providers/openrouter/chat.js +0 -490
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +0 -10
- package/esm/src/providers/openrouter/openai/sdk-messages.js +0 -99
- package/esm/src/providers/openrouter/resolve-api-key.d.ts +0 -3
- package/esm/src/providers/openrouter/resolve-api-key.js +0 -8
- package/esm/src/providers/openrouter/speech.d.ts +0 -18
- package/esm/src/providers/openrouter/speech.js +0 -109
- package/esm/src/providers/probe.d.ts +0 -5
- package/esm/src/providers/probe.js +0 -15
- package/esm/src/providers/shared/builtin-wire.d.ts +0 -4
- package/esm/src/providers/shared/builtin-wire.js +0 -9
- package/esm/src/providers/shared/vault.d.ts +0 -13
- package/esm/src/providers/shared/vault.js +0 -41
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// invariant: The one place a match becomes an action. Every boundary calls `detectAt`; nothing
|
|
2
|
+
// else reads the matrix to decide what a match does.
|
|
3
|
+
import { applySpans, spansFromPatterns } from '../observability/spans.js';
|
|
4
|
+
import { INBOUND_BOUNDARIES, recordOf, TOOL_BOUNDARIES } from './boundaries.js';
|
|
5
|
+
import { canaryLeakRanges } from './canary.js';
|
|
6
|
+
import { actionAt, DETECTORS, detects, } from './detectors.js';
|
|
7
|
+
import { boundaryNote } from './egress.js';
|
|
8
|
+
import { notePattern, SYSTEM_BOUNDARY } from './egress-patterns.js';
|
|
9
|
+
import { imageLeakSpans, linkLeakSpans } from './egress-urls.js';
|
|
10
|
+
import { CANARY_HIT, hitFromSpan, PROMPT_ECHO_HIT } from './hits.js';
|
|
11
|
+
import { injectionSpans } from './injection.js';
|
|
12
|
+
import { promptEchoRanges } from './prompt-echo.js';
|
|
13
|
+
import { DETECT_RULES, detectRule } from './rules.js';
|
|
14
|
+
import { sensitiveSpans } from './sensitive.js';
|
|
15
|
+
import { SENSITIVE_GROUPS } from './sensitive-groups.js';
|
|
16
|
+
import { TheoremError } from './theorem-error.js';
|
|
17
|
+
import { directives, mergedRanges } from './tool-directives.js';
|
|
18
|
+
import { ownToolsWithout, toolLeakSpans } from './tool-leak.js';
|
|
19
|
+
const STRENGTH = ['allow', 'flag', 'redact', 'block'];
|
|
20
|
+
function stronger(left, right) {
|
|
21
|
+
if (right === 'ignore')
|
|
22
|
+
return left;
|
|
23
|
+
return STRENGTH.indexOf(right) > STRENGTH.indexOf(left) ? right : left;
|
|
24
|
+
}
|
|
25
|
+
/** Each sensitive group alone, for a scan that reads one detector at a time. */
|
|
26
|
+
const ONLY = recordOf(SENSITIVE_GROUPS, (group) => recordOf(SENSITIVE_GROUPS, (other) => other === group));
|
|
27
|
+
const NO_SCOPE = {};
|
|
28
|
+
/**
|
|
29
|
+
* The {@linkcode LeakScope} of a turn under `detect`: each part only while its detector is above
|
|
30
|
+
* `ignore` somewhere, so a profile that reads for neither binds nothing to read for.
|
|
31
|
+
*/
|
|
32
|
+
function leakScopeOf(detect, turn) {
|
|
33
|
+
const canary = detects(detect, 'canary_leak') ? turn.canary : undefined;
|
|
34
|
+
const guarded = detects(detect, 'prompt_leak') && turn.privateSystem?.length;
|
|
35
|
+
return {
|
|
36
|
+
...(canary ? { canary } : {}),
|
|
37
|
+
...(canary && turn.canaryGiven ? { canaryGiven: true } : {}),
|
|
38
|
+
...(guarded ? { privateSystem: turn.privateSystem } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The {@linkcode DetectScope} of a turn under `policy`: each part only while a detector reads it. */
|
|
42
|
+
function scopeOf(policy, turn) {
|
|
43
|
+
const { detect } = policy;
|
|
44
|
+
const leaks = leakScopeOf(detect, turn);
|
|
45
|
+
const urls = detects(detect, 'ungiven_images') || detects(detect, 'ungiven_links');
|
|
46
|
+
const note = detects(detect, 'marker_leak')
|
|
47
|
+
? boundaryNote({ canary: leaks.canary, labels: turn.labels })
|
|
48
|
+
: undefined;
|
|
49
|
+
const tools = detects(detect, 'tool_leak')
|
|
50
|
+
? ownToolsWithout(turn.ownTools, detect.innocent)
|
|
51
|
+
: undefined;
|
|
52
|
+
return {
|
|
53
|
+
...leaks,
|
|
54
|
+
...(urls && turn.givenUrls ? { givenUrls: turn.givenUrls } : {}),
|
|
55
|
+
...(urls ? { allow: policy.allow } : {}),
|
|
56
|
+
...(note ? { note } : {}),
|
|
57
|
+
...(tools ? { ownTools: tools } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** The detectors a stream gate reads itself, as the text grows. */
|
|
61
|
+
const SCOPED = ['canary_leak', 'prompt_leak'];
|
|
62
|
+
function isScoped(detector) {
|
|
63
|
+
return SCOPED.includes(detector);
|
|
64
|
+
}
|
|
65
|
+
/** One span for each run of overlapping `ranges`: a passage matched window by window is one match. */
|
|
66
|
+
function ranged(ranges, kind) {
|
|
67
|
+
return mergedRanges(ranges).map(([start, end]) => ({ start, end, kind }));
|
|
68
|
+
}
|
|
69
|
+
/** What `detector` lets through in a turn of `scope`. */
|
|
70
|
+
function urlScope(detector, scope) {
|
|
71
|
+
return { ...scope.allow?.[detector], ...(scope.givenUrls ? { given: scope.givenUrls } : {}) };
|
|
72
|
+
}
|
|
73
|
+
const MARKERS = new RegExp(SYSTEM_BOUNDARY.source, 'gi');
|
|
74
|
+
/** Every match of the host's `matchers` in `text`, each named for its pattern. */
|
|
75
|
+
function hostSpans(text, matchers, kind) {
|
|
76
|
+
return matchers.flatMap(({ name, regex }) => spansFromPatterns(text, [regex], kind).map((span) => ({ ...span, name })));
|
|
77
|
+
}
|
|
78
|
+
/** The placeholder a host's pattern leaves for a detector, where it is not the sensitive one. */
|
|
79
|
+
const HOST_KIND = {
|
|
80
|
+
injection: 'injection',
|
|
81
|
+
tool_instructions: 'directive',
|
|
82
|
+
tool_leak: 'tool',
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* What `detector` finds in `text`: with Theorem's patterns, the host's, both, or neither, as its
|
|
86
|
+
* `source` says. Left out, Theorem's only. A host's pattern is read on the text as written.
|
|
87
|
+
*/
|
|
88
|
+
function spansOf(detector, text, scope, source, skipImages = false) {
|
|
89
|
+
if (!source)
|
|
90
|
+
return theoremSpans(detector, text, scope, skipImages);
|
|
91
|
+
const own = hostSpans(text, source.matchers, HOST_KIND[detector] ?? 'sensitive');
|
|
92
|
+
return source.theorem ? [...theoremSpans(detector, text, scope, skipImages), ...own] : own;
|
|
93
|
+
}
|
|
94
|
+
/** With `skipImages`, `ungiven_links` leaves an image's own markup to `ungiven_images`. */
|
|
95
|
+
function theoremSpans(detector, text, scope, skipImages = false) {
|
|
96
|
+
if (detector === 'injection')
|
|
97
|
+
return injectionSpans(text);
|
|
98
|
+
if (detector === 'marker_leak') {
|
|
99
|
+
const patterns = [MARKERS, ...(scope.note ? [notePattern(scope.note)] : [])];
|
|
100
|
+
return spansFromPatterns(text, patterns, 'prompt');
|
|
101
|
+
}
|
|
102
|
+
if (detector === 'ungiven_images') {
|
|
103
|
+
const spans = imageLeakSpans(text, urlScope(detector, scope));
|
|
104
|
+
return spans.map((span) => ({ ...span, kind: 'image' }));
|
|
105
|
+
}
|
|
106
|
+
if (detector === 'ungiven_links') {
|
|
107
|
+
const spans = linkLeakSpans(text, urlScope(detector, scope), skipImages);
|
|
108
|
+
return spans.map((span) => ({ ...span, kind: 'link' }));
|
|
109
|
+
}
|
|
110
|
+
if (detector === 'canary_leak') {
|
|
111
|
+
const { canary, canaryGiven } = scope;
|
|
112
|
+
return canary && !canaryGiven ? ranged(canaryLeakRanges(text, canary), 'canary') : [];
|
|
113
|
+
}
|
|
114
|
+
if (detector === 'prompt_leak') {
|
|
115
|
+
const { privateSystem, canary } = scope;
|
|
116
|
+
return privateSystem ? ranged(promptEchoRanges(text, privateSystem, canary), 'prompt') : [];
|
|
117
|
+
}
|
|
118
|
+
if (detector === 'tool_leak')
|
|
119
|
+
return toolLeakSpans(text, scope.ownTools);
|
|
120
|
+
if (detector === 'tool_instructions') {
|
|
121
|
+
return directives(text, scope.callable).map((found) => ({ ...found, kind: 'directive' }));
|
|
122
|
+
}
|
|
123
|
+
return sensitiveSpans(text, ONLY[detector]);
|
|
124
|
+
}
|
|
125
|
+
/** The hit for a match of `detector`. One of ours never carries what it matched. */
|
|
126
|
+
function hitOf(detector, text, span) {
|
|
127
|
+
const { start, end } = span;
|
|
128
|
+
if (detector === 'canary_leak')
|
|
129
|
+
return { ...CANARY_HIT, span: { start, end } };
|
|
130
|
+
if (detector === 'prompt_leak')
|
|
131
|
+
return { ...PROMPT_ECHO_HIT, span: { start, end } };
|
|
132
|
+
if (detector === 'tool_instructions' && span.signal) {
|
|
133
|
+
const severity = span.signal === 'order' || span.signal === 'authority' ? 'medium' : 'high';
|
|
134
|
+
return { ...hitFromSpan(text, span, DETECT_RULES[detector], severity), signal: span.signal };
|
|
135
|
+
}
|
|
136
|
+
return hitFromSpan(text, span, DETECT_RULES[detector], 'high');
|
|
137
|
+
}
|
|
138
|
+
/** Whether `detector` leaves an image to `ungiven_images`, read at `boundary` beside it. */
|
|
139
|
+
function leavesImages(detector, boundary, detect) {
|
|
140
|
+
return detector === 'ungiven_links' && detect.ungiven_images[boundary] !== 'ignore';
|
|
141
|
+
}
|
|
142
|
+
/** What a host's `find` matched in `text`, or how it failed. */
|
|
143
|
+
function foundBy(find, text, boundary) {
|
|
144
|
+
try {
|
|
145
|
+
const spans = find(text, boundary ? { boundary } : {});
|
|
146
|
+
if (!Array.isArray(spans))
|
|
147
|
+
return 'find_result';
|
|
148
|
+
const inside = spans.every(({ start, end }) => Number.isInteger(start) &&
|
|
149
|
+
Number.isInteger(end) &&
|
|
150
|
+
start >= 0 &&
|
|
151
|
+
start < end &&
|
|
152
|
+
end <= text.length);
|
|
153
|
+
return inside ? spans.map(({ start, end }) => ({ start, end, kind: 'host' })) : 'find_result';
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// why: A reading that failed found nothing it can vouch for, so the text does not cross.
|
|
157
|
+
return 'find_threw';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function hostReader(host, boundary, chosen) {
|
|
161
|
+
const rule = detectRule(host.id);
|
|
162
|
+
return {
|
|
163
|
+
key: host.id,
|
|
164
|
+
rule,
|
|
165
|
+
label: host.label,
|
|
166
|
+
chosen,
|
|
167
|
+
spans(text) {
|
|
168
|
+
const matched = hostSpans(text, host.matchers, 'host');
|
|
169
|
+
if (!host.find)
|
|
170
|
+
return matched;
|
|
171
|
+
const found = foundBy(host.find, text, boundary);
|
|
172
|
+
return typeof found === 'string' ? found : [...matched, ...found];
|
|
173
|
+
},
|
|
174
|
+
hit: (text, span) => ({ ...hitFromSpan(text, span, rule, 'high'), label: host.label }),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** The host's own detectors that read `boundary`. */
|
|
178
|
+
function hostAt(boundary, detect) {
|
|
179
|
+
return (detect.host ?? []).filter(({ actions }) => actions[boundary] !== 'ignore');
|
|
180
|
+
}
|
|
181
|
+
/** Every detector that reads `boundary`, Theorem's then the host's; among `keys` when given. */
|
|
182
|
+
function readersAt(boundary, detect, scope, keys) {
|
|
183
|
+
const readers = [];
|
|
184
|
+
for (const detector of DETECTORS) {
|
|
185
|
+
const chosen = detect[detector][boundary];
|
|
186
|
+
if (chosen === 'ignore')
|
|
187
|
+
continue;
|
|
188
|
+
const skip = leavesImages(detector, boundary, detect);
|
|
189
|
+
readers.push({
|
|
190
|
+
key: detector,
|
|
191
|
+
rule: DETECT_RULES[detector],
|
|
192
|
+
chosen,
|
|
193
|
+
spans: (text) => spansOf(detector, text, scope, detect.sources?.[detector], skip),
|
|
194
|
+
hit: (text, span) => hitOf(detector, text, span),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
for (const host of hostAt(boundary, detect)) {
|
|
198
|
+
readers.push(hostReader(host, boundary, host.actions[boundary]));
|
|
199
|
+
}
|
|
200
|
+
return keys ? readers.filter(({ key }) => keys.includes(key)) : readers;
|
|
201
|
+
}
|
|
202
|
+
/** The hit of a host's `find` that failed: it names the detector, how it failed and no match. */
|
|
203
|
+
function failedHit({ rule, label }, signal) {
|
|
204
|
+
return {
|
|
205
|
+
rule,
|
|
206
|
+
severity: 'high',
|
|
207
|
+
...(label === undefined ? {} : { label }),
|
|
208
|
+
...(signal === undefined ? {} : { signal }),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The longest single text the person or host may send, in characters: far over any model's
|
|
213
|
+
* context, and under what the detectors read safely.
|
|
214
|
+
*/
|
|
215
|
+
const MAX_INBOUND_CHARS = 1_000_000;
|
|
216
|
+
const INBOUND = new Set(INBOUND_BOUNDARIES);
|
|
217
|
+
/** Refuses a text the person or host sent that is over `MAX_INBOUND_CHARS`; `where` names it for the log. */
|
|
218
|
+
function refuseOverlong(text, where) {
|
|
219
|
+
if (text.length <= MAX_INBOUND_CHARS)
|
|
220
|
+
return;
|
|
221
|
+
// labels-exempt: internal diagnostic; the person reads `input.too_long`
|
|
222
|
+
throw new TheoremError('input', `${where} text of ${text.length} characters is over ${MAX_INBOUND_CHARS}`, {
|
|
223
|
+
copy: { key: 'input.too_long', params: { maxChars: MAX_INBOUND_CHARS } },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/** Reads `text` as it crosses `boundary` under the profile's resolved matrix. */
|
|
227
|
+
function detectAt(text, boundary, detect, scope = NO_SCOPE) {
|
|
228
|
+
if (INBOUND.has(boundary))
|
|
229
|
+
refuseOverlong(text, boundary);
|
|
230
|
+
const hits = [];
|
|
231
|
+
const redact = [];
|
|
232
|
+
let action = 'allow';
|
|
233
|
+
for (const reader of readersAt(boundary, detect, scope)) {
|
|
234
|
+
const spans = reader.spans(text);
|
|
235
|
+
if (typeof spans === 'string') {
|
|
236
|
+
hits.push(failedHit(reader, spans));
|
|
237
|
+
action = 'block';
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (spans.length === 0)
|
|
241
|
+
continue;
|
|
242
|
+
for (const span of spans)
|
|
243
|
+
hits.push(reader.hit(text, span));
|
|
244
|
+
if (reader.chosen === 'redact')
|
|
245
|
+
redact.push(...spans);
|
|
246
|
+
action = stronger(action, reader.chosen);
|
|
247
|
+
}
|
|
248
|
+
if (action === 'block')
|
|
249
|
+
return { action, hits };
|
|
250
|
+
if (redact.length === 0)
|
|
251
|
+
return { action, text, hits };
|
|
252
|
+
const replaced = applySpans(text, redact);
|
|
253
|
+
// why: Replacing a match can join what was around it into a new one: that text does not cross.
|
|
254
|
+
if (leftAfterRedact(replaced, boundary, detect, scope))
|
|
255
|
+
return { action: 'block', hits };
|
|
256
|
+
return { action, text: replaced, hits };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Whether `replaced`, a text whose matches were replaced, still holds a match of a detector set
|
|
260
|
+
* to `redact` or `block` at `boundary`, among `detectors`.
|
|
261
|
+
*/
|
|
262
|
+
function leftAfterRedact(replaced, boundary, detect, scope, detectors) {
|
|
263
|
+
return readersAt(boundary, detect, scope, detectors).some(({ chosen, spans }) => {
|
|
264
|
+
if (chosen === 'flag')
|
|
265
|
+
return false;
|
|
266
|
+
const left = spans(replaced);
|
|
267
|
+
return !left || left.length > 0;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** The detectors that read `boundary`: every one not set to `ignore`. */
|
|
271
|
+
function detectorsAt(boundary, detect) {
|
|
272
|
+
return DETECTORS.filter((detector) => detect[detector][boundary] !== 'ignore');
|
|
273
|
+
}
|
|
274
|
+
/** Whether `scope` holds the part `detector` reads for. One that reads no scope always has its part. */
|
|
275
|
+
function hasPart(detector, scope) {
|
|
276
|
+
if (detector === 'canary_leak')
|
|
277
|
+
return Boolean(scope.canary);
|
|
278
|
+
if (detector === 'prompt_leak')
|
|
279
|
+
return Boolean(scope.privateSystem?.length);
|
|
280
|
+
if (detector === 'tool_leak')
|
|
281
|
+
return Boolean(scope.ownTools?.names.length);
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Whether any detector reads one of `boundaries`. Given the turn's `scope`, a
|
|
286
|
+
* detector whose part is absent does not count: it has nothing to find.
|
|
287
|
+
*/
|
|
288
|
+
function detectReads(boundaries, detect, scope) {
|
|
289
|
+
return boundaries.some((boundary) => hostAt(boundary, detect).length > 0 ||
|
|
290
|
+
detectorsAt(boundary, detect).some((detector) => !scope || hasPart(detector, scope)));
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Reads the stretch `[from, to)` of `window`, a text released as it streams. A match is reported
|
|
294
|
+
* with the stretch it starts in. One being replaced is released whole: `to` is pulled back to its
|
|
295
|
+
* start when the stretch would cut it, and `taken` is how far the release got. A detector set to
|
|
296
|
+
* `block` that the stream settled a match of blocks even when this reading finds none: the two
|
|
297
|
+
* are then out of step, and the stream's finding stands.
|
|
298
|
+
*/
|
|
299
|
+
function detectRelease(window, { from, to, settled }, boundary, detect, { detectors, scope }) {
|
|
300
|
+
const failed = [];
|
|
301
|
+
const found = readersAt(boundary, detect, scope, detectors).flatMap((reader) => {
|
|
302
|
+
const spans = reader.spans(window);
|
|
303
|
+
if (typeof spans === 'string')
|
|
304
|
+
failed.push(failedHit(reader, spans));
|
|
305
|
+
return (typeof spans === 'string' ? [] : spans)
|
|
306
|
+
.filter((span) => span.end > from && span.start < to)
|
|
307
|
+
.map((span) => ({ span, rule: reader.rule, label: reader.label, chosen: reader.chosen }));
|
|
308
|
+
});
|
|
309
|
+
if (failed.length > 0)
|
|
310
|
+
return { action: 'block', hits: failed, taken: 0 };
|
|
311
|
+
const hitOf = ({ span, rule, label }) => ({
|
|
312
|
+
...hitFromSpan(window, span, rule, 'high'),
|
|
313
|
+
...(label === undefined ? {} : { label }),
|
|
314
|
+
});
|
|
315
|
+
const blocking = found.filter(({ chosen }) => chosen === 'block');
|
|
316
|
+
if (blocking.length > 0)
|
|
317
|
+
return { action: 'block', hits: blocking.map(hitOf), taken: 0 };
|
|
318
|
+
const unread = settled.filter((key) => actionAt(detect, key, boundary) === 'block');
|
|
319
|
+
if (unread.length > 0) {
|
|
320
|
+
const hits = unread.map((key) => failedHit({
|
|
321
|
+
rule: detectRule(key),
|
|
322
|
+
label: detect.host?.find(({ id }) => id === key)?.label,
|
|
323
|
+
}));
|
|
324
|
+
return { action: 'block', hits, taken: 0 };
|
|
325
|
+
}
|
|
326
|
+
const replaced = found.filter(({ chosen }) => chosen === 'redact');
|
|
327
|
+
let end = to;
|
|
328
|
+
for (;;) {
|
|
329
|
+
const cut = replaced.find(({ span }) => span.start < end && span.end > end);
|
|
330
|
+
if (!cut)
|
|
331
|
+
break;
|
|
332
|
+
end = Math.max(from, cut.span.start);
|
|
333
|
+
}
|
|
334
|
+
const inside = found.filter(({ span, chosen }) => chosen === 'redact' ? span.end <= end : span.start >= from && span.start < end);
|
|
335
|
+
const spans = inside
|
|
336
|
+
.filter(({ chosen }) => chosen === 'redact')
|
|
337
|
+
.map(({ span }) => ({ ...span, start: Math.max(0, span.start - from), end: span.end - from }));
|
|
338
|
+
const text = applySpans(window.slice(from, end), spans);
|
|
339
|
+
if (spans.length > 0 && leftAfterRedact(text, boundary, detect, scope, detectors)) {
|
|
340
|
+
return { action: 'block', hits: inside.map(hitOf), taken: 0 };
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
action: inside.reduce((action, { chosen }) => stronger(action, chosen), 'allow'),
|
|
344
|
+
text,
|
|
345
|
+
hits: inside.map(hitOf),
|
|
346
|
+
taken: end - from,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/** `text` with every match of `detectors` replaced by its placeholder, whatever a profile sets. */
|
|
350
|
+
function redactDetectors(text, detectors) {
|
|
351
|
+
return applySpans(text, detectors.flatMap((detector) => spansOf(detector, text, NO_SCOPE)));
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* What `detectors` find in a text a trace stores, whatever action a profile sets. `detect` is
|
|
355
|
+
* the profile's, for the patterns a host added to each.
|
|
356
|
+
*/
|
|
357
|
+
function storedSpans(text, detectors, sides, detect) {
|
|
358
|
+
return detectors.flatMap((detector) => {
|
|
359
|
+
const turn = detect?.sources?.[detector];
|
|
360
|
+
const source = sides === true
|
|
361
|
+
? turn
|
|
362
|
+
: { theorem: sides.theorem, matchers: sides.host ? (turn?.matchers ?? []) : [] };
|
|
363
|
+
return spansOf(detector, text, NO_SCOPE, source);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/** What a host's own detectors find in a text a trace stores. A `find` that fails covers it all. */
|
|
367
|
+
function storedHostSpans(text, detect) {
|
|
368
|
+
return (detect?.host ?? []).flatMap((host) => {
|
|
369
|
+
const matched = hostSpans(text, host.matchers, 'host');
|
|
370
|
+
if (!host.find || !text)
|
|
371
|
+
return matched;
|
|
372
|
+
const found = foundBy(host.find, text);
|
|
373
|
+
return typeof found === 'string'
|
|
374
|
+
? [{ start: 0, end: text.length, kind: 'host' }]
|
|
375
|
+
: [...matched, ...found];
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
/** A reader for everything crossing `boundary` in one request. */
|
|
379
|
+
function boundaryReader(boundary, detect) {
|
|
380
|
+
const hits = [];
|
|
381
|
+
let action = 'allow';
|
|
382
|
+
return {
|
|
383
|
+
read(text) {
|
|
384
|
+
const detected = detectAt(text, boundary, detect);
|
|
385
|
+
hits.push(...detected.hits);
|
|
386
|
+
action = stronger(action, detected.action);
|
|
387
|
+
return detected.text ?? '';
|
|
388
|
+
},
|
|
389
|
+
found: () => ({ action, hits }),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const TOOL_STAGE = Object.fromEntries(TOOL_BOUNDARIES.map((boundary) => [
|
|
393
|
+
boundary,
|
|
394
|
+
boundary.startsWith('tool_arguments') ? 'tool_call' : 'tool_result',
|
|
395
|
+
]));
|
|
396
|
+
/** The trace stage each boundary reports under. */
|
|
397
|
+
const BOUNDARY_STAGE = {
|
|
398
|
+
user: 'input',
|
|
399
|
+
slots: 'input',
|
|
400
|
+
context: 'input',
|
|
401
|
+
repair: 'input',
|
|
402
|
+
attachment: 'attachment',
|
|
403
|
+
voice: 'attachment',
|
|
404
|
+
history: 'history',
|
|
405
|
+
injected: 'history',
|
|
406
|
+
system: 'system',
|
|
407
|
+
live_user: 'live_inbound',
|
|
408
|
+
...TOOL_STAGE,
|
|
409
|
+
reply: 'output_final',
|
|
410
|
+
reply_structured: 'output_final',
|
|
411
|
+
live_reply: 'live_outbound',
|
|
412
|
+
thought: 'thought',
|
|
413
|
+
};
|
|
414
|
+
/** Host-built per-turn system text is `assembled`; everything else a detector reads is `untrusted`. */
|
|
415
|
+
function trustAt(boundary) {
|
|
416
|
+
return boundary === 'system' ? 'assembled' : 'untrusted';
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* The guardrail event for what was found at a boundary, or `undefined` when nothing matched.
|
|
420
|
+
* `stage` is the boundary's own unless the text was read as it streamed.
|
|
421
|
+
*/
|
|
422
|
+
function detectEvent(boundary, found, provenance, stage = BOUNDARY_STAGE[boundary]) {
|
|
423
|
+
if (found.action === 'allow')
|
|
424
|
+
return undefined;
|
|
425
|
+
return {
|
|
426
|
+
stage,
|
|
427
|
+
boundary,
|
|
428
|
+
trust: trustAt(boundary),
|
|
429
|
+
action: found.action,
|
|
430
|
+
hits: found.hits,
|
|
431
|
+
...(provenance ? { provenance } : {}),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
export { boundaryReader, detectAt, detectEvent, detectorsAt, detectReads, detectRelease, hostAt, isScoped, leakScopeOf, redactDetectors, refuseOverlong, scopeOf, storedHostSpans, storedSpans, stronger, };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `guardrails.detect` on a reply once it has ended. The stream gate
|
|
3
|
+
* (`progressive-yield.ts`) already reported and replaced what it released; this
|
|
4
|
+
* reads the reply whole, so a match the stream held back, or one in the
|
|
5
|
+
* structured output, meets the action the profile set before the reply crosses.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { Boundary } from './boundaries.js';
|
|
10
|
+
import { type Detection, type DetectScope } from './detect-at.js';
|
|
11
|
+
import type { ResolvedDetect } from './detectors.js';
|
|
12
|
+
import type { GuardrailEvent, GuardrailHit } from './event-schemas.js';
|
|
13
|
+
import { type LabelOverrides } from './labels.js';
|
|
14
|
+
import type { OutboundPayload } from './types.js';
|
|
15
|
+
/** The boundaries a turn's reply crosses: its text, and its structured output. */
|
|
16
|
+
declare const TURN_REPLY: readonly ["reply", "reply_structured"];
|
|
17
|
+
/** A reply read whole at its boundaries. */
|
|
18
|
+
interface ReplyRead {
|
|
19
|
+
/** The reply to send on: every match set to `redact` replaced. */
|
|
20
|
+
payload: OutboundPayload;
|
|
21
|
+
/** Whether `payload.text` differs from the text as written. */
|
|
22
|
+
rewritten: boolean;
|
|
23
|
+
/** One event for each boundary with something to report. */
|
|
24
|
+
events: GuardrailEvent[];
|
|
25
|
+
/** Set when a match blocks: the reply does not cross. */
|
|
26
|
+
blocked?: GuardrailHit[];
|
|
27
|
+
}
|
|
28
|
+
/** A JSON value read at `boundary`, with each match in a string replaced, or what stops it crossing. */
|
|
29
|
+
declare function readValue(value: unknown, boundary: Boundary, detect: ResolvedDetect, scope?: DetectScope): Pick<Detection, 'action' | 'hits'> & {
|
|
30
|
+
value?: unknown;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Reads a reply's text at `boundary`, and its structured output at
|
|
34
|
+
* `reply_structured`. A flag in text the stream released was reported then, so
|
|
35
|
+
* it is reported here only past `reportedTo`: how far into the text the stream
|
|
36
|
+
* released, or all of it when left out. `scope` is what the detectors of what
|
|
37
|
+
* is the profile's own read of the turn.
|
|
38
|
+
*/
|
|
39
|
+
declare function readReply(written: OutboundPayload, detect: ResolvedDetect, { boundary, reportedTo, scope, }: {
|
|
40
|
+
boundary: 'reply' | 'live_reply';
|
|
41
|
+
reportedTo?: number;
|
|
42
|
+
scope?: DetectScope;
|
|
43
|
+
}): ReplyRead;
|
|
44
|
+
/**
|
|
45
|
+
* The hits a reply stays stopped on: this reading's block, or else a leak the
|
|
46
|
+
* stream `stopped` on that this reading does not find. The two are out of
|
|
47
|
+
* step then, and the stream's finding stands.
|
|
48
|
+
*/
|
|
49
|
+
declare function standingBlock(read: ReplyRead, stopped?: readonly GuardrailHit[]): GuardrailHit[] | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* `text` as the host receives it after `shown`, the reply text it already has:
|
|
52
|
+
* the rest of it when it carries on from there, or else set apart from it.
|
|
53
|
+
*/
|
|
54
|
+
declare function replyAfter(shown: string, text: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* The refusal as the host receives it after `shown`: the whole refusal when the user has
|
|
57
|
+
* none of the reply, or the cut that ends the part they have.
|
|
58
|
+
*/
|
|
59
|
+
declare function refusalAfter(shown: string, labels?: LabelOverrides): string;
|
|
60
|
+
export type { ReplyRead };
|
|
61
|
+
export { readReply, readValue, refusalAfter, replyAfter, standingBlock, TURN_REPLY };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `guardrails.detect` on a reply once it has ended. The stream gate
|
|
3
|
+
* (`progressive-yield.ts`) already reported and replaced what it released; this
|
|
4
|
+
* reads the reply whole, so a match the stream held back, or one in the
|
|
5
|
+
* structured output, meets the action the profile set before the reply crosses.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { mapStrings } from '../kernel/engine/tree.js';
|
|
10
|
+
import { detectAt, detectEvent, detectReads, } from './detect-at.js';
|
|
11
|
+
import { labelText } from './labels.js';
|
|
12
|
+
import { EGRESS_RULES } from './rules.js';
|
|
13
|
+
import { textForScan } from './serialize.js';
|
|
14
|
+
/** The boundaries a turn's reply crosses: its text, and its structured output. */
|
|
15
|
+
const TURN_REPLY = ['reply', 'reply_structured'];
|
|
16
|
+
/** A JSON value read at `boundary`, with each match in a string replaced, or what stops it crossing. */
|
|
17
|
+
function readValue(value, boundary, detect, scope = {}) {
|
|
18
|
+
if (!detectReads([boundary], detect))
|
|
19
|
+
return { action: 'allow', hits: [], value };
|
|
20
|
+
const scan = textForScan(value);
|
|
21
|
+
if (scan.unscannable) {
|
|
22
|
+
// why: Cannot inspect it, so cannot vouch for it. Fail closed.
|
|
23
|
+
return { action: 'block', hits: [{ rule: EGRESS_RULES.unscannable, severity: 'high' }] };
|
|
24
|
+
}
|
|
25
|
+
const found = detectAt(scan.text, boundary, detect, scope);
|
|
26
|
+
if (found.action !== 'redact')
|
|
27
|
+
return { action: found.action, hits: found.hits, value };
|
|
28
|
+
const redacted = mapStrings(value, (leaf) => detectAt(leaf, boundary, detect, scope).text ?? '');
|
|
29
|
+
const left = detectAt(textForScan(redacted).text, boundary, detect, scope).action;
|
|
30
|
+
// why: A match in a key, or one that runs across values, has no string to replace: it does not cross.
|
|
31
|
+
if (left === 'redact' || left === 'block')
|
|
32
|
+
return { action: 'block', hits: found.hits };
|
|
33
|
+
return { action: 'redact', hits: found.hits, value: redacted };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reads a reply's text at `boundary`, and its structured output at
|
|
37
|
+
* `reply_structured`. A flag in text the stream released was reported then, so
|
|
38
|
+
* it is reported here only past `reportedTo`: how far into the text the stream
|
|
39
|
+
* released, or all of it when left out. `scope` is what the detectors of what
|
|
40
|
+
* is the profile's own read of the turn.
|
|
41
|
+
*/
|
|
42
|
+
function readReply(written, detect, { boundary, reportedTo = written.text.length, scope = {}, }) {
|
|
43
|
+
const text = detectAt(written.text, boundary, detect, scope);
|
|
44
|
+
const structured = written.structured === undefined
|
|
45
|
+
? undefined
|
|
46
|
+
: readValue(written.structured, 'reply_structured', detect, scope);
|
|
47
|
+
const flagged = reportedTo >= written.text.length
|
|
48
|
+
? []
|
|
49
|
+
: text.hits.filter((hit) => !hit.span || hit.span.start >= reportedTo);
|
|
50
|
+
const events = [
|
|
51
|
+
text.action === 'flag'
|
|
52
|
+
? detectEvent(boundary, { action: flagged.length > 0 ? 'flag' : 'allow', hits: flagged })
|
|
53
|
+
: detectEvent(boundary, text),
|
|
54
|
+
structured ? detectEvent('reply_structured', structured) : undefined,
|
|
55
|
+
].filter((event) => event !== undefined);
|
|
56
|
+
const blocked = [text, structured].flatMap((found) => found?.action === 'block' ? found.hits : []);
|
|
57
|
+
return {
|
|
58
|
+
payload: {
|
|
59
|
+
text: text.text ?? written.text,
|
|
60
|
+
...(structured ? { structured: structured.value } : {}),
|
|
61
|
+
},
|
|
62
|
+
rewritten: text.action === 'redact',
|
|
63
|
+
events,
|
|
64
|
+
...(blocked.length > 0 ? { blocked } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The hits a reply stays stopped on: this reading's block, or else a leak the
|
|
69
|
+
* stream `stopped` on that this reading does not find. The two are out of
|
|
70
|
+
* step then, and the stream's finding stands.
|
|
71
|
+
*/
|
|
72
|
+
function standingBlock(read, stopped = []) {
|
|
73
|
+
if (read.blocked)
|
|
74
|
+
return read.blocked;
|
|
75
|
+
const reread = new Set(read.events.flatMap((event) => event.hits.map((hit) => hit.rule)));
|
|
76
|
+
const unread = stopped.filter((hit) => !reread.has(hit.rule));
|
|
77
|
+
return unread.length > 0 ? unread : undefined;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* `text` as the host receives it after `shown`, the reply text it already has:
|
|
81
|
+
* the rest of it when it carries on from there, or else set apart from it.
|
|
82
|
+
*/
|
|
83
|
+
function replyAfter(shown, text) {
|
|
84
|
+
if (!shown)
|
|
85
|
+
return text;
|
|
86
|
+
return text.startsWith(shown) ? text.slice(shown.length) : `\n\n${text}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The refusal as the host receives it after `shown`: the whole refusal when the user has
|
|
90
|
+
* none of the reply, or the cut that ends the part they have.
|
|
91
|
+
*/
|
|
92
|
+
function refusalAfter(shown, labels) {
|
|
93
|
+
return labelText(shown ? 'egress.refusal_cut' : 'egress.refusal', {}, labels);
|
|
94
|
+
}
|
|
95
|
+
export { readReply, readValue, refusalAfter, replyAfter, standingBlock, TURN_REPLY };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `guardrails.detect` on text released as it streams: a reply, a Live reply, a
|
|
3
|
+
* thought. The stream scanner (`egress-stream.ts`) says where a match could
|
|
4
|
+
* still be under way, so nothing a detector reads is released early, and where
|
|
5
|
+
* one settled; `detectRelease` then reads the stretch being released and takes
|
|
6
|
+
* the action the profile set.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { Boundary } from './boundaries.js';
|
|
11
|
+
import { type DetectScope, type Release } from './detect-at.js';
|
|
12
|
+
import { type Detector, type ResolvedDetect } from './detectors.js';
|
|
13
|
+
/** Text read at one boundary as it streams. Indices are of the text as written. */
|
|
14
|
+
interface DetectStream {
|
|
15
|
+
/** Read the next piece of the text. */
|
|
16
|
+
push(fragment: string): void;
|
|
17
|
+
/** The earliest index a match could still start at: nothing from there on is released yet. */
|
|
18
|
+
holdFrom(): number;
|
|
19
|
+
/**
|
|
20
|
+
* Release the text from `from` up to `to`, or all of it once it has `ended`:
|
|
21
|
+
* what crosses, what was found in it, and how far the release got.
|
|
22
|
+
*/
|
|
23
|
+
take(from: number, to: number, ended?: boolean): Release;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A reader for text streaming across `boundary` in a turn of `scope`, or `undefined` when no
|
|
27
|
+
* detector it streams reads it. `canary_leak` and `prompt_leak` (`isScoped`) are not among them:
|
|
28
|
+
* the gate at the boundary reads those as the text grows. Nor are the detectors in `leave`, which
|
|
29
|
+
* another reader at the boundary has.
|
|
30
|
+
*/
|
|
31
|
+
declare function createDetectStream(boundary: Boundary, detect: ResolvedDetect, scope?: DetectScope, leave?: readonly Detector[]): DetectStream | undefined;
|
|
32
|
+
export type { DetectStream };
|
|
33
|
+
export { createDetectStream };
|