@theoremjs/agents 0.3.4-next.484.g20d8b9d → 0.3.4-next.522.g9e684cd
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 +61 -51
- package/esm/mod.d.ts +2 -2
- package/esm/mod.js +1 -1
- package/esm/src/guardrails/credential-scan.d.ts +1 -1
- package/esm/src/guardrails/credential-scan.js +41 -18
- package/esm/src/guardrails/destinations.js +6 -2
- package/esm/src/guardrails/detect-at.d.ts +5 -5
- package/esm/src/guardrails/detect-at.js +51 -41
- package/esm/src/guardrails/detect-reply.d.ts +1 -1
- package/esm/src/guardrails/detect-reply.js +4 -2
- package/esm/src/guardrails/egress-automata.js +203 -202
- package/esm/src/guardrails/egress-compiler.js +1 -4
- package/esm/src/guardrails/egress-stream.js +10 -3
- package/esm/src/guardrails/egress-urls.js +4 -2
- package/esm/src/guardrails/error.d.ts +1 -1
- package/esm/src/guardrails/error.js +19 -2
- package/esm/src/guardrails/host-patterns.d.ts +1 -4
- package/esm/src/guardrails/host-patterns.js +1 -10
- package/esm/src/guardrails/injection-patterns.d.ts +3 -1
- package/esm/src/guardrails/injection-patterns.js +87 -4
- package/esm/src/guardrails/labels.d.ts +2 -2
- package/esm/src/guardrails/labels.js +7 -5
- package/esm/src/guardrails/mod.d.ts +2 -2
- package/esm/src/guardrails/mod.js +1 -1
- package/esm/src/guardrails/normalize.js +4 -2
- package/esm/src/guardrails/prompt-echo.js +4 -2
- package/esm/src/guardrails/sanitize.d.ts +1 -1
- package/esm/src/guardrails/sanitize.js +4 -3
- package/esm/src/guardrails/sensitive.d.ts +2 -2
- package/esm/src/guardrails/sensitive.js +1 -1
- package/esm/src/host/client-turn.js +8 -1
- package/esm/src/interface/profile-interface.js +13 -0
- package/esm/src/kernel/answer-tool.d.ts +14 -0
- package/esm/src/kernel/answer-tool.js +26 -0
- package/esm/src/kernel/engine/decision-references.d.ts +14 -0
- package/esm/src/kernel/engine/decision-references.js +55 -0
- package/esm/src/kernel/engine/decision.d.ts +3 -3
- package/esm/src/kernel/engine/decision.js +38 -22
- package/esm/src/kernel/engine/embedding.js +0 -2
- package/esm/src/kernel/engine/runner/gates.js +40 -5
- package/esm/src/kernel/engine/runner/mod.js +13 -5
- package/esm/src/kernel/engine/runner/state.d.ts +9 -1
- package/esm/src/kernel/engine/runner/state.js +10 -1
- package/esm/src/kernel/engine/runner/steps.js +50 -13
- package/esm/src/kernel/engine/runner/stream.d.ts +14 -8
- package/esm/src/kernel/engine/runner/stream.js +25 -13
- package/esm/src/kernel/engine/turn-trace.js +2 -2
- package/esm/src/kernel/mod.d.ts +1 -1
- package/esm/src/kernel/mod.js +1 -1
- package/esm/src/kernel/profile-presence.js +12 -0
- package/esm/src/kernel/profile-scope.js +14 -14
- package/esm/src/kernel/protocols/binding.d.ts +3 -1
- package/esm/src/kernel/protocols/binding.js +6 -0
- package/esm/src/kernel/protocols/limits.d.ts +10 -11
- package/esm/src/kernel/protocols/limits.js +9 -23
- package/esm/src/kernel/protocols/runtime.d.ts +3 -2
- package/esm/src/kernel/protocols/runtime.js +3 -4
- package/esm/src/kernel/provider-budget.d.ts +2 -2
- package/esm/src/kernel/provider-budget.js +22 -12
- package/esm/src/kernel/provider-runtime.js +8 -5
- package/esm/src/kernel/registry/profiles.js +3 -3
- package/esm/src/kernel/registry/resolve.js +4 -0
- package/esm/src/kernel/registry/schemas.js +1 -1
- package/esm/src/kernel/schema.js +28 -2
- package/esm/src/kernel/tools/execute.js +3 -1
- package/esm/src/kernel/tools/image-fetch.d.ts +2 -4
- package/esm/src/kernel/tools/image-fetch.js +19 -20
- package/esm/src/kernel/tools/mcp-sessions.d.ts +7 -2
- package/esm/src/kernel/tools/mcp-sessions.js +21 -12
- package/esm/src/kernel/tools/registry.js +21 -0
- package/esm/src/kernel/tools/remote.d.ts +0 -2
- package/esm/src/kernel/tools/remote.js +28 -23
- package/esm/src/kernel/tools/types.d.ts +30 -4
- package/esm/src/kernel/turn-events.d.ts +2 -0
- package/esm/src/kernel/turn-events.js +1 -0
- package/esm/src/kernel/types.d.ts +4 -2
- package/esm/src/observability/spans.js +7 -8
- package/esm/src/observability/trace-catalog.js +1 -0
- package/esm/src/providers/embedding-limits.js +4 -4
- package/esm/src/providers/gemini/{embedding-protocol.d.ts → embeddings-protocol.d.ts} +3 -3
- package/esm/src/providers/gemini/{embedding-protocol.js → embeddings-protocol.js} +3 -3
- package/esm/src/providers/gemini/live-protocol.js +4 -1
- package/esm/src/providers/gemini/provider.js +2 -2
- package/esm/src/providers/local/embeddings-protocol.d.ts +7 -0
- package/esm/src/providers/local/{embedding-protocol.js → embeddings-protocol.js} +2 -2
- package/esm/src/providers/local/provider.js +2 -2
- package/esm/src/providers/mod.d.ts +3 -3
- package/esm/src/providers/mod.js +3 -3
- package/esm/src/providers/openrouter/decisions-protocol.js +11 -8
- package/esm/src/providers/openrouter/embeddings-protocol.d.ts +6 -0
- package/esm/src/providers/openrouter/{embedding-protocol.js → embeddings-protocol.js} +2 -2
- package/esm/src/providers/openrouter/provider.js +2 -2
- package/package.json +1 -1
- package/esm/src/providers/local/embedding-protocol.d.ts +0 -7
- package/esm/src/providers/openrouter/embedding-protocol.d.ts +0 -6
|
@@ -117,10 +117,93 @@ const INJECTION_PATTERNS = [
|
|
|
117
117
|
IGNORE_MULTILANG,
|
|
118
118
|
OVERRIDE_FRAME,
|
|
119
119
|
];
|
|
120
|
+
/**
|
|
121
|
+
* A pattern whose `exec` is a scan written by hand, which finds exactly the matches of its `source`.
|
|
122
|
+
* V8 grows its regex stack once per step of a repeated group whose steps differ in length, so on a run
|
|
123
|
+
* of millions of characters that regex throws. The egress automata are still built from `source`.
|
|
124
|
+
*/
|
|
125
|
+
class ScannedPattern extends RegExp {
|
|
126
|
+
exec(text) {
|
|
127
|
+
for (let start = this.lastIndex; start < text.length; start++) {
|
|
128
|
+
const end = this.endOf(text, start);
|
|
129
|
+
if (end < 0)
|
|
130
|
+
continue;
|
|
131
|
+
this.lastIndex = end;
|
|
132
|
+
return Object.assign([text.slice(start, end)], {
|
|
133
|
+
index: start,
|
|
134
|
+
input: text,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
this.lastIndex = 0;
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const WHITESPACE = /\s/;
|
|
142
|
+
function isHex(code) {
|
|
143
|
+
return (code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102);
|
|
144
|
+
}
|
|
145
|
+
function isLetter(code) {
|
|
146
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
147
|
+
}
|
|
148
|
+
/** A character `\b` counts as part of a word: a letter, a digit, or `_`. */
|
|
149
|
+
function isWordChar(code) {
|
|
150
|
+
return isLetter(code) || (code >= 48 && code <= 57) || code === 95;
|
|
151
|
+
}
|
|
152
|
+
class HexBlob extends ScannedPattern {
|
|
153
|
+
constructor() {
|
|
154
|
+
super('(?:[0-9a-f]{2}[\\s]?){8,}', 'gi');
|
|
155
|
+
}
|
|
156
|
+
endOf(text, start) {
|
|
157
|
+
let at = start;
|
|
158
|
+
let pairs = 0;
|
|
159
|
+
while (isHex(text.charCodeAt(at)) && isHex(text.charCodeAt(at + 1))) {
|
|
160
|
+
at += 2;
|
|
161
|
+
pairs++;
|
|
162
|
+
if (at < text.length && WHITESPACE.test(text[at]))
|
|
163
|
+
at++;
|
|
164
|
+
}
|
|
165
|
+
return pairs >= 8 ? at : -1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
class PipeSeparated extends ScannedPattern {
|
|
169
|
+
constructor() {
|
|
170
|
+
super('\\b(?:[A-Za-z]+\\|){2,}[A-Za-z]+\\b', 'g');
|
|
171
|
+
}
|
|
172
|
+
endOf(text, start) {
|
|
173
|
+
if (!isLetter(text.charCodeAt(start)) || isWordChar(text.charCodeAt(start - 1)))
|
|
174
|
+
return -1;
|
|
175
|
+
let at = start;
|
|
176
|
+
let piped = 0;
|
|
177
|
+
let lastWordEnd = -1;
|
|
178
|
+
for (;;) {
|
|
179
|
+
let end = at;
|
|
180
|
+
while (isLetter(text.charCodeAt(end)))
|
|
181
|
+
end++;
|
|
182
|
+
if (end > at && text[end] === '|') {
|
|
183
|
+
piped++;
|
|
184
|
+
lastWordEnd = end;
|
|
185
|
+
at = end + 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (end > at && piped >= 2 && !isWordChar(text.charCodeAt(end)))
|
|
189
|
+
return end;
|
|
190
|
+
// why: The regex backtracks one step, so the last piped word is the final one.
|
|
191
|
+
return piped >= 3 ? lastWordEnd : -1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
120
195
|
/** Runs that might encode an injection; each is decoded and checked in `injection.ts`. */
|
|
121
|
-
const BASE64_BLOB = /[A-Za-z0-9+/]{16
|
|
122
|
-
const HEX_BLOB =
|
|
196
|
+
const BASE64_BLOB = /[A-Za-z0-9+/]{16}[A-Za-z0-9+/]*={0,2}/g;
|
|
197
|
+
const HEX_BLOB = new HexBlob();
|
|
123
198
|
const SPACED_LETTERS = /\b(?:[A-Za-z] ){3,}[A-Za-z]\b/g;
|
|
124
199
|
/** Three+ alphabetic tokens joined by `|` (no shell spaces around pipes). */
|
|
125
|
-
const PIPE_SEPARATED =
|
|
126
|
-
|
|
200
|
+
const PIPE_SEPARATED = new PipeSeparated();
|
|
201
|
+
/** A pattern of its own for one scan to move `lastIndex` on, of the same class as `pattern`. */
|
|
202
|
+
function freshPattern(pattern) {
|
|
203
|
+
if (pattern instanceof HexBlob)
|
|
204
|
+
return new HexBlob();
|
|
205
|
+
if (pattern instanceof PipeSeparated)
|
|
206
|
+
return new PipeSeparated();
|
|
207
|
+
return new RegExp(pattern.source, pattern.flags);
|
|
208
|
+
}
|
|
209
|
+
export { BASE64_BLOB, freshPattern, HEX_BLOB, INJECTION_PATTERNS, overrideFrame, PIPE_SEPARATED, SPACED_LETTERS, };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Values that fill a label template's `{param}` placeholders. */
|
|
2
2
|
export type LabelParams = Record<string, string | number>;
|
|
3
3
|
/** Keys are stable API. */
|
|
4
|
-
export declare const LABEL_KEYS: readonly ["provider.decision_failed", "provider.decision_unavailable", "provider.decision_model_missing", "provider.decision_ended", "provider.decision_images_off", "provider.decision_box_model", "provider.budget_exhausted", "provider.adapter_version", "provider.adapter_methods", "provider.adapter_schemas", "provider.fallback_slot", "provider.model_fallback_slot", "provider.host_model", "provider.turn_profile", "provider.model_binding_missing", "provider.session_closed", "provider.settled_call", "provider.session_unavailable", "provider.live_input", "provider.tool_cancel_missing", "provider.kernel_gate", "provider.event_invalid", "provider.response_identity", "provider.credential_resolution", "provider.credential_shape", "provider.stored_continuation", "provider.continuation_mismatch", "provider.continuation_contract", "provider.stateless_history", "provider.events_after_completion", "provider.duplicate_call", "provider.tool_cancel_live", "provider.complete_unavailable", "provider.parallel_tools", "provider.binding_identity", "provider.profile_type", "provider.event_type", "provider.feature", "provider.input_kind", "provider.output_kind", "provider.builtin", "provider.credential_missing", "provider.profile_configuration", "continue.instruction", "canary.bind_note", "user_data.note", "context.note", "taint.blocked", "taint.reason_steered", "taint.reason_tainted", "taint.destination_blocked", "taint.destination_confirm", "advisory.notice_elevated", "advisory.notice_high", "advisory.guidance", "attachments.too_many_files", "attachments.too_many_of_kind", "attachments.file_too_large", "attachments.turn_too_large", "attachments.not_accepted", "attachments.mime_not_allowed", "
|
|
4
|
+
export declare const LABEL_KEYS: readonly ["provider.decision_failed", "provider.decision_unavailable", "provider.decision_model_missing", "provider.decision_ended", "provider.decision_images_off", "provider.decision_box_model", "provider.budget_exhausted", "provider.adapter_version", "provider.adapter_methods", "provider.adapter_schemas", "provider.fallback_slot", "provider.model_fallback_slot", "provider.host_model", "provider.turn_profile", "provider.model_binding_missing", "provider.session_closed", "provider.settled_call", "provider.session_unavailable", "provider.live_input", "provider.tool_cancel_missing", "provider.kernel_gate", "provider.event_invalid", "provider.response_identity", "provider.credential_resolution", "provider.credential_shape", "provider.stored_continuation", "provider.continuation_mismatch", "provider.continuation_contract", "provider.stateless_history", "provider.events_after_completion", "provider.duplicate_call", "provider.tool_cancel_live", "provider.complete_unavailable", "provider.parallel_tools", "provider.binding_identity", "provider.profile_type", "provider.event_type", "provider.feature", "provider.input_kind", "provider.output_kind", "provider.builtin", "provider.credential_missing", "provider.profile_configuration", "continue.instruction", "canary.bind_note", "user_data.note", "context.note", "taint.blocked", "taint.reason_steered", "taint.reason_tainted", "taint.destination_blocked", "taint.destination_confirm", "advisory.notice_elevated", "advisory.notice_high", "advisory.guidance", "attachments.too_many_files", "attachments.too_many_of_kind", "attachments.file_too_large", "attachments.turn_too_large", "attachments.not_accepted", "attachments.mime_not_allowed", "attachments.limits_unconfigured", "quota.exhausted", "quota.busy", "error.config", "error.request", "error.input", "error.action", "error.auth", "error.rate_limit", "error.busy", "error.unsupported", "error.unavailable", "error.bad_response", "error.network", "error.timeout", "error.safety", "error.blocked", "error.declined", "error.failed", "error.cancelled", "error.internal", "repair.default_guidance", "repair.items_guidance", "repair.prompt_header", "repair.prompt_intro", "repair.prompt_instructions", "repair.history_heading", "repair.section_previous_output", "repair.section_validator_rejection", "repair.section_repair_guidance", "repair.section_instructions", "structured.answer_tool", "structured.answer_missing", "compaction.request", "compaction.tool_call", "compaction.tool_result", "decision.box_image", "detect.blocked", "detect.call_blocked", "detect.output_blocked", "detect.shown_blocked", "detect.hint.ids", "detect.hint.financial", "detect.hint.network", "detect.hint.credentials", "detect.hint.injection", "detect.hint.tool_instructions", "detect.hint.canary_leak", "detect.hint.prompt_leak", "detect.hint.marker_leak", "detect.hint.ungiven_images", "detect.hint.ungiven_links", "detect.hint.tool_leak", "detect.hint.own", "egress.default_repair_guidance", "egress.refusal", "egress.refusal_cut", "egress.rejection", "egress.rejection_found", "egress.hint_unscannable", "egress.hint_provider_tool_leak", "thought.omitted_image", "thought.omitted_link", "thought.omitted_instructions", "session.abandon_gated", "session.tool_denied", "session.tool_aborted", "session.sign_in", "sign_in.link", "sign_in.pending", "sign_in.done", "sign_in.declined", "sign_in.expired", "sign_in.out_of_scope", "session.gate_expired", "session.provider_state_unowned", "session.conversation_unknown", "session.turn_ended", "session.gate_pending", "session.part_skipped", "live.session_ended", "voice.unsupported", "voice.permission", "voice.unavailable", "voice.failed", "voice.empty", "stop.cancelled", "stop.interrupted", "stop.length", "stop.filtered", "stop.provider_error", "stop.stream_incomplete", "stop.step_limit", "composer.model", "composer.effort", "tool.awaiting_user", "tool.completed_hidden", "tool.t2_loader_needs_snapshot", "tool.t2_loader_shape", "tool.t2_loader_output_invalid", "tool.execution_uncertain", "tool.input_invalid", "tool.input_invalid_after_mutate", "tool.output_invalid_after_mutate", "tool.handler_no_output", "tool.did_not_run", "tool.page_no_answer", "tool.page_timed_out", "tool.output_invalid", "tool.not_loaded_t2", "tool.wait_unknown", "tool.wait_cycle", "tool.not_visible", "tool.builtin_not_enabled", "tool.provider_native", "tool.not_registered", "tool.builtin_needs_snapshot", "tool.not_allowed", "tool.not_eligible", "tool.unsupported_type", "tool.agent_call_limit", "tool.agent_failed", "tool.activity_failed", "protocol.not_served", "protocol.profile_type", "protocol.options", "protocol.option_clash", "protocol.base_options", "protocol.id_clash", "protocol.ws_headers", "protocol.builtin_unwritten", "protocol.provider_missing", "protocol.connection", "protocol.part_schema", "protocol.part_invalid", "protocol.key_invalid", "protocol.no_key", "protocol.reply_shape", "protocol.endpoint_url", "protocol.endpoint_host", "protocol.http", "protocol.no_operation", "protocol.definition", "protocol.provider_id_clash", "protocol.history_needed"];
|
|
5
5
|
/** One of {@linkcode LABEL_KEYS}. */
|
|
6
6
|
export type LabelKey = (typeof LABEL_KEYS)[number];
|
|
7
7
|
/**
|
|
@@ -10,7 +10,7 @@ export type LabelKey = (typeof LABEL_KEYS)[number];
|
|
|
10
10
|
* puts into history. Only these overrides travel to the browser; the rest
|
|
11
11
|
* (repair prompts, canary, taint, egress) stay on the host.
|
|
12
12
|
*/
|
|
13
|
-
export declare const CLIENT_LABEL_KEYS: readonly ["attachments.too_many_files", "attachments.too_many_of_kind", "attachments.file_too_large", "attachments.turn_too_large", "attachments.not_accepted", "attachments.mime_not_allowed", "
|
|
13
|
+
export declare const CLIENT_LABEL_KEYS: readonly ["attachments.too_many_files", "attachments.too_many_of_kind", "attachments.file_too_large", "attachments.turn_too_large", "attachments.not_accepted", "attachments.mime_not_allowed", "attachments.limits_unconfigured", "detect.blocked", "error.config", "error.request", "error.input", "error.action", "error.auth", "error.rate_limit", "error.busy", "error.unsupported", "error.unavailable", "error.bad_response", "error.network", "error.timeout", "error.safety", "error.blocked", "error.declined", "error.failed", "error.cancelled", "error.internal", "session.abandon_gated", "session.tool_denied", "session.sign_in", "session.gate_expired", "session.turn_ended", "session.gate_pending", "session.part_skipped", "live.session_ended", "tool.awaiting_user", "tool.completed_hidden", "tool.execution_uncertain", "voice.unsupported", "voice.permission", "voice.unavailable", "voice.failed", "voice.empty", "stop.cancelled", "stop.interrupted", "stop.length", "stop.filtered", "stop.provider_error", "stop.stream_incomplete", "stop.step_limit", "composer.model", "composer.effort"];
|
|
14
14
|
/** One of {@linkcode CLIENT_LABEL_KEYS}. */
|
|
15
15
|
export type ClientLabelKey = (typeof CLIENT_LABEL_KEYS)[number];
|
|
16
16
|
/** Host-supplied replacement templates, `{param}` placeholders included. */
|
|
@@ -64,7 +64,6 @@ export const LABEL_KEYS = [
|
|
|
64
64
|
'attachments.turn_too_large',
|
|
65
65
|
'attachments.not_accepted',
|
|
66
66
|
'attachments.mime_not_allowed',
|
|
67
|
-
'input.too_long',
|
|
68
67
|
'attachments.limits_unconfigured',
|
|
69
68
|
'quota.exhausted',
|
|
70
69
|
'quota.busy',
|
|
@@ -96,6 +95,8 @@ export const LABEL_KEYS = [
|
|
|
96
95
|
'repair.section_validator_rejection',
|
|
97
96
|
'repair.section_repair_guidance',
|
|
98
97
|
'repair.section_instructions',
|
|
98
|
+
'structured.answer_tool',
|
|
99
|
+
'structured.answer_missing',
|
|
99
100
|
'compaction.request',
|
|
100
101
|
'compaction.tool_call',
|
|
101
102
|
'compaction.tool_result',
|
|
@@ -222,7 +223,6 @@ export const CLIENT_LABEL_KEYS = [
|
|
|
222
223
|
'attachments.turn_too_large',
|
|
223
224
|
'attachments.not_accepted',
|
|
224
225
|
'attachments.mime_not_allowed',
|
|
225
|
-
'input.too_long',
|
|
226
226
|
'attachments.limits_unconfigured',
|
|
227
227
|
'detect.blocked',
|
|
228
228
|
'error.config',
|
|
@@ -380,7 +380,6 @@ const DEFAULTS = {
|
|
|
380
380
|
? `Sorry, ${name} is a file type that can't be used here.`
|
|
381
381
|
: "Sorry, that file type can't be used here.";
|
|
382
382
|
},
|
|
383
|
-
'input.too_long': (params) => `Sorry, that message is too long. Please keep it under ${Number(params.maxChars).toLocaleString('en-US')} characters.`,
|
|
384
383
|
'attachments.limits_unconfigured': "Sorry, files can't be used here at the moment.",
|
|
385
384
|
'quota.exhausted': (params) => params.perDay === 1
|
|
386
385
|
? "You've reached today's limit of 1 message."
|
|
@@ -414,6 +413,8 @@ const DEFAULTS = {
|
|
|
414
413
|
'repair.section_validator_rejection': '### VALIDATOR REJECTION',
|
|
415
414
|
'repair.section_repair_guidance': '### REPAIR GUIDANCE',
|
|
416
415
|
'repair.section_instructions': '### INSTRUCTIONS',
|
|
416
|
+
'structured.answer_tool': 'Call this once your work is done, with your final answer as its input. Text you write outside this call is shown as progress, not taken as the answer.',
|
|
417
|
+
'structured.answer_missing': 'The reply did not call {tool}. Call {tool} with your final answer.',
|
|
417
418
|
'compaction.request': 'Summarize the conversation above, including any earlier summary in it. Call the participants the user and the assistant, and keep every name, number, fact, and decision either may need later. Reply with the summary only.',
|
|
418
419
|
'compaction.tool_call': 'Called {tool} with {arguments}',
|
|
419
420
|
'compaction.tool_result': '{tool} returned: {result}',
|
|
@@ -594,7 +595,6 @@ export const LABEL_NOTES = {
|
|
|
594
595
|
'attachments.turn_too_large': "Shown to the user when a message's files together are larger than Turn bytes. Takes {maxTurnBytes}.",
|
|
595
596
|
'attachments.not_accepted': "Shown to the user who sends files or a voice note to an agent that takes none. Takes {channel}: 'voice' or 'attachment'.",
|
|
596
597
|
'attachments.mime_not_allowed': "Shown to the user when a file's type is not in the agent's accepted types. Takes {mimeType}, {channel} ('voice' or 'attachment'), and {fileName} when the file has one.",
|
|
597
|
-
'input.too_long': 'Shown to the user when one text a turn sends, such as the message or a text file, is longer than Theorem reads. Takes {maxChars}.',
|
|
598
598
|
'attachments.limits_unconfigured': 'Shown to the user who sends a file to an agent whose file limits are not set.',
|
|
599
599
|
'quota.exhausted': 'Shown to the user who has used up the daily message cap. Takes {perDay}.',
|
|
600
600
|
'quota.busy': 'Shown to the user who sends a message while their last one is still being answered (`quotaBusy`).',
|
|
@@ -626,6 +626,8 @@ export const LABEL_NOTES = {
|
|
|
626
626
|
'repair.section_validator_rejection': "The heading over the validator's or egress check's reason in the repair request.",
|
|
627
627
|
'repair.section_repair_guidance': 'The heading over the repair guidance in the repair request.',
|
|
628
628
|
'repair.section_instructions': 'The heading over the numbered steps in the repair request.',
|
|
629
|
+
'structured.answer_tool': "The description of the answer tool: on a structured turn that has tools, the model calls it with its final answer, and the tool's input is the structured output.",
|
|
630
|
+
'structured.answer_missing': 'The validator rejection in the repair request when a structured turn with tools ended without calling the answer tool. Takes {tool}.',
|
|
629
631
|
'compaction.request': 'Sent to the compactor after the messages it compacts, asking for the summary.',
|
|
630
632
|
'compaction.tool_call': 'How a tool call reads to the compactor. Takes {tool} and {arguments}, the call as JSON.',
|
|
631
633
|
'compaction.tool_result': 'How a tool result reads to the compactor. Takes {tool} and {result}, the output as sent to the model.',
|
|
@@ -759,9 +761,9 @@ const LABEL_PLACEHOLDERS = {
|
|
|
759
761
|
'attachments.turn_too_large': ['maxTurnBytes'],
|
|
760
762
|
'attachments.not_accepted': ['channel'],
|
|
761
763
|
'attachments.mime_not_allowed': ['mimeType', 'channel', 'fileName'],
|
|
762
|
-
'input.too_long': ['maxChars'],
|
|
763
764
|
'quota.exhausted': ['perDay'],
|
|
764
765
|
'repair.history_heading': ['count'],
|
|
766
|
+
'structured.answer_missing': TOOL,
|
|
765
767
|
'compaction.tool_call': ['tool', 'arguments'],
|
|
766
768
|
'compaction.tool_result': ['tool', 'result'],
|
|
767
769
|
'decision.box_image': ['image'],
|
|
@@ -25,7 +25,7 @@ export { errorCopiesSchema, errorKindSchema, guardrailEventSchema } from './even
|
|
|
25
25
|
export { guardrailFromHits, guardrailFromVerdict, guardrailTurnEvent, projectGuardrailTurnEvent, } from './events.js';
|
|
26
26
|
export { hitFromSpan, projectGuardrailEvent, } from './hits.js';
|
|
27
27
|
export type { CompiledPatterns, HostPattern } from './host-patterns.js';
|
|
28
|
-
export { listsPatterns,
|
|
28
|
+
export { listsPatterns, patternListProblem, } from './host-patterns.js';
|
|
29
29
|
export { injectionSpans } from './injection.js';
|
|
30
30
|
export type { ClientLabelKey, LabelKey, LabelOverrides, LabelParams } from './labels.js';
|
|
31
31
|
export { CLIENT_LABEL_KEYS, LABEL_KEYS, labelCatalogRows, labelDefault, labelText, overrideLabels, resetLabels, validateLabelOverrides, } from './labels.js';
|
|
@@ -50,5 +50,5 @@ export type { Directive, DirectiveSignal } from './tool-directives.js';
|
|
|
50
50
|
export { advisoryLevel, DIRECTIVE_SIGNALS, directives } from './tool-directives.js';
|
|
51
51
|
export type { GuardedToolText, InspectedToolArguments } from './tool-result.js';
|
|
52
52
|
export { checkTaintGate, composeToolText, guardToolFailureText, guardToolResult, inspectToolArguments, isRemoteOrigin, isSuspicious, isTainted, recordTaint, TOOL_CLOSE, toolCallEvent, wrapToolData, } from './tool-result.js';
|
|
53
|
-
export type { AdvisoryLevel, BlockedReplyOnBlock, BlockedReplySpec, DestinationGate, GuardrailAction, GuardrailContext, GuardrailEvent, GuardrailHit, GuardrailStage, HostGuardrailsSpec, NetworkGuardrailSpec, OutboundPayload, ProfileGuardrailsSpec, Provenance, QuotaGuardrailSpec, ResolvedBlockedReply, ResolvedGuardrailPolicy, Severity, TaintGate, TaintGuardrailSpec, ToolOrigin, TrustLevel, TurnTaint, Verdict, } from './types.js';
|
|
53
|
+
export type { AdvisoryLevel, BlockedReplyOnBlock, BlockedReplySpec, DecisionDisclosureEnforcer, DecisionDisclosureVerdict, DecisionGuardrailsSpec, DestinationGate, GuardrailAction, GuardrailContext, GuardrailEvent, GuardrailHit, GuardrailStage, HostGuardrailsSpec, NetworkGuardrailSpec, OutboundPayload, ProfileGuardrailsSpec, Provenance, QuotaGuardrailSpec, ResolvedBlockedReply, ResolvedGuardrailPolicy, Severity, TaintGate, TaintGuardrailSpec, ToolOrigin, TrustLevel, TurnTaint, Verdict, } from './types.js';
|
|
54
54
|
export { ADVISORY_LEVELS, BLOCKED_REPLY_ON_BLOCK, DESTINATION_GATES, GUARDRAIL_STAGES, SEVERITIES, TAINT_GATES, TOOL_ORIGINS, TRUST_LEVELS, } from './types.js';
|
|
@@ -16,7 +16,7 @@ export { describeError, ERROR_KINDS, errorKind, isAbortError, isTimeoutError, ki
|
|
|
16
16
|
export { errorCopiesSchema, errorKindSchema, guardrailEventSchema } from './event-schemas.js';
|
|
17
17
|
export { guardrailFromHits, guardrailFromVerdict, guardrailTurnEvent, projectGuardrailTurnEvent, } from './events.js';
|
|
18
18
|
export { hitFromSpan, projectGuardrailEvent, } from './hits.js';
|
|
19
|
-
export { listsPatterns,
|
|
19
|
+
export { listsPatterns, patternListProblem, } from './host-patterns.js';
|
|
20
20
|
export { injectionSpans } from './injection.js';
|
|
21
21
|
export { CLIENT_LABEL_KEYS, LABEL_KEYS, labelCatalogRows, labelDefault, labelText, overrideLabels, resetLabels, validateLabelOverrides, } from './labels.js';
|
|
22
22
|
export { abortLiveOutboundTurn, createLiveOutboundGateSession, finalizeLiveOutboundTurn, processLiveOutboundBatch, } from './live-outbound-gate.js';
|
|
@@ -134,8 +134,10 @@ function withoutMatches(view, pattern) {
|
|
|
134
134
|
let last = 0;
|
|
135
135
|
const keep = (to) => {
|
|
136
136
|
kept.text += view.text.slice(last, to);
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
for (let at = last; at < to; at++) {
|
|
138
|
+
kept.start.push(view.start[at]);
|
|
139
|
+
kept.end.push(view.end[at]);
|
|
140
|
+
}
|
|
139
141
|
};
|
|
140
142
|
for (const match of view.text.matchAll(pattern)) {
|
|
141
143
|
keep(match.index);
|
|
@@ -61,8 +61,10 @@ function promptReadings(stretches, canary) {
|
|
|
61
61
|
const readings = { plain: [], slotted: [] };
|
|
62
62
|
for (const view of PROMPT_VIEWS) {
|
|
63
63
|
const read = viewReadings(stretches.map(view), canary === undefined ? undefined : view(canary));
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
for (const each of read.plain)
|
|
65
|
+
readings.plain.push(each);
|
|
66
|
+
for (const each of read.slotted)
|
|
67
|
+
readings.slotted.push(each);
|
|
66
68
|
}
|
|
67
69
|
return readings;
|
|
68
70
|
}
|
|
@@ -19,7 +19,7 @@ declare function sanitizeProjectId(id: string | undefined): string | undefined;
|
|
|
19
19
|
*/
|
|
20
20
|
declare function sanitizeHistory(history: TurnHistoryMessage[], reader: BoundaryReader): TurnHistoryMessage[];
|
|
21
21
|
/** The error a turn ends on when a detector set to `block` matched at `boundary`. */
|
|
22
|
-
declare function requestRefused(boundary: Boundary): TheoremError;
|
|
22
|
+
declare function requestRefused(boundary: Boundary, reason?: string): TheoremError;
|
|
23
23
|
/** A turn request read at each boundary it crosses. */
|
|
24
24
|
interface SanitizedTurnRequest {
|
|
25
25
|
request: NormalizedTurnRequest;
|
|
@@ -91,9 +91,10 @@ function sanitizeHistory(history, reader) {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
/** The error a turn ends on when a detector set to `block` matched at `boundary`. */
|
|
94
|
-
function requestRefused(boundary) {
|
|
94
|
+
function requestRefused(boundary, reason) {
|
|
95
|
+
const why = reason ? `: ${reason}` : '';
|
|
95
96
|
// labels-exempt: developer contract / internal diagnostic — the user's wording is `detect.blocked`
|
|
96
|
-
return new TheoremError('input', `guardrails.detect blocked the request at ${boundary}`, {
|
|
97
|
+
return new TheoremError('input', `guardrails.detect blocked the request at ${boundary}${why}`, {
|
|
97
98
|
copy: { key: 'detect.blocked' },
|
|
98
99
|
});
|
|
99
100
|
}
|
|
@@ -125,7 +126,7 @@ function sanitizeTurnRequestWithEvents(req, profile) {
|
|
|
125
126
|
if (event)
|
|
126
127
|
events.push(guardrailTurnEvent(event));
|
|
127
128
|
if (found.action === 'block')
|
|
128
|
-
refusal ??= requestRefused(boundary);
|
|
129
|
+
refusal ??= requestRefused(boundary, found.errorInternal);
|
|
129
130
|
}
|
|
130
131
|
events.push(...told.events.map(guardrailTurnEvent));
|
|
131
132
|
if (told.blocked)
|
|
@@ -4,8 +4,8 @@ declare const CARD_CANDIDATE: RegExp;
|
|
|
4
4
|
interface SensitivePattern {
|
|
5
5
|
group: SensitiveGroup;
|
|
6
6
|
pattern: RegExp;
|
|
7
|
-
/** Whether a match at `at` of `text` counts. */
|
|
8
|
-
hit?: (match: string, text: string, at: number) => boolean;
|
|
7
|
+
/** Whether a match at `at` of `text` counts; `lower` is `text` in lower case. */
|
|
8
|
+
hit?: (match: string, text: string, at: number, lower?: () => string) => boolean;
|
|
9
9
|
}
|
|
10
10
|
/** Every pattern, with its group. */
|
|
11
11
|
declare const SENSITIVE_PATTERNS: readonly SensitivePattern[];
|
|
@@ -81,7 +81,7 @@ const SENSITIVE_PATTERNS = [
|
|
|
81
81
|
...CREDENTIALS.map((rule) => ({
|
|
82
82
|
group: 'credentials',
|
|
83
83
|
pattern: new RegExp(rule.pattern.source, `${rule.pattern.flags}g`),
|
|
84
|
-
hit: (match, text, at) => credentialHit(rule, GLOBAL_ALLOWLIST, match, text, at),
|
|
84
|
+
hit: (match, text, at, lower) => credentialHit(rule, GLOBAL_ALLOWLIST, match, text, at, lower?.()),
|
|
85
85
|
})),
|
|
86
86
|
];
|
|
87
87
|
const ALL_GROUPS = {
|
|
@@ -33,6 +33,13 @@ function stripErrorInternal(event) {
|
|
|
33
33
|
const { errorInternal: _internal, ...rest } = event;
|
|
34
34
|
return rest;
|
|
35
35
|
}
|
|
36
|
+
function stripCompactionInternal(event) {
|
|
37
|
+
if (event.type !== 'compaction' || event.failure?.errorInternal === undefined) {
|
|
38
|
+
return event;
|
|
39
|
+
}
|
|
40
|
+
const { errorInternal: _internal, ...failure } = event.failure;
|
|
41
|
+
return { ...event, failure };
|
|
42
|
+
}
|
|
36
43
|
function stripGuardrailInternal(event) {
|
|
37
44
|
if (event.type !== 'guardrail' || !event.guardrail?.errorInternal) {
|
|
38
45
|
return event;
|
|
@@ -54,7 +61,7 @@ function stripEvidenceRaw(event) {
|
|
|
54
61
|
function forClient(event, options) {
|
|
55
62
|
let out = options?.reader === 'builder'
|
|
56
63
|
? builderWording(event)
|
|
57
|
-
: stripGuardrailInternal(stripErrorInternal(event));
|
|
64
|
+
: stripCompactionInternal(stripGuardrailInternal(stripErrorInternal(event)));
|
|
58
65
|
if (!options?.includeEvidenceRaw) {
|
|
59
66
|
out = stripEvidenceRaw(out);
|
|
60
67
|
}
|
|
@@ -29,6 +29,19 @@ const modelBinding = z.object({
|
|
|
29
29
|
meter: z.enum(COMPACTION_METERS).optional(),
|
|
30
30
|
})
|
|
31
31
|
.optional(),
|
|
32
|
+
limits: z
|
|
33
|
+
.object({
|
|
34
|
+
operationTimeoutMs: z.number().optional(),
|
|
35
|
+
connectTimeoutMs: z.number().optional(),
|
|
36
|
+
liveStepTimeoutMs: z.number().optional(),
|
|
37
|
+
maxWireRecordBytes: z.number().optional(),
|
|
38
|
+
maxWireResponseBytes: z.number().optional(),
|
|
39
|
+
maxEventBytes: z.number().optional(),
|
|
40
|
+
maxStepBytes: z.number().optional(),
|
|
41
|
+
maxStepEvents: z.number().optional(),
|
|
42
|
+
maxPendingToolCalls: z.number().optional(),
|
|
43
|
+
})
|
|
44
|
+
.optional(),
|
|
32
45
|
});
|
|
33
46
|
const outputs = z.object({
|
|
34
47
|
structured: z
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type LabelOverrides } from '../guardrails/labels.js';
|
|
2
|
+
import type { ModelRequest, ResolvedGeneration } from './types.js';
|
|
3
|
+
/** The tool a structured turn with tools answers through. */
|
|
4
|
+
export declare const ANSWER_TOOL = "final_answer";
|
|
5
|
+
/**
|
|
6
|
+
* True when the turn's structured output comes from the answer tool, not from a response format.
|
|
7
|
+
*
|
|
8
|
+
* why: Many endpoints refuse a response format beside tools, and many models given both answer in
|
|
9
|
+
* JSON at once without calling a tool. Read it off the turn's generation, not one step's: the
|
|
10
|
+
* step that must answer has its tools taken away, and still answers through this tool.
|
|
11
|
+
*/
|
|
12
|
+
export declare function answersByTool(generation: Pick<ResolvedGeneration, 'structured' | 'tools'>): boolean;
|
|
13
|
+
/** The request with the answer tool in place of the response format, when the turn answers by tool. */
|
|
14
|
+
export declare function withAnswerTool(request: ModelRequest, generation: Pick<ResolvedGeneration, 'structured' | 'tools'>, labels: LabelOverrides | undefined): ModelRequest;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { labelText } from '../guardrails/labels.js';
|
|
2
|
+
/** The tool a structured turn with tools answers through. */
|
|
3
|
+
export const ANSWER_TOOL = 'final_answer';
|
|
4
|
+
/**
|
|
5
|
+
* True when the turn's structured output comes from the answer tool, not from a response format.
|
|
6
|
+
*
|
|
7
|
+
* why: Many endpoints refuse a response format beside tools, and many models given both answer in
|
|
8
|
+
* JSON at once without calling a tool. Read it off the turn's generation, not one step's: the
|
|
9
|
+
* step that must answer has its tools taken away, and still answers through this tool.
|
|
10
|
+
*/
|
|
11
|
+
export function answersByTool(generation) {
|
|
12
|
+
return generation.structured !== null && generation.tools.wire.length > 0;
|
|
13
|
+
}
|
|
14
|
+
/** The request with the answer tool in place of the response format, when the turn answers by tool. */
|
|
15
|
+
export function withAnswerTool(request, generation, labels) {
|
|
16
|
+
const { structured } = generation;
|
|
17
|
+
if (!structured || !answersByTool(generation))
|
|
18
|
+
return request;
|
|
19
|
+
const tool = {
|
|
20
|
+
type: 'function',
|
|
21
|
+
name: ANSWER_TOOL,
|
|
22
|
+
description: labelText('structured.answer_tool', {}, labels),
|
|
23
|
+
parameters: structured.jsonSchema,
|
|
24
|
+
};
|
|
25
|
+
return { ...request, structured: null, wireTools: [...(request.wireTools ?? []), tool] };
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DecisionAsset } from '../types.js';
|
|
2
|
+
/** A run of text, or the image a reference in it stands for. */
|
|
3
|
+
export type ReferencePiece = string | {
|
|
4
|
+
reference: string;
|
|
5
|
+
asset: DecisionAsset;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* A string split at the image references in it, as TypeSafe reads them: a string that is exactly a
|
|
9
|
+
* reference is that image; otherwise references match as plain text, left to right, never
|
|
10
|
+
* overlapping, the longest where two start together. A string with no reference comes back whole.
|
|
11
|
+
*/
|
|
12
|
+
export declare function splitReferences(text: string, assets: Record<string, DecisionAsset>): ReferencePiece[];
|
|
13
|
+
/** The images the strings of `values` reference, in the order of `assets`. */
|
|
14
|
+
export declare function referencedAssets(values: readonly unknown[], assets: Record<string, DecisionAsset>): Record<string, DecisionAsset>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A string split at the image references in it, as TypeSafe reads them: a string that is exactly a
|
|
3
|
+
* reference is that image; otherwise references match as plain text, left to right, never
|
|
4
|
+
* overlapping, the longest where two start together. A string with no reference comes back whole.
|
|
5
|
+
*/
|
|
6
|
+
export function splitReferences(text, assets) {
|
|
7
|
+
if (Object.hasOwn(assets, text))
|
|
8
|
+
return [{ reference: text, asset: assets[text] }];
|
|
9
|
+
const references = Object.keys(assets);
|
|
10
|
+
const pieces = [];
|
|
11
|
+
let from = 0;
|
|
12
|
+
while (from < text.length) {
|
|
13
|
+
let at = -1;
|
|
14
|
+
let found = '';
|
|
15
|
+
for (const reference of references) {
|
|
16
|
+
const index = text.indexOf(reference, from);
|
|
17
|
+
if (index < 0)
|
|
18
|
+
continue;
|
|
19
|
+
if (at < 0 || index < at || (index === at && reference.length > found.length)) {
|
|
20
|
+
at = index;
|
|
21
|
+
found = reference;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (at < 0)
|
|
25
|
+
break;
|
|
26
|
+
if (at > from)
|
|
27
|
+
pieces.push(text.slice(from, at));
|
|
28
|
+
pieces.push({ reference: found, asset: assets[found] });
|
|
29
|
+
from = at + found.length;
|
|
30
|
+
}
|
|
31
|
+
if (from < text.length)
|
|
32
|
+
pieces.push(text.slice(from));
|
|
33
|
+
return pieces;
|
|
34
|
+
}
|
|
35
|
+
/** Every string value in a JSON value, keys left out: the only places a reference is read. */
|
|
36
|
+
function* stringValues(value) {
|
|
37
|
+
if (typeof value === 'string')
|
|
38
|
+
yield value;
|
|
39
|
+
else if (Array.isArray(value))
|
|
40
|
+
for (const item of value)
|
|
41
|
+
yield* stringValues(item);
|
|
42
|
+
else if (value !== null && typeof value === 'object')
|
|
43
|
+
for (const item of Object.values(value))
|
|
44
|
+
yield* stringValues(item);
|
|
45
|
+
}
|
|
46
|
+
/** The images the strings of `values` reference, in the order of `assets`. */
|
|
47
|
+
export function referencedAssets(values, assets) {
|
|
48
|
+
const used = new Set();
|
|
49
|
+
for (const value of values)
|
|
50
|
+
for (const text of stringValues(value))
|
|
51
|
+
for (const piece of splitReferences(text, assets))
|
|
52
|
+
if (typeof piece !== 'string')
|
|
53
|
+
used.add(piece.reference);
|
|
54
|
+
return Object.fromEntries(Object.entries(assets).filter(([reference]) => used.has(reference)));
|
|
55
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { TraceSink } from '../../observability/trace-sink.js';
|
|
|
3
3
|
import type { ProviderHostOptions } from '../protocols/runtime.js';
|
|
4
4
|
import type { KernelRegistry } from '../registry/kernel-registry.js';
|
|
5
5
|
import type { DecisionProfile, DecisionRequest, DecisionResult } from '../types.js';
|
|
6
|
-
/**
|
|
6
|
+
/** Each decision failure code, and the error kind it reads as. */
|
|
7
7
|
declare const DECISION_ERROR_KINDS: {
|
|
8
8
|
readonly invalid_request: "request";
|
|
9
9
|
readonly authentication: "auth";
|
|
@@ -36,8 +36,8 @@ export declare function validateDecisionRequest(request: Omit<DecisionRequest, '
|
|
|
36
36
|
questions: unknown;
|
|
37
37
|
}, profile: DecisionProfile): asserts request is DecisionRequest;
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* One decision, traced however it ends. A transient failure (408, 429, a 5xx gateway or a network
|
|
40
|
+
* error) is retried within five seconds; a trace write failure never changes the decision's outcome.
|
|
41
41
|
*/
|
|
42
42
|
export declare function runDecisionInRegistry(registry: KernelRegistry, request: DecisionRequest, options: RunDecisionOptions): Promise<DecisionResult>;
|
|
43
43
|
export {};
|