@oh-my-pi/pi-ai 17.2.14 → 17.3.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 (38) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/types/auth/sqlite-credential-store.d.ts +1 -3
  3. package/dist/types/auth-storage.d.ts +1 -11
  4. package/dist/types/providers/anthropic-client.d.ts +5 -5
  5. package/dist/types/providers/anthropic.d.ts +1 -4
  6. package/dist/types/providers/vision-guard.d.ts +5 -1
  7. package/dist/types/registry/amazon-bedrock.d.ts +1 -2
  8. package/dist/types/registry/aws.d.ts +3 -1
  9. package/dist/types/registry/registry.d.ts +1 -1
  10. package/dist/types/stream.d.ts +6 -0
  11. package/dist/types/types.d.ts +17 -6
  12. package/dist/types/usage/opencode-go.d.ts +12 -1
  13. package/dist/types/usage.d.ts +0 -19
  14. package/dist/types/utils/empty-completion-retry.d.ts +1 -0
  15. package/dist/types/utils/thinking-loop.d.ts +6 -17
  16. package/package.json +5 -5
  17. package/src/auth/sqlite-credential-store.ts +0 -56
  18. package/src/auth-storage.ts +44 -78
  19. package/src/providers/anthropic-client.ts +6 -10
  20. package/src/providers/anthropic.ts +107 -259
  21. package/src/providers/devin.ts +3 -3
  22. package/src/providers/google-antigravity-forced-tool.md +1 -0
  23. package/src/providers/google-gemini-cli.ts +8 -0
  24. package/src/providers/google-shared.ts +12 -7
  25. package/src/providers/ollama.ts +15 -3
  26. package/src/providers/openai-shared.ts +33 -14
  27. package/src/providers/vision-guard.ts +15 -3
  28. package/src/registry/amazon-bedrock.ts +1 -1
  29. package/src/registry/aws.ts +3 -2
  30. package/src/registry/oauth/perplexity.ts +32 -4
  31. package/src/registry/together.ts +7 -3
  32. package/src/stream.ts +440 -46
  33. package/src/types.ts +17 -6
  34. package/src/usage/opencode-go.ts +169 -58
  35. package/src/usage/xai-oauth.ts +49 -8
  36. package/src/usage.ts +0 -20
  37. package/src/utils/empty-completion-retry.ts +4 -1
  38. package/src/utils/thinking-loop.ts +17 -38
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.3.0] - 2026-08-13
6
+
7
+ ### Breaking Changes
8
+
9
+ - Renamed `withGeminiThinkingLoopGuard` to `withThinkingLoopGuard`; the guard applies to Gemini, DeepSeek, and Grok model-id families.
10
+
11
+ ### Changed
12
+
13
+ - Updated OpenCode Go integration to use the official usage endpoint, removing hardcoded caps, enabling real-time credential validation, and routing multi-key pools based on rolling and weekly headroom.
14
+ - Optimized Anthropic prompt caching with rolling 5-minute breakpoints and idle refreshes to keep the prompt prefix warm.
15
+
16
+ ### Fixed
17
+
18
+ - Fixed Ollama chat adapter to correctly forward sampling parameters like temperature and topP to the provider.
19
+ - Fixed OpenAI agent turns ending prematurely after a web search with no visible answer, ensuring the agent continues processing the search results.
20
+ - Fixed a resource leak where completed model streams retained provider concurrency permits longer than necessary.
21
+ - Fixed image input support for qwen3.8-max and newer models when using DashScope compatible-mode.
22
+ - Fixed xAI usage reporting falling back to a stale cache when a new weekly cycle starts with 0% consumed credits.
23
+ - Fixed Together AI login validation failures by querying the authenticated models list instead of a hardcoded model.
24
+ - Fixed credential-health probes and usage fetches failing when using reference-stored API keys (such as environment variables or commands) by ensuring secrets are correctly resolved.
25
+ - Fixed Perplexity email-OTP login by preserving the session cookies required for verification.
26
+ - Fixed thinking configuration for OpenAI and Daybreak models to correctly send reasoning.effort: "none" when thinking is disabled.
27
+ - Fixed Grok runaway thinking streams bypassing the thinking-loop guard.
28
+
29
+ ### Removed
30
+
31
+ - Removed legacy local request-cost estimation machinery and database schemas previously used for OpenCode Go estimates.
32
+
33
+ ## [17.2.15] - 2026-08-12
34
+
35
+ ### Fixed
36
+
37
+ - Fixed an issue where AWS_BEDROCK_SKIP_AUTH failed to expose Amazon Bedrock models when AWS credential files were unavailable.
38
+ - Fixed an issue where forceReasoningOff was ignored by Anthropic and Google transports, which allowed native thinking alongside a caller-supplied external scratchpad.
39
+
5
40
  ## [17.2.14] - 2026-08-11
