@linxiraos/pi-ai 1.1.27 → 1.2.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 (195) hide show
  1. package/CHANGELOG.md +2 -24
  2. package/README.md +1 -1
  3. package/dist/types/auth/affinity.d.ts +33 -6
  4. package/dist/types/auth/cascade.d.ts +25 -3
  5. package/dist/types/auth/oauth.d.ts +9 -6
  6. package/dist/types/auth/policy.d.ts +1 -0
  7. package/dist/types/auth/refresh.d.ts +1 -2
  8. package/dist/types/auth/select.d.ts +6 -4
  9. package/dist/types/auth/sqlite-credential-store.d.ts +21 -1
  10. package/dist/types/auth/store.d.ts +8 -2
  11. package/dist/types/auth/types.d.ts +87 -14
  12. package/dist/types/auth-broker/client.d.ts +3 -3
  13. package/dist/types/auth-broker/discover.d.ts +1 -1
  14. package/dist/types/auth-broker/remote-store.d.ts +6 -4
  15. package/dist/types/auth-broker/server.d.ts +2 -0
  16. package/dist/types/auth-broker/snapshot-cache.d.ts +12 -0
  17. package/dist/types/auth-gateway/dispatch.d.ts +7 -3
  18. package/dist/types/auth-gateway/http.d.ts +0 -2
  19. package/dist/types/auth-gateway/server.d.ts +3 -3
  20. package/dist/types/auth-gateway/session-state.d.ts +6 -0
  21. package/dist/types/auth-storage.d.ts +7 -6
  22. package/dist/types/dialect/terminator-wait.d.ts +19 -0
  23. package/dist/types/env-api-key.d.ts +21 -0
  24. package/dist/types/error/auth.d.ts +10 -0
  25. package/dist/types/error/flags.d.ts +23 -0
  26. package/dist/types/images/index.d.ts +2 -3
  27. package/dist/types/images/shared.d.ts +6 -0
  28. package/dist/types/index.d.ts +1 -0
  29. package/dist/types/judgment/index.d.ts +2 -1
  30. package/dist/types/judgment/native.d.ts +80 -0
  31. package/dist/types/judgment/openai-decisions.d.ts +3 -0
  32. package/dist/types/judgment/question-file.d.ts +52 -0
  33. package/dist/types/judgment/systemone.d.ts +7 -0
  34. package/dist/types/judgment/types.d.ts +2 -2
  35. package/dist/types/judgment/wire.d.ts +36 -0
  36. package/dist/types/provider-details.d.ts +1 -1
  37. package/dist/types/provider-session-state.d.ts +2 -2
  38. package/dist/types/providers/anthropic-client.d.ts +10 -0
  39. package/dist/types/providers/anthropic-state.d.ts +2 -0
  40. package/dist/types/providers/anthropic.d.ts +23 -1
  41. package/dist/types/providers/connect-frame.d.ts +44 -0
  42. package/dist/types/providers/cursor/interaction-query.d.ts +0 -2
  43. package/dist/types/providers/cursor.d.ts +8 -8
  44. package/dist/types/providers/google-auth.d.ts +4 -1
  45. package/dist/types/providers/google-shared.d.ts +24 -4
  46. package/dist/types/providers/inference-headers.d.ts +6 -0
  47. package/dist/types/providers/mock.d.ts +3 -1
  48. package/dist/types/providers/openai-codex/request-transformer.d.ts +2 -0
  49. package/dist/types/providers/openai-codex-responses.d.ts +1 -1
  50. package/dist/types/providers/openai-configuration-update.d.ts +20 -11
  51. package/dist/types/providers/openai-responses-wire.d.ts +1 -55
  52. package/dist/types/providers/openai-responses.d.ts +8 -0
  53. package/dist/types/providers/openai-shared.d.ts +16 -4
  54. package/dist/types/providers/pi-native-client.d.ts +1 -1
  55. package/dist/types/providers/transform-messages.d.ts +14 -0
  56. package/dist/types/providers/vision-guard.d.ts +1 -1
  57. package/dist/types/registry/oauth/cloudflare-workers-ai.d.ts +3 -0
  58. package/dist/types/registry/oauth/cursor.d.ts +10 -2
  59. package/dist/types/registry/oauth/index.d.ts +2 -0
  60. package/dist/types/stream.d.ts +0 -21
  61. package/dist/types/transcription/types.d.ts +2 -1
  62. package/dist/types/types.d.ts +76 -11
  63. package/dist/types/usage.d.ts +2 -0
  64. package/dist/types/utils/anthropic-auth.d.ts +21 -7
  65. package/dist/types/utils/aws-profile.d.ts +5 -0
  66. package/dist/types/utils/block-symbols.d.ts +14 -0
  67. package/dist/types/utils/glyph-codec.d.ts +1 -1
  68. package/dist/types/utils/leaked-thinking-stream.d.ts +1 -1
  69. package/dist/types/utils/resolve-peer.d.ts +2 -0
  70. package/dist/types/utils/retry-wait.d.ts +9 -0
  71. package/dist/types/utils/schema/normalize.d.ts +8 -3
  72. package/package.json +6 -6
  73. package/src/auth/affinity.ts +130 -37
  74. package/src/auth/blocks.ts +90 -14
  75. package/src/auth/cascade.ts +68 -9
  76. package/src/auth/health.ts +2 -2
  77. package/src/auth/oauth.ts +25 -10
  78. package/src/auth/policy.ts +10 -1
  79. package/src/auth/pool.ts +0 -5
  80. package/src/auth/refresh.ts +17 -12
  81. package/src/auth/resets.ts +1 -1
  82. package/src/auth/rotation.ts +8 -2
  83. package/src/auth/select.ts +99 -29
  84. package/src/auth/sqlite-credential-store.ts +119 -49
  85. package/src/auth/store.ts +8 -2
  86. package/src/auth/types.ts +96 -15
  87. package/src/auth/usage-cache.ts +16 -5
  88. package/src/auth/usage.ts +2 -2
  89. package/src/auth-broker/client.ts +7 -4
  90. package/src/auth-broker/discover.ts +6 -5
  91. package/src/auth-broker/remote-store.ts +200 -62
  92. package/src/auth-broker/server.ts +316 -130
  93. package/src/auth-broker/snapshot-cache.ts +142 -2
  94. package/src/auth-gateway/dispatch.ts +9 -5
  95. package/src/auth-gateway/http.ts +0 -8
  96. package/src/auth-gateway/routes/systemone.ts +16 -12
  97. package/src/auth-gateway/server.ts +34 -19
  98. package/src/auth-gateway/session-state.ts +51 -9
  99. package/src/auth-storage.ts +23 -9
  100. package/src/dialect/deepseek.ts +10 -2
  101. package/src/dialect/gemini.ts +8 -2
  102. package/src/dialect/hermes.ts +8 -2
  103. package/src/dialect/kimi.ts +8 -2
  104. package/src/dialect/qwen-xml.ts +8 -2
  105. package/src/dialect/qwen3.ts +8 -2
  106. package/src/dialect/terminator-wait.ts +42 -0
  107. package/src/env-api-key.ts +78 -0
  108. package/src/error/auth.ts +20 -0
  109. package/src/error/flags.ts +93 -0
  110. package/src/error/rate-limit.ts +8 -6
  111. package/src/images/index.ts +3 -17
  112. package/src/images/openai-hosted.ts +13 -4
  113. package/src/images/openai-images.ts +19 -13
  114. package/src/images/shared.ts +10 -2
  115. package/src/index.ts +1 -0
  116. package/src/judgment/index.ts +2 -1
  117. package/src/judgment/native.ts +333 -0
  118. package/src/judgment/openai-decisions-predicate.md +9 -0
  119. package/src/judgment/openai-decisions.ts +129 -0
  120. package/src/judgment/question-file.ts +90 -0
  121. package/src/judgment/systemone.ts +65 -0
  122. package/src/judgment/text.ts +9 -1
  123. package/src/judgment/types.ts +2 -2
  124. package/src/judgment/wire.ts +42 -0
  125. package/src/provider-details.ts +1 -1
  126. package/src/provider-session-state.ts +2 -2
  127. package/src/providers/amazon-bedrock.ts +19 -8
  128. package/src/providers/anthropic-client.ts +16 -1
  129. package/src/providers/anthropic-state.ts +6 -0
  130. package/src/providers/anthropic.ts +857 -202
  131. package/src/providers/apple-foundation-models.ts +10 -4
  132. package/src/providers/aws-credentials.ts +2 -2
  133. package/src/providers/azure-openai-responses.ts +1 -1
  134. package/src/providers/connect-frame.ts +107 -0
  135. package/src/providers/cursor/interaction-query.ts +1 -9
  136. package/src/providers/cursor.ts +510 -101
  137. package/src/providers/devin.ts +65 -57
  138. package/src/providers/factory-droid/gemini.ts +38 -51
  139. package/src/providers/gitlab-duo-workflow.ts +5 -2
  140. package/src/providers/google-auth.ts +10 -3
  141. package/src/providers/google-shared.ts +134 -105
  142. package/src/providers/google-vertex.ts +0 -1
  143. package/src/providers/google.ts +1 -1
  144. package/src/providers/inference-headers.ts +19 -0
  145. package/src/providers/mock.ts +4 -0
  146. package/src/providers/ollama.ts +1 -1
  147. package/src/providers/openai-codex/request-transformer.ts +16 -11
  148. package/src/providers/openai-codex-responses.ts +143 -70
  149. package/src/providers/openai-completions.ts +9 -2
  150. package/src/providers/openai-configuration-update.ts +54 -15
  151. package/src/providers/openai-responses-wire.ts +1 -55
  152. package/src/providers/openai-responses.ts +90 -29
  153. package/src/providers/openai-shared.ts +154 -43
  154. package/src/providers/pi-native-client.ts +2 -2
  155. package/src/providers/systemone-server.ts +1 -1
  156. package/src/providers/transcriptions-server.ts +1 -1
  157. package/src/providers/transform-messages.ts +238 -82
  158. package/src/providers/vision-guard.ts +1 -1
  159. package/src/registry/aws.ts +51 -14
  160. package/src/registry/cloudflare-ai-gateway.ts +26 -3
  161. package/src/registry/hooks/api-key.ts +1 -0
  162. package/src/registry/hooks/env.ts +2 -5
  163. package/src/registry/oauth/callback-server.ts +6 -1
  164. package/src/registry/oauth/cloudflare-ai-gateway.ts +2 -2
  165. package/src/registry/oauth/cloudflare-workers-ai.ts +32 -0
  166. package/src/registry/oauth/cursor.ts +80 -17
  167. package/src/registry/oauth/index.ts +5 -0
  168. package/src/registry/oauth/snowflake.ts +1 -1
  169. package/src/stream.ts +5 -72
  170. package/src/transcription/types.ts +2 -1
  171. package/src/types.ts +101 -12
  172. package/src/usage/claude.ts +16 -12
  173. package/src/usage/commandcode.ts +1 -1
  174. package/src/usage/cursor.ts +14 -25
  175. package/src/usage/github-copilot.ts +1 -1
  176. package/src/usage/kimi.ts +1 -1
  177. package/src/usage/minimax-code.ts +8 -1
  178. package/src/usage.ts +8 -0
  179. package/src/utils/anthropic-auth.ts +34 -10
  180. package/src/utils/aws-profile.ts +10 -4
  181. package/src/utils/block-symbols.ts +17 -0
  182. package/src/utils/empty-completion-retry.ts +2 -3
  183. package/src/utils/glyph-codec.ts +200 -7
  184. package/src/utils/leaked-thinking-stream.ts +47 -25
  185. package/src/utils/resolve-peer.ts +7 -0
  186. package/src/utils/retry-wait.ts +19 -0
  187. package/src/utils/schema/CONSTRAINTS.md +9 -7
  188. package/src/utils/schema/fields.ts +20 -0
  189. package/src/utils/schema/normalize.ts +65 -23
  190. package/src/utils/schema/wire.ts +3 -3
  191. package/src/utils/thinking-loop.ts +18 -3
  192. package/src/utils/transport-fetch.ts +1 -1
  193. package/src/utils.ts +22 -6
  194. package/dist/types/judgment/typesafe.d.ts +0 -49
  195. package/src/judgment/typesafe.ts +0 -183
