@oh-my-pi/pi-ai 18.6.1 → 18.7.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 (55) hide show
  1. package/CHANGELOG.md +38 -38
  2. package/README.md +15 -0
  3. package/dist/types/auth/types.d.ts +4 -0
  4. package/dist/types/auth-broker/refresher.d.ts +6 -1
  5. package/dist/types/error/flags.d.ts +15 -0
  6. package/dist/types/images/index.d.ts +2 -3
  7. package/dist/types/providers/anthropic.d.ts +15 -2
  8. package/dist/types/providers/google-auth.d.ts +4 -1
  9. package/dist/types/providers/mock.d.ts +3 -1
  10. package/dist/types/providers/openai-responses-wire.d.ts +0 -54
  11. package/dist/types/providers/openai-responses.d.ts +32 -3
  12. package/dist/types/providers/openai-shared.d.ts +4 -1
  13. package/dist/types/registry/oauth/cursor.d.ts +9 -1
  14. package/dist/types/registry/oauth/index.d.ts +2 -0
  15. package/dist/types/registry/oauth/snowflake.d.ts +3 -0
  16. package/dist/types/registry/snowflake.d.ts +4 -0
  17. package/dist/types/types.d.ts +72 -13
  18. package/dist/types/utils/idle-iterator.d.ts +11 -0
  19. package/dist/types/utils.d.ts +6 -0
  20. package/package.json +6 -6
  21. package/src/auth/cascade.ts +6 -2
  22. package/src/auth/health.ts +1 -0
  23. package/src/auth/select.ts +3 -1
  24. package/src/auth/types.ts +4 -0
  25. package/src/auth-broker/refresher.ts +8 -1
  26. package/src/error/flags.ts +22 -0
  27. package/src/images/index.ts +3 -17
  28. package/src/providers/anthropic.ts +111 -45
  29. package/src/providers/devin.ts +3 -1
  30. package/src/providers/factory-droid.ts +2 -0
  31. package/src/providers/gitlab-duo.ts +1 -0
  32. package/src/providers/google-auth.ts +10 -3
  33. package/src/providers/mock.ts +4 -0
  34. package/src/providers/openai-anthropic-shim.ts +1 -0
  35. package/src/providers/openai-codex-responses.ts +37 -38
  36. package/src/providers/openai-completions.ts +3 -0
  37. package/src/providers/openai-responses-wire.ts +0 -54
  38. package/src/providers/openai-responses.ts +349 -6
  39. package/src/providers/openai-shared.ts +20 -9
  40. package/src/providers/pi-native-server.ts +1 -0
  41. package/src/registry/hooks/custom.ts +2 -0
  42. package/src/registry/hooks/env.ts +2 -5
  43. package/src/registry/oauth/cursor.ts +57 -3
  44. package/src/registry/oauth/index.ts +5 -0
  45. package/src/registry/oauth/snowflake.ts +183 -0
  46. package/src/registry/registry.ts +2 -0
  47. package/src/registry/snowflake.ts +149 -0
  48. package/src/stream.ts +35 -33
  49. package/src/types.ts +93 -14
  50. package/src/usage/claude.ts +3 -0
  51. package/src/usage/cursor.ts +14 -25
  52. package/src/usage/minimax-code.ts +8 -1
  53. package/src/utils/idle-iterator.ts +80 -35
  54. package/src/utils/thinking-loop.ts +18 -10
  55. package/src/utils.ts +19 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,43 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.7.0] - 2026-10-06
