@jsonstudio/llms 0.6.1449 → 0.6.1643

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.js +6 -1
  2. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +4 -6
  3. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +179 -41
  4. package/dist/conversion/compat/actions/antigravity-thought-signature-cache.js +73 -14
  5. package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +165 -10
  6. package/dist/conversion/compat/actions/gemini-cli-request.js +72 -13
  7. package/dist/conversion/compat/antigravity-session-signature.d.ts +68 -1
  8. package/dist/conversion/compat/antigravity-session-signature.js +833 -21
  9. package/dist/conversion/compat/profiles/anthropic-claude-code.json +17 -0
  10. package/dist/conversion/compat/profiles/chat-gemini-cli.json +1 -0
  11. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +33 -8
  12. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +17 -1
  13. package/dist/conversion/hub/pipeline/compat/compat-profile-store.js +12 -3
  14. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
  15. package/dist/conversion/hub/pipeline/hub-pipeline.js +24 -0
  16. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +20 -0
  17. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +26 -1
  18. package/dist/conversion/hub/process/chat-process.js +300 -67
  19. package/dist/conversion/hub/response/provider-response.js +4 -3
  20. package/dist/conversion/shared/gemini-tool-utils.js +134 -9
  21. package/dist/conversion/shared/text-markup-normalizer.js +90 -1
  22. package/dist/conversion/shared/thought-signature-validator.d.ts +1 -1
  23. package/dist/conversion/shared/thought-signature-validator.js +2 -1
  24. package/dist/quota/apikey-reset.d.ts +17 -0
  25. package/dist/quota/apikey-reset.js +43 -0
  26. package/dist/quota/index.d.ts +2 -0
  27. package/dist/quota/index.js +1 -0
  28. package/dist/quota/quota-manager.d.ts +44 -0
  29. package/dist/quota/quota-manager.js +491 -0
  30. package/dist/quota/quota-state.d.ts +6 -0
  31. package/dist/quota/quota-state.js +167 -0
  32. package/dist/quota/types.d.ts +61 -0
  33. package/dist/quota/types.js +1 -0
  34. package/dist/router/virtual-router/bootstrap.js +103 -6
  35. package/dist/router/virtual-router/engine-health.js +104 -0
  36. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +18 -0
  37. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +1 -2
  38. package/dist/router/virtual-router/engine-selection/tier-priority.js +2 -2
  39. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +34 -10
  40. package/dist/router/virtual-router/engine-selection/tier-selection.js +250 -6
  41. package/dist/router/virtual-router/engine-selection.js +2 -2
  42. package/dist/router/virtual-router/engine.d.ts +16 -1
  43. package/dist/router/virtual-router/engine.js +320 -42
  44. package/dist/router/virtual-router/features.js +20 -2
  45. package/dist/router/virtual-router/success-center.d.ts +10 -0
  46. package/dist/router/virtual-router/success-center.js +32 -0
  47. package/dist/router/virtual-router/types.d.ts +48 -0
  48. package/dist/servertool/clock/config.d.ts +2 -0
  49. package/dist/servertool/clock/config.js +10 -2
  50. package/dist/servertool/clock/daemon.js +3 -0
  51. package/dist/servertool/clock/ntp.d.ts +18 -0
  52. package/dist/servertool/clock/ntp.js +318 -0
  53. package/dist/servertool/clock/paths.d.ts +1 -0
  54. package/dist/servertool/clock/paths.js +3 -0
  55. package/dist/servertool/clock/state.d.ts +2 -0
  56. package/dist/servertool/clock/state.js +15 -2
  57. package/dist/servertool/clock/tasks.d.ts +1 -0
  58. package/dist/servertool/clock/tasks.js +24 -1
  59. package/dist/servertool/clock/types.d.ts +21 -0
  60. package/dist/servertool/engine.js +105 -1
  61. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.d.ts +1 -0
  62. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +201 -0
  63. package/dist/servertool/handlers/clock-auto.js +39 -4
  64. package/dist/servertool/handlers/clock.js +145 -16
  65. package/dist/servertool/handlers/followup-request-builder.js +84 -0
  66. package/dist/servertool/handlers/stop-message-auto.js +1 -1
  67. package/dist/servertool/server-side-tools.d.ts +1 -0
  68. package/dist/servertool/server-side-tools.js +1 -0
  69. package/dist/servertool/types.d.ts +2 -0
  70. package/dist/tools/apply-patch/execution-capturer.js +24 -3
  71. package/package.json +3 -2
