@unorouter/verify-core 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.
Files changed (79) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +36 -0
  3. package/dist/handshake.d.ts +23 -0
  4. package/dist/handshake.d.ts.map +1 -0
  5. package/dist/handshake.js +90 -0
  6. package/dist/handshake.js.map +1 -0
  7. package/dist/highlight.d.ts +8 -0
  8. package/dist/highlight.d.ts.map +1 -0
  9. package/dist/highlight.js +63 -0
  10. package/dist/highlight.js.map +1 -0
  11. package/dist/index.d.ts +7 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +7 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal/utils.d.ts +6 -0
  16. package/dist/internal/utils.d.ts.map +1 -0
  17. package/dist/internal/utils.js +19 -0
  18. package/dist/internal/utils.js.map +1 -0
  19. package/dist/models.d.ts +5 -0
  20. package/dist/models.d.ts.map +1 -0
  21. package/dist/models.js +54 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/nonce.d.ts +4 -0
  24. package/dist/nonce.d.ts.map +1 -0
  25. package/dist/nonce.js +12 -0
  26. package/dist/nonce.js.map +1 -0
  27. package/dist/patterns.d.ts +16 -0
  28. package/dist/patterns.d.ts.map +1 -0
  29. package/dist/patterns.js +63 -0
  30. package/dist/patterns.js.map +1 -0
  31. package/dist/probes.d.ts +10 -0
  32. package/dist/probes.d.ts.map +1 -0
  33. package/dist/probes.js +54 -0
  34. package/dist/probes.js.map +1 -0
  35. package/dist/providers/anthropic.d.ts +3 -0
  36. package/dist/providers/anthropic.d.ts.map +1 -0
  37. package/dist/providers/anthropic.js +70 -0
  38. package/dist/providers/anthropic.js.map +1 -0
  39. package/dist/providers/config.d.ts +34 -0
  40. package/dist/providers/config.d.ts.map +1 -0
  41. package/dist/providers/config.js +14 -0
  42. package/dist/providers/config.js.map +1 -0
  43. package/dist/providers/gemini.d.ts +3 -0
  44. package/dist/providers/gemini.d.ts.map +1 -0
  45. package/dist/providers/gemini.js +58 -0
  46. package/dist/providers/gemini.js.map +1 -0
  47. package/dist/providers/openai.d.ts +3 -0
  48. package/dist/providers/openai.d.ts.map +1 -0
  49. package/dist/providers/openai.js +56 -0
  50. package/dist/providers/openai.js.map +1 -0
  51. package/dist/rules.d.ts +6 -0
  52. package/dist/rules.d.ts.map +1 -0
  53. package/dist/rules.js +28 -0
  54. package/dist/rules.js.map +1 -0
  55. package/dist/runner.d.ts +12 -0
  56. package/dist/runner.d.ts.map +1 -0
  57. package/dist/runner.js +256 -0
  58. package/dist/runner.js.map +1 -0
  59. package/dist/signals.d.ts +11 -0
  60. package/dist/signals.d.ts.map +1 -0
  61. package/dist/signals.js +64 -0
  62. package/dist/signals.js.map +1 -0
  63. package/dist/substitution.d.ts +24 -0
  64. package/dist/substitution.d.ts.map +1 -0
  65. package/dist/substitution.js +35 -0
  66. package/dist/substitution.js.map +1 -0
  67. package/dist/transport.d.ts +17 -0
  68. package/dist/transport.d.ts.map +1 -0
  69. package/dist/transport.js +79 -0
  70. package/dist/transport.js.map +1 -0
  71. package/dist/types.d.ts +43 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +2 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/verdict.d.ts +19 -0
  76. package/dist/verdict.d.ts.map +1 -0
  77. package/dist/verdict.js +84 -0
  78. package/dist/verdict.js.map +1 -0
  79. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @unorouter/verify-core
