@prestyj/ai 4.2.77 → 4.3.34

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
  }
@@ -293,7 +312,7 @@ function toAnthropicToolChoice(choice) {
293
312
  return { type: "tool", name: choice.name };
294
313
  }
295
314
  function supportsAdaptiveThinking(model) {
296
- return /opus-4-6|sonnet-4-6/.test(model);
315
+ return /opus-4-7|opus-4-6|sonnet-4-6/.test(model);
297
316
  }
298
317
  function toAnthropicThinking(level, maxTokens, model) {
299
318
  if (supportsAdaptiveThinking(model)) {
@@ -320,10 +339,10 @@ function toAnthropicThinking(level, maxTokens, model) {
320
339
  };
321
340
  }
322
341
  function remapToolCallId(id, idMap) {
323
- if (id.startsWith("call_")) return id;
342
+ if (!id.startsWith("toolu_")) return id;
324
343
  const existing = idMap.get(id);
325
344
  if (existing) return existing;
326
- const mapped = `call_${id.replace(/^toolu_/, "")}`;
345
+ const mapped = `call_${id.slice(5)}`;
327
346
  idMap.set(id, mapped);
328
347
  return mapped;
329
348
  }
@@ -378,13 +397,18 @@ 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;
410
+ } else if (options?.thinking && hasToolCalls && options.provider !== "glm") {
411
+ assistantMsg.reasoning_content = " ";
388
412
  }
389
413
  out.push(assistantMsg);
390
414
  continue;
@@ -450,17 +474,16 @@ function normalizeOpenAIStopReason(reason) {
450
474
  }
451
475
 
452
476
  // 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) {
