@oh-my-pi/pi-ai 15.12.3 → 15.12.4
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.
- package/CHANGELOG.md +23 -0
- package/dist/types/auth-broker/remote-store.d.ts +1 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +1 -1
- package/dist/types/auth-gateway/types.d.ts +7 -1
- package/dist/types/auth-storage.d.ts +19 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +1 -1
- package/dist/types/providers/anthropic-messages-server.d.ts +2 -2
- package/dist/types/providers/google-shared.d.ts +17 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +2 -2
- package/dist/types/providers/openai-chat-server.d.ts +2 -2
- package/dist/types/providers/openai-chat-wire.d.ts +644 -0
- package/dist/types/providers/openai-codex-responses.d.ts +1 -1
- package/dist/types/providers/openai-completions.d.ts +1 -1
- package/dist/types/providers/openai-responses-server-schema.d.ts +2 -2
- package/dist/types/providers/openai-responses-server.d.ts +2 -2
- package/dist/types/providers/openai-responses-shared.d.ts +5 -6
- package/dist/types/providers/openai-responses-wire.d.ts +6065 -0
- package/dist/types/providers/openai-responses.d.ts +3 -3
- package/dist/types/providers/pi-native-server.d.ts +2 -1
- package/dist/types/usage.d.ts +1 -1
- package/dist/types/utils/openai-http.d.ts +58 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +4 -5
- package/src/auth-broker/remote-store.ts +9 -0
- package/src/auth-broker/wire-schemas.ts +1 -1
- package/src/auth-gateway/server.ts +16 -2
- package/src/auth-gateway/types.ts +8 -0
- package/src/auth-storage.ts +100 -8
- package/src/providers/anthropic-messages-server-schema.ts +1 -1
- package/src/providers/anthropic-messages-server.ts +31 -10
- package/src/providers/anthropic.ts +3 -2
- package/src/providers/azure-openai-responses.ts +63 -49
- package/src/providers/gitlab-duo.ts +3 -3
- package/src/providers/google-gemini-cli.ts +4 -7
- package/src/providers/google-shared.ts +98 -33
- package/src/providers/openai-anthropic-shim.ts +2 -2
- package/src/providers/openai-chat-server-schema.ts +3 -2
- package/src/providers/openai-chat-server.ts +30 -8
- package/src/providers/openai-chat-wire.ts +847 -0
- package/src/providers/openai-codex-responses.ts +10 -10
- package/src/providers/openai-completions.ts +141 -141
- package/src/providers/openai-responses-server-schema.ts +3 -2
- package/src/providers/openai-responses-server.ts +50 -43
- package/src/providers/openai-responses-shared.ts +25 -19
- package/src/providers/openai-responses-wire.ts +6391 -0
- package/src/providers/openai-responses.ts +60 -68
- package/src/providers/pi-native-server.ts +42 -15
- package/src/registry/oauth/gitlab-duo.ts +82 -12
- package/src/stream.ts +20 -7
- package/src/usage.ts +1 -1
- package/src/utils/openai-http.ts +153 -0
- package/src/utils.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.12.4] - 2026-06-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `GITLAB_CLIENT_ID` and `GITLAB_REDIRECT_URI` env-var overrides for the GitLab Duo OAuth login flow so users running with their own GitLab OAuth application can replace the bundled credentials when GitLab rejects the bundled `client_id`'s redirect URI. Setting `GITLAB_REDIRECT_URI` also disables the random-port fallback (strict OAuth providers reject mismatched URIs anyway). ([#2424](https://github.com/can1357/oh-my-pi/issues/2424))
|
|
10
|
+
- Added `AuthStorage.listStoredCredentials()` and `AuthStorage.removeCredential()` for per-account credential management.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replaced the OpenAI SDK client usage in `openai-completions`, `openai-responses`, `azure-openai-responses`, and `openai-codex-responses` with the new internal `postOpenAIStream` OpenAI-wire JSON/SSE transport
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Fixed streaming providers to cancel upstream model requests when the client closes the response body, so interrupted SSE sessions stop instead of continuing in the background
|
|
19
|
+
- Fixed: provider request builders treat unknown `model.maxTokens` (`null`) as "no model cap" instead of coercing to `0` via `Math.min`; Anthropic falls back to the 64k Claude-Code cap for its required `max_tokens`.
|
|
20
|
+
- Fixed transient stream failures on OpenAI-compatible providers by retrying HTTP 408/429/5xx responses and transient network errors with Retry-After/quota-hint aware backoff
|
|
21
|
+
- Fixed SSE stream handling for OpenAI-compatible responses by parsing wire-level JSON frames directly and honoring `[DONE]` termination
|
|
22
|
+
- Fixed stream error handling for OpenAI-compatible providers by preserving structured HTTP status/headers and response body details from failed requests for retry and strict-tool fallback logic
|
|
23
|
+
- Fixed OpenAI-compat streams ending with a bare `finish_reason: "error"` (gateways like OpenRouter reporting upstream failures, e.g. Gemini `MALFORMED_FUNCTION_CALL`) surfacing as a non-retryable `Provider finish_reason: error`. The reason is now mapped to `Provider returned error finish_reason`, which the session retry classifier recognizes as transient, so the turn auto-retries instead of stopping with a pinned error banner.
|
|
24
|
+
- Fixed `SqliteAuthCredentialStore.open()` crashing with `SQLITE_BUSY_RECOVERY` (errno 261) when several `omp --session` panes restore concurrently after an unclean shutdown: `PRAGMA busy_timeout = 5000` now runs as a standalone statement BEFORE `PRAGMA journal_mode=WAL` (the first lock-taking statement during WAL recovery), and `open()` retries the BUSY family — `SQLITE_BUSY`, `SQLITE_BUSY_RECOVERY`, `SQLITE_BUSY_SNAPSHOT`, `SQLITE_BUSY_TIMEOUT` — with bounded exponential backoff. The exhausted-retry error message includes the DB path. Exported `isSqliteBusyError(err)` for callers that need the same classifier ([#2421](https://github.com/can1357/oh-my-pi/issues/2421)).
|
|
25
|
+
- Fixed MiniMax-M3 OpenAI-compatible streams rendering reasoning twice when the same chunk carried both `<think>…</think>` content and structured `reasoning_content`; structured reasoning now wins and cumulative MiniMax reasoning snapshots are collapsed to deltas. ([#2433](https://github.com/can1357/oh-my-pi/issues/2433))
|
|
26
|
+
- Fixed Gemini turns silently halting the agent when the model returned `finishReason: STOP` with only an empty (or whitespace-only) text part and no tool call — the well-known "empty response" failure. All Google surfaces (public Generative Language `streamGoogle`, Vertex `streamGoogleVertex`, and Cloud Code Assist `google-gemini-cli`/`google-antigravity`) now classify such a turn as empty via the shared `hasMeaningfulGoogleContent` check and retry it up to `MAX_EMPTY_STREAM_RETRIES` times before surfacing an error. The Cloud Code Assist path previously had an empty-stream retry that never fired for this case (its `hasContent` flag counted an empty-string text part as content), and the public/Vertex path had no retry at all; the retry now emits a single `start` event so no duplicate partial message leaks downstream.
|
|
27
|
+
|
|
5
28
|
## [15.12.1] - 2026-06-12
|
|
6
29
|
|
|
7
30
|
### Added
|
|
@@ -37,6 +37,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
37
37
|
*/
|
|
38
38
|
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
39
39
|
deleteAuthCredential(id: number, disabledCause: string): void;
|
|
40
|
+
deleteAuthCredentialRemote(id: number, disabledCause: string): Promise<boolean>;
|
|
40
41
|
tryDisableAuthCredentialIfMatches(id: number, _expectedData: string, disabledCause: string): boolean;
|
|
41
42
|
waitForFreshSnapshot(maxWaitMs: number, opts?: {
|
|
42
43
|
signal?: AbortSignal;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* keys are rejected — the previous implementation used a hand-rolled
|
|
11
11
|
* `hasOnlyFields` allowlist for the same effect.
|
|
12
12
|
*/
|
|
13
|
-
import
|
|
13
|
+
import { z } from "zod/v4";
|
|
14
14
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
|
15
15
|
export declare const oauthCredentialSchema: z.ZodObject<{
|
|
16
16
|
type: z.ZodLiteral<"oauth">;
|
|
@@ -90,10 +90,16 @@ export interface AuthGatewayParsedRequest {
|
|
|
90
90
|
stream: boolean;
|
|
91
91
|
options: AuthGatewayParsedRequestOptions;
|
|
92
92
|
}
|
|
93
|
+
export interface AuthGatewayStreamControl {
|
|
94
|
+
/** Gateway request signal. Encoders stop producing frames when it aborts. */
|
|
95
|
+
signal?: AbortSignal;
|
|
96
|
+
/** Called when the HTTP response body is cancelled by the client. */
|
|
97
|
+
onCancel?: (reason?: unknown) => void;
|
|
98
|
+
}
|
|
93
99
|
export interface AuthGatewayFormatModule {
|
|
94
100
|
parseRequest(body: unknown, headers?: Headers): AuthGatewayParsedRequest;
|
|
95
101
|
encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown>;
|
|
96
|
-
encodeStream(events: AssistantMessageEventStream, requestedModelId: string, options?: AuthGatewayParsedRequestOptions): ReadableStream<Uint8Array>;
|
|
102
|
+
encodeStream(events: AssistantMessageEventStream, requestedModelId: string, options?: AuthGatewayParsedRequestOptions, control?: AuthGatewayStreamControl): ReadableStream<Uint8Array>;
|
|
97
103
|
/**
|
|
98
104
|
* Emit a protocol-specific error envelope. OpenAI returns
|
|
99
105
|
* `{ error: { message, type } }`; Anthropic returns
|
|
@@ -308,6 +308,11 @@ export interface AuthCredentialStore {
|
|
|
308
308
|
* `replaceAuthCredentialsForProvider`.
|
|
309
309
|
*/
|
|
310
310
|
replaceAuthCredentialsRemote?(provider: string, credentials: AuthCredential[]): Promise<StoredAuthCredential[]>;
|
|
311
|
+
/**
|
|
312
|
+
* Optional async write hook for disabling one stored credential. Remote stores
|
|
313
|
+
* use it to await broker persistence before AuthStorage updates its snapshot.
|
|
314
|
+
*/
|
|
315
|
+
deleteAuthCredentialRemote?(id: number, disabledCause: string): Promise<boolean>;
|
|
311
316
|
/**
|
|
312
317
|
* Optional async write hook for clearing every credential for a provider
|
|
313
318
|
* (logout). When present, `AuthStorage.remove` routes through this instead
|
|
@@ -585,10 +590,18 @@ export declare class AuthStorage {
|
|
|
585
590
|
* Set credential for a provider.
|
|
586
591
|
*/
|
|
587
592
|
set(provider: string, credential: AuthCredentialEntry): Promise<void>;
|
|
593
|
+
/**
|
|
594
|
+
* List stored credential rows, optionally filtered by provider.
|
|
595
|
+
*/
|
|
596
|
+
listStoredCredentials(provider?: string): StoredAuthCredential[];
|
|
588
597
|
/**
|
|
589
598
|
* Remove credential for a provider.
|
|
590
599
|
*/
|
|
591
600
|
remove(provider: string): Promise<void>;
|
|
601
|
+
/**
|
|
602
|
+
* Remove one stored credential for a provider.
|
|
603
|
+
*/
|
|
604
|
+
removeCredential(provider: string, credentialId: number): Promise<boolean>;
|
|
592
605
|
/**
|
|
593
606
|
* List all providers with credentials.
|
|
594
607
|
*/
|
|
@@ -890,6 +903,12 @@ export declare class AuthStorage {
|
|
|
890
903
|
*/
|
|
891
904
|
describeCredentialSource(provider: string, sessionId?: string): string | undefined;
|
|
892
905
|
}
|
|
906
|
+
/**
|
|
907
|
+
* SQLite's busy result code family — base `SQLITE_BUSY` plus the extended
|
|
908
|
+
* variants `SQLITE_BUSY_RECOVERY` (concurrent WAL recovery), `SQLITE_BUSY_SNAPSHOT`,
|
|
909
|
+
* and `SQLITE_BUSY_TIMEOUT`. All warrant the same backoff-and-retry treatment.
|
|
910
|
+
*/
|
|
911
|
+
export declare function isSqliteBusyError(err: unknown): boolean;
|
|
893
912
|
/**
|
|
894
913
|
* Default SQLite-backed implementation of {@link AuthCredentialStore}.
|
|
895
914
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Used by `anthropic-messages.ts:parseRequest` to validate the inbound JSON
|
|
8
8
|
* before walking it into pi-ai's canonical `Context`.
|
|
9
9
|
*/
|
|
10
|
-
import
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
11
|
import type { ContentBlockParam, ImageBlockParam, MessageCreateParams, MessageParam, TextBlockParam, Tool, ToolChoice } from "./anthropic-wire";
|
|
12
12
|
export declare const cacheControlSchema: z.ZodObject<{
|
|
13
13
|
type: z.ZodLiteral<"ephemeral">;
|
|
@@ -4,11 +4,11 @@ import type { AssistantMessage, AssistantMessageEventStream } from "../types";
|
|
|
4
4
|
* gateway translation. Inbound: foreign HTTP body → omp Context. Outbound:
|
|
5
5
|
* omp AssistantMessage[Stream] → Anthropic-shaped JSON / SSE.
|
|
6
6
|
*/
|
|
7
|
-
import type { AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
|
|
7
|
+
import type { AuthGatewayStreamControl, AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
|
|
8
8
|
export type { ParsedRequest };
|
|
9
9
|
export declare function parseRequest(body: unknown, headers?: Headers): ParsedRequest;
|
|
10
10
|
export declare function encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown>;
|
|
11
|
-
export declare function encodeStream(events: AssistantMessageEventStream, requestedModelId: string): ReadableStream<Uint8Array>;
|
|
11
|
+
export declare function encodeStream(events: AssistantMessageEventStream, requestedModelId: string, _options?: ParsedRequest["options"], control?: AuthGatewayStreamControl): ReadableStream<Uint8Array>;
|
|
12
12
|
/**
|
|
13
13
|
* Anthropic error envelope: `{ type: "error", error: { type, message } }`.
|
|
14
14
|
* See https://docs.anthropic.com/en/api/errors. Returned as a `Response` so
|
|
@@ -97,6 +97,23 @@ export declare function mapStopReason(reason: FinishReason): StopReason;
|
|
|
97
97
|
* Map string finish reason to our StopReason (for raw API responses).
|
|
98
98
|
*/
|
|
99
99
|
export declare function mapStopReasonString(reason: string): StopReason;
|
|
100
|
+
/**
|
|
101
|
+
* Bounded retries for the well-known Gemini "empty response" failure: a benign
|
|
102
|
+
* `finishReason: STOP` carrying only an empty/whitespace text part and no tool call.
|
|
103
|
+
* Shared by the public/Vertex `streamGoogleGenAI` path and the Cloud Code Assist
|
|
104
|
+
* (`google-gemini-cli`/`google-antigravity`) provider so both apply the same policy.
|
|
105
|
+
*/
|
|
106
|
+
export declare const MAX_EMPTY_STREAM_RETRIES = 2;
|
|
107
|
+
export declare const EMPTY_STREAM_BASE_DELAY_MS = 500;
|
|
108
|
+
/**
|
|
109
|
+
* Whether a completed Google assistant message carries content worth delivering.
|
|
110
|
+
*
|
|
111
|
+
* A tool call or any non-whitespace text counts as meaningful. An empty/whitespace-only
|
|
112
|
+
* text part — or thinking that never produced an answer — is the "empty response" failure:
|
|
113
|
+
* delivered as-is the agent loop has nothing to act on and silently halts, so the request
|
|
114
|
+
* must be retried instead of surfaced.
|
|
115
|
+
*/
|
|
116
|
+
export declare function hasMeaningfulGoogleContent(output: AssistantMessage): boolean;
|
|
100
117
|
export declare function nextToolCallId(name: string): string;
|
|
101
118
|
/**
|
|
102
119
|
* Push the appropriate `text_end` / `thinking_end` event for the given block.
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* non-strict defaults (e.g. `stream_options.include_obfuscation`) — does not
|
|
8
8
|
* trip 400s on shapes we simply ignore.
|
|
9
9
|
*/
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
|
+
import type { ChatCompletionContentPart, ChatCompletionCreateParams, ChatCompletionMessageParam, ChatCompletionMessageToolCall, ChatCompletionTool, ChatCompletionToolChoiceOption } from "./openai-chat-wire";
|
|
12
12
|
export declare const textPartSchema: z.ZodObject<{
|
|
13
13
|
type: z.ZodLiteral<"text">;
|
|
14
14
|
text: z.ZodString;
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Parsed inbound OpenAI chat-completions request, ready to feed into pi-ai
|
|
3
3
|
* `stream(model, context, options)`.
|
|
4
4
|
*/
|
|
5
|
-
import type { AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
|
|
5
|
+
import type { AuthGatewayStreamControl, AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
|
|
6
6
|
import type { AssistantMessage, AssistantMessageEventStream } from "../types";
|
|
7
7
|
export type { ParsedRequest };
|
|
8
8
|
export declare function parseRequest(body: unknown, headers?: Headers): ParsedRequest;
|
|
9
9
|
export declare function encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown>;
|
|
10
|
-
export declare function encodeStream(events: AssistantMessageEventStream, requestedModelId: string, options?: ParsedRequest["options"]): ReadableStream<Uint8Array>;
|
|
10
|
+
export declare function encodeStream(events: AssistantMessageEventStream, requestedModelId: string, options?: ParsedRequest["options"], control?: AuthGatewayStreamControl): ReadableStream<Uint8Array>;
|
|
11
11
|
/**
|
|
12
12
|
* OpenAI chat-completions error envelope:
|
|
13
13
|
* `{ error: { message, type } }`
|