theorum 0.1.15 → 1.1.3
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 +241 -98
- package/esm/mod.d.ts +57 -28
- package/esm/mod.js +43 -23
- package/esm/src/cli/commands/bench.js +18 -18
- package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
- package/esm/src/cli/commands/fuzz-canary.js +191 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
- package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
- package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
- package/esm/src/cli/commands/guardrails-eval.js +15 -0
- package/esm/src/cli/commands/profile.js +35 -15
- package/esm/src/cli/commands/run.d.ts +3 -0
- package/esm/src/cli/commands/run.js +23 -32
- package/esm/src/cli/commands/test.d.ts +10 -1
- package/esm/src/cli/commands/test.js +34 -34
- package/esm/src/cli/event-log.d.ts +19 -0
- package/esm/src/cli/event-log.js +147 -0
- package/esm/src/cli/index.js +57 -11
- package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
- package/esm/src/cli/matrix/synthesizer.js +45 -118
- package/esm/src/guardrails/canary-gate.d.ts +21 -0
- package/esm/src/guardrails/canary-gate.js +32 -0
- package/esm/src/guardrails/canary.d.ts +34 -0
- package/esm/src/guardrails/canary.js +150 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +213 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
- package/esm/src/guardrails/corpus/live-attacks.js +231 -0
- package/esm/src/guardrails/corpus/mod.d.ts +14 -0
- package/esm/src/guardrails/corpus/mod.js +11 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
- package/esm/src/guardrails/corpus/secrets.js +17 -0
- package/esm/src/guardrails/corpus/strings.d.ts +28 -0
- package/esm/src/guardrails/corpus/strings.js +34 -0
- package/esm/src/guardrails/corpus/types.d.ts +38 -0
- package/esm/src/guardrails/corpus/types.js +6 -0
- package/esm/src/guardrails/egress.d.ts +32 -0
- package/esm/src/guardrails/egress.js +87 -0
- package/esm/src/guardrails/error.d.ts +14 -23
- package/esm/src/guardrails/error.js +87 -76
- package/esm/src/guardrails/eval/corpus.d.ts +108 -0
- package/esm/src/guardrails/eval/corpus.js +978 -0
- package/esm/src/guardrails/eval/mod.d.ts +51 -0
- package/esm/src/guardrails/eval/mod.js +133 -0
- package/esm/src/guardrails/eval/score.d.ts +66 -0
- package/esm/src/guardrails/eval/score.js +114 -0
- package/esm/src/guardrails/events.d.ts +25 -0
- package/esm/src/guardrails/events.js +56 -0
- package/esm/src/guardrails/hits.d.ts +24 -0
- package/esm/src/guardrails/hits.js +45 -0
- package/esm/src/guardrails/injection.js +28 -5
- package/esm/src/guardrails/lexicon.d.ts +39 -0
- package/esm/src/guardrails/lexicon.js +200 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
- package/esm/src/guardrails/live-outbound-gate.js +222 -0
- package/esm/src/guardrails/mod.d.ts +30 -6
- package/esm/src/guardrails/mod.js +20 -5
- package/esm/src/guardrails/network.d.ts +19 -0
- package/esm/src/guardrails/network.js +234 -0
- package/esm/src/guardrails/policy.d.ts +35 -0
- package/esm/src/guardrails/policy.js +50 -0
- package/esm/src/guardrails/progressive-yield.d.ts +51 -0
- package/esm/src/guardrails/progressive-yield.js +98 -0
- package/esm/src/guardrails/quota.d.ts +17 -3
- package/esm/src/guardrails/quota.js +18 -4
- package/esm/src/guardrails/sanitize.d.ts +45 -19
- package/esm/src/guardrails/sanitize.js +177 -94
- package/esm/src/guardrails/sensitive.js +2 -1
- package/esm/src/guardrails/serialize.d.ts +35 -0
- package/esm/src/guardrails/serialize.js +58 -0
- package/esm/src/guardrails/testing.d.ts +17 -0
- package/esm/src/guardrails/testing.js +13 -0
- package/esm/src/guardrails/theorum-error.d.ts +12 -0
- package/esm/src/guardrails/theorum-error.js +15 -0
- package/esm/src/guardrails/tool-directives.d.ts +48 -0
- package/esm/src/guardrails/tool-directives.js +124 -0
- package/esm/src/guardrails/tool-result.d.ts +93 -0
- package/esm/src/guardrails/tool-result.js +276 -0
- package/esm/src/guardrails/types.d.ts +291 -0
- package/esm/src/guardrails/types.js +72 -0
- package/esm/src/host/client-turn.d.ts +19 -0
- package/esm/src/host/client-turn.js +36 -0
- package/esm/src/host/mint-trace.d.ts +1 -1
- package/esm/src/host/mod.d.ts +5 -3
- package/esm/src/host/mod.js +4 -3
- package/esm/src/kernel/auth/crypto.d.ts +42 -0
- package/esm/src/kernel/auth/crypto.js +106 -0
- package/esm/src/kernel/auth/mod.d.ts +11 -0
- package/esm/src/kernel/auth/mod.js +11 -0
- package/esm/src/kernel/auth/oauth.d.ts +47 -0
- package/esm/src/kernel/auth/oauth.js +278 -0
- package/esm/src/kernel/auth/types.d.ts +133 -0
- package/esm/src/kernel/auth/types.js +13 -0
- package/esm/src/kernel/engine/delta.d.ts +24 -2
- package/esm/src/kernel/engine/delta.js +478 -39
- package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
- package/esm/src/kernel/engine/live-inbound.js +31 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
- package/esm/src/kernel/engine/live-ingress.js +47 -0
- package/esm/src/kernel/engine/repair.js +13 -12
- package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
- package/esm/src/kernel/engine/runner/gates.js +130 -43
- package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
- package/esm/src/kernel/engine/runner/mod.js +192 -53
- package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
- package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
- package/esm/src/kernel/engine/runner/stages.js +89 -0
- package/esm/src/kernel/engine/runner/state.d.ts +31 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
- package/esm/src/kernel/engine/runner/steps.js +244 -43
- package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
- package/esm/src/kernel/engine/runner/stream.js +140 -44
- package/esm/src/kernel/engine/session/mod.d.ts +25 -0
- package/esm/src/kernel/engine/session/mod.js +557 -0
- package/esm/src/kernel/interaction-parts.d.ts +14 -0
- package/esm/src/kernel/interaction-parts.js +23 -0
- package/esm/src/kernel/mod.d.ts +21 -10
- package/esm/src/kernel/mod.js +11 -8
- package/esm/src/kernel/profile-graph.d.ts +159 -0
- package/esm/src/kernel/profile-graph.js +156 -0
- package/esm/src/kernel/registry/attachments.d.ts +12 -10
- package/esm/src/kernel/registry/attachments.js +33 -27
- package/esm/src/kernel/registry/catalog.d.ts +25 -24
- package/esm/src/kernel/registry/catalog.js +60 -101
- package/esm/src/kernel/registry/ingress.d.ts +9 -4
- package/esm/src/kernel/registry/ingress.js +97 -75
- package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
- package/esm/src/kernel/registry/profile-outputs.js +8 -0
- package/esm/src/kernel/registry/profiles.d.ts +55 -12
- package/esm/src/kernel/registry/profiles.js +413 -73
- package/esm/src/kernel/registry/provider-request.js +13 -7
- package/esm/src/kernel/registry/resolve.d.ts +8 -8
- package/esm/src/kernel/registry/resolve.js +169 -154
- package/esm/src/kernel/registry/schemas.js +1 -1
- package/esm/src/kernel/registry/sole-model.d.ts +8 -0
- package/esm/src/kernel/registry/sole-model.js +10 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
- package/esm/src/kernel/registry/system-prompt.js +40 -0
- package/esm/src/kernel/registry/system-role.d.ts +8 -0
- package/esm/src/kernel/registry/system-role.js +14 -0
- package/esm/src/kernel/registry/vault.d.ts +12 -7
- package/esm/src/kernel/registry/vault.js +32 -10
- package/esm/src/kernel/schema.d.ts +231 -0
- package/esm/src/kernel/schema.js +607 -0
- package/esm/src/kernel/stages.d.ts +175 -0
- package/esm/src/kernel/stages.js +476 -0
- package/esm/src/kernel/stop.d.ts +78 -19
- package/esm/src/kernel/stop.js +51 -16
- package/esm/src/kernel/tools/events.d.ts +41 -0
- package/esm/src/kernel/tools/events.js +71 -0
- package/esm/src/kernel/tools/execute.d.ts +84 -0
- package/esm/src/kernel/tools/execute.js +614 -0
- package/esm/src/kernel/tools/harness.d.ts +8 -0
- package/esm/src/kernel/tools/harness.js +46 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +101 -0
- package/esm/src/kernel/tools/mod.d.ts +13 -0
- package/esm/src/kernel/tools/mod.js +11 -0
- package/esm/src/kernel/tools/permission.d.ts +15 -0
- package/esm/src/kernel/tools/permission.js +47 -0
- package/esm/src/kernel/tools/project.d.ts +12 -0
- package/esm/src/kernel/tools/project.js +36 -0
- package/esm/src/kernel/tools/registry.d.ts +23 -0
- package/esm/src/kernel/tools/registry.js +81 -0
- package/esm/src/kernel/tools/remote.d.ts +94 -0
- package/esm/src/kernel/tools/remote.js +577 -0
- package/esm/src/kernel/tools/resolve.d.ts +39 -0
- package/esm/src/kernel/tools/resolve.js +283 -0
- package/esm/src/kernel/tools/schema.d.ts +15 -0
- package/esm/src/kernel/tools/schema.js +176 -0
- package/esm/src/kernel/tools/stage-run.d.ts +105 -0
- package/esm/src/kernel/tools/stage-run.js +155 -0
- package/esm/src/kernel/tools/types.d.ts +394 -0
- package/esm/src/kernel/tools/types.js +9 -0
- package/esm/src/kernel/types.d.ts +540 -256
- package/esm/src/kernel/util/find-last.d.ts +2 -0
- package/esm/src/kernel/util/find-last.js +10 -0
- package/esm/src/observability/destinations.d.ts +31 -0
- package/esm/src/observability/destinations.js +67 -0
- package/esm/src/observability/mod.d.ts +10 -3
- package/esm/src/observability/mod.js +6 -2
- package/esm/src/observability/policy.d.ts +27 -0
- package/esm/src/observability/policy.js +80 -0
- package/esm/src/observability/resolve-policy.d.ts +16 -0
- package/esm/src/observability/resolve-policy.js +64 -0
- package/esm/src/observability/trace-attach.d.ts +8 -4
- package/esm/src/observability/trace-attach.js +50 -29
- package/esm/src/observability/trace-record.d.ts +23 -13
- package/esm/src/observability/trace-record.js +96 -39
- package/esm/src/observability/trace-sink.d.ts +19 -0
- package/esm/src/observability/trace-sink.js +10 -0
- package/esm/src/observability/trace-usage.d.ts +10 -3
- package/esm/src/observability/trace-usage.js +70 -17
- package/esm/src/observability/trace.d.ts +18 -7
- package/esm/src/observability/trace.js +34 -17
- package/esm/src/observability/types.d.ts +113 -0
- package/esm/src/observability/types.js +11 -0
- package/esm/src/presets/google/speech-voices.d.ts +11 -0
- package/esm/src/presets/google/speech-voices.js +41 -0
- package/esm/src/presets/google.d.ts +36 -24
- package/esm/src/presets/google.js +50 -63
- package/esm/src/presets/mod.d.ts +2 -2
- package/esm/src/presets/mod.js +1 -1
- package/esm/src/providers/create-provider.d.ts +20 -17
- package/esm/src/providers/create-provider.js +72 -26
- package/esm/src/providers/google/interactions/framing.d.ts +23 -0
- package/esm/src/providers/google/interactions/framing.js +269 -0
- package/esm/src/providers/google/interactions/mod.d.ts +7 -0
- package/esm/src/providers/google/interactions/mod.js +7 -0
- package/esm/src/providers/google/interactions/stream.d.ts +83 -0
- package/esm/src/providers/google/interactions/stream.js +588 -0
- package/esm/src/providers/google/keys.d.ts +26 -0
- package/esm/src/providers/{keys.js → google/keys.js} +19 -31
- package/esm/src/providers/google/live/framing.d.ts +49 -0
- package/esm/src/providers/google/live/framing.js +552 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
- package/esm/src/providers/google/live/openapi-schema.js +46 -0
- package/esm/src/providers/google/live/session.d.ts +25 -0
- package/esm/src/providers/google/live/session.js +134 -0
- package/esm/src/providers/google/live/stream.d.ts +45 -0
- package/esm/src/providers/google/live/stream.js +214 -0
- package/esm/src/providers/google/urls.d.ts +6 -0
- package/esm/src/providers/google/urls.js +6 -0
- package/esm/src/providers/local/local.d.ts +30 -0
- package/esm/src/providers/{local.js → local/local.js} +66 -126
- package/esm/src/providers/local/mod.d.ts +9 -0
- package/esm/src/providers/local/mod.js +9 -0
- package/esm/src/providers/mod.d.ts +6 -3
- package/esm/src/providers/mod.js +3 -1
- package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
- package/esm/src/providers/openrouter/cache-control.js +23 -0
- package/esm/src/providers/openrouter/chat.d.ts +107 -0
- package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
- package/esm/src/providers/openrouter/image.d.ts +34 -0
- package/esm/src/providers/openrouter/image.js +275 -0
- package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
- package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
- package/esm/src/providers/openrouter/openai/compat.js +213 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -0
- package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
- package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
- package/esm/src/providers/openrouter/speech.d.ts +23 -0
- package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
- package/esm/src/providers/probe.d.ts +1 -0
- package/esm/src/providers/probe.js +22 -0
- package/esm/src/providers/shared/pcm.d.ts +12 -0
- package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
- package/esm/src/providers/shared/sse.d.ts +18 -0
- package/esm/src/providers/shared/sse.js +87 -0
- package/esm/src/providers/shared/tool-args.d.ts +17 -0
- package/esm/src/providers/shared/tool-args.js +45 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
- package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
- package/esm/src/providers/types.d.ts +27 -0
- package/esm/src/providers/types.js +1 -0
- package/package.json +11 -7
- package/docs/cli.md +0 -97
- package/docs/guardrails.md +0 -178
- package/docs/host.md +0 -97
- package/docs/kernel.md +0 -404
- package/docs/observability.md +0 -105
- package/docs/openrouter.md +0 -125
- package/docs/presets-google.md +0 -91
- package/docs/presets.md +0 -88
- package/docs/providers.md +0 -202
- package/docs/streaming.md +0 -96
- package/esm/src/kernel/engine/boundary.d.ts +0 -10
- package/esm/src/kernel/engine/boundary.js +0 -55
- package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
- package/esm/src/kernel/engine/runner/tools.js +0 -198
- package/esm/src/kernel/registry/tools.d.ts +0 -12
- package/esm/src/kernel/registry/tools.js +0 -36
- package/esm/src/providers/expose-for-tests.d.ts +0 -1
- package/esm/src/providers/expose-for-tests.js +0 -25
- package/esm/src/providers/gemini-tape.d.ts +0 -2
- package/esm/src/providers/google-tap.d.ts +0 -3
- package/esm/src/providers/interactions.d.ts +0 -5
- package/esm/src/providers/interactions.js +0 -169
- package/esm/src/providers/keys.d.ts +0 -19
- package/esm/src/providers/local.d.ts +0 -29
- package/esm/src/providers/openrouter-mod.d.ts +0 -13
- package/esm/src/providers/openrouter-mod.js +0 -12
- package/esm/src/providers/openrouter-payload.d.ts +0 -39
- package/esm/src/providers/openrouter-payload.js +0 -195
- package/esm/src/providers/openrouter.d.ts +0 -15
- package/esm/src/providers/pcm.d.ts +0 -7
- package/esm/src/providers/provider.d.ts +0 -15
- package/esm/src/providers/provider.js +0 -202
- package/esm/src/providers/speech.d.ts +0 -23
- package/esm/src/providers/sse.d.ts +0 -7
- package/esm/src/providers/sse.js +0 -55
- package/esm/src/streaming/mod.d.ts +0 -9
- package/esm/src/streaming/mod.js +0 -8
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
|
@@ -1,87 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stress / matrix turn synthesis for the THEORUM CLI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Custom tools come from `profile.tools.allow` (visibility via loadTier).
|
|
5
|
+
* Provider builtins come from `models.*.builtInTools`. `--search` /
|
|
6
|
+
* `--map` only verify those ids are listed on the selected model.
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import { getTool } from '../../kernel/registry/catalog.js';
|
|
11
10
|
import { FIXTURE_PNG_BASE64, FIXTURE_WAV_BASE64, getFixtureForMime } from './fixtures.js';
|
|
11
|
+
function defaultModelId(profile) {
|
|
12
|
+
const ids = Object.keys(profile.models);
|
|
13
|
+
return profile.defaultModel ?? ids[0] ?? '';
|
|
14
|
+
}
|
|
12
15
|
export function synthesizeLiteCombo(profile) {
|
|
13
|
-
const
|
|
16
|
+
const model = profile.allowModelSelect && profile.models.fast ? 'fast' : undefined;
|
|
14
17
|
return {
|
|
15
18
|
profile: profile.id,
|
|
16
|
-
|
|
17
|
-
tools: {},
|
|
19
|
+
model,
|
|
18
20
|
input: {
|
|
19
21
|
text: `Ping test for profile ${profile.id}. Respond concisely with confirmation.`,
|
|
20
22
|
},
|
|
21
23
|
};
|
|
22
24
|
}
|
|
23
|
-
function
|
|
24
|
-
if (profile.
|
|
25
|
-
return
|
|
25
|
+
function resolveStressModel(profile) {
|
|
26
|
+
if (!profile.allowModelSelect) {
|
|
27
|
+
return undefined;
|
|
26
28
|
}
|
|
27
|
-
if (profile.
|
|
28
|
-
|
|
29
|
-
return keys[keys.length - 1];
|
|
30
|
-
}
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
function isEnabled(active, id) {
|
|
34
|
-
return active[id] === true;
|
|
35
|
-
}
|
|
36
|
-
/** Drop tools whose registered `conflictsWith` siblings are also enabled. */
|
|
37
|
-
function applyRegisteredConflicts(allowed, active) {
|
|
38
|
-
for (const id of allowed) {
|
|
39
|
-
if (!isEnabled(active, id)) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const conflicts = getTool(id)?.conflictsWith ?? [];
|
|
43
|
-
if (conflicts.some((other) => isEnabled(active, other))) {
|
|
44
|
-
active[id] = false;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Enable allowlisted tools, optionally forcing one preferred tool and clearing
|
|
50
|
-
* what it conflicts with (and tools that conflict with it).
|
|
51
|
-
*/
|
|
52
|
-
function resolveStressTools(allowed, prefer) {
|
|
53
|
-
const active = {};
|
|
54
|
-
for (const t of allowed) {
|
|
55
|
-
active[t] = true;
|
|
56
|
-
}
|
|
57
|
-
if (prefer && allowed.includes(prefer)) {
|
|
58
|
-
active[prefer] = true;
|
|
59
|
-
for (const c of getTool(prefer)?.conflictsWith ?? []) {
|
|
60
|
-
if (allowed.includes(c)) {
|
|
61
|
-
active[c] = false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
for (const t of allowed) {
|
|
65
|
-
if (t === prefer) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const otherConflicts = getTool(t)?.conflictsWith ?? [];
|
|
69
|
-
if (otherConflicts.includes(prefer)) {
|
|
70
|
-
active[t] = false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
29
|
+
if (profile.models.smart) {
|
|
30
|
+
return 'smart';
|
|
73
31
|
}
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
/** Builtins on the allowlist that declare conflicts (candidates for matrix variants). */
|
|
78
|
-
function conflictingAllowlistedTools(allowed) {
|
|
79
|
-
return allowed.filter((id) => {
|
|
80
|
-
const entry = getTool(id);
|
|
81
|
-
return entry?.kind === 'builtin' && (entry.conflictsWith?.length ?? 0) > 0;
|
|
82
|
-
});
|
|
32
|
+
const ids = Object.keys(profile.models);
|
|
33
|
+
return ids.length > 1 ? ids[ids.length - 1] : undefined;
|
|
83
34
|
}
|
|
84
35
|
function resolveStressAttachments(profile) {
|
|
36
|
+
if (profile.type === 'speech' || profile.type === 'live' || !profile.inputs) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
85
39
|
const attachments = [];
|
|
86
40
|
const accept = profile.inputs.attachments?.accept;
|
|
87
41
|
if (accept && accept.length > 0) {
|
|
@@ -98,22 +52,23 @@ function resolveStressAttachments(profile) {
|
|
|
98
52
|
return attachments;
|
|
99
53
|
}
|
|
100
54
|
function resolveStressVoice(profile) {
|
|
55
|
+
if (profile.type === 'speech' || profile.type === 'live' || !profile.inputs) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
101
58
|
const voice = [];
|
|
102
59
|
if (profile.inputs.voice?.accept && profile.inputs.voice.accept.length > 0) {
|
|
103
60
|
voice.push({ mimeType: 'audio/wav', data: FIXTURE_WAV_BASE64 });
|
|
104
61
|
}
|
|
105
62
|
return voice;
|
|
106
63
|
}
|
|
107
|
-
export function synthesizeStressCombo(profile
|
|
108
|
-
const
|
|
109
|
-
const activeTools = resolveStressTools(profile.tools.allow ?? [], options.preferTool);
|
|
64
|
+
export function synthesizeStressCombo(profile) {
|
|
65
|
+
const model = resolveStressModel(profile);
|
|
110
66
|
const attachments = resolveStressAttachments(profile);
|
|
111
67
|
const voice = resolveStressVoice(profile);
|
|
112
68
|
const promptText = `Execute comprehensive test turn for profile ${profile.id}. Validate all instructions and produce required outputs.`;
|
|
113
69
|
return {
|
|
114
70
|
profile: profile.id,
|
|
115
|
-
|
|
116
|
-
tools: activeTools,
|
|
71
|
+
model,
|
|
117
72
|
input: {
|
|
118
73
|
text: promptText,
|
|
119
74
|
attachments: attachments.length > 0 ? attachments : undefined,
|
|
@@ -122,55 +77,27 @@ export function synthesizeStressCombo(profile, options = {}) {
|
|
|
122
77
|
};
|
|
123
78
|
}
|
|
124
79
|
export function synthesizeMatrixCombos(profile) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
// Variants: each allowlisted conflicting builtin that the primary pass dropped.
|
|
137
|
-
for (const id of conflictingAllowlistedTools(allowed)) {
|
|
138
|
-
if (primary.tools?.[id] === true) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const variant = synthesizeStressCombo(profile, { preferTool: id });
|
|
142
|
-
if (variant.tools?.[id] !== true) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
combos.push({
|
|
146
|
-
name: `Conflict variant (${id} preferred)`,
|
|
147
|
-
req: variant,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return combos;
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
name: 'Lite (connectivity)',
|
|
83
|
+
req: synthesizeLiteCombo(profile),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'Stress (all modalities + primary tools)',
|
|
87
|
+
req: synthesizeStressCombo(profile),
|
|
88
|
+
},
|
|
89
|
+
];
|
|
151
90
|
}
|
|
152
|
-
/**
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
91
|
+
/** Ensure CLI grounding flags match model builtInTools. */
|
|
92
|
+
function assertGroundingFlagsOnModel(profile, options) {
|
|
93
|
+
const modelId = options.mode && profile.models[options.mode] ? options.mode : defaultModelId(profile);
|
|
94
|
+
const builtins = new Set(profile.models[modelId]?.builtInTools ?? []);
|
|
95
|
+
if (options.search === true && !builtins.has('googleSearch')) {
|
|
96
|
+
throw new Error(`--search requires googleSearch on models.${modelId}.builtInTools`);
|
|
156
97
|
}
|
|
157
|
-
if (options.map
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function applyExplicitToolOverrides(base, profile, options) {
|
|
162
|
-
const allowed = profile.tools.allow ?? [];
|
|
163
|
-
base.tools = base.tools ?? {};
|
|
164
|
-
applyGoogleFlagAliases(base.tools, allowed, options);
|
|
165
|
-
// If map was explicitly requested on, prefer it against its registered conflicts.
|
|
166
|
-
if (options.map === true && allowed.includes('googleMaps')) {
|
|
167
|
-
const preferred = resolveStressTools(allowed, 'googleMaps');
|
|
168
|
-
for (const id of allowed) {
|
|
169
|
-
base.tools[id] = preferred[id] === true;
|
|
170
|
-
}
|
|
171
|
-
return;
|
|
98
|
+
if (options.map === true && !builtins.has('googleMaps')) {
|
|
99
|
+
throw new Error(`--map requires googleMaps on models.${modelId}.builtInTools`);
|
|
172
100
|
}
|
|
173
|
-
applyRegisteredConflicts(allowed, base.tools);
|
|
174
101
|
}
|
|
175
102
|
export function buildCustomTurnRequest(profile, options) {
|
|
176
103
|
if (options.lite) {
|
|
@@ -178,10 +105,10 @@ export function buildCustomTurnRequest(profile, options) {
|
|
|
178
105
|
}
|
|
179
106
|
const base = synthesizeStressCombo(profile);
|
|
180
107
|
if (options.mode) {
|
|
181
|
-
base.
|
|
108
|
+
base.model = options.mode;
|
|
182
109
|
}
|
|
183
110
|
if (options.search !== undefined || options.map !== undefined) {
|
|
184
|
-
|
|
111
|
+
assertGroundingFlagsOnModel(profile, options);
|
|
185
112
|
}
|
|
186
113
|
return base;
|
|
187
114
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canary-only batch helper for hosts that need stream lookback without full
|
|
3
|
+
* egress. Live production path: `live-outbound-gate.ts` (progressive yield).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import type { TurnEvent } from '../kernel/types.js';
|
|
8
|
+
import { type CanaryStreamGate } from './canary.js';
|
|
9
|
+
export interface CanaryGateSession {
|
|
10
|
+
canary: string;
|
|
11
|
+
gate: CanaryStreamGate;
|
|
12
|
+
lastStreamType?: 'text' | 'thought';
|
|
13
|
+
}
|
|
14
|
+
declare function createCanaryGateSession(canary: string): CanaryGateSession;
|
|
15
|
+
declare function filterCanaryGatedEvents(session: CanaryGateSession, events: TurnEvent[]): {
|
|
16
|
+
leaked: true;
|
|
17
|
+
} | {
|
|
18
|
+
leaked: false;
|
|
19
|
+
events: TurnEvent[];
|
|
20
|
+
};
|
|
21
|
+
export { createCanaryGateSession, filterCanaryGatedEvents };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canary-only batch helper for hosts that need stream lookback without full
|
|
3
|
+
* egress. Live production path: `live-outbound-gate.ts` (progressive yield).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { createCanaryStreamGate, eventHasCanary, isStreamedCanaryEvent, } from './canary.js';
|
|
8
|
+
function createCanaryGateSession(canary) {
|
|
9
|
+
return { canary, gate: createCanaryStreamGate(canary) };
|
|
10
|
+
}
|
|
11
|
+
function filterCanaryGatedEvents(session, events) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const event of events) {
|
|
14
|
+
if (isStreamedCanaryEvent(event)) {
|
|
15
|
+
session.lastStreamType = event.type;
|
|
16
|
+
const result = session.gate.process(event.text ?? '');
|
|
17
|
+
if (result.leak) {
|
|
18
|
+
return { leaked: true };
|
|
19
|
+
}
|
|
20
|
+
if (result.emit) {
|
|
21
|
+
out.push({ ...event, text: result.emit });
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (eventHasCanary(event, session.canary)) {
|
|
26
|
+
return { leaked: true };
|
|
27
|
+
}
|
|
28
|
+
out.push(event);
|
|
29
|
+
}
|
|
30
|
+
return { leaked: false, events: out };
|
|
31
|
+
}
|
|
32
|
+
export { createCanaryGateSession, filterCanaryGatedEvents };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { TurnEvent } from '../kernel/types.js';
|
|
2
|
+
declare const USER_OPEN = "<user_data>";
|
|
3
|
+
declare const USER_CLOSE = "</user_data>";
|
|
4
|
+
declare const OMIT_CANARY = "[omitted - canary]";
|
|
5
|
+
declare function mintCanary(): string;
|
|
6
|
+
declare function wrapUserData(text: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Append the canary bind note to the host's system prompt.
|
|
9
|
+
*
|
|
10
|
+
* The note is mechanism text with an overridable registered default
|
|
11
|
+
* (`canary.bind_note` in the lexicon) or a per-profile template
|
|
12
|
+
* (`guardrails.canary.bindNote`). Either way the template must contain the
|
|
13
|
+
* `{canary}` placeholder — a note without the token binds nothing.
|
|
14
|
+
*/
|
|
15
|
+
declare function bindCanary(system: string, canary: string, bindNote?: string): string;
|
|
16
|
+
declare function scanTextForCanaryLeak(text: string, canary: string): boolean;
|
|
17
|
+
declare function eventHasCanary(event: TurnEvent, canary: string): boolean;
|
|
18
|
+
type CanaryGateResult = {
|
|
19
|
+
leak: true;
|
|
20
|
+
} | {
|
|
21
|
+
leak: false;
|
|
22
|
+
emit: string;
|
|
23
|
+
};
|
|
24
|
+
interface CanaryStreamGate {
|
|
25
|
+
process: (fragment: string) => CanaryGateResult;
|
|
26
|
+
flush: () => CanaryGateResult;
|
|
27
|
+
}
|
|
28
|
+
declare function createCanaryStreamGate(canary: string): CanaryStreamGate;
|
|
29
|
+
declare function isStreamedCanaryEvent(event: TurnEvent): event is TurnEvent & {
|
|
30
|
+
type: 'text' | 'thought';
|
|
31
|
+
};
|
|
32
|
+
declare function redactCanary(event: TurnEvent, canary: string): TurnEvent;
|
|
33
|
+
export type { CanaryGateResult, CanaryStreamGate };
|
|
34
|
+
export { bindCanary, createCanaryStreamGate, eventHasCanary, isStreamedCanaryEvent, mintCanary, OMIT_CANARY, redactCanary, scanTextForCanaryLeak, USER_CLOSE, USER_OPEN, wrapUserData, };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { mapStrings } from '../kernel/engine/tree.js';
|
|
2
|
+
import { TheorumError } from './error.js';
|
|
3
|
+
import { lexiconText } from './lexicon.js';
|
|
4
|
+
import { scanTextOf } from './serialize.js';
|
|
5
|
+
const USER_OPEN = '<user_data>';
|
|
6
|
+
const USER_CLOSE = '</user_data>';
|
|
7
|
+
const CANARY_PREFIX = 'theo-';
|
|
8
|
+
const CANARY_BYTES = 16;
|
|
9
|
+
const HEX_RADIX = 16;
|
|
10
|
+
const HEX_PAD = 2;
|
|
11
|
+
const OMIT_CANARY = '[omitted - canary]';
|
|
12
|
+
const FENCE = /<\/?user_data>/gi;
|
|
13
|
+
/** Base64 prefix hint for the literal string "theo". */
|
|
14
|
+
const B64_THEO_HINT = 'dGhlbw';
|
|
15
|
+
function mintCanary() {
|
|
16
|
+
const bytes = new Uint8Array(CANARY_BYTES);
|
|
17
|
+
crypto.getRandomValues(bytes);
|
|
18
|
+
let hex = '';
|
|
19
|
+
for (const byte of bytes) {
|
|
20
|
+
hex += byte.toString(HEX_RADIX).padStart(HEX_PAD, '0');
|
|
21
|
+
}
|
|
22
|
+
return `${CANARY_PREFIX}${hex}`;
|
|
23
|
+
}
|
|
24
|
+
function stripUserFences(text) {
|
|
25
|
+
return text.replaceAll(FENCE, '').trim();
|
|
26
|
+
}
|
|
27
|
+
function wrapUserData(text) {
|
|
28
|
+
return `${USER_OPEN}\n${stripUserFences(text)}\n${USER_CLOSE}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Append the canary bind note to the host's system prompt.
|
|
32
|
+
*
|
|
33
|
+
* The note is mechanism text with an overridable registered default
|
|
34
|
+
* (`canary.bind_note` in the lexicon) or a per-profile template
|
|
35
|
+
* (`guardrails.canary.bindNote`). Either way the template must contain the
|
|
36
|
+
* `{canary}` placeholder — a note without the token binds nothing.
|
|
37
|
+
*/
|
|
38
|
+
function bindCanary(system, canary, bindNote) {
|
|
39
|
+
if (!canary) {
|
|
40
|
+
return system;
|
|
41
|
+
}
|
|
42
|
+
if (bindNote !== undefined && !bindNote.includes('{canary}')) {
|
|
43
|
+
throw new TheorumError('guardrails.canary.bindNote must contain the {canary} placeholder');
|
|
44
|
+
}
|
|
45
|
+
const note = lexiconText('canary.bind_note', { canary }, bindNote);
|
|
46
|
+
if (!system) {
|
|
47
|
+
return note;
|
|
48
|
+
}
|
|
49
|
+
return `${system}\n\n${note}`;
|
|
50
|
+
}
|
|
51
|
+
function scanTextForCanaryLeak(text, canary) {
|
|
52
|
+
if (!text || !canary) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (text.includes(canary)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const encoded = btoa(canary);
|
|
60
|
+
if (text.includes(encoded)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* ignore invalid btoa input */
|
|
66
|
+
}
|
|
67
|
+
if (!text.includes('theo') && !text.includes(B64_THEO_HINT)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const hex = canary.startsWith(CANARY_PREFIX) ? canary.slice(CANARY_PREFIX.length) : '';
|
|
71
|
+
if (hex.length > 0) {
|
|
72
|
+
const spaced = hex.split('').join(' ');
|
|
73
|
+
if (text.includes(spaced)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
function eventHasCanary(event, canary) {
|
|
80
|
+
if (!canary) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (event.text && scanTextForCanaryLeak(event.text, canary)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (event.error && scanTextForCanaryLeak(event.error, canary)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (event.structured !== undefined &&
|
|
90
|
+
scanTextForCanaryLeak(scanTextOf(event.structured), canary)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (event.tool !== undefined && scanTextForCanaryLeak(scanTextOf(event.tool), canary)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (event.grounding !== undefined && scanTextForCanaryLeak(scanTextOf(event.grounding), canary)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (event.evidence !== undefined && scanTextForCanaryLeak(scanTextOf(event.evidence), canary)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (event.session !== undefined && scanTextForCanaryLeak(scanTextOf(event.session), canary)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (event.sessionResumptionHandle &&
|
|
106
|
+
scanTextForCanaryLeak(event.sessionResumptionHandle, canary)) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function createCanaryStreamGate(canary) {
|
|
112
|
+
const overlap = Math.max(0, canary.length - 1);
|
|
113
|
+
let pending = '';
|
|
114
|
+
function step(window) {
|
|
115
|
+
if (scanTextForCanaryLeak(window, canary)) {
|
|
116
|
+
return { leak: true };
|
|
117
|
+
}
|
|
118
|
+
const safeEnd = Math.max(0, window.length - overlap);
|
|
119
|
+
const emit = window.slice(0, safeEnd);
|
|
120
|
+
pending = window.slice(safeEnd);
|
|
121
|
+
return { leak: false, emit };
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
process(fragment) {
|
|
125
|
+
if (!fragment) {
|
|
126
|
+
return { leak: false, emit: '' };
|
|
127
|
+
}
|
|
128
|
+
return step(pending + fragment);
|
|
129
|
+
},
|
|
130
|
+
flush() {
|
|
131
|
+
if (scanTextForCanaryLeak(pending, canary)) {
|
|
132
|
+
return { leak: true };
|
|
133
|
+
}
|
|
134
|
+
const emit = pending;
|
|
135
|
+
pending = '';
|
|
136
|
+
return { leak: false, emit };
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function isStreamedCanaryEvent(event) {
|
|
141
|
+
return event.type === 'text' || event.type === 'thought';
|
|
142
|
+
}
|
|
143
|
+
function redactCanary(event, canary) {
|
|
144
|
+
const next = mapStrings(event, (text) => text.replaceAll(canary, OMIT_CANARY));
|
|
145
|
+
if (next && typeof next === 'object') {
|
|
146
|
+
return next;
|
|
147
|
+
}
|
|
148
|
+
return event;
|
|
149
|
+
}
|
|
150
|
+
export { bindCanary, createCanaryStreamGate, eventHasCanary, isStreamedCanaryEvent, mintCanary, OMIT_CANARY, redactCanary, scanTextForCanaryLeak, USER_CLOSE, USER_OPEN, wrapUserData, };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthetic canary egress attack catalog — events built at fuzz time from a canary token.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** lexicon-exempt-file: adversarial corpus fixture — not runtime user or model copy (P2) */
|
|
7
|
+
import type { TurnEvent } from '../../kernel/types.js';
|
|
8
|
+
import type { CanaryEgressCatalogEntry } from './types.js';
|
|
9
|
+
/** Fixed token so canary fuzz reports are reproducible across runs. */
|
|
10
|
+
export declare const FIXED_CANARY = "theo-0123456789abcdef0123456789abcdef";
|
|
11
|
+
export interface CanaryEgressAttack extends CanaryEgressCatalogEntry {
|
|
12
|
+
events: TurnEvent[];
|
|
13
|
+
}
|
|
14
|
+
/** Build the full canary egress adversarial bank for a given token. */
|
|
15
|
+
export declare function buildCanaryEgressAttacks(canary: string): CanaryEgressAttack[];
|
|
16
|
+
/** Catalog metadata without event payloads (for docs / inventory). */
|
|
17
|
+
export declare function canaryEgressCatalog(canary: string): CanaryEgressCatalogEntry[];
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthetic canary egress attack catalog — events built at fuzz time from a canary token.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** Fixed token so canary fuzz reports are reproducible across runs. */
|
|
7
|
+
export const FIXED_CANARY = 'theo-0123456789abcdef0123456789abcdef';
|
|
8
|
+
function splitTextChunks(canary, text) {
|
|
9
|
+
const idx = text.indexOf(canary);
|
|
10
|
+
if (idx < 0) {
|
|
11
|
+
return [{ type: 'text', text }];
|
|
12
|
+
}
|
|
13
|
+
const before = text.slice(0, idx + Math.ceil(canary.length / 2));
|
|
14
|
+
const after = text.slice(idx + Math.ceil(canary.length / 2));
|
|
15
|
+
return [
|
|
16
|
+
{ type: 'text', text: before },
|
|
17
|
+
{ type: 'text', text: after },
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
function allSplitPoints(canary) {
|
|
21
|
+
const cases = [];
|
|
22
|
+
for (let i = 1; i < canary.length; i++) {
|
|
23
|
+
cases.push({
|
|
24
|
+
name: `split-at-${i}`,
|
|
25
|
+
category: 'split-stream',
|
|
26
|
+
shouldBlock: true,
|
|
27
|
+
events: [
|
|
28
|
+
{ type: 'text', text: canary.slice(0, i) },
|
|
29
|
+
{ type: 'text', text: canary.slice(i) },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return cases;
|
|
34
|
+
}
|
|
35
|
+
function charByChar(canary) {
|
|
36
|
+
return {
|
|
37
|
+
name: 'char-by-char',
|
|
38
|
+
category: 'split-stream',
|
|
39
|
+
shouldBlock: true,
|
|
40
|
+
events: [...canary].map((ch) => ({ type: 'text', text: ch })),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Build the full canary egress adversarial bank for a given token. */
|
|
44
|
+
export function buildCanaryEgressAttacks(canary) {
|
|
45
|
+
const hex = canary.slice('theo-'.length);
|
|
46
|
+
const spacedHex = hex.split('').join(' ');
|
|
47
|
+
const b64 = btoa(canary);
|
|
48
|
+
const attacks = [
|
|
49
|
+
{
|
|
50
|
+
name: 'literal-text',
|
|
51
|
+
category: 'literal',
|
|
52
|
+
shouldBlock: true,
|
|
53
|
+
events: [{ type: 'text', text: `Here is the token: ${canary}` }],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'literal-thought',
|
|
57
|
+
category: 'literal',
|
|
58
|
+
shouldBlock: true,
|
|
59
|
+
events: [{ type: 'thought', text: `internal ${canary}` }],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'literal-structured',
|
|
63
|
+
category: 'literal',
|
|
64
|
+
shouldBlock: true,
|
|
65
|
+
events: [{ type: 'structured', structured: { secret: canary } }],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'literal-tool',
|
|
69
|
+
category: 'literal',
|
|
70
|
+
shouldBlock: true,
|
|
71
|
+
events: [{ type: 'tool', tool: { name: 'leak', arguments: { token: canary } } }],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'literal-grounding',
|
|
75
|
+
category: 'literal',
|
|
76
|
+
shouldBlock: true,
|
|
77
|
+
events: [{ type: 'grounding', grounding: { sources: [], metadata: { canary } } }],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'literal-evidence',
|
|
81
|
+
category: 'literal',
|
|
82
|
+
shouldBlock: true,
|
|
83
|
+
events: [{ type: 'evidence', evidence: { provider: 'google', raw: { canary } } }],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'base64-text',
|
|
87
|
+
category: 'encoding',
|
|
88
|
+
shouldBlock: true,
|
|
89
|
+
events: [{ type: 'text', text: `decode: ${b64}` }],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'spaced-hex-text',
|
|
93
|
+
category: 'encoding',
|
|
94
|
+
shouldBlock: true,
|
|
95
|
+
events: [{ type: 'text', text: `theo- ${spacedHex}` }],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'split-wrapper',
|
|
99
|
+
category: 'split-stream',
|
|
100
|
+
shouldBlock: true,
|
|
101
|
+
events: splitTextChunks(canary, `prefix ${canary} suffix`),
|
|
102
|
+
},
|
|
103
|
+
charByChar(canary),
|
|
104
|
+
{
|
|
105
|
+
name: 'thought-then-text-split',
|
|
106
|
+
category: 'split-stream',
|
|
107
|
+
shouldBlock: true,
|
|
108
|
+
events: [
|
|
109
|
+
{ type: 'thought', text: canary.slice(0, 20) },
|
|
110
|
+
{ type: 'text', text: canary.slice(20) },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'benign-theorem',
|
|
115
|
+
category: 'benign',
|
|
116
|
+
shouldBlock: false,
|
|
117
|
+
events: [{ type: 'text', text: 'Welcome to theorem proving and theory.' }],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'benign-prefix-only',
|
|
121
|
+
category: 'benign',
|
|
122
|
+
shouldBlock: false,
|
|
123
|
+
events: [{ type: 'text', text: 'theo-' }],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'benign-random-hex',
|
|
127
|
+
category: 'benign',
|
|
128
|
+
shouldBlock: false,
|
|
129
|
+
events: [{ type: 'text', text: 'theo-deadbeeffeedfacecafebabecafebabe' }],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'benign-safe-reply',
|
|
133
|
+
category: 'benign',
|
|
134
|
+
shouldBlock: false,
|
|
135
|
+
events: [
|
|
136
|
+
{ type: 'text', text: 'Hello! How can I help you today?' },
|
|
137
|
+
{ type: 'tokens', tokens: { input: 1, output: 2, total: 3 } },
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
attacks.push(...allSplitPoints(canary));
|
|
142
|
+
return attacks;
|
|
143
|
+
}
|
|
144
|
+
/** Catalog metadata without event payloads (for docs / inventory). */
|
|
145
|
+
export function canaryEgressCatalog(canary) {
|
|
146
|
+
return buildCanaryEgressAttacks(canary).map(({ name, category, shouldBlock }) => ({
|
|
147
|
+
name,
|
|
148
|
+
category,
|
|
149
|
+
shouldBlock,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound sanitize fuzz runner — exercises all sanitize channels against the corpus.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Run inbound adversarial fuzz against the corpus. Returns false when expected catches are missed.
|
|
8
|
+
*/
|
|
9
|
+
export declare function runInboundGuardrailFuzz(options?: {
|
|
10
|
+
quiet?: boolean;
|
|
11
|
+
}): boolean;
|