theorum 1.0.0 → 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 +81 -25
- package/esm/mod.d.ts +48 -29
- package/esm/mod.js +37 -24
- package/esm/src/cli/commands/bench.js +13 -16
- package/esm/src/cli/commands/fuzz-canary.js +25 -29
- 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 +12 -3
- package/esm/src/cli/commands/run.js +8 -9
- package/esm/src/cli/commands/test.js +8 -8
- package/esm/src/cli/event-log.d.ts +1 -1
- package/esm/src/cli/index.js +12 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +6 -6
- package/esm/src/cli/matrix/synthesizer.js +23 -22
- package/esm/src/guardrails/canary-gate.d.ts +2 -1
- package/esm/src/guardrails/canary-gate.js +2 -1
- package/esm/src/guardrails/canary.d.ts +9 -1
- package/esm/src/guardrails/canary.js +21 -10
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +1 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +12 -15
- package/esm/src/guardrails/corpus/inbound-payloads.js +1 -0
- package/esm/src/guardrails/corpus/live-attacks.js +0 -1
- package/esm/src/guardrails/corpus/mod.d.ts +1 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +1 -0
- package/esm/src/guardrails/corpus/secrets.js +1 -0
- package/esm/src/guardrails/corpus/strings.d.ts +1 -0
- package/esm/src/guardrails/corpus/strings.js +1 -0
- package/esm/src/guardrails/corpus/types.d.ts +1 -0
- package/esm/src/guardrails/egress.d.ts +26 -3
- package/esm/src/guardrails/egress.js +69 -18
- package/esm/src/guardrails/error.d.ts +14 -23
- package/esm/src/guardrails/error.js +86 -77
- 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/lexicon.d.ts +39 -0
- package/esm/src/guardrails/lexicon.js +200 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +15 -13
- package/esm/src/guardrails/live-outbound-gate.js +131 -89
- package/esm/src/guardrails/mod.d.ts +19 -4
- package/esm/src/guardrails/mod.js +12 -3
- 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 +36 -6
- package/esm/src/guardrails/sanitize.js +152 -60
- package/esm/src/guardrails/serialize.d.ts +35 -0
- package/esm/src/guardrails/serialize.js +58 -0
- package/esm/src/guardrails/testing.d.ts +7 -0
- package/esm/src/guardrails/testing.js +4 -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.js +4 -0
- package/esm/src/host/mint-trace.d.ts +1 -1
- 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.js +43 -2
- package/esm/src/kernel/engine/live-inbound.d.ts +15 -3
- package/esm/src/kernel/engine/live-inbound.js +22 -8
- 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.js +127 -40
- package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
- package/esm/src/kernel/engine/runner/mod.js +182 -43
- 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 +24 -0
- package/esm/src/kernel/engine/runner/steps.js +128 -61
- package/esm/src/kernel/engine/runner/stream.d.ts +9 -2
- package/esm/src/kernel/engine/runner/stream.js +114 -55
- package/esm/src/kernel/engine/session/mod.d.ts +3 -1
- package/esm/src/kernel/engine/session/mod.js +386 -34
- 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 +19 -7
- package/esm/src/kernel/mod.js +10 -5
- 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 +10 -10
- package/esm/src/kernel/registry/attachments.js +29 -26
- package/esm/src/kernel/registry/catalog.d.ts +25 -11
- package/esm/src/kernel/registry/catalog.js +59 -24
- package/esm/src/kernel/registry/ingress.js +34 -22
- 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 +34 -14
- package/esm/src/kernel/registry/profiles.js +306 -59
- package/esm/src/kernel/registry/provider-request.js +2 -0
- package/esm/src/kernel/registry/resolve.d.ts +7 -6
- package/esm/src/kernel/registry/resolve.js +102 -117
- 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 +2 -2
- package/esm/src/kernel/registry/vault.js +4 -4
- package/esm/src/kernel/schema.d.ts +97 -11
- package/esm/src/kernel/schema.js +256 -111
- package/esm/src/kernel/stages.d.ts +175 -0
- package/esm/src/kernel/stages.js +476 -0
- package/esm/src/kernel/stop.d.ts +65 -11
- package/esm/src/kernel/stop.js +46 -11
- 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 +63 -24
- package/esm/src/kernel/tools/execute.js +475 -224
- package/esm/src/kernel/tools/harness.js +16 -14
- package/esm/src/kernel/tools/invoke.js +26 -8
- package/esm/src/kernel/tools/mod.d.ts +3 -1
- package/esm/src/kernel/tools/mod.js +2 -1
- 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.js +5 -3
- package/esm/src/kernel/tools/registry.js +20 -10
- 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 +17 -7
- package/esm/src/kernel/tools/resolve.js +38 -17
- package/esm/src/kernel/tools/schema.d.ts +2 -0
- package/esm/src/kernel/tools/schema.js +23 -6
- 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 +198 -20
- package/esm/src/kernel/types.d.ts +253 -114
- 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 +3 -1
- package/esm/src/observability/trace-attach.js +17 -10
- package/esm/src/observability/trace-record.d.ts +12 -4
- package/esm/src/observability/trace-record.js +63 -19
- package/esm/src/observability/trace-sink.d.ts +19 -0
- package/esm/src/observability/trace-sink.js +10 -0
- package/esm/src/observability/trace.d.ts +14 -11
- package/esm/src/observability/trace.js +23 -14
- package/esm/src/observability/types.d.ts +113 -0
- package/esm/src/observability/types.js +11 -0
- package/esm/src/providers/create-provider.d.ts +7 -4
- package/esm/src/providers/create-provider.js +21 -7
- package/esm/src/providers/google/interactions/framing.d.ts +8 -0
- package/esm/src/providers/google/interactions/framing.js +74 -13
- package/esm/src/providers/google/live/framing.d.ts +5 -2
- package/esm/src/providers/google/live/framing.js +49 -12
- package/esm/src/providers/google/live/openapi-schema.js +3 -2
- package/esm/src/providers/google/live/stream.d.ts +9 -0
- package/esm/src/providers/google/live/stream.js +16 -3
- 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 +21 -1
- package/esm/src/providers/openrouter/chat.js +51 -6
- package/esm/src/providers/openrouter/image.js +8 -19
- package/esm/src/providers/openrouter/openai/chat-payload.js +23 -1
- package/esm/src/providers/openrouter/openai/compat.d.ts +7 -1
- package/esm/src/providers/openrouter/openai/compat.js +23 -4
- package/esm/src/providers/openrouter/openai/image-payload.js +8 -2
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +1 -1
- package/esm/src/providers/openrouter/openai/sdk-messages.js +32 -5
- package/package.json +1 -1
- package/esm/src/interface/blocks.d.ts +0 -20
- package/esm/src/interface/blocks.js +0 -180
- package/esm/src/interface/from-profile.d.ts +0 -14
- package/esm/src/interface/from-profile.js +0 -104
- package/esm/src/interface/inputs.d.ts +0 -8
- package/esm/src/interface/inputs.js +0 -127
- package/esm/src/interface/mod.d.ts +0 -10
- package/esm/src/interface/mod.js +0 -9
- package/esm/src/interface/types.d.ts +0 -153
- package/esm/src/interface/types.js +0 -13
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 and Model Context Protocol authorization types.
|
|
3
|
+
*
|
|
4
|
+
* Implements current July 28, 2026 MCP specification requirements:
|
|
5
|
+
* - RFC 9728: Protected Resource Metadata
|
|
6
|
+
* - RFC 8414 & OpenID Connect: Authorization Server Metadata
|
|
7
|
+
* - RFC 9207: Authorization Server Issuer Identification (`iss` validation)
|
|
8
|
+
* - RFC 8707: Resource Indicators
|
|
9
|
+
* - RFC 7636: PKCE with S256
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** RFC 9728 Protected Resource Metadata */
|
|
14
|
+
export interface ProtectedResourceMetadata {
|
|
15
|
+
resource: string;
|
|
16
|
+
authorization_servers: string[];
|
|
17
|
+
scopes_supported?: string[];
|
|
18
|
+
bearer_methods_supported?: string[];
|
|
19
|
+
resource_documentation?: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/** RFC 8414 / OpenID Connect Discovery Metadata */
|
|
23
|
+
export interface AuthorizationServerMetadata {
|
|
24
|
+
issuer: string;
|
|
25
|
+
authorization_endpoint: string;
|
|
26
|
+
token_endpoint: string;
|
|
27
|
+
registration_endpoint?: string;
|
|
28
|
+
scopes_supported?: string[];
|
|
29
|
+
response_types_supported?: string[];
|
|
30
|
+
grant_types_supported?: string[];
|
|
31
|
+
code_challenge_methods_supported?: string[];
|
|
32
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
33
|
+
client_id_metadata_document_supported?: boolean;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/** Standard OAuth 2.1 token response */
|
|
37
|
+
export interface OAuthTokens {
|
|
38
|
+
access_token: string;
|
|
39
|
+
token_type?: string;
|
|
40
|
+
expires_in?: number;
|
|
41
|
+
refresh_token?: string;
|
|
42
|
+
scope?: string;
|
|
43
|
+
/** Custom extra parameters returned by AS */
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
/** Normalized OAuth credential for storage / pass-through in TurnRequest */
|
|
47
|
+
export interface OAuth2Credential {
|
|
48
|
+
type: 'oauth2';
|
|
49
|
+
issuer: string;
|
|
50
|
+
resource?: string;
|
|
51
|
+
accessToken: string;
|
|
52
|
+
refreshToken?: string;
|
|
53
|
+
expiresAt?: number;
|
|
54
|
+
tokenEndpoint: string;
|
|
55
|
+
clientId: string;
|
|
56
|
+
scope?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Bearer token credential */
|
|
59
|
+
export interface BearerCredential {
|
|
60
|
+
type: 'bearer';
|
|
61
|
+
token: string;
|
|
62
|
+
}
|
|
63
|
+
/** API key credential */
|
|
64
|
+
export interface ApiKeyCredential {
|
|
65
|
+
type: 'api_key';
|
|
66
|
+
key: string;
|
|
67
|
+
headerName?: string;
|
|
68
|
+
headerPrefix?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Universal union of supported tool credentials */
|
|
71
|
+
export type ToolCredential = OAuth2Credential | BearerCredential | ApiKeyCredential;
|
|
72
|
+
/** Options for creating an OAuth 2.1 PKCE authorization flow */
|
|
73
|
+
export interface CreatePkceFlowOptions {
|
|
74
|
+
/** Target resource server URL (e.g. MCP server URL) or direct AS URL */
|
|
75
|
+
resourceServerUrl: string;
|
|
76
|
+
/** The OAuth client id or Client ID Metadata Document HTTPS URL */
|
|
77
|
+
clientId: string;
|
|
78
|
+
/** Redirect URI registered with the OAuth client */
|
|
79
|
+
redirectUri: string;
|
|
80
|
+
/** Desired scopes */
|
|
81
|
+
scopes?: string[];
|
|
82
|
+
/** Secret string used to HMAC-sign the stateless state envelope */
|
|
83
|
+
signingSecret: string;
|
|
84
|
+
/** State envelope TTL in milliseconds (default: 10 minutes) */
|
|
85
|
+
stateTtlMs?: number;
|
|
86
|
+
/** Optional pre-resolved discovery metadata to bypass runtime fetch */
|
|
87
|
+
preResolved?: {
|
|
88
|
+
issuer?: string;
|
|
89
|
+
authorizationEndpoint?: string;
|
|
90
|
+
tokenEndpoint?: string;
|
|
91
|
+
resource?: string;
|
|
92
|
+
};
|
|
93
|
+
/** Optional fetch function for testing or proxying */
|
|
94
|
+
fetchFn?: typeof fetch;
|
|
95
|
+
}
|
|
96
|
+
/** Result of initiating a PKCE flow */
|
|
97
|
+
export interface PkceFlowResult {
|
|
98
|
+
authorizationUrl: string;
|
|
99
|
+
state: string;
|
|
100
|
+
codeVerifier: string;
|
|
101
|
+
codeChallenge: string;
|
|
102
|
+
issuer: string;
|
|
103
|
+
resource?: string;
|
|
104
|
+
}
|
|
105
|
+
/** Options for exchanging an authorization code */
|
|
106
|
+
export interface ExchangePkceCodeOptions {
|
|
107
|
+
code: string;
|
|
108
|
+
state: string;
|
|
109
|
+
/** `iss` parameter returned in the authorization response query (RFC 9207) */
|
|
110
|
+
iss?: string;
|
|
111
|
+
/** Redirect URI matching the authorization request */
|
|
112
|
+
redirectUri: string;
|
|
113
|
+
/** Secret used to sign the state envelope */
|
|
114
|
+
signingSecret: string;
|
|
115
|
+
/** Optional token endpoint override (defaults to discovered AS token_endpoint) */
|
|
116
|
+
tokenEndpoint?: string;
|
|
117
|
+
fetchFn?: typeof fetch;
|
|
118
|
+
}
|
|
119
|
+
/** Result of token exchange */
|
|
120
|
+
export interface ExchangePkceCodeResult {
|
|
121
|
+
tokens: OAuthTokens;
|
|
122
|
+
credential: OAuth2Credential;
|
|
123
|
+
}
|
|
124
|
+
/** Options for refreshing an expired OAuth token */
|
|
125
|
+
export interface RefreshOAuthTokenOptions {
|
|
126
|
+
refreshToken: string;
|
|
127
|
+
tokenEndpoint: string;
|
|
128
|
+
clientId: string;
|
|
129
|
+
resource?: string;
|
|
130
|
+
scope?: string;
|
|
131
|
+
issuer: string;
|
|
132
|
+
fetchFn?: typeof fetch;
|
|
133
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 and Model Context Protocol authorization types.
|
|
3
|
+
*
|
|
4
|
+
* Implements current July 28, 2026 MCP specification requirements:
|
|
5
|
+
* - RFC 9728: Protected Resource Metadata
|
|
6
|
+
* - RFC 8414 & OpenID Connect: Authorization Server Metadata
|
|
7
|
+
* - RFC 9207: Authorization Server Issuer Identification (`iss` validation)
|
|
8
|
+
* - RFC 8707: Resource Indicators
|
|
9
|
+
* - RFC 7636: PKCE with S256
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
@@ -770,6 +770,20 @@ const INTERMEDIATE_KEYS = [
|
|
|
770
770
|
'intermediateTokens',
|
|
771
771
|
'intermediate',
|
|
772
772
|
];
|
|
773
|
+
const CACHED_KEYS = [
|
|
774
|
+
'total_cached_tokens',
|
|
775
|
+
'totalCachedTokens',
|
|
776
|
+
'cached_tokens',
|
|
777
|
+
'cachedTokens',
|
|
778
|
+
'cached',
|
|
779
|
+
];
|
|
780
|
+
const CACHE_WRITE_KEYS = [
|
|
781
|
+
'cache_write_tokens',
|
|
782
|
+
'cacheWriteTokens',
|
|
783
|
+
'cache_creation_input_tokens',
|
|
784
|
+
'cacheCreationInputTokens',
|
|
785
|
+
'cacheWrite',
|
|
786
|
+
];
|
|
773
787
|
const TOTAL_KEYS = [
|
|
774
788
|
'total_tokens',
|
|
775
789
|
'totalTokens',
|
|
@@ -789,18 +803,43 @@ function pickNumericField(record, keys) {
|
|
|
789
803
|
}
|
|
790
804
|
return 0;
|
|
791
805
|
}
|
|
806
|
+
function detailsRecord(raw) {
|
|
807
|
+
for (const key of [
|
|
808
|
+
'prompt_tokens_details',
|
|
809
|
+
'promptTokensDetails',
|
|
810
|
+
'input_tokens_details',
|
|
811
|
+
'inputTokensDetails',
|
|
812
|
+
]) {
|
|
813
|
+
const value = raw[key];
|
|
814
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
815
|
+
return value;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return undefined;
|
|
819
|
+
}
|
|
792
820
|
function extractUsageTokens(raw) {
|
|
793
821
|
if (!raw || typeof raw !== 'object') {
|
|
794
822
|
return undefined;
|
|
795
823
|
}
|
|
796
824
|
const r = raw;
|
|
825
|
+
const details = detailsRecord(r);
|
|
797
826
|
const input = pickNumericField(r, INPUT_KEYS);
|
|
798
827
|
const output = pickNumericField(r, OUTPUT_KEYS);
|
|
799
828
|
const thinking = pickNumericField(r, THINKING_KEYS);
|
|
800
829
|
const toolUse = pickNumericField(r, TOOL_KEYS);
|
|
801
830
|
const intermediate = pickNumericField(r, INTERMEDIATE_KEYS);
|
|
831
|
+
const cached = pickNumericField(r, CACHED_KEYS) || (details ? pickNumericField(details, CACHED_KEYS) : 0);
|
|
832
|
+
const cacheWrite = pickNumericField(r, CACHE_WRITE_KEYS) ||
|
|
833
|
+
(details ? pickNumericField(details, CACHE_WRITE_KEYS) : 0);
|
|
802
834
|
const total = pickNumericField(r, TOTAL_KEYS) || input + output + thinking + toolUse + intermediate;
|
|
803
|
-
if (input > 0 ||
|
|
835
|
+
if (input > 0 ||
|
|
836
|
+
output > 0 ||
|
|
837
|
+
thinking > 0 ||
|
|
838
|
+
toolUse > 0 ||
|
|
839
|
+
intermediate > 0 ||
|
|
840
|
+
cached > 0 ||
|
|
841
|
+
cacheWrite > 0 ||
|
|
842
|
+
total > 0) {
|
|
804
843
|
return {
|
|
805
844
|
input,
|
|
806
845
|
output,
|
|
@@ -808,6 +847,8 @@ function extractUsageTokens(raw) {
|
|
|
808
847
|
toolUse,
|
|
809
848
|
total,
|
|
810
849
|
...(intermediate > 0 ? { intermediate } : {}),
|
|
850
|
+
...(cached > 0 ? { cached } : {}),
|
|
851
|
+
...(cacheWrite > 0 ? { cacheWrite } : {}),
|
|
811
852
|
};
|
|
812
853
|
}
|
|
813
854
|
return undefined;
|
|
@@ -896,7 +937,7 @@ function parseStructuredOutput(text) {
|
|
|
896
937
|
return { ok: true, structured: JSON.parse(text) };
|
|
897
938
|
}
|
|
898
939
|
catch {
|
|
899
|
-
return { ok: false, error: 'structured output was not valid JSON' };
|
|
940
|
+
return { ok: false, error: 'structured output was not valid JSON' }; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
900
941
|
}
|
|
901
942
|
}
|
|
902
943
|
export { codeExecutionEvidence, codeExecutionStepKey, eventsFromComplete, eventsFromDelta, eventsFromInteractionSteps, extractTokenEvent, extractUsageTokens, googleBuiltinEvidence, groundingFromEvent, isCodeExecutionType, isGoogleBuiltinStepType, mergeCodeExecutionPayload, parseStructuredOutput, };
|
|
@@ -3,7 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type { Profile } from '../types.js';
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import type { Profile, TurnEvent } from '../types.js';
|
|
7
|
+
export interface LiveInboundPrepareResult {
|
|
8
|
+
text: string;
|
|
9
|
+
/** Present when inbound sanitize redacted spans. */
|
|
10
|
+
guardrail?: TurnEvent;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize profile-controlled inbound text and wrap with user_data fencing.
|
|
14
|
+
*
|
|
15
|
+
* Resolves through the shared policy so Live ingress and the turn engine cannot
|
|
16
|
+
* drift apart on what an unset switch means. Returns an optional guardrail event
|
|
17
|
+
* for the session stream when redaction ran. Match previews follow
|
|
18
|
+
* `observability.include.guardrailMatchPreview`.
|
|
19
|
+
*/
|
|
20
|
+
declare function prepareLiveInboundText(profile: Profile, text: string): LiveInboundPrepareResult;
|
|
9
21
|
export { prepareLiveInboundText };
|
|
@@ -4,14 +4,28 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { wrapUserData } from '../../guardrails/canary.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import { guardrailFromHits, projectGuardrailTurnEvent } from '../../guardrails/events.js';
|
|
8
|
+
import { detectionForTrust, resolveGuardrailPolicy } from '../../guardrails/policy.js';
|
|
9
|
+
import { detectText } from '../../guardrails/sanitize.js';
|
|
10
|
+
import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize profile-controlled inbound text and wrap with user_data fencing.
|
|
13
|
+
*
|
|
14
|
+
* Resolves through the shared policy so Live ingress and the turn engine cannot
|
|
15
|
+
* drift apart on what an unset switch means. Returns an optional guardrail event
|
|
16
|
+
* for the session stream when redaction ran. Match previews follow
|
|
17
|
+
* `observability.include.guardrailMatchPreview`.
|
|
18
|
+
*/
|
|
9
19
|
function prepareLiveInboundText(profile, text) {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
const policy = resolveGuardrailPolicy(profile.guardrails);
|
|
21
|
+
const detected = detectText(text, detectionForTrust(policy, 'untrusted'));
|
|
22
|
+
const raw = guardrailFromHits('live_inbound', 'untrusted', detected.hits, 'redact');
|
|
23
|
+
const includeMatch = resolveObservabilityPolicy(profile.observability).include
|
|
24
|
+
.guardrailMatchPreview;
|
|
25
|
+
const guardrail = raw ? projectGuardrailTurnEvent(raw, includeMatch) : undefined;
|
|
26
|
+
return {
|
|
27
|
+
text: wrapUserData(detected.text),
|
|
28
|
+
...(guardrail ? { guardrail } : {}),
|
|
29
|
+
};
|
|
16
30
|
}
|
|
17
31
|
export { prepareLiveInboundText };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { LiveIngressSpec, Profile } from '../types.js';
|
|
7
|
+
export type LiveIngressChannel = keyof LiveIngressSpec;
|
|
8
|
+
/** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
|
|
9
|
+
export declare function liveIngressChannelDefault(channel: LiveIngressChannel): boolean;
|
|
10
|
+
/** Resolve one channel from an ingress spec object (no profile wrapper). */
|
|
11
|
+
export declare function liveIngressEnabledFromSpec(ingress: LiveIngressSpec | undefined, channel: LiveIngressChannel): boolean;
|
|
12
|
+
/** Whether a realtime ingress channel is enabled on the profile. */
|
|
13
|
+
export declare function liveIngressEnabled(profile: Profile, channel: LiveIngressChannel): boolean;
|
|
14
|
+
/** True when at least one realtime ingress channel is enabled. */
|
|
15
|
+
export declare function hasAnyLiveIngress(profile: Profile): boolean;
|
|
16
|
+
/** Reject profiles with every ingress channel disabled. */
|
|
17
|
+
export declare function assertLiveIngressConfigured(profile: Profile): void;
|
|
18
|
+
/** Reject send* calls when the profile disabled that ingress channel. */
|
|
19
|
+
export declare function assertLiveIngress(profile: Profile, channel: LiveIngressChannel): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { TheorumError } from '../../guardrails/error.js';
|
|
7
|
+
const LIVE_INGRESS_CHANNELS = ['audio', 'video', 'text'];
|
|
8
|
+
function assertLiveProfile(profile) {
|
|
9
|
+
if (profile.type !== 'live') {
|
|
10
|
+
throw new TheorumError(`Profile '${profile.id}' is not type 'live'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
11
|
+
}
|
|
12
|
+
return profile;
|
|
13
|
+
}
|
|
14
|
+
/** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
|
|
15
|
+
export function liveIngressChannelDefault(channel) {
|
|
16
|
+
return channel !== 'text';
|
|
17
|
+
}
|
|
18
|
+
/** Resolve one channel from an ingress spec object (no profile wrapper). */
|
|
19
|
+
export function liveIngressEnabledFromSpec(ingress, channel) {
|
|
20
|
+
const value = ingress?.[channel];
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return liveIngressChannelDefault(channel);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** Whether a realtime ingress channel is enabled on the profile. */
|
|
26
|
+
export function liveIngressEnabled(profile, channel) {
|
|
27
|
+
const live = assertLiveProfile(profile);
|
|
28
|
+
return liveIngressEnabledFromSpec(live.live.ingress, channel);
|
|
29
|
+
}
|
|
30
|
+
/** True when at least one realtime ingress channel is enabled. */
|
|
31
|
+
export function hasAnyLiveIngress(profile) {
|
|
32
|
+
const live = assertLiveProfile(profile);
|
|
33
|
+
return LIVE_INGRESS_CHANNELS.some((channel) => liveIngressEnabledFromSpec(live.live.ingress, channel));
|
|
34
|
+
}
|
|
35
|
+
/** Reject profiles with every ingress channel disabled. */
|
|
36
|
+
export function assertLiveIngressConfigured(profile) {
|
|
37
|
+
const live = assertLiveProfile(profile);
|
|
38
|
+
if (hasAnyLiveIngress(live))
|
|
39
|
+
return;
|
|
40
|
+
throw new TheorumError(`Profile '${live.id}': at least one live.ingress channel (audio, video, text) must be enabled`);
|
|
41
|
+
}
|
|
42
|
+
/** Reject send* calls when the profile disabled that ingress channel. */
|
|
43
|
+
export function assertLiveIngress(profile, channel) {
|
|
44
|
+
if (liveIngressEnabled(profile, channel))
|
|
45
|
+
return;
|
|
46
|
+
throw new TheorumError(`Profile '${profile.id}' live.ingress.${channel} is disabled — cannot send on this channel`);
|
|
47
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { lexiconText } from '../../guardrails/lexicon.js';
|
|
2
|
+
import { profileTurnOutputs } from '../registry/profile-outputs.js';
|
|
1
3
|
const MAX_REPAIR_HISTORY_EXCHANGES = 2;
|
|
2
4
|
const MAX_REPAIR_HISTORY_MESSAGES = MAX_REPAIR_HISTORY_EXCHANGES * 2;
|
|
3
5
|
function scopeHistory(history) {
|
|
@@ -11,28 +13,27 @@ function formatHistoryBlock(messages) {
|
|
|
11
13
|
return '';
|
|
12
14
|
}
|
|
13
15
|
const lines = messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`);
|
|
14
|
-
|
|
16
|
+
const heading = lexiconText('repair.history_heading', { count: messages.length });
|
|
17
|
+
return `${heading}\n${lines.join('\n')}\n\n`;
|
|
15
18
|
}
|
|
16
19
|
function synthesizeRepairPrompt(args) {
|
|
17
20
|
const { profile, repair, history } = args;
|
|
18
21
|
const guidance = repair.guidance ||
|
|
19
|
-
profile
|
|
20
|
-
'
|
|
22
|
+
profileTurnOutputs(profile)?.validation?.repairGuidance ||
|
|
23
|
+
lexiconText('repair.default_guidance');
|
|
21
24
|
const historyBlock = formatHistoryBlock(scopeHistory(history));
|
|
22
|
-
let prompt =
|
|
23
|
-
prompt +=
|
|
24
|
-
prompt +=
|
|
25
|
-
prompt +=
|
|
25
|
+
let prompt = `${lexiconText('repair.prompt_header')}\n\n`;
|
|
26
|
+
prompt += `${lexiconText('repair.prompt_intro')}\n\n`;
|
|
27
|
+
prompt += `${lexiconText('repair.section_previous_output')}\n\`\`\`\n${repair.previousOutput.trim()}\n\`\`\`\n\n`;
|
|
28
|
+
prompt += `${lexiconText('repair.section_validator_rejection')}\n${repair.rejection.trim()}\n\n`;
|
|
26
29
|
if (guidance.trim()) {
|
|
27
|
-
prompt +=
|
|
30
|
+
prompt += `${lexiconText('repair.section_repair_guidance')}\n${guidance.trim()}\n\n`;
|
|
28
31
|
}
|
|
29
32
|
if (historyBlock) {
|
|
30
33
|
prompt += historyBlock;
|
|
31
34
|
}
|
|
32
|
-
prompt +=
|
|
33
|
-
prompt +=
|
|
34
|
-
prompt += `2. Apply the repair guidance without inventing unsupported facts.\n`;
|
|
35
|
-
prompt += `3. Return only the corrected assistant output required by the active profile.`;
|
|
35
|
+
prompt += `${lexiconText('repair.section_instructions')}\n`;
|
|
36
|
+
prompt += lexiconText('repair.prompt_instructions');
|
|
36
37
|
return prompt;
|
|
37
38
|
}
|
|
38
39
|
export { synthesizeRepairPrompt };
|
|
@@ -1,14 +1,40 @@
|
|
|
1
|
+
import { runEnforcer } from '../../../guardrails/egress.js';
|
|
1
2
|
import { TheorumError, throwIfAborted, toErrorEvent } from '../../../guardrails/error.js';
|
|
3
|
+
import { guardrailFromVerdict } from '../../../guardrails/events.js';
|
|
4
|
+
import { lexiconText } from '../../../guardrails/lexicon.js';
|
|
5
|
+
import { resolveGuardrailPolicy } from '../../../guardrails/policy.js';
|
|
2
6
|
import { sanitizeTurnRequest } from '../../../guardrails/sanitize.js';
|
|
7
|
+
import { profileTurnOutputs } from '../../registry/profile-outputs.js';
|
|
3
8
|
import { resolveTurn } from '../../registry/resolve.js';
|
|
4
9
|
import { getStructured } from '../../registry/schemas.js';
|
|
10
|
+
import { injectWouldExceedMaxSteps } from '../../stages.js';
|
|
11
|
+
import { findLast } from '../../util/find-last.js';
|
|
5
12
|
import { collectValidationFailures, formatValidationFailures } from './schema-validation.js';
|
|
13
|
+
import { applyTurnStage } from './stages.js';
|
|
6
14
|
import { executeAttempt } from './steps.js';
|
|
15
|
+
/** Internal reason recorded when a turn is withheld; mapped to public copy on emit. */
|
|
16
|
+
const WITHHELD = 'Turn withheld: egress disclosure violation'; // lexicon-exempt: internal marker mapped by publicError
|
|
7
17
|
function collectAttemptText(events) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (const event of events) {
|
|
20
|
+
if (event.type === 'text' && event.text) {
|
|
21
|
+
parts.push(event.text);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return parts.join('');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Project attempt events into the egress payload.
|
|
28
|
+
*
|
|
29
|
+
* Structured output travels alongside text so a profile with `outputs.structured`
|
|
30
|
+
* is covered by its own egress policy rather than passing unexamined.
|
|
31
|
+
*/
|
|
32
|
+
function projectOutbound(events) {
|
|
33
|
+
const structured = findLast(events, (e) => e.type === 'structured')?.structured;
|
|
34
|
+
return {
|
|
35
|
+
text: collectAttemptText(events),
|
|
36
|
+
...(structured !== undefined ? { structured } : {}),
|
|
37
|
+
};
|
|
12
38
|
}
|
|
13
39
|
function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
|
|
14
40
|
return {
|
|
@@ -25,31 +51,45 @@ function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
|
|
|
25
51
|
}
|
|
26
52
|
async function evaluateEgressOutcome(args) {
|
|
27
53
|
const { egress, attemptEvents, generation, request, profile, canRetry } = args;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
const payload = projectOutbound(attemptEvents);
|
|
55
|
+
const context = {
|
|
56
|
+
stage: 'output_final',
|
|
57
|
+
trust: 'untrusted',
|
|
58
|
+
profileId: profile.id,
|
|
59
|
+
...(generation.canary ? { canary: generation.canary } : {}),
|
|
60
|
+
...(request.input?.slots ? { slots: request.input.slots } : {}),
|
|
61
|
+
...(request.input?.role ? { role: request.input.role } : {}),
|
|
62
|
+
};
|
|
63
|
+
const verdict = await runEnforcer(egress.enforce, payload, context);
|
|
64
|
+
const guardrail = guardrailFromVerdict('output_final', 'untrusted', verdict);
|
|
65
|
+
// `flag` is advisory: the hit is recorded, the turn still releases.
|
|
66
|
+
if (verdict.action === 'allow' || verdict.action === 'flag') {
|
|
67
|
+
return { outcome: { action: 'pass' }, guardrail };
|
|
68
|
+
}
|
|
69
|
+
// The policy supplied safe replacement prose — release that instead.
|
|
70
|
+
if (verdict.action === 'redact') {
|
|
71
|
+
return {
|
|
72
|
+
outcome: { action: 'refusal', event: { type: 'text', text: verdict.text } },
|
|
73
|
+
guardrail,
|
|
74
|
+
};
|
|
38
75
|
}
|
|
39
76
|
if (egress.onBlock === 'refuse_to_user') {
|
|
40
|
-
|
|
77
|
+
// Only emit a text turn when the policy supplied copy. Without it the kernel
|
|
78
|
+
// has nothing to say — an empty text event reads as a successful empty reply —
|
|
79
|
+
// so fall back to the same withheld error the exhausted-retry path uses.
|
|
80
|
+
return {
|
|
81
|
+
outcome: verdict.refusal
|
|
82
|
+
? { action: 'refusal', event: { type: 'text', text: verdict.refusal } }
|
|
83
|
+
: { action: 'withhold', event: toErrorEvent(WITHHELD) },
|
|
84
|
+
guardrail,
|
|
85
|
+
};
|
|
41
86
|
}
|
|
42
87
|
if (canRetry) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const nextRequest = buildRepairRequest(request, attemptText, rejectionMsg, repairGuidance);
|
|
47
|
-
return { action: 'retry', nextRequest };
|
|
88
|
+
const repairGuidance = egress.repairGuidance || lexiconText('egress.default_repair_guidance');
|
|
89
|
+
const nextRequest = buildRepairRequest(request, payload.text, verdict.rejection, repairGuidance);
|
|
90
|
+
return { outcome: { action: 'retry', nextRequest }, guardrail };
|
|
48
91
|
}
|
|
49
|
-
return {
|
|
50
|
-
action: 'withhold',
|
|
51
|
-
event: toErrorEvent('Turn withheld: egress disclosure violation'),
|
|
52
|
-
};
|
|
92
|
+
return { outcome: { action: 'withhold', event: toErrorEvent(WITHHELD) }, guardrail };
|
|
53
93
|
}
|
|
54
94
|
async function evaluateValidationOutcome(args) {
|
|
55
95
|
const { validation, generation, latestStructured, request, canRetry } = args;
|
|
@@ -97,7 +137,7 @@ function updateFlowForRetry(flow, nextReq) {
|
|
|
97
137
|
}
|
|
98
138
|
async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
|
|
99
139
|
const canRetry = flow.currentAttempt < maxRetries;
|
|
100
|
-
const outcome = await evaluateEgressOutcome({
|
|
140
|
+
const { outcome, guardrail } = await evaluateEgressOutcome({
|
|
101
141
|
egress,
|
|
102
142
|
attemptEvents: state.attemptEvents,
|
|
103
143
|
generation: flow.currentGen,
|
|
@@ -105,6 +145,10 @@ async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
|
|
|
105
145
|
profile,
|
|
106
146
|
canRetry,
|
|
107
147
|
});
|
|
148
|
+
if (guardrail) {
|
|
149
|
+
state.allEmittedEvents.push(guardrail);
|
|
150
|
+
yield guardrail;
|
|
151
|
+
}
|
|
108
152
|
if (outcome.action === 'refusal') {
|
|
109
153
|
state.allEmittedEvents.push(outcome.event);
|
|
110
154
|
yield outcome.event;
|
|
@@ -151,18 +195,59 @@ function gateStatusToAction(status, terminalStatus = 'terminal') {
|
|
|
151
195
|
}
|
|
152
196
|
async function* executeSingleAttemptCycle(args) {
|
|
153
197
|
const { flow, state, profile, system, provider, upstream, maxRetries } = args;
|
|
154
|
-
const validation = profile
|
|
155
|
-
const egress = profile.guardrails
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
const validation = profileTurnOutputs(profile)?.validation;
|
|
199
|
+
const egress = resolveGuardrailPolicy(profile.guardrails).egress;
|
|
200
|
+
// Fresh maxSteps budget per validation/egress attempt. before_end inject
|
|
201
|
+
// re-entry inside this cycle still accumulates stepCount (do not reset there).
|
|
202
|
+
state.stepCount = 0;
|
|
203
|
+
let latestStructured;
|
|
204
|
+
// before_end may inject and re-enter the step loop under maxSteps.
|
|
205
|
+
for (;;) {
|
|
206
|
+
state.attemptEvents = [];
|
|
207
|
+
state.withheldVisible = false;
|
|
208
|
+
const attempt = yield* executeAttempt({
|
|
209
|
+
safe: flow.currentReq,
|
|
210
|
+
profile,
|
|
211
|
+
generation: flow.currentGen,
|
|
212
|
+
system,
|
|
213
|
+
provider,
|
|
214
|
+
upstream,
|
|
215
|
+
state,
|
|
216
|
+
});
|
|
217
|
+
latestStructured = attempt.latestStructured;
|
|
218
|
+
// Tool / gate suspension — do not before_end; finalize with that stop.
|
|
219
|
+
if (state.lastStop?.kind === 'tool' || state.lastStop?.kind === 'gate') {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (state.lastStop?.kind === 'cancelled') {
|
|
223
|
+
return { status: 'terminal' };
|
|
224
|
+
}
|
|
225
|
+
const beforeEnd = yield* applyTurnStage({
|
|
226
|
+
profile,
|
|
227
|
+
generation: flow.currentGen,
|
|
228
|
+
state,
|
|
229
|
+
stage: 'before_end',
|
|
230
|
+
step: Math.max(state.stepCount, 1),
|
|
231
|
+
onStage: flow.currentReq.onStage,
|
|
232
|
+
signal: flow.currentReq.signal,
|
|
233
|
+
host: flow.currentGen.host,
|
|
234
|
+
injectWouldExceedMaxSteps: injectWouldExceedMaxSteps(state.stepCount, flow.currentGen.maxSteps),
|
|
235
|
+
});
|
|
236
|
+
if (beforeEnd.abort) {
|
|
237
|
+
state.lastStop = {
|
|
238
|
+
kind: 'cancelled',
|
|
239
|
+
...(typeof beforeEnd.abort === 'object' && beforeEnd.abort.reason
|
|
240
|
+
? { native: beforeEnd.abort.reason }
|
|
241
|
+
: {}),
|
|
242
|
+
};
|
|
243
|
+
return { status: 'terminal' };
|
|
244
|
+
}
|
|
245
|
+
if (beforeEnd.injectCount > 0) {
|
|
246
|
+
// Host extended the turn — another provider step under maxSteps.
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
166
251
|
if (egress?.enforce) {
|
|
167
252
|
const status = yield* handleEgressGate(egress, flow, state, profile, maxRetries);
|
|
168
253
|
const action = gateStatusToAction(status, 'terminal');
|
|
@@ -178,13 +263,15 @@ async function* executeSingleAttemptCycle(args) {
|
|
|
178
263
|
}
|
|
179
264
|
}
|
|
180
265
|
if (validation || egress?.enforce) {
|
|
181
|
-
|
|
182
|
-
|
|
266
|
+
// Progressive-yield already released text/thought live under egress — unless it
|
|
267
|
+
// withheld them mid-stream. A passing final verdict on the full text supersedes
|
|
268
|
+
// that partial-window decision, so the buffer is released instead of dropped.
|
|
269
|
+
yield* yieldBufferedAttemptEvents(state.attemptEvents, !state.withheldVisible);
|
|
183
270
|
}
|
|
184
271
|
return { status: 'success' };
|
|
185
272
|
}
|
|
186
273
|
async function* runAttemptsWithValidation(safe, profile, generation, system, provider, upstream, state) {
|
|
187
|
-
const maxRetries = Math.max(profile
|
|
274
|
+
const maxRetries = Math.max(profileTurnOutputs(profile)?.validation?.maxRetries ?? 0, resolveGuardrailPolicy(profile.guardrails).egress?.maxRetries ?? 0);
|
|
188
275
|
const flow = {
|
|
189
276
|
currentAttempt: 0,
|
|
190
277
|
currentGen: generation,
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import type { ModelProvider, TurnEvent, TurnRequest } from '../../types.js';
|
|
10
|
+
import type { TraceSink } from '../../../observability/trace-sink.js';
|
|
11
|
+
import type { ModelProvider, TurnEvent, TurnHistoryMessage, TurnRequest } from '../../types.js';
|
|
12
|
+
/** Fold one history message to a compaction transcript line (media → markers). */
|
|
13
|
+
declare function compactionTranscriptLine(m: TurnHistoryMessage): string;
|
|
12
14
|
/** Execute one host turn against a provider adapter. */
|
|
13
|
-
declare function runTurn(req: TurnRequest, provider: ModelProvider,
|
|
14
|
-
export { runTurn };
|
|
15
|
+
declare function runTurn(req: TurnRequest, provider: ModelProvider, sinkOverride?: TraceSink): AsyncGenerator<TurnEvent>;
|
|
16
|
+
export { compactionTranscriptLine, runTurn };
|