@prestyj/ai 4.2.76 → 4.3.15

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.cjs CHANGED
@@ -104,44 +104,62 @@ var EventStream = class {
104
104
  }
105
105
  };
106
106
  var StreamResult = class {
107
- events;
108
107
  response;
108
+ buffer = [];
109
+ done = false;
110
+ error = null;
109
111
  resolveResponse;
110
112
  rejectResponse;
111
- hasConsumer = false;
112
- constructor() {
113
- this.events = new EventStream();
113
+ resolveWait = null;
114
+ constructor(generator) {
114
115
  this.response = new Promise((resolve, reject) => {
115
116
  this.resolveResponse = resolve;
116
117
  this.rejectResponse = reject;
117
118
  });
119
+ this.pump(generator);
118
120
  }
119
- push(event) {
120
- this.events.push(event);
121
- }
122
- complete(response) {
123
- this.events.close();
124
- this.resolveResponse(response);
125
- }
126
- abort(error) {
127
- this.events.abort(error);
128
- this.rejectResponse(error);
121
+ async pump(generator) {
122
+ try {
123
+ let next = await generator.next();
124
+ while (!next.done) {
125
+ this.buffer.push(next.value);
126
+ this.resolveWait?.();
127
+ this.resolveWait = null;
128
+ next = await generator.next();
129
+ }
130
+ this.done = true;
131
+ this.resolveResponse(next.value);
132
+ this.resolveWait?.();
133
+ this.resolveWait = null;
134
+ } catch (err) {
135
+ const error = err instanceof Error ? err : new Error(String(err));
136
+ this.error = error;
137
+ this.done = true;
138
+ this.rejectResponse(error);
139
+ this.resolveWait?.();
140
+ this.resolveWait = null;
141
+ }
129
142
  }
130
- [Symbol.asyncIterator]() {
131
- this.hasConsumer = true;
132
- return this.events[Symbol.asyncIterator]();
143
+ async *[Symbol.asyncIterator]() {
144
+ let index = 0;
145
+ while (true) {
146
+ while (index < this.buffer.length) {
147
+ yield this.buffer[index++];
148
+ }
149
+ if (this.error) throw this.error;
150
+ if (this.done) return;
151
+ await new Promise((r) => {
152
+ this.resolveWait = r;
153
+ if (this.buffer.length > index || this.done || this.error) {
154
+ this.resolveWait = null;
155
+ r();
156
+ }
157
+ });
158
+ }
133
159
  }
134
160
  then(onfulfilled, onrejected) {
135
- this.drainEvents().catch(() => {
136
- });
137
161
  return this.response.then(onfulfilled, onrejected);
138
162
  }
139
- async drainEvents() {
140
- if (this.hasConsumer) return;
141
- this.hasConsumer = true;
142
- for await (const _ of this.events) {
143
- }
144
- }
145
163
  };
146
164
 
147
165
  // src/providers/anthropic.ts
@@ -215,6 +233,7 @@ function toAnthropicMessages(messages, cacheControl) {
215
233
  if (part.type === "raw") return part.data;
216
234
  return null;
217
235
  }).filter(Boolean);
236
+ if (Array.isArray(content) && content.length === 0) continue;
218
237
  out.push({ role: "assistant", content });
219
238
  continue;
220
239
  }
@@ -378,13 +397,16 @@ function toOpenAIMessages(messages, options) {
378
397
  ) : void 0;
379
398
  const textParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join("") : void 0;
380
399
  const thinkingParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "thinking").map((p) => p.text).join("") : void 0;
400
+ const contentValue = parts || textParts || null;
401
+ const hasToolCalls = toolCalls && toolCalls.length > 0;
402
+ if (!contentValue && !hasToolCalls) continue;
381
403
  const assistantMsg = {
382
404
  role: "assistant",
383
- content: parts || textParts || null,
384
- ...toolCalls?.length ? { tool_calls: toolCalls } : {}
405
+ content: contentValue,
406
+ ...hasToolCalls ? { tool_calls: toolCalls } : {}
385
407
  };