6
41
 
7
42
  ### Added
@@ -7,7 +7,7 @@
7
7
  import { Database } from "bun:sqlite";
8
8
  import type { AuthCredential, AuthCredentialStore, CredentialRefreshLeaseFence, DisabledCredentialSummary, StoredAuthCredential, StoredCredentialBlock } from "../auth-storage.js";
9
9
  import type { OAuthCredentials } from "../registry/oauth/types.js";
10
- import type { ClientUsageReport, ClientUsageSummary, UsageCostHistoryEntry, UsageCostHistoryQuery, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
10
+ import type { ClientUsageReport, ClientUsageSummary, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
11
11
  export declare const USAGE_REPORT_TTL_MS: number;
12
12
  type SerializedCredentialRecord = {
13
13
  credentialType: AuthCredential["type"];
@@ -77,8 +77,6 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
77
77
  releaseCredentialRefreshLease(credentialId: number, owner: string): void;
78
78
  recordUsageSnapshots(entries: UsageHistoryEntry[]): void;
79
79
  listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
80
- recordUsageCosts(entries: UsageCostHistoryEntry[]): void;
81
- listUsageCosts(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[];
82
80
  recordClientUsage(report: ClientUsageReport): void;
83
81
  getClientUsageSummary(sinceMs: number): ClientUsageSummary;
84
82
  /**
@@ -1,7 +1,7 @@
1
1
  import type { ApiKeyResolver } from "./auth-retry.js";
2
2
  import type { OAuthAuthInfo, OAuthController, OAuthCredentials, OAuthProviderId } from "./registry/oauth/types.js";
3
3
  import type { Provider } from "./types.js";
4
- import type { ClientUsageReport, ClientUsageSummary, CredentialRankingStrategy, ObservedUsageEntry, UsageCostHistoryEntry, UsageCostHistoryQuery, UsageHistoryEntry, UsageHistoryQuery, UsageLogger, UsageProvider, UsageReport } from "./usage.js";
4
+ import type { ClientUsageReport, ClientUsageSummary, CredentialRankingStrategy, ObservedUsageEntry, UsageHistoryEntry, UsageHistoryQuery, UsageLogger, UsageProvider, UsageReport } from "./usage.js";
5
5
  import { type CodexResetConsumeCode, type CodexResetCredit } from "./usage/openai-codex-reset.js";
6
6
  export { isSqliteBusyError, isSqliteCorruptionError, SqliteAuthCredentialStore, } from "./auth/sqlite-credential-store.js";
7
7
  export type ApiKeyCredential = {
@@ -311,10 +311,6 @@ export interface AuthCredentialStore {
311
311
  * skipped — the broker host records into its own database instead.
312
312
  */
313
313
  recordUsageSnapshots?(entries: UsageHistoryEntry[]): void;
314
- /** Append observed request costs for providers without upstream usage APIs. */
315
- recordUsageCosts?(entries: UsageCostHistoryEntry[]): void;
316
- /** Read observed request costs, oldest first. */
317
- listUsageCosts?(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[];
318
314
  /** Read recorded usage-limit snapshots, oldest first. */
319
315
  listUsageHistory?(query?: UsageHistoryQuery): UsageHistoryEntry[];
320
316
  /**
@@ -883,12 +879,6 @@ export declare class AuthStorage {
883
879
  * store has no durable history (e.g. a broker-backed remote store).
884
880
  */
885
881
  listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
886
- /** Record one observed provider request cost for later local usage aggregation. */
887
- recordUsageCost(provider: Provider, costUsd: number, options?: {
888
- sessionId?: string;
889
- recordedAt?: number;
890
- baseUrl?: string;
891
- }): boolean;
892
882
  /**
893
883
  * Forward one completed request's usage to the store's observer hook.
894
884
  * Broker-backed stores batch these into per-install reports so the broker
@@ -1,7 +1,7 @@
1
1
  import { AnthropicApiError, AnthropicConnectionError, AnthropicConnectionTimeoutError } from "../error/index.js";
2
2
  export { AnthropicApiError, AnthropicConnectionError, AnthropicConnectionTimeoutError };
3
3
  import type { FetchImpl } from "../types.js";
4
- import type { MessageCreateParamsStreaming } from "./anthropic-wire.js";
4
+ import type { MessageCreateParams } from "./anthropic-wire.js";
5
5
  /** Per-request options accepted by {@link AnthropicMessages.create}. */
6
6
  export interface AnthropicRequestOptions {
7
7
  signal?: AbortSignal;
@@ -78,7 +78,7 @@ export declare class AnthropicApiRequest {
78
78
  export declare class AnthropicMessages {
79
79
  #private;
80
80
  constructor(client: AnthropicMessagesClient, path: string);
81
- create(params: MessageCreateParamsStreaming, options?: AnthropicRequestOptions): AnthropicApiRequest;
81
+ create(params: MessageCreateParams, options?: AnthropicRequestOptions): AnthropicApiRequest;
82
82
  }
83
83
  /**
84
84
  * Structural interface satisfied by both {@link AnthropicMessagesClient} and
@@ -87,11 +87,11 @@ export declare class AnthropicMessages {
87
87
  */
88
88
  export interface AnthropicMessagesClientLike {
89
89
  messages: {
90
- create(params: MessageCreateParamsStreaming, options?: AnthropicRequestOptions): unknown;
90
+ create(params: MessageCreateParams, options?: AnthropicRequestOptions): unknown;
91
91
  };
92
92
  beta?: {
93
93
  messages: {
94
- create(params: MessageCreateParamsStreaming, options?: AnthropicRequestOptions): unknown;
94
+ create(params: MessageCreateParams, options?: AnthropicRequestOptions): unknown;
95
95
  };
96
96
  };
97
97
  }
@@ -102,5 +102,5 @@ export declare class AnthropicMessagesClient implements AnthropicMessagesClientL
102
102
  readonly messages: AnthropicMessages;
103
103
  };
104
104
  constructor(options: AnthropicClientOptions);
105
- request(path: string, params: MessageCreateParamsStreaming, options?: AnthropicRequestOptions): AnthropicApiRequest;
105
+ request(path: string, params: MessageCreateParams, options?: AnthropicRequestOptions): AnthropicApiRequest;
106
106
  }
@@ -1,6 +1,6 @@
1
1
  import type { Api, FetchImpl, Message, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
2
2
  import { type AnthropicFetchOptions, type AnthropicMessagesClientLike } from "./anthropic-client.js";
3
- import { type FallbackParam, type MessageParam, type TextBlockParam } from "./anthropic-wire.js";
3
+ import { type FallbackParam, type MessageParam } from "./anthropic-wire.js";
4
4
  export type AnthropicHeaderOptions = {
5
5
  apiKey: string;
6
6
  baseUrl?: string;
@@ -17,7 +17,6 @@ export type AnthropicHeaderOptions = {
17
17
  export declare function normalizeAnthropicBaseUrl(baseUrl?: string): string | undefined;
18
18
  export declare function buildBetaHeader(baseBetas: readonly string[], extraBetas: readonly string[]): string;
19
19
  export declare function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<string, string>;
20
- type AnthropicCacheControl = NonNullable<TextBlockParam["cache_control"]>;
21
20
  /**
22
21
  * Clears the in-session "server rejected fast mode" sticky flag. Call when the
23
22
  * caller is explicitly re-arming `serviceTier: "priority"` (e.g. user toggled
@@ -229,14 +228,12 @@ export declare const streamAnthropic: StreamFunction<"anthropic-messages">;
229
228
  export type AnthropicSystemBlock = {
230
229
  type: "text";
231
230
  text: string;
232
- cache_control?: AnthropicCacheControl;
233
231
  };
234
232
  type SystemBlockOptions = {
235
233
  includeClaudeCodeInstruction?: boolean;
236
234
  extraInstructions?: string[];
237
235
  /** Text of the first user message — used as fingerprint seed for the billing header. */
238
236
  firstUserMessageText?: string;
239
- cacheControl?: AnthropicCacheControl;
240
237
  };
241
238
  export declare function buildAnthropicSystemBlocks(systemPrompt: readonly string[] | undefined, options?: SystemBlockOptions): AnthropicSystemBlock[] | undefined;
242
239
  export declare function normalizeExtraBetas(betas?: string[] | string): string[];
@@ -12,7 +12,11 @@ export declare function joinTextWithImagePlaceholder(text: string, omittedImages
12
12
  * multimodal content arrays for. The compatible-mode endpoint also serves
13
13
  * multimodal Qwen SKUs without `vl` in the id (e.g. `qwen3.7-plus`), so this
14
14
  * guard only covers families verified to be text-only for issue #1859:
15
- * `qwen*-max` and `qwen*-coder*`.
15
+ * `qwen*-coder*` and `qwen*-max` up to and including `qwen3.7-max`.
16
+ *
17
+ * Qwen-Max became multimodal at `qwen3.8-max` (image input, issue #8019), so
18
+ * `-max` SKUs at version 3.8 or newer are excluded — otherwise the override
19
+ * would strip images from a genuinely vision-capable flagship (issue #8305).
16
20
  *
17
21
  * Used as a defensive override in `convertMessages` so a misconfigured custom
18
22
  * provider (issue #1859) can't drive the request into an unrecoverable 400.
@@ -1,8 +1,7 @@
1
- import { resolveAwsRegistryApiKey } from "./aws.js";
2
1
  export declare const amazonBedrockProvider: {
3
2
  readonly id: "amazon-bedrock";
4
3
  readonly name: "Amazon Bedrock";
5
- readonly envKeys: typeof resolveAwsRegistryApiKey;
4
+ readonly envKeys: () => string | undefined;
6
5
  readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
7
6
  region: string | undefined;
8
7
  profile: string | undefined;
@@ -8,6 +8,8 @@ export interface AwsBedrockProviderOptions extends Readonly<Record<string, unkno
8
8
  }
9
9
  export declare function hasAwsCredentialSource(): boolean;
10
10
  /** Registry key marker for AWS transports that resolve their own bearer/IAM credentials. */
11
- export declare function resolveAwsRegistryApiKey(): string | undefined;
11
+ export declare function resolveAwsRegistryApiKey(options?: {
12
+ allowSkipAuth?: boolean;
13
+ }): string | undefined;
12
14
  /** Resolve a real AWS bearer token while filtering the registry's auth marker. */
13
15
  export declare function resolveAwsBearerToken(apiKey?: string, bearerToken?: string): string | undefined;
@@ -25,7 +25,7 @@ declare const ALL: ({
25
25
  } | {
26
26
  readonly id: "amazon-bedrock";
27
27
  readonly name: "Amazon Bedrock";
28
- readonly envKeys: typeof import("./aws.js").resolveAwsRegistryApiKey;
28
+ readonly envKeys: () => string | undefined;
29
29
  readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
30
30
  region: string | undefined;
31
31
  profile: string | undefined;
@@ -10,6 +10,12 @@ export declare function isOfficialCodexApiUrl(baseUrl: string | undefined): bool
10
10
  export declare function configureProviderMaxInFlightRequests(limits: Record<string, number> | undefined): void;
11
11
  export declare const __providerInFlightForTesting: {
12
12
  setRoot(root: string | undefined): void;
13
+ setHeartbeatTimings(timings: {
14
+ heartbeatMs?: number;
15
+ heartbeatFlushTimeoutMs?: number;
16
+ } | undefined): void;
17
+ setHeartbeatWriter(writer: ((writeProviderInFlightInfo: () => Promise<void>) => Promise<void>) | undefined): void;
18
+ setLeaseRemover(remover: ((leasePath: string) => Promise<void>) | undefined): void;
13
19
  providerDir(provider: string): string;
14
20
  lockDir(provider: string): string;
15
21
  captureStaleLockRelease(provider: string): Promise<(() => Promise<void>) | null>;
@@ -228,6 +228,16 @@ export interface StreamOptions {
228
228
  signal?: AbortSignal;
229
229
  apiKey?: string;
230
230
  cacheRetention?: CacheRetention;
231
+ /**
232
+ * Keep Anthropic's 5-minute prompt cache warm across bounded idle gaps.
233
+ *
234
+ * This is an ownership flag, not a general provider default: exactly one
235
+ * primary agent loop sharing `providerSessionState` should enable it.
236
+ * Side-channel and advisor requests must leave it unset.
237
+ */
238
+ anthropicCacheRefresh?: boolean;
239
+ /** @internal Marks a replay-only Anthropic request that must use non-streaming `max_tokens: 0`. */
240
+ anthropicCacheRefreshRequest?: boolean;
231
241
  /**
232
242
  * Additional headers to include in provider requests.
233
243
  * These are merged on top of model-defined headers.
@@ -295,8 +305,9 @@ export interface StreamOptions {
295
305
  */
296
306
  statefulResponses?: boolean;
297
307
  /**
298
- * Emit `reasoning: { effort: "none" }` for OpenAI Responses and Codex requests.
299
- * Used when a caller supplies an external reasoning scratchpad; other transports ignore it.
308
+ * Disable native reasoning when the caller supplies an external scratchpad.
309
+ * OpenAI Responses emits `reasoning: { effort: "none" }`; Anthropic and
310
+ * Google transports use their native thinking-off controls.
300
311
  */
301
312
  forceReasoningOff?: boolean;
302
313
  /**
@@ -374,10 +385,10 @@ export interface StreamOptions {
374
385
  */
375
386
  providerRetryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
376
387
  /**
377
- * Accept a Google `STOP` response with no visible text or tool call as a
378
- * successful completion. Passive callers such as advisors use this because
379
- * silence is a valid result; interactive agent turns retain empty-response
380
- * retries by default. Ignored by non-Google providers.
388
+ * Accept a normal provider stop with no visible text or tool call as a
389
+ * successful completion. Passive callers and zero-output cache refreshes use
390
+ * this because silence is their expected result; interactive agent turns
391
+ * retain empty-response retries by default.
381
392
  */
382
393
  acceptEmptyResponse?: boolean;
383
394
  /**
@@ -1,2 +1,13 @@
1
- import type { UsageProvider } from "../usage.js";
1
+ import type { CredentialRankingStrategy, UsageProvider } from "../usage.js";
2
2
  export declare const opencodeGoUsageProvider: UsageProvider;
3
+ /**
4
+ * Multi-key pools rank by real headroom on the rolling and weekly windows.
5
+ *
6
+ * The monthly window is deliberately display-only: an exhausted monthly can
7
+ * still serve requests when the account's console "Use balance" fallback is
8
+ * enabled, and the usage endpoint does not report that flag — blocking on it
9
+ * would bench a working key until the subscription anniversary. Hard monthly
10
+ * failures still rotate credentials via the `401 Insufficient balance`
11
+ * usage-limit classification ([#3169](https://github.com/can1357/oh-my-pi/issues/3169)).
12
+ */
13
+ export declare const opencodeGoRankingStrategy: CredentialRankingStrategy;
@@ -139,23 +139,6 @@ export interface UsageHistoryQuery {
139
139
  /** Inclusive lower bound on {@link UsageHistoryEntry.recordedAt} (epoch ms). */
140
140
  sinceMs?: number;
141
141
  }
142
- /** One observed provider request cost, attributed to the credential that made it. */
143
- export interface UsageCostHistoryEntry {
144
- /** Epoch ms the request completed. */
145
- recordedAt: number;
146
- provider: Provider;
147
- /** Stable credential identity key (account/email/project/secret derived). */
148
- accountKey: string;
149
- /** Estimated request cost in USD. */
150
- costUsd: number;
151
- }
152
- /** Filter for reading observed request costs. */
153
- export interface UsageCostHistoryQuery {
154
- provider?: string;
155
- accountKey?: string;
156
- /** Inclusive lower bound on {@link UsageCostHistoryEntry.recordedAt} (epoch ms). */
157
- sinceMs?: number;
158
- }
159
142
  /**
160
143
  * Aggregated request usage a client observed for one (provider, model) pair.
161
144
  * Clients fold every completed request into per-pair buckets and flush them to
@@ -468,8 +451,6 @@ export interface UsageFetchContext {
468
451
  fetch: FetchImpl;
469
452
  logger?: UsageLogger;
470
453
  retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
471
- /** Observed request-cost history for providers without upstream usage APIs. */
472
- listUsageCosts?: (query?: UsageCostHistoryQuery) => UsageCostHistoryEntry[];
473
454
  }
474
455
  /** Provider implementation for fetching usage information. */
475
456
  export interface UsageProvider {
@@ -11,6 +11,7 @@ export declare function hasVisibleAssistantContent(message: AssistantMessage): b
11
11
  interface EmptyCompletionRetryOptions {
12
12
  signal?: AbortSignal;
13
13
  providerRetryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
14
+ acceptEmptyResponse?: boolean;
14
15
  }
15
16
  /**
16
17
  * Wrap a single-attempt provider stream with bounded empty-completion retries.
@@ -5,24 +5,13 @@ import { AssistantMessageEventStream } from "./event-stream.js";
5
5
  * classifiers treat it as a transient (retryable) stop without bespoke rules. */
6
6
  export declare const THINKING_LOOP_ERROR_MARKER = "Thinking loop detected";
7
7
  /**
8
- * True when `model` is a Gemini model whose native thinking stream surfaces the
9
- * "thought summary" titles this module's header guard counts.
8
+ * True when `model.id` belongs to a family guarded for thinking/response loops:
9
+ * Gemini, DeepSeek, or Grok.
10
10
  *
11
- * OpenAI-compat transports can serve Gemini under an arbitrary provider/id, so they
12
- * carry the explicit `compat.enableGeminiThinkingLoopGuard` flag; direct Gemini
13
- * transports carry a clearly shaped id/provider, so a string match is sufficient.
14
- */
15
- export declare function isGeminiThinkingModel(model: Model<Api>): boolean;
16
- /**
17
- * True when `model` should be guarded for thinking/response loops (Gemini & DeepSeek).
18
- *
19
- * OpenAI-compat transports can serve Gemini or DeepSeek under an arbitrary provider/id.
20
- * Direct Gemini/DeepSeek transports carry a clearly shaped id/provider, so a string match
21
- * is sufficient.
11
+ * Model identity is derived only from its id; provider and compatibility metadata
12
+ * do not opt opaque aliases into the guard.
22
13
  */
23
14
  export declare function isLoopGuardedModel(model: Model<Api>, options?: StreamOptions): boolean;
24
- /** @deprecated Use isLoopGuardedModel instead. */
25
- export declare function isGeminiThinkingLoopModel(model: Model<Api>): boolean;
26
15
  /**
27
16
  * Stateful detector fed the streamed thinking deltas. `push` returns a
28
17
  * human-readable reason the first time a loop shape is recognized; the caller
@@ -81,7 +70,7 @@ export declare class GeminiHeaderRunDetector {
81
70
  /**
82
71
  * Wrap a provider stream with the loop guard. `controller` is the guard's own
83
72
  * abort handle: aborting it (after wiring it into the provider's signal via
84
- * {@link withGeminiThinkingLoopGuard}) tears down the upstream once a loop
73
+ * {@link withThinkingLoopGuard}) tears down the upstream once a loop
85
74
  * trips.
86
75
  */
87
76
  export declare function guardThinkingLoopStream(inner: AssistantMessageEventStream, model: Model<Api>, controller: AbortController, options?: StreamOptions): AssistantMessageEventStream;
@@ -93,7 +82,7 @@ export declare function guardThinkingLoopStream(inner: AssistantMessageEventStre
93
82
  * stall; bounding the re-samples and the final cook pass lives in the
94
83
  * result-awaiting caller.
95
84
  */
96
- export declare function withGeminiThinkingLoopGuard<O extends {
85
+ export declare function withThinkingLoopGuard<O extends {
97
86
  signal?: AbortSignal;
98
87
  loopGuard?: {
99
88
  enabled?: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-ai",
4
- "version": "17.2.14",
4
+ "version": "17.3.0",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -38,10 +38,10 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.1",
41
- "@oh-my-pi/omptype": "17.2.14",
42
- "@oh-my-pi/pi-catalog": "17.2.14",
43
- "@oh-my-pi/pi-utils": "17.2.14",
44
- "@oh-my-pi/pi-wire": "17.2.14"
41
+ "@oh-my-pi/omptype": "17.3.0",
42
+ "@oh-my-pi/pi-catalog": "17.3.0",
43
+ "@oh-my-pi/pi-utils": "17.3.0",
44
+ "@oh-my-pi/pi-wire": "17.3.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@bufbuild/protoc-gen-es": "^2.12.1",
@@ -25,8 +25,6 @@ import type {
25
25
  ClientProviderUsage,
26
26
  ClientUsageReport,
27
27
  ClientUsageSummary,
28
- UsageCostHistoryEntry,
29
- UsageCostHistoryQuery,
30
28
  UsageHistoryEntry,
31
29
  UsageHistoryQuery,
32
30
  } from "../usage";
@@ -387,8 +385,6 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
387
385
  #releaseCredentialRefreshLeaseStmt: Statement;
388
386
  #credentialBlockReconcileAfter: Map<string, number> = new Map();
389
387
  #insertUsageHistoryStmt: Statement;
390
- #insertUsageCostStmt: Statement;
391
- #listUsageCostsStmt: Statement;
392
388
  #lastUsageHistoryStmt: Statement;
393
389
  #listUsageHistoryStmt: Statement;
394
390
  #updateUsageHistoryStmt: Statement;
@@ -516,12 +512,6 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
516
512
  this.#listUsageHistoryStmt = this.#db.prepare(
517
513
  "SELECT recorded_at, provider, account_key, email, account_id, limit_id, label, window_label, used_fraction, status, resets_at FROM usage_history WHERE recorded_at >= ? AND (? IS NULL OR provider = ?) ORDER BY recorded_at ASC",
518
514
  );
519
- this.#insertUsageCostStmt = this.#db.prepare(
520
- "INSERT INTO usage_cost_history (recorded_at, provider, account_key, cost_usd) VALUES (?, ?, ?, ?)",
521
- );
522
- this.#listUsageCostsStmt = this.#db.prepare(
523
- "SELECT recorded_at, provider, account_key, cost_usd FROM usage_cost_history WHERE recorded_at >= ? AND (? IS NULL OR provider = ?) AND (? IS NULL OR account_key = ?) ORDER BY recorded_at ASC",
524
- );
525
515
  }
526
516
 
527
517
  static async open(dbPath: string = getAgentDbPath()): Promise<SqliteAuthCredentialStore> {
@@ -634,14 +624,6 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
634
624
  resets_at INTEGER
635
625
  );
636
626
  CREATE INDEX IF NOT EXISTS idx_usage_history_series ON usage_history(provider, account_key, limit_id, recorded_at);
637
- CREATE TABLE IF NOT EXISTS usage_cost_history (
638
- id INTEGER PRIMARY KEY AUTOINCREMENT,
639
- recorded_at INTEGER NOT NULL,
640
- provider TEXT NOT NULL,
641
- account_key TEXT NOT NULL,
642
- cost_usd REAL NOT NULL
643
- );
644
- CREATE INDEX IF NOT EXISTS idx_usage_cost_history_lookup ON usage_cost_history(provider, account_key, recorded_at);
645
627
  CREATE INDEX IF NOT EXISTS idx_usage_history_recorded ON usage_history(recorded_at);
646
628
  CREATE TABLE IF NOT EXISTS clients (
647
629
  install_id TEXT PRIMARY KEY,
@@ -1769,42 +1751,6 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
1769
1751
  return [];
1770
1752
  }
1771
1753
  }
1772
- recordUsageCosts(entries: UsageCostHistoryEntry[]): void {
1773
- try {
1774
- for (const entry of entries) {
1775
- this.#insertUsageCostStmt.run(entry.recordedAt, entry.provider, entry.accountKey, entry.costUsd);
1776
- }
1777
- } catch {
1778
- // Cost history is best-effort; never break request persistence.
1779
- }
1780
- }
1781
-
1782
- listUsageCosts(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[] {
1783
- try {
1784
- const provider = query?.provider ?? null;
1785
- const accountKey = query?.accountKey ?? null;
1786
- const rows = this.#listUsageCostsStmt.all(
1787
- query?.sinceMs ?? 0,
1788
- provider,
1789
- provider,
1790
- accountKey,
1791
- accountKey,
1792
- ) as Array<{
1793
- recorded_at: number;
1794
- provider: string;
1795
- account_key: string;
1796
- cost_usd: number;
1797
- }>;
1798
- return rows.map(row => ({
1799
- recordedAt: row.recorded_at,
1800
- provider: row.provider as Provider,
1801
- accountKey: row.account_key,
1802
- costUsd: row.cost_usd,
1803
- }));
1804
- } catch {
1805
- return [];
1806
- }
1807
- }
1808
1754
 
1809
1755
  recordClientUsage(report: ClientUsageReport): void {
1810
1756
  const now = Date.now();
@@ -2051,8 +1997,6 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
2051
1997
  this.#lastUsageHistoryStmt.finalize();
2052
1998
  this.#listUsageHistoryStmt.finalize();
2053
1999
  this.#updateUsageHistoryStmt.finalize();
2054
- this.#insertUsageCostStmt.finalize();
2055
- this.#listUsageCostsStmt.finalize();
2056
2000
  this.#updateIfMatchesStmt.finalize();
2057
2001
  this.#updateIfMatchesWithLeaseStmt.finalize();
2058
2002
  this.#deleteIfMatchesWithLeaseStmt.finalize();