@prestyj/ai 5.8.0 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -41,6 +41,21 @@ interface ToolResult {
41
41
  toolCallId: string;
42
42
  content: ToolResultContent;
43
43
  isError?: boolean;
44
+ /**
45
+ * Set when the agent loop trimmed `content` to fit a per-result or per-turn
46
+ * budget. The provider (model input) and the persistent transcript both see
47
+ * the trimmed `content`, but the live `tool_call_end` event carried the FULL
48
+ * preview — so this marker makes that divergence explicit and reconcilable.
49
+ * Internal metadata only: it is never serialized onto the provider wire.
50
+ */
51
+ capped?: {
52
+ /** Length of the original, untrimmed string content. */
53
+ originalChars: number;
54
+ /** Length of the trimmed content actually sent to the model. */
55
+ keptChars: number;
56
+ /** Which budget triggered the trim. */
57
+ scope: "per-result" | "per-turn";
58
+ };
44
59
  }
45
60
  interface ServerToolCall {
46
61
  type: "server_tool_call";
package/dist/index.d.ts CHANGED
@@ -41,6 +41,21 @@ interface ToolResult {
41
41
  toolCallId: string;
42
42
  content: ToolResultContent;
43
43
  isError?: boolean;
44
+ /**
45
+ * Set when the agent loop trimmed `content` to fit a per-result or per-turn
46
+ * budget. The provider (model input) and the persistent transcript both see
47
+ * the trimmed `content`, but the live `tool_call_end` event carried the FULL
48
+ * preview — so this marker makes that divergence explicit and reconcilable.
49
+ * Internal metadata only: it is never serialized onto the provider wire.
50
+ */
51
+ capped?: {
52
+ /** Length of the original, untrimmed string content. */
53
+ originalChars: number;
54
+ /** Length of the trimmed content actually sent to the model. */
55
+ keptChars: number;
56
+ /** Which budget triggered the trim. */
57
+ scope: "per-result" | "per-turn";
58
+ };
44
59
  }
45
60
  interface ServerToolCall {
46
61
  type: "server_tool_call";
package/dist/index.js CHANGED
@@ -264,6 +264,9 @@ function providerGuidance(provider, message, statusCode) {
264
264
  if (lower.includes("context_length_exceeded") || lower.includes("prompt is too long")) {
265
265
  return `Context window for this ${name} model is full. Compact the conversation to shrink history, or start a new session.`;
266
266
  }
267
+ if (lower.includes("many-image request") || lower.includes("image dimensions") && lower.includes("max allowed size")) {
268
+ return `An image in conversation history exceeds ${name}'s many-image limit. Restart EZ Coder so restored images are resized, then retry; if it persists, start a new session.`;
269
+ }
267
270
  if (statusCode === 413 || lower.includes("request_too_large") || lower.includes("request exceeds the maximum size")) {
268
271
  return `The request to ${name} is too large. Compact the conversation to shrink history, or start a new session.`;
269
272
  }
@@ -760,9 +763,14 @@ function toAnthropicMessages(messages, cacheControl) {
760
763
  continue;
761
764
  }
762
765
  if (msg.role === "user") {
766
+ if (typeof msg.content === "string") {
767
+ if (msg.content === "") continue;
768
+ } else if (!msg.content.some((p) => !(p.type === "text" && p.text === ""))) {
769
+ continue;
770
+ }
763
771
  out.push({
764
772
  role: "user",
765
- content: typeof msg.content === "string" ? msg.content : msg.content.map((part) => {
773
+ content: typeof msg.content === "string" ? msg.content : msg.content.filter((part) => !(part.type === "text" && part.text === "")).map((part) => {
766
774
  if (part.type === "text") return { type: "text", text: part.text };
767
775
  if (part.type === "video") {
768
776
  return {
@@ -787,6 +795,7 @@ function toAnthropicMessages(messages, cacheControl) {
787
795
  continue;
788
796
  }
789
797
  if (msg.role === "assistant") {
798
+ if (typeof msg.content === "string" && msg.content === "") continue;
790
799
  const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(msg.content, msgIdx > trajectoryStartIdx, idMap);
791
800
  if (Array.isArray(content) && content.length === 0) continue;
792
801
  out.push({ role: "assistant", content });
@@ -907,7 +916,7 @@ function remapToolCallId(id, idMap) {
907
916
  if (!id.startsWith("toolu_")) return id;
908
917
  const existing = idMap.get(id);
909
918
  if (existing) return existing;
910
- const mapped = `call_${id.slice(5)}`;
919
+ const mapped = `call_${id.slice(6)}`;
911
920
  idMap.set(id, mapped);
912
921
  return mapped;
913
922
  }
@@ -1518,6 +1527,12 @@ async function* runStream(options) {
1518
1527
  statusCode: 504
1519
1528
  });
1520
1529
  }
1530
+ if (stopReason === null) {
1531
+ throw new ProviderError("anthropic", "Stream ended before completion (no stop_reason).", {
1532
+ statusCode: 504,
1533
+ cause: { partialContent: contentParts, outputTokens }
1534
+ });
1535
+ }
1521
1536
  const normalizedStop = normalizeAnthropicStopReason(stopReason);
1522
1537
  const response = {
1523
1538
  message: {
@@ -1784,6 +1799,17 @@ function getEnvironment() {
1784
1799
  }
1785
1800
 
1786
1801
  // src/providers/openai.ts
1802
+ function toKimiK3Effort(level) {
1803
+ switch (level) {
1804
+ case "low":
1805
+ return "low";
1806
+ case "medium":
1807
+ case "high":
1808
+ return "high";
1809
+ default:
1810
+ return "max";
1811
+ }
1812
+ }
1787
1813
  function extractOpenAIUsage(usage) {
1788
1814
  let cacheRead = 0;
1789
1815
  let cacheWrite = 0;
@@ -1840,6 +1866,7 @@ async function* runStream2(options) {
1840
1866
  const client = createClient2(options);
1841
1867
  const isKimiK3 = options.provider === "moonshot" && options.model === "kimi-k3";
1842
1868
  const isManagedKimiK3 = isKimiK3 && options.baseUrl?.replace(/\/+$/, "").endsWith("/coding/v1") === true;
1869
+ const k3Effort = options.thinking ? toKimiK3Effort(options.thinking) : void 0;
1843
1870
  const isKimiK27 = options.provider === "moonshot" && options.model.startsWith("kimi-k2.7-code");
1844
1871
  const hasFixedKimiSampling = isKimiK3 || isKimiK27;
1845
1872
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" && !isKimiK3 && !isKimiK27 || options.provider === "xiaomi";
@@ -1854,9 +1881,11 @@ async function* runStream2(options) {
1854
1881
  }
1855
1882
  const messages = toOpenAIMessages(downgradedMessages, {
1856
1883
  provider: options.provider,
1857
- // K3 and K2.7 preserve reasoning even when the user hides thinking in the
1858
- // UI; keep assistant tool-call history wire-valid in that display mode.
1859
- thinking: isKimiK3 || isKimiK27 || !!options.thinking,
1884
+ // K2.7 preserves reasoning even when the user hides thinking in the UI;
1885
+ // keep assistant tool-call history wire-valid in that display mode. A
1886
+ // disabled K3 must NOT carry placeholder reasoning_content (mirrors the
1887
+ // official CLI: reasoning is preserved only while thinking is enabled).
1888
+ thinking: isKimiK27 || !!options.thinking,
1860
1889
  supportsImages: options.supportsImages
1861
1890
  });
1862
1891
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
@@ -1889,9 +1918,11 @@ async function* runStream2(options) {
1889
1918
  if (isKimiK3) {
1890
1919
  const paramsAny = params;
1891
1920
  if (isManagedKimiK3) {
1892
- paramsAny.thinking = { type: "enabled", effort: "max", keep: "all" };
1921
+ paramsAny.thinking = k3Effort ? { type: "enabled", effort: k3Effort, keep: "all" } : { type: "disabled" };
1922
+ } else if (k3Effort) {
1923
+ paramsAny.reasoning_effort = k3Effort;
1893
1924
  } else {
1894
- paramsAny.reasoning_effort = "max";
1925
+ paramsAny.thinking = { type: "disabled" };
1895
1926
  }
1896
1927
  }
1897
1928
  if (usesThinkingParam) {
@@ -1997,6 +2028,12 @@ async function* runStream2(options) {
1997
2028
  statusCode: 504
1998
2029
  });
1999
2030
  }
2031
+ if (finishReason === null) {
2032
+ throw new ProviderError(providerName, "Stream ended before completion (no finish_reason).", {
2033
+ statusCode: 504,
2034
+ cause: { partialText: textAccum, outputTokens }
2035
+ });
2036
+ }
2000
2037
  if (thinkingAccum) {
2001
2038
  contentParts.push({ type: "thinking", text: thinkingAccum });
2002
2039
  }