@framers/agentos 0.10.25 → 0.10.27
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/dist/cognition/emergent/SandboxedToolForge.d.ts +30 -1
- package/dist/cognition/emergent/SandboxedToolForge.d.ts.map +1 -1
- package/dist/cognition/emergent/SandboxedToolForge.js +87 -6
- package/dist/cognition/emergent/SandboxedToolForge.js.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.js +2 -2
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts +40 -0
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js +181 -8
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js.map +1 -1
- package/dist/core/llm/providers/openai-cache-params.js +2 -2
- package/dist/core/llm/providers/openai-cache-params.js.map +1 -1
- package/dist/io/channels/telephony/providers/plivo.d.ts +81 -24
- package/dist/io/channels/telephony/providers/plivo.d.ts.map +1 -1
- package/dist/io/channels/telephony/providers/plivo.js +643 -52
- package/dist/io/channels/telephony/providers/plivo.js.map +1 -1
- package/dist/io/channels/telephony/providers/telnyx.d.ts +22 -8
- package/dist/io/channels/telephony/providers/telnyx.d.ts.map +1 -1
- package/dist/io/channels/telephony/providers/telnyx.js +45 -12
- package/dist/io/channels/telephony/providers/telnyx.js.map +1 -1
- package/dist/io/channels/telephony/providers/twilio.d.ts +12 -4
- package/dist/io/channels/telephony/providers/twilio.d.ts.map +1 -1
- package/dist/io/channels/telephony/providers/twilio.js +115 -19
- package/dist/io/channels/telephony/providers/twilio.js.map +1 -1
- package/package.json +1 -1
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
*
|
|
29
29
|
* Allowlisted APIs (each requires explicit opt-in via {@link SandboxAPI}):
|
|
30
30
|
* - `fetch` — HTTP requests, domain-restricted via {@link SandboxedToolForgeConfig.fetchDomainAllowlist}.
|
|
31
|
-
* - `fs.readFile` — Read-only file access,
|
|
31
|
+
* - `fs.readFile` — Read-only file access, max 1 MB, restricted to the
|
|
32
|
+
* configured roots after symlink resolution (a link inside a root cannot
|
|
33
|
+
* be used to reach a file outside one).
|
|
32
34
|
* - `crypto` — Hashing and HMAC only (`createHash`, `createHmac`).
|
|
33
35
|
*/
|
|
34
36
|
import type { SandboxExecutionRequest, SandboxExecutionResult, SandboxAPI } from './types.js';
|
|
@@ -101,6 +103,20 @@ export declare class SandboxedToolForge {
|
|
|
101
103
|
private readonly fetchDomainAllowlist;
|
|
102
104
|
/** Filesystem roots sandboxed reads may access. */
|
|
103
105
|
private readonly fsReadRoots;
|
|
106
|
+
/**
|
|
107
|
+
* Real path of each configured root, resolved lazily and cached PER ROOT.
|
|
108
|
+
*
|
|
109
|
+
* Both sides of a containment check have to be real paths: a configured
|
|
110
|
+
* root is frequently itself a link (on macOS `/tmp` is a link to
|
|
111
|
+
* `/private/tmp`), so comparing a resolved file against an unresolved root
|
|
112
|
+
* would deny perfectly legitimate reads.
|
|
113
|
+
*
|
|
114
|
+
* Per root, not per set: a set-wide cache that is dropped whenever ANY
|
|
115
|
+
* root fails to resolve would re-resolve the roots that DID succeed, and a
|
|
116
|
+
* root symlink repointed in between would then be followed to its new
|
|
117
|
+
* target — losing the pinning this cache exists to provide.
|
|
118
|
+
*/
|
|
119
|
+
private readonly realFsReadRootCache;
|
|
104
120
|
/**
|
|
105
121
|
* Hardened node:vm sandbox shared across all execute() calls. Owns the
|
|
106
122
|
* codeGeneration restriction, frozen console, and explicit-undefined
|
|
@@ -149,6 +165,19 @@ export declare class SandboxedToolForge {
|
|
|
149
165
|
* the raw message in that case. The hints map to the 4 most common
|
|
150
166
|
* LLM forge mistakes observed in production.
|
|
151
167
|
*/
|
|
168
|
+
private resolveReadRoots;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve one configured root, pinning the first success.
|
|
171
|
+
*
|
|
172
|
+
* A resolved root is pinned for the life of the forge, deliberately:
|
|
173
|
+
* re-resolving it per read would let a root symlink be repointed
|
|
174
|
+
* underneath a running sandbox, which is the move this containment check
|
|
175
|
+
* exists to stop. A caller that needs to follow a retargeted root builds a
|
|
176
|
+
* new forge. A FAILED resolution is not pinned, so a root that becomes
|
|
177
|
+
* readable later is picked up — and because each root is cached on its
|
|
178
|
+
* own, one unreadable root can never un-pin a sibling that resolved.
|
|
179
|
+
*/
|
|
180
|
+
private resolveReadRoot;
|
|
152
181
|
private describeSyntaxError;
|
|
153
182
|
validateCode(code: string, allowlist: SandboxAPI[]): {
|
|
154
183
|
valid: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxedToolForge.d.ts","sourceRoot":"","sources":["../../../src/cognition/emergent/SandboxedToolForge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SandboxedToolForge.d.ts","sourceRoot":"","sources":["../../../src/cognition/emergent/SandboxedToolForge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAKH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAe9F;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AA+BD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,kBAAkB;IAC7B,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAW;IAEhD,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IAEvC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsC;IAE1E;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAE1C;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,wBAAwB;IAY7C;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,mBAAmB;IAiC3B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE;IAsC7F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA4HhF;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;CAkG1B"}
|
|
@@ -28,11 +28,13 @@
|
|
|
28
28
|
*
|
|
29
29
|
* Allowlisted APIs (each requires explicit opt-in via {@link SandboxAPI}):
|
|
30
30
|
* - `fetch` — HTTP requests, domain-restricted via {@link SandboxedToolForgeConfig.fetchDomainAllowlist}.
|
|
31
|
-
* - `fs.readFile` — Read-only file access,
|
|
31
|
+
* - `fs.readFile` — Read-only file access, max 1 MB, restricted to the
|
|
32
|
+
* configured roots after symlink resolution (a link inside a root cannot
|
|
33
|
+
* be used to reach a file outside one).
|
|
32
34
|
* - `crypto` — Hashing and HMAC only (`createHash`, `createHmac`).
|
|
33
35
|
*/
|
|
34
36
|
import { createHash, createHmac, randomUUID } from 'node:crypto';
|
|
35
|
-
import { readFile } from 'node:fs/promises';
|
|
37
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
36
38
|
import * as path from 'node:path';
|
|
37
39
|
import { CodeSandbox } from '../../safety/sandbox/executor/CodeSandbox.js';
|
|
38
40
|
/**
|
|
@@ -104,6 +106,20 @@ export class SandboxedToolForge {
|
|
|
104
106
|
* defaults (128 MB memory, 5000 ms timeout, no domain restrictions).
|
|
105
107
|
*/
|
|
106
108
|
constructor(config) {
|
|
109
|
+
/**
|
|
110
|
+
* Real path of each configured root, resolved lazily and cached PER ROOT.
|
|
111
|
+
*
|
|
112
|
+
* Both sides of a containment check have to be real paths: a configured
|
|
113
|
+
* root is frequently itself a link (on macOS `/tmp` is a link to
|
|
114
|
+
* `/private/tmp`), so comparing a resolved file against an unresolved root
|
|
115
|
+
* would deny perfectly legitimate reads.
|
|
116
|
+
*
|
|
117
|
+
* Per root, not per set: a set-wide cache that is dropped whenever ANY
|
|
118
|
+
* root fails to resolve would re-resolve the roots that DID succeed, and a
|
|
119
|
+
* root symlink repointed in between would then be followed to its new
|
|
120
|
+
* target — losing the pinning this cache exists to provide.
|
|
121
|
+
*/
|
|
122
|
+
this.realFsReadRootCache = new Map();
|
|
107
123
|
this.memoryMB = config?.memoryMB ?? 128;
|
|
108
124
|
this.timeoutMs = config?.timeoutMs ?? 5000;
|
|
109
125
|
this.fetchDomainAllowlist = (config?.fetchDomainAllowlist ?? []).map((d) => d.toLowerCase());
|
|
@@ -148,6 +164,34 @@ export class SandboxedToolForge {
|
|
|
148
164
|
* the raw message in that case. The hints map to the 4 most common
|
|
149
165
|
* LLM forge mistakes observed in production.
|
|
150
166
|
*/
|
|
167
|
+
resolveReadRoots() {
|
|
168
|
+
return Promise.all(this.fsReadRoots.map((root) => this.resolveReadRoot(root)));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Resolve one configured root, pinning the first success.
|
|
172
|
+
*
|
|
173
|
+
* A resolved root is pinned for the life of the forge, deliberately:
|
|
174
|
+
* re-resolving it per read would let a root symlink be repointed
|
|
175
|
+
* underneath a running sandbox, which is the move this containment check
|
|
176
|
+
* exists to stop. A caller that needs to follow a retargeted root builds a
|
|
177
|
+
* new forge. A FAILED resolution is not pinned, so a root that becomes
|
|
178
|
+
* readable later is picked up — and because each root is cached on its
|
|
179
|
+
* own, one unreadable root can never un-pin a sibling that resolved.
|
|
180
|
+
*/
|
|
181
|
+
resolveReadRoot(root) {
|
|
182
|
+
const cached = this.realFsReadRootCache.get(root);
|
|
183
|
+
if (cached !== undefined)
|
|
184
|
+
return cached;
|
|
185
|
+
const pending = realpath(root).catch(() => {
|
|
186
|
+
// Drop the failure so the next read retries this root. Its lexical
|
|
187
|
+
// form never matches a real path, so an unresolvable root cannot
|
|
188
|
+
// widen the sandbox while it stays unreadable.
|
|
189
|
+
this.realFsReadRootCache.delete(root);
|
|
190
|
+
return root;
|
|
191
|
+
});
|
|
192
|
+
this.realFsReadRootCache.set(root, pending);
|
|
193
|
+
return pending;
|
|
194
|
+
}
|
|
151
195
|
describeSyntaxError(message) {
|
|
152
196
|
const m = message || '';
|
|
153
197
|
if (/Unexpected token 'const'/.test(m) || /Unexpected token 'let'/.test(m)) {
|
|
@@ -376,13 +420,50 @@ export class SandboxedToolForge {
|
|
|
376
420
|
extras.fs = {
|
|
377
421
|
readFile: async (filePath) => {
|
|
378
422
|
const resolvedPath = path.resolve(filePath);
|
|
379
|
-
|
|
380
|
-
|
|
423
|
+
// Containment via path.relative, not a string prefix: a root that
|
|
424
|
+
// already ends in a separator (the filesystem root `/`, a Windows
|
|
425
|
+
// drive root `C:\`) would otherwise be compared against a doubled
|
|
426
|
+
// separator and deny every file beneath it. path.relative also
|
|
427
|
+
// applies the platform's own case rules.
|
|
428
|
+
const withinRoots = (candidate, roots) => roots.some((root) => {
|
|
429
|
+
const relative = path.relative(root, candidate);
|
|
430
|
+
// '' means the candidate IS the root. A relative path that
|
|
431
|
+
// climbs out ('..' or '../x') or stays absolute (a different
|
|
432
|
+
// Windows drive) is outside it. Checking for the '..' segment
|
|
433
|
+
// rather than the '..' prefix keeps a sibling named '..foo'
|
|
434
|
+
// from reading as an escape.
|
|
435
|
+
return (relative === '' ||
|
|
436
|
+
(relative !== '..' &&
|
|
437
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
438
|
+
!path.isAbsolute(relative)));
|
|
381
439
|
});
|
|
382
|
-
|
|
440
|
+
// Lexical pass: rejects the obvious `../../etc/passwd` shape before
|
|
441
|
+
// the sandbox pays for any filesystem call.
|
|
442
|
+
if (!withinRoots(resolvedPath, this.fsReadRoots)) {
|
|
383
443
|
throw new Error(`fs.readFile blocked: path "${resolvedPath}" is outside the allowed roots`);
|
|
384
444
|
}
|
|
385
|
-
|
|
445
|
+
// Lexical containment is NOT containment. A symlink sitting inside
|
|
446
|
+
// an allowed root resolves to an in-root string while pointing at
|
|
447
|
+
// any file on the machine, so string-prefix checking alone reads
|
|
448
|
+
// whatever the link targets (CWE-22). Follow the link chain and
|
|
449
|
+
// re-check against the roots' own real paths.
|
|
450
|
+
//
|
|
451
|
+
// The path has already cleared the lexical check, so a resolution
|
|
452
|
+
// failure here names something inside an allowed root: surface the
|
|
453
|
+
// filesystem's own error (ENOENT and friends) rather than masking
|
|
454
|
+
// a missing file as a containment failure.
|
|
455
|
+
const realPath = await realpath(resolvedPath);
|
|
456
|
+
if (!withinRoots(realPath, await this.resolveReadRoots())) {
|
|
457
|
+
throw new Error(`fs.readFile blocked: path "${resolvedPath}" resolves outside the allowed roots`);
|
|
458
|
+
}
|
|
459
|
+
// Read the RESOLVED path, not the caller's string: re-resolving the
|
|
460
|
+
// original would walk the link a second time, and the link can be
|
|
461
|
+
// repointed between the two walks. (A component of the resolved
|
|
462
|
+
// path could still be swapped for a link in that window — closing
|
|
463
|
+
// that needs an O_NOFOLLOW handle, which node's fs/promises does
|
|
464
|
+
// not expose; it requires local write access inside an allowed
|
|
465
|
+
// root, which is already outside this sandbox's threat model.)
|
|
466
|
+
const data = await readFile(realPath);
|
|
386
467
|
if (data.byteLength > 1048576) {
|
|
387
468
|
throw new Error(`fs.readFile blocked: file exceeds 1 MB limit (${data.byteLength} bytes)`);
|
|
388
469
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxedToolForge.js","sourceRoot":"","sources":["../../../src/cognition/emergent/SandboxedToolForge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SandboxedToolForge.js","sourceRoot":"","sources":["../../../src/cognition/emergent/SandboxedToolForge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,8CAA8C,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,sCAAsC,CAAC;AA0CnE,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,aAAa,GAAoC;IACrD,CAAC,aAAa,EAAE,qBAAqB,CAAC;IACtC,CAAC,iBAAiB,EAAE,yBAAyB,CAAC;IAC9C,CAAC,uBAAuB,EAAE,6BAA6B,CAAC;IACxD,CAAC,gBAAgB,EAAE,wBAAwB,CAAC;IAC5C,CAAC,aAAa,EAAE,iCAAiC,CAAC;IAClD,CAAC,eAAe,EAAE,+BAA+B,CAAC;IAClD,CAAC,gBAAgB,EAAE,6BAA6B,CAAC;IACjD,CAAC,mBAAmB,EAAE,mCAAmC,CAAC;IAC1D,CAAC,mBAAmB,EAAE,wBAAwB,CAAC;IAC/C,CAAC,oBAAoB,EAAE,wBAAwB,CAAC;IAChD,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAC1C,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;IAC9C,CAAC,wBAAwB,EAAE,4BAA4B,CAAC;IACxD,CAAC,sBAAsB,EAAE,0BAA0B,CAAC;CACrD,CAAC;AAEF,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,kBAAkB;IAmC7B;;;;;OAKG;IACH,YAAY,MAAiC;QA5B7C;;;;;;;;;;;;WAYG;QACc,wBAAmB,GAAG,IAAI,GAAG,EAA2B,CAAC;QAgBxE,IAAI,CAAC,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,GAAG,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,IAAI,CAAC;QAC3C,IAAI,CAAC,oBAAoB,GAAG,CAAC,MAAM,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7F,IAAI,CAAC,WAAW,GAAG,CAAC,MAAM,EAAE,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9F,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,6EAA6E;IAC7E,uBAAuB;IACvB,6EAA6E;IAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH;;;;;;OAMG;IACK,gBAAgB;QACtB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjF,CAAC;IAED;;;;;;;;;;OAUG;IACK,eAAe,CAAC,IAAY;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QAExC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACxC,mEAAmE;YACnE,iEAAiE;YACjE,+CAA+C;YAC/C,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,mBAAmB,CAAC,OAAe;QACzC,MAAM,CAAC,GAAG,OAAO,IAAI,EAAE,CAAC;QACxB,IAAI,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,OAAO,CACL,uEAAuE;gBACvE,sGAAsG;gBACtG,gFAAgF;gBAChF,8HAA8H,CAC/H,CAAC;QACJ,CAAC;QACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,OAAO,CACL,oFAAoF;gBACpF,kHAAkH,CACnH,CAAC;QACJ,CAAC;QACD,IAAI,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,CACL,mIAAmI,CACpI,CAAC;QACJ,CAAC;QACD,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,OAAO,CACL,+DAA+D;gBAC/D,wFAAwF,CACzF,CAAC;QACJ,CAAC;QACD,IAAI,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,OAAO,oFAAoF,CAAC;QAC9F,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,SAAuB;QAChD,MAAM,UAAU,GAAa,EAAE,CAAC;QAEhC,gCAAgC;QAChC,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE,CAAC;YAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9D,UAAU,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,gEAAgE;QAChE,wEAAwE;QACxE,uCAAuC;QACvC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjE,uEAAuE;YACvE,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChE,UAAU,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAClG,CAAC;IAED,6EAA6E;IAC7E,kBAAkB;IAClB,6EAA6E;IAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,KAAK,CAAC,OAAO,CAAC,OAAgC;QAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC;QACpD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,+CAA+C;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,2BAA2B,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACpE,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;gBAC1D,eAAe,EAAE,CAAC;aACnB,CAAC;QACJ,CAAC;QAED,oEAAoE;QACpE,qEAAqE;QACrE,uEAAuE;QACvE,kEAAkE;QAClE,uEAAuE;QACvE,IAAI,CAAC;YACH,MAAM,iBAAiB,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK;gBACnD,qBAAqB;YACvB,CAAC,CAAC,CAAC,WAA8B,CAAC;YAClC,IAAI,iBAAiB,CAAC,uBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;QAC1E,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;YAC3C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,iCAAiC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9E,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;gBAC1D,eAAe,EAAE,CAAC;aACnB,CAAC;QACJ,CAAC;QAED,wEAAwE;QACxE,qEAAqE;QACrE,uEAAuE;QACvE,wBAAwB;QACxB,MAAM,WAAW,GAAG;QAChB,OAAO,CAAC,IAAI;;;;;;;;oCAQgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC;eAClD,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;KAC7C,CAAC;QAEF,sEAAsE;QACtE,sEAAsE;QACtE,2DAA2D;QAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE/D,qEAAqE;QACrE,uEAAuE;QACvE,oEAAoE;QACpE,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC;QAElD,uEAAuE;QACvE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAChD,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE;SAC7C,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC;QACjD,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC;QAC5D,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;QAElE,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,IAAI,MAAM,IAAI,0BAA0B,CAAC;YAC3E,MAAM,YAAY,GAChB,UAAU,CAAC,MAAM,KAAK,SAAS;gBAC7B,CAAC,CAAC,6BAA6B,OAAO,IAAI;gBAC1C,CAAC,CAAC,oBAAoB,SAAS,EAAE,CAAC;YACtC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,YAAY;gBACnB,eAAe;gBACf,eAAe;aAChB,CAAC;QACJ,CAAC;QAED,+DAA+D;QAC/D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;QACpD,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,+CAA+C;gBACtD,eAAe;gBACf,eAAe;aAChB,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5D,IAAI,MAAe,CAAC;QACpB,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACxC,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM;YACN,eAAe;YACf,eAAe;SAChB,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,6BAA6B;IAC7B,6EAA6E;IAE7E;;;;;OAKG;IACK,iBAAiB,CAAC,SAAuB;QAC/C,uDAAuD;QACvD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,CAAC;YAClD,MAAM,CAAC,KAAK,GAAG,KAAK,EAClB,YAAsC,EACtC,IAA8B,EAC9B,EAAE;gBACF,MAAM,MAAM,GAAG,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC;gBAClF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACxC,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,2BAA2B,CAAC,CAAC;gBAC7E,CAAC;gBACD,OAAO,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,IAAW,CAAC,CAAC;YAC/C,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,EAAE,GAAG;gBACV,QAAQ,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;oBACnC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBAC5C,kEAAkE;oBAClE,kEAAkE;oBAClE,kEAAkE;oBAClE,+DAA+D;oBAC/D,yCAAyC;oBACzC,MAAM,WAAW,GAAG,CAAC,SAAiB,EAAE,KAAwB,EAAW,EAAE,CAC3E,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;wBAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;wBAChD,2DAA2D;wBAC3D,6DAA6D;wBAC7D,8DAA8D;wBAC9D,4DAA4D;wBAC5D,6BAA6B;wBAC7B,OAAO,CACL,QAAQ,KAAK,EAAE;4BACf,CAAC,QAAQ,KAAK,IAAI;gCAChB,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;gCACrC,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAC9B,CAAC;oBACJ,CAAC,CAAC,CAAC;oBAEL,oEAAoE;oBACpE,4CAA4C;oBAC5C,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;wBACjD,MAAM,IAAI,KAAK,CACb,8BAA8B,YAAY,gCAAgC,CAC3E,CAAC;oBACJ,CAAC;oBAED,mEAAmE;oBACnE,kEAAkE;oBAClE,iEAAiE;oBACjE,gEAAgE;oBAChE,8CAA8C;oBAC9C,EAAE;oBACF,kEAAkE;oBAClE,mEAAmE;oBACnE,kEAAkE;oBAClE,2CAA2C;oBAC3C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;oBAC9C,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC;wBAC1D,MAAM,IAAI,KAAK,CACb,8BAA8B,YAAY,sCAAsC,CACjF,CAAC;oBACJ,CAAC;oBAED,oEAAoE;oBACpE,kEAAkE;oBAClE,gEAAgE;oBAChE,kEAAkE;oBAClE,iEAAiE;oBACjE,+DAA+D;oBAC/D,+DAA+D;oBAC/D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBACtC,IAAI,IAAI,CAAC,UAAU,GAAG,OAAS,EAAE,CAAC;wBAChC,MAAM,IAAI,KAAK,CACb,iDAAiD,IAAI,CAAC,UAAU,SAAS,CAC1E,CAAC;oBACJ,CAAC;oBACD,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAChC,CAAC;aACF,CAAC;QACJ,CAAC;QAED,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,MAAM,GAAG;gBACd,UAAU,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;gBAC9B,UAAU,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;gBACxD,UAAU,EAAE,CAAC,SAAiB,EAAE,GAAW,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC;aAC3E,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// File: backend/agentos/core/llm/providers/implementations/MistralProvider.ts
|
|
2
|
-
import {
|
|
2
|
+
import { sha256Hex } from '../../../utils/sha256.js';
|
|
3
3
|
import { OpenAIProvider } from './OpenAIProvider.js';
|
|
4
4
|
/** Mistral accepts only tool call ids made of nine letters and digits. */
|
|
5
5
|
const MISTRAL_TOOL_CALL_ID = /^[A-Za-z0-9]{9}$/;
|
|
@@ -16,7 +16,7 @@ const MISTRAL_TOOL_CALL_ID = /^[A-Za-z0-9]{9}$/;
|
|
|
16
16
|
export function toMistralToolCallId(id) {
|
|
17
17
|
if (MISTRAL_TOOL_CALL_ID.test(id))
|
|
18
18
|
return id;
|
|
19
|
-
return
|
|
19
|
+
return sha256Hex(id).slice(0, 9);
|
|
20
20
|
}
|
|
21
21
|
/** The messages with every tool call id in Mistral's form. */
|
|
22
22
|
function withMistralToolCallIds(messages) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MistralProvider.js","sourceRoot":"","sources":["../../../../../src/core/llm/providers/implementations/MistralProvider.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAuB9E,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"MistralProvider.js","sourceRoot":"","sources":["../../../../../src/core/llm/providers/implementations/MistralProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -181,6 +181,46 @@ export declare class OpenRouterProvider implements IProvider {
|
|
|
181
181
|
* jittered exponential backoff; 4xx request errors throw immediately.
|
|
182
182
|
*/
|
|
183
183
|
private makeApiRequest;
|
|
184
|
+
/**
|
|
185
|
+
* Masks the configured API keys (the raw config value and each key in a
|
|
186
|
+
* comma-separated pool) and any base-URL credentials out of text bound
|
|
187
|
+
* for an error, a log line or a health report.
|
|
188
|
+
*
|
|
189
|
+
* @param text - Text that may quote a request header or URL.
|
|
190
|
+
* @returns The text with those secrets replaced by `[redacted]`.
|
|
191
|
+
*/
|
|
192
|
+
private redactSecrets;
|
|
193
|
+
/** Every configured secret: the raw key setting, each pooled key, and base-URL credentials. */
|
|
194
|
+
private configuredSecrets;
|
|
195
|
+
/**
|
|
196
|
+
* Masks the end of a text that was cut short when that end is the start
|
|
197
|
+
* of a configured secret. {@link redactSecrets} matches whole secrets
|
|
198
|
+
* only, so a key split by the cut would otherwise show all but its last
|
|
199
|
+
* characters.
|
|
200
|
+
*
|
|
201
|
+
* @param text - Text cut at a size limit or a timeout.
|
|
202
|
+
* @returns The text with any such tail replaced by `[redacted]`.
|
|
203
|
+
*/
|
|
204
|
+
private maskCutSecretTail;
|
|
205
|
+
/**
|
|
206
|
+
* A response body as kept in error details, with the configured keys
|
|
207
|
+
* masked: text directly, JSON values through their serialized form.
|
|
208
|
+
* Anything that does not serialize, such as a live stream whose `req`
|
|
209
|
+
* holds the Authorization header, is replaced by a placeholder.
|
|
210
|
+
*
|
|
211
|
+
* @param data - The response body as axios returned it.
|
|
212
|
+
* @returns A plain value that is safe to log or serialize.
|
|
213
|
+
*/
|
|
214
|
+
private redactResponseData;
|
|
215
|
+
/**
|
|
216
|
+
* The parts of a caught error worth keeping in details: its name, string
|
|
217
|
+
* `code` and masked message. An AxiosError's config and request carry the
|
|
218
|
+
* Authorization header, so they are left out.
|
|
219
|
+
*
|
|
220
|
+
* @param error - The caught error.
|
|
221
|
+
* @returns A plain description that is safe to log or serialize.
|
|
222
|
+
*/
|
|
223
|
+
private describeError;
|
|
184
224
|
private parseSseStream;
|
|
185
225
|
}
|
|
186
226
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenRouterProvider.d.ts","sourceRoot":"","sources":["../../../../../src/core/llm/providers/implementations/OpenRouterProvider.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAGH,OAAO,EACL,SAAS,EACT,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,SAAS,EACT,UAAU,EACV,wBAAwB,EACxB,yBAAyB,EAE1B,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenRouterProvider.d.ts","sourceRoot":"","sources":["../../../../../src/core/llm/providers/implementations/OpenRouterProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { ApiKeyPool } from '../../../providers/ApiKeyPool.js';
|
|
|
12
12
|
import { createGMIErrorFromError, GMIErrorCode } from '../../../utils/errors.js'; // Corrected import path
|
|
13
13
|
import { clampMaxOutputTokens } from '../model-output-limits.js';
|
|
14
14
|
import { stripGeminiOnlyParams } from '../openrouter-only-params.js';
|
|
15
|
+
import { baseUrlCredentials, redactUrlSecrets } from '../url-secrets.js';
|
|
15
16
|
/**
|
|
16
17
|
* Default OpenRouter provider-routing preferences from the environment.
|
|
17
18
|
*
|
|
@@ -92,6 +93,74 @@ export function mapOpenRouterUsage(apiUsage) {
|
|
|
92
93
|
: {}),
|
|
93
94
|
};
|
|
94
95
|
}
|
|
96
|
+
/** Longest streamed error body read into an error, in bytes. */
|
|
97
|
+
const MAX_STREAM_ERROR_BODY_BYTES = 8192;
|
|
98
|
+
/** How long reading a streamed error body may take, in milliseconds. */
|
|
99
|
+
const STREAM_ERROR_BODY_TIMEOUT_MS = 2000;
|
|
100
|
+
/** Whether a value is a Node readable stream, as axios returns for `responseType: 'stream'`. */
|
|
101
|
+
function isReadableStream(value) {
|
|
102
|
+
return (typeof value === 'object' &&
|
|
103
|
+
value !== null &&
|
|
104
|
+
typeof value.pipe === 'function' &&
|
|
105
|
+
typeof value.on === 'function');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Reads up to {@link MAX_STREAM_ERROR_BODY_BYTES} of a streamed error body as
|
|
109
|
+
* UTF-8 text, waiting at most {@link STREAM_ERROR_BODY_TIMEOUT_MS}, then
|
|
110
|
+
* destroys the stream so its socket is released.
|
|
111
|
+
*
|
|
112
|
+
* @param stream - The error response body axios returned as a stream.
|
|
113
|
+
* @returns The text read, possibly empty, and whether it is the whole body
|
|
114
|
+
* (false when the size limit, the timeout or a stream error cut it short).
|
|
115
|
+
*/
|
|
116
|
+
async function readErrorStream(stream) {
|
|
117
|
+
const chunks = [];
|
|
118
|
+
let size = 0;
|
|
119
|
+
let ended = false;
|
|
120
|
+
try {
|
|
121
|
+
await new Promise((resolve) => {
|
|
122
|
+
const timer = setTimeout(finish, STREAM_ERROR_BODY_TIMEOUT_MS);
|
|
123
|
+
function finish() {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
stream.removeListener('data', onData);
|
|
126
|
+
stream.removeListener('end', onEnd);
|
|
127
|
+
stream.removeListener('error', finish);
|
|
128
|
+
resolve();
|
|
129
|
+
}
|
|
130
|
+
function onEnd() {
|
|
131
|
+
ended = true;
|
|
132
|
+
finish();
|
|
133
|
+
}
|
|
134
|
+
function onData(chunk) {
|
|
135
|
+
const bytes = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
136
|
+
chunks.push(bytes);
|
|
137
|
+
size += bytes.length;
|
|
138
|
+
if (size >= MAX_STREAM_ERROR_BODY_BYTES)
|
|
139
|
+
finish();
|
|
140
|
+
}
|
|
141
|
+
stream.on('data', onData);
|
|
142
|
+
stream.once('end', onEnd);
|
|
143
|
+
stream.once('error', finish);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
stream.destroy?.();
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
text: Buffer.concat(chunks).subarray(0, MAX_STREAM_ERROR_BODY_BYTES).toString('utf8'),
|
|
151
|
+
complete: ended && size <= MAX_STREAM_ERROR_BODY_BYTES,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** An error body read as text: the parsed JSON object when it is one, else the text. */
|
|
155
|
+
function parseErrorBody(text) {
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(text);
|
|
158
|
+
return parsed !== null && typeof parsed === 'object' ? parsed : text;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return text;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
95
164
|
export class OpenRouterProvider {
|
|
96
165
|
constructor() {
|
|
97
166
|
this.providerId = 'openrouter';
|
|
@@ -658,9 +727,9 @@ export class OpenRouterProvider {
|
|
|
658
727
|
return {
|
|
659
728
|
isHealthy: false,
|
|
660
729
|
details: {
|
|
661
|
-
message: `OpenRouter health check failed: ${err.message}
|
|
730
|
+
message: this.redactSecrets(`OpenRouter health check failed: ${err.message}`),
|
|
662
731
|
status: err.response?.status,
|
|
663
|
-
responseData: err.response?.data,
|
|
732
|
+
responseData: this.redactResponseData(err.response?.data),
|
|
664
733
|
},
|
|
665
734
|
};
|
|
666
735
|
}
|
|
@@ -859,6 +928,17 @@ export class OpenRouterProvider {
|
|
|
859
928
|
if (axios.isAxiosError(error)) {
|
|
860
929
|
statusCode = error.response?.status;
|
|
861
930
|
errorData = error.response?.data;
|
|
931
|
+
// A streamed request's error body is the live response stream,
|
|
932
|
+
// whose `req` holds the request head with the Authorization
|
|
933
|
+
// header. Read a bounded prefix and drop the stream, so the
|
|
934
|
+
// message can use the body and nothing keeps the request.
|
|
935
|
+
if (isReadableStream(errorData)) {
|
|
936
|
+
const { text, complete } = await readErrorStream(errorData);
|
|
937
|
+
// A body cut short can end inside a key, which whole-secret
|
|
938
|
+
// masking would not match. Whole keys are masked first, so a key
|
|
939
|
+
// whose last characters also start a key is not split in two.
|
|
940
|
+
errorData = parseErrorBody(complete ? text : this.maskCutSecretTail(this.redactSecrets(text)));
|
|
941
|
+
}
|
|
862
942
|
transportFailure = error.response === undefined;
|
|
863
943
|
const retryAfterRaw = error.response?.headers?.['retry-after'];
|
|
864
944
|
const parsedRetryAfter = typeof retryAfterRaw === 'string' ? parseInt(retryAfterRaw, 10) : NaN;
|
|
@@ -869,7 +949,7 @@ export class OpenRouterProvider {
|
|
|
869
949
|
errorMessage = errorData.error.message || errorMessage;
|
|
870
950
|
errorType = errorData.error.type || errorType;
|
|
871
951
|
}
|
|
872
|
-
else if (typeof errorData === 'string') {
|
|
952
|
+
else if (typeof errorData === 'string' && errorData.trim()) {
|
|
873
953
|
errorMessage = errorData;
|
|
874
954
|
}
|
|
875
955
|
else if (error.message) {
|
|
@@ -887,8 +967,16 @@ export class OpenRouterProvider {
|
|
|
887
967
|
// Prefix the status code into the message so downstream retry/fallback
|
|
888
968
|
// logic (e.g. isRetryableError, which greps for \b402\b) can route on it
|
|
889
969
|
// even when the OR API body provides a friendlier description.
|
|
890
|
-
const decoratedMessage = statusCode ? `[${statusCode}] ${errorMessage}` : errorMessage;
|
|
891
|
-
lastError = new OpenRouterProviderError(decoratedMessage, 'API_REQUEST_FAILED', statusCode, errorType, {
|
|
970
|
+
const decoratedMessage = this.redactSecrets(statusCode ? `[${statusCode}] ${errorMessage}` : errorMessage);
|
|
971
|
+
lastError = new OpenRouterProviderError(decoratedMessage, 'API_REQUEST_FAILED', statusCode, errorType, {
|
|
972
|
+
requestEndpoint: endpoint,
|
|
973
|
+
requestBodyPreview: body ? JSON.stringify(body).substring(0, 200) + '...' : undefined,
|
|
974
|
+
responseData: this.redactResponseData(errorData),
|
|
975
|
+
// Not the AxiosError itself: its config and request carry the
|
|
976
|
+
// Authorization header, so logging or serializing the details
|
|
977
|
+
// would print the API key.
|
|
978
|
+
underlyingError: this.describeError(error),
|
|
979
|
+
});
|
|
892
980
|
const retryable = transportFailure ||
|
|
893
981
|
statusCode === 408 ||
|
|
894
982
|
statusCode === 429 ||
|
|
@@ -908,6 +996,88 @@ export class OpenRouterProvider {
|
|
|
908
996
|
// the compiler + any future refactor honest.
|
|
909
997
|
throw lastError ?? new OpenRouterProviderError('OpenRouter request failed.', 'API_REQUEST_FAILED');
|
|
910
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Masks the configured API keys (the raw config value and each key in a
|
|
1001
|
+
* comma-separated pool) and any base-URL credentials out of text bound
|
|
1002
|
+
* for an error, a log line or a health report.
|
|
1003
|
+
*
|
|
1004
|
+
* @param text - Text that may quote a request header or URL.
|
|
1005
|
+
* @returns The text with those secrets replaced by `[redacted]`.
|
|
1006
|
+
*/
|
|
1007
|
+
redactSecrets(text) {
|
|
1008
|
+
const rawKeys = this.config?.apiKey ?? '';
|
|
1009
|
+
const keys = [rawKeys, ...rawKeys.split(',').map((key) => key.trim())];
|
|
1010
|
+
return redactUrlSecrets(text, this.config?.baseURL, keys);
|
|
1011
|
+
}
|
|
1012
|
+
/** Every configured secret: the raw key setting, each pooled key, and base-URL credentials. */
|
|
1013
|
+
configuredSecrets() {
|
|
1014
|
+
const rawKeys = this.config?.apiKey ?? '';
|
|
1015
|
+
return [rawKeys, ...rawKeys.split(',').map((key) => key.trim()), baseUrlCredentials(this.config?.baseURL) ?? ''].filter((secret) => secret.length > 0);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Masks the end of a text that was cut short when that end is the start
|
|
1019
|
+
* of a configured secret. {@link redactSecrets} matches whole secrets
|
|
1020
|
+
* only, so a key split by the cut would otherwise show all but its last
|
|
1021
|
+
* characters.
|
|
1022
|
+
*
|
|
1023
|
+
* @param text - Text cut at a size limit or a timeout.
|
|
1024
|
+
* @returns The text with any such tail replaced by `[redacted]`.
|
|
1025
|
+
*/
|
|
1026
|
+
maskCutSecretTail(text) {
|
|
1027
|
+
let cut = 0;
|
|
1028
|
+
for (const secret of this.configuredSecrets()) {
|
|
1029
|
+
for (let length = Math.min(secret.length - 1, text.length); length > cut; length--) {
|
|
1030
|
+
if (text.endsWith(secret.slice(0, length))) {
|
|
1031
|
+
cut = length;
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return cut > 0 ? `${text.slice(0, text.length - cut)}[redacted]` : text;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* A response body as kept in error details, with the configured keys
|
|
1040
|
+
* masked: text directly, JSON values through their serialized form.
|
|
1041
|
+
* Anything that does not serialize, such as a live stream whose `req`
|
|
1042
|
+
* holds the Authorization header, is replaced by a placeholder.
|
|
1043
|
+
*
|
|
1044
|
+
* @param data - The response body as axios returned it.
|
|
1045
|
+
* @returns A plain value that is safe to log or serialize.
|
|
1046
|
+
*/
|
|
1047
|
+
redactResponseData(data) {
|
|
1048
|
+
if (data === undefined || data === null)
|
|
1049
|
+
return data;
|
|
1050
|
+
if (typeof data === 'string')
|
|
1051
|
+
return this.redactSecrets(data);
|
|
1052
|
+
if (isReadableStream(data))
|
|
1053
|
+
return '[stream body not read]';
|
|
1054
|
+
try {
|
|
1055
|
+
const json = JSON.stringify(data);
|
|
1056
|
+
return json === undefined ? undefined : JSON.parse(this.redactSecrets(json));
|
|
1057
|
+
}
|
|
1058
|
+
catch {
|
|
1059
|
+
return '[unserializable response body]';
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* The parts of a caught error worth keeping in details: its name, string
|
|
1064
|
+
* `code` and masked message. An AxiosError's config and request carry the
|
|
1065
|
+
* Authorization header, so they are left out.
|
|
1066
|
+
*
|
|
1067
|
+
* @param error - The caught error.
|
|
1068
|
+
* @returns A plain description that is safe to log or serialize.
|
|
1069
|
+
*/
|
|
1070
|
+
describeError(error) {
|
|
1071
|
+
if (error instanceof Error) {
|
|
1072
|
+
const code = error.code;
|
|
1073
|
+
return {
|
|
1074
|
+
name: error.name,
|
|
1075
|
+
...(typeof code === 'string' ? { code } : {}),
|
|
1076
|
+
message: this.redactSecrets(error.message),
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
return { message: this.redactSecrets(String(error)) };
|
|
1080
|
+
}
|
|
911
1081
|
async *parseSseStream(stream) {
|
|
912
1082
|
let buffer = '';
|
|
913
1083
|
const readableStream = stream;
|
|
@@ -928,11 +1098,14 @@ export class OpenRouterProvider {
|
|
|
928
1098
|
}
|
|
929
1099
|
}
|
|
930
1100
|
catch (error) {
|
|
931
|
-
const message = error instanceof Error ? error.message : "OpenRouter stream parsing/reading error";
|
|
932
|
-
|
|
1101
|
+
const message = this.redactSecrets(error instanceof Error ? error.message : "OpenRouter stream parsing/reading error");
|
|
1102
|
+
// A stream error can be an AxiosError holding the request config, so
|
|
1103
|
+
// only its description is logged and kept.
|
|
1104
|
+
const description = this.describeError(error);
|
|
1105
|
+
console.error("OpenRouterProvider: Error reading or parsing SSE stream:", message, description);
|
|
933
1106
|
if (error instanceof OpenRouterProviderError)
|
|
934
1107
|
throw error;
|
|
935
|
-
throw new OpenRouterProviderError(message, 'STREAM_PARSING_ERROR', undefined, undefined,
|
|
1108
|
+
throw new OpenRouterProviderError(message, 'STREAM_PARSING_ERROR', undefined, undefined, description);
|
|
936
1109
|
}
|
|
937
1110
|
finally {
|
|
938
1111
|
if (typeof readableStream.destroy === 'function') {
|