@prestyj/ai 5.27.0 → 5.28.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/README.md CHANGED
@@ -40,10 +40,10 @@ Tool parameters are Zod schemas. Converted to JSON Schema at the provider bounda
40
40
 
41
41
  | Provider | Models | Notes |
42
42
  |---|---|---|
43
- | `anthropic` | Claude Fable 5.1, Opus 5.5, Opus 5, Sonnet 5, Haiku 4.5 | Extended thinking, prompt caching, server-side compaction |
43
+ | `anthropic` | Claude Fable 5.1, Opus 5.5, Opus 5, Sonnet 5.5, Sonnet 5, Haiku 4.5 | Extended thinking, prompt caching, server-side compaction |
44
44
  | `openai` | GPT-4.1, o3, o4-mini | Supports OAuth (codex endpoint) and API keys |
45
45
  | `glm` | GLM-5.3 | Z.AI platform, OpenAI-compatible |
46
- | `moonshot` | Kimi K3, Kimi K2.7 Code | Moonshot platform, OpenAI-compatible |
46
+ | `moonshot` | Kimi K3, Kimi K2.8 Preview, Kimi K2.7 Code | Moonshot platform, OpenAI-compatible |
47
47
 
48
48
  ---
49
49
 
package/dist/index.cjs CHANGED
@@ -1100,7 +1100,7 @@ function isAdaptiveThinkingModel(model) {
1100
1100
  function toAnthropicThinking(level, maxTokens, model) {
1101
1101
  if (isAdaptiveThinkingModel(model)) {
1102
1102
  let effort = level;
1103
- if (effort === "xhigh" && !/opus-5|opus-4-8|opus-4-7/.test(model)) {
1103
+ if (effort === "xhigh" && !/opus-5|opus-4[-.]8|opus-4[-.]7|sonnet-5[-.]5/.test(model)) {
1104
1104
  effort = "high";
1105
1105
  }
1106
1106
  return {
@@ -2103,7 +2103,7 @@ async function* runStream2(options) {
2103
2103
  const endpointKey = reasoningFieldKey(providerName, options.baseUrl, options.model);
2104
2104
  const client = createClient2(options);
2105
2105
  const isLocal = options.provider === "local";
2106
- const isKimiK3 = options.provider === "moonshot" && options.model === "kimi-k3";
2106
+ const isKimiK3 = options.provider === "moonshot" && (options.model === "kimi-k3" || options.model === "kimi-for-coding");
2107
2107
  const isManagedKimiK3 = isKimiK3 && options.baseUrl?.replace(/\/+$/, "").endsWith("/coding/v1") === true;
2108
2108
  const k3Effort = options.thinking ? toKimiK3Effort(options.thinking) : void 0;
2109
2109
  const isKimiK27 = options.provider === "moonshot" && options.model.startsWith("kimi-k2.7-code");
@@ -2577,7 +2577,7 @@ function extractRequestIdFromMessage(message) {
2577
2577
 
2578
2578
  // src/providers/openai-codex.ts
2579
2579
  var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
2580
- var CODEX_CLIENT_VERSION = "0.155.1";
2580
+ var CODEX_CLIENT_VERSION = "0.159.1";
2581
2581
  var CODEX_REQUEST_COMPRESSION_MIN_BYTES = 16 * 1024;
2582
2582
  var zstdInitPromise;
2583
2583
  async function encodeCodexRequest(body) {
@@ -2623,7 +2623,7 @@ async function encodeCodexRequest(body) {
2623
2623
  }
2624
2624
  }
2625
2625
  function usesResponsesLite(model) {
2626
- return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-");
2626
+ return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-") || model.startsWith("gpt-6.");
2627
2627
  }
2628
2628
  function outputTextKey(itemId, contentIndex) {
2629
2629
  return `${itemId ?? ""}:${contentIndex ?? 0}`;
@@ -2745,7 +2745,7 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2745
2745
  if (response.status === 400 && message === `The '${options.model}' model is not supported when using Codex with a ChatGPT account.`) {
2746
2746
  hint = "This model is not available through your ChatGPT account. Switch to a model listed for OpenAI via the model selector, or check your ChatGPT usage limits.";
2747
2747
  } else if (response.status === 404 && text.includes("does not exist")) {
2748
- hint = "This model is not in OpenAI's current catalog for your ChatGPT account. Switch to GPT-6 Astra, GPT-6 Sol, or GPT-6 Luna via the model selector.";
2748
+ hint = "This model is not in OpenAI's current catalog for your ChatGPT account. Switch to GPT-6 Astra, GPT-6.1 Sol, or GPT-6 Luna via the model selector.";
2749
2749
  }
2750
2750
  throw new ProviderError("openai", message, {
2751
2751
  statusCode: response.status,
@@ -2759,6 +2759,8 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2759
2759
  const contentParts = [];
2760
2760
  let textAccum = "";
2761
2761
  const toolCalls = /* @__PURE__ */ new Map();
2762
+ const finishedToolCalls = /* @__PURE__ */ new Set();
2763
+ let terminal;
2762
2764
  const orderedItems = [];
2763
2765
  const outputItemTypes = /* @__PURE__ */ new Map();
2764
2766
  const outputTextByPart = /* @__PURE__ */ new Map();
@@ -2902,6 +2904,7 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2902
2904
  for (const [key, tc] of toolCalls) {
2903
2905
  if (key.endsWith(`|${itemId}`)) {
2904
2906
  tc.argsJson = argsStr;
2907
+ finishedToolCalls.add(key);
2905
2908
  break;
2906
2909
  }
2907
2910
  }
@@ -2927,6 +2930,7 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2927
2930
  const id = `${callId}|${itemId}`;
2928
2931
  const tc = toolCalls.get(id);
2929
2932
  if (tc) {
2933
+ finishedToolCalls.add(id);
2930
2934
  orderedItems.push({ kind: "tool", id });
2931
2935
  const args = parseToolArguments(tc.argsJson);
2932
2936
  yield {
@@ -2938,8 +2942,17 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2938
2942
  }
2939
2943
  }
2940
2944
  }
2941
- if (type === "response.completed" || type === "response.done") {
2945
+ if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
2942
2946
  const resp = event.response;
2947
+ if (type === "response.incomplete" || resp?.status === "incomplete") {
2948
+ const details = resp?.incomplete_details;
2949
+ terminal = {
2950
+ status: "incomplete",
2951
+ reason: typeof details?.reason === "string" ? details.reason : void 0
2952
+ };
2953
+ } else {
2954
+ terminal = { status: "completed" };
2955
+ }
2943
2956
  const usage = resp?.usage;
2944
2957
  if (usage) {
2945
2958
  cacheRead = usage.input_tokens_details?.cached_tokens ?? 0;
@@ -2949,6 +2962,25 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2949
2962
  }
2950
2963
  }
2951
2964
  }
2965
+ if (!terminal) {
2966
+ throw new ProviderError("openai", "Stream ended before completion (no response.completed).", {
2967
+ statusCode: 504
2968
+ });
2969
+ }
2970
+ const droppedToolCalls = [...toolCalls.keys()].filter((id) => !finishedToolCalls.has(id)).length;
2971
+ if (terminal.status === "completed") {
2972
+ for (const [id, tc] of toolCalls) {
2973
+ if (!finishedToolCalls.has(id)) {
2974
+ throw new ProviderError(
2975
+ "openai",
2976
+ `Codex reply completed with an unfinished tool call: ${tc.name} (${id}).`,
2977
+ { statusCode: 502 }
2978
+ );
2979
+ }
2980
+ }
2981
+ } else {
2982
+ providerDiag("codex_incomplete", { reason: terminal.reason ?? null, droppedToolCalls });
2983
+ }
2952
2984
  const seenTool = /* @__PURE__ */ new Set();
2953
2985
  let textInserted = false;
2954
2986
  for (const entry of orderedItems) {
@@ -2975,7 +3007,7 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2975
3007
  contentParts.push({ type: "text", text: textAccum });
2976
3008
  }
2977
3009
  for (const [id, tc] of toolCalls) {
2978
- if (seenTool.has(id)) continue;
3010
+ if (seenTool.has(id) || !finishedToolCalls.has(id)) continue;
2979
3011
  seenTool.add(id);
2980
3012
  contentParts.push({
2981
3013
  type: "tool_call",
@@ -2984,8 +3016,15 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
2984
3016
  args: parseToolArguments(tc.argsJson)
2985
3017
  });
2986
3018
  }
3019
+ if (droppedToolCalls > 0) {
3020
+ let last = contentParts.at(-1);
3021
+ while (last?.type === "raw" && isEncryptedReasoning(last.data)) {
3022
+ contentParts.pop();
3023
+ last = contentParts.at(-1);
3024
+ }
3025
+ }
2987
3026
  const hasToolCalls = contentParts.some((p) => p.type === "tool_call");
2988
- const stopReason = hasToolCalls ? "tool_use" : "end_turn";
3027
+ const stopReason = terminal.status === "incomplete" ? incompleteStopReason(terminal.reason) : hasToolCalls ? "tool_use" : "end_turn";
2989
3028
  const streamResponse = {
2990
3029
  message: {
2991
3030
  role: "assistant",
@@ -3002,6 +3041,11 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3002
3041
  yield { type: "done", stopReason };
3003
3042
  return streamResponse;
3004
3043
  }
3044
+ function incompleteStopReason(reason) {
3045
+ if (reason === "max_output_tokens") return "max_tokens";
3046
+ if (reason === "content_filter") return "refusal";
3047
+ return "error";
3048
+ }
3005
3049
  async function* parseSSE(body) {
3006
3050
  for await (const event of readSseStream(body)) {
3007
3051
  const data = event.data.trim();
@@ -4137,6 +4181,8 @@ var CIRCULAR = "[CIRCULAR]";
4137
4181
  var SENSITIVE_NAME = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|key|auth(?:orization)?|bearer|cookie|credential|private[_-]?key|password|passwd|secret)(?:$|[_-])/i;
4138
4182
  var ENV_SECRET_ASSIGNMENT = /\b((?:[A-Z0-9]+_)*(?:API_?KEY|ACCESS_TOKEN|REFRESH_TOKEN|TOKEN|KEY|AUTH|AUTHORIZATION|BEARER|CREDENTIALS?|PASSWORD|PASSWD|SECRET))\b(\s*[=:]\s*)(["']?)(?!\$|process\.env|os\.environ|import\.meta|env\.)(?=[^\s,"';}]*\d)([^\s,"';}=$][^\s,"';}]{7,})\3/g;
4139
4183
  var COMPACT_SECRET_ASSIGNMENT = /\b((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|auth|authorization|credentials?|password|passwd|secret)|(?:[a-z0-9]+[_-])+key)=(["']?)(?!\$)([^\s,"'&;}=][^\s,"'&;}]{7,})\2/gi;
4184
+ var URL_USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/?#@:"'`<>]*:[^\s/?#"'`<>]+@/gi;
4185
+ var URL_USERINFO_TO_FIRST_AT = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]*:[^\s/@]+@/gi;
4140
4186
  function escaped(value) {
4141
4187
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4142
4188
  }
@@ -4160,7 +4206,8 @@ function redactText(text, options = {}) {
4160
4206
  /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g,
4161
4207
  REDACTED
4162
4208
  );
4163
- result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, `$1${REDACTED}@`);
4209
+ result = result.replace(URL_USERINFO, `$1${REDACTED}@`);
4210
+ result = result.replace(URL_USERINFO_TO_FIRST_AT, `$1${REDACTED}@`);
4164
4211
  result = result.replace(
4165
4212
  /\b(authorization\s*[:=]\s*)(?:bearer|basic)\s+[^\s,;]+/gi,
4166
4213
  `$1${REDACTED}`
@@ -4204,7 +4251,7 @@ function isMediaObject(value) {
4204
4251
  function redactValue(value, options = {}) {
4205
4252
  const maxDepth = options.maxDepth ?? 20;
4206
4253
  const maxEntries = options.maxEntries ?? 1e4;
4207
- const seen = /* @__PURE__ */ new WeakSet();
4254
+ const ancestors = /* @__PURE__ */ new WeakSet();
4208
4255
  let entries = 0;
4209
4256
  const visit = (current, depth, sensitive = false) => {
4210
4257
  if (typeof current === "string") {
@@ -4218,8 +4265,15 @@ function redactValue(value, options = {}) {
4218
4265
  if (isBinary(current)) return current;
4219
4266
  if (current instanceof Date) return new Date(current.getTime());
4220
4267
  if (depth >= maxDepth) return TRUNCATED;
4221
- if (seen.has(current)) return CIRCULAR;
4222
- seen.add(current);
4268
+ if (ancestors.has(current)) return CIRCULAR;
4269
+ ancestors.add(current);
4270
+ try {
4271
+ return cloneObject(current, depth);
4272
+ } finally {
4273
+ ancestors.delete(current);
4274
+ }
4275
+ };
4276
+ const cloneObject = (current, depth) => {
4223
4277
  if (current instanceof Error) {
4224
4278
  const error = {
4225
4279
  name: current.name,