@axiorank/ai-sdk 0.1.0

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/index.cjs ADDED
@@ -0,0 +1,1457 @@
1
+ 'use strict';
2
+
3
+ // src/extract.ts
4
+ function extractPromptText(prompt) {
5
+ const out = [];
6
+ for (const message of prompt) {
7
+ if (typeof message.content === "string") {
8
+ out.push(message.content);
9
+ continue;
10
+ }
11
+ for (const part of message.content) {
12
+ if (part.type === "text") out.push(part.text);
13
+ }
14
+ }
15
+ return out.join("\n");
16
+ }
17
+ function extractCompletionText(content) {
18
+ return content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
19
+ }
20
+ function extractToolCalls(content) {
21
+ return content.filter(
22
+ (part) => part.type === "tool-call"
23
+ );
24
+ }
25
+ function parseToolInput(input) {
26
+ try {
27
+ const parsed = JSON.parse(input);
28
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
29
+ return parsed;
30
+ }
31
+ return { input: parsed };
32
+ } catch {
33
+ return { input };
34
+ }
35
+ }
36
+
37
+ // ../sdk/dist/chunk-I4IYZBCH.js
38
+ var LIMITS = {
39
+ /** Max object/array nesting inspected before passing values through. */
40
+ maxDepth: 8,
41
+ /** Max number of string leaves inspected. */
42
+ maxLeaves: 2e3,
43
+ /** Max total bytes of string content inspected. */
44
+ maxTotalBytes: 256 * 1024,
45
+ /** Per-leaf: only the first N chars are scanned by detectors. */
46
+ maxLeafScan: 64 * 1024,
47
+ /** Max signals retained per evaluation (bounds the stored array). */
48
+ maxSignals: 50
49
+ };
50
+ function transformStrings(value, fn, state, path = "", depth = 0) {
51
+ if (typeof value === "string") {
52
+ if (state.leaves >= LIMITS.maxLeaves || state.bytes >= LIMITS.maxTotalBytes) {
53
+ state.truncated = true;
54
+ return value;
55
+ }
56
+ state.leaves += 1;
57
+ state.bytes += value.length;
58
+ return fn(path, value, state);
59
+ }
60
+ if (Array.isArray(value)) {
61
+ if (depth >= LIMITS.maxDepth) {
62
+ state.truncated = true;
63
+ return value;
64
+ }
65
+ return value.map(
66
+ (item, i) => transformStrings(item, fn, state, `${path}[${i}]`, depth + 1)
67
+ );
68
+ }
69
+ if (value !== null && typeof value === "object") {
70
+ if (depth >= LIMITS.maxDepth) {
71
+ state.truncated = true;
72
+ return value;
73
+ }
74
+ const out = {};
75
+ for (const [key, child] of Object.entries(value)) {
76
+ const childPath = path ? `${path}.${key}` : key;
77
+ out[key] = transformStrings(child, fn, state, childPath, depth + 1);
78
+ }
79
+ return out;
80
+ }
81
+ return value;
82
+ }
83
+ var INVISIBLES = /[\u00AD\u034F\u200B-\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\uFEFF]/g;
84
+ var CONFUSABLES = {
85
+ // Cyrillic lowercase
86
+ \u0430: "a",
87
+ \u0435: "e",
88
+ \u043E: "o",
89
+ \u0440: "p",
90
+ \u0441: "c",
91
+ \u0443: "y",
92
+ \u0445: "x",
93
+ \u0456: "i",
94
+ \u0458: "j",
95
+ \u0455: "s",
96
+ "\u0501": "d",
97
+ // Cyrillic uppercase
98
+ \u0410: "A",
99
+ \u0412: "B",
100
+ \u0415: "E",
101
+ \u041A: "K",
102
+ \u041C: "M",
103
+ \u041D: "H",
104
+ \u041E: "O",
105
+ \u0420: "P",
106
+ \u0421: "C",
107
+ \u0422: "T",
108
+ \u0423: "Y",
109
+ \u0425: "X",
110
+ // Greek lowercase
111
+ \u03B1: "a",
112
+ \u03B9: "i",
113
+ \u03BD: "v",
114
+ \u03BF: "o",
115
+ \u03C1: "p",
116
+ \u03C5: "u",
117
+ // Greek uppercase
118
+ \u0391: "A",
119
+ \u0392: "B",
120
+ \u0395: "E",
121
+ \u0396: "Z",
122
+ \u0397: "H",
123
+ \u0399: "I",
124
+ \u039A: "K",
125
+ \u039C: "M",
126
+ \u039D: "N",
127
+ \u039F: "O",
128
+ \u03A1: "P",
129
+ \u03A4: "T",
130
+ \u03A5: "Y",
131
+ \u03A7: "X"
132
+ };
133
+ var CONFUSABLE_RE = new RegExp(`[${Object.keys(CONFUSABLES).join("")}]`, "g");
134
+ function canonicalize(value) {
135
+ return value.normalize("NFKC").replace(INVISIBLES, "").replace(CONFUSABLE_RE, (ch) => CONFUSABLES[ch] ?? ch);
136
+ }
137
+ function makeCanonicalVariantScanner() {
138
+ return (value) => {
139
+ const text = value.length > LIMITS.maxLeafScan ? value.slice(0, LIMITS.maxLeafScan) : value;
140
+ const canonical = canonicalize(text);
141
+ return canonical !== text ? [{ kind: "canonical", value: canonical }] : [];
142
+ };
143
+ }
144
+ function fnv1a(value) {
145
+ let h = 2166136261;
146
+ for (let i = 0; i < value.length; i++) {
147
+ h ^= value.charCodeAt(i);
148
+ h = Math.imul(h, 16777619);
149
+ }
150
+ return (h >>> 0).toString(16).padStart(8, "0");
151
+ }
152
+ function cosmeticFingerprint(value) {
153
+ return `redacted \xB7 sha256:${fnv1a(value)}`;
154
+ }
155
+ function maskMiddle(value, keep = 2) {
156
+ if (value.length <= keep * 2) return "\u2022".repeat(value.length);
157
+ const head = value.slice(0, keep);
158
+ const tail = keep > 0 ? value.slice(-keep) : "";
159
+ const middle = "\u2022".repeat(Math.max(3, value.length - keep * 2));
160
+ return `${head}${middle}${tail}`;
161
+ }
162
+ function truncateEvidence(value, max = 80) {
163
+ const oneLine = value.replace(/\s+/g, " ").trim();
164
+ return oneLine.length > max ? `${oneLine.slice(0, max)}\u2026` : oneLine;
165
+ }
166
+ function applyRedactions(value, spans) {
167
+ if (spans.length === 0) return value;
168
+ const sorted = [...spans].sort((a, b) => a.start - b.start);
169
+ const merged = [];
170
+ for (const span of sorted) {
171
+ const last = merged[merged.length - 1];
172
+ if (last && span.start <= last.end) {
173
+ last.end = Math.max(last.end, span.end);
174
+ } else {
175
+ merged.push({ ...span });
176
+ }
177
+ }
178
+ let out = value;
179
+ for (let i = merged.length - 1; i >= 0; i--) {
180
+ const s = merged[i];
181
+ out = out.slice(0, s.start) + s.mask + out.slice(s.end);
182
+ }
183
+ return out;
184
+ }
185
+ function withFlags(re, required) {
186
+ let flags = re.flags;
187
+ for (const f of required) if (!flags.includes(f)) flags += f;
188
+ return flags === re.flags ? re : new RegExp(re.source, flags);
189
+ }
190
+ function scanSecrets(value, specs, fingerprint = cosmeticFingerprint) {
191
+ const matches = [];
192
+ for (const spec of specs) {
193
+ const re = withFlags(spec.regex, "gd");
194
+ for (const m of value.matchAll(re)) {
195
+ const group = spec.group ?? 0;
196
+ const secret = m[group];
197
+ const span = m.indices?.[group];
198
+ if (!secret || !span) continue;
199
+ matches.push({
200
+ detector: spec.id,
201
+ category: "secret",
202
+ severity: spec.severity,
203
+ label: spec.label,
204
+ evidence: fingerprint(secret),
205
+ ...spec.critical ? { critical: true } : {},
206
+ redact: { start: span[0], end: span[1], mask: `\u2039redacted:${spec.id}\u203A` }
207
+ });
208
+ }
209
+ }
210
+ return matches;
211
+ }
212
+ function scanFindings(value, category, specs) {
213
+ const matches = [];
214
+ for (const spec of specs) {
215
+ const re = withFlags(spec.regex, "g");
216
+ for (const m of value.matchAll(re)) {
217
+ const whole = m[0];
218
+ if (!whole) continue;
219
+ matches.push({
220
+ detector: spec.id,
221
+ category,
222
+ severity: spec.severity,
223
+ label: spec.label,
224
+ evidence: (spec.evidence ?? truncateEvidence)(whole),
225
+ ...spec.critical ? { critical: true } : {}
226
+ });
227
+ }
228
+ }
229
+ return matches;
230
+ }
231
+ var ABUSE_PATTERNS = [
232
+ {
233
+ id: "rate_abuse.path_probe",
234
+ label: "Sensitive path probe",
235
+ severity: "high",
236
+ regex: /(?:^|\/)(?:\.env\b|\.git(?:\/|\b)|\.aws(?:\/|\b)|\.ssh(?:\/|\b)|id_rsa\b|wp-login\.php|wp-admin\b|xmlrpc\.php|phpmyadmin\b|actuator\/env|server-status\b)/i
237
+ },
238
+ {
239
+ id: "rate_abuse.enumeration",
240
+ label: "Deep pagination / enumeration",
241
+ severity: "medium",
242
+ regex: /(?:^|[?&])(?:page|offset|start|p)=\d{4,}/i
243
+ },
244
+ {
245
+ id: "rate_abuse.bulk_limit",
246
+ label: "Oversized result window",
247
+ severity: "medium",
248
+ regex: /(?:^|[?&])(?:limit|per_page|page_size|count|size)=(?:[1-9]\d{3,})/i
249
+ },
250
+ {
251
+ id: "rate_abuse.graphql_introspection",
252
+ label: "GraphQL introspection",
253
+ severity: "medium",
254
+ regex: /\b__schema\b|\bIntrospectionQuery\b/
255
+ },
256
+ {
257
+ id: "rate_abuse.wildcard_fields",
258
+ label: "Wildcard field selection",
259
+ severity: "low",
260
+ regex: /(?:^|[?&])(?:fields|select|include)=\*/i
261
+ },
262
+ {
263
+ id: "rate_abuse.automation_client",
264
+ label: "Automation / scraper client",
265
+ severity: "low",
266
+ regex: /\b(?:python-requests|aiohttp|httpx|scrapy|go-http-client|node-fetch|okhttp|libwww-perl|wget|curl\/[\d.]+|headlesschrome|phantomjs|puppeteer|playwright)\b/i
267
+ }
268
+ ];
269
+ var abuseDetector = {
270
+ id: "rate_abuse",
271
+ category: "rate_abuse",
272
+ inspect(ctx) {
273
+ return scanFindings(ctx.value, "rate_abuse", ABUSE_PATTERNS);
274
+ }
275
+ };
276
+ var CARD_PATTERNS = [
277
+ {
278
+ id: "supply_chain.code_execution",
279
+ label: "Code-execution capability",
280
+ severity: "high",
281
+ regex: /\b(?:exec|execute|eval|spawn|shell|sudo|run command|os command|terminal)\b/i
282
+ },
283
+ {
284
+ id: "supply_chain.fund_movement",
285
+ label: "Payment / fund-movement capability",
286
+ severity: "high",
287
+ regex: /\b(?:payment|transfer|wire|payout|withdraw|charge|refund|disburse)\b/i
288
+ },
289
+ {
290
+ id: "supply_chain.credential_access",
291
+ label: "Credential / secret access capability",
292
+ severity: "high",
293
+ regex: /\b(?:credential|secret|password|api key|private key|ssh key|access token)\b/i
294
+ },
295
+ {
296
+ id: "supply_chain.admin_reach",
297
+ label: "Administrative capability",
298
+ severity: "medium",
299
+ regex: /\b(?:admin|administrator|superuser|impersonate|grant role)\b/i
300
+ },
301
+ {
302
+ id: "supply_chain.wildcard_scope",
303
+ label: "Wildcard scope / permission",
304
+ severity: "medium",
305
+ regex: /\b(?:read|write|admin|scope|access|permissions?)\s\*|(?:^|\s)\*(?:\s|$)/i
306
+ }
307
+ ];
308
+ function normalize(value) {
309
+ return value.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_\-./:]+/g, " ");
310
+ }
311
+ var cardCapabilityDetector = {
312
+ id: "supply_chain.capability",
313
+ category: "supply_chain",
314
+ inspect(ctx) {
315
+ return scanFindings(normalize(ctx.value), "supply_chain", CARD_PATTERNS);
316
+ }
317
+ };
318
+ var RM_RF = /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\b/g;
319
+ var SQL_DROP = /\b(?:drop|truncate)\s+(?:table|database|schema|index|view)\b/gi;
320
+ var SQL_MUTATE = /\b(?:delete\s+from|update)\s+["'`\w.]+/gi;
321
+ var RESOURCE_DELETE = /\b(?:delete|destroy|terminate|remove|teardown)[-_\s]?(?:bucket|instance|volume|database|cluster|stack|deployment|namespace|table|repo(?:sitory)?)\b/gi;
322
+ var FORCE_PUSH = /\bgit\s+push\b[\s\S]{0,30}?(?:--force\b|--force-with-lease\b|\s-f\b)/gi;
323
+ var DANGEROUS_FLAG = /(?:^|\s)(?:--all|--recursive|--no-preserve-root|--force)\b/gi;
324
+ function mk(detector, label, severity, match) {
325
+ return {
326
+ detector,
327
+ category: "destructive",
328
+ severity,
329
+ label,
330
+ evidence: truncateEvidence(match)
331
+ };
332
+ }
333
+ var destructiveDetector = {
334
+ id: "destructive",
335
+ category: "destructive",
336
+ inspect(ctx) {
337
+ const v = ctx.value;
338
+ const out = [];
339
+ for (const m of v.matchAll(RM_RF)) {
340
+ out.push(mk("destructive.rm_rf", "Recursive/forced file delete", "high", m[0]));
341
+ }
342
+ for (const m of v.matchAll(SQL_DROP)) {
343
+ out.push(mk("destructive.sql_drop", "SQL DROP/TRUNCATE", "high", m[0]));
344
+ }
345
+ for (const m of v.matchAll(SQL_MUTATE)) {
346
+ const start = m.index ?? 0;
347
+ if (!/\bwhere\b/i.test(v.slice(start, start + 160))) {
348
+ out.push(
349
+ mk("destructive.sql_no_where", "SQL DELETE/UPDATE without WHERE", "high", m[0])
350
+ );
351
+ }
352
+ }
353
+ for (const m of v.matchAll(RESOURCE_DELETE)) {
354
+ out.push(mk("destructive.resource_delete", "Resource destruction", "high", m[0]));
355
+ }
356
+ for (const m of v.matchAll(FORCE_PUSH)) {
357
+ out.push(mk("destructive.force_push", "Git force push", "medium", m[0]));
358
+ }
359
+ for (const m of v.matchAll(DANGEROUS_FLAG)) {
360
+ out.push(mk("destructive.dangerous_flag", "Dangerous CLI flag", "low", m[0]));
361
+ }
362
+ return out;
363
+ }
364
+ };
365
+ var SELECT_STAR = /\bselect\s+\*\s+from\b/gi;
366
+ var BULK_EXPORT = /\b(?:export|dump|download|scrape)\s+(?:all|everything|\*|database|table|users|customers|records)\b/gi;
367
+ var LARGE_VALUE = 2e4;
368
+ var egressDetector = {
369
+ id: "egress",
370
+ category: "egress",
371
+ inspect(ctx) {
372
+ const v = ctx.value;
373
+ const out = [];
374
+ for (const m of v.matchAll(SELECT_STAR)) {
375
+ out.push({
376
+ detector: "egress.select_star",
377
+ category: "egress",
378
+ severity: "medium",
379
+ label: "Unbounded SELECT *",
380
+ evidence: truncateEvidence(m[0])
381
+ });
382
+ }
383
+ for (const m of v.matchAll(BULK_EXPORT)) {
384
+ out.push({
385
+ detector: "egress.bulk_export",
386
+ category: "egress",
387
+ severity: "high",
388
+ label: "Bulk data export",
389
+ evidence: truncateEvidence(m[0])
390
+ });
391
+ }
392
+ if (v.length >= LARGE_VALUE) {
393
+ out.push({
394
+ detector: "egress.large_value",
395
+ category: "egress",
396
+ severity: "low",
397
+ label: "Large field value",
398
+ evidence: `${v.length.toLocaleString("en-US")} chars`
399
+ });
400
+ }
401
+ return out;
402
+ }
403
+ };
404
+ var SPECS = [
405
+ {
406
+ id: "injection.prompt",
407
+ label: "Prompt injection",
408
+ severity: "high",
409
+ regex: /\b(?:ignore|disregard|forget|override)\b[\s\S]{0,40}?\b(?:previous|prior|above|earlier|all|your)\b[\s\S]{0,20}?\b(?:instructions?|prompts?|rules?|guidelines?|context|directives?)\b/gi
410
+ },
411
+ {
412
+ id: "injection.system_override",
413
+ label: "System-prompt override",
414
+ severity: "high",
415
+ regex: /\b(?:you\s+are\s+now|act\s+as(?:\s+if)?|pretend\s+to\s+be|new\s+system\s+prompt|developer\s+mode|jailbreak)\b/gi
416
+ },
417
+ {
418
+ id: "injection.sql",
419
+ label: "SQL injection",
420
+ severity: "high",
421
+ regex: /(?:'|%27)\s*(?:or|and)\s*(?:'|%27|\d)|\bunion\s+select\b|\bor\s+1\s*=\s*1\b|;\s*drop\s+table\b/gi
422
+ },
423
+ {
424
+ id: "injection.shell",
425
+ label: "Shell/command injection",
426
+ severity: "high",
427
+ regex: /[;&|]\s*(?:rm|cat|curl|wget|nc|bash|sh|chmod|chown|kill|mkfs|dd|eval)\b|\$\([^)]{0,120}\)|`[^`]{0,120}`/g
428
+ },
429
+ {
430
+ id: "injection.ssrf",
431
+ label: "SSRF / internal endpoint",
432
+ severity: "high",
433
+ regex: /\b(?:169\.254\.169\.254|metadata\.google\.internal|127\.0\.0\.1|0\.0\.0\.0)\b|\bfile:\/\/|\bgopher:\/\//gi
434
+ },
435
+ {
436
+ id: "injection.path_traversal",
437
+ label: "Path traversal",
438
+ severity: "medium",
439
+ regex: /(?:\.\.[/\\]){2,}|\/etc\/(?:passwd|shadow)\b|c:\\windows\\system32/gi
440
+ }
441
+ ];
442
+ var injectionDetector = {
443
+ id: "injection",
444
+ category: "injection",
445
+ inspect: (ctx) => scanFindings(ctx.value, "injection", SPECS)
446
+ };
447
+ var SPECS2 = [
448
+ {
449
+ id: "injection.jailbreak.dan",
450
+ label: "DAN-style jailbreak",
451
+ severity: "high",
452
+ regex: /\bDAN\b|\bdo\s+anything\s+now\b|\bstay\s+in\s+character\b/gi
453
+ },
454
+ {
455
+ id: "injection.jailbreak.no_restrictions",
456
+ label: "No-restrictions jailbreak",
457
+ severity: "high",
458
+ regex: /\b(?:unrestricted|unfiltered|uncensored|jailbroken|no\s+longer\s+bound)\b|\b(?:no|without|free\s+(?:from|of))\s+(?:restrictions?|rules?|filters?|limitations?|guidelines?|guardrails?|censorship|safety\s+(?:rules|guidelines))\b/gi
459
+ },
460
+ {
461
+ id: "injection.jailbreak.policy_override",
462
+ label: "Safety-policy override",
463
+ severity: "high",
464
+ regex: /\b(?:ignore|bypass|disable|turn\s+off|switch\s+off|forget)\b[\s\S]{0,30}?\b(?:safety|content\s+(?:policy|filter)|moderation|guardrails?|ethical\s+guidelines?|restrictions?)\b/gi
465
+ },
466
+ {
467
+ id: "injection.jailbreak.roleplay_escape",
468
+ label: "Roleplay-escape jailbreak",
469
+ severity: "medium",
470
+ regex: /\b(?:hypothetically|in\s+a\s+(?:fictional|hypothetical)\s+(?:story|scenario|world)|for\s+(?:educational|research)\s+purposes\s+only)\b[\s\S]{0,60}?\b(?:no\s+rules|without\s+restrictions?|anything|illegal|harmful)\b/gi
471
+ },
472
+ {
473
+ id: "injection.jailbreak.persona",
474
+ label: "Adversarial persona",
475
+ severity: "high",
476
+ regex: /\b(?:evil\s+(?:mode|assistant|confidant|ai)|opposite\s+day|do\s+not\s+(?:follow|obey)\s+(?:any\s+)?(?:rules?|policies|guidelines?)|you\s+have\s+no\s+(?:rules?|restrictions?|guidelines?|filters?))\b/gi
477
+ }
478
+ ];
479
+ var jailbreakDetector = {
480
+ id: "jailbreak",
481
+ category: "injection",
482
+ inspect: (ctx) => scanFindings(ctx.value, "injection", SPECS2)
483
+ };
484
+ var SPECS3 = [
485
+ {
486
+ id: "injection.tool_output_override",
487
+ label: "Instruction override in tool output",
488
+ severity: "high",
489
+ // Three shapes aimed at hijacking the agent from inside untrusted content:
490
+ // classic "ignore previous instructions", a re-tasking ("your new task
491
+ // is…"), and a secrecy directive ("do not tell the user…").
492
+ regex: /\b(?:ignore|disregard|forget|override)\b[\s\S]{0,40}?\b(?:previous|prior|above|earlier|all|the)\b[\s\S]{0,20}?\b(?:instructions?|prompts?|rules?|context|messages?|directives?)\b|\byour\s+(?:new\s+|real\s+|actual\s+)?(?:task|instruction|goal|objective|job)\s+is\b|\bdo\s+not\s+(?:tell|inform|reveal\s+to|mention\s+to|notify)\s+(?:the\s+)?(?:user|human|operator)\b/gi
493
+ },
494
+ {
495
+ id: "injection.exfil_markdown",
496
+ label: "Data-exfiltration markdown link or image",
497
+ severity: "high",
498
+ // A markdown image/link whose URL carries query data or a template
499
+ // placeholder - the classic "render this image to leak the conversation"
500
+ // vector. A plain link with no params is not flagged (too noisy).
501
+ regex: /!?\[[^\]]*\]\(\s*(?:https?:)?\/\/[^)\s]*(?:\?[^)\s]+|\{\{[^)\s]*\}\}|\$\{[^)\s]*\})[^)\s]*\)/gi
502
+ },
503
+ {
504
+ id: "injection.tool_directive",
505
+ label: "Embedded tool-call directive",
506
+ severity: "high",
507
+ // Content instructing the agent to invoke a tool/function, or a fake
508
+ // function-call object smuggled into prose.
509
+ regex: /\b(?:call|invoke|use|run|execute|trigger)\s+(?:the\s+)?[`"']?[\w.\-]{2,}[`"']?\s+(?:tool|function|command|endpoint|api)\b|"(?:tool_name|tool_call|function_call|function)"\s*:/gi
510
+ },
511
+ {
512
+ id: "injection.role_marker",
513
+ label: "Fake role or chat marker in content",
514
+ severity: "medium",
515
+ // Chat-template / role markers embedded in untrusted text to forge a new
516
+ // turn or a system message.
517
+ regex: /<\|im_(?:start|end)\|>|<\/?(?:system|assistant)\b[^>]*>|\[\/?INST\]|\[\/?SYS\]|(?:^|\n)\s{0,3}#{1,3}\s*system\s*:/gi
518
+ },
519
+ {
520
+ id: "injection.encoded_payload",
521
+ label: "Suspicious encoded blob",
522
+ severity: "medium",
523
+ // A long contiguous base64 run embedded in otherwise-readable content - a
524
+ // common way to hide instructions from prose-level detectors. Kept long to
525
+ // avoid flagging short ids/hashes; medium severity, monitor-first.
526
+ regex: /[A-Za-z0-9+/]{200,}={0,2}/g
527
+ }
528
+ ];
529
+ var HIDDEN_UNICODE = /[\u{200B}-\u{200D}\u{2060}\u{FEFF}\u{202A}-\u{202E}\u{2066}-\u{2069}\u{E0000}-\u{E007F}]/gu;
530
+ var outputInjectionDetector = {
531
+ id: "injection.output",
532
+ category: "injection",
533
+ inspect(ctx) {
534
+ if (ctx.phase !== "result") return [];
535
+ const matches = scanFindings(ctx.value, "injection", SPECS3);
536
+ const hidden = ctx.value.match(HIDDEN_UNICODE);
537
+ if (hidden) {
538
+ matches.push({
539
+ detector: "injection.hidden_unicode",
540
+ category: "injection",
541
+ severity: "high",
542
+ label: "Hidden unicode smuggling",
543
+ evidence: `${hidden.length} hidden/bidirectional character${hidden.length === 1 ? "" : "s"} in output`
544
+ });
545
+ }
546
+ return matches;
547
+ }
548
+ };
549
+ var PII_MASK = {
550
+ "pii.email": "\u2039redacted:pii.email\u203A",
551
+ "pii.ssn": "\u2039redacted:pii.ssn\u203A",
552
+ "pii.phone": "\u2039redacted:pii.phone\u203A",
553
+ "pii.credit_card": "\u2039redacted:pii.card\u203A"
554
+ };
555
+ function piiRedact(enabled, detector, index, length) {
556
+ if (!enabled) return void 0;
557
+ return { start: index, end: index + length, mask: PII_MASK[detector] };
558
+ }
559
+ var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
560
+ var SSN = /\b(?!000|666|9\d\d)\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g;
561
+ var PHONE = /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b/g;
562
+ var CARD = /\b(?:\d[ -]?){13,19}\b/g;
563
+ function luhnValid(digits) {
564
+ let sum = 0;
565
+ let alt = false;
566
+ for (let i = digits.length - 1; i >= 0; i--) {
567
+ let d = digits.charCodeAt(i) - 48;
568
+ if (d < 0 || d > 9) return false;
569
+ if (alt) {
570
+ d *= 2;
571
+ if (d > 9) d -= 9;
572
+ }
573
+ sum += d;
574
+ alt = !alt;
575
+ }
576
+ return sum % 10 === 0;
577
+ }
578
+ var piiDetector = {
579
+ id: "pii",
580
+ category: "pii",
581
+ inspect(ctx) {
582
+ const v = ctx.value;
583
+ const out = [];
584
+ for (const m of v.matchAll(EMAIL)) {
585
+ out.push({
586
+ detector: "pii.email",
587
+ category: "pii",
588
+ severity: "medium",
589
+ label: "Email address",
590
+ evidence: maskMiddle(m[0]),
591
+ redact: piiRedact(ctx.redactPii, "pii.email", m.index, m[0].length)
592
+ });
593
+ }
594
+ for (const m of v.matchAll(SSN)) {
595
+ out.push({
596
+ detector: "pii.ssn",
597
+ category: "pii",
598
+ severity: "high",
599
+ label: "US Social Security Number",
600
+ evidence: maskMiddle(m[0], 0),
601
+ redact: piiRedact(ctx.redactPii, "pii.ssn", m.index, m[0].length)
602
+ });
603
+ }
604
+ for (const m of v.matchAll(PHONE)) {
605
+ out.push({
606
+ detector: "pii.phone",
607
+ category: "pii",
608
+ severity: "low",
609
+ label: "Phone number",
610
+ evidence: maskMiddle(m[0]),
611
+ redact: piiRedact(ctx.redactPii, "pii.phone", m.index, m[0].length)
612
+ });
613
+ }
614
+ for (const m of v.matchAll(CARD)) {
615
+ const digits = m[0].replace(/[ -]/g, "");
616
+ if (digits.length >= 13 && digits.length <= 19 && luhnValid(digits)) {
617
+ out.push({
618
+ detector: "pii.credit_card",
619
+ category: "pii",
620
+ severity: "high",
621
+ critical: true,
622
+ label: "Credit card number",
623
+ evidence: maskMiddle(digits, 0),
624
+ redact: piiRedact(ctx.redactPii, "pii.credit_card", m.index, m[0].length)
625
+ });
626
+ }
627
+ }
628
+ return out;
629
+ }
630
+ };
631
+ var SECRET_PATTERNS = [
632
+ {
633
+ id: "secret.aws_access_key",
634
+ label: "AWS access key id",
635
+ severity: "critical",
636
+ critical: true,
637
+ regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[0-9A-Z]{16}\b/
638
+ },
639
+ {
640
+ id: "secret.aws_secret_key",
641
+ label: "AWS secret access key",
642
+ severity: "critical",
643
+ critical: true,
644
+ regex: /aws.{0,24}?(?:secret|private).{0,24}?["'=:\s]([A-Za-z0-9/+]{40})\b/i,
645
+ group: 1
646
+ },
647
+ {
648
+ id: "secret.github_token",
649
+ label: "GitHub token",
650
+ severity: "critical",
651
+ critical: true,
652
+ regex: /\bgh[posru]_[A-Za-z0-9]{36,}\b/
653
+ },
654
+ {
655
+ id: "secret.github_pat",
656
+ label: "GitHub fine-grained PAT",
657
+ severity: "critical",
658
+ critical: true,
659
+ regex: /\bgithub_pat_[A-Za-z0-9_]{22,}\b/
660
+ },
661
+ {
662
+ id: "secret.slack_token",
663
+ label: "Slack token",
664
+ severity: "critical",
665
+ critical: true,
666
+ regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/
667
+ },
668
+ {
669
+ id: "secret.google_api_key",
670
+ label: "Google API key",
671
+ severity: "high",
672
+ regex: /\bAIza[0-9A-Za-z_-]{35}\b/
673
+ },
674
+ {
675
+ id: "secret.llm_key",
676
+ label: "LLM provider API key",
677
+ severity: "critical",
678
+ critical: true,
679
+ regex: /\bsk-(?:ant-|proj-|live-)?[A-Za-z0-9_-]{20,}\b/
680
+ },
681
+ {
682
+ id: "secret.stripe_key",
683
+ label: "Stripe live key",
684
+ severity: "critical",
685
+ critical: true,
686
+ regex: /\b[sr]k_live_[A-Za-z0-9]{16,}\b/
687
+ },
688
+ {
689
+ id: "secret.private_key",
690
+ label: "Private key (PEM)",
691
+ severity: "critical",
692
+ critical: true,
693
+ regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/
694
+ },
695
+ {
696
+ id: "secret.jwt",
697
+ label: "JSON Web Token",
698
+ severity: "high",
699
+ regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/
700
+ },
701
+ {
702
+ id: "secret.bearer",
703
+ label: "Bearer token",
704
+ severity: "high",
705
+ regex: /\bbearer\s+([A-Za-z0-9._-]{16,})\b/i,
706
+ group: 1
707
+ },
708
+ {
709
+ id: "secret.assignment",
710
+ label: "Hardcoded credential",
711
+ severity: "medium",
712
+ regex: /(?:password|passwd|pwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)["']?\s*[:=]\s*["']?([^\s"']{8,})/i,
713
+ group: 1
714
+ }
715
+ ];
716
+ var secretsDetector = {
717
+ id: "secrets",
718
+ category: "secret",
719
+ // `ctx.fingerprint` is injected by the engine (salted SHA-256 on the server,
720
+ // a cosmetic hash in the universal build); scanSecrets falls back to the
721
+ // cosmetic default when it is unset.
722
+ inspect: (ctx) => scanSecrets(ctx.value, SECRET_PATTERNS, ctx.fingerprint)
723
+ };
724
+ var DETECTORS = [
725
+ secretsDetector,
726
+ piiDetector,
727
+ destructiveDetector,
728
+ injectionDetector,
729
+ jailbreakDetector,
730
+ outputInjectionDetector,
731
+ egressDetector
732
+ ];
733
+ [...DETECTORS, abuseDetector];
734
+ [...DETECTORS, cardCapabilityDetector];
735
+ var SEVERITY_POINTS = {
736
+ low: 8,
737
+ medium: 18,
738
+ high: 35,
739
+ critical: 55
740
+ };
741
+ var DECAY = 0.6;
742
+ var CRITICAL_FLOOR = 90;
743
+ function clamp(n, min, max) {
744
+ return Math.min(max, Math.max(min, n));
745
+ }
746
+ function pointsFor(severity) {
747
+ return SEVERITY_POINTS[severity];
748
+ }
749
+ function combine(base, signals) {
750
+ const points = signals.map((s) => s.points).sort((a, b2) => b2 - a);
751
+ const contribution = Math.min(
752
+ points.reduce((acc, p, i) => acc + p * DECAY ** i, 0),
753
+ 100
754
+ );
755
+ const b = clamp(base, 0, 100) / 100;
756
+ let final = 100 * (1 - (1 - b) * (1 - contribution / 100));
757
+ if (signals.some((s) => s.critical)) {
758
+ final = Math.max(final, CRITICAL_FLOOR);
759
+ }
760
+ return clamp(Math.round(final), 0, 100);
761
+ }
762
+ var RISK_MAP = {
763
+ "github.read": 10,
764
+ "github.push": 40,
765
+ "gmail.send": 80,
766
+ "slack.post": 50,
767
+ "aws.delete": 100,
768
+ "db.query": 70,
769
+ // Inbound HTTP methods (the verify path scores requests as `inbound.<method>`).
770
+ // Reads are cheap; writes/deletes warrant a higher prior.
771
+ "inbound.get": 10,
772
+ "inbound.head": 5,
773
+ "inbound.options": 5,
774
+ "inbound.post": 35,
775
+ "inbound.put": 40,
776
+ "inbound.patch": 40,
777
+ "inbound.delete": 60
778
+ };
779
+ var RISK_PREFIX_MAP = {
780
+ "aws.": 90,
781
+ "github.": 30,
782
+ "gmail.": 70,
783
+ "inbound.": 25
784
+ };
785
+ var DEFAULT_RISK = 50;
786
+ function calculateRisk(toolName) {
787
+ const tool = toolName.trim().toLowerCase();
788
+ const exact = RISK_MAP[tool];
789
+ if (exact !== void 0) return exact;
790
+ let best = null;
791
+ let bestLen = -1;
792
+ for (const [prefix, score] of Object.entries(RISK_PREFIX_MAP)) {
793
+ if (tool.startsWith(prefix) && prefix.length > bestLen) {
794
+ best = score;
795
+ bestLen = prefix.length;
796
+ }
797
+ }
798
+ return best ?? DEFAULT_RISK;
799
+ }
800
+ var TOOL_NAME_LOCATION = "(tool_name)";
801
+ var PAYLOAD_LOCATION = "(payload)";
802
+ var ENCODED_SECRET_MASK = "\u2039redacted:obfuscation.encoded_secret\u203A";
803
+ var UNIVERSAL_DEPS = {
804
+ fingerprint: cosmeticFingerprint,
805
+ makeVariantScanner: makeCanonicalVariantScanner
806
+ };
807
+ function runVariantDetectors(ctx, detectors, scanVariants) {
808
+ const signals = [];
809
+ const hitKinds = [];
810
+ let secretInVariant = false;
811
+ for (const variant of scanVariants(ctx.value)) {
812
+ const result = runDetectors(
813
+ { ...ctx, path: `${ctx.path} (${variant.kind})`, value: variant.value },
814
+ detectors
815
+ );
816
+ if (result.signals.length > 0) {
817
+ signals.push(...result.signals);
818
+ hitKinds.push(variant.kind);
819
+ }
820
+ if (result.redactions.length > 0) secretInVariant = true;
821
+ }
822
+ return { signals, secretInVariant, hitKinds };
823
+ }
824
+ function obfuscationSignal(location, hitKinds) {
825
+ return {
826
+ detector: "obfuscation.encoded_payload",
827
+ category: "injection",
828
+ severity: "medium",
829
+ label: "Obfuscated content",
830
+ points: pointsFor("medium"),
831
+ location,
832
+ evidence: `signals surfaced only after ${[...new Set(hitKinds)].join(", ")}`
833
+ };
834
+ }
835
+ function runDetectors(ctx, detectors) {
836
+ const scanCtx = ctx.value.length > LIMITS.maxLeafScan ? { ...ctx, value: ctx.value.slice(0, LIMITS.maxLeafScan) } : ctx;
837
+ const signals = [];
838
+ const redactions = [];
839
+ for (const detector of detectors) {
840
+ for (const match of detector.inspect(scanCtx)) {
841
+ signals.push({
842
+ detector: match.detector,
843
+ category: match.category,
844
+ severity: match.severity,
845
+ label: match.label,
846
+ points: pointsFor(match.severity),
847
+ location: ctx.path,
848
+ evidence: match.evidence,
849
+ ...match.critical ? { critical: true } : {}
850
+ });
851
+ if (match.redact) redactions.push(match.redact);
852
+ }
853
+ }
854
+ return { signals, redactions };
855
+ }
856
+ var CATEGORY_ORDER = {
857
+ secret: 0,
858
+ bot_spoof: 1,
859
+ supply_chain: 2,
860
+ destructive: 3,
861
+ injection: 4,
862
+ rate_abuse: 5,
863
+ pii: 6,
864
+ egress: 7
865
+ };
866
+ function finalize(signals) {
867
+ const seen = /* @__PURE__ */ new Set();
868
+ const unique = [];
869
+ for (const s of signals) {
870
+ const key = `${s.detector}|${s.location}`;
871
+ if (seen.has(key)) continue;
872
+ seen.add(key);
873
+ unique.push(s);
874
+ }
875
+ unique.sort(
876
+ (a, b) => b.points - a.points || CATEGORY_ORDER[a.category] - CATEGORY_ORDER[b.category] || a.location.localeCompare(b.location)
877
+ );
878
+ return unique.slice(0, LIMITS.maxSignals);
879
+ }
880
+ function inspectContent(toolName, args, detectors = DETECTORS, phase = "request", deps = UNIVERSAL_DEPS) {
881
+ const normalizedTool = canonicalize(toolName).trim().toLowerCase();
882
+ const collected = [];
883
+ const state = { leaves: 0, bytes: 0, truncated: false };
884
+ const scanVariants = deps.makeVariantScanner();
885
+ collected.push(
886
+ ...runDetectors(
887
+ {
888
+ toolName: normalizedTool,
889
+ path: TOOL_NAME_LOCATION,
890
+ value: normalizedTool,
891
+ phase,
892
+ fingerprint: deps.fingerprint,
893
+ redactPii: deps.redactPii
894
+ },
895
+ detectors
896
+ ).signals
897
+ );
898
+ const redactedPayload = transformStrings(
899
+ args,
900
+ (path, value) => {
901
+ const ctx = {
902
+ toolName: normalizedTool,
903
+ path,
904
+ value,
905
+ phase,
906
+ fingerprint: deps.fingerprint,
907
+ redactPii: deps.redactPii
908
+ };
909
+ const { signals, redactions } = runDetectors(ctx, detectors);
910
+ collected.push(...signals);
911
+ const variants = runVariantDetectors(ctx, detectors, scanVariants);
912
+ if (variants.hitKinds.length > 0) {
913
+ collected.push(...variants.signals);
914
+ collected.push(obfuscationSignal(path, variants.hitKinds));
915
+ }
916
+ if (variants.secretInVariant) return ENCODED_SECRET_MASK;
917
+ return applyRedactions(value, redactions);
918
+ },
919
+ state
920
+ );
921
+ if (state.truncated) {
922
+ collected.push({
923
+ detector: "egress.oversized_payload",
924
+ category: "egress",
925
+ severity: "medium",
926
+ label: "Oversized payload",
927
+ points: pointsFor("medium"),
928
+ location: PAYLOAD_LOCATION,
929
+ evidence: "payload too large to fully inspect"
930
+ });
931
+ }
932
+ return { signals: finalize(collected), redactedPayload };
933
+ }
934
+ function scoreToolCall(toolName, args, detectors = DETECTORS, phase = "request", deps = UNIVERSAL_DEPS) {
935
+ const { signals, redactedPayload } = inspectContent(
936
+ toolName,
937
+ args,
938
+ detectors,
939
+ phase,
940
+ deps
941
+ );
942
+ const base = calculateRisk(canonicalize(toolName));
943
+ const score = combine(base, signals);
944
+ return { score, base, signals, redactedPayload };
945
+ }
946
+ var DEFAULT_DENY_RISK = 75;
947
+ var DEFAULT_HOLD_RISK = 50;
948
+ function localDecision(score, signals) {
949
+ if (signals.some((s) => s.category === "secret")) {
950
+ return {
951
+ decision: "deny",
952
+ reason: "A live secret was detected in the payload.",
953
+ rule: "deny-live-secret"
954
+ };
955
+ }
956
+ if (signals.some((s) => s.category === "destructive")) {
957
+ return {
958
+ decision: "deny",
959
+ reason: "A destructive operation was detected.",
960
+ rule: "deny-destructive"
961
+ };
962
+ }
963
+ if (score >= DEFAULT_DENY_RISK) {
964
+ return {
965
+ decision: "deny",
966
+ reason: `Risk score ${score} is at or above the ${DEFAULT_DENY_RISK} threshold.`,
967
+ rule: `risk>=${DEFAULT_DENY_RISK}`
968
+ };
969
+ }
970
+ if (score >= DEFAULT_HOLD_RISK && signals.length > 0) {
971
+ return {
972
+ decision: "hold",
973
+ reason: `Risk score ${score} is in the review band (${DEFAULT_HOLD_RISK} to ${DEFAULT_DENY_RISK - 1}). A human should approve this call.`,
974
+ rule: `risk>=${DEFAULT_HOLD_RISK}`
975
+ };
976
+ }
977
+ return {
978
+ decision: "allow",
979
+ reason: "No default policy rule matched.",
980
+ rule: null
981
+ };
982
+ }
983
+ function toPublicSignal(s) {
984
+ return {
985
+ detector: s.detector,
986
+ category: s.category,
987
+ severity: s.severity,
988
+ label: s.label,
989
+ location: s.location,
990
+ evidence: s.evidence
991
+ };
992
+ }
993
+ function inspect(tool, args = {}) {
994
+ const { score, signals, redactedPayload } = scoreToolCall(tool, args);
995
+ const verdict = localDecision(score, signals);
996
+ return {
997
+ decision: verdict.decision,
998
+ risk: score,
999
+ reason: verdict.reason,
1000
+ signals: signals.map(toPublicSignal),
1001
+ redactedPayload
1002
+ };
1003
+ }
1004
+ function inspectPrompt(text) {
1005
+ const { score, signals, redactedPayload } = scoreToolCall("model.prompt", {
1006
+ prompt: text
1007
+ });
1008
+ const verdict = localDecision(score, signals);
1009
+ return {
1010
+ decision: verdict.decision,
1011
+ risk: score,
1012
+ reason: verdict.reason,
1013
+ signals: signals.map(toPublicSignal),
1014
+ redactedPayload
1015
+ };
1016
+ }
1017
+ function inspectCompletion(text) {
1018
+ const { score, signals, redactedPayload } = scoreToolCall(
1019
+ "model.completion",
1020
+ { completion: text },
1021
+ void 0,
1022
+ "result"
1023
+ );
1024
+ const verdict = localDecision(score, signals);
1025
+ return {
1026
+ decision: verdict.decision,
1027
+ risk: score,
1028
+ reason: verdict.reason,
1029
+ signals: signals.map(toPublicSignal),
1030
+ redactedPayload
1031
+ };
1032
+ }
1033
+
1034
+ // ../sdk/dist/chunk-KPEPLFMX.js
1035
+ var AxioRankError = class extends Error {
1036
+ constructor(message) {
1037
+ super(message);
1038
+ this.name = "AxioRankError";
1039
+ }
1040
+ };
1041
+ var AxioRankDeniedError = class extends AxioRankError {
1042
+ result;
1043
+ constructor(result) {
1044
+ super(`AxioRank denied tool call: ${result.reason}`);
1045
+ this.name = "AxioRankDeniedError";
1046
+ this.result = result;
1047
+ }
1048
+ };
1049
+
1050
+ // src/guard-result.ts
1051
+ var ZERO_USAGE = {
1052
+ inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
1053
+ outputTokens: { total: 0, text: 0, reasoning: 0 }
1054
+ };
1055
+ function deniedError(verdict) {
1056
+ const result = {
1057
+ decision: "deny",
1058
+ reason: verdict.reason,
1059
+ risk: verdict.risk,
1060
+ auditLogId: "",
1061
+ signals: verdict.signals
1062
+ };
1063
+ return new AxioRankDeniedError(result);
1064
+ }
1065
+ function blockedGenerateResult(refusal) {
1066
+ return {
1067
+ content: [{ type: "text", text: refusal }],
1068
+ finishReason: { unified: "content-filter", raw: void 0 },
1069
+ usage: ZERO_USAGE,
1070
+ warnings: []
1071
+ };
1072
+ }
1073
+ function redactCompletionContent(content, redactedText) {
1074
+ const out = [];
1075
+ let replaced = false;
1076
+ for (const part of content) {
1077
+ if (part.type === "text") {
1078
+ if (!replaced) {
1079
+ out.push({ type: "text", text: redactedText });
1080
+ replaced = true;
1081
+ }
1082
+ continue;
1083
+ }
1084
+ out.push(part);
1085
+ }
1086
+ if (!replaced) out.push({ type: "text", text: redactedText });
1087
+ return out;
1088
+ }
1089
+ function blockCompletionContent(refusal) {
1090
+ return [{ type: "text", text: refusal }];
1091
+ }
1092
+ function stripToolCalls(content, blockedIds, note) {
1093
+ const out = content.filter(
1094
+ (part) => !(part.type === "tool-call" && blockedIds.has(part.toolCallId))
1095
+ );
1096
+ if (note) out.push({ type: "text", text: note });
1097
+ return out;
1098
+ }
1099
+ function refusalStream(refusal) {
1100
+ const id = "axiorank-refusal";
1101
+ return new ReadableStream({
1102
+ start(controller) {
1103
+ controller.enqueue({ type: "stream-start", warnings: [] });
1104
+ controller.enqueue({ type: "text-start", id });
1105
+ controller.enqueue({ type: "text-delta", id, delta: refusal });
1106
+ controller.enqueue({ type: "text-end", id });
1107
+ controller.enqueue({
1108
+ type: "finish",
1109
+ usage: ZERO_USAGE,
1110
+ finishReason: { unified: "content-filter", raw: void 0 }
1111
+ });
1112
+ controller.close();
1113
+ }
1114
+ });
1115
+ }
1116
+
1117
+ // src/inspect-client.ts
1118
+ function createInspector(opts) {
1119
+ return opts.mode === "hosted" ? createHostedInspector(opts) : createLocalInspector();
1120
+ }
1121
+ function verdictFromLocal(phase, r) {
1122
+ return {
1123
+ phase,
1124
+ decision: r.decision,
1125
+ blocked: r.decision !== "allow",
1126
+ reason: r.reason,
1127
+ risk: r.risk,
1128
+ signals: r.signals
1129
+ };
1130
+ }
1131
+ function createLocalInspector() {
1132
+ return {
1133
+ async prompt(text) {
1134
+ return verdictFromLocal("prompt", inspectPrompt(text));
1135
+ },
1136
+ async completion(text) {
1137
+ return verdictFromLocal("completion", inspectCompletion(text));
1138
+ },
1139
+ async toolCall(tool, args) {
1140
+ return verdictFromLocal("tool-call", inspect(tool, args));
1141
+ }
1142
+ };
1143
+ }
1144
+ function verdictFromHosted(phase, j) {
1145
+ const decision = j.decision === "deny" ? "deny" : j.decision === "hold" ? "hold" : j.transform === "redact" ? "redact" : "allow";
1146
+ return {
1147
+ phase,
1148
+ decision,
1149
+ blocked: decision === "deny" || decision === "hold",
1150
+ reason: j.reason ?? "",
1151
+ risk: typeof j.risk === "number" ? j.risk : 0,
1152
+ signals: j.signals ?? [],
1153
+ redactedText: j.transform === "redact" ? j.redactedText ?? "" : void 0
1154
+ };
1155
+ }
1156
+ function failVerdict(phase, opts, reason) {
1157
+ const blocked = !opts.failOpen;
1158
+ return {
1159
+ phase,
1160
+ decision: blocked ? "deny" : "allow",
1161
+ blocked,
1162
+ reason: `AxioRank ${reason} (${opts.failOpen ? "fail-open" : "fail-closed"})`,
1163
+ risk: 0,
1164
+ signals: []
1165
+ };
1166
+ }
1167
+ function createHostedInspector(opts) {
1168
+ const url = `${opts.baseUrl}/api/gateway/tool-call`;
1169
+ const post = async (phase, body) => {
1170
+ const controller = new AbortController();
1171
+ const timer = setTimeout(() => controller.abort(), opts.timeoutMs);
1172
+ try {
1173
+ const res = await opts.fetch(url, {
1174
+ method: "POST",
1175
+ headers: {
1176
+ "content-type": "application/json",
1177
+ authorization: `Bearer ${opts.apiKey}`
1178
+ },
1179
+ body: JSON.stringify(body),
1180
+ signal: controller.signal
1181
+ });
1182
+ if (!res.ok) return failVerdict(phase, opts, `returned ${res.status}`);
1183
+ const json = await res.json();
1184
+ return verdictFromHosted(phase, json);
1185
+ } catch {
1186
+ return failVerdict(phase, opts, "was unreachable");
1187
+ } finally {
1188
+ clearTimeout(timer);
1189
+ }
1190
+ };
1191
+ return {
1192
+ prompt(text, meta) {
1193
+ const model = meta.model ?? opts.model;
1194
+ return post("prompt", {
1195
+ tool: model ?? "model",
1196
+ phase: "prompt",
1197
+ promptText: text,
1198
+ model,
1199
+ traceId: meta.traceId,
1200
+ source: "sdk"
1201
+ });
1202
+ },
1203
+ completion(text, meta) {
1204
+ const model = meta.model ?? opts.model;
1205
+ return post("completion", {
1206
+ tool: model ?? "model",
1207
+ phase: "completion",
1208
+ completionText: text,
1209
+ model,
1210
+ traceId: meta.traceId,
1211
+ source: "sdk"
1212
+ });
1213
+ },
1214
+ toolCall(tool, args, meta) {
1215
+ return post("tool-call", {
1216
+ tool,
1217
+ arguments: args,
1218
+ model: meta.model ?? opts.model,
1219
+ traceId: meta.traceId,
1220
+ source: "sdk"
1221
+ });
1222
+ }
1223
+ };
1224
+ }
1225
+
1226
+ // src/stream.ts
1227
+ async function guardStream(args, opts, inspector) {
1228
+ const meta = { model: opts.model ?? args.model.modelId };
1229
+ if (opts.inspectPrompts) {
1230
+ const verdict = await inspector.prompt(
1231
+ extractPromptText(args.params.prompt),
1232
+ meta
1233
+ );
1234
+ opts.onDecision?.("prompt", verdict);
1235
+ if (verdict.blocked) {
1236
+ if (opts.onDeny === "throw") throw deniedError(verdict);
1237
+ return { stream: refusalStream(opts.refusal) };
1238
+ }
1239
+ }
1240
+ const result = await args.doStream();
1241
+ const transform = opts.stream === "passthrough" ? passthroughGuard(opts, inspector, meta) : bufferGuard(opts, inspector, meta);
1242
+ return { ...result, stream: result.stream.pipeThrough(transform) };
1243
+ }
1244
+ function collectText(parts) {
1245
+ return parts.flatMap((part) => part.type === "text-delta" ? [part.delta] : []).join("");
1246
+ }
1247
+ function bufferGuard(opts, inspector, meta) {
1248
+ const parts = [];
1249
+ return new TransformStream({
1250
+ transform(chunk) {
1251
+ parts.push(chunk);
1252
+ },
1253
+ async flush(controller) {
1254
+ let redactedText;
1255
+ let blockCompletion = false;
1256
+ if (opts.inspectCompletions) {
1257
+ const text = collectText(parts);
1258
+ if (text) {
1259
+ const verdict = await inspector.completion(text, meta);
1260
+ opts.onDecision?.("completion", verdict);
1261
+ if (verdict.blocked) {
1262
+ if (opts.onDeny === "throw") {
1263
+ controller.error(deniedError(verdict));
1264
+ return;
1265
+ }
1266
+ blockCompletion = true;
1267
+ } else if (verdict.decision === "redact" && verdict.redactedText !== void 0) {
1268
+ redactedText = verdict.redactedText;
1269
+ }
1270
+ }
1271
+ }
1272
+ const blockedIds = /* @__PURE__ */ new Set();
1273
+ if (opts.inspectToolCalls) {
1274
+ for (const part of parts) {
1275
+ if (part.type !== "tool-call") continue;
1276
+ const verdict = await inspector.toolCall(
1277
+ part.toolName,
1278
+ parseToolInput(part.input),
1279
+ meta
1280
+ );
1281
+ opts.onDecision?.("tool-call", verdict);
1282
+ if (verdict.blocked) {
1283
+ if (opts.onDeny === "throw") {
1284
+ controller.error(deniedError(verdict));
1285
+ return;
1286
+ }
1287
+ blockedIds.add(part.toolCallId);
1288
+ }
1289
+ }
1290
+ }
1291
+ emitBuffered(
1292
+ controller,
1293
+ parts,
1294
+ blockCompletion ? opts.refusal : redactedText,
1295
+ blockedIds
1296
+ );
1297
+ }
1298
+ });
1299
+ }
1300
+ function emitBuffered(controller, parts, finalText, blockedIds) {
1301
+ let emittedText = false;
1302
+ for (const part of parts) {
1303
+ if (part.type === "text-start" || part.type === "text-delta" || part.type === "text-end") {
1304
+ if (finalText === void 0) {
1305
+ controller.enqueue(part);
1306
+ continue;
1307
+ }
1308
+ if (!emittedText) {
1309
+ const id = part.id;
1310
+ controller.enqueue({ type: "text-start", id });
1311
+ controller.enqueue({ type: "text-delta", id, delta: finalText });
1312
+ controller.enqueue({ type: "text-end", id });
1313
+ emittedText = true;
1314
+ }
1315
+ continue;
1316
+ }
1317
+ if (part.type === "tool-call" && blockedIds.has(part.toolCallId)) continue;
1318
+ if ((part.type === "tool-input-start" || part.type === "tool-input-delta" || part.type === "tool-input-end") && blockedIds.has(part.id)) {
1319
+ continue;
1320
+ }
1321
+ controller.enqueue(part);
1322
+ }
1323
+ }
1324
+ function passthroughGuard(opts, inspector, meta) {
1325
+ let text = "";
1326
+ const toolCalls = [];
1327
+ return new TransformStream({
1328
+ transform(chunk, controller) {
1329
+ controller.enqueue(chunk);
1330
+ if (chunk.type === "text-delta") text += chunk.delta;
1331
+ else if (chunk.type === "tool-call") {
1332
+ toolCalls.push({
1333
+ tool: chunk.toolName,
1334
+ args: parseToolInput(chunk.input)
1335
+ });
1336
+ }
1337
+ },
1338
+ async flush(controller) {
1339
+ if (opts.inspectCompletions && text) {
1340
+ const verdict = await inspector.completion(text, meta);
1341
+ opts.onDecision?.("completion", verdict);
1342
+ if (verdict.blocked && opts.onDeny === "throw") {
1343
+ controller.error(deniedError(verdict));
1344
+ return;
1345
+ }
1346
+ }
1347
+ if (opts.inspectToolCalls) {
1348
+ for (const call of toolCalls) {
1349
+ const verdict = await inspector.toolCall(call.tool, call.args, meta);
1350
+ opts.onDecision?.("tool-call", verdict);
1351
+ if (verdict.blocked && opts.onDeny === "throw") {
1352
+ controller.error(deniedError(verdict));
1353
+ return;
1354
+ }
1355
+ }
1356
+ }
1357
+ }
1358
+ });
1359
+ }
1360
+
1361
+ // src/types.ts
1362
+ var DEFAULT_BASE_URL = "https://app.axiorank.com";
1363
+ var DEFAULT_REFUSAL = "[blocked by AxioRank]";
1364
+ function resolveOptions(o = {}) {
1365
+ const apiKey = o.apiKey ?? process.env.AXIORANK_API_KEY ?? process.env.AXIORANK_KEY;
1366
+ const baseUrl = (o.baseUrl ?? process.env.AXIORANK_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
1367
+ const requested = o.mode ?? (apiKey ? "hosted" : "local");
1368
+ const mode = requested === "hosted" && !apiKey ? "local" : requested;
1369
+ return {
1370
+ mode,
1371
+ apiKey,
1372
+ baseUrl,
1373
+ onDeny: o.onDeny ?? "block",
1374
+ failOpen: o.failOpen ?? true,
1375
+ inspectPrompts: o.inspectPrompts ?? true,
1376
+ inspectCompletions: o.inspectCompletions ?? true,
1377
+ inspectToolCalls: o.inspectToolCalls ?? true,
1378
+ stream: o.stream ?? "buffer",
1379
+ refusal: o.refusal ?? DEFAULT_REFUSAL,
1380
+ onDecision: o.onDecision,
1381
+ timeoutMs: o.timeoutMs ?? 1e4,
1382
+ model: o.model,
1383
+ fetch: o.fetch ?? globalThis.fetch
1384
+ };
1385
+ }
1386
+
1387
+ // src/middleware.ts
1388
+ function createAxioRankMiddleware(options = {}) {
1389
+ const opts = resolveOptions(options);
1390
+ const inspector = createInspector(opts);
1391
+ return {
1392
+ specificationVersion: "v4",
1393
+ async wrapGenerate({ doGenerate, params, model }) {
1394
+ const meta = { model: opts.model ?? model.modelId };
1395
+ if (opts.inspectPrompts) {
1396
+ const verdict = await inspector.prompt(
1397
+ extractPromptText(params.prompt),
1398
+ meta
1399
+ );
1400
+ opts.onDecision?.("prompt", verdict);
1401
+ if (verdict.blocked) {
1402
+ if (opts.onDeny === "throw") throw deniedError(verdict);
1403
+ return blockedGenerateResult(opts.refusal);
1404
+ }
1405
+ }
1406
+ const result = await doGenerate();
1407
+ let content = result.content;
1408
+ if (opts.inspectCompletions) {
1409
+ const text = extractCompletionText(content);
1410
+ if (text) {
1411
+ const verdict = await inspector.completion(text, meta);
1412
+ opts.onDecision?.("completion", verdict);
1413
+ if (verdict.blocked) {
1414
+ if (opts.onDeny === "throw") throw deniedError(verdict);
1415
+ content = blockCompletionContent(opts.refusal);
1416
+ } else if (verdict.decision === "redact" && verdict.redactedText !== void 0) {
1417
+ content = redactCompletionContent(content, verdict.redactedText);
1418
+ }
1419
+ }
1420
+ }
1421
+ if (opts.inspectToolCalls) {
1422
+ const calls = extractToolCalls(content);
1423
+ if (calls.length > 0) {
1424
+ const blockedIds = /* @__PURE__ */ new Set();
1425
+ for (const call of calls) {
1426
+ const verdict = await inspector.toolCall(
1427
+ call.toolName,
1428
+ parseToolInput(call.input),
1429
+ meta
1430
+ );
1431
+ opts.onDecision?.("tool-call", verdict);
1432
+ if (verdict.blocked) {
1433
+ if (opts.onDeny === "throw") throw deniedError(verdict);
1434
+ blockedIds.add(call.toolCallId);
1435
+ }
1436
+ }
1437
+ if (blockedIds.size > 0) {
1438
+ content = stripToolCalls(
1439
+ content,
1440
+ blockedIds,
1441
+ `${opts.refusal} (${blockedIds.size} tool call(s) removed)`
1442
+ );
1443
+ }
1444
+ }
1445
+ }
1446
+ return content === result.content ? result : { ...result, content };
1447
+ },
1448
+ async wrapStream({ doStream, params, model }) {
1449
+ return guardStream({ doStream, params, model }, opts, inspector);
1450
+ }
1451
+ };
1452
+ }
1453
+
1454
+ exports.AxioRankDeniedError = AxioRankDeniedError;
1455
+ exports.createAxioRankMiddleware = createAxioRankMiddleware;
1456
+ //# sourceMappingURL=index.cjs.map
1457
+ //# sourceMappingURL=index.cjs.map