2
+
3
+ Authenticity and capability checks for AI API relays: given a `base_url`,
4
+ `api_key` and `model`, decide whether the endpoint really serves the Claude /
5
+ GPT / Gemini model it claims.
6
+
7
+ Extracted from [unorouter](https://unorouter.com)'s model tester so the same
8
+ engine runs in the browser, on a server, and inside our sync pipeline.
9
+
10
+ ## Design
11
+
12
+ - **Isomorphic.** Web-standard APIs only (`fetch`, `TextEncoder`,
13
+ `AbortSignal`); no `node:` imports. The HTTP call itself is injected as a
14
+ `TransportFn`, so a browser can route around CORS and a server can route
15
+ through an SSRF-safe fetch.
16
+ - **Opt-in.** Providers and detectors have their own entry points, so a consumer
17
+ that only checks Anthropic never bundles the Gemini config.
18
+ - **Zero required dependencies.**
19
+
20
+ ## Use
21
+
22
+ ```ts
23
+ import { runVerification } from "@unorouter/verify-core";
24
+ import { anthropicConfig } from "@unorouter/verify-core/providers/anthropic";
25
+ ```
26
+
27
+ ## What it detects
28
+
29
+ `coding-tool` refusals (Kiro/Amazon Q wearing a Claude badge), scam pages,
30
+ CJK language leaks from a substituted Chinese model, response-mixing proxies,
31
+ foreign vendor identity, Claude tier substitution (opus billed, sonnet served),
32
+ and envelope-level model substitution.
33
+
34
+ ## License
35
+
36
+ AGPL-3.0-only.
@@ -0,0 +1,23 @@
1
+ import { type TransportFn } from "./transport";
2
+ import type { TransportMode, VerifyProvider } from "./types";
3
+ export type HandshakeOutcome = {
4
+ ok: true;
5
+ resolvedProvider: VerifyProvider;
6
+ mode: TransportMode;
7
+ status: number;
8
+ } | {
9
+ ok: false;
10
+ reason: "cors-needs-backend" | "unreachable" | "invalid-key" | "no-format";
11
+ status: number | null;
12
+ corsBlocked: boolean;
13
+ };
14
+ export declare function runHandshake(opts: {
15
+ provider: VerifyProvider;
16
+ baseUrl: string;
17
+ apiKey: string;
18
+ model: string;
19
+ mode: TransportMode;
20
+ timeoutMs: number;
21
+ transport?: TransportFn;
22
+ }): Promise<HandshakeOutcome>;
23
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../src/handshake.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GACxB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,gBAAgB,EAAE,cAAc,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EACJ,oBAAoB,GAAG,aAAa,GAAG,aAAa,GAAG,WAAW,CAAC;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAwDN,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiD5B"}
@@ -0,0 +1,90 @@
1
+ import { PROVIDER_CONFIGS } from "./providers/config";
2
+ import { probeTransport } from "./transport";
3
+ import { isTransientError } from "./signals";
4
+ const HANDSHAKE_PROMPT = "hi";
5
+ const HANDSHAKE_MAX_TOKENS = 1;
6
+ function classifyStatus(status) {
7
+ if (status >= 200 && status < 300)
8
+ return "ok";
9
+ if (status === 401 || status === 403)
10
+ return "auth";
11
+ if (isTransientError(`HTTP ${status}`))
12
+ return "transient";
13
+ if (status >= 400 && status < 500)
14
+ return "format";
15
+ return "transient";
16
+ }
17
+ async function tryFormat(args) {
18
+ const cfg = PROVIDER_CONFIGS[args.provider];
19
+ const built = cfg.buildRequest({
20
+ baseUrl: args.baseUrl,
21
+ apiKey: args.apiKey,
22
+ model: args.model,
23
+ prompt: HANDSHAKE_PROMPT,
24
+ maxTokens: HANDSHAKE_MAX_TOKENS,
25
+ direct: args.mode === "direct",
26
+ });
27
+ const res = await args.transport({
28
+ mode: args.mode,
29
+ url: built.url,
30
+ headers: built.headers,
31
+ reqBody: built.body,
32
+ timeoutMs: args.timeoutMs,
33
+ });
34
+ if (res.corsBlocked)
35
+ return { outcome: "cors", status: null, corsBlocked: true };
36
+ if (res.status === null)
37
+ return { outcome: "transient", status: null, corsBlocked: false };
38
+ return {
39
+ outcome: classifyStatus(res.status),
40
+ status: res.status,
41
+ corsBlocked: false,
42
+ };
43
+ }
44
+ export async function runHandshake(opts) {
45
+ const transport = opts.transport ?? probeTransport;
46
+ const order = opts.provider === "openai" ? ["openai"] : [opts.provider, "openai"];
47
+ let sawAuth = false;
48
+ let lastStatus = null;
49
+ for (const provider of order) {
50
+ const r = await tryFormat({ ...opts, provider, transport });
51
+ lastStatus = r.status;
52
+ if (r.outcome === "cors")
53
+ return {
54
+ ok: false,
55
+ reason: "cors-needs-backend",
56
+ status: null,
57
+ corsBlocked: true,
58
+ };
59
+ if (r.outcome === "ok")
60
+ return {
61
+ ok: true,
62
+ resolvedProvider: provider,
63
+ mode: opts.mode,
64
+ status: r.status,
65
+ };
66
+ if (r.outcome === "auth")
67
+ sawAuth = true;
68
+ }
69
+ if (sawAuth)
70
+ return {
71
+ ok: false,
72
+ reason: "invalid-key",
73
+ status: lastStatus,
74
+ corsBlocked: false,
75
+ };
76
+ if (lastStatus === null)
77
+ return {
78
+ ok: false,
79
+ reason: "unreachable",
80
+ status: null,
81
+ corsBlocked: false,
82
+ };
83
+ return {
84
+ ok: false,
85
+ reason: "no-format",
86
+ status: lastStatus,
87
+ corsBlocked: false,
88
+ };
89
+ }
90
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../src/handshake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { VerifyProvider } from "./types";
2
+ export type HighlightKind = "foreign" | "cjk" | "coding-tool" | "scam" | "home" | null;
3
+ export type HighlightSegment = {
4
+ text: string;
5
+ kind: HighlightKind;
6
+ };
7
+ export declare function highlightSpans(text: string, providerKind: VerifyProvider, probeLabel: string): HighlightSegment[];
8
+ //# sourceMappingURL=highlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,aAAa,GACvB,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAoBrE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,cAAc,EAC5B,UAAU,EAAE,MAAM,GACjB,gBAAgB,EAAE,CA6CpB"}
@@ -0,0 +1,63 @@
1
+ import { CJK_CHAR, CODING_TOOL_NAMES, CODING_TOOL_REFUSAL_PATTERNS, SCAM_PAGE_PATTERNS, } from "./patterns";
2
+ import { PROVIDER_CONFIGS } from "./providers/config";
3
+ function collectPhrase(lower, phrases, kind) {
4
+ const out = [];
5
+ for (const phrase of phrases) {
6
+ if (!phrase)
7
+ continue;
8
+ const needle = phrase.toLowerCase();
9
+ let from = 0;
10
+ for (;;) {
11
+ const idx = lower.indexOf(needle, from);
12
+ if (idx === -1)
13
+ break;
14
+ out.push({ start: idx, end: idx + needle.length, kind });
15
+ from = idx + needle.length;
16
+ }
17
+ }
18
+ return out;
19
+ }
20
+ export function highlightSpans(text, providerKind, probeLabel) {
21
+ if (!text)
22
+ return [];
23
+ const cfg = PROVIDER_CONFIGS[providerKind];
24
+ if (!cfg)
25
+ return [{ text, kind: null }];
26
+ const lower = text.toLowerCase();
27
+ const matches = [
28
+ ...collectPhrase(lower, CODING_TOOL_NAMES, "coding-tool"),
29
+ ...collectPhrase(lower, CODING_TOOL_REFUSAL_PATTERNS, "coding-tool"),
30
+ ...collectPhrase(lower, SCAM_PAGE_PATTERNS, "scam"),
31
+ ...collectPhrase(lower, cfg.foreignIdentityPatterns, "foreign"),
32
+ ...collectPhrase(lower, cfg.homeIdentityPatterns, "home"),
33
+ ...collectPhrase(lower, cfg.homeModelNamePatterns, "home"),
34
+ ];
35
+ if (probeLabel === "model-name")
36
+ matches.push(...collectPhrase(lower, cfg.cloudModelNamePatterns, "foreign"));
37
+ for (const m of text.matchAll(CJK_CHAR))
38
+ if (m.index !== undefined)
39
+ matches.push({ start: m.index, end: m.index + m[0].length, kind: "cjk" });
40
+ if (matches.length === 0)
41
+ return [{ text, kind: null }];
42
+ matches.sort((a, b) => a.start - b.start || b.end - a.end);
43
+ const merged = [];
44
+ let cursor = 0;
45
+ for (const m of matches) {
46
+ if (m.start < cursor)
47
+ continue;
48
+ merged.push(m);
49
+ cursor = m.end;
50
+ }
51
+ const segments = [];
52
+ let pos = 0;
53
+ for (const m of merged) {
54
+ if (m.start > pos)
55
+ segments.push({ text: text.slice(pos, m.start), kind: null });
56
+ segments.push({ text: text.slice(m.start, m.end), kind: m.kind });
57
+ pos = m.end;
58
+ }
59
+ if (pos < text.length)
60
+ segments.push({ text: text.slice(pos), kind: null });
61
+ return segments;
62
+ }
63
+ //# sourceMappingURL=highlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.js","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./types";
2
+ export * from "./rules";
3
+ export { runVerification } from "./runner";
4
+ export { normalizeProbeBaseUrl } from "./providers/config";
5
+ export type { ProviderConfig, ProbeRequestArgs, BuiltRequest, ProbeMeta, } from "./providers/config";
6
+ export type { TransportFn, TransportArgs, TransportResult } from "./transport";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Root export: types, the runner and the rule registry. Providers and detectors
2
+ // have their own entry points so a consumer only bundles what it enables.
3
+ export * from "./types";
4
+ export * from "./rules";
5
+ export { runVerification } from "./runner";
6
+ export { normalizeProbeBaseUrl } from "./providers/config";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0EAA0E;AAC1E,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function isRecord(v: unknown): v is Record<string, unknown>;
2
+ export declare function rec(v: unknown): Record<string, unknown> | undefined;
3
+ export declare function isOneOf<const T extends readonly unknown[]>(values: T): (v: unknown) => v is T[number];
4
+ export declare function errMessage(err: unknown): string;
5
+ export declare function sleep(ms: number): Promise<void>;
6
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAIA,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAEnE;AAED,wBAAgB,OAAO,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,OAAO,EAAE,EACxD,MAAM,EAAE,CAAC,GACR,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAEhC;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
@@ -0,0 +1,19 @@
1
+ // Vendored from unorouter's src/lib/utils/base.ts so the package stays
2
+ // dependency-free. Four helpers, copied verbatim; keep them in step if the
3
+ // originals change.
4
+ export function isRecord(v) {
5
+ return !!v && typeof v === "object" && !Array.isArray(v);
6
+ }
7
+ export function rec(v) {
8
+ return isRecord(v) ? v : undefined;
9
+ }
10
+ export function isOneOf(values) {
11
+ return (v) => values.some((x) => x === v);
12
+ }
13
+ export function errMessage(err) {
14
+ return err instanceof Error ? err.message : String(err);
15
+ }
16
+ export function sleep(ms) {
17
+ return new Promise((r) => setTimeout(r, ms));
18
+ }
19
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,2EAA2E;AAC3E,oBAAoB;AAEpB,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAU;IAC5B,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,MAAS;IAET,OAAO,CAAC,CAAC,EAAkB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { VerifyProvider } from "./types";
2
+ export declare const CURATED_MODELS: Record<VerifyProvider, readonly string[]>;
3
+ export declare function vendorForRow(provider: VerifyProvider, model?: string): string;
4
+ export declare function providerForModel(model: string): VerifyProvider | null;
5
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAU9C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAUpE,CAAC;AAgBF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAerE"}
package/dist/models.js ADDED
@@ -0,0 +1,54 @@
1
+ import { isOneOf } from "./internal/utils";
2
+ const VERIFY_PROVIDERS = [
3
+ "anthropic",
4
+ "openai",
5
+ "gemini",
6
+ ];
7
+ const isVerifyProvider = isOneOf(VERIFY_PROVIDERS);
8
+ export const CURATED_MODELS = {
9
+ anthropic: [
10
+ "claude-opus-4-6",
11
+ "claude-opus-4-7",
12
+ "claude-opus-4-8",
13
+ "claude-sonnet-4-6",
14
+ "claude-haiku-4-5",
15
+ ],
16
+ openai: ["gpt-5.5", "gpt-5.4", "gpt-5.1", "o3", "o4-mini"],
17
+ gemini: ["gemini-3.1-pro-preview", "gemini-3.1-flash", "gemini-2.5-pro"],
18
+ };
19
+ const MODEL_TO_PROVIDER = {};
20
+ for (const provider of Object.keys(CURATED_MODELS)) {
21
+ if (!isVerifyProvider(provider))
22
+ continue;
23
+ for (const model of CURATED_MODELS[provider]) {
24
+ MODEL_TO_PROVIDER[model] = provider;
25
+ }
26
+ }
27
+ const PROVIDER_VENDOR = {
28
+ anthropic: "anthropic",
29
+ openai: "openai",
30
+ gemini: "google",
31
+ };
32
+ export function vendorForRow(provider, model) {
33
+ const inferred = model ? providerForModel(model) : null;
34
+ return PROVIDER_VENDOR[inferred ?? provider];
35
+ }
36
+ export function providerForModel(model) {
37
+ const id = model.trim().toLowerCase();
38
+ if (!id)
39
+ return null;
40
+ if (MODEL_TO_PROVIDER[id])
41
+ return MODEL_TO_PROVIDER[id];
42
+ if (id.includes("claude"))
43
+ return "anthropic";
44
+ if (id.includes("gemini"))
45
+ return "gemini";
46
+ if (id.startsWith("gpt-") ||
47
+ id.startsWith("o1") ||
48
+ id.startsWith("o3") ||
49
+ id.startsWith("o4") ||
50
+ id.startsWith("chatgpt"))
51
+ return "openai";
52
+ return null;
53
+ }
54
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,MAAM,gBAAgB,GAA8B;IAClD,WAAW;IACX,QAAQ;IACR,QAAQ;CACT,CAAC;AAEF,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEnD,MAAM,CAAC,MAAM,cAAc,GAA8C;IACvE,SAAS,EAAE;QACT,iBAAiB;QACjB,iBAAiB;QACjB,iBAAiB;QACjB,mBAAmB;QACnB,kBAAkB;KACnB;IACD,MAAM,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC;IAC1D,MAAM,EAAE,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC;CACzE,CAAC;AAEF,MAAM,iBAAiB,GAAmC,EAAE,CAAC;AAC7D,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;IACnD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QAAE,SAAS;IAC1C,KAAK,MAAM,KAAK,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,iBAAiB,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;IACtC,CAAC;AACH,CAAC;AAED,MAAM,eAAe,GAAmC;IACtD,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,QAAwB,EAAE,KAAc;IACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,OAAO,eAAe,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACtC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,IAAI,iBAAiB,CAAC,EAAE,CAAC;QAAE,OAAO,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACxD,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,WAAW,CAAC;IAC9C,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3C,IACE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QACrB,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QACnB,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QACnB,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QACnB,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAExB,OAAO,QAAQ,CAAC;IAClB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function makeNonce(): string;
2
+ export declare function nonceTag(nonce: string): string;
3
+ export declare function echoesNonce(text: string, nonce: string): boolean;
4
+ //# sourceMappingURL=nonce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nonce.d.ts","sourceRoot":"","sources":["../src/nonce.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhE"}
package/dist/nonce.js ADDED
@@ -0,0 +1,12 @@
1
+ export function makeNonce() {
2
+ const bytes = new Uint8Array(4);
3
+ crypto.getRandomValues(bytes);
4
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
5
+ }
6
+ export function nonceTag(nonce) {
7
+ return `Begin your reply with the tag [${nonce}] then a space, then your answer.`;
8
+ }
9
+ export function echoesNonce(text, nonce) {
10
+ return text.includes(nonce.toLowerCase());
11
+ }
12
+ //# sourceMappingURL=nonce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nonce.js","sourceRoot":"","sources":["../src/nonce.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAO,kCAAkC,KAAK,mCAAmC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const CODING_TOOL_REFUSAL_PATTERNS: string[];
2
+ export declare const CODING_TOOL_NAMES: string[];
3
+ export declare const SCAM_PAGE_PATTERNS: string[];
4
+ export declare const VENDOR_PATTERNS: {
5
+ readonly anthropic: readonly ["anthropic", "claude"];
6
+ readonly openai: readonly ["openai", "chatgpt", "gpt-3", "gpt-4", "gpt-5", "o1-", "o3-", "o4-"];
7
+ readonly google: readonly ["google", "deepmind", "gemini"];
8
+ readonly other: readonly ["deepseek", "qwen", "moonshot", "kimi", "mistral", "llama", "meta", "grok", "xai", "zhipu"];
9
+ };
10
+ export type VendorKey = keyof typeof VENDOR_PATTERNS;
11
+ export declare function foreignPatternsExcept(home: VendorKey): string[];
12
+ export declare const CLOUD_HOST_PATTERNS: string[];
13
+ export declare const FAKE_RESPONSE_SIGNATURES: string[];
14
+ export declare const CJK_CHAR: RegExp;
15
+ export declare const CJK_LEAK_MIN_CHARS = 4;
16
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../src/patterns.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,UA2BxC,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAAiC,CAAC;AAEhE,eAAO,MAAM,kBAAkB,UAM9B,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;CAgBlB,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,eAAe,CAAC;AAErD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,EAAE,CAK/D;AAGD,eAAO,MAAM,mBAAmB,UAA6E,CAAC;AAE9G,eAAO,MAAM,wBAAwB,UAA0B,CAAC;AAEhE,eAAO,MAAM,QAAQ,QAAuB,CAAC;AAE7C,eAAO,MAAM,kBAAkB,IAAI,CAAC"}
@@ -0,0 +1,63 @@
1
+ export const CODING_TOOL_REFUSAL_PATTERNS = [
2
+ "assist with development",
3
+ "here to assist with development tasks",
4
+ "sensitive, personal, or emotional",
5
+ "i'm here to help with coding",
6
+ "i'm here to help with development",
7
+ "i'm designed to help with development",
8
+ "let me help you with your code",
9
+ "i'm a coding assistant",
10
+ "development tasks, writing, analysis",
11
+ "infrastructure and configuration",
12
+ "falls outside what i can help with",
13
+ "i'm focused on software development",
14
+ "focused on software development and coding",
15
+ "best suited for software development",
16
+ "i'm built to help with software development",
17
+ "i'm built to help with coding",
18
+ "help with software development, coding",
19
+ "what can i help you build",
20
+ "got a tricky bug",
21
+ "got a coding challenge",
22
+ "got any code challenges",
23
+ "i'm here to help with software",
24
+ "i'm here for coding",
25
+ "i'm droid",
26
+ "development workflows, cli commands",
27
+ "here to help with coding, development workflows",
28
+ ];
29
+ export const CODING_TOOL_NAMES = ["kiro", "cascade", "codeium"];
30
+ export const SCAM_PAGE_PATTERNS = [
31
+ "token被盗",
32
+ "token被人盗刷",
33
+ "本站token",
34
+ "盗取token",
35
+ "微信jemes",
36
+ ];
37
+ export const VENDOR_PATTERNS = {
38
+ anthropic: ["anthropic", "claude"],
39
+ openai: ["openai", "chatgpt", "gpt-3", "gpt-4", "gpt-5", "o1-", "o3-", "o4-"],
40
+ google: ["google", "deepmind", "gemini"],
41
+ other: [
42
+ "deepseek",
43
+ "qwen",
44
+ "moonshot",
45
+ "kimi",
46
+ "mistral",
47
+ "llama",
48
+ "meta",
49
+ "grok",
50
+ "xai",
51
+ "zhipu",
52
+ ],
53
+ };
54
+ export function foreignPatternsExcept(home) {
55
+ const keys = Object.keys(VENDOR_PATTERNS).filter((k) => k in VENDOR_PATTERNS);
56
+ return keys.filter((k) => k !== home).flatMap((k) => [...VENDOR_PATTERNS[k]]);
57
+ }
58
+ // prettier-ignore
59
+ export const CLOUD_HOST_PATTERNS = ["amazon", "aws", "bedrock", "google", "vertex", "microsoft", "azure", "foundry"];
60
+ export const FAKE_RESPONSE_SIGNATURES = ["claude sonnet (4.0)"];
61
+ export const CJK_CHAR = /[぀-ヿ㐀-䶿一-鿿豈-﫿가-힯]/g;
62
+ export const CJK_LEAK_MIN_CHARS = 4;
63
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../src/patterns.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,yBAAyB;IACzB,uCAAuC;IACvC,mCAAmC;IACnC,8BAA8B;IAC9B,mCAAmC;IACnC,uCAAuC;IACvC,gCAAgC;IAChC,wBAAwB;IACxB,sCAAsC;IACtC,kCAAkC;IAClC,oCAAoC;IACpC,qCAAqC;IACrC,4CAA4C;IAC5C,sCAAsC;IACtC,6CAA6C;IAC7C,+BAA+B;IAC/B,wCAAwC;IACxC,2BAA2B;IAC3B,kBAAkB;IAClB,wBAAwB;IACxB,yBAAyB;IACzB,gCAAgC;IAChC,qBAAqB;IACrB,WAAW;IACX,qCAAqC;IACrC,iDAAiD;CAClD,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,SAAS;IACT,WAAW;IACX,SAAS;IACT,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC;IAClC,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;IAC7E,MAAM,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC;IACxC,KAAK,EAAE;QACL,UAAU;QACV,MAAM;QACN,UAAU;QACV,MAAM;QACN,SAAS;QACT,OAAO;QACP,MAAM;QACN,MAAM;QACN,KAAK;QACL,OAAO;KACR;CACO,CAAC;AAIX,MAAM,UAAU,qBAAqB,CAAC,IAAe;IACnD,MAAM,IAAI,GAAgB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,IAAI,eAAe,CAC5C,CAAC;IACF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,kBAAkB;AAClB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAC,KAAK,EAAC,SAAS,EAAC,QAAQ,EAAC,QAAQ,EAAC,WAAW,EAAC,OAAO,EAAC,SAAS,CAAC,CAAC;AAE9G,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,qBAAqB,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;AAE7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ProviderConfig } from "./providers/config";
2
+ import type { ProbeLabel } from "./types";
3
+ export type ProbeDef = {
4
+ label: ProbeLabel;
5
+ maxTokens: number;
6
+ buildPrompt: (nonce: string) => string;
7
+ evaluate: (text: string, cfg: ProviderConfig) => boolean;
8
+ };
9
+ export declare const PROBES: readonly ProbeDef[];
10
+ //# sourceMappingURL=probes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAUzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;CAC1D,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,SAAS,QAAQ,EAkErC,CAAC"}
package/dist/probes.js ADDED
@@ -0,0 +1,54 @@
1
+ import { cjkLeak, hasCodingToolRefusal, hasForeignIdentity, hasScamPage, includesAny, } from "./signals";
2
+ import { CLOUD_HOST_PATTERNS, FAKE_RESPONSE_SIGNATURES } from "./patterns";
3
+ import { nonceTag } from "./nonce";
4
+ export const PROBES = [
5
+ {
6
+ label: "emotional",
7
+ maxTokens: 200,
8
+ buildPrompt: (n) => `Tell me a 2-sentence sad story about a lost kitten. ${nonceTag(n)}`,
9
+ evaluate: (text) => !hasCodingToolRefusal(text) && !cjkLeak(text) && text.length >= 40,
10
+ },
11
+ {
12
+ label: "creative",
13
+ maxTokens: 120,
14
+ buildPrompt: (n) => `Write a haiku about the ocean at sunrise. ${nonceTag(n)}`,
15
+ evaluate: (text) => !hasCodingToolRefusal(text) && !cjkLeak(text) && text.length >= 25,
16
+ },
17
+ {
18
+ label: "identity",
19
+ maxTokens: 60,
20
+ buildPrompt: (n) => "Which AI lab developed and trained the model you are running on? " +
21
+ "Not the company hosting you, the lab that trained the model. " +
22
+ `One word answer. ${nonceTag(n)}`,
23
+ evaluate: (text, cfg) => {
24
+ if (hasCodingToolRefusal(text) || hasScamPage(text))
25
+ return false;
26
+ if (hasForeignIdentity(text, cfg.foreignIdentityPatterns, cfg.cloudModelNamePatterns, "identity"))
27
+ return false;
28
+ if (includesAny(text, cfg.homeIdentityPatterns))
29
+ return true;
30
+ if (cfg.acceptsCloudHostIdentity &&
31
+ includesAny(text, CLOUD_HOST_PATTERNS))
32
+ return true;
33
+ return false;
34
+ },
35
+ },
36
+ {
37
+ label: "model-name",
38
+ maxTokens: 80,
39
+ buildPrompt: (n) => `Which model are you? Reply with only your model name. ${nonceTag(n)}`,
40
+ evaluate: (text, cfg) => {
41
+ if (hasCodingToolRefusal(text) || hasScamPage(text))
42
+ return false;
43
+ if (hasForeignIdentity(text, cfg.foreignIdentityPatterns, cfg.cloudModelNamePatterns, "model-name"))
44
+ return false;
45
+ if (!includesAny(text, cfg.homeModelNamePatterns))
46
+ return false;
47
+ const stripped = text.replace(/^\s*\[[a-z0-9]{4,8}\]\s*/i, "").trim();
48
+ if (FAKE_RESPONSE_SIGNATURES.includes(stripped))
49
+ return false;
50
+ return true;
51
+ },
52
+ },
53
+ ];
54
+ //# sourceMappingURL=probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probes.js","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EACP,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAUnC,MAAM,CAAC,MAAM,MAAM,GAAwB;IACzC;QACE,KAAK,EAAE,WAAW;QAClB,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CACjB,uDAAuD,QAAQ,CAAC,CAAC,CAAC,EAAE;QACtE,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CACjB,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE;KACrE;IACD;QACE,KAAK,EAAE,UAAU;QACjB,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CACjB,6CAA6C,QAAQ,CAAC,CAAC,CAAC,EAAE;QAC5D,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CACjB,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE;KACrE;IACD;QACE,KAAK,EAAE,UAAU;QACjB,SAAS,EAAE,EAAE;QACb,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CACjB,mEAAmE;YACnE,+DAA+D;YAC/D,oBAAoB,QAAQ,CAAC,CAAC,CAAC,EAAE;QACnC,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YACtB,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAClE,IACE,kBAAkB,CAChB,IAAI,EACJ,GAAG,CAAC,uBAAuB,EAC3B,GAAG,CAAC,sBAAsB,EAC1B,UAAU,CACX;gBAED,OAAO,KAAK,CAAC;YACf,IAAI,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,oBAAoB,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC7D,IACE,GAAG,CAAC,wBAAwB;gBAC5B,WAAW,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBAEtC,OAAO,IAAI,CAAC;YACd,OAAO,KAAK,CAAC;QACf,CAAC;KACF;IACD;QACE,KAAK,EAAE,YAAY;QACnB,SAAS,EAAE,EAAE;QACb,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CACjB,yDAAyD,QAAQ,CAAC,CAAC,CAAC,EAAE;QACxE,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YACtB,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAClE,IACE,kBAAkB,CAChB,IAAI,EACJ,GAAG,CAAC,uBAAuB,EAC3B,GAAG,CAAC,sBAAsB,EAC1B,YAAY,CACb;gBAED,OAAO,KAAK,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,qBAAqB,CAAC;gBAAE,OAAO,KAAK,CAAC;YAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtE,IAAI,wBAAwB,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type ProviderConfig } from "./config";
2
+ export declare const anthropicConfig: ProviderConfig;
3
+ //# sourceMappingURL=anthropic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAgElB,eAAO,MAAM,eAAe,EAAE,cAa7B,CAAC"}