package/CHANGELOG.md CHANGED
@@ -1,30 +1,8 @@
1
- # Changelog
2
-
3
1
  ## [Unreleased]
4
2
 
5
- ## [1.1.27] - 2026-10-07
6
-
7
- - 版本线推进至 1.1.27;本版无独立用户可见变化。
8
-
9
- ### Changed
10
-
11
- - 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)).
12
- - 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))
13
-
14
- ### Fixed
15
-
16
- - 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)).
17
- - 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))
18
- - 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))
19
- - 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))
20
- - 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)).
21
- - 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))
22
- - 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)).
23
- - 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)).
24
-
25
- ## [1.1.25] - 2026-10-03
3
+ ## [1.2.0] - 2026-10-12
26
4
 
27
- - 版本线推进至 1.1.25;本版无独立用户可见变化。
5
+ - 版本线推进至 1.2.0;本版无独立用户可见变化。
28
6
 
29
7
  ## [1.1.24] - 2026-10-03
30
8
 
package/README.md CHANGED
@@ -1043,7 +1043,7 @@ The default inference role must hold either `SNOWFLAKE.CORTEX_USER` or the narro
1043
1043
 
1044
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
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.
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. `zetacode models snowflake --json` lists available models only after credentials are configured.
1047
1047
 
1048
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
1049
 