@@ -2,6 +2,26 @@ import { jsonClone } from '../hub/types/json.js';
2
2
  function isPlainRecord(value) {
3
3
  return !!value && typeof value === 'object' && !Array.isArray(value);
4
4
  }
5
+ function normalizeSchemaMapEntries(value) {
6
+ if (!Array.isArray(value)) {
7
+ return undefined;
8
+ }
9
+ const out = {};
10
+ for (const entry of value) {
11
+ if (!isPlainRecord(entry)) {
12
+ return undefined;
13
+ }
14
+ const key = typeof entry.key === 'string' ? entry.key.trim() : '';
15
+ if (!key) {
16
+ return undefined;
17
+ }
18
+ if (!Object.prototype.hasOwnProperty.call(entry, 'value')) {
19
+ return undefined;
20
+ }
21
+ out[key] = entry.value;
22
+ }
23
+ return out;
24
+ }
5
25
  function pickBestSchemaVariant(variants) {
6
26
  if (!Array.isArray(variants) || variants.length === 0) {
7
27
  return { type: 'object', properties: {} };
@@ -45,7 +65,63 @@ function normalizeSchemaTypes(value) {
45
65
  const out = {};
46
66
  for (const [key, val] of Object.entries(record)) {
47
67
  if (key === 'type' && typeof val === 'string') {
48
- out[key] = val.toUpperCase();
68
+ // Antigravity-Manager alignment: keep JSON-Schema-like lowercase types.
69
+ out[key] = val.trim().toLowerCase();
70
+ continue;
71
+ }
72
+ if (key === 'type' && Array.isArray(val)) {
73
+ // Antigravity-Manager alignment: Gemini protobuf Schema.type is not repeating.
74
+ // JSON Schema may use union types like ["string","null"], which triggers:
75
+ // "Proto field is not repeating, cannot start list."
76
+ const candidates = val
77
+ .filter((entry) => typeof entry === 'string')
78
+ .map((entry) => String(entry).trim().toLowerCase())
79
+ .filter((entry) => entry && entry !== 'null');
80
+ const preferred = candidates.includes('string')
81
+ ? 'string'
82
+ : candidates.includes('object')
83
+ ? 'object'
84
+ : candidates.includes('array')
85
+ ? 'array'
86
+ : candidates.includes('integer')
87
+ ? 'integer'
88
+ : candidates.includes('number')
89
+ ? 'number'
90
+ : candidates.includes('boolean')
91
+ ? 'boolean'
92
+ : candidates[0];
93
+ if (preferred) {
94
+ out[key] = preferred;
95
+ }
96
+ continue;
97
+ }
98
+ if (key === 'properties') {
99
+ const normalizedMap = normalizeSchemaMapEntries(val);
100
+ if (normalizedMap) {
101
+ out[key] = normalizeSchemaTypes(normalizedMap);
102
+ continue;
103
+ }
104
+ if (val && typeof val === 'object') {
105
+ out[key] = normalizeSchemaTypes(val);
106
+ continue;
107
+ }
108
+ }
109
+ if (key === 'enum' && Array.isArray(val)) {
110
+ // Antigravity-Manager alignment: Gemini rejects non-string enum values in tool schemas.
111
+ out[key] = val.map((entry) => {
112
+ if (typeof entry === 'string')
113
+ return entry;
114
+ if (entry === null)
115
+ return 'null';
116
+ if (typeof entry === 'number' || typeof entry === 'boolean')
117
+ return String(entry);
118
+ try {
119
+ return JSON.stringify(entry);
120
+ }
121
+ catch {
122
+ return String(entry);
123
+ }
124
+ });
49
125
  continue;
50
126
  }
51
127
  if ((key === 'properties' || key === 'items') && val && typeof val === 'object') {
@@ -105,6 +181,40 @@ function cloneParameters(value) {
105
181
  }
106
182
  const cloned = {};
107
183
  for (const [key, entry] of Object.entries(value)) {
184
+ // Antigravity-Manager alignment: some tool pipelines serialize schema maps as [{ key, value }, ...].
185
+ // Gemini expects `properties` to be an object/map, not a list.
186
+ if (key === 'properties') {
187
+ const normalizedMap = normalizeSchemaMapEntries(entry);
188
+ if (normalizedMap) {
189
+ cloned.properties = cloneParameters(normalizedMap);
190
+ continue;
191
+ }
192
+ }
193
+ // Antigravity-Manager alignment: Gemini protobuf Schema.type is not repeating.
194
+ // Drop union array types like ["string","null"] to a single stable type.
195
+ if (key === 'type' && Array.isArray(entry)) {
196
+ const candidates = entry
197
+ .filter((v) => typeof v === 'string')
198
+ .map((v) => String(v).trim().toLowerCase())
199
+ .filter((v) => v && v !== 'null');
200
+ if (candidates.length) {
201
+ // Prefer STRING to maximize compatibility.
202
+ cloned.type = candidates.includes('string')
203
+ ? 'string'
204
+ : candidates.includes('object')
205
+ ? 'object'
206
+ : candidates.includes('array')
207
+ ? 'array'
208
+ : candidates.includes('integer')
209
+ ? 'integer'
210
+ : candidates.includes('number')
211
+ ? 'number'
212
+ : candidates.includes('boolean')
213
+ ? 'boolean'
214
+ : candidates[0];
215
+ }
216
+ continue;
217
+ }
108
218
  // Gemini function_declarations.parameters only support a subset of JSON Schema.
109
219
  // Drop meta/unsupported fields that cause INVALID_ARGUMENT, such as $schema/exclusiveMinimum/propertyNames.
110
220
  if (typeof key === 'string') {
@@ -132,12 +242,27 @@ function cloneParameters(value) {
132
242
  // Preserve `required` for Gemini validation (align with gcli2api), but still drop additionalProperties.
133
243
  if (key === 'additionalProperties')
134
244
  continue;
245
+ if (key === 'external_web_access')
246
+ continue;
135
247
  // Combinators are handled at the node level above.
136
248
  if (key === 'oneOf' || key === 'anyOf' || key === 'allOf')
137
249
  continue;
138
250
  }
139
251
  cloned[key] = cloneParameters(entry);
140
252
  }
253
+ // Antigravity-Manager alignment: required must only reference keys present in properties.
254
+ const propNode = cloned.properties;
255
+ const reqNode = cloned.required;
256
+ if (isPlainRecord(propNode) && Array.isArray(reqNode)) {
257
+ const valid = new Set(Object.keys(propNode));
258
+ const filtered = reqNode.filter((v) => typeof v === 'string' && valid.has(v));
259
+ if (filtered.length) {
260
+ cloned.required = filtered;
261
+ }
262
+ else {
263
+ delete cloned.required;
264
+ }
265
+ }
141
266
  return cloned;
142
267
  }
143
268
  return { type: 'object', properties: {} };
@@ -255,11 +380,11 @@ export function buildGeminiToolsFromBridge(defs, options) {
255
380
  // ignore
256
381
  }
257
382
  props.command = {
258
- type: 'STRING',
383
+ type: 'string',
259
384
  description: 'Shell command to execute.'
260
385
  };
261
386
  // Keep workdir simple (avoid anyOf string|null patterns).
262
- props.workdir = { type: 'STRING', description: 'Working directory.' };
387
+ props.workdir = { type: 'string', description: 'Working directory.' };
263
388
  }
264
389
  else {
265
390
  if (!Object.prototype.hasOwnProperty.call(props, 'cmd') && Object.prototype.hasOwnProperty.call(props, 'command')) {
@@ -298,10 +423,10 @@ export function buildGeminiToolsFromBridge(defs, options) {
298
423
  props.text = props.chars;
299
424
  }
300
425
  if (!Object.prototype.hasOwnProperty.call(props, 'session_id')) {
301
- props.session_id = { type: isAntigravity ? 'NUMBER' : 'number' };
426
+ props.session_id = { type: 'number' };
302
427
  }
303
428
  if (!Object.prototype.hasOwnProperty.call(props, 'chars')) {
304
- props.chars = { type: isAntigravity ? 'STRING' : 'string' };
429
+ props.chars = { type: 'string' };
305
430
  }
306
431
  params.properties = props;
307
432
  try {
@@ -319,25 +444,25 @@ export function buildGeminiToolsFromBridge(defs, options) {
319
444
  // - input/instructions/text: historical aliases containing patch text
320
445
  if (!Object.prototype.hasOwnProperty.call(props, 'patch')) {
321
446
  props.patch = {
322
- type: isAntigravity ? 'STRING' : 'string',
447
+ type: 'string',
323
448
  description: 'Patch text (*** Begin Patch / *** End Patch or GNU unified diff).'
324
449
  };
325
450
  }
326
451
  if (!Object.prototype.hasOwnProperty.call(props, 'input')) {
327
452
  props.input = {
328
- type: isAntigravity ? 'STRING' : 'string',
453
+ type: 'string',
329
454
  description: 'Alias of patch (patch text). Prefer patch.'
330
455
  };
331
456
  }
332
457
  if (!Object.prototype.hasOwnProperty.call(props, 'instructions')) {
333
458
  props.instructions = {
334
- type: isAntigravity ? 'STRING' : 'string',
459
+ type: 'string',
335
460
  description: 'Alias of patch (patch text). Prefer patch.'
336
461
  };
337
462
  }
338
463
  if (!Object.prototype.hasOwnProperty.call(props, 'text')) {
339
464
  props.text = {
340
- type: isAntigravity ? 'STRING' : 'string',
465
+ type: 'string',
341
466
  description: 'Alias of patch (patch text). Prefer patch.'
342
467
  };
343
468
  }
@@ -129,8 +129,12 @@ export function extractToolNamespaceXmlBlocksFromText(text) {
129
129
  try {
130
130
  if (typeof text !== 'string' || !text)
131
131
  return null;
132
- if (!text.includes('<tool:'))
132
+ const hasOpenTag = text.includes('<tool:');
133
+ const hasCloseTag = /<\/\s*tool:\s*[A-Za-z0-9_]+\s*>/i.test(text);
134
+ const hasToolPrefix = /(?:^|\n)\s*(?:[•*+-]\s*)?tool:[A-Za-z0-9_]+/i.test(text);
135
+ if (!hasOpenTag && !(hasCloseTag && hasToolPrefix)) {
133
136
  return null;
137
+ }
134
138
  const out = [];
135
139
  const blockRe = /<\s*tool:([A-Za-z0-9_]+)\s*>([\s\S]*?)<\/\s*tool:\s*\1\s*>/gi;
136
140
  let bm;
@@ -211,6 +215,91 @@ export function extractToolNamespaceXmlBlocksFromText(text) {
211
215
  }
212
216
  out.push({ id: `call_${Math.random().toString(36).slice(2, 10)}`, name: lname, args: argsStr });
213
217
  }
218
+ // Broken variant (seen in the wild):
219
+ // tool:exec_command (tool:exec_command)
220
+ // <command>...</command>
221
+ // </tool:exec_command>
222
+ //
223
+ // Best-effort: treat it as an implicit <tool:NAME> open tag.
224
+ const prefixBlockRe = /(?:^|\n)\s*(?:[•*+-]\s*)?tool:([A-Za-z0-9_]+)[^\n]*\n([\s\S]*?)<\/\s*tool:\s*\1\s*>/gi;
225
+ let pm;
226
+ while ((pm = prefixBlockRe.exec(text)) !== null) {
227
+ const rawName = String(pm[1] || '').trim();
228
+ const lname = rawName.toLowerCase();
229
+ if (!lname || !KNOWN_TOOLS.has(lname))
230
+ continue;
231
+ const inner = String(pm[2] || '');
232
+ const argsObj = {};
233
+ const kvRe = /<\s*([A-Za-z_][A-Za-z0-9_]*)\s*>([\s\S]*?)<\/\s*\1\s*>/g;
234
+ let km;
235
+ while ((km = kvRe.exec(inner)) !== null) {
236
+ const rawKey = String(km[1] || '').trim();
237
+ const key = normalizeKey(rawKey).toLowerCase();
238
+ if (!key)
239
+ continue;
240
+ if (key === 'requires_approval')
241
+ continue;
242
+ const rawVal = String(km[2] ?? '');
243
+ const value = tryParsePrimitiveValue(rawVal);
244
+ if (lname === 'exec_command' && (key === 'command' || key === 'cmd')) {
245
+ const cmd = coerceCommandValueToString(value);
246
+ if (cmd.trim().length) {
247
+ argsObj.cmd = cmd;
248
+ argsObj.command = cmd;
249
+ }
250
+ continue;
251
+ }
252
+ if (lname === 'exec_command' && (key === 'cwd' || key === 'workdir')) {
253
+ const wd = String(value ?? rawVal).trim();
254
+ if (wd)
255
+ argsObj.workdir = wd;
256
+ continue;
257
+ }
258
+ if (lname === 'write_stdin') {
259
+ if (key === 'input' || key === 'data' || key === 'chars' || key === 'text') {
260
+ argsObj.chars = rawVal;
261
+ continue;
262
+ }
263
+ if (key === 'session_id') {
264
+ const n = Number.parseInt(String(value), 10);
265
+ argsObj.session_id = Number.isFinite(n) ? n : value;
266
+ continue;
267
+ }
268
+ }
269
+ if (lname === 'apply_patch' && (key === 'patch' || key === 'input' || key === 'text' || key === 'instructions')) {
270
+ const patchText = typeof value === 'string' ? value : rawVal;
271
+ if (typeof patchText === 'string' && patchText.trim().length) {
272
+ argsObj.patch = patchText;
273
+ }
274
+ continue;
275
+ }
276
+ argsObj[key] = value;
277
+ }
278
+ const filtered = filterArgsForTool(lname, argsObj);
279
+ if (lname === 'exec_command') {
280
+ const cmd = typeof filtered?.cmd === 'string' ? String(filtered.cmd).trim() : '';
281
+ if (!cmd)
282
+ continue;
283
+ }
284
+ if (lname === 'write_stdin') {
285
+ const sid = filtered?.session_id;
286
+ if (sid === undefined || sid === null || String(sid).trim().length === 0)
287
+ continue;
288
+ }
289
+ if (lname === 'apply_patch') {
290
+ const patchText = typeof filtered?.patch === 'string' ? String(filtered.patch).trim() : '';
291
+ if (!patchText)
292
+ continue;
293
+ }
294
+ let argsStr = '{}';
295
+ try {
296
+ argsStr = JSON.stringify(filtered);
297
+ }
298
+ catch {
299
+ argsStr = '{}';
300
+ }
301
+ out.push({ id: `call_${Math.random().toString(36).slice(2, 10)}`, name: lname, args: argsStr });
302
+ }
214
303
  return out.length ? out : null;
215
304
  }
216
305
  catch {
@@ -7,7 +7,7 @@
7
7
  import type { JsonObject, JsonValue } from '../hub/types/json.js';
8
8
  export interface ThoughtSignatureValidationOptions {
9
9
  /**
10
- * 最小签名长度(默认 10 个字符)
10
+ * 最小签名长度(默认 50 个字符)
11
11
  */
12
12
  minLength?: number;
13
13
  /**
@@ -5,7 +5,8 @@
5
5
  * 参考 gcli2api 的实现,确保与上游 API 的兼容性。
6
6
  */
7
7
  const DEFAULT_OPTIONS = {
8
- minLength: 10,
8
+ // Antigravity-Manager alignment: treat short signatures as noise.
9
+ minLength: 50,
9
10
  allowEmptyThinkingWithSignature: true,
10
11
  convertToTextOnFailure: true
11
12
  };
@@ -0,0 +1,17 @@
1
+ export type DailyResetParseResult = {
2
+ ok: true;
3
+ kind: 'local' | 'utc';
4
+ hour: number;
5
+ minute: number;
6
+ } | {
7
+ ok: false;
8
+ error: string;
9
+ };
10
+ export declare function parseDailyResetTime(input: string | null | undefined): DailyResetParseResult;
11
+ export declare function computeNextDailyResetAtMs(args: {
12
+ nowMs: number;
13
+ resetTime?: string | null;
14
+ }): {
15
+ resetAtMs: number;
16
+ resetTimeNormalized: string;
17
+ };
@@ -0,0 +1,43 @@
1
+ function pad2(n) {
2
+ return String(n).padStart(2, '0');
3
+ }
4
+ export function parseDailyResetTime(input) {
5
+ const raw = typeof input === 'string' ? input.trim() : '';
6
+ const value = raw || '12:00';
7
+ const isUtc = value.toUpperCase().endsWith('Z');
8
+ const base = isUtc ? value.slice(0, -1) : value;
9
+ const m = base.match(/^(\d{1,2}):(\d{2})$/);
10
+ if (!m) {
11
+ return { ok: false, error: `invalid reset time format: ${value}` };
12
+ }
13
+ const hour = Number(m[1]);
14
+ const minute = Number(m[2]);
15
+ if (!Number.isFinite(hour) || !Number.isFinite(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
16
+ return { ok: false, error: `invalid reset time value: ${value}` };
17
+ }
18
+ return { ok: true, kind: isUtc ? 'utc' : 'local', hour, minute };
19
+ }
20
+ export function computeNextDailyResetAtMs(args) {
21
+ const parsed = parseDailyResetTime(args.resetTime);
22
+ const fallback = { ok: true, kind: 'local', hour: 12, minute: 0 };
23
+ const resolved = parsed.ok ? parsed : fallback;
24
+ const kind = resolved.kind;
25
+ const hour = resolved.hour;
26
+ const minute = resolved.minute;
27
+ const resetTimeNormalized = `${pad2(hour)}:${pad2(minute)}${kind === 'utc' ? 'Z' : ''}`;
28
+ const now = new Date(args.nowMs);
29
+ let candidate;
30
+ if (kind === 'utc') {
31
+ candidate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), hour, minute, 0, 0));
32
+ if (candidate.getTime() <= args.nowMs) {
33
+ candidate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, hour, minute, 0, 0));
34
+ }
35
+ }
36
+ else {
37
+ candidate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute, 0, 0);
38
+ if (candidate.getTime() <= args.nowMs) {
39
+ candidate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, hour, minute, 0, 0);
40
+ }
41
+ }
42
+ return { resetAtMs: candidate.getTime(), resetTimeNormalized };
43
+ }
@@ -0,0 +1,2 @@
1
+ export type { QuotaReason, QuotaAuthType, QuotaAuthIssue, StaticQuotaConfig, QuotaState, QuotaStore, QuotaStoreSnapshot } from './types.js';
2
+ export { QuotaManager } from './quota-manager.js';
@@ -0,0 +1 @@
1
+ export { QuotaManager } from './quota-manager.js';
@@ -0,0 +1,44 @@
1
+ import type { ProviderErrorEvent, ProviderSuccessEvent, ProviderQuotaView } from '../router/virtual-router/types.js';
2
+ import type { QuotaState, QuotaStore, StaticQuotaConfig } from './types.js';
3
+ export declare class QuotaManager {
4
+ private readonly staticConfigs;
5
+ private readonly states;
6
+ private readonly store;
7
+ private persistTimer;
8
+ private dirty;
9
+ constructor(options?: {
10
+ store?: QuotaStore | null;
11
+ });
12
+ hydrateFromStore(): Promise<void>;
13
+ registerProviderStaticConfig(providerKey: string, cfg: StaticQuotaConfig): void;
14
+ ensureProvider(providerKey: string): QuotaState;
15
+ disableProvider(options: {
16
+ providerKey: string;
17
+ mode: 'cooldown' | 'blacklist';
18
+ durationMs: number;
19
+ reason?: string;
20
+ }): void;
21
+ recoverProvider(providerKey: string): void;
22
+ resetProvider(providerKey: string): void;
23
+ onProviderError(ev: ProviderErrorEvent): void;
24
+ onProviderSuccess(ev: ProviderSuccessEvent): void;
25
+ /**
26
+ * External quota snapshot ingestion hook (host-driven).
27
+ * This API is intentionally small: the host adapter translates provider-specific
28
+ * quota responses into a normalized per-providerKey inPool/cooldown/blacklist update.
29
+ */
30
+ updateProviderPoolState(options: {
31
+ providerKey: string;
32
+ inPool: boolean;
33
+ reason?: string | null;
34
+ cooldownUntil?: number | null;
35
+ blacklistUntil?: number | null;
36
+ }): void;
37
+ getQuotaView(): ProviderQuotaView;
38
+ getSnapshot(): {
39
+ updatedAtMs: number;
40
+ providers: Record<string, QuotaState>;
41
+ };
42
+ private markDirty;
43
+ persistNow(): Promise<void>;
44
+ }