@prestyj/ai 4.2.77 → 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.js CHANGED
@@ -58,44 +58,62 @@ var EventStream = class {
58
58
  }
59
59
  };
60
60
  var StreamResult = class {
61
- events;
62
61
  response;
62
+ buffer = [];
63
+ done = false;
64
+ error = null;
63
65
  resolveResponse;
64
66
  rejectResponse;
65
- hasConsumer = false;
66
- constructor() {
67
- this.events = new EventStream();
67
+ resolveWait = null;
68
+ constructor(generator) {
68
69
  this.response = new Promise((resolve, reject) => {
69
70
  this.resolveResponse = resolve;
70
71
  this.rejectResponse = reject;
71
72
  });
73
+ this.pump(generator);
72
74
  }
73
- push(event) {
74
- this.events.push(event);
75
- }
76
- complete(response) {
77
- this.events.close();
78
- this.resolveResponse(response);
79
- }
80
- abort(error) {
81
- this.events.abort(error);
82
- this.rejectResponse(error);
75
+ async pump(generator) {
76
+ try {
77
+ let next = await generator.next();
78
+ while (!next.done) {
79
+ this.buffer.push(next.value);
80
+ this.resolveWait?.();
81
+ this.resolveWait = null;
82
+ next = await generator.next();
83
+ }
84
+ this.done = true;
85
+ this.resolveResponse(next.value);
86
+ this.resolveWait?.();
87
+ this.resolveWait = null;
88
+ } catch (err) {
89
+ const error = err instanceof Error ? err : new Error(String(err));
90
+ this.error = error;
91
+ this.done = true;
92
+ this.rejectResponse(error);
93
+ this.resolveWait?.();
94
+ this.resolveWait = null;
95
+ }
83
96
  }
84
- [Symbol.asyncIterator]() {
85
- this.hasConsumer = true;
86
- return this.events[Symbol.asyncIterator]();
97
+ async *[Symbol.asyncIterator]() {
98
+ let index = 0;
99
+ while (true) {
100
+ while (index < this.buffer.length) {
101
+ yield this.buffer[index++];
102
+ }
103
+ if (this.error) throw this.error;
104
+ if (this.done) return;
105
+ await new Promise((r) => {
106
+ this.resolveWait = r;
107
+ if (this.buffer.length > index || this.done || this.error) {
108
+ this.resolveWait = null;
109
+ r();
110
+ }
111
+ });
112
+ }
87
113
  }
88
114
  then(onfulfilled, onrejected) {
89
- this.drainEvents().catch(() => {
90
- });
91
115
  return this.response.then(onfulfilled, onrejected);
92
116
  }
93
- async drainEvents() {
94
- if (this.hasConsumer) return;
95
- this.hasConsumer = true;
96
- for await (const _ of this.events) {
97
- }
98
- }
99
117
  };
100
118
 
101
119
  // src/providers/anthropic.ts
@@ -169,6 +187,7 @@ function toAnthropicMessages(messages, cacheControl) {
169
187
  if (part.type === "raw") return part.data;
170
188
  return null;
171
189
  }).filter(Boolean);
190
+ if (Array.isArray(content) && content.length === 0) continue;
172
191
  out.push({ role: "assistant", content });
173
192
  continue;
174
193
  }
@@ -332,13 +351,16 @@ function toOpenAIMessages(messages, options) {
332
351
  ) : void 0;
333
352
  const textParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join("") : void 0;
334
353
  const thinkingParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "thinking").map((p) => p.text).join("") : void 0;
354
+ const contentValue = parts || textParts || null;
355
+ const hasToolCalls = toolCalls && toolCalls.length > 0;
356
+ if (!contentValue && !hasToolCalls) continue;
335
357
  const assistantMsg = {
336
358
  role: "assistant",
337
- content: parts || textParts || null,
338
- ...toolCalls?.length ? { tool_calls: toolCalls } : {}
359
+ content: contentValue,
360
+ ...hasToolCalls ? { tool_calls: toolCalls } : {}
339
361
  };