477
+ function createClient(options) {
459
478
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
460
- const client = new import_sdk.default({
479
+ return new import_sdk.default({
461
480
  ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
462
481
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
463
482
  ...options.fetch ? { fetch: options.fetch } : {},
483
+ // Allow SDK retries for connection-level failures (socket hang up, 500s,
484
+ // connection refused). Our stall detection handles abort-initiated retries
485
+ // separately — SDK retries only fire on genuine transport errors.
486
+ maxRetries: 2,
464
487
  ...isOAuth ? {
465
488
  defaultHeaders: {
466
489
  "user-agent": "claude-cli/2.1.75",
@@ -468,6 +491,14 @@ async function runStream(options, result) {
468
491
  }
469
492
  } : {}
470
493
  });
494
+ }
495
+ function streamAnthropic(options) {
496
+ return new StreamResult(runStream(options));
497
+ }
498
+ async function* runStream(options) {
499
+ const client = createClient(options);
500
+ const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
501
+ const useStreaming = options.streaming !== false;
471
502
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
472
503
  const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
473
504
  const system = isOAuth ? [
@@ -513,128 +544,323 @@ async function runStream(options, result) {
513
544
  ];
514
545
  return contextEdits.length ? { context_management: { edits: contextEdits } } : {};
515
546
  })(),
516
- stream: true
547
+ stream: useStreaming
517
548
  };
549
+ const hasAdaptiveThinking = options.model.includes("opus-4-7") || options.model.includes("opus-4.7") || 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
550
  const betaHeaders = [
519
551
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
520
552
  ...options.compaction ? ["compact-2026-01-12"] : [],
521
- ...options.clearToolUses ? ["context-management-2025-06-27"] : []
553
+ ...options.clearToolUses ? ["context-management-2025-06-27"] : [],
554
+ "fine-grained-tool-streaming-2025-05-14",
555
+ ...!hasAdaptiveThinking ? ["interleaved-thinking-2025-05-14"] : []
522
556
  ];
523
- const stream2 = client.messages.stream(params, {
557
+ const requestOptions = {
524
558
  signal: options.signal ?? void 0,
525
559
  ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
526
- });
560
+ };
561
+ if (!useStreaming) {
562
+ try {
563
+ const message = await client.messages.create(
564
+ { ...params, stream: false },
565
+ requestOptions
566
+ );
567
+ yield* synthesizeEventsFromMessage(message);
568
+ return messageToResponse(message);
569
+ } catch (err) {
570
+ throw toError(err);
571
+ }
572
+ }
573
+ const stream2 = client.messages.stream(params, requestOptions);
527
574
  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;
575
+ const blocks = /* @__PURE__ */ new Map();
576
+ let inputTokens = 0;
577
+ let outputTokens = 0;
578
+ let cacheRead;
579
+ let cacheWrite;
580
+ let stopReason = null;
581
+ const keepalive = { type: "keepalive" };
582
+ try {
583
+ for await (const event of stream2) {
584
+ switch (event.type) {
585
+ case "message_start": {
586
+ const usage = event.message.usage;
587
+ inputTokens = usage.input_tokens;
588
+ const usageAny = usage;
589
+ if (usageAny.cache_read_input_tokens != null) {
590
+ cacheRead = usageAny.cache_read_input_tokens;
591
+ }
592
+ if (usageAny.cache_creation_input_tokens != null) {
593
+ cacheWrite = usageAny.cache_creation_input_tokens;
594
+ }
595
+ yield keepalive;
596
+ break;
597
+ }
598
+ case "content_block_start": {
599
+ const block = event.content_block;
600
+ const idx = event.index;
601
+ const accum = {
602
+ type: block.type,
603
+ text: "",
604
+ thinking: "",
605
+ signature: "",
606
+ toolId: "",
607
+ toolName: "",
608
+ argsJson: "",
609
+ input: void 0,
610
+ raw: null
611
+ };
612
+ if (block.type === "tool_use") {
613
+ accum.toolId = block.id;
614
+ accum.toolName = block.name;
615
+ } else if (block.type === "server_tool_use") {
616
+ accum.toolId = block.id;
617
+ accum.toolName = block.name;
618
+ accum.input = block.input;
619
+ } else if (block.type === "redacted_thinking") {
620
+ accum.raw = block;
621
+ }
622
+ blocks.set(idx, accum);
623
+ yield keepalive;
624
+ break;
625
+ }
626
+ case "content_block_delta": {
627
+ const accum = blocks.get(event.index);
628
+ if (!accum) break;
629
+ const delta = event.delta;
630
+ const deltaType = delta.type;
631
+ if (deltaType === "text_delta") {
632
+ const text = delta.text;
633
+ accum.text += text;
634
+ yield { type: "text_delta", text };
635
+ } else if (deltaType === "thinking_delta") {
636
+ const text = delta.thinking;
637
+ accum.thinking += text;
638
+ yield { type: "thinking_delta", text };
639
+ } else if (deltaType === "input_json_delta") {
640
+ const partialJson = delta.partial_json;
641
+ accum.argsJson += partialJson;
642
+ yield {
643
+ type: "toolcall_delta",
644
+ id: accum.toolId,
645
+ name: accum.toolName,
646
+ argsJson: partialJson
647
+ };
648
+ } else if (deltaType === "signature_delta") {
649
+ accum.signature = delta.signature;
650
+ }
651
+ break;
652
+ }
653
+ case "content_block_stop": {
654
+ const accum = blocks.get(event.index);
655
+ if (!accum) break;
656
+ if (accum.type === "text") {
657
+ contentParts.push({ type: "text", text: accum.text });
658
+ } else if (accum.type === "thinking") {
659
+ contentParts.push({
660
+ type: "thinking",
661
+ text: accum.thinking,
662
+ signature: accum.signature
663
+ });
664
+ yield keepalive;
665
+ } else if (accum.type === "tool_use") {
666
+ let args = {};
667
+ try {
668
+ args = JSON.parse(accum.argsJson);
669
+ } catch {
670
+ }
671
+ const tc = {
672
+ type: "tool_call",
673
+ id: accum.toolId,
674
+ name: accum.toolName,
675
+ args
676
+ };
677
+ contentParts.push(tc);
678
+ yield {
679
+ type: "toolcall_done",
680
+ id: tc.id,
681
+ name: tc.name,
682
+ args: tc.args
683
+ };
684
+ } else if (accum.type === "server_tool_use") {
685
+ const stc = {
686
+ type: "server_tool_call",
687
+ id: accum.toolId,
688
+ name: accum.toolName,
689
+ input: accum.input
690
+ };
691
+ contentParts.push(stc);
692
+ yield {
693
+ type: "server_toolcall",
694
+ id: stc.id,
695
+ name: stc.name,
696
+ input: stc.input
697
+ };
698
+ } else if (accum.type === "redacted_thinking" && accum.raw) {
699
+ contentParts.push({ type: "raw", data: accum.raw });
700
+ yield keepalive;
701
+ } else {
702
+ const msg = stream2.currentMessage;
703
+ const rawBlock = msg?.content[event.index];
704
+ if (rawBlock) {
705
+ const blockType = rawBlock.type;
706
+ if (blockType === "web_search_tool_result") {
707
+ const str = {
708
+ type: "server_tool_result",
709
+ toolUseId: rawBlock.tool_use_id,
710
+ resultType: blockType,
711
+ data: rawBlock
712
+ };
713
+ contentParts.push(str);
714
+ yield {
715
+ type: "server_toolresult",
716
+ toolUseId: str.toolUseId,
717
+ resultType: str.resultType,
718
+ data: str.data
719
+ };
720
+ } else {
721
+ contentParts.push({ type: "raw", data: rawBlock });
722
+ }
723
+ }
724
+ }
725
+ blocks.delete(event.index);
726
+ break;
727
+ }
728
+ case "message_delta": {
729
+ const delta = event.delta;
730
+ if (delta.stop_reason) {
731
+ stopReason = delta.stop_reason;
732
+ }
733
+ const usage = event.usage;
734
+ if (usage?.output_tokens != null) {
735
+ outputTokens = usage.output_tokens;
736
+ }
737
+ yield keepalive;
738
+ break;
739
+ }
740
+ // message_stop — loop exits naturally
741
+ default:
742
+ yield keepalive;
743
+ break;
545
744
  }
546
745
  }
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
746
+ } catch (err) {
747
+ throw toError(err);
748
+ }
749
+ const normalizedStop = normalizeAnthropicStopReason(stopReason);
750
+ const response = {
751
+ message: {
752
+ role: "assistant",
753
+ content: contentParts.length > 0 ? contentParts : ""
754
+ },
755
+ stopReason: normalizedStop,
756
+ usage: {
757
+ inputTokens,
758
+ outputTokens,
759
+ ...cacheRead != null && { cacheRead },
760
+ ...cacheWrite != null && { cacheWrite }
761
+ }
762
+ };
763
+ yield { type: "done", stopReason: normalizedStop };
764
+ return response;
765
+ }
766
+ function* synthesizeEventsFromMessage(message) {
767
+ for (const block of message.content) {
768
+ const blk = block;
769
+ const type = blk.type;
770
+ if (type === "text") {
771
+ const text = blk.text;
772
+ if (text) yield { type: "text_delta", text };
773
+ } else if (type === "thinking") {
774
+ const text = blk.thinking;
775
+ if (text) yield { type: "thinking_delta", text };
776
+ } else if (type === "tool_use") {
777
+ const argsJson = JSON.stringify(blk.input ?? {});
778
+ yield {
779
+ type: "toolcall_delta",
780
+ id: blk.id,
781
+ name: blk.name,
782
+ argsJson
567
783
  };
568
- contentParts.push(tc);
569
- result.push({
784
+ yield {
570
785
  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
786
+ id: blk.id,
787
+ name: blk.name,
788
+ args: blk.input ?? {}
581
789
  };
582
- contentParts.push(stc);
583
- result.push({
790
+ } else if (type === "server_tool_use") {
791
+ yield {
584
792
  type: "server_toolcall",
585
- id: stc.id,
586
- name: stc.name,
587
- input: stc.input
793
+ id: blk.id,
794
+ name: blk.name,
795
+ input: blk.input
796
+ };
797
+ } else if (type === "web_search_tool_result") {
798
+ yield {
799
+ type: "server_toolresult",
800
+ toolUseId: blk.tool_use_id,
801
+ resultType: type,
802
+ data: blk
803
+ };
804
+ }
805
+ }
806
+ yield { type: "done", stopReason: normalizeAnthropicStopReason(message.stop_reason) };
807
+ }
808
+ function messageToResponse(message) {
809
+ const contentParts = [];
810
+ for (const block of message.content) {
811
+ const blk = block;
812
+ const type = blk.type;
813
+ if (type === "text") {
814
+ contentParts.push({ type: "text", text: blk.text });
815
+ } else if (type === "thinking") {
816
+ contentParts.push({
817
+ type: "thinking",
818
+ text: blk.thinking,
819
+ signature: blk.signature ?? ""
820
+ });
821
+ } else if (type === "tool_use") {
822
+ contentParts.push({
823
+ type: "tool_call",
824
+ id: blk.id,
825
+ name: blk.name,
826
+ args: blk.input ?? {}
827
+ });
828
+ } else if (type === "server_tool_use") {
829
+ contentParts.push({
830
+ type: "server_tool_call",
831
+ id: blk.id,
832
+ name: blk.name,
833
+ input: blk.input
834
+ });
835
+ } else if (type === "web_search_tool_result") {
836
+ contentParts.push({
837
+ type: "server_tool_result",
838
+ toolUseId: blk.tool_use_id,
839
+ resultType: type,
840
+ data: blk
588
841
  });
589
842
  } 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
- }
843
+ contentParts.push({ type: "raw", data: blk });
609
844
  }
610
- });
611
- 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
628
- }
629
- }
630
- };
631
- result.push({ type: "done", stopReason });
632
- result.complete(response);
633
- } catch (err) {
634
- const error = toError(err);
635
- result.push({ type: "error", error });
636
- result.abort(error);
637
845
  }
