@oh-my-pi/pi-ai 18.3.5 → 18.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +29 -48
  2. package/dist/types/auth/blocks.d.ts +10 -19
  3. package/dist/types/auth/cascade.d.ts +1 -1
  4. package/dist/types/auth/rotation.d.ts +6 -3
  5. package/dist/types/auth/types.d.ts +23 -3
  6. package/dist/types/auth/usage-cache.d.ts +14 -2
  7. package/dist/types/auth/usage.d.ts +2 -2
  8. package/dist/types/auth-broker/client.d.ts +2 -1
  9. package/dist/types/auth-broker/remote-store.d.ts +5 -6
  10. package/dist/types/auth-broker/types.d.ts +3 -1
  11. package/dist/types/auth-broker/wire-schemas.d.ts +2 -1
  12. package/dist/types/auth-retry.d.ts +22 -13
  13. package/dist/types/images/types.d.ts +9 -0
  14. package/dist/types/judgment/types.d.ts +3 -2
  15. package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
  16. package/dist/types/providers/cursor-pi-args.d.ts +10 -0
  17. package/dist/types/providers/cursor.d.ts +6 -2
  18. package/dist/types/providers/google-shared.d.ts +13 -2
  19. package/dist/types/usage/claude-reset.d.ts +6 -0
  20. package/dist/types/usage/claude.d.ts +6 -0
  21. package/dist/types/usage.d.ts +2 -0
  22. package/dist/types/utils/proxy.d.ts +2 -2
  23. package/package.json +6 -6
  24. package/src/auth/blocks.ts +79 -37
  25. package/src/auth/cascade.ts +5 -4
  26. package/src/auth/resets.ts +104 -40
  27. package/src/auth/rotation.ts +62 -21
  28. package/src/auth/select.ts +9 -2
  29. package/src/auth/types.ts +28 -3
  30. package/src/auth/usage-cache.ts +45 -2
  31. package/src/auth/usage.ts +52 -25
  32. package/src/auth-broker/client.ts +13 -0
  33. package/src/auth-broker/remote-store.ts +128 -27
  34. package/src/auth-broker/server.ts +20 -0
  35. package/src/auth-broker/types.ts +4 -1
  36. package/src/auth-broker/wire-schemas.ts +7 -0
  37. package/src/auth-retry.ts +50 -28
  38. package/src/images/openai-hosted.ts +18 -3
  39. package/src/images/types.ts +9 -0
  40. package/src/judgment/types.ts +10 -6
  41. package/src/judgment/typesafe.ts +2 -2
  42. package/src/providers/anthropic.ts +53 -13
  43. package/src/providers/cursor/exec-modern.ts +1 -0
  44. package/src/providers/cursor/proto/agent.proto +5 -0
  45. package/src/providers/cursor-pi-args.ts +14 -0
  46. package/src/providers/cursor.ts +66 -28
  47. package/src/providers/google-gemini-cli.ts +2 -19
  48. package/src/providers/google-shared.ts +30 -22
  49. package/src/providers/ollama.ts +2 -0
  50. package/src/registry/oauth/oauth.html +206 -185
  51. package/src/usage/claude-reset.ts +45 -11
  52. package/src/usage/claude.ts +79 -49
  53. package/src/usage.ts +2 -0
  54. package/src/utils/proxy.ts +3 -3
  55. package/src/utils/schema/CONSTRAINTS.md +30 -28
  56. package/src/utils/schema/normalize.ts +1 -1
  57. package/src/utils/schema/wire.ts +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.4.0] - 2026-09-28
6
+
7
+ ### Breaking Changes
8
+
9
+ - Changed `LimitsApi.rotate()` to return a `CredentialRotation` object (`{ switched, afterSiblingWait? }`) instead of a boolean. Check `.switched` explicitly, since the returned object is always truthy.
10
+
11
+ ### Added
12
+
13
+ - Added image metadata to hosted OpenAI image-generation results, including the model used and each generated image’s dimensions and quality.
14
+
15
+ ### Changed
16
+
17
+ - Redesigned the browser page displayed during OAuth login.
18
+
19
+ ### Fixed
20
+
21
+ - Fixed Anthropic accounts remaining blocked after quota resets, including for broker-connected clients, while preserving independent authentication and model-limit restrictions.
22
+ - Fixed concurrent usage refreshes repeatedly probing providers after failures, reducing unnecessary usage-endpoint requests.
23
+ - Fixed Gemini and Antigravity usage and cost reporting when upstream responses omit prompt-token counts or report more cached tokens than prompt tokens.
24
+ - Fixed credential failover after authentication errors so stored credentials rotate through every distinct sibling instead of stopping after one attempt; rotation can also wait briefly for a temporarily blocked healthy sibling to become available.
25
+ - Fixed native judge responses without token counts producing invalid usage and cost values.
26
+ - Fixed Cursor usage, cost, and context accounting to include final input, cache, reasoning, and output metrics, improving compaction and handoff sizing.
27
+ - Fixed Cursor MCP tool calls routed through external executors being returned as text instead of tool calls.
28
+ - Fixed Cursor shell-tool timeouts being displayed in milliseconds rather than seconds.
29
+ - Fixed fetch requests over Unix sockets when `PI_PROXY` is configured.
30
+ - Fixed Ollama chat turns being recorded with zero cost; usage is now priced using the model’s cost information.
31
+ - Fixed Anthropic requests failing after native compaction when per-message effort settings were present; effort controls are now handled correctly with compaction.
32
+
5
33
  ## [18.3.5] - 2026-09-27
6
34
 
7
35
  ### Breaking Changes
@@ -2276,51 +2304,4 @@
2276
2304
  - Fixed explicit request-debug mode to overwrite existing `.res.log` files for the requested path instead of failing when they already exist