340
- if (thinkingParts || toolCalls?.length) {
341
- assistantMsg.reasoning_content = thinkingParts || " ";
362
+ if (thinkingParts) {
363
+ assistantMsg.reasoning_content = thinkingParts;
342
364
  }
343
365
  out.push(assistantMsg);
344
366
  continue;
@@ -404,17 +426,16 @@ function normalizeOpenAIStopReason(reason) {
404
426
  }
405
427
 
406
428
  // src/providers/anthropic.ts
407
- function streamAnthropic(options) {
408
- const result = new StreamResult();
409
- runStream(options, result).catch((err) => result.abort(toError(err)));
410
- return result;
411
- }
412
- async function runStream(options, result) {
429
+ function createClient(options) {
413
430
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
414
- const client = new Anthropic({
431
+ return new Anthropic({
415
432
  ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
416
433
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
417
434
  ...options.fetch ? { fetch: options.fetch } : {},
435
+ // Allow SDK retries for connection-level failures (socket hang up, 500s,
436
+ // connection refused). Our stall detection handles abort-initiated retries
437
+ // separately — SDK retries only fire on genuine transport errors.
438
+ maxRetries: 2,
418
439
  ...isOAuth ? {
419
440
  defaultHeaders: {
420
441
  "user-agent": "claude-cli/2.1.75",
@@ -422,6 +443,13 @@ async function runStream(options, result) {
422
443
  }
423
444
  } : {}
424
445
  });
446
+ }
447
+ function streamAnthropic(options) {
448
+ return new StreamResult(runStream(options));
449
+ }
450
+ async function* runStream(options) {
451
+ const client = createClient(options);
452
+ const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
425
453
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
426
454
  const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
427
455
  const system = isOAuth ? [
@@ -469,126 +497,209 @@ async function runStream(options, result) {
469
497
  })(),
470
498
  stream: true
471
499
  };
500
+ 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");
472
501
  const betaHeaders = [
473
502
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
474
503
  ...options.compaction ? ["compact-2026-01-12"] : [],
475
- ...options.clearToolUses ? ["context-management-2025-06-27"] : []
504
+ ...options.clearToolUses ? ["context-management-2025-06-27"] : [],
505
+ "fine-grained-tool-streaming-2025-05-14",
506
+ ...!hasAdaptiveThinking ? ["interleaved-thinking-2025-05-14"] : []
476
507
  ];
477
508
  const stream2 = client.messages.stream(params, {
478
509
  signal: options.signal ?? void 0,
479
510
  ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
480
511
  });
481
512
  const contentParts = [];
482
- let currentToolId = "";
483
- let currentToolName = "";
484
- stream2.on("text", (text) => {
485
- result.push({ type: "text_delta", text });
486
- });
487
- stream2.on("thinking", (thinkingDelta) => {
488
- result.push({ type: "thinking_delta", text: thinkingDelta });
489
- });
490
- stream2.on("streamEvent", (event) => {
491
- if (event.type === "content_block_start") {
492
- if (event.content_block.type === "tool_use") {
493
- currentToolId = event.content_block.id;
494
- currentToolName = event.content_block.name;
495
- }
496
- if (event.content_block.type === "server_tool_use") {
497
- currentToolId = event.content_block.id;
498
- currentToolName = event.content_block.name;
499
- }
500
- }
501
- });
502
- stream2.on("inputJson", (delta) => {
503
- result.push({
504
- type: "toolcall_delta",
505
- id: currentToolId,
506
- name: currentToolName,
507
- argsJson: delta
508
- });
509
- });
510
- stream2.on("contentBlock", (block) => {
511
- if (block.type === "text") {
512
- contentParts.push({ type: "text", text: block.text });
513
- } else if (block.type === "thinking") {
514
- contentParts.push({ type: "thinking", text: block.thinking, signature: block.signature });
515
- } else if (block.type === "tool_use") {
516
- const tc = {
517
- type: "tool_call",
518
- id: block.id,
519
- name: block.name,
520
- args: block.input
521
- };
522
- contentParts.push(tc);
523
- result.push({
524
- type: "toolcall_done",
525
- id: tc.id,
526
- name: tc.name,
527
- args: tc.args
528
- });
529
- } else if (block.type === "server_tool_use") {
530
- const stc = {
531
- type: "server_tool_call",
532
- id: block.id,
533
- name: block.name,
534
- input: block.input
535
- };
536
- contentParts.push(stc);
537
- result.push({
538
- type: "server_toolcall",
539
- id: stc.id,
540
- name: stc.name,
541
- input: stc.input
542
- });
543
- } else {
544
- const raw = block;
545
- const blockType = raw.type;
546
- if (blockType === "web_search_tool_result") {
547
- const str = {
548
- type: "server_tool_result",
549
- toolUseId: raw.tool_use_id,
550
- resultType: blockType,
551
- data: raw
552
- };
553
- contentParts.push(str);
554
- result.push({
555
- type: "server_toolresult",
556
- toolUseId: str.toolUseId,
557
- resultType: str.resultType,
558
- data: str.data
559
- });
560
- } else {
561
- contentParts.push({ type: "raw", data: raw });
562
- }
563
- }
564
- });
513
+ const blocks = /* @__PURE__ */ new Map();
514
+ let inputTokens = 0;
515
+ let outputTokens = 0;
516
+ let cacheRead;
517
+ let cacheWrite;
518
+ let stopReason = null;
519
+ const keepalive = { type: "keepalive" };
565
520
  try {
566
- const finalMessage = await stream2.finalMessage();
567
- const stopReason = normalizeAnthropicStopReason(finalMessage.stop_reason);
568
- const response = {
569
- message: {
570
- role: "assistant",
571
- content: contentParts.length > 0 ? contentParts : ""
572
- },
573
- stopReason,
574
- usage: {
575
- inputTokens: finalMessage.usage.input_tokens,
576
- outputTokens: finalMessage.usage.output_tokens,
577
- ...finalMessage.usage.cache_read_input_tokens != null && {
578
- cacheRead: finalMessage.usage.cache_read_input_tokens
579
- },
580
- ...finalMessage.usage.cache_creation_input_tokens != null && {
581
- cacheWrite: finalMessage.usage.cache_creation_input_tokens
521
+ for await (const event of stream2) {
522
+ switch (event.type) {
523
+ case "message_start": {
524
+ const usage = event.message.usage;
525
+ inputTokens = usage.input_tokens;
526
+ const usageAny = usage;
527
+ if (usageAny.cache_read_input_tokens != null) {
528
+ cacheRead = usageAny.cache_read_input_tokens;
529
+ }
530
+ if (usageAny.cache_creation_input_tokens != null) {
531
+ cacheWrite = usageAny.cache_creation_input_tokens;
532
+ }
533
+ yield keepalive;
534
+ break;
535
+ }
536
+ case "content_block_start": {
537
+ const block = event.content_block;
538
+ const idx = event.index;
539
+ const accum = {
540
+ type: block.type,
541
+ text: "",
542
+ thinking: "",
543
+ signature: "",
544
+ toolId: "",
545
+ toolName: "",
546
+ argsJson: "",
547
+ input: void 0,
548
+ raw: null
549
+ };
550
+ if (block.type === "tool_use") {
551
+ accum.toolId = block.id;
552
+ accum.toolName = block.name;
553
+ } else if (block.type === "server_tool_use") {
554
+ accum.toolId = block.id;
555
+ accum.toolName = block.name;
556
+ accum.input = block.input;
557
+ } else if (block.type === "redacted_thinking") {
558
+ accum.raw = block;
559
+ }
560
+ blocks.set(idx, accum);
561
+ yield keepalive;
562
+ break;
563
+ }
564
+ case "content_block_delta": {
565
+ const accum = blocks.get(event.index);
566
+ if (!accum) break;
567
+ const delta = event.delta;
568
+ const deltaType = delta.type;
569
+ if (deltaType === "text_delta") {
570
+ const text = delta.text;
571
+ accum.text += text;
572
+ yield { type: "text_delta", text };
573
+ } else if (deltaType === "thinking_delta") {
574
+ const text = delta.thinking;
575
+ accum.thinking += text;
576
+ yield { type: "thinking_delta", text };
577
+ } else if (deltaType === "input_json_delta") {
578
+ const partialJson = delta.partial_json;
579
+ accum.argsJson += partialJson;
580
+ yield {
581
+ type: "toolcall_delta",
582
+ id: accum.toolId,
583
+ name: accum.toolName,
584
+ argsJson: partialJson
585
+ };
586
+ } else if (deltaType === "signature_delta") {
587
+ accum.signature = delta.signature;
588
+ }
589
+ break;
590
+ }
591
+ case "content_block_stop": {
592
+ const accum = blocks.get(event.index);
593
+ if (!accum) break;
594
+ if (accum.type === "text") {
595
+ contentParts.push({ type: "text", text: accum.text });
596
+ } else if (accum.type === "thinking") {
597
+ contentParts.push({
598
+ type: "thinking",
599
+ text: accum.thinking,
600
+ signature: accum.signature
601
+ });
602
+ yield keepalive;
603
+ } else if (accum.type === "tool_use") {
604
+ let args = {};
605
+ try {
606
+ args = JSON.parse(accum.argsJson);
607
+ } catch {
608
+ }
609
+ const tc = {
610
+ type: "tool_call",
611
+ id: accum.toolId,
612
+ name: accum.toolName,
613
+ args
614
+ };
615
+ contentParts.push(tc);
616
+ yield {
617
+ type: "toolcall_done",
618
+ id: tc.id,
619
+ name: tc.name,
620
+ args: tc.args
621
+ };
622
+ } else if (accum.type === "server_tool_use") {
623
+ const stc = {
624
+ type: "server_tool_call",
625
+ id: accum.toolId,
626
+ name: accum.toolName,
627
+ input: accum.input
628
+ };
629
+ contentParts.push(stc);
630
+ yield {
631
+ type: "server_toolcall",
632
+ id: stc.id,
633
+ name: stc.name,
634
+ input: stc.input
635
+ };
636
+ } else if (accum.type === "redacted_thinking" && accum.raw) {
637
+ contentParts.push({ type: "raw", data: accum.raw });
638
+ yield keepalive;
639
+ } else {
640
+ const msg = stream2.currentMessage;
641
+ const rawBlock = msg?.content[event.index];
642
+ if (rawBlock) {
643
+ const blockType = rawBlock.type;
644
+ if (blockType === "web_search_tool_result") {
645
+ const str = {
646
+ type: "server_tool_result",
647
+ toolUseId: rawBlock.tool_use_id,
648
+ resultType: blockType,
649
+ data: rawBlock
650
+ };
651
+ contentParts.push(str);
652
+ yield {
653
+ type: "server_toolresult",
654
+ toolUseId: str.toolUseId,
655
+ resultType: str.resultType,
656
+ data: str.data
657
+ };
658
+ } else {
659
+ contentParts.push({ type: "raw", data: rawBlock });
660
+ }
661
+ }
662
+ }
663
+ blocks.delete(event.index);
664
+ break;
665
+ }
666
+ case "message_delta": {
667
+ const delta = event.delta;
668
+ if (delta.stop_reason) {
669
+ stopReason = delta.stop_reason;
670
+ }
671
+ const usage = event.usage;
672
+ if (usage?.output_tokens != null) {
673
+ outputTokens = usage.output_tokens;
674
+ }
675
+ yield keepalive;
676
+ break;
582
677
  }
678
+ // message_stop — loop exits naturally
679
+ default:
680
+ yield keepalive;
681
+ break;
583
682
  }
584
- };
585
- result.push({ type: "done", stopReason });
586
- result.complete(response);
683
+ }
587
684
  } catch (err) {
588
- const error = toError(err);
589
- result.push({ type: "error", error });
590
- result.abort(error);
685
+ throw toError(err);
591
686
  }
687
+ const normalizedStop = normalizeAnthropicStopReason(stopReason);
688
+ const response = {
689
+ message: {
690
+ role: "assistant",
691
+ content: contentParts.length > 0 ? contentParts : ""
692
+ },
693
+ stopReason: normalizedStop,
694
+ usage: {
695
+ inputTokens,
696
+ outputTokens,
697
+ ...cacheRead != null && { cacheRead },
698
+ ...cacheWrite != null && { cacheWrite }
699
+ }
700
+ };
701
+ yield { type: "done", stopReason: normalizedStop };
702
+ return response;
592
703
  }
593
704
  function toError(err) {
594
705
  if (err instanceof Anthropic.APIError) {
@@ -605,19 +716,20 @@ function toError(err) {
605
716
 
606
717
  // src/providers/openai.ts
607
718
  import OpenAI from "openai";
608
- function streamOpenAI(options) {
609
- const result = new StreamResult();
610
- const providerName = options.provider ?? "openai";
611
- runStream2(options, result).catch((err) => result.abort(toError2(err, providerName)));
612
- return result;
613
- }
614
- async function runStream2(options, result) {
615
- const client = new OpenAI({
719
+ function createClient2(options) {
720
+ return new OpenAI({
616
721
  apiKey: options.apiKey,
617
722
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
618
723
  ...options.fetch ? { fetch: options.fetch } : {}
619
724
  });
620
- const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot";
725
+ }
726
+ function streamOpenAI(options) {
727
+ return new StreamResult(runStream2(options));
728
+ }
729
+ async function* runStream2(options) {
730
+ const providerName = options.provider ?? "openai";
731
+ const client = createClient2(options);
732
+ const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
621
733
  const messages = toOpenAIMessages(options.messages, { provider: options.provider });
622
734
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
623
735
  const effectiveTemp = options.temperature ?? defaultTemp;
@@ -625,7 +737,7 @@ async function runStream2(options, result) {
625
737
  model: options.model,
626
738
  messages,
627
739
  stream: true,
628
- ...options.maxTokens ? { max_tokens: options.maxTokens } : {},
740
+ ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
629
741
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
630
742
  ...options.topP != null ? { top_p: options.topP } : {},
631
743
  ...options.stop ? { stop: options.stop } : {},
@@ -643,11 +755,31 @@ async function runStream2(options, result) {
643
755
  }
644
756
  }
645
757
  if (usesThinkingParam) {
646
- params.thinking = options.thinking ? { type: "enabled" } : { type: "disabled" };
758
+ if (options.thinking) {
759
+ params.thinking = { type: "enabled" };
760
+ } else {
761
+ params.thinking = { type: "disabled" };
762
+ }
763
+ }
764
+ if (globalThis.process && globalThis.process.env?.GGAI_DUMP_REQUEST) {
765
+ const fs = await import("fs");
766
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
767
+ const dumpPath = `/tmp/ggai-request-${ts}.json`;
768
+ fs.writeFileSync(dumpPath, JSON.stringify(params, null, 2));
769
+ fs.appendFileSync(
770
+ "/tmp/ggai-requests.log",
771
+ `[${ts}] ${dumpPath} messages=${params.messages.length}
772
+ `
773
+ );
774
+ }
775
+ let stream2;
776
+ try {
777
+ stream2 = await client.chat.completions.create(params, {
778
+ signal: options.signal ?? void 0
779
+ });
780
+ } catch (err) {
781
+ throw toError2(err, providerName);
647
782
  }
648
- const stream2 = await client.chat.completions.create(params, {
649
- signal: options.signal ?? void 0
650
- });
651
783
  const contentParts = [];
652
784
  const toolCallAccum = /* @__PURE__ */ new Map();
653
785
  let textAccum = "";
@@ -674,11 +806,13 @@ async function runStream2(options, result) {
674
806
  const reasoningContent = delta.reasoning_content;
675
807
  if (typeof reasoningContent === "string" && reasoningContent) {
676
808
  thinkingAccum += reasoningContent;
677
- result.push({ type: "thinking_delta", text: reasoningContent });
809
+ if (options.thinking) {
810
+ yield { type: "thinking_delta", text: reasoningContent };
811
+ }
678
812
  }
679
813
  if (delta.content) {
680
814
  textAccum += delta.content;
681
- result.push({ type: "text_delta", text: delta.content });
815
+ yield { type: "text_delta", text: delta.content };
682
816
  }
683
817
  if (delta.tool_calls) {
684
818
  for (const tc of delta.tool_calls) {
@@ -695,12 +829,12 @@ async function runStream2(options, result) {
695
829
  if (tc.function?.name) accum.name = tc.function.name;
696
830
  if (tc.function?.arguments) {
697
831
  accum.argsJson += tc.function.arguments;
698
- result.push({
832
+ yield {
699
833
  type: "toolcall_delta",
700
834
  id: accum.id,
701
835
  name: accum.name,
702
836
  argsJson: tc.function.arguments
703
- });
837
+ };
704
838
  }
705
839
  }
706
840
  }
@@ -724,12 +858,12 @@ async function runStream2(options, result) {
724
858
  args
725
859
  };
726
860
  contentParts.push(toolCall);
727
- result.push({
861
+ yield {
728
862
  type: "toolcall_done",
729
863
  id: tc.id,
730
864
  name: tc.name,
731
865
  args
732
- });
866
+ };
733
867
  }
734
868
  const stopReason = normalizeOpenAIStopReason(finishReason);
735
869
  const response = {
@@ -740,14 +874,20 @@ async function runStream2(options, result) {
740
874
  stopReason,
741
875
  usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
742
876
  };
743
- result.push({ type: "done", stopReason });
744
- result.complete(response);
877
+ yield { type: "done", stopReason };
878
+ return response;
745
879
  }
746
880
  function toError2(err, provider = "openai") {
747
881
  if (err instanceof OpenAI.APIError) {
748
882
  let msg = err.message;
749
883
  const body = err.error;
750
884
  if (body) {
885
+ const modelName = body.model || "";
886
+ const _code = body.code || "";
887
+ const message = body.message || "";
888
+ if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
889
+ 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.`;
890
+ }
751
891
  msg += ` | body: ${JSON.stringify(body)}`;
752
892
  }
753
893
  return new ProviderError(provider, msg, {
@@ -765,11 +905,9 @@ function toError2(err, provider = "openai") {
765
905
  import os from "os";
766
906
  var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
767
907
  function streamOpenAICodex(options) {
768
- const result = new StreamResult();
769
- runStream3(options, result).catch((err) => result.abort(toError3(err)));
770
- return result;
908
+ return new StreamResult(runStream3(options));
771
909
  }
772
- async function runStream3(options, result) {
910
+ async function* runStream3(options) {
773
911
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
774
912
  const url = `${baseUrl}/codex/responses`;
775
913
  const { system, input } = toCodexInput(options.messages);
@@ -819,6 +957,11 @@ async function runStream3(options, result) {
819
957
  message += `
820
958
 
821
959
  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`;
960
+ }
961
+ if (response.status === 404 && text.includes("does not exist")) {
962
+ message += `
963
+
964
+ 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.`;
822
965
  }
823
966
  throw new ProviderError("openai", message, {
824
967
  statusCode: response.status
@@ -846,11 +989,11 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
846
989
  if (type === "response.output_text.delta") {
847
990
  const delta = event.delta;
848
991
  textAccum += delta;
849
- result.push({ type: "text_delta", text: delta });
992
+ yield { type: "text_delta", text: delta };
850
993
  }
851
994
  if (type === "response.reasoning_summary_text.delta") {
852
995
  const delta = event.delta;
853
- result.push({ type: "thinking_delta", text: delta });
996
+ yield { type: "thinking_delta", text: delta };
854
997
  }
855
998
  if (type === "response.output_item.added") {
856
999
  const item = event.item;
@@ -868,12 +1011,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
868
1011
  for (const [key, tc] of toolCalls) {
869
1012
  if (key.endsWith(`|${itemId}`)) {
870
1013
  tc.argsJson += delta;
871
- result.push({
1014
+ yield {
872
1015
  type: "toolcall_delta",
873
1016
  id: tc.id,
874
1017
  name: tc.name,
875
1018
  argsJson: delta
876
- });
1019
+ };
877
1020
  break;
878
1021
  }
879
1022
  }
@@ -901,12 +1044,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
901
1044
  args = JSON.parse(tc.argsJson);
902
1045
  } catch {
903
1046
  }
904
- result.push({
1047
+ yield {
905
1048
  type: "toolcall_done",
906
1049
  id: tc.id,
907
1050
  name: tc.name,
908
1051
  args
909
- });
1052
+ };
910
1053
  }
911
1054
  }
912
1055
  }
@@ -946,8 +1089,8 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
946
1089
  stopReason,
947
1090
  usage: { inputTokens, outputTokens }
948
1091
  };
949
- result.push({ type: "done", stopReason });
950
- result.complete(streamResponse);
1092
+ yield { type: "done", stopReason };
1093
+ return streamResponse;
951
1094
  }
952
1095
  async function* parseSSE(body) {
953
1096
  const reader = body.getReader();
@@ -1061,13 +1204,6 @@ function toCodexTools(tools) {
1061
1204
  strict: null
1062
1205
  }));
1063
1206
  }
1064
- function toError3(err) {
1065
- if (err instanceof ProviderError) return err;
1066
- if (err instanceof Error) {
1067
- return new ProviderError("openai", err.message, { cause: err });
1068
- }
1069
- return new ProviderError("openai", String(err));
1070
- }
1071
1207
 
1072
1208
  // src/provider-registry.ts
1073
1209
  var ProviderRegistryImpl = class {
@@ -1107,10 +1243,16 @@ var providerRegistry = new ProviderRegistryImpl();
1107
1243
 
1108
1244
  // src/stream.ts
1109
1245
  var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
1110
- var GLM_REGULAR_BASE_URL = "https://api.z.ai/api/paas/v4";
1111
1246
  providerRegistry.register("anthropic", {
1112
1247
  stream: (options) => streamAnthropic(options)
1113
1248
  });
1249
+ providerRegistry.register("xiaomi", {
1250
+ stream: (options) => streamOpenAI({
1251
+ ...options,
1252
+ baseUrl: options.baseUrl ?? "https://token-plan-sgp.xiaomimimo.com/v1",
1253
+ webSearch: false
1254
+ })
1255
+ });
1114
1256
  providerRegistry.register("openai", {
1115
1257
  stream: (options) => {
1116
1258
  if (options.accountId) {
@@ -1120,10 +1262,10 @@ providerRegistry.register("openai", {
1120
1262
  }
1121
1263
  });
1122
1264
  providerRegistry.register("glm", {
1123
- stream: (options) => {
1124
- if (options.baseUrl) return streamOpenAI(options);
1125
- return streamGLMWithFallback(options);
1126
- }
1265
+ stream: (options) => streamOpenAI({
1266
+ ...options,
1267
+ baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
1268
+ })
1127
1269
  });
1128
1270
  providerRegistry.register("moonshot", {
1129
1271
  stream: (options) => streamOpenAI({
@@ -1131,6 +1273,18 @@ providerRegistry.register("moonshot", {
1131
1273
  baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1132
1274
  })
1133
1275
  });
1276
+ providerRegistry.register("minimax", {
1277
+ stream: (options) => streamAnthropic({
1278
+ ...options,
1279
+ baseUrl: options.baseUrl ?? "https://api.minimax.io/anthropic",
1280
+ // MiniMax's Anthropic-compatible API does not support Anthropic-specific
1281
+ // server tools (web_search), context_management, or server-side tools.
1282
+ webSearch: false,
1283
+ compaction: false,
1284
+ clearToolUses: false,
1285
+ serverTools: void 0
1286
+ })
1287
+ });
1134
1288
  function stream(options) {
1135
1289
  const entry = providerRegistry.get(options.provider);
1136
1290
  if (!entry) {
@@ -1140,32 +1294,6 @@ function stream(options) {
1140
1294
  }
1141
1295
  return entry.stream(options);
1142
1296
  }
1143
- function streamGLMWithFallback(options) {
1144
- const result = new StreamResult();
1145
- runGLMWithFallback(options, result).catch((err) => {
1146
- result.abort(err instanceof Error ? err : new Error(String(err)));
1147
- });
1148
- return result;
1149
- }
1150
- async function runGLMWithFallback(options, result) {
1151
- const codingResult = streamOpenAI({ ...options, baseUrl: GLM_CODING_BASE_URL });
1152
- try {
1153
- for await (const event of codingResult) {
1154
- result.push(event);
1155
- }
1156
- result.complete(await codingResult.response);
1157
- } catch {
1158
- const regularResult = streamOpenAI({ ...options, baseUrl: GLM_REGULAR_BASE_URL });
1159
- try {
1160
- for await (const event of regularResult) {
1161
- result.push(event);
1162
- }
1163
- result.complete(await regularResult.response);
1164
- } catch (fallbackErr) {
1165
- result.abort(fallbackErr instanceof Error ? fallbackErr : new Error(String(fallbackErr)));
1166
- }
1167
- }
1168
- }
1169
1297
 
1170
1298
  // src/providers/palsu.ts
1171
1299
  function palsuText(text) {
@@ -1193,31 +1321,29 @@ function chunkText(text, size) {
1193
1321
  }
1194
1322
  return chunks.length > 0 ? chunks : [""];
1195
1323
  }
1196
- function simulateStream(message, stopReason, result, signal, cacheUsage) {
1324
+ async function* simulateStream(message, stopReason, signal, cacheUsage) {
1197
1325
  if (signal?.aborted) {
1198
- result.abort(new Error("aborted"));
1199
- return;
1326
+ throw new Error("aborted");
1200
1327
  }
1201
1328
  const content = typeof message.content === "string" ? message.content ? [{ type: "text", text: message.content }] : [] : message.content;
1202
1329
  let outputChars = 0;
1203
1330
  for (const part of content) {
1204
1331
  if (signal?.aborted) {
1205
- result.abort(new Error("aborted"));
1206
- return;
1332
+ throw new Error("aborted");
1207
1333
  }
1208
1334
  if (part.type === "text") {
1209
1335
  const chunks = chunkText(part.text, DEFAULT_CHUNK_SIZE);
1210
1336
  for (const chunk of chunks) {
1211
- result.push({ type: "text_delta", text: chunk });
1337
+ yield { type: "text_delta", text: chunk };
1212
1338
  outputChars += chunk.length;
1213
1339
  }
1214
1340
  } else if (part.type === "thinking") {
1215
- result.push({ type: "thinking_delta", text: part.text });
1341
+ yield { type: "thinking_delta", text: part.text };
1216
1342
  outputChars += part.text.length;
1217
1343
  } else if (part.type === "tool_call") {
1218
1344
  const argsJson = JSON.stringify(part.args);
1219
- result.push({ type: "toolcall_delta", id: part.id, name: part.name, argsJson });
1220
- result.push({ type: "toolcall_done", id: part.id, name: part.name, args: part.args });
1345
+ yield { type: "toolcall_delta", id: part.id, name: part.name, argsJson };
1346
+ yield { type: "toolcall_done", id: part.id, name: part.name, args: part.args };
1221
1347
  outputChars += argsJson.length;
1222
1348
  }
1223
1349
  }
@@ -1228,8 +1354,8 @@ function simulateStream(message, stopReason, result, signal, cacheUsage) {
1228
1354
  ...cacheUsage?.cacheRead ? { cacheRead: cacheUsage.cacheRead } : {},
1229
1355
  ...cacheUsage?.cacheWrite ? { cacheWrite: cacheUsage.cacheWrite } : {}
1230
1356
  };
1231
- result.push({ type: "done", stopReason });
1232
- result.complete({ message, stopReason, usage });
1357
+ yield { type: "done", stopReason };
1358
+ return { message, stopReason, usage };
1233
1359
  }
1234
1360
  function computeCacheUsage(current, previous) {
1235
1361
  if (!previous) {
@@ -1301,24 +1427,21 @@ function registerPalsuProvider(config) {
1301
1427
  state.callCount++;
1302
1428
  const ms = modelStates.get(options.model);
1303
1429
  const responseDef = (ms && ms.responses.length > 0 ? ms.responses.shift() : void 0) ?? (responses.length > 0 ? responses.shift() : void 0) ?? ms?.defaultResponse ?? defaultResponse;
1304
- const result = new StreamResult();
1305
1430
  let cacheUsage;
1306
1431
  if (enableCache) {
1307
1432
  const serialized = JSON.stringify(options.messages);
1308
1433
  cacheUsage = computeCacheUsage(serialized, lastMessagesSerialized);
1309
1434
  lastMessagesSerialized = serialized;
1310
1435
  }
1311
- const rawMessage = typeof responseDef === "function" ? responseDef(options.messages, options, state) : responseDef;
1312
- Promise.resolve(rawMessage).then(
1313
- (message) => {
1314
- const hasToolCalls = Array.isArray(message.content) && message.content.some((p) => p.type === "tool_call");
1315
- const explicitStop = message._stopReason;
1316
- const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
1317
- simulateStream(message, stopReason, result, options.signal, cacheUsage);
1318
- },
1319
- (err) => result.abort(err instanceof Error ? err : new Error(String(err)))
1320
- );
1321
- return result;
1436
+ const gen = (async function* () {
1437
+ const rawMessage = typeof responseDef === "function" ? responseDef(options.messages, options, state) : responseDef;
1438
+ const message = await Promise.resolve(rawMessage);
1439
+ const hasToolCalls = Array.isArray(message.content) && message.content.some((p) => p.type === "tool_call");
1440
+ const explicitStop = message._stopReason;
1441
+ const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
1442
+ return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
1443
+ })();
1444
+ return new StreamResult(gen);
1322
1445
  }
1323
1446
  });
1324
1447
  return handle;