@theoremjs/agents 0.0.0-stage → 0.3.1
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/LICENSE +21 -0
- package/README.md +545 -2
- package/esm/_dnt.polyfills.d.ts +66 -0
- package/esm/_dnt.polyfills.js +51 -0
- package/esm/mod.d.ts +79 -0
- package/esm/mod.js +49 -0
- package/esm/package.json +3 -0
- package/esm/src/cli/bin.d.ts +7 -0
- package/esm/src/cli/bin.js +9 -0
- package/esm/src/cli/commands/bench.d.ts +6 -0
- package/esm/src/cli/commands/bench.js +480 -0
- 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 +37 -0
- package/esm/src/cli/commands/eval.js +190 -0
- package/esm/src/cli/commands/fuzz-canary.d.ts +15 -0
- package/esm/src/cli/commands/fuzz-canary.js +241 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +2 -0
- package/esm/src/cli/commands/fuzz-guardrails.js +5 -0
- package/esm/src/cli/commands/profile.d.ts +2 -0
- package/esm/src/cli/commands/profile.js +80 -0
- package/esm/src/cli/commands/run.d.ts +13 -0
- package/esm/src/cli/commands/run.js +45 -0
- package/esm/src/cli/commands/test.d.ts +26 -0
- package/esm/src/cli/commands/test.js +143 -0
- package/esm/src/cli/event-log.d.ts +15 -0
- package/esm/src/cli/event-log.js +136 -0
- package/esm/src/cli/index.d.ts +10 -0
- package/esm/src/cli/index.js +267 -0
- package/esm/src/cli/matrix/fixtures.d.ts +10 -0
- package/esm/src/cli/matrix/fixtures.js +67 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +18 -0
- package/esm/src/cli/matrix/synthesizer.js +103 -0
- package/esm/src/evals/attachments.d.ts +5 -0
- package/esm/src/evals/attachments.js +55 -0
- package/esm/src/evals/breakdown.d.ts +30 -0
- package/esm/src/evals/breakdown.js +69 -0
- package/esm/src/evals/graders/answer.d.ts +16 -0
- package/esm/src/evals/graders/answer.js +134 -0
- package/esm/src/evals/graders/code.d.ts +40 -0
- package/esm/src/evals/graders/code.js +184 -0
- package/esm/src/evals/graders/judge.d.ts +24 -0
- package/esm/src/evals/graders/judge.js +367 -0
- package/esm/src/evals/graders/latency.d.ts +6 -0
- package/esm/src/evals/graders/latency.js +82 -0
- package/esm/src/evals/graders/live.d.ts +14 -0
- package/esm/src/evals/graders/live.js +42 -0
- package/esm/src/evals/graders/media.d.ts +18 -0
- package/esm/src/evals/graders/media.js +91 -0
- package/esm/src/evals/graders/shared.d.ts +11 -0
- package/esm/src/evals/graders/shared.js +62 -0
- package/esm/src/evals/graders/transcript.d.ts +19 -0
- package/esm/src/evals/graders/transcript.js +202 -0
- package/esm/src/evals/mod.d.ts +34 -0
- package/esm/src/evals/mod.js +22 -0
- package/esm/src/evals/record.d.ts +41 -0
- package/esm/src/evals/record.js +107 -0
- package/esm/src/evals/rubrics/catalog.d.ts +148 -0
- package/esm/src/evals/rubrics/catalog.js +846 -0
- package/esm/src/evals/rubrics/mod.d.ts +24 -0
- package/esm/src/evals/rubrics/mod.js +55 -0
- package/esm/src/evals/rubrics/mustache.d.ts +23 -0
- package/esm/src/evals/rubrics/mustache.js +154 -0
- package/esm/src/evals/rubrics/types.d.ts +54 -0
- package/esm/src/evals/rubrics/types.js +74 -0
- package/esm/src/evals/run.d.ts +75 -0
- package/esm/src/evals/run.js +418 -0
- package/esm/src/evals/suite.d.ts +28 -0
- package/esm/src/evals/suite.js +97 -0
- package/esm/src/evals/summary.d.ts +35 -0
- package/esm/src/evals/summary.js +31 -0
- package/esm/src/evals/trial.d.ts +14 -0
- package/esm/src/evals/trial.js +135 -0
- package/esm/src/evals/types.d.ts +169 -0
- package/esm/src/evals/types.js +113 -0
- package/esm/src/evals/verdict.d.ts +23 -0
- package/esm/src/evals/verdict.js +48 -0
- 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 +22 -0
- package/esm/src/guardrails/canary-gate.js +40 -0
- package/esm/src/guardrails/canary.d.ts +146 -0
- package/esm/src/guardrails/canary.js +974 -0
- package/esm/src/guardrails/compile-egress.d.ts +12 -0
- package/esm/src/guardrails/compile-egress.js +12 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +24 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +305 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +5 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +199 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +5 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +120 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +15 -0
- package/esm/src/guardrails/corpus/live-attacks.js +229 -0
- package/esm/src/guardrails/corpus/mod.d.ts +7 -0
- package/esm/src/guardrails/corpus/mod.js +4 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +12 -0
- package/esm/src/guardrails/corpus/secrets.js +16 -0
- package/esm/src/guardrails/corpus/strings.d.ts +23 -0
- package/esm/src/guardrails/corpus/strings.js +29 -0
- package/esm/src/guardrails/corpus/types.d.ts +36 -0
- package/esm/src/guardrails/corpus/types.js +1 -0
- 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 +12 -0
- package/esm/src/guardrails/egress-automata.js +25582 -0
- package/esm/src/guardrails/egress-compiler.d.ts +55 -0
- package/esm/src/guardrails/egress-compiler.js +431 -0
- package/esm/src/guardrails/egress-patterns.d.ts +26 -0
- package/esm/src/guardrails/egress-patterns.js +36 -0
- package/esm/src/guardrails/egress-stream.d.ts +123 -0
- package/esm/src/guardrails/egress-stream.js +774 -0
- package/esm/src/guardrails/egress-urls.d.ts +86 -0
- package/esm/src/guardrails/egress-urls.js +909 -0
- package/esm/src/guardrails/egress.d.ts +46 -0
- package/esm/src/guardrails/egress.js +124 -0
- package/esm/src/guardrails/error.d.ts +31 -0
- package/esm/src/guardrails/error.js +96 -0
- package/esm/src/guardrails/event-schemas.d.ts +100 -0
- package/esm/src/guardrails/event-schemas.js +48 -0
- package/esm/src/guardrails/events.d.ts +13 -0
- package/esm/src/guardrails/events.js +45 -0
- package/esm/src/guardrails/hints.d.ts +14 -0
- package/esm/src/guardrails/hints.js +57 -0
- package/esm/src/guardrails/hits.d.ts +14 -0
- package/esm/src/guardrails/hits.js +30 -0
- 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 +20 -0
- package/esm/src/guardrails/injection-patterns.js +126 -0
- 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 +30 -0
- package/esm/src/guardrails/injection.js +460 -0
- package/esm/src/guardrails/labels.d.ts +40 -0
- package/esm/src/guardrails/labels.js +874 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +55 -0
- package/esm/src/guardrails/live-outbound-gate.js +298 -0
- package/esm/src/guardrails/mod.d.ts +54 -0
- package/esm/src/guardrails/mod.js +34 -0
- package/esm/src/guardrails/network.d.ts +46 -0
- package/esm/src/guardrails/network.js +330 -0
- package/esm/src/guardrails/normalize.d.ts +20 -0
- package/esm/src/guardrails/normalize.js +165 -0
- package/esm/src/guardrails/policy.d.ts +4 -0
- package/esm/src/guardrails/policy.js +21 -0
- package/esm/src/guardrails/progressive-yield.d.ts +110 -0
- package/esm/src/guardrails/progressive-yield.js +281 -0
- package/esm/src/guardrails/prompt-echo.d.ts +40 -0
- package/esm/src/guardrails/prompt-echo.js +306 -0
- package/esm/src/guardrails/quota.d.ts +21 -0
- package/esm/src/guardrails/quota.js +80 -0
- package/esm/src/guardrails/rules.d.ts +47 -0
- package/esm/src/guardrails/rules.js +47 -0
- package/esm/src/guardrails/sanitize.d.ts +40 -0
- package/esm/src/guardrails/sanitize.js +160 -0
- 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 +22 -0
- package/esm/src/guardrails/sensitive.js +234 -0
- package/esm/src/guardrails/serialize.d.ts +15 -0
- package/esm/src/guardrails/serialize.js +37 -0
- package/esm/src/guardrails/testing.d.ts +11 -0
- package/esm/src/guardrails/testing.js +10 -0
- package/esm/src/guardrails/theorem-error.d.ts +21 -0
- package/esm/src/guardrails/theorem-error.js +54 -0
- package/esm/src/guardrails/thought-guard.d.ts +76 -0
- package/esm/src/guardrails/thought-guard.js +664 -0
- package/esm/src/guardrails/tool-directives.d.ts +38 -0
- package/esm/src/guardrails/tool-directives.js +195 -0
- 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 +92 -0
- package/esm/src/guardrails/tool-result.js +272 -0
- package/esm/src/guardrails/types.d.ts +250 -0
- package/esm/src/guardrails/types.js +58 -0
- package/esm/src/host/client-turn.d.ts +36 -0
- package/esm/src/host/client-turn.js +80 -0
- package/esm/src/host/mint-trace.d.ts +29 -0
- package/esm/src/host/mint-trace.js +60 -0
- package/esm/src/host/mod.d.ts +13 -0
- package/esm/src/host/mod.js +11 -0
- package/esm/src/host/readStreamingJsonStringField.d.ts +2 -0
- package/esm/src/host/readStreamingJsonStringField.js +27 -0
- package/esm/src/host/reply.d.ts +13 -0
- package/esm/src/host/reply.js +41 -0
- package/esm/src/interface/blocks.d.ts +11 -0
- package/esm/src/interface/blocks.js +283 -0
- package/esm/src/interface/composer-actions.d.ts +24 -0
- package/esm/src/interface/composer-actions.js +33 -0
- package/esm/src/interface/draft.d.ts +20 -0
- package/esm/src/interface/draft.js +39 -0
- package/esm/src/interface/from-profile.d.ts +24 -0
- package/esm/src/interface/from-profile.js +179 -0
- package/esm/src/interface/history.d.ts +46 -0
- package/esm/src/interface/history.js +278 -0
- package/esm/src/interface/inputs.d.ts +11 -0
- package/esm/src/interface/inputs.js +67 -0
- package/esm/src/interface/mod.d.ts +33 -0
- package/esm/src/interface/mod.js +22 -0
- package/esm/src/interface/models.d.ts +26 -0
- package/esm/src/interface/models.js +50 -0
- package/esm/src/interface/pending.d.ts +57 -0
- package/esm/src/interface/pending.js +152 -0
- package/esm/src/interface/profile-interface.d.ts +4 -0
- package/esm/src/interface/profile-interface.js +237 -0
- package/esm/src/interface/session.d.ts +71 -0
- package/esm/src/interface/session.js +128 -0
- 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 +32 -0
- package/esm/src/interface/tool-calls.js +127 -0
- package/esm/src/interface/tool-invoke.d.ts +6 -0
- package/esm/src/interface/tool-invoke.js +22 -0
- package/esm/src/interface/tool-media.d.ts +11 -0
- package/esm/src/interface/tool-media.js +113 -0
- package/esm/src/interface/types.d.ts +278 -0
- package/esm/src/interface/types.js +5 -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/credential-source.d.ts +23 -0
- package/esm/src/kernel/auth/credential-source.js +11 -0
- package/esm/src/kernel/auth/crypto.d.ts +56 -0
- package/esm/src/kernel/auth/crypto.js +163 -0
- package/esm/src/kernel/auth/mod.d.ts +5 -0
- package/esm/src/kernel/auth/mod.js +5 -0
- package/esm/src/kernel/auth/oauth.d.ts +18 -0
- package/esm/src/kernel/auth/oauth.js +439 -0
- package/esm/src/kernel/auth/scope-refusal.d.ts +9 -0
- package/esm/src/kernel/auth/scope-refusal.js +8 -0
- package/esm/src/kernel/auth/typed-secret.d.ts +13 -0
- package/esm/src/kernel/auth/typed-secret.js +23 -0
- package/esm/src/kernel/auth/types.d.ts +152 -0
- package/esm/src/kernel/auth/types.js +1 -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 +72 -0
- package/esm/src/kernel/default-scope.js +121 -0
- 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 +71 -0
- package/esm/src/kernel/engine/compaction.js +203 -0
- package/esm/src/kernel/engine/decision.d.ts +43 -0
- package/esm/src/kernel/engine/decision.js +439 -0
- package/esm/src/kernel/engine/embedding.d.ts +8 -0
- package/esm/src/kernel/engine/embedding.js +118 -0
- package/esm/src/kernel/engine/hash.d.ts +6 -0
- package/esm/src/kernel/engine/hash.js +22 -0
- package/esm/src/kernel/engine/live-inbound.d.ts +19 -0
- package/esm/src/kernel/engine/live-inbound.js +44 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +15 -0
- package/esm/src/kernel/engine/live-ingress.js +42 -0
- package/esm/src/kernel/engine/record.d.ts +3 -0
- package/esm/src/kernel/engine/record.js +11 -0
- package/esm/src/kernel/engine/repair.d.ts +7 -0
- package/esm/src/kernel/engine/repair.js +36 -0
- package/esm/src/kernel/engine/runner/gates.d.ts +5 -0
- package/esm/src/kernel/engine/runner/gates.js +508 -0
- package/esm/src/kernel/engine/runner/mod.d.ts +29 -0
- package/esm/src/kernel/engine/runner/mod.js +625 -0
- 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 +35 -0
- package/esm/src/kernel/engine/runner/schema-validation.js +179 -0
- package/esm/src/kernel/engine/runner/stages.d.ts +28 -0
- package/esm/src/kernel/engine/runner/stages.js +41 -0
- package/esm/src/kernel/engine/runner/state.d.ts +97 -0
- package/esm/src/kernel/engine/runner/state.js +50 -0
- 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 +14 -0
- package/esm/src/kernel/engine/runner/steps.js +519 -0
- package/esm/src/kernel/engine/runner/stream.d.ts +71 -0
- package/esm/src/kernel/engine/runner/stream.js +376 -0
- package/esm/src/kernel/engine/runner/usage.d.ts +45 -0
- package/esm/src/kernel/engine/runner/usage.js +148 -0
- package/esm/src/kernel/engine/session/mod.d.ts +33 -0
- package/esm/src/kernel/engine/session/mod.js +1182 -0
- package/esm/src/kernel/engine/session/session-trace.d.ts +115 -0
- package/esm/src/kernel/engine/session/session-trace.js +500 -0
- package/esm/src/kernel/engine/token-estimate.d.ts +23 -0
- package/esm/src/kernel/engine/token-estimate.js +38 -0
- package/esm/src/kernel/engine/tool-trace.d.ts +58 -0
- package/esm/src/kernel/engine/tool-trace.js +132 -0
- package/esm/src/kernel/engine/tree.d.ts +2 -0
- package/esm/src/kernel/engine/tree.js +17 -0
- package/esm/src/kernel/engine/turn-trace.d.ts +119 -0
- package/esm/src/kernel/engine/turn-trace.js +855 -0
- package/esm/src/kernel/engine/usage.d.ts +36 -0
- package/esm/src/kernel/engine/usage.js +151 -0
- package/esm/src/kernel/interaction-parts.d.ts +13 -0
- package/esm/src/kernel/interaction-parts.js +40 -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 +74 -0
- package/esm/src/kernel/mod.js +45 -0
- package/esm/src/kernel/profile-graph.d.ts +145 -0
- package/esm/src/kernel/profile-graph.js +158 -0
- package/esm/src/kernel/profile-presence.d.ts +18 -0
- package/esm/src/kernel/profile-presence.js +158 -0
- package/esm/src/kernel/profile-scope.d.ts +40 -0
- package/esm/src/kernel/profile-scope.js +274 -0
- 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 +46 -0
- package/esm/src/kernel/registry/attachments.js +220 -0
- package/esm/src/kernel/registry/catalog.d.ts +25 -0
- package/esm/src/kernel/registry/catalog.js +85 -0
- 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 +12 -0
- package/esm/src/kernel/registry/ingress.js +179 -0
- package/esm/src/kernel/registry/kernel-registry.d.ts +15 -0
- package/esm/src/kernel/registry/kernel-registry.js +15 -0
- package/esm/src/kernel/registry/profile-outputs.d.ts +3 -0
- package/esm/src/kernel/registry/profile-outputs.js +10 -0
- package/esm/src/kernel/registry/profiles.d.ts +116 -0
- package/esm/src/kernel/registry/profiles.js +773 -0
- package/esm/src/kernel/registry/provider-request.d.ts +6 -0
- package/esm/src/kernel/registry/provider-request.js +34 -0
- package/esm/src/kernel/registry/resolve.d.ts +17 -0
- package/esm/src/kernel/registry/resolve.js +238 -0
- package/esm/src/kernel/registry/schemas.d.ts +12 -0
- package/esm/src/kernel/registry/schemas.js +27 -0
- 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/sole-model.d.ts +2 -0
- package/esm/src/kernel/registry/sole-model.js +4 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +8 -0
- package/esm/src/kernel/registry/system-prompt.js +32 -0
- package/esm/src/kernel/registry/system-role.d.ts +3 -0
- package/esm/src/kernel/registry/system-role.js +9 -0
- package/esm/src/kernel/registry/vault.d.ts +14 -0
- package/esm/src/kernel/registry/vault.js +1 -0
- package/esm/src/kernel/schema.d.ts +226 -0
- package/esm/src/kernel/schema.js +895 -0
- package/esm/src/kernel/scope.d.ts +29 -0
- package/esm/src/kernel/scope.js +45 -0
- package/esm/src/kernel/stages.d.ts +176 -0
- package/esm/src/kernel/stages.js +431 -0
- package/esm/src/kernel/stop.d.ts +116 -0
- package/esm/src/kernel/stop.js +137 -0
- package/esm/src/kernel/system-parts.d.ts +26 -0
- package/esm/src/kernel/system-parts.js +98 -0
- package/esm/src/kernel/tools/activity-label.d.ts +22 -0
- package/esm/src/kernel/tools/activity-label.js +435 -0
- 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 +66 -0
- package/esm/src/kernel/tools/events.js +184 -0
- package/esm/src/kernel/tools/execute.d.ts +100 -0
- package/esm/src/kernel/tools/execute.js +1173 -0
- package/esm/src/kernel/tools/gate-answer.d.ts +60 -0
- package/esm/src/kernel/tools/gate-answer.js +65 -0
- 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 +17 -0
- package/esm/src/kernel/tools/harness.js +43 -0
- 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 +32 -0
- package/esm/src/kernel/tools/invoke.js +282 -0
- package/esm/src/kernel/tools/mcp-sessions.d.ts +23 -0
- package/esm/src/kernel/tools/mcp-sessions.js +69 -0
- package/esm/src/kernel/tools/mod.d.ts +10 -0
- package/esm/src/kernel/tools/mod.js +7 -0
- package/esm/src/kernel/tools/model-text.d.ts +16 -0
- package/esm/src/kernel/tools/model-text.js +31 -0
- 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 +15 -0
- package/esm/src/kernel/tools/permission.js +38 -0
- package/esm/src/kernel/tools/project.d.ts +11 -0
- package/esm/src/kernel/tools/project.js +55 -0
- package/esm/src/kernel/tools/registry.d.ts +27 -0
- package/esm/src/kernel/tools/registry.js +143 -0
- package/esm/src/kernel/tools/remote.d.ts +144 -0
- package/esm/src/kernel/tools/remote.js +1125 -0
- package/esm/src/kernel/tools/resolve.d.ts +31 -0
- package/esm/src/kernel/tools/resolve.js +267 -0
- 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 +10 -0
- package/esm/src/kernel/tools/schema.js +95 -0
- package/esm/src/kernel/tools/signed-in-fetch.d.ts +19 -0
- package/esm/src/kernel/tools/signed-in-fetch.js +45 -0
- package/esm/src/kernel/tools/stage-run.d.ts +116 -0
- package/esm/src/kernel/tools/stage-run.js +198 -0
- package/esm/src/kernel/tools/types.d.ts +497 -0
- package/esm/src/kernel/tools/types.js +1 -0
- 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 +776 -0
- package/esm/src/kernel/turn-events.js +543 -0
- package/esm/src/kernel/types.d.ts +1025 -0
- package/esm/src/kernel/types.js +1 -0
- package/esm/src/kernel/util/base64.d.ts +4 -0
- package/esm/src/kernel/util/base64.js +15 -0
- package/esm/src/kernel/util/exact-type.d.ts +31 -0
- package/esm/src/kernel/util/exact-type.js +1 -0
- package/esm/src/kernel/util/find-last.d.ts +3 -0
- package/esm/src/kernel/util/find-last.js +9 -0
- package/esm/src/kernel/util/mime.d.ts +2 -0
- package/esm/src/kernel/util/mime.js +4 -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 +2 -0
- package/esm/src/kernel/util/record.js +4 -0
- package/esm/src/observability/destinations.d.ts +14 -0
- package/esm/src/observability/destinations.js +43 -0
- package/esm/src/observability/jsonl.d.ts +18 -0
- package/esm/src/observability/jsonl.js +110 -0
- package/esm/src/observability/mod.d.ts +22 -0
- package/esm/src/observability/mod.js +16 -0
- package/esm/src/observability/openinference.d.ts +11 -0
- package/esm/src/observability/openinference.js +205 -0
- package/esm/src/observability/otlp.d.ts +71 -0
- package/esm/src/observability/otlp.js +98 -0
- package/esm/src/observability/phoenix.d.ts +30 -0
- package/esm/src/observability/phoenix.js +71 -0
- package/esm/src/observability/policy.d.ts +17 -0
- package/esm/src/observability/policy.js +73 -0
- package/esm/src/observability/resolve-policy.d.ts +8 -0
- package/esm/src/observability/resolve-policy.js +64 -0
- package/esm/src/observability/spans.d.ts +22 -0
- package/esm/src/observability/spans.js +72 -0
- package/esm/src/observability/trace-catalog.d.ts +128 -0
- package/esm/src/observability/trace-catalog.js +906 -0
- package/esm/src/observability/trace-record.d.ts +23 -0
- package/esm/src/observability/trace-record.js +228 -0
- package/esm/src/observability/trace-schema.d.ts +57 -0
- package/esm/src/observability/trace-schema.js +59 -0
- package/esm/src/observability/trace-sink.d.ts +14 -0
- package/esm/src/observability/trace-sink.js +3 -0
- package/esm/src/observability/trace-span.d.ts +98 -0
- package/esm/src/observability/trace-span.js +201 -0
- package/esm/src/observability/trace.d.ts +13 -0
- package/esm/src/observability/trace.js +37 -0
- package/esm/src/observability/types.d.ts +109 -0
- package/esm/src/observability/types.js +3 -0
- package/esm/src/presets/google/speech-voices.d.ts +9 -0
- package/esm/src/presets/google/speech-voices.js +38 -0
- package/esm/src/presets/google-limits.d.ts +13 -0
- package/esm/src/presets/google-limits.js +18 -0
- package/esm/src/presets/google.d.ts +119 -0
- package/esm/src/presets/google.js +197 -0
- package/esm/src/presets/local.d.ts +26 -0
- package/esm/src/presets/local.js +30 -0
- package/esm/src/presets/mod.d.ts +11 -0
- package/esm/src/presets/mod.js +9 -0
- package/esm/src/presets/openrouter.d.ts +26 -0
- package/esm/src/presets/openrouter.js +31 -0
- 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 +105 -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 +8 -0
- package/esm/src/providers/google/api-error.js +123 -0
- 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 +5 -0
- package/esm/src/providers/google/grounding.js +226 -0
- package/esm/src/providers/google/interactions/framing.d.ts +27 -0
- package/esm/src/providers/google/interactions/framing.js +304 -0
- package/esm/src/providers/google/interactions/steps.d.ts +12 -0
- package/esm/src/providers/google/interactions/steps.js +200 -0
- package/esm/src/providers/google/interactions/stream.d.ts +23 -0
- package/esm/src/providers/google/interactions/stream.js +230 -0
- package/esm/src/providers/google/live/framing.d.ts +61 -0
- package/esm/src/providers/google/live/framing.js +597 -0
- package/esm/src/providers/google/live/stream.d.ts +13 -0
- package/esm/src/providers/google/live/stream.js +57 -0
- package/esm/src/providers/google/thinking.d.ts +3 -0
- package/esm/src/providers/google/thinking.js +8 -0
- package/esm/src/providers/google/urls.d.ts +4 -0
- package/esm/src/providers/google/urls.js +4 -0
- package/esm/src/providers/google/usage.d.ts +7 -0
- package/esm/src/providers/google/usage.js +49 -0
- 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 +32 -0
- package/esm/src/providers/mod.js +23 -0
- 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 +15 -0
- package/esm/src/providers/openrouter/image.js +34 -0
- 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 +19 -0
- package/esm/src/providers/openrouter/openai/compat.js +162 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +9 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +115 -0
- 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/openai/usage.d.ts +8 -0
- package/esm/src/providers/openrouter/openai/usage.js +56 -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.d.ts +14 -0
- package/esm/src/providers/shared/pcm.js +78 -0
- 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 +11 -0
- package/esm/src/providers/shared/response-identity.js +12 -0
- package/esm/src/providers/shared/retry.d.ts +17 -0
- package/esm/src/providers/shared/retry.js +90 -0
- package/esm/src/providers/shared/sse.d.ts +6 -0
- package/esm/src/providers/shared/sse.js +93 -0
- package/esm/src/providers/shared/structured-output.d.ts +4 -0
- package/esm/src/providers/shared/structured-output.js +11 -0
- package/esm/src/providers/shared/tool-args.d.ts +24 -0
- package/esm/src/providers/shared/tool-args.js +88 -0
- 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 +8 -0
- package/esm/src/providers/shared/upstream-tap.js +85 -0
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/shared/upstream-tape.js +88 -0
- 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 +51 -0
- package/esm/src/providers/types.js +2 -0
- 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 +46 -0
- package/esm/src/surface/formats.js +178 -0
- package/esm/src/surface/mod.d.ts +16 -0
- package/esm/src/surface/mod.js +12 -0
- package/esm/src/surface/runtime.d.ts +44 -0
- package/esm/src/surface/runtime.js +441 -0
- package/esm/src/surface/tools.d.ts +9 -0
- package/esm/src/surface/tools.js +62 -0
- package/esm/src/surface/types.d.ts +110 -0
- package/esm/src/surface/types.js +4 -0
- package/package.json +100 -4
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The detectors, Theorem's and the host's own, run on a reply as it streams.
|
|
3
|
+
*
|
|
4
|
+
* Each matches its patterns on the reply as written and, for
|
|
5
|
+
* injection phrasing, on
|
|
6
|
+
* rewrites of it (typo-folded, Unicode-folded, rot13, leet, URL-decoded, read
|
|
7
|
+
* backwards). The stream keeps each rewrite growing with the reply and, for
|
|
8
|
+
* every pattern, an automaton accepting every match of it and more
|
|
9
|
+
* (`egress-automata.ts`). Text is held from the earliest place any match could
|
|
10
|
+
* still be under way in any rewrite, and nowhere else: a match that completes
|
|
11
|
+
* later starts at or after that place, so none of it has been released.
|
|
12
|
+
*
|
|
13
|
+
* A match whose outcome no later text can change is settled; a settled match
|
|
14
|
+
* of the pattern itself (not the automaton) is a hit, and does what
|
|
15
|
+
* `guardrails.detect` sets (`detect-stream.ts`). Each character is read a bounded number of times, so a
|
|
16
|
+
* long reply costs time in proportion to its length.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { EGRESS_PATTERNS, notePattern, } from './egress-patterns.js';
|
|
21
|
+
import { IMAGE_PATTERNS, imageReadings, LINK_PATTERNS, linkReadings, } from './egress-urls.js';
|
|
22
|
+
import { decodeUrlRuns, INJECTION_BLOBS, TYPO_TARGETS, tryLeet, tryRot13, typoNormalize, } from './injection.js';
|
|
23
|
+
import { REVERSED_INJECTION_PATTERNS } from './injection-reversed.js';
|
|
24
|
+
import { isEmoji, normalizeCodePoint } from './normalize.js';
|
|
25
|
+
import { detectRule } from './rules.js';
|
|
26
|
+
import { cardHit, SENSITIVE_PATTERNS } from './sensitive.js';
|
|
27
|
+
import { namePatterns } from './tool-leak.js';
|
|
28
|
+
const UNITS = 0x10000;
|
|
29
|
+
function compile(data) {
|
|
30
|
+
const classCount = data.classStarts.length;
|
|
31
|
+
const classOf = new Uint16Array(UNITS);
|
|
32
|
+
for (let k = 0; k < classCount; k++) {
|
|
33
|
+
classOf.fill(k, data.classStarts[k], data.classStarts[k + 1] ?? UNITS);
|
|
34
|
+
}
|
|
35
|
+
const has = data.charsets.map((classes) => {
|
|
36
|
+
const row = new Uint8Array(classCount);
|
|
37
|
+
for (const k of classes)
|
|
38
|
+
row[k] = 1;
|
|
39
|
+
return row;
|
|
40
|
+
});
|
|
41
|
+
const size = data.nodes.length;
|
|
42
|
+
const pattern = new Int32Array(size);
|
|
43
|
+
const final = new Uint8Array(size);
|
|
44
|
+
const edges = [];
|
|
45
|
+
data.nodes.forEach((node, id) => {
|
|
46
|
+
pattern[id] = node[0];
|
|
47
|
+
final[id] = node[1];
|
|
48
|
+
edges.push(Int32Array.from(node.slice(2)));
|
|
49
|
+
});
|
|
50
|
+
return { classOf, has, pattern, final, edges, initials: data.initials, leads: data.leads, size };
|
|
51
|
+
}
|
|
52
|
+
/** Theorem's automata, once `loadEgressAutomata` has read them. */
|
|
53
|
+
let bundled;
|
|
54
|
+
let forward;
|
|
55
|
+
let backward;
|
|
56
|
+
/**
|
|
57
|
+
* Reads Theorem's automata, the most of the kernel's size, the first time a stream needs them.
|
|
58
|
+
* Every gate's constructor waits for this, so an app that never streams a reply never loads them.
|
|
59
|
+
*/
|
|
60
|
+
async function loadEgressAutomata() {
|
|
61
|
+
if (bundled)
|
|
62
|
+
return;
|
|
63
|
+
const { FORWARD_AUTOMATON, REVERSED_AUTOMATON } = await import('./egress-automata.js');
|
|
64
|
+
bundled = { forward: FORWARD_AUTOMATON, reversed: REVERSED_AUTOMATON };
|
|
65
|
+
}
|
|
66
|
+
function loaded() {
|
|
67
|
+
if (!bundled) {
|
|
68
|
+
throw new Error('A stream was opened before loadEgressAutomata() finished.'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
69
|
+
}
|
|
70
|
+
return bundled;
|
|
71
|
+
}
|
|
72
|
+
/** The code units a case-insensitive regex reads as each one: ES `Canonicalize` without `u`. */
|
|
73
|
+
let caseUnits;
|
|
74
|
+
function canonicalUnit(unit) {
|
|
75
|
+
const upper = String.fromCharCode(unit).toUpperCase();
|
|
76
|
+
if (upper.length !== 1)
|
|
77
|
+
return unit;
|
|
78
|
+
const code = upper.charCodeAt(0);
|
|
79
|
+
return unit >= 0x80 && code < 0x80 ? unit : code;
|
|
80
|
+
}
|
|
81
|
+
function unitsLike(unit) {
|
|
82
|
+
if (!caseUnits) {
|
|
83
|
+
caseUnits = new Map();
|
|
84
|
+
for (let u = 0; u < UNITS; u++) {
|
|
85
|
+
const canon = canonicalUnit(u);
|
|
86
|
+
const units = caseUnits.get(canon);
|
|
87
|
+
if (units)
|
|
88
|
+
units.push(u);
|
|
89
|
+
else
|
|
90
|
+
caseUnits.set(canon, [u]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return caseUnits.get(canonicalUnit(unit)) ?? [unit];
|
|
94
|
+
}
|
|
95
|
+
/** Class starts that give each of the sorted `units` a class of its own. */
|
|
96
|
+
function classStartsOf(units) {
|
|
97
|
+
const classStarts = [0];
|
|
98
|
+
for (const unit of units) {
|
|
99
|
+
if (classStarts.at(-1) !== unit)
|
|
100
|
+
classStarts.push(unit);
|
|
101
|
+
if (unit + 1 < UNITS)
|
|
102
|
+
classStarts.push(unit + 1);
|
|
103
|
+
}
|
|
104
|
+
return classStarts;
|
|
105
|
+
}
|
|
106
|
+
/** Literal automata, kept per text so each is built once. */
|
|
107
|
+
const literalAutomata = new Map();
|
|
108
|
+
/** An automaton for `text` as written, case aside, as `notePattern` reads it. */
|
|
109
|
+
function literalAutomaton(text) {
|
|
110
|
+
const cached = literalAutomata.get(text);
|
|
111
|
+
if (cached)
|
|
112
|
+
return cached;
|
|
113
|
+
const positions = Array.from({ length: text.length }, (_, i) => unitsLike(text.charCodeAt(i)));
|
|
114
|
+
const classStarts = classStartsOf([...new Set(positions.flat())].sort((a, b) => a - b));
|
|
115
|
+
const classOfUnit = (unit) => classStarts.indexOf(unit);
|
|
116
|
+
const charsets = positions.map((like) => like.map(classOfUnit));
|
|
117
|
+
const nodes = [...positions.map((_, i) => [0, 0, i + 1, i]), [0, 1]];
|
|
118
|
+
const automaton = compile({ classStarts, charsets, initials: [0], leads: [-1], nodes });
|
|
119
|
+
literalAutomata.set(text, automaton);
|
|
120
|
+
return automaton;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* An automaton for each of `texts` as written, case and all: pattern `i` is `texts[i]`. Kept
|
|
124
|
+
* per list, so a profile's is built once. Each unit has a charset of its own: charset `k` is the
|
|
125
|
+
* class of `units[k]`.
|
|
126
|
+
*/
|
|
127
|
+
function literalsAutomaton(texts) {
|
|
128
|
+
const key = JSON.stringify(texts);
|
|
129
|
+
const cached = literalAutomata.get(key);
|
|
130
|
+
if (cached)
|
|
131
|
+
return cached;
|
|
132
|
+
const units = [...new Set(texts.flatMap((text) => Array.from(text, (c) => c.charCodeAt(0))))];
|
|
133
|
+
units.sort((a, b) => a - b);
|
|
134
|
+
const classStarts = classStartsOf(units);
|
|
135
|
+
const charsets = units.map((unit) => [classStarts.indexOf(unit)]);
|
|
136
|
+
const nodes = [];
|
|
137
|
+
const initials = texts.map((text, id) => {
|
|
138
|
+
const first = nodes.length;
|
|
139
|
+
for (let i = 0; i < text.length; i++) {
|
|
140
|
+
nodes.push([id, 0, nodes.length + 1, units.indexOf(text.charCodeAt(i))]);
|
|
141
|
+
}
|
|
142
|
+
nodes.push([id, 1]);
|
|
143
|
+
return first;
|
|
144
|
+
});
|
|
145
|
+
const leads = texts.map(() => -1);
|
|
146
|
+
const automaton = compile({ classStarts, charsets, initials, leads, nodes });
|
|
147
|
+
literalAutomata.set(key, automaton);
|
|
148
|
+
return automaton;
|
|
149
|
+
}
|
|
150
|
+
/** The first edges of the given patterns, per character class. */
|
|
151
|
+
function startEdges(automaton, patterns) {
|
|
152
|
+
const classCount = automaton.has[0]?.length ?? 0;
|
|
153
|
+
const byClass = Array.from({ length: classCount }, () => []);
|
|
154
|
+
for (const p of patterns) {
|
|
155
|
+
const e = automaton.edges[automaton.initials[p]];
|
|
156
|
+
for (let i = 0; i < e.length; i += 2) {
|
|
157
|
+
const set = automaton.has[e[i + 1]];
|
|
158
|
+
for (let k = 0; k < classCount; k++)
|
|
159
|
+
if (set[k])
|
|
160
|
+
byClass[k]?.push(e[i]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return byClass.map((targets) => Int32Array.from(targets));
|
|
164
|
+
}
|
|
165
|
+
function grow(target, piece) {
|
|
166
|
+
target.text += piece;
|
|
167
|
+
target.fresh += piece;
|
|
168
|
+
}
|
|
169
|
+
/** Where `fresh` starts in `text`. */
|
|
170
|
+
function freshFrom(source) {
|
|
171
|
+
return source.text.length - source.fresh.length;
|
|
172
|
+
}
|
|
173
|
+
function unitAt(source, at) {
|
|
174
|
+
const base = freshFrom(source);
|
|
175
|
+
return at >= base ? source.fresh.charCodeAt(at - base) : source.text.charCodeAt(at);
|
|
176
|
+
}
|
|
177
|
+
function sliceOf(source, from, to = source.text.length) {
|
|
178
|
+
const base = freshFrom(source);
|
|
179
|
+
return from >= base ? source.fresh.slice(from - base, to - base) : source.text.slice(from, to);
|
|
180
|
+
}
|
|
181
|
+
/** The reply itself. */
|
|
182
|
+
function rawView(reply) {
|
|
183
|
+
const view = {
|
|
184
|
+
text: '',
|
|
185
|
+
fresh: '',
|
|
186
|
+
rawAt: (j) => j,
|
|
187
|
+
update() {
|
|
188
|
+
view.text = reply.text;
|
|
189
|
+
view.fresh = reply.fresh;
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
return view;
|
|
193
|
+
}
|
|
194
|
+
/** A rewrite of each UTF-16 unit on its own (rot13, leet): settled as soon as read. */
|
|
195
|
+
function unitView(source, rewrite) {
|
|
196
|
+
const view = {
|
|
197
|
+
text: '',
|
|
198
|
+
fresh: '',
|
|
199
|
+
rawAt: source.rawAt,
|
|
200
|
+
update() {
|
|
201
|
+
view.fresh = '';
|
|
202
|
+
grow(view, rewrite(sliceOf(source, view.text.length)));
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
return view;
|
|
206
|
+
}
|
|
207
|
+
const WORD_UNIT = /\w/;
|
|
208
|
+
const MAX_TARGET = Math.max(...TYPO_TARGETS.map((target) => target.length));
|
|
209
|
+
/** Whether a word still being written could yet fold to a typo target: a misspelling may be a letter longer. */
|
|
210
|
+
function mayFold(run) {
|
|
211
|
+
return run.length <= MAX_TARGET + 1 && /^[A-Za-z]*$/.test(run);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* `typoNormalize` of the source. It rewrites whole words (`\w` runs), so a
|
|
215
|
+
* word settles when a non-word character ends it, or as soon as it can no
|
|
216
|
+
* longer fold to a target (then the rest of it passes as is). A corrected word
|
|
217
|
+
* may be a letter longer or shorter than the word as written, so each
|
|
218
|
+
* character keeps the source index it stands for.
|
|
219
|
+
*/
|
|
220
|
+
function typoView(source) {
|
|
221
|
+
let read = 0;
|
|
222
|
+
/** The word being read, while it may still fold. */
|
|
223
|
+
let word;
|
|
224
|
+
let inWord = false;
|
|
225
|
+
/** Per settled character, the source index it stands for. */
|
|
226
|
+
const from = [];
|
|
227
|
+
const put = (piece, start, length) => {
|
|
228
|
+
grow(view, piece);
|
|
229
|
+
for (let i = 0; i < piece.length; i++)
|
|
230
|
+
from.push(start + Math.min(i, length - 1));
|
|
231
|
+
};
|
|
232
|
+
const view = {
|
|
233
|
+
text: '',
|
|
234
|
+
fresh: '',
|
|
235
|
+
rawAt: (j) => source.rawAt(j < from.length ? from[j] : read - (word?.length ?? 0)),
|
|
236
|
+
update() {
|
|
237
|
+
view.fresh = '';
|
|
238
|
+
for (const end = source.text.length; read < end; read++) {
|
|
239
|
+
const unit = String.fromCharCode(unitAt(source, read));
|
|
240
|
+
const wordUnit = WORD_UNIT.test(unit);
|
|
241
|
+
if (wordUnit && !inWord)
|
|
242
|
+
word = '';
|
|
243
|
+
inWord = wordUnit;
|
|
244
|
+
if (!wordUnit) {
|
|
245
|
+
if (word !== undefined)
|
|
246
|
+
put(typoNormalize(word), read - word.length, word.length);
|
|
247
|
+
word = undefined;
|
|
248
|
+
put(unit, read, 1);
|
|
249
|
+
}
|
|
250
|
+
else if (word === undefined)
|
|
251
|
+
put(unit, read, 1);
|
|
252
|
+
else
|
|
253
|
+
word += unit;
|
|
254
|
+
}
|
|
255
|
+
if (word !== undefined && !mayFold(word)) {
|
|
256
|
+
put(word, read - word.length, word.length);
|
|
257
|
+
word = undefined;
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
return view;
|
|
262
|
+
}
|
|
263
|
+
function mappedView(pendingAt) {
|
|
264
|
+
const view = {
|
|
265
|
+
text: '',
|
|
266
|
+
fresh: '',
|
|
267
|
+
from: [],
|
|
268
|
+
rawAt: (j) => (j < view.from.length ? view.from[j] : pendingAt()),
|
|
269
|
+
update() { },
|
|
270
|
+
};
|
|
271
|
+
return view;
|
|
272
|
+
}
|
|
273
|
+
function append(view, text, from) {
|
|
274
|
+
grow(view, text);
|
|
275
|
+
for (let i = 0; i < text.length; i++)
|
|
276
|
+
view.from.push(from);
|
|
277
|
+
}
|
|
278
|
+
const HIGH_LO = 0xd800;
|
|
279
|
+
const HIGH_HI = 0xdbff;
|
|
280
|
+
const ASCII_LETTER = /[A-Za-z]/;
|
|
281
|
+
const LOW_LO = 0xdc00;
|
|
282
|
+
const LOW_HI = 0xdfff;
|
|
283
|
+
/**
|
|
284
|
+
* `normalizeForDetection` of the reply, in its three passes: each code point
|
|
285
|
+
* mapped, emoji runs between letters dropped, a backslash before a letter
|
|
286
|
+
* dropped. Unsettled: a high surrogate whose low half may follow (in the reply
|
|
287
|
+
* or in the mapped text), an emoji run after a letter until the character
|
|
288
|
+
* after it, a backslash until the character after it.
|
|
289
|
+
*/
|
|
290
|
+
function normalizedReplyView(reply) {
|
|
291
|
+
let read = 0;
|
|
292
|
+
/** Mapped text the emoji pass has not read: at most a trailing high surrogate. */
|
|
293
|
+
let mapped = '';
|
|
294
|
+
let mappedFrom = [];
|
|
295
|
+
/** The emoji run after a letter, waiting on the character after it. */
|
|
296
|
+
let run = '';
|
|
297
|
+
let runFrom = [];
|
|
298
|
+
let afterLetter = false;
|
|
299
|
+
/** A backslash waiting on the character after it: its reply index, or -1. */
|
|
300
|
+
let slashFrom = -1;
|
|
301
|
+
const pendingAt = () => Math.min(read, mappedFrom[0] ?? read, runFrom[0] ?? read, slashFrom < 0 ? read : slashFrom);
|
|
302
|
+
const view = mappedView(pendingAt);
|
|
303
|
+
function backslashPass(unit, from) {
|
|
304
|
+
if (slashFrom >= 0) {
|
|
305
|
+
if (!ASCII_LETTER.test(unit))
|
|
306
|
+
append(view, '\\', slashFrom);
|
|
307
|
+
slashFrom = -1;
|
|
308
|
+
}
|
|
309
|
+
if (unit === '\\')
|
|
310
|
+
slashFrom = from;
|
|
311
|
+
else
|
|
312
|
+
append(view, unit, from);
|
|
313
|
+
}
|
|
314
|
+
function emojiPass(point, from) {
|
|
315
|
+
if (isEmoji(point) && (afterLetter || run)) {
|
|
316
|
+
run += point;
|
|
317
|
+
for (let i = 0; i < point.length; i++)
|
|
318
|
+
runFrom.push(from);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (run) {
|
|
322
|
+
if (!ASCII_LETTER.test(point)) {
|
|
323
|
+
for (let i = 0; i < run.length; i++)
|
|
324
|
+
backslashPass(run[i], runFrom[i]);
|
|
325
|
+
}
|
|
326
|
+
run = '';
|
|
327
|
+
runFrom = [];
|
|
328
|
+
}
|
|
329
|
+
for (let i = 0; i < point.length; i++)
|
|
330
|
+
backslashPass(point[i], from);
|
|
331
|
+
afterLetter = ASCII_LETTER.test(point) && point.length === 1;
|
|
332
|
+
}
|
|
333
|
+
/** Feed the mapped text to the emoji pass by code point, keeping a high surrogate at the end. */
|
|
334
|
+
function drainMapped() {
|
|
335
|
+
let i = 0;
|
|
336
|
+
while (i < mapped.length) {
|
|
337
|
+
const code = mapped.charCodeAt(i);
|
|
338
|
+
const high = code >= HIGH_LO && code <= HIGH_HI;
|
|
339
|
+
if (high && i + 1 >= mapped.length)
|
|
340
|
+
break;
|
|
341
|
+
const low = mapped.charCodeAt(i + 1);
|
|
342
|
+
const width = high && low >= LOW_LO && low <= LOW_HI ? 2 : 1;
|
|
343
|
+
emojiPass(mapped.slice(i, i + width), mappedFrom[i]);
|
|
344
|
+
i += width;
|
|
345
|
+
}
|
|
346
|
+
mapped = mapped.slice(i);
|
|
347
|
+
mappedFrom = mappedFrom.slice(i);
|
|
348
|
+
}
|
|
349
|
+
view.update = () => {
|
|
350
|
+
view.fresh = '';
|
|
351
|
+
const end = reply.text.length;
|
|
352
|
+
while (read < end) {
|
|
353
|
+
const code = unitAt(reply, read);
|
|
354
|
+
const high = code >= HIGH_LO && code <= HIGH_HI;
|
|
355
|
+
if (high && read + 1 >= end)
|
|
356
|
+
break;
|
|
357
|
+
const low = unitAt(reply, read + 1);
|
|
358
|
+
const width = high && low >= LOW_LO && low <= LOW_HI ? 2 : 1;
|
|
359
|
+
const out = normalizeCodePoint(sliceOf(reply, read, read + width));
|
|
360
|
+
mapped += out;
|
|
361
|
+
for (let i = 0; i < out.length; i++)
|
|
362
|
+
mappedFrom.push(read);
|
|
363
|
+
read += width;
|
|
364
|
+
}
|
|
365
|
+
drainMapped();
|
|
366
|
+
};
|
|
367
|
+
return view;
|
|
368
|
+
}
|
|
369
|
+
const URL_HEX = /[0-9A-Fa-f]/;
|
|
370
|
+
/**
|
|
371
|
+
* `decodeUrlRuns` of the reply: a run of `%XX` escapes is unsettled until the
|
|
372
|
+
* character after it shows the run is over. Its decoded text holds from the
|
|
373
|
+
* run's first `%`.
|
|
374
|
+
*/
|
|
375
|
+
function urlView(reply) {
|
|
376
|
+
let read = 0;
|
|
377
|
+
/** Where the escape run being read starts, or -1. */
|
|
378
|
+
let runStart = -1;
|
|
379
|
+
const view = mappedView(() => (runStart < 0 ? read : runStart));
|
|
380
|
+
view.update = () => {
|
|
381
|
+
view.fresh = '';
|
|
382
|
+
const end = reply.text.length;
|
|
383
|
+
while (read < end) {
|
|
384
|
+
const unit = String.fromCharCode(unitAt(reply, read));
|
|
385
|
+
if (runStart < 0) {
|
|
386
|
+
if (unit === '%') {
|
|
387
|
+
runStart = read;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
append(view, unit, read);
|
|
391
|
+
read++;
|
|
392
|
+
}
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
// why: Inside a run: complete escapes end at runStart + 3k.
|
|
396
|
+
const offset = (read - runStart) % 3;
|
|
397
|
+
const fits = offset === 0 ? unit === '%' : URL_HEX.test(unit);
|
|
398
|
+
if (fits) {
|
|
399
|
+
read++;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
endRun(read - offset);
|
|
403
|
+
// why: The broken escape's text is literal; `read` rereads from its end.
|
|
404
|
+
for (let i = read - offset; i < read; i++)
|
|
405
|
+
append(view, String.fromCharCode(unitAt(reply, i)), i);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
function endRun(end) {
|
|
409
|
+
if (end > runStart)
|
|
410
|
+
append(view, decodeUrlRuns(sliceOf(reply, runStart, end)), runStart);
|
|
411
|
+
runStart = -1;
|
|
412
|
+
}
|
|
413
|
+
return view;
|
|
414
|
+
}
|
|
415
|
+
const INJECTION_KINDS = new Set([
|
|
416
|
+
'injection',
|
|
417
|
+
'base64',
|
|
418
|
+
'hex',
|
|
419
|
+
'spaced',
|
|
420
|
+
'pipe',
|
|
421
|
+
]);
|
|
422
|
+
const BLOB_HITS = new Map(INJECTION_BLOBS.map(({ pattern, hit }) => [pattern, hit]));
|
|
423
|
+
const SENSITIVE_HITS = new Map(SENSITIVE_PATTERNS.flatMap(({ pattern, hit }) => (hit ? [[pattern, hit]] : [])));
|
|
424
|
+
/** What a match of `detector`, or of the host's own by its id, reports, with `detectAt`'s severity. */
|
|
425
|
+
function detectorRule(detector) {
|
|
426
|
+
return { rule: detectRule(detector), severity: 'high', detector };
|
|
427
|
+
}
|
|
428
|
+
const INJECTION_RULE = detectorRule('injection');
|
|
429
|
+
const MARKER_RULE = detectorRule('marker_leak');
|
|
430
|
+
const TOOL_RULE = detectorRule('tool_leak');
|
|
431
|
+
/** What a pattern's match reports: its detector's rule. */
|
|
432
|
+
function patternRule({ kind, group }) {
|
|
433
|
+
if (INJECTION_KINDS.has(kind))
|
|
434
|
+
return INJECTION_RULE;
|
|
435
|
+
switch (kind) {
|
|
436
|
+
case 'sensitive':
|
|
437
|
+
case 'card':
|
|
438
|
+
return group === undefined ? undefined : detectorRule(group);
|
|
439
|
+
case 'boundary':
|
|
440
|
+
return MARKER_RULE;
|
|
441
|
+
case 'image':
|
|
442
|
+
return detectorRule('ungiven_images');
|
|
443
|
+
default:
|
|
444
|
+
return detectorRule('ungiven_links');
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/** How a settled match of `pattern` is read: a test of the match, or a finder in place of the regex. */
|
|
448
|
+
function patternReading(kind, pattern, urls) {
|
|
449
|
+
if (kind === 'card')
|
|
450
|
+
return { hit: cardHit };
|
|
451
|
+
if (kind === 'image' || kind === 'link') {
|
|
452
|
+
const index = (kind === 'image' ? IMAGE_PATTERNS : LINK_PATTERNS).indexOf(pattern);
|
|
453
|
+
const reading = urls[kind][index];
|
|
454
|
+
return 'find' in reading ? { find: reading.find } : { hit: reading.test };
|
|
455
|
+
}
|
|
456
|
+
const hit = BLOB_HITS.get(pattern) ?? SENSITIVE_HITS.get(pattern);
|
|
457
|
+
return hit ? { hit } : {};
|
|
458
|
+
}
|
|
459
|
+
/** The patterns of `detectors`, read on the reply as written. */
|
|
460
|
+
function forwardPatterns(detectors, { given, allow, skipImages }) {
|
|
461
|
+
const scope = (check) => ({ ...check, ...(given ? { given } : {}) });
|
|
462
|
+
const urls = {
|
|
463
|
+
image: imageReadings(scope(allow?.ungiven_images)),
|
|
464
|
+
link: linkReadings(scope(allow?.ungiven_links), skipImages ?? detectors.includes('ungiven_images')),
|
|
465
|
+
};
|
|
466
|
+
const out = [];
|
|
467
|
+
EGRESS_PATTERNS.forEach((entry, id) => {
|
|
468
|
+
const rule = patternRule(entry);
|
|
469
|
+
if (!(rule?.detector && detectors.includes(rule.detector)))
|
|
470
|
+
return;
|
|
471
|
+
out.push({
|
|
472
|
+
...rule,
|
|
473
|
+
id,
|
|
474
|
+
regex: new RegExp(entry.pattern.source, entry.pattern.flags),
|
|
475
|
+
...patternReading(entry.kind, entry.pattern, urls),
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
/** The injection patterns, for the rewritten views. */
|
|
481
|
+
function injectionPatterns() {
|
|
482
|
+
const out = [];
|
|
483
|
+
EGRESS_PATTERNS.forEach(({ kind, pattern }, id) => {
|
|
484
|
+
if (kind !== 'injection')
|
|
485
|
+
return;
|
|
486
|
+
const hit = BLOB_HITS.get(pattern);
|
|
487
|
+
out.push({
|
|
488
|
+
...INJECTION_RULE,
|
|
489
|
+
id,
|
|
490
|
+
regex: new RegExp(pattern.source, pattern.flags),
|
|
491
|
+
...(hit ? { hit } : {}),
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
return out;
|
|
495
|
+
}
|
|
496
|
+
function reversedPatterns() {
|
|
497
|
+
return REVERSED_INJECTION_PATTERNS.map((pattern, id) => ({
|
|
498
|
+
...INJECTION_RULE,
|
|
499
|
+
id,
|
|
500
|
+
regex: new RegExp(pattern.source, pattern.flags),
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* One view scanned for some patterns: the automata, run one character at a
|
|
505
|
+
* time, give where each pattern could still be matching; the pattern's own
|
|
506
|
+
* regex, run only where the automaton reached a match, finds the match and
|
|
507
|
+
* whether it has settled. A pattern opened by an optional repeat of one class
|
|
508
|
+
* has no thread for the repeat: a thread of it starts where the run of that
|
|
509
|
+
* class it is in started, and the run the text ends in is still under way.
|
|
510
|
+
* A pattern whose matches nest (tags inside tags) is
|
|
511
|
+
* read instead by its finder, once over each stretch that settles.
|
|
512
|
+
*/
|
|
513
|
+
function createScan(view, automaton, patterns) {
|
|
514
|
+
const starts = startEdges(automaton, patterns.map((p) => p.id));
|
|
515
|
+
const local = new Int32Array(automaton.initials.length).fill(-1);
|
|
516
|
+
patterns.forEach((p, i) => {
|
|
517
|
+
local[p.id] = i;
|
|
518
|
+
});
|
|
519
|
+
let nodes = new Int32Array(automaton.size);
|
|
520
|
+
let from = new Int32Array(automaton.size);
|
|
521
|
+
let next = new Int32Array(automaton.size);
|
|
522
|
+
let nextFrom = new Int32Array(automaton.size);
|
|
523
|
+
let count = 0;
|
|
524
|
+
const slot = new Int32Array(automaton.size);
|
|
525
|
+
const mark = new Int32Array(automaton.size);
|
|
526
|
+
let step = 0;
|
|
527
|
+
let fed = 0;
|
|
528
|
+
const reached = new Uint8Array(patterns.length);
|
|
529
|
+
/** Whether the regex must look again: a match of it was unsettled, or its automaton sat on a final state. */
|
|
530
|
+
const recheck = new Uint8Array(patterns.length);
|
|
531
|
+
const resume = new Int32Array(patterns.length);
|
|
532
|
+
const liveFrom = new Int32Array(patterns.length);
|
|
533
|
+
const atFinal = new Uint8Array(patterns.length);
|
|
534
|
+
const finds = Uint8Array.from(patterns, (p) => (p.find ? 1 : 0));
|
|
535
|
+
/**
|
|
536
|
+
* Per finder: where its automaton first started a thread since the finder
|
|
537
|
+
* last read, or -1. A construct starts where a thread does, so a stretch
|
|
538
|
+
* with none needs no read; skipping it keeps a reply with no constructs from
|
|
539
|
+
* being reread whole at every step.
|
|
540
|
+
*/
|
|
541
|
+
const openedFrom = new Int32Array(patterns.length).fill(-1);
|
|
542
|
+
const lastOpened = new Int32Array(patterns.length).fill(-1);
|
|
543
|
+
const leadOf = Int32Array.from(patterns, (p) => automaton.leads[p.id] ?? -1);
|
|
544
|
+
const leads = [...new Set(leadOf)].filter((lead) => lead >= 0);
|
|
545
|
+
const led = patterns.flatMap((_, p) => (leadOf[p] < 0 ? [] : [p]));
|
|
546
|
+
/** Per lead charset: where the run of it the text ends in started, or -1. */
|
|
547
|
+
const runFrom = new Int32Array(automaton.has.length).fill(-1);
|
|
548
|
+
function feed() {
|
|
549
|
+
const { text, fresh } = view;
|
|
550
|
+
const base = freshFrom(view);
|
|
551
|
+
const { classOf, has, edges, final, pattern } = automaton;
|
|
552
|
+
for (; fed < text.length; fed++) {
|
|
553
|
+
const code = fed >= base ? fresh.charCodeAt(fed - base) : text.charCodeAt(fed);
|
|
554
|
+
const cls = classOf[code];
|
|
555
|
+
step++;
|
|
556
|
+
let size = 0;
|
|
557
|
+
const add = (target, start) => {
|
|
558
|
+
if (mark[target] === step) {
|
|
559
|
+
const at = slot[target];
|
|
560
|
+
if (start < nextFrom[at])
|
|
561
|
+
nextFrom[at] = start;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
mark[target] = step;
|
|
565
|
+
slot[target] = size;
|
|
566
|
+
next[size] = target;
|
|
567
|
+
nextFrom[size] = start;
|
|
568
|
+
size++;
|
|
569
|
+
if (final[target])
|
|
570
|
+
reached[local[pattern[target]]] = 1;
|
|
571
|
+
};
|
|
572
|
+
for (let i = 0; i < count; i++) {
|
|
573
|
+
const e = edges[nodes[i]];
|
|
574
|
+
const start = from[i];
|
|
575
|
+
for (let k = 0; k < e.length; k += 2) {
|
|
576
|
+
if (has[e[k + 1]][cls])
|
|
577
|
+
add(e[k], start);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
const first = starts[cls];
|
|
581
|
+
for (let k = 0; k < first.length; k++) {
|
|
582
|
+
const target = first[k];
|
|
583
|
+
const p = local[pattern[target]];
|
|
584
|
+
const lead = leadOf[p];
|
|
585
|
+
const run = lead < 0 ? -1 : runFrom[lead];
|
|
586
|
+
const start = run < 0 ? fed : run;
|
|
587
|
+
add(target, start);
|
|
588
|
+
if (finds[p]) {
|
|
589
|
+
lastOpened[p] = fed;
|
|
590
|
+
if (openedFrom[p] < 0)
|
|
591
|
+
openedFrom[p] = start;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
for (const lead of leads) {
|
|
595
|
+
if (!has[lead][cls])
|
|
596
|
+
runFrom[lead] = -1;
|
|
597
|
+
else if (runFrom[lead] < 0)
|
|
598
|
+
runFrom[lead] = fed;
|
|
599
|
+
}
|
|
600
|
+
[nodes, next] = [next, nodes];
|
|
601
|
+
[from, nextFrom] = [nextFrom, from];
|
|
602
|
+
count = size;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/** Per pattern: the earliest start still under way, and whether it sits on a final state. */
|
|
606
|
+
function live() {
|
|
607
|
+
const end = view.text.length;
|
|
608
|
+
liveFrom.fill(end);
|
|
609
|
+
atFinal.fill(0);
|
|
610
|
+
let earliest = end;
|
|
611
|
+
for (let i = 0; i < count; i++) {
|
|
612
|
+
const node = nodes[i];
|
|
613
|
+
const p = local[automaton.pattern[node]];
|
|
614
|
+
const start = from[i];
|
|
615
|
+
if (start < liveFrom[p])
|
|
616
|
+
liveFrom[p] = start;
|
|
617
|
+
if (automaton.final[node])
|
|
618
|
+
atFinal[p] = 1;
|
|
619
|
+
if (start < earliest)
|
|
620
|
+
earliest = start;
|
|
621
|
+
}
|
|
622
|
+
for (const p of led) {
|
|
623
|
+
const run = runFrom[leadOf[p]];
|
|
624
|
+
if (run < 0)
|
|
625
|
+
continue;
|
|
626
|
+
if (run < liveFrom[p])
|
|
627
|
+
liveFrom[p] = run;
|
|
628
|
+
if (run < earliest)
|
|
629
|
+
earliest = run;
|
|
630
|
+
}
|
|
631
|
+
return earliest;
|
|
632
|
+
}
|
|
633
|
+
/** A finder's hit up to `settledTo`, run only on a stretch its automaton opened a thread in. */
|
|
634
|
+
function found(p, settledTo) {
|
|
635
|
+
const opened = openedFrom[p];
|
|
636
|
+
const from = resume[p];
|
|
637
|
+
if (opened < 0 || opened >= settledTo) {
|
|
638
|
+
resume[p] = settledTo;
|
|
639
|
+
return undefined;
|
|
640
|
+
}
|
|
641
|
+
const { find, rule, severity, detector } = patterns[p];
|
|
642
|
+
const at = find?.(view.text, from, settledTo);
|
|
643
|
+
if (at === undefined) {
|
|
644
|
+
resume[p] = settledTo;
|
|
645
|
+
openedFrom[p] = lastOpened[p] >= settledTo ? settledTo : -1;
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
// why: A detector's match need not end the reply, so the next call finds the one after it.
|
|
649
|
+
resume[p] = Math.max(from, at) + 1;
|
|
650
|
+
return { rule, severity, start: view.rawAt(at), detector };
|
|
651
|
+
}
|
|
652
|
+
/** A regex pattern's hit up to `settledTo`. */
|
|
653
|
+
function matched(p, settledTo) {
|
|
654
|
+
if (!reached[p] && !recheck[p]) {
|
|
655
|
+
resume[p] = settledTo;
|
|
656
|
+
return undefined;
|
|
657
|
+
}
|
|
658
|
+
reached[p] = 0;
|
|
659
|
+
const text = view.text;
|
|
660
|
+
const { regex, hit, rule, severity, detector } = patterns[p];
|
|
661
|
+
regex.lastIndex = resume[p];
|
|
662
|
+
for (;;) {
|
|
663
|
+
const match = regex.exec(text);
|
|
664
|
+
if (!match || match.index >= settledTo) {
|
|
665
|
+
resume[p] = settledTo;
|
|
666
|
+
recheck[p] = match || atFinal[p] ? 1 : 0;
|
|
667
|
+
return undefined;
|
|
668
|
+
}
|
|
669
|
+
const [blob] = match;
|
|
670
|
+
if (blob && (!hit || hit(blob, text, match.index))) {
|
|
671
|
+
// why: The scan reads on from the end of the match, so the next call finds the one after it.
|
|
672
|
+
resume[p] = regex.lastIndex;
|
|
673
|
+
recheck[p] = 1;
|
|
674
|
+
return { rule, severity, start: view.rawAt(match.index), detector };
|
|
675
|
+
}
|
|
676
|
+
if (!blob)
|
|
677
|
+
regex.lastIndex++;
|
|
678
|
+
resume[p] = regex.lastIndex;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function detect() {
|
|
682
|
+
for (let p = 0; p < patterns.length; p++) {
|
|
683
|
+
const settledTo = liveFrom[p];
|
|
684
|
+
if (settledTo <= resume[p])
|
|
685
|
+
continue;
|
|
686
|
+
const hit = patterns[p].find ? found(p, settledTo) : matched(p, settledTo);
|
|
687
|
+
if (hit)
|
|
688
|
+
return hit;
|
|
689
|
+
}
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
/** Read what the view settled into `hits`; the reply index to hold from. */
|
|
694
|
+
run(hits) {
|
|
695
|
+
feed();
|
|
696
|
+
const earliest = live();
|
|
697
|
+
for (let hit = detect(); hit; hit = detect())
|
|
698
|
+
hits.push(hit);
|
|
699
|
+
return view.rawAt(earliest);
|
|
700
|
+
},
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
/** Compiled host automata, kept per table so each is built once. */
|
|
704
|
+
const hostAutomata = new WeakMap();
|
|
705
|
+
function hostAutomaton(data) {
|
|
706
|
+
let automaton = hostAutomata.get(data);
|
|
707
|
+
if (!automaton) {
|
|
708
|
+
automaton = compile(data);
|
|
709
|
+
hostAutomata.set(data, automaton);
|
|
710
|
+
}
|
|
711
|
+
return automaton;
|
|
712
|
+
}
|
|
713
|
+
/** Scans the detectors' patterns, Theorem's and the host's own, as a reply streams. */
|
|
714
|
+
function createEgressStream(options = {}) {
|
|
715
|
+
const reply = { text: '', fresh: '' };
|
|
716
|
+
const raw = rawView(reply);
|
|
717
|
+
const views = [raw];
|
|
718
|
+
const scan = (view, automaton, patterns) => {
|
|
719
|
+
if (!views.includes(view))
|
|
720
|
+
views.push(view);
|
|
721
|
+
return createScan(view, automaton, patterns);
|
|
722
|
+
};
|
|
723
|
+
const scans = [];
|
|
724
|
+
const detectors = options.detect ?? [];
|
|
725
|
+
const forwardScan = forwardPatterns(detectors, options);
|
|
726
|
+
if (forwardScan.length > 0) {
|
|
727
|
+
forward ??= compile(loaded().forward);
|
|
728
|
+
scans.push(scan(raw, forward, forwardScan));
|
|
729
|
+
}
|
|
730
|
+
if (detectors.includes('injection')) {
|
|
731
|
+
forward ??= compile(loaded().forward);
|
|
732
|
+
backward ??= compile(loaded().reversed);
|
|
733
|
+
const normalized = normalizedReplyView(reply);
|
|
734
|
+
const injection = injectionPatterns();
|
|
735
|
+
scans.push(scan(raw, backward, reversedPatterns()), scan(typoView(raw), forward, injection), scan(normalized, forward, injection), scan(typoView(normalized), forward, injection), scan(unitView(raw, tryRot13), forward, injection), scan(unitView(raw, (text) => tryLeet(text) ?? text), forward, injection), scan(urlView(reply), forward, injection));
|
|
736
|
+
}
|
|
737
|
+
if (detectors.includes('marker_leak') && options.note) {
|
|
738
|
+
const regex = notePattern(options.note);
|
|
739
|
+
scans.push(scan(raw, literalAutomaton(options.note), [{ ...MARKER_RULE, id: 0, regex }]));
|
|
740
|
+
}
|
|
741
|
+
if (detectors.includes('tool_leak') && options.tools) {
|
|
742
|
+
const names = namePatterns(options.tools);
|
|
743
|
+
const patterns = names.map(({ regex }, id) => ({ ...TOOL_RULE, id, regex }));
|
|
744
|
+
if (names.length > 0) {
|
|
745
|
+
scans.push(scan(raw, literalsAutomaton(names.map(({ literal }) => literal)), patterns));
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
for (const { detector, compiled, matchers } of options.own ?? []) {
|
|
749
|
+
const patterns = matchers.map(({ regex }, id) => ({
|
|
750
|
+
...detectorRule(detector),
|
|
751
|
+
id,
|
|
752
|
+
regex: new RegExp(regex.source, regex.flags),
|
|
753
|
+
}));
|
|
754
|
+
scans.push(scan(raw, hostAutomaton(compiled.automaton), patterns));
|
|
755
|
+
}
|
|
756
|
+
let hold = 0;
|
|
757
|
+
return {
|
|
758
|
+
push(chunk) {
|
|
759
|
+
reply.text += chunk;
|
|
760
|
+
reply.fresh = chunk;
|
|
761
|
+
for (const view of views)
|
|
762
|
+
view.update();
|
|
763
|
+
const hits = [];
|
|
764
|
+
let from = reply.text.length;
|
|
765
|
+
for (const s of scans) {
|
|
766
|
+
from = Math.min(from, s.run(hits));
|
|
767
|
+
}
|
|
768
|
+
hold = from;
|
|
769
|
+
return hits;
|
|
770
|
+
},
|
|
771
|
+
holdFrom: () => hold,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
export { createEgressStream, loadEgressAutomata, normalizedReplyView, typoView, urlView };
|