@x-otto/plugin-otto-wire-protocols 0.1.0-alpha.15 → 0.1.0-alpha.18

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/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@x-otto/plugin-otto-wire-protocols",
3
- "version": "0.1.0-alpha.15",
3
+ "version": "0.1.0-alpha.18",
4
4
  "private": false,
5
5
  "description": "Three standard wire protocol implementations (anthropic-messages/openai-responses/openai-completions) — runtime support for provider plugins. Publishable standalone so non-CLI hosts (e.g. desktop apps depending only on @x-otto/coding) can install it without the full @x-otto/cli dependency tree.",
6
6
  "dependencies": {
7
7
  "@anthropic-ai/sdk": "0.88.0",
8
- "@x-otto/provider": "0.1.0-alpha.15",
9
- "@x-otto/interchange": "0.1.0-alpha.15",
10
- "@x-otto/plugin": "0.1.0-alpha.15",
11
- "@x-otto/shared": "0.1.0-alpha.15"
8
+ "@x-otto/provider": "0.1.0-alpha.18",
9
+ "@x-otto/shared": "0.1.0-alpha.18",
10
+ "@x-otto/plugin": "0.1.0-alpha.18",
11
+ "@x-otto/interchange": "0.1.0-alpha.18"
12
12
  },
13
13
  "type": "module",
14
14
  "files": [
@@ -1 +1 @@
1
- {"apiVersion":1,"compilerVersion":"0.1.0-alpha.15","sourceHash":"a5bc1aa9b8c0b8ff5005874fe95d2b28cbda16315fda7cee115c6f82fa980563"}
1
+ {"apiVersion":1,"compilerVersion":"0.1.0-alpha.18","sourceHash":"351f1300a8c8f67cbe2bb9d9bfb16787f280ed3147fad3eda349aa1d5822450a"}
@@ -16615,11 +16615,11 @@ function B2(e8) {
16615
16615
  }
16616
16616
  }
