acp-kernel 0.0.69 → 0.0.70-pr.293.160

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.
@@ -25,8 +25,9 @@ export interface BiliMessage extends CoreMessage {
25
25
  /** Anthropic: the original content block for an image or a structured
26
26
  * tool_result. Restored verbatim by coreToAnthropic. */
27
27
  rawAnthropicBlock?: unknown;
28
- /** OpenAI chat: the original content part for an image_url, or the
29
- * original message object for a developer-role message. */
28
+ /** OpenAI chat: the original content part for an image_url (data: or
29
+ * remote URL) or the original message object for a developer-role
30
+ * message. */
30
31
  rawOpenaiContent?: unknown;
31
32
  /** Responses API: the original input item (for input_image, or a raw
32
33
  * function_call / function_call_output we pass through). */
@@ -40,6 +41,13 @@ export interface BiliMessage extends CoreMessage {
40
41
  * back on subsequent requests or the API returns HTTP 400; stored so
41
42
  * coreToOpenai can reattach it. */
42
43
  reasoningContent?: string;
44
+ /** OpenAI: the host's wire carried `reasoning_content` with a BLANK value.
45
+ * Strict-echo thinking models (DeepSeek et al.) reject a replayed
46
+ * assistant turn whose reasoning_content is MISSING but accept a blank
47
+ * one, so "blank" and "absent" are different requests. openaiToCore sets
48
+ * this when it found no reasoning text to store; coreToOpenai re-emits
49
+ * the key (blank) instead of dropping it. */
50
+ reasoningPresent?: boolean;
43
51
  /** Anthropic tool_result.is_error. Marks the tool result as an error so
44
52
  * the model knows the tool failed (not just returned an error string). */
45
53
  toolIsError?: boolean;
@@ -53,8 +61,8 @@ export interface BiliMessage extends CoreMessage {
53
61
  /** The base64 data of an image (without the data: prefix). Lets the
54
62
  * reverse conversion rebuild the full image payload. */
55
63
  imageBase64?: string;
56
- /** OpenAI chat: ALL original image_url content parts (each a data: URL
57
- * part) for a user message carrying more than one image, in wire order.
64
+ /** OpenAI chat: ALL original image_url content parts (data: or remote
65
+ * URL) for a user message carrying more than one image, in wire order.
58
66
  * coreToOpenai re-emits these verbatim (after the text part). The
59
67
  * singular `rawOpenaiContent` still covers the single-image case and
60
68
  * legacy persisted state. Typed as unknown[] (not OpenAIContentPart) to
@@ -1 +1 @@
1
- {"version":3,"file":"bili-message.d.ts","sourceRoot":"","sources":["../../src/wire/bili-message.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;2DAE2D;AAC3D,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC5C;6DACyD;IACzD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;gEAC4D;IAC5D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;iEAC6D;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;6DAEyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;wCAGoC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;+EAC2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;2EACuE;IACvE,YAAY,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACtC;;+CAE2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;6DACyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;yDAKqD;IACrD,qBAAqB,CAAC,EAAE,OAAO,EAAE,CAAC;CACrC;AAED;+EAC+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEjE;AAED;;8DAE8D;AAC9D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3F"}
1
+ {"version":3,"file":"bili-message.d.ts","sourceRoot":"","sources":["../../src/wire/bili-message.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;2DAE2D;AAC3D,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC5C;6DACyD;IACzD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;mBAEe;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;iEAC6D;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;6DAEyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;wCAGoC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;kDAK8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;+EAC2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;2EACuE;IACvE,YAAY,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACtC;;+CAE2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;6DACyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;yDAKqD;IACrD,qBAAqB,CAAC,EAAE,OAAO,EAAE,CAAC;CACrC;AAED;+EAC+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEjE;AAED;;8DAE8D;AAC9D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3F"}
@@ -258,12 +258,19 @@ function openaiToCore(body) {
258
258
  role: "user",
259
259
  contentType: "text",
260
260
  text,
261
- ...imgs.length === 1 && firstParsed ? { rawOpenaiContent: imgs[0], imageMediaType: firstParsed.mediaType, imageBase64: firstParsed.base64 } : imgs.length > 1 ? { rawOpenaiContentParts: imgs } : {}
261
+ ...imgs.length === 1 ? {
262
+ rawOpenaiContent: imgs[0],
263
+ // data: URLs only — remote URLs ride rawOpenaiContent
264
+ // verbatim (#187/#291)
265
+ ...firstParsed ? { imageMediaType: firstParsed.mediaType, imageBase64: firstParsed.base64 } : {}
266
+ } : imgs.length > 1 ? { rawOpenaiContentParts: imgs } : {}
262
267
  });
263
268
  break;
264
269
  }
265
270
  case "assistant": {
266
- const fieldReasoning = typeof m.reasoning_content === "string" ? m.reasoning_content : "";
271
+ const rawReasoning = m.reasoning_content;
272
+ const fieldPresent = typeof rawReasoning === "string";
273
+ const fieldReasoning = fieldPresent ? rawReasoning : "";
267
274
  let reasoning = fieldReasoning;
268
275
  let text = stringContent(m.content);
269
276
  if (!reasoning) {
@@ -273,6 +280,7 @@ function openaiToCore(body) {
273
280
  text = split.text;
274
281
  }
275
282
  }
283
+ const blankEcho = fieldPresent && reasoning.length === 0;
276
284
  if (reasoning) {
277
285
  const base = deriveMessageId("assistant", "reasoning", reasoning);
278
286
  msgs.push({
@@ -285,7 +293,7 @@ function openaiToCore(body) {
285
293
  }
286
294
  if (text) {
287
295
  const base = deriveMessageId("assistant", "text", text);
288
- msgs.push({ id: clusters.next(base), role: "assistant", contentType: "text", text });
296
+ msgs.push({ id: clusters.next(base), role: "assistant", contentType: "text", text, ...blankEcho ? { reasoningPresent: true } : {} });
289
297
  }
290
298
  if (Array.isArray(m.tool_calls)) {
291
299
  for (const tc of m.tool_calls) {
@@ -299,7 +307,8 @@ function openaiToCore(body) {
299
307
  contentType: "tool-call",
300
308
  toolName: tc.function.name,
301
309
  toolCallId: tc.id,
302
- text: tc.function.arguments ?? ""
310
+ text: tc.function.arguments ?? "",
311
+ ...blankEcho ? { reasoningPresent: true } : {}
303
312
  });
304
313
  toolNames.set(tc.id, tc.function.name);
305
314
  }
@@ -331,15 +340,16 @@ function coreToOpenai(messages) {
331
340
  const flush = () => {
332
341
  if (!pending) return;
333
342
  const reasoning = pending.reasoning !== null && pending.reasoning.length > 0 ? pending.reasoning : void 0;
343
+ const reasoningField = reasoning !== void 0 ? { reasoning_content: reasoning } : pending.reasoningPresent ? { reasoning_content: "" } : {};
334
344
  if (pending.toolCalls.length > 0) {
335
345
  out.push({
336
346
  role: "assistant",
337
347
  content: pending.text ?? null,
338
348
  tool_calls: pending.toolCalls,
339
- ...reasoning ? { reasoning_content: reasoning } : {}
349
+ ...reasoningField
340
350
  });
341
351
  } else if (pending.text !== null) {
342
- out.push({ role: "assistant", content: pending.text, ...reasoning ? { reasoning_content: reasoning } : {} });
352
+ out.push({ role: "assistant", content: pending.text, ...reasoningField });
343
353
  } else if (reasoning) {
344
354
  out.push({ role: "assistant", content: null, reasoning_content: reasoning });
345
355
  }
@@ -347,7 +357,8 @@ function coreToOpenai(messages) {
347
357
  };
348
358
  for (const m of messages) {
349
359
  if (m.role === "assistant") {
350
- if (!pending) pending = { text: null, toolCalls: [], reasoning: null };
360
+ if (!pending) pending = { text: null, toolCalls: [], reasoning: null, reasoningPresent: false };
361
+ if (m.reasoningPresent) pending.reasoningPresent = true;
351
362
  if (m.contentType === "reasoning") {
352
363
  pending.reasoning = (pending.reasoning ?? "") + (m.reasoningContent ?? m.text ?? "");
353
364
  } else if (m.contentType === "text") {
@@ -423,7 +434,7 @@ function allImageParts(content) {
423
434
  if (!("type" in p) || p.type !== "image_url" || !("image_url" in p)) continue;
424
435
  const imagePart = p;
425
436
  const url = imagePart.image_url?.url;
426
- if (typeof url === "string" && parseDataUrl(url)) out.push(p);
437
+ if (typeof url === "string") out.push(p);
427
438
  }
428
439
  return out;
429
440
  }
@@ -456,6 +467,12 @@ function partText(part) {
456
467
  function messageContent(content) {
457
468
  return typeof content === "string" ? content : content.map(partText).join("\n");
458
469
  }
470
+ function userDisplayText(content, text) {
471
+ if (typeof content === "string") return text;
472
+ const hasImage = content.some((part) => part.type === "input_image");
473
+ const hasTextPart = content.some((part) => part.type === "input_text" || part.type === "output_text");
474
+ return !text || hasImage && !hasTextPart ? "[image]" : text;
475
+ }
459
476
  function reasoningText(item) {
460
477
  const fromParts = (parts, type) => {
461
478
  if (!Array.isArray(parts)) return "";
@@ -533,15 +550,17 @@ function responsesToCore(body) {
533
550
  effText = split.text;
534
551
  }
535
552
  }
536
- if (effText) {
537
- coreId = clusters.next(deriveMessageId(role, "text", effText));
553
+ const hasImage = Array.isArray(message.content) && message.content.some((part) => part.type === "input_image");
554
+ if (effText || role === "user" && hasImage) {
555
+ const displayText = role === "user" ? userDisplayText(message.content, effText) : effText;
556
+ coreId = clusters.next(deriveMessageId(role, "text", displayText));
538
557
  const imageUrl = Array.isArray(message.content) ? message.content.find((part) => part.type === "input_image" && typeof part.image_url === "string")?.image_url : void 0;
539
558
  const image = typeof imageUrl === "string" ? parseDataUrl(imageUrl) : void 0;
540
559
  msgs.push({
541
560
  id: coreId,
542
561
  role,
543
562
  contentType: "text",
544
- text: effText,
563
+ text: displayText,
545
564
  rawResponsesItem: item,
546
565
  ...image ? { imageMediaType: image.mediaType, imageBase64: image.base64 } : {}
547
566
  });
@@ -701,7 +720,8 @@ function coreToResponses(messages, customToolCallIds = /* @__PURE__ */ new Set()
701
720
  if (message.role === "system") {
702
721
  out.push({ type: "message", role: "developer", content: message.text ?? "" });
703
722
  } else if (message.role === "user") {
704
- if (raw?.type === "message" && messageContent(raw.content) === (message.text ?? "")) out.push(raw);
723
+ const rawMessage = raw?.type === "message" ? raw : void 0;
724
+ if (rawMessage && userDisplayText(rawMessage.content, messageContent(rawMessage.content)) === (message.text ?? "")) out.push(rawMessage);
705
725
  else out.push({ type: "message", role: "user", content: message.text ?? "" });
706
726
  } else if (message.role === "assistant") {
707
727
  if (message.contentType === "text") {