@albinocrabs/o-switcher 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/CONTRIBUTING.md +72 -0
- package/LICENSE +21 -0
- package/README.md +361 -0
- package/dist/chunk-BTDKGS7P.js +1777 -0
- package/dist/chunk-BTDKGS7P.js.map +1 -0
- package/dist/index.cjs +2585 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2021 -0
- package/dist/index.d.ts +2021 -0
- package/dist/index.js +835 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.cjs +1177 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +22 -0
- package/dist/plugin.d.ts +22 -0
- package/dist/plugin.js +194 -0
- package/dist/plugin.js.map +1 -0
- package/docs/api-reference.md +286 -0
- package/docs/architecture.md +511 -0
- package/docs/examples.md +190 -0
- package/docs/getting-started.md +316 -0
- package/package.json +60 -0
- package/scripts/collect-errors.ts +159 -0
- package/scripts/corpus.jsonl +5 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,2021 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import pino from 'pino';
|
|
3
|
+
import { EventEmitter } from 'eventemitter3';
|
|
4
|
+
import { IBreaker, CircuitState } from 'cockatiel';
|
|
5
|
+
import { ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Zod 4 schemas for O-Switcher configuration.
|
|
9
|
+
*
|
|
10
|
+
* Defines validation schemas for the switcher configuration subtree
|
|
11
|
+
* within OpenCode's effective merged config (D-04, D-05, D-06).
|
|
12
|
+
* All optional fields have sensible defaults so minimal config works.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Schema for exponential backoff configuration.
|
|
17
|
+
* All fields have defaults per D-10.
|
|
18
|
+
*/
|
|
19
|
+
declare const BackoffConfigSchema: z.ZodObject<{
|
|
20
|
+
base_ms: z.ZodDefault<z.ZodNumber>;
|
|
21
|
+
multiplier: z.ZodDefault<z.ZodNumber>;
|
|
22
|
+
max_ms: z.ZodDefault<z.ZodNumber>;
|
|
23
|
+
jitter: z.ZodDefault<z.ZodEnum<{
|
|
24
|
+
full: "full";
|
|
25
|
+
equal: "equal";
|
|
26
|
+
none: "none";
|
|
27
|
+
}>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
/**
|
|
30
|
+
* Schema for a single target definition.
|
|
31
|
+
* target_id and provider_id are required; all other fields have defaults.
|
|
32
|
+
* No credential fields -- provider_id is a pointer to OpenCode's auth store (D-13).
|
|
33
|
+
*/
|
|
34
|
+
declare const TargetConfigSchema: z.ZodObject<{
|
|
35
|
+
target_id: z.ZodString;
|
|
36
|
+
provider_id: z.ZodString;
|
|
37
|
+
profile: z.ZodOptional<z.ZodString>;
|
|
38
|
+
endpoint_id: z.ZodOptional<z.ZodString>;
|
|
39
|
+
capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
40
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
operator_priority: z.ZodDefault<z.ZodNumber>;
|
|
42
|
+
policy_tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
43
|
+
retry_budget: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
failover_budget: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
backoff: z.ZodOptional<z.ZodObject<{
|
|
46
|
+
base_ms: z.ZodDefault<z.ZodNumber>;
|
|
47
|
+
multiplier: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
max_ms: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
jitter: z.ZodDefault<z.ZodEnum<{
|
|
50
|
+
full: "full";
|
|
51
|
+
equal: "equal";
|
|
52
|
+
none: "none";
|
|
53
|
+
}>>;
|
|
54
|
+
}, z.core.$strip>>;
|
|
55
|
+
concurrency_limit: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
circuit_breaker: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
failure_threshold: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
failure_rate_threshold: z.ZodDefault<z.ZodNumber>;
|
|
59
|
+
sliding_window_size: z.ZodDefault<z.ZodNumber>;
|
|
60
|
+
half_open_after_ms: z.ZodDefault<z.ZodNumber>;
|
|
61
|
+
half_open_max_probes: z.ZodDefault<z.ZodNumber>;
|
|
62
|
+
success_threshold: z.ZodDefault<z.ZodNumber>;
|
|
63
|
+
}, z.core.$strip>>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
/**
|
|
66
|
+
* Top-level schema for the O-Switcher configuration subtree.
|
|
67
|
+
* Targets are optional — when absent, auto-discovery populates them from OpenCode providers.
|
|
68
|
+
* When provided, at least one target is required (min 1).
|
|
69
|
+
* Shorthand fields `retry` and `timeout` map to retry_budget and max_expected_latency_ms.
|
|
70
|
+
*/
|
|
71
|
+
declare const SwitcherConfigSchema: z.ZodObject<{
|
|
72
|
+
targets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
73
|
+
target_id: z.ZodString;
|
|
74
|
+
provider_id: z.ZodString;
|
|
75
|
+
profile: z.ZodOptional<z.ZodString>;
|
|
76
|
+
endpoint_id: z.ZodOptional<z.ZodString>;
|
|
77
|
+
capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
78
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
79
|
+
operator_priority: z.ZodDefault<z.ZodNumber>;
|
|
80
|
+
policy_tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
81
|
+
retry_budget: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
failover_budget: z.ZodOptional<z.ZodNumber>;
|
|
83
|
+
backoff: z.ZodOptional<z.ZodObject<{
|
|
84
|
+
base_ms: z.ZodDefault<z.ZodNumber>;
|
|
85
|
+
multiplier: z.ZodDefault<z.ZodNumber>;
|
|
86
|
+
max_ms: z.ZodDefault<z.ZodNumber>;
|
|
87
|
+
jitter: z.ZodDefault<z.ZodEnum<{
|
|
88
|
+
full: "full";
|
|
89
|
+
equal: "equal";
|
|
90
|
+
none: "none";
|
|
91
|
+
}>>;
|
|
92
|
+
}, z.core.$strip>>;
|
|
93
|
+
concurrency_limit: z.ZodOptional<z.ZodNumber>;
|
|
94
|
+
circuit_breaker: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
failure_threshold: z.ZodDefault<z.ZodNumber>;
|
|
96
|
+
failure_rate_threshold: z.ZodDefault<z.ZodNumber>;
|
|
97
|
+
sliding_window_size: z.ZodDefault<z.ZodNumber>;
|
|
98
|
+
half_open_after_ms: z.ZodDefault<z.ZodNumber>;
|
|
99
|
+
half_open_max_probes: z.ZodDefault<z.ZodNumber>;
|
|
100
|
+
success_threshold: z.ZodDefault<z.ZodNumber>;
|
|
101
|
+
}, z.core.$strip>>;
|
|
102
|
+
}, z.core.$strip>>>;
|
|
103
|
+
retry: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
timeout: z.ZodOptional<z.ZodNumber>;
|
|
105
|
+
retry_budget: z.ZodDefault<z.ZodNumber>;
|
|
106
|
+
failover_budget: z.ZodDefault<z.ZodNumber>;
|
|
107
|
+
backoff: z.ZodDefault<z.ZodObject<{
|
|
108
|
+
base_ms: z.ZodDefault<z.ZodNumber>;
|
|
109
|
+
multiplier: z.ZodDefault<z.ZodNumber>;
|
|
110
|
+
max_ms: z.ZodDefault<z.ZodNumber>;
|
|
111
|
+
jitter: z.ZodDefault<z.ZodEnum<{
|
|
112
|
+
full: "full";
|
|
113
|
+
equal: "equal";
|
|
114
|
+
none: "none";
|
|
115
|
+
}>>;
|
|
116
|
+
}, z.core.$strip>>;
|
|
117
|
+
deployment_mode_hint: z.ZodDefault<z.ZodEnum<{
|
|
118
|
+
"plugin-only": "plugin-only";
|
|
119
|
+
"server-companion": "server-companion";
|
|
120
|
+
"sdk-control": "sdk-control";
|
|
121
|
+
auto: "auto";
|
|
122
|
+
}>>;
|
|
123
|
+
routing_weights: z.ZodDefault<z.ZodObject<{
|
|
124
|
+
health: z.ZodDefault<z.ZodNumber>;
|
|
125
|
+
latency: z.ZodDefault<z.ZodNumber>;
|
|
126
|
+
failure: z.ZodDefault<z.ZodNumber>;
|
|
127
|
+
priority: z.ZodDefault<z.ZodNumber>;
|
|
128
|
+
}, z.core.$strip>>;
|
|
129
|
+
queue_limit: z.ZodDefault<z.ZodNumber>;
|
|
130
|
+
concurrency_limit: z.ZodDefault<z.ZodNumber>;
|
|
131
|
+
backpressure_threshold: z.ZodDefault<z.ZodNumber>;
|
|
132
|
+
circuit_breaker: z.ZodDefault<z.ZodObject<{
|
|
133
|
+
failure_threshold: z.ZodDefault<z.ZodNumber>;
|
|
134
|
+
failure_rate_threshold: z.ZodDefault<z.ZodNumber>;
|
|
135
|
+
sliding_window_size: z.ZodDefault<z.ZodNumber>;
|
|
136
|
+
half_open_after_ms: z.ZodDefault<z.ZodNumber>;
|
|
137
|
+
half_open_max_probes: z.ZodDefault<z.ZodNumber>;
|
|
138
|
+
success_threshold: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
}, z.core.$strip>>;
|
|
140
|
+
max_expected_latency_ms: z.ZodDefault<z.ZodNumber>;
|
|
141
|
+
}, z.core.$strip>;
|
|
142
|
+
/** Inferred type for a validated backoff configuration. */
|
|
143
|
+
type BackoffConfig = z.infer<typeof BackoffConfigSchema>;
|
|
144
|
+
/** Inferred type for a validated target configuration. */
|
|
145
|
+
type TargetConfig = z.infer<typeof TargetConfigSchema>;
|
|
146
|
+
/** Inferred type for a validated switcher configuration. */
|
|
147
|
+
type SwitcherConfig = z.infer<typeof SwitcherConfigSchema>;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Configuration validation with field-path diagnostics.
|
|
151
|
+
*
|
|
152
|
+
* Validates raw configuration input against the SwitcherConfigSchema
|
|
153
|
+
* and returns a frozen, immutable config object on success.
|
|
154
|
+
* On failure, throws ConfigValidationError with structured diagnostics
|
|
155
|
+
* containing field paths, messages, and received values (D-05).
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
/** A single diagnostic entry describing a validation failure. */
|
|
159
|
+
interface ConfigDiagnostic {
|
|
160
|
+
readonly path: string;
|
|
161
|
+
readonly message: string;
|
|
162
|
+
readonly received: unknown;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Error thrown when configuration validation fails.
|
|
166
|
+
* Contains structured diagnostics with field paths and error messages.
|
|
167
|
+
*/
|
|
168
|
+
declare class ConfigValidationError extends Error {
|
|
169
|
+
readonly diagnostics: ReadonlyArray<ConfigDiagnostic>;
|
|
170
|
+
constructor(diagnostics: ReadonlyArray<ConfigDiagnostic>);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validates raw input against the SwitcherConfigSchema.
|
|
174
|
+
*
|
|
175
|
+
* On success, returns a frozen (immutable) SwitcherConfig.
|
|
176
|
+
* On failure, throws ConfigValidationError with structured diagnostics.
|
|
177
|
+
*/
|
|
178
|
+
declare const validateConfig: (raw: unknown) => SwitcherConfig;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Default configuration values for O-Switcher.
|
|
182
|
+
*
|
|
183
|
+
* These constants define sensible defaults for all optional configuration
|
|
184
|
+
* fields, ensuring minimal config works out of the box (D-10).
|
|
185
|
+
*/
|
|
186
|
+
/** Default maximum retry attempts per request. */
|
|
187
|
+
declare const DEFAULT_RETRY_BUDGET = 3;
|
|
188
|
+
/** Default maximum failover attempts per request. */
|
|
189
|
+
declare const DEFAULT_FAILOVER_BUDGET = 2;
|
|
190
|
+
/** Default base delay in milliseconds for exponential backoff. */
|
|
191
|
+
declare const DEFAULT_BACKOFF_BASE_MS = 1000;
|
|
192
|
+
/** Default multiplier for exponential backoff. */
|
|
193
|
+
declare const DEFAULT_BACKOFF_MULTIPLIER = 2;
|
|
194
|
+
/** Default maximum delay in milliseconds for exponential backoff. */
|
|
195
|
+
declare const DEFAULT_BACKOFF_MAX_MS = 30000;
|
|
196
|
+
/** Default jitter strategy for exponential backoff. */
|
|
197
|
+
declare const DEFAULT_BACKOFF_JITTER: "full";
|
|
198
|
+
/** Default retry shorthand value (maps to retry_budget). */
|
|
199
|
+
declare const DEFAULT_RETRY = 3;
|
|
200
|
+
/** Default timeout shorthand value in ms (maps to max_expected_latency_ms). */
|
|
201
|
+
declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Profile storage types for O-Switcher.
|
|
205
|
+
*
|
|
206
|
+
* TODO (D-73): When OpenCode supports multiple credentials per provider
|
|
207
|
+
* natively, O-Switcher should read them directly. The watcher + copy mechanism
|
|
208
|
+
* becomes unnecessary.
|
|
209
|
+
*/
|
|
210
|
+
/** OAuth credential shape matching OpenCode auth.json OAuth entries. */
|
|
211
|
+
interface OAuthCredential {
|
|
212
|
+
readonly type: 'oauth';
|
|
213
|
+
readonly refresh: string;
|
|
214
|
+
readonly access: string;
|
|
215
|
+
readonly expires: number;
|
|
216
|
+
readonly accountId?: string;
|
|
217
|
+
}
|
|
218
|
+
/** API key credential shape matching OpenCode auth.json API key entries. */
|
|
219
|
+
interface ApiKeyCredential {
|
|
220
|
+
readonly type: 'api-key';
|
|
221
|
+
readonly key: string;
|
|
222
|
+
}
|
|
223
|
+
/** Union of all supported credential types. */
|
|
224
|
+
type AuthCredential = OAuthCredential | ApiKeyCredential;
|
|
225
|
+
/** A single stored profile entry. */
|
|
226
|
+
interface ProfileEntry {
|
|
227
|
+
readonly id: string;
|
|
228
|
+
readonly provider: string;
|
|
229
|
+
readonly type: 'oauth' | 'api-key';
|
|
230
|
+
readonly credentials: AuthCredential;
|
|
231
|
+
readonly created: string;
|
|
232
|
+
}
|
|
233
|
+
/** Profile store keyed by profile ID (e.g., "openai-1"). */
|
|
234
|
+
type ProfileStore = Record<string, ProfileEntry>;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Auto-discovery of targets from OpenCode provider configuration.
|
|
238
|
+
*
|
|
239
|
+
* Pure functions: given a provider config map or a ProfileStore, produce arrays
|
|
240
|
+
* of TargetConfig objects with sensible defaults (D-55, D-56, D-71).
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Minimal interface for an OpenCode provider config entry.
|
|
245
|
+
* Only the fields needed for target discovery are required.
|
|
246
|
+
*/
|
|
247
|
+
interface ProviderConfigLike {
|
|
248
|
+
readonly id?: string;
|
|
249
|
+
readonly name?: string;
|
|
250
|
+
readonly options?: Record<string, unknown>;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Discovers targets from OpenCode provider configuration.
|
|
254
|
+
*
|
|
255
|
+
* For each provider entry in the map, creates a TargetConfig with:
|
|
256
|
+
* - target_id = the config key (e.g., "anthropic", "openai")
|
|
257
|
+
* - provider_id = provider.id if present, otherwise the config key
|
|
258
|
+
* - capabilities = ["chat"]
|
|
259
|
+
* - enabled = true
|
|
260
|
+
* - operator_priority = 0
|
|
261
|
+
* - policy_tags = []
|
|
262
|
+
*
|
|
263
|
+
* Skips falsy entries. Returns validated TargetConfig objects.
|
|
264
|
+
*/
|
|
265
|
+
declare const discoverTargets: (providerConfig: Record<string, ProviderConfigLike>) => TargetConfig[];
|
|
266
|
+
/**
|
|
267
|
+
* Discovers targets from the O-Switcher profile store (D-71).
|
|
268
|
+
*
|
|
269
|
+
* For each profile entry in the store, creates a TargetConfig with:
|
|
270
|
+
* - target_id = profile.id (e.g., "openai-1")
|
|
271
|
+
* - provider_id = profile.provider (e.g., "openai")
|
|
272
|
+
* - profile = profile.id (links to profile for credential resolution)
|
|
273
|
+
* - capabilities = ["chat"]
|
|
274
|
+
* - enabled = true
|
|
275
|
+
* - operator_priority = 0
|
|
276
|
+
* - policy_tags = []
|
|
277
|
+
*
|
|
278
|
+
* Returns validated TargetConfig objects via TargetConfigSchema.parse().
|
|
279
|
+
*/
|
|
280
|
+
declare const discoverTargetsFromProfiles: (store: ProfileStore) => TargetConfig[];
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Target registry types.
|
|
284
|
+
*
|
|
285
|
+
* Defines the TargetEntry interface with all FOUN-02 fields,
|
|
286
|
+
* the TargetState enum, and RegistrySnapshot for read-only access.
|
|
287
|
+
*
|
|
288
|
+
* SECURITY: No credential fields (api_key, token, secret, password).
|
|
289
|
+
* provider_id is a pointer to OpenCode's auth store (D-13, SECU-01, SECU-02).
|
|
290
|
+
*/
|
|
291
|
+
/**
|
|
292
|
+
* All valid states for a target in the registry.
|
|
293
|
+
*
|
|
294
|
+
* - Active: healthy and available for routing
|
|
295
|
+
* - CoolingDown: temporarily unavailable after transient failure
|
|
296
|
+
* - ReauthRequired: authentication failure, awaiting credential refresh
|
|
297
|
+
* - PolicyBlocked: blocked by policy (403), no retry
|
|
298
|
+
* - CircuitOpen: circuit breaker tripped, no requests allowed
|
|
299
|
+
* - CircuitHalfOpen: circuit breaker probing with limited requests
|
|
300
|
+
* - Draining: operator-initiated drain, no new requests
|
|
301
|
+
* - Disabled: operator-disabled or config-disabled
|
|
302
|
+
*/
|
|
303
|
+
declare const TARGET_STATES: readonly ["Active", "CoolingDown", "ReauthRequired", "PolicyBlocked", "CircuitOpen", "CircuitHalfOpen", "Draining", "Disabled"];
|
|
304
|
+
/** Target state type. */
|
|
305
|
+
type TargetState = (typeof TARGET_STATES)[number];
|
|
306
|
+
/**
|
|
307
|
+
* A single target entry in the registry.
|
|
308
|
+
*
|
|
309
|
+
* Contains all fields per FOUN-02: target_id, provider_id, endpoint_id,
|
|
310
|
+
* capabilities, enabled, state, health_score, cooldown_until, latency_ema_ms,
|
|
311
|
+
* failure_score, operator_priority, policy_tags.
|
|
312
|
+
*
|
|
313
|
+
* NO credential fields -- provider_id maps to OpenCode's credential store.
|
|
314
|
+
*/
|
|
315
|
+
interface TargetEntry {
|
|
316
|
+
readonly target_id: string;
|
|
317
|
+
readonly provider_id: string;
|
|
318
|
+
readonly profile: string | undefined;
|
|
319
|
+
readonly endpoint_id: string | undefined;
|
|
320
|
+
readonly capabilities: readonly string[];
|
|
321
|
+
readonly enabled: boolean;
|
|
322
|
+
readonly state: TargetState;
|
|
323
|
+
readonly health_score: number;
|
|
324
|
+
readonly cooldown_until: number | null;
|
|
325
|
+
readonly latency_ema_ms: number;
|
|
326
|
+
readonly failure_score: number;
|
|
327
|
+
readonly operator_priority: number;
|
|
328
|
+
readonly policy_tags: readonly string[];
|
|
329
|
+
}
|
|
330
|
+
/** Read-only snapshot of the entire registry. */
|
|
331
|
+
interface RegistrySnapshot {
|
|
332
|
+
readonly targets: ReadonlyArray<Readonly<TargetEntry>>;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* In-memory target registry.
|
|
337
|
+
*
|
|
338
|
+
* Initialized from validated SwitcherConfig. Single authoritative source
|
|
339
|
+
* of target state during runtime (D-09). All state mutations go through
|
|
340
|
+
* the registry.
|
|
341
|
+
*
|
|
342
|
+
* No credential material stored -- provider_id is a pointer only (D-13).
|
|
343
|
+
*/
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Target registry managing per-target state.
|
|
347
|
+
*
|
|
348
|
+
* Provides methods for state mutation, health scoring, cooldown management,
|
|
349
|
+
* and read-only snapshot generation.
|
|
350
|
+
*/
|
|
351
|
+
declare class TargetRegistry {
|
|
352
|
+
private readonly targets;
|
|
353
|
+
constructor(config: SwitcherConfig);
|
|
354
|
+
/** Returns the target entry for the given id, or undefined if not found. */
|
|
355
|
+
getTarget(id: string): TargetEntry | undefined;
|
|
356
|
+
/** Returns a readonly array of all target entries. */
|
|
357
|
+
getAllTargets(): ReadonlyArray<Readonly<TargetEntry>>;
|
|
358
|
+
/**
|
|
359
|
+
* Updates the state of a target.
|
|
360
|
+
* @returns true if the target was found and updated, false otherwise.
|
|
361
|
+
*/
|
|
362
|
+
updateState(id: string, newState: TargetState): boolean;
|
|
363
|
+
/**
|
|
364
|
+
* Records a success (1) or failure (0) observation for a target.
|
|
365
|
+
* Updates health_score via EMA.
|
|
366
|
+
*/
|
|
367
|
+
recordObservation(id: string, observation: 0 | 1): void;
|
|
368
|
+
/** Sets the cooldown_until timestamp for a target. */
|
|
369
|
+
setCooldown(id: string, untilMs: number): void;
|
|
370
|
+
/** Updates the latency EMA for a target. */
|
|
371
|
+
updateLatency(id: string, ms: number): void;
|
|
372
|
+
/**
|
|
373
|
+
* Adds a new target entry to the registry.
|
|
374
|
+
* @returns true if added, false if target_id already exists.
|
|
375
|
+
*/
|
|
376
|
+
addTarget(entry: TargetEntry): boolean;
|
|
377
|
+
/**
|
|
378
|
+
* Removes a target entry from the registry.
|
|
379
|
+
* @returns true if found and removed, false if not found.
|
|
380
|
+
*/
|
|
381
|
+
removeTarget(id: string): boolean;
|
|
382
|
+
/** Returns a deep-frozen snapshot of all targets. */
|
|
383
|
+
getSnapshot(): RegistrySnapshot;
|
|
384
|
+
}
|
|
385
|
+
/** Factory function to create a registry from validated config. */
|
|
386
|
+
declare const createRegistry: (config: SwitcherConfig) => TargetRegistry;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Health score computation using Exponential Moving Average (EMA).
|
|
390
|
+
*
|
|
391
|
+
* Pure functions for updating health scores and latency EMAs.
|
|
392
|
+
* Health score ranges from 0.0 (completely unhealthy) to 1.0 (perfect health).
|
|
393
|
+
*/
|
|
394
|
+
/** Initial health score for newly registered targets. */
|
|
395
|
+
declare const INITIAL_HEALTH_SCORE = 1;
|
|
396
|
+
/** Default EMA smoothing factor (alpha). Higher = more reactive to recent observations. */
|
|
397
|
+
declare const DEFAULT_ALPHA = 0.1;
|
|
398
|
+
/**
|
|
399
|
+
* Updates health score using EMA formula.
|
|
400
|
+
*
|
|
401
|
+
* Formula: alpha * observation + (1 - alpha) * currentScore
|
|
402
|
+
*
|
|
403
|
+
* @param currentScore - Current health score (0.0 to 1.0)
|
|
404
|
+
* @param observation - 1 for success, 0 for failure
|
|
405
|
+
* @param alpha - EMA smoothing factor (default 0.1)
|
|
406
|
+
* @returns Updated health score
|
|
407
|
+
*/
|
|
408
|
+
declare const updateHealthScore: (currentScore: number, observation: 0 | 1, alpha?: number) => number;
|
|
409
|
+
/**
|
|
410
|
+
* Updates latency EMA using the same EMA formula.
|
|
411
|
+
*
|
|
412
|
+
* @param currentEma - Current latency EMA in milliseconds
|
|
413
|
+
* @param observedMs - Observed latency in milliseconds
|
|
414
|
+
* @param alpha - EMA smoothing factor (default 0.1)
|
|
415
|
+
* @returns Updated latency EMA
|
|
416
|
+
*/
|
|
417
|
+
declare const updateLatencyEma: (currentEma: number, observedMs: number, alpha?: number) => number;
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Deployment mode types.
|
|
421
|
+
*
|
|
422
|
+
* Defines the three deployment modes, signal fidelity levels,
|
|
423
|
+
* and per-mode capability maps (FOUN-03, FOUN-04, D-16).
|
|
424
|
+
*/
|
|
425
|
+
/** The three supported deployment modes. */
|
|
426
|
+
type DeploymentMode = 'plugin-only' | 'server-companion' | 'sdk-control';
|
|
427
|
+
/**
|
|
428
|
+
* Signal fidelity indicates how error information is obtained.
|
|
429
|
+
*
|
|
430
|
+
* - 'direct': raw HTTP status codes and headers available
|
|
431
|
+
* - 'heuristic': classification from message patterns and events
|
|
432
|
+
*/
|
|
433
|
+
type SignalFidelity = 'direct' | 'heuristic';
|
|
434
|
+
/** Capability map for a deployment mode. */
|
|
435
|
+
interface ModeCapabilities {
|
|
436
|
+
readonly mode: DeploymentMode;
|
|
437
|
+
readonly signalFidelity: SignalFidelity;
|
|
438
|
+
readonly hasHttpStatus: boolean;
|
|
439
|
+
readonly hasRetryAfterHeader: boolean;
|
|
440
|
+
readonly hasOperatorApi: boolean;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Deployment mode detection.
|
|
445
|
+
*
|
|
446
|
+
* Determines the active deployment mode from a config hint.
|
|
447
|
+
* When hint is 'auto', defaults to 'plugin-only' (auto-detection
|
|
448
|
+
* logic will be refined in Phase 3 when mode adapters are built).
|
|
449
|
+
*
|
|
450
|
+
* Maps each mode to its signal fidelity and capability set (FOUN-03, FOUN-04).
|
|
451
|
+
*/
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Detects the deployment mode from a configuration hint.
|
|
455
|
+
*
|
|
456
|
+
* If hint is not 'auto', returns the hint directly.
|
|
457
|
+
* If 'auto', defaults to 'plugin-only' (conservative default).
|
|
458
|
+
*/
|
|
459
|
+
declare const detectDeploymentMode: (hint: "plugin-only" | "server-companion" | "sdk-control" | "auto") => DeploymentMode;
|
|
460
|
+
/**
|
|
461
|
+
* Returns the signal fidelity for a given deployment mode.
|
|
462
|
+
*
|
|
463
|
+
* - plugin-only: 'heuristic' (no raw HTTP status codes)
|
|
464
|
+
* - server-companion / sdk-control: 'direct' (full HTTP access)
|
|
465
|
+
*/
|
|
466
|
+
declare const getSignalFidelity: (mode: DeploymentMode) => SignalFidelity;
|
|
467
|
+
/**
|
|
468
|
+
* Returns the full capability map for a deployment mode.
|
|
469
|
+
*
|
|
470
|
+
* plugin-only has no HTTP status, no Retry-After, no operator API.
|
|
471
|
+
* server-companion and sdk-control have all capabilities.
|
|
472
|
+
*/
|
|
473
|
+
declare const getModeCapabilities: (mode: DeploymentMode) => ModeCapabilities;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Pino-based structured audit logger with credential redaction.
|
|
477
|
+
*
|
|
478
|
+
* Provides logger creation with automatic redaction of secret-pattern
|
|
479
|
+
* fields (SECU-04), child logger creation for per-request correlation IDs,
|
|
480
|
+
* and UUID v4 correlation ID generation.
|
|
481
|
+
*/
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Pino redaction paths for secret-pattern fields.
|
|
485
|
+
*
|
|
486
|
+
* Includes both top-level paths (for direct log object fields)
|
|
487
|
+
* and wildcard paths (for nested objects) to ensure comprehensive
|
|
488
|
+
* credential redaction regardless of nesting depth.
|
|
489
|
+
*/
|
|
490
|
+
declare const REDACT_PATHS: readonly string[];
|
|
491
|
+
/** Options for creating an audit logger. */
|
|
492
|
+
interface AuditLoggerOptions {
|
|
493
|
+
readonly level?: string;
|
|
494
|
+
readonly destination?: pino.DestinationStream;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Creates a pino logger with secret redaction configured.
|
|
498
|
+
*
|
|
499
|
+
* All fields matching REDACT_PATHS patterns are replaced with '[Redacted]'
|
|
500
|
+
* in log output, preventing credential leakage (SECU-04).
|
|
501
|
+
*/
|
|
502
|
+
declare const createAuditLogger: (options?: AuditLoggerOptions) => pino.Logger;
|
|
503
|
+
/**
|
|
504
|
+
* Creates a child logger with a request_id binding for correlation.
|
|
505
|
+
*
|
|
506
|
+
* All log entries from the child logger include the request_id field,
|
|
507
|
+
* enabling end-to-end request tracing in the audit trail.
|
|
508
|
+
*/
|
|
509
|
+
declare const createRequestLogger: (baseLogger: pino.Logger, requestId: string) => pino.Logger;
|
|
510
|
+
/**
|
|
511
|
+
* Generates a UUID v4 correlation ID using Node.js crypto.
|
|
512
|
+
*
|
|
513
|
+
* @returns A UUID v4 string suitable for request correlation.
|
|
514
|
+
*/
|
|
515
|
+
declare const generateCorrelationId: () => string;
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Audit trail types.
|
|
519
|
+
*
|
|
520
|
+
* Defines the AuditEvent interface and CorrelationId type
|
|
521
|
+
* for structured logging with request correlation (SECU-04).
|
|
522
|
+
*/
|
|
523
|
+
/** Unique correlation identifier for request tracing. */
|
|
524
|
+
type CorrelationId = string;
|
|
525
|
+
/**
|
|
526
|
+
* Structured audit event for the O-Switcher audit trail.
|
|
527
|
+
*
|
|
528
|
+
* Every audit event includes a request_id for correlation,
|
|
529
|
+
* a timestamp, and an event_type discriminator.
|
|
530
|
+
* Additional fields are allowed via index signature.
|
|
531
|
+
*/
|
|
532
|
+
interface AuditEvent {
|
|
533
|
+
readonly request_id: string;
|
|
534
|
+
readonly timestamp: string;
|
|
535
|
+
readonly event_type: string;
|
|
536
|
+
readonly [key: string]: unknown;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Error taxonomy: 10 error classes as a Zod discriminated union.
|
|
541
|
+
*
|
|
542
|
+
* Defines NormalizedSignal (classifier input), ClassificationResult (classifier output),
|
|
543
|
+
* and helper functions for retry decisions and target state transitions.
|
|
544
|
+
*
|
|
545
|
+
* Per D-01, D-02, D-03, ERRC-01.
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Discriminated union of all 10 error classes.
|
|
550
|
+
* The 'class' field is the discriminator.
|
|
551
|
+
*/
|
|
552
|
+
declare const ErrorClassSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
553
|
+
class: z.ZodLiteral<"RateLimited">;
|
|
554
|
+
retryable: z.ZodLiteral<true>;
|
|
555
|
+
retry_after_ms: z.ZodOptional<z.ZodNumber>;
|
|
556
|
+
provider_reason: z.ZodOptional<z.ZodString>;
|
|
557
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
558
|
+
class: z.ZodLiteral<"QuotaExhausted">;
|
|
559
|
+
retryable: z.ZodLiteral<false>;
|
|
560
|
+
provider_reason: z.ZodOptional<z.ZodString>;
|
|
561
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
562
|
+
class: z.ZodLiteral<"AuthFailure">;
|
|
563
|
+
retryable: z.ZodLiteral<false>;
|
|
564
|
+
recovery_attempted: z.ZodDefault<z.ZodBoolean>;
|
|
565
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
566
|
+
class: z.ZodLiteral<"PermissionFailure">;
|
|
567
|
+
retryable: z.ZodLiteral<false>;
|
|
568
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
569
|
+
class: z.ZodLiteral<"PolicyFailure">;
|
|
570
|
+
retryable: z.ZodLiteral<false>;
|
|
571
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
572
|
+
class: z.ZodLiteral<"RegionRestriction">;
|
|
573
|
+
retryable: z.ZodLiteral<false>;
|
|
574
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
575
|
+
class: z.ZodLiteral<"ModelUnavailable">;
|
|
576
|
+
retryable: z.ZodLiteral<false>;
|
|
577
|
+
failover_eligible: z.ZodLiteral<true>;
|
|
578
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
579
|
+
class: z.ZodLiteral<"TransientServerFailure">;
|
|
580
|
+
retryable: z.ZodLiteral<true>;
|
|
581
|
+
http_status: z.ZodOptional<z.ZodNumber>;
|
|
582
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
583
|
+
class: z.ZodLiteral<"TransportFailure">;
|
|
584
|
+
retryable: z.ZodLiteral<true>;
|
|
585
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
586
|
+
class: z.ZodLiteral<"InterruptedExecution">;
|
|
587
|
+
retryable: z.ZodLiteral<true>;
|
|
588
|
+
partial_output_bytes: z.ZodOptional<z.ZodNumber>;
|
|
589
|
+
}, z.core.$strip>], "class">;
|
|
590
|
+
/** Inferred type for any error class variant. */
|
|
591
|
+
type ErrorClass = z.infer<typeof ErrorClassSchema>;
|
|
592
|
+
/**
|
|
593
|
+
* Normalized signal: the common input format for the classifier.
|
|
594
|
+
*
|
|
595
|
+
* Direct adapters populate http_status and response_body.
|
|
596
|
+
* Heuristic adapters populate error_message and error_type.
|
|
597
|
+
* detection_mode is always set by the adapter (D-02).
|
|
598
|
+
*/
|
|
599
|
+
interface NormalizedSignal {
|
|
600
|
+
readonly http_status?: number;
|
|
601
|
+
readonly error_type?: string;
|
|
602
|
+
readonly error_message?: string;
|
|
603
|
+
readonly response_body?: unknown;
|
|
604
|
+
readonly detection_mode: 'direct' | 'heuristic';
|
|
605
|
+
readonly provider_id?: string;
|
|
606
|
+
readonly retry_after_ms?: number;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Classification result: the classifier output.
|
|
610
|
+
*
|
|
611
|
+
* Contains the error class, detection mode, confidence level,
|
|
612
|
+
* and the raw signal that produced it.
|
|
613
|
+
*/
|
|
614
|
+
interface ClassificationResult {
|
|
615
|
+
readonly error_class: ErrorClass;
|
|
616
|
+
readonly detection_mode: 'direct' | 'heuristic';
|
|
617
|
+
readonly confidence: 'high' | 'medium' | 'low';
|
|
618
|
+
readonly raw_signal: NormalizedSignal;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Checks whether an error class is retryable.
|
|
622
|
+
*
|
|
623
|
+
* @param errorClass - The classified error.
|
|
624
|
+
* @returns true if the error is retryable.
|
|
625
|
+
*/
|
|
626
|
+
declare const isRetryable: (errorClass: ErrorClass) => boolean;
|
|
627
|
+
/**
|
|
628
|
+
* Maps an error class to the target state transition it triggers.
|
|
629
|
+
*
|
|
630
|
+
* Returns null when the error should not cause a target state change
|
|
631
|
+
* (e.g., transient failures that are handled by retry).
|
|
632
|
+
*
|
|
633
|
+
* @param errorClass - The classified error.
|
|
634
|
+
* @returns The new target state, or null if no transition.
|
|
635
|
+
*/
|
|
636
|
+
declare const getTargetStateTransition: (errorClass: ErrorClass) => TargetState | null;
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Mode-agnostic error classifier core.
|
|
640
|
+
*
|
|
641
|
+
* Accepts a NormalizedSignal and routes to direct (HTTP) or heuristic
|
|
642
|
+
* (message pattern) classification based on signal content.
|
|
643
|
+
*
|
|
644
|
+
* Per D-01, ERRC-01.
|
|
645
|
+
*/
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Classifies a NormalizedSignal into an ErrorClass.
|
|
649
|
+
*
|
|
650
|
+
* Routes to direct (HTTP) or heuristic (message pattern) classification
|
|
651
|
+
* based on whether http_status is present in the signal.
|
|
652
|
+
*
|
|
653
|
+
* @param signal - The normalized error signal to classify.
|
|
654
|
+
* @returns ClassificationResult with error class, mode, confidence, and raw signal.
|
|
655
|
+
*/
|
|
656
|
+
declare const classify: (signal: NormalizedSignal) => ClassificationResult;
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Direct signal adapter: HTTP status + body to NormalizedSignal.
|
|
660
|
+
*
|
|
661
|
+
* Used in server-companion and SDK-control modes where raw HTTP
|
|
662
|
+
* status codes, response bodies, and headers are available.
|
|
663
|
+
*
|
|
664
|
+
* Per D-01, ERRC-02.
|
|
665
|
+
*/
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Extracts Retry-After value from response headers as milliseconds.
|
|
669
|
+
*
|
|
670
|
+
* Supports integer seconds format. HTTP-date format is not supported
|
|
671
|
+
* (providers typically send integer seconds).
|
|
672
|
+
*
|
|
673
|
+
* @param headers - Response headers (case-insensitive lookup for retry-after).
|
|
674
|
+
* @returns Retry-After in milliseconds, or undefined if not present/parseable.
|
|
675
|
+
*/
|
|
676
|
+
declare const extractRetryAfterMs: (headers?: Record<string, string>) => number | undefined;
|
|
677
|
+
/**
|
|
678
|
+
* Creates a NormalizedSignal from an HTTP response.
|
|
679
|
+
*
|
|
680
|
+
* Always sets detection_mode='direct' since we have raw HTTP access.
|
|
681
|
+
*
|
|
682
|
+
* @param status - HTTP status code.
|
|
683
|
+
* @param body - Parsed response body.
|
|
684
|
+
* @param headers - Response headers (optional; used for Retry-After).
|
|
685
|
+
* @returns NormalizedSignal ready for classifier.
|
|
686
|
+
*/
|
|
687
|
+
declare const directSignalFromResponse: (status: number, body: unknown, headers?: Record<string, string>) => NormalizedSignal;
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Heuristic signal adapter: message pattern to NormalizedSignal.
|
|
691
|
+
*
|
|
692
|
+
* Used in plugin-only mode where HTTP status codes are not available.
|
|
693
|
+
* Classification relies on message string matching against known patterns.
|
|
694
|
+
*
|
|
695
|
+
* Per D-01, ERRC-03.
|
|
696
|
+
*/
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Creates a NormalizedSignal from a session event message.
|
|
700
|
+
*
|
|
701
|
+
* Always sets detection_mode='heuristic' since no HTTP access is available.
|
|
702
|
+
* No http_status field is set (not available in plugin-only mode).
|
|
703
|
+
*
|
|
704
|
+
* @param eventType - The event type (e.g., 'error', 'retry', 'status').
|
|
705
|
+
* @param message - The error or status message string.
|
|
706
|
+
* @param providerId - Optional provider identifier.
|
|
707
|
+
* @returns NormalizedSignal ready for classifier.
|
|
708
|
+
*/
|
|
709
|
+
declare const heuristicSignalFromEvent: (eventType: string, message: string, providerId?: string) => NormalizedSignal;
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Provider-specific error pattern corpus.
|
|
713
|
+
*
|
|
714
|
+
* Contains known error response patterns for Anthropic, OpenAI, Google,
|
|
715
|
+
* and AWS Bedrock (direct classification) and heuristic message patterns
|
|
716
|
+
* for plugin-only mode classification.
|
|
717
|
+
*
|
|
718
|
+
* Per ERRC-02, ERRC-05, Pitfalls 1-3.
|
|
719
|
+
*/
|
|
720
|
+
/** A provider-specific error response pattern for direct classification. */
|
|
721
|
+
interface ProviderErrorPattern {
|
|
722
|
+
readonly provider: string;
|
|
723
|
+
readonly http_status: number;
|
|
724
|
+
readonly error_type_field: string;
|
|
725
|
+
readonly error_type_value: string;
|
|
726
|
+
readonly error_class: string;
|
|
727
|
+
readonly notes?: string;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Known provider error patterns for direct HTTP status classification.
|
|
731
|
+
*
|
|
732
|
+
* Order matters for matching: more specific patterns (with error_type_value)
|
|
733
|
+
* should be checked before generic status-code fallbacks.
|
|
734
|
+
*/
|
|
735
|
+
declare const PROVIDER_PATTERNS: ReadonlyArray<ProviderErrorPattern>;
|
|
736
|
+
/** A heuristic pattern for plugin-only mode message classification. */
|
|
737
|
+
interface HeuristicPattern {
|
|
738
|
+
readonly pattern: RegExp;
|
|
739
|
+
readonly error_class: string;
|
|
740
|
+
readonly confidence: 'high' | 'medium' | 'low';
|
|
741
|
+
readonly provider?: string;
|
|
742
|
+
readonly notes?: string;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Heuristic patterns for plugin-only mode classification.
|
|
746
|
+
*
|
|
747
|
+
* Order matters: more specific patterns should come first.
|
|
748
|
+
* TEMPORAL_QUOTA_PATTERN is checked separately before these patterns
|
|
749
|
+
* to correctly handle Pitfall 2.
|
|
750
|
+
*/
|
|
751
|
+
declare const HEURISTIC_PATTERNS: ReadonlyArray<HeuristicPattern>;
|
|
752
|
+
/**
|
|
753
|
+
* Temporal quota pattern to distinguish quota exhaustion from transient rate limits.
|
|
754
|
+
*
|
|
755
|
+
* Matches messages like "Too many tokens per day" from Bedrock, which arrive as
|
|
756
|
+
* ThrottlingException but represent non-retryable quota exhaustion (Pitfall 2).
|
|
757
|
+
*/
|
|
758
|
+
declare const TEMPORAL_QUOTA_PATTERN: RegExp;
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Exponential backoff with jitter and Retry-After floor.
|
|
762
|
+
*
|
|
763
|
+
* Pure function that computes the delay before the next retry attempt.
|
|
764
|
+
* Supports full jitter, equal jitter, and no jitter modes.
|
|
765
|
+
*
|
|
766
|
+
* Per D-10, D-11, Pitfall 4.
|
|
767
|
+
*/
|
|
768
|
+
/** Configuration for backoff computation. */
|
|
769
|
+
interface BackoffParams {
|
|
770
|
+
readonly base_ms: number;
|
|
771
|
+
readonly multiplier: number;
|
|
772
|
+
readonly max_ms: number;
|
|
773
|
+
readonly jitter: 'full' | 'equal' | 'none';
|
|
774
|
+
}
|
|
775
|
+
/** Default backoff parameters per D-10. */
|
|
776
|
+
declare const DEFAULT_BACKOFF_PARAMS: BackoffParams;
|
|
777
|
+
/**
|
|
778
|
+
* Computes the backoff delay in milliseconds for a given retry attempt.
|
|
779
|
+
*
|
|
780
|
+
* Formula: rawDelay = min(base_ms * multiplier^attempt, max_ms)
|
|
781
|
+
*
|
|
782
|
+
* Jitter modes:
|
|
783
|
+
* - 'full': Math.random() * rawDelay (uniform [0, rawDelay])
|
|
784
|
+
* - 'equal': rawDelay/2 + Math.random() * rawDelay/2 (uniform [rawDelay/2, rawDelay])
|
|
785
|
+
* - 'none': rawDelay (deterministic)
|
|
786
|
+
*
|
|
787
|
+
* The Retry-After value (if provided) is used as a floor: the result
|
|
788
|
+
* is at least retryAfterMs, preventing retry storms (Pitfall 4 fix).
|
|
789
|
+
*
|
|
790
|
+
* @param attempt - Zero-based retry attempt number.
|
|
791
|
+
* @param params - Backoff configuration parameters.
|
|
792
|
+
* @param retryAfterMs - Optional Retry-After value in ms to use as floor.
|
|
793
|
+
* @returns Delay in milliseconds before next retry.
|
|
794
|
+
*/
|
|
795
|
+
declare const computeBackoffMs: (attempt: number, params: BackoffParams, retryAfterMs?: number) => number;
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Bounded retry policy with budget tracking.
|
|
799
|
+
*
|
|
800
|
+
* Decides whether to retry, abort, or declare exhaustion based on
|
|
801
|
+
* the error classification and remaining budget.
|
|
802
|
+
*
|
|
803
|
+
* Per D-10, D-12, ERRC-06, ERRC-07, ERRC-08.
|
|
804
|
+
*/
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* A retry decision: retry with delay, abort immediately, or budget exhausted.
|
|
808
|
+
*/
|
|
809
|
+
type RetryDecision = {
|
|
810
|
+
readonly action: 'retry';
|
|
811
|
+
readonly delay_ms: number;
|
|
812
|
+
readonly attempt: number;
|
|
813
|
+
} | {
|
|
814
|
+
readonly action: 'abort';
|
|
815
|
+
readonly reason: string;
|
|
816
|
+
readonly target_state: TargetState | null;
|
|
817
|
+
} | {
|
|
818
|
+
readonly action: 'exhausted';
|
|
819
|
+
readonly attempts_used: number;
|
|
820
|
+
};
|
|
821
|
+
/**
|
|
822
|
+
* A retry policy with budget and backoff configuration.
|
|
823
|
+
*/
|
|
824
|
+
interface RetryPolicy {
|
|
825
|
+
readonly budget: number;
|
|
826
|
+
readonly backoffParams: BackoffParams;
|
|
827
|
+
readonly decide: (classification: ClassificationResult, attemptsSoFar: number) => RetryDecision;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Creates a bounded retry policy.
|
|
831
|
+
*
|
|
832
|
+
* Decision logic:
|
|
833
|
+
* 1. Non-retryable errors (AuthFailure, PermissionFailure, QuotaExhausted,
|
|
834
|
+
* PolicyFailure, RegionRestriction, ModelUnavailable) -> abort immediately
|
|
835
|
+
* with target state transition (D-12)
|
|
836
|
+
* 2. Budget exhausted -> return 'exhausted' with attempt count
|
|
837
|
+
* 3. Retryable errors -> compute backoff with Retry-After floor and return 'retry'
|
|
838
|
+
*
|
|
839
|
+
* AuthFailure special handling (ERRC-06): if recovery_attempted is true,
|
|
840
|
+
* abort with ReauthRequired state. The caller is responsible for setting
|
|
841
|
+
* recovery_attempted on the first recovery attempt.
|
|
842
|
+
*
|
|
843
|
+
* @param budget - Maximum number of retry attempts.
|
|
844
|
+
* @param backoffParams - Backoff configuration (defaults to full jitter).
|
|
845
|
+
* @returns A RetryPolicy instance.
|
|
846
|
+
*/
|
|
847
|
+
declare const createRetryPolicy: (budget: number, backoffParams?: BackoffParams) => RetryPolicy;
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Routing domain types for Phase 2.
|
|
851
|
+
*
|
|
852
|
+
* Defines ExclusionReason, AdmissionResult, SelectionRecord, RoutingWeights,
|
|
853
|
+
* CircuitBreakerConfig, CircuitBreaker, and ConcurrencyTrackerApi types.
|
|
854
|
+
*
|
|
855
|
+
* Per D-17, D-20, D-22, D-24, D-26.
|
|
856
|
+
*/
|
|
857
|
+
|
|
858
|
+
/** 7 exclusion reasons per ROUT-03, D-17. */
|
|
859
|
+
declare const EXCLUSION_REASONS: readonly ["disabled", "policy_blocked", "reauth_required", "cooldown_active", "circuit_open", "capability_mismatch", "draining"];
|
|
860
|
+
/** Why a target was excluded from routing. */
|
|
861
|
+
type ExclusionReason = (typeof EXCLUSION_REASONS)[number];
|
|
862
|
+
/** 4 admission outcomes per ADMC-02, D-20. */
|
|
863
|
+
declare const ADMISSION_RESULTS: readonly ["admitted", "queued", "degraded", "rejected"];
|
|
864
|
+
/** Admission control decision outcome. */
|
|
865
|
+
type AdmissionResult = (typeof ADMISSION_RESULTS)[number];
|
|
866
|
+
/** Admission decision with reason string. */
|
|
867
|
+
interface AdmissionDecision {
|
|
868
|
+
readonly result: AdmissionResult;
|
|
869
|
+
readonly reason: string;
|
|
870
|
+
}
|
|
871
|
+
/** Scoring weights for target selection per D-17. */
|
|
872
|
+
interface RoutingWeights {
|
|
873
|
+
readonly health: number;
|
|
874
|
+
readonly latency: number;
|
|
875
|
+
readonly failure: number;
|
|
876
|
+
readonly priority: number;
|
|
877
|
+
}
|
|
878
|
+
/** Circuit breaker configuration per D-22. */
|
|
879
|
+
interface CircuitBreakerConfig {
|
|
880
|
+
readonly failure_threshold: number;
|
|
881
|
+
readonly failure_rate_threshold: number;
|
|
882
|
+
readonly sliding_window_size: number;
|
|
883
|
+
readonly half_open_after_ms: number;
|
|
884
|
+
readonly half_open_max_probes: number;
|
|
885
|
+
readonly success_threshold: number;
|
|
886
|
+
}
|
|
887
|
+
/** Full audit record for a target selection per D-26. */
|
|
888
|
+
interface SelectionRecord {
|
|
889
|
+
readonly request_id: string;
|
|
890
|
+
readonly timestamp_ms: number;
|
|
891
|
+
readonly candidates: ReadonlyArray<{
|
|
892
|
+
readonly target_id: string;
|
|
893
|
+
readonly score: number;
|
|
894
|
+
readonly health_score: number;
|
|
895
|
+
readonly latency_ema_ms: number;
|
|
896
|
+
}>;
|
|
897
|
+
readonly excluded: ReadonlyArray<{
|
|
898
|
+
readonly target_id: string;
|
|
899
|
+
readonly reason: ExclusionReason;
|
|
900
|
+
}>;
|
|
901
|
+
readonly selected: {
|
|
902
|
+
readonly target_id: string;
|
|
903
|
+
readonly score: number;
|
|
904
|
+
readonly rank: number;
|
|
905
|
+
} | null;
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Circuit breaker interface per D-18.
|
|
909
|
+
* recordSuccess/recordFailure are async because they use cockatiel's
|
|
910
|
+
* execute() internally to properly manage state transitions.
|
|
911
|
+
*/
|
|
912
|
+
interface CircuitBreaker {
|
|
913
|
+
state(): TargetState;
|
|
914
|
+
recordSuccess(): Promise<void>;
|
|
915
|
+
recordFailure(): Promise<void>;
|
|
916
|
+
allowRequest(): boolean;
|
|
917
|
+
reset(): void;
|
|
918
|
+
}
|
|
919
|
+
/** Concurrency tracker interface per D-24. */
|
|
920
|
+
interface ConcurrencyTrackerApi {
|
|
921
|
+
acquire(target_id: string): boolean;
|
|
922
|
+
release(target_id: string): void;
|
|
923
|
+
headroom(target_id: string): number;
|
|
924
|
+
active(target_id: string): number;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Typed event bus for routing events per D-25.
|
|
929
|
+
*
|
|
930
|
+
* Uses eventemitter3 with a typed event map for type-safe emit/on.
|
|
931
|
+
* 6 event types: circuit_state_change, target_excluded, health_updated,
|
|
932
|
+
* admission_decision, cooldown_set, cooldown_expired.
|
|
933
|
+
*/
|
|
934
|
+
|
|
935
|
+
/** Typed event map for all routing events. */
|
|
936
|
+
interface RoutingEventMap {
|
|
937
|
+
circuit_state_change: (payload: {
|
|
938
|
+
target_id: string;
|
|
939
|
+
from: TargetState;
|
|
940
|
+
to: TargetState;
|
|
941
|
+
reason: string;
|
|
942
|
+
}) => void;
|
|
943
|
+
target_excluded: (payload: {
|
|
944
|
+
target_id: string;
|
|
945
|
+
reason: ExclusionReason;
|
|
946
|
+
}) => void;
|
|
947
|
+
health_updated: (payload: {
|
|
948
|
+
target_id: string;
|
|
949
|
+
old_score: number;
|
|
950
|
+
new_score: number;
|
|
951
|
+
}) => void;
|
|
952
|
+
admission_decision: (payload: {
|
|
953
|
+
request_id: string;
|
|
954
|
+
result: AdmissionResult;
|
|
955
|
+
reason: string;
|
|
956
|
+
}) => void;
|
|
957
|
+
cooldown_set: (payload: {
|
|
958
|
+
target_id: string;
|
|
959
|
+
until_ms: number;
|
|
960
|
+
error_class: string;
|
|
961
|
+
}) => void;
|
|
962
|
+
cooldown_expired: (payload: {
|
|
963
|
+
target_id: string;
|
|
964
|
+
}) => void;
|
|
965
|
+
}
|
|
966
|
+
/** Creates a typed event bus for routing events. */
|
|
967
|
+
declare const createRoutingEventBus: () => EventEmitter<RoutingEventMap>;
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* DualBreaker: composite IBreaker for cockatiel circuit breaker.
|
|
971
|
+
*
|
|
972
|
+
* Composes ConsecutiveBreaker (N consecutive failures) with
|
|
973
|
+
* CountBreaker (failure rate in sliding window). The circuit opens
|
|
974
|
+
* if EITHER trigger fires (OR logic per D-22 dual trigger).
|
|
975
|
+
*
|
|
976
|
+
* Uses CountBreaker (not SamplingBreaker) because D-22's sliding_window
|
|
977
|
+
* refers to request count, not time duration (Pitfall 2 from research).
|
|
978
|
+
*/
|
|
979
|
+
|
|
980
|
+
/** Composite breaker that trips on consecutive failures OR failure rate. */
|
|
981
|
+
declare class DualBreaker implements IBreaker {
|
|
982
|
+
private readonly consecutive;
|
|
983
|
+
private readonly count;
|
|
984
|
+
constructor(consecutiveThreshold: number, countOpts: {
|
|
985
|
+
threshold: number;
|
|
986
|
+
size: number;
|
|
987
|
+
minimumNumberOfCalls?: number;
|
|
988
|
+
});
|
|
989
|
+
/** Serializable state for both internal breakers. */
|
|
990
|
+
get state(): unknown;
|
|
991
|
+
/** Restore state for both internal breakers. */
|
|
992
|
+
set state(value: unknown);
|
|
993
|
+
/**
|
|
994
|
+
* Record success on both breakers.
|
|
995
|
+
* ConsecutiveBreaker.success() takes no args (resets counter).
|
|
996
|
+
* CountBreaker.success() takes CircuitState.
|
|
997
|
+
*/
|
|
998
|
+
success(state: CircuitState): void;
|
|
999
|
+
/**
|
|
1000
|
+
* Record failure on both breakers.
|
|
1001
|
+
* Returns true if EITHER breaker trips (OR logic).
|
|
1002
|
+
* ConsecutiveBreaker.failure() takes no args.
|
|
1003
|
+
* CountBreaker.failure() takes CircuitState.
|
|
1004
|
+
*/
|
|
1005
|
+
failure(state: CircuitState): boolean;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* CircuitBreaker wrapper around cockatiel's CircuitBreakerPolicy.
|
|
1010
|
+
*
|
|
1011
|
+
* Wraps cockatiel internals behind the CircuitBreaker interface from types.ts.
|
|
1012
|
+
* Maps cockatiel CircuitState to TargetState. Emits circuit_state_change events
|
|
1013
|
+
* via the routing event bus.
|
|
1014
|
+
*
|
|
1015
|
+
* Per D-18: cockatiel in wrapper pattern. We do NOT use execute() for routing;
|
|
1016
|
+
* instead we use recordSuccess/recordFailure to update the breaker state after
|
|
1017
|
+
* external request completion. Internally, we call policy.execute() with
|
|
1018
|
+
* success/failure functions to let cockatiel manage state transitions correctly.
|
|
1019
|
+
*
|
|
1020
|
+
* Note: cockatiel closes from HalfOpen after 1 success (native behavior).
|
|
1021
|
+
* This is accepted per research recommendation. D-22's success_threshold:2
|
|
1022
|
+
* is documented but not enforced at this layer.
|
|
1023
|
+
*/
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Create a circuit breaker for a specific target.
|
|
1027
|
+
*
|
|
1028
|
+
* @param targetId - Target identifier for event attribution.
|
|
1029
|
+
* @param config - Circuit breaker configuration.
|
|
1030
|
+
* @param eventBus - Optional event bus for state change notifications.
|
|
1031
|
+
* @returns CircuitBreaker interface implementation.
|
|
1032
|
+
*/
|
|
1033
|
+
declare const createCircuitBreaker: (targetId: string, config: CircuitBreakerConfig, eventBus?: EventEmitter<RoutingEventMap>) => CircuitBreaker;
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Per-target concurrency tracking per D-24.
|
|
1037
|
+
*
|
|
1038
|
+
* Tracks in-flight request counts per target with acquire/release semantics.
|
|
1039
|
+
* Each target has an independent slot counter with a configurable limit.
|
|
1040
|
+
*/
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Create a concurrency tracker with a default per-target limit.
|
|
1044
|
+
*
|
|
1045
|
+
* @param defaultLimit - Default concurrency limit for targets.
|
|
1046
|
+
* @returns ConcurrencyTrackerApi with additional setLimit method for per-target overrides.
|
|
1047
|
+
*/
|
|
1048
|
+
declare const createConcurrencyTracker: (defaultLimit: number) => ConcurrencyTrackerApi & {
|
|
1049
|
+
setLimit(target_id: string, limit: number): void;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Policy engine: filter-then-score target selection per D-17.
|
|
1054
|
+
*
|
|
1055
|
+
* Pure functions that select the best target from a registry snapshot.
|
|
1056
|
+
* No I/O, no external state dependencies -- deterministic for identical inputs.
|
|
1057
|
+
*
|
|
1058
|
+
* Score formula (D-17):
|
|
1059
|
+
* score = w_health * health_score
|
|
1060
|
+
* - w_latency * normalizeLatency(latency_ema_ms, maxLatencyMs)
|
|
1061
|
+
* - w_failure * failure_score
|
|
1062
|
+
* + w_priority * operator_priority
|
|
1063
|
+
*
|
|
1064
|
+
* Tie-breaking: target_id lexicographic ascending for full determinism (Pitfall 5).
|
|
1065
|
+
*
|
|
1066
|
+
* Exclusion order:
|
|
1067
|
+
* 1. !enabled -> 'disabled'
|
|
1068
|
+
* 2. PolicyBlocked -> 'policy_blocked'
|
|
1069
|
+
* 3. ReauthRequired -> 'reauth_required'
|
|
1070
|
+
* 4. Draining -> 'draining'
|
|
1071
|
+
* 5. Disabled state -> 'disabled'
|
|
1072
|
+
* 6. Circuit breaker denies -> 'circuit_open'
|
|
1073
|
+
* 7. Cooldown active -> 'cooldown_active'
|
|
1074
|
+
* 8. Capability mismatch -> 'capability_mismatch'
|
|
1075
|
+
*
|
|
1076
|
+
* Targets in `excludeTargets` are silently filtered (failover loop, D-19).
|
|
1077
|
+
*/
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* Normalizes latency to [0, 1] range using max expected latency.
|
|
1081
|
+
* Clamps to 1.0 when latency exceeds maxExpectedMs.
|
|
1082
|
+
*
|
|
1083
|
+
* @param latencyEmaMs - Exponential moving average latency in ms.
|
|
1084
|
+
* @param maxExpectedMs - Maximum expected latency for normalization.
|
|
1085
|
+
* @returns Normalized latency in [0, 1].
|
|
1086
|
+
*/
|
|
1087
|
+
declare const normalizeLatency: (latencyEmaMs: number, maxExpectedMs: number) => number;
|
|
1088
|
+
/**
|
|
1089
|
+
* Computes the routing score for a target using D-17 weighted formula.
|
|
1090
|
+
*
|
|
1091
|
+
* Higher score = better target.
|
|
1092
|
+
*
|
|
1093
|
+
* @param target - The target entry to score.
|
|
1094
|
+
* @param weights - Scoring weights configuration.
|
|
1095
|
+
* @param maxLatencyMs - Maximum expected latency for normalization.
|
|
1096
|
+
* @returns Numeric score for ranking.
|
|
1097
|
+
*/
|
|
1098
|
+
declare const computeScore: (target: TargetEntry, weights: RoutingWeights, maxLatencyMs: number) => number;
|
|
1099
|
+
/**
|
|
1100
|
+
* Checks a single target against all exclusion criteria.
|
|
1101
|
+
*
|
|
1102
|
+
* Returns the first matching ExclusionReason, or null if eligible.
|
|
1103
|
+
* Check order matters -- earlier checks take priority.
|
|
1104
|
+
*
|
|
1105
|
+
* @param target - Target to evaluate.
|
|
1106
|
+
* @param circuitBreakers - Circuit breaker map by target_id.
|
|
1107
|
+
* @param requiredCapabilities - Capabilities the request needs.
|
|
1108
|
+
* @param excludeTargets - Set of target_ids to silently skip (failover).
|
|
1109
|
+
* @param nowMs - Current timestamp for cooldown evaluation.
|
|
1110
|
+
* @returns ExclusionReason or null if eligible.
|
|
1111
|
+
*/
|
|
1112
|
+
declare const getExclusionReason: (target: TargetEntry, circuitBreakers: ReadonlyMap<string, CircuitBreaker>, requiredCapabilities: readonly string[], excludeTargets: ReadonlySet<string>, nowMs: number) => ExclusionReason | null;
|
|
1113
|
+
/**
|
|
1114
|
+
* Selects the best target from a registry snapshot using filter-then-score.
|
|
1115
|
+
*
|
|
1116
|
+
* 1. Filter: exclude ineligible targets, recording reasons.
|
|
1117
|
+
* 2. Silently filter targets in excludeTargets (failover re-selection prevention).
|
|
1118
|
+
* 3. Score: compute weighted score for each eligible target.
|
|
1119
|
+
* 4. Sort: by score DESC, then target_id ASC for determinism.
|
|
1120
|
+
* 5. Return full SelectionRecord per D-26.
|
|
1121
|
+
*
|
|
1122
|
+
* @param snapshot - Registry snapshot with all targets.
|
|
1123
|
+
* @param circuitBreakers - Circuit breaker map by target_id.
|
|
1124
|
+
* @param requiredCapabilities - Capabilities the request requires.
|
|
1125
|
+
* @param weights - Scoring weights for ranking.
|
|
1126
|
+
* @param maxLatencyMs - Max expected latency for normalization.
|
|
1127
|
+
* @param nowMs - Current timestamp for cooldown checks.
|
|
1128
|
+
* @param request_id - Request identifier for audit trail.
|
|
1129
|
+
* @param excludeTargets - Optional set of target_ids to silently skip.
|
|
1130
|
+
* @param logger - Optional pino logger for selection audit logging.
|
|
1131
|
+
* @returns SelectionRecord with full audit information.
|
|
1132
|
+
*/
|
|
1133
|
+
declare const selectTarget: (snapshot: RegistrySnapshot, circuitBreakers: ReadonlyMap<string, CircuitBreaker>, requiredCapabilities: readonly string[], weights: RoutingWeights, maxLatencyMs: number, nowMs: number, request_id: string, excludeTargets?: ReadonlySet<string>, logger?: pino.Logger) => SelectionRecord;
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Adaptive cooldown manager per D-23.
|
|
1137
|
+
*
|
|
1138
|
+
* computeCooldownMs: Pure function that calculates cooldown duration
|
|
1139
|
+
* based on error class, consecutive failures, and backoff configuration.
|
|
1140
|
+
* - RateLimited with retry_after_ms: uses retry_after as base
|
|
1141
|
+
* - RateLimited without retry_after_ms: exponential backoff
|
|
1142
|
+
* - TransientServerFailure: half the backoff duration
|
|
1143
|
+
* - Other error classes: fixed 5000ms base
|
|
1144
|
+
* - All results include jitter (10-25%) to prevent thundering herd (Pitfall 7)
|
|
1145
|
+
* - All results clamped to maxMs
|
|
1146
|
+
*
|
|
1147
|
+
* CooldownManager: Coordinates cooldown state with TargetRegistry
|
|
1148
|
+
* and event bus. Uses passive checking (no setTimeout, Pitfall 6).
|
|
1149
|
+
*/
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Computes the cooldown duration in milliseconds for a target.
|
|
1153
|
+
*
|
|
1154
|
+
* The duration adapts to the error class:
|
|
1155
|
+
* - RateLimited + retry_after_ms: uses server-provided value as base
|
|
1156
|
+
* - RateLimited (no retry_after): exponential backoff from consecutiveFailures
|
|
1157
|
+
* - TransientServerFailure: half the exponential backoff
|
|
1158
|
+
* - All others: fixed 5000ms base
|
|
1159
|
+
*
|
|
1160
|
+
* Jitter of 10-25% is added to prevent thundering herd.
|
|
1161
|
+
* Result is clamped to maxMs.
|
|
1162
|
+
*
|
|
1163
|
+
* @param errorClass - The classified error that triggered cooldown.
|
|
1164
|
+
* @param consecutiveFailures - Number of consecutive failures for this target.
|
|
1165
|
+
* @param backoffParams - Backoff configuration parameters.
|
|
1166
|
+
* @param maxMs - Maximum cooldown duration in ms.
|
|
1167
|
+
* @returns Cooldown duration in ms with jitter applied.
|
|
1168
|
+
*/
|
|
1169
|
+
declare const computeCooldownMs: (errorClass: ErrorClass, consecutiveFailures: number, backoffParams: BackoffParams, maxMs: number) => number;
|
|
1170
|
+
/** CooldownManager interface for coordinating cooldown state. */
|
|
1171
|
+
interface CooldownManager {
|
|
1172
|
+
/** Sets a target into cooldown for the given duration. */
|
|
1173
|
+
setCooldown(target_id: string, durationMs: number, errorClass: string): void;
|
|
1174
|
+
/** Checks all targets for expired cooldowns and transitions them to Active. */
|
|
1175
|
+
checkExpired(nowMs: number): readonly string[];
|
|
1176
|
+
/** Checks whether a specific target is currently in cooldown. */
|
|
1177
|
+
isInCooldown(target_id: string, nowMs: number): boolean;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Creates a CooldownManager that coordinates with TargetRegistry and event bus.
|
|
1181
|
+
*
|
|
1182
|
+
* Uses passive checking (no setTimeout) per Pitfall 6.
|
|
1183
|
+
* checkExpired should be called periodically or before routing decisions.
|
|
1184
|
+
*
|
|
1185
|
+
* @param registry - Target registry for state mutations.
|
|
1186
|
+
* @param eventBus - Optional event bus for cooldown events.
|
|
1187
|
+
* @returns CooldownManager instance.
|
|
1188
|
+
*/
|
|
1189
|
+
declare const createCooldownManager: (registry: TargetRegistry, eventBus?: EventEmitter<RoutingEventMap>) => CooldownManager;
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Layered admission controller per D-20.
|
|
1193
|
+
*
|
|
1194
|
+
* Three-layer design:
|
|
1195
|
+
* 1. checkHardRejects: Pure function checking 4 preconditions (reject fast path)
|
|
1196
|
+
* 2. Backpressure detection: queue.pending > threshold -> degraded
|
|
1197
|
+
* 3. Concurrency gating via p-queue: bounded concurrency and queue capacity
|
|
1198
|
+
*
|
|
1199
|
+
* Uses queue.size for backpressure (queue depth = items waiting, not running).
|
|
1200
|
+
* Note: p-queue semantics differ from plan assumption -- pending = running, size = waiting.
|
|
1201
|
+
*/
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Context for hard reject checks.
|
|
1205
|
+
* All fields are readonly -- pure input for the pure checkHardRejects function.
|
|
1206
|
+
*/
|
|
1207
|
+
interface AdmissionContext {
|
|
1208
|
+
readonly hasEligibleTargets: boolean;
|
|
1209
|
+
readonly operatorPaused: boolean;
|
|
1210
|
+
readonly retryBudgetRemaining: number;
|
|
1211
|
+
readonly failoverBudgetRemaining: number;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Configuration for the admission controller.
|
|
1215
|
+
*/
|
|
1216
|
+
interface AdmissionConfig {
|
|
1217
|
+
readonly concurrencyLimit: number;
|
|
1218
|
+
readonly queueLimit: number;
|
|
1219
|
+
readonly backpressureThreshold: number;
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Admission controller interface.
|
|
1223
|
+
*
|
|
1224
|
+
* admit() checks admission without executing.
|
|
1225
|
+
* execute() checks admission and runs the function through p-queue if admitted.
|
|
1226
|
+
*/
|
|
1227
|
+
interface AdmissionController {
|
|
1228
|
+
admit(request_id: string, ctx: AdmissionContext): Promise<AdmissionDecision>;
|
|
1229
|
+
execute<T>(fn: () => Promise<T>, request_id: string, ctx: AdmissionContext): Promise<{
|
|
1230
|
+
decision: AdmissionDecision;
|
|
1231
|
+
result?: T;
|
|
1232
|
+
}>;
|
|
1233
|
+
readonly pending: number;
|
|
1234
|
+
readonly size: number;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Layer 1: Pure hard reject checks.
|
|
1238
|
+
*
|
|
1239
|
+
* Returns an AdmissionDecision with result 'rejected' if any precondition
|
|
1240
|
+
* fails, or null if all checks pass (no hard reject).
|
|
1241
|
+
*
|
|
1242
|
+
* Check order:
|
|
1243
|
+
* 1. No eligible targets
|
|
1244
|
+
* 2. Operator pause active
|
|
1245
|
+
* 3. Retry budget exhausted
|
|
1246
|
+
* 4. Failover budget exhausted
|
|
1247
|
+
*
|
|
1248
|
+
* @param ctx - Admission context with current system state.
|
|
1249
|
+
* @returns Rejection decision or null if no hard reject applies.
|
|
1250
|
+
*/
|
|
1251
|
+
declare const checkHardRejects: (ctx: AdmissionContext) => AdmissionDecision | null;
|
|
1252
|
+
/**
|
|
1253
|
+
* Creates a layered admission controller with p-queue integration.
|
|
1254
|
+
*
|
|
1255
|
+
* Layer 1: checkHardRejects (pure, fast path)
|
|
1256
|
+
* Layer 2: Backpressure detection (queue.pending vs threshold)
|
|
1257
|
+
* Layer 3: Queue capacity check and p-queue concurrency gating
|
|
1258
|
+
*
|
|
1259
|
+
* @param config - Admission controller configuration.
|
|
1260
|
+
* @param eventBus - Optional event bus for admission_decision events.
|
|
1261
|
+
* @returns AdmissionController instance.
|
|
1262
|
+
*/
|
|
1263
|
+
declare const createAdmissionController: (config: AdmissionConfig, eventBus?: EventEmitter<RoutingEventMap>) => AdmissionController;
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Event bus log subscriber for routing events.
|
|
1267
|
+
*
|
|
1268
|
+
* Subscribes to all RoutingEventMap events and logs them via pino.
|
|
1269
|
+
* Per D-74: child logger with component context.
|
|
1270
|
+
* Per D-75: info for decisions, warn for degraded, debug for scoring.
|
|
1271
|
+
* Per D-79: circuit breaker transitions at info.
|
|
1272
|
+
* Per D-80: cooldown set/expired at info.
|
|
1273
|
+
* Per D-81: admission at info (admitted) or warn (rejected/degraded).
|
|
1274
|
+
*/
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Creates a log subscriber for routing events.
|
|
1278
|
+
*
|
|
1279
|
+
* Subscribes to all 6 event types on the provided event bus and logs
|
|
1280
|
+
* each at the appropriate level using a child logger with component context.
|
|
1281
|
+
*
|
|
1282
|
+
* @param eventBus - Typed routing event bus.
|
|
1283
|
+
* @param logger - Base pino logger (child with component:'routing' is created).
|
|
1284
|
+
* @returns A dispose function that removes all listeners.
|
|
1285
|
+
*/
|
|
1286
|
+
declare const createLogSubscriber: (eventBus: EventEmitter<RoutingEventMap>, logger: pino.Logger) => (() => void);
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Nested retry-failover orchestrator per D-19.
|
|
1290
|
+
*
|
|
1291
|
+
* Implements the two-loop structure:
|
|
1292
|
+
* - Outer loop: failover across targets (bounded by failoverBudget)
|
|
1293
|
+
* - Inner loop: retry on same target (bounded by retryBudget)
|
|
1294
|
+
*
|
|
1295
|
+
* Decision tree per attempt:
|
|
1296
|
+
* 1. Success -> return immediately, record success
|
|
1297
|
+
* 2. ModelUnavailable -> early failover (break inner, try next target)
|
|
1298
|
+
* 3. Non-retryable (Auth, Permission, Quota, Policy, Region) -> abort entirely
|
|
1299
|
+
* 4. Retryable + budget remaining -> retry with backoff
|
|
1300
|
+
* 5. Retryable + budget exhausted -> failover to next target
|
|
1301
|
+
*
|
|
1302
|
+
* Composes: selectTarget (policy engine), createRetryPolicy (retry),
|
|
1303
|
+
* circuit breakers, cooldown manager, concurrency tracker.
|
|
1304
|
+
*/
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Minimal registry interface used by the failover orchestrator.
|
|
1308
|
+
* Decoupled from concrete TargetRegistry class for testability.
|
|
1309
|
+
*/
|
|
1310
|
+
interface FailoverRegistry {
|
|
1311
|
+
getSnapshot(): RegistrySnapshot;
|
|
1312
|
+
recordObservation(id: string, observation: 0 | 1): void;
|
|
1313
|
+
updateLatency(id: string, ms: number): void;
|
|
1314
|
+
updateState(id: string, newState: TargetState): boolean;
|
|
1315
|
+
setCooldown(id: string, untilMs: number): void;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Record of a single attempt within the failover loop.
|
|
1320
|
+
*/
|
|
1321
|
+
interface FailoverAttempt {
|
|
1322
|
+
readonly target_id: string;
|
|
1323
|
+
/** 1-based attempt number within this target. */
|
|
1324
|
+
readonly attempt_no: number;
|
|
1325
|
+
/** 0-based failover iteration. */
|
|
1326
|
+
readonly failover_no: number;
|
|
1327
|
+
readonly outcome: 'success' | 'retry' | 'failover' | 'abort';
|
|
1328
|
+
readonly error_class?: string;
|
|
1329
|
+
readonly latency_ms: number;
|
|
1330
|
+
readonly selection: SelectionRecord;
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Result of the failover orchestrator: success with value or failure with reason.
|
|
1334
|
+
*/
|
|
1335
|
+
type FailoverResult = {
|
|
1336
|
+
readonly outcome: 'success';
|
|
1337
|
+
readonly target_id: string;
|
|
1338
|
+
readonly attempts: readonly FailoverAttempt[];
|
|
1339
|
+
readonly total_retries: number;
|
|
1340
|
+
readonly total_failovers: number;
|
|
1341
|
+
readonly value: unknown;
|
|
1342
|
+
} | {
|
|
1343
|
+
readonly outcome: 'failure';
|
|
1344
|
+
readonly reason: string;
|
|
1345
|
+
readonly attempts: readonly FailoverAttempt[];
|
|
1346
|
+
readonly total_retries: number;
|
|
1347
|
+
readonly total_failovers: number;
|
|
1348
|
+
readonly last_error_class?: string;
|
|
1349
|
+
};
|
|
1350
|
+
/**
|
|
1351
|
+
* Function type for executing a request attempt against a target.
|
|
1352
|
+
* Provided by the caller (Phase 3 execution layer).
|
|
1353
|
+
*/
|
|
1354
|
+
type AttemptFn = (target_id: string) => Promise<{
|
|
1355
|
+
success: boolean;
|
|
1356
|
+
value?: unknown;
|
|
1357
|
+
error_class?: ErrorClass;
|
|
1358
|
+
latency_ms: number;
|
|
1359
|
+
}>;
|
|
1360
|
+
/**
|
|
1361
|
+
* Dependencies for the failover orchestrator.
|
|
1362
|
+
*/
|
|
1363
|
+
interface FailoverDeps {
|
|
1364
|
+
readonly registry: FailoverRegistry;
|
|
1365
|
+
readonly circuitBreakers: ReadonlyMap<string, CircuitBreaker>;
|
|
1366
|
+
readonly concurrency: ConcurrencyTrackerApi;
|
|
1367
|
+
readonly cooldownManager: CooldownManager;
|
|
1368
|
+
readonly eventBus?: EventEmitter<RoutingEventMap>;
|
|
1369
|
+
readonly weights: RoutingWeights;
|
|
1370
|
+
readonly maxLatencyMs: number;
|
|
1371
|
+
readonly retryBudget: number;
|
|
1372
|
+
readonly failoverBudget: number;
|
|
1373
|
+
readonly backoffParams: BackoffParams;
|
|
1374
|
+
/** Optional pino logger for structured failover logging (Phase 8). */
|
|
1375
|
+
readonly logger?: pino.Logger;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Failover orchestrator interface.
|
|
1379
|
+
*/
|
|
1380
|
+
interface FailoverOrchestrator {
|
|
1381
|
+
execute(request_id: string, attemptFn: AttemptFn, requiredCapabilities: readonly string[]): Promise<FailoverResult>;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Creates a nested retry-failover orchestrator.
|
|
1385
|
+
*
|
|
1386
|
+
* The outer loop iterates up to failoverBudget + 1 targets.
|
|
1387
|
+
* The inner loop iterates up to retryBudget + 1 attempts per target.
|
|
1388
|
+
* Maximum theoretical attempts = (failoverBudget + 1) * (retryBudget + 1).
|
|
1389
|
+
*
|
|
1390
|
+
* @param deps - Orchestrator dependencies.
|
|
1391
|
+
* @returns FailoverOrchestrator instance.
|
|
1392
|
+
*/
|
|
1393
|
+
declare const createFailoverOrchestrator: (deps: FailoverDeps) => FailoverOrchestrator;
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Execution layer types for Phase 3.
|
|
1397
|
+
*
|
|
1398
|
+
* Defines StreamChunk, AdapterRequest, AdapterResult, ModeAdapter,
|
|
1399
|
+
* ContinuationMode, SegmentProvenance, StitchedOutput,
|
|
1400
|
+
* ExecutionProvenance, and ExecutionResult.
|
|
1401
|
+
*
|
|
1402
|
+
* Per D-27 through D-38.
|
|
1403
|
+
*/
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* A single chunk of streamed output with position and timing.
|
|
1407
|
+
*/
|
|
1408
|
+
interface StreamChunk {
|
|
1409
|
+
readonly text: string;
|
|
1410
|
+
readonly index: number;
|
|
1411
|
+
readonly timestamp_ms: number;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Request payload for a mode adapter execution.
|
|
1415
|
+
*/
|
|
1416
|
+
interface AdapterRequest {
|
|
1417
|
+
readonly request_id: string;
|
|
1418
|
+
readonly prompt: string;
|
|
1419
|
+
readonly params?: Record<string, unknown>;
|
|
1420
|
+
readonly signal?: AbortSignal;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Result from a mode adapter execution attempt.
|
|
1424
|
+
*
|
|
1425
|
+
* detection_mode indicates whether error classification used
|
|
1426
|
+
* raw HTTP signals ('direct') or message pattern matching ('heuristic').
|
|
1427
|
+
*/
|
|
1428
|
+
interface AdapterResult {
|
|
1429
|
+
readonly success: boolean;
|
|
1430
|
+
readonly chunks: readonly StreamChunk[];
|
|
1431
|
+
readonly error_class?: ErrorClass;
|
|
1432
|
+
readonly latency_ms: number;
|
|
1433
|
+
readonly http_status?: number;
|
|
1434
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
1435
|
+
readonly detection_mode: 'direct' | 'heuristic';
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Mode adapter interface per D-27.
|
|
1439
|
+
*
|
|
1440
|
+
* Each deployment mode (plugin-only, server-companion, sdk-control)
|
|
1441
|
+
* provides an adapter that executes requests against a target.
|
|
1442
|
+
*/
|
|
1443
|
+
interface ModeAdapter {
|
|
1444
|
+
execute(target_id: string, request: AdapterRequest): Promise<AdapterResult>;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Continuation mode per D-31.
|
|
1448
|
+
*
|
|
1449
|
+
* Describes how a resumed segment relates to the interrupted one:
|
|
1450
|
+
* - same_target_resume: retry on the same target
|
|
1451
|
+
* - same_model_alternate_target: same model, different provider target
|
|
1452
|
+
* - cross_model_semantic: different model entirely (non-deterministic)
|
|
1453
|
+
* - controlled_reexecution: full re-execution when no viable continuation exists
|
|
1454
|
+
*/
|
|
1455
|
+
type ContinuationMode = 'same_target_resume' | 'same_model_alternate_target' | 'cross_model_semantic' | 'controlled_reexecution';
|
|
1456
|
+
/**
|
|
1457
|
+
* Provenance metadata for a single segment of stitched output per D-32, D-33.
|
|
1458
|
+
*
|
|
1459
|
+
* non_deterministic is true when continuation crosses model boundaries,
|
|
1460
|
+
* meaning the output may not be semantically consistent with the prior segment.
|
|
1461
|
+
*/
|
|
1462
|
+
interface SegmentProvenance {
|
|
1463
|
+
readonly request_id: string;
|
|
1464
|
+
readonly segment_id: number;
|
|
1465
|
+
readonly source_target_id: string;
|
|
1466
|
+
readonly continuation_mode: ContinuationMode;
|
|
1467
|
+
readonly failover_reason?: string;
|
|
1468
|
+
readonly visible_offset: number;
|
|
1469
|
+
readonly non_deterministic: boolean;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Assembled multi-segment output with provenance per STRM-05, D-33.
|
|
1473
|
+
*
|
|
1474
|
+
* continuation_boundaries marks character positions where segments join.
|
|
1475
|
+
*/
|
|
1476
|
+
interface StitchedOutput {
|
|
1477
|
+
readonly text: string;
|
|
1478
|
+
readonly segments: readonly SegmentProvenance[];
|
|
1479
|
+
readonly continuation_boundaries: readonly number[];
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Full execution provenance per D-37, D-38.
|
|
1483
|
+
*
|
|
1484
|
+
* Tracks all segments, continuation modes, involved targets,
|
|
1485
|
+
* and whether the output is degraded.
|
|
1486
|
+
*/
|
|
1487
|
+
interface ExecutionProvenance {
|
|
1488
|
+
readonly request_id: string;
|
|
1489
|
+
readonly segments: readonly SegmentProvenance[];
|
|
1490
|
+
readonly continuation_modes: readonly ContinuationMode[];
|
|
1491
|
+
readonly targets_involved: readonly string[];
|
|
1492
|
+
readonly total_attempts: number;
|
|
1493
|
+
readonly degraded: boolean;
|
|
1494
|
+
readonly degraded_reason?: string;
|
|
1495
|
+
readonly heuristic_detection: boolean;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Final execution result combining output text with provenance.
|
|
1499
|
+
*/
|
|
1500
|
+
interface ExecutionResult {
|
|
1501
|
+
readonly success: boolean;
|
|
1502
|
+
readonly output: string;
|
|
1503
|
+
readonly provenance: ExecutionProvenance;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* StreamBuffer interface for accumulating stream chunks.
|
|
1507
|
+
*
|
|
1508
|
+
* Append-only buffer that provides confirmed text snapshot
|
|
1509
|
+
* on interruption for stream resume/stitching.
|
|
1510
|
+
*/
|
|
1511
|
+
interface StreamBuffer {
|
|
1512
|
+
append(chunk: StreamChunk): number;
|
|
1513
|
+
confirmed(): readonly StreamChunk[];
|
|
1514
|
+
confirmedCharCount(): number;
|
|
1515
|
+
snapshot(): string;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Append-only stream buffer with confirmed boundary per D-30.
|
|
1520
|
+
*
|
|
1521
|
+
* Accumulates StreamChunks during a streaming response.
|
|
1522
|
+
* On interruption, confirmed() returns all chunks received so far,
|
|
1523
|
+
* and snapshot() returns the concatenated confirmed text for
|
|
1524
|
+
* stream stitching.
|
|
1525
|
+
*/
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* Creates a new StreamBuffer instance.
|
|
1529
|
+
*
|
|
1530
|
+
* The buffer is append-only: chunks can be added but never removed.
|
|
1531
|
+
* This preserves a reliable confirmed boundary for stream resume.
|
|
1532
|
+
*
|
|
1533
|
+
* @returns A StreamBuffer instance.
|
|
1534
|
+
*/
|
|
1535
|
+
declare const createStreamBuffer: () => StreamBuffer;
|
|
1536
|
+
|
|
1537
|
+
/**
|
|
1538
|
+
* Multi-segment stream stitcher with provenance metadata per D-31 through D-33.
|
|
1539
|
+
*
|
|
1540
|
+
* Assembles output from multiple segments (potentially from different targets
|
|
1541
|
+
* after failover) into a single StitchedOutput with per-segment provenance
|
|
1542
|
+
* and continuation boundary markers.
|
|
1543
|
+
*
|
|
1544
|
+
* Cross-target continuations are annotated as non_deterministic per D-33/TRAN-02
|
|
1545
|
+
* because the output may not be semantically consistent across model boundaries.
|
|
1546
|
+
*/
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* StreamStitcher interface for assembling multi-segment output.
|
|
1550
|
+
*/
|
|
1551
|
+
interface StreamStitcher {
|
|
1552
|
+
addSegment(buffer: StreamBuffer, provenance: Omit<SegmentProvenance, 'visible_offset'>): void;
|
|
1553
|
+
assemble(): StitchedOutput;
|
|
1554
|
+
segmentCount(): number;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Determines the continuation mode based on target and model relationship per D-31.
|
|
1558
|
+
*
|
|
1559
|
+
* - Same target: same_target_resume (deterministic)
|
|
1560
|
+
* - Different target, same model: same_model_alternate_target
|
|
1561
|
+
* - Different target, different model: cross_model_semantic (non-deterministic)
|
|
1562
|
+
*
|
|
1563
|
+
* Note: controlled_reexecution is set by the orchestrator when no viable
|
|
1564
|
+
* continuation target exists, not by the stitcher.
|
|
1565
|
+
*
|
|
1566
|
+
* @param oldTargetId - The target ID of the previous segment.
|
|
1567
|
+
* @param newTargetId - The target ID of the new segment.
|
|
1568
|
+
* @param sameModel - Whether both targets serve the same model.
|
|
1569
|
+
* @returns The appropriate ContinuationMode.
|
|
1570
|
+
*/
|
|
1571
|
+
declare const determineContinuationMode: (oldTargetId: string, newTargetId: string, sameModel: boolean) => ContinuationMode;
|
|
1572
|
+
/**
|
|
1573
|
+
* Creates a new StreamStitcher instance.
|
|
1574
|
+
*
|
|
1575
|
+
* The stitcher accumulates segments via addSegment() and produces
|
|
1576
|
+
* a final StitchedOutput via assemble() with:
|
|
1577
|
+
* - Concatenated text from all segments
|
|
1578
|
+
* - Per-segment provenance with computed visible_offset
|
|
1579
|
+
* - continuation_boundaries marking join points between segments
|
|
1580
|
+
*
|
|
1581
|
+
* @param _requestId - The correlation request ID (reserved for future audit use).
|
|
1582
|
+
* @returns A StreamStitcher instance.
|
|
1583
|
+
*/
|
|
1584
|
+
declare const createStreamStitcher: (_requestId: string) => StreamStitcher;
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* Audit collector for per-request audit trail accumulation.
|
|
1588
|
+
*
|
|
1589
|
+
* Subscribes to routing events and accumulates FailoverAttempt
|
|
1590
|
+
* and SegmentProvenance records per request. On flush(), emits
|
|
1591
|
+
* a complete AuditRecord to pino with all AUDT-02 fields
|
|
1592
|
+
* (request_id, attempt_no, segment_no, selected_target, candidates,
|
|
1593
|
+
* selection_reason, failure_class, retry_no, failover_no, latency_ms, outcome).
|
|
1594
|
+
*
|
|
1595
|
+
* flush() is always called in a finally block even on zero-attempt
|
|
1596
|
+
* failures per Pitfall 3.
|
|
1597
|
+
*/
|
|
1598
|
+
|
|
1599
|
+
/**
|
|
1600
|
+
* AuditCollector interface for accumulating and flushing audit records.
|
|
1601
|
+
*
|
|
1602
|
+
* Single-use per request: after flush(), the collector emits
|
|
1603
|
+
* its accumulated data and is considered complete.
|
|
1604
|
+
*/
|
|
1605
|
+
interface AuditCollector {
|
|
1606
|
+
/** Records a failover attempt. */
|
|
1607
|
+
recordAttempt(attempt: FailoverAttempt): void;
|
|
1608
|
+
/** Records a segment provenance entry. */
|
|
1609
|
+
recordSegment(segment: SegmentProvenance): void;
|
|
1610
|
+
/** Flushes the accumulated audit record to pino. */
|
|
1611
|
+
flush(outcome: 'success' | 'failure', finalTarget?: string, stats?: {
|
|
1612
|
+
total_retries?: number;
|
|
1613
|
+
total_failovers?: number;
|
|
1614
|
+
latency_ms?: number;
|
|
1615
|
+
}): void;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Creates an AuditCollector for a specific request.
|
|
1619
|
+
*
|
|
1620
|
+
* The collector accumulates FailoverAttempt and SegmentProvenance
|
|
1621
|
+
* records and flushes them as a single pino log entry with
|
|
1622
|
+
* event_type 'request_complete'.
|
|
1623
|
+
*
|
|
1624
|
+
* @param logger - The base pino logger (a child logger with request_id is created internally).
|
|
1625
|
+
* @param requestId - The correlation request ID for this request.
|
|
1626
|
+
* @returns An AuditCollector instance.
|
|
1627
|
+
*/
|
|
1628
|
+
declare const createAuditCollector: (logger: pino.Logger, requestId: string) => AuditCollector;
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Execution orchestrator: wires ModeAdapter + FailoverOrchestrator +
|
|
1632
|
+
* StreamStitcher + AuditCollector into a single execute call.
|
|
1633
|
+
*
|
|
1634
|
+
* Produces ExecutionResult with full provenance metadata including
|
|
1635
|
+
* segments, continuation_modes, targets_involved, degraded flag (D-38),
|
|
1636
|
+
* and heuristic_detection flag (D-37).
|
|
1637
|
+
*
|
|
1638
|
+
* AuditCollector.flush() is ALWAYS called via try/finally even on
|
|
1639
|
+
* zero-attempt failures (Pitfall 3).
|
|
1640
|
+
*/
|
|
1641
|
+
|
|
1642
|
+
/**
|
|
1643
|
+
* Dependencies for the execution orchestrator.
|
|
1644
|
+
*/
|
|
1645
|
+
interface ExecutionDeps {
|
|
1646
|
+
/** Mode adapter for executing requests against targets. */
|
|
1647
|
+
readonly adapter: ModeAdapter;
|
|
1648
|
+
/** Failover orchestrator for retry-failover logic. */
|
|
1649
|
+
readonly failover: FailoverOrchestrator;
|
|
1650
|
+
/** Base pino logger for audit trail. */
|
|
1651
|
+
readonly logger: pino.Logger;
|
|
1652
|
+
/** Current deployment mode. */
|
|
1653
|
+
readonly mode: DeploymentMode;
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Execution orchestrator interface.
|
|
1657
|
+
*/
|
|
1658
|
+
interface ExecutionOrchestrator {
|
|
1659
|
+
/** Executes a request through the full pipeline. */
|
|
1660
|
+
execute(request: AdapterRequest): Promise<ExecutionResult>;
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Creates an execution orchestrator that composes adapter, failover,
|
|
1664
|
+
* stream stitcher, and audit collector into an end-to-end pipeline.
|
|
1665
|
+
*
|
|
1666
|
+
* @param deps - Execution dependencies.
|
|
1667
|
+
* @returns An ExecutionOrchestrator instance.
|
|
1668
|
+
*/
|
|
1669
|
+
declare const createExecutionOrchestrator: (deps: ExecutionDeps) => ExecutionOrchestrator;
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Shared adapter configuration types.
|
|
1673
|
+
*
|
|
1674
|
+
* Provides the dependency injection interface for all mode adapters.
|
|
1675
|
+
*/
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Dependencies injected into mode adapters.
|
|
1679
|
+
*
|
|
1680
|
+
* classifyError is the unified error classification function
|
|
1681
|
+
* that routes to direct or heuristic classification.
|
|
1682
|
+
*/
|
|
1683
|
+
interface AdapterDeps {
|
|
1684
|
+
readonly classifyError: (signal: NormalizedSignal) => ClassificationResult;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Adapter factory: selects the correct ModeAdapter by DeploymentMode.
|
|
1689
|
+
*
|
|
1690
|
+
* Provides a single entry point for creating the appropriate adapter
|
|
1691
|
+
* based on the resolved deployment mode. Uses exhaustive switch
|
|
1692
|
+
* with never default for type safety.
|
|
1693
|
+
*/
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Creates the appropriate ModeAdapter for the given deployment mode.
|
|
1697
|
+
*
|
|
1698
|
+
* @param mode - The resolved deployment mode.
|
|
1699
|
+
* @param deps - Shared adapter dependencies.
|
|
1700
|
+
* @returns The ModeAdapter implementation for the specified mode.
|
|
1701
|
+
* @throws Error if an unknown deployment mode is provided (compile-time exhaustive check).
|
|
1702
|
+
*/
|
|
1703
|
+
declare const createModeAdapter: (mode: DeploymentMode, deps: AdapterDeps) => ModeAdapter;
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Operator command result types.
|
|
1707
|
+
*
|
|
1708
|
+
* All types are readonly interfaces per D-39. Operator commands
|
|
1709
|
+
* return structured results for programmatic consumption.
|
|
1710
|
+
*/
|
|
1711
|
+
|
|
1712
|
+
/** Dependencies injected into all operator commands. */
|
|
1713
|
+
interface OperatorDeps {
|
|
1714
|
+
readonly registry: TargetRegistry;
|
|
1715
|
+
readonly circuitBreakers: ReadonlyMap<string, CircuitBreaker>;
|
|
1716
|
+
readonly configRef: ConfigRef;
|
|
1717
|
+
readonly logger: pino.Logger;
|
|
1718
|
+
readonly traceBuffer: RequestTraceBuffer;
|
|
1719
|
+
}
|
|
1720
|
+
/** Mutable config reference with atomic swap for reload. */
|
|
1721
|
+
interface ConfigRef {
|
|
1722
|
+
current(): SwitcherConfig;
|
|
1723
|
+
swap(config: SwitcherConfig): void;
|
|
1724
|
+
}
|
|
1725
|
+
/** Enriched target view returned by listTargets. */
|
|
1726
|
+
interface TargetView {
|
|
1727
|
+
readonly target_id: string;
|
|
1728
|
+
readonly provider_id: string;
|
|
1729
|
+
readonly profile: string | undefined;
|
|
1730
|
+
readonly state: TargetState;
|
|
1731
|
+
readonly health_score: number;
|
|
1732
|
+
readonly circuit_breaker_state: TargetState;
|
|
1733
|
+
readonly cooldown_until: number | null;
|
|
1734
|
+
readonly enabled: boolean;
|
|
1735
|
+
readonly latency_ema_ms: number;
|
|
1736
|
+
readonly failure_score: number;
|
|
1737
|
+
readonly operator_priority: number;
|
|
1738
|
+
}
|
|
1739
|
+
/** Result of listTargets command. */
|
|
1740
|
+
interface ListTargetsResult {
|
|
1741
|
+
readonly targets: ReadonlyArray<TargetView>;
|
|
1742
|
+
readonly count: number;
|
|
1743
|
+
}
|
|
1744
|
+
/** Result of pauseTarget, resumeTarget, drainTarget, disableTarget commands. */
|
|
1745
|
+
interface TargetActionResult {
|
|
1746
|
+
readonly success: boolean;
|
|
1747
|
+
readonly target_id: string;
|
|
1748
|
+
readonly action: string;
|
|
1749
|
+
readonly error?: string;
|
|
1750
|
+
}
|
|
1751
|
+
/** Result of inspectRequest command. */
|
|
1752
|
+
interface InspectResult {
|
|
1753
|
+
readonly found: boolean;
|
|
1754
|
+
readonly request_id: string;
|
|
1755
|
+
readonly trace?: RequestTraceEntry;
|
|
1756
|
+
}
|
|
1757
|
+
/** Result of reloadConfig command. */
|
|
1758
|
+
interface ReloadResult {
|
|
1759
|
+
readonly success: boolean;
|
|
1760
|
+
readonly added: readonly string[];
|
|
1761
|
+
readonly removed: readonly string[];
|
|
1762
|
+
readonly modified: readonly string[];
|
|
1763
|
+
readonly diagnostics?: ReadonlyArray<ConfigDiagnostic>;
|
|
1764
|
+
}
|
|
1765
|
+
/** A single request trace entry stored in the ring buffer. */
|
|
1766
|
+
interface RequestTraceEntry {
|
|
1767
|
+
readonly request_id: string;
|
|
1768
|
+
readonly timestamp_ms: number;
|
|
1769
|
+
readonly outcome: string;
|
|
1770
|
+
readonly target_id?: string;
|
|
1771
|
+
readonly total_attempts: number;
|
|
1772
|
+
readonly total_segments: number;
|
|
1773
|
+
readonly attempts: readonly unknown[];
|
|
1774
|
+
readonly segments: readonly unknown[];
|
|
1775
|
+
}
|
|
1776
|
+
/** Ring buffer interface for request trace storage. */
|
|
1777
|
+
interface RequestTraceBuffer {
|
|
1778
|
+
/** Records a trace entry, evicting the oldest if at capacity. */
|
|
1779
|
+
record(entry: RequestTraceEntry): void;
|
|
1780
|
+
/** Looks up a trace entry by request ID. Returns undefined if not found. */
|
|
1781
|
+
lookup(requestId: string): RequestTraceEntry | undefined;
|
|
1782
|
+
/** Returns current number of entries in the buffer. */
|
|
1783
|
+
size(): number;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Pure operator command functions per D-39.
|
|
1788
|
+
*
|
|
1789
|
+
* All commands take OperatorDeps and return structured results.
|
|
1790
|
+
* No separate operator state -- all mutations go through TargetRegistry (D-40).
|
|
1791
|
+
* All commands work without code changes (OPER-05) -- they are runtime function calls.
|
|
1792
|
+
*/
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Creates a ring buffer for request trace storage.
|
|
1796
|
+
*
|
|
1797
|
+
* Uses Map for O(1) lookup and array for insertion order tracking.
|
|
1798
|
+
* When size exceeds capacity, oldest entries are evicted.
|
|
1799
|
+
*
|
|
1800
|
+
* @param capacity - Maximum number of entries to store.
|
|
1801
|
+
*/
|
|
1802
|
+
declare const createRequestTraceBuffer: (capacity: number) => RequestTraceBuffer;
|
|
1803
|
+
/**
|
|
1804
|
+
* Lists all targets with enriched circuit breaker state.
|
|
1805
|
+
* Per D-39: returns target_id, state, health_score, circuit_breaker_state,
|
|
1806
|
+
* cooldown_until, enabled, latency_ema_ms, failure_score, operator_priority.
|
|
1807
|
+
*/
|
|
1808
|
+
declare const listTargets: (deps: OperatorDeps) => ListTargetsResult;
|
|
1809
|
+
/** Pauses a target by setting state to Disabled. Per research: reuse Disabled with semantic action name. */
|
|
1810
|
+
declare const pauseTarget: (deps: OperatorDeps, targetId: string) => TargetActionResult;
|
|
1811
|
+
/** Resumes a target by setting state to Active. */
|
|
1812
|
+
declare const resumeTarget: (deps: OperatorDeps, targetId: string) => TargetActionResult;
|
|
1813
|
+
/** Drains a target by setting state to Draining. No new requests will be routed. */
|
|
1814
|
+
declare const drainTarget: (deps: OperatorDeps, targetId: string) => TargetActionResult;
|
|
1815
|
+
/** Disables a target by setting state to Disabled. */
|
|
1816
|
+
declare const disableTarget: (deps: OperatorDeps, targetId: string) => TargetActionResult;
|
|
1817
|
+
/** Inspects a request by looking up its trace in the ring buffer. */
|
|
1818
|
+
declare const inspectRequest: (deps: OperatorDeps, requestId: string) => InspectResult;
|
|
1819
|
+
/**
|
|
1820
|
+
* Reloads configuration with diff-apply logic.
|
|
1821
|
+
*
|
|
1822
|
+
* 1. Validates raw config via validateConfig
|
|
1823
|
+
* 2. On validation failure: returns diagnostics, no changes applied (D-43)
|
|
1824
|
+
* 3. On success: computes diff, applies to registry, swaps config reference
|
|
1825
|
+
*/
|
|
1826
|
+
declare const reloadConfig: (deps: OperatorDeps, rawConfig: unknown) => ReloadResult;
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Config reload with diff-apply logic per D-42, D-43.
|
|
1830
|
+
*
|
|
1831
|
+
* computeConfigDiff: Three-pass diff (added, removed, modified) per Pitfall 4.
|
|
1832
|
+
* applyConfigDiff: Applies diff to registry. Removed targets are Disabled
|
|
1833
|
+
* (not deleted) to protect in-flight requests per Pitfall 1.
|
|
1834
|
+
*/
|
|
1835
|
+
|
|
1836
|
+
/** The result of diffing old and new configuration. */
|
|
1837
|
+
interface ConfigDiff {
|
|
1838
|
+
readonly added: readonly TargetConfig[];
|
|
1839
|
+
readonly removed: readonly string[];
|
|
1840
|
+
readonly modified: readonly TargetConfig[];
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Computes a three-pass diff between old and new configs per Pitfall 4.
|
|
1844
|
+
*
|
|
1845
|
+
* Pass 1: Find targets in newConfig not in oldConfig (added)
|
|
1846
|
+
* Pass 2: Find targets in oldConfig not in newConfig (removed)
|
|
1847
|
+
* Pass 3: For intersection, compare mutable fields (modified)
|
|
1848
|
+
*/
|
|
1849
|
+
declare const computeConfigDiff: (oldConfig: SwitcherConfig, newConfig: SwitcherConfig) => ConfigDiff;
|
|
1850
|
+
/**
|
|
1851
|
+
* Applies a config diff to the registry.
|
|
1852
|
+
*
|
|
1853
|
+
* - Added targets: creates fresh TargetEntry with INITIAL_HEALTH_SCORE
|
|
1854
|
+
* - Removed targets: sets state to Disabled (not deleted) per Pitfall 1
|
|
1855
|
+
* - Modified targets: rebuilds entry with updated config fields
|
|
1856
|
+
*/
|
|
1857
|
+
declare const applyConfigDiff: (registry: TargetRegistry, diff: ConfigDiff) => void;
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* Bearer token authentication middleware for server-companion mode.
|
|
1861
|
+
*
|
|
1862
|
+
* Per D-44: Server-companion mode operator endpoints require Bearer token
|
|
1863
|
+
* authentication. Plugin-only mode commands inherit OpenCode session auth.
|
|
1864
|
+
*
|
|
1865
|
+
* Per Pitfall 3: Uses crypto.timingSafeEqual for constant-time token
|
|
1866
|
+
* comparison to prevent timing attacks.
|
|
1867
|
+
*/
|
|
1868
|
+
/** Result of bearer token validation. */
|
|
1869
|
+
interface AuthResult {
|
|
1870
|
+
readonly authorized: boolean;
|
|
1871
|
+
readonly reason?: string;
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Validates a Bearer token from the Authorization header.
|
|
1875
|
+
*
|
|
1876
|
+
* @param authHeader - The raw Authorization header value, or undefined if absent.
|
|
1877
|
+
* @param expectedToken - The expected token to compare against.
|
|
1878
|
+
* @returns AuthResult indicating whether the request is authorized.
|
|
1879
|
+
*/
|
|
1880
|
+
declare const validateBearerToken: (authHeader: string | undefined, expectedToken: string) => AuthResult;
|
|
1881
|
+
|
|
1882
|
+
/**
|
|
1883
|
+
* OpenCode plugin tool wrappers for operator commands.
|
|
1884
|
+
*
|
|
1885
|
+
* Per D-39 and OPER-05: Each operator command is exposed as an OpenCode
|
|
1886
|
+
* plugin tool with Zod-validated args. Plugin tools return JSON-stringified
|
|
1887
|
+
* results from the pure command functions.
|
|
1888
|
+
*
|
|
1889
|
+
* Uses @opencode-ai/plugin/tool for tool definition with Zod schema.
|
|
1890
|
+
*/
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* Creates operator tool definitions bound to the given dependencies.
|
|
1894
|
+
*
|
|
1895
|
+
* Each tool wraps a pure operator command function, validates args via Zod,
|
|
1896
|
+
* and returns JSON-stringified results.
|
|
1897
|
+
*
|
|
1898
|
+
* @param deps - Operator dependencies (registry, circuit breakers, config, logger, trace buffer).
|
|
1899
|
+
*/
|
|
1900
|
+
/** Map of all operator tool definitions keyed by command name. */
|
|
1901
|
+
interface OperatorTools {
|
|
1902
|
+
readonly listTargets: ToolDefinition;
|
|
1903
|
+
readonly pauseTarget: ToolDefinition;
|
|
1904
|
+
readonly resumeTarget: ToolDefinition;
|
|
1905
|
+
readonly drainTarget: ToolDefinition;
|
|
1906
|
+
readonly disableTarget: ToolDefinition;
|
|
1907
|
+
readonly inspectRequest: ToolDefinition;
|
|
1908
|
+
readonly reloadConfig: ToolDefinition;
|
|
1909
|
+
}
|
|
1910
|
+
declare const createOperatorTools: (deps: OperatorDeps) => OperatorTools;
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* Profile store CRUD operations.
|
|
1914
|
+
*
|
|
1915
|
+
* Pure functions for in-memory store manipulation (addProfile, removeProfile,
|
|
1916
|
+
* listProfiles, nextProfileId) plus file I/O helpers (loadProfiles, saveProfiles).
|
|
1917
|
+
*
|
|
1918
|
+
* File writes are atomic: write to .tmp then rename (D-67).
|
|
1919
|
+
* Default paths target ~/.local/share/o-switcher/profiles.json (D-62).
|
|
1920
|
+
*/
|
|
1921
|
+
|
|
1922
|
+
/**
|
|
1923
|
+
* Loads the profile store from disk.
|
|
1924
|
+
* Returns empty store if file does not exist.
|
|
1925
|
+
*/
|
|
1926
|
+
declare const loadProfiles: (path?: string) => Promise<ProfileStore>;
|
|
1927
|
+
/**
|
|
1928
|
+
* Saves the profile store to disk atomically.
|
|
1929
|
+
* Writes to a .tmp file first, then renames to the target path (D-67).
|
|
1930
|
+
* Creates parent directories if they do not exist.
|
|
1931
|
+
*
|
|
1932
|
+
* @param store - Profile store to persist.
|
|
1933
|
+
* @param path - Target file path.
|
|
1934
|
+
* @param logger - Optional pino logger for disk write logging.
|
|
1935
|
+
*/
|
|
1936
|
+
declare const saveProfiles: (store: ProfileStore, path?: string, logger?: pino.Logger) => Promise<void>;
|
|
1937
|
+
/**
|
|
1938
|
+
* Adds a profile to the store. Pure function — returns a new store.
|
|
1939
|
+
*
|
|
1940
|
+
* Assigns a sequential ID via nextProfileId (e.g., openai-1, openai-2).
|
|
1941
|
+
* Skips if credentials match an existing entry for the same provider (dedup).
|
|
1942
|
+
*/
|
|
1943
|
+
declare const addProfile: (store: ProfileStore, provider: string, credentials: AuthCredential) => ProfileStore;
|
|
1944
|
+
/**
|
|
1945
|
+
* Removes a profile by ID. Pure function — returns new store and removal status.
|
|
1946
|
+
*/
|
|
1947
|
+
declare const removeProfile: (store: ProfileStore, id: string) => {
|
|
1948
|
+
store: ProfileStore;
|
|
1949
|
+
removed: boolean;
|
|
1950
|
+
};
|
|
1951
|
+
/**
|
|
1952
|
+
* Lists all profiles sorted by created date (oldest first).
|
|
1953
|
+
*/
|
|
1954
|
+
declare const listProfiles: (store: ProfileStore) => ProfileEntry[];
|
|
1955
|
+
/**
|
|
1956
|
+
* Computes the next sequential profile ID for a provider.
|
|
1957
|
+
*
|
|
1958
|
+
* Finds the maximum N in existing "provider-N" entries and returns "provider-(N+1)".
|
|
1959
|
+
* Returns "provider-1" when no profiles exist for the provider.
|
|
1960
|
+
*/
|
|
1961
|
+
declare const nextProfileId: (store: ProfileStore, provider: string) => string;
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
* Auth.json file watcher with debounce and credential comparison.
|
|
1965
|
+
*
|
|
1966
|
+
* Watches OpenCode's auth.json for changes, saves the PREVIOUS credential
|
|
1967
|
+
* to O-Switcher's profile store before accepting the new one (D-65, D-66).
|
|
1968
|
+
*
|
|
1969
|
+
* On first run with empty profiles, initializes from current auth.json (D-72).
|
|
1970
|
+
* Debounce at 100ms collapses rapid fs.watch events (D-67).
|
|
1971
|
+
*/
|
|
1972
|
+
|
|
1973
|
+
/** Watcher interface with start/stop lifecycle. */
|
|
1974
|
+
interface AuthWatcher {
|
|
1975
|
+
start(): Promise<void>;
|
|
1976
|
+
stop(): void;
|
|
1977
|
+
}
|
|
1978
|
+
/** Options for creating an auth watcher. */
|
|
1979
|
+
interface AuthWatcherOptions {
|
|
1980
|
+
readonly authJsonPath?: string;
|
|
1981
|
+
readonly profilesPath?: string;
|
|
1982
|
+
readonly logger?: pino.Logger;
|
|
1983
|
+
}
|
|
1984
|
+
/**
|
|
1985
|
+
* Creates a file watcher for OpenCode's auth.json.
|
|
1986
|
+
*
|
|
1987
|
+
* Watches for credential changes and saves previous credentials to the
|
|
1988
|
+
* O-Switcher profile store before they are overwritten.
|
|
1989
|
+
*/
|
|
1990
|
+
declare const createAuthWatcher: (options?: AuthWatcherOptions) => AuthWatcher;
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* OpenCode plugin tool definitions for profile management.
|
|
1994
|
+
*
|
|
1995
|
+
* Per D-68, D-69, D-70: profiles-list and profiles-remove tools.
|
|
1996
|
+
* Follows the exact pattern from src/operator/plugin-tools.ts.
|
|
1997
|
+
*
|
|
1998
|
+
* Tools are stateless — they call loadProfiles/saveProfiles on each invocation,
|
|
1999
|
+
* so they can be created eagerly before plugin state is initialized.
|
|
2000
|
+
*/
|
|
2001
|
+
|
|
2002
|
+
/** Map of profile tool definitions keyed by command name. */
|
|
2003
|
+
interface ProfileTools {
|
|
2004
|
+
readonly profilesList: ToolDefinition;
|
|
2005
|
+
readonly profilesRemove: ToolDefinition;
|
|
2006
|
+
}
|
|
2007
|
+
/** Options for creating profile tools. */
|
|
2008
|
+
interface ProfileToolsOptions {
|
|
2009
|
+
readonly profilesPath?: string;
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Creates profile tool definitions.
|
|
2013
|
+
*
|
|
2014
|
+
* Each tool operates on the profiles.json file directly (load/save per call).
|
|
2015
|
+
* No shared state required — tools are safe to create eagerly.
|
|
2016
|
+
*
|
|
2017
|
+
* @param options - Optional path override for profiles.json (useful for testing).
|
|
2018
|
+
*/
|
|
2019
|
+
declare const createProfileTools: (options?: ProfileToolsOptions) => ProfileTools;
|
|
2020
|
+
|
|
2021
|
+
export { ADMISSION_RESULTS, type AdapterRequest, type AdapterResult, type AdmissionConfig, type AdmissionContext, type AdmissionController, type AdmissionDecision, type AdmissionResult, type ApiKeyCredential, type AttemptFn, type AuditCollector, type AuditEvent, type AuditLoggerOptions, type AuthCredential, type AuthResult, type AuthWatcher, type AuthWatcherOptions, type BackoffConfig, BackoffConfigSchema, type BackoffParams, type CircuitBreaker, type CircuitBreakerConfig, type ClassificationResult, type ConcurrencyTrackerApi, type ConfigDiagnostic, type ConfigDiff, type ConfigRef, ConfigValidationError, type ContinuationMode, type CooldownManager, type CorrelationId, DEFAULT_ALPHA, DEFAULT_BACKOFF_BASE_MS, DEFAULT_BACKOFF_JITTER, DEFAULT_BACKOFF_MAX_MS, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_BACKOFF_PARAMS, DEFAULT_FAILOVER_BUDGET, DEFAULT_RETRY, DEFAULT_RETRY_BUDGET, DEFAULT_TIMEOUT_MS, type DeploymentMode, DualBreaker, EXCLUSION_REASONS, type ErrorClass, ErrorClassSchema, type ExclusionReason, type ExecutionDeps, type ExecutionOrchestrator, type ExecutionProvenance, type ExecutionResult, type FailoverAttempt, type FailoverDeps, type FailoverOrchestrator, type FailoverRegistry, type FailoverResult, HEURISTIC_PATTERNS, INITIAL_HEALTH_SCORE, type InspectResult, type ListTargetsResult, type ModeAdapter, type ModeCapabilities, type NormalizedSignal, type OAuthCredential, type OperatorDeps, type OperatorTools, PROVIDER_PATTERNS, type ProfileEntry, type ProfileStore, type ProfileTools, type ProfileToolsOptions, type ProviderConfigLike, REDACT_PATHS, type RegistrySnapshot, type ReloadResult, type RequestTraceBuffer, type RequestTraceEntry, type RetryDecision, type RetryPolicy, type RoutingEventMap, type RoutingWeights, type SegmentProvenance, type SelectionRecord, type SignalFidelity, type StitchedOutput, type StreamBuffer, type StreamChunk, type StreamStitcher, type SwitcherConfig, SwitcherConfigSchema, TARGET_STATES, TEMPORAL_QUOTA_PATTERN, type TargetActionResult, type TargetConfig, TargetConfigSchema, type TargetEntry, TargetRegistry, type TargetState, type TargetView, addProfile, applyConfigDiff, checkHardRejects, classify, computeBackoffMs, computeConfigDiff, computeCooldownMs, computeScore, createAdmissionController, createAuditCollector, createAuditLogger, createAuthWatcher, createCircuitBreaker, createConcurrencyTracker, createCooldownManager, createExecutionOrchestrator, createFailoverOrchestrator, createLogSubscriber, createModeAdapter, createOperatorTools, createProfileTools, createRegistry, createRequestLogger, createRequestTraceBuffer, createRetryPolicy, createRoutingEventBus, createStreamBuffer, createStreamStitcher, detectDeploymentMode, determineContinuationMode, directSignalFromResponse, disableTarget, discoverTargets, discoverTargetsFromProfiles, drainTarget, extractRetryAfterMs, generateCorrelationId, getExclusionReason, getModeCapabilities, getSignalFidelity, getTargetStateTransition, heuristicSignalFromEvent, inspectRequest, isRetryable, listProfiles, listTargets, loadProfiles, nextProfileId, normalizeLatency, pauseTarget, reloadConfig, removeProfile, resumeTarget, saveProfiles, selectTarget, updateHealthScore, updateLatencyEma, validateBearerToken, validateConfig };
|