16617
16617
  async function* me(e8) {
16618
- let { url: t6, headers: r4, body: i3, identity: a3, signal: o3, timeout: s3, idleTimeout: c3, onResponse: l2, registeredToolNames: u } = e8, d = L2(), p = (e9 = false) => z2(a3, R3(d, e9), d, d.stopReason), m = false, h = false, g = false, _3, v3 = bt({ url: t6, body: i3, headers: r4, signal: o3, timeout: s3, idleTimeout: c3, onResponse: (e9) => {
16619
- _3 = e9, l2?.(e9);
16618
+ let { url: t6, headers: r4, body: i3, identity: a3, signal: o3, timeout: s3, idleTimeout: c3, onResponse: l2, registeredToolNames: u } = e8, d = L2(), p = (e9 = false) => z2(a3, R3(d, e9), d, d.stopReason), m = false, h = false, g = false, _3 = false, v3, y3 = bt({ url: t6, body: i3, headers: r4, signal: o3, timeout: s3, idleTimeout: c3, onResponse: (e9) => {
16619
+ v3 = e9, l2?.(e9);
16620
16620
  } });
16621
16621
  try {
16622
- for await (let e9 of v3) {
16622
+ for await (let e9 of y3) {
16623
16623
  if (e9.data === `[DONE]`) break;
16624
16624
  let t7;
16625
16625
  try {
@@ -16638,7 +16638,7 @@ async function* me(e8) {
16638
16638
  let r5 = t7.choices?.[0];
16639
16639
  if (!r5) continue;
16640
16640
  let i4 = r5.delta;
16641
- if (m || (m = true, yield { type: `start`, partial: p() }), r5.finish_reason && (d.stopReason = B2(r5.finish_reason)), !i4) continue;
16641
+ if (m || (m = true, yield { type: `start`, partial: p() }), r5.finish_reason && (_3 = true, d.stopReason = B2(r5.finish_reason)), !i4) continue;
16642
16642
  if (i4.content) {
16643
16643
  let e10 = i4.content;
16644
16644
  h || (h = true, yield { type: `text_start`, index: 0, partial: p() }), d.textContent += e10, yield { type: `text_delta`, index: 0, delta: e10, partial: p() };
@@ -16656,7 +16656,7 @@ async function* me(e8) {
16656
16656
  }
16657
16657
  g && (yield { type: `thinking_end`, index: 0, content: d.thinkingText, partial: p() }), h && (yield { type: `text_end`, index: 0, content: d.textContent, partial: p() });
16658
16658
  for (let [e9, t7] of d.toolCalls) yield { type: `tool_call_end`, index: e9, toolCall: N2(t7.id, t7.name, t7.argumentsJson), partial: p() };
16659
- yield { type: `done`, reason: d.stopReason, message: P2(p(true), u ?? /* @__PURE__ */ new Set()), providerUsage: A3(_3) };
16659
+ _3 || (d.stopReason = `incomplete`), yield { type: `done`, reason: d.stopReason, message: P2(p(true), u ?? /* @__PURE__ */ new Set()), providerUsage: A3(v3) };
16660
16660
  } catch (e9) {
16661
16661
  yield { type: `error`, error: e9 instanceof Error ? e9 : new Ce(`Unknown Chat Completions stream error`, { code: `PROVIDER_STREAM_ERROR` }) };
16662
16662
  }
@@ -17021,7 +17021,8 @@ function initStreamState(modelId) {
17021
17021
  ...j3(),
17022
17022
  model: modelId,
17023
17023
  blocks: /* @__PURE__ */ new Map(),
17024
- stopReason: "end_turn"
17024
+ stopReason: "end_turn",
17025
+ sawStopReason: false
17025
17026
  };
17026
17027
  }
17027
17028
  function buildAssistantMessage(state, final = false) {
@@ -17138,6 +17139,7 @@ function dispatchStreamEvent(event, state) {
17138
17139
  if (event.usage.cache_read_input_tokens != null) {
17139
17140
  state.cacheReadTokens = event.usage.cache_read_input_tokens;
17140
17141
  }
17142
+ state.sawStopReason = true;
17141
17143
  state.stopReason = mapStopReason(event.delta.stop_reason);
17142
17144
  return [];
17143
17145
  }
@@ -17291,6 +17293,9 @@ var AnthropicStream = class {
17291
17293
  if (signal.aborted) {
17292
17294
  throw new Anthropic.APIUserAbortError();
17293
17295
  }
17296
+ if (!state.sawStopReason) {
17297
+ state.stopReason = "incomplete";
17298
+ }
17294
17299
  const providerUsage = extractProviderUsageFromHeaders(response?.headers);
17295
17300
  const doneMessage = buildAssistantMessage(state, true);
17296
17301
  yield {
@@ -17483,7 +17488,8 @@ function initStreamState2(model) {
17483
17488
  ...j3(),
17484
17489
  model,
17485
17490
  blocks: /* @__PURE__ */ new Map(),
17486
- stopReason: "end_turn"
17491
+ stopReason: "end_turn",
17492
+ sawStopReason: false
17487
17493
  };
17488
17494
  }
17489
17495
  function buildAssistantMessage2(state, final = false) {
@@ -17549,6 +17555,7 @@ function dispatchResponsesSSEEvent(type, data, state) {
17549
17555
  }
17550
17556
  case "response.completed":
17551
17557
  case "response.incomplete": {
17558
+ state.sawStopReason = true;
17552
17559
  const response = data.response;
17553
17560
  const usage = response?.usage;
17554
17561
  if (usage) {
@@ -17722,6 +17729,9 @@ var OpenAIResponsesStream = class {
17722
17729
  };
17723
17730
  }
17724
17731
  }
17732
+ if (!state.sawStopReason) {
17733
+ state.stopReason = "incomplete";
17734
+ }
17725
17735
  yield {
17726
17736
  type: "done",
17727
17737
  reason: state.stopReason,
@@ -445,6 +445,13 @@ interface AnthropicStreamState extends StreamUsage {
445
445
  model: string
446
446
  blocks: Map<number, StreamBlock>
447
447
  stopReason: StopReason
448
+ /**
449
+ * RFC-421(新发现,会话 fe6bc77e 事故复盘):是否收到过 `message_delta`(唯一携带
450
+ * `stop_reason` 的事件)。流因 `message_stop` 或对端正常关闭而结束、但从未见过
451
+ * `message_delta` 时,`stopReason` 会悄悄停在默认值 'end_turn'——见
452
+ * chat-completions-stream.ts 同名字段注释,判据与根因完全一致(provider-agnostic)。
453
+ */
454
+ sawStopReason: boolean
448
455
  }
449
456
 
450
457
  function initStreamState(modelId: string): AnthropicStreamState {
@@ -453,6 +460,7 @@ function initStreamState(modelId: string): AnthropicStreamState {
453
460
  model: modelId,
454
461
  blocks: new Map(),
455
462
  stopReason: 'end_turn',
463
+ sawStopReason: false,
456
464
  }
457
465
  }
458
466
 
@@ -589,6 +597,7 @@ function dispatchStreamEvent(
589
597
  if (event.usage.cache_read_input_tokens != null) {
590
598
  state.cacheReadTokens = event.usage.cache_read_input_tokens
591
599
  }
600
+ state.sawStopReason = true
592
601
  state.stopReason = mapStopReason(event.delta.stop_reason)
593
602
  return []
594
603
  }
@@ -910,6 +919,11 @@ class AnthropicStream implements ProviderStream {
910
919
  // 成功路径 usage 快照:仅 Anthropic OAuth 订阅计划的响应带 anthropic-ratelimit-unified-*
911
920
  // 头族(API key 走标准 RPM/TPM,headers 里没有该头族 → extractProviderUsageFromHeaders
912
921
  // 返回 null,天然实现"API key 不显示")。
922
+ // RFC-421:流正常结束(无 SSE/网络异常)但从未见过 message_delta 的 stop_reason——
923
+ // 诚实标注为 'incomplete' 而非让默认值 'end_turn' 冒充"模型正常说完"。
924
+ if (!state.sawStopReason) {
925
+ state.stopReason = 'incomplete'
926
+ }
913
927
  const providerUsage = extractProviderUsageFromHeaders(response?.headers)
914
928
  const doneMessage = buildAssistantMessage(state, true)
915
929
  yield {
@@ -230,6 +230,13 @@ interface ResponsesStreamState extends StreamUsage {
230
230
  model: Model
231
231
  blocks: Map<number, StreamBlock>
232
232
  stopReason: StopReason
233
+ /**
234
+ * RFC-421(新发现,会话 fe6bc77e 事故复盘):是否收到过 `response.completed` /
235
+ * `response.incomplete`(唯二携带终态 stopReason 的事件)。流因 `[DONE]` 或对端正常
236
+ * 关闭而结束、但从未见过这两类事件时,`stopReason` 会悄悄停在默认值 'end_turn'——见
237
+ * chat-completions-stream.ts 同名字段注释,判据与根因完全一致(provider-agnostic)。
238
+ */
239
+ sawStopReason: boolean
233
240
  }
234
241
 
235
242
  function ensureBlock(
@@ -251,6 +258,7 @@ function initStreamState(model: Model): ResponsesStreamState {
251
258
  model,
252
259
  blocks: new Map(),
253
260
  stopReason: 'end_turn',
261
+ sawStopReason: false,
254
262
  }
255
263
  }
256
264
 
@@ -333,6 +341,7 @@ function dispatchResponsesSSEEvent(
333
341
 
334
342
  case 'response.completed':
335
343
  case 'response.incomplete': {
344
+ state.sawStopReason = true
336
345
  const response = data.response as Record<string, unknown> | undefined
337
346
  const usage = response?.usage as Record<string, unknown> | undefined
338
347
  if (usage) {
@@ -548,6 +557,12 @@ class OpenAIResponsesStream implements ProviderStream {
548
557
  }
549
558
  }
550
559
 
560
+ // RFC-421:流正常结束(无 SSE/网络异常)但从未见过 response.completed/incomplete——
561
+ // 诚实标注为 'incomplete' 而非让默认值 'end_turn' 冒充"模型正常说完"。
562
+ if (!state.sawStopReason) {
563
+ state.stopReason = 'incomplete'
564
+ }
565
+
551
566
  yield {
552
567
  type: 'done',
553
568
  reason: state.stopReason,