@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.
Files changed (226) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/types/api-registry.d.ts +1 -1
  3. package/dist/types/auth-broker/client.d.ts +2 -2
  4. package/dist/types/auth-broker/discover.d.ts +1 -1
  5. package/dist/types/auth-broker/index.d.ts +7 -7
  6. package/dist/types/auth-broker/refresher.d.ts +1 -1
  7. package/dist/types/auth-broker/remote-store.d.ts +6 -6
  8. package/dist/types/auth-broker/server.d.ts +1 -1
  9. package/dist/types/auth-broker/snapshot-cache.d.ts +1 -1
  10. package/dist/types/auth-broker/types.d.ts +2 -2
  11. package/dist/types/auth-gateway/index.d.ts +3 -3
  12. package/dist/types/auth-gateway/server.d.ts +3 -3
  13. package/dist/types/auth-gateway/types.d.ts +1 -1
  14. package/dist/types/auth-retry.d.ts +2 -2
  15. package/dist/types/auth-storage.d.ts +6 -6
  16. package/dist/types/dialect/anthropic.d.ts +1 -1
  17. package/dist/types/dialect/catalog.d.ts +1 -1
  18. package/dist/types/dialect/coercion.d.ts +1 -1
  19. package/dist/types/dialect/deepseek.d.ts +1 -1
  20. package/dist/types/dialect/demotion.d.ts +16 -14
  21. package/dist/types/dialect/examples.d.ts +1 -1
  22. package/dist/types/dialect/factory.d.ts +1 -1
  23. package/dist/types/dialect/gemini.d.ts +1 -1
  24. package/dist/types/dialect/gemma.d.ts +1 -1
  25. package/dist/types/dialect/glm.d.ts +1 -1
  26. package/dist/types/dialect/harmony.d.ts +1 -1
  27. package/dist/types/dialect/hermes.d.ts +1 -1
  28. package/dist/types/dialect/history.d.ts +2 -2
  29. package/dist/types/dialect/index.d.ts +11 -11
  30. package/dist/types/dialect/inventory.d.ts +1 -1
  31. package/dist/types/dialect/kimi.d.ts +1 -1
  32. package/dist/types/dialect/minimax.d.ts +1 -1
  33. package/dist/types/dialect/owned-stream.d.ts +2 -2
  34. package/dist/types/dialect/qwen3.d.ts +1 -1
  35. package/dist/types/dialect/rendering.d.ts +2 -2
  36. package/dist/types/dialect/thinking.d.ts +1 -1
  37. package/dist/types/dialect/types.d.ts +1 -1
  38. package/dist/types/dialect/xml.d.ts +1 -1
  39. package/dist/types/error/classes.d.ts +1 -1
  40. package/dist/types/error/finalize.d.ts +3 -3
  41. package/dist/types/error/flags.d.ts +1 -1
  42. package/dist/types/error/format.d.ts +1 -1
  43. package/dist/types/error/index.d.ts +13 -13
  44. package/dist/types/error/provider.d.ts +1 -1
  45. package/dist/types/error/rate-limit.d.ts +1 -1
  46. package/dist/types/index.d.ts +47 -47
  47. package/dist/types/provider-details.d.ts +1 -1
  48. package/dist/types/providers/amazon-bedrock.d.ts +1 -1
  49. package/dist/types/providers/anthropic-client.d.ts +3 -3
  50. package/dist/types/providers/anthropic-messages-server-schema.d.ts +1 -1
  51. package/dist/types/providers/anthropic-messages-server.d.ts +2 -2
  52. package/dist/types/providers/anthropic-wire.d.ts +1 -1
  53. package/dist/types/providers/anthropic.d.ts +3 -3
  54. package/dist/types/providers/aws-credentials.d.ts +2 -2
  55. package/dist/types/providers/azure-openai-responses.d.ts +2 -2
  56. package/dist/types/providers/cursor.d.ts +18 -3
  57. package/dist/types/providers/devin.d.ts +1 -1
  58. package/dist/types/providers/error-message.d.ts +1 -1
  59. package/dist/types/providers/github-copilot-headers.d.ts +1 -1
  60. package/dist/types/providers/gitlab-duo-workflow.d.ts +2 -2
  61. package/dist/types/providers/gitlab-duo.d.ts +2 -2
  62. package/dist/types/providers/google-auth.d.ts +1 -1
  63. package/dist/types/providers/google-gemini-cli.d.ts +6 -4
  64. package/dist/types/providers/google-interactions.d.ts +3 -3
  65. package/dist/types/providers/google-shared.d.ts +7 -5
  66. package/dist/types/providers/google-vertex.d.ts +2 -2
  67. package/dist/types/providers/google.d.ts +2 -2
  68. package/dist/types/providers/kimi.d.ts +3 -3
  69. package/dist/types/providers/mock.d.ts +2 -2
  70. package/dist/types/providers/ollama.d.ts +1 -1
  71. package/dist/types/providers/openai-anthropic-shim.d.ts +2 -2
  72. package/dist/types/providers/openai-chat-server-schema.d.ts +1 -1
  73. package/dist/types/providers/openai-chat-server.d.ts +2 -2
  74. package/dist/types/providers/openai-codex/request-transformer.d.ts +1 -1
  75. package/dist/types/providers/openai-codex/response-handler.d.ts +1 -1
  76. package/dist/types/providers/openai-codex-responses.d.ts +3 -3
  77. package/dist/types/providers/openai-completions.d.ts +3 -3
  78. package/dist/types/providers/openai-reasoning-fallback.d.ts +1 -1
  79. package/dist/types/providers/openai-responses-server-schema.d.ts +1 -1
  80. package/dist/types/providers/openai-responses-server.d.ts +2 -2
  81. package/dist/types/providers/openai-responses.d.ts +5 -5
  82. package/dist/types/providers/openai-shared.d.ts +10 -9
  83. package/dist/types/providers/pi-native-client.d.ts +1 -1
  84. package/dist/types/providers/pi-native-server.d.ts +2 -2
  85. package/dist/types/providers/register-builtins.d.ts +3 -3
  86. package/dist/types/providers/synthetic.d.ts +3 -3
  87. package/dist/types/providers/transform-messages.d.ts +1 -1
  88. package/dist/types/providers/vision-guard.d.ts +1 -1
  89. package/dist/types/registry/alibaba-coding-plan.d.ts +1 -1
  90. package/dist/types/registry/anthropic.d.ts +1 -1
  91. package/dist/types/registry/api-key-login.d.ts +1 -1
  92. package/dist/types/registry/api-key-validation.d.ts +1 -1
  93. package/dist/types/registry/baseten.d.ts +2 -2
  94. package/dist/types/registry/cerebras.d.ts +2 -2
  95. package/dist/types/registry/cloudflare-ai-gateway.d.ts +1 -1
  96. package/dist/types/registry/coreweave.d.ts +2 -2
  97. package/dist/types/registry/cursor.d.ts +1 -1
  98. package/dist/types/registry/deepseek.d.ts +1 -1
  99. package/dist/types/registry/devin.d.ts +1 -1
  100. package/dist/types/registry/firepass.d.ts +2 -2
  101. package/dist/types/registry/fireworks.d.ts +2 -2
  102. package/dist/types/registry/github-copilot.d.ts +1 -1
  103. package/dist/types/registry/gitlab-duo-workflow.d.ts +1 -1
  104. package/dist/types/registry/gitlab-duo.d.ts +1 -1
  105. package/dist/types/registry/google-antigravity.d.ts +1 -1
  106. package/dist/types/registry/google-gemini-cli.d.ts +1 -1
  107. package/dist/types/registry/huggingface.d.ts +2 -2
  108. package/dist/types/registry/index.d.ts +4 -4
  109. package/dist/types/registry/kagi.d.ts +1 -1
  110. package/dist/types/registry/kilo.d.ts +1 -1
  111. package/dist/types/registry/kimi-code.d.ts +1 -1
  112. package/dist/types/registry/litellm.d.ts +1 -1
  113. package/dist/types/registry/llama-cpp.d.ts +1 -1
  114. package/dist/types/registry/lm-studio.d.ts +1 -1
  115. package/dist/types/registry/minimax-code-cn.d.ts +1 -1
  116. package/dist/types/registry/minimax-code.d.ts +1 -1
  117. package/dist/types/registry/moonshot.d.ts +2 -2
  118. package/dist/types/registry/nanogpt.d.ts +2 -2
  119. package/dist/types/registry/nvidia.d.ts +1 -1
  120. package/dist/types/registry/oauth/anthropic.d.ts +3 -3
  121. package/dist/types/registry/oauth/callback-server.d.ts +1 -1
  122. package/dist/types/registry/oauth/cursor.d.ts +1 -1
  123. package/dist/types/registry/oauth/devin.d.ts +1 -1
  124. package/dist/types/registry/oauth/github-copilot.d.ts +2 -2
  125. package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +2 -2
  126. package/dist/types/registry/oauth/gitlab-duo.d.ts +1 -1
  127. package/dist/types/registry/oauth/google-antigravity.d.ts +1 -1
  128. package/dist/types/registry/oauth/google-gemini-cli.d.ts +1 -1
  129. package/dist/types/registry/oauth/google-oauth-shared.d.ts +2 -2
  130. package/dist/types/registry/oauth/index.d.ts +2 -2
  131. package/dist/types/registry/oauth/kimi.d.ts +1 -1
  132. package/dist/types/registry/oauth/minimax-code.d.ts +2 -2
  133. package/dist/types/registry/oauth/openai-codex.d.ts +1 -1
  134. package/dist/types/registry/oauth/opencode.d.ts +1 -1
  135. package/dist/types/registry/oauth/perplexity.d.ts +1 -1
  136. package/dist/types/registry/oauth/types.d.ts +2 -2
  137. package/dist/types/registry/oauth/wafer.d.ts +1 -1
  138. package/dist/types/registry/oauth/xai-oauth.d.ts +3 -3
  139. package/dist/types/registry/oauth/xiaomi.d.ts +1 -1
  140. package/dist/types/registry/ollama-cloud.d.ts +1 -1
  141. package/dist/types/registry/ollama.d.ts +1 -1
  142. package/dist/types/registry/openai-codex-device.d.ts +1 -1
  143. package/dist/types/registry/openai-codex.d.ts +1 -1
  144. package/dist/types/registry/opencode-go.d.ts +1 -1
  145. package/dist/types/registry/opencode-zen.d.ts +1 -1
  146. package/dist/types/registry/openrouter.d.ts +2 -2
  147. package/dist/types/registry/parallel.d.ts +1 -1
  148. package/dist/types/registry/perplexity.d.ts +2 -2
  149. package/dist/types/registry/qianfan.d.ts +2 -2
  150. package/dist/types/registry/qwen-portal.d.ts +1 -1
  151. package/dist/types/registry/registry.d.ts +69 -69
  152. package/dist/types/registry/sakana.d.ts +2 -2
  153. package/dist/types/registry/synthetic.d.ts +2 -2
  154. package/dist/types/registry/tavily.d.ts +1 -1
  155. package/dist/types/registry/together.d.ts +1 -1
  156. package/dist/types/registry/types.d.ts +1 -1
  157. package/dist/types/registry/umans.d.ts +2 -2
  158. package/dist/types/registry/venice.d.ts +2 -2
  159. package/dist/types/registry/vercel-ai-gateway.d.ts +1 -1
  160. package/dist/types/registry/vllm.d.ts +1 -1
  161. package/dist/types/registry/wafer-serverless.d.ts +1 -1
  162. package/dist/types/registry/xai-oauth.d.ts +1 -1
  163. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +1 -1
  164. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +1 -1
  165. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +1 -1
  166. package/dist/types/registry/xiaomi.d.ts +1 -1
  167. package/dist/types/registry/zai.d.ts +2 -2
  168. package/dist/types/registry/zenmux.d.ts +2 -2
  169. package/dist/types/registry/zhipu-coding-plan.d.ts +2 -2
  170. package/dist/types/stream.d.ts +6 -6
  171. package/dist/types/types.d.ts +24 -21
  172. package/dist/types/usage/claude.d.ts +1 -1
  173. package/dist/types/usage/gemini.d.ts +1 -1
  174. package/dist/types/usage/github-copilot.d.ts +1 -1
  175. package/dist/types/usage/google-antigravity.d.ts +1 -1
  176. package/dist/types/usage/kimi.d.ts +1 -1
  177. package/dist/types/usage/minimax-code.d.ts +1 -1
  178. package/dist/types/usage/ollama.d.ts +1 -1
  179. package/dist/types/usage/openai-codex-reset.d.ts +1 -1
  180. package/dist/types/usage/openai-codex.d.ts +1 -1
  181. package/dist/types/usage/opencode-go.d.ts +1 -1
  182. package/dist/types/usage/zai.d.ts +1 -1
  183. package/dist/types/usage.d.ts +1 -1
  184. package/dist/types/utils/block-symbols.d.ts +15 -0
  185. package/dist/types/utils/empty-completion-retry.d.ts +2 -2
  186. package/dist/types/utils/event-stream.d.ts +1 -1
  187. package/dist/types/utils/harmony-leak.d.ts +1 -1
  188. package/dist/types/utils/http-inspector.d.ts +19 -0
  189. package/dist/types/utils/leaked-thinking-stream.d.ts +1 -1
  190. package/dist/types/utils/openai-http.d.ts +3 -9
  191. package/dist/types/utils/provider-response.d.ts +1 -1
  192. package/dist/types/utils/proxy.d.ts +1 -1
  193. package/dist/types/utils/request-debug.d.ts +1 -1
  194. package/dist/types/utils/retry.d.ts +1 -1
  195. package/dist/types/utils/schema/equality.d.ts +1 -1
  196. package/dist/types/utils/schema/index.d.ts +15 -15
  197. package/dist/types/utils/schema/normalize.d.ts +2 -2
  198. package/dist/types/utils/schema/spill.d.ts +1 -1
  199. package/dist/types/utils/schema/wire.d.ts +1 -1
  200. package/dist/types/utils/sse-debug.d.ts +1 -1
  201. package/dist/types/utils/thinking-loop.d.ts +2 -2
  202. package/dist/types/utils/tool-call-loop-guard.d.ts +1 -1
  203. package/dist/types/utils/tool-choice.d.ts +1 -1
  204. package/dist/types/utils/validation.d.ts +1 -1
  205. package/dist/types/utils.d.ts +14 -2
  206. package/package.json +4 -4
  207. package/src/dialect/demotion.ts +18 -19
  208. package/src/error/rate-limit.ts +1 -1
  209. package/src/providers/azure-openai-responses.ts +3 -2
  210. package/src/providers/cursor.ts +36 -0
  211. package/src/providers/google-gemini-cli.ts +12 -3
  212. package/src/providers/google-shared.ts +3 -1
  213. package/src/providers/mock.ts +10 -3
  214. package/src/providers/openai-codex-responses.ts +47 -39
  215. package/src/providers/openai-completions.ts +48 -9
  216. package/src/providers/openai-responses.ts +37 -23
  217. package/src/providers/openai-shared.ts +145 -26
  218. package/src/providers/transform-messages.ts +63 -9
  219. package/src/registry/oauth/wafer.ts +1 -1
  220. package/src/stream.ts +6 -0
  221. package/src/types.ts +27 -3
  222. package/src/utils/block-symbols.ts +18 -0
  223. package/src/utils/http-inspector.ts +32 -4
  224. package/src/utils/openai-http.ts +6 -9
  225. package/src/utils/retry-after.ts +12 -1
  226. package/src/utils.ts +78 -0
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Normalizes Copilot quota usage into the shared UsageReport schema.
5
5
  */
