@cydm/pie 1.0.40 → 1.0.42

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.
@@ -3,7 +3,7 @@ import {
3
3
  EventStream,
4
4
  streamSimple,
5
5
  validateToolArguments
6
- } from "./chunk-HPTWCKJE.js";
6
+ } from "./chunk-NGQ3PUOC.js";
7
7
 
8
8
  // ../../packages/agent-core/src/agent-loop.ts
9
9
  function createLoopErrorMessage(error, config, signal) {
@@ -12,7 +12,11 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
12
12
  throw Error('Dynamic require of "' + x + '" is not supported');
13
13
  });
14
14
  var __commonJS = (cb, mod) => function __require2() {
15
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
+ try {
16
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
17
+ } catch (e) {
18
+ throw mod = 0, e;
19
+ }
16
20
  };
17
21
  var __export = (target, all) => {
18
22
  for (var name in all)
@@ -4,7 +4,7 @@ import {
4
4
  __export,
5
5
  __require,
6
6
  __toESM
7
- } from "./chunk-TG2EQLX2.js";
7
+ } from "./chunk-LZQXQOPJ.js";
8
8
 
9
9
  // ../../node_modules/partial-json/dist/options.js
10
10
  var require_options = __commonJS({
@@ -3338,9 +3338,9 @@ var NodeFileSystem = class {
3338
3338
  if (length === 0) return new Uint8Array();
3339
3339
  const handle = await getNodeFs().promises.open(path, "r");
3340
3340
  try {
3341
- const buffer = Buffer.allocUnsafe(length);
3342
- const { bytesRead } = await handle.read(buffer, 0, length, 0);
3343
- return new Uint8Array(buffer.subarray(0, bytesRead));
3341
+ const view = new Uint8Array(length);
3342
+ const { bytesRead } = await handle.read(view, 0, length, 0);
3343
+ return view.subarray(0, bytesRead);
3344
3344
  } finally {
3345
3345
  await handle.close();
3346
3346
  }
@@ -3805,7 +3805,10 @@ var PuerTSFileSystem = class {
3805
3805
  while (offset < length) {
3806
3806
  const requested = Math.min(length - offset, 65536);
3807
3807
  if (offset > 0 && typeof bridge.ReadRangeHex !== "function") {
3808
- throw new Error("The Unity file bridge cannot read prefixes beyond 64 KiB. Update PieFileBridge with ReadRangeHex.");
3808
+ console.warn(
3809
+ `[pie] readFilePrefix truncated at 64 KiB: the Unity file bridge is missing ReadRangeHex. Update the Pie package so PieFileBridge matches the runtime bundle. (requested ${length} bytes of '${path}')`
3810
+ );
3811
+ break;
3809
3812
  }
3810
3813
  const hex = String((offset === 0 ? bridge.ReadPrefixHex(normalizedPath, requested) : bridge.ReadRangeHex(normalizedPath, offset, requested)) ?? "");
3811
3814
  if (hex.length % 2 !== 0) throw new Error("The Unity file bridge returned an invalid hex prefix.");
@@ -4162,6 +4165,7 @@ function isAbortErrorMessage(message) {
4162
4165
  var nodeHttps = null;
4163
4166
  var nodeHttp = null;
4164
4167
  var nodeUrl = null;
4168
+ var nodeStringDecoder = null;
4165
4169
  function getNodeHttps() {
4166
4170
  if (!nodeHttps) {
4167
4171
  nodeHttps = require3("https");
@@ -4180,6 +4184,12 @@ function getNodeUrl() {
4180
4184
  }
4181
4185
  return nodeUrl;
4182
4186
  }
4187
+ function getNodeStringDecoder() {
4188
+ if (!nodeStringDecoder) {
4189
+ nodeStringDecoder = require3("string_decoder");
4190
+ }
4191
+ return nodeStringDecoder;
4192
+ }
4183
4193
  var globalHttpsAgent = null;
4184
4194
  var globalHttpAgent = null;
4185
4195
  var agentStats = {
@@ -4322,8 +4332,9 @@ var NodeHttpClient = class {
4322
4332
  const sseStartTime = Date.now();
4323
4333
  let totalBytes = 0;
4324
4334
  let lineCount = 0;
4325
- let bufferChunks = [];
4326
- let bufferLength = 0;
4335
+ const { StringDecoder } = getNodeStringDecoder();
4336
+ const decoder = new StringDecoder("utf8");
4337
+ let textBuffer = "";
4327
4338
  const lineQueue = [];
4328
4339
  let queueHead = 0;
4329
4340
  let done = false;
@@ -4343,23 +4354,21 @@ var NodeHttpClient = class {
4343
4354
  logHttp("ERROR", `Request [${requestId}] SSE stream error`, { error: e.message });
4344
4355
  };
4345
4356
  const flushBuffer = (final = false) => {
4346
- if (bufferLength === 0) return;
4347
- const buffer = Buffer.concat(bufferChunks, bufferLength);
4348
- const bufferStr = buffer.toString("utf-8");
4349
- const lines = bufferStr.split("\n");
4357
+ if (final) {
4358
+ textBuffer += decoder.end();
4359
+ }
4360
+ if (textBuffer.length === 0) return;
4361
+ const lines = textBuffer.split("\n");
4350
4362
  const lastLine = lines.pop();
4351
4363
  if (final) {
4352
4364
  if (lastLine !== void 0 && lastLine.length > 0) {
4353
4365
  lines.push(lastLine);
4354
4366
  }
4355
- bufferChunks = [];
4356
- bufferLength = 0;
4367
+ textBuffer = "";
4357
4368
  } else if (lastLine !== void 0) {
4358
- bufferChunks = [Buffer.from(lastLine)];
4359
- bufferLength = lastLine.length;
4369
+ textBuffer = lastLine;
4360
4370
  } else {
4361
- bufferChunks = [];
4362
- bufferLength = 0;
4371
+ textBuffer = "";
4363
4372
  }
4364
4373
  for (const line of lines) {
4365
4374
  lineQueue.push(line);
@@ -4379,17 +4388,15 @@ var NodeHttpClient = class {
4379
4388
  }
4380
4389
  res.on("data", (chunk) => {
4381
4390
  totalBytes += chunk.length;
4382
- bufferChunks.push(chunk);
4383
- bufferLength += chunk.length;
4384
- if (bufferLength > 16384 || chunk.includes(10)) {
4391
+ const decoded = decoder.write(chunk);
4392
+ textBuffer += decoded;
4393
+ if (textBuffer.length > 16384 || decoded.includes("\n")) {
4385
4394
  flushBuffer();
4386
4395
  }
4387
4396
  wake({ done: false, value: void 0 });
4388
4397
  });
4389
4398
  res.on("end", () => {
4390
- if (bufferLength > 0) {
4391
- flushBuffer(true);
4392
- }
4399
+ flushBuffer(true);
4393
4400
  if (!res.complete) {
4394
4401
  failStream(new Error("SSE stream ended before response completed"));
4395
4402
  return;
@@ -5212,6 +5219,8 @@ function detectCompat(_model) {
5212
5219
  requiresAssistantAfterToolResult: false,
5213
5220
  requiresThinkingAsText: true,
5214
5221
  supportsStrictMode: true,
5222
+ streamRequiresDone: true,
5223
+ streamRequiresFinishReason: true,
5215
5224
  cacheCapability: NO_CACHE_CAPABILITY
5216
5225
  };
5217
5226
  }
@@ -5229,6 +5238,8 @@ function getCompat(model) {
5229
5238
  requiresAssistantAfterToolResult: model.compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
5230
5239
  requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
5231
5240
  supportsStrictMode: model.compat.supportsStrictMode ?? detected.supportsStrictMode,
5241
+ streamRequiresDone: model.compat.streamRequiresDone ?? detected.streamRequiresDone,
5242
+ streamRequiresFinishReason: model.compat.streamRequiresFinishReason ?? detected.streamRequiresFinishReason,
5232
5243
  cacheCapability: model.compat.cacheCapability ?? detected.cacheCapability
5233
5244
  };
5234
5245
  }
@@ -5539,7 +5550,7 @@ function hasAssistantContent(message) {
5539
5550
  return block.text.trim().length > 0;
5540
5551
  }
5541
5552
  if (block.type === "thinking") {
5542
- return block.thinking.trim().length > 0;
5553
+ return false;
5543
5554
  }
5544
5555
  if (block.type === "toolCall") {
5545
5556
  return block.name.trim().length > 0;
@@ -5548,6 +5559,9 @@ function hasAssistantContent(message) {
5548
5559
  });
5549
5560
  }
5550
5561
  function assertAssistantHasContent(message) {
5562
+ if (message.stopReason === "length") {
5563
+ return;
5564
+ }
5551
5565
  if (!hasAssistantContent(message)) {
5552
5566
  throw new EmptyAssistantContentError();
5553
5567
  }
@@ -5571,6 +5585,14 @@ function logEmptyAssistantContentError(error, model, options) {
5571
5585
 
5572
5586
  // ../../packages/ai/src/providers/openai-compat.ts
5573
5587
  var DEFAULT_OPENAI_COMPAT_STREAM_TIMEOUT_MS = 10 * 60 * 1e3;
5588
+ var OPENAI_COMPAT_PROTOCOL_ERROR_CODE = "provider_response_protocol_error";
5589
+ var OpenAICompatProtocolError = class extends Error {
5590
+ code = OPENAI_COMPAT_PROTOCOL_ERROR_CODE;
5591
+ constructor(message) {
5592
+ super(`${OPENAI_COMPAT_PROTOCOL_ERROR_CODE}: ${message}`);
5593
+ this.name = "OpenAICompatProtocolError";
5594
+ }
5595
+ };
5574
5596
  function getOpenAICompatStreamTimeoutMs() {
5575
5597
  const value = Number(process.env.PIE_OPENAI_COMPAT_STREAM_TIMEOUT_MS || "");
5576
5598
  return Number.isFinite(value) && value > 0 ? value : DEFAULT_OPENAI_COMPAT_STREAM_TIMEOUT_MS;
@@ -5623,6 +5645,44 @@ function formatOpenAIStreamError(error) {
5623
5645
  }
5624
5646
  return metadata.length > 0 ? `${message} (${metadata.join(", ")})` : message;
5625
5647
  }
5648
+ function isRecord(value) {
5649
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5650
+ }
5651
+ function createProtocolError(message) {
5652
+ return new OpenAICompatProtocolError(message);
5653
+ }
5654
+ function parseSSEDataLine(line) {
5655
+ const trimmed = line.trim();
5656
+ if (!trimmed || trimmed.startsWith(":")) return null;
5657
+ if (!trimmed.startsWith("data:")) return null;
5658
+ const data = trimmed.slice(5);
5659
+ return data.startsWith(" ") ? data.slice(1) : data;
5660
+ }
5661
+ function parseFinalToolArguments(partialArgs, toolName) {
5662
+ const text = partialArgs.trim();
5663
+ if (!text) return {};
5664
+ let parsed;
5665
+ try {
5666
+ parsed = JSON.parse(text);
5667
+ } catch (error) {
5668
+ const message = error instanceof Error ? error.message : String(error);
5669
+ throw createProtocolError(`OpenAI-compatible stream returned incomplete or invalid JSON arguments for tool "${toolName || "unknown"}": ${message}`);
5670
+ }
5671
+ if (!isRecord(parsed)) {
5672
+ throw createProtocolError(`OpenAI-compatible stream returned non-object JSON arguments for tool "${toolName || "unknown"}".`);
5673
+ }
5674
+ return parsed;
5675
+ }
5676
+ function tryParseFinalToolArguments(partialArgs) {
5677
+ const text = partialArgs.trim();
5678
+ if (!text) return null;
5679
+ try {
5680
+ const parsed = JSON.parse(text);
5681
+ return isRecord(parsed) ? parsed : null;
5682
+ } catch {
5683
+ return null;
5684
+ }
5685
+ }
5626
5686
  var streamOpenAICompletions = (model, context, options) => {
5627
5687
  const stream = new AssistantMessageEventStream();
5628
5688
  (async () => {
@@ -5673,8 +5733,94 @@ var streamOpenAICompletions = (model, context, options) => {
5673
5733
  let currentBlock = null;
5674
5734
  const blocks = output.content;
5675
5735
  const blockIndex = () => blocks.length - 1;
5676
- const toolCalls = /* @__PURE__ */ new Map();
5736
+ const toolCallEntries = [];
5737
+ const toolCallsByIndex = /* @__PURE__ */ new Map();
5738
+ const toolCallsById = /* @__PURE__ */ new Map();
5677
5739
  let usageSnapshot = {};
5740
+ let sawDone = false;
5741
+ let sawFinishReason = false;
5742
+ let rawFinishReason = null;
5743
+ const createToolCallEntry = (toolCall, index) => {
5744
+ const block = {
5745
+ type: "toolCall",
5746
+ id: toolCall.id || "",
5747
+ name: toolCall.function?.name || "",
5748
+ arguments: {}
5749
+ };
5750
+ output.content.push(block);
5751
+ const entry = { block, partialArgs: "", contentIndex: blockIndex(), index };
5752
+ toolCallEntries.push(entry);
5753
+ stream.push({
5754
+ type: "toolcall_start",
5755
+ contentIndex: entry.contentIndex,
5756
+ partial: output
5757
+ });
5758
+ return entry;
5759
+ };
5760
+ const bindToolCallIndex = (index, entry) => {
5761
+ const existing = toolCallsByIndex.get(index);
5762
+ if (existing && existing !== entry) {
5763
+ throw createProtocolError(`OpenAI-compatible stream reused tool call index ${index} for multiple calls.`);
5764
+ }
5765
+ if (entry.index !== void 0 && entry.index !== index) {
5766
+ throw createProtocolError(`OpenAI-compatible stream moved tool call "${entry.block.id || entry.block.name || "unknown"}" from index ${entry.index} to ${index}.`);
5767
+ }
5768
+ entry.index = index;
5769
+ toolCallsByIndex.set(index, entry);
5770
+ };
5771
+ const rememberToolCallId = (id, entry) => {
5772
+ const existing = toolCallsById.get(id);
5773
+ if (existing && existing !== entry) {
5774
+ throw createProtocolError(`OpenAI-compatible stream reused tool call id "${id}" for multiple calls.`);
5775
+ }
5776
+ if (entry.block.id && entry.block.id !== id) {
5777
+ throw createProtocolError(`OpenAI-compatible stream changed tool call id from "${entry.block.id}" to "${id}".`);
5778
+ }
5779
+ entry.block.id = id;
5780
+ toolCallsById.set(id, entry);
5781
+ };
5782
+ const resolveToolCallEntry = (toolCall, toolCallCount) => {
5783
+ const id = typeof toolCall.id === "string" && toolCall.id.length > 0 ? toolCall.id : "";
5784
+ if (Number.isInteger(toolCall.index) && toolCall.index >= 0) {
5785
+ let entry = toolCallsByIndex.get(toolCall.index);
5786
+ if (entry && id && entry.block.id && entry.block.id !== id) {
5787
+ throw createProtocolError(`OpenAI-compatible stream changed tool call id at index ${toolCall.index} from "${entry.block.id}" to "${id}".`);
5788
+ }
5789
+ if (!entry && id) {
5790
+ entry = toolCallsById.get(id);
5791
+ if (entry) bindToolCallIndex(toolCall.index, entry);
5792
+ }
5793
+ if (!entry) {
5794
+ entry = createToolCallEntry(toolCall, toolCall.index);
5795
+ bindToolCallIndex(toolCall.index, entry);
5796
+ }
5797
+ if (id) rememberToolCallId(id, entry);
5798
+ return entry;
5799
+ }
5800
+ if (id) {
5801
+ let entry = toolCallsById.get(id);
5802
+ if (!entry) {
5803
+ const unpaired = toolCallEntries.filter((candidate) => !candidate.block.id);
5804
+ if (unpaired.length === 1) {
5805
+ rememberToolCallId(id, unpaired[0]);
5806
+ return unpaired[0];
5807
+ }
5808
+ if (unpaired.length > 1) {
5809
+ throw createProtocolError(`OpenAI-compatible stream returned tool call id "${id}" without an index while multiple unnamed calls are in flight; the call cannot be disambiguated.`);
5810
+ }
5811
+ entry = createToolCallEntry(toolCall);
5812
+ rememberToolCallId(id, entry);
5813
+ }
5814
+ return entry;
5815
+ }
5816
+ if (toolCallEntries.length === 1) {
5817
+ return toolCallEntries[0];
5818
+ }
5819
+ if (toolCallEntries.length === 0 && toolCallCount === 1) {
5820
+ return createToolCallEntry(toolCall);
5821
+ }
5822
+ throw createProtocolError("OpenAI-compatible stream returned tool call deltas without index or id and the call could not be disambiguated.");
5823
+ };
5678
5824
  const finishCurrentBlock = (block) => {
5679
5825
  if (block) {
5680
5826
  if (block.type === "text") {
@@ -5699,155 +5845,166 @@ var streamOpenAICompletions = (model, context, options) => {
5699
5845
  }
5700
5846
  for await (const line of response.readSSELines(options?.signal)) {
5701
5847
  {
5702
- const trimmed = line.trim();
5703
- if (!trimmed || trimmed.startsWith(":")) continue;
5704
- if (trimmed.startsWith("data: ")) {
5705
- const data = trimmed.slice(6);
5706
- if (data === "[DONE]") break;
5707
- let chunk;
5708
- try {
5709
- chunk = JSON.parse(data);
5710
- } catch {
5711
- continue;
5712
- }
5713
- if (chunk?.error) {
5714
- throw new Error(formatOpenAIStreamError(chunk.error));
5848
+ const data = parseSSEDataLine(line);
5849
+ if (data === null) continue;
5850
+ if (data === "[DONE]") {
5851
+ sawDone = true;
5852
+ break;
5853
+ }
5854
+ let chunk;
5855
+ try {
5856
+ chunk = JSON.parse(data);
5857
+ } catch (error) {
5858
+ const message = error instanceof Error ? error.message : String(error);
5859
+ throw createProtocolError(`OpenAI-compatible stream returned malformed SSE JSON: ${message}`);
5860
+ }
5861
+ if (chunk?.error) {
5862
+ throw new Error(formatOpenAIStreamError(chunk.error));
5863
+ }
5864
+ if (chunk.usage) {
5865
+ usageSnapshot = mergeOpenAIUsage(usageSnapshot, chunk.usage);
5866
+ const normalizedUsage = normalizeOpenAIUsage(usageSnapshot);
5867
+ output.usage = {
5868
+ input: normalizedUsage.input,
5869
+ output: normalizedUsage.output,
5870
+ cacheRead: normalizedUsage.cacheRead,
5871
+ cacheWrite: 0,
5872
+ totalTokens: normalizedUsage.totalTokens,
5873
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
5874
+ };
5875
+ output.usage.cost = calculateCost(model, output.usage);
5876
+ if (process.env.PIE_DEBUG_CACHE) {
5877
+ const normalizedPromptTokens = normalizedUsage.input + normalizedUsage.cacheRead;
5878
+ console.error("[CACHE DEBUG] Response usage:", JSON.stringify({
5879
+ input_tokens: normalizedUsage.input,
5880
+ output_tokens: normalizedUsage.output,
5881
+ cached_tokens: normalizedUsage.cacheRead,
5882
+ cache_hit_rate: normalizedPromptTokens > 0 ? `${(normalizedUsage.cacheRead / normalizedPromptTokens * 100).toFixed(1)}%` : "N/A"
5883
+ }, null, 2));
5715
5884
  }
5716
- if (chunk.usage) {
5717
- usageSnapshot = mergeOpenAIUsage(usageSnapshot, chunk.usage);
5718
- const normalizedUsage = normalizeOpenAIUsage(usageSnapshot);
5719
- output.usage = {
5720
- input: normalizedUsage.input,
5721
- output: normalizedUsage.output,
5722
- cacheRead: normalizedUsage.cacheRead,
5723
- cacheWrite: 0,
5724
- totalTokens: normalizedUsage.totalTokens,
5725
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
5726
- };
5727
- output.usage.cost = calculateCost(model, output.usage);
5728
- if (process.env.PIE_DEBUG_CACHE) {
5729
- const normalizedPromptTokens = normalizedUsage.input + normalizedUsage.cacheRead;
5730
- console.error("[CACHE DEBUG] Response usage:", JSON.stringify({
5731
- input_tokens: normalizedUsage.input,
5732
- output_tokens: normalizedUsage.output,
5733
- cached_tokens: normalizedUsage.cacheRead,
5734
- cache_hit_rate: normalizedPromptTokens > 0 ? `${(normalizedUsage.cacheRead / normalizedPromptTokens * 100).toFixed(1)}%` : "N/A"
5735
- }, null, 2));
5885
+ }
5886
+ const choice = chunk.choices?.[0];
5887
+ if (!choice) continue;
5888
+ if (choice.finish_reason) {
5889
+ sawFinishReason = true;
5890
+ rawFinishReason = String(choice.finish_reason);
5891
+ output.stopReason = mapOpenAIStopReason(choice.finish_reason);
5892
+ }
5893
+ if (choice.delta) {
5894
+ if (choice.delta.content !== null && choice.delta.content !== void 0 && choice.delta.content.length > 0) {
5895
+ if (!currentBlock || currentBlock.type !== "text") {
5896
+ finishCurrentBlock(currentBlock);
5897
+ currentBlock = { type: "text", text: "" };
5898
+ output.content.push(currentBlock);
5899
+ stream.push({
5900
+ type: "text_start",
5901
+ contentIndex: blockIndex(),
5902
+ partial: output
5903
+ });
5904
+ }
5905
+ if (currentBlock.type === "text") {
5906
+ currentBlock.text += choice.delta.content;
5907
+ stream.push({
5908
+ type: "text_delta",
5909
+ contentIndex: blockIndex(),
5910
+ delta: choice.delta.content,
5911
+ partial: output
5912
+ });
5736
5913
  }
5737
5914
  }
5738
- const choice = chunk.choices?.[0];
5739
- if (!choice) continue;
5740
- if (choice.finish_reason) {
5741
- output.stopReason = mapOpenAIStopReason(choice.finish_reason);
5915
+ const reasoningFields = [
5916
+ "reasoning_content",
5917
+ "reasoning",
5918
+ "reasoning_text"
5919
+ ];
5920
+ let foundReasoningField = null;
5921
+ for (const field of reasoningFields) {
5922
+ if (choice.delta[field] !== null && choice.delta[field] !== void 0 && choice.delta[field].length > 0) {
5923
+ foundReasoningField = field;
5924
+ break;
5925
+ }
5742
5926
  }
5743
- if (choice.delta) {
5744
- if (choice.delta.content !== null && choice.delta.content !== void 0 && choice.delta.content.length > 0) {
5745
- if (!currentBlock || currentBlock.type !== "text") {
5746
- finishCurrentBlock(currentBlock);
5747
- currentBlock = { type: "text", text: "" };
5748
- output.content.push(currentBlock);
5749
- stream.push({
5750
- type: "text_start",
5751
- contentIndex: blockIndex(),
5752
- partial: output
5753
- });
5754
- }
5755
- if (currentBlock.type === "text") {
5756
- currentBlock.text += choice.delta.content;
5757
- stream.push({
5758
- type: "text_delta",
5759
- contentIndex: blockIndex(),
5760
- delta: choice.delta.content,
5761
- partial: output
5762
- });
5763
- }
5927
+ if (foundReasoningField) {
5928
+ if (!currentBlock || currentBlock.type !== "thinking") {
5929
+ finishCurrentBlock(currentBlock);
5930
+ currentBlock = {
5931
+ type: "thinking",
5932
+ thinking: "",
5933
+ thinkingSignature: foundReasoningField
5934
+ };
5935
+ output.content.push(currentBlock);
5936
+ stream.push({
5937
+ type: "thinking_start",
5938
+ contentIndex: blockIndex(),
5939
+ partial: output
5940
+ });
5764
5941
  }
5765
- const reasoningFields = [
5766
- "reasoning_content",
5767
- "reasoning",
5768
- "reasoning_text"
5769
- ];
5770
- let foundReasoningField = null;
5771
- for (const field of reasoningFields) {
5772
- if (choice.delta[field] !== null && choice.delta[field] !== void 0 && choice.delta[field].length > 0) {
5773
- foundReasoningField = field;
5774
- break;
5775
- }
5942
+ if (currentBlock.type === "thinking") {
5943
+ const delta = choice.delta[foundReasoningField];
5944
+ currentBlock.thinking += delta;
5945
+ stream.push({
5946
+ type: "thinking_delta",
5947
+ contentIndex: blockIndex(),
5948
+ delta,
5949
+ partial: output
5950
+ });
5776
5951
  }
5777
- if (foundReasoningField) {
5778
- if (!currentBlock || currentBlock.type !== "thinking") {
5779
- finishCurrentBlock(currentBlock);
5780
- currentBlock = {
5781
- type: "thinking",
5782
- thinking: "",
5783
- thinkingSignature: foundReasoningField
5784
- };
5785
- output.content.push(currentBlock);
5786
- stream.push({
5787
- type: "thinking_start",
5788
- contentIndex: blockIndex(),
5789
- partial: output
5790
- });
5791
- }
5792
- if (currentBlock.type === "thinking") {
5793
- const delta = choice.delta[foundReasoningField];
5794
- currentBlock.thinking += delta;
5952
+ }
5953
+ if (choice.delta.tool_calls) {
5954
+ const deltaToolCalls = choice.delta.tool_calls;
5955
+ for (const toolCall of deltaToolCalls) {
5956
+ finishCurrentBlock(currentBlock);
5957
+ currentBlock = null;
5958
+ const entry = resolveToolCallEntry(toolCall, deltaToolCalls.length);
5959
+ if (entry) {
5960
+ if (toolCall.id) rememberToolCallId(toolCall.id, entry);
5961
+ if (toolCall.function?.name) {
5962
+ if (entry.block.name && entry.block.name !== toolCall.function.name) {
5963
+ throw createProtocolError(`OpenAI-compatible stream changed tool call name from "${entry.block.name}" to "${toolCall.function.name}".`);
5964
+ }
5965
+ entry.block.name = toolCall.function.name;
5966
+ }
5967
+ let delta = "";
5968
+ if (toolCall.function?.arguments) {
5969
+ delta = toolCall.function.arguments;
5970
+ entry.partialArgs += delta;
5971
+ entry.block.arguments = parseStreamingJson(entry.partialArgs);
5972
+ }
5795
5973
  stream.push({
5796
- type: "thinking_delta",
5797
- contentIndex: blockIndex(),
5974
+ type: "toolcall_delta",
5975
+ contentIndex: entry.contentIndex,
5798
5976
  delta,
5799
5977
  partial: output
5800
5978
  });
5801
5979
  }
5802
5980
  }
5803
- if (choice.delta.tool_calls) {
5804
- for (const [toolCallPosition, toolCall] of choice.delta.tool_calls.entries()) {
5805
- const toolCallIndex = Number.isInteger(toolCall.index) && toolCall.index >= 0 ? toolCall.index : toolCallPosition;
5806
- finishCurrentBlock(currentBlock);
5807
- currentBlock = null;
5808
- let entry = toolCalls.get(toolCallIndex);
5809
- if (!entry) {
5810
- const block = {
5811
- type: "toolCall",
5812
- id: toolCall.id || "",
5813
- name: toolCall.function?.name || "",
5814
- arguments: {}
5815
- };
5816
- output.content.push(block);
5817
- entry = { block, partialArgs: "", contentIndex: blockIndex() };
5818
- toolCalls.set(toolCallIndex, entry);
5819
- stream.push({
5820
- type: "toolcall_start",
5821
- contentIndex: entry.contentIndex,
5822
- partial: output
5823
- });
5824
- }
5825
- if (entry) {
5826
- if (toolCall.id) entry.block.id = toolCall.id;
5827
- if (toolCall.function?.name)
5828
- entry.block.name = toolCall.function.name;
5829
- let delta = "";
5830
- if (toolCall.function?.arguments) {
5831
- delta = toolCall.function.arguments;
5832
- entry.partialArgs += delta;
5833
- entry.block.arguments = parseStreamingJson(entry.partialArgs);
5834
- }
5835
- stream.push({
5836
- type: "toolcall_delta",
5837
- contentIndex: entry.contentIndex,
5838
- delta,
5839
- partial: output
5840
- });
5841
- }
5842
- }
5843
- }
5844
5981
  }
5845
5982
  }
5846
5983
  }
5847
5984
  }
5848
5985
  finishCurrentBlock(currentBlock);
5849
- for (const { block, partialArgs, contentIndex } of toolCalls.values()) {
5850
- block.arguments = parseStreamingJson(partialArgs);
5986
+ if (compat.streamRequiresDone && !sawDone) {
5987
+ throw createProtocolError(`OpenAI-compatible stream ended without data: [DONE]${rawFinishReason ? ` (last finish_reason=${rawFinishReason})` : ""}. Set compat.streamRequiresDone=false for gateways that legitimately omit the terminator.`);
5988
+ }
5989
+ if (compat.streamRequiresFinishReason && !sawFinishReason) {
5990
+ throw createProtocolError("OpenAI-compatible stream ended without finish_reason. Set compat.streamRequiresFinishReason=false for endpoints that omit it.");
5991
+ }
5992
+ if (output.stopReason === "toolUse" && toolCallEntries.length === 0) {
5993
+ throw createProtocolError(`OpenAI-compatible stream ended with finish_reason=${rawFinishReason} but did not include any tool calls.`);
5994
+ }
5995
+ if (output.stopReason === "length" && toolCallEntries.length > 0) {
5996
+ const hasTruncatedArgs = toolCallEntries.some(
5997
+ ({ partialArgs }) => tryParseFinalToolArguments(partialArgs) === null
5998
+ );
5999
+ if (hasTruncatedArgs) {
6000
+ throw new Error("OpenAI-compatible stream ended with finish_reason=length before tool call arguments were complete. Increase maxTokens or reduce prompt size.");
6001
+ }
6002
+ }
6003
+ for (const { block, partialArgs, contentIndex } of toolCallEntries) {
6004
+ if (!block.name.trim()) {
6005
+ throw createProtocolError("OpenAI-compatible stream returned a tool call without a function name.");
6006
+ }
6007
+ block.arguments = parseFinalToolArguments(partialArgs, block.name);
5851
6008
  stream.push({ type: "toolcall_end", contentIndex, toolCall: block, partial: output });
5852
6009
  }
5853
6010
  if (output.stopReason !== "error" && output.stopReason !== "aborted" && output.content.some((block) => block.type === "toolCall")) {
@@ -9772,6 +9929,7 @@ export {
9772
9929
  EventStream,
9773
9930
  getModelCacheCapability,
9774
9931
  EMPTY_ASSISTANT_CONTENT_ERROR,
9932
+ OPENAI_COMPAT_PROTOCOL_ERROR_CODE,
9775
9933
  streamSimple,
9776
9934
  completeSimple,
9777
9935
  MODEL_CLASS_FALLBACK_ORDER,
@@ -8,10 +8,10 @@ import {
8
8
  createSharedWebSearchTool,
9
9
  interpretShellExit,
10
10
  requestInteraction
11
- } from "./chunk-SNSSIR3E.js";
11
+ } from "./chunk-2GVFMYIV.js";
12
12
  import {
13
13
  Type
14
- } from "./chunk-HPTWCKJE.js";
14
+ } from "./chunk-NGQ3PUOC.js";
15
15
 
16
16
  // src/config.ts
17
17
  import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";