@theoremjs/agents 0.3.4-next.522.g9e684cd → 0.3.4-next.528.g44c76f9
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/esm/src/guardrails/detect-at.d.ts +4 -2
- package/esm/src/guardrails/detect-at.js +41 -22
- package/esm/src/guardrails/detect-reply.d.ts +1 -1
- package/esm/src/guardrails/detect-reply.js +2 -1
- package/esm/src/guardrails/error.d.ts +5 -1
- package/esm/src/guardrails/error.js +44 -1
- package/esm/src/guardrails/event-schemas.d.ts +5 -0
- package/esm/src/guardrails/event-schemas.js +1 -0
- package/esm/src/guardrails/network.js +3 -3
- package/esm/src/host/client-turn.js +3 -2
- package/esm/src/kernel/engine/decision.d.ts +10 -0
- package/esm/src/kernel/engine/decision.js +34 -18
- package/esm/src/kernel/engine/turn-trace.js +2 -0
- package/esm/src/kernel/tools/coordinate.js +6 -6
- package/esm/src/kernel/tools/execute.js +5 -4
- package/esm/src/kernel/tools/memory-execution.js +11 -7
- package/esm/src/kernel/tools/operation.d.ts +2 -1
- package/esm/src/kernel/tools/operation.js +13 -11
- package/esm/src/kernel/tools/remote.js +8 -1
- package/esm/src/observability/trace-catalog.js +2 -0
- package/esm/src/providers/shared/retry.js +1 -2
- package/esm/src/providers/shared/upstream-tap.d.ts +0 -2
- package/esm/src/providers/shared/upstream-tap.js +2 -16
- package/package.json +1 -1
|
@@ -15,6 +15,8 @@ interface Detection {
|
|
|
15
15
|
hits: GuardrailHit[];
|
|
16
16
|
/** Why a host's `find` failed, for the builder only: it reaches `guardrail.errorInternal`. */
|
|
17
17
|
errorInternal?: string;
|
|
18
|
+
/** The same failure in the kernel's words only, with no text from the host: what the trace stores. */
|
|
19
|
+
errorSummary?: string;
|
|
18
20
|
}
|
|
19
21
|
declare function stronger(left: DetectOutcome, right: DetectOutcome | 'ignore'): DetectOutcome;
|
|
20
22
|
/**
|
|
@@ -94,7 +96,7 @@ declare function storedHostSpans(text: string, detect?: ResolvedDetect): RedactS
|
|
|
94
96
|
interface BoundaryReader {
|
|
95
97
|
/** The text to let through, or `''` once a match blocks: the caller reads `found().action`. */
|
|
96
98
|
read(text: string): string;
|
|
97
|
-
found(): Pick<Detection, 'action' | 'hits' | 'errorInternal'>;
|
|
99
|
+
found(): Pick<Detection, 'action' | 'hits' | 'errorInternal' | 'errorSummary'>;
|
|
98
100
|
}
|
|
99
101
|
/** A reader for everything crossing `boundary` in one request. */
|
|
100
102
|
declare function boundaryReader(boundary: Boundary, detect: ResolvedDetect): BoundaryReader;
|
|
@@ -102,6 +104,6 @@ declare function boundaryReader(boundary: Boundary, detect: ResolvedDetect): Bou
|
|
|
102
104
|
* The guardrail event for what was found at a boundary, or `undefined` when nothing matched.
|
|
103
105
|
* `stage` is the boundary's own unless the text was read as it streamed.
|
|
104
106
|
*/
|
|
105
|
-
declare function detectEvent(boundary: Boundary, found: Pick<Detection, 'action' | 'hits' | 'errorInternal'>, provenance?: Provenance, stage?: GuardrailStage): GuardrailEvent | undefined;
|
|
107
|
+
declare function detectEvent(boundary: Boundary, found: Pick<Detection, 'action' | 'hits' | 'errorInternal' | 'errorSummary'>, provenance?: Provenance, stage?: GuardrailStage): GuardrailEvent | undefined;
|
|
106
108
|
export type { BoundaryReader, Detection, DetectOutcome, DetectScope, Release, StoredSides, StreamRead, Stretch, };
|
|
107
109
|
export { boundaryReader, detectAt, detectEvent, detectorsAt, detectReads, detectRelease, hostAt, isScoped, leakScopeOf, redactDetectors, scopeOf, storedHostSpans, storedSpans, stronger, };
|
|
@@ -16,6 +16,14 @@ import { sensitiveSpans } from './sensitive.js';
|
|
|
16
16
|
import { SENSITIVE_GROUPS } from './sensitive-groups.js';
|
|
17
17
|
import { directives, mergedRanges } from './tool-directives.js';
|
|
18
18
|
import { ownToolsWithout, toolLeakSpans } from './tool-leak.js';
|
|
19
|
+
function failureText(failures) {
|
|
20
|
+
if (failures.length === 0)
|
|
21
|
+
return {};
|
|
22
|
+
return {
|
|
23
|
+
errorInternal: failures.map(({ errorInternal }) => errorInternal).join('\n'),
|
|
24
|
+
errorSummary: failures.map(({ errorSummary }) => errorSummary).join('\n'),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
19
27
|
const STRENGTH = ['allow', 'flag', 'redact', 'block'];
|
|
20
28
|
function stronger(left, right) {
|
|
21
29
|
if (right === 'ignore')
|
|
@@ -139,15 +147,18 @@ function hitOf(detector, text, span) {
|
|
|
139
147
|
function leavesImages(detector, boundary, detect) {
|
|
140
148
|
return detector === 'ungiven_links' && detect.ungiven_images[boundary] !== 'ignore';
|
|
141
149
|
}
|
|
150
|
+
/** A thrown value's type: a name of letters only, else `Error`, so no host text gets through. */
|
|
151
|
+
function thrownType(err) {
|
|
152
|
+
const name = err instanceof Error ? err.name : typeof err;
|
|
153
|
+
return /^[A-Za-z]{1,64}$/.test(name) ? name : 'Error';
|
|
154
|
+
}
|
|
142
155
|
/** What a host's `find` matched in `text`, or how it failed. */
|
|
143
156
|
function foundBy(find, text, boundary) {
|
|
144
157
|
try {
|
|
145
158
|
const spans = find(text, boundary ? { boundary } : {});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
reason: 'find returned no list of stretches inside the text',
|
|
150
|
-
};
|
|
159
|
+
// labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
160
|
+
const said = 'find returned no list of stretches inside the text';
|
|
161
|
+
const outside = { signal: 'find_result', reason: said, summary: said };
|
|
151
162
|
if (!Array.isArray(spans))
|
|
152
163
|
return outside;
|
|
153
164
|
const inside = spans.every(({ start, end }) => Number.isInteger(start) &&
|
|
@@ -159,7 +170,12 @@ function foundBy(find, text, boundary) {
|
|
|
159
170
|
}
|
|
160
171
|
catch (err) {
|
|
161
172
|
// why: A reading that failed found nothing it can vouch for, so the text does not cross.
|
|
162
|
-
|
|
173
|
+
const id = crypto.randomUUID().slice(0, 8);
|
|
174
|
+
return {
|
|
175
|
+
signal: 'find_threw',
|
|
176
|
+
reason: `find threw: ${describeError(err)} (error ${id})`,
|
|
177
|
+
summary: `find threw ${thrownType(err)} (error ${id})`,
|
|
178
|
+
};
|
|
163
179
|
}
|
|
164
180
|
}
|
|
165
181
|
function hostReader(host, boundary, chosen) {
|
|
@@ -174,8 +190,10 @@ function hostReader(host, boundary, chosen) {
|
|
|
174
190
|
if (!host.find)
|
|
175
191
|
return matched;
|
|
176
192
|
const found = foundBy(host.find, text, boundary);
|
|
177
|
-
if (!Array.isArray(found))
|
|
178
|
-
|
|
193
|
+
if (!Array.isArray(found)) {
|
|
194
|
+
const by = `Host detector '${host.id}'`;
|
|
195
|
+
return { ...found, reason: `${by} ${found.reason}`, summary: `${by} ${found.summary}` };
|
|
196
|
+
}
|
|
179
197
|
return [...matched, ...found];
|
|
180
198
|
},
|
|
181
199
|
hit: (text, span) => ({ ...hitFromSpan(text, span, rule, 'high'), label: host.label }),
|
|
@@ -219,13 +237,13 @@ function failedHit({ rule, label }, signal) {
|
|
|
219
237
|
function detectAt(text, boundary, detect, scope = NO_SCOPE) {
|
|
220
238
|
const hits = [];
|
|
221
239
|
const redact = [];
|
|
222
|
-
const
|
|
240
|
+
const failures = [];
|
|
223
241
|
let action = 'allow';
|
|
224
242
|
for (const reader of readersAt(boundary, detect, scope)) {
|
|
225
243
|
const spans = reader.spans(text);
|
|
226
244
|
if (!Array.isArray(spans)) {
|
|
227
245
|
hits.push(failedHit(reader, spans.signal));
|
|
228
|
-
|
|
246
|
+
failures.push({ errorInternal: spans.reason, errorSummary: spans.summary });
|
|
229
247
|
action = 'block';
|
|
230
248
|
continue;
|
|
231
249
|
}
|
|
@@ -239,7 +257,7 @@ function detectAt(text, boundary, detect, scope = NO_SCOPE) {
|
|
|
239
257
|
action = stronger(action, reader.chosen);
|
|
240
258
|
}
|
|
241
259
|
if (action === 'block') {
|
|
242
|
-
return { action, hits, ...(
|
|
260
|
+
return { action, hits, ...failureText(failures) };
|
|
243
261
|
}
|
|
244
262
|
if (redact.length === 0)
|
|
245
263
|
return { action, text, hits };
|
|
@@ -292,12 +310,12 @@ function detectReads(boundaries, detect, scope) {
|
|
|
292
310
|
*/
|
|
293
311
|
function detectRelease(window, { from, to, settled }, boundary, detect, { detectors, scope }) {
|
|
294
312
|
const failed = [];
|
|
295
|
-
const
|
|
313
|
+
const failures = [];
|
|
296
314
|
const found = readersAt(boundary, detect, scope, detectors).flatMap((reader) => {
|
|
297
315
|
const spans = reader.spans(window);
|
|
298
316
|
if (!Array.isArray(spans)) {
|
|
299
317
|
failed.push(failedHit(reader, spans.signal));
|
|
300
|
-
|
|
318
|
+
failures.push({ errorInternal: spans.reason, errorSummary: spans.summary });
|
|
301
319
|
return [];
|
|
302
320
|
}
|
|
303
321
|
return spans
|
|
@@ -305,7 +323,7 @@ function detectRelease(window, { from, to, settled }, boundary, detect, { detect
|
|
|
305
323
|
.map((span) => ({ span, rule: reader.rule, label: reader.label, chosen: reader.chosen }));
|
|
306
324
|
});
|
|
307
325
|
if (failed.length > 0) {
|
|
308
|
-
return { action: 'block', hits: failed, taken: 0,
|
|
326
|
+
return { action: 'block', hits: failed, taken: 0, ...failureText(failures) };
|
|
309
327
|
}
|
|
310
328
|
const hitOf = ({ span, rule, label }) => ({
|
|
311
329
|
...hitFromSpan(window, span, rule, 'high'),
|
|
@@ -377,23 +395,23 @@ function storedHostSpans(text, detect) {
|
|
|
377
395
|
/** A reader for everything crossing `boundary` in one request. */
|
|
378
396
|
function boundaryReader(boundary, detect) {
|
|
379
397
|
const hits = [];
|
|
380
|
-
const
|
|
398
|
+
const failures = [];
|
|
381
399
|
let action = 'allow';
|
|
382
400
|
return {
|
|
383
401
|
read(text) {
|
|
384
402
|
const detected = detectAt(text, boundary, detect);
|
|
385
403
|
for (const hit of detected.hits)
|
|
386
404
|
hits.push(hit);
|
|
387
|
-
if (detected.errorInternal)
|
|
388
|
-
|
|
405
|
+
if (detected.errorInternal && detected.errorSummary) {
|
|
406
|
+
failures.push({
|
|
407
|
+
errorInternal: detected.errorInternal,
|
|
408
|
+
errorSummary: detected.errorSummary,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
389
411
|
action = stronger(action, detected.action);
|
|
390
412
|
return detected.text ?? '';
|
|
391
413
|
},
|
|
392
|
-
found: () => ({
|
|
393
|
-
action,
|
|
394
|
-
hits,
|
|
395
|
-
...(reasons.length ? { errorInternal: reasons.join('\n') } : {}),
|
|
396
|
-
}),
|
|
414
|
+
found: () => ({ action, hits, ...failureText(failures) }),
|
|
397
415
|
};
|
|
398
416
|
}
|
|
399
417
|
const TOOL_STAGE = Object.fromEntries(TOOL_BOUNDARIES.map((boundary) => [
|
|
@@ -439,6 +457,7 @@ function detectEvent(boundary, found, provenance, stage = BOUNDARY_STAGE[boundar
|
|
|
439
457
|
...(found.action === 'block' && found.errorInternal
|
|
440
458
|
? { errorInternal: found.errorInternal }
|
|
441
459
|
: {}),
|
|
460
|
+
...(found.action === 'block' && found.errorSummary ? { errorSummary: found.errorSummary } : {}),
|
|
442
461
|
};
|
|
443
462
|
}
|
|
444
463
|
export { boundaryReader, detectAt, detectEvent, detectorsAt, detectReads, detectRelease, hostAt, isScoped, leakScopeOf, redactDetectors, scopeOf, storedHostSpans, storedSpans, stronger, };
|
|
@@ -26,7 +26,7 @@ interface ReplyRead {
|
|
|
26
26
|
blocked?: GuardrailHit[];
|
|
27
27
|
}
|
|
28
28
|
/** A JSON value read at `boundary`, with each match in a string replaced, or what stops it crossing. */
|
|
29
|
-
declare function readValue(value: unknown, boundary: Boundary, detect: ResolvedDetect, scope?: DetectScope): Pick<Detection, 'action' | 'hits' | 'errorInternal'> & {
|
|
29
|
+
declare function readValue(value: unknown, boundary: Boundary, detect: ResolvedDetect, scope?: DetectScope): Pick<Detection, 'action' | 'hits' | 'errorInternal' | 'errorSummary'> & {
|
|
30
30
|
value?: unknown;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
@@ -24,7 +24,8 @@ function readValue(value, boundary, detect, scope = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
const found = detectAt(scan.text, boundary, detect, scope);
|
|
26
26
|
if (found.action !== 'redact') {
|
|
27
|
-
const
|
|
27
|
+
const { errorInternal, errorSummary } = found;
|
|
28
|
+
const why = errorInternal && errorSummary ? { errorInternal, errorSummary } : {};
|
|
28
29
|
return { action: found.action, hits: found.hits, value, ...why };
|
|
29
30
|
}
|
|
30
31
|
const redacted = mapStrings(value, (leaf) => detectAt(leaf, boundary, detect, scope).text ?? '');
|
|
@@ -18,6 +18,10 @@ declare function errorCopyText(copy: ErrorCopies, labels?: LabelOverrides): stri
|
|
|
18
18
|
declare function publicError(err: unknown, labels?: LabelOverrides): string;
|
|
19
19
|
/** Raw diagnostic for hosts, traces, and logs; never shown to end users. Each cause that says something new follows on its own line. */
|
|
20
20
|
declare function describeError(err: unknown): string;
|
|
21
|
+
/** fetch rejects only when no response came back, so any non-abort, non-timeout rejection is `network`. */
|
|
22
|
+
declare function networkError(err: unknown): unknown;
|
|
23
|
+
/** `send`, with a failed send or body read named as `networkError` names it. */
|
|
24
|
+
declare function sendOrNetworkError(send: typeof fetch, ...args: Parameters<typeof fetch>): Promise<Response>;
|
|
21
25
|
type ProducedError = TurnEventOf<'error'> & {
|
|
22
26
|
errorInternal: string;
|
|
23
27
|
};
|
|
@@ -28,4 +32,4 @@ declare function withPublicLabels(event: TurnEvent, labels?: LabelOverrides): Tu
|
|
|
28
32
|
export type { ErrorCopies, ErrorCopy, ErrorKind, TheoremErrorOptions } from './theorem-error.js';
|
|
29
33
|
export { ERROR_KINDS } from './theorem-error.js';
|
|
30
34
|
export type { ProducedError };
|
|
31
|
-
export { describeError, errorCopyText, errorKind, isAbortError, isTimeoutError, kindOfHttpStatus, publicError, TheoremError, throwIfAborted, toErrorEvent, withPublicLabels, };
|
|
35
|
+
export { describeError, errorCopyText, errorKind, isAbortError, isTimeoutError, kindOfHttpStatus, networkError, publicError, sendOrNetworkError, TheoremError, throwIfAborted, toErrorEvent, withPublicLabels, };
|
|
@@ -88,6 +88,49 @@ function describeError(err) {
|
|
|
88
88
|
}
|
|
89
89
|
return lines.join('\n');
|
|
90
90
|
}
|
|
91
|
+
/** fetch rejects only when no response came back, so any non-abort, non-timeout rejection is `network`. */
|
|
92
|
+
function networkError(err) {
|
|
93
|
+
if (err instanceof TheoremError || isAbortError(err) || isTimeoutError(err)) {
|
|
94
|
+
return err;
|
|
95
|
+
}
|
|
96
|
+
return new TheoremError('network', describeError(err), { cause: err });
|
|
97
|
+
}
|
|
98
|
+
/** `response` with a body whose read that fails is named as `networkError` names it. */
|
|
99
|
+
function bodyOrNetworkError(response) {
|
|
100
|
+
if (!response.body)
|
|
101
|
+
return response;
|
|
102
|
+
const reader = response.body.getReader();
|
|
103
|
+
const body = new ReadableStream({
|
|
104
|
+
async pull(controller) {
|
|
105
|
+
try {
|
|
106
|
+
const next = await reader.read();
|
|
107
|
+
if (next.done)
|
|
108
|
+
controller.close();
|
|
109
|
+
else
|
|
110
|
+
controller.enqueue(next.value);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
controller.error(networkError(err));
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
cancel: (reason) => reader.cancel(reason),
|
|
117
|
+
}, { highWaterMark: 0 });
|
|
118
|
+
const named = new Response(body, response);
|
|
119
|
+
Object.defineProperties(named, {
|
|
120
|
+
url: { value: response.url },
|
|
121
|
+
redirected: { value: response.redirected },
|
|
122
|
+
});
|
|
123
|
+
return named;
|
|
124
|
+
}
|
|
125
|
+
/** `send`, with a failed send or body read named as `networkError` names it. */
|
|
126
|
+
async function sendOrNetworkError(send, ...args) {
|
|
127
|
+
try {
|
|
128
|
+
return bodyOrNetworkError(await send(...args));
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
throw networkError(err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
91
134
|
/** No user wording yet: `withPublicLabels` adds it where the event reaches the host, the one place that knows the profile. */
|
|
92
135
|
function toErrorEvent(err) {
|
|
93
136
|
return {
|
|
@@ -112,4 +155,4 @@ function withPublicLabels(event, labels) {
|
|
|
112
155
|
return event;
|
|
113
156
|
}
|
|
114
157
|
export { ERROR_KINDS } from './theorem-error.js';
|
|
115
|
-
export { describeError, errorCopyText, errorKind, isAbortError, isTimeoutError, kindOfHttpStatus, publicError, TheoremError, throwIfAborted, toErrorEvent, withPublicLabels, };
|
|
158
|
+
export { describeError, errorCopyText, errorKind, isAbortError, isTimeoutError, kindOfHttpStatus, networkError, publicError, sendOrNetworkError, TheoremError, throwIfAborted, toErrorEvent, withPublicLabels, };
|
|
@@ -95,6 +95,11 @@ export interface GuardrailEvent {
|
|
|
95
95
|
provenance?: Provenance;
|
|
96
96
|
/** The block verdict's `errorInternal`: builder-only, stripped by `forClient`. */
|
|
97
97
|
errorInternal?: string;
|
|
98
|
+
/**
|
|
99
|
+
* A failed host detector's `errorInternal` in the kernel's words only, with the same error id:
|
|
100
|
+
* what the trace stores. Builder-only, stripped by `forClient`.
|
|
101
|
+
*/
|
|
102
|
+
errorSummary?: string;
|
|
98
103
|
}
|
|
99
104
|
/** The schema a guardrail event parses against. */
|
|
100
105
|
export declare const guardrailEventSchema: z.ZodType<GuardrailEvent>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TheoremError } from './error.js';
|
|
1
|
+
import { sendOrNetworkError, TheoremError } from './error.js';
|
|
2
2
|
/**
|
|
3
3
|
* IPv4 ranges refused outright:
|
|
4
4
|
* - Loopback: 127.0.0.0/8
|
|
@@ -265,7 +265,7 @@ export async function fetchGuarded(url, init, options) {
|
|
|
265
265
|
sent.set(name, value);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
|
-
const response = await fetchFn
|
|
268
|
+
const response = await sendOrNetworkError(fetchFn, target.href, {
|
|
269
269
|
...init,
|
|
270
270
|
method,
|
|
271
271
|
headers: sent,
|
|
@@ -305,7 +305,7 @@ export function dnsOverHttpsResolver(options) {
|
|
|
305
305
|
const url = new URL(options.endpoint);
|
|
306
306
|
url.searchParams.set('name', hostname);
|
|
307
307
|
url.searchParams.set('type', String(type));
|
|
308
|
-
const response = await fetchFn
|
|
308
|
+
const response = await sendOrNetworkError(fetchFn, url.href, {
|
|
309
309
|
headers: { accept: 'application/dns-json' },
|
|
310
310
|
...(signal ? { signal } : {}),
|
|
311
311
|
});
|
|
@@ -41,10 +41,11 @@ function stripCompactionInternal(event) {
|
|
|
41
41
|
return { ...event, failure };
|
|
42
42
|
}
|
|
43
43
|
function stripGuardrailInternal(event) {
|
|
44
|
-
if (event.type !== 'guardrail' ||
|
|
44
|
+
if (event.type !== 'guardrail' ||
|
|
45
|
+
!(event.guardrail?.errorInternal || event.guardrail?.errorSummary)) {
|
|
45
46
|
return event;
|
|
46
47
|
}
|
|
47
|
-
const { errorInternal: _internal, ...guardrail } = event.guardrail;
|
|
48
|
+
const { errorInternal: _internal, errorSummary: _summary, ...guardrail } = event.guardrail;
|
|
48
49
|
return { ...event, guardrail };
|
|
49
50
|
}
|
|
50
51
|
function stripEvidenceRaw(event) {
|
|
@@ -5,16 +5,26 @@ 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
|
+
readonly invalid_config: "config";
|
|
8
9
|
readonly invalid_request: "request";
|
|
10
|
+
readonly invalid_input: "input";
|
|
11
|
+
readonly not_allowed: "action";
|
|
9
12
|
readonly authentication: "auth";
|
|
10
13
|
readonly permission: "auth";
|
|
11
14
|
readonly rate_limited: "rate_limit";
|
|
15
|
+
readonly busy: "busy";
|
|
16
|
+
readonly unsupported: "unsupported";
|
|
12
17
|
readonly unavailable: "unavailable";
|
|
13
18
|
readonly network: "network";
|
|
14
19
|
readonly timeout: "timeout";
|
|
15
20
|
readonly cancelled: "cancelled";
|
|
16
21
|
readonly malformed_response: "bad_response";
|
|
22
|
+
readonly refused: "safety";
|
|
23
|
+
readonly blocked: "blocked";
|
|
17
24
|
readonly disclosure_blocked: "blocked";
|
|
25
|
+
readonly declined: "declined";
|
|
26
|
+
readonly failed: "failed";
|
|
27
|
+
readonly internal: "internal";
|
|
18
28
|
};
|
|
19
29
|
/**
|
|
20
30
|
* A normalized native-decision failure; response bodies are deliberately omitted. What the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TheoremError, throwIfAborted } from '../../guardrails/error.js';
|
|
1
|
+
import { errorKind, TheoremError, throwIfAborted } from '../../guardrails/error.js';
|
|
2
2
|
import { labelText } from '../../guardrails/labels.js';
|
|
3
3
|
import { traceJson } from '../../observability/trace-span.js';
|
|
4
4
|
import { guardedCall } from '../provider-runtime.js';
|
|
@@ -8,16 +8,47 @@ import { tracedCall } from './call-trace.js';
|
|
|
8
8
|
import { referencedAssets } from './decision-references.js';
|
|
9
9
|
/** Each decision failure code, and the error kind it reads as. */
|
|
10
10
|
const DECISION_ERROR_KINDS = {
|
|
11
|
+
invalid_config: 'config',
|
|
11
12
|
invalid_request: 'request',
|
|
13
|
+
invalid_input: 'input',
|
|
14
|
+
not_allowed: 'action',
|
|
12
15
|
authentication: 'auth',
|
|
13
16
|
permission: 'auth',
|
|
14
17
|
rate_limited: 'rate_limit',
|
|
18
|
+
busy: 'busy',
|
|
19
|
+
unsupported: 'unsupported',
|
|
15
20
|
unavailable: 'unavailable',
|
|
16
21
|
network: 'network',
|
|
17
22
|
timeout: 'timeout',
|
|
18
23
|
cancelled: 'cancelled',
|
|
19
24
|
malformed_response: 'bad_response',
|
|
25
|
+
refused: 'safety',
|
|
26
|
+
blocked: 'blocked',
|
|
20
27
|
disclosure_blocked: 'blocked',
|
|
28
|
+
declined: 'declined',
|
|
29
|
+
failed: 'failed',
|
|
30
|
+
internal: 'internal',
|
|
31
|
+
};
|
|
32
|
+
/** Each kind's code: a thrown failure keeps its kind. */
|
|
33
|
+
const DECISION_CODES = {
|
|
34
|
+
config: 'invalid_config',
|
|
35
|
+
request: 'invalid_request',
|
|
36
|
+
input: 'invalid_input',
|
|
37
|
+
action: 'not_allowed',
|
|
38
|
+
auth: 'authentication',
|
|
39
|
+
rate_limit: 'rate_limited',
|
|
40
|
+
busy: 'busy',
|
|
41
|
+
unsupported: 'unsupported',
|
|
42
|
+
unavailable: 'unavailable',
|
|
43
|
+
bad_response: 'malformed_response',
|
|
44
|
+
network: 'network',
|
|
45
|
+
timeout: 'timeout',
|
|
46
|
+
safety: 'refused',
|
|
47
|
+
blocked: 'blocked',
|
|
48
|
+
declined: 'declined',
|
|
49
|
+
failed: 'failed',
|
|
50
|
+
cancelled: 'cancelled',
|
|
51
|
+
internal: 'internal',
|
|
21
52
|
};
|
|
22
53
|
/**
|
|
23
54
|
* A normalized native-decision failure; response bodies are deliberately omitted. What the
|
|
@@ -363,25 +394,10 @@ function recordResult(root, result) {
|
|
|
363
394
|
function decisionProviderError(error) {
|
|
364
395
|
if (error instanceof DecisionError)
|
|
365
396
|
return error;
|
|
366
|
-
const kind = error
|
|
367
|
-
? error.kind
|
|
368
|
-
: error instanceof TypeError
|
|
369
|
-
? 'network'
|
|
370
|
-
: 'bad_response';
|
|
371
|
-
const codes = {
|
|
372
|
-
auth: 'authentication',
|
|
373
|
-
rate_limit: 'rate_limited',
|
|
374
|
-
network: 'network',
|
|
375
|
-
busy: 'unavailable',
|
|
376
|
-
unavailable: 'unavailable',
|
|
377
|
-
timeout: 'timeout',
|
|
378
|
-
cancelled: 'cancelled',
|
|
379
|
-
request: 'invalid_request',
|
|
380
|
-
unsupported: 'invalid_request',
|
|
381
|
-
};
|
|
397
|
+
const kind = errorKind(error);
|
|
382
398
|
const code = kind === 'auth' && isRecord(error) && error.status === 403
|
|
383
399
|
? 'permission'
|
|
384
|
-
:
|
|
400
|
+
: DECISION_CODES[kind];
|
|
385
401
|
return new DecisionError(code,
|
|
386
402
|
// why: A framework failure's message holds no response body, and says what stopped the decision.
|
|
387
403
|
error instanceof TheoremError ? error.message : labelText('provider.decision_failed'), isRecord(error) && typeof error.status === 'number' ? error.status : undefined, { cause: error });
|
|
@@ -278,6 +278,7 @@ function guardrailAttributes(guardrail) {
|
|
|
278
278
|
hits: guardrail.hits.map((hit) => ({
|
|
279
279
|
rule: hit.rule,
|
|
280
280
|
severity: hit.severity,
|
|
281
|
+
...optional('signal', hit.signal),
|
|
281
282
|
...(hit.span ? { start: hit.span.start, end: hit.span.end } : {}),
|
|
282
283
|
...optional('match', hit.match),
|
|
283
284
|
...optional('label', hit.label),
|
|
@@ -285,6 +286,7 @@ function guardrailAttributes(guardrail) {
|
|
|
285
286
|
})),
|
|
286
287
|
...(guardrail.provenance ? { provenance: { ...guardrail.provenance } } : {}),
|
|
287
288
|
...(guardrail.errorInternal ? { error: traceContent(guardrail.errorInternal) } : {}),
|
|
289
|
+
...optional('error_summary', guardrail.errorSummary),
|
|
288
290
|
};
|
|
289
291
|
}
|
|
290
292
|
/** What a stream check looked at, for its pass record. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TheoremError, throwIfAborted } from '../../guardrails/error.js';
|
|
2
2
|
import { labelText } from '../../guardrails/labels.js';
|
|
3
3
|
import { jsonObjectSchema } from '../json.js';
|
|
4
|
-
import {
|
|
4
|
+
import { operationBroken, operationWireValue, toolInvocationHash, toolOperationOutcomeSchema, } from './operation.js';
|
|
5
5
|
class ReplayedOperation {
|
|
6
6
|
outcome;
|
|
7
7
|
constructor(outcome) {
|
|
@@ -41,7 +41,7 @@ class CoordinatedExecution {
|
|
|
41
41
|
if (result.kind === 'running' || result.kind === 'uncertain')
|
|
42
42
|
throw uncertainError();
|
|
43
43
|
if (result.kind !== 'acquired' || typeof result.claimToken !== 'string' || !result.claimToken)
|
|
44
|
-
throw
|
|
44
|
+
throw operationBroken("The tool coordinator's begin returned no claim"); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
45
45
|
this.claim = { ...operation, claimToken: result.claimToken };
|
|
46
46
|
throwIfAborted(this.signal);
|
|
47
47
|
return this.claim;
|
|
@@ -64,7 +64,7 @@ class CoordinatedExecution {
|
|
|
64
64
|
return undefined;
|
|
65
65
|
}
|
|
66
66
|
if (!this.terminal)
|
|
67
|
-
throw
|
|
67
|
+
throw operationBroken('The tool run ended with no terminal event'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
68
68
|
const { modelResult, outputRaw, failure, awaiting, denied, callNotStarted, aborted, pendingInject, } = settlement;
|
|
69
69
|
const effects = this.claim ? (failure ? 'unknown' : 'completed') : 'not_started';
|
|
70
70
|
const outcome = {
|
|
@@ -96,7 +96,7 @@ class CoordinatedExecution {
|
|
|
96
96
|
if (saved.event.tool.operationId !== operationId ||
|
|
97
97
|
saved.event.tool.callId !== callId ||
|
|
98
98
|
saved.event.tool.name !== toolId)
|
|
99
|
-
throw
|
|
99
|
+
throw operationBroken("The tool coordinator's saved outcome is another operation's"); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
100
100
|
this.committed = true;
|
|
101
101
|
return saved;
|
|
102
102
|
}
|
|
@@ -122,7 +122,7 @@ export async function* coordinateTool(coordinator, identity, invocation, signal,
|
|
|
122
122
|
throw uncertainError();
|
|
123
123
|
if (existing?.kind === 'settled') {
|
|
124
124
|
if (!authorizeReplay)
|
|
125
|
-
throw
|
|
125
|
+
throw operationBroken('A settled tool operation has no replay check'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
126
126
|
const saved = execution.replay(new ReplayedOperation(existing.outcome));
|
|
127
127
|
await authorizeReplay(saved);
|
|
128
128
|
throwIfAborted(signal);
|
|
@@ -146,7 +146,7 @@ export async function* coordinateTool(coordinator, identity, invocation, signal,
|
|
|
146
146
|
catch (error) {
|
|
147
147
|
if (error instanceof ReplayedOperation) {
|
|
148
148
|
if (!authorizeReplay)
|
|
149
|
-
throw
|
|
149
|
+
throw operationBroken('A settled tool operation has no replay check'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
150
150
|
const saved = execution.replay(error);
|
|
151
151
|
await authorizeReplay(saved);
|
|
152
152
|
yield saved.event;
|
|
@@ -16,7 +16,7 @@ import { dispatchToolBody, failedActivity, failedActivityOf, failureEvent, messa
|
|
|
16
16
|
import { HandlerEvents } from './handler-events.js';
|
|
17
17
|
import { fetchToolImages } from './image-fetch.js';
|
|
18
18
|
import { formatToolFailureForModel, formatToolResult, frameToolFailure } from './model-text.js';
|
|
19
|
-
import {
|
|
19
|
+
import { operationBroken, operationWireValue } from './operation.js';
|
|
20
20
|
import { checkPermission, gateDetails, isGateResumeDenied, isGateResumeGranted, isResumeContinuation, } from './permission.js';
|
|
21
21
|
import { executeHttpTool, executeMcpTool, failureWithoutSecret, modelResultFromOutput, omitSecret, outcomeFromUnauth, parseToolOutput, refusedCredentialOutcome, resolveToolAuth, withMedia, } from './remote.js';
|
|
22
22
|
import { extractLoadedIds, profileToolAllow, profileToolsSpec, promoteLoadedTools, } from './resolve.js';
|
|
@@ -467,13 +467,14 @@ async function* receivePageAnswer(tool, ctx, claim, base) {
|
|
|
467
467
|
const execution = ctx.pageExecution;
|
|
468
468
|
if (tool.answeredBy !== 'page' || !execution)
|
|
469
469
|
return ctx;
|
|
470
|
-
if (!claim?.invocation)
|
|
471
|
-
throw
|
|
470
|
+
if (!claim?.invocation) {
|
|
471
|
+
throw new TheoremError('config', `Page tool '${tool.name}' needs a tool execution coordinator (execution)`);
|
|
472
|
+
}
|
|
472
473
|
const argumentsForPage = jsonObjectSchema.parse(operationWireValue(claim.invocation.preparedInput));
|
|
473
474
|
const dispatch = await execution.dispatch(claim, ctx.signal);
|
|
474
475
|
throwIfAborted(ctx.signal);
|
|
475
476
|
if (!dispatch.grantId)
|
|
476
|
-
throw
|
|
477
|
+
throw operationBroken('The page dispatch returned no grantId'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
477
478
|
yield toolEvent(base, {
|
|
478
479
|
phase: 'dispatch',
|
|
479
480
|
grantId: dispatch.grantId,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TheoremError } from '../../guardrails/error.js';
|
|
2
|
+
import { operationBroken, } from './operation.js';
|
|
2
3
|
/** How many operations a memory coordinator tracks before it refuses new ones. */
|
|
3
4
|
const DEFAULT_MEMORY_EXECUTION_LIMIT = 256;
|
|
4
5
|
/**
|
|
@@ -23,6 +24,8 @@ export function createMemoryToolExecution(options = {}) {
|
|
|
23
24
|
const record = records.get(keyOf(claim));
|
|
24
25
|
return record?.state === 'running' && record.claimToken === claim.claimToken;
|
|
25
26
|
};
|
|
27
|
+
const full = () => new TheoremError('config', `The memory tool coordinator holds its maxOperations of ${limit}`);
|
|
28
|
+
const notHeld = () => operationBroken('The memory tool coordinator holds no running claim for this token'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
26
29
|
return {
|
|
27
30
|
lookup: (operation) => Promise.resolve(known(operation)),
|
|
28
31
|
begin: (operation) => {
|
|
@@ -30,28 +33,29 @@ export function createMemoryToolExecution(options = {}) {
|
|
|
30
33
|
if (earlier)
|
|
31
34
|
return Promise.resolve(earlier);
|
|
32
35
|
if (records.size >= limit)
|
|
33
|
-
return Promise.reject(
|
|
36
|
+
return Promise.reject(full());
|
|
34
37
|
const claimToken = crypto.randomUUID();
|
|
35
38
|
records.set(keyOf(operation), { state: 'running', claimToken });
|
|
36
39
|
return Promise.resolve({ kind: 'acquired', claimToken });
|
|
37
40
|
},
|
|
38
41
|
settle: (claim, outcome) => {
|
|
39
42
|
if (!holds(claim))
|
|
40
|
-
return Promise.reject(
|
|
43
|
+
return Promise.reject(notHeld());
|
|
41
44
|
records.set(keyOf(claim), { state: 'settled', outcome });
|
|
42
45
|
return Promise.resolve();
|
|
43
46
|
},
|
|
44
47
|
settleNotStarted: (operation, outcome) => {
|
|
45
|
-
if (records.has(keyOf(operation)))
|
|
46
|
-
return Promise.reject(
|
|
48
|
+
if (records.has(keyOf(operation))) {
|
|
49
|
+
return Promise.reject(operationBroken('The memory tool coordinator already has a record for this operation'));
|
|
50
|
+
}
|
|
47
51
|
if (records.size >= limit)
|
|
48
|
-
return Promise.reject(
|
|
52
|
+
return Promise.reject(full());
|
|
49
53
|
records.set(keyOf(operation), { state: 'settled', outcome });
|
|
50
54
|
return Promise.resolve();
|
|
51
55
|
},
|
|
52
56
|
markUncertain: (claim) => {
|
|
53
57
|
if (!holds(claim))
|
|
54
|
-
return Promise.reject(
|
|
58
|
+
return Promise.reject(notHeld());
|
|
55
59
|
records.set(keyOf(claim), { state: 'uncertain' });
|
|
56
60
|
return Promise.resolve();
|
|
57
61
|
},
|
|
@@ -67,7 +67,8 @@ export interface ToolExecutionCoordinator {
|
|
|
67
67
|
settleNotStarted(operation: ToolOperationIdentity, outcome: ToolOperationOutcome): Promise<void>;
|
|
68
68
|
markUncertain(claim: ToolOperationClaim, reason: ToolOperationUncertainty): Promise<void>;
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
/** A coordinator, a page dispatch or the kernel broke the tool-operation contract. */
|
|
71
|
+
export declare function operationBroken(reason: string): TheoremError;
|
|
71
72
|
export declare function toolInvocationHash(value: unknown): Promise<string>;
|
|
72
73
|
export declare const toolOperationOutcomeSchema: z.ZodType<ToolOperationOutcome>;
|
|
73
74
|
export declare function operationWireValue(value: unknown): JsonValue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { TheoremError } from '../../guardrails/error.js';
|
|
3
3
|
import { errorKindSchema, guardrailHit, provenance } from '../../guardrails/event-schemas.js';
|
|
4
4
|
import { jsonObjectSchema, jsonValueSchema } from '../json.js';
|
|
5
5
|
import { interactionPartSchema, TURN_EVENT_SCHEMAS, turnHistoryMessageSchema, } from '../turn-events.js';
|
|
@@ -7,10 +7,9 @@ export const savedToolInvocationSchema = z.strictObject({
|
|
|
7
7
|
accepted: jsonObjectSchema,
|
|
8
8
|
preparedInput: jsonValueSchema,
|
|
9
9
|
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return error;
|
|
10
|
+
/** A coordinator, a page dispatch or the kernel broke the tool-operation contract. */
|
|
11
|
+
export function operationBroken(reason) {
|
|
12
|
+
return new TheoremError('internal', reason);
|
|
14
13
|
}
|
|
15
14
|
export async function toolInvocationHash(value) {
|
|
16
15
|
const ordered = (item) => {
|
|
@@ -78,20 +77,23 @@ export function operationWireValue(value) {
|
|
|
78
77
|
if (!item || typeof item !== 'object')
|
|
79
78
|
return item;
|
|
80
79
|
if (ancestors.has(item))
|
|
81
|
-
throw
|
|
80
|
+
throw operationBroken('Tool operation value has a cycle'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
82
81
|
const prototype = Object.getPrototypeOf(item);
|
|
83
82
|
if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null) {
|
|
84
|
-
throw
|
|
83
|
+
throw operationBroken('Tool operation value holds an object that is not plain data'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
84
|
+
}
|
|
85
|
+
if (Object.hasOwn(item, '__proto__')) {
|
|
86
|
+
throw operationBroken('Tool operation value has an own __proto__ key'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
85
87
|
}
|
|
86
|
-
if (Object.hasOwn(item, '__proto__'))
|
|
87
|
-
throw operationUnavailable();
|
|
88
88
|
ancestors.add(item);
|
|
89
89
|
try {
|
|
90
90
|
if (Array.isArray(item))
|
|
91
91
|
return item.map(normalize);
|
|
92
92
|
const entries = Object.entries(Object.getOwnPropertyDescriptors(item));
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
const accessor = entries.find(([, descriptor]) => descriptor.get || descriptor.set);
|
|
94
|
+
if (accessor) {
|
|
95
|
+
throw operationBroken(`Tool operation value has a getter or setter at '${accessor[0]}'`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
96
|
+
}
|
|
95
97
|
return Object.fromEntries(entries
|
|
96
98
|
.filter(([, descriptor]) => descriptor.enumerable && descriptor.value !== undefined)
|
|
97
99
|
.map(([key, descriptor]) => [key, normalize(descriptor.value)]));
|
|
@@ -382,13 +382,20 @@ async function* remoteAuthAndPreBody(args) {
|
|
|
382
382
|
...(authRes.audience ? { audience: authRes.audience } : {}),
|
|
383
383
|
};
|
|
384
384
|
}
|
|
385
|
+
/** A thrown request's code: a kind with a code of its own, else `request_failed`. */
|
|
386
|
+
const THROWN_CODES = {
|
|
387
|
+
network: 'network_error',
|
|
388
|
+
cancelled: 'cancelled',
|
|
389
|
+
timeout: 'timeout',
|
|
390
|
+
};
|
|
385
391
|
function* thrownOutcome(err, checks) {
|
|
386
392
|
if (errorKind(err) === 'blocked') {
|
|
387
393
|
const blocked = networkBlockedEvent();
|
|
388
394
|
checks.record(blocked);
|
|
389
395
|
return failureOutcome(yield* networkBlocked(err, blocked), false);
|
|
390
396
|
}
|
|
391
|
-
|
|
397
|
+
const kind = errorKind(err);
|
|
398
|
+
return failureOutcome({ code: THROWN_CODES[kind] ?? 'request_failed', kind, message: messageOf(err) }, false);
|
|
392
399
|
}
|
|
393
400
|
const OMIT_CREDENTIAL = '[omitted - credential]';
|
|
394
401
|
/** The most of an error or unparseable body quoted back in a failure message. */
|
|
@@ -646,6 +646,7 @@ const HITS = fields('error', 'Hits', 'Each rule that matched.', {
|
|
|
646
646
|
open: true,
|
|
647
647
|
},
|
|
648
648
|
severity: attr('error', 'Severity', 'text', 'How serious the rule says a match is.', SEVERITY_OPTIONS),
|
|
649
|
+
signal: attr('error', 'Signal', 'text', "What made a `tool_instructions` match one, or how a host detector's `find` failed: `find_threw` or `find_result`."),
|
|
649
650
|
start: attr('error', 'From', 'number', 'Where the match starts in the checked text.'),
|
|
650
651
|
end: attr('error', 'To', 'number', 'Where the match ends in the checked text.'),
|
|
651
652
|
match: attr('error', 'Match', 'text', 'The matched text, exact. Kept only when the profile records match previews.'),
|
|
@@ -680,6 +681,7 @@ const TRACE_EVENTS = {
|
|
|
680
681
|
depth: attr('tool', 'Hops', 'number', "Tool calls between the user's turn and this text: 1 for a direct call, more through another agent."),
|
|
681
682
|
}),
|
|
682
683
|
error: attr('error', 'Detail', 'content', "The guardrail's internal reason."),
|
|
684
|
+
error_summary: attr('error', 'Failure', 'text', "Which host detector failed, how, and its error id, in the kernel's words only. The host stream's reason ends with the same id."),
|
|
683
685
|
},
|
|
684
686
|
},
|
|
685
687
|
'theorem.attempt.retry': {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { TheoremError } from '../../guardrails/error.js';
|
|
2
|
-
import { networkError } from './upstream-tap.js';
|
|
1
|
+
import { networkError, TheoremError } from '../../guardrails/error.js';
|
|
3
2
|
const BACKOFF_MS = [1000, 2000, 4000, 8000];
|
|
4
3
|
/** All the waiting one call may do between tries; a wait that would pass it sends the last answer back. */
|
|
5
4
|
export const WAIT_BUDGET_MS = 5000;
|
|
@@ -3,6 +3,4 @@ export declare function tapeHeaderValue(key: string, value: string): string;
|
|
|
3
3
|
export declare function tapeHeaders(headers?: HeadersInit): Record<string, string>;
|
|
4
4
|
export declare function throwRow(err: unknown): Record<string, unknown>;
|
|
5
5
|
export declare function tapFetch(tap: ModelRequest['tapUpstream'], send?: typeof fetch, keySlot?: KeySlot): typeof fetch;
|
|
6
|
-
/** fetch rejects only when no response came back, so any non-abort, non-timeout rejection is `network`. */
|
|
7
|
-
export declare function networkError(err: unknown): unknown;
|
|
8
6
|
export declare function networkFetch(send?: typeof fetch): typeof fetch;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sendOrNetworkError } from '../../guardrails/error.js';
|
|
2
2
|
const SECRET_HEADER = /key|auth|cookie|secret|token/i;
|
|
3
3
|
export function tapeHeaderValue(key, value) {
|
|
4
4
|
if (SECRET_HEADER.test(key)) {
|
|
@@ -66,20 +66,6 @@ export function tapFetch(tap, send = fetch, keySlot) {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
/** fetch rejects only when no response came back, so any non-abort, non-timeout rejection is `network`. */
|
|
70
|
-
export function networkError(err) {
|
|
71
|
-
if (err instanceof TheoremError || isAbortError(err) || isTimeoutError(err)) {
|
|
72
|
-
return err;
|
|
73
|
-
}
|
|
74
|
-
return new TheoremError('network', describeError(err), { cause: err });
|
|
75
|
-
}
|
|
76
69
|
export function networkFetch(send = fetch) {
|
|
77
|
-
return
|
|
78
|
-
try {
|
|
79
|
-
return await send(url, init);
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
throw networkError(err);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
70
|
+
return (url, init) => sendOrNetworkError(send, url, init);
|
|
85
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/agents",
|
|
3
|
-
"version": "0.3.4-next.
|
|
3
|
+
"version": "0.3.4-next.528.g44c76f9",
|
|
4
4
|
"description": "A flat TypeScript agent kernel for typed profiles, deterministic turn execution, registered tools, provider adapters, guardrails, and host-injected traces.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|