@oh-my-pi/pi-ai 16.3.6 → 16.3.8
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 +18 -0
- package/dist/types/api-registry.d.ts +1 -1
- package/dist/types/auth-broker/client.d.ts +2 -2
- package/dist/types/auth-broker/discover.d.ts +1 -1
- package/dist/types/auth-broker/index.d.ts +7 -7
- package/dist/types/auth-broker/refresher.d.ts +1 -1
- package/dist/types/auth-broker/remote-store.d.ts +6 -6
- package/dist/types/auth-broker/server.d.ts +1 -1
- package/dist/types/auth-broker/snapshot-cache.d.ts +1 -1
- package/dist/types/auth-broker/types.d.ts +2 -2
- package/dist/types/auth-gateway/index.d.ts +3 -3
- package/dist/types/auth-gateway/server.d.ts +3 -3
- package/dist/types/auth-gateway/types.d.ts +1 -1
- package/dist/types/auth-retry.d.ts +2 -2
- package/dist/types/auth-storage.d.ts +6 -6
- package/dist/types/dialect/anthropic.d.ts +1 -1
- package/dist/types/dialect/catalog.d.ts +1 -1
- package/dist/types/dialect/coercion.d.ts +1 -1
- package/dist/types/dialect/deepseek.d.ts +1 -1
- package/dist/types/dialect/demotion.d.ts +16 -14
- package/dist/types/dialect/examples.d.ts +1 -1
- package/dist/types/dialect/factory.d.ts +1 -1
- package/dist/types/dialect/gemini.d.ts +1 -1
- package/dist/types/dialect/gemma.d.ts +1 -1
- package/dist/types/dialect/glm.d.ts +1 -1
- package/dist/types/dialect/harmony.d.ts +1 -1
- package/dist/types/dialect/hermes.d.ts +1 -1
- package/dist/types/dialect/history.d.ts +2 -2
- package/dist/types/dialect/index.d.ts +11 -11
- package/dist/types/dialect/inventory.d.ts +1 -1
- package/dist/types/dialect/kimi.d.ts +1 -1
- package/dist/types/dialect/minimax.d.ts +1 -1
- package/dist/types/dialect/owned-stream.d.ts +2 -2
- package/dist/types/dialect/qwen3.d.ts +1 -1
- package/dist/types/dialect/rendering.d.ts +2 -2
- package/dist/types/dialect/thinking.d.ts +1 -1
- package/dist/types/dialect/types.d.ts +1 -1
- package/dist/types/dialect/xml.d.ts +1 -1
- package/dist/types/error/classes.d.ts +1 -1
- package/dist/types/error/finalize.d.ts +3 -3
- package/dist/types/error/flags.d.ts +1 -1
- package/dist/types/error/format.d.ts +1 -1
- package/dist/types/error/index.d.ts +13 -13
- package/dist/types/error/provider.d.ts +1 -1
- package/dist/types/error/rate-limit.d.ts +1 -1
- package/dist/types/index.d.ts +47 -47
- package/dist/types/provider-details.d.ts +1 -1
- package/dist/types/providers/amazon-bedrock.d.ts +1 -1
- package/dist/types/providers/anthropic-client.d.ts +3 -3
- 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/anthropic-wire.d.ts +1 -1
- package/dist/types/providers/anthropic.d.ts +3 -3
- package/dist/types/providers/aws-credentials.d.ts +2 -2
- package/dist/types/providers/azure-openai-responses.d.ts +2 -2
- package/dist/types/providers/cursor.d.ts +18 -3
- package/dist/types/providers/devin.d.ts +1 -1
- package/dist/types/providers/error-message.d.ts +1 -1
- package/dist/types/providers/github-copilot-headers.d.ts +1 -1
- package/dist/types/providers/gitlab-duo-workflow.d.ts +2 -2
- package/dist/types/providers/gitlab-duo.d.ts +2 -2
- package/dist/types/providers/google-auth.d.ts +1 -1
- package/dist/types/providers/google-gemini-cli.d.ts +6 -4
- package/dist/types/providers/google-interactions.d.ts +3 -3
- package/dist/types/providers/google-shared.d.ts +7 -5
- package/dist/types/providers/google-vertex.d.ts +2 -2
- package/dist/types/providers/google.d.ts +2 -2
- package/dist/types/providers/kimi.d.ts +3 -3
- package/dist/types/providers/mock.d.ts +2 -2
- package/dist/types/providers/ollama.d.ts +1 -1
- package/dist/types/providers/openai-anthropic-shim.d.ts +2 -2
- package/dist/types/providers/openai-chat-server-schema.d.ts +1 -1
- package/dist/types/providers/openai-chat-server.d.ts +2 -2
- package/dist/types/providers/openai-codex/request-transformer.d.ts +1 -1
- package/dist/types/providers/openai-codex/response-handler.d.ts +1 -1
- package/dist/types/providers/openai-codex-responses.d.ts +3 -3
- package/dist/types/providers/openai-completions.d.ts +3 -3
- package/dist/types/providers/openai-reasoning-fallback.d.ts +1 -1
- package/dist/types/providers/openai-responses-server-schema.d.ts +1 -1
- package/dist/types/providers/openai-responses-server.d.ts +2 -2
- package/dist/types/providers/openai-responses.d.ts +5 -5
- package/dist/types/providers/openai-shared.d.ts +10 -9
- package/dist/types/providers/pi-native-client.d.ts +1 -1
- package/dist/types/providers/pi-native-server.d.ts +2 -2
- package/dist/types/providers/register-builtins.d.ts +3 -3
- package/dist/types/providers/synthetic.d.ts +3 -3
- package/dist/types/providers/transform-messages.d.ts +1 -1
- package/dist/types/providers/vision-guard.d.ts +1 -1
- package/dist/types/registry/alibaba-coding-plan.d.ts +1 -1
- package/dist/types/registry/anthropic.d.ts +1 -1
- package/dist/types/registry/api-key-login.d.ts +1 -1
- package/dist/types/registry/api-key-validation.d.ts +1 -1
- package/dist/types/registry/baseten.d.ts +2 -2
- package/dist/types/registry/cerebras.d.ts +2 -2
- package/dist/types/registry/cloudflare-ai-gateway.d.ts +1 -1
- package/dist/types/registry/coreweave.d.ts +2 -2
- package/dist/types/registry/cursor.d.ts +1 -1
- package/dist/types/registry/deepseek.d.ts +1 -1
- package/dist/types/registry/devin.d.ts +1 -1
- package/dist/types/registry/firepass.d.ts +2 -2
- package/dist/types/registry/fireworks.d.ts +2 -2
- package/dist/types/registry/github-copilot.d.ts +1 -1
- package/dist/types/registry/gitlab-duo-workflow.d.ts +1 -1
- package/dist/types/registry/gitlab-duo.d.ts +1 -1
- package/dist/types/registry/google-antigravity.d.ts +1 -1
- package/dist/types/registry/google-gemini-cli.d.ts +1 -1
- package/dist/types/registry/huggingface.d.ts +2 -2
- package/dist/types/registry/index.d.ts +4 -4
- package/dist/types/registry/kagi.d.ts +1 -1
- package/dist/types/registry/kilo.d.ts +1 -1
- package/dist/types/registry/kimi-code.d.ts +1 -1
- package/dist/types/registry/litellm.d.ts +1 -1
- package/dist/types/registry/llama-cpp.d.ts +1 -1
- package/dist/types/registry/lm-studio.d.ts +1 -1
- package/dist/types/registry/minimax-code-cn.d.ts +1 -1
- package/dist/types/registry/minimax-code.d.ts +1 -1
- package/dist/types/registry/moonshot.d.ts +2 -2
- package/dist/types/registry/nanogpt.d.ts +2 -2
- package/dist/types/registry/nvidia.d.ts +1 -1
- package/dist/types/registry/oauth/anthropic.d.ts +3 -3
- package/dist/types/registry/oauth/callback-server.d.ts +1 -1
- package/dist/types/registry/oauth/cursor.d.ts +1 -1
- package/dist/types/registry/oauth/devin.d.ts +1 -1
- package/dist/types/registry/oauth/github-copilot.d.ts +2 -2
- package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +2 -2
- package/dist/types/registry/oauth/gitlab-duo.d.ts +1 -1
- package/dist/types/registry/oauth/google-antigravity.d.ts +1 -1
- package/dist/types/registry/oauth/google-gemini-cli.d.ts +1 -1
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +2 -2
- package/dist/types/registry/oauth/index.d.ts +2 -2
- package/dist/types/registry/oauth/kimi.d.ts +1 -1
- package/dist/types/registry/oauth/minimax-code.d.ts +2 -2
- package/dist/types/registry/oauth/openai-codex.d.ts +1 -1
- package/dist/types/registry/oauth/opencode.d.ts +1 -1
- package/dist/types/registry/oauth/perplexity.d.ts +1 -1
- package/dist/types/registry/oauth/types.d.ts +2 -2
- package/dist/types/registry/oauth/wafer.d.ts +1 -1
- package/dist/types/registry/oauth/xai-oauth.d.ts +3 -3
- package/dist/types/registry/oauth/xiaomi.d.ts +1 -1
- package/dist/types/registry/ollama-cloud.d.ts +1 -1
- package/dist/types/registry/ollama.d.ts +1 -1
- package/dist/types/registry/openai-codex-device.d.ts +1 -1
- package/dist/types/registry/openai-codex.d.ts +1 -1
- package/dist/types/registry/opencode-go.d.ts +1 -1
- package/dist/types/registry/opencode-zen.d.ts +1 -1
- package/dist/types/registry/openrouter.d.ts +2 -2
- package/dist/types/registry/parallel.d.ts +1 -1
- package/dist/types/registry/perplexity.d.ts +2 -2
- package/dist/types/registry/qianfan.d.ts +2 -2
- package/dist/types/registry/qwen-portal.d.ts +1 -1
- package/dist/types/registry/registry.d.ts +69 -69
- package/dist/types/registry/sakana.d.ts +2 -2
- package/dist/types/registry/synthetic.d.ts +2 -2
- package/dist/types/registry/tavily.d.ts +1 -1
- package/dist/types/registry/together.d.ts +1 -1
- package/dist/types/registry/types.d.ts +1 -1
- package/dist/types/registry/umans.d.ts +2 -2
- package/dist/types/registry/venice.d.ts +2 -2
- package/dist/types/registry/vercel-ai-gateway.d.ts +1 -1
- package/dist/types/registry/vllm.d.ts +1 -1
- package/dist/types/registry/wafer-serverless.d.ts +1 -1
- package/dist/types/registry/xai-oauth.d.ts +1 -1
- package/dist/types/registry/xiaomi-token-plan-ams.d.ts +1 -1
- package/dist/types/registry/xiaomi-token-plan-cn.d.ts +1 -1
- package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +1 -1
- package/dist/types/registry/xiaomi.d.ts +1 -1
- package/dist/types/registry/zai.d.ts +2 -2
- package/dist/types/registry/zenmux.d.ts +2 -2
- package/dist/types/registry/zhipu-coding-plan.d.ts +2 -2
- package/dist/types/stream.d.ts +6 -6
- package/dist/types/types.d.ts +24 -21
- package/dist/types/usage/claude.d.ts +1 -1
- package/dist/types/usage/gemini.d.ts +1 -1
- package/dist/types/usage/github-copilot.d.ts +1 -1
- package/dist/types/usage/google-antigravity.d.ts +1 -1
- package/dist/types/usage/kimi.d.ts +1 -1
- package/dist/types/usage/minimax-code.d.ts +1 -1
- package/dist/types/usage/ollama.d.ts +1 -1
- package/dist/types/usage/openai-codex-reset.d.ts +1 -1
- package/dist/types/usage/openai-codex.d.ts +1 -1
- package/dist/types/usage/opencode-go.d.ts +1 -1
- package/dist/types/usage/zai.d.ts +1 -1
- package/dist/types/usage.d.ts +1 -1
- package/dist/types/utils/block-symbols.d.ts +15 -0
- package/dist/types/utils/empty-completion-retry.d.ts +2 -2
- package/dist/types/utils/event-stream.d.ts +1 -1
- package/dist/types/utils/harmony-leak.d.ts +1 -1
- package/dist/types/utils/http-inspector.d.ts +19 -0
- package/dist/types/utils/leaked-thinking-stream.d.ts +1 -1
- package/dist/types/utils/openai-http.d.ts +3 -9
- package/dist/types/utils/provider-response.d.ts +1 -1
- package/dist/types/utils/proxy.d.ts +1 -1
- package/dist/types/utils/request-debug.d.ts +1 -1
- package/dist/types/utils/retry.d.ts +1 -1
- package/dist/types/utils/schema/equality.d.ts +1 -1
- package/dist/types/utils/schema/index.d.ts +15 -15
- package/dist/types/utils/schema/normalize.d.ts +2 -2
- package/dist/types/utils/schema/spill.d.ts +1 -1
- package/dist/types/utils/schema/wire.d.ts +1 -1
- package/dist/types/utils/sse-debug.d.ts +1 -1
- package/dist/types/utils/thinking-loop.d.ts +2 -2
- package/dist/types/utils/tool-call-loop-guard.d.ts +1 -1
- package/dist/types/utils/tool-choice.d.ts +1 -1
- package/dist/types/utils/validation.d.ts +1 -1
- package/dist/types/utils.d.ts +14 -2
- package/package.json +4 -4
- package/src/dialect/demotion.ts +18 -19
- package/src/error/rate-limit.ts +1 -1
- package/src/providers/azure-openai-responses.ts +3 -2
- package/src/providers/cursor.ts +36 -0
- package/src/providers/google-gemini-cli.ts +12 -3
- package/src/providers/google-shared.ts +3 -1
- package/src/providers/mock.ts +10 -3
- package/src/providers/openai-codex-responses.ts +47 -39
- package/src/providers/openai-completions.ts +48 -9
- package/src/providers/openai-responses.ts +37 -23
- package/src/providers/openai-shared.ts +145 -26
- package/src/providers/transform-messages.ts +63 -9
- package/src/registry/oauth/wafer.ts +1 -1
- package/src/stream.ts +6 -0
- package/src/types.ts +27 -3
- package/src/utils/block-symbols.ts +18 -0
- package/src/utils/http-inspector.ts +32 -4
- package/src/utils/openai-http.ts +6 -9
- package/src/utils/retry-after.ts +12 -1
- package/src/utils.ts +78 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CredentialRankingStrategy, UsageProvider } from "../usage";
|
|
1
|
+
import type { CredentialRankingStrategy, UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const antigravityUsageProvider: UsageProvider;
|
|
3
3
|
/**
|
|
4
4
|
* Antigravity quotas are returned per backend counter (Anthropic / Google /
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { UsageProvider } from "../usage";
|
|
1
|
+
import type { UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const kimiUsageProvider: UsageProvider;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { UsageProvider } from "../usage";
|
|
1
|
+
import type { UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const minimaxCodeUsageProvider: UsageProvider;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UsageProvider } from "../usage";
|
|
1
|
+
import type { UsageProvider } from "../usage.js";
|
|
2
2
|
/** Registers Ollama accounts with usage views even though no quota endpoint is exposed. */
|
|
3
3
|
export declare const ollamaUsageProvider: UsageProvider;
|
|
4
4
|
/** Registers Ollama Cloud accounts with usage views until a quota endpoint is available. */
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* (the `/usage reset` command + auto-redeem) and any out-of-band tooling can
|
|
19
19
|
* share one wire contract.
|
|
20
20
|
*/
|
|
21
|
-
import type { FetchImpl } from "../types";
|
|
21
|
+
import type { FetchImpl } from "../types.js";
|
|
22
22
|
/** A single redeemable (or already-spent) saved reset. */
|
|
23
23
|
export interface CodexResetCredit {
|
|
24
24
|
/** Opaque credit id, e.g. `RateLimitResetCredit_…`. Pass to {@link consumeCodexResetCredit}. */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { CredentialRankingStrategy, UsageProvider } from "../usage";
|
|
1
|
+
import type { CredentialRankingStrategy, UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const openaiCodexUsageProvider: UsageProvider;
|
|
3
3
|
export declare const codexRankingStrategy: CredentialRankingStrategy;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { UsageProvider } from "../usage";
|
|
1
|
+
import type { UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const opencodeGoUsageProvider: UsageProvider;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { UsageProvider } from "../usage";
|
|
1
|
+
import type { UsageProvider } from "../usage.js";
|
|
2
2
|
export declare const zaiUsageProvider: UsageProvider;
|
package/dist/types/usage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FetchImpl, Provider } from "./types";
|
|
1
|
+
import type { FetchImpl, Provider } from "./types.js";
|
|
2
2
|
export type UsageUnit = "percent" | "tokens" | "requests" | "usd" | "minutes" | "bytes" | "unknown";
|
|
3
3
|
export type UsageStatus = "ok" | "warning" | "exhausted" | "unknown";
|
|
4
4
|
/** Time window for a limit (e.g. 5h, 7d, monthly). */
|
|
@@ -34,3 +34,18 @@ export declare const kCursorExecResolved: unique symbol;
|
|
|
34
34
|
export type CursorExecResolvedCarrier = object & {
|
|
35
35
|
[kCursorExecResolved]?: true;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Marks a text block synthesized by cross-model thinking demotion in
|
|
39
|
+
* `transformMessages`. Converters that flatten adjacent text blocks into one
|
|
40
|
+
* string (openai-completions) insert a paragraph separator after marked
|
|
41
|
+
* blocks; unmarked adjacent blocks keep their original byte sequence.
|
|
42
|
+
* Symbol-keyed so the marker never persists across the JSONL round-trip and
|
|
43
|
+
* never reaches the wire.
|
|
44
|
+
*/
|
|
45
|
+
export declare const kDemotedThinking: unique symbol;
|
|
46
|
+
/** Carries the demoted-thinking marker without exposing a string-keyed property. */
|
|
47
|
+
export type DemotedThinkingCarrier = object & {
|
|
48
|
+
[kDemotedThinking]?: boolean;
|
|
49
|
+
};
|
|
50
|
+
/** True for text blocks synthesized by cross-model thinking demotion. */
|
|
51
|
+
export declare function isDemotedThinking(block: DemotedThinkingCarrier | null | undefined): boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AssistantMessage, Context } from "../types";
|
|
2
|
-
import { AssistantMessageEventStream } from "./event-stream";
|
|
1
|
+
import type { AssistantMessage, Context } from "../types.js";
|
|
2
|
+
import { AssistantMessageEventStream } from "./event-stream.js";
|
|
3
3
|
export declare const MAX_EMPTY_COMPLETION_RETRIES = 2;
|
|
4
4
|
export declare const EMPTY_COMPLETION_BASE_DELAY_MS = 500;
|
|
5
5
|
/**
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* hashline DSL form. Other tools and surfaces fall through to
|
|
8
8
|
* abort-and-retry handled by the agent loop.
|
|
9
9
|
*/
|
|
10
|
-
import type { AssistantMessage, Model, ToolCall } from "../types";
|
|
10
|
+
import type { AssistantMessage, Model, ToolCall } from "../types.js";
|
|
11
11
|
declare const SIGNAL_ORDER: readonly ["M", "C", "G", "S", "B", "R", "T"];
|
|
12
12
|
export type HarmonySignalClass = "H" | (typeof SIGNAL_ORDER)[number];
|
|
13
13
|
export type HarmonySurface = "assistant_text" | "assistant_thinking" | "tool_arg";
|
|
@@ -13,6 +13,25 @@ export type CapturedHttpErrorResponse = {
|
|
|
13
13
|
bodyText?: string;
|
|
14
14
|
bodyJson?: unknown;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Build the JSON persisted for a rejected request. Request fields stay at the
|
|
18
|
+
* top level (so existing dump parsers still read `body`); the provider's error
|
|
19
|
+
* is added under `errorResponse` so a failed request is diagnosable from the
|
|
20
|
+
* dump file rather than the request alone.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildHttp400DumpPayload(dump: RawHttpRequestDump, error: unknown, message: string): RawHttpRequestDump & {
|
|
23
|
+
errorResponse: {
|
|
24
|
+
status: number | undefined;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/** HTTP statuses whose rejected request we persist for post-hoc diagnosis: the
|
|
29
|
+
* request-content rejections that wedge a session. 400 (bad request) and 413
|
|
30
|
+
* (payload too large — an oversized image / snapcompact frame payload that 413s
|
|
31
|
+
* and empties the turn). Auth (401/403), not-found (404), rate limits and 5xx
|
|
32
|
+
* are excluded: 429/5xx are retried, so persisting them here would write one
|
|
33
|
+
* dump per attempt. */
|
|
34
|
+
export declare function shouldDumpRejectedRequest(error: unknown): boolean;
|
|
16
35
|
export declare function appendRawHttpRequestDumpFor400(message: string, error: unknown, dump: RawHttpRequestDump | undefined): Promise<string>;
|
|
17
36
|
export declare function finalizeErrorMessage(error: unknown, rawRequestDump: RawHttpRequestDump | undefined, capturedErrorResponse?: CapturedHttpErrorResponse): Promise<string>;
|
|
18
37
|
/**
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* `../dialect/owned-stream.ts`, minus all in-band tool-call grammar: tool-call
|
|
25
25
|
* events are forwarded verbatim.
|
|
26
26
|
*/
|
|
27
|
-
import { AssistantMessageEventStream } from "./event-stream";
|
|
27
|
+
import { AssistantMessageEventStream } from "./event-stream.js";
|
|
28
28
|
/**
|
|
29
29
|
* Wrap a provider stream so leaked reasoning fences are healed into thinking
|
|
30
30
|
* blocks live, for every provider. Returns a new stream that re-projects the
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* chain-state detectors, which regex over `error.message`.
|
|
16
16
|
*/
|
|
17
17
|
import { type SseEventObserver } from "@oh-my-pi/pi-utils";
|
|
18
|
-
import * as AIError from "../error";
|
|
19
|
-
import { OpenAIHttpError } from "../error";
|
|
18
|
+
import * as AIError from "../error/index.js";
|
|
19
|
+
import { OpenAIHttpError } from "../error/index.js";
|
|
20
20
|
export { OpenAIHttpError };
|
|
21
|
-
import type { FetchImpl } from "../types";
|
|
21
|
+
import type { FetchImpl } from "../types.js";
|
|
22
22
|
export interface OpenAIStreamRequestInit {
|
|
23
23
|
url: string;
|
|
24
24
|
headers: Record<string, string>;
|
|
@@ -26,12 +26,6 @@ export interface OpenAIStreamRequestInit {
|
|
|
26
26
|
body: unknown;
|
|
27
27
|
signal: AbortSignal;
|
|
28
28
|
fetch?: FetchImpl;
|
|
29
|
-
/**
|
|
30
|
-
* Total attempts (initial + retries). Defaults to {@link DEFAULT_MAX_ATTEMPTS}.
|
|
31
|
-
* Pass `1` when a first-event watchdog is armed so retries cannot silently
|
|
32
|
-
* extend the caller's deadline (mirrors the old `maxRetries: 0` hint).
|
|
33
|
-
*/
|
|
34
|
-
maxAttempts?: number;
|
|
35
29
|
/** Raw wire-frame observer (`onSseEvent` debug pipeline). */
|
|
36
30
|
onSseEvent?: SseEventObserver;
|
|
37
31
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { Api, Model, ProviderResponseMetadata, StreamOptions } from "../types";
|
|
1
|
+
import type { Api, Model, ProviderResponseMetadata, StreamOptions } from "../types.js";
|
|
2
2
|
export declare function normalizeProviderResponse(response: Response, requestId?: string | null, metadata?: Record<string, unknown>): ProviderResponseMetadata;
|
|
3
3
|
export declare function notifyProviderResponse(options: Pick<StreamOptions, "onResponse"> | undefined, response: Response, model?: Model<Api>, requestId?: string | null, metadata?: Record<string, unknown>): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as tls from "node:tls";
|
|
2
|
-
import type { FetchImpl } from "../types";
|
|
2
|
+
import type { FetchImpl } from "../types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Checks if a host is local or cloud metadata, which should always bypass the proxy
|
|
5
5
|
* (e.g. localhost, 127/8, ::1, 169.254.169.254, metadata.google.internal).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { JsonObject } from "./types";
|
|
1
|
+
import type { JsonObject } from "./types.js";
|
|
2
2
|
export declare function areJsonValuesEqual(left: unknown, right: unknown): boolean;
|
|
3
3
|
export declare function mergeCompatibleEnumSchemas(existing: unknown, incoming: unknown): JsonObject | null;
|
|
4
4
|
export declare function mergePropertySchemas(existing: unknown, incoming: unknown): unknown;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export * from "./adapt";
|
|
2
|
-
export * from "./compatibility";
|
|
3
|
-
export * from "./dereference";
|
|
4
|
-
export * from "./draft";
|
|
5
|
-
export * from "./equality";
|
|
6
|
-
export * from "./fields";
|
|
7
|
-
export * from "./json-schema-validator";
|
|
8
|
-
export * from "./meta-validator";
|
|
9
|
-
export * from "./normalize";
|
|
10
|
-
export * from "./spill";
|
|
11
|
-
export * from "./strict-tool-validation";
|
|
12
|
-
export * from "./types";
|
|
13
|
-
export * from "./typescript";
|
|
14
|
-
export * from "./wire";
|
|
15
|
-
export * from "./zod-decontaminate";
|
|
1
|
+
export * from "./adapt.js";
|
|
2
|
+
export * from "./compatibility.js";
|
|
3
|
+
export * from "./dereference.js";
|
|
4
|
+
export * from "./draft.js";
|
|
5
|
+
export * from "./equality.js";
|
|
6
|
+
export * from "./fields.js";
|
|
7
|
+
export * from "./json-schema-validator.js";
|
|
8
|
+
export * from "./meta-validator.js";
|
|
9
|
+
export * from "./normalize.js";
|
|
10
|
+
export * from "./spill.js";
|
|
11
|
+
export * from "./strict-tool-validation.js";
|
|
12
|
+
export * from "./types.js";
|
|
13
|
+
export * from "./typescript.js";
|
|
14
|
+
export * from "./wire.js";
|
|
15
|
+
export * from "./zod-decontaminate.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type DescriptionSpillFormat } from "./spill";
|
|
2
|
-
import { type JsonObject } from "./types";
|
|
1
|
+
import { type DescriptionSpillFormat } from "./spill.js";
|
|
2
|
+
import { type JsonObject } from "./types.js";
|
|
3
3
|
export type ResidualSchemaIncompatibility = "type-array" | "type-null" | "nullable" | "combiners";
|
|
4
4
|
export interface NormalizeSchemaOptions {
|
|
5
5
|
unsupportedFields: (key: string) => boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ServerSentEvent } from "@oh-my-pi/pi-utils";
|
|
2
|
-
import type { RawSseEvent } from "../types";
|
|
2
|
+
import type { RawSseEvent } from "../types.js";
|
|
3
3
|
type RawSseObserver = (event: RawSseEvent) => void;
|
|
4
4
|
export declare function notifyRawSseEvent(observer: RawSseObserver | undefined, event: ServerSentEvent | RawSseEvent): void;
|
|
5
5
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Api, Model, StreamOptions } from "../types";
|
|
2
|
-
import { AssistantMessageEventStream } from "./event-stream";
|
|
1
|
+
import type { Api, Model, StreamOptions } from "../types.js";
|
|
2
|
+
import { AssistantMessageEventStream } from "./event-stream.js";
|
|
3
3
|
/** Stable lead phrase of the guard's error message; exported for tests. The
|
|
4
4
|
* message also carries "stream stall" so the session + transport retry
|
|
5
5
|
* classifiers treat it as a transient (retryable) stop without bespoke rules. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, ToolResultMessage } from "../types";
|
|
1
|
+
import type { AssistantMessage, ToolResultMessage } from "../types.js";
|
|
2
2
|
/** Runtime settings for cross-turn tool-call repetition detection. */
|
|
3
3
|
export interface ToolCallLoopGuardOptions {
|
|
4
4
|
readonly threshold: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utility functions for mapping unified ToolChoice to provider-specific formats.
|
|
3
3
|
*/
|
|
4
|
-
import type { ToolChoice } from "../types";
|
|
4
|
+
import type { ToolChoice } from "../types.js";
|
|
5
5
|
/** OpenAI Completions API tool choice format */
|
|
6
6
|
export type OpenAICompletionsToolChoice = "auto" | "none" | "required" | {
|
|
7
7
|
type: "function";
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ResponseInput } from "./providers/openai-responses-wire";
|
|
2
|
-
import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } from "./types";
|
|
1
|
+
import type { ResponseInput } from "./providers/openai-responses-wire.js";
|
|
2
|
+
import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } from "./types.js";
|
|
3
3
|
export { isRecord } from "@oh-my-pi/pi-utils";
|
|
4
4
|
export declare function normalizeSystemPrompts(systemPrompt: readonly string[] | string | undefined | null): string[];
|
|
5
5
|
export declare function normalizeToolCallId(id: string): string;
|
|
@@ -14,6 +14,18 @@ export declare function normalizeResponsesToolCallId(id: string, itemPrefix?: Re
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function truncateResponseItemId(id: string, prefix: string): string;
|
|
16
16
|
export declare function sanitizeOpenAIResponsesHistoryItemsForReplay(items: Array<Record<string, unknown>>): ResponseInput;
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize assistant-native Responses history for replay.
|
|
19
|
+
*
|
|
20
|
+
* Returns `undefined` for hidden-empty turns that only contain reasoning and an
|
|
21
|
+
* empty assistant message, allowing callers to rebuild visible transcript
|
|
22
|
+
* history instead of replaying stale native state.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sanitizeOpenAIResponsesAssistantHistoryItemsForReplay(items: Array<Record<string, unknown>>): ResponseInput | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Drop hidden-only fallback assistant replay after a native Responses snapshot is rejected.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeOpenAIResponsesAssistantFallbackItemsForReplay(items: ResponseInput): ResponseInput;
|
|
17
29
|
export declare function createOpenAIResponsesHistoryPayload(provider: string, items: Array<Record<string, unknown>>, incremental?: boolean): OpenAIResponsesHistoryPayload;
|
|
18
30
|
export declare function getOpenAIResponsesHistoryPayload(providerPayload: ProviderPayload | undefined, currentProvider: string, fallbackProvider?: string): OpenAIResponsesHistoryPayload | undefined;
|
|
19
31
|
export declare function getOpenAIResponsesHistoryItems(providerPayload: ProviderPayload | undefined, currentProvider: string, fallbackProvider?: string): Array<Record<string, unknown>> | undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "16.3.
|
|
4
|
+
"version": "16.3.8",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.0",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "16.3.
|
|
42
|
-
"@oh-my-pi/pi-utils": "16.3.
|
|
43
|
-
"@oh-my-pi/pi-wire": "16.3.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "16.3.8",
|
|
42
|
+
"@oh-my-pi/pi-utils": "16.3.8",
|
|
43
|
+
"@oh-my-pi/pi-wire": "16.3.8",
|
|
44
44
|
"arktype": "^2.2.0",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
package/src/dialect/demotion.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { preferredDialect } from "@oh-my-pi/pi-catalog/identity";
|
|
2
2
|
import { getDialectDefinition } from "./factory";
|
|
3
3
|
|
|
4
|
-
const CLAUDE_FABLE_ID = /(?:^|[./])claude[-.]fable(?:[-.]|$)/i;
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Wrap a prior-turn reasoning string for demotion into native conversation
|
|
8
|
-
* history — the cross-provider / cross-model case where the target cannot
|
|
9
|
-
* it as a structured thinking block (verified end-to-end against Gemini
|
|
10
|
-
* replayed unsigned `thought` part is schema-accepted but silently
|
|
11
|
-
* neither recalled nor influencing generation).
|
|
6
|
+
* history — the cross-provider / cross-model case where the target cannot
|
|
7
|
+
* replay it as a structured thinking block (verified end-to-end against Gemini
|
|
8
|
+
* 3: a replayed unsigned `thought` part is schema-accepted but silently
|
|
9
|
+
* discarded — neither recalled nor influencing generation).
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
* requests that replay prior
|
|
15
|
-
*
|
|
16
|
-
* chain-of-thought as an attempt to duplicate model outputs and
|
|
17
|
-
*
|
|
18
|
-
*
|
|
11
|
+
* The Anthropic/Claude dialect is the primary exception: Anthropic's
|
|
12
|
+
* `reasoning_extraction` classifier blocks requests that replay prior
|
|
13
|
+
* reasoning inside `<thinking>` / `antml:thinking` tags — it reads the
|
|
14
|
+
* wrapped chain-of-thought as an attempt to duplicate model outputs and
|
|
15
|
+
* refuses (Fable) or leaks it as visible reasoning (Opus / Sonnet / Haiku /
|
|
16
|
+
* Mythos). Every Anthropic-dialect Claude model therefore receives prior
|
|
17
|
+
* reasoning as bare assistant prose: no tag, no wrapper, no trailing newline.
|
|
19
18
|
* Heat is cumulative (block count and early-conversation position also raise
|
|
20
19
|
* it), so this lowers per-block signal but does not license unbounded replay.
|
|
21
|
-
*
|
|
20
|
+
*
|
|
21
|
+
* Harmony and Gemma are the other exceptions: their `renderThinking` emits
|
|
22
22
|
* chat-template control tokens (`<|channel|>analysis`, `<|channel>thought`)
|
|
23
|
-
* that must not appear inside a structured native message, so they fall back
|
|
24
|
-
* a plain `<think>` block. Every other dialect's thinking form is
|
|
25
|
-
* XML tags or a markdown fence.
|
|
23
|
+
* that must not appear inside a structured native message, so they fall back
|
|
24
|
+
* to a plain `<think>` block. Every other dialect's thinking form is
|
|
25
|
+
* inline-safe XML tags or a markdown fence.
|
|
26
26
|
*
|
|
27
27
|
* The result does not append a delimiter; callers that flatten adjacent blocks
|
|
28
28
|
* into a single string must insert their own separator.
|
|
@@ -33,9 +33,8 @@ const CLAUDE_FABLE_ID = /(?:^|[./])claude[-.]fable(?:[-.]|$)/i;
|
|
|
33
33
|
export function renderDemotedThinking(modelId: string, text: string): string {
|
|
34
34
|
if (!text) return "";
|
|
35
35
|
text = text.toWellFormed();
|
|
36
|
-
const canonicalId = bareModelId(modelId);
|
|
37
36
|
const dialect = preferredDialect(modelId);
|
|
38
|
-
if (
|
|
37
|
+
if (dialect === "anthropic") return text;
|
|
39
38
|
if (dialect === "harmony" || dialect === "gemma") return `<think>\n${text}\n</think>`;
|
|
40
39
|
return getDialectDefinition(dialect).renderThinking(text);
|
|
41
40
|
}
|
package/src/error/rate-limit.ts
CHANGED
|
@@ -100,7 +100,7 @@ export function calculateRateLimitBackoffMs(reason: RateLimitReason): number {
|
|
|
100
100
|
|
|
101
101
|
/** Detect usage/quota limit errors in error messages (persistent, requires credential switch). */
|
|
102
102
|
const USAGE_LIMIT_PATTERN =
|
|
103
|
-
/usage.?limit|usage_limit_reached|usage_not_included|limit_reached|quota.?exceeded|
|
|
103
|
+
/usage.?limit|usage_limit_reached|usage_not_included|limit_reached|quota.?(?:exceeded|reached|insufficient)|额度不足|额度耗尽|resource.?exhausted|exhausted your capacity|quota will reset|insufficient.?(?:balance|quota)/i;
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
106
|
* HTTP status codes that, absent richer body classification, represent an
|
|
@@ -170,8 +170,9 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
170
170
|
body: params,
|
|
171
171
|
signal: requestSignal,
|
|
172
172
|
fetch: options?.fetch,
|
|
173
|
-
//
|
|
174
|
-
|
|
173
|
+
// Transient 408/429/5xx get Retry-After-aware transport retries;
|
|
174
|
+
// the first-event watchdog aborts `requestSignal`, so retries
|
|
175
|
+
// cannot extend the caller's deadline.
|
|
175
176
|
onSseEvent: rawSseObserver,
|
|
176
177
|
});
|
|
177
178
|
openaiStream = handle.events;
|
package/src/providers/cursor.ts
CHANGED
|
@@ -1111,6 +1111,17 @@ async function handleExecServerMessage(
|
|
|
1111
1111
|
case "grepArgs": {
|
|
1112
1112
|
const args = execMsg.message.value;
|
|
1113
1113
|
if (!args.toolCallId) args.toolCallId = crypto.randomUUID();
|
|
1114
|
+
// Cursor's model sometimes emits `grepArgs` with an empty `pattern` and a
|
|
1115
|
+
// non-empty `glob`, expecting grep to list files matching the glob. Reject
|
|
1116
|
+
// that up front with an actionable error so the model retries with a real
|
|
1117
|
+
// regex or switches to `ls`/`read`, instead of the local grep tool
|
|
1118
|
+
// surfacing a bare "Pattern must not be empty" (issue #4574) after the
|
|
1119
|
+
// synthesized block has already been persisted with a placeholder pattern.
|
|
1120
|
+
const emptyPatternError = emptyGrepPatternRejection(args.pattern, args.glob);
|
|
1121
|
+
if (emptyPatternError !== null) {
|
|
1122
|
+
sendExecClientMessage(h2Request, execMsg, "grepResult", buildGrepErrorResult(emptyPatternError));
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1114
1125
|
// Mirror the coding-agent bridge's arg mapping so live UI (from
|
|
1115
1126
|
// `tool_execution_start`) and rebuilt transcript (from this block)
|
|
1116
1127
|
// display identical args.
|
|
@@ -1835,6 +1846,31 @@ function buildGrepErrorResult(error: string) {
|
|
|
1835
1846
|
});
|
|
1836
1847
|
}
|
|
1837
1848
|
|
|
1849
|
+
/**
|
|
1850
|
+
* Reject a Cursor exec-channel `grepArgs` frame whose `pattern` is empty or
|
|
1851
|
+
* whitespace-only. Returns an actionable error message when the pattern is
|
|
1852
|
+
* unusable (with a `glob`-aware hint when the model likely meant to list
|
|
1853
|
+
* files), or `null` when the pattern is valid and grep should run.
|
|
1854
|
+
*
|
|
1855
|
+
* Exported for tests. Cursor's model sometimes sends `pattern=""` together
|
|
1856
|
+
* with a non-empty `glob`, expecting grep to enumerate matching files; the
|
|
1857
|
+
* downstream coding-agent `grep` tool rejects that with a bare "Pattern must
|
|
1858
|
+
* not be empty", which the TUI renders as `?` in the tool preview (issue
|
|
1859
|
+
* #4574). Handling it at the Cursor exec dispatch keeps the synthesized
|
|
1860
|
+
* `toolCall` block off the persisted assistant message and gives the model a
|
|
1861
|
+
* specific recovery hint.
|
|
1862
|
+
*/
|
|
1863
|
+
export function emptyGrepPatternRejection(pattern: string | undefined, glob: string | undefined): string | null {
|
|
1864
|
+
if (pattern && pattern.trim().length > 0) return null;
|
|
1865
|
+
if (glob && glob.length > 0) {
|
|
1866
|
+
return (
|
|
1867
|
+
`grep pattern is required (received an empty pattern). To list files matching "${glob}", ` +
|
|
1868
|
+
`pass a non-empty regex (e.g. ".") and set path to that glob, or use the ls/read tool instead.`
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
return "grep pattern is required (received an empty pattern).";
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1838
1874
|
function buildDiagnosticsResultFromToolResult(path: string, toolResult: ToolResultMessage) {
|
|
1839
1875
|
const text = toolResultToText(toolResult);
|
|
1840
1876
|
if (toolResult.isError) {
|
|
@@ -263,6 +263,8 @@ export interface GoogleGeminiCliOptions extends StreamOptions {
|
|
|
263
263
|
*/
|
|
264
264
|
suppress?: { level: GoogleThinkingLevel } | { budget: number };
|
|
265
265
|
};
|
|
266
|
+
/** Request that Cloud Code Assist omit human-readable thought summaries while still allowing internal reasoning. */
|
|
267
|
+
hideThinkingSummary?: boolean;
|
|
266
268
|
/**
|
|
267
269
|
* Upstream wire model id override for collapsed effort-tier variants.
|
|
268
270
|
* Serialized as `requestModelId ?? model.requestModelId ?? model.id`.
|
|
@@ -1002,8 +1004,8 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
1002
1004
|
}
|
|
1003
1005
|
|
|
1004
1006
|
const streamed = await streamResponse(currentResponse);
|
|
1005
|
-
if (streamed) {
|
|
1006
|
-
receivedContent =
|
|
1007
|
+
if (output.stopReason !== "stop" || streamed) {
|
|
1008
|
+
receivedContent = streamed;
|
|
1007
1009
|
break;
|
|
1008
1010
|
}
|
|
1009
1011
|
|
|
@@ -1012,6 +1014,13 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
1012
1014
|
}
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1017
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
1018
|
+
throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", {
|
|
1019
|
+
provider: model.provider,
|
|
1020
|
+
kind: "output",
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1015
1024
|
if (!receivedContent) {
|
|
1016
1025
|
throw new AIError.ProviderResponseError("Cloud Code Assist API returned an empty response", {
|
|
1017
1026
|
provider: model.provider,
|
|
@@ -1242,7 +1251,7 @@ export function buildRequest(
|
|
|
1242
1251
|
// Thinking config
|
|
1243
1252
|
if (options.thinking?.enabled && model.reasoning) {
|
|
1244
1253
|
generationConfig.thinkingConfig = {
|
|
1245
|
-
includeThoughts:
|
|
1254
|
+
includeThoughts: !options.hideThinkingSummary,
|
|
1246
1255
|
};
|
|
1247
1256
|
// Gemini 3 models use thinkingLevel, older models use thinkingBudget
|
|
1248
1257
|
if (options.thinking.level !== undefined) {
|
|
@@ -75,6 +75,8 @@ export interface GoogleSharedStreamOptions extends StreamOptions {
|
|
|
75
75
|
budgetTokens?: number;
|
|
76
76
|
level?: GoogleThinkingLevel;
|
|
77
77
|
};
|
|
78
|
+
/** Request that Google omit human-readable thought summaries while still allowing internal reasoning. */
|
|
79
|
+
hideThinkingSummary?: boolean;
|
|
78
80
|
/** Gemini/Vertex serving tier (`flex`/`priority`); other values are omitted. */
|
|
79
81
|
serviceTier?: ServiceTier;
|
|
80
82
|
/**
|
|
@@ -859,7 +861,7 @@ export function buildGoogleGenerateContentParams<T extends "google-generative-ai
|
|
|
859
861
|
}
|
|
860
862
|
|
|
861
863
|
if (options.thinking?.enabled && model.reasoning) {
|
|
862
|
-
const cfg: ThinkingConfig = { includeThoughts:
|
|
864
|
+
const cfg: ThinkingConfig = { includeThoughts: !options.hideThinkingSummary };
|
|
863
865
|
if (options.thinking.level !== undefined) {
|
|
864
866
|
// GoogleThinkingLevel mirrors the SDK's `ThinkingLevel` string enum values 1:1.
|
|
865
867
|
cfg.thinkingLevel = options.thinking.level as ThinkingLevel;
|
package/src/providers/mock.ts
CHANGED
|
@@ -446,10 +446,17 @@ function mergeUsage(partial?: Partial<Omit<Usage, "cost">> & { cost?: Partial<Us
|
|
|
446
446
|
if (costProvided) {
|
|
447
447
|
merged.cost = { ...base.cost, ...partial.cost } as Usage["cost"];
|
|
448
448
|
}
|
|
449
|
-
// Recompute totalTokens when not explicitly provided (canonical formula matches types.ts
|
|
450
|
-
// input + output + cacheRead + cacheWrite).
|
|
449
|
+
// Recompute totalTokens when not explicitly provided (canonical formula matches types.ts).
|
|
451
450
|
if (partial.totalTokens === undefined) {
|
|
452
|
-
|
|
451
|
+
const orchestration = merged.orchestration;
|
|
452
|
+
merged.totalTokens =
|
|
453
|
+
merged.input +
|
|
454
|
+
merged.output +
|
|
455
|
+
merged.cacheRead +
|
|
456
|
+
merged.cacheWrite +
|
|
457
|
+
(orchestration?.input ?? 0) +
|
|
458
|
+
(orchestration?.output ?? 0) +
|
|
459
|
+
(orchestration?.cacheRead ?? 0);
|
|
453
460
|
}
|
|
454
461
|
// Recompute cost.total when cost components were supplied without an explicit total.
|
|
455
462
|
if (costProvided && partial.cost?.total === undefined) {
|