2277
2305
  - Fixed OpenAI Responses `previous_response_id` chaining on Zero Data Retention orgs: the in-provider retry classifier missed the ZDR-specific 400 ("Previous response cannot be used for this organization due to Zero Data Retention"), so chained turns kept failing every other request after a brief recovery — the chain was reset but not disabled, so the next successful full-replay turn re-armed it. The ZDR phrasing is now classified categorically: one strike disables chaining for the session (skipping the three-strike circuit breaker) and the in-call retry drops `store: true`/`previous_response_id` and replays the full transcript instead ([#2341](https://github.com/can1357/oh-my-pi/issues/2341)).
2278
2306
 
2279
- ## [15.11.4] - 2026-06-12
2280
-
2281
- ### Added
2282
-
2283
- - Codex/Responses providers now map `end_turn: false` on the terminal stream event (Codex backend signal for "response ended, turn didn't" — commentary-only progress updates) to `stopDetails: { type: "pause_turn" }` with stopReason `"stop"`, so the agent loop can re-sample instead of ending the turn. Wired in `openai-codex-responses` and `processResponsesStream` (`openai-responses`/`azure-openai-responses`); inert for backends that never send the field.
2284
- - Added Codex upstream protocol features to `openai-codex-responses` (tracking codex-rs as of June 2026): `onModerationMetadata` callback surfacing `response.metadata` → `openai_chatgpt_moderation_metadata` on both transports; `reasoningContext` option emitting `reasoning.context` (`auto`/`current_turn`/`all_turns`); `clientMetadata` option emitting `client_metadata` in the request body (canonical `x-codex-turn-metadata` envelope) without breaking the websocket append fast-path; and an opt-in `responsesLite` mode mirroring codex-rs — lite header on HTTP requests and the websocket upgrade, `ws_request_header_*` marker in `response.create` client metadata, lite-keyed socket pooling, image-detail stripping, forced serial tool calls, and `reasoning.context: all_turns` default. Dormant until OpenAI flips `use_responses_lite` in the model catalog.
2285
- - Added `withOAuthAccess` — the `withAuth` counterpart for OAuth-access consumers: runs an operation through the central a/b/c auth-retry policy (resolve → force-refresh same account → rotate to a sibling) while handing the attempt the full `OAuthAccess` (bearer plus `accountId`/`projectId`/`enterpriseUrl` identity metadata). Use it instead of hand-rolled `getOAuthAccess` + fetch flows so 401s and usage-limits rotate credentials instead of failing the call.
2286
- - Added `ProviderHttpError` — a typed HTTP error carrying `status`, `headers`, and `code` — replacing the ad-hoc `as Error & { status?... }` / `Object.assign` hacks at provider throw sites, with per-provider subclasses `CodexApiError`, `AuthGatewayError`, `GoogleApiError`, `GeminiCliApiError`, `OllamaApiError`, and `BedrockApiError`; `AnthropicApiError` now extends it. Google, Gemini CLI, Ollama, and Bedrock HTTP errors now also carry response headers, so server-suggested `retry-after` delays are visible to retry classification on those paths. The internal `withHttpStatus` helper was removed.
2287
- - Added stateful SSE turn chaining for OpenAI Codex (on by default; disable with `PI_CODEX_STATEFUL=0` or `statefulResponses: false`): SSE requests now reuse `previous_response_id` with delta-only input instead of replaying the full transcript, mirroring the websocket fast-path via a shared transport-aware builder. Any history mutation or option change falls back to a full replay; a server-side `previous_response_not_found` (HTTP or in-stream) resets the chain and retries the turn with full context, and three consecutive stale failures disable chaining for the session.
2288
- - Added stateful `previous_response_id` chaining to the platform OpenAI Responses provider (`openai-responses`): on by default against the official api.openai.com endpoint (forces `store: true`, which chaining requires), off for other Responses endpoints; override with `statefulResponses` or `PI_OPENAI_STATEFUL`. Chain detection compares the wire form of the conversation arguments alone — per-turn trailing scaffolding such as the GPT-5 "Juice: 0" developer item is excluded from the append-baseline prefix check and re-appended to the delta — and a rejected/stale previous response falls back to a one-shot full replay with the same circuit breaker.
2289
- - Added `AuthStorage.getOAuthAccountIdentity()` and the `OAuthAccountIdentity` type — a read-only lookup returning the `accountId`/`email`/`projectId` of the OAuth credential a session is currently routed to, for display and metadata paths.
2290
-
2291
- ### Changed
2292
-
2293
- - The GPT-5 "Juice: 0" no-reasoning developer item in `applyResponsesReasoningParams` is now gated on the resolved `compat.requiresJuiceZeroHack` flag (auto-detected from GPT-5-family model names by `@oh-my-pi/pi-catalog`, overridable per model) instead of an inline model-name check.
2294
-
2295
- ### Fixed
2296
-
2297
- - Fixed websocket append fast-path to remain usable when only `client_metadata` changes between turns
2298
- - Fixed `onModerationMetadata` handling so exceptions thrown by callback observers no longer terminate the response stream
2299
- - Fixed local SQLite OAuth credential caches returning a stale Anthropic access token after another `omp` process refreshed and persisted the same row. `AuthStorage` now syncs the selected row from storage before returning or force-refreshing OAuth credentials, so concurrent sessions pick up peer-rotated tokens instead of surfacing a one-turn `401 Invalid authentication credentials`.
2300
- - Fixed forced OAuth preflight refresh failures being swallowed silently in credential selection; they now emit a debug log (`OAuth preflight refresh failed`) so stale-refresh-token replays from concurrent sessions are diagnosable.
2301
-
2302
- ## [15.11.3] - 2026-06-11
2303
-
2304
- ### Fixed
2305
-
2306
- - Fixed GitHub Copilot long-context model requests to use the upstream `requestModelId` when calling Anthropic, OpenAI Responses, and OpenAI Completions APIs
2307
- - Fixed GitHub Copilot model enablement to deduplicate catalog variants by upstream model ID when enabling all models
2308
-
2309
- ## [15.11.2] - 2026-06-11
2310
-
2311
- ### Fixed
2312
-
2313
- - Fixed Anthropic encoding of error tool results with whitespace-only content so requests no longer 400 with `tool_result: content cannot be empty if is_error is true`
2314
-
2315
- ## [15.11.1] - 2026-06-11
2316
-
2317
- ### Changed
2318
-
2319
- - Exported `resolveAnthropicMetadataUserId` so non-streaming Anthropic Messages consumers (e.g. the coding-agent web search provider) can produce the same Claude-Code-shaped `metadata.user_id` as the main streaming path.
2320
-
2321
- ### Fixed
2322
-
2323
- - Preserved Anthropic `stop_details` on assistant messages so refusal and sensitive classifier stops remain structurally visible to callers. ([#2290](https://github.com/can1357/oh-my-pi/issues/2290))
2324
- - Fixed OpenAI Responses, Azure OpenAI Responses, and OpenAI Completions streams hanging until the 120s idle watchdog errored the turn when a provider delivers the terminal frame but never sends `[DONE]` nor closes the connection. `processResponsesStream` now breaks out of the event loop on `response.completed`/`response.incomplete` (mirroring the Codex websocket/SSE terminal break), and the completions consumer breaks once `finish_reason` plus a usage payload arrived — or, for hosts that never send usage, ends the stream cleanly via a short post-finish grace window (`iterateWithTerminalGrace`) that aborts the transport to release the socket.
2325
-
2326
- Older entries are archived in [packages/ai/CHANGELOG.md@d58593a30902](https://github.com/can1357/oh-my-pi/blob/d58593a3090258473304608d68ffd1f620e6b695/packages/ai/CHANGELOG.md).
2307
+ Older entries are archived in [packages/ai/CHANGELOG.md@dfbf3cc34eeb](https://github.com/can1357/oh-my-pi/blob/dfbf3cc34eeb5653580f51bfbcae558a9840f697/packages/ai/CHANGELOG.md).
@@ -11,6 +11,10 @@ export declare const DEFAULT_BLOCK_MS = 60000;
11
11
  export declare function providerTypeKey(provider: string, type: AuthCredential["type"]): string;
12
12
  /** Scoped backoff map key used by the credential block store. */
13
13
  export declare function scopedBackoffKey(providerKey: string, blockScope: string | undefined): string;
14
+ /** Authentication backoffs apply to every model and cannot be healed by quota evidence. */
15
+ export declare const AUTH_BLOCK_SCOPE = "auth";
16
+ /** Account-policy backoffs apply to every model and survive quota resets. */
17
+ export declare const ACCOUNT_POLICY_BLOCK_SCOPE = "account-policy";
14
18
  /** Scope key for one model-specific account-policy block, shared by selectors and rate limits. */
15
19
  export declare function modelAccountPolicyBlockScope(provider: string, modelId: string | undefined): string | undefined;
16
20
  /** Scope keys that a request must check, including model-policy constraints. */
@@ -67,26 +71,15 @@ export declare class CredentialBlocks implements BlocksApi {
67
71
  * that `markUsageLimitReached` set for the now-obsolete reset time.
68
72
  */
69
73
  clearAll(provider: string, credentialId: number): void;
70
- /**
71
- * Clear one backoff scope. The in-memory backoff is per scope so it is
72
- * dropped directly; the persisted store deletes a credential's blocks as a
73
- * unit, so it is only purged once no other scope still holds a live block.
74
- * Leaving a persisted row behind is safe: the scope it belongs to is
75
- * unblocked in memory, and the row heals on the pass where its own meter
76
- * recovers.
77
- */
78
- clearScope(provider: string, credentialId: number, providerKey: string, blockScope: string | undefined): void;
74
+ /** Clear exactly one quota scope, preserving unrelated account and model blocks. */
75
+ clearScope(provider: string, credentialId: number, providerKey: string, blockScope: string | undefined): boolean;
79
76
  /** Providers whose stale usage-limit blocks a healthy live report may clear. */
80
77
  supportsHealing(provider: Provider): boolean;
81
78
  /**
82
79
  * Whether a fresh report could lift what currently blocks this credential.
83
80
  *
84
- * A strategy that names healable scopes can only vouch for those scopes, so
85
- * a live unscoped block — an Opus/Sonnet usage limit, a refresh failure —
86
- * keeps the credential unusable whatever the report says about a tier. A
87
- * probe then cannot change the outcome and must not be spent; the tier scope
88
- * heals on a later pass, once the block that actually holds the credential
89
- * has lifted.
81
+ * Claude reports can heal legacy account-wide quota blocks too. Explicit
82
+ * auth/account-policy blocks never justify a usage probe.
90
83
  */
91
84
  canHeal(provider: Provider, providerKey: string, credentialIndex: number, blockScopeOrScopes: string | readonly string[] | undefined): boolean;
92
85
  /**
@@ -107,9 +100,7 @@ export declare class CredentialBlocks implements BlocksApi {
107
100
  * Broker-server seam: persist one credential block and notify snapshot waiters.
108
101
  */
109
102
  upsert(block: StoredCredentialBlock): void;
110
- /**
111
- * Broker-server seam: clear all persisted blocks for one credential and notify snapshot waiters.
112
- */
113
- delete(credentialId: number, providerKey: string, blockScope: string): void;
103
+ /** Broker-server seam: clear exactly one persisted and local block and notify snapshot waiters. */
104
+ delete(credentialId: number, providerKey: string, blockScope: string, invalidateUsage?: boolean): void;
114
105
  deleteAll(credentialId: number): void;
115
106
  }
@@ -1,4 +1,4 @@
1
- import type { ApiKeyResolver, ResolvedApiKey } from "../auth-retry.js";
1
+ import { type ApiKeyResolver, type ResolvedApiKey } from "../auth-retry.js";
2
2
  import type { SessionAffinity } from "./affinity.js";
3
3
  import type { CredentialPool } from "./pool.js";
4
4
  import type { CredentialSelector } from "./select.js";
@@ -5,7 +5,7 @@ import type { KeyOverrides } from "./cascade.js";
5
5
  import type { SessionAffinity } from "./affinity.js";
6
6
  import type { CredentialPool } from "./pool.js";
7
7
  import type { AuthCredentialStore } from "./store.js";
8
- import type { InvalidateCredentialMatchingOptions, LimitsApi, MarkUsageLimitOptions, RotateCredentialOptions, UsageLimitMarkResult } from "./types.js";
8
+ import type { CredentialRotation, InvalidateCredentialMatchingOptions, LimitsApi, MarkUsageLimitOptions, RotateCredentialOptions, UsageLimitMarkResult } from "./types.js";
9
9
  import type { UsageService } from "./usage.js";
10
10
  /** Routing scope and strategy for one failed credential. */
11
11
  export type CredentialBlockRouting = {
@@ -60,7 +60,10 @@ export declare class RateLimits implements LimitsApi {
60
60
  * reload when no broker hook is wired) and block it, then drop matching
61
61
  * sticky state.
62
62
  *
63
- * Returns whether another usable credential of the same type remains.
63
+ * For usage-limit and account-policy failures with no free sibling, sleeps
64
+ * until the earliest sibling unblocks when that is at most
65
+ * {@link SIBLING_UNBLOCK_WAIT_MAX_MS} away, then reports `afterSiblingWait`.
66
+ * Aborting `options.signal` during that wait rejects.
64
67
  */
65
- rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<boolean>;
68
+ rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<CredentialRotation>;
66
69
  }
@@ -564,6 +564,10 @@ export interface ResetCreditRedeemOutcome {
564
564
  /** One stored account's live saved-reset status, from {@link AuthStorage.resets.list}. */
565
565
  export interface ResetCreditAccountStatus extends UsageResetCredits {
566
566
  provider: string;
567
+ /** Live quota evidence from this exact account's reset-discovery response. */
568
+ report?: UsageReport;
569
+ /** Provider-requested wait before retrying throttled reset discovery (not redemption). */
570
+ retryAfterMs?: number;
567
571
  credentialId: number;
568
572
  accountId?: string;
569
573
  email?: string;
@@ -627,6 +631,20 @@ export type RotateCredentialOptions = {
627
631
  credentialId?: number;
628
632
  signal?: AbortSignal;
629
633
  };
634
+ /**
635
+ * Outcome of {@link LimitsApi.rotate}.
636
+ *
637
+ * `switched` is `true` when a usable same-type sibling credential is available,
638
+ * so the caller's next resolve hands it out. `afterSiblingWait` is `true` when
639
+ * no sibling was free at the failure but rotation slept out a sibling's short
640
+ * block (e.g. a Cloud Code Assist capacity 429 that resets in under a second);
641
+ * the freed credential may be one the caller already sent in this request, so
642
+ * attempted-bearer dedupe must let it through once.
643
+ */
644
+ export interface CredentialRotation {
645
+ switched: boolean;
646
+ afterSiblingWait?: boolean;
647
+ }
630
648
  /** Filter saved reset credits by provider and session. */
631
649
  export type ListResetCreditsOptions = {
632
650
  provider?: string;
@@ -1107,9 +1125,11 @@ export interface LimitsApi {
1107
1125
  * reload when no broker hook is wired) and block it, then drop matching
1108
1126
  * sticky state.
1109
1127
  *
1110
- * Returns whether another usable credential of the same type remains.
1128
+ * For usage-limit and account-policy failures with no free sibling, waits
1129
+ * (abortable via `options.signal`) when a sibling's block expires within a
1130
+ * few seconds, then reports `afterSiblingWait`.
1111
1131
  */
1112
- rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<boolean>;
1132
+ rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<CredentialRotation>;
1113
1133
  /** Invalidate a credential matching an API key after authentication failure. */
1114
1134
  invalidateMatching(provider: string, apiKey: string, options?: InvalidateCredentialMatchingOptions): Promise<boolean>;
1115
1135
  }
@@ -1134,7 +1154,7 @@ export interface BlocksApi {
1134
1154
  */
1135
1155
  upsert(block: StoredCredentialBlock): void;
1136
1156
  /**
1137
- * Broker-server seam: clear all persisted blocks for one credential and notify snapshot waiters.
1157
+ * Broker-server seam: clear one exact persisted block and notify snapshot waiters.
1138
1158
  */
1139
1159
  delete(credentialId: number, providerKey: string, blockScope: string): void;
1140
1160
  /** Delete all persisted blocks for a credential. */
@@ -8,8 +8,8 @@ export declare const USAGE_HEADER_INGEST_INTERVAL_MS = 60000;
8
8
  /**
9
9
  * Per-credential cool-down after a usage fetch fails. While this window is
10
10
  * active we serve the last successful value to avoid dropping the credential
11
- * from the report; without a previous value we just return null and retry
12
- * on the next poll. Used by UsageService.
11
+ * from the report; without a previous value we return null until the
12
+ * cooldown expires. Report invalidation does not bypass this cooldown.
13
13
  */
14
14
  export declare const USAGE_FAILURE_BACKOFF_MS = 10000;
15
15
  /** Default usage fetch timeout; used by the AuthStorage facade. */
@@ -44,6 +44,16 @@ export declare class UsageCache {
44
44
  constructor(store: AuthCredentialStore, pool: CredentialPool, usageProviders: (provider: Provider) => UsageProvider | undefined);
45
45
  get epoch(): number;
46
46
  bumpEpoch(): void;
47
+ /** Identifies confirmed recoveries that supersede an in-flight failed probe. */
48
+ recoveryEpoch(provider: Provider): number;
49
+ /**
50
+ * Changes when a user refresh, confirmed reset, or usage-implementation swap
51
+ * makes an in-flight probe too old to answer. Block marks do not change it:
52
+ * they stop a racing report from being cached, never trigger a re-probe.
53
+ */
54
+ refreshEpoch(provider: Provider): number;
55
+ /** Failure cooldowns survive report invalidation and process restarts. */
56
+ failureKey(reportKey: string): string;
47
57
  get<T>(key: string): UsageCacheEntry<T> | undefined;
48
58
  getStale<T>(key: string): UsageCacheEntry<T> | undefined;
49
59
  set<T>(key: string, entry: UsageCacheEntry<T>): void;
@@ -58,6 +68,8 @@ export declare class UsageCache {
58
68
  * `/usage` reflects a freshly-redeemed reset instead of stale numbers.
59
69
  */
60
70
  invalidate(provider: string, baseUrl?: string): void;
71
+ /** A confirmed reset permits one fresh probe even during a pre-reset failure cooldown. */
72
+ invalidateAfterReset(provider: Provider, baseUrl?: string): void;
61
73
  /**
62
74
  * Expire cached reports for a provider after its runtime usage implementation changes.
63
75
  * This keeps a newly installed extension provider from serving a built-in snapshot.
@@ -102,8 +102,8 @@ export declare class UsageService implements UsageApi {
102
102
  }): Promise<UsageReport[] | null>;
103
103
  /**
104
104
  * Discard cached usage reports before a user-requested refresh. The next
105
- * read probes upstream serially per provider; a failure reports no fresh
106
- * usage instead of replaying an invalidated last-good snapshot.
105
+ * read probes upstream serially per provider unless a failure cooldown is
106
+ * active. Failed probes never replay an invalidated last-good snapshot.
107
107
  */
108
108
  invalidate(provider?: string, signal?: AbortSignal): Promise<void>;
109
109
  }
@@ -6,7 +6,7 @@
6
6
  * `/v1/healthz` require a bearer token.
7
7
  */
8
8
  import type { AuthCredential, DisabledCredentialSummary } from "../auth-storage.js";
9
- import type { ClientUsageReportRequest, ClientUsageReportResponse, ClientUsageSummaryResponse, CredentialBlockRequest, CredentialBlockResponse, CredentialBlocksDeleteResponse, CredentialDisableResponse, CredentialRefreshResponse, CredentialUploadResponse, HealthzResponse, SnapshotResponse, SnapshotStreamEvent, UsageHistoryResponse, UsageResponse, UsageStaleResponse } from "./types.js";
9
+ import type { ClientUsageReportRequest, ClientUsageReportResponse, ClientUsageSummaryResponse, CredentialBlockDeleteRequest, CredentialBlockRequest, CredentialBlockResponse, CredentialBlocksDeleteResponse, CredentialDisableResponse, CredentialRefreshResponse, CredentialUploadResponse, HealthzResponse, SnapshotResponse, SnapshotStreamEvent, UsageHistoryResponse, UsageResponse, UsageStaleResponse } from "./types.js";
10
10
  export interface AuthBrokerClientOptions {
11
11
  /** Base URL (e.g. `https://broker.tailnet:8765`). Trailing slashes are trimmed. */
12
12
  url: string;
@@ -96,5 +96,6 @@ export declare class AuthBrokerClient {
96
96
  listDisabledCredentials(provider?: string, signal?: AbortSignal): Promise<DisabledCredentialSummary[]>;
97
97
  uploadCredential(provider: string, credential: AuthCredential, signal?: AbortSignal): Promise<CredentialUploadResponse>;
98
98
  upsertCredentialBlock(id: number, block: CredentialBlockRequest, signal?: AbortSignal): Promise<CredentialBlockResponse>;
99
+ deleteCredentialBlock(id: number, block: CredentialBlockDeleteRequest, signal?: AbortSignal): Promise<CredentialBlocksDeleteResponse>;
99
100
  deleteCredentialBlocks(id: number, signal?: AbortSignal): Promise<CredentialBlocksDeleteResponse>;
100
101
  }
@@ -76,7 +76,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
76
76
  getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
77
77
  listCredentialBlocks(credentialIds: readonly number[]): StoredCredentialBlock[];
78
78
  upsertCredentialBlock(block: StoredCredentialBlock): void;
79
- deleteCredentialBlock(_credentialId: number, _providerKey: string, _blockScope: string): void;
79
+ deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
80
80
  deleteCredentialBlocks(credentialId: number): void;
81
81
  cleanExpiredCredentialBlocks(nowMs: number): void;
82
82
  /**
@@ -131,9 +131,8 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
131
131
  refreshOAuthCredential(_provider: Provider, credentialId: number, _credential: OAuthCredential, signal?: AbortSignal): Promise<OAuthCredentials>;
132
132
  /**
133
133
  * Store-level hook consumed by `AuthStorage.usage.reports()` — proxies
134
- * to the broker's `/v1/usage` endpoint. The broker's egress IP isn't
135
- * rate-limited by Anthropic's per-IP `/usage` cap the way a heavy
136
- * residential laptop is, so all credentials surface every cycle.
134
+ * to the broker's `/v1/usage` endpoint. Shared per-credential caches and
135
+ * cooldowns keep separate clients from multiplying provider probes.
137
136
  */
138
137
  fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
139
138
  /**
@@ -142,8 +141,8 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
142
141
  * same response (coalesced + cached), then overlays any client-observed
143
142
  * header hints for the matching credential.
144
143
  *
145
- * The broker already aggregates with its own 30s TTL on the server side; our
146
- * 15s client TTL is below that so we usually re-use the broker's cache too.
144
+ * The broker caches each credential independently; the short client TTL
145
+ * also folds sequential consumers into one broker round-trip.
147
146
  */
148
147
  getUsageReport(provider: Provider, credential: OAuthCredential, signal?: AbortSignal): Promise<UsageReport | null>;
149
148
  ingestUsageReport(provider: Provider, credential: OAuthCredential, report: UsageReport): boolean;
@@ -73,11 +73,13 @@ export interface DisabledCredentialsResponse {
73
73
  }
74
74
  /** POST /v1/credential/:id/block request body. */
75
75
  export type CredentialBlockRequest = CredentialBlockSnapshot;
76
+ /** DELETE /v1/credential/:id/block request body; empty scope targets only the global row. */
77
+ export type CredentialBlockDeleteRequest = Pick<CredentialBlockSnapshot, "providerKey" | "blockScope">;
76
78
  /** POST /v1/credential/:id/block response body. */
77
79
  export interface CredentialBlockResponse {
78
80
  ok: boolean;
79
81
  }
80
- /** DELETE /v1/credential/:id/blocks response body. */
82
+ /** DELETE /v1/credential/:id/block or /blocks response body. */
81
83
  export interface CredentialBlocksDeleteResponse {
82
84
  ok: boolean;
83
85
  }
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { type FluentType } from "@oh-my-pi/omptype";
16
16
  import { type ApiKeyCredential, type AuthCredential, type AuthCredentialSnapshotEntry, type DisabledCredentialSummary, type OAuthCredential, type RemoteOAuthCredential, type SnapshotCredential } from "../auth-storage.js";
17
- import type { ClientUsageReportRequest, ClientUsageReportResponse, ClientUsageSummaryResponse, CredentialBlockRequest, CredentialBlockResponse, CredentialBlockSnapshot, CredentialBlocksDeleteResponse, CredentialDisableResponse, CredentialRefreshResponse, CredentialUploadRequest, CredentialUploadResponse, DisabledCredentialsResponse, HealthzResponse, RefresherSchedule, SnapshotEntry, SnapshotResponse, SnapshotStreamEntryEvent, SnapshotStreamEvent, SnapshotStreamRemovedEvent, SnapshotStreamSnapshotEvent, UsageHistoryResponse, UsageResponse, UsageStaleResponse } from "./types.js";
17
+ import type { ClientUsageReportRequest, ClientUsageReportResponse, ClientUsageSummaryResponse, CredentialBlockDeleteRequest, CredentialBlockRequest, CredentialBlockResponse, CredentialBlockSnapshot, CredentialBlocksDeleteResponse, CredentialDisableResponse, CredentialRefreshResponse, CredentialUploadRequest, CredentialUploadResponse, DisabledCredentialsResponse, HealthzResponse, RefresherSchedule, SnapshotEntry, SnapshotResponse, SnapshotStreamEntryEvent, SnapshotStreamEvent, SnapshotStreamRemovedEvent, SnapshotStreamSnapshotEvent, UsageHistoryResponse, UsageResponse, UsageStaleResponse } from "./types.js";
18
18
  /** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
19
19
  export declare const oauthCredentialSchema: FluentType<OAuthCredential>;
20
20
  /** OAuth credential as it appears in broker snapshots — refresh replaced with sentinel. */
@@ -62,6 +62,7 @@ export declare const disabledCredentialSummarySchema: FluentType<DisabledCredent
62
62
  /** Broker `GET /v1/credentials/disabled` response. */
63
63
  export declare const disabledCredentialsResponseSchema: FluentType<DisabledCredentialsResponse>;
64
64
  export declare const credentialBlockRequestSchema: FluentType<CredentialBlockRequest>;
65
+ export declare const credentialBlockDeleteRequestSchema: FluentType<CredentialBlockDeleteRequest>;
65
66
  export declare const credentialBlockResponseSchema: FluentType<CredentialBlockResponse>;
66
67
  export declare const credentialBlocksDeleteResponseSchema: FluentType<CredentialBlocksDeleteResponse>;
67
68
  export declare const usageStaleResponseSchema: FluentType<UsageStaleResponse>;
@@ -13,10 +13,10 @@ import { isAuthRetryableError } from "./error/auth-classify.js";
13
13
  * - `error !== undefined && lastChance` → **step (c): switch account**
14
14
  * (invalidate/usage-limit the current credential and rotate to a sibling).
15
15
  *
16
- * Current drivers preserve that bounded a/b/c sequence for ordinary 401/auth
17
- * failures. Account-scoped policy denials, 403s, and usage-limit failures skip
18
- * refresh and may repeat step (c) until the resolver returns `undefined`,
19
- * cycles, or hits {@link AUTH_RETRY_MAX_ATTEMPTS}.
16
+ * Current drivers give an ordinary 401/auth failure one step (b) before
17
+ * repeating step (c) through distinct siblings. Account-scoped policy denials,
18
+ * 403s, and usage-limit failures skip refresh. Rotation stops when the resolver
19
+ * returns `undefined`, cycles, or hits {@link AUTH_RETRY_MAX_ATTEMPTS}.
20
20
  */
21
21
  export interface ApiKeyResolveContext {
22
22
  /** True when the resolver should rotate to a sibling credential. */
@@ -36,11 +36,23 @@ export interface ResolvedApiKey {
36
36
  apiKey: string;
37
37
  /** Durable row id of the credential that supplied this bearer, when known. */
38
38
  credentialId?: number;
39
+ /**
40
+ * Resolved after `LimitsApi.rotate` slept out a sibling's short block
41
+ * (`afterSiblingWait`): the driver accepts this bearer even if the request
42
+ * already sent it before that block.
43
+ */
44
+ afterSiblingWait?: boolean;
39
45
  }
40
46
  export type ApiKeyResolution = string | ResolvedApiKey | undefined;
41
47
  export type ApiKeyResolver = (ctx: ApiKeyResolveContext) => Promise<ApiKeyResolution> | ApiKeyResolution;
42
48
  /** Extract the bearer while preserving optional credential provenance for streaming callers. */
43
49
  export declare function resolvedApiKeyBearer(resolved: ApiKeyResolution): string | undefined;
50
+ /**
51
+ * Mark a post-rotation resolution as following a sibling-unblock wait so the
52
+ * retry driver may resend a bearer it already tried. Used by the rotating
53
+ * resolvers (`KeyCascade.resolver`, coding-agent `createApiKeyResolver`).
54
+ */
55
+ export declare function markAfterSiblingWait(resolved: ApiKeyResolution): ApiKeyResolution;
44
56
  /** A static bearer string, or a {@link ApiKeyResolver} that mints/rotates one. */
45
57
  export type ApiKey = string | ApiKeyResolver;
46
58
  /** Keyless-provider credential marker; transports must not send it in authentication headers. */
@@ -63,9 +75,8 @@ export declare function resolveApiKeyOnce(key: ApiKey | undefined, signal?: Abor
63
75
  export declare function seedApiKeyResolver(seed: ApiKeyResolution, resolver: ApiKeyResolver): ApiKeyResolver;
64
76
  export { isAuthRetryableError };
65
77
  /**
66
- * Legacy bounded a/b/c retry sequence retained for public compatibility:
67
- * `false` → refresh-same, `true` → rotate/switch. Current drivers consume it
68
- * once for ordinary 401/auth failures; usage/account-limit failures may repeat
78
+ * Legacy a/b/c retry sequence retained for public compatibility:
79
+ * `false` → refresh-same, `true` → rotate/switch. Current drivers may repeat
69
80
  * sibling rotation until a termination guard fires.
70
81
  */
71
82
  export declare const AUTH_RETRY_STEPS: readonly boolean[];
@@ -79,8 +90,6 @@ export interface AuthRetryKeyState {
79
90
  lastKey: string;
80
91
  /** Whether the current credential already consumed its 401 refresh-same retry. */
81
92
  refreshedCurrent: boolean;
82
- /** Whether the legacy non-usage auth path already switched to one sibling. */
83
- legacyAuthSwitchUsed: boolean;
84
93
  /** Whether this operation already replayed once after an explicit token-refresh request. */
85
94
  tokenRefreshReplayUsed?: boolean;
86
95
  /** Total outbound attempts accepted for this logical operation, including the initial request. */
@@ -97,9 +106,9 @@ export declare function resolveNextAuthRetryKey(state: AuthRetryKeyState, resolv
97
106
  * applicable policy is exhausted, the resolver declines or cycles, or the
98
107
  * operation reaches {@link AUTH_RETRY_MAX_ATTEMPTS}. An explicit typed
99
108
  * token-refresh request gets exactly one refresh-current replay and never
100
- * enters sibling rotation. Ordinary 401/auth failures retain one
101
- * refresh-same plus one sibling switch; 403/usage-limit failures rotate
102
- * directly through distinct siblings.
109
+ * enters sibling rotation. Ordinary 401/auth failures get one refresh-same,
110
+ * then rotate through distinct siblings; 403/usage-limit failures skip the
111
+ * refresh and rotate directly.
103
112
  *
104
113
  * Used by non-streaming consumers (image generation, web search, completion
105
114
  * helpers). The streaming driver in `stream.ts` implements the same policy with
@@ -142,7 +151,7 @@ export interface WithOAuthAccessOptions {
142
151
  * - initial → `getOAuthAccess` (or `opts.seed`).
143
152
  * - typed token-refresh request → one forced refresh-current replay, then stop.
144
153
  * - 401/auth failure → one `getOAuthAccess` with `forceRefresh: true` for the
145
- * current account, then sibling rotation.
154
+ * current account, then sibling rotation through distinct credentials.
146
155
  * - 403/usage-limit failure → `rotateSessionCredential` directly, without a
147
156
  * force-refresh detour.
148
157
  *
@@ -14,11 +14,20 @@ export interface ImageGenerationRequest {
14
14
  export interface GeneratedImage {
15
15
  data: string;
16
16
  mimeType: string;
17
+ /** Output dimensions (`WIDTHxHEIGHT`) the provider reports for this image, which may differ from the request. */
18
+ size?: string;
19
+ /** Rendering quality the provider reports for this image. */
20
+ quality?: string;
17
21
  }
18
22
  export interface ImageGenerationResult {
19
23
  images: GeneratedImage[];
20
24
  text?: string;
21
25
  usage: Usage;
26
+ /**
27
+ * Image model the provider reports having run. Hosted backends may substitute their own model for the
28
+ * selected catalog entry (the ChatGPT/Codex backend does), so callers should prefer this over the catalog id.
29
+ */
30
+ model?: string;
22
31
  }
23
32
  export interface ImageGenerationOptions {
24
33
  apiKey: ApiKey;
@@ -101,6 +101,7 @@ export declare class JudgmentParseError extends Error {
101
101
  }
102
102
  /**
103
103
  * Usage from a backend that reports token counts and, optionally, one billed
104
- * USD amount. Judgment pricing is input-only, so the amount lands on `input`.
104
+ * USD amount. Missing or non-finite amounts contribute zero to session totals.
105
+ * Judgment pricing is input-only, so the amount lands on `input`.
105
106
  */
106
- export declare function tokenUsage(input: number, output: number, cost?: number): Usage;
107
+ export declare function tokenUsage(input: number | undefined, output: number | undefined, cost?: number): Usage;
@@ -17,7 +17,7 @@ import type { ToolResultMessage } from "../../types.js";
17
17
  * virtual registry. Re-exported here because this is where the frame builders
18
18
  * and their translation are consumed together.
19
19
  */
20
- export { cursorEditOwnedReadPath, cursorRawReadPath, omitUndefinedArgs, piEscapeRegexLiteral, piGrepSkip, piJoinPath, piLimit, piLsPath, piReadDisplayPath, piReadPath, piReadPathHasRange, piTimeout, } from "../cursor-pi-args.js";
20
+ export { cursorEditOwnedReadPath, cursorRawReadPath, omitUndefinedArgs, piEscapeRegexLiteral, piGrepSkip, piJoinPath, piLimit, piLsPath, piReadDisplayPath, piReadPath, piReadPathHasRange, piTimeout, shellTimeoutSeconds, } from "../cursor-pi-args.js";
21
21
  /** Flatten a tool result's content into the single `output` string the Pi frames carry. */
22
22
  export declare function piOutputText(toolResult: ToolResultMessage): string;
23
23
  /**
@@ -124,6 +124,16 @@ export declare function piLimit(limit: number | undefined): number | undefined;
124
124
  * Negative values have no local meaning and fall back to the default.
125
125
  */
126
126
  export declare function piTimeout(timeout: number | undefined): number | undefined;
127
+ /**
128
+ * Convert a legacy `ShellArgs`/`ShellStreamArgs` timeout into bash-tool seconds.
129
+ *
130
+ * Cursor states that budget in milliseconds — its own `ShellTimeout` result
131
+ * echoes it as `timeout_ms`, and `hard_timeout` documents the same unit — while
132
+ * the bash tool takes seconds and rejects anything past 3600, so forwarding the
133
+ * raw value turned a model-requested 15 s into `requested 15000s`. Sub-second
134
+ * budgets round up: 0 seconds means "no deadline" to the bash tool.
135
+ */
136
+ export declare function shellTimeoutSeconds(timeoutMs: number | undefined): number | undefined;
127
137
  /**
128
138
  * Drop keys whose value is `undefined` so optional local-tool kwargs stay
129
139
  * absent rather than present-as-undefined.
@@ -211,11 +211,15 @@ export declare function mergeCursorMcpToolCallArgs(streamed: Record<string, unkn
211
211
  * `agent-loop.ts` execution pass skips it — Cursor's server-driven exec
212
212
  * channel already ran the tool via the bridge and buffered the result, so
213
213
  * treating this block as runnable would re-execute the same side-effecting
214
- * tool a second time.
214
+ * tool a second time. Pass `{ executed: false }` for a frame nothing ran: a
215
+ * call handed to an external executor is only a handoff while the marker is
216
+ * absent, and the consumer that runs it needs a runnable block.
215
217
  *
216
218
  * Exported for tests to exercise ordering with adjacent text/thinking blocks.
217
219
  */
218
- export declare function synthesizeCursorExecToolCall(output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, toolCallId: string, toolName: string, args: Record<string, unknown>): void;
220
+ export declare function synthesizeCursorExecToolCall(output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, toolCallId: string, toolName: string, args: Record<string, unknown>, { executed }?: {
221
+ executed?: boolean;
222
+ }): void;
219
223
  /** Exported for tests: drives one Cursor interaction update through the streaming state machine. */
220
224
  export declare function processInteractionUpdate(update: any, output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, usageState: UsageState): void;
221
225
  /**
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Shared utilities for Google Generative AI and Google Cloud Code Assist providers.
3
3
  */
4
- import type { AssistantMessage, Context, FetchImpl, Model, ServiceTier, StopReason, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall } from "../types.js";
4
+ import type { AssistantMessage, Context, FetchImpl, Model, ServiceTier, StopReason, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall, Usage } from "../types.js";
5
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
6
6
  import { normalizeSchemaForGoogle } from "../utils/schema/index.js";
7
- import type { Content, FinishReason, FunctionCallingConfigMode, GenerateContentParameters, GenerateContentResponse, Part } from "./google-types.js";
7
+ import type { Content, FinishReason, FunctionCallingConfigMode, GenerateContentParameters, GenerateContentResponse, Part, UsageMetadata } from "./google-types.js";
8
8
  export type { Content, FunctionCallingConfigMode, GenerateContentParameters, GenerateContentResponse, ThinkingConfig, } from "./google-types.js";
9
9
  export { normalizeSchemaForGoogle };
10
10
  type GoogleApiType = "google-generative-ai" | "google-gemini-cli" | "google-vertex";
@@ -120,6 +120,17 @@ export declare const EMPTY_STREAM_BASE_DELAY_MS = 500;
120
120
  * must be retried instead of surfaced.
121
121
  */
122
122
  export declare function hasMeaningfulGoogleContent(output: AssistantMessage): boolean;
123
+ /**
124
+ * Maps Gemini `usageMetadata` onto {@link Usage} (cost zeroed for `calculateCost`).
125
+ *
126
+ * `promptTokenCount` includes `cachedContentTokenCount`, so input = prompt − cached,
127
+ * matching the OpenAI convention where input + cacheRead = total prompt tokens.
128
+ * Upstream sometimes omits `promptTokenCount` or reports a cache count above the
129
+ * prompt (Antigravity), so the prompt falls back to `total − candidates − thoughts`
130
+ * and cached tokens are clamped to it: input is never negative.
131
+ * Ref: https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse.UsageMetadata
132
+ */
133
+ export declare function mapGoogleUsage(metadata: UsageMetadata): Usage;
123
134
  export declare function nextToolCallId(name: string): string;
124
135
  /**
125
136
  * Push the appropriate `text_end` / `thinking_end` event for the given block.
@@ -5,6 +5,10 @@ export declare function claudeResetClearedBlockScopes(cleared: readonly string[]
5
5
  /** OAuth credential and transport used for an account's Claude reset operations. */
6
6
  export interface ClaudeResetAuth {
7
7
  accessToken: string;
8
+ /** Observe a safe discovery read's throttle without retrying any mutation. */
9
+ onRateLimited?: (retryAfterMs: number | undefined) => void;
10
+ accountId?: string;
11
+ email?: string;
8
12
  orgId?: string;
9
13
  baseUrl?: string;
10
14
  fetch: FetchImpl;
@@ -13,6 +17,8 @@ export interface ClaudeResetAuth {
13
17
  /** Live reset eligibility plus the exact organization and host used for redemption. */
14
18
  export interface ClaudeResetCreditList extends UsageResetCredits {
15
19
  credits: UsageResetCredit[];
20
+ /** Quota evidence parsed from the same successful discovery response. */
21
+ report?: UsageReport;
16
22
  orgId?: string;
17
23
  /** OAuth API base that answered discovery; pass back as consume `baseUrl`. */
18
24
  baseUrl?: string;
@@ -1,4 +1,10 @@
1
1
  import { type CredentialRankingStrategy, type UsageProvider, type UsageReport } from "../usage.js";
2
2
  export declare function parseClaudeRateLimitHeaders(headers: Record<string, string>, now?: number): UsageReport | null;
3
+ /** Parse the quota evidence in one live usage response, without any additional requests. */
4
+ export declare function parseClaudeUsagePayload(raw: unknown, identity?: {
5
+ accountId?: string;
6
+ email?: string;
7
+ orgId?: string;
8
+ }, endpoint?: string, fetchedAt?: number): UsageReport | null;
3
9
  export declare const claudeUsageProvider: UsageProvider;
4
10
  export declare const claudeRankingStrategy: CredentialRankingStrategy;
@@ -669,6 +669,8 @@ export interface CredentialRankingStrategy {
669
669
  limits: UsageLimit[];
670
670
  healthy?: boolean;
671
671
  }[];
672
+ /** Whether fresh reports can heal legacy account-wide quota backoffs. */
673
+ healsGlobalBlocks?: boolean;
672
674
  /** Fallback window durations (ms) when limits don't specify durationMs. */
673
675
  windowDefaults: {
674
676
  primaryMs: number;
@@ -23,8 +23,8 @@ export declare function getProxyForUrl(provider: string, url: URL): string | und
23
23
  /**
24
24
  * Return `init` with `proxy: proxyUrl` set when the request should tunnel.
25
25
  * A caller-supplied `init.proxy` always wins (the innermost, most specific
26
- * decision); local/metadata hosts, NO_PROXY matches, and unparseable URLs
27
- * pass through unchanged.
26
+ * decision); Unix sockets, local/metadata hosts, NO_PROXY matches, and
27
+ * unparseable URLs pass through unchanged.
28
28
  */
29
29
  export declare function withProxyInit(input: string | URL | Request, init: RequestInit | undefined, proxyUrl: string): RequestInit | undefined;
30
30
  /**