386
- if (thinkingParts || toolCalls?.length) {
387
- assistantMsg.reasoning_content = thinkingParts || " ";
408
+ if (thinkingParts) {
409
+ assistantMsg.reasoning_content = thinkingParts;
388
410
  }
389
411
  out.push(assistantMsg);
390
412
  continue;
@@ -450,17 +472,16 @@ function normalizeOpenAIStopReason(reason) {
450
472
  }
451
473
 
452
474
  // src/providers/anthropic.ts
453
- function streamAnthropic(options) {
454
- const result = new StreamResult();
455
- runStream(options, result).catch((err) => result.abort(toError(err)));
456
- return result;
457
- }
458
- async function runStream(options, result) {
475
+ function createClient(options) {
459
476
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
460
- const client = new import_sdk.default({
477
+ return new import_sdk.default({
461
478
  ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
462
479
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
463
480
  ...options.fetch ? { fetch: options.fetch } : {},
481
+ // Allow SDK retries for connection-level failures (socket hang up, 500s,
482
+ // connection refused). Our stall detection handles abort-initiated retries
483
+ // separately — SDK retries only fire on genuine transport errors.
484
+ maxRetries: 2,
464
485
  ...isOAuth ? {
465
486
  defaultHeaders: {
466
487
  "user-agent": "claude-cli/2.1.75",
@@ -468,6 +489,13 @@ async function runStream(options, result) {
468
489
  }
469
490
  } : {}
470
491
  });
492
+ }
493
+ function streamAnthropic(options) {
494
+ return new StreamResult(runStream(options));
495
+ }
496
+ async function* runStream(options) {
497
+ const client = createClient(options);
498
+ const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
471
499
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
472
500
  const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
473
501
  const system = isOAuth ? [
@@ -515,126 +543,209 @@ async function runStream(options, result) {
515
543
  })(),
516
544
  stream: true
517
545
  };
546
+ const hasAdaptiveThinking = options.model.includes("opus-4-6") || options.model.includes("opus-4.6") || options.model.includes("sonnet-4-6") || options.model.includes("sonnet-4.6");
518
547
  const betaHeaders = [
519
548
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
520
549
  ...options.compaction ? ["compact-2026-01-12"] : [],
521
- ...options.clearToolUses ? ["context-management-2025-06-27"] : []
550
+ ...options.clearToolUses ? ["context-management-2025-06-27"] : [],
551
+ "fine-grained-tool-streaming-2025-05-14",
552
+ ...!hasAdaptiveThinking ? ["interleaved-thinking-2025-05-14"] : []
522
553
  ];
523
554
  const stream2 = client.messages.stream(params, {
524
555
  signal: options.signal ?? void 0,
525
556
  ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
526
557
  });
527
558
  const contentParts = [];
528
- let currentToolId = "";
529
- let currentToolName = "";
530
- stream2.on("text", (text) => {
531
- result.push({ type: "text_delta", text });
532
- });
533
- stream2.on("thinking", (thinkingDelta) => {
534
- result.push({ type: "thinking_delta", text: thinkingDelta });
535
- });
536
- stream2.on("streamEvent", (event) => {
537
- if (event.type === "content_block_start") {
538
- if (event.content_block.type === "tool_use") {
539
- currentToolId = event.content_block.id;
540
- currentToolName = event.content_block.name;
541
- }
542
- if (event.content_block.type === "server_tool_use") {
543
- currentToolId = event.content_block.id;
544
- currentToolName = event.content_block.name;
545
- }
546
- }
547
- });
548
- stream2.on("inputJson", (delta) => {
549
- result.push({
550
- type: "toolcall_delta",
551
- id: currentToolId,
552
- name: currentToolName,
553
- argsJson: delta
554
- });
555
- });
556
- stream2.on("contentBlock", (block) => {
557
- if (block.type === "text") {
558
- contentParts.push({ type: "text", text: block.text });
559
- } else if (block.type === "thinking") {
560
- contentParts.push({ type: "thinking", text: block.thinking, signature: block.signature });
561
- } else if (block.type === "tool_use") {
562
- const tc = {
563
- type: "tool_call",
564
- id: block.id,
565
- name: block.name,
566
- args: block.input
567
- };
568
- contentParts.push(tc);
569
- result.push({
570
- type: "toolcall_done",
571
- id: tc.id,
572
- name: tc.name,
573
- args: tc.args
574
- });
575
- } else if (block.type === "server_tool_use") {
576
- const stc = {
577
- type: "server_tool_call",
578
- id: block.id,
579
- name: block.name,
580
- input: block.input
581
- };
582
- contentParts.push(stc);
583
- result.push({
584
- type: "server_toolcall",
585
- id: stc.id,
586
- name: stc.name,
587
- input: stc.input
588
- });
589
- } else {
590
- const raw = block;
591
- const blockType = raw.type;
592
- if (blockType === "web_search_tool_result") {
593
- const str = {
594
- type: "server_tool_result",
595
- toolUseId: raw.tool_use_id,
596
- resultType: blockType,
597
- data: raw
598
- };
599
- contentParts.push(str);
600
- result.push({
601
- type: "server_toolresult",
602
- toolUseId: str.toolUseId,
603
- resultType: str.resultType,
604
- data: str.data
605
- });
606
- } else {
607
- contentParts.push({ type: "raw", data: raw });
608
- }
609
- }
610
- });
559
+ const blocks = /* @__PURE__ */ new Map();
560
+ let inputTokens = 0;
561
+ let outputTokens = 0;
562
+ let cacheRead;
563
+ let cacheWrite;
564
+ let stopReason = null;
565
+ const keepalive = { type: "keepalive" };
611
566
  try {
612
- const finalMessage = await stream2.finalMessage();
613
- const stopReason = normalizeAnthropicStopReason(finalMessage.stop_reason);
614
- const response = {
615
- message: {
616
- role: "assistant",
617
- content: contentParts.length > 0 ? contentParts : ""
618
- },
619
- stopReason,
620
- usage: {
621
- inputTokens: finalMessage.usage.input_tokens,
622
- outputTokens: finalMessage.usage.output_tokens,
623
- ...finalMessage.usage.cache_read_input_tokens != null && {
624
- cacheRead: finalMessage.usage.cache_read_input_tokens
625
- },
626
- ...finalMessage.usage.cache_creation_input_tokens != null && {
627
- cacheWrite: finalMessage.usage.cache_creation_input_tokens
567
+ for await (const event of stream2) {
568
+ switch (event.type) {
569
+ case "message_start": {
570
+ const usage = event.message.usage;
571
+ inputTokens = usage.input_tokens;
572
+ const usageAny = usage;
573
+ if (usageAny.cache_read_input_tokens != null) {
574
+ cacheRead = usageAny.cache_read_input_tokens;
575
+ }
576
+ if (usageAny.cache_creation_input_tokens != null) {
577
+ cacheWrite = usageAny.cache_creation_input_tokens;
578
+ }
579
+ yield keepalive;
580
+ break;
581
+ }
582
+ case "content_block_start": {
583
+ const block = event.content_block;
584
+ const idx = event.index;
585
+ const accum = {
586
+ type: block.type,
587
+ text: "",
588
+ thinking: "",
589
+ signature: "",
590
+ toolId: "",
591
+ toolName: "",
592
+ argsJson: "",
593
+ input: void 0,
594
+ raw: null
595
+ };
596
+ if (block.type === "tool_use") {
597
+ accum.toolId = block.id;
598
+ accum.toolName = block.name;
599
+ } else if (block.type === "server_tool_use") {
600
+ accum.toolId = block.id;
601
+ accum.toolName = block.name;
602
+ accum.input = block.input;
603
+ } else if (block.type === "redacted_thinking") {
604
+ accum.raw = block;
605
+ }
606
+ blocks.set(idx, accum);
607
+ yield keepalive;
608
+ break;
609
+ }
610
+ case "content_block_delta": {
611
+ const accum = blocks.get(event.index);
612
+ if (!accum) break;
613
+ const delta = event.delta;
614
+ const deltaType = delta.type;
615
+ if (deltaType === "text_delta") {
616
+ const text = delta.text;
617
+ accum.text += text;
618
+ yield { type: "text_delta", text };
619
+ } else if (deltaType === "thinking_delta") {
620
+ const text = delta.thinking;
621
+ accum.thinking += text;
622
+ yield { type: "thinking_delta", text };
623
+ } else if (deltaType === "input_json_delta") {
624
+ const partialJson = delta.partial_json;
625
+ accum.argsJson += partialJson;
626
+ yield {
627
+ type: "toolcall_delta",
628
+ id: accum.toolId,
629
+ name: accum.toolName,
630
+ argsJson: partialJson
631
+ };
632
+ } else if (deltaType === "signature_delta") {
633
+ accum.signature = delta.signature;
634
+ }
635
+ break;
636
+ }
637
+ case "content_block_stop": {
638
+ const accum = blocks.get(event.index);
639
+ if (!accum) break;
640
+ if (accum.type === "text") {
641
+ contentParts.push({ type: "text", text: accum.text });
642
+ } else if (accum.type === "thinking") {
643
+ contentParts.push({
644
+ type: "thinking",
645
+ text: accum.thinking,
646
+ signature: accum.signature
647
+ });
648
+ yield keepalive;
649
+ } else if (accum.type === "tool_use") {
650
+ let args = {};
651
+ try {
652
+ args = JSON.parse(accum.argsJson);
653
+ } catch {
654
+ }
655
+ const tc = {
656
+ type: "tool_call",
657
+ id: accum.toolId,
658
+ name: accum.toolName,
659
+ args
660
+ };
661
+ contentParts.push(tc);
662
+ yield {
663
+ type: "toolcall_done",
664
+ id: tc.id,
665
+ name: tc.name,
666
+ args: tc.args
667
+ };
668
+ } else if (accum.type === "server_tool_use") {
669
+ const stc = {
670
+ type: "server_tool_call",
671
+ id: accum.toolId,
672
+ name: accum.toolName,
673
+ input: accum.input
674
+ };
675
+ contentParts.push(stc);
676
+ yield {
677
+ type: "server_toolcall",
678
+ id: stc.id,
679
+ name: stc.name,
680
+ input: stc.input
681
+ };
682
+ } else if (accum.type === "redacted_thinking" && accum.raw) {
683
+ contentParts.push({ type: "raw", data: accum.raw });
684
+ yield keepalive;
685
+ } else {
686
+ const msg = stream2.currentMessage;
687
+ const rawBlock = msg?.content[event.index];
688
+ if (rawBlock) {
689
+ const blockType = rawBlock.type;
690
+ if (blockType === "web_search_tool_result") {
691
+ const str = {
692
+ type: "server_tool_result",
693
+ toolUseId: rawBlock.tool_use_id,
694
+ resultType: blockType,
695
+ data: rawBlock
696
+ };
697
+ contentParts.push(str);
698
+ yield {
699
+ type: "server_toolresult",
700
+ toolUseId: str.toolUseId,
701
+ resultType: str.resultType,
702
+ data: str.data
703
+ };
704
+ } else {
705
+ contentParts.push({ type: "raw", data: rawBlock });
706
+ }
707
+ }
708
+ }
709
+ blocks.delete(event.index);
710
+ break;
711
+ }
712
+ case "message_delta": {
713
+ const delta = event.delta;
714
+ if (delta.stop_reason) {
715
+ stopReason = delta.stop_reason;
716
+ }
717
+ const usage = event.usage;
718
+ if (usage?.output_tokens != null) {
719
+ outputTokens = usage.output_tokens;
720
+ }
721
+ yield keepalive;
722
+ break;
628
723
  }
724
+ // message_stop — loop exits naturally
725
+ default:
726
+ yield keepalive;
727
+ break;
629
728
  }
630
- };
631
- result.push({ type: "done", stopReason });
632
- result.complete(response);
729
+ }
633
730
  } catch (err) {
634
- const error = toError(err);
635
- result.push({ type: "error", error });
636
- result.abort(error);
731
+ throw toError(err);
637
732
  }
733
+ const normalizedStop = normalizeAnthropicStopReason(stopReason);
734
+ const response = {
735
+ message: {
736
+ role: "assistant",
737
+ content: contentParts.length > 0 ? contentParts : ""
738
+ },
739
+ stopReason: normalizedStop,
740
+ usage: {
741
+ inputTokens,
742
+ outputTokens,
743
+ ...cacheRead != null && { cacheRead },
744
+ ...cacheWrite != null && { cacheWrite }
745
+ }
746
+ };
747
+ yield { type: "done", stopReason: normalizedStop };
748
+ return response;
638
749
  }
639
750
  function toError(err) {
640
751
  if (err instanceof import_sdk.default.APIError) {
@@ -651,19 +762,20 @@ function toError(err) {
651
762
 
652
763
  // src/providers/openai.ts
653
764
  var import_openai = __toESM(require("openai"), 1);
654
- function streamOpenAI(options) {
655
- const result = new StreamResult();
656
- const providerName = options.provider ?? "openai";
657
- runStream2(options, result).catch((err) => result.abort(toError2(err, providerName)));
658
- return result;
659
- }
660
- async function runStream2(options, result) {
661
- const client = new import_openai.default({
765
+ function createClient2(options) {
766
+ return new import_openai.default({
662
767
  apiKey: options.apiKey,
663
768
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
664
769
  ...options.fetch ? { fetch: options.fetch } : {}
665
770
  });
666
- const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot";
771
+ }
772
+ function streamOpenAI(options) {
773
+ return new StreamResult(runStream2(options));
774
+ }
775
+ async function* runStream2(options) {
776
+ const providerName = options.provider ?? "openai";
777
+ const client = createClient2(options);
778
+ const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
667
779
  const messages = toOpenAIMessages(options.messages, { provider: options.provider });
668
780
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
669
781
  const effectiveTemp = options.temperature ?? defaultTemp;
@@ -671,7 +783,7 @@ async function runStream2(options, result) {
671
783
  model: options.model,
672
784
  messages,
673
785
  stream: true,
674
- ...options.maxTokens ? { max_tokens: options.maxTokens } : {},
786
+ ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
675
787
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
676
788
  ...options.topP != null ? { top_p: options.topP } : {},
677
789
  ...options.stop ? { stop: options.stop } : {},
@@ -689,11 +801,31 @@ async function runStream2(options, result) {
689
801
  }
690
802
  }
691
803
  if (usesThinkingParam) {
692
- params.thinking = options.thinking ? { type: "enabled" } : { type: "disabled" };
804
+ if (options.thinking) {
805
+ params.thinking = { type: "enabled" };
806
+ } else {
807
+ params.thinking = { type: "disabled" };
808
+ }
809
+ }
810
+ if (globalThis.process && globalThis.process.env?.GGAI_DUMP_REQUEST) {
811
+ const fs = await import("fs");
812
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
813
+ const dumpPath = `/tmp/ggai-request-${ts}.json`;
814
+ fs.writeFileSync(dumpPath, JSON.stringify(params, null, 2));
815
+ fs.appendFileSync(
816
+ "/tmp/ggai-requests.log",
817
+ `[${ts}] ${dumpPath} messages=${params.messages.length}
818
+ `
819
+ );
820
+ }
821
+ let stream2;
822
+ try {
823
+ stream2 = await client.chat.completions.create(params, {
824
+ signal: options.signal ?? void 0
825
+ });
826
+ } catch (err) {
827
+ throw toError2(err, providerName);
693
828
  }
694
- const stream2 = await client.chat.completions.create(params, {
695
- signal: options.signal ?? void 0
696
- });
697
829
  const contentParts = [];
698
830
  const toolCallAccum = /* @__PURE__ */ new Map();
699
831
  let textAccum = "";
@@ -720,11 +852,13 @@ async function runStream2(options, result) {
720
852
  const reasoningContent = delta.reasoning_content;
721
853
  if (typeof reasoningContent === "string" && reasoningContent) {
722
854
  thinkingAccum += reasoningContent;
723
- result.push({ type: "thinking_delta", text: reasoningContent });
855
+ if (options.thinking) {
856
+ yield { type: "thinking_delta", text: reasoningContent };
857
+ }
724
858
  }
725
859
  if (delta.content) {
726
860
  textAccum += delta.content;
727
- result.push({ type: "text_delta", text: delta.content });
861
+ yield { type: "text_delta", text: delta.content };
728
862
  }
729
863
  if (delta.tool_calls) {
730
864
  for (const tc of delta.tool_calls) {
@@ -741,12 +875,12 @@ async function runStream2(options, result) {
741
875
  if (tc.function?.name) accum.name = tc.function.name;
742
876
  if (tc.function?.arguments) {
743
877
  accum.argsJson += tc.function.arguments;
744
- result.push({
878
+ yield {
745
879
  type: "toolcall_delta",
746
880
  id: accum.id,
747
881
  name: accum.name,
748
882
  argsJson: tc.function.arguments
749
- });
883
+ };
750
884
  }
751
885
  }
752
886
  }
@@ -770,12 +904,12 @@ async function runStream2(options, result) {
770
904
  args
771
905
  };
772
906
  contentParts.push(toolCall);
773
- result.push({
907
+ yield {
774
908
  type: "toolcall_done",
775
909
  id: tc.id,
776
910
  name: tc.name,
777
911
  args
778
- });
912
+ };
779
913
  }
780
914
  const stopReason = normalizeOpenAIStopReason(finishReason);
781
915
  const response = {
@@ -786,14 +920,20 @@ async function runStream2(options, result) {
786
920
  stopReason,
787
921
  usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
788
922
  };
789
- result.push({ type: "done", stopReason });
790
- result.complete(response);
923
+ yield { type: "done", stopReason };
924
+ return response;
791
925
  }
792
926
  function toError2(err, provider = "openai") {
793
927
  if (err instanceof import_openai.default.APIError) {
794
928
  let msg = err.message;
795
929
  const body = err.error;
796
930
  if (body) {
931
+ const modelName = body.model || "";
932
+ const _code = body.code || "";
933
+ const message = body.message || "";
934
+ if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
935
+ msg = `codex-mini-latest requires an OpenAI Pro or Max subscription. You currently have access to GPT-5.4 and GPT-5.4 Mini with your account.`;
936
+ }
797
937
  msg += ` | body: ${JSON.stringify(body)}`;
798
938
  }
799
939
  return new ProviderError(provider, msg, {
@@ -811,11 +951,9 @@ function toError2(err, provider = "openai") {
811
951
  var import_node_os = __toESM(require("os"), 1);
812
952
  var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
813
953
  function streamOpenAICodex(options) {
814
- const result = new StreamResult();
815
- runStream3(options, result).catch((err) => result.abort(toError3(err)));
816
- return result;
954
+ return new StreamResult(runStream3(options));
817
955
  }
818
- async function runStream3(options, result) {
956
+ async function* runStream3(options) {
819
957
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
820
958
  const url = `${baseUrl}/codex/responses`;
821
959
  const { system, input } = toCodexInput(options.messages);
@@ -865,6 +1003,11 @@ async function runStream3(options, result) {
865
1003
  message += `
866
1004
 
867
1005
  Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription. The "codex-spark" variants require ChatGPT Pro. Ensure your account has an active subscription at https://chatgpt.com/settings`;
1006
+ }
1007
+ if (response.status === 404 && text.includes("does not exist")) {
1008
+ message += `
1009
+
1010
+ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GPT-5.4 and GPT-5.4 Mini work with any active ChatGPT plan.`;
868
1011
  }
869
1012
  throw new ProviderError("openai", message, {
870
1013
  statusCode: response.status
@@ -892,11 +1035,11 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
892
1035
  if (type === "response.output_text.delta") {
893
1036
  const delta = event.delta;
894
1037
  textAccum += delta;
895
- result.push({ type: "text_delta", text: delta });
1038
+ yield { type: "text_delta", text: delta };
896
1039
  }
897
1040
  if (type === "response.reasoning_summary_text.delta") {
898
1041
  const delta = event.delta;
899
- result.push({ type: "thinking_delta", text: delta });
1042
+ yield { type: "thinking_delta", text: delta };
900
1043
  }
901
1044
  if (type === "response.output_item.added") {
902
1045
  const item = event.item;
@@ -914,12 +1057,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
914
1057
  for (const [key, tc] of toolCalls) {
915
1058
  if (key.endsWith(`|${itemId}`)) {
916
1059
  tc.argsJson += delta;
917
- result.push({
1060
+ yield {
918
1061
  type: "toolcall_delta",
919
1062
  id: tc.id,
920
1063
  name: tc.name,
921
1064
  argsJson: delta
922
- });
1065
+ };
923
1066
  break;
924
1067
  }
925
1068
  }
@@ -947,12 +1090,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
947
1090
  args = JSON.parse(tc.argsJson);
948
1091
  } catch {
949
1092
  }
950
- result.push({
1093
+ yield {
951
1094
  type: "toolcall_done",
952
1095
  id: tc.id,
953
1096
  name: tc.name,
954
1097
  args
955
- });
1098
+ };
956
1099
  }
957
1100
  }
958
1101
  }
@@ -992,8 +1135,8 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
992
1135
  stopReason,
993
1136
  usage: { inputTokens, outputTokens }
994
1137
  };
995
- result.push({ type: "done", stopReason });
996
- result.complete(streamResponse);
1138
+ yield { type: "done", stopReason };
1139
+ return streamResponse;
997
1140
  }
998
1141
  async function* parseSSE(body) {
999
1142
  const reader = body.getReader();
@@ -1107,13 +1250,6 @@ function toCodexTools(tools) {
1107
1250
  strict: null
1108
1251
  }));
1109
1252
  }
1110
- function toError3(err) {
1111
- if (err instanceof ProviderError) return err;
1112
- if (err instanceof Error) {
1113
- return new ProviderError("openai", err.message, { cause: err });
1114
- }
1115
- return new ProviderError("openai", String(err));
1116
- }
1117
1253
 
1118
1254
  // src/provider-registry.ts
1119
1255
  var ProviderRegistryImpl = class {
@@ -1153,10 +1289,16 @@ var providerRegistry = new ProviderRegistryImpl();
1153
1289
 
1154
1290
  // src/stream.ts
1155
1291
  var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
1156
- var GLM_REGULAR_BASE_URL = "https://api.z.ai/api/paas/v4";
1157
1292
  providerRegistry.register("anthropic", {
1158
1293
  stream: (options) => streamAnthropic(options)
1159
1294
  });
1295
+ providerRegistry.register("xiaomi", {
1296
+ stream: (options) => streamOpenAI({
1297
+ ...options,
1298
+ baseUrl: options.baseUrl ?? "https://token-plan-sgp.xiaomimimo.com/v1",
1299
+ webSearch: false
1300
+ })
1301
+ });
1160
1302
  providerRegistry.register("openai", {
1161
1303
  stream: (options) => {
1162
1304
  if (options.accountId) {
@@ -1166,10 +1308,10 @@ providerRegistry.register("openai", {
1166
1308
  }
1167
1309
  });
1168
1310
  providerRegistry.register("glm", {
1169
- stream: (options) => {
1170
- if (options.baseUrl) return streamOpenAI(options);
1171
- return streamGLMWithFallback(options);
1172
- }
1311
+ stream: (options) => streamOpenAI({
1312
+ ...options,
1313
+ baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
1314
+ })
1173
1315
  });
1174
1316
  providerRegistry.register("moonshot", {
1175
1317
  stream: (options) => streamOpenAI({
@@ -1177,6 +1319,18 @@ providerRegistry.register("moonshot", {
1177
1319
  baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1178
1320
  })
1179
1321
  });
1322
+ providerRegistry.register("minimax", {
1323
+ stream: (options) => streamAnthropic({
1324
+ ...options,
1325
+ baseUrl: options.baseUrl ?? "https://api.minimax.io/anthropic",
1326
+ // MiniMax's Anthropic-compatible API does not support Anthropic-specific
1327
+ // server tools (web_search), context_management, or server-side tools.
1328
+ webSearch: false,
1329
+ compaction: false,
1330
+ clearToolUses: false,
1331
+ serverTools: void 0
1332
+ })
1333
+ });
1180
1334
  function stream(options) {
1181
1335
  const entry = providerRegistry.get(options.provider);
1182
1336
  if (!entry) {
@@ -1186,32 +1340,6 @@ function stream(options) {
1186
1340
  }
1187
1341
  return entry.stream(options);
1188
1342
  }
1189
- function streamGLMWithFallback(options) {
1190
- const result = new StreamResult();
1191
- runGLMWithFallback(options, result).catch((err) => {
1192
- result.abort(err instanceof Error ? err : new Error(String(err)));
1193
- });
1194
- return result;
1195
- }
1196
- async function runGLMWithFallback(options, result) {
1197
- const codingResult = streamOpenAI({ ...options, baseUrl: GLM_CODING_BASE_URL });
1198
- try {
1199
- for await (const event of codingResult) {
1200
- result.push(event);
1201
- }
1202
- result.complete(await codingResult.response);
1203
- } catch {
1204
- const regularResult = streamOpenAI({ ...options, baseUrl: GLM_REGULAR_BASE_URL });
1205
- try {
1206
- for await (const event of regularResult) {
1207
- result.push(event);
1208
- }
1209
- result.complete(await regularResult.response);
1210
- } catch (fallbackErr) {
1211
- result.abort(fallbackErr instanceof Error ? fallbackErr : new Error(String(fallbackErr)));
1212
- }
1213
- }
1214
- }
1215
1343
 
1216
1344
  // src/providers/palsu.ts
1217
1345
  function palsuText(text) {
@@ -1239,31 +1367,29 @@ function chunkText(text, size) {
1239
1367
  }
1240
1368
  return chunks.length > 0 ? chunks : [""];
1241
1369
  }
1242
- function simulateStream(message, stopReason, result, signal, cacheUsage) {
1370
+ async function* simulateStream(message, stopReason, signal, cacheUsage) {
1243
1371
  if (signal?.aborted) {
1244
- result.abort(new Error("aborted"));
1245
- return;
1372
+ throw new Error("aborted");
1246
1373
  }
1247
1374
  const content = typeof message.content === "string" ? message.content ? [{ type: "text", text: message.content }] : [] : message.content;
1248
1375
  let outputChars = 0;
1249
1376
  for (const part of content) {
1250
1377
  if (signal?.aborted) {
1251
- result.abort(new Error("aborted"));
1252
- return;
1378
+ throw new Error("aborted");
1253
1379
  }
1254
1380
  if (part.type === "text") {
1255
1381
  const chunks = chunkText(part.text, DEFAULT_CHUNK_SIZE);
1256
1382
  for (const chunk of chunks) {
1257
- result.push({ type: "text_delta", text: chunk });
1383
+ yield { type: "text_delta", text: chunk };
1258
1384
  outputChars += chunk.length;
1259
1385
  }
1260
1386
  } else if (part.type === "thinking") {
1261
- result.push({ type: "thinking_delta", text: part.text });
1387
+ yield { type: "thinking_delta", text: part.text };
1262
1388
  outputChars += part.text.length;
1263
1389
  } else if (part.type === "tool_call") {
1264
1390
  const argsJson = JSON.stringify(part.args);
1265
- result.push({ type: "toolcall_delta", id: part.id, name: part.name, argsJson });
1266
- result.push({ type: "toolcall_done", id: part.id, name: part.name, args: part.args });
1391
+ yield { type: "toolcall_delta", id: part.id, name: part.name, argsJson };
1392
+ yield { type: "toolcall_done", id: part.id, name: part.name, args: part.args };
1267
1393
  outputChars += argsJson.length;
1268
1394
  }
1269
1395
  }
@@ -1274,8 +1400,8 @@ function simulateStream(message, stopReason, result, signal, cacheUsage) {
1274
1400
  ...cacheUsage?.cacheRead ? { cacheRead: cacheUsage.cacheRead } : {},
1275
1401
  ...cacheUsage?.cacheWrite ? { cacheWrite: cacheUsage.cacheWrite } : {}
1276
1402
  };
1277
- result.push({ type: "done", stopReason });
1278
- result.complete({ message, stopReason, usage });
1403
+ yield { type: "done", stopReason };
1404
+ return { message, stopReason, usage };
1279
1405
  }
1280
1406
  function computeCacheUsage(current, previous) {
1281
1407
  if (!previous) {
@@ -1347,24 +1473,21 @@ function registerPalsuProvider(config) {
1347
1473
  state.callCount++;
1348
1474
  const ms = modelStates.get(options.model);
1349
1475
  const responseDef = (ms && ms.responses.length > 0 ? ms.responses.shift() : void 0) ?? (responses.length > 0 ? responses.shift() : void 0) ?? ms?.defaultResponse ?? defaultResponse;
1350
- const result = new StreamResult();
1351
1476
  let cacheUsage;
1352
1477
  if (enableCache) {
1353
1478
  const serialized = JSON.stringify(options.messages);
1354
1479
  cacheUsage = computeCacheUsage(serialized, lastMessagesSerialized);
1355
1480
  lastMessagesSerialized = serialized;
1356
1481
  }
1357
- const rawMessage = typeof responseDef === "function" ? responseDef(options.messages, options, state) : responseDef;
1358
- Promise.resolve(rawMessage).then(
1359
- (message) => {
1360
- const hasToolCalls = Array.isArray(message.content) && message.content.some((p) => p.type === "tool_call");
1361
- const explicitStop = message._stopReason;
1362
- const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
1363
- simulateStream(message, stopReason, result, options.signal, cacheUsage);
1364
- },
1365
- (err) => result.abort(err instanceof Error ? err : new Error(String(err)))
1366
- );
1367
- return result;
1482
+ const gen = (async function* () {
1483
+ const rawMessage = typeof responseDef === "function" ? responseDef(options.messages, options, state) : responseDef;
1484
+ const message = await Promise.resolve(rawMessage);
1485
+ const hasToolCalls = Array.isArray(message.content) && message.content.some((p) => p.type === "tool_call");
1486
+ const explicitStop = message._stopReason;
1487
+ const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
1488
+ return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
1489
+ })();
1490
+ return new StreamResult(gen);
1368
1491
  }
1369
1492
  });
1370
1493
  return handle;