6
- import type { UsageProvider } from "../usage";
6
+ import type { UsageProvider } from "../usage.js";
7
7
  export declare const githubCopilotUsageProvider: UsageProvider;
@@ -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;
@@ -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
  /**
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage, AssistantMessageEvent } from "../types";
1
+ import type { AssistantMessage, AssistantMessageEvent } from "../types.js";
2
2
  export declare class EventStream<T, R = T> implements AsyncIterable<T> {
3
3
  #private;
4
4
  queue: T[];
@@ -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 { FetchImpl } from "../types";
1
+ import type { FetchImpl } from "../types.js";
2
2
  export type RequestDebugHeaders = Headers | Record<string, string | string[] | number | undefined | null> | undefined;
3
3
  export interface RequestDebugPayload {
4
4
  method: string;
@@ -1,4 +1,4 @@
1
- import { isCopilotTransientModelError } from "../error/flags";
1
+ import { isCopilotTransientModelError } from "../error/flags.js";
2
2
  export { isCopilotTransientModelError };
3
3
  /**
4
4
  * Wrap an initial Copilot request so transient `model_not_supported` 400s are
@@ -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,4 +1,4 @@
1
- import type { JsonObject } from "./types";
1
+ import type { JsonObject } from "./types.js";
2
2
  export type DescriptionSpillFormat = "spill" | "paren";
3
3
  /**
4
4
  * Demote stripped JSON Schema keywords into a node's `description` so the model
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import type { Type } from "arktype";
14
14
  import { type ZodType } from "zod/v4";
15
- import type { Tool } from "../../types";
15
+ import type { Tool } from "../../types.js";
16
16
  /**
17
17
  * True when `value` is a live Zod schema instance.
18
18
  *
@@ -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";
@@ -1,4 +1,4 @@
1
- import type { Tool, ToolCall } from "../types";
1
+ import type { Tool, ToolCall } from "../types.js";
2
2
  /**
3
3
  * Finds a tool by name and validates the tool call arguments against its schema.
4
4
  * @param tools Array of tool definitions
@@ -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.6",
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.6",
42
- "@oh-my-pi/pi-utils": "16.3.6",
43
- "@oh-my-pi/pi-wire": "16.3.6",
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
  },
@@ -1,28 +1,28 @@
1
- import { bareModelId, preferredDialect } from "@oh-my-pi/pi-catalog/identity";
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 replay
9
- * it as a structured thinking block (verified end-to-end against Gemini 3: a
10
- * replayed unsigned `thought` part is schema-accepted but silently discarded —
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
- * Fable is the exception: Anthropic's `reasoning_extraction` classifier blocks
14
- * requests that replay prior reasoning inside `<thinking>` / `antml:thinking`
15
- * tags OR the older `_Hmm. …_` italic envelope — it reads the wrapped
16
- * chain-of-thought as an attempt to duplicate model outputs and refuses the
17
- * whole turn. Fable therefore receives prior reasoning as bare assistant prose:
18
- * no tag, no `_Hmm.` wrapper, no trailing newline.
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
- * Harmony and Gemma are also exceptions: their `renderThinking` emits
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 to
24
- * a plain `<think>` block. Every other dialect's thinking form is inline-safe
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 (CLAUDE_FABLE_ID.test(canonicalId)) return text;
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
  }
@@ -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|quota.?reached|resource.?exhausted|exhausted your capacity|quota will reset|insufficient.?(?:balance|quota)/i;
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
- // Watchdog armed no retries, so they cannot silently extend the deadline.
174
- maxAttempts: requestTimeoutMs !== undefined ? 1 : undefined,
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;
@@ -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 = true;
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: true,
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: true };
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;
@@ -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
- merged.totalTokens = merged.input + merged.output + merged.cacheRead + merged.cacheWrite;
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) {