6
+
7
+ ### Added
8
+
9
+ - Added `getOAuthCredentialProvider()` to resolve login aliases, such as `openai-codex-device`, to the provider where their credentials are stored.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed Ultrafast service-tier billing and usage accounting: GPT-6 Astra now applies its published premium rates—6× on the OpenAI API and 8× included usage on Codex—and is counted toward the premium-request limit.
14
+ - Fixed Vertex AI authentication on Windows when credentials are created with `gcloud auth application-default login`.
15
+ - Fixed selecting Cursor accounts by email through `auth.accountPolicies` and `/session pin`; newly refreshed and existing accounts now retain the account email.
16
+
17
+ ## [18.6.3] - 2026-10-06
18
+
19
+ ### Added
20
+
21
+ - Added per-model `compat.statefulResponses` to enable or disable stored Responses chaining (`previous_response_id` with `store: true`) for one endpoint without the official-only request fields that `compat.officialEndpoint` implies ([#13686](https://github.com/can1357/oh-my-pi/pull/13686) by [@alphastorm](https://github.com/alphastorm)).
22
+ - Added Snowflake Cortex with browser OAuth, token refresh, PAT environment authentication, and streaming Claude/OpenAI models with local tool execution ([#14507](https://github.com/can1357/oh-my-pi/pull/14507) by [@jorgoose](https://github.com/jorgoose)).
23
+ - `AuthStorage.health.check()` accepts `excludeProviders` to skip credentials of providers the caller does not serve ([#14234](https://github.com/can1357/oh-my-pi/pull/14234) by [@will-bogusz](https://github.com/will-bogusz))
24
+
25
+ ### Changed
26
+
27
+ - Reduced CPU spent on thinking-loop detection while streaming long reasoning ([#14284](https://github.com/can1357/oh-my-pi/pull/14284) by [@abilliontokens](https://github.com/abilliontokens)).
28
+ - Added `recordAffinity: false` to credential resolution options, selecting as the session would without pinning the choice to that session ([#14512](https://github.com/can1357/oh-my-pi/pull/14512) by [@will-bogusz](https://github.com/will-bogusz))
29
+
30
+ ### Fixed
31
+
32
+ - Codex native-lane steering rejections (`unsupported_native_inflight_message`) now classify as retryable from their error text alone, matching the provider's own classification, and `AIError.isCodexSteerRejection()` identifies them so the agent retry can stay on the same model ([#14242](https://github.com/can1357/oh-my-pi/pull/14242) by [@alphastorm](https://github.com/alphastorm))
33
+ - Fixed replayed Responses and Codex history, including persisted Codex user/developer and assistant items, sending `detail: "original"` images to endpoints whose `supportsImageDetailOriginal` is off ([#13687](https://github.com/can1357/oh-my-pi/pull/13687) by [@alphastorm](https://github.com/alphastorm)).
34
+ - Fixed thinking in turns kept after Anthropic native compaction being rejected or dropped on the next request ([#14251](https://github.com/can1357/oh-my-pi/pull/14251) by [@will-bogusz](https://github.com/will-bogusz))
35
+ - Fixed Claude usage being re-polled every 10 seconds while Anthropic rate-limits the account; a failed refresh now waits a minute before trying again ([#14515](https://github.com/can1357/oh-my-pi/pull/14515) by [@will-bogusz](https://github.com/will-bogusz))
36
+ - Fixed Devin requests skipping the `onPayload` hook, so payload capture now sees each Devin chat request and a returned replacement is what gets sent ([#14506](https://github.com/can1357/oh-my-pi/pull/14506) by [@will-bogusz](https://github.com/will-bogusz))
37
+ - An OpenAI Responses turn whose connection drops mid-stream can now recover the finished answer from the provider (on hosts that store results, such as Muse Code) instead of re-running the whole turn and discarding the reasoning already done. Storage is opt-in: pass `storeResponses: true`, set `PI_MUSE_STORE_RESPONSES=1`, or set a process-wide default with `configureProviderStoreResponses` ([#14293](https://github.com/can1357/oh-my-pi/pull/14293) and [#14534](https://github.com/can1357/oh-my-pi/pull/14534) by [@abilliontokens](https://github.com/abilliontokens)).
38
+ - Fixed the auth broker exiting when a background OAuth refresh sweep cannot read the credential store; the failure is now logged and the next sweep retries ([#14538](https://github.com/can1357/oh-my-pi/issues/14538))
39
+ - Fixed Anthropic OAuth billing headers changing during developer-first sessions and side turns, preserving the prompt-cache prefix ([#14495](https://github.com/can1357/oh-my-pi/issues/14495)).
40
+ - Fixed OpenAI-compatible chat-completions gateways recording completed turns as client-cancelled because the connection closed before their `[DONE]` sentinel arrived ([#14481](https://github.com/can1357/oh-my-pi/issues/14481)).
41
+
5
42
  ## [18.6.1] - 2026-10-04
6
43
 
7
44
  ### Fixed
@@ -2290,41 +2327,4 @@
2290
2327
  - Fixed truncated Gemini and Gemma tool blocks from being emitted as plain text during streaming
2291
2328
  - Fixed Gemini/Gemma in-band tool-call parsing around Python comments, raw/unicode string literals, and Gemma close-token text inside string values.
2292
2329
 
2293
- ## [15.13.2] - 2026-06-15
2294
-
2295
- ### Added
2296
-
2297
- - Added `jsonSchemaToTypeScript` to `@oh-my-pi/pi-ai/utils/schema` to render JSON Schema argument shapes as compact, human-readable TypeScript-style signatures
2298
- - Added the generic `ToolExample` type (`ToolCallExample`/`ToolCompareExample`/`ToolNoteExample`, parameterized over a tool's argument shape) and an `examples` property on the `Tool` interface for defining tool-call examples once as data.
2299
- - Added `renderToolExamples` (via `@oh-my-pi/pi-ai/grammar`) to render a tool's examples into an `<examples>` block in the model's native tool-call syntax, with an optional `_i` intent-field placeholder injected when intent tracing is active.
2300
- - Added per-grammar `renderToolCall` rendering of a single tool-call invocation (the inner element only, without the parallel-call block envelope), distinct from `renderAssistantToolCalls` which renders a complete block of one or more parallel calls.
2301
- - Added a `GrammarRenderOptions.example` flag to `renderToolCall`: when set, the invocation renders as the bare payload — Harmony emits just the JSON arguments, dropping the verbose `<|start|>…<|message|>…<|call|>` envelope — so `renderToolExamples` keeps `<examples>` blocks legible.
2302
- - Added an `abortOnFabrication` parameter to `wrapInbandToolStream` (default `true`): when `false`, a fabricated in-band tool-result continuation is discarded without aborting the provider request instead of cutting the turn short.
2303
- - Added `@oh-my-pi/pi-ai/utils/harmony-leak` export with helpers to detect, audit, and recover GPT-5 Harmony tool-call header leaks
2304
- - Added the `@oh-my-pi/pi-ai/grammar` public entrypoint for grammar factories, prompt/call rendering, in-band scanning, history encoding, and related typed utilities
2305
- - Added a unified in-band tool-call grammar engine with syntax-owned scanners, prompts, history rendering, tool-result rendering, and stream adaptation for GLM, Hermes/Qwen, Kimi, XML/Anthropic, DeepSeek, Harmony, and pi-native formats.
2306
-
2307
- ### Changed
2308
-
2309
- - Changed Harmony in-band tool-call rendering to omit the `<|constrain|>json` marker before the payload in `commentary` channel calls
2310
- - Changed tool inventory rendering to present each tool’s `Parameters` section as a simplified TypeScript-style signature derived from its wire schema
2311
- - Added raw in-band tool-call block capture to parsed owned tool calls so debugging can inspect the exact model-emitted call syntax.
2312
- - Moved the canonical `ToolCallSyntax` union to `@oh-my-pi/pi-catalog/identity` and re-exported it from `@oh-my-pi/pi-ai/grammar` so the catalog can own the syntax vocabulary without an `@oh-my-pi/pi-ai` runtime import; all existing import paths are unchanged.
2313
- - Made tool-call argument validation more lenient for schema-directed scalar coercions, including object/array stringification and 0/1 boolean coercion.
2314
- - Changed `renderToolInventory` (the verbose system-prompt inventory and `/dump`) to render each tool as a `# Tool: <name>` markdown section instead of a `<tool name="…">…</tool>` wrapper.
2315
-
2316
- ### Fixed
2317
-
2318
- - Fixed Harmony leak handling support by adding `recoverHarmonyToolCall` plus leak-detection workflows for contaminated assistant messages so recoverable tool-call arguments can be safely truncated and retried
2319
- - Fixed false-positive gating in Harmony leak heuristics using signal-based checks so unrelated text containing `to=functions...` is not treated as leaked tool-call markup
2320
- - Routed Kimi, DeepSeek DSML, and plain thinking markup healing through the shared in-band scanners so provider leak repair and owned tool calling parse the same wire formats.
2321
- - Fixed Cursor provider (`cursor-agent` API) streaming dropping large MCP tool-call arguments — most visibly the built-in `task` tool's `tasks` array on multi-subagent dispatches, which failed downstream schema validation with `tasks: Invalid input: expected array, received undefined`. Two upstream behaviors were fighting the stream handler in `packages/ai/src/providers/cursor.ts`: (1) `args_text_delta` carries the _cumulative_ args text so far per `agent.proto`, but the handler concatenated each snapshot onto the buffer, garbling the JSON; (2) `tool_call_completed` carries an `McpArgs` map that omits oversized parameters entirely and downgrades unparsable values to their raw string fallback, but the handler unconditionally overwrote the streamed args with that map. The handler now strips the already-buffered prefix from each `args_text_delta` snapshot (falling back to append when the snapshot doesn't extend the buffer) and merges the decoded `McpArgs` map into the streamed args — preserving streamed keys the completion frame omits and the structured value when the completion frame downgrades to a string. ([#2615](https://github.com/can1357/oh-my-pi/issues/2615))
2322
- - Fixed Codex Responses stream mis-routing interleaved `function_call_arguments.delta` events when more than one tool call was open concurrently. The runtime tracked a singleton `currentItem`/`currentBlock`, so every delta — regardless of `item_id` — was appended to whichever item was most recently added, and `output_item.done` for the earlier call then overwrote a sibling's stored arguments (visible as `tasks: Invalid input: expected array, received undefined` on the `task` tool). Open items are now keyed by `item_id` with `output_index` fallback; deltas/done events route to the matching block, late deltas whose item already closed are dropped instead of corrupting a sibling, and `toolcall_*` stream events emit the right `contentIndex` per call ([#2619](https://github.com/can1357/oh-my-pi/issues/2619)).
2323
-
2324
- ## [15.13.1] - 2026-06-15
2325
-
2326
- ### Fixed
2327
-
2328
- - Fixed the auth-broker (`OMP_AUTH_BROKER_URL`) rejecting OAuth credentials that carry provider-specific extension fields (e.g. an MCP server's `tokenUrl`/`clientId`/`clientSecret`/`resource` embedded for self-contained token refresh): the OAuth credential wire schema was `.strict()`, so `POST /v1/credential` failed with `400 unrecognized_keys` and a broker-backed MCP reauth reported success while the reloaded credential lacked its refresh material and could no longer refresh. The OAuth wire schema now uses `.loose()` to preserve unknown fields — matching the field-preserving local SQLite store — so extra OAuth fields round-trip through broker set->get (envelope and API-key schemas stay strict).
2329
-
2330
- Older entries are archived in [packages/ai/CHANGELOG.md@bac7e83b5b0e](https://github.com/can1357/oh-my-pi/blob/bac7e83b5b0eb86c909c17830a6666efc359578b/packages/ai/CHANGELOG.md).
2330
+ Older entries are archived in [packages/ai/CHANGELOG.md@a20cc0d04b64](https://github.com/can1357/oh-my-pi/blob/a20cc0d04b64f4a68fcc559cbd258743d984c50e/packages/ai/CHANGELOG.md).
package/README.md CHANGED
@@ -77,6 +77,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
77
77
  - **QwenCloud Token Plan** (supports `/login alibaba-token-plan`, `ALIBABA_TOKEN_PLAN_API_KEY`, or `BAILIAN_TOKEN_PLAN_API_KEY`; interactive login first selects a region — International (Singapore, default), China (Beijing) for 百炼 Token Plan keys, or a custom base URL — since region keys are non-interchangeable, then optionally stores a `home.qwencloud.com` Cookie request header for best-effort 5-hour and 7-day quota reporting)
78
78
  To enable quota reporting, sign in to the Token Plan dashboard, copy the `Cookie` request-header value from a `home.qwencloud.com` request in browser developer tools, and paste it at the second login prompt. Press Enter to skip; the Cookie is sensitive and session-lived, so rerun login when it expires.
79
79
  - **Cloudflare AI Gateway** (supports `/login cloudflare-ai-gateway`, or `CLOUDFLARE_AI_GATEWAY_API_KEY` with `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_GATEWAY_ID`)
80
+ - **Snowflake Cortex** (browser `/login snowflake`, or `SNOWFLAKE_ACCOUNT` + `SNOWFLAKE_PAT`; account-billed Cortex REST API)
80
81
  - **Ollama** (local OpenAI-compatible runtime; optional `OLLAMA_API_KEY`)
81
82
  - **Ollama Cloud** (hosted native Ollama API; requires `OLLAMA_CLOUD_API_KEY`)
82
83
  - **llama.cpp** (local OpenAI and Anthropic compatible inference server)
@@ -961,6 +962,7 @@ In Node.js environments, you can set environment variables to avoid passing API
961
962
  | ZenMux | `ZENMUX_API_KEY` |
962
963
  | vLLM | `VLLM_API_KEY` |
963
964
  | Cloudflare AI Gateway | `CLOUDFLARE_AI_GATEWAY_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
965
+ | Snowflake Cortex | `SNOWFLAKE_PAT` + `SNOWFLAKE_ACCOUNT` |
964
966
  | GitHub Copilot | `COPILOT_GITHUB_TOKEN` or `GH_TOKEN` or `GITHUB_TOKEN` |
965
967
 
966
968
  `/login cloudflare-ai-gateway` collects and stores the gateway token, account ID, and gateway ID. For environment configuration, set all three Cloudflare values above. OMP derives provider endpoints from the account and gateway IDs.
@@ -1029,9 +1031,22 @@ Several providers support OAuth authentication (some also support static API key
1029
1031
  - **Google Gemini CLI** (Gemini 2.0/2.5 via Google Cloud Code Assist; free tier or paid subscription)
1030
1032
  - **Antigravity** (Free Gemini 3, Claude, GPT-OSS via Google Cloud)
1031
1033
  - **Qwen Portal** (Qwen OAuth token or API key)
1034
+ - **Snowflake Cortex** (Snowflake account OAuth with PKCE and refresh, subject to the account's local-application integration)
1032
1035
 
1033
1036
  For paid Cloud Code Assist subscriptions, set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` to your project ID.
1034
1037
 
1038
+ ### Snowflake Cortex
1039
+
1040
+ `/login snowflake` prompts for an account identifier (for example, `myorg-myaccount`), an HTTPS Snowflake account URL, or a Snowsight account link. It opens Snowflake's built-in `SNOWFLAKE$LOCAL_APPLICATION` OAuth flow using PKCE and a `127.0.0.1` callback (port 54551, with an ephemeral-port fallback). The stored account selects the inference endpoint; OAuth credentials cannot be sent to a different account through a custom model endpoint.
1041
+
1042
+ The default inference role must hold either `SNOWFLAKE.CORTEX_USER` or the narrower, REST-only `SNOWFLAKE.CORTEX_REST_API_USER` (recommended for least privilege). `CORTEX_REST_API_USER` cannot be granted to a user directly; grant it to a custom role, and revoke `CORTEX_USER` from users who already hold it so the narrower control applies. The Cortex REST API is not available in China-region (`.snowflakecomputing.cn`) accounts. An administrator can check local-app availability with `SHOW SECURITY INTEGRATIONS LIKE 'SNOWFLAKE$LOCAL_APPLICATION'` and enable the integration or refresh issuance if necessary. When Snowflake issues a refresh token, omp refreshes the access token automatically. Without one, login remains valid until expiry; then sign in again or use the PAT environment alternative.
1043
+
1044
+ For PAT authentication, set both `SNOWFLAKE_ACCOUNT` and `SNOWFLAKE_PAT`. Prefer a short-lived PAT restricted to a dedicated inference role and the required user-scoped network allowlist; do not disable network-policy enforcement. See [Snowflake PAT setup](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens) and [local-application OAuth](https://docs.snowflake.com/en/user-guide/oauth-local-applications).
1045
+
1046
+ Claude models use `/api/v2/cortex/v1/messages`; OpenAI models use `/api/v2/cortex/v1/chat/completions`. Both reuse omp's streaming transports and local tool execution. The 15 bundled models are a documented roster, not an account entitlement check: availability still depends on Cortex REST API enablement, role grants, model allowlists, and region. If authentication succeeds but Cortex returns `003001` ("This account is not allowed to access this endpoint"), Snowflake has not enabled Cortex REST for the account. [Self-service trial accounts disable AI features until a credit card is added](https://docs.snowflake.com/en/user-guide/admin-trial-account#label-trial-account-ai-features) (adding one does not end the trial); REST access can remain disabled even after AI SQL functions work, in which case Snowflake support must enable it. Cross-region inference requires an account administrator's explicit decision. `omp models snowflake --json` lists available models only after credentials are configured.
1047
+
1048
+ This provider uses the [public, account-billed Cortex REST API](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api). CoCo-subscription coverage is unverified. Catalog prices are per-million-token estimates from [Consumption Table 6(b)](https://www.snowflake.com/legal-files/CreditConsumptionTable.pdf) at [$2 per AI Credit](https://docs.snowflake.com/en/user-guide/snowflake-cortex/pricing) for on-demand global routing, not your contract price. Output is capped at the REST API's documented 16,384-token maximum; context windows are conservative AI_COMPLETE limits, not verified REST maxima. Cortex compatibility rules limit explicit cache retention to five minutes. Paste an account identifier or current Snowsight link; legacy `app.snowflake.com/<region>/<locator>` links are rejected because they don't map uniformly to an account host.
1049
+
1035
1050
  ### Vertex AI (ADC)
1036
1051
 
1037
1052
  Vertex AI models use Application Default Credentials (ADC):
@@ -220,6 +220,8 @@ export interface CheckCredentialsOptions {
220
220
  completionProbe?: CompletionProbe;
221
221
  /** Per-credential completion probe timeout (ms). Defaults to `timeoutMs`. */
222
222
  completionTimeoutMs?: number;
223
+ /** Providers whose credentials are skipped entirely (not probed, not reported). */
224
+ excludeProviders?: ReadonlySet<string>;
223
225
  }
224
226
  /**
225
227
  * Sentinel value placed in OAuth `refresh` fields when a credential is shared
@@ -424,6 +426,8 @@ export type AuthApiKeyOptions = {
424
426
  forceRefresh?: boolean;
425
427
  /** Explicit provider-401 recovery; generic force refreshes leave this unset. */
426
428
  refreshReason?: OAuthRefreshReason;
429
+ /** When false, select as `sessionId` would without recording the choice as that session's sticky credential. */
430
+ recordAffinity?: boolean;
427
431
  };
428
432
  /** Non-secret identity bound to the OAuth credential selected for one request attempt. */
429
433
  export interface OAuthRequestIdentity {
@@ -20,6 +20,11 @@ export declare class AuthBrokerRefresher {
20
20
  start(): void;
21
21
  stop(): void;
22
22
  getSchedule(): AuthBrokerRefresherSchedule;
23
- /** Run one sweep. Exposed for tests. */
23
+ /**
24
+ * Run one sweep. Exposed for tests.
25
+ *
26
+ * Never rejects: callers fire it unawaited from a timer, so a failed sweep
27
+ * (e.g. an unreadable credential store) is logged and retried next interval.
28
+ */
24
29
  tick(): Promise<void>;
25
30
  }
@@ -43,6 +43,21 @@ export declare function isResponsesRequestBodyReadTimeout(message: {
43
43
  errorMessage?: string;
44
44
  requestBodyReadTimeoutFullReplay?: boolean;
45
45
  }): boolean;
46
+ /**
47
+ * Codex code for the experimental native turn lane refusing a mid-response
48
+ * `response.steer`; the server then drops the in-flight response and closes
49
+ * the socket.
50
+ */
51
+ export declare const CODEX_NATIVE_LANE_STEER_REJECTED_CODE = "unsupported_native_inflight_message";
52
+ /**
53
+ * A Codex turn the native turn lane dropped because omp steered it. The
54
+ * rejection answers our own `response.steer`, not the model's health: the
55
+ * provider stops steering the session, so the same model replays cleanly.
56
+ */
57
+ export declare function isCodexSteerRejection(message: {
58
+ api?: Api;
59
+ errorMessage?: string;
60
+ }): boolean;
46
61
  export declare const TRANSIENT_TRANSPORT_PATTERN: RegExp;
47
62
  export declare const ANTHROPIC_ACCOUNT_POLICY_PATTERN: RegExp;
48
63
  /** Whether an error message represents an Anthropic account-scoped permission/policy denial. */
@@ -1,4 +1,4 @@
1
- import type { Api, Model } from "@oh-my-pi/pi-catalog/types";
1
+ import { type Api, type ImageGenerationApi, type Model } from "@oh-my-pi/pi-catalog/types";
2
2
  import type { ImageGenerationOptions, ImageGenerationRequest, ImageGenerationResult } from "./types.js";
3
3
  export * from "./google-antigravity.js";
4
4
  export * from "./google-generative-ai.js";
@@ -6,8 +6,7 @@ export * from "./openai-hosted.js";
6
6
  export * from "./openai-images.js";
7
7
  export * from "./openrouter-images.js";
8
8
  export * from "./types.js";
9
- /** Catalog APIs {@link generateImage} serves; the hosted Responses pair needs an explicit carrier model. */
10
- export type ImageGenerationApi = "openai-images" | "openrouter-images" | "google-generative-ai" | "google-gemini-cli" | "openai-responses" | "openai-codex-responses";
9
+ export type { ImageGenerationApi } from "@oh-my-pi/pi-catalog/types";
11
10
  /** Whether a catalog API generates images through one of the pi-ai image clients. */
12
11
  export declare function isImageGenerationApi(api: Api): api is ImageGenerationApi;
13
12
  /** Generate (or edit, when `request.inputImages` is set) images through the transport selected by the model's `api`. */
@@ -1,4 +1,5 @@
1
- import type { AnthropicOutputEffort, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
1
+ import type { Effort } from "@oh-my-pi/pi-catalog/effort";
2
+ import type { AnthropicCompactionRequest, AnthropicOutputEffort, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
2
3
  import { type AnthropicFetchOptions, type AnthropicMessagesClientLike } from "./anthropic-client.js";
3
4
  import { type FallbackParam, type MessageParam, type TextBlockParam } from "./anthropic-wire.js";
4
5
  import { resolvesToOfficialAnthropicEndpoint, supportsAnthropicCompaction, supportsAnthropicCompactionOnClient } from "./anthropic-compaction.js";
@@ -209,7 +210,7 @@ export type AnthropicSystemBlock = {
209
210
  type SystemBlockOptions = {
210
211
  includeClaudeCodeInstruction?: boolean;
211
212
  extraInstructions?: string[];
212
- /** Text of the first user message — used as fingerprint seed for the billing header. */
213
+ /** Text of the first conversational wire user turn, used as the billing fingerprint seed. */
213
214
  firstUserMessageText?: string;
214
215
  /** Cache lifetime shared by the OAuth system breakpoint and later message breakpoints. */
215
216
  cacheControl?: AnthropicCacheControl;
@@ -234,6 +235,16 @@ export declare function budgetThinkingOutput(maxTokens: number | undefined, budg
234
235
  maxTokens: number;
235
236
  budgetTokens: number;
236
237
  };
238
+ /**
239
+ * The effort whose thinking allowance an on-demand compaction request over
240
+ * `messages` gets. It sends no effort change of its own, so once an earlier
241
+ * request recorded an effort, the one it left in force applies, even when
242
+ * `requested` differs or is off. When that request named none, the API's
243
+ * per-model default applies, which is not known here: the request gets the
244
+ * largest effort's allowance (`max_tokens` is a cap, not spend). Without a
245
+ * recorded effort the request sets its own.
246
+ */
247
+ export declare function resolveAnthropicCompactionEffort(model: Model<"anthropic-messages">, messages: readonly Message[], requested: Effort | undefined): Effort | undefined;
237
248
  /**
238
249
  * Whether the conversation has stopped being thinking-led, so routes with
239
250
  * `compat.stripThinkingHistory` drop the budget `thinking` config and replay
@@ -269,5 +280,7 @@ export declare function convertAnthropicMessages(messages: Message[], model: Mod
269
280
  dropAllThinking?: boolean;
270
281
  droppedThinkingBlocks?: ReadonlySet<string>;
271
282
  credentialId?: number;
283
+ /** On-demand compaction request: it ends inside the conversation, so it gets nothing live requests lack there. */
284
+ compactionRequest?: AnthropicCompactionRequest;
272
285
  }): AnthropicMessageParam[];
273
286
  export declare function normalizeAnthropicToolSchema(schema: unknown): unknown;
@@ -5,13 +5,16 @@
5
5
  * Sources, in priority order:
6
6
  * 1. `GOOGLE_APPLICATION_CREDENTIALS` env → file with `type: "service_account"` (RS256 JWT exchange)
7
7
  * or `type: "authorized_user"` (refresh-token exchange).
8
- * 2. `~/.config/gcloud/application_default_credentials.json` (user ADC, same authorized_user flow).
8
+ * 2. gcloud user ADC: `%APPDATA%\gcloud\application_default_credentials.json` on Windows,
9
+ * `~/.config/gcloud/application_default_credentials.json` elsewhere (authorized_user flow).
9
10
  * 3. GCE / Cloud Run metadata server (`metadata.google.internal`).
10
11
  *
11
12
  * Tokens are cached per source key and refreshed `GOOGLE_VERTEX_REFRESH_SKEW_MS` before expiry
12
13
  * (default 60s). Concurrent callers waiting on a refresh share the same in-flight promise.
13
14
  */
14
15
  import type { FetchImpl } from "../types.js";
16
+ /** gcloud's user ADC file: `%APPDATA%\gcloud\…` on Windows, `~/.config/gcloud/…` elsewhere. */
17
+ export declare function userAdcPath(platform?: typeof process.platform, appData?: string | undefined): string;
15
18
  /**
16
19
  * Returns a Bearer access token suitable for the `Authorization` header on Vertex AI calls.
17
20
  * The token is cached in module scope and refreshed `GOOGLE_VERTEX_REFRESH_SKEW_MS` ms before it expires.
@@ -41,7 +41,7 @@
41
41
  * // Inspect calls afterwards.
42
42
  * expect(mock.calls).toHaveLength(2);
43
43
  */
44
- import type { AnthropicFallbackCreditHandle, Api, Context, Model, SimpleStreamOptions, StopDetails, StopReason, Usage } from "../types.js";
44
+ import type { AnthropicFallbackCreditHandle, Api, Context, Model, ServiceTier, SimpleStreamOptions, StopDetails, StopReason, Usage } from "../types.js";
45
45
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
46
46
  /** The API string this provider serves. */
47
47
  export declare const MOCK_API: "mock";
@@ -78,6 +78,8 @@ export interface MockResponse {
78
78
  usage?: Partial<Omit<Usage, "cost">> & {
79
79
  cost?: Partial<Usage["cost"]>;
80
80
  };
81
+ /** Service tier the mock reports serving the turn, as a real provider's response echo would. */
82
+ serviceTier?: ServiceTier;
81
83
  /** Pre-set responseId. */
82
84
  responseId?: string;
83
85
  /** If set, the stream emits a terminal error event instead of completing. */
@@ -906,60 +906,6 @@ export declare namespace Response {
906
906
  */
907
907
  type: "error";
908
908
  }
909
- /**
910
- * A moderation result produced for the response input or output.
911
- */
912
- interface ModerationResult {
913
- /**
914
- * A dictionary of moderation categories to booleans, True if the input is flagged
915
- * under this category.
916
- */
917
- categories: {
918
- [key: string]: boolean;
919
- };
920
- /**
921
- * Which modalities of input are reflected by the score for each category.
922
- */
923
- category_applied_input_types: {
924
- [key: string]: Array<"text" | "image">;
925
- };
926
- /**
927
- * A dictionary of moderation categories to scores.
928
- */
929
- category_scores: {
930
- [key: string]: number;
931
- };
932
- /**
933
- * A boolean indicating whether the content was flagged by any category.
934
- */
935
- flagged: boolean;
936
- /**
937
- * The moderation model that produced this result.
938
- */
939
- model: string;
940
- /**
941
- * The object type, which was always `moderation_result` for successful moderation
942
- * results.
943
- */
944
- type: "moderation_result";
945
- }
946
- /**
947
- * An error produced while attempting moderation for the response input or output.
948
- */
949
- interface Error {
950
- /**
951
- * The error code.
952
- */
953
- code: string;
954
- /**
955
- * The error message.
956
- */
957
- message: string;
958
- /**
959
- * The object type, which was always `error` for moderation failures.
960
- */
961
- type: "error";
962
- }
963
909
  }
964
910
  }
965
911
  /**
@@ -16,9 +16,9 @@ export interface OpenAIResponsesOptions extends StreamOptions {
16
16
  /**
17
17
  * Stateful turns: chain via `previous_response_id` + delta input instead of
18
18
  * replaying the full transcript. Forces `store: true` (the platform only
19
- * resolves stored responses). Defaults ON against the official OpenAI API
20
- * and OFF for other Responses endpoints; `PI_OPENAI_STATEFUL` overrides the
21
- * default, and `false` here vetoes everything. Requires `sessionId` +
19
+ * resolves stored responses). Precedence: this option, then
20
+ * `PI_OPENAI_STATEFUL`, then `compat.statefulResponses`, then ON against the
21
+ * official OpenAI API and OFF elsewhere. Requires `sessionId` +
22
22
  * `providerSessionState`. Falls back to a full replay whenever history
23
23
  * mutates or the server reports a stale id.
24
24
  */
@@ -60,6 +60,27 @@ export interface OpenAIResponsesOptions extends StreamOptions {
60
60
  /** Opt-in GPT-5.6+ prompt-cache policy. Unsupported explicit mode fails locally. */
61
61
  promptCache?: OpenAIPromptCacheOptions;
62
62
  }
63
+ /**
64
+ * Poll a stored Responses result until it reaches an adoptable terminal state.
65
+ * 404s, 5xx, and transport failures mean "not yet" (an in-flight run only
66
+ * becomes retrievable once the server finishes it); any other status, a
67
+ * mismatched id, or a failed/cancelled run ends the poll immediately. The
68
+ * whole recovery is bounded by one wall-clock deadline: each request's timeout
69
+ * is clamped to the time left, and no sleep may cross the deadline, so a
70
+ * stalled host cannot hold an already-failed turn past it. Returns the
71
+ * terminal object, or undefined on give-up/abort.
72
+ *
73
+ * @internal Exported for tests.
74
+ */
75
+ export declare function pollOpenAIResponsesResultForCompletion(args: {
76
+ fetchImpl: NonNullable<StreamOptions["fetch"]>;
77
+ url: string;
78
+ headers: Record<string, string>;
79
+ responseId: string;
80
+ signal?: AbortSignal;
81
+ /** Sleep between polls; defaults to a real wait honoring `signal`. */
82
+ wait?: (ms: number, signal?: AbortSignal) => Promise<void>;
83
+ }): Promise<Record<string, unknown> | undefined>;
63
84
  interface OpenAIResponsesProviderSessionState extends ProviderSessionState, OpenAIStrictToolsState, OpenAIReasoningEffortFallbackState {
64
85
  nativeHistoryReplayWarmed: boolean;
65
86
  /** Stateful `previous_response_id` chain baselines, keyed by baseUrl/model/session. */
@@ -85,6 +106,14 @@ interface OpenAIResponsesChainState {
85
106
  /** Set once chaining is judged unsupported for this session (circuit breaker). */
86
107
  disabled: boolean;
87
108
  }
109
+ /**
110
+ * Set the process-wide storage default per provider id, used when a request
111
+ * leaves `storeResponses` unset. Hosts call this from their settings so every
112
+ * request follows the setting, including direct `streamSimple`/`completeSimple`
113
+ * side calls that do not pass per-request options. `PI_MUSE_STORE_RESPONSES`
114
+ * still overrides it.
115
+ */
116
+ export declare function configureProviderStoreResponses(byProvider: Readonly<Record<string, boolean>> | undefined): void;
88
117
  /**
89
118
  * Drop the account-bound half of every retained `openai-responses` record in
90
119
  * `states`: the stateful `previous_response_id` chain baselines.
@@ -76,8 +76,11 @@ export declare function applyOpenAIServiceTier(params: {
76
76
  * resolved request tier. Scoped to `provider: "openai"` (the only standard
77
77
  * Responses biller) so an echoed `service_tier` from an Azure/OpenRouter/Copilot
78
78
  * proxy can never skew those costs.
79
+ *
80
+ * Returns the tier the turn ran on for the caller to record on the message, and
81
+ * counts the premium request the tier bills.
79
82
  */
80
- export declare function applyOpenAIResponsesServiceTierCost(model: Pick<Model, "provider" | "serviceTierCost">, usage: AssistantMessage["usage"], responseServiceTier: unknown, requestServiceTier: ServiceTier | null | undefined): void;
83
+ export declare function applyOpenAIResponsesServiceTierCost(model: Pick<Model, "provider" | "serviceTierCost" | "api" | "identity">, usage: AssistantMessage["usage"], responseServiceTier: unknown, requestServiceTier: ServiceTier | null | undefined): ServiceTier | undefined;
81
84
  /**
82
85
  * Reconcile token-price estimates with a gateway's authoritative account charge.
83
86
  * BYOK turns (`is_byok: true`) price from the provider spend in
@@ -1,3 +1,4 @@
1
+ import type { FetchImpl } from "../../types.js";
1
2
  import type { OAuthController, OAuthCredentials } from "./types.js";
2
3
  export interface CursorAuthParams {
3
4
  verifier: string;
@@ -13,6 +14,13 @@ export declare function pollCursorAuth(uuid: string, verifier: string): Promise<
13
14
  export declare function loginCursor(onAuthUrl: (url: string) => void, onPollStart?: () => void): Promise<OAuthCredentials>;
14
15
  export declare function loginCursorHook(callbacks: OAuthController): Promise<OAuthCredentials>;
15
16
  export declare function refreshCursorToken(apiKeyOrRefreshToken: string): Promise<OAuthCredentials>;
16
- export declare function refreshCursorHook(credentials: OAuthCredentials): Promise<OAuthCredentials>;
17
+ export declare function refreshCursorHook(credentials: OAuthCredentials, signal?: AbortSignal): Promise<OAuthCredentials>;
18
+ /** Request headers that present `accessToken` to cursor.com as its user's web session. */
19
+ export declare function cursorSessionHeaders(userId: string, accessToken: string): Record<string, string>;
20
+ /**
21
+ * Email of the Cursor account that owns `accessToken`, read from its cursor.com
22
+ * profile. Undefined when the token names no user or the profile names another.
23
+ */
24
+ export declare function fetchCursorAccountEmail(accessToken: string, fetchImpl?: FetchImpl, signal?: AbortSignal): Promise<string | undefined>;
17
25
  export declare function extractCursorAccessTokenUserId(accessToken: string): string | undefined;
18
26
  export declare function isCursorTokenExpiringSoon(token: string, thresholdSeconds?: number): boolean;
@@ -43,3 +43,5 @@ export declare function getOAuthApiKey(provider: OAuthProvider, credentials: Rec
43
43
  * Get list of OAuth providers.
44
44
  */
45
45
  export declare function getOAuthProviders(): OAuthProviderInfo[];
46
+ /** Provider id that stores `providerId`'s credentials: login aliases (`openai-codex-device`) map to their target (`openai-codex`). */
47
+ export declare function getOAuthCredentialProvider(providerId: string): string;
@@ -0,0 +1,3 @@
1
+ import type { OAuthController, OAuthCredentials } from "./types.js";
2
+ export declare function loginSnowflake(ctrl: OAuthController): Promise<OAuthCredentials>;
3
+ export declare function refreshSnowflakeToken(credentials: OAuthCredentials, signal?: AbortSignal): Promise<OAuthCredentials>;
@@ -0,0 +1,4 @@
1
+ import type { ProviderTransport } from "./build.js";
2
+ /** Normalize account identifiers and Snowsight links without accepting arbitrary credential-receiving hosts. */
3
+ export declare function normalizeSnowflakeAccountUrl(input: string): string;
4
+ export declare const snowflakeTransport: ProviderTransport;
@@ -157,18 +157,31 @@ export declare function shouldSendServiceTier(serviceTier: ServiceTier | null |
157
157
  */
158
158
  export declare function realizesPriorityServiceTier(serviceTier: ServiceTier | null | undefined, model: ServiceTierModel): boolean;
159
159
  /**
160
- * Premium-request weight contributed by a priority request to a provider that
161
- * realizes it and bills extra. Mirrors GitHub Copilot's `premiumRequests`
162
- * accounting so the "premium requests" stat aggregates priority traffic across
163
- * the OpenAI family, direct Anthropic fast mode, and Google priority.
160
+ * Premium-request weight contributed by a request a provider bills above
161
+ * standard. Priority (Fast mode) counts 1 on every provider that realizes it;
162
+ * `ultrafast` counts 1 on the OpenAI family, where it is a premium serving tier
163
+ * (its cost premium is recorded separately in `usage.cost`). Mirrors GitHub
164
+ * Copilot's `premiumRequests` accounting so the "premium requests" stat
165
+ * aggregates premium traffic across the OpenAI family, direct Anthropic fast
166
+ * mode, and Google priority.
164
167
  *
165
- * Returns 1 only when priority is actually realized on the wire for `model`
166
- * (see {@link realizesPriorityServiceTier}) and the provider bills it as a
167
- * premium request. OpenRouter is excluded — it bills per its own pricing, not
168
- * Copilot-premium semantics — as are Bedrock/Vertex Claude, where priority is
169
- * silently dropped.
168
+ * Returns 1 only when the tier is actually realized on the wire for `model`
169
+ * (see {@link realizesPriorityServiceTier} and {@link shouldSendServiceTier})
170
+ * and the provider bills it as a premium request. OpenRouter is excluded — it
171
+ * bills per its own pricing, not Copilot-premium semantics — as are
172
+ * Bedrock/Vertex Claude, where priority is silently dropped.
173
+ *
174
+ * Pass `served: true` when the tier is the one the provider reported serving
175
+ * (an assistant message's {@link AssistantMessage.serviceTier}) rather than a
176
+ * requested setting: realization is then already proven, so the wire gate is
177
+ * skipped and a model without discovery metadata (a stats-backfill row) still
178
+ * counts.
170
179
  */
171
- export declare function getPriorityPremiumRequests(serviceTier: ServiceTier | null | undefined, model: ServiceTierModel): number;
180
+ export declare function getPremiumServiceTierRequests(serviceTier: ServiceTier | null | undefined, model: ServiceTierModel, options?: {
181
+ served?: boolean;
182
+ }): number;
183
+ /** Parse a provider-reported `service_tier` echo into a known tier, or `undefined` for anything else. */
184
+ export declare function parseServiceTier(value: unknown): ServiceTier | undefined;
172
185
  /**
173
186
  * Coerce a persisted service-tier value to a {@link ServiceTierByFamily}. Newer
174
187
  * sessions store the family map directly; legacy sessions stored a single
@@ -215,6 +228,11 @@ export interface CodexCompactionRequestContext extends CodexCompactionMetadata {
215
228
  export interface AnthropicCompactionRequest {
216
229
  /** Custom summarization prompt; replaces the API default entirely when set. */
217
230
  instructions?: string;
231
+ /**
232
+ * Replayed summaries' file metadata due before this time (ms) ends the request;
233
+ * later metadata replays with the retained tail, which the new summary carries.
234
+ */
235
+ filesDueBefore?: number;
218
236
  }
219
237
  /** OpenAI's GPT-5.6+ explicit prompt-cache controls. */
220
238
  export interface OpenAIPromptCacheOptions {
@@ -333,6 +351,16 @@ export interface StreamOptions {
333
351
  * `false` so `previous_response_id` cannot explain a result.
334
352
  */
335
353
  statefulResponses?: boolean;
354
+ /**
355
+ * Store this request's result server-side on hosts that support it
356
+ * (`compat.storeResponses`, e.g. Muse Code), so a stream that drops
357
+ * mid-turn resumes from `GET /responses/{id}` instead of re-running the
358
+ * turn. Privacy: stored runs retain prompts and outputs on the provider.
359
+ * Unset falls back to `PI_MUSE_STORE_RESPONSES`, then the host's
360
+ * `configureProviderStoreResponses` default, else off. Ignored on hosts
361
+ * without the capability.
362
+ */
363
+ storeResponses?: boolean;
336
364
  /**
337
365
  * Disable native reasoning when the caller supplies an external scratchpad.
338
366
  * OpenAI Responses emits `reasoning: { effort: "none" }`; Anthropic and
@@ -363,6 +391,8 @@ export interface StreamOptions {
363
391
  * are not covered.
364
392
  */
365
393
  maxInFlightRequests?: Record<string, number>;
394
+ /** @internal Keep the in-flight permit until a provider's bounded terminal drain finishes. */
395
+ waitForTerminalDrain?: boolean;
366
396
  /**
367
397
  * Optional callback for inspecting or replacing provider payloads before sending.
368
398
  * Return undefined to keep the payload unchanged.
@@ -820,11 +850,31 @@ export interface AnthropicCompactionPayload {
820
850
  encryptedContent?: string;
821
851
  /**
822
852
  * Harness-appended file metadata (`<files>` section) kept out of the
823
- * byte-identical block. Replayed as a user message after the native block:
824
- * the converter replaces the summary message with the block and skips its
825
- * text, so without this the metadata would be invisible to this provider.
853
+ * byte-identical block. The converter replaces the summary message with the
854
+ * block and skips its text, so it replays this as a user message after the
855
+ * block's retained tail: before the first message created after the summary.
826
856
  */
827
857
  filesText?: string;
858
+ /**
859
+ * File metadata of earlier summaries whose replay position lies inside this
860
+ * summary's retained tail. Retained messages must reach the API unchanged,
861
+ * so each keeps replaying where it did: before the first message created
862
+ * after `after` (the earlier summary's commit time).
863
+ */
864
+ retainedFiles?: AnthropicCompactionFiles[];
865
+ /**
866
+ * Set on summaries whose retained tail replays unchanged: file metadata
867
+ * after the tail, earlier metadata at `retainedFiles`. Summaries persisted
868
+ * without it keep their original layout (metadata after the first retained
869
+ * turn), since later thinking was signed against those bytes.
870
+ */
871
+ exactTail?: true;
872
+ }
873
+ /** File metadata replayed at a fixed point of a natively compacted conversation. */
874
+ export interface AnthropicCompactionFiles {
875
+ text: string;
876
+ /** Replays before the first message created after this time (ms). */
877
+ after: number;
828
878
  }
829
879
  export type ProviderPayload = OpenAIResponsesHistoryPayload | AnthropicMessagePayload | AnthropicCompactionPayload;
830
880
  /** Provider-reported rewrite applied to request content before inference. */
@@ -928,6 +978,15 @@ export interface AssistantMessage {
928
978
  * other than what was requested.
929
979
  */
930
980
  upstreamModel?: string;
981
+ /**
982
+ * Service tier the provider reported serving this turn, when the API echoes
983
+ * one (`response.service_tier`), falling back to the tier the request carried
984
+ * when the response omits the echo. Absent when the provider reports no tier
985
+ * or the echo cannot be trusted (proxies). This is the tier the turn actually
986
+ * ran on, which is what cost, premium-request, and speed accounting key on —
987
+ * the session's live setting may already have changed.
988
+ */
989
+ serviceTier?: ServiceTier;
931
990
  usage: Usage;
932
991
  stopReason: StopReason;
933
992
  stopDetails?: StopDetails | null;
@@ -138,6 +138,12 @@ export interface TerminalGraceIteratorOptions {
138
138
  * the transport until that pending read settles.
139
139
  */
140
140
  onGraceEnd?: () => void;
141
+ /**
142
+ * Wait for the bounded drain when the consumer returns early. Capped
143
+ * requests must not release their in-flight permit while the transport
144
+ * still owns a live connection.
145
+ */
146
+ awaitDrainOnReturn?: boolean;
141
147
  }
142
148
  /**
143
149
  * Yields items from an async iterable until the consumer marks the stream
@@ -148,5 +154,10 @@ export interface TerminalGraceIteratorOptions {
148
154
  * hangs on `iterator.next()` until the idle watchdog converts an
149
155
  * already-successful turn into a timeout error. Grace expiry is a clean end
150
156
  * of iteration, never an error.
157
+ *
158
+ * A consumer that stops early (`break`) after `finishedAtMs()` is set leaves
159
+ * the source draining until `[DONE]`/EOF or the same grace deadline. Without
160
+ * `awaitDrainOnReturn`, the consumer is not held up; capped requests await
161
+ * the drain before completing and releasing their in-flight permit.
151
162
  */
152
163
  export declare function iterateWithTerminalGrace<T>(iterable: AsyncIterable<T>, options: TerminalGraceIteratorOptions): AsyncGenerator<T>;