@oh-my-pi/pi-ai 18.11.3 → 18.12.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.
- package/CHANGELOG.md +31 -42
- package/dist/types/auth/cascade.d.ts +5 -3
- package/dist/types/auth/select.d.ts +5 -1
- package/dist/types/auth/types.d.ts +12 -2
- package/dist/types/dialect/coercion.d.ts +6 -0
- package/dist/types/error/classes.d.ts +1 -1
- package/dist/types/error/flags.d.ts +4 -1
- package/dist/types/error/index.d.ts +1 -0
- package/dist/types/error/network-cause.d.ts +40 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/judgment/native.d.ts +28 -2
- package/dist/types/judgment/wire.d.ts +3 -30
- package/dist/types/providers/anthropic-signature.d.ts +12 -2
- package/dist/types/providers/anthropic.d.ts +12 -1
- package/dist/types/providers/connection-prewarm.d.ts +15 -0
- package/dist/types/providers/transform-messages.d.ts +11 -7
- package/dist/types/types.d.ts +7 -0
- package/dist/types/utils/narration.d.ts +9 -0
- package/dist/types/utils/request-body.d.ts +11 -0
- package/dist/types/utils/request-compression.d.ts +18 -0
- package/dist/types/utils/transport-fetch.d.ts +8 -0
- package/package.json +6 -6
- package/src/auth/cascade.ts +23 -13
- package/src/auth/refresh.ts +39 -12
- package/src/auth/select.ts +8 -2
- package/src/auth/types.ts +13 -2
- package/src/auth-gateway/server.ts +22 -19
- package/src/dialect/coercion.ts +16 -0
- package/src/dialect/hermes.ts +7 -11
- package/src/dialect/qwen3.ts +3 -11
- package/src/error/classes.ts +3 -2
- package/src/error/flags.ts +6 -3
- package/src/error/index.ts +1 -0
- package/src/error/network-cause.ts +179 -0
- package/src/index.ts +1 -0
- package/src/judgment/native.ts +46 -12
- package/src/judgment/systemone.ts +4 -9
- package/src/judgment/wire.ts +3 -32
- package/src/providers/amazon-bedrock.ts +3 -0
- package/src/providers/anthropic-client.ts +1 -1
- package/src/providers/anthropic-signature.ts +35 -8
- package/src/providers/anthropic.ts +35 -18
- package/src/providers/connection-prewarm.ts +78 -0
- package/src/providers/factory-droid/gemini.ts +9 -3
- package/src/providers/ollama.ts +8 -0
- package/src/providers/openai-codex-responses.ts +9 -45
- package/src/providers/openai-completions.ts +2 -1
- package/src/providers/openai-responses.ts +2 -1
- package/src/providers/transform-messages.ts +55 -31
- package/src/stream.ts +5 -55
- package/src/types.ts +7 -0
- package/src/utils/narration.ts +18 -0
- package/src/utils/openai-http.ts +25 -18
- package/src/utils/request-body.ts +27 -0
- package/src/utils/request-compression.ts +152 -0
- package/src/utils/request-debug.ts +23 -9
- package/src/utils/schema/dereference.ts +7 -7
- package/src/utils/schema/draft.ts +10 -2
- package/src/utils/schema/json-schema-validator.ts +4 -10
- package/src/utils/schema/normalize.ts +6 -9
- package/src/utils/transport-fetch.ts +37 -0
- package/src/utils/validation.ts +4 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.12.0] - 2026-10-17
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- `PI_CODEX_ZSTD` is replaced by `PI_REQUEST_COMPRESSION`; set `PI_REQUEST_COMPRESSION=0` to send Codex (and every other) request body uncompressed ([#16086](https://github.com/can1357/oh-my-pi/pull/16086) by [@H4vC](https://github.com/H4vC))
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added `ThinkingContent.narration`, set on Anthropic and Bedrock thinking blocks whose signature marks them as between-tool narration, and `presentNarrationAsAnswer(message)` to show those blocks as reply text while replay keeps them as signed thinking
|
|
14
|
+
- Added `keys.peek(provider, { includeExpired: true })` to name the stored account behind an expired OAuth token for reads that never send it, such as keying an offline model-cache lookup ([#16061](https://github.com/can1357/oh-my-pi/pull/16061) by [@gchai](https://github.com/gchai))
|
|
15
|
+
- Added `prewarmModelConnection(model)` (`providers/connection-prewarm`), which opens the model's API connection ahead of a request using the same transport and TLS settings so the request reuses it instead of paying the connection handshake ([#16086](https://github.com/can1357/oh-my-pi/pull/16086) by [@H4vC](https://github.com/H4vC))
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Anthropic requests to the official API now gzip the request body, so long conversations reach the model sooner on slow uplinks; set `PI_REQUEST_COMPRESSION=0` to disable it ([#16044](https://github.com/can1357/oh-my-pi/pull/16044) by [@H4vC](https://github.com/H4vC))
|
|
20
|
+
- GitHub Copilot requests (chat completions, Responses, and Claude messages) now send zstd-compressed bodies, cutting upload size by roughly 60–75% on long conversations; set `PI_REQUEST_COMPRESSION=0` to disable it ([#16086](https://github.com/can1357/oh-my-pi/pull/16086) by [@H4vC](https://github.com/H4vC))
|
|
21
|
+
- A rejected request-body encoding now only disables compression for that host, and only after the uncompressed resend succeeds, so a genuine 400 no longer turns compression off ([#16086](https://github.com/can1357/oh-my-pi/pull/16086) by [@H4vC](https://github.com/H4vC))
|
|
22
|
+
- Connection warm-up now covers every catalog provider, not only the official Anthropic API: it re-warms the origin a model's requests actually reach (Copilot plan hosts, `MOONSHOT_BASE_URL`, regional hosts), throttled per host; custom providers, built-in provider ids routed to a gateway, local hosts, and Anthropic gateways are never probed ([#16086](https://github.com/can1357/oh-my-pi/pull/16086) by [@H4vC](https://github.com/H4vC))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Fixed Ollama and Ollama Cloud streaming errors being silently accepted as successful responses ([#16039](https://github.com/can1357/oh-my-pi/pull/16039) by [@mottopanikeiku](https://github.com/mottopanikeiku))
|
|
27
|
+
- Fixed the hermes in-band scanner exposing a truncated tool name for streaming tool calls: `toolStart` now waits for the complete JSON name, so a stream that splits or stops mid-name never reports a partial tool name ([#16064](https://github.com/can1357/oh-my-pi/pull/16064) by [@adi-IL](https://github.com/adi-IL))
|
|
28
|
+
- Fixed `keys.peek()` rotating through stored accounts on each call; it now returns the first account whose token has not expired, every time, so model discovery keeps reading one account's cached catalog ([#16061](https://github.com/can1357/oh-my-pi/pull/16061) by [@gchai](https://github.com/gchai))
|
|
29
|
+
- Fixed local JSON Schema references with URI-percent-encoded, JSON Pointer-escaped, or empty definition names resolving incorrectly during dereferencing, strict conversion, and value validation ([#15926](https://github.com/can1357/oh-my-pi/pull/15926) by [@jchanghong023](https://github.com/jchanghong023)).
|
|
30
|
+
- Fixed network failures showing a generic "Connection error."; offline, connection-refused, and TLS-verification failures now name the host and the cause ([#16036](https://github.com/can1357/oh-my-pi/pull/16036) by [@jorgoose](https://github.com/jorgoose))
|
|
31
|
+
- Fixed Jev image limits rejecting inputs for unrelated native judges and redundant TypeSafe image judgments when only detail changes ([#16002](https://github.com/can1357/oh-my-pi/pull/16002) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy)).
|
|
32
|
+
- Fixed SIGTERM interrupting an in-flight OAuth token rotation before its replacement refresh token was saved ([#15845](https://github.com/can1357/oh-my-pi/issues/15845)).
|
|
33
|
+
- Fixed sessions getting stuck on HTTP 400 after replaying tool names unsupported by the target provider, including invented `proc://*` calls and Google tool names sent to Bedrock ([#15839](https://github.com/can1357/oh-my-pi/issues/15839))
|
|
34
|
+
|
|
5
35
|
## [18.11.3] - 2026-10-16
|
|
6
36
|
|
|
7
37
|
### Fixed
|
|
@@ -2327,45 +2357,4 @@
|
|
|
2327
2357
|
- Removed the `setNextRequestDebugPath`, `clearNextRequestDebugPath`, and `getNextRequestDebugPath` utility functions for request debugging, as request/response recording now relies exclusively on the `PI_REQ_DEBUG` environment variable.
|
|
2328
2358
|
- Removed Wafer Pass (`wafer-pass`) login support; Wafer Serverless remains available as `wafer-serverless`.
|
|
2329
2359
|
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
### Changed
|
|
2333
|
-
|
|
2334
|
-
- Changed OpenAI Responses and Codex Responses custom grammar tool requests to leave `parallel_tool_calls` unset instead of forcing serial tool calls; Codex `responsesLite` still disables parallel tool calls when tools are present.
|
|
2335
|
-
|
|
2336
|
-
### Fixed
|
|
2337
|
-
|
|
2338
|
-
- Fixed Bedrock `/btw` and other no-tool ephemeral turns failing after prior tool calls by sending the required sentinel `toolConfig` whenever replayed history contains `toolUse`/`toolResult` blocks. ([#3124](https://github.com/can1357/oh-my-pi/issues/3124))
|
|
2339
|
-
- Fixed Anthropic Messages pre-content TLS `bad record MAC` server transport errors surfacing before the provider retry loop exhausts its budget. ([#3134](https://github.com/can1357/oh-my-pi/issues/3134))
|
|
2340
|
-
- Fixed API-key login flows replacing existing stored keys for the same provider, so providers such as NVIDIA NIM can keep multiple active keys available for session-level rotation. ([#2923](https://github.com/can1357/oh-my-pi/issues/2923))
|
|
2341
|
-
- Fixed `openai-codex-responses` forwarding sampling controls (`temperature`, `top_p`, `top_k`, `min_p`, `presence_penalty`, `repetition_penalty`) into the Codex request body — the ChatGPT-subscription Codex backend rejects each of them with a 400 `{"detail":"Unsupported parameter: temperature"}`, so any caller setting non-default `StreamOptions` saw every turn fail. The provider now drops the full sampling set (matching codex-rs), and the auth-gateway's defensive strip on both `buildStreamOptions` and the pi-native path was widened from `{temperature, topP}` to the same set plus `stopSequences`/`frequencyPenalty`. ([#3117](https://github.com/can1357/oh-my-pi/issues/3117))
|
|
2342
|
-
- Fixed Anthropic Messages retry classification for transient TLS/server-error failures such as `tls: bad record MAC (type=server_error)`. These pre-content transport blips are now retried inside the provider loop before the session sees an error banner.
|
|
2343
|
-
|
|
2344
|
-
## [16.1.4] - 2026-06-19
|
|
2345
|
-
|
|
2346
|
-
### Added
|
|
2347
|
-
|
|
2348
|
-
- Added bounded auto-retry for empty assistant completions specifically to the OpenAI Responses provider
|
|
2349
|
-
- Added bounded auto-retry for empty assistant completions across the OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages providers. A benign terminal stop that streamed no content and billed no output tokens — the signature of a flaky OpenAI-/Anthropic-compatible gateway that intermittently 200s with an empty body — is now retried up to twice with exponential backoff (honoring `providerRetryWait`) before being surfaced, instead of silently stalling the agent loop. Retries fire only before any content streams, so live streaming (including thinking) is never delayed, retried, or duplicated.
|
|
2350
|
-
|
|
2351
|
-
### Fixed
|
|
2352
|
-
|
|
2353
|
-
- Fixed the Antigravity (`google-antigravity`) request builder dropping `labels.model_enum` when the wire profile does not declare one. Required for Claude 4.6 ids whose `AntigravityModelWireProfile` carries only `maxOutputTokens` (no captured `model_enum`); the label is now emitted only when the catalog defines it. ([#3067](https://github.com/can1357/oh-my-pi/issues/3067))
|
|
2354
|
-
|
|
2355
|
-
## [16.1.3] - 2026-06-19
|
|
2356
|
-
|
|
2357
|
-
### Added
|
|
2358
|
-
|
|
2359
|
-
- Added regression test pinning that `openai-completions` emits a `thinking` block for `reasoning_content` deltas even when `delta.content` is explicitly JSON `null` (the DeepSeek-format dual-key pattern used by custom GLM/Qwen reasoning providers). See [#2996](https://github.com/can1357/oh-my-pi/issues/2996).
|
|
2360
|
-
|
|
2361
|
-
### Changed
|
|
2362
|
-
|
|
2363
|
-
- Improved the thinking loop guard to treat assistant text loops as retryable errors
|
|
2364
|
-
- Refined text normalization logic to reduce false positives in the thinking loop detector
|
|
2365
|
-
|
|
2366
|
-
### Fixed
|
|
2367
|
-
|
|
2368
|
-
- Fixed Ollama chat requests sending image payloads to text-only models. Image blocks are now omitted and replaced with the standard non-vision placeholder for models without vision support, while vision-capable Ollama models continue to receive images. ([#3009](https://github.com/can1357/oh-my-pi/pull/3009) by [@serverinspector](https://github.com/serverinspector))
|
|
2369
|
-
- Fixed `SqliteAuthCredentialStore.close()` leaking one-off prepared statements created by inline `this.#db.prepare()` calls in `#authCredentialsTableExists`, `#readAuthSchemaVersion`, `#inferAuthSchemaVersion`, `#migrateAuthSchemaV0ToV1`, `#backfillCredentialIdentityKeys`, and `updateAuthCredential`. Each statement is now wrapped in `try/finally` with `stmt.finalize()`, and the `close()` method finalizes `#insertUsageCostStmt` and `#listUsageCostsStmt` which were previously missed. This caused EBUSY on Windows when tests tried to delete temp dirs containing open SQLite handles.
|
|
2370
|
-
|
|
2371
|
-
Older entries are archived in [packages/ai/CHANGELOG.md@0595d346917d](https://github.com/can1357/oh-my-pi/blob/0595d346917d152ea2b710332817de599f749626/packages/ai/CHANGELOG.md).
|
|
2360
|
+
Older entries are archived in [packages/ai/CHANGELOG.md@50ec66f75d57](https://github.com/can1357/oh-my-pi/blob/50ec66f75d57ad55838a9fe4da5e66f29af4d73c/packages/ai/CHANGELOG.md).
|
|
@@ -2,7 +2,7 @@ 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 } from "./types.js";
|
|
5
|
+
import type { AuthApiKeyOptions, AuthPeekOptions, 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;
|
|
@@ -114,9 +114,11 @@ export declare class KeyCascade implements KeysApi {
|
|
|
114
114
|
* Used for model discovery where we only need to know if credentials exist
|
|
115
115
|
* and get a best-effort token. GitHub Copilot's peek must preserve
|
|
116
116
|
* enterprise routing metadata because discovery needs a structured
|
|
117
|
-
* credential to reach the correct host.
|
|
117
|
+
* credential to reach the correct host. With several stored accounts every
|
|
118
|
+
* peek returns the same one, so credential-scoped discovery caches stay on
|
|
119
|
+
* one namespace.
|
|
118
120
|
*/
|
|
119
|
-
peek(provider: string): Promise<string | undefined>;
|
|
121
|
+
peek(provider: string, options?: AuthPeekOptions): Promise<string | undefined>;
|
|
120
122
|
/**
|
|
121
123
|
* Resolve a bearer together with the stored row that supplied it, for a request.
|
|
122
124
|
* Same precedence as {@link KeyCascade.get}, but rejects with
|
|
@@ -55,7 +55,11 @@ export declare class CredentialSelector {
|
|
|
55
55
|
/**
|
|
56
56
|
* Selects a credential of the specified type for a provider.
|
|
57
57
|
* Returns both the credential and its index in the original array (for updates/removal).
|
|
58
|
-
*
|
|
58
|
+
* With `sessionId` the scan starts at the session's hashed index; without one it starts at the
|
|
59
|
+
* first stored credential and never advances round-robin. Model discovery peeks through this,
|
|
60
|
+
* and credential-scoped catalogs (e.g. GitHub Copilot) key their cache rows on the credential
|
|
61
|
+
* picked, so a rotating pick would read another account's cache namespace on every pass.
|
|
62
|
+
* Blocked credentials are skipped when possible.
|
|
59
63
|
*/
|
|
60
64
|
selectByType<T extends AuthCredential["type"]>(provider: string, type: T, sessionId?: string, filter?: (credential: AuthCredential) => boolean): {
|
|
61
65
|
credential: Extract<AuthCredential, {
|
|
@@ -631,6 +631,15 @@ export interface AuthSourceOptions {
|
|
|
631
631
|
*/
|
|
632
632
|
env?: "dedicated" | "aliases" | "none";
|
|
633
633
|
}
|
|
634
|
+
/** Options for {@link KeysApi.peek}. */
|
|
635
|
+
export interface AuthPeekOptions {
|
|
636
|
+
/**
|
|
637
|
+
* Fall back to an expired OAuth access token when no stored account holds an
|
|
638
|
+
* unexpired one. Only for reads that never send the token, e.g. keying an
|
|
639
|
+
* offline model-cache lookup on the account it names.
|
|
640
|
+
*/
|
|
641
|
+
includeExpired?: boolean;
|
|
642
|
+
}
|
|
634
643
|
/** Where a provider's auth comes from ({@link CredentialOrigin}) plus whether any leg holds a concrete key. */
|
|
635
644
|
export interface AuthSource extends CredentialOrigin {
|
|
636
645
|
/** False when the only auth is an ambient AUTHENTICATED_SENTINEL source (AWS profile, ADC). */
|
|
@@ -850,9 +859,10 @@ export interface KeysApi {
|
|
|
850
859
|
* Peek at API key for a provider without refreshing OAuth tokens.
|
|
851
860
|
* Used for model discovery where we only need to know if credentials exist
|
|
852
861
|
* and get a best-effort token. For GitHub Copilot we preserve enterprise
|
|
853
|
-
* routing metadata so discovery can hit the correct host.
|
|
862
|
+
* routing metadata so discovery can hit the correct host. Repeated peeks
|
|
863
|
+
* return the same stored credential; they never rotate accounts.
|
|
854
864
|
*/
|
|
855
|
-
peek(provider: string): Promise<string | undefined>;
|
|
865
|
+
peek(provider: string, options?: AuthPeekOptions): Promise<string | undefined>;
|
|
856
866
|
/** Resolve the first active auth source by precedence without refreshing credentials. */
|
|
857
867
|
source(provider: string, options?: AuthSourceOptions): AuthSource | undefined;
|
|
858
868
|
/**
|
|
@@ -21,3 +21,9 @@ export declare function partialSuffixOverlap(text: string, tag: string): number;
|
|
|
21
21
|
export declare function partialSuffixOverlapAny(text: string, tags: readonly string[]): number;
|
|
22
22
|
export declare function normalizeKimiFunctionName(rawId: string): string;
|
|
23
23
|
export declare function asRecord(value: unknown): Record<string, unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* Extract a COMPLETE tool name from a partial in-band JSON body.
|
|
26
|
+
* Returns undefined until the name string is fully closed, so streaming
|
|
27
|
+
* scanners never start a tool call under a truncated name.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseCompleteToolName(body: string): string | undefined;
|
|
@@ -47,7 +47,7 @@ export declare class AnthropicApiError extends ProviderHttpError {
|
|
|
47
47
|
}
|
|
48
48
|
/** Network-level failure (DNS, TLS, socket reset) after retries were exhausted. */
|
|
49
49
|
export declare class AnthropicConnectionError extends Error {
|
|
50
|
-
constructor(cause: unknown);
|
|
50
|
+
constructor(cause: unknown, requestUrl?: string);
|
|
51
51
|
}
|
|
52
52
|
/** No response headers arrived within the configured request timeout. */
|
|
53
53
|
export declare class AnthropicConnectionTimeoutError extends Error {
|
|
@@ -72,7 +72,10 @@ export declare function isCodexSteerRejection(message: {
|
|
|
72
72
|
* Transport-failure wording that marks an error transient. Covers
|
|
73
73
|
* diagnostics forwarded verbatim by gateways/proxies in other runtimes, e.g.
|
|
74
74
|
* undici's "other side closed" and Go's `net.ErrClosed` ("use of closed
|
|
75
|
-
* network connection", #15385).
|
|
75
|
+
* network connection", #15385). Also covers OMP's own network-failure
|
|
76
|
+
* sentences from `networkFailureMessage` (offline / unreachable / refused /
|
|
77
|
+
* TLS), which must stay at least as retryable as the bare `Connection error.`
|
|
78
|
+
* text they replaced.
|
|
76
79
|
*/
|
|
77
80
|
export declare const TRANSIENT_TRANSPORT_PATTERN: RegExp;
|
|
78
81
|
export declare const ANTHROPIC_ACCOUNT_POLICY_PATTERN: RegExp;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local transport diagnosis for a rejected `fetch`.
|
|
3
|
+
*
|
|
4
|
+
* Bun and Node describe the same outage with different shapes — Bun throws a
|
|
5
|
+
* `TypeError` carrying `code: "ETIMEOUT" | "FailedToOpenSocket" |
|
|
6
|
+
* "ConnectionRefused"`, undici nests an `ENOTFOUND`/`ECONNREFUSED` errno under
|
|
7
|
+
* `TypeError: fetch failed` (sometimes through an `AggregateError`), and
|
|
8
|
+
* `fetchWithRetry` may already have flattened the chain into a message-only
|
|
9
|
+
* `Error`. Both the code and the text are inspected so every one of those
|
|
10
|
+
* shapes lands in the right bucket.
|
|
11
|
+
*/
|
|
12
|
+
/** Transport-level failure families that get their own user-visible wording. */
|
|
13
|
+
export type NetworkFailureKind = "offline" | "unreachable" | "refused" | "tls";
|
|
14
|
+
/** What {@link classifyNetworkCause} reports about a rejected `fetch`: the failure family plus the bits its sentence needs. */
|
|
15
|
+
export interface NetworkFailure {
|
|
16
|
+
kind: NetworkFailureKind;
|
|
17
|
+
/** Runtime error code when the chain exposed one (`ETIMEOUT`, `FailedToOpenSocket`, …). */
|
|
18
|
+
code: string | undefined;
|
|
19
|
+
/** Short parenthetical detail for the user-visible message. */
|
|
20
|
+
detail: string | undefined;
|
|
21
|
+
/** Host named by the error text, when it named one. */
|
|
22
|
+
host: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Walk an error's `cause`/`errors` graph (bounded depth, cycle-safe) and report
|
|
26
|
+
* the outermost link that describes a local transport failure. Returns
|
|
27
|
+
* `undefined` for anything else, including HTTP-level provider errors.
|
|
28
|
+
*/
|
|
29
|
+
export declare function classifyNetworkCause(err: unknown): NetworkFailure | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* User-visible sentence for a network failure, or `undefined` when the error is
|
|
32
|
+
* not one. `requestUrl` wins over any host parsed out of the error text.
|
|
33
|
+
*/
|
|
34
|
+
export declare function networkFailureMessage(err: unknown, requestUrl?: string): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Re-describe a rejected `fetch` with {@link networkFailureMessage}, keeping the
|
|
37
|
+
* original error as `cause`. Anything that is not a local transport failure is
|
|
38
|
+
* returned untouched, so provider/HTTP errors keep their identity.
|
|
39
|
+
*/
|
|
40
|
+
export declare function networkFailureError(err: unknown, requestUrl?: string): unknown;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -64,6 +64,7 @@ export * from "./utils/anthropic-auth.js";
|
|
|
64
64
|
export * from "./utils/event-stream.js";
|
|
65
65
|
export * from "./utils/glyph-codec.js";
|
|
66
66
|
export * from "./utils/openrouter-headers.js";
|
|
67
|
+
export * from "./utils/narration.js";
|
|
67
68
|
export * from "./utils/schema/index.js";
|
|
68
69
|
export * from "./utils/thinking-loop.js";
|
|
69
70
|
export * from "./utils/tool-call-loop-guard.js";
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native judgment client: the {@link Judge} backend for catalog models whose
|
|
3
|
+
* API answers typed questions directly ({@link isJudgmentApi}).
|
|
4
|
+
*
|
|
5
|
+
* Each judgment API is one wire dialect: TypeSafe's System One route
|
|
6
|
+
* (`POST /v1/systemone`), OpenRouter Decisions (`POST /api/alpha/decisions`,
|
|
7
|
+
* same body), Cloudflare Workers AI (`POST /ai/run/{model}`, same body plus
|
|
8
|
+
* top-level data URL images; `systemone.ts`), and OpenAI Decisions
|
|
9
|
+
* (`POST /v1/decisions`, translated both ways; `openai-decisions.ts`).
|
|
10
|
+
* Credentials flow through {@link withAuth}, so a stored key rotates on
|
|
11
|
+
* 401/403 exactly like chat providers; transient 429/5xx responses retry with
|
|
12
|
+
* bounded, `retry-after`-aware backoff. Endpoints that cap the questions per
|
|
13
|
+
* request (the catalog's `judgment-questions-max`, e.g. OpenJEV at 16) get
|
|
14
|
+
* oversized requests split into chunks under `maxQuestions`, at most
|
|
15
|
+
* {@link CHUNK_PARALLEL} in flight at once.
|
|
16
|
+
*
|
|
17
|
+
* Environment (mirrors the official TypeSafe SDK, for judges built without an
|
|
18
|
+
* explicit model): `TYPESAFE_API_KEY` is resolved by the auth registry
|
|
19
|
+
* (`rules/auth/typesafe.kdl`), `TYPESAFE_BASE_URL` overrides the API root,
|
|
20
|
+
* `TYPESAFE_DEFAULT_MODEL` the model.
|
|
21
|
+
*/
|
|
22
|
+
import { type JudgmentImageInput } from "@oh-my-pi/pi-catalog/compat/judgment-images";
|
|
1
23
|
import type { Api, FetchImpl } from "@oh-my-pi/pi-catalog/types";
|
|
2
24
|
import { type ApiKey } from "../auth-retry.js";
|
|
3
25
|
import * as AIError from "../error/index.js";
|
|
@@ -21,12 +43,12 @@ export declare function isJudgmentApi(api: Api): api is JudgmentApi;
|
|
|
21
43
|
export declare function nativeJudgmentSupportsImages(model: Model): boolean;
|
|
22
44
|
/**
|
|
23
45
|
* Whether `model`'s native judgment API takes these exact images: image support plus every documented
|
|
24
|
-
*
|
|
46
|
+
* transport and catalog bound. A judge chain skips a candidate that would refuse them instead of failing on it.
|
|
25
47
|
*/
|
|
26
48
|
export declare function nativeJudgmentAcceptsImages(model: Model, images: readonly ImageContent[]): boolean;
|
|
27
49
|
/**
|
|
28
50
|
* Reject images that `model`'s native judgment API cannot take — no image support, an unsupported or
|
|
29
|
-
* unreferenced asset, or a
|
|
51
|
+
* unreferenced asset, or a transport or catalog bound exceeded — before any credential or upstream work.
|
|
30
52
|
* @throws {AIError.ValidationError} naming the reason.
|
|
31
53
|
*/
|
|
32
54
|
export declare function checkNativeJudgmentImages(model: Model, request: JudgmentRequest, imageAssets?: JudgmentImageAssets): void;
|
|
@@ -62,6 +84,8 @@ export interface NativeJudgeOptions extends NativeJudgeConnection {
|
|
|
62
84
|
* dialects that require explicit opt-in but cannot enable a wire without image support.
|
|
63
85
|
*/
|
|
64
86
|
supportsImages?: boolean;
|
|
87
|
+
/** Catalog-selected image bounds, applied in addition to the wire's transport constraints. */
|
|
88
|
+
imageInput?: JudgmentImageInput;
|
|
65
89
|
}
|
|
66
90
|
/** Per-call options for {@link NativeJudge.judge}. */
|
|
67
91
|
export interface NativeJudgeCallOptions extends JudgeOptions {
|
|
@@ -90,6 +114,8 @@ export declare class NativeJudge implements Judge {
|
|
|
90
114
|
readonly baseUrl: string;
|
|
91
115
|
constructor(options: NativeJudgeOptions);
|
|
92
116
|
judge<Q extends Questions>(request: JudgmentRequest<Q>, options?: NativeJudgeCallOptions): Promise<JudgmentResult<Q>>;
|
|
117
|
+
/** Effective image detail for cache identity after applying this wire's forwarding behavior. */
|
|
118
|
+
effectiveImageDetail(detail: ImageContent["detail"]): ImageContent["detail"];
|
|
93
119
|
}
|
|
94
120
|
/**
|
|
95
121
|
* The {@link NativeJudge} for a catalog judge model: its API's dialect, wire id
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* judgment module; `native.ts` maps every judgment API to its dialect.
|
|
5
5
|
*/
|
|
6
6
|
import type { ImageContent, Usage } from "../types.js";
|
|
7
|
+
import type { JudgmentImageInput } from "@oh-my-pi/pi-catalog/compat/judgment-images";
|
|
7
8
|
import type { JudgmentRequest } from "./types.js";
|
|
8
9
|
/** Where one attempt posts and which bearer token it sends. */
|
|
9
10
|
export interface JudgmentTarget {
|
|
@@ -20,36 +21,6 @@ export interface DecodedJudgment {
|
|
|
20
21
|
refused: readonly string[];
|
|
21
22
|
usage: Usage;
|
|
22
23
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Image input one judgment dialect accepts. Every cap is optional; {@link NativeJudge}
|
|
25
|
-
* rejects a request exceeding one locally instead of spending an upstream call on it.
|
|
26
|
-
*/
|
|
27
|
-
export interface JudgmentImageInput {
|
|
28
|
-
/** Accepted lowercase MIME types; unset accepts any image type. */
|
|
29
|
-
mimeTypes?: readonly string[];
|
|
30
|
-
/** Images per request. */
|
|
31
|
-
maxCount?: number;
|
|
32
|
-
/** Decoded bytes per image. */
|
|
33
|
-
maxBytes?: number;
|
|
34
|
-
/** Decoded bytes across all images. */
|
|
35
|
-
maxTotalBytes?: number;
|
|
36
|
-
/** Pixels per image, when the image header reports dimensions. */
|
|
37
|
-
maxPixels?: number;
|
|
38
|
-
/** Minimum pixels per image, when the image header reports dimensions. */
|
|
39
|
-
minPixels?: number;
|
|
40
|
-
/** Inclusive bounds on each side, when the image header reports dimensions. */
|
|
41
|
-
minSide?: number;
|
|
42
|
-
maxSide?: number;
|
|
43
|
-
}
|
|
44
|
-
/** Shared TypeSafe Jev/OpenRouter image limits (https://docs.typesafe.ai/images#limits); out-of-range images are rejected upstream, never resized. */
|
|
45
|
-
export declare const JEV_IMAGE_INPUT: {
|
|
46
|
-
readonly mimeTypes: readonly ["image/png", "image/jpeg"];
|
|
47
|
-
readonly maxBytes: number;
|
|
48
|
-
readonly minPixels: 78400;
|
|
49
|
-
readonly maxPixels: 2458624;
|
|
50
|
-
readonly minSide: 280;
|
|
51
|
-
readonly maxSide: 1568;
|
|
52
|
-
};
|
|
53
24
|
/** Image assets kept separately from judgment state for asset-aware native wires. */
|
|
54
25
|
export type JudgmentImageAssets = Readonly<Record<string, ImageContent>>;
|
|
55
26
|
/** How one judgment API addresses, encodes, and decodes a request. */
|
|
@@ -69,6 +40,8 @@ export interface JudgmentWire {
|
|
|
69
40
|
supportsImageAssets?: boolean;
|
|
70
41
|
/** Whether image support must be enabled explicitly for direct NativeJudge construction. */
|
|
71
42
|
imagesRequireOptIn?: boolean;
|
|
43
|
+
/** Whether this wire drops per-image detail metadata. */
|
|
44
|
+
ignoresImageDetail?: boolean;
|
|
72
45
|
/** The response's answers and usage; `undefined` when the payload is not this API's response shape. */
|
|
73
46
|
decode(payload: unknown): DecodedJudgment | undefined;
|
|
74
47
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Reads the cleartext header Anthropic embeds in thinking-block signatures.
|
|
3
3
|
*
|
|
4
4
|
* A signature is a base64 protobuf: outer `{1: version, 2: envelope}`, envelope
|
|
5
5
|
* `{1: header, 2..5: nonce/ciphertext/tag}`, header
|
|
6
|
-
* `{1: schema, 5: hash, 6: model, 8:
|
|
6
|
+
* `{1: schema, 5: hash, 6: model, 8: kind, 11: org uuid, 15: profile, 21: unix ts}`.
|
|
7
7
|
* Field 6 names the model that actually produced the block — a concrete
|
|
8
8
|
* snapshot (`claude-haiku-4-5-20251001`) or an internal codename
|
|
9
9
|
* (`numbat-v6-…`) — which lets a caller notice a gateway serving something other
|
|
10
10
|
* than the requested model. The v4 outer format (`CAQS…`) ships a header
|
|
11
11
|
* without field 6; the decoder then yields nothing.
|
|
12
|
+
* Field 8 names the block kind: `thinking` for reasoning, `narration` for the
|
|
13
|
+
* user-facing prose the API converts into a thinking block when the model
|
|
14
|
+
* writes it between tool calls (connector-text summarization).
|
|
12
15
|
*/
|
|
13
16
|
/**
|
|
14
17
|
* Model id embedded in an Anthropic thinking signature, or undefined when the
|
|
@@ -16,6 +19,13 @@
|
|
|
16
19
|
* nothing resembling a model id.
|
|
17
20
|
*/
|
|
18
21
|
export declare function servedModelFromAnthropicSignature(signature: string): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Whether an Anthropic thinking signature marks its block as narration: prose
|
|
24
|
+
* the model addressed to the user between tool calls, which the API returns as
|
|
25
|
+
* a thinking block. Only an exact `narration` kind matches; any other or
|
|
26
|
+
* unreadable header stays reasoning.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isAnthropicNarrationSignature(signature: string): boolean;
|
|
19
29
|
/**
|
|
20
30
|
* Model id from an OpenRouter reasoning item — a `reasoning_details` entry on
|
|
21
31
|
* chat completions or a Responses `reasoning` output item — which forwards the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effort } from "@oh-my-pi/pi-catalog/effort";
|
|
2
|
-
import type { AnthropicCompactionRequest, AnthropicOutputEffort, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
|
|
2
|
+
import type { AnthropicCompactionRequest, AnthropicOutputEffort, Api, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
|
|
3
3
|
import { type AnthropicFetchOptions, type AnthropicMessagesClientLike } from "./anthropic-client.js";
|
|
4
4
|
import { type FallbackParam, type MessageParam, type TextBlockParam } from "./anthropic-wire.js";
|
|
5
5
|
import { resolvesToOfficialAnthropicEndpoint, supportsAnthropicCompaction, supportsAnthropicCompactionOnClient } from "./anthropic-compaction.js";
|
|
@@ -243,6 +243,17 @@ type SystemBlockOptions = {
|
|
|
243
243
|
export declare function buildAnthropicSystemBlocks(systemPrompt: readonly string[] | undefined, options?: SystemBlockOptions): AnthropicSystemBlock[] | undefined;
|
|
244
244
|
export declare function normalizeExtraBetas(betas?: string[] | string): string[];
|
|
245
245
|
export declare function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): AnthropicClientOptionsResult;
|
|
246
|
+
/**
|
|
247
|
+
* Warm-up target for a first-party `anthropic` model: the effective official
|
|
248
|
+
* API URL plus the TLS options real requests use (sockets pool per TLS
|
|
249
|
+
* configuration, so a warm-up made with other options is never reused).
|
|
250
|
+
* `undefined` when requests resolve elsewhere — `ANTHROPIC_BASE_URL` or
|
|
251
|
+
* Foundry gateways never receive unsolicited probes.
|
|
252
|
+
*/
|
|
253
|
+
export declare function resolveAnthropicPrewarmTarget(model: Model<Api>): {
|
|
254
|
+
url: string;
|
|
255
|
+
init: RequestInit;
|
|
256
|
+
} | undefined;
|
|
246
257
|
/**
|
|
247
258
|
* True when enabled thinking on `model` is budget thinking
|
|
248
259
|
* (`thinking.type: "enabled"` with `budget_tokens`) rather than adaptive.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Api, FetchImpl, Model } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Opens, or keeps alive, a pooled connection to {@link model}'s API host so
|
|
4
|
+
* the next turn skips DNS + TCP + TLS. Sends a credential-less `HEAD` (nothing
|
|
5
|
+
* is billed; hosts answer 401/404/405) through the same transport — proxy,
|
|
6
|
+
* extra CA, Cowork profile, TLS options — as real requests, because sockets
|
|
7
|
+
* pool per transport configuration and a warm-up made any other way (e.g.
|
|
8
|
+
* `fetch.preconnect`, which also throws on Windows) is never reused.
|
|
9
|
+
* Throttled per origin; best effort, never throws.
|
|
10
|
+
*/
|
|
11
|
+
export declare function prewarmModelConnection(model: Model<Api>, options?: {
|
|
12
|
+
fetch?: FetchImpl;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
/** Test hook: forgets every warm-up so the next call is not throttled. */
|
|
15
|
+
export declare function resetConnectionPrewarmForTesting(): void;
|
|
@@ -29,12 +29,14 @@ export declare function isResponsesFamilyApi(api: Api | undefined): boolean;
|
|
|
29
29
|
export declare function responsesCallComponent(id: string): string;
|
|
30
30
|
export declare function appendDuplicateSuffix(originalId: string, suffix: string, maxLength: number): string;
|
|
31
31
|
/**
|
|
32
|
-
* Whether a tool-call name cannot
|
|
33
|
-
* longer than
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Whether a tool-call name cannot be replayed to the target: missing, empty,
|
|
33
|
+
* longer than its declaration limit, or containing a character outside its
|
|
34
|
+
* declaration charset. Models emit such names when the name slot carries
|
|
35
|
+
* invocation text (whitespace, NULs) or an invented internal URL (`proc://*`).
|
|
36
|
+
*
|
|
37
|
+
* Without a target, use the OpenAI Responses native-history constraints.
|
|
36
38
|
*/
|
|
37
|
-
export declare function isMalformedToolCallName(name: unknown): boolean;
|
|
39
|
+
export declare function isMalformedToolCallName(name: unknown, allowedName?: RegExp, maxLength?: number): boolean;
|
|
38
40
|
/**
|
|
39
41
|
* Whether {@link transformMessages} drops `msg` from every replay: a turn cut
|
|
40
42
|
* off by `length` / `error` / `aborted` that carries no text and no tool call.
|
|
@@ -45,9 +47,11 @@ export declare function shouldDropTruncatedThinkingOnlyAssistant(msg: AssistantM
|
|
|
45
47
|
* surviving one: {@link sanitizeMalformedToolCalls} drops its malformed tool
|
|
46
48
|
* calls, then {@link shouldDropTruncatedThinkingOnlyAssistant} a truncated
|
|
47
49
|
* turn left with no text and no tool call. On an already sanitized list this
|
|
48
|
-
* is `!shouldDropTruncatedThinkingOnlyAssistant(msg)`.
|
|
50
|
+
* is `!shouldDropTruncatedThinkingOnlyAssistant(msg)`. Pass the target `model`
|
|
51
|
+
* to apply its tool-name limits; without one, any provider-declarable name survives.
|
|
49
52
|
*/
|
|
50
|
-
export declare function isSurvivingAssistant(msg: AssistantMessage): boolean;
|
|
53
|
+
export declare function isSurvivingAssistant(msg: AssistantMessage, model?: Model): boolean;
|
|
54
|
+
export declare function isAnthropicMessagesModel(model: Model): model is Model<"anthropic-messages">;
|
|
51
55
|
/**
|
|
52
56
|
* Normalize tool call ID for cross-provider compatibility.
|
|
53
57
|
* OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
|
package/dist/types/types.d.ts
CHANGED
|
@@ -648,6 +648,13 @@ export interface ThinkingContent {
|
|
|
648
648
|
* replay natively on another host.
|
|
649
649
|
*/
|
|
650
650
|
summary?: boolean;
|
|
651
|
+
/**
|
|
652
|
+
* `true`: the block is narration — prose the model addressed to the user
|
|
653
|
+
* between tool calls, which the provider returned as a signed thinking block
|
|
654
|
+
* (Anthropic connector-text summarization). It stays a thinking block for
|
|
655
|
+
* replay; user-facing surfaces present it as answer text.
|
|
656
|
+
*/
|
|
657
|
+
narration?: boolean;
|
|
651
658
|
}
|
|
652
659
|
export interface RedactedThinkingContent {
|
|
653
660
|
type: "redactedThinking";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AssistantMessage } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The message as a user-facing surface should present it: every narration
|
|
4
|
+
* thinking block (see `ThinkingContent.narration`) becomes a text block, so it
|
|
5
|
+
* renders as part of the answer instead of folding into reasoning. Returns the
|
|
6
|
+
* input unchanged when it holds no narration. Display-only: replay must keep
|
|
7
|
+
* the original thinking blocks and their signatures.
|
|
8
|
+
*/
|
|
9
|
+
export declare function presentNarrationAsAnswer(message: AssistantMessage): AssistantMessage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bytes of a request body as the server reads them: the `gzip`/`zstd`
|
|
3
|
+
* `Content-Encoding` applied by request compression is undone, any other
|
|
4
|
+
* encoding is returned untouched.
|
|
5
|
+
*/
|
|
6
|
+
export declare function decodeRequestBodyBytes(bytes: Uint8Array, contentEncoding: string | null): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Text of a string or byte request body after undoing its content encoding;
|
|
9
|
+
* `undefined` when the request has no such body (none, stream, form data).
|
|
10
|
+
*/
|
|
11
|
+
export declare function requestBodyText(init: RequestInit | undefined): string | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FetchImpl, Model } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compresses request bodies from {@link model} to its provider's own endpoint
|
|
4
|
+
* when its catalog rules declare encodings the endpoint decodes
|
|
5
|
+
* (`request-encodings` / `endpoint-hosts` KDL axes). Long sessions resend
|
|
6
|
+
* multi-MB transcripts every turn, so upload time dominates time-to-first-token
|
|
7
|
+
* on ordinary uplinks.
|
|
8
|
+
*
|
|
9
|
+
* Install it beneath any body rewriter or signer (e.g. the cch attestation
|
|
10
|
+
* patch): those must see the uncompressed bytes. Other hosts, small bodies,
|
|
11
|
+
* bodies that already carry `Content-Encoding`, and `PI_REQUEST_COMPRESSION=0`
|
|
12
|
+
* pass through untouched. A possible rejection is resent uncompressed once;
|
|
13
|
+
* only when that resend succeeds is the encoding latched off for the host, so
|
|
14
|
+
* a genuine 400 never disables compression.
|
|
15
|
+
*/
|
|
16
|
+
export declare function wrapFetchForRequestCompression(base: FetchImpl, model: Model): FetchImpl;
|
|
17
|
+
/** Test hook: clears every per-host rejection latch. */
|
|
18
|
+
export declare function resetRequestCompressionForTesting(): void;
|
|
@@ -12,6 +12,14 @@ import type { Api, FetchImpl, Model } from "../types.js";
|
|
|
12
12
|
* layer (three PI_REQ_DEBUG dumps for one request).
|
|
13
13
|
*/
|
|
14
14
|
export declare function transportFetch(model: Model<Api>, fetchImpl: FetchImpl | undefined): FetchImpl;
|
|
15
|
+
/**
|
|
16
|
+
* Origin of the last inference request {@link model} actually sent, after
|
|
17
|
+
* every dispatch-time rewrite (credential endpoints, env overrides, regional
|
|
18
|
+
* hosts); `undefined` before its first request.
|
|
19
|
+
*/
|
|
20
|
+
export declare function lastDispatchOrigin(model: Model<Api>): string | undefined;
|
|
21
|
+
/** Test hook: forgets every recorded dispatch origin. */
|
|
22
|
+
export declare function resetDispatchOriginsForTesting(): void;
|
|
15
23
|
/** Options-bag form of {@link transportFetch}; returns `options` untouched when its fetch is already built. */
|
|
16
24
|
export declare function withTransportFetch<T extends {
|
|
17
25
|
fetch?: FetchImpl;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.12.0",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -155,11 +155,11 @@
|
|
|
155
155
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@oh-my-pi/omptype": "18.
|
|
159
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
160
|
-
"@oh-my-pi/pi-natives": "18.
|
|
161
|
-
"@oh-my-pi/pi-utils": "18.
|
|
162
|
-
"@oh-my-pi/pi-wire": "18.
|
|
158
|
+
"@oh-my-pi/omptype": "18.12.0",
|
|
159
|
+
"@oh-my-pi/pi-catalog": "18.12.0",
|
|
160
|
+
"@oh-my-pi/pi-natives": "18.12.0",
|
|
161
|
+
"@oh-my-pi/pi-utils": "18.12.0",
|
|
162
|
+
"@oh-my-pi/pi-wire": "18.12.0"
|
|
163
163
|
},
|
|
164
164
|
"devDependencies": {
|
|
165
165
|
"@types/bun": "^1.3.14"
|