846
+ const usage = message.usage;
847
+ const inputTokens = usage.input_tokens ?? 0;
848
+ const outputTokens = usage.output_tokens ?? 0;
849
+ const cacheRead = usage.cache_read_input_tokens;
850
+ const cacheWrite = usage.cache_creation_input_tokens;
851
+ return {
852
+ message: {
853
+ role: "assistant",
854
+ content: contentParts.length > 0 ? contentParts : ""
855
+ },
856
+ stopReason: normalizeAnthropicStopReason(message.stop_reason),
857
+ usage: {
858
+ inputTokens,
859
+ outputTokens,
860
+ ...cacheRead != null && { cacheRead },
861
+ ...cacheWrite != null && { cacheWrite }
862
+ }
863
+ };
638
864
  }
639
865
  function toError(err) {
640
866
  if (err instanceof import_sdk.default.APIError) {
@@ -651,49 +877,85 @@ function toError(err) {
651
877
 
652
878
  // src/providers/openai.ts
653
879
  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({
880
+ function createClient2(options) {
881
+ return new import_openai.default({
662
882
  apiKey: options.apiKey,
663
883
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
664
884
  ...options.fetch ? { fetch: options.fetch } : {}
665
885
  });
666
- const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot";
667
- const messages = toOpenAIMessages(options.messages, { provider: options.provider });
886
+ }
887
+ function streamOpenAI(options) {
888
+ return new StreamResult(runStream2(options));
889
+ }
890
+ async function* runStream2(options) {
891
+ const providerName = options.provider ?? "openai";
892
+ const useStreaming = options.streaming !== false;
893
+ const client = createClient2(options);
894
+ const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
895
+ const messages = toOpenAIMessages(options.messages, {
896
+ provider: options.provider,
897
+ thinking: !!options.thinking
898
+ });
668
899
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
669
900
  const effectiveTemp = options.temperature ?? defaultTemp;
670
901
  const params = {
671
902
  model: options.model,
672
903
  messages,
673
- stream: true,
674
- ...options.maxTokens ? { max_tokens: options.maxTokens } : {},
904
+ stream: useStreaming,
905
+ ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
675
906
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
676
907
  ...options.topP != null ? { top_p: options.topP } : {},
677
908
  ...options.stop ? { stop: options.stop } : {},
678
909
  ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
679
910
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
680
911
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
681
- stream_options: { include_usage: true }
912
+ ...useStreaming ? { stream_options: { include_usage: true } } : {}
682
913
  };
683
- if (options.webSearch) {
684
- if (options.provider === "moonshot") {
685
- const raw = params;
686
- const tools = (raw.tools ?? []).slice();
687
- tools.push({ type: "builtin_function", function: { name: "$web_search" } });
688
- raw.tools = tools;
914
+ if (options.provider === "openai" || options.provider === "moonshot") {
915
+ const paramsAny = params;
916
+ paramsAny.prompt_cache_key = "ezcoder";
917
+ const retention = options.cacheRetention ?? "short";
918
+ if (retention === "long") {
919
+ paramsAny.prompt_cache_retention = "24h";
689
920
  }
690
921
  }
691
922
  if (usesThinkingParam) {
692
- params.thinking = options.thinking ? { type: "enabled" } : { type: "disabled" };
923
+ if (options.thinking) {
924
+ params.thinking = { type: "enabled" };
925
+ } else {
926
+ params.thinking = { type: "disabled" };
927
+ }
928
+ }
929
+ if (globalThis.process && globalThis.process.env?.GGAI_DUMP_REQUEST) {
930
+ const fs = await import("fs");
931
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
932
+ const dumpPath = `/tmp/ggai-request-${ts}.json`;
933
+ fs.writeFileSync(dumpPath, JSON.stringify(params, null, 2));
934
+ fs.appendFileSync(
935
+ "/tmp/ggai-requests.log",
936
+ `[${ts}] ${dumpPath} messages=${params.messages.length}
937
+ `
938
+ );
939
+ }
940
+ if (!useStreaming) {
941
+ try {
942
+ const completion = await client.chat.completions.create(params, {
943
+ signal: options.signal ?? void 0
944
+ });
945
+ yield* synthesizeEventsFromCompletion(completion, !!options.thinking);
946
+ return completionToResponse(completion);
947
+ } catch (err) {
948
+ throw toError2(err, providerName);
949
+ }
950
+ }
951
+ let stream2;
952
+ try {
953
+ stream2 = await client.chat.completions.create(params, {
954
+ signal: options.signal ?? void 0
955
+ });
956
+ } catch (err) {
957
+ throw toError2(err, providerName);
693
958
  }
694
- const stream2 = await client.chat.completions.create(params, {
695
- signal: options.signal ?? void 0
696
- });
697
959
  const contentParts = [];
698
960
  const toolCallAccum = /* @__PURE__ */ new Map();
699
961
  let textAccum = "";
@@ -710,6 +972,10 @@ async function runStream2(options, result) {
710
972
  if (details?.cached_tokens) {
711
973
  cacheRead = details.cached_tokens;
712
974
  }
975
+ const usageAny = chunk.usage;
976
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
977
+ cacheRead = usageAny.cached_tokens;
978
+ }
713
979
  inputTokens = chunk.usage.prompt_tokens - cacheRead;
714
980
  }
715
981
  if (!choice) continue;
@@ -720,11 +986,13 @@ async function runStream2(options, result) {
720
986
  const reasoningContent = delta.reasoning_content;
721
987
  if (typeof reasoningContent === "string" && reasoningContent) {
722
988
  thinkingAccum += reasoningContent;
723
- result.push({ type: "thinking_delta", text: reasoningContent });
989
+ if (options.thinking) {
990
+ yield { type: "thinking_delta", text: reasoningContent };
991
+ }
724
992
  }
725
993
  if (delta.content) {
726
994
  textAccum += delta.content;
727
- result.push({ type: "text_delta", text: delta.content });
995
+ yield { type: "text_delta", text: delta.content };
728
996
  }
729
997
  if (delta.tool_calls) {
730
998
  for (const tc of delta.tool_calls) {
@@ -741,12 +1009,12 @@ async function runStream2(options, result) {
741
1009
  if (tc.function?.name) accum.name = tc.function.name;
742
1010
  if (tc.function?.arguments) {
743
1011
  accum.argsJson += tc.function.arguments;
744
- result.push({
1012
+ yield {
745
1013
  type: "toolcall_delta",
746
1014
  id: accum.id,
747
1015
  name: accum.name,
748
1016
  argsJson: tc.function.arguments
749
- });
1017
+ };
750
1018
  }
751
1019
  }
752
1020
  }
@@ -770,12 +1038,12 @@ async function runStream2(options, result) {
770
1038
  args
771
1039
  };
772
1040
  contentParts.push(toolCall);
773
- result.push({
1041
+ yield {
774
1042
  type: "toolcall_done",
775
1043
  id: tc.id,
776
1044
  name: tc.name,
777
1045
  args
778
- });
1046
+ };
779
1047
  }
780
1048
  const stopReason = normalizeOpenAIStopReason(finishReason);
781
1049
  const response = {
@@ -786,14 +1054,116 @@ async function runStream2(options, result) {
786
1054
  stopReason,
787
1055
  usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
788
1056
  };
789
- result.push({ type: "done", stopReason });
790
- result.complete(response);
1057
+ yield { type: "done", stopReason };
1058
+ return response;
1059
+ }
1060
+ function* synthesizeEventsFromCompletion(completion, thinkingEnabled) {
1061
+ const choice = completion.choices?.[0];
1062
+ if (!choice) {
1063
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(null) };
1064
+ return;
1065
+ }
1066
+ const msg = choice.message;
1067
+ const reasoning = msg.reasoning_content;
1068
+ if (typeof reasoning === "string" && reasoning && thinkingEnabled) {
1069
+ yield { type: "thinking_delta", text: reasoning };
1070
+ }
1071
+ if (typeof msg.content === "string" && msg.content) {
1072
+ yield { type: "text_delta", text: msg.content };
1073
+ }
1074
+ const toolCalls = msg.tool_calls;
1075
+ if (toolCalls) {
1076
+ for (const tc of toolCalls) {
1077
+ const argsJson = tc.function?.arguments ?? "";
1078
+ if (argsJson) {
1079
+ yield {
1080
+ type: "toolcall_delta",
1081
+ id: tc.id,
1082
+ name: tc.function?.name ?? "",
1083
+ argsJson
1084
+ };
1085
+ }
1086
+ let args = {};
1087
+ try {
1088
+ args = JSON.parse(argsJson);
1089
+ } catch {
1090
+ }
1091
+ yield {
1092
+ type: "toolcall_done",
1093
+ id: tc.id,
1094
+ name: tc.function?.name ?? "",
1095
+ args
1096
+ };
1097
+ }
1098
+ }
1099
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(choice.finish_reason ?? null) };
1100
+ }
1101
+ function completionToResponse(completion) {
1102
+ const choice = completion.choices?.[0];
1103
+ const contentParts = [];
1104
+ let textAccum = "";
1105
+ if (choice) {
1106
+ const msg = choice.message;
1107
+ const reasoning = msg.reasoning_content;
1108
+ if (typeof reasoning === "string" && reasoning) {
1109
+ contentParts.push({ type: "thinking", text: reasoning });
1110
+ }
1111
+ if (typeof msg.content === "string" && msg.content) {
1112
+ textAccum = msg.content;
1113
+ contentParts.push({ type: "text", text: msg.content });
1114
+ }
1115
+ const toolCalls = msg.tool_calls;
1116
+ if (toolCalls) {
1117
+ for (const tc of toolCalls) {
1118
+ let args = {};
1119
+ try {
1120
+ args = JSON.parse(tc.function?.arguments ?? "{}");
1121
+ } catch {
1122
+ }
1123
+ const toolCall = {
1124
+ type: "tool_call",
1125
+ id: tc.id,
1126
+ name: tc.function?.name ?? "",
1127
+ args
1128
+ };
1129
+ contentParts.push(toolCall);
1130
+ }
1131
+ }
1132
+ }
1133
+ let inputTokens = 0;
1134
+ let outputTokens = 0;
1135
+ let cacheRead = 0;
1136
+ if (completion.usage) {
1137
+ outputTokens = completion.usage.completion_tokens;
1138
+ const details = completion.usage.prompt_tokens_details;
1139
+ if (details?.cached_tokens) cacheRead = details.cached_tokens;
1140
+ const usageAny = completion.usage;
1141
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1142
+ cacheRead = usageAny.cached_tokens;
1143
+ }
1144
+ inputTokens = completion.usage.prompt_tokens - cacheRead;
1145
+ }
1146
+ const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
1147
+ return {
1148
+ message: {
1149
+ role: "assistant",
1150
+ content: contentParts.length > 0 ? contentParts : textAccum
1151
+ },
1152
+ stopReason,
1153
+ usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
1154
+ };
791
1155
  }
792
1156
  function toError2(err, provider = "openai") {
793
1157
  if (err instanceof import_openai.default.APIError) {
794
1158
  let msg = err.message;
795
1159
  const body = err.error;
796
1160
  if (body) {
1161
+ const modelName = body.model || "";
1162
+ const _code = body.code || "";
1163
+ const message = body.message || "";
1164
+ if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
1165
+ 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.`;
1166
+ }
797
1167
  msg += ` | body: ${JSON.stringify(body)}`;
798
1168
  }
799
1169
  return new ProviderError(provider, msg, {
@@ -811,11 +1181,9 @@ function toError2(err, provider = "openai") {
811
1181
  var import_node_os = __toESM(require("os"), 1);
812
1182
  var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
813
1183
  function streamOpenAICodex(options) {
814
- const result = new StreamResult();
815
- runStream3(options, result).catch((err) => result.abort(toError3(err)));
816
- return result;
1184
+ return new StreamResult(runStream3(options));
817
1185
  }
818
- async function runStream3(options, result) {
1186
+ async function* runStream3(options) {
819
1187
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
820
1188
  const url = `${baseUrl}/codex/responses`;
821
1189
  const { system, input } = toCodexInput(options.messages);
@@ -865,6 +1233,11 @@ async function runStream3(options, result) {
865
1233
  message += `
866
1234
 
867
1235
  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`;
1236
+ }
1237
+ if (response.status === 404 && text.includes("does not exist")) {
1238
+ message += `
1239
+
1240
+ 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
1241
  }
869
1242
  throw new ProviderError("openai", message, {
870
1243
  statusCode: response.status
@@ -892,11 +1265,11 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
892
1265
  if (type === "response.output_text.delta") {
893
1266
  const delta = event.delta;
894
1267
  textAccum += delta;
895
- result.push({ type: "text_delta", text: delta });
1268
+ yield { type: "text_delta", text: delta };
896
1269
  }
897
1270
  if (type === "response.reasoning_summary_text.delta") {
898
1271
  const delta = event.delta;
899
- result.push({ type: "thinking_delta", text: delta });
1272
+ yield { type: "thinking_delta", text: delta };
900
1273
  }
901
1274
  if (type === "response.output_item.added") {
902
1275
  const item = event.item;
@@ -914,12 +1287,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
914
1287
  for (const [key, tc] of toolCalls) {
915
1288
  if (key.endsWith(`|${itemId}`)) {
916
1289
  tc.argsJson += delta;
917
- result.push({
1290
+ yield {
918
1291
  type: "toolcall_delta",
919
1292
  id: tc.id,
920
1293
  name: tc.name,
921
1294
  argsJson: delta
922
- });
1295
+ };
923
1296
  break;
924
1297
  }
925
1298
  }
@@ -947,12 +1320,12 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
947
1320
  args = JSON.parse(tc.argsJson);
948
1321
  } catch {
949
1322
  }
950
- result.push({
1323
+ yield {
951
1324
  type: "toolcall_done",
952
1325
  id: tc.id,
953
1326
  name: tc.name,
954
1327
  args
955
- });
1328
+ };
956
1329
  }
957
1330
  }
958
1331
  }
@@ -992,8 +1365,8 @@ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription
992
1365
  stopReason,
993
1366
  usage: { inputTokens, outputTokens }
994
1367
  };
995
- result.push({ type: "done", stopReason });
996
- result.complete(streamResponse);
1368
+ yield { type: "done", stopReason };
1369
+ return streamResponse;
997
1370
  }
998
1371
  async function* parseSSE(body) {
999
1372
  const reader = body.getReader();
@@ -1107,13 +1480,6 @@ function toCodexTools(tools) {
1107
1480
  strict: null
1108
1481
  }));
1109
1482
  }
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
1483
 
1118
1484
  // src/provider-registry.ts
1119
1485
  var ProviderRegistryImpl = class {
@@ -1153,10 +1519,16 @@ var providerRegistry = new ProviderRegistryImpl();
1153
1519
 
1154
1520
  // src/stream.ts
1155
1521
  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
1522
  providerRegistry.register("anthropic", {
1158
1523
  stream: (options) => streamAnthropic(options)
1159
1524
  });
1525
+ providerRegistry.register("xiaomi", {
1526
+ stream: (options) => streamOpenAI({
1527
+ ...options,
1528
+ baseUrl: options.baseUrl ?? "https://token-plan-sgp.xiaomimimo.com/v1",
1529
+ webSearch: false
1530
+ })
1531
+ });
1160
1532
  providerRegistry.register("openai", {
1161
1533
  stream: (options) => {
1162
1534
  if (options.accountId) {
@@ -1166,10 +1538,10 @@ providerRegistry.register("openai", {
1166
1538
  }
1167
1539
  });
1168
1540
  providerRegistry.register("glm", {
1169
- stream: (options) => {
1170
- if (options.baseUrl) return streamOpenAI(options);
1171
- return streamGLMWithFallback(options);
1172
- }
1541
+ stream: (options) => streamOpenAI({
1542
+ ...options,
1543
+ baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
1544
+ })
1173
1545
  });
1174
1546
  providerRegistry.register("moonshot", {
1175
1547
  stream: (options) => streamOpenAI({
@@ -1177,6 +1549,24 @@ providerRegistry.register("moonshot", {
1177
1549
  baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1178
1550
  })
1179
1551
  });
1552
+ providerRegistry.register("openrouter", {
1553
+ stream: (options) => streamOpenAI({
1554
+ ...options,
1555
+ baseUrl: options.baseUrl ?? "https://openrouter.ai/api/v1"
1556
+ })
1557
+ });
1558
+ providerRegistry.register("minimax", {
1559
+ stream: (options) => streamAnthropic({
1560
+ ...options,
1561
+ baseUrl: options.baseUrl ?? "https://api.minimax.io/anthropic",
1562
+ // MiniMax's Anthropic-compatible API does not support Anthropic-specific
1563
+ // server tools (web_search), context_management, or server-side tools.
1564
+ webSearch: false,
1565
+ compaction: false,
1566
+ clearToolUses: false,
1567
+ serverTools: void 0
1568
+ })
1569
+ });
1180
1570
  function stream(options) {
1181
1571
  const entry = providerRegistry.get(options.provider);
1182
1572
  if (!entry) {
@@ -1186,32 +1576,6 @@ function stream(options) {
1186
1576
  }
1187
1577
  return entry.stream(options);
1188
1578
  }
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
1579
 
1216
1580
  // src/providers/palsu.ts
1217
1581
  function palsuText(text) {
@@ -1239,31 +1603,29 @@ function chunkText(text, size) {
1239
1603
  }
1240
1604
  return chunks.length > 0 ? chunks : [""];
1241
1605
  }
1242
- function simulateStream(message, stopReason, result, signal, cacheUsage) {
1606
+ async function* simulateStream(message, stopReason, signal, cacheUsage) {
1243
1607
  if (signal?.aborted) {
1244
- result.abort(new Error("aborted"));
1245
- return;
1608
+ throw new Error("aborted");
1246
1609
  }
1247
1610
  const content = typeof message.content === "string" ? message.content ? [{ type: "text", text: message.content }] : [] : message.content;
1248
1611
  let outputChars = 0;
1249
1612
  for (const part of content) {
1250
1613
  if (signal?.aborted) {
1251
- result.abort(new Error("aborted"));
1252
- return;
1614
+ throw new Error("aborted");
1253
1615
  }
1254
1616
  if (part.type === "text") {
1255
1617
  const chunks = chunkText(part.text, DEFAULT_CHUNK_SIZE);
1256
1618
  for (const chunk of chunks) {
1257
- result.push({ type: "text_delta", text: chunk });
1619
+ yield { type: "text_delta", text: chunk };
1258
1620
  outputChars += chunk.length;
1259
1621
  }
1260
1622
  } else if (part.type === "thinking") {
1261
- result.push({ type: "thinking_delta", text: part.text });
1623
+ yield { type: "thinking_delta", text: part.text };
1262
1624
  outputChars += part.text.length;
1263
1625
  } else if (part.type === "tool_call") {
1264
1626
  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 });
1627
+ yield { type: "toolcall_delta", id: part.id, name: part.name, argsJson };
1628
+ yield { type: "toolcall_done", id: part.id, name: part.name, args: part.args };
1267
1629
  outputChars += argsJson.length;
1268
1630
  }
1269
1631
  }
@@ -1274,8 +1636,8 @@ function simulateStream(message, stopReason, result, signal, cacheUsage) {
1274
1636
  ...cacheUsage?.cacheRead ? { cacheRead: cacheUsage.cacheRead } : {},
1275
1637
  ...cacheUsage?.cacheWrite ? { cacheWrite: cacheUsage.cacheWrite } : {}
1276
1638
  };
1277
- result.push({ type: "done", stopReason });
1278
- result.complete({ message, stopReason, usage });
1639
+ yield { type: "done", stopReason };
1640
+ return { message, stopReason, usage };
1279
1641
  }
1280
1642
  function computeCacheUsage(current, previous) {
1281
1643
  if (!previous) {
@@ -1347,24 +1709,21 @@ function registerPalsuProvider(config) {
1347
1709
  state.callCount++;
1348
1710
  const ms = modelStates.get(options.model);
1349
1711
  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
1712
  let cacheUsage;
1352
1713
  if (enableCache) {
1353
1714
  const serialized = JSON.stringify(options.messages);
1354
1715
  cacheUsage = computeCacheUsage(serialized, lastMessagesSerialized);
1355
1716
  lastMessagesSerialized = serialized;
1356
1717
  }
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;
1718
+ const gen = (async function* () {
1719
+ const rawMessage = typeof responseDef === "function" ? responseDef(options.messages, options, state) : responseDef;
1720
+ const message = await Promise.resolve(rawMessage);
1721
+ const hasToolCalls = Array.isArray(message.content) && message.content.some((p) => p.type === "tool_call");
1722
+ const explicitStop = message._stopReason;
1723
+ const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
1724
+ return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
1725
+ })();
1726
+ return new StreamResult(gen);
1368
1727
  }
1369
1728
  });
1370
1729
  return handle;