@intx/inference-discovery 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -0
- package/media/sample.jpg +0 -0
- package/media/sample.mp4 +0 -0
- package/media/sample.pdf +0 -0
- package/media/sample.wav +0 -0
- package/package.json +19 -0
- package/src/catalog/capability.test.ts +43 -0
- package/src/catalog/capability.ts +38 -0
- package/src/catalog/index.ts +10 -0
- package/src/catalog/intent.test.ts +94 -0
- package/src/catalog/intent.ts +297 -0
- package/src/catalog/manifest.test.ts +72 -0
- package/src/catalog/manifest.ts +12 -0
- package/src/catalog/support-matrix.test.ts +79 -0
- package/src/catalog/support-matrix.ts +344 -0
- package/src/ci-guard.test.ts +36 -0
- package/src/ci-guard.ts +9 -0
- package/src/cli.test.ts +129 -0
- package/src/cli.ts +133 -0
- package/src/content-type.test.ts +45 -0
- package/src/content-type.ts +20 -0
- package/src/env.test.ts +31 -0
- package/src/env.ts +36 -0
- package/src/index.ts +24 -0
- package/src/manifest.test.ts +56 -0
- package/src/manifest.ts +29 -0
- package/src/plugin.ts +70 -0
- package/src/runner.test.ts +508 -0
- package/src/runner.ts +242 -0
- package/src/write-capture.test.ts +168 -0
- package/src/write-capture.ts +83 -0
- package/tsconfig.json +4 -0
- package/tsconfig.tsbuildinfo +1 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export interface ParsedCLIRun {
|
|
2
|
+
kind: "run";
|
|
3
|
+
provider: string;
|
|
4
|
+
models: readonly string[];
|
|
5
|
+
capabilities: readonly string[];
|
|
6
|
+
all: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ParsedCLIHelp {
|
|
10
|
+
kind: "help";
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ParsedCLIError {
|
|
15
|
+
kind: "error";
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ParsedCLI = ParsedCLIRun | ParsedCLIHelp | ParsedCLIError;
|
|
20
|
+
|
|
21
|
+
export const HELP_TEXT = `Usage: discover --provider <name> [options]
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--provider <name> Required. Selects the provider plug-in to invoke.
|
|
25
|
+
--model <name> Restrict to this model. Repeatable.
|
|
26
|
+
--only <capability> Restrict to this capability. Repeatable.
|
|
27
|
+
--all Run every supported model x capability combination.
|
|
28
|
+
Mutually exclusive with --model and --only.
|
|
29
|
+
--help, -h Show this message.
|
|
30
|
+
|
|
31
|
+
When --all is omitted and no --model/--only flags are given, the runner
|
|
32
|
+
will fail; pass --all explicitly or narrow the scope with --model/--only.
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
function takeValue(
|
|
36
|
+
argv: readonly string[],
|
|
37
|
+
index: number,
|
|
38
|
+
flag: string,
|
|
39
|
+
): { value: string; nextIndex: number } | { error: string } {
|
|
40
|
+
const next = argv[index + 1];
|
|
41
|
+
if (next === undefined || next.startsWith("-")) {
|
|
42
|
+
return { error: `Flag ${flag} requires a value` };
|
|
43
|
+
}
|
|
44
|
+
return { value: next, nextIndex: index + 2 };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function parseCLI(argv: readonly string[]): ParsedCLI {
|
|
48
|
+
let provider: string | undefined;
|
|
49
|
+
const models: string[] = [];
|
|
50
|
+
const capabilities: string[] = [];
|
|
51
|
+
let all = false;
|
|
52
|
+
|
|
53
|
+
let i = 0;
|
|
54
|
+
while (i < argv.length) {
|
|
55
|
+
const arg = argv[i];
|
|
56
|
+
if (arg === undefined) {
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (arg === "--help" || arg === "-h") {
|
|
61
|
+
return { kind: "help", message: HELP_TEXT };
|
|
62
|
+
}
|
|
63
|
+
if (arg === "--all") {
|
|
64
|
+
all = true;
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (arg === "--provider") {
|
|
69
|
+
const result = takeValue(argv, i, "--provider");
|
|
70
|
+
if ("error" in result) {
|
|
71
|
+
return { kind: "error", message: result.error };
|
|
72
|
+
}
|
|
73
|
+
if (provider !== undefined) {
|
|
74
|
+
return {
|
|
75
|
+
kind: "error",
|
|
76
|
+
message: "--provider may only be specified once",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
provider = result.value;
|
|
80
|
+
i = result.nextIndex;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (arg === "--model") {
|
|
84
|
+
const result = takeValue(argv, i, "--model");
|
|
85
|
+
if ("error" in result) {
|
|
86
|
+
return { kind: "error", message: result.error };
|
|
87
|
+
}
|
|
88
|
+
models.push(result.value);
|
|
89
|
+
i = result.nextIndex;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === "--only") {
|
|
93
|
+
const result = takeValue(argv, i, "--only");
|
|
94
|
+
if ("error" in result) {
|
|
95
|
+
return { kind: "error", message: result.error };
|
|
96
|
+
}
|
|
97
|
+
capabilities.push(result.value);
|
|
98
|
+
i = result.nextIndex;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
return { kind: "error", message: `Unknown argument: ${arg}` };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (provider === undefined) {
|
|
105
|
+
return {
|
|
106
|
+
kind: "error",
|
|
107
|
+
message: "--provider <name> is required",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (all && (models.length > 0 || capabilities.length > 0)) {
|
|
112
|
+
return {
|
|
113
|
+
kind: "error",
|
|
114
|
+
message: "--all is mutually exclusive with --model and --only",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!all && models.length === 0 && capabilities.length === 0) {
|
|
119
|
+
return {
|
|
120
|
+
kind: "error",
|
|
121
|
+
message:
|
|
122
|
+
"Specify --all or narrow the scope with --model/--only (at least one of either)",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
kind: "run",
|
|
128
|
+
provider,
|
|
129
|
+
models,
|
|
130
|
+
capabilities,
|
|
131
|
+
all,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { detectResponseKind } from "./content-type";
|
|
3
|
+
|
|
4
|
+
describe("detectResponseKind", () => {
|
|
5
|
+
test("returns 'json' for application/json", () => {
|
|
6
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
7
|
+
expect(detectResponseKind(headers)).toBe("json");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("returns 'json' for application/json with charset", () => {
|
|
11
|
+
const headers = new Headers({
|
|
12
|
+
"content-type": "application/json; charset=utf-8",
|
|
13
|
+
});
|
|
14
|
+
expect(detectResponseKind(headers)).toBe("json");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("returns 'sse' for text/event-stream", () => {
|
|
18
|
+
const headers = new Headers({ "content-type": "text/event-stream" });
|
|
19
|
+
expect(detectResponseKind(headers)).toBe("sse");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("returns 'sse' for text/event-stream with charset", () => {
|
|
23
|
+
const headers = new Headers({
|
|
24
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
25
|
+
});
|
|
26
|
+
expect(detectResponseKind(headers)).toBe("sse");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("matches Content-Type case-insensitively", () => {
|
|
30
|
+
const headers = new Headers({ "content-type": "Application/JSON" });
|
|
31
|
+
expect(detectResponseKind(headers)).toBe("json");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("throws when content-type is missing", () => {
|
|
35
|
+
const headers = new Headers();
|
|
36
|
+
expect(() => detectResponseKind(headers)).toThrow(
|
|
37
|
+
/response has no Content-Type/,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("throws on unknown content-type", () => {
|
|
42
|
+
const headers = new Headers({ "content-type": "text/plain" });
|
|
43
|
+
expect(() => detectResponseKind(headers)).toThrow(/text\/plain/);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ResponseKind = "sse" | "json";
|
|
2
|
+
|
|
3
|
+
export function detectResponseKind(headers: Headers): ResponseKind {
|
|
4
|
+
const raw = headers.get("content-type");
|
|
5
|
+
if (raw === null) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
"Cannot detect response kind: response has no Content-Type header",
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
const normalized = raw.trim().toLowerCase();
|
|
11
|
+
if (normalized.startsWith("text/event-stream")) {
|
|
12
|
+
return "sse";
|
|
13
|
+
}
|
|
14
|
+
if (normalized.startsWith("application/json")) {
|
|
15
|
+
return "json";
|
|
16
|
+
}
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Unsupported response Content-Type: ${raw}. Expected text/event-stream or application/json.`,
|
|
19
|
+
);
|
|
20
|
+
}
|
package/src/env.test.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { requireEnv, requireEnvSet } from "./env";
|
|
3
|
+
|
|
4
|
+
describe("requireEnv", () => {
|
|
5
|
+
test("returns the value when set", () => {
|
|
6
|
+
expect(requireEnv("FOO", { FOO: "bar" })).toBe("bar");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("throws when missing", () => {
|
|
10
|
+
expect(() => requireEnv("FOO", {})).toThrow(/FOO/);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("throws when empty", () => {
|
|
14
|
+
expect(() => requireEnv("FOO", { FOO: "" })).toThrow(/FOO/);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("requireEnvSet", () => {
|
|
19
|
+
test("returns all values when all set", () => {
|
|
20
|
+
const result = requireEnvSet(["A", "B"], { A: "1", B: "2", C: "3" });
|
|
21
|
+
expect(result).toEqual({ A: "1", B: "2" });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("throws listing all missing names", () => {
|
|
25
|
+
expect(() => requireEnvSet(["A", "B", "C"], { B: "2" })).toThrow(/A, C/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("treats empty values as missing", () => {
|
|
29
|
+
expect(() => requireEnvSet(["A"], { A: "" })).toThrow(/A/);
|
|
30
|
+
});
|
|
31
|
+
});
|
package/src/env.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function requireEnv(
|
|
2
|
+
name: string,
|
|
3
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
4
|
+
): string {
|
|
5
|
+
const value = env[name];
|
|
6
|
+
if (value === undefined || value === "") {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`Required environment variable ${name} is not set. ` +
|
|
9
|
+
`Export it before running discovery.`,
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function requireEnvSet(
|
|
16
|
+
names: readonly string[],
|
|
17
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
18
|
+
): Record<string, string> {
|
|
19
|
+
const result: Record<string, string> = {};
|
|
20
|
+
const missing: string[] = [];
|
|
21
|
+
for (const name of names) {
|
|
22
|
+
const value = env[name];
|
|
23
|
+
if (value === undefined || value === "") {
|
|
24
|
+
missing.push(name);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
result[name] = value;
|
|
28
|
+
}
|
|
29
|
+
if (missing.length > 0) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Required environment variables are not set: ${missing.join(", ")}. ` +
|
|
32
|
+
`Export them before running discovery.`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ProviderPlugin,
|
|
3
|
+
CaptureStep,
|
|
4
|
+
CapturedResponse,
|
|
5
|
+
IterateCaptureStepsOpts,
|
|
6
|
+
} from "./plugin";
|
|
7
|
+
export { runCapture, type FetchLike, type RunCaptureOpts } from "./runner";
|
|
8
|
+
export {
|
|
9
|
+
writeCapture,
|
|
10
|
+
type ResponseBody,
|
|
11
|
+
type WriteCaptureInput,
|
|
12
|
+
} from "./write-capture";
|
|
13
|
+
export { detectResponseKind, type ResponseKind } from "./content-type";
|
|
14
|
+
export { assertNotCI } from "./ci-guard";
|
|
15
|
+
export { requireEnv, requireEnvSet } from "./env";
|
|
16
|
+
export {
|
|
17
|
+
parseCLI,
|
|
18
|
+
HELP_TEXT,
|
|
19
|
+
type ParsedCLI,
|
|
20
|
+
type ParsedCLIRun,
|
|
21
|
+
type ParsedCLIHelp,
|
|
22
|
+
type ParsedCLIError,
|
|
23
|
+
} from "./cli";
|
|
24
|
+
export { buildManifest, type BuildManifestOpts } from "./manifest";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { type } from "arktype";
|
|
3
|
+
import { FixtureManifest } from "./catalog";
|
|
4
|
+
import { buildManifest } from "./manifest";
|
|
5
|
+
|
|
6
|
+
describe("buildManifest", () => {
|
|
7
|
+
test("produces a record accepted by FixtureManifest", () => {
|
|
8
|
+
const manifest = buildManifest({
|
|
9
|
+
provider: "google-genai",
|
|
10
|
+
model: "gemini-2.5-flash",
|
|
11
|
+
capability: "plain-text",
|
|
12
|
+
now: () => new Date("2026-05-22T00:00:00Z"),
|
|
13
|
+
});
|
|
14
|
+
const validated = FixtureManifest(manifest);
|
|
15
|
+
expect(validated instanceof type.errors).toBe(false);
|
|
16
|
+
expect(manifest.provider).toBe("google-genai");
|
|
17
|
+
expect(manifest.model).toBe("gemini-2.5-flash");
|
|
18
|
+
expect(manifest.capability).toBe("plain-text");
|
|
19
|
+
expect(manifest.capturedAt).toBe("2026-05-22T00:00:00.000Z");
|
|
20
|
+
expect(manifest.schemaVersion).toBe("1");
|
|
21
|
+
expect(manifest.observedModelVersion).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("omits observedModelVersion when not provided", () => {
|
|
25
|
+
const manifest = buildManifest({
|
|
26
|
+
provider: "p",
|
|
27
|
+
model: "m",
|
|
28
|
+
capability: "plain-text",
|
|
29
|
+
now: () => new Date("2026-05-22T00:00:00Z"),
|
|
30
|
+
});
|
|
31
|
+
expect("observedModelVersion" in manifest).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("includes observedModelVersion when provided", () => {
|
|
35
|
+
const manifest = buildManifest({
|
|
36
|
+
provider: "p",
|
|
37
|
+
model: "m",
|
|
38
|
+
capability: "plain-text",
|
|
39
|
+
now: () => new Date("2026-05-22T00:00:00Z"),
|
|
40
|
+
observedModelVersion: "abc123",
|
|
41
|
+
});
|
|
42
|
+
expect(manifest.observedModelVersion).toBe("abc123");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("uses real Date by default", () => {
|
|
46
|
+
const before = new Date().toISOString();
|
|
47
|
+
const manifest = buildManifest({
|
|
48
|
+
provider: "p",
|
|
49
|
+
model: "m",
|
|
50
|
+
capability: "plain-text",
|
|
51
|
+
});
|
|
52
|
+
const after = new Date().toISOString();
|
|
53
|
+
expect(manifest.capturedAt >= before).toBe(true);
|
|
54
|
+
expect(manifest.capturedAt <= after).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
import { type Capability, FixtureManifest } from "./catalog";
|
|
3
|
+
|
|
4
|
+
export interface BuildManifestOpts {
|
|
5
|
+
provider: string;
|
|
6
|
+
model: string;
|
|
7
|
+
capability: Capability;
|
|
8
|
+
now?: () => Date;
|
|
9
|
+
observedModelVersion?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function buildManifest(opts: BuildManifestOpts): FixtureManifest {
|
|
13
|
+
const now = opts.now ?? (() => new Date());
|
|
14
|
+
const base: Record<string, unknown> = {
|
|
15
|
+
provider: opts.provider,
|
|
16
|
+
model: opts.model,
|
|
17
|
+
capability: opts.capability,
|
|
18
|
+
capturedAt: now().toISOString(),
|
|
19
|
+
schemaVersion: "1",
|
|
20
|
+
};
|
|
21
|
+
if (opts.observedModelVersion !== undefined) {
|
|
22
|
+
base.observedModelVersion = opts.observedModelVersion;
|
|
23
|
+
}
|
|
24
|
+
const validated = FixtureManifest(base);
|
|
25
|
+
if (validated instanceof type.errors) {
|
|
26
|
+
throw new Error(`Invalid manifest: ${validated.summary}`);
|
|
27
|
+
}
|
|
28
|
+
return validated;
|
|
29
|
+
}
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Capability, CapabilityIntent } from "./catalog";
|
|
2
|
+
|
|
3
|
+
export interface CapturedResponse {
|
|
4
|
+
status: number;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
// Populated for application/json responses; null for SSE.
|
|
7
|
+
parsed: unknown | null;
|
|
8
|
+
// Populated for text/event-stream responses; null otherwise. Iterators
|
|
9
|
+
// that consume streaming turn-1 responses to build a turn-2 body parse
|
|
10
|
+
// these bytes themselves — the runner does not interpret SSE.
|
|
11
|
+
bytes: Uint8Array | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IterateCaptureStepsOpts {
|
|
15
|
+
model: string;
|
|
16
|
+
capability: Capability;
|
|
17
|
+
intent: CapabilityIntent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface CaptureStepBase {
|
|
21
|
+
// Subdirectory under the capture root for this step's artifacts.
|
|
22
|
+
// null places the step's files directly under the capture root; a string
|
|
23
|
+
// segregates them (e.g. "turn-1", "turn-2", "upload", "generate").
|
|
24
|
+
subdir: string | null;
|
|
25
|
+
url: string;
|
|
26
|
+
// Defaults to "POST" when omitted.
|
|
27
|
+
method?: "POST" | "PUT" | "PATCH";
|
|
28
|
+
// Headers the step contributes on top of the runner's content-type default.
|
|
29
|
+
// Per-step headers may override the default content-type (for example,
|
|
30
|
+
// a multipart upload). They MUST NOT collide with the plug-in's auth
|
|
31
|
+
// headers — the runner detects that collision and throws, on the
|
|
32
|
+
// principle that auth is a plug-in-wide invariant and capability-
|
|
33
|
+
// specific overrides belong on the step.
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A step whose body is a JSON-serializable value. The runner writes it to
|
|
38
|
+
// `request.json` after JSON.stringify and sends it with the default
|
|
39
|
+
// `Content-Type: application/json` unless overridden via `headers`.
|
|
40
|
+
export interface JsonCaptureStep extends CaptureStepBase {
|
|
41
|
+
kind: "json";
|
|
42
|
+
body: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// A step whose body is raw bytes (e.g. a multipart upload envelope, a
|
|
46
|
+
// single-part octet-stream). The runner writes the bytes to `request.bin`
|
|
47
|
+
// and sends them verbatim with the supplied `contentType`. The plug-in
|
|
48
|
+
// owns content-type because there is no sensible default for non-JSON
|
|
49
|
+
// bodies.
|
|
50
|
+
export interface RawCaptureStep extends CaptureStepBase {
|
|
51
|
+
kind: "raw";
|
|
52
|
+
contentType: string;
|
|
53
|
+
body: Uint8Array;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type CaptureStep = JsonCaptureStep | RawCaptureStep;
|
|
57
|
+
|
|
58
|
+
export interface ProviderPlugin {
|
|
59
|
+
name: string;
|
|
60
|
+
models: readonly string[];
|
|
61
|
+
redactRequestHeaders: readonly string[];
|
|
62
|
+
redactResponseHeaders: readonly string[];
|
|
63
|
+
// Plug-in-wide credentials only. Capability-specific headers (beta
|
|
64
|
+
// flags, upload-protocol markers) belong on the step's `headers` map.
|
|
65
|
+
buildAuthHeaders(): Record<string, string>;
|
|
66
|
+
extractReasoningTrace?(parsed: unknown): unknown | null;
|
|
67
|
+
iterateCaptureSteps(
|
|
68
|
+
opts: IterateCaptureStepsOpts,
|
|
69
|
+
): Generator<CaptureStep, void, CapturedResponse>;
|
|
70
|
+
}
|