@linxiraos/pi-ai 1.2.1 → 1.3.1
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 +98 -0
- package/README.md +4 -2
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/auth/cascade.d.ts +5 -3
- package/dist/types/auth/pool.d.ts +3 -3
- package/dist/types/auth/select.d.ts +5 -1
- package/dist/types/auth/types.d.ts +15 -5
- package/dist/types/auth/usage-cache.d.ts +3 -1
- package/dist/types/auth-gateway/clients.d.ts +67 -0
- package/dist/types/auth-gateway/dispatch.d.ts +28 -10
- package/dist/types/auth-gateway/http.d.ts +2 -0
- package/dist/types/auth-gateway/index.d.ts +1 -0
- package/dist/types/auth-gateway/server.d.ts +9 -4
- package/dist/types/auth-gateway/session-state.d.ts +6 -0
- package/dist/types/auth-gateway/types.d.ts +16 -2
- package/dist/types/dialect/coercion.d.ts +6 -0
- package/dist/types/dialect/deepseek.d.ts +13 -0
- package/dist/types/error/classes.d.ts +1 -1
- package/dist/types/error/flags.d.ts +15 -0
- package/dist/types/error/index.d.ts +1 -0
- package/dist/types/error/network-cause.d.ts +40 -0
- package/dist/types/images/shared.d.ts +26 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/judgment/native.d.ts +62 -3
- package/dist/types/judgment/systemone.d.ts +9 -2
- package/dist/types/judgment/text.d.ts +11 -5
- package/dist/types/judgment/types.d.ts +37 -6
- package/dist/types/judgment/wire.d.ts +18 -3
- package/dist/types/provider-session-state.d.ts +2 -2
- package/dist/types/providers/anthropic-images.d.ts +7 -0
- package/dist/types/providers/anthropic-signature.d.ts +12 -2
- package/dist/types/providers/anthropic-state.d.ts +7 -0
- package/dist/types/providers/anthropic.d.ts +21 -6
- package/dist/types/providers/connection-prewarm.d.ts +15 -0
- package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
- package/dist/types/providers/cursor-pi-args.d.ts +13 -0
- package/dist/types/providers/cursor.d.ts +7 -0
- package/dist/types/providers/ollama.d.ts +6 -1
- package/dist/types/providers/openai-codex-responses.d.ts +6 -0
- package/dist/types/providers/openai-responses.d.ts +9 -0
- package/dist/types/providers/openai-shared.d.ts +30 -0
- package/dist/types/providers/systemone-server.d.ts +7 -3
- package/dist/types/providers/transform-messages.d.ts +28 -5
- package/dist/types/registry/oauth/google-antigravity.d.ts +1 -1
- package/dist/types/stream.d.ts +0 -2
- package/dist/types/types.d.ts +33 -9
- package/dist/types/usage/muse-code.d.ts +1 -1
- package/dist/types/utils/dsml-leak.d.ts +17 -13
- package/dist/types/utils/narration.d.ts +9 -0
- package/dist/types/utils/openai-http.d.ts +2 -0
- package/dist/types/utils/request-body.d.ts +11 -0
- package/dist/types/utils/request-compression.d.ts +21 -0
- package/dist/types/utils/tool-call-arguments.d.ts +2 -1
- package/dist/types/utils/transport-fetch.d.ts +8 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +7 -7
- package/src/auth/cascade.ts +23 -13
- package/src/auth/pool.ts +3 -3
- package/src/auth/refresh.ts +39 -12
- package/src/auth/select.ts +8 -2
- package/src/auth/types.ts +16 -5
- package/src/auth/usage-cache.ts +23 -7
- package/src/auth/usage.ts +2 -4
- package/src/auth-broker/remote-store.ts +31 -6
- package/src/auth-gateway/clients.ts +318 -0
- package/src/auth-gateway/dispatch.ts +66 -35
- package/src/auth-gateway/http.ts +11 -5
- package/src/auth-gateway/index.ts +1 -0
- package/src/auth-gateway/routes/embeddings.ts +4 -10
- package/src/auth-gateway/routes/images.ts +6 -10
- package/src/auth-gateway/routes/rerank.ts +4 -10
- package/src/auth-gateway/routes/speech.ts +10 -10
- package/src/auth-gateway/routes/systemone.ts +22 -12
- package/src/auth-gateway/routes/transcriptions.ts +4 -2
- package/src/auth-gateway/routes/video.ts +12 -15
- package/src/auth-gateway/server.ts +136 -53
- package/src/auth-gateway/session-state.ts +14 -0
- package/src/auth-gateway/types.ts +16 -2
- package/src/dialect/coercion.ts +16 -0
- package/src/dialect/deepseek.ts +344 -81
- 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 +24 -3
- package/src/error/index.ts +1 -0
- package/src/error/network-cause.ts +179 -0
- package/src/images/codex-format-directive.md +3 -0
- package/src/images/google-antigravity.ts +2 -1
- package/src/images/google-generative-ai.ts +4 -2
- package/src/images/openai-hosted.ts +25 -2
- package/src/images/openai-images.ts +11 -6
- package/src/images/shared.ts +77 -3
- package/src/index.ts +1 -0
- package/src/judgment/chat.ts +17 -1
- package/src/judgment/native.ts +238 -12
- package/src/judgment/openai-decisions.ts +30 -6
- package/src/judgment/systemone.ts +153 -11
- package/src/judgment/text.ts +26 -7
- package/src/judgment/types.ts +46 -6
- package/src/judgment/wire.ts +19 -3
- package/src/provider-session-state.ts +2 -2
- package/src/providers/amazon-bedrock.ts +4 -1
- package/src/providers/anthropic-client.ts +1 -1
- package/src/providers/anthropic-images.ts +263 -0
- package/src/providers/anthropic-messages-server.ts +84 -1
- package/src/providers/anthropic-signature.ts +35 -8
- package/src/providers/anthropic-state.ts +13 -0
- package/src/providers/anthropic.ts +479 -397
- package/src/providers/connection-prewarm.ts +78 -0
- package/src/providers/cowork-fetch.ts +5 -1
- package/src/providers/cursor/exec-modern.ts +1 -0
- package/src/providers/cursor-pi-args.ts +19 -0
- package/src/providers/cursor.ts +53 -13
- package/src/providers/devin.ts +10 -4
- package/src/providers/factory-droid/gemini.ts +9 -3
- package/src/providers/google-gemini-cli.ts +11 -1
- package/src/providers/google-shared.ts +2 -6
- package/src/providers/images-server.ts +6 -1
- package/src/providers/ollama.ts +23 -5
- package/src/providers/openai-anthropic-shim.ts +1 -0
- package/src/providers/openai-chat-server.ts +29 -0
- package/src/providers/openai-codex/access-programs.ts +2 -1
- package/src/providers/openai-codex-responses.ts +49 -82
- package/src/providers/openai-completions.ts +117 -19
- package/src/providers/openai-responses.ts +25 -2
- package/src/providers/openai-shared.ts +36 -5
- package/src/providers/pi-native-server.ts +1 -0
- package/src/providers/systemone-server.ts +72 -12
- package/src/providers/transform-messages.ts +104 -55
- package/src/registry/oauth/google-antigravity.ts +1 -1
- package/src/stream.ts +13 -73
- package/src/types.ts +33 -9
- package/src/usage/claude.ts +2 -9
- package/src/usage/github-copilot.ts +26 -4
- package/src/usage/muse-code.ts +115 -29
- package/src/usage/openai-codex.ts +13 -7
- package/src/usage/opencode-go.ts +7 -8
- package/src/usage/xai-oauth.ts +2 -9
- package/src/utils/dsml-leak.ts +88 -33
- package/src/utils/leaked-thinking-stream.ts +10 -0
- package/src/utils/narration.ts +18 -0
- package/src/utils/openai-http.ts +27 -18
- package/src/utils/request-body.ts +27 -0
- package/src/utils/request-compression.ts +202 -0
- package/src/utils/request-debug.ts +23 -9
- package/src/utils/schema/dereference.ts +51 -18
- 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/tool-call-arguments.ts +2 -1
- package/src/utils/transport-fetch.ts +37 -0
- package/src/utils/validation.ts +26 -16
- package/src/utils.ts +38 -23
|
@@ -218,6 +218,13 @@ export declare function mergeCursorMcpToolCallArgs(streamed: Record<string, unkn
|
|
|
218
218
|
* call handed to an external executor is only a handoff while the marker is
|
|
219
219
|
* absent, and the consumer that runs it needs a runnable block.
|
|
220
220
|
*
|
|
221
|
+
* A frame whose `toolCallId` already has a block in `output` is the server
|
|
222
|
+
* re-dispatching that call (a new `execId`, e.g. a `read` re-sent once a
|
|
223
|
+
* parallel shell created the file). It adds no block: a second block with the
|
|
224
|
+
* same id makes every rebuilt history replay the call twice, which Cursor
|
|
225
|
+
* rejects (issue #15749). The re-run's result supersedes the first one in the
|
|
226
|
+
* host's result buffer.
|
|
227
|
+
*
|
|
221
228
|
* Exported for tests to exercise ordering with adjacent text/thinking blocks.
|
|
222
229
|
*/
|
|
223
230
|
export declare function synthesizeCursorExecToolCall(output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, toolCallId: string, toolName: string, args: Record<string, unknown>, { executed }?: {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import type { StreamFunction, StreamOptions, ToolChoice } from "../types.js";
|
|
1
|
+
import type { Model, StreamFunction, StreamOptions, ToolChoice } from "../types.js";
|
|
2
2
|
export interface OllamaChatOptions extends StreamOptions {
|
|
3
3
|
reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
4
4
|
disableReasoning?: boolean;
|
|
5
5
|
toolChoice?: ToolChoice;
|
|
6
6
|
}
|
|
7
|
+
/** Whether Ollama chat removes assistant thinking from replayed history. */
|
|
8
|
+
export declare function ollamaChatDropsOwnThinking(model: {
|
|
9
|
+
api?: string;
|
|
10
|
+
compat?: Model["compat"];
|
|
11
|
+
} | null | undefined): boolean;
|
|
7
12
|
/** Retry EOS-only Ollama completions before the agent loop sees an empty stop. */
|
|
8
13
|
export declare const streamOllama: StreamFunction<"ollama-chat">;
|
|
@@ -6,6 +6,12 @@ export type { CodexAttestationProvider } from "./openai-codex-attestation.js";
|
|
|
6
6
|
import type { ResponseInput } from "./openai-responses-wire.js";
|
|
7
7
|
export interface OpenAICodexResponsesOptions extends StreamOptions {
|
|
8
8
|
reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
9
|
+
/**
|
|
10
|
+
* Run with the least reasoning the model allows; takes precedence over
|
|
11
|
+
* `reasoning`. Sends the `none` tier where the catalog's disable mode is
|
|
12
|
+
* `none-effort`, else the model's lowest supported effort.
|
|
13
|
+
*/
|
|
14
|
+
disableReasoning?: boolean;
|
|
9
15
|
reasoningSummary?: "auto" | "concise" | "detailed" | null;
|
|
10
16
|
/** Explicit `reasoning.context` replay scope. Omitted by default so Codex applies its native request policy. */
|
|
11
17
|
reasoningContext?: CodexReasoningContext;
|
|
@@ -114,6 +114,15 @@ interface OpenAIResponsesChainState {
|
|
|
114
114
|
/** Set once chaining is judged unsupported for this session (circuit breaker). */
|
|
115
115
|
disabled: boolean;
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether the next request for `model` replays native Responses history items
|
|
119
|
+
* (encrypted reasoning, item ids), as {@link buildParams} decides it. A session
|
|
120
|
+
* is cold, rebuilding turns from message content, from the first request on a
|
|
121
|
+
* connection-bound host (`compat.connectionBoundNativeHistory`, GitHub Copilot)
|
|
122
|
+
* and after `close()` on any host, until a response succeeds. Without session
|
|
123
|
+
* state every request replays.
|
|
124
|
+
*/
|
|
125
|
+
export declare function openAIResponsesReplaysNativeHistory(model: Pick<Model, "api" | "provider" | "compat"> | null | undefined, providerSessionState: Map<string, ProviderSessionState> | undefined): boolean;
|
|
117
126
|
/**
|
|
118
127
|
* Set the process-wide storage default per provider id, used when a request
|
|
119
128
|
* leaves `storeResponses` unset. Hosts call this from their settings so every
|
|
@@ -399,6 +399,22 @@ interface StrictToolsRetryContext {
|
|
|
399
399
|
strictToolsApplied: boolean;
|
|
400
400
|
tools: Tool[] | undefined;
|
|
401
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Read OpenRouter's `error.metadata.raw` from a captured non-2xx body.
|
|
404
|
+
*
|
|
405
|
+
* OpenRouter routes to a different upstream per request, so its top-level
|
|
406
|
+
* `error.message` is a generic policy sentence ("Access denied by security
|
|
407
|
+
* policy") and the only place the routed provider's own words appear is
|
|
408
|
+
* `error.metadata.raw`. Providers that proxy through OpenRouter also hang the
|
|
409
|
+
* upstream provider name there.
|
|
410
|
+
*
|
|
411
|
+
* The detail lives in the decoded body captured on the thrown
|
|
412
|
+
* {@link AIError.OpenAIHttpError}, not on the error object itself: no error
|
|
413
|
+
* class in this package carries an `error` property, so reading
|
|
414
|
+
* `error.error.metadata.raw` unconditionally yielded `undefined` and the
|
|
415
|
+
* upstream detail was dropped from every surfaced message.
|
|
416
|
+
*/
|
|
417
|
+
export declare function readOpenRouterRawMetadata(capturedErrorResponse: CapturedHttpErrorResponse | undefined): string | undefined;
|
|
402
418
|
/** Decide whether an OpenAI-family request should retry once with non-strict tools. */
|
|
403
419
|
export declare function shouldRetryWithoutStrictTools(error: unknown, capturedErrorResponse: CapturedHttpErrorResponse | undefined, context: StrictToolsRetryContext): boolean;
|
|
404
420
|
export declare const OPENAI_RESPONSES_PROGRESS_EVENT_TYPES: ReadonlySet<string>;
|
|
@@ -525,6 +541,20 @@ export interface BuildResponsesInputOptions<TApi extends Api> {
|
|
|
525
541
|
* Callers gate on {@link isHarmonyDialectModel}. Items are copied, not mutated.
|
|
526
542
|
*/
|
|
527
543
|
export declare function escapeReplayedControlTokens(items: ResponseInput): ResponseInput;
|
|
544
|
+
/**
|
|
545
|
+
* Whether a same-provider assistant turn may replay its native items while the
|
|
546
|
+
* provider session is still cold (#489). A cold session rebuilds turns from
|
|
547
|
+
* message content because some backends bind native items to one connection
|
|
548
|
+
* (GitHub Copilot: `401 input item does not belong to this connection`, #488).
|
|
549
|
+
* Binding needs server-issued state that survives replay sanitization: an
|
|
550
|
+
* `encrypted_content` blob or an item id. A turn with neither, whose every
|
|
551
|
+
* reasoning item carries plaintext `reasoning_text`, has nothing to bind and is
|
|
552
|
+
* exactly what the server receives once the session warms; rebuilding it would
|
|
553
|
+
* drop that reasoning and change the prompt prefix the server cached.
|
|
554
|
+
* Summary-only reasoning keeps the rebuild: it is no evidence of a server that
|
|
555
|
+
* returns plaintext reasoning.
|
|
556
|
+
*/
|
|
557
|
+
export declare function isColdReplayableResponsesTurn(items: ResponseInput): boolean;
|
|
528
558
|
export declare function buildResponsesInput<TApi extends Api>(options: BuildResponsesInputOptions<TApi>): ResponseInput;
|
|
529
559
|
/**
|
|
530
560
|
* Non-empty `reasoning_text` shipped for a synthesized reasoning item when no
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { JudgmentRequest, JudgmentResult } from "../judgment/types.js";
|
|
2
|
+
import type { JudgmentImageAssets } from "../judgment/wire.js";
|
|
2
3
|
export interface SystemOneParsedRequest {
|
|
3
4
|
modelId: string;
|
|
4
5
|
request: JudgmentRequest;
|
|
6
|
+
imageAssets?: JudgmentImageAssets;
|
|
5
7
|
}
|
|
6
8
|
/** `POST /v1/systemone` response body; `cost` (USD) appears only when the route billed one. */
|
|
7
9
|
export interface SystemOneResponseBody {
|
|
@@ -14,10 +16,12 @@ export interface SystemOneResponseBody {
|
|
|
14
16
|
};
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
|
-
* Validate the routing-relevant parts of a System One request body
|
|
19
|
+
* Validate the routing-relevant parts of a System One request body, including
|
|
20
|
+
* top-level embedded images and inline TypeSafe image assets.
|
|
18
21
|
*
|
|
19
|
-
* @throws {AIError.ValidationError}
|
|
20
|
-
*
|
|
22
|
+
* @throws {AIError.ValidationError} when required routing fields are missing
|
|
23
|
+
* or malformed, supplied image data is invalid, or an asset uses multipart
|
|
24
|
+
* `part`, which this JSON ingress cannot read.
|
|
21
25
|
*/
|
|
22
26
|
export declare function parseRequest(body: unknown): SystemOneParsedRequest;
|
|
23
27
|
/** Re-encode a judgment result in TypeSafe's response shape. */
|
|
@@ -9,6 +9,10 @@ import type { Api, AssistantMessage, Message, Model } from "../types.js";
|
|
|
9
9
|
* MUST not push a normalized id past this bound.
|
|
10
10
|
*/
|
|
11
11
|
export declare const MAX_TOOL_CALL_ID_LENGTH = 64;
|
|
12
|
+
/** Replace unsupported tool-call ID characters and cap the result for strict replay providers. */
|
|
13
|
+
export declare function normalizeToolCallId(id: string, maxLength?: number): string;
|
|
14
|
+
/** Whether `id` already satisfies the strict 1–64 `[a-zA-Z0-9_-]` tool-call ID contract. */
|
|
15
|
+
export declare function isValidToolCallId(id: string): boolean;
|
|
12
16
|
/**
|
|
13
17
|
* OpenAI Responses-family APIs mint composite tool ids (`call_id|item_id`);
|
|
14
18
|
* opaque Chat Completions ids do not (openai-completions preserves same-model
|
|
@@ -25,12 +29,31 @@ export declare function isResponsesFamilyApi(api: Api | undefined): boolean;
|
|
|
25
29
|
export declare function responsesCallComponent(id: string): string;
|
|
26
30
|
export declare function appendDuplicateSuffix(originalId: string, suffix: string, maxLength: number): string;
|
|
27
31
|
/**
|
|
28
|
-
* Whether a tool-call name cannot
|
|
29
|
-
* longer than
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isMalformedToolCallName(name: unknown, allowedName?: RegExp, maxLength?: number): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether {@link transformMessages} drops `msg` from every replay: a turn cut
|
|
42
|
+
* off by `length` / `error` / `aborted` that carries no text and no tool call.
|
|
43
|
+
*/
|
|
44
|
+
export declare function shouldDropTruncatedThinkingOnlyAssistant(msg: AssistantMessage): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether `msg` reaches a replay as an assistant turn, so it can be the latest
|
|
47
|
+
* surviving one: {@link sanitizeMalformedToolCalls} drops its malformed tool
|
|
48
|
+
* calls, then {@link shouldDropTruncatedThinkingOnlyAssistant} a truncated
|
|
49
|
+
* turn left with no text and no tool call. On an already sanitized list this
|
|
50
|
+
* is `!shouldDropTruncatedThinkingOnlyAssistant(msg)`. Pass the target `model`
|
|
51
|
+
* to apply its tool-name limits; without one, any provider-declarable name survives.
|
|
32
52
|
*/
|
|
33
|
-
export declare function
|
|
53
|
+
export declare function isSurvivingAssistant(msg: AssistantMessage, model?: Model): boolean;
|
|
54
|
+
export declare function isAnthropicMessagesModel(model: Model): model is Model<"anthropic-messages">;
|
|
55
|
+
/** Whether `turn` came from `model` itself: its own turns replay their native reasoning unchanged. */
|
|
56
|
+
export declare function isSameModelTurn(turn: Pick<AssistantMessage, "provider" | "api" | "model">, model: Pick<Model, "provider" | "api" | "id">): boolean;
|
|
34
57
|
/**
|
|
35
58
|
* Normalize tool call ID for cross-provider compatibility.
|
|
36
59
|
* OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Antigravity OAuth flow (Gemini
|
|
2
|
+
* Google Antigravity OAuth flow (Gemini, Claude, GPT-OSS via Google Cloud)
|
|
3
3
|
* Uses different OAuth credentials than google-gemini-cli for access to additional models.
|
|
4
4
|
*/
|
|
5
5
|
import type { AfterExchangeHook } from "../hooks/types.js";
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -5,8 +5,6 @@ import type { GoogleGeminiCliOptions } from "./providers/google-gemini-cli.js";
|
|
|
5
5
|
import type { GoogleVertexOptions } from "./providers/google-vertex.js";
|
|
6
6
|
import type { Api, AssistantMessage, Context, Model, OptionsForApi, SimpleStreamOptions, ToolChoice } from "./types.js";
|
|
7
7
|
import { AssistantMessageEventStream } from "./utils/event-stream.js";
|
|
8
|
-
/** Strict official-Codex endpoint check; exact origin or a path boundary after {@link CODEX_BASE_URL}. */
|
|
9
|
-
export declare function isOfficialCodexApiUrl(baseUrl: string | undefined): boolean;
|
|
10
8
|
export declare function configureProviderMaxInFlightRequests(limits: Record<string, number> | undefined): void;
|
|
11
9
|
export declare const __providerInFlightForTesting: {
|
|
12
10
|
setRoot(root: string | undefined): void;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { ApiKey } from "./auth-retry.js";
|
|
|
9
9
|
import type { OAuthRequestIdentity } from "./auth/types.js";
|
|
10
10
|
import type { BedrockOptions } from "./providers/amazon-bedrock.js";
|
|
11
11
|
import type { AnthropicOptions } from "./providers/anthropic.js";
|
|
12
|
-
import type { FallbackParam, StopDetails } from "./providers/anthropic-wire.js";
|
|
12
|
+
import type { FallbackParam, MessageCreateParamsStreaming, StopDetails } from "./providers/anthropic-wire.js";
|
|
13
13
|
import type { AzureOpenAIResponsesOptions } from "./providers/azure-openai-responses.js";
|
|
14
14
|
import type { CursorOptions } from "./providers/cursor.js";
|
|
15
15
|
import type { AppleFoundationModelsOptions } from "./providers/apple-foundation-models.js";
|
|
@@ -267,6 +267,11 @@ export interface StreamOptions {
|
|
|
267
267
|
frequencyPenalty?: number;
|
|
268
268
|
maxTokens?: number;
|
|
269
269
|
signal?: AbortSignal;
|
|
270
|
+
/**
|
|
271
|
+
* OpenAI chat-completions HTTP attempt budget. When set, transient provider
|
|
272
|
+
* errors are not replayed above the transport, so retries share this limit.
|
|
273
|
+
*/
|
|
274
|
+
maxHttpAttempts?: number;
|
|
270
275
|
apiKey?: string;
|
|
271
276
|
/** @internal Stored credential row serving this request, when known. */
|
|
272
277
|
credentialId?: number;
|
|
@@ -398,8 +403,6 @@ export interface StreamOptions {
|
|
|
398
403
|
liveSteering?: LiveSteering;
|
|
399
404
|
/** Canonical Codex compaction classification; ignored by other providers. */
|
|
400
405
|
codexCompaction?: CodexCompactionRequestContext;
|
|
401
|
-
/** Codex Code Mode tool exposure snapshot emitted as `tool_namespaces_info` turn metadata; ignored by other providers. */
|
|
402
|
-
toolNamespacesInfo?: unknown;
|
|
403
406
|
/**
|
|
404
407
|
* Optional per-provider concurrent request cap for LLM stream calls. Keys are
|
|
405
408
|
* provider ids (`model.provider`); positive numeric values cap in-flight
|
|
@@ -645,6 +648,13 @@ export interface ThinkingContent {
|
|
|
645
648
|
* replay natively on another host.
|
|
646
649
|
*/
|
|
647
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;
|
|
648
658
|
}
|
|
649
659
|
export interface RedactedThinkingContent {
|
|
650
660
|
type: "redactedThinking";
|
|
@@ -652,11 +662,12 @@ export interface RedactedThinkingContent {
|
|
|
652
662
|
}
|
|
653
663
|
/**
|
|
654
664
|
* Anthropic server-side-fallback boundary marker persisted on assistant
|
|
655
|
-
* turns whose
|
|
656
|
-
* `AnthropicOptions.fallbacks
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
665
|
+
* turns whose response switched models mid-stream, whether the request
|
|
666
|
+
* opted into `AnthropicOptions.fallbacks` or a gateway opted in on its
|
|
667
|
+
* behalf. Consumers other than the Anthropic provider MUST ignore it —
|
|
668
|
+
* `transformMessages` strips the block on any non-Anthropic hop, and
|
|
669
|
+
* `convertAnthropicMessages` only puts it on the wire for an opted-in
|
|
670
|
+
* request to the official endpoint.
|
|
660
671
|
*/
|
|
661
672
|
export interface AnthropicFallbackContent {
|
|
662
673
|
type: "fallback";
|
|
@@ -983,6 +994,12 @@ export type AssistantRetryRecovery = {
|
|
|
983
994
|
export interface ContextSnapshot {
|
|
984
995
|
promptTokens: number;
|
|
985
996
|
nonMessageTokens: number;
|
|
997
|
+
/**
|
|
998
|
+
* Estimated tool-schema tokens included in `nonMessageTokens` (0 when the
|
|
999
|
+
* model defers schemas server-side). Lets a later model whose schema policy
|
|
1000
|
+
* differs rebase the provider anchor instead of inheriting its schemas.
|
|
1001
|
+
*/
|
|
1002
|
+
toolSchemaTokens?: number;
|
|
986
1003
|
/** Estimated prompt tokens removed by local history rewrites after this provider snapshot was recorded. */
|
|
987
1004
|
historyRewriteTokensRemoved?: number;
|
|
988
1005
|
/**
|
|
@@ -1031,6 +1048,12 @@ export interface AssistantMessage {
|
|
|
1031
1048
|
*/
|
|
1032
1049
|
serviceTier?: ServiceTier;
|
|
1033
1050
|
usage: Usage;
|
|
1051
|
+
/**
|
|
1052
|
+
* Visible output tokens a history rewrite (shake) has since removed from
|
|
1053
|
+
* `content`. `usage.output` still counts them, so a replay estimate subtracts
|
|
1054
|
+
* them to keep the turn's reasoning without recharging the removed text.
|
|
1055
|
+
*/
|
|
1056
|
+
elidedOutputTokens?: number;
|
|
1034
1057
|
stopReason: StopReason;
|
|
1035
1058
|
stopDetails?: StopDetails | null;
|
|
1036
1059
|
errorMessage?: string;
|
|
@@ -1418,7 +1441,8 @@ export type AssistantMessageEvent = {
|
|
|
1418
1441
|
export interface AnthropicFallbackCreditHandle {
|
|
1419
1442
|
token: string;
|
|
1420
1443
|
prefillClaim?: boolean | null;
|
|
1421
|
-
|
|
1444
|
+
/** The refused request's body, replayed with the credit token on the fallback model. */
|
|
1445
|
+
params: MessageCreateParamsStreaming;
|
|
1422
1446
|
betas?: readonly string[];
|
|
1423
1447
|
betaHeader?: string;
|
|
1424
1448
|
expiresAt: number;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const museCodeUsageProvider: UsageProvider;
|
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Removal of DeepSeek
|
|
2
|
+
* Removal of DeepSeek tool-call markup left in visible assistant text.
|
|
3
3
|
*
|
|
4
4
|
* DeepSeek models on OpenAI-compatible hosts sometimes emit their tool calls as
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* runs no healer at all). That markup must not stay
|
|
9
|
-
* teaches the model to keep writing tool calls as text
|
|
10
|
-
* strip it before the message is committed and tell the
|
|
5
|
+
* text in `content`: the V4 `<|DSML|…>` envelope or the V3 chat-template
|
|
6
|
+
* tokens (`<|tool▁calls▁begin|>` …). A well-formed call is healed into a
|
|
7
|
+
* structured call by `StreamMarkupHealing`, but a degraded model can drop
|
|
8
|
+
* required tags (or the host runs no healer at all). That markup must not stay
|
|
9
|
+
* in history: replaying it teaches the model to keep writing tool calls as text
|
|
10
|
+
* (issue #10556). Callers strip it before the message is committed and tell the
|
|
11
|
+
* model its call failed.
|
|
11
12
|
*/
|
|
12
13
|
import type { AssistantMessage, Model } from "../types.js";
|
|
13
14
|
/**
|
|
14
15
|
* Whether DSML leak recovery applies to `model`: a DeepSeek-class model, or one
|
|
15
16
|
* explicitly configured with the `dsml` stream healer (e.g. a `models.yml` entry
|
|
16
|
-
* whose id the classifier cannot place). Other models never write
|
|
17
|
-
* calls, so
|
|
17
|
+
* whose id the classifier cannot place). Other models never write DeepSeek tool
|
|
18
|
+
* calls, so such markup from them is prose and is left alone.
|
|
18
19
|
*/
|
|
19
20
|
export declare function isDsmlLeakRecoveryTarget(model: Model): boolean;
|
|
20
21
|
/**
|
|
21
|
-
* Strip leaked
|
|
22
|
-
* Returns `undefined` when `text` carries
|
|
22
|
+
* Strip leaked DeepSeek tool-call markup (DSML or chat-template tokens) from
|
|
23
|
+
* `text`, leaving quoted code intact. Returns `undefined` when `text` carries
|
|
24
|
+
* no such markup.
|
|
23
25
|
*/
|
|
24
26
|
export declare function stripDsmlToolMarkup(text: string): string | undefined;
|
|
25
27
|
/**
|
|
26
|
-
* Remove leaked DSML tool-call markup from
|
|
27
|
-
*
|
|
28
|
+
* Remove leaked DSML tool-call markup from an assistant message before commit,
|
|
29
|
+
* including reasoning blocks and turns with valid structured calls. Rewritten
|
|
30
|
+
* thinking with an opaque signature is discarded; OpenAI-compatible reasoning
|
|
31
|
+
* field names are replay markers, not signatures, and survive cleanup.
|
|
28
32
|
*/
|
|
29
33
|
export declare function removeDsmlToolMarkupLeak(message: AssistantMessage): boolean;
|
|
@@ -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;
|
|
@@ -26,6 +26,8 @@ export interface OpenAIStreamRequestInit {
|
|
|
26
26
|
body: unknown;
|
|
27
27
|
signal: AbortSignal;
|
|
28
28
|
fetch?: FetchImpl;
|
|
29
|
+
/** Override the default transport attempt budget for this request. */
|
|
30
|
+
maxAttempts?: number;
|
|
29
31
|
/** Optional caller-specific gate composed with shared transport retry exclusions. */
|
|
30
32
|
shouldRetryResponse?: (response: Response, bodyText: string) => boolean | Promise<boolean>;
|
|
31
33
|
/**
|
|
@@ -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,21 @@
|
|
|
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 — the host's decode error, or a
|
|
13
|
+
* non-JSON 400/403/415 from a proxy in front of it — is resent uncompressed
|
|
14
|
+
* once; only when that resend succeeds is the encoding latched off for the
|
|
15
|
+
* host, so a genuine 400 never disables compression. When a non-JSON refusal
|
|
16
|
+
* recurs uncompressed with the same status and a non-JSON body, that status
|
|
17
|
+
* stops being re-checked for the host.
|
|
18
|
+
*/
|
|
19
|
+
export declare function wrapFetchForRequestCompression(base: FetchImpl, model: Model): FetchImpl;
|
|
20
|
+
/** Test hook: clears every per-host latch. */
|
|
21
|
+
export declare function resetRequestCompressionForTesting(): void;
|
|
@@ -5,7 +5,8 @@ export declare function parseToolCallArguments(json: string | undefined): ToolCa
|
|
|
5
5
|
* Native Responses `function_call.arguments` to persist for replay.
|
|
6
6
|
*
|
|
7
7
|
* {@link parseToolCallArguments} repairs some invalid JSON (e.g. `"name": ,`) and the tool runs with the
|
|
8
|
-
* repaired value; replay
|
|
8
|
+
* repaired value; replay must carry what was executed (#14155), not the {@link invalidToolCallArguments}
|
|
9
|
+
* diagnostic it substitutes for stored arguments that do not parse (#15568).
|
|
9
10
|
* Returns `raw` when it already parses or the arguments were unrepairable, else the executed arguments.
|
|
10
11
|
*/
|
|
11
12
|
export declare function replayableToolCallArguments(raw: string, executed: ToolCall["arguments"]): string;
|
|
@@ -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/dist/types/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResponseInput } from "./providers/openai-responses-wire.js";
|
|
2
|
+
export { normalizeToolCallId } from "./providers/transform-messages.js";
|
|
2
3
|
import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } from "./types.js";
|
|
3
4
|
export { isRecord } from "@linxiraos/pi-utils";
|
|
4
5
|
/**
|
|
@@ -9,7 +10,6 @@ export { isRecord } from "@linxiraos/pi-utils";
|
|
|
9
10
|
*/
|
|
10
11
|
export declare function getHeaderCaseInsensitive(headers: Record<string, string> | undefined, headerName: string): string | undefined;
|
|
11
12
|
export declare function normalizeSystemPrompts(systemPrompt: readonly string[] | string | undefined | null): string[];
|
|
12
|
-
export declare function normalizeToolCallId(id: string): string;
|
|
13
13
|
type ResponsesToolItemIdPrefix = "fc" | "ctc";
|
|
14
14
|
/** Preserve opaque call IDs for Responses replay while normalizing or generating the separate item ID. */
|
|
15
15
|
export declare function normalizeResponsesToolCallId(id: string, itemPrefix?: ResponsesToolItemIdPrefix): {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxiraos/pi-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -145,18 +145,18 @@
|
|
|
145
145
|
},
|
|
146
146
|
"scripts": {
|
|
147
147
|
"check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
|
|
148
|
-
"check:types": "
|
|
148
|
+
"check:types": "tsc -p tsconfig.json --noEmit",
|
|
149
149
|
"lint": "oxlint .",
|
|
150
150
|
"test": "bun test --parallel",
|
|
151
151
|
"fix": "oxlint --fix --fix-suggestions . && bun run fmt",
|
|
152
152
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
153
153
|
},
|
|
154
154
|
"dependencies": {
|
|
155
|
-
"@linxiraos/pi-omptype": "1.
|
|
156
|
-
"@linxiraos/pi-catalog": "1.
|
|
157
|
-
"@linxiraos/pi-natives": "1.
|
|
158
|
-
"@linxiraos/pi-utils": "1.
|
|
159
|
-
"@linxiraos/pi-wire": "1.
|
|
155
|
+
"@linxiraos/pi-omptype": "1.3.1",
|
|
156
|
+
"@linxiraos/pi-catalog": "1.3.1",
|
|
157
|
+
"@linxiraos/pi-natives": "1.3.1",
|
|
158
|
+
"@linxiraos/pi-utils": "1.3.1",
|
|
159
|
+
"@linxiraos/pi-wire": "1.3.1"
|
|
160
160
|
},
|
|
161
161
|
"devDependencies": {
|
|
162
162
|
"@types/bun": "1.4.3"
|
package/src/auth/cascade.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { CredentialPool } from "./pool";
|
|
|
10
10
|
import type { CredentialSelector, OAuthResolutionResult } from "./select";
|
|
11
11
|
import type {
|
|
12
12
|
AuthApiKeyOptions,
|
|
13
|
+
AuthPeekOptions,
|
|
13
14
|
AuthCredential,
|
|
14
15
|
AuthSource,
|
|
15
16
|
AuthSourceOptions,
|
|
@@ -275,9 +276,11 @@ export class KeyCascade implements KeysApi {
|
|
|
275
276
|
* Used for model discovery where we only need to know if credentials exist
|
|
276
277
|
* and get a best-effort token. GitHub Copilot's peek must preserve
|
|
277
278
|
* enterprise routing metadata because discovery needs a structured
|
|
278
|
-
* credential to reach the correct host.
|
|
279
|
+
* credential to reach the correct host. With several stored accounts every
|
|
280
|
+
* peek returns the same one, so credential-scoped discovery caches stay on
|
|
281
|
+
* one namespace.
|
|
279
282
|
*/
|
|
280
|
-
async peek(provider: string): Promise<string | undefined> {
|
|
283
|
+
async peek(provider: string, options?: AuthPeekOptions): Promise<string | undefined> {
|
|
281
284
|
const runtimeKey = this.#deps.overrides.runtimeKey(provider);
|
|
282
285
|
if (runtimeKey) {
|
|
283
286
|
return runtimeKey;
|
|
@@ -292,19 +295,26 @@ export class KeyCascade implements KeysApi {
|
|
|
292
295
|
|
|
293
296
|
// Precedence: a deliberate OAuth/login credential wins, then an explicit env var,
|
|
294
297
|
// then a stored static api_key (which may be a stale broker-migrated copy) as a last resort.
|
|
295
|
-
|
|
298
|
+
// Peek never refreshes, so only an unexpired OAuth access token is usable
|
|
299
|
+
// unless the caller only needs the account it names.
|
|
300
|
+
const now = Date.now();
|
|
301
|
+
const oauthSelection =
|
|
302
|
+
this.#deps.selector.selectByType(
|
|
303
|
+
provider,
|
|
304
|
+
"oauth",
|
|
305
|
+
undefined,
|
|
306
|
+
credential =>
|
|
307
|
+
credential.type === "oauth" && Number.isFinite(credential.expires) && credential.expires > now,
|
|
308
|
+
) ?? (options?.includeExpired ? this.#deps.selector.selectByType(provider, "oauth") : undefined);
|
|
296
309
|
if (oauthSelection) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
apiEndpoint: oauthSelection.credential.apiEndpoint,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
return oauthSelection.credential.access;
|
|
310
|
+
if (provider === "github-copilot") {
|
|
311
|
+
return JSON.stringify({
|
|
312
|
+
token: oauthSelection.credential.access,
|
|
313
|
+
enterpriseUrl: oauthSelection.credential.enterpriseUrl,
|
|
314
|
+
apiEndpoint: oauthSelection.credential.apiEndpoint,
|
|
315
|
+
});
|
|
307
316
|
}
|
|
317
|
+
return oauthSelection.credential.access;
|
|
308
318
|
}
|
|
309
319
|
|
|
310
320
|
const loginApiKeySelection = this.#deps.selector.selectByType(
|
package/src/auth/pool.ts
CHANGED
|
@@ -182,7 +182,7 @@ export class CredentialPool implements CredentialsApi {
|
|
|
182
182
|
* The store is shared across every zeta process, but the pool is an
|
|
183
183
|
* in-process cache refreshed only by this process's own writes. Without
|
|
184
184
|
* this a long-running session ranks a stale pool for its whole lifetime:
|
|
185
|
-
* `
|
|
185
|
+
* `zetacode auth` in another terminal is invisible, rotation reports no usable
|
|
186
186
|
* sibling while a freshly added account sits unblocked in SQLite, and the
|
|
187
187
|
* turn degrades to the fallback chain. The auth-broker path already polls;
|
|
188
188
|
* direct-store sessions had no equivalent.
|
|
@@ -719,7 +719,7 @@ export class CredentialPool implements CredentialsApi {
|
|
|
719
719
|
}
|
|
720
720
|
|
|
721
721
|
/**
|
|
722
|
-
* Disabled credential tombstones for display surfaces (`
|
|
722
|
+
* Disabled credential tombstones for display surfaces (`zetacode usage`,
|
|
723
723
|
* broker `GET /v1/credentials/disabled`). Empty when the backing store
|
|
724
724
|
* keeps no tombstones or the remote broker predates the endpoint.
|
|
725
725
|
*/
|
|
@@ -732,7 +732,7 @@ export class CredentialPool implements CredentialsApi {
|
|
|
732
732
|
* Force the backing store to revalidate its credential snapshot, then
|
|
733
733
|
* reload. Remote broker stores re-fetch the snapshot; local stores are
|
|
734
734
|
* always current, so only the reload runs. Callers that pair live
|
|
735
|
-
* per-credential data with stored identities (`
|
|
735
|
+
* per-credential data with stored identities (`zetacode usage`) use this so a
|
|
736
736
|
* disk-cached snapshot cannot misattribute fresh reports.
|
|
737
737
|
*/
|
|
738
738
|
async revalidate(): Promise<void> {
|