@@ -1,4 +1,4 @@
1
- import type { AuthCredential, OAuthCredential, SessionsApi } from "./types.js";
1
+ import type { AuthCredential, OAuthCredential, SessionRestrictionLease, SessionsApi } from "./types.js";
2
2
  import type { AuthCredentialStore } from "./store.js";
3
3
  import type { CredentialPool } from "./pool.js";
4
4
  import type { KeyOverrides } from "./cascade.js";
@@ -13,10 +13,31 @@ export type SessionCredential = {
13
13
  /** Set only by the public user-facing pin API; automatic warm affinity leaves it absent. */
14
14
  explicit?: true;
15
15
  };
16
+ /** One installed restriction: the allowed OAuth identity keys and the lease that lifts it. */
17
+ type SessionRestriction = {
18
+ readonly allowed: ReadonlySet<string>;
19
+ readonly lease: SessionRestrictionLease;
20
+ };
21
+ /**
22
+ * Provider → session id → installed restriction. Owned by `AuthStorage` so
23
+ * restrictions outlive store replacement, which rebuilds every store-bound
24
+ * module (pins included) around the same identities.
25
+ */
26
+ export type SessionRestrictions = Map<string, Map<string, SessionRestriction>>;
16
27
  /** Session → credential affinity (pins), persisted in the store cache. */
17
28
  export declare class SessionAffinity implements SessionsApi {
18
29
  #private;
19
- constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides);
30
+ constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides, restrictions: SessionRestrictions);
31
+ restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
32
+ unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
33
+ /** True when {@link restrict} limits `sessionId` for `provider`. */
34
+ isRestricted(provider: string, sessionId: string | undefined): boolean;
35
+ /**
36
+ * True when `credential` may serve `sessionId`: always for an unrestricted
37
+ * session, otherwise only an OAuth credential whose identity key is allowed.
38
+ */
39
+ allows(provider: string, sessionId: string | undefined, credential: AuthCredential): boolean;
40
+ permits(provider: string, sessionId: string, credentialId: number): boolean;
20
41
  /** Drop every pin for a provider, both in memory and in the persisted cache. */
21
42
  clearProvider(provider: string): void;
22
43
  /** Drop only this in-memory session pin after OAuth selection falls through. */
@@ -33,7 +54,11 @@ export declare class SessionAffinity implements SessionsApi {
33
54
  * rewrites were pure database churn.
34
55
  */
35
56
  record(provider: string, sessionId: string | undefined, type: AuthCredential["type"], index: number, lastUsedAtMs?: number, explicit?: boolean): void;
36
- /** Retrieves the last credential used by a session. */
57
+ /**
58
+ * Retrieves the last credential used by a session. A restricted session
59
+ * never sees a pin outside its allowlist — inherited, restored, or recorded
60
+ * before the restriction — so it re-ranks inside the allowlist instead.
61
+ */
37
62
  get(provider: string, sessionId: string | undefined): SessionCredential | undefined;
38
63
  /** Clears the last credential used by a session for a provider. */
39
64
  clear(provider: string, sessionId: string | undefined): void;
@@ -55,14 +80,15 @@ export declare class SessionAffinity implements SessionsApi {
55
80
  restoredAtMs?: number;
56
81
  }): boolean;
57
82
  /**
58
- * Copy every stored credential affinity from one live session to another.
83
+ * Copy stored credential affinities from one live session to another.
59
84
  *
60
85
  * The target receives its own sticky entries, so request resolution, usage
61
86
  * blocking, credential rotation, metadata, and persisted pins all continue
62
87
  * through the target session id without retaining a live dependency on the
63
- * source session.
88
+ * source session. `include` limits the copy to the providers it accepts,
89
+ * given whether the source's affinity is an explicit user pin.
64
90
  */
65
- inherit(sourceSessionId: string, targetSessionId: string): number;
91
+ inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
66
92
  /**
67
93
  * Release a session's sticky credential so its next `KeyCascade.get` call
68
94
  * re-runs native pool ranking. This never blocks or penalizes the released
@@ -71,3 +97,4 @@ export declare class SessionAffinity implements SessionsApi {
71
97
  */
72
98
  release(provider: string, sessionId: string): boolean;
73
99
  }
100
+ export {};
@@ -2,12 +2,20 @@ 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
- import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi, OAuthRequestIdentity } from "./types.js";
5
+ import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi } from "./types.js";
6
6
  /** Runtime (--api-key) and config (models.yml) key overrides plus the config-value resolver. */
7
7
  export declare class KeyOverrides {
8
8
  #private;
9
9
  constructor(resolver?: (config: string) => Promise<string | undefined>);
10
10
  has(provider: string): boolean;
11
+ /**
12
+ * Whether an override replaces OAuth for a session. Any override does for an
13
+ * unrestricted session. A restricted session skips the runtime key, but a
14
+ * config key still blocks OAuth: it marks the provider's endpoint (often a
15
+ * proxy) as taking that key, so the session fails closed instead of sending
16
+ * a pooled OAuth token there (see `KeyCascade.get`).
17
+ */
18
+ suppressesOAuth(provider: string, restricted: boolean): boolean;
11
19
  runtimeKey(provider: string): string | undefined;
12
20
  configKey(provider: string): string | undefined;
13
21
  /** Config value consulted only after stored OAuth/login credentials. */
@@ -121,15 +129,29 @@ export declare class KeyCascade implements KeysApi {
121
129
  * credential to reach the correct host.
122
130
  */
123
131
  peek(provider: string): Promise<string | undefined>;
124
- /** Resolve a bearer together with the stored row that supplied it. */
132
+ /**
133
+ * Resolve a bearer together with the stored row that supplied it, for a request.
134
+ * Same precedence as {@link KeyCascade.get}, but rejects with
135
+ * {@link AIError.OAuthRefreshUnavailableError} (transient, retryable) when OAuth
136
+ * refresh failed with a retryable error and no other permitted source could serve
137
+ * the request, so request paths retry instead of reporting a missing key.
138
+ */
125
139
  getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
126
140
  /**
127
141
  * Get API key for a provider.
128
142
  * Priority (first match wins): runtime override, config override, OAuth,
129
143
  * login API key, environment variable, another stored API key, then the
130
144
  * lowest-tier OMP compat mirror.
145
+ * login API key, environment variable, then another stored API key.
146
+ * A session restricted by `sessions.restrict` resolves only its allowed
147
+ * OAuth accounts and throws {@link AIError.MissingApiKeyError} when none
148
+ * can serve or a config key (a models.yml `apiKey`, often for a proxy
149
+ * `baseUrl`) owns the provider, rather than using any other credential.
150
+ * A transient OAuth refresh failure resolves `undefined`, so availability
151
+ * probes move on to their next candidate; request paths use
152
+ * {@link KeyCascade.getWithCredential}, which surfaces it.
131
153
  */
132
- get(provider: string, sessionId?: string, options?: AuthApiKeyOptions, onCredentialId?: (id: number, identity?: OAuthRequestIdentity) => void): Promise<string | undefined>;
154
+ get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
133
155
  /**
134
156
  * Build an {@link ApiKeyResolver} backed by this storage, implementing the
135
157
  * central a/b/c auth-retry policy:
@@ -37,9 +37,11 @@ export declare class OAuthAccounts implements OAuthApi {
37
37
  * `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
38
38
  * scenarios, prefer API-key resolution.
39
39
  *
40
- * Returns `undefined` when no OAuth credential is available, the
41
- * credential fails to refresh, or runtime/config overrides have replaced
42
- * OAuth with an explicit API key.
40
+ * Returns `undefined` when no usable OAuth credential is available
41
+ * (none stored, or every one definitively failed to refresh) or
42
+ * runtime/config overrides have replaced OAuth with an explicit API key.
43
+ * Rejects with {@link AIError.OAuthRefreshUnavailableError} (transient,
44
+ * retryable) when a retryable refresh failure left no usable credential.
43
45
  */
44
46
  access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
45
47
  /**
@@ -65,9 +67,10 @@ export declare class OAuthAccounts implements OAuthApi {
65
67
  * Resolve one stored OAuth credential by its durable storage row id.
66
68
  *
67
69
  * Unlike the normal session resolver, this method never ranks, rotates, or
68
- * falls back to sibling credentials. A forced refresh re-mints only the
69
- * requested row, preserving exact-account affinity for operations whose
70
- * provenance and policy boundary are tied to one workspace.
70
+ * falls back to sibling credentials. A generic forced refresh re-mints only the
71
+ * requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
72
+ * still-usable recent mint of that row. Either way exact-account affinity holds
73
+ * for operations whose provenance and policy boundary are tied to one workspace.
71
74
  *
72
75
  * Returns `undefined` when the row does not exist for `provider` or an
73
76
  * explicit runtime/config API-key override suppresses OAuth.
@@ -14,6 +14,7 @@ export declare class AccountPolicies {
14
14
  */
15
15
  replace(policies: AuthAccountPolicies, defaultReservePct: number | undefined, storedCredentials?: ReadonlyMap<string, readonly AuthCredential[]>): void;
16
16
  validateUsageCapability(provider: string, canFetchUsage: boolean): void;
17
+ /** Throw on ambiguous selectors for `provider`; a selector whose account is absent is inert and warns once. */
17
18
  validateFor(provider: string, credentials: readonly AuthCredential[]): void;
18
19
  /**
19
20
  * Return the configured account policy matching an OAuth identity.
@@ -1,7 +1,6 @@
1
1
  import type { OAuthCredentials } from "../registry/oauth/types.js";
2
2
  import type { Provider } from "../types.js";
3
3
  import { type CredentialPool } from "./pool.js";
4
- import type { AccountPolicies } from "./policy.js";
5
4
  import { type AuthCredentialStore } from "./store.js";
6
5
  import { type AuthCredentialSnapshotEntry, type AuthStorageOptions, type OAuthCredential, type OAuthRefreshByIdOptions, type OAuthRefreshReason, type StoredOAuthRefreshOptions, type StoredOAuthRefreshResult } from "./types.js";
7
6
  /**
@@ -30,8 +29,8 @@ export declare function mergeRefreshedCredential<T extends OAuthCredential>(curr
30
29
  export interface OAuthRefresherDeps {
31
30
  store: AuthCredentialStore;
32
31
  pool: CredentialPool;
33
- policies: AccountPolicies;
34
32
  override?: AuthStorageOptions["refreshOAuthCredential"];
33
+ overrideMints?: AuthStorageOptions["refreshOAuthCredentialMints"];
35
34
  }
36
35
  /** Single-flighted, lease-guarded OAuth refresh with compare-and-set persistence. */
37
36
  export declare class OAuthRefresher {
@@ -8,7 +8,7 @@ import { type CredentialPool } from "./pool.js";
8
8
  import { type ApiKeySelection, type OAuthSelection } from "./rank.js";
9
9
  import { type OAuthRefresher } from "./refresh.js";
10
10
  import type { AuthCredentialStore } from "./store.js";
11
- import type { ApiKeyCredential, AuthApiKeyOptions, AuthCredential, OAuthCredential } from "./types.js";
11
+ import { type ApiKeyCredential, type AuthApiKeyOptions, type AuthCredential, type OAuthCredential } from "./types.js";
12
12
  import type { UsageService } from "./usage.js";
13
13
  /** Temporary block after a transient OAuth refresh failure. */
14
14
  export declare const OAUTH_REFRESH_FAILURE_BACKOFF_MS: number;
@@ -32,6 +32,8 @@ export type TryOAuthOptions = {
32
32
  blockScopes?: readonly string[];
33
33
  /** When false, a definitive failure of THIS credential returns undefined instead of falling back to the ranked/round-robin selector (target-only resolution). */
34
34
  allowFallback?: boolean;
35
+ /** Receives a non-definitive refresh failure that left this credential unusable; the caller filters for retryable ones. */
36
+ onTransientRefreshFailure?: (error: unknown) => void;
35
37
  };
36
38
  /** Services consulted by CredentialSelector for policy, usage, blocks, refresh, and session affinity. */
37
39
  export interface CredentialSelectorDeps {
@@ -69,9 +71,9 @@ export declare class CredentialSelector {
69
71
  * 1. strict: unblocked credentials only, usage limits respected, plan
70
72
  * filter enforced (when any account is confirmed eligible);
71
73
  * 2. plan-fitting last resort: same plan filter, but blocked/exhausted
72
- * accounts are allowed (blocked candidates rank earliest-unblocking
73
- * first) so the caller gets real usage-limit semantics from the wire
74
- * instead of a missing key;
74
+ * accounts are allowed (an explicit session pin first, then blocked
75
+ * candidates earliest-unblocking first) so the caller gets real
76
+ * usage-limit semantics from the wire instead of a missing key;
75
77
  * 3. unfiltered last resort: the plan filter matched nothing usable —
76
78
  * skip it and try every account once; the server is the final arbiter
77
79
  * of model access.
@@ -7,7 +7,7 @@
7
7
  import type { Database } from "bun:sqlite";
8
8
  import { isSqliteBusyError, isSqliteCorruptionError } from "@linxiraos/pi-utils";
9
9
  import type { AuthCredentialStore, CredentialRefreshLeaseFence } from "./store.js";
10
- import type { AuthCredential, DisabledCredentialSummary, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
10
+ import type { AuthCredential, DisabledCredentialSummary, OAuthCredential, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
11
11
  import type { OAuthCredentials } from "../registry/oauth/types.js";
12
12
  import type { ClientUsageReport, ClientUsageSummary, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
13
13
  export declare const USAGE_REPORT_TTL_MS: number;
@@ -18,7 +18,26 @@ type SerializedCredentialRecord = {
18
18
  };
19
19
  export { isSqliteBusyError, isSqliteCorruptionError };
20
20
  export declare function serializeCredential(provider: string, credential: AuthCredential): SerializedCredentialRecord | null;
21
+ /**
22
+ * Identity key of a credential: the `identityKey` that broker snapshots carry
23
+ * and that broker account pools and `sessions.restrict` match, such as
24
+ * `email:<address>|org:<id>` for org-scoped providers. `null` for an API key or
25
+ * an OAuth credential without any account, email, project, or org identity.
26
+ */
21
27
  export declare function resolveCredentialIdentityKey(provider: string, credential: AuthCredential): string | null;
28
+ /**
29
+ * Whether upserting `incoming` replaces the active row holding `existing` (stored
30
+ * under `existingIdentityKey`) instead of inserting a new row beside it.
31
+ */
32
+ export declare function matchesReplacementCredential(provider: string, existing: AuthCredential | null, existingIdentityKey: string | null, incoming: AuthCredential): boolean;
33
+ /**
34
+ * Whether two OAuth credentials belong to the same account of the same organization,
35
+ * whichever identity key each is stored under. The email decides when both carry one;
36
+ * otherwise a matching account id does, unless it is the organization's own id (a
37
+ * ChatGPT workspace id shared by every member). Project ids are shared between users
38
+ * (every consumer Antigravity login has the same one) and never count.
39
+ */
40
+ export declare function isSameOAuthAccount(a: OAuthCredential, b: OAuthCredential): boolean;
22
41
  /**
23
42
  * Default SQLite-backed implementation of {@link AuthCredentialStore}.
24
43
  *
@@ -60,6 +79,7 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
60
79
  deleteCachePrefix(prefix: string): void;
61
80
  cleanExpiredCache(): void;
62
81
  getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
82
+ getCredentialBlockScopes(credentialId: number, providerKey: string): Map<string, number>;
63
83
  getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
64
84
  upsertCredentialBlock(block: StoredCredentialBlock): void;
65
85
  deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
@@ -71,6 +71,12 @@ export interface CredentialCacheStore {
71
71
  export interface CredentialBlockStore {
72
72
  /** Non-expired block for one (credential, providerKey, scope) key, or undefined. */
73
73
  getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
74
+ /**
75
+ * Non-expired blocks for one credential and providerKey, keyed by block
76
+ * scope (`""` is unscoped), in one read. Optional: selectors fall back to
77
+ * {@link CredentialBlockStore.getCredentialBlock} per scope when absent.
78
+ */
79
+ getCredentialBlockScopes?(credentialId: number, providerKey: string): ReadonlyMap<string, number>;
74
80
  /** Earliest time a shared-store block should be eligible for live-usage reconciliation. */
75
81
  getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
76
82
  /** Upsert with MAX semantics: keep the later blockedUntilMs on conflict. */
@@ -116,8 +122,8 @@ export interface UsageLedgerStore {
116
122
  }
117
123
  /** Broker-delegated OAuth refresh and usage-report operations. */
118
124
  export interface CredentialUpstream {
119
- /** Optional hook to notify the underlying store that usage report cache is stale. */
120
- invalidateUsageCache(signal?: AbortSignal): Promise<void>;
125
+ /** Optional hook to notify the underlying store that usage reports (one provider's, when given) are stale. */
126
+ invalidateUsageCache(provider?: string, signal?: AbortSignal): Promise<void>;
121
127
  /**
122
128
  * Optional store-supplied OAuth refresh. When present, `AuthStorage` uses
123
129
  * it before the per-provider local refresh path. `RemoteAuthCredentialStore`
@@ -316,6 +316,12 @@ export type AuthStorageOptions = {
316
316
  * the actual refresh token never leaves the broker.
317
317
  */
318
318
  refreshOAuthCredential?: (provider: Provider, credentialId: number, credential: OAuthCredential, signal?: AbortSignal, reason?: OAuthRefreshReason) => Promise<OAuthCredentials>;
319
+ /**
320
+ * Set when {@link refreshOAuthCredential} exchanges the refresh token with the
321
+ * provider itself instead of delegating (e.g. to a broker), so its tokens count
322
+ * as this process's mints for auth-recovery reuse.
323
+ */
324
+ refreshOAuthCredentialMints?: boolean;
319
325
  /**
320
326
  * Human-readable description of the credential store backing this
321
327
  * AuthStorage instance. Surfaced through {@link AuthStorage.keys.describe}
@@ -409,7 +415,7 @@ export interface ModelUsageHealthOptions {
409
415
  export type AuthApiKeyOptions = {
410
416
  baseUrl?: string;
411
417
  modelId?: string;
412
- /** Provider account ids known to serve `modelId` from multi-account discovery; OAuth selection prefers them and tries other accounts only as a last resort. */
418
+ /** {@link oauthAccountKey}s of accounts known to serve `modelId` from multi-account discovery; OAuth selection prefers them and tries other accounts only as a last resort. */
413
419
  accountIds?: readonly string[];
414
420
  /**
415
421
  * Caller's cancel signal. Threaded into any broker-bound OAuth refresh so
@@ -429,6 +435,19 @@ export type AuthApiKeyOptions = {
429
435
  /** When false, select as `sessionId` would without recording the choice as that session's sticky credential. */
430
436
  recordAffinity?: boolean;
431
437
  };
438
+ /**
439
+ * Identity that multi-account discovery records in `Model.accountAccess` and
440
+ * that {@link AuthApiKeyOptions.accountIds} selection matches: the provider
441
+ * account id (Codex `chatgpt_account_id`), else the login email for providers
442
+ * whose credentials carry no account id (Antigravity), else the project id for
443
+ * a login whose email lookup failed. Same precedence as the credential store's
444
+ * identity key.
445
+ */
446
+ export declare function oauthAccountKey(identity: {
447
+ accountId?: string;
448
+ email?: string;
449
+ projectId?: string;
450
+ }): string | undefined;
432
451
  /** Non-secret identity bound to the OAuth credential selected for one request attempt. */
433
452
  export interface OAuthRequestIdentity {
434
453
  orgId?: string;
@@ -814,9 +833,20 @@ export interface KeysApi {
814
833
  * 6. Stored API key (e.g. a broker-migrated copy) — last resort, so an explicit env var wins
815
834
  * 7. OMP compatibility mirror — below every stored credential, so a key the
816
835
  * user saves locally always wins over the upstream fallback
836
+ *
837
+ * Resolves `undefined` when no permitted credential can serve, including when
838
+ * OAuth refresh failed transiently (network, timeout, 5xx), so availability
839
+ * probes move on to their next candidate. Request paths use
840
+ * {@link KeysApi.getWithCredential}, which surfaces that failure.
817
841
  */
818
842
  get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
819
- /** Resolve a bearer together with its durable stored credential row id, when known. */
843
+ /**
844
+ * Resolve a bearer together with its durable stored credential row id, when known.
845
+ * Same precedence as {@link KeysApi.get}, but rejects with
846
+ * `OAuthRefreshUnavailableError` (transient, retryable) when OAuth refresh failed
847
+ * with a retryable error and no other permitted source could serve the request;
848
+ * the stored credential is kept.
849
+ */
820
850
  getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
821
851
  /**
822
852
  * Peek at API key for a provider without refreshing OAuth tokens.
@@ -913,7 +943,7 @@ export interface KeysApi {
913
943
  modelId?: string;
914
944
  }): ApiKeyResolver;
915
945
  }
916
- /** Controls whether a row-id refresh may reuse a token minted by this refresher. */
946
+ /** Controls whether a row-id refresh may reuse a recently minted token. */
917
947
  export interface OAuthRefreshByIdOptions {
918
948
  /**
919
949
  * Return the stored credential when it still holds a fresh access token this
@@ -921,6 +951,12 @@ export interface OAuthRefreshByIdOptions {
921
951
  * refresh tokens repeatedly when a provider rejects every valid bearer.
922
952
  */
923
953
  reuseRecentMint?: boolean;
954
+ /**
955
+ * Provider-401 recovery intent. Forwarded to a delegated refresh (an auth
956
+ * broker), which may then return a token it minted in the last five minutes.
957
+ * Local reuse still requires {@link reuseRecentMint}.
958
+ */
959
+ reason?: OAuthRefreshReason;
924
960
  }
925
961
  /** OAuth login, access, account identity, and refresh operations. */
926
962
  export interface OAuthApi {
@@ -942,9 +978,11 @@ export interface OAuthApi {
942
978
  * `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
943
979
  * scenarios, prefer {@link AuthStorage.keys.get}.
944
980
  *
945
- * Returns `undefined` when no OAuth credential is available, the
946
- * credential fails to refresh, or runtime/config overrides have replaced
947
- * OAuth with an explicit API key.
981
+ * Returns `undefined` when no usable OAuth credential is available (none
982
+ * stored, or every one definitively failed to refresh) or runtime/config
983
+ * overrides have replaced OAuth with an explicit API key. Rejects with
984
+ * `OAuthRefreshUnavailableError` (transient, retryable) when a retryable
985
+ * refresh failure left no usable credential.
948
986
  */
949
987
  access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
950
988
  /**
@@ -960,9 +998,10 @@ export interface OAuthApi {
960
998
  * Resolve one stored OAuth credential by its durable storage row id.
961
999
  *
962
1000
  * Unlike the normal session resolver, this method never ranks, rotates, or
963
- * falls back to sibling credentials. A forced refresh re-mints only the
964
- * requested row, preserving exact-account affinity for operations whose
965
- * provenance and policy boundary are tied to one workspace.
1001
+ * falls back to sibling credentials. A generic forced refresh re-mints only the
1002
+ * requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
1003
+ * still-usable recent mint of that row. Either way exact-account affinity holds
1004
+ * for operations whose provenance and policy boundary are tied to one workspace.
966
1005
  *
967
1006
  * Returns `undefined` when the row does not exist for `provider` or an
968
1007
  * explicit runtime/config API-key override suppresses OAuth.
@@ -996,8 +1035,9 @@ export interface OAuthApi {
996
1035
  * Refresh the OAuth credential with the given id through a per-credential
997
1036
  * single-flight. Concurrent callers for the same row await the same upstream
998
1037
  * refresh attempt, which is required for providers that rotate refresh tokens
999
- * on every successful refresh. Mints unconditionally unless
1000
- * {@link OAuthRefreshByIdOptions.reuseRecentMint} is set.
1038
+ * on every successful refresh. Mints unless {@link OAuthRefreshByIdOptions.reuseRecentMint}
1039
+ * finds a recent local mint, or a delegated refresh given
1040
+ * {@link OAuthRefreshByIdOptions.reason} `"auth-recovery"` returns its recent mint.
1001
1041
  */
1002
1042
  refresh(id: number, signal?: AbortSignal, options?: OAuthRefreshByIdOptions): Promise<AuthCredentialSnapshotEntry>;
1003
1043
  /**
@@ -1005,6 +1045,11 @@ export interface OAuthApi {
1005
1045
  */
1006
1046
  refreshStored<T extends OAuthCredential = OAuthCredential>(provider: string, options: StoredOAuthRefreshOptions<T>): Promise<StoredOAuthRefreshResult<T>>;
1007
1047
  }
1048
+ /**
1049
+ * Handle for one {@link SessionsApi.restrict} call. {@link SessionsApi.unrestrict}
1050
+ * lifts a restriction only with the lease of the call that installed it.
1051
+ */
1052
+ export type SessionRestrictionLease = symbol;
1008
1053
  /** Session credential affinity operations. */
1009
1054
  export interface SessionsApi {
1010
1055
  /**
@@ -1024,14 +1069,42 @@ export interface SessionsApi {
1024
1069
  restoredAtMs?: number;
1025
1070
  }): boolean;
1026
1071
  /**
1027
- * Copy every stored credential affinity from one live session to another.
1072
+ * Copy stored credential affinities from one live session to another.
1028
1073
  *
1029
1074
  * The target receives its own sticky entries, so request resolution, usage
1030
1075
  * blocking, credential rotation, metadata, and persisted pins all continue
1031
1076
  * through the target session id without retaining a live dependency on the
1032
- * source session.
1077
+ * source session. `include` limits the copy to the providers it accepts,
1078
+ * given whether the source's affinity is an explicit user pin.
1079
+ */
1080
+ inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
1081
+ /**
1082
+ * Restrict one session's credentials for `provider` to the OAuth accounts
1083
+ * whose identity key (`email:<address>|org:<id>` for org-scoped providers;
1084
+ * otherwise `account:`, `email:`, or `project:` — the keys broker account
1085
+ * pools use) is listed. Selection, session pins, inherited affinity,
1086
+ * fallback passes, and rotation never leave the list, and runtime, config,
1087
+ * environment, and stored API keys are not used for that session; when no
1088
+ * listed account can serve, key resolution fails instead of borrowing
1089
+ * another account. An empty list allows no account. Replaces any earlier
1090
+ * restriction for the same provider and session; {@link inherit} does not
1091
+ * copy restrictions. Restrictions are never evicted, because that would
1092
+ * widen a live session; the owner passes the returned lease to
1093
+ * {@link unrestrict} when the session ends.
1094
+ */
1095
+ restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
1096
+ /**
1097
+ * Lift the restriction that `lease` installed. Once a later {@link restrict}
1098
+ * call has replaced it for the same provider and session, this does nothing,
1099
+ * so a stale owner never lifts the restriction a newer owner relies on.
1100
+ */
1101
+ unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
1102
+ /**
1103
+ * Whether stored credential `credentialId` may serve `sessionId`: always for
1104
+ * an unrestricted session, otherwise only an OAuth account its
1105
+ * {@link restrict} list names.
1033
1106
  */
1034
- inherit(sourceSessionId: string, targetSessionId: string): number;
1107
+ permits(provider: string, sessionId: string, credentialId: number): boolean;
1035
1108
  /**
1036
1109
  * Release a session's sticky credential so its next {@link getApiKey} call
1037
1110
  * re-runs native pool ranking. This never blocks or penalizes the released
@@ -1,8 +1,8 @@
1
1
  /**
2
- * HTTP client for the omp auth-broker server.
2
+ * HTTP client for the zetacode auth-broker server.
3
3
  *
4
4
  * Used by {@link RemoteAuthCredentialStore} (snapshot pulls) and by
5
- * `omp auth-broker status` (liveness checks). All endpoints except
5
+ * `zetacode auth-broker status` (liveness checks). All endpoints except
6
6
  * `/v1/healthz` require a bearer token.
7
7
  */
8
8
  import type { AuthCredential, DisabledCredentialSummary, OAuthRefreshReason } from "../auth-storage.js";
@@ -85,7 +85,7 @@ export declare class AuthBrokerClient {
85
85
  fetchClientUsageSummary(query?: {
86
86
  sinceMs?: number;
87
87
  }, signal?: AbortSignal): Promise<ClientUsageSummaryResponse>;
88
- notifyUsageStale(signal?: AbortSignal): Promise<UsageStaleResponse>;
88
+ notifyUsageStale(provider?: string, signal?: AbortSignal): Promise<UsageStaleResponse>;
89
89
  refreshCredential(id: number, signal?: AbortSignal, reason?: OAuthRefreshReason): Promise<CredentialRefreshResponse>;
90
90
  disableCredential(id: number, cause: string, signal?: AbortSignal): Promise<CredentialDisableResponse>;
91
91
  /**
@@ -18,7 +18,7 @@ export interface DiscoverAuthStorageOptions {
18
18
  accountPolicies?: AuthAccountPolicies;
19
19
  authStorageOptions?: Omit<AuthStorageOptions, "accountPolicies" | "configValueResolver" | "sourceLabel">;
20
20
  }
21
- /** Path to the local bearer token file. Created by `omp auth-broker token`. */
21
+ /** Path to the local bearer token file. Created by `zetacode auth-broker token`. */
22
22
  export declare function getAuthBrokerTokenFilePath(): string;
23
23
  export interface AuthAccountPolicyConfig {
24
24
  accountPolicies: AuthAccountPolicies;
@@ -73,6 +73,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
73
73
  /** Broker-backed disabled tombstones; empty against brokers predating the endpoint. */
74
74
  listDisabledCredentials(provider?: string, signal?: AbortSignal): Promise<DisabledCredentialSummary[]>;
75
75
  getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
76
+ getCredentialBlockScopes(credentialId: number, providerKey: string): Map<string, number>;
76
77
  getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
77
78
  listCredentialBlocks(credentialIds: readonly number[]): StoredCredentialBlock[];
78
79
  upsertCredentialBlock(block: StoredCredentialBlock): void;
@@ -121,7 +122,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
121
122
  /** Drop all cache rows whose keys start with the supplied prefix. */
122
123
  deleteCachePrefix(prefix: string): void;
123
124
  cleanExpiredCache(): void;
124
- invalidateUsageCache(signal?: AbortSignal): Promise<void>;
125
+ invalidateUsageCache(provider?: string, signal?: AbortSignal): Promise<void>;
125
126
  /**
126
127
  * Store-level hook consumed by `AuthStorage` — routes refresh through the
127
128
  * broker so the actual refresh token never leaves the broker host. Returns
@@ -148,9 +149,10 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
148
149
  ingestUsageReport(provider: Provider, credential: OAuthCredential, report: UsageReport): boolean;
149
150
  /**
150
151
  * Fold locally observed request usage into the pending report and schedule
151
- * a flush. One `POST /v1/usage/observed` at most per flush interval; on
152
- * failure the batch is retained and retried with the next flush. A 404
153
- * (pre-endpoint broker) disables reporting for the life of this store.
152
+ * a flush. One `POST /v1/usage/observed` at most per flush interval, plus a
153
+ * final one when the process exits; on failure the batch is retained and
154
+ * retried with the next flush. A 404 (pre-endpoint broker) disables
155
+ * reporting for the life of this store.
154
156
  *
155
157
  * `client` overrides the reporting identity — the auth-gateway attributes
156
158
  * each request to the originating install/app instead of the gateway host.
@@ -17,6 +17,8 @@ export interface AuthBrokerServerOptions {
17
17
  bind?: string;
18
18
  /** Accept any of these bearer tokens. Empty disables auth (loopback only). */
19
19
  bearerTokens: string[];
20
+ /** Honor forwarded peer headers only when the connecting proxy is trusted. Default false. */
21
+ trustProxyHeaders?: boolean;
20
22
  /** Broker version string surfaced on `/v1/healthz`. */
21
23
  version?: string;
22
24
  /** Refresh credentials expiring within this window. Default 5 min. */
@@ -15,3 +15,15 @@ export interface WriteAuthBrokerSnapshotCacheOptions {
15
15
  }
16
16
  export declare function readAuthBrokerSnapshotCache(opts: ReadAuthBrokerSnapshotCacheOptions): Promise<SnapshotResponse | null>;
17
17
  export declare function writeAuthBrokerSnapshotCache(opts: WriteAuthBrokerSnapshotCacheOptions): Promise<void>;
18
+ /**
19
+ * Fire-and-forget cache write for snapshot callbacks. The first snapshot after
20
+ * a quiet period is written immediately; later ones within
21
+ * {@link WRITE_COALESCE_MS} collapse into a single trailing write of the
22
+ * newest. A snapshot whose generation and credential content match the last
23
+ * write is skipped (see {@link UNCHANGED_REWRITE_MS}). Pending writes are
24
+ * flushed on process shutdown and by {@link flushAuthBrokerSnapshotCacheWrites};
25
+ * failures are logged, never thrown.
26
+ */
27
+ export declare function scheduleAuthBrokerSnapshotCacheWrite(opts: WriteAuthBrokerSnapshotCacheOptions): void;
28
+ /** Write every pending scheduled snapshot now and wait for in-flight writes to settle. */
29
+ export declare function flushAuthBrokerSnapshotCacheWrites